diff options
Diffstat (limited to 'sound/soc')
49 files changed, 5313 insertions, 273 deletions
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 97b255233175..276585215160 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig | |||
@@ -28,6 +28,7 @@ source "sound/soc/at91/Kconfig" | |||
28 | source "sound/soc/pxa/Kconfig" | 28 | source "sound/soc/pxa/Kconfig" |
29 | source "sound/soc/s3c24xx/Kconfig" | 29 | source "sound/soc/s3c24xx/Kconfig" |
30 | source "sound/soc/sh/Kconfig" | 30 | source "sound/soc/sh/Kconfig" |
31 | source "sound/soc/fsl/Kconfig" | ||
31 | 32 | ||
32 | # Supported codecs | 33 | # Supported codecs |
33 | source "sound/soc/codecs/Kconfig" | 34 | source "sound/soc/codecs/Kconfig" |
diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 304140377632..4869c9ae7a03 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile | |||
@@ -1,4 +1,4 @@ | |||
1 | snd-soc-core-objs := soc-core.o soc-dapm.o | 1 | snd-soc-core-objs := soc-core.o soc-dapm.o |
2 | 2 | ||
3 | obj-$(CONFIG_SND_SOC) += snd-soc-core.o | 3 | obj-$(CONFIG_SND_SOC) += snd-soc-core.o |
4 | obj-$(CONFIG_SND_SOC) += codecs/ at91/ pxa/ s3c24xx/ sh/ | 4 | obj-$(CONFIG_SND_SOC) += codecs/ at91/ pxa/ s3c24xx/ sh/ fsl/ |
diff --git a/sound/soc/at91/at91-pcm.c b/sound/soc/at91/at91-pcm.c index b39b95a47040..67c88e322fb1 100644 --- a/sound/soc/at91/at91-pcm.c +++ b/sound/soc/at91/at91-pcm.c | |||
@@ -23,7 +23,6 @@ | |||
23 | #include <linux/dma-mapping.h> | 23 | #include <linux/dma-mapping.h> |
24 | #include <linux/atmel_pdc.h> | 24 | #include <linux/atmel_pdc.h> |
25 | 25 | ||
26 | #include <sound/driver.h> | ||
27 | #include <sound/core.h> | 26 | #include <sound/core.h> |
28 | #include <sound/pcm.h> | 27 | #include <sound/pcm.h> |
29 | #include <sound/pcm_params.h> | 28 | #include <sound/pcm_params.h> |
diff --git a/sound/soc/at91/at91-ssc.c b/sound/soc/at91/at91-ssc.c index 3d4e32cff75e..f642d2dd4ec3 100644 --- a/sound/soc/at91/at91-ssc.c +++ b/sound/soc/at91/at91-ssc.c | |||
@@ -22,7 +22,6 @@ | |||
22 | #include <linux/clk.h> | 22 | #include <linux/clk.h> |
23 | #include <linux/atmel_pdc.h> | 23 | #include <linux/atmel_pdc.h> |
24 | 24 | ||
25 | #include <sound/driver.h> | ||
26 | #include <sound/core.h> | 25 | #include <sound/core.h> |
27 | #include <sound/pcm.h> | 26 | #include <sound/pcm.h> |
28 | #include <sound/pcm_params.h> | 27 | #include <sound/pcm_params.h> |
diff --git a/sound/soc/at91/eti_b1_wm8731.c b/sound/soc/at91/eti_b1_wm8731.c index 820a676c56bf..ad3ad9d662f8 100644 --- a/sound/soc/at91/eti_b1_wm8731.c +++ b/sound/soc/at91/eti_b1_wm8731.c | |||
@@ -28,7 +28,6 @@ | |||
28 | #include <linux/timer.h> | 28 | #include <linux/timer.h> |
29 | #include <linux/interrupt.h> | 29 | #include <linux/interrupt.h> |
30 | #include <linux/platform_device.h> | 30 | #include <linux/platform_device.h> |
31 | #include <sound/driver.h> | ||
32 | #include <sound/core.h> | 31 | #include <sound/core.h> |
33 | #include <sound/pcm.h> | 32 | #include <sound/pcm.h> |
34 | #include <sound/soc.h> | 33 | #include <sound/soc.h> |
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 78248808a9d8..898a7d363284 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig | |||
@@ -37,3 +37,6 @@ config SND_SOC_CS4270_VD33_ERRATA | |||
37 | bool | 37 | bool |
38 | depends on SND_SOC_CS4270 | 38 | depends on SND_SOC_CS4270 |
39 | 39 | ||
40 | config SND_SOC_TLV320AIC3X | ||
41 | tristate | ||
42 | depends on SND_SOC && I2C | ||
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 7ad78e36d506..c6e5338c2666 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile | |||
@@ -4,6 +4,7 @@ snd-soc-wm8750-objs := wm8750.o | |||
4 | snd-soc-wm8753-objs := wm8753.o | 4 | snd-soc-wm8753-objs := wm8753.o |
5 | snd-soc-wm9712-objs := wm9712.o | 5 | snd-soc-wm9712-objs := wm9712.o |
6 | snd-soc-cs4270-objs := cs4270.o | 6 | snd-soc-cs4270-objs := cs4270.o |
7 | snd-soc-tlv320aic3x-objs := tlv320aic3x.o | ||
7 | 8 | ||
8 | obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o | 9 | obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o |
9 | obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o | 10 | obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o |
@@ -11,3 +12,4 @@ obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o | |||
11 | obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o | 12 | obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o |
12 | obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o | 13 | obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o |
13 | obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o | 14 | obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o |
15 | obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o | ||
diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c index 0b8a6f8b3668..242130cf1abd 100644 --- a/sound/soc/codecs/ac97.c +++ b/sound/soc/codecs/ac97.c | |||
@@ -19,7 +19,6 @@ | |||
19 | #include <linux/init.h> | 19 | #include <linux/init.h> |
20 | #include <linux/kernel.h> | 20 | #include <linux/kernel.h> |
21 | #include <linux/device.h> | 21 | #include <linux/device.h> |
22 | #include <sound/driver.h> | ||
23 | #include <sound/core.h> | 22 | #include <sound/core.h> |
24 | #include <sound/pcm.h> | 23 | #include <sound/pcm.h> |
25 | #include <sound/ac97_codec.h> | 24 | #include <sound/ac97_codec.h> |
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index dab22cc97ead..bf2ab72d49bf 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c | |||
@@ -28,7 +28,6 @@ | |||
28 | 28 | ||
29 | #include <linux/module.h> | 29 | #include <linux/module.h> |
30 | #include <linux/platform_device.h> | 30 | #include <linux/platform_device.h> |
31 | #include <sound/driver.h> | ||
32 | #include <sound/core.h> | 31 | #include <sound/core.h> |
33 | #include <sound/soc.h> | 32 | #include <sound/soc.h> |
34 | #include <sound/initval.h> | 33 | #include <sound/initval.h> |
@@ -48,12 +47,130 @@ struct cs4270_private { | |||
48 | unsigned int mode; /* The mode (I2S or left-justified) */ | 47 | unsigned int mode; /* The mode (I2S or left-justified) */ |
49 | }; | 48 | }; |
50 | 49 | ||
51 | /* The number of MCLK/LRCK ratios supported by the CS4270 */ | 50 | /* |
52 | #define NUM_MCLK_RATIOS 9 | 51 | * The codec isn't really big-endian or little-endian, since the I2S |
52 | * interface requires data to be sent serially with the MSbit first. | ||
53 | * However, to support BE and LE I2S devices, we specify both here. That | ||
54 | * way, ALSA will always match the bit patterns. | ||
55 | */ | ||
56 | #define CS4270_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ | ||
57 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \ | ||
58 | SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \ | ||
59 | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \ | ||
60 | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \ | ||
61 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE) | ||
62 | |||
63 | #ifdef USE_I2C | ||
64 | |||
65 | /* CS4270 registers addresses */ | ||
66 | #define CS4270_CHIPID 0x01 /* Chip ID */ | ||
67 | #define CS4270_PWRCTL 0x02 /* Power Control */ | ||
68 | #define CS4270_MODE 0x03 /* Mode Control */ | ||
69 | #define CS4270_FORMAT 0x04 /* Serial Format, ADC/DAC Control */ | ||
70 | #define CS4270_TRANS 0x05 /* Transition Control */ | ||
71 | #define CS4270_MUTE 0x06 /* Mute Control */ | ||
72 | #define CS4270_VOLA 0x07 /* DAC Channel A Volume Control */ | ||
73 | #define CS4270_VOLB 0x08 /* DAC Channel B Volume Control */ | ||
74 | |||
75 | #define CS4270_FIRSTREG 0x01 | ||
76 | #define CS4270_LASTREG 0x08 | ||
77 | #define CS4270_NUMREGS (CS4270_LASTREG - CS4270_FIRSTREG + 1) | ||
53 | 78 | ||
54 | /* The actual MCLK/LRCK ratios, in increasing numerical order */ | 79 | /* Bit masks for the CS4270 registers */ |
55 | static unsigned int mclk_ratios[NUM_MCLK_RATIOS] = | 80 | #define CS4270_CHIPID_ID 0xF0 |
56 | {64, 96, 128, 192, 256, 384, 512, 768, 1024}; | 81 | #define CS4270_CHIPID_REV 0x0F |
82 | #define CS4270_PWRCTL_FREEZE 0x80 | ||
83 | #define CS4270_PWRCTL_PDN_ADC 0x20 | ||
84 | #define CS4270_PWRCTL_PDN_DAC 0x02 | ||
85 | #define CS4270_PWRCTL_PDN 0x01 | ||
86 | #define CS4270_MODE_SPEED_MASK 0x30 | ||
87 | #define CS4270_MODE_1X 0x00 | ||
88 | #define CS4270_MODE_2X 0x10 | ||
89 | #define CS4270_MODE_4X 0x20 | ||
90 | #define CS4270_MODE_SLAVE 0x30 | ||
91 | #define CS4270_MODE_DIV_MASK 0x0E | ||
92 | #define CS4270_MODE_DIV1 0x00 | ||
93 | #define CS4270_MODE_DIV15 0x02 | ||
94 | #define CS4270_MODE_DIV2 0x04 | ||
95 | #define CS4270_MODE_DIV3 0x06 | ||
96 | #define CS4270_MODE_DIV4 0x08 | ||
97 | #define CS4270_MODE_POPGUARD 0x01 | ||
98 | #define CS4270_FORMAT_FREEZE_A 0x80 | ||
99 | #define CS4270_FORMAT_FREEZE_B 0x40 | ||
100 | #define CS4270_FORMAT_LOOPBACK 0x20 | ||
101 | #define CS4270_FORMAT_DAC_MASK 0x18 | ||
102 | #define CS4270_FORMAT_DAC_LJ 0x00 | ||
103 | #define CS4270_FORMAT_DAC_I2S 0x08 | ||
104 | #define CS4270_FORMAT_DAC_RJ16 0x18 | ||
105 | #define CS4270_FORMAT_DAC_RJ24 0x10 | ||
106 | #define CS4270_FORMAT_ADC_MASK 0x01 | ||
107 | #define CS4270_FORMAT_ADC_LJ 0x00 | ||
108 | #define CS4270_FORMAT_ADC_I2S 0x01 | ||
109 | #define CS4270_TRANS_ONE_VOL 0x80 | ||
110 | #define CS4270_TRANS_SOFT 0x40 | ||
111 | #define CS4270_TRANS_ZERO 0x20 | ||
112 | #define CS4270_TRANS_INV_ADC_A 0x08 | ||
113 | #define CS4270_TRANS_INV_ADC_B 0x10 | ||
114 | #define CS4270_TRANS_INV_DAC_A 0x02 | ||
115 | #define CS4270_TRANS_INV_DAC_B 0x04 | ||
116 | #define CS4270_TRANS_DEEMPH 0x01 | ||
117 | #define CS4270_MUTE_AUTO 0x20 | ||
118 | #define CS4270_MUTE_ADC_A 0x08 | ||
119 | #define CS4270_MUTE_ADC_B 0x10 | ||
120 | #define CS4270_MUTE_POLARITY 0x04 | ||
121 | #define CS4270_MUTE_DAC_A 0x01 | ||
122 | #define CS4270_MUTE_DAC_B 0x02 | ||
123 | |||
124 | /* | ||
125 | * Clock Ratio Selection for Master Mode with I2C enabled | ||
126 | * | ||
127 | * The data for this chart is taken from Table 5 of the CS4270 reference | ||
128 | * manual. | ||
129 | * | ||
130 | * This table is used to determine how to program the Mode Control register. | ||
131 | * It is also used by cs4270_set_dai_sysclk() to tell ALSA which sampling | ||
132 | * rates the CS4270 currently supports. | ||
133 | * | ||
134 | * Each element in this array corresponds to the ratios in mclk_ratios[]. | ||
135 | * These two arrays need to be in sync. | ||
136 | * | ||
137 | * 'speed_mode' is the corresponding bit pattern to be written to the | ||
138 | * MODE bits of the Mode Control Register | ||
139 | * | ||
140 | * 'mclk' is the corresponding bit pattern to be wirten to the MCLK bits of | ||
141 | * the Mode Control Register. | ||
142 | * | ||
143 | * In situations where a single ratio is represented by multiple speed | ||
144 | * modes, we favor the slowest speed. E.g, for a ratio of 128, we pick | ||
145 | * double-speed instead of quad-speed. However, the CS4270 errata states | ||
146 | * that Divide-By-1.5 can cause failures, so we avoid that mode where | ||
147 | * possible. | ||
148 | * | ||
149 | * ERRATA: There is an errata for the CS4270 where divide-by-1.5 does not | ||
150 | * work if VD = 3.3V. If this effects you, select the | ||
151 | * CONFIG_SND_SOC_CS4270_VD33_ERRATA Kconfig option, and the driver will | ||
152 | * never select any sample rates that require divide-by-1.5. | ||
153 | */ | ||
154 | static struct { | ||
155 | unsigned int ratio; | ||
156 | u8 speed_mode; | ||
157 | u8 mclk; | ||
158 | } cs4270_mode_ratios[] = { | ||
159 | {64, CS4270_MODE_4X, CS4270_MODE_DIV1}, | ||
160 | #ifndef CONFIG_SND_SOC_CS4270_VD33_ERRATA | ||
161 | {96, CS4270_MODE_4X, CS4270_MODE_DIV15}, | ||
162 | #endif | ||
163 | {128, CS4270_MODE_2X, CS4270_MODE_DIV1}, | ||
164 | {192, CS4270_MODE_4X, CS4270_MODE_DIV3}, | ||
165 | {256, CS4270_MODE_1X, CS4270_MODE_DIV1}, | ||
166 | {384, CS4270_MODE_2X, CS4270_MODE_DIV3}, | ||
167 | {512, CS4270_MODE_1X, CS4270_MODE_DIV2}, | ||
168 | {768, CS4270_MODE_1X, CS4270_MODE_DIV3}, | ||
169 | {1024, CS4270_MODE_1X, CS4270_MODE_DIV4} | ||
170 | }; | ||
171 | |||
172 | /* The number of MCLK/LRCK ratios supported by the CS4270 */ | ||
173 | #define NUM_MCLK_RATIOS ARRAY_SIZE(cs4270_mode_ratios) | ||
57 | 174 | ||
58 | /* | 175 | /* |
59 | * Determine the CS4270 samples rates. | 176 | * Determine the CS4270 samples rates. |
@@ -97,7 +214,7 @@ static int cs4270_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai, | |||
97 | cs4270->mclk = freq; | 214 | cs4270->mclk = freq; |
98 | 215 | ||
99 | for (i = 0; i < NUM_MCLK_RATIOS; i++) { | 216 | for (i = 0; i < NUM_MCLK_RATIOS; i++) { |
100 | unsigned int rate = freq / mclk_ratios[i]; | 217 | unsigned int rate = freq / cs4270_mode_ratios[i].ratio; |
101 | rates |= snd_pcm_rate_to_rate_bit(rate); | 218 | rates |= snd_pcm_rate_to_rate_bit(rate); |
102 | if (rate < rate_min) | 219 | if (rate < rate_min) |
103 | rate_min = rate; | 220 | rate_min = rate; |
@@ -155,80 +272,6 @@ static int cs4270_set_dai_fmt(struct snd_soc_codec_dai *codec_dai, | |||
155 | } | 272 | } |
156 | 273 | ||
157 | /* | 274 | /* |
158 | * The codec isn't really big-endian or little-endian, since the I2S | ||
159 | * interface requires data to be sent serially with the MSbit first. | ||
160 | * However, to support BE and LE I2S devices, we specify both here. That | ||
161 | * way, ALSA will always match the bit patterns. | ||
162 | */ | ||
163 | #define CS4270_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ | ||
164 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \ | ||
165 | SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \ | ||
166 | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \ | ||
167 | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \ | ||
168 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE) | ||
169 | |||
170 | #ifdef USE_I2C | ||
171 | |||
172 | /* CS4270 registers addresses */ | ||
173 | #define CS4270_CHIPID 0x01 /* Chip ID */ | ||
174 | #define CS4270_PWRCTL 0x02 /* Power Control */ | ||
175 | #define CS4270_MODE 0x03 /* Mode Control */ | ||
176 | #define CS4270_FORMAT 0x04 /* Serial Format, ADC/DAC Control */ | ||
177 | #define CS4270_TRANS 0x05 /* Transition Control */ | ||
178 | #define CS4270_MUTE 0x06 /* Mute Control */ | ||
179 | #define CS4270_VOLA 0x07 /* DAC Channel A Volume Control */ | ||
180 | #define CS4270_VOLB 0x08 /* DAC Channel B Volume Control */ | ||
181 | |||
182 | #define CS4270_FIRSTREG 0x01 | ||
183 | #define CS4270_LASTREG 0x08 | ||
184 | #define CS4270_NUMREGS (CS4270_LASTREG - CS4270_FIRSTREG + 1) | ||
185 | |||
186 | /* Bit masks for the CS4270 registers */ | ||
187 | #define CS4270_CHIPID_ID 0xF0 | ||
188 | #define CS4270_CHIPID_REV 0x0F | ||
189 | #define CS4270_PWRCTL_FREEZE 0x80 | ||
190 | #define CS4270_PWRCTL_PDN_ADC 0x20 | ||
191 | #define CS4270_PWRCTL_PDN_DAC 0x02 | ||
192 | #define CS4270_PWRCTL_PDN 0x01 | ||
193 | #define CS4270_MODE_SPEED_MASK 0x30 | ||
194 | #define CS4270_MODE_1X 0x00 | ||
195 | #define CS4270_MODE_2X 0x10 | ||
196 | #define CS4270_MODE_4X 0x20 | ||
197 | #define CS4270_MODE_SLAVE 0x30 | ||
198 | #define CS4270_MODE_DIV_MASK 0x0E | ||
199 | #define CS4270_MODE_DIV1 0x00 | ||
200 | #define CS4270_MODE_DIV15 0x02 | ||
201 | #define CS4270_MODE_DIV2 0x04 | ||
202 | #define CS4270_MODE_DIV3 0x06 | ||
203 | #define CS4270_MODE_DIV4 0x08 | ||
204 | #define CS4270_MODE_POPGUARD 0x01 | ||
205 | #define CS4270_FORMAT_FREEZE_A 0x80 | ||
206 | #define CS4270_FORMAT_FREEZE_B 0x40 | ||
207 | #define CS4270_FORMAT_LOOPBACK 0x20 | ||
208 | #define CS4270_FORMAT_DAC_MASK 0x18 | ||
209 | #define CS4270_FORMAT_DAC_LJ 0x00 | ||
210 | #define CS4270_FORMAT_DAC_I2S 0x08 | ||
211 | #define CS4270_FORMAT_DAC_RJ16 0x18 | ||
212 | #define CS4270_FORMAT_DAC_RJ24 0x10 | ||
213 | #define CS4270_FORMAT_ADC_MASK 0x01 | ||
214 | #define CS4270_FORMAT_ADC_LJ 0x00 | ||
215 | #define CS4270_FORMAT_ADC_I2S 0x01 | ||
216 | #define CS4270_TRANS_ONE_VOL 0x80 | ||
217 | #define CS4270_TRANS_SOFT 0x40 | ||
218 | #define CS4270_TRANS_ZERO 0x20 | ||
219 | #define CS4270_TRANS_INV_ADC_A 0x08 | ||
220 | #define CS4270_TRANS_INV_ADC_B 0x10 | ||
221 | #define CS4270_TRANS_INV_DAC_A 0x02 | ||
222 | #define CS4270_TRANS_INV_DAC_B 0x04 | ||
223 | #define CS4270_TRANS_DEEMPH 0x01 | ||
224 | #define CS4270_MUTE_AUTO 0x20 | ||
225 | #define CS4270_MUTE_ADC_A 0x08 | ||
226 | #define CS4270_MUTE_ADC_B 0x10 | ||
227 | #define CS4270_MUTE_POLARITY 0x04 | ||
228 | #define CS4270_MUTE_DAC_A 0x01 | ||
229 | #define CS4270_MUTE_DAC_B 0x02 | ||
230 | |||
231 | /* | ||
232 | * A list of addresses on which this CS4270 could use. I2C addresses are | 275 | * A list of addresses on which this CS4270 could use. I2C addresses are |
233 | * 7 bits. For the CS4270, the upper four bits are always 1001, and the | 276 | * 7 bits. For the CS4270, the upper four bits are always 1001, and the |
234 | * lower three bits are determined via the AD2, AD1, and AD0 pins | 277 | * lower three bits are determined via the AD2, AD1, and AD0 pins |
@@ -315,53 +358,6 @@ static int cs4270_i2c_write(struct snd_soc_codec *codec, unsigned int reg, | |||
315 | } | 358 | } |
316 | 359 | ||
317 | /* | 360 | /* |
318 | * Clock Ratio Selection for Master Mode with I2C enabled | ||
319 | * | ||
320 | * The data for this chart is taken from Table 5 of the CS4270 reference | ||
321 | * manual. | ||
322 | * | ||
323 | * This table is used to determine how to program the Mode Control register. | ||
324 | * It is also used by cs4270_set_dai_sysclk() to tell ALSA which sampling | ||
325 | * rates the CS4270 currently supports. | ||
326 | * | ||
327 | * Each element in this array corresponds to the ratios in mclk_ratios[]. | ||
328 | * These two arrays need to be in sync. | ||
329 | * | ||
330 | * 'speed_mode' is the corresponding bit pattern to be written to the | ||
331 | * MODE bits of the Mode Control Register | ||
332 | * | ||
333 | * 'mclk' is the corresponding bit pattern to be wirten to the MCLK bits of | ||
334 | * the Mode Control Register. | ||
335 | * | ||
336 | * In situations where a single ratio is represented by multiple speed | ||
337 | * modes, we favor the slowest speed. E.g, for a ratio of 128, we pick | ||
338 | * double-speed instead of quad-speed. However, the CS4270 errata states | ||
339 | * that Divide-By-1.5 can cause failures, so we avoid that mode where | ||
340 | * possible. | ||
341 | * | ||
342 | * ERRATA: There is an errata for the CS4270 where divide-by-1.5 does not | ||
343 | * work if VD = 3.3V. If this effects you, select the | ||
344 | * CONFIG_SND_SOC_CS4270_VD33_ERRATA Kconfig option, and the driver will | ||
345 | * never select any sample rates that require divide-by-1.5. | ||
346 | */ | ||
347 | static struct { | ||
348 | u8 speed_mode; | ||
349 | u8 mclk; | ||
350 | } cs4270_mode_ratios[NUM_MCLK_RATIOS] = { | ||
351 | {CS4270_MODE_4X, CS4270_MODE_DIV1}, /* 64 */ | ||
352 | #ifndef CONFIG_SND_SOC_CS4270_VD33_ERRATA | ||
353 | {CS4270_MODE_4X, CS4270_MODE_DIV15}, /* 96 */ | ||
354 | #endif | ||
355 | {CS4270_MODE_2X, CS4270_MODE_DIV1}, /* 128 */ | ||
356 | {CS4270_MODE_4X, CS4270_MODE_DIV3}, /* 192 */ | ||
357 | {CS4270_MODE_1X, CS4270_MODE_DIV1}, /* 256 */ | ||
358 | {CS4270_MODE_2X, CS4270_MODE_DIV3}, /* 384 */ | ||
359 | {CS4270_MODE_1X, CS4270_MODE_DIV2}, /* 512 */ | ||
360 | {CS4270_MODE_1X, CS4270_MODE_DIV3}, /* 768 */ | ||
361 | {CS4270_MODE_1X, CS4270_MODE_DIV4} /* 1024 */ | ||
362 | }; | ||
363 | |||
364 | /* | ||
365 | * Program the CS4270 with the given hardware parameters. | 361 | * Program the CS4270 with the given hardware parameters. |
366 | * | 362 | * |
367 | * The .dai_ops functions are used to provide board-specific data, like | 363 | * The .dai_ops functions are used to provide board-specific data, like |
@@ -388,7 +384,7 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream, | |||
388 | ratio = cs4270->mclk / rate; /* MCLK/LRCK ratio */ | 384 | ratio = cs4270->mclk / rate; /* MCLK/LRCK ratio */ |
389 | 385 | ||
390 | for (i = 0; i < NUM_MCLK_RATIOS; i++) { | 386 | for (i = 0; i < NUM_MCLK_RATIOS; i++) { |
391 | if (mclk_ratios[i] == ratio) | 387 | if (cs4270_mode_ratios[i].ratio == ratio) |
392 | break; | 388 | break; |
393 | } | 389 | } |
394 | 390 | ||
@@ -669,7 +665,7 @@ error: | |||
669 | return ret; | 665 | return ret; |
670 | } | 666 | } |
671 | 667 | ||
672 | #endif | 668 | #endif /* USE_I2C*/ |
673 | 669 | ||
674 | struct snd_soc_codec_dai cs4270_dai = { | 670 | struct snd_soc_codec_dai cs4270_dai = { |
675 | .name = "CS4270", | 671 | .name = "CS4270", |
@@ -687,10 +683,6 @@ struct snd_soc_codec_dai cs4270_dai = { | |||
687 | .rates = 0, | 683 | .rates = 0, |
688 | .formats = CS4270_FORMATS, | 684 | .formats = CS4270_FORMATS, |
689 | }, | 685 | }, |
690 | .dai_ops = { | ||
691 | .set_sysclk = cs4270_set_dai_sysclk, | ||
692 | .set_fmt = cs4270_set_dai_fmt, | ||
693 | } | ||
694 | }; | 686 | }; |
695 | EXPORT_SYMBOL_GPL(cs4270_dai); | 687 | EXPORT_SYMBOL_GPL(cs4270_dai); |
696 | 688 | ||
@@ -752,6 +744,8 @@ static int cs4270_probe(struct platform_device *pdev) | |||
752 | if (codec->control_data) { | 744 | if (codec->control_data) { |
753 | /* Initialize codec ops */ | 745 | /* Initialize codec ops */ |
754 | cs4270_dai.ops.hw_params = cs4270_hw_params; | 746 | cs4270_dai.ops.hw_params = cs4270_hw_params; |
747 | cs4270_dai.dai_ops.set_sysclk = cs4270_set_dai_sysclk; | ||
748 | cs4270_dai.dai_ops.set_fmt = cs4270_set_dai_fmt; | ||
755 | #ifdef CONFIG_SND_SOC_CS4270_HWMUTE | 749 | #ifdef CONFIG_SND_SOC_CS4270_HWMUTE |
756 | cs4270_dai.dai_ops.digital_mute = cs4270_mute; | 750 | cs4270_dai.dai_ops.digital_mute = cs4270_mute; |
757 | #endif | 751 | #endif |
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c new file mode 100644 index 000000000000..710e0287ef8c --- /dev/null +++ b/sound/soc/codecs/tlv320aic3x.c | |||
@@ -0,0 +1,1274 @@ | |||
1 | /* | ||
2 | * ALSA SoC TLV320AIC3X codec driver | ||
3 | * | ||
4 | * Author: Vladimir Barinov, <vbarinov@ru.mvista.com> | ||
5 | * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com> | ||
6 | * | ||
7 | * Based on sound/soc/codecs/wm8753.c by Liam Girdwood | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | * | ||
13 | * Notes: | ||
14 | * The AIC3X is a driver for a low power stereo audio | ||
15 | * codecs aic31, aic32, aic33. | ||
16 | * | ||
17 | * It supports full aic33 codec functionality. | ||
18 | * The compatibility with aic32, aic31 is as follows: | ||
19 | * aic32 | aic31 | ||
20 | * --------------------------------------- | ||
21 | * MONO_LOUT -> N/A | MONO_LOUT -> N/A | ||
22 | * | IN1L -> LINE1L | ||
23 | * | IN1R -> LINE1R | ||
24 | * | IN2L -> LINE2L | ||
25 | * | IN2R -> LINE2R | ||
26 | * | MIC3L/R -> N/A | ||
27 | * truncated internal functionality in | ||
28 | * accordance with documentation | ||
29 | * --------------------------------------- | ||
30 | * | ||
31 | * Hence the machine layer should disable unsupported inputs/outputs by | ||
32 | * snd_soc_dapm_set_endpoint(codec, "MONO_LOUT", 0), etc. | ||
33 | */ | ||
34 | |||
35 | #include <linux/module.h> | ||
36 | #include <linux/moduleparam.h> | ||
37 | #include <linux/init.h> | ||
38 | #include <linux/delay.h> | ||
39 | #include <linux/pm.h> | ||
40 | #include <linux/i2c.h> | ||
41 | #include <linux/platform_device.h> | ||
42 | #include <sound/core.h> | ||
43 | #include <sound/pcm.h> | ||
44 | #include <sound/pcm_params.h> | ||
45 | #include <sound/soc.h> | ||
46 | #include <sound/soc-dapm.h> | ||
47 | #include <sound/initval.h> | ||
48 | |||
49 | #include "tlv320aic3x.h" | ||
50 | |||
51 | #define AUDIO_NAME "aic3x" | ||
52 | #define AIC3X_VERSION "0.1" | ||
53 | |||
54 | /* codec private data */ | ||
55 | struct aic3x_priv { | ||
56 | unsigned int sysclk; | ||
57 | int master; | ||
58 | }; | ||
59 | |||
60 | /* | ||
61 | * AIC3X register cache | ||
62 | * We can't read the AIC3X register space when we are | ||
63 | * using 2 wire for device control, so we cache them instead. | ||
64 | * There is no point in caching the reset register | ||
65 | */ | ||
66 | static const u8 aic3x_reg[AIC3X_CACHEREGNUM] = { | ||
67 | 0x00, 0x00, 0x00, 0x10, /* 0 */ | ||
68 | 0x04, 0x00, 0x00, 0x00, /* 4 */ | ||
69 | 0x00, 0x00, 0x00, 0x01, /* 8 */ | ||
70 | 0x00, 0x00, 0x00, 0x80, /* 12 */ | ||
71 | 0x80, 0xff, 0xff, 0x78, /* 16 */ | ||
72 | 0x78, 0x78, 0x78, 0x78, /* 20 */ | ||
73 | 0x78, 0x00, 0x00, 0xfe, /* 24 */ | ||
74 | 0x00, 0x00, 0xfe, 0x00, /* 28 */ | ||
75 | 0x18, 0x18, 0x00, 0x00, /* 32 */ | ||
76 | 0x00, 0x00, 0x00, 0x00, /* 36 */ | ||
77 | 0x00, 0x00, 0x00, 0x80, /* 40 */ | ||
78 | 0x80, 0x00, 0x00, 0x00, /* 44 */ | ||
79 | 0x00, 0x00, 0x00, 0x04, /* 48 */ | ||
80 | 0x00, 0x00, 0x00, 0x00, /* 52 */ | ||
81 | 0x00, 0x00, 0x04, 0x00, /* 56 */ | ||
82 | 0x00, 0x00, 0x00, 0x00, /* 60 */ | ||
83 | 0x00, 0x04, 0x00, 0x00, /* 64 */ | ||
84 | 0x00, 0x00, 0x00, 0x00, /* 68 */ | ||
85 | 0x04, 0x00, 0x00, 0x00, /* 72 */ | ||
86 | 0x00, 0x00, 0x00, 0x00, /* 76 */ | ||
87 | 0x00, 0x00, 0x00, 0x00, /* 80 */ | ||
88 | 0x00, 0x00, 0x00, 0x00, /* 84 */ | ||
89 | 0x00, 0x00, 0x00, 0x00, /* 88 */ | ||
90 | 0x00, 0x00, 0x00, 0x00, /* 92 */ | ||
91 | 0x00, 0x00, 0x00, 0x00, /* 96 */ | ||
92 | 0x00, 0x00, 0x02, /* 100 */ | ||
93 | }; | ||
94 | |||
95 | /* | ||
96 | * read aic3x register cache | ||
97 | */ | ||
98 | static inline unsigned int aic3x_read_reg_cache(struct snd_soc_codec *codec, | ||
99 | unsigned int reg) | ||
100 | { | ||
101 | u8 *cache = codec->reg_cache; | ||
102 | if (reg >= AIC3X_CACHEREGNUM) | ||
103 | return -1; | ||
104 | return cache[reg]; | ||
105 | } | ||
106 | |||
107 | /* | ||
108 | * write aic3x register cache | ||
109 | */ | ||
110 | static inline void aic3x_write_reg_cache(struct snd_soc_codec *codec, | ||
111 | u8 reg, u8 value) | ||
112 | { | ||
113 | u8 *cache = codec->reg_cache; | ||
114 | if (reg >= AIC3X_CACHEREGNUM) | ||
115 | return; | ||
116 | cache[reg] = value; | ||
117 | } | ||
118 | |||
119 | /* | ||
120 | * write to the aic3x register space | ||
121 | */ | ||
122 | static int aic3x_write(struct snd_soc_codec *codec, unsigned int reg, | ||
123 | unsigned int value) | ||
124 | { | ||
125 | u8 data[2]; | ||
126 | |||
127 | /* data is | ||
128 | * D15..D8 aic3x register offset | ||
129 | * D7...D0 register data | ||
130 | */ | ||
131 | data[0] = reg & 0xff; | ||
132 | data[1] = value & 0xff; | ||
133 | |||
134 | aic3x_write_reg_cache(codec, data[0], data[1]); | ||
135 | if (codec->hw_write(codec->control_data, data, 2) == 2) | ||
136 | return 0; | ||
137 | else | ||
138 | return -EIO; | ||
139 | } | ||
140 | |||
141 | #define SOC_DAPM_SINGLE_AIC3X(xname, reg, shift, mask, invert) \ | ||
142 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ | ||
143 | .info = snd_soc_info_volsw, \ | ||
144 | .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw_aic3x, \ | ||
145 | .private_value = SOC_SINGLE_VALUE(reg, shift, mask, invert) } | ||
146 | |||
147 | /* | ||
148 | * All input lines are connected when !0xf and disconnected with 0xf bit field, | ||
149 | * so we have to use specific dapm_put call for input mixer | ||
150 | */ | ||
151 | static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol, | ||
152 | struct snd_ctl_elem_value *ucontrol) | ||
153 | { | ||
154 | struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); | ||
155 | int reg = kcontrol->private_value & 0xff; | ||
156 | int shift = (kcontrol->private_value >> 8) & 0x0f; | ||
157 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
158 | int invert = (kcontrol->private_value >> 24) & 0x01; | ||
159 | unsigned short val, val_mask; | ||
160 | int ret; | ||
161 | struct snd_soc_dapm_path *path; | ||
162 | int found = 0; | ||
163 | |||
164 | val = (ucontrol->value.integer.value[0] & mask); | ||
165 | |||
166 | mask = 0xf; | ||
167 | if (val) | ||
168 | val = mask; | ||
169 | |||
170 | if (invert) | ||
171 | val = mask - val; | ||
172 | val_mask = mask << shift; | ||
173 | val = val << shift; | ||
174 | |||
175 | mutex_lock(&widget->codec->mutex); | ||
176 | |||
177 | if (snd_soc_test_bits(widget->codec, reg, val_mask, val)) { | ||
178 | /* find dapm widget path assoc with kcontrol */ | ||
179 | list_for_each_entry(path, &widget->codec->dapm_paths, list) { | ||
180 | if (path->kcontrol != kcontrol) | ||
181 | continue; | ||
182 | |||
183 | /* found, now check type */ | ||
184 | found = 1; | ||
185 | if (val) | ||
186 | /* new connection */ | ||
187 | path->connect = invert ? 0 : 1; | ||
188 | else | ||
189 | /* old connection must be powered down */ | ||
190 | path->connect = invert ? 1 : 0; | ||
191 | break; | ||
192 | } | ||
193 | |||
194 | if (found) | ||
195 | snd_soc_dapm_sync_endpoints(widget->codec); | ||
196 | } | ||
197 | |||
198 | ret = snd_soc_update_bits(widget->codec, reg, val_mask, val); | ||
199 | |||
200 | mutex_unlock(&widget->codec->mutex); | ||
201 | return ret; | ||
202 | } | ||
203 | |||
204 | static const char *aic3x_left_dac_mux[] = { "DAC_L1", "DAC_L3", "DAC_L2" }; | ||
205 | static const char *aic3x_right_dac_mux[] = { "DAC_R1", "DAC_R3", "DAC_R2" }; | ||
206 | static const char *aic3x_left_hpcom_mux[] = | ||
207 | { "differential of HPLOUT", "constant VCM", "single-ended" }; | ||
208 | static const char *aic3x_right_hpcom_mux[] = | ||
209 | { "differential of HPROUT", "constant VCM", "single-ended", | ||
210 | "differential of HPLCOM", "external feedback" }; | ||
211 | static const char *aic3x_linein_mode_mux[] = { "single-ended", "differential" }; | ||
212 | |||
213 | #define LDAC_ENUM 0 | ||
214 | #define RDAC_ENUM 1 | ||
215 | #define LHPCOM_ENUM 2 | ||
216 | #define RHPCOM_ENUM 3 | ||
217 | #define LINE1L_ENUM 4 | ||
218 | #define LINE1R_ENUM 5 | ||
219 | #define LINE2L_ENUM 6 | ||
220 | #define LINE2R_ENUM 7 | ||
221 | |||
222 | static const struct soc_enum aic3x_enum[] = { | ||
223 | SOC_ENUM_SINGLE(DAC_LINE_MUX, 6, 3, aic3x_left_dac_mux), | ||
224 | SOC_ENUM_SINGLE(DAC_LINE_MUX, 4, 3, aic3x_right_dac_mux), | ||
225 | SOC_ENUM_SINGLE(HPLCOM_CFG, 4, 3, aic3x_left_hpcom_mux), | ||
226 | SOC_ENUM_SINGLE(HPRCOM_CFG, 3, 5, aic3x_right_hpcom_mux), | ||
227 | SOC_ENUM_SINGLE(LINE1L_2_LADC_CTRL, 7, 2, aic3x_linein_mode_mux), | ||
228 | SOC_ENUM_SINGLE(LINE1R_2_RADC_CTRL, 7, 2, aic3x_linein_mode_mux), | ||
229 | SOC_ENUM_SINGLE(LINE2L_2_LADC_CTRL, 7, 2, aic3x_linein_mode_mux), | ||
230 | SOC_ENUM_SINGLE(LINE2R_2_RADC_CTRL, 7, 2, aic3x_linein_mode_mux), | ||
231 | }; | ||
232 | |||
233 | static const struct snd_kcontrol_new aic3x_snd_controls[] = { | ||
234 | /* Output */ | ||
235 | SOC_DOUBLE_R("PCM Playback Volume", LDAC_VOL, RDAC_VOL, 0, 0x7f, 1), | ||
236 | |||
237 | SOC_DOUBLE_R("Line DAC Playback Volume", DACL1_2_LLOPM_VOL, | ||
238 | DACR1_2_RLOPM_VOL, 0, 0x7f, 1), | ||
239 | SOC_DOUBLE_R("Line DAC Playback Switch", LLOPM_CTRL, RLOPM_CTRL, 3, | ||
240 | 0x01, 0), | ||
241 | SOC_DOUBLE_R("Line PGA Bypass Playback Volume", PGAL_2_LLOPM_VOL, | ||
242 | PGAR_2_RLOPM_VOL, 0, 0x7f, 1), | ||
243 | SOC_DOUBLE_R("Line Line2 Bypass Playback Volume", LINE2L_2_LLOPM_VOL, | ||
244 | LINE2R_2_RLOPM_VOL, 0, 0x7f, 1), | ||
245 | |||
246 | SOC_DOUBLE_R("Mono DAC Playback Volume", DACL1_2_MONOLOPM_VOL, | ||
247 | DACR1_2_MONOLOPM_VOL, 0, 0x7f, 1), | ||
248 | SOC_SINGLE("Mono DAC Playback Switch", MONOLOPM_CTRL, 3, 0x01, 0), | ||
249 | SOC_DOUBLE_R("Mono PGA Bypass Playback Volume", PGAL_2_MONOLOPM_VOL, | ||
250 | PGAR_2_MONOLOPM_VOL, 0, 0x7f, 1), | ||
251 | SOC_DOUBLE_R("Mono Line2 Bypass Playback Volume", LINE2L_2_MONOLOPM_VOL, | ||
252 | LINE2R_2_MONOLOPM_VOL, 0, 0x7f, 1), | ||
253 | |||
254 | SOC_DOUBLE_R("HP DAC Playback Volume", DACL1_2_HPLOUT_VOL, | ||
255 | DACR1_2_HPROUT_VOL, 0, 0x7f, 1), | ||
256 | SOC_DOUBLE_R("HP DAC Playback Switch", HPLOUT_CTRL, HPROUT_CTRL, 3, | ||
257 | 0x01, 0), | ||
258 | SOC_DOUBLE_R("HP PGA Bypass Playback Volume", PGAL_2_HPLOUT_VOL, | ||
259 | PGAR_2_HPROUT_VOL, 0, 0x7f, 1), | ||
260 | SOC_DOUBLE_R("HP Line2 Bypass Playback Volume", LINE2L_2_HPLOUT_VOL, | ||
261 | LINE2R_2_HPROUT_VOL, 0, 0x7f, 1), | ||
262 | |||
263 | SOC_DOUBLE_R("HPCOM DAC Playback Volume", DACL1_2_HPLCOM_VOL, | ||
264 | DACR1_2_HPRCOM_VOL, 0, 0x7f, 1), | ||
265 | SOC_DOUBLE_R("HPCOM DAC Playback Switch", HPLCOM_CTRL, HPRCOM_CTRL, 3, | ||
266 | 0x01, 0), | ||
267 | SOC_DOUBLE_R("HPCOM PGA Bypass Playback Volume", PGAL_2_HPLCOM_VOL, | ||
268 | PGAR_2_HPRCOM_VOL, 0, 0x7f, 1), | ||
269 | SOC_DOUBLE_R("HPCOM Line2 Bypass Playback Volume", LINE2L_2_HPLCOM_VOL, | ||
270 | LINE2R_2_HPRCOM_VOL, 0, 0x7f, 1), | ||
271 | |||
272 | /* | ||
273 | * Note: enable Automatic input Gain Controller with care. It can | ||
274 | * adjust PGA to max value when ADC is on and will never go back. | ||
275 | */ | ||
276 | SOC_DOUBLE_R("AGC Switch", LAGC_CTRL_A, RAGC_CTRL_A, 7, 0x01, 0), | ||
277 | |||
278 | /* Input */ | ||
279 | SOC_DOUBLE_R("PGA Capture Volume", LADC_VOL, RADC_VOL, 0, 0x7f, 0), | ||
280 | SOC_DOUBLE_R("PGA Capture Switch", LADC_VOL, RADC_VOL, 7, 0x01, 1), | ||
281 | }; | ||
282 | |||
283 | /* add non dapm controls */ | ||
284 | static int aic3x_add_controls(struct snd_soc_codec *codec) | ||
285 | { | ||
286 | int err, i; | ||
287 | |||
288 | for (i = 0; i < ARRAY_SIZE(aic3x_snd_controls); i++) { | ||
289 | err = snd_ctl_add(codec->card, | ||
290 | snd_soc_cnew(&aic3x_snd_controls[i], | ||
291 | codec, NULL)); | ||
292 | if (err < 0) | ||
293 | return err; | ||
294 | } | ||
295 | |||
296 | return 0; | ||
297 | } | ||
298 | |||
299 | /* Left DAC Mux */ | ||
300 | static const struct snd_kcontrol_new aic3x_left_dac_mux_controls = | ||
301 | SOC_DAPM_ENUM("Route", aic3x_enum[LDAC_ENUM]); | ||
302 | |||
303 | /* Right DAC Mux */ | ||
304 | static const struct snd_kcontrol_new aic3x_right_dac_mux_controls = | ||
305 | SOC_DAPM_ENUM("Route", aic3x_enum[RDAC_ENUM]); | ||
306 | |||
307 | /* Left HPCOM Mux */ | ||
308 | static const struct snd_kcontrol_new aic3x_left_hpcom_mux_controls = | ||
309 | SOC_DAPM_ENUM("Route", aic3x_enum[LHPCOM_ENUM]); | ||
310 | |||
311 | /* Right HPCOM Mux */ | ||
312 | static const struct snd_kcontrol_new aic3x_right_hpcom_mux_controls = | ||
313 | SOC_DAPM_ENUM("Route", aic3x_enum[RHPCOM_ENUM]); | ||
314 | |||
315 | /* Left DAC_L1 Mixer */ | ||
316 | static const struct snd_kcontrol_new aic3x_left_dac_mixer_controls[] = { | ||
317 | SOC_DAPM_SINGLE("Line Switch", DACL1_2_LLOPM_VOL, 7, 1, 0), | ||
318 | SOC_DAPM_SINGLE("Mono Switch", DACL1_2_MONOLOPM_VOL, 7, 1, 0), | ||
319 | SOC_DAPM_SINGLE("HP Switch", DACL1_2_HPLOUT_VOL, 7, 1, 0), | ||
320 | SOC_DAPM_SINGLE("HPCOM Switch", DACL1_2_HPLCOM_VOL, 7, 1, 0), | ||
321 | }; | ||
322 | |||
323 | /* Right DAC_R1 Mixer */ | ||
324 | static const struct snd_kcontrol_new aic3x_right_dac_mixer_controls[] = { | ||
325 | SOC_DAPM_SINGLE("Line Switch", DACR1_2_RLOPM_VOL, 7, 1, 0), | ||
326 | SOC_DAPM_SINGLE("Mono Switch", DACR1_2_MONOLOPM_VOL, 7, 1, 0), | ||
327 | SOC_DAPM_SINGLE("HP Switch", DACR1_2_HPROUT_VOL, 7, 1, 0), | ||
328 | SOC_DAPM_SINGLE("HPCOM Switch", DACR1_2_HPRCOM_VOL, 7, 1, 0), | ||
329 | }; | ||
330 | |||
331 | /* Left PGA Mixer */ | ||
332 | static const struct snd_kcontrol_new aic3x_left_pga_mixer_controls[] = { | ||
333 | SOC_DAPM_SINGLE_AIC3X("Line1L Switch", LINE1L_2_LADC_CTRL, 3, 1, 1), | ||
334 | SOC_DAPM_SINGLE_AIC3X("Line2L Switch", LINE2L_2_LADC_CTRL, 3, 1, 1), | ||
335 | SOC_DAPM_SINGLE_AIC3X("Mic3L Switch", MIC3LR_2_LADC_CTRL, 4, 1, 1), | ||
336 | }; | ||
337 | |||
338 | /* Right PGA Mixer */ | ||
339 | static const struct snd_kcontrol_new aic3x_right_pga_mixer_controls[] = { | ||
340 | SOC_DAPM_SINGLE_AIC3X("Line1R Switch", LINE1R_2_RADC_CTRL, 3, 1, 1), | ||
341 | SOC_DAPM_SINGLE_AIC3X("Line2R Switch", LINE2R_2_RADC_CTRL, 3, 1, 1), | ||
342 | SOC_DAPM_SINGLE_AIC3X("Mic3R Switch", MIC3LR_2_RADC_CTRL, 0, 1, 1), | ||
343 | }; | ||
344 | |||
345 | /* Left Line1 Mux */ | ||
346 | static const struct snd_kcontrol_new aic3x_left_line1_mux_controls = | ||
347 | SOC_DAPM_ENUM("Route", aic3x_enum[LINE1L_ENUM]); | ||
348 | |||
349 | /* Right Line1 Mux */ | ||
350 | static const struct snd_kcontrol_new aic3x_right_line1_mux_controls = | ||
351 | SOC_DAPM_ENUM("Route", aic3x_enum[LINE1R_ENUM]); | ||
352 | |||
353 | /* Left Line2 Mux */ | ||
354 | static const struct snd_kcontrol_new aic3x_left_line2_mux_controls = | ||
355 | SOC_DAPM_ENUM("Route", aic3x_enum[LINE2L_ENUM]); | ||
356 | |||
357 | /* Right Line2 Mux */ | ||
358 | static const struct snd_kcontrol_new aic3x_right_line2_mux_controls = | ||
359 | SOC_DAPM_ENUM("Route", aic3x_enum[LINE2R_ENUM]); | ||
360 | |||
361 | /* Left PGA Bypass Mixer */ | ||
362 | static const struct snd_kcontrol_new aic3x_left_pga_bp_mixer_controls[] = { | ||
363 | SOC_DAPM_SINGLE("Line Switch", PGAL_2_LLOPM_VOL, 7, 1, 0), | ||
364 | SOC_DAPM_SINGLE("Mono Switch", PGAL_2_MONOLOPM_VOL, 7, 1, 0), | ||
365 | SOC_DAPM_SINGLE("HP Switch", PGAL_2_HPLOUT_VOL, 7, 1, 0), | ||
366 | SOC_DAPM_SINGLE("HPCOM Switch", PGAL_2_HPLCOM_VOL, 7, 1, 0), | ||
367 | }; | ||
368 | |||
369 | /* Right PGA Bypass Mixer */ | ||
370 | static const struct snd_kcontrol_new aic3x_right_pga_bp_mixer_controls[] = { | ||
371 | SOC_DAPM_SINGLE("Line Switch", PGAR_2_RLOPM_VOL, 7, 1, 0), | ||
372 | SOC_DAPM_SINGLE("Mono Switch", PGAR_2_MONOLOPM_VOL, 7, 1, 0), | ||
373 | SOC_DAPM_SINGLE("HP Switch", PGAR_2_HPROUT_VOL, 7, 1, 0), | ||
374 | SOC_DAPM_SINGLE("HPCOM Switch", PGAR_2_HPRCOM_VOL, 7, 1, 0), | ||
375 | }; | ||
376 | |||
377 | /* Left Line2 Bypass Mixer */ | ||
378 | static const struct snd_kcontrol_new aic3x_left_line2_bp_mixer_controls[] = { | ||
379 | SOC_DAPM_SINGLE("Line Switch", LINE2L_2_LLOPM_VOL, 7, 1, 0), | ||
380 | SOC_DAPM_SINGLE("Mono Switch", LINE2L_2_MONOLOPM_VOL, 7, 1, 0), | ||
381 | SOC_DAPM_SINGLE("HP Switch", LINE2L_2_HPLOUT_VOL, 7, 1, 0), | ||
382 | SOC_DAPM_SINGLE("HPCOM Switch", LINE2L_2_HPLCOM_VOL, 7, 1, 0), | ||
383 | }; | ||
384 | |||
385 | /* Right Line2 Bypass Mixer */ | ||
386 | static const struct snd_kcontrol_new aic3x_right_line2_bp_mixer_controls[] = { | ||
387 | SOC_DAPM_SINGLE("Line Switch", LINE2R_2_RLOPM_VOL, 7, 1, 0), | ||
388 | SOC_DAPM_SINGLE("Mono Switch", LINE2R_2_MONOLOPM_VOL, 7, 1, 0), | ||
389 | SOC_DAPM_SINGLE("HP Switch", LINE2R_2_HPROUT_VOL, 7, 1, 0), | ||
390 | SOC_DAPM_SINGLE("HPCOM Switch", LINE2R_2_HPRCOM_VOL, 7, 1, 0), | ||
391 | }; | ||
392 | |||
393 | static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = { | ||
394 | /* Left DAC to Left Outputs */ | ||
395 | SND_SOC_DAPM_DAC("Left DAC", "Left Playback", DAC_PWR, 7, 0), | ||
396 | SND_SOC_DAPM_MUX("Left DAC Mux", SND_SOC_NOPM, 0, 0, | ||
397 | &aic3x_left_dac_mux_controls), | ||
398 | SND_SOC_DAPM_MIXER("Left DAC_L1 Mixer", SND_SOC_NOPM, 0, 0, | ||
399 | &aic3x_left_dac_mixer_controls[0], | ||
400 | ARRAY_SIZE(aic3x_left_dac_mixer_controls)), | ||
401 | SND_SOC_DAPM_MUX("Left HPCOM Mux", SND_SOC_NOPM, 0, 0, | ||
402 | &aic3x_left_hpcom_mux_controls), | ||
403 | SND_SOC_DAPM_PGA("Left Line Out", LLOPM_CTRL, 0, 0, NULL, 0), | ||
404 | SND_SOC_DAPM_PGA("Left HP Out", HPLOUT_CTRL, 0, 0, NULL, 0), | ||
405 | SND_SOC_DAPM_PGA("Left HP Com", HPLCOM_CTRL, 0, 0, NULL, 0), | ||
406 | |||
407 | /* Right DAC to Right Outputs */ | ||
408 | SND_SOC_DAPM_DAC("Right DAC", "Right Playback", DAC_PWR, 6, 0), | ||
409 | SND_SOC_DAPM_MUX("Right DAC Mux", SND_SOC_NOPM, 0, 0, | ||
410 | &aic3x_right_dac_mux_controls), | ||
411 | SND_SOC_DAPM_MIXER("Right DAC_R1 Mixer", SND_SOC_NOPM, 0, 0, | ||
412 | &aic3x_right_dac_mixer_controls[0], | ||
413 | ARRAY_SIZE(aic3x_right_dac_mixer_controls)), | ||
414 | SND_SOC_DAPM_MUX("Right HPCOM Mux", SND_SOC_NOPM, 0, 0, | ||
415 | &aic3x_right_hpcom_mux_controls), | ||
416 | SND_SOC_DAPM_PGA("Right Line Out", RLOPM_CTRL, 0, 0, NULL, 0), | ||
417 | SND_SOC_DAPM_PGA("Right HP Out", HPROUT_CTRL, 0, 0, NULL, 0), | ||
418 | SND_SOC_DAPM_PGA("Right HP Com", HPRCOM_CTRL, 0, 0, NULL, 0), | ||
419 | |||
420 | /* Mono Output */ | ||
421 | SND_SOC_DAPM_PGA("Mono Out", MONOLOPM_CTRL, 0, 0, NULL, 0), | ||
422 | |||
423 | /* Left Inputs to Left ADC */ | ||
424 | SND_SOC_DAPM_ADC("Left ADC", "Left Capture", LINE1L_2_LADC_CTRL, 2, 0), | ||
425 | SND_SOC_DAPM_MIXER("Left PGA Mixer", SND_SOC_NOPM, 0, 0, | ||
426 | &aic3x_left_pga_mixer_controls[0], | ||
427 | ARRAY_SIZE(aic3x_left_pga_mixer_controls)), | ||
428 | SND_SOC_DAPM_MUX("Left Line1L Mux", SND_SOC_NOPM, 0, 0, | ||
429 | &aic3x_left_line1_mux_controls), | ||
430 | SND_SOC_DAPM_MUX("Left Line2L Mux", SND_SOC_NOPM, 0, 0, | ||
431 | &aic3x_left_line2_mux_controls), | ||
432 | |||
433 | /* Right Inputs to Right ADC */ | ||
434 | SND_SOC_DAPM_ADC("Right ADC", "Right Capture", | ||
435 | LINE1R_2_RADC_CTRL, 2, 0), | ||
436 | SND_SOC_DAPM_MIXER("Right PGA Mixer", SND_SOC_NOPM, 0, 0, | ||
437 | &aic3x_right_pga_mixer_controls[0], | ||
438 | ARRAY_SIZE(aic3x_right_pga_mixer_controls)), | ||
439 | SND_SOC_DAPM_MUX("Right Line1R Mux", SND_SOC_NOPM, 0, 0, | ||
440 | &aic3x_right_line1_mux_controls), | ||
441 | SND_SOC_DAPM_MUX("Right Line2R Mux", SND_SOC_NOPM, 0, 0, | ||
442 | &aic3x_right_line2_mux_controls), | ||
443 | |||
444 | /* Mic Bias */ | ||
445 | SND_SOC_DAPM_MICBIAS("Mic Bias 2V", MICBIAS_CTRL, 6, 0), | ||
446 | SND_SOC_DAPM_MICBIAS("Mic Bias 2.5V", MICBIAS_CTRL, 7, 0), | ||
447 | SND_SOC_DAPM_MICBIAS("Mic Bias AVDD", MICBIAS_CTRL, 6, 0), | ||
448 | SND_SOC_DAPM_MICBIAS("Mic Bias AVDD", MICBIAS_CTRL, 7, 0), | ||
449 | |||
450 | /* Left PGA to Left Output bypass */ | ||
451 | SND_SOC_DAPM_MIXER("Left PGA Bypass Mixer", SND_SOC_NOPM, 0, 0, | ||
452 | &aic3x_left_pga_bp_mixer_controls[0], | ||
453 | ARRAY_SIZE(aic3x_left_pga_bp_mixer_controls)), | ||
454 | |||
455 | /* Right PGA to Right Output bypass */ | ||
456 | SND_SOC_DAPM_MIXER("Right PGA Bypass Mixer", SND_SOC_NOPM, 0, 0, | ||
457 | &aic3x_right_pga_bp_mixer_controls[0], | ||
458 | ARRAY_SIZE(aic3x_right_pga_bp_mixer_controls)), | ||
459 | |||
460 | /* Left Line2 to Left Output bypass */ | ||
461 | SND_SOC_DAPM_MIXER("Left Line2 Bypass Mixer", SND_SOC_NOPM, 0, 0, | ||
462 | &aic3x_left_line2_bp_mixer_controls[0], | ||
463 | ARRAY_SIZE(aic3x_left_line2_bp_mixer_controls)), | ||
464 | |||
465 | /* Right Line2 to Right Output bypass */ | ||
466 | SND_SOC_DAPM_MIXER("Right Line2 Bypass Mixer", SND_SOC_NOPM, 0, 0, | ||
467 | &aic3x_right_line2_bp_mixer_controls[0], | ||
468 | ARRAY_SIZE(aic3x_right_line2_bp_mixer_controls)), | ||
469 | |||
470 | SND_SOC_DAPM_OUTPUT("LLOUT"), | ||
471 | SND_SOC_DAPM_OUTPUT("RLOUT"), | ||
472 | SND_SOC_DAPM_OUTPUT("MONO_LOUT"), | ||
473 | SND_SOC_DAPM_OUTPUT("HPLOUT"), | ||
474 | SND_SOC_DAPM_OUTPUT("HPROUT"), | ||
475 | SND_SOC_DAPM_OUTPUT("HPLCOM"), | ||
476 | SND_SOC_DAPM_OUTPUT("HPRCOM"), | ||
477 | |||
478 | SND_SOC_DAPM_INPUT("MIC3L"), | ||
479 | SND_SOC_DAPM_INPUT("MIC3R"), | ||
480 | SND_SOC_DAPM_INPUT("LINE1L"), | ||
481 | SND_SOC_DAPM_INPUT("LINE1R"), | ||
482 | SND_SOC_DAPM_INPUT("LINE2L"), | ||
483 | SND_SOC_DAPM_INPUT("LINE2R"), | ||
484 | }; | ||
485 | |||
486 | static const char *intercon[][3] = { | ||
487 | /* Left Output */ | ||
488 | {"Left DAC Mux", "DAC_L1", "Left DAC"}, | ||
489 | {"Left DAC Mux", "DAC_L2", "Left DAC"}, | ||
490 | {"Left DAC Mux", "DAC_L3", "Left DAC"}, | ||
491 | |||
492 | {"Left DAC_L1 Mixer", "Line Switch", "Left DAC Mux"}, | ||
493 | {"Left DAC_L1 Mixer", "Mono Switch", "Left DAC Mux"}, | ||
494 | {"Left DAC_L1 Mixer", "HP Switch", "Left DAC Mux"}, | ||
495 | {"Left DAC_L1 Mixer", "HPCOM Switch", "Left DAC Mux"}, | ||
496 | {"Left Line Out", NULL, "Left DAC Mux"}, | ||
497 | {"Left HP Out", NULL, "Left DAC Mux"}, | ||
498 | |||
499 | {"Left HPCOM Mux", "differential of HPLOUT", "Left DAC_L1 Mixer"}, | ||
500 | {"Left HPCOM Mux", "constant VCM", "Left DAC_L1 Mixer"}, | ||
501 | {"Left HPCOM Mux", "single-ended", "Left DAC_L1 Mixer"}, | ||
502 | |||
503 | {"Left Line Out", NULL, "Left DAC_L1 Mixer"}, | ||
504 | {"Mono Out", NULL, "Left DAC_L1 Mixer"}, | ||
505 | {"Left HP Out", NULL, "Left DAC_L1 Mixer"}, | ||
506 | {"Left HP Com", NULL, "Left HPCOM Mux"}, | ||
507 | |||
508 | {"LLOUT", NULL, "Left Line Out"}, | ||
509 | {"LLOUT", NULL, "Left Line Out"}, | ||
510 | {"HPLOUT", NULL, "Left HP Out"}, | ||
511 | {"HPLCOM", NULL, "Left HP Com"}, | ||
512 | |||
513 | /* Right Output */ | ||
514 | {"Right DAC Mux", "DAC_R1", "Right DAC"}, | ||
515 | {"Right DAC Mux", "DAC_R2", "Right DAC"}, | ||
516 | {"Right DAC Mux", "DAC_R3", "Right DAC"}, | ||
517 | |||
518 | {"Right DAC_R1 Mixer", "Line Switch", "Right DAC Mux"}, | ||
519 | {"Right DAC_R1 Mixer", "Mono Switch", "Right DAC Mux"}, | ||
520 | {"Right DAC_R1 Mixer", "HP Switch", "Right DAC Mux"}, | ||
521 | {"Right DAC_R1 Mixer", "HPCOM Switch", "Right DAC Mux"}, | ||
522 | {"Right Line Out", NULL, "Right DAC Mux"}, | ||
523 | {"Right HP Out", NULL, "Right DAC Mux"}, | ||
524 | |||
525 | {"Right HPCOM Mux", "differential of HPROUT", "Right DAC_R1 Mixer"}, | ||
526 | {"Right HPCOM Mux", "constant VCM", "Right DAC_R1 Mixer"}, | ||
527 | {"Right HPCOM Mux", "single-ended", "Right DAC_R1 Mixer"}, | ||
528 | {"Right HPCOM Mux", "differential of HPLCOM", "Right DAC_R1 Mixer"}, | ||
529 | {"Right HPCOM Mux", "external feedback", "Right DAC_R1 Mixer"}, | ||
530 | |||
531 | {"Right Line Out", NULL, "Right DAC_R1 Mixer"}, | ||
532 | {"Mono Out", NULL, "Right DAC_R1 Mixer"}, | ||
533 | {"Right HP Out", NULL, "Right DAC_R1 Mixer"}, | ||
534 | {"Right HP Com", NULL, "Right HPCOM Mux"}, | ||
535 | |||
536 | {"RLOUT", NULL, "Right Line Out"}, | ||
537 | {"RLOUT", NULL, "Right Line Out"}, | ||
538 | {"HPROUT", NULL, "Right HP Out"}, | ||
539 | {"HPRCOM", NULL, "Right HP Com"}, | ||
540 | |||
541 | /* Mono Output */ | ||
542 | {"MONOLOUT", NULL, "Mono Out"}, | ||
543 | {"MONOLOUT", NULL, "Mono Out"}, | ||
544 | |||
545 | /* Left Input */ | ||
546 | {"Left Line1L Mux", "single-ended", "LINE1L"}, | ||
547 | {"Left Line1L Mux", "differential", "LINE1L"}, | ||
548 | |||
549 | {"Left Line2L Mux", "single-ended", "LINE2L"}, | ||
550 | {"Left Line2L Mux", "differential", "LINE2L"}, | ||
551 | |||
552 | {"Left PGA Mixer", "Line1L Switch", "Left Line1L Mux"}, | ||
553 | {"Left PGA Mixer", "Line2L Switch", "Left Line2L Mux"}, | ||
554 | {"Left PGA Mixer", "Mic3L Switch", "MIC3L"}, | ||
555 | |||
556 | {"Left ADC", NULL, "Left PGA Mixer"}, | ||
557 | |||
558 | /* Right Input */ | ||
559 | {"Right Line1R Mux", "single-ended", "LINE1R"}, | ||
560 | {"Right Line1R Mux", "differential", "LINE1R"}, | ||
561 | |||
562 | {"Right Line2R Mux", "single-ended", "LINE2R"}, | ||
563 | {"Right Line2R Mux", "differential", "LINE2R"}, | ||
564 | |||
565 | {"Right PGA Mixer", "Line1R Switch", "Right Line1R Mux"}, | ||
566 | {"Right PGA Mixer", "Line2R Switch", "Right Line2R Mux"}, | ||
567 | {"Right PGA Mixer", "Mic3R Switch", "MIC3R"}, | ||
568 | |||
569 | {"Right ADC", NULL, "Right PGA Mixer"}, | ||
570 | |||
571 | /* Left PGA Bypass */ | ||
572 | {"Left PGA Bypass Mixer", "Line Switch", "Left PGA Mixer"}, | ||
573 | {"Left PGA Bypass Mixer", "Mono Switch", "Left PGA Mixer"}, | ||
574 | {"Left PGA Bypass Mixer", "HP Switch", "Left PGA Mixer"}, | ||
575 | {"Left PGA Bypass Mixer", "HPCOM Switch", "Left PGA Mixer"}, | ||
576 | |||
577 | {"Left HPCOM Mux", "differential of HPLOUT", "Left PGA Bypass Mixer"}, | ||
578 | {"Left HPCOM Mux", "constant VCM", "Left PGA Bypass Mixer"}, | ||
579 | {"Left HPCOM Mux", "single-ended", "Left PGA Bypass Mixer"}, | ||
580 | |||
581 | {"Left Line Out", NULL, "Left PGA Bypass Mixer"}, | ||
582 | {"Mono Out", NULL, "Left PGA Bypass Mixer"}, | ||
583 | {"Left HP Out", NULL, "Left PGA Bypass Mixer"}, | ||
584 | |||
585 | /* Right PGA Bypass */ | ||
586 | {"Right PGA Bypass Mixer", "Line Switch", "Right PGA Mixer"}, | ||
587 | {"Right PGA Bypass Mixer", "Mono Switch", "Right PGA Mixer"}, | ||
588 | {"Right PGA Bypass Mixer", "HP Switch", "Right PGA Mixer"}, | ||
589 | {"Right PGA Bypass Mixer", "HPCOM Switch", "Right PGA Mixer"}, | ||
590 | |||
591 | {"Right HPCOM Mux", "differential of HPROUT", "Right PGA Bypass Mixer"}, | ||
592 | {"Right HPCOM Mux", "constant VCM", "Right PGA Bypass Mixer"}, | ||
593 | {"Right HPCOM Mux", "single-ended", "Right PGA Bypass Mixer"}, | ||
594 | {"Right HPCOM Mux", "differential of HPLCOM", "Right PGA Bypass Mixer"}, | ||
595 | {"Right HPCOM Mux", "external feedback", "Right PGA Bypass Mixer"}, | ||
596 | |||
597 | {"Right Line Out", NULL, "Right PGA Bypass Mixer"}, | ||
598 | {"Mono Out", NULL, "Right PGA Bypass Mixer"}, | ||
599 | {"Right HP Out", NULL, "Right PGA Bypass Mixer"}, | ||
600 | |||
601 | /* Left Line2 Bypass */ | ||
602 | {"Left Line2 Bypass Mixer", "Line Switch", "Left Line2L Mux"}, | ||
603 | {"Left Line2 Bypass Mixer", "Mono Switch", "Left Line2L Mux"}, | ||
604 | {"Left Line2 Bypass Mixer", "HP Switch", "Left Line2L Mux"}, | ||
605 | {"Left Line2 Bypass Mixer", "HPCOM Switch", "Left Line2L Mux"}, | ||
606 | |||
607 | {"Left HPCOM Mux", "differential of HPLOUT", "Left Line2 Bypass Mixer"}, | ||
608 | {"Left HPCOM Mux", "constant VCM", "Left Line2 Bypass Mixer"}, | ||
609 | {"Left HPCOM Mux", "single-ended", "Left Line2 Bypass Mixer"}, | ||
610 | |||
611 | {"Left Line Out", NULL, "Left Line2 Bypass Mixer"}, | ||
612 | {"Mono Out", NULL, "Left Line2 Bypass Mixer"}, | ||
613 | {"Left HP Out", NULL, "Left Line2 Bypass Mixer"}, | ||
614 | |||
615 | /* Right Line2 Bypass */ | ||
616 | {"Right Line2 Bypass Mixer", "Line Switch", "Right Line2R Mux"}, | ||
617 | {"Right Line2 Bypass Mixer", "Mono Switch", "Right Line2R Mux"}, | ||
618 | {"Right Line2 Bypass Mixer", "HP Switch", "Right Line2R Mux"}, | ||
619 | {"Right Line2 Bypass Mixer", "HPCOM Switch", "Right Line2R Mux"}, | ||
620 | |||
621 | {"Right HPCOM Mux", "differential of HPROUT", "Right Line2 Bypass Mixer"}, | ||
622 | {"Right HPCOM Mux", "constant VCM", "Right Line2 Bypass Mixer"}, | ||
623 | {"Right HPCOM Mux", "single-ended", "Right Line2 Bypass Mixer"}, | ||
624 | {"Right HPCOM Mux", "differential of HPLCOM", "Right Line2 Bypass Mixer"}, | ||
625 | {"Right HPCOM Mux", "external feedback", "Right Line2 Bypass Mixer"}, | ||
626 | |||
627 | {"Right Line Out", NULL, "Right Line2 Bypass Mixer"}, | ||
628 | {"Mono Out", NULL, "Right Line2 Bypass Mixer"}, | ||
629 | {"Right HP Out", NULL, "Right Line2 Bypass Mixer"}, | ||
630 | |||
631 | /* terminator */ | ||
632 | {NULL, NULL, NULL}, | ||
633 | }; | ||
634 | |||
635 | static int aic3x_add_widgets(struct snd_soc_codec *codec) | ||
636 | { | ||
637 | int i; | ||
638 | |||
639 | for (i = 0; i < ARRAY_SIZE(aic3x_dapm_widgets); i++) | ||
640 | snd_soc_dapm_new_control(codec, &aic3x_dapm_widgets[i]); | ||
641 | |||
642 | /* set up audio path interconnects */ | ||
643 | for (i = 0; intercon[i][0] != NULL; i++) | ||
644 | snd_soc_dapm_connect_input(codec, intercon[i][0], | ||
645 | intercon[i][1], intercon[i][2]); | ||
646 | |||
647 | snd_soc_dapm_new_widgets(codec); | ||
648 | return 0; | ||
649 | } | ||
650 | |||
651 | struct aic3x_rate_divs { | ||
652 | u32 mclk; | ||
653 | u32 rate; | ||
654 | u32 fsref_reg; | ||
655 | u8 sr_reg:4; | ||
656 | u8 pllj_reg; | ||
657 | u16 plld_reg; | ||
658 | }; | ||
659 | |||
660 | /* AIC3X codec mclk clock divider coefficients */ | ||
661 | static const struct aic3x_rate_divs aic3x_divs[] = { | ||
662 | /* 8k */ | ||
663 | {22579200, 8000, 48000, 0xa, 8, 7075}, | ||
664 | {33868800, 8000, 48000, 0xa, 5, 8049}, | ||
665 | /* 11.025k */ | ||
666 | {22579200, 11025, 44100, 0x6, 8, 0}, | ||
667 | {33868800, 11025, 44100, 0x6, 5, 3333}, | ||
668 | /* 16k */ | ||
669 | {22579200, 16000, 48000, 0x4, 8, 7075}, | ||
670 | {33868800, 16000, 48000, 0x4, 5, 8049}, | ||
671 | /* 22.05k */ | ||
672 | {22579200, 22050, 44100, 0x2, 8, 0}, | ||
673 | {33868800, 22050, 44100, 0x2, 5, 3333}, | ||
674 | /* 32k */ | ||
675 | {22579200, 32000, 48000, 0x1, 8, 7075}, | ||
676 | {33868800, 32000, 48000, 0x1, 5, 8049}, | ||
677 | /* 44.1k */ | ||
678 | {22579200, 44100, 44100, 0x0, 8, 0}, | ||
679 | {33868800, 44100, 44100, 0x0, 5, 3333}, | ||
680 | /* 48k */ | ||
681 | {22579200, 48000, 48000, 0x0, 8, 7075}, | ||
682 | {33868800, 48000, 48000, 0x0, 5, 8049}, | ||
683 | /* 64k */ | ||
684 | {22579200, 96000, 96000, 0x1, 8, 7075}, | ||
685 | {33868800, 96000, 96000, 0x1, 5, 8049}, | ||
686 | /* 88.2k */ | ||
687 | {22579200, 88200, 88200, 0x0, 8, 0}, | ||
688 | {33868800, 88200, 88200, 0x0, 5, 3333}, | ||
689 | /* 96k */ | ||
690 | {22579200, 96000, 96000, 0x0, 8, 7075}, | ||
691 | {33868800, 96000, 96000, 0x0, 5, 8049}, | ||
692 | }; | ||
693 | |||
694 | static inline int aic3x_get_divs(int mclk, int rate) | ||
695 | { | ||
696 | int i; | ||
697 | |||
698 | for (i = 0; i < ARRAY_SIZE(aic3x_divs); i++) { | ||
699 | if (aic3x_divs[i].rate == rate && aic3x_divs[i].mclk == mclk) | ||
700 | return i; | ||
701 | } | ||
702 | |||
703 | return 0; | ||
704 | } | ||
705 | |||
706 | static int aic3x_hw_params(struct snd_pcm_substream *substream, | ||
707 | struct snd_pcm_hw_params *params) | ||
708 | { | ||
709 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
710 | struct snd_soc_device *socdev = rtd->socdev; | ||
711 | struct snd_soc_codec *codec = socdev->codec; | ||
712 | struct aic3x_priv *aic3x = codec->private_data; | ||
713 | int i; | ||
714 | u8 data, pll_p, pll_r, pll_j; | ||
715 | u16 pll_d; | ||
716 | |||
717 | i = aic3x_get_divs(aic3x->sysclk, params_rate(params)); | ||
718 | |||
719 | /* Route Left DAC to left channel input and | ||
720 | * right DAC to right channel input */ | ||
721 | data = (LDAC2LCH | RDAC2RCH); | ||
722 | switch (aic3x_divs[i].fsref_reg) { | ||
723 | case 44100: | ||
724 | data |= FSREF_44100; | ||
725 | break; | ||
726 | case 48000: | ||
727 | data |= FSREF_48000; | ||
728 | break; | ||
729 | case 88200: | ||
730 | data |= FSREF_44100 | DUAL_RATE_MODE; | ||
731 | break; | ||
732 | case 96000: | ||
733 | data |= FSREF_48000 | DUAL_RATE_MODE; | ||
734 | break; | ||
735 | } | ||
736 | aic3x_write(codec, AIC3X_CODEC_DATAPATH_REG, data); | ||
737 | |||
738 | /* codec sample rate select */ | ||
739 | data = aic3x_divs[i].sr_reg; | ||
740 | data |= (data << 4); | ||
741 | aic3x_write(codec, AIC3X_SAMPLE_RATE_SEL_REG, data); | ||
742 | |||
743 | /* Use PLL for generation Fsref by equation: | ||
744 | * Fsref = (MCLK * K * R)/(2048 * P); | ||
745 | * Fix P = 2 and R = 1 and calculate K, if | ||
746 | * K = J.D, i.e. J - an interger portion of K and D is the fractional | ||
747 | * one with 4 digits of precision; | ||
748 | * Example: | ||
749 | * For MCLK = 22.5792 MHz and Fsref = 48kHz: | ||
750 | * Select P = 2, R= 1, K = 8.7074, which results in J = 8, D = 7074 | ||
751 | */ | ||
752 | pll_p = 2; | ||
753 | pll_r = 1; | ||
754 | pll_j = aic3x_divs[i].pllj_reg; | ||
755 | pll_d = aic3x_divs[i].plld_reg; | ||
756 | |||
757 | data = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG); | ||
758 | aic3x_write(codec, AIC3X_PLL_PROGA_REG, data | (pll_p << PLLP_SHIFT)); | ||
759 | aic3x_write(codec, AIC3X_OVRF_STATUS_AND_PLLR_REG, pll_r << PLLR_SHIFT); | ||
760 | aic3x_write(codec, AIC3X_PLL_PROGB_REG, pll_j << PLLJ_SHIFT); | ||
761 | aic3x_write(codec, AIC3X_PLL_PROGC_REG, (pll_d >> 6) << PLLD_MSB_SHIFT); | ||
762 | aic3x_write(codec, AIC3X_PLL_PROGD_REG, | ||
763 | (pll_d & 0x3F) << PLLD_LSB_SHIFT); | ||
764 | |||
765 | /* select data word length */ | ||
766 | data = | ||
767 | aic3x_read_reg_cache(codec, AIC3X_ASD_INTF_CTRLB) & (~(0x3 << 4)); | ||
768 | switch (params_format(params)) { | ||
769 | case SNDRV_PCM_FORMAT_S16_LE: | ||
770 | break; | ||
771 | case SNDRV_PCM_FORMAT_S20_3LE: | ||
772 | data |= (0x01 << 4); | ||
773 | break; | ||
774 | case SNDRV_PCM_FORMAT_S24_LE: | ||
775 | data |= (0x02 << 4); | ||
776 | break; | ||
777 | case SNDRV_PCM_FORMAT_S32_LE: | ||
778 | data |= (0x03 << 4); | ||
779 | break; | ||
780 | } | ||
781 | aic3x_write(codec, AIC3X_ASD_INTF_CTRLB, data); | ||
782 | |||
783 | return 0; | ||
784 | } | ||
785 | |||
786 | static int aic3x_mute(struct snd_soc_codec_dai *dai, int mute) | ||
787 | { | ||
788 | struct snd_soc_codec *codec = dai->codec; | ||
789 | u8 ldac_reg = aic3x_read_reg_cache(codec, LDAC_VOL) & ~MUTE_ON; | ||
790 | u8 rdac_reg = aic3x_read_reg_cache(codec, RDAC_VOL) & ~MUTE_ON; | ||
791 | |||
792 | if (mute) { | ||
793 | aic3x_write(codec, LDAC_VOL, ldac_reg | MUTE_ON); | ||
794 | aic3x_write(codec, RDAC_VOL, rdac_reg | MUTE_ON); | ||
795 | } else { | ||
796 | aic3x_write(codec, LDAC_VOL, ldac_reg); | ||
797 | aic3x_write(codec, RDAC_VOL, rdac_reg); | ||
798 | } | ||
799 | |||
800 | return 0; | ||
801 | } | ||
802 | |||
803 | static int aic3x_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai, | ||
804 | int clk_id, unsigned int freq, int dir) | ||
805 | { | ||
806 | struct snd_soc_codec *codec = codec_dai->codec; | ||
807 | struct aic3x_priv *aic3x = codec->private_data; | ||
808 | |||
809 | switch (freq) { | ||
810 | case 22579200: | ||
811 | case 33868800: | ||
812 | aic3x->sysclk = freq; | ||
813 | return 0; | ||
814 | } | ||
815 | |||
816 | return -EINVAL; | ||
817 | } | ||
818 | |||
819 | static int aic3x_set_dai_fmt(struct snd_soc_codec_dai *codec_dai, | ||
820 | unsigned int fmt) | ||
821 | { | ||
822 | struct snd_soc_codec *codec = codec_dai->codec; | ||
823 | struct aic3x_priv *aic3x = codec->private_data; | ||
824 | u8 iface_areg = 0; | ||
825 | u8 iface_breg = 0; | ||
826 | |||
827 | /* set master/slave audio interface */ | ||
828 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
829 | case SND_SOC_DAIFMT_CBM_CFM: | ||
830 | aic3x->master = 1; | ||
831 | iface_areg |= BIT_CLK_MASTER | WORD_CLK_MASTER; | ||
832 | break; | ||
833 | case SND_SOC_DAIFMT_CBS_CFS: | ||
834 | aic3x->master = 0; | ||
835 | break; | ||
836 | default: | ||
837 | return -EINVAL; | ||
838 | } | ||
839 | |||
840 | /* interface format */ | ||
841 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
842 | case SND_SOC_DAIFMT_I2S: | ||
843 | break; | ||
844 | case SND_SOC_DAIFMT_DSP_A: | ||
845 | iface_breg |= (0x01 << 6); | ||
846 | break; | ||
847 | case SND_SOC_DAIFMT_RIGHT_J: | ||
848 | iface_breg |= (0x02 << 6); | ||
849 | break; | ||
850 | case SND_SOC_DAIFMT_LEFT_J: | ||
851 | iface_breg |= (0x03 << 6); | ||
852 | break; | ||
853 | default: | ||
854 | return -EINVAL; | ||
855 | } | ||
856 | |||
857 | /* set iface */ | ||
858 | aic3x_write(codec, AIC3X_ASD_INTF_CTRLA, iface_areg); | ||
859 | aic3x_write(codec, AIC3X_ASD_INTF_CTRLB, iface_breg); | ||
860 | |||
861 | return 0; | ||
862 | } | ||
863 | |||
864 | static int aic3x_dapm_event(struct snd_soc_codec *codec, int event) | ||
865 | { | ||
866 | struct aic3x_priv *aic3x = codec->private_data; | ||
867 | u8 reg; | ||
868 | |||
869 | switch (event) { | ||
870 | case SNDRV_CTL_POWER_D0: | ||
871 | /* all power is driven by DAPM system */ | ||
872 | if (aic3x->master) { | ||
873 | /* enable pll */ | ||
874 | reg = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG); | ||
875 | aic3x_write(codec, AIC3X_PLL_PROGA_REG, | ||
876 | reg | PLL_ENABLE); | ||
877 | } | ||
878 | break; | ||
879 | case SNDRV_CTL_POWER_D1: | ||
880 | case SNDRV_CTL_POWER_D2: | ||
881 | break; | ||
882 | case SNDRV_CTL_POWER_D3hot: | ||
883 | /* | ||
884 | * all power is driven by DAPM system, | ||
885 | * so output power is safe if bypass was set | ||
886 | */ | ||
887 | if (aic3x->master) { | ||
888 | /* disable pll */ | ||
889 | reg = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG); | ||
890 | aic3x_write(codec, AIC3X_PLL_PROGA_REG, | ||
891 | reg & ~PLL_ENABLE); | ||
892 | } | ||
893 | break; | ||
894 | case SNDRV_CTL_POWER_D3cold: | ||
895 | /* force all power off */ | ||
896 | reg = aic3x_read_reg_cache(codec, LINE1L_2_LADC_CTRL); | ||
897 | aic3x_write(codec, LINE1L_2_LADC_CTRL, reg & ~LADC_PWR_ON); | ||
898 | reg = aic3x_read_reg_cache(codec, LINE1R_2_RADC_CTRL); | ||
899 | aic3x_write(codec, LINE1R_2_RADC_CTRL, reg & ~RADC_PWR_ON); | ||
900 | |||
901 | reg = aic3x_read_reg_cache(codec, DAC_PWR); | ||
902 | aic3x_write(codec, DAC_PWR, reg & ~(LDAC_PWR_ON | RDAC_PWR_ON)); | ||
903 | |||
904 | reg = aic3x_read_reg_cache(codec, HPLOUT_CTRL); | ||
905 | aic3x_write(codec, HPLOUT_CTRL, reg & ~HPLOUT_PWR_ON); | ||
906 | reg = aic3x_read_reg_cache(codec, HPROUT_CTRL); | ||
907 | aic3x_write(codec, HPROUT_CTRL, reg & ~HPROUT_PWR_ON); | ||
908 | |||
909 | reg = aic3x_read_reg_cache(codec, HPLCOM_CTRL); | ||
910 | aic3x_write(codec, HPLCOM_CTRL, reg & ~HPLCOM_PWR_ON); | ||
911 | reg = aic3x_read_reg_cache(codec, HPRCOM_CTRL); | ||
912 | aic3x_write(codec, HPRCOM_CTRL, reg & ~HPRCOM_PWR_ON); | ||
913 | |||
914 | reg = aic3x_read_reg_cache(codec, MONOLOPM_CTRL); | ||
915 | aic3x_write(codec, MONOLOPM_CTRL, reg & ~MONOLOPM_PWR_ON); | ||
916 | |||
917 | reg = aic3x_read_reg_cache(codec, LLOPM_CTRL); | ||
918 | aic3x_write(codec, LLOPM_CTRL, reg & ~LLOPM_PWR_ON); | ||
919 | reg = aic3x_read_reg_cache(codec, RLOPM_CTRL); | ||
920 | aic3x_write(codec, RLOPM_CTRL, reg & ~RLOPM_PWR_ON); | ||
921 | |||
922 | if (aic3x->master) { | ||
923 | /* disable pll */ | ||
924 | reg = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG); | ||
925 | aic3x_write(codec, AIC3X_PLL_PROGA_REG, | ||
926 | reg & ~PLL_ENABLE); | ||
927 | } | ||
928 | break; | ||
929 | } | ||
930 | codec->dapm_state = event; | ||
931 | |||
932 | return 0; | ||
933 | } | ||
934 | |||
935 | #define AIC3X_RATES SNDRV_PCM_RATE_8000_96000 | ||
936 | #define AIC3X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ | ||
937 | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) | ||
938 | |||
939 | struct snd_soc_codec_dai aic3x_dai = { | ||
940 | .name = "aic3x", | ||
941 | .playback = { | ||
942 | .stream_name = "Playback", | ||
943 | .channels_min = 1, | ||
944 | .channels_max = 2, | ||
945 | .rates = AIC3X_RATES, | ||
946 | .formats = AIC3X_FORMATS,}, | ||
947 | .capture = { | ||
948 | .stream_name = "Capture", | ||
949 | .channels_min = 1, | ||
950 | .channels_max = 2, | ||
951 | .rates = AIC3X_RATES, | ||
952 | .formats = AIC3X_FORMATS,}, | ||
953 | .ops = { | ||
954 | .hw_params = aic3x_hw_params, | ||
955 | }, | ||
956 | .dai_ops = { | ||
957 | .digital_mute = aic3x_mute, | ||
958 | .set_sysclk = aic3x_set_dai_sysclk, | ||
959 | .set_fmt = aic3x_set_dai_fmt, | ||
960 | } | ||
961 | }; | ||
962 | EXPORT_SYMBOL_GPL(aic3x_dai); | ||
963 | |||
964 | static int aic3x_suspend(struct platform_device *pdev, pm_message_t state) | ||
965 | { | ||
966 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
967 | struct snd_soc_codec *codec = socdev->codec; | ||
968 | |||
969 | aic3x_dapm_event(codec, SNDRV_CTL_POWER_D3cold); | ||
970 | |||
971 | return 0; | ||
972 | } | ||
973 | |||
974 | static int aic3x_resume(struct platform_device *pdev) | ||
975 | { | ||
976 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
977 | struct snd_soc_codec *codec = socdev->codec; | ||
978 | int i; | ||
979 | u8 data[2]; | ||
980 | u8 *cache = codec->reg_cache; | ||
981 | |||
982 | /* Sync reg_cache with the hardware */ | ||
983 | for (i = 0; i < ARRAY_SIZE(aic3x_reg); i++) { | ||
984 | data[0] = i; | ||
985 | data[1] = cache[i]; | ||
986 | codec->hw_write(codec->control_data, data, 2); | ||
987 | } | ||
988 | |||
989 | aic3x_dapm_event(codec, codec->suspend_dapm_state); | ||
990 | |||
991 | return 0; | ||
992 | } | ||
993 | |||
994 | /* | ||
995 | * initialise the AIC3X driver | ||
996 | * register the mixer and dsp interfaces with the kernel | ||
997 | */ | ||
998 | static int aic3x_init(struct snd_soc_device *socdev) | ||
999 | { | ||
1000 | struct snd_soc_codec *codec = socdev->codec; | ||
1001 | int reg, ret = 0; | ||
1002 | |||
1003 | codec->name = "aic3x"; | ||
1004 | codec->owner = THIS_MODULE; | ||
1005 | codec->read = aic3x_read_reg_cache; | ||
1006 | codec->write = aic3x_write; | ||
1007 | codec->dapm_event = aic3x_dapm_event; | ||
1008 | codec->dai = &aic3x_dai; | ||
1009 | codec->num_dai = 1; | ||
1010 | codec->reg_cache_size = sizeof(aic3x_reg); | ||
1011 | codec->reg_cache = kmemdup(aic3x_reg, sizeof(aic3x_reg), GFP_KERNEL); | ||
1012 | if (codec->reg_cache == NULL) | ||
1013 | return -ENOMEM; | ||
1014 | |||
1015 | aic3x_write(codec, AIC3X_PAGE_SELECT, PAGE0_SELECT); | ||
1016 | aic3x_write(codec, AIC3X_RESET, SOFT_RESET); | ||
1017 | |||
1018 | /* register pcms */ | ||
1019 | ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); | ||
1020 | if (ret < 0) { | ||
1021 | printk(KERN_ERR "aic3x: failed to create pcms\n"); | ||
1022 | goto pcm_err; | ||
1023 | } | ||
1024 | |||
1025 | /* DAC default volume and mute */ | ||
1026 | aic3x_write(codec, LDAC_VOL, DEFAULT_VOL | MUTE_ON); | ||
1027 | aic3x_write(codec, RDAC_VOL, DEFAULT_VOL | MUTE_ON); | ||
1028 | |||
1029 | /* DAC to HP default volume and route to Output mixer */ | ||
1030 | aic3x_write(codec, DACL1_2_HPLOUT_VOL, DEFAULT_VOL | ROUTE_ON); | ||
1031 | aic3x_write(codec, DACR1_2_HPROUT_VOL, DEFAULT_VOL | ROUTE_ON); | ||
1032 | aic3x_write(codec, DACL1_2_HPLCOM_VOL, DEFAULT_VOL | ROUTE_ON); | ||
1033 | aic3x_write(codec, DACR1_2_HPRCOM_VOL, DEFAULT_VOL | ROUTE_ON); | ||
1034 | /* DAC to Line Out default volume and route to Output mixer */ | ||
1035 | aic3x_write(codec, DACL1_2_LLOPM_VOL, DEFAULT_VOL | ROUTE_ON); | ||
1036 | aic3x_write(codec, DACR1_2_RLOPM_VOL, DEFAULT_VOL | ROUTE_ON); | ||
1037 | /* DAC to Mono Line Out default volume and route to Output mixer */ | ||
1038 | aic3x_write(codec, DACL1_2_MONOLOPM_VOL, DEFAULT_VOL | ROUTE_ON); | ||
1039 | aic3x_write(codec, DACR1_2_MONOLOPM_VOL, DEFAULT_VOL | ROUTE_ON); | ||
1040 | |||
1041 | /* unmute all outputs */ | ||
1042 | reg = aic3x_read_reg_cache(codec, LLOPM_CTRL); | ||
1043 | aic3x_write(codec, LLOPM_CTRL, reg | UNMUTE); | ||
1044 | reg = aic3x_read_reg_cache(codec, RLOPM_CTRL); | ||
1045 | aic3x_write(codec, RLOPM_CTRL, reg | UNMUTE); | ||
1046 | reg = aic3x_read_reg_cache(codec, MONOLOPM_CTRL); | ||
1047 | aic3x_write(codec, MONOLOPM_CTRL, reg | UNMUTE); | ||
1048 | reg = aic3x_read_reg_cache(codec, HPLOUT_CTRL); | ||
1049 | aic3x_write(codec, HPLOUT_CTRL, reg | UNMUTE); | ||
1050 | reg = aic3x_read_reg_cache(codec, HPROUT_CTRL); | ||
1051 | aic3x_write(codec, HPROUT_CTRL, reg | UNMUTE); | ||
1052 | reg = aic3x_read_reg_cache(codec, HPLCOM_CTRL); | ||
1053 | aic3x_write(codec, HPLCOM_CTRL, reg | UNMUTE); | ||
1054 | reg = aic3x_read_reg_cache(codec, HPRCOM_CTRL); | ||
1055 | aic3x_write(codec, HPRCOM_CTRL, reg | UNMUTE); | ||
1056 | |||
1057 | /* ADC default volume and unmute */ | ||
1058 | aic3x_write(codec, LADC_VOL, DEFAULT_GAIN); | ||
1059 | aic3x_write(codec, RADC_VOL, DEFAULT_GAIN); | ||
1060 | /* By default route Line1 to ADC PGA mixer */ | ||
1061 | aic3x_write(codec, LINE1L_2_LADC_CTRL, 0x0); | ||
1062 | aic3x_write(codec, LINE1R_2_RADC_CTRL, 0x0); | ||
1063 | |||
1064 | /* PGA to HP Bypass default volume, disconnect from Output Mixer */ | ||
1065 | aic3x_write(codec, PGAL_2_HPLOUT_VOL, DEFAULT_VOL); | ||
1066 | aic3x_write(codec, PGAR_2_HPROUT_VOL, DEFAULT_VOL); | ||
1067 | aic3x_write(codec, PGAL_2_HPLCOM_VOL, DEFAULT_VOL); | ||
1068 | aic3x_write(codec, PGAR_2_HPRCOM_VOL, DEFAULT_VOL); | ||
1069 | /* PGA to Line Out default volume, disconnect from Output Mixer */ | ||
1070 | aic3x_write(codec, PGAL_2_LLOPM_VOL, DEFAULT_VOL); | ||
1071 | aic3x_write(codec, PGAR_2_RLOPM_VOL, DEFAULT_VOL); | ||
1072 | /* PGA to Mono Line Out default volume, disconnect from Output Mixer */ | ||
1073 | aic3x_write(codec, PGAL_2_MONOLOPM_VOL, DEFAULT_VOL); | ||
1074 | aic3x_write(codec, PGAR_2_MONOLOPM_VOL, DEFAULT_VOL); | ||
1075 | |||
1076 | /* Line2 to HP Bypass default volume, disconnect from Output Mixer */ | ||
1077 | aic3x_write(codec, LINE2L_2_HPLOUT_VOL, DEFAULT_VOL); | ||
1078 | aic3x_write(codec, LINE2R_2_HPROUT_VOL, DEFAULT_VOL); | ||
1079 | aic3x_write(codec, LINE2L_2_HPLCOM_VOL, DEFAULT_VOL); | ||
1080 | aic3x_write(codec, LINE2R_2_HPRCOM_VOL, DEFAULT_VOL); | ||
1081 | /* Line2 Line Out default volume, disconnect from Output Mixer */ | ||
1082 | aic3x_write(codec, LINE2L_2_LLOPM_VOL, DEFAULT_VOL); | ||
1083 | aic3x_write(codec, LINE2R_2_RLOPM_VOL, DEFAULT_VOL); | ||
1084 | /* Line2 to Mono Out default volume, disconnect from Output Mixer */ | ||
1085 | aic3x_write(codec, LINE2L_2_MONOLOPM_VOL, DEFAULT_VOL); | ||
1086 | aic3x_write(codec, LINE2R_2_MONOLOPM_VOL, DEFAULT_VOL); | ||
1087 | |||
1088 | /* off, with power on */ | ||
1089 | aic3x_dapm_event(codec, SNDRV_CTL_POWER_D3hot); | ||
1090 | |||
1091 | aic3x_add_controls(codec); | ||
1092 | aic3x_add_widgets(codec); | ||
1093 | ret = snd_soc_register_card(socdev); | ||
1094 | if (ret < 0) { | ||
1095 | printk(KERN_ERR "aic3x: failed to register card\n"); | ||
1096 | goto card_err; | ||
1097 | } | ||
1098 | |||
1099 | return ret; | ||
1100 | |||
1101 | card_err: | ||
1102 | snd_soc_free_pcms(socdev); | ||
1103 | snd_soc_dapm_free(socdev); | ||
1104 | pcm_err: | ||
1105 | kfree(codec->reg_cache); | ||
1106 | return ret; | ||
1107 | } | ||
1108 | |||
1109 | static struct snd_soc_device *aic3x_socdev; | ||
1110 | |||
1111 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | ||
1112 | /* | ||
1113 | * AIC3X 2 wire address can be up to 4 devices with device addresses | ||
1114 | * 0x18, 0x19, 0x1A, 0x1B | ||
1115 | */ | ||
1116 | static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END }; | ||
1117 | |||
1118 | /* Magic definition of all other variables and things */ | ||
1119 | I2C_CLIENT_INSMOD; | ||
1120 | |||
1121 | static struct i2c_driver aic3x_i2c_driver; | ||
1122 | static struct i2c_client client_template; | ||
1123 | |||
1124 | /* | ||
1125 | * If the i2c layer weren't so broken, we could pass this kind of data | ||
1126 | * around | ||
1127 | */ | ||
1128 | static int aic3x_codec_probe(struct i2c_adapter *adap, int addr, int kind) | ||
1129 | { | ||
1130 | struct snd_soc_device *socdev = aic3x_socdev; | ||
1131 | struct aic3x_setup_data *setup = socdev->codec_data; | ||
1132 | struct snd_soc_codec *codec = socdev->codec; | ||
1133 | struct i2c_client *i2c; | ||
1134 | int ret; | ||
1135 | |||
1136 | if (addr != setup->i2c_address) | ||
1137 | return -ENODEV; | ||
1138 | |||
1139 | client_template.adapter = adap; | ||
1140 | client_template.addr = addr; | ||
1141 | |||
1142 | i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL); | ||
1143 | if (i2c == NULL) { | ||
1144 | kfree(codec); | ||
1145 | return -ENOMEM; | ||
1146 | } | ||
1147 | i2c_set_clientdata(i2c, codec); | ||
1148 | codec->control_data = i2c; | ||
1149 | |||
1150 | ret = i2c_attach_client(i2c); | ||
1151 | if (ret < 0) { | ||
1152 | printk(KERN_ERR "aic3x: failed to attach codec at addr %x\n", | ||
1153 | addr); | ||
1154 | goto err; | ||
1155 | } | ||
1156 | |||
1157 | ret = aic3x_init(socdev); | ||
1158 | if (ret < 0) { | ||
1159 | printk(KERN_ERR "aic3x: failed to initialise AIC3X\n"); | ||
1160 | goto err; | ||
1161 | } | ||
1162 | return ret; | ||
1163 | |||
1164 | err: | ||
1165 | kfree(codec); | ||
1166 | kfree(i2c); | ||
1167 | return ret; | ||
1168 | } | ||
1169 | |||
1170 | static int aic3x_i2c_detach(struct i2c_client *client) | ||
1171 | { | ||
1172 | struct snd_soc_codec *codec = i2c_get_clientdata(client); | ||
1173 | i2c_detach_client(client); | ||
1174 | kfree(codec->reg_cache); | ||
1175 | kfree(client); | ||
1176 | return 0; | ||
1177 | } | ||
1178 | |||
1179 | static int aic3x_i2c_attach(struct i2c_adapter *adap) | ||
1180 | { | ||
1181 | return i2c_probe(adap, &addr_data, aic3x_codec_probe); | ||
1182 | } | ||
1183 | |||
1184 | /* machine i2c codec control layer */ | ||
1185 | static struct i2c_driver aic3x_i2c_driver = { | ||
1186 | .driver = { | ||
1187 | .name = "aic3x I2C Codec", | ||
1188 | .owner = THIS_MODULE, | ||
1189 | }, | ||
1190 | .id = I2C_DRIVERID_I2CDEV, | ||
1191 | .attach_adapter = aic3x_i2c_attach, | ||
1192 | .detach_client = aic3x_i2c_detach, | ||
1193 | .command = NULL, | ||
1194 | }; | ||
1195 | |||
1196 | static struct i2c_client client_template = { | ||
1197 | .name = "AIC3X", | ||
1198 | .driver = &aic3x_i2c_driver, | ||
1199 | }; | ||
1200 | #endif | ||
1201 | |||
1202 | static int aic3x_probe(struct platform_device *pdev) | ||
1203 | { | ||
1204 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
1205 | struct aic3x_setup_data *setup; | ||
1206 | struct snd_soc_codec *codec; | ||
1207 | struct aic3x_priv *aic3x; | ||
1208 | int ret = 0; | ||
1209 | |||
1210 | printk(KERN_INFO "AIC3X Audio Codec %s\n", AIC3X_VERSION); | ||
1211 | |||
1212 | setup = socdev->codec_data; | ||
1213 | codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); | ||
1214 | if (codec == NULL) | ||
1215 | return -ENOMEM; | ||
1216 | |||
1217 | aic3x = kzalloc(sizeof(struct aic3x_priv), GFP_KERNEL); | ||
1218 | if (aic3x == NULL) { | ||
1219 | kfree(codec); | ||
1220 | return -ENOMEM; | ||
1221 | } | ||
1222 | |||
1223 | codec->private_data = aic3x; | ||
1224 | socdev->codec = codec; | ||
1225 | mutex_init(&codec->mutex); | ||
1226 | INIT_LIST_HEAD(&codec->dapm_widgets); | ||
1227 | INIT_LIST_HEAD(&codec->dapm_paths); | ||
1228 | |||
1229 | aic3x_socdev = socdev; | ||
1230 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | ||
1231 | if (setup->i2c_address) { | ||
1232 | normal_i2c[0] = setup->i2c_address; | ||
1233 | codec->hw_write = (hw_write_t) i2c_master_send; | ||
1234 | ret = i2c_add_driver(&aic3x_i2c_driver); | ||
1235 | if (ret != 0) | ||
1236 | printk(KERN_ERR "can't add i2c driver"); | ||
1237 | } | ||
1238 | #else | ||
1239 | /* Add other interfaces here */ | ||
1240 | #endif | ||
1241 | return ret; | ||
1242 | } | ||
1243 | |||
1244 | static int aic3x_remove(struct platform_device *pdev) | ||
1245 | { | ||
1246 | struct snd_soc_device *socdev = platform_get_drvdata(pdev); | ||
1247 | struct snd_soc_codec *codec = socdev->codec; | ||
1248 | |||
1249 | /* power down chip */ | ||
1250 | if (codec->control_data) | ||
1251 | aic3x_dapm_event(codec, SNDRV_CTL_POWER_D3); | ||
1252 | |||
1253 | snd_soc_free_pcms(socdev); | ||
1254 | snd_soc_dapm_free(socdev); | ||
1255 | #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) | ||
1256 | i2c_del_driver(&aic3x_i2c_driver); | ||
1257 | #endif | ||
1258 | kfree(codec->private_data); | ||
1259 | kfree(codec); | ||
1260 | |||
1261 | return 0; | ||
1262 | } | ||
1263 | |||
1264 | struct snd_soc_codec_device soc_codec_dev_aic3x = { | ||
1265 | .probe = aic3x_probe, | ||
1266 | .remove = aic3x_remove, | ||
1267 | .suspend = aic3x_suspend, | ||
1268 | .resume = aic3x_resume, | ||
1269 | }; | ||
1270 | EXPORT_SYMBOL_GPL(soc_codec_dev_aic3x); | ||
1271 | |||
1272 | MODULE_DESCRIPTION("ASoC TLV320AIC3X codec driver"); | ||
1273 | MODULE_AUTHOR("Vladimir Barinov"); | ||
1274 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/codecs/tlv320aic3x.h b/sound/soc/codecs/tlv320aic3x.h new file mode 100644 index 000000000000..d0cdeeb629de --- /dev/null +++ b/sound/soc/codecs/tlv320aic3x.h | |||
@@ -0,0 +1,181 @@ | |||
1 | /* | ||
2 | * ALSA SoC TLV320AIC3X codec driver | ||
3 | * | ||
4 | * Author: Vladimir Barinov, <vbarinov@ru.mvista.com> | ||
5 | * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #ifndef _AIC3X_H | ||
13 | #define _AIC3X_H | ||
14 | |||
15 | /* AIC3X register space */ | ||
16 | #define AIC3X_CACHEREGNUM 103 | ||
17 | |||
18 | /* Page select register */ | ||
19 | #define AIC3X_PAGE_SELECT 0 | ||
20 | /* Software reset register */ | ||
21 | #define AIC3X_RESET 1 | ||
22 | /* Codec Sample rate select register */ | ||
23 | #define AIC3X_SAMPLE_RATE_SEL_REG 2 | ||
24 | /* PLL progrramming register A */ | ||
25 | #define AIC3X_PLL_PROGA_REG 3 | ||
26 | /* PLL progrramming register B */ | ||
27 | #define AIC3X_PLL_PROGB_REG 4 | ||
28 | /* PLL progrramming register C */ | ||
29 | #define AIC3X_PLL_PROGC_REG 5 | ||
30 | /* PLL progrramming register D */ | ||
31 | #define AIC3X_PLL_PROGD_REG 6 | ||
32 | /* Codec datapath setup register */ | ||
33 | #define AIC3X_CODEC_DATAPATH_REG 7 | ||
34 | /* Audio serial data interface control register A */ | ||
35 | #define AIC3X_ASD_INTF_CTRLA 8 | ||
36 | /* Audio serial data interface control register B */ | ||
37 | #define AIC3X_ASD_INTF_CTRLB 9 | ||
38 | /* Audio overflow status and PLL R value programming register */ | ||
39 | #define AIC3X_OVRF_STATUS_AND_PLLR_REG 11 | ||
40 | |||
41 | /* ADC PGA Gain control registers */ | ||
42 | #define LADC_VOL 15 | ||
43 | #define RADC_VOL 16 | ||
44 | /* MIC3 control registers */ | ||
45 | #define MIC3LR_2_LADC_CTRL 17 | ||
46 | #define MIC3LR_2_RADC_CTRL 18 | ||
47 | /* Line1 Input control registers */ | ||
48 | #define LINE1L_2_LADC_CTRL 19 | ||
49 | #define LINE1R_2_RADC_CTRL 22 | ||
50 | /* Line2 Input control registers */ | ||
51 | #define LINE2L_2_LADC_CTRL 20 | ||
52 | #define LINE2R_2_RADC_CTRL 23 | ||
53 | /* MICBIAS Control Register */ | ||
54 | #define MICBIAS_CTRL 25 | ||
55 | |||
56 | /* AGC Control Registers A, B, C */ | ||
57 | #define LAGC_CTRL_A 26 | ||
58 | #define LAGC_CTRL_B 27 | ||
59 | #define LAGC_CTRL_C 28 | ||
60 | #define RAGC_CTRL_A 29 | ||
61 | #define RAGC_CTRL_B 30 | ||
62 | #define RAGC_CTRL_C 31 | ||
63 | |||
64 | /* DAC Power and Left High Power Output control registers */ | ||
65 | #define DAC_PWR 37 | ||
66 | #define HPLCOM_CFG 37 | ||
67 | /* Right High Power Output control registers */ | ||
68 | #define HPRCOM_CFG 38 | ||
69 | /* DAC Output Switching control registers */ | ||
70 | #define DAC_LINE_MUX 41 | ||
71 | /* High Power Output Driver Pop Reduction registers */ | ||
72 | #define HPOUT_POP_REDUCTION 42 | ||
73 | /* DAC Digital control registers */ | ||
74 | #define LDAC_VOL 43 | ||
75 | #define RDAC_VOL 44 | ||
76 | /* High Power Output control registers */ | ||
77 | #define LINE2L_2_HPLOUT_VOL 45 | ||
78 | #define LINE2R_2_HPROUT_VOL 62 | ||
79 | #define PGAL_2_HPLOUT_VOL 46 | ||
80 | #define PGAR_2_HPROUT_VOL 63 | ||
81 | #define DACL1_2_HPLOUT_VOL 47 | ||
82 | #define DACR1_2_HPROUT_VOL 64 | ||
83 | #define HPLOUT_CTRL 51 | ||
84 | #define HPROUT_CTRL 65 | ||
85 | /* High Power COM control registers */ | ||
86 | #define LINE2L_2_HPLCOM_VOL 52 | ||
87 | #define LINE2R_2_HPRCOM_VOL 69 | ||
88 | #define PGAL_2_HPLCOM_VOL 53 | ||
89 | #define PGAR_2_HPRCOM_VOL 70 | ||
90 | #define DACL1_2_HPLCOM_VOL 54 | ||
91 | #define DACR1_2_HPRCOM_VOL 71 | ||
92 | #define HPLCOM_CTRL 58 | ||
93 | #define HPRCOM_CTRL 72 | ||
94 | /* Mono Line Output Plus/Minus control registers */ | ||
95 | #define LINE2L_2_MONOLOPM_VOL 73 | ||
96 | #define LINE2R_2_MONOLOPM_VOL 76 | ||
97 | #define PGAL_2_MONOLOPM_VOL 74 | ||
98 | #define PGAR_2_MONOLOPM_VOL 77 | ||
99 | #define DACL1_2_MONOLOPM_VOL 75 | ||
100 | #define DACR1_2_MONOLOPM_VOL 78 | ||
101 | #define MONOLOPM_CTRL 79 | ||
102 | /* Line Output Plus/Minus control registers */ | ||
103 | #define LINE2L_2_LLOPM_VOL 80 | ||
104 | #define LINE2R_2_RLOPM_VOL 90 | ||
105 | #define PGAL_2_LLOPM_VOL 81 | ||
106 | #define PGAR_2_RLOPM_VOL 91 | ||
107 | #define DACL1_2_LLOPM_VOL 82 | ||
108 | #define DACR1_2_RLOPM_VOL 92 | ||
109 | #define LLOPM_CTRL 86 | ||
110 | #define RLOPM_CTRL 93 | ||
111 | /* Clock generation control register */ | ||
112 | #define AIC3X_CLKGEN_CTRL_REG 102 | ||
113 | |||
114 | /* Page select register bits */ | ||
115 | #define PAGE0_SELECT 0 | ||
116 | #define PAGE1_SELECT 1 | ||
117 | |||
118 | /* Audio serial data interface control register A bits */ | ||
119 | #define BIT_CLK_MASTER 0x80 | ||
120 | #define WORD_CLK_MASTER 0x40 | ||
121 | |||
122 | /* Codec Datapath setup register 7 */ | ||
123 | #define FSREF_44100 (1 << 7) | ||
124 | #define FSREF_48000 (0 << 7) | ||
125 | #define DUAL_RATE_MODE ((1 << 5) | (1 << 6)) | ||
126 | #define LDAC2LCH (0x1 << 3) | ||
127 | #define RDAC2RCH (0x1 << 1) | ||
128 | |||
129 | /* PLL registers bitfields */ | ||
130 | #define PLLP_SHIFT 0 | ||
131 | #define PLLR_SHIFT 0 | ||
132 | #define PLLJ_SHIFT 2 | ||
133 | #define PLLD_MSB_SHIFT 0 | ||
134 | #define PLLD_LSB_SHIFT 2 | ||
135 | |||
136 | /* Clock generation register bits */ | ||
137 | #define PLL_CLKIN_SHIFT 4 | ||
138 | #define MCLK_SOURCE 0x0 | ||
139 | #define PLL_CLKDIV_SHIFT 0 | ||
140 | |||
141 | /* Software reset register bits */ | ||
142 | #define SOFT_RESET 0x80 | ||
143 | |||
144 | /* PLL progrramming register A bits */ | ||
145 | #define PLL_ENABLE 0x80 | ||
146 | |||
147 | /* Route bits */ | ||
148 | #define ROUTE_ON 0x80 | ||
149 | |||
150 | /* Mute bits */ | ||
151 | #define UNMUTE 0x08 | ||
152 | #define MUTE_ON 0x80 | ||
153 | |||
154 | /* Power bits */ | ||
155 | #define LADC_PWR_ON 0x04 | ||
156 | #define RADC_PWR_ON 0x04 | ||
157 | #define LDAC_PWR_ON 0x80 | ||
158 | #define RDAC_PWR_ON 0x40 | ||
159 | #define HPLOUT_PWR_ON 0x01 | ||
160 | #define HPROUT_PWR_ON 0x01 | ||
161 | #define HPLCOM_PWR_ON 0x01 | ||
162 | #define HPRCOM_PWR_ON 0x01 | ||
163 | #define MONOLOPM_PWR_ON 0x01 | ||
164 | #define LLOPM_PWR_ON 0x01 | ||
165 | #define RLOPM_PWR_ON 0x01 | ||
166 | |||
167 | #define INVERT_VOL(val) (0x7f - val) | ||
168 | |||
169 | /* Default output volume (inverted) */ | ||
170 | #define DEFAULT_VOL INVERT_VOL(0x50) | ||
171 | /* Default input volume */ | ||
172 | #define DEFAULT_GAIN 0x20 | ||
173 | |||
174 | struct aic3x_setup_data { | ||
175 | unsigned short i2c_address; | ||
176 | }; | ||
177 | |||
178 | extern struct snd_soc_codec_dai aic3x_dai; | ||
179 | extern struct snd_soc_codec_device soc_codec_dev_aic3x; | ||
180 | |||
181 | #endif /* _AIC3X_H */ | ||
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index 7ca0b5268289..9c33fe874928 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c | |||
@@ -19,7 +19,6 @@ | |||
19 | #include <linux/pm.h> | 19 | #include <linux/pm.h> |
20 | #include <linux/i2c.h> | 20 | #include <linux/i2c.h> |
21 | #include <linux/platform_device.h> | 21 | #include <linux/platform_device.h> |
22 | #include <sound/driver.h> | ||
23 | #include <sound/core.h> | 22 | #include <sound/core.h> |
24 | #include <sound/pcm.h> | 23 | #include <sound/pcm.h> |
25 | #include <sound/pcm_params.h> | 24 | #include <sound/pcm_params.h> |
@@ -562,13 +561,13 @@ static int wm8731_init(struct snd_soc_device *socdev) | |||
562 | 561 | ||
563 | /* set the update bits */ | 562 | /* set the update bits */ |
564 | reg = wm8731_read_reg_cache(codec, WM8731_LOUT1V); | 563 | reg = wm8731_read_reg_cache(codec, WM8731_LOUT1V); |
565 | wm8731_write(codec, WM8731_LOUT1V, reg | 0x0100); | 564 | wm8731_write(codec, WM8731_LOUT1V, reg & ~0x0100); |
566 | reg = wm8731_read_reg_cache(codec, WM8731_ROUT1V); | 565 | reg = wm8731_read_reg_cache(codec, WM8731_ROUT1V); |
567 | wm8731_write(codec, WM8731_ROUT1V, reg | 0x0100); | 566 | wm8731_write(codec, WM8731_ROUT1V, reg & ~0x0100); |
568 | reg = wm8731_read_reg_cache(codec, WM8731_LINVOL); | 567 | reg = wm8731_read_reg_cache(codec, WM8731_LINVOL); |
569 | wm8731_write(codec, WM8731_LINVOL, reg | 0x0100); | 568 | wm8731_write(codec, WM8731_LINVOL, reg & ~0x0100); |
570 | reg = wm8731_read_reg_cache(codec, WM8731_RINVOL); | 569 | reg = wm8731_read_reg_cache(codec, WM8731_RINVOL); |
571 | wm8731_write(codec, WM8731_RINVOL, reg | 0x0100); | 570 | wm8731_write(codec, WM8731_RINVOL, reg & ~0x0100); |
572 | 571 | ||
573 | wm8731_add_controls(codec); | 572 | wm8731_add_controls(codec); |
574 | wm8731_add_widgets(codec); | 573 | wm8731_add_widgets(codec); |
diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c index 28684eeda738..77a857b997a2 100644 --- a/sound/soc/codecs/wm8750.c +++ b/sound/soc/codecs/wm8750.c | |||
@@ -19,7 +19,6 @@ | |||
19 | #include <linux/pm.h> | 19 | #include <linux/pm.h> |
20 | #include <linux/i2c.h> | 20 | #include <linux/i2c.h> |
21 | #include <linux/platform_device.h> | 21 | #include <linux/platform_device.h> |
22 | #include <sound/driver.h> | ||
23 | #include <sound/core.h> | 22 | #include <sound/core.h> |
24 | #include <sound/pcm.h> | 23 | #include <sound/pcm.h> |
25 | #include <sound/pcm_params.h> | 24 | #include <sound/pcm_params.h> |
@@ -189,7 +188,7 @@ SOC_ENUM("Bass Boost", wm8750_enum[0]), | |||
189 | SOC_ENUM("Bass Filter", wm8750_enum[1]), | 188 | SOC_ENUM("Bass Filter", wm8750_enum[1]), |
190 | SOC_SINGLE("Bass Volume", WM8750_BASS, 0, 15, 1), | 189 | SOC_SINGLE("Bass Volume", WM8750_BASS, 0, 15, 1), |
191 | 190 | ||
192 | SOC_SINGLE("Treble Volume", WM8750_TREBLE, 0, 15, 0), | 191 | SOC_SINGLE("Treble Volume", WM8750_TREBLE, 0, 15, 1), |
193 | SOC_ENUM("Treble Cut-off", wm8750_enum[2]), | 192 | SOC_ENUM("Treble Cut-off", wm8750_enum[2]), |
194 | 193 | ||
195 | SOC_SINGLE("3D Switch", WM8750_3D, 0, 1, 0), | 194 | SOC_SINGLE("3D Switch", WM8750_3D, 0, 1, 0), |
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index efced934566d..ddd9c71b3fde 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c | |||
@@ -41,13 +41,13 @@ | |||
41 | #include <linux/pm.h> | 41 | #include <linux/pm.h> |
42 | #include <linux/i2c.h> | 42 | #include <linux/i2c.h> |
43 | #include <linux/platform_device.h> | 43 | #include <linux/platform_device.h> |
44 | #include <sound/driver.h> | ||
45 | #include <sound/core.h> | 44 | #include <sound/core.h> |
46 | #include <sound/pcm.h> | 45 | #include <sound/pcm.h> |
47 | #include <sound/pcm_params.h> | 46 | #include <sound/pcm_params.h> |
48 | #include <sound/soc.h> | 47 | #include <sound/soc.h> |
49 | #include <sound/soc-dapm.h> | 48 | #include <sound/soc-dapm.h> |
50 | #include <sound/initval.h> | 49 | #include <sound/initval.h> |
50 | #include <sound/tlv.h> | ||
51 | #include <asm/div64.h> | 51 | #include <asm/div64.h> |
52 | 52 | ||
53 | #include "wm8753.h" | 53 | #include "wm8753.h" |
@@ -258,6 +258,8 @@ static int wm8753_set_dai(struct snd_kcontrol *kcontrol, | |||
258 | return 1; | 258 | return 1; |
259 | } | 259 | } |
260 | 260 | ||
261 | static const DECLARE_TLV_DB_LINEAR(rec_mix_tlv, -1500, 600); | ||
262 | |||
261 | static const struct snd_kcontrol_new wm8753_snd_controls[] = { | 263 | static const struct snd_kcontrol_new wm8753_snd_controls[] = { |
262 | SOC_DOUBLE_R("PCM Volume", WM8753_LDAC, WM8753_RDAC, 0, 255, 0), | 264 | SOC_DOUBLE_R("PCM Volume", WM8753_LDAC, WM8753_RDAC, 0, 255, 0), |
263 | 265 | ||
@@ -287,8 +289,8 @@ SOC_SINGLE("Bass Volume", WM8753_BASS, 0, 15, 1), | |||
287 | SOC_SINGLE("Treble Volume", WM8753_TREBLE, 0, 15, 1), | 289 | SOC_SINGLE("Treble Volume", WM8753_TREBLE, 0, 15, 1), |
288 | SOC_ENUM("Treble Cut-off", wm8753_enum[2]), | 290 | SOC_ENUM("Treble Cut-off", wm8753_enum[2]), |
289 | 291 | ||
290 | SOC_DOUBLE("Sidetone Capture Volume", WM8753_RECMIX1, 0, 4, 7, 1), | 292 | SOC_DOUBLE_TLV("Sidetone Capture Volume", WM8753_RECMIX1, 0, 4, 7, 1, rec_mix_tlv), |
291 | SOC_SINGLE("Voice Sidetone Capture Volume", WM8753_RECMIX2, 0, 7, 1), | 293 | SOC_SINGLE_TLV("Voice Sidetone Capture Volume", WM8753_RECMIX2, 0, 7, 1, rec_mix_tlv), |
292 | 294 | ||
293 | SOC_DOUBLE_R("Capture Volume", WM8753_LINVOL, WM8753_RINVOL, 0, 63, 0), | 295 | SOC_DOUBLE_R("Capture Volume", WM8753_LINVOL, WM8753_RINVOL, 0, 63, 0), |
294 | SOC_DOUBLE_R("Capture ZC Switch", WM8753_LINVOL, WM8753_RINVOL, 6, 1, 0), | 296 | SOC_DOUBLE_R("Capture ZC Switch", WM8753_LINVOL, WM8753_RINVOL, 6, 1, 0), |
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index 986b5d59cefa..590baea3c4c3 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c | |||
@@ -19,7 +19,6 @@ | |||
19 | #include <linux/version.h> | 19 | #include <linux/version.h> |
20 | #include <linux/kernel.h> | 20 | #include <linux/kernel.h> |
21 | #include <linux/device.h> | 21 | #include <linux/device.h> |
22 | #include <sound/driver.h> | ||
23 | #include <sound/core.h> | 22 | #include <sound/core.h> |
24 | #include <sound/pcm.h> | 23 | #include <sound/pcm.h> |
25 | #include <sound/ac97_codec.h> | 24 | #include <sound/ac97_codec.h> |
@@ -102,7 +101,8 @@ SOC_SINGLE("Speaker Playback ZC Switch", AC97_MASTER, 7, 1, 0), | |||
102 | SOC_SINGLE("Speaker Playback Invert Switch", AC97_MASTER, 6, 1, 0), | 101 | SOC_SINGLE("Speaker Playback Invert Switch", AC97_MASTER, 6, 1, 0), |
103 | SOC_SINGLE("Headphone Playback ZC Switch", AC97_HEADPHONE, 7, 1, 0), | 102 | SOC_SINGLE("Headphone Playback ZC Switch", AC97_HEADPHONE, 7, 1, 0), |
104 | SOC_SINGLE("Mono Playback ZC Switch", AC97_MASTER_MONO, 7, 1, 0), | 103 | SOC_SINGLE("Mono Playback ZC Switch", AC97_MASTER_MONO, 7, 1, 0), |
105 | SOC_SINGLE("Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 0), | 104 | SOC_SINGLE("Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 1), |
105 | SOC_SINGLE("Mono Playback Switch", AC97_MASTER_MONO, 15, 1, 1), | ||
106 | 106 | ||
107 | SOC_SINGLE("ALC Target Volume", AC97_CODEC_CLASS_REV, 12, 15, 0), | 107 | SOC_SINGLE("ALC Target Volume", AC97_CODEC_CLASS_REV, 12, 15, 0), |
108 | SOC_SINGLE("ALC Hold Time", AC97_CODEC_CLASS_REV, 8, 15, 0), | 108 | SOC_SINGLE("ALC Hold Time", AC97_CODEC_CLASS_REV, 8, 15, 0), |
@@ -131,7 +131,7 @@ SOC_SINGLE("Aux Playback Headphone Volume", AC97_CD, 12, 7, 1), | |||
131 | SOC_SINGLE("Aux Playback Speaker Volume", AC97_CD, 8, 7, 1), | 131 | SOC_SINGLE("Aux Playback Speaker Volume", AC97_CD, 8, 7, 1), |
132 | SOC_SINGLE("Aux Playback Phone Volume", AC97_CD, 4, 7, 1), | 132 | SOC_SINGLE("Aux Playback Phone Volume", AC97_CD, 4, 7, 1), |
133 | 133 | ||
134 | SOC_SINGLE("Phone Volume", AC97_PHONE, 0, 15, 0), | 134 | SOC_SINGLE("Phone Volume", AC97_PHONE, 0, 15, 1), |
135 | SOC_DOUBLE("Line Capture Volume", AC97_LINE, 8, 0, 31, 1), | 135 | SOC_DOUBLE("Line Capture Volume", AC97_LINE, 8, 0, 31, 1), |
136 | 136 | ||
137 | SOC_SINGLE("Capture 20dB Boost Switch", AC97_REC_SEL, 14, 1, 0), | 137 | SOC_SINGLE("Capture 20dB Boost Switch", AC97_REC_SEL, 14, 1, 0), |
@@ -145,8 +145,8 @@ SOC_ENUM("Bass Control", wm9712_enum[5]), | |||
145 | SOC_SINGLE("Bass Cut-off Switch", AC97_MASTER_TONE, 12, 1, 1), | 145 | SOC_SINGLE("Bass Cut-off Switch", AC97_MASTER_TONE, 12, 1, 1), |
146 | SOC_SINGLE("Tone Cut-off Switch", AC97_MASTER_TONE, 4, 1, 1), | 146 | SOC_SINGLE("Tone Cut-off Switch", AC97_MASTER_TONE, 4, 1, 1), |
147 | SOC_SINGLE("Playback Attenuate (-6dB) Switch", AC97_MASTER_TONE, 6, 1, 0), | 147 | SOC_SINGLE("Playback Attenuate (-6dB) Switch", AC97_MASTER_TONE, 6, 1, 0), |
148 | SOC_SINGLE("Bass Volume", AC97_MASTER_TONE, 8, 15, 0), | 148 | SOC_SINGLE("Bass Volume", AC97_MASTER_TONE, 8, 15, 1), |
149 | SOC_SINGLE("Treble Volume", AC97_MASTER_TONE, 0, 15, 0), | 149 | SOC_SINGLE("Treble Volume", AC97_MASTER_TONE, 0, 15, 1), |
150 | 150 | ||
151 | SOC_SINGLE("Capture ADC Switch", AC97_REC_GAIN, 15, 1, 1), | 151 | SOC_SINGLE("Capture ADC Switch", AC97_REC_GAIN, 15, 1, 1), |
152 | SOC_ENUM("Capture Volume Steps", wm9712_enum[6]), | 152 | SOC_ENUM("Capture Volume Steps", wm9712_enum[6]), |
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig new file mode 100644 index 000000000000..257101f44e9e --- /dev/null +++ b/sound/soc/fsl/Kconfig | |||
@@ -0,0 +1,20 @@ | |||
1 | menu "ALSA SoC audio for Freescale SOCs" | ||
2 | |||
3 | config SND_SOC_MPC8610 | ||
4 | bool "ALSA SoC support for the MPC8610 SOC" | ||
5 | depends on SND_SOC && MPC8610_HPCD | ||
6 | default y if MPC8610 | ||
7 | help | ||
8 | Say Y if you want to add support for codecs attached to the SSI | ||
9 | device on an MPC8610. | ||
10 | |||
11 | config SND_SOC_MPC8610_HPCD | ||
12 | bool "ALSA SoC support for the Freescale MPC8610 HPCD board" | ||
13 | depends on SND_SOC_MPC8610 | ||
14 | select SND_SOC_CS4270 | ||
15 | select SND_SOC_CS4270_VD33_ERRATA | ||
16 | default y if MPC8610_HPCD | ||
17 | help | ||
18 | Say Y if you want to enable audio on the Freescale MPC8610 HPCD. | ||
19 | |||
20 | endmenu | ||
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile new file mode 100644 index 000000000000..62f680a4a776 --- /dev/null +++ b/sound/soc/fsl/Makefile | |||
@@ -0,0 +1,6 @@ | |||
1 | # MPC8610 HPCD Machine Support | ||
2 | obj-$(CONFIG_SND_SOC_MPC8610_HPCD) += mpc8610_hpcd.o | ||
3 | |||
4 | # MPC8610 Platform Support | ||
5 | obj-$(CONFIG_SND_SOC_MPC8610) += fsl_ssi.o fsl_dma.o | ||
6 | |||
diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c new file mode 100644 index 000000000000..652514fc8142 --- /dev/null +++ b/sound/soc/fsl/fsl_dma.c | |||
@@ -0,0 +1,841 @@ | |||
1 | /* | ||
2 | * Freescale DMA ALSA SoC PCM driver | ||
3 | * | ||
4 | * Author: Timur Tabi <timur@freescale.com> | ||
5 | * | ||
6 | * Copyright 2007-2008 Freescale Semiconductor, Inc. This file is licensed | ||
7 | * under the terms of the GNU General Public License version 2. This | ||
8 | * program is licensed "as is" without any warranty of any kind, whether | ||
9 | * express or implied. | ||
10 | * | ||
11 | * This driver implements ASoC support for the Elo DMA controller, which is | ||
12 | * the DMA controller on Freescale 83xx, 85xx, and 86xx SOCs. In ALSA terms, | ||
13 | * the PCM driver is what handles the DMA buffer. | ||
14 | */ | ||
15 | |||
16 | #include <linux/module.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/platform_device.h> | ||
19 | #include <linux/dma-mapping.h> | ||
20 | #include <linux/interrupt.h> | ||
21 | #include <linux/delay.h> | ||
22 | |||
23 | #include <sound/driver.h> | ||
24 | #include <sound/core.h> | ||
25 | #include <sound/pcm.h> | ||
26 | #include <sound/pcm_params.h> | ||
27 | #include <sound/soc.h> | ||
28 | |||
29 | #include <asm/io.h> | ||
30 | |||
31 | #include "fsl_dma.h" | ||
32 | |||
33 | /* | ||
34 | * The formats that the DMA controller supports, which is anything | ||
35 | * that is 8, 16, or 32 bits. | ||
36 | */ | ||
37 | #define FSLDMA_PCM_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ | ||
38 | SNDRV_PCM_FMTBIT_U8 | \ | ||
39 | SNDRV_PCM_FMTBIT_S16_LE | \ | ||
40 | SNDRV_PCM_FMTBIT_S16_BE | \ | ||
41 | SNDRV_PCM_FMTBIT_U16_LE | \ | ||
42 | SNDRV_PCM_FMTBIT_U16_BE | \ | ||
43 | SNDRV_PCM_FMTBIT_S24_LE | \ | ||
44 | SNDRV_PCM_FMTBIT_S24_BE | \ | ||
45 | SNDRV_PCM_FMTBIT_U24_LE | \ | ||
46 | SNDRV_PCM_FMTBIT_U24_BE | \ | ||
47 | SNDRV_PCM_FMTBIT_S32_LE | \ | ||
48 | SNDRV_PCM_FMTBIT_S32_BE | \ | ||
49 | SNDRV_PCM_FMTBIT_U32_LE | \ | ||
50 | SNDRV_PCM_FMTBIT_U32_BE) | ||
51 | |||
52 | #define FSLDMA_PCM_RATES (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_192000 | \ | ||
53 | SNDRV_PCM_RATE_CONTINUOUS) | ||
54 | |||
55 | /* DMA global data. This structure is used by fsl_dma_open() to determine | ||
56 | * which DMA channels to assign to a substream. Unfortunately, ASoC V1 does | ||
57 | * not allow the machine driver to provide this information to the PCM | ||
58 | * driver in advance, and there's no way to differentiate between the two | ||
59 | * DMA controllers. So for now, this driver only supports one SSI device | ||
60 | * using two DMA channels. We cannot support multiple DMA devices. | ||
61 | * | ||
62 | * ssi_stx_phys: bus address of SSI STX register | ||
63 | * ssi_srx_phys: bus address of SSI SRX register | ||
64 | * dma_channel: pointer to the DMA channel's registers | ||
65 | * irq: IRQ for this DMA channel | ||
66 | * assigned: set to 1 if that DMA channel is assigned to a substream | ||
67 | */ | ||
68 | static struct { | ||
69 | dma_addr_t ssi_stx_phys; | ||
70 | dma_addr_t ssi_srx_phys; | ||
71 | struct ccsr_dma_channel __iomem *dma_channel[2]; | ||
72 | unsigned int irq[2]; | ||
73 | unsigned int assigned[2]; | ||
74 | } dma_global_data; | ||
75 | |||
76 | /* | ||
77 | * The number of DMA links to use. Two is the bare minimum, but if you | ||
78 | * have really small links you might need more. | ||
79 | */ | ||
80 | #define NUM_DMA_LINKS 2 | ||
81 | |||
82 | /** fsl_dma_private: p-substream DMA data | ||
83 | * | ||
84 | * Each substream has a 1-to-1 association with a DMA channel. | ||
85 | * | ||
86 | * The link[] array is first because it needs to be aligned on a 32-byte | ||
87 | * boundary, so putting it first will ensure alignment without padding the | ||
88 | * structure. | ||
89 | * | ||
90 | * @link[]: array of link descriptors | ||
91 | * @controller_id: which DMA controller (0, 1, ...) | ||
92 | * @channel_id: which DMA channel on the controller (0, 1, 2, ...) | ||
93 | * @dma_channel: pointer to the DMA channel's registers | ||
94 | * @irq: IRQ for this DMA channel | ||
95 | * @substream: pointer to the substream object, needed by the ISR | ||
96 | * @ssi_sxx_phys: bus address of the STX or SRX register to use | ||
97 | * @ld_buf_phys: physical address of the LD buffer | ||
98 | * @current_link: index into link[] of the link currently being processed | ||
99 | * @dma_buf_phys: physical address of the DMA buffer | ||
100 | * @dma_buf_next: physical address of the next period to process | ||
101 | * @dma_buf_end: physical address of the byte after the end of the DMA | ||
102 | * @buffer period_size: the size of a single period | ||
103 | * @num_periods: the number of periods in the DMA buffer | ||
104 | */ | ||
105 | struct fsl_dma_private { | ||
106 | struct fsl_dma_link_descriptor link[NUM_DMA_LINKS]; | ||
107 | unsigned int controller_id; | ||
108 | unsigned int channel_id; | ||
109 | struct ccsr_dma_channel __iomem *dma_channel; | ||
110 | unsigned int irq; | ||
111 | struct snd_pcm_substream *substream; | ||
112 | dma_addr_t ssi_sxx_phys; | ||
113 | dma_addr_t ld_buf_phys; | ||
114 | unsigned int current_link; | ||
115 | dma_addr_t dma_buf_phys; | ||
116 | dma_addr_t dma_buf_next; | ||
117 | dma_addr_t dma_buf_end; | ||
118 | size_t period_size; | ||
119 | unsigned int num_periods; | ||
120 | }; | ||
121 | |||
122 | /** | ||
123 | * fsl_dma_hardare: define characteristics of the PCM hardware. | ||
124 | * | ||
125 | * The PCM hardware is the Freescale DMA controller. This structure defines | ||
126 | * the capabilities of that hardware. | ||
127 | * | ||
128 | * Since the sampling rate and data format are not controlled by the DMA | ||
129 | * controller, we specify no limits for those values. The only exception is | ||
130 | * period_bytes_min, which is set to a reasonably low value to prevent the | ||
131 | * DMA controller from generating too many interrupts per second. | ||
132 | * | ||
133 | * Since each link descriptor has a 32-bit byte count field, we set | ||
134 | * period_bytes_max to the largest 32-bit number. We also have no maximum | ||
135 | * number of periods. | ||
136 | */ | ||
137 | static const struct snd_pcm_hardware fsl_dma_hardware = { | ||
138 | |||
139 | .info = SNDRV_PCM_INFO_INTERLEAVED | | ||
140 | SNDRV_PCM_INFO_MMAP | | ||
141 | SNDRV_PCM_INFO_MMAP_VALID, | ||
142 | .formats = FSLDMA_PCM_FORMATS, | ||
143 | .rates = FSLDMA_PCM_RATES, | ||
144 | .rate_min = 5512, | ||
145 | .rate_max = 192000, | ||
146 | .period_bytes_min = 512, /* A reasonable limit */ | ||
147 | .period_bytes_max = (u32) -1, | ||
148 | .periods_min = NUM_DMA_LINKS, | ||
149 | .periods_max = (unsigned int) -1, | ||
150 | .buffer_bytes_max = 128 * 1024, /* A reasonable limit */ | ||
151 | }; | ||
152 | |||
153 | /** | ||
154 | * fsl_dma_abort_stream: tell ALSA that the DMA transfer has aborted | ||
155 | * | ||
156 | * This function should be called by the ISR whenever the DMA controller | ||
157 | * halts data transfer. | ||
158 | */ | ||
159 | static void fsl_dma_abort_stream(struct snd_pcm_substream *substream) | ||
160 | { | ||
161 | unsigned long flags; | ||
162 | |||
163 | snd_pcm_stream_lock_irqsave(substream, flags); | ||
164 | |||
165 | if (snd_pcm_running(substream)) | ||
166 | snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); | ||
167 | |||
168 | snd_pcm_stream_unlock_irqrestore(substream, flags); | ||
169 | } | ||
170 | |||
171 | /** | ||
172 | * fsl_dma_update_pointers - update LD pointers to point to the next period | ||
173 | * | ||
174 | * As each period is completed, this function changes the the link | ||
175 | * descriptor pointers for that period to point to the next period. | ||
176 | */ | ||
177 | static void fsl_dma_update_pointers(struct fsl_dma_private *dma_private) | ||
178 | { | ||
179 | struct fsl_dma_link_descriptor *link = | ||
180 | &dma_private->link[dma_private->current_link]; | ||
181 | |||
182 | /* Update our link descriptors to point to the next period */ | ||
183 | if (dma_private->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
184 | link->source_addr = | ||
185 | cpu_to_be32(dma_private->dma_buf_next); | ||
186 | else | ||
187 | link->dest_addr = | ||
188 | cpu_to_be32(dma_private->dma_buf_next); | ||
189 | |||
190 | /* Update our variables for next time */ | ||
191 | dma_private->dma_buf_next += dma_private->period_size; | ||
192 | |||
193 | if (dma_private->dma_buf_next >= dma_private->dma_buf_end) | ||
194 | dma_private->dma_buf_next = dma_private->dma_buf_phys; | ||
195 | |||
196 | if (++dma_private->current_link >= NUM_DMA_LINKS) | ||
197 | dma_private->current_link = 0; | ||
198 | } | ||
199 | |||
200 | /** | ||
201 | * fsl_dma_isr: interrupt handler for the DMA controller | ||
202 | * | ||
203 | * @irq: IRQ of the DMA channel | ||
204 | * @dev_id: pointer to the dma_private structure for this DMA channel | ||
205 | */ | ||
206 | static irqreturn_t fsl_dma_isr(int irq, void *dev_id) | ||
207 | { | ||
208 | struct fsl_dma_private *dma_private = dev_id; | ||
209 | struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel; | ||
210 | irqreturn_t ret = IRQ_NONE; | ||
211 | u32 sr, sr2 = 0; | ||
212 | |||
213 | /* We got an interrupt, so read the status register to see what we | ||
214 | were interrupted for. | ||
215 | */ | ||
216 | sr = in_be32(&dma_channel->sr); | ||
217 | |||
218 | if (sr & CCSR_DMA_SR_TE) { | ||
219 | dev_err(dma_private->substream->pcm->card->dev, | ||
220 | "DMA transmit error (controller=%u channel=%u irq=%u\n", | ||
221 | dma_private->controller_id, | ||
222 | dma_private->channel_id, irq); | ||
223 | fsl_dma_abort_stream(dma_private->substream); | ||
224 | sr2 |= CCSR_DMA_SR_TE; | ||
225 | ret = IRQ_HANDLED; | ||
226 | } | ||
227 | |||
228 | if (sr & CCSR_DMA_SR_CH) | ||
229 | ret = IRQ_HANDLED; | ||
230 | |||
231 | if (sr & CCSR_DMA_SR_PE) { | ||
232 | dev_err(dma_private->substream->pcm->card->dev, | ||
233 | "DMA%u programming error (channel=%u irq=%u)\n", | ||
234 | dma_private->controller_id, | ||
235 | dma_private->channel_id, irq); | ||
236 | fsl_dma_abort_stream(dma_private->substream); | ||
237 | sr2 |= CCSR_DMA_SR_PE; | ||
238 | ret = IRQ_HANDLED; | ||
239 | } | ||
240 | |||
241 | if (sr & CCSR_DMA_SR_EOLNI) { | ||
242 | sr2 |= CCSR_DMA_SR_EOLNI; | ||
243 | ret = IRQ_HANDLED; | ||
244 | } | ||
245 | |||
246 | if (sr & CCSR_DMA_SR_CB) | ||
247 | ret = IRQ_HANDLED; | ||
248 | |||
249 | if (sr & CCSR_DMA_SR_EOSI) { | ||
250 | struct snd_pcm_substream *substream = dma_private->substream; | ||
251 | |||
252 | /* Tell ALSA we completed a period. */ | ||
253 | snd_pcm_period_elapsed(substream); | ||
254 | |||
255 | /* | ||
256 | * Update our link descriptors to point to the next period. We | ||
257 | * only need to do this if the number of periods is not equal to | ||
258 | * the number of links. | ||
259 | */ | ||
260 | if (dma_private->num_periods != NUM_DMA_LINKS) | ||
261 | fsl_dma_update_pointers(dma_private); | ||
262 | |||
263 | sr2 |= CCSR_DMA_SR_EOSI; | ||
264 | ret = IRQ_HANDLED; | ||
265 | } | ||
266 | |||
267 | if (sr & CCSR_DMA_SR_EOLSI) { | ||
268 | sr2 |= CCSR_DMA_SR_EOLSI; | ||
269 | ret = IRQ_HANDLED; | ||
270 | } | ||
271 | |||
272 | /* Clear the bits that we set */ | ||
273 | if (sr2) | ||
274 | out_be32(&dma_channel->sr, sr2); | ||
275 | |||
276 | return ret; | ||
277 | } | ||
278 | |||
279 | /** | ||
280 | * fsl_dma_new: initialize this PCM driver. | ||
281 | * | ||
282 | * This function is called when the codec driver calls snd_soc_new_pcms(), | ||
283 | * once for each .dai_link in the machine driver's snd_soc_machine | ||
284 | * structure. | ||
285 | */ | ||
286 | static int fsl_dma_new(struct snd_card *card, struct snd_soc_codec_dai *dai, | ||
287 | struct snd_pcm *pcm) | ||
288 | { | ||
289 | static u64 fsl_dma_dmamask = DMA_BIT_MASK(32); | ||
290 | int ret; | ||
291 | |||
292 | if (!card->dev->dma_mask) | ||
293 | card->dev->dma_mask = &fsl_dma_dmamask; | ||
294 | |||
295 | if (!card->dev->coherent_dma_mask) | ||
296 | card->dev->coherent_dma_mask = fsl_dma_dmamask; | ||
297 | |||
298 | ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev, | ||
299 | fsl_dma_hardware.buffer_bytes_max, | ||
300 | &pcm->streams[0].substream->dma_buffer); | ||
301 | if (ret) { | ||
302 | dev_err(card->dev, | ||
303 | "Can't allocate playback DMA buffer (size=%u)\n", | ||
304 | fsl_dma_hardware.buffer_bytes_max); | ||
305 | return -ENOMEM; | ||
306 | } | ||
307 | |||
308 | ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev, | ||
309 | fsl_dma_hardware.buffer_bytes_max, | ||
310 | &pcm->streams[1].substream->dma_buffer); | ||
311 | if (ret) { | ||
312 | snd_dma_free_pages(&pcm->streams[0].substream->dma_buffer); | ||
313 | dev_err(card->dev, | ||
314 | "Can't allocate capture DMA buffer (size=%u)\n", | ||
315 | fsl_dma_hardware.buffer_bytes_max); | ||
316 | return -ENOMEM; | ||
317 | } | ||
318 | |||
319 | return 0; | ||
320 | } | ||
321 | |||
322 | /** | ||
323 | * fsl_dma_open: open a new substream. | ||
324 | * | ||
325 | * Each substream has its own DMA buffer. | ||
326 | */ | ||
327 | static int fsl_dma_open(struct snd_pcm_substream *substream) | ||
328 | { | ||
329 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
330 | struct fsl_dma_private *dma_private; | ||
331 | dma_addr_t ld_buf_phys; | ||
332 | unsigned int channel; | ||
333 | int ret = 0; | ||
334 | |||
335 | /* | ||
336 | * Reject any DMA buffer whose size is not a multiple of the period | ||
337 | * size. We need to make sure that the DMA buffer can be evenly divided | ||
338 | * into periods. | ||
339 | */ | ||
340 | ret = snd_pcm_hw_constraint_integer(runtime, | ||
341 | SNDRV_PCM_HW_PARAM_PERIODS); | ||
342 | if (ret < 0) { | ||
343 | dev_err(substream->pcm->card->dev, "invalid buffer size\n"); | ||
344 | return ret; | ||
345 | } | ||
346 | |||
347 | channel = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1; | ||
348 | |||
349 | if (dma_global_data.assigned[channel]) { | ||
350 | dev_err(substream->pcm->card->dev, | ||
351 | "DMA channel already assigned\n"); | ||
352 | return -EBUSY; | ||
353 | } | ||
354 | |||
355 | dma_private = dma_alloc_coherent(substream->pcm->dev, | ||
356 | sizeof(struct fsl_dma_private), &ld_buf_phys, GFP_KERNEL); | ||
357 | if (!dma_private) { | ||
358 | dev_err(substream->pcm->card->dev, | ||
359 | "can't allocate DMA private data\n"); | ||
360 | return -ENOMEM; | ||
361 | } | ||
362 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
363 | dma_private->ssi_sxx_phys = dma_global_data.ssi_stx_phys; | ||
364 | else | ||
365 | dma_private->ssi_sxx_phys = dma_global_data.ssi_srx_phys; | ||
366 | |||
367 | dma_private->dma_channel = dma_global_data.dma_channel[channel]; | ||
368 | dma_private->irq = dma_global_data.irq[channel]; | ||
369 | dma_private->substream = substream; | ||
370 | dma_private->ld_buf_phys = ld_buf_phys; | ||
371 | dma_private->dma_buf_phys = substream->dma_buffer.addr; | ||
372 | |||
373 | /* We only support one DMA controller for now */ | ||
374 | dma_private->controller_id = 0; | ||
375 | dma_private->channel_id = channel; | ||
376 | |||
377 | ret = request_irq(dma_private->irq, fsl_dma_isr, 0, "DMA", dma_private); | ||
378 | if (ret) { | ||
379 | dev_err(substream->pcm->card->dev, | ||
380 | "can't register ISR for IRQ %u (ret=%i)\n", | ||
381 | dma_private->irq, ret); | ||
382 | dma_free_coherent(substream->pcm->dev, | ||
383 | sizeof(struct fsl_dma_private), | ||
384 | dma_private, dma_private->ld_buf_phys); | ||
385 | return ret; | ||
386 | } | ||
387 | |||
388 | dma_global_data.assigned[channel] = 1; | ||
389 | |||
390 | snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); | ||
391 | snd_soc_set_runtime_hwparams(substream, &fsl_dma_hardware); | ||
392 | runtime->private_data = dma_private; | ||
393 | |||
394 | return 0; | ||
395 | } | ||
396 | |||
397 | /** | ||
398 | * fsl_dma_hw_params: allocate the DMA buffer and the DMA link descriptors. | ||
399 | * | ||
400 | * ALSA divides the DMA buffer into N periods. We create NUM_DMA_LINKS link | ||
401 | * descriptors that ping-pong from one period to the next. For example, if | ||
402 | * there are six periods and two link descriptors, this is how they look | ||
403 | * before playback starts: | ||
404 | * | ||
405 | * The last link descriptor | ||
406 | * ____________ points back to the first | ||
407 | * | | | ||
408 | * V | | ||
409 | * ___ ___ | | ||
410 | * | |->| |->| | ||
411 | * |___| |___| | ||
412 | * | | | ||
413 | * | | | ||
414 | * V V | ||
415 | * _________________________________________ | ||
416 | * | | | | | | | The DMA buffer is | ||
417 | * | | | | | | | divided into 6 parts | ||
418 | * |______|______|______|______|______|______| | ||
419 | * | ||
420 | * and here's how they look after the first period is finished playing: | ||
421 | * | ||
422 | * ____________ | ||
423 | * | | | ||
424 | * V | | ||
425 | * ___ ___ | | ||
426 | * | |->| |->| | ||
427 | * |___| |___| | ||
428 | * | | | ||
429 | * |______________ | ||
430 | * | | | ||
431 | * V V | ||
432 | * _________________________________________ | ||
433 | * | | | | | | | | ||
434 | * | | | | | | | | ||
435 | * |______|______|______|______|______|______| | ||
436 | * | ||
437 | * The first link descriptor now points to the third period. The DMA | ||
438 | * controller is currently playing the second period. When it finishes, it | ||
439 | * will jump back to the first descriptor and play the third period. | ||
440 | * | ||
441 | * There are four reasons we do this: | ||
442 | * | ||
443 | * 1. The only way to get the DMA controller to automatically restart the | ||
444 | * transfer when it gets to the end of the buffer is to use chaining | ||
445 | * mode. Basic direct mode doesn't offer that feature. | ||
446 | * 2. We need to receive an interrupt at the end of every period. The DMA | ||
447 | * controller can generate an interrupt at the end of every link transfer | ||
448 | * (aka segment). Making each period into a DMA segment will give us the | ||
449 | * interrupts we need. | ||
450 | * 3. By creating only two link descriptors, regardless of the number of | ||
451 | * periods, we do not need to reallocate the link descriptors if the | ||
452 | * number of periods changes. | ||
453 | * 4. All of the audio data is still stored in a single, contiguous DMA | ||
454 | * buffer, which is what ALSA expects. We're just dividing it into | ||
455 | * contiguous parts, and creating a link descriptor for each one. | ||
456 | * | ||
457 | * Note that due to a quirk of the SSI's STX register, the target address | ||
458 | * for the DMA operations depends on the sample size. So we don't program | ||
459 | * the dest_addr (for playback -- source_addr for capture) fields in the | ||
460 | * link descriptors here. We do that in fsl_dma_prepare() | ||
461 | */ | ||
462 | static int fsl_dma_hw_params(struct snd_pcm_substream *substream, | ||
463 | struct snd_pcm_hw_params *hw_params) | ||
464 | { | ||
465 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
466 | struct fsl_dma_private *dma_private = runtime->private_data; | ||
467 | struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel; | ||
468 | |||
469 | dma_addr_t temp_addr; /* Pointer to next period */ | ||
470 | u64 temp_link; /* Pointer to next link descriptor */ | ||
471 | u32 mr; /* Temporary variable for MR register */ | ||
472 | |||
473 | unsigned int i; | ||
474 | |||
475 | /* Get all the parameters we need */ | ||
476 | size_t buffer_size = params_buffer_bytes(hw_params); | ||
477 | size_t period_size = params_period_bytes(hw_params); | ||
478 | |||
479 | /* Initialize our DMA tracking variables */ | ||
480 | dma_private->period_size = period_size; | ||
481 | dma_private->num_periods = params_periods(hw_params); | ||
482 | dma_private->dma_buf_end = dma_private->dma_buf_phys + buffer_size; | ||
483 | dma_private->dma_buf_next = dma_private->dma_buf_phys + | ||
484 | (NUM_DMA_LINKS * period_size); | ||
485 | if (dma_private->dma_buf_next >= dma_private->dma_buf_end) | ||
486 | dma_private->dma_buf_next = dma_private->dma_buf_phys; | ||
487 | |||
488 | /* | ||
489 | * Initialize each link descriptor. | ||
490 | * | ||
491 | * The actual address in STX0 (destination for playback, source for | ||
492 | * capture) is based on the sample size, but we don't know the sample | ||
493 | * size in this function, so we'll have to adjust that later. See | ||
494 | * comments in fsl_dma_prepare(). | ||
495 | * | ||
496 | * The DMA controller does not have a cache, so the CPU does not | ||
497 | * need to tell it to flush its cache. However, the DMA | ||
498 | * controller does need to tell the CPU to flush its cache. | ||
499 | * That's what the SNOOP bit does. | ||
500 | * | ||
501 | * Also, even though the DMA controller supports 36-bit addressing, for | ||
502 | * simplicity we currently support only 32-bit addresses for the audio | ||
503 | * buffer itself. | ||
504 | */ | ||
505 | temp_addr = substream->dma_buffer.addr; | ||
506 | temp_link = dma_private->ld_buf_phys + | ||
507 | sizeof(struct fsl_dma_link_descriptor); | ||
508 | |||
509 | for (i = 0; i < NUM_DMA_LINKS; i++) { | ||
510 | struct fsl_dma_link_descriptor *link = &dma_private->link[i]; | ||
511 | |||
512 | link->count = cpu_to_be32(period_size); | ||
513 | link->source_attr = cpu_to_be32(CCSR_DMA_ATR_SNOOP); | ||
514 | link->dest_attr = cpu_to_be32(CCSR_DMA_ATR_SNOOP); | ||
515 | link->next = cpu_to_be64(temp_link); | ||
516 | |||
517 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
518 | link->source_addr = cpu_to_be32(temp_addr); | ||
519 | else | ||
520 | link->dest_addr = cpu_to_be32(temp_addr); | ||
521 | |||
522 | temp_addr += period_size; | ||
523 | temp_link += sizeof(struct fsl_dma_link_descriptor); | ||
524 | } | ||
525 | /* The last link descriptor points to the first */ | ||
526 | dma_private->link[i - 1].next = cpu_to_be64(dma_private->ld_buf_phys); | ||
527 | |||
528 | /* Tell the DMA controller where the first link descriptor is */ | ||
529 | out_be32(&dma_channel->clndar, | ||
530 | CCSR_DMA_CLNDAR_ADDR(dma_private->ld_buf_phys)); | ||
531 | out_be32(&dma_channel->eclndar, | ||
532 | CCSR_DMA_ECLNDAR_ADDR(dma_private->ld_buf_phys)); | ||
533 | |||
534 | /* The manual says the BCR must be clear before enabling EMP */ | ||
535 | out_be32(&dma_channel->bcr, 0); | ||
536 | |||
537 | /* | ||
538 | * Program the mode register for interrupts, external master control, | ||
539 | * and source/destination hold. Also clear the Channel Abort bit. | ||
540 | */ | ||
541 | mr = in_be32(&dma_channel->mr) & | ||
542 | ~(CCSR_DMA_MR_CA | CCSR_DMA_MR_DAHE | CCSR_DMA_MR_SAHE); | ||
543 | |||
544 | /* | ||
545 | * We want External Master Start and External Master Pause enabled, | ||
546 | * because the SSI is controlling the DMA controller. We want the DMA | ||
547 | * controller to be set up in advance, and then we signal only the SSI | ||
548 | * to start transfering. | ||
549 | * | ||
550 | * We want End-Of-Segment Interrupts enabled, because this will generate | ||
551 | * an interrupt at the end of each segment (each link descriptor | ||
552 | * represents one segment). Each DMA segment is the same thing as an | ||
553 | * ALSA period, so this is how we get an interrupt at the end of every | ||
554 | * period. | ||
555 | * | ||
556 | * We want Error Interrupt enabled, so that we can get an error if | ||
557 | * the DMA controller is mis-programmed somehow. | ||
558 | */ | ||
559 | mr |= CCSR_DMA_MR_EOSIE | CCSR_DMA_MR_EIE | CCSR_DMA_MR_EMP_EN | | ||
560 | CCSR_DMA_MR_EMS_EN; | ||
561 | |||
562 | /* For playback, we want the destination address to be held. For | ||
563 | capture, set the source address to be held. */ | ||
564 | mr |= (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? | ||
565 | CCSR_DMA_MR_DAHE : CCSR_DMA_MR_SAHE; | ||
566 | |||
567 | out_be32(&dma_channel->mr, mr); | ||
568 | |||
569 | return 0; | ||
570 | } | ||
571 | |||
572 | /** | ||
573 | * fsl_dma_prepare - prepare the DMA registers for playback. | ||
574 | * | ||
575 | * This function is called after the specifics of the audio data are known, | ||
576 | * i.e. snd_pcm_runtime is initialized. | ||
577 | * | ||
578 | * In this function, we finish programming the registers of the DMA | ||
579 | * controller that are dependent on the sample size. | ||
580 | * | ||
581 | * One of the drawbacks with big-endian is that when copying integers of | ||
582 | * different sizes to a fixed-sized register, the address to which the | ||
583 | * integer must be copied is dependent on the size of the integer. | ||
584 | * | ||
585 | * For example, if P is the address of a 32-bit register, and X is a 32-bit | ||
586 | * integer, then X should be copied to address P. However, if X is a 16-bit | ||
587 | * integer, then it should be copied to P+2. If X is an 8-bit register, | ||
588 | * then it should be copied to P+3. | ||
589 | * | ||
590 | * So for playback of 8-bit samples, the DMA controller must transfer single | ||
591 | * bytes from the DMA buffer to the last byte of the STX0 register, i.e. | ||
592 | * offset by 3 bytes. For 16-bit samples, the offset is two bytes. | ||
593 | * | ||
594 | * For 24-bit samples, the offset is 1 byte. However, the DMA controller | ||
595 | * does not support 3-byte copies (the DAHTS register supports only 1, 2, 4, | ||
596 | * and 8 bytes at a time). So we do not support packed 24-bit samples. | ||
597 | * 24-bit data must be padded to 32 bits. | ||
598 | */ | ||
599 | static int fsl_dma_prepare(struct snd_pcm_substream *substream) | ||
600 | { | ||
601 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
602 | struct fsl_dma_private *dma_private = runtime->private_data; | ||
603 | struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel; | ||
604 | u32 mr; | ||
605 | unsigned int i; | ||
606 | dma_addr_t ssi_sxx_phys; /* Bus address of SSI STX register */ | ||
607 | unsigned int frame_size; /* Number of bytes per frame */ | ||
608 | |||
609 | ssi_sxx_phys = dma_private->ssi_sxx_phys; | ||
610 | |||
611 | mr = in_be32(&dma_channel->mr) & ~(CCSR_DMA_MR_BWC_MASK | | ||
612 | CCSR_DMA_MR_SAHTS_MASK | CCSR_DMA_MR_DAHTS_MASK); | ||
613 | |||
614 | switch (runtime->sample_bits) { | ||
615 | case 8: | ||
616 | mr |= CCSR_DMA_MR_DAHTS_1 | CCSR_DMA_MR_SAHTS_1; | ||
617 | ssi_sxx_phys += 3; | ||
618 | break; | ||
619 | case 16: | ||
620 | mr |= CCSR_DMA_MR_DAHTS_2 | CCSR_DMA_MR_SAHTS_2; | ||
621 | ssi_sxx_phys += 2; | ||
622 | break; | ||
623 | case 32: | ||
624 | mr |= CCSR_DMA_MR_DAHTS_4 | CCSR_DMA_MR_SAHTS_4; | ||
625 | break; | ||
626 | default: | ||
627 | dev_err(substream->pcm->card->dev, | ||
628 | "unsupported sample size %u\n", runtime->sample_bits); | ||
629 | return -EINVAL; | ||
630 | } | ||
631 | |||
632 | frame_size = runtime->frame_bits / 8; | ||
633 | /* | ||
634 | * BWC should always be a multiple of the frame size. BWC determines | ||
635 | * how many bytes are sent/received before the DMA controller checks the | ||
636 | * SSI to see if it needs to stop. For playback, the transmit FIFO can | ||
637 | * hold three frames, so we want to send two frames at a time. For | ||
638 | * capture, the receive FIFO is triggered when it contains one frame, so | ||
639 | * we want to receive one frame at a time. | ||
640 | */ | ||
641 | |||
642 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
643 | mr |= CCSR_DMA_MR_BWC(2 * frame_size); | ||
644 | else | ||
645 | mr |= CCSR_DMA_MR_BWC(frame_size); | ||
646 | |||
647 | out_be32(&dma_channel->mr, mr); | ||
648 | |||
649 | /* | ||
650 | * Program the address of the DMA transfer to/from the SSI. | ||
651 | */ | ||
652 | for (i = 0; i < NUM_DMA_LINKS; i++) { | ||
653 | struct fsl_dma_link_descriptor *link = &dma_private->link[i]; | ||
654 | |||
655 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
656 | link->dest_addr = cpu_to_be32(ssi_sxx_phys); | ||
657 | else | ||
658 | link->source_addr = cpu_to_be32(ssi_sxx_phys); | ||
659 | } | ||
660 | |||
661 | return 0; | ||
662 | } | ||
663 | |||
664 | /** | ||
665 | * fsl_dma_pointer: determine the current position of the DMA transfer | ||
666 | * | ||
667 | * This function is called by ALSA when ALSA wants to know where in the | ||
668 | * stream buffer the hardware currently is. | ||
669 | * | ||
670 | * For playback, the SAR register contains the physical address of the most | ||
671 | * recent DMA transfer. For capture, the value is in the DAR register. | ||
672 | * | ||
673 | * The base address of the buffer is stored in the source_addr field of the | ||
674 | * first link descriptor. | ||
675 | */ | ||
676 | static snd_pcm_uframes_t fsl_dma_pointer(struct snd_pcm_substream *substream) | ||
677 | { | ||
678 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
679 | struct fsl_dma_private *dma_private = runtime->private_data; | ||
680 | struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel; | ||
681 | dma_addr_t position; | ||
682 | snd_pcm_uframes_t frames; | ||
683 | |||
684 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
685 | position = in_be32(&dma_channel->sar); | ||
686 | else | ||
687 | position = in_be32(&dma_channel->dar); | ||
688 | |||
689 | frames = bytes_to_frames(runtime, position - dma_private->dma_buf_phys); | ||
690 | |||
691 | /* | ||
692 | * If the current address is just past the end of the buffer, wrap it | ||
693 | * around. | ||
694 | */ | ||
695 | if (frames == runtime->buffer_size) | ||
696 | frames = 0; | ||
697 | |||
698 | return frames; | ||
699 | } | ||
700 | |||
701 | /** | ||
702 | * fsl_dma_hw_free: release resources allocated in fsl_dma_hw_params() | ||
703 | * | ||
704 | * Release the resources allocated in fsl_dma_hw_params() and de-program the | ||
705 | * registers. | ||
706 | * | ||
707 | * This function can be called multiple times. | ||
708 | */ | ||
709 | static int fsl_dma_hw_free(struct snd_pcm_substream *substream) | ||
710 | { | ||
711 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
712 | struct fsl_dma_private *dma_private = runtime->private_data; | ||
713 | |||
714 | if (dma_private) { | ||
715 | struct ccsr_dma_channel __iomem *dma_channel; | ||
716 | |||
717 | dma_channel = dma_private->dma_channel; | ||
718 | |||
719 | /* Stop the DMA */ | ||
720 | out_be32(&dma_channel->mr, CCSR_DMA_MR_CA); | ||
721 | out_be32(&dma_channel->mr, 0); | ||
722 | |||
723 | /* Reset all the other registers */ | ||
724 | out_be32(&dma_channel->sr, -1); | ||
725 | out_be32(&dma_channel->clndar, 0); | ||
726 | out_be32(&dma_channel->eclndar, 0); | ||
727 | out_be32(&dma_channel->satr, 0); | ||
728 | out_be32(&dma_channel->sar, 0); | ||
729 | out_be32(&dma_channel->datr, 0); | ||
730 | out_be32(&dma_channel->dar, 0); | ||
731 | out_be32(&dma_channel->bcr, 0); | ||
732 | out_be32(&dma_channel->nlndar, 0); | ||
733 | out_be32(&dma_channel->enlndar, 0); | ||
734 | } | ||
735 | |||
736 | return 0; | ||
737 | } | ||
738 | |||
739 | /** | ||
740 | * fsl_dma_close: close the stream. | ||
741 | */ | ||
742 | static int fsl_dma_close(struct snd_pcm_substream *substream) | ||
743 | { | ||
744 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
745 | struct fsl_dma_private *dma_private = runtime->private_data; | ||
746 | int dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1; | ||
747 | |||
748 | if (dma_private) { | ||
749 | if (dma_private->irq) | ||
750 | free_irq(dma_private->irq, dma_private); | ||
751 | |||
752 | if (dma_private->ld_buf_phys) { | ||
753 | dma_unmap_single(substream->pcm->dev, | ||
754 | dma_private->ld_buf_phys, | ||
755 | sizeof(dma_private->link), DMA_TO_DEVICE); | ||
756 | } | ||
757 | |||
758 | /* Deallocate the fsl_dma_private structure */ | ||
759 | dma_free_coherent(substream->pcm->dev, | ||
760 | sizeof(struct fsl_dma_private), | ||
761 | dma_private, dma_private->ld_buf_phys); | ||
762 | substream->runtime->private_data = NULL; | ||
763 | } | ||
764 | |||
765 | dma_global_data.assigned[dir] = 0; | ||
766 | |||
767 | return 0; | ||
768 | } | ||
769 | |||
770 | /* | ||
771 | * Remove this PCM driver. | ||
772 | */ | ||
773 | static void fsl_dma_free_dma_buffers(struct snd_pcm *pcm) | ||
774 | { | ||
775 | struct snd_pcm_substream *substream; | ||
776 | unsigned int i; | ||
777 | |||
778 | for (i = 0; i < ARRAY_SIZE(pcm->streams); i++) { | ||
779 | substream = pcm->streams[i].substream; | ||
780 | if (substream) { | ||
781 | snd_dma_free_pages(&substream->dma_buffer); | ||
782 | substream->dma_buffer.area = NULL; | ||
783 | substream->dma_buffer.addr = 0; | ||
784 | } | ||
785 | } | ||
786 | } | ||
787 | |||
788 | static struct snd_pcm_ops fsl_dma_ops = { | ||
789 | .open = fsl_dma_open, | ||
790 | .close = fsl_dma_close, | ||
791 | .ioctl = snd_pcm_lib_ioctl, | ||
792 | .hw_params = fsl_dma_hw_params, | ||
793 | .hw_free = fsl_dma_hw_free, | ||
794 | .prepare = fsl_dma_prepare, | ||
795 | .pointer = fsl_dma_pointer, | ||
796 | }; | ||
797 | |||
798 | struct snd_soc_platform fsl_soc_platform = { | ||
799 | .name = "fsl-dma", | ||
800 | .pcm_ops = &fsl_dma_ops, | ||
801 | .pcm_new = fsl_dma_new, | ||
802 | .pcm_free = fsl_dma_free_dma_buffers, | ||
803 | }; | ||
804 | EXPORT_SYMBOL_GPL(fsl_soc_platform); | ||
805 | |||
806 | /** | ||
807 | * fsl_dma_configure: store the DMA parameters from the fabric driver. | ||
808 | * | ||
809 | * This function is called by the ASoC fabric driver to give us the DMA and | ||
810 | * SSI channel information. | ||
811 | * | ||
812 | * Unfortunately, ASoC V1 does make it possible to determine the DMA/SSI | ||
813 | * data when a substream is created, so for now we need to store this data | ||
814 | * into a global variable. This means that we can only support one DMA | ||
815 | * controller, and hence only one SSI. | ||
816 | */ | ||
817 | int fsl_dma_configure(struct fsl_dma_info *dma_info) | ||
818 | { | ||
819 | static int initialized; | ||
820 | |||
821 | /* We only support one DMA controller for now */ | ||
822 | if (initialized) | ||
823 | return 0; | ||
824 | |||
825 | dma_global_data.ssi_stx_phys = dma_info->ssi_stx_phys; | ||
826 | dma_global_data.ssi_srx_phys = dma_info->ssi_srx_phys; | ||
827 | dma_global_data.dma_channel[0] = dma_info->dma_channel[0]; | ||
828 | dma_global_data.dma_channel[1] = dma_info->dma_channel[1]; | ||
829 | dma_global_data.irq[0] = dma_info->dma_irq[0]; | ||
830 | dma_global_data.irq[1] = dma_info->dma_irq[1]; | ||
831 | dma_global_data.assigned[0] = 0; | ||
832 | dma_global_data.assigned[1] = 0; | ||
833 | |||
834 | initialized = 1; | ||
835 | return 1; | ||
836 | } | ||
837 | EXPORT_SYMBOL_GPL(fsl_dma_configure); | ||
838 | |||
839 | MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); | ||
840 | MODULE_DESCRIPTION("Freescale Elo DMA ASoC PCM module"); | ||
841 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/fsl/fsl_dma.h b/sound/soc/fsl/fsl_dma.h new file mode 100644 index 000000000000..430a6ce8b0d0 --- /dev/null +++ b/sound/soc/fsl/fsl_dma.h | |||
@@ -0,0 +1,149 @@ | |||
1 | /* | ||
2 | * mpc8610-pcm.h - ALSA PCM interface for the Freescale MPC8610 SoC | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #ifndef _MPC8610_PCM_H | ||
10 | #define _MPC8610_PCM_H | ||
11 | |||
12 | struct ccsr_dma { | ||
13 | u8 res0[0x100]; | ||
14 | struct ccsr_dma_channel { | ||
15 | __be32 mr; /* Mode register */ | ||
16 | __be32 sr; /* Status register */ | ||
17 | __be32 eclndar; /* Current link descriptor extended addr reg */ | ||
18 | __be32 clndar; /* Current link descriptor address register */ | ||
19 | __be32 satr; /* Source attributes register */ | ||
20 | __be32 sar; /* Source address register */ | ||
21 | __be32 datr; /* Destination attributes register */ | ||
22 | __be32 dar; /* Destination address register */ | ||
23 | __be32 bcr; /* Byte count register */ | ||
24 | __be32 enlndar; /* Next link descriptor extended address reg */ | ||
25 | __be32 nlndar; /* Next link descriptor address register */ | ||
26 | u8 res1[4]; | ||
27 | __be32 eclsdar; /* Current list descriptor extended addr reg */ | ||
28 | __be32 clsdar; /* Current list descriptor address register */ | ||
29 | __be32 enlsdar; /* Next list descriptor extended address reg */ | ||
30 | __be32 nlsdar; /* Next list descriptor address register */ | ||
31 | __be32 ssr; /* Source stride register */ | ||
32 | __be32 dsr; /* Destination stride register */ | ||
33 | u8 res2[0x38]; | ||
34 | } channel[4]; | ||
35 | __be32 dgsr; | ||
36 | }; | ||
37 | |||
38 | #define CCSR_DMA_MR_BWC_DISABLED 0x0F000000 | ||
39 | #define CCSR_DMA_MR_BWC_SHIFT 24 | ||
40 | #define CCSR_DMA_MR_BWC_MASK 0x0F000000 | ||
41 | #define CCSR_DMA_MR_BWC(x) \ | ||
42 | ((ilog2(x) << CCSR_DMA_MR_BWC_SHIFT) & CCSR_DMA_MR_BWC_MASK) | ||
43 | #define CCSR_DMA_MR_EMP_EN 0x00200000 | ||
44 | #define CCSR_DMA_MR_EMS_EN 0x00040000 | ||
45 | #define CCSR_DMA_MR_DAHTS_MASK 0x00030000 | ||
46 | #define CCSR_DMA_MR_DAHTS_1 0x00000000 | ||
47 | #define CCSR_DMA_MR_DAHTS_2 0x00010000 | ||
48 | #define CCSR_DMA_MR_DAHTS_4 0x00020000 | ||
49 | #define CCSR_DMA_MR_DAHTS_8 0x00030000 | ||
50 | #define CCSR_DMA_MR_SAHTS_MASK 0x0000C000 | ||
51 | #define CCSR_DMA_MR_SAHTS_1 0x00000000 | ||
52 | #define CCSR_DMA_MR_SAHTS_2 0x00004000 | ||
53 | #define CCSR_DMA_MR_SAHTS_4 0x00008000 | ||
54 | #define CCSR_DMA_MR_SAHTS_8 0x0000C000 | ||
55 | #define CCSR_DMA_MR_DAHE 0x00002000 | ||
56 | #define CCSR_DMA_MR_SAHE 0x00001000 | ||
57 | #define CCSR_DMA_MR_SRW 0x00000400 | ||
58 | #define CCSR_DMA_MR_EOSIE 0x00000200 | ||
59 | #define CCSR_DMA_MR_EOLNIE 0x00000100 | ||
60 | #define CCSR_DMA_MR_EOLSIE 0x00000080 | ||
61 | #define CCSR_DMA_MR_EIE 0x00000040 | ||
62 | #define CCSR_DMA_MR_XFE 0x00000020 | ||
63 | #define CCSR_DMA_MR_CDSM_SWSM 0x00000010 | ||
64 | #define CCSR_DMA_MR_CA 0x00000008 | ||
65 | #define CCSR_DMA_MR_CTM 0x00000004 | ||
66 | #define CCSR_DMA_MR_CC 0x00000002 | ||
67 | #define CCSR_DMA_MR_CS 0x00000001 | ||
68 | |||
69 | #define CCSR_DMA_SR_TE 0x00000080 | ||
70 | #define CCSR_DMA_SR_CH 0x00000020 | ||
71 | #define CCSR_DMA_SR_PE 0x00000010 | ||
72 | #define CCSR_DMA_SR_EOLNI 0x00000008 | ||
73 | #define CCSR_DMA_SR_CB 0x00000004 | ||
74 | #define CCSR_DMA_SR_EOSI 0x00000002 | ||
75 | #define CCSR_DMA_SR_EOLSI 0x00000001 | ||
76 | |||
77 | /* ECLNDAR takes bits 32-36 of the CLNDAR register */ | ||
78 | static inline u32 CCSR_DMA_ECLNDAR_ADDR(u64 x) | ||
79 | { | ||
80 | return (x >> 32) & 0xf; | ||
81 | } | ||
82 | |||
83 | #define CCSR_DMA_CLNDAR_ADDR(x) ((x) & 0xFFFFFFFE) | ||
84 | #define CCSR_DMA_CLNDAR_EOSIE 0x00000008 | ||
85 | |||
86 | /* SATR and DATR, combined */ | ||
87 | #define CCSR_DMA_ATR_PBATMU 0x20000000 | ||
88 | #define CCSR_DMA_ATR_TFLOWLVL_0 0x00000000 | ||
89 | #define CCSR_DMA_ATR_TFLOWLVL_1 0x06000000 | ||
90 | #define CCSR_DMA_ATR_TFLOWLVL_2 0x08000000 | ||
91 | #define CCSR_DMA_ATR_TFLOWLVL_3 0x0C000000 | ||
92 | #define CCSR_DMA_ATR_PCIORDER 0x02000000 | ||
93 | #define CCSR_DMA_ATR_SME 0x01000000 | ||
94 | #define CCSR_DMA_ATR_NOSNOOP 0x00040000 | ||
95 | #define CCSR_DMA_ATR_SNOOP 0x00050000 | ||
96 | #define CCSR_DMA_ATR_ESAD_MASK 0x0000000F | ||
97 | |||
98 | /** | ||
99 | * List Descriptor for extended chaining mode DMA operations. | ||
100 | * | ||
101 | * The CLSDAR register points to the first (in a linked-list) List | ||
102 | * Descriptor. Each object must be aligned on a 32-byte boundary. Each | ||
103 | * list descriptor points to a linked-list of link Descriptors. | ||
104 | */ | ||
105 | struct fsl_dma_list_descriptor { | ||
106 | __be64 next; /* Address of next list descriptor */ | ||
107 | __be64 first_link; /* Address of first link descriptor */ | ||
108 | __be32 source; /* Source stride */ | ||
109 | __be32 dest; /* Destination stride */ | ||
110 | u8 res[8]; /* Reserved */ | ||
111 | } __attribute__ ((aligned(32), packed)); | ||
112 | |||
113 | /** | ||
114 | * Link Descriptor for basic and extended chaining mode DMA operations. | ||
115 | * | ||
116 | * A Link Descriptor points to a single DMA buffer. Each link descriptor | ||
117 | * must be aligned on a 32-byte boundary. | ||
118 | */ | ||
119 | struct fsl_dma_link_descriptor { | ||
120 | __be32 source_attr; /* Programmed into SATR register */ | ||
121 | __be32 source_addr; /* Programmed into SAR register */ | ||
122 | __be32 dest_attr; /* Programmed into DATR register */ | ||
123 | __be32 dest_addr; /* Programmed into DAR register */ | ||
124 | __be64 next; /* Address of next link descriptor */ | ||
125 | __be32 count; /* Byte count */ | ||
126 | u8 res[4]; /* Reserved */ | ||
127 | } __attribute__ ((aligned(32), packed)); | ||
128 | |||
129 | /* DMA information needed to create a snd_soc_cpu_dai object | ||
130 | * | ||
131 | * ssi_stx_phys: bus address of SSI STX register to use | ||
132 | * ssi_srx_phys: bus address of SSI SRX register to use | ||
133 | * dma[0]: points to the DMA channel to use for playback | ||
134 | * dma[1]: points to the DMA channel to use for capture | ||
135 | * dma_irq[0]: IRQ of the DMA channel to use for playback | ||
136 | * dma_irq[1]: IRQ of the DMA channel to use for capture | ||
137 | */ | ||
138 | struct fsl_dma_info { | ||
139 | dma_addr_t ssi_stx_phys; | ||
140 | dma_addr_t ssi_srx_phys; | ||
141 | struct ccsr_dma_channel __iomem *dma_channel[2]; | ||
142 | unsigned int dma_irq[2]; | ||
143 | }; | ||
144 | |||
145 | extern struct snd_soc_platform fsl_soc_platform; | ||
146 | |||
147 | int fsl_dma_configure(struct fsl_dma_info *dma_info); | ||
148 | |||
149 | #endif | ||
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c new file mode 100644 index 000000000000..145ad13d52d1 --- /dev/null +++ b/sound/soc/fsl/fsl_ssi.c | |||
@@ -0,0 +1,644 @@ | |||
1 | /* | ||
2 | * Freescale SSI ALSA SoC Digital Audio Interface (DAI) driver | ||
3 | * | ||
4 | * Author: Timur Tabi <timur@freescale.com> | ||
5 | * | ||
6 | * Copyright 2007-2008 Freescale Semiconductor, Inc. This file is licensed | ||
7 | * under the terms of the GNU General Public License version 2. This | ||
8 | * program is licensed "as is" without any warranty of any kind, whether | ||
9 | * express or implied. | ||
10 | */ | ||
11 | |||
12 | #include <linux/init.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/interrupt.h> | ||
15 | #include <linux/device.h> | ||
16 | #include <linux/delay.h> | ||
17 | |||
18 | #include <sound/driver.h> | ||
19 | #include <sound/core.h> | ||
20 | #include <sound/pcm.h> | ||
21 | #include <sound/pcm_params.h> | ||
22 | #include <sound/initval.h> | ||
23 | #include <sound/soc.h> | ||
24 | |||
25 | #include <asm/immap_86xx.h> | ||
26 | |||
27 | #include "fsl_ssi.h" | ||
28 | |||
29 | /** | ||
30 | * FSLSSI_I2S_RATES: sample rates supported by the I2S | ||
31 | * | ||
32 | * This driver currently only supports the SSI running in I2S slave mode, | ||
33 | * which means the codec determines the sample rate. Therefore, we tell | ||
34 | * ALSA that we support all rates and let the codec driver decide what rates | ||
35 | * are really supported. | ||
36 | */ | ||
37 | #define FSLSSI_I2S_RATES (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_192000 | \ | ||
38 | SNDRV_PCM_RATE_CONTINUOUS) | ||
39 | |||
40 | /** | ||
41 | * FSLSSI_I2S_FORMATS: audio formats supported by the SSI | ||
42 | * | ||
43 | * This driver currently only supports the SSI running in I2S slave mode. | ||
44 | * | ||
45 | * The SSI has a limitation in that the samples must be in the same byte | ||
46 | * order as the host CPU. This is because when multiple bytes are written | ||
47 | * to the STX register, the bytes and bits must be written in the same | ||
48 | * order. The STX is a shift register, so all the bits need to be aligned | ||
49 | * (bit-endianness must match byte-endianness). Processors typically write | ||
50 | * the bits within a byte in the same order that the bytes of a word are | ||
51 | * written in. So if the host CPU is big-endian, then only big-endian | ||
52 | * samples will be written to STX properly. | ||
53 | */ | ||
54 | #ifdef __BIG_ENDIAN | ||
55 | #define FSLSSI_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | \ | ||
56 | SNDRV_PCM_FMTBIT_S18_3BE | SNDRV_PCM_FMTBIT_S20_3BE | \ | ||
57 | SNDRV_PCM_FMTBIT_S24_3BE | SNDRV_PCM_FMTBIT_S24_BE) | ||
58 | #else | ||
59 | #define FSLSSI_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \ | ||
60 | SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE | \ | ||
61 | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE) | ||
62 | #endif | ||
63 | |||
64 | /** | ||
65 | * fsl_ssi_private: per-SSI private data | ||
66 | * | ||
67 | * @name: short name for this device ("SSI0", "SSI1", etc) | ||
68 | * @ssi: pointer to the SSI's registers | ||
69 | * @ssi_phys: physical address of the SSI registers | ||
70 | * @irq: IRQ of this SSI | ||
71 | * @dev: struct device pointer | ||
72 | * @playback: the number of playback streams opened | ||
73 | * @capture: the number of capture streams opened | ||
74 | * @cpu_dai: the CPU DAI for this device | ||
75 | * @dev_attr: the sysfs device attribute structure | ||
76 | * @stats: SSI statistics | ||
77 | */ | ||
78 | struct fsl_ssi_private { | ||
79 | char name[8]; | ||
80 | struct ccsr_ssi __iomem *ssi; | ||
81 | dma_addr_t ssi_phys; | ||
82 | unsigned int irq; | ||
83 | struct device *dev; | ||
84 | unsigned int playback; | ||
85 | unsigned int capture; | ||
86 | struct snd_soc_cpu_dai cpu_dai; | ||
87 | struct device_attribute dev_attr; | ||
88 | |||
89 | struct { | ||
90 | unsigned int rfrc; | ||
91 | unsigned int tfrc; | ||
92 | unsigned int cmdau; | ||
93 | unsigned int cmddu; | ||
94 | unsigned int rxt; | ||
95 | unsigned int rdr1; | ||
96 | unsigned int rdr0; | ||
97 | unsigned int tde1; | ||
98 | unsigned int tde0; | ||
99 | unsigned int roe1; | ||
100 | unsigned int roe0; | ||
101 | unsigned int tue1; | ||
102 | unsigned int tue0; | ||
103 | unsigned int tfs; | ||
104 | unsigned int rfs; | ||
105 | unsigned int tls; | ||
106 | unsigned int rls; | ||
107 | unsigned int rff1; | ||
108 | unsigned int rff0; | ||
109 | unsigned int tfe1; | ||
110 | unsigned int tfe0; | ||
111 | } stats; | ||
112 | }; | ||
113 | |||
114 | /** | ||
115 | * fsl_ssi_isr: SSI interrupt handler | ||
116 | * | ||
117 | * Although it's possible to use the interrupt handler to send and receive | ||
118 | * data to/from the SSI, we use the DMA instead. Programming is more | ||
119 | * complicated, but the performance is much better. | ||
120 | * | ||
121 | * This interrupt handler is used only to gather statistics. | ||
122 | * | ||
123 | * @irq: IRQ of the SSI device | ||
124 | * @dev_id: pointer to the ssi_private structure for this SSI device | ||
125 | */ | ||
126 | static irqreturn_t fsl_ssi_isr(int irq, void *dev_id) | ||
127 | { | ||
128 | struct fsl_ssi_private *ssi_private = dev_id; | ||
129 | struct ccsr_ssi __iomem *ssi = ssi_private->ssi; | ||
130 | irqreturn_t ret = IRQ_NONE; | ||
131 | __be32 sisr; | ||
132 | __be32 sisr2 = 0; | ||
133 | |||
134 | /* We got an interrupt, so read the status register to see what we | ||
135 | were interrupted for. We mask it with the Interrupt Enable register | ||
136 | so that we only check for events that we're interested in. | ||
137 | */ | ||
138 | sisr = in_be32(&ssi->sisr) & in_be32(&ssi->sier); | ||
139 | |||
140 | if (sisr & CCSR_SSI_SISR_RFRC) { | ||
141 | ssi_private->stats.rfrc++; | ||
142 | sisr2 |= CCSR_SSI_SISR_RFRC; | ||
143 | ret = IRQ_HANDLED; | ||
144 | } | ||
145 | |||
146 | if (sisr & CCSR_SSI_SISR_TFRC) { | ||
147 | ssi_private->stats.tfrc++; | ||
148 | sisr2 |= CCSR_SSI_SISR_TFRC; | ||
149 | ret = IRQ_HANDLED; | ||
150 | } | ||
151 | |||
152 | if (sisr & CCSR_SSI_SISR_CMDAU) { | ||
153 | ssi_private->stats.cmdau++; | ||
154 | ret = IRQ_HANDLED; | ||
155 | } | ||
156 | |||
157 | if (sisr & CCSR_SSI_SISR_CMDDU) { | ||
158 | ssi_private->stats.cmddu++; | ||
159 | ret = IRQ_HANDLED; | ||
160 | } | ||
161 | |||
162 | if (sisr & CCSR_SSI_SISR_RXT) { | ||
163 | ssi_private->stats.rxt++; | ||
164 | ret = IRQ_HANDLED; | ||
165 | } | ||
166 | |||
167 | if (sisr & CCSR_SSI_SISR_RDR1) { | ||
168 | ssi_private->stats.rdr1++; | ||
169 | ret = IRQ_HANDLED; | ||
170 | } | ||
171 | |||
172 | if (sisr & CCSR_SSI_SISR_RDR0) { | ||
173 | ssi_private->stats.rdr0++; | ||
174 | ret = IRQ_HANDLED; | ||
175 | } | ||
176 | |||
177 | if (sisr & CCSR_SSI_SISR_TDE1) { | ||
178 | ssi_private->stats.tde1++; | ||
179 | ret = IRQ_HANDLED; | ||
180 | } | ||
181 | |||
182 | if (sisr & CCSR_SSI_SISR_TDE0) { | ||
183 | ssi_private->stats.tde0++; | ||
184 | ret = IRQ_HANDLED; | ||
185 | } | ||
186 | |||
187 | if (sisr & CCSR_SSI_SISR_ROE1) { | ||
188 | ssi_private->stats.roe1++; | ||
189 | sisr2 |= CCSR_SSI_SISR_ROE1; | ||
190 | ret = IRQ_HANDLED; | ||
191 | } | ||
192 | |||
193 | if (sisr & CCSR_SSI_SISR_ROE0) { | ||
194 | ssi_private->stats.roe0++; | ||
195 | sisr2 |= CCSR_SSI_SISR_ROE0; | ||
196 | ret = IRQ_HANDLED; | ||
197 | } | ||
198 | |||
199 | if (sisr & CCSR_SSI_SISR_TUE1) { | ||
200 | ssi_private->stats.tue1++; | ||
201 | sisr2 |= CCSR_SSI_SISR_TUE1; | ||
202 | ret = IRQ_HANDLED; | ||
203 | } | ||
204 | |||
205 | if (sisr & CCSR_SSI_SISR_TUE0) { | ||
206 | ssi_private->stats.tue0++; | ||
207 | sisr2 |= CCSR_SSI_SISR_TUE0; | ||
208 | ret = IRQ_HANDLED; | ||
209 | } | ||
210 | |||
211 | if (sisr & CCSR_SSI_SISR_TFS) { | ||
212 | ssi_private->stats.tfs++; | ||
213 | ret = IRQ_HANDLED; | ||
214 | } | ||
215 | |||
216 | if (sisr & CCSR_SSI_SISR_RFS) { | ||
217 | ssi_private->stats.rfs++; | ||
218 | ret = IRQ_HANDLED; | ||
219 | } | ||
220 | |||
221 | if (sisr & CCSR_SSI_SISR_TLS) { | ||
222 | ssi_private->stats.tls++; | ||
223 | ret = IRQ_HANDLED; | ||
224 | } | ||
225 | |||
226 | if (sisr & CCSR_SSI_SISR_RLS) { | ||
227 | ssi_private->stats.rls++; | ||
228 | ret = IRQ_HANDLED; | ||
229 | } | ||
230 | |||
231 | if (sisr & CCSR_SSI_SISR_RFF1) { | ||
232 | ssi_private->stats.rff1++; | ||
233 | ret = IRQ_HANDLED; | ||
234 | } | ||
235 | |||
236 | if (sisr & CCSR_SSI_SISR_RFF0) { | ||
237 | ssi_private->stats.rff0++; | ||
238 | ret = IRQ_HANDLED; | ||
239 | } | ||
240 | |||
241 | if (sisr & CCSR_SSI_SISR_TFE1) { | ||
242 | ssi_private->stats.tfe1++; | ||
243 | ret = IRQ_HANDLED; | ||
244 | } | ||
245 | |||
246 | if (sisr & CCSR_SSI_SISR_TFE0) { | ||
247 | ssi_private->stats.tfe0++; | ||
248 | ret = IRQ_HANDLED; | ||
249 | } | ||
250 | |||
251 | /* Clear the bits that we set */ | ||
252 | if (sisr2) | ||
253 | out_be32(&ssi->sisr, sisr2); | ||
254 | |||
255 | return ret; | ||
256 | } | ||
257 | |||
258 | /** | ||
259 | * fsl_ssi_startup: create a new substream | ||
260 | * | ||
261 | * This is the first function called when a stream is opened. | ||
262 | * | ||
263 | * If this is the first stream open, then grab the IRQ and program most of | ||
264 | * the SSI registers. | ||
265 | */ | ||
266 | static int fsl_ssi_startup(struct snd_pcm_substream *substream) | ||
267 | { | ||
268 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
269 | struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data; | ||
270 | |||
271 | /* | ||
272 | * If this is the first stream opened, then request the IRQ | ||
273 | * and initialize the SSI registers. | ||
274 | */ | ||
275 | if (!ssi_private->playback && !ssi_private->capture) { | ||
276 | struct ccsr_ssi __iomem *ssi = ssi_private->ssi; | ||
277 | int ret; | ||
278 | |||
279 | ret = request_irq(ssi_private->irq, fsl_ssi_isr, 0, | ||
280 | ssi_private->name, ssi_private); | ||
281 | if (ret < 0) { | ||
282 | dev_err(substream->pcm->card->dev, | ||
283 | "could not claim irq %u\n", ssi_private->irq); | ||
284 | return ret; | ||
285 | } | ||
286 | |||
287 | /* | ||
288 | * Section 16.5 of the MPC8610 reference manual says that the | ||
289 | * SSI needs to be disabled before updating the registers we set | ||
290 | * here. | ||
291 | */ | ||
292 | clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN); | ||
293 | |||
294 | /* | ||
295 | * Program the SSI into I2S Slave Non-Network Synchronous mode. | ||
296 | * Also enable the transmit and receive FIFO. | ||
297 | * | ||
298 | * FIXME: Little-endian samples require a different shift dir | ||
299 | */ | ||
300 | clrsetbits_be32(&ssi->scr, CCSR_SSI_SCR_I2S_MODE_MASK, | ||
301 | CCSR_SSI_SCR_TFR_CLK_DIS | | ||
302 | CCSR_SSI_SCR_I2S_MODE_SLAVE | CCSR_SSI_SCR_SYN); | ||
303 | |||
304 | out_be32(&ssi->stcr, | ||
305 | CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 | | ||
306 | CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TEFS | | ||
307 | CCSR_SSI_STCR_TSCKP); | ||
308 | |||
309 | out_be32(&ssi->srcr, | ||
310 | CCSR_SSI_SRCR_RXBIT0 | CCSR_SSI_SRCR_RFEN0 | | ||
311 | CCSR_SSI_SRCR_RFSI | CCSR_SSI_SRCR_REFS | | ||
312 | CCSR_SSI_SRCR_RSCKP); | ||
313 | |||
314 | /* | ||
315 | * The DC and PM bits are only used if the SSI is the clock | ||
316 | * master. | ||
317 | */ | ||
318 | |||
319 | /* 4. Enable the interrupts and DMA requests */ | ||
320 | out_be32(&ssi->sier, | ||
321 | CCSR_SSI_SIER_TFRC_EN | CCSR_SSI_SIER_TDMAE | | ||
322 | CCSR_SSI_SIER_TIE | CCSR_SSI_SIER_TUE0_EN | | ||
323 | CCSR_SSI_SIER_TUE1_EN | CCSR_SSI_SIER_RFRC_EN | | ||
324 | CCSR_SSI_SIER_RDMAE | CCSR_SSI_SIER_RIE | | ||
325 | CCSR_SSI_SIER_ROE0_EN | CCSR_SSI_SIER_ROE1_EN); | ||
326 | |||
327 | /* | ||
328 | * Set the watermark for transmit FIFI 0 and receive FIFO 0. We | ||
329 | * don't use FIFO 1. Since the SSI only supports stereo, the | ||
330 | * watermark should never be an odd number. | ||
331 | */ | ||
332 | out_be32(&ssi->sfcsr, | ||
333 | CCSR_SSI_SFCSR_TFWM0(6) | CCSR_SSI_SFCSR_RFWM0(2)); | ||
334 | |||
335 | /* | ||
336 | * We keep the SSI disabled because if we enable it, then the | ||
337 | * DMA controller will start. It's not supposed to start until | ||
338 | * the SCR.TE (or SCR.RE) bit is set, but it does anyway. The | ||
339 | * DMA controller will transfer one "BWC" of data (i.e. the | ||
340 | * amount of data that the MR.BWC bits are set to). The reason | ||
341 | * this is bad is because at this point, the PCM driver has not | ||
342 | * finished initializing the DMA controller. | ||
343 | */ | ||
344 | } | ||
345 | |||
346 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
347 | ssi_private->playback++; | ||
348 | |||
349 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) | ||
350 | ssi_private->capture++; | ||
351 | |||
352 | return 0; | ||
353 | } | ||
354 | |||
355 | /** | ||
356 | * fsl_ssi_prepare: prepare the SSI. | ||
357 | * | ||
358 | * Most of the SSI registers have been programmed in the startup function, | ||
359 | * but the word length must be programmed here. Unfortunately, programming | ||
360 | * the SxCCR.WL bits requires the SSI to be temporarily disabled. This can | ||
361 | * cause a problem with supporting simultaneous playback and capture. If | ||
362 | * the SSI is already playing a stream, then that stream may be temporarily | ||
363 | * stopped when you start capture. | ||
364 | * | ||
365 | * Note: The SxCCR.DC and SxCCR.PM bits are only used if the SSI is the | ||
366 | * clock master. | ||
367 | */ | ||
368 | static int fsl_ssi_prepare(struct snd_pcm_substream *substream) | ||
369 | { | ||
370 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
371 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
372 | struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data; | ||
373 | |||
374 | struct ccsr_ssi __iomem *ssi = ssi_private->ssi; | ||
375 | u32 wl; | ||
376 | |||
377 | wl = CCSR_SSI_SxCCR_WL(snd_pcm_format_width(runtime->format)); | ||
378 | |||
379 | clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN); | ||
380 | |||
381 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
382 | clrsetbits_be32(&ssi->stccr, CCSR_SSI_SxCCR_WL_MASK, wl); | ||
383 | else | ||
384 | clrsetbits_be32(&ssi->srccr, CCSR_SSI_SxCCR_WL_MASK, wl); | ||
385 | |||
386 | setbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN); | ||
387 | |||
388 | return 0; | ||
389 | } | ||
390 | |||
391 | /** | ||
392 | * fsl_ssi_trigger: start and stop the DMA transfer. | ||
393 | * | ||
394 | * This function is called by ALSA to start, stop, pause, and resume the DMA | ||
395 | * transfer of data. | ||
396 | * | ||
397 | * The DMA channel is in external master start and pause mode, which | ||
398 | * means the SSI completely controls the flow of data. | ||
399 | */ | ||
400 | static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd) | ||
401 | { | ||
402 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
403 | struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data; | ||
404 | struct ccsr_ssi __iomem *ssi = ssi_private->ssi; | ||
405 | |||
406 | switch (cmd) { | ||
407 | case SNDRV_PCM_TRIGGER_START: | ||
408 | case SNDRV_PCM_TRIGGER_RESUME: | ||
409 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
410 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
411 | setbits32(&ssi->scr, CCSR_SSI_SCR_TE); | ||
412 | } else { | ||
413 | setbits32(&ssi->scr, CCSR_SSI_SCR_RE); | ||
414 | |||
415 | /* | ||
416 | * I think we need this delay to allow time for the SSI | ||
417 | * to put data into its FIFO. Without it, ALSA starts | ||
418 | * to complain about overruns. | ||
419 | */ | ||
420 | msleep(1); | ||
421 | } | ||
422 | break; | ||
423 | |||
424 | case SNDRV_PCM_TRIGGER_STOP: | ||
425 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
426 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
427 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
428 | clrbits32(&ssi->scr, CCSR_SSI_SCR_TE); | ||
429 | else | ||
430 | clrbits32(&ssi->scr, CCSR_SSI_SCR_RE); | ||
431 | break; | ||
432 | |||
433 | default: | ||
434 | return -EINVAL; | ||
435 | } | ||
436 | |||
437 | return 0; | ||
438 | } | ||
439 | |||
440 | /** | ||
441 | * fsl_ssi_shutdown: shutdown the SSI | ||
442 | * | ||
443 | * Shutdown the SSI if there are no other substreams open. | ||
444 | */ | ||
445 | static void fsl_ssi_shutdown(struct snd_pcm_substream *substream) | ||
446 | { | ||
447 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
448 | struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data; | ||
449 | |||
450 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
451 | ssi_private->playback--; | ||
452 | |||
453 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) | ||
454 | ssi_private->capture--; | ||
455 | |||
456 | /* | ||
457 | * If this is the last active substream, disable the SSI and release | ||
458 | * the IRQ. | ||
459 | */ | ||
460 | if (!ssi_private->playback && !ssi_private->capture) { | ||
461 | struct ccsr_ssi __iomem *ssi = ssi_private->ssi; | ||
462 | |||
463 | clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN); | ||
464 | |||
465 | free_irq(ssi_private->irq, ssi_private); | ||
466 | } | ||
467 | } | ||
468 | |||
469 | /** | ||
470 | * fsl_ssi_set_sysclk: set the clock frequency and direction | ||
471 | * | ||
472 | * This function is called by the machine driver to tell us what the clock | ||
473 | * frequency and direction are. | ||
474 | * | ||
475 | * Currently, we only support operating as a clock slave (SND_SOC_CLOCK_IN), | ||
476 | * and we don't care about the frequency. Return an error if the direction | ||
477 | * is not SND_SOC_CLOCK_IN. | ||
478 | * | ||
479 | * @clk_id: reserved, should be zero | ||
480 | * @freq: the frequency of the given clock ID, currently ignored | ||
481 | * @dir: SND_SOC_CLOCK_IN (clock slave) or SND_SOC_CLOCK_OUT (clock master) | ||
482 | */ | ||
483 | static int fsl_ssi_set_sysclk(struct snd_soc_cpu_dai *cpu_dai, | ||
484 | int clk_id, unsigned int freq, int dir) | ||
485 | { | ||
486 | |||
487 | return (dir == SND_SOC_CLOCK_IN) ? 0 : -EINVAL; | ||
488 | } | ||
489 | |||
490 | /** | ||
491 | * fsl_ssi_set_fmt: set the serial format. | ||
492 | * | ||
493 | * This function is called by the machine driver to tell us what serial | ||
494 | * format to use. | ||
495 | * | ||
496 | * Currently, we only support I2S mode. Return an error if the format is | ||
497 | * not SND_SOC_DAIFMT_I2S. | ||
498 | * | ||
499 | * @format: one of SND_SOC_DAIFMT_xxx | ||
500 | */ | ||
501 | static int fsl_ssi_set_fmt(struct snd_soc_cpu_dai *cpu_dai, unsigned int format) | ||
502 | { | ||
503 | return (format == SND_SOC_DAIFMT_I2S) ? 0 : -EINVAL; | ||
504 | } | ||
505 | |||
506 | /** | ||
507 | * fsl_ssi_dai_template: template CPU DAI for the SSI | ||
508 | */ | ||
509 | static struct snd_soc_cpu_dai fsl_ssi_dai_template = { | ||
510 | .playback = { | ||
511 | /* The SSI does not support monaural audio. */ | ||
512 | .channels_min = 2, | ||
513 | .channels_max = 2, | ||
514 | .rates = FSLSSI_I2S_RATES, | ||
515 | .formats = FSLSSI_I2S_FORMATS, | ||
516 | }, | ||
517 | .capture = { | ||
518 | .channels_min = 2, | ||
519 | .channels_max = 2, | ||
520 | .rates = FSLSSI_I2S_RATES, | ||
521 | .formats = FSLSSI_I2S_FORMATS, | ||
522 | }, | ||
523 | .ops = { | ||
524 | .startup = fsl_ssi_startup, | ||
525 | .prepare = fsl_ssi_prepare, | ||
526 | .shutdown = fsl_ssi_shutdown, | ||
527 | .trigger = fsl_ssi_trigger, | ||
528 | }, | ||
529 | .dai_ops = { | ||
530 | .set_sysclk = fsl_ssi_set_sysclk, | ||
531 | .set_fmt = fsl_ssi_set_fmt, | ||
532 | }, | ||
533 | }; | ||
534 | |||
535 | /** | ||
536 | * fsl_sysfs_ssi_show: display SSI statistics | ||
537 | * | ||
538 | * Display the statistics for the current SSI device. | ||
539 | */ | ||
540 | static ssize_t fsl_sysfs_ssi_show(struct device *dev, | ||
541 | struct device_attribute *attr, char *buf) | ||
542 | { | ||
543 | struct fsl_ssi_private *ssi_private = | ||
544 | container_of(attr, struct fsl_ssi_private, dev_attr); | ||
545 | ssize_t length; | ||
546 | |||
547 | length = sprintf(buf, "rfrc=%u", ssi_private->stats.rfrc); | ||
548 | length += sprintf(buf + length, "\ttfrc=%u", ssi_private->stats.tfrc); | ||
549 | length += sprintf(buf + length, "\tcmdau=%u", ssi_private->stats.cmdau); | ||
550 | length += sprintf(buf + length, "\tcmddu=%u", ssi_private->stats.cmddu); | ||
551 | length += sprintf(buf + length, "\trxt=%u", ssi_private->stats.rxt); | ||
552 | length += sprintf(buf + length, "\trdr1=%u", ssi_private->stats.rdr1); | ||
553 | length += sprintf(buf + length, "\trdr0=%u", ssi_private->stats.rdr0); | ||
554 | length += sprintf(buf + length, "\ttde1=%u", ssi_private->stats.tde1); | ||
555 | length += sprintf(buf + length, "\ttde0=%u", ssi_private->stats.tde0); | ||
556 | length += sprintf(buf + length, "\troe1=%u", ssi_private->stats.roe1); | ||
557 | length += sprintf(buf + length, "\troe0=%u", ssi_private->stats.roe0); | ||
558 | length += sprintf(buf + length, "\ttue1=%u", ssi_private->stats.tue1); | ||
559 | length += sprintf(buf + length, "\ttue0=%u", ssi_private->stats.tue0); | ||
560 | length += sprintf(buf + length, "\ttfs=%u", ssi_private->stats.tfs); | ||
561 | length += sprintf(buf + length, "\trfs=%u", ssi_private->stats.rfs); | ||
562 | length += sprintf(buf + length, "\ttls=%u", ssi_private->stats.tls); | ||
563 | length += sprintf(buf + length, "\trls=%u", ssi_private->stats.rls); | ||
564 | length += sprintf(buf + length, "\trff1=%u", ssi_private->stats.rff1); | ||
565 | length += sprintf(buf + length, "\trff0=%u", ssi_private->stats.rff0); | ||
566 | length += sprintf(buf + length, "\ttfe1=%u", ssi_private->stats.tfe1); | ||
567 | length += sprintf(buf + length, "\ttfe0=%u\n", ssi_private->stats.tfe0); | ||
568 | |||
569 | return length; | ||
570 | } | ||
571 | |||
572 | /** | ||
573 | * fsl_ssi_create_dai: create a snd_soc_cpu_dai structure | ||
574 | * | ||
575 | * This function is called by the machine driver to create a snd_soc_cpu_dai | ||
576 | * structure. The function creates an ssi_private object, which contains | ||
577 | * the snd_soc_cpu_dai. It also creates the sysfs statistics device. | ||
578 | */ | ||
579 | struct snd_soc_cpu_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info) | ||
580 | { | ||
581 | struct snd_soc_cpu_dai *fsl_ssi_dai; | ||
582 | struct fsl_ssi_private *ssi_private; | ||
583 | int ret = 0; | ||
584 | struct device_attribute *dev_attr; | ||
585 | |||
586 | ssi_private = kzalloc(sizeof(struct fsl_ssi_private), GFP_KERNEL); | ||
587 | if (!ssi_private) { | ||
588 | dev_err(ssi_info->dev, "could not allocate DAI object\n"); | ||
589 | return NULL; | ||
590 | } | ||
591 | memcpy(&ssi_private->cpu_dai, &fsl_ssi_dai_template, | ||
592 | sizeof(struct snd_soc_cpu_dai)); | ||
593 | |||
594 | fsl_ssi_dai = &ssi_private->cpu_dai; | ||
595 | dev_attr = &ssi_private->dev_attr; | ||
596 | |||
597 | sprintf(ssi_private->name, "ssi%u", (u8) ssi_info->id); | ||
598 | ssi_private->ssi = ssi_info->ssi; | ||
599 | ssi_private->ssi_phys = ssi_info->ssi_phys; | ||
600 | ssi_private->irq = ssi_info->irq; | ||
601 | ssi_private->dev = ssi_info->dev; | ||
602 | |||
603 | ssi_private->dev->driver_data = fsl_ssi_dai; | ||
604 | |||
605 | /* Initialize the the device_attribute structure */ | ||
606 | dev_attr->attr.name = "ssi-stats"; | ||
607 | dev_attr->attr.mode = S_IRUGO; | ||
608 | dev_attr->show = fsl_sysfs_ssi_show; | ||
609 | |||
610 | ret = device_create_file(ssi_private->dev, dev_attr); | ||
611 | if (ret) { | ||
612 | dev_err(ssi_info->dev, "could not create sysfs %s file\n", | ||
613 | ssi_private->dev_attr.attr.name); | ||
614 | kfree(fsl_ssi_dai); | ||
615 | return NULL; | ||
616 | } | ||
617 | |||
618 | fsl_ssi_dai->private_data = ssi_private; | ||
619 | fsl_ssi_dai->name = ssi_private->name; | ||
620 | fsl_ssi_dai->id = ssi_info->id; | ||
621 | |||
622 | return fsl_ssi_dai; | ||
623 | } | ||
624 | EXPORT_SYMBOL_GPL(fsl_ssi_create_dai); | ||
625 | |||
626 | /** | ||
627 | * fsl_ssi_destroy_dai: destroy the snd_soc_cpu_dai object | ||
628 | * | ||
629 | * This function undoes the operations of fsl_ssi_create_dai() | ||
630 | */ | ||
631 | void fsl_ssi_destroy_dai(struct snd_soc_cpu_dai *fsl_ssi_dai) | ||
632 | { | ||
633 | struct fsl_ssi_private *ssi_private = | ||
634 | container_of(fsl_ssi_dai, struct fsl_ssi_private, cpu_dai); | ||
635 | |||
636 | device_remove_file(ssi_private->dev, &ssi_private->dev_attr); | ||
637 | |||
638 | kfree(ssi_private); | ||
639 | } | ||
640 | EXPORT_SYMBOL_GPL(fsl_ssi_destroy_dai); | ||
641 | |||
642 | MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); | ||
643 | MODULE_DESCRIPTION("Freescale Synchronous Serial Interface (SSI) ASoC Driver"); | ||
644 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/fsl/fsl_ssi.h b/sound/soc/fsl/fsl_ssi.h new file mode 100644 index 000000000000..c5ce88e15651 --- /dev/null +++ b/sound/soc/fsl/fsl_ssi.h | |||
@@ -0,0 +1,224 @@ | |||
1 | /* | ||
2 | * fsl_ssi.h - ALSA SSI interface for the Freescale MPC8610 SoC | ||
3 | * | ||
4 | * Author: Timur Tabi <timur@freescale.com> | ||
5 | * | ||
6 | * Copyright 2007-2008 Freescale Semiconductor, Inc. This file is licensed | ||
7 | * under the terms of the GNU General Public License version 2. This | ||
8 | * program is licensed "as is" without any warranty of any kind, whether | ||
9 | * express or implied. | ||
10 | */ | ||
11 | |||
12 | #ifndef _MPC8610_I2S_H | ||
13 | #define _MPC8610_I2S_H | ||
14 | |||
15 | /* SSI Register Map */ | ||
16 | struct ccsr_ssi { | ||
17 | __be32 stx0; /* 0x.0000 - SSI Transmit Data Register 0 */ | ||
18 | __be32 stx1; /* 0x.0004 - SSI Transmit Data Register 1 */ | ||
19 | __be32 srx0; /* 0x.0008 - SSI Receive Data Register 0 */ | ||
20 | __be32 srx1; /* 0x.000C - SSI Receive Data Register 1 */ | ||
21 | __be32 scr; /* 0x.0010 - SSI Control Register */ | ||
22 | __be32 sisr; /* 0x.0014 - SSI Interrupt Status Register Mixed */ | ||
23 | __be32 sier; /* 0x.0018 - SSI Interrupt Enable Register */ | ||
24 | __be32 stcr; /* 0x.001C - SSI Transmit Configuration Register */ | ||
25 | __be32 srcr; /* 0x.0020 - SSI Receive Configuration Register */ | ||
26 | __be32 stccr; /* 0x.0024 - SSI Transmit Clock Control Register */ | ||
27 | __be32 srccr; /* 0x.0028 - SSI Receive Clock Control Register */ | ||
28 | __be32 sfcsr; /* 0x.002C - SSI FIFO Control/Status Register */ | ||
29 | __be32 str; /* 0x.0030 - SSI Test Register */ | ||
30 | __be32 sor; /* 0x.0034 - SSI Option Register */ | ||
31 | __be32 sacnt; /* 0x.0038 - SSI AC97 Control Register */ | ||
32 | __be32 sacadd; /* 0x.003C - SSI AC97 Command Address Register */ | ||
33 | __be32 sacdat; /* 0x.0040 - SSI AC97 Command Data Register */ | ||
34 | __be32 satag; /* 0x.0044 - SSI AC97 Tag Register */ | ||
35 | __be32 stmsk; /* 0x.0048 - SSI Transmit Time Slot Mask Register */ | ||
36 | __be32 srmsk; /* 0x.004C - SSI Receive Time Slot Mask Register */ | ||
37 | __be32 saccst; /* 0x.0050 - SSI AC97 Channel Status Register */ | ||
38 | __be32 saccen; /* 0x.0054 - SSI AC97 Channel Enable Register */ | ||
39 | __be32 saccdis; /* 0x.0058 - SSI AC97 Channel Disable Register */ | ||
40 | }; | ||
41 | |||
42 | #define CCSR_SSI_SCR_RFR_CLK_DIS 0x00000800 | ||
43 | #define CCSR_SSI_SCR_TFR_CLK_DIS 0x00000400 | ||
44 | #define CCSR_SSI_SCR_TCH_EN 0x00000100 | ||
45 | #define CCSR_SSI_SCR_SYS_CLK_EN 0x00000080 | ||
46 | #define CCSR_SSI_SCR_I2S_MODE_MASK 0x00000060 | ||
47 | #define CCSR_SSI_SCR_I2S_MODE_NORMAL 0x00000000 | ||
48 | #define CCSR_SSI_SCR_I2S_MODE_MASTER 0x00000020 | ||
49 | #define CCSR_SSI_SCR_I2S_MODE_SLAVE 0x00000040 | ||
50 | #define CCSR_SSI_SCR_SYN 0x00000010 | ||
51 | #define CCSR_SSI_SCR_NET 0x00000008 | ||
52 | #define CCSR_SSI_SCR_RE 0x00000004 | ||
53 | #define CCSR_SSI_SCR_TE 0x00000002 | ||
54 | #define CCSR_SSI_SCR_SSIEN 0x00000001 | ||
55 | |||
56 | #define CCSR_SSI_SISR_RFRC 0x01000000 | ||
57 | #define CCSR_SSI_SISR_TFRC 0x00800000 | ||
58 | #define CCSR_SSI_SISR_CMDAU 0x00040000 | ||
59 | #define CCSR_SSI_SISR_CMDDU 0x00020000 | ||
60 | #define CCSR_SSI_SISR_RXT 0x00010000 | ||
61 | #define CCSR_SSI_SISR_RDR1 0x00008000 | ||
62 | #define CCSR_SSI_SISR_RDR0 0x00004000 | ||
63 | #define CCSR_SSI_SISR_TDE1 0x00002000 | ||
64 | #define CCSR_SSI_SISR_TDE0 0x00001000 | ||
65 | #define CCSR_SSI_SISR_ROE1 0x00000800 | ||
66 | #define CCSR_SSI_SISR_ROE0 0x00000400 | ||
67 | #define CCSR_SSI_SISR_TUE1 0x00000200 | ||
68 | #define CCSR_SSI_SISR_TUE0 0x00000100 | ||
69 | #define CCSR_SSI_SISR_TFS 0x00000080 | ||
70 | #define CCSR_SSI_SISR_RFS 0x00000040 | ||
71 | #define CCSR_SSI_SISR_TLS 0x00000020 | ||
72 | #define CCSR_SSI_SISR_RLS 0x00000010 | ||
73 | #define CCSR_SSI_SISR_RFF1 0x00000008 | ||
74 | #define CCSR_SSI_SISR_RFF0 0x00000004 | ||
75 | #define CCSR_SSI_SISR_TFE1 0x00000002 | ||
76 | #define CCSR_SSI_SISR_TFE0 0x00000001 | ||
77 | |||
78 | #define CCSR_SSI_SIER_RFRC_EN 0x01000000 | ||
79 | #define CCSR_SSI_SIER_TFRC_EN 0x00800000 | ||
80 | #define CCSR_SSI_SIER_RDMAE 0x00400000 | ||
81 | #define CCSR_SSI_SIER_RIE 0x00200000 | ||
82 | #define CCSR_SSI_SIER_TDMAE 0x00100000 | ||
83 | #define CCSR_SSI_SIER_TIE 0x00080000 | ||
84 | #define CCSR_SSI_SIER_CMDAU_EN 0x00040000 | ||
85 | #define CCSR_SSI_SIER_CMDDU_EN 0x00020000 | ||
86 | #define CCSR_SSI_SIER_RXT_EN 0x00010000 | ||
87 | #define CCSR_SSI_SIER_RDR1_EN 0x00008000 | ||
88 | #define CCSR_SSI_SIER_RDR0_EN 0x00004000 | ||
89 | #define CCSR_SSI_SIER_TDE1_EN 0x00002000 | ||
90 | #define CCSR_SSI_SIER_TDE0_EN 0x00001000 | ||
91 | #define CCSR_SSI_SIER_ROE1_EN 0x00000800 | ||
92 | #define CCSR_SSI_SIER_ROE0_EN 0x00000400 | ||
93 | #define CCSR_SSI_SIER_TUE1_EN 0x00000200 | ||
94 | #define CCSR_SSI_SIER_TUE0_EN 0x00000100 | ||
95 | #define CCSR_SSI_SIER_TFS_EN 0x00000080 | ||
96 | #define CCSR_SSI_SIER_RFS_EN 0x00000040 | ||
97 | #define CCSR_SSI_SIER_TLS_EN 0x00000020 | ||
98 | #define CCSR_SSI_SIER_RLS_EN 0x00000010 | ||
99 | #define CCSR_SSI_SIER_RFF1_EN 0x00000008 | ||
100 | #define CCSR_SSI_SIER_RFF0_EN 0x00000004 | ||
101 | #define CCSR_SSI_SIER_TFE1_EN 0x00000002 | ||
102 | #define CCSR_SSI_SIER_TFE0_EN 0x00000001 | ||
103 | |||
104 | #define CCSR_SSI_STCR_TXBIT0 0x00000200 | ||
105 | #define CCSR_SSI_STCR_TFEN1 0x00000100 | ||
106 | #define CCSR_SSI_STCR_TFEN0 0x00000080 | ||
107 | #define CCSR_SSI_STCR_TFDIR 0x00000040 | ||
108 | #define CCSR_SSI_STCR_TXDIR 0x00000020 | ||
109 | #define CCSR_SSI_STCR_TSHFD 0x00000010 | ||
110 | #define CCSR_SSI_STCR_TSCKP 0x00000008 | ||
111 | #define CCSR_SSI_STCR_TFSI 0x00000004 | ||
112 | #define CCSR_SSI_STCR_TFSL 0x00000002 | ||
113 | #define CCSR_SSI_STCR_TEFS 0x00000001 | ||
114 | |||
115 | #define CCSR_SSI_SRCR_RXEXT 0x00000400 | ||
116 | #define CCSR_SSI_SRCR_RXBIT0 0x00000200 | ||
117 | #define CCSR_SSI_SRCR_RFEN1 0x00000100 | ||
118 | #define CCSR_SSI_SRCR_RFEN0 0x00000080 | ||
119 | #define CCSR_SSI_SRCR_RFDIR 0x00000040 | ||
120 | #define CCSR_SSI_SRCR_RXDIR 0x00000020 | ||
121 | #define CCSR_SSI_SRCR_RSHFD 0x00000010 | ||
122 | #define CCSR_SSI_SRCR_RSCKP 0x00000008 | ||
123 | #define CCSR_SSI_SRCR_RFSI 0x00000004 | ||
124 | #define CCSR_SSI_SRCR_RFSL 0x00000002 | ||
125 | #define CCSR_SSI_SRCR_REFS 0x00000001 | ||
126 | |||
127 | /* STCCR and SRCCR */ | ||
128 | #define CCSR_SSI_SxCCR_DIV2 0x00040000 | ||
129 | #define CCSR_SSI_SxCCR_PSR 0x00020000 | ||
130 | #define CCSR_SSI_SxCCR_WL_SHIFT 13 | ||
131 | #define CCSR_SSI_SxCCR_WL_MASK 0x0001E000 | ||
132 | #define CCSR_SSI_SxCCR_WL(x) \ | ||
133 | (((((x) / 2) - 1) << CCSR_SSI_SxCCR_WL_SHIFT) & CCSR_SSI_SxCCR_WL_MASK) | ||
134 | #define CCSR_SSI_SxCCR_DC_SHIFT 8 | ||
135 | #define CCSR_SSI_SxCCR_DC_MASK 0x00001F00 | ||
136 | #define CCSR_SSI_SxCCR_DC(x) \ | ||
137 | ((((x) - 1) << CCSR_SSI_SxCCR_DC_SHIFT) & CCSR_SSI_SxCCR_DC_MASK) | ||
138 | #define CCSR_SSI_SxCCR_PM_SHIFT 0 | ||
139 | #define CCSR_SSI_SxCCR_PM_MASK 0x000000FF | ||
140 | #define CCSR_SSI_SxCCR_PM(x) \ | ||
141 | ((((x) - 1) << CCSR_SSI_SxCCR_PM_SHIFT) & CCSR_SSI_SxCCR_PM_MASK) | ||
142 | |||
143 | /* | ||
144 | * The xFCNT bits are read-only, and the xFWM bits are read/write. Use the | ||
145 | * CCSR_SSI_SFCSR_xFCNTy() macros to read the FIFO counters, and use the | ||
146 | * CCSR_SSI_SFCSR_xFWMy() macros to set the watermarks. | ||
147 | */ | ||
148 | #define CCSR_SSI_SFCSR_RFCNT1_SHIFT 28 | ||
149 | #define CCSR_SSI_SFCSR_RFCNT1_MASK 0xF0000000 | ||
150 | #define CCSR_SSI_SFCSR_RFCNT1(x) \ | ||
151 | (((x) & CCSR_SSI_SFCSR_RFCNT1_MASK) >> CCSR_SSI_SFCSR_RFCNT1_SHIFT) | ||
152 | #define CCSR_SSI_SFCSR_TFCNT1_SHIFT 24 | ||
153 | #define CCSR_SSI_SFCSR_TFCNT1_MASK 0x0F000000 | ||
154 | #define CCSR_SSI_SFCSR_TFCNT1(x) \ | ||
155 | (((x) & CCSR_SSI_SFCSR_TFCNT1_MASK) >> CCSR_SSI_SFCSR_TFCNT1_SHIFT) | ||
156 | #define CCSR_SSI_SFCSR_RFWM1_SHIFT 20 | ||
157 | #define CCSR_SSI_SFCSR_RFWM1_MASK 0x00F00000 | ||
158 | #define CCSR_SSI_SFCSR_RFWM1(x) \ | ||
159 | (((x) << CCSR_SSI_SFCSR_RFWM1_SHIFT) & CCSR_SSI_SFCSR_RFWM1_MASK) | ||
160 | #define CCSR_SSI_SFCSR_TFWM1_SHIFT 16 | ||
161 | #define CCSR_SSI_SFCSR_TFWM1_MASK 0x000F0000 | ||
162 | #define CCSR_SSI_SFCSR_TFWM1(x) \ | ||
163 | (((x) << CCSR_SSI_SFCSR_TFWM1_SHIFT) & CCSR_SSI_SFCSR_TFWM1_MASK) | ||
164 | #define CCSR_SSI_SFCSR_RFCNT0_SHIFT 12 | ||
165 | #define CCSR_SSI_SFCSR_RFCNT0_MASK 0x0000F000 | ||
166 | #define CCSR_SSI_SFCSR_RFCNT0(x) \ | ||
167 | (((x) & CCSR_SSI_SFCSR_RFCNT0_MASK) >> CCSR_SSI_SFCSR_RFCNT0_SHIFT) | ||
168 | #define CCSR_SSI_SFCSR_TFCNT0_SHIFT 8 | ||
169 | #define CCSR_SSI_SFCSR_TFCNT0_MASK 0x00000F00 | ||
170 | #define CCSR_SSI_SFCSR_TFCNT0(x) \ | ||
171 | (((x) & CCSR_SSI_SFCSR_TFCNT0_MASK) >> CCSR_SSI_SFCSR_TFCNT0_SHIFT) | ||
172 | #define CCSR_SSI_SFCSR_RFWM0_SHIFT 4 | ||
173 | #define CCSR_SSI_SFCSR_RFWM0_MASK 0x000000F0 | ||
174 | #define CCSR_SSI_SFCSR_RFWM0(x) \ | ||
175 | (((x) << CCSR_SSI_SFCSR_RFWM0_SHIFT) & CCSR_SSI_SFCSR_RFWM0_MASK) | ||
176 | #define CCSR_SSI_SFCSR_TFWM0_SHIFT 0 | ||
177 | #define CCSR_SSI_SFCSR_TFWM0_MASK 0x0000000F | ||
178 | #define CCSR_SSI_SFCSR_TFWM0(x) \ | ||
179 | (((x) << CCSR_SSI_SFCSR_TFWM0_SHIFT) & CCSR_SSI_SFCSR_TFWM0_MASK) | ||
180 | |||
181 | #define CCSR_SSI_STR_TEST 0x00008000 | ||
182 | #define CCSR_SSI_STR_RCK2TCK 0x00004000 | ||
183 | #define CCSR_SSI_STR_RFS2TFS 0x00002000 | ||
184 | #define CCSR_SSI_STR_RXSTATE(x) (((x) >> 8) & 0x1F) | ||
185 | #define CCSR_SSI_STR_TXD2RXD 0x00000080 | ||
186 | #define CCSR_SSI_STR_TCK2RCK 0x00000040 | ||
187 | #define CCSR_SSI_STR_TFS2RFS 0x00000020 | ||
188 | #define CCSR_SSI_STR_TXSTATE(x) ((x) & 0x1F) | ||
189 | |||
190 | #define CCSR_SSI_SOR_CLKOFF 0x00000040 | ||
191 | #define CCSR_SSI_SOR_RX_CLR 0x00000020 | ||
192 | #define CCSR_SSI_SOR_TX_CLR 0x00000010 | ||
193 | #define CCSR_SSI_SOR_INIT 0x00000008 | ||
194 | #define CCSR_SSI_SOR_WAIT_SHIFT 1 | ||
195 | #define CCSR_SSI_SOR_WAIT_MASK 0x00000006 | ||
196 | #define CCSR_SSI_SOR_WAIT(x) (((x) & 3) << CCSR_SSI_SOR_WAIT_SHIFT) | ||
197 | #define CCSR_SSI_SOR_SYNRST 0x00000001 | ||
198 | |||
199 | /* Instantiation data for an SSI interface | ||
200 | * | ||
201 | * This structure contains all the information that the the SSI driver needs | ||
202 | * to instantiate an SSI interface with ALSA. The machine driver should | ||
203 | * create this structure, fill it in, call fsl_ssi_create_dai(), and then | ||
204 | * delete the structure. | ||
205 | * | ||
206 | * id: which SSI this is (0, 1, etc. ) | ||
207 | * ssi: pointer to the SSI's registers | ||
208 | * ssi_phys: physical address of the SSI registers | ||
209 | * irq: IRQ of this SSI | ||
210 | * dev: struct device, used to create the sysfs statistics file | ||
211 | */ | ||
212 | struct fsl_ssi_info { | ||
213 | unsigned int id; | ||
214 | struct ccsr_ssi __iomem *ssi; | ||
215 | dma_addr_t ssi_phys; | ||
216 | unsigned int irq; | ||
217 | struct device *dev; | ||
218 | }; | ||
219 | |||
220 | struct snd_soc_cpu_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info); | ||
221 | void fsl_ssi_destroy_dai(struct snd_soc_cpu_dai *fsl_ssi_dai); | ||
222 | |||
223 | #endif | ||
224 | |||
diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c new file mode 100644 index 000000000000..f26c4b2e8b6e --- /dev/null +++ b/sound/soc/fsl/mpc8610_hpcd.c | |||
@@ -0,0 +1,631 @@ | |||
1 | /** | ||
2 | * Freescale MPC8610HPCD ALSA SoC Fabric driver | ||
3 | * | ||
4 | * Author: Timur Tabi <timur@freescale.com> | ||
5 | * | ||
6 | * Copyright 2007-2008 Freescale Semiconductor, Inc. This file is licensed | ||
7 | * under the terms of the GNU General Public License version 2. This | ||
8 | * program is licensed "as is" without any warranty of any kind, whether | ||
9 | * express or implied. | ||
10 | */ | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/interrupt.h> | ||
14 | #include <linux/of_device.h> | ||
15 | #include <linux/of_platform.h> | ||
16 | #include <sound/soc.h> | ||
17 | #include <asm/immap_86xx.h> | ||
18 | |||
19 | #include "../codecs/cs4270.h" | ||
20 | #include "fsl_dma.h" | ||
21 | #include "fsl_ssi.h" | ||
22 | |||
23 | /** | ||
24 | * mpc8610_hpcd_data: fabric-specific ASoC device data | ||
25 | * | ||
26 | * This structure contains data for a single sound platform device on an | ||
27 | * MPC8610 HPCD. Some of the data is taken from the device tree. | ||
28 | */ | ||
29 | struct mpc8610_hpcd_data { | ||
30 | struct snd_soc_device sound_devdata; | ||
31 | struct snd_soc_dai_link dai; | ||
32 | struct snd_soc_machine machine; | ||
33 | unsigned int dai_format; | ||
34 | unsigned int codec_clk_direction; | ||
35 | unsigned int cpu_clk_direction; | ||
36 | unsigned int clk_frequency; | ||
37 | struct ccsr_guts __iomem *guts; | ||
38 | struct ccsr_ssi __iomem *ssi; | ||
39 | unsigned int ssi_id; /* 0 = SSI1, 1 = SSI2, etc */ | ||
40 | unsigned int ssi_irq; | ||
41 | unsigned int dma_id; /* 0 = DMA1, 1 = DMA2, etc */ | ||
42 | unsigned int dma_irq[2]; | ||
43 | struct ccsr_dma_channel __iomem *dma[2]; | ||
44 | unsigned int dma_channel_id[2]; /* 0 = ch 0, 1 = ch 1, etc*/ | ||
45 | }; | ||
46 | |||
47 | /** | ||
48 | * mpc8610_hpcd_machine_probe: initalize the board | ||
49 | * | ||
50 | * This function is called when platform_device_add() is called. It is used | ||
51 | * to initialize the board-specific hardware. | ||
52 | * | ||
53 | * Here we program the DMACR and PMUXCR registers. | ||
54 | */ | ||
55 | static int mpc8610_hpcd_machine_probe(struct platform_device *sound_device) | ||
56 | { | ||
57 | struct mpc8610_hpcd_data *machine_data = | ||
58 | sound_device->dev.platform_data; | ||
59 | |||
60 | /* Program the signal routing between the SSI and the DMA */ | ||
61 | guts_set_dmacr(machine_data->guts, machine_data->dma_id + 1, | ||
62 | machine_data->dma_channel_id[0], CCSR_GUTS_DMACR_DEV_SSI); | ||
63 | guts_set_dmacr(machine_data->guts, machine_data->dma_id + 1, | ||
64 | machine_data->dma_channel_id[1], CCSR_GUTS_DMACR_DEV_SSI); | ||
65 | |||
66 | guts_set_pmuxcr_dma(machine_data->guts, machine_data->dma_id, | ||
67 | machine_data->dma_channel_id[0], 0); | ||
68 | guts_set_pmuxcr_dma(machine_data->guts, machine_data->dma_id, | ||
69 | machine_data->dma_channel_id[1], 0); | ||
70 | |||
71 | guts_set_pmuxcr_dma(machine_data->guts, 1, 0, 0); | ||
72 | guts_set_pmuxcr_dma(machine_data->guts, 1, 3, 0); | ||
73 | guts_set_pmuxcr_dma(machine_data->guts, 0, 3, 0); | ||
74 | |||
75 | switch (machine_data->ssi_id) { | ||
76 | case 0: | ||
77 | clrsetbits_be32(&machine_data->guts->pmuxcr, | ||
78 | CCSR_GUTS_PMUXCR_SSI1_MASK, CCSR_GUTS_PMUXCR_SSI1_SSI); | ||
79 | break; | ||
80 | case 1: | ||
81 | clrsetbits_be32(&machine_data->guts->pmuxcr, | ||
82 | CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI2_SSI); | ||
83 | break; | ||
84 | } | ||
85 | |||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | /** | ||
90 | * mpc8610_hpcd_startup: program the board with various hardware parameters | ||
91 | * | ||
92 | * This function takes board-specific information, like clock frequencies | ||
93 | * and serial data formats, and passes that information to the codec and | ||
94 | * transport drivers. | ||
95 | */ | ||
96 | static int mpc8610_hpcd_startup(struct snd_pcm_substream *substream) | ||
97 | { | ||
98 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
99 | struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai; | ||
100 | struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai; | ||
101 | struct mpc8610_hpcd_data *machine_data = | ||
102 | rtd->socdev->dev->platform_data; | ||
103 | int ret = 0; | ||
104 | |||
105 | /* Tell the CPU driver what the serial protocol is. */ | ||
106 | if (cpu_dai->dai_ops.set_fmt) { | ||
107 | ret = cpu_dai->dai_ops.set_fmt(cpu_dai, | ||
108 | machine_data->dai_format); | ||
109 | if (ret < 0) { | ||
110 | dev_err(substream->pcm->card->dev, | ||
111 | "could not set CPU driver audio format\n"); | ||
112 | return ret; | ||
113 | } | ||
114 | } | ||
115 | |||
116 | /* Tell the codec driver what the serial protocol is. */ | ||
117 | if (codec_dai->dai_ops.set_fmt) { | ||
118 | ret = codec_dai->dai_ops.set_fmt(codec_dai, | ||
119 | machine_data->dai_format); | ||
120 | if (ret < 0) { | ||
121 | dev_err(substream->pcm->card->dev, | ||
122 | "could not set codec driver audio format\n"); | ||
123 | return ret; | ||
124 | } | ||
125 | } | ||
126 | |||
127 | /* | ||
128 | * Tell the CPU driver what the clock frequency is, and whether it's a | ||
129 | * slave or master. | ||
130 | */ | ||
131 | if (cpu_dai->dai_ops.set_sysclk) { | ||
132 | ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, 0, | ||
133 | machine_data->clk_frequency, | ||
134 | machine_data->cpu_clk_direction); | ||
135 | if (ret < 0) { | ||
136 | dev_err(substream->pcm->card->dev, | ||
137 | "could not set CPU driver clock parameters\n"); | ||
138 | return ret; | ||
139 | } | ||
140 | } | ||
141 | |||
142 | /* | ||
143 | * Tell the codec driver what the MCLK frequency is, and whether it's | ||
144 | * a slave or master. | ||
145 | */ | ||
146 | if (codec_dai->dai_ops.set_sysclk) { | ||
147 | ret = codec_dai->dai_ops.set_sysclk(codec_dai, 0, | ||
148 | machine_data->clk_frequency, | ||
149 | machine_data->codec_clk_direction); | ||
150 | if (ret < 0) { | ||
151 | dev_err(substream->pcm->card->dev, | ||
152 | "could not set codec driver clock params\n"); | ||
153 | return ret; | ||
154 | } | ||
155 | } | ||
156 | |||
157 | return 0; | ||
158 | } | ||
159 | |||
160 | /** | ||
161 | * mpc8610_hpcd_machine_remove: Remove the sound device | ||
162 | * | ||
163 | * This function is called to remove the sound device for one SSI. We | ||
164 | * de-program the DMACR and PMUXCR register. | ||
165 | */ | ||
166 | int mpc8610_hpcd_machine_remove(struct platform_device *sound_device) | ||
167 | { | ||
168 | struct mpc8610_hpcd_data *machine_data = | ||
169 | sound_device->dev.platform_data; | ||
170 | |||
171 | /* Restore the signal routing */ | ||
172 | |||
173 | guts_set_dmacr(machine_data->guts, machine_data->dma_id + 1, | ||
174 | machine_data->dma_channel_id[0], 0); | ||
175 | guts_set_dmacr(machine_data->guts, machine_data->dma_id + 1, | ||
176 | machine_data->dma_channel_id[1], 0); | ||
177 | |||
178 | switch (machine_data->ssi_id) { | ||
179 | case 0: | ||
180 | clrsetbits_be32(&machine_data->guts->pmuxcr, | ||
181 | CCSR_GUTS_PMUXCR_SSI1_MASK, CCSR_GUTS_PMUXCR_SSI1_LA); | ||
182 | break; | ||
183 | case 1: | ||
184 | clrsetbits_be32(&machine_data->guts->pmuxcr, | ||
185 | CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI1_LA); | ||
186 | break; | ||
187 | } | ||
188 | |||
189 | return 0; | ||
190 | } | ||
191 | |||
192 | /** | ||
193 | * mpc8610_hpcd_ops: ASoC fabric driver operations | ||
194 | */ | ||
195 | static struct snd_soc_ops mpc8610_hpcd_ops = { | ||
196 | .startup = mpc8610_hpcd_startup, | ||
197 | }; | ||
198 | |||
199 | /** | ||
200 | * mpc8610_hpcd_machine: ASoC machine data | ||
201 | */ | ||
202 | static struct snd_soc_machine mpc8610_hpcd_machine = { | ||
203 | .probe = mpc8610_hpcd_machine_probe, | ||
204 | .remove = mpc8610_hpcd_machine_remove, | ||
205 | .name = "MPC8610 HPCD", | ||
206 | .num_links = 1, | ||
207 | }; | ||
208 | |||
209 | /** | ||
210 | * mpc8610_hpcd_probe: OF probe function for the fabric driver | ||
211 | * | ||
212 | * This function gets called when an SSI node is found in the device tree. | ||
213 | * | ||
214 | * Although this is a fabric driver, the SSI node is the "master" node with | ||
215 | * respect to audio hardware connections. Therefore, we create a new ASoC | ||
216 | * device for each new SSI node that has a codec attached. | ||
217 | * | ||
218 | * FIXME: Currently, we only support one DMA controller, so if there are | ||
219 | * multiple SSI nodes with codecs, only the first will be supported. | ||
220 | * | ||
221 | * FIXME: Even if we did support multiple DMA controllers, we have no | ||
222 | * mechanism for assigning DMA controllers and channels to the individual | ||
223 | * SSI devices. We also probably aren't compatible with the generic Elo DMA | ||
224 | * device driver. | ||
225 | */ | ||
226 | static int mpc8610_hpcd_probe(struct of_device *ofdev, | ||
227 | const struct of_device_id *match) | ||
228 | { | ||
229 | struct device_node *np = ofdev->node; | ||
230 | struct device_node *codec_np = NULL; | ||
231 | struct device_node *guts_np = NULL; | ||
232 | struct device_node *dma_np = NULL; | ||
233 | struct device_node *dma_channel_np = NULL; | ||
234 | const phandle *codec_ph; | ||
235 | const char *sprop; | ||
236 | const u32 *iprop; | ||
237 | struct resource res; | ||
238 | struct platform_device *sound_device = NULL; | ||
239 | struct mpc8610_hpcd_data *machine_data; | ||
240 | struct fsl_ssi_info ssi_info; | ||
241 | struct fsl_dma_info dma_info; | ||
242 | int ret = -ENODEV; | ||
243 | |||
244 | machine_data = kzalloc(sizeof(struct mpc8610_hpcd_data), GFP_KERNEL); | ||
245 | if (!machine_data) | ||
246 | return -ENOMEM; | ||
247 | |||
248 | memset(&ssi_info, 0, sizeof(ssi_info)); | ||
249 | memset(&dma_info, 0, sizeof(dma_info)); | ||
250 | |||
251 | ssi_info.dev = &ofdev->dev; | ||
252 | |||
253 | /* | ||
254 | * We are only interested in SSIs with a codec phandle in them, so let's | ||
255 | * make sure this SSI has one. | ||
256 | */ | ||
257 | codec_ph = of_get_property(np, "codec-handle", NULL); | ||
258 | if (!codec_ph) | ||
259 | goto error; | ||
260 | |||
261 | codec_np = of_find_node_by_phandle(*codec_ph); | ||
262 | if (!codec_np) | ||
263 | goto error; | ||
264 | |||
265 | /* The MPC8610 HPCD only knows about the CS4270 codec, so reject | ||
266 | anything else. */ | ||
267 | if (!of_device_is_compatible(codec_np, "cirrus,cs4270")) | ||
268 | goto error; | ||
269 | |||
270 | /* Get the device ID */ | ||
271 | iprop = of_get_property(np, "cell-index", NULL); | ||
272 | if (!iprop) { | ||
273 | dev_err(&ofdev->dev, "cell-index property not found\n"); | ||
274 | ret = -EINVAL; | ||
275 | goto error; | ||
276 | } | ||
277 | machine_data->ssi_id = *iprop; | ||
278 | ssi_info.id = *iprop; | ||
279 | |||
280 | /* Get the serial format and clock direction. */ | ||
281 | sprop = of_get_property(np, "fsl,mode", NULL); | ||
282 | if (!sprop) { | ||
283 | dev_err(&ofdev->dev, "fsl,mode property not found\n"); | ||
284 | ret = -EINVAL; | ||
285 | goto error; | ||
286 | } | ||
287 | |||
288 | if (strcasecmp(sprop, "i2s-slave") == 0) { | ||
289 | machine_data->dai_format = SND_SOC_DAIFMT_I2S; | ||
290 | machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT; | ||
291 | machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN; | ||
292 | |||
293 | /* | ||
294 | * In i2s-slave mode, the codec has its own clock source, so we | ||
295 | * need to get the frequency from the device tree and pass it to | ||
296 | * the codec driver. | ||
297 | */ | ||
298 | iprop = of_get_property(codec_np, "clock-frequency", NULL); | ||
299 | if (!iprop || !*iprop) { | ||
300 | dev_err(&ofdev->dev, "codec bus-frequency property " | ||
301 | "is missing or invalid\n"); | ||
302 | ret = -EINVAL; | ||
303 | goto error; | ||
304 | } | ||
305 | machine_data->clk_frequency = *iprop; | ||
306 | } else if (strcasecmp(sprop, "i2s-master") == 0) { | ||
307 | machine_data->dai_format = SND_SOC_DAIFMT_I2S; | ||
308 | machine_data->codec_clk_direction = SND_SOC_CLOCK_IN; | ||
309 | machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT; | ||
310 | } else if (strcasecmp(sprop, "lj-slave") == 0) { | ||
311 | machine_data->dai_format = SND_SOC_DAIFMT_LEFT_J; | ||
312 | machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT; | ||
313 | machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN; | ||
314 | } else if (strcasecmp(sprop, "lj-master") == 0) { | ||
315 | machine_data->dai_format = SND_SOC_DAIFMT_LEFT_J; | ||
316 | machine_data->codec_clk_direction = SND_SOC_CLOCK_IN; | ||
317 | machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT; | ||
318 | } else if (strcasecmp(sprop, "rj-master") == 0) { | ||
319 | machine_data->dai_format = SND_SOC_DAIFMT_RIGHT_J; | ||
320 | machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT; | ||
321 | machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN; | ||
322 | } else if (strcasecmp(sprop, "rj-master") == 0) { | ||
323 | machine_data->dai_format = SND_SOC_DAIFMT_RIGHT_J; | ||
324 | machine_data->codec_clk_direction = SND_SOC_CLOCK_IN; | ||
325 | machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT; | ||
326 | } else if (strcasecmp(sprop, "ac97-slave") == 0) { | ||
327 | machine_data->dai_format = SND_SOC_DAIFMT_AC97; | ||
328 | machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT; | ||
329 | machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN; | ||
330 | } else if (strcasecmp(sprop, "ac97-master") == 0) { | ||
331 | machine_data->dai_format = SND_SOC_DAIFMT_AC97; | ||
332 | machine_data->codec_clk_direction = SND_SOC_CLOCK_IN; | ||
333 | machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT; | ||
334 | } else { | ||
335 | dev_err(&ofdev->dev, | ||
336 | "unrecognized fsl,mode property \"%s\"\n", sprop); | ||
337 | ret = -EINVAL; | ||
338 | goto error; | ||
339 | } | ||
340 | |||
341 | if (!machine_data->clk_frequency) { | ||
342 | dev_err(&ofdev->dev, "unknown clock frequency\n"); | ||
343 | ret = -EINVAL; | ||
344 | goto error; | ||
345 | } | ||
346 | |||
347 | /* Read the SSI information from the device tree */ | ||
348 | ret = of_address_to_resource(np, 0, &res); | ||
349 | if (ret) { | ||
350 | dev_err(&ofdev->dev, "could not obtain SSI address\n"); | ||
351 | goto error; | ||
352 | } | ||
353 | if (!res.start) { | ||
354 | dev_err(&ofdev->dev, "invalid SSI address\n"); | ||
355 | goto error; | ||
356 | } | ||
357 | ssi_info.ssi_phys = res.start; | ||
358 | |||
359 | machine_data->ssi = ioremap(ssi_info.ssi_phys, sizeof(struct ccsr_ssi)); | ||
360 | if (!machine_data->ssi) { | ||
361 | dev_err(&ofdev->dev, "could not map SSI address %x\n", | ||
362 | ssi_info.ssi_phys); | ||
363 | ret = -EINVAL; | ||
364 | goto error; | ||
365 | } | ||
366 | ssi_info.ssi = machine_data->ssi; | ||
367 | |||
368 | |||
369 | /* Get the IRQ of the SSI */ | ||
370 | machine_data->ssi_irq = irq_of_parse_and_map(np, 0); | ||
371 | if (!machine_data->ssi_irq) { | ||
372 | dev_err(&ofdev->dev, "could not get SSI IRQ\n"); | ||
373 | ret = -EINVAL; | ||
374 | goto error; | ||
375 | } | ||
376 | ssi_info.irq = machine_data->ssi_irq; | ||
377 | |||
378 | |||
379 | /* Map the global utilities registers. */ | ||
380 | guts_np = of_find_compatible_node(NULL, NULL, "fsl,mpc8610-guts"); | ||
381 | if (!guts_np) { | ||
382 | dev_err(&ofdev->dev, "could not obtain address of GUTS\n"); | ||
383 | ret = -EINVAL; | ||
384 | goto error; | ||
385 | } | ||
386 | machine_data->guts = of_iomap(guts_np, 0); | ||
387 | of_node_put(guts_np); | ||
388 | if (!machine_data->guts) { | ||
389 | dev_err(&ofdev->dev, "could not map GUTS\n"); | ||
390 | ret = -EINVAL; | ||
391 | goto error; | ||
392 | } | ||
393 | |||
394 | /* Find the DMA channels to use. For now, we always use the first DMA | ||
395 | controller. */ | ||
396 | for_each_compatible_node(dma_np, NULL, "fsl,mpc8610-dma") { | ||
397 | iprop = of_get_property(dma_np, "cell-index", NULL); | ||
398 | if (iprop && (*iprop == 0)) { | ||
399 | of_node_put(dma_np); | ||
400 | break; | ||
401 | } | ||
402 | } | ||
403 | if (!dma_np) { | ||
404 | dev_err(&ofdev->dev, "could not find DMA node\n"); | ||
405 | ret = -EINVAL; | ||
406 | goto error; | ||
407 | } | ||
408 | machine_data->dma_id = *iprop; | ||
409 | |||
410 | /* | ||
411 | * Find the DMA channels to use. For now, we always use DMA channel 0 | ||
412 | * for playback, and DMA channel 1 for capture. | ||
413 | */ | ||
414 | while ((dma_channel_np = of_get_next_child(dma_np, dma_channel_np))) { | ||
415 | iprop = of_get_property(dma_channel_np, "cell-index", NULL); | ||
416 | /* Is it DMA channel 0? */ | ||
417 | if (iprop && (*iprop == 0)) { | ||
418 | /* dma_channel[0] and dma_irq[0] are for playback */ | ||
419 | dma_info.dma_channel[0] = of_iomap(dma_channel_np, 0); | ||
420 | dma_info.dma_irq[0] = | ||
421 | irq_of_parse_and_map(dma_channel_np, 0); | ||
422 | machine_data->dma_channel_id[0] = *iprop; | ||
423 | continue; | ||
424 | } | ||
425 | if (iprop && (*iprop == 1)) { | ||
426 | /* dma_channel[1] and dma_irq[1] are for capture */ | ||
427 | dma_info.dma_channel[1] = of_iomap(dma_channel_np, 0); | ||
428 | dma_info.dma_irq[1] = | ||
429 | irq_of_parse_and_map(dma_channel_np, 0); | ||
430 | machine_data->dma_channel_id[1] = *iprop; | ||
431 | continue; | ||
432 | } | ||
433 | } | ||
434 | if (!dma_info.dma_channel[0] || !dma_info.dma_channel[1] || | ||
435 | !dma_info.dma_irq[0] || !dma_info.dma_irq[1]) { | ||
436 | dev_err(&ofdev->dev, "could not find DMA channels\n"); | ||
437 | ret = -EINVAL; | ||
438 | goto error; | ||
439 | } | ||
440 | |||
441 | dma_info.ssi_stx_phys = ssi_info.ssi_phys + | ||
442 | offsetof(struct ccsr_ssi, stx0); | ||
443 | dma_info.ssi_srx_phys = ssi_info.ssi_phys + | ||
444 | offsetof(struct ccsr_ssi, srx0); | ||
445 | |||
446 | /* We have the DMA information, so tell the DMA driver what it is */ | ||
447 | if (!fsl_dma_configure(&dma_info)) { | ||
448 | dev_err(&ofdev->dev, "could not instantiate DMA device\n"); | ||
449 | ret = -EBUSY; | ||
450 | goto error; | ||
451 | } | ||
452 | |||
453 | /* | ||
454 | * Initialize our DAI data structure. We should probably get this | ||
455 | * information from the device tree. | ||
456 | */ | ||
457 | machine_data->dai.name = "CS4270"; | ||
458 | machine_data->dai.stream_name = "CS4270"; | ||
459 | |||
460 | machine_data->dai.cpu_dai = fsl_ssi_create_dai(&ssi_info); | ||
461 | machine_data->dai.codec_dai = &cs4270_dai; /* The codec_dai we want */ | ||
462 | machine_data->dai.ops = &mpc8610_hpcd_ops; | ||
463 | |||
464 | mpc8610_hpcd_machine.dai_link = &machine_data->dai; | ||
465 | |||
466 | /* Allocate a new audio platform device structure */ | ||
467 | sound_device = platform_device_alloc("soc-audio", -1); | ||
468 | if (!sound_device) { | ||
469 | dev_err(&ofdev->dev, "platform device allocation failed\n"); | ||
470 | ret = -ENOMEM; | ||
471 | goto error; | ||
472 | } | ||
473 | |||
474 | machine_data->sound_devdata.machine = &mpc8610_hpcd_machine; | ||
475 | machine_data->sound_devdata.codec_dev = &soc_codec_device_cs4270; | ||
476 | machine_data->sound_devdata.platform = &fsl_soc_platform; | ||
477 | |||
478 | sound_device->dev.platform_data = machine_data; | ||
479 | |||
480 | |||
481 | /* Set the platform device and ASoC device to point to each other */ | ||
482 | platform_set_drvdata(sound_device, &machine_data->sound_devdata); | ||
483 | |||
484 | machine_data->sound_devdata.dev = &sound_device->dev; | ||
485 | |||
486 | |||
487 | /* Tell ASoC to probe us. This will call mpc8610_hpcd_machine.probe(), | ||
488 | if it exists. */ | ||
489 | ret = platform_device_add(sound_device); | ||
490 | |||
491 | if (ret) { | ||
492 | dev_err(&ofdev->dev, "platform device add failed\n"); | ||
493 | goto error; | ||
494 | } | ||
495 | |||
496 | dev_set_drvdata(&ofdev->dev, sound_device); | ||
497 | |||
498 | return 0; | ||
499 | |||
500 | error: | ||
501 | of_node_put(codec_np); | ||
502 | of_node_put(guts_np); | ||
503 | of_node_put(dma_np); | ||
504 | of_node_put(dma_channel_np); | ||
505 | |||
506 | if (sound_device) | ||
507 | platform_device_unregister(sound_device); | ||
508 | |||
509 | if (machine_data->dai.cpu_dai) | ||
510 | fsl_ssi_destroy_dai(machine_data->dai.cpu_dai); | ||
511 | |||
512 | if (ssi_info.ssi) | ||
513 | iounmap(ssi_info.ssi); | ||
514 | |||
515 | if (ssi_info.irq) | ||
516 | irq_dispose_mapping(ssi_info.irq); | ||
517 | |||
518 | if (dma_info.dma_channel[0]) | ||
519 | iounmap(dma_info.dma_channel[0]); | ||
520 | |||
521 | if (dma_info.dma_channel[1]) | ||
522 | iounmap(dma_info.dma_channel[1]); | ||
523 | |||
524 | if (dma_info.dma_irq[0]) | ||
525 | irq_dispose_mapping(dma_info.dma_irq[0]); | ||
526 | |||
527 | if (dma_info.dma_irq[1]) | ||
528 | irq_dispose_mapping(dma_info.dma_irq[1]); | ||
529 | |||
530 | if (machine_data->guts) | ||
531 | iounmap(machine_data->guts); | ||
532 | |||
533 | kfree(machine_data); | ||
534 | |||
535 | return ret; | ||
536 | } | ||
537 | |||
538 | /** | ||
539 | * mpc8610_hpcd_remove: remove the OF device | ||
540 | * | ||
541 | * This function is called when the OF device is removed. | ||
542 | */ | ||
543 | static int mpc8610_hpcd_remove(struct of_device *ofdev) | ||
544 | { | ||
545 | struct platform_device *sound_device = dev_get_drvdata(&ofdev->dev); | ||
546 | struct mpc8610_hpcd_data *machine_data = | ||
547 | sound_device->dev.platform_data; | ||
548 | |||
549 | platform_device_unregister(sound_device); | ||
550 | |||
551 | if (machine_data->dai.cpu_dai) | ||
552 | fsl_ssi_destroy_dai(machine_data->dai.cpu_dai); | ||
553 | |||
554 | if (machine_data->ssi) | ||
555 | iounmap(machine_data->ssi); | ||
556 | |||
557 | if (machine_data->dma[0]) | ||
558 | iounmap(machine_data->dma[0]); | ||
559 | |||
560 | if (machine_data->dma[1]) | ||
561 | iounmap(machine_data->dma[1]); | ||
562 | |||
563 | if (machine_data->dma_irq[0]) | ||
564 | irq_dispose_mapping(machine_data->dma_irq[0]); | ||
565 | |||
566 | if (machine_data->dma_irq[1]) | ||
567 | irq_dispose_mapping(machine_data->dma_irq[1]); | ||
568 | |||
569 | if (machine_data->guts) | ||
570 | iounmap(machine_data->guts); | ||
571 | |||
572 | kfree(machine_data); | ||
573 | sound_device->dev.platform_data = NULL; | ||
574 | |||
575 | dev_set_drvdata(&ofdev->dev, NULL); | ||
576 | |||
577 | return 0; | ||
578 | } | ||
579 | |||
580 | static struct of_device_id mpc8610_hpcd_match[] = { | ||
581 | { | ||
582 | .compatible = "fsl,mpc8610-ssi", | ||
583 | }, | ||
584 | {} | ||
585 | }; | ||
586 | MODULE_DEVICE_TABLE(of, mpc8610_hpcd_match); | ||
587 | |||
588 | static struct of_platform_driver mpc8610_hpcd_of_driver = { | ||
589 | .owner = THIS_MODULE, | ||
590 | .name = "mpc8610_hpcd", | ||
591 | .match_table = mpc8610_hpcd_match, | ||
592 | .probe = mpc8610_hpcd_probe, | ||
593 | .remove = mpc8610_hpcd_remove, | ||
594 | }; | ||
595 | |||
596 | /** | ||
597 | * mpc8610_hpcd_init: fabric driver initialization. | ||
598 | * | ||
599 | * This function is called when this module is loaded. | ||
600 | */ | ||
601 | static int __init mpc8610_hpcd_init(void) | ||
602 | { | ||
603 | int ret; | ||
604 | |||
605 | printk(KERN_INFO "Freescale MPC8610 HPCD ALSA SoC fabric driver\n"); | ||
606 | |||
607 | ret = of_register_platform_driver(&mpc8610_hpcd_of_driver); | ||
608 | |||
609 | if (ret) | ||
610 | printk(KERN_ERR | ||
611 | "mpc8610-hpcd: failed to register platform driver\n"); | ||
612 | |||
613 | return ret; | ||
614 | } | ||
615 | |||
616 | /** | ||
617 | * mpc8610_hpcd_exit: fabric driver exit | ||
618 | * | ||
619 | * This function is called when this driver is unloaded. | ||
620 | */ | ||
621 | static void __exit mpc8610_hpcd_exit(void) | ||
622 | { | ||
623 | of_unregister_platform_driver(&mpc8610_hpcd_of_driver); | ||
624 | } | ||
625 | |||
626 | module_init(mpc8610_hpcd_init); | ||
627 | module_exit(mpc8610_hpcd_exit); | ||
628 | |||
629 | MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); | ||
630 | MODULE_DESCRIPTION("Freescale MPC8610 HPCD ALSA SoC fabric driver"); | ||
631 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index a83e22937c27..484f883459e0 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig | |||
@@ -53,3 +53,12 @@ config SND_PXA2XX_SOC_TOSA | |||
53 | help | 53 | help |
54 | Say Y if you want to add support for SoC audio on Sharp | 54 | Say Y if you want to add support for SoC audio on Sharp |
55 | Zaurus SL-C6000x models (Tosa). | 55 | Zaurus SL-C6000x models (Tosa). |
56 | |||
57 | config SND_PXA2XX_SOC_E800 | ||
58 | tristate "SoC AC97 Audio support for e800" | ||
59 | depends on SND_PXA2XX_SOC && MACH_E800 | ||
60 | select SND_SOC_WM9712 | ||
61 | select SND_PXA2XX_SOC_AC97 | ||
62 | help | ||
63 | Say Y if you want to add support for SoC audio on the | ||
64 | Toshiba e800 PDA | ||
diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile index 78e0d6b07d1d..04e5646f75ba 100644 --- a/sound/soc/pxa/Makefile +++ b/sound/soc/pxa/Makefile | |||
@@ -11,10 +11,12 @@ obj-$(CONFIG_SND_PXA2XX_SOC_I2S) += snd-soc-pxa2xx-i2s.o | |||
11 | snd-soc-corgi-objs := corgi.o | 11 | snd-soc-corgi-objs := corgi.o |
12 | snd-soc-poodle-objs := poodle.o | 12 | snd-soc-poodle-objs := poodle.o |
13 | snd-soc-tosa-objs := tosa.o | 13 | snd-soc-tosa-objs := tosa.o |
14 | snd-soc-e800-objs := e800_wm9712.o | ||
14 | snd-soc-spitz-objs := spitz.o | 15 | snd-soc-spitz-objs := spitz.o |
15 | 16 | ||
16 | obj-$(CONFIG_SND_PXA2XX_SOC_CORGI) += snd-soc-corgi.o | 17 | obj-$(CONFIG_SND_PXA2XX_SOC_CORGI) += snd-soc-corgi.o |
17 | obj-$(CONFIG_SND_PXA2XX_SOC_POODLE) += snd-soc-poodle.o | 18 | obj-$(CONFIG_SND_PXA2XX_SOC_POODLE) += snd-soc-poodle.o |
18 | obj-$(CONFIG_SND_PXA2XX_SOC_TOSA) += snd-soc-tosa.o | 19 | obj-$(CONFIG_SND_PXA2XX_SOC_TOSA) += snd-soc-tosa.o |
20 | obj-$(CONFIG_SND_PXA2XX_SOC_E800) += snd-soc-e800.o | ||
19 | obj-$(CONFIG_SND_PXA2XX_SOC_SPITZ) += snd-soc-spitz.o | 21 | obj-$(CONFIG_SND_PXA2XX_SOC_SPITZ) += snd-soc-spitz.o |
20 | 22 | ||
diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c index 5ee51a994ac3..3f34e531bebf 100644 --- a/sound/soc/pxa/corgi.c +++ b/sound/soc/pxa/corgi.c | |||
@@ -22,7 +22,6 @@ | |||
22 | #include <linux/timer.h> | 22 | #include <linux/timer.h> |
23 | #include <linux/interrupt.h> | 23 | #include <linux/interrupt.h> |
24 | #include <linux/platform_device.h> | 24 | #include <linux/platform_device.h> |
25 | #include <sound/driver.h> | ||
26 | #include <sound/core.h> | 25 | #include <sound/core.h> |
27 | #include <sound/pcm.h> | 26 | #include <sound/pcm.h> |
28 | #include <sound/soc.h> | 27 | #include <sound/soc.h> |
diff --git a/sound/soc/pxa/e800_wm9712.c b/sound/soc/pxa/e800_wm9712.c new file mode 100644 index 000000000000..06e8afb25277 --- /dev/null +++ b/sound/soc/pxa/e800_wm9712.c | |||
@@ -0,0 +1,89 @@ | |||
1 | /* | ||
2 | * e800-wm9712.c -- SoC audio for e800 | ||
3 | * | ||
4 | * Based on tosa.c | ||
5 | * | ||
6 | * Copyright 2007 (c) Ian Molton <spyro@f2s.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms of the GNU General Public License as published by the | ||
10 | * Free Software Foundation; version 2 ONLY. | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include <linux/module.h> | ||
15 | #include <linux/moduleparam.h> | ||
16 | #include <linux/device.h> | ||
17 | |||
18 | #include <sound/core.h> | ||
19 | #include <sound/pcm.h> | ||
20 | #include <sound/soc.h> | ||
21 | #include <sound/soc-dapm.h> | ||
22 | |||
23 | #include <asm/mach-types.h> | ||
24 | #include <asm/arch/pxa-regs.h> | ||
25 | #include <asm/arch/hardware.h> | ||
26 | #include <asm/arch/audio.h> | ||
27 | |||
28 | #include "../codecs/wm9712.h" | ||
29 | #include "pxa2xx-pcm.h" | ||
30 | #include "pxa2xx-ac97.h" | ||
31 | |||
32 | static struct snd_soc_machine e800; | ||
33 | |||
34 | static struct snd_soc_dai_link e800_dai[] = { | ||
35 | { | ||
36 | .name = "AC97 Aux", | ||
37 | .stream_name = "AC97 Aux", | ||
38 | .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX], | ||
39 | .codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX], | ||
40 | }, | ||
41 | }; | ||
42 | |||
43 | static struct snd_soc_machine e800 = { | ||
44 | .name = "Toshiba e800", | ||
45 | .dai_link = e800_dai, | ||
46 | .num_links = ARRAY_SIZE(e800_dai), | ||
47 | }; | ||
48 | |||
49 | static struct snd_soc_device e800_snd_devdata = { | ||
50 | .machine = &e800, | ||
51 | .platform = &pxa2xx_soc_platform, | ||
52 | .codec_dev = &soc_codec_dev_wm9712, | ||
53 | }; | ||
54 | |||
55 | static struct platform_device *e800_snd_device; | ||
56 | |||
57 | static int __init e800_init(void) | ||
58 | { | ||
59 | int ret; | ||
60 | |||
61 | if (!machine_is_e800()) | ||
62 | return -ENODEV; | ||
63 | |||
64 | e800_snd_device = platform_device_alloc("soc-audio", -1); | ||
65 | if (!e800_snd_device) | ||
66 | return -ENOMEM; | ||
67 | |||
68 | platform_set_drvdata(e800_snd_device, &e800_snd_devdata); | ||
69 | e800_snd_devdata.dev = &e800_snd_device->dev; | ||
70 | ret = platform_device_add(e800_snd_device); | ||
71 | |||
72 | if (ret) | ||
73 | platform_device_put(e800_snd_device); | ||
74 | |||
75 | return ret; | ||
76 | } | ||
77 | |||
78 | static void __exit e800_exit(void) | ||
79 | { | ||
80 | platform_device_unregister(e800_snd_device); | ||
81 | } | ||
82 | |||
83 | module_init(e800_init); | ||
84 | module_exit(e800_exit); | ||
85 | |||
86 | /* Module information */ | ||
87 | MODULE_AUTHOR("Ian Molton <spyro@f2s.com>"); | ||
88 | MODULE_DESCRIPTION("ALSA SoC driver for e800"); | ||
89 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c index 0915cf740421..5ae59bd309a3 100644 --- a/sound/soc/pxa/poodle.c +++ b/sound/soc/pxa/poodle.c | |||
@@ -19,7 +19,6 @@ | |||
19 | #include <linux/timer.h> | 19 | #include <linux/timer.h> |
20 | #include <linux/interrupt.h> | 20 | #include <linux/interrupt.h> |
21 | #include <linux/platform_device.h> | 21 | #include <linux/platform_device.h> |
22 | #include <sound/driver.h> | ||
23 | #include <sound/core.h> | 22 | #include <sound/core.h> |
24 | #include <sound/pcm.h> | 23 | #include <sound/pcm.h> |
25 | #include <sound/soc.h> | 24 | #include <sound/soc.h> |
diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index 60e6f4677f93..815c15336255 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c | |||
@@ -17,7 +17,6 @@ | |||
17 | #include <linux/wait.h> | 17 | #include <linux/wait.h> |
18 | #include <linux/delay.h> | 18 | #include <linux/delay.h> |
19 | 19 | ||
20 | #include <sound/driver.h> | ||
21 | #include <sound/core.h> | 20 | #include <sound/core.h> |
22 | #include <sound/pcm.h> | 21 | #include <sound/pcm.h> |
23 | #include <sound/ac97_codec.h> | 22 | #include <sound/ac97_codec.h> |
diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c index 50c5c83f67db..692b90002489 100644 --- a/sound/soc/pxa/pxa2xx-i2s.c +++ b/sound/soc/pxa/pxa2xx-i2s.c | |||
@@ -18,7 +18,6 @@ | |||
18 | #include <linux/module.h> | 18 | #include <linux/module.h> |
19 | #include <linux/device.h> | 19 | #include <linux/device.h> |
20 | #include <linux/delay.h> | 20 | #include <linux/delay.h> |
21 | #include <sound/driver.h> | ||
22 | #include <sound/core.h> | 21 | #include <sound/core.h> |
23 | #include <sound/pcm.h> | 22 | #include <sound/pcm.h> |
24 | #include <sound/initval.h> | 23 | #include <sound/initval.h> |
diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c index 35e8fa3a469c..daeaa4c8b876 100644 --- a/sound/soc/pxa/pxa2xx-pcm.c +++ b/sound/soc/pxa/pxa2xx-pcm.c | |||
@@ -16,7 +16,6 @@ | |||
16 | #include <linux/slab.h> | 16 | #include <linux/slab.h> |
17 | #include <linux/dma-mapping.h> | 17 | #include <linux/dma-mapping.h> |
18 | 18 | ||
19 | #include <sound/driver.h> | ||
20 | #include <sound/core.h> | 19 | #include <sound/core.h> |
21 | #include <sound/pcm.h> | 20 | #include <sound/pcm.h> |
22 | #include <sound/pcm_params.h> | 21 | #include <sound/pcm_params.h> |
diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c index 4dd8f35312b3..d56709e15435 100644 --- a/sound/soc/pxa/spitz.c +++ b/sound/soc/pxa/spitz.c | |||
@@ -22,7 +22,6 @@ | |||
22 | #include <linux/timer.h> | 22 | #include <linux/timer.h> |
23 | #include <linux/interrupt.h> | 23 | #include <linux/interrupt.h> |
24 | #include <linux/platform_device.h> | 24 | #include <linux/platform_device.h> |
25 | #include <sound/driver.h> | ||
26 | #include <sound/core.h> | 25 | #include <sound/core.h> |
27 | #include <sound/pcm.h> | 26 | #include <sound/pcm.h> |
28 | #include <sound/soc.h> | 27 | #include <sound/soc.h> |
diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c index 5504e30acf14..e4d40b528ca4 100644 --- a/sound/soc/pxa/tosa.c +++ b/sound/soc/pxa/tosa.c | |||
@@ -25,7 +25,6 @@ | |||
25 | #include <linux/moduleparam.h> | 25 | #include <linux/moduleparam.h> |
26 | #include <linux/device.h> | 26 | #include <linux/device.h> |
27 | 27 | ||
28 | #include <sound/driver.h> | ||
29 | #include <sound/core.h> | 28 | #include <sound/core.h> |
30 | #include <sound/pcm.h> | 29 | #include <sound/pcm.h> |
31 | #include <sound/soc.h> | 30 | #include <sound/soc.h> |
diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig index 5632a2e1518d..1f6dbfc4caa8 100644 --- a/sound/soc/s3c24xx/Kconfig +++ b/sound/soc/s3c24xx/Kconfig | |||
@@ -10,6 +10,9 @@ config SND_S3C24XX_SOC | |||
10 | config SND_S3C24XX_SOC_I2S | 10 | config SND_S3C24XX_SOC_I2S |
11 | tristate | 11 | tristate |
12 | 12 | ||
13 | config SND_S3C2412_SOC_I2S | ||
14 | tristate | ||
15 | |||
13 | config SND_S3C2443_SOC_AC97 | 16 | config SND_S3C2443_SOC_AC97 |
14 | tristate | 17 | tristate |
15 | select AC97_BUS | 18 | select AC97_BUS |
@@ -34,4 +37,12 @@ config SND_S3C24XX_SOC_SMDK2443_WM9710 | |||
34 | Say Y if you want to add support for SoC audio on smdk2443 | 37 | Say Y if you want to add support for SoC audio on smdk2443 |
35 | with the WM9710. | 38 | with the WM9710. |
36 | 39 | ||
40 | config SND_S3C24XX_SOC_LN2440SBC_ALC650 | ||
41 | tristate "SoC AC97 Audio support for LN2440SBC - ALC650" | ||
42 | depends on SND_S3C24XX_SOC | ||
43 | select SND_S3C2443_SOC_AC97 | ||
44 | select SND_SOC_AC97_CODEC | ||
45 | help | ||
46 | Say Y if you want to add support for SoC audio on ln2440sbc | ||
47 | with the ALC650. | ||
37 | 48 | ||
diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile index 13c92f0fa1e4..0aa5fb0b9700 100644 --- a/sound/soc/s3c24xx/Makefile +++ b/sound/soc/s3c24xx/Makefile | |||
@@ -1,15 +1,19 @@ | |||
1 | # S3c24XX Platform Support | 1 | # S3c24XX Platform Support |
2 | snd-soc-s3c24xx-objs := s3c24xx-pcm.o | 2 | snd-soc-s3c24xx-objs := s3c24xx-pcm.o |
3 | snd-soc-s3c24xx-i2s-objs := s3c24xx-i2s.o | 3 | snd-soc-s3c24xx-i2s-objs := s3c24xx-i2s.o |
4 | snd-soc-s3c2412-i2s-objs := s3c2412-i2s.o | ||
4 | snd-soc-s3c2443-ac97-objs := s3c2443-ac97.o | 5 | snd-soc-s3c2443-ac97-objs := s3c2443-ac97.o |
5 | 6 | ||
6 | obj-$(CONFIG_SND_S3C24XX_SOC) += snd-soc-s3c24xx.o | 7 | obj-$(CONFIG_SND_S3C24XX_SOC) += snd-soc-s3c24xx.o |
7 | obj-$(CONFIG_SND_S3C24XX_SOC_I2S) += snd-soc-s3c24xx-i2s.o | 8 | obj-$(CONFIG_SND_S3C24XX_SOC_I2S) += snd-soc-s3c24xx-i2s.o |
8 | obj-$(CONFIG_SND_S3C2443_SOC_AC97) += snd-soc-s3c2443-ac97.o | 9 | obj-$(CONFIG_SND_S3C2443_SOC_AC97) += snd-soc-s3c2443-ac97.o |
10 | obj-$(CONFIG_SND_S3C2412_SOC_I2S) += snd-soc-s3c2412-i2s.o | ||
9 | 11 | ||
10 | # S3C24XX Machine Support | 12 | # S3C24XX Machine Support |
11 | snd-soc-neo1973-wm8753-objs := neo1973_wm8753.o | 13 | snd-soc-neo1973-wm8753-objs := neo1973_wm8753.o |
12 | snd-soc-smdk2443-wm9710-objs := smdk2443_wm9710.o | 14 | snd-soc-smdk2443-wm9710-objs := smdk2443_wm9710.o |
15 | snd-soc-ln2440sbc-alc650-objs := ln2440sbc_alc650.o | ||
13 | 16 | ||
14 | obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o | 17 | obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o |
15 | obj-$(CONFIG_SND_S3C24XX_SOC_SMDK2443_WM9710) += snd-soc-smdk2443-wm9710.o | 18 | obj-$(CONFIG_SND_S3C24XX_SOC_SMDK2443_WM9710) += snd-soc-smdk2443-wm9710.o |
19 | obj-$(CONFIG_SND_S3C24XX_SOC_LN2440SBC_ALC650) += snd-soc-ln2440sbc-alc650.o | ||
diff --git a/sound/soc/s3c24xx/ln2440sbc_alc650.c b/sound/soc/s3c24xx/ln2440sbc_alc650.c new file mode 100644 index 000000000000..9ed8f2e8da10 --- /dev/null +++ b/sound/soc/s3c24xx/ln2440sbc_alc650.c | |||
@@ -0,0 +1,85 @@ | |||
1 | /* | ||
2 | * SoC audio for ln2440sbc | ||
3 | * | ||
4 | * Copyright 2007 KonekTel, a.s. | ||
5 | * Author: Ivan Kuten | ||
6 | * ivan.kuten@promwad.com | ||
7 | * | ||
8 | * Heavily based on smdk2443_wm9710.c | ||
9 | * Copyright 2007 Wolfson Microelectronics PLC. | ||
10 | * Author: Graeme Gregory | ||
11 | * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or modify | ||
14 | * it under the terms of the GNU General Public License version 2 as | ||
15 | * published by the Free Software Foundation. | ||
16 | * | ||
17 | */ | ||
18 | |||
19 | #include <linux/module.h> | ||
20 | #include <linux/device.h> | ||
21 | #include <sound/core.h> | ||
22 | #include <sound/pcm.h> | ||
23 | #include <sound/soc.h> | ||
24 | #include <sound/soc-dapm.h> | ||
25 | |||
26 | #include "../codecs/ac97.h" | ||
27 | #include "s3c24xx-pcm.h" | ||
28 | #include "s3c24xx-ac97.h" | ||
29 | |||
30 | static struct snd_soc_machine ln2440sbc; | ||
31 | |||
32 | static struct snd_soc_dai_link ln2440sbc_dai[] = { | ||
33 | { | ||
34 | .name = "AC97", | ||
35 | .stream_name = "AC97 HiFi", | ||
36 | .cpu_dai = &s3c2443_ac97_dai[0], | ||
37 | .codec_dai = &ac97_dai, | ||
38 | }, | ||
39 | }; | ||
40 | |||
41 | static struct snd_soc_machine ln2440sbc = { | ||
42 | .name = "LN2440SBC", | ||
43 | .dai_link = ln2440sbc_dai, | ||
44 | .num_links = ARRAY_SIZE(ln2440sbc_dai), | ||
45 | }; | ||
46 | |||
47 | static struct snd_soc_device ln2440sbc_snd_ac97_devdata = { | ||
48 | .machine = &ln2440sbc, | ||
49 | .platform = &s3c24xx_soc_platform, | ||
50 | .codec_dev = &soc_codec_dev_ac97, | ||
51 | }; | ||
52 | |||
53 | static struct platform_device *ln2440sbc_snd_ac97_device; | ||
54 | |||
55 | static int __init ln2440sbc_init(void) | ||
56 | { | ||
57 | int ret; | ||
58 | |||
59 | ln2440sbc_snd_ac97_device = platform_device_alloc("soc-audio", -1); | ||
60 | if (!ln2440sbc_snd_ac97_device) | ||
61 | return -ENOMEM; | ||
62 | |||
63 | platform_set_drvdata(ln2440sbc_snd_ac97_device, | ||
64 | &ln2440sbc_snd_ac97_devdata); | ||
65 | ln2440sbc_snd_ac97_devdata.dev = &ln2440sbc_snd_ac97_device->dev; | ||
66 | ret = platform_device_add(ln2440sbc_snd_ac97_device); | ||
67 | |||
68 | if (ret) | ||
69 | platform_device_put(ln2440sbc_snd_ac97_device); | ||
70 | |||
71 | return ret; | ||
72 | } | ||
73 | |||
74 | static void __exit ln2440sbc_exit(void) | ||
75 | { | ||
76 | platform_device_unregister(ln2440sbc_snd_ac97_device); | ||
77 | } | ||
78 | |||
79 | module_init(ln2440sbc_init); | ||
80 | module_exit(ln2440sbc_exit); | ||
81 | |||
82 | /* Module information */ | ||
83 | MODULE_AUTHOR("Ivan Kuten"); | ||
84 | MODULE_DESCRIPTION("ALSA SoC ALC650 LN2440SBC"); | ||
85 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/s3c24xx/neo1973_wm8753.c b/sound/soc/s3c24xx/neo1973_wm8753.c index f1f6b9478af9..6ee115ceb011 100644 --- a/sound/soc/s3c24xx/neo1973_wm8753.c +++ b/sound/soc/s3c24xx/neo1973_wm8753.c | |||
@@ -22,7 +22,6 @@ | |||
22 | #include <linux/interrupt.h> | 22 | #include <linux/interrupt.h> |
23 | #include <linux/platform_device.h> | 23 | #include <linux/platform_device.h> |
24 | #include <linux/i2c.h> | 24 | #include <linux/i2c.h> |
25 | #include <sound/driver.h> | ||
26 | #include <sound/core.h> | 25 | #include <sound/core.h> |
27 | #include <sound/pcm.h> | 26 | #include <sound/pcm.h> |
28 | #include <sound/soc.h> | 27 | #include <sound/soc.h> |
@@ -30,13 +29,15 @@ | |||
30 | 29 | ||
31 | #include <asm/mach-types.h> | 30 | #include <asm/mach-types.h> |
32 | #include <asm/hardware/scoop.h> | 31 | #include <asm/hardware/scoop.h> |
33 | #include <asm/arch/regs-iis.h> | ||
34 | #include <asm/arch/regs-clock.h> | 32 | #include <asm/arch/regs-clock.h> |
35 | #include <asm/arch/regs-gpio.h> | 33 | #include <asm/arch/regs-gpio.h> |
36 | #include <asm/hardware.h> | 34 | #include <asm/hardware.h> |
37 | #include <asm/arch/audio.h> | 35 | #include <asm/arch/audio.h> |
38 | #include <asm/io.h> | 36 | #include <asm/io.h> |
39 | #include <asm/arch/spi-gpio.h> | 37 | #include <asm/arch/spi-gpio.h> |
38 | |||
39 | #include <asm/plat-s3c24xx/regs-iis.h> | ||
40 | |||
40 | #include "../codecs/wm8753.h" | 41 | #include "../codecs/wm8753.h" |
41 | #include "lm4857.h" | 42 | #include "lm4857.h" |
42 | #include "s3c24xx-pcm.h" | 43 | #include "s3c24xx-pcm.h" |
diff --git a/sound/soc/s3c24xx/s3c2412-i2s.c b/sound/soc/s3c24xx/s3c2412-i2s.c new file mode 100644 index 000000000000..c4a46dd589b3 --- /dev/null +++ b/sound/soc/s3c24xx/s3c2412-i2s.c | |||
@@ -0,0 +1,744 @@ | |||
1 | /* sound/soc/s3c24xx/s3c2412-i2s.c | ||
2 | * | ||
3 | * ALSA Soc Audio Layer - S3C2412 I2S driver | ||
4 | * | ||
5 | * Copyright (c) 2006 Wolfson Microelectronics PLC. | ||
6 | * Graeme Gregory graeme.gregory@wolfsonmicro.com | ||
7 | * linux@wolfsonmicro.com | ||
8 | * | ||
9 | * Copyright (c) 2007, 2004-2005 Simtec Electronics | ||
10 | * http://armlinux.simtec.co.uk/ | ||
11 | * Ben Dooks <ben@simtec.co.uk> | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or modify it | ||
14 | * under the terms of the GNU General Public License as published by the | ||
15 | * Free Software Foundation; either version 2 of the License, or (at your | ||
16 | * option) any later version. | ||
17 | */ | ||
18 | |||
19 | #include <linux/init.h> | ||
20 | #include <linux/module.h> | ||
21 | #include <linux/device.h> | ||
22 | #include <linux/delay.h> | ||
23 | #include <linux/clk.h> | ||
24 | #include <linux/kernel.h> | ||
25 | |||
26 | #include <sound/core.h> | ||
27 | #include <sound/pcm.h> | ||
28 | #include <sound/pcm_params.h> | ||
29 | #include <sound/initval.h> | ||
30 | #include <sound/soc.h> | ||
31 | #include <asm/hardware.h> | ||
32 | |||
33 | #include <linux/io.h> | ||
34 | #include <asm/dma.h> | ||
35 | |||
36 | #include <asm/plat-s3c24xx/regs-s3c2412-iis.h> | ||
37 | |||
38 | #include <asm/arch/regs-gpio.h> | ||
39 | #include <asm/arch/audio.h> | ||
40 | #include <asm/arch/dma.h> | ||
41 | |||
42 | #include "s3c24xx-pcm.h" | ||
43 | #include "s3c2412-i2s.h" | ||
44 | |||
45 | #define S3C2412_I2S_DEBUG 0 | ||
46 | #define S3C2412_I2S_DEBUG_CON 0 | ||
47 | |||
48 | #if S3C2412_I2S_DEBUG | ||
49 | #define DBG(x...) printk(KERN_INFO x) | ||
50 | #else | ||
51 | #define DBG(x...) do { } while (0) | ||
52 | #endif | ||
53 | |||
54 | static struct s3c2410_dma_client s3c2412_dma_client_out = { | ||
55 | .name = "I2S PCM Stereo out" | ||
56 | }; | ||
57 | |||
58 | static struct s3c2410_dma_client s3c2412_dma_client_in = { | ||
59 | .name = "I2S PCM Stereo in" | ||
60 | }; | ||
61 | |||
62 | static struct s3c24xx_pcm_dma_params s3c2412_i2s_pcm_stereo_out = { | ||
63 | .client = &s3c2412_dma_client_out, | ||
64 | .channel = DMACH_I2S_OUT, | ||
65 | .dma_addr = S3C2410_PA_IIS + S3C2412_IISTXD, | ||
66 | .dma_size = 4, | ||
67 | }; | ||
68 | |||
69 | static struct s3c24xx_pcm_dma_params s3c2412_i2s_pcm_stereo_in = { | ||
70 | .client = &s3c2412_dma_client_in, | ||
71 | .channel = DMACH_I2S_IN, | ||
72 | .dma_addr = S3C2410_PA_IIS + S3C2412_IISRXD, | ||
73 | .dma_size = 4, | ||
74 | }; | ||
75 | |||
76 | struct s3c2412_i2s_info { | ||
77 | struct device *dev; | ||
78 | void __iomem *regs; | ||
79 | struct clk *iis_clk; | ||
80 | struct clk *iis_pclk; | ||
81 | struct clk *iis_cclk; | ||
82 | |||
83 | u32 suspend_iismod; | ||
84 | u32 suspend_iiscon; | ||
85 | u32 suspend_iispsr; | ||
86 | }; | ||
87 | |||
88 | static struct s3c2412_i2s_info s3c2412_i2s; | ||
89 | |||
90 | #define bit_set(v, b) (((v) & (b)) ? 1 : 0) | ||
91 | |||
92 | #if S3C2412_I2S_DEBUG_CON | ||
93 | static void dbg_showcon(const char *fn, u32 con) | ||
94 | { | ||
95 | printk(KERN_DEBUG "%s: LRI=%d, TXFEMPT=%d, RXFEMPT=%d, TXFFULL=%d, RXFFULL=%d\n", fn, | ||
96 | bit_set(con, S3C2412_IISCON_LRINDEX), | ||
97 | bit_set(con, S3C2412_IISCON_TXFIFO_EMPTY), | ||
98 | bit_set(con, S3C2412_IISCON_RXFIFO_EMPTY), | ||
99 | bit_set(con, S3C2412_IISCON_TXFIFO_FULL), | ||
100 | bit_set(con, S3C2412_IISCON_RXFIFO_FULL)); | ||
101 | |||
102 | printk(KERN_DEBUG "%s: PAUSE: TXDMA=%d, RXDMA=%d, TXCH=%d, RXCH=%d\n", | ||
103 | fn, | ||
104 | bit_set(con, S3C2412_IISCON_TXDMA_PAUSE), | ||
105 | bit_set(con, S3C2412_IISCON_RXDMA_PAUSE), | ||
106 | bit_set(con, S3C2412_IISCON_TXCH_PAUSE), | ||
107 | bit_set(con, S3C2412_IISCON_RXCH_PAUSE)); | ||
108 | printk(KERN_DEBUG "%s: ACTIVE: TXDMA=%d, RXDMA=%d, IIS=%d\n", fn, | ||
109 | bit_set(con, S3C2412_IISCON_TXDMA_ACTIVE), | ||
110 | bit_set(con, S3C2412_IISCON_RXDMA_ACTIVE), | ||
111 | bit_set(con, S3C2412_IISCON_IIS_ACTIVE)); | ||
112 | } | ||
113 | #else | ||
114 | static inline void dbg_showcon(const char *fn, u32 con) | ||
115 | { | ||
116 | } | ||
117 | #endif | ||
118 | |||
119 | /* Turn on or off the transmission path. */ | ||
120 | static void s3c2412_snd_txctrl(int on) | ||
121 | { | ||
122 | struct s3c2412_i2s_info *i2s = &s3c2412_i2s; | ||
123 | void __iomem *regs = i2s->regs; | ||
124 | u32 fic, con, mod; | ||
125 | |||
126 | DBG("%s(%d)\n", __func__, on); | ||
127 | |||
128 | fic = readl(regs + S3C2412_IISFIC); | ||
129 | con = readl(regs + S3C2412_IISCON); | ||
130 | mod = readl(regs + S3C2412_IISMOD); | ||
131 | |||
132 | DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic); | ||
133 | |||
134 | if (on) { | ||
135 | con |= S3C2412_IISCON_TXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE; | ||
136 | con &= ~S3C2412_IISCON_TXDMA_PAUSE; | ||
137 | con &= ~S3C2412_IISCON_TXCH_PAUSE; | ||
138 | |||
139 | switch (mod & S3C2412_IISMOD_MODE_MASK) { | ||
140 | case S3C2412_IISMOD_MODE_TXONLY: | ||
141 | case S3C2412_IISMOD_MODE_TXRX: | ||
142 | /* do nothing, we are in the right mode */ | ||
143 | break; | ||
144 | |||
145 | case S3C2412_IISMOD_MODE_RXONLY: | ||
146 | mod &= ~S3C2412_IISMOD_MODE_MASK; | ||
147 | mod |= S3C2412_IISMOD_MODE_TXRX; | ||
148 | break; | ||
149 | |||
150 | default: | ||
151 | dev_err(i2s->dev, "TXEN: Invalid MODE in IISMOD\n"); | ||
152 | } | ||
153 | |||
154 | writel(con, regs + S3C2412_IISCON); | ||
155 | writel(mod, regs + S3C2412_IISMOD); | ||
156 | } else { | ||
157 | /* Note, we do not have any indication that the FIFO problems | ||
158 | * tha the S3C2410/2440 had apply here, so we should be able | ||
159 | * to disable the DMA and TX without resetting the FIFOS. | ||
160 | */ | ||
161 | |||
162 | con |= S3C2412_IISCON_TXDMA_PAUSE; | ||
163 | con |= S3C2412_IISCON_TXCH_PAUSE; | ||
164 | con &= ~S3C2412_IISCON_TXDMA_ACTIVE; | ||
165 | |||
166 | switch (mod & S3C2412_IISMOD_MODE_MASK) { | ||
167 | case S3C2412_IISMOD_MODE_TXRX: | ||
168 | mod &= ~S3C2412_IISMOD_MODE_MASK; | ||
169 | mod |= S3C2412_IISMOD_MODE_RXONLY; | ||
170 | break; | ||
171 | |||
172 | case S3C2412_IISMOD_MODE_TXONLY: | ||
173 | mod &= ~S3C2412_IISMOD_MODE_MASK; | ||
174 | con &= ~S3C2412_IISCON_IIS_ACTIVE; | ||
175 | break; | ||
176 | |||
177 | default: | ||
178 | dev_err(i2s->dev, "TXDIS: Invalid MODE in IISMOD\n"); | ||
179 | } | ||
180 | |||
181 | writel(mod, regs + S3C2412_IISMOD); | ||
182 | writel(con, regs + S3C2412_IISCON); | ||
183 | } | ||
184 | |||
185 | fic = readl(regs + S3C2412_IISFIC); | ||
186 | dbg_showcon(__func__, con); | ||
187 | DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic); | ||
188 | } | ||
189 | |||
190 | static void s3c2412_snd_rxctrl(int on) | ||
191 | { | ||
192 | struct s3c2412_i2s_info *i2s = &s3c2412_i2s; | ||
193 | void __iomem *regs = i2s->regs; | ||
194 | u32 fic, con, mod; | ||
195 | |||
196 | DBG("%s(%d)\n", __func__, on); | ||
197 | |||
198 | fic = readl(regs + S3C2412_IISFIC); | ||
199 | con = readl(regs + S3C2412_IISCON); | ||
200 | mod = readl(regs + S3C2412_IISMOD); | ||
201 | |||
202 | DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic); | ||
203 | |||
204 | if (on) { | ||
205 | con |= S3C2412_IISCON_RXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE; | ||
206 | con &= ~S3C2412_IISCON_RXDMA_PAUSE; | ||
207 | con &= ~S3C2412_IISCON_RXCH_PAUSE; | ||
208 | |||
209 | switch (mod & S3C2412_IISMOD_MODE_MASK) { | ||
210 | case S3C2412_IISMOD_MODE_TXRX: | ||
211 | case S3C2412_IISMOD_MODE_RXONLY: | ||
212 | /* do nothing, we are in the right mode */ | ||
213 | break; | ||
214 | |||
215 | case S3C2412_IISMOD_MODE_TXONLY: | ||
216 | mod &= ~S3C2412_IISMOD_MODE_MASK; | ||
217 | mod |= S3C2412_IISMOD_MODE_TXRX; | ||
218 | break; | ||
219 | |||
220 | default: | ||
221 | dev_err(i2s->dev, "RXEN: Invalid MODE in IISMOD\n"); | ||
222 | } | ||
223 | |||
224 | writel(mod, regs + S3C2412_IISMOD); | ||
225 | writel(con, regs + S3C2412_IISCON); | ||
226 | } else { | ||
227 | /* See txctrl notes on FIFOs. */ | ||
228 | |||
229 | con &= ~S3C2412_IISCON_RXDMA_ACTIVE; | ||
230 | con |= S3C2412_IISCON_RXDMA_PAUSE; | ||
231 | con |= S3C2412_IISCON_RXCH_PAUSE; | ||
232 | |||
233 | switch (mod & S3C2412_IISMOD_MODE_MASK) { | ||
234 | case S3C2412_IISMOD_MODE_RXONLY: | ||
235 | con &= ~S3C2412_IISCON_IIS_ACTIVE; | ||
236 | mod &= ~S3C2412_IISMOD_MODE_MASK; | ||
237 | break; | ||
238 | |||
239 | case S3C2412_IISMOD_MODE_TXRX: | ||
240 | mod &= ~S3C2412_IISMOD_MODE_MASK; | ||
241 | mod |= S3C2412_IISMOD_MODE_TXONLY; | ||
242 | break; | ||
243 | |||
244 | default: | ||
245 | dev_err(i2s->dev, "RXEN: Invalid MODE in IISMOD\n"); | ||
246 | } | ||
247 | |||
248 | writel(con, regs + S3C2412_IISCON); | ||
249 | writel(mod, regs + S3C2412_IISMOD); | ||
250 | } | ||
251 | |||
252 | fic = readl(regs + S3C2412_IISFIC); | ||
253 | DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic); | ||
254 | } | ||
255 | |||
256 | |||
257 | /* | ||
258 | * Wait for the LR signal to allow synchronisation to the L/R clock | ||
259 | * from the codec. May only be needed for slave mode. | ||
260 | */ | ||
261 | static int s3c2412_snd_lrsync(void) | ||
262 | { | ||
263 | u32 iiscon; | ||
264 | unsigned long timeout = jiffies + msecs_to_jiffies(5); | ||
265 | |||
266 | DBG("Entered %s\n", __func__); | ||
267 | |||
268 | while (1) { | ||
269 | iiscon = readl(s3c2412_i2s.regs + S3C2412_IISCON); | ||
270 | if (iiscon & S3C2412_IISCON_LRINDEX) | ||
271 | break; | ||
272 | |||
273 | if (timeout < jiffies) { | ||
274 | printk(KERN_ERR "%s: timeout\n", __func__); | ||
275 | return -ETIMEDOUT; | ||
276 | } | ||
277 | } | ||
278 | |||
279 | return 0; | ||
280 | } | ||
281 | |||
282 | /* | ||
283 | * Check whether CPU is the master or slave | ||
284 | */ | ||
285 | static inline int s3c2412_snd_is_clkmaster(void) | ||
286 | { | ||
287 | u32 iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD); | ||
288 | |||
289 | DBG("Entered %s\n", __func__); | ||
290 | |||
291 | iismod &= S3C2412_IISMOD_MASTER_MASK; | ||
292 | return !(iismod == S3C2412_IISMOD_SLAVE); | ||
293 | } | ||
294 | |||
295 | /* | ||
296 | * Set S3C2412 I2S DAI format | ||
297 | */ | ||
298 | static int s3c2412_i2s_set_fmt(struct snd_soc_cpu_dai *cpu_dai, | ||
299 | unsigned int fmt) | ||
300 | { | ||
301 | u32 iismod; | ||
302 | |||
303 | |||
304 | DBG("Entered %s\n", __func__); | ||
305 | |||
306 | iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD); | ||
307 | DBG("hw_params r: IISMOD: %x \n", iismod); | ||
308 | |||
309 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
310 | case SND_SOC_DAIFMT_CBM_CFM: | ||
311 | iismod &= ~S3C2412_IISMOD_MASTER_MASK; | ||
312 | iismod |= S3C2412_IISMOD_SLAVE; | ||
313 | break; | ||
314 | case SND_SOC_DAIFMT_CBS_CFS: | ||
315 | iismod &= ~S3C2412_IISMOD_MASTER_MASK; | ||
316 | iismod |= S3C2412_IISMOD_MASTER_INTERNAL; | ||
317 | break; | ||
318 | default: | ||
319 | DBG("unknwon master/slave format\n"); | ||
320 | return -EINVAL; | ||
321 | } | ||
322 | |||
323 | iismod &= ~S3C2412_IISMOD_SDF_MASK; | ||
324 | |||
325 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
326 | case SND_SOC_DAIFMT_RIGHT_J: | ||
327 | iismod |= S3C2412_IISMOD_SDF_MSB; | ||
328 | break; | ||
329 | case SND_SOC_DAIFMT_LEFT_J: | ||
330 | iismod |= S3C2412_IISMOD_SDF_LSB; | ||
331 | break; | ||
332 | case SND_SOC_DAIFMT_I2S: | ||
333 | iismod |= S3C2412_IISMOD_SDF_IIS; | ||
334 | break; | ||
335 | default: | ||
336 | DBG("Unknown data format\n"); | ||
337 | return -EINVAL; | ||
338 | } | ||
339 | |||
340 | writel(iismod, s3c2412_i2s.regs + S3C2412_IISMOD); | ||
341 | DBG("hw_params w: IISMOD: %x \n", iismod); | ||
342 | return 0; | ||
343 | } | ||
344 | |||
345 | static int s3c2412_i2s_hw_params(struct snd_pcm_substream *substream, | ||
346 | struct snd_pcm_hw_params *params) | ||
347 | { | ||
348 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
349 | u32 iismod; | ||
350 | |||
351 | DBG("Entered %s\n", __func__); | ||
352 | |||
353 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
354 | rtd->dai->cpu_dai->dma_data = &s3c2412_i2s_pcm_stereo_out; | ||
355 | else | ||
356 | rtd->dai->cpu_dai->dma_data = &s3c2412_i2s_pcm_stereo_in; | ||
357 | |||
358 | /* Working copies of register */ | ||
359 | iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD); | ||
360 | DBG("%s: r: IISMOD: %x\n", __func__, iismod); | ||
361 | |||
362 | switch (params_format(params)) { | ||
363 | case SNDRV_PCM_FORMAT_S8: | ||
364 | iismod |= S3C2412_IISMOD_8BIT; | ||
365 | break; | ||
366 | case SNDRV_PCM_FORMAT_S16_LE: | ||
367 | iismod &= ~S3C2412_IISMOD_8BIT; | ||
368 | break; | ||
369 | } | ||
370 | |||
371 | writel(iismod, s3c2412_i2s.regs + S3C2412_IISMOD); | ||
372 | DBG("%s: w: IISMOD: %x\n", __func__, iismod); | ||
373 | return 0; | ||
374 | } | ||
375 | |||
376 | static int s3c2412_i2s_trigger(struct snd_pcm_substream *substream, int cmd) | ||
377 | { | ||
378 | int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); | ||
379 | unsigned long irqs; | ||
380 | int ret = 0; | ||
381 | |||
382 | DBG("Entered %s\n", __func__); | ||
383 | |||
384 | switch (cmd) { | ||
385 | case SNDRV_PCM_TRIGGER_START: | ||
386 | /* On start, ensure that the FIFOs are cleared and reset. */ | ||
387 | |||
388 | writel(capture ? S3C2412_IISFIC_RXFLUSH : S3C2412_IISFIC_TXFLUSH, | ||
389 | s3c2412_i2s.regs + S3C2412_IISFIC); | ||
390 | |||
391 | /* clear again, just in case */ | ||
392 | writel(0x0, s3c2412_i2s.regs + S3C2412_IISFIC); | ||
393 | |||
394 | case SNDRV_PCM_TRIGGER_RESUME: | ||
395 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
396 | if (!s3c2412_snd_is_clkmaster()) { | ||
397 | ret = s3c2412_snd_lrsync(); | ||
398 | if (ret) | ||
399 | goto exit_err; | ||
400 | } | ||
401 | |||
402 | local_irq_save(irqs); | ||
403 | |||
404 | if (capture) | ||
405 | s3c2412_snd_rxctrl(1); | ||
406 | else | ||
407 | s3c2412_snd_txctrl(1); | ||
408 | |||
409 | local_irq_restore(irqs); | ||
410 | break; | ||
411 | |||
412 | case SNDRV_PCM_TRIGGER_STOP: | ||
413 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
414 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
415 | local_irq_save(irqs); | ||
416 | |||
417 | if (capture) | ||
418 | s3c2412_snd_rxctrl(0); | ||
419 | else | ||
420 | s3c2412_snd_txctrl(0); | ||
421 | |||
422 | local_irq_restore(irqs); | ||
423 | break; | ||
424 | default: | ||
425 | ret = -EINVAL; | ||
426 | break; | ||
427 | } | ||
428 | |||
429 | exit_err: | ||
430 | return ret; | ||
431 | } | ||
432 | |||
433 | /* default table of all avaialable root fs divisors */ | ||
434 | static unsigned int s3c2412_iis_fs[] = { 256, 512, 384, 768, 0 }; | ||
435 | |||
436 | int s3c2412_iis_calc_rate(struct s3c2412_rate_calc *info, | ||
437 | unsigned int *fstab, | ||
438 | unsigned int rate, struct clk *clk) | ||
439 | { | ||
440 | unsigned long clkrate = clk_get_rate(clk); | ||
441 | unsigned int div; | ||
442 | unsigned int fsclk; | ||
443 | unsigned int actual; | ||
444 | unsigned int fs; | ||
445 | unsigned int fsdiv; | ||
446 | signed int deviation = 0; | ||
447 | unsigned int best_fs = 0; | ||
448 | unsigned int best_div = 0; | ||
449 | unsigned int best_rate = 0; | ||
450 | unsigned int best_deviation = INT_MAX; | ||
451 | |||
452 | |||
453 | if (fstab == NULL) | ||
454 | fstab = s3c2412_iis_fs; | ||
455 | |||
456 | for (fs = 0;; fs++) { | ||
457 | fsdiv = s3c2412_iis_fs[fs]; | ||
458 | |||
459 | if (fsdiv == 0) | ||
460 | break; | ||
461 | |||
462 | fsclk = clkrate / fsdiv; | ||
463 | div = fsclk / rate; | ||
464 | |||
465 | if ((fsclk % rate) > (rate / 2)) | ||
466 | div++; | ||
467 | |||
468 | if (div <= 1) | ||
469 | continue; | ||
470 | |||
471 | actual = clkrate / (fsdiv * div); | ||
472 | deviation = actual - rate; | ||
473 | |||
474 | printk(KERN_DEBUG "%dfs: div %d => result %d, deviation %d\n", | ||
475 | fsdiv, div, actual, deviation); | ||
476 | |||
477 | deviation = abs(deviation); | ||
478 | |||
479 | if (deviation < best_deviation) { | ||
480 | best_fs = fsdiv; | ||
481 | best_div = div; | ||
482 | best_rate = actual; | ||
483 | best_deviation = deviation; | ||
484 | } | ||
485 | |||
486 | if (deviation == 0) | ||
487 | break; | ||
488 | } | ||
489 | |||
490 | printk(KERN_DEBUG "best: fs=%d, div=%d, rate=%d\n", | ||
491 | best_fs, best_div, best_rate); | ||
492 | |||
493 | info->fs_div = best_fs; | ||
494 | info->clk_div = best_div; | ||
495 | |||
496 | return 0; | ||
497 | } | ||
498 | EXPORT_SYMBOL_GPL(s3c2412_iis_calc_rate); | ||
499 | |||
500 | /* | ||
501 | * Set S3C2412 Clock source | ||
502 | */ | ||
503 | static int s3c2412_i2s_set_sysclk(struct snd_soc_cpu_dai *cpu_dai, | ||
504 | int clk_id, unsigned int freq, int dir) | ||
505 | { | ||
506 | u32 iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD); | ||
507 | |||
508 | DBG("%s(%p, %d, %u, %d)\n", __func__, cpu_dai, clk_id, | ||
509 | freq, dir); | ||
510 | |||
511 | switch (clk_id) { | ||
512 | case S3C2412_CLKSRC_PCLK: | ||
513 | iismod &= ~S3C2412_IISMOD_MASTER_MASK; | ||
514 | iismod |= S3C2412_IISMOD_MASTER_INTERNAL; | ||
515 | break; | ||
516 | case S3C2412_CLKSRC_I2SCLK: | ||
517 | iismod &= ~S3C2412_IISMOD_MASTER_MASK; | ||
518 | iismod |= S3C2412_IISMOD_MASTER_EXTERNAL; | ||
519 | break; | ||
520 | default: | ||
521 | return -EINVAL; | ||
522 | } | ||
523 | |||
524 | writel(iismod, s3c2412_i2s.regs + S3C2412_IISMOD); | ||
525 | return 0; | ||
526 | } | ||
527 | |||
528 | /* | ||
529 | * Set S3C2412 Clock dividers | ||
530 | */ | ||
531 | static int s3c2412_i2s_set_clkdiv(struct snd_soc_cpu_dai *cpu_dai, | ||
532 | int div_id, int div) | ||
533 | { | ||
534 | struct s3c2412_i2s_info *i2s = &s3c2412_i2s; | ||
535 | u32 reg; | ||
536 | |||
537 | DBG("%s(%p, %d, %d)\n", __func__, cpu_dai, div_id, div); | ||
538 | |||
539 | switch (div_id) { | ||
540 | case S3C2412_DIV_BCLK: | ||
541 | reg = readl(i2s->regs + S3C2412_IISMOD); | ||
542 | reg &= ~S3C2412_IISMOD_BCLK_MASK; | ||
543 | writel(reg | div, i2s->regs + S3C2412_IISMOD); | ||
544 | |||
545 | DBG("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD)); | ||
546 | break; | ||
547 | |||
548 | case S3C2412_DIV_RCLK: | ||
549 | if (div > 3) { | ||
550 | /* convert value to bit field */ | ||
551 | |||
552 | switch (div) { | ||
553 | case 256: | ||
554 | div = S3C2412_IISMOD_RCLK_256FS; | ||
555 | break; | ||
556 | |||
557 | case 384: | ||
558 | div = S3C2412_IISMOD_RCLK_384FS; | ||
559 | break; | ||
560 | |||
561 | case 512: | ||
562 | div = S3C2412_IISMOD_RCLK_512FS; | ||
563 | break; | ||
564 | |||
565 | case 768: | ||
566 | div = S3C2412_IISMOD_RCLK_768FS; | ||
567 | break; | ||
568 | |||
569 | default: | ||
570 | return -EINVAL; | ||
571 | } | ||
572 | } | ||
573 | |||
574 | reg = readl(s3c2412_i2s.regs + S3C2412_IISMOD); | ||
575 | reg &= ~S3C2412_IISMOD_RCLK_MASK; | ||
576 | writel(reg | div, i2s->regs + S3C2412_IISMOD); | ||
577 | DBG("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD)); | ||
578 | break; | ||
579 | |||
580 | case S3C2412_DIV_PRESCALER: | ||
581 | if (div >= 0) { | ||
582 | writel((div << 8) | S3C2412_IISPSR_PSREN, | ||
583 | i2s->regs + S3C2412_IISPSR); | ||
584 | } else { | ||
585 | writel(0x0, i2s->regs + S3C2412_IISPSR); | ||
586 | } | ||
587 | DBG("%s: PSR=%08x\n", __func__, readl(i2s->regs + S3C2412_IISPSR)); | ||
588 | break; | ||
589 | |||
590 | default: | ||
591 | return -EINVAL; | ||
592 | } | ||
593 | |||
594 | return 0; | ||
595 | } | ||
596 | |||
597 | struct clk *s3c2412_get_iisclk(void) | ||
598 | { | ||
599 | return s3c2412_i2s.iis_clk; | ||
600 | } | ||
601 | EXPORT_SYMBOL_GPL(s3c2412_get_iisclk); | ||
602 | |||
603 | |||
604 | static int s3c2412_i2s_probe(struct platform_device *pdev) | ||
605 | { | ||
606 | DBG("Entered %s\n", __func__); | ||
607 | |||
608 | s3c2412_i2s.dev = &pdev->dev; | ||
609 | |||
610 | s3c2412_i2s.regs = ioremap(S3C2410_PA_IIS, 0x100); | ||
611 | if (s3c2412_i2s.regs == NULL) | ||
612 | return -ENXIO; | ||
613 | |||
614 | s3c2412_i2s.iis_pclk = clk_get(&pdev->dev, "iis"); | ||
615 | if (s3c2412_i2s.iis_pclk == NULL) { | ||
616 | DBG("failed to get iis_clock\n"); | ||
617 | iounmap(s3c2412_i2s.regs); | ||
618 | return -ENODEV; | ||
619 | } | ||
620 | |||
621 | s3c2412_i2s.iis_cclk = clk_get(&pdev->dev, "i2sclk"); | ||
622 | if (s3c2412_i2s.iis_cclk == NULL) { | ||
623 | DBG("failed to get i2sclk clock\n"); | ||
624 | iounmap(s3c2412_i2s.regs); | ||
625 | return -ENODEV; | ||
626 | } | ||
627 | |||
628 | clk_set_parent(s3c2412_i2s.iis_cclk, clk_get(NULL, "mpll")); | ||
629 | |||
630 | clk_enable(s3c2412_i2s.iis_pclk); | ||
631 | clk_enable(s3c2412_i2s.iis_cclk); | ||
632 | |||
633 | s3c2412_i2s.iis_clk = s3c2412_i2s.iis_pclk; | ||
634 | |||
635 | /* Configure the I2S pins in correct mode */ | ||
636 | s3c2410_gpio_cfgpin(S3C2410_GPE0, S3C2410_GPE0_I2SLRCK); | ||
637 | s3c2410_gpio_cfgpin(S3C2410_GPE1, S3C2410_GPE1_I2SSCLK); | ||
638 | s3c2410_gpio_cfgpin(S3C2410_GPE2, S3C2410_GPE2_CDCLK); | ||
639 | s3c2410_gpio_cfgpin(S3C2410_GPE3, S3C2410_GPE3_I2SSDI); | ||
640 | s3c2410_gpio_cfgpin(S3C2410_GPE4, S3C2410_GPE4_I2SSDO); | ||
641 | |||
642 | s3c2412_snd_txctrl(0); | ||
643 | s3c2412_snd_rxctrl(0); | ||
644 | |||
645 | return 0; | ||
646 | } | ||
647 | |||
648 | #ifdef CONFIG_PM | ||
649 | static int s3c2412_i2s_suspend(struct platform_device *dev, | ||
650 | struct snd_soc_cpu_dai *dai) | ||
651 | { | ||
652 | struct s3c2412_i2s_info *i2s = &s3c2412_i2s; | ||
653 | u32 iismod; | ||
654 | |||
655 | if (dai->active) { | ||
656 | i2s->suspend_iismod = readl(i2s->regs + S3C2412_IISMOD); | ||
657 | i2s->suspend_iiscon = readl(i2s->regs + S3C2412_IISCON); | ||
658 | i2s->suspend_iispsr = readl(i2s->regs + S3C2412_IISPSR); | ||
659 | |||
660 | /* some basic suspend checks */ | ||
661 | |||
662 | iismod = readl(i2s->regs + S3C2412_IISMOD); | ||
663 | |||
664 | if (iismod & S3C2412_IISCON_RXDMA_ACTIVE) | ||
665 | dev_warn(&dev->dev, "%s: RXDMA active?\n", __func__); | ||
666 | |||
667 | if (iismod & S3C2412_IISCON_TXDMA_ACTIVE) | ||
668 | dev_warn(&dev->dev, "%s: TXDMA active?\n", __func__); | ||
669 | |||
670 | if (iismod & S3C2412_IISCON_IIS_ACTIVE) | ||
671 | dev_warn(&dev->dev, "%s: IIS active\n", __func__); | ||
672 | } | ||
673 | |||
674 | return 0; | ||
675 | } | ||
676 | |||
677 | static int s3c2412_i2s_resume(struct platform_device *pdev, | ||
678 | struct snd_soc_cpu_dai *dai) | ||
679 | { | ||
680 | struct s3c2412_i2s_info *i2s = &s3c2412_i2s; | ||
681 | |||
682 | dev_info(&pdev->dev, "dai_active %d, IISMOD %08x, IISCON %08x\n", | ||
683 | dai->active, i2s->suspend_iismod, i2s->suspend_iiscon); | ||
684 | |||
685 | if (dai->active) { | ||
686 | writel(i2s->suspend_iiscon, i2s->regs + S3C2412_IISCON); | ||
687 | writel(i2s->suspend_iismod, i2s->regs + S3C2412_IISMOD); | ||
688 | writel(i2s->suspend_iispsr, i2s->regs + S3C2412_IISPSR); | ||
689 | |||
690 | writel(S3C2412_IISFIC_RXFLUSH | S3C2412_IISFIC_TXFLUSH, | ||
691 | i2s->regs + S3C2412_IISFIC); | ||
692 | |||
693 | ndelay(250); | ||
694 | writel(0x0, i2s->regs + S3C2412_IISFIC); | ||
695 | |||
696 | } | ||
697 | |||
698 | return 0; | ||
699 | } | ||
700 | #else | ||
701 | #define s3c2412_i2s_suspend NULL | ||
702 | #define s3c2412_i2s_resume NULL | ||
703 | #endif /* CONFIG_PM */ | ||
704 | |||
705 | #define S3C2412_I2S_RATES \ | ||
706 | (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \ | ||
707 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ | ||
708 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) | ||
709 | |||
710 | struct snd_soc_cpu_dai s3c2412_i2s_dai = { | ||
711 | .name = "s3c2412-i2s", | ||
712 | .id = 0, | ||
713 | .type = SND_SOC_DAI_I2S, | ||
714 | .probe = s3c2412_i2s_probe, | ||
715 | .suspend = s3c2412_i2s_suspend, | ||
716 | .resume = s3c2412_i2s_resume, | ||
717 | .playback = { | ||
718 | .channels_min = 2, | ||
719 | .channels_max = 2, | ||
720 | .rates = S3C2412_I2S_RATES, | ||
721 | .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE, | ||
722 | }, | ||
723 | .capture = { | ||
724 | .channels_min = 2, | ||
725 | .channels_max = 2, | ||
726 | .rates = S3C2412_I2S_RATES, | ||
727 | .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE, | ||
728 | }, | ||
729 | .ops = { | ||
730 | .trigger = s3c2412_i2s_trigger, | ||
731 | .hw_params = s3c2412_i2s_hw_params, | ||
732 | }, | ||
733 | .dai_ops = { | ||
734 | .set_fmt = s3c2412_i2s_set_fmt, | ||
735 | .set_clkdiv = s3c2412_i2s_set_clkdiv, | ||
736 | .set_sysclk = s3c2412_i2s_set_sysclk, | ||
737 | }, | ||
738 | }; | ||
739 | EXPORT_SYMBOL_GPL(s3c2412_i2s_dai); | ||
740 | |||
741 | /* Module information */ | ||
742 | MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>"); | ||
743 | MODULE_DESCRIPTION("S3C2412 I2S SoC Interface"); | ||
744 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/s3c24xx/s3c2412-i2s.h b/sound/soc/s3c24xx/s3c2412-i2s.h new file mode 100644 index 000000000000..27f48e1ffa86 --- /dev/null +++ b/sound/soc/s3c24xx/s3c2412-i2s.h | |||
@@ -0,0 +1,38 @@ | |||
1 | /* sound/soc/s3c24xx/s3c2412-i2s.c | ||
2 | * | ||
3 | * ALSA Soc Audio Layer - S3C2412 I2S driver | ||
4 | * | ||
5 | * Copyright (c) 2007 Simtec Electronics | ||
6 | * http://armlinux.simtec.co.uk/ | ||
7 | * Ben Dooks <ben@simtec.co.uk> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify it | ||
10 | * under the terms of the GNU General Public License as published by the | ||
11 | * Free Software Foundation; either version 2 of the License, or (at your | ||
12 | * option) any later version. | ||
13 | */ | ||
14 | |||
15 | #ifndef __SND_SOC_S3C24XX_S3C2412_I2S_H | ||
16 | #define __SND_SOC_S3C24XX_S3C2412_I2S_H __FILE__ | ||
17 | |||
18 | #define S3C2412_DIV_BCLK (1) | ||
19 | #define S3C2412_DIV_RCLK (2) | ||
20 | #define S3C2412_DIV_PRESCALER (3) | ||
21 | |||
22 | #define S3C2412_CLKSRC_PCLK (0) | ||
23 | #define S3C2412_CLKSRC_I2SCLK (1) | ||
24 | |||
25 | extern struct clk *s3c2412_get_iisclk(void); | ||
26 | |||
27 | extern struct snd_soc_cpu_dai s3c2412_i2s_dai; | ||
28 | |||
29 | struct s3c2412_rate_calc { | ||
30 | unsigned int clk_div; /* for prescaler */ | ||
31 | unsigned int fs_div; /* for root frame clock */ | ||
32 | }; | ||
33 | |||
34 | extern int s3c2412_iis_calc_rate(struct s3c2412_rate_calc *info, | ||
35 | unsigned int *fstab, | ||
36 | unsigned int rate, struct clk *clk); | ||
37 | |||
38 | #endif /* __SND_SOC_S3C24XX_S3C2412_I2S_H */ | ||
diff --git a/sound/soc/s3c24xx/s3c2443-ac97.c b/sound/soc/s3c24xx/s3c2443-ac97.c index 758a2637e7ac..1c1ddbf7f3c0 100644 --- a/sound/soc/s3c24xx/s3c2443-ac97.c +++ b/sound/soc/s3c24xx/s3c2443-ac97.c | |||
@@ -23,7 +23,6 @@ | |||
23 | #include <linux/delay.h> | 23 | #include <linux/delay.h> |
24 | #include <linux/clk.h> | 24 | #include <linux/clk.h> |
25 | 25 | ||
26 | #include <sound/driver.h> | ||
27 | #include <sound/core.h> | 26 | #include <sound/core.h> |
28 | #include <sound/pcm.h> | 27 | #include <sound/pcm.h> |
29 | #include <sound/ac97_codec.h> | 28 | #include <sound/ac97_codec.h> |
@@ -253,7 +252,7 @@ static int s3c2443_ac97_probe(struct platform_device *pdev) | |||
253 | ac_glbctrl |= S3C_AC97_GLBCTRL_TRANSFERDATAENABLE; | 252 | ac_glbctrl |= S3C_AC97_GLBCTRL_TRANSFERDATAENABLE; |
254 | writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL); | 253 | writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL); |
255 | 254 | ||
256 | ret = request_irq(IRQ_S3C2443_AC97, s3c2443_ac97_irq, | 255 | ret = request_irq(IRQ_S3C244x_AC97, s3c2443_ac97_irq, |
257 | IRQF_DISABLED, "AC97", NULL); | 256 | IRQF_DISABLED, "AC97", NULL); |
258 | if (ret < 0) { | 257 | if (ret < 0) { |
259 | printk(KERN_ERR "s3c24xx-ac97: interrupt request failed.\n"); | 258 | printk(KERN_ERR "s3c24xx-ac97: interrupt request failed.\n"); |
@@ -266,7 +265,7 @@ static int s3c2443_ac97_probe(struct platform_device *pdev) | |||
266 | 265 | ||
267 | static void s3c2443_ac97_remove(struct platform_device *pdev) | 266 | static void s3c2443_ac97_remove(struct platform_device *pdev) |
268 | { | 267 | { |
269 | free_irq(IRQ_S3C2443_AC97, NULL); | 268 | free_irq(IRQ_S3C244x_AC97, NULL); |
270 | clk_disable(s3c24xx_ac97.ac97_clk); | 269 | clk_disable(s3c24xx_ac97.ac97_clk); |
271 | clk_put(s3c24xx_ac97.ac97_clk); | 270 | clk_put(s3c24xx_ac97.ac97_clk); |
272 | iounmap(s3c24xx_ac97.regs); | 271 | iounmap(s3c24xx_ac97.regs); |
diff --git a/sound/soc/s3c24xx/s3c24xx-ac97.h b/sound/soc/s3c24xx/s3c24xx-ac97.h index 2b835e8260fa..bf03e8ed16c3 100644 --- a/sound/soc/s3c24xx/s3c24xx-ac97.h +++ b/sound/soc/s3c24xx/s3c24xx-ac97.h | |||
@@ -20,6 +20,12 @@ | |||
20 | #define AC_CMD_ADDR(x) (x << 16) | 20 | #define AC_CMD_ADDR(x) (x << 16) |
21 | #define AC_CMD_DATA(x) (x & 0xffff) | 21 | #define AC_CMD_DATA(x) (x & 0xffff) |
22 | 22 | ||
23 | #ifdef CONFIG_CPU_S3C2440 | ||
24 | #define IRQ_S3C244x_AC97 IRQ_S3C2440_AC97 | ||
25 | #else | ||
26 | #define IRQ_S3C244x_AC97 IRQ_S3C2443_AC97 | ||
27 | #endif | ||
28 | |||
23 | extern struct snd_soc_cpu_dai s3c2443_ac97_dai[]; | 29 | extern struct snd_soc_cpu_dai s3c2443_ac97_dai[]; |
24 | 30 | ||
25 | #endif /*S3C24XXAC97_H_*/ | 31 | #endif /*S3C24XXAC97_H_*/ |
diff --git a/sound/soc/s3c24xx/s3c24xx-i2s.c b/sound/soc/s3c24xx/s3c24xx-i2s.c index cd89c4105fcd..0a3c630951be 100644 --- a/sound/soc/s3c24xx/s3c24xx-i2s.c +++ b/sound/soc/s3c24xx/s3c24xx-i2s.c | |||
@@ -24,7 +24,7 @@ | |||
24 | #include <linux/device.h> | 24 | #include <linux/device.h> |
25 | #include <linux/delay.h> | 25 | #include <linux/delay.h> |
26 | #include <linux/clk.h> | 26 | #include <linux/clk.h> |
27 | #include <sound/driver.h> | 27 | #include <linux/jiffies.h> |
28 | #include <sound/core.h> | 28 | #include <sound/core.h> |
29 | #include <sound/pcm.h> | 29 | #include <sound/pcm.h> |
30 | #include <sound/pcm_params.h> | 30 | #include <sound/pcm_params.h> |
@@ -33,13 +33,14 @@ | |||
33 | 33 | ||
34 | #include <asm/hardware.h> | 34 | #include <asm/hardware.h> |
35 | #include <asm/io.h> | 35 | #include <asm/io.h> |
36 | #include <asm/arch/regs-iis.h> | ||
37 | #include <asm/arch/regs-gpio.h> | 36 | #include <asm/arch/regs-gpio.h> |
38 | #include <asm/arch/regs-clock.h> | 37 | #include <asm/arch/regs-clock.h> |
39 | #include <asm/arch/audio.h> | 38 | #include <asm/arch/audio.h> |
40 | #include <asm/dma.h> | 39 | #include <asm/dma.h> |
41 | #include <asm/arch/dma.h> | 40 | #include <asm/arch/dma.h> |
42 | 41 | ||
42 | #include <asm/plat-s3c24xx/regs-iis.h> | ||
43 | |||
43 | #include "s3c24xx-pcm.h" | 44 | #include "s3c24xx-pcm.h" |
44 | #include "s3c24xx-i2s.h" | 45 | #include "s3c24xx-i2s.h" |
45 | 46 | ||
@@ -75,6 +76,10 @@ static struct s3c24xx_pcm_dma_params s3c24xx_i2s_pcm_stereo_in = { | |||
75 | struct s3c24xx_i2s_info { | 76 | struct s3c24xx_i2s_info { |
76 | void __iomem *regs; | 77 | void __iomem *regs; |
77 | struct clk *iis_clk; | 78 | struct clk *iis_clk; |
79 | u32 iiscon; | ||
80 | u32 iismod; | ||
81 | u32 iisfcon; | ||
82 | u32 iispsr; | ||
78 | }; | 83 | }; |
79 | static struct s3c24xx_i2s_info s3c24xx_i2s; | 84 | static struct s3c24xx_i2s_info s3c24xx_i2s; |
80 | 85 | ||
@@ -184,7 +189,7 @@ static int s3c24xx_snd_lrsync(void) | |||
184 | if (iiscon & S3C2410_IISCON_LRINDEX) | 189 | if (iiscon & S3C2410_IISCON_LRINDEX) |
185 | break; | 190 | break; |
186 | 191 | ||
187 | if (timeout < jiffies) | 192 | if (time_after(jiffies, timeout)) |
188 | return -ETIMEDOUT; | 193 | return -ETIMEDOUT; |
189 | } | 194 | } |
190 | 195 | ||
@@ -405,6 +410,38 @@ static int s3c24xx_i2s_probe(struct platform_device *pdev) | |||
405 | return 0; | 410 | return 0; |
406 | } | 411 | } |
407 | 412 | ||
413 | #ifdef CONFIG_PM | ||
414 | int s3c24xx_i2s_suspend(struct platform_device *pdev, | ||
415 | struct snd_soc_cpu_dai *cpu_dai) | ||
416 | { | ||
417 | s3c24xx_i2s.iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON); | ||
418 | s3c24xx_i2s.iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD); | ||
419 | s3c24xx_i2s.iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON); | ||
420 | s3c24xx_i2s.iispsr = readl(s3c24xx_i2s.regs + S3C2410_IISPSR); | ||
421 | |||
422 | clk_disable(s3c24xx_i2s.iis_clk); | ||
423 | |||
424 | return 0; | ||
425 | } | ||
426 | |||
427 | int s3c24xx_i2s_resume(struct platform_device *pdev, | ||
428 | struct snd_soc_cpu_dai *cpu_dai) | ||
429 | { | ||
430 | clk_enable(s3c24xx_i2s.iis_clk); | ||
431 | |||
432 | writel(s3c24xx_i2s.iiscon, s3c24xx_i2s.regs + S3C2410_IISCON); | ||
433 | writel(s3c24xx_i2s.iismod, s3c24xx_i2s.regs + S3C2410_IISMOD); | ||
434 | writel(s3c24xx_i2s.iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON); | ||
435 | writel(s3c24xx_i2s.iispsr, s3c24xx_i2s.regs + S3C2410_IISPSR); | ||
436 | |||
437 | return 0; | ||
438 | } | ||
439 | #else | ||
440 | #define s3c24xx_i2s_suspend NULL | ||
441 | #define s3c24xx_i2s_resume NULL | ||
442 | #endif | ||
443 | |||
444 | |||
408 | #define S3C24XX_I2S_RATES \ | 445 | #define S3C24XX_I2S_RATES \ |
409 | (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \ | 446 | (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \ |
410 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ | 447 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ |
@@ -415,6 +452,8 @@ struct snd_soc_cpu_dai s3c24xx_i2s_dai = { | |||
415 | .id = 0, | 452 | .id = 0, |
416 | .type = SND_SOC_DAI_I2S, | 453 | .type = SND_SOC_DAI_I2S, |
417 | .probe = s3c24xx_i2s_probe, | 454 | .probe = s3c24xx_i2s_probe, |
455 | .suspend = s3c24xx_i2s_suspend, | ||
456 | .resume = s3c24xx_i2s_resume, | ||
418 | .playback = { | 457 | .playback = { |
419 | .channels_min = 2, | 458 | .channels_min = 2, |
420 | .channels_max = 2, | 459 | .channels_max = 2, |
diff --git a/sound/soc/s3c24xx/s3c24xx-pcm.c b/sound/soc/s3c24xx/s3c24xx-pcm.c index 4107a87d4de3..29a6c82f873a 100644 --- a/sound/soc/s3c24xx/s3c24xx-pcm.c +++ b/sound/soc/s3c24xx/s3c24xx-pcm.c | |||
@@ -24,7 +24,6 @@ | |||
24 | #include <linux/slab.h> | 24 | #include <linux/slab.h> |
25 | #include <linux/dma-mapping.h> | 25 | #include <linux/dma-mapping.h> |
26 | 26 | ||
27 | #include <sound/driver.h> | ||
28 | #include <sound/core.h> | 27 | #include <sound/core.h> |
29 | #include <sound/pcm.h> | 28 | #include <sound/pcm.h> |
30 | #include <sound/pcm_params.h> | 29 | #include <sound/pcm_params.h> |
@@ -49,7 +48,9 @@ static const struct snd_pcm_hardware s3c24xx_pcm_hardware = { | |||
49 | .info = SNDRV_PCM_INFO_INTERLEAVED | | 48 | .info = SNDRV_PCM_INFO_INTERLEAVED | |
50 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | 49 | SNDRV_PCM_INFO_BLOCK_TRANSFER | |
51 | SNDRV_PCM_INFO_MMAP | | 50 | SNDRV_PCM_INFO_MMAP | |
52 | SNDRV_PCM_INFO_MMAP_VALID, | 51 | SNDRV_PCM_INFO_MMAP_VALID | |
52 | SNDRV_PCM_INFO_PAUSE | | ||
53 | SNDRV_PCM_INFO_RESUME, | ||
53 | .formats = SNDRV_PCM_FMTBIT_S16_LE | | 54 | .formats = SNDRV_PCM_FMTBIT_S16_LE | |
54 | SNDRV_PCM_FMTBIT_U16_LE | | 55 | SNDRV_PCM_FMTBIT_U16_LE | |
55 | SNDRV_PCM_FMTBIT_U8 | | 56 | SNDRV_PCM_FMTBIT_U8 | |
@@ -176,28 +177,6 @@ static int s3c24xx_pcm_hw_params(struct snd_pcm_substream *substream, | |||
176 | } | 177 | } |
177 | } | 178 | } |
178 | 179 | ||
179 | /* channel needs configuring for mem=>device, increment memory addr, | ||
180 | * sync to pclk, half-word transfers to the IIS-FIFO. */ | ||
181 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
182 | s3c2410_dma_devconfig(prtd->params->channel, | ||
183 | S3C2410_DMASRC_MEM, S3C2410_DISRCC_INC | | ||
184 | S3C2410_DISRCC_APB, prtd->params->dma_addr); | ||
185 | |||
186 | s3c2410_dma_config(prtd->params->channel, | ||
187 | prtd->params->dma_size, | ||
188 | S3C2410_DCON_SYNC_PCLK | | ||
189 | S3C2410_DCON_HANDSHAKE); | ||
190 | } else { | ||
191 | s3c2410_dma_config(prtd->params->channel, | ||
192 | prtd->params->dma_size, | ||
193 | S3C2410_DCON_HANDSHAKE | | ||
194 | S3C2410_DCON_SYNC_PCLK); | ||
195 | |||
196 | s3c2410_dma_devconfig(prtd->params->channel, | ||
197 | S3C2410_DMASRC_HW, 0x3, | ||
198 | prtd->params->dma_addr); | ||
199 | } | ||
200 | |||
201 | s3c2410_dma_set_buffdone_fn(prtd->params->channel, | 180 | s3c2410_dma_set_buffdone_fn(prtd->params->channel, |
202 | s3c24xx_audio_buffdone); | 181 | s3c24xx_audio_buffdone); |
203 | 182 | ||
@@ -246,6 +225,28 @@ static int s3c24xx_pcm_prepare(struct snd_pcm_substream *substream) | |||
246 | if (!prtd->params) | 225 | if (!prtd->params) |
247 | return 0; | 226 | return 0; |
248 | 227 | ||
228 | /* channel needs configuring for mem=>device, increment memory addr, | ||
229 | * sync to pclk, half-word transfers to the IIS-FIFO. */ | ||
230 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
231 | s3c2410_dma_devconfig(prtd->params->channel, | ||
232 | S3C2410_DMASRC_MEM, S3C2410_DISRCC_INC | | ||
233 | S3C2410_DISRCC_APB, prtd->params->dma_addr); | ||
234 | |||
235 | s3c2410_dma_config(prtd->params->channel, | ||
236 | prtd->params->dma_size, | ||
237 | S3C2410_DCON_SYNC_PCLK | | ||
238 | S3C2410_DCON_HANDSHAKE); | ||
239 | } else { | ||
240 | s3c2410_dma_config(prtd->params->channel, | ||
241 | prtd->params->dma_size, | ||
242 | S3C2410_DCON_HANDSHAKE | | ||
243 | S3C2410_DCON_SYNC_PCLK); | ||
244 | |||
245 | s3c2410_dma_devconfig(prtd->params->channel, | ||
246 | S3C2410_DMASRC_HW, 0x3, | ||
247 | prtd->params->dma_addr); | ||
248 | } | ||
249 | |||
249 | /* flush the DMA channel */ | 250 | /* flush the DMA channel */ |
250 | s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_FLUSH); | 251 | s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_FLUSH); |
251 | prtd->dma_loaded = 0; | 252 | prtd->dma_loaded = 0; |
diff --git a/sound/soc/s3c24xx/smdk2443_wm9710.c b/sound/soc/s3c24xx/smdk2443_wm9710.c index d46cd811ceb3..b4a56302b9ab 100644 --- a/sound/soc/s3c24xx/smdk2443_wm9710.c +++ b/sound/soc/s3c24xx/smdk2443_wm9710.c | |||
@@ -17,7 +17,6 @@ | |||
17 | 17 | ||
18 | #include <linux/module.h> | 18 | #include <linux/module.h> |
19 | #include <linux/device.h> | 19 | #include <linux/device.h> |
20 | #include <sound/driver.h> | ||
21 | #include <sound/core.h> | 20 | #include <sound/core.h> |
22 | #include <sound/pcm.h> | 21 | #include <sound/pcm.h> |
23 | #include <sound/soc.h> | 22 | #include <sound/soc.h> |
diff --git a/sound/soc/sh/dma-sh7760.c b/sound/soc/sh/dma-sh7760.c index cdee374b843e..7a3ce80d6727 100644 --- a/sound/soc/sh/dma-sh7760.c +++ b/sound/soc/sh/dma-sh7760.c | |||
@@ -16,7 +16,6 @@ | |||
16 | #include <linux/init.h> | 16 | #include <linux/init.h> |
17 | #include <linux/platform_device.h> | 17 | #include <linux/platform_device.h> |
18 | #include <linux/dma-mapping.h> | 18 | #include <linux/dma-mapping.h> |
19 | #include <sound/driver.h> | ||
20 | #include <sound/core.h> | 19 | #include <sound/core.h> |
21 | #include <sound/pcm.h> | 20 | #include <sound/pcm.h> |
22 | #include <sound/pcm_params.h> | 21 | #include <sound/pcm_params.h> |
diff --git a/sound/soc/sh/hac.c b/sound/soc/sh/hac.c index 8e3f03908cdb..b7b676b3d671 100644 --- a/sound/soc/sh/hac.c +++ b/sound/soc/sh/hac.c | |||
@@ -21,7 +21,6 @@ | |||
21 | #include <linux/interrupt.h> | 21 | #include <linux/interrupt.h> |
22 | #include <linux/wait.h> | 22 | #include <linux/wait.h> |
23 | #include <linux/delay.h> | 23 | #include <linux/delay.h> |
24 | #include <sound/driver.h> | ||
25 | #include <sound/core.h> | 24 | #include <sound/core.h> |
26 | #include <sound/pcm.h> | 25 | #include <sound/pcm.h> |
27 | #include <sound/ac97_codec.h> | 26 | #include <sound/ac97_codec.h> |
@@ -105,7 +104,7 @@ static int hac_get_codec_data(struct hac_priv *hac, unsigned short r, | |||
105 | unsigned int to1, to2, i; | 104 | unsigned int to1, to2, i; |
106 | unsigned short adr; | 105 | unsigned short adr; |
107 | 106 | ||
108 | for (i = 0; i < AC97_READ_RETRY; ++i) { | 107 | for (i = AC97_READ_RETRY; i; i--) { |
109 | *v = 0; | 108 | *v = 0; |
110 | /* wait for HAC to receive something from the codec */ | 109 | /* wait for HAC to receive something from the codec */ |
111 | for (to1 = TMO_E4; | 110 | for (to1 = TMO_E4; |
@@ -132,7 +131,7 @@ static int hac_get_codec_data(struct hac_priv *hac, unsigned short r, | |||
132 | udelay(21); | 131 | udelay(21); |
133 | } | 132 | } |
134 | HACREG(HACRSR) &= ~(RSR_STDRY | RSR_STARY); | 133 | HACREG(HACRSR) &= ~(RSR_STDRY | RSR_STARY); |
135 | return (i < AC97_READ_RETRY); | 134 | return i; |
136 | } | 135 | } |
137 | 136 | ||
138 | static unsigned short hac_read_codec_aux(struct hac_priv *hac, | 137 | static unsigned short hac_read_codec_aux(struct hac_priv *hac, |
@@ -141,7 +140,7 @@ static unsigned short hac_read_codec_aux(struct hac_priv *hac, | |||
141 | unsigned short val; | 140 | unsigned short val; |
142 | unsigned int i, to; | 141 | unsigned int i, to; |
143 | 142 | ||
144 | for (i = 0; i < AC97_READ_RETRY; i++) { | 143 | for (i = AC97_READ_RETRY; i; i--) { |
145 | /* send_read_request */ | 144 | /* send_read_request */ |
146 | local_irq_disable(); | 145 | local_irq_disable(); |
147 | HACREG(HACTSR) &= ~(TSR_CMDAMT); | 146 | HACREG(HACTSR) &= ~(TSR_CMDAMT); |
@@ -159,10 +158,7 @@ static unsigned short hac_read_codec_aux(struct hac_priv *hac, | |||
159 | break; | 158 | break; |
160 | } | 159 | } |
161 | 160 | ||
162 | if (i == AC97_READ_RETRY) | 161 | return i ? val : ~0; |
163 | return ~0; | ||
164 | |||
165 | return val; | ||
166 | } | 162 | } |
167 | 163 | ||
168 | static void hac_ac97_write(struct snd_ac97 *ac97, unsigned short reg, | 164 | static void hac_ac97_write(struct snd_ac97 *ac97, unsigned short reg, |
@@ -172,7 +168,7 @@ static void hac_ac97_write(struct snd_ac97 *ac97, unsigned short reg, | |||
172 | struct hac_priv *hac = &hac_cpu_data[unit_id]; | 168 | struct hac_priv *hac = &hac_cpu_data[unit_id]; |
173 | unsigned int i, to; | 169 | unsigned int i, to; |
174 | /* write_codec_aux */ | 170 | /* write_codec_aux */ |
175 | for (i = 0; i < AC97_WRITE_RETRY; i++) { | 171 | for (i = AC97_WRITE_RETRY; i; i--) { |
176 | /* send_write_request */ | 172 | /* send_write_request */ |
177 | local_irq_disable(); | 173 | local_irq_disable(); |
178 | HACREG(HACTSR) &= ~(TSR_CMDDMT | TSR_CMDAMT); | 174 | HACREG(HACTSR) &= ~(TSR_CMDDMT | TSR_CMDAMT); |
diff --git a/sound/soc/sh/sh7760-ac97.c b/sound/soc/sh/sh7760-ac97.c index 5563f14511fa..2f91de84c5c7 100644 --- a/sound/soc/sh/sh7760-ac97.c +++ b/sound/soc/sh/sh7760-ac97.c | |||
@@ -9,7 +9,6 @@ | |||
9 | #include <linux/module.h> | 9 | #include <linux/module.h> |
10 | #include <linux/moduleparam.h> | 10 | #include <linux/moduleparam.h> |
11 | #include <linux/platform_device.h> | 11 | #include <linux/platform_device.h> |
12 | #include <sound/driver.h> | ||
13 | #include <sound/core.h> | 12 | #include <sound/core.h> |
14 | #include <sound/pcm.h> | 13 | #include <sound/pcm.h> |
15 | #include <sound/soc.h> | 14 | #include <sound/soc.h> |
diff --git a/sound/soc/sh/ssi.c b/sound/soc/sh/ssi.c index b72bc316cb8e..3388bc3d62d1 100644 --- a/sound/soc/sh/ssi.c +++ b/sound/soc/sh/ssi.c | |||
@@ -30,7 +30,6 @@ | |||
30 | #include <linux/init.h> | 30 | #include <linux/init.h> |
31 | #include <linux/module.h> | 31 | #include <linux/module.h> |
32 | #include <linux/platform_device.h> | 32 | #include <linux/platform_device.h> |
33 | #include <sound/driver.h> | ||
34 | #include <sound/core.h> | 33 | #include <sound/core.h> |
35 | #include <sound/pcm.h> | 34 | #include <sound/pcm.h> |
36 | #include <sound/initval.h> | 35 | #include <sound/initval.h> |
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index e6a67b58f296..9eb5479787c1 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c | |||
@@ -32,7 +32,6 @@ | |||
32 | #include <linux/pm.h> | 32 | #include <linux/pm.h> |
33 | #include <linux/bitops.h> | 33 | #include <linux/bitops.h> |
34 | #include <linux/platform_device.h> | 34 | #include <linux/platform_device.h> |
35 | #include <sound/driver.h> | ||
36 | #include <sound/core.h> | 35 | #include <sound/core.h> |
37 | #include <sound/pcm.h> | 36 | #include <sound/pcm.h> |
38 | #include <sound/pcm_params.h> | 37 | #include <sound/pcm_params.h> |
@@ -288,16 +287,25 @@ static void close_delayed_work(struct work_struct *work) | |||
288 | /* are we waiting on this codec DAI stream */ | 287 | /* are we waiting on this codec DAI stream */ |
289 | if (codec_dai->pop_wait == 1) { | 288 | if (codec_dai->pop_wait == 1) { |
290 | 289 | ||
290 | /* power down the codec to D1 if no longer active */ | ||
291 | if (codec->active == 0) { | ||
292 | dbg("pop wq D1 %s %s\n", codec->name, | ||
293 | codec_dai->playback.stream_name); | ||
294 | snd_soc_dapm_device_event(socdev, | ||
295 | SNDRV_CTL_POWER_D1); | ||
296 | } | ||
297 | |||
291 | codec_dai->pop_wait = 0; | 298 | codec_dai->pop_wait = 0; |
292 | snd_soc_dapm_stream_event(codec, codec_dai->playback.stream_name, | 299 | snd_soc_dapm_stream_event(codec, |
300 | codec_dai->playback.stream_name, | ||
293 | SND_SOC_DAPM_STREAM_STOP); | 301 | SND_SOC_DAPM_STREAM_STOP); |
294 | 302 | ||
295 | /* power down the codec power domain if no longer active */ | 303 | /* power down the codec power domain if no longer active */ |
296 | if (codec->active == 0) { | 304 | if (codec->active == 0) { |
297 | dbg("pop wq D3 %s %s\n", codec->name, | 305 | dbg("pop wq D3 %s %s\n", codec->name, |
298 | codec_dai->playback.stream_name); | 306 | codec_dai->playback.stream_name); |
299 | if (codec->dapm_event) | 307 | snd_soc_dapm_device_event(socdev, |
300 | codec->dapm_event(codec, SNDRV_CTL_POWER_D3hot); | 308 | SNDRV_CTL_POWER_D3hot); |
301 | } | 309 | } |
302 | } | 310 | } |
303 | } | 311 | } |
@@ -353,12 +361,12 @@ static int soc_codec_close(struct snd_pcm_substream *substream) | |||
353 | } else { | 361 | } else { |
354 | /* capture streams can be powered down now */ | 362 | /* capture streams can be powered down now */ |
355 | snd_soc_dapm_stream_event(codec, | 363 | snd_soc_dapm_stream_event(codec, |
356 | codec_dai->capture.stream_name, SND_SOC_DAPM_STREAM_STOP); | 364 | codec_dai->capture.stream_name, |
365 | SND_SOC_DAPM_STREAM_STOP); | ||
357 | 366 | ||
358 | if (codec->active == 0 && codec_dai->pop_wait == 0){ | 367 | if (codec->active == 0 && codec_dai->pop_wait == 0) |
359 | if (codec->dapm_event) | 368 | snd_soc_dapm_device_event(socdev, |
360 | codec->dapm_event(codec, SNDRV_CTL_POWER_D3hot); | 369 | SNDRV_CTL_POWER_D3hot); |
361 | } | ||
362 | } | 370 | } |
363 | 371 | ||
364 | mutex_unlock(&pcm_mutex); | 372 | mutex_unlock(&pcm_mutex); |
@@ -433,8 +441,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) | |||
433 | /* no delayed work - do we need to power up codec */ | 441 | /* no delayed work - do we need to power up codec */ |
434 | if (codec->dapm_state != SNDRV_CTL_POWER_D0) { | 442 | if (codec->dapm_state != SNDRV_CTL_POWER_D0) { |
435 | 443 | ||
436 | if (codec->dapm_event) | 444 | snd_soc_dapm_device_event(socdev, SNDRV_CTL_POWER_D1); |
437 | codec->dapm_event(codec, SNDRV_CTL_POWER_D1); | ||
438 | 445 | ||
439 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 446 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
440 | snd_soc_dapm_stream_event(codec, | 447 | snd_soc_dapm_stream_event(codec, |
@@ -445,8 +452,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) | |||
445 | codec_dai->capture.stream_name, | 452 | codec_dai->capture.stream_name, |
446 | SND_SOC_DAPM_STREAM_START); | 453 | SND_SOC_DAPM_STREAM_START); |
447 | 454 | ||
448 | if (codec->dapm_event) | 455 | snd_soc_dapm_device_event(socdev, SNDRV_CTL_POWER_D0); |
449 | codec->dapm_event(codec, SNDRV_CTL_POWER_D0); | ||
450 | if (codec_dai->dai_ops.digital_mute) | 456 | if (codec_dai->dai_ops.digital_mute) |
451 | codec_dai->dai_ops.digital_mute(codec_dai, 0); | 457 | codec_dai->dai_ops.digital_mute(codec_dai, 0); |
452 | 458 | ||
@@ -639,6 +645,10 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state) | |||
639 | dai->dai_ops.digital_mute(dai, 1); | 645 | dai->dai_ops.digital_mute(dai, 1); |
640 | } | 646 | } |
641 | 647 | ||
648 | /* suspend all pcms */ | ||
649 | for (i = 0; i < machine->num_links; i++) | ||
650 | snd_pcm_suspend_all(machine->dai_link[i].pcm); | ||
651 | |||
642 | if (machine->suspend_pre) | 652 | if (machine->suspend_pre) |
643 | machine->suspend_pre(pdev, state); | 653 | machine->suspend_pre(pdev, state); |
644 | 654 | ||
@@ -873,6 +883,7 @@ static int soc_new_pcm(struct snd_soc_device *socdev, | |||
873 | return ret; | 883 | return ret; |
874 | } | 884 | } |
875 | 885 | ||
886 | dai_link->pcm = pcm; | ||
876 | pcm->private_data = rtd; | 887 | pcm->private_data = rtd; |
877 | soc_pcm_ops.mmap = socdev->platform->pcm_ops->mmap; | 888 | soc_pcm_ops.mmap = socdev->platform->pcm_ops->mmap; |
878 | soc_pcm_ops.pointer = socdev->platform->pcm_ops->pointer; | 889 | soc_pcm_ops.pointer = socdev->platform->pcm_ops->pointer; |
@@ -1090,7 +1101,6 @@ int snd_soc_register_card(struct snd_soc_device *socdev) | |||
1090 | struct snd_soc_machine *machine = socdev->machine; | 1101 | struct snd_soc_machine *machine = socdev->machine; |
1091 | int ret = 0, i, ac97 = 0, err = 0; | 1102 | int ret = 0, i, ac97 = 0, err = 0; |
1092 | 1103 | ||
1093 | mutex_lock(&codec->mutex); | ||
1094 | for(i = 0; i < machine->num_links; i++) { | 1104 | for(i = 0; i < machine->num_links; i++) { |
1095 | if (socdev->machine->dai_link[i].init) { | 1105 | if (socdev->machine->dai_link[i].init) { |
1096 | err = socdev->machine->dai_link[i].init(codec); | 1106 | err = socdev->machine->dai_link[i].init(codec); |
@@ -1116,12 +1126,14 @@ int snd_soc_register_card(struct snd_soc_device *socdev) | |||
1116 | goto out; | 1126 | goto out; |
1117 | } | 1127 | } |
1118 | 1128 | ||
1129 | mutex_lock(&codec->mutex); | ||
1119 | #ifdef CONFIG_SND_SOC_AC97_BUS | 1130 | #ifdef CONFIG_SND_SOC_AC97_BUS |
1120 | if (ac97) { | 1131 | if (ac97) { |
1121 | ret = soc_ac97_dev_register(codec); | 1132 | ret = soc_ac97_dev_register(codec); |
1122 | if (ret < 0) { | 1133 | if (ret < 0) { |
1123 | printk(KERN_ERR "asoc: AC97 device register failed\n"); | 1134 | printk(KERN_ERR "asoc: AC97 device register failed\n"); |
1124 | snd_card_free(codec->card); | 1135 | snd_card_free(codec->card); |
1136 | mutex_unlock(&codec->mutex); | ||
1125 | goto out; | 1137 | goto out; |
1126 | } | 1138 | } |
1127 | } | 1139 | } |
@@ -1134,8 +1146,10 @@ int snd_soc_register_card(struct snd_soc_device *socdev) | |||
1134 | err = device_create_file(socdev->dev, &dev_attr_codec_reg); | 1146 | err = device_create_file(socdev->dev, &dev_attr_codec_reg); |
1135 | if (err < 0) | 1147 | if (err < 0) |
1136 | printk(KERN_WARNING "asoc: failed to add codec sysfs entries\n"); | 1148 | printk(KERN_WARNING "asoc: failed to add codec sysfs entries\n"); |
1137 | out: | 1149 | |
1138 | mutex_unlock(&codec->mutex); | 1150 | mutex_unlock(&codec->mutex); |
1151 | |||
1152 | out: | ||
1139 | return ret; | 1153 | return ret; |
1140 | } | 1154 | } |
1141 | EXPORT_SYMBOL_GPL(snd_soc_register_card); | 1155 | EXPORT_SYMBOL_GPL(snd_soc_register_card); |
@@ -1215,7 +1229,6 @@ struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template, | |||
1215 | memcpy(&template, _template, sizeof(template)); | 1229 | memcpy(&template, _template, sizeof(template)); |
1216 | if (long_name) | 1230 | if (long_name) |
1217 | template.name = long_name; | 1231 | template.name = long_name; |
1218 | template.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; | ||
1219 | template.index = 0; | 1232 | template.index = 0; |
1220 | 1233 | ||
1221 | return snd_ctl_new1(&template, data); | 1234 | return snd_ctl_new1(&template, data); |
@@ -1350,13 +1363,16 @@ EXPORT_SYMBOL_GPL(snd_soc_info_enum_ext); | |||
1350 | int snd_soc_info_volsw_ext(struct snd_kcontrol *kcontrol, | 1363 | int snd_soc_info_volsw_ext(struct snd_kcontrol *kcontrol, |
1351 | struct snd_ctl_elem_info *uinfo) | 1364 | struct snd_ctl_elem_info *uinfo) |
1352 | { | 1365 | { |
1353 | int mask = kcontrol->private_value; | 1366 | int max = kcontrol->private_value; |
1367 | |||
1368 | if (max == 1) | ||
1369 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | ||
1370 | else | ||
1371 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
1354 | 1372 | ||
1355 | uinfo->type = | ||
1356 | mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
1357 | uinfo->count = 1; | 1373 | uinfo->count = 1; |
1358 | uinfo->value.integer.min = 0; | 1374 | uinfo->value.integer.min = 0; |
1359 | uinfo->value.integer.max = mask; | 1375 | uinfo->value.integer.max = max; |
1360 | return 0; | 1376 | return 0; |
1361 | } | 1377 | } |
1362 | EXPORT_SYMBOL_GPL(snd_soc_info_volsw_ext); | 1378 | EXPORT_SYMBOL_GPL(snd_soc_info_volsw_ext); |
@@ -1373,15 +1389,18 @@ EXPORT_SYMBOL_GPL(snd_soc_info_volsw_ext); | |||
1373 | int snd_soc_info_volsw(struct snd_kcontrol *kcontrol, | 1389 | int snd_soc_info_volsw(struct snd_kcontrol *kcontrol, |
1374 | struct snd_ctl_elem_info *uinfo) | 1390 | struct snd_ctl_elem_info *uinfo) |
1375 | { | 1391 | { |
1376 | int mask = (kcontrol->private_value >> 16) & 0xff; | 1392 | int max = (kcontrol->private_value >> 16) & 0xff; |
1377 | int shift = (kcontrol->private_value >> 8) & 0x0f; | 1393 | int shift = (kcontrol->private_value >> 8) & 0x0f; |
1378 | int rshift = (kcontrol->private_value >> 12) & 0x0f; | 1394 | int rshift = (kcontrol->private_value >> 12) & 0x0f; |
1379 | 1395 | ||
1380 | uinfo->type = | 1396 | if (max == 1) |
1381 | mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; | 1397 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; |
1398 | else | ||
1399 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
1400 | |||
1382 | uinfo->count = shift == rshift ? 1 : 2; | 1401 | uinfo->count = shift == rshift ? 1 : 2; |
1383 | uinfo->value.integer.min = 0; | 1402 | uinfo->value.integer.min = 0; |
1384 | uinfo->value.integer.max = mask; | 1403 | uinfo->value.integer.max = max; |
1385 | return 0; | 1404 | return 0; |
1386 | } | 1405 | } |
1387 | EXPORT_SYMBOL_GPL(snd_soc_info_volsw); | 1406 | EXPORT_SYMBOL_GPL(snd_soc_info_volsw); |
@@ -1402,7 +1421,8 @@ int snd_soc_get_volsw(struct snd_kcontrol *kcontrol, | |||
1402 | int reg = kcontrol->private_value & 0xff; | 1421 | int reg = kcontrol->private_value & 0xff; |
1403 | int shift = (kcontrol->private_value >> 8) & 0x0f; | 1422 | int shift = (kcontrol->private_value >> 8) & 0x0f; |
1404 | int rshift = (kcontrol->private_value >> 12) & 0x0f; | 1423 | int rshift = (kcontrol->private_value >> 12) & 0x0f; |
1405 | int mask = (kcontrol->private_value >> 16) & 0xff; | 1424 | int max = (kcontrol->private_value >> 16) & 0xff; |
1425 | int mask = (1 << fls(max)) - 1; | ||
1406 | int invert = (kcontrol->private_value >> 24) & 0x01; | 1426 | int invert = (kcontrol->private_value >> 24) & 0x01; |
1407 | 1427 | ||
1408 | ucontrol->value.integer.value[0] = | 1428 | ucontrol->value.integer.value[0] = |
@@ -1412,10 +1432,10 @@ int snd_soc_get_volsw(struct snd_kcontrol *kcontrol, | |||
1412 | (snd_soc_read(codec, reg) >> rshift) & mask; | 1432 | (snd_soc_read(codec, reg) >> rshift) & mask; |
1413 | if (invert) { | 1433 | if (invert) { |
1414 | ucontrol->value.integer.value[0] = | 1434 | ucontrol->value.integer.value[0] = |
1415 | mask - ucontrol->value.integer.value[0]; | 1435 | max - ucontrol->value.integer.value[0]; |
1416 | if (shift != rshift) | 1436 | if (shift != rshift) |
1417 | ucontrol->value.integer.value[1] = | 1437 | ucontrol->value.integer.value[1] = |
1418 | mask - ucontrol->value.integer.value[1]; | 1438 | max - ucontrol->value.integer.value[1]; |
1419 | } | 1439 | } |
1420 | 1440 | ||
1421 | return 0; | 1441 | return 0; |
@@ -1438,25 +1458,24 @@ int snd_soc_put_volsw(struct snd_kcontrol *kcontrol, | |||
1438 | int reg = kcontrol->private_value & 0xff; | 1458 | int reg = kcontrol->private_value & 0xff; |
1439 | int shift = (kcontrol->private_value >> 8) & 0x0f; | 1459 | int shift = (kcontrol->private_value >> 8) & 0x0f; |
1440 | int rshift = (kcontrol->private_value >> 12) & 0x0f; | 1460 | int rshift = (kcontrol->private_value >> 12) & 0x0f; |
1441 | int mask = (kcontrol->private_value >> 16) & 0xff; | 1461 | int max = (kcontrol->private_value >> 16) & 0xff; |
1462 | int mask = (1 << fls(max)) - 1; | ||
1442 | int invert = (kcontrol->private_value >> 24) & 0x01; | 1463 | int invert = (kcontrol->private_value >> 24) & 0x01; |
1443 | int err; | ||
1444 | unsigned short val, val2, val_mask; | 1464 | unsigned short val, val2, val_mask; |
1445 | 1465 | ||
1446 | val = (ucontrol->value.integer.value[0] & mask); | 1466 | val = (ucontrol->value.integer.value[0] & mask); |
1447 | if (invert) | 1467 | if (invert) |
1448 | val = mask - val; | 1468 | val = max - val; |
1449 | val_mask = mask << shift; | 1469 | val_mask = mask << shift; |
1450 | val = val << shift; | 1470 | val = val << shift; |
1451 | if (shift != rshift) { | 1471 | if (shift != rshift) { |
1452 | val2 = (ucontrol->value.integer.value[1] & mask); | 1472 | val2 = (ucontrol->value.integer.value[1] & mask); |
1453 | if (invert) | 1473 | if (invert) |
1454 | val2 = mask - val2; | 1474 | val2 = max - val2; |
1455 | val_mask |= mask << rshift; | 1475 | val_mask |= mask << rshift; |
1456 | val |= val2 << rshift; | 1476 | val |= val2 << rshift; |
1457 | } | 1477 | } |
1458 | err = snd_soc_update_bits(codec, reg, val_mask, val); | 1478 | return snd_soc_update_bits(codec, reg, val_mask, val); |
1459 | return err; | ||
1460 | } | 1479 | } |
1461 | EXPORT_SYMBOL_GPL(snd_soc_put_volsw); | 1480 | EXPORT_SYMBOL_GPL(snd_soc_put_volsw); |
1462 | 1481 | ||
@@ -1473,13 +1492,16 @@ EXPORT_SYMBOL_GPL(snd_soc_put_volsw); | |||
1473 | int snd_soc_info_volsw_2r(struct snd_kcontrol *kcontrol, | 1492 | int snd_soc_info_volsw_2r(struct snd_kcontrol *kcontrol, |
1474 | struct snd_ctl_elem_info *uinfo) | 1493 | struct snd_ctl_elem_info *uinfo) |
1475 | { | 1494 | { |
1476 | int mask = (kcontrol->private_value >> 12) & 0xff; | 1495 | int max = (kcontrol->private_value >> 12) & 0xff; |
1496 | |||
1497 | if (max == 1) | ||
1498 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | ||
1499 | else | ||
1500 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
1477 | 1501 | ||
1478 | uinfo->type = | ||
1479 | mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
1480 | uinfo->count = 2; | 1502 | uinfo->count = 2; |
1481 | uinfo->value.integer.min = 0; | 1503 | uinfo->value.integer.min = 0; |
1482 | uinfo->value.integer.max = mask; | 1504 | uinfo->value.integer.max = max; |
1483 | return 0; | 1505 | return 0; |
1484 | } | 1506 | } |
1485 | EXPORT_SYMBOL_GPL(snd_soc_info_volsw_2r); | 1507 | EXPORT_SYMBOL_GPL(snd_soc_info_volsw_2r); |
@@ -1500,7 +1522,8 @@ int snd_soc_get_volsw_2r(struct snd_kcontrol *kcontrol, | |||
1500 | int reg = kcontrol->private_value & 0xff; | 1522 | int reg = kcontrol->private_value & 0xff; |
1501 | int reg2 = (kcontrol->private_value >> 24) & 0xff; | 1523 | int reg2 = (kcontrol->private_value >> 24) & 0xff; |
1502 | int shift = (kcontrol->private_value >> 8) & 0x0f; | 1524 | int shift = (kcontrol->private_value >> 8) & 0x0f; |
1503 | int mask = (kcontrol->private_value >> 12) & 0xff; | 1525 | int max = (kcontrol->private_value >> 12) & 0xff; |
1526 | int mask = (1<<fls(max))-1; | ||
1504 | int invert = (kcontrol->private_value >> 20) & 0x01; | 1527 | int invert = (kcontrol->private_value >> 20) & 0x01; |
1505 | 1528 | ||
1506 | ucontrol->value.integer.value[0] = | 1529 | ucontrol->value.integer.value[0] = |
@@ -1509,9 +1532,9 @@ int snd_soc_get_volsw_2r(struct snd_kcontrol *kcontrol, | |||
1509 | (snd_soc_read(codec, reg2) >> shift) & mask; | 1532 | (snd_soc_read(codec, reg2) >> shift) & mask; |
1510 | if (invert) { | 1533 | if (invert) { |
1511 | ucontrol->value.integer.value[0] = | 1534 | ucontrol->value.integer.value[0] = |
1512 | mask - ucontrol->value.integer.value[0]; | 1535 | max - ucontrol->value.integer.value[0]; |
1513 | ucontrol->value.integer.value[1] = | 1536 | ucontrol->value.integer.value[1] = |
1514 | mask - ucontrol->value.integer.value[1]; | 1537 | max - ucontrol->value.integer.value[1]; |
1515 | } | 1538 | } |
1516 | 1539 | ||
1517 | return 0; | 1540 | return 0; |
@@ -1534,7 +1557,8 @@ int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol, | |||
1534 | int reg = kcontrol->private_value & 0xff; | 1557 | int reg = kcontrol->private_value & 0xff; |
1535 | int reg2 = (kcontrol->private_value >> 24) & 0xff; | 1558 | int reg2 = (kcontrol->private_value >> 24) & 0xff; |
1536 | int shift = (kcontrol->private_value >> 8) & 0x0f; | 1559 | int shift = (kcontrol->private_value >> 8) & 0x0f; |
1537 | int mask = (kcontrol->private_value >> 12) & 0xff; | 1560 | int max = (kcontrol->private_value >> 12) & 0xff; |
1561 | int mask = (1 << fls(max)) - 1; | ||
1538 | int invert = (kcontrol->private_value >> 20) & 0x01; | 1562 | int invert = (kcontrol->private_value >> 20) & 0x01; |
1539 | int err; | 1563 | int err; |
1540 | unsigned short val, val2, val_mask; | 1564 | unsigned short val, val2, val_mask; |
@@ -1544,8 +1568,8 @@ int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol, | |||
1544 | val2 = (ucontrol->value.integer.value[1] & mask); | 1568 | val2 = (ucontrol->value.integer.value[1] & mask); |
1545 | 1569 | ||
1546 | if (invert) { | 1570 | if (invert) { |
1547 | val = mask - val; | 1571 | val = max - val; |
1548 | val2 = mask - val2; | 1572 | val2 = max - val2; |
1549 | } | 1573 | } |
1550 | 1574 | ||
1551 | val = val << shift; | 1575 | val = val << shift; |
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 29a546fecacf..620d7ea3c15f 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c | |||
@@ -43,7 +43,6 @@ | |||
43 | #include <linux/bitops.h> | 43 | #include <linux/bitops.h> |
44 | #include <linux/platform_device.h> | 44 | #include <linux/platform_device.h> |
45 | #include <linux/jiffies.h> | 45 | #include <linux/jiffies.h> |
46 | #include <sound/driver.h> | ||
47 | #include <sound/core.h> | 46 | #include <sound/core.h> |
48 | #include <sound/pcm.h> | 47 | #include <sound/pcm.h> |
49 | #include <sound/pcm_params.h> | 48 | #include <sound/pcm_params.h> |
@@ -524,11 +523,13 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) | |||
524 | continue; | 523 | continue; |
525 | 524 | ||
526 | if (event == SND_SOC_DAPM_STREAM_START) { | 525 | if (event == SND_SOC_DAPM_STREAM_START) { |
527 | ret = w->event(w, SND_SOC_DAPM_PRE_PMU); | 526 | ret = w->event(w, |
527 | NULL, SND_SOC_DAPM_PRE_PMU); | ||
528 | if (ret < 0) | 528 | if (ret < 0) |
529 | return ret; | 529 | return ret; |
530 | } else if (event == SND_SOC_DAPM_STREAM_STOP) { | 530 | } else if (event == SND_SOC_DAPM_STREAM_STOP) { |
531 | ret = w->event(w, SND_SOC_DAPM_PRE_PMD); | 531 | ret = w->event(w, |
532 | NULL, SND_SOC_DAPM_PRE_PMD); | ||
532 | if (ret < 0) | 533 | if (ret < 0) |
533 | return ret; | 534 | return ret; |
534 | } | 535 | } |
@@ -539,11 +540,13 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) | |||
539 | continue; | 540 | continue; |
540 | 541 | ||
541 | if (event == SND_SOC_DAPM_STREAM_START) { | 542 | if (event == SND_SOC_DAPM_STREAM_START) { |
542 | ret = w->event(w, SND_SOC_DAPM_POST_PMU); | 543 | ret = w->event(w, |
544 | NULL, SND_SOC_DAPM_POST_PMU); | ||
543 | if (ret < 0) | 545 | if (ret < 0) |
544 | return ret; | 546 | return ret; |
545 | } else if (event == SND_SOC_DAPM_STREAM_STOP) { | 547 | } else if (event == SND_SOC_DAPM_STREAM_STOP) { |
546 | ret = w->event(w, SND_SOC_DAPM_POST_PMD); | 548 | ret = w->event(w, |
549 | NULL, SND_SOC_DAPM_POST_PMD); | ||
547 | if (ret < 0) | 550 | if (ret < 0) |
548 | return ret; | 551 | return ret; |
549 | } | 552 | } |
@@ -567,26 +570,30 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) | |||
567 | if (power) { | 570 | if (power) { |
568 | /* power up event */ | 571 | /* power up event */ |
569 | if (w->event_flags & SND_SOC_DAPM_PRE_PMU) { | 572 | if (w->event_flags & SND_SOC_DAPM_PRE_PMU) { |
570 | ret = w->event(w, SND_SOC_DAPM_PRE_PMU); | 573 | ret = w->event(w, |
574 | NULL, SND_SOC_DAPM_PRE_PMU); | ||
571 | if (ret < 0) | 575 | if (ret < 0) |
572 | return ret; | 576 | return ret; |
573 | } | 577 | } |
574 | dapm_update_bits(w); | 578 | dapm_update_bits(w); |
575 | if (w->event_flags & SND_SOC_DAPM_POST_PMU){ | 579 | if (w->event_flags & SND_SOC_DAPM_POST_PMU){ |
576 | ret = w->event(w, SND_SOC_DAPM_POST_PMU); | 580 | ret = w->event(w, |
581 | NULL, SND_SOC_DAPM_POST_PMU); | ||
577 | if (ret < 0) | 582 | if (ret < 0) |
578 | return ret; | 583 | return ret; |
579 | } | 584 | } |
580 | } else { | 585 | } else { |
581 | /* power down event */ | 586 | /* power down event */ |
582 | if (w->event_flags & SND_SOC_DAPM_PRE_PMD) { | 587 | if (w->event_flags & SND_SOC_DAPM_PRE_PMD) { |
583 | ret = w->event(w, SND_SOC_DAPM_PRE_PMD); | 588 | ret = w->event(w, |
589 | NULL, SND_SOC_DAPM_PRE_PMD); | ||
584 | if (ret < 0) | 590 | if (ret < 0) |
585 | return ret; | 591 | return ret; |
586 | } | 592 | } |
587 | dapm_update_bits(w); | 593 | dapm_update_bits(w); |
588 | if (w->event_flags & SND_SOC_DAPM_POST_PMD) { | 594 | if (w->event_flags & SND_SOC_DAPM_POST_PMD) { |
589 | ret = w->event(w, SND_SOC_DAPM_POST_PMD); | 595 | ret = w->event(w, |
596 | NULL, SND_SOC_DAPM_POST_PMD); | ||
590 | if (ret < 0) | 597 | if (ret < 0) |
591 | return ret; | 598 | return ret; |
592 | } | 599 | } |
@@ -692,7 +699,7 @@ static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget, | |||
692 | return 0; | 699 | return 0; |
693 | } | 700 | } |
694 | 701 | ||
695 | /* test and update the power status of a mixer widget */ | 702 | /* test and update the power status of a mixer or switch widget */ |
696 | static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, | 703 | static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, |
697 | struct snd_kcontrol *kcontrol, int reg, | 704 | struct snd_kcontrol *kcontrol, int reg, |
698 | int val_mask, int val, int invert) | 705 | int val_mask, int val, int invert) |
@@ -700,7 +707,8 @@ static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, | |||
700 | struct snd_soc_dapm_path *path; | 707 | struct snd_soc_dapm_path *path; |
701 | int found = 0; | 708 | int found = 0; |
702 | 709 | ||
703 | if (widget->id != snd_soc_dapm_mixer) | 710 | if (widget->id != snd_soc_dapm_mixer && |
711 | widget->id != snd_soc_dapm_switch) | ||
704 | return -ENODEV; | 712 | return -ENODEV; |
705 | 713 | ||
706 | if (!snd_soc_test_bits(widget->codec, reg, val_mask, val)) | 714 | if (!snd_soc_test_bits(widget->codec, reg, val_mask, val)) |
@@ -963,7 +971,6 @@ int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec) | |||
963 | { | 971 | { |
964 | struct snd_soc_dapm_widget *w; | 972 | struct snd_soc_dapm_widget *w; |
965 | 973 | ||
966 | mutex_lock(&codec->mutex); | ||
967 | list_for_each_entry(w, &codec->dapm_widgets, list) | 974 | list_for_each_entry(w, &codec->dapm_widgets, list) |
968 | { | 975 | { |
969 | if (w->new) | 976 | if (w->new) |
@@ -998,7 +1005,6 @@ int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec) | |||
998 | } | 1005 | } |
999 | 1006 | ||
1000 | dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP); | 1007 | dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP); |
1001 | mutex_unlock(&codec->mutex); | ||
1002 | return 0; | 1008 | return 0; |
1003 | } | 1009 | } |
1004 | EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets); | 1010 | EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets); |
@@ -1019,8 +1025,9 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, | |||
1019 | int reg = kcontrol->private_value & 0xff; | 1025 | int reg = kcontrol->private_value & 0xff; |
1020 | int shift = (kcontrol->private_value >> 8) & 0x0f; | 1026 | int shift = (kcontrol->private_value >> 8) & 0x0f; |
1021 | int rshift = (kcontrol->private_value >> 12) & 0x0f; | 1027 | int rshift = (kcontrol->private_value >> 12) & 0x0f; |
1022 | int mask = (kcontrol->private_value >> 16) & 0xff; | 1028 | int max = (kcontrol->private_value >> 16) & 0xff; |
1023 | int invert = (kcontrol->private_value >> 24) & 0x01; | 1029 | int invert = (kcontrol->private_value >> 24) & 0x01; |
1030 | int mask = (1 << fls(max)) - 1; | ||
1024 | 1031 | ||
1025 | /* return the saved value if we are powered down */ | 1032 | /* return the saved value if we are powered down */ |
1026 | if (widget->id == snd_soc_dapm_pga && !widget->power) { | 1033 | if (widget->id == snd_soc_dapm_pga && !widget->power) { |
@@ -1035,10 +1042,10 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, | |||
1035 | (snd_soc_read(widget->codec, reg) >> rshift) & mask; | 1042 | (snd_soc_read(widget->codec, reg) >> rshift) & mask; |
1036 | if (invert) { | 1043 | if (invert) { |
1037 | ucontrol->value.integer.value[0] = | 1044 | ucontrol->value.integer.value[0] = |
1038 | mask - ucontrol->value.integer.value[0]; | 1045 | max - ucontrol->value.integer.value[0]; |
1039 | if (shift != rshift) | 1046 | if (shift != rshift) |
1040 | ucontrol->value.integer.value[1] = | 1047 | ucontrol->value.integer.value[1] = |
1041 | mask - ucontrol->value.integer.value[1]; | 1048 | max - ucontrol->value.integer.value[1]; |
1042 | } | 1049 | } |
1043 | 1050 | ||
1044 | return 0; | 1051 | return 0; |
@@ -1061,7 +1068,8 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, | |||
1061 | int reg = kcontrol->private_value & 0xff; | 1068 | int reg = kcontrol->private_value & 0xff; |
1062 | int shift = (kcontrol->private_value >> 8) & 0x0f; | 1069 | int shift = (kcontrol->private_value >> 8) & 0x0f; |
1063 | int rshift = (kcontrol->private_value >> 12) & 0x0f; | 1070 | int rshift = (kcontrol->private_value >> 12) & 0x0f; |
1064 | int mask = (kcontrol->private_value >> 16) & 0xff; | 1071 | int max = (kcontrol->private_value >> 16) & 0xff; |
1072 | int mask = (1 << fls(max)) - 1; | ||
1065 | int invert = (kcontrol->private_value >> 24) & 0x01; | 1073 | int invert = (kcontrol->private_value >> 24) & 0x01; |
1066 | unsigned short val, val2, val_mask; | 1074 | unsigned short val, val2, val_mask; |
1067 | int ret; | 1075 | int ret; |
@@ -1069,13 +1077,13 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, | |||
1069 | val = (ucontrol->value.integer.value[0] & mask); | 1077 | val = (ucontrol->value.integer.value[0] & mask); |
1070 | 1078 | ||
1071 | if (invert) | 1079 | if (invert) |
1072 | val = mask - val; | 1080 | val = max - val; |
1073 | val_mask = mask << shift; | 1081 | val_mask = mask << shift; |
1074 | val = val << shift; | 1082 | val = val << shift; |
1075 | if (shift != rshift) { | 1083 | if (shift != rshift) { |
1076 | val2 = (ucontrol->value.integer.value[1] & mask); | 1084 | val2 = (ucontrol->value.integer.value[1] & mask); |
1077 | if (invert) | 1085 | if (invert) |
1078 | val2 = mask - val2; | 1086 | val2 = max - val2; |
1079 | val_mask |= mask << rshift; | 1087 | val_mask |= mask << rshift; |
1080 | val |= val2 << rshift; | 1088 | val |= val2 << rshift; |
1081 | } | 1089 | } |
@@ -1093,13 +1101,17 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, | |||
1093 | dapm_mixer_update_power(widget, kcontrol, reg, val_mask, val, invert); | 1101 | dapm_mixer_update_power(widget, kcontrol, reg, val_mask, val, invert); |
1094 | if (widget->event) { | 1102 | if (widget->event) { |
1095 | if (widget->event_flags & SND_SOC_DAPM_PRE_REG) { | 1103 | if (widget->event_flags & SND_SOC_DAPM_PRE_REG) { |
1096 | ret = widget->event(widget, SND_SOC_DAPM_PRE_REG); | 1104 | ret = widget->event(widget, kcontrol, |
1097 | if (ret < 0) | 1105 | SND_SOC_DAPM_PRE_REG); |
1106 | if (ret < 0) { | ||
1107 | ret = 1; | ||
1098 | goto out; | 1108 | goto out; |
1109 | } | ||
1099 | } | 1110 | } |
1100 | ret = snd_soc_update_bits(widget->codec, reg, val_mask, val); | 1111 | ret = snd_soc_update_bits(widget->codec, reg, val_mask, val); |
1101 | if (widget->event_flags & SND_SOC_DAPM_POST_REG) | 1112 | if (widget->event_flags & SND_SOC_DAPM_POST_REG) |
1102 | ret = widget->event(widget, SND_SOC_DAPM_POST_REG); | 1113 | ret = widget->event(widget, kcontrol, |
1114 | SND_SOC_DAPM_POST_REG); | ||
1103 | } else | 1115 | } else |
1104 | ret = snd_soc_update_bits(widget->codec, reg, val_mask, val); | 1116 | ret = snd_soc_update_bits(widget->codec, reg, val_mask, val); |
1105 | 1117 | ||
@@ -1174,13 +1186,15 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, | |||
1174 | dapm_mux_update_power(widget, kcontrol, mask, mux, e); | 1186 | dapm_mux_update_power(widget, kcontrol, mask, mux, e); |
1175 | if (widget->event) { | 1187 | if (widget->event) { |
1176 | if (widget->event_flags & SND_SOC_DAPM_PRE_REG) { | 1188 | if (widget->event_flags & SND_SOC_DAPM_PRE_REG) { |
1177 | ret = widget->event(widget, SND_SOC_DAPM_PRE_REG); | 1189 | ret = widget->event(widget, |
1190 | kcontrol, SND_SOC_DAPM_PRE_REG); | ||
1178 | if (ret < 0) | 1191 | if (ret < 0) |
1179 | goto out; | 1192 | goto out; |
1180 | } | 1193 | } |
1181 | ret = snd_soc_update_bits(widget->codec, e->reg, mask, val); | 1194 | ret = snd_soc_update_bits(widget->codec, e->reg, mask, val); |
1182 | if (widget->event_flags & SND_SOC_DAPM_POST_REG) | 1195 | if (widget->event_flags & SND_SOC_DAPM_POST_REG) |
1183 | ret = widget->event(widget, SND_SOC_DAPM_POST_REG); | 1196 | ret = widget->event(widget, |
1197 | kcontrol, SND_SOC_DAPM_POST_REG); | ||
1184 | } else | 1198 | } else |
1185 | ret = snd_soc_update_bits(widget->codec, e->reg, mask, val); | 1199 | ret = snd_soc_update_bits(widget->codec, e->reg, mask, val); |
1186 | 1200 | ||
@@ -1280,6 +1294,29 @@ int snd_soc_dapm_stream_event(struct snd_soc_codec *codec, | |||
1280 | EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_event); | 1294 | EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_event); |
1281 | 1295 | ||
1282 | /** | 1296 | /** |
1297 | * snd_soc_dapm_device_event - send a device event to the dapm core | ||
1298 | * @socdev: audio device | ||
1299 | * @event: device event | ||
1300 | * | ||
1301 | * Sends a device event to the dapm core. The core then makes any | ||
1302 | * necessary machine or codec power changes.. | ||
1303 | * | ||
1304 | * Returns 0 for success else error. | ||
1305 | */ | ||
1306 | int snd_soc_dapm_device_event(struct snd_soc_device *socdev, int event) | ||
1307 | { | ||
1308 | struct snd_soc_codec *codec = socdev->codec; | ||
1309 | struct snd_soc_machine *machine = socdev->machine; | ||
1310 | |||
1311 | if (machine->dapm_event) | ||
1312 | machine->dapm_event(machine, event); | ||
1313 | if (codec->dapm_event) | ||
1314 | codec->dapm_event(codec, event); | ||
1315 | return 0; | ||
1316 | } | ||
1317 | EXPORT_SYMBOL_GPL(snd_soc_dapm_device_event); | ||
1318 | |||
1319 | /** | ||
1283 | * snd_soc_dapm_set_endpoint - set audio endpoint status | 1320 | * snd_soc_dapm_set_endpoint - set audio endpoint status |
1284 | * @codec: audio codec | 1321 | * @codec: audio codec |
1285 | * @endpoint: audio signal endpoint (or start point) | 1322 | * @endpoint: audio signal endpoint (or start point) |