summaryrefslogtreecommitdiffstats
path: root/meta/packages/linux
diff options
context:
space:
mode:
authorRichard Purdie <richard@openedhand.com>2007-06-26 21:36:34 +0000
committerRichard Purdie <richard@openedhand.com>2007-06-26 21:36:34 +0000
commit8689ce9ada86d66337dcd7dc54bc58e318912fac (patch)
tree3f92b314d1402cd5a0fbdb1f065990e9ed500d91 /meta/packages/linux
parentd3076aca9d68e617b26c19f75dc701f38716bbc3 (diff)
downloadpoky-8689ce9ada86d66337dcd7dc54bc58e318912fac.tar.gz
linux-rp-2.6.17: Sync with OE
git-svn-id: https://svn.o-hand.com/repos/poky/trunk@2013 311d38ba-8fff-0310-9ca6-ca027cbcb966
Diffstat (limited to 'meta/packages/linux')
-rw-r--r--meta/packages/linux/linux-rp-2.6.17/asoc-v0.12.4_2.6.17.patch31713
-rw-r--r--meta/packages/linux/linux-rp-2.6.17/tosa-lcdnoise-r0.patch157
-rw-r--r--meta/packages/linux/linux-rp-2.6.17/wm9712-reset-loop-r2.patch44
-rw-r--r--meta/packages/linux/linux-rp-2.6.17/wm9712-suspend-cold-res-r2.patch16
-rw-r--r--meta/packages/linux/linux-rp-2.6.20/defconfig-poodle1
-rw-r--r--meta/packages/linux/linux-rp_2.6.17.bb16
6 files changed, 31939 insertions, 8 deletions
diff --git a/meta/packages/linux/linux-rp-2.6.17/asoc-v0.12.4_2.6.17.patch b/meta/packages/linux/linux-rp-2.6.17/asoc-v0.12.4_2.6.17.patch
new file mode 100644
index 0000000000..4f9672299b
--- /dev/null
+++ b/meta/packages/linux/linux-rp-2.6.17/asoc-v0.12.4_2.6.17.patch
@@ -0,0 +1,31713 @@
1Index: linux-2.6-pxa-new/Documentation/sound/alsa/soc/DAI.txt
2===================================================================
3--- /dev/null
4+++ linux-2.6-pxa-new/Documentation/sound/alsa/soc/DAI.txt
5@@ -0,0 +1,546 @@
6+ASoC currently supports the three main Digital Audio Interfaces (DAI) found on
7+SoC controllers and portable audio CODECS today, namely AC97, I2S and PCM.
8+
9+
10+AC97
11+====
12+
13+ AC97 is a five wire interface commonly found on many PC sound cards. It is
14+now also popular in many portable devices. This DAI has a reset line and time
15+multiplexes its data on its SDATA_OUT (playback) and SDATA_IN (capture) lines.
16+The bit clock (BCLK) is always driven by the CODEC (usually 12.288MHz) and the
17+frame (FRAME) (usually 48kHz) is always driven by the controller. Each AC97
18+frame is 21uS long and is divided into 13 time slots.
19+
20+The AC97 specification can be found at :-
21+http://www.intel.com/design/chipsets/audio/ac97_r23.pdf
22+
23+
24+I2S
25+===
26+
27+ I2S is a common 4 wire DAI used in HiFi, STB and portable devices. The Tx and
28+Rx lines are used for audio transmision, whilst the bit clock (BCLK) and
29+left/right clock (LRC) synchronise the link. I2S is flexible in that either the
30+controller or CODEC can drive (master) the BCLK and LRC clock lines. Bit clock
31+usually varies depending on the sample rate and the master system clock
32+(SYSCLK). LRCLK is the same as the sample rate. A few devices support separate
33+ADC and DAC LRCLK's, this allows for similtanious capture and playback at
34+different sample rates.
35+
36+I2S has several different operating modes:-
37+
38+ o I2S - MSB is transmitted on the falling edge of the first BCLK after LRC
39+ transition.
40+
41+ o Left Justified - MSB is transmitted on transition of LRC.
42+
43+ o Right Justified - MSB is transmitted sample size BCLK's before LRC
44+ transition.
45+
46+PCM
47+===
48+
49+PCM is another 4 wire interface, very similar to I2S, that can support a more
50+flexible protocol. It has bit clock (BCLK) and sync (SYNC) lines that are used
51+to synchronise the link whilst the Tx and Rx lines are used to transmit and
52+receive the audio data. Bit clock usually varies depending on sample rate
53+whilst sync runs at the sample rate. PCM also supports Time Division
54+Multiplexing (TDM) in that several devices can use the bus similtaniuosly (This
55+is sometimes referred to as network mode).
56+
57+Common PCM operating modes:-
58+
59+ o Mode A - MSB is transmitted on falling edge of first BCLK after FRAME/SYNC.
60+
61+ o Mode B - MSB is transmitted on rising edge of FRAME/SYNC.
62+
63+
64+ASoC DAI Configuration
65+======================
66+
67+Every CODEC DAI and SoC DAI must have their capabilities defined in order to
68+be configured together at runtime when the audio and clocking parameters are
69+known. This is achieved by creating an array of struct snd_soc_hw_mode in the
70+the CODEC and SoC interface drivers. Each element in the array describes a DAI
71+mode and each mode is usually based upon the DAI system clock to sample rate
72+ratio (FS).
73+
74+i.e. 48k sample rate @ 256 FS = sytem clock of 12.288 MHz
75+ 48000 * 256 = 12288000
76+
77+The CPU and Codec DAI modes are then ANDed together at runtime to determine the
78+rutime DAI configuration for both the Codec and CPU.
79+
80+When creating a new codec or SoC DAI it's probably best to start of with a few
81+sample rates first and then test your interface.
82+
83+struct snd_soc_dai_mode is defined (in soc.h) as:-
84+
85+/* SoC DAI mode */
86+struct snd_soc_dai_mode {
87+ u16 fmt; /* SND_SOC_DAIFMT_* */
88+ u16 tdm; /* SND_SOC_HWTDM_* */
89+ u64 pcmfmt; /* SNDRV_PCM_FMTBIT_* */
90+ u16 pcmrate; /* SND_SOC_HWRATE_* */
91+ u16 pcmdir:2; /* SND_SOC_HWDIR_* */
92+ u16 flags:8; /* hw flags */
93+ u16 fs; /* mclk to rate divider */
94+ u64 bfs; /* mclk to bclk dividers */
95+ unsigned long priv; /* private mode data */
96+};
97+
98+fmt:
99+----
100+This field defines the DAI mode hardware format (e.g. I2S settings) and
101+supports the following settings:-
102+
103+ 1) hardware DAI formats
104+
105+#define SND_SOC_DAIFMT_I2S (1 << 0) /* I2S mode */
106+#define SND_SOC_DAIFMT_RIGHT_J (1 << 1) /* Right justified mode */
107+#define SND_SOC_DAIFMT_LEFT_J (1 << 2) /* Left Justified mode */
108+#define SND_SOC_DAIFMT_DSP_A (1 << 3) /* L data msb after FRM */
109+#define SND_SOC_DAIFMT_DSP_B (1 << 4) /* L data msb during FRM */
110+#define SND_SOC_DAIFMT_AC97 (1 << 5) /* AC97 */
111+
112+ 2) hw DAI signal inversions
113+
114+#define SND_SOC_DAIFMT_NB_NF (1 << 8) /* normal bit clock + frame */
115+#define SND_SOC_DAIFMT_NB_IF (1 << 9) /* normal bclk + inv frm */
116+#define SND_SOC_DAIFMT_IB_NF (1 << 10) /* invert bclk + nor frm */
117+#define SND_SOC_DAIFMT_IB_IF (1 << 11) /* invert bclk + frm */
118+
119+ 3) hw clock masters
120+ This is wrt the codec, the inverse is true for the interface
121+ i.e. if the codec is clk and frm master then the interface is
122+ clk and frame slave.
123+
124+#define SND_SOC_DAIFMT_CBM_CFM (1 << 12) /* codec clk & frm master */
125+#define SND_SOC_DAIFMT_CBS_CFM (1 << 13) /* codec clk slave & frm master */
126+#define SND_SOC_DAIFMT_CBM_CFS (1 << 14) /* codec clk master & frame slave */
127+#define SND_SOC_DAIFMT_CBS_CFS (1 << 15) /* codec clk & frm slave */
128+
129+At least one option from each section must be selected. Multiple selections are
130+also supported e.g.
131+
132+ .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_RIGHT_J | \
133+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_NB_IF | SND_SOC_DAIFMT_IB_NF | \
134+ SND_SOC_DAIFMT_IB_IF
135+
136+
137+tdm:
138+------
139+This field defines the Time Division Multiplexing left and right word
140+positions for the DAI mode if applicable. Set to SND_SOC_DAITDM_LRDW(0,0) for
141+no TDM.
142+
143+
144+pcmfmt:
145+---------
146+The hardware PCM format. This describes the PCM formats supported by the DAI
147+mode e.g.
148+
149+ .pcmfmt = SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \
150+ SNDRV_PCM_FORMAT_S24_3LE
151+
152+pcmrate:
153+----------
154+The PCM sample rates supported by the DAI mode. e.g.
155+
156+ .pcmrate = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
157+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
158+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000
159+
160+
161+pcmdir:
162+---------
163+The stream directions supported by this mode. e.g. playback and capture
164+
165+
166+flags:
167+--------
168+The DAI hardware flags supported by the mode.
169+
170+/* use bfs mclk divider mode (BCLK = MCLK / x) */
171+#define SND_SOC_DAI_BFS_DIV 0x1
172+/* use bfs rate mulitplier (BCLK = RATE * x)*/
173+#define SND_SOC_DAI_BFS_RATE 0x2
174+/* use bfs rcw multiplier (BCLK = RATE * CHN * WORD SIZE) */
175+#define SND_SOC_DAI_BFS_RCW 0x4
176+/* capture and playback can use different clocks */
177+#define SND_SOC_DAI_ASYNC 0x8
178+
179+NOTE: Bitclock division and mulitiplication modes can be safely matched by the
180+core logic.
181+
182+
183+fs:
184+-----
185+The FS supported by this DAI mode FS is the ratio between the system clock and
186+the sample rate. See above
187+
188+bfs:
189+------
190+BFS is the ratio of BCLK to MCLK or the ratio of BCLK to sample rate (this
191+depends on the codec or CPU DAI).
192+
193+The BFS supported by the DAI mode. This can either be the ratio between the
194+bitclock (BCLK) and the sample rate OR the ratio between the system clock and
195+the sample rate. Depends on the flags above.
196+
197+priv:
198+-----
199+private codec mode data.
200+
201+
202+
203+Examples
204+========
205+
206+Note that Codec DAI and CPU DAI examples are interchangeable in these examples
207+as long as the bus master is reversed. i.e.
208+
209+ SND_SOC_DAIFMT_CBM_CFM would become SND_SOC_DAIFMT_CBS_CFS
210+ and vice versa.
211+
212+This applies to all SND_SOC_DAIFMT_CB*_CF*.
213+
214+Example 1
215+---------
216+
217+Simple codec that only runs at 8k & 48k @ 256FS in master mode, can generate a
218+BCLK of either MCLK/2 or MCLK/4.
219+
220+ /* codec master */
221+ {
222+ .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM,
223+ .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
224+ .pcmrate = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000,
225+ .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE,
226+ .flags = SND_SOC_DAI_BFS_DIV,
227+ .fs = 256,
228+ .bfs = SND_SOC_FSBD(2) | SND_SOC_FSBD(4),
229+ }
230+
231+
232+Example 2
233+---------
234+Simple codec that only runs at 8k & 48k @ 256FS in master mode, can generate a
235+BCLK of either Rate * 32 or Rate * 64.
236+
237+ /* codec master */
238+ {
239+ .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM,
240+ .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
241+ .pcmrate = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000,
242+ .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE,
243+ .flags = SND_SOC_DAI_BFS_RATE,
244+ .fs = 256,
245+ .bfs = 32,
246+ },
247+ {
248+ .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM,
249+ .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
250+ .pcmrate = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000,
251+ .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE,
252+ .flags = SND_SOC_DAI_BFS_RATE,
253+ .fs = 256,
254+ .bfs = 64,
255+ },
256+
257+
258+Example 3
259+---------
260+Codec that runs at 8k & 48k @ 256FS in master mode, can generate a BCLK that
261+is a multiple of Rate * channels * word size. (RCW) i.e.
262+
263+ BCLK = 8000 * 2 * 16 (8k, stereo, 16bit)
264+ = 256kHz
265+
266+This codecs supports a RCW multiple of 1,2
267+
268+ {
269+ .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM,
270+ .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
271+ .pcmrate = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000,
272+ .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE,
273+ .flags = SND_SOC_DAI_BFS_RCW,
274+ .fs = 256,
275+ .bfs = SND_SOC_FSBW(1) | SND_SOC_FSBW(2),
276+ }
277+
278+
279+Example 4
280+---------
281+Codec that only runs at 8k & 48k @ 256FS in master mode, can generate a
282+BCLK of either Rate * 32 or Rate * 64. Codec can also run in slave mode as long
283+as BCLK is rate * 32 or rate * 64.
284+
285+ /* codec master */
286+ {
287+ .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM,
288+ .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
289+ .pcmrate = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000,
290+ .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE,
291+ .flags = SND_SOC_DAI_BFS_RATE,
292+ .fs = 256,
293+ .bfs = 32,
294+ },
295+ {
296+ .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM,
297+ .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
298+ .pcmrate = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000,
299+ .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE,
300+ .flags = SND_SOC_DAI_BFS_RATE,
301+ .fs = 256,
302+ .bfs = 64,
303+ },
304+
305+ /* codec slave */
306+ {
307+ .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS,
308+ .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
309+ .pcmdir = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000,
310+ .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE,
311+ .flags = SND_SOC_DAI_BFS_RATE,
312+ .fs = SND_SOC_FS_ALL,
313+ .bfs = 32,
314+ },
315+ {
316+ .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS,
317+ .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
318+ .pcmdir = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000,
319+ .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE,
320+ .flags = SND_SOC_DAI_BFS_RATE,
321+ .fs = SND_SOC_FS_ALL,
322+ .bfs = 64,
323+ },
324+
325+
326+Example 5
327+---------
328+Codec that only runs at 8k, 16k, 32k, 48k, 96k @ 128FS, 192FS & 256FS in master
329+mode and can generate a BCLK of MCLK / (1,2,4,8,16). Codec can also run in slave
330+mode as and does not care about FS or BCLK (as long as there is enough bandwidth).
331+
332+ #define CODEC_FSB \
333+ (SND_SOC_FSBD(1) | SND_SOC_FSBD(2) | SND_SOC_FSBD(4) | \
334+ SND_SOC_FSBD(8) | SND_SOC_FSBD(16))
335+
336+ #define CODEC_RATES \
337+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 |\
338+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
339+
340+ /* codec master @ 128, 192 & 256 FS */
341+ {
342+ .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM,
343+ .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
344+ .pcmrate = CODEC_RATES,
345+ .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE,
346+ .flags = SND_SOC_DAI_BFS_DIV,
347+ .fs = 128,
348+ .bfs = CODEC_FSB,
349+ },
350+
351+ {
352+ .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM,
353+ .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
354+ .pcmrate = CODEC_RATES,
355+ .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE,
356+ .flags = SND_SOC_DAI_BFS_DIV,
357+ .fs = 192,
358+ .bfs = CODEC_FSB
359+ },
360+
361+ {
362+ .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM,
363+ .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
364+ .pcmrate = CODEC_RATES,
365+ .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE,
366+ .flags = SND_SOC_DAI_BFS_DIV,
367+ .fs = 256,
368+ .bfs = CODEC_FSB,
369+ },
370+
371+ /* codec slave */
372+ {
373+ .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS,
374+ .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
375+ .pcmrate = CODEC_RATES,
376+ .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE,
377+ .fs = SND_SOC_FS_ALL,
378+ .bfs = SND_SOC_FSB_ALL,
379+ },
380+
381+
382+Example 6
383+---------
384+Codec that only runs at 8k, 44.1k, 48k @ different FS in master mode (for use
385+with a fixed MCLK) and can generate a BCLK of MCLK / (1,2,4,8,16).
386+Codec can also run in slave mode as and does not care about FS or BCLK (as long
387+as there is enough bandwidth). Codec can support 16, 24 and 32 bit PCM sample
388+sizes.
389+
390+ #define CODEC_FSB \
391+ (SND_SOC_FSBD(1) | SND_SOC_FSBD(2) | SND_SOC_FSBD(4) | \
392+ SND_SOC_FSBD(8) | SND_SOC_FSBD(16))
393+
394+ #define CODEC_PCM_FORMATS \
395+ (SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \
396+ SNDRV_PCM_FORMAT_S24_3LE | SNDRV_PCM_FORMAT_S24_LE | SNDRV_PCM_FORMAT_S32_LE)
397+
398+ /* codec master */
399+ {
400+ .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM,
401+ .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
402+ .pcmrate = SNDRV_PCM_RATE_8000,
403+ .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE,
404+ .flags = SND_SOC_DAI_BFS_DIV,
405+ .fs = 1536,
406+ .bfs = CODEC_FSB,
407+ },
408+
409+ {
410+ .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM,
411+ .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
412+ .pcmrate = SNDRV_PCM_RATE_44100,
413+ .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE,
414+ .flags = SND_SOC_DAI_BFS_DIV,
415+ .fs = 272,
416+ .bfs = CODEC_FSB,
417+ },
418+
419+ {
420+ .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM,
421+ .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
422+ .pcmrate = SNDRV_PCM_RATE_48000,
423+ .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE,
424+ .flags = SND_SOC_DAI_BFS_DIV,
425+ .fs = 256,
426+ .bfs = CODEC_FSB,
427+ },
428+
429+ /* codec slave */
430+ {
431+ .fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS,
432+ .pcmfmt = SNDRV_PCM_FORMAT_S16_LE,
433+ .pcmrate = CODEC_RATES,
434+ .pcmdir = SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE,
435+ .fs = SND_SOC_FS_ALL,
436+ .bfs = SND_SOC_FSB_ALL,
437+ },
438+
439+
440+Example 7
441+---------
442+AC97 Codec that does not support VRA (i.e only runs at 48k).
443+
444+ #define AC97_DIR \
445+ (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
446+
447+ #define AC97_PCM_FORMATS \
448+ (SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S18_3LE | \
449+ SNDRV_PCM_FORMAT_S20_3LE)
450+
451+ /* AC97 with no VRA */
452+ {
453+ .pcmfmt = AC97_PCM_FORMATS,
454+ .pcmrate = SNDRV_PCM_RATE_48000,
455+ }
456+
457+
458+Example 8
459+---------
460+
461+CPU DAI that supports 8k - 48k @ 256FS and BCLK = MCLK / 4 in master mode.
462+Slave mode (CPU DAI is FRAME master) supports 8k - 96k at any FS as long as
463+BCLK = 64 * rate. (Intel XScale I2S controller).
464+
465+ #define PXA_I2S_DAIFMT \
466+ (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF)
467+
468+ #define PXA_I2S_DIR \
469+ (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
470+
471+ #define PXA_I2S_RATES \
472+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
473+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
474+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
475+
476+ /* priv is divider */
477+ static struct snd_soc_dai_mode pxa2xx_i2s_modes[] = {
478+ /* pxa2xx I2S frame and clock master modes */
479+ {
480+ .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
481+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
482+ .pcmrate = SNDRV_PCM_RATE_8000,
483+ .pcmdir = PXA_I2S_DIR,
484+ .flags = SND_SOC_DAI_BFS_DIV,
485+ .fs = 256,
486+ .bfs = SND_SOC_FSBD(4),
487+ .priv = 0x48,
488+ },
489+ {
490+ .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
491+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
492+ .pcmrate = SNDRV_PCM_RATE_11025,
493+ .pcmdir = PXA_I2S_DIR,
494+ .flags = SND_SOC_DAI_BFS_DIV,
495+ .fs = 256,
496+ .bfs = SND_SOC_FSBD(4),
497+ .priv = 0x34,
498+ },
499+ {
500+ .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
501+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
502+ .pcmrate = SNDRV_PCM_RATE_16000,
503+ .pcmdir = PXA_I2S_DIR,
504+ .flags = SND_SOC_DAI_BFS_DIV,
505+ .fs = 256,
506+ .bfs = SND_SOC_FSBD(4),
507+ .priv = 0x24,
508+ },
509+ {
510+ .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
511+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
512+ .pcmrate = SNDRV_PCM_RATE_22050,
513+ .pcmdir = PXA_I2S_DIR,
514+ .flags = SND_SOC_DAI_BFS_DIV,
515+ .fs = 256,
516+ .bfs = SND_SOC_FSBD(4),
517+ .priv = 0x1a,
518+ },
519+ {
520+ .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
521+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
522+ .pcmrate = SNDRV_PCM_RATE_44100,
523+ .pcmdir = PXA_I2S_DIR,
524+ .flags = SND_SOC_DAI_BFS_DIV,
525+ .fs = 256,
526+ .bfs = SND_SOC_FSBD(4),
527+ .priv = 0xd,
528+ },
529+ {
530+ .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
531+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
532+ .pcmrate = SNDRV_PCM_RATE_48000,
533+ .pcmdir = PXA_I2S_DIR,
534+ .flags = SND_SOC_DAI_BFS_DIV,
535+ .fs = 256,
536+ .bfs = SND_SOC_FSBD(4),
537+ .priv = 0xc,
538+ },
539+
540+ /* pxa2xx I2S frame master and clock slave mode */
541+ {
542+ .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBM_CFS,
543+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
544+ .pcmrate = PXA_I2S_RATES,
545+ .pcmdir = PXA_I2S_DIR,
546+ .fs = SND_SOC_FS_ALL,
547+ .flags = SND_SOC_DAI_BFS_RATE,
548+ .bfs = 64,
549+ .priv = 0x48,
550+ },
551+};
552Index: linux-2.6-pxa-new/Documentation/sound/alsa/soc/clocking.txt
553===================================================================
554--- /dev/null
555+++ linux-2.6-pxa-new/Documentation/sound/alsa/soc/clocking.txt
556@@ -0,0 +1,314 @@
557+Audio Clocking
558+==============
559+
560+This text describes the audio clocking terms in ASoC and digital audio in
561+general. Note: Audio clocking can be complex !
562+
563+
564+Master Clock
565+------------
566+
567+Every audio subsystem is driven by a master clock (sometimes refered to as MCLK
568+or SYSCLK). This audio master clock can be derived from a number of sources
569+(e.g. crystal, PLL, CPU clock) and is responsible for producing the correct
570+audio playback and capture sample rates.
571+
572+Some master clocks (e.g. PLL's and CPU based clocks) are configuarble in that
573+their speed can be altered by software (depending on the system use and to save
574+power). Other master clocks are fixed at at set frequency (i.e. crystals).
575+
576+
577+DAI Clocks
578+----------
579+The Digital Audio Interface is usually driven by a Bit Clock (often referred to
580+as BCLK). This clock is used to drive the digital audio data across the link
581+between the codec and CPU.
582+
583+The DAI also has a frame clock to signal the start of each audio frame. This
584+clock is sometimes referred to as LRC (left right clock) or FRAME. This clock
585+runs at exactly the sample rate (LRC = Rate).
586+
587+Bit Clock can be generated as follows:-
588+
589+BCLK = MCLK / x
590+
591+ or
592+
593+BCLK = LRC * x
594+
595+ or
596+
597+BCLK = LRC * Channels * Word Size
598+
599+This relationship depends on the codec or SoC CPU in particular. ASoC can quite
600+easily match BCLK generated by division (SND_SOC_DAI_BFS_DIV) with BCLK by
601+multiplication (SND_SOC_DAI_BFS_RATE) or BCLK generated by
602+Rate * Channels * Word size (RCW or SND_SOC_DAI_BFS_RCW).
603+
604+
605+ASoC Clocking
606+-------------
607+
608+The ASoC core determines the clocking for each particular configuration at
609+runtime. This is to allow for dynamic audio clocking wereby the audio clock is
610+variable and depends on the system state or device usage scenario. i.e. a voice
611+call requires slower clocks (and hence less power) than MP3 playback.
612+
613+ASoC will call the config_sysclock() function for the target machine during the
614+audio parameters configuration. The function is responsible for then clocking
615+the machine audio subsytem and returning the audio clock speed to the core.
616+This function should also call the codec and cpu DAI clock_config() functions
617+to configure their respective internal clocking if required.
618+
619+
620+ASoC Clocking Control Flow
621+--------------------------
622+
623+The ASoC core will call the machine drivers config_sysclock() when most of the
624+DAI capabilities are known. The machine driver is then responsible for calling
625+the codec and/or CPU DAI drivers with the selected capabilities and the current
626+MCLK. Note that the machine driver is also resonsible for setting the MCLK (and
627+enabling it).
628+
629+ (1) Match Codec and CPU DAI capabilities. At this point we have
630+ matched the majority of the DAI fields and now need to make sure this
631+ mode is currently clockable.
632+
633+ (2) machine->config_sysclk() is now called with the matched DAI FS, sample
634+ rate and BCLK master. This function then gets/sets the current audio
635+ clock (depening on usage) and calls the codec and CPUI DAI drivers with
636+ the FS, rate, BCLK master and MCLK.
637+
638+ (3) Codec/CPU DAI config_sysclock(). This function checks that the FS, rate,
639+ BCLK master and MCLK are acceptable for the codec or CPU DAI. It also
640+ sets the DAI internal state to work with said clocks.
641+
642+The config_sysclk() functions for CPU, codec and machine should return the MCLK
643+on success and 0 on failure.
644+
645+
646+Examples (b = BCLK, l = LRC)
647+============================
648+
649+Example 1
650+---------
651+
652+Simple codec that only runs at 48k @ 256FS in master mode.
653+
654+CPU only runs as slave DAI, however it generates a variable MCLK.
655+
656+ -------- ---------
657+ | | <----mclk--- | |
658+ | Codec |b -----------> | CPU |
659+ | |l -----------> | |
660+ | | | |
661+ -------- ---------
662+
663+The codec driver has the following config_sysclock()
664+
665+ static unsigned int config_sysclk(struct snd_soc_codec_dai *dai,
666+ struct snd_soc_clock_info *info, unsigned int clk)
667+ {
668+ /* make sure clock is 256 * rate */
669+ if(info->rate << 8 == clk) {
670+ dai->mclk = clk;
671+ return clk;
672+ }
673+
674+ return 0;
675+ }
676+
677+The CPU I2S DAI driver has the following config_sysclk()
678+
679+ static unsigned int config_sysclk(struct snd_soc_codec_dai *dai,
680+ struct snd_soc_clock_info *info, unsigned int clk)
681+ {
682+ /* can we support this clk */
683+ if(set_audio_clk(clk) < 0)
684+ return -EINVAL;
685+
686+ dai->mclk = clk;
687+ return dai->clk;
688+ }
689+
690+The machine driver config_sysclk() in this example is as follows:-
691+
692+ unsigned int machine_config_sysclk(struct snd_soc_pcm_runtime *rtd,
693+ struct snd_soc_clock_info *info)
694+ {
695+ int clk = info->rate * info->fs;
696+
697+ /* check that CPU can deliver clock */
698+ if(rtd->cpu_dai->config_sysclk(rtd->cpu_dai, info, clk) < 0)
699+ return -EINVAL;
700+
701+ /* can codec work with this clock */
702+ return rtd->codec_dai->config_sysclk(rtd->codec_dai, info, clk);
703+ }
704+
705+
706+Example 2
707+---------
708+
709+Codec that can master at 8k and 48k at various FS (and hence supports a fixed
710+set of input MCLK's) and can also be slave at various FS .
711+
712+The CPU can master at 8k and 48k @256 FS and can be slave at any FS.
713+
714+MCLK is a 12.288MHz crystal on this machine.
715+
716+ -------- ---------
717+ | | <---xtal---> | |
718+ | Codec |b <----------> | CPU |
719+ | |l <----------> | |
720+ | | | |
721+ -------- ---------
722+
723+
724+The codec driver has the following config_sysclock()
725+
726+ /* supported input clocks */
727+ const static int hifi_clks[] = {11289600, 12000000, 12288000,
728+ 16934400, 18432000};
729+
730+ static unsigned int config_hsysclk(struct snd_soc_codec_dai *dai,
731+ struct snd_soc_clock_info *info, unsigned int clk)
732+ {
733+ int i;
734+
735+ /* is clk supported */
736+ for(i = 0; i < ARRAY_SIZE(hifi_clks); i++) {
737+ if(clk == hifi_clks[i]) {
738+ dai->mclk = clk;
739+ return clk;
740+ }
741+ }
742+
743+ /* this clk is not supported */
744+ return 0;
745+ }
746+
747+The CPU I2S DAI driver has the following config_sysclk()
748+
749+ static unsigned int config_sysclk(struct snd_soc_codec_dai *dai,
750+ struct snd_soc_clock_info *info, unsigned int clk)
751+ {
752+ /* are we master or slave */
753+ if (info->bclk_master &
754+ (SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_CBM_CFS)) {
755+
756+ /* we can only master @ 256FS */
757+ if(info->rate << 8 == clk) {
758+ dai->mclk = clk;
759+ return dai->mclk;
760+ }
761+ } else {
762+ /* slave we can run at any FS */
763+ dai->mclk = clk;
764+ return dai->mclk;
765+ }
766+
767+ /* not supported */
768+ return dai->clk;
769+ }
770+
771+The machine driver config_sysclk() in this example is as follows:-
772+
773+ unsigned int machine_config_sysclk(struct snd_soc_pcm_runtime *rtd,
774+ struct snd_soc_clock_info *info)
775+ {
776+ int clk = 12288000; /* 12.288MHz */
777+
778+ /* who's driving the link */
779+ if (info->bclk_master &
780+ (SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_CBM_CFS)) {
781+ /* codec master */
782+
783+ /* check that CPU can work with clock */
784+ if(rtd->cpu_dai->config_sysclk(rtd->cpu_dai, info, clk) < 0)
785+ return -EINVAL;
786+
787+ /* can codec work with this clock */
788+ return rtd->codec_dai->config_sysclk(rtd->codec_dai, info, clk);
789+ } else {
790+ /* cpu master */
791+
792+ /* check that codec can work with clock */
793+ if(rtd->codec_dai->config_sysclk(rtd->codec_dai, info, clk) < 0)
794+ return -EINVAL;
795+
796+ /* can CPU work with this clock */
797+ return rtd->cpu_dai->config_sysclk(rtd->cpu_dai, info, clk);
798+ }
799+ }
800+
801+
802+
803+Example 3
804+---------
805+
806+Codec that masters at 8k ... 48k @256 FS. Codec can also be slave and
807+doesn't care about FS. The codec has an internal PLL and dividers to generate
808+the necessary internal clocks (for 256FS).
809+
810+CPU can only be slave and doesn't care about FS.
811+
812+MCLK is a non controllable 13MHz clock from the CPU.
813+
814+
815+ -------- ---------
816+ | | <----mclk--- | |
817+ | Codec |b <----------> | CPU |
818+ | |l <----------> | |
819+ | | | |
820+ -------- ---------
821+
822+The codec driver has the following config_sysclock()
823+
824+ /* valid PCM clock dividers * 2 */
825+ static int pcm_divs[] = {2, 6, 11, 4, 8, 12, 16};
826+
827+ static unsigned int config_vsysclk(struct snd_soc_codec_dai *dai,
828+ struct snd_soc_clock_info *info, unsigned int clk)
829+ {
830+ int i, j, best_clk = info->fs * info->rate;
831+
832+ /* can we run at this clk without the PLL ? */
833+ for (i = 0; i < ARRAY_SIZE(pcm_divs); i++) {
834+ if ((best_clk >> 1) * pcm_divs[i] == clk) {
835+ dai->pll_in = 0;
836+ dai->clk_div = pcm_divs[i];
837+ dai->mclk = best_clk;
838+ return dai->mclk;
839+ }
840+ }
841+
842+ /* now check for PLL support */
843+ for (i = 0; i < ARRAY_SIZE(pll_div); i++) {
844+ if (pll_div[i].pll_in == clk) {
845+ for (j = 0; j < ARRAY_SIZE(pcm_divs); j++) {
846+ if (pll_div[i].pll_out == pcm_divs[j] * (best_clk >> 1)) {
847+ dai->pll_in = clk;
848+ dai->pll_out = pll_div[i].pll_out;
849+ dai->clk_div = pcm_divs[j];
850+ dai->mclk = best_clk;
851+ return dai->mclk;
852+ }
853+ }
854+ }
855+ }
856+
857+ /* this clk is not supported */
858+ return 0;
859+ }
860+
861+
862+The CPU I2S DAI driver has the does not need a config_sysclk() as it can slave
863+at any FS.
864+
865+ unsigned int config_sysclk(struct snd_soc_pcm_runtime *rtd,
866+ struct snd_soc_clock_info *info)
867+ {
868+ /* codec has pll that generates mclk from 13MHz xtal */
869+ return rtd->codec_dai->config_sysclk(rtd->codec_dai, info, 13000000);
870+ }
871Index: linux-2.6-pxa-new/Documentation/sound/alsa/soc/codec.txt
872===================================================================
873--- /dev/null
874+++ linux-2.6-pxa-new/Documentation/sound/alsa/soc/codec.txt
875@@ -0,0 +1,232 @@
876+ASoC Codec Driver
877+=================
878+
879+The codec driver is generic and hardware independent code that configures the
880+codec to provide audio capture and playback. It should contain no code that is
881+specific to the target platform or machine. All platform and machine specific
882+code should be added to the platform and machine drivers respectively.
883+
884+Each codec driver must provide the following features:-
885+
886+ 1) Digital audio interface (DAI) description
887+ 2) Digital audio interface configuration
888+ 3) PCM's description
889+ 4) Codec control IO - using I2C, 3 Wire(SPI) or both API's
890+ 5) Mixers and audio controls
891+ 6) Sysclk configuration
892+ 7) Codec audio operations
893+
894+Optionally, codec drivers can also provide:-
895+
896+ 8) DAPM description.
897+ 9) DAPM event handler.
898+10) DAC Digital mute control.
899+
900+It's probably best to use this guide in conjuction with the existing codec
901+driver code in sound/soc/codecs/
902+
903+ASoC Codec driver breakdown
904+===========================
905+
906+1 - Digital Audio Interface (DAI) description
907+---------------------------------------------
908+The DAI is a digital audio data transfer link between the codec and host SoC
909+CPU. It typically has data transfer capabilities in both directions
910+(playback and capture) and can run at a variety of different speeds.
911+Supported interfaces currently include AC97, I2S and generic PCM style links.
912+Please read DAI.txt for implementation information.
913+
914+
915+2 - Digital Audio Interface (DAI) configuration
916+-----------------------------------------------
917+DAI configuration is handled by the codec_pcm_prepare function and is
918+responsible for configuring and starting the DAI on the codec. This can be
919+called multiple times and is atomic. It can access the runtime parameters.
920+
921+This usually consists of a large function with numerous switch statements to
922+set up each configuration option. These options are set by the core at runtime.
923+
924+
925+3 - Codec PCM's
926+---------------
927+Each codec must have it's PCM's defined. This defines the number of channels,
928+stream names, callbacks and codec name. It is also used to register the DAI
929+with the ASoC core. The PCM structure also associates the DAI capabilities with
930+the ALSA PCM.
931+
932+e.g.
933+
934+static struct snd_soc_pcm_codec wm8731_pcm_client = {
935+ .name = "WM8731",
936+ .playback = {
937+ .stream_name = "Playback",
938+ .channels_min = 1,
939+ .channels_max = 2,
940+ },
941+ .capture = {
942+ .stream_name = "Capture",
943+ .channels_min = 1,
944+ .channels_max = 2,
945+ },
946+ .config_sysclk = wm8731_config_sysclk,
947+ .ops = {
948+ .prepare = wm8731_pcm_prepare,
949+ },
950+ .caps = {
951+ .num_modes = ARRAY_SIZE(wm8731_hwfmt),
952+ .modes = &wm8731_hwfmt[0],
953+ },
954+};
955+
956+
957+4 - Codec control IO
958+--------------------
959+The codec can ususally be controlled via an I2C or SPI style interface (AC97
960+combines control with data in the DAI). The codec drivers will have to provide
961+functions to read and write the codec registers along with supplying a register
962+cache:-
963+
964+ /* IO control data and register cache */
965+ void *control_data; /* codec control (i2c/3wire) data */
966+ void *reg_cache;
967+
968+Codec read/write should do any data formatting and call the hardware read write
969+below to perform the IO. These functions are called by the core and alsa when
970+performing DAPM or changing the mixer:-
971+
972+ unsigned int (*read)(struct snd_soc_codec *, unsigned int);
973+ int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);
974+
975+Codec hardware IO functions - usually points to either the I2C, SPI or AC97
976+read/write:-
977+
978+ hw_write_t hw_write;
979+ hw_read_t hw_read;
980+
981+
982+5 - Mixers and audio controls
983+-----------------------------
984+All the codec mixers and audio controls can be defined using the convenience
985+macros defined in soc.h.
986+
987+ #define SOC_SINGLE(xname, reg, shift, mask, invert)
988+
989+Defines a single control as follows:-
990+
991+ xname = Control name e.g. "Playback Volume"
992+ reg = codec register
993+ shift = control bit(s) offset in register
994+ mask = control bit size(s) e.g. mask of 7 = 3 bits
995+ invert = the control is inverted
996+
997+Other macros include:-
998+
999+ #define SOC_DOUBLE(xname, reg, shift_left, shift_right, mask, invert)
1000+
1001+A stereo control
1002+
1003+ #define SOC_DOUBLE_R(xname, reg_left, reg_right, shift, mask, invert)
1004+
1005+A stereo control spanning 2 registers
1006+
1007+ #define SOC_ENUM_SINGLE(xreg, xshift, xmask, xtexts)
1008+
1009+Defines an single enumerated control as follows:-
1010+
1011+ xreg = register
1012+ xshift = control bit(s) offset in register
1013+ xmask = control bit(s) size
1014+ xtexts = pointer to array of strings that describe each setting
1015+
1016+ #define SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, xtexts)
1017+
1018+Defines a stereo enumerated control
1019+
1020+
1021+6 - System clock configuration.
1022+-------------------------------
1023+The system clock that drives the audio subsystem can change depending on sample
1024+rate and the system power state. i.e.
1025+
1026+o Higher sample rates sometimes need a higher system clock.
1027+o Low system power states can sometimes limit the available clocks.
1028+
1029+This function is a callback that the machine driver can call to set and
1030+determine if the clock and sample rate combination is supported by the codec at
1031+the present time (and system state).
1032+
1033+NOTE: If the codec has a PLL then it has a lot more flexability wrt clock and
1034+sample rate combinations.
1035+
1036+Your config_sysclock function should return the MCLK if it's a valid
1037+combination for your codec else 0;
1038+
1039+Please read clocking.txt now.
1040+
1041+
1042+7 - Codec Audio Operations
1043+--------------------------
1044+The codec driver also supports the following alsa operations:-
1045+
1046+/* SoC audio ops */
1047+struct snd_soc_ops {
1048+ int (*startup)(snd_pcm_substream_t *);
1049+ void (*shutdown)(snd_pcm_substream_t *);
1050+ int (*hw_params)(snd_pcm_substream_t *, snd_pcm_hw_params_t *);
1051+ int (*hw_free)(snd_pcm_substream_t *);
1052+ int (*prepare)(snd_pcm_substream_t *);
1053+};
1054+
1055+Please refer to the alsa driver PCM documentation for details.
1056+http://www.alsa-project.org/~iwai/writing-an-alsa-driver/c436.htm
1057+
1058+
1059+8 - DAPM description.
1060+---------------------
1061+The Dynamic Audio Power Management description describes the codec's power
1062+components, their relationships and registers to the ASoC core. Please read
1063+dapm.txt for details of building the description.
1064+
1065+Please also see the examples in other codec drivers.
1066+
1067+
1068+9 - DAPM event handler
1069+----------------------
1070+This function is a callback that handles codec domain PM calls and system
1071+domain PM calls (e.g. suspend and resume). It's used to put the codec to sleep
1072+when not in use.
1073+
1074+Power states:-
1075+
1076+ SNDRV_CTL_POWER_D0: /* full On */
1077+ /* vref/mid, clk and osc on, active */
1078+
1079+ SNDRV_CTL_POWER_D1: /* partial On */
1080+ SNDRV_CTL_POWER_D2: /* partial On */
1081+
1082+ SNDRV_CTL_POWER_D3hot: /* Off, with power */
1083+ /* everything off except vref/vmid, inactive */
1084+
1085+ SNDRV_CTL_POWER_D3cold: /* Everything Off, without power */
1086+
1087+
1088+10 - Codec DAC digital mute control.
1089+------------------------------------
1090+Most codecs have a digital mute before the DAC's that can be used to minimise
1091+any system noise. The mute stops any digital data from entering the DAC.
1092+
1093+A callback can be created that is called by the core for each codec DAI when the
1094+mute is applied or freed.
1095+
1096+i.e.
1097+
1098+static int wm8974_mute(struct snd_soc_codec *codec,
1099+ struct snd_soc_codec_dai *dai, int mute)
1100+{
1101+ u16 mute_reg = wm8974_read_reg_cache(codec, WM8974_DAC) & 0xffbf;
1102+ if(mute)
1103+ wm8974_write(codec, WM8974_DAC, mute_reg | 0x40);
1104+ else
1105+ wm8974_write(codec, WM8974_DAC, mute_reg);
1106+ return 0;
1107+}
1108Index: linux-2.6-pxa-new/Documentation/sound/alsa/soc/dapm.txt
1109===================================================================
1110--- /dev/null
1111+++ linux-2.6-pxa-new/Documentation/sound/alsa/soc/dapm.txt
1112@@ -0,0 +1,297 @@
1113+Dynamic Audio Power Management for Portable Devices
1114+===================================================
1115+
1116+1. Description
1117+==============
1118+
1119+Dynamic Audio Power Management (DAPM) is designed to allow portable Linux devices
1120+to use the minimum amount of power within the audio subsystem at all times. It
1121+is independent of other kernel PM and as such, can easily co-exist with the
1122+other PM systems.
1123+
1124+DAPM is also completely transparent to all user space applications as all power
1125+switching is done within the ASoC core. No code changes or recompiling are
1126+required for user space applications. DAPM makes power switching descisions based
1127+upon any audio stream (capture/playback) activity and audio mixer settings
1128+within the device.
1129+
1130+DAPM spans the whole machine. It covers power control within the entire audio
1131+subsystem, this includes internal codec power blocks and machine level power
1132+systems.
1133+
1134+There are 4 power domains within DAPM
1135+
1136+ 1. Codec domain - VREF, VMID (core codec and audio power)
1137+ Usually controlled at codec probe/remove and suspend/resume, although
1138+ can be set at stream time if power is not needed for sidetone, etc.
1139+
1140+ 2. Platform/Machine domain - physically connected inputs and outputs
1141+ Is platform/machine and user action specific, is configured by the
1142+ machine driver and responds to asynchronous events e.g when HP
1143+ are inserted
1144+
1145+ 3. Path domain - audio susbsystem signal paths
1146+ Automatically set when mixer and mux settings are changed by the user.
1147+ e.g. alsamixer, amixer.
1148+
1149+ 4. Stream domain - DAC's and ADC's.
1150+ Enabled and disabled when stream playback/capture is started and
1151+ stopped respectively. e.g. aplay, arecord.
1152+
1153+All DAPM power switching descisons are made automatically by consulting an audio
1154+routing map of the whole machine. This map is specific to each machine and
1155+consists of the interconnections between every audio component (including
1156+internal codec components). All audio components that effect power are called
1157+widgets hereafter.
1158+
1159+
1160+2. DAPM Widgets
1161+===============
1162+
1163+Audio DAPM widgets fall into a number of types:-
1164+
1165+ o Mixer - Mixes several analog signals into a single analog signal.
1166+ o Mux - An analog switch that outputs only 1 of it's inputs.
1167+ o PGA - A programmable gain amplifier or attenuation widget.
1168+ o ADC - Analog to Digital Converter
1169+ o DAC - Digital to Analog Converter
1170+ o Switch - An analog switch
1171+ o Input - A codec input pin
1172+ o Output - A codec output pin
1173+ o Headphone - Headphone (and optional Jack)
1174+ o Mic - Mic (and optional Jack)
1175+ o Line - Line Input/Output (and optional Jack)
1176+ o Speaker - Speaker
1177+ o Pre - Special PRE widget (exec before all others)
1178+ o Post - Special POST widget (exec after all others)
1179+
1180+(Widgets are defined in include/sound/soc-dapm.h)
1181+
1182+Widgets are usually added in the codec driver and the machine driver. There are
1183+convience macros defined in soc-dapm.h that can be used to quickly build a
1184+list of widgets of the codecs and machines DAPM widgets.
1185+
1186+Most widgets have a name, register, shift and invert. Some widgets have extra
1187+parameters for stream name and kcontrols.
1188+
1189+
1190+2.1 Stream Domain Widgets
1191+-------------------------
1192+
1193+Stream Widgets relate to the stream power domain and only consist of ADC's
1194+(analog to digital converters) and DAC's (digital to analog converters).
1195+
1196+Stream widgets have the following format:-
1197+
1198+SND_SOC_DAPM_DAC(name, stream name, reg, shift, invert),
1199+
1200+NOTE: the stream name must match the corresponding stream name in your codecs
1201+snd_soc_codec_dai.
1202+
1203+e.g. stream widgets for HiFi playback and capture
1204+
1205+SND_SOC_DAPM_DAC("HiFi DAC", "HiFi Playback", REG, 3, 1),
1206+SND_SOC_DAPM_ADC("HiFi ADC", "HiFi Capture", REG, 2, 1),
1207+
1208+
1209+2.2 Path Domain Widgets
1210+-----------------------
1211+
1212+Path domain widgets have a ability to control or effect the audio signal or
1213+audio paths within the audio subsystem. They have the following form:-
1214+
1215+SND_SOC_DAPM_PGA(name, reg, shift, invert, controls, num_controls)
1216+
1217+Any widget kcontrols can be set using the controls and num_controls members.
1218+
1219+e.g. Mixer widget (the kcontrols are declared first)
1220+
1221+/* Output Mixer */
1222+static const snd_kcontrol_new_t wm8731_output_mixer_controls[] = {
1223+SOC_DAPM_SINGLE("Line Bypass Switch", WM8731_APANA, 3, 1, 0),
1224+SOC_DAPM_SINGLE("Mic Sidetone Switch", WM8731_APANA, 5, 1, 0),
1225+SOC_DAPM_SINGLE("HiFi Playback Switch", WM8731_APANA, 4, 1, 0),
1226+};
1227+
1228+SND_SOC_DAPM_MIXER("Output Mixer", WM8731_PWR, 4, 1, wm8731_output_mixer_controls,
1229+ ARRAY_SIZE(wm8731_output_mixer_controls)),
1230+
1231+
1232+2.3 Platform/Machine domain Widgets
1233+-----------------------------------
1234+
1235+Machine widgets are different from codec widgets in that they don't have a
1236+codec register bit associated with them. A machine widget is assigned to each
1237+machine audio component (non codec) that can be independently powered. e.g.
1238+
1239+ o Speaker Amp
1240+ o Microphone Bias
1241+ o Jack connectors
1242+
1243+A machine widget can have an optional call back.
1244+
1245+e.g. Jack connector widget for an external Mic that enables Mic Bias
1246+when the Mic is inserted:-
1247+
1248+static int spitz_mic_bias(struct snd_soc_dapm_widget* w, int event)
1249+{
1250+ if(SND_SOC_DAPM_EVENT_ON(event))
1251+ set_scoop_gpio(&spitzscoop2_device.dev, SPITZ_SCP2_MIC_BIAS);
1252+ else
1253+ reset_scoop_gpio(&spitzscoop2_device.dev, SPITZ_SCP2_MIC_BIAS);
1254+
1255+ return 0;
1256+}
1257+
1258+SND_SOC_DAPM_MIC("Mic Jack", spitz_mic_bias),
1259+
1260+
1261+2.4 Codec Domain
1262+----------------
1263+
1264+The Codec power domain has no widgets and is handled by the codecs DAPM event
1265+handler. This handler is called when the codec powerstate is changed wrt to any
1266+stream event or by kernel PM events.
1267+
1268+
1269+2.5 Virtual Widgets
1270+-------------------
1271+
1272+Sometimes widgets exist in the codec or machine audio map that don't have any
1273+corresponding register bit for power control. In this case it's necessary to
1274+create a virtual widget - a widget with no control bits e.g.
1275+
1276+SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_DAPM_NOPM, 0, 0, NULL, 0),
1277+
1278+This can be used to merge to signal paths together in software.
1279+
1280+After all the widgets have been defined, they can then be added to the DAPM
1281+subsystem individually with a call to snd_soc_dapm_new_control().
1282+
1283+
1284+3. Codec Widget Interconnections
1285+================================
1286+
1287+Widgets are connected to each other within the codec and machine by audio
1288+paths (called interconnections). Each interconnection must be defined in order
1289+to create a map of all audio paths between widgets.
1290+This is easiest with a diagram of the codec (and schematic of the machine audio
1291+system), as it requires joining widgets together via their audio signal paths.
1292+
1293+i.e. from the WM8731 codec's output mixer (wm8731.c)
1294+
1295+The WM8731 output mixer has 3 inputs (sources)
1296+
1297+ 1. Line Bypass Input
1298+ 2. DAC (HiFi playback)
1299+ 3. Mic Sidetone Input
1300+
1301+Each input in this example has a kcontrol associated with it (defined in example
1302+above) and is connected to the output mixer via it's kcontrol name. We can now
1303+connect the destination widget (wrt audio signal) with it's source widgets.
1304+
1305+ /* output mixer */
1306+ {"Output Mixer", "Line Bypass Switch", "Line Input"},
1307+ {"Output Mixer", "HiFi Playback Switch", "DAC"},
1308+ {"Output Mixer", "Mic Sidetone Switch", "Mic Bias"},
1309+
1310+So we have :-
1311+
1312+ Destination Widget <=== Path Name <=== Source Widget
1313+
1314+Or:-
1315+
1316+ Sink, Path, Source
1317+
1318+Or :-
1319+
1320+ "Output Mixer" is connected to the "DAC" via the "HiFi Playback Switch".
1321+
1322+When there is no path name connecting widgets (e.g. a direct connection) we
1323+pass NULL for the path name.
1324+
1325+Interconnections are created with a call to:-
1326+
1327+snd_soc_dapm_connect_input(codec, sink, path, source);
1328+
1329+Finally, snd_soc_dapm_new_widgets(codec) must be called after all widgets and
1330+interconnections have been registered with the core. This causes the core to
1331+scan the codec and machine so that the internal DAPM state matches the
1332+physical state of the machine.
1333+
1334+
1335+3.1 Machine Widget Interconnections
1336+-----------------------------------
1337+Machine widget interconnections are created in the same way as codec ones and
1338+directly connect the codec pins to machine level widgets.
1339+
1340+e.g. connects the speaker out codec pins to the internal speaker.
1341+
1342+ /* ext speaker connected to codec pins LOUT2, ROUT2 */
1343+ {"Ext Spk", NULL , "ROUT2"},
1344+ {"Ext Spk", NULL , "LOUT2"},
1345+
1346+This allows the DAPM to power on and off pins that are connected (and in use)
1347+and pins that are NC respectively.
1348+
1349+
1350+4 Endpoint Widgets
1351+===================
1352+An endpoint is a start or end point (widget) of an audio signal within the
1353+machine and includes the codec. e.g.
1354+
1355+ o Headphone Jack
1356+ o Internal Speaker
1357+ o Internal Mic
1358+ o Mic Jack
1359+ o Codec Pins
1360+
1361+When a codec pin is NC it can be marked as not used with a call to
1362+
1363+snd_soc_dapm_set_endpoint(codec, "Widget Name", 0);
1364+
1365+The last argument is 0 for inactive and 1 for active. This way the pin and its
1366+input widget will never be powered up and consume power.
1367+
1368+This also applies to machine widgets. e.g. if a headphone is connected to a
1369+jack then the jack can be marked active. If the headphone is removed, then
1370+the headphone jack can be marked inactive.
1371+
1372+
1373+5 DAPM Widget Events
1374+====================
1375+
1376+Some widgets can register their interest with the DAPM core in PM events.
1377+e.g. A Speaker with an amplifier registers a widget so the amplifier can be
1378+powered only when the spk is in use.
1379+
1380+/* turn speaker amplifier on/off depending on use */
1381+static int corgi_amp_event(struct snd_soc_dapm_widget *w, int event)
1382+{
1383+ if (SND_SOC_DAPM_EVENT_ON(event))
1384+ set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_APM_ON);
1385+ else
1386+ reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_APM_ON);
1387+
1388+ return 0;
1389+}
1390+
1391+/* corgi machine dapm widgets */
1392+static const struct snd_soc_dapm_widget wm8731_dapm_widgets =
1393+ SND_SOC_DAPM_SPK("Ext Spk", corgi_amp_event);
1394+
1395+Please see soc-dapm.h for all other widgets that support events.
1396+
1397+
1398+5.1 Event types
1399+---------------
1400+
1401+The following event types are supported by event widgets.
1402+
1403+/* dapm event types */
1404+#define SND_SOC_DAPM_PRE_PMU 0x1 /* before widget power up */
1405+#define SND_SOC_DAPM_POST_PMU 0x2 /* after widget power up */
1406+#define SND_SOC_DAPM_PRE_PMD 0x4 /* before widget power down */
1407+#define SND_SOC_DAPM_POST_PMD 0x8 /* after widget power down */
1408+#define SND_SOC_DAPM_PRE_REG 0x10 /* before audio path setup */
1409+#define SND_SOC_DAPM_POST_REG 0x20 /* after audio path setup */
1410Index: linux-2.6-pxa-new/Documentation/sound/alsa/soc/machine.txt
1411===================================================================
1412--- /dev/null
1413+++ linux-2.6-pxa-new/Documentation/sound/alsa/soc/machine.txt
1414@@ -0,0 +1,114 @@
1415+ASoC Machine Driver
1416+===================
1417+
1418+The ASoC machine (or board) driver is the code that glues together the platform
1419+and codec drivers.
1420+
1421+The machine driver can contain codec and platform specific code. It registers
1422+the audio subsystem with the kernel as a platform device and is represented by
1423+the following struct:-
1424+
1425+/* SoC machine */
1426+struct snd_soc_machine {
1427+ char *name;
1428+
1429+ int (*probe)(struct platform_device *pdev);
1430+ int (*remove)(struct platform_device *pdev);
1431+
1432+ /* the pre and post PM functions are used to do any PM work before and
1433+ * after the codec and DAI's do any PM work. */
1434+ int (*suspend_pre)(struct platform_device *pdev, pm_message_t state);
1435+ int (*suspend_post)(struct platform_device *pdev, pm_message_t state);
1436+ int (*resume_pre)(struct platform_device *pdev);
1437+ int (*resume_post)(struct platform_device *pdev);
1438+
1439+ /* machine stream operations */
1440+ struct snd_soc_ops *ops;
1441+
1442+ /* CPU <--> Codec DAI links */
1443+ struct snd_soc_dai_link *dai_link;
1444+ int num_links;
1445+};
1446+
1447+probe()/remove()
1448+----------------
1449+probe/remove are optional. Do any machine specific probe here.
1450+
1451+
1452+suspend()/resume()
1453+------------------
1454+The machine driver has pre and post versions of suspend and resume to take care
1455+of any machine audio tasks that have to be done before or after the codec, DAI's
1456+and DMA is suspended and resumed. Optional.
1457+
1458+
1459+Machine operations
1460+------------------
1461+The machine specific audio operations can be set here. Again this is optional.
1462+
1463+
1464+Machine DAI Configuration
1465+-------------------------
1466+The machine DAI configuration glues all the codec and CPU DAI's together. It can
1467+also be used to set up the DAI system clock and for any machine related DAI
1468+initialisation e.g. the machine audio map can be connected to the codec audio
1469+map, unconnnected codec pins can be set as such. Please see corgi.c, spitz.c
1470+for examples.
1471+
1472+struct snd_soc_dai_link is used to set up each DAI in your machine. e.g.
1473+
1474+/* corgi digital audio interface glue - connects codec <--> CPU */
1475+static struct snd_soc_dai_link corgi_dai = {
1476+ .name = "WM8731",
1477+ .stream_name = "WM8731",
1478+ .cpu_dai = &pxa_i2s_dai,
1479+ .codec_dai = &wm8731_dai,
1480+ .init = corgi_wm8731_init,
1481+ .config_sysclk = corgi_config_sysclk,
1482+};
1483+
1484+struct snd_soc_machine then sets up the machine with it's DAI's. e.g.
1485+
1486+/* corgi audio machine driver */
1487+static struct snd_soc_machine snd_soc_machine_corgi = {
1488+ .name = "Corgi",
1489+ .dai_link = &corgi_dai,
1490+ .num_links = 1,
1491+ .ops = &corgi_ops,
1492+};
1493+
1494+
1495+Machine Audio Subsystem
1496+-----------------------
1497+
1498+The machine soc device glues the platform, machine and codec driver together.
1499+Private data can also be set here. e.g.
1500+
1501+/* corgi audio private data */
1502+static struct wm8731_setup_data corgi_wm8731_setup = {
1503+ .i2c_address = 0x1b,
1504+};
1505+
1506+/* corgi audio subsystem */
1507+static struct snd_soc_device corgi_snd_devdata = {
1508+ .machine = &snd_soc_machine_corgi,
1509+ .platform = &pxa2xx_soc_platform,
1510+ .codec_dev = &soc_codec_dev_wm8731,
1511+ .codec_data = &corgi_wm8731_setup,
1512+};
1513+
1514+
1515+Machine Power Map
1516+-----------------
1517+
1518+The machine driver can optionally extend the codec power map and to become an
1519+audio power map of the audio subsystem. This allows for automatic power up/down
1520+of speaker/HP amplifiers, etc. Codec pins can be connected to the machines jack
1521+sockets in the machine init function. See soc/pxa/spitz.c and dapm.txt for
1522+details.
1523+
1524+
1525+Machine Controls
1526+----------------
1527+
1528+Machine specific audio mixer controls can be added in the dai init function.
1529\ No newline at end of file
1530Index: linux-2.6-pxa-new/Documentation/sound/alsa/soc/overview.txt
1531===================================================================
1532--- /dev/null
1533+++ linux-2.6-pxa-new/Documentation/sound/alsa/soc/overview.txt
1534@@ -0,0 +1,83 @@
1535+ALSA SoC Layer
1536+==============
1537+
1538+The overall project goal of the ALSA System on Chip (ASoC) layer is to provide
1539+better ALSA support for embedded system on chip procesors (e.g. pxa2xx, au1x00,
1540+iMX, etc) and portable audio codecs. Currently there is some support in the
1541+kernel for SoC audio, however it has some limitations:-
1542+
1543+ * Currently, codec drivers are often tightly coupled to the underlying SoC
1544+ cpu. This is not ideal and leads to code duplication i.e. Linux now has 4
1545+ different wm8731 drivers for 4 different SoC platforms.
1546+
1547+ * There is no standard method to signal user initiated audio events.
1548+ e.g. Headphone/Mic insertion, Headphone/Mic detection after an insertion
1549+ event. These are quite common events on portable devices and ofter require
1550+ machine specific code to re route audio, enable amps etc after such an event.
1551+
1552+ * Current drivers tend to power up the entire codec when playing
1553+ (or recording) audio. This is fine for a PC, but tends to waste a lot of
1554+ power on portable devices. There is also no support for saving power via
1555+ changing codec oversampling rates, bias currents, etc.
1556+
1557+
1558+ASoC Design
1559+===========
1560+
1561+The ASoC layer is designed to address these issues and provide the following
1562+features :-
1563+
1564+ * Codec independence. Allows reuse of codec drivers on other platforms
1565+ and machines.
1566+
1567+ * Easy I2S/PCM audio interface setup between codec and SoC. Each SoC interface
1568+ and codec registers it's audio interface capabilities with the core and are
1569+ subsequently matched and configured when the application hw params are known.
1570+
1571+ * Dynamic Audio Power Management (DAPM). DAPM automatically sets the codec to
1572+ it's minimum power state at all times. This includes powering up/down
1573+ internal power blocks depending on the internal codec audio routing and any
1574+ active streams.
1575+
1576+ * Pop and click reduction. Pops and clicks can be reduced by powering the
1577+ codec up/down in the correct sequence (including using digital mute). ASoC
1578+ signals the codec when to change power states.
1579+
1580+ * Machine specific controls: Allow machines to add controls to the sound card
1581+ e.g. volume control for speaker amp.
1582+
1583+To achieve all this, ASoC basically splits an embedded audio system into 3
1584+components :-
1585+
1586+ * Codec driver: The codec driver is platform independent and contains audio
1587+ controls, audio interface capabilities, codec dapm definition and codec IO
1588+ functions.
1589+
1590+ * Platform driver: The platform driver contains the audio dma engine and audio
1591+ interface drivers (e.g. I2S, AC97, PCM) for that platform.
1592+
1593+ * Machine driver: The machine driver handles any machine specific controls and
1594+ audio events. i.e. turing on an amp at start of playback.
1595+
1596+
1597+Documentation
1598+=============
1599+
1600+The documentation is spilt into the following sections:-
1601+
1602+overview.txt: This file.
1603+
1604+codec.txt: Codec driver internals.
1605+
1606+DAI.txt: Description of Digital Audio Interface standards and how to configure
1607+a DAI within your codec and CPU DAI drivers.
1608+
1609+dapm.txt: Dynamic Audio Power Management
1610+
1611+platform.txt: Platform audio DMA and DAI.
1612+
1613+machine.txt: Machine driver internals.
1614+
1615+pop_clicks.txt: How to minimise audio artifacts.
1616+
1617+clocking.txt: ASoC clocking for best power performance.
1618\ No newline at end of file
1619Index: linux-2.6-pxa-new/Documentation/sound/alsa/soc/platform.txt
1620===================================================================
1621--- /dev/null
1622+++ linux-2.6-pxa-new/Documentation/sound/alsa/soc/platform.txt
1623@@ -0,0 +1,58 @@
1624+ASoC Platform Driver
1625+====================
1626+
1627+An ASoC platform driver can be divided into audio DMA and SoC DAI configuration
1628+and control. The platform drivers only target the SoC CPU and must have no board
1629+specific code.
1630+
1631+Audio DMA
1632+=========
1633+
1634+The platform DMA driver optionally supports the following alsa operations:-
1635+
1636+/* SoC audio ops */
1637+struct snd_soc_ops {
1638+ int (*startup)(snd_pcm_substream_t *);
1639+ void (*shutdown)(snd_pcm_substream_t *);
1640+ int (*hw_params)(snd_pcm_substream_t *, snd_pcm_hw_params_t *);
1641+ int (*hw_free)(snd_pcm_substream_t *);
1642+ int (*prepare)(snd_pcm_substream_t *);
1643+ int (*trigger)(snd_pcm_substream_t *, int);
1644+};
1645+
1646+The platform driver exports it's DMA functionailty via struct snd_soc_platform:-
1647+
1648+struct snd_soc_platform {
1649+ char *name;
1650+
1651+ int (*probe)(struct platform_device *pdev);
1652+ int (*remove)(struct platform_device *pdev);
1653+ int (*suspend)(struct platform_device *pdev, struct snd_soc_cpu_dai *cpu_dai);
1654+ int (*resume)(struct platform_device *pdev, struct snd_soc_cpu_dai *cpu_dai);
1655+
1656+ /* pcm creation and destruction */
1657+ int (*pcm_new)(snd_card_t *, struct snd_soc_codec_dai *, snd_pcm_t *);
1658+ void (*pcm_free)(snd_pcm_t *);
1659+
1660+ /* platform stream ops */
1661+ snd_pcm_ops_t *pcm_ops;
1662+};
1663+
1664+Please refer to the alsa driver documentation for details of audio DMA.
1665+http://www.alsa-project.org/~iwai/writing-an-alsa-driver/c436.htm
1666+
1667+An example DMA driver is soc/pxa/pxa2xx-pcm.c
1668+
1669+
1670+SoC DAI Drivers
1671+===============
1672+
1673+Each SoC DAI driver must provide the following features:-
1674+
1675+ 1) Digital audio interface (DAI) description
1676+ 2) Digital audio interface configuration
1677+ 3) PCM's description
1678+ 4) Sysclk configuration
1679+ 5) Suspend and resume (optional)
1680+
1681+Please see codec.txt for a description of items 1 - 4.
1682Index: linux-2.6-pxa-new/Documentation/sound/alsa/soc/pops_clicks.txt
1683===================================================================
1684--- /dev/null
1685+++ linux-2.6-pxa-new/Documentation/sound/alsa/soc/pops_clicks.txt
1686@@ -0,0 +1,52 @@
1687+Audio Pops and Clicks
1688+=====================
1689+
1690+Pops and clicks are unwanted audio artifacts caused by the powering up and down
1691+of components within the audio subsystem. This is noticable on PC's when an audio
1692+module is either loaded or unloaded (at module load time the sound card is
1693+powered up and causes a popping noise on the speakers).
1694+
1695+Pops and clicks can be more frequent on portable systems with DAPM. This is because
1696+the components within the subsystem are being dynamically powered depending on
1697+the audio usage and this can subsequently cause a small pop or click every time a
1698+component power state is changed.
1699+
1700+
1701+Minimising Playback Pops and Clicks
1702+===================================
1703+
1704+Playback pops in portable audio subsystems cannot be completely eliminated atm,
1705+however future audio codec hardware will have better pop and click supression.
1706+Pops can be reduced within playback by powering the audio components in a
1707+specific order. This order is different for startup and shutdown and follows
1708+some basic rules:-
1709+
1710+ Startup Order :- DAC --> Mixers --> Output PGA --> Digital Unmute
1711+
1712+ Shutdown Order :- Digital Mute --> Output PGA --> Mixers --> DAC
1713+
1714+This assumes that the codec PCM output path from the DAC is via a mixer and then
1715+a PGA (programmable gain amplifier) before being output to the speakers.
1716+
1717+
1718+Minimising Capture Pops and Clicks
1719+==================================
1720+
1721+Capture artifacts are somewhat easier to get rid as we can delay activating the
1722+ADC until all the pops have occured. This follows similar power rules to
1723+playback in that components are powered in a sequence depending upon stream
1724+startup or shutdown.
1725+
1726+ Startup Order - Input PGA --> Mixers --> ADC
1727+
1728+ Shutdown Order - ADC --> Mixers --> Input PGA
1729+
1730+
1731+Zipper Noise
1732+============
1733+An unwanted zipper noise can occur within the audio playback or capture stream
1734+when a volume control is changed near its maximum gain value. The zipper noise
1735+is heard when the gain increase or decrease changes the mean audio signal
1736+amplitude too quickly. It can be minimised by enabling the zero cross setting
1737+for each volume control. The ZC forces the gain change to occur when the signal
1738+crosses the zero amplitude line.
1739Index: linux-2.6-pxa-new/include/sound/ac97_codec.h
1740===================================================================
1741--- linux-2.6-pxa-new.orig/include/sound/ac97_codec.h
1742+++ linux-2.6-pxa-new/include/sound/ac97_codec.h
1743@@ -425,6 +425,7 @@ struct snd_ac97_build_ops {
1744
1745 struct snd_ac97_bus_ops {
1746 void (*reset) (struct snd_ac97 *ac97);
1747+ void (*warm_reset)(struct snd_ac97 *ac97);
1748 void (*write) (struct snd_ac97 *ac97, unsigned short reg, unsigned short val);
1749 unsigned short (*read) (struct snd_ac97 *ac97, unsigned short reg);
1750 void (*wait) (struct snd_ac97 *ac97);
1751Index: linux-2.6-pxa-new/include/sound/soc-dapm.h
1752===================================================================
1753--- /dev/null
1754+++ linux-2.6-pxa-new/include/sound/soc-dapm.h
1755@@ -0,0 +1,286 @@
1756+/*
1757+ * linux/sound/soc-dapm.h -- ALSA SoC Dynamic Audio Power Management
1758+ *
1759+ * Author: Liam Girdwood
1760+ * Created: Aug 11th 2005
1761+ * Copyright: Wolfson Microelectronics. PLC.
1762+ *
1763+ * This program is free software; you can redistribute it and/or modify
1764+ * it under the terms of the GNU General Public License version 2 as
1765+ * published by the Free Software Foundation.
1766+ */
1767+
1768+#ifndef __LINUX_SND_SOC_DAPM_H
1769+#define __LINUX_SND_SOC_DAPM_H
1770+
1771+#include <linux/device.h>
1772+#include <linux/types.h>
1773+#include <sound/control.h>
1774+#include <sound/soc.h>
1775+
1776+/* widget has no PM register bit */
1777+#define SND_SOC_NOPM -1
1778+
1779+/*
1780+ * SoC dynamic audio power managment
1781+ *
1782+ * We can have upto 4 power domains
1783+ * 1. Codec domain - VREF, VMID
1784+ * Usually controlled at codec probe/remove, although can be set
1785+ * at stream time if power is not needed for sidetone, etc.
1786+ * 2. Platform/Machine domain - physically connected inputs and outputs
1787+ * Is platform/machine and user action specific, is set in the machine
1788+ * driver and by userspace e.g when HP are inserted
1789+ * 3. Path domain - Internal codec path mixers
1790+ * Are automatically set when mixer and mux settings are
1791+ * changed by the user.
1792+ * 4. Stream domain - DAC's and ADC's.
1793+ * Enabled when stream playback/capture is started.
1794+ */
1795+
1796+/* codec domain */
1797+#define SND_SOC_DAPM_VMID(wname) \
1798+{ .id = snd_soc_dapm_vmid, .name = wname, .kcontrols = NULL, \
1799+ .num_kcontrols = 0}
1800+
1801+/* platform domain */
1802+#define SND_SOC_DAPM_INPUT(wname) \
1803+{ .id = snd_soc_dapm_input, .name = wname, .kcontrols = NULL, \
1804+ .num_kcontrols = 0}
1805+#define SND_SOC_DAPM_OUTPUT(wname) \
1806+{ .id = snd_soc_dapm_output, .name = wname, .kcontrols = NULL, \
1807+ .num_kcontrols = 0}
1808+#define SND_SOC_DAPM_MIC(wname, wevent) \
1809+{ .id = snd_soc_dapm_mic, .name = wname, .kcontrols = NULL, \
1810+ .num_kcontrols = 0, .event = wevent, \
1811+ .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD}
1812+#define SND_SOC_DAPM_HP(wname, wevent) \
1813+{ .id = snd_soc_dapm_hp, .name = wname, .kcontrols = NULL, \
1814+ .num_kcontrols = 0, .event = wevent, \
1815+ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD}
1816+#define SND_SOC_DAPM_SPK(wname, wevent) \
1817+{ .id = snd_soc_dapm_spk, .name = wname, .kcontrols = NULL, \
1818+ .num_kcontrols = 0, .event = wevent, \
1819+ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD}
1820+#define SND_SOC_DAPM_LINE(wname, wevent) \
1821+{ .id = snd_soc_dapm_line, .name = wname, .kcontrols = NULL, \
1822+ .num_kcontrols = 0, .event = wevent, \
1823+ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD}
1824+
1825+/* path domain */
1826+#define SND_SOC_DAPM_PGA(wname, wreg, wshift, winvert,\
1827+ wcontrols, wncontrols) \
1828+{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
1829+ .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols}
1830+#define SND_SOC_DAPM_MIXER(wname, wreg, wshift, winvert, \
1831+ wcontrols, wncontrols)\
1832+{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
1833+ .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols}
1834+#define SND_SOC_DAPM_MICBIAS(wname, wreg, wshift, winvert) \
1835+{ .id = snd_soc_dapm_micbias, .name = wname, .reg = wreg, .shift = wshift, \
1836+ .invert = winvert, .kcontrols = NULL, .num_kcontrols = 0}
1837+#define SND_SOC_DAPM_SWITCH(wname, wreg, wshift, winvert, wcontrols) \
1838+{ .id = snd_soc_dapm_switch, .name = wname, .reg = wreg, .shift = wshift, \
1839+ .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1}
1840+#define SND_SOC_DAPM_MUX(wname, wreg, wshift, winvert, wcontrols) \
1841+{ .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \
1842+ .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1}
1843+
1844+/* path domain with event - event handler must return 0 for success */
1845+#define SND_SOC_DAPM_PGA_E(wname, wreg, wshift, winvert, wcontrols, \
1846+ wncontrols, wevent, wflags) \
1847+{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
1848+ .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols, \
1849+ .event = wevent, .event_flags = wflags}
1850+#define SND_SOC_DAPM_MIXER_E(wname, wreg, wshift, winvert, wcontrols, \
1851+ wncontrols, wevent, wflags) \
1852+{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
1853+ .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols, \
1854+ .event = wevent, .event_flags = wflags}
1855+#define SND_SOC_DAPM_MICBIAS_E(wname, wreg, wshift, winvert, wevent, wflags) \
1856+{ .id = snd_soc_dapm_micbias, .name = wname, .reg = wreg, .shift = wshift, \
1857+ .invert = winvert, .kcontrols = NULL, .num_kcontrols = 0, \
1858+ .event = wevent, .event_flags = wflags}
1859+#define SND_SOC_DAPM_SWITCH_E(wname, wreg, wshift, winvert, wcontrols, \
1860+ wevent, wflags) \
1861+{ .id = snd_soc_dapm_switch, .name = wname, .reg = wreg, .shift = wshift, \
1862+ .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1 \
1863+ .event = wevent, .event_flags = wflags}
1864+#define SND_SOC_DAPM_MUX_E(wname, wreg, wshift, winvert, wcontrols, \
1865+ wevent, wflags) \
1866+{ .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \
1867+ .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1, \
1868+ .event = wevent, .event_flags = wflags}
1869+
1870+/* events that are pre and post DAPM */
1871+#define SND_SOC_DAPM_PRE(wname, wevent) \
1872+{ .id = snd_soc_dapm_pre, .name = wname, .kcontrols = NULL, \
1873+ .num_kcontrols = 0, .event = wevent, \
1874+ .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD}
1875+#define SND_SOC_DAPM_POST(wname, wevent) \
1876+{ .id = snd_soc_dapm_post, .name = wname, .kcontrols = NULL, \
1877+ .num_kcontrols = 0, .event = wevent, \
1878+ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD}
1879+
1880+/* stream domain */
1881+#define SND_SOC_DAPM_DAC(wname, stname, wreg, wshift, winvert) \
1882+{ .id = snd_soc_dapm_dac, .name = wname, .sname = stname, .reg = wreg, \
1883+ .shift = wshift, .invert = winvert}
1884+#define SND_SOC_DAPM_ADC(wname, stname, wreg, wshift, winvert) \
1885+{ .id = snd_soc_dapm_adc, .name = wname, .sname = stname, .reg = wreg, \
1886+ .shift = wshift, .invert = winvert}
1887+
1888+/* dapm kcontrol types */
1889+#define SOC_DAPM_SINGLE(xname, reg, shift, mask, invert) \
1890+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
1891+ .info = snd_soc_info_volsw, \
1892+ .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
1893+ .private_value = SOC_SINGLE_VALUE(reg, shift, mask, invert) }
1894+#define SOC_DAPM_DOUBLE(xname, reg, shift_left, shift_right, mask, invert, \
1895+ power) \
1896+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
1897+ .info = snd_soc_info_volsw, \
1898+ .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
1899+ .private_value = (reg) | ((shift_left) << 8) | ((shift_right) << 12) |\
1900+ ((mask) << 16) | ((invert) << 24) }
1901+#define SOC_DAPM_ENUM(xname, xenum) \
1902+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
1903+ .info = snd_soc_info_enum_double, \
1904+ .get = snd_soc_dapm_get_enum_double, \
1905+ .put = snd_soc_dapm_put_enum_double, \
1906+ .private_value = (unsigned long)&xenum }
1907+
1908+/* dapm stream operations */
1909+#define SND_SOC_DAPM_STREAM_NOP 0x0
1910+#define SND_SOC_DAPM_STREAM_START 0x1
1911+#define SND_SOC_DAPM_STREAM_STOP 0x2
1912+#define SND_SOC_DAPM_STREAM_SUSPEND 0x4
1913+#define SND_SOC_DAPM_STREAM_RESUME 0x8
1914+#define SND_SOC_DAPM_STREAM_PAUSE_PUSH 0x10
1915+#define SND_SOC_DAPM_STREAM_PAUSE_RELEASE 0x20
1916+
1917+/* dapm event types */
1918+#define SND_SOC_DAPM_PRE_PMU 0x1 /* before widget power up */
1919+#define SND_SOC_DAPM_POST_PMU 0x2 /* after widget power up */
1920+#define SND_SOC_DAPM_PRE_PMD 0x4 /* before widget power down */
1921+#define SND_SOC_DAPM_POST_PMD 0x8 /* after widget power down */
1922+#define SND_SOC_DAPM_PRE_REG 0x10 /* before audio path setup */
1923+#define SND_SOC_DAPM_POST_REG 0x20 /* after audio path setup */
1924+
1925+/* convenience event type detection */
1926+#define SND_SOC_DAPM_EVENT_ON(e) \
1927+ (e & (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU))
1928+#define SND_SOC_DAPM_EVENT_OFF(e) \
1929+ (e & (SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD))
1930+
1931+struct snd_soc_dapm_widget;
1932+enum snd_soc_dapm_type;
1933+struct snd_soc_dapm_path;
1934+struct snd_soc_dapm_pin;
1935+
1936+/* dapm controls */
1937+int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
1938+ struct snd_ctl_elem_value *ucontrol);
1939+int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
1940+ struct snd_ctl_elem_value *ucontrol);
1941+int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol,
1942+ struct snd_ctl_elem_value *ucontrol);
1943+int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
1944+ struct snd_ctl_elem_value *ucontrol);
1945+int snd_soc_dapm_new_control(struct snd_soc_codec *codec,
1946+ const struct snd_soc_dapm_widget *widget);
1947+
1948+/* dapm path setup */
1949+int snd_soc_dapm_connect_input(struct snd_soc_codec *codec,
1950+ const char *sink_name, const char *control_name, const char *src_name);
1951+int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec);
1952+void snd_soc_dapm_free(struct snd_soc_device *socdev);
1953+
1954+/* dapm events */
1955+int snd_soc_dapm_stream_event(struct snd_soc_codec *codec, char *stream,
1956+ int event);
1957+
1958+/* dapm sys fs - used by the core */
1959+int snd_soc_dapm_sys_add(struct device *dev);
1960+
1961+/* dapm audio endpoint control */
1962+int snd_soc_dapm_set_endpoint(struct snd_soc_codec *codec,
1963+ char *pin, int status);
1964+int snd_soc_dapm_sync_endpoints(struct snd_soc_codec *codec);
1965+
1966+/* dapm widget types */
1967+enum snd_soc_dapm_type {
1968+ snd_soc_dapm_input = 0, /* input pin */
1969+ snd_soc_dapm_output, /* output pin */
1970+ snd_soc_dapm_mux, /* selects 1 analog signal from many inputs */
1971+ snd_soc_dapm_mixer, /* mixes several analog signals together */
1972+ snd_soc_dapm_pga, /* programmable gain/attenuation (volume) */
1973+ snd_soc_dapm_adc, /* analog to digital converter */
1974+ snd_soc_dapm_dac, /* digital to analog converter */
1975+ snd_soc_dapm_micbias, /* microphone bias (power) */
1976+ snd_soc_dapm_mic, /* microphone */
1977+ snd_soc_dapm_hp, /* headphones */
1978+ snd_soc_dapm_spk, /* speaker */
1979+ snd_soc_dapm_line, /* line input/output */
1980+ snd_soc_dapm_switch, /* analog switch */
1981+ snd_soc_dapm_vmid, /* codec bias/vmid - to minimise pops */
1982+ snd_soc_dapm_pre, /* machine specific pre widget - exec first */
1983+ snd_soc_dapm_post, /* machine specific post widget - exec last */
1984+};
1985+
1986+/* dapm audio path between two widgets */
1987+struct snd_soc_dapm_path {
1988+ char *name;
1989+ char *long_name;
1990+
1991+ /* source (input) and sink (output) widgets */
1992+ struct snd_soc_dapm_widget *source;
1993+ struct snd_soc_dapm_widget *sink;
1994+ struct snd_kcontrol *kcontrol;
1995+
1996+ /* status */
1997+ u32 connect:1; /* source and sink widgets are connected */
1998+ u32 walked:1; /* path has been walked */
1999+
2000+ struct list_head list_source;
2001+ struct list_head list_sink;
2002+ struct list_head list;
2003+};
2004+
2005+/* dapm widget */
2006+struct snd_soc_dapm_widget {
2007+ enum snd_soc_dapm_type id;
2008+ char *name; /* widget name */
2009+ char *sname; /* stream name */
2010+ struct snd_soc_codec *codec;
2011+ struct list_head list;
2012+
2013+ /* dapm control */
2014+ short reg; /* negative reg = no direct dapm */
2015+ unsigned char shift; /* bits to shift */
2016+ unsigned int saved_value; /* widget saved value */
2017+ unsigned int value; /* widget current value */
2018+ unsigned char power:1; /* block power status */
2019+ unsigned char invert:1; /* invert the power bit */
2020+ unsigned char active:1; /* active stream on DAC, ADC's */
2021+ unsigned char connected:1; /* connected codec pin */
2022+ unsigned char new:1; /* cnew complete */
2023+ unsigned char ext:1; /* has external widgets */
2024+ unsigned char muted:1; /* muted for pop reduction */
2025+ unsigned char suspend:1; /* was active before suspend */
2026+ unsigned char pmdown:1; /* waiting for timeout */
2027+
2028+ /* external events */
2029+ unsigned short event_flags; /* flags to specify event types */
2030+ int (*event)(struct snd_soc_dapm_widget*, int);
2031+
2032+ /* kcontrols that relate to this widget */
2033+ int num_kcontrols;
2034+ const struct snd_kcontrol_new *kcontrols;
2035+
2036+ /* widget input and outputs */
2037+ struct list_head sources;
2038+ struct list_head sinks;
2039+};
2040+
2041+#endif
2042Index: linux-2.6-pxa-new/include/sound/soc.h
2043===================================================================
2044--- /dev/null
2045+++ linux-2.6-pxa-new/include/sound/soc.h
2046@@ -0,0 +1,487 @@
2047+/*
2048+ * linux/sound/soc.h -- ALSA SoC Layer
2049+ *
2050+ * Author: Liam Girdwood
2051+ * Created: Aug 11th 2005
2052+ * Copyright: Wolfson Microelectronics. PLC.
2053+ *
2054+ * This program is free software; you can redistribute it and/or modify
2055+ * it under the terms of the GNU General Public License version 2 as
2056+ * published by the Free Software Foundation.
2057+ */
2058+
2059+#ifndef __LINUX_SND_SOC_H
2060+#define __LINUX_SND_SOC_H
2061+
2062+#include <linux/platform_device.h>
2063+#include <linux/types.h>
2064+#include <sound/driver.h>
2065+#include <sound/core.h>
2066+#include <sound/pcm.h>
2067+#include <sound/control.h>
2068+#include <sound/ac97_codec.h>
2069+
2070+#define SND_SOC_VERSION "0.12.4"
2071+
2072+/*
2073+ * Convenience kcontrol builders
2074+ */
2075+#define SOC_SINGLE_VALUE(reg,shift,mask,invert) ((reg) | ((shift) << 8) |\
2076+ ((shift) << 12) | ((mask) << 16) | ((invert) << 24))
2077+#define SOC_SINGLE_VALUE_EXT(reg,mask,invert) ((reg) | ((mask) << 16) |\
2078+ ((invert) << 31))
2079+#define SOC_SINGLE(xname, reg, shift, mask, invert) \
2080+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
2081+ .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\
2082+ .put = snd_soc_put_volsw, \
2083+ .private_value = SOC_SINGLE_VALUE(reg, shift, mask, invert) }
2084+#define SOC_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \
2085+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
2086+ .info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \
2087+ .put = snd_soc_put_volsw, \
2088+ .private_value = (reg) | ((shift_left) << 8) | \
2089+ ((shift_right) << 12) | ((mask) << 16) | ((invert) << 24) }
2090+#define SOC_DOUBLE_R(xname, reg_left, reg_right, shift, mask, invert) \
2091+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
2092+ .info = snd_soc_info_volsw_2r, \
2093+ .get = snd_soc_get_volsw_2r, .put = snd_soc_put_volsw_2r, \
2094+ .private_value = (reg_left) | ((shift) << 8) | \
2095+ ((mask) << 12) | ((invert) << 20) | ((reg_right) << 24) }
2096+#define SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, xtexts) \
2097+{ .reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \
2098+ .mask = xmask, .texts = xtexts }
2099+#define SOC_ENUM_SINGLE(xreg, xshift, xmask, xtexts) \
2100+ SOC_ENUM_DOUBLE(xreg, xshift, xshift, xmask, xtexts)
2101+#define SOC_ENUM_SINGLE_EXT(xmask, xtexts) \
2102+{ .mask = xmask, .texts = xtexts }
2103+#define SOC_ENUM(xname, xenum) \
2104+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,\
2105+ .info = snd_soc_info_enum_double, \
2106+ .get = snd_soc_get_enum_double, .put = snd_soc_put_enum_double, \
2107+ .private_value = (unsigned long)&xenum }
2108+#define SOC_SINGLE_EXT(xname, xreg, xmask, xinvert,\
2109+ xhandler_get, xhandler_put) \
2110+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
2111+ .info = snd_soc_info_volsw_ext, \
2112+ .get = xhandler_get, .put = xhandler_put, \
2113+ .private_value = SOC_SINGLE_VALUE_EXT(xreg, xmask, xinvert) }
2114+#define SOC_SINGLE_BOOL_EXT(xname, xdata, xhandler_get, xhandler_put) \
2115+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
2116+ .info = snd_soc_info_bool_ext, \
2117+ .get = xhandler_get, .put = xhandler_put, \
2118+ .private_value = xdata }
2119+#define SOC_ENUM_EXT(xname, xenum, xhandler_get, xhandler_put) \
2120+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
2121+ .info = snd_soc_info_enum_ext, \
2122+ .get = xhandler_get, .put = xhandler_put, \
2123+ .private_value = (unsigned long)&xenum }
2124+
2125+/*
2126+ * Digital Audio Interface (DAI) types
2127+ */
2128+#define SND_SOC_DAI_AC97 0x1
2129+#define SND_SOC_DAI_I2S 0x2
2130+#define SND_SOC_DAI_PCM 0x4
2131+
2132+/*
2133+ * DAI hardware audio formats
2134+ */
2135+#define SND_SOC_DAIFMT_I2S (1 << 0) /* I2S mode */
2136+#define SND_SOC_DAIFMT_RIGHT_J (1 << 1) /* Right justified mode */
2137+#define SND_SOC_DAIFMT_LEFT_J (1 << 2) /* Left Justified mode */
2138+#define SND_SOC_DAIFMT_DSP_A (1 << 3) /* L data msb after FRM or LRC */
2139+#define SND_SOC_DAIFMT_DSP_B (1 << 4) /* L data msb during FRM or LRC */
2140+#define SND_SOC_DAIFMT_AC97 (1 << 5) /* AC97 */
2141+
2142+/*
2143+ * DAI hardware signal inversions
2144+ */
2145+#define SND_SOC_DAIFMT_NB_NF (1 << 8) /* normal bit clock + frame */
2146+#define SND_SOC_DAIFMT_NB_IF (1 << 9) /* normal bclk + inv frm */
2147+#define SND_SOC_DAIFMT_IB_NF (1 << 10) /* invert bclk + nor frm */
2148+#define SND_SOC_DAIFMT_IB_IF (1 << 11) /* invert bclk + frm */
2149+
2150+/*
2151+ * DAI hardware clock masters
2152+ * This is wrt the codec, the inverse is true for the interface
2153+ * i.e. if the codec is clk and frm master then the interface is
2154+ * clk and frame slave.
2155+ */
2156+#define SND_SOC_DAIFMT_CBM_CFM (1 << 12) /* codec clk & frm master */
2157+#define SND_SOC_DAIFMT_CBS_CFM (1 << 13) /* codec clk slave & frm master */
2158+#define SND_SOC_DAIFMT_CBM_CFS (1 << 14) /* codec clk master & frame slave */
2159+#define SND_SOC_DAIFMT_CBS_CFS (1 << 15) /* codec clk & frm slave */
2160+
2161+#define SND_SOC_DAIFMT_FORMAT_MASK 0x00ff
2162+#define SND_SOC_DAIFMT_INV_MASK 0x0f00
2163+#define SND_SOC_DAIFMT_CLOCK_MASK 0xf000
2164+
2165+/*
2166+ * DAI hardware audio direction
2167+ */
2168+#define SND_SOC_DAIDIR_PLAYBACK 0x1
2169+#define SND_SOC_DAIDIR_CAPTURE 0x2
2170+
2171+/*
2172+ * DAI hardware Time Division Multiplexing (TDM) Slots
2173+ * Left and Right data word positions
2174+ * This is measured in words (sample size) and not bits.
2175+ */
2176+#define SND_SOC_DAITDM_LRDW(l,r) ((l << 8) | r)
2177+
2178+/*
2179+ * DAI hardware clock ratios
2180+ * bit clock can either be a generated by dividing mclk or
2181+ * by multiplying sample rate, hence there are 2 definitions below
2182+ * depending on codec type.
2183+ */
2184+/* ratio of sample rate to mclk/sysclk */
2185+#define SND_SOC_FS_ALL 0xffff /* all mclk supported */
2186+
2187+/* bit clock dividers */
2188+#define SND_SOC_FSBD(x) (1 << (x - 1)) /* ratio mclk:bclk */
2189+#define SND_SOC_FSBD_REAL(x) (ffs(x))
2190+
2191+/* bit clock ratio to (sample rate * channels * word size) */
2192+#define SND_SOC_FSBW(x) (1 << (x - 1))
2193+#define SND_SOC_FSBW_REAL(x) (ffs(x))
2194+/* all bclk ratios supported */
2195+#define SND_SOC_FSB_ALL ~0ULL
2196+
2197+/*
2198+ * DAI hardware flags
2199+ */
2200+/* use bfs mclk divider mode (BCLK = MCLK / x) */
2201+#define SND_SOC_DAI_BFS_DIV 0x1
2202+/* use bfs rate mulitplier (BCLK = RATE * x)*/
2203+#define SND_SOC_DAI_BFS_RATE 0x2
2204+/* use bfs rcw multiplier (BCLK = RATE * CHN * WORD SIZE) */
2205+#define SND_SOC_DAI_BFS_RCW 0x4
2206+/* capture and playback can use different clocks */
2207+#define SND_SOC_DAI_ASYNC 0x8
2208+/* can use gated BCLK */
2209+#define SND_SOC_DAI_GATED 0x10
2210+
2211+/*
2212+ * AC97 codec ID's bitmask
2213+ */
2214+#define SND_SOC_DAI_AC97_ID0 (1 << 0)
2215+#define SND_SOC_DAI_AC97_ID1 (1 << 1)
2216+#define SND_SOC_DAI_AC97_ID2 (1 << 2)
2217+#define SND_SOC_DAI_AC97_ID3 (1 << 3)
2218+
2219+struct snd_soc_device;
2220+struct snd_soc_pcm_stream;
2221+struct snd_soc_ops;
2222+struct snd_soc_dai_mode;
2223+struct snd_soc_pcm_runtime;
2224+struct snd_soc_codec_dai;
2225+struct snd_soc_cpu_dai;
2226+struct snd_soc_codec;
2227+struct snd_soc_machine_config;
2228+struct soc_enum;
2229+struct snd_soc_ac97_ops;
2230+struct snd_soc_clock_info;
2231+
2232+typedef int (*hw_write_t)(void *,const char* ,int);
2233+typedef int (*hw_read_t)(void *,char* ,int);
2234+
2235+extern struct snd_ac97_bus_ops soc_ac97_ops;
2236+
2237+/* pcm <-> DAI connect */
2238+void snd_soc_free_pcms(struct snd_soc_device *socdev);
2239+int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid);
2240+int snd_soc_register_card(struct snd_soc_device *socdev);
2241+
2242+/* set runtime hw params */
2243+int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,
2244+ const struct snd_pcm_hardware *hw);
2245+int snd_soc_get_rate(int rate);
2246+
2247+/* codec IO */
2248+#define snd_soc_read(codec, reg) codec->read(codec, reg)
2249+#define snd_soc_write(codec, reg, value) codec->write(codec, reg, value)
2250+
2251+/* codec register bit access */
2252+int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg,
2253+ unsigned short mask, unsigned short value);
2254+int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg,
2255+ unsigned short mask, unsigned short value);
2256+
2257+int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
2258+ struct snd_ac97_bus_ops *ops, int num);
2259+void snd_soc_free_ac97_codec(struct snd_soc_codec *codec);
2260+
2261+/*
2262+ *Controls
2263+ */
2264+struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template,
2265+ void *data, char *long_name);
2266+int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol,
2267+ struct snd_ctl_elem_info *uinfo);
2268+int snd_soc_info_enum_ext(struct snd_kcontrol *kcontrol,
2269+ struct snd_ctl_elem_info *uinfo);
2270+int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol,
2271+ struct snd_ctl_elem_value *ucontrol);
2272+int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,
2273+ struct snd_ctl_elem_value *ucontrol);
2274+int snd_soc_info_volsw(struct snd_kcontrol *kcontrol,
2275+ struct snd_ctl_elem_info *uinfo);
2276+int snd_soc_info_volsw_ext(struct snd_kcontrol *kcontrol,
2277+ struct snd_ctl_elem_info *uinfo);
2278+int snd_soc_info_bool_ext(struct snd_kcontrol *kcontrol,
2279+ struct snd_ctl_elem_info *uinfo);
2280+int snd_soc_get_volsw(struct snd_kcontrol *kcontrol,
2281+ struct snd_ctl_elem_value *ucontrol);
2282+int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
2283+ struct snd_ctl_elem_value *ucontrol);
2284+int snd_soc_info_volsw_2r(struct snd_kcontrol *kcontrol,
2285+ struct snd_ctl_elem_info *uinfo);
2286+int snd_soc_get_volsw_2r(struct snd_kcontrol *kcontrol,
2287+ struct snd_ctl_elem_value *ucontrol);
2288+int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol,
2289+ struct snd_ctl_elem_value *ucontrol);
2290+
2291+/* SoC PCM stream information */
2292+struct snd_soc_pcm_stream {
2293+ char *stream_name;
2294+ unsigned int rate_min; /* min rate */
2295+ unsigned int rate_max; /* max rate */
2296+ unsigned int channels_min; /* min channels */
2297+ unsigned int channels_max; /* max channels */
2298+ unsigned int active:1; /* stream is in use */
2299+};
2300+
2301+/* SoC audio ops */
2302+struct snd_soc_ops {
2303+ int (*startup)(struct snd_pcm_substream *);
2304+ void (*shutdown)(struct snd_pcm_substream *);
2305+ int (*hw_params)(struct snd_pcm_substream *, struct snd_pcm_hw_params *);
2306+ int (*hw_free)(struct snd_pcm_substream *);
2307+ int (*prepare)(struct snd_pcm_substream *);
2308+ int (*trigger)(struct snd_pcm_substream *, int);
2309+};
2310+
2311+/* SoC DAI hardware mode */
2312+struct snd_soc_dai_mode {
2313+ u16 fmt; /* SND_SOC_DAIFMT_* */
2314+ u16 tdm; /* SND_SOC_HWTDM_* */
2315+ u64 pcmfmt; /* SNDRV_PCM_FMTBIT_* */
2316+ u16 pcmrate; /* SND_SOC_HWRATE_* */
2317+ u16 pcmdir:2; /* SND_SOC_HWDIR_* */
2318+ u16 flags:8; /* hw flags */
2319+ u16 fs; /* mclk to rate divider */
2320+ u64 bfs; /* mclk to bclk dividers */
2321+ unsigned long priv; /* private mode data */
2322+};
2323+
2324+/* DAI capabilities */
2325+struct snd_soc_dai_cap {
2326+ int num_modes; /* number of DAI modes */
2327+ struct snd_soc_dai_mode *mode; /* array of supported DAI modes */
2328+};
2329+
2330+/* SoC Codec DAI */
2331+struct snd_soc_codec_dai {
2332+ char *name;
2333+ int id;
2334+
2335+ /* DAI capabilities */
2336+ struct snd_soc_pcm_stream playback;
2337+ struct snd_soc_pcm_stream capture;
2338+ struct snd_soc_dai_cap caps;
2339+
2340+ /* DAI runtime info */
2341+ struct snd_soc_dai_mode dai_runtime;
2342+ struct snd_soc_ops ops;
2343+ unsigned int (*config_sysclk)(struct snd_soc_codec_dai*,
2344+ struct snd_soc_clock_info *info, unsigned int clk);
2345+ int (*digital_mute)(struct snd_soc_codec *,
2346+ struct snd_soc_codec_dai*, int);
2347+ unsigned int mclk; /* the audio master clock */
2348+ unsigned int pll_in; /* the PLL input clock */
2349+ unsigned int pll_out; /* the PLL output clock */
2350+ unsigned int clk_div; /* internal clock divider << 1 (for fractions) */
2351+ unsigned int active;
2352+ unsigned char pop_wait:1;
2353+
2354+ /* DAI private data */
2355+ void *private_data;
2356+};
2357+
2358+/* SoC CPU DAI */
2359+struct snd_soc_cpu_dai {
2360+
2361+ /* DAI description */
2362+ char *name;
2363+ unsigned int id;
2364+ unsigned char type;
2365+
2366+ /* DAI callbacks */
2367+ int (*probe)(struct platform_device *pdev);
2368+ void (*remove)(struct platform_device *pdev);
2369+ int (*suspend)(struct platform_device *pdev,
2370+ struct snd_soc_cpu_dai *cpu_dai);
2371+ int (*resume)(struct platform_device *pdev,
2372+ struct snd_soc_cpu_dai *cpu_dai);
2373+ unsigned int (*config_sysclk)(struct snd_soc_cpu_dai *cpu_dai,
2374+ struct snd_soc_clock_info *info, unsigned int clk);
2375+
2376+ /* DAI capabilities */
2377+ struct snd_soc_pcm_stream capture;
2378+ struct snd_soc_pcm_stream playback;
2379+ struct snd_soc_dai_cap caps;
2380+
2381+ /* DAI runtime info */
2382+ struct snd_soc_dai_mode dai_runtime;
2383+ struct snd_soc_ops ops;
2384+ struct snd_pcm_runtime *runtime;
2385+ unsigned char active:1;
2386+ unsigned int mclk;
2387+ void *dma_data;
2388+
2389+ /* DAI private data */
2390+ void *private_data;
2391+};
2392+
2393+/* SoC Audio Codec */
2394+struct snd_soc_codec {
2395+ char *name;
2396+ struct module *owner;
2397+ struct mutex mutex;
2398+
2399+ /* callbacks */
2400+ int (*dapm_event)(struct snd_soc_codec *codec, int event);
2401+
2402+ /* runtime */
2403+ struct snd_card *card;
2404+ struct snd_ac97 *ac97; /* for ad-hoc ac97 devices */
2405+ unsigned int active;
2406+ unsigned int pcm_devs;
2407+ void *private_data;
2408+
2409+ /* codec IO */
2410+ void *control_data; /* codec control (i2c/3wire) data */
2411+ unsigned int (*read)(struct snd_soc_codec *, unsigned int);
2412+ int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);
2413+ hw_write_t hw_write;
2414+ hw_read_t hw_read;
2415+ void *reg_cache;
2416+ short reg_cache_size;
2417+ short reg_cache_step;
2418+
2419+ /* dapm */
2420+ struct list_head dapm_widgets;
2421+ struct list_head dapm_paths;
2422+ unsigned int dapm_state;
2423+ unsigned int suspend_dapm_state;
2424+
2425+ /* codec DAI's */
2426+ struct snd_soc_codec_dai *dai;
2427+ unsigned int num_dai;
2428+};
2429+
2430+/* codec device */
2431+struct snd_soc_codec_device {
2432+ int (*probe)(struct platform_device *pdev);
2433+ int (*remove)(struct platform_device *pdev);
2434+ int (*suspend)(struct platform_device *pdev, pm_message_t state);
2435+ int (*resume)(struct platform_device *pdev);
2436+};
2437+
2438+/* SoC platform interface */
2439+struct snd_soc_platform {
2440+ char *name;
2441+
2442+ int (*probe)(struct platform_device *pdev);
2443+ int (*remove)(struct platform_device *pdev);
2444+ int (*suspend)(struct platform_device *pdev,
2445+ struct snd_soc_cpu_dai *cpu_dai);
2446+ int (*resume)(struct platform_device *pdev,
2447+ struct snd_soc_cpu_dai *cpu_dai);
2448+
2449+ /* pcm creation and destruction */
2450+ int (*pcm_new)(struct snd_card *, struct snd_soc_codec_dai *,
2451+ struct snd_pcm *);
2452+ void (*pcm_free)(struct snd_pcm *);
2453+
2454+ /* platform stream ops */
2455+ struct snd_pcm_ops *pcm_ops;
2456+};
2457+
2458+/* SoC machine DAI configuration, glues a codec and cpu DAI together */
2459+struct snd_soc_dai_link {
2460+ char *name; /* Codec name */
2461+ char *stream_name; /* Stream name */
2462+
2463+ /* DAI */
2464+ struct snd_soc_codec_dai *codec_dai;
2465+ struct snd_soc_cpu_dai *cpu_dai;
2466+ u32 flags; /* DAI config preference flags */
2467+
2468+ /* codec/machine specific init - e.g. add machine controls */
2469+ int (*init)(struct snd_soc_codec *codec);
2470+
2471+ /* audio sysclock configuration */
2472+ unsigned int (*config_sysclk)(struct snd_soc_pcm_runtime *rtd,
2473+ struct snd_soc_clock_info *info);
2474+};
2475+
2476+/* SoC machine */
2477+struct snd_soc_machine {
2478+ char *name;
2479+
2480+ int (*probe)(struct platform_device *pdev);
2481+ int (*remove)(struct platform_device *pdev);
2482+
2483+ /* the pre and post PM functions are used to do any PM work before and
2484+ * after the codec and DAI's do any PM work. */
2485+ int (*suspend_pre)(struct platform_device *pdev, pm_message_t state);
2486+ int (*suspend_post)(struct platform_device *pdev, pm_message_t state);
2487+ int (*resume_pre)(struct platform_device *pdev);
2488+ int (*resume_post)(struct platform_device *pdev);
2489+
2490+ /* machine stream operations */
2491+ struct snd_soc_ops *ops;
2492+
2493+ /* CPU <--> Codec DAI links */
2494+ struct snd_soc_dai_link *dai_link;
2495+ int num_links;
2496+};
2497+
2498+/* SoC Device - the audio subsystem */
2499+struct snd_soc_device {
2500+ struct device *dev;
2501+ struct snd_soc_machine *machine;
2502+ struct snd_soc_platform *platform;
2503+ struct snd_soc_codec *codec;
2504+ struct snd_soc_codec_device *codec_dev;
2505+ void *codec_data;
2506+};
2507+
2508+/* runtime channel data */
2509+struct snd_soc_pcm_runtime {
2510+ struct snd_soc_codec_dai *codec_dai;
2511+ struct snd_soc_cpu_dai *cpu_dai;
2512+ struct snd_soc_device *socdev;
2513+};
2514+
2515+/* enumerated kcontrol */
2516+struct soc_enum {
2517+ unsigned short reg;
2518+ unsigned short reg2;
2519+ unsigned char shift_l;
2520+ unsigned char shift_r;
2521+ unsigned int mask;
2522+ const char **texts;
2523+ void *dapm;
2524+};
2525+
2526+/* clocking configuration data */
2527+struct snd_soc_clock_info {
2528+ unsigned int rate;
2529+ unsigned int fs;
2530+ unsigned int bclk_master;
2531+};
2532+
2533+#endif
2534Index: linux-2.6-pxa-new/sound/Kconfig
2535===================================================================
2536--- linux-2.6-pxa-new.orig/sound/Kconfig
2537+++ linux-2.6-pxa-new/sound/Kconfig
2538@@ -76,6 +76,8 @@ source "sound/sparc/Kconfig"
2539
2540 source "sound/parisc/Kconfig"
2541
2542+source "sound/soc/Kconfig"
2543+
2544 endmenu
2545
2546 menu "Open Sound System"
2547Index: linux-2.6-pxa-new/sound/soc/Kconfig
2548===================================================================
2549--- /dev/null
2550+++ linux-2.6-pxa-new/sound/soc/Kconfig
2551@@ -0,0 +1,37 @@
2552+#
2553+# SoC audio configuration
2554+#
2555+
2556+menu "SoC audio support"
2557+ depends on SND!=n
2558+
2559+config SND_SOC_AC97_BUS
2560+ bool
2561+
2562+config SND_SOC
2563+ tristate "SoC audio support"
2564+ ---help---
2565+
2566+ If you want SoC support, you should say Y here and also to the
2567+ specific driver for your SoC below. You will also need to select the
2568+ specific codec(s) attached to the SoC
2569+
2570+ This SoC audio support can also be built as a module. If so, the module
2571+ will be called snd-soc-core.
2572+
2573+# All the supported Soc's
2574+menu "Soc Platforms"
2575+depends on SND_SOC
2576+source "sound/soc/pxa/Kconfig"
2577+source "sound/soc/at91/Kconfig"
2578+source "sound/soc/imx/Kconfig"
2579+source "sound/soc/s3c24xx/Kconfig"
2580+endmenu
2581+
2582+# Supported codecs
2583+menu "Soc Codecs"
2584+depends on SND_SOC
2585+source "sound/soc/codecs/Kconfig"
2586+endmenu
2587+
2588+endmenu
2589Index: linux-2.6-pxa-new/sound/soc/Makefile
2590===================================================================
2591--- /dev/null
2592+++ linux-2.6-pxa-new/sound/soc/Makefile
2593@@ -0,0 +1,4 @@
2594+snd-soc-core-objs := soc-core.o soc-dapm.o
2595+
2596+obj-$(CONFIG_SND_SOC) += snd-soc-core.o
2597+obj-$(CONFIG_SND_SOC) += pxa/ at91/ imx/ s3c24xx/ codecs/
2598Index: linux-2.6-pxa-new/sound/soc/codecs/Kconfig
2599===================================================================
2600--- /dev/null
2601+++ linux-2.6-pxa-new/sound/soc/codecs/Kconfig
2602@@ -0,0 +1,90 @@
2603+config SND_SOC_AC97_CODEC
2604+ tristate "SoC generic AC97 support"
2605+ depends SND_SOC
2606+ help
2607+ Say Y or M if you want generic AC97 support. This is not required
2608+ for the AC97 codecs listed below.
2609+
2610+config SND_SOC_WM8711
2611+ tristate "SoC driver for the WM8711 codec"
2612+ depends SND_SOC
2613+ help
2614+ Say Y or M if you want to support the WM8711 codec.
2615+
2616+config SND_SOC_WM8510
2617+ tristate "SoC driver for the WM8510 codec"
2618+ depends SND_SOC
2619+ help
2620+ Say Y or M if you want to support the WM8711 codec.
2621+
2622+config SND_SOC_WM8731
2623+ tristate "SoC driver for the WM8731 codec"
2624+ depends SND_SOC
2625+ help
2626+ Say Y or M if you want to support the WM8731 codec.
2627+
2628+config SND_SOC_WM8750
2629+ tristate "SoC driver for the WM8750 codec"
2630+ depends SND_SOC
2631+ help
2632+ Say Y or M if you want to support the WM8750 codec.
2633+
2634+config SND_SOC_WM8753
2635+ tristate "SoC driver for the WM8753 codec"
2636+ depends SND_SOC
2637+ help
2638+ Say Y or M if you want to support the WM8753 codec.
2639+
2640+config SND_SOC_WM8772
2641+ tristate "SoC driver for the WM8772 codec"
2642+ depends SND_SOC
2643+ help
2644+ Say Y or M if you want to support the WM8772 codec.
2645+
2646+config SND_SOC_WM8971
2647+ tristate "SoC driver for the WM8971 codec"
2648+ depends SND_SOC
2649+ help
2650+ Say Y or M if you want to support the WM8971 codec.
2651+
2652+config SND_SOC_WM8976
2653+ tristate "SoC driver for the WM8976 codec"
2654+ depends SND_SOC
2655+ help
2656+ Say Y or M if you want to support the WM8976 codec.
2657+
2658+config SND_SOC_WM8974
2659+ tristate "SoC driver for the WM8974 codec"
2660+ depends SND_SOC
2661+ help
2662+ Say Y or M if you want to support the WM8974 codec.
2663+
2664+config SND_SOC_WM8980
2665+ tristate "SoC driver for the WM8980 codec"
2666+ depends SND_SOC
2667+ help
2668+ Say Y or M if you want to support the WM8980 codec.
2669+
2670+config SND_SOC_WM9713
2671+ tristate "SoC driver for the WM9713 codec"
2672+ depends SND_SOC
2673+ help
2674+ Say Y or M if you want to support the WM9713 codec.
2675+
2676+config SND_SOC_WM9712
2677+ tristate "SoC driver for the WM9712 codec"
2678+ depends SND_SOC
2679+ help
2680+ Say Y or M if you want to support the WM9712 codec.
2681+
2682+config SND_SOC_UDA1380
2683+ tristate "SoC driver for the UDA1380 codec"
2684+ depends SND_SOC
2685+ help
2686+ Say Y or M if you want to support the UDA1380 codec.
2687+
2688+config SND_SOC_AK4535
2689+ tristate "SoC driver for the AK4535 codec"
2690+ depends SND_SOC
2691+ help
2692+ Say Y or M if you want to support the AK4535 codec.
2693Index: linux-2.6-pxa-new/sound/soc/codecs/Makefile
2694===================================================================
2695--- /dev/null
2696+++ linux-2.6-pxa-new/sound/soc/codecs/Makefile
2697@@ -0,0 +1,31 @@
2698+snd-soc-ac97-objs := ac97.o
2699+snd-soc-wm8711-objs := wm8711.o
2700+snd-soc-wm8510-objs := wm8510.o
2701+snd-soc-wm8731-objs := wm8731.o
2702+snd-soc-wm8750-objs := wm8750.o
2703+snd-soc-wm8753-objs := wm8753.o
2704+snd-soc-wm8772-objs := wm8772.o
2705+snd-soc-wm8971-objs := wm8971.o
2706+snd-soc-wm8974-objs := wm8974.o
2707+snd-soc-wm8976-objs := wm8976.o
2708+snd-soc-wm8980-objs := wm8980.o
2709+snd-soc-uda1380-objs := uda1380.o
2710+snd-soc-ak4535-objs := ak4535.o
2711+snd-soc-wm9713-objs := wm9713.o
2712+snd-soc-wm9712-objs := wm9712.o
2713+
2714+obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o
2715+obj-$(CONFIG_SND_SOC_WM8711) += snd-soc-wm8711.o
2716+obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o
2717+obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o
2718+obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o
2719+obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o
2720+obj-$(CONFIG_SND_SOC_WM8772) += snd-soc-wm8772.o
2721+obj-$(CONFIG_SND_SOC_WM8971) += snd-soc-wm8971.o
2722+obj-$(CONFIG_SND_SOC_WM8974) += snd-soc-wm8974.o
2723+obj-$(CONFIG_SND_SOC_WM8976) += snd-soc-wm8976.o
2724+obj-$(CONFIG_SND_SOC_WM8980) += snd-soc-wm8980.o
2725+obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o
2726+obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o
2727+obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o
2728+obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
2729Index: linux-2.6-pxa-new/sound/soc/codecs/ac97.c
2730===================================================================
2731--- /dev/null
2732+++ linux-2.6-pxa-new/sound/soc/codecs/ac97.c
2733@@ -0,0 +1,167 @@
2734+/*
2735+ * ac97.c -- ALSA Soc AC97 codec support
2736+ *
2737+ * Copyright 2005 Wolfson Microelectronics PLC.
2738+ * Author: Liam Girdwood
2739+ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
2740+ *
2741+ * This program is free software; you can redistribute it and/or modify it
2742+ * under the terms of the GNU General Public License as published by the
2743+ * Free Software Foundation; either version 2 of the License, or (at your
2744+ * option) any later version.
2745+ *
2746+ * Revision history
2747+ * 17th Oct 2005 Initial version.
2748+ *
2749+ * Generic AC97 support.
2750+ */
2751+
2752+#include <linux/init.h>
2753+#include <linux/kernel.h>
2754+#include <linux/device.h>
2755+#include <sound/driver.h>
2756+#include <sound/core.h>
2757+#include <sound/pcm.h>
2758+#include <sound/ac97_codec.h>
2759+#include <sound/initval.h>
2760+#include <sound/soc.h>
2761+
2762+#define AC97_VERSION "0.5"
2763+
2764+#define AC97_DIR \
2765+ (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
2766+
2767+#define AC97_RATES \
2768+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
2769+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
2770+ SNDRV_PCM_RATE_48000)
2771+
2772+/* may need to expand this */
2773+static struct snd_soc_dai_mode soc_ac97[] = {
2774+ {0, 0, SNDRV_PCM_FMTBIT_S16_LE, AC97_RATES},
2775+ {0, 0, SNDRV_PCM_FMTBIT_S18_3LE, AC97_RATES},
2776+ {0, 0, SNDRV_PCM_FMTBIT_S20_3LE, AC97_RATES},
2777+};
2778+
2779+static int ac97_prepare(struct snd_pcm_substream *substream)
2780+{
2781+ struct snd_pcm_runtime *runtime = substream->runtime;
2782+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
2783+ struct snd_soc_device *socdev = rtd->socdev;
2784+ struct snd_soc_codec *codec = socdev->codec;
2785+
2786+ int reg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
2787+ AC97_PCM_FRONT_DAC_RATE : AC97_PCM_LR_ADC_RATE;
2788+ return snd_ac97_set_rate(codec->ac97, reg, runtime->rate);
2789+}
2790+
2791+static struct snd_soc_codec_dai ac97_dai = {
2792+ .name = "AC97 HiFi",
2793+ .playback = {
2794+ .stream_name = "AC97 Playback",
2795+ .channels_min = 1,
2796+ .channels_max = 2,},
2797+ .capture = {
2798+ .stream_name = "AC97 Capture",
2799+ .channels_min = 1,
2800+ .channels_max = 2,},
2801+ .ops = {
2802+ .prepare = ac97_prepare,},
2803+ .caps = {
2804+ .num_modes = ARRAY_SIZE(soc_ac97),
2805+ .mode = soc_ac97,},
2806+};
2807+
2808+static unsigned int ac97_read(struct snd_soc_codec *codec,
2809+ unsigned int reg)
2810+{
2811+ return soc_ac97_ops.read(codec->ac97, reg);
2812+}
2813+
2814+static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
2815+ unsigned int val)
2816+{
2817+ soc_ac97_ops.write(codec->ac97, reg, val);
2818+ return 0;
2819+}
2820+
2821+static int ac97_soc_probe(struct platform_device *pdev)
2822+{
2823+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
2824+ struct snd_soc_codec *codec;
2825+ struct snd_ac97_bus *ac97_bus;
2826+ struct snd_ac97_template ac97_template;
2827+ int ret = 0;
2828+
2829+ printk(KERN_INFO "AC97 SoC Audio Codec %s\n", AC97_VERSION);
2830+
2831+ socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
2832+ if (socdev->codec == NULL)
2833+ return -ENOMEM;
2834+ codec = socdev->codec;
2835+ mutex_init(&codec->mutex);
2836+
2837+ codec->name = "AC97";
2838+ codec->owner = THIS_MODULE;
2839+ codec->dai = &ac97_dai;
2840+ codec->num_dai = 1;
2841+ codec->write = ac97_write;
2842+ codec->read = ac97_read;
2843+ INIT_LIST_HEAD(&codec->dapm_widgets);
2844+ INIT_LIST_HEAD(&codec->dapm_paths);
2845+
2846+ /* register pcms */
2847+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
2848+ if(ret < 0)
2849+ goto err;
2850+
2851+ /* add codec as bus device for standard ac97 */
2852+ ret = snd_ac97_bus(codec->card, 0, &soc_ac97_ops, NULL, &ac97_bus);
2853+ if(ret < 0)
2854+ goto bus_err;
2855+
2856+ memset(&ac97_template, 0, sizeof(struct snd_ac97_template));
2857+ ret = snd_ac97_mixer(ac97_bus, &ac97_template, &codec->ac97);
2858+ if(ret < 0)
2859+ goto bus_err;
2860+
2861+ ret = snd_soc_register_card(socdev);
2862+ if (ret < 0)
2863+ goto bus_err;
2864+ return 0;
2865+
2866+bus_err:
2867+ snd_soc_free_pcms(socdev);
2868+
2869+err:
2870+ kfree(socdev->codec->reg_cache);
2871+ kfree(socdev->codec);
2872+ socdev->codec = NULL;
2873+ return ret;
2874+}
2875+
2876+static int ac97_soc_remove(struct platform_device *pdev)
2877+{
2878+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
2879+ struct snd_soc_codec *codec = socdev->codec;
2880+
2881+ if(codec == NULL)
2882+ return 0;
2883+
2884+ snd_soc_free_pcms(socdev);
2885+ kfree(socdev->codec->reg_cache);
2886+ kfree(socdev->codec);
2887+
2888+ return 0;
2889+}
2890+
2891+struct snd_soc_codec_device soc_codec_dev_ac97= {
2892+ .probe = ac97_soc_probe,
2893+ .remove = ac97_soc_remove,
2894+};
2895+
2896+EXPORT_SYMBOL_GPL(soc_codec_dev_ac97);
2897+
2898+MODULE_DESCRIPTION("Soc Generic AC97 driver");
2899+MODULE_AUTHOR("Liam Girdwood");
2900+MODULE_LICENSE("GPL");
2901Index: linux-2.6-pxa-new/sound/soc/codecs/ac97.h
2902===================================================================
2903--- /dev/null
2904+++ linux-2.6-pxa-new/sound/soc/codecs/ac97.h
2905@@ -0,0 +1,18 @@
2906+/*
2907+ * linux/sound/codecs/ac97.h -- ALSA SoC Layer
2908+ *
2909+ * Author: Liam Girdwood
2910+ * Created: Dec 1st 2005
2911+ * Copyright: Wolfson Microelectronics. PLC.
2912+ *
2913+ * This program is free software; you can redistribute it and/or modify
2914+ * it under the terms of the GNU General Public License version 2 as
2915+ * published by the Free Software Foundation.
2916+ */
2917+
2918+#ifndef __LINUX_SND_SOC_AC97_H
2919+#define __LINUX_SND_SOC_AC97_H
2920+
2921+extern struct snd_soc_codec_device soc_codec_dev_ac97;
2922+
2923+#endif
2924Index: linux-2.6-pxa-new/sound/soc/codecs/ak4535.c
2925===================================================================
2926--- /dev/null
2927+++ linux-2.6-pxa-new/sound/soc/codecs/ak4535.c
2928@@ -0,0 +1,701 @@
2929+/*
2930+ * ak4535.c -- AK4535 ALSA Soc Audio driver
2931+ *
2932+ * Copyright 2005 Openedhand Ltd.
2933+ *
2934+ * Author: Richard Purdie <richard@openedhand.com>
2935+ *
2936+ * Based on wm8753.c by Liam Girdwood
2937+ *
2938+ * This program is free software; you can redistribute it and/or modify
2939+ * it under the terms of the GNU General Public License version 2 as
2940+ * published by the Free Software Foundation.
2941+ */
2942+
2943+#include <linux/module.h>
2944+#include <linux/moduleparam.h>
2945+#include <linux/init.h>
2946+#include <linux/delay.h>
2947+#include <linux/pm.h>
2948+#include <linux/i2c.h>
2949+#include <linux/platform_device.h>
2950+#include <sound/driver.h>
2951+#include <sound/core.h>
2952+#include <sound/pcm.h>
2953+#include <sound/pcm_params.h>
2954+#include <sound/soc.h>
2955+#include <sound/soc-dapm.h>
2956+#include <sound/initval.h>
2957+
2958+#include "ak4535.h"
2959+
2960+#define AUDIO_NAME "ak4535"
2961+#define AK4535_VERSION "0.3"
2962+
2963+struct snd_soc_codec_device soc_codec_dev_ak4535;
2964+
2965+/*
2966+ * ak4535 register cache
2967+ */
2968+static const u16 ak4535_reg[AK4535_CACHEREGNUM] = {
2969+ 0x0000, 0x0080, 0x0000, 0x0003,
2970+ 0x0002, 0x0000, 0x0011, 0x0001,
2971+ 0x0000, 0x0040, 0x0036, 0x0010,
2972+ 0x0000, 0x0000, 0x0057, 0x0000,
2973+};
2974+
2975+#define AK4535_DAIFMT \
2976+ (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_CBS_CFS | \
2977+ SND_SOC_DAIFMT_NB_NF)
2978+
2979+#define AK4535_DIR \
2980+ (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
2981+
2982+#define AK4535_RATES \
2983+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
2984+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
2985+ SNDRV_PCM_RATE_48000)
2986+
2987+static struct snd_soc_dai_mode ak4535_modes[] = {
2988+ /* codec frame and clock slave modes */
2989+ {
2990+ .fmt = AK4535_DAIFMT,
2991+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
2992+ .pcmrate = AK4535_RATES,
2993+ .pcmdir = AK4535_DIR,
2994+ .flags = SND_SOC_DAI_BFS_RATE,
2995+ .fs = 256,
2996+ .bfs = 64,
2997+ },
2998+ {
2999+ .fmt = AK4535_DAIFMT,
3000+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
3001+ .pcmrate = AK4535_RATES,
3002+ .pcmdir = AK4535_DIR,
3003+ .flags = SND_SOC_DAI_BFS_RATE,
3004+ .fs = 256,
3005+ .bfs = 32,
3006+ },
3007+};
3008+
3009+/*
3010+ * read ak4535 register cache
3011+ */
3012+static inline unsigned int ak4535_read_reg_cache(struct snd_soc_codec *codec,
3013+ unsigned int reg)
3014+{
3015+ u16 *cache = codec->reg_cache;
3016+ if (reg >= AK4535_CACHEREGNUM)
3017+ return -1;
3018+ return cache[reg];
3019+}
3020+
3021+/*
3022+ * write ak4535 register cache
3023+ */
3024+static inline void ak4535_write_reg_cache(struct snd_soc_codec *codec,
3025+ u16 reg, unsigned int value)
3026+{
3027+ u16 *cache = codec->reg_cache;
3028+ if (reg >= AK4535_CACHEREGNUM)
3029+ return;
3030+ cache[reg] = value;
3031+}
3032+
3033+/*
3034+ * write to the AK4535 register space
3035+ */
3036+static int ak4535_write(struct snd_soc_codec *codec, unsigned int reg,
3037+ unsigned int value)
3038+{
3039+ u8 data[2];
3040+
3041+ /* data is
3042+ * D15..D8 AK4535 register offset
3043+ * D7...D0 register data
3044+ */
3045+ data[0] = reg & 0xff;
3046+ data[1] = value & 0xff;
3047+
3048+ ak4535_write_reg_cache (codec, reg, value);
3049+ if (codec->hw_write(codec->control_data, data, 2) == 2)
3050+ return 0;
3051+ else
3052+ return -EIO;
3053+}
3054+
3055+static const char *ak4535_mono_gain[] = {"+6dB", "-17dB"};
3056+static const char *ak4535_mono_out[] = {"(L + R)/2", "Hi-Z"};
3057+static const char *ak4535_hp_out[] = {"Stereo", "Mono"};
3058+static const char *ak4535_deemp[] = {"44.1kHz", "Off", "48kHz", "32kHz"};
3059+static const char *ak4535_mic_select[] = {"Internal", "External"};
3060+
3061+static const struct soc_enum ak4535_enum[] = {
3062+ SOC_ENUM_SINGLE(AK4535_SIG1, 7, 2, ak4535_mono_gain),
3063+ SOC_ENUM_SINGLE(AK4535_SIG1, 6, 2, ak4535_mono_out),
3064+ SOC_ENUM_SINGLE(AK4535_MODE2, 2, 2, ak4535_hp_out),
3065+ SOC_ENUM_SINGLE(AK4535_DAC, 0, 4, ak4535_deemp),
3066+ SOC_ENUM_SINGLE(AK4535_MIC, 1, 2, ak4535_mic_select),
3067+};
3068+
3069+static const struct snd_kcontrol_new ak4535_snd_controls[] = {
3070+ SOC_SINGLE("ALC2 Switch", AK4535_SIG1, 1, 1, 0),
3071+ SOC_ENUM("Mono 1 Output", ak4535_enum[1]),
3072+ SOC_ENUM("Mono 1 Gain", ak4535_enum[0]),
3073+ SOC_ENUM("Headphone Output", ak4535_enum[2]),
3074+ SOC_ENUM("Playback Deemphasis", ak4535_enum[3]),
3075+ SOC_SINGLE("Bass Volume", AK4535_DAC, 2, 3, 0),
3076+ SOC_SINGLE("Mic Boost (+20dB) Switch", AK4535_MIC, 0, 1, 0),
3077+ SOC_ENUM("Mic Select", ak4535_enum[4]),
3078+ SOC_SINGLE("ALC Operation Time", AK4535_TIMER, 0, 3, 0),
3079+ SOC_SINGLE("ALC Recovery Time", AK4535_TIMER, 2, 3, 0),
3080+ SOC_SINGLE("ALC ZC Time", AK4535_TIMER, 4, 3, 0),
3081+ SOC_SINGLE("ALC 1 Switch", AK4535_ALC1, 5, 1, 0),
3082+ SOC_SINGLE("ALC 2 Switch", AK4535_ALC1, 6, 1, 0),
3083+ SOC_SINGLE("ALC Volume", AK4535_ALC2, 0, 127, 0),
3084+ SOC_SINGLE("Capture Volume", AK4535_PGA, 0, 127, 0),
3085+ SOC_SINGLE("Left Playback Volume", AK4535_LATT, 0, 127, 1),
3086+ SOC_SINGLE("Right Playback Volume", AK4535_RATT, 0, 127, 1),
3087+ SOC_SINGLE("AUX Bypass Volume", AK4535_VOL, 0, 15, 0),
3088+ SOC_SINGLE("Mic Sidetone Volume", AK4535_VOL, 4, 7, 0),
3089+};
3090+
3091+/* add non dapm controls */
3092+static int ak4535_add_controls(struct snd_soc_codec *codec)
3093+{
3094+ int err, i;
3095+
3096+ for (i = 0; i < ARRAY_SIZE(ak4535_snd_controls); i++) {
3097+ err = snd_ctl_add(codec->card,
3098+ snd_soc_cnew(&ak4535_snd_controls[i],codec, NULL));
3099+ if (err < 0)
3100+ return err;
3101+ }
3102+
3103+ return 0;
3104+}
3105+
3106+/* Mono 1 Mixer */
3107+static const struct snd_kcontrol_new ak4535_mono1_mixer_controls[] = {
3108+ SOC_DAPM_SINGLE("Mic Sidetone Switch", AK4535_SIG1, 4, 1, 0),
3109+ SOC_DAPM_SINGLE("Mono Playback Switch", AK4535_SIG1, 5, 1, 0),
3110+};
3111+
3112+/* Stereo Mixer */
3113+static const struct snd_kcontrol_new ak4535_stereo_mixer_controls[] = {
3114+ SOC_DAPM_SINGLE("Mic Sidetone Switch", AK4535_SIG2, 4, 1, 0),
3115+ SOC_DAPM_SINGLE("Playback Switch", AK4535_SIG2, 7, 1, 0),
3116+ SOC_DAPM_SINGLE("Aux Bypass Switch", AK4535_SIG2, 5, 1, 0),
3117+};
3118+
3119+/* Input Mixer */
3120+static const struct snd_kcontrol_new ak4535_input_mixer_controls[] = {
3121+ SOC_DAPM_SINGLE("Mic Capture Switch", AK4535_MIC, 2, 1, 0),
3122+ SOC_DAPM_SINGLE("Aux Capture Switch", AK4535_MIC, 5, 1, 0),
3123+};
3124+
3125+/* Input mux */
3126+static const struct snd_kcontrol_new ak4535_input_mux_control =
3127+ SOC_DAPM_ENUM("Input Select", ak4535_enum[0]);
3128+
3129+/* HP L switch */
3130+static const struct snd_kcontrol_new ak4535_hpl_control =
3131+ SOC_DAPM_SINGLE("Switch", AK4535_SIG2, 1, 1, 1);
3132+
3133+/* HP R switch */
3134+static const struct snd_kcontrol_new ak4535_hpr_control =
3135+ SOC_DAPM_SINGLE("Switch", AK4535_SIG2, 0, 1, 1);
3136+
3137+/* Speaker switch */
3138+static const struct snd_kcontrol_new ak4535_spk_control =
3139+ SOC_DAPM_SINGLE("Switch", AK4535_MODE2, 0, 0, 0);
3140+
3141+/* mono 2 switch */
3142+static const struct snd_kcontrol_new ak4535_mono2_control =
3143+ SOC_DAPM_SINGLE("Switch", AK4535_SIG1, 0, 1, 0);
3144+
3145+/* Line out switch */
3146+static const struct snd_kcontrol_new ak4535_line_control =
3147+ SOC_DAPM_SINGLE("Switch", AK4535_SIG2, 6, 1, 0);
3148+
3149+/* ak4535 dapm widgets */
3150+static const struct snd_soc_dapm_widget ak4535_dapm_widgets[] = {
3151+ SND_SOC_DAPM_MIXER("Stereo Mixer", SND_SOC_NOPM, 0, 0,
3152+ &ak4535_stereo_mixer_controls[0],
3153+ ARRAY_SIZE(ak4535_stereo_mixer_controls)),
3154+ SND_SOC_DAPM_MIXER("Mono1 Mixer", SND_SOC_NOPM, 0, 0,
3155+ &ak4535_mono1_mixer_controls[0],
3156+ ARRAY_SIZE(ak4535_mono1_mixer_controls)),
3157+ SND_SOC_DAPM_MIXER("Input Mixer", SND_SOC_NOPM, 0, 0,
3158+ &ak4535_input_mixer_controls[0],
3159+ ARRAY_SIZE(ak4535_mono1_mixer_controls)),
3160+ SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0,
3161+ &ak4535_input_mux_control),
3162+ SND_SOC_DAPM_DAC("DAC", "Playback", AK4535_PM2, 0, 0),
3163+ SND_SOC_DAPM_SWITCH("Mono 2 Enable", SND_SOC_NOPM, 0, 0,
3164+ &ak4535_mono2_control),
3165+ SND_SOC_DAPM_SWITCH("Speaker Enable", SND_SOC_NOPM, 0, 0,
3166+ &ak4535_spk_control),
3167+ SND_SOC_DAPM_SWITCH("Line Out Enable", SND_SOC_NOPM, 0, 0,
3168+ &ak4535_line_control),
3169+ SND_SOC_DAPM_SWITCH("Left HP Enable", SND_SOC_NOPM, 0, 0,
3170+ &ak4535_hpl_control),
3171+ SND_SOC_DAPM_SWITCH("Right HP Enable", SND_SOC_NOPM, 0, 0,
3172+ &ak4535_hpr_control),
3173+ SND_SOC_DAPM_OUTPUT("LOUT"),
3174+ SND_SOC_DAPM_OUTPUT("HPL"),
3175+ SND_SOC_DAPM_OUTPUT("ROUT"),
3176+ SND_SOC_DAPM_OUTPUT("HPR"),
3177+ SND_SOC_DAPM_OUTPUT("SPP"),
3178+ SND_SOC_DAPM_OUTPUT("SPN"),
3179+ SND_SOC_DAPM_OUTPUT("MOUT1"),
3180+ SND_SOC_DAPM_OUTPUT("MOUT2"),
3181+ SND_SOC_DAPM_OUTPUT("MICOUT"),
3182+ SND_SOC_DAPM_ADC("ADC", "Capture", AK4535_PM1, 0, 1),
3183+ SND_SOC_DAPM_PGA("Spk Amp", AK4535_PM2, 3, 0, NULL, 0),
3184+ SND_SOC_DAPM_PGA("HP R Amp", AK4535_PM2, 1, 0, NULL, 0),
3185+ SND_SOC_DAPM_PGA("HP L Amp", AK4535_PM2, 2, 0, NULL, 0),
3186+ SND_SOC_DAPM_PGA("Mic", AK4535_PM1, 1, 0, NULL, 0),
3187+ SND_SOC_DAPM_PGA("Line Out", AK4535_PM1, 4, 0, NULL, 0),
3188+ SND_SOC_DAPM_PGA("Mono Out", AK4535_PM1, 3, 0, NULL, 0),
3189+ SND_SOC_DAPM_PGA("AUX In", AK4535_PM1, 2, 0, NULL, 0),
3190+
3191+ SND_SOC_DAPM_MICBIAS("Mic Int Bias", AK4535_MIC, 3, 0),
3192+ SND_SOC_DAPM_MICBIAS("Mic Ext Bias", AK4535_MIC, 4, 0),
3193+ SND_SOC_DAPM_INPUT("MICIN"),
3194+ SND_SOC_DAPM_INPUT("MICEXT"),
3195+ SND_SOC_DAPM_INPUT("AUX"),
3196+ SND_SOC_DAPM_INPUT("MIN"),
3197+ SND_SOC_DAPM_INPUT("AIN"),
3198+};
3199+
3200+static const char *audio_map[][3] = {
3201+ /*stereo mixer */
3202+ {"Stereo Mixer", "Playback Switch", "DAC"},
3203+ {"Stereo Mixer", "Mic Sidetone Switch", "Mic"},
3204+ {"Stereo Mixer", "Aux Bypass Switch", "AUX In"},
3205+
3206+ /* mono1 mixer */
3207+ {"Mono1 Mixer", "Mic Sidetone Switch", "Mic"},
3208+ {"Mono1 Mixer", "Mono Playback Switch", "DAC"},
3209+
3210+ /* mono2 mixer */
3211+ {"Mono2 Mixer", "Mono Playback Switch", "Stereo Mixer"},
3212+
3213+ /* Mic */
3214+ {"AIN", NULL, "Mic"},
3215+ {"Input Mux", "Internal", "Mic Int Bias"},
3216+ {"Input Mux", "External", "Mic Ext Bias"},
3217+ {"Mic Int Bias", NULL, "MICIN"},
3218+ {"Mic Ext Bias", NULL, "MICEXT"},
3219+ {"MICOUT", NULL, "Input Mux"},
3220+
3221+ /* line out */
3222+ {"LOUT", "Switch", "Line"},
3223+ {"ROUT", "Switch", "Line Out Enable"},
3224+ {"Line Out Enable", NULL, "Line Out"},
3225+ {"Line Out", NULL, "Stereo Mixer"},
3226+
3227+ /* mono1 out */
3228+ {"MOUT1", NULL, "Mono Out"},
3229+ {"Mono Out", NULL, "Mono Mixer"},
3230+
3231+ /* left HP */
3232+ {"HPL", "Switch", "Left HP Enable"},
3233+ {"Left HP Enable", NULL, "HP L Amp"},
3234+ {"HP L Amp", NULL, "Stereo Mixer"},
3235+
3236+ /* right HP */
3237+ {"HPR", "Switch", "Right HP Enable"},
3238+ {"Right HP Enable", NULL, "HP R Amp"},
3239+ {"HP R Amp", NULL, "Stereo Mixer"},
3240+
3241+ /* speaker */
3242+ {"SPP", "Switch", "Speaker Enable"},
3243+ {"SPN", "Switch", "Speaker Enable"},
3244+ {"Speaker Enable", NULL, "Spk Amp"},
3245+ {"Spk Amp", NULL, "MIN"},
3246+
3247+ /* mono 2 */
3248+ {"MOUT2", "Switch", "Mono 2 Enable"},
3249+ {"Mono 2 Enable", NULL, "Stereo Mixer"},
3250+
3251+ /* Aux In */
3252+ {"Aux In", NULL, "AUX"},
3253+
3254+ /* ADC */
3255+ {"ADC", NULL, "Input Mixer"},
3256+ {"Input Mixer", "Mic Capture Switch", "Mic"},
3257+ {"Input Mixer", "Aux Capture Switch", "Aux In"},
3258+
3259+ /* terminator */
3260+ {NULL, NULL, NULL},
3261+};
3262+
3263+static int ak4535_add_widgets(struct snd_soc_codec *codec)
3264+{
3265+ int i;
3266+
3267+ for(i = 0; i < ARRAY_SIZE(ak4535_dapm_widgets); i++) {
3268+ snd_soc_dapm_new_control(codec, &ak4535_dapm_widgets[i]);
3269+ }
3270+
3271+ /* set up audio path audio_mapnects */
3272+ for(i = 0; audio_map[i][0] != NULL; i++) {
3273+ snd_soc_dapm_connect_input(codec, audio_map[i][0],
3274+ audio_map[i][1], audio_map[i][2]);
3275+ }
3276+
3277+ snd_soc_dapm_new_widgets(codec);
3278+ return 0;
3279+}
3280+
3281+static int ak4535_pcm_prepare(struct snd_pcm_substream *substream)
3282+{
3283+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
3284+ struct snd_soc_device *socdev = rtd->socdev;
3285+ struct snd_soc_codec *codec = socdev->codec;
3286+ u8 mode = 0, mode2;
3287+ int bfs;
3288+
3289+ mode2 = ak4535_read_reg_cache(codec, AK4535_MODE2);
3290+ bfs = SND_SOC_FSBW_REAL(rtd->codec_dai->dai_runtime.bfs);
3291+ snd_assert(bfs, return -ENODEV);
3292+
3293+ /* interface format */
3294+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
3295+ case SND_SOC_DAIFMT_I2S:
3296+ mode = 0x0002;
3297+ break;
3298+ case SND_SOC_DAIFMT_LEFT_J:
3299+ mode = 0x0001;
3300+ break;
3301+ }
3302+
3303+ /* set fs */
3304+ switch (rtd->codec_dai->dai_runtime.fs) {
3305+ case 1024:
3306+ mode2 |= (0x3 << 5);
3307+ break;
3308+ case 512:
3309+ mode2 |= (0x2 << 5);
3310+ break;
3311+ case 256:
3312+ mode2 |= (0x1 << 5);
3313+ break;
3314+ }
3315+
3316+ /* bfs */
3317+ if (bfs == 64)
3318+ mode |= 0x4;
3319+
3320+ /* set rate */
3321+ ak4535_write(codec, AK4535_MODE1, mode);
3322+ ak4535_write(codec, AK4535_MODE2, mode2);
3323+
3324+ return 0;
3325+}
3326+
3327+static unsigned int ak4535_config_sysclk(struct snd_soc_codec_dai *dai,
3328+ struct snd_soc_clock_info *info, unsigned int clk)
3329+{
3330+ if (info->fs != 256)
3331+ return 0;
3332+
3333+ /* we only support 256 FS atm */
3334+ if (info->rate * info->fs == clk) {
3335+ dai->mclk = clk;
3336+ return clk;
3337+ }
3338+
3339+ return 0;
3340+}
3341+
3342+static int ak4535_mute(struct snd_soc_codec *codec,
3343+ struct snd_soc_codec_dai *dai, int mute)
3344+{
3345+ u16 mute_reg = ak4535_read_reg_cache(codec, AK4535_DAC) & 0xffdf;
3346+ if (mute)
3347+ ak4535_write(codec, AK4535_DAC, mute_reg);
3348+ else
3349+ ak4535_write(codec, AK4535_DAC, mute_reg | 0x20);
3350+ return 0;
3351+}
3352+
3353+static int ak4535_dapm_event(struct snd_soc_codec *codec, int event)
3354+{
3355+ switch (event) {
3356+ case SNDRV_CTL_POWER_D0: /* full On */
3357+ /* vref/mid, clk and osc on, dac unmute, active */
3358+ case SNDRV_CTL_POWER_D1: /* partial On */
3359+ case SNDRV_CTL_POWER_D2: /* partial On */
3360+ break;
3361+ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
3362+ /* everything off except vref/vmid, dac mute, inactive */
3363+ ak4535_write(codec, AK4535_PM1, 0x80);
3364+ ak4535_write(codec, AK4535_PM2, 0x0);
3365+ break;
3366+ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
3367+ /* everything off, inactive */
3368+ ak4535_write(codec, AK4535_PM1, 0x0);
3369+ ak4535_write(codec, AK4535_PM2, 0x80);
3370+ break;
3371+ }
3372+ codec->dapm_state = event;
3373+ return 0;
3374+}
3375+
3376+struct snd_soc_codec_dai ak4535_dai = {
3377+ .name = "AK4535",
3378+ .playback = {
3379+ .stream_name = "Playback",
3380+ .channels_min = 1,
3381+ .channels_max = 2,
3382+ },
3383+ .capture = {
3384+ .stream_name = "Capture",
3385+ .channels_min = 1,
3386+ .channels_max = 2,
3387+ },
3388+ .config_sysclk = ak4535_config_sysclk,
3389+ .digital_mute = ak4535_mute,
3390+ .ops = {
3391+ .prepare = ak4535_pcm_prepare,
3392+ },
3393+ .caps = {
3394+ .num_modes = ARRAY_SIZE(ak4535_modes),
3395+ .mode = ak4535_modes,
3396+ },
3397+};
3398+EXPORT_SYMBOL_GPL(ak4535_dai);
3399+
3400+static int ak4535_suspend(struct platform_device *pdev, pm_message_t state)
3401+{
3402+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
3403+ struct snd_soc_codec *codec = socdev->codec;
3404+
3405+ ak4535_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
3406+ return 0;
3407+}
3408+
3409+static int ak4535_resume(struct platform_device *pdev)
3410+{
3411+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
3412+ struct snd_soc_codec *codec = socdev->codec;
3413+ int i;
3414+ u8 data[2];
3415+ u16 *cache = codec->reg_cache;
3416+
3417+ /* Sync reg_cache with the hardware */
3418+ for (i = 0; i < ARRAY_SIZE(ak4535_reg); i++) {
3419+ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
3420+ data[1] = cache[i] & 0x00ff;
3421+ codec->hw_write(codec->control_data, data, 2);
3422+ }
3423+ ak4535_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
3424+ ak4535_dapm_event(codec, codec->suspend_dapm_state);
3425+ return 0;
3426+}
3427+
3428+/*
3429+ * initialise the AK4535 driver
3430+ * register the mixer and dsp interfaces with the kernel
3431+ */
3432+static int ak4535_init(struct snd_soc_device *socdev)
3433+{
3434+ struct snd_soc_codec *codec = socdev->codec;
3435+ int ret = 0;
3436+
3437+ codec->name = "AK4535";
3438+ codec->owner = THIS_MODULE;
3439+ codec->read = ak4535_read_reg_cache;
3440+ codec->write = ak4535_write;
3441+ codec->dapm_event = ak4535_dapm_event;
3442+ codec->dai = &ak4535_dai;
3443+ codec->num_dai = 1;
3444+ codec->reg_cache_size = ARRAY_SIZE(ak4535_reg);
3445+ codec->reg_cache =
3446+ kzalloc(sizeof(u16) * ARRAY_SIZE(ak4535_reg), GFP_KERNEL);
3447+ if (codec->reg_cache == NULL)
3448+ return -ENOMEM;
3449+ memcpy(codec->reg_cache, ak4535_reg,
3450+ sizeof(u16) * ARRAY_SIZE(ak4535_reg));
3451+ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(ak4535_reg);
3452+
3453+ /* register pcms */
3454+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
3455+ if (ret < 0) {
3456+ kfree(codec->reg_cache);
3457+ return ret;
3458+ }
3459+
3460+ /* power on device */
3461+ ak4535_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
3462+
3463+ ak4535_add_controls(codec);
3464+ ak4535_add_widgets(codec);
3465+ ret = snd_soc_register_card(socdev);
3466+ if (ret < 0) {
3467+ snd_soc_free_pcms(socdev);
3468+ snd_soc_dapm_free(socdev);
3469+ }
3470+
3471+ return ret;
3472+}
3473+
3474+static struct snd_soc_device *ak4535_socdev;
3475+
3476+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
3477+
3478+#define I2C_DRIVERID_AK4535 0xfefe /* liam - need a proper id */
3479+
3480+static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
3481+
3482+/* Magic definition of all other variables and things */
3483+I2C_CLIENT_INSMOD;
3484+
3485+static struct i2c_driver ak4535_i2c_driver;
3486+static struct i2c_client client_template;
3487+
3488+/* If the i2c layer weren't so broken, we could pass this kind of data
3489+ around */
3490+static int ak4535_codec_probe(struct i2c_adapter *adap, int addr, int kind)
3491+{
3492+ struct snd_soc_device *socdev = ak4535_socdev;
3493+ struct ak4535_setup_data *setup = socdev->codec_data;
3494+ struct snd_soc_codec *codec = socdev->codec;
3495+ struct i2c_client *i2c;
3496+ int ret;
3497+
3498+ if (addr != setup->i2c_address)
3499+ return -ENODEV;
3500+
3501+ client_template.adapter = adap;
3502+ client_template.addr = addr;
3503+
3504+ i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
3505+ if (i2c == NULL){
3506+ kfree(codec);
3507+ return -ENOMEM;
3508+ }
3509+ memcpy(i2c, &client_template, sizeof(struct i2c_client));
3510+ i2c_set_clientdata(i2c, codec);
3511+ codec->control_data = i2c;
3512+
3513+ ret = i2c_attach_client(i2c);
3514+ if (ret < 0) {
3515+ printk(KERN_ERR "failed to attach codec at addr %x\n", addr);
3516+ goto err;
3517+ }
3518+
3519+ ret = ak4535_init(socdev);
3520+ if (ret < 0) {
3521+ printk(KERN_ERR "failed to initialise AK4535\n");
3522+ goto err;
3523+ }
3524+ return ret;
3525+
3526+err:
3527+ kfree(codec);
3528+ kfree(i2c);
3529+ return ret;
3530+}
3531+
3532+static int ak4535_i2c_detach(struct i2c_client *client)
3533+{
3534+ struct snd_soc_codec* codec = i2c_get_clientdata(client);
3535+ i2c_detach_client(client);
3536+ kfree(codec->reg_cache);
3537+ kfree(client);
3538+
3539+ return 0;
3540+}
3541+
3542+static int ak4535_i2c_attach(struct i2c_adapter *adap)
3543+{
3544+ return i2c_probe(adap, &addr_data, ak4535_codec_probe);
3545+}
3546+
3547+/* corgi i2c codec control layer */
3548+static struct i2c_driver ak4535_i2c_driver = {
3549+ .driver = {
3550+ .name = "AK4535 I2C Codec",
3551+ .owner = THIS_MODULE,
3552+ },
3553+ .id = I2C_DRIVERID_AK4535,
3554+ .attach_adapter = ak4535_i2c_attach,
3555+ .detach_client = ak4535_i2c_detach,
3556+ .command = NULL,
3557+};
3558+
3559+static struct i2c_client client_template = {
3560+ .name = "AK4535",
3561+ .driver = &ak4535_i2c_driver,
3562+};
3563+#endif
3564+
3565+static int ak4535_probe(struct platform_device *pdev)
3566+{
3567+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
3568+ struct ak4535_setup_data *setup;
3569+ struct snd_soc_codec* codec;
3570+ int ret = 0;
3571+
3572+ printk(KERN_INFO "AK4535 Audio Codec %s", AK4535_VERSION);
3573+
3574+ setup = socdev->codec_data;
3575+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
3576+ if (codec == NULL)
3577+ return -ENOMEM;
3578+
3579+ socdev->codec = codec;
3580+ mutex_init(&codec->mutex);
3581+ INIT_LIST_HEAD(&codec->dapm_widgets);
3582+ INIT_LIST_HEAD(&codec->dapm_paths);
3583+
3584+ ak4535_socdev = socdev;
3585+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
3586+ if (setup->i2c_address) {
3587+ normal_i2c[0] = setup->i2c_address;
3588+ codec->hw_write = (hw_write_t)i2c_master_send;
3589+ ret = i2c_add_driver(&ak4535_i2c_driver);
3590+ if (ret != 0)
3591+ printk(KERN_ERR "can't add i2c driver");
3592+ }
3593+#else
3594+ /* Add other interfaces here */
3595+#endif
3596+ return ret;
3597+}
3598+
3599+/* power down chip */
3600+static int ak4535_remove(struct platform_device *pdev)
3601+{
3602+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
3603+ struct snd_soc_codec* codec = socdev->codec;
3604+
3605+ if (codec->control_data)
3606+ ak4535_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
3607+
3608+ snd_soc_free_pcms(socdev);
3609+ snd_soc_dapm_free(socdev);
3610+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
3611+ i2c_del_driver(&ak4535_i2c_driver);
3612+#endif
3613+ kfree(codec);
3614+
3615+ return 0;
3616+}
3617+
3618+struct snd_soc_codec_device soc_codec_dev_ak4535 = {
3619+ .probe = ak4535_probe,
3620+ .remove = ak4535_remove,
3621+ .suspend = ak4535_suspend,
3622+ .resume = ak4535_resume,
3623+};
3624+
3625+EXPORT_SYMBOL_GPL(soc_codec_dev_ak4535);
3626+
3627+MODULE_DESCRIPTION("Soc AK4535 driver");
3628+MODULE_AUTHOR("Richard Purdie");
3629+MODULE_LICENSE("GPL");
3630Index: linux-2.6-pxa-new/sound/soc/codecs/ak4535.h
3631===================================================================
3632--- /dev/null
3633+++ linux-2.6-pxa-new/sound/soc/codecs/ak4535.h
3634@@ -0,0 +1,46 @@
3635+/*
3636+ * ak4535.h -- AK4535 Soc Audio driver
3637+ *
3638+ * Copyright 2005 Openedhand Ltd.
3639+ *
3640+ * Author: Richard Purdie <richard@openedhand.com>
3641+ *
3642+ * Based on wm8753.h
3643+ *
3644+ * This program is free software; you can redistribute it and/or modify
3645+ * it under the terms of the GNU General Public License version 2 as
3646+ * published by the Free Software Foundation.
3647+ */
3648+
3649+#ifndef _AK4535_H
3650+#define _AK4535_H
3651+
3652+/* AK4535 register space */
3653+
3654+#define AK4535_PM1 0x0
3655+#define AK4535_PM2 0x1
3656+#define AK4535_SIG1 0x2
3657+#define AK4535_SIG2 0x3
3658+#define AK4535_MODE1 0x4
3659+#define AK4535_MODE2 0x5
3660+#define AK4535_DAC 0x6
3661+#define AK4535_MIC 0x7
3662+#define AK4535_TIMER 0x8
3663+#define AK4535_ALC1 0x9
3664+#define AK4535_ALC2 0xa
3665+#define AK4535_PGA 0xb
3666+#define AK4535_LATT 0xc
3667+#define AK4535_RATT 0xd
3668+#define AK4535_VOL 0xe
3669+#define AK4535_STATUS 0xf
3670+
3671+#define AK4535_CACHEREGNUM 0x10
3672+
3673+struct ak4535_setup_data {
3674+ unsigned short i2c_address;
3675+};
3676+
3677+extern struct snd_soc_codec_dai ak4535_dai;
3678+extern struct snd_soc_codec_device soc_codec_dev_ak4535;
3679+
3680+#endif
3681Index: linux-2.6-pxa-new/sound/soc/codecs/uda1380.c
3682===================================================================
3683--- /dev/null
3684+++ linux-2.6-pxa-new/sound/soc/codecs/uda1380.c
3685@@ -0,0 +1,582 @@
3686+/*
3687+ * uda1380.c - Philips UDA1380 ALSA SoC audio driver
3688+ *
3689+ * This program is free software; you can redistribute it and/or modify
3690+ * it under the terms of the GNU General Public License version 2 as
3691+ * published by the Free Software Foundation.
3692+ *
3693+ * Modified by Richard Purdie <richard@openedhand.com> to fit into SoC
3694+ * codec model.
3695+ *
3696+ * Copyright (c) 2005 Giorgio Padrin <giorgio@mandarinlogiq.org>
3697+ * Copyright 2005 Openedhand Ltd.
3698+ */
3699+
3700+#include <linux/module.h>
3701+#include <linux/init.h>
3702+#include <linux/types.h>
3703+#include <linux/string.h>
3704+#include <linux/slab.h>
3705+#include <linux/errno.h>
3706+#include <linux/ioctl.h>
3707+#include <linux/delay.h>
3708+#include <linux/i2c.h>
3709+#include <sound/driver.h>
3710+#include <sound/core.h>
3711+#include <sound/control.h>
3712+#include <sound/initval.h>
3713+#include <sound/info.h>
3714+#include <sound/soc.h>
3715+#include <sound/soc-dapm.h>
3716+
3717+#include "uda1380.h"
3718+
3719+#define UDA1380_VERSION "0.4"
3720+
3721+/*
3722+ * uda1380 register cache
3723+ */
3724+static const u16 uda1380_reg[UDA1380_CACHEREGNUM] = {
3725+ 0x0502, 0x0000, 0x0000, 0x3f3f,
3726+ 0x0202, 0x0000, 0x0000, 0x0000,
3727+ 0x0000, 0x0000, 0x0000, 0x0000,
3728+ 0x0000, 0x0000, 0x0000, 0x0000,
3729+ 0x0000, 0xff00, 0x0000, 0x4800,
3730+ 0x0000, 0x0000, 0x0000, 0x0000,
3731+ 0x0000, 0x0000, 0x0000, 0x0000,
3732+ 0x0000, 0x0000, 0x0000, 0x0000,
3733+ 0x0000, 0x8000, 0x0002, 0x0000,
3734+};
3735+
3736+#define UDA1380_DAIFMT \
3737+ (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_CBS_CFS | \
3738+ SND_SOC_DAIFMT_NB_NF)
3739+
3740+#define UDA1380_DIR \
3741+ (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
3742+
3743+#define UDA1380_RATES \
3744+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
3745+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
3746+ SNDRV_PCM_RATE_48000)
3747+
3748+static struct snd_soc_dai_mode uda1380_modes[] = {
3749+ /* slave rates capture & playback */
3750+ {
3751+ .fmt = UDA1380_DAIFMT,
3752+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
3753+ .pcmrate = UDA1380_RATES,
3754+ .pcmdir = UDA1380_DIR,
3755+ .flags = SND_SOC_DAI_BFS_RATE,
3756+ .fs = 256,
3757+ .bfs = 64,
3758+ },
3759+
3760+ /* slave rates playback */
3761+ {
3762+ .fmt = UDA1380_DAIFMT,
3763+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
3764+ .pcmrate = SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000,
3765+ .pcmdir = SND_SOC_DAIDIR_PLAYBACK,
3766+ .flags = SND_SOC_DAI_BFS_RATE,
3767+ .fs = 256,
3768+ .bfs = 64,
3769+ },
3770+};
3771+
3772+/*
3773+ * read uda1380 register cache
3774+ */
3775+static inline unsigned int uda1380_read_reg_cache(struct snd_soc_codec *codec,
3776+ unsigned int reg)
3777+{
3778+ u16 *cache = codec->reg_cache;
3779+ if (reg == UDA1380_RESET)
3780+ return 0;
3781+ if (reg >= UDA1380_CACHEREGNUM)
3782+ return -1;
3783+ return cache[reg];
3784+}
3785+
3786+/*
3787+ * write uda1380 register cache
3788+ */
3789+static inline void uda1380_write_reg_cache(struct snd_soc_codec *codec,
3790+ u16 reg, unsigned int value)
3791+{
3792+ u16 *cache = codec->reg_cache;
3793+ if (reg >= UDA1380_CACHEREGNUM)
3794+ return;
3795+ cache[reg] = value;
3796+}
3797+
3798+/*
3799+ * write to the UDA1380 register space
3800+ */
3801+static int uda1380_write(struct snd_soc_codec *codec, unsigned int reg,
3802+ unsigned int value)
3803+{
3804+ u8 data[3];
3805+
3806+ /* data is
3807+ * data[0] is register offset
3808+ * data[1] is MS byte
3809+ * data[2] is LS byte
3810+ */
3811+ data[0] = reg;
3812+ data[1] = (value & 0xff00) >> 8;
3813+ data[2] = value & 0x00ff;
3814+
3815+ uda1380_write_reg_cache (codec, reg, value);
3816+ if (codec->hw_write(codec->control_data, data, 3) == 3)
3817+ return 0;
3818+ else
3819+ return -EIO;
3820+}
3821+
3822+#define uda1380_reset(c) uda1380_write(c, UDA1380_RESET, 0)
3823+
3824+/* declarations of ALSA reg_elem_REAL controls */
3825+static const char *uda1380_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz",
3826+ "96kHz"};
3827+static const char *uda1380_input_sel[] = {"Line", "Mic"};
3828+
3829+static const struct soc_enum uda1380_enum[] = {
3830+ SOC_ENUM_DOUBLE(UDA1380_DEEMP, 0, 8, 5, uda1380_deemp),
3831+ SOC_ENUM_SINGLE(UDA1380_ADC, 3, 2, uda1380_input_sel),
3832+};
3833+
3834+static const struct snd_kcontrol_new uda1380_snd_controls[] = {
3835+ SOC_DOUBLE("Playback Volume", UDA1380_MVOL, 0, 8, 127, 0),
3836+ SOC_DOUBLE("Treble Volume", UDA1380_MODE, 4, 12, 3, 0),
3837+ SOC_DOUBLE("Bass Volume", UDA1380_MODE, 0, 8, 15, 0),
3838+ SOC_ENUM("Playback De-emphasis", uda1380_enum[0]),
3839+ SOC_DOUBLE("Capture Volume", UDA1380_DEC, 0, 8, 127, 0),
3840+ SOC_DOUBLE("Line Capture Volume", UDA1380_PGA, 0, 8, 15, 0),
3841+ SOC_SINGLE("Mic Capture Volume", UDA1380_PGA, 8, 11, 0),
3842+ SOC_DOUBLE("Playback Switch", UDA1380_DEEMP, 3, 11, 1, 0),
3843+ SOC_SINGLE("Capture Switch", UDA1380_PGA, 15, 1, 0),
3844+ SOC_SINGLE("AGC Timing", UDA1380_AGC, 8, 7, 0),
3845+ SOC_SINGLE("AGC Target level", UDA1380_AGC, 2, 3, 1),
3846+ SOC_SINGLE("AGC Switch", UDA1380_AGC, 0, 1, 0),
3847+};
3848+
3849+/* add non dapm controls */
3850+static int uda1380_add_controls(struct snd_soc_codec *codec)
3851+{
3852+ int err, i;
3853+
3854+ for (i = 0; i < ARRAY_SIZE(uda1380_snd_controls); i++) {
3855+ err = snd_ctl_add(codec->card,
3856+ snd_soc_cnew(&uda1380_snd_controls[i],codec, NULL));
3857+ if (err < 0)
3858+ return err;
3859+ }
3860+
3861+ return 0;
3862+}
3863+
3864+/* Input mux */
3865+static const struct snd_kcontrol_new uda1380_input_mux_control =
3866+ SOC_DAPM_ENUM("Input Select", uda1380_enum[1]);
3867+
3868+static const struct snd_soc_dapm_widget uda1380_dapm_widgets[] = {
3869+ SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0,
3870+ &uda1380_input_mux_control),
3871+ SND_SOC_DAPM_PGA("Left PGA", UDA1380_PM, 3, 0, NULL, 0),
3872+ SND_SOC_DAPM_PGA("Right PGA", UDA1380_PM, 1, 0, NULL, 0),
3873+ SND_SOC_DAPM_PGA("Mic LNA", UDA1380_PM, 4, 0, NULL, 0),
3874+ SND_SOC_DAPM_ADC("Left ADC", "Left Capture", UDA1380_PM, 2, 0),
3875+ SND_SOC_DAPM_ADC("Right ADC", "Right Capture", UDA1380_PM, 0, 0),
3876+ SND_SOC_DAPM_INPUT("VINM"),
3877+ SND_SOC_DAPM_INPUT("VINL"),
3878+ SND_SOC_DAPM_INPUT("VINR"),
3879+ SND_SOC_DAPM_MIXER("Analog Mixer", UDA1380_PM, 6, 0, NULL, 0),
3880+ SND_SOC_DAPM_OUTPUT("VOUTLHP"),
3881+ SND_SOC_DAPM_OUTPUT("VOUTRHP"),
3882+ SND_SOC_DAPM_OUTPUT("VOUTL"),
3883+ SND_SOC_DAPM_OUTPUT("VOUTR"),
3884+ SND_SOC_DAPM_DAC("DAC", "Playback", UDA1380_PM, 10, 0),
3885+ SND_SOC_DAPM_PGA("HeadPhone Driver", UDA1380_PM, 13, 0, NULL, 0),
3886+};
3887+
3888+static const char *audio_map[][3] = {
3889+
3890+ /* analog mixer setup is different from diagram for dapm */
3891+ {"HeadPhone Driver", NULL, "Analog Mixer"},
3892+ {"VOUTR", NULL, "Analog Mixer"},
3893+ {"VOUTL", NULL, "Analog Mixer"},
3894+ {"Analog Mixer", NULL, "VINR"},
3895+ {"Analog Mixer", NULL, "VINL"},
3896+ {"Analog Mixer", NULL, "DAC"},
3897+
3898+ /* headphone driver */
3899+ {"VOUTLHP", NULL, "HeadPhone Driver"},
3900+ {"VOUTRHP", NULL, "HeadPhone Driver"},
3901+
3902+ /* input mux */
3903+ {"Left ADC", NULL, "Input Mux"},
3904+ {"Input Mux", "Mic", "Mic LNA"},
3905+ {"Input Mux", "Line", "Left PGA"},
3906+
3907+ /* right input */
3908+ {"Right ADC", NULL, "Right PGA"},
3909+
3910+ /* inputs */
3911+ {"Mic LNA", NULL, "VINM"},
3912+ {"Left PGA", NULL, "VINL"},
3913+ {"Right PGA", NULL, "VINR"},
3914+
3915+ /* terminator */
3916+ {NULL, NULL, NULL},
3917+};
3918+
3919+static int uda1380_add_widgets(struct snd_soc_codec *codec)
3920+{
3921+ int i;
3922+
3923+ for(i = 0; i < ARRAY_SIZE(uda1380_dapm_widgets); i++) {
3924+ snd_soc_dapm_new_control(codec, &uda1380_dapm_widgets[i]);
3925+ }
3926+
3927+ /* set up audio path audio_mapnects */
3928+ for(i = 0; audio_map[i][0] != NULL; i++) {
3929+ snd_soc_dapm_connect_input(codec, audio_map[i][0],
3930+ audio_map[i][1], audio_map[i][2]);
3931+ }
3932+
3933+ snd_soc_dapm_new_widgets(codec);
3934+ return 0;
3935+}
3936+
3937+static int uda1380_pcm_prepare(struct snd_pcm_substream *substream)
3938+{
3939+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
3940+ struct snd_soc_device *socdev = rtd->socdev;
3941+ struct snd_soc_codec *codec = socdev->codec;
3942+ u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
3943+
3944+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
3945+ uda1380_write(codec, UDA1380_CLK, R00_EN_DAC | R00_EN_INT | clk);
3946+ else
3947+ uda1380_write(codec, UDA1380_CLK, R00_EN_ADC | R00_EN_DEC | clk);
3948+
3949+ return 0;
3950+}
3951+
3952+static void uda1380_pcm_shutdown(struct snd_pcm_substream *substream)
3953+{
3954+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
3955+ struct snd_soc_device *socdev = rtd->socdev;
3956+ struct snd_soc_codec *codec = socdev->codec;
3957+ u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
3958+
3959+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
3960+ uda1380_write(codec, UDA1380_CLK, ~(R00_EN_DAC | R00_EN_INT) & clk);
3961+ else
3962+ uda1380_write(codec, UDA1380_CLK, ~(R00_EN_ADC | R00_EN_DEC) & clk);
3963+}
3964+
3965+static unsigned int uda1380_config_sysclk(struct snd_soc_codec_dai *dai,
3966+ struct snd_soc_clock_info *info, unsigned int clk)
3967+{
3968+ if(info->fs != 256)
3969+ return 0;
3970+
3971+ /* we only support 256 FS atm */
3972+ if(info->rate * info->fs == clk) {
3973+ dai->mclk = clk;
3974+ return clk;
3975+ }
3976+
3977+ return 0;
3978+}
3979+
3980+static int uda1380_mute(struct snd_soc_codec *codec,
3981+ struct snd_soc_codec_dai *dai, int mute)
3982+{
3983+ u16 mute_reg = uda1380_read_reg_cache(codec, UDA1380_DEEMP) & 0xbfff;
3984+ if(mute)
3985+ uda1380_write(codec, UDA1380_DEEMP, mute_reg | 0x4000);
3986+ else
3987+ uda1380_write(codec, UDA1380_DEEMP, mute_reg);
3988+ return 0;
3989+}
3990+
3991+static int uda1380_dapm_event(struct snd_soc_codec *codec, int event)
3992+{
3993+ switch (event) {
3994+ case SNDRV_CTL_POWER_D0: /* full On */
3995+ case SNDRV_CTL_POWER_D1: /* partial On */
3996+ case SNDRV_CTL_POWER_D2: /* partial On */
3997+ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
3998+ /* everything off except internal bias */
3999+ uda1380_write(codec, UDA1380_PM, R02_PON_BIAS);
4000+ break;
4001+ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
4002+ /* everything off, inactive */
4003+ uda1380_write(codec, UDA1380_PM, 0x0);
4004+ break;
4005+ }
4006+ codec->dapm_state = event;
4007+ return 0;
4008+}
4009+
4010+struct snd_soc_codec_dai uda1380_dai = {
4011+ .name = "UDA1380",
4012+ .playback = {
4013+ .stream_name = "Playback",
4014+ .channels_min = 1,
4015+ .channels_max = 2,
4016+ },
4017+ .capture = {
4018+ .stream_name = "Capture",
4019+ .channels_min = 1,
4020+ .channels_max = 2,
4021+ },
4022+ .config_sysclk = uda1380_config_sysclk,
4023+ .digital_mute = uda1380_mute,
4024+ .ops = {
4025+ .prepare = uda1380_pcm_prepare,
4026+ .shutdown = uda1380_pcm_shutdown,
4027+ },
4028+ .caps = {
4029+ .num_modes = ARRAY_SIZE(uda1380_modes),
4030+ .mode = uda1380_modes,
4031+ },
4032+};
4033+EXPORT_SYMBOL_GPL(uda1380_dai);
4034+
4035+static int uda1380_suspend(struct platform_device *pdev, pm_message_t state)
4036+{
4037+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
4038+ struct snd_soc_codec *codec = socdev->codec;
4039+
4040+ uda1380_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
4041+ return 0;
4042+}
4043+
4044+static int uda1380_resume(struct platform_device *pdev)
4045+{
4046+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
4047+ struct snd_soc_codec *codec = socdev->codec;
4048+ int i;
4049+ u8 data[2];
4050+ u16 *cache = codec->reg_cache;
4051+
4052+ /* Sync reg_cache with the hardware */
4053+ for (i = 0; i < ARRAY_SIZE(uda1380_reg); i++) {
4054+ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
4055+ data[1] = cache[i] & 0x00ff;
4056+ codec->hw_write(codec->control_data, data, 2);
4057+ }
4058+ uda1380_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
4059+ uda1380_dapm_event(codec, codec->suspend_dapm_state);
4060+ return 0;
4061+}
4062+
4063+/*
4064+ * initialise the UDA1380 driver
4065+ * register the mixer and dsp interfaces with the kernel
4066+ */
4067+static int uda1380_init(struct snd_soc_device *socdev)
4068+{
4069+ struct snd_soc_codec *codec = socdev->codec;
4070+ int ret = 0;
4071+
4072+ codec->name = "UDA1380";
4073+ codec->owner = THIS_MODULE;
4074+ codec->read = uda1380_read_reg_cache;
4075+ codec->write = uda1380_write;
4076+ codec->dapm_event = uda1380_dapm_event;
4077+ codec->dai = &uda1380_dai;
4078+ codec->num_dai = 1;
4079+ codec->reg_cache_size = ARRAY_SIZE(uda1380_reg);
4080+ codec->reg_cache =
4081+ kzalloc(sizeof(u16) * ARRAY_SIZE(uda1380_reg), GFP_KERNEL);
4082+ if (codec->reg_cache == NULL)
4083+ return -ENOMEM;
4084+ memcpy(codec->reg_cache, uda1380_reg,
4085+ sizeof(u16) * ARRAY_SIZE(uda1380_reg));
4086+ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(uda1380_reg);
4087+ uda1380_reset(codec);
4088+
4089+ /* register pcms */
4090+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
4091+ if(ret < 0) {
4092+ kfree(codec->reg_cache);
4093+ return ret;
4094+ }
4095+
4096+ /* power on device */
4097+ uda1380_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
4098+ uda1380_write(codec, UDA1380_CLK, 0);
4099+
4100+ /* uda1380 init */
4101+ uda1380_add_controls(codec);
4102+ uda1380_add_widgets(codec);
4103+ ret = snd_soc_register_card(socdev);
4104+ if(ret < 0) {
4105+ snd_soc_free_pcms(socdev);
4106+ snd_soc_dapm_free(socdev);
4107+ }
4108+
4109+ return ret;
4110+}
4111+
4112+static struct snd_soc_device *uda1380_socdev;
4113+
4114+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
4115+
4116+#define I2C_DRIVERID_UDA1380 0xfefe /* liam - need a proper id */
4117+
4118+static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
4119+
4120+/* Magic definition of all other variables and things */
4121+I2C_CLIENT_INSMOD;
4122+
4123+static struct i2c_driver uda1380_i2c_driver;
4124+static struct i2c_client client_template;
4125+
4126+/* If the i2c layer weren't so broken, we could pass this kind of data
4127+ around */
4128+
4129+static int uda1380_codec_probe(struct i2c_adapter *adap, int addr, int kind)
4130+{
4131+ struct snd_soc_device *socdev = uda1380_socdev;
4132+ struct uda1380_setup_data *setup = socdev->codec_data;
4133+ struct snd_soc_codec *codec = socdev->codec;
4134+ struct i2c_client *i2c;
4135+ int ret;
4136+
4137+ if (addr != setup->i2c_address)
4138+ return -ENODEV;
4139+
4140+ client_template.adapter = adap;
4141+ client_template.addr = addr;
4142+
4143+ i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
4144+ if (i2c == NULL){
4145+ kfree(codec);
4146+ return -ENOMEM;
4147+ }
4148+ memcpy(i2c, &client_template, sizeof(struct i2c_client));
4149+ i2c_set_clientdata(i2c, codec);
4150+ codec->control_data = i2c;
4151+
4152+ ret = i2c_attach_client(i2c);
4153+ if(ret < 0) {
4154+ printk(KERN_ERR "failed to attach codec at addr %x\n", addr);
4155+ goto err;
4156+ }
4157+
4158+ ret = uda1380_init(socdev);
4159+ if(ret < 0) {
4160+ printk(KERN_ERR "failed to initialise UDA1380\n");
4161+ goto err;
4162+ }
4163+ return ret;
4164+
4165+err:
4166+ kfree(codec);
4167+ kfree(i2c);
4168+ return ret;
4169+}
4170+
4171+static int uda1380_i2c_detach(struct i2c_client *client)
4172+{
4173+ struct snd_soc_codec* codec = i2c_get_clientdata(client);
4174+ i2c_detach_client(client);
4175+ kfree(codec->reg_cache);
4176+ kfree(client);
4177+ return 0;
4178+}
4179+
4180+static int uda1380_i2c_attach(struct i2c_adapter *adap)
4181+{
4182+ return i2c_probe(adap, &addr_data, uda1380_codec_probe);
4183+}
4184+
4185+/* corgi i2c codec control layer */
4186+static struct i2c_driver uda1380_i2c_driver = {
4187+ .driver = {
4188+ .name = "UDA1380 I2C Codec",
4189+ .owner = THIS_MODULE,
4190+ },
4191+ .id = I2C_DRIVERID_UDA1380,
4192+ .attach_adapter = uda1380_i2c_attach,
4193+ .detach_client = uda1380_i2c_detach,
4194+ .command = NULL,
4195+};
4196+
4197+static struct i2c_client client_template = {
4198+ .name = "UDA1380",
4199+ .driver = &uda1380_i2c_driver,
4200+};
4201+#endif
4202+
4203+static int uda1380_probe(struct platform_device *pdev)
4204+{
4205+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
4206+ struct uda1380_setup_data *setup;
4207+ struct snd_soc_codec* codec;
4208+ int ret = 0;
4209+
4210+ printk(KERN_INFO "UDA1380 Audio Codec %s", UDA1380_VERSION);
4211+
4212+ setup = socdev->codec_data;
4213+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
4214+ if (codec == NULL)
4215+ return -ENOMEM;
4216+
4217+ socdev->codec = codec;
4218+ mutex_init(&codec->mutex);
4219+ INIT_LIST_HEAD(&codec->dapm_widgets);
4220+ INIT_LIST_HEAD(&codec->dapm_paths);
4221+
4222+ uda1380_socdev = socdev;
4223+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
4224+ if (setup->i2c_address) {
4225+ normal_i2c[0] = setup->i2c_address;
4226+ codec->hw_write = (hw_write_t)i2c_master_send;
4227+ ret = i2c_add_driver(&uda1380_i2c_driver);
4228+ if (ret != 0)
4229+ printk(KERN_ERR "can't add i2c driver");
4230+ }
4231+#else
4232+ /* Add other interfaces here */
4233+#endif
4234+ return ret;
4235+}
4236+
4237+/* power down chip */
4238+static int uda1380_remove(struct platform_device *pdev)
4239+{
4240+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
4241+ struct snd_soc_codec* codec = socdev->codec;
4242+
4243+ if (codec->control_data)
4244+ uda1380_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
4245+
4246+ snd_soc_free_pcms(socdev);
4247+ snd_soc_dapm_free(socdev);
4248+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
4249+ i2c_del_driver(&uda1380_i2c_driver);
4250+#endif
4251+ kfree(codec);
4252+
4253+ return 0;
4254+}
4255+
4256+struct snd_soc_codec_device soc_codec_dev_uda1380 = {
4257+ .probe = uda1380_probe,
4258+ .remove = uda1380_remove,
4259+ .suspend = uda1380_suspend,
4260+ .resume = uda1380_resume,
4261+};
4262+
4263+EXPORT_SYMBOL_GPL(soc_codec_dev_uda1380);
4264+
4265+MODULE_AUTHOR("Giorgio Padrin");
4266+MODULE_DESCRIPTION("Audio support for codec Philips UDA1380");
4267+MODULE_LICENSE("GPL");
4268Index: linux-2.6-pxa-new/sound/soc/codecs/uda1380.h
4269===================================================================
4270--- /dev/null
4271+++ linux-2.6-pxa-new/sound/soc/codecs/uda1380.h
4272@@ -0,0 +1,56 @@
4273+/*
4274+ * Audio support for Philips UDA1380
4275+ *
4276+ * This program is free software; you can redistribute it and/or modify
4277+ * it under the terms of the GNU General Public License version 2 as
4278+ * published by the Free Software Foundation.
4279+ *
4280+ * Copyright (c) 2005 Giorgio Padrin <giorgio@mandarinlogiq.org>
4281+ */
4282+
4283+#define UDA1380_CLK 0x00
4284+#define UDA1380_IFACE 0x01
4285+#define UDA1380_PM 0x02
4286+#define UDA1380_AMIX 0x03
4287+#define UDA1380_HP 0x04
4288+#define UDA1380_MVOL 0x10
4289+#define UDA1380_MIXVOL 0x11
4290+#define UDA1380_MODE 0x12
4291+#define UDA1380_DEEMP 0x13
4292+#define UDA1380_MIXER 0x14
4293+#define UDA1380_INTSTAT 0x18
4294+#define UDA1380_DEC 0x20
4295+#define UDA1380_PGA 0x21
4296+#define UDA1380_ADC 0x22
4297+#define UDA1380_AGC 0x23
4298+#define UDA1380_DECSTAT 0x28
4299+#define UDA1380_RESET 0x7f
4300+
4301+#define UDA1380_CACHEREGNUM 0x24
4302+
4303+/* Register flags */
4304+#define R00_EN_ADC 0x0800
4305+#define R00_EN_DEC 0x0400
4306+#define R00_EN_DAC 0x0200
4307+#define R00_EN_INT 0x0100
4308+#define R02_PON_HP 0x2000
4309+#define R02_PON_DAC 0x0400
4310+#define R02_PON_BIAS 0x0100
4311+#define R02_PON_LNA 0x0010
4312+#define R02_PON_PGAL 0x0008
4313+#define R02_PON_ADCL 0x0004
4314+#define R02_PON_PGAR 0x0002
4315+#define R02_PON_ADCR 0x0001
4316+#define R13_MTM 0x4000
4317+#define R21_MT_ADC 0x8000
4318+#define R22_SEL_LNA 0x0008
4319+#define R22_SEL_MIC 0x0004
4320+#define R22_SKIP_DCFIL 0x0002
4321+#define R23_AGC_EN 0x0001
4322+
4323+struct uda1380_setup_data {
4324+ unsigned short i2c_address;
4325+};
4326+
4327+extern struct snd_soc_codec_dai uda1380_dai;
4328+extern struct snd_soc_codec_device soc_codec_dev_uda1380;
4329Index: linux-2.6-pxa-new/sound/soc/codecs/wm8731.c
4330===================================================================
4331--- /dev/null
4332+++ linux-2.6-pxa-new/sound/soc/codecs/wm8731.c
4333@@ -0,0 +1,886 @@
4334+/*
4335+ * wm8731.c -- WM8731 ALSA SoC Audio driver
4336+ *
4337+ * Copyright 2005 Openedhand Ltd.
4338+ *
4339+ * Author: Richard Purdie <richard@openedhand.com>
4340+ *
4341+ * Based on wm8753.c by Liam Girdwood
4342+ *
4343+ * This program is free software; you can redistribute it and/or modify
4344+ * it under the terms of the GNU General Public License version 2 as
4345+ * published by the Free Software Foundation.
4346+ */
4347+
4348+#include <linux/module.h>
4349+#include <linux/moduleparam.h>
4350+#include <linux/init.h>
4351+#include <linux/delay.h>
4352+#include <linux/pm.h>
4353+#include <linux/i2c.h>
4354+#include <linux/platform_device.h>
4355+#include <sound/driver.h>
4356+#include <sound/core.h>
4357+#include <sound/pcm.h>
4358+#include <sound/pcm_params.h>
4359+#include <sound/soc.h>
4360+#include <sound/soc-dapm.h>
4361+#include <sound/initval.h>
4362+
4363+#include "wm8731.h"
4364+
4365+#define AUDIO_NAME "wm8731"
4366+#define WM8731_VERSION "0.12"
4367+
4368+/*
4369+ * Debug
4370+ */
4371+
4372+#define WM8731_DEBUG 0
4373+
4374+#ifdef WM8731_DEBUG
4375+#define dbg(format, arg...) \
4376+ printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
4377+#else
4378+#define dbg(format, arg...) do {} while (0)
4379+#endif
4380+#define err(format, arg...) \
4381+ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
4382+#define info(format, arg...) \
4383+ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
4384+#define warn(format, arg...) \
4385+ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
4386+
4387+struct snd_soc_codec_device soc_codec_dev_wm8731;
4388+
4389+/*
4390+ * wm8731 register cache
4391+ * We can't read the WM8731 register space when we are
4392+ * using 2 wire for device control, so we cache them instead.
4393+ * There is no point in caching the reset register
4394+ */
4395+static const u16 wm8731_reg[WM8731_CACHEREGNUM] = {
4396+ 0x0097, 0x0097, 0x0079, 0x0079,
4397+ 0x000a, 0x0008, 0x009f, 0x000a,
4398+ 0x0000, 0x0000
4399+};
4400+
4401+#define WM8731_DAIFMT \
4402+ (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_RIGHT_J | \
4403+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_NB_IF | SND_SOC_DAIFMT_IB_NF | \
4404+ SND_SOC_DAIFMT_IB_IF)
4405+
4406+#define WM8731_DIR \
4407+ (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
4408+
4409+#define WM8731_RATES \
4410+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
4411+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
4412+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
4413+
4414+#define WM8731_HIFI_BITS \
4415+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
4416+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
4417+
4418+static struct snd_soc_dai_mode wm8731_modes[] = {
4419+ /* codec frame and clock master modes */
4420+ /* 8k */
4421+ {
4422+ .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
4423+ .pcmfmt = WM8731_HIFI_BITS,
4424+ .pcmrate = SNDRV_PCM_RATE_8000,
4425+ .pcmdir = WM8731_DIR,
4426+ .flags = SND_SOC_DAI_BFS_RATE,
4427+ .fs = 1536,
4428+ .bfs = 64,
4429+ },
4430+ {
4431+ .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
4432+ .pcmfmt = WM8731_HIFI_BITS,
4433+ .pcmrate = SNDRV_PCM_RATE_8000,
4434+ .pcmdir = WM8731_DIR,
4435+ .flags = SND_SOC_DAI_BFS_RATE,
4436+ .fs = 2304,
4437+ .bfs = 64,
4438+ },
4439+ {
4440+ .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
4441+ .pcmfmt = WM8731_HIFI_BITS,
4442+ .pcmrate = SNDRV_PCM_RATE_8000,
4443+ .pcmdir = WM8731_DIR,
4444+ .flags = SND_SOC_DAI_BFS_RATE,
4445+ .fs = 1408,
4446+ .bfs = 64,
4447+ },
4448+ {
4449+ .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
4450+ .pcmfmt = WM8731_HIFI_BITS,
4451+ .pcmrate = SNDRV_PCM_RATE_8000,
4452+ .pcmdir = WM8731_DIR,
4453+ .flags = SND_SOC_DAI_BFS_RATE,
4454+ .fs = 2112,
4455+ .bfs = 64,
4456+ },
4457+
4458+ /* 32k */
4459+ {
4460+ .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
4461+ .pcmfmt = WM8731_HIFI_BITS,
4462+ .pcmrate = SNDRV_PCM_RATE_32000,
4463+ .pcmdir = WM8731_DIR,
4464+ .flags = SND_SOC_DAI_BFS_RATE,
4465+ .fs = 384,
4466+ .bfs = 64,
4467+ },
4468+ {
4469+ .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
4470+ .pcmfmt = WM8731_HIFI_BITS,
4471+ .pcmrate = SNDRV_PCM_RATE_32000,
4472+ .pcmdir = WM8731_DIR,
4473+ .flags = SND_SOC_DAI_BFS_RATE,
4474+ .fs = 576,
4475+ .bfs = 64,
4476+ },
4477+
4478+ /* 44.1k & 48k */
4479+ {
4480+ .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
4481+ .pcmfmt = WM8731_HIFI_BITS,
4482+ .pcmrate = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
4483+ .pcmdir = WM8731_DIR,
4484+ .flags = SND_SOC_DAI_BFS_RATE,
4485+ .fs = 256,
4486+ .bfs = 64,
4487+ },
4488+ {
4489+ .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
4490+ .pcmfmt = WM8731_HIFI_BITS,
4491+ .pcmrate = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
4492+ .pcmdir = WM8731_DIR,
4493+ .flags = SND_SOC_DAI_BFS_RATE,
4494+ .fs = 384,
4495+ .bfs = 64,
4496+ },
4497+
4498+ /* 88.2 & 96k */
4499+ {
4500+ .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
4501+ .pcmfmt = WM8731_HIFI_BITS,
4502+ .pcmrate = SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000,
4503+ .pcmdir = WM8731_DIR,
4504+ .flags = SND_SOC_DAI_BFS_RATE,
4505+ .fs = 128,
4506+ .bfs = 64,
4507+ },
4508+ {
4509+ .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
4510+ .pcmfmt = WM8731_HIFI_BITS,
4511+ .pcmrate = SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000,
4512+ .pcmdir = WM8731_DIR,
4513+ .flags = SND_SOC_DAI_BFS_RATE,
4514+ .fs = 192,
4515+ .bfs = 64,
4516+ },
4517+
4518+ /* USB codec frame and clock master modes */
4519+ /* 8k */
4520+ {
4521+ .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
4522+ .pcmfmt = WM8731_HIFI_BITS,
4523+ .pcmrate = SNDRV_PCM_RATE_8000,
4524+ .pcmdir = WM8731_DIR,
4525+ .flags = SND_SOC_DAI_BFS_DIV,
4526+ .fs = 1500,
4527+ .bfs = SND_SOC_FSBD(1),
4528+ },
4529+
4530+ /* 44.1k */
4531+ {
4532+ .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
4533+ .pcmfmt = WM8731_HIFI_BITS,
4534+ .pcmrate = SNDRV_PCM_RATE_44100,
4535+ .pcmdir = WM8731_DIR,
4536+ .flags = SND_SOC_DAI_BFS_DIV,
4537+ .fs = 272,
4538+ .bfs = SND_SOC_FSBD(1),
4539+ },
4540+
4541+ /* 48k */
4542+ {
4543+ .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
4544+ .pcmfmt = WM8731_HIFI_BITS,
4545+ .pcmrate = SNDRV_PCM_RATE_48000,
4546+ .pcmdir = WM8731_DIR,
4547+ .flags = SND_SOC_DAI_BFS_DIV,
4548+ .fs = 250,
4549+ .bfs = SND_SOC_FSBD(1),
4550+ },
4551+
4552+ /* 88.2k */
4553+ {
4554+ .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
4555+ .pcmfmt = WM8731_HIFI_BITS,
4556+ .pcmrate = SNDRV_PCM_RATE_88200,
4557+ .pcmdir = WM8731_DIR,
4558+ .flags = SND_SOC_DAI_BFS_DIV,
4559+ .fs = 136,
4560+ .bfs = SND_SOC_FSBD(1),
4561+ },
4562+
4563+ /* 96k */
4564+ {
4565+ .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
4566+ .pcmfmt = WM8731_HIFI_BITS,
4567+ .pcmrate = SNDRV_PCM_RATE_96000,
4568+ .pcmdir = WM8731_DIR,
4569+ .flags = SND_SOC_DAI_BFS_DIV,
4570+ .fs = 125,
4571+ .bfs = SND_SOC_FSBD(1),
4572+ },
4573+
4574+ /* codec frame and clock slave modes */
4575+ {
4576+ .fmt = WM8731_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
4577+ .pcmfmt = WM8731_HIFI_BITS,
4578+ .pcmrate = WM8731_RATES,
4579+ .pcmdir = WM8731_DIR,
4580+ .flags = SND_SOC_DAI_BFS_DIV,
4581+ .fs = SND_SOC_FS_ALL,
4582+ .bfs = SND_SOC_FSB_ALL,
4583+ },
4584+};
4585+
4586+/*
4587+ * read wm8731 register cache
4588+ */
4589+static inline unsigned int wm8731_read_reg_cache(struct snd_soc_codec *codec,
4590+ unsigned int reg)
4591+{
4592+ u16 *cache = codec->reg_cache;
4593+ if (reg == WM8731_RESET)
4594+ return 0;
4595+ if (reg >= WM8731_CACHEREGNUM)
4596+ return -1;
4597+ return cache[reg];
4598+}
4599+
4600+/*
4601+ * write wm8731 register cache
4602+ */
4603+static inline void wm8731_write_reg_cache(struct snd_soc_codec *codec,
4604+ u16 reg, unsigned int value)
4605+{
4606+ u16 *cache = codec->reg_cache;
4607+ if (reg >= WM8731_CACHEREGNUM)
4608+ return;
4609+ cache[reg] = value;
4610+}
4611+
4612+/*
4613+ * write to the WM8731 register space
4614+ */
4615+static int wm8731_write(struct snd_soc_codec *codec, unsigned int reg,
4616+ unsigned int value)
4617+{
4618+ u8 data[2];
4619+
4620+ /* data is
4621+ * D15..D9 WM8731 register offset
4622+ * D8...D0 register data
4623+ */
4624+ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
4625+ data[1] = value & 0x00ff;
4626+
4627+ wm8731_write_reg_cache (codec, reg, value);
4628+ if (codec->hw_write(codec->control_data, data, 2) == 2)
4629+ return 0;
4630+ else
4631+ return -EIO;
4632+}
4633+
4634+#define wm8731_reset(c) wm8731_write(c, WM8731_RESET, 0)
4635+
4636+static const char *wm8731_input_select[] = {"Line In", "Mic"};
4637+static const char *wm8731_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
4638+
4639+static const struct soc_enum wm8731_enum[] = {
4640+ SOC_ENUM_SINGLE(WM8731_APANA, 2, 2, wm8731_input_select),
4641+ SOC_ENUM_SINGLE(WM8731_APDIGI, 1, 4, wm8731_deemph),
4642+};
4643+
4644+static const struct snd_kcontrol_new wm8731_snd_controls[] = {
4645+
4646+SOC_DOUBLE_R("Master Playback Volume", WM8731_LOUT1V, WM8731_ROUT1V,
4647+ 0, 127, 0),
4648+SOC_DOUBLE_R("Master Playback ZC Switch", WM8731_LOUT1V, WM8731_ROUT1V,
4649+ 7, 1, 0),
4650+
4651+SOC_DOUBLE_R("Capture Volume", WM8731_LINVOL, WM8731_RINVOL, 0, 31, 0),
4652+SOC_DOUBLE_R("Line Capture Switch", WM8731_LINVOL, WM8731_RINVOL, 7, 1, 1),
4653+
4654+SOC_SINGLE("Mic Boost (+20dB)", WM8731_APANA, 0, 1, 0),
4655+SOC_SINGLE("Capture Mic Switch", WM8731_APANA, 1, 1, 1),
4656+
4657+SOC_SINGLE("Sidetone Playback Volume", WM8731_APANA, 6, 3, 1),
4658+
4659+SOC_SINGLE("ADC High Pass Filter Switch", WM8731_APDIGI, 0, 1, 1),
4660+SOC_SINGLE("Store DC Offset Switch", WM8731_APDIGI, 4, 1, 0),
4661+
4662+SOC_ENUM("Playback De-emphasis", wm8731_enum[1]),
4663+};
4664+
4665+/* add non dapm controls */
4666+static int wm8731_add_controls(struct snd_soc_codec *codec)
4667+{
4668+ int err, i;
4669+
4670+ for (i = 0; i < ARRAY_SIZE(wm8731_snd_controls); i++) {
4671+ if ((err = snd_ctl_add(codec->card,
4672+ snd_soc_cnew(&wm8731_snd_controls[i],codec, NULL))) < 0)
4673+ return err;
4674+ }
4675+
4676+ return 0;
4677+}
4678+
4679+/* Output Mixer */
4680+static const struct snd_kcontrol_new wm8731_output_mixer_controls[] = {
4681+SOC_DAPM_SINGLE("Line Bypass Switch", WM8731_APANA, 3, 1, 0),
4682+SOC_DAPM_SINGLE("Mic Sidetone Switch", WM8731_APANA, 5, 1, 0),
4683+SOC_DAPM_SINGLE("HiFi Playback Switch", WM8731_APANA, 4, 1, 0),
4684+};
4685+
4686+/* Input mux */
4687+static const struct snd_kcontrol_new wm8731_input_mux_controls =
4688+SOC_DAPM_ENUM("Input Select", wm8731_enum[0]);
4689+
4690+static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = {
4691+SND_SOC_DAPM_MIXER("Output Mixer", WM8731_PWR, 4, 1,
4692+ &wm8731_output_mixer_controls[0],
4693+ ARRAY_SIZE(wm8731_output_mixer_controls)),
4694+SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8731_PWR, 3, 1),
4695+SND_SOC_DAPM_OUTPUT("LOUT"),
4696+SND_SOC_DAPM_OUTPUT("LHPOUT"),
4697+SND_SOC_DAPM_OUTPUT("ROUT"),
4698+SND_SOC_DAPM_OUTPUT("RHPOUT"),
4699+SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8731_PWR, 2, 1),
4700+SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, &wm8731_input_mux_controls),
4701+SND_SOC_DAPM_PGA("Line Input", WM8731_PWR, 0, 1, NULL, 0),
4702+SND_SOC_DAPM_MICBIAS("Mic Bias", WM8731_PWR, 1, 1),
4703+SND_SOC_DAPM_INPUT("MICIN"),
4704+SND_SOC_DAPM_INPUT("RLINEIN"),
4705+SND_SOC_DAPM_INPUT("LLINEIN"),
4706+};
4707+
4708+static const char *intercon[][3] = {
4709+ /* output mixer */
4710+ {"Output Mixer", "Line Bypass Switch", "Line Input"},
4711+ {"Output Mixer", "HiFi Playback Switch", "DAC"},
4712+ {"Output Mixer", "Mic Sidetone Switch", "Mic Bias"},
4713+
4714+ /* outputs */
4715+ {"RHPOUT", NULL, "Output Mixer"},
4716+ {"ROUT", NULL, "Output Mixer"},
4717+ {"LHPOUT", NULL, "Output Mixer"},
4718+ {"LOUT", NULL, "Output Mixer"},
4719+
4720+ /* input mux */
4721+ {"Input Mux", "Line In", "Line Input"},
4722+ {"Input Mux", "Mic", "Mic Bias"},
4723+ {"ADC", NULL, "Input Mux"},
4724+
4725+ /* inputs */
4726+ {"Line Input", NULL, "LLINEIN"},
4727+ {"Line Input", NULL, "RLINEIN"},
4728+ {"Mic Bias", NULL, "MICIN"},
4729+
4730+ /* terminator */
4731+ {NULL, NULL, NULL},
4732+};
4733+
4734+static int wm8731_add_widgets(struct snd_soc_codec *codec)
4735+{
4736+ int i;
4737+
4738+ for(i = 0; i < ARRAY_SIZE(wm8731_dapm_widgets); i++) {
4739+ snd_soc_dapm_new_control(codec, &wm8731_dapm_widgets[i]);
4740+ }
4741+
4742+ /* set up audio path interconnects */
4743+ for(i = 0; intercon[i][0] != NULL; i++) {
4744+ snd_soc_dapm_connect_input(codec, intercon[i][0],
4745+ intercon[i][1], intercon[i][2]);
4746+ }
4747+
4748+ snd_soc_dapm_new_widgets(codec);
4749+ return 0;
4750+}
4751+
4752+struct _coeff_div {
4753+ u32 mclk;
4754+ u32 rate;
4755+ u16 fs;
4756+ u8 sr:4;
4757+ u8 bosr:1;
4758+ u8 usb:1;
4759+};
4760+
4761+/* codec mclk clock divider coefficients */
4762+static const struct _coeff_div coeff_div[] = {
4763+ /* 48k */
4764+ {12288000, 48000, 256, 0x0, 0x0, 0x0},
4765+ {18432000, 48000, 384, 0x0, 0x1, 0x0},
4766+ {12000000, 48000, 250, 0x0, 0x0, 0x1},
4767+
4768+ /* 32k */
4769+ {12288000, 32000, 384, 0x6, 0x0, 0x0},
4770+ {18432000, 32000, 576, 0x6, 0x1, 0x0},
4771+
4772+ /* 8k */
4773+ {12288000, 8000, 1536, 0x3, 0x0, 0x0},
4774+ {18432000, 8000, 2304, 0x3, 0x1, 0x0},
4775+ {11289600, 8000, 1408, 0xb, 0x0, 0x0},
4776+ {16934400, 8000, 2112, 0xb, 0x1, 0x0},
4777+ {12000000, 8000, 1500, 0x3, 0x0, 0x1},
4778+
4779+ /* 96k */
4780+ {12288000, 96000, 128, 0x7, 0x0, 0x0},
4781+ {18432000, 96000, 192, 0x7, 0x1, 0x0},
4782+ {12000000, 96000, 125, 0x7, 0x0, 0x1},
4783+
4784+ /* 44.1k */
4785+ {11289600, 44100, 256, 0x8, 0x0, 0x0},
4786+ {16934400, 44100, 384, 0x8, 0x1, 0x0},
4787+ {12000000, 44100, 272, 0x8, 0x1, 0x1},
4788+
4789+ /* 88.2k */
4790+ {11289600, 88200, 128, 0xf, 0x0, 0x0},
4791+ {16934400, 88200, 192, 0xf, 0x1, 0x0},
4792+ {12000000, 88200, 136, 0xf, 0x1, 0x1},
4793+};
4794+
4795+static inline int get_coeff(int mclk, int rate)
4796+{
4797+ int i;
4798+
4799+ for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
4800+ if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
4801+ return i;
4802+ }
4803+ return 0;
4804+}
4805+
4806+/* WM8731 supports numerous clocks per sample rate */
4807+static unsigned int wm8731_config_sysclk(struct snd_soc_codec_dai *dai,
4808+ struct snd_soc_clock_info *info, unsigned int clk)
4809+{
4810+ dai->mclk = 0;
4811+
4812+ /* check that the calculated FS and rate actually match a clock from
4813+ * the machine driver */
4814+ if (info->fs * info->rate == clk)
4815+ dai->mclk = clk;
4816+
4817+ return dai->mclk;
4818+}
4819+
4820+static int wm8731_pcm_prepare(struct snd_pcm_substream *substream)
4821+{
4822+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
4823+ struct snd_soc_device *socdev = rtd->socdev;
4824+ struct snd_soc_codec *codec = socdev->codec;
4825+ u16 iface = 0, srate;
4826+ int i = get_coeff(rtd->codec_dai->mclk,
4827+ snd_soc_get_rate(rtd->codec_dai->dai_runtime.pcmrate));
4828+
4829+ /* set master/slave audio interface */
4830+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
4831+ case SND_SOC_DAIFMT_CBM_CFM:
4832+ iface |= 0x0040;
4833+ break;
4834+ case SND_SOC_DAIFMT_CBS_CFS:
4835+ break;
4836+ }
4837+ srate = (coeff_div[i].sr << 2) |
4838+ (coeff_div[i].bosr << 1) | coeff_div[i].usb;
4839+ wm8731_write(codec, WM8731_SRATE, srate);
4840+
4841+ /* interface format */
4842+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
4843+ case SND_SOC_DAIFMT_I2S:
4844+ iface |= 0x0002;
4845+ break;
4846+ case SND_SOC_DAIFMT_RIGHT_J:
4847+ break;
4848+ case SND_SOC_DAIFMT_LEFT_J:
4849+ iface |= 0x0001;
4850+ break;
4851+ case SND_SOC_DAIFMT_DSP_A:
4852+ iface |= 0x0003;
4853+ break;
4854+ case SND_SOC_DAIFMT_DSP_B:
4855+ iface |= 0x0013;
4856+ break;
4857+ }
4858+
4859+ /* bit size */
4860+ switch (rtd->codec_dai->dai_runtime.pcmfmt) {
4861+ case SNDRV_PCM_FMTBIT_S16_LE:
4862+ break;
4863+ case SNDRV_PCM_FMTBIT_S20_3LE:
4864+ iface |= 0x0004;
4865+ break;
4866+ case SNDRV_PCM_FMTBIT_S24_LE:
4867+ iface |= 0x0008;
4868+ break;
4869+ case SNDRV_PCM_FMTBIT_S32_LE:
4870+ iface |= 0x000c;
4871+ break;
4872+ }
4873+
4874+ /* clock inversion */
4875+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_INV_MASK) {
4876+ case SND_SOC_DAIFMT_NB_NF:
4877+ break;
4878+ case SND_SOC_DAIFMT_IB_IF:
4879+ iface |= 0x0090;
4880+ break;
4881+ case SND_SOC_DAIFMT_IB_NF:
4882+ iface |= 0x0080;
4883+ break;
4884+ case SND_SOC_DAIFMT_NB_IF:
4885+ iface |= 0x0010;
4886+ break;
4887+ }
4888+
4889+ /* set iface */
4890+ wm8731_write(codec, WM8731_IFACE, iface);
4891+
4892+ /* set active */
4893+ wm8731_write(codec, WM8731_ACTIVE, 0x0001);
4894+ return 0;
4895+}
4896+
4897+static void wm8731_shutdown(struct snd_pcm_substream *substream)
4898+{
4899+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
4900+ struct snd_soc_device *socdev = rtd->socdev;
4901+ struct snd_soc_codec *codec = socdev->codec;
4902+
4903+ /* deactivate */
4904+ if (!codec->active) {
4905+ udelay(50);
4906+ wm8731_write(codec, WM8731_ACTIVE, 0x0);
4907+ }
4908+}
4909+
4910+static int wm8731_mute(struct snd_soc_codec *codec,
4911+ struct snd_soc_codec_dai *dai, int mute)
4912+{
4913+ u16 mute_reg = wm8731_read_reg_cache(codec, WM8731_APDIGI) & 0xfff7;
4914+ if (mute)
4915+ wm8731_write(codec, WM8731_APDIGI, mute_reg | 0x8);
4916+ else
4917+ wm8731_write(codec, WM8731_APDIGI, mute_reg);
4918+ return 0;
4919+}
4920+
4921+static int wm8731_dapm_event(struct snd_soc_codec *codec, int event)
4922+{
4923+ u16 reg = wm8731_read_reg_cache(codec, WM8731_PWR) & 0xff7f;
4924+
4925+ switch (event) {
4926+ case SNDRV_CTL_POWER_D0: /* full On */
4927+ /* vref/mid, osc on, dac unmute */
4928+ wm8731_write(codec, WM8731_PWR, reg);
4929+ break;
4930+ case SNDRV_CTL_POWER_D1: /* partial On */
4931+ case SNDRV_CTL_POWER_D2: /* partial On */
4932+ break;
4933+ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
4934+ /* everything off except vref/vmid, */
4935+ wm8731_write(codec, WM8731_PWR, reg | 0x0040);
4936+ break;
4937+ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
4938+ /* everything off, dac mute, inactive */
4939+ wm8731_write(codec, WM8731_ACTIVE, 0x0);
4940+ wm8731_write(codec, WM8731_PWR, 0xffff);
4941+ break;
4942+ }
4943+ codec->dapm_state = event;
4944+ return 0;
4945+}
4946+
4947+struct snd_soc_codec_dai wm8731_dai = {
4948+ .name = "WM8731",
4949+ .playback = {
4950+ .stream_name = "Playback",
4951+ .channels_min = 1,
4952+ .channels_max = 2,
4953+ },
4954+ .capture = {
4955+ .stream_name = "Capture",
4956+ .channels_min = 1,
4957+ .channels_max = 2,
4958+ },
4959+ .config_sysclk = wm8731_config_sysclk,
4960+ .digital_mute = wm8731_mute,
4961+ .ops = {
4962+ .prepare = wm8731_pcm_prepare,
4963+ .shutdown = wm8731_shutdown,
4964+ },
4965+ .caps = {
4966+ .num_modes = ARRAY_SIZE(wm8731_modes),
4967+ .mode = wm8731_modes,
4968+ },
4969+};
4970+EXPORT_SYMBOL_GPL(wm8731_dai);
4971+
4972+static int wm8731_suspend(struct platform_device *pdev, pm_message_t state)
4973+{
4974+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
4975+ struct snd_soc_codec *codec = socdev->codec;
4976+
4977+ wm8731_write(codec, WM8731_ACTIVE, 0x0);
4978+ wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
4979+ return 0;
4980+}
4981+
4982+static int wm8731_resume(struct platform_device *pdev)
4983+{
4984+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
4985+ struct snd_soc_codec *codec = socdev->codec;
4986+ int i;
4987+ u8 data[2];
4988+ u16 *cache = codec->reg_cache;
4989+
4990+ /* Sync reg_cache with the hardware */
4991+ for (i = 0; i < ARRAY_SIZE(wm8731_reg); i++) {
4992+ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
4993+ data[1] = cache[i] & 0x00ff;
4994+ codec->hw_write(codec->control_data, data, 2);
4995+ }
4996+ wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
4997+ wm8731_dapm_event(codec, codec->suspend_dapm_state);
4998+ return 0;
4999+}
5000+
5001+/*
5002+ * initialise the WM8731 driver
5003+ * register the mixer and dsp interfaces with the kernel
5004+ */
5005+static int wm8731_init(struct snd_soc_device *socdev)
5006+{
5007+ struct snd_soc_codec *codec = socdev->codec;
5008+ int reg, ret = 0;
5009+
5010+ codec->name = "WM8731";
5011+ codec->owner = THIS_MODULE;
5012+ codec->read = wm8731_read_reg_cache;
5013+ codec->write = wm8731_write;
5014+ codec->dapm_event = wm8731_dapm_event;
5015+ codec->dai = &wm8731_dai;
5016+ codec->num_dai = 1;
5017+ codec->reg_cache_size = ARRAY_SIZE(wm8731_reg);
5018+
5019+ codec->reg_cache =
5020+ kzalloc(sizeof(u16) * ARRAY_SIZE(wm8731_reg), GFP_KERNEL);
5021+ if (codec->reg_cache == NULL)
5022+ return -ENOMEM;
5023+ memcpy(codec->reg_cache,
5024+ wm8731_reg, sizeof(u16) * ARRAY_SIZE(wm8731_reg));
5025+ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm8731_reg);
5026+
5027+ wm8731_reset(codec);
5028+
5029+ /* register pcms */
5030+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
5031+ if (ret < 0) {
5032+ kfree(codec->reg_cache);
5033+ return ret;
5034+ }
5035+
5036+ /* power on device */
5037+ wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
5038+
5039+ /* set the update bits */
5040+ reg = wm8731_read_reg_cache(codec, WM8731_LOUT1V);
5041+ wm8731_write(codec, WM8731_LOUT1V, reg | 0x0100);
5042+ reg = wm8731_read_reg_cache(codec, WM8731_ROUT1V);
5043+ wm8731_write(codec, WM8731_ROUT1V, reg | 0x0100);
5044+ reg = wm8731_read_reg_cache(codec, WM8731_LINVOL);
5045+ wm8731_write(codec, WM8731_LINVOL, reg | 0x0100);
5046+ reg = wm8731_read_reg_cache(codec, WM8731_RINVOL);
5047+ wm8731_write(codec, WM8731_RINVOL, reg | 0x0100);
5048+
5049+ wm8731_add_controls(codec);
5050+ wm8731_add_widgets(codec);
5051+ ret = snd_soc_register_card(socdev);
5052+ if (ret < 0) {
5053+ snd_soc_free_pcms(socdev);
5054+ snd_soc_dapm_free(socdev);
5055+ }
5056+
5057+ return ret;
5058+}
5059+
5060+static struct snd_soc_device *wm8731_socdev;
5061+
5062+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
5063+
5064+/*
5065+ * WM8731 2 wire address is determined by GPIO5
5066+ * state during powerup.
5067+ * low = 0x1a
5068+ * high = 0x1b
5069+ */
5070+static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
5071+
5072+/* Magic definition of all other variables and things */
5073+I2C_CLIENT_INSMOD;
5074+
5075+static struct i2c_driver wm8731_i2c_driver;
5076+static struct i2c_client client_template;
5077+
5078+/* If the i2c layer weren't so broken, we could pass this kind of data
5079+ around */
5080+
5081+static int wm8731_codec_probe(struct i2c_adapter *adap, int addr, int kind)
5082+{
5083+ struct snd_soc_device *socdev = wm8731_socdev;
5084+ struct wm8731_setup_data *setup = socdev->codec_data;
5085+ struct snd_soc_codec *codec = socdev->codec;
5086+ struct i2c_client *i2c;
5087+ int ret;
5088+
5089+ if (addr != setup->i2c_address)
5090+ return -ENODEV;
5091+
5092+ client_template.adapter = adap;
5093+ client_template.addr = addr;
5094+
5095+ i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
5096+ if (i2c == NULL) {
5097+ kfree(codec);
5098+ return -ENOMEM;
5099+ }
5100+ memcpy(i2c, &client_template, sizeof(struct i2c_client));
5101+ i2c_set_clientdata(i2c, codec);
5102+ codec->control_data = i2c;
5103+
5104+ ret = i2c_attach_client(i2c);
5105+ if (ret < 0) {
5106+ err("failed to attach codec at addr %x\n", addr);
5107+ goto err;
5108+ }
5109+
5110+ ret = wm8731_init(socdev);
5111+ if (ret < 0) {
5112+ err("failed to initialise WM8731\n");
5113+ goto err;
5114+ }
5115+ return ret;
5116+
5117+err:
5118+ kfree(codec);
5119+ kfree(i2c);
5120+ return ret;
5121+}
5122+
5123+static int wm8731_i2c_detach(struct i2c_client *client)
5124+{
5125+ struct snd_soc_codec* codec = i2c_get_clientdata(client);
5126+ i2c_detach_client(client);
5127+ kfree(codec->reg_cache);
5128+ kfree(client);
5129+ return 0;
5130+}
5131+
5132+static int wm8731_i2c_attach(struct i2c_adapter *adap)
5133+{
5134+ return i2c_probe(adap, &addr_data, wm8731_codec_probe);
5135+}
5136+
5137+/* corgi i2c codec control layer */
5138+static struct i2c_driver wm8731_i2c_driver = {
5139+ .driver = {
5140+ .name = "WM8731 I2C Codec",
5141+ .owner = THIS_MODULE,
5142+ },
5143+ .id = I2C_DRIVERID_WM8731,
5144+ .attach_adapter = wm8731_i2c_attach,
5145+ .detach_client = wm8731_i2c_detach,
5146+ .command = NULL,
5147+};
5148+
5149+static struct i2c_client client_template = {
5150+ .name = "WM8731",
5151+ .driver = &wm8731_i2c_driver,
5152+};
5153+#endif
5154+
5155+static int wm8731_probe(struct platform_device *pdev)
5156+{
5157+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
5158+ struct wm8731_setup_data *setup;
5159+ struct snd_soc_codec *codec;
5160+ int ret = 0;
5161+
5162+ info("WM8731 Audio Codec %s", WM8731_VERSION);
5163+
5164+ setup = socdev->codec_data;
5165+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
5166+ if (codec == NULL)
5167+ return -ENOMEM;
5168+
5169+ socdev->codec = codec;
5170+ mutex_init(&codec->mutex);
5171+ INIT_LIST_HEAD(&codec->dapm_widgets);
5172+ INIT_LIST_HEAD(&codec->dapm_paths);
5173+
5174+ wm8731_socdev = socdev;
5175+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
5176+ if (setup->i2c_address) {
5177+ normal_i2c[0] = setup->i2c_address;
5178+ codec->hw_write = (hw_write_t)i2c_master_send;
5179+ ret = i2c_add_driver(&wm8731_i2c_driver);
5180+ if (ret != 0)
5181+ printk(KERN_ERR "can't add i2c driver");
5182+ }
5183+#else
5184+ /* Add other interfaces here */
5185+#endif
5186+ return ret;
5187+}
5188+
5189+/* power down chip */
5190+static int wm8731_remove(struct platform_device *pdev)
5191+{
5192+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
5193+ struct snd_soc_codec *codec = socdev->codec;
5194+
5195+ if (codec->control_data)
5196+ wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
5197+
5198+ snd_soc_free_pcms(socdev);
5199+ snd_soc_dapm_free(socdev);
5200+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
5201+ i2c_del_driver(&wm8731_i2c_driver);
5202+#endif
5203+ kfree(codec);
5204+
5205+ return 0;
5206+}
5207+
5208+struct snd_soc_codec_device soc_codec_dev_wm8731 = {
5209+ .probe = wm8731_probe,
5210+ .remove = wm8731_remove,
5211+ .suspend = wm8731_suspend,
5212+ .resume = wm8731_resume,
5213+};
5214+
5215+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8731);
5216+
5217+MODULE_DESCRIPTION("ASoC WM8731 driver");
5218+MODULE_AUTHOR("Richard Purdie");
5219+MODULE_LICENSE("GPL");
5220Index: linux-2.6-pxa-new/sound/soc/codecs/wm8731.h
5221===================================================================
5222--- /dev/null
5223+++ linux-2.6-pxa-new/sound/soc/codecs/wm8731.h
5224@@ -0,0 +1,41 @@
5225+/*
5226+ * wm8731.h -- WM8731 Soc Audio driver
5227+ *
5228+ * Copyright 2005 Openedhand Ltd.
5229+ *
5230+ * Author: Richard Purdie <richard@openedhand.com>
5231+ *
5232+ * Based on wm8753.h
5233+ *
5234+ * This program is free software; you can redistribute it and/or modify
5235+ * it under the terms of the GNU General Public License version 2 as
5236+ * published by the Free Software Foundation.
5237+ */
5238+
5239+#ifndef _WM8731_H
5240+#define _WM8731_H
5241+
5242+/* WM8731 register space */
5243+
5244+#define WM8731_LINVOL 0x00
5245+#define WM8731_RINVOL 0x01
5246+#define WM8731_LOUT1V 0x02
5247+#define WM8731_ROUT1V 0x03
5248+#define WM8731_APANA 0x04
5249+#define WM8731_APDIGI 0x05
5250+#define WM8731_PWR 0x06
5251+#define WM8731_IFACE 0x07
5252+#define WM8731_SRATE 0x08
5253+#define WM8731_ACTIVE 0x09
5254+#define WM8731_RESET 0x0f
5255+
5256+#define WM8731_CACHEREGNUM 10
5257+
5258+struct wm8731_setup_data {
5259+ unsigned short i2c_address;
5260+};
5261+
5262+extern struct snd_soc_codec_dai wm8731_dai;
5263+extern struct snd_soc_codec_device soc_codec_dev_wm8731;
5264+
5265+#endif
5266Index: linux-2.6-pxa-new/sound/soc/codecs/wm8750.c
5267===================================================================
5268--- /dev/null
5269+++ linux-2.6-pxa-new/sound/soc/codecs/wm8750.c
5270@@ -0,0 +1,1282 @@
5271+/*
5272+ * wm8750.c -- WM8750 ALSA SoC audio driver
5273+ *
5274+ * Copyright 2005 Openedhand Ltd.
5275+ *
5276+ * Author: Richard Purdie <richard@openedhand.com>
5277+ *
5278+ * Based on WM8753.c
5279+ *
5280+ * This program is free software; you can redistribute it and/or modify
5281+ * it under the terms of the GNU General Public License version 2 as
5282+ * published by the Free Software Foundation.
5283+ */
5284+
5285+#include <linux/module.h>
5286+#include <linux/moduleparam.h>
5287+#include <linux/init.h>
5288+#include <linux/delay.h>
5289+#include <linux/pm.h>
5290+#include <linux/i2c.h>
5291+#include <linux/platform_device.h>
5292+#include <sound/driver.h>
5293+#include <sound/core.h>
5294+#include <sound/pcm.h>
5295+#include <sound/pcm_params.h>
5296+#include <sound/soc.h>
5297+#include <sound/soc-dapm.h>
5298+#include <sound/initval.h>
5299+
5300+#include "wm8750.h"
5301+
5302+#define AUDIO_NAME "WM8750"
5303+#define WM8750_VERSION "0.11"
5304+
5305+/*
5306+ * Debug
5307+ */
5308+
5309+#define WM8750_DEBUG 0
5310+
5311+#ifdef WM8750_DEBUG
5312+#define dbg(format, arg...) \
5313+ printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
5314+#else
5315+#define dbg(format, arg...) do {} while (0)
5316+#endif
5317+#define err(format, arg...) \
5318+ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
5319+#define info(format, arg...) \
5320+ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
5321+#define warn(format, arg...) \
5322+ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
5323+
5324+static struct workqueue_struct *wm8750_workq = NULL;
5325+static struct work_struct wm8750_dapm_work;
5326+
5327+/*
5328+ * wm8750 register cache
5329+ * We can't read the WM8750 register space when we
5330+ * are using 2 wire for device control, so we cache them instead.
5331+ */
5332+static const u16 wm8750_reg[] = {
5333+ 0x0097, 0x0097, 0x0079, 0x0079, /* 0 */
5334+ 0x0000, 0x0008, 0x0000, 0x000a, /* 4 */
5335+ 0x0000, 0x0000, 0x00ff, 0x00ff, /* 8 */
5336+ 0x000f, 0x000f, 0x0000, 0x0000, /* 12 */
5337+ 0x0000, 0x007b, 0x0000, 0x0032, /* 16 */
5338+ 0x0000, 0x00c3, 0x00c3, 0x00c0, /* 20 */
5339+ 0x0000, 0x0000, 0x0000, 0x0000, /* 24 */
5340+ 0x0000, 0x0000, 0x0000, 0x0000, /* 28 */
5341+ 0x0000, 0x0000, 0x0050, 0x0050, /* 32 */
5342+ 0x0050, 0x0050, 0x0050, 0x0050, /* 36 */
5343+ 0x0079, 0x0079, 0x0079, /* 40 */
5344+};
5345+
5346+#define WM8750_HIFI_DAIFMT \
5347+ (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_RIGHT_J | \
5348+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_NB_IF | SND_SOC_DAIFMT_IB_NF | \
5349+ SND_SOC_DAIFMT_IB_IF)
5350+
5351+#define WM8750_DIR \
5352+ (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
5353+
5354+#define WM8750_HIFI_FSB \
5355+ (SND_SOC_FSBD(1) | SND_SOC_FSBD(2) | SND_SOC_FSBD(4) | \
5356+ SND_SOC_FSBD(8) | SND_SOC_FSBD(16))
5357+
5358+#define WM8750_HIFI_RATES \
5359+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
5360+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
5361+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
5362+
5363+#define WM8750_HIFI_BITS \
5364+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
5365+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
5366+
5367+static struct snd_soc_dai_mode wm8750_modes[] = {
5368+ /* common codec frame and clock master modes */
5369+ /* 8k */
5370+ {
5371+ .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
5372+ .pcmfmt = WM8750_HIFI_BITS,
5373+ .pcmrate = SNDRV_PCM_RATE_8000,
5374+ .pcmdir = WM8750_DIR,
5375+ .flags = SND_SOC_DAI_BFS_DIV,
5376+ .fs = 1536,
5377+ .bfs = WM8750_HIFI_FSB,
5378+ },
5379+ {
5380+ .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
5381+ .pcmfmt = WM8750_HIFI_BITS,
5382+ .pcmrate = SNDRV_PCM_RATE_8000,
5383+ .pcmdir = WM8750_DIR,
5384+ .flags = SND_SOC_DAI_BFS_DIV,
5385+ .fs = 1408,
5386+ .bfs = WM8750_HIFI_FSB,
5387+ },
5388+ {
5389+ .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
5390+ .pcmfmt = WM8750_HIFI_BITS,
5391+ .pcmrate = SNDRV_PCM_RATE_8000,
5392+ .pcmdir = WM8750_DIR,
5393+ .flags = SND_SOC_DAI_BFS_DIV,
5394+ .fs = 2304,
5395+ .bfs = WM8750_HIFI_FSB,
5396+ },
5397+ {
5398+ .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
5399+ .pcmfmt = WM8750_HIFI_BITS,
5400+ .pcmrate = SNDRV_PCM_RATE_8000,
5401+ .pcmdir = WM8750_DIR,
5402+ .flags = SND_SOC_DAI_BFS_DIV,
5403+ .fs = 2112,
5404+ .bfs = WM8750_HIFI_FSB,
5405+ },
5406+ {
5407+ .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
5408+ .pcmfmt = WM8750_HIFI_BITS,
5409+ .pcmrate = SNDRV_PCM_RATE_8000,
5410+ .pcmdir = WM8750_DIR,
5411+ .flags = SND_SOC_DAI_BFS_DIV,
5412+ .fs = 1500,
5413+ .bfs = WM8750_HIFI_FSB,
5414+ },
5415+
5416+ /* 11.025k */
5417+ {
5418+ .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
5419+ .pcmfmt = WM8750_HIFI_BITS,
5420+ .pcmrate = SNDRV_PCM_RATE_11025,
5421+ .pcmdir = WM8750_DIR,
5422+ .flags = SND_SOC_DAI_BFS_DIV,
5423+ .fs = 1024,
5424+ .bfs = WM8750_HIFI_FSB,
5425+ },
5426+ {
5427+ .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
5428+ .pcmfmt = WM8750_HIFI_BITS,
5429+ .pcmrate = SNDRV_PCM_RATE_11025,
5430+ .pcmdir = WM8750_DIR,
5431+ .flags = SND_SOC_DAI_BFS_DIV,
5432+ .fs = 1536,
5433+ .bfs = WM8750_HIFI_FSB,
5434+ },
5435+ {
5436+ .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
5437+ .pcmfmt = WM8750_HIFI_BITS,
5438+ .pcmrate = SNDRV_PCM_RATE_11025,
5439+ .pcmdir = WM8750_DIR,
5440+ .flags = SND_SOC_DAI_BFS_DIV,
5441+ .fs = 1088,
5442+ .bfs = WM8750_HIFI_FSB,
5443+ },
5444+
5445+ /* 16k */
5446+ {
5447+ .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
5448+ .pcmfmt = WM8750_HIFI_BITS,
5449+ .pcmrate = SNDRV_PCM_RATE_16000,
5450+ .pcmdir = WM8750_DIR,
5451+ .flags = SND_SOC_DAI_BFS_DIV,
5452+ .fs = 768,
5453+ .bfs = WM8750_HIFI_FSB,
5454+ },
5455+ {
5456+ .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
5457+ .pcmfmt = WM8750_HIFI_BITS,
5458+ .pcmrate = SNDRV_PCM_RATE_16000,
5459+ .pcmdir = WM8750_DIR,
5460+ .flags = SND_SOC_DAI_BFS_DIV,
5461+ .fs = 1152,
5462+ .bfs = WM8750_HIFI_FSB
5463+ },
5464+ {
5465+ .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
5466+ .pcmfmt = WM8750_HIFI_BITS,
5467+ .pcmrate = SNDRV_PCM_RATE_16000,
5468+ .pcmdir = WM8750_DIR,
5469+ .flags = SND_SOC_DAI_BFS_DIV,
5470+ .fs = 750,
5471+ .bfs = WM8750_HIFI_FSB,
5472+ },
5473+
5474+ /* 22.05k */
5475+ {
5476+ .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
5477+ .pcmfmt = WM8750_HIFI_BITS,
5478+ .pcmrate = SNDRV_PCM_RATE_22050,
5479+ .pcmdir = WM8750_DIR,
5480+ .flags = SND_SOC_DAI_BFS_DIV,
5481+ .fs = 512,
5482+ .bfs = WM8750_HIFI_FSB,
5483+ },
5484+ {
5485+ .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
5486+ .pcmfmt = WM8750_HIFI_BITS,
5487+ .pcmrate = SNDRV_PCM_RATE_22050,
5488+ .pcmdir = WM8750_DIR,
5489+ .flags = SND_SOC_DAI_BFS_DIV,
5490+ .fs = 768,
5491+ .bfs = WM8750_HIFI_FSB,
5492+ },
5493+ {
5494+ .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
5495+ .pcmfmt = WM8750_HIFI_BITS,
5496+ .pcmrate = SNDRV_PCM_RATE_22050,
5497+ .pcmdir = WM8750_DIR,
5498+ .flags = SND_SOC_DAI_BFS_DIV,
5499+ .fs = 544,
5500+ .bfs = WM8750_HIFI_FSB,
5501+ },
5502+
5503+ /* 32k */
5504+ {
5505+ .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
5506+ .pcmfmt = WM8750_HIFI_BITS,
5507+ .pcmrate = SNDRV_PCM_RATE_32000,
5508+ .pcmdir = WM8750_DIR,
5509+ .flags = SND_SOC_DAI_BFS_DIV,
5510+ .fs = 384,
5511+ .bfs = WM8750_HIFI_FSB,
5512+ },
5513+ {
5514+ .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
5515+ .pcmfmt = WM8750_HIFI_BITS,
5516+ .pcmrate = SNDRV_PCM_RATE_32000,
5517+ .pcmdir = WM8750_DIR,
5518+ .flags = SND_SOC_DAI_BFS_DIV,
5519+ .fs = 576,
5520+ .bfs = WM8750_HIFI_FSB,
5521+ },
5522+ {
5523+ .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
5524+ .pcmfmt = WM8750_HIFI_BITS,
5525+ .pcmrate = SNDRV_PCM_RATE_32000,
5526+ .pcmdir = WM8750_DIR,
5527+ .flags = SND_SOC_DAI_BFS_DIV,
5528+ .fs = 375,
5529+ .bfs = WM8750_HIFI_FSB,
5530+ },
5531+
5532+ /* 44.1k & 48k */
5533+ {
5534+ .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
5535+ .pcmfmt = WM8750_HIFI_BITS,
5536+ .pcmrate = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
5537+ .pcmdir = WM8750_DIR,
5538+ .flags = SND_SOC_DAI_BFS_DIV,
5539+ .fs = 256,
5540+ .bfs = WM8750_HIFI_FSB,
5541+ },
5542+ {
5543+ .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
5544+ .pcmfmt = WM8750_HIFI_BITS,
5545+ .pcmrate = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
5546+ .pcmdir = WM8750_DIR,
5547+ .flags = SND_SOC_DAI_BFS_DIV,
5548+ .fs = 384,
5549+ .bfs = WM8750_HIFI_FSB,
5550+ },
5551+ {
5552+ .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
5553+ .pcmfmt = WM8750_HIFI_BITS,
5554+ .pcmrate = SNDRV_PCM_RATE_44100,
5555+ .pcmdir = WM8750_DIR,
5556+ .flags = SND_SOC_DAI_BFS_DIV,
5557+ .fs = 272,
5558+ .bfs = WM8750_HIFI_FSB,
5559+ },
5560+ {
5561+ .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
5562+ .pcmfmt = WM8750_HIFI_BITS,
5563+ .pcmrate = SNDRV_PCM_RATE_48000,
5564+ .pcmdir = WM8750_DIR,
5565+ .flags = SND_SOC_DAI_BFS_DIV,
5566+ .fs = 250,
5567+ .bfs = WM8750_HIFI_FSB,
5568+ },
5569+
5570+ /* 88.2k & 96k */
5571+ {
5572+ .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
5573+ .pcmfmt = WM8750_HIFI_BITS,
5574+ .pcmrate = SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000,
5575+ .pcmdir = WM8750_DIR,
5576+ .flags = SND_SOC_DAI_BFS_DIV,
5577+ .fs = 128,
5578+ .bfs = WM8750_HIFI_FSB,
5579+ },
5580+ {
5581+ .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
5582+ .pcmfmt = WM8750_HIFI_BITS,
5583+ .pcmrate = SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000,
5584+ .pcmdir = WM8750_DIR,
5585+ .flags = SND_SOC_DAI_BFS_DIV,
5586+ .fs = 192,
5587+ .bfs = WM8750_HIFI_FSB,
5588+ },
5589+ {
5590+ .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
5591+ .pcmfmt = WM8750_HIFI_BITS,
5592+ .pcmrate = SNDRV_PCM_RATE_88200,
5593+ .pcmdir = WM8750_DIR,
5594+ .flags = SND_SOC_DAI_BFS_DIV,
5595+ .fs = 136,
5596+ .bfs = WM8750_HIFI_FSB,
5597+ },
5598+ {
5599+ .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
5600+ .pcmfmt = WM8750_HIFI_BITS,
5601+ .pcmrate = SNDRV_PCM_RATE_96000,
5602+ .pcmdir = WM8750_DIR,
5603+ .flags = SND_SOC_DAI_BFS_DIV,
5604+ .fs = 125,
5605+ .bfs = WM8750_HIFI_FSB,
5606+ },
5607+
5608+ /* codec frame and clock slave modes */
5609+ {
5610+ .fmt = WM8750_HIFI_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
5611+ .pcmfmt = WM8750_HIFI_BITS,
5612+ .pcmrate = WM8750_HIFI_RATES,
5613+ .pcmdir = WM8750_DIR,
5614+ .flags = SND_SOC_DAI_BFS_DIV,
5615+ .fs = SND_SOC_FS_ALL,
5616+ .bfs = SND_SOC_FSB_ALL,
5617+ },
5618+};
5619+
5620+/*
5621+ * read wm8750 register cache
5622+ */
5623+static inline unsigned int wm8750_read_reg_cache(struct snd_soc_codec *codec,
5624+ unsigned int reg)
5625+{
5626+ u16 *cache = codec->reg_cache;
5627+ if (reg > WM8750_CACHE_REGNUM)
5628+ return -1;
5629+ return cache[reg];
5630+}
5631+
5632+/*
5633+ * write wm8750 register cache
5634+ */
5635+static inline void wm8750_write_reg_cache(struct snd_soc_codec *codec,
5636+ unsigned int reg, unsigned int value)
5637+{
5638+ u16 *cache = codec->reg_cache;
5639+ if (reg > WM8750_CACHE_REGNUM)
5640+ return;
5641+ cache[reg] = value;
5642+}
5643+
5644+static int wm8750_write(struct snd_soc_codec *codec, unsigned int reg,
5645+ unsigned int value)
5646+{
5647+ u8 data[2];
5648+
5649+ /* data is
5650+ * D15..D9 WM8753 register offset
5651+ * D8...D0 register data
5652+ */
5653+ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
5654+ data[1] = value & 0x00ff;
5655+
5656+ wm8750_write_reg_cache (codec, reg, value);
5657+ if (codec->hw_write(codec->control_data, data, 2) == 2)
5658+ return 0;
5659+ else
5660+ return -EIO;
5661+}
5662+
5663+#define wm8750_reset(c) wm8750_write(c, WM8750_RESET, 0)
5664+
5665+/*
5666+ * WM8750 Controls
5667+ */
5668+static const char *wm8750_bass[] = {"Linear Control", "Adaptive Boost"};
5669+static const char *wm8750_bass_filter[] = { "130Hz @ 48kHz", "200Hz @ 48kHz" };
5670+static const char *wm8750_treble[] = {"8kHz", "4kHz"};
5671+static const char *wm8750_3d_lc[] = {"200Hz", "500Hz"};
5672+static const char *wm8750_3d_uc[] = {"2.2kHz", "1.5kHz"};
5673+static const char *wm8750_3d_func[] = {"Capture", "Playback"};
5674+static const char *wm8750_alc_func[] = {"Off", "Right", "Left", "Stereo"};
5675+static const char *wm8750_ng_type[] = {"Constant PGA Gain",
5676+ "Mute ADC Output"};
5677+static const char *wm8750_line_mux[] = {"Line 1", "Line 2", "Line 3", "PGA",
5678+ "Differential"};
5679+static const char *wm8750_pga_sel[] = {"Line 1", "Line 2", "Line 3",
5680+ "Differential"};
5681+static const char *wm8750_out3[] = {"VREF", "ROUT1 + Vol", "MonoOut",
5682+ "ROUT1"};
5683+static const char *wm8750_diff_sel[] = {"Line 1", "Line 2"};
5684+static const char *wm8750_adcpol[] = {"Normal", "L Invert", "R Invert",
5685+ "L + R Invert"};
5686+static const char *wm8750_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
5687+static const char *wm8750_mono_mux[] = {"Stereo", "Mono (Left)",
5688+ "Mono (Right)", "Digital Mono"};
5689+
5690+static const struct soc_enum wm8750_enum[] = {
5691+SOC_ENUM_SINGLE(WM8750_BASS, 7, 2, wm8750_bass),
5692+SOC_ENUM_SINGLE(WM8750_BASS, 6, 2, wm8750_bass_filter),
5693+SOC_ENUM_SINGLE(WM8750_TREBLE, 6, 2, wm8750_treble),
5694+SOC_ENUM_SINGLE(WM8750_3D, 5, 2, wm8750_3d_lc),
5695+SOC_ENUM_SINGLE(WM8750_3D, 6, 2, wm8750_3d_uc),
5696+SOC_ENUM_SINGLE(WM8750_3D, 7, 2, wm8750_3d_func),
5697+SOC_ENUM_SINGLE(WM8750_ALC1, 7, 4, wm8750_alc_func),
5698+SOC_ENUM_SINGLE(WM8750_NGATE, 1, 2, wm8750_ng_type),
5699+SOC_ENUM_SINGLE(WM8750_LOUTM1, 0, 5, wm8750_line_mux),
5700+SOC_ENUM_SINGLE(WM8750_ROUTM1, 0, 5, wm8750_line_mux),
5701+SOC_ENUM_SINGLE(WM8750_LADCIN, 6, 4, wm8750_pga_sel), /* 10 */
5702+SOC_ENUM_SINGLE(WM8750_RADCIN, 6, 4, wm8750_pga_sel),
5703+SOC_ENUM_SINGLE(WM8750_ADCTL2, 7, 4, wm8750_out3),
5704+SOC_ENUM_SINGLE(WM8750_ADCIN, 8, 2, wm8750_diff_sel),
5705+SOC_ENUM_SINGLE(WM8750_ADCDAC, 5, 4, wm8750_adcpol),
5706+SOC_ENUM_SINGLE(WM8750_ADCDAC, 1, 4, wm8750_deemph),
5707+SOC_ENUM_SINGLE(WM8750_ADCIN, 6, 4, wm8750_mono_mux), /* 16 */
5708+
5709+};
5710+
5711+static const struct snd_kcontrol_new wm8750_snd_controls[] = {
5712+
5713+SOC_DOUBLE_R("Capture Volume", WM8750_LINVOL, WM8750_RINVOL, 0, 63, 0),
5714+SOC_DOUBLE_R("Capture ZC Switch", WM8750_LINVOL, WM8750_RINVOL, 6, 1, 0),
5715+SOC_DOUBLE_R("Capture Switch", WM8750_LINVOL, WM8750_RINVOL, 7, 1, 1),
5716+
5717+SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8750_LOUT1V,
5718+ WM8750_ROUT1V, 7, 1, 0),
5719+SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8750_LOUT2V,
5720+ WM8750_ROUT2V, 7, 1, 0),
5721+
5722+SOC_ENUM("Playback De-emphasis", wm8750_enum[15]),
5723+
5724+SOC_ENUM("Capture Polarity", wm8750_enum[14]),
5725+SOC_SINGLE("Playback 6dB Attenuate", WM8750_ADCDAC, 7, 1, 0),
5726+SOC_SINGLE("Capture 6dB Attenuate", WM8750_ADCDAC, 8, 1, 0),
5727+
5728+SOC_DOUBLE_R("PCM Volume", WM8750_LDAC, WM8750_RDAC, 0, 255, 0),
5729+
5730+SOC_ENUM("Bass Boost", wm8750_enum[0]),
5731+SOC_ENUM("Bass Filter", wm8750_enum[1]),
5732+SOC_SINGLE("Bass Volume", WM8750_BASS, 0, 15, 1),
5733+
5734+SOC_SINGLE("Treble Volume", WM8750_TREBLE, 0, 15, 0),
5735+SOC_ENUM("Treble Cut-off", wm8750_enum[2]),
5736+
5737+SOC_SINGLE("3D Switch", WM8750_3D, 0, 1, 0),
5738+SOC_SINGLE("3D Volume", WM8750_3D, 1, 15, 0),
5739+SOC_ENUM("3D Lower Cut-off", wm8750_enum[3]),
5740+SOC_ENUM("3D Upper Cut-off", wm8750_enum[4]),
5741+SOC_ENUM("3D Mode", wm8750_enum[5]),
5742+
5743+SOC_SINGLE("ALC Capture Target Volume", WM8750_ALC1, 0, 7, 0),
5744+SOC_SINGLE("ALC Capture Max Volume", WM8750_ALC1, 4, 7, 0),
5745+SOC_ENUM("ALC Capture Function", wm8750_enum[6]),
5746+SOC_SINGLE("ALC Capture ZC Switch", WM8750_ALC2, 7, 1, 0),
5747+SOC_SINGLE("ALC Capture Hold Time", WM8750_ALC2, 0, 15, 0),
5748+SOC_SINGLE("ALC Capture Decay Time", WM8750_ALC3, 4, 15, 0),
5749+SOC_SINGLE("ALC Capture Attack Time", WM8750_ALC3, 0, 15, 0),
5750+SOC_SINGLE("ALC Capture NG Threshold", WM8750_NGATE, 3, 31, 0),
5751+SOC_ENUM("ALC Capture NG Type", wm8750_enum[4]),
5752+SOC_SINGLE("ALC Capture NG Switch", WM8750_NGATE, 0, 1, 0),
5753+
5754+SOC_SINGLE("Left ADC Capture Volume", WM8750_LADC, 0, 255, 0),
5755+SOC_SINGLE("Right ADC Capture Volume", WM8750_RADC, 0, 255, 0),
5756+
5757+SOC_SINGLE("ZC Timeout Switch", WM8750_ADCTL1, 0, 1, 0),
5758+SOC_SINGLE("Playback Invert Switch", WM8750_ADCTL1, 1, 1, 0),
5759+
5760+SOC_SINGLE("Right Speaker Playback Invert Switch", WM8750_ADCTL2, 4, 1, 0),
5761+
5762+/* Unimplemented */
5763+/* ADCDAC Bit 0 - ADCHPD */
5764+/* ADCDAC Bit 4 - HPOR */
5765+/* ADCTL1 Bit 2,3 - DATSEL */
5766+/* ADCTL1 Bit 4,5 - DMONOMIX */
5767+/* ADCTL1 Bit 6,7 - VSEL */
5768+/* ADCTL2 Bit 2 - LRCM */
5769+/* ADCTL2 Bit 3 - TRI */
5770+/* ADCTL3 Bit 5 - HPFLREN */
5771+/* ADCTL3 Bit 6 - VROI */
5772+/* ADCTL3 Bit 7,8 - ADCLRM */
5773+/* ADCIN Bit 4 - LDCM */
5774+/* ADCIN Bit 5 - RDCM */
5775+
5776+SOC_DOUBLE_R("Mic Boost", WM8750_LADCIN, WM8750_RADCIN, 4, 3, 0),
5777+
5778+SOC_DOUBLE_R("Bypass Left Playback Volume", WM8750_LOUTM1,
5779+ WM8750_LOUTM2, 4, 7, 1),
5780+SOC_DOUBLE_R("Bypass Right Playback Volume", WM8750_ROUTM1,
5781+ WM8750_ROUTM2, 4, 7, 1),
5782+SOC_DOUBLE_R("Bypass Mono Playback Volume", WM8750_MOUTM1,
5783+ WM8750_MOUTM2, 4, 7, 1),
5784+
5785+SOC_SINGLE("Mono Playback ZC Switch", WM8750_MOUTV, 7, 1, 0),
5786+
5787+SOC_DOUBLE_R("Headphone Playback Volume", WM8750_LOUT1V, WM8750_ROUT1V,
5788+ 0, 127, 0),
5789+SOC_DOUBLE_R("Speaker Playback Volume", WM8750_LOUT2V, WM8750_ROUT2V,
5790+ 0, 127, 0),
5791+
5792+SOC_SINGLE("Mono Playback Volume", WM8750_MOUTV, 0, 127, 0),
5793+
5794+};
5795+
5796+/* add non dapm controls */
5797+static int wm8750_add_controls(struct snd_soc_codec *codec)
5798+{
5799+ int err, i;
5800+
5801+ for (i = 0; i < ARRAY_SIZE(wm8750_snd_controls); i++) {
5802+ err = snd_ctl_add(codec->card,
5803+ snd_soc_cnew(&wm8750_snd_controls[i],codec, NULL));
5804+ if (err < 0)
5805+ return err;
5806+ }
5807+ return 0;
5808+}
5809+
5810+/*
5811+ * DAPM Controls
5812+ */
5813+
5814+/* Left Mixer */
5815+static const struct snd_kcontrol_new wm8750_left_mixer_controls[] = {
5816+SOC_DAPM_SINGLE("Playback Switch", WM8750_LOUTM1, 8, 1, 0),
5817+SOC_DAPM_SINGLE("Left Bypass Switch", WM8750_LOUTM1, 7, 1, 0),
5818+SOC_DAPM_SINGLE("Right Playback Switch", WM8750_LOUTM2, 8, 1, 0),
5819+SOC_DAPM_SINGLE("Right Bypass Switch", WM8750_LOUTM2, 7, 1, 0),
5820+};
5821+
5822+/* Right Mixer */
5823+static const struct snd_kcontrol_new wm8750_right_mixer_controls[] = {
5824+SOC_DAPM_SINGLE("Left Playback Switch", WM8750_ROUTM1, 8, 1, 0),
5825+SOC_DAPM_SINGLE("Left Bypass Switch", WM8750_ROUTM1, 7, 1, 0),
5826+SOC_DAPM_SINGLE("Playback Switch", WM8750_ROUTM2, 8, 1, 0),
5827+SOC_DAPM_SINGLE("Right Bypass Switch", WM8750_ROUTM2, 7, 1, 0),
5828+};
5829+
5830+/* Mono Mixer */
5831+static const struct snd_kcontrol_new wm8750_mono_mixer_controls[] = {
5832+SOC_DAPM_SINGLE("Left Playback Switch", WM8750_MOUTM1, 8, 1, 0),
5833+SOC_DAPM_SINGLE("Left Bypass Switch", WM8750_MOUTM1, 7, 1, 0),
5834+SOC_DAPM_SINGLE("Right Playback Switch", WM8750_MOUTM2, 8, 1, 0),
5835+SOC_DAPM_SINGLE("Right Bypass Switch", WM8750_MOUTM2, 7, 1, 0),
5836+};
5837+
5838+/* Left Line Mux */
5839+static const struct snd_kcontrol_new wm8750_left_line_controls =
5840+SOC_DAPM_ENUM("Route", wm8750_enum[8]);
5841+
5842+/* Right Line Mux */
5843+static const struct snd_kcontrol_new wm8750_right_line_controls =
5844+SOC_DAPM_ENUM("Route", wm8750_enum[9]);
5845+
5846+/* Left PGA Mux */
5847+static const struct snd_kcontrol_new wm8750_left_pga_controls =
5848+SOC_DAPM_ENUM("Route", wm8750_enum[10]);
5849+
5850+/* Right PGA Mux */
5851+static const struct snd_kcontrol_new wm8750_right_pga_controls =
5852+SOC_DAPM_ENUM("Route", wm8750_enum[11]);
5853+
5854+/* Out 3 Mux */
5855+static const struct snd_kcontrol_new wm8750_out3_controls =
5856+SOC_DAPM_ENUM("Route", wm8750_enum[12]);
5857+
5858+/* Differential Mux */
5859+static const struct snd_kcontrol_new wm8750_diffmux_controls =
5860+SOC_DAPM_ENUM("Route", wm8750_enum[13]);
5861+
5862+/* Mono ADC Mux */
5863+static const struct snd_kcontrol_new wm8750_monomux_controls =
5864+SOC_DAPM_ENUM("Route", wm8750_enum[16]);
5865+
5866+static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = {
5867+ SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
5868+ &wm8750_left_mixer_controls[0],
5869+ ARRAY_SIZE(wm8750_left_mixer_controls)),
5870+ SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
5871+ &wm8750_right_mixer_controls[0],
5872+ ARRAY_SIZE(wm8750_right_mixer_controls)),
5873+ SND_SOC_DAPM_MIXER("Mono Mixer", WM8750_PWR2, 2, 0,
5874+ &wm8750_mono_mixer_controls[0],
5875+ ARRAY_SIZE(wm8750_mono_mixer_controls)),
5876+
5877+ SND_SOC_DAPM_PGA("Right Out 2", WM8750_PWR2, 3, 0, NULL, 0),
5878+ SND_SOC_DAPM_PGA("Left Out 2", WM8750_PWR2, 4, 0, NULL, 0),
5879+ SND_SOC_DAPM_PGA("Right Out 1", WM8750_PWR2, 5, 0, NULL, 0),
5880+ SND_SOC_DAPM_PGA("Left Out 1", WM8750_PWR2, 6, 0, NULL, 0),
5881+ SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8750_PWR2, 7, 0),
5882+ SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8750_PWR2, 8, 0),
5883+
5884+ SND_SOC_DAPM_MICBIAS("Mic Bias", WM8750_PWR1, 1, 0),
5885+ SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8750_PWR1, 2, 0),
5886+ SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8750_PWR1, 3, 0),
5887+
5888+ SND_SOC_DAPM_MUX("Left PGA Mux", WM8750_PWR1, 5, 0,
5889+ &wm8750_left_pga_controls),
5890+ SND_SOC_DAPM_MUX("Right PGA Mux", WM8750_PWR1, 4, 0,
5891+ &wm8750_right_pga_controls),
5892+ SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0,
5893+ &wm8750_left_line_controls),
5894+ SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0,
5895+ &wm8750_right_line_controls),
5896+
5897+ SND_SOC_DAPM_MUX("Out3 Mux", SND_SOC_NOPM, 0, 0, &wm8750_out3_controls),
5898+ SND_SOC_DAPM_PGA("Out 3", WM8750_PWR2, 1, 0, NULL, 0),
5899+ SND_SOC_DAPM_PGA("Mono Out 1", WM8750_PWR2, 2, 0, NULL, 0),
5900+
5901+ SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0,
5902+ &wm8750_diffmux_controls),
5903+ SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0,
5904+ &wm8750_monomux_controls),
5905+ SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0,
5906+ &wm8750_monomux_controls),
5907+
5908+ SND_SOC_DAPM_OUTPUT("LOUT1"),
5909+ SND_SOC_DAPM_OUTPUT("ROUT1"),
5910+ SND_SOC_DAPM_OUTPUT("LOUT2"),
5911+ SND_SOC_DAPM_OUTPUT("ROUT2"),
5912+ SND_SOC_DAPM_OUTPUT("MONO"),
5913+ SND_SOC_DAPM_OUTPUT("OUT3"),
5914+
5915+ SND_SOC_DAPM_INPUT("LINPUT1"),
5916+ SND_SOC_DAPM_INPUT("LINPUT2"),
5917+ SND_SOC_DAPM_INPUT("LINPUT3"),
5918+ SND_SOC_DAPM_INPUT("RINPUT1"),
5919+ SND_SOC_DAPM_INPUT("RINPUT2"),
5920+ SND_SOC_DAPM_INPUT("RINPUT3"),
5921+};
5922+
5923+static const char *audio_map[][3] = {
5924+ /* left mixer */
5925+ {"Left Mixer", "Playback Switch", "Left DAC"},
5926+ {"Left Mixer", "Left Bypass Switch", "Left Line Mux"},
5927+ {"Left Mixer", "Right Playback Switch", "Right DAC"},
5928+ {"Left Mixer", "Right Bypass Switch", "Right Line Mux"},
5929+
5930+ /* right mixer */
5931+ {"Right Mixer", "Left Playback Switch", "Left DAC"},
5932+ {"Right Mixer", "Left Bypass Switch", "Left Line Mux"},
5933+ {"Right Mixer", "Playback Switch", "Right DAC"},
5934+ {"Right Mixer", "Right Bypass Switch", "Right Line Mux"},
5935+
5936+ /* left out 1 */
5937+ {"Left Out 1", NULL, "Left Mixer"},
5938+ {"LOUT1", NULL, "Left Out 1"},
5939+
5940+ /* left out 2 */
5941+ {"Left Out 2", NULL, "Left Mixer"},
5942+ {"LOUT2", NULL, "Left Out 2"},
5943+
5944+ /* right out 1 */
5945+ {"Right Out 1", NULL, "Right Mixer"},
5946+ {"ROUT1", NULL, "Right Out 1"},
5947+
5948+ /* right out 2 */
5949+ {"Right Out 2", NULL, "Right Mixer"},
5950+ {"ROUT2", NULL, "Right Out 2"},
5951+
5952+ /* mono mixer */
5953+ {"Mono Mixer", "Left Playback Switch", "Left DAC"},
5954+ {"Mono Mixer", "Left Bypass Switch", "Left Line Mux"},
5955+ {"Mono Mixer", "Right Playback Switch", "Right DAC"},
5956+ {"Mono Mixer", "Right Bypass Switch", "Right Line Mux"},
5957+
5958+ /* mono out */
5959+ {"Mono Out 1", NULL, "Mono Mixer"},
5960+ {"MONO1", NULL, "Mono Out 1"},
5961+
5962+ /* out 3 */
5963+ {"Out3 Mux", "VREF", "VREF"},
5964+ {"Out3 Mux", "ROUT1 + Vol", "ROUT1"},
5965+ {"Out3 Mux", "ROUT1", "Right Mixer"},
5966+ {"Out3 Mux", "MonoOut", "MONO1"},
5967+ {"Out 3", NULL, "Out3 Mux"},
5968+ {"OUT3", NULL, "Out 3"},
5969+
5970+ /* Left Line Mux */
5971+ {"Left Line Mux", "Line 1", "LINPUT1"},
5972+ {"Left Line Mux", "Line 2", "LINPUT2"},
5973+ {"Left Line Mux", "Line 3", "LINPUT3"},
5974+ {"Left Line Mux", "PGA", "Left PGA Mux"},
5975+ {"Left Line Mux", "Differential", "Differential Mux"},
5976+
5977+ /* Right Line Mux */
5978+ {"Right Line Mux", "Line 1", "RINPUT1"},
5979+ {"Right Line Mux", "Line 2", "RINPUT2"},
5980+ {"Right Line Mux", "Line 3", "RINPUT3"},
5981+ {"Right Line Mux", "PGA", "Right PGA Mux"},
5982+ {"Right Line Mux", "Differential", "Differential Mux"},
5983+
5984+ /* Left PGA Mux */
5985+ {"Left PGA Mux", "Line 1", "LINPUT1"},
5986+ {"Left PGA Mux", "Line 2", "LINPUT2"},
5987+ {"Left PGA Mux", "Line 3", "LINPUT3"},
5988+ {"Left PGA Mux", "Differential", "Differential Mux"},
5989+
5990+ /* Right PGA Mux */
5991+ {"Right PGA Mux", "Line 1", "RINPUT1"},
5992+ {"Right PGA Mux", "Line 2", "RINPUT2"},
5993+ {"Right PGA Mux", "Line 3", "RINPUT3"},
5994+ {"Right PGA Mux", "Differential", "Differential Mux"},
5995+
5996+ /* Differential Mux */
5997+ {"Differential Mux", "Line 1", "LINPUT1"},
5998+ {"Differential Mux", "Line 1", "RINPUT1"},
5999+ {"Differential Mux", "Line 2", "LINPUT2"},
6000+ {"Differential Mux", "Line 2", "RINPUT2"},
6001+
6002+ /* Left ADC Mux */
6003+ {"Left ADC Mux", "Stereo", "Left PGA Mux"},
6004+ {"Left ADC Mux", "Mono (Left)", "Left PGA Mux"},
6005+ {"Left ADC Mux", "Digital Mono", "Left PGA Mux"},
6006+
6007+ /* Right ADC Mux */
6008+ {"Right ADC Mux", "Stereo", "Right PGA Mux"},
6009+ {"Right ADC Mux", "Mono (Right)", "Right PGA Mux"},
6010+ {"Right ADC Mux", "Digital Mono", "Right PGA Mux"},
6011+
6012+ /* ADC */
6013+ {"Left ADC", NULL, "Left ADC Mux"},
6014+ {"Right ADC", NULL, "Right ADC Mux"},
6015+
6016+ /* terminator */
6017+ {NULL, NULL, NULL},
6018+};
6019+
6020+static int wm8750_add_widgets(struct snd_soc_codec *codec)
6021+{
6022+ int i;
6023+
6024+ for(i = 0; i < ARRAY_SIZE(wm8750_dapm_widgets); i++) {
6025+ snd_soc_dapm_new_control(codec, &wm8750_dapm_widgets[i]);
6026+ }
6027+
6028+ /* set up audio path audio_mapnects */
6029+ for(i = 0; audio_map[i][0] != NULL; i++) {
6030+ snd_soc_dapm_connect_input(codec, audio_map[i][0],
6031+ audio_map[i][1], audio_map[i][2]);
6032+ }
6033+
6034+ snd_soc_dapm_new_widgets(codec);
6035+ return 0;
6036+}
6037+
6038+struct _coeff_div {
6039+ u32 mclk;
6040+ u32 rate;
6041+ u16 fs;
6042+ u8 sr:5;
6043+ u8 usb:1;
6044+};
6045+
6046+/* codec hifi mclk clock divider coefficients */
6047+static const struct _coeff_div coeff_div[] = {
6048+ /* 8k */
6049+ {12288000, 8000, 1536, 0x6, 0x0},
6050+ {11289600, 8000, 1408, 0x16, 0x0},
6051+ {18432000, 8000, 2304, 0x7, 0x0},
6052+ {16934400, 8000, 2112, 0x17, 0x0},
6053+ {12000000, 8000, 1500, 0x6, 0x1},
6054+
6055+ /* 11.025k */
6056+ {11289600, 11025, 1024, 0x18, 0x0},
6057+ {16934400, 11025, 1536, 0x19, 0x0},
6058+ {12000000, 11025, 1088, 0x19, 0x1},
6059+
6060+ /* 16k */
6061+ {12288000, 16000, 768, 0xa, 0x0},
6062+ {18432000, 16000, 1152, 0xb, 0x0},
6063+ {12000000, 16000, 750, 0xa, 0x1},
6064+
6065+ /* 22.05k */
6066+ {11289600, 22050, 512, 0x1a, 0x0},
6067+ {16934400, 22050, 768, 0x1b, 0x0},
6068+ {12000000, 22050, 544, 0x1b, 0x1},
6069+
6070+ /* 32k */
6071+ {12288000, 32000, 384, 0xc, 0x0},
6072+ {18432000, 32000, 576, 0xd, 0x0},
6073+ {12000000, 32000, 375, 0xa, 0x1},
6074+
6075+ /* 44.1k */
6076+ {11289600, 44100, 256, 0x10, 0x0},
6077+ {16934400, 44100, 384, 0x11, 0x0},
6078+ {12000000, 44100, 272, 0x11, 0x1},
6079+
6080+ /* 48k */
6081+ {12288000, 48000, 256, 0x0, 0x0},
6082+ {18432000, 48000, 384, 0x1, 0x0},
6083+ {12000000, 48000, 250, 0x0, 0x1},
6084+
6085+ /* 88.2k */
6086+ {11289600, 88200, 128, 0x1e, 0x0},
6087+ {16934400, 88200, 192, 0x1f, 0x0},
6088+ {12000000, 88200, 136, 0x1f, 0x1},
6089+
6090+ /* 96k */
6091+ {12288000, 96000, 128, 0xe, 0x0},
6092+ {18432000, 96000, 192, 0xf, 0x0},
6093+ {12000000, 96000, 125, 0xe, 0x1},
6094+};
6095+
6096+static inline int get_coeff(int mclk, int rate)
6097+{
6098+ int i;
6099+
6100+ for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
6101+ if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
6102+ return i;
6103+ }
6104+
6105+ printk(KERN_ERR "wm8750: could not get coeff for mclk %d @ rate %d\n",
6106+ mclk, rate);
6107+ return -EINVAL;
6108+}
6109+
6110+/* WM8750 supports numerous input clocks per sample rate */
6111+static unsigned int wm8750_config_sysclk(struct snd_soc_codec_dai *dai,
6112+ struct snd_soc_clock_info *info, unsigned int clk)
6113+{
6114+ dai->mclk = clk;
6115+ return dai->mclk;
6116+}
6117+
6118+static int wm8750_pcm_prepare(struct snd_pcm_substream *substream)
6119+{
6120+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
6121+ struct snd_soc_device *socdev = rtd->socdev;
6122+ struct snd_soc_codec *codec = socdev->codec;
6123+ u16 iface = 0, bfs, srate = 0;
6124+ int i = get_coeff(rtd->codec_dai->mclk,
6125+ snd_soc_get_rate(rtd->codec_dai->dai_runtime.pcmrate));
6126+
6127+ /* is coefficient valid ? */
6128+ if (i < 0)
6129+ return i;
6130+
6131+ bfs = SND_SOC_FSBD_REAL(rtd->codec_dai->dai_runtime.bfs);
6132+
6133+ /* set master/slave audio interface */
6134+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
6135+ case SND_SOC_DAIFMT_CBM_CFM:
6136+ iface = 0x0040;
6137+ break;
6138+ case SND_SOC_DAIFMT_CBS_CFS:
6139+ break;
6140+ }
6141+
6142+ /* interface format */
6143+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
6144+ case SND_SOC_DAIFMT_I2S:
6145+ iface |= 0x0002;
6146+ break;
6147+ case SND_SOC_DAIFMT_RIGHT_J:
6148+ break;
6149+ case SND_SOC_DAIFMT_LEFT_J:
6150+ iface |= 0x0001;
6151+ break;
6152+ case SND_SOC_DAIFMT_DSP_A:
6153+ iface |= 0x0003;
6154+ break;
6155+ case SND_SOC_DAIFMT_DSP_B:
6156+ iface |= 0x0013;
6157+ break;
6158+ }
6159+
6160+ /* bit size */
6161+ switch (rtd->codec_dai->dai_runtime.pcmfmt) {
6162+ case SNDRV_PCM_FMTBIT_S16_LE:
6163+ break;
6164+ case SNDRV_PCM_FMTBIT_S20_3LE:
6165+ iface |= 0x0004;
6166+ break;
6167+ case SNDRV_PCM_FMTBIT_S24_LE:
6168+ iface |= 0x0008;
6169+ break;
6170+ case SNDRV_PCM_FMTBIT_S32_LE:
6171+ iface |= 0x000c;
6172+ break;
6173+ }
6174+
6175+ /* clock inversion */
6176+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_INV_MASK) {
6177+ case SND_SOC_DAIFMT_NB_NF:
6178+ break;
6179+ case SND_SOC_DAIFMT_IB_IF:
6180+ iface |= 0x0090;
6181+ break;
6182+ case SND_SOC_DAIFMT_IB_NF:
6183+ iface |= 0x0080;
6184+ break;
6185+ case SND_SOC_DAIFMT_NB_IF:
6186+ iface |= 0x0010;
6187+ break;
6188+ }
6189+
6190+ /* set bclk divisor rate */
6191+ switch (bfs) {
6192+ case 1:
6193+ break;
6194+ case 4:
6195+ srate |= (0x1 << 7);
6196+ break;
6197+ case 8:
6198+ srate |= (0x2 << 7);
6199+ break;
6200+ case 16:
6201+ srate |= (0x3 << 7);
6202+ break;
6203+ }
6204+
6205+ /* set iface & srate */
6206+ wm8750_write(codec, WM8750_IFACE, iface);
6207+ wm8750_write(codec, WM8750_SRATE, srate |
6208+ (coeff_div[i].sr << 1) | coeff_div[i].usb);
6209+
6210+ return 0;
6211+}
6212+
6213+static int wm8750_mute(struct snd_soc_codec *codec,
6214+ struct snd_soc_codec_dai *dai, int mute)
6215+{
6216+ u16 mute_reg = wm8750_read_reg_cache(codec, WM8750_ADCDAC) & 0xfff7;
6217+ if (mute)
6218+ wm8750_write(codec, WM8750_ADCDAC, mute_reg | 0x8);
6219+ else
6220+ wm8750_write(codec, WM8750_ADCDAC, mute_reg);
6221+ return 0;
6222+}
6223+
6224+static int wm8750_dapm_event(struct snd_soc_codec *codec, int event)
6225+{
6226+ u16 pwr_reg = wm8750_read_reg_cache(codec, WM8750_PWR1) & 0xfe3e;
6227+
6228+ switch (event) {
6229+ case SNDRV_CTL_POWER_D0: /* full On */
6230+ /* set vmid to 50k and unmute dac */
6231+ wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x00c0);
6232+ break;
6233+ case SNDRV_CTL_POWER_D1: /* partial On */
6234+ case SNDRV_CTL_POWER_D2: /* partial On */
6235+ /* set vmid to 5k for quick power up */
6236+ wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x01c1);
6237+ break;
6238+ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
6239+ /* mute dac and set vmid to 500k, enable VREF */
6240+ wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x0141);
6241+ break;
6242+ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
6243+ wm8750_write(codec, WM8750_PWR1, 0x0001);
6244+ break;
6245+ }
6246+ codec->dapm_state = event;
6247+ return 0;
6248+}
6249+
6250+struct snd_soc_codec_dai wm8750_dai = {
6251+ .name = "WM8750",
6252+ .playback = {
6253+ .stream_name = "Playback",
6254+ .channels_min = 1,
6255+ .channels_max = 2,
6256+ },
6257+ .capture = {
6258+ .stream_name = "Capture",
6259+ .channels_min = 1,
6260+ .channels_max = 2,
6261+ },
6262+ .config_sysclk = wm8750_config_sysclk,
6263+ .digital_mute = wm8750_mute,
6264+ .ops = {
6265+ .prepare = wm8750_pcm_prepare,
6266+ },
6267+ .caps = {
6268+ .num_modes = ARRAY_SIZE(wm8750_modes),
6269+ .mode = wm8750_modes,
6270+ },
6271+};
6272+EXPORT_SYMBOL_GPL(wm8750_dai);
6273+
6274+static void wm8750_work(void *data)
6275+{
6276+ struct snd_soc_codec *codec = (struct snd_soc_codec *)data;
6277+ wm8750_dapm_event(codec, codec->dapm_state);
6278+}
6279+
6280+static int wm8750_suspend(struct platform_device *pdev, pm_message_t state)
6281+{
6282+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
6283+ struct snd_soc_codec *codec = socdev->codec;
6284+
6285+ wm8750_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
6286+ return 0;
6287+}
6288+
6289+static int wm8750_resume(struct platform_device *pdev)
6290+{
6291+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
6292+ struct snd_soc_codec *codec = socdev->codec;
6293+ int i;
6294+ u8 data[2];
6295+ u16 *cache = codec->reg_cache;
6296+
6297+ /* Sync reg_cache with the hardware */
6298+ for (i = 0; i < ARRAY_SIZE(wm8750_reg); i++) {
6299+ if (i == WM8750_RESET)
6300+ continue;
6301+ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
6302+ data[1] = cache[i] & 0x00ff;
6303+ codec->hw_write(codec->control_data, data, 2);
6304+ }
6305+
6306+ wm8750_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
6307+
6308+ /* charge wm8750 caps */
6309+ if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0) {
6310+ wm8750_dapm_event(codec, SNDRV_CTL_POWER_D2);
6311+ codec->dapm_state = SNDRV_CTL_POWER_D0;
6312+ queue_delayed_work(wm8750_workq, &wm8750_dapm_work,
6313+ msecs_to_jiffies(1000));
6314+ }
6315+
6316+ return 0;
6317+}
6318+
6319+/*
6320+ * initialise the WM8750 driver
6321+ * register the mixer and dsp interfaces with the kernel
6322+ */
6323+static int wm8750_init(struct snd_soc_device *socdev)
6324+{
6325+ struct snd_soc_codec *codec = socdev->codec;
6326+ int reg, ret = 0;
6327+
6328+ codec->name = "WM8750";
6329+ codec->owner = THIS_MODULE;
6330+ codec->read = wm8750_read_reg_cache;
6331+ codec->write = wm8750_write;
6332+ codec->dapm_event = wm8750_dapm_event;
6333+ codec->dai = &wm8750_dai;
6334+ codec->num_dai = 1;
6335+ codec->reg_cache_size = ARRAY_SIZE(wm8750_reg);
6336+
6337+ codec->reg_cache =
6338+ kzalloc(sizeof(u16) * ARRAY_SIZE(wm8750_reg), GFP_KERNEL);
6339+ if (codec->reg_cache == NULL)
6340+ return -ENOMEM;
6341+ memcpy(codec->reg_cache, wm8750_reg,
6342+ sizeof(u16) * ARRAY_SIZE(wm8750_reg));
6343+ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm8750_reg);
6344+
6345+ wm8750_reset(codec);
6346+
6347+ /* register pcms */
6348+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
6349+ if (ret < 0) {
6350+ kfree(codec->reg_cache);
6351+ return ret;
6352+ }
6353+
6354+ /* charge output caps */
6355+ wm8750_dapm_event(codec, SNDRV_CTL_POWER_D2);
6356+ codec->dapm_state = SNDRV_CTL_POWER_D3hot;
6357+ queue_delayed_work(wm8750_workq, &wm8750_dapm_work,
6358+ msecs_to_jiffies(1000));
6359+
6360+ /* set the update bits */
6361+ reg = wm8750_read_reg_cache(codec, WM8750_LDAC);
6362+ wm8750_write(codec, WM8750_LDAC, reg | 0x0100);
6363+ reg = wm8750_read_reg_cache(codec, WM8750_RDAC);
6364+ wm8750_write(codec, WM8750_RDAC, reg | 0x0100);
6365+ reg = wm8750_read_reg_cache(codec, WM8750_LOUT1V);
6366+ wm8750_write(codec, WM8750_LOUT1V, reg | 0x0100);
6367+ reg = wm8750_read_reg_cache(codec, WM8750_ROUT1V);
6368+ wm8750_write(codec, WM8750_ROUT1V, reg | 0x0100);
6369+ reg = wm8750_read_reg_cache(codec, WM8750_LOUT2V);
6370+ wm8750_write(codec, WM8750_LOUT2V, reg | 0x0100);
6371+ reg = wm8750_read_reg_cache(codec, WM8750_ROUT2V);
6372+ wm8750_write(codec, WM8750_ROUT2V, reg | 0x0100);
6373+ reg = wm8750_read_reg_cache(codec, WM8750_LINVOL);
6374+ wm8750_write(codec, WM8750_LINVOL, reg | 0x0100);
6375+ reg = wm8750_read_reg_cache(codec, WM8750_RINVOL);
6376+ wm8750_write(codec, WM8750_RINVOL, reg | 0x0100);
6377+
6378+ wm8750_add_controls(codec);
6379+ wm8750_add_widgets(codec);
6380+ ret = snd_soc_register_card(socdev);
6381+ if (ret < 0) {
6382+ snd_soc_free_pcms(socdev);
6383+ snd_soc_dapm_free(socdev);
6384+ }
6385+
6386+ return ret;
6387+}
6388+
6389+/* If the i2c layer weren't so broken, we could pass this kind of data
6390+ around */
6391+static struct snd_soc_device *wm8750_socdev;
6392+
6393+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
6394+
6395+/*
6396+ * WM8731 2 wire address is determined by GPIO5
6397+ * state during powerup.
6398+ * low = 0x1a
6399+ * high = 0x1b
6400+ */
6401+static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
6402+
6403+/* Magic definition of all other variables and things */
6404+I2C_CLIENT_INSMOD;
6405+
6406+static struct i2c_driver wm8750_i2c_driver;
6407+static struct i2c_client client_template;
6408+
6409+static int wm8750_codec_probe(struct i2c_adapter *adap, int addr, int kind)
6410+{
6411+ struct snd_soc_device *socdev = wm8750_socdev;
6412+ struct wm8750_setup_data *setup = socdev->codec_data;
6413+ struct snd_soc_codec *codec = socdev->codec;
6414+ struct i2c_client *i2c;
6415+ int ret;
6416+
6417+ if (addr != setup->i2c_address)
6418+ return -ENODEV;
6419+
6420+ client_template.adapter = adap;
6421+ client_template.addr = addr;
6422+
6423+ i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
6424+ if (i2c == NULL) {
6425+ kfree(codec);
6426+ return -ENOMEM;
6427+ }
6428+ memcpy(i2c, &client_template, sizeof(struct i2c_client));
6429+ i2c_set_clientdata(i2c, codec);
6430+ codec->control_data = i2c;
6431+
6432+ ret = i2c_attach_client(i2c);
6433+ if (ret < 0) {
6434+ err("failed to attach codec at addr %x\n", addr);
6435+ goto err;
6436+ }
6437+
6438+ ret = wm8750_init(socdev);
6439+ if (ret < 0) {
6440+ err("failed to initialise WM8750\n");
6441+ goto err;
6442+ }
6443+ return ret;
6444+
6445+err:
6446+ kfree(codec);
6447+ kfree(i2c);
6448+ return ret;
6449+}
6450+
6451+static int wm8750_i2c_detach(struct i2c_client *client)
6452+{
6453+ struct snd_soc_codec *codec = i2c_get_clientdata(client);
6454+ i2c_detach_client(client);
6455+ kfree(codec->reg_cache);
6456+ kfree(client);
6457+ return 0;
6458+}
6459+
6460+static int wm8750_i2c_attach(struct i2c_adapter *adap)
6461+{
6462+ return i2c_probe(adap, &addr_data, wm8750_codec_probe);
6463+}
6464+
6465+/* corgi i2c codec control layer */
6466+static struct i2c_driver wm8750_i2c_driver = {
6467+ .driver = {
6468+ .name = "WM8750 I2C Codec",
6469+ .owner = THIS_MODULE,
6470+ },
6471+ .id = I2C_DRIVERID_WM8750,
6472+ .attach_adapter = wm8750_i2c_attach,
6473+ .detach_client = wm8750_i2c_detach,
6474+ .command = NULL,
6475+};
6476+
6477+static struct i2c_client client_template = {
6478+ .name = "WM8750",
6479+ .driver = &wm8750_i2c_driver,
6480+};
6481+#endif
6482+
6483+static int wm8750_probe(struct platform_device *pdev)
6484+{
6485+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
6486+ struct wm8750_setup_data *setup = socdev->codec_data;
6487+ struct snd_soc_codec *codec;
6488+ int ret = 0;
6489+
6490+ info("WM8750 Audio Codec %s", WM8750_VERSION);
6491+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
6492+ if (codec == NULL)
6493+ return -ENOMEM;
6494+
6495+ socdev->codec = codec;
6496+ mutex_init(&codec->mutex);
6497+ INIT_LIST_HEAD(&codec->dapm_widgets);
6498+ INIT_LIST_HEAD(&codec->dapm_paths);
6499+ wm8750_socdev = socdev;
6500+ INIT_WORK(&wm8750_dapm_work, wm8750_work, codec);
6501+ wm8750_workq = create_workqueue("wm8750");
6502+ if (wm8750_workq == NULL) {
6503+ kfree(codec);
6504+ return -ENOMEM;
6505+ }
6506+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
6507+ if (setup->i2c_address) {
6508+ normal_i2c[0] = setup->i2c_address;
6509+ codec->hw_write = (hw_write_t)i2c_master_send;
6510+ ret = i2c_add_driver(&wm8750_i2c_driver);
6511+ if (ret != 0)
6512+ printk(KERN_ERR "can't add i2c driver");
6513+ }
6514+#else
6515+ /* Add other interfaces here */
6516+#endif
6517+
6518+ return ret;
6519+}
6520+
6521+/* power down chip */
6522+static int wm8750_remove(struct platform_device *pdev)
6523+{
6524+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
6525+ struct snd_soc_codec *codec = socdev->codec;
6526+
6527+ if (codec->control_data)
6528+ wm8750_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
6529+ if (wm8750_workq)
6530+ destroy_workqueue(wm8750_workq);
6531+ snd_soc_free_pcms(socdev);
6532+ snd_soc_dapm_free(socdev);
6533+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
6534+ i2c_del_driver(&wm8750_i2c_driver);
6535+#endif
6536+ kfree(codec);
6537+
6538+ return 0;
6539+}
6540+
6541+struct snd_soc_codec_device soc_codec_dev_wm8750 = {
6542+ .probe = wm8750_probe,
6543+ .remove = wm8750_remove,
6544+ .suspend = wm8750_suspend,
6545+ .resume = wm8750_resume,
6546+};
6547+
6548+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8750);
6549+
6550+MODULE_DESCRIPTION("ASoC WM8750 driver");
6551+MODULE_AUTHOR("Liam Girdwood");
6552+MODULE_LICENSE("GPL");
6553Index: linux-2.6-pxa-new/sound/soc/codecs/wm8750.h
6554===================================================================
6555--- /dev/null
6556+++ linux-2.6-pxa-new/sound/soc/codecs/wm8750.h
6557@@ -0,0 +1,66 @@
6558+/*
6559+ * Copyright 2005 Openedhand Ltd.
6560+ *
6561+ * Author: Richard Purdie <richard@openedhand.com>
6562+ *
6563+ * Based on WM8753.h
6564+ *
6565+ * This program is free software; you can redistribute it and/or modify
6566+ * it under the terms of the GNU General Public License version 2 as
6567+ * published by the Free Software Foundation.
6568+ *
6569+ */
6570+
6571+#ifndef _WM8750_H
6572+#define _WM8750_H
6573+
6574+/* WM8750 register space */
6575+
6576+#define WM8750_LINVOL 0x00
6577+#define WM8750_RINVOL 0x01
6578+#define WM8750_LOUT1V 0x02
6579+#define WM8750_ROUT1V 0x03
6580+#define WM8750_ADCDAC 0x05
6581+#define WM8750_IFACE 0x07
6582+#define WM8750_SRATE 0x08
6583+#define WM8750_LDAC 0x0a
6584+#define WM8750_RDAC 0x0b
6585+#define WM8750_BASS 0x0c
6586+#define WM8750_TREBLE 0x0d
6587+#define WM8750_RESET 0x0f
6588+#define WM8750_3D 0x10
6589+#define WM8750_ALC1 0x11
6590+#define WM8750_ALC2 0x12
6591+#define WM8750_ALC3 0x13
6592+#define WM8750_NGATE 0x14
6593+#define WM8750_LADC 0x15
6594+#define WM8750_RADC 0x16
6595+#define WM8750_ADCTL1 0x17
6596+#define WM8750_ADCTL2 0x18
6597+#define WM8750_PWR1 0x19
6598+#define WM8750_PWR2 0x1a
6599+#define WM8750_ADCTL3 0x1b
6600+#define WM8750_ADCIN 0x1f
6601+#define WM8750_LADCIN 0x20
6602+#define WM8750_RADCIN 0x21
6603+#define WM8750_LOUTM1 0x22
6604+#define WM8750_LOUTM2 0x23
6605+#define WM8750_ROUTM1 0x24
6606+#define WM8750_ROUTM2 0x25
6607+#define WM8750_MOUTM1 0x26
6608+#define WM8750_MOUTM2 0x27
6609+#define WM8750_LOUT2V 0x28
6610+#define WM8750_ROUT2V 0x29
6611+#define WM8750_MOUTV 0x2a
6612+
6613+#define WM8750_CACHE_REGNUM 0x2a
6614+
6615+struct wm8750_setup_data {
6616+ unsigned short i2c_address;
6617+ unsigned int mclk;
6618+};
6619+
6620+extern struct snd_soc_codec_dai wm8750_dai;
6621+extern struct snd_soc_codec_device soc_codec_dev_wm8750;
6622+
6623+#endif
6624Index: linux-2.6-pxa-new/sound/soc/codecs/wm8753.c
6625===================================================================
6626--- /dev/null
6627+++ linux-2.6-pxa-new/sound/soc/codecs/wm8753.c
6628@@ -0,0 +1,2128 @@
6629+/*
6630+ * wm8753.c -- WM8753 ALSA Soc Audio driver
6631+ *
6632+ * Copyright 2003 Wolfson Microelectronics PLC.
6633+ * Author: Liam Girdwood
6634+ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
6635+ *
6636+ * This program is free software; you can redistribute it and/or modify it
6637+ * under the terms of the GNU General Public License as published by the
6638+ * Free Software Foundation; either version 2 of the License, or (at your
6639+ * option) any later version.
6640+ *
6641+ * Notes:
6642+ * The WM8753 is a low power, high quality stereo codec with integrated PCM
6643+ * codec designed for portable digital telephony applications.
6644+ *
6645+ * Dual DAI:-
6646+ *
6647+ * This driver support 2 DAI PCM's. This makes the default PCM available for
6648+ * HiFi audio (e.g. MP3, ogg) playback/capture and the other PCM available for
6649+ * voice.
6650+ *
6651+ * Please note that the voice PCM can be connected directly to a Bluetooth
6652+ * codec or GSM modem and thus cannot be read or written to, although it is
6653+ * available to be configured with snd_hw_params(), etc and kcontrols in the
6654+ * normal alsa manner.
6655+ *
6656+ * Fast DAI switching:-
6657+ *
6658+ * The driver can now fast switch between the DAI configurations via a
6659+ * an alsa kcontrol. This allows the PCM to remain open.
6660+ *
6661+ */
6662+
6663+#include <linux/module.h>
6664+#include <linux/moduleparam.h>
6665+#include <linux/version.h>
6666+#include <linux/kernel.h>
6667+#include <linux/init.h>
6668+#include <linux/delay.h>
6669+#include <linux/pm.h>
6670+#include <linux/i2c.h>
6671+#include <linux/platform_device.h>
6672+#include <sound/driver.h>
6673+#include <sound/core.h>
6674+#include <sound/pcm.h>
6675+#include <sound/pcm_params.h>
6676+#include <sound/soc.h>
6677+#include <sound/soc-dapm.h>
6678+#include <sound/initval.h>
6679+
6680+#include "wm8753.h"
6681+
6682+#define AUDIO_NAME "wm8753"
6683+#define WM8753_VERSION "0.16"
6684+
6685+/*
6686+ * Debug
6687+ */
6688+
6689+#define WM8753_DEBUG 0
6690+
6691+#ifdef WM8753_DEBUG
6692+#define dbg(format, arg...) \
6693+ printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
6694+#else
6695+#define dbg(format, arg...) do {} while (0)
6696+#endif
6697+#define err(format, arg...) \
6698+ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
6699+#define info(format, arg...) \
6700+ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
6701+#define warn(format, arg...) \
6702+ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
6703+
6704+static int caps_charge = 2000;
6705+module_param(caps_charge, int, 0);
6706+MODULE_PARM_DESC(caps_charge, "WM8753 cap charge time (msecs)");
6707+
6708+static struct workqueue_struct *wm8753_workq = NULL;
6709+static struct work_struct wm8753_dapm_work;
6710+static void wm8753_set_dai_mode(struct snd_soc_codec *codec,
6711+ unsigned int mode);
6712+
6713+/*
6714+ * wm8753 register cache
6715+ * We can't read the WM8753 register space when we
6716+ * are using 2 wire for device control, so we cache them instead.
6717+ */
6718+static const u16 wm8753_reg[] = {
6719+ 0x0008, 0x0000, 0x000a, 0x000a,
6720+ 0x0033, 0x0000, 0x0007, 0x00ff,
6721+ 0x00ff, 0x000f, 0x000f, 0x007b,
6722+ 0x0000, 0x0032, 0x0000, 0x00c3,
6723+ 0x00c3, 0x00c0, 0x0000, 0x0000,
6724+ 0x0000, 0x0000, 0x0000, 0x0000,
6725+ 0x0000, 0x0000, 0x0000, 0x0000,
6726+ 0x0000, 0x0000, 0x0000, 0x0055,
6727+ 0x0005, 0x0050, 0x0055, 0x0050,
6728+ 0x0055, 0x0050, 0x0055, 0x0079,
6729+ 0x0079, 0x0079, 0x0079, 0x0079,
6730+ 0x0000, 0x0000, 0x0000, 0x0000,
6731+ 0x0097, 0x0097, 0x0000, 0x0004,
6732+ 0x0000, 0x0083, 0x0024, 0x01ba,
6733+ 0x0000, 0x0083, 0x0024, 0x01ba,
6734+ 0x0000, 0x0000
6735+};
6736+
6737+#define WM8753_DAIFMT \
6738+ (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_RIGHT_J | \
6739+ SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | \
6740+ SND_SOC_DAIFMT_NB_IF | SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_IB_IF)
6741+
6742+#define WM8753_DIR \
6743+ (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
6744+
6745+#define WM8753_HIFI_FSB \
6746+ (SND_SOC_FSBD(1) | SND_SOC_FSBD(2) | SND_SOC_FSBD(4) | \
6747+ SND_SOC_FSBD(8) | SND_SOC_FSBD(16))
6748+
6749+#define WM8753_HIFI_RATES \
6750+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
6751+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
6752+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
6753+
6754+#define WM8753_HIFI_BITS \
6755+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
6756+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
6757+
6758+/*
6759+ * HiFi modes
6760+ */
6761+static struct snd_soc_dai_mode wm8753_hifi_modes[] = {
6762+ /* codec frame and clock master modes */
6763+ /* 8k */
6764+ {
6765+ .fmt = WM8753_DAIFMT | SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_CBM_CFS,
6766+ .pcmfmt = WM8753_HIFI_BITS,
6767+ .pcmrate = SNDRV_PCM_RATE_8000,
6768+ .pcmdir = WM8753_DIR,
6769+ .flags = SND_SOC_DAI_BFS_DIV,
6770+ .fs = 1536,
6771+ .bfs = WM8753_HIFI_FSB,
6772+ },
6773+ {
6774+ .fmt = WM8753_DAIFMT | SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_CBM_CFS,
6775+ .pcmfmt = WM8753_HIFI_BITS,
6776+ .pcmrate = SNDRV_PCM_RATE_8000,
6777+ .pcmdir = WM8753_DIR,
6778+ .flags = SND_SOC_DAI_BFS_DIV,
6779+ .fs = 1408,
6780+ .bfs = WM8753_HIFI_FSB,
6781+ },
6782+ {
6783+ .fmt = WM8753_DAIFMT | SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_CBM_CFS,
6784+ .pcmfmt = WM8753_HIFI_BITS,
6785+ .pcmrate = SNDRV_PCM_RATE_8000,
6786+ .pcmdir = WM8753_DIR,
6787+ .flags = SND_SOC_DAI_BFS_DIV,
6788+ .fs = 2304,
6789+ .bfs = WM8753_HIFI_FSB,
6790+ },
6791+ {
6792+ .fmt = WM8753_DAIFMT | SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_CBM_CFS,
6793+ .pcmfmt = WM8753_HIFI_BITS,
6794+ .pcmrate = SNDRV_PCM_RATE_8000,
6795+ .pcmdir = WM8753_DIR,
6796+ .flags = SND_SOC_DAI_BFS_DIV,
6797+ .fs = 2112,
6798+ .bfs = WM8753_HIFI_FSB,
6799+ },
6800+ {
6801+ .fmt = WM8753_DAIFMT | SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_CBM_CFS,
6802+ .pcmfmt = WM8753_HIFI_BITS,
6803+ .pcmrate = SNDRV_PCM_RATE_8000,
6804+ .pcmdir = WM8753_DIR,
6805+ .flags = SND_SOC_DAI_BFS_DIV,
6806+ .fs = 1500,
6807+ .bfs = WM8753_HIFI_FSB,
6808+ },
6809+
6810+ /* 11.025k */
6811+ {
6812+ .fmt = WM8753_DAIFMT | SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_CBM_CFS,
6813+ .pcmfmt = WM8753_HIFI_BITS,
6814+ .pcmrate = SNDRV_PCM_RATE_11025,
6815+ .pcmdir = WM8753_DIR,
6816+ .flags = SND_SOC_DAI_BFS_DIV,
6817+ .fs = 1024,
6818+ .bfs = WM8753_HIFI_FSB,
6819+ },
6820+ {
6821+ .fmt = WM8753_DAIFMT | SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_CBM_CFS,
6822+ .pcmfmt = WM8753_HIFI_BITS,
6823+ .pcmrate = SNDRV_PCM_RATE_11025,
6824+ .pcmdir = WM8753_DIR,
6825+ .flags = SND_SOC_DAI_BFS_DIV,
6826+ .fs = 1536,
6827+ .bfs = WM8753_HIFI_FSB,
6828+ },
6829+ {
6830+ .fmt = WM8753_DAIFMT | SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_CBM_CFS,
6831+ .pcmfmt = WM8753_HIFI_BITS,
6832+ .pcmrate = SNDRV_PCM_RATE_11025,
6833+ .pcmdir = WM8753_DIR,
6834+ .flags = SND_SOC_DAI_BFS_DIV,
6835+ .fs = 1088,
6836+ .bfs = WM8753_HIFI_FSB,
6837+ },
6838+
6839+ /* 16k */
6840+ {
6841+ .fmt = WM8753_DAIFMT | SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_CBM_CFS,
6842+ .pcmfmt = WM8753_HIFI_BITS,
6843+ .pcmrate = SNDRV_PCM_RATE_16000,
6844+ .pcmdir = WM8753_DIR,
6845+ .flags = SND_SOC_DAI_BFS_DIV,
6846+ .fs = 768,
6847+ .bfs = WM8753_HIFI_FSB,
6848+ },
6849+ {
6850+ .fmt = WM8753_DAIFMT | SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_CBM_CFS,
6851+ .pcmfmt= WM8753_HIFI_BITS,
6852+ .pcmrate = SNDRV_PCM_RATE_16000,
6853+ .pcmdir = WM8753_DIR,
6854+ .flags = SND_SOC_DAI_BFS_DIV,
6855+ .fs = 1152,
6856+ .bfs = WM8753_HIFI_FSB,
6857+ },
6858+ {
6859+ .fmt = WM8753_DAIFMT | SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_CBM_CFS,
6860+ .pcmfmt = WM8753_HIFI_BITS,
6861+ .pcmrate = SNDRV_PCM_RATE_16000,
6862+ .pcmdir = WM8753_DIR,
6863+ .flags = SND_SOC_DAI_BFS_DIV,
6864+ .fs = 750,
6865+ .bfs = WM8753_HIFI_FSB,
6866+ },
6867+
6868+ /* 22.05k */
6869+ {
6870+ .fmt = WM8753_DAIFMT | SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_CBM_CFS,
6871+ .pcmfmt = WM8753_HIFI_BITS,
6872+ .pcmrate = SNDRV_PCM_RATE_22050,
6873+ .pcmdir = WM8753_DIR,
6874+ .flags = SND_SOC_DAI_BFS_DIV,
6875+ .fs = 512,
6876+ .bfs = WM8753_HIFI_FSB,
6877+ },
6878+ {
6879+ .fmt = WM8753_DAIFMT | SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_CBM_CFS,
6880+ .pcmfmt = WM8753_HIFI_BITS,
6881+ .pcmrate = SNDRV_PCM_RATE_22050,
6882+ .pcmdir = WM8753_DIR,
6883+ .flags = SND_SOC_DAI_BFS_DIV,
6884+ .fs = 768,
6885+ .bfs = WM8753_HIFI_FSB,
6886+ },
6887+ {
6888+ .fmt = WM8753_DAIFMT | SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_CBM_CFS,
6889+ .pcmfmt = WM8753_HIFI_BITS,
6890+ .pcmrate = SNDRV_PCM_RATE_22050,
6891+ .pcmdir = WM8753_DIR,
6892+ .flags = SND_SOC_DAI_BFS_DIV,
6893+ .fs = 544,
6894+ .bfs = WM8753_HIFI_FSB,
6895+ },
6896+
6897+ /* 32k */
6898+ {
6899+ .fmt = WM8753_DAIFMT | SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_CBM_CFS,
6900+ .pcmfmt = WM8753_HIFI_BITS,
6901+ .pcmrate = SNDRV_PCM_RATE_32000,
6902+ .pcmdir = WM8753_DIR,
6903+ .flags = SND_SOC_DAI_BFS_DIV,
6904+ .fs = 384,
6905+ .bfs = WM8753_HIFI_FSB,
6906+ },
6907+ {
6908+ .fmt = WM8753_DAIFMT | SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_CBM_CFS,
6909+ .pcmfmt = WM8753_HIFI_BITS,
6910+ .pcmrate = SNDRV_PCM_RATE_32000,
6911+ .pcmdir = WM8753_DIR,
6912+ .flags = SND_SOC_DAI_BFS_DIV,
6913+ .fs = 576,
6914+ .bfs = WM8753_HIFI_FSB,
6915+ },
6916+ {
6917+ .fmt = WM8753_DAIFMT | SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_CBM_CFS,
6918+ .pcmfmt = WM8753_HIFI_BITS,
6919+ .pcmrate = SNDRV_PCM_RATE_32000,
6920+ .pcmdir = WM8753_DIR,
6921+ .flags = SND_SOC_DAI_BFS_DIV,
6922+ .fs = 375,
6923+ .bfs = WM8753_HIFI_FSB,
6924+ },
6925+
6926+ /* 44.1k & 48k */
6927+ {
6928+ .fmt = WM8753_DAIFMT | SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_CBM_CFS,
6929+ .pcmfmt = WM8753_HIFI_BITS,
6930+ .pcmrate = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
6931+ .pcmdir = WM8753_DIR,
6932+ .flags = SND_SOC_DAI_BFS_DIV,
6933+ .fs = 256,
6934+ .bfs = WM8753_HIFI_FSB,
6935+ },
6936+ {
6937+ .fmt = WM8753_DAIFMT | SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_CBM_CFS,
6938+ .pcmfmt = WM8753_HIFI_BITS,
6939+ .pcmrate = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
6940+ .pcmdir = WM8753_DIR,
6941+ .flags = SND_SOC_DAI_BFS_DIV,
6942+ .fs = 384,
6943+ .bfs = WM8753_HIFI_FSB,
6944+ },
6945+ {
6946+ .fmt = WM8753_DAIFMT | SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_CBM_CFS,
6947+ .pcmfmt = WM8753_HIFI_BITS,
6948+ .pcmrate = SNDRV_PCM_RATE_48000,
6949+ .pcmdir = WM8753_DIR,
6950+ .flags = SND_SOC_DAI_BFS_DIV,
6951+ .fs = 250,
6952+ .bfs = WM8753_HIFI_FSB,
6953+ },
6954+ {
6955+ .fmt = WM8753_DAIFMT | SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_CBM_CFS,
6956+ .pcmfmt = WM8753_HIFI_BITS,
6957+ .pcmrate = SNDRV_PCM_RATE_44100,
6958+ .pcmdir = WM8753_DIR,
6959+ .flags = SND_SOC_DAI_BFS_DIV,
6960+ .fs = 272,
6961+ .bfs = WM8753_HIFI_FSB,
6962+ },
6963+
6964+ /* 88.2k & 96k */
6965+ {
6966+ .fmt = WM8753_DAIFMT | SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_CBM_CFS,
6967+ .pcmfmt = WM8753_HIFI_BITS,
6968+ .pcmrate = SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000,
6969+ .pcmdir = WM8753_DIR,
6970+ .flags = SND_SOC_DAI_BFS_DIV,
6971+ .fs = 128,
6972+ .bfs = WM8753_HIFI_FSB,
6973+ },
6974+ {
6975+ .fmt = WM8753_DAIFMT | SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_CBM_CFS,
6976+ .pcmfmt = WM8753_HIFI_BITS,
6977+ .pcmrate = SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000,
6978+ .pcmdir = WM8753_DIR,
6979+ .flags = SND_SOC_DAI_BFS_DIV,
6980+ .fs = 192,
6981+ .bfs = WM8753_HIFI_FSB,
6982+ },
6983+ {
6984+ .fmt = WM8753_DAIFMT | SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_CBM_CFS,
6985+ .pcmfmt = WM8753_HIFI_BITS,
6986+ .pcmrate = SNDRV_PCM_RATE_88200,
6987+ .pcmdir = WM8753_DIR,
6988+ .flags = SND_SOC_DAI_BFS_DIV,
6989+ .fs = 136,
6990+ .bfs = WM8753_HIFI_FSB,
6991+ },
6992+ {
6993+ .fmt = WM8753_DAIFMT | SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_CBM_CFS,
6994+ .pcmfmt = WM8753_HIFI_BITS,
6995+ .pcmrate = SNDRV_PCM_RATE_96000,
6996+ .pcmdir = WM8753_DIR,
6997+ .flags = SND_SOC_DAI_BFS_DIV,
6998+ .fs = 125,
6999+ .bfs = WM8753_HIFI_FSB,
7000+ },
7001+
7002+ /* codec frame and clock slave modes */
7003+ {
7004+ .fmt = WM8753_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
7005+ .pcmfmt = WM8753_HIFI_BITS,
7006+ .pcmrate = WM8753_HIFI_RATES,
7007+ .pcmdir = WM8753_DIR,
7008+ .flags = SND_SOC_DAI_BFS_DIV,
7009+ .fs = SND_SOC_FS_ALL,
7010+ .bfs = SND_SOC_FSB_ALL,
7011+ },
7012+};
7013+
7014+#define WM8753_VOICE_FSB \
7015+ (SND_SOC_FSBD(1) | SND_SOC_FSBD(2) | SND_SOC_FSBD(4) | \
7016+ SND_SOC_FSBD(8) | SND_SOC_FSBD(16))
7017+
7018+#define WM8753_VOICE_RATES \
7019+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
7020+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
7021+ SNDRV_PCM_RATE_48000)
7022+
7023+#define WM8753_VOICE_BITS \
7024+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
7025+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
7026+
7027+/*
7028+ * Voice modes
7029+ */
7030+static struct snd_soc_dai_mode wm8753_voice_modes[] = {
7031+
7032+ /* master modes */
7033+ {
7034+ .fmt = WM8753_DAIFMT | SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_CBM_CFS,
7035+ .pcmfmt = WM8753_VOICE_BITS,
7036+ .pcmrate = WM8753_VOICE_RATES,
7037+ .pcmdir = WM8753_DIR,
7038+ .flags = SND_SOC_DAI_BFS_DIV,
7039+ .fs = 256,
7040+ .bfs = WM8753_VOICE_FSB,
7041+ },
7042+ {
7043+ .fmt = WM8753_DAIFMT | SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_CBM_CFS,
7044+ .pcmfmt = WM8753_VOICE_BITS,
7045+ .pcmrate = WM8753_VOICE_RATES,
7046+ .pcmdir = WM8753_DIR,
7047+ .flags = SND_SOC_DAI_BFS_DIV,
7048+ .fs = 384,
7049+ .bfs = WM8753_VOICE_FSB,
7050+ },
7051+
7052+ /* slave modes */
7053+ {
7054+ .fmt = WM8753_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
7055+ .pcmfmt = WM8753_VOICE_BITS,
7056+ .pcmrate = WM8753_VOICE_RATES,
7057+ .pcmdir = WM8753_DIR,
7058+ .flags = SND_SOC_DAI_BFS_DIV,
7059+ .fs = SND_SOC_FS_ALL,
7060+ .bfs = SND_SOC_FSB_ALL,
7061+ },
7062+};
7063+
7064+
7065+/*
7066+ * Mode 4
7067+ */
7068+static struct snd_soc_dai_mode wm8753_mixed_modes[] = {
7069+ /* slave modes */
7070+ {
7071+ .fmt = WM8753_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
7072+ .pcmfmt = WM8753_HIFI_BITS,
7073+ .pcmrate = WM8753_HIFI_RATES,
7074+ .pcmdir = WM8753_DIR,
7075+ .flags = SND_SOC_DAI_BFS_DIV,
7076+ .fs = SND_SOC_FS_ALL,
7077+ .bfs = SND_SOC_FSB_ALL,
7078+ },
7079+};
7080+
7081+/*
7082+ * read wm8753 register cache
7083+ */
7084+static inline unsigned int wm8753_read_reg_cache(struct snd_soc_codec *codec,
7085+ unsigned int reg)
7086+{
7087+ u16 *cache = codec->reg_cache;
7088+ if (reg < 1 || reg > (ARRAY_SIZE(wm8753_reg) + 1))
7089+ return -1;
7090+ return cache[reg - 1];
7091+}
7092+
7093+/*
7094+ * write wm8753 register cache
7095+ */
7096+static inline void wm8753_write_reg_cache(struct snd_soc_codec *codec,
7097+ unsigned int reg, unsigned int value)
7098+{
7099+ u16 *cache = codec->reg_cache;
7100+ if (reg < 1 || reg > 0x3f)
7101+ return;
7102+ cache[reg - 1] = value;
7103+}
7104+
7105+/*
7106+ * write to the WM8753 register space
7107+ */
7108+static int wm8753_write(struct snd_soc_codec *codec, unsigned int reg,
7109+ unsigned int value)
7110+{
7111+ u8 data[2];
7112+
7113+ /* data is
7114+ * D15..D9 WM8753 register offset
7115+ * D8...D0 register data
7116+ */
7117+ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
7118+ data[1] = value & 0x00ff;
7119+
7120+ wm8753_write_reg_cache (codec, reg, value);
7121+ if (codec->hw_write(codec->control_data, data, 2) == 2)
7122+ return 0;
7123+ else
7124+ return -EIO;
7125+}
7126+
7127+#define wm8753_reset(c) wm8753_write(c, WM8753_RESET, 0)
7128+
7129+/*
7130+ * WM8753 Controls
7131+ */
7132+static const char *wm8753_base[] = {"Linear Control", "Adaptive Boost"};
7133+static const char *wm8753_base_filter[] =
7134+ {"130Hz @ 48kHz", "200Hz @ 48kHz", "100Hz @ 16kHz", "400Hz @ 48kHz",
7135+ "100Hz @ 8kHz", "200Hz @ 8kHz"};
7136+static const char *wm8753_treble[] = {"8kHz", "4kHz"};
7137+static const char *wm8753_alc_func[] = {"Off", "Right", "Left", "Stereo"};
7138+static const char *wm8753_ng_type[] = {"Constant PGA Gain", "Mute ADC Output"};
7139+static const char *wm8753_3d_func[] = {"Capture", "Playback"};
7140+static const char *wm8753_3d_uc[] = {"2.2kHz", "1.5kHz"};
7141+static const char *wm8753_3d_lc[] = {"200Hz", "500Hz"};
7142+static const char *wm8753_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz"};
7143+static const char *wm8753_mono_mix[] = {"Stereo", "Left", "Right", "Mono"};
7144+static const char *wm8753_dac_phase[] = {"Non Inverted", "Inverted"};
7145+static const char *wm8753_line_mix[] = {"Line 1 + 2", "Line 1 - 2",
7146+ "Line 1", "Line 2"};
7147+static const char *wm8753_mono_mux[] = {"Line Mix", "Rx Mix"};
7148+static const char *wm8753_right_mux[] = {"Line 2", "Rx Mix"};
7149+static const char *wm8753_left_mux[] = {"Line 1", "Rx Mix"};
7150+static const char *wm8753_rxmsel[] = {"RXP - RXN", "RXP + RXN", "RXP", "RXN"};
7151+static const char *wm8753_sidetone_mux[] = {"Left PGA", "Mic 1", "Mic 2",
7152+ "Right PGA"};
7153+static const char *wm8753_mono2_src[] = {"Inverted Mono 1", "Left", "Right",
7154+ "Left + Right"};
7155+static const char *wm8753_out3[] = {"VREF", "ROUT2", "Left + Right"};
7156+static const char *wm8753_out4[] = {"VREF", "Capture ST", "LOUT2"};
7157+static const char *wm8753_radcsel[] = {"PGA", "Line or RXP-RXN", "Sidetone"};
7158+static const char *wm8753_ladcsel[] = {"PGA", "Line or RXP-RXN", "Line"};
7159+static const char *wm8753_mono_adc[] = {"Stereo", "Analogue Mix Left",
7160+ "Analogue Mix Right", "Digital Mono Mix"};
7161+static const char *wm8753_adc_hp[] = {"3.4Hz @ 48kHz", "82Hz @ 16k",
7162+ "82Hz @ 8kHz", "170Hz @ 8kHz"};
7163+static const char *wm8753_adc_filter[] = {"HiFi", "Voice"};
7164+static const char *wm8753_mic_sel[] = {"Mic 1", "Mic 2", "Mic 3"};
7165+static const char *wm8753_dai_mode[] = {"DAI 0", "DAI 1", "DAI 2", "DAI 3"};
7166+
7167+static const struct soc_enum wm8753_enum[] = {
7168+SOC_ENUM_SINGLE(WM8753_BASS, 7, 2, wm8753_base), // 0
7169+SOC_ENUM_SINGLE(WM8753_BASS, 4, 6, wm8753_base_filter), // 1
7170+SOC_ENUM_SINGLE(WM8753_TREBLE, 6, 2, wm8753_treble), // 2
7171+SOC_ENUM_SINGLE(WM8753_ALC1, 7, 4, wm8753_alc_func), // 3
7172+SOC_ENUM_SINGLE(WM8753_NGATE, 1, 2, wm8753_ng_type), // 4
7173+SOC_ENUM_SINGLE(WM8753_3D, 7, 2, wm8753_3d_func), // 5
7174+SOC_ENUM_SINGLE(WM8753_3D, 6, 2, wm8753_3d_uc), // 6
7175+SOC_ENUM_SINGLE(WM8753_3D, 5, 2, wm8753_3d_lc), // 7
7176+SOC_ENUM_SINGLE(WM8753_DAC, 1, 4, wm8753_deemp), // 8
7177+SOC_ENUM_SINGLE(WM8753_DAC, 4, 4, wm8753_mono_mix), // 9
7178+SOC_ENUM_SINGLE(WM8753_DAC, 6, 2, wm8753_dac_phase), // 10
7179+SOC_ENUM_SINGLE(WM8753_INCTL1, 3, 4, wm8753_line_mix), // 11
7180+SOC_ENUM_SINGLE(WM8753_INCTL1, 2, 2, wm8753_mono_mux), // 12
7181+SOC_ENUM_SINGLE(WM8753_INCTL1, 1, 2, wm8753_right_mux), // 13
7182+SOC_ENUM_SINGLE(WM8753_INCTL1, 0, 2, wm8753_left_mux), // 14
7183+SOC_ENUM_SINGLE(WM8753_INCTL2, 6, 4, wm8753_rxmsel), // 15
7184+SOC_ENUM_SINGLE(WM8753_INCTL2, 4, 4, wm8753_sidetone_mux),// 16
7185+SOC_ENUM_SINGLE(WM8753_OUTCTL, 7, 4, wm8753_mono2_src), // 17
7186+SOC_ENUM_SINGLE(WM8753_OUTCTL, 0, 3, wm8753_out3), // 18
7187+SOC_ENUM_SINGLE(WM8753_ADCTL2, 7, 3, wm8753_out4), // 19
7188+SOC_ENUM_SINGLE(WM8753_ADCIN, 2, 3, wm8753_radcsel), // 20
7189+SOC_ENUM_SINGLE(WM8753_ADCIN, 0, 3, wm8753_ladcsel), // 21
7190+SOC_ENUM_SINGLE(WM8753_ADCIN, 4, 4, wm8753_mono_adc), // 22
7191+SOC_ENUM_SINGLE(WM8753_ADC, 2, 4, wm8753_adc_hp), // 23
7192+SOC_ENUM_SINGLE(WM8753_ADC, 4, 2, wm8753_adc_filter), // 24
7193+SOC_ENUM_SINGLE(WM8753_MICBIAS, 6, 3, wm8753_mic_sel), // 25
7194+SOC_ENUM_SINGLE(WM8753_IOCTL, 2, 4, wm8753_dai_mode), // 26
7195+};
7196+
7197+
7198+static int wm8753_get_dai(struct snd_kcontrol *kcontrol,
7199+ struct snd_ctl_elem_value *ucontrol)
7200+{
7201+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
7202+ int mode = wm8753_read_reg_cache(codec, WM8753_IOCTL);
7203+
7204+ ucontrol->value.integer.value[0] = (mode & 0xc) >> 2;
7205+ return 0;
7206+}
7207+
7208+static int wm8753_set_dai(struct snd_kcontrol *kcontrol,
7209+ struct snd_ctl_elem_value *ucontrol)
7210+{
7211+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
7212+ int mode = wm8753_read_reg_cache(codec, WM8753_IOCTL);
7213+
7214+ if (((mode &0xc) >> 2) == ucontrol->value.integer.value[0])
7215+ return 0;
7216+
7217+ mode &= 0xfff3;
7218+ mode |= (ucontrol->value.integer.value[0] << 2);
7219+
7220+ wm8753_write(codec, WM8753_IOCTL, mode);
7221+ wm8753_set_dai_mode(codec, ucontrol->value.integer.value[0]);
7222+ return 1;
7223+}
7224+
7225+static const struct snd_kcontrol_new wm8753_snd_controls[] = {
7226+SOC_DOUBLE_R("PCM Volume", WM8753_LDAC, WM8753_RDAC, 0, 255, 0),
7227+
7228+SOC_DOUBLE_R("ADC Capture Volume", WM8753_LADC, WM8753_RADC, 0, 63, 0),
7229+SOC_DOUBLE_R("ADC Capture Switch", WM8753_LINVOL, WM8753_RINVOL, 7, 1, 0),
7230+SOC_DOUBLE_R("ADC Capture ZC Switch", WM8753_LINVOL, WM8753_RINVOL, 6, 1, 0),
7231+
7232+SOC_DOUBLE_R("Headphone Playback Volume", WM8753_LOUT1V, WM8753_ROUT1V, 0, 127, 0),
7233+SOC_DOUBLE_R("Speaker Playback Volume", WM8753_LOUT2V, WM8753_ROUT2V, 0, 127, 0),
7234+
7235+SOC_SINGLE("Mono Playback Volume", WM8753_MOUTV, 0, 127, 0),
7236+
7237+SOC_DOUBLE_R("Bypass Playback Volume", WM8753_LOUTM1, WM8753_ROUTM1, 4, 7, 1),
7238+SOC_DOUBLE_R("Sidetone Playback Volume", WM8753_LOUTM2, WM8753_ROUTM2, 4, 7, 1),
7239+SOC_DOUBLE_R("Voice Playback Volume", WM8753_LOUTM2, WM8753_ROUTM2, 0, 7, 1),
7240+
7241+SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8753_LOUT1V, WM8753_ROUT1V, 7, 1, 0),
7242+SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8753_LOUT2V, WM8753_ROUT2V, 7, 1, 0),
7243+
7244+SOC_SINGLE("Mono Bypass Playback Volume", WM8753_MOUTM1, 4, 7, 1),
7245+SOC_SINGLE("Mono Sidetone Playback Volume", WM8753_MOUTM2, 4, 7, 1),
7246+SOC_SINGLE("Mono Voice Playback Volume", WM8753_MOUTM2, 4, 7, 1),
7247+SOC_SINGLE("Mono Playback ZC Switch", WM8753_MOUTV, 7, 1, 0),
7248+
7249+SOC_ENUM("Bass Boost", wm8753_enum[0]),
7250+SOC_ENUM("Bass Filter", wm8753_enum[1]),
7251+SOC_SINGLE("Bass Volume", WM8753_BASS, 0, 7, 1),
7252+
7253+SOC_SINGLE("Treble Volume", WM8753_TREBLE, 0, 7, 0),
7254+SOC_ENUM("Treble Cut-off", wm8753_enum[2]),
7255+
7256+SOC_DOUBLE("Sidetone Capture Volume", WM8753_RECMIX1, 0, 4, 7, 1),
7257+SOC_SINGLE("Voice Sidetone Capture Volume", WM8753_RECMIX2, 0, 7, 1),
7258+
7259+SOC_DOUBLE_R("Capture Volume", WM8753_LINVOL, WM8753_RINVOL, 0, 63, 0),
7260+SOC_DOUBLE_R("Capture ZC Switch", WM8753_LINVOL, WM8753_RINVOL, 6, 1, 0),
7261+SOC_DOUBLE_R("Capture Switch", WM8753_LINVOL, WM8753_RINVOL, 7, 1, 0),
7262+
7263+SOC_ENUM("Capture Filter Select", wm8753_enum[23]),
7264+SOC_ENUM("Capture Filter Cut-off", wm8753_enum[24]),
7265+SOC_SINGLE("Capture Filter Switch", WM8753_ADC, 0, 1, 1),
7266+
7267+SOC_SINGLE("ALC Capture Target Volume", WM8753_ALC1, 0, 7, 0),
7268+SOC_SINGLE("ALC Capture Max Volume", WM8753_ALC1, 4, 7, 0),
7269+SOC_ENUM("ALC Capture Function", wm8753_enum[3]),
7270+SOC_SINGLE("ALC Capture ZC Switch", WM8753_ALC2, 8, 1, 0),
7271+SOC_SINGLE("ALC Capture Hold Time", WM8753_ALC2, 0, 15, 1),
7272+SOC_SINGLE("ALC Capture Decay Time", WM8753_ALC3, 4, 15, 1),
7273+SOC_SINGLE("ALC Capture Attack Time", WM8753_ALC3, 0, 15, 0),
7274+SOC_SINGLE("ALC Capture NG Threshold", WM8753_NGATE, 3, 31, 0),
7275+SOC_ENUM("ALC Capture NG Type", wm8753_enum[4]),
7276+SOC_SINGLE("ALC Capture NG Switch", WM8753_NGATE, 0, 1, 0),
7277+
7278+SOC_ENUM("3D Function", wm8753_enum[5]),
7279+SOC_ENUM("3D Upper Cut-off", wm8753_enum[6]),
7280+SOC_ENUM("3D Lower Cut-off", wm8753_enum[7]),
7281+SOC_SINGLE("3D Volume", WM8753_3D, 1, 15, 0),
7282+SOC_SINGLE("3D Switch", WM8753_3D, 0, 1, 0),
7283+
7284+SOC_SINGLE("Capture 6dB Attenuate", WM8753_ADCTL1, 2, 1, 0),
7285+SOC_SINGLE("Playback 6dB Attenuate", WM8753_ADCTL1, 1, 1, 0),
7286+
7287+SOC_ENUM("De-emphasis", wm8753_enum[8]),
7288+SOC_ENUM("Playback Mono Mix", wm8753_enum[9]),
7289+SOC_ENUM("Playback Phase", wm8753_enum[10]),
7290+
7291+SOC_SINGLE("Mic2 Capture Volume", WM8753_INCTL1, 7, 3, 0),
7292+SOC_SINGLE("Mic1 Capture Volume", WM8753_INCTL1, 5, 3, 0),
7293+
7294+SOC_ENUM_EXT("DAI Mode", wm8753_enum[26], wm8753_get_dai, wm8753_set_dai),
7295+};
7296+
7297+/* add non dapm controls */
7298+static int wm8753_add_controls(struct snd_soc_codec *codec)
7299+{
7300+ int err, i;
7301+
7302+ for (i = 0; i < ARRAY_SIZE(wm8753_snd_controls); i++) {
7303+ err = snd_ctl_add(codec->card,
7304+ snd_soc_cnew(&wm8753_snd_controls[i],codec, NULL));
7305+ if (err < 0)
7306+ return err;
7307+ }
7308+ return 0;
7309+}
7310+
7311+/*
7312+ * _DAPM_ Controls
7313+ */
7314+
7315+/* Left Mixer */
7316+static const struct snd_kcontrol_new wm8753_left_mixer_controls[] = {
7317+SOC_DAPM_SINGLE("Voice Playback Switch", WM8753_LOUTM2, 8, 1, 0),
7318+SOC_DAPM_SINGLE("Sidetone Playback Switch", WM8753_LOUTM2, 7, 1, 0),
7319+SOC_DAPM_SINGLE("Left Playback Switch", WM8753_LOUTM1, 8, 1, 0),
7320+SOC_DAPM_SINGLE("Bypass Playback Switch", WM8753_LOUTM1, 7, 1, 0),
7321+};
7322+
7323+/* Right mixer */
7324+static const struct snd_kcontrol_new wm8753_right_mixer_controls[] = {
7325+SOC_DAPM_SINGLE("Voice Playback Switch", WM8753_ROUTM2, 8, 1, 0),
7326+SOC_DAPM_SINGLE("Sidetone Playback Switch", WM8753_ROUTM2, 7, 1, 0),
7327+SOC_DAPM_SINGLE("Right Playback Switch", WM8753_ROUTM1, 8, 1, 0),
7328+SOC_DAPM_SINGLE("Bypass Playback Switch", WM8753_ROUTM1, 7, 1, 0),
7329+};
7330+
7331+/* Mono mixer */
7332+static const struct snd_kcontrol_new wm8753_mono_mixer_controls[] = {
7333+SOC_DAPM_SINGLE("Left Playback Switch", WM8753_MOUTM1, 8, 1, 0),
7334+SOC_DAPM_SINGLE("Right Playback Switch", WM8753_MOUTM2, 8, 1, 0),
7335+SOC_DAPM_SINGLE("Voice Playback Switch", WM8753_MOUTM2, 3, 1, 0),
7336+SOC_DAPM_SINGLE("Sidetone Playback Switch", WM8753_MOUTM2, 7, 1, 0),
7337+SOC_DAPM_SINGLE("Bypass Playback Switch", WM8753_MOUTM1, 7, 1, 0),
7338+};
7339+
7340+/* Mono 2 Mux */
7341+static const struct snd_kcontrol_new wm8753_mono2_controls =
7342+SOC_DAPM_ENUM("Route", wm8753_enum[17]);
7343+
7344+/* Out 3 Mux */
7345+static const struct snd_kcontrol_new wm8753_out3_controls =
7346+SOC_DAPM_ENUM("Route", wm8753_enum[18]);
7347+
7348+/* Out 4 Mux */
7349+static const struct snd_kcontrol_new wm8753_out4_controls =
7350+SOC_DAPM_ENUM("Route", wm8753_enum[19]);
7351+
7352+/* ADC Mono Mix */
7353+static const struct snd_kcontrol_new wm8753_adc_mono_controls =
7354+SOC_DAPM_ENUM("Route", wm8753_enum[22]);
7355+
7356+/* Record mixer */
7357+static const struct snd_kcontrol_new wm8753_record_mixer_controls[] = {
7358+SOC_DAPM_SINGLE("Voice Capture Switch", WM8753_RECMIX2, 3, 1, 0),
7359+SOC_DAPM_SINGLE("Left Capture Switch", WM8753_RECMIX1, 3, 1, 0),
7360+SOC_DAPM_SINGLE("Right Capture Switch", WM8753_RECMIX1, 7, 1, 0),
7361+};
7362+
7363+/* Left ADC mux */
7364+static const struct snd_kcontrol_new wm8753_adc_left_controls =
7365+SOC_DAPM_ENUM("Route", wm8753_enum[21]);
7366+
7367+/* Right ADC mux */
7368+static const struct snd_kcontrol_new wm8753_adc_right_controls =
7369+SOC_DAPM_ENUM("Route", wm8753_enum[20]);
7370+
7371+/* MIC mux */
7372+static const struct snd_kcontrol_new wm8753_mic_mux_controls =
7373+SOC_DAPM_ENUM("Route", wm8753_enum[16]);
7374+
7375+/* ALC mixer */
7376+static const struct snd_kcontrol_new wm8753_alc_mixer_controls[] = {
7377+SOC_DAPM_SINGLE("Line Capture Switch", WM8753_INCTL2, 3, 1, 0),
7378+SOC_DAPM_SINGLE("Mic2 Capture Switch", WM8753_INCTL2, 2, 1, 0),
7379+SOC_DAPM_SINGLE("Mic1 Capture Switch", WM8753_INCTL2, 1, 1, 0),
7380+SOC_DAPM_SINGLE("Rx Capture Switch", WM8753_INCTL2, 0, 1, 0),
7381+};
7382+
7383+/* Left Line mux */
7384+static const struct snd_kcontrol_new wm8753_line_left_controls =
7385+SOC_DAPM_ENUM("Route", wm8753_enum[14]);
7386+
7387+/* Right Line mux */
7388+static const struct snd_kcontrol_new wm8753_line_right_controls =
7389+SOC_DAPM_ENUM("Route", wm8753_enum[13]);
7390+
7391+/* Mono Line mux */
7392+static const struct snd_kcontrol_new wm8753_line_mono_controls =
7393+SOC_DAPM_ENUM("Route", wm8753_enum[12]);
7394+
7395+/* Line mux and mixer */
7396+static const struct snd_kcontrol_new wm8753_line_mux_mix_controls =
7397+SOC_DAPM_ENUM("Route", wm8753_enum[11]);
7398+
7399+/* Rx mux and mixer */
7400+static const struct snd_kcontrol_new wm8753_rx_mux_mix_controls =
7401+SOC_DAPM_ENUM("Route", wm8753_enum[15]);
7402+
7403+/* Mic Selector Mux */
7404+static const struct snd_kcontrol_new wm8753_mic_sel_mux_controls =
7405+SOC_DAPM_ENUM("Route", wm8753_enum[25]);
7406+
7407+static const struct snd_soc_dapm_widget wm8753_dapm_widgets[] = {
7408+SND_SOC_DAPM_MICBIAS("Mic Bias", WM8753_PWR1, 5, 0),
7409+SND_SOC_DAPM_MIXER("Left Mixer", WM8753_PWR4, 0, 0,
7410+ &wm8753_left_mixer_controls[0], ARRAY_SIZE(wm8753_left_mixer_controls)),
7411+SND_SOC_DAPM_PGA("Left Out 1", WM8753_PWR3, 8, 0, NULL, 0),
7412+SND_SOC_DAPM_PGA("Left Out 2", WM8753_PWR3, 6, 0, NULL, 0),
7413+SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback", WM8753_PWR1, 3, 0),
7414+SND_SOC_DAPM_OUTPUT("LOUT1"),
7415+SND_SOC_DAPM_OUTPUT("LOUT2"),
7416+SND_SOC_DAPM_MIXER("Right Mixer", WM8753_PWR4, 1, 0,
7417+ &wm8753_right_mixer_controls[0], ARRAY_SIZE(wm8753_right_mixer_controls)),
7418+SND_SOC_DAPM_PGA("Right Out 1", WM8753_PWR3, 7, 0, NULL, 0),
7419+SND_SOC_DAPM_PGA("Right Out 2", WM8753_PWR3, 5, 0, NULL, 0),
7420+SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback", WM8753_PWR1, 2, 0),
7421+SND_SOC_DAPM_OUTPUT("ROUT1"),
7422+SND_SOC_DAPM_OUTPUT("ROUT2"),
7423+SND_SOC_DAPM_MIXER("Mono Mixer", WM8753_PWR4, 2, 0,
7424+ &wm8753_mono_mixer_controls[0], ARRAY_SIZE(wm8753_mono_mixer_controls)),
7425+SND_SOC_DAPM_PGA("Mono Out 1", WM8753_PWR3, 2, 0, NULL, 0),
7426+SND_SOC_DAPM_PGA("Mono Out 2", WM8753_PWR3, 1, 0, NULL, 0),
7427+SND_SOC_DAPM_DAC("Voice DAC", "Voice Playback", WM8753_PWR1, 4, 0),
7428+SND_SOC_DAPM_OUTPUT("MONO1"),
7429+SND_SOC_DAPM_MUX("Mono 2 Mux", SND_SOC_NOPM, 0, 0, &wm8753_mono2_controls),
7430+SND_SOC_DAPM_OUTPUT("MONO2"),
7431+SND_SOC_DAPM_MIXER("Out3 Left + Right", -1, 0, 0, NULL, 0),
7432+SND_SOC_DAPM_MUX("Out3 Mux", SND_SOC_NOPM, 0, 0, &wm8753_out3_controls),
7433+SND_SOC_DAPM_PGA("Out 3", WM8753_PWR3, 4, 0, NULL, 0),
7434+SND_SOC_DAPM_OUTPUT("OUT3"),
7435+SND_SOC_DAPM_MUX("Out4 Mux", SND_SOC_NOPM, 0, 0, &wm8753_out4_controls),
7436+SND_SOC_DAPM_PGA("Out 4", WM8753_PWR3, 3, 0, NULL, 0),
7437+SND_SOC_DAPM_OUTPUT("OUT4"),
7438+SND_SOC_DAPM_MIXER("Playback Mixer", WM8753_PWR4, 3, 0,
7439+ &wm8753_record_mixer_controls[0],
7440+ ARRAY_SIZE(wm8753_record_mixer_controls)),
7441+SND_SOC_DAPM_ADC("Left ADC", "Left Voice Capture", WM8753_PWR2, 3, 0),
7442+SND_SOC_DAPM_ADC("Right ADC", "Right Voice Capture", WM8753_PWR2, 2, 0),
7443+SND_SOC_DAPM_MUX("Capture Left Mixer", SND_SOC_NOPM, 0, 0,
7444+ &wm8753_adc_mono_controls),
7445+SND_SOC_DAPM_MUX("Capture Right Mixer", SND_SOC_NOPM, 0, 0,
7446+ &wm8753_adc_mono_controls),
7447+SND_SOC_DAPM_MUX("Capture Left Mux", SND_SOC_NOPM, 0, 0,
7448+ &wm8753_adc_left_controls),
7449+SND_SOC_DAPM_MUX("Capture Right Mux", SND_SOC_NOPM, 0, 0,
7450+ &wm8753_adc_right_controls),
7451+SND_SOC_DAPM_MUX("Mic Sidetone Mux", SND_SOC_NOPM, 0, 0,
7452+ &wm8753_mic_mux_controls),
7453+SND_SOC_DAPM_PGA("Left Capture Volume", WM8753_PWR2, 5, 0, NULL, 0),
7454+SND_SOC_DAPM_PGA("Right Capture Volume", WM8753_PWR2, 4, 0, NULL, 0),
7455+SND_SOC_DAPM_MIXER("ALC Mixer", WM8753_PWR2, 6, 0,
7456+ &wm8753_alc_mixer_controls[0], ARRAY_SIZE(wm8753_alc_mixer_controls)),
7457+SND_SOC_DAPM_MUX("Line Left Mux", SND_SOC_NOPM, 0, 0,
7458+ &wm8753_line_left_controls),
7459+SND_SOC_DAPM_MUX("Line Right Mux", SND_SOC_NOPM, 0, 0,
7460+ &wm8753_line_right_controls),
7461+SND_SOC_DAPM_MUX("Line Mono Mux", SND_SOC_NOPM, 0, 0,
7462+ &wm8753_line_mono_controls),
7463+SND_SOC_DAPM_MUX("Line Mixer", SND_SOC_NOPM, 0, 0,
7464+ &wm8753_line_mux_mix_controls),
7465+SND_SOC_DAPM_MUX("Rx Mixer", SND_SOC_NOPM, 0, 0,
7466+ &wm8753_rx_mux_mix_controls),
7467+SND_SOC_DAPM_PGA("Mic 1 Volume", WM8753_PWR2, 8, 0, NULL, 0),
7468+SND_SOC_DAPM_PGA("Mic 2 Volume", WM8753_PWR2, 7, 0, NULL, 0),
7469+SND_SOC_DAPM_MUX("Mic Selection Mux", SND_SOC_NOPM, 0, 0,
7470+ &wm8753_mic_sel_mux_controls),
7471+SND_SOC_DAPM_INPUT("LINE1"),
7472+SND_SOC_DAPM_INPUT("LINE2"),
7473+SND_SOC_DAPM_INPUT("RXP"),
7474+SND_SOC_DAPM_INPUT("RXN"),
7475+SND_SOC_DAPM_INPUT("ACIN"),
7476+SND_SOC_DAPM_INPUT("ACOP"),
7477+SND_SOC_DAPM_INPUT("MIC1N"),
7478+SND_SOC_DAPM_INPUT("MIC1"),
7479+SND_SOC_DAPM_INPUT("MIC2N"),
7480+SND_SOC_DAPM_INPUT("MIC2"),
7481+SND_SOC_DAPM_VMID("VREF"),
7482+};
7483+
7484+static const char *audio_map[][3] = {
7485+ /* left mixer */
7486+ {"Left Mixer", "Left Playback Switch", "Left DAC"},
7487+ {"Left Mixer", "Voice Playback Switch", "Voice DAC"},
7488+ {"Left Mixer", "Sidetone Playback Switch", "Mic Sidetone Mux"},
7489+ {"Left Mixer", "Bypass Playback Switch", "Line Left Mux"},
7490+
7491+ /* right mixer */
7492+ {"Right Mixer", "Right Playback Switch", "Right DAC"},
7493+ {"Right Mixer", "Voice Playback Switch", "Voice DAC"},
7494+ {"Right Mixer", "Sidetone Playback Switch", "Mic Sidetone Mux"},
7495+ {"Right Mixer", "Bypass Playback Switch", "Line Right Mux"},
7496+
7497+ /* mono mixer */
7498+ {"Mono Mixer", "Voice Playback Switch", "Voice DAC"},
7499+ {"Mono Mixer", "Left Playback Switch", "Left DAC"},
7500+ {"Mono Mixer", "Right Playback Switch", "Right DAC"},
7501+ {"Mono Mixer", "Sidetone Playback Switch", "Mic Sidetone Mux"},
7502+ {"Mono Mixer", "Bypass Playback Switch", "Line Mono Mux"},
7503+
7504+ /* left out */
7505+ {"Left Out 1", NULL, "Left Mixer"},
7506+ {"Left Out 2", NULL, "Left Mixer"},
7507+ {"LOUT1", NULL, "Left Out 1"},
7508+ {"LOUT2", NULL, "Left Out 2"},
7509+
7510+ /* right out */
7511+ {"Right Out 1", NULL, "Right Mixer"},
7512+ {"Right Out 2", NULL, "Right Mixer"},
7513+ {"ROUT1", NULL, "Right Out 1"},
7514+ {"ROUT2", NULL, "Right Out 2"},
7515+
7516+ /* mono 1 out */
7517+ {"Mono Out 1", NULL, "Mono Mixer"},
7518+ {"MONO1", NULL, "Mono Out 1"},
7519+
7520+ /* mono 2 out */
7521+ {"Mono 2 Mux", "Left + Right", "Out3 Left + Right"},
7522+ {"Mono 2 Mux", "Inverted Mono 1", "MONO1"},
7523+ {"Mono 2 Mux", "Left", "Left Mixer"},
7524+ {"Mono 2 Mux", "Right", "Right Mixer"},
7525+ {"Mono Out 2", NULL, "Mono 2 Mux"},
7526+ {"MONO2", NULL, "Mono Out 2"},
7527+
7528+ /* out 3 */
7529+ {"Out3 Left + Right", NULL, "Left Mixer"},
7530+ {"Out3 Left + Right", NULL, "Right Mixer"},
7531+ {"Out3 Mux", "VREF", "VREF"},
7532+ {"Out3 Mux", "Left + Right", "Out3 Left + Right"},
7533+ {"Out3 Mux", "ROUT2", "ROUT2"},
7534+ {"Out 3", NULL, "Out3 Mux"},
7535+ {"OUT3", NULL, "Out 3"},
7536+
7537+ /* out 4 */
7538+ {"Out4 Mux", "VREF", "VREF"},
7539+ {"Out4 Mux", "Capture ST", "Capture ST Mixer"},
7540+ {"Out4 Mux", "LOUT2", "LOUT2"},
7541+ {"Out 4", NULL, "Out4 Mux"},
7542+ {"OUT4", NULL, "Out 4"},
7543+
7544+ /* record mixer */
7545+ {"Playback Mixer", "Left Capture Switch", "Left Mixer"},
7546+ {"Playback Mixer", "Voice Capture Switch", "Mono Mixer"},
7547+ {"Playback Mixer", "Right Capture Switch", "Right Mixer"},
7548+
7549+ /* Mic/SideTone Mux */
7550+ {"Mic Sidetone Mux", "Left PGA", "Left Capture Volume"},
7551+ {"Mic Sidetone Mux", "Right PGA", "Right Capture Volume"},
7552+ {"Mic Sidetone Mux", "Mic 1", "Mic 1 Volume"},
7553+ {"Mic Sidetone Mux", "Mic 2", "Mic 2 Volume"},
7554+
7555+ /* Capture Left Mux */
7556+ {"Capture Left Mux", "PGA", "Left Capture Volume"},
7557+ {"Capture Left Mux", "Line or RXP-RXN", "Line Left Mux"},
7558+ {"Capture Left Mux", "Line", "LINE1"},
7559+
7560+ /* Capture Right Mux */
7561+ {"Capture Right Mux", "PGA", "Right Capture Volume"},
7562+ {"Capture Right Mux", "Line or RXP-RXN", "Line Right Mux"},
7563+ {"Capture Right Mux", "Sidetone", "Capture ST Mixer"},
7564+
7565+ /* Mono Capture mixer-mux */
7566+ {"Capture Right Mixer", "Stereo", "Capture Right Mux"},
7567+ {"Capture Left Mixer", "Analogue Mix Left", "Capture Left Mux"},
7568+ {"Capture Left Mixer", "Analogue Mix Left", "Capture Right Mux"},
7569+ {"Capture Right Mixer", "Analogue Mix Right", "Capture Left Mux"},
7570+ {"Capture Right Mixer", "Analogue Mix Right", "Capture Right Mux"},
7571+ {"Capture Left Mixer", "Digital Mono Mix", "Capture Left Mux"},
7572+ {"Capture Left Mixer", "Digital Mono Mix", "Capture Right Mux"},
7573+ {"Capture Right Mixer", "Digital Mono Mix", "Capture Left Mux"},
7574+ {"Capture Right Mixer", "Digital Mono Mix", "Capture Right Mux"},
7575+
7576+ /* ADC */
7577+ {"Left ADC", NULL, "Capture Left Mixer"},
7578+ {"Right ADC", NULL, "Capture Right Mixer"},
7579+
7580+ /* Left Capture Volume */
7581+ {"Left Capture Volume", NULL, "ACIN"},
7582+
7583+ /* Right Capture Volume */
7584+ {"Right Capture Volume", NULL, "Mic 2 Volume"},
7585+
7586+ /* ALC Mixer */
7587+ {"ALC Mixer", "Line Capture Switch", "Line Mixer"},
7588+ {"ALC Mixer", "Mic2 Capture Switch", "Mic 2 Volume"},
7589+ {"ALC Mixer", "Mic1 Capture Switch", "Mic 1 Volume"},
7590+ {"ALC Mixer", "Rx Capture Switch", "Rx Mixer"},
7591+
7592+ /* Line Left Mux */
7593+ {"Line Left Mux", "Line 1", "LINE1"},
7594+ {"Line Left Mux", "Rx Mix", "Rx Mixer"},
7595+
7596+ /* Line Right Mux */
7597+ {"Line Right Mux", "Line 2", "LINE2"},
7598+ {"Line Right Mux", "Rx Mix", "Rx Mixer"},
7599+
7600+ /* Line Mono Mux */
7601+ {"Line Mono Mux", "Line Mix", "Line Mixer"},
7602+ {"Line Mono Mux", "Rx Mix", "Rx Mixer"},
7603+
7604+ /* Line Mixer/Mux */
7605+ {"Line Mixer", "Line 1 + 2", "LINE1"},
7606+ {"Line Mixer", "Line 1 - 2", "LINE1"},
7607+ {"Line Mixer", "Line 1 + 2", "LINE2"},
7608+ {"Line Mixer", "Line 1 - 2", "LINE2"},
7609+ {"Line Mixer", "Line 1", "LINE1"},
7610+ {"Line Mixer", "Line 2", "LINE2"},
7611+
7612+ /* Rx Mixer/Mux */
7613+ {"Rx Mixer", "RXP - RXN", "RXP"},
7614+ {"Rx Mixer", "RXP + RXN", "RXP"},
7615+ {"Rx Mixer", "RXP - RXN", "RXN"},
7616+ {"Rx Mixer", "RXP + RXN", "RXN"},
7617+ {"Rx Mixer", "RXP", "RXP"},
7618+ {"Rx Mixer", "RXN", "RXN"},
7619+
7620+ /* Mic 1 Volume */
7621+ {"Mic 1 Volume", NULL, "MIC1N"},
7622+ {"Mic 1 Volume", NULL, "Mic Selection Mux"},
7623+
7624+ /* Mic 2 Volume */
7625+ {"Mic 2 Volume", NULL, "MIC2N"},
7626+ {"Mic 2 Volume", NULL, "MIC2"},
7627+
7628+ /* Mic Selector Mux */
7629+ {"Mic Selection Mux", "Mic 1", "MIC1"},
7630+ {"Mic Selection Mux", "Mic 2", "MIC2N"},
7631+ {"Mic Selection Mux", "Mic 3", "MIC2"},
7632+
7633+ /* ACOP */
7634+ {"ACOP", NULL, "ALC Mixer"},
7635+
7636+ /* terminator */
7637+ {NULL, NULL, NULL},
7638+};
7639+
7640+static int wm8753_add_widgets(struct snd_soc_codec *codec)
7641+{
7642+ int i;
7643+
7644+ for(i = 0; i < ARRAY_SIZE(wm8753_dapm_widgets); i++) {
7645+ snd_soc_dapm_new_control(codec, &wm8753_dapm_widgets[i]);
7646+ }
7647+
7648+ /* set up the WM8753 audio map */
7649+ for(i = 0; audio_map[i][0] != NULL; i++) {
7650+ snd_soc_dapm_connect_input(codec, audio_map[i][0],
7651+ audio_map[i][1], audio_map[i][2]);
7652+ }
7653+
7654+ snd_soc_dapm_new_widgets(codec);
7655+ return 0;
7656+}
7657+
7658+/* PLL divisors */
7659+struct _pll_div {
7660+ u32 pll_in; /* ext clock input */
7661+ u32 pll_out; /* pll out freq */
7662+ u32 div2:1;
7663+ u32 n:4;
7664+ u32 k:24;
7665+};
7666+
7667+/*
7668+ * PLL divisors -
7669+ */
7670+static const struct _pll_div pll_div[] = {
7671+ {13000000, 12288000, 0, 0x7, 0x23F54A},
7672+ {13000000, 11289600, 0, 0x6, 0x3CA2F5},
7673+ {12000000, 12288000, 0, 0x8, 0x0C49BA},
7674+ {12000000, 11289600, 0, 0x7, 0x21B08A},
7675+ {24000000, 12288000, 1, 0x8, 0x0C49BA},
7676+ {24000000, 11289600, 1, 0x7, 0x21B08A},
7677+ {12288000, 11289600, 0, 0x7, 0x166667},
7678+ {26000000, 11289600, 1, 0x6, 0x3CA2F5},
7679+ {26000000, 12288000, 1, 0x7, 0x23F54A},
7680+};
7681+
7682+static u32 wm8753_config_pll(struct snd_soc_codec *codec,
7683+ struct snd_soc_codec_dai *dai, int pll)
7684+{
7685+ u16 reg;
7686+ int found = 0;
7687+
7688+ if (pll == 1) {
7689+ reg = wm8753_read_reg_cache(codec, WM8753_CLOCK) & 0xffef;
7690+ if (!dai->pll_in || !dai->mclk) {
7691+ /* disable PLL1 */
7692+ wm8753_write(codec, WM8753_PLL1CTL1, 0x0026);
7693+ wm8753_write(codec, WM8753_CLOCK, reg);
7694+ return 0;
7695+ } else {
7696+ u16 value = 0;
7697+ int i = 0;
7698+
7699+ /* if we cant match, then use good values for N and K */
7700+ for (;i < ARRAY_SIZE(pll_div); i++) {
7701+ if (pll_div[i].pll_out == dai->pll_out &&
7702+ pll_div[i].pll_in == dai->pll_in) {
7703+ found = 1;
7704+ break;
7705+ }
7706+ }
7707+
7708+ if (!found)
7709+ goto err;
7710+
7711+ /* set up N and K PLL divisor ratios */
7712+ /* bits 8:5 = PLL_N, bits 3:0 = PLL_K[21:18] */
7713+ value = (pll_div[i].n << 5) + ((pll_div[i].k & 0x3c0000) >> 18);
7714+ wm8753_write(codec, WM8753_PLL1CTL2, value);
7715+
7716+ /* bits 8:0 = PLL_K[17:9] */
7717+ value = (pll_div[i].k & 0x03fe00) >> 9;
7718+ wm8753_write(codec, WM8753_PLL1CTL3, value);
7719+
7720+ /* bits 8:0 = PLL_K[8:0] */
7721+ value = pll_div[i].k & 0x0001ff;
7722+ wm8753_write(codec, WM8753_PLL1CTL4, value);
7723+
7724+ /* set PLL1 as input and enable */
7725+ wm8753_write(codec, WM8753_PLL1CTL1, 0x0027 |
7726+ (pll_div[i].div2 << 3));
7727+ wm8753_write(codec, WM8753_CLOCK, reg | 0x0010);
7728+ }
7729+ } else {
7730+ reg = wm8753_read_reg_cache(codec, WM8753_CLOCK) & 0xfff7;
7731+ if (!dai->pll_in || !dai->mclk) {
7732+ /* disable PLL2 */
7733+ wm8753_write(codec, WM8753_PLL2CTL1, 0x0026);
7734+ wm8753_write(codec, WM8753_CLOCK, reg);
7735+ return 0;
7736+ } else {
7737+ u16 value = 0;
7738+ int i = 0;
7739+
7740+ /* if we cant match, then use good values for N and K */
7741+ for (;i < ARRAY_SIZE(pll_div); i++) {
7742+ if (pll_div[i].pll_out == dai->pll_out &&
7743+ pll_div[i].pll_in == dai->pll_in) {
7744+ found = 1;
7745+ break;
7746+ }
7747+ }
7748+
7749+ if (!found)
7750+ goto err;
7751+
7752+ /* set up N and K PLL divisor ratios */
7753+ /* bits 8:5 = PLL_N, bits 3:0 = PLL_K[21:18] */
7754+ value = (pll_div[i].n << 5) + ((pll_div[i].k & 0x3c0000) >> 18);
7755+ wm8753_write(codec, WM8753_PLL2CTL2, value);
7756+
7757+ /* bits 8:0 = PLL_K[17:9] */
7758+ value = (pll_div[i].k & 0x03fe00) >> 9;
7759+ wm8753_write(codec, WM8753_PLL2CTL3, value);
7760+
7761+ /* bits 8:0 = PLL_K[8:0] */
7762+ value = pll_div[i].k & 0x0001ff;
7763+ wm8753_write(codec, WM8753_PLL2CTL4, value);
7764+
7765+ /* set PLL1 as input and enable */
7766+ wm8753_write(codec, WM8753_PLL2CTL1, 0x0027 |
7767+ (pll_div[i].div2 << 3));
7768+ wm8753_write(codec, WM8753_CLOCK, reg | 0x0008);
7769+ }
7770+ }
7771+
7772+ return dai->pll_in;
7773+err:
7774+ return 0;
7775+}
7776+
7777+struct _coeff_div {
7778+ u32 mclk;
7779+ u32 rate;
7780+ u16 fs;
7781+ u8 sr:5;
7782+ u8 usb:1;
7783+};
7784+
7785+/* codec hifi mclk (after PLL) clock divider coefficients */
7786+static const struct _coeff_div coeff_div[] = {
7787+ /* 8k */
7788+ {12288000, 8000, 1536, 0x6, 0x0},
7789+ {11289600, 8000, 1408, 0x16, 0x0},
7790+ {18432000, 8000, 2304, 0x7, 0x0},
7791+ {16934400, 8000, 2112, 0x17, 0x0},
7792+ {12000000, 8000, 1500, 0x6, 0x1},
7793+
7794+ /* 11.025k */
7795+ {11289600, 11025, 1024, 0x18, 0x0},
7796+ {16934400, 11025, 1536, 0x19, 0x0},
7797+ {12000000, 11025, 1088, 0x19, 0x1},
7798+
7799+ /* 16k */
7800+ {12288000, 16000, 768, 0xa, 0x0},
7801+ {18432000, 16000, 1152, 0xb, 0x0},
7802+ {12000000, 16000, 750, 0xa, 0x1},
7803+
7804+ /* 22.05k */
7805+ {11289600, 22050, 512, 0x1a, 0x0},
7806+ {16934400, 22050, 768, 0x1b, 0x0},
7807+ {12000000, 22050, 544, 0x1b, 0x1},
7808+
7809+ /* 32k */
7810+ {12288000, 32000, 384, 0xc, 0x0},
7811+ {18432000, 32000, 576, 0xd, 0x0},
7812+ {12000000, 32000, 375, 0xa, 0x1},
7813+
7814+ /* 44.1k */
7815+ {11289600, 44100, 256, 0x10, 0x0},
7816+ {16934400, 44100, 384, 0x11, 0x0},
7817+ {12000000, 44100, 272, 0x11, 0x1},
7818+
7819+ /* 48k */
7820+ {12288000, 48000, 256, 0x0, 0x0},
7821+ {18432000, 48000, 384, 0x1, 0x0},
7822+ {12000000, 48000, 250, 0x0, 0x1},
7823+
7824+ /* 88.2k */
7825+ {11289600, 88200, 128, 0x1e, 0x0},
7826+ {16934400, 88200, 192, 0x1f, 0x0},
7827+ {12000000, 88200, 136, 0x1f, 0x1},
7828+
7829+ /* 96k */
7830+ {12288000, 96000, 128, 0xe, 0x0},
7831+ {18432000, 96000, 192, 0xf, 0x0},
7832+ {12000000, 96000, 125, 0xe, 0x1},
7833+};
7834+
7835+static int get_coeff(int mclk, int rate)
7836+{
7837+ int i;
7838+
7839+ for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
7840+ if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
7841+ return i;
7842+ }
7843+ return -EINVAL;
7844+}
7845+
7846+/* supported HiFi input clocks (that don't use PLL) */
7847+const static int hifi_clks[] = {11289600, 12000000, 12288000,
7848+ 16934400, 18432000};
7849+
7850+/* The HiFi interface can be clocked in one of two ways:-
7851+ * o No PLL - MCLK is used directly.
7852+ * o PLL - PLL is used to generate audio MCLK from input clock.
7853+ *
7854+ * We use the direct method if we can as it saves power.
7855+ */
7856+static unsigned int wm8753_config_i2s_sysclk(struct snd_soc_codec_dai *dai,
7857+ struct snd_soc_clock_info *info, unsigned int clk)
7858+{
7859+ int i, pll_out;
7860+
7861+ /* is clk supported without the PLL */
7862+ for(i = 0; i < ARRAY_SIZE(hifi_clks); i++) {
7863+ if (clk == hifi_clks[i]) {
7864+ dai->mclk = clk;
7865+ dai->pll_in = dai->pll_out = 0;
7866+ dai->clk_div = 1;
7867+ return clk;
7868+ }
7869+ }
7870+
7871+ /* determine best PLL output speed */
7872+ if (info->bclk_master &
7873+ (SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_CBM_CFS)) {
7874+ pll_out = info->fs * info->rate;
7875+ } else {
7876+ /* calc slave clock */
7877+ switch (info->rate){
7878+ case 11025:
7879+ case 22050:
7880+ case 44100:
7881+ case 88200:
7882+ pll_out = 11289600;
7883+ break;
7884+ default:
7885+ pll_out = 12288000;
7886+ break;
7887+ }
7888+ }
7889+
7890+ /* are input & output clocks supported by PLL */
7891+ for (i = 0;i < ARRAY_SIZE(pll_div); i++) {
7892+ if (pll_div[i].pll_in == clk && pll_div[i].pll_out == pll_out) {
7893+ dai->pll_in = clk;
7894+ dai->pll_out = dai->mclk = pll_out;
7895+ return pll_out;
7896+ }
7897+ }
7898+
7899+ /* this clk is not supported */
7900+ return 0;
7901+}
7902+
7903+/* valid PCM clock dividers * 2 */
7904+static int pcm_divs[] = {2, 6, 11, 4, 8, 12, 16};
7905+
7906+/* The Voice interface can be clocked in one of four ways:-
7907+ * o No PLL - MCLK is used directly.
7908+ * o Div - MCLK is directly divided.
7909+ * o PLL - PLL is used to generate audio MCLK from input clock.
7910+ * o PLL & Div - PLL and post divider are used.
7911+ *
7912+ * We use the non PLL methods if we can, as it saves power.
7913+ */
7914+
7915+static unsigned int wm8753_config_pcm_sysclk(struct snd_soc_codec_dai *dai,
7916+ struct snd_soc_clock_info *info, unsigned int clk)
7917+{
7918+ int i, j, best_clk = info->fs * info->rate;
7919+
7920+ /* can we run at this clk without the PLL ? */
7921+ for (i = 0; i < ARRAY_SIZE(pcm_divs); i++) {
7922+ if ((best_clk >> 1) * pcm_divs[i] == clk) {
7923+ dai->pll_in = 0;
7924+ dai->clk_div = pcm_divs[i];
7925+ dai->mclk = best_clk;
7926+ return dai->mclk;
7927+ }
7928+ }
7929+
7930+ /* now check for PLL support */
7931+ for (i = 0; i < ARRAY_SIZE(pll_div); i++) {
7932+ if (pll_div[i].pll_in == clk) {
7933+ for (j = 0; j < ARRAY_SIZE(pcm_divs); j++) {
7934+ if (pll_div[i].pll_out == pcm_divs[j] * (best_clk >> 1)) {
7935+ dai->pll_in = clk;
7936+ dai->pll_out = pll_div[i].pll_out;
7937+ dai->clk_div = pcm_divs[j];
7938+ dai->mclk = best_clk;
7939+ return dai->mclk;
7940+ }
7941+ }
7942+ }
7943+ }
7944+
7945+ /* this clk is not supported */
7946+ return 0;
7947+}
7948+
7949+/* set the format and bit size for ADC and Voice DAC */
7950+static void wm8753_adc_vdac_prepare(struct snd_pcm_substream *substream)
7951+{
7952+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
7953+ struct snd_soc_device *socdev = rtd->socdev;
7954+ struct snd_soc_codec *codec = socdev->codec;
7955+ u16 voice = wm8753_read_reg_cache(codec, WM8753_PCM) & 0x01e0;
7956+
7957+ /* interface format */
7958+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
7959+ case SND_SOC_DAIFMT_I2S:
7960+ voice |= 0x0002;
7961+ break;
7962+ case SND_SOC_DAIFMT_RIGHT_J:
7963+ break;
7964+ case SND_SOC_DAIFMT_LEFT_J:
7965+ voice |= 0x0001;
7966+ break;
7967+ case SND_SOC_DAIFMT_DSP_A:
7968+ voice |= 0x0003;
7969+ break;
7970+ case SND_SOC_DAIFMT_DSP_B:
7971+ voice |= 0x0013;
7972+ break;
7973+ }
7974+
7975+ /* bit size */
7976+ switch (rtd->codec_dai->dai_runtime.pcmfmt) {
7977+ case SNDRV_PCM_FMTBIT_S16_LE:
7978+ break;
7979+ case SNDRV_PCM_FMTBIT_S20_3LE:
7980+ voice |= 0x0004;
7981+ break;
7982+ case SNDRV_PCM_FMTBIT_S24_LE:
7983+ voice |= 0x0008;
7984+ break;
7985+ case SNDRV_PCM_FMTBIT_S32_LE:
7986+ voice |= 0x000c;
7987+ break;
7988+ }
7989+
7990+ wm8753_write(codec, WM8753_PCM, voice);
7991+}
7992+
7993+/* configure PCM DAI */
7994+static int wm8753_pcm_dai_prepare(struct snd_pcm_substream *substream)
7995+{
7996+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
7997+ struct snd_soc_device *socdev = rtd->socdev;
7998+ struct snd_soc_codec *codec = socdev->codec;
7999+ u16 voice, ioctl, srate, srate2, fs, bfs, clock;
8000+ unsigned int rate;
8001+
8002+ bfs = SND_SOC_FSBD_REAL(rtd->codec_dai->dai_runtime.bfs);
8003+ fs = rtd->codec_dai->dai_runtime.fs;
8004+ rate = snd_soc_get_rate(rtd->codec_dai->dai_runtime.pcmrate);
8005+ voice = wm8753_read_reg_cache(codec, WM8753_PCM) & 0x001f;
8006+
8007+ /* set master/slave audio interface */
8008+ ioctl = wm8753_read_reg_cache(codec, WM8753_IOCTL) & 0x01fd;
8009+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
8010+ case SND_SOC_DAIFMT_CBM_CFM:
8011+ ioctl |= 0x0002;
8012+ case SND_SOC_DAIFMT_CBM_CFS:
8013+ voice |= 0x0040;
8014+ break;
8015+ }
8016+
8017+ /* do we need to enable the PLL */
8018+ if (rtd->codec_dai->pll_in) {
8019+ if (wm8753_config_pll(codec, rtd->codec_dai, 2) !=
8020+ rtd->codec_dai->pll_in) {
8021+ err("could not set pll to %d --> %d",
8022+ rtd->codec_dai->pll_in, rtd->codec_dai->pll_out);
8023+ return -ENODEV;
8024+ }
8025+ }
8026+
8027+ /* set up PCM divider */
8028+ clock = wm8753_read_reg_cache(codec, WM8753_CLOCK) & 0x003f;
8029+ switch (rtd->codec_dai->clk_div) {
8030+ case 2: /* 1 */
8031+ break;
8032+ case 6: /* 3 */
8033+ clock |= (0x2 << 6);
8034+ break;
8035+ case 11: /* 5.5 */
8036+ clock |= (0x3 << 6);
8037+ break;
8038+ case 4: /* 2 */
8039+ clock |= (0x4 << 6);
8040+ break;
8041+ case 8: /* 4 */
8042+ clock |= (0x5 << 6);
8043+ break;
8044+ case 12: /* 6 */
8045+ clock |= (0x6 << 6);
8046+ break;
8047+ case 16: /* 8 */
8048+ clock |= (0x7 << 6);
8049+ break;
8050+ default:
8051+ printk(KERN_ERR "wm8753: invalid PCM clk divider %d\n",
8052+ rtd->codec_dai->clk_div);
8053+ break;
8054+ }
8055+ wm8753_write(codec, WM8753_CLOCK, clock);
8056+
8057+ /* set bclk divisor rate */
8058+ srate2 = wm8753_read_reg_cache(codec, WM8753_SRATE2) & 0x003f;
8059+ switch (bfs) {
8060+ case 1:
8061+ break;
8062+ case 2:
8063+ srate2 |= (0x1 << 6);
8064+ break;
8065+ case 4:
8066+ srate2 |= (0x2 << 6);
8067+ break;
8068+ case 8:
8069+ srate2 |= (0x3 << 6);
8070+ break;
8071+ case 16:
8072+ srate2 |= (0x4 << 6);
8073+ break;
8074+ }
8075+ wm8753_write(codec, WM8753_SRATE2, srate2);
8076+
8077+ srate = wm8753_read_reg_cache(codec, WM8753_SRATE1) & 0x017f;
8078+ if (rtd->codec_dai->dai_runtime.fs == 384)
8079+ srate |= 0x80;
8080+ wm8753_write(codec, WM8753_SRATE1, srate);
8081+
8082+ /* clock inversion */
8083+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_INV_MASK) {
8084+ case SND_SOC_DAIFMT_IB_IF:
8085+ voice |= 0x0090;
8086+ break;
8087+ case SND_SOC_DAIFMT_IB_NF:
8088+ voice |= 0x0080;
8089+ break;
8090+ case SND_SOC_DAIFMT_NB_IF:
8091+ voice |= 0x0010;
8092+ break;
8093+ }
8094+ //printk("voice %x %x ioctl %x %x srate2 %x %x srate1 %x %x\n",
8095+ //WM8753_PCM, voice, WM8753_IOCTL, ioctl, WM8753_SRATE2,
8096+ //srate2, WM8753_SRATE1, srate);
8097+
8098+ wm8753_write(codec, WM8753_IOCTL, ioctl);
8099+ wm8753_write(codec, WM8753_PCM, voice);
8100+ return 0;
8101+}
8102+
8103+/* configure hifi DAC wordlength and format */
8104+static void wm8753_hdac_prepare(struct snd_pcm_substream *substream)
8105+{
8106+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
8107+ struct snd_soc_device *socdev = rtd->socdev;
8108+ struct snd_soc_codec *codec = socdev->codec;
8109+ u16 hifi = wm8753_read_reg_cache(codec, WM8753_HIFI) & 0x01e0;
8110+
8111+ /* interface format */
8112+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
8113+ case SND_SOC_DAIFMT_I2S:
8114+ hifi |= 0x0002;
8115+ break;
8116+ case SND_SOC_DAIFMT_RIGHT_J:
8117+ break;
8118+ case SND_SOC_DAIFMT_LEFT_J:
8119+ hifi |= 0x0001;
8120+ break;
8121+ case SND_SOC_DAIFMT_DSP_A:
8122+ hifi |= 0x0003;
8123+ break;
8124+ case SND_SOC_DAIFMT_DSP_B:
8125+ hifi |= 0x0013;
8126+ break;
8127+ }
8128+
8129+ /* bit size */
8130+ switch (rtd->codec_dai->dai_runtime.pcmfmt) {
8131+ case SNDRV_PCM_FMTBIT_S16_LE:
8132+ break;
8133+ case SNDRV_PCM_FMTBIT_S20_3LE:
8134+ hifi |= 0x0004;
8135+ break;
8136+ case SNDRV_PCM_FMTBIT_S24_LE:
8137+ hifi |= 0x0008;
8138+ break;
8139+ case SNDRV_PCM_FMTBIT_S32_LE:
8140+ hifi |= 0x000c;
8141+ break;
8142+ }
8143+
8144+ wm8753_write(codec, WM8753_HIFI, hifi);
8145+}
8146+
8147+/* configure i2s (hifi) DAI clocking */
8148+static int wm8753_i2s_dai_prepare(struct snd_pcm_substream *substream)
8149+{
8150+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
8151+ struct snd_soc_device *socdev = rtd->socdev;
8152+ struct snd_soc_codec *codec = socdev->codec;
8153+ u16 srate, bfs, hifi, ioctl;
8154+ unsigned int rate;
8155+ int i = 0;
8156+
8157+ bfs = SND_SOC_FSBD_REAL(rtd->codec_dai->dai_runtime.bfs);
8158+ rate = snd_soc_get_rate(rtd->codec_dai->dai_runtime.pcmrate);
8159+ hifi = wm8753_read_reg_cache(codec, WM8753_HIFI) & 0x001f;
8160+
8161+ /* is coefficient valid ? */
8162+ if ((i = get_coeff(rtd->codec_dai->mclk, rate)) < 0)
8163+ return i;
8164+
8165+ srate = wm8753_read_reg_cache(codec, WM8753_SRATE1) & 0x01c0;
8166+ wm8753_write(codec, WM8753_SRATE1, srate | (coeff_div[i].sr << 1) |
8167+ coeff_div[i].usb);
8168+
8169+ /* do we need to enable the PLL */
8170+ if (rtd->codec_dai->pll_in) {
8171+ if (wm8753_config_pll(codec, rtd->codec_dai, 1) !=
8172+ rtd->codec_dai->pll_in) {
8173+ err("could not set pll to %d --> %d",
8174+ rtd->codec_dai->pll_in, rtd->codec_dai->pll_out);
8175+ return -ENODEV;
8176+ }
8177+ }
8178+
8179+ /* set bclk divisor rate */
8180+ srate = wm8753_read_reg_cache(codec, WM8753_SRATE2) & 0x01c7;
8181+ switch (bfs) {
8182+ case 1:
8183+ break;
8184+ case 2:
8185+ srate |= (0x1 << 3);
8186+ break;
8187+ case 4:
8188+ srate |= (0x2 << 3);
8189+ break;
8190+ case 8:
8191+ srate |= (0x3 << 3);
8192+ break;
8193+ case 16:
8194+ srate |= (0x4 << 3);
8195+ break;
8196+ }
8197+ wm8753_write(codec, WM8753_SRATE2, srate);
8198+
8199+ /* set master/slave audio interface */
8200+ ioctl = wm8753_read_reg_cache(codec, WM8753_IOCTL) & 0x00fe;
8201+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
8202+ case SND_SOC_DAIFMT_CBM_CFM:
8203+ ioctl |= 0x0001;
8204+ case SND_SOC_DAIFMT_CBM_CFS:
8205+ hifi |= 0x0040;
8206+ break;
8207+ }
8208+
8209+ /* clock inversion */
8210+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_INV_MASK) {
8211+ case SND_SOC_DAIFMT_IB_IF:
8212+ hifi |= 0x0090;
8213+ break;
8214+ case SND_SOC_DAIFMT_IB_NF:
8215+ hifi |= 0x0080;
8216+ break;
8217+ case SND_SOC_DAIFMT_NB_IF:
8218+ hifi |= 0x0010;
8219+ break;
8220+ }
8221+ wm8753_write(codec, WM8753_IOCTL, ioctl);
8222+ wm8753_write(codec, WM8753_HIFI, hifi);
8223+ return 0;
8224+}
8225+
8226+static int wm8753_mode1v_prepare (struct snd_pcm_substream *substream)
8227+{
8228+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
8229+ struct snd_soc_device *socdev = rtd->socdev;
8230+ struct snd_soc_codec *codec = socdev->codec;
8231+ u16 clock;
8232+
8233+ /* set clk source as pcmclk */
8234+ clock = wm8753_read_reg_cache(codec, WM8753_CLOCK) & 0xfffb;
8235+ wm8753_write(codec, WM8753_CLOCK, clock);
8236+
8237+ wm8753_adc_vdac_prepare(substream);
8238+ return wm8753_pcm_dai_prepare(substream);
8239+}
8240+
8241+static int wm8753_mode1h_prepare (struct snd_pcm_substream *substream)
8242+{
8243+ wm8753_hdac_prepare(substream);
8244+ return wm8753_i2s_dai_prepare(substream);
8245+}
8246+
8247+static int wm8753_mode2_prepare (struct snd_pcm_substream *substream)
8248+{
8249+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
8250+ struct snd_soc_device *socdev = rtd->socdev;
8251+ struct snd_soc_codec *codec = socdev->codec;
8252+ u16 clock;
8253+
8254+ /* set clk source as pcmclk */
8255+ clock = wm8753_read_reg_cache(codec, WM8753_CLOCK) & 0xfffb;
8256+ wm8753_write(codec, WM8753_CLOCK, clock);
8257+
8258+ wm8753_adc_vdac_prepare(substream);
8259+ return wm8753_i2s_dai_prepare(substream);
8260+}
8261+
8262+static int wm8753_mode3_prepare (struct snd_pcm_substream *substream)
8263+{
8264+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
8265+ struct snd_soc_device *socdev = rtd->socdev;
8266+ struct snd_soc_codec *codec = socdev->codec;
8267+ u16 clock;
8268+
8269+ /* set clk source as mclk */
8270+ clock = wm8753_read_reg_cache(codec, WM8753_CLOCK) & 0xfffb;
8271+ wm8753_write(codec, WM8753_CLOCK, clock | 0x4);
8272+
8273+ wm8753_hdac_prepare(substream);
8274+ wm8753_adc_vdac_prepare(substream);
8275+ return wm8753_i2s_dai_prepare(substream);
8276+}
8277+
8278+static int wm8753_mode4_prepare (struct snd_pcm_substream *substream)
8279+{
8280+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
8281+ struct snd_soc_device *socdev = rtd->socdev;
8282+ struct snd_soc_codec *codec = socdev->codec;
8283+ u16 clock;
8284+
8285+ /* set clk source as mclk */
8286+ clock = wm8753_read_reg_cache(codec, WM8753_CLOCK) & 0xfffb;
8287+ wm8753_write(codec, WM8753_CLOCK, clock | 0x4);
8288+
8289+ wm8753_hdac_prepare(substream);
8290+ wm8753_adc_vdac_prepare(substream);
8291+ return wm8753_i2s_dai_prepare(substream);
8292+}
8293+
8294+static int wm8753_mute(struct snd_soc_codec *codec,
8295+ struct snd_soc_codec_dai *dai, int mute)
8296+{
8297+ u16 mute_reg = wm8753_read_reg_cache(codec, WM8753_DAC) & 0xfff7;
8298+
8299+ /* the digital mute covers the HiFi and Voice DAC's on the WM8753.
8300+ * make sure we check if they are not both active when we mute */
8301+ if (mute && dai->id == 1) {
8302+ if (!wm8753_dai[WM8753_DAI_VOICE].playback.active ||
8303+ !wm8753_dai[WM8753_DAI_HIFI].playback.active)
8304+ wm8753_write(codec, WM8753_DAC, mute_reg | 0x8);
8305+ } else {
8306+ if (mute)
8307+ wm8753_write(codec, WM8753_DAC, mute_reg | 0x8);
8308+ else
8309+ wm8753_write(codec, WM8753_DAC, mute_reg);
8310+ }
8311+
8312+ return 0;
8313+}
8314+
8315+static int wm8753_dapm_event(struct snd_soc_codec *codec, int event)
8316+{
8317+ u16 pwr_reg = wm8753_read_reg_cache(codec, WM8753_PWR1) & 0xfe3e;
8318+
8319+ switch (event) {
8320+ case SNDRV_CTL_POWER_D0: /* full On */
8321+ /* set vmid to 50k and unmute dac */
8322+ wm8753_write(codec, WM8753_PWR1, pwr_reg | 0x00c0);
8323+ break;
8324+ case SNDRV_CTL_POWER_D1: /* partial On */
8325+ case SNDRV_CTL_POWER_D2: /* partial On */
8326+ /* set vmid to 5k for quick power up */
8327+ wm8753_write(codec, WM8753_PWR1, pwr_reg | 0x01c1);
8328+ break;
8329+ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
8330+ /* mute dac and set vmid to 500k, enable VREF */
8331+ wm8753_write(codec, WM8753_PWR1, pwr_reg | 0x0141);
8332+ break;
8333+ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
8334+ wm8753_write(codec, WM8753_PWR1, 0x0001);
8335+ break;
8336+ }
8337+ codec->dapm_state = event;
8338+ return 0;
8339+}
8340+
8341+/*
8342+ * The WM8753 supports upto 4 different and mutually exclusive DAI
8343+ * configurations. This gives 2 PCM's available for use, hifi and voice.
8344+ * NOTE: The Voice PCM cannot play or caputure audio to the CPU as it's DAI
8345+ * is connected between the wm8753 and a BT codec or GSM modem.
8346+ *
8347+ * 1. Voice over PCM DAI - HIFI DAC over HIFI DAI
8348+ * 2. Voice over HIFI DAI - HIFI disabled
8349+ * 3. Voice disabled - HIFI over HIFI
8350+ * 4. Voice disabled - HIFI over HIFI, uses voice DAI LRC for capture
8351+ */
8352+static const struct snd_soc_codec_dai wm8753_all_dai[] = {
8353+/* DAI HiFi mode 1 */
8354+{ .name = "WM8753 HiFi",
8355+ .id = 1,
8356+ .playback = {
8357+ .stream_name = "HiFi Playback",
8358+ .channels_min = 1,
8359+ .channels_max = 2,},
8360+ .capture = { /* dummy for fast DAI switching */
8361+ .stream_name = "HiFi Capture",
8362+ .channels_min = 1,
8363+ .channels_max = 2,},
8364+ .config_sysclk = wm8753_config_i2s_sysclk,
8365+ .digital_mute = wm8753_mute,
8366+ .ops = {
8367+ .prepare = wm8753_mode1h_prepare,},
8368+ .caps = {
8369+ .num_modes = ARRAY_SIZE(wm8753_hifi_modes),
8370+ .mode = wm8753_hifi_modes,},
8371+},
8372+/* DAI Voice mode 1 */
8373+{ .name = "WM8753 Voice",
8374+ .id = 1,
8375+ .playback = {
8376+ .stream_name = "Voice Playback",
8377+ .channels_min = 1,
8378+ .channels_max = 1,},
8379+ .capture = {
8380+ .stream_name = "Voice Capture",
8381+ .channels_min = 1,
8382+ .channels_max = 2,},
8383+ .config_sysclk = wm8753_config_pcm_sysclk,
8384+ .digital_mute = wm8753_mute,
8385+ .ops = {
8386+ .prepare = wm8753_mode1v_prepare,},
8387+ .caps = {
8388+ .num_modes = ARRAY_SIZE(wm8753_voice_modes),
8389+ .mode = wm8753_voice_modes,},
8390+},
8391+/* DAI HiFi mode 2 - dummy */
8392+{ .name = "WM8753 HiFi",
8393+ .id = 2,
8394+},
8395+/* DAI Voice mode 2 */
8396+{ .name = "WM8753 Voice",
8397+ .id = 2,
8398+ .playback = {
8399+ .stream_name = "Voice Playback",
8400+ .channels_min = 1,
8401+ .channels_max = 1,},
8402+ .capture = {
8403+ .stream_name = "Voice Capture",
8404+ .channels_min = 1,
8405+ .channels_max = 2,},
8406+ .config_sysclk = wm8753_config_i2s_sysclk,
8407+ .digital_mute = wm8753_mute,
8408+ .ops = {
8409+ .prepare = wm8753_mode2_prepare,},
8410+ .caps = {
8411+ .num_modes = ARRAY_SIZE(wm8753_voice_modes),
8412+ .mode = wm8753_voice_modes,},
8413+},
8414+/* DAI HiFi mode 3 */
8415+{ .name = "WM8753 HiFi",
8416+ .id = 3,
8417+ .playback = {
8418+ .stream_name = "HiFi Playback",
8419+ .channels_min = 1,
8420+ .channels_max = 2,},
8421+ .capture = {
8422+ .stream_name = "HiFi Capture",
8423+ .channels_min = 1,
8424+ .channels_max = 2,},
8425+ .config_sysclk = wm8753_config_i2s_sysclk,
8426+ .digital_mute = wm8753_mute,
8427+ .ops = {
8428+ .prepare = wm8753_mode3_prepare,},
8429+ .caps = {
8430+ .num_modes = ARRAY_SIZE(wm8753_hifi_modes),
8431+ .mode = wm8753_hifi_modes,},
8432+},
8433+/* DAI Voice mode 3 - dummy */
8434+{ .name = "WM8753 Voice",
8435+ .id = 3,
8436+},
8437+/* DAI HiFi mode 4 */
8438+{ .name = "WM8753 HiFi",
8439+ .id = 4,
8440+ .playback = {
8441+ .stream_name = "HiFi Playback",
8442+ .channels_min = 1,
8443+ .channels_max = 2,},
8444+ .capture = {
8445+ .stream_name = "HiFi Capture",
8446+ .channels_min = 1,
8447+ .channels_max = 2,},
8448+ .config_sysclk = wm8753_config_i2s_sysclk,
8449+ .digital_mute = wm8753_mute,
8450+ .ops = {
8451+ .prepare = wm8753_mode4_prepare,},
8452+ .caps = {
8453+ .num_modes = ARRAY_SIZE(wm8753_mixed_modes),
8454+ .mode = wm8753_mixed_modes,},
8455+},
8456+/* DAI Voice mode 4 - dummy */
8457+{ .name = "WM8753 Voice",
8458+ .id = 4,
8459+},
8460+};
8461+
8462+struct snd_soc_codec_dai wm8753_dai[2];
8463+EXPORT_SYMBOL_GPL(wm8753_dai);
8464+
8465+static void wm8753_set_dai_mode(struct snd_soc_codec *codec, unsigned int mode)
8466+{
8467+ if (mode < 4) {
8468+ wm8753_dai[0] = wm8753_all_dai[mode << 1];
8469+ wm8753_dai[1] = wm8753_all_dai[(mode << 1) + 1];
8470+ }
8471+}
8472+
8473+static void wm8753_work(void *data)
8474+{
8475+ struct snd_soc_codec *codec = (struct snd_soc_codec *)data;
8476+ wm8753_dapm_event(codec, codec->dapm_state);
8477+}
8478+
8479+static int wm8753_suspend(struct platform_device *pdev, pm_message_t state)
8480+{
8481+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
8482+ struct snd_soc_codec *codec = socdev->codec;
8483+
8484+ wm8753_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
8485+ return 0;
8486+}
8487+
8488+static int wm8753_resume(struct platform_device *pdev)
8489+{
8490+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
8491+ struct snd_soc_codec *codec = socdev->codec;
8492+ int i;
8493+ u8 data[2];
8494+ u16 *cache = codec->reg_cache;
8495+
8496+ /* Sync reg_cache with the hardware */
8497+ for (i = 0; i < ARRAY_SIZE(wm8753_reg); i++) {
8498+ if (i + 1 == WM8753_RESET)
8499+ continue;
8500+ data[0] = ((i + 1) << 1) | ((cache[i] >> 8) & 0x0001);
8501+ data[1] = cache[i] & 0x00ff;
8502+ codec->hw_write(codec->control_data, data, 2);
8503+ }
8504+
8505+ wm8753_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
8506+
8507+ /* charge wm8753 caps */
8508+ if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0) {
8509+ wm8753_dapm_event(codec, SNDRV_CTL_POWER_D2);
8510+ codec->dapm_state = SNDRV_CTL_POWER_D0;
8511+ queue_delayed_work(wm8753_workq, &wm8753_dapm_work,
8512+ msecs_to_jiffies(caps_charge));
8513+ }
8514+
8515+ return 0;
8516+}
8517+
8518+/*
8519+ * initialise the WM8753 driver
8520+ * register the mixer and dsp interfaces with the kernel
8521+ */
8522+static int wm8753_init(struct snd_soc_device *socdev)
8523+{
8524+ struct snd_soc_codec *codec = socdev->codec;
8525+ int reg, ret = 0;
8526+
8527+ codec->name = "WM8753";
8528+ codec->owner = THIS_MODULE;
8529+ codec->read = wm8753_read_reg_cache;
8530+ codec->write = wm8753_write;
8531+ codec->dapm_event = wm8753_dapm_event;
8532+ codec->dai = wm8753_dai;
8533+ codec->num_dai = 2;
8534+ codec->reg_cache_size = ARRAY_SIZE(wm8753_reg);
8535+
8536+ codec->reg_cache =
8537+ kzalloc(sizeof(u16) * ARRAY_SIZE(wm8753_reg), GFP_KERNEL);
8538+ if (codec->reg_cache == NULL)
8539+ return -ENOMEM;
8540+ memcpy(codec->reg_cache, wm8753_reg,
8541+ sizeof(u16) * ARRAY_SIZE(wm8753_reg));
8542+ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm8753_reg);
8543+ wm8753_set_dai_mode(codec, 0);
8544+
8545+ wm8753_reset(codec);
8546+
8547+ /* register pcms */
8548+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
8549+ if (ret < 0) {
8550+ kfree(codec->reg_cache);
8551+ return ret;
8552+ }
8553+
8554+ /* charge output caps */
8555+ wm8753_dapm_event(codec, SNDRV_CTL_POWER_D2);
8556+ codec->dapm_state = SNDRV_CTL_POWER_D3hot;
8557+ queue_delayed_work(wm8753_workq,
8558+ &wm8753_dapm_work, msecs_to_jiffies(caps_charge));
8559+
8560+ /* set the update bits */
8561+ reg = wm8753_read_reg_cache(codec, WM8753_LDAC);
8562+ wm8753_write(codec, WM8753_LDAC, reg | 0x0100);
8563+ reg = wm8753_read_reg_cache(codec, WM8753_RDAC);
8564+ wm8753_write(codec, WM8753_RDAC, reg | 0x0100);
8565+ reg = wm8753_read_reg_cache(codec, WM8753_LOUT1V);
8566+ wm8753_write(codec, WM8753_LOUT1V, reg | 0x0100);
8567+ reg = wm8753_read_reg_cache(codec, WM8753_ROUT1V);
8568+ wm8753_write(codec, WM8753_ROUT1V, reg | 0x0100);
8569+ reg = wm8753_read_reg_cache(codec, WM8753_LOUT2V);
8570+ wm8753_write(codec, WM8753_LOUT2V, reg | 0x0100);
8571+ reg = wm8753_read_reg_cache(codec, WM8753_ROUT2V);
8572+ wm8753_write(codec, WM8753_ROUT2V, reg | 0x0100);
8573+ reg = wm8753_read_reg_cache(codec, WM8753_LINVOL);
8574+ wm8753_write(codec, WM8753_LINVOL, reg | 0x0100);
8575+ reg = wm8753_read_reg_cache(codec, WM8753_RINVOL);
8576+ wm8753_write(codec, WM8753_RINVOL, reg | 0x0100);
8577+
8578+ wm8753_add_controls(codec);
8579+ wm8753_add_widgets(codec);
8580+ ret = snd_soc_register_card(socdev);
8581+ if (ret < 0) {
8582+ snd_soc_free_pcms(socdev);
8583+ snd_soc_dapm_free(socdev);
8584+ }
8585+
8586+ return ret;
8587+}
8588+
8589+/* If the i2c layer weren't so broken, we could pass this kind of data
8590+ around */
8591+static struct snd_soc_device *wm8753_socdev;
8592+
8593+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
8594+
8595+/*
8596+ * WM8753 2 wire address is determined by GPIO5
8597+ * state during powerup.
8598+ * low = 0x1a
8599+ * high = 0x1b
8600+ */
8601+#define I2C_DRIVERID_WM8753 0xfefe /* liam - need a proper id */
8602+
8603+static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
8604+
8605+/* Magic definition of all other variables and things */
8606+I2C_CLIENT_INSMOD;
8607+
8608+static struct i2c_driver wm8753_i2c_driver;
8609+static struct i2c_client client_template;
8610+
8611+static int wm8753_codec_probe(struct i2c_adapter *adap, int addr, int kind)
8612+{
8613+ struct snd_soc_device *socdev = wm8753_socdev;
8614+ struct wm8753_setup_data *setup = socdev->codec_data;
8615+ struct snd_soc_codec *codec = socdev->codec;
8616+ struct i2c_client *i2c;
8617+ int ret;
8618+
8619+ if (addr != setup->i2c_address)
8620+ return -ENODEV;
8621+
8622+ client_template.adapter = adap;
8623+ client_template.addr = addr;
8624+
8625+ i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
8626+ if (i2c == NULL){
8627+ kfree(codec);
8628+ return -ENOMEM;
8629+ }
8630+ memcpy(i2c, &client_template, sizeof(struct i2c_client));
8631+ i2c_set_clientdata(i2c, codec);
8632+ codec->control_data = i2c;
8633+
8634+ ret = i2c_attach_client(i2c);
8635+ if (ret < 0) {
8636+ err("failed to attach codec at addr %x\n", addr);
8637+ goto err;
8638+ }
8639+
8640+ ret = wm8753_init(socdev);
8641+ if (ret < 0) {
8642+ err("failed to initialise WM8753\n");
8643+ goto err;
8644+ }
8645+
8646+ return ret;
8647+
8648+err:
8649+ kfree(codec);
8650+ kfree(i2c);
8651+ return ret;
8652+}
8653+
8654+static int wm8753_i2c_detach(struct i2c_client *client)
8655+{
8656+ struct snd_soc_codec *codec = i2c_get_clientdata(client);
8657+ i2c_detach_client(client);
8658+ kfree(codec->reg_cache);
8659+ kfree(client);
8660+ return 0;
8661+}
8662+
8663+static int wm8753_i2c_attach(struct i2c_adapter *adap)
8664+{
8665+ return i2c_probe(adap, &addr_data, wm8753_codec_probe);
8666+}
8667+
8668+/* corgi i2c codec control layer */
8669+static struct i2c_driver wm8753_i2c_driver = {
8670+ .driver = {
8671+ .name = "WM8753 I2C Codec",
8672+ .owner = THIS_MODULE,
8673+ },
8674+ .id = I2C_DRIVERID_WM8753,
8675+ .attach_adapter = wm8753_i2c_attach,
8676+ .detach_client = wm8753_i2c_detach,
8677+ .command = NULL,
8678+};
8679+
8680+static struct i2c_client client_template = {
8681+ .name = "WM8753",
8682+ .driver = &wm8753_i2c_driver,
8683+};
8684+#endif
8685+
8686+static int wm8753_probe(struct platform_device *pdev)
8687+{
8688+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
8689+ struct wm8753_setup_data *setup;
8690+ struct snd_soc_codec *codec;
8691+ int ret = 0;
8692+
8693+ info("WM8753 Audio Codec %s", WM8753_VERSION);
8694+
8695+ setup = socdev->codec_data;
8696+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
8697+ if (codec == NULL)
8698+ return -ENOMEM;
8699+
8700+ socdev->codec = codec;
8701+ mutex_init(&codec->mutex);
8702+ INIT_LIST_HEAD(&codec->dapm_widgets);
8703+ INIT_LIST_HEAD(&codec->dapm_paths);
8704+ wm8753_socdev = socdev;
8705+ INIT_WORK(&wm8753_dapm_work, wm8753_work, codec);
8706+ wm8753_workq = create_workqueue("wm8753");
8707+ if (wm8753_workq == NULL) {
8708+ kfree(codec);
8709+ return -ENOMEM;
8710+ }
8711+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
8712+ if (setup->i2c_address) {
8713+ normal_i2c[0] = setup->i2c_address;
8714+ codec->hw_write = (hw_write_t)i2c_master_send;
8715+ ret = i2c_add_driver(&wm8753_i2c_driver);
8716+ if (ret != 0)
8717+ printk(KERN_ERR "can't add i2c driver");
8718+ }
8719+#else
8720+ /* Add other interfaces here */
8721+#endif
8722+ return ret;
8723+}
8724+
8725+/* power down chip */
8726+static int wm8753_remove(struct platform_device *pdev)
8727+{
8728+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
8729+ struct snd_soc_codec *codec = socdev->codec;
8730+
8731+ if (codec->control_data)
8732+ wm8753_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
8733+ if (wm8753_workq)
8734+ destroy_workqueue(wm8753_workq);
8735+ snd_soc_free_pcms(socdev);
8736+ snd_soc_dapm_free(socdev);
8737+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
8738+ i2c_del_driver(&wm8753_i2c_driver);
8739+#endif
8740+ kfree(codec);
8741+
8742+ return 0;
8743+}
8744+
8745+struct snd_soc_codec_device soc_codec_dev_wm8753 = {
8746+ .probe = wm8753_probe,
8747+ .remove = wm8753_remove,
8748+ .suspend = wm8753_suspend,
8749+ .resume = wm8753_resume,
8750+};
8751+
8752+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8753);
8753+
8754+MODULE_DESCRIPTION("ASoC WM8753 driver");
8755+MODULE_AUTHOR("Liam Girdwood");
8756+MODULE_LICENSE("GPL");
8757Index: linux-2.6-pxa-new/sound/soc/codecs/wm8753.h
8758===================================================================
8759--- /dev/null
8760+++ linux-2.6-pxa-new/sound/soc/codecs/wm8753.h
8761@@ -0,0 +1,91 @@
8762+/*
8763+ * wm8753.h -- audio driver for WM8753
8764+ *
8765+ * Copyright 2003 Wolfson Microelectronics PLC.
8766+ * Author: Liam Girdwood
8767+ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
8768+ *
8769+ * This program is free software; you can redistribute it and/or modify it
8770+ * under the terms of the GNU General Public License as published by the
8771+ * Free Software Foundation; either version 2 of the License, or (at your
8772+ * option) any later version.
8773+ *
8774+ */
8775+
8776+#ifndef _WM8753_H
8777+#define _WM8753_H
8778+
8779+/* WM8753 register space */
8780+
8781+#define WM8753_DAC 0x01
8782+#define WM8753_ADC 0x02
8783+#define WM8753_PCM 0x03
8784+#define WM8753_HIFI 0x04
8785+#define WM8753_IOCTL 0x05
8786+#define WM8753_SRATE1 0x06
8787+#define WM8753_SRATE2 0x07
8788+#define WM8753_LDAC 0x08
8789+#define WM8753_RDAC 0x09
8790+#define WM8753_BASS 0x0a
8791+#define WM8753_TREBLE 0x0b
8792+#define WM8753_ALC1 0x0c
8793+#define WM8753_ALC2 0x0d
8794+#define WM8753_ALC3 0x0e
8795+#define WM8753_NGATE 0x0f
8796+#define WM8753_LADC 0x10
8797+#define WM8753_RADC 0x11
8798+#define WM8753_ADCTL1 0x12
8799+#define WM8753_3D 0x13
8800+#define WM8753_PWR1 0x14
8801+#define WM8753_PWR2 0x15
8802+#define WM8753_PWR3 0x16
8803+#define WM8753_PWR4 0x17
8804+#define WM8753_ID 0x18
8805+#define WM8753_INTPOL 0x19
8806+#define WM8753_INTEN 0x1a
8807+#define WM8753_GPIO1 0x1b
8808+#define WM8753_GPIO2 0x1c
8809+#define WM8753_RESET 0x1f
8810+#define WM8753_RECMIX1 0x20
8811+#define WM8753_RECMIX2 0x21
8812+#define WM8753_LOUTM1 0x22
8813+#define WM8753_LOUTM2 0x23
8814+#define WM8753_ROUTM1 0x24
8815+#define WM8753_ROUTM2 0x25
8816+#define WM8753_MOUTM1 0x26
8817+#define WM8753_MOUTM2 0x27
8818+#define WM8753_LOUT1V 0x28
8819+#define WM8753_ROUT1V 0x29
8820+#define WM8753_LOUT2V 0x2a
8821+#define WM8753_ROUT2V 0x2b
8822+#define WM8753_MOUTV 0x2c
8823+#define WM8753_OUTCTL 0x2d
8824+#define WM8753_ADCIN 0x2e
8825+#define WM8753_INCTL1 0x2f
8826+#define WM8753_INCTL2 0x30
8827+#define WM8753_LINVOL 0x31
8828+#define WM8753_RINVOL 0x32
8829+#define WM8753_MICBIAS 0x33
8830+#define WM8753_CLOCK 0x34
8831+#define WM8753_PLL1CTL1 0x35
8832+#define WM8753_PLL1CTL2 0x36
8833+#define WM8753_PLL1CTL3 0x37
8834+#define WM8753_PLL1CTL4 0x38
8835+#define WM8753_PLL2CTL1 0x39
8836+#define WM8753_PLL2CTL2 0x3a
8837+#define WM8753_PLL2CTL3 0x3b
8838+#define WM8753_PLL2CTL4 0x3c
8839+#define WM8753_BIASCTL 0x3d
8840+#define WM8753_ADCTL2 0x3f
8841+
8842+struct wm8753_setup_data {
8843+ unsigned short i2c_address;
8844+};
8845+
8846+#define WM8753_DAI_HIFI 0
8847+#define WM8753_DAI_VOICE 1
8848+
8849+extern struct snd_soc_codec_dai wm8753_dai[2];
8850+extern struct snd_soc_codec_device soc_codec_dev_wm8753;
8851+
8852+#endif
8853Index: linux-2.6-pxa-new/sound/soc/codecs/wm8772.c
8854===================================================================
8855--- /dev/null
8856+++ linux-2.6-pxa-new/sound/soc/codecs/wm8772.c
8857@@ -0,0 +1,806 @@
8858+/*
8859+ * wm8772.c -- WM8772 ALSA Soc Audio driver
8860+ *
8861+ * Copyright 2005 Wolfson Microelectronics PLC.
8862+ * Author: Liam Girdwood
8863+ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
8864+ *
8865+ * This program is free software; you can redistribute it and/or modify it
8866+ * under the terms of the GNU General Public License as published by the
8867+ * Free Software Foundation; either version 2 of the License, or (at your
8868+ * option) any later version.
8869+ *
8870+ */
8871+
8872+#include <linux/module.h>
8873+#include <linux/moduleparam.h>
8874+#include <linux/version.h>
8875+#include <linux/kernel.h>
8876+#include <linux/init.h>
8877+#include <linux/delay.h>
8878+#include <linux/pm.h>
8879+#include <linux/i2c.h>
8880+
8881+#include <sound/driver.h>
8882+#include <sound/core.h>
8883+#include <sound/pcm.h>
8884+#include <sound/pcm_params.h>
8885+#include <sound/soc.h>
8886+#include <sound/soc-dapm.h>
8887+#include <sound/initval.h>
8888+
8889+#include "wm8772.h"
8890+
8891+#define AUDIO_NAME "WM8772"
8892+#define WM8772_VERSION "0.3"
8893+
8894+/*
8895+ * wm8772 register cache
8896+ * We can't read the WM8772 register space when we
8897+ * are using 2 wire for device control, so we cache them instead.
8898+ */
8899+static const u16 wm8772_reg[] = {
8900+ 0x00ff, 0x00ff, 0x0120, 0x0000, /* 0 */
8901+ 0x00ff, 0x00ff, 0x00ff, 0x00ff, /* 4 */
8902+ 0x00ff, 0x0000, 0x0080, 0x0040, /* 8 */
8903+ 0x0000
8904+};
8905+
8906+#define WM8772_DAIFMT \
8907+ (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_RIGHT_J | \
8908+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_IB_NF)
8909+
8910+#define WM8772_DIR \
8911+ (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
8912+
8913+#define WM8772_PRATES \
8914+ (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
8915+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
8916+
8917+#define WM8772_CRATES \
8918+ (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
8919+ SNDRV_PCM_RATE_96000)
8920+
8921+static struct snd_soc_dai_mode wm8772_modes[] = {
8922+ /* common codec frame and clock master modes */
8923+ /* 32k */
8924+ {
8925+ .fmt = WM8772_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
8926+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
8927+ .pcmrate = SNDRV_PCM_RATE_32000,
8928+ .pcmdir = WM8772_DIR,
8929+ .flags = SND_SOC_DAI_BFS_RATE,
8930+ .fs = 768,
8931+ .bfs = 64,
8932+ },
8933+ {
8934+ .fmt = WM8772_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
8935+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
8936+ .pcmrate = SNDRV_PCM_RATE_32000,
8937+ .pcmdir = WM8772_DIR,
8938+ .flags = SND_SOC_DAI_BFS_RATE,
8939+ .fs = 512,
8940+ .bfs = 64,
8941+ },
8942+ {
8943+ .fmt = WM8772_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
8944+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
8945+ .pcmrate = SNDRV_PCM_RATE_32000,
8946+ .pcmdir = WM8772_DIR,
8947+ .flags = SND_SOC_DAI_BFS_RATE,
8948+ .fs = 384,
8949+ .bfs = 64,
8950+ },
8951+ {
8952+ .fmt = WM8772_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
8953+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
8954+ .pcmrate = SNDRV_PCM_RATE_32000,
8955+ .pcmdir = WM8772_DIR,
8956+ .flags = SND_SOC_DAI_BFS_RATE,
8957+ .fs = 256,
8958+ .bfs = 64,
8959+ },
8960+ {
8961+ .fmt = WM8772_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
8962+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
8963+ .pcmrate = SNDRV_PCM_RATE_32000,
8964+ .pcmdir = WM8772_DIR,
8965+ .flags = SND_SOC_DAI_BFS_RATE,
8966+ .fs = 192,
8967+ .bfs = 64,
8968+ },
8969+ {
8970+ .fmt = WM8772_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
8971+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
8972+ .pcmrate = SNDRV_PCM_RATE_32000,
8973+ .pcmdir = WM8772_DIR,
8974+ .flags = SND_SOC_DAI_BFS_RATE,
8975+ .fs = 128,
8976+ .bfs = 64,
8977+ },
8978+
8979+ /* 44.1k */
8980+ {
8981+ .fmt = WM8772_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
8982+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
8983+ .pcmrate = SNDRV_PCM_RATE_44100,
8984+ .pcmdir = WM8772_DIR,
8985+ .flags = SND_SOC_DAI_BFS_RATE,
8986+ .fs = 768,
8987+ .bfs = 64,
8988+ },
8989+ {
8990+ .fmt = WM8772_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
8991+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
8992+ .pcmrate = SNDRV_PCM_RATE_44100,
8993+ .pcmdir = WM8772_DIR,
8994+ .flags = SND_SOC_DAI_BFS_RATE,
8995+ .fs = 512,
8996+ .bfs = 64,
8997+ },
8998+ {
8999+ .fmt = WM8772_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
9000+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
9001+ .pcmrate = SNDRV_PCM_RATE_44100,
9002+ .pcmdir = WM8772_DIR,
9003+ .flags = SND_SOC_DAI_BFS_RATE,
9004+ .fs = 384,
9005+ .bfs = 64,
9006+ },
9007+ {
9008+ .fmt = WM8772_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
9009+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
9010+ .pcmrate = SNDRV_PCM_RATE_44100,
9011+ .pcmdir = WM8772_DIR,
9012+ .flags = SND_SOC_DAI_BFS_RATE,
9013+ .fs = 256,
9014+ .bfs = 64,
9015+ },
9016+ {
9017+ .fmt = WM8772_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
9018+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
9019+ .pcmrate = SNDRV_PCM_RATE_44100,
9020+ .pcmdir = WM8772_DIR,
9021+ .flags = SND_SOC_DAI_BFS_RATE,
9022+ .fs = 192,
9023+ .bfs = 64,
9024+ },
9025+ {
9026+ .fmt = WM8772_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
9027+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
9028+ .pcmrate = SNDRV_PCM_RATE_44100,
9029+ .pcmdir = WM8772_DIR,
9030+ .flags = SND_SOC_DAI_BFS_RATE,
9031+ .fs = 128,
9032+ .bfs = 64,
9033+ },
9034+
9035+ /* 48k */
9036+ {
9037+ .fmt = WM8772_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
9038+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
9039+ .pcmrate = SNDRV_PCM_RATE_48000,
9040+ .pcmdir = WM8772_DIR,
9041+ .flags = SND_SOC_DAI_BFS_RATE,
9042+ .fs = 768,
9043+ .bfs = 64,
9044+ },
9045+ {
9046+ .fmt = WM8772_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
9047+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
9048+ .pcmrate = SNDRV_PCM_RATE_48000,
9049+ .pcmdir = WM8772_DIR,
9050+ .flags = SND_SOC_DAI_BFS_RATE,
9051+ .fs = 512,
9052+ .bfs = 64,
9053+ },
9054+ {
9055+ .fmt = WM8772_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
9056+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
9057+ .pcmrate = SNDRV_PCM_RATE_48000,
9058+ .pcmdir = WM8772_DIR,
9059+ .flags = SND_SOC_DAI_BFS_RATE,
9060+ .fs = 384,
9061+ .bfs = 64,
9062+ },
9063+ {
9064+ .fmt = WM8772_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
9065+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
9066+ .pcmrate = SNDRV_PCM_RATE_48000,
9067+ .pcmdir = WM8772_DIR,
9068+ .flags = SND_SOC_DAI_BFS_RATE,
9069+ .fs = 256,
9070+ .bfs = 64,
9071+ },
9072+ {
9073+ .fmt = WM8772_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
9074+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
9075+ .pcmrate = SNDRV_PCM_RATE_48000,
9076+ .pcmdir = WM8772_DIR,
9077+ .flags = SND_SOC_DAI_BFS_RATE,
9078+ .fs = 192,
9079+ .bfs = 64,
9080+ },
9081+ {
9082+ .fmt = WM8772_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
9083+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
9084+ .pcmrate = SNDRV_PCM_RATE_48000,
9085+ .pcmdir = WM8772_DIR,
9086+ .flags = SND_SOC_DAI_BFS_RATE,
9087+ .fs = 128,
9088+ .bfs = 64,
9089+ },
9090+
9091+ /* 96k */
9092+ {
9093+ .fmt = WM8772_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
9094+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
9095+ .pcmrate = SNDRV_PCM_RATE_96000,
9096+ .pcmdir = WM8772_DIR,
9097+ .flags = SND_SOC_DAI_BFS_RATE,
9098+ .fs = 384,
9099+ .bfs = 64,
9100+ },
9101+ {
9102+ .fmt = WM8772_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
9103+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
9104+ .pcmrate = SNDRV_PCM_RATE_96000,
9105+ .pcmdir = WM8772_DIR,
9106+ .flags = SND_SOC_DAI_BFS_RATE,
9107+ .fs = 256,
9108+ .bfs = 64,
9109+ },
9110+ {
9111+ .fmt = WM8772_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
9112+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
9113+ .pcmrate = SNDRV_PCM_RATE_96000,
9114+ .pcmdir = WM8772_DIR,
9115+ .flags = SND_SOC_DAI_BFS_RATE,
9116+ .fs = 192,
9117+ .bfs = 64,
9118+ },
9119+ {
9120+ .fmt = WM8772_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
9121+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
9122+ .pcmrate = SNDRV_PCM_RATE_96000,
9123+ .pcmdir = WM8772_DIR,
9124+ .pcmrate = SND_SOC_DAI_BFS_RATE,
9125+ .fs = 128,
9126+ .bfs = 64,
9127+ },
9128+
9129+ /* 192k */
9130+ {
9131+ .fmt = WM8772_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
9132+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
9133+ .pcmrate = SNDRV_PCM_RATE_192000,
9134+ .pcmdir = SND_SOC_DAIDIR_PLAYBACK,
9135+ .flags = SND_SOC_DAI_BFS_RATE,
9136+ .fs = 192,
9137+ .bfs = 64,
9138+ },
9139+ {
9140+ .fmt = WM8772_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
9141+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
9142+ .pcmrate = SNDRV_PCM_RATE_192000,
9143+ .pcmdir = SND_SOC_DAIDIR_PLAYBACK,
9144+ .flags = SND_SOC_DAI_BFS_RATE,
9145+ .fs = 128,
9146+ .bfs = 64,
9147+ },
9148+
9149+ /* slave mode */
9150+ {
9151+ .fmt = WM8772_DAIFMT,
9152+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
9153+ .pcmrate = WM8772_PRATES,
9154+ .pcmdir = SND_SOC_DAIDIR_PLAYBACK,
9155+ .fs = SND_SOC_FS_ALL,
9156+ .bfs = SND_SOC_FSB_ALL,
9157+ },
9158+ {
9159+ .fmt = WM8772_DAIFMT,
9160+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
9161+ .pcmrate = WM8772_CRATES,
9162+ .pcmdir = SND_SOC_DAIDIR_CAPTURE,
9163+ .fs = SND_SOC_FS_ALL,
9164+ .bfs = SND_SOC_FSB_ALL,
9165+ },
9166+};
9167+
9168+/*
9169+ * read wm8772 register cache
9170+ */
9171+static inline unsigned int wm8772_read_reg_cache(struct snd_soc_codec * codec,
9172+ unsigned int reg)
9173+{
9174+ u16 *cache = codec->reg_cache;
9175+ if (reg > WM8772_CACHE_REGNUM)
9176+ return -1;
9177+ return cache[reg];
9178+}
9179+
9180+/*
9181+ * write wm8772 register cache
9182+ */
9183+static inline void wm8772_write_reg_cache(struct snd_soc_codec * codec,
9184+ unsigned int reg, unsigned int value)
9185+{
9186+ u16 *cache = codec->reg_cache;
9187+ if (reg > WM8772_CACHE_REGNUM)
9188+ return;
9189+ cache[reg] = value;
9190+}
9191+
9192+static int wm8772_write(struct snd_soc_codec * codec, unsigned int reg,
9193+ unsigned int value)
9194+{
9195+ u8 data[2];
9196+
9197+ /* data is
9198+ * D15..D9 WM8772 register offset
9199+ * D8...D0 register data
9200+ */
9201+ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
9202+ data[1] = value & 0x00ff;
9203+
9204+ wm8772_write_reg_cache (codec, reg, value);
9205+ if (codec->hw_write(codec->control_data, data, 2) == 2)
9206+ return 0;
9207+ else
9208+ return -1;
9209+}
9210+
9211+#define wm8772_reset(c) wm8772_write(c, WM8772_RESET, 0)
9212+
9213+/*
9214+ * WM8772 Controls
9215+ */
9216+static const char *wm8772_zero_flag[] = {"All Ch", "Ch 1", "Ch 2", "Ch3"};
9217+
9218+static const struct soc_enum wm8772_enum[] = {
9219+SOC_ENUM_SINGLE(WM8772_DACCTRL, 0, 4, wm8772_zero_flag),
9220+};
9221+
9222+static const struct snd_kcontrol_new wm8772_snd_controls[] = {
9223+
9224+SOC_SINGLE("Left1 Playback Volume", WM8772_LDAC1VOL, 0, 255, 0),
9225+SOC_SINGLE("Left2 Playback Volume", WM8772_LDAC2VOL, 0, 255, 0),
9226+SOC_SINGLE("Left3 Playback Volume", WM8772_LDAC3VOL, 0, 255, 0),
9227+SOC_SINGLE("Right1 Playback Volume", WM8772_RDAC1VOL, 0, 255, 0),
9228+SOC_SINGLE("Right1 Playback Volume", WM8772_RDAC2VOL, 0, 255, 0),
9229+SOC_SINGLE("Right1 Playback Volume", WM8772_RDAC3VOL, 0, 255, 0),
9230+SOC_SINGLE("Master Playback Volume", WM8772_MDACVOL, 0, 255, 0),
9231+
9232+SOC_SINGLE("Playback Switch", WM8772_DACCH, 0, 1, 0),
9233+SOC_SINGLE("Capture Switch", WM8772_ADCCTRL, 2, 1, 0),
9234+
9235+SOC_SINGLE("Demp1 Playback Switch", WM8772_DACCTRL, 6, 1, 0),
9236+SOC_SINGLE("Demp2 Playback Switch", WM8772_DACCTRL, 7, 1, 0),
9237+SOC_SINGLE("Demp3 Playback Switch", WM8772_DACCTRL, 8, 1, 0),
9238+
9239+SOC_SINGLE("Phase Invert 1 Switch", WM8772_IFACE, 6, 1, 0),
9240+SOC_SINGLE("Phase Invert 2 Switch", WM8772_IFACE, 7, 1, 0),
9241+SOC_SINGLE("Phase Invert 3 Switch", WM8772_IFACE, 8, 1, 0),
9242+
9243+SOC_SINGLE("Playback ZC Switch", WM8772_DACCTRL, 0, 1, 0),
9244+
9245+SOC_SINGLE("Capture High Pass Switch", WM8772_ADCCTRL, 3, 1, 0),
9246+};
9247+
9248+/* add non dapm controls */
9249+static int wm8772_add_controls(struct snd_soc_codec *codec)
9250+{
9251+ int err, i;
9252+
9253+ for (i = 0; i < ARRAY_SIZE(wm8772_snd_controls); i++) {
9254+ err = snd_ctl_add(codec->card,
9255+ snd_soc_cnew(&wm8772_snd_controls[i],codec, NULL));
9256+ if (err < 0)
9257+ return err;
9258+ }
9259+ return 0;
9260+}
9261+
9262+/* valid wm8772 mclk frequencies */
9263+static const int freq_table[5][6] = {
9264+ {4096000, 6144000, 8192000, 12288000, 16384000, 24576000},
9265+ {5644800, 8467000, 11289600, 16934000, 22579200, 33868800},
9266+ {6144000, 9216000, 12288000, 18432000, 24576000, 36864000},
9267+ {12288000, 18432000, 24576000, 36864000, 0, 0},
9268+ {24576000, 36864000, 0, 0, 0},
9269+};
9270+
9271+static unsigned int check_freq(int rate, unsigned int freq)
9272+{
9273+ int i;
9274+
9275+ for(i = 0; i < 6; i++) {
9276+ if(freq == freq_table[i][rate])
9277+ return freq;
9278+ }
9279+ return 0;
9280+}
9281+
9282+static unsigned int wm8772_config_sysclk(struct snd_soc_codec_dai *dai,
9283+ struct snd_soc_clock_info *info, unsigned int clk)
9284+{
9285+ switch (info->rate){
9286+ case 32000:
9287+ dai->mclk = check_freq(0, clk);
9288+ break;
9289+ case 44100:
9290+ dai->mclk = check_freq(1, clk);
9291+ break;
9292+ case 48000:
9293+ dai->mclk = check_freq(2, clk);
9294+ break;
9295+ case 96000:
9296+ dai->mclk = check_freq(3, clk);
9297+ break;
9298+ case 192000:
9299+ dai->mclk = check_freq(4, clk);
9300+ break;
9301+ default:
9302+ dai->mclk = 0;
9303+ }
9304+ return dai->mclk;
9305+}
9306+
9307+static int wm8772_pcm_prepare(struct snd_pcm_substream *substream)
9308+{
9309+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
9310+ struct snd_soc_device *socdev = rtd->socdev;
9311+ struct snd_soc_codec *codec = socdev->codec;
9312+ u16 diface = wm8772_read_reg_cache(codec, WM8772_IFACE) & 0xffc0;
9313+ u16 diface_ctrl = wm8772_read_reg_cache(codec, WM8772_DACRATE) & 0xfe1f;
9314+ u16 aiface = 0;
9315+ u16 aiface_ctrl = wm8772_read_reg_cache(codec, WM8772_ADCCTRL) & 0xfcff;
9316+
9317+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
9318+
9319+ /* set master/slave audio interface */
9320+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
9321+ case SND_SOC_DAIFMT_CBM_CFM:
9322+ diface_ctrl |= 0x0010;
9323+ break;
9324+ case SND_SOC_DAIFMT_CBS_CFS:
9325+ break;
9326+ }
9327+
9328+ /* interface format */
9329+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
9330+ case SND_SOC_DAIFMT_I2S:
9331+ diface |= 0x0002;
9332+ break;
9333+ case SND_SOC_DAIFMT_RIGHT_J:
9334+ break;
9335+ case SND_SOC_DAIFMT_LEFT_J:
9336+ diface |= 0x0001;
9337+ break;
9338+ case SND_SOC_DAIFMT_DSP_A:
9339+ diface |= 0x0003;
9340+ break;
9341+ case SND_SOC_DAIFMT_DSP_B:
9342+ diface |= 0x0007;
9343+ break;
9344+ }
9345+
9346+ /* bit size */
9347+ switch (rtd->codec_dai->dai_runtime.pcmfmt) {
9348+ case SNDRV_PCM_FMTBIT_S16_LE:
9349+ break;
9350+ case SNDRV_PCM_FORMAT_S20_3LE:
9351+ diface |= 0x0010;
9352+ break;
9353+ case SNDRV_PCM_FORMAT_S24_3LE:
9354+ diface |= 0x0020;
9355+ break;
9356+ case SNDRV_PCM_FORMAT_S32_LE:
9357+ diface |= 0x0030;
9358+ break;
9359+ }
9360+
9361+ /* clock inversion */
9362+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_INV_MASK) {
9363+ case SND_SOC_DAIFMT_NB_NF:
9364+ break;
9365+ case SND_SOC_DAIFMT_IB_NF:
9366+ diface |= 0x0008;
9367+ break;
9368+ }
9369+
9370+ /* set rate */
9371+ switch (rtd->codec_dai->dai_runtime.fs) {
9372+ case 768:
9373+ diface_ctrl |= (0x5 << 6);
9374+ break;
9375+ case 512:
9376+ diface_ctrl |= (0x4 << 6);
9377+ break;
9378+ case 384:
9379+ diface_ctrl |= (0x3 << 6);
9380+ break;
9381+ case 256:
9382+ diface_ctrl |= (0x2 << 6);
9383+ break;
9384+ case 192:
9385+ diface_ctrl |= (0x1 << 6);
9386+ break;
9387+ }
9388+
9389+ wm8772_write(codec, WM8772_DACRATE, diface_ctrl);
9390+ wm8772_write(codec, WM8772_IFACE, diface);
9391+
9392+ } else {
9393+
9394+ /* set master/slave audio interface */
9395+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
9396+ case SND_SOC_DAIFMT_CBM_CFM:
9397+ aiface |= 0x0010;
9398+ break;
9399+ case SND_SOC_DAIFMT_CBS_CFS:
9400+ break;
9401+ }
9402+
9403+ /* interface format */
9404+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
9405+ case SND_SOC_DAIFMT_I2S:
9406+ aiface |= 0x0002;
9407+ break;
9408+ case SND_SOC_DAIFMT_RIGHT_J:
9409+ break;
9410+ case SND_SOC_DAIFMT_LEFT_J:
9411+ aiface |= 0x0001;
9412+ break;
9413+ case SND_SOC_DAIFMT_DSP_A:
9414+ aiface |= 0x0003;
9415+ break;
9416+ case SND_SOC_DAIFMT_DSP_B:
9417+ aiface |= 0x0003;
9418+ aiface_ctrl |= 0x0010;
9419+ break;
9420+ }
9421+
9422+ /* bit size */
9423+ switch (rtd->codec_dai->dai_runtime.pcmfmt) {
9424+ case SNDRV_PCM_FMTBIT_S16_LE:
9425+ break;
9426+ case SNDRV_PCM_FMTBIT_S20_3LE:
9427+ aiface |= 0x0004;
9428+ break;
9429+ case SNDRV_PCM_FMTBIT_S24_LE:
9430+ aiface |= 0x0008;
9431+ break;
9432+ case SNDRV_PCM_FMTBIT_S32_LE:
9433+ aiface |= 0x000c;
9434+ break;
9435+ }
9436+
9437+ /* clock inversion */
9438+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_INV_MASK) {
9439+ case SND_SOC_DAIFMT_NB_NF:
9440+ break;
9441+ case SND_SOC_DAIFMT_IB_NF:
9442+ aiface_ctrl |= 0x0020;
9443+ break;
9444+ }
9445+
9446+ /* set rate */
9447+ switch (rtd->codec_dai->dai_runtime.fs) {
9448+ case 768:
9449+ aiface |= (0x5 << 5);
9450+ break;
9451+ case 512:
9452+ aiface |= (0x4 << 5);
9453+ break;
9454+ case 384:
9455+ aiface |= (0x3 << 5);
9456+ break;
9457+ case 256:
9458+ aiface |= (0x2 << 5);
9459+ break;
9460+ }
9461+
9462+ wm8772_write(codec, WM8772_ADCCTRL, aiface_ctrl);
9463+ wm8772_write(codec, WM8772_ADCRATE, aiface);
9464+ }
9465+
9466+ return 0;
9467+}
9468+
9469+static int wm8772_dapm_event(struct snd_soc_codec *codec, int event)
9470+{
9471+ u16 master = wm8772_read_reg_cache(codec, WM8772_DACRATE) & 0xffe0;
9472+
9473+ switch (event) {
9474+ case SNDRV_CTL_POWER_D0: /* full On */
9475+ /* vref/mid, clk and osc on, dac unmute, active */
9476+ wm8772_write(codec, WM8772_DACRATE, master);
9477+ break;
9478+ case SNDRV_CTL_POWER_D1: /* partial On */
9479+ case SNDRV_CTL_POWER_D2: /* partial On */
9480+ break;
9481+ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
9482+ /* everything off except vref/vmid, dac mute, inactive */
9483+ wm8772_write(codec, WM8772_DACRATE, master | 0x0f);
9484+ break;
9485+ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
9486+ /* everything off, dac mute, inactive */
9487+ wm8772_write(codec, WM8772_DACRATE, master | 0x1f);
9488+ break;
9489+ }
9490+ codec->dapm_state = event;
9491+ return 0;
9492+}
9493+
9494+struct snd_soc_codec_dai wm8772_dai = {
9495+ .name = "WM8772",
9496+ .playback = {
9497+ .stream_name = "Playback",
9498+ .channels_min = 1,
9499+ .channels_max = 6,
9500+ },
9501+ .capture = {
9502+ .stream_name = "Capture",
9503+ .channels_min = 1,
9504+ .channels_max = 2,
9505+ },
9506+ .config_sysclk = wm8772_config_sysclk,
9507+ .ops = {
9508+ .prepare = wm8772_pcm_prepare,
9509+ },
9510+ .caps = {
9511+ .num_modes = ARRAY_SIZE(wm8772_modes),
9512+ .mode = wm8772_modes,
9513+ },
9514+};
9515+EXPORT_SYMBOL_GPL(wm8772_dai);
9516+
9517+static int wm8772_suspend(struct platform_device *pdev, pm_message_t state)
9518+{
9519+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
9520+ struct snd_soc_codec *codec = socdev->codec;
9521+
9522+ wm8772_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
9523+ return 0;
9524+}
9525+
9526+static int wm8772_resume(struct platform_device *pdev)
9527+{
9528+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
9529+ struct snd_soc_codec *codec = socdev->codec;
9530+ int i;
9531+ u8 data[2];
9532+ u16 *cache = codec->reg_cache;
9533+
9534+ /* Sync reg_cache with the hardware */
9535+ for (i = 0; i < ARRAY_SIZE(wm8772_reg); i++) {
9536+ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
9537+ data[1] = cache[i] & 0x00ff;
9538+ codec->hw_write(codec->control_data, data, 2);
9539+ }
9540+ wm8772_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
9541+ wm8772_dapm_event(codec, codec->suspend_dapm_state);
9542+ return 0;
9543+}
9544+
9545+/*
9546+ * initialise the WM8772 driver
9547+ * register the mixer and dsp interfaces with the kernel
9548+ */
9549+static int wm8772_init(struct snd_soc_device *socdev)
9550+{
9551+ struct snd_soc_codec *codec = socdev->codec;
9552+ int reg, ret = 0;
9553+
9554+ codec->name = "WM8772";
9555+ codec->owner = THIS_MODULE;
9556+ codec->read = wm8772_read_reg_cache;
9557+ codec->write = wm8772_write;
9558+ codec->dapm_event = wm8772_dapm_event;
9559+ codec->dai = &wm8772_dai;
9560+ codec->num_dai = 1;
9561+ codec->reg_cache_size = ARRAY_SIZE(wm8772_reg);
9562+ codec->reg_cache =
9563+ kzalloc(sizeof(u16) * ARRAY_SIZE(wm8772_reg), GFP_KERNEL);
9564+ if (codec->reg_cache == NULL)
9565+ return -ENOMEM;
9566+ memcpy(codec->reg_cache, wm8772_reg,
9567+ sizeof(u16) * ARRAY_SIZE(wm8772_reg));
9568+ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm8772_reg);
9569+
9570+ wm8772_reset(codec);
9571+
9572+ /* register pcms */
9573+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
9574+ if(ret < 0) {
9575+ kfree(codec->reg_cache);
9576+ return ret;
9577+ }
9578+
9579+ /* power on device */
9580+ wm8772_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
9581+
9582+ /* set the update bits */
9583+ reg = wm8772_read_reg_cache(codec, WM8772_MDACVOL);
9584+ wm8772_write(codec, WM8772_MDACVOL, reg | 0x0100);
9585+ reg = wm8772_read_reg_cache(codec, WM8772_LDAC1VOL);
9586+ wm8772_write(codec, WM8772_LDAC1VOL, reg | 0x0100);
9587+ reg = wm8772_read_reg_cache(codec, WM8772_LDAC2VOL);
9588+ wm8772_write(codec, WM8772_LDAC2VOL, reg | 0x0100);
9589+ reg = wm8772_read_reg_cache(codec, WM8772_LDAC3VOL);
9590+ wm8772_write(codec, WM8772_LDAC3VOL, reg | 0x0100);
9591+ reg = wm8772_read_reg_cache(codec, WM8772_RDAC1VOL);
9592+ wm8772_write(codec, WM8772_RDAC1VOL, reg | 0x0100);
9593+ reg = wm8772_read_reg_cache(codec, WM8772_RDAC2VOL);
9594+ wm8772_write(codec, WM8772_RDAC2VOL, reg | 0x0100);
9595+ reg = wm8772_read_reg_cache(codec, WM8772_RDAC3VOL);
9596+ wm8772_write(codec, WM8772_RDAC3VOL, reg | 0x0100);
9597+
9598+ wm8772_add_controls(codec);
9599+ ret = snd_soc_register_card(socdev);
9600+ if (ret < 0) {
9601+ snd_soc_free_pcms(socdev);
9602+ snd_soc_dapm_free(socdev);
9603+ }
9604+
9605+ return ret;
9606+}
9607+
9608+static struct snd_soc_device *wm8772_socdev;
9609+
9610+static int wm8772_probe(struct platform_device *pdev)
9611+{
9612+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
9613+ struct wm8772_setup_data *setup;
9614+ struct snd_soc_codec *codec;
9615+ int ret = 0;
9616+
9617+ printk(KERN_INFO "WM8772 Audio Codec %s", WM8772_VERSION);
9618+
9619+ setup = socdev->codec_data;
9620+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
9621+ if (codec == NULL)
9622+ return -ENOMEM;
9623+
9624+ socdev->codec = codec;
9625+ mutex_init(&codec->mutex);
9626+ INIT_LIST_HEAD(&codec->dapm_widgets);
9627+ INIT_LIST_HEAD(&codec->dapm_paths);
9628+
9629+ wm8772_socdev = socdev;
9630+
9631+ /* Add other interfaces here */
9632+#warning do SPI device probe here and then call wm8772_init()
9633+
9634+ return ret;
9635+}
9636+
9637+/* power down chip */
9638+static int wm8772_remove(struct platform_device *pdev)
9639+{
9640+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
9641+ struct snd_soc_codec *codec = socdev->codec;
9642+
9643+ if (codec->control_data)
9644+ wm8772_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
9645+
9646+ snd_soc_free_pcms(socdev);
9647+ kfree(codec);
9648+
9649+ return 0;
9650+}
9651+
9652+struct snd_soc_codec_device soc_codec_dev_wm8772 = {
9653+ .probe = wm8772_probe,
9654+ .remove = wm8772_remove,
9655+ .suspend = wm8772_suspend,
9656+ .resume = wm8772_resume,
9657+};
9658+
9659+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8772);
9660+
9661+MODULE_DESCRIPTION("ASoC WM8772 driver");
9662+MODULE_AUTHOR("Liam Girdwood");
9663+MODULE_LICENSE("GPL");
9664Index: linux-2.6-pxa-new/sound/soc/codecs/wm8772.h
9665===================================================================
9666--- /dev/null
9667+++ linux-2.6-pxa-new/sound/soc/codecs/wm8772.h
9668@@ -0,0 +1,40 @@
9669+/*
9670+ * wm8772.h -- audio driver for WM8772
9671+ *
9672+ * Copyright 2005 Wolfson Microelectronics PLC.
9673+ * Author: Liam Girdwood
9674+ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
9675+ *
9676+ * This program is free software; you can redistribute it and/or modify it
9677+ * under the terms of the GNU General Public License as published by the
9678+ * Free Software Foundation; either version 2 of the License, or (at your
9679+ * option) any later version.
9680+ *
9681+ */
9682+
9683+#ifndef _WM8772_H
9684+#define _WM8772_H
9685+
9686+/* WM8772 register space */
9687+
9688+#define WM8772_LDAC1VOL 0x00
9689+#define WM8772_RDAC1VOL 0x01
9690+#define WM8772_DACCH 0x02
9691+#define WM8772_IFACE 0x03
9692+#define WM8772_LDAC2VOL 0x04
9693+#define WM8772_RDAC2VOL 0x05
9694+#define WM8772_LDAC3VOL 0x06
9695+#define WM8772_RDAC3VOL 0x07
9696+#define WM8772_MDACVOL 0x08
9697+#define WM8772_DACCTRL 0x09
9698+#define WM8772_DACRATE 0x0a
9699+#define WM8772_ADCRATE 0x0b
9700+#define WM8772_ADCCTRL 0x0c
9701+#define WM8772_RESET 0x1f
9702+
9703+#define WM8772_CACHE_REGNUM 10
9704+
9705+extern struct snd_soc_codec_dai wm8772_dai;
9706+extern struct snd_soc_codec_device soc_codec_dev_wm8772;
9707+
9708+#endif
9709Index: linux-2.6-pxa-new/sound/soc/codecs/wm8971.c
9710===================================================================
9711--- /dev/null
9712+++ linux-2.6-pxa-new/sound/soc/codecs/wm8971.c
9713@@ -0,0 +1,1214 @@
9714+/*
9715+ * wm8971.c -- WM8971 ALSA SoC Audio driver
9716+ *
9717+ * Copyright 2005 Lab126, Inc.
9718+ *
9719+ * Author: Kenneth Kiraly <kiraly@lab126.com>
9720+ *
9721+ * Based on wm8753.c by Liam Girdwood
9722+ *
9723+ * This program is free software; you can redistribute it and/or modify it
9724+ * under the terms of the GNU General Public License as published by the
9725+ * Free Software Foundation; either version 2 of the License, or (at your
9726+ * option) any later version.
9727+ */
9728+
9729+#include <linux/module.h>
9730+#include <linux/moduleparam.h>
9731+#include <linux/init.h>
9732+#include <linux/delay.h>
9733+#include <linux/pm.h>
9734+#include <linux/i2c.h>
9735+#include <linux/platform_device.h>
9736+#include <sound/driver.h>
9737+#include <sound/core.h>
9738+#include <sound/pcm.h>
9739+#include <sound/pcm_params.h>
9740+#include <sound/soc.h>
9741+#include <sound/soc-dapm.h>
9742+#include <sound/initval.h>
9743+
9744+#include "wm8971.h"
9745+
9746+#define AUDIO_NAME "wm8971"
9747+#define WM8971_VERSION "0.8"
9748+
9749+#undef WM8971_DEBUG
9750+
9751+#ifdef WM8971_DEBUG
9752+#define dbg(format, arg...) \
9753+ printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
9754+#else
9755+#define dbg(format, arg...) do {} while (0)
9756+#endif
9757+#define err(format, arg...) \
9758+ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
9759+#define info(format, arg...) \
9760+ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
9761+#define warn(format, arg...) \
9762+ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
9763+
9764+#define WM8971_REG_COUNT 43
9765+
9766+static struct workqueue_struct *wm8971_workq = NULL;
9767+static struct work_struct wm8971_dapm_work;
9768+
9769+/*
9770+ * wm8971 register cache
9771+ * We can't read the WM8971 register space when we
9772+ * are using 2 wire for device control, so we cache them instead.
9773+ */
9774+static const u16 wm8971_reg[] = {
9775+ 0x0097, 0x0097, 0x0079, 0x0079, /* 0 */
9776+ 0x0000, 0x0008, 0x0000, 0x000a, /* 4 */
9777+ 0x0000, 0x0000, 0x00ff, 0x00ff, /* 8 */
9778+ 0x000f, 0x000f, 0x0000, 0x0000, /* 12 */
9779+ 0x0000, 0x007b, 0x0000, 0x0032, /* 16 */
9780+ 0x0000, 0x00c3, 0x00c3, 0x00c0, /* 20 */
9781+ 0x0000, 0x0000, 0x0000, 0x0000, /* 24 */
9782+ 0x0000, 0x0000, 0x0000, 0x0000, /* 28 */
9783+ 0x0000, 0x0000, 0x0050, 0x0050, /* 32 */
9784+ 0x0050, 0x0050, 0x0050, 0x0050, /* 36 */
9785+ 0x0079, 0x0079, 0x0079, /* 40 */
9786+};
9787+
9788+#define WM8971_HIFI_DAIFMT \
9789+ (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_RIGHT_J | \
9790+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_NB_IF | SND_SOC_DAIFMT_IB_NF | \
9791+ SND_SOC_DAIFMT_IB_IF)
9792+
9793+#define WM8971_DIR \
9794+ (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
9795+
9796+#define WM8971_HIFI_FSB \
9797+ (SND_SOC_FSBD(1) | SND_SOC_FSBD(2) | SND_SOC_FSBD(4) | \
9798+ SND_SOC_FSBD(8) | SND_SOC_FSBD(16))
9799+
9800+#define WM8971_HIFI_RATES \
9801+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
9802+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
9803+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
9804+
9805+#define WM8971_HIFI_BITS \
9806+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
9807+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
9808+
9809+static struct snd_soc_dai_mode wm8971_modes[] = {
9810+ /* common codec frame and clock master modes */
9811+ /* 8k */
9812+ {
9813+ .fmt = WM8971_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
9814+ .pcmfmt = WM8971_HIFI_BITS,
9815+ .pcmrate = SNDRV_PCM_RATE_8000,
9816+ .pcmdir = WM8971_DIR,
9817+ .flags = SND_SOC_DAI_BFS_DIV,
9818+ .fs = 1536,
9819+ .bfs = WM8971_HIFI_FSB,
9820+ },
9821+ {
9822+ .fmt = WM8971_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
9823+ .pcmfmt = WM8971_HIFI_BITS,
9824+ .pcmrate = SNDRV_PCM_RATE_8000,
9825+ .pcmdir = WM8971_DIR,
9826+ .flags = SND_SOC_DAI_BFS_DIV,
9827+ .fs = 1408,
9828+ .bfs = WM8971_HIFI_FSB,
9829+ },
9830+ {
9831+ .fmt = WM8971_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
9832+ .pcmfmt = WM8971_HIFI_BITS,
9833+ .pcmrate = SNDRV_PCM_RATE_8000,
9834+ .pcmdir = WM8971_DIR,
9835+ .flags = SND_SOC_DAI_BFS_DIV,
9836+ .fs = 2304,
9837+ .bfs = WM8971_HIFI_FSB,
9838+ },
9839+ {
9840+ .fmt = WM8971_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
9841+ .pcmfmt = WM8971_HIFI_BITS,
9842+ .pcmrate = SNDRV_PCM_RATE_8000,
9843+ .pcmdir = WM8971_DIR,
9844+ .flags = SND_SOC_DAI_BFS_DIV,
9845+ .fs = 2112,
9846+ .bfs = WM8971_HIFI_FSB,
9847+ },
9848+ {
9849+ .fmt = WM8971_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
9850+ .pcmfmt = WM8971_HIFI_BITS,
9851+ .pcmrate = SNDRV_PCM_RATE_8000,
9852+ .pcmdir = WM8971_DIR,
9853+ .flags = SND_SOC_DAI_BFS_DIV,
9854+ .fs = 1500,
9855+ .bfs = WM8971_HIFI_FSB,
9856+ },
9857+
9858+ /* 11.025k */
9859+ {
9860+ .fmt = WM8971_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
9861+ .pcmfmt = WM8971_HIFI_BITS,
9862+ .pcmrate = SNDRV_PCM_RATE_11025,
9863+ .pcmdir = WM8971_DIR,
9864+ .flags = SND_SOC_DAI_BFS_DIV,
9865+ .fs = 1024,
9866+ .bfs = WM8971_HIFI_FSB,
9867+ },
9868+ {
9869+ .fmt = WM8971_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
9870+ .pcmfmt = WM8971_HIFI_BITS,
9871+ .pcmrate = SNDRV_PCM_RATE_11025,
9872+ .pcmdir = WM8971_DIR,
9873+ .flags = SND_SOC_DAI_BFS_DIV,
9874+ .fs = 1536,
9875+ .bfs = WM8971_HIFI_FSB,
9876+ },
9877+ {
9878+ .fmt = WM8971_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
9879+ .pcmfmt = WM8971_HIFI_BITS,
9880+ .pcmrate = SNDRV_PCM_RATE_11025,
9881+ .pcmdir = WM8971_DIR,
9882+ .flags = SND_SOC_DAI_BFS_DIV,
9883+ .fs = 1088,
9884+ .bfs = WM8971_HIFI_FSB,
9885+ },
9886+
9887+ /* 16k */
9888+ {
9889+ .fmt = WM8971_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
9890+ .pcmfmt = WM8971_HIFI_BITS,
9891+ .pcmrate = SNDRV_PCM_RATE_16000,
9892+ .pcmdir = WM8971_DIR,
9893+ .flags = SND_SOC_DAI_BFS_DIV,
9894+ .fs = 768,
9895+ .bfs = WM8971_HIFI_FSB,
9896+ },
9897+ {
9898+ .fmt = WM8971_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
9899+ .pcmfmt = WM8971_HIFI_BITS,
9900+ .pcmrate = SNDRV_PCM_RATE_16000,
9901+ .pcmdir = WM8971_DIR,
9902+ .flags = SND_SOC_DAI_BFS_DIV,
9903+ .fs = 1152,
9904+ .bfs = WM8971_HIFI_FSB
9905+ },
9906+ {
9907+ .fmt = WM8971_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
9908+ .pcmfmt = WM8971_HIFI_BITS,
9909+ .pcmrate = SNDRV_PCM_RATE_16000,
9910+ .pcmdir = WM8971_DIR,
9911+ .flags = SND_SOC_DAI_BFS_DIV,
9912+ .fs = 750,
9913+ .bfs = WM8971_HIFI_FSB,
9914+ },
9915+
9916+ /* 22.05k */
9917+ {
9918+ .fmt = WM8971_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
9919+ .pcmfmt = WM8971_HIFI_BITS,
9920+ .pcmrate = SNDRV_PCM_RATE_22050,
9921+ .pcmdir = WM8971_DIR,
9922+ .flags = SND_SOC_DAI_BFS_DIV,
9923+ .fs = 512,
9924+ .bfs = WM8971_HIFI_FSB,
9925+ },
9926+ {
9927+ .fmt = WM8971_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
9928+ .pcmfmt = WM8971_HIFI_BITS,
9929+ .pcmrate = SNDRV_PCM_RATE_22050,
9930+ .pcmdir = WM8971_DIR,
9931+ .flags = SND_SOC_DAI_BFS_DIV,
9932+ .fs = 768,
9933+ .bfs = WM8971_HIFI_FSB,
9934+ },
9935+ {
9936+ .fmt = WM8971_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
9937+ .pcmfmt = WM8971_HIFI_BITS,
9938+ .pcmrate = SNDRV_PCM_RATE_22050,
9939+ .pcmdir = WM8971_DIR,
9940+ .flags = SND_SOC_DAI_BFS_DIV,
9941+ .fs = 544,
9942+ .bfs = WM8971_HIFI_FSB,
9943+ },
9944+
9945+ /* 32k */
9946+ {
9947+ .fmt = WM8971_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
9948+ .pcmfmt = WM8971_HIFI_BITS,
9949+ .pcmrate = SNDRV_PCM_RATE_32000,
9950+ .pcmdir = WM8971_DIR,
9951+ .flags = SND_SOC_DAI_BFS_DIV,
9952+ .fs = 384,
9953+ .bfs = WM8971_HIFI_FSB,
9954+ },
9955+ {
9956+ .fmt = WM8971_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
9957+ .pcmfmt = WM8971_HIFI_BITS,
9958+ .pcmrate = SNDRV_PCM_RATE_32000,
9959+ .pcmdir = WM8971_DIR,
9960+ .flags = SND_SOC_DAI_BFS_DIV,
9961+ .fs = 576,
9962+ .bfs = WM8971_HIFI_FSB,
9963+ },
9964+ {
9965+ .fmt = WM8971_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
9966+ .pcmfmt = WM8971_HIFI_BITS,
9967+ .pcmrate = SNDRV_PCM_RATE_32000,
9968+ .pcmdir = WM8971_DIR,
9969+ .flags = SND_SOC_DAI_BFS_DIV,
9970+ .fs = 375,
9971+ .bfs = WM8971_HIFI_FSB,
9972+ },
9973+
9974+ /* 44.1k & 48k */
9975+ {
9976+ .fmt = WM8971_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
9977+ .pcmfmt = WM8971_HIFI_BITS,
9978+ .pcmrate = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
9979+ .pcmdir = WM8971_DIR,
9980+ .flags = SND_SOC_DAI_BFS_DIV,
9981+ .fs = 256,
9982+ .bfs = WM8971_HIFI_FSB,
9983+ },
9984+ {
9985+ .fmt = WM8971_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
9986+ .pcmfmt = WM8971_HIFI_BITS,
9987+ .pcmrate = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
9988+ .pcmdir = WM8971_DIR,
9989+ .flags = SND_SOC_DAI_BFS_DIV,
9990+ .fs = 384,
9991+ .bfs = WM8971_HIFI_FSB,
9992+ },
9993+ {
9994+ .fmt = WM8971_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
9995+ .pcmfmt = WM8971_HIFI_BITS,
9996+ .pcmrate = SNDRV_PCM_RATE_44100,
9997+ .pcmdir = WM8971_DIR,
9998+ .flags = SND_SOC_DAI_BFS_DIV,
9999+ .fs = 272,
10000+ .bfs = WM8971_HIFI_FSB,
10001+ },
10002+ {
10003+ .fmt = WM8971_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
10004+ .pcmfmt = WM8971_HIFI_BITS,
10005+ .pcmrate = SNDRV_PCM_RATE_48000,
10006+ .pcmdir = WM8971_DIR,
10007+ .flags = SND_SOC_DAI_BFS_DIV,
10008+ .fs = 250,
10009+ .bfs = WM8971_HIFI_FSB,
10010+ },
10011+
10012+ /* 88.2k & 96k */
10013+ {
10014+ .fmt = WM8971_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
10015+ .pcmfmt = WM8971_HIFI_BITS,
10016+ .pcmrate = SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000,
10017+ .pcmdir = WM8971_DIR,
10018+ .flags = SND_SOC_DAI_BFS_DIV,
10019+ .fs = 128,
10020+ .bfs = WM8971_HIFI_FSB,
10021+ },
10022+ {
10023+ .fmt = WM8971_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
10024+ .pcmfmt = WM8971_HIFI_BITS,
10025+ .pcmrate = SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000,
10026+ .pcmdir = WM8971_DIR,
10027+ .flags = SND_SOC_DAI_BFS_DIV,
10028+ .fs = 192,
10029+ .bfs = WM8971_HIFI_FSB,
10030+ },
10031+ {
10032+ .fmt = WM8971_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
10033+ .pcmfmt = WM8971_HIFI_BITS,
10034+ .pcmrate = SNDRV_PCM_RATE_88200,
10035+ .pcmdir = WM8971_DIR,
10036+ .flags = SND_SOC_DAI_BFS_DIV,
10037+ .fs = 136,
10038+ .bfs = WM8971_HIFI_FSB,
10039+ },
10040+ {
10041+ .fmt = WM8971_HIFI_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
10042+ .pcmfmt = WM8971_HIFI_BITS,
10043+ .pcmrate = SNDRV_PCM_RATE_96000,
10044+ .pcmdir = WM8971_DIR,
10045+ .flags = SND_SOC_DAI_BFS_DIV,
10046+ .fs = 125,
10047+ .bfs = WM8971_HIFI_FSB,
10048+ },
10049+
10050+ /* codec frame and clock slave modes */
10051+ {
10052+ .fmt = WM8971_HIFI_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
10053+ .pcmfmt = WM8971_HIFI_BITS,
10054+ .pcmrate = WM8971_HIFI_RATES,
10055+ .pcmdir = WM8971_DIR,
10056+ .flags = SND_SOC_DAI_BFS_DIV,
10057+ .fs = SND_SOC_FS_ALL,
10058+ .bfs = SND_SOC_FSB_ALL,
10059+ },
10060+};
10061+
10062+static inline unsigned int wm8971_read_reg_cache(struct snd_soc_codec *codec,
10063+ unsigned int reg)
10064+{
10065+ u16 *cache = codec->reg_cache;
10066+ if (reg < WM8971_REG_COUNT)
10067+ return cache[reg];
10068+
10069+ return -1;
10070+}
10071+
10072+static inline void wm8971_write_reg_cache(struct snd_soc_codec *codec,
10073+ unsigned int reg, unsigned int value)
10074+{
10075+ u16 *cache = codec->reg_cache;
10076+ if (reg < WM8971_REG_COUNT)
10077+ cache[reg] = value;
10078+}
10079+
10080+static int wm8971_write(struct snd_soc_codec *codec, unsigned int reg,
10081+ unsigned int value)
10082+{
10083+ u8 data[2];
10084+
10085+ /* data is
10086+ * D15..D9 WM8753 register offset
10087+ * D8...D0 register data
10088+ */
10089+ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
10090+ data[1] = value & 0x00ff;
10091+
10092+ wm8971_write_reg_cache (codec, reg, value);
10093+ if (codec->hw_write(codec->control_data, data, 2) == 2)
10094+ return 0;
10095+ else
10096+ return -EIO;
10097+}
10098+
10099+#define wm8971_reset(c) wm8971_write(c, WM8971_RESET, 0)
10100+
10101+/* WM8971 Controls */
10102+static const char *wm8971_bass[] = { "Linear Control", "Adaptive Boost" };
10103+static const char *wm8971_bass_filter[] = { "130Hz @ 48kHz",
10104+ "200Hz @ 48kHz" };
10105+static const char *wm8971_treble[] = { "8kHz", "4kHz" };
10106+static const char *wm8971_alc_func[] = { "Off", "Right", "Left", "Stereo" };
10107+static const char *wm8971_ng_type[] = { "Constant PGA Gain",
10108+ "Mute ADC Output" };
10109+static const char *wm8971_deemp[] = { "None", "32kHz", "44.1kHz", "48kHz" };
10110+static const char *wm8971_mono_mux[] = {"Stereo", "Mono (Left)",
10111+ "Mono (Right)", "Digital Mono"};
10112+static const char *wm8971_dac_phase[] = { "Non Inverted", "Inverted" };
10113+static const char *wm8971_lline_mux[] = {"Line", "NC", "NC", "PGA",
10114+ "Differential"};
10115+static const char *wm8971_rline_mux[] = {"Line", "Mic", "NC", "PGA",
10116+ "Differential"};
10117+static const char *wm8971_lpga_sel[] = {"Line", "NC", "NC", "Differential"};
10118+static const char *wm8971_rpga_sel[] = {"Line", "Mic", "NC", "Differential"};
10119+static const char *wm8971_adcpol[] = {"Normal", "L Invert", "R Invert",
10120+ "L + R Invert"};
10121+
10122+static const struct soc_enum wm8971_enum[] = {
10123+ SOC_ENUM_SINGLE(WM8971_BASS, 7, 2, wm8971_bass), /* 0 */
10124+ SOC_ENUM_SINGLE(WM8971_BASS, 6, 2, wm8971_bass_filter),
10125+ SOC_ENUM_SINGLE(WM8971_TREBLE, 6, 2, wm8971_treble),
10126+ SOC_ENUM_SINGLE(WM8971_ALC1, 7, 4, wm8971_alc_func),
10127+ SOC_ENUM_SINGLE(WM8971_NGATE, 1, 2, wm8971_ng_type), /* 4 */
10128+ SOC_ENUM_SINGLE(WM8971_ADCDAC, 1, 4, wm8971_deemp),
10129+ SOC_ENUM_SINGLE(WM8971_ADCTL1, 4, 4, wm8971_mono_mux),
10130+ SOC_ENUM_SINGLE(WM8971_ADCTL1, 1, 2, wm8971_dac_phase),
10131+ SOC_ENUM_SINGLE(WM8971_LOUTM1, 0, 5, wm8971_lline_mux), /* 8 */
10132+ SOC_ENUM_SINGLE(WM8971_ROUTM1, 0, 5, wm8971_rline_mux),
10133+ SOC_ENUM_SINGLE(WM8971_LADCIN, 6, 4, wm8971_lpga_sel),
10134+ SOC_ENUM_SINGLE(WM8971_RADCIN, 6, 4, wm8971_rpga_sel),
10135+ SOC_ENUM_SINGLE(WM8971_ADCDAC, 5, 4, wm8971_adcpol), /* 12 */
10136+ SOC_ENUM_SINGLE(WM8971_ADCIN, 6, 4, wm8971_mono_mux),
10137+};
10138+
10139+static const struct snd_kcontrol_new wm8971_snd_controls[] = {
10140+ SOC_DOUBLE_R("Capture Volume", WM8971_LINVOL, WM8971_RINVOL, 0, 63, 0),
10141+ SOC_DOUBLE_R("Capture ZC Switch", WM8971_LINVOL, WM8971_RINVOL, 6, 1, 0),
10142+ SOC_DOUBLE_R("Capture Switch", WM8971_LINVOL, WM8971_RINVOL, 7, 1, 1),
10143+
10144+ SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8971_LOUT1V,
10145+ WM8971_ROUT1V, 7, 1, 0),
10146+ SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8971_LOUT2V,
10147+ WM8971_ROUT2V, 7, 1, 0),
10148+ SOC_SINGLE("Mono Playback ZC Switch", WM8971_MOUTV, 7, 1, 0),
10149+
10150+ SOC_DOUBLE_R("PCM Volume", WM8971_LDAC, WM8971_RDAC, 0, 255, 0),
10151+
10152+ SOC_DOUBLE_R("Bypass Left Playback Volume", WM8971_LOUTM1,
10153+ WM8971_LOUTM2, 4, 7, 1),
10154+ SOC_DOUBLE_R("Bypass Right Playback Volume", WM8971_ROUTM1,
10155+ WM8971_ROUTM2, 4, 7, 1),
10156+ SOC_DOUBLE_R("Bypass Mono Playback Volume", WM8971_MOUTM1,
10157+ WM8971_MOUTM2, 4, 7, 1),
10158+
10159+ SOC_DOUBLE_R("Headphone Playback Volume", WM8971_LOUT1V,
10160+ WM8971_ROUT1V, 0, 127, 0),
10161+ SOC_DOUBLE_R("Speaker Playback Volume", WM8971_LOUT2V,
10162+ WM8971_ROUT2V, 0, 127, 0),
10163+
10164+ SOC_ENUM("Bass Boost", wm8971_enum[0]),
10165+ SOC_ENUM("Bass Filter", wm8971_enum[1]),
10166+ SOC_SINGLE("Bass Volume", WM8971_BASS, 0, 7, 1),
10167+
10168+ SOC_SINGLE("Treble Volume", WM8971_TREBLE, 0, 7, 0),
10169+ SOC_ENUM("Treble Cut-off", wm8971_enum[2]),
10170+
10171+ SOC_SINGLE("Capture Filter Switch", WM8971_ADCDAC, 0, 1, 1),
10172+
10173+ SOC_SINGLE("ALC Target Volume", WM8971_ALC1, 0, 7, 0),
10174+ SOC_SINGLE("ALC Max Volume", WM8971_ALC1, 4, 7, 0),
10175+
10176+ SOC_SINGLE("ALC Capture Target Volume", WM8971_ALC1, 0, 7, 0),
10177+ SOC_SINGLE("ALC Capture Max Volume", WM8971_ALC1, 4, 7, 0),
10178+ SOC_ENUM("ALC Capture Function", wm8971_enum[3]),
10179+ SOC_SINGLE("ALC Capture ZC Switch", WM8971_ALC2, 7, 1, 0),
10180+ SOC_SINGLE("ALC Capture Hold Time", WM8971_ALC2, 0, 15, 0),
10181+ SOC_SINGLE("ALC Capture Decay Time", WM8971_ALC3, 4, 15, 0),
10182+ SOC_SINGLE("ALC Capture Attack Time", WM8971_ALC3, 0, 15, 0),
10183+ SOC_SINGLE("ALC Capture NG Threshold", WM8971_NGATE, 3, 31, 0),
10184+ SOC_ENUM("ALC Capture NG Type", wm8971_enum[4]),
10185+ SOC_SINGLE("ALC Capture NG Switch", WM8971_NGATE, 0, 1, 0),
10186+
10187+ SOC_SINGLE("Capture 6dB Attenuate", WM8971_ADCDAC, 8, 1, 0),
10188+ SOC_SINGLE("Playback 6dB Attenuate", WM8971_ADCDAC, 7, 1, 0),
10189+
10190+ SOC_ENUM("Playback De-emphasis", wm8971_enum[5]),
10191+ SOC_ENUM("Playback Function", wm8971_enum[6]),
10192+ SOC_ENUM("Playback Phase", wm8971_enum[7]),
10193+
10194+ SOC_DOUBLE_R("Mic Boost", WM8971_LADCIN, WM8971_RADCIN, 4, 3, 0),
10195+};
10196+
10197+/* add non-DAPM controls */
10198+static int wm8971_add_controls(struct snd_soc_codec *codec)
10199+{
10200+ int err, i;
10201+
10202+ for (i = 0; i < ARRAY_SIZE(wm8971_snd_controls); i++) {
10203+ err = snd_ctl_add(codec->card,
10204+ snd_soc_cnew(&wm8971_snd_controls[i],codec, NULL));
10205+ if (err < 0)
10206+ return err;
10207+ }
10208+
10209+ return 0;
10210+}
10211+
10212+/*
10213+ * DAPM Controls
10214+ */
10215+
10216+/* Left Mixer */
10217+static const struct snd_kcontrol_new wm8971_left_mixer_controls[] = {
10218+SOC_DAPM_SINGLE("Playback Switch", WM8971_LOUTM1, 8, 1, 0),
10219+SOC_DAPM_SINGLE("Left Bypass Switch", WM8971_LOUTM1, 7, 1, 0),
10220+SOC_DAPM_SINGLE("Right Playback Switch", WM8971_LOUTM2, 8, 1, 0),
10221+SOC_DAPM_SINGLE("Right Bypass Switch", WM8971_LOUTM2, 7, 1, 0),
10222+};
10223+
10224+/* Right Mixer */
10225+static const struct snd_kcontrol_new wm8971_right_mixer_controls[] = {
10226+SOC_DAPM_SINGLE("Left Playback Switch", WM8971_ROUTM1, 8, 1, 0),
10227+SOC_DAPM_SINGLE("Left Bypass Switch", WM8971_ROUTM1, 7, 1, 0),
10228+SOC_DAPM_SINGLE("Playback Switch", WM8971_ROUTM2, 8, 1, 0),
10229+SOC_DAPM_SINGLE("Right Bypass Switch", WM8971_ROUTM2, 7, 1, 0),
10230+};
10231+
10232+/* Mono Mixer */
10233+static const struct snd_kcontrol_new wm8971_mono_mixer_controls[] = {
10234+SOC_DAPM_SINGLE("Left Playback Switch", WM8971_MOUTM1, 8, 1, 0),
10235+SOC_DAPM_SINGLE("Left Bypass Switch", WM8971_MOUTM1, 7, 1, 0),
10236+SOC_DAPM_SINGLE("Right Playback Switch", WM8971_MOUTM2, 8, 1, 0),
10237+SOC_DAPM_SINGLE("Right Bypass Switch", WM8971_MOUTM2, 7, 1, 0),
10238+};
10239+
10240+/* Left Line Mux */
10241+static const struct snd_kcontrol_new wm8971_left_line_controls =
10242+SOC_DAPM_ENUM("Route", wm8971_enum[8]);
10243+
10244+/* Right Line Mux */
10245+static const struct snd_kcontrol_new wm8971_right_line_controls =
10246+SOC_DAPM_ENUM("Route", wm8971_enum[9]);
10247+
10248+/* Left PGA Mux */
10249+static const struct snd_kcontrol_new wm8971_left_pga_controls =
10250+SOC_DAPM_ENUM("Route", wm8971_enum[10]);
10251+
10252+/* Right PGA Mux */
10253+static const struct snd_kcontrol_new wm8971_right_pga_controls =
10254+SOC_DAPM_ENUM("Route", wm8971_enum[11]);
10255+
10256+/* Mono ADC Mux */
10257+static const struct snd_kcontrol_new wm8971_monomux_controls =
10258+SOC_DAPM_ENUM("Route", wm8971_enum[13]);
10259+
10260+static const struct snd_soc_dapm_widget wm8971_dapm_widgets[] = {
10261+ SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
10262+ &wm8971_left_mixer_controls[0],
10263+ ARRAY_SIZE(wm8971_left_mixer_controls)),
10264+ SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
10265+ &wm8971_right_mixer_controls[0],
10266+ ARRAY_SIZE(wm8971_right_mixer_controls)),
10267+ SND_SOC_DAPM_MIXER("Mono Mixer", WM8971_PWR2, 2, 0,
10268+ &wm8971_mono_mixer_controls[0],
10269+ ARRAY_SIZE(wm8971_mono_mixer_controls)),
10270+
10271+ SND_SOC_DAPM_PGA("Right Out 2", WM8971_PWR2, 3, 0, NULL, 0),
10272+ SND_SOC_DAPM_PGA("Left Out 2", WM8971_PWR2, 4, 0, NULL, 0),
10273+ SND_SOC_DAPM_PGA("Right Out 1", WM8971_PWR2, 5, 0, NULL, 0),
10274+ SND_SOC_DAPM_PGA("Left Out 1", WM8971_PWR2, 6, 0, NULL, 0),
10275+ SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8971_PWR2, 7, 0),
10276+ SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8971_PWR2, 8, 0),
10277+ SND_SOC_DAPM_PGA("Mono Out 1", WM8971_PWR2, 2, 0, NULL, 0),
10278+
10279+ SND_SOC_DAPM_MICBIAS("Mic Bias", WM8971_PWR1, 1, 0),
10280+ SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8971_PWR1, 2, 0),
10281+ SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8971_PWR1, 3, 0),
10282+
10283+ SND_SOC_DAPM_MUX("Left PGA Mux", WM8971_PWR1, 5, 0,
10284+ &wm8971_left_pga_controls),
10285+ SND_SOC_DAPM_MUX("Right PGA Mux", WM8971_PWR1, 4, 0,
10286+ &wm8971_right_pga_controls),
10287+ SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0,
10288+ &wm8971_left_line_controls),
10289+ SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0,
10290+ &wm8971_right_line_controls),
10291+
10292+ SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0,
10293+ &wm8971_monomux_controls),
10294+ SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0,
10295+ &wm8971_monomux_controls),
10296+
10297+ SND_SOC_DAPM_OUTPUT("LOUT1"),
10298+ SND_SOC_DAPM_OUTPUT("ROUT1"),
10299+ SND_SOC_DAPM_OUTPUT("LOUT2"),
10300+ SND_SOC_DAPM_OUTPUT("ROUT2"),
10301+ SND_SOC_DAPM_OUTPUT("MONO"),
10302+
10303+ SND_SOC_DAPM_INPUT("LINPUT1"),
10304+ SND_SOC_DAPM_INPUT("RINPUT1"),
10305+ SND_SOC_DAPM_INPUT("MIC"),
10306+};
10307+
10308+static const char *audio_map[][3] = {
10309+ /* left mixer */
10310+ {"Left Mixer", "Playback Switch", "Left DAC"},
10311+ {"Left Mixer", "Left Bypass Switch", "Left Line Mux"},
10312+ {"Left Mixer", "Right Playback Switch", "Right DAC"},
10313+ {"Left Mixer", "Right Bypass Switch", "Right Line Mux"},
10314+
10315+ /* right mixer */
10316+ {"Right Mixer", "Left Playback Switch", "Left DAC"},
10317+ {"Right Mixer", "Left Bypass Switch", "Left Line Mux"},
10318+ {"Right Mixer", "Playback Switch", "Right DAC"},
10319+ {"Right Mixer", "Right Bypass Switch", "Right Line Mux"},
10320+
10321+ /* left out 1 */
10322+ {"Left Out 1", NULL, "Left Mixer"},
10323+ {"LOUT1", NULL, "Left Out 1"},
10324+
10325+ /* left out 2 */
10326+ {"Left Out 2", NULL, "Left Mixer"},
10327+ {"LOUT2", NULL, "Left Out 2"},
10328+
10329+ /* right out 1 */
10330+ {"Right Out 1", NULL, "Right Mixer"},
10331+ {"ROUT1", NULL, "Right Out 1"},
10332+
10333+ /* right out 2 */
10334+ {"Right Out 2", NULL, "Right Mixer"},
10335+ {"ROUT2", NULL, "Right Out 2"},
10336+
10337+ /* mono mixer */
10338+ {"Mono Mixer", "Left Playback Switch", "Left DAC"},
10339+ {"Mono Mixer", "Left Bypass Switch", "Left Line Mux"},
10340+ {"Mono Mixer", "Right Playback Switch", "Right DAC"},
10341+ {"Mono Mixer", "Right Bypass Switch", "Right Line Mux"},
10342+
10343+ /* mono out */
10344+ {"Mono Out", NULL, "Mono Mixer"},
10345+ {"MONO1", NULL, "Mono Out"},
10346+
10347+ /* Left Line Mux */
10348+ {"Left Line Mux", "Line", "LINPUT1"},
10349+ {"Left Line Mux", "PGA", "Left PGA Mux"},
10350+ {"Left Line Mux", "Differential", "Differential Mux"},
10351+
10352+ /* Right Line Mux */
10353+ {"Right Line Mux", "Line", "RINPUT1"},
10354+ {"Right Line Mux", "Mic", "MIC"},
10355+ {"Right Line Mux", "PGA", "Right PGA Mux"},
10356+ {"Right Line Mux", "Differential", "Differential Mux"},
10357+
10358+ /* Left PGA Mux */
10359+ {"Left PGA Mux", "Line", "LINPUT1"},
10360+ {"Left PGA Mux", "Differential", "Differential Mux"},
10361+
10362+ /* Right PGA Mux */
10363+ {"Right PGA Mux", "Line", "RINPUT1"},
10364+ {"Right PGA Mux", "Differential", "Differential Mux"},
10365+
10366+ /* Differential Mux */
10367+ {"Differential Mux", "Line", "LINPUT1"},
10368+ {"Differential Mux", "Line", "RINPUT1"},
10369+
10370+ /* Left ADC Mux */
10371+ {"Left ADC Mux", "Stereo", "Left PGA Mux"},
10372+ {"Left ADC Mux", "Mono (Left)", "Left PGA Mux"},
10373+ {"Left ADC Mux", "Digital Mono", "Left PGA Mux"},
10374+
10375+ /* Right ADC Mux */
10376+ {"Right ADC Mux", "Stereo", "Right PGA Mux"},
10377+ {"Right ADC Mux", "Mono (Right)", "Right PGA Mux"},
10378+ {"Right ADC Mux", "Digital Mono", "Right PGA Mux"},
10379+
10380+ /* ADC */
10381+ {"Left ADC", NULL, "Left ADC Mux"},
10382+ {"Right ADC", NULL, "Right ADC Mux"},
10383+
10384+ /* terminator */
10385+ {NULL, NULL, NULL},
10386+};
10387+
10388+static int wm8971_add_widgets(struct snd_soc_codec *codec)
10389+{
10390+ int i;
10391+
10392+ for(i = 0; i < ARRAY_SIZE(wm8971_dapm_widgets); i++) {
10393+ snd_soc_dapm_new_control(codec, &wm8971_dapm_widgets[i]);
10394+ }
10395+
10396+ /* set up audio path audio_mapnects */
10397+ for(i = 0; audio_map[i][0] != NULL; i++) {
10398+ snd_soc_dapm_connect_input(codec, audio_map[i][0],
10399+ audio_map[i][1], audio_map[i][2]);
10400+ }
10401+
10402+ snd_soc_dapm_new_widgets(codec);
10403+ return 0;
10404+}
10405+
10406+struct _coeff_div {
10407+ u32 mclk;
10408+ u32 rate;
10409+ u16 fs;
10410+ u8 sr:5;
10411+ u8 usb:1;
10412+};
10413+
10414+/* codec hifi mclk clock divider coefficients */
10415+static const struct _coeff_div coeff_div[] = {
10416+ /* 8k */
10417+ {12288000, 8000, 1536, 0x6, 0x0},
10418+ {11289600, 8000, 1408, 0x16, 0x0},
10419+ {18432000, 8000, 2304, 0x7, 0x0},
10420+ {16934400, 8000, 2112, 0x17, 0x0},
10421+ {12000000, 8000, 1500, 0x6, 0x1},
10422+
10423+ /* 11.025k */
10424+ {11289600, 11025, 1024, 0x18, 0x0},
10425+ {16934400, 11025, 1536, 0x19, 0x0},
10426+ {12000000, 11025, 1088, 0x19, 0x1},
10427+
10428+ /* 16k */
10429+ {12288000, 16000, 768, 0xa, 0x0},
10430+ {18432000, 16000, 1152, 0xb, 0x0},
10431+ {12000000, 16000, 750, 0xa, 0x1},
10432+
10433+ /* 22.05k */
10434+ {11289600, 22050, 512, 0x1a, 0x0},
10435+ {16934400, 22050, 768, 0x1b, 0x0},
10436+ {12000000, 22050, 544, 0x1b, 0x1},
10437+
10438+ /* 32k */
10439+ {12288000, 32000, 384, 0xc, 0x0},
10440+ {18432000, 32000, 576, 0xd, 0x0},
10441+ {12000000, 32000, 375, 0xa, 0x1},
10442+
10443+ /* 44.1k */
10444+ {11289600, 44100, 256, 0x10, 0x0},
10445+ {16934400, 44100, 384, 0x11, 0x0},
10446+ {12000000, 44100, 272, 0x11, 0x1},
10447+
10448+ /* 48k */
10449+ {12288000, 48000, 256, 0x0, 0x0},
10450+ {18432000, 48000, 384, 0x1, 0x0},
10451+ {12000000, 48000, 250, 0x0, 0x1},
10452+
10453+ /* 88.2k */
10454+ {11289600, 88200, 128, 0x1e, 0x0},
10455+ {16934400, 88200, 192, 0x1f, 0x0},
10456+ {12000000, 88200, 136, 0x1f, 0x1},
10457+
10458+ /* 96k */
10459+ {12288000, 96000, 128, 0xe, 0x0},
10460+ {18432000, 96000, 192, 0xf, 0x0},
10461+ {12000000, 96000, 125, 0xe, 0x1},
10462+};
10463+
10464+static int get_coeff(int mclk, int rate)
10465+{
10466+ int i;
10467+
10468+ for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
10469+ if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
10470+ return i;
10471+ }
10472+ return -EINVAL;
10473+}
10474+
10475+/* WM8971 supports numerous input clocks per sample rate */
10476+static unsigned int wm8971_config_sysclk(struct snd_soc_codec_dai *dai,
10477+ struct snd_soc_clock_info *info, unsigned int clk)
10478+{
10479+ dai->mclk = 0;
10480+
10481+ /* check that the calculated FS and rate actually match a clock from
10482+ * the machine driver */
10483+ if (info->fs * info->rate == clk)
10484+ dai->mclk = clk;
10485+
10486+ return dai->mclk;
10487+}
10488+
10489+static int wm8971_pcm_prepare(struct snd_pcm_substream *substream)
10490+{
10491+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
10492+ struct snd_soc_device *socdev = rtd->socdev;
10493+ struct snd_soc_codec *codec = socdev->codec;
10494+ u16 iface = 0, bfs, srate = 0;
10495+ int i = get_coeff(rtd->codec_dai->mclk,
10496+ snd_soc_get_rate(rtd->codec_dai->dai_runtime.pcmrate));
10497+
10498+ /* is coefficient valid ? */
10499+ if (i < 0)
10500+ return i;
10501+
10502+ bfs = SND_SOC_FSBD_REAL(rtd->codec_dai->dai_runtime.bfs);
10503+
10504+ /* set master/slave audio interface */
10505+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
10506+ case SND_SOC_DAIFMT_CBM_CFM:
10507+ iface |= 0x0040;
10508+ break;
10509+ case SND_SOC_DAIFMT_CBS_CFS:
10510+ break;
10511+ }
10512+
10513+ /* interface format */
10514+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
10515+ case SND_SOC_DAIFMT_I2S:
10516+ iface |= 0x0002;
10517+ break;
10518+ case SND_SOC_DAIFMT_RIGHT_J:
10519+ break;
10520+ case SND_SOC_DAIFMT_LEFT_J:
10521+ iface |= 0x0001;
10522+ break;
10523+ case SND_SOC_DAIFMT_DSP_A:
10524+ iface |= 0x0003;
10525+ break;
10526+ case SND_SOC_DAIFMT_DSP_B:
10527+ iface |= 0x0013;
10528+ break;
10529+ }
10530+
10531+ /* bit size */
10532+ switch (rtd->codec_dai->dai_runtime.pcmfmt) {
10533+ case SNDRV_PCM_FMTBIT_S16_LE:
10534+ break;
10535+ case SNDRV_PCM_FMTBIT_S20_3LE:
10536+ iface |= 0x0004;
10537+ break;
10538+ case SNDRV_PCM_FMTBIT_S24_LE:
10539+ iface |= 0x0008;
10540+ break;
10541+ case SNDRV_PCM_FMTBIT_S32_LE:
10542+ iface |= 0x000c;
10543+ break;
10544+ }
10545+
10546+ /* clock inversion */
10547+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_INV_MASK) {
10548+ case SND_SOC_DAIFMT_NB_NF:
10549+ break;
10550+ case SND_SOC_DAIFMT_IB_IF:
10551+ iface |= 0x0090;
10552+ break;
10553+ case SND_SOC_DAIFMT_IB_NF:
10554+ iface |= 0x0080;
10555+ break;
10556+ case SND_SOC_DAIFMT_NB_IF:
10557+ iface |= 0x0010;
10558+ break;
10559+ }
10560+
10561+ /* set bclk divisor rate */
10562+ switch (bfs) {
10563+ case 1:
10564+ break;
10565+ case 4:
10566+ srate |= (0x1 << 7);
10567+ break;
10568+ case 8:
10569+ srate |= (0x2 << 7);
10570+ break;
10571+ case 16:
10572+ srate |= (0x3 << 7);
10573+ break;
10574+ }
10575+
10576+ /* set iface & srate */
10577+ wm8971_write(codec, WM8971_AUDIO, iface);
10578+ wm8971_write(codec, WM8971_SRATE, srate |
10579+ (coeff_div[i].sr << 1) | coeff_div[i].usb);
10580+ return 0;
10581+}
10582+
10583+static int wm8971_mute(struct snd_soc_codec *codec,
10584+ struct snd_soc_codec_dai *dai, int mute)
10585+{
10586+ u16 mute_reg = wm8971_read_reg_cache(codec, WM8971_ADCDAC) & 0xfff7;
10587+ if (mute)
10588+ wm8971_write(codec, WM8971_ADCDAC, mute_reg | 0x8);
10589+ else
10590+ wm8971_write(codec, WM8971_ADCDAC, mute_reg);
10591+ return 0;
10592+}
10593+
10594+static int wm8971_dapm_event(struct snd_soc_codec *codec, int event)
10595+{
10596+ u16 pwr_reg = wm8971_read_reg_cache(codec, WM8971_PWR1) & 0xfe3e;
10597+
10598+ switch (event) {
10599+ case SNDRV_CTL_POWER_D0: /* full On */
10600+ /* set vmid to 50k and unmute dac */
10601+ wm8971_write(codec, WM8971_PWR1, pwr_reg | 0x00c1);
10602+ break;
10603+ case SNDRV_CTL_POWER_D1: /* partial On */
10604+ case SNDRV_CTL_POWER_D2: /* partial On */
10605+ /* set vmid to 5k for quick power up */
10606+ wm8971_write(codec, WM8971_PWR1, pwr_reg | 0x01c0);
10607+ break;
10608+ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
10609+ /* mute dac and set vmid to 500k, enable VREF */
10610+ wm8971_write(codec, WM8971_PWR1, pwr_reg | 0x0140);
10611+ break;
10612+ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
10613+ wm8971_write(codec, WM8971_PWR1, 0x0001);
10614+ break;
10615+ }
10616+ codec->dapm_state = event;
10617+ return 0;
10618+}
10619+
10620+struct snd_soc_codec_dai wm8971_dai = {
10621+ .name = "WM8971",
10622+ .playback = {
10623+ .stream_name = "Playback",
10624+ .channels_min = 1,
10625+ .channels_max = 2,
10626+ },
10627+ .capture = {
10628+ .stream_name = "Capture",
10629+ .channels_min = 1,
10630+ .channels_max = 2,
10631+ },
10632+ .config_sysclk = wm8971_config_sysclk,
10633+ .digital_mute = wm8971_mute,
10634+ .ops = {
10635+ .prepare = wm8971_pcm_prepare,
10636+ },
10637+ .caps = {
10638+ .num_modes = ARRAY_SIZE(wm8971_modes),
10639+ .mode = wm8971_modes,
10640+ },
10641+};
10642+EXPORT_SYMBOL_GPL(wm8971_dai);
10643+
10644+static void wm8971_work(void *data)
10645+{
10646+ struct snd_soc_codec *codec = (struct snd_soc_codec *)data;
10647+ wm8971_dapm_event(codec, codec->dapm_state);
10648+}
10649+
10650+static int wm8971_suspend(struct platform_device *pdev, pm_message_t state)
10651+{
10652+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
10653+ struct snd_soc_codec *codec = socdev->codec;
10654+
10655+ wm8971_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
10656+ return 0;
10657+}
10658+
10659+static int wm8971_resume(struct platform_device *pdev)
10660+{
10661+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
10662+ struct snd_soc_codec *codec = socdev->codec;
10663+ int i;
10664+ u8 data[2];
10665+ u16 *cache = codec->reg_cache;
10666+
10667+ /* Sync reg_cache with the hardware */
10668+ for (i = 0; i < ARRAY_SIZE(wm8971_reg); i++) {
10669+ if (i + 1 == WM8971_RESET)
10670+ continue;
10671+ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
10672+ data[1] = cache[i] & 0x00ff;
10673+ codec->hw_write(codec->control_data, data, 2);
10674+ }
10675+
10676+ wm8971_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
10677+
10678+ /* charge wm8971 caps */
10679+ if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0) {
10680+ wm8971_dapm_event(codec, SNDRV_CTL_POWER_D2);
10681+ codec->dapm_state = SNDRV_CTL_POWER_D0;
10682+ queue_delayed_work(wm8971_workq, &wm8971_dapm_work,
10683+ msecs_to_jiffies(1000));
10684+ }
10685+
10686+ return 0;
10687+}
10688+
10689+static int wm8971_init(struct snd_soc_device *socdev)
10690+{
10691+ struct snd_soc_codec *codec = socdev->codec;
10692+ int reg, ret = 0;
10693+
10694+ codec->name = "WM8971";
10695+ codec->owner = THIS_MODULE;
10696+ codec->read = wm8971_read_reg_cache;
10697+ codec->write = wm8971_write;
10698+ codec->dapm_event = wm8971_dapm_event;
10699+ codec->dai = &wm8971_dai;
10700+ codec->reg_cache_size = ARRAY_SIZE(wm8971_reg);
10701+ codec->num_dai = 1;
10702+ codec->reg_cache =
10703+ kzalloc(sizeof(u16) * ARRAY_SIZE(wm8971_reg), GFP_KERNEL);
10704+ if (codec->reg_cache == NULL)
10705+ return -ENOMEM;
10706+ memcpy(codec->reg_cache, wm8971_reg,
10707+ sizeof(u16) * ARRAY_SIZE(wm8971_reg));
10708+ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm8971_reg);
10709+
10710+ wm8971_reset(codec);
10711+
10712+ /* register pcms */
10713+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
10714+ if (ret < 0) {
10715+ kfree(codec->reg_cache);
10716+ return ret;
10717+ }
10718+
10719+ /* charge output caps */
10720+ wm8971_dapm_event(codec, SNDRV_CTL_POWER_D2);
10721+ codec->dapm_state = SNDRV_CTL_POWER_D3hot;
10722+ queue_delayed_work(wm8971_workq, &wm8971_dapm_work,
10723+ msecs_to_jiffies(1000));
10724+
10725+ /* set the update bits */
10726+ reg = wm8971_read_reg_cache(codec, WM8971_LDAC);
10727+ wm8971_write(codec, WM8971_LDAC, reg | 0x0100);
10728+ reg = wm8971_read_reg_cache(codec, WM8971_RDAC);
10729+ wm8971_write(codec, WM8971_RDAC, reg | 0x0100);
10730+
10731+ reg = wm8971_read_reg_cache(codec, WM8971_LOUT1V);
10732+ wm8971_write(codec, WM8971_LOUT1V, reg | 0x0100);
10733+ reg = wm8971_read_reg_cache(codec, WM8971_ROUT1V);
10734+ wm8971_write(codec, WM8971_ROUT1V, reg | 0x0100);
10735+
10736+ reg = wm8971_read_reg_cache(codec, WM8971_LOUT2V);
10737+ wm8971_write(codec, WM8971_LOUT2V, reg | 0x0100);
10738+ reg = wm8971_read_reg_cache(codec, WM8971_ROUT2V);
10739+ wm8971_write(codec, WM8971_ROUT2V, reg | 0x0100);
10740+
10741+ reg = wm8971_read_reg_cache(codec, WM8971_LINVOL);
10742+ wm8971_write(codec, WM8971_LINVOL, reg | 0x0100);
10743+ reg = wm8971_read_reg_cache(codec, WM8971_RINVOL);
10744+ wm8971_write(codec, WM8971_RINVOL, reg | 0x0100);
10745+
10746+ wm8971_add_controls(codec);
10747+ wm8971_add_widgets(codec);
10748+ ret = snd_soc_register_card(socdev);
10749+ if (ret < 0) {
10750+ snd_soc_free_pcms(socdev);
10751+ snd_soc_dapm_free(socdev);
10752+ }
10753+
10754+ return ret;
10755+}
10756+
10757+/* If the i2c layer weren't so broken, we could pass this kind of data
10758+ around */
10759+static struct snd_soc_device *wm8971_socdev;
10760+
10761+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
10762+
10763+/*
10764+ * WM8731 2 wire address is determined by GPIO5
10765+ * state during powerup.
10766+ * low = 0x1a
10767+ * high = 0x1b
10768+ */
10769+#define I2C_DRIVERID_WM8971 0xfefe /* liam - need a proper id */
10770+
10771+static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
10772+
10773+/* Magic definition of all other variables and things */
10774+I2C_CLIENT_INSMOD;
10775+
10776+static struct i2c_driver wm8971_i2c_driver;
10777+static struct i2c_client client_template;
10778+
10779+static int wm8971_codec_probe(struct i2c_adapter *adap, int addr, int kind)
10780+{
10781+ struct snd_soc_device *socdev = wm8971_socdev;
10782+ struct wm8971_setup_data *setup = socdev->codec_data;
10783+ struct snd_soc_codec *codec = socdev->codec;
10784+ struct i2c_client *i2c;
10785+ int ret;
10786+
10787+ if (addr != setup->i2c_address)
10788+ return -ENODEV;
10789+
10790+ client_template.adapter = adap;
10791+ client_template.addr = addr;
10792+
10793+ i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
10794+ if (i2c == NULL) {
10795+ kfree(codec);
10796+ return -ENOMEM;
10797+ }
10798+ memcpy(i2c, &client_template, sizeof(struct i2c_client));
10799+
10800+ i2c_set_clientdata(i2c, codec);
10801+
10802+ codec->control_data = i2c;
10803+
10804+ ret = i2c_attach_client(i2c);
10805+ if (ret < 0) {
10806+ err("failed to attach codec at addr %x\n", addr);
10807+ goto err;
10808+ }
10809+
10810+ ret = wm8971_init(socdev);
10811+ if (ret < 0) {
10812+ err("failed to initialise WM8971\n");
10813+ goto err;
10814+ }
10815+ return ret;
10816+
10817+err:
10818+ kfree(codec);
10819+ kfree(i2c);
10820+ return ret;
10821+}
10822+
10823+static int wm8971_i2c_detach(struct i2c_client *client)
10824+{
10825+ struct snd_soc_codec* codec = i2c_get_clientdata(client);
10826+ i2c_detach_client(client);
10827+ kfree(codec->reg_cache);
10828+ kfree(client);
10829+ return 0;
10830+}
10831+
10832+static int wm8971_i2c_attach(struct i2c_adapter *adap)
10833+{
10834+ return i2c_probe(adap, &addr_data, wm8971_codec_probe);
10835+}
10836+
10837+/* corgi i2c codec control layer */
10838+static struct i2c_driver wm8971_i2c_driver = {
10839+ .driver = {
10840+ .name = "WM8971 I2C Codec",
10841+ .owner = THIS_MODULE,
10842+ },
10843+ .id = I2C_DRIVERID_WM8971,
10844+ .attach_adapter = wm8971_i2c_attach,
10845+ .detach_client = wm8971_i2c_detach,
10846+ .command = NULL,
10847+};
10848+
10849+static struct i2c_client client_template = {
10850+ .name = "WM8971",
10851+ .driver = &wm8971_i2c_driver,
10852+};
10853+#endif
10854+
10855+static int wm8971_probe(struct platform_device *pdev)
10856+{
10857+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
10858+ struct wm8971_setup_data *setup;
10859+ struct snd_soc_codec *codec;
10860+ int ret = 0;
10861+
10862+ info("WM8971 Audio Codec %s", WM8971_VERSION);
10863+
10864+ setup = socdev->codec_data;
10865+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
10866+ if (codec == NULL)
10867+ return -ENOMEM;
10868+
10869+ socdev->codec = codec;
10870+ mutex_init(&codec->mutex);
10871+ INIT_LIST_HEAD(&codec->dapm_widgets);
10872+ INIT_LIST_HEAD(&codec->dapm_paths);
10873+ wm8971_socdev = socdev;
10874+
10875+ INIT_WORK(&wm8971_dapm_work, wm8971_work, codec);
10876+ wm8971_workq = create_workqueue("wm8971");
10877+ if (wm8971_workq == NULL) {
10878+ kfree(codec);
10879+ return -ENOMEM;
10880+ }
10881+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
10882+ if (setup->i2c_address) {
10883+ normal_i2c[0] = setup->i2c_address;
10884+ codec->hw_write = (hw_write_t)i2c_master_send;
10885+ ret = i2c_add_driver(&wm8971_i2c_driver);
10886+ if (ret != 0)
10887+ printk(KERN_ERR "can't add i2c driver");
10888+ }
10889+#else
10890+ /* Add other interfaces here */
10891+#endif
10892+
10893+ return ret;
10894+}
10895+
10896+/* power down chip */
10897+static int wm8971_remove(struct platform_device *pdev)
10898+{
10899+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
10900+ struct snd_soc_codec *codec = socdev->codec;
10901+
10902+ if (codec->control_data)
10903+ wm8971_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
10904+ if (wm8971_workq)
10905+ destroy_workqueue(wm8971_workq);
10906+ snd_soc_free_pcms(socdev);
10907+ snd_soc_dapm_free(socdev);
10908+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
10909+ i2c_del_driver(&wm8971_i2c_driver);
10910+#endif
10911+ kfree(codec);
10912+
10913+ return 0;
10914+}
10915+
10916+struct snd_soc_codec_device soc_codec_dev_wm8971 = {
10917+ .probe = wm8971_probe,
10918+ .remove = wm8971_remove,
10919+ .suspend = wm8971_suspend,
10920+ .resume = wm8971_resume,
10921+};
10922+
10923+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8971);
10924+
10925+MODULE_DESCRIPTION("ASoC WM8971 driver");
10926+MODULE_AUTHOR("Lab126");
10927+MODULE_LICENSE("GPL");
10928Index: linux-2.6-pxa-new/sound/soc/codecs/wm8971.h
10929===================================================================
10930--- /dev/null
10931+++ linux-2.6-pxa-new/sound/soc/codecs/wm8971.h
10932@@ -0,0 +1,61 @@
10933+/*
10934+ * wm8971.h -- audio driver for WM8971
10935+ *
10936+ * Copyright 2005 Lab126, Inc.
10937+ *
10938+ * Author: Kenneth Kiraly <kiraly@lab126.com>
10939+ *
10940+ * This program is free software; you can redistribute it and/or modify it
10941+ * under the terms of the GNU General Public License as published by the
10942+ * Free Software Foundation; either version 2 of the License, or (at your
10943+ * option) any later version.
10944+ *
10945+ */
10946+
10947+#ifndef _WM8971_H
10948+#define _WM8971_H
10949+
10950+#define WM8971_LINVOL 0x00
10951+#define WM8971_RINVOL 0x01
10952+#define WM8971_LOUT1V 0x02
10953+#define WM8971_ROUT1V 0x03
10954+#define WM8971_ADCDAC 0x05
10955+#define WM8971_AUDIO 0x07
10956+#define WM8971_SRATE 0x08
10957+#define WM8971_LDAC 0x0a
10958+#define WM8971_RDAC 0x0b
10959+#define WM8971_BASS 0x0c
10960+#define WM8971_TREBLE 0x0d
10961+#define WM8971_RESET 0x0f
10962+#define WM8971_ALC1 0x11
10963+#define WM8971_ALC2 0x12
10964+#define WM8971_ALC3 0x13
10965+#define WM8971_NGATE 0x14
10966+#define WM8971_LADC 0x15
10967+#define WM8971_RADC 0x16
10968+#define WM8971_ADCTL1 0x17
10969+#define WM8971_ADCTL2 0x18
10970+#define WM8971_PWR1 0x19
10971+#define WM8971_PWR2 0x1a
10972+#define WM8971_ADCTL3 0x1b
10973+#define WM8971_ADCIN 0x1f
10974+#define WM8971_LADCIN 0x20
10975+#define WM8971_RADCIN 0x21
10976+#define WM8971_LOUTM1 0x22
10977+#define WM8971_LOUTM2 0x23
10978+#define WM8971_ROUTM1 0x24
10979+#define WM8971_ROUTM2 0x25
10980+#define WM8971_MOUTM1 0x26
10981+#define WM8971_MOUTM2 0x27
10982+#define WM8971_LOUT2V 0x28
10983+#define WM8971_ROUT2V 0x29
10984+#define WM8971_MOUTV 0x2A
10985+
10986+struct wm8971_setup_data {
10987+ unsigned short i2c_address;
10988+};
10989+
10990+extern struct snd_soc_codec_dai wm8971_dai;
10991+extern struct snd_soc_codec_device soc_codec_dev_wm8971;
10992+
10993+#endif
10994Index: linux-2.6-pxa-new/sound/soc/codecs/wm8974.c
10995===================================================================
10996--- /dev/null
10997+++ linux-2.6-pxa-new/sound/soc/codecs/wm8974.c
10998@@ -0,0 +1,935 @@
10999+/*
11000+ * wm8974.c -- WM8974 ALSA Soc Audio driver
11001+ *
11002+ * Copyright 2006 Wolfson Microelectronics PLC.
11003+ *
11004+ * Author: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
11005+ *
11006+ * This program is free software; you can redistribute it and/or modify
11007+ * it under the terms of the GNU General Public License version 2 as
11008+ * published by the Free Software Foundation.
11009+ */
11010+
11011+#include <linux/module.h>
11012+#include <linux/moduleparam.h>
11013+#include <linux/version.h>
11014+#include <linux/kernel.h>
11015+#include <linux/init.h>
11016+#include <linux/delay.h>
11017+#include <linux/pm.h>
11018+#include <linux/i2c.h>
11019+#include <linux/platform_device.h>
11020+#include <sound/driver.h>
11021+#include <sound/core.h>
11022+#include <sound/pcm.h>
11023+#include <sound/pcm_params.h>
11024+#include <sound/soc.h>
11025+#include <sound/soc-dapm.h>
11026+#include <sound/initval.h>
11027+
11028+#include "wm8974.h"
11029+
11030+#define AUDIO_NAME "wm8974"
11031+#define WM8974_VERSION "0.5"
11032+
11033+/*
11034+ * Debug
11035+ */
11036+
11037+#define WM8974_DEBUG 0
11038+
11039+#ifdef WM8974_DEBUG
11040+#define dbg(format, arg...) \
11041+ printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
11042+#else
11043+#define dbg(format, arg...) do {} while (0)
11044+#endif
11045+#define err(format, arg...) \
11046+ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
11047+#define info(format, arg...) \
11048+ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
11049+#define warn(format, arg...) \
11050+ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
11051+
11052+struct snd_soc_codec_device soc_codec_dev_wm8974;
11053+
11054+/*
11055+ * wm8974 register cache
11056+ * We can't read the WM8974 register space when we are
11057+ * using 2 wire for device control, so we cache them instead.
11058+ */
11059+static const u16 wm8974_reg[WM8974_CACHEREGNUM] = {
11060+ 0x0000, 0x0000, 0x0000, 0x0000,
11061+ 0x0050, 0x0000, 0x0140, 0x0000,
11062+ 0x0000, 0x0000, 0x0000, 0x00ff,
11063+ 0x0000, 0x0000, 0x0100, 0x00ff,
11064+ 0x0000, 0x0000, 0x012c, 0x002c,
11065+ 0x002c, 0x002c, 0x002c, 0x0000,
11066+ 0x0032, 0x0000, 0x0000, 0x0000,
11067+ 0x0000, 0x0000, 0x0000, 0x0000,
11068+ 0x0038, 0x000b, 0x0032, 0x0000,
11069+ 0x0008, 0x000c, 0x0093, 0x00e9,
11070+ 0x0000, 0x0000, 0x0000, 0x0000,
11071+ 0x0003, 0x0010, 0x0000, 0x0000,
11072+ 0x0000, 0x0002, 0x0000, 0x0000,
11073+ 0x0000, 0x0000, 0x0039, 0x0000,
11074+ 0x0000,
11075+};
11076+
11077+#define WM8974_DAIFMT \
11078+ (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_RIGHT_J | \
11079+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_NB_IF | SND_SOC_DAIFMT_IB_NF | \
11080+ SND_SOC_DAIFMT_IB_IF)
11081+
11082+#define WM8974_DIR \
11083+ (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
11084+
11085+#define WM8974_RATES \
11086+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
11087+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
11088+ SNDRV_PCM_RATE_48000)
11089+
11090+#define WM8794_BCLK \
11091+ (SND_SOC_FSBD(1) | SND_SOC_FSBD(2) | SND_SOC_FSBD(4) | SND_SOC_FSBD(8) |\
11092+ SND_SOC_FSBD(16) | SND_SOC_FSBD(32))
11093+
11094+#define WM8794_HIFI_BITS \
11095+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
11096+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
11097+
11098+static struct snd_soc_dai_mode wm8974_modes[] = {
11099+ /* codec frame and clock master modes */
11100+ {
11101+ .fmt = WM8974_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
11102+ .pcmfmt = WM8794_HIFI_BITS,
11103+ .pcmrate = WM8974_RATES,
11104+ .pcmdir = WM8974_DIR,
11105+ .fs = 256,
11106+ .bfs = WM8794_BCLK,
11107+ },
11108+
11109+ /* codec frame and clock slave modes */
11110+ {
11111+ .fmt = WM8974_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
11112+ .pcmfmt = WM8794_HIFI_BITS,
11113+ .pcmrate = WM8974_RATES,
11114+ .pcmdir = WM8974_DIR,
11115+ .flags = SND_SOC_DAI_BFS_DIV,
11116+ .fs = SND_SOC_FS_ALL,
11117+ .bfs = SND_SOC_FSB_ALL,
11118+ },
11119+};
11120+
11121+/*
11122+ * read wm8974 register cache
11123+ */
11124+static inline unsigned int wm8974_read_reg_cache(struct snd_soc_codec * codec,
11125+ unsigned int reg)
11126+{
11127+ u16 *cache = codec->reg_cache;
11128+ if (reg == WM8974_RESET)
11129+ return 0;
11130+ if (reg >= WM8974_CACHEREGNUM)
11131+ return -1;
11132+ return cache[reg];
11133+}
11134+
11135+/*
11136+ * write wm8974 register cache
11137+ */
11138+static inline void wm8974_write_reg_cache(struct snd_soc_codec *codec,
11139+ u16 reg, unsigned int value)
11140+{
11141+ u16 *cache = codec->reg_cache;
11142+ if (reg >= WM8974_CACHEREGNUM)
11143+ return;
11144+ cache[reg] = value;
11145+}
11146+
11147+/*
11148+ * write to the WM8974 register space
11149+ */
11150+static int wm8974_write(struct snd_soc_codec *codec, unsigned int reg,
11151+ unsigned int value)
11152+{
11153+ u8 data[2];
11154+
11155+ /* data is
11156+ * D15..D9 WM8974 register offset
11157+ * D8...D0 register data
11158+ */
11159+ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
11160+ data[1] = value & 0x00ff;
11161+
11162+ wm8974_write_reg_cache (codec, reg, value);
11163+ if (codec->hw_write(codec->control_data, data, 2) == 2)
11164+ return 0;
11165+ else
11166+ return -EIO;
11167+}
11168+
11169+#define wm8974_reset(c) wm8974_write(c, WM8974_RESET, 0)
11170+
11171+static const char *wm8974_companding[] = {"Off", "NC", "u-law", "A-law" };
11172+static const char *wm8974_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz" };
11173+static const char *wm8974_eqmode[] = {"Capture", "Playback" };
11174+static const char *wm8974_bw[] = {"Narrow", "Wide" };
11175+static const char *wm8974_eq1[] = {"80Hz", "105Hz", "135Hz", "175Hz" };
11176+static const char *wm8974_eq2[] = {"230Hz", "300Hz", "385Hz", "500Hz" };
11177+static const char *wm8974_eq3[] = {"650Hz", "850Hz", "1.1kHz", "1.4kHz" };
11178+static const char *wm8974_eq4[] = {"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz" };
11179+static const char *wm8974_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz" };
11180+static const char *wm8974_alc[] = {"ALC", "Limiter" };
11181+
11182+static const struct soc_enum wm8974_enum[] = {
11183+ SOC_ENUM_SINGLE(WM8974_COMP, 1, 4, wm8974_companding), /* adc */
11184+ SOC_ENUM_SINGLE(WM8974_COMP, 3, 4, wm8974_companding), /* dac */
11185+ SOC_ENUM_SINGLE(WM8974_DAC, 4, 4, wm8974_deemp),
11186+ SOC_ENUM_SINGLE(WM8974_EQ1, 8, 2, wm8974_eqmode),
11187+
11188+ SOC_ENUM_SINGLE(WM8974_EQ1, 5, 4, wm8974_eq1),
11189+ SOC_ENUM_SINGLE(WM8974_EQ2, 8, 2, wm8974_bw),
11190+ SOC_ENUM_SINGLE(WM8974_EQ2, 5, 4, wm8974_eq2),
11191+ SOC_ENUM_SINGLE(WM8974_EQ3, 8, 2, wm8974_bw),
11192+
11193+ SOC_ENUM_SINGLE(WM8974_EQ3, 5, 4, wm8974_eq3),
11194+ SOC_ENUM_SINGLE(WM8974_EQ4, 8, 2, wm8974_bw),
11195+ SOC_ENUM_SINGLE(WM8974_EQ4, 5, 4, wm8974_eq4),
11196+ SOC_ENUM_SINGLE(WM8974_EQ5, 8, 2, wm8974_bw),
11197+
11198+ SOC_ENUM_SINGLE(WM8974_EQ5, 5, 4, wm8974_eq5),
11199+ SOC_ENUM_SINGLE(WM8974_ALC3, 8, 2, wm8974_alc),
11200+};
11201+
11202+static const struct snd_kcontrol_new wm8974_snd_controls[] = {
11203+
11204+SOC_SINGLE("Digital Loopback Switch", WM8974_COMP, 0, 1, 0),
11205+
11206+SOC_ENUM("DAC Companding", wm8974_enum[1]),
11207+SOC_ENUM("ADC Companding", wm8974_enum[0]),
11208+
11209+SOC_ENUM("Playback De-emphasis", wm8974_enum[2]),
11210+SOC_SINGLE("DAC Inversion Switch", WM8974_DAC, 0, 1, 0),
11211+
11212+SOC_SINGLE("PCM Volume", WM8974_DACVOL, 0, 127, 0),
11213+
11214+SOC_SINGLE("High Pass Filter Switch", WM8974_ADC, 8, 1, 0),
11215+SOC_SINGLE("High Pass Cut Off", WM8974_ADC, 4, 7, 0),
11216+SOC_SINGLE("ADC Inversion Switch", WM8974_COMP, 0, 1, 0),
11217+
11218+SOC_SINGLE("Capture Volume", WM8974_ADCVOL, 0, 127, 0),
11219+
11220+SOC_ENUM("Equaliser Function", wm8974_enum[3]),
11221+SOC_ENUM("EQ1 Cut Off", wm8974_enum[4]),
11222+SOC_SINGLE("EQ1 Volume", WM8974_EQ1, 0, 31, 1),
11223+
11224+SOC_ENUM("Equaliser EQ2 Bandwith", wm8974_enum[5]),
11225+SOC_ENUM("EQ2 Cut Off", wm8974_enum[6]),
11226+SOC_SINGLE("EQ2 Volume", WM8974_EQ2, 0, 31, 1),
11227+
11228+SOC_ENUM("Equaliser EQ3 Bandwith", wm8974_enum[7]),
11229+SOC_ENUM("EQ3 Cut Off", wm8974_enum[8]),
11230+SOC_SINGLE("EQ3 Volume", WM8974_EQ3, 0, 31, 1),
11231+
11232+SOC_ENUM("Equaliser EQ4 Bandwith", wm8974_enum[9]),
11233+SOC_ENUM("EQ4 Cut Off", wm8974_enum[10]),
11234+SOC_SINGLE("EQ4 Volume", WM8974_EQ4, 0, 31, 1),
11235+
11236+SOC_ENUM("Equaliser EQ5 Bandwith", wm8974_enum[11]),
11237+SOC_ENUM("EQ5 Cut Off", wm8974_enum[12]),
11238+SOC_SINGLE("EQ5 Volume", WM8974_EQ5, 0, 31, 1),
11239+
11240+SOC_SINGLE("DAC Playback Limiter Switch", WM8974_DACLIM1, 8, 1, 0),
11241+SOC_SINGLE("DAC Playback Limiter Decay", WM8974_DACLIM1, 4, 15, 0),
11242+SOC_SINGLE("DAC Playback Limiter Attack", WM8974_DACLIM1, 0, 15, 0),
11243+
11244+SOC_SINGLE("DAC Playback Limiter Threshold", WM8974_DACLIM2, 4, 7, 0),
11245+SOC_SINGLE("DAC Playback Limiter Boost", WM8974_DACLIM2, 0, 15, 0),
11246+
11247+SOC_SINGLE("ALC Enable Switch", WM8974_ALC1, 8, 1, 0),
11248+SOC_SINGLE("ALC Capture Max Gain", WM8974_ALC1, 3, 7, 0),
11249+SOC_SINGLE("ALC Capture Min Gain", WM8974_ALC1, 0, 7, 0),
11250+
11251+SOC_SINGLE("ALC Capture ZC Switch", WM8974_ALC2, 8, 1, 0),
11252+SOC_SINGLE("ALC Capture Hold", WM8974_ALC2, 4, 7, 0),
11253+SOC_SINGLE("ALC Capture Target", WM8974_ALC2, 0, 15, 0),
11254+
11255+SOC_ENUM("ALC Capture Mode", wm8974_enum[13]),
11256+SOC_SINGLE("ALC Capture Decay", WM8974_ALC3, 4, 15, 0),
11257+SOC_SINGLE("ALC Capture Attack", WM8974_ALC3, 0, 15, 0),
11258+
11259+SOC_SINGLE("ALC Capture Noise Gate Switch", WM8974_NGATE, 3, 1, 0),
11260+SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8974_NGATE, 0, 7, 0),
11261+
11262+SOC_SINGLE("Capture PGA ZC Switch", WM8974_INPPGA, 7, 1, 0),
11263+SOC_SINGLE("Capture PGA Volume", WM8974_INPPGA, 0, 63, 0),
11264+
11265+SOC_SINGLE("Speaker Playback ZC Switch", WM8974_SPKVOL, 7, 1, 0),
11266+SOC_SINGLE("Speaker Playback Switch", WM8974_SPKVOL, 6, 1, 1),
11267+SOC_SINGLE("Speaker Playback Volume", WM8974_SPKVOL, 0, 63, 0),
11268+
11269+SOC_SINGLE("Capture Boost(+20dB)", WM8974_ADCBOOST, 8, 1, 0),
11270+SOC_SINGLE("Mono Playback Switch", WM8974_MONOMIX, 6, 1, 0),
11271+};
11272+
11273+/* add non dapm controls */
11274+static int wm8974_add_controls(struct snd_soc_codec *codec)
11275+{
11276+ int err, i;
11277+
11278+ for (i = 0; i < ARRAY_SIZE(wm8974_snd_controls); i++) {
11279+ err = snd_ctl_add(codec->card,
11280+ snd_soc_cnew(&wm8974_snd_controls[i],codec, NULL));
11281+ if (err < 0)
11282+ return err;
11283+ }
11284+
11285+ return 0;
11286+}
11287+
11288+/* Speaker Output Mixer */
11289+static const struct snd_kcontrol_new wm8974_speaker_mixer_controls[] = {
11290+SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_SPKMIX, 1, 1, 0),
11291+SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_SPKMIX, 5, 1, 0),
11292+SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_SPKMIX, 0, 1, 1),
11293+};
11294+
11295+/* Mono Output Mixer */
11296+static const struct snd_kcontrol_new wm8974_mono_mixer_controls[] = {
11297+SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_MONOMIX, 1, 1, 0),
11298+SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_MONOMIX, 2, 1, 0),
11299+SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_MONOMIX, 0, 1, 1),
11300+};
11301+
11302+/* AUX Input boost vol */
11303+static const struct snd_kcontrol_new wm8974_aux_boost_controls =
11304+SOC_DAPM_SINGLE("Aux Volume", WM8974_ADCBOOST, 0, 7, 0);
11305+
11306+/* Mic Input boost vol */
11307+static const struct snd_kcontrol_new wm8974_mic_boost_controls =
11308+SOC_DAPM_SINGLE("Mic Volume", WM8974_ADCBOOST, 4, 7, 0);
11309+
11310+/* Capture boost switch */
11311+static const struct snd_kcontrol_new wm8974_capture_boost_controls =
11312+SOC_DAPM_SINGLE("Capture Boost Switch", WM8974_INPPGA, 6, 1, 0);
11313+
11314+/* Aux In to PGA */
11315+static const struct snd_kcontrol_new wm8974_aux_capture_boost_controls =
11316+SOC_DAPM_SINGLE("Aux Capture Boost Switch", WM8974_INPPGA, 2, 1, 0);
11317+
11318+/* Mic P In to PGA */
11319+static const struct snd_kcontrol_new wm8974_micp_capture_boost_controls =
11320+SOC_DAPM_SINGLE("Mic P Capture Boost Switch", WM8974_INPPGA, 0, 1, 0);
11321+
11322+/* Mic N In to PGA */
11323+static const struct snd_kcontrol_new wm8974_micn_capture_boost_controls =
11324+SOC_DAPM_SINGLE("Mic N Capture Boost Switch", WM8974_INPPGA, 1, 1, 0);
11325+
11326+static const struct snd_soc_dapm_widget wm8974_dapm_widgets[] = {
11327+SND_SOC_DAPM_MIXER("Speaker Mixer", WM8974_POWER3, 2, 0,
11328+ &wm8974_speaker_mixer_controls[0],
11329+ ARRAY_SIZE(wm8974_speaker_mixer_controls)),
11330+SND_SOC_DAPM_MIXER("Mono Mixer", WM8974_POWER3, 3, 0,
11331+ &wm8974_mono_mixer_controls[0],
11332+ ARRAY_SIZE(wm8974_mono_mixer_controls)),
11333+SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8974_POWER3, 0, 0),
11334+SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8974_POWER3, 0, 0),
11335+SND_SOC_DAPM_PGA("Aux Input", WM8974_POWER1, 6, 0, NULL, 0),
11336+SND_SOC_DAPM_PGA("SpkN Out", WM8974_POWER3, 5, 0, NULL, 0),
11337+SND_SOC_DAPM_PGA("SpkP Out", WM8974_POWER3, 6, 0, NULL, 0),
11338+SND_SOC_DAPM_PGA("Mono Out", WM8974_POWER3, 7, 0, NULL, 0),
11339+SND_SOC_DAPM_PGA("Mic PGA", WM8974_POWER2, 2, 0, NULL, 0),
11340+
11341+SND_SOC_DAPM_PGA("Aux Boost", SND_SOC_NOPM, 0, 0,
11342+ &wm8974_aux_boost_controls, 1),
11343+SND_SOC_DAPM_PGA("Mic Boost", SND_SOC_NOPM, 0, 0,
11344+ &wm8974_mic_boost_controls, 1),
11345+SND_SOC_DAPM_SWITCH("Capture Boost", SND_SOC_NOPM, 0, 0,
11346+ &wm8974_capture_boost_controls),
11347+
11348+SND_SOC_DAPM_MIXER("Boost Mixer", WM8974_POWER2, 4, 0, NULL, 0),
11349+
11350+SND_SOC_DAPM_MICBIAS("Mic Bias", WM8974_POWER1, 4, 0),
11351+
11352+SND_SOC_DAPM_INPUT("MICN"),
11353+SND_SOC_DAPM_INPUT("MICP"),
11354+SND_SOC_DAPM_INPUT("AUX"),
11355+SND_SOC_DAPM_OUTPUT("MONOOUT"),
11356+SND_SOC_DAPM_OUTPUT("SPKOUTP"),
11357+SND_SOC_DAPM_OUTPUT("SPKOUTN"),
11358+};
11359+
11360+static const char *audio_map[][3] = {
11361+ /* Mono output mixer */
11362+ {"Mono Mixer", "PCM Playback Switch", "DAC"},
11363+ {"Mono Mixer", "Aux Playback Switch", "Aux Input"},
11364+ {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
11365+
11366+ /* Speaker output mixer */
11367+ {"Speaker Mixer", "PCM Playback Switch", "DAC"},
11368+ {"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
11369+ {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
11370+
11371+ /* Outputs */
11372+ {"Mono Out", NULL, "Mono Mixer"},
11373+ {"MONOOUT", NULL, "Mono Out"},
11374+ {"SpkN Out", NULL, "Speaker Mixer"},
11375+ {"SpkP Out", NULL, "Speaker Mixer"},
11376+ {"SPKOUTN", NULL, "SpkN Out"},
11377+ {"SPKOUTP", NULL, "SpkP Out"},
11378+
11379+ /* Boost Mixer */
11380+ {"Boost Mixer", NULL, "ADC"},
11381+ {"Capture Boost Switch", "Aux Capture Boost Switch", "AUX"},
11382+ {"Aux Boost", "Aux Volume", "Boost Mixer"},
11383+ {"Capture Boost", "Capture Switch", "Boost Mixer"},
11384+ {"Mic Boost", "Mic Volume", "Boost Mixer"},
11385+
11386+ /* Inputs */
11387+ {"MICP", NULL, "Mic Boost"},
11388+ {"MICN", NULL, "Mic PGA"},
11389+ {"Mic PGA", NULL, "Capture Boost"},
11390+ {"AUX", NULL, "Aux Input"},
11391+
11392+ /* terminator */
11393+ {NULL, NULL, NULL},
11394+};
11395+
11396+static int wm8974_add_widgets(struct snd_soc_codec *codec)
11397+{
11398+ int i;
11399+
11400+ for(i = 0; i < ARRAY_SIZE(wm8974_dapm_widgets); i++) {
11401+ snd_soc_dapm_new_control(codec, &wm8974_dapm_widgets[i]);
11402+ }
11403+
11404+ /* set up audio path audio_mapnects */
11405+ for(i = 0; audio_map[i][0] != NULL; i++) {
11406+ snd_soc_dapm_connect_input(codec, audio_map[i][0],
11407+ audio_map[i][1], audio_map[i][2]);
11408+ }
11409+
11410+ snd_soc_dapm_new_widgets(codec);
11411+ return 0;
11412+}
11413+
11414+struct pll_ {
11415+ unsigned int in_hz, out_hz;
11416+ unsigned int pre:4; /* prescale - 1 */
11417+ unsigned int n:4;
11418+ unsigned int k;
11419+};
11420+
11421+struct pll_ pll[] = {
11422+ {12000000, 11289600, 0, 7, 0x86c220},
11423+ {12000000, 12288000, 0, 8, 0x3126e8},
11424+ {13000000, 11289600, 0, 6, 0xf28bd4},
11425+ {13000000, 12288000, 0, 7, 0x8fd525},
11426+ {12288000, 11289600, 0, 7, 0x59999a},
11427+ {11289600, 12288000, 0, 8, 0x80dee9},
11428+ /* liam - add more entries */
11429+};
11430+
11431+static int set_pll(struct snd_soc_codec *codec, unsigned int in,
11432+ unsigned int out)
11433+{
11434+ int i;
11435+ u16 reg;
11436+
11437+ if(out == 0) {
11438+ reg = wm8974_read_reg_cache(codec, WM8974_POWER1);
11439+ wm8974_write(codec, WM8974_POWER1, reg & 0x1df);
11440+ return 0;
11441+ }
11442+
11443+ for(i = 0; i < ARRAY_SIZE(pll); i++) {
11444+ if (in == pll[i].in_hz && out == pll[i].out_hz) {
11445+ wm8974_write(codec, WM8974_PLLN, (pll[i].pre << 4) | pll[i].n);
11446+ wm8974_write(codec, WM8974_PLLK1, pll[i].k >> 18);
11447+ wm8974_write(codec, WM8974_PLLK1, (pll[i].k >> 9) && 0x1ff);
11448+ wm8974_write(codec, WM8974_PLLK1, pll[i].k && 0x1ff);
11449+ reg = wm8974_read_reg_cache(codec, WM8974_POWER1);
11450+ wm8974_write(codec, WM8974_POWER1, reg | 0x020);
11451+ return 0;
11452+ }
11453+ }
11454+ return -EINVAL;
11455+}
11456+
11457+/* mclk dividers * 2 */
11458+static unsigned char mclk_div[] = {2, 3, 4, 6, 8, 12, 16, 24};
11459+
11460+/* we need 256FS to drive the DAC's and ADC's */
11461+static unsigned int wm8974_config_sysclk(struct snd_soc_codec_dai *dai,
11462+ struct snd_soc_clock_info *info, unsigned int clk)
11463+{
11464+ int i, j, best_clk = info->fs * info->rate;
11465+
11466+ /* can we run at this clk without the PLL ? */
11467+ for (i = 0; i < ARRAY_SIZE(mclk_div); i++) {
11468+ if ((best_clk >> 1) * mclk_div[i] == clk) {
11469+ dai->pll_in = 0;
11470+ dai->clk_div = mclk_div[i];
11471+ dai->mclk = best_clk;
11472+ return dai->mclk;
11473+ }
11474+ }
11475+
11476+ /* now check for PLL support */
11477+ for (i = 0; i < ARRAY_SIZE(pll); i++) {
11478+ if (pll[i].in_hz == clk) {
11479+ for (j = 0; j < ARRAY_SIZE(mclk_div); j++) {
11480+ if (pll[i].out_hz == mclk_div[j] * (best_clk >> 1)) {
11481+ dai->pll_in = clk;
11482+ dai->pll_out = pll[i].out_hz;
11483+ dai->clk_div = mclk_div[j];
11484+ dai->mclk = best_clk;
11485+ return dai->mclk;
11486+ }
11487+ }
11488+ }
11489+ }
11490+
11491+ /* this clk is not supported */
11492+ return 0;
11493+}
11494+
11495+static int wm8974_pcm_prepare(struct snd_pcm_substream *substream)
11496+{
11497+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
11498+ struct snd_soc_device *socdev = rtd->socdev;
11499+ struct snd_soc_codec *codec = socdev->codec;
11500+ struct snd_soc_codec_dai *dai = rtd->codec_dai;
11501+ u16 iface = 0, bfs, clk = 0, adn;
11502+ int fs = 48000 << 7, i;
11503+
11504+ bfs = SND_SOC_FSBD_REAL(rtd->codec_dai->dai_runtime.bfs);
11505+ switch (bfs) {
11506+ case 2:
11507+ clk |= 0x1 << 2;
11508+ break;
11509+ case 4:
11510+ clk |= 0x2 << 2;
11511+ break;
11512+ case 8:
11513+ clk |= 0x3 << 2;
11514+ break;
11515+ case 16:
11516+ clk |= 0x4 << 2;
11517+ break;
11518+ case 32:
11519+ clk |= 0x5 << 2;
11520+ break;
11521+ }
11522+
11523+ /* set master/slave audio interface */
11524+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
11525+ case SND_SOC_DAIFMT_CBM_CFM:
11526+ clk |= 0x0001;
11527+ break;
11528+ case SND_SOC_DAIFMT_CBS_CFS:
11529+ break;
11530+ }
11531+
11532+ /* interface format */
11533+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
11534+ case SND_SOC_DAIFMT_I2S:
11535+ iface |= 0x0010;
11536+ break;
11537+ case SND_SOC_DAIFMT_RIGHT_J:
11538+ break;
11539+ case SND_SOC_DAIFMT_LEFT_J:
11540+ iface |= 0x0008;
11541+ break;
11542+ case SND_SOC_DAIFMT_DSP_A:
11543+ iface |= 0x00018;
11544+ break;
11545+ }
11546+
11547+ /* bit size */
11548+ switch (rtd->codec_dai->dai_runtime.pcmfmt) {
11549+ case SNDRV_PCM_FMTBIT_S16_LE:
11550+ break;
11551+ case SNDRV_PCM_FMTBIT_S20_3LE:
11552+ iface |= 0x0020;
11553+ break;
11554+ case SNDRV_PCM_FMTBIT_S24_LE:
11555+ iface |= 0x0040;
11556+ break;
11557+ case SNDRV_PCM_FMTBIT_S32_LE:
11558+ iface |= 0x0060;
11559+ break;
11560+ }
11561+
11562+ /* clock inversion */
11563+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_INV_MASK) {
11564+ case SND_SOC_DAIFMT_NB_NF:
11565+ break;
11566+ case SND_SOC_DAIFMT_IB_IF:
11567+ iface |= 0x0180;
11568+ break;
11569+ case SND_SOC_DAIFMT_IB_NF:
11570+ iface |= 0x0100;
11571+ break;
11572+ case SND_SOC_DAIFMT_NB_IF:
11573+ iface |= 0x0080;
11574+ break;
11575+ }
11576+
11577+ /* filter coefficient */
11578+ adn = wm8974_read_reg_cache(codec, WM8974_ADD) & 0x1f1;
11579+ switch (rtd->codec_dai->dai_runtime.pcmrate) {
11580+ case SNDRV_PCM_RATE_8000:
11581+ adn |= 0x5 << 1;
11582+ fs = 8000 << 7;
11583+ break;
11584+ case SNDRV_PCM_RATE_11025:
11585+ adn |= 0x4 << 1;
11586+ fs = 11025 << 7;
11587+ break;
11588+ case SNDRV_PCM_RATE_16000:
11589+ adn |= 0x3 << 1;
11590+ fs = 16000 << 7;
11591+ break;
11592+ case SNDRV_PCM_RATE_22050:
11593+ adn |= 0x2 << 1;
11594+ fs = 22050 << 7;
11595+ break;
11596+ case SNDRV_PCM_RATE_32000:
11597+ adn |= 0x1 << 1;
11598+ fs = 32000 << 7;
11599+ break;
11600+ case SNDRV_PCM_RATE_44100:
11601+ fs = 44100 << 7;
11602+ break;
11603+ }
11604+
11605+ /* do we need to enable the PLL */
11606+ if(dai->pll_in)
11607+ set_pll(codec, dai->pll_in, dai->pll_out);
11608+
11609+ /* divide the clock to 256 fs */
11610+ for(i = 0; i < ARRAY_SIZE(mclk_div); i++) {
11611+ if (dai->clk_div == mclk_div[i]) {
11612+ clk |= i << 5;
11613+ clk &= 0xff;
11614+ goto set;
11615+ }
11616+ }
11617+
11618+set:
11619+ /* set iface */
11620+ wm8974_write(codec, WM8974_IFACE, iface);
11621+ wm8974_write(codec, WM8974_CLOCK, clk);
11622+
11623+ return 0;
11624+}
11625+
11626+static int wm8974_hw_free(struct snd_pcm_substream *substream)
11627+{
11628+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
11629+ struct snd_soc_device *socdev = rtd->socdev;
11630+ struct snd_soc_codec *codec = socdev->codec;
11631+ set_pll(codec, 0, 0);
11632+ return 0;
11633+}
11634+
11635+static int wm8974_mute(struct snd_soc_codec *codec,
11636+ struct snd_soc_codec_dai *dai, int mute)
11637+{
11638+ u16 mute_reg = wm8974_read_reg_cache(codec, WM8974_DAC) & 0xffbf;
11639+ if(mute)
11640+ wm8974_write(codec, WM8974_DAC, mute_reg | 0x40);
11641+ else
11642+ wm8974_write(codec, WM8974_DAC, mute_reg);
11643+ return 0;
11644+}
11645+
11646+/* liam need to make this lower power with dapm */
11647+static int wm8974_dapm_event(struct snd_soc_codec *codec, int event)
11648+{
11649+
11650+ switch (event) {
11651+ case SNDRV_CTL_POWER_D0: /* full On */
11652+ /* vref/mid, clk and osc on, dac unmute, active */
11653+ wm8974_write(codec, WM8974_POWER1, 0x1ff);
11654+ wm8974_write(codec, WM8974_POWER2, 0x1ff);
11655+ wm8974_write(codec, WM8974_POWER3, 0x1ff);
11656+ break;
11657+ case SNDRV_CTL_POWER_D1: /* partial On */
11658+ case SNDRV_CTL_POWER_D2: /* partial On */
11659+ break;
11660+ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
11661+ /* everything off except vref/vmid, dac mute, inactive */
11662+
11663+ break;
11664+ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
11665+ /* everything off, dac mute, inactive */
11666+ wm8974_write(codec, WM8974_POWER1, 0x0);
11667+ wm8974_write(codec, WM8974_POWER2, 0x0);
11668+ wm8974_write(codec, WM8974_POWER3, 0x0);
11669+ break;
11670+ }
11671+ codec->dapm_state = event;
11672+ return 0;
11673+}
11674+
11675+struct snd_soc_codec_dai wm8974_dai = {
11676+ .name = "WM8974 HiFi",
11677+ .playback = {
11678+ .stream_name = "Playback",
11679+ .channels_min = 1,
11680+ .channels_max = 1,
11681+ },
11682+ .capture = {
11683+ .stream_name = "Capture",
11684+ .channels_min = 1,
11685+ .channels_max = 1,
11686+ },
11687+ .config_sysclk = wm8974_config_sysclk,
11688+ .digital_mute = wm8974_mute,
11689+ .ops = {
11690+ .prepare = wm8974_pcm_prepare,
11691+ .hw_free = wm8974_hw_free,
11692+ },
11693+ .caps = {
11694+ .num_modes = ARRAY_SIZE(wm8974_modes),
11695+ .mode = wm8974_modes,
11696+ },
11697+};
11698+EXPORT_SYMBOL_GPL(wm8974_dai);
11699+
11700+static int wm8974_suspend(struct platform_device *pdev, pm_message_t state)
11701+{
11702+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
11703+ struct snd_soc_codec *codec = socdev->codec;
11704+
11705+ wm8974_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
11706+ return 0;
11707+}
11708+
11709+static int wm8974_resume(struct platform_device *pdev)
11710+{
11711+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
11712+ struct snd_soc_codec *codec = socdev->codec;
11713+ int i;
11714+ u8 data[2];
11715+ u16 *cache = codec->reg_cache;
11716+
11717+ /* Sync reg_cache with the hardware */
11718+ for (i = 0; i < ARRAY_SIZE(wm8974_reg); i++) {
11719+ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
11720+ data[1] = cache[i] & 0x00ff;
11721+ codec->hw_write(codec->control_data, data, 2);
11722+ }
11723+ wm8974_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
11724+ wm8974_dapm_event(codec, codec->suspend_dapm_state);
11725+ return 0;
11726+}
11727+
11728+/*
11729+ * initialise the WM8974 driver
11730+ * register the mixer and dsp interfaces with the kernel
11731+ */
11732+static int wm8974_init(struct snd_soc_device *socdev)
11733+{
11734+ struct snd_soc_codec *codec = socdev->codec;
11735+ int ret = 0;
11736+
11737+ codec->name = "WM8974";
11738+ codec->owner = THIS_MODULE;
11739+ codec->read = wm8974_read_reg_cache;
11740+ codec->write = wm8974_write;
11741+ codec->dapm_event = wm8974_dapm_event;
11742+ codec->dai = &wm8974_dai;
11743+ codec->num_dai = 1;
11744+ codec->reg_cache_size = ARRAY_SIZE(wm8974_reg);
11745+ codec->reg_cache =
11746+ kzalloc(sizeof(u16) * ARRAY_SIZE(wm8974_reg), GFP_KERNEL);
11747+ if (codec->reg_cache == NULL)
11748+ return -ENOMEM;
11749+ memcpy(codec->reg_cache, wm8974_reg,
11750+ sizeof(u16) * ARRAY_SIZE(wm8974_reg));
11751+ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm8974_reg);
11752+
11753+ wm8974_reset(codec);
11754+
11755+ /* register pcms */
11756+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
11757+ if(ret < 0) {
11758+ kfree(codec->reg_cache);
11759+ return ret;
11760+ }
11761+
11762+ /* power on device */
11763+ wm8974_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
11764+ wm8974_add_controls(codec);
11765+ wm8974_add_widgets(codec);
11766+ ret = snd_soc_register_card(socdev);
11767+ if(ret < 0) {
11768+ snd_soc_free_pcms(socdev);
11769+ snd_soc_dapm_free(socdev);
11770+ }
11771+
11772+ return ret;
11773+}
11774+
11775+static struct snd_soc_device *wm8974_socdev;
11776+
11777+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
11778+
11779+/*
11780+ * WM8974 2 wire address is 0x1a
11781+ */
11782+#define I2C_DRIVERID_WM8974 0xfefe /* liam - need a proper id */
11783+
11784+static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
11785+
11786+/* Magic definition of all other variables and things */
11787+I2C_CLIENT_INSMOD;
11788+
11789+static struct i2c_driver wm8974_i2c_driver;
11790+static struct i2c_client client_template;
11791+
11792+/* If the i2c layer weren't so broken, we could pass this kind of data
11793+ around */
11794+
11795+static int wm8974_codec_probe(struct i2c_adapter *adap, int addr, int kind)
11796+{
11797+ struct snd_soc_device *socdev = wm8974_socdev;
11798+ struct wm8974_setup_data *setup = socdev->codec_data;
11799+ struct snd_soc_codec *codec = socdev->codec;
11800+ struct i2c_client *i2c;
11801+ int ret;
11802+
11803+ if (addr != setup->i2c_address)
11804+ return -ENODEV;
11805+
11806+ client_template.adapter = adap;
11807+ client_template.addr = addr;
11808+
11809+ i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
11810+ if (i2c == NULL) {
11811+ kfree(codec);
11812+ return -ENOMEM;
11813+ }
11814+ memcpy(i2c, &client_template, sizeof(struct i2c_client));
11815+ i2c_set_clientdata(i2c, codec);
11816+ codec->control_data = i2c;
11817+
11818+ ret = i2c_attach_client(i2c);
11819+ if(ret < 0) {
11820+ err("failed to attach codec at addr %x\n", addr);
11821+ goto err;
11822+ }
11823+
11824+ ret = wm8974_init(socdev);
11825+ if(ret < 0) {
11826+ err("failed to initialise WM8974\n");
11827+ goto err;
11828+ }
11829+ return ret;
11830+
11831+err:
11832+ kfree(codec);
11833+ kfree(i2c);
11834+ return ret;
11835+}
11836+
11837+static int wm8974_i2c_detach(struct i2c_client *client)
11838+{
11839+ struct snd_soc_codec *codec = i2c_get_clientdata(client);
11840+ i2c_detach_client(client);
11841+ kfree(codec->reg_cache);
11842+ kfree(client);
11843+ return 0;
11844+}
11845+
11846+static int wm8974_i2c_attach(struct i2c_adapter *adap)
11847+{
11848+ return i2c_probe(adap, &addr_data, wm8974_codec_probe);
11849+}
11850+
11851+/* corgi i2c codec control layer */
11852+static struct i2c_driver wm8974_i2c_driver = {
11853+ .driver = {
11854+ .name = "WM8974 I2C Codec",
11855+ .owner = THIS_MODULE,
11856+ },
11857+ .id = I2C_DRIVERID_WM8974,
11858+ .attach_adapter = wm8974_i2c_attach,
11859+ .detach_client = wm8974_i2c_detach,
11860+ .command = NULL,
11861+};
11862+
11863+static struct i2c_client client_template = {
11864+ .name = "WM8974",
11865+ .driver = &wm8974_i2c_driver,
11866+};
11867+#endif
11868+
11869+static int wm8974_probe(struct platform_device *pdev)
11870+{
11871+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
11872+ struct wm8974_setup_data *setup;
11873+ struct snd_soc_codec *codec;
11874+ int ret = 0;
11875+
11876+ info("WM8974 Audio Codec %s", WM8974_VERSION);
11877+
11878+ setup = socdev->codec_data;
11879+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
11880+ if (codec == NULL)
11881+ return -ENOMEM;
11882+
11883+ socdev->codec = codec;
11884+ mutex_init(&codec->mutex);
11885+ INIT_LIST_HEAD(&codec->dapm_widgets);
11886+ INIT_LIST_HEAD(&codec->dapm_paths);
11887+
11888+ wm8974_socdev = socdev;
11889+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
11890+ if (setup->i2c_address) {
11891+ normal_i2c[0] = setup->i2c_address;
11892+ codec->hw_write = (hw_write_t)i2c_master_send;
11893+ ret = i2c_add_driver(&wm8974_i2c_driver);
11894+ if (ret != 0)
11895+ printk(KERN_ERR "can't add i2c driver");
11896+ }
11897+#else
11898+ /* Add other interfaces here */
11899+#endif
11900+ return ret;
11901+}
11902+
11903+/* power down chip */
11904+static int wm8974_remove(struct platform_device *pdev)
11905+{
11906+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
11907+ struct snd_soc_codec *codec = socdev->codec;
11908+
11909+ if (codec->control_data)
11910+ wm8974_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
11911+
11912+ snd_soc_free_pcms(socdev);
11913+ snd_soc_dapm_free(socdev);
11914+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
11915+ i2c_del_driver(&wm8974_i2c_driver);
11916+#endif
11917+ kfree(codec);
11918+
11919+ return 0;
11920+}
11921+
11922+struct snd_soc_codec_device soc_codec_dev_wm8974 = {
11923+ .probe = wm8974_probe,
11924+ .remove = wm8974_remove,
11925+ .suspend = wm8974_suspend,
11926+ .resume = wm8974_resume,
11927+};
11928+
11929+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8974);
11930+
11931+MODULE_DESCRIPTION("ASoC WM8974 driver");
11932+MODULE_AUTHOR("Liam Girdwood");
11933+MODULE_LICENSE("GPL");
11934Index: linux-2.6-pxa-new/sound/soc/codecs/wm8974.h
11935===================================================================
11936--- /dev/null
11937+++ linux-2.6-pxa-new/sound/soc/codecs/wm8974.h
11938@@ -0,0 +1,64 @@
11939+/*
11940+ * wm8974.h -- WM8974 Soc Audio driver
11941+ *
11942+ * This program is free software; you can redistribute it and/or modify
11943+ * it under the terms of the GNU General Public License version 2 as
11944+ * published by the Free Software Foundation.
11945+ */
11946+
11947+#ifndef _WM8974_H
11948+#define _WM8974_H
11949+
11950+/* WM8974 register space */
11951+
11952+#define WM8974_RESET 0x0
11953+#define WM8974_POWER1 0x1
11954+#define WM8974_POWER2 0x2
11955+#define WM8974_POWER3 0x3
11956+#define WM8974_IFACE 0x4
11957+#define WM8974_COMP 0x5
11958+#define WM8974_CLOCK 0x6
11959+#define WM8974_ADD 0x7
11960+#define WM8974_GPIO 0x8
11961+#define WM8974_DAC 0xa
11962+#define WM8974_DACVOL 0xb
11963+#define WM8974_ADC 0xe
11964+#define WM8974_ADCVOL 0xf
11965+#define WM8974_EQ1 0x12
11966+#define WM8974_EQ2 0x13
11967+#define WM8974_EQ3 0x14
11968+#define WM8974_EQ4 0x15
11969+#define WM8974_EQ5 0x16
11970+#define WM8974_DACLIM1 0x18
11971+#define WM8974_DACLIM2 0x19
11972+#define WM8974_NOTCH1 0x1b
11973+#define WM8974_NOTCH2 0x1c
11974+#define WM8974_NOTCH3 0x1d
11975+#define WM8974_NOTCH4 0x1e
11976+#define WM8974_ALC1 0x20
11977+#define WM8974_ALC2 0x21
11978+#define WM8974_ALC3 0x22
11979+#define WM8974_NGATE 0x23
11980+#define WM8974_PLLN 0x24
11981+#define WM8974_PLLK1 0x25
11982+#define WM8974_PLLK2 0x26
11983+#define WM8974_PLLK3 0x27
11984+#define WM8974_ATTEN 0x28
11985+#define WM8974_INPUT 0x2c
11986+#define WM8974_INPPGA 0x2d
11987+#define WM8974_ADCBOOST 0x2f
11988+#define WM8974_OUTPUT 0x31
11989+#define WM8974_SPKMIX 0x32
11990+#define WM8974_SPKVOL 0x36
11991+#define WM8974_MONOMIX 0x38
11992+
11993+#define WM8974_CACHEREGNUM 57
11994+
11995+struct wm8974_setup_data {
11996+ unsigned short i2c_address;
11997+};
11998+
11999+extern struct snd_soc_codec_dai wm8974_dai;
12000+extern struct snd_soc_codec_device soc_codec_dev_wm8974;
12001+
12002+#endif
12003Index: linux-2.6-pxa-new/sound/soc/codecs/wm9712.c
12004===================================================================
12005--- /dev/null
12006+++ linux-2.6-pxa-new/sound/soc/codecs/wm9712.c
12007@@ -0,0 +1,781 @@
12008+/*
12009+ * wm9712.c -- ALSA Soc WM9712 codec support
12010+ *
12011+ * Copyright 2006 Wolfson Microelectronics PLC.
12012+ * Author: Liam Girdwood
12013+ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
12014+ *
12015+ * This program is free software; you can redistribute it and/or modify it
12016+ * under the terms of the GNU General Public License as published by the
12017+ * Free Software Foundation; either version 2 of the License, or (at your
12018+ * option) any later version.
12019+ *
12020+ * Revision history
12021+ * 4th Feb 2006 Initial version.
12022+ */
12023+
12024+#include <linux/init.h>
12025+#include <linux/module.h>
12026+#include <linux/version.h>
12027+#include <linux/kernel.h>
12028+#include <linux/device.h>
12029+#include <sound/driver.h>
12030+#include <sound/core.h>
12031+#include <sound/pcm.h>
12032+#include <sound/ac97_codec.h>
12033+#include <sound/initval.h>
12034+#include <sound/soc.h>
12035+#include <sound/soc-dapm.h>
12036+
12037+#define WM9712_VERSION "0.4"
12038+
12039+static unsigned int ac97_read(struct snd_soc_codec *codec,
12040+ unsigned int reg);
12041+static int ac97_write(struct snd_soc_codec *codec,
12042+ unsigned int reg, unsigned int val);
12043+
12044+#define AC97_DIR \
12045+ (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
12046+
12047+#define AC97_RATES \
12048+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
12049+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
12050+ SNDRV_PCM_RATE_48000)
12051+
12052+/* may need to expand this */
12053+static struct snd_soc_dai_mode ac97_modes[] = {
12054+ {
12055+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE,
12056+ .pcmrate = AC97_RATES,
12057+ .pcmdir = AC97_DIR,
12058+ },
12059+};
12060+
12061+/*
12062+ * WM9712 register cache
12063+ */
12064+static const u16 wm9712_reg[] = {
12065+ 0x6174, 0x8000, 0x8000, 0x8000, // 6
12066+ 0xf0f0, 0xaaa0, 0xc008, 0x6808, // e
12067+ 0xe808, 0xaaa0, 0xad00, 0x8000, // 16
12068+ 0xe808, 0x3000, 0x8000, 0x0000, // 1e
12069+ 0x0000, 0x0000, 0x0000, 0x000f, // 26
12070+ 0x0405, 0x0410, 0xbb80, 0xbb80, // 2e
12071+ 0x0000, 0xbb80, 0x0000, 0x0000, // 36
12072+ 0x0000, 0x2000, 0x0000, 0x0000, // 3e
12073+ 0x0000, 0x0000, 0x0000, 0x0000, // 46
12074+ 0x0000, 0x0000, 0xf83e, 0xffff, // 4e
12075+ 0x0000, 0x0000, 0x0000, 0xf83e, // 56
12076+ 0x0008, 0x0000, 0x0000, 0x0000, // 5e
12077+ 0xb032, 0x3e00, 0x0000, 0x0000, // 66
12078+ 0x0000, 0x0000, 0x0000, 0x0000, // 6e
12079+ 0x0000, 0x0000, 0x0000, 0x0006, // 76
12080+ 0x0001, 0x0000, 0x574d, 0x4c12, // 7e
12081+ 0x0000, 0x0000 // virtual hp mixers
12082+};
12083+
12084+/* virtual HP mixers regs */
12085+#define HPL_MIXER 0x80
12086+#define HPR_MIXER 0x82
12087+
12088+static const char *wm9712_alc_select[] = {"None", "Left", "Right", "Stereo"};
12089+static const char *wm9712_alc_mux[] = {"Stereo", "Left", "Right", "None"};
12090+static const char *wm9712_out3_src[] = {"Left", "VREF", "Left + Right",
12091+ "Mono"};
12092+static const char *wm9712_spk_src[] = {"Speaker Mix", "Headphone Mix"};
12093+static const char *wm9712_rec_adc[] = {"Stereo", "Left", "Right", "Mute"};
12094+static const char *wm9712_base[] = {"Linear Control", "Adaptive Boost"};
12095+static const char *wm9712_rec_gain[] = {"+1.5dB Steps", "+0.75dB Steps"};
12096+static const char *wm9712_mic[] = {"Mic 1", "Differential", "Mic 2",
12097+ "Stereo"};
12098+static const char *wm9712_rec_sel[] = {"Mic", "NC", "NC", "Speaker Mixer",
12099+ "Line", "Headphone Mixer", "Phone Mixer", "Phone"};
12100+static const char *wm9712_ng_type[] = {"Constant Gain", "Mute"};
12101+static const char *wm9712_diff_sel[] = {"Mic", "Line"};
12102+
12103+static const struct soc_enum wm9712_enum[] = {
12104+SOC_ENUM_SINGLE(AC97_PCI_SVID, 14, 4, wm9712_alc_select),
12105+SOC_ENUM_SINGLE(AC97_VIDEO, 12, 4, wm9712_alc_mux),
12106+SOC_ENUM_SINGLE(AC97_AUX, 9, 4, wm9712_out3_src),
12107+SOC_ENUM_SINGLE(AC97_AUX, 8, 2, wm9712_spk_src),
12108+SOC_ENUM_SINGLE(AC97_REC_SEL, 12, 4, wm9712_rec_adc),
12109+SOC_ENUM_SINGLE(AC97_MASTER_TONE, 15, 2, wm9712_base),
12110+SOC_ENUM_DOUBLE(AC97_REC_GAIN, 14, 6, 2, wm9712_rec_gain),
12111+SOC_ENUM_SINGLE(AC97_MIC, 5, 4, wm9712_mic),
12112+SOC_ENUM_SINGLE(AC97_REC_SEL, 8, 8, wm9712_rec_sel),
12113+SOC_ENUM_SINGLE(AC97_REC_SEL, 0, 8, wm9712_rec_sel),
12114+SOC_ENUM_SINGLE(AC97_PCI_SVID, 5, 2, wm9712_ng_type),
12115+SOC_ENUM_SINGLE(0x5c, 8, 2, wm9712_diff_sel),
12116+};
12117+
12118+static const struct snd_kcontrol_new wm9712_snd_ac97_controls[] = {
12119+SOC_DOUBLE("Speaker Playback Volume", AC97_MASTER, 8, 0, 31, 1),
12120+SOC_SINGLE("Speaker Playback Switch", AC97_MASTER, 15, 1, 1),
12121+SOC_DOUBLE("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1),
12122+SOC_SINGLE("Headphone Playback Switch", AC97_HEADPHONE,15, 1, 1),
12123+
12124+SOC_SINGLE("Speaker Playback ZC Switch", AC97_MASTER, 7, 1, 0),
12125+SOC_SINGLE("Speaker Playback Invert Switch", AC97_MASTER, 6, 1, 0),
12126+SOC_SINGLE("Headphone Playback ZC Switch", AC97_HEADPHONE, 7, 1, 0),
12127+SOC_SINGLE("Mono Playback ZC Switch", AC97_MASTER_MONO, 7, 1, 0),
12128+SOC_SINGLE("Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 0),
12129+
12130+SOC_SINGLE("ALC Target Volume", AC97_CODEC_CLASS_REV, 12, 15, 0),
12131+SOC_SINGLE("ALC Hold Time", AC97_CODEC_CLASS_REV, 8, 15, 0),
12132+SOC_SINGLE("ALC Decay Time", AC97_CODEC_CLASS_REV, 4, 15, 0),
12133+SOC_SINGLE("ALC Attack Time", AC97_CODEC_CLASS_REV, 0, 15, 0),
12134+SOC_ENUM("ALC Function", wm9712_enum[0]),
12135+SOC_SINGLE("ALC Max Volume", AC97_PCI_SVID, 11, 7, 0),
12136+SOC_SINGLE("ALC ZC Timeout", AC97_PCI_SVID, 9, 3, 1),
12137+SOC_SINGLE("ALC ZC Switch", AC97_PCI_SVID, 8, 1, 0),
12138+SOC_SINGLE("ALC NG Switch", AC97_PCI_SVID, 7, 1, 0),
12139+SOC_ENUM("ALC NG Type", wm9712_enum[10]),
12140+SOC_SINGLE("ALC NG Threshold", AC97_PCI_SVID, 0, 31, 1),
12141+
12142+SOC_SINGLE("Mic Headphone Volume", AC97_VIDEO, 12, 7, 1),
12143+SOC_SINGLE("ALC Headphone Volume", AC97_VIDEO, 7, 7, 1),
12144+
12145+SOC_SINGLE("Out3 Switch", AC97_AUX, 15, 1, 1),
12146+SOC_SINGLE("Out3 ZC Switch", AC97_AUX, 7, 1, 1),
12147+SOC_SINGLE("Out3 Volume", AC97_AUX, 0, 31, 1),
12148+
12149+SOC_SINGLE("PCBeep Bypass Headphone Volume", AC97_PC_BEEP, 12, 7, 1),
12150+SOC_SINGLE("PCBeep Bypass Speaker Volume", AC97_PC_BEEP, 8, 7, 1),
12151+SOC_SINGLE("PCBeep Bypass Phone Volume", AC97_PC_BEEP, 4, 7, 1),
12152+
12153+SOC_SINGLE("Aux Playback Headphone Volume", AC97_CD, 12, 7, 1),
12154+SOC_SINGLE("Aux Playback Speaker Volume", AC97_CD, 8, 7, 1),
12155+SOC_SINGLE("Aux Playback Phone Volume", AC97_CD, 4, 7, 1),
12156+
12157+SOC_SINGLE("Phone Volume", AC97_PHONE, 0, 15, 0),
12158+SOC_DOUBLE("Line Capture Volume", AC97_LINE, 8, 0, 31, 1),
12159+
12160+SOC_SINGLE("Capture 20dB Boost Switch", AC97_REC_SEL, 14, 1, 0),
12161+SOC_SINGLE("Capture to Phone 20dB Boost Switch", AC97_REC_SEL, 11, 1, 1),
12162+
12163+SOC_SINGLE("3D Upper Cut-off Switch", AC97_3D_CONTROL, 5, 1, 1),
12164+SOC_SINGLE("3D Lower Cut-off Switch", AC97_3D_CONTROL, 4, 1, 1),
12165+SOC_SINGLE("3D Playback Volume", AC97_3D_CONTROL, 0, 15, 0),
12166+
12167+SOC_ENUM("Bass Control", wm9712_enum[5]),
12168+SOC_SINGLE("Bass Cut-off Switch", AC97_MASTER_TONE, 12, 1, 1),
12169+SOC_SINGLE("Tone Cut-off Switch", AC97_MASTER_TONE, 4, 1, 1),
12170+SOC_SINGLE("Playback Attenuate (-6dB) Switch", AC97_MASTER_TONE, 6, 1, 0),
12171+SOC_SINGLE("Bass Volume", AC97_MASTER_TONE, 8, 15, 0),
12172+SOC_SINGLE("Treble Volume", AC97_MASTER_TONE, 0, 15, 0),
12173+
12174+SOC_SINGLE("Capture ADC Switch", AC97_REC_GAIN, 15, 1, 1),
12175+SOC_ENUM("Capture Volume Steps", wm9712_enum[6]),
12176+SOC_DOUBLE("Capture Volume", AC97_REC_GAIN, 8, 0, 63, 1),
12177+SOC_SINGLE("Capture ZC Switch", AC97_REC_GAIN, 7, 1, 0),
12178+
12179+SOC_SINGLE("Mic 1 Volume", AC97_MIC, 8, 31, 1),
12180+SOC_SINGLE("Mic 2 Volume", AC97_MIC, 0, 31, 1),
12181+SOC_SINGLE("Mic 20dB Boost Switch", AC97_MIC, 7, 1, 0),
12182+};
12183+
12184+/* add non dapm controls */
12185+static int wm9712_add_controls(struct snd_soc_codec *codec)
12186+{
12187+ int err, i;
12188+
12189+ for (i = 0; i < ARRAY_SIZE(wm9712_snd_ac97_controls); i++) {
12190+ err = snd_ctl_add(codec->card,
12191+ snd_soc_cnew(&wm9712_snd_ac97_controls[i],codec, NULL));
12192+ if (err < 0)
12193+ return err;
12194+ }
12195+ return 0;
12196+}
12197+
12198+/* We have to create a fake left and right HP mixers because
12199+ * the codec only has a single control that is shared by both channels.
12200+ * This makes it impossible to determine the audio path.
12201+ */
12202+static int mixer_event (struct snd_soc_dapm_widget *w, int event)
12203+{
12204+ u16 l, r, beep, line, phone, mic, pcm, aux;
12205+
12206+ l = ac97_read(w->codec, HPL_MIXER);
12207+ r = ac97_read(w->codec, HPR_MIXER);
12208+ beep = ac97_read(w->codec, AC97_PC_BEEP);
12209+ mic = ac97_read(w->codec, AC97_VIDEO);
12210+ phone = ac97_read(w->codec, AC97_PHONE);
12211+ line = ac97_read(w->codec, AC97_LINE);
12212+ pcm = ac97_read(w->codec, AC97_PCM);
12213+ aux = ac97_read(w->codec, AC97_CD);
12214+
12215+ if (l & 0x1 || r & 0x1)
12216+ ac97_write(w->codec, AC97_VIDEO, mic & 0x7fff);
12217+ else
12218+ ac97_write(w->codec, AC97_VIDEO, mic | 0x8000);
12219+
12220+ if (l & 0x2 || r & 0x2)
12221+ ac97_write(w->codec, AC97_PCM, pcm & 0x7fff);
12222+ else
12223+ ac97_write(w->codec, AC97_PCM, pcm | 0x8000);
12224+
12225+ if (l & 0x4 || r & 0x4)
12226+ ac97_write(w->codec, AC97_LINE, line & 0x7fff);
12227+ else
12228+ ac97_write(w->codec, AC97_LINE, line | 0x8000);
12229+
12230+ if (l & 0x8 || r & 0x8)
12231+ ac97_write(w->codec, AC97_PHONE, phone & 0x7fff);
12232+ else
12233+ ac97_write(w->codec, AC97_PHONE, phone | 0x8000);
12234+
12235+ if (l & 0x10 || r & 0x10)
12236+ ac97_write(w->codec, AC97_CD, aux & 0x7fff);
12237+ else
12238+ ac97_write(w->codec, AC97_CD, aux | 0x8000);
12239+
12240+ if (l & 0x20 || r & 0x20)
12241+ ac97_write(w->codec, AC97_PC_BEEP, beep & 0x7fff);
12242+ else
12243+ ac97_write(w->codec, AC97_PC_BEEP, beep | 0x8000);
12244+
12245+ return 0;
12246+}
12247+
12248+/* Left Headphone Mixers */
12249+static const struct snd_kcontrol_new wm9712_hpl_mixer_controls[] = {
12250+ SOC_DAPM_SINGLE("PCBeep Bypass Switch", HPL_MIXER, 5, 1, 0),
12251+ SOC_DAPM_SINGLE("Aux Playback Switch", HPL_MIXER, 4, 1, 0),
12252+ SOC_DAPM_SINGLE("Phone Bypass Switch", HPL_MIXER, 3, 1, 0),
12253+ SOC_DAPM_SINGLE("Line Bypass Switch", HPL_MIXER, 2, 1, 0),
12254+ SOC_DAPM_SINGLE("PCM Playback Switch", HPL_MIXER, 1, 1, 0),
12255+ SOC_DAPM_SINGLE("Mic Sidetone Switch", HPL_MIXER, 0, 1, 0),
12256+};
12257+
12258+/* Right Headphone Mixers */
12259+static const struct snd_kcontrol_new wm9712_hpr_mixer_controls[] = {
12260+ SOC_DAPM_SINGLE("PCBeep Bypass Switch", HPR_MIXER, 5, 1, 0),
12261+ SOC_DAPM_SINGLE("Aux Playback Switch", HPR_MIXER, 4, 1, 0),
12262+ SOC_DAPM_SINGLE("Phone Bypass Switch", HPR_MIXER, 3, 1, 0),
12263+ SOC_DAPM_SINGLE("Line Bypass Switch", HPR_MIXER, 2, 1, 0),
12264+ SOC_DAPM_SINGLE("PCM Playback Switch", HPR_MIXER, 1, 1, 0),
12265+ SOC_DAPM_SINGLE("Mic Sidetone Switch", HPR_MIXER, 0, 1, 0),
12266+};
12267+
12268+/* Speaker Mixer */
12269+static const struct snd_kcontrol_new wm9712_speaker_mixer_controls[] = {
12270+ SOC_DAPM_SINGLE("PCBeep Bypass Switch", AC97_PC_BEEP, 11, 1, 1),
12271+ SOC_DAPM_SINGLE("Aux Playback Switch", AC97_CD, 11, 1, 1),
12272+ SOC_DAPM_SINGLE("Phone Bypass Switch", AC97_PHONE, 14, 1, 1),
12273+ SOC_DAPM_SINGLE("Line Bypass Switch", AC97_LINE, 14, 1, 1),
12274+ SOC_DAPM_SINGLE("PCM Playback Switch", AC97_PCM, 14, 1, 1),
12275+};
12276+
12277+/* Phone Mixer */
12278+static const struct snd_kcontrol_new wm9712_phone_mixer_controls[] = {
12279+ SOC_DAPM_SINGLE("PCBeep Bypass Switch", AC97_PC_BEEP, 7, 1, 1),
12280+ SOC_DAPM_SINGLE("Aux Playback Switch", AC97_CD, 7, 1, 1),
12281+ SOC_DAPM_SINGLE("Line Bypass Switch", AC97_LINE, 13, 1, 1),
12282+ SOC_DAPM_SINGLE("PCM Playback Switch", AC97_PCM, 13, 1, 1),
12283+ SOC_DAPM_SINGLE("Mic 1 Sidetone Switch", AC97_MIC, 14, 1, 1),
12284+ SOC_DAPM_SINGLE("Mic 2 Sidetone Switch", AC97_MIC, 13, 1, 1),
12285+};
12286+
12287+/* ALC headphone mux */
12288+static const struct snd_kcontrol_new wm9712_alc_mux_controls =
12289+SOC_DAPM_ENUM("Route", wm9712_enum[1]);
12290+
12291+/* out 3 mux */
12292+static const struct snd_kcontrol_new wm9712_out3_mux_controls =
12293+SOC_DAPM_ENUM("Route", wm9712_enum[2]);
12294+
12295+/* spk mux */
12296+static const struct snd_kcontrol_new wm9712_spk_mux_controls =
12297+SOC_DAPM_ENUM("Route", wm9712_enum[3]);
12298+
12299+/* Capture to Phone mux */
12300+static const struct snd_kcontrol_new wm9712_capture_phone_mux_controls =
12301+SOC_DAPM_ENUM("Route", wm9712_enum[4]);
12302+
12303+/* Capture left select */
12304+static const struct snd_kcontrol_new wm9712_capture_selectl_controls =
12305+SOC_DAPM_ENUM("Route", wm9712_enum[8]);
12306+
12307+/* Capture right select */
12308+static const struct snd_kcontrol_new wm9712_capture_selectr_controls =
12309+SOC_DAPM_ENUM("Route", wm9712_enum[9]);
12310+
12311+/* Mic select */
12312+static const struct snd_kcontrol_new wm9712_mic_src_controls =
12313+SOC_DAPM_ENUM("Route", wm9712_enum[7]);
12314+
12315+/* diff select */
12316+static const struct snd_kcontrol_new wm9712_diff_sel_controls =
12317+SOC_DAPM_ENUM("Route", wm9712_enum[11]);
12318+
12319+static const struct snd_soc_dapm_widget wm9712_dapm_widgets[] = {
12320+SND_SOC_DAPM_MUX("ALC Sidetone Mux", SND_SOC_NOPM, 0, 0,
12321+ &wm9712_alc_mux_controls),
12322+SND_SOC_DAPM_MUX("Out3 Mux", SND_SOC_NOPM, 0, 0,
12323+ &wm9712_out3_mux_controls),
12324+SND_SOC_DAPM_MUX("Speaker Mux", SND_SOC_NOPM, 0, 0,
12325+ &wm9712_spk_mux_controls),
12326+SND_SOC_DAPM_MUX("Capture Phone Mux", SND_SOC_NOPM, 0, 0,
12327+ &wm9712_capture_phone_mux_controls),
12328+SND_SOC_DAPM_MUX("Left Capture Select", SND_SOC_NOPM, 0, 0,
12329+ &wm9712_capture_selectl_controls),
12330+SND_SOC_DAPM_MUX("Right Capture Select", SND_SOC_NOPM, 0, 0,
12331+ &wm9712_capture_selectr_controls),
12332+SND_SOC_DAPM_MUX("Mic Select Source", SND_SOC_NOPM, 0, 0,
12333+ &wm9712_mic_src_controls),
12334+SND_SOC_DAPM_MUX("Differential Source", SND_SOC_NOPM, 0, 0,
12335+ &wm9712_diff_sel_controls),
12336+SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
12337+SND_SOC_DAPM_MIXER_E("Left HP Mixer", AC97_INT_PAGING, 9, 1,
12338+ &wm9712_hpl_mixer_controls[0], ARRAY_SIZE(wm9712_hpl_mixer_controls),
12339+ mixer_event, SND_SOC_DAPM_POST_REG),
12340+SND_SOC_DAPM_MIXER_E("Right HP Mixer", AC97_INT_PAGING, 8, 1,
12341+ &wm9712_hpr_mixer_controls[0], ARRAY_SIZE(wm9712_hpr_mixer_controls),
12342+ mixer_event, SND_SOC_DAPM_POST_REG),
12343+SND_SOC_DAPM_MIXER("Phone Mixer", AC97_INT_PAGING, 6, 1,
12344+ &wm9712_phone_mixer_controls[0], ARRAY_SIZE(wm9712_phone_mixer_controls)),
12345+SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_INT_PAGING, 7, 1,
12346+ &wm9712_speaker_mixer_controls[0],
12347+ ARRAY_SIZE(wm9712_speaker_mixer_controls)),
12348+SND_SOC_DAPM_MIXER("Mono Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
12349+SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback", AC97_INT_PAGING, 14, 1),
12350+SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback", AC97_INT_PAGING, 13, 1),
12351+SND_SOC_DAPM_DAC("Aux DAC", "Aux Playback", SND_SOC_NOPM, 0, 0),
12352+SND_SOC_DAPM_ADC("Left ADC", "Left HiFi Capture", AC97_INT_PAGING, 12, 1),
12353+SND_SOC_DAPM_ADC("Right ADC", "Right HiFi Capture", AC97_INT_PAGING, 11, 1),
12354+SND_SOC_DAPM_PGA("Headphone PGA", AC97_INT_PAGING, 4, 1, NULL, 0),
12355+SND_SOC_DAPM_PGA("Speaker PGA", AC97_INT_PAGING, 3, 1, NULL, 0),
12356+SND_SOC_DAPM_PGA("Out 3 PGA", AC97_INT_PAGING, 5, 1, NULL, 0),
12357+SND_SOC_DAPM_PGA("Line PGA", AC97_INT_PAGING, 2, 1, NULL, 0),
12358+SND_SOC_DAPM_PGA("Phone PGA", AC97_INT_PAGING, 1, 1, NULL, 0),
12359+SND_SOC_DAPM_PGA("Mic PGA", AC97_INT_PAGING, 0, 1, NULL, 0),
12360+SND_SOC_DAPM_MICBIAS("Mic Bias", AC97_INT_PAGING, 10, 1),
12361+SND_SOC_DAPM_OUTPUT("MONOOUT"),
12362+SND_SOC_DAPM_OUTPUT("HPOUTL"),
12363+SND_SOC_DAPM_OUTPUT("HPOUTR"),
12364+SND_SOC_DAPM_OUTPUT("LOUT2"),
12365+SND_SOC_DAPM_OUTPUT("ROUT2"),
12366+SND_SOC_DAPM_OUTPUT("OUT3"),
12367+SND_SOC_DAPM_INPUT("LINEINL"),
12368+SND_SOC_DAPM_INPUT("LINEINR"),
12369+SND_SOC_DAPM_INPUT("PHONE"),
12370+SND_SOC_DAPM_INPUT("PCBEEP"),
12371+SND_SOC_DAPM_INPUT("MIC1"),
12372+SND_SOC_DAPM_INPUT("MIC2"),
12373+};
12374+
12375+static const char *audio_map[][3] = {
12376+ /* virtual mixer - mixes left & right channels for spk and mono */
12377+ {"AC97 Mixer", NULL, "Left DAC"},
12378+ {"AC97 Mixer", NULL, "Right DAC"},
12379+
12380+ /* Left HP mixer */
12381+ {"Left HP Mixer", "PCBeep Bypass Switch", "PCBEEP"},
12382+ {"Left HP Mixer", "Aux Playback Switch", "Aux DAC"},
12383+ {"Left HP Mixer", "Phone Bypass Switch", "Phone PGA"},
12384+ {"Left HP Mixer", "Line Bypass Switch", "Line PGA"},
12385+ {"Left HP Mixer", "PCM Playback Switch", "Left DAC"},
12386+ {"Left HP Mixer", "Mic Sidetone Switch", "Mic PGA"},
12387+ {"Left HP Mixer", NULL, "ALC Sidetone Mux"},
12388+ //{"Right HP Mixer", NULL, "HP Mixer"},
12389+
12390+ /* Right HP mixer */
12391+ {"Right HP Mixer", "PCBeep Bypass Switch", "PCBEEP"},
12392+ {"Right HP Mixer", "Aux Playback Switch", "Aux DAC"},
12393+ {"Right HP Mixer", "Phone Bypass Switch", "Phone PGA"},
12394+ {"Right HP Mixer", "Line Bypass Switch", "Line PGA"},
12395+ {"Right HP Mixer", "PCM Playback Switch", "Right DAC"},
12396+ {"Right HP Mixer", "Mic Sidetone Switch", "Mic PGA"},
12397+ {"Right HP Mixer", NULL, "ALC Sidetone Mux"},
12398+
12399+ /* speaker mixer */
12400+ {"Speaker Mixer", "PCBeep Bypass Switch", "PCBEEP"},
12401+ {"Speaker Mixer", "Line Bypass Switch", "Line PGA"},
12402+ {"Speaker Mixer", "PCM Playback Switch", "AC97 Mixer"},
12403+ {"Speaker Mixer", "Phone Bypass Switch", "Phone PGA"},
12404+ {"Speaker Mixer", "Aux Playback Switch", "Aux DAC"},
12405+
12406+ /* Phone mixer */
12407+ {"Phone Mixer", "PCBeep Bypass Switch", "PCBEEP"},
12408+ {"Phone Mixer", "Line Bypass Switch", "Line PGA"},
12409+ {"Phone Mixer", "Aux Playback Switch", "Aux DAC"},
12410+ {"Phone Mixer", "PCM Playback Switch", "AC97 Mixer"},
12411+ {"Phone Mixer", "Mic 1 Sidetone Switch", "Mic PGA"},
12412+ {"Phone Mixer", "Mic 2 Sidetone Switch", "Mic PGA"},
12413+
12414+ /* inputs */
12415+ {"Line PGA", NULL, "LINEINL"},
12416+ {"Line PGA", NULL, "LINEINR"},
12417+ {"Phone PGA", NULL, "PHONE"},
12418+ {"Mic PGA", NULL, "MIC1"},
12419+ {"Mic PGA", NULL, "MIC2"},
12420+
12421+ /* left capture selector */
12422+ {"Left Capture Select", "Mic", "MIC1"},
12423+ {"Left Capture Select", "Speaker Mixer", "Speaker Mixer"},
12424+ {"Left Capture Select", "Line", "LINEINL"},
12425+ {"Left Capture Select", "Headphone Mixer", "Left HP Mixer"},
12426+ {"Left Capture Select", "Phone Mixer", "Phone Mixer"},
12427+ {"Left Capture Select", "Phone", "PHONE"},
12428+
12429+ /* right capture selector */
12430+ {"Right Capture Select", "Mic", "MIC2"},
12431+ {"Right Capture Select", "Speaker Mixer", "Speaker Mixer"},
12432+ {"Right Capture Select", "Line", "LINEINR"},
12433+ {"Right Capture Select", "Headphone Mixer", "Right HP Mixer"},
12434+ {"Right Capture Select", "Phone Mixer", "Phone Mixer"},
12435+ {"Right Capture Select", "Phone", "PHONE"},
12436+
12437+ /* ALC Sidetone */
12438+ {"ALC Sidetone Mux", "Stereo", "Left Capture Select"},
12439+ {"ALC Sidetone Mux", "Stereo", "Right Capture Select"},
12440+ {"ALC Sidetone Mux", "Left", "Left Capture Select"},
12441+ {"ALC Sidetone Mux", "Right", "Right Capture Select"},
12442+
12443+ /* ADC's */
12444+ {"Left ADC", NULL, "Left Capture Select"},
12445+ {"Right ADC", NULL, "Right Capture Select"},
12446+
12447+ /* outputs */
12448+ {"MONOOUT", NULL, "Phone Mixer"},
12449+ {"HPOUTL", NULL, "Headphone PGA"},
12450+ {"Headphone PGA", NULL, "Left HP Mixer"},
12451+ {"HPOUTR", NULL, "Headphone PGA"},
12452+ {"Headphone PGA", NULL, "Right HP Mixer"},
12453+
12454+ /* mono hp mixer */
12455+ {"Mono HP Mixer", NULL, "Left HP Mixer"},
12456+ {"Mono HP Mixer", NULL, "Right HP Mixer"},
12457+
12458+ /* Out3 Mux */
12459+ {"Out3 Mux", "Left", "Left HP Mixer"},
12460+ {"Out3 Mux", "Mono", "Phone Mixer"},
12461+ {"Out3 Mux", "Left + Right", "Mono HP Mixer"},
12462+ {"Out 3 PGA", NULL, "Out3 Mux"},
12463+ {"OUT3", NULL, "Out 3 PGA"},
12464+
12465+ /* speaker Mux */
12466+ {"Speaker Mux", "Speaker Mix", "Speaker Mixer"},
12467+ {"Speaker Mux", "Headphone Mix", "Mono HP Mixer"},
12468+ {"Speaker PGA", NULL, "Speaker Mux"},
12469+ {"LOUT2", NULL, "Speaker PGA"},
12470+ {"ROUT2", NULL, "Speaker PGA"},
12471+
12472+ {NULL, NULL, NULL},
12473+};
12474+
12475+static int wm9712_add_widgets(struct snd_soc_codec *codec)
12476+{
12477+ int i;
12478+
12479+ for(i = 0; i < ARRAY_SIZE(wm9712_dapm_widgets); i++) {
12480+ snd_soc_dapm_new_control(codec, &wm9712_dapm_widgets[i]);
12481+ }
12482+
12483+ /* set up audio path audio_mapnects */
12484+ for(i = 0; audio_map[i][0] != NULL; i++) {
12485+ snd_soc_dapm_connect_input(codec, audio_map[i][0],
12486+ audio_map[i][1], audio_map[i][2]);
12487+ }
12488+
12489+ snd_soc_dapm_new_widgets(codec);
12490+ return 0;
12491+}
12492+
12493+static unsigned int ac97_read(struct snd_soc_codec *codec,
12494+ unsigned int reg)
12495+{
12496+ u16 *cache = codec->reg_cache;
12497+
12498+ if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
12499+ reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 ||
12500+ reg == AC97_REC_GAIN)
12501+ return soc_ac97_ops.read(codec->ac97, reg);
12502+ else {
12503+ reg = reg >> 1;
12504+
12505+ if (reg > (ARRAY_SIZE(wm9712_reg)))
12506+ return -EIO;
12507+
12508+ return cache[reg];
12509+ }
12510+}
12511+
12512+static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
12513+ unsigned int val)
12514+{
12515+ u16 *cache = codec->reg_cache;
12516+
12517+ soc_ac97_ops.write(codec->ac97, reg, val);
12518+ reg = reg >> 1;
12519+ if (reg <= (ARRAY_SIZE(wm9712_reg)))
12520+ cache[reg] = val;
12521+
12522+ return 0;
12523+}
12524+
12525+static int ac97_prepare(struct snd_pcm_substream *substream)
12526+{
12527+ struct snd_pcm_runtime *runtime = substream->runtime;
12528+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
12529+ struct snd_soc_device *socdev = rtd->socdev;
12530+ struct snd_soc_codec *codec = socdev->codec;
12531+ int reg;
12532+ u16 vra;
12533+
12534+ vra = ac97_read(codec, AC97_EXTENDED_STATUS);
12535+ ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1);
12536+
12537+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
12538+ reg = AC97_PCM_FRONT_DAC_RATE;
12539+ else
12540+ reg = AC97_PCM_LR_ADC_RATE;
12541+
12542+ return ac97_write(codec, reg, runtime->rate);
12543+}
12544+
12545+static int ac97_aux_prepare(struct snd_pcm_substream *substream)
12546+{
12547+ struct snd_pcm_runtime *runtime = substream->runtime;
12548+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
12549+ struct snd_soc_device *socdev = rtd->socdev;
12550+ struct snd_soc_codec *codec = socdev->codec;
12551+ u16 vra, xsle;
12552+
12553+ vra = ac97_read(codec, AC97_EXTENDED_STATUS);
12554+ ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1);
12555+ xsle = ac97_read(codec, AC97_PCI_SID);
12556+ ac97_write(codec, AC97_PCI_SID, xsle | 0x8000);
12557+
12558+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
12559+ return -ENODEV;
12560+
12561+ return ac97_write(codec, AC97_PCM_SURR_DAC_RATE, runtime->rate);
12562+}
12563+
12564+struct snd_soc_codec_dai wm9712_dai[] = {
12565+{
12566+ .name = "AC97 HiFi",
12567+ .playback = {
12568+ .stream_name = "HiFi Playback",
12569+ .channels_min = 1,
12570+ .channels_max = 2,},
12571+ .capture = {
12572+ .stream_name = "HiFi Capture",
12573+ .channels_min = 1,
12574+ .channels_max = 2,},
12575+ .ops = {
12576+ .prepare = ac97_prepare,},
12577+ .caps = {
12578+ .num_modes = ARRAY_SIZE(ac97_modes),
12579+ .mode = ac97_modes,},
12580+ },
12581+ {
12582+ .name = "AC97 Aux",
12583+ .playback = {
12584+ .stream_name = "Aux Playback",
12585+ .channels_min = 1,
12586+ .channels_max = 1,},
12587+ .ops = {
12588+ .prepare = ac97_aux_prepare,},
12589+ .caps = {
12590+ .num_modes = ARRAY_SIZE(ac97_modes),
12591+ .mode = ac97_modes,},
12592+ },
12593+};
12594+EXPORT_SYMBOL_GPL(wm9712_dai);
12595+
12596+static int wm9712_dapm_event(struct snd_soc_codec *codec, int event)
12597+{
12598+ u16 reg;
12599+
12600+ switch (event) {
12601+ case SNDRV_CTL_POWER_D0: /* full On */
12602+ /* liam - maybe enable thermal shutdown */
12603+ reg = ac97_read(codec, AC97_EXTENDED_MID) & 0xdfff;
12604+ ac97_write(codec, AC97_EXTENDED_MID, reg);
12605+ break;
12606+ case SNDRV_CTL_POWER_D1: /* partial On */
12607+ case SNDRV_CTL_POWER_D2: /* partial On */
12608+ break;
12609+ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
12610+ /* enable master bias and vmid */
12611+ reg = ac97_read(codec, AC97_EXTENDED_MID) & 0xbbff;
12612+ ac97_write(codec, AC97_EXTENDED_MID, reg);
12613+ ac97_write(codec, AC97_POWERDOWN, 0x0000);
12614+ break;
12615+ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
12616+ /* disable everything including AC link */
12617+ ac97_write(codec, AC97_EXTENDED_MID, 0xffff);
12618+ ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff);
12619+ ac97_write(codec, AC97_POWERDOWN, 0xffff);
12620+ break;
12621+ }
12622+ codec->dapm_state = event;
12623+ return 0;
12624+}
12625+
12626+static int wm9712_reset(struct snd_soc_codec *codec, int try_warm)
12627+{
12628+ if (try_warm && soc_ac97_ops.warm_reset) {
12629+ soc_ac97_ops.warm_reset(codec->ac97);
12630+ if (!(ac97_read(codec, 0) & 0x8000))
12631+ return 1;
12632+ }
12633+
12634+ soc_ac97_ops.reset(codec->ac97);
12635+ if (ac97_read(codec, 0) & 0x8000)
12636+ goto err;
12637+ return 0;
12638+
12639+err:
12640+ printk(KERN_ERR "WM9712 AC97 reset failed\n");
12641+ return -EIO;
12642+}
12643+
12644+static int wm9712_soc_suspend(struct platform_device *pdev,
12645+ pm_message_t state)
12646+{
12647+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
12648+ struct snd_soc_codec *codec = socdev->codec;
12649+
12650+ wm9712_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
12651+ return 0;
12652+}
12653+
12654+static int wm9712_soc_resume(struct platform_device *pdev)
12655+{
12656+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
12657+ struct snd_soc_codec *codec = socdev->codec;
12658+ int i, ret;
12659+ u16 *cache = codec->reg_cache;
12660+
12661+ ret = wm9712_reset(codec, 1);
12662+ if (ret < 0){
12663+ printk(KERN_ERR "could not reset AC97 codec\n");
12664+ return ret;
12665+ }
12666+
12667+ wm9712_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
12668+
12669+ if (ret == 0) {
12670+ /* Sync reg_cache with the hardware after cold reset */
12671+ for (i = 2; i < ARRAY_SIZE(wm9712_reg) << 1; i+=2) {
12672+ if (i == AC97_INT_PAGING || i == AC97_POWERDOWN ||
12673+ (i > 0x58 && i != 0x5c))
12674+ continue;
12675+ soc_ac97_ops.write(codec->ac97, i, cache[i>>1]);
12676+ }
12677+ }
12678+
12679+ if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0)
12680+ wm9712_dapm_event(codec, SNDRV_CTL_POWER_D0);
12681+
12682+ return ret;
12683+}
12684+
12685+static int wm9712_soc_probe(struct platform_device *pdev)
12686+{
12687+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
12688+ struct snd_soc_codec *codec;
12689+ int ret = 0;
12690+
12691+ printk(KERN_INFO "WM9711/WM9712 SoC Audio Codec %s\n", WM9712_VERSION);
12692+
12693+ socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
12694+ if (socdev->codec == NULL)
12695+ return -ENOMEM;
12696+ codec = socdev->codec;
12697+ mutex_init(&codec->mutex);
12698+
12699+ codec->reg_cache =
12700+ kzalloc(sizeof(u16) * ARRAY_SIZE(wm9712_reg), GFP_KERNEL);
12701+ if (codec->reg_cache == NULL) {
12702+ kfree(codec->ac97);
12703+ kfree(socdev->codec);
12704+ socdev->codec = NULL;
12705+ return -ENOMEM;
12706+ }
12707+ memcpy(codec->reg_cache, wm9712_reg, sizeof(u16) * ARRAY_SIZE(wm9712_reg));
12708+ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm9712_reg);
12709+ codec->reg_cache_step = 2;
12710+
12711+ codec->name = "WM9712";
12712+ codec->owner = THIS_MODULE;
12713+ codec->dai = wm9712_dai;
12714+ codec->num_dai = ARRAY_SIZE(wm9712_dai);
12715+ codec->write = ac97_write;
12716+ codec->read = ac97_read;
12717+ codec->dapm_event = wm9712_dapm_event;
12718+ INIT_LIST_HEAD(&codec->dapm_widgets);
12719+ INIT_LIST_HEAD(&codec->dapm_paths);
12720+
12721+ ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
12722+ if (ret < 0)
12723+ goto err;
12724+
12725+ /* register pcms */
12726+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
12727+ if (ret < 0)
12728+ goto pcm_err;
12729+
12730+ ret = wm9712_reset(codec, 0);
12731+ if (ret < 0) {
12732+ printk(KERN_ERR "AC97 link error\n");
12733+ goto reset_err;
12734+ }
12735+
12736+ /* set alc mux to none */
12737+ ac97_write(codec, AC97_VIDEO, ac97_read(codec, AC97_VIDEO) | 0x3000);
12738+
12739+ wm9712_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
12740+ wm9712_add_controls(codec);
12741+ wm9712_add_widgets(codec);
12742+ ret = snd_soc_register_card(socdev);
12743+ if (ret < 0)
12744+ goto reset_err;
12745+
12746+ return 0;
12747+
12748+reset_err:
12749+ snd_soc_free_pcms(socdev);
12750+
12751+pcm_err:
12752+ snd_soc_free_ac97_codec(codec);
12753+
12754+err:
12755+ kfree(socdev->codec->reg_cache);
12756+ kfree(socdev->codec);
12757+ socdev->codec = NULL;
12758+ return ret;
12759+}
12760+
12761+static int wm9712_soc_remove(struct platform_device *pdev)
12762+{
12763+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
12764+ struct snd_soc_codec *codec = socdev->codec;
12765+
12766+ if (codec == NULL)
12767+ return 0;
12768+
12769+ snd_soc_dapm_free(socdev);
12770+ snd_soc_free_pcms(socdev);
12771+ snd_soc_free_ac97_codec(codec);
12772+ kfree(codec->reg_cache);
12773+ kfree(codec);
12774+ return 0;
12775+}
12776+
12777+struct snd_soc_codec_device soc_codec_dev_wm9712 = {
12778+ .probe = wm9712_soc_probe,
12779+ .remove = wm9712_soc_remove,
12780+ .suspend = wm9712_soc_suspend,
12781+ .resume = wm9712_soc_resume,
12782+};
12783+
12784+EXPORT_SYMBOL_GPL(soc_codec_dev_wm9712);
12785+
12786+MODULE_DESCRIPTION("ASoC WM9711/WM9712 driver");
12787+MODULE_AUTHOR("Liam Girdwood");
12788+MODULE_LICENSE("GPL");
12789Index: linux-2.6-pxa-new/sound/soc/codecs/wm9712.h
12790===================================================================
12791--- /dev/null
12792+++ linux-2.6-pxa-new/sound/soc/codecs/wm9712.h
12793@@ -0,0 +1,14 @@
12794+/*
12795+ * wm9712.h -- WM9712 Soc Audio driver
12796+ */
12797+
12798+#ifndef _WM9712_H
12799+#define _WM9712_H
12800+
12801+#define WM9712_DAI_AC97_HIFI 0
12802+#define WM9712_DAI_AC97_AUX 1
12803+
12804+extern struct snd_soc_codec_dai wm9712_dai[2];
12805+extern struct snd_soc_codec_device soc_codec_dev_wm9712;
12806+
12807+#endif
12808Index: linux-2.6-pxa-new/sound/soc/codecs/wm9713.c
12809===================================================================
12810--- /dev/null
12811+++ linux-2.6-pxa-new/sound/soc/codecs/wm9713.c
12812@@ -0,0 +1,1313 @@
12813+/*
12814+ * wm9713.c -- ALSA Soc WM9713 codec support
12815+ *
12816+ * Copyright 2006 Wolfson Microelectronics PLC.
12817+ * Author: Liam Girdwood
12818+ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
12819+ *
12820+ * This program is free software; you can redistribute it and/or modify it
12821+ * under the terms of the GNU General Public License as published by the
12822+ * Free Software Foundation; either version 2 of the License, or (at your
12823+ * option) any later version.
12824+ *
12825+ * Revision history
12826+ * 4th Feb 2006 Initial version.
12827+ *
12828+ * Features:-
12829+ *
12830+ * o Support for AC97 Codec, Voice DAC and Aux DAC
12831+ * o Support for DAPM
12832+ */
12833+
12834+#include <linux/init.h>
12835+#include <linux/module.h>
12836+#include <linux/device.h>
12837+#include <sound/driver.h>
12838+#include <sound/core.h>
12839+#include <sound/pcm.h>
12840+#include <sound/ac97_codec.h>
12841+#include <sound/initval.h>
12842+#include <sound/soc.h>
12843+#include <sound/soc-dapm.h>
12844+
12845+#define WM9713_VERSION "0.12"
12846+
12847+struct wm9713 {
12848+ u32 pll; /* current PLL frequency */
12849+ u32 pll_resume; /* PLL resume frequency */
12850+};
12851+
12852+static unsigned int ac97_read(struct snd_soc_codec *codec,
12853+ unsigned int reg);
12854+static int ac97_write(struct snd_soc_codec *codec,
12855+ unsigned int reg, unsigned int val);
12856+
12857+#define AC97_DIR \
12858+ (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
12859+
12860+#define AC97_RATES \
12861+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | \
12862+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
12863+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
12864+ SNDRV_PCM_RATE_48000)
12865+
12866+/* may need to expand this */
12867+static struct snd_soc_dai_mode ac97_modes[] = {
12868+ {
12869+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE,
12870+ .pcmrate = AC97_RATES,
12871+ },
12872+};
12873+
12874+#define WM9713_VOICE_DAIFMT \
12875+ (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J | \
12876+ SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_DSP_A | \
12877+ SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | \
12878+ SND_SOC_DAIFMT_NB_IF | SND_SOC_DAIFMT_IB_NF | \
12879+ SND_SOC_DAIFMT_IB_IF)
12880+
12881+#define WM9713_DIR \
12882+ (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
12883+
12884+#define WM9713_VOICE_FSB \
12885+ (SND_SOC_FSBD(1) | SND_SOC_FSBD(2) | SND_SOC_FSBD(4) | \
12886+ SND_SOC_FSBD(8) | SND_SOC_FSBD(16))
12887+
12888+#define WM9713_VOICE_RATES \
12889+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \
12890+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | \
12891+ SNDRV_PCM_RATE_96000)
12892+
12893+#define WM9713_HIFI_BITS \
12894+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
12895+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
12896+
12897+/*
12898+ * Voice modes
12899+ */
12900+static struct snd_soc_dai_mode wm9713_voice_modes[] = {
12901+ /* master modes */
12902+ {
12903+ .fmt = WM9713_VOICE_DAIFMT | SND_SOC_DAIFMT_CBM_CFM | \
12904+ SND_SOC_DAIFMT_CBM_CFS,
12905+ .pcmfmt = WM9713_HIFI_BITS,
12906+ .pcmrate = WM9713_VOICE_RATES,
12907+ .pcmdir = WM9713_DIR,
12908+ .flags = SND_SOC_DAI_BFS_DIV,
12909+ .fs = 256,
12910+ .bfs = WM9713_VOICE_FSB,
12911+ },
12912+
12913+ /* slave modes */
12914+ {
12915+ .fmt = WM9713_VOICE_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
12916+ .pcmfmt = WM9713_HIFI_BITS,
12917+ .pcmrate = WM9713_VOICE_RATES,
12918+ .pcmdir = WM9713_DIR,
12919+ .flags = SND_SOC_DAI_BFS_DIV,
12920+ .fs = SND_SOC_FS_ALL,
12921+ .bfs = SND_SOC_FSB_ALL,
12922+ },
12923+};
12924+
12925+/*
12926+ * WM9713 register cache
12927+ * Reg 0x3c bit 15 is used by touch driver.
12928+ */
12929+static const u16 wm9713_reg[] = {
12930+ 0x6174, 0x8080, 0x8080, 0x8080, // 6
12931+ 0xc880, 0xe808, 0xe808, 0x0808, // e
12932+ 0x00da, 0x8000, 0xd600, 0xaaa0, // 16
12933+ 0xaaa0, 0xaaa0, 0x0000, 0x0000, // 1e
12934+ 0x0f0f, 0x0040, 0x0000, 0x7f00, // 26
12935+ 0x0405, 0x0410, 0xbb80, 0xbb80, // 2e
12936+ 0x0000, 0xbb80, 0x0000, 0x4523, // 36
12937+ 0x0000, 0x2000, 0x7eff, 0xffff, // 3e
12938+ 0x0000, 0x0000, 0x0080, 0x0000, // 46
12939+ 0x0000, 0x0000, 0xfffe, 0xffff, // 4e
12940+ 0x0000, 0x0000, 0x0000, 0xfffe, // 56
12941+ 0x4000, 0x0000, 0x0000, 0x0000, // 5e
12942+ 0xb032, 0x3e00, 0x0000, 0x0000, // 66
12943+ 0x0000, 0x0000, 0x0000, 0x0000, // 6e
12944+ 0x0000, 0x0000, 0x0000, 0x0006, // 76
12945+ 0x0001, 0x0000, 0x574d, 0x4c13, // 7e
12946+ 0x0000, 0x0000, 0x0000 // virtual hp & mic mixers
12947+};
12948+
12949+/* virtual HP mixers regs */
12950+#define HPL_MIXER 0x80
12951+#define HPR_MIXER 0x82
12952+#define MICB_MUX 0x82
12953+
12954+static const char *wm9713_mic_mixer[] = {"Stereo", "Mic 1", "Mic 2", "Mute"};
12955+static const char *wm9713_rec_mux[] = {"Stereo", "Left", "Right", "Mute"};
12956+static const char *wm9713_rec_src[] =
12957+ {"Mic 1", "Mic 2", "Line", "Mono In", "Headphone", "Speaker",
12958+ "Mono Out", "Zh"};
12959+static const char *wm9713_rec_gain[] = {"+1.5dB Steps", "+0.75dB Steps"};
12960+static const char *wm9713_alc_select[] = {"None", "Left", "Right", "Stereo"};
12961+static const char *wm9713_mono_pga[] = {"Vmid", "Zh", "Mono", "Inv",
12962+ "Mono Vmid", "Inv Vmid"};
12963+static const char *wm9713_spk_pga[] =
12964+ {"Vmid", "Zh", "Headphone", "Speaker", "Inv", "Headphone Vmid",
12965+ "Speaker Vmid", "Inv Vmid"};
12966+static const char *wm9713_hp_pga[] = {"Vmid", "Zh", "Headphone",
12967+ "Headphone Vmid"};
12968+static const char *wm9713_out3_pga[] = {"Vmid", "Zh", "Inv 1", "Inv 1 Vmid"};
12969+static const char *wm9713_out4_pga[] = {"Vmid", "Zh", "Inv 2", "Inv 2 Vmid"};
12970+static const char *wm9713_dac_inv[] =
12971+ {"Off", "Mono", "Speaker", "Left Headphone", "Right Headphone",
12972+ "Headphone Mono", "NC", "Vmid"};
12973+static const char *wm9713_bass[] = {"Linear Control", "Adaptive Boost"};
12974+static const char *wm9713_ng_type[] = {"Constant Gain", "Mute"};
12975+static const char *wm9713_mic_select[] = {"Mic 1", "Mic 2 A", "Mic 2 B"};
12976+static const char *wm9713_micb_select[] = {"MPB", "MPA"};
12977+
12978+static const struct soc_enum wm9713_enum[] = {
12979+SOC_ENUM_SINGLE(AC97_LINE, 3, 4, wm9713_mic_mixer), /* record mic mixer 0 */
12980+SOC_ENUM_SINGLE(AC97_VIDEO, 14, 4, wm9713_rec_mux), /* record mux hp 1 */
12981+SOC_ENUM_SINGLE(AC97_VIDEO, 9, 4, wm9713_rec_mux), /* record mux mono 2 */
12982+SOC_ENUM_SINGLE(AC97_VIDEO, 3, 8, wm9713_rec_src), /* record mux left 3 */
12983+SOC_ENUM_SINGLE(AC97_VIDEO, 0, 8, wm9713_rec_src), /* record mux right 4*/
12984+SOC_ENUM_DOUBLE(AC97_CD, 14, 6, 2, wm9713_rec_gain), /* record step size 5 */
12985+SOC_ENUM_SINGLE(AC97_PCI_SVID, 14, 4, wm9713_alc_select), /* alc source select 6*/
12986+SOC_ENUM_SINGLE(AC97_REC_GAIN, 14, 4, wm9713_mono_pga), /* mono input select 7 */
12987+SOC_ENUM_SINGLE(AC97_REC_GAIN, 11, 8, wm9713_spk_pga), /* speaker left input select 8 */
12988+SOC_ENUM_SINGLE(AC97_REC_GAIN, 8, 8, wm9713_spk_pga), /* speaker right input select 9 */
12989+SOC_ENUM_SINGLE(AC97_REC_GAIN, 6, 3, wm9713_hp_pga), /* headphone left input 10 */
12990+SOC_ENUM_SINGLE(AC97_REC_GAIN, 4, 3, wm9713_hp_pga), /* headphone right input 11 */
12991+SOC_ENUM_SINGLE(AC97_REC_GAIN, 2, 4, wm9713_out3_pga), /* out 3 source 12 */
12992+SOC_ENUM_SINGLE(AC97_REC_GAIN, 0, 4, wm9713_out4_pga), /* out 4 source 13 */
12993+SOC_ENUM_SINGLE(AC97_REC_GAIN_MIC, 13, 8, wm9713_dac_inv), /* dac invert 1 14 */
12994+SOC_ENUM_SINGLE(AC97_REC_GAIN_MIC, 10, 8, wm9713_dac_inv), /* dac invert 2 15 */
12995+SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 15, 2, wm9713_bass), /* bass control 16 */
12996+SOC_ENUM_SINGLE(AC97_PCI_SVID, 5, 2, wm9713_ng_type), /* noise gate type 17 */
12997+SOC_ENUM_SINGLE(AC97_3D_CONTROL, 12, 3, wm9713_mic_select), /* mic selection 18 */
12998+SOC_ENUM_SINGLE(MICB_MUX, 0, 2, wm9713_micb_select), /* mic selection 19 */
12999+};
13000+
13001+static const struct snd_kcontrol_new wm9713_snd_ac97_controls[] = {
13002+SOC_DOUBLE("Speaker Playback Volume", AC97_MASTER, 8, 0, 31, 1),
13003+SOC_DOUBLE("Speaker Playback Switch", AC97_MASTER, 15, 7, 1, 1),
13004+SOC_DOUBLE("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1),
13005+SOC_DOUBLE("Headphone Playback Switch", AC97_HEADPHONE,15, 7, 1, 1),
13006+SOC_DOUBLE("Line In Volume", AC97_PC_BEEP, 8, 0, 31, 1),
13007+SOC_DOUBLE("PCM Playback Volume", AC97_PHONE, 8, 0, 31, 1),
13008+SOC_SINGLE("Mic 1 Volume", AC97_MIC, 8, 31, 1),
13009+SOC_SINGLE("Mic 2 Volume", AC97_MIC, 0, 31, 1),
13010+
13011+SOC_SINGLE("Mic Boost (+20dB) Switch", AC97_LINE, 5, 1, 0),
13012+SOC_SINGLE("Mic Headphone Mixer Volume", AC97_LINE, 0, 7, 1),
13013+
13014+SOC_SINGLE("Capture Switch", AC97_CD, 15, 1, 1),
13015+SOC_ENUM("Capture Volume Steps", wm9713_enum[5]),
13016+SOC_DOUBLE("Capture Volume", AC97_CD, 8, 0, 63, 0),
13017+SOC_SINGLE("Capture ZC Switch", AC97_CD, 7, 1, 0),
13018+
13019+SOC_SINGLE("Capture to Headphone Volume", AC97_VIDEO, 11, 7, 1),
13020+SOC_SINGLE("Capture to Mono Boost (+20dB) Switch", AC97_VIDEO, 8, 1, 0),
13021+SOC_SINGLE("Capture ADC Boost (+20dB) Switch", AC97_VIDEO, 6, 1, 0),
13022+
13023+SOC_SINGLE("ALC Target Volume", AC97_CODEC_CLASS_REV, 12, 15, 0),
13024+SOC_SINGLE("ALC Hold Time", AC97_CODEC_CLASS_REV, 8, 15, 0),
13025+SOC_SINGLE("ALC Decay Time ", AC97_CODEC_CLASS_REV, 4, 15, 0),
13026+SOC_SINGLE("ALC Attack Time", AC97_CODEC_CLASS_REV, 0, 15, 0),
13027+SOC_ENUM("ALC Function", wm9713_enum[6]),
13028+SOC_SINGLE("ALC Max Volume", AC97_PCI_SVID, 11, 7, 0),
13029+SOC_SINGLE("ALC ZC Timeout", AC97_PCI_SVID, 9, 3, 0),
13030+SOC_SINGLE("ALC ZC Switch", AC97_PCI_SVID, 8, 1, 0),
13031+SOC_SINGLE("ALC NG Switch", AC97_PCI_SVID, 7, 1, 0),
13032+SOC_ENUM("ALC NG Type", wm9713_enum[17]),
13033+SOC_SINGLE("ALC NG Threshold", AC97_PCI_SVID, 0, 31, 0),
13034+
13035+SOC_DOUBLE("Speaker Playback ZC Switch", AC97_MASTER, 14, 6, 1, 0),
13036+SOC_DOUBLE("Headphone Playback ZC Switch", AC97_HEADPHONE, 14, 6, 1, 0),
13037+
13038+SOC_SINGLE("Out4 Playback Switch", AC97_MASTER_MONO, 15, 1, 1),
13039+SOC_SINGLE("Out4 Playback ZC Switch", AC97_MASTER_MONO, 14, 1, 0),
13040+SOC_SINGLE("Out4 Playback Volume", AC97_MASTER_MONO, 8, 63, 1),
13041+
13042+SOC_SINGLE("Out3 Playback Switch", AC97_MASTER_MONO, 7, 1, 1),
13043+SOC_SINGLE("Out3 Playback ZC Switch", AC97_MASTER_MONO, 6, 1, 0),
13044+SOC_SINGLE("Out3 Playback Volume", AC97_MASTER_MONO, 0, 63, 1),
13045+
13046+SOC_SINGLE("Mono Capture Volume", AC97_MASTER_TONE, 8, 31, 1),
13047+SOC_SINGLE("Mono Playback Switch", AC97_MASTER_TONE, 7, 1, 1),
13048+SOC_SINGLE("Mono Playback ZC Switch", AC97_MASTER_TONE, 6, 1, 0),
13049+SOC_SINGLE("Mono Playback Volume", AC97_MASTER_TONE, 0, 31, 1),
13050+
13051+SOC_SINGLE("PC Beep Playback Headphone Volume", AC97_AUX, 12, 7, 1),
13052+SOC_SINGLE("PC Beep Playback Speaker Volume", AC97_AUX, 8, 7, 1),
13053+SOC_SINGLE("PC Beep Playback Mono Volume", AC97_AUX, 4, 7, 1),
13054+
13055+SOC_SINGLE("Voice Playback Headphone Volume", AC97_PCM, 12, 7, 1),
13056+SOC_SINGLE("Voice Playback Master Volume", AC97_PCM, 8, 7, 1),
13057+SOC_SINGLE("Voice Playback Mono Volume", AC97_PCM, 4, 7, 1),
13058+
13059+SOC_SINGLE("Aux Playback Headphone Volume", AC97_REC_SEL, 12, 7, 1),
13060+SOC_SINGLE("Aux Playback Master Volume", AC97_REC_SEL, 8, 7, 1),
13061+SOC_SINGLE("Aux Playback Mono Volume", AC97_REC_SEL, 4, 7, 1),
13062+
13063+SOC_ENUM("Bass Control", wm9713_enum[16]),
13064+SOC_SINGLE("Bass Cut-off Switch", AC97_GENERAL_PURPOSE, 12, 1, 1),
13065+SOC_SINGLE("Tone Cut-off Switch", AC97_GENERAL_PURPOSE, 4, 1, 1),
13066+SOC_SINGLE("Playback Attenuate (-6dB) Switch", AC97_GENERAL_PURPOSE, 6, 1, 0),
13067+SOC_SINGLE("Bass Volume", AC97_GENERAL_PURPOSE, 8, 15, 1),
13068+SOC_SINGLE("Tone Volume", AC97_GENERAL_PURPOSE, 0, 15, 1),
13069+
13070+SOC_SINGLE("3D Upper Cut-off Switch", AC97_REC_GAIN_MIC, 5, 1, 0),
13071+SOC_SINGLE("3D Lower Cut-off Switch", AC97_REC_GAIN_MIC, 4, 1, 0),
13072+SOC_SINGLE("3D Depth", AC97_REC_GAIN_MIC, 0, 15, 1),
13073+};
13074+
13075+/* add non dapm controls */
13076+static int wm9713_add_controls(struct snd_soc_codec *codec)
13077+{
13078+ int err, i;
13079+
13080+ for (i = 0; i < ARRAY_SIZE(wm9713_snd_ac97_controls); i++) {
13081+ err = snd_ctl_add(codec->card,
13082+ snd_soc_cnew(&wm9713_snd_ac97_controls[i],codec, NULL));
13083+ if (err < 0)
13084+ return err;
13085+ }
13086+ return 0;
13087+}
13088+
13089+/* We have to create a fake left and right HP mixers because
13090+ * the codec only has a single control that is shared by both channels.
13091+ * This makes it impossible to determine the audio path using the current
13092+ * register map, thus we add a new (virtual) register to help determine the
13093+ * audio route within the device.
13094+ */
13095+static int mixer_event (struct snd_soc_dapm_widget *w, int event)
13096+{
13097+ u16 l, r, beep, tone, phone, rec, pcm, aux;
13098+
13099+ l = ac97_read(w->codec, HPL_MIXER);
13100+ r = ac97_read(w->codec, HPR_MIXER);
13101+ beep = ac97_read(w->codec, AC97_PC_BEEP);
13102+ tone = ac97_read(w->codec, AC97_MASTER_TONE);
13103+ phone = ac97_read(w->codec, AC97_PHONE);
13104+ rec = ac97_read(w->codec, AC97_REC_SEL);
13105+ pcm = ac97_read(w->codec, AC97_PCM);
13106+ aux = ac97_read(w->codec, AC97_AUX);
13107+
13108+ if (event & SND_SOC_DAPM_PRE_REG)
13109+ return 0;
13110+ if (l & 0x1 || r & 0x1)
13111+ ac97_write(w->codec, AC97_PC_BEEP, beep & 0x7fff);
13112+ else
13113+ ac97_write(w->codec, AC97_PC_BEEP, beep | 0x8000);
13114+
13115+ if (l & 0x2 || r & 0x2)
13116+ ac97_write(w->codec, AC97_MASTER_TONE, tone & 0x7fff);
13117+ else
13118+ ac97_write(w->codec, AC97_MASTER_TONE, tone | 0x8000);
13119+
13120+ if (l & 0x4 || r & 0x4)
13121+ ac97_write(w->codec, AC97_PHONE, phone & 0x7fff);
13122+ else
13123+ ac97_write(w->codec, AC97_PHONE, phone | 0x8000);
13124+
13125+ if (l & 0x8 || r & 0x8)
13126+ ac97_write(w->codec, AC97_REC_SEL, rec & 0x7fff);
13127+ else
13128+ ac97_write(w->codec, AC97_REC_SEL, rec | 0x8000);
13129+
13130+ if (l & 0x10 || r & 0x10)
13131+ ac97_write(w->codec, AC97_PCM, pcm & 0x7fff);
13132+ else
13133+ ac97_write(w->codec, AC97_PCM, pcm | 0x8000);
13134+
13135+ if (l & 0x20 || r & 0x20)
13136+ ac97_write(w->codec, AC97_AUX, aux & 0x7fff);
13137+ else
13138+ ac97_write(w->codec, AC97_AUX, aux | 0x8000);
13139+
13140+ return 0;
13141+}
13142+
13143+/* Left Headphone Mixers */
13144+static const struct snd_kcontrol_new wm9713_hpl_mixer_controls[] = {
13145+SOC_DAPM_SINGLE("PC Beep Playback Switch", HPL_MIXER, 5, 1, 0),
13146+SOC_DAPM_SINGLE("Voice Playback Switch", HPL_MIXER, 4, 1, 0),
13147+SOC_DAPM_SINGLE("Aux Playback Switch", HPL_MIXER, 3, 1, 0),
13148+SOC_DAPM_SINGLE("PCM Playback Switch", HPL_MIXER, 2, 1, 0),
13149+SOC_DAPM_SINGLE("MonoIn Playback Switch", HPL_MIXER, 1, 1, 0),
13150+SOC_DAPM_SINGLE("Bypass Playback Switch", HPL_MIXER, 0, 1, 0),
13151+};
13152+
13153+/* Right Headphone Mixers */
13154+static const struct snd_kcontrol_new wm9713_hpr_mixer_controls[] = {
13155+SOC_DAPM_SINGLE("PC Beep Playback Switch", HPR_MIXER, 5, 1, 0),
13156+SOC_DAPM_SINGLE("Voice Playback Switch", HPR_MIXER, 4, 1, 0),
13157+SOC_DAPM_SINGLE("Aux Playback Switch", HPR_MIXER, 3, 1, 0),
13158+SOC_DAPM_SINGLE("PCM Playback Switch", HPR_MIXER, 2, 1, 0),
13159+SOC_DAPM_SINGLE("MonoIn Playback Switch", HPR_MIXER, 1, 1, 0),
13160+SOC_DAPM_SINGLE("Bypass Playback Switch", HPR_MIXER, 0, 1, 0),
13161+};
13162+
13163+/* headphone capture mux */
13164+static const struct snd_kcontrol_new wm9713_hp_rec_mux_controls =
13165+SOC_DAPM_ENUM("Route", wm9713_enum[1]);
13166+
13167+/* headphone mic mux */
13168+static const struct snd_kcontrol_new wm9713_hp_mic_mux_controls =
13169+SOC_DAPM_ENUM("Route", wm9713_enum[0]);
13170+
13171+/* Speaker Mixer */
13172+static const struct snd_kcontrol_new wm9713_speaker_mixer_controls[] = {
13173+SOC_DAPM_SINGLE("PC Beep Playback Switch", AC97_AUX, 11, 1, 1),
13174+SOC_DAPM_SINGLE("Voice Playback Switch", AC97_PCM, 11, 1, 1),
13175+SOC_DAPM_SINGLE("Aux Playback Switch", AC97_REC_SEL, 11, 1, 1),
13176+SOC_DAPM_SINGLE("PCM Playback Switch", AC97_PHONE, 14, 1, 1),
13177+SOC_DAPM_SINGLE("MonoIn Playback Switch", AC97_MASTER_TONE, 14, 1, 1),
13178+SOC_DAPM_SINGLE("Bypass Playback Switch", AC97_PC_BEEP, 14, 1, 1),
13179+};
13180+
13181+/* Mono Mixer */
13182+static const struct snd_kcontrol_new wm9713_mono_mixer_controls[] = {
13183+SOC_DAPM_SINGLE("PC Beep Playback Switch", AC97_AUX, 7, 1, 1),
13184+SOC_DAPM_SINGLE("Voice Playback Switch", AC97_PCM, 7, 1, 1),
13185+SOC_DAPM_SINGLE("Aux Playback Switch", AC97_REC_SEL, 7, 1, 1),
13186+SOC_DAPM_SINGLE("PCM Playback Switch", AC97_PHONE, 13, 1, 1),
13187+SOC_DAPM_SINGLE("MonoIn Playback Switch", AC97_MASTER_TONE, 13, 1, 1),
13188+SOC_DAPM_SINGLE("Bypass Playback Switch", AC97_PC_BEEP, 13, 1, 1),
13189+SOC_DAPM_SINGLE("Mic 1 Sidetone Switch", AC97_LINE, 7, 1, 1),
13190+SOC_DAPM_SINGLE("Mic 2 Sidetone Switch", AC97_LINE, 6, 1, 1),
13191+};
13192+
13193+/* mono mic mux */
13194+static const struct snd_kcontrol_new wm9713_mono_mic_mux_controls =
13195+SOC_DAPM_ENUM("Route", wm9713_enum[2]);
13196+
13197+/* mono output mux */
13198+static const struct snd_kcontrol_new wm9713_mono_mux_controls =
13199+SOC_DAPM_ENUM("Route", wm9713_enum[7]);
13200+
13201+/* speaker left output mux */
13202+static const struct snd_kcontrol_new wm9713_hp_spkl_mux_controls =
13203+SOC_DAPM_ENUM("Route", wm9713_enum[8]);
13204+
13205+/* speaker right output mux */
13206+static const struct snd_kcontrol_new wm9713_hp_spkr_mux_controls =
13207+SOC_DAPM_ENUM("Route", wm9713_enum[9]);
13208+
13209+/* headphone left output mux */
13210+static const struct snd_kcontrol_new wm9713_hpl_out_mux_controls =
13211+SOC_DAPM_ENUM("Route", wm9713_enum[10]);
13212+
13213+/* headphone right output mux */
13214+static const struct snd_kcontrol_new wm9713_hpr_out_mux_controls =
13215+SOC_DAPM_ENUM("Route", wm9713_enum[11]);
13216+
13217+/* Out3 mux */
13218+static const struct snd_kcontrol_new wm9713_out3_mux_controls =
13219+SOC_DAPM_ENUM("Route", wm9713_enum[12]);
13220+
13221+/* Out4 mux */
13222+static const struct snd_kcontrol_new wm9713_out4_mux_controls =
13223+SOC_DAPM_ENUM("Route", wm9713_enum[13]);
13224+
13225+/* DAC inv mux 1 */
13226+static const struct snd_kcontrol_new wm9713_dac_inv1_mux_controls =
13227+SOC_DAPM_ENUM("Route", wm9713_enum[14]);
13228+
13229+/* DAC inv mux 2 */
13230+static const struct snd_kcontrol_new wm9713_dac_inv2_mux_controls =
13231+SOC_DAPM_ENUM("Route", wm9713_enum[15]);
13232+
13233+/* Capture source left */
13234+static const struct snd_kcontrol_new wm9713_rec_srcl_mux_controls =
13235+SOC_DAPM_ENUM("Route", wm9713_enum[3]);
13236+
13237+/* Capture source right */
13238+static const struct snd_kcontrol_new wm9713_rec_srcr_mux_controls =
13239+SOC_DAPM_ENUM("Route", wm9713_enum[4]);
13240+
13241+/* mic source */
13242+static const struct snd_kcontrol_new wm9713_mic_sel_mux_controls =
13243+SOC_DAPM_ENUM("Route", wm9713_enum[18]);
13244+
13245+/* mic source B virtual control */
13246+static const struct snd_kcontrol_new wm9713_micb_sel_mux_controls =
13247+SOC_DAPM_ENUM("Route", wm9713_enum[19]);
13248+
13249+static const struct snd_soc_dapm_widget wm9713_dapm_widgets[] = {
13250+SND_SOC_DAPM_MUX("Capture Headphone Mux", SND_SOC_NOPM, 0, 0,
13251+ &wm9713_hp_rec_mux_controls),
13252+SND_SOC_DAPM_MUX("Sidetone Mux", SND_SOC_NOPM, 0, 0,
13253+ &wm9713_hp_mic_mux_controls),
13254+SND_SOC_DAPM_MUX("Capture Mono Mux", SND_SOC_NOPM, 0, 0,
13255+ &wm9713_mono_mic_mux_controls),
13256+SND_SOC_DAPM_MUX("Mono Out Mux", SND_SOC_NOPM, 0, 0,
13257+ &wm9713_mono_mux_controls),
13258+SND_SOC_DAPM_MUX("Left Speaker Out Mux", SND_SOC_NOPM, 0, 0,
13259+ &wm9713_hp_spkl_mux_controls),
13260+SND_SOC_DAPM_MUX("Right Speaker Out Mux", SND_SOC_NOPM, 0, 0,
13261+ &wm9713_hp_spkr_mux_controls),
13262+SND_SOC_DAPM_MUX("Left Headphone Out Mux", SND_SOC_NOPM, 0, 0,
13263+ &wm9713_hpl_out_mux_controls),
13264+SND_SOC_DAPM_MUX("Right Headphone Out Mux", SND_SOC_NOPM, 0, 0,
13265+ &wm9713_hpr_out_mux_controls),
13266+SND_SOC_DAPM_MUX("Out 3 Mux", SND_SOC_NOPM, 0, 0,
13267+ &wm9713_out3_mux_controls),
13268+SND_SOC_DAPM_MUX("Out 4 Mux", SND_SOC_NOPM, 0, 0,
13269+ &wm9713_out4_mux_controls),
13270+SND_SOC_DAPM_MUX("DAC Inv Mux 1", SND_SOC_NOPM, 0, 0,
13271+ &wm9713_dac_inv1_mux_controls),
13272+SND_SOC_DAPM_MUX("DAC Inv Mux 2", SND_SOC_NOPM, 0, 0,
13273+ &wm9713_dac_inv2_mux_controls),
13274+SND_SOC_DAPM_MUX("Left Capture Source", SND_SOC_NOPM, 0, 0,
13275+ &wm9713_rec_srcl_mux_controls),
13276+SND_SOC_DAPM_MUX("Right Capture Source", SND_SOC_NOPM, 0, 0,
13277+ &wm9713_rec_srcr_mux_controls),
13278+SND_SOC_DAPM_MUX("Mic A Source", SND_SOC_NOPM, 0, 0,
13279+ &wm9713_mic_sel_mux_controls ),
13280+SND_SOC_DAPM_MUX("Mic B Source", SND_SOC_NOPM, 0, 0,
13281+ &wm9713_micb_sel_mux_controls ),
13282+SND_SOC_DAPM_MIXER_E("Left HP Mixer", AC97_EXTENDED_MID, 3, 1,
13283+ &wm9713_hpl_mixer_controls[0], ARRAY_SIZE(wm9713_hpl_mixer_controls),
13284+ mixer_event, SND_SOC_DAPM_POST_REG),
13285+SND_SOC_DAPM_MIXER_E("Right HP Mixer", AC97_EXTENDED_MID, 2, 1,
13286+ &wm9713_hpr_mixer_controls[0], ARRAY_SIZE(wm9713_hpr_mixer_controls),
13287+ mixer_event, SND_SOC_DAPM_POST_REG),
13288+SND_SOC_DAPM_MIXER("Mono Mixer", AC97_EXTENDED_MID, 0, 1,
13289+ &wm9713_mono_mixer_controls[0], ARRAY_SIZE(wm9713_mono_mixer_controls)),
13290+SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_EXTENDED_MID, 1, 1,
13291+ &wm9713_speaker_mixer_controls[0],
13292+ ARRAY_SIZE(wm9713_speaker_mixer_controls)),
13293+SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback", AC97_EXTENDED_MID, 7, 1),
13294+SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback", AC97_EXTENDED_MID, 6, 1),
13295+SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
13296+SND_SOC_DAPM_MIXER("HP Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
13297+SND_SOC_DAPM_MIXER("Capture Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
13298+SND_SOC_DAPM_DAC("Voice DAC", "Voice Playback", AC97_EXTENDED_MID, 12, 1),
13299+SND_SOC_DAPM_DAC("Aux DAC", "Aux Playback", AC97_EXTENDED_MID, 11, 1),
13300+SND_SOC_DAPM_ADC("Left ADC", "Left HiFi Capture", AC97_EXTENDED_MID, 5, 1),
13301+SND_SOC_DAPM_ADC("Right ADC", "Right HiFi Capture", AC97_EXTENDED_MID, 4, 1),
13302+SND_SOC_DAPM_PGA("Left Headphone", AC97_EXTENDED_MSTATUS, 10, 1, NULL, 0),
13303+SND_SOC_DAPM_PGA("Right Headphone", AC97_EXTENDED_MSTATUS, 9, 1, NULL, 0),
13304+SND_SOC_DAPM_PGA("Left Speaker", AC97_EXTENDED_MSTATUS, 8, 1, NULL, 0),
13305+SND_SOC_DAPM_PGA("Right Speaker", AC97_EXTENDED_MSTATUS, 7, 1, NULL, 0),
13306+SND_SOC_DAPM_PGA("Out 3", AC97_EXTENDED_MSTATUS, 11, 1, NULL, 0),
13307+SND_SOC_DAPM_PGA("Out 4", AC97_EXTENDED_MSTATUS, 12, 1, NULL, 0),
13308+SND_SOC_DAPM_PGA("Mono Out", AC97_EXTENDED_MSTATUS, 13, 1, NULL, 0),
13309+SND_SOC_DAPM_PGA("Left Line In", AC97_EXTENDED_MSTATUS, 6, 1, NULL, 0),
13310+SND_SOC_DAPM_PGA("Right Line In", AC97_EXTENDED_MSTATUS, 5, 1, NULL, 0),
13311+SND_SOC_DAPM_PGA("Mono In", AC97_EXTENDED_MSTATUS, 4, 1, NULL, 0),
13312+SND_SOC_DAPM_PGA("Mic A PGA", AC97_EXTENDED_MSTATUS, 3, 1, NULL, 0),
13313+SND_SOC_DAPM_PGA("Mic B PGA", AC97_EXTENDED_MSTATUS, 2, 1, NULL, 0),
13314+SND_SOC_DAPM_PGA("Mic A Pre Amp", AC97_EXTENDED_MSTATUS, 1, 1, NULL, 0),
13315+SND_SOC_DAPM_PGA("Mic B Pre Amp", AC97_EXTENDED_MSTATUS, 0, 1, NULL, 0),
13316+SND_SOC_DAPM_MICBIAS("Mic Bias", AC97_EXTENDED_MSTATUS, 14, 1),
13317+SND_SOC_DAPM_OUTPUT("MONO"),
13318+SND_SOC_DAPM_OUTPUT("HPL"),
13319+SND_SOC_DAPM_OUTPUT("HPR"),
13320+SND_SOC_DAPM_OUTPUT("SPKL"),
13321+SND_SOC_DAPM_OUTPUT("SPKR"),
13322+SND_SOC_DAPM_OUTPUT("OUT3"),
13323+SND_SOC_DAPM_OUTPUT("OUT4"),
13324+SND_SOC_DAPM_INPUT("LINEL"),
13325+SND_SOC_DAPM_INPUT("LINER"),
13326+SND_SOC_DAPM_INPUT("MONOIN"),
13327+SND_SOC_DAPM_INPUT("PCBEEP"),
13328+SND_SOC_DAPM_INPUT("MIC1"),
13329+SND_SOC_DAPM_INPUT("MIC2A"),
13330+SND_SOC_DAPM_INPUT("MIC2B"),
13331+SND_SOC_DAPM_VMID("VMID"),
13332+};
13333+
13334+static const char *audio_map[][3] = {
13335+ /* left HP mixer */
13336+ {"Left HP Mixer", "PC Beep Playback Switch", "PCBEEP"},
13337+ {"Left HP Mixer", "Voice Playback Switch", "Voice DAC"},
13338+ {"Left HP Mixer", "Aux Playback Switch", "Aux DAC"},
13339+ {"Left HP Mixer", "Bypass Playback Switch", "Left Line In"},
13340+ {"Left HP Mixer", "PCM Playback Switch", "Left DAC"},
13341+ {"Left HP Mixer", "MonoIn Playback Switch", "Mono In"},
13342+ {"Left HP Mixer", NULL, "Capture Headphone Mux"},
13343+
13344+ /* right HP mixer */
13345+ {"Right HP Mixer", "PC Beep Playback Switch", "PCBEEP"},
13346+ {"Right HP Mixer", "Voice Playback Switch", "Voice DAC"},
13347+ {"Right HP Mixer", "Aux Playback Switch", "Aux DAC"},
13348+ {"Right HP Mixer", "Bypass Playback Switch", "Right Line In"},
13349+ {"Right HP Mixer", "PCM Playback Switch", "Right DAC"},
13350+ {"Right HP Mixer", "MonoIn Playback Switch", "Mono In"},
13351+ {"Right HP Mixer", NULL, "Capture Headphone Mux"},
13352+
13353+ /* virtual mixer - mixes left & right channels for spk and mono */
13354+ {"AC97 Mixer", NULL, "Left DAC"},
13355+ {"AC97 Mixer", NULL, "Right DAC"},
13356+ {"Line Mixer", NULL, "Right Line In"},
13357+ {"Line Mixer", NULL, "Left Line In"},
13358+ {"HP Mixer", NULL, "Left HP Mixer"},
13359+ {"HP Mixer", NULL, "Right HP Mixer"},
13360+ {"Capture Mixer", NULL, "Left Capture Source"},
13361+ {"Capture Mixer", NULL, "Right Capture Source"},
13362+
13363+ /* speaker mixer */
13364+ {"Speaker Mixer", "PC Beep Playback Switch", "PCBEEP"},
13365+ {"Speaker Mixer", "Voice Playback Switch", "Voice DAC"},
13366+ {"Speaker Mixer", "Aux Playback Switch", "Aux DAC"},
13367+ {"Speaker Mixer", "Bypass Playback Switch", "Line Mixer"},
13368+ {"Speaker Mixer", "PCM Playback Switch", "AC97 Mixer"},
13369+ {"Speaker Mixer", "MonoIn Playback Switch", "Mono In"},
13370+
13371+ /* mono mixer */
13372+ {"Mono Mixer", "PC Beep Playback Switch", "PCBEEP"},
13373+ {"Mono Mixer", "Voice Playback Switch", "Voice DAC"},
13374+ {"Mono Mixer", "Aux Playback Switch", "Aux DAC"},
13375+ {"Mono Mixer", "Bypass Playback Switch", "Line Mixer"},
13376+ {"Mono Mixer", "PCM Playback Switch", "AC97 Mixer"},
13377+ {"Mono Mixer", NULL, "Capture Mono Mux"},
13378+
13379+ /* DAC inv mux 1 */
13380+ {"DAC Inv Mux 1", "Mono", "Mono Mixer"},
13381+ {"DAC Inv Mux 1", "Speaker", "Speaker Mixer"},
13382+ {"DAC Inv Mux 1", "Left Headphone", "Left HP Mixer"},
13383+ {"DAC Inv Mux 1", "Right Headphone", "Right HP Mixer"},
13384+ {"DAC Inv Mux 1", "Headphone Mono", "HP Mixer"},
13385+
13386+ /* DAC inv mux 2 */
13387+ {"DAC Inv Mux 2", "Mono", "Mono Mixer"},
13388+ {"DAC Inv Mux 2", "Speaker", "Speaker Mixer"},
13389+ {"DAC Inv Mux 2", "Left Headphone", "Left HP Mixer"},
13390+ {"DAC Inv Mux 2", "Right Headphone", "Right HP Mixer"},
13391+ {"DAC Inv Mux 2", "Headphone Mono", "HP Mixer"},
13392+
13393+ /* headphone left mux */
13394+ {"Left Headphone Out Mux", "Headphone", "Left HP Mixer"},
13395+
13396+ /* headphone right mux */
13397+ {"Right Headphone Out Mux", "Headphone", "Right HP Mixer"},
13398+
13399+ /* speaker left mux */
13400+ {"Left Speaker Out Mux", "Headphone", "Left HP Mixer"},
13401+ {"Left Speaker Out Mux", "Speaker", "Speaker Mixer"},
13402+ {"Left Speaker Out Mux", "Inv", "DAC Inv Mux 1"},
13403+
13404+ /* speaker right mux */
13405+ {"Right Speaker Out Mux", "Headphone", "Right HP Mixer"},
13406+ {"Right Speaker Out Mux", "Speaker", "Speaker Mixer"},
13407+ {"Right Speaker Out Mux", "Inv", "DAC Inv Mux 2"},
13408+
13409+ /* mono mux */
13410+ {"Mono Out Mux", "Mono", "Mono Mixer"},
13411+ {"Mono Out Mux", "Inv", "DAC Inv Mux 1"},
13412+
13413+ /* out 3 mux */
13414+ {"Out 3 Mux", "Inv 1", "DAC Inv Mux 1"},
13415+
13416+ /* out 4 mux */
13417+ {"Out 4 Mux", "Inv 2", "DAC Inv Mux 2"},
13418+
13419+ /* output pga */
13420+ {"HPL", NULL, "Left Headphone"},
13421+ {"Left Headphone", NULL, "Left Headphone Out Mux"},
13422+ {"HPR", NULL, "Right Headphone"},
13423+ {"Right Headphone", NULL, "Right Headphone Out Mux"},
13424+ {"OUT3", NULL, "Out 3"},
13425+ {"Out 3", NULL, "Out 3 Mux"},
13426+ {"OUT4", NULL, "Out 4"},
13427+ {"Out 4", NULL, "Out 4 Mux"},
13428+ {"SPKL", NULL, "Left Speaker"},
13429+ {"Left Speaker", NULL, "Left Speaker Out Mux"},
13430+ {"SPKR", NULL, "Right Speaker"},
13431+ {"Right Speaker", NULL, "Right Speaker Out Mux"},
13432+ {"MONO", NULL, "Mono Out"},
13433+ {"Mono Out", NULL, "Mono Out Mux"},
13434+
13435+ /* input pga */
13436+ {"Left Line In", NULL, "LINEL"},
13437+ {"Right Line In", NULL, "LINER"},
13438+ {"Mono In", NULL, "MONOIN"},
13439+ {"Mic A PGA", NULL, "Mic A Pre Amp"},
13440+ {"Mic B PGA", NULL, "Mic B Pre Amp"},
13441+
13442+ /* left capture select */
13443+ {"Left Capture Source", "Mic 1", "Mic A Pre Amp"},
13444+ {"Left Capture Source", "Mic 2", "Mic B Pre Amp"},
13445+ {"Left Capture Source", "Line", "LINEL"},
13446+ {"Left Capture Source", "Mono In", "MONOIN"},
13447+ {"Left Capture Source", "Headphone", "Left HP Mixer"},
13448+ {"Left Capture Source", "Speaker", "Speaker Mixer"},
13449+ {"Left Capture Source", "Mono Out", "Mono Mixer"},
13450+
13451+ /* right capture select */
13452+ {"Right Capture Source", "Mic 1", "Mic A Pre Amp"},
13453+ {"Right Capture Source", "Mic 2", "Mic B Pre Amp"},
13454+ {"Right Capture Source", "Line", "LINER"},
13455+ {"Right Capture Source", "Mono In", "MONOIN"},
13456+ {"Right Capture Source", "Headphone", "Right HP Mixer"},
13457+ {"Right Capture Source", "Speaker", "Speaker Mixer"},
13458+ {"Right Capture Source", "Mono Out", "Mono Mixer"},
13459+
13460+ /* left ADC */
13461+ {"Left ADC", NULL, "Left Capture Source"},
13462+
13463+ /* right ADC */
13464+ {"Right ADC", NULL, "Right Capture Source"},
13465+
13466+ /* mic */
13467+ {"Mic A Pre Amp", NULL, "Mic A Source"},
13468+ {"Mic A Source", "Mic 1", "MIC1"},
13469+ {"Mic A Source", "Mic 2 A", "MIC2A"},
13470+ {"Mic A Source", "Mic 2 B", "Mic B Source"},
13471+ {"Mic B Pre Amp", "MPB", "Mic B Source"},
13472+ {"Mic B Source", NULL, "MIC2B"},
13473+
13474+ /* headphone capture */
13475+ {"Capture Headphone Mux", "Stereo", "Capture Mixer"},
13476+ {"Capture Headphone Mux", "Left", "Left Capture Source"},
13477+ {"Capture Headphone Mux", "Right", "Right Capture Source"},
13478+
13479+ /* mono capture */
13480+ {"Capture Mono Mux", "Stereo", "Capture Mixer"},
13481+ {"Capture Mono Mux", "Left", "Left Capture Source"},
13482+ {"Capture Mono Mux", "Right", "Right Capture Source"},
13483+
13484+ {NULL, NULL, NULL},
13485+};
13486+
13487+static int wm9713_add_widgets(struct snd_soc_codec *codec)
13488+{
13489+ int i;
13490+
13491+ for(i = 0; i < ARRAY_SIZE(wm9713_dapm_widgets); i++) {
13492+ snd_soc_dapm_new_control(codec, &wm9713_dapm_widgets[i]);
13493+ }
13494+
13495+ /* set up audio path audio_mapnects */
13496+ for(i = 0; audio_map[i][0] != NULL; i++) {
13497+ snd_soc_dapm_connect_input(codec, audio_map[i][0],
13498+ audio_map[i][1], audio_map[i][2]);
13499+ }
13500+
13501+ snd_soc_dapm_new_widgets(codec);
13502+ return 0;
13503+}
13504+
13505+static unsigned int ac97_read(struct snd_soc_codec *codec,
13506+ unsigned int reg)
13507+{
13508+ u16 *cache = codec->reg_cache;
13509+
13510+ if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
13511+ reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 ||
13512+ reg == AC97_CD)
13513+ return soc_ac97_ops.read(codec->ac97, reg);
13514+ else {
13515+ reg = reg >> 1;
13516+
13517+ if (reg > (ARRAY_SIZE(wm9713_reg)))
13518+ return -EIO;
13519+
13520+ return cache[reg];
13521+ }
13522+}
13523+
13524+static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
13525+ unsigned int val)
13526+{
13527+ u16 *cache = codec->reg_cache;
13528+ if (reg < 0x7c)
13529+ soc_ac97_ops.write(codec->ac97, reg, val);
13530+ reg = reg >> 1;
13531+ if (reg <= (ARRAY_SIZE(wm9713_reg)))
13532+ cache[reg] = val;
13533+
13534+ return 0;
13535+}
13536+
13537+struct pll_ {
13538+ unsigned int in_hz;
13539+ unsigned int lf:1; /* allows low frequency use */
13540+ unsigned int sdm:1; /* allows fraction n div */
13541+ unsigned int divsel:1; /* enables input clock div */
13542+ unsigned int divctl:1; /* input clock divider */
13543+ unsigned int n:4;
13544+ unsigned int k;
13545+};
13546+
13547+struct pll_ pll[] = {
13548+ {13000000, 0, 1, 0, 0, 7, 0x23f488},
13549+ {2048000, 1, 0, 0, 0, 12, 0x0},
13550+ {4096000, 1, 0, 0, 0, 6, 0x0},
13551+ {12288000, 0, 0, 0, 0, 8, 0x0},
13552+ /* liam - add more entries */
13553+};
13554+
13555+/* we must have either 24.576MHz or a PLL freq */
13556+static unsigned int wm9713_config_ac97sysclk(struct snd_soc_codec_dai *dai,
13557+ struct snd_soc_clock_info *info, unsigned int clk)
13558+{
13559+ int i;
13560+ dai->mclk = 0;
13561+
13562+ /* first check if we can get away witout burning any PLL power */
13563+ if (24576000 == clk) {
13564+ /* standard AC97 clock */
13565+ dai->mclk = clk;
13566+ goto out;
13567+ }
13568+
13569+ /* ok no standard clock, so we must now try the PLL */
13570+ for(i = 0; i < ARRAY_SIZE(pll); i++) {
13571+ if (clk == pll[i].in_hz) {
13572+ dai->mclk = clk; /* clock out */
13573+ goto out;
13574+ }
13575+ }
13576+
13577+out:
13578+ return dai->mclk;
13579+}
13580+
13581+/* The WM9713 voice DAC can only run at 256FS. This interface and DAC are
13582+ * clocked by the main AC97 clock divided down to 256 FS.
13583+ */
13584+static unsigned int wm9713_config_vsysclk(struct snd_soc_codec_dai *dai,
13585+ struct snd_soc_clock_info *info, unsigned int clk)
13586+{
13587+
13588+ int i, j, best_clk = info->fs * info->rate;
13589+
13590+ /* can we run at this clk without the PLL ? */
13591+ for (i = 1; i <= 16; i++) {
13592+ if (best_clk * i == clk) {
13593+ dai->pll_in = 0;
13594+ dai->clk_div = i << 1;
13595+ dai->mclk = best_clk;
13596+ return dai->mclk;
13597+ }
13598+ }
13599+
13600+ /* now check for PLL support */
13601+ for (i = 0; i < ARRAY_SIZE(pll); i++) {
13602+ if (pll[i].in_hz == clk) {
13603+ for (j = 1; j <= 16; j++) {
13604+ if (24576000 == j * best_clk) {
13605+ dai->pll_in = clk;
13606+ dai->pll_out = 24576000;
13607+ dai->clk_div = j << 1;
13608+ dai->mclk = best_clk;
13609+ return dai->mclk;
13610+ }
13611+ }
13612+ }
13613+ }
13614+
13615+ /* this clk is not supported */
13616+ return 0;
13617+}
13618+
13619+u32 wm9713_set_pll(struct snd_soc_codec *codec, u32 in)
13620+{
13621+ struct wm9713 *wm = (struct wm9713*)codec->private_data;
13622+ int i;
13623+ u16 reg, reg2;
13624+
13625+ /* turn PLL off ? */
13626+ if (in == 0) {
13627+ /* disable PLL power and select ext source */
13628+ reg = ac97_read(codec, AC97_HANDSET_RATE);
13629+ ac97_write(codec, AC97_HANDSET_RATE, reg | 0x0080);
13630+ reg = ac97_read(codec, AC97_EXTENDED_MID);
13631+ ac97_write(codec, AC97_EXTENDED_MID, reg | 0x0200);
13632+ wm->pll = 0;
13633+ return 0;
13634+ }
13635+
13636+ for (i = 0; i < ARRAY_SIZE(pll); i++) {
13637+ if (pll[i].in_hz == in)
13638+ goto found;
13639+ }
13640+ return -EINVAL;
13641+
13642+found:
13643+ if (pll[i].sdm == 0) {
13644+ reg = (pll[i].n << 12) | (pll[i].lf << 11) |
13645+ (pll[i].divsel << 9) | (pll[i].divctl << 8);
13646+ ac97_write(codec, AC97_LINE1_LEVEL, reg);
13647+ } else {
13648+ /* write the fractional k to the reg 0x46 pages */
13649+ reg2 = (pll[i].n << 12) | (pll[i].lf << 11) | (pll[i].sdm << 10) |
13650+ (pll[i].divsel << 9) | (pll[i].divctl << 8);
13651+
13652+ reg = reg2 | (0x5 << 4) | (pll[i].k >> 20); /* K [21:20] */
13653+ ac97_write(codec, AC97_LINE1_LEVEL, reg);
13654+
13655+ reg = reg2 | (0x4 << 4) | ((pll[i].k >> 16) & 0xf); /* K [19:16] */
13656+ ac97_write(codec, AC97_LINE1_LEVEL, reg);
13657+
13658+ reg = reg2 | (0x3 << 4) | ((pll[i].k >> 12) & 0xf); /* K [15:12] */
13659+ ac97_write(codec, AC97_LINE1_LEVEL, reg);
13660+
13661+ reg = reg2 | (0x2 << 4) | ((pll[i].k >> 8) & 0xf); /* K [11:8] */
13662+ ac97_write(codec, AC97_LINE1_LEVEL, reg);
13663+
13664+ reg = reg2 | (0x1 << 4) | ((pll[i].k >> 4) & 0xf); /* K [7:4] */
13665+ ac97_write(codec, AC97_LINE1_LEVEL, reg);
13666+
13667+ reg = reg2 | (0x0 << 4) | (pll[i].k & 0xf); /* K [3:0] */
13668+ ac97_write(codec, AC97_LINE1_LEVEL, reg);
13669+ }
13670+
13671+ /* turn PLL on and select as source */
13672+ reg = ac97_read(codec, AC97_EXTENDED_MID);
13673+ ac97_write(codec, AC97_EXTENDED_MID, reg & 0xfdff);
13674+ reg = ac97_read(codec, AC97_HANDSET_RATE);
13675+ ac97_write(codec, AC97_HANDSET_RATE, reg & 0xff7f);
13676+ /* wait 10ms AC97 link frames for the link to stabilise */
13677+ schedule_timeout_interruptible(msecs_to_jiffies(10));
13678+ wm->pll = in;
13679+ return 0;
13680+}
13681+EXPORT_SYMBOL_GPL(wm9713_set_pll);
13682+
13683+static int wm9713_voice_prepare(struct snd_pcm_substream *substream)
13684+{
13685+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
13686+ struct snd_soc_device *socdev = rtd->socdev;
13687+ struct snd_soc_codec *codec = socdev->codec;
13688+ u16 reg = 0x8000, bfs, div, gpio;
13689+
13690+ bfs = SND_SOC_FSBD_REAL(rtd->codec_dai->dai_runtime.bfs);
13691+ gpio = ac97_read(codec, AC97_GPIO_CFG) & 0xffe2;
13692+
13693+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CLOCK_MASK){
13694+ case SND_SOC_DAIFMT_CBM_CFM:
13695+ reg |= 0x4000;
13696+ gpio |= 0x0008;
13697+ break;
13698+ case SND_SOC_DAIFMT_CBM_CFS:
13699+ reg |= 0x6000;
13700+ gpio |= 0x000c;
13701+ break;
13702+ case SND_SOC_DAIFMT_CBS_CFS:
13703+ reg |= 0x0200;
13704+ gpio |= 0x000d;
13705+ break;
13706+ case SND_SOC_DAIFMT_CBS_CFM:
13707+ gpio |= 0x0009;
13708+ break;
13709+ }
13710+ ac97_write(codec, AC97_GPIO_CFG, gpio);
13711+
13712+ /* enable PLL if needed */
13713+ if (rtd->codec_dai->pll_in)
13714+ wm9713_set_pll(codec, rtd->codec_dai->pll_in);
13715+
13716+ /* set the PCM divider */
13717+ div = ac97_read(codec, AC97_HANDSET_RATE) & 0xf0ff;
13718+ ac97_write(codec, AC97_HANDSET_RATE, div |
13719+ ((rtd->codec_dai->clk_div >> 1) -1) << 8);
13720+
13721+ /* clock inversion */
13722+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_INV_MASK) {
13723+ case SND_SOC_DAIFMT_IB_IF:
13724+ reg |= 0x00c0;
13725+ break;
13726+ case SND_SOC_DAIFMT_IB_NF:
13727+ reg |= 0x0080;
13728+ break;
13729+ case SND_SOC_DAIFMT_NB_IF:
13730+ reg |= 0x0040;
13731+ break;
13732+ }
13733+
13734+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
13735+ case SND_SOC_DAIFMT_I2S:
13736+ reg |= 0x0002;
13737+ break;
13738+ case SND_SOC_DAIFMT_RIGHT_J:
13739+ break;
13740+ case SND_SOC_DAIFMT_LEFT_J:
13741+ reg |= 0x0001;
13742+ break;
13743+ case SND_SOC_DAIFMT_DSP_A:
13744+ reg |= 0x0003;
13745+ break;
13746+ case SND_SOC_DAIFMT_DSP_B:
13747+ reg |= 0x0043;
13748+ break;
13749+ }
13750+
13751+ switch (rtd->codec_dai->dai_runtime.pcmfmt) {
13752+ case SNDRV_PCM_FMTBIT_S16_LE:
13753+ break;
13754+ case SNDRV_PCM_FMTBIT_S20_3LE:
13755+ reg |= 0x0004;
13756+ break;
13757+ case SNDRV_PCM_FMTBIT_S24_LE:
13758+ reg |= 0x0008;
13759+ break;
13760+ case SNDRV_PCM_FMTBIT_S32_LE:
13761+ reg |= 0x000c;
13762+ break;
13763+ }
13764+
13765+ switch (bfs) {
13766+ case 2:
13767+ reg |= (0x1 << 9);
13768+ break;
13769+ case 4:
13770+ reg |= (0x2 << 9);
13771+ break;
13772+ case 8:
13773+ reg |= (0x3 << 9);
13774+ break;
13775+ case 16:
13776+ reg |= (0x4 << 9);
13777+ break;
13778+ }
13779+
13780+ /* enable PCM interface in master mode */
13781+ ac97_write(codec, AC97_CENTER_LFE_MASTER, reg);
13782+ return 0;
13783+}
13784+
13785+static void wm9713_shutdown(struct snd_pcm_substream *substream)
13786+{
13787+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
13788+ struct snd_soc_device *socdev = rtd->socdev;
13789+ struct snd_soc_codec *codec = socdev->codec;
13790+
13791+ if (!codec->active)
13792+ wm9713_set_pll(codec, 0);
13793+}
13794+
13795+static void wm9713_voiceshutdown(snd_pcm_substream_t *substream)
13796+{
13797+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
13798+ struct snd_soc_device *socdev = rtd->socdev;
13799+ struct snd_soc_codec *codec = socdev->codec;
13800+ u16 status;
13801+
13802+ wm9713_shutdown(substream);
13803+
13804+ /* Gracefully shut down the voice interface. */
13805+ status = ac97_read(codec, AC97_EXTENDED_STATUS) | 0x1000;
13806+ ac97_write(codec,AC97_HANDSET_RATE,0x0280);
13807+ schedule_timeout_interruptible(msecs_to_jiffies(1));
13808+ ac97_write(codec,AC97_HANDSET_RATE,0x0F80);
13809+ ac97_write(codec,AC97_EXTENDED_MID,status);
13810+}
13811+
13812+static int ac97_hifi_prepare(struct snd_pcm_substream *substream)
13813+{
13814+ struct snd_pcm_runtime *runtime = substream->runtime;
13815+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
13816+ struct snd_soc_device *socdev = rtd->socdev;
13817+ struct snd_soc_codec *codec = socdev->codec;
13818+ int reg;
13819+ u16 vra;
13820+
13821+ /* we need a 24576000Hz clock to run at the correct speed */
13822+ if (rtd->codec_dai->mclk != 24576000)
13823+ wm9713_set_pll(codec, rtd->codec_dai->mclk);
13824+
13825+ vra = ac97_read(codec, AC97_EXTENDED_STATUS);
13826+ ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1);
13827+
13828+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
13829+ reg = AC97_PCM_FRONT_DAC_RATE;
13830+ else
13831+ reg = AC97_PCM_LR_ADC_RATE;
13832+
13833+ return ac97_write(codec, reg, runtime->rate);
13834+}
13835+
13836+static int ac97_aux_prepare(struct snd_pcm_substream *substream)
13837+{
13838+ struct snd_pcm_runtime *runtime = substream->runtime;
13839+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
13840+ struct snd_soc_device *socdev = rtd->socdev;
13841+ struct snd_soc_codec *codec = socdev->codec;
13842+ u16 vra, xsle;
13843+
13844+ /* we need a 24576000Hz clock to run at the correct speed */
13845+ if (rtd->codec_dai->mclk != 24576000)
13846+ wm9713_set_pll(codec, rtd->codec_dai->mclk);
13847+
13848+ vra = ac97_read(codec, AC97_EXTENDED_STATUS);
13849+ ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1);
13850+ xsle = ac97_read(codec, AC97_PCI_SID);
13851+ ac97_write(codec, AC97_PCI_SID, xsle | 0x8000);
13852+
13853+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
13854+ return -ENODEV;
13855+
13856+ return ac97_write(codec, AC97_PCM_SURR_DAC_RATE, runtime->rate);
13857+}
13858+
13859+struct snd_soc_codec_dai wm9713_dai[] = {
13860+{
13861+ .name = "AC97 HiFi",
13862+ .playback = {
13863+ .stream_name = "HiFi Playback",
13864+ .channels_min = 1,
13865+ .channels_max = 2,},
13866+ .capture = {
13867+ .stream_name = "HiFi Capture",
13868+ .channels_min = 1,
13869+ .channels_max = 2,},
13870+ .config_sysclk = wm9713_config_ac97sysclk,
13871+ .ops = {
13872+ .shutdown = wm9713_shutdown,
13873+ .prepare = ac97_hifi_prepare,},
13874+ .caps = {
13875+ .num_modes = ARRAY_SIZE(ac97_modes),
13876+ .mode = ac97_modes,},},
13877+ {
13878+ .name = "AC97 Aux",
13879+ .playback = {
13880+ .stream_name = "Aux Playback",
13881+ .channels_min = 1,
13882+ .channels_max = 1,},
13883+ .config_sysclk = wm9713_config_ac97sysclk,
13884+ .ops = {
13885+ .shutdown = wm9713_shutdown,
13886+ .prepare = ac97_aux_prepare,},
13887+ .caps = {
13888+ .num_modes = ARRAY_SIZE(ac97_modes),
13889+ .mode = ac97_modes,}
13890+ },
13891+ {
13892+ .name = "WM9713 Voice",
13893+ .playback = {
13894+ .stream_name = "Voice Playback",
13895+ .channels_min = 1,
13896+ .channels_max = 1,},
13897+ .capture = {
13898+ .stream_name = "Voice Capture",
13899+ .channels_min = 1,
13900+ .channels_max = 2,},
13901+ .config_sysclk = wm9713_config_vsysclk,
13902+ .ops = {
13903+ .prepare = wm9713_voice_prepare,
13904+ .shutdown = wm9713_voiceshutdown,},
13905+ .caps = {
13906+ .num_modes = ARRAY_SIZE(wm9713_voice_modes),
13907+ .mode = wm9713_voice_modes,},
13908+ },
13909+};
13910+EXPORT_SYMBOL_GPL(wm9713_dai);
13911+
13912+int wm9713_reset(struct snd_soc_codec *codec, int try_warm)
13913+{
13914+ if (try_warm && soc_ac97_ops.warm_reset) {
13915+ soc_ac97_ops.warm_reset(codec->ac97);
13916+ if (!(ac97_read(codec, 0) & 0x8000))
13917+ return 1;
13918+ }
13919+
13920+ soc_ac97_ops.reset(codec->ac97);
13921+ if (ac97_read(codec, 0) & 0x8000)
13922+ return -EIO;
13923+ return 0;
13924+}
13925+EXPORT_SYMBOL_GPL(wm9713_reset);
13926+
13927+static int wm9713_dapm_event(struct snd_soc_codec *codec, int event)
13928+{
13929+ u16 reg;
13930+
13931+ switch (event) {
13932+ case SNDRV_CTL_POWER_D0: /* full On */
13933+ /* enable thermal shutdown */
13934+ reg = ac97_read(codec, AC97_EXTENDED_MID) & 0x1bff;
13935+ ac97_write(codec, AC97_EXTENDED_MID, reg);
13936+ break;
13937+ case SNDRV_CTL_POWER_D1: /* partial On */
13938+ case SNDRV_CTL_POWER_D2: /* partial On */
13939+ break;
13940+ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
13941+ /* enable master bias and vmid */
13942+ reg = ac97_read(codec, AC97_EXTENDED_MID) & 0x3bff;
13943+ ac97_write(codec, AC97_EXTENDED_MID, reg);
13944+ ac97_write(codec, AC97_POWERDOWN, 0x0000);
13945+ break;
13946+ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
13947+ /* disable everything including AC link */
13948+ ac97_write(codec, AC97_EXTENDED_MID, 0xffff);
13949+ ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff);
13950+ ac97_write(codec, AC97_POWERDOWN, 0xffff);
13951+ break;
13952+ }
13953+ codec->dapm_state = event;
13954+ return 0;
13955+}
13956+
13957+static int wm9713_soc_suspend(struct platform_device *pdev,
13958+ pm_message_t state)
13959+{
13960+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
13961+ struct snd_soc_codec *codec = socdev->codec;
13962+ struct wm9713 *wm = (struct wm9713*)codec->private_data;
13963+
13964+ if (wm->pll) {
13965+ wm->pll_resume = wm->pll;
13966+ wm9713_set_pll(codec, 0);
13967+ }
13968+ wm9713_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
13969+ return 0;
13970+}
13971+
13972+static int wm9713_soc_resume(struct platform_device *pdev)
13973+{
13974+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
13975+ struct snd_soc_codec *codec = socdev->codec;
13976+ struct wm9713 *wm = (struct wm9713*)codec->private_data;
13977+ int i, ret;
13978+ u16 *cache = codec->reg_cache;
13979+
13980+ if ((ret = wm9713_reset(codec, 1)) < 0){
13981+ printk(KERN_ERR "could not reset AC97 codec\n");
13982+ return ret;
13983+ }
13984+
13985+ wm9713_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
13986+
13987+ /* only synchronise the codec if warm reset failed */
13988+ if (ret == 0) {
13989+ for (i = 2; i < ARRAY_SIZE(wm9713_reg) << 1; i+=2) {
13990+ if (i == AC97_POWERDOWN || i == AC97_EXTENDED_MID ||
13991+ i == AC97_EXTENDED_MSTATUS || i > 0x66)
13992+ continue;
13993+ soc_ac97_ops.write(codec->ac97, i, cache[i>>1]);
13994+ }
13995+ }
13996+
13997+ if (wm->pll_resume) {
13998+ wm9713_set_pll(codec, wm->pll_resume);
13999+ wm->pll_resume = 0;
14000+ }
14001+
14002+ if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0)
14003+ wm9713_dapm_event(codec, SNDRV_CTL_POWER_D0);
14004+
14005+ return ret;
14006+}
14007+
14008+static int wm9713_soc_probe(struct platform_device *pdev)
14009+{
14010+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
14011+ struct snd_soc_codec *codec;
14012+ int ret = 0, reg;
14013+
14014+ printk(KERN_INFO "WM9713/WM9714 SoC Audio Codec %s\n", WM9713_VERSION);
14015+
14016+ socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
14017+ if (socdev->codec == NULL)
14018+ return -ENOMEM;
14019+ codec = socdev->codec;
14020+ mutex_init(&codec->mutex);
14021+
14022+ codec->reg_cache =
14023+ kzalloc(sizeof(u16) * ARRAY_SIZE(wm9713_reg), GFP_KERNEL);
14024+ if (codec->reg_cache == NULL){
14025+ kfree(socdev->codec);
14026+ socdev->codec = NULL;
14027+ return -ENOMEM;
14028+ }
14029+ memcpy(codec->reg_cache, wm9713_reg,
14030+ sizeof(u16) * ARRAY_SIZE(wm9713_reg));
14031+ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm9713_reg);
14032+ codec->reg_cache_step = 2;
14033+
14034+ codec->private_data = kzalloc(sizeof(struct wm9713), GFP_KERNEL);
14035+ if (codec->private_data == NULL) {
14036+ kfree(codec->reg_cache);
14037+ kfree(socdev->codec);
14038+ socdev->codec = NULL;
14039+ return -ENOMEM;
14040+ }
14041+
14042+ codec->name = "WM9713";
14043+ codec->owner = THIS_MODULE;
14044+ codec->dai = wm9713_dai;
14045+ codec->num_dai = ARRAY_SIZE(wm9713_dai);
14046+ codec->write = ac97_write;
14047+ codec->read = ac97_read;
14048+ codec->dapm_event = wm9713_dapm_event;
14049+ INIT_LIST_HEAD(&codec->dapm_widgets);
14050+ INIT_LIST_HEAD(&codec->dapm_paths);
14051+
14052+ ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
14053+ if (ret < 0)
14054+ goto err;
14055+
14056+ /* register pcms */
14057+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
14058+ if (ret < 0)
14059+ goto pcm_err;
14060+
14061+ /* do a cold reset for the controller and then try
14062+ * a warm reset followed by an optional cold reset for codec */
14063+ wm9713_reset(codec, 0);
14064+ ret = wm9713_reset(codec, 1);
14065+ if (ret < 0) {
14066+ printk(KERN_ERR "AC97 link error\n");
14067+ goto reset_err;
14068+ }
14069+
14070+ wm9713_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
14071+
14072+ /* unmute the adc - move to kcontrol */
14073+ reg = ac97_read(codec, AC97_CD) & 0x7fff;
14074+ ac97_write(codec, AC97_CD, reg);
14075+
14076+ wm9713_add_controls(codec);
14077+ wm9713_add_widgets(codec);
14078+ ret = snd_soc_register_card(socdev);
14079+ if (ret < 0)
14080+ goto reset_err;
14081+ return 0;
14082+
14083+reset_err:
14084+ snd_soc_free_pcms(socdev);
14085+
14086+pcm_err:
14087+ snd_soc_free_ac97_codec(codec);
14088+
14089+err:
14090+ kfree(socdev->codec->private_data);
14091+ kfree(socdev->codec->reg_cache);
14092+ kfree(socdev->codec);
14093+ socdev->codec = NULL;
14094+ return ret;
14095+}
14096+
14097+static int wm9713_soc_remove(struct platform_device *pdev)
14098+{
14099+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
14100+ struct snd_soc_codec *codec = socdev->codec;
14101+
14102+ if (codec == NULL)
14103+ return 0;
14104+
14105+ snd_soc_dapm_free(socdev);
14106+ snd_soc_free_pcms(socdev);
14107+ snd_soc_free_ac97_codec(codec);
14108+ kfree(codec->private_data);
14109+ kfree(codec->reg_cache);
14110+ kfree(codec);
14111+ return 0;
14112+}
14113+
14114+struct snd_soc_codec_device soc_codec_dev_wm9713= {
14115+ .probe = wm9713_soc_probe,
14116+ .remove = wm9713_soc_remove,
14117+ .suspend = wm9713_soc_suspend,
14118+ .resume = wm9713_soc_resume,
14119+};
14120+
14121+EXPORT_SYMBOL_GPL(soc_codec_dev_wm9713);
14122+
14123+MODULE_DESCRIPTION("ASoC WM9713/WM9714 driver");
14124+MODULE_AUTHOR("Liam Girdwood");
14125+MODULE_LICENSE("GPL");
14126Index: linux-2.6-pxa-new/sound/soc/codecs/wm9713.h
14127===================================================================
14128--- /dev/null
14129+++ linux-2.6-pxa-new/sound/soc/codecs/wm9713.h
14130@@ -0,0 +1,18 @@
14131+/*
14132+ * wm9713.h -- WM9713 Soc Audio driver
14133+ */
14134+
14135+#ifndef _WM9713_H
14136+#define _WM9713_H
14137+
14138+#define WM9713_DAI_AC97_HIFI 0
14139+#define WM9713_DAI_AC97_AUX 1
14140+#define WM9713_DAI_PCM_VOICE 2
14141+
14142+extern struct snd_soc_codec_device soc_codec_dev_wm9713;
14143+extern struct snd_soc_codec_dai wm9713_dai[3];
14144+
14145+u32 wm9713_set_pll(struct snd_soc_codec *codec, u32 in);
14146+int wm9713_reset(struct snd_soc_codec *codec, int try_warm);
14147+
14148+#endif
14149Index: linux-2.6-pxa-new/sound/soc/pxa/Kconfig
14150===================================================================
14151--- /dev/null
14152+++ linux-2.6-pxa-new/sound/soc/pxa/Kconfig
14153@@ -0,0 +1,125 @@
14154+menu "SoC Audio for the Intel PXA2xx"
14155+
14156+config SND_PXA2xx_SOC
14157+ tristate "SoC Audio for the Intel PXA2xx chip"
14158+ depends on ARCH_PXA && SND
14159+ select SND_PCM
14160+ help
14161+ Say Y or M if you want to add support for codecs attached to
14162+ the PXA2xx AC97, I2S or SSP interface. You will also need
14163+ to select the audio interfaces to support below.
14164+
14165+config SND_PXA2xx_AC97
14166+ tristate
14167+ select SND_AC97_CODEC
14168+
14169+config SND_PXA2xx_SOC_AC97
14170+ tristate
14171+ select SND_AC97_BUS
14172+ select SND_SOC_AC97_BUS
14173+
14174+config SND_PXA2xx_SOC_I2S
14175+ tristate
14176+
14177+config SND_PXA2xx_SOC_SSP
14178+ tristate
14179+ select PXA_SSP
14180+
14181+config SND_PXA2xx_SOC_MAINSTONE
14182+ tristate "SoC AC97 Audio support for Intel Mainstone"
14183+ depends on SND_PXA2xx_SOC && MACH_MAINSTONE
14184+ select SND_PXA2xx_AC97
14185+ help
14186+ Say Y if you want to add support for generic AC97 SoC audio on Mainstone.
14187+
14188+config SND_PXA2xx_SOC_MAINSTONE_WM8731
14189+ tristate "SoC I2S Audio support for Intel Mainstone - WM8731"
14190+ depends on SND_PXA2xx_SOC && MACH_MAINSTONE
14191+ select SND_PXA2xx_SOC_I2S
14192+ help
14193+ Say Y if you want to add support for SoC audio on Mainstone
14194+ with the WM8731.
14195+
14196+config SND_PXA2xx_SOC_MAINSTONE_WM8753
14197+ tristate "SoC I2S/SSP Audio support for Intel Mainstone - WM8753"
14198+ depends on SND_PXA2xx_SOC && MACH_MAINSTONE
14199+ select SND_PXA2xx_SOC_I2S
14200+ select SND_PXA2xx_SOC_SSP
14201+ help
14202+ Say Y if you want to add support for SoC audio on Mainstone
14203+ with the WM8753.
14204+
14205+config SND_PXA2xx_SOC_MAINSTONE_WM8974
14206+ tristate "SoC I2S Audio support for Intel Mainstone - WM8974"
14207+ depends on SND_PXA2xx_SOC && MACH_MAINSTONE
14208+ select SND_PXA2xx_SOC_I2S
14209+ help
14210+ Say Y if you want to add support for SoC audio on Mainstone
14211+ with the WM8974.
14212+
14213+config SND_PXA2xx_SOC_MAINSTONE_WM9713
14214+ tristate "SoC I2S/SSP Audio support for Intel Mainstone - WM9713"
14215+ depends on SND_PXA2xx_SOC && MACH_MAINSTONE
14216+ select SND_PXA2xx_SOC_AC97
14217+ select SND_PXA2xx_SOC_SSP
14218+ help
14219+ Say Y if you want to add support for SoC voice audio on Mainstone
14220+ with the WM9713.
14221+
14222+config SND_MAINSTONE_BASEBAND
14223+ tristate "Example SoC Baseband Audio support for Intel Mainstone"
14224+ depends on SND_PXA2xx_SOC && MACH_MAINSTONE
14225+ select SND_PXA2xx_SOC_AC97
14226+ help
14227+ Say Y if you want to add support for SoC baseband on Mainstone
14228+ with the WM9713 and example Baseband modem.
14229+
14230+config SND_MAINSTONE_BLUETOOTH
14231+ tristate "Example SoC Bluetooth Audio support for Intel Mainstone"
14232+ depends on SND_PXA2xx_SOC && MACH_MAINSTONE
14233+ select SND_PXA2xx_SOC_I2S
14234+ help
14235+ Say Y if you want to add support for SoC bluetooth on Mainstone
14236+ with the WM8753 and example Bluetooth codec.
14237+
14238+config SND_PXA2xx_SOC_MAINSTONE_WM9712
14239+ tristate "SoC I2S/SSP Audio support for Intel Mainstone - WM9712"
14240+ depends on SND_PXA2xx_SOC && MACH_MAINSTONE
14241+ select SND_PXA2xx_SOC_AC97
14242+ help
14243+ Say Y if you want to add support for SoC voice audio on Mainstone
14244+ with the WM9712.
14245+
14246+config SND_PXA2xx_SOC_CORGI
14247+ tristate "SoC Audio support for Sharp Zaurus SL-C7x0"
14248+ depends on SND_PXA2xx_SOC && PXA_SHARP_C7xx
14249+ select SND_PXA2xx_SOC_I2S
14250+ help
14251+ Say Y if you want to add support for SoC audio on Sharp
14252+ Zaurus SL-C7x0 models (Corgi, Shepherd, Husky).
14253+
14254+config SND_PXA2xx_SOC_SPITZ
14255+ tristate "SoC Audio support for Sharp Zaurus SL-Cxx00"
14256+ depends on SND_PXA2xx_SOC && PXA_SHARP_Cxx00
14257+ select SND_PXA2xx_SOC_I2S
14258+ help
14259+ Say Y if you want to add support for SoC audio on Sharp
14260+ Zaurus SL-Cxx00 models (Spitz, Borzoi and Akita).
14261+
14262+config SND_PXA2xx_SOC_POODLE
14263+ tristate "SoC Audio support for Poodle"
14264+ depends on SND_PXA2xx_SOC && MACH_POODLE
14265+ select SND_PXA2xx_SOC_I2S
14266+ help
14267+ Say Y if you want to add support for SoC audio on Sharp
14268+ Zaurus SL-5600 model (Poodle).
14269+
14270+config SND_PXA2xx_SOC_TOSA
14271+ tristate "SoC AC97 Audio support for Tosa"
14272+ depends on SND_PXA2xx_SOC && MACH_TOSA
14273+ select SND_PXA2xx_SOC_AC97
14274+ help
14275+ Say Y if you want to add support for SoC audio on Sharp
14276+ Zaurus SL-C6000x models (Tosa).
14277+
14278+endmenu
14279Index: linux-2.6-pxa-new/sound/soc/pxa/Makefile
14280===================================================================
14281--- /dev/null
14282+++ linux-2.6-pxa-new/sound/soc/pxa/Makefile
14283@@ -0,0 +1,36 @@
14284+# PXA Platform Support
14285+snd-soc-pxa2xx-objs := pxa2xx-pcm.o
14286+snd-soc-pxa2xx-ac97-objs := pxa2xx-ac97.o
14287+snd-soc-pxa2xx-i2s-objs := pxa2xx-i2s.o
14288+snd-soc-pxa2xx-ssp-objs := pxa2xx-ssp.o
14289+
14290+obj-$(CONFIG_SND_PXA2xx_SOC) += snd-soc-pxa2xx.o
14291+obj-$(CONFIG_SND_PXA2xx_SOC_AC97) += snd-soc-pxa2xx-ac97.o
14292+obj-$(CONFIG_SND_PXA2xx_SOC_I2S) += snd-soc-pxa2xx-i2s.o
14293+obj-$(CONFIG_SND_PXA2xx_SOC_SSP) += snd-soc-pxa2xx-ssp.o
14294+
14295+# PXA Machine Support
14296+snd-soc-corgi-objs := corgi.o
14297+snd-soc-mainstone-wm8731-objs := mainstone_wm8731.o
14298+snd-soc-mainstone-wm8753-objs := mainstone_wm8753.o
14299+snd-soc-mainstone-wm8974-objs := mainstone_wm8974.o
14300+snd-soc-mainstone-wm9713-objs := mainstone_wm9713.o
14301+snd-soc-mainstone-wm9712-objs := mainstone_wm9712.o
14302+snd-soc-mainstone-baseband-objs := mainstone_baseband.o
14303+snd-soc-mainstone-bluetooth-objs := mainstone_bluetooth.o
14304+snd-soc-poodle-objs := poodle.o
14305+snd-soc-tosa-objs := tosa.o
14306+snd-soc-spitz-objs := spitz.o
14307+
14308+obj-$(CONFIG_SND_PXA2xx_SOC_CORGI) += snd-soc-corgi.o
14309+obj-$(CONFIG_SND_PXA2xx_SOC_MAINSTONE_WM8731) += snd-soc-mainstone-wm8731.o
14310+obj-$(CONFIG_SND_PXA2xx_SOC_MAINSTONE_WM8753) += snd-soc-mainstone-wm8753.o
14311+obj-$(CONFIG_SND_PXA2xx_SOC_MAINSTONE_WM8974) += snd-soc-mainstone-wm8974.o
14312+obj-$(CONFIG_SND_PXA2xx_SOC_MAINSTONE_WM9713) += snd-soc-mainstone-wm9713.o
14313+obj-$(CONFIG_SND_PXA2xx_SOC_MAINSTONE_WM9712) += snd-soc-mainstone-wm9712.o
14314+obj-$(CONFIG_SND_MAINSTONE_BASEBAND) += snd-soc-mainstone-baseband.o
14315+obj-$(CONFIG_SND_MAINSTONE_BLUETOOTH) += snd-soc-mainstone-bluetooth.o
14316+obj-$(CONFIG_SND_PXA2xx_SOC_POODLE) += snd-soc-poodle.o
14317+obj-$(CONFIG_SND_PXA2xx_SOC_TOSA) += snd-soc-tosa.o
14318+obj-$(CONFIG_SND_PXA2xx_SOC_SPITZ) += snd-soc-spitz.o
14319+
14320Index: linux-2.6-pxa-new/sound/soc/pxa/corgi.c
14321===================================================================
14322--- /dev/null
14323+++ linux-2.6-pxa-new/sound/soc/pxa/corgi.c
14324@@ -0,0 +1,361 @@
14325+/*
14326+ * corgi.c -- SoC audio for Corgi
14327+ *
14328+ * Copyright 2005 Wolfson Microelectronics PLC.
14329+ * Copyright 2005 Openedhand Ltd.
14330+ *
14331+ * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
14332+ * Richard Purdie <richard@openedhand.com>
14333+ *
14334+ * This program is free software; you can redistribute it and/or modify it
14335+ * under the terms of the GNU General Public License as published by the
14336+ * Free Software Foundation; either version 2 of the License, or (at your
14337+ * option) any later version.
14338+ *
14339+ * Revision history
14340+ * 30th Nov 2005 Initial version.
14341+ *
14342+ */
14343+
14344+#include <linux/module.h>
14345+#include <linux/moduleparam.h>
14346+#include <linux/timer.h>
14347+#include <linux/interrupt.h>
14348+#include <linux/platform_device.h>
14349+#include <sound/driver.h>
14350+#include <sound/core.h>
14351+#include <sound/pcm.h>
14352+#include <sound/soc.h>
14353+#include <sound/soc-dapm.h>
14354+
14355+#include <asm/mach-types.h>
14356+#include <asm/hardware/scoop.h>
14357+#include <asm/arch/pxa-regs.h>
14358+#include <asm/arch/hardware.h>
14359+#include <asm/arch/corgi.h>
14360+#include <asm/arch/audio.h>
14361+
14362+#include "../codecs/wm8731.h"
14363+#include "pxa2xx-pcm.h"
14364+
14365+#define CORGI_HP 0
14366+#define CORGI_MIC 1
14367+#define CORGI_LINE 2
14368+#define CORGI_HEADSET 3
14369+#define CORGI_HP_OFF 4
14370+#define CORGI_SPK_ON 0
14371+#define CORGI_SPK_OFF 1
14372+
14373+ /* audio clock in Hz - rounded from 12.235MHz */
14374+#define CORGI_AUDIO_CLOCK 12288000
14375+
14376+static int corgi_jack_func;
14377+static int corgi_spk_func;
14378+
14379+static void corgi_ext_control(struct snd_soc_codec *codec)
14380+{
14381+ int spk = 0, mic = 0, line = 0, hp = 0, hs = 0;
14382+
14383+ /* set up jack connection */
14384+ switch (corgi_jack_func) {
14385+ case CORGI_HP:
14386+ hp = 1;
14387+ /* set = unmute headphone */
14388+ set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L);
14389+ set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R);
14390+ break;
14391+ case CORGI_MIC:
14392+ mic = 1;
14393+ /* reset = mute headphone */
14394+ reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L);
14395+ reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R);
14396+ break;
14397+ case CORGI_LINE:
14398+ line = 1;
14399+ reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L);
14400+ reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R);
14401+ break;
14402+ case CORGI_HEADSET:
14403+ hs = 1;
14404+ mic = 1;
14405+ reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L);
14406+ set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R);
14407+ break;
14408+ }
14409+
14410+ if (corgi_spk_func == CORGI_SPK_ON)
14411+ spk = 1;
14412+
14413+ /* set the enpoints to their new connetion states */
14414+ snd_soc_dapm_set_endpoint(codec, "Ext Spk", spk);
14415+ snd_soc_dapm_set_endpoint(codec, "Mic Jack", mic);
14416+ snd_soc_dapm_set_endpoint(codec, "Line Jack", line);
14417+ snd_soc_dapm_set_endpoint(codec, "Headphone Jack", hp);
14418+ snd_soc_dapm_set_endpoint(codec, "Headset Jack", hs);
14419+
14420+ /* signal a DAPM event */
14421+ snd_soc_dapm_sync_endpoints(codec);
14422+}
14423+
14424+static int corgi_startup(struct snd_pcm_substream *substream)
14425+{
14426+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
14427+ struct snd_soc_codec *codec = rtd->socdev->codec;
14428+
14429+ /* check the jack status at stream startup */
14430+ corgi_ext_control(codec);
14431+ return 0;
14432+}
14433+
14434+/* we need to unmute the HP at shutdown as the mute burns power on corgi */
14435+static int corgi_shutdown(struct snd_pcm_substream *substream)
14436+{
14437+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
14438+ struct snd_soc_codec *codec = rtd->socdev->codec;
14439+
14440+ /* set = unmute headphone */
14441+ set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L);
14442+ set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R);
14443+ return 0;
14444+}
14445+
14446+static struct snd_soc_ops corgi_ops = {
14447+ .startup = corgi_startup,
14448+ .shutdown = corgi_shutdown,
14449+};
14450+
14451+static int corgi_get_jack(struct snd_kcontrol *kcontrol,
14452+ struct snd_ctl_elem_value *ucontrol)
14453+{
14454+ ucontrol->value.integer.value[0] = corgi_jack_func;
14455+ return 0;
14456+}
14457+
14458+static int corgi_set_jack(struct snd_kcontrol *kcontrol,
14459+ struct snd_ctl_elem_value *ucontrol)
14460+{
14461+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
14462+
14463+ if (corgi_jack_func == ucontrol->value.integer.value[0])
14464+ return 0;
14465+
14466+ corgi_jack_func = ucontrol->value.integer.value[0];
14467+ corgi_ext_control(codec);
14468+ return 1;
14469+}
14470+
14471+static int corgi_get_spk(struct snd_kcontrol *kcontrol,
14472+ struct snd_ctl_elem_value *ucontrol)
14473+{
14474+ ucontrol->value.integer.value[0] = corgi_spk_func;
14475+ return 0;
14476+}
14477+
14478+static int corgi_set_spk(struct snd_kcontrol *kcontrol,
14479+ struct snd_ctl_elem_value *ucontrol)
14480+{
14481+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
14482+
14483+ if (corgi_spk_func == ucontrol->value.integer.value[0])
14484+ return 0;
14485+
14486+ corgi_spk_func = ucontrol->value.integer.value[0];
14487+ corgi_ext_control(codec);
14488+ return 1;
14489+}
14490+
14491+static int corgi_amp_event(struct snd_soc_dapm_widget *w, int event)
14492+{
14493+ if (SND_SOC_DAPM_EVENT_ON(event))
14494+ set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_APM_ON);
14495+ else
14496+ reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_APM_ON);
14497+
14498+ return 0;
14499+}
14500+
14501+static int corgi_mic_event(struct snd_soc_dapm_widget *w, int event)
14502+{
14503+ if (SND_SOC_DAPM_EVENT_ON(event))
14504+ set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MIC_BIAS);
14505+ else
14506+ reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MIC_BIAS);
14507+
14508+ return 0;
14509+}
14510+
14511+/* corgi machine dapm widgets */
14512+static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = {
14513+SND_SOC_DAPM_HP("Headphone Jack", NULL),
14514+SND_SOC_DAPM_MIC("Mic Jack", corgi_mic_event),
14515+SND_SOC_DAPM_SPK("Ext Spk", corgi_amp_event),
14516+SND_SOC_DAPM_LINE("Line Jack", NULL),
14517+SND_SOC_DAPM_HP("Headset Jack", NULL),
14518+};
14519+
14520+/* Corgi machine audio map (connections to the codec pins) */
14521+static const char *audio_map[][3] = {
14522+
14523+ /* headset Jack - in = micin, out = LHPOUT*/
14524+ {"Headset Jack", NULL, "LHPOUT"},
14525+
14526+ /* headphone connected to LHPOUT1, RHPOUT1 */
14527+ {"Headphone Jack", NULL, "LHPOUT"},
14528+ {"Headphone Jack", NULL, "RHPOUT"},
14529+
14530+ /* speaker connected to LOUT, ROUT */
14531+ {"Ext Spk", NULL, "ROUT"},
14532+ {"Ext Spk", NULL, "LOUT"},
14533+
14534+ /* mic is connected to MICIN (via right channel of headphone jack) */
14535+ {"MICIN", NULL, "Mic Jack"},
14536+
14537+ /* Same as the above but no mic bias for line signals */
14538+ {"MICIN", NULL, "Line Jack"},
14539+
14540+ {NULL, NULL, NULL},
14541+};
14542+
14543+static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset",
14544+ "Off"};
14545+static const char *spk_function[] = {"On", "Off"};
14546+static const struct soc_enum corgi_enum[] = {
14547+ SOC_ENUM_SINGLE_EXT(5, jack_function),
14548+ SOC_ENUM_SINGLE_EXT(2, spk_function),
14549+};
14550+
14551+static const struct snd_kcontrol_new wm8731_corgi_controls[] = {
14552+ SOC_ENUM_EXT("Jack Function", corgi_enum[0], corgi_get_jack,
14553+ corgi_set_jack),
14554+ SOC_ENUM_EXT("Speaker Function", corgi_enum[1], corgi_get_spk,
14555+ corgi_set_spk),
14556+};
14557+
14558+/*
14559+ * Logic for a wm8731 as connected on a Sharp SL-C7x0 Device
14560+ */
14561+static int corgi_wm8731_init(struct snd_soc_codec *codec)
14562+{
14563+ int i, err;
14564+
14565+ snd_soc_dapm_set_endpoint(codec, "LLINEIN", 0);
14566+ snd_soc_dapm_set_endpoint(codec, "RLINEIN", 0);
14567+
14568+ /* Add corgi specific controls */
14569+ for (i = 0; i < ARRAY_SIZE(wm8731_corgi_controls); i++) {
14570+ err = snd_ctl_add(codec->card,
14571+ snd_soc_cnew(&wm8731_corgi_controls[i],codec, NULL));
14572+ if (err < 0)
14573+ return err;
14574+ }
14575+
14576+ /* Add corgi specific widgets */
14577+ for(i = 0; i < ARRAY_SIZE(wm8731_dapm_widgets); i++) {
14578+ snd_soc_dapm_new_control(codec, &wm8731_dapm_widgets[i]);
14579+ }
14580+
14581+ /* Set up corgi specific audio path audio_map */
14582+ for(i = 0; audio_map[i][0] != NULL; i++) {
14583+ snd_soc_dapm_connect_input(codec, audio_map[i][0],
14584+ audio_map[i][1], audio_map[i][2]);
14585+ }
14586+
14587+ snd_soc_dapm_sync_endpoints(codec);
14588+ return 0;
14589+}
14590+
14591+static unsigned int corgi_config_sysclk(struct snd_soc_pcm_runtime *rtd,
14592+ struct snd_soc_clock_info *info)
14593+{
14594+ if (info->bclk_master & SND_SOC_DAIFMT_CBS_CFS) {
14595+ /* pxa2xx is i2s master */
14596+ switch (info->rate) {
14597+ case 44100:
14598+ case 88200:
14599+ /* configure codec digital filters for 44.1, 88.2 */
14600+ rtd->codec_dai->config_sysclk(rtd->codec_dai, info,
14601+ 11289600);
14602+ break;
14603+ default:
14604+ /* configure codec digital filters for all other rates */
14605+ rtd->codec_dai->config_sysclk(rtd->codec_dai, info,
14606+ CORGI_AUDIO_CLOCK);
14607+ break;
14608+ }
14609+ /* config pxa i2s as master */
14610+ return rtd->cpu_dai->config_sysclk(rtd->cpu_dai, info,
14611+ CORGI_AUDIO_CLOCK);
14612+ } else {
14613+ /* codec is i2s master -
14614+ * only configure codec DAI clock and filters */
14615+ return rtd->codec_dai->config_sysclk(rtd->codec_dai, info,
14616+ CORGI_AUDIO_CLOCK);
14617+ }
14618+}
14619+
14620+/* corgi digital audio interface glue - connects codec <--> CPU */
14621+static struct snd_soc_dai_link corgi_dai = {
14622+ .name = "WM8731",
14623+ .stream_name = "WM8731",
14624+ .cpu_dai = &pxa_i2s_dai,
14625+ .codec_dai = &wm8731_dai,
14626+ .init = corgi_wm8731_init,
14627+ .config_sysclk = corgi_config_sysclk,
14628+};
14629+
14630+/* corgi audio machine driver */
14631+static struct snd_soc_machine snd_soc_machine_corgi = {
14632+ .name = "Corgi",
14633+ .dai_link = &corgi_dai,
14634+ .num_links = 1,
14635+ .ops = &corgi_ops,
14636+};
14637+
14638+/* corgi audio private data */
14639+static struct wm8731_setup_data corgi_wm8731_setup = {
14640+ .i2c_address = 0x1b,
14641+};
14642+
14643+/* corgi audio subsystem */
14644+static struct snd_soc_device corgi_snd_devdata = {
14645+ .machine = &snd_soc_machine_corgi,
14646+ .platform = &pxa2xx_soc_platform,
14647+ .codec_dev = &soc_codec_dev_wm8731,
14648+ .codec_data = &corgi_wm8731_setup,
14649+};
14650+
14651+static struct platform_device *corgi_snd_device;
14652+
14653+static int __init corgi_init(void)
14654+{
14655+ int ret;
14656+
14657+ if (!(machine_is_corgi() || machine_is_shepherd() || machine_is_husky()))
14658+ return -ENODEV;
14659+
14660+ corgi_snd_device = platform_device_alloc("soc-audio", -1);
14661+ if (!corgi_snd_device)
14662+ return -ENOMEM;
14663+
14664+ platform_set_drvdata(corgi_snd_device, &corgi_snd_devdata);
14665+ corgi_snd_devdata.dev = &corgi_snd_device->dev;
14666+ ret = platform_device_add(corgi_snd_device);
14667+
14668+ if (ret)
14669+ platform_device_put(corgi_snd_device);
14670+
14671+ return ret;
14672+}
14673+
14674+static void __exit corgi_exit(void)
14675+{
14676+ platform_device_unregister(corgi_snd_device);
14677+}
14678+
14679+module_init(corgi_init);
14680+module_exit(corgi_exit);
14681+
14682+/* Module information */
14683+MODULE_AUTHOR("Richard Purdie");
14684+MODULE_DESCRIPTION("ALSA SoC Corgi");
14685+MODULE_LICENSE("GPL");
14686Index: linux-2.6-pxa-new/sound/soc/pxa/mainstone.c
14687===================================================================
14688--- /dev/null
14689+++ linux-2.6-pxa-new/sound/soc/pxa/mainstone.c
14690@@ -0,0 +1,126 @@
14691+/*
14692+ * mainstone.c -- SoC audio for Mainstone
14693+ *
14694+ * Copyright 2005 Wolfson Microelectronics PLC.
14695+ * Author: Liam Girdwood
14696+ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
14697+ *
14698+ * Mainstone audio amplifier code taken from arch/arm/mach-pxa/mainstone.c
14699+ * Copyright: MontaVista Software Inc.
14700+ *
14701+ * This program is free software; you can redistribute it and/or modify it
14702+ * under the terms of the GNU General Public License as published by the
14703+ * Free Software Foundation; either version 2 of the License, or (at your
14704+ * option) any later version.
14705+ *
14706+ * Revision history
14707+ * 30th Oct 2005 Initial version.
14708+ *
14709+ */
14710+
14711+#include <linux/module.h>
14712+#include <linux/moduleparam.h>
14713+#include <linux/device.h>
14714+#include <linux/i2c.h>
14715+#include <sound/driver.h>
14716+#include <sound/core.h>
14717+#include <sound/pcm.h>
14718+#include <sound/soc.h>
14719+#include <sound/soc-dapm.h>
14720+
14721+#include <asm/arch/pxa-regs.h>
14722+#include <asm/arch/mainstone.h>
14723+#include <asm/arch/audio.h>
14724+
14725+#include "../codecs/ac97.h"
14726+#include "pxa2xx-pcm.h"
14727+
14728+static struct snd_soc_machine mainstone;
14729+static long mst_audio_suspend_mask;
14730+
14731+static int mainstone_suspend(struct platform_device *pdev, pm_message_t state)
14732+{
14733+ mst_audio_suspend_mask = MST_MSCWR2;
14734+ MST_MSCWR2 |= MST_MSCWR2_AC97_SPKROFF;
14735+ return 0;
14736+}
14737+
14738+static int mainstone_resume(struct platform_device *pdev)
14739+{
14740+ MST_MSCWR2 &= mst_audio_suspend_mask | ~MST_MSCWR2_AC97_SPKROFF;
14741+ return 0;
14742+}
14743+
14744+static int mainstone_probe(struct platform_device *pdev)
14745+{
14746+ MST_MSCWR2 &= ~MST_MSCWR2_AC97_SPKROFF;
14747+ return 0;
14748+}
14749+
14750+static int mainstone_remove(struct platform_device *pdev)
14751+{
14752+ MST_MSCWR2 |= MST_MSCWR2_AC97_SPKROFF;
14753+ return 0;
14754+}
14755+
14756+static struct snd_soc_machine_config codecs[] = {
14757+{
14758+ .name = "AC97",
14759+ .sname = "AC97 HiFi",
14760+ .iface = &pxa_ac97_interface[0],
14761+},
14762+{
14763+ .name = "AC97 Aux",
14764+ .sname = "AC97 Aux",
14765+ .iface = &pxa_ac97_interface[1],
14766+},
14767+};
14768+
14769+static struct snd_soc_machine mainstone = {
14770+ .name = "Mainstone",
14771+ .probe = mainstone_probe,
14772+ .remove = mainstone_remove,
14773+ .suspend_pre = mainstone_suspend,
14774+ .resume_post = mainstone_resume,
14775+ .config = codecs,
14776+ .nconfigs = ARRAY_SIZE(codecs),
14777+};
14778+
14779+static struct snd_soc_device mainstone_snd_devdata = {
14780+ .machine = &mainstone,
14781+ .platform = &pxa2xx_soc_platform,
14782+ .codec_dev = &soc_codec_dev_ac97,
14783+};
14784+
14785+static struct platform_device *mainstone_snd_device;
14786+
14787+static int __init mainstone_init(void)
14788+{
14789+ int ret;
14790+
14791+ mainstone_snd_device = platform_device_alloc("soc-audio", -1);
14792+ if (!mainstone_snd_device)
14793+ return -ENOMEM;
14794+
14795+ platform_set_drvdata(mainstone_snd_device, &mainstone_snd_devdata);
14796+ mainstone_snd_devdata.dev = &mainstone_snd_device->dev;
14797+ ret = platform_device_add(mainstone_snd_device);
14798+
14799+ if (ret)
14800+ platform_device_put(mainstone_snd_device);
14801+
14802+ return ret;
14803+}
14804+
14805+static void __exit mainstone_exit(void)
14806+{
14807+ platform_device_unregister(mainstone_snd_device);
14808+}
14809+
14810+module_init(mainstone_init);
14811+module_exit(mainstone_exit);
14812+
14813+/* Module information */
14814+MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
14815+MODULE_DESCRIPTION("ALSA SoC Mainstone");
14816+MODULE_LICENSE("GPL");
14817Index: linux-2.6-pxa-new/sound/soc/pxa/mainstone_baseband.c
14818===================================================================
14819--- /dev/null
14820+++ linux-2.6-pxa-new/sound/soc/pxa/mainstone_baseband.c
14821@@ -0,0 +1,249 @@
14822+/*
14823+ * mainstone_baseband.c
14824+ * Mainstone Example Baseband modem -- ALSA Soc Audio Layer
14825+ *
14826+ * Copyright 2006 Wolfson Microelectronics PLC.
14827+ * Author: Liam Girdwood
14828+ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
14829+ *
14830+ * This program is free software; you can redistribute it and/or modify it
14831+ * under the terms of the GNU General Public License as published by the
14832+ * Free Software Foundation; either version 2 of the License, or (at your
14833+ * option) any later version.
14834+ *
14835+ * Revision history
14836+ * 15th Apr 2006 Initial version.
14837+ *
14838+ * This is example code to demonstrate connecting a baseband modem to the PCM
14839+ * DAI on the WM9713 codec on the Intel Mainstone platform. It is by no means
14840+ * complete as it requires code to control the modem.
14841+ *
14842+ * The architecture consists of the WM9713 AC97 DAI connected to the PXA27x
14843+ * AC97 controller and the WM9713 PCM DAI connected to the basebands DAI. The
14844+ * baseband is controlled via a serial port. Audio is routed between the PXA27x
14845+ * and the baseband via internal WM9713 analog paths.
14846+ *
14847+ * This driver is not the baseband modem driver. This driver only calls
14848+ * functions from the Baseband driver to set up it's PCM DAI.
14849+ *
14850+ * It's intended to use this driver as follows:-
14851+ *
14852+ * 1. open() WM9713 PCM audio device.
14853+ * 2. open() serial device (for AT commands).
14854+ * 3. configure PCM audio device (rate etc) - sets up WM9713 PCM DAI,
14855+ * this will also set up the baseband PCM DAI (via calling baseband driver).
14856+ * 4. send any further AT commands to set up baseband.
14857+ * 5. configure codec audio mixer paths.
14858+ * 6. open(), configure and read/write AC97 audio device - to Tx/Rx voice
14859+ *
14860+ * The PCM audio device is opened but IO is never performed on it as the IO is
14861+ * directly between the codec and the baseband (and not the CPU).
14862+ *
14863+ * TODO:
14864+ * o Implement callbacks
14865+ */
14866+
14867+#include <linux/init.h>
14868+#include <linux/module.h>
14869+#include <linux/platform_device.h>
14870+
14871+#include <sound/driver.h>
14872+#include <sound/core.h>
14873+#include <sound/pcm.h>
14874+#include <sound/soc.h>
14875+#include <sound/soc-dapm.h>
14876+
14877+#include <asm/hardware.h>
14878+#include <asm/arch/pxa-regs.h>
14879+#include <asm/arch/audio.h>
14880+#include <asm/arch/ssp.h>
14881+
14882+#include "../codecs/wm9713.h"
14883+#include "pxa2xx-pcm.h"
14884+
14885+static struct snd_soc_machine mainstone;
14886+
14887+#define BASEBAND_XXX_DAIFMT \
14888+ (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBS_CFS |\
14889+ SND_SOC_DAIFMT_NB_NF)
14890+
14891+#define BASEBAND_XXX_DIR \
14892+ (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
14893+
14894+/*
14895+ * PCM modes - 8k 16bit mono baseband modem is master
14896+ */
14897+static struct snd_soc_dai_mode mainstone_example_modes[] = {
14898+ /* port master clk & frame modes */
14899+ {BASEBAND_XXX_DAIFMT, SND_SOC_DAITDM_LRDW(0,0), SNDRV_PCM_FORMAT_S16_LE,
14900+ SNDRV_PCM_RATE_8000, BASEBAND_XXX_DIR, SND_SOC_DAI_BFS_RATE, 256, 64},
14901+};
14902+
14903+/* Do specific baseband PCM voice startup here */
14904+static int mainstone_baseband_startup(struct snd_pcm_substream *substream)
14905+{
14906+ return 0;
14907+}
14908+
14909+/* Do specific baseband PCM voice shutdown here */
14910+static void mainstone_baseband_shutdown (struct snd_pcm_substream *substream)
14911+{
14912+}
14913+
14914+/* Do specific baseband modem PCM voice hw params init here */
14915+static int mainstone_baseband_hw_params(struct snd_pcm_substream *substream,
14916+ struct snd_pcm_hw_params *params)
14917+{
14918+ return 0;
14919+}
14920+
14921+/* Do specific baseband modem PCM voice hw params free here */
14922+static int mainstone_baseband_hw_free(struct snd_pcm_substream *substream)
14923+{
14924+ return 0;
14925+}
14926+
14927+static struct snd_soc_cpu_dai mainstone_example_dai[] = {
14928+ { .name = "Baseband",
14929+ .id = 0,
14930+ .type = SND_SOC_DAI_PCM,
14931+ .playback = {
14932+ .channels_min = 1,
14933+ .channels_max = 1,},
14934+ .capture = {
14935+ .channels_min = 1,
14936+ .channels_max = 1,},
14937+ .ops = {
14938+ .startup = mainstone_baseband_startup,
14939+ .shutdown = mainstone_baseband_shutdown,
14940+ .hw_params = mainstone_baseband_hw_params,
14941+ .hw_free = mainstone_baseband_hw_free,
14942+ },
14943+ .caps = {
14944+ .mode = mainstone_example_modes,
14945+ .num_modes = ARRAY_SIZE(mainstone_example_modes),},
14946+ },
14947+};
14948+
14949+/* do we need to do any thing on the mainstone when the stream is
14950+ * started and stopped
14951+ */
14952+static int mainstone_startup(struct snd_pcm_substream *substream)
14953+{
14954+ return 0;
14955+}
14956+
14957+static void mainstone_shutdown(struct snd_pcm_substream *substream)
14958+{
14959+}
14960+
14961+static struct snd_soc_ops mainstone_ops = {
14962+ .startup = mainstone_startup,
14963+ .shutdown = mainstone_shutdown,
14964+};
14965+
14966+/* PM */
14967+static int mainstone_suspend(struct platform_device *pdev, pm_message_t state)
14968+{
14969+ return 0;
14970+}
14971+
14972+static int mainstone_resume(struct platform_device *pdev)
14973+{
14974+ return 0;
14975+}
14976+
14977+static int mainstone_probe(struct platform_device *pdev)
14978+{
14979+ return 0;
14980+}
14981+
14982+static int mainstone_remove(struct platform_device *pdev)
14983+{
14984+ return 0;
14985+}
14986+
14987+static int mainstone_wm9713_init(struct snd_soc_codec *codec)
14988+{
14989+ return 0;
14990+}
14991+
14992+unsigned int mainstone_config_sysclk(struct snd_soc_pcm_runtime *rtd,
14993+ struct snd_soc_clock_info *info)
14994+{
14995+ /* wm8753 has pll that generates mclk from 13MHz xtal */
14996+ return rtd->codec_dai->config_sysclk(rtd->codec_dai, info, 13000000);
14997+}
14998+
14999+/* the physical audio connections between the WM9713, Baseband and pxa2xx */
15000+static struct snd_soc_dai_link mainstone_dai[] = {
15001+{
15002+ .name = "AC97",
15003+ .stream_name = "AC97 HiFi",
15004+ .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI],
15005+ .codec_dai = &wm9713_dai[WM9713_DAI_AC97_HIFI],
15006+ .init = mainstone_wm9713_init,
15007+},
15008+{
15009+ .name = "AC97 Aux",
15010+ .stream_name = "AC97 Aux",
15011+ .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX],
15012+ .codec_dai = &wm9713_dai[WM9713_DAI_AC97_AUX],
15013+},
15014+{
15015+ .name = "Baseband",
15016+ .stream_name = "Voice",
15017+ .cpu_dai = mainstone_example_dai,
15018+ .codec_dai = &wm9713_dai[WM9713_DAI_PCM_VOICE],
15019+ .config_sysclk = mainstone_config_sysclk,
15020+},
15021+};
15022+
15023+static struct snd_soc_machine mainstone = {
15024+ .name = "Mainstone",
15025+ .probe = mainstone_probe,
15026+ .remove = mainstone_remove,
15027+ .suspend_pre = mainstone_suspend,
15028+ .resume_post = mainstone_resume,
15029+ .ops = &mainstone_ops,
15030+ .dai_link = mainstone_dai,
15031+ .num_links = ARRAY_SIZE(mainstone_dai),
15032+};
15033+
15034+static struct snd_soc_device mainstone_snd_ac97_devdata = {
15035+ .machine = &mainstone,
15036+ .platform = &pxa2xx_soc_platform,
15037+ .codec_dev = &soc_codec_dev_wm9713,
15038+};
15039+
15040+static struct platform_device *mainstone_snd_ac97_device;
15041+
15042+static int __init mainstone_init(void)
15043+{
15044+ int ret;
15045+
15046+ mainstone_snd_ac97_device = platform_device_alloc("soc-audio", -1);
15047+ if (!mainstone_snd_ac97_device)
15048+ return -ENOMEM;
15049+
15050+ platform_set_drvdata(mainstone_snd_ac97_device, &mainstone_snd_ac97_devdata);
15051+ mainstone_snd_ac97_devdata.dev = &mainstone_snd_ac97_device->dev;
15052+
15053+ if((ret = platform_device_add(mainstone_snd_ac97_device)) != 0)
15054+ platform_device_put(mainstone_snd_ac97_device);
15055+
15056+ return ret;
15057+}
15058+
15059+static void __exit mainstone_exit(void)
15060+{
15061+ platform_device_unregister(mainstone_snd_ac97_device);
15062+}
15063+
15064+module_init(mainstone_init);
15065+module_exit(mainstone_exit);
15066+
15067+/* Module information */
15068+MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
15069+MODULE_DESCRIPTION("Mainstone Example Baseband PCM Interface");
15070+MODULE_LICENSE("GPL");
15071Index: linux-2.6-pxa-new/sound/soc/pxa/mainstone_bluetooth.c
15072===================================================================
15073--- /dev/null
15074+++ linux-2.6-pxa-new/sound/soc/pxa/mainstone_bluetooth.c
15075@@ -0,0 +1,399 @@
15076+/*
15077+ * mainstone_bluetooth.c
15078+ * Mainstone Example Bluetooth -- ALSA Soc Audio Layer
15079+ *
15080+ * Copyright 2006 Wolfson Microelectronics PLC.
15081+ * Author: Liam Girdwood
15082+ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
15083+ *
15084+ * This program is free software; you can redistribute it and/or modify it
15085+ * under the terms of the GNU General Public License as published by the
15086+ * Free Software Foundation; either version 2 of the License, or (at your
15087+ * option) any later version.
15088+ *
15089+ * Revision history
15090+ * 15th May 2006 Initial version.
15091+ *
15092+ * This is example code to demonstrate connecting a bluetooth codec to the PCM
15093+ * DAI on the WM8753 codec on the Intel Mainstone platform. It is by no means
15094+ * complete as it requires code to control the BT codec.
15095+ *
15096+ * The architecture consists of the WM8753 HIFI DAI connected to the PXA27x
15097+ * I2S controller and the WM8753 PCM DAI connected to the bluetooth DAI. The
15098+ * bluetooth codec and wm8753 are controlled via I2C. Audio is routed between
15099+ * the PXA27x and the bluetooth via internal WM8753 analog paths.
15100+ *
15101+ * This example supports the following audio input/outputs.
15102+ *
15103+ * o Board mounted Mic and Speaker (spk has amplifier)
15104+ * o Headphones via jack socket
15105+ * o BT source and sink
15106+ *
15107+ * This driver is not the bluetooth codec driver. This driver only calls
15108+ * functions from the Bluetooth driver to set up it's PCM DAI.
15109+ *
15110+ * It's intended to use the driver as follows:-
15111+ *
15112+ * 1. open() WM8753 PCM audio device.
15113+ * 2. configure PCM audio device (rate etc) - sets up WM8753 PCM DAI,
15114+ * this should also set up the BT codec DAI (via calling bt driver).
15115+ * 3. configure codec audio mixer paths.
15116+ * 4. open(), configure and read/write HIFI audio device - to Tx/Rx voice
15117+ *
15118+ * The PCM audio device is opened but IO is never performed on it as the IO is
15119+ * directly between the codec and the BT codec (and not the CPU).
15120+ *
15121+ * TODO:
15122+ * o Implement callbacks
15123+ */
15124+
15125+#include <linux/init.h>
15126+#include <linux/module.h>
15127+#include <linux/platform_device.h>
15128+
15129+#include <sound/driver.h>
15130+#include <sound/core.h>
15131+#include <sound/pcm.h>
15132+#include <sound/soc.h>
15133+#include <sound/soc-dapm.h>
15134+
15135+#include <asm/hardware.h>
15136+#include <asm/arch/pxa-regs.h>
15137+#include <asm/arch/audio.h>
15138+#include <asm/arch/ssp.h>
15139+
15140+#include "../codecs/wm8753.h"
15141+#include "pxa2xx-pcm.h"
15142+
15143+static struct snd_soc_machine mainstone;
15144+
15145+#define BLUETOOTH_DAIFMT \
15146+ (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBS_CFS |\
15147+ SND_SOC_DAIFMT_NB_NF)
15148+
15149+#define BLUETOOTH_DIR \
15150+ (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
15151+
15152+/*
15153+ * PCM modes - 8k 16bit mono BT codec is master
15154+ */
15155+static struct snd_soc_dai_mode mainstone_bt_modes[] = {
15156+ /* port master clk & frame modes */
15157+ {BLUETOOTH_DAIFMT, SND_SOC_DAITDM_LRDW(0,0), SNDRV_PCM_FORMAT_S16_LE,
15158+ SNDRV_PCM_RATE_8000, BLUETOOTH_DIR, SND_SOC_DAI_BFS_RATE, 256, 64},
15159+};
15160+
15161+/* Do specific bluetooth PCM startup here */
15162+static int mainstone_bt_startup(struct snd_pcm_substream *substream)
15163+{
15164+ return 0;
15165+}
15166+
15167+/* Do specific bluetooth PCM shutdown here */
15168+static void mainstone_bt_shutdown (struct snd_pcm_substream *substream)
15169+{
15170+}
15171+
15172+/* Do pecific bluetooth PCM hw params init here */
15173+static int mainstone_bt_hw_params(struct snd_pcm_substream *substream,
15174+ struct snd_pcm_hw_params *params)
15175+{
15176+ return 0;
15177+}
15178+
15179+/* Do specific bluetooth PCM hw params free here */
15180+static int mainstone_bt_hw_free(struct snd_pcm_substream *substream)
15181+{
15182+ return 0;
15183+}
15184+
15185+static struct snd_soc_cpu_dai mainstone_bt_dai[] = {
15186+ { .name = "Bluetooth",
15187+ .id = 0,
15188+ .type = SND_SOC_DAI_PCM,
15189+ .playback = {
15190+ .channels_min = 1,
15191+ .channels_max = 1,},
15192+ .capture = {
15193+ .channels_min = 1,
15194+ .channels_max = 1,},
15195+ .ops = {
15196+ .startup = mainstone_bt_startup,
15197+ .shutdown = mainstone_bt_shutdown,
15198+ .hw_params = mainstone_bt_hw_params,
15199+ .hw_free = mainstone_bt_hw_free,
15200+ },
15201+ .caps = {
15202+ .mode = mainstone_bt_modes,
15203+ .num_modes = ARRAY_SIZE(mainstone_bt_modes),},
15204+ },
15205+};
15206+
15207+/* do we need to do any thing on the mainstone when the stream is
15208+ * started and stopped
15209+ */
15210+static int mainstone_startup(struct snd_pcm_substream *substream)
15211+{
15212+ return 0;
15213+}
15214+
15215+static void mainstone_shutdown(struct snd_pcm_substream *substream)
15216+{
15217+}
15218+
15219+static struct snd_soc_ops mainstone_ops = {
15220+ .startup = mainstone_startup,
15221+ .shutdown = mainstone_shutdown,
15222+};
15223+
15224+/* PM */
15225+static int mainstone_suspend(struct platform_device *pdev, pm_message_t state)
15226+{
15227+ return 0;
15228+}
15229+
15230+static int mainstone_resume(struct platform_device *pdev)
15231+{
15232+ return 0;
15233+}
15234+
15235+static int mainstone_probe(struct platform_device *pdev)
15236+{
15237+ return 0;
15238+}
15239+
15240+static int mainstone_remove(struct platform_device *pdev)
15241+{
15242+ return 0;
15243+}
15244+
15245+/*
15246+ * Machine audio functions.
15247+ *
15248+ * The machine now has 3 extra audio controls.
15249+ *
15250+ * Jack function: Sets function (device plugged into Jack) to nothing (Off)
15251+ * or Headphones.
15252+ *
15253+ * Mic function: Set the on board Mic to On or Off
15254+ * Spk function: Set the on board Spk to On or Off
15255+ *
15256+ * example: BT playback (of far end) and capture (of near end)
15257+ * Set Mic and Speaker to On, open BT alsa interface as above and set up
15258+ * internal audio paths.
15259+ */
15260+
15261+static int machine_jack_func = 0;
15262+static int machine_spk_func = 0;
15263+static int machine_mic_func = 0;
15264+
15265+static int machine_get_jack(struct snd_kcontrol *kcontrol,
15266+ struct snd_ctl_elem_value *ucontrol)
15267+{
15268+ ucontrol->value.integer.value[0] = machine_jack_func;
15269+ return 0;
15270+}
15271+
15272+static int machine_set_jack(struct snd_kcontrol *kcontrol,
15273+ struct snd_ctl_elem_value *ucontrol)
15274+{
15275+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
15276+ machine_jack_func = ucontrol->value.integer.value[0];
15277+ snd_soc_dapm_set_endpoint(codec, "Headphone Jack", machine_jack_func);
15278+ return 0;
15279+}
15280+
15281+static int machine_get_spk(struct snd_kcontrol *kcontrol,
15282+ struct snd_ctl_elem_value *ucontrol)
15283+{
15284+ ucontrol->value.integer.value[0] = machine_spk_func;
15285+ return 0;
15286+}
15287+
15288+static int machine_set_spk(struct snd_kcontrol *kcontrol,
15289+ struct snd_ctl_elem_value *ucontrol)
15290+{
15291+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
15292+ machine_spk_func = ucontrol->value.integer.value[0];
15293+ snd_soc_dapm_set_endpoint(codec, "Spk", machine_spk_func);
15294+ return 0;
15295+}
15296+
15297+static int machine_get_mic(struct snd_kcontrol *kcontrol,
15298+ struct snd_ctl_elem_value *ucontrol)
15299+{
15300+ ucontrol->value.integer.value[0] = machine_spk_func;
15301+ return 0;
15302+}
15303+
15304+static int machine_set_mic(struct snd_kcontrol *kcontrol,
15305+ struct snd_ctl_elem_value *ucontrol)
15306+{
15307+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
15308+ machine_spk_func = ucontrol->value.integer.value[0];
15309+ snd_soc_dapm_set_endpoint(codec, "Mic", machine_mic_func);
15310+ return 0;
15311+}
15312+
15313+/* turns on board speaker amp on/off */
15314+static int machine_amp_event(struct snd_soc_dapm_widget *w, int event)
15315+{
15316+#if 0
15317+ if (SND_SOC_DAPM_EVENT_ON(event))
15318+ /* on */
15319+ else
15320+ /* off */
15321+#endif
15322+ return 0;
15323+}
15324+
15325+/* machine dapm widgets */
15326+static const struct snd_soc_dapm_widget machine_dapm_widgets[] = {
15327+SND_SOC_DAPM_HP("Headphone Jack", NULL),
15328+SND_SOC_DAPM_SPK("Spk", machine_amp_event),
15329+SND_SOC_DAPM_MIC("Mic", NULL),
15330+};
15331+
15332+/* machine connections to the codec pins */
15333+static const char* audio_map[][3] = {
15334+
15335+ /* headphone connected to LOUT1, ROUT1 */
15336+ {"Headphone Jack", NULL, "LOUT"},
15337+ {"Headphone Jack", NULL, "ROUT"},
15338+
15339+ /* speaker connected to LOUT2, ROUT2 */
15340+ {"Spk", NULL, "ROUT2"},
15341+ {"Spk", NULL, "LOUT2"},
15342+
15343+ /* mic is connected to MIC1 (via Mic Bias) */
15344+ {"MIC1", NULL, "Mic Bias"},
15345+ {"Mic Bias", NULL, "Mic"},
15346+
15347+ {NULL, NULL, NULL},
15348+};
15349+
15350+static const char* jack_function[] = {"Off", "Headphone"};
15351+static const char* spk_function[] = {"Off", "On"};
15352+static const char* mic_function[] = {"Off", "On"};
15353+static const struct soc_enum machine_ctl_enum[] = {
15354+ SOC_ENUM_SINGLE_EXT(2, jack_function),
15355+ SOC_ENUM_SINGLE_EXT(2, spk_function),
15356+ SOC_ENUM_SINGLE_EXT(2, mic_function),
15357+};
15358+
15359+static const struct snd_kcontrol_new wm8753_machine_controls[] = {
15360+ SOC_ENUM_EXT("Jack Function", machine_ctl_enum[0], machine_get_jack, machine_set_jack),
15361+ SOC_ENUM_EXT("Speaker Function", machine_ctl_enum[1], machine_get_spk, machine_set_spk),
15362+ SOC_ENUM_EXT("Mic Function", machine_ctl_enum[2], machine_get_mic, machine_set_mic),
15363+};
15364+
15365+static int mainstone_wm8753_init(struct snd_soc_codec *codec)
15366+{
15367+ int i, err;
15368+
15369+ /* not used on this machine - e.g. will never be powered up */
15370+ snd_soc_dapm_set_endpoint(codec, "OUT3", 0);
15371+ snd_soc_dapm_set_endpoint(codec, "OUT4", 0);
15372+ snd_soc_dapm_set_endpoint(codec, "MONO2", 0);
15373+ snd_soc_dapm_set_endpoint(codec, "MONO1", 0);
15374+ snd_soc_dapm_set_endpoint(codec, "LINE1", 0);
15375+ snd_soc_dapm_set_endpoint(codec, "LINE2", 0);
15376+ snd_soc_dapm_set_endpoint(codec, "RXP", 0);
15377+ snd_soc_dapm_set_endpoint(codec, "RXN", 0);
15378+ snd_soc_dapm_set_endpoint(codec, "MIC2", 0);
15379+
15380+ /* Add machine specific controls */
15381+ for (i = 0; i < ARRAY_SIZE(wm8753_machine_controls); i++) {
15382+ if ((err = snd_ctl_add(codec->card,
15383+ snd_soc_cnew(&wm8753_machine_controls[i],codec, NULL))) < 0)
15384+ return err;
15385+ }
15386+
15387+ /* Add machine specific widgets */
15388+ for(i = 0; i < ARRAY_SIZE(machine_dapm_widgets); i++) {
15389+ snd_soc_dapm_new_control(codec, &machine_dapm_widgets[i]);
15390+ }
15391+
15392+ /* Set up machine specific audio path audio_mapnects */
15393+ for(i = 0; audio_map[i][0] != NULL; i++) {
15394+ snd_soc_dapm_connect_input(codec, audio_map[i][0], audio_map[i][1], audio_map[i][2]);
15395+ }
15396+
15397+ snd_soc_dapm_sync_endpoints(codec);
15398+ return 0;
15399+}
15400+
15401+/* this configures the clocking between the WM8753 and the BT codec */
15402+unsigned int mainstone_config_sysclk(struct snd_soc_pcm_runtime *rtd,
15403+ struct snd_soc_clock_info *info)
15404+{
15405+ /* wm8753 has pll that generates mclk from 13MHz xtal */
15406+ return rtd->codec_dai->config_sysclk(rtd->codec_dai, info, 13000000);
15407+}
15408+
15409+static struct snd_soc_dai_link mainstone_dai[] = {
15410+{ /* Hifi Playback - for similatious use with voice below */
15411+ .name = "WM8753",
15412+ .stream_name = "WM8753 HiFi",
15413+ .cpu_dai = &pxa_i2s_dai,
15414+ .codec_dai = &wm8753_dai[WM8753_DAI_HIFI],
15415+ .init = mainstone_wm8753_init,
15416+ .config_sysclk = mainstone_config_sysclk,
15417+},
15418+{ /* Voice via BT */
15419+ .name = "Bluetooth",
15420+ .stream_name = "Voice",
15421+ .cpu_dai = mainstone_bt_dai,
15422+ .codec_dai = &wm8753_dai[WM8753_DAI_VOICE],
15423+ .config_sysclk = mainstone_config_sysclk,
15424+},
15425+};
15426+
15427+static struct snd_soc_machine mainstone = {
15428+ .name = "Mainstone",
15429+ .probe = mainstone_probe,
15430+ .remove = mainstone_remove,
15431+ .suspend_pre = mainstone_suspend,
15432+ .resume_post = mainstone_resume,
15433+ .ops = &mainstone_ops,
15434+ .dai_link = mainstone_dai,
15435+ .num_links = ARRAY_SIZE(mainstone_dai),
15436+};
15437+
15438+static struct snd_soc_device mainstone_snd_wm8753_devdata = {
15439+ .machine = &mainstone,
15440+ .platform = &pxa2xx_soc_platform,
15441+ .codec_dev = &soc_codec_dev_wm8753,
15442+};
15443+
15444+static struct platform_device *mainstone_snd_wm8753_device;
15445+
15446+static int __init mainstone_init(void)
15447+{
15448+ int ret;
15449+
15450+ mainstone_snd_wm8753_device = platform_device_alloc("soc-audio", -1);
15451+ if (!mainstone_snd_wm8753_device)
15452+ return -ENOMEM;
15453+
15454+ platform_set_drvdata(mainstone_snd_wm8753_device, &mainstone_snd_wm8753_devdata);
15455+ mainstone_snd_wm8753_devdata.dev = &mainstone_snd_wm8753_device->dev;
15456+
15457+ if((ret = platform_device_add(mainstone_snd_wm8753_device)) != 0)
15458+ platform_device_put(mainstone_snd_wm8753_device);
15459+
15460+ return ret;
15461+}
15462+
15463+static void __exit mainstone_exit(void)
15464+{
15465+ platform_device_unregister(mainstone_snd_wm8753_device);
15466+}
15467+
15468+module_init(mainstone_init);
15469+module_exit(mainstone_exit);
15470+
15471+/* Module information */
15472+MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
15473+MODULE_DESCRIPTION("Mainstone Example Bluetooth PCM Interface");
15474+MODULE_LICENSE("GPL");
15475Index: linux-2.6-pxa-new/sound/soc/pxa/mainstone_wm8731.c
15476===================================================================
15477--- /dev/null
15478+++ linux-2.6-pxa-new/sound/soc/pxa/mainstone_wm8731.c
15479@@ -0,0 +1,156 @@
15480+/*
15481+ * mainstone.c -- SoC audio for Mainstone
15482+ *
15483+ * Copyright 2005 Wolfson Microelectronics PLC.
15484+ * Author: Liam Girdwood
15485+ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
15486+ *
15487+ * Mainstone audio amplifier code taken from arch/arm/mach-pxa/mainstone.c
15488+ * Copyright: MontaVista Software Inc.
15489+ *
15490+ * This program is free software; you can redistribute it and/or modify it
15491+ * under the terms of the GNU General Public License as published by the
15492+ * Free Software Foundation; either version 2 of the License, or (at your
15493+ * option) any later version.
15494+ *
15495+ * Revision history
15496+ * 5th June 2006 Initial version.
15497+ *
15498+ */
15499+
15500+#include <linux/module.h>
15501+#include <linux/moduleparam.h>
15502+#include <linux/device.h>
15503+#include <linux/i2c.h>
15504+#include <sound/driver.h>
15505+#include <sound/core.h>
15506+#include <sound/pcm.h>
15507+#include <sound/soc.h>
15508+#include <sound/soc-dapm.h>
15509+
15510+#include <asm/arch/pxa-regs.h>
15511+#include <asm/arch/mainstone.h>
15512+#include <asm/arch/audio.h>
15513+
15514+#include "../codecs/wm8731.h"
15515+#include "pxa2xx-pcm.h"
15516+
15517+static struct snd_soc_machine mainstone;
15518+
15519+
15520+static const struct snd_soc_dapm_widget dapm_widgets[] = {
15521+ SND_SOC_DAPM_MIC("Int Mic", NULL),
15522+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
15523+};
15524+
15525+static const char* intercon[][3] = {
15526+
15527+ /* speaker connected to LHPOUT */
15528+ {"Ext Spk", NULL, "LHPOUT"},
15529+
15530+ /* mic is connected to Mic Jack, with WM8731 Mic Bias */
15531+ {"MICIN", NULL, "Mic Bias"},
15532+ {"Mic Bias", NULL, "Int Mic"},
15533+
15534+ /* terminator */
15535+ {NULL, NULL, NULL},
15536+};
15537+
15538+/*
15539+ * Logic for a wm8731 as connected on a Endrelia ETI-B1 board.
15540+ */
15541+static int mainstone_wm8731_init(struct snd_soc_codec *codec)
15542+{
15543+ int i;
15544+
15545+
15546+ /* Add specific widgets */
15547+ for(i = 0; i < ARRAY_SIZE(dapm_widgets); i++) {
15548+ snd_soc_dapm_new_control(codec, &dapm_widgets[i]);
15549+ }
15550+
15551+ /* Set up specific audio path interconnects */
15552+ for(i = 0; intercon[i][0] != NULL; i++) {
15553+ snd_soc_dapm_connect_input(codec, intercon[i][0], intercon[i][1], intercon[i][2]);
15554+ }
15555+
15556+ /* not connected */
15557+ snd_soc_dapm_set_endpoint(codec, "RLINEIN", 0);
15558+ snd_soc_dapm_set_endpoint(codec, "LLINEIN", 0);
15559+
15560+ /* always connected */
15561+ snd_soc_dapm_set_endpoint(codec, "Int Mic", 1);
15562+ snd_soc_dapm_set_endpoint(codec, "Ext Spk", 1);
15563+
15564+ snd_soc_dapm_sync_endpoints(codec);
15565+
15566+ return 0;
15567+}
15568+
15569+unsigned int mainstone_config_sysclk(struct snd_soc_pcm_runtime *rtd,
15570+ struct snd_soc_clock_info *info)
15571+{
15572+ /* we have a 12.288MHz crystal */
15573+ return rtd->codec_dai->config_sysclk(rtd->codec_dai, info, 12288000);
15574+}
15575+
15576+static struct snd_soc_dai_link mainstone_dai[] = {
15577+{
15578+ .name = "WM8731",
15579+ .stream_name = "WM8731 HiFi",
15580+ .cpu_dai = &pxa_i2s_dai,
15581+ .codec_dai = &wm8731_dai,
15582+ .init = mainstone_wm8731_init,
15583+ .config_sysclk = mainstone_config_sysclk,
15584+},
15585+};
15586+
15587+static struct snd_soc_machine mainstone = {
15588+ .name = "Mainstone",
15589+ .dai_link = mainstone_dai,
15590+ .num_links = ARRAY_SIZE(mainstone_dai),
15591+};
15592+
15593+static struct wm8731_setup_data corgi_wm8731_setup = {
15594+ .i2c_address = 0x1b,
15595+};
15596+
15597+static struct snd_soc_device mainstone_snd_devdata = {
15598+ .machine = &mainstone,
15599+ .platform = &pxa2xx_soc_platform,
15600+ .codec_dev = &soc_codec_dev_wm8731,
15601+ .codec_data = &corgi_wm8731_setup,
15602+};
15603+
15604+static struct platform_device *mainstone_snd_device;
15605+
15606+static int __init mainstone_init(void)
15607+{
15608+ int ret;
15609+
15610+ mainstone_snd_device = platform_device_alloc("soc-audio", -1);
15611+ if (!mainstone_snd_device)
15612+ return -ENOMEM;
15613+
15614+ platform_set_drvdata(mainstone_snd_device, &mainstone_snd_devdata);
15615+ mainstone_snd_devdata.dev = &mainstone_snd_device->dev;
15616+ ret = platform_device_add(mainstone_snd_device);
15617+
15618+ if (ret)
15619+ platform_device_put(mainstone_snd_device);
15620+
15621+ return ret;
15622+}
15623+
15624+static void __exit mainstone_exit(void)
15625+{
15626+ platform_device_unregister(mainstone_snd_device);
15627+}
15628+
15629+module_init(mainstone_init);
15630+module_exit(mainstone_exit);
15631+
15632+/* Module information */
15633+MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
15634+MODULE_DESCRIPTION("ALSA SoC WM8731 Mainstone");
15635+MODULE_LICENSE("GPL");
15636Index: linux-2.6-pxa-new/sound/soc/pxa/mainstone_wm8753.c
15637===================================================================
15638--- /dev/null
15639+++ linux-2.6-pxa-new/sound/soc/pxa/mainstone_wm8753.c
15640@@ -0,0 +1,226 @@
15641+/*
15642+ * mainstone.c -- SoC audio for Mainstone
15643+ *
15644+ * Copyright 2005 Wolfson Microelectronics PLC.
15645+ * Author: Liam Girdwood
15646+ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
15647+ *
15648+ * Mainstone audio amplifier code taken from arch/arm/mach-pxa/mainstone.c
15649+ * Copyright: MontaVista Software Inc.
15650+ *
15651+ * This program is free software; you can redistribute it and/or modify it
15652+ * under the terms of the GNU General Public License as published by the
15653+ * Free Software Foundation; either version 2 of the License, or (at your
15654+ * option) any later version.
15655+ *
15656+ * Revision history
15657+ * 30th Oct 2005 Initial version.
15658+ *
15659+ */
15660+
15661+#include <linux/module.h>
15662+#include <linux/moduleparam.h>
15663+#include <linux/device.h>
15664+#include <linux/i2c.h>
15665+#include <sound/driver.h>
15666+#include <sound/core.h>
15667+#include <sound/pcm.h>
15668+#include <sound/soc.h>
15669+#include <sound/soc-dapm.h>
15670+
15671+#include <asm/arch/pxa-regs.h>
15672+#include <asm/arch/mainstone.h>
15673+#include <asm/arch/audio.h>
15674+
15675+#include "../codecs/wm8753.h"
15676+#include "pxa2xx-pcm.h"
15677+
15678+static struct snd_soc_machine mainstone;
15679+
15680+static int mainstone_startup(struct snd_pcm_substream *substream)
15681+{
15682+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
15683+
15684+ if(rtd->cpu_dai->type == SND_SOC_DAI_PCM && rtd->cpu_dai->id == 1) {
15685+ /* enable USB on the go MUX so we can use SSPFRM2 */
15686+ MST_MSCWR2 |= MST_MSCWR2_USB_OTG_SEL;
15687+ MST_MSCWR2 &= ~MST_MSCWR2_USB_OTG_RST;
15688+ }
15689+ return 0;
15690+}
15691+
15692+static void mainstone_shutdown(struct snd_pcm_substream *substream)
15693+{
15694+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
15695+
15696+ if(rtd->cpu_dai->type == SND_SOC_DAI_PCM && rtd->cpu_dai->id == 1) {
15697+ /* disable USB on the go MUX so we can use ttyS0 */
15698+ MST_MSCWR2 &= ~MST_MSCWR2_USB_OTG_SEL;
15699+ MST_MSCWR2 |= MST_MSCWR2_USB_OTG_RST;
15700+ }
15701+}
15702+
15703+static struct snd_soc_ops mainstone_ops = {
15704+ .startup = mainstone_startup,
15705+ .shutdown = mainstone_shutdown,
15706+};
15707+
15708+static long mst_audio_suspend_mask;
15709+
15710+static int mainstone_suspend(struct platform_device *pdev, pm_message_t state)
15711+{
15712+ mst_audio_suspend_mask = MST_MSCWR2;
15713+ MST_MSCWR2 |= MST_MSCWR2_AC97_SPKROFF;
15714+ return 0;
15715+}
15716+
15717+static int mainstone_resume(struct platform_device *pdev)
15718+{
15719+ MST_MSCWR2 &= mst_audio_suspend_mask | ~MST_MSCWR2_AC97_SPKROFF;
15720+ return 0;
15721+}
15722+
15723+static int mainstone_probe(struct platform_device *pdev)
15724+{
15725+ MST_MSCWR2 &= ~MST_MSCWR2_AC97_SPKROFF;
15726+ return 0;
15727+}
15728+
15729+static int mainstone_remove(struct platform_device *pdev)
15730+{
15731+ MST_MSCWR2 |= MST_MSCWR2_AC97_SPKROFF;
15732+ return 0;
15733+}
15734+
15735+/* example machine audio_mapnections */
15736+static const char* audio_map[][3] = {
15737+
15738+ /* mic is connected to mic1 - with bias */
15739+ {"MIC1", NULL, "Mic Bias"},
15740+ {"MIC1N", NULL, "Mic Bias"},
15741+ {"Mic Bias", NULL, "Mic1 Jack"},
15742+ {"Mic Bias", NULL, "Mic1 Jack"},
15743+
15744+ {"ACIN", NULL, "ACOP"},
15745+ {NULL, NULL, NULL},
15746+};
15747+
15748+/* headphone detect support on my board */
15749+static const char * hp_pol[] = {"Headphone", "Speaker"};
15750+static const struct soc_enum wm8753_enum =
15751+ SOC_ENUM_SINGLE(WM8753_OUTCTL, 1, 2, hp_pol);
15752+
15753+static const struct snd_kcontrol_new wm8753_mainstone_controls[] = {
15754+ SOC_SINGLE("Headphone Detect Switch", WM8753_OUTCTL, 6, 1, 0),
15755+ SOC_ENUM("Headphone Detect Polarity", wm8753_enum),
15756+};
15757+
15758+/*
15759+ * This is an example machine initialisation for a wm8753 connected to a
15760+ * Mainstone II. It is missing logic to detect hp/mic insertions and logic
15761+ * to re-route the audio in such an event.
15762+ */
15763+static int mainstone_wm8753_init(struct snd_soc_codec *codec)
15764+{
15765+ int i, err;
15766+
15767+ /* set up mainstone codec pins */
15768+ snd_soc_dapm_set_endpoint(codec, "RXP", 0);
15769+ snd_soc_dapm_set_endpoint(codec, "RXN", 0);
15770+ snd_soc_dapm_set_endpoint(codec, "MIC2", 0);
15771+
15772+ /* add mainstone specific controls */
15773+ for (i = 0; i < ARRAY_SIZE(wm8753_mainstone_controls); i++) {
15774+ if ((err = snd_ctl_add(codec->card,
15775+ snd_soc_cnew(&wm8753_mainstone_controls[i],codec, NULL))) < 0)
15776+ return err;
15777+ }
15778+
15779+ /* set up mainstone specific audio path audio_mapnects */
15780+ for(i = 0; audio_map[i][0] != NULL; i++) {
15781+ snd_soc_dapm_connect_input(codec, audio_map[i][0], audio_map[i][1], audio_map[i][2]);
15782+ }
15783+
15784+ snd_soc_dapm_sync_endpoints(codec);
15785+ return 0;
15786+}
15787+
15788+unsigned int mainstone_config_sysclk(struct snd_soc_pcm_runtime *rtd,
15789+ struct snd_soc_clock_info *info)
15790+{
15791+ /* wm8753 has pll that generates mclk from 13MHz xtal */
15792+ return rtd->codec_dai->config_sysclk(rtd->codec_dai, info, 13000000);
15793+}
15794+
15795+static struct snd_soc_dai_link mainstone_dai[] = {
15796+{ /* Hifi Playback - for similatious use with voice below */
15797+ .name = "WM8753",
15798+ .stream_name = "WM8753 HiFi",
15799+ .cpu_dai = &pxa_i2s_dai,
15800+ .codec_dai = &wm8753_dai[WM8753_DAI_HIFI],
15801+ .init = mainstone_wm8753_init,
15802+ .config_sysclk = mainstone_config_sysclk,
15803+},
15804+{ /* Voice via BT */
15805+ .name = "Bluetooth",
15806+ .stream_name = "Voice",
15807+ .cpu_dai = &pxa_ssp_dai[1],
15808+ .codec_dai = &wm8753_dai[WM8753_DAI_VOICE],
15809+ .config_sysclk = mainstone_config_sysclk,
15810+},
15811+};
15812+
15813+static struct snd_soc_machine mainstone = {
15814+ .name = "Mainstone",
15815+ .probe = mainstone_probe,
15816+ .remove = mainstone_remove,
15817+ .suspend_pre = mainstone_suspend,
15818+ .resume_post = mainstone_resume,
15819+ .ops = &mainstone_ops,
15820+ .dai_link = mainstone_dai,
15821+ .num_links = ARRAY_SIZE(mainstone_dai),
15822+};
15823+
15824+static struct wm8753_setup_data mainstone_wm8753_setup = {
15825+ .i2c_address = 0x1a,
15826+};
15827+
15828+static struct snd_soc_device mainstone_snd_devdata = {
15829+ .machine = &mainstone,
15830+ .platform = &pxa2xx_soc_platform,
15831+ .codec_dev = &soc_codec_dev_wm8753,
15832+ .codec_data = &mainstone_wm8753_setup,
15833+};
15834+
15835+static struct platform_device *mainstone_snd_device;
15836+
15837+static int __init mainstone_init(void)
15838+{
15839+ int ret;
15840+
15841+ mainstone_snd_device = platform_device_alloc("soc-audio", -1);
15842+ if (!mainstone_snd_device)
15843+ return -ENOMEM;
15844+
15845+ platform_set_drvdata(mainstone_snd_device, &mainstone_snd_devdata);
15846+ mainstone_snd_devdata.dev = &mainstone_snd_device->dev;
15847+ ret = platform_device_add(mainstone_snd_device);
15848+
15849+ if (ret)
15850+ platform_device_put(mainstone_snd_device);
15851+
15852+ return ret;
15853+}
15854+
15855+static void __exit mainstone_exit(void)
15856+{
15857+ platform_device_unregister(mainstone_snd_device);
15858+}
15859+
15860+module_init(mainstone_init);
15861+module_exit(mainstone_exit);
15862+
15863+/* Module information */
15864+MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
15865+MODULE_DESCRIPTION("ALSA SoC WM8753 Mainstone");
15866+MODULE_LICENSE("GPL");
15867Index: linux-2.6-pxa-new/sound/soc/pxa/mainstone_wm8974.c
15868===================================================================
15869--- /dev/null
15870+++ linux-2.6-pxa-new/sound/soc/pxa/mainstone_wm8974.c
15871@@ -0,0 +1,112 @@
15872+/*
15873+ * mainstone.c -- SoC audio for Mainstone
15874+ *
15875+ * Copyright 2005 Wolfson Microelectronics PLC.
15876+ * Author: Liam Girdwood
15877+ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
15878+ *
15879+ * Mainstone audio amplifier code taken from arch/arm/mach-pxa/mainstone.c
15880+ * Copyright: MontaVista Software Inc.
15881+ *
15882+ * This program is free software; you can redistribute it and/or modify it
15883+ * under the terms of the GNU General Public License as published by the
15884+ * Free Software Foundation; either version 2 of the License, or (at your
15885+ * option) any later version.
15886+ *
15887+ * Revision history
15888+ * 30th Oct 2005 Initial version.
15889+ *
15890+ */
15891+
15892+#include <linux/module.h>
15893+#include <linux/moduleparam.h>
15894+#include <linux/device.h>
15895+#include <linux/i2c.h>
15896+#include <sound/driver.h>
15897+#include <sound/core.h>
15898+#include <sound/pcm.h>
15899+#include <sound/soc.h>
15900+#include <sound/soc-dapm.h>
15901+
15902+#include <asm/arch/pxa-regs.h>
15903+#include <asm/arch/mainstone.h>
15904+#include <asm/arch/audio.h>
15905+
15906+#include "../codecs/wm8974.h"
15907+#include "pxa2xx-pcm.h"
15908+
15909+static struct snd_soc_machine mainstone;
15910+
15911+static int mainstone_wm8974_init(struct snd_soc_codec *codec)
15912+{
15913+ return 0;
15914+}
15915+
15916+unsigned int mainstone_config_sysclk(struct snd_soc_pcm_runtime *rtd,
15917+ struct snd_soc_clock_info *info)
15918+{
15919+ /* we have a PLL */
15920+ return rtd->codec_dai->config_sysclk(rtd->codec_dai, info, 12288000);
15921+
15922+}
15923+
15924+static struct snd_soc_dai_link mainstone_dai[] = {
15925+{
15926+ .name = "WM8974",
15927+ .stream_name = "WM8974 HiFi",
15928+ .cpu_dai = &pxa_i2s_dai,
15929+ .codec_dai = &wm8974_dai,
15930+ .init = mainstone_wm8974_init,
15931+ .config_sysclk = mainstone_config_sysclk,
15932+},
15933+};
15934+
15935+static struct snd_soc_machine mainstone = {
15936+ .name = "Mainstone",
15937+ .dai_link = mainstone_dai,
15938+ .num_links = ARRAY_SIZE(mainstone_dai),
15939+};
15940+
15941+static struct wm8974_setup_data mainstone_wm8974_setup = {
15942+ .i2c_address = 0x1a,
15943+};
15944+
15945+static struct snd_soc_device mainstone_snd_devdata = {
15946+ .machine = &mainstone,
15947+ .platform = &pxa2xx_soc_platform,
15948+ .codec_dev = &soc_codec_dev_wm8974,
15949+ .codec_data = &mainstone_wm8974_setup,
15950+};
15951+
15952+static struct platform_device *mainstone_snd_device;
15953+
15954+static int __init mainstone_init(void)
15955+{
15956+ int ret;
15957+
15958+ mainstone_snd_device = platform_device_alloc("soc-audio", -1);
15959+ if (!mainstone_snd_device)
15960+ return -ENOMEM;
15961+
15962+ platform_set_drvdata(mainstone_snd_device, &mainstone_snd_devdata);
15963+ mainstone_snd_devdata.dev = &mainstone_snd_device->dev;
15964+ ret = platform_device_add(mainstone_snd_device);
15965+
15966+ if (ret)
15967+ platform_device_put(mainstone_snd_device);
15968+
15969+ return ret;
15970+}
15971+
15972+static void __exit mainstone_exit(void)
15973+{
15974+ platform_device_unregister(mainstone_snd_device);
15975+}
15976+
15977+module_init(mainstone_init);
15978+module_exit(mainstone_exit);
15979+
15980+/* Module information */
15981+MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
15982+MODULE_DESCRIPTION("ALSA SoC Mainstone");
15983+MODULE_LICENSE("GPL");
15984Index: linux-2.6-pxa-new/sound/soc/pxa/mainstone_wm9712.c
15985===================================================================
15986--- /dev/null
15987+++ linux-2.6-pxa-new/sound/soc/pxa/mainstone_wm9712.c
15988@@ -0,0 +1,171 @@
15989+/*
15990+ * mainstone.c -- SoC audio for Mainstone
15991+ *
15992+ * Copyright 2006 Wolfson Microelectronics PLC.
15993+ * Author: Liam Girdwood
15994+ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
15995+ *
15996+ * Mainstone audio amplifier code taken from arch/arm/mach-pxa/mainstone.c
15997+ * Copyright: MontaVista Software Inc.
15998+ *
15999+ * This program is free software; you can redistribute it and/or modify it
16000+ * under the terms of the GNU General Public License as published by the
16001+ * Free Software Foundation; either version 2 of the License, or (at your
16002+ * option) any later version.
16003+ *
16004+ * Revision history
16005+ * 29th Jan 2006 Initial version.
16006+ *
16007+ */
16008+
16009+#include <linux/module.h>
16010+#include <linux/moduleparam.h>
16011+#include <linux/device.h>
16012+#include <linux/i2c.h>
16013+#include <sound/driver.h>
16014+#include <sound/core.h>
16015+#include <sound/pcm.h>
16016+#include <sound/soc.h>
16017+#include <sound/soc-dapm.h>
16018+
16019+#include <asm/arch/pxa-regs.h>
16020+#include <asm/arch/mainstone.h>
16021+#include <asm/arch/audio.h>
16022+
16023+#include "../codecs/wm9712.h"
16024+#include "pxa2xx-pcm.h"
16025+
16026+static struct snd_soc_machine mainstone;
16027+static long mst_audio_suspend_mask;
16028+
16029+static int mainstone_suspend(struct platform_device *pdev, pm_message_t state)
16030+{
16031+ mst_audio_suspend_mask = MST_MSCWR2;
16032+ MST_MSCWR2 |= MST_MSCWR2_AC97_SPKROFF;
16033+ return 0;
16034+}
16035+
16036+static int mainstone_resume(struct platform_device *pdev)
16037+{
16038+ MST_MSCWR2 &= mst_audio_suspend_mask | ~MST_MSCWR2_AC97_SPKROFF;
16039+ return 0;
16040+}
16041+
16042+static int mainstone_probe(struct platform_device *pdev)
16043+{
16044+ MST_MSCWR2 &= ~MST_MSCWR2_AC97_SPKROFF;
16045+ return 0;
16046+}
16047+
16048+static int mainstone_remove(struct platform_device *pdev)
16049+{
16050+ MST_MSCWR2 |= MST_MSCWR2_AC97_SPKROFF;
16051+ return 0;
16052+}
16053+
16054+/* mainstone machine dapm widgets */
16055+static const struct snd_soc_dapm_widget mainstone_dapm_widgets[] = {
16056+ SND_SOC_DAPM_MIC("Mic (Internal)", NULL),
16057+};
16058+
16059+/* example machine interconnections */
16060+static const char* intercon[][3] = {
16061+
16062+ /* mic is connected to mic1 - with bias */
16063+ {"MIC1", NULL, "Mic Bias"},
16064+ {"Mic Bias", NULL, "Mic (Internal)"},
16065+
16066+ {NULL, NULL, NULL},
16067+};
16068+
16069+/*
16070+ * This is an example machine initialisation for a wm8753 connected to a
16071+ * Mainstone II. It is missing logic to detect hp/mic insertions and logic
16072+ * to re-route the audio in such an event.
16073+ */
16074+static int mainstone_wm9712_init(struct snd_soc_codec *codec)
16075+{
16076+ int i;
16077+
16078+ /* set up mainstone codec pins */
16079+ snd_soc_dapm_set_endpoint(codec, "RXP", 0);
16080+ snd_soc_dapm_set_endpoint(codec, "RXN", 0);
16081+ //snd_soc_dapm_set_endpoint(codec, "MIC2", 0);
16082+
16083+ /* Add mainstone specific widgets */
16084+ for(i = 0; i < ARRAY_SIZE(mainstone_dapm_widgets); i++) {
16085+ snd_soc_dapm_new_control(codec, &mainstone_dapm_widgets[i]);
16086+ }
16087+
16088+ /* set up mainstone specific audio path interconnects */
16089+ for(i = 0; intercon[i][0] != NULL; i++) {
16090+ snd_soc_dapm_connect_input(codec, intercon[i][0], intercon[i][1], intercon[i][2]);
16091+ }
16092+
16093+ snd_soc_dapm_sync_endpoints(codec);
16094+ return 0;
16095+}
16096+
16097+static struct snd_soc_dai_link mainstone_dai[] = {
16098+{
16099+ .name = "AC97",
16100+ .stream_name = "AC97 HiFi",
16101+ .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI],
16102+ .codec_dai = &wm9712_dai[WM9712_DAI_AC97_HIFI],
16103+ .init = mainstone_wm9712_init,
16104+},
16105+{
16106+ .name = "AC97 Aux",
16107+ .stream_name = "AC97 Aux",
16108+ .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX],
16109+ .codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX],
16110+},
16111+};
16112+
16113+static struct snd_soc_machine mainstone = {
16114+ .name = "Mainstone",
16115+ .probe = mainstone_probe,
16116+ .remove = mainstone_remove,
16117+ .suspend_pre = mainstone_suspend,
16118+ .resume_post = mainstone_resume,
16119+ .dai_link = mainstone_dai,
16120+ .num_links = ARRAY_SIZE(mainstone_dai),
16121+};
16122+
16123+static struct snd_soc_device mainstone_snd_ac97_devdata = {
16124+ .machine = &mainstone,
16125+ .platform = &pxa2xx_soc_platform,
16126+ .codec_dev = &soc_codec_dev_wm9712,
16127+};
16128+
16129+static struct platform_device *mainstone_snd_ac97_device;
16130+
16131+static int __init mainstone_init(void)
16132+{
16133+ int ret;
16134+
16135+ mainstone_snd_ac97_device = platform_device_alloc("soc-audio", -1);
16136+ if (!mainstone_snd_ac97_device)
16137+ return -ENOMEM;
16138+
16139+ platform_set_drvdata(mainstone_snd_ac97_device, &mainstone_snd_ac97_devdata);
16140+ mainstone_snd_ac97_devdata.dev = &mainstone_snd_ac97_device->dev;
16141+
16142+ if((ret = platform_device_add(mainstone_snd_ac97_device)) != 0)
16143+ platform_device_put(mainstone_snd_ac97_device);
16144+
16145+ return ret;
16146+}
16147+
16148+static void __exit mainstone_exit(void)
16149+{
16150+ platform_device_unregister(mainstone_snd_ac97_device);
16151+}
16152+
16153+module_init(mainstone_init);
16154+module_exit(mainstone_exit);
16155+
16156+/* Module information */
16157+MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
16158+MODULE_DESCRIPTION("ALSA SoC WM9712 Mainstone");
16159+MODULE_LICENSE("GPL");
16160Index: linux-2.6-pxa-new/sound/soc/pxa/mainstone_wm9713.c
16161===================================================================
16162--- /dev/null
16163+++ linux-2.6-pxa-new/sound/soc/pxa/mainstone_wm9713.c
16164@@ -0,0 +1,263 @@
16165+/*
16166+ * mainstone.c -- SoC audio for Mainstone
16167+ *
16168+ * Copyright 2006 Wolfson Microelectronics PLC.
16169+ * Author: Liam Girdwood
16170+ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
16171+ *
16172+ * Mainstone audio amplifier code taken from arch/arm/mach-pxa/mainstone.c
16173+ * Copyright: MontaVista Software Inc.
16174+ *
16175+ * This program is free software; you can redistribute it and/or modify it
16176+ * under the terms of the GNU General Public License as published by the
16177+ * Free Software Foundation; either version 2 of the License, or (at your
16178+ * option) any later version.
16179+ *
16180+ * Revision history
16181+ * 29th Jan 2006 Initial version.
16182+ *
16183+ */
16184+
16185+#include <linux/module.h>
16186+#include <linux/moduleparam.h>
16187+#include <linux/device.h>
16188+#include <linux/i2c.h>
16189+#include <sound/driver.h>
16190+#include <sound/core.h>
16191+#include <sound/pcm.h>
16192+#include <sound/soc.h>
16193+#include <sound/soc-dapm.h>
16194+
16195+#include <asm/arch/pxa-regs.h>
16196+#include <asm/arch/mainstone.h>
16197+#include <asm/arch/audio.h>
16198+
16199+#include "../codecs/wm9713.h"
16200+#include "pxa2xx-pcm.h"
16201+
16202+static struct snd_soc_machine mainstone;
16203+
16204+static int mainstone_startup(struct snd_pcm_substream *substream)
16205+{
16206+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
16207+
16208+ if(rtd->cpu_dai->type == SND_SOC_DAI_PCM && rtd->cpu_dai->id == 1) {
16209+ /* enable USB on the go MUX so we can use SSPFRM2 */
16210+ MST_MSCWR2 |= MST_MSCWR2_USB_OTG_SEL;
16211+ MST_MSCWR2 &= ~MST_MSCWR2_USB_OTG_RST;
16212+ }
16213+ return 0;
16214+}
16215+
16216+static void mainstone_shutdown(struct snd_pcm_substream *substream)
16217+{
16218+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
16219+
16220+ if(rtd->cpu_dai->type == SND_SOC_DAI_PCM && rtd->cpu_dai->id == 1) {
16221+ /* disable USB on the go MUX so we can use ttyS0 */
16222+ MST_MSCWR2 &= ~MST_MSCWR2_USB_OTG_SEL;
16223+ MST_MSCWR2 |= MST_MSCWR2_USB_OTG_RST;
16224+ }
16225+}
16226+
16227+static struct snd_soc_ops mainstone_ops = {
16228+ .startup = mainstone_startup,
16229+ .shutdown = mainstone_shutdown,
16230+};
16231+
16232+static int test = 0;
16233+static int get_test(struct snd_kcontrol *kcontrol,
16234+ struct snd_ctl_elem_value *ucontrol)
16235+{
16236+ ucontrol->value.integer.value[0] = test;
16237+ return 0;
16238+}
16239+
16240+static int set_test(struct snd_kcontrol *kcontrol,
16241+ struct snd_ctl_elem_value *ucontrol)
16242+{
16243+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
16244+
16245+ test = ucontrol->value.integer.value[0];
16246+ if(test) {
16247+
16248+ } else {
16249+
16250+ }
16251+ return 0;
16252+}
16253+
16254+static long mst_audio_suspend_mask;
16255+
16256+static int mainstone_suspend(struct platform_device *pdev, pm_message_t state)
16257+{
16258+ mst_audio_suspend_mask = MST_MSCWR2;
16259+ MST_MSCWR2 |= MST_MSCWR2_AC97_SPKROFF;
16260+ return 0;
16261+}
16262+
16263+static int mainstone_resume(struct platform_device *pdev)
16264+{
16265+ MST_MSCWR2 &= mst_audio_suspend_mask | ~MST_MSCWR2_AC97_SPKROFF;
16266+ return 0;
16267+}
16268+
16269+static int mainstone_probe(struct platform_device *pdev)
16270+{
16271+ MST_MSCWR2 &= ~MST_MSCWR2_AC97_SPKROFF;
16272+ return 0;
16273+}
16274+
16275+static int mainstone_remove(struct platform_device *pdev)
16276+{
16277+ MST_MSCWR2 |= MST_MSCWR2_AC97_SPKROFF;
16278+ return 0;
16279+}
16280+
16281+static const char* test_function[] = {"Off", "On"};
16282+static const struct soc_enum mainstone_enum[] = {
16283+ SOC_ENUM_SINGLE_EXT(2, test_function),
16284+};
16285+
16286+static const struct snd_kcontrol_new mainstone_controls[] = {
16287+ SOC_ENUM_EXT("ATest Function", mainstone_enum[0], get_test, set_test),
16288+};
16289+
16290+/* mainstone machine dapm widgets */
16291+static const struct snd_soc_dapm_widget mainstone_dapm_widgets[] = {
16292+ SND_SOC_DAPM_MIC("Mic 1", NULL),
16293+ SND_SOC_DAPM_MIC("Mic 2", NULL),
16294+ SND_SOC_DAPM_MIC("Mic 3", NULL),
16295+};
16296+
16297+/* example machine audio_mapnections */
16298+static const char* audio_map[][3] = {
16299+
16300+ /* mic is connected to mic1 - with bias */
16301+ {"MIC1", NULL, "Mic Bias"},
16302+ {"Mic Bias", NULL, "Mic 1"},
16303+ /* mic is connected to mic2A - with bias */
16304+ {"MIC2A", NULL, "Mic Bias"},
16305+ {"Mic Bias", NULL, "Mic 2"},
16306+ /* mic is connected to mic2B - with bias */
16307+ {"MIC2B", NULL, "Mic Bias"},
16308+ {"Mic Bias", NULL, "Mic 3"},
16309+
16310+ {NULL, NULL, NULL},
16311+};
16312+
16313+/*
16314+ * This is an example machine initialisation for a wm9713 connected to a
16315+ * Mainstone II. It is missing logic to detect hp/mic insertions and logic
16316+ * to re-route the audio in such an event.
16317+ */
16318+static int mainstone_wm9713_init(struct snd_soc_codec *codec)
16319+{
16320+ int i, err;
16321+
16322+ /* set up mainstone codec pins */
16323+ snd_soc_dapm_set_endpoint(codec, "RXP", 0);
16324+ snd_soc_dapm_set_endpoint(codec, "RXN", 0);
16325+ //snd_soc_dapm_set_endpoint(codec, "MIC2", 0);
16326+
16327+ /* Add test specific controls */
16328+ for (i = 0; i < ARRAY_SIZE(mainstone_controls); i++) {
16329+ if ((err = snd_ctl_add(codec->card,
16330+ snd_soc_cnew(&mainstone_controls[i],codec, NULL))) < 0)
16331+ return err;
16332+ }
16333+
16334+ /* Add mainstone specific widgets */
16335+ for(i = 0; i < ARRAY_SIZE(mainstone_dapm_widgets); i++) {
16336+ snd_soc_dapm_new_control(codec, &mainstone_dapm_widgets[i]);
16337+ }
16338+
16339+ /* set up mainstone specific audio path audio_mapnects */
16340+ for(i = 0; audio_map[i][0] != NULL; i++) {
16341+ snd_soc_dapm_connect_input(codec, audio_map[i][0], audio_map[i][1], audio_map[i][2]);
16342+ }
16343+
16344+ snd_soc_dapm_sync_endpoints(codec);
16345+ return 0;
16346+}
16347+
16348+/* configure the system audio clock */
16349+unsigned int mainstone_config_sysclk(struct snd_soc_pcm_runtime *rtd,
16350+ struct snd_soc_clock_info *info)
16351+{
16352+ return rtd->codec_dai->config_sysclk(rtd->codec_dai, info, 24576000);
16353+}
16354+
16355+static struct snd_soc_dai_link mainstone_dai[] = {
16356+{
16357+ .name = "AC97",
16358+ .stream_name = "AC97 HiFi",
16359+ .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI],
16360+ .codec_dai = &wm9713_dai[WM9713_DAI_AC97_HIFI],
16361+ .init = mainstone_wm9713_init,
16362+ .config_sysclk = mainstone_config_sysclk,
16363+},
16364+{
16365+ .name = "AC97 Aux",
16366+ .stream_name = "AC97 Aux",
16367+ .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX],
16368+ .codec_dai = &wm9713_dai[WM9713_DAI_AC97_AUX],
16369+ .config_sysclk = mainstone_config_sysclk,
16370+},
16371+{
16372+ .name = "WM9713",
16373+ .stream_name = "WM9713 Voice",
16374+ .cpu_dai = &pxa_ssp_dai[PXA2XX_DAI_SSP2],
16375+ .codec_dai = &wm9713_dai[WM9713_DAI_PCM_VOICE],
16376+ .config_sysclk = mainstone_config_sysclk,
16377+},
16378+};
16379+
16380+static struct snd_soc_machine mainstone = {
16381+ .name = "Mainstone",
16382+ .probe = mainstone_probe,
16383+ .remove = mainstone_remove,
16384+ .suspend_pre = mainstone_suspend,
16385+ .resume_post = mainstone_resume,
16386+ .ops = &mainstone_ops,
16387+ .dai_link = mainstone_dai,
16388+ .num_links = ARRAY_SIZE(mainstone_dai),
16389+};
16390+
16391+static struct snd_soc_device mainstone_snd_ac97_devdata = {
16392+ .machine = &mainstone,
16393+ .platform = &pxa2xx_soc_platform,
16394+ .codec_dev = &soc_codec_dev_wm9713,
16395+};
16396+
16397+static struct platform_device *mainstone_snd_ac97_device;
16398+
16399+static int __init mainstone_init(void)
16400+{
16401+ int ret;
16402+
16403+ mainstone_snd_ac97_device = platform_device_alloc("soc-audio", -1);
16404+ if (!mainstone_snd_ac97_device)
16405+ return -ENOMEM;
16406+
16407+ platform_set_drvdata(mainstone_snd_ac97_device, &mainstone_snd_ac97_devdata);
16408+ mainstone_snd_ac97_devdata.dev = &mainstone_snd_ac97_device->dev;
16409+
16410+ if((ret = platform_device_add(mainstone_snd_ac97_device)) != 0)
16411+ platform_device_put(mainstone_snd_ac97_device);
16412+
16413+ return ret;
16414+}
16415+
16416+static void __exit mainstone_exit(void)
16417+{
16418+ platform_device_unregister(mainstone_snd_ac97_device);
16419+}
16420+
16421+module_init(mainstone_init);
16422+module_exit(mainstone_exit);
16423+
16424+/* Module information */
16425+MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
16426+MODULE_DESCRIPTION("ALSA SoC WM9713 Mainstone");
16427+MODULE_LICENSE("GPL");
16428Index: linux-2.6-pxa-new/sound/soc/pxa/poodle.c
16429===================================================================
16430--- /dev/null
16431+++ linux-2.6-pxa-new/sound/soc/pxa/poodle.c
16432@@ -0,0 +1,329 @@
16433+/*
16434+ * poodle.c -- SoC audio for Poodle
16435+ *
16436+ * Copyright 2005 Wolfson Microelectronics PLC.
16437+ * Copyright 2005 Openedhand Ltd.
16438+ *
16439+ * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
16440+ * Richard Purdie <richard@openedhand.com>
16441+ *
16442+ * This program is free software; you can redistribute it and/or modify it
16443+ * under the terms of the GNU General Public License as published by the
16444+ * Free Software Foundation; either version 2 of the License, or (at your
16445+ * option) any later version.
16446+ *
16447+ */
16448+
16449+#include <linux/module.h>
16450+#include <linux/moduleparam.h>
16451+#include <linux/timer.h>
16452+#include <linux/interrupt.h>
16453+#include <linux/platform_device.h>
16454+#include <sound/driver.h>
16455+#include <sound/core.h>
16456+#include <sound/pcm.h>
16457+#include <sound/soc.h>
16458+#include <sound/soc-dapm.h>
16459+
16460+#include <asm/mach-types.h>
16461+#include <asm/hardware/locomo.h>
16462+#include <asm/arch/pxa-regs.h>
16463+#include <asm/arch/hardware.h>
16464+#include <asm/arch/poodle.h>
16465+#include <asm/arch/audio.h>
16466+
16467+#include "../codecs/wm8731.h"
16468+#include "pxa2xx-pcm.h"
16469+
16470+#define POODLE_HP 1
16471+#define POODLE_HP_OFF 0
16472+#define POODLE_SPK_ON 1
16473+#define POODLE_SPK_OFF 0
16474+
16475+ /* audio clock in Hz - rounded from 12.235MHz */
16476+#define POODLE_AUDIO_CLOCK 12288000
16477+
16478+static int poodle_jack_func;
16479+static int poodle_spk_func;
16480+
16481+static void poodle_ext_control(struct snd_soc_codec *codec)
16482+{
16483+ int spk = 0;
16484+
16485+ /* set up jack connection */
16486+ if (poodle_jack_func == POODLE_HP) {
16487+ /* set = unmute headphone */
16488+ locomo_gpio_write(&poodle_locomo_device.dev,
16489+ POODLE_LOCOMO_GPIO_MUTE_L, 1);
16490+ locomo_gpio_write(&poodle_locomo_device.dev,
16491+ POODLE_LOCOMO_GPIO_MUTE_R, 1);
16492+ snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 1);
16493+ } else {
16494+ locomo_gpio_write(&poodle_locomo_device.dev,
16495+ POODLE_LOCOMO_GPIO_MUTE_L, 0);
16496+ locomo_gpio_write(&poodle_locomo_device.dev,
16497+ POODLE_LOCOMO_GPIO_MUTE_R, 0);
16498+ snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0);
16499+ }
16500+
16501+ if (poodle_spk_func == POODLE_SPK_ON)
16502+ spk = 1;
16503+
16504+ /* set the enpoints to their new connetion states */
16505+ snd_soc_dapm_set_endpoint(codec, "Ext Spk", spk);
16506+
16507+ /* signal a DAPM event */
16508+ snd_soc_dapm_sync_endpoints(codec);
16509+}
16510+
16511+static int poodle_startup(struct snd_pcm_substream *substream)
16512+{
16513+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
16514+ struct snd_soc_codec *codec = rtd->socdev->codec;
16515+
16516+ /* check the jack status at stream startup */
16517+ poodle_ext_control(codec);
16518+ return 0;
16519+}
16520+
16521+/* we need to unmute the HP at shutdown as the mute burns power on poodle */
16522+static int poodle_shutdown(struct snd_pcm_substream *substream)
16523+{
16524+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
16525+ struct snd_soc_codec *codec = rtd->socdev->codec;
16526+
16527+ /* set = unmute headphone */
16528+ locomo_gpio_write(&poodle_locomo_device.dev,
16529+ POODLE_LOCOMO_GPIO_MUTE_L, 1);
16530+ locomo_gpio_write(&poodle_locomo_device.dev,
16531+ POODLE_LOCOMO_GPIO_MUTE_R, 1);
16532+ return 0;
16533+}
16534+
16535+static struct snd_soc_ops poodle_ops = {
16536+ .startup = poodle_startup,
16537+ .shutdown = poodle_shutdown,
16538+};
16539+
16540+static int poodle_get_jack(struct snd_kcontrol *kcontrol,
16541+ struct snd_ctl_elem_value *ucontrol)
16542+{
16543+ ucontrol->value.integer.value[0] = poodle_jack_func;
16544+ return 0;
16545+}
16546+
16547+static int poodle_set_jack(struct snd_kcontrol *kcontrol,
16548+ struct snd_ctl_elem_value *ucontrol)
16549+{
16550+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
16551+
16552+ if (poodle_jack_func == ucontrol->value.integer.value[0])
16553+ return 0;
16554+
16555+ poodle_jack_func = ucontrol->value.integer.value[0];
16556+ poodle_ext_control(codec);
16557+ return 1;
16558+}
16559+
16560+static int poodle_get_spk(struct snd_kcontrol *kcontrol,
16561+ struct snd_ctl_elem_value *ucontrol)
16562+{
16563+ ucontrol->value.integer.value[0] = poodle_spk_func;
16564+ return 0;
16565+}
16566+
16567+static int poodle_set_spk(struct snd_kcontrol *kcontrol,
16568+ struct snd_ctl_elem_value *ucontrol)
16569+{
16570+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
16571+
16572+ if (poodle_spk_func == ucontrol->value.integer.value[0])
16573+ return 0;
16574+
16575+ poodle_spk_func = ucontrol->value.integer.value[0];
16576+ poodle_ext_control(codec);
16577+ return 1;
16578+}
16579+
16580+static int poodle_amp_event(struct snd_soc_dapm_widget *w, int event)
16581+{
16582+ if (SND_SOC_DAPM_EVENT_ON(event))
16583+ locomo_gpio_write(&poodle_locomo_device.dev,
16584+ POODLE_LOCOMO_GPIO_AMP_ON, 0);
16585+ else
16586+ locomo_gpio_write(&poodle_locomo_device.dev,
16587+ POODLE_LOCOMO_GPIO_AMP_ON, 1);
16588+
16589+ return 0;
16590+}
16591+
16592+/* poodle machine dapm widgets */
16593+static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = {
16594+SND_SOC_DAPM_HP("Headphone Jack", NULL),
16595+SND_SOC_DAPM_SPK("Ext Spk", poodle_amp_event),
16596+};
16597+
16598+/* Corgi machine audio_mapnections to the codec pins */
16599+static const char *audio_map[][3] = {
16600+
16601+ /* headphone connected to LHPOUT1, RHPOUT1 */
16602+ {"Headphone Jack", NULL, "LHPOUT"},
16603+ {"Headphone Jack", NULL, "RHPOUT"},
16604+
16605+ /* speaker connected to LOUT, ROUT */
16606+ {"Ext Spk", NULL, "ROUT"},
16607+ {"Ext Spk", NULL, "LOUT"},
16608+
16609+ {NULL, NULL, NULL},
16610+};
16611+
16612+static const char *jack_function[] = {"Off", "Headphone"};
16613+static const char *spk_function[] = {"Off", "On"};
16614+static const struct soc_enum poodle_enum[] = {
16615+ SOC_ENUM_SINGLE_EXT(2, jack_function),
16616+ SOC_ENUM_SINGLE_EXT(2, spk_function),
16617+};
16618+
16619+static const snd_kcontrol_new_t wm8731_poodle_controls[] = {
16620+ SOC_ENUM_EXT("Jack Function", poodle_enum[0], poodle_get_jack,
16621+ poodle_set_jack),
16622+ SOC_ENUM_EXT("Speaker Function", poodle_enum[1], poodle_get_spk,
16623+ poodle_set_spk),
16624+};
16625+
16626+/*
16627+ * Logic for a wm8731 as connected on a Sharp SL-C7x0 Device
16628+ */
16629+static int poodle_wm8731_init(struct snd_soc_codec *codec)
16630+{
16631+ int i, err;
16632+
16633+ snd_soc_dapm_set_endpoint(codec, "LLINEIN", 0);
16634+ snd_soc_dapm_set_endpoint(codec, "RLINEIN", 0);
16635+ snd_soc_dapm_set_endpoint(codec, "MICIN", 1);
16636+
16637+ /* Add poodle specific controls */
16638+ for (i = 0; i < ARRAY_SIZE(wm8731_poodle_controls); i++) {
16639+ err = snd_ctl_add(codec->card,
16640+ snd_soc_cnew(&wm8731_poodle_controls[i],codec, NULL));
16641+ if (err < 0)
16642+ return err;
16643+ }
16644+
16645+ /* Add poodle specific widgets */
16646+ for (i = 0; i < ARRAY_SIZE(wm8731_dapm_widgets); i++) {
16647+ snd_soc_dapm_new_control(codec, &wm8731_dapm_widgets[i]);
16648+ }
16649+
16650+ /* Set up poodle specific audio path audio_map */
16651+ for (i = 0; audio_map[i][0] != NULL; i++) {
16652+ snd_soc_dapm_connect_input(codec, audio_map[i][0],
16653+ audio_map[i][1], audio_map[i][2]);
16654+ }
16655+
16656+ snd_soc_dapm_sync_endpoints(codec);
16657+ return 0;
16658+}
16659+
16660+static unsigned int poodle_config_sysclk(struct snd_soc_pcm_runtime *rtd,
16661+ struct snd_soc_clock_info *info)
16662+{
16663+ if (info->bclk_master & SND_SOC_DAIFMT_CBS_CFS) {
16664+ /* pxa2xx is i2s master */
16665+ switch (info->rate) {
16666+ case 44100:
16667+ case 88200:
16668+ /* configure codec digital filters for 44.1, 88.2 */
16669+ rtd->codec_dai->config_sysclk(rtd->codec_dai, info,
16670+ 11289600);
16671+ break;
16672+ default:
16673+ /* configure codec digital filters for all other rates */
16674+ rtd->codec_dai->config_sysclk(rtd->codec_dai, info,
16675+ POODLE_AUDIO_CLOCK);
16676+ break;
16677+ }
16678+ return rtd->cpu_dai->config_sysclk(rtd->cpu_dai, info,
16679+ POODLE_AUDIO_CLOCK);
16680+ } else {
16681+ /* codec is i2s master -
16682+ * only configure codec DAI clock and filters */
16683+ return rtd->codec_dai->config_sysclk(rtd->codec_dai, info,
16684+ POODLE_AUDIO_CLOCK);
16685+ }
16686+}
16687+
16688+/* poodle digital audio interface glue - connects codec <--> CPU */
16689+static struct snd_soc_dai_link poodle_dai = {
16690+ .name = "WM8731",
16691+ .stream_name = "WM8731",
16692+ .cpu_dai = &pxa_i2s_dai,
16693+ .codec_dai = &wm8731_dai,
16694+ .init = poodle_wm8731_init,
16695+ .config_sysclk = poodle_config_sysclk,
16696+};
16697+
16698+/* poodle audio machine driver */
16699+static struct snd_soc_machine snd_soc_machine_poodle = {
16700+ .name = "Poodle",
16701+ .dai_link = &poodle_dai,
16702+ .num_links = 1,
16703+ .ops = &poodle_ops,
16704+};
16705+
16706+/* poodle audio private data */
16707+static struct wm8731_setup_data poodle_wm8731_setup = {
16708+ .i2c_address = 0x1b,
16709+};
16710+
16711+/* poodle audio subsystem */
16712+static struct snd_soc_device poodle_snd_devdata = {
16713+ .machine = &snd_soc_machine_poodle,
16714+ .platform = &pxa2xx_soc_platform,
16715+ .codec_dev = &soc_codec_dev_wm8731,
16716+ .codec_data = &poodle_wm8731_setup,
16717+};
16718+
16719+static struct platform_device *poodle_snd_device;
16720+
16721+static int __init poodle_init(void)
16722+{
16723+ int ret;
16724+
16725+ if (!machine_is_poodle())
16726+ return -ENODEV;
16727+
16728+ locomo_gpio_set_dir(&poodle_locomo_device.dev,
16729+ POODLE_LOCOMO_GPIO_AMP_ON, 0);
16730+ /* should we mute HP at startup - burning power ?*/
16731+ locomo_gpio_set_dir(&poodle_locomo_device.dev,
16732+ POODLE_LOCOMO_GPIO_MUTE_L, 0);
16733+ locomo_gpio_set_dir(&poodle_locomo_device.dev,
16734+ POODLE_LOCOMO_GPIO_MUTE_R, 0);
16735+
16736+ poodle_snd_device = platform_device_alloc("soc-audio", -1);
16737+ if (!poodle_snd_device)
16738+ return -ENOMEM;
16739+
16740+ platform_set_drvdata(poodle_snd_device, &poodle_snd_devdata);
16741+ poodle_snd_devdata.dev = &poodle_snd_device->dev;
16742+ ret = platform_device_add(poodle_snd_device);
16743+
16744+ if (ret)
16745+ platform_device_put(poodle_snd_device);
16746+
16747+ return ret;
16748+}
16749+
16750+static void __exit poodle_exit(void)
16751+{
16752+ platform_device_unregister(poodle_snd_device);
16753+}
16754+
16755+module_init(poodle_init);
16756+module_exit(poodle_exit);
16757+
16758+/* Module information */
16759+MODULE_AUTHOR("Richard Purdie");
16760+MODULE_DESCRIPTION("ALSA SoC Poodle");
16761+MODULE_LICENSE("GPL");
16762Index: linux-2.6-pxa-new/sound/soc/pxa/pxa2xx-ac97.c
16763===================================================================
16764--- /dev/null
16765+++ linux-2.6-pxa-new/sound/soc/pxa/pxa2xx-ac97.c
16766@@ -0,0 +1,437 @@
16767+/*
16768+ * linux/sound/pxa2xx-ac97.c -- AC97 support for the Intel PXA2xx chip.
16769+ *
16770+ * Author: Nicolas Pitre
16771+ * Created: Dec 02, 2004
16772+ * Copyright: MontaVista Software Inc.
16773+ *
16774+ * This program is free software; you can redistribute it and/or modify
16775+ * it under the terms of the GNU General Public License version 2 as
16776+ * published by the Free Software Foundation.
16777+ */
16778+
16779+#include <linux/init.h>
16780+#include <linux/module.h>
16781+#include <linux/platform_device.h>
16782+#include <linux/interrupt.h>
16783+#include <linux/wait.h>
16784+#include <linux/delay.h>
16785+
16786+#include <sound/driver.h>
16787+#include <sound/core.h>
16788+#include <sound/pcm.h>
16789+#include <sound/ac97_codec.h>
16790+#include <sound/initval.h>
16791+#include <sound/soc.h>
16792+
16793+#include <asm/irq.h>
16794+#include <linux/mutex.h>
16795+#include <asm/hardware.h>
16796+#include <asm/arch/pxa-regs.h>
16797+#include <asm/arch/audio.h>
16798+
16799+#include "pxa2xx-pcm.h"
16800+
16801+static DEFINE_MUTEX(car_mutex);
16802+static DECLARE_WAIT_QUEUE_HEAD(gsr_wq);
16803+static volatile long gsr_bits;
16804+
16805+#define AC97_DIR \
16806+ (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
16807+
16808+#define AC97_RATES \
16809+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
16810+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
16811+
16812+/* may need to expand this */
16813+static struct snd_soc_dai_mode pxa2xx_ac97_modes[] = {
16814+ {
16815+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
16816+ .pcmrate = AC97_RATES,
16817+ .pcmdir = AC97_DIR,
16818+ },
16819+};
16820+
16821+/*
16822+ * Beware PXA27x bugs:
16823+ *
16824+ * o Slot 12 read from modem space will hang controller.
16825+ * o CDONE, SDONE interrupt fails after any slot 12 IO.
16826+ *
16827+ * We therefore have an hybrid approach for waiting on SDONE (interrupt or
16828+ * 1 jiffy timeout if interrupt never comes).
16829+ */
16830+
16831+static unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97,
16832+ unsigned short reg)
16833+{
16834+ unsigned short val = -1;
16835+ volatile u32 *reg_addr;
16836+
16837+ mutex_lock(&car_mutex);
16838+
16839+ /* set up primary or secondary codec/modem space */
16840+#ifdef CONFIG_PXA27x
16841+ reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE;
16842+#else
16843+ if (reg == AC97_GPIO_STATUS)
16844+ reg_addr = ac97->num ? &SMC_REG_BASE : &PMC_REG_BASE;
16845+ else
16846+ reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE;
16847+#endif
16848+ reg_addr += (reg >> 1);
16849+
16850+#ifndef CONFIG_PXA27x
16851+ if (reg == AC97_GPIO_STATUS) {
16852+ /* read from controller cache */
16853+ val = *reg_addr;
16854+ goto out;
16855+ }
16856+#endif
16857+
16858+ /* start read access across the ac97 link */
16859+ GSR = GSR_CDONE | GSR_SDONE;
16860+ gsr_bits = 0;
16861+ val = *reg_addr;
16862+
16863+ wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1);
16864+ if (!((GSR | gsr_bits) & GSR_SDONE)) {
16865+ printk(KERN_ERR "%s: read error (ac97_reg=%x GSR=%#lx)\n",
16866+ __FUNCTION__, reg, GSR | gsr_bits);
16867+ val = -1;
16868+ goto out;
16869+ }
16870+
16871+ /* valid data now */
16872+ GSR = GSR_CDONE | GSR_SDONE;
16873+ gsr_bits = 0;
16874+ val = *reg_addr;
16875+ /* but we've just started another cycle... */
16876+ wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1);
16877+
16878+out: mutex_unlock(&car_mutex);
16879+ return val;
16880+}
16881+
16882+static void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
16883+ unsigned short val)
16884+{
16885+ volatile u32 *reg_addr;
16886+
16887+ mutex_lock(&car_mutex);
16888+
16889+ /* set up primary or secondary codec/modem space */
16890+#ifdef CONFIG_PXA27x
16891+ reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE;
16892+#else
16893+ if (reg == AC97_GPIO_STATUS)
16894+ reg_addr = ac97->num ? &SMC_REG_BASE : &PMC_REG_BASE;
16895+ else
16896+ reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE;
16897+#endif
16898+ reg_addr += (reg >> 1);
16899+
16900+ GSR = GSR_CDONE | GSR_SDONE;
16901+ gsr_bits = 0;
16902+ *reg_addr = val;
16903+ wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_CDONE, 1);
16904+ if (!((GSR | gsr_bits) & GSR_CDONE))
16905+ printk(KERN_ERR "%s: write error (ac97_reg=%x GSR=%#lx)\n",
16906+ __FUNCTION__, reg, GSR | gsr_bits);
16907+
16908+ mutex_unlock(&car_mutex);
16909+}
16910+
16911+static void pxa2xx_ac97_warm_reset(struct snd_ac97 *ac97)
16912+{
16913+ gsr_bits = 0;
16914+
16915+#ifdef CONFIG_PXA27x
16916+ /* warm reset broken on Bulverde,
16917+ so manually keep AC97 reset high */
16918+ pxa_gpio_mode(113 | GPIO_OUT | GPIO_DFLT_HIGH);
16919+ udelay(10);
16920+ GCR |= GCR_WARM_RST;
16921+ pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT);
16922+ udelay(500);
16923+#else
16924+ GCR |= GCR_WARM_RST | GCR_PRIRDY_IEN | GCR_SECRDY_IEN;
16925+ wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1);
16926+#endif
16927+
16928+ if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)))
16929+ printk(KERN_INFO "%s: warm reset timeout (GSR=%#lx)\n",
16930+ __FUNCTION__, gsr_bits);
16931+
16932+ GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN);
16933+ GCR |= GCR_SDONE_IE|GCR_CDONE_IE;
16934+}
16935+
16936+static void pxa2xx_ac97_cold_reset(struct snd_ac97 *ac97)
16937+{
16938+ GCR &= GCR_COLD_RST; /* clear everything but nCRST */
16939+ GCR &= ~GCR_COLD_RST; /* then assert nCRST */
16940+
16941+ gsr_bits = 0;
16942+#ifdef CONFIG_PXA27x
16943+ /* PXA27x Developers Manual section 13.5.2.2.1 */
16944+ pxa_set_cken(1 << 31, 1);
16945+ udelay(5);
16946+ pxa_set_cken(1 << 31, 0);
16947+ GCR = GCR_COLD_RST;
16948+ udelay(50);
16949+#else
16950+ GCR = GCR_COLD_RST;
16951+ GCR |= GCR_CDONE_IE|GCR_SDONE_IE;
16952+ wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1);
16953+#endif
16954+
16955+ if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)))
16956+ printk(KERN_INFO "%s: cold reset timeout (GSR=%#lx)\n",
16957+ __FUNCTION__, gsr_bits);
16958+
16959+ GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN);
16960+ GCR |= GCR_SDONE_IE|GCR_CDONE_IE;
16961+}
16962+
16963+static irqreturn_t pxa2xx_ac97_irq(int irq, void *dev_id)
16964+{
16965+ long status;
16966+
16967+ status = GSR;
16968+ if (status) {
16969+ GSR = status;
16970+ gsr_bits |= status;
16971+ wake_up(&gsr_wq);
16972+
16973+#ifdef CONFIG_PXA27x
16974+ /* Although we don't use those we still need to clear them
16975+ since they tend to spuriously trigger when MMC is used
16976+ (hardware bug? go figure)... */
16977+ MISR = MISR_EOC;
16978+ PISR = PISR_EOC;
16979+ MCSR = MCSR_EOC;
16980+#endif
16981+
16982+ return IRQ_HANDLED;
16983+ }
16984+
16985+ return IRQ_NONE;
16986+}
16987+
16988+struct snd_ac97_bus_ops soc_ac97_ops = {
16989+ .read = pxa2xx_ac97_read,
16990+ .write = pxa2xx_ac97_write,
16991+ .warm_reset = pxa2xx_ac97_warm_reset,
16992+ .reset = pxa2xx_ac97_cold_reset,
16993+};
16994+
16995+static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_stereo_out = {
16996+ .name = "AC97 PCM Stereo out",
16997+ .dev_addr = __PREG(PCDR),
16998+ .drcmr = &DRCMRTXPCDR,
16999+ .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
17000+ DCMD_BURST32 | DCMD_WIDTH4,
17001+};
17002+
17003+static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_stereo_in = {
17004+ .name = "AC97 PCM Stereo in",
17005+ .dev_addr = __PREG(PCDR),
17006+ .drcmr = &DRCMRRXPCDR,
17007+ .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
17008+ DCMD_BURST32 | DCMD_WIDTH4,
17009+};
17010+
17011+static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_aux_mono_out = {
17012+ .name = "AC97 Aux PCM (Slot 5) Mono out",
17013+ .dev_addr = __PREG(MODR),
17014+ .drcmr = &DRCMRTXMODR,
17015+ .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
17016+ DCMD_BURST16 | DCMD_WIDTH2,
17017+};
17018+
17019+static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_aux_mono_in = {
17020+ .name = "AC97 Aux PCM (Slot 5) Mono in",
17021+ .dev_addr = __PREG(MODR),
17022+ .drcmr = &DRCMRRXMODR,
17023+ .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
17024+ DCMD_BURST16 | DCMD_WIDTH2,
17025+};
17026+
17027+static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_mic_mono_in = {
17028+ .name = "AC97 Mic PCM (Slot 6) Mono in",
17029+ .dev_addr = __PREG(MCDR),
17030+ .drcmr = &DRCMRRXMCDR,
17031+ .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
17032+ DCMD_BURST16 | DCMD_WIDTH2,
17033+};
17034+
17035+#ifdef CONFIG_PM
17036+static int pxa2xx_ac97_suspend(struct platform_device *pdev,
17037+ struct snd_soc_cpu_dai *dai)
17038+{
17039+ GCR |= GCR_ACLINK_OFF;
17040+ pxa_set_cken(CKEN2_AC97, 0);
17041+ return 0;
17042+}
17043+
17044+static int pxa2xx_ac97_resume(struct platform_device *pdev,
17045+ struct snd_soc_cpu_dai *dai)
17046+{
17047+ pxa_gpio_mode(GPIO31_SYNC_AC97_MD);
17048+ pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD);
17049+ pxa_gpio_mode(GPIO28_BITCLK_AC97_MD);
17050+ pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD);
17051+#ifdef CONFIG_PXA27x
17052+ /* Use GPIO 113 as AC97 Reset on Bulverde */
17053+ pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT);
17054+#endif
17055+ pxa_set_cken(CKEN2_AC97, 1);
17056+ return 0;
17057+}
17058+
17059+#else
17060+#define pxa2xx_ac97_suspend NULL
17061+#define pxa2xx_ac97_resume NULL
17062+#endif
17063+
17064+static int pxa2xx_ac97_probe(struct platform_device *pdev)
17065+{
17066+ int ret;
17067+
17068+ ret = request_irq(IRQ_AC97, pxa2xx_ac97_irq, 0, "AC97", NULL);
17069+ if (ret < 0)
17070+ goto err;
17071+
17072+ pxa_gpio_mode(GPIO31_SYNC_AC97_MD);
17073+ pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD);
17074+ pxa_gpio_mode(GPIO28_BITCLK_AC97_MD);
17075+ pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD);
17076+#ifdef CONFIG_PXA27x
17077+ /* Use GPIO 113 as AC97 Reset on Bulverde */
17078+ pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT);
17079+#endif
17080+ pxa_set_cken(CKEN2_AC97, 1);
17081+ return 0;
17082+
17083+ err:
17084+ if (CKEN & CKEN2_AC97) {
17085+ GCR |= GCR_ACLINK_OFF;
17086+ free_irq(IRQ_AC97, NULL);
17087+ pxa_set_cken(CKEN2_AC97, 0);
17088+ }
17089+ return ret;
17090+}
17091+
17092+static void pxa2xx_ac97_remove(struct platform_device *pdev)
17093+{
17094+ GCR |= GCR_ACLINK_OFF;
17095+ free_irq(IRQ_AC97, NULL);
17096+ pxa_set_cken(CKEN2_AC97, 0);
17097+}
17098+
17099+static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream,
17100+ struct snd_pcm_hw_params *params)
17101+{
17102+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
17103+
17104+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
17105+ rtd->cpu_dai->dma_data = &pxa2xx_ac97_pcm_stereo_out;
17106+ else
17107+ rtd->cpu_dai->dma_data = &pxa2xx_ac97_pcm_stereo_in;
17108+
17109+ return 0;
17110+}
17111+
17112+static int pxa2xx_ac97_hw_aux_params(struct snd_pcm_substream *substream,
17113+ struct snd_pcm_hw_params *params)
17114+{
17115+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
17116+
17117+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
17118+ rtd->cpu_dai->dma_data = &pxa2xx_ac97_pcm_aux_mono_out;
17119+ else
17120+ rtd->cpu_dai->dma_data = &pxa2xx_ac97_pcm_aux_mono_in;
17121+
17122+ return 0;
17123+}
17124+
17125+static int pxa2xx_ac97_hw_mic_params(struct snd_pcm_substream *substream,
17126+ struct snd_pcm_hw_params *params)
17127+{
17128+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
17129+
17130+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
17131+ return -ENODEV;
17132+ else
17133+ rtd->cpu_dai->dma_data = &pxa2xx_ac97_pcm_mic_mono_in;
17134+
17135+ return 0;
17136+}
17137+
17138+/*
17139+ * There is only 1 physical AC97 interface for pxa2xx, but it
17140+ * has extra fifo's that can be used for aux DACs and ADCs.
17141+ */
17142+struct snd_soc_cpu_dai pxa_ac97_dai[] = {
17143+{
17144+ .name = "pxa2xx-ac97",
17145+ .id = 0,
17146+ .type = SND_SOC_DAI_AC97,
17147+ .probe = pxa2xx_ac97_probe,
17148+ .remove = pxa2xx_ac97_remove,
17149+ .suspend = pxa2xx_ac97_suspend,
17150+ .resume = pxa2xx_ac97_resume,
17151+ .playback = {
17152+ .stream_name = "AC97 Playback",
17153+ .channels_min = 2,
17154+ .channels_max = 2,},
17155+ .capture = {
17156+ .stream_name = "AC97 Capture",
17157+ .channels_min = 2,
17158+ .channels_max = 2,},
17159+ .ops = {
17160+ .hw_params = pxa2xx_ac97_hw_params,},
17161+ .caps = {
17162+ .num_modes = ARRAY_SIZE(pxa2xx_ac97_modes),
17163+ .mode = pxa2xx_ac97_modes,},
17164+},
17165+{
17166+ .name = "pxa2xx-ac97-aux",
17167+ .id = 1,
17168+ .type = SND_SOC_DAI_AC97,
17169+ .playback = {
17170+ .stream_name = "AC97 Aux Playback",
17171+ .channels_min = 1,
17172+ .channels_max = 1,},
17173+ .capture = {
17174+ .stream_name = "AC97 Aux Capture",
17175+ .channels_min = 1,
17176+ .channels_max = 1,},
17177+ .ops = {
17178+ .hw_params = pxa2xx_ac97_hw_aux_params,},
17179+ .caps = {
17180+ .num_modes = ARRAY_SIZE(pxa2xx_ac97_modes),
17181+ .mode = pxa2xx_ac97_modes,},
17182+},
17183+{
17184+ .name = "pxa2xx-ac97-mic",
17185+ .id = 2,
17186+ .type = SND_SOC_DAI_AC97,
17187+ .capture = {
17188+ .stream_name = "AC97 Mic Capture",
17189+ .channels_min = 1,
17190+ .channels_max = 1,},
17191+ .ops = {
17192+ .hw_params = pxa2xx_ac97_hw_mic_params,},
17193+ .caps = {
17194+ .num_modes = ARRAY_SIZE(pxa2xx_ac97_modes),
17195+ .mode = pxa2xx_ac97_modes,},},
17196+};
17197+
17198+EXPORT_SYMBOL_GPL(pxa_ac97_dai);
17199+EXPORT_SYMBOL_GPL(soc_ac97_ops);
17200+
17201+MODULE_AUTHOR("Nicolas Pitre");
17202+MODULE_DESCRIPTION("AC97 driver for the Intel PXA2xx chip");
17203+MODULE_LICENSE("GPL");
17204Index: linux-2.6-pxa-new/sound/soc/pxa/pxa2xx-i2s.c
17205===================================================================
17206--- /dev/null
17207+++ linux-2.6-pxa-new/sound/soc/pxa/pxa2xx-i2s.c
17208@@ -0,0 +1,354 @@
17209+/*
17210+ * pxa2xx-i2s.c -- ALSA Soc Audio Layer
17211+ *
17212+ * Copyright 2005 Wolfson Microelectronics PLC.
17213+ * Author: Liam Girdwood
17214+ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
17215+ *
17216+ * This program is free software; you can redistribute it and/or modify it
17217+ * under the terms of the GNU General Public License as published by the
17218+ * Free Software Foundation; either version 2 of the License, or (at your
17219+ * option) any later version.
17220+ *
17221+ * Revision history
17222+ * 12th Aug 2005 Initial version.
17223+ */
17224+
17225+#include <linux/init.h>
17226+#include <linux/module.h>
17227+#include <linux/device.h>
17228+#include <linux/delay.h>
17229+#include <sound/driver.h>
17230+#include <sound/core.h>
17231+#include <sound/pcm.h>
17232+#include <sound/initval.h>
17233+#include <sound/soc.h>
17234+
17235+#include <asm/hardware.h>
17236+#include <asm/arch/pxa-regs.h>
17237+#include <asm/arch/audio.h>
17238+
17239+#include "pxa2xx-pcm.h"
17240+
17241+/* used to disable sysclk if external crystal is used */
17242+static int extclk;
17243+module_param(extclk, int, 0);
17244+MODULE_PARM_DESC(extclk, "set to 1 to disable pxa2xx i2s sysclk");
17245+
17246+struct pxa_i2s_port {
17247+ u32 sadiv;
17248+ u32 sacr0;
17249+ u32 sacr1;
17250+ u32 saimr;
17251+ int master;
17252+};
17253+static struct pxa_i2s_port pxa_i2s;
17254+
17255+#define PXA_I2S_DAIFMT \
17256+ (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF)
17257+
17258+#define PXA_I2S_DIR \
17259+ (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
17260+
17261+#define PXA_I2S_RATES \
17262+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
17263+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
17264+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
17265+
17266+/* priv is divider */
17267+static struct snd_soc_dai_mode pxa2xx_i2s_modes[] = {
17268+ /* pxa2xx I2S frame and clock master modes */
17269+ {
17270+ .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
17271+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
17272+ .pcmrate = SNDRV_PCM_RATE_8000,
17273+ .pcmdir = PXA_I2S_DIR,
17274+ .flags = SND_SOC_DAI_BFS_DIV,
17275+ .fs = 256,
17276+ .bfs = SND_SOC_FSBD(4),
17277+ .priv = 0x48,
17278+ },
17279+ {
17280+ .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
17281+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
17282+ .pcmrate = SNDRV_PCM_RATE_11025,
17283+ .pcmdir = PXA_I2S_DIR,
17284+ .flags = SND_SOC_DAI_BFS_DIV,
17285+ .fs = 256,
17286+ .bfs = SND_SOC_FSBD(4),
17287+ .priv = 0x34,
17288+ },
17289+ {
17290+ .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
17291+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
17292+ .pcmrate = SNDRV_PCM_RATE_16000,
17293+ .pcmdir = PXA_I2S_DIR,
17294+ .flags = SND_SOC_DAI_BFS_DIV,
17295+ .fs = 256,
17296+ .bfs = SND_SOC_FSBD(4),
17297+ .priv = 0x24,
17298+ },
17299+ {
17300+ .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
17301+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
17302+ .pcmrate = SNDRV_PCM_RATE_22050,
17303+ .pcmdir = PXA_I2S_DIR,
17304+ .flags = SND_SOC_DAI_BFS_DIV,
17305+ .fs = 256,
17306+ .bfs = SND_SOC_FSBD(4),
17307+ .priv = 0x1a,
17308+ },
17309+ {
17310+ .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
17311+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
17312+ .pcmrate = SNDRV_PCM_RATE_44100,
17313+ .pcmdir = PXA_I2S_DIR,
17314+ .flags = SND_SOC_DAI_BFS_DIV,
17315+ .fs = 256,
17316+ .bfs = SND_SOC_FSBD(4),
17317+ .priv = 0xd,
17318+ },
17319+ {
17320+ .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
17321+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
17322+ .pcmrate = SNDRV_PCM_RATE_48000,
17323+ .pcmdir = PXA_I2S_DIR,
17324+ .flags = SND_SOC_DAI_BFS_DIV,
17325+ .fs = 256,
17326+ .bfs = SND_SOC_FSBD(4),
17327+ .priv = 0xc,
17328+ },
17329+
17330+ /* pxa2xx I2S frame master and clock slave mode */
17331+ {
17332+ .fmt = PXA_I2S_DAIFMT | SND_SOC_DAIFMT_CBM_CFS,
17333+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
17334+ .pcmrate = PXA_I2S_RATES,
17335+ .pcmdir = PXA_I2S_DIR,
17336+ .fs = SND_SOC_FS_ALL,
17337+ .flags = SND_SOC_DAI_BFS_RATE,
17338+ .bfs = 64,
17339+ .priv = 0x48,
17340+ },
17341+};
17342+
17343+static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_out = {
17344+ .name = "I2S PCM Stereo out",
17345+ .dev_addr = __PREG(SADR),
17346+ .drcmr = &DRCMRTXSADR,
17347+ .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
17348+ DCMD_BURST32 | DCMD_WIDTH4,
17349+};
17350+
17351+static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_in = {
17352+ .name = "I2S PCM Stereo in",
17353+ .dev_addr = __PREG(SADR),
17354+ .drcmr = &DRCMRRXSADR,
17355+ .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
17356+ DCMD_BURST32 | DCMD_WIDTH4,
17357+};
17358+
17359+static struct pxa2xx_gpio gpio_bus[] = {
17360+ { /* I2S SoC Slave */
17361+ .rx = GPIO29_SDATA_IN_I2S_MD,
17362+ .tx = GPIO30_SDATA_OUT_I2S_MD,
17363+ .clk = GPIO28_BITCLK_IN_I2S_MD,
17364+ .frm = GPIO31_SYNC_I2S_MD,
17365+ },
17366+ { /* I2S SoC Master */
17367+#ifdef CONFIG_PXA27x
17368+ .sys = GPIO113_I2S_SYSCLK_MD,
17369+#else
17370+ .sys = GPIO32_SYSCLK_I2S_MD,
17371+#endif
17372+ .rx = GPIO29_SDATA_IN_I2S_MD,
17373+ .tx = GPIO30_SDATA_OUT_I2S_MD,
17374+ .clk = GPIO28_BITCLK_OUT_I2S_MD,
17375+ .frm = GPIO31_SYNC_I2S_MD,
17376+ },
17377+};
17378+
17379+static int pxa2xx_i2s_startup(struct snd_pcm_substream *substream)
17380+{
17381+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
17382+
17383+ if (!rtd->cpu_dai->active) {
17384+ SACR0 |= SACR0_RST;
17385+ SACR0 = 0;
17386+ }
17387+
17388+ return 0;
17389+}
17390+
17391+/* wait for I2S controller to be ready */
17392+static int pxa_i2s_wait(void)
17393+{
17394+ int i;
17395+
17396+ /* flush the Rx FIFO */
17397+ for(i = 0; i < 16; i++)
17398+ SADR;
17399+ return 0;
17400+}
17401+
17402+static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream,
17403+ struct snd_pcm_hw_params *params)
17404+{
17405+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
17406+
17407+ pxa_i2s.master = 0;
17408+ if (rtd->cpu_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CBS_CFS)
17409+ pxa_i2s.master = 1;
17410+
17411+ if (pxa_i2s.master && !extclk)
17412+ pxa_gpio_mode(gpio_bus[pxa_i2s.master].sys);
17413+
17414+ pxa_gpio_mode(gpio_bus[pxa_i2s.master].rx);
17415+ pxa_gpio_mode(gpio_bus[pxa_i2s.master].tx);
17416+ pxa_gpio_mode(gpio_bus[pxa_i2s.master].frm);
17417+ pxa_gpio_mode(gpio_bus[pxa_i2s.master].clk);
17418+ pxa_set_cken(CKEN8_I2S, 1);
17419+ pxa_i2s_wait();
17420+
17421+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
17422+ rtd->cpu_dai->dma_data = &pxa2xx_i2s_pcm_stereo_out;
17423+ else
17424+ rtd->cpu_dai->dma_data = &pxa2xx_i2s_pcm_stereo_in;
17425+
17426+ /* is port used by another stream */
17427+ if (!(SACR0 & SACR0_ENB)) {
17428+
17429+ SACR0 = 0;
17430+ SACR1 = 0;
17431+ if (pxa_i2s.master)
17432+ SACR0 |= SACR0_BCKD;
17433+
17434+ SACR0 |= SACR0_RFTH(14) | SACR0_TFTH(1);
17435+
17436+ if (rtd->cpu_dai->dai_runtime.fmt & SND_SOC_DAIFMT_LEFT_J)
17437+ SACR1 |= SACR1_AMSL;
17438+ }
17439+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
17440+ SAIMR |= SAIMR_TFS;
17441+ else
17442+ SAIMR |= SAIMR_RFS;
17443+
17444+ SADIV = rtd->cpu_dai->dai_runtime.priv;
17445+ return 0;
17446+}
17447+
17448+static int pxa2xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
17449+{
17450+ int ret = 0;
17451+
17452+ switch (cmd) {
17453+ case SNDRV_PCM_TRIGGER_START:
17454+ SACR0 |= SACR0_ENB;
17455+ break;
17456+ case SNDRV_PCM_TRIGGER_RESUME:
17457+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
17458+ case SNDRV_PCM_TRIGGER_STOP:
17459+ case SNDRV_PCM_TRIGGER_SUSPEND:
17460+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
17461+ break;
17462+ default:
17463+ ret = -EINVAL;
17464+ }
17465+
17466+ return ret;
17467+}
17468+
17469+static void pxa2xx_i2s_shutdown(struct snd_pcm_substream *substream)
17470+{
17471+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
17472+ SACR1 |= SACR1_DRPL;
17473+ SAIMR &= ~SAIMR_TFS;
17474+ } else {
17475+ SACR1 |= SACR1_DREC;
17476+ SAIMR &= ~SAIMR_RFS;
17477+ }
17478+
17479+ if (SACR1 & (SACR1_DREC | SACR1_DRPL)) {
17480+ SACR0 &= ~SACR0_ENB;
17481+ pxa_i2s_wait();
17482+ pxa_set_cken(CKEN8_I2S, 0);
17483+ }
17484+}
17485+
17486+#ifdef CONFIG_PM
17487+static int pxa2xx_i2s_suspend(struct platform_device *dev,
17488+ struct snd_soc_cpu_dai *dai)
17489+{
17490+ if (!dai->active)
17491+ return 0;
17492+
17493+ /* store registers */
17494+ pxa_i2s.sacr0 = SACR0;
17495+ pxa_i2s.sacr1 = SACR1;
17496+ pxa_i2s.saimr = SAIMR;
17497+ pxa_i2s.sadiv = SADIV;
17498+
17499+ /* deactivate link */
17500+ SACR0 &= ~SACR0_ENB;
17501+ pxa_i2s_wait();
17502+ return 0;
17503+}
17504+
17505+static int pxa2xx_i2s_resume(struct platform_device *pdev,
17506+ struct snd_soc_cpu_dai *dai)
17507+{
17508+ if (!dai->active)
17509+ return 0;
17510+
17511+ pxa_i2s_wait();
17512+
17513+ SACR0 = pxa_i2s.sacr0 &= ~SACR0_ENB;
17514+ SACR1 = pxa_i2s.sacr1;
17515+ SAIMR = pxa_i2s.saimr;
17516+ SADIV = pxa_i2s.sadiv;
17517+ SACR0 |= SACR0_ENB;
17518+
17519+ return 0;
17520+}
17521+
17522+#else
17523+#define pxa2xx_i2s_suspend NULL
17524+#define pxa2xx_i2s_resume NULL
17525+#endif
17526+
17527+/* pxa2xx I2S sysclock is always 256 FS */
17528+static unsigned int pxa_i2s_config_sysclk(struct snd_soc_cpu_dai *iface,
17529+ struct snd_soc_clock_info *info, unsigned int clk)
17530+{
17531+ return info->rate << 8;
17532+}
17533+
17534+struct snd_soc_cpu_dai pxa_i2s_dai = {
17535+ .name = "pxa2xx-i2s",
17536+ .id = 0,
17537+ .type = SND_SOC_DAI_I2S,
17538+ .suspend = pxa2xx_i2s_suspend,
17539+ .resume = pxa2xx_i2s_resume,
17540+ .config_sysclk = pxa_i2s_config_sysclk,
17541+ .playback = {
17542+ .channels_min = 2,
17543+ .channels_max = 2,},
17544+ .capture = {
17545+ .channels_min = 2,
17546+ .channels_max = 2,},
17547+ .ops = {
17548+ .startup = pxa2xx_i2s_startup,
17549+ .shutdown = pxa2xx_i2s_shutdown,
17550+ .trigger = pxa2xx_i2s_trigger,
17551+ .hw_params = pxa2xx_i2s_hw_params,},
17552+ .caps = {
17553+ .num_modes = ARRAY_SIZE(pxa2xx_i2s_modes),
17554+ .mode = pxa2xx_i2s_modes,},
17555+};
17556+
17557+EXPORT_SYMBOL_GPL(pxa_i2s_dai);
17558+
17559+/* Module information */
17560+MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
17561+MODULE_DESCRIPTION("pxa2xx I2S SoC Interface");
17562+MODULE_LICENSE("GPL");
17563Index: linux-2.6-pxa-new/sound/soc/pxa/pxa2xx-pcm.c
17564===================================================================
17565--- /dev/null
17566+++ linux-2.6-pxa-new/sound/soc/pxa/pxa2xx-pcm.c
17567@@ -0,0 +1,363 @@
17568+/*
17569+ * linux/sound/arm/pxa2xx-pcm.c -- ALSA PCM interface for the Intel PXA2xx chip
17570+ *
17571+ * Author: Nicolas Pitre
17572+ * Created: Nov 30, 2004
17573+ * Copyright: (C) 2004 MontaVista Software, Inc.
17574+ *
17575+ * This program is free software; you can redistribute it and/or modify
17576+ * it under the terms of the GNU General Public License version 2 as
17577+ * published by the Free Software Foundation.
17578+ */
17579+
17580+#include <linux/module.h>
17581+#include <linux/init.h>
17582+#include <linux/platform_device.h>
17583+#include <linux/slab.h>
17584+#include <linux/dma-mapping.h>
17585+
17586+#include <sound/driver.h>
17587+#include <sound/core.h>
17588+#include <sound/pcm.h>
17589+#include <sound/pcm_params.h>
17590+#include <sound/soc.h>
17591+
17592+#include <asm/dma.h>
17593+#include <asm/hardware.h>
17594+#include <asm/arch/pxa-regs.h>
17595+#include <asm/arch/audio.h>
17596+
17597+#include "pxa2xx-pcm.h"
17598+
17599+static const struct snd_pcm_hardware pxa2xx_pcm_hardware = {
17600+ .info = SNDRV_PCM_INFO_MMAP |
17601+ SNDRV_PCM_INFO_MMAP_VALID |
17602+ SNDRV_PCM_INFO_INTERLEAVED |
17603+ SNDRV_PCM_INFO_PAUSE |
17604+ SNDRV_PCM_INFO_RESUME,
17605+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
17606+ SNDRV_PCM_FMTBIT_S24_LE |
17607+ SNDRV_PCM_FMTBIT_S32_LE,
17608+ .period_bytes_min = 32,
17609+ .period_bytes_max = 8192 - 32,
17610+ .periods_min = 1,
17611+ .periods_max = PAGE_SIZE/sizeof(pxa_dma_desc),
17612+ .buffer_bytes_max = 128 * 1024,
17613+ .fifo_size = 32,
17614+};
17615+
17616+struct pxa2xx_runtime_data {
17617+ int dma_ch;
17618+ struct pxa2xx_pcm_dma_params *params;
17619+ pxa_dma_desc *dma_desc_array;
17620+ dma_addr_t dma_desc_array_phys;
17621+};
17622+
17623+static void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id)
17624+{
17625+ struct snd_pcm_substream *substream = dev_id;
17626+ struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
17627+ int dcsr;
17628+
17629+ dcsr = DCSR(dma_ch);
17630+ DCSR(dma_ch) = dcsr & ~DCSR_STOPIRQEN;
17631+
17632+ if (dcsr & DCSR_ENDINTR) {
17633+ snd_pcm_period_elapsed(substream);
17634+ } else {
17635+ printk( KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n",
17636+ prtd->params->name, dma_ch, dcsr );
17637+ }
17638+}
17639+
17640+static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
17641+ struct snd_pcm_hw_params *params)
17642+{
17643+ struct snd_pcm_runtime *runtime = substream->runtime;
17644+ struct pxa2xx_runtime_data *prtd = runtime->private_data;
17645+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
17646+ struct pxa2xx_pcm_dma_params *dma = rtd->cpu_dai->dma_data;
17647+ size_t totsize = params_buffer_bytes(params);
17648+ size_t period = params_period_bytes(params);
17649+ pxa_dma_desc *dma_desc;
17650+ dma_addr_t dma_buff_phys, next_desc_phys;
17651+ int ret;
17652+
17653+ /* this may get called several times by oss emulation
17654+ * with different params */
17655+ if (prtd->params == NULL) {
17656+ prtd->params = dma;
17657+ ret = pxa_request_dma(prtd->params->name, DMA_PRIO_LOW,
17658+ pxa2xx_pcm_dma_irq, substream);
17659+ if (ret < 0)
17660+ return ret;
17661+ prtd->dma_ch = ret;
17662+ } else if (prtd->params != dma) {
17663+ pxa_free_dma(prtd->dma_ch);
17664+ prtd->params = dma;
17665+ ret = pxa_request_dma(prtd->params->name, DMA_PRIO_LOW,
17666+ pxa2xx_pcm_dma_irq, substream);
17667+ if (ret < 0)
17668+ return ret;
17669+ prtd->dma_ch = ret;
17670+ }
17671+
17672+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
17673+ runtime->dma_bytes = totsize;
17674+
17675+ dma_desc = prtd->dma_desc_array;
17676+ next_desc_phys = prtd->dma_desc_array_phys;
17677+ dma_buff_phys = runtime->dma_addr;
17678+ do {
17679+ next_desc_phys += sizeof(pxa_dma_desc);
17680+ dma_desc->ddadr = next_desc_phys;
17681+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
17682+ dma_desc->dsadr = dma_buff_phys;
17683+ dma_desc->dtadr = prtd->params->dev_addr;
17684+ } else {
17685+ dma_desc->dsadr = prtd->params->dev_addr;
17686+ dma_desc->dtadr = dma_buff_phys;
17687+ }
17688+ if (period > totsize)
17689+ period = totsize;
17690+ dma_desc->dcmd = prtd->params->dcmd | period | DCMD_ENDIRQEN;
17691+ dma_desc++;
17692+ dma_buff_phys += period;
17693+ } while (totsize -= period);
17694+ dma_desc[-1].ddadr = prtd->dma_desc_array_phys;
17695+
17696+ return 0;
17697+}
17698+
17699+static int pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream)
17700+{
17701+ struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
17702+
17703+ if (prtd && prtd->params)
17704+ *prtd->params->drcmr = 0;
17705+
17706+ if (prtd->dma_ch) {
17707+ snd_pcm_set_runtime_buffer(substream, NULL);
17708+ pxa_free_dma(prtd->dma_ch);
17709+ prtd->dma_ch = 0;
17710+ }
17711+
17712+ return 0;
17713+}
17714+
17715+static int pxa2xx_pcm_prepare(struct snd_pcm_substream *substream)
17716+{
17717+ struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
17718+
17719+ DCSR(prtd->dma_ch) &= ~DCSR_RUN;
17720+ DCSR(prtd->dma_ch) = 0;
17721+ DCMD(prtd->dma_ch) = 0;
17722+ *prtd->params->drcmr = prtd->dma_ch | DRCMR_MAPVLD;
17723+
17724+ return 0;
17725+}
17726+
17727+static int pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
17728+{
17729+ struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
17730+ int ret = 0;
17731+
17732+ switch (cmd) {
17733+ case SNDRV_PCM_TRIGGER_START:
17734+ DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys;
17735+ DCSR(prtd->dma_ch) = DCSR_RUN;
17736+ break;
17737+
17738+ case SNDRV_PCM_TRIGGER_STOP:
17739+ case SNDRV_PCM_TRIGGER_SUSPEND:
17740+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
17741+ DCSR(prtd->dma_ch) &= ~DCSR_RUN;
17742+ break;
17743+
17744+ case SNDRV_PCM_TRIGGER_RESUME:
17745+ DCSR(prtd->dma_ch) |= DCSR_RUN;
17746+ break;
17747+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
17748+ DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys;
17749+ DCSR(prtd->dma_ch) |= DCSR_RUN;
17750+ break;
17751+
17752+ default:
17753+ ret = -EINVAL;
17754+ }
17755+
17756+ return ret;
17757+}
17758+
17759+static snd_pcm_uframes_t
17760+pxa2xx_pcm_pointer(struct snd_pcm_substream *substream)
17761+{
17762+ struct snd_pcm_runtime *runtime = substream->runtime;
17763+ struct pxa2xx_runtime_data *prtd = runtime->private_data;
17764+
17765+ dma_addr_t ptr = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
17766+ DSADR(prtd->dma_ch) : DTADR(prtd->dma_ch);
17767+ snd_pcm_uframes_t x = bytes_to_frames(runtime, ptr - runtime->dma_addr);
17768+
17769+ if (x == runtime->buffer_size)
17770+ x = 0;
17771+ return x;
17772+}
17773+
17774+static int pxa2xx_pcm_open(struct snd_pcm_substream *substream)
17775+{
17776+ struct snd_pcm_runtime *runtime = substream->runtime;
17777+ struct pxa2xx_runtime_data *prtd;
17778+ int ret;
17779+
17780+ snd_soc_set_runtime_hwparams(substream, &pxa2xx_pcm_hardware);
17781+
17782+ /*
17783+ * For mysterious reasons (and despite what the manual says)
17784+ * playback samples are lost if the DMA count is not a multiple
17785+ * of the DMA burst size. Let's add a rule to enforce that.
17786+ */
17787+ ret = snd_pcm_hw_constraint_step(runtime, 0,
17788+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
17789+ if (ret)
17790+ goto out;
17791+
17792+ ret = snd_pcm_hw_constraint_step(runtime, 0,
17793+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
17794+ if (ret)
17795+ goto out;
17796+
17797+ prtd = kzalloc(sizeof(struct pxa2xx_runtime_data), GFP_KERNEL);
17798+ if (prtd == NULL) {
17799+ ret = -ENOMEM;
17800+ goto out;
17801+ }
17802+
17803+ prtd->dma_desc_array =
17804+ dma_alloc_writecombine(substream->pcm->card->dev, PAGE_SIZE,
17805+ &prtd->dma_desc_array_phys, GFP_KERNEL);
17806+ if (!prtd->dma_desc_array) {
17807+ ret = -ENOMEM;
17808+ goto err1;
17809+ }
17810+
17811+ runtime->private_data = prtd;
17812+ return 0;
17813+
17814+ err1:
17815+ kfree(prtd);
17816+ out:
17817+ return ret;
17818+}
17819+
17820+static int pxa2xx_pcm_close(struct snd_pcm_substream *substream)
17821+{
17822+ struct snd_pcm_runtime *runtime = substream->runtime;
17823+ struct pxa2xx_runtime_data *prtd = runtime->private_data;
17824+
17825+ dma_free_writecombine(substream->pcm->card->dev, PAGE_SIZE,
17826+ prtd->dma_desc_array, prtd->dma_desc_array_phys);
17827+ kfree(prtd);
17828+ return 0;
17829+}
17830+
17831+static int pxa2xx_pcm_mmap(struct snd_pcm_substream *substream,
17832+ struct vm_area_struct *vma)
17833+{
17834+ struct snd_pcm_runtime *runtime = substream->runtime;
17835+ return dma_mmap_writecombine(substream->pcm->card->dev, vma,
17836+ runtime->dma_area,
17837+ runtime->dma_addr,
17838+ runtime->dma_bytes);
17839+}
17840+
17841+struct snd_pcm_ops pxa2xx_pcm_ops = {
17842+ .open = pxa2xx_pcm_open,
17843+ .close = pxa2xx_pcm_close,
17844+ .ioctl = snd_pcm_lib_ioctl,
17845+ .hw_params = pxa2xx_pcm_hw_params,
17846+ .hw_free = pxa2xx_pcm_hw_free,
17847+ .prepare = pxa2xx_pcm_prepare,
17848+ .trigger = pxa2xx_pcm_trigger,
17849+ .pointer = pxa2xx_pcm_pointer,
17850+ .mmap = pxa2xx_pcm_mmap,
17851+};
17852+
17853+static int pxa2xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
17854+{
17855+ struct snd_pcm_substream *substream = pcm->streams[stream].substream;
17856+ struct snd_dma_buffer *buf = &substream->dma_buffer;
17857+ size_t size = pxa2xx_pcm_hardware.buffer_bytes_max;
17858+ buf->dev.type = SNDRV_DMA_TYPE_DEV;
17859+ buf->dev.dev = pcm->card->dev;
17860+ buf->private_data = NULL;
17861+ buf->area = dma_alloc_writecombine(pcm->card->dev, size,
17862+ &buf->addr, GFP_KERNEL);
17863+ if (!buf->area)
17864+ return -ENOMEM;
17865+ buf->bytes = size;
17866+ return 0;
17867+}
17868+
17869+static void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
17870+{
17871+ struct snd_pcm_substream *substream;
17872+ struct snd_dma_buffer *buf;
17873+ int stream;
17874+
17875+ for (stream = 0; stream < 2; stream++) {
17876+ substream = pcm->streams[stream].substream;
17877+ if (!substream)
17878+ continue;
17879+
17880+ buf = &substream->dma_buffer;
17881+ if (!buf->area)
17882+ continue;
17883+
17884+ dma_free_writecombine(pcm->card->dev, buf->bytes,
17885+ buf->area, buf->addr);
17886+ buf->area = NULL;
17887+ }
17888+}
17889+
17890+static u64 pxa2xx_pcm_dmamask = DMA_32BIT_MASK;
17891+
17892+int pxa2xx_pcm_new(struct snd_card *card, struct snd_soc_codec_dai *dai,
17893+ struct snd_pcm *pcm)
17894+{
17895+ int ret = 0;
17896+
17897+ if (!card->dev->dma_mask)
17898+ card->dev->dma_mask = &pxa2xx_pcm_dmamask;
17899+ if (!card->dev->coherent_dma_mask)
17900+ card->dev->coherent_dma_mask = DMA_32BIT_MASK;
17901+
17902+ if (dai->playback.channels_min) {
17903+ ret = pxa2xx_pcm_preallocate_dma_buffer(pcm,
17904+ SNDRV_PCM_STREAM_PLAYBACK);
17905+ if (ret)
17906+ goto out;
17907+ }
17908+
17909+ if (dai->capture.channels_min) {
17910+ ret = pxa2xx_pcm_preallocate_dma_buffer(pcm,
17911+ SNDRV_PCM_STREAM_CAPTURE);
17912+ if (ret)
17913+ goto out;
17914+ }
17915+ out:
17916+ return ret;
17917+}
17918+
17919+struct snd_soc_platform pxa2xx_soc_platform = {
17920+ .name = "pxa2xx-audio",
17921+ .pcm_ops = &pxa2xx_pcm_ops,
17922+ .pcm_new = pxa2xx_pcm_new,
17923+ .pcm_free = pxa2xx_pcm_free_dma_buffers,
17924+};
17925+
17926+EXPORT_SYMBOL_GPL(pxa2xx_soc_platform);
17927+
17928+MODULE_AUTHOR("Nicolas Pitre");
17929+MODULE_DESCRIPTION("Intel PXA2xx PCM DMA module");
17930+MODULE_LICENSE("GPL");
17931Index: linux-2.6-pxa-new/sound/soc/pxa/pxa2xx-pcm.h
17932===================================================================
17933--- /dev/null
17934+++ linux-2.6-pxa-new/sound/soc/pxa/pxa2xx-pcm.h
17935@@ -0,0 +1,48 @@
17936+/*
17937+ * linux/sound/arm/pxa2xx-pcm.h -- ALSA PCM interface for the Intel PXA2xx chip
17938+ *
17939+ * Author: Nicolas Pitre
17940+ * Created: Nov 30, 2004
17941+ * Copyright: MontaVista Software, Inc.
17942+ *
17943+ * This program is free software; you can redistribute it and/or modify
17944+ * it under the terms of the GNU General Public License version 2 as
17945+ * published by the Free Software Foundation.
17946+ */
17947+
17948+#ifndef _PXA2XX_PCM_H
17949+#define _PXA2XX_PCM_H
17950+
17951+struct pxa2xx_pcm_dma_params {
17952+ char *name; /* stream identifier */
17953+ u32 dcmd; /* DMA descriptor dcmd field */
17954+ volatile u32 *drcmr; /* the DMA request channel to use */
17955+ u32 dev_addr; /* device physical address for DMA */
17956+};
17957+
17958+struct pxa2xx_gpio {
17959+ u32 sys;
17960+ u32 rx;
17961+ u32 tx;
17962+ u32 clk;
17963+ u32 frm;
17964+};
17965+
17966+/* pxa2xx DAI ID's */
17967+#define PXA2XX_DAI_AC97_HIFI 0
17968+#define PXA2XX_DAI_AC97_AUX 1
17969+#define PXA2XX_DAI_AC97_MIC 2
17970+#define PXA2XX_DAI_I2S 0
17971+#define PXA2XX_DAI_SSP1 0
17972+#define PXA2XX_DAI_SSP2 1
17973+#define PXA2XX_DAI_SSP3 2
17974+
17975+extern struct snd_soc_cpu_dai pxa_ac97_dai[3];
17976+extern struct snd_soc_cpu_dai pxa_i2s_dai;
17977+extern struct snd_soc_cpu_dai pxa_ssp_dai[3];
17978+
17979+/* platform data */
17980+extern struct snd_soc_platform pxa2xx_soc_platform;
17981+extern struct snd_ac97_bus_ops pxa2xx_ac97_ops;
17982+
17983+#endif
17984Index: linux-2.6-pxa-new/sound/soc/pxa/pxa2xx-ssp.c
17985===================================================================
17986--- /dev/null
17987+++ linux-2.6-pxa-new/sound/soc/pxa/pxa2xx-ssp.c
17988@@ -0,0 +1,767 @@
17989+/*
17990+ * pxa2xx-ssp.c -- ALSA Soc Audio Layer
17991+ *
17992+ * Copyright 2005 Wolfson Microelectronics PLC.
17993+ * Author: Liam Girdwood
17994+ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
17995+ *
17996+ * This program is free software; you can redistribute it and/or modify it
17997+ * under the terms of the GNU General Public License as published by the
17998+ * Free Software Foundation; either version 2 of the License, or (at your
17999+ * option) any later version.
18000+ *
18001+ * Revision history
18002+ * 12th Aug 2005 Initial version.
18003+ *
18004+ * TODO:
18005+ * o Fix master mode (bug)
18006+ * o Fix resume (bug)
18007+ * o Add support for other clocks
18008+ * o Test network mode for > 16bit sample size
18009+ */
18010+
18011+#include <linux/init.h>
18012+#include <linux/module.h>
18013+#include <linux/platform_device.h>
18014+
18015+#include <sound/driver.h>
18016+#include <sound/core.h>
18017+#include <sound/pcm.h>
18018+#include <sound/initval.h>
18019+#include <sound/soc.h>
18020+
18021+#include <asm/hardware.h>
18022+#include <asm/arch/pxa-regs.h>
18023+#include <asm/arch/audio.h>
18024+#include <asm/arch/ssp.h>
18025+
18026+#include "pxa2xx-pcm.h"
18027+
18028+/*
18029+ * SSP sysclock frequency in Hz
18030+ * Neither default pxa2xx PLL clocks are good for audio, hence pxa27x
18031+ * has audio clock. I would recommend using the pxa27x audio clock or an
18032+ * external clock or making the codec master to gurantee better sample rates.
18033+ */
18034+#ifdef CONFIG_PXA27x
18035+static int sysclk[3] = {13000000, 13000000, 13000000};
18036+#else
18037+static int sysclk[3] = {1843200, 1843200, 1843200};
18038+#endif
18039+module_param_array(sysclk, int, NULL, 0);
18040+MODULE_PARM_DESC(sysclk, "sysclk frequency in Hz");
18041+
18042+/*
18043+ * SSP sysclock source.
18044+ * sysclk is ignored if audio clock is used
18045+ */
18046+#ifdef CONFIG_PXA27x
18047+static int clksrc[3] = {0, 0, 0};
18048+#else
18049+static int clksrc[3] = {0, 0, 0};
18050+#endif
18051+module_param_array(clksrc, int, NULL, 0);
18052+MODULE_PARM_DESC(clksrc,
18053+ "sysclk source, 0 = internal PLL, 1 = ext, 2 = network, 3 = audio clock");
18054+
18055+/*
18056+ * SSP GPIO's
18057+ */
18058+#define GPIO26_SSP1RX_MD (26 | GPIO_ALT_FN_1_IN)
18059+#define GPIO25_SSP1TX_MD (25 | GPIO_ALT_FN_2_OUT)
18060+#define GPIO23_SSP1CLKS_MD (23 | GPIO_ALT_FN_2_IN)
18061+#define GPIO24_SSP1FRMS_MD (24 | GPIO_ALT_FN_2_IN)
18062+#define GPIO23_SSP1CLKM_MD (23 | GPIO_ALT_FN_2_OUT)
18063+#define GPIO24_SSP1FRMM_MD (24 | GPIO_ALT_FN_2_OUT)
18064+
18065+#define GPIO11_SSP2RX_MD (11 | GPIO_ALT_FN_2_IN)
18066+#define GPIO13_SSP2TX_MD (13 | GPIO_ALT_FN_1_OUT)
18067+#define GPIO22_SSP2CLKS_MD (22 | GPIO_ALT_FN_3_IN)
18068+#define GPIO88_SSP2FRMS_MD (88 | GPIO_ALT_FN_3_IN)
18069+#define GPIO22_SSP2CLKM_MD (22 | GPIO_ALT_FN_3_OUT)
18070+#define GPIO88_SSP2FRMM_MD (88 | GPIO_ALT_FN_3_OUT)
18071+
18072+#define GPIO82_SSP3RX_MD (82 | GPIO_ALT_FN_1_IN)
18073+#define GPIO81_SSP3TX_MD (81 | GPIO_ALT_FN_1_OUT)
18074+#define GPIO84_SSP3CLKS_MD (84 | GPIO_ALT_FN_1_IN)
18075+#define GPIO83_SSP3FRMS_MD (83 | GPIO_ALT_FN_1_IN)
18076+#define GPIO84_SSP3CLKM_MD (84 | GPIO_ALT_FN_1_OUT)
18077+#define GPIO83_SSP3FRMM_MD (83 | GPIO_ALT_FN_1_OUT)
18078+
18079+#define PXA_SSP_MDAIFMT \
18080+ (SND_SOC_DAIFMT_DSP_B |SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_CBS_CFS | SND_SOC_DAIFMT_CBM_CFS | \
18081+ SND_SOC_DAIFMT_CBS_CFM | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_NB_IF)
18082+
18083+#define PXA_SSP_SDAIFMT \
18084+ (SND_SOC_DAIFMT_DSP_B |SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_CBM_CFS | \
18085+ SND_SOC_DAIFMT_CBS_CFM | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_NB_IF)
18086+
18087+#define PXA_SSP_DIR \
18088+ (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
18089+
18090+#define PXA_SSP_RATES \
18091+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
18092+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
18093+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | \
18094+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000)
18095+
18096+#define PXA_SSP_BITS \
18097+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
18098+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
18099+
18100+/*
18101+ * SSP modes
18102+ */
18103+static struct snd_soc_dai_mode pxa2xx_ssp_modes[] = {
18104+ /* port slave clk & frame modes */
18105+ {
18106+ .fmt = PXA_SSP_SDAIFMT,
18107+ .pcmfmt = PXA_SSP_BITS,
18108+ .pcmrate = PXA_SSP_RATES,
18109+ .pcmdir = PXA_SSP_DIR,
18110+ .fs = SND_SOC_FS_ALL,
18111+ .bfs = SND_SOC_FSB_ALL,
18112+ },
18113+
18114+ /* port master clk & frame modes */
18115+#ifdef CONFIG_PXA27x
18116+ {
18117+ .fmt = PXA_SSP_MDAIFMT,
18118+ .pcmfmt = PXA_SSP_BITS,
18119+ .pcmrate = SNDRV_PCM_RATE_8000,
18120+ .pcmdir = PXA_SSP_DIR,
18121+ .flags = SND_SOC_DAI_BFS_RCW,
18122+ .fs = 256,
18123+ .bfs = SND_SOC_FSBW(1),
18124+ },
18125+ {
18126+ .fmt = PXA_SSP_MDAIFMT,
18127+ .pcmfmt = PXA_SSP_BITS,
18128+ .pcmrate = SNDRV_PCM_RATE_11025,
18129+ .pcmdir = PXA_SSP_DIR,
18130+ .flags = SND_SOC_DAI_BFS_RCW,
18131+ .fs = 256,
18132+ .bfs = SND_SOC_FSBW(1),
18133+ },
18134+ {
18135+ .fmt = PXA_SSP_MDAIFMT,
18136+ .pcmfmt = PXA_SSP_BITS,
18137+ .pcmrate = SNDRV_PCM_RATE_16000,
18138+ .pcmdir = PXA_SSP_DIR,
18139+ .flags = SND_SOC_DAI_BFS_RCW,
18140+ .fs = 256,
18141+ .bfs = SND_SOC_FSBW(1),
18142+ },
18143+ {
18144+ .fmt = PXA_SSP_MDAIFMT,
18145+ .pcmfmt = PXA_SSP_BITS,
18146+ .pcmrate = SNDRV_PCM_RATE_22050,
18147+ .pcmdir = PXA_SSP_DIR,
18148+ .flags = SND_SOC_DAI_BFS_RCW,
18149+ .fs = 256,
18150+ .bfs = SND_SOC_FSBW(1),
18151+ },
18152+ {
18153+ .fmt = PXA_SSP_MDAIFMT,
18154+ .pcmfmt = PXA_SSP_BITS,
18155+ .pcmrate = SNDRV_PCM_RATE_32000,
18156+ .pcmdir = PXA_SSP_DIR,
18157+ .flags = SND_SOC_DAI_BFS_RCW,
18158+ .fs = 256,
18159+ .bfs = SND_SOC_FSBW(1),
18160+ },
18161+ {
18162+ .fmt = PXA_SSP_MDAIFMT,
18163+ .pcmfmt = PXA_SSP_BITS,
18164+ .pcmrate = SNDRV_PCM_RATE_44100,
18165+ .pcmdir = PXA_SSP_DIR,
18166+ .flags = SND_SOC_DAI_BFS_RCW,
18167+ .fs = 256,
18168+ .bfs = SND_SOC_FSBW(1),
18169+ },
18170+ {
18171+ .fmt = PXA_SSP_MDAIFMT,
18172+ .pcmfmt = PXA_SSP_BITS,
18173+ .pcmrate = SNDRV_PCM_RATE_48000,
18174+ .pcmdir = PXA_SSP_DIR,
18175+ .flags = SND_SOC_DAI_BFS_RCW,
18176+ .fs = 256,
18177+ .bfs = SND_SOC_FSBW(1),
18178+ },
18179+ {
18180+ .fmt = PXA_SSP_MDAIFMT,
18181+ .pcmfmt = PXA_SSP_BITS,
18182+ .pcmrate = SNDRV_PCM_RATE_88200,
18183+ .pcmdir = PXA_SSP_DIR,
18184+ .flags = SND_SOC_DAI_BFS_RCW,
18185+ .fs = 128,
18186+ .bfs = SND_SOC_FSBW(1),
18187+ },
18188+ {
18189+ .fmt = PXA_SSP_MDAIFMT,
18190+ .pcmfmt = PXA_SSP_BITS,
18191+ .pcmrate = SNDRV_PCM_RATE_96000,
18192+ .pcmdir = PXA_SSP_DIR,
18193+ .flags = SND_SOC_DAI_BFS_RCW,
18194+ .fs = 128,
18195+ .bfs = SND_SOC_FSBW(1),
18196+ },
18197+#endif
18198+};
18199+
18200+static struct ssp_dev ssp[3];
18201+#ifdef CONFIG_PM
18202+static struct ssp_state ssp_state[3];
18203+#endif
18204+
18205+static struct pxa2xx_pcm_dma_params pxa2xx_ssp1_pcm_mono_out = {
18206+ .name = "SSP1 PCM Mono out",
18207+ .dev_addr = __PREG(SSDR_P1),
18208+ .drcmr = &DRCMRTXSSDR,
18209+ .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
18210+ DCMD_BURST16 | DCMD_WIDTH2,
18211+};
18212+
18213+static struct pxa2xx_pcm_dma_params pxa2xx_ssp1_pcm_mono_in = {
18214+ .name = "SSP1 PCM Mono in",
18215+ .dev_addr = __PREG(SSDR_P1),
18216+ .drcmr = &DRCMRRXSSDR,
18217+ .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
18218+ DCMD_BURST16 | DCMD_WIDTH2,
18219+};
18220+
18221+static struct pxa2xx_pcm_dma_params pxa2xx_ssp1_pcm_stereo_out = {
18222+ .name = "SSP1 PCM Stereo out",
18223+ .dev_addr = __PREG(SSDR_P1),
18224+ .drcmr = &DRCMRTXSSDR,
18225+ .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
18226+ DCMD_BURST16 | DCMD_WIDTH4,
18227+};
18228+
18229+static struct pxa2xx_pcm_dma_params pxa2xx_ssp1_pcm_stereo_in = {
18230+ .name = "SSP1 PCM Stereo in",
18231+ .dev_addr = __PREG(SSDR_P1),
18232+ .drcmr = &DRCMRRXSSDR,
18233+ .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
18234+ DCMD_BURST16 | DCMD_WIDTH4,
18235+};
18236+
18237+static struct pxa2xx_pcm_dma_params pxa2xx_ssp2_pcm_mono_out = {
18238+ .name = "SSP2 PCM Mono out",
18239+ .dev_addr = __PREG(SSDR_P2),
18240+ .drcmr = &DRCMRTXSS2DR,
18241+ .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
18242+ DCMD_BURST16 | DCMD_WIDTH2,
18243+};
18244+
18245+static struct pxa2xx_pcm_dma_params pxa2xx_ssp2_pcm_mono_in = {
18246+ .name = "SSP2 PCM Mono in",
18247+ .dev_addr = __PREG(SSDR_P2),
18248+ .drcmr = &DRCMRRXSS2DR,
18249+ .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
18250+ DCMD_BURST16 | DCMD_WIDTH2,
18251+};
18252+
18253+static struct pxa2xx_pcm_dma_params pxa2xx_ssp2_pcm_stereo_out = {
18254+ .name = "SSP2 PCM Stereo out",
18255+ .dev_addr = __PREG(SSDR_P2),
18256+ .drcmr = &DRCMRTXSS2DR,
18257+ .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
18258+ DCMD_BURST16 | DCMD_WIDTH4,
18259+};
18260+
18261+static struct pxa2xx_pcm_dma_params pxa2xx_ssp2_pcm_stereo_in = {
18262+ .name = "SSP2 PCM Stereo in",
18263+ .dev_addr = __PREG(SSDR_P2),
18264+ .drcmr = &DRCMRRXSS2DR,
18265+ .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
18266+ DCMD_BURST16 | DCMD_WIDTH4,
18267+};
18268+
18269+static struct pxa2xx_pcm_dma_params pxa2xx_ssp3_pcm_mono_out = {
18270+ .name = "SSP3 PCM Mono out",
18271+ .dev_addr = __PREG(SSDR_P3),
18272+ .drcmr = &DRCMRTXSS3DR,
18273+ .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
18274+ DCMD_BURST16 | DCMD_WIDTH2,
18275+};
18276+
18277+static struct pxa2xx_pcm_dma_params pxa2xx_ssp3_pcm_mono_in = {
18278+ .name = "SSP3 PCM Mono in",
18279+ .dev_addr = __PREG(SSDR_P3),
18280+ .drcmr = &DRCMRRXSS3DR,
18281+ .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
18282+ DCMD_BURST16 | DCMD_WIDTH2,
18283+};
18284+
18285+static struct pxa2xx_pcm_dma_params pxa2xx_ssp3_pcm_stereo_out = {
18286+ .name = "SSP3 PCM Stereo out",
18287+ .dev_addr = __PREG(SSDR_P3),
18288+ .drcmr = &DRCMRTXSS3DR,
18289+ .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
18290+ DCMD_BURST16 | DCMD_WIDTH4,
18291+};
18292+
18293+static struct pxa2xx_pcm_dma_params pxa2xx_ssp3_pcm_stereo_in = {
18294+ .name = "SSP3 PCM Stereo in",
18295+ .dev_addr = __PREG(SSDR_P3),
18296+ .drcmr = &DRCMRRXSS3DR,
18297+ .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
18298+ DCMD_BURST16 | DCMD_WIDTH4,
18299+};
18300+
18301+static struct pxa2xx_pcm_dma_params *ssp_dma_params[3][4] = {
18302+ {&pxa2xx_ssp1_pcm_mono_out, &pxa2xx_ssp1_pcm_mono_in,
18303+ &pxa2xx_ssp1_pcm_stereo_out,&pxa2xx_ssp1_pcm_stereo_in,},
18304+ {&pxa2xx_ssp2_pcm_mono_out, &pxa2xx_ssp2_pcm_mono_in,
18305+ &pxa2xx_ssp2_pcm_stereo_out, &pxa2xx_ssp2_pcm_stereo_in,},
18306+ {&pxa2xx_ssp3_pcm_mono_out, &pxa2xx_ssp3_pcm_mono_in,
18307+ &pxa2xx_ssp3_pcm_stereo_out,&pxa2xx_ssp3_pcm_stereo_in,},
18308+};
18309+
18310+static struct pxa2xx_gpio ssp_gpios[3][4] = {
18311+ {{ /* SSP1 SND_SOC_DAIFMT_CBM_CFM */
18312+ .rx = GPIO26_SSP1RX_MD,
18313+ .tx = GPIO25_SSP1TX_MD,
18314+ .clk = (23 | GPIO_ALT_FN_2_IN),
18315+ .frm = (24 | GPIO_ALT_FN_2_IN),
18316+ },
18317+ { /* SSP1 SND_SOC_DAIFMT_CBS_CFS */
18318+ .rx = GPIO26_SSP1RX_MD,
18319+ .tx = GPIO25_SSP1TX_MD,
18320+ .clk = (23 | GPIO_ALT_FN_2_OUT),
18321+ .frm = (24 | GPIO_ALT_FN_2_OUT),
18322+ },
18323+ { /* SSP1 SND_SOC_DAIFMT_CBS_CFM */
18324+ .rx = GPIO26_SSP1RX_MD,
18325+ .tx = GPIO25_SSP1TX_MD,
18326+ .clk = (23 | GPIO_ALT_FN_2_OUT),
18327+ .frm = (24 | GPIO_ALT_FN_2_IN),
18328+ },
18329+ { /* SSP1 SND_SOC_DAIFMT_CBM_CFS */
18330+ .rx = GPIO26_SSP1RX_MD,
18331+ .tx = GPIO25_SSP1TX_MD,
18332+ .clk = (23 | GPIO_ALT_FN_2_IN),
18333+ .frm = (24 | GPIO_ALT_FN_2_OUT),
18334+ }},
18335+ {{ /* SSP2 SND_SOC_DAIFMT_CBM_CFM */
18336+ .rx = GPIO11_SSP2RX_MD,
18337+ .tx = GPIO13_SSP2TX_MD,
18338+ .clk = (22 | GPIO_ALT_FN_3_IN),
18339+ .frm = (88 | GPIO_ALT_FN_3_IN),
18340+ },
18341+ { /* SSP2 SND_SOC_DAIFMT_CBS_CFS */
18342+ .rx = GPIO11_SSP2RX_MD,
18343+ .tx = GPIO13_SSP2TX_MD,
18344+ .clk = (22 | GPIO_ALT_FN_3_OUT),
18345+ .frm = (88 | GPIO_ALT_FN_3_OUT),
18346+ },
18347+ { /* SSP2 SND_SOC_DAIFMT_CBS_CFM */
18348+ .rx = GPIO11_SSP2RX_MD,
18349+ .tx = GPIO13_SSP2TX_MD,
18350+ .clk = (22 | GPIO_ALT_FN_3_OUT),
18351+ .frm = (88 | GPIO_ALT_FN_3_IN),
18352+ },
18353+ { /* SSP2 SND_SOC_DAIFMT_CBM_CFS */
18354+ .rx = GPIO11_SSP2RX_MD,
18355+ .tx = GPIO13_SSP2TX_MD,
18356+ .clk = (22 | GPIO_ALT_FN_3_IN),
18357+ .frm = (88 | GPIO_ALT_FN_3_OUT),
18358+ }},
18359+ {{ /* SSP3 SND_SOC_DAIFMT_CBM_CFM */
18360+ .rx = GPIO82_SSP3RX_MD,
18361+ .tx = GPIO81_SSP3TX_MD,
18362+ .clk = (84 | GPIO_ALT_FN_3_IN),
18363+ .frm = (83 | GPIO_ALT_FN_3_IN),
18364+ },
18365+ { /* SSP3 SND_SOC_DAIFMT_CBS_CFS */
18366+ .rx = GPIO82_SSP3RX_MD,
18367+ .tx = GPIO81_SSP3TX_MD,
18368+ .clk = (84 | GPIO_ALT_FN_3_OUT),
18369+ .frm = (83 | GPIO_ALT_FN_3_OUT),
18370+ },
18371+ { /* SSP3 SND_SOC_DAIFMT_CBS_CFM */
18372+ .rx = GPIO82_SSP3RX_MD,
18373+ .tx = GPIO81_SSP3TX_MD,
18374+ .clk = (84 | GPIO_ALT_FN_3_OUT),
18375+ .frm = (83 | GPIO_ALT_FN_3_IN),
18376+ },
18377+ { /* SSP3 SND_SOC_DAIFMT_CBM_CFS */
18378+ .rx = GPIO82_SSP3RX_MD,
18379+ .tx = GPIO81_SSP3TX_MD,
18380+ .clk = (84 | GPIO_ALT_FN_3_IN),
18381+ .frm = (83 | GPIO_ALT_FN_3_OUT),
18382+ }},
18383+};
18384+
18385+static int pxa2xx_ssp_startup(struct snd_pcm_substream *substream)
18386+{
18387+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
18388+ int ret = 0;
18389+
18390+ if (!rtd->cpu_dai->active) {
18391+ ret = ssp_init (&ssp[rtd->cpu_dai->id], rtd->cpu_dai->id + 1,
18392+ SSP_NO_IRQ);
18393+ if (ret < 0)
18394+ return ret;
18395+ ssp_disable(&ssp[rtd->cpu_dai->id]);
18396+ }
18397+ return ret;
18398+}
18399+
18400+static void pxa2xx_ssp_shutdown(struct snd_pcm_substream *substream)
18401+{
18402+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
18403+
18404+ if (!rtd->cpu_dai->active) {
18405+ ssp_disable(&ssp[rtd->cpu_dai->id]);
18406+ ssp_exit(&ssp[rtd->cpu_dai->id]);
18407+ }
18408+}
18409+
18410+#ifdef CONFIG_PM
18411+
18412+#if defined (CONFIG_PXA27x)
18413+static int cken[3] = {CKEN23_SSP1, CKEN3_SSP2, CKEN4_SSP3};
18414+#else
18415+static int cken[3] = {CKEN3_SSP, CKEN9_NSSP, CKEN10_ASSP};
18416+#endif
18417+
18418+static int pxa2xx_ssp_suspend(struct platform_device *pdev,
18419+ struct snd_soc_cpu_dai *dai)
18420+{
18421+ if (!dai->active)
18422+ return 0;
18423+
18424+ ssp_save_state(&ssp[dai->id], &ssp_state[dai->id]);
18425+ pxa_set_cken(cken[dai->id], 0);
18426+ return 0;
18427+}
18428+
18429+static int pxa2xx_ssp_resume(struct platform_device *pdev,
18430+ struct snd_soc_cpu_dai *dai)
18431+{
18432+ if (!dai->active)
18433+ return 0;
18434+
18435+ pxa_set_cken(cken[dai->id], 1);
18436+ ssp_restore_state(&ssp[dai->id], &ssp_state[dai->id]);
18437+ ssp_enable(&ssp[dai->id]);
18438+
18439+ return 0;
18440+}
18441+
18442+#else
18443+#define pxa2xx_ssp_suspend NULL
18444+#define pxa2xx_ssp_resume NULL
18445+#endif
18446+
18447+/* todo - check clk source and PLL before returning clock rate */
18448+static unsigned int pxa_ssp_config_sysclk(struct snd_soc_cpu_dai *dai,
18449+ struct snd_soc_clock_info *info, unsigned int clk)
18450+{
18451+ /* audio clock ? (divide by 1) */
18452+ if (clksrc[dai->id] == 3) {
18453+ switch(info->rate){
18454+ case 8000:
18455+ case 16000:
18456+ case 32000:
18457+ case 48000:
18458+ case 96000:
18459+ return 12288000;
18460+ break;
18461+ case 11025:
18462+ case 22050:
18463+ case 44100:
18464+ case 88200:
18465+ return 11289600;
18466+ break;
18467+ }
18468+ }
18469+
18470+ /* pll */
18471+ return sysclk[dai->id];
18472+}
18473+
18474+#ifdef CONFIG_PXA27x
18475+static u32 pxa27x_set_audio_clk(unsigned int rate, unsigned int fs)
18476+{
18477+ u32 aclk = 0, div = 0;
18478+
18479+ if (rate == 0 || fs == 0)
18480+ return 0;
18481+
18482+ switch(rate){
18483+ case 8000:
18484+ case 16000:
18485+ case 32000:
18486+ case 48000:
18487+ case 96000:
18488+ aclk = 0x2 << 4;
18489+ div = 12288000 / (rate * fs);
18490+ break;
18491+ case 11025:
18492+ case 22050:
18493+ case 44100:
18494+ case 88200:
18495+ aclk = 0x1 << 4;
18496+ div = 11289600 / (rate * fs);
18497+ break;
18498+ }
18499+
18500+ aclk |= ffs(div) - 1;
18501+ return aclk;
18502+}
18503+#endif
18504+
18505+static inline int get_scr(int srate, int id)
18506+{
18507+ if (srate == 0)
18508+ return 0;
18509+ return (sysclk[id] / srate) - 1;
18510+}
18511+
18512+static int pxa2xx_ssp_hw_params(struct snd_pcm_substream *substream,
18513+ struct snd_pcm_hw_params *params)
18514+{
18515+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
18516+ int fmt = 0, dma = 0, fs, chn = params_channels(params);
18517+ u32 ssp_mode = 0, ssp_setup = 0, psp_mode = 0, rate = 0;
18518+
18519+ fs = rtd->cpu_dai->dai_runtime.fs;
18520+
18521+ /* select correct DMA params */
18522+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
18523+ dma = 1;
18524+ if (chn == 2 || rtd->cpu_dai->dai_runtime.pcmfmt != PXA_SSP_BITS)
18525+ dma += 2;
18526+ rtd->cpu_dai->dma_data = ssp_dma_params[rtd->cpu_dai->id][dma];
18527+
18528+ /* is port used by another stream */
18529+ if (SSCR0 & SSCR0_SSE)
18530+ return 0;
18531+
18532+ /* bit size */
18533+ switch(rtd->cpu_dai->dai_runtime.pcmfmt) {
18534+ case SNDRV_PCM_FMTBIT_S16_LE:
18535+ ssp_mode |=SSCR0_DataSize(16);
18536+ break;
18537+ case SNDRV_PCM_FMTBIT_S24_LE:
18538+ ssp_mode |=(SSCR0_EDSS | SSCR0_DataSize(8));
18539+ /* use network mode for stereo samples > 16 bits */
18540+ if (chn == 2) {
18541+ ssp_mode |= (SSCR0_MOD | SSCR0_SlotsPerFrm(2) << 24);
18542+ /* active slots 0,1 */
18543+ SSTSA_P(rtd->cpu_dai->id +1) = 0x3;
18544+ SSRSA_P(rtd->cpu_dai->id +1) = 0x3;
18545+ }
18546+ break;
18547+ case SNDRV_PCM_FMTBIT_S32_LE:
18548+ ssp_mode |= (SSCR0_EDSS | SSCR0_DataSize(16));
18549+ /* use network mode for stereo samples > 16 bits */
18550+ if (chn == 2) {
18551+ ssp_mode |= (SSCR0_MOD | SSCR0_SlotsPerFrm(2) << 24);
18552+ /* active slots 0,1 */
18553+ SSTSA_P(rtd->cpu_dai->id +1) = 0x3;
18554+ SSRSA_P(rtd->cpu_dai->id +1) = 0x3;
18555+ }
18556+ break;
18557+ }
18558+
18559+ ssp_mode |= SSCR0_PSP;
18560+ ssp_setup = SSCR1_RxTresh(14) | SSCR1_TxTresh(1) |
18561+ SSCR1_TRAIL | SSCR1_RWOT;
18562+
18563+ switch(rtd->cpu_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
18564+ case SND_SOC_DAIFMT_CBM_CFM:
18565+ ssp_setup |= (SSCR1_SCLKDIR | SSCR1_SFRMDIR);
18566+ break;
18567+ case SND_SOC_DAIFMT_CBM_CFS:
18568+ ssp_setup |= SSCR1_SCLKDIR;
18569+ break;
18570+ case SND_SOC_DAIFMT_CBS_CFM:
18571+ ssp_setup |= SSCR1_SFRMDIR;
18572+ break;
18573+ }
18574+
18575+ switch(rtd->cpu_dai->dai_runtime.fmt) {
18576+ case SND_SOC_DAIFMT_CBS_CFS:
18577+ fmt = 1;
18578+ break;
18579+ case SND_SOC_DAIFMT_CBS_CFM:
18580+ fmt = 2;
18581+ break;
18582+ case SND_SOC_DAIFMT_CBM_CFS:
18583+ fmt = 3;
18584+ break;
18585+ }
18586+
18587+ pxa_gpio_mode(ssp_gpios[rtd->cpu_dai->id][fmt].rx);
18588+ pxa_gpio_mode(ssp_gpios[rtd->cpu_dai->id][fmt].tx);
18589+ pxa_gpio_mode(ssp_gpios[rtd->cpu_dai->id][fmt].frm);
18590+ pxa_gpio_mode(ssp_gpios[rtd->cpu_dai->id][fmt].clk);
18591+
18592+ switch (rtd->cpu_dai->dai_runtime.fmt & SND_SOC_DAIFMT_INV_MASK) {
18593+ case SND_SOC_DAIFMT_NB_NF:
18594+ psp_mode |= SSPSP_SFRMP | SSPSP_FSRT;
18595+ break;
18596+ }
18597+
18598+ if (rtd->cpu_dai->dai_runtime.fmt & SND_SOC_DAIFMT_DSP_A)
18599+ psp_mode |= SSPSP_SCMODE(2);
18600+ if (rtd->cpu_dai->dai_runtime.fmt & SND_SOC_DAIFMT_DSP_B)
18601+ psp_mode |= SSPSP_SCMODE(3);
18602+
18603+ switch(clksrc[rtd->cpu_dai->id]) {
18604+ case 2: /* network clock */
18605+ ssp_mode |= SSCR0_NCS | SSCR0_MOD;
18606+ case 1: /* external clock */
18607+ ssp_mode |= SSCR0_ECS;
18608+ case 0: /* internal clock */
18609+ rate = get_scr(snd_soc_get_rate(rtd->cpu_dai->dai_runtime.pcmrate),
18610+ rtd->cpu_dai->id);
18611+ break;
18612+#ifdef CONFIG_PXA27x
18613+ case 3: /* audio clock */
18614+ ssp_mode |= (1 << 30);
18615+ SSACD_P(rtd->cpu_dai->id) = (0x1 << 3) |
18616+ pxa27x_set_audio_clk(
18617+ snd_soc_get_rate(rtd->cpu_dai->dai_runtime.pcmrate), fs);
18618+ break;
18619+#endif
18620+ }
18621+
18622+ ssp_config(&ssp[rtd->cpu_dai->id], ssp_mode, ssp_setup, psp_mode,
18623+ SSCR0_SerClkDiv(rate));
18624+#if 0
18625+ printk("SSCR0 %x SSCR1 %x SSTO %x SSPSP %x SSSR %x\n",
18626+ SSCR0_P(rtd->cpu_dai->id+1), SSCR1_P(rtd->cpu_dai->id+1),
18627+ SSTO_P(rtd->cpu_dai->id+1), SSPSP_P(rtd->cpu_dai->id+1),
18628+ SSSR_P(rtd->cpu_dai->id+1));
18629+#endif
18630+ return 0;
18631+}
18632+
18633+static int pxa2xx_ssp_trigger(struct snd_pcm_substream *substream, int cmd)
18634+{
18635+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
18636+ int ret = 0;
18637+
18638+ switch (cmd) {
18639+ case SNDRV_PCM_TRIGGER_RESUME:
18640+ ssp_enable(&ssp[rtd->cpu_dai->id]);
18641+ break;
18642+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
18643+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
18644+ SSCR1_P(rtd->cpu_dai->id+1) |= SSCR1_TSRE;
18645+ else
18646+ SSCR1_P(rtd->cpu_dai->id+1) |= SSCR1_RSRE;
18647+ SSSR_P(rtd->cpu_dai->id+1) |= SSSR_P(rtd->cpu_dai->id+1);
18648+ break;
18649+ case SNDRV_PCM_TRIGGER_START:
18650+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
18651+ SSCR1_P(rtd->cpu_dai->id+1) |= SSCR1_TSRE;
18652+ else
18653+ SSCR1_P(rtd->cpu_dai->id+1) |= SSCR1_RSRE;
18654+ ssp_enable(&ssp[rtd->cpu_dai->id]);
18655+ break;
18656+ case SNDRV_PCM_TRIGGER_STOP:
18657+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
18658+ SSCR1_P(rtd->cpu_dai->id+1) &= ~SSCR1_TSRE;
18659+ else
18660+ SSCR1_P(rtd->cpu_dai->id+1) &= ~SSCR1_RSRE;
18661+ break;
18662+ case SNDRV_PCM_TRIGGER_SUSPEND:
18663+ ssp_disable(&ssp[rtd->cpu_dai->id]);
18664+ break;
18665+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
18666+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
18667+ SSCR1_P(rtd->cpu_dai->id+1) &= ~SSCR1_TSRE;
18668+ else
18669+ SSCR1_P(rtd->cpu_dai->id+1) &= ~SSCR1_RSRE;
18670+ break;
18671+
18672+ default:
18673+ ret = -EINVAL;
18674+ }
18675+#if 0
18676+ printk("SSCR0 %x SSCR1 %x SSTO %x SSPSP %x SSSR %x\n",
18677+ SSCR0_P(rtd->cpu_dai->id+1), SSCR1_P(rtd->cpu_dai->id+1),
18678+ SSTO_P(rtd->cpu_dai->id+1), SSPSP_P(rtd->cpu_dai->id+1),
18679+ SSSR_P(rtd->cpu_dai->id+1));
18680+#endif
18681+ return ret;
18682+}
18683+
18684+struct snd_soc_cpu_dai pxa_ssp_dai[] = {
18685+ { .name = "pxa2xx-ssp1",
18686+ .id = 0,
18687+ .type = SND_SOC_DAI_PCM,
18688+ .suspend = pxa2xx_ssp_suspend,
18689+ .resume = pxa2xx_ssp_resume,
18690+ .config_sysclk = pxa_ssp_config_sysclk,
18691+ .playback = {
18692+ .channels_min = 1,
18693+ .channels_max = 2,},
18694+ .capture = {
18695+ .channels_min = 1,
18696+ .channels_max = 2,},
18697+ .ops = {
18698+ .startup = pxa2xx_ssp_startup,
18699+ .shutdown = pxa2xx_ssp_shutdown,
18700+ .trigger = pxa2xx_ssp_trigger,
18701+ .hw_params = pxa2xx_ssp_hw_params,},
18702+ .caps = {
18703+ .mode = pxa2xx_ssp_modes,
18704+ .num_modes = ARRAY_SIZE(pxa2xx_ssp_modes),},
18705+ },
18706+ { .name = "pxa2xx-ssp2",
18707+ .id = 1,
18708+ .type = SND_SOC_DAI_PCM,
18709+ .suspend = pxa2xx_ssp_suspend,
18710+ .resume = pxa2xx_ssp_resume,
18711+ .config_sysclk = pxa_ssp_config_sysclk,
18712+ .playback = {
18713+ .channels_min = 1,
18714+ .channels_max = 2,},
18715+ .capture = {
18716+ .channels_min = 1,
18717+ .channels_max = 2,},
18718+ .ops = {
18719+ .startup = pxa2xx_ssp_startup,
18720+ .shutdown = pxa2xx_ssp_shutdown,
18721+ .trigger = pxa2xx_ssp_trigger,
18722+ .hw_params = pxa2xx_ssp_hw_params,},
18723+ .caps = {
18724+ .mode = pxa2xx_ssp_modes,
18725+ .num_modes = ARRAY_SIZE(pxa2xx_ssp_modes),},
18726+ },
18727+ { .name = "pxa2xx-ssp3",
18728+ .id = 2,
18729+ .type = SND_SOC_DAI_PCM,
18730+ .suspend = pxa2xx_ssp_suspend,
18731+ .resume = pxa2xx_ssp_resume,
18732+ .config_sysclk = pxa_ssp_config_sysclk,
18733+ .playback = {
18734+ .channels_min = 1,
18735+ .channels_max = 2,},
18736+ .capture = {
18737+ .channels_min = 1,
18738+ .channels_max = 2,},
18739+ .ops = {
18740+ .startup = pxa2xx_ssp_startup,
18741+ .shutdown = pxa2xx_ssp_shutdown,
18742+ .trigger = pxa2xx_ssp_trigger,
18743+ .hw_params = pxa2xx_ssp_hw_params,},
18744+ .caps = {
18745+ .mode = pxa2xx_ssp_modes,
18746+ .num_modes = ARRAY_SIZE(pxa2xx_ssp_modes),},
18747+ },
18748+};
18749+
18750+EXPORT_SYMBOL_GPL(pxa_ssp_dai);
18751+
18752+/* Module information */
18753+MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
18754+MODULE_DESCRIPTION("pxa2xx SSP/PCM SoC Interface");
18755+MODULE_LICENSE("GPL");
18756Index: linux-2.6-pxa-new/sound/soc/pxa/spitz.c
18757===================================================================
18758--- /dev/null
18759+++ linux-2.6-pxa-new/sound/soc/pxa/spitz.c
18760@@ -0,0 +1,374 @@
18761+/*
18762+ * spitz.c -- SoC audio for Sharp SL-Cxx00 models Spitz, Borzoi and Akita
18763+ *
18764+ * Copyright 2005 Wolfson Microelectronics PLC.
18765+ * Copyright 2005 Openedhand Ltd.
18766+ *
18767+ * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
18768+ * Richard Purdie <richard@openedhand.com>
18769+ *
18770+ * This program is free software; you can redistribute it and/or modify it
18771+ * under the terms of the GNU General Public License as published by the
18772+ * Free Software Foundation; either version 2 of the License, or (at your
18773+ * option) any later version.
18774+ *
18775+ * Revision history
18776+ * 30th Nov 2005 Initial version.
18777+ *
18778+ */
18779+
18780+#include <linux/module.h>
18781+#include <linux/moduleparam.h>
18782+#include <linux/timer.h>
18783+#include <linux/interrupt.h>
18784+#include <linux/platform_device.h>
18785+#include <sound/driver.h>
18786+#include <sound/core.h>
18787+#include <sound/pcm.h>
18788+#include <sound/soc.h>
18789+#include <sound/soc-dapm.h>
18790+
18791+#include <asm/mach-types.h>
18792+#include <asm/hardware/scoop.h>
18793+#include <asm/arch/pxa-regs.h>
18794+#include <asm/arch/hardware.h>
18795+#include <asm/arch/akita.h>
18796+#include <asm/arch/spitz.h>
18797+#include <asm/mach-types.h>
18798+#include "../codecs/wm8750.h"
18799+#include "pxa2xx-pcm.h"
18800+
18801+#define SPITZ_HP 0
18802+#define SPITZ_MIC 1
18803+#define SPITZ_LINE 2
18804+#define SPITZ_HEADSET 3
18805+#define SPITZ_HP_OFF 4
18806+#define SPITZ_SPK_ON 0
18807+#define SPITZ_SPK_OFF 1
18808+
18809+ /* audio clock in Hz - rounded from 12.235MHz */
18810+#define SPITZ_AUDIO_CLOCK 12288000
18811+
18812+static int spitz_jack_func;
18813+static int spitz_spk_func;
18814+
18815+static void spitz_ext_control(struct snd_soc_codec *codec)
18816+{
18817+ if (spitz_spk_func == SPITZ_SPK_ON)
18818+ snd_soc_dapm_set_endpoint(codec, "Ext Spk", 1);
18819+ else
18820+ snd_soc_dapm_set_endpoint(codec, "Ext Spk", 0);
18821+
18822+ /* set up jack connection */
18823+ switch (spitz_jack_func) {
18824+ case SPITZ_HP:
18825+ /* enable and unmute hp jack, disable mic bias */
18826+ snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0);
18827+ snd_soc_dapm_set_endpoint(codec, "Mic Jack", 0);
18828+ snd_soc_dapm_set_endpoint(codec, "Line Jack", 0);
18829+ snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 1);
18830+ set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L);
18831+ set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R);
18832+ break;
18833+ case SPITZ_MIC:
18834+ /* enable mic jack and bias, mute hp */
18835+ snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0);
18836+ snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0);
18837+ snd_soc_dapm_set_endpoint(codec, "Line Jack", 0);
18838+ snd_soc_dapm_set_endpoint(codec, "Mic Jack", 1);
18839+ reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L);
18840+ reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R);
18841+ break;
18842+ case SPITZ_LINE:
18843+ /* enable line jack, disable mic bias and mute hp */
18844+ snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0);
18845+ snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0);
18846+ snd_soc_dapm_set_endpoint(codec, "Mic Jack", 0);
18847+ snd_soc_dapm_set_endpoint(codec, "Line Jack", 1);
18848+ reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L);
18849+ reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R);
18850+ break;
18851+ case SPITZ_HEADSET:
18852+ /* enable and unmute headset jack enable mic bias, mute L hp */
18853+ snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0);
18854+ snd_soc_dapm_set_endpoint(codec, "Mic Jack", 1);
18855+ snd_soc_dapm_set_endpoint(codec, "Line Jack", 0);
18856+ snd_soc_dapm_set_endpoint(codec, "Headset Jack", 1);
18857+ reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L);
18858+ set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R);
18859+ break;
18860+ case SPITZ_HP_OFF:
18861+
18862+ /* jack removed, everything off */
18863+ snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0);
18864+ snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0);
18865+ snd_soc_dapm_set_endpoint(codec, "Mic Jack", 0);
18866+ snd_soc_dapm_set_endpoint(codec, "Line Jack", 0);
18867+ reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L);
18868+ reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R);
18869+ break;
18870+ }
18871+ snd_soc_dapm_sync_endpoints(codec);
18872+}
18873+
18874+static int spitz_startup(struct snd_pcm_substream *substream)
18875+{
18876+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
18877+ struct snd_soc_codec *codec = rtd->socdev->codec;
18878+
18879+ /* check the jack status at stream startup */
18880+ spitz_ext_control(codec);
18881+ return 0;
18882+}
18883+
18884+static struct snd_soc_ops spitz_ops = {
18885+ .startup = spitz_startup,
18886+};
18887+
18888+static int spitz_get_jack(struct snd_kcontrol *kcontrol,
18889+ struct snd_ctl_elem_value *ucontrol)
18890+{
18891+ ucontrol->value.integer.value[0] = spitz_jack_func;
18892+ return 0;
18893+}
18894+
18895+static int spitz_set_jack(struct snd_kcontrol *kcontrol,
18896+ struct snd_ctl_elem_value *ucontrol)
18897+{
18898+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
18899+
18900+ if (spitz_jack_func == ucontrol->value.integer.value[0])
18901+ return 0;
18902+
18903+ spitz_jack_func = ucontrol->value.integer.value[0];
18904+ spitz_ext_control(codec);
18905+ return 1;
18906+}
18907+
18908+static int spitz_get_spk(struct snd_kcontrol *kcontrol,
18909+ struct snd_ctl_elem_value *ucontrol)
18910+{
18911+ ucontrol->value.integer.value[0] = spitz_spk_func;
18912+ return 0;
18913+}
18914+
18915+static int spitz_set_spk(struct snd_kcontrol *kcontrol,
18916+ struct snd_ctl_elem_value *ucontrol)
18917+{
18918+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
18919+
18920+ if (spitz_spk_func == ucontrol->value.integer.value[0])
18921+ return 0;
18922+
18923+ spitz_spk_func = ucontrol->value.integer.value[0];
18924+ spitz_ext_control(codec);
18925+ return 1;
18926+}
18927+
18928+static int spitz_mic_bias(struct snd_soc_dapm_widget *w, int event)
18929+{
18930+ if (machine_is_borzoi() || machine_is_spitz()) {
18931+ if (SND_SOC_DAPM_EVENT_ON(event))
18932+ set_scoop_gpio(&spitzscoop2_device.dev,
18933+ SPITZ_SCP2_MIC_BIAS);
18934+ else
18935+ reset_scoop_gpio(&spitzscoop2_device.dev,
18936+ SPITZ_SCP2_MIC_BIAS);
18937+ }
18938+
18939+ if (machine_is_akita()) {
18940+ if (SND_SOC_DAPM_EVENT_ON(event))
18941+ akita_set_ioexp(&akitaioexp_device.dev,
18942+ AKITA_IOEXP_MIC_BIAS);
18943+ else
18944+ akita_reset_ioexp(&akitaioexp_device.dev,
18945+ AKITA_IOEXP_MIC_BIAS);
18946+ }
18947+ return 0;
18948+}
18949+
18950+/* spitz machine dapm widgets */
18951+static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = {
18952+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
18953+ SND_SOC_DAPM_MIC("Mic Jack", spitz_mic_bias),
18954+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
18955+ SND_SOC_DAPM_LINE("Line Jack", NULL),
18956+
18957+ /* headset is a mic and mono headphone */
18958+ SND_SOC_DAPM_HP("Headset Jack", NULL),
18959+};
18960+
18961+/* Spitz machine audio_map */
18962+static const char *audio_map[][3] = {
18963+
18964+ /* headphone connected to LOUT1, ROUT1 */
18965+ {"Headphone Jack", NULL, "LOUT1"},
18966+ {"Headphone Jack", NULL, "ROUT1"},
18967+
18968+ /* headset connected to ROUT1 and LINPUT1 with bias (def below) */
18969+ {"Headset Jack", NULL, "ROUT1"},
18970+
18971+ /* ext speaker connected to LOUT2, ROUT2 */
18972+ {"Ext Spk", NULL , "ROUT2"},
18973+ {"Ext Spk", NULL , "LOUT2"},
18974+
18975+ /* mic is connected to input 1 - with bias */
18976+ {"LINPUT1", NULL, "Mic Bias"},
18977+ {"Mic Bias", NULL, "Mic Jack"},
18978+
18979+ /* line is connected to input 1 - no bias */
18980+ {"LINPUT1", NULL, "Line Jack"},
18981+
18982+ {NULL, NULL, NULL},
18983+};
18984+
18985+static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset",
18986+ "Off"};
18987+static const char *spk_function[] = {"On", "Off"};
18988+static const struct soc_enum spitz_enum[] = {
18989+ SOC_ENUM_SINGLE_EXT(5, jack_function),
18990+ SOC_ENUM_SINGLE_EXT(2, spk_function),
18991+};
18992+
18993+static const struct snd_kcontrol_new wm8750_spitz_controls[] = {
18994+ SOC_ENUM_EXT("Jack Function", spitz_enum[0], spitz_get_jack,
18995+ spitz_set_jack),
18996+ SOC_ENUM_EXT("Speaker Function", spitz_enum[1], spitz_get_spk,
18997+ spitz_set_spk),
18998+};
18999+
19000+/*
19001+ * Logic for a wm8750 as connected on a Sharp SL-Cxx00 Device
19002+ */
19003+static int spitz_wm8750_init(struct snd_soc_codec *codec)
19004+{
19005+ int i, err;
19006+
19007+ /* NC codec pins */
19008+ snd_soc_dapm_set_endpoint(codec, "RINPUT1", 0);
19009+ snd_soc_dapm_set_endpoint(codec, "LINPUT2", 0);
19010+ snd_soc_dapm_set_endpoint(codec, "RINPUT2", 0);
19011+ snd_soc_dapm_set_endpoint(codec, "LINPUT3", 0);
19012+ snd_soc_dapm_set_endpoint(codec, "RINPUT3", 0);
19013+ snd_soc_dapm_set_endpoint(codec, "OUT3", 0);
19014+ snd_soc_dapm_set_endpoint(codec, "MONO", 0);
19015+
19016+ /* Add spitz specific controls */
19017+ for (i = 0; i < ARRAY_SIZE(wm8750_spitz_controls); i++) {
19018+ err = snd_ctl_add(codec->card,
19019+ snd_soc_cnew(&wm8750_spitz_controls[i], codec, NULL));
19020+ if (err < 0)
19021+ return err;
19022+ }
19023+
19024+ /* Add spitz specific widgets */
19025+ for (i = 0; i < ARRAY_SIZE(wm8750_dapm_widgets); i++) {
19026+ snd_soc_dapm_new_control(codec, &wm8750_dapm_widgets[i]);
19027+ }
19028+
19029+ /* Set up spitz specific audio path audio_map */
19030+ for (i = 0; audio_map[i][0] != NULL; i++) {
19031+ snd_soc_dapm_connect_input(codec, audio_map[i][0],
19032+ audio_map[i][1], audio_map[i][2]);
19033+ }
19034+
19035+ snd_soc_dapm_sync_endpoints(codec);
19036+ return 0;
19037+}
19038+
19039+static unsigned int spitz_config_sysclk(struct snd_soc_pcm_runtime *rtd,
19040+ struct snd_soc_clock_info *info)
19041+{
19042+ if (info->bclk_master & SND_SOC_DAIFMT_CBS_CFS) {
19043+ /* pxa2xx is i2s master */
19044+ switch (info->rate) {
19045+ case 11025:
19046+ case 22050:
19047+ case 44100:
19048+ case 88200:
19049+ /* configure codec digital filters
19050+ * for 11.025, 22.05, 44.1, 88.2 */
19051+ rtd->codec_dai->config_sysclk(rtd->codec_dai, info,
19052+ 11289600);
19053+ break;
19054+ default:
19055+ /* configure codec digital filters for all other rates */
19056+ rtd->codec_dai->config_sysclk(rtd->codec_dai, info,
19057+ SPITZ_AUDIO_CLOCK);
19058+ break;
19059+ }
19060+ /* configure pxa2xx i2s interface clocks as master */
19061+ return rtd->cpu_dai->config_sysclk(rtd->cpu_dai, info,
19062+ SPITZ_AUDIO_CLOCK);
19063+ } else {
19064+ /* codec is i2s master - only configure codec DAI clock */
19065+ return rtd->codec_dai->config_sysclk(rtd->codec_dai, info,
19066+ SPITZ_AUDIO_CLOCK);
19067+ }
19068+}
19069+
19070+/* spitz digital audio interface glue - connects codec <--> CPU */
19071+static struct snd_soc_dai_link spitz_dai = {
19072+ .name = "wm8750",
19073+ .stream_name = "WM8750",
19074+ .cpu_dai = &pxa_i2s_dai,
19075+ .codec_dai = &wm8750_dai,
19076+ .init = spitz_wm8750_init,
19077+ .config_sysclk = spitz_config_sysclk,
19078+};
19079+
19080+/* spitz audio machine driver */
19081+static struct snd_soc_machine snd_soc_machine_spitz = {
19082+ .name = "Spitz",
19083+ .dai_link = &spitz_dai,
19084+ .num_links = 1,
19085+ .ops = &spitz_ops,
19086+};
19087+
19088+/* spitz audio private data */
19089+static struct wm8750_setup_data spitz_wm8750_setup = {
19090+ .i2c_address = 0x1b,
19091+};
19092+
19093+/* spitz audio subsystem */
19094+static struct snd_soc_device spitz_snd_devdata = {
19095+ .machine = &snd_soc_machine_spitz,
19096+ .platform = &pxa2xx_soc_platform,
19097+ .codec_dev = &soc_codec_dev_wm8750,
19098+ .codec_data = &spitz_wm8750_setup,
19099+};
19100+
19101+static struct platform_device *spitz_snd_device;
19102+
19103+static int __init spitz_init(void)
19104+{
19105+ int ret;
19106+
19107+ if (!(machine_is_spitz() || machine_is_borzoi() || machine_is_akita()))
19108+ return -ENODEV;
19109+
19110+ spitz_snd_device = platform_device_alloc("soc-audio", -1);
19111+ if (!spitz_snd_device)
19112+ return -ENOMEM;
19113+
19114+ platform_set_drvdata(spitz_snd_device, &spitz_snd_devdata);
19115+ spitz_snd_devdata.dev = &spitz_snd_device->dev;
19116+ ret = platform_device_add(spitz_snd_device);
19117+
19118+ if (ret)
19119+ platform_device_put(spitz_snd_device);
19120+
19121+ return ret;
19122+}
19123+
19124+static void __exit spitz_exit(void)
19125+{
19126+ platform_device_unregister(spitz_snd_device);
19127+}
19128+
19129+module_init(spitz_init);
19130+module_exit(spitz_exit);
19131+
19132+MODULE_AUTHOR("Richard Purdie");
19133+MODULE_DESCRIPTION("ALSA SoC Spitz");
19134+MODULE_LICENSE("GPL");
19135Index: linux-2.6-pxa-new/sound/soc/pxa/tosa.c
19136===================================================================
19137--- /dev/null
19138+++ linux-2.6-pxa-new/sound/soc/pxa/tosa.c
19139@@ -0,0 +1,287 @@
19140+/*
19141+ * tosa.c -- SoC audio for Tosa
19142+ *
19143+ * Copyright 2005 Wolfson Microelectronics PLC.
19144+ * Copyright 2005 Openedhand Ltd.
19145+ *
19146+ * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
19147+ * Richard Purdie <richard@openedhand.com>
19148+ *
19149+ * This program is free software; you can redistribute it and/or modify it
19150+ * under the terms of the GNU General Public License as published by the
19151+ * Free Software Foundation; either version 2 of the License, or (at your
19152+ * option) any later version.
19153+ *
19154+ * Revision history
19155+ * 30th Nov 2005 Initial version.
19156+ *
19157+ * GPIO's
19158+ * 1 - Jack Insertion
19159+ * 5 - Hookswitch (headset answer/hang up switch)
19160+ *
19161+ */
19162+
19163+#include <linux/module.h>
19164+#include <linux/moduleparam.h>
19165+#include <linux/device.h>
19166+
19167+#include <sound/driver.h>
19168+#include <sound/core.h>
19169+#include <sound/pcm.h>
19170+#include <sound/soc.h>
19171+#include <sound/soc-dapm.h>
19172+
19173+#include <asm/mach-types.h>
19174+#include <asm/hardware/tmio.h>
19175+#include <asm/arch/pxa-regs.h>
19176+#include <asm/arch/hardware.h>
19177+#include <asm/arch/audio.h>
19178+#include <asm/arch/tosa.h>
19179+
19180+#include "../codecs/wm9712.h"
19181+#include "pxa2xx-pcm.h"
19182+
19183+static struct snd_soc_machine tosa;
19184+
19185+#define TOSA_HP 0
19186+#define TOSA_MIC_INT 1
19187+#define TOSA_HEADSET 2
19188+#define TOSA_HP_OFF 3
19189+#define TOSA_SPK_ON 0
19190+#define TOSA_SPK_OFF 1
19191+
19192+static int tosa_jack_func;
19193+static int tosa_spk_func;
19194+
19195+static void tosa_ext_control(struct snd_soc_codec *codec)
19196+{
19197+ int spk = 0, mic_int = 0, hp = 0, hs = 0;
19198+
19199+ /* set up jack connection */
19200+ switch (tosa_jack_func) {
19201+ case TOSA_HP:
19202+ hp = 1;
19203+ break;
19204+ case TOSA_MIC_INT:
19205+ mic_int = 1;
19206+ break;
19207+ case TOSA_HEADSET:
19208+ hs = 1;
19209+ break;
19210+ }
19211+
19212+ if (tosa_spk_func == TOSA_SPK_ON)
19213+ spk = 1;
19214+
19215+ snd_soc_dapm_set_endpoint(codec, "Speaker", spk);
19216+ snd_soc_dapm_set_endpoint(codec, "Mic (Internal)", mic_int);
19217+ snd_soc_dapm_set_endpoint(codec, "Headphone Jack", hp);
19218+ snd_soc_dapm_set_endpoint(codec, "Headset Jack", hs);
19219+ snd_soc_dapm_sync_endpoints(codec);
19220+}
19221+
19222+static int tosa_startup(struct snd_pcm_substream *substream)
19223+{
19224+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
19225+ struct snd_soc_codec *codec = rtd->socdev->codec;
19226+
19227+ /* check the jack status at stream startup */
19228+ tosa_ext_control(codec);
19229+ return 0;
19230+}
19231+
19232+static struct snd_soc_ops tosa_ops = {
19233+ .startup = tosa_startup,
19234+};
19235+
19236+static int tosa_get_jack(struct snd_kcontrol *kcontrol,
19237+ struct snd_ctl_elem_value *ucontrol)
19238+{
19239+ ucontrol->value.integer.value[0] = tosa_jack_func;
19240+ return 0;
19241+}
19242+
19243+static int tosa_set_jack(struct snd_kcontrol *kcontrol,
19244+ struct snd_ctl_elem_value *ucontrol)
19245+{
19246+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
19247+
19248+ if (tosa_jack_func == ucontrol->value.integer.value[0])
19249+ return 0;
19250+
19251+ tosa_jack_func = ucontrol->value.integer.value[0];
19252+ tosa_ext_control(codec);
19253+ return 1;
19254+}
19255+
19256+static int tosa_get_spk(struct snd_kcontrol *kcontrol,
19257+ struct snd_ctl_elem_value *ucontrol)
19258+{
19259+ ucontrol->value.integer.value[0] = tosa_spk_func;
19260+ return 0;
19261+}
19262+
19263+static int tosa_set_spk(struct snd_kcontrol *kcontrol,
19264+ struct snd_ctl_elem_value *ucontrol)
19265+{
19266+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
19267+
19268+ if (tosa_spk_func == ucontrol->value.integer.value[0])
19269+ return 0;
19270+
19271+ tosa_spk_func = ucontrol->value.integer.value[0];
19272+ tosa_ext_control(codec);
19273+ return 1;
19274+}
19275+
19276+/* tosa dapm event handlers */
19277+static int tosa_hp_event(struct snd_soc_dapm_widget *w, int event)
19278+{
19279+ if (SND_SOC_DAPM_EVENT_ON(event))
19280+ set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_L_MUTE);
19281+ else
19282+ reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_L_MUTE);
19283+ return 0;
19284+}
19285+
19286+/* tosa machine dapm widgets */
19287+static const struct snd_soc_dapm_widget tosa_dapm_widgets[] = {
19288+SND_SOC_DAPM_HP("Headphone Jack", tosa_hp_event),
19289+SND_SOC_DAPM_HP("Headset Jack", NULL),
19290+SND_SOC_DAPM_MIC("Mic (Internal)", NULL),
19291+SND_SOC_DAPM_SPK("Speaker", NULL),
19292+};
19293+
19294+/* tosa audio map */
19295+static const char *audio_map[][3] = {
19296+
19297+ /* headphone connected to HPOUTL, HPOUTR */
19298+ {"Headphone Jack", NULL, "HPOUTL"},
19299+ {"Headphone Jack", NULL, "HPOUTR"},
19300+
19301+ /* ext speaker connected to LOUT2, ROUT2 */
19302+ {"Speaker", NULL, "LOUT2"},
19303+ {"Speaker", NULL, "ROUT2"},
19304+
19305+ /* internal mic is connected to mic1, mic2 differential - with bias */
19306+ {"MIC1", NULL, "Mic Bias"},
19307+ {"MIC2", NULL, "Mic Bias"},
19308+ {"Mic Bias", NULL, "Mic (Internal)"},
19309+
19310+ /* headset is connected to HPOUTR, and LINEINR with bias */
19311+ {"Headset Jack", NULL, "HPOUTR"},
19312+ {"LINEINR", NULL, "Mic Bias"},
19313+ {"Mic Bias", NULL, "Headset Jack"},
19314+
19315+ {NULL, NULL, NULL},
19316+};
19317+
19318+static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset",
19319+ "Off"};
19320+static const char *spk_function[] = {"On", "Off"};
19321+static const struct soc_enum tosa_enum[] = {
19322+ SOC_ENUM_SINGLE_EXT(5, jack_function),
19323+ SOC_ENUM_SINGLE_EXT(2, spk_function),
19324+};
19325+
19326+static const struct snd_kcontrol_new tosa_controls[] = {
19327+ SOC_ENUM_EXT("Jack Function", tosa_enum[0], tosa_get_jack,
19328+ tosa_set_jack),
19329+ SOC_ENUM_EXT("Speaker Function", tosa_enum[1], tosa_get_spk,
19330+ tosa_set_spk),
19331+};
19332+
19333+static int tosa_ac97_init(struct snd_soc_codec *codec)
19334+{
19335+ int i, err;
19336+
19337+ snd_soc_dapm_set_endpoint(codec, "OUT3", 0);
19338+ snd_soc_dapm_set_endpoint(codec, "MONOOUT", 0);
19339+
19340+ /* add tosa specific controls */
19341+ for (i = 0; i < ARRAY_SIZE(tosa_controls); i++) {
19342+ err = snd_ctl_add(codec->card,
19343+ snd_soc_cnew(&tosa_controls[i],codec, NULL));
19344+ if (err < 0)
19345+ return err;
19346+ }
19347+
19348+ /* add tosa specific widgets */
19349+ for (i = 0; i < ARRAY_SIZE(tosa_dapm_widgets); i++) {
19350+ snd_soc_dapm_new_control(codec, &tosa_dapm_widgets[i]);
19351+ }
19352+
19353+ /* set up tosa specific audio path audio_map */
19354+ for (i = 0; audio_map[i][0] != NULL; i++) {
19355+ snd_soc_dapm_connect_input(codec, audio_map[i][0],
19356+ audio_map[i][1], audio_map[i][2]);
19357+ }
19358+
19359+ snd_soc_dapm_sync_endpoints(codec);
19360+ return 0;
19361+}
19362+
19363+static struct snd_soc_dai_link tosa_dai[] = {
19364+{
19365+ .name = "AC97",
19366+ .stream_name = "AC97 HiFi",
19367+ .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI],
19368+ .codec_dai = &wm9712_dai[WM9712_DAI_AC97_HIFI],
19369+ .init = tosa_ac97_init,
19370+},
19371+{
19372+ .name = "AC97 Aux",
19373+ .stream_name = "AC97 Aux",
19374+ .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX],
19375+ .codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX],
19376+},
19377+};
19378+
19379+static struct snd_soc_machine tosa = {
19380+ .name = "Tosa",
19381+ .dai_link = tosa_dai,
19382+ .num_links = ARRAY_SIZE(tosa_dai),
19383+ .ops = &tosa_ops,
19384+};
19385+
19386+static struct snd_soc_device tosa_snd_devdata = {
19387+ .machine = &tosa,
19388+ .platform = &pxa2xx_soc_platform,
19389+ .codec_dev = &soc_codec_dev_wm9712,
19390+};
19391+
19392+static struct platform_device *tosa_snd_device;
19393+
19394+static int __init tosa_init(void)
19395+{
19396+ int ret;
19397+
19398+ if (!machine_is_tosa())
19399+ return -ENODEV;
19400+
19401+ tosa_snd_device = platform_device_alloc("soc-audio", -1);
19402+ if (!tosa_snd_device)
19403+ return -ENOMEM;
19404+
19405+ platform_set_drvdata(tosa_snd_device, &tosa_snd_devdata);
19406+ tosa_snd_devdata.dev = &tosa_snd_device->dev;
19407+ ret = platform_device_add(tosa_snd_device);
19408+
19409+ if (ret)
19410+ platform_device_put(tosa_snd_device);
19411+
19412+ return ret;
19413+}
19414+
19415+static void __exit tosa_exit(void)
19416+{
19417+ platform_device_unregister(tosa_snd_device);
19418+}
19419+
19420+module_init(tosa_init);
19421+module_exit(tosa_exit);
19422+
19423+/* Module information */
19424+MODULE_AUTHOR("Richard Purdie");
19425+MODULE_DESCRIPTION("ALSA SoC Tosa");
19426+MODULE_LICENSE("GPL");
19427Index: linux-2.6-pxa-new/sound/soc/soc-dapm.c
19428===================================================================
19429--- /dev/null
19430+++ linux-2.6-pxa-new/sound/soc/soc-dapm.c
19431@@ -0,0 +1,1327 @@
19432+/*
19433+ * soc-dapm.c -- ALSA SoC Dynamic Audio Power Management
19434+ *
19435+ * Copyright 2005 Wolfson Microelectronics PLC.
19436+ * Author: Liam Girdwood
19437+ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
19438+ *
19439+ * This program is free software; you can redistribute it and/or modify it
19440+ * under the terms of the GNU General Public License as published by the
19441+ * Free Software Foundation; either version 2 of the License, or (at your
19442+ * option) any later version.
19443+ *
19444+ * Revision history
19445+ * 12th Aug 2005 Initial version.
19446+ * 25th Oct 2005 Implemented path power domain.
19447+ * 18th Dec 2005 Implemented machine and stream level power domain.
19448+ *
19449+ * Features:
19450+ * o Changes power status of internal codec blocks depending on the
19451+ * dynamic configuration of codec internal audio paths and active
19452+ * DAC's/ADC's.
19453+ * o Platform power domain - can support external components i.e. amps and
19454+ * mic/meadphone insertion events.
19455+ * o Automatic Mic Bias support
19456+ * o Jack insertion power event initiation - e.g. hp insertion will enable
19457+ * sinks, dacs, etc
19458+ * o Delayed powerdown of audio susbsytem to reduce pops between a quick
19459+ * device reopen.
19460+ *
19461+ * Todo:
19462+ * o DAPM power change sequencing - allow for configurable per
19463+ * codec sequences.
19464+ * o Support for analogue bias optimisation.
19465+ * o Support for reduced codec oversampling rates.
19466+ * o Support for reduced codec bias currents.
19467+ */
19468+
19469+#include <linux/module.h>
19470+#include <linux/moduleparam.h>
19471+#include <linux/init.h>
19472+#include <linux/delay.h>
19473+#include <linux/pm.h>
19474+#include <linux/bitops.h>
19475+#include <linux/platform_device.h>
19476+#include <linux/jiffies.h>
19477+#include <sound/driver.h>
19478+#include <sound/core.h>
19479+#include <sound/pcm.h>
19480+#include <sound/pcm_params.h>
19481+#include <sound/soc-dapm.h>
19482+#include <sound/initval.h>
19483+
19484+/* debug */
19485+#define DAPM_DEBUG 0
19486+#if DAPM_DEBUG
19487+#define dump_dapm(codec, action) dbg_dump_dapm(codec, action)
19488+#define dbg(format, arg...) printk(format, ## arg)
19489+#else
19490+#define dump_dapm(codec, action)
19491+#define dbg(format, arg...)
19492+#endif
19493+
19494+#define POP_DEBUG 0
19495+#if POP_DEBUG
19496+#define POP_TIME 500 /* 500 msecs - change if pop debug is too fast */
19497+#define pop_wait(time) schedule_timeout_interruptible(msecs_to_jiffies(time))
19498+#define pop_dbg(format, arg...) printk(format, ## arg); pop_wait(POP_TIME)
19499+#else
19500+#define pop_dbg(format, arg...)
19501+#define pop_wait(time)
19502+#endif
19503+
19504+/* dapm power sequences - make this per codec in the future */
19505+static int dapm_up_seq[] = {
19506+ snd_soc_dapm_pre, snd_soc_dapm_micbias, snd_soc_dapm_mic,
19507+ snd_soc_dapm_mux, snd_soc_dapm_dac, snd_soc_dapm_mixer, snd_soc_dapm_pga,
19508+ snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk, snd_soc_dapm_post
19509+};
19510+static int dapm_down_seq[] = {
19511+ snd_soc_dapm_pre, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk,
19512+ snd_soc_dapm_pga, snd_soc_dapm_mixer, snd_soc_dapm_dac, snd_soc_dapm_mic,
19513+ snd_soc_dapm_micbias, snd_soc_dapm_mux, snd_soc_dapm_post
19514+};
19515+
19516+static int dapm_status = 1;
19517+module_param(dapm_status, int, 0);
19518+MODULE_PARM_DESC(dapm_status, "enable DPM sysfs entries");
19519+
19520+/* create a new dapm widget */
19521+static struct snd_soc_dapm_widget *dapm_cnew_widget(
19522+ const struct snd_soc_dapm_widget *_widget)
19523+{
19524+ struct snd_soc_dapm_widget* widget;
19525+ widget = kmalloc(sizeof(struct snd_soc_dapm_widget), GFP_KERNEL);
19526+ if (!widget)
19527+ return NULL;
19528+
19529+ memcpy(widget, _widget, sizeof(struct snd_soc_dapm_widget));
19530+ return widget;
19531+}
19532+
19533+/* set up initial codec paths */
19534+static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
19535+ struct snd_soc_dapm_path *p, int i)
19536+{
19537+ switch (w->id) {
19538+ case snd_soc_dapm_switch:
19539+ case snd_soc_dapm_mixer: {
19540+ int val;
19541+ int reg = w->kcontrols[i].private_value & 0xff;
19542+ int shift = (w->kcontrols[i].private_value >> 8) & 0x0f;
19543+ int mask = (w->kcontrols[i].private_value >> 16) & 0xff;
19544+ int invert = (w->kcontrols[i].private_value >> 24) & 0x01;
19545+
19546+ val = snd_soc_read(w->codec, reg);
19547+ val = (val >> shift) & mask;
19548+
19549+ if ((invert && !val) || (!invert && val))
19550+ p->connect = 1;
19551+ else
19552+ p->connect = 0;
19553+ }
19554+ break;
19555+ case snd_soc_dapm_mux: {
19556+ struct soc_enum *e = (struct soc_enum *)w->kcontrols[i].private_value;
19557+ int val, item, bitmask;
19558+
19559+ for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
19560+ ;
19561+ val = snd_soc_read(w->codec, e->reg);
19562+ item = (val >> e->shift_l) & (bitmask - 1);
19563+
19564+ p->connect = 0;
19565+ for (i = 0; i < e->mask; i++) {
19566+ if (!(strcmp(p->name, e->texts[i])) && item == i)
19567+ p->connect = 1;
19568+ }
19569+ }
19570+ break;
19571+ /* does not effect routing - always connected */
19572+ case snd_soc_dapm_pga:
19573+ case snd_soc_dapm_output:
19574+ case snd_soc_dapm_adc:
19575+ case snd_soc_dapm_input:
19576+ case snd_soc_dapm_dac:
19577+ case snd_soc_dapm_micbias:
19578+ case snd_soc_dapm_vmid:
19579+ p->connect = 1;
19580+ break;
19581+ /* does effect routing - dynamically connected */
19582+ case snd_soc_dapm_hp:
19583+ case snd_soc_dapm_mic:
19584+ case snd_soc_dapm_spk:
19585+ case snd_soc_dapm_line:
19586+ case snd_soc_dapm_pre:
19587+ case snd_soc_dapm_post:
19588+ p->connect = 0;
19589+ break;
19590+ }
19591+}
19592+
19593+/* connect mux widget to it's interconnecting audio paths */
19594+static int dapm_connect_mux(struct snd_soc_codec *codec,
19595+ struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,
19596+ struct snd_soc_dapm_path *path, const char *control_name,
19597+ const struct snd_kcontrol_new *kcontrol)
19598+{
19599+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
19600+ int i;
19601+
19602+ for (i = 0; i < e->mask; i++) {
19603+ if (!(strcmp(control_name, e->texts[i]))) {
19604+ list_add(&path->list, &codec->dapm_paths);
19605+ list_add(&path->list_sink, &dest->sources);
19606+ list_add(&path->list_source, &src->sinks);
19607+ path->name = (char*)e->texts[i];
19608+ dapm_set_path_status(dest, path, 0);
19609+ return 0;
19610+ }
19611+ }
19612+
19613+ return -ENODEV;
19614+}
19615+
19616+/* connect mixer widget to it's interconnecting audio paths */
19617+static int dapm_connect_mixer(struct snd_soc_codec *codec,
19618+ struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,
19619+ struct snd_soc_dapm_path *path, const char *control_name)
19620+{
19621+ int i;
19622+
19623+ /* search for mixer kcontrol */
19624+ for (i = 0; i < dest->num_kcontrols; i++) {
19625+ if (!strcmp(control_name, dest->kcontrols[i].name)) {
19626+ list_add(&path->list, &codec->dapm_paths);
19627+ list_add(&path->list_sink, &dest->sources);
19628+ list_add(&path->list_source, &src->sinks);
19629+ path->name = dest->kcontrols[i].name;
19630+ dapm_set_path_status(dest, path, i);
19631+ return 0;
19632+ }
19633+ }
19634+ return -ENODEV;
19635+}
19636+
19637+/* update dapm codec register bits */
19638+static int dapm_update_bits(struct snd_soc_dapm_widget *widget)
19639+{
19640+ int change, power;
19641+ unsigned short old, new;
19642+ struct snd_soc_codec *codec = widget->codec;
19643+
19644+ /* check for valid widgets */
19645+ if (widget->reg < 0 || widget->id == snd_soc_dapm_input ||
19646+ widget->id == snd_soc_dapm_output ||
19647+ widget->id == snd_soc_dapm_hp ||
19648+ widget->id == snd_soc_dapm_mic ||
19649+ widget->id == snd_soc_dapm_line ||
19650+ widget->id == snd_soc_dapm_spk)
19651+ return 0;
19652+
19653+ power = widget->power;
19654+ if (widget->invert)
19655+ power = (power ? 0:1);
19656+
19657+ old = snd_soc_read(codec, widget->reg);
19658+ new = (old & ~(0x1 << widget->shift)) | (power << widget->shift);
19659+
19660+ change = old != new;
19661+ if (change) {
19662+ pop_dbg("pop test %s : %s in %d ms\n", widget->name,
19663+ widget->power ? "on" : "off", POP_TIME);
19664+ snd_soc_write(codec, widget->reg, new);
19665+ pop_wait(POP_TIME);
19666+ }
19667+ dbg("reg old %x new %x change %d\n", old, new, change);
19668+ return change;
19669+}
19670+
19671+/* ramps the volume up or down to minimise pops before or after a
19672+ * DAPM power event */
19673+static int dapm_set_pga(struct snd_soc_dapm_widget *widget, int power)
19674+{
19675+ const struct snd_kcontrol_new *k = widget->kcontrols;
19676+
19677+ if (widget->muted && !power)
19678+ return 0;
19679+ if (!widget->muted && power)
19680+ return 0;
19681+
19682+ if (widget->num_kcontrols && k) {
19683+ int reg = k->private_value & 0xff;
19684+ int shift = (k->private_value >> 8) & 0x0f;
19685+ int mask = (k->private_value >> 16) & 0xff;
19686+ int invert = (k->private_value >> 24) & 0x01;
19687+
19688+ if (power) {
19689+ int i;
19690+ /* power up has happended, increase volume to last level */
19691+ if (invert) {
19692+ for (i = mask; i > widget->saved_value; i--)
19693+ snd_soc_update_bits(widget->codec, reg, mask, i);
19694+ } else {
19695+ for (i = 0; i < widget->saved_value; i++)
19696+ snd_soc_update_bits(widget->codec, reg, mask, i);
19697+ }
19698+ widget->muted = 0;
19699+ } else {
19700+ /* power down is about to occur, decrease volume to mute */
19701+ int val = snd_soc_read(widget->codec, reg);
19702+ int i = widget->saved_value = (val >> shift) & mask;
19703+ if (invert) {
19704+ for (; i < mask; i++)
19705+ snd_soc_update_bits(widget->codec, reg, mask, i);
19706+ } else {
19707+ for (; i > 0; i--)
19708+ snd_soc_update_bits(widget->codec, reg, mask, i);
19709+ }
19710+ widget->muted = 1;
19711+ }
19712+ }
19713+ return 0;
19714+}
19715+
19716+/* create new dapm mixer control */
19717+static int dapm_new_mixer(struct snd_soc_codec *codec,
19718+ struct snd_soc_dapm_widget *w)
19719+{
19720+ int i, ret = 0;
19721+ char name[32];
19722+ struct snd_soc_dapm_path *path;
19723+
19724+ /* add kcontrol */
19725+ for (i = 0; i < w->num_kcontrols; i++) {
19726+
19727+ /* match name */
19728+ list_for_each_entry(path, &w->sources, list_sink) {
19729+
19730+ /* mixer/mux paths name must match control name */
19731+ if (path->name != (char*)w->kcontrols[i].name)
19732+ continue;
19733+
19734+ /* add dapm control with long name */
19735+ snprintf(name, 32, "%s %s", w->name, w->kcontrols[i].name);
19736+ path->long_name = kstrdup (name, GFP_KERNEL);
19737+ if (path->long_name == NULL)
19738+ return -ENOMEM;
19739+
19740+ path->kcontrol = snd_soc_cnew(&w->kcontrols[i], w,
19741+ path->long_name);
19742+ ret = snd_ctl_add(codec->card, path->kcontrol);
19743+ if (ret < 0) {
19744+ printk(KERN_ERR "asoc: failed to add dapm kcontrol %s\n",
19745+ path->long_name);
19746+ kfree(path->long_name);
19747+ path->long_name = NULL;
19748+ return ret;
19749+ }
19750+ }
19751+ }
19752+ return ret;
19753+}
19754+
19755+/* create new dapm mux control */
19756+static int dapm_new_mux(struct snd_soc_codec *codec,
19757+ struct snd_soc_dapm_widget *w)
19758+{
19759+ struct snd_soc_dapm_path *path = NULL;
19760+ struct snd_kcontrol *kcontrol;
19761+ int ret = 0;
19762+
19763+ if (!w->num_kcontrols) {
19764+ printk(KERN_ERR "asoc: mux %s has no controls\n", w->name);
19765+ return -EINVAL;
19766+ }
19767+
19768+ kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name);
19769+ ret = snd_ctl_add(codec->card, kcontrol);
19770+ if (ret < 0)
19771+ goto err;
19772+
19773+ list_for_each_entry(path, &w->sources, list_sink)
19774+ path->kcontrol = kcontrol;
19775+
19776+ return ret;
19777+
19778+err:
19779+ printk(KERN_ERR "asoc: failed to add kcontrol %s\n", w->name);
19780+ return ret;
19781+}
19782+
19783+/* create new dapm volume control */
19784+static int dapm_new_pga(struct snd_soc_codec *codec,
19785+ struct snd_soc_dapm_widget *w)
19786+{
19787+ struct snd_kcontrol *kcontrol;
19788+ int ret = 0;
19789+
19790+ if (!w->num_kcontrols)
19791+ return -EINVAL;
19792+
19793+ kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name);
19794+ ret = snd_ctl_add(codec->card, kcontrol);
19795+ if (ret < 0) {
19796+ printk(KERN_ERR "asoc: failed to add kcontrol %s\n", w->name);
19797+ return ret;
19798+ }
19799+
19800+ return ret;
19801+}
19802+
19803+/* reset 'walked' bit for each dapm path */
19804+static inline void dapm_clear_walk(struct snd_soc_codec *codec)
19805+{
19806+ struct snd_soc_dapm_path *p;
19807+
19808+ list_for_each_entry(p, &codec->dapm_paths, list)
19809+ p->walked = 0;
19810+}
19811+
19812+/*
19813+ * Recursively check for a completed path to an active or physically connected
19814+ * output widget. Returns number of complete paths.
19815+ */
19816+static int is_connected_output_ep(struct snd_soc_dapm_widget *widget)
19817+{
19818+ struct snd_soc_dapm_path *path;
19819+ int con = 0;
19820+
19821+ if (widget->id == snd_soc_dapm_adc && widget->active)
19822+ return 1;
19823+
19824+ if (widget->connected) {
19825+ /* connected pin ? */
19826+ if (widget->id == snd_soc_dapm_output && !widget->ext)
19827+ return 1;
19828+
19829+ /* connected jack or spk ? */
19830+ if (widget->id == snd_soc_dapm_hp || widget->id == snd_soc_dapm_spk ||
19831+ widget->id == snd_soc_dapm_line)
19832+ return 1;
19833+ }
19834+
19835+ list_for_each_entry(path, &widget->sinks, list_source) {
19836+ if (path->walked)
19837+ continue;
19838+
19839+ if (path->sink && path->connect) {
19840+ path->walked = 1;
19841+ con += is_connected_output_ep(path->sink);
19842+ }
19843+ }
19844+
19845+ return con;
19846+}
19847+
19848+/*
19849+ * Recursively check for a completed path to an active or physically connected
19850+ * input widget. Returns number of complete paths.
19851+ */
19852+static int is_connected_input_ep(struct snd_soc_dapm_widget *widget)
19853+{
19854+ struct snd_soc_dapm_path *path;
19855+ int con = 0;
19856+
19857+ /* active stream ? */
19858+ if (widget->id == snd_soc_dapm_dac && widget->active)
19859+ return 1;
19860+
19861+ if (widget->connected) {
19862+ /* connected pin ? */
19863+ if (widget->id == snd_soc_dapm_input && !widget->ext)
19864+ return 1;
19865+
19866+ /* connected VMID/Bias for lower pops */
19867+ if (widget->id == snd_soc_dapm_vmid)
19868+ return 1;
19869+
19870+ /* connected jack ? */
19871+ if (widget->id == snd_soc_dapm_mic || widget->id == snd_soc_dapm_line)
19872+ return 1;
19873+ }
19874+
19875+ list_for_each_entry(path, &widget->sources, list_sink) {
19876+ if (path->walked)
19877+ continue;
19878+
19879+ if (path->source && path->connect) {
19880+ path->walked = 1;
19881+ con += is_connected_input_ep(path->source);
19882+ }
19883+ }
19884+
19885+ return con;
19886+}
19887+
19888+/*
19889+ * Scan each dapm widget for complete audio path.
19890+ * A complete path is a route that has valid endpoints i.e.:-
19891+ *
19892+ * o DAC to output pin.
19893+ * o Input Pin to ADC.
19894+ * o Input pin to Output pin (bypass, sidetone)
19895+ * o DAC to ADC (loopback).
19896+ */
19897+int dapm_power_widgets(struct snd_soc_codec *codec, int event)
19898+{
19899+ struct snd_soc_dapm_widget *w;
19900+ int in, out, i, c = 1, *seq = NULL, ret = 0, power_change, power;
19901+
19902+ /* do we have a sequenced stream event */
19903+ if (event == SND_SOC_DAPM_STREAM_START) {
19904+ c = ARRAY_SIZE(dapm_up_seq);
19905+ seq = dapm_up_seq;
19906+ } else if (event == SND_SOC_DAPM_STREAM_STOP) {
19907+ c = ARRAY_SIZE(dapm_down_seq);
19908+ seq = dapm_down_seq;
19909+ }
19910+
19911+ for(i = 0; i < c; i++) {
19912+ list_for_each_entry(w, &codec->dapm_widgets, list) {
19913+
19914+ /* is widget in stream order */
19915+ if (seq && seq[i] && w->id != seq[i])
19916+ continue;
19917+
19918+ /* vmid - no action */
19919+ if (w->id == snd_soc_dapm_vmid)
19920+ continue;
19921+
19922+ /* active ADC */
19923+ if (w->id == snd_soc_dapm_adc && w->active) {
19924+ in = is_connected_input_ep(w);
19925+ dapm_clear_walk(w->codec);
19926+ w->power = (in != 0) ? 1 : 0;
19927+ dapm_update_bits(w);
19928+ continue;
19929+ }
19930+
19931+ /* active DAC */
19932+ if (w->id == snd_soc_dapm_dac && w->active) {
19933+ out = is_connected_output_ep(w);
19934+ dapm_clear_walk(w->codec);
19935+ w->power = (out != 0) ? 1 : 0;
19936+ dapm_update_bits(w);
19937+ continue;
19938+ }
19939+
19940+ /* programmable gain/attenuation */
19941+ if (w->id == snd_soc_dapm_pga) {
19942+ int on;
19943+ in = is_connected_input_ep(w);
19944+ dapm_clear_walk(w->codec);
19945+ out = is_connected_output_ep(w);
19946+ dapm_clear_walk(w->codec);
19947+ w->power = on = (out != 0 && in != 0) ? 1 : 0;
19948+
19949+ if (!on)
19950+ dapm_set_pga(w, on); /* lower volume to reduce pops */
19951+ dapm_update_bits(w);
19952+ if (on)
19953+ dapm_set_pga(w, on); /* restore volume from zero */
19954+
19955+ continue;
19956+ }
19957+
19958+ /* pre and post event widgets */
19959+ if (w->id == snd_soc_dapm_pre) {
19960+ if (!w->event)
19961+ continue;
19962+
19963+ if (event == SND_SOC_DAPM_STREAM_START) {
19964+ ret = w->event(w, SND_SOC_DAPM_PRE_PMU);
19965+ if (ret < 0)
19966+ return ret;
19967+ } else if (event == SND_SOC_DAPM_STREAM_STOP) {
19968+ ret = w->event(w, SND_SOC_DAPM_PRE_PMD);
19969+ if (ret < 0)
19970+ return ret;
19971+ }
19972+ continue;
19973+ }
19974+ if (w->id == snd_soc_dapm_post) {
19975+ if (!w->event)
19976+ continue;
19977+
19978+ if (event == SND_SOC_DAPM_STREAM_START) {
19979+ ret = w->event(w, SND_SOC_DAPM_POST_PMU);
19980+ if (ret < 0)
19981+ return ret;
19982+ } else if (event == SND_SOC_DAPM_STREAM_STOP) {
19983+ ret = w->event(w, SND_SOC_DAPM_POST_PMD);
19984+ if (ret < 0)
19985+ return ret;
19986+ }
19987+ continue;
19988+ }
19989+
19990+ /* all other widgets */
19991+ in = is_connected_input_ep(w);
19992+ dapm_clear_walk(w->codec);
19993+ out = is_connected_output_ep(w);
19994+ dapm_clear_walk(w->codec);
19995+ power = (out != 0 && in != 0) ? 1 : 0;
19996+ power_change = (w->power == power) ? 0: 1;
19997+ w->power = power;
19998+
19999+ /* call any power change event handlers */
20000+ if (power_change) {
20001+ if (w->event) {
20002+ dbg("power %s event for %s flags %x\n",
20003+ w->power ? "on" : "off", w->name, w->event_flags);
20004+ if (power) {
20005+ /* power up event */
20006+ if (w->event_flags & SND_SOC_DAPM_PRE_PMU) {
20007+ ret = w->event(w, SND_SOC_DAPM_PRE_PMU);
20008+ if (ret < 0)
20009+ return ret;
20010+ }
20011+ dapm_update_bits(w);
20012+ if (w->event_flags & SND_SOC_DAPM_POST_PMU){
20013+ ret = w->event(w, SND_SOC_DAPM_POST_PMU);
20014+ if (ret < 0)
20015+ return ret;
20016+ }
20017+ } else {
20018+ /* power down event */
20019+ if (w->event_flags & SND_SOC_DAPM_PRE_PMD) {
20020+ ret = w->event(w, SND_SOC_DAPM_PRE_PMD);
20021+ if (ret < 0)
20022+ return ret;
20023+ }
20024+ dapm_update_bits(w);
20025+ if (w->event_flags & SND_SOC_DAPM_POST_PMD) {
20026+ ret = w->event(w, SND_SOC_DAPM_POST_PMD);
20027+ if (ret < 0)
20028+ return ret;
20029+ }
20030+ }
20031+ } else
20032+ /* no event handler */
20033+ dapm_update_bits(w);
20034+ }
20035+ }
20036+ }
20037+
20038+ return ret;
20039+}
20040+
20041+#if DAPM_DEBUG
20042+static void dbg_dump_dapm(struct snd_soc_codec* codec, const char *action)
20043+{
20044+ struct snd_soc_dapm_widget *w;
20045+ struct snd_soc_dapm_path *p = NULL;
20046+ int in, out;
20047+
20048+ printk("DAPM %s %s\n", codec->name, action);
20049+
20050+ list_for_each_entry(w, &codec->dapm_widgets, list) {
20051+
20052+ /* only display widgets that effect routing */
20053+ switch (w->id) {
20054+ case snd_soc_dapm_pre:
20055+ case snd_soc_dapm_post:
20056+ case snd_soc_dapm_vmid:
20057+ continue;
20058+ case snd_soc_dapm_mux:
20059+ case snd_soc_dapm_output:
20060+ case snd_soc_dapm_input:
20061+ case snd_soc_dapm_switch:
20062+ case snd_soc_dapm_hp:
20063+ case snd_soc_dapm_mic:
20064+ case snd_soc_dapm_spk:
20065+ case snd_soc_dapm_line:
20066+ case snd_soc_dapm_micbias:
20067+ case snd_soc_dapm_dac:
20068+ case snd_soc_dapm_adc:
20069+ case snd_soc_dapm_pga:
20070+ case snd_soc_dapm_mixer:
20071+ if (w->name) {
20072+ in = is_connected_input_ep(w);
20073+ dapm_clear_walk(w->codec);
20074+ out = is_connected_output_ep(w);
20075+ dapm_clear_walk(w->codec);
20076+ printk("%s: %s in %d out %d\n", w->name,
20077+ w->power ? "On":"Off",in, out);
20078+
20079+ list_for_each_entry(p, &w->sources, list_sink) {
20080+ if (p->connect)
20081+ printk(" in %s %s\n", p->name ? p->name : "static",
20082+ p->source->name);
20083+ }
20084+ list_for_each_entry(p, &w->sinks, list_source) {
20085+ p = list_entry(lp, struct snd_soc_dapm_path, list_source);
20086+ if (p->connect)
20087+ printk(" out %s %s\n", p->name ? p->name : "static",
20088+ p->sink->name);
20089+ }
20090+ }
20091+ break;
20092+ }
20093+ }
20094+}
20095+#endif
20096+
20097+/* test and update the power status of a mux widget */
20098+int dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
20099+ struct snd_kcontrol *kcontrol, int mask, int val, struct soc_enum* e)
20100+{
20101+ struct snd_soc_dapm_path *path;
20102+ int found = 0;
20103+
20104+ if (widget->id != snd_soc_dapm_mux)
20105+ return -ENODEV;
20106+
20107+ if (!snd_soc_test_bits(widget->codec, e->reg, mask, val))
20108+ return 0;
20109+
20110+ /* find dapm widget path assoc with kcontrol */
20111+ list_for_each_entry(path, &widget->codec->dapm_paths, list) {
20112+ if (path->kcontrol != kcontrol)
20113+ continue;
20114+
20115+ if (!path->name || ! e->texts[val])
20116+ continue;
20117+
20118+ found = 1;
20119+ /* we now need to match the string in the enum to the path */
20120+ if (!(strcmp(path->name, e->texts[val])))
20121+ path->connect = 1; /* new connection */
20122+ else
20123+ path->connect = 0; /* old connection must be powered down */
20124+ }
20125+
20126+ if (found)
20127+ dapm_power_widgets(widget->codec, SND_SOC_DAPM_STREAM_NOP);
20128+
20129+ return 0;
20130+}
20131+EXPORT_SYMBOL_GPL(dapm_mux_update_power);
20132+
20133+/* test and update the power status of a mixer widget */
20134+int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
20135+ struct snd_kcontrol *kcontrol, int reg, int val_mask, int val, int invert)
20136+{
20137+ struct snd_soc_dapm_path *path;
20138+ int found = 0;
20139+
20140+ if (widget->id != snd_soc_dapm_mixer)
20141+ return -ENODEV;
20142+
20143+ if (!snd_soc_test_bits(widget->codec, reg, val_mask, val))
20144+ return 0;
20145+
20146+ /* find dapm widget path assoc with kcontrol */
20147+ list_for_each_entry(path, &widget->codec->dapm_paths, list) {
20148+ if (path->kcontrol != kcontrol)
20149+ continue;
20150+
20151+ /* found, now check type */
20152+ found = 1;
20153+ if (val)
20154+ /* new connection */
20155+ path->connect = invert ? 0:1;
20156+ else
20157+ /* old connection must be powered down */
20158+ path->connect = invert ? 1:0;
20159+ break;
20160+ }
20161+
20162+ if (found)
20163+ dapm_power_widgets(widget->codec, SND_SOC_DAPM_STREAM_NOP);
20164+
20165+ return 0;
20166+}
20167+EXPORT_SYMBOL_GPL(dapm_mixer_update_power);
20168+
20169+/* show dapm widget status in sys fs */
20170+static ssize_t dapm_widget_show(struct device *dev,
20171+ struct device_attribute *attr, char *buf)
20172+{
20173+ struct snd_soc_device *devdata = dev_get_drvdata(dev);
20174+ struct snd_soc_codec *codec = devdata->codec;
20175+ struct snd_soc_dapm_widget *w;
20176+ int count = 0;
20177+ char *state = "not set";
20178+
20179+ list_for_each_entry(w, &codec->dapm_widgets, list) {
20180+
20181+ /* only display widgets that burnm power */
20182+ switch (w->id) {
20183+ case snd_soc_dapm_hp:
20184+ case snd_soc_dapm_mic:
20185+ case snd_soc_dapm_spk:
20186+ case snd_soc_dapm_line:
20187+ case snd_soc_dapm_micbias:
20188+ case snd_soc_dapm_dac:
20189+ case snd_soc_dapm_adc:
20190+ case snd_soc_dapm_pga:
20191+ case snd_soc_dapm_mixer:
20192+ if (w->name)
20193+ count += sprintf(buf + count, "%s: %s\n",
20194+ w->name, w->power ? "On":"Off");
20195+ break;
20196+ default:
20197+ break;
20198+ }
20199+ }
20200+
20201+ switch(codec->dapm_state){
20202+ case SNDRV_CTL_POWER_D0:
20203+ state = "D0";
20204+ break;
20205+ case SNDRV_CTL_POWER_D1:
20206+ state = "D1";
20207+ break;
20208+ case SNDRV_CTL_POWER_D2:
20209+ state = "D2";
20210+ break;
20211+ case SNDRV_CTL_POWER_D3hot:
20212+ state = "D3hot";
20213+ break;
20214+ case SNDRV_CTL_POWER_D3cold:
20215+ state = "D3cold";
20216+ break;
20217+ }
20218+ count += sprintf(buf + count, "PM State: %s\n", state);
20219+
20220+ return count;
20221+}
20222+
20223+static DEVICE_ATTR(dapm_widget, 0444, dapm_widget_show, NULL);
20224+
20225+int snd_soc_dapm_sys_add(struct device *dev)
20226+{
20227+ int ret = 0;
20228+
20229+ if (dapm_status)
20230+ ret = device_create_file(dev, &dev_attr_dapm_widget);
20231+
20232+ return ret;
20233+}
20234+
20235+static void snd_soc_dapm_sys_remove(struct device *dev)
20236+{
20237+ if (dapm_status)
20238+ device_remove_file(dev, &dev_attr_dapm_widget);
20239+}
20240+
20241+/* free all dapm widgets and resources */
20242+void dapm_free_widgets(struct snd_soc_codec *codec)
20243+{
20244+ struct snd_soc_dapm_widget *w, *next_w;
20245+ struct snd_soc_dapm_path *p, *next_p;
20246+
20247+ list_for_each_entry_safe(w, next_w, &codec->dapm_widgets, list) {
20248+ list_del(&w->list);
20249+ kfree(w);
20250+ }
20251+
20252+ list_for_each_entry_safe(p, next_p, &codec->dapm_paths, list) {
20253+ list_del(&p->list);
20254+ kfree(p->long_name);
20255+ kfree(p);
20256+ }
20257+}
20258+
20259+/**
20260+ * snd_soc_dapm_sync_endpoints - scan and power dapm paths
20261+ * @codec: audio codec
20262+ *
20263+ * Walks all dapm audio paths and powers widgets according to their
20264+ * stream or path usage.
20265+ *
20266+ * Returns 0 for success.
20267+ */
20268+int snd_soc_dapm_sync_endpoints(struct snd_soc_codec *codec)
20269+{
20270+ return dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP);
20271+}
20272+EXPORT_SYMBOL_GPL(snd_soc_dapm_sync_endpoints);
20273+
20274+/**
20275+ * snd_soc_dapm_connect_input - connect dapm widgets
20276+ * @codec: audio codec
20277+ * @sink: name of target widget
20278+ * @control: mixer control name
20279+ * @source: name of source name
20280+ *
20281+ * Connects 2 dapm widgets together via a named audio path. The sink is
20282+ * the widget receiving the audio signal, whilst the source is the sender
20283+ * of the audio signal.
20284+ *
20285+ * Returns 0 for success else error.
20286+ */
20287+int snd_soc_dapm_connect_input(struct snd_soc_codec *codec, const char *sink,
20288+ const char * control, const char *source)
20289+{
20290+ struct snd_soc_dapm_path *path;
20291+ struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w;
20292+ int ret = 0;
20293+
20294+ /* find src and dest widgets */
20295+ list_for_each_entry(w, &codec->dapm_widgets, list) {
20296+
20297+ if (!wsink && !(strcmp(w->name, sink))) {
20298+ wsink = w;
20299+ continue;
20300+ }
20301+ if (!wsource && !(strcmp(w->name, source))) {
20302+ wsource = w;
20303+ }
20304+ }
20305+
20306+ if (wsource == NULL || wsink == NULL)
20307+ return -ENODEV;
20308+
20309+ path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL);
20310+ if (!path)
20311+ return -ENOMEM;
20312+
20313+ path->source = wsource;
20314+ path->sink = wsink;
20315+ INIT_LIST_HEAD(&path->list);
20316+ INIT_LIST_HEAD(&path->list_source);
20317+ INIT_LIST_HEAD(&path->list_sink);
20318+
20319+ /* check for external widgets */
20320+ if (wsink->id == snd_soc_dapm_input) {
20321+ if (wsource->id == snd_soc_dapm_micbias ||
20322+ wsource->id == snd_soc_dapm_mic ||
20323+ wsink->id == snd_soc_dapm_line)
20324+ wsink->ext = 1;
20325+ }
20326+ if (wsource->id == snd_soc_dapm_output) {
20327+ if (wsink->id == snd_soc_dapm_spk ||
20328+ wsink->id == snd_soc_dapm_hp ||
20329+ wsink->id == snd_soc_dapm_line)
20330+ wsource->ext = 1;
20331+ }
20332+
20333+ /* connect static paths */
20334+ if (control == NULL) {
20335+ list_add(&path->list, &codec->dapm_paths);
20336+ list_add(&path->list_sink, &wsink->sources);
20337+ list_add(&path->list_source, &wsource->sinks);
20338+ path->connect = 1;
20339+ return 0;
20340+ }
20341+
20342+ /* connect dynamic paths */
20343+ switch(wsink->id) {
20344+ case snd_soc_dapm_adc:
20345+ case snd_soc_dapm_dac:
20346+ case snd_soc_dapm_pga:
20347+ case snd_soc_dapm_input:
20348+ case snd_soc_dapm_output:
20349+ case snd_soc_dapm_micbias:
20350+ case snd_soc_dapm_vmid:
20351+ case snd_soc_dapm_pre:
20352+ case snd_soc_dapm_post:
20353+ list_add(&path->list, &codec->dapm_paths);
20354+ list_add(&path->list_sink, &wsink->sources);
20355+ list_add(&path->list_source, &wsource->sinks);
20356+ path->connect = 1;
20357+ return 0;
20358+ case snd_soc_dapm_mux:
20359+ ret = dapm_connect_mux(codec, wsource, wsink, path, control,
20360+ &wsink->kcontrols[0]);
20361+ if (ret != 0)
20362+ goto err;
20363+ break;
20364+ case snd_soc_dapm_switch:
20365+ case snd_soc_dapm_mixer:
20366+ ret = dapm_connect_mixer(codec, wsource, wsink, path, control);
20367+ if (ret != 0)
20368+ goto err;
20369+ break;
20370+ case snd_soc_dapm_hp:
20371+ case snd_soc_dapm_mic:
20372+ case snd_soc_dapm_line:
20373+ case snd_soc_dapm_spk:
20374+ list_add(&path->list, &codec->dapm_paths);
20375+ list_add(&path->list_sink, &wsink->sources);
20376+ list_add(&path->list_source, &wsource->sinks);
20377+ path->connect = 0;
20378+ return 0;
20379+ }
20380+ return 0;
20381+
20382+err:
20383+ printk(KERN_WARNING "asoc: no dapm match for %s --> %s --> %s\n", source,
20384+ control, sink);
20385+ kfree(path);
20386+ return ret;
20387+}
20388+EXPORT_SYMBOL_GPL(snd_soc_dapm_connect_input);
20389+
20390+/**
20391+ * snd_soc_dapm_new_widgets - add new dapm widgets
20392+ * @codec: audio codec
20393+ *
20394+ * Checks the codec for any new dapm widgets and creates them if found.
20395+ *
20396+ * Returns 0 for success.
20397+ */
20398+int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec)
20399+{
20400+ struct snd_soc_dapm_widget *w;
20401+
20402+ mutex_lock(&codec->mutex);
20403+ list_for_each_entry(w, &codec->dapm_widgets, list)
20404+ {
20405+ if (w->new)
20406+ continue;
20407+
20408+ switch(w->id) {
20409+ case snd_soc_dapm_switch:
20410+ case snd_soc_dapm_mixer:
20411+ dapm_new_mixer(codec, w);
20412+ break;
20413+ case snd_soc_dapm_mux:
20414+ dapm_new_mux(codec, w);
20415+ break;
20416+ case snd_soc_dapm_adc:
20417+ case snd_soc_dapm_dac:
20418+ case snd_soc_dapm_pga:
20419+ dapm_new_pga(codec, w);
20420+ break;
20421+ case snd_soc_dapm_input:
20422+ case snd_soc_dapm_output:
20423+ case snd_soc_dapm_micbias:
20424+ case snd_soc_dapm_spk:
20425+ case snd_soc_dapm_hp:
20426+ case snd_soc_dapm_mic:
20427+ case snd_soc_dapm_line:
20428+ case snd_soc_dapm_vmid:
20429+ case snd_soc_dapm_pre:
20430+ case snd_soc_dapm_post:
20431+ break;
20432+ }
20433+ w->new = 1;
20434+ }
20435+
20436+ dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP);
20437+ mutex_unlock(&codec->mutex);
20438+ return 0;
20439+}
20440+EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets);
20441+
20442+/**
20443+ * snd_soc_dapm_get_volsw - dapm mixer get callback
20444+ * @kcontrol: mixer control
20445+ * @uinfo: control element information
20446+ *
20447+ * Callback to get the value of a dapm mixer control.
20448+ *
20449+ * Returns 0 for success.
20450+ */
20451+int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
20452+ struct snd_ctl_elem_value *ucontrol)
20453+{
20454+ struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
20455+ int reg = kcontrol->private_value & 0xff;
20456+ int shift = (kcontrol->private_value >> 8) & 0x0f;
20457+ int rshift = (kcontrol->private_value >> 12) & 0x0f;
20458+ int mask = (kcontrol->private_value >> 16) & 0xff;
20459+ int invert = (kcontrol->private_value >> 24) & 0x01;
20460+
20461+ /* return the saved value if we are powered down */
20462+ if (widget->id == snd_soc_dapm_pga && !widget->power) {
20463+ ucontrol->value.integer.value[0] = widget->saved_value;
20464+ return 0;
20465+ }
20466+
20467+ ucontrol->value.integer.value[0] =
20468+ (snd_soc_read(widget->codec, reg) >> shift) & mask;
20469+ if (shift != rshift)
20470+ ucontrol->value.integer.value[1] =
20471+ (snd_soc_read(widget->codec, reg) >> rshift) & mask;
20472+ if (invert) {
20473+ ucontrol->value.integer.value[0] =
20474+ mask - ucontrol->value.integer.value[0];
20475+ if (shift != rshift)
20476+ ucontrol->value.integer.value[1] =
20477+ mask - ucontrol->value.integer.value[1];
20478+ }
20479+
20480+ return 0;
20481+}
20482+EXPORT_SYMBOL_GPL(snd_soc_dapm_get_volsw);
20483+
20484+/**
20485+ * snd_soc_dapm_put_volsw - dapm mixer set callback
20486+ * @kcontrol: mixer control
20487+ * @uinfo: control element information
20488+ *
20489+ * Callback to set the value of a dapm mixer control.
20490+ *
20491+ * Returns 0 for success.
20492+ */
20493+int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
20494+ struct snd_ctl_elem_value *ucontrol)
20495+{
20496+ struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
20497+ int reg = kcontrol->private_value & 0xff;
20498+ int shift = (kcontrol->private_value >> 8) & 0x0f;
20499+ int rshift = (kcontrol->private_value >> 12) & 0x0f;
20500+ int mask = (kcontrol->private_value >> 16) & 0xff;
20501+ int invert = (kcontrol->private_value >> 24) & 0x01;
20502+ unsigned short val, val2, val_mask;
20503+ int ret;
20504+
20505+ val = (ucontrol->value.integer.value[0] & mask);
20506+
20507+ if (invert)
20508+ val = mask - val;
20509+ val_mask = mask << shift;
20510+ val = val << shift;
20511+ if (shift != rshift) {
20512+ val2 = (ucontrol->value.integer.value[1] & mask);
20513+ if (invert)
20514+ val2 = mask - val2;
20515+ val_mask |= mask << rshift;
20516+ val |= val2 << rshift;
20517+ }
20518+
20519+ mutex_lock(&widget->codec->mutex);
20520+ widget->value = val;
20521+
20522+ /* save volume value if the widget is powered down */
20523+ if (widget->id == snd_soc_dapm_pga && !widget->power) {
20524+ widget->saved_value = val;
20525+ mutex_unlock(&widget->codec->mutex);
20526+ return 1;
20527+ }
20528+
20529+ dapm_mixer_update_power(widget, kcontrol, reg, val_mask, val, invert);
20530+ if (widget->event) {
20531+ if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
20532+ ret = widget->event(widget, SND_SOC_DAPM_PRE_REG);
20533+ if (ret < 0)
20534+ goto out;
20535+ }
20536+ ret = snd_soc_update_bits(widget->codec, reg, val_mask, val);
20537+ if (widget->event_flags & SND_SOC_DAPM_POST_REG)
20538+ ret = widget->event(widget, SND_SOC_DAPM_POST_REG);
20539+ } else
20540+ ret = snd_soc_update_bits(widget->codec, reg, val_mask, val);
20541+
20542+out:
20543+ mutex_unlock(&widget->codec->mutex);
20544+ return ret;
20545+}
20546+EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw);
20547+
20548+/**
20549+ * snd_soc_dapm_get_enum_double - dapm enumerated double mixer get callback
20550+ * @kcontrol: mixer control
20551+ * @uinfo: control element information
20552+ *
20553+ * Callback to get the value of a dapm enumerated double mixer control.
20554+ *
20555+ * Returns 0 for success.
20556+ */
20557+int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol,
20558+ struct snd_ctl_elem_value *ucontrol)
20559+{
20560+ struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
20561+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
20562+ unsigned short val, bitmask;
20563+
20564+ for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
20565+ ;
20566+ val = snd_soc_read(widget->codec, e->reg);
20567+ ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (bitmask - 1);
20568+ if (e->shift_l != e->shift_r)
20569+ ucontrol->value.enumerated.item[1] =
20570+ (val >> e->shift_r) & (bitmask - 1);
20571+
20572+ return 0;
20573+}
20574+EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_double);
20575+
20576+/**
20577+ * snd_soc_dapm_put_enum_double - dapm enumerated double mixer set callback
20578+ * @kcontrol: mixer control
20579+ * @uinfo: control element information
20580+ *
20581+ * Callback to set the value of a dapm enumerated double mixer control.
20582+ *
20583+ * Returns 0 for success.
20584+ */
20585+int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
20586+ struct snd_ctl_elem_value *ucontrol)
20587+{
20588+ struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
20589+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
20590+ unsigned short val, mux;
20591+ unsigned short mask, bitmask;
20592+ int ret = 0;
20593+
20594+ for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
20595+ ;
20596+ if (ucontrol->value.enumerated.item[0] > e->mask - 1)
20597+ return -EINVAL;
20598+ mux = ucontrol->value.enumerated.item[0];
20599+ val = mux << e->shift_l;
20600+ mask = (bitmask - 1) << e->shift_l;
20601+ if (e->shift_l != e->shift_r) {
20602+ if (ucontrol->value.enumerated.item[1] > e->mask - 1)
20603+ return -EINVAL;
20604+ val |= ucontrol->value.enumerated.item[1] << e->shift_r;
20605+ mask |= (bitmask - 1) << e->shift_r;
20606+ }
20607+
20608+ mutex_lock(&widget->codec->mutex);
20609+ widget->value = val;
20610+ dapm_mux_update_power(widget, kcontrol, mask, mux, e);
20611+ if (widget->event) {
20612+ if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
20613+ ret = widget->event(widget, SND_SOC_DAPM_PRE_REG);
20614+ if (ret < 0)
20615+ goto out;
20616+ }
20617+ ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
20618+ if (widget->event_flags & SND_SOC_DAPM_POST_REG)
20619+ ret = widget->event(widget, SND_SOC_DAPM_POST_REG);
20620+ } else
20621+ ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
20622+
20623+out:
20624+ mutex_unlock(&widget->codec->mutex);
20625+ return ret;
20626+}
20627+EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double);
20628+
20629+/**
20630+ * snd_soc_dapm_new_control - create new dapm control
20631+ * @codec: audio codec
20632+ * @widget: widget template
20633+ *
20634+ * Creates a new dapm control based upon the template.
20635+ *
20636+ * Returns 0 for success else error.
20637+ */
20638+int snd_soc_dapm_new_control(struct snd_soc_codec *codec,
20639+ const struct snd_soc_dapm_widget *widget)
20640+{
20641+ struct snd_soc_dapm_widget *w;
20642+
20643+ if ((w = dapm_cnew_widget(widget)) == NULL)
20644+ return -ENOMEM;
20645+
20646+ w->codec = codec;
20647+ INIT_LIST_HEAD(&w->sources);
20648+ INIT_LIST_HEAD(&w->sinks);
20649+ INIT_LIST_HEAD(&w->list);
20650+ list_add(&w->list, &codec->dapm_widgets);
20651+
20652+ /* machine layer set ups unconnected pins and insertions */
20653+ w->connected = 1;
20654+ return 0;
20655+}
20656+EXPORT_SYMBOL_GPL(snd_soc_dapm_new_control);
20657+
20658+/**
20659+ * snd_soc_dapm_stream_event - send a stream event to the dapm core
20660+ * @codec: audio codec
20661+ * @stream: stream name
20662+ * @event: stream event
20663+ *
20664+ * Sends a stream event to the dapm core. The core then makes any
20665+ * necessary widget power changes.
20666+ *
20667+ * Returns 0 for success else error.
20668+ */
20669+int snd_soc_dapm_stream_event(struct snd_soc_codec *codec,
20670+ char *stream, int event)
20671+{
20672+ struct snd_soc_dapm_widget *w;
20673+
20674+ mutex_lock(&codec->mutex);
20675+ list_for_each_entry(w, &codec->dapm_widgets, list)
20676+ {
20677+ if (!w->sname)
20678+ continue;
20679+ dbg("widget %s\n %s stream %s event %d\n", w->name, w->sname,
20680+ stream, event);
20681+ if (strstr(w->sname, stream)) {
20682+ switch(event) {
20683+ case SND_SOC_DAPM_STREAM_START:
20684+ w->active = 1;
20685+ break;
20686+ case SND_SOC_DAPM_STREAM_STOP:
20687+ w->active = 0;
20688+ break;
20689+ case SND_SOC_DAPM_STREAM_SUSPEND:
20690+ if (w->active)
20691+ w->suspend = 1;
20692+ w->active = 0;
20693+ break;
20694+ case SND_SOC_DAPM_STREAM_RESUME:
20695+ if (w->suspend) {
20696+ w->active = 1;
20697+ w->suspend = 0;
20698+ }
20699+ break;
20700+ case SND_SOC_DAPM_STREAM_PAUSE_PUSH:
20701+ break;
20702+ case SND_SOC_DAPM_STREAM_PAUSE_RELEASE:
20703+ break;
20704+ }
20705+ }
20706+ }
20707+ mutex_unlock(&codec->mutex);
20708+
20709+ dapm_power_widgets(codec, event);
20710+ dump_dapm(codec, __FUNCTION__);
20711+ return 0;
20712+}
20713+EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_event);
20714+
20715+/**
20716+ * snd_soc_dapm_set_endpoint - set audio endpoint status
20717+ * @codec: audio codec
20718+ * @endpoint: audio signal endpoint (or start point)
20719+ * @status: point status
20720+ *
20721+ * Set audio endpoint status - connected or disconnected.
20722+ *
20723+ * Returns 0 for success else error.
20724+ */
20725+int snd_soc_dapm_set_endpoint(struct snd_soc_codec *codec,
20726+ char *endpoint, int status)
20727+{
20728+ struct snd_soc_dapm_widget *w;
20729+
20730+ list_for_each_entry(w, &codec->dapm_widgets, list) {
20731+ if (!strcmp(w->name, endpoint)) {
20732+ w->connected = status;
20733+ }
20734+ }
20735+
20736+ return 0;
20737+}
20738+EXPORT_SYMBOL_GPL(snd_soc_dapm_set_endpoint);
20739+
20740+/**
20741+ * snd_soc_dapm_free - free dapm resources
20742+ * @socdev: SoC device
20743+ *
20744+ * Free all dapm widgets and resources.
20745+ */
20746+void snd_soc_dapm_free(struct snd_soc_device *socdev)
20747+{
20748+ struct snd_soc_codec *codec = socdev->codec;
20749+
20750+ snd_soc_dapm_sys_remove(socdev->dev);
20751+ dapm_free_widgets(codec);
20752+}
20753+EXPORT_SYMBOL_GPL(snd_soc_dapm_free);
20754+
20755+/* Module information */
20756+MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
20757+MODULE_DESCRIPTION("Dynamic Audio Power Management core for ALSA SoC");
20758+MODULE_LICENSE("GPL");
20759Index: linux-2.6-pxa-new/sound/soc/soc-core.c
20760===================================================================
20761--- /dev/null
20762+++ linux-2.6-pxa-new/sound/soc/soc-core.c
20763@@ -0,0 +1,2063 @@
20764+/*
20765+ * soc-core.c -- ALSA SoC Audio Layer
20766+ *
20767+ * Copyright 2005 Wolfson Microelectronics PLC.
20768+ * Author: Liam Girdwood
20769+ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
20770+ *
20771+ * This program is free software; you can redistribute it and/or modify it
20772+ * under the terms of the GNU General Public License as published by the
20773+ * Free Software Foundation; either version 2 of the License, or (at your
20774+ * option) any later version.
20775+ *
20776+ * Revision history
20777+ * 12th Aug 2005 Initial version.
20778+ * 25th Oct 2005 Working Codec, Interface and Platform registration.
20779+ *
20780+ * TODO:
20781+ * o Add hw rules to enforce rates, etc.
20782+ * o More testing with other codecs/machines.
20783+ * o Add more codecs and platforms to ensure good API coverage.
20784+ * o Support TDM on PCM and I2S
20785+ */
20786+
20787+#include <linux/module.h>
20788+#include <linux/moduleparam.h>
20789+#include <linux/init.h>
20790+#include <linux/delay.h>
20791+#include <linux/pm.h>
20792+#include <linux/bitops.h>
20793+#include <linux/platform_device.h>
20794+#include <sound/driver.h>
20795+#include <sound/core.h>
20796+#include <sound/pcm.h>
20797+#include <sound/pcm_params.h>
20798+#include <sound/soc.h>
20799+#include <sound/soc-dapm.h>
20800+#include <sound/initval.h>
20801+
20802+/* debug */
20803+#define SOC_DEBUG 1
20804+#if SOC_DEBUG
20805+#define dbg(format, arg...) printk(format, ## arg)
20806+#else
20807+#define dbg(format, arg...)
20808+#endif
20809+/* debug DAI capabilities matching */
20810+#define SOC_DEBUG_DAI 1
20811+#if SOC_DEBUG_DAI
20812+#define dbgc(format, arg...) printk(format, ## arg)
20813+#else
20814+#define dbgc(format, arg...)
20815+#endif
20816+
20817+#define CODEC_CPU(codec, cpu) ((codec << 4) | cpu)
20818+
20819+static DEFINE_MUTEX(pcm_mutex);
20820+static DEFINE_MUTEX(io_mutex);
20821+static struct workqueue_struct *soc_workq;
20822+static struct work_struct soc_stream_work;
20823+static DECLARE_WAIT_QUEUE_HEAD(soc_pm_waitq);
20824+
20825+/* supported sample rates */
20826+/* ATTENTION: these values depend on the definition in pcm.h! */
20827+static const unsigned int rates[] = {
20828+ 5512, 8000, 11025, 16000, 22050, 32000, 44100,
20829+ 48000, 64000, 88200, 96000, 176400, 192000
20830+};
20831+
20832+/*
20833+ * This is a timeout to do a DAPM powerdown after a stream is closed().
20834+ * It can be used to eliminate pops between different playback streams, e.g.
20835+ * between two audio tracks.
20836+ */
20837+static int pmdown_time = 5000;
20838+module_param(pmdown_time, int, 0);
20839+MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)");
20840+
20841+#ifdef CONFIG_SND_SOC_AC97_BUS
20842+/* unregister ac97 codec */
20843+static int soc_ac97_dev_unregister(struct snd_soc_codec *codec)
20844+{
20845+ if (codec->ac97->dev.bus)
20846+ device_unregister(&codec->ac97->dev);
20847+ return 0;
20848+}
20849+
20850+/* stop no dev release warning */
20851+static void soc_ac97_device_release(struct device *dev){}
20852+
20853+/* register ac97 codec to bus */
20854+static int soc_ac97_dev_register(struct snd_soc_codec *codec)
20855+{
20856+ int err;
20857+
20858+ codec->ac97->dev.bus = &ac97_bus_type;
20859+ codec->ac97->dev.parent = NULL;
20860+ codec->ac97->dev.release = soc_ac97_device_release;
20861+
20862+ snprintf(codec->ac97->dev.bus_id, BUS_ID_SIZE, "%d-%d:%s",
20863+ codec->card->number, 0, codec->name);
20864+ err = device_register(&codec->ac97->dev);
20865+ if (err < 0) {
20866+ snd_printk(KERN_ERR "Can't register ac97 bus\n");
20867+ codec->ac97->dev.bus = NULL;
20868+ return err;
20869+ }
20870+ return 0;
20871+}
20872+#endif
20873+
20874+static inline const char* get_dai_name(int type)
20875+{
20876+ switch(type) {
20877+ case SND_SOC_DAI_AC97:
20878+ return "AC97";
20879+ case SND_SOC_DAI_I2S:
20880+ return "I2S";
20881+ case SND_SOC_DAI_PCM:
20882+ return "PCM";
20883+ }
20884+ return NULL;
20885+}
20886+
20887+/* get rate format from rate */
20888+static inline int soc_get_rate_format(int rate)
20889+{
20890+ int i;
20891+
20892+ for (i = 0; i < ARRAY_SIZE(rates); i++) {
20893+ if (rates[i] == rate)
20894+ return 1 << i;
20895+ }
20896+ return 0;
20897+}
20898+
20899+/* gets the audio system mclk/sysclk for the given parameters */
20900+static unsigned inline int soc_get_mclk(struct snd_soc_pcm_runtime *rtd,
20901+ struct snd_soc_clock_info *info)
20902+{
20903+ struct snd_soc_device *socdev = rtd->socdev;
20904+ struct snd_soc_machine *machine = socdev->machine;
20905+ int i;
20906+
20907+ /* find the matching machine config and get it's mclk for the given
20908+ * sample rate and hardware format */
20909+ for(i = 0; i < machine->num_links; i++) {
20910+ if (machine->dai_link[i].cpu_dai == rtd->cpu_dai &&
20911+ machine->dai_link[i].config_sysclk)
20912+ return machine->dai_link[i].config_sysclk(rtd, info);
20913+ }
20914+ return 0;
20915+}
20916+
20917+/* changes a bitclk multiplier mask to a divider mask */
20918+static u64 soc_bfs_rcw_to_div(u64 bfs, int rate, unsigned int mclk,
20919+ unsigned int pcmfmt, unsigned int chn)
20920+{
20921+ int i, j;
20922+ u64 bfs_ = 0;
20923+ int size = snd_pcm_format_physical_width(pcmfmt), min = 0;
20924+
20925+ if (size <= 0)
20926+ return 0;
20927+
20928+ /* the minimum bit clock that has enough bandwidth */
20929+ min = size * rate * chn;
20930+ dbgc("rcw --> div min bclk %d with mclk %d\n", min, mclk);
20931+
20932+ for (i = 0; i < 64; i++) {
20933+ if ((bfs >> i) & 0x1) {
20934+ j = min * (i + 1);
20935+ bfs_ |= SND_SOC_FSBD(mclk/j);
20936+ dbgc("rcw --> div support mult %d\n",
20937+ SND_SOC_FSBD_REAL(1<<i));
20938+ }
20939+ }
20940+
20941+ return bfs_;
20942+}
20943+
20944+/* changes a bitclk divider mask to a multiplier mask */
20945+static u64 soc_bfs_div_to_rcw(u64 bfs, int rate, unsigned int mclk,
20946+ unsigned int pcmfmt, unsigned int chn)
20947+{
20948+ int i, j;
20949+ u64 bfs_ = 0;
20950+
20951+ int size = snd_pcm_format_physical_width(pcmfmt), min = 0;
20952+
20953+ if (size <= 0)
20954+ return 0;
20955+
20956+ /* the minimum bit clock that has enough bandwidth */
20957+ min = size * rate * chn;
20958+ dbgc("div to rcw min bclk %d with mclk %d\n", min, mclk);
20959+
20960+ for (i = 0; i < 64; i++) {
20961+ if ((bfs >> i) & 0x1) {
20962+ j = mclk / (i + 1);
20963+ if (j >= min) {
20964+ bfs_ |= SND_SOC_FSBW(j/min);
20965+ dbgc("div --> rcw support div %d\n",
20966+ SND_SOC_FSBW_REAL(1<<i));
20967+ }
20968+ }
20969+ }
20970+
20971+ return bfs_;
20972+}
20973+
20974+/* changes a constant bitclk to a multiplier mask */
20975+static u64 soc_bfs_rate_to_rcw(u64 bfs, int rate, unsigned int mclk,
20976+ unsigned int pcmfmt, unsigned int chn)
20977+{
20978+ unsigned int bfs_ = rate * bfs;
20979+ int size = snd_pcm_format_physical_width(pcmfmt), min = 0;
20980+
20981+ if (size <= 0)
20982+ return 0;
20983+
20984+ /* the minimum bit clock that has enough bandwidth */
20985+ min = size * rate * chn;
20986+ dbgc("rate --> rcw min bclk %d with mclk %d\n", min, mclk);
20987+
20988+ if (bfs_ < min)
20989+ return 0;
20990+ else {
20991+ bfs_ = SND_SOC_FSBW(bfs_/min);
20992+ dbgc("rate --> rcw support div %d\n", SND_SOC_FSBW_REAL(bfs_));
20993+ return bfs_;
20994+ }
20995+}
20996+
20997+/* changes a bitclk multiplier mask to a divider mask */
20998+static u64 soc_bfs_rate_to_div(u64 bfs, int rate, unsigned int mclk,
20999+ unsigned int pcmfmt, unsigned int chn)
21000+{
21001+ unsigned int bfs_ = rate * bfs;
21002+ int size = snd_pcm_format_physical_width(pcmfmt), min = 0;
21003+
21004+ if (size <= 0)
21005+ return 0;
21006+
21007+ /* the minimum bit clock that has enough bandwidth */
21008+ min = size * rate * chn;
21009+ dbgc("rate --> div min bclk %d with mclk %d\n", min, mclk);
21010+
21011+ if (bfs_ < min)
21012+ return 0;
21013+ else {
21014+ bfs_ = SND_SOC_FSBW(mclk/bfs_);
21015+ dbgc("rate --> div support div %d\n", SND_SOC_FSBD_REAL(bfs_));
21016+ return bfs_;
21017+ }
21018+}
21019+
21020+/* Matches codec DAI and SoC CPU DAI hardware parameters */
21021+static int soc_hw_match_params(struct snd_pcm_substream *substream,
21022+ struct snd_pcm_hw_params *params)
21023+{
21024+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
21025+ struct snd_soc_dai_mode *codec_dai_mode = NULL;
21026+ struct snd_soc_dai_mode *cpu_dai_mode = NULL;
21027+ struct snd_soc_clock_info clk_info;
21028+ unsigned int fs, mclk, rate = params_rate(params),
21029+ chn, j, k, cpu_bclk, codec_bclk, pcmrate;
21030+ u16 fmt = 0;
21031+ u64 codec_bfs, cpu_bfs;
21032+
21033+ dbg("asoc: match version %s\n", SND_SOC_VERSION);
21034+ clk_info.rate = rate;
21035+ pcmrate = soc_get_rate_format(rate);
21036+
21037+ /* try and find a match from the codec and cpu DAI capabilities */
21038+ for (j = 0; j < rtd->codec_dai->caps.num_modes; j++) {
21039+ for (k = 0; k < rtd->cpu_dai->caps.num_modes; k++) {
21040+ codec_dai_mode = &rtd->codec_dai->caps.mode[j];
21041+ cpu_dai_mode = &rtd->cpu_dai->caps.mode[k];
21042+
21043+ if (!(codec_dai_mode->pcmrate & cpu_dai_mode->pcmrate &
21044+ pcmrate)) {
21045+ dbgc("asoc: DAI[%d:%d] failed to match rate\n", j, k);
21046+ continue;
21047+ }
21048+
21049+ fmt = codec_dai_mode->fmt & cpu_dai_mode->fmt;
21050+ if (!(fmt & SND_SOC_DAIFMT_FORMAT_MASK)) {
21051+ dbgc("asoc: DAI[%d:%d] failed to match format\n", j, k);
21052+ continue;
21053+ }
21054+
21055+ if (!(fmt & SND_SOC_DAIFMT_CLOCK_MASK)) {
21056+ dbgc("asoc: DAI[%d:%d] failed to match clock masters\n",
21057+ j, k);
21058+ continue;
21059+ }
21060+
21061+ if (!(fmt & SND_SOC_DAIFMT_INV_MASK)) {
21062+ dbgc("asoc: DAI[%d:%d] failed to match invert\n", j, k);
21063+ continue;
21064+ }
21065+
21066+ if (!(codec_dai_mode->pcmfmt & cpu_dai_mode->pcmfmt)) {
21067+ dbgc("asoc: DAI[%d:%d] failed to match pcm format\n", j, k);
21068+ continue;
21069+ }
21070+
21071+ if (!(codec_dai_mode->pcmdir & cpu_dai_mode->pcmdir)) {
21072+ dbgc("asoc: DAI[%d:%d] failed to match direction\n", j, k);
21073+ continue;
21074+ }
21075+
21076+ /* todo - still need to add tdm selection */
21077+ rtd->cpu_dai->dai_runtime.fmt =
21078+ rtd->codec_dai->dai_runtime.fmt =
21079+ 1 << (ffs(fmt & SND_SOC_DAIFMT_FORMAT_MASK) -1) |
21080+ 1 << (ffs(fmt & SND_SOC_DAIFMT_CLOCK_MASK) - 1) |
21081+ 1 << (ffs(fmt & SND_SOC_DAIFMT_INV_MASK) - 1);
21082+ clk_info.bclk_master =
21083+ rtd->cpu_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CLOCK_MASK;
21084+
21085+ /* make sure the ratio between rate and master
21086+ * clock is acceptable*/
21087+ fs = (cpu_dai_mode->fs & codec_dai_mode->fs);
21088+ if (fs == 0) {
21089+ dbgc("asoc: DAI[%d:%d] failed to match FS\n", j, k);
21090+ continue;
21091+ }
21092+ clk_info.fs = rtd->cpu_dai->dai_runtime.fs =
21093+ rtd->codec_dai->dai_runtime.fs = fs;
21094+
21095+ /* calculate audio system clocking using slowest clocks possible*/
21096+ mclk = soc_get_mclk(rtd, &clk_info);
21097+ if (mclk == 0) {
21098+ dbgc("asoc: DAI[%d:%d] configuration not clockable\n", j, k);
21099+ dbgc("asoc: rate %d fs %d master %x\n", rate, fs,
21100+ clk_info.bclk_master);
21101+ continue;
21102+ }
21103+
21104+ /* calculate word size (per channel) and frame size */
21105+ rtd->codec_dai->dai_runtime.pcmfmt =
21106+ rtd->cpu_dai->dai_runtime.pcmfmt =
21107+ 1 << params_format(params);
21108+
21109+ chn = params_channels(params);
21110+ /* i2s always has left and right */
21111+ if (params_channels(params) == 1 &&
21112+ rtd->cpu_dai->dai_runtime.fmt & (SND_SOC_DAIFMT_I2S |
21113+ SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_LEFT_J))
21114+ chn <<= 1;
21115+
21116+ /* Calculate bfs - the ratio between bitclock and the sample rate
21117+ * We must take into consideration the dividers and multipliers
21118+ * used in the codec and cpu DAI modes. We always choose the
21119+ * lowest possible clocks to reduce power.
21120+ */
21121+ switch (CODEC_CPU(codec_dai_mode->flags, cpu_dai_mode->flags)) {
21122+ case CODEC_CPU(SND_SOC_DAI_BFS_DIV, SND_SOC_DAI_BFS_DIV):
21123+ /* cpu & codec bfs dividers */
21124+ rtd->cpu_dai->dai_runtime.bfs =
21125+ rtd->codec_dai->dai_runtime.bfs =
21126+ 1 << (fls(codec_dai_mode->bfs & cpu_dai_mode->bfs) - 1);
21127+ break;
21128+ case CODEC_CPU(SND_SOC_DAI_BFS_DIV, SND_SOC_DAI_BFS_RCW):
21129+ /* normalise bfs codec divider & cpu rcw mult */
21130+ codec_bfs = soc_bfs_div_to_rcw(codec_dai_mode->bfs, rate,
21131+ mclk, rtd->codec_dai->dai_runtime.pcmfmt, chn);
21132+ rtd->cpu_dai->dai_runtime.bfs =
21133+ 1 << (ffs(codec_bfs & cpu_dai_mode->bfs) - 1);
21134+ cpu_bfs = soc_bfs_rcw_to_div(cpu_dai_mode->bfs, rate, mclk,
21135+ rtd->codec_dai->dai_runtime.pcmfmt, chn);
21136+ rtd->codec_dai->dai_runtime.bfs =
21137+ 1 << (fls(codec_dai_mode->bfs & cpu_bfs) - 1);
21138+ break;
21139+ case CODEC_CPU(SND_SOC_DAI_BFS_RCW, SND_SOC_DAI_BFS_DIV):
21140+ /* normalise bfs codec rcw mult & cpu divider */
21141+ codec_bfs = soc_bfs_rcw_to_div(codec_dai_mode->bfs, rate,
21142+ mclk, rtd->codec_dai->dai_runtime.pcmfmt, chn);
21143+ rtd->cpu_dai->dai_runtime.bfs =
21144+ 1 << (fls(codec_bfs & cpu_dai_mode->bfs) -1);
21145+ cpu_bfs = soc_bfs_div_to_rcw(cpu_dai_mode->bfs, rate, mclk,
21146+ rtd->codec_dai->dai_runtime.pcmfmt, chn);
21147+ rtd->codec_dai->dai_runtime.bfs =
21148+ 1 << (ffs(codec_dai_mode->bfs & cpu_bfs) -1);
21149+ break;
21150+ case CODEC_CPU(SND_SOC_DAI_BFS_RCW, SND_SOC_DAI_BFS_RCW):
21151+ /* codec & cpu bfs rate rcw multipliers */
21152+ rtd->cpu_dai->dai_runtime.bfs =
21153+ rtd->codec_dai->dai_runtime.bfs =
21154+ 1 << (ffs(codec_dai_mode->bfs & cpu_dai_mode->bfs) -1);
21155+ break;
21156+ case CODEC_CPU(SND_SOC_DAI_BFS_DIV, SND_SOC_DAI_BFS_RATE):
21157+ /* normalise cpu bfs rate const multiplier & codec div */
21158+ cpu_bfs = soc_bfs_rate_to_div(cpu_dai_mode->bfs, rate,
21159+ mclk, rtd->codec_dai->dai_runtime.pcmfmt, chn);
21160+ if(codec_dai_mode->bfs & cpu_bfs) {
21161+ rtd->codec_dai->dai_runtime.bfs = cpu_bfs;
21162+ rtd->cpu_dai->dai_runtime.bfs = cpu_dai_mode->bfs;
21163+ } else
21164+ rtd->cpu_dai->dai_runtime.bfs = 0;
21165+ break;
21166+ case CODEC_CPU(SND_SOC_DAI_BFS_RCW, SND_SOC_DAI_BFS_RATE):
21167+ /* normalise cpu bfs rate const multiplier & codec rcw mult */
21168+ cpu_bfs = soc_bfs_rate_to_rcw(cpu_dai_mode->bfs, rate,
21169+ mclk, rtd->codec_dai->dai_runtime.pcmfmt, chn);
21170+ if(codec_dai_mode->bfs & cpu_bfs) {
21171+ rtd->codec_dai->dai_runtime.bfs = cpu_bfs;
21172+ rtd->cpu_dai->dai_runtime.bfs = cpu_dai_mode->bfs;
21173+ } else
21174+ rtd->cpu_dai->dai_runtime.bfs = 0;
21175+ break;
21176+ case CODEC_CPU(SND_SOC_DAI_BFS_RATE, SND_SOC_DAI_BFS_RCW):
21177+ /* normalise cpu bfs rate rcw multiplier & codec const mult */
21178+ codec_bfs = soc_bfs_rate_to_rcw(codec_dai_mode->bfs, rate,
21179+ mclk, rtd->codec_dai->dai_runtime.pcmfmt, chn);
21180+ if(cpu_dai_mode->bfs & codec_bfs) {
21181+ rtd->cpu_dai->dai_runtime.bfs = codec_bfs;
21182+ rtd->codec_dai->dai_runtime.bfs = codec_dai_mode->bfs;
21183+ } else
21184+ rtd->cpu_dai->dai_runtime.bfs = 0;
21185+ break;
21186+ case CODEC_CPU(SND_SOC_DAI_BFS_RATE, SND_SOC_DAI_BFS_DIV):
21187+ /* normalise cpu bfs div & codec const mult */
21188+ codec_bfs = soc_bfs_rate_to_div(codec_dai_mode->bfs, rate,
21189+ mclk, rtd->codec_dai->dai_runtime.pcmfmt, chn);
21190+ if(codec_dai_mode->bfs & codec_bfs) {
21191+ rtd->cpu_dai->dai_runtime.bfs = codec_bfs;
21192+ rtd->codec_dai->dai_runtime.bfs = codec_dai_mode->bfs;
21193+ } else
21194+ rtd->cpu_dai->dai_runtime.bfs = 0;
21195+ break;
21196+ case CODEC_CPU(SND_SOC_DAI_BFS_RATE, SND_SOC_DAI_BFS_RATE):
21197+ /* cpu & codec constant mult */
21198+ if(codec_dai_mode->bfs == cpu_dai_mode->bfs)
21199+ rtd->cpu_dai->dai_runtime.bfs =
21200+ rtd->codec_dai->dai_runtime.bfs =
21201+ codec_dai_mode->bfs;
21202+ else
21203+ rtd->cpu_dai->dai_runtime.bfs =
21204+ rtd->codec_dai->dai_runtime.bfs = 0;
21205+ break;
21206+ default:
21207+ if(codec_dai_mode->flags == 0)
21208+ printk(KERN_ERR "asoc: error missing codec DAI flags\n");
21209+ else
21210+ printk(KERN_ERR "asoc: error missing CPU DAI flags\n");
21211+ break;
21212+ }
21213+
21214+ /* make sure the bit clock speed is acceptable */
21215+ if (!rtd->cpu_dai->dai_runtime.bfs ||
21216+ !rtd->codec_dai->dai_runtime.bfs) {
21217+ dbgc("asoc: DAI[%d:%d] failed to match BFS\n", j, k);
21218+ dbgc("asoc: cpu_dai %llu codec %llu\n",
21219+ rtd->cpu_dai->dai_runtime.bfs,
21220+ rtd->codec_dai->dai_runtime.bfs);
21221+ dbgc("asoc: mclk %d hwfmt %x\n", mclk, fmt);
21222+ continue;
21223+ }
21224+
21225+ goto found;
21226+ }
21227+ }
21228+ printk(KERN_ERR "asoc: no matching DAI found between codec and CPU\n");
21229+ return -EINVAL;
21230+
21231+found:
21232+ /* we have matching DAI's, so complete the runtime info */
21233+ rtd->codec_dai->dai_runtime.pcmrate =
21234+ rtd->cpu_dai->dai_runtime.pcmrate =
21235+ soc_get_rate_format(rate);
21236+
21237+ rtd->codec_dai->dai_runtime.priv = codec_dai_mode->priv;
21238+ rtd->cpu_dai->dai_runtime.priv = cpu_dai_mode->priv;
21239+ rtd->codec_dai->dai_runtime.flags = codec_dai_mode->flags;
21240+ rtd->cpu_dai->dai_runtime.flags = cpu_dai_mode->flags;
21241+
21242+ /* for debug atm */
21243+ dbg("asoc: DAI[%d:%d] Match OK\n", j, k);
21244+ if (rtd->codec_dai->dai_runtime.flags == SND_SOC_DAI_BFS_DIV) {
21245+ codec_bclk = (rtd->codec_dai->dai_runtime.fs * params_rate(params)) /
21246+ SND_SOC_FSBD_REAL(rtd->codec_dai->dai_runtime.bfs);
21247+ dbg("asoc: codec fs %d mclk %d bfs div %d bclk %d\n",
21248+ rtd->codec_dai->dai_runtime.fs, mclk,
21249+ SND_SOC_FSBD_REAL(rtd->codec_dai->dai_runtime.bfs), codec_bclk);
21250+ } else if(rtd->codec_dai->dai_runtime.flags == SND_SOC_DAI_BFS_RATE) {
21251+ codec_bclk = params_rate(params) * rtd->codec_dai->dai_runtime.bfs;
21252+ dbg("asoc: codec fs %d mclk %d bfs rate mult %llu bclk %d\n",
21253+ rtd->codec_dai->dai_runtime.fs, mclk,
21254+ rtd->codec_dai->dai_runtime.bfs, codec_bclk);
21255+ } else if (rtd->cpu_dai->dai_runtime.flags == SND_SOC_DAI_BFS_RCW) {
21256+ codec_bclk = params_rate(params) * params_channels(params) *
21257+ snd_pcm_format_physical_width(rtd->codec_dai->dai_runtime.pcmfmt) *
21258+ SND_SOC_FSBW_REAL(rtd->codec_dai->dai_runtime.bfs);
21259+ dbg("asoc: codec fs %d mclk %d bfs rcw mult %d bclk %d\n",
21260+ rtd->codec_dai->dai_runtime.fs, mclk,
21261+ SND_SOC_FSBW_REAL(rtd->codec_dai->dai_runtime.bfs), codec_bclk);
21262+ } else
21263+ codec_bclk = 0;
21264+
21265+ if (rtd->cpu_dai->dai_runtime.flags == SND_SOC_DAI_BFS_DIV) {
21266+ cpu_bclk = (rtd->cpu_dai->dai_runtime.fs * params_rate(params)) /
21267+ SND_SOC_FSBD_REAL(rtd->cpu_dai->dai_runtime.bfs);
21268+ dbg("asoc: cpu fs %d mclk %d bfs div %d bclk %d\n",
21269+ rtd->cpu_dai->dai_runtime.fs, mclk,
21270+ SND_SOC_FSBD_REAL(rtd->cpu_dai->dai_runtime.bfs), cpu_bclk);
21271+ } else if (rtd->cpu_dai->dai_runtime.flags == SND_SOC_DAI_BFS_RATE) {
21272+ cpu_bclk = params_rate(params) * rtd->cpu_dai->dai_runtime.bfs;
21273+ dbg("asoc: cpu fs %d mclk %d bfs rate mult %llu bclk %d\n",
21274+ rtd->cpu_dai->dai_runtime.fs, mclk,
21275+ rtd->cpu_dai->dai_runtime.bfs, cpu_bclk);
21276+ } else if (rtd->cpu_dai->dai_runtime.flags == SND_SOC_DAI_BFS_RCW) {
21277+ cpu_bclk = params_rate(params) * params_channels(params) *
21278+ snd_pcm_format_physical_width(rtd->cpu_dai->dai_runtime.pcmfmt) *
21279+ SND_SOC_FSBW_REAL(rtd->cpu_dai->dai_runtime.bfs);
21280+ dbg("asoc: cpu fs %d mclk %d bfs mult rcw %d bclk %d\n",
21281+ rtd->cpu_dai->dai_runtime.fs, mclk,
21282+ SND_SOC_FSBW_REAL(rtd->cpu_dai->dai_runtime.bfs), cpu_bclk);
21283+ } else
21284+ cpu_bclk = 0;
21285+
21286+ /*
21287+ * Check we have matching bitclocks. If we don't then it means the
21288+ * sysclock returned by either the codec or cpu DAI (selected by the
21289+ * machine sysclock function) is wrong compared with the supported DAI
21290+ * modes for the codec or cpu DAI.
21291+ */
21292+ if (cpu_bclk != codec_bclk && cpu_bclk){
21293+ printk(KERN_ERR
21294+ "asoc: codec and cpu bitclocks differ, audio may be wrong speed\n"
21295+ );
21296+ printk(KERN_ERR "asoc: codec %d != cpu %d\n", codec_bclk, cpu_bclk);
21297+ }
21298+
21299+ switch(rtd->cpu_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
21300+ case SND_SOC_DAIFMT_CBM_CFM:
21301+ dbg("asoc: DAI codec BCLK master, LRC master\n");
21302+ break;
21303+ case SND_SOC_DAIFMT_CBS_CFM:
21304+ dbg("asoc: DAI codec BCLK slave, LRC master\n");
21305+ break;
21306+ case SND_SOC_DAIFMT_CBM_CFS:
21307+ dbg("asoc: DAI codec BCLK master, LRC slave\n");
21308+ break;
21309+ case SND_SOC_DAIFMT_CBS_CFS:
21310+ dbg("asoc: DAI codec BCLK slave, LRC slave\n");
21311+ break;
21312+ }
21313+ dbg("asoc: mode %x, invert %x\n",
21314+ rtd->cpu_dai->dai_runtime.fmt & SND_SOC_DAIFMT_FORMAT_MASK,
21315+ rtd->cpu_dai->dai_runtime.fmt & SND_SOC_DAIFMT_INV_MASK);
21316+ dbg("asoc: audio rate %d chn %d fmt %x\n", params_rate(params),
21317+ params_channels(params), params_format(params));
21318+
21319+ return 0;
21320+}
21321+
21322+static inline u32 get_rates(struct snd_soc_dai_mode *modes, int nmodes)
21323+{
21324+ int i;
21325+ u32 rates = 0;
21326+
21327+ for(i = 0; i < nmodes; i++)
21328+ rates |= modes[i].pcmrate;
21329+
21330+ return rates;
21331+}
21332+
21333+static inline u64 get_formats(struct snd_soc_dai_mode *modes, int nmodes)
21334+{
21335+ int i;
21336+ u64 formats = 0;
21337+
21338+ for(i = 0; i < nmodes; i++)
21339+ formats |= modes[i].pcmfmt;
21340+
21341+ return formats;
21342+}
21343+
21344+/*
21345+ * Called by ALSA when a PCM substream is opened, the runtime->hw record is
21346+ * then initialized and any private data can be allocated. This also calls
21347+ * startup for the cpu DAI, platform, machine and codec DAI.
21348+ */
21349+static int soc_pcm_open(struct snd_pcm_substream *substream)
21350+{
21351+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
21352+ struct snd_soc_device *socdev = rtd->socdev;
21353+ struct snd_pcm_runtime *runtime = substream->runtime;
21354+ struct snd_soc_machine *machine = socdev->machine;
21355+ struct snd_soc_platform *platform = socdev->platform;
21356+ struct snd_soc_codec_dai *codec_dai = rtd->codec_dai;
21357+ struct snd_soc_cpu_dai *cpu_dai = rtd->cpu_dai;
21358+ int ret = 0;
21359+
21360+ mutex_lock(&pcm_mutex);
21361+
21362+ /* startup the audio subsystem */
21363+ if (rtd->cpu_dai->ops.startup) {
21364+ ret = rtd->cpu_dai->ops.startup(substream);
21365+ if (ret < 0) {
21366+ printk(KERN_ERR "asoc: can't open interface %s\n",
21367+ rtd->cpu_dai->name);
21368+ goto out;
21369+ }
21370+ }
21371+
21372+ if (platform->pcm_ops->open) {
21373+ ret = platform->pcm_ops->open(substream);
21374+ if (ret < 0) {
21375+ printk(KERN_ERR "asoc: can't open platform %s\n", platform->name);
21376+ goto platform_err;
21377+ }
21378+ }
21379+
21380+ if (machine->ops && machine->ops->startup) {
21381+ ret = machine->ops->startup(substream);
21382+ if (ret < 0) {
21383+ printk(KERN_ERR "asoc: %s startup failed\n", machine->name);
21384+ goto machine_err;
21385+ }
21386+ }
21387+
21388+ if (rtd->codec_dai->ops.startup) {
21389+ ret = rtd->codec_dai->ops.startup(substream);
21390+ if (ret < 0) {
21391+ printk(KERN_ERR "asoc: can't open codec %s\n",
21392+ rtd->codec_dai->name);
21393+ goto codec_dai_err;
21394+ }
21395+ }
21396+
21397+ /* create runtime params from DMA, codec and cpu DAI */
21398+ if (runtime->hw.rates)
21399+ runtime->hw.rates &=
21400+ get_rates(codec_dai->caps.mode, codec_dai->caps.num_modes) &
21401+ get_rates(cpu_dai->caps.mode, cpu_dai->caps.num_modes);
21402+ else
21403+ runtime->hw.rates =
21404+ get_rates(codec_dai->caps.mode, codec_dai->caps.num_modes) &
21405+ get_rates(cpu_dai->caps.mode, cpu_dai->caps.num_modes);
21406+ if (runtime->hw.formats)
21407+ runtime->hw.formats &=
21408+ get_formats(codec_dai->caps.mode, codec_dai->caps.num_modes) &
21409+ get_formats(cpu_dai->caps.mode, cpu_dai->caps.num_modes);
21410+ else
21411+ runtime->hw.formats =
21412+ get_formats(codec_dai->caps.mode, codec_dai->caps.num_modes) &
21413+ get_formats(cpu_dai->caps.mode, cpu_dai->caps.num_modes);
21414+
21415+ /* Check that the codec and cpu DAI's are compatible */
21416+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
21417+ runtime->hw.rate_min =
21418+ max(rtd->codec_dai->playback.rate_min,
21419+ rtd->cpu_dai->playback.rate_min);
21420+ runtime->hw.rate_max =
21421+ min(rtd->codec_dai->playback.rate_max,
21422+ rtd->cpu_dai->playback.rate_max);
21423+ runtime->hw.channels_min =
21424+ max(rtd->codec_dai->playback.channels_min,
21425+ rtd->cpu_dai->playback.channels_min);
21426+ runtime->hw.channels_max =
21427+ min(rtd->codec_dai->playback.channels_max,
21428+ rtd->cpu_dai->playback.channels_max);
21429+ } else {
21430+ runtime->hw.rate_min =
21431+ max(rtd->codec_dai->capture.rate_min,
21432+ rtd->cpu_dai->capture.rate_min);
21433+ runtime->hw.rate_max =
21434+ min(rtd->codec_dai->capture.rate_max,
21435+ rtd->cpu_dai->capture.rate_max);
21436+ runtime->hw.channels_min =
21437+ max(rtd->codec_dai->capture.channels_min,
21438+ rtd->cpu_dai->capture.channels_min);
21439+ runtime->hw.channels_max =
21440+ min(rtd->codec_dai->capture.channels_max,
21441+ rtd->cpu_dai->capture.channels_max);
21442+ }
21443+
21444+ snd_pcm_limit_hw_rates(runtime);
21445+ if (!runtime->hw.rates) {
21446+ printk(KERN_ERR "asoc: %s <-> %s No matching rates\n",
21447+ rtd->codec_dai->name, rtd->cpu_dai->name);
21448+ goto codec_dai_err;
21449+ }
21450+ if (!runtime->hw.formats) {
21451+ printk(KERN_ERR "asoc: %s <-> %s No matching formats\n",
21452+ rtd->codec_dai->name, rtd->cpu_dai->name);
21453+ goto codec_dai_err;
21454+ }
21455+ if (!runtime->hw.channels_min || !runtime->hw.channels_max) {
21456+ printk(KERN_ERR "asoc: %s <-> %s No matching channels\n",
21457+ rtd->codec_dai->name, rtd->cpu_dai->name);
21458+ goto codec_dai_err;
21459+ }
21460+
21461+ dbg("asoc: %s <-> %s info:\n", rtd->codec_dai->name, rtd->cpu_dai->name);
21462+ dbg("asoc: rate mask 0x%x\n", runtime->hw.rates);
21463+ dbg("asoc: min ch %d max ch %d\n", runtime->hw.channels_min,
21464+ runtime->hw.channels_max);
21465+ dbg("asoc: min rate %d max rate %d\n", runtime->hw.rate_min,
21466+ runtime->hw.rate_max);
21467+
21468+
21469+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
21470+ rtd->cpu_dai->playback.active = rtd->codec_dai->playback.active = 1;
21471+ else
21472+ rtd->cpu_dai->capture.active = rtd->codec_dai->capture.active = 1;
21473+ rtd->cpu_dai->active = rtd->codec_dai->active = 1;
21474+ rtd->cpu_dai->runtime = runtime;
21475+ socdev->codec->active++;
21476+ mutex_unlock(&pcm_mutex);
21477+ return 0;
21478+
21479+codec_dai_err:
21480+ if (machine->ops && machine->ops->shutdown)
21481+ machine->ops->shutdown(substream);
21482+
21483+machine_err:
21484+ if (platform->pcm_ops->close)
21485+ platform->pcm_ops->close(substream);
21486+
21487+platform_err:
21488+ if (rtd->cpu_dai->ops.shutdown)
21489+ rtd->cpu_dai->ops.shutdown(substream);
21490+out:
21491+ mutex_unlock(&pcm_mutex);
21492+ return ret;
21493+}
21494+
21495+/*
21496+ * Power down the audio subsytem pmdown_time msecs after close is called.
21497+ * This is to ensure there are no pops or clicks in between any music tracks
21498+ * due to DAPM power cycling.
21499+ */
21500+static void close_delayed_work(void *data)
21501+{
21502+ struct snd_soc_device *socdev = data;
21503+ struct snd_soc_codec *codec = socdev->codec;
21504+ struct snd_soc_codec_dai *codec_dai;
21505+ int i;
21506+
21507+ mutex_lock(&pcm_mutex);
21508+ for(i = 0; i < codec->num_dai; i++) {
21509+ codec_dai = &codec->dai[i];
21510+
21511+ dbg("pop wq checking: %s status: %s waiting: %s\n",
21512+ codec_dai->playback.stream_name,
21513+ codec_dai->playback.active ? "active" : "inactive",
21514+ codec_dai->pop_wait ? "yes" : "no");
21515+
21516+ /* are we waiting on this codec DAI stream */
21517+ if (codec_dai->pop_wait == 1) {
21518+
21519+ codec_dai->pop_wait = 0;
21520+ snd_soc_dapm_stream_event(codec, codec_dai->playback.stream_name,
21521+ SND_SOC_DAPM_STREAM_STOP);
21522+
21523+ /* power down the codec power domain if no longer active */
21524+ if (codec->active == 0) {
21525+ dbg("pop wq D3 %s %s\n", codec->name,
21526+ codec_dai->playback.stream_name);
21527+ if (codec->dapm_event)
21528+ codec->dapm_event(codec, SNDRV_CTL_POWER_D3hot);
21529+ }
21530+ }
21531+ }
21532+ mutex_unlock(&pcm_mutex);
21533+}
21534+
21535+/*
21536+ * Called by ALSA when a PCM substream is closed. Private data can be
21537+ * freed here. The cpu DAI, codec DAI, machine and platform are also
21538+ * shutdown.
21539+ */
21540+static int soc_codec_close(struct snd_pcm_substream *substream)
21541+{
21542+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
21543+ struct snd_soc_device *socdev = rtd->socdev;
21544+ struct snd_soc_machine *machine = socdev->machine;
21545+ struct snd_soc_platform *platform = socdev->platform;
21546+ struct snd_soc_codec *codec = socdev->codec;
21547+
21548+ mutex_lock(&pcm_mutex);
21549+
21550+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
21551+ rtd->cpu_dai->playback.active = rtd->codec_dai->playback.active = 0;
21552+ else
21553+ rtd->cpu_dai->capture.active = rtd->codec_dai->capture.active = 0;
21554+
21555+ if (rtd->codec_dai->playback.active == 0 &&
21556+ rtd->codec_dai->capture.active == 0) {
21557+ rtd->cpu_dai->active = rtd->codec_dai->active = 0;
21558+ }
21559+ codec->active--;
21560+
21561+ if (rtd->cpu_dai->ops.shutdown)
21562+ rtd->cpu_dai->ops.shutdown(substream);
21563+
21564+ if (rtd->codec_dai->ops.shutdown)
21565+ rtd->codec_dai->ops.shutdown(substream);
21566+
21567+ if (machine->ops && machine->ops->shutdown)
21568+ machine->ops->shutdown(substream);
21569+
21570+ if (platform->pcm_ops->close)
21571+ platform->pcm_ops->close(substream);
21572+ rtd->cpu_dai->runtime = NULL;
21573+
21574+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
21575+ /* start delayed pop wq here for playback streams */
21576+ rtd->codec_dai->pop_wait = 1;
21577+ queue_delayed_work(soc_workq, &soc_stream_work,
21578+ msecs_to_jiffies(pmdown_time));
21579+ } else {
21580+ /* capture streams can be powered down now */
21581+ snd_soc_dapm_stream_event(codec, rtd->codec_dai->capture.stream_name,
21582+ SND_SOC_DAPM_STREAM_STOP);
21583+
21584+ if (codec->active == 0 && rtd->codec_dai->pop_wait == 0){
21585+ if (codec->dapm_event)
21586+ codec->dapm_event(codec, SNDRV_CTL_POWER_D3hot);
21587+ }
21588+ }
21589+
21590+ mutex_unlock(&pcm_mutex);
21591+ return 0;
21592+}
21593+
21594+/*
21595+ * Called by ALSA when the PCM substream is prepared, can set format, sample
21596+ * rate, etc. This function is non atomic and can be called multiple times,
21597+ * it can refer to the runtime info.
21598+ */
21599+static int soc_pcm_prepare(struct snd_pcm_substream *substream)
21600+{
21601+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
21602+ struct snd_soc_device *socdev = rtd->socdev;
21603+ struct snd_soc_platform *platform = socdev->platform;
21604+ struct snd_soc_codec *codec = socdev->codec;
21605+ int ret = 0;
21606+
21607+ mutex_lock(&pcm_mutex);
21608+ if (platform->pcm_ops->prepare) {
21609+ ret = platform->pcm_ops->prepare(substream);
21610+ if (ret < 0) {
21611+ printk(KERN_ERR "asoc: platform prepare error\n");
21612+ goto out;
21613+ }
21614+ }
21615+
21616+ if (rtd->codec_dai->ops.prepare) {
21617+ ret = rtd->codec_dai->ops.prepare(substream);
21618+ if (ret < 0) {
21619+ printk(KERN_ERR "asoc: codec DAI prepare error\n");
21620+ goto out;
21621+ }
21622+ }
21623+
21624+ if (rtd->cpu_dai->ops.prepare)
21625+ ret = rtd->cpu_dai->ops.prepare(substream);
21626+
21627+ /* we only want to start a DAPM playback stream if we are not waiting
21628+ * on an existing one stopping */
21629+ if (rtd->codec_dai->pop_wait) {
21630+ /* we are waiting for the delayed work to start */
21631+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
21632+ snd_soc_dapm_stream_event(codec,
21633+ rtd->codec_dai->capture.stream_name,
21634+ SND_SOC_DAPM_STREAM_START);
21635+ else {
21636+ rtd->codec_dai->pop_wait = 0;
21637+ cancel_delayed_work(&soc_stream_work);
21638+ if (rtd->codec_dai->digital_mute)
21639+ rtd->codec_dai->digital_mute(codec, rtd->codec_dai, 0);
21640+ }
21641+ } else {
21642+ /* no delayed work - do we need to power up codec */
21643+ if (codec->dapm_state != SNDRV_CTL_POWER_D0) {
21644+
21645+ if (codec->dapm_event)
21646+ codec->dapm_event(codec, SNDRV_CTL_POWER_D1);
21647+
21648+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
21649+ snd_soc_dapm_stream_event(codec,
21650+ rtd->codec_dai->playback.stream_name,
21651+ SND_SOC_DAPM_STREAM_START);
21652+ else
21653+ snd_soc_dapm_stream_event(codec,
21654+ rtd->codec_dai->capture.stream_name,
21655+ SND_SOC_DAPM_STREAM_START);
21656+
21657+ if (codec->dapm_event)
21658+ codec->dapm_event(codec, SNDRV_CTL_POWER_D0);
21659+ if (rtd->codec_dai->digital_mute)
21660+ rtd->codec_dai->digital_mute(codec, rtd->codec_dai, 0);
21661+
21662+ } else {
21663+ /* codec already powered - power on widgets */
21664+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
21665+ snd_soc_dapm_stream_event(codec,
21666+ rtd->codec_dai->playback.stream_name,
21667+ SND_SOC_DAPM_STREAM_START);
21668+ else
21669+ snd_soc_dapm_stream_event(codec,
21670+ rtd->codec_dai->capture.stream_name,
21671+ SND_SOC_DAPM_STREAM_START);
21672+ if (rtd->codec_dai->digital_mute)
21673+ rtd->codec_dai->digital_mute(codec, rtd->codec_dai, 0);
21674+ }
21675+ }
21676+
21677+out:
21678+ mutex_unlock(&pcm_mutex);
21679+ return ret;
21680+}
21681+
21682+/*
21683+ * Called by ALSA when the hardware params are set by application. This
21684+ * function can also be called multiple times and can allocate buffers
21685+ * (using snd_pcm_lib_* ). It's non-atomic.
21686+ */
21687+static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
21688+ struct snd_pcm_hw_params *params)
21689+{
21690+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
21691+ struct snd_soc_device *socdev = rtd->socdev;
21692+ struct snd_soc_platform *platform = socdev->platform;
21693+ struct snd_soc_machine *machine = socdev->machine;
21694+ int ret = 0;
21695+
21696+ mutex_lock(&pcm_mutex);
21697+
21698+ /* we don't need to match any AC97 params */
21699+ if (rtd->cpu_dai->type != SND_SOC_DAI_AC97) {
21700+ ret = soc_hw_match_params(substream, params);
21701+ if (ret < 0)
21702+ goto out;
21703+ } else {
21704+ struct snd_soc_clock_info clk_info;
21705+ clk_info.rate = params_rate(params);
21706+ ret = soc_get_mclk(rtd, &clk_info);
21707+ if (ret < 0)
21708+ goto out;
21709+ }
21710+
21711+ if (rtd->codec_dai->ops.hw_params) {
21712+ ret = rtd->codec_dai->ops.hw_params(substream, params);
21713+ if (ret < 0) {
21714+ printk(KERN_ERR "asoc: can't set codec %s hw params\n",
21715+ rtd->codec_dai->name);
21716+ goto out;
21717+ }
21718+ }
21719+
21720+ if (rtd->cpu_dai->ops.hw_params) {
21721+ ret = rtd->cpu_dai->ops.hw_params(substream, params);
21722+ if (ret < 0) {
21723+ printk(KERN_ERR "asoc: can't set interface %s hw params\n",
21724+ rtd->cpu_dai->name);
21725+ goto interface_err;
21726+ }
21727+ }
21728+
21729+ if (platform->pcm_ops->hw_params) {
21730+ ret = platform->pcm_ops->hw_params(substream, params);
21731+ if (ret < 0) {
21732+ printk(KERN_ERR "asoc: can't set platform %s hw params\n",
21733+ platform->name);
21734+ goto platform_err;
21735+ }
21736+ }
21737+
21738+ if (machine->ops && machine->ops->hw_params) {
21739+ ret = machine->ops->hw_params(substream, params);
21740+ if (ret < 0) {
21741+ printk(KERN_ERR "asoc: machine hw_params failed\n");
21742+ goto machine_err;
21743+ }
21744+ }
21745+
21746+out:
21747+ mutex_unlock(&pcm_mutex);
21748+ return ret;
21749+
21750+machine_err:
21751+ if (platform->pcm_ops->hw_free)
21752+ platform->pcm_ops->hw_free(substream);
21753+
21754+platform_err:
21755+ if (rtd->cpu_dai->ops.hw_free)
21756+ rtd->cpu_dai->ops.hw_free(substream);
21757+
21758+interface_err:
21759+ if (rtd->codec_dai->ops.hw_free)
21760+ rtd->codec_dai->ops.hw_free(substream);
21761+
21762+ mutex_unlock(&pcm_mutex);
21763+ return ret;
21764+}
21765+
21766+/*
21767+ * Free's resources allocated by hw_params, can be called multiple times
21768+ */
21769+static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
21770+{
21771+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
21772+ struct snd_soc_device *socdev = rtd->socdev;
21773+ struct snd_soc_platform *platform = socdev->platform;
21774+ struct snd_soc_codec *codec = socdev->codec;
21775+ struct snd_soc_machine *machine = socdev->machine;
21776+
21777+ mutex_lock(&pcm_mutex);
21778+
21779+ /* apply codec digital mute */
21780+ if (!codec->active && rtd->codec_dai->digital_mute)
21781+ rtd->codec_dai->digital_mute(codec, rtd->codec_dai, 1);
21782+
21783+ /* free any machine hw params */
21784+ if (machine->ops && machine->ops->hw_free)
21785+ machine->ops->hw_free(substream);
21786+
21787+ /* free any DMA resources */
21788+ if (platform->pcm_ops->hw_free)
21789+ platform->pcm_ops->hw_free(substream);
21790+
21791+ /* now free hw params for the DAI's */
21792+ if (rtd->codec_dai->ops.hw_free)
21793+ rtd->codec_dai->ops.hw_free(substream);
21794+
21795+ if (rtd->cpu_dai->ops.hw_free)
21796+ rtd->cpu_dai->ops.hw_free(substream);
21797+
21798+ mutex_unlock(&pcm_mutex);
21799+ return 0;
21800+}
21801+
21802+static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
21803+{
21804+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
21805+ struct snd_soc_device *socdev = rtd->socdev;
21806+ struct snd_soc_platform *platform = socdev->platform;
21807+ int ret;
21808+
21809+ if (rtd->codec_dai->ops.trigger) {
21810+ ret = rtd->codec_dai->ops.trigger(substream, cmd);
21811+ if (ret < 0)
21812+ return ret;
21813+ }
21814+
21815+ if (platform->pcm_ops->trigger) {
21816+ ret = platform->pcm_ops->trigger(substream, cmd);
21817+ if (ret < 0)
21818+ return ret;
21819+ }
21820+
21821+ if (rtd->cpu_dai->ops.trigger) {
21822+ ret = rtd->cpu_dai->ops.trigger(substream, cmd);
21823+ if (ret < 0)
21824+ return ret;
21825+ }
21826+ return 0;
21827+}
21828+
21829+/* ASoC PCM operations */
21830+static struct snd_pcm_ops soc_pcm_ops = {
21831+ .open = soc_pcm_open,
21832+ .close = soc_codec_close,
21833+ .hw_params = soc_pcm_hw_params,
21834+ .hw_free = soc_pcm_hw_free,
21835+ .prepare = soc_pcm_prepare,
21836+ .trigger = soc_pcm_trigger,
21837+};
21838+
21839+#ifdef CONFIG_PM
21840+/* powers down audio subsystem for suspend */
21841+static int soc_suspend(struct platform_device *pdev, pm_message_t state)
21842+{
21843+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
21844+ struct snd_soc_machine *machine = socdev->machine;
21845+ struct snd_soc_platform *platform = socdev->platform;
21846+ struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
21847+ struct snd_soc_codec *codec = socdev->codec;
21848+ int i;
21849+
21850+ /* mute any active DAC's */
21851+ for(i = 0; i < machine->num_links; i++) {
21852+ struct snd_soc_codec_dai *dai = machine->dai_link[i].codec_dai;
21853+ if (dai->digital_mute && dai->playback.active)
21854+ dai->digital_mute(codec, dai, 1);
21855+ }
21856+
21857+ if (machine->suspend_pre)
21858+ machine->suspend_pre(pdev, state);
21859+
21860+ for(i = 0; i < machine->num_links; i++) {
21861+ struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai;
21862+ if (cpu_dai->suspend && cpu_dai->type != SND_SOC_DAI_AC97)
21863+ cpu_dai->suspend(pdev, cpu_dai);
21864+ if (platform->suspend)
21865+ platform->suspend(pdev, cpu_dai);
21866+ }
21867+
21868+ /* close any waiting streams and save state */
21869+ flush_workqueue(soc_workq);
21870+ codec->suspend_dapm_state = codec->dapm_state;
21871+
21872+ for(i = 0; i < codec->num_dai; i++) {
21873+ char *stream = codec->dai[i].playback.stream_name;
21874+ if (stream != NULL)
21875+ snd_soc_dapm_stream_event(codec, stream,
21876+ SND_SOC_DAPM_STREAM_SUSPEND);
21877+ stream = codec->dai[i].capture.stream_name;
21878+ if (stream != NULL)
21879+ snd_soc_dapm_stream_event(codec, stream,
21880+ SND_SOC_DAPM_STREAM_SUSPEND);
21881+ }
21882+
21883+ if (codec_dev->suspend)
21884+ codec_dev->suspend(pdev, state);
21885+
21886+ for(i = 0; i < machine->num_links; i++) {
21887+ struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai;
21888+ if (cpu_dai->suspend && cpu_dai->type == SND_SOC_DAI_AC97)
21889+ cpu_dai->suspend(pdev, cpu_dai);
21890+ }
21891+
21892+ if (machine->suspend_post)
21893+ machine->suspend_post(pdev, state);
21894+
21895+ return 0;
21896+}
21897+
21898+/* powers up audio subsystem after a suspend */
21899+static int soc_resume(struct platform_device *pdev)
21900+{
21901+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
21902+ struct snd_soc_machine *machine = socdev->machine;
21903+ struct snd_soc_platform *platform = socdev->platform;
21904+ struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
21905+ struct snd_soc_codec *codec = socdev->codec;
21906+ int i;
21907+
21908+ if (machine->resume_pre)
21909+ machine->resume_pre(pdev);
21910+
21911+ for(i = 0; i < machine->num_links; i++) {
21912+ struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai;
21913+ if (cpu_dai->resume && cpu_dai->type == SND_SOC_DAI_AC97)
21914+ cpu_dai->resume(pdev, cpu_dai);
21915+ }
21916+
21917+ if (codec_dev->resume)
21918+ codec_dev->resume(pdev);
21919+
21920+ for(i = 0; i < codec->num_dai; i++) {
21921+ char* stream = codec->dai[i].playback.stream_name;
21922+ if (stream != NULL)
21923+ snd_soc_dapm_stream_event(codec, stream,
21924+ SND_SOC_DAPM_STREAM_RESUME);
21925+ stream = codec->dai[i].capture.stream_name;
21926+ if (stream != NULL)
21927+ snd_soc_dapm_stream_event(codec, stream,
21928+ SND_SOC_DAPM_STREAM_RESUME);
21929+ }
21930+
21931+ /* unmute any active DAC's */
21932+ for(i = 0; i < machine->num_links; i++) {
21933+ struct snd_soc_codec_dai *dai = machine->dai_link[i].codec_dai;
21934+ if (dai->digital_mute && dai->playback.active)
21935+ dai->digital_mute(codec, dai, 0);
21936+ }
21937+
21938+ for(i = 0; i < machine->num_links; i++) {
21939+ struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai;
21940+ if (cpu_dai->resume && cpu_dai->type != SND_SOC_DAI_AC97)
21941+ cpu_dai->resume(pdev, cpu_dai);
21942+ if (platform->resume)
21943+ platform->resume(pdev, cpu_dai);
21944+ }
21945+
21946+ if (machine->resume_post)
21947+ machine->resume_post(pdev);
21948+
21949+ return 0;
21950+}
21951+
21952+#else
21953+#define soc_suspend NULL
21954+#define soc_resume NULL
21955+#endif
21956+
21957+/* probes a new socdev */
21958+static int soc_probe(struct platform_device *pdev)
21959+{
21960+ int ret = 0, i;
21961+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
21962+ struct snd_soc_machine *machine = socdev->machine;
21963+ struct snd_soc_platform *platform = socdev->platform;
21964+ struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
21965+
21966+ if (machine->probe) {
21967+ ret = machine->probe(pdev);
21968+ if(ret < 0)
21969+ return ret;
21970+ }
21971+
21972+ for (i = 0; i < machine->num_links; i++) {
21973+ struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai;
21974+ if (cpu_dai->probe) {
21975+ ret = cpu_dai->probe(pdev);
21976+ if(ret < 0)
21977+ goto cpu_dai_err;
21978+ }
21979+ }
21980+
21981+ if (codec_dev->probe) {
21982+ ret = codec_dev->probe(pdev);
21983+ if(ret < 0)
21984+ goto cpu_dai_err;
21985+ }
21986+
21987+ if (platform->probe) {
21988+ ret = platform->probe(pdev);
21989+ if(ret < 0)
21990+ goto platform_err;
21991+ }
21992+
21993+ /* DAPM stream work */
21994+ soc_workq = create_workqueue("kdapm");
21995+ if (soc_workq == NULL)
21996+ goto work_err;
21997+ INIT_WORK(&soc_stream_work, close_delayed_work, socdev);
21998+ return 0;
21999+
22000+work_err:
22001+ if (platform->remove)
22002+ platform->remove(pdev);
22003+
22004+platform_err:
22005+ if (codec_dev->remove)
22006+ codec_dev->remove(pdev);
22007+
22008+cpu_dai_err:
22009+ for (i--; i > 0; i--) {
22010+ struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai;
22011+ if (cpu_dai->remove)
22012+ cpu_dai->remove(pdev);
22013+ }
22014+
22015+ if (machine->remove)
22016+ machine->remove(pdev);
22017+
22018+ return ret;
22019+}
22020+
22021+/* removes a socdev */
22022+static int soc_remove(struct platform_device *pdev)
22023+{
22024+ int i;
22025+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
22026+ struct snd_soc_machine *machine = socdev->machine;
22027+ struct snd_soc_platform *platform = socdev->platform;
22028+ struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
22029+
22030+ if (soc_workq)
22031+ destroy_workqueue(soc_workq);
22032+
22033+ if (platform->remove)
22034+ platform->remove(pdev);
22035+
22036+ if (codec_dev->remove)
22037+ codec_dev->remove(pdev);
22038+
22039+ for (i = 0; i < machine->num_links; i++) {
22040+ struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai;
22041+ if (cpu_dai->remove)
22042+ cpu_dai->remove(pdev);
22043+ }
22044+
22045+ if (machine->remove)
22046+ machine->remove(pdev);
22047+
22048+ return 0;
22049+}
22050+
22051+/* ASoC platform driver */
22052+static struct platform_driver soc_driver = {
22053+ .driver = {
22054+ .name = "soc-audio",
22055+ },
22056+ .probe = soc_probe,
22057+ .remove = soc_remove,
22058+ .suspend = soc_suspend,
22059+ .resume = soc_resume,
22060+};
22061+
22062+/* create a new pcm */
22063+static int soc_new_pcm(struct snd_soc_device *socdev,
22064+ struct snd_soc_dai_link *dai_link, int num)
22065+{
22066+ struct snd_soc_codec *codec = socdev->codec;
22067+ struct snd_soc_codec_dai *codec_dai = dai_link->codec_dai;
22068+ struct snd_soc_cpu_dai *cpu_dai = dai_link->cpu_dai;
22069+ struct snd_soc_pcm_runtime *rtd;
22070+ struct snd_pcm *pcm;
22071+ char new_name[64];
22072+ int ret = 0, playback = 0, capture = 0;
22073+
22074+ rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime), GFP_KERNEL);
22075+ if (rtd == NULL)
22076+ return -ENOMEM;
22077+ rtd->cpu_dai = cpu_dai;
22078+ rtd->codec_dai = codec_dai;
22079+ rtd->socdev = socdev;
22080+
22081+ /* check client and interface hw capabilities */
22082+ sprintf(new_name, "%s %s-%s-%d",dai_link->stream_name, codec_dai->name,
22083+ get_dai_name(cpu_dai->type), num);
22084+
22085+ if (codec_dai->playback.channels_min)
22086+ playback = 1;
22087+ if (codec_dai->capture.channels_min)
22088+ capture = 1;
22089+
22090+ ret = snd_pcm_new(codec->card, new_name, codec->pcm_devs++, playback,
22091+ capture, &pcm);
22092+ if (ret < 0) {
22093+ printk(KERN_ERR "asoc: can't create pcm for codec %s\n", codec->name);
22094+ kfree(rtd);
22095+ return ret;
22096+ }
22097+
22098+ pcm->private_data = rtd;
22099+ soc_pcm_ops.mmap = socdev->platform->pcm_ops->mmap;
22100+ soc_pcm_ops.pointer = socdev->platform->pcm_ops->pointer;
22101+ soc_pcm_ops.ioctl = socdev->platform->pcm_ops->ioctl;
22102+ soc_pcm_ops.copy = socdev->platform->pcm_ops->copy;
22103+ soc_pcm_ops.silence = socdev->platform->pcm_ops->silence;
22104+ soc_pcm_ops.ack = socdev->platform->pcm_ops->ack;
22105+ soc_pcm_ops.page = socdev->platform->pcm_ops->page;
22106+
22107+ if (playback)
22108+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &soc_pcm_ops);
22109+
22110+ if (capture)
22111+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &soc_pcm_ops);
22112+
22113+ ret = socdev->platform->pcm_new(codec->card, codec_dai, pcm);
22114+ if (ret < 0) {
22115+ printk(KERN_ERR "asoc: platform pcm constructor failed\n");
22116+ kfree(rtd);
22117+ return ret;
22118+ }
22119+
22120+ pcm->private_free = socdev->platform->pcm_free;
22121+ printk(KERN_INFO "asoc: %s <-> %s mapping ok\n", codec_dai->name,
22122+ cpu_dai->name);
22123+ return ret;
22124+}
22125+
22126+/* codec register dump */
22127+static ssize_t codec_reg_show(struct device *dev,
22128+ struct device_attribute *attr, char *buf)
22129+{
22130+ struct snd_soc_device *devdata = dev_get_drvdata(dev);
22131+ struct snd_soc_codec *codec = devdata->codec;
22132+ int i, step = 1, count = 0;
22133+
22134+ if (!codec->reg_cache_size)
22135+ return 0;
22136+
22137+ if (codec->reg_cache_step)
22138+ step = codec->reg_cache_step;
22139+
22140+ count += sprintf(buf, "%s registers\n", codec->name);
22141+ for(i = 0; i < codec->reg_cache_size; i += step)
22142+ count += sprintf(buf + count, "%2x: %4x\n", i, codec->read(codec, i));
22143+
22144+ return count;
22145+}
22146+static DEVICE_ATTR(codec_reg, 0444, codec_reg_show, NULL);
22147+
22148+/**
22149+ * snd_soc_new_ac97_codec - initailise AC97 device
22150+ * @codec: audio codec
22151+ * @ops: AC97 bus operations
22152+ * @num: AC97 codec number
22153+ *
22154+ * Initialises AC97 codec resources for use by ad-hoc devices only.
22155+ */
22156+int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
22157+ struct snd_ac97_bus_ops *ops, int num)
22158+{
22159+ mutex_lock(&codec->mutex);
22160+
22161+ codec->ac97 = kzalloc(sizeof(struct snd_ac97), GFP_KERNEL);
22162+ if (codec->ac97 == NULL) {
22163+ mutex_unlock(&codec->mutex);
22164+ return -ENOMEM;
22165+ }
22166+
22167+ codec->ac97->bus = kzalloc(sizeof(struct snd_ac97_bus), GFP_KERNEL);
22168+ if (codec->ac97->bus == NULL) {
22169+ kfree(codec->ac97);
22170+ codec->ac97 = NULL;
22171+ mutex_unlock(&codec->mutex);
22172+ return -ENOMEM;
22173+ }
22174+
22175+ codec->ac97->bus->ops = ops;
22176+ codec->ac97->num = num;
22177+ mutex_unlock(&codec->mutex);
22178+ return 0;
22179+}
22180+EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec);
22181+
22182+/**
22183+ * snd_soc_free_ac97_codec - free AC97 codec device
22184+ * @codec: audio codec
22185+ *
22186+ * Frees AC97 codec device resources.
22187+ */
22188+void snd_soc_free_ac97_codec(struct snd_soc_codec *codec)
22189+{
22190+ mutex_lock(&codec->mutex);
22191+ kfree(codec->ac97->bus);
22192+ kfree(codec->ac97);
22193+ codec->ac97 = NULL;
22194+ mutex_unlock(&codec->mutex);
22195+}
22196+EXPORT_SYMBOL_GPL(snd_soc_free_ac97_codec);
22197+
22198+/**
22199+ * snd_soc_update_bits - update codec register bits
22200+ * @codec: audio codec
22201+ * @reg: codec register
22202+ * @mask: register mask
22203+ * @value: new value
22204+ *
22205+ * Writes new register value.
22206+ *
22207+ * Returns 1 for change else 0.
22208+ */
22209+int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg,
22210+ unsigned short mask, unsigned short value)
22211+{
22212+ int change;
22213+ unsigned short old, new;
22214+
22215+ mutex_lock(&io_mutex);
22216+ old = snd_soc_read(codec, reg);
22217+ new = (old & ~mask) | value;
22218+ change = old != new;
22219+ if (change)
22220+ snd_soc_write(codec, reg, new);
22221+
22222+ mutex_unlock(&io_mutex);
22223+ return change;
22224+}
22225+EXPORT_SYMBOL_GPL(snd_soc_update_bits);
22226+
22227+/**
22228+ * snd_soc_test_bits - test register for change
22229+ * @codec: audio codec
22230+ * @reg: codec register
22231+ * @mask: register mask
22232+ * @value: new value
22233+ *
22234+ * Tests a register with a new value and checks if the new value is
22235+ * different from the old value.
22236+ *
22237+ * Returns 1 for change else 0.
22238+ */
22239+int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg,
22240+ unsigned short mask, unsigned short value)
22241+{
22242+ int change;
22243+ unsigned short old, new;
22244+
22245+ mutex_lock(&io_mutex);
22246+ old = snd_soc_read(codec, reg);
22247+ new = (old & ~mask) | value;
22248+ change = old != new;
22249+ mutex_unlock(&io_mutex);
22250+
22251+ return change;
22252+}
22253+EXPORT_SYMBOL_GPL(snd_soc_test_bits);
22254+
22255+/**
22256+ * snd_soc_get_rate - get int sample rate
22257+ * @hwpcmrate: the hardware pcm rate
22258+ *
22259+ * Returns the audio rate integaer value, else 0.
22260+ */
22261+int snd_soc_get_rate(int hwpcmrate)
22262+{
22263+ int rate = ffs(hwpcmrate) - 1;
22264+
22265+ if (rate > ARRAY_SIZE(rates))
22266+ return 0;
22267+ return rates[rate];
22268+}
22269+EXPORT_SYMBOL_GPL(snd_soc_get_rate);
22270+
22271+/**
22272+ * snd_soc_new_pcms - create new sound card and pcms
22273+ * @socdev: the SoC audio device
22274+ *
22275+ * Create a new sound card based upon the codec and interface pcms.
22276+ *
22277+ * Returns 0 for success, else error.
22278+ */
22279+int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char * xid)
22280+{
22281+ struct snd_soc_codec *codec = socdev->codec;
22282+ struct snd_soc_machine *machine = socdev->machine;
22283+ int ret = 0, i;
22284+
22285+ mutex_lock(&codec->mutex);
22286+
22287+ /* register a sound card */
22288+ codec->card = snd_card_new(idx, xid, codec->owner, 0);
22289+ if (!codec->card) {
22290+ printk(KERN_ERR "asoc: can't create sound card for codec %s\n",
22291+ codec->name);
22292+ mutex_unlock(&codec->mutex);
22293+ return -ENODEV;
22294+ }
22295+
22296+ codec->card->dev = socdev->dev;
22297+ codec->card->private_data = codec;
22298+ strncpy(codec->card->driver, codec->name, sizeof(codec->card->driver));
22299+
22300+ /* create the pcms */
22301+ for(i = 0; i < machine->num_links; i++) {
22302+ ret = soc_new_pcm(socdev, &machine->dai_link[i], i);
22303+ if (ret < 0) {
22304+ printk(KERN_ERR "asoc: can't create pcm %s\n",
22305+ machine->dai_link[i].stream_name);
22306+ mutex_unlock(&codec->mutex);
22307+ return ret;
22308+ }
22309+ }
22310+
22311+ mutex_unlock(&codec->mutex);
22312+ return ret;
22313+}
22314+EXPORT_SYMBOL_GPL(snd_soc_new_pcms);
22315+
22316+/**
22317+ * snd_soc_register_card - register sound card
22318+ * @socdev: the SoC audio device
22319+ *
22320+ * Register a SoC sound card. Also registers an AC97 device if the
22321+ * codec is AC97 for ad hoc devices.
22322+ *
22323+ * Returns 0 for success, else error.
22324+ */
22325+int snd_soc_register_card(struct snd_soc_device *socdev)
22326+{
22327+ struct snd_soc_codec *codec = socdev->codec;
22328+ struct snd_soc_machine *machine = socdev->machine;
22329+ int ret = 0, i, ac97 = 0, err = 0;
22330+
22331+ mutex_lock(&codec->mutex);
22332+ for(i = 0; i < machine->num_links; i++) {
22333+ if (socdev->machine->dai_link[i].init) {
22334+ err = socdev->machine->dai_link[i].init(codec);
22335+ if (err < 0) {
22336+ printk(KERN_ERR "asoc: failed to init %s\n",
22337+ socdev->machine->dai_link[i].stream_name);
22338+ continue;
22339+ }
22340+ }
22341+ if (socdev->machine->dai_link[i].cpu_dai->type == SND_SOC_DAI_AC97)
22342+ ac97 = 1;
22343+ }
22344+ snprintf(codec->card->shortname, sizeof(codec->card->shortname),
22345+ "%s", machine->name);
22346+ snprintf(codec->card->longname, sizeof(codec->card->longname),
22347+ "%s (%s)", machine->name, codec->name);
22348+
22349+ ret = snd_card_register(codec->card);
22350+ if (ret < 0) {
22351+ printk(KERN_ERR "asoc: failed to register soundcard for codec %s\n",
22352+ codec->name);
22353+ goto out;
22354+ }
22355+
22356+#ifdef CONFIG_SND_SOC_AC97_BUS
22357+ if (ac97) {
22358+ ret = soc_ac97_dev_register(codec);
22359+ if (ret < 0) {
22360+ printk(KERN_ERR "asoc: AC97 device register failed\n");
22361+ snd_card_free(codec->card);
22362+ goto out;
22363+ }
22364+ }
22365+#endif
22366+
22367+ err = snd_soc_dapm_sys_add(socdev->dev);
22368+ if (err < 0)
22369+ printk(KERN_WARNING "asoc: failed to add dapm sysfs entries\n");
22370+
22371+ err = device_create_file(socdev->dev, &dev_attr_codec_reg);
22372+ if (err < 0)
22373+ printk(KERN_WARNING "asoc: failed to add codec sysfs entries\n");
22374+out:
22375+ mutex_unlock(&codec->mutex);
22376+ return ret;
22377+}
22378+EXPORT_SYMBOL_GPL(snd_soc_register_card);
22379+
22380+/**
22381+ * snd_soc_free_pcms - free sound card and pcms
22382+ * @socdev: the SoC audio device
22383+ *
22384+ * Frees sound card and pcms associated with the socdev.
22385+ * Also unregister the codec if it is an AC97 device.
22386+ */
22387+void snd_soc_free_pcms(struct snd_soc_device *socdev)
22388+{
22389+ struct snd_soc_codec *codec = socdev->codec;
22390+
22391+ mutex_lock(&codec->mutex);
22392+#ifdef CONFIG_SND_SOC_AC97_BUS
22393+ if (codec->ac97)
22394+ soc_ac97_dev_unregister(codec);
22395+#endif
22396+
22397+ if (codec->card)
22398+ snd_card_free(codec->card);
22399+ device_remove_file(socdev->dev, &dev_attr_codec_reg);
22400+ mutex_unlock(&codec->mutex);
22401+}
22402+EXPORT_SYMBOL_GPL(snd_soc_free_pcms);
22403+
22404+/**
22405+ * snd_soc_set_runtime_hwparams - set the runtime hardware parameters
22406+ * @substream: the pcm substream
22407+ * @hw: the hardware parameters
22408+ *
22409+ * Sets the substream runtime hardware parameters.
22410+ */
22411+int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,
22412+ const struct snd_pcm_hardware *hw)
22413+{
22414+ struct snd_pcm_runtime *runtime = substream->runtime;
22415+ runtime->hw.info = hw->info;
22416+ runtime->hw.formats = hw->formats;
22417+ runtime->hw.period_bytes_min = hw->period_bytes_min;
22418+ runtime->hw.period_bytes_max = hw->period_bytes_max;
22419+ runtime->hw.periods_min = hw->periods_min;
22420+ runtime->hw.periods_max = hw->periods_max;
22421+ runtime->hw.buffer_bytes_max = hw->buffer_bytes_max;
22422+ runtime->hw.fifo_size = hw->fifo_size;
22423+ return 0;
22424+}
22425+EXPORT_SYMBOL_GPL(snd_soc_set_runtime_hwparams);
22426+
22427+/**
22428+ * snd_soc_cnew - create new control
22429+ * @_template: control template
22430+ * @data: control private data
22431+ * @lnng_name: control long name
22432+ *
22433+ * Create a new mixer control from a template control.
22434+ *
22435+ * Returns 0 for success, else error.
22436+ */
22437+struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template,
22438+ void *data, char *long_name)
22439+{
22440+ struct snd_kcontrol_new template;
22441+
22442+ memcpy(&template, _template, sizeof(template));
22443+ if (long_name)
22444+ template.name = long_name;
22445+ template.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
22446+ template.index = 0;
22447+
22448+ return snd_ctl_new1(&template, data);
22449+}
22450+EXPORT_SYMBOL_GPL(snd_soc_cnew);
22451+
22452+/**
22453+ * snd_soc_info_enum_double - enumerated double mixer info callback
22454+ * @kcontrol: mixer control
22455+ * @uinfo: control element information
22456+ *
22457+ * Callback to provide information about a double enumerated
22458+ * mixer control.
22459+ *
22460+ * Returns 0 for success.
22461+ */
22462+int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol,
22463+ struct snd_ctl_elem_info *uinfo)
22464+{
22465+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
22466+
22467+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
22468+ uinfo->count = e->shift_l == e->shift_r ? 1 : 2;
22469+ uinfo->value.enumerated.items = e->mask;
22470+
22471+ if (uinfo->value.enumerated.item > e->mask - 1)
22472+ uinfo->value.enumerated.item = e->mask - 1;
22473+ strcpy(uinfo->value.enumerated.name,
22474+ e->texts[uinfo->value.enumerated.item]);
22475+ return 0;
22476+}
22477+EXPORT_SYMBOL_GPL(snd_soc_info_enum_double);
22478+
22479+/**
22480+ * snd_soc_get_enum_double - enumerated double mixer get callback
22481+ * @kcontrol: mixer control
22482+ * @uinfo: control element information
22483+ *
22484+ * Callback to get the value of a double enumerated mixer.
22485+ *
22486+ * Returns 0 for success.
22487+ */
22488+int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol,
22489+ struct snd_ctl_elem_value *ucontrol)
22490+{
22491+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
22492+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
22493+ unsigned short val, bitmask;
22494+
22495+ for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
22496+ ;
22497+ val = snd_soc_read(codec, e->reg);
22498+ ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (bitmask - 1);
22499+ if (e->shift_l != e->shift_r)
22500+ ucontrol->value.enumerated.item[1] =
22501+ (val >> e->shift_r) & (bitmask - 1);
22502+
22503+ return 0;
22504+}
22505+EXPORT_SYMBOL_GPL(snd_soc_get_enum_double);
22506+
22507+/**
22508+ * snd_soc_put_enum_double - enumerated double mixer put callback
22509+ * @kcontrol: mixer control
22510+ * @uinfo: control element information
22511+ *
22512+ * Callback to set the value of a double enumerated mixer.
22513+ *
22514+ * Returns 0 for success.
22515+ */
22516+int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,
22517+ struct snd_ctl_elem_value *ucontrol)
22518+{
22519+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
22520+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
22521+ unsigned short val;
22522+ unsigned short mask, bitmask;
22523+
22524+ for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
22525+ ;
22526+ if (ucontrol->value.enumerated.item[0] > e->mask - 1)
22527+ return -EINVAL;
22528+ val = ucontrol->value.enumerated.item[0] << e->shift_l;
22529+ mask = (bitmask - 1) << e->shift_l;
22530+ if (e->shift_l != e->shift_r) {
22531+ if (ucontrol->value.enumerated.item[1] > e->mask - 1)
22532+ return -EINVAL;
22533+ val |= ucontrol->value.enumerated.item[1] << e->shift_r;
22534+ mask |= (bitmask - 1) << e->shift_r;
22535+ }
22536+
22537+ return snd_soc_update_bits(codec, e->reg, mask, val);
22538+}
22539+EXPORT_SYMBOL_GPL(snd_soc_put_enum_double);
22540+
22541+/**
22542+ * snd_soc_info_enum_ext - external enumerated single mixer info callback
22543+ * @kcontrol: mixer control
22544+ * @uinfo: control element information
22545+ *
22546+ * Callback to provide information about an external enumerated
22547+ * single mixer.
22548+ *
22549+ * Returns 0 for success.
22550+ */
22551+int snd_soc_info_enum_ext(struct snd_kcontrol *kcontrol,
22552+ struct snd_ctl_elem_info *uinfo)
22553+{
22554+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
22555+
22556+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
22557+ uinfo->count = 1;
22558+ uinfo->value.enumerated.items = e->mask;
22559+
22560+ if (uinfo->value.enumerated.item > e->mask - 1)
22561+ uinfo->value.enumerated.item = e->mask - 1;
22562+ strcpy(uinfo->value.enumerated.name,
22563+ e->texts[uinfo->value.enumerated.item]);
22564+ return 0;
22565+}
22566+EXPORT_SYMBOL_GPL(snd_soc_info_enum_ext);
22567+
22568+/**
22569+ * snd_soc_info_volsw_ext - external single mixer info callback
22570+ * @kcontrol: mixer control
22571+ * @uinfo: control element information
22572+ *
22573+ * Callback to provide information about a single external mixer control.
22574+ *
22575+ * Returns 0 for success.
22576+ */
22577+int snd_soc_info_volsw_ext(struct snd_kcontrol *kcontrol,
22578+ struct snd_ctl_elem_info *uinfo)
22579+{
22580+ int mask = kcontrol->private_value;
22581+
22582+ uinfo->type =
22583+ mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
22584+ uinfo->count = 1;
22585+ uinfo->value.integer.min = 0;
22586+ uinfo->value.integer.max = mask;
22587+ return 0;
22588+}
22589+EXPORT_SYMBOL_GPL(snd_soc_info_volsw_ext);
22590+
22591+/**
22592+ * snd_soc_info_bool_ext - external single boolean mixer info callback
22593+ * @kcontrol: mixer control
22594+ * @uinfo: control element information
22595+ *
22596+ * Callback to provide information about a single boolean external mixer control.
22597+ *
22598+ * Returns 0 for success.
22599+ */
22600+int snd_soc_info_bool_ext(struct snd_kcontrol *kcontrol,
22601+ struct snd_ctl_elem_info *uinfo)
22602+{
22603+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
22604+ uinfo->count = 1;
22605+ uinfo->value.integer.min = 0;
22606+ uinfo->value.integer.max = 1;
22607+ return 0;
22608+}
22609+EXPORT_SYMBOL_GPL(snd_soc_info_bool_ext);
22610+
22611+/**
22612+ * snd_soc_info_volsw - single mixer info callback
22613+ * @kcontrol: mixer control
22614+ * @uinfo: control element information
22615+ *
22616+ * Callback to provide information about a single mixer control.
22617+ *
22618+ * Returns 0 for success.
22619+ */
22620+int snd_soc_info_volsw(struct snd_kcontrol *kcontrol,
22621+ struct snd_ctl_elem_info *uinfo)
22622+{
22623+ int mask = (kcontrol->private_value >> 16) & 0xff;
22624+ int shift = (kcontrol->private_value >> 8) & 0x0f;
22625+ int rshift = (kcontrol->private_value >> 12) & 0x0f;
22626+
22627+ uinfo->type =
22628+ mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
22629+ uinfo->count = shift == rshift ? 1 : 2;
22630+ uinfo->value.integer.min = 0;
22631+ uinfo->value.integer.max = mask;
22632+ return 0;
22633+}
22634+EXPORT_SYMBOL_GPL(snd_soc_info_volsw);
22635+
22636+/**
22637+ * snd_soc_get_volsw - single mixer get callback
22638+ * @kcontrol: mixer control
22639+ * @uinfo: control element information
22640+ *
22641+ * Callback to get the value of a single mixer control.
22642+ *
22643+ * Returns 0 for success.
22644+ */
22645+int snd_soc_get_volsw(struct snd_kcontrol *kcontrol,
22646+ struct snd_ctl_elem_value *ucontrol)
22647+{
22648+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
22649+ int reg = kcontrol->private_value & 0xff;
22650+ int shift = (kcontrol->private_value >> 8) & 0x0f;
22651+ int rshift = (kcontrol->private_value >> 12) & 0x0f;
22652+ int mask = (kcontrol->private_value >> 16) & 0xff;
22653+ int invert = (kcontrol->private_value >> 24) & 0x01;
22654+
22655+ ucontrol->value.integer.value[0] =
22656+ (snd_soc_read(codec, reg) >> shift) & mask;
22657+ if (shift != rshift)
22658+ ucontrol->value.integer.value[1] =
22659+ (snd_soc_read(codec, reg) >> rshift) & mask;
22660+ if (invert) {
22661+ ucontrol->value.integer.value[0] =
22662+ mask - ucontrol->value.integer.value[0];
22663+ if (shift != rshift)
22664+ ucontrol->value.integer.value[1] =
22665+ mask - ucontrol->value.integer.value[1];
22666+ }
22667+
22668+ return 0;
22669+}
22670+EXPORT_SYMBOL_GPL(snd_soc_get_volsw);
22671+
22672+/**
22673+ * snd_soc_put_volsw - single mixer put callback
22674+ * @kcontrol: mixer control
22675+ * @uinfo: control element information
22676+ *
22677+ * Callback to set the value of a single mixer control.
22678+ *
22679+ * Returns 0 for success.
22680+ */
22681+int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
22682+ struct snd_ctl_elem_value *ucontrol)
22683+{
22684+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
22685+ int reg = kcontrol->private_value & 0xff;
22686+ int shift = (kcontrol->private_value >> 8) & 0x0f;
22687+ int rshift = (kcontrol->private_value >> 12) & 0x0f;
22688+ int mask = (kcontrol->private_value >> 16) & 0xff;
22689+ int invert = (kcontrol->private_value >> 24) & 0x01;
22690+ int err;
22691+ unsigned short val, val2, val_mask;
22692+
22693+ val = (ucontrol->value.integer.value[0] & mask);
22694+ if (invert)
22695+ val = mask - val;
22696+ val_mask = mask << shift;
22697+ val = val << shift;
22698+ if (shift != rshift) {
22699+ val2 = (ucontrol->value.integer.value[1] & mask);
22700+ if (invert)
22701+ val2 = mask - val2;
22702+ val_mask |= mask << rshift;
22703+ val |= val2 << rshift;
22704+ }
22705+ err = snd_soc_update_bits(codec, reg, val_mask, val);
22706+ return err;
22707+}
22708+EXPORT_SYMBOL_GPL(snd_soc_put_volsw);
22709+
22710+/**
22711+ * snd_soc_info_volsw_2r - double mixer info callback
22712+ * @kcontrol: mixer control
22713+ * @uinfo: control element information
22714+ *
22715+ * Callback to provide information about a double mixer control that
22716+ * spans 2 codec registers.
22717+ *
22718+ * Returns 0 for success.
22719+ */
22720+int snd_soc_info_volsw_2r(struct snd_kcontrol *kcontrol,
22721+ struct snd_ctl_elem_info *uinfo)
22722+{
22723+ int mask = (kcontrol->private_value >> 12) & 0xff;
22724+
22725+ uinfo->type =
22726+ mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
22727+ uinfo->count = 2;
22728+ uinfo->value.integer.min = 0;
22729+ uinfo->value.integer.max = mask;
22730+ return 0;
22731+}
22732+EXPORT_SYMBOL_GPL(snd_soc_info_volsw_2r);
22733+
22734+/**
22735+ * snd_soc_get_volsw_2r - double mixer get callback
22736+ * @kcontrol: mixer control
22737+ * @uinfo: control element information
22738+ *
22739+ * Callback to get the value of a double mixer control that spans 2 registers.
22740+ *
22741+ * Returns 0 for success.
22742+ */
22743+int snd_soc_get_volsw_2r(struct snd_kcontrol *kcontrol,
22744+ struct snd_ctl_elem_value *ucontrol)
22745+{
22746+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
22747+ int reg = kcontrol->private_value & 0xff;
22748+ int reg2 = (kcontrol->private_value >> 24) & 0xff;
22749+ int shift = (kcontrol->private_value >> 8) & 0x0f;
22750+ int mask = (kcontrol->private_value >> 12) & 0xff;
22751+ int invert = (kcontrol->private_value >> 20) & 0x01;
22752+
22753+ ucontrol->value.integer.value[0] =
22754+ (snd_soc_read(codec, reg) >> shift) & mask;
22755+ ucontrol->value.integer.value[1] =
22756+ (snd_soc_read(codec, reg2) >> shift) & mask;
22757+ if (invert) {
22758+ ucontrol->value.integer.value[0] =
22759+ mask - ucontrol->value.integer.value[0];
22760+ ucontrol->value.integer.value[1] =
22761+ mask - ucontrol->value.integer.value[1];
22762+ }
22763+
22764+ return 0;
22765+}
22766+EXPORT_SYMBOL_GPL(snd_soc_get_volsw_2r);
22767+
22768+/**
22769+ * snd_soc_put_volsw_2r - double mixer set callback
22770+ * @kcontrol: mixer control
22771+ * @uinfo: control element information
22772+ *
22773+ * Callback to set the value of a double mixer control that spans 2 registers.
22774+ *
22775+ * Returns 0 for success.
22776+ */
22777+int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol,
22778+ struct snd_ctl_elem_value *ucontrol)
22779+{
22780+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
22781+ int reg = kcontrol->private_value & 0xff;
22782+ int reg2 = (kcontrol->private_value >> 24) & 0xff;
22783+ int shift = (kcontrol->private_value >> 8) & 0x0f;
22784+ int mask = (kcontrol->private_value >> 12) & 0xff;
22785+ int invert = (kcontrol->private_value >> 20) & 0x01;
22786+ int err;
22787+ unsigned short val, val2, val_mask;
22788+
22789+ val_mask = mask << shift;
22790+ val = (ucontrol->value.integer.value[0] & mask);
22791+ val2 = (ucontrol->value.integer.value[1] & mask);
22792+
22793+ if (invert) {
22794+ val = mask - val;
22795+ val2 = mask - val2;
22796+ }
22797+
22798+ val = val << shift;
22799+ val2 = val2 << shift;
22800+
22801+ if ((err = snd_soc_update_bits(codec, reg, val_mask, val)) < 0)
22802+ return err;
22803+
22804+ err = snd_soc_update_bits(codec, reg2, val_mask, val2);
22805+ return err;
22806+}
22807+EXPORT_SYMBOL_GPL(snd_soc_put_volsw_2r);
22808+
22809+static int __devinit snd_soc_init(void)
22810+{
22811+ printk(KERN_INFO "ASoC version %s\n", SND_SOC_VERSION);
22812+ return platform_driver_register(&soc_driver);
22813+}
22814+
22815+static void snd_soc_exit(void)
22816+{
22817+ platform_driver_unregister(&soc_driver);
22818+}
22819+
22820+module_init(snd_soc_init);
22821+module_exit(snd_soc_exit);
22822+
22823+/* Module information */
22824+MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
22825+MODULE_DESCRIPTION("ALSA SoC Core");
22826+MODULE_LICENSE("GPL");
22827Index: linux-2.6-pxa-new/sound/soc/at91/Kconfig
22828===================================================================
22829--- /dev/null
22830+++ linux-2.6-pxa-new/sound/soc/at91/Kconfig
22831@@ -0,0 +1,24 @@
22832+menu "SoC Audio for the Atmel AT91"
22833+
22834+config SND_AT91_SOC
22835+ tristate "SoC Audio for the Atmel AT91 System-on-Chip"
22836+ depends on ARCH_AT91 && SND
22837+ select SND_PCM
22838+ help
22839+ Say Y or M if you want to add support for codecs attached to
22840+ the AT91 SSC interface. You will also need
22841+ to select the audio interfaces to support below.
22842+
22843+config SND_AT91_SOC_I2S
22844+ tristate
22845+
22846+config SND_AT91_SOC_ETI_B1_WM8731
22847+ tristate "SoC I2S Audio support for Endrelia ETI-B1 board"
22848+ depends on SND_AT91_SOC && MACH_ETI_B1
22849+ select SND_AT91_SOC_I2S
22850+ select SND_SOC_WM8731
22851+ help
22852+ Say Y if you want to add support for SoC audio on Endrelia
22853+ ETI-B1 board.
22854+
22855+endmenu
22856Index: linux-2.6-pxa-new/sound/soc/at91/Makefile
22857===================================================================
22858--- /dev/null
22859+++ linux-2.6-pxa-new/sound/soc/at91/Makefile
22860@@ -0,0 +1,11 @@
22861+# AT91 Platform Support
22862+snd-soc-at91-objs := at91rm9200-pcm.o
22863+snd-soc-at91-i2s-objs := at91rm9200-i2s.o
22864+
22865+obj-$(CONFIG_SND_AT91_SOC) += snd-soc-at91.o
22866+obj-$(CONFIG_SND_AT91_SOC_I2S) += snd-soc-at91-i2s.o
22867+
22868+# AT91 Machine Support
22869+snd-soc-eti-b1-wm8731-objs := eti_b1_wm8731.o
22870+
22871+obj-$(CONFIG_SND_AT91_SOC_ETI_B1_WM8731) += snd-soc-eti-b1-wm8731.o
22872Index: linux-2.6-pxa-new/sound/soc/at91/at91rm9200-i2s.c
22873===================================================================
22874--- /dev/null
22875+++ linux-2.6-pxa-new/sound/soc/at91/at91rm9200-i2s.c
22876@@ -0,0 +1,715 @@
22877+/*
22878+ * at91rm9200-i2s.c -- ALSA Soc Audio Layer Platform driver and DMA engine
22879+ *
22880+ * Author: Frank Mandarino <fmandarino@endrelia.com>
22881+ * Endrelia Technologies Inc.
22882+ *
22883+ * Based on pxa2xx Platform drivers by
22884+ * Liam Girdwood <liam.girdwood@wolfsonmicro.com>
22885+ *
22886+ * This program is free software; you can redistribute it and/or modify it
22887+ * under the terms of the GNU General Public License as published by the
22888+ * Free Software Foundation; either version 2 of the License, or (at your
22889+ * option) any later version.
22890+ *
22891+ * Revision history
22892+ * 3rd Mar 2006 Initial version.
22893+ */
22894+
22895+#include <linux/init.h>
22896+#include <linux/module.h>
22897+#include <linux/interrupt.h>
22898+#include <linux/device.h>
22899+#include <linux/delay.h>
22900+#include <linux/clk.h>
22901+#include <sound/driver.h>
22902+#include <sound/core.h>
22903+#include <sound/pcm.h>
22904+#include <sound/initval.h>
22905+#include <sound/soc.h>
22906+
22907+#include <asm/arch/at91rm9200.h>
22908+#include <asm/arch/at91rm9200_ssc.h>
22909+#include <asm/arch/at91rm9200_pdc.h>
22910+#include <asm/arch/hardware.h>
22911+
22912+#include "at91rm9200-pcm.h"
22913+
22914+#if 0
22915+#define DBG(x...) printk(KERN_DEBUG "at91rm9200-i2s:" x)
22916+#else
22917+#define DBG(x...)
22918+#endif
22919+
22920+#define AT91RM9200_I2S_DAIFMT \
22921+ (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS | SND_SOC_DAIFMT_NB_NF)
22922+
22923+#define AT91RM9200_I2S_DIR \
22924+ (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
22925+
22926+/* priv is (SSC_CMR.DIV << 16 | SSC_TCMR.PERIOD ) */
22927+static struct snd_soc_dai_mode at91rm9200_i2s[] = {
22928+
22929+ /* 8k: BCLK = (MCLK/10) = (60MHz/50) = 1.2MHz */
22930+ {
22931+ .fmt = AT91RM9200_I2S_DAIFMT,
22932+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
22933+ .pcmrate = SNDRV_PCM_RATE_8000,
22934+ .pcmdir = AT91RM9200_I2S_DIR,
22935+ .flags = SND_SOC_DAI_BFS_DIV,
22936+ .fs = 1500,
22937+ .bfs = SND_SOC_FSBD(10),
22938+ .priv = (25 << 16 | 74),
22939+ },
22940+
22941+ /* 16k: BCLK = (MCLK/3) ~= (60MHz/14) = 4.285714MHz */
22942+ {
22943+ .fmt = AT91RM9200_I2S_DAIFMT,
22944+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
22945+ .pcmrate = SNDRV_PCM_RATE_16000,
22946+ .pcmdir = AT91RM9200_I2S_DIR,
22947+ .flags = SND_SOC_DAI_BFS_DIV,
22948+ .fs = 750,
22949+ .bfs = SND_SOC_FSBD(3),
22950+ .priv = (7 << 16 | 133),
22951+ },
22952+
22953+ /* 32k: BCLK = (MCLK/3) ~= (60MHz/14) = 4.285714MHz */
22954+ {
22955+ .fmt = AT91RM9200_I2S_DAIFMT,
22956+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
22957+ .pcmrate = SNDRV_PCM_RATE_32000,
22958+ .pcmdir = AT91RM9200_I2S_DIR,
22959+ .flags = SND_SOC_DAI_BFS_DIV,
22960+ .fs = 375,
22961+ .bfs = SND_SOC_FSBD(3),
22962+ .priv = (7 << 16 | 66),
22963+ },
22964+
22965+ /* 48k: BCLK = (MCLK/5) ~= (60MHz/26) = 2.3076923MHz */
22966+ {
22967+ .fmt = AT91RM9200_I2S_DAIFMT,
22968+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
22969+ .pcmrate = SNDRV_PCM_RATE_48000,
22970+ .pcmdir = AT91RM9200_I2S_DIR,
22971+ .flags = SND_SOC_DAI_BFS_DIV,
22972+ .fs = 250,
22973+ .bfs SND_SOC_FSBD(5),
22974+ .priv = (13 << 16 | 23),
22975+ },
22976+};
22977+
22978+
22979+/*
22980+ * SSC registers required by the PCM DMA engine.
22981+ */
22982+static struct at91rm9200_ssc_regs ssc_reg[3] = {
22983+ {
22984+ .cr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_SSC_CR),
22985+ .ier = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_SSC_IER),
22986+ .idr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_SSC_IDR),
22987+ },
22988+ {
22989+ .cr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_SSC_CR),
22990+ .ier = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_SSC_IER),
22991+ .idr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_SSC_IDR),
22992+ },
22993+ {
22994+ .cr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_SSC_CR),
22995+ .ier = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_SSC_IER),
22996+ .idr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_SSC_IDR),
22997+ },
22998+};
22999+
23000+static struct at91rm9200_pdc_regs pdc_tx_reg[3] = {
23001+ {
23002+ .xpr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_PDC_TPR),
23003+ .xcr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_PDC_TCR),
23004+ .xnpr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_PDC_TNPR),
23005+ .xncr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_PDC_TNCR),
23006+ .ptcr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_PDC_PTCR),
23007+ },
23008+ {
23009+ .xpr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_PDC_TPR),
23010+ .xcr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_PDC_TCR),
23011+ .xnpr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_PDC_TNPR),
23012+ .xncr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_PDC_TNCR),
23013+ .ptcr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_PDC_PTCR),
23014+ },
23015+ {
23016+ .xpr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_PDC_TPR),
23017+ .xcr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_PDC_TCR),
23018+ .xnpr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_PDC_TNPR),
23019+ .xncr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_PDC_TNCR),
23020+ .ptcr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_PDC_PTCR),
23021+ },
23022+};
23023+
23024+static struct at91rm9200_pdc_regs pdc_rx_reg[3] = {
23025+ {
23026+ .xpr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_PDC_RPR),
23027+ .xcr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_PDC_RCR),
23028+ .xnpr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_PDC_RNPR),
23029+ .xncr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_PDC_RNCR),
23030+ .ptcr = (void __iomem *) (AT91_VA_BASE_SSC0 + AT91_PDC_PTCR),
23031+ },
23032+ {
23033+ .xpr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_PDC_RPR),
23034+ .xcr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_PDC_RCR),
23035+ .xnpr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_PDC_RNPR),
23036+ .xncr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_PDC_RNCR),
23037+ .ptcr = (void __iomem *) (AT91_VA_BASE_SSC1 + AT91_PDC_PTCR),
23038+ },
23039+ {
23040+ .xpr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_PDC_RPR),
23041+ .xcr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_PDC_RCR),
23042+ .xnpr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_PDC_RNPR),
23043+ .xncr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_PDC_RNCR),
23044+ .ptcr = (void __iomem *) (AT91_VA_BASE_SSC2 + AT91_PDC_PTCR),
23045+ },
23046+};
23047+
23048+/*
23049+ * SSC & PDC status bits for transmit and receive.
23050+ */
23051+static struct at91rm9200_ssc_mask ssc_tx_mask = {
23052+ .ssc_enable = AT91_SSC_TXEN,
23053+ .ssc_disable = AT91_SSC_TXDIS,
23054+ .ssc_endx = AT91_SSC_ENDTX,
23055+ .ssc_endbuf = AT91_SSC_TXBUFE,
23056+ .pdc_enable = AT91_PDC_TXTEN,
23057+ .pdc_disable = AT91_PDC_TXTDIS,
23058+};
23059+
23060+static struct at91rm9200_ssc_mask ssc_rx_mask = {
23061+ .ssc_enable = AT91_SSC_RXEN,
23062+ .ssc_disable = AT91_SSC_RXDIS,
23063+ .ssc_endx = AT91_SSC_ENDRX,
23064+ .ssc_endbuf = AT91_SSC_RXBUFF,
23065+ .pdc_enable = AT91_PDC_RXTEN,
23066+ .pdc_disable = AT91_PDC_RXTDIS,
23067+};
23068+
23069+/*
23070+ * A MUTEX is used to protect an SSC initialzed flag which allows
23071+ * the substream hw_params() call to initialize the SSC only if
23072+ * there are no other substreams open. If there are other
23073+ * substreams open, the hw_param() call can only check that
23074+ * it is using the same format and rate.
23075+ */
23076+static DECLARE_MUTEX(ssc0_mutex);
23077+static DECLARE_MUTEX(ssc1_mutex);
23078+static DECLARE_MUTEX(ssc2_mutex);
23079+
23080+/*
23081+ * DMA parameters.
23082+ */
23083+static at91rm9200_pcm_dma_params_t ssc_dma_params[3][2] = {
23084+ {{
23085+ .name = "SSC0/I2S PCM Stereo out",
23086+ .ssc = &ssc_reg[0],
23087+ .pdc = &pdc_tx_reg[0],
23088+ .mask = &ssc_tx_mask,
23089+ },
23090+ {
23091+ .name = "SSC0/I2S PCM Stereo in",
23092+ .ssc = &ssc_reg[0],
23093+ .pdc = &pdc_rx_reg[0],
23094+ .mask = &ssc_rx_mask,
23095+ }},
23096+ {{
23097+ .name = "SSC1/I2S PCM Stereo out",
23098+ .ssc = &ssc_reg[1],
23099+ .pdc = &pdc_tx_reg[1],
23100+ .mask = &ssc_tx_mask,
23101+ },
23102+ {
23103+ .name = "SSC1/I2S PCM Stereo in",
23104+ .ssc = &ssc_reg[1],
23105+ .pdc = &pdc_rx_reg[1],
23106+ .mask = &ssc_rx_mask,
23107+ }},
23108+ {{
23109+ .name = "SSC2/I2S PCM Stereo out",
23110+ .ssc = &ssc_reg[2],
23111+ .pdc = &pdc_tx_reg[2],
23112+ .mask = &ssc_tx_mask,
23113+ },
23114+ {
23115+ .name = "SSC1/I2S PCM Stereo in",
23116+ .ssc = &ssc_reg[2],
23117+ .pdc = &pdc_rx_reg[2],
23118+ .mask = &ssc_rx_mask,
23119+ }},
23120+};
23121+
23122+
23123+struct at91rm9200_ssc_state {
23124+ u32 ssc_cmr;
23125+ u32 ssc_rcmr;
23126+ u32 ssc_rfmr;
23127+ u32 ssc_tcmr;
23128+ u32 ssc_tfmr;
23129+ u32 ssc_sr;
23130+ u32 ssc_imr;
23131+};
23132+
23133+static struct at91rm9200_ssc_info {
23134+ char *name;
23135+ void __iomem *ssc_base;
23136+ u32 pid;
23137+ spinlock_t lock; /* lock for dir_mask */
23138+ int dir_mask; /* 0=unused, 1=playback, 2=capture */
23139+ struct semaphore *mutex;
23140+ int initialized;
23141+ int pcmfmt;
23142+ int rate;
23143+ at91rm9200_pcm_dma_params_t *dma_params[2];
23144+ struct at91rm9200_ssc_state ssc_state;
23145+
23146+} ssc_info[3] = {
23147+ {
23148+ .name = "ssc0",
23149+ .ssc_base = (void __iomem *) AT91_VA_BASE_SSC0,
23150+ .pid = AT91_ID_SSC0,
23151+ .lock = SPIN_LOCK_UNLOCKED,
23152+ .dir_mask = 0,
23153+ .mutex = &ssc0_mutex,
23154+ .initialized = 0,
23155+ },
23156+ {
23157+ .name = "ssc1",
23158+ .ssc_base = (void __iomem *) AT91_VA_BASE_SSC1,
23159+ .pid = AT91_ID_SSC1,
23160+ .lock = SPIN_LOCK_UNLOCKED,
23161+ .dir_mask = 0,
23162+ .mutex = &ssc1_mutex,
23163+ .initialized = 0,
23164+ },
23165+ {
23166+ .name = "ssc2",
23167+ .ssc_base = (void __iomem *) AT91_VA_BASE_SSC2,
23168+ .pid = AT91_ID_SSC2,
23169+ .lock = SPIN_LOCK_UNLOCKED,
23170+ .dir_mask = 0,
23171+ .mutex = &ssc2_mutex,
23172+ .initialized = 0,
23173+ },
23174+};
23175+
23176+
23177+static irqreturn_t at91rm9200_i2s_interrupt(int irq, void *dev_id)
23178+{
23179+ struct at91rm9200_ssc_info *ssc_p = dev_id;
23180+ at91rm9200_pcm_dma_params_t *dma_params;
23181+ u32 ssc_sr;
23182+ int i;
23183+
23184+ ssc_sr = at91_ssc_read(ssc_p->ssc_base + AT91_SSC_SR)
23185+ & at91_ssc_read(ssc_p->ssc_base + AT91_SSC_IMR);
23186+
23187+ /*
23188+ * Loop through the substreams attached to this SSC. If
23189+ * a DMA-related interrupt occurred on that substream, call
23190+ * the DMA interrupt handler function, if one has been
23191+ * registered in the dma_params structure by the PCM driver.
23192+ */
23193+ for (i = 0; i < ARRAY_SIZE(ssc_p->dma_params); i++) {
23194+ dma_params = ssc_p->dma_params[i];
23195+
23196+ if (dma_params != NULL && dma_params->dma_intr_handler != NULL &&
23197+ (ssc_sr &
23198+ (dma_params->mask->ssc_endx | dma_params->mask->ssc_endbuf)))
23199+
23200+ dma_params->dma_intr_handler(ssc_sr, dma_params->substream);
23201+ }
23202+
23203+ return IRQ_HANDLED;
23204+}
23205+
23206+static int at91rm9200_i2s_startup(struct snd_pcm_substream *substream)
23207+{
23208+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
23209+ struct at91rm9200_ssc_info *ssc_p = &ssc_info[rtd->cpu_dai->id];
23210+ int dir_mask;
23211+
23212+ DBG("i2s_startup: SSC_SR=0x%08lx\n",
23213+ at91_ssc_read(ssc_p->ssc_base + AT91_SSC_SR));
23214+ dir_mask = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0x1 : 0x2;
23215+
23216+ spin_lock_irq(&ssc_p->lock);
23217+ if (ssc_p->dir_mask & dir_mask) {
23218+ spin_unlock_irq(&ssc_p->lock);
23219+ return -EBUSY;
23220+ }
23221+ ssc_p->dir_mask |= dir_mask;
23222+ spin_unlock_irq(&ssc_p->lock);
23223+
23224+ return 0;
23225+}
23226+
23227+static void at91rm9200_i2s_shutdown(struct snd_pcm_substream *substream)
23228+{
23229+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
23230+ struct at91rm9200_ssc_info *ssc_p = &ssc_info[rtd->cpu_dai->id];
23231+ at91rm9200_pcm_dma_params_t *dma_params = rtd->cpu_dai->dma_data;
23232+ int dir, dir_mask;
23233+
23234+ dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1;
23235+
23236+ if (dma_params != NULL) {
23237+ at91_ssc_write(dma_params->ssc->cr, dma_params->mask->ssc_disable);
23238+ DBG("%s disabled SSC_SR=0x%08lx\n", (dir ? "receive" : "transmit"),
23239+ at91_ssc_read(ssc_p->ssc_base + AT91_SSC_SR));
23240+
23241+ dma_params->substream = NULL;
23242+ ssc_p->dma_params[dir] = NULL;
23243+ }
23244+
23245+ dir_mask = 1 << dir;
23246+
23247+ spin_lock_irq(&ssc_p->lock);
23248+ ssc_p->dir_mask &= ~dir_mask;
23249+ if (!ssc_p->dir_mask) {
23250+ /* Shutdown the SSC clock. */
23251+ DBG("Stopping pid %d clock\n", ssc_p->pid);
23252+ at91_sys_write(AT91_PMC_PCDR, 1<<ssc_p->pid);
23253+
23254+ if (ssc_p->initialized)
23255+ free_irq(ssc_p->pid, ssc_p);
23256+
23257+ /* Reset the SSC */
23258+ at91_ssc_write(ssc_p->ssc_base + AT91_SSC_CR, AT91_SSC_SWRST);
23259+
23260+ /* Force a re-init on the next hw_params() call. */
23261+ ssc_p->initialized = 0;
23262+ }
23263+ spin_unlock_irq(&ssc_p->lock);
23264+}
23265+
23266+#ifdef CONFIG_PM
23267+static int at91rm9200_i2s_suspend(struct platform_device *pdev,
23268+ struct snd_soc_cpu_dai *dai)
23269+{
23270+ struct at91rm9200_ssc_info *ssc_p;
23271+
23272+ if(!dai->active)
23273+ return 0;
23274+
23275+ ssc_p = &ssc_info[dai->id];
23276+
23277+ /* Save the status register before disabling transmit and receive. */
23278+ ssc_p->state->ssc_sr = at91_ssc_read(ssc_p->ssc_base + AT91_SSC_SR);
23279+ at91_ssc_write(ssc_p->ssc_base +
23280+ AT91_SSC_CR, AT91_SSC_TXDIS | AT91_SSC_RXDIS);
23281+
23282+ /* Save the current interrupt mask, then disable unmasked interrupts. */
23283+ ssc_p->state->ssc_imr = at91_ssc_read(ssc_p->ssc_base + AT91_SSC_IMR);
23284+ at91_ssc_write(ssc_p->ssc_base + AT91_SSC_IDR, ssc_p->state->ssc_imr);
23285+
23286+ ssc_p->state->ssc_cmr = at91_ssc_read(ssc_p->ssc_base + AT91_SSC_CMR);
23287+ ssc_p->state->ssc_rcmr = at91_ssc_read(ssc_p->ssc_base + AT91_SSC_RCMR);
23288+ ssc_p->state->ssc_rfmr = at91_ssc_read(ssc_p->ssc_base + AT91_SSC_RCMR);
23289+ ssc_p->state->ssc_tcmr = at91_ssc_read(ssc_p->ssc_base + AT91_SSC_RCMR);
23290+ ssc_p->state->ssc_tfmr = at91_ssc_read(ssc_p->ssc_base + AT91_SSC_RCMR);
23291+
23292+ return 0;
23293+}
23294+
23295+static int at91rm9200_i2s_resume(struct platform_device *pdev,
23296+ struct snd_soc_cpu_dai *dai)
23297+{
23298+ struct at91rm9200_ssc_info *ssc_p;
23299+ u32 cr_mask;
23300+
23301+ if(!dai->active)
23302+ return 0;
23303+
23304+ ssc_p = &ssc_info[dai->id];
23305+
23306+ at91_ssc_write(ssc_p->ssc_base + AT91_SSC_RCMR, ssc_p->state->ssc_tfmr);
23307+ at91_ssc_write(ssc_p->ssc_base + AT91_SSC_RCMR, ssc_p->state->ssc_tcmr);
23308+ at91_ssc_write(ssc_p->ssc_base + AT91_SSC_RCMR, ssc_p->state->ssc_rfmr);
23309+ at91_ssc_write(ssc_p->ssc_base + AT91_SSC_RCMR, ssc_p->state->ssc_rcmr);
23310+ at91_ssc_write(ssc_p->ssc_base + AT91_SSC_CMR, ssc_p->state->ssc_cmr);
23311+
23312+ at91_ssc_write(ssc_p->ssc_base + AT91_SSC_IER, ssc_p->state->ssc_imr);
23313+
23314+ at91_ssc_write(ssc_p->ssc_base + AT91_SSC_CR,
23315+ ((ssc_p->state->ssc_sr & AT91_SSC_RXENA) ? AT91_SSC_RXEN : 0) |
23316+ ((ssc_p->state->ssc_sr & AT91_SSC_TXENA) ? AT91_SSC_TXEN : 0));
23317+
23318+ return 0;
23319+}
23320+
23321+#else
23322+#define at91rm9200_i2s_suspend NULL
23323+#define at91rm9200_i2s_resume NULL
23324+#endif
23325+
23326+static unsigned int at91rm9200_i2s_config_sysclk(
23327+ struct snd_soc_cpu_dai *iface, struct snd_soc_clock_info *info,
23328+ unsigned int clk)
23329+{
23330+ /* Currently, there is only support for USB (12Mhz) mode */
23331+ if (clk != 12000000)
23332+ return 0;
23333+ return 12000000;
23334+}
23335+
23336+static int at91rm9200_i2s_hw_params(struct snd_pcm_substream *substream,
23337+ struct snd_pcm_hw_params *params)
23338+{
23339+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
23340+ int id = rtd->cpu_dai->id;
23341+ struct at91rm9200_ssc_info *ssc_p = &ssc_info[id];
23342+ at91rm9200_pcm_dma_params_t *dma_params;
23343+ unsigned int pcmfmt, rate;
23344+ int dir, channels, bits;
23345+ struct clk *mck_clk;
23346+ unsigned long bclk;
23347+ u32 div, period, tfmr, rfmr, tcmr, rcmr;
23348+ int ret;
23349+
23350+ /*
23351+ * Currently, there is only one set of dma params for
23352+ * each direction. If more are added, this code will
23353+ * have to be changed to select the proper set.
23354+ */
23355+ dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1;
23356+
23357+ dma_params = &ssc_dma_params[id][dir];
23358+ dma_params->substream = substream;
23359+
23360+ ssc_p->dma_params[dir] = dma_params;
23361+ rtd->cpu_dai->dma_data = dma_params;
23362+
23363+ rate = params_rate(params);
23364+ channels = params_channels(params);
23365+
23366+ pcmfmt = rtd->cpu_dai->dai_runtime.pcmfmt;
23367+ switch (pcmfmt) {
23368+ case SNDRV_PCM_FMTBIT_S16_LE:
23369+ /* likely this is all we'll ever support, but ... */
23370+ bits = 16;
23371+ dma_params->pdc_xfer_size = 2;
23372+ break;
23373+ default:
23374+ printk(KERN_WARNING "at91rm9200-i2s: unsupported format %x\n",
23375+ pcmfmt);
23376+ return -EINVAL;
23377+ }
23378+
23379+ /* Don't allow both SSC substreams to initialize at the same time. */
23380+ down(ssc_p->mutex);
23381+
23382+ /*
23383+ * If this SSC is alreadly initialized, then this substream must use
23384+ * the same format and rate.
23385+ */
23386+ if (ssc_p->initialized) {
23387+ if (pcmfmt != ssc_p->pcmfmt || rate != ssc_p->rate) {
23388+ printk(KERN_WARNING "at91rm9200-i2s: "
23389+ "incompatible substream in other direction\n");
23390+ up(ssc_p->mutex);
23391+ return -EINVAL;
23392+ }
23393+ } else {
23394+ /* Enable PMC peripheral clock for this SSC */
23395+ DBG("Starting pid %d clock\n", ssc_p->pid);
23396+ at91_sys_write(AT91_PMC_PCER, 1<<ssc_p->pid);
23397+
23398+ /* Reset the SSC */
23399+ at91_ssc_write(ssc_p->ssc_base + AT91_SSC_CR, AT91_SSC_SWRST);
23400+
23401+ at91_ssc_write(ssc_p->ssc_base + AT91_PDC_RPR, 0);
23402+ at91_ssc_write(ssc_p->ssc_base + AT91_PDC_RCR, 0);
23403+ at91_ssc_write(ssc_p->ssc_base + AT91_PDC_RNPR, 0);
23404+ at91_ssc_write(ssc_p->ssc_base + AT91_PDC_RNCR, 0);
23405+ at91_ssc_write(ssc_p->ssc_base + AT91_PDC_TPR, 0);
23406+ at91_ssc_write(ssc_p->ssc_base + AT91_PDC_TCR, 0);
23407+ at91_ssc_write(ssc_p->ssc_base + AT91_PDC_TNPR, 0);
23408+ at91_ssc_write(ssc_p->ssc_base + AT91_PDC_TNCR, 0);
23409+
23410+ mck_clk = clk_get(NULL, "mck");
23411+
23412+ div = rtd->cpu_dai->dai_runtime.priv >> 16;
23413+ period = rtd->cpu_dai->dai_runtime.priv & 0xffff;
23414+ bclk = 60000000 / (2 * div);
23415+
23416+ DBG("mck %ld fsbd %d bfs %d bfs_real %d bclk %ld div %d period %d\n",
23417+ clk_get_rate(mck_clk),
23418+ SND_SOC_FSBD(6),
23419+ rtd->cpu_dai->dai_runtime.bfs,
23420+ SND_SOC_FSBD_REAL(rtd->cpu_dai->dai_runtime.bfs),
23421+ bclk,
23422+ div,
23423+ period);
23424+
23425+ clk_put(mck_clk);
23426+
23427+ at91_ssc_write(ssc_p->ssc_base + AT91_SSC_CMR, div);
23428+
23429+ /*
23430+ * Setup the TFMR and RFMR for the proper data format.
23431+ */
23432+ tfmr =
23433+ (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE)
23434+ | (( 0 << 23) & AT91_SSC_FSDEN)
23435+ | (( AT91_SSC_FSOS_NEGATIVE ) & AT91_SSC_FSOS)
23436+ | (((bits - 1) << 16) & AT91_SSC_FSLEN)
23437+ | (((channels - 1) << 8) & AT91_SSC_DATNB)
23438+ | (( 1 << 7) & AT91_SSC_MSBF)
23439+ | (( 0 << 5) & AT91_SSC_DATDEF)
23440+ | (((bits - 1) << 0) & AT91_SSC_DATALEN);
23441+ DBG("SSC_TFMR=0x%08x\n", tfmr);
23442+ at91_ssc_write(ssc_p->ssc_base + AT91_SSC_TFMR, tfmr);
23443+
23444+ rfmr =
23445+ (( AT91_SSC_FSEDGE_POSITIVE ) & AT91_SSC_FSEDGE)
23446+ | (( AT91_SSC_FSOS_NONE ) & AT91_SSC_FSOS)
23447+ | (( 0 << 16) & AT91_SSC_FSLEN)
23448+ | (((channels - 1) << 8) & AT91_SSC_DATNB)
23449+ | (( 1 << 7) & AT91_SSC_MSBF)
23450+ | (( 0 << 5) & AT91_SSC_LOOP)
23451+ | (((bits - 1) << 0) & AT91_SSC_DATALEN);
23452+
23453+ DBG("SSC_RFMR=0x%08x\n", rfmr);
23454+ at91_ssc_write(ssc_p->ssc_base + AT91_SSC_RFMR, rfmr);
23455+
23456+ /*
23457+ * Setup the TCMR and RCMR to generate the proper BCLK
23458+ * and LRC signals.
23459+ */
23460+ tcmr =
23461+ (( period << 24) & AT91_SSC_PERIOD)
23462+ | (( 1 << 16) & AT91_SSC_STTDLY)
23463+ | (( AT91_SSC_START_FALLING_RF ) & AT91_SSC_START)
23464+ | (( AT91_SSC_CKI_FALLING ) & AT91_SSC_CKI)
23465+ | (( AT91_SSC_CKO_CONTINUOUS ) & AT91_SSC_CKO)
23466+ | (( AT91_SSC_CKS_DIV ) & AT91_SSC_CKS);
23467+
23468+ DBG("SSC_TCMR=0x%08x\n", tcmr);
23469+ at91_ssc_write(ssc_p->ssc_base + AT91_SSC_TCMR, tcmr);
23470+
23471+ rcmr =
23472+ (( 0 << 24) & AT91_SSC_PERIOD)
23473+ | (( 1 << 16) & AT91_SSC_STTDLY)
23474+ | (( AT91_SSC_START_TX_RX ) & AT91_SSC_START)
23475+ | (( AT91_SSC_CK_RISING ) & AT91_SSC_CKI)
23476+ | (( AT91_SSC_CKO_NONE ) & AT91_SSC_CKO)
23477+ | (( AT91_SSC_CKS_CLOCK ) & AT91_SSC_CKS);
23478+
23479+ DBG("SSC_RCMR=0x%08x\n", rcmr);
23480+ at91_ssc_write(ssc_p->ssc_base + AT91_SSC_RCMR, rcmr);
23481+
23482+ if ((ret = request_irq(ssc_p->pid, at91rm9200_i2s_interrupt,
23483+ 0, ssc_p->name, ssc_p)) < 0) {
23484+ printk(KERN_WARNING "at91rm9200-i2s: request_irq failure\n");
23485+ return ret;
23486+ }
23487+
23488+ /*
23489+ * Save the current substream parameters in order to check
23490+ * that the substream in the opposite direction uses the
23491+ * same parameters.
23492+ */
23493+ ssc_p->pcmfmt = pcmfmt;
23494+ ssc_p->rate = rate;
23495+ ssc_p->initialized = 1;
23496+
23497+ DBG("hw_params: SSC initialized\n");
23498+ }
23499+
23500+ up(ssc_p->mutex);
23501+
23502+ return 0;
23503+}
23504+
23505+
23506+static int at91rm9200_i2s_prepare(struct snd_pcm_substream *substream)
23507+{
23508+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
23509+ at91rm9200_pcm_dma_params_t *dma_params = rtd->cpu_dai->dma_data;
23510+
23511+ at91_ssc_write(dma_params->ssc->cr, dma_params->mask->ssc_enable);
23512+
23513+ DBG("%s enabled SSC_SR=0x%08lx\n",
23514+ substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? "transmit" : "receive",
23515+ at91_ssc_read(ssc_info[rtd->cpu_dai->id].ssc_base + AT91_SSC_SR));
23516+ return 0;
23517+}
23518+
23519+
23520+struct snd_soc_cpu_dai at91rm9200_i2s_dai[] = {
23521+ { .name = "at91rm9200-ssc0/i2s",
23522+ .id = 0,
23523+ .type = SND_SOC_DAI_I2S,
23524+ .suspend = at91rm9200_i2s_suspend,
23525+ .resume = at91rm9200_i2s_resume,
23526+ .config_sysclk = at91rm9200_i2s_config_sysclk,
23527+ .playback = {
23528+ .channels_min = 1,
23529+ .channels_max = 2,},
23530+ .capture = {
23531+ .channels_min = 1,
23532+ .channels_max = 2,},
23533+ .ops = {
23534+ .startup = at91rm9200_i2s_startup,
23535+ .shutdown = at91rm9200_i2s_shutdown,
23536+ .prepare = at91rm9200_i2s_prepare,
23537+ .hw_params = at91rm9200_i2s_hw_params,},
23538+ .caps = {
23539+ .mode = &at91rm9200_i2s[0],
23540+ .num_modes = ARRAY_SIZE(at91rm9200_i2s),},
23541+ },
23542+ { .name = "at91rm9200-ssc1/i2s",
23543+ .id = 1,
23544+ .type = SND_SOC_DAI_I2S,
23545+ .suspend = at91rm9200_i2s_suspend,
23546+ .resume = at91rm9200_i2s_resume,
23547+ .config_sysclk = at91rm9200_i2s_config_sysclk,
23548+ .playback = {
23549+ .channels_min = 1,
23550+ .channels_max = 2,},
23551+ .capture = {
23552+ .channels_min = 1,
23553+ .channels_max = 2,},
23554+ .ops = {
23555+ .startup = at91rm9200_i2s_startup,
23556+ .shutdown = at91rm9200_i2s_shutdown,
23557+ .prepare = at91rm9200_i2s_prepare,
23558+ .hw_params = at91rm9200_i2s_hw_params,},
23559+ .caps = {
23560+ .mode = &at91rm9200_i2s[0],
23561+ .num_modes = ARRAY_SIZE(at91rm9200_i2s),},
23562+ },
23563+ { .name = "at91rm9200-ssc2/i2s",
23564+ .id = 2,
23565+ .type = SND_SOC_DAI_I2S,
23566+ .suspend = at91rm9200_i2s_suspend,
23567+ .resume = at91rm9200_i2s_resume,
23568+ .config_sysclk = at91rm9200_i2s_config_sysclk,
23569+ .playback = {
23570+ .channels_min = 1,
23571+ .channels_max = 2,},
23572+ .capture = {
23573+ .channels_min = 1,
23574+ .channels_max = 2,},
23575+ .ops = {
23576+ .startup = at91rm9200_i2s_startup,
23577+ .shutdown = at91rm9200_i2s_shutdown,
23578+ .prepare = at91rm9200_i2s_prepare,
23579+ .hw_params = at91rm9200_i2s_hw_params,},
23580+ .caps = {
23581+ .mode = &at91rm9200_i2s[0],
23582+ .num_modes = ARRAY_SIZE(at91rm9200_i2s),},
23583+ },
23584+};
23585+
23586+EXPORT_SYMBOL_GPL(at91rm9200_i2s_dai);
23587+
23588+/* Module information */
23589+MODULE_AUTHOR("Frank Mandarino, fmandarino@endrelia.com, www.endrelia.com");
23590+MODULE_DESCRIPTION("AT91RM9200 I2S ASoC Interface");
23591+MODULE_LICENSE("GPL");
23592Index: linux-2.6-pxa-new/sound/soc/at91/at91rm9200-pcm.c
23593===================================================================
23594--- /dev/null
23595+++ linux-2.6-pxa-new/sound/soc/at91/at91rm9200-pcm.c
23596@@ -0,0 +1,428 @@
23597+/*
23598+ * at91rm9200-pcm.c -- ALSA PCM interface for the Atmel AT91RM9200 chip.
23599+ *
23600+ * Author: Frank Mandarino <fmandarino@endrelia.com>
23601+ * Endrelia Technologies Inc.
23602+ * Created: Mar 3, 2006
23603+ *
23604+ * Based on pxa2xx-pcm.c by:
23605+ *
23606+ * Author: Nicolas Pitre
23607+ * Created: Nov 30, 2004
23608+ * Copyright: (C) 2004 MontaVista Software, Inc.
23609+ *
23610+ * This program is free software; you can redistribute it and/or modify
23611+ * it under the terms of the GNU General Public License version 2 as
23612+ * published by the Free Software Foundation.
23613+ */
23614+
23615+#include <linux/module.h>
23616+#include <linux/init.h>
23617+#include <linux/platform_device.h>
23618+#include <linux/slab.h>
23619+#include <linux/dma-mapping.h>
23620+
23621+#include <sound/driver.h>
23622+#include <sound/core.h>
23623+#include <sound/pcm.h>
23624+#include <sound/pcm_params.h>
23625+#include <sound/soc.h>
23626+
23627+#include <asm/arch/at91rm9200.h>
23628+#include <asm/arch/at91rm9200_ssc.h>
23629+#include <asm/arch/at91rm9200_pdc.h>
23630+#include <asm/arch/hardware.h>
23631+
23632+#include "at91rm9200-pcm.h"
23633+
23634+#if 0
23635+#define DBG(x...) printk(KERN_INFO "at91rm9200-pcm: " x)
23636+#else
23637+#define DBG(x...)
23638+#endif
23639+
23640+static const snd_pcm_hardware_t at91rm9200_pcm_hardware = {
23641+ .info = SNDRV_PCM_INFO_MMAP |
23642+ SNDRV_PCM_INFO_MMAP_VALID |
23643+ SNDRV_PCM_INFO_INTERLEAVED |
23644+ SNDRV_PCM_INFO_PAUSE,
23645+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
23646+ .period_bytes_min = 32,
23647+ .period_bytes_max = 8192,
23648+ .periods_min = 2,
23649+ .periods_max = 1024,
23650+ .buffer_bytes_max = 32 * 1024,
23651+};
23652+
23653+struct at91rm9200_runtime_data {
23654+ at91rm9200_pcm_dma_params_t *params;
23655+ dma_addr_t dma_buffer; /* physical address of dma buffer */
23656+ dma_addr_t dma_buffer_end; /* first address beyond DMA buffer */
23657+ size_t period_size;
23658+ dma_addr_t period_ptr; /* physical address of next period */
23659+ u32 pdc_xpr_save; /* PDC register save */
23660+ u32 pdc_xcr_save;
23661+ u32 pdc_xnpr_save;
23662+ u32 pdc_xncr_save;
23663+};
23664+
23665+static void at91rm9200_pcm_dma_irq(u32 ssc_sr,
23666+ struct snd_pcm_substream *substream)
23667+{
23668+ struct at91rm9200_runtime_data *prtd = substream->runtime->private_data;
23669+ at91rm9200_pcm_dma_params_t *params = prtd->params;
23670+ static int count = 0;
23671+
23672+ count++;
23673+
23674+ if (ssc_sr & params->mask->ssc_endbuf) {
23675+
23676+ printk(KERN_WARNING
23677+ "at91rm9200-pcm: buffer %s on %s (SSC_SR=%#x, count=%d)\n",
23678+ substream->stream == SNDRV_PCM_STREAM_PLAYBACK
23679+ ? "underrun" : "overrun",
23680+ params->name, ssc_sr, count);
23681+
23682+ /* re-start the PDC */
23683+ at91_ssc_write(params->pdc->ptcr, params->mask->pdc_disable);
23684+
23685+ prtd->period_ptr += prtd->period_size;
23686+ if (prtd->period_ptr >= prtd->dma_buffer_end) {
23687+ prtd->period_ptr = prtd->dma_buffer;
23688+ }
23689+
23690+ at91_ssc_write(params->pdc->xpr, prtd->period_ptr);
23691+ at91_ssc_write(params->pdc->xcr,
23692+ prtd->period_size / params->pdc_xfer_size);
23693+
23694+ at91_ssc_write(params->pdc->ptcr, params->mask->pdc_enable);
23695+ }
23696+
23697+ if (ssc_sr & params->mask->ssc_endx) {
23698+
23699+ /* Load the PDC next pointer and counter registers */
23700+ prtd->period_ptr += prtd->period_size;
23701+ if (prtd->period_ptr >= prtd->dma_buffer_end) {
23702+ prtd->period_ptr = prtd->dma_buffer;
23703+ }
23704+ at91_ssc_write(params->pdc->xnpr, prtd->period_ptr);
23705+ at91_ssc_write(params->pdc->xncr,
23706+ prtd->period_size / params->pdc_xfer_size);
23707+ }
23708+
23709+ snd_pcm_period_elapsed(substream);
23710+}
23711+
23712+static int at91rm9200_pcm_hw_params(struct snd_pcm_substream *substream,
23713+ struct snd_pcm_hw_params *params)
23714+{
23715+ snd_pcm_runtime_t *runtime = substream->runtime;
23716+ struct at91rm9200_runtime_data *prtd = runtime->private_data;
23717+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
23718+
23719+ /* this may get called several times by oss emulation
23720+ * with different params */
23721+
23722+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
23723+ runtime->dma_bytes = params_buffer_bytes(params);
23724+
23725+ prtd->params = rtd->cpu_dai->dma_data;
23726+ prtd->params->dma_intr_handler = at91rm9200_pcm_dma_irq;
23727+
23728+ prtd->dma_buffer = runtime->dma_addr;
23729+ prtd->dma_buffer_end = runtime->dma_addr + runtime->dma_bytes;
23730+ prtd->period_size = params_period_bytes(params);
23731+
23732+ DBG("hw_params: DMA for %s initialized (dma_bytes=%d, period_size=%d)\n",
23733+ prtd->params->name, runtime->dma_bytes, prtd->period_size);
23734+ return 0;
23735+}
23736+
23737+static int at91rm9200_pcm_hw_free(struct snd_pcm_substream *substream)
23738+{
23739+ struct at91rm9200_runtime_data *prtd = substream->runtime->private_data;
23740+ at91rm9200_pcm_dma_params_t *params = prtd->params;
23741+
23742+ if (params != NULL) {
23743+ at91_ssc_write(params->pdc->ptcr, params->mask->pdc_disable);
23744+ prtd->params->dma_intr_handler = NULL;
23745+ }
23746+
23747+ return 0;
23748+}
23749+
23750+static int at91rm9200_pcm_prepare(struct snd_pcm_substream *substream)
23751+{
23752+ struct at91rm9200_runtime_data *prtd = substream->runtime->private_data;
23753+ at91rm9200_pcm_dma_params_t *params = prtd->params;
23754+
23755+ at91_ssc_write(params->ssc->idr,
23756+ params->mask->ssc_endx | params->mask->ssc_endbuf);
23757+
23758+ at91_ssc_write(params->pdc->ptcr, params->mask->pdc_disable);
23759+ return 0;
23760+}
23761+
23762+static int at91rm9200_pcm_trigger(struct snd_pcm_substream *substream,
23763+ int cmd)
23764+{
23765+ struct at91rm9200_runtime_data *prtd = substream->runtime->private_data;
23766+ at91rm9200_pcm_dma_params_t *params = prtd->params;
23767+ int ret = 0;
23768+
23769+ switch (cmd) {
23770+ case SNDRV_PCM_TRIGGER_START:
23771+ prtd->period_ptr = prtd->dma_buffer;
23772+
23773+ at91_ssc_write(params->pdc->xpr, prtd->period_ptr);
23774+ at91_ssc_write(params->pdc->xcr,
23775+ prtd->period_size / params->pdc_xfer_size);
23776+
23777+ prtd->period_ptr += prtd->period_size;
23778+ at91_ssc_write(params->pdc->xnpr, prtd->period_ptr);
23779+ at91_ssc_write(params->pdc->xncr,
23780+ prtd->period_size / params->pdc_xfer_size);
23781+
23782+ DBG("trigger: period_ptr=%lx, xpr=%lx, xcr=%ld, xnpr=%lx, xncr=%ld\n",
23783+ (unsigned long) prtd->period_ptr,
23784+ at91_ssc_read(params->pdc->xpr),
23785+ at91_ssc_read(params->pdc->xcr),
23786+ at91_ssc_read(params->pdc->xnpr),
23787+ at91_ssc_read(params->pdc->xncr));
23788+
23789+ at91_ssc_write(params->ssc->ier,
23790+ params->mask->ssc_endx | params->mask->ssc_endbuf);
23791+
23792+ at91_ssc_write(params->pdc->ptcr, params->mask->pdc_enable);
23793+
23794+ DBG("sr=%lx imr=%lx\n", at91_ssc_read(params->ssc->ier - 4),
23795+ at91_ssc_read(params->ssc->ier + 8));
23796+ break;
23797+
23798+ case SNDRV_PCM_TRIGGER_STOP:
23799+ case SNDRV_PCM_TRIGGER_SUSPEND:
23800+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
23801+ at91_ssc_write(params->pdc->ptcr, params->mask->pdc_disable);
23802+ break;
23803+
23804+ case SNDRV_PCM_TRIGGER_RESUME:
23805+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
23806+ at91_ssc_write(params->pdc->ptcr, params->mask->pdc_enable);
23807+ break;
23808+
23809+ default:
23810+ ret = -EINVAL;
23811+ }
23812+
23813+ return ret;
23814+}
23815+
23816+static snd_pcm_uframes_t at91rm9200_pcm_pointer(
23817+ struct snd_pcm_substream *substream)
23818+{
23819+ struct snd_pcm_runtime *runtime = substream->runtime;
23820+ struct at91rm9200_runtime_data *prtd = runtime->private_data;
23821+ at91rm9200_pcm_dma_params_t *params = prtd->params;
23822+ dma_addr_t ptr;
23823+ snd_pcm_uframes_t x;
23824+
23825+ ptr = (dma_addr_t) at91_ssc_read(params->pdc->xpr);
23826+ x = bytes_to_frames(runtime, ptr - prtd->dma_buffer);
23827+
23828+ if (x == runtime->buffer_size)
23829+ x = 0;
23830+ return x;
23831+}
23832+
23833+static int at91rm9200_pcm_open(struct snd_pcm_substream *substream)
23834+{
23835+ struct snd_pcm_runtime *runtime = substream->runtime;
23836+ struct at91rm9200_runtime_data *prtd;
23837+ int ret = 0;
23838+
23839+ snd_soc_set_runtime_hwparams(substream, &at91rm9200_pcm_hardware);
23840+
23841+ /* ensure that buffer size is a multiple of period size */
23842+ ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
23843+ if (ret < 0)
23844+ goto out;
23845+
23846+ prtd = kzalloc(sizeof(struct at91rm9200_runtime_data), GFP_KERNEL);
23847+ if (prtd == NULL) {
23848+ ret = -ENOMEM;
23849+ goto out;
23850+ }
23851+ runtime->private_data = prtd;
23852+
23853+ out:
23854+ return ret;
23855+}
23856+
23857+static int at91rm9200_pcm_close(struct snd_pcm_substream *substream)
23858+{
23859+ struct at91rm9200_runtime_data *prtd = substream->runtime->private_data;
23860+
23861+ kfree(prtd);
23862+ return 0;
23863+}
23864+
23865+static int at91rm9200_pcm_mmap(struct snd_pcm_substream *substream,
23866+ struct vm_area_struct *vma)
23867+{
23868+ struct snd_pcm_runtime *runtime = substream->runtime;
23869+
23870+ return dma_mmap_writecombine(substream->pcm->card->dev, vma,
23871+ runtime->dma_area,
23872+ runtime->dma_addr,
23873+ runtime->dma_bytes);
23874+}
23875+
23876+struct snd_pcm_ops at91rm9200_pcm_ops = {
23877+ .open = at91rm9200_pcm_open,
23878+ .close = at91rm9200_pcm_close,
23879+ .ioctl = snd_pcm_lib_ioctl,
23880+ .hw_params = at91rm9200_pcm_hw_params,
23881+ .hw_free = at91rm9200_pcm_hw_free,
23882+ .prepare = at91rm9200_pcm_prepare,
23883+ .trigger = at91rm9200_pcm_trigger,
23884+ .pointer = at91rm9200_pcm_pointer,
23885+ .mmap = at91rm9200_pcm_mmap,
23886+};
23887+
23888+static int at91rm9200_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
23889+ int stream)
23890+{
23891+ struct snd_pcm_substream *substream = pcm->streams[stream].substream;
23892+ struct snd_dma_buffer *buf = &substream->dma_buffer;
23893+ size_t size = at91rm9200_pcm_hardware.buffer_bytes_max;
23894+
23895+ buf->dev.type = SNDRV_DMA_TYPE_DEV;
23896+ buf->dev.dev = pcm->card->dev;
23897+ buf->private_data = NULL;
23898+ buf->area = dma_alloc_writecombine(pcm->card->dev, size,
23899+ &buf->addr, GFP_KERNEL);
23900+
23901+ DBG("preallocate_dma_buffer: area=%p, addr=%p, size=%d\n",
23902+ (void *) buf->area,
23903+ (void *) buf->addr,
23904+ size);
23905+
23906+ if (!buf->area)
23907+ return -ENOMEM;
23908+
23909+ buf->bytes = size;
23910+ return 0;
23911+}
23912+
23913+static u64 at91rm9200_pcm_dmamask = 0xffffffff;
23914+
23915+static int at91rm9200_pcm_new(struct snd_card *card,
23916+ struct snd_soc_codec_dai *dai, struct snd_pcm *pcm)
23917+{
23918+ int ret = 0;
23919+
23920+ if (!card->dev->dma_mask)
23921+ card->dev->dma_mask = &at91rm9200_pcm_dmamask;
23922+ if (!card->dev->coherent_dma_mask)
23923+ card->dev->coherent_dma_mask = 0xffffffff;
23924+
23925+ if (dai->playback.channels_min) {
23926+ ret = at91rm9200_pcm_preallocate_dma_buffer(pcm,
23927+ SNDRV_PCM_STREAM_PLAYBACK);
23928+ if (ret)
23929+ goto out;
23930+ }
23931+
23932+ if (dai->capture.channels_min) {
23933+ ret = at91rm9200_pcm_preallocate_dma_buffer(pcm,
23934+ SNDRV_PCM_STREAM_CAPTURE);
23935+ if (ret)
23936+ goto out;
23937+ }
23938+ out:
23939+ return ret;
23940+}
23941+
23942+static void at91rm9200_pcm_free_dma_buffers(struct snd_pcm *pcm)
23943+{
23944+ struct snd_pcm_substream *substream;
23945+ struct snd_dma_buffer *buf;
23946+ int stream;
23947+
23948+ for (stream = 0; stream < 2; stream++) {
23949+ substream = pcm->streams[stream].substream;
23950+ if (!substream)
23951+ continue;
23952+
23953+ buf = &substream->dma_buffer;
23954+ if (!buf->area)
23955+ continue;
23956+
23957+ dma_free_writecombine(pcm->card->dev, buf->bytes,
23958+ buf->area, buf->addr);
23959+ buf->area = NULL;
23960+ }
23961+}
23962+
23963+static int at91rm9200_pcm_suspend(struct platform_device *pdev,
23964+ struct snd_soc_cpu_dai *dai)
23965+{
23966+ struct snd_pcm_runtime *runtime = dai->runtime;
23967+ struct at91rm9200_runtime_data *prtd;
23968+ at91rm9200_pcm_dma_params_t *params;
23969+
23970+ if (!runtime)
23971+ return 0;
23972+
23973+ prtd = runtime->private_data;
23974+ params = prtd->params;
23975+
23976+ /* disable the PDC and save the PDC registers */
23977+
23978+ at91_ssc_write(params->pdc->ptcr, params->mask->pdc_disable);
23979+
23980+ prtd->pdc_xpr_save = at91_ssc_read(params->pdc->xpr);
23981+ prtd->pdc_xcr_save = at91_ssc_read(params->pdc->xcr);
23982+ prtd->pdc_xnpr_save = at91_ssc_read(params->pdc->xnpr);
23983+ prtd->pdc_xncr_save = at91_ssc_read(params->pdc->xncr);
23984+
23985+ return 0;
23986+}
23987+
23988+static int at91rm9200_pcm_resume(struct platform_device *pdev,
23989+ struct snd_soc_cpu_dai *dai)
23990+{
23991+ struct snd_pcm_runtime *runtime = dai->runtime;
23992+ struct at91rm9200_runtime_data *prtd;
23993+ at91rm9200_pcm_dma_params_t *params;
23994+
23995+ if (!runtime)
23996+ return 0;
23997+
23998+ prtd = runtime->private_data;
23999+ params = prtd->params;
24000+
24001+ /* restore the PDC registers and enable the PDC */
24002+ at91_ssc_write(params->pdc->xpr, prtd->pdc_xpr_save);
24003+ at91_ssc_write(params->pdc->xcr, prtd->pdc_xcr_save);
24004+ at91_ssc_write(params->pdc->xnpr, prtd->pdc_xnpr_save);
24005+ at91_ssc_write(params->pdc->xncr, prtd->pdc_xncr_save);
24006+
24007+ at91_ssc_write(params->pdc->ptcr, params->mask->pdc_enable);
24008+ return 0;
24009+}
24010+
24011+struct snd_soc_platform at91rm9200_soc_platform = {
24012+ .name = "at91rm9200-audio",
24013+ .pcm_ops = &at91rm9200_pcm_ops,
24014+ .pcm_new = at91rm9200_pcm_new,
24015+ .pcm_free = at91rm9200_pcm_free_dma_buffers,
24016+ .suspend = at91rm9200_pcm_suspend,
24017+ .resume = at91rm9200_pcm_resume,
24018+};
24019+
24020+EXPORT_SYMBOL_GPL(at91rm9200_soc_platform);
24021+
24022+MODULE_AUTHOR("Frank Mandarino <fmandarino@endrelia.com>");
24023+MODULE_DESCRIPTION("Atmel AT91RM9200 PCM module");
24024+MODULE_LICENSE("GPL");
24025Index: linux-2.6-pxa-new/sound/soc/at91/at91rm9200-pcm.h
24026===================================================================
24027--- /dev/null
24028+++ linux-2.6-pxa-new/sound/soc/at91/at91rm9200-pcm.h
24029@@ -0,0 +1,75 @@
24030+/*
24031+ * at91rm9200-pcm.h - ALSA PCM interface for the Atmel AT91RM9200 chip
24032+ *
24033+ * Author: Frank Mandarino <fmandarino@endrelia.com>
24034+ * Endrelia Technologies Inc.
24035+ * Created: Mar 3, 2006
24036+ *
24037+ * Based on pxa2xx-pcm.h by:
24038+ *
24039+ * Author: Nicolas Pitre
24040+ * Created: Nov 30, 2004
24041+ * Copyright: MontaVista Software, Inc.
24042+ *
24043+ * This program is free software; you can redistribute it and/or modify
24044+ * it under the terms of the GNU General Public License version 2 as
24045+ * published by the Free Software Foundation.
24046+ */
24047+
24048+/*
24049+ * Registers and status bits that are required by the PCM driver.
24050+ */
24051+struct at91rm9200_ssc_regs {
24052+ void __iomem *cr; /* SSC control */
24053+ void __iomem *ier; /* SSC interrupt enable */
24054+ void __iomem *idr; /* SSC interrupt disable */
24055+};
24056+
24057+struct at91rm9200_pdc_regs {
24058+ void __iomem *xpr; /* PDC recv/trans pointer */
24059+ void __iomem *xcr; /* PDC recv/trans counter */
24060+ void __iomem *xnpr; /* PDC next recv/trans pointer */
24061+ void __iomem *xncr; /* PDC next recv/trans counter */
24062+ void __iomem *ptcr; /* PDC transfer control */
24063+};
24064+
24065+struct at91rm9200_ssc_mask {
24066+ u32 ssc_enable; /* SSC recv/trans enable */
24067+ u32 ssc_disable; /* SSC recv/trans disable */
24068+ u32 ssc_endx; /* SSC ENDTX or ENDRX */
24069+ u32 ssc_endbuf; /* SSC TXBUFE or RXBUFF */
24070+ u32 pdc_enable; /* PDC recv/trans enable */
24071+ u32 pdc_disable; /* PDC recv/trans disable */
24072+};
24073+
24074+
24075+/*
24076+ * This structure, shared between the PCM driver and the interface,
24077+ * contains all information required by the PCM driver to perform the
24078+ * PDC DMA operation. All fields except dma_intr_handler() are initialized
24079+ * by the interface. The dms_intr_handler() pointer is set by the PCM
24080+ * driver and called by the interface SSC interrupt handler if it is
24081+ * non-NULL.
24082+ */
24083+typedef struct {
24084+ char *name; /* stream identifier */
24085+ int pdc_xfer_size; /* PDC counter increment in bytes */
24086+ struct at91rm9200_ssc_regs *ssc; /* SSC register addresses */
24087+ struct at91rm9200_pdc_regs *pdc; /* PDC receive/transmit registers */
24088+ struct at91rm9200_ssc_mask *mask;/* SSC & PDC status bits */
24089+ snd_pcm_substream_t *substream;
24090+ void (*dma_intr_handler)(u32, snd_pcm_substream_t *);
24091+} at91rm9200_pcm_dma_params_t;
24092+
24093+extern struct snd_soc_cpu_dai at91rm9200_i2s_dai[3];
24094+extern struct snd_soc_platform at91rm9200_soc_platform;
24095+
24096+
24097+/*
24098+ * SSC I/O helpers.
24099+ * E.g., at91_ssc_write(AT91_SSC(1) + AT91_SSC_CR, AT91_SSC_RXEN);
24100+ */
24101+#define AT91_SSC(x) (((x)==0) ? AT91_VA_BASE_SSC0 :\
24102+ ((x)==1) ? AT91_VA_BASE_SSC1 : ((x)==2) ? AT91_VA_BASE_SSC2 : NULL)
24103+#define at91_ssc_read(a) ((unsigned long) __raw_readl(a))
24104+#define at91_ssc_write(a,v) __raw_writel((v),(a))
24105Index: linux-2.6-pxa-new/sound/soc/imx/imx-ssi.c
24106===================================================================
24107--- /dev/null
24108+++ linux-2.6-pxa-new/sound/soc/imx/imx-ssi.c
24109@@ -0,0 +1,452 @@
24110+/*
24111+ * imx-ssi.c -- SSI driver for Freescale IMX
24112+ *
24113+ * Copyright 2006 Wolfson Microelectronics PLC.
24114+ * Author: Liam Girdwood
24115+ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
24116+ *
24117+ * Based on mxc-alsa-mc13783 (C) 2006 Freescale.
24118+ *
24119+ * This program is free software; you can redistribute it and/or modify it
24120+ * under the terms of the GNU General Public License as published by the
24121+ * Free Software Foundation; either version 2 of the License, or (at your
24122+ * option) any later version.
24123+ *
24124+ * Revision history
24125+ * 29th Aug 2006 Initial version.
24126+ *
24127+ */
24128+
24129+#define IMX_DSP_DAIFMT \
24130+ ( SND_SOC_DAIFMT_DSP__A |SND_SOC_DAIFMT_DSP_B | \
24131+ SND_SOC_DAIFMT_CBS_CFS |SND_SOC_DAIFMT_CBM_CFS | \
24132+ SND_SOC_DAIFMT_CBS_CFM |SND_SOC_DAIFMT_NB_NF |\
24133+ SND_SOC_DAIFMT_NB_IF)
24134+
24135+#define IMX_DSP_DIR \
24136+ (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
24137+
24138+#define IMX_DSP_RATES \
24139+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | \
24140+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
24141+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
24142+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \
24143+ SNDRV_PCM_RATE_96000)
24144+
24145+#define IMX_DSP_BITS \
24146+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
24147+ SNDRV_PCM_FMTBIT_S24_LE)
24148+
24149+static struct snd_soc_dai_mode imx_dsp_pcm_modes[] = {
24150+
24151+ /* frame master and clock slave mode */
24152+ {IMX_DSP_DAIFMT | SND_SOC_DAIFMT_CBM_CFS,
24153+ SND_SOC_DAITDM_LRDW(0,0), IMX_DSP_BITS, IMX_DSP_RATES,
24154+ IMX_DSP_DIR, 0, SND_SOC_FS_ALL,
24155+ SND_SOC_FSB(32) | SND_SOC_FSB(32) | SND_SOC_FSB(16)},
24156+
24157+};
24158+
24159+static imx_pcm_dma_params_t imx_ssi1_pcm_stereo_out = {
24160+ .name = "SSI1 PCM Stereo out",
24161+ .params = {
24162+ .bd_number = 1,
24163+ .transfer_type = emi_2_per,
24164+ .watermark_level = SDMA_TXFIFO_WATERMARK,
24165+ .word_size = TRANSFER_16BIT, // maybe add this in setup func
24166+ .per_address = SSI1_STX0,
24167+ .event_id = DMA_REQ_SSI1_TX1,
24168+ .peripheral_type = SSI,
24169+ },
24170+};
24171+
24172+static imx_pcm_dma_params_t imx_ssi1_pcm_stereo_in = {
24173+ .name = "SSI1 PCM Stereo in",
24174+ .params = {
24175+ .bd_number = 1,
24176+ .transfer_type = per_2_emi,
24177+ .watermark_level = SDMA_RXFIFO_WATERMARK,
24178+ .word_size = TRANSFER_16BIT, // maybe add this in setup func
24179+ .per_address = SSI1_SRX0,
24180+ .event_id = DMA_REQ_SSI1_RX1,
24181+ .peripheral_type = SSI,
24182+ },
24183+};
24184+
24185+static imx_pcm_dma_params_t imx_ssi2_pcm_stereo_out = {
24186+ .name = "SSI2 PCM Stereo out",
24187+ .params = {
24188+ .bd_number = 1,
24189+ .transfer_type = per_2_emi,
24190+ .watermark_level = SDMA_TXFIFO_WATERMARK,
24191+ .word_size = TRANSFER_16BIT, // maybe add this in setup func
24192+ .per_address = SSI2_STX0,
24193+ .event_id = DMA_REQ_SSI2_TX1,
24194+ .peripheral_type = SSI,
24195+ },
24196+};
24197+
24198+static imx_pcm_dma_params_t imx_ssi2_pcm_stereo_in = {
24199+ .name = "SSI2 PCM Stereo in",
24200+ .params = {
24201+ .bd_number = 1,
24202+ .transfer_type = per_2_emi,
24203+ .watermark_level = SDMA_RXFIFO_WATERMARK,
24204+ .word_size = TRANSFER_16BIT, // maybe add this in setup func
24205+ .per_address = SSI2_SRX0,
24206+ .event_id = DMA_REQ_SSI2_RX1,
24207+ .peripheral_type = SSI,
24208+ },
24209+};
24210+
24211+static int imx_dsp_startup(struct snd_pcm_substream *substream)
24212+{
24213+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
24214+
24215+ if (!rtd->cpu_dai->active) {
24216+
24217+ }
24218+
24219+ return 0;
24220+}
24221+
24222+static int imx_ssi1_hw_tx_params(struct snd_pcm_substream *substream,
24223+ struct snd_pcm_hw_params *params)
24224+{
24225+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
24226+ struct snd_soc_device *socdev = rtd->socdev;
24227+ struct snd_soc_codec *codec = socdev->codec;
24228+ u16 bfs, div;
24229+
24230+ bfs = SND_SOC_FSBD_REAL(rtd->cpu_dai->dai_runtime.bfs);
24231+
24232+ SSI1_STCR = 0;
24233+ SSI1_STCCR = 0;
24234+
24235+ /* DAI mode */
24236+ switch(rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
24237+ case SND_SOC_DAIFMT_DSP_B:
24238+ SSI1_STCR |= SSI_STCR_TEFS; // data 1 bit after sync
24239+ case SND_SOC_DAIFMT_DSP_A:
24240+ SSI1_STCR |= SSI_STCR_TFSL; // frame is 1 bclk long
24241+ break;
24242+ }
24243+
24244+ /* DAI clock inversion */
24245+ switch(rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_INV_MASK) {
24246+ case SND_SOC_DAIFMT_IB_IF:
24247+ SSI1_STCR |= SSI_STCR_TFSI | SSI_STCR_TSCKP;
24248+ break;
24249+ case SND_SOC_DAIFMT_IB_NF:
24250+ SSI1_STCR |= SSI_STCR_TSCKP;
24251+ break;
24252+ case SND_SOC_DAIFMT_NB_IF:
24253+ SSI1_STCR |= SSI_STCR_TFSI;
24254+ break;
24255+ }
24256+
24257+ /* DAI data (word) size */
24258+ switch(rtd->codec_dai->dai_runtime.pcmfmt) {
24259+ case SNDRV_PCM_FMTBIT_S16_LE:
24260+ SSI1_STCCR |= SSI_STCCR_WL(16);
24261+ break;
24262+ case SNDRV_PCM_FMTBIT_S20_3LE:
24263+ SSI1_STCCR |= SSI_STCCR_WL(20);
24264+ break;
24265+ case SNDRV_PCM_FMTBIT_S24_LE:
24266+ SSI1_STCCR |= SSI_STCCR_WL(24);
24267+ break;
24268+ }
24269+
24270+ /* DAI clock master masks */
24271+ switch(rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CLOCK_MASK){
24272+ case SND_SOC_DAIFMT_CBM_CFM:
24273+ SSI1_STCR |= SSI_STCR_TFDIR | SSI_STCR_TXDIR;
24274+ break;
24275+ case SND_SOC_DAIFMT_CBS_CFM:
24276+ SSI1_STCR |= SSI_STCR_TFDIR;
24277+ break;
24278+ case SND_SOC_DAIFMT_CBM_CFS:
24279+ SSI1_STCR |= SSI_STCR_TXDIR;
24280+ break;
24281+ }
24282+
24283+ /* DAI BCLK ratio to SYSCLK / MCLK */
24284+ /* prescaler modulus - todo */
24285+ switch (bfs) {
24286+ case 2:
24287+ break;
24288+ case 4:
24289+ break;
24290+ case 8:
24291+ break;
24292+ case 16:
24293+ break;
24294+ }
24295+
24296+ /* TDM - todo, only fifo 0 atm */
24297+ SSI1_STCR |= SSI_STCR_TFEN0;
24298+ SSI1_STCCR |= SSI_STCCR_DC(params_channels(params));
24299+
24300+ return 0;
24301+}
24302+
24303+static int imx_ssi1_hw_rx_params(struct snd_pcm_substream *substream,
24304+ struct snd_pcm_hw_params *params)
24305+{
24306+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
24307+ struct snd_soc_device *socdev = rtd->socdev;
24308+ struct snd_soc_codec *codec = socdev->codec;
24309+ u16 bfs, div;
24310+
24311+ bfs = SND_SOC_FSBD_REAL(rtd->cpu_dai->dai_runtime.bfs);
24312+
24313+ SSI1_SRCR = 0;
24314+ SSI1_SRCCR = 0;
24315+
24316+ /* DAI mode */
24317+ switch(rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
24318+ case SND_SOC_DAIFMT_DSP_B:
24319+ SSI1_SRCR |= SSI_SRCR_REFS; // data 1 bit after sync
24320+ case SND_SOC_DAIFMT_DSP_A:
24321+ SSI1_SRCR |= SSI_SRCR_RFSL; // frame is 1 bclk long
24322+ break;
24323+ }
24324+
24325+ /* DAI clock inversion */
24326+ switch(rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_INV_MASK) {
24327+ case SND_SOC_DAIFMT_IB_IF:
24328+ SSI1_SRCR |= SSI_SRCR_TFSI | SSI_SRCR_TSCKP;
24329+ break;
24330+ case SND_SOC_DAIFMT_IB_NF:
24331+ SSI1_SRCR |= SSI_SRCR_RSCKP;
24332+ break;
24333+ case SND_SOC_DAIFMT_NB_IF:
24334+ SSI1_SRCR |= SSI_SRCR_RFSI;
24335+ break;
24336+ }
24337+
24338+ /* DAI data (word) size */
24339+ switch(rtd->codec_dai->dai_runtime.pcmfmt) {
24340+ case SNDRV_PCM_FMTBIT_S16_LE:
24341+ SSI1_SRCCR |= SSI_SRCCR_WL(16);
24342+ break;
24343+ case SNDRV_PCM_FMTBIT_S20_3LE:
24344+ SSI1_SRCCR |= SSI_SRCCR_WL(20);
24345+ break;
24346+ case SNDRV_PCM_FMTBIT_S24_LE:
24347+ SSI1_SRCCR |= SSI_SRCCR_WL(24);
24348+ break;
24349+ }
24350+
24351+ /* DAI clock master masks */
24352+ switch(rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CLOCK_MASK){
24353+ case SND_SOC_DAIFMT_CBM_CFM:
24354+ SSI1_SRCR |= SSI_SRCR_RFDIR | SSI_SRCR_RXDIR;
24355+ break;
24356+ case SND_SOC_DAIFMT_CBS_CFM:
24357+ SSI1_SRCR |= SSI_SRCR_RFDIR;
24358+ break;
24359+ case SND_SOC_DAIFMT_CBM_CFS:
24360+ SSI1_SRCR |= SSI_SRCR_RXDIR;
24361+ break;
24362+ }
24363+
24364+ /* DAI BCLK ratio to SYSCLK / MCLK */
24365+ /* prescaler modulus - todo */
24366+ switch (bfs) {
24367+ case 2:
24368+ break;
24369+ case 4:
24370+ break;
24371+ case 8:
24372+ break;
24373+ case 16:
24374+ break;
24375+ }
24376+
24377+ /* TDM - todo, only fifo 0 atm */
24378+ SSI1_SRCR |= SSI_SRCR_RFEN0;
24379+ SSI1_SRCCR |= SSI_SRCCR_DC(params_channels(params));
24380+
24381+ return 0;
24382+}
24383+
24384+static int imx_ssi_dsp_hw_params(struct snd_pcm_substream *substream,
24385+ struct snd_pcm_hw_params *params)
24386+{
24387+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
24388+
24389+ /* clear register if not enabled */
24390+ if(!(SSI1_SCR & SSI_SCR_SSIEN))
24391+ SSI1_SCR = 0;
24392+
24393+ /* async */
24394+ if (rtd->cpu_dai->flags & SND_SOC_DAI_ASYNC)
24395+ SSI1_SCR |= SSI_SCR_SYN;
24396+
24397+ /* DAI mode */
24398+ switch(rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
24399+ case SND_SOC_DAIFMT_I2S:
24400+ case SND_SOC_DAIFMT_LEFT_J:
24401+ SSI1_SCR |= SSI_SCR_NET;
24402+ break;
24403+ }
24404+
24405+ /* TDM - to complete */
24406+
24407+ /* Tx/Rx config */
24408+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
24409+ return imx_ssi1_dsp_hw_tx_params(substream, params);
24410+ } else {
24411+ return imx_ssi1_dsp_hw_rx_params(substream, params);
24412+ }
24413+}
24414+
24415+
24416+
24417+static int imx_ssi_dsp_trigger(struct snd_pcm_substream *substream, int cmd)
24418+{
24419+ int ret = 0;
24420+
24421+ switch (cmd) {
24422+ case SNDRV_PCM_TRIGGER_START:
24423+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
24424+ SSI1_SCR |= SSI_SCR_TE;
24425+ SSI1_SIER |= SSI_SIER_TDMAE;
24426+ } else {
24427+ SSI1_SCR |= SSI_SCR_RE;
24428+ SSI1_SIER |= SSI_SIER_RDMAE;
24429+ }
24430+ SSI1_SCR |= SSI_SCR_SSIEN;
24431+
24432+ break;
24433+ case SNDRV_PCM_TRIGGER_RESUME:
24434+ break;
24435+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
24436+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
24437+ SSI1_SCR |= SSI_SCR_TE;
24438+ else
24439+ SSI1_SCR |= SSI_SCR_RE;
24440+ break
24441+ case SNDRV_PCM_TRIGGER_STOP:
24442+ case SNDRV_PCM_TRIGGER_SUSPEND:
24443+ break;
24444+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
24445+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
24446+ SSI1_SCR &= ~SSI_SCR_TE;
24447+ else
24448+ SSI1_SCR &= ~SSI_SCR_RE;
24449+ break;
24450+ default:
24451+ ret = -EINVAL;
24452+ }
24453+
24454+ return ret;
24455+}
24456+
24457+static void imx_ssi_dsp_shutdown(struct snd_pcm_substream *substream)
24458+{
24459+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
24460+
24461+ /* shutdown SSI */
24462+ if (!rtd->cpu_dai->active) {
24463+ if(rtd->cpu_dai->id == 0)
24464+ SSI1_SCR &= ~SSI_SCR_SSIEN;
24465+ else
24466+ SSI2_SCR &= ~SSI_SCR_SSIEN;
24467+ }
24468+}
24469+
24470+#ifdef CONFIG_PM
24471+static int imx_ssi_dsp_suspend(struct platform_device *dev,
24472+ struct snd_soc_cpu_dai *dai)
24473+{
24474+ if(!dai->active)
24475+ return 0;
24476+
24477+ if(rtd->cpu_dai->id == 0)
24478+ SSI1_SCR &= ~SSI_SCR_SSIEN;
24479+ else
24480+ SSI2_SCR &= ~SSI_SCR_SSIEN;
24481+
24482+ return 0;
24483+}
24484+
24485+static int imx_ssi_dsp_resume(struct platform_device *pdev,
24486+ struct snd_soc_cpu_dai *dai)
24487+{
24488+ if(!dai->active)
24489+ return 0;
24490+
24491+ if(rtd->cpu_dai->id == 0)
24492+ SSI1_SCR |= SSI_SCR_SSIEN;
24493+ else
24494+ SSI2_SCR |= SSI_SCR_SSIEN;
24495+
24496+ return 0;
24497+}
24498+
24499+#else
24500+#define imx_ssi_dsp_suspend NULL
24501+#define imx_ssi_dsp_resume NULL
24502+#endif
24503+
24504+static unsigned int imx_ssi_config_dsp_sysclk(struct snd_soc_cpu_dai *iface,
24505+ struct snd_soc_clock_info *info, unsigned int clk)
24506+{
24507+ return clk;
24508+}
24509+
24510+struct snd_soc_cpu_dai imx_ssi_dsp_dai = {
24511+ .name = "imx-dsp-1",
24512+ .id = 0,
24513+ .type = SND_SOC_DAI_PCM,
24514+ .suspend = imx_ssi_dsp_suspend,
24515+ .resume = imx_ssi_dsp_resume,
24516+ .config_sysclk = imx_ssi_config_dsp_sysclk,
24517+ .playback = {
24518+ .channels_min = 1,
24519+ .channels_max = 2,},
24520+ .capture = {
24521+ .channels_min = 1,
24522+ .channels_max = 2,},
24523+ .ops = {
24524+ .startup = imx_ssi_dsp_startup,
24525+ .shutdown = imx_ssi_dsp_shutdown,
24526+ .trigger = imx_ssi_trigger,
24527+ .hw_params = imx_ssi_dsp_hw_params,},
24528+ .caps = {
24529+ .num_modes = ARRAY_SIZE(imx_dsp_modes),
24530+ .mode = imx_dsp_modes,},
24531+},
24532+{
24533+ .name = "imx-dsp-2",
24534+ .id = 1,
24535+ .type = SND_SOC_DAI_PCM,
24536+ .suspend = imx_ssi_dsp_suspend,
24537+ .resume = imx_ssi_dsp_resume,
24538+ .config_sysclk = imx_ssi_config_dsp_sysclk,
24539+ .playback = {
24540+ .channels_min = 1,
24541+ .channels_max = 2,},
24542+ .capture = {
24543+ .channels_min = 1,
24544+ .channels_max = 2,},
24545+ .ops = {
24546+ .startup = imx_dsp_startup,
24547+ .shutdown = imx_dsp_shutdown,
24548+ .trigger = imx_ssi1_trigger,
24549+ .hw_params = imx_ssi1_pcm_hw_params,},
24550+ .caps = {
24551+ .num_modes = ARRAY_SIZE(imx_dsp_modes),
24552+ .mode = imx_dsp_modes,},
24553+};
24554+
24555+
24556+EXPORT_SYMBOL_GPL(imx_ssi_dsp_dai);
24557+
24558+/* Module information */
24559+MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
24560+MODULE_DESCRIPTION("i.MX ASoC SSI driver");
24561+MODULE_LICENSE("GPL");
24562Index: linux-2.6-pxa-new/sound/soc/imx/Kconfig
24563===================================================================
24564--- /dev/null
24565+++ linux-2.6-pxa-new/sound/soc/imx/Kconfig
24566@@ -0,0 +1,31 @@
24567+menu "SoC Audio for the Freescale i.MX"
24568+
24569+config SND_MXC_SOC
24570+ tristate "SoC Audio for the Freescale i.MX CPU"
24571+ depends on ARCH_MXC && SND
24572+ select SND_PCM
24573+ help
24574+ Say Y or M if you want to add support for codecs attached to
24575+ the MXC AC97, I2S or SSP interface. You will also need
24576+ to select the audio interfaces to support below.
24577+
24578+config SND_MXC_AC97
24579+ tristate
24580+ select SND_AC97_CODEC
24581+
24582+config SND_MXC_SOC_AC97
24583+ tristate
24584+ select SND_AC97_BUS
24585+
24586+config SND_MXC_SOC_SSI
24587+ tristate
24588+
24589+config SND_MXC_SOC_MX3_WM8753
24590+ tristate "SoC Audio support for MX31 - WM8753"
24591+ depends on SND_MXC_SOC && ARCH_MX3
24592+ select SND_MXC_SOC_SSI
24593+ help
24594+ Say Y if you want to add support for SoC audio on MX31ADS
24595+ with the WM8753.
24596+
24597+endmenu
24598Index: linux-2.6-pxa-new/sound/soc/imx/Makefile
24599===================================================================
24600--- /dev/null
24601+++ linux-2.6-pxa-new/sound/soc/imx/Makefile
24602@@ -0,0 +1,18 @@
24603+# i.MX Platform Support
24604+snd-soc-imx21-objs := imx21-pcm.o
24605+snd-soc-imx31-objs := imx31-pcm.o
24606+snd-soc-imx-ac97-objs := imx-ac97.o
24607+snd-soc-imx-i2s-objs := imx-i2s.o
24608+
24609+obj-$(CONFIG_SND_MXC_SOC) += snd-soc-imx.o
24610+obj-$(CONFIG_SND_MXC_SOC_AC97) += snd-soc-imx-ac97.o
24611+obj-$(CONFIG_SND_MXC_SOC_I2S) += snd-soc-imx-i2s.o
24612+
24613+# i.MX Machine Support
24614+snd-soc-mx31ads-wm8753-objs := mx31ads_wm8753.o
24615+obj-$(CONFIG_SND_SOC_MX31ADS_WM8753) += snd-soc-mx31ads-wm8753.o
24616+snd-soc-mx21ads-wm8753-objs := mx21ads_wm8753.o
24617+obj-$(CONFIG_SND_SOC_MX21ADS_WM8753) += snd-soc-mx21ads-wm8753.o
24618+snd-soc-mx21ads-wm8731-objs := mx21ads_wm8731.o
24619+obj-$(CONFIG_SND_SOC_MX21ADS_WM8731) += snd-soc-mx21ads-wm8731.o
24620+
24621Index: linux-2.6.17/sound/Makefile
24622===================================================================
24623--- linux-2.6.17.orig/sound/Makefile 2006-06-18 02:49:35.000000000 +0100
24624+++ linux-2.6.17/sound/Makefile 2006-07-04 14:04:41.000000000 +0100
24625@@ -4,7 +4,7 @@
24626 obj-$(CONFIG_SOUND) += soundcore.o
24627 obj-$(CONFIG_SOUND_PRIME) += oss/
24628 obj-$(CONFIG_DMASOUND) += oss/
24629-obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ synth/ usb/ sparc/ parisc/ pcmcia/ mips/
24630+obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ synth/ usb/ sparc/ parisc/ pcmcia/ mips/ soc/
24631
24632 ifeq ($(CONFIG_SND),y)
24633 obj-y += last.o
24634Index: linux-2.6-pxa-new/sound/soc/codecs/wm8711.c
24635===================================================================
24636--- /dev/null
24637+++ linux-2.6-pxa-new/sound/soc/codecs/wm8711.c
24638@@ -0,0 +1,843 @@
24639+/*
24640+ * wm8711.c -- WM8711 ALSA SoC Audio driver
24641+ *
24642+ * Copyright 2006 Wolfson Microelectronics
24643+ *
24644+ * Author: Mike Arthur <linux@wolfsonmicro.com>
24645+ *
24646+ * Based on wm8711.c by Richard Purdie
24647+ *
24648+ * This program is free software; you can redistribute it and/or modify
24649+ * it under the terms of the GNU General Public License version 2 as
24650+ * published by the Free Software Foundation.
24651+ */
24652+
24653+#include <linux/module.h>
24654+#include <linux/moduleparam.h>
24655+#include <linux/init.h>
24656+#include <linux/delay.h>
24657+#include <linux/pm.h>
24658+#include <linux/i2c.h>
24659+#include <linux/platform_device.h>
24660+#include <sound/driver.h>
24661+#include <sound/core.h>
24662+#include <sound/pcm.h>
24663+#include <sound/pcm_params.h>
24664+#include <sound/soc.h>
24665+#include <sound/soc-dapm.h>
24666+#include <sound/initval.h>
24667+
24668+#include "wm8711.h"
24669+
24670+#define AUDIO_NAME "wm8711"
24671+#define WM8711_VERSION "0.2"
24672+
24673+/*
24674+ * Debug
24675+ */
24676+
24677+#define WM8711_DEBUG 0
24678+
24679+#ifdef WM8711_DEBUG
24680+#define dbg(format, arg...) \
24681+ printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
24682+#else
24683+#define dbg(format, arg...) do {} while (0)
24684+#endif
24685+#define err(format, arg...) \
24686+ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
24687+#define info(format, arg...) \
24688+ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
24689+#define warn(format, arg...) \
24690+ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
24691+
24692+struct snd_soc_codec_device soc_codec_dev_wm8711;
24693+
24694+/*
24695+ * wm8711 register cache
24696+ * We can't read the WM8711 register space when we are
24697+ * using 2 wire for device control, so we cache them instead.
24698+ * There is no point in caching the reset register
24699+ */
24700+static const u16 wm8711_reg[WM8711_CACHEREGNUM] = {
24701+ 0x0079, 0x0079, 0x000a, 0x0008,
24702+ 0x009f, 0x000a, 0x0000, 0x0000
24703+};
24704+
24705+#define WM8711_DAIFMT \
24706+ (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_RIGHT_J | \
24707+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_NB_IF | SND_SOC_DAIFMT_IB_NF | \
24708+ SND_SOC_DAIFMT_IB_IF)
24709+
24710+#define WM8711_DIR \
24711+ (SND_SOC_DAIDIR_PLAYBACK)
24712+
24713+#define WM8711_RATES \
24714+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
24715+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
24716+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
24717+
24718+#define WM8711_HIFI_BITS \
24719+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
24720+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE | \
24721+ SNDRV_PCM_FMTBIT_S32_LE)
24722+
24723+static struct snd_soc_dai_mode wm8711_modes[] = {
24724+ /* codec frame and clock master modes */
24725+ /* 8k */
24726+ {
24727+ .fmt = WM8711_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
24728+ .pcmfmt = WM8711_HIFI_BITS,
24729+ .pcmrate = SNDRV_PCM_RATE_8000,
24730+ .pcmdir = WM8711_DIR,
24731+ .flags = SND_SOC_DAI_BFS_RATE,
24732+ .fs = 1536,
24733+ .bfs = 64,
24734+ },
24735+ {
24736+ .fmt = WM8711_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
24737+ .pcmfmt = WM8711_HIFI_BITS,
24738+ .pcmrate = SNDRV_PCM_RATE_8000,
24739+ .pcmdir = WM8711_DIR,
24740+ .flags = SND_SOC_DAI_BFS_RATE,
24741+ .fs = 2304,
24742+ .bfs = 64,
24743+ },
24744+ {
24745+ .fmt = WM8711_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
24746+ .pcmfmt = WM8711_HIFI_BITS,
24747+ .pcmrate = SNDRV_PCM_RATE_8000,
24748+ .pcmdir = WM8711_DIR,
24749+ .flags = SND_SOC_DAI_BFS_RATE,
24750+ .fs = 1408,
24751+ .bfs = 64,
24752+ },
24753+ {
24754+ .fmt = WM8711_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
24755+ .pcmfmt = WM8711_HIFI_BITS,
24756+ .pcmrate = SNDRV_PCM_RATE_8000,
24757+ .pcmdir = WM8711_DIR,
24758+ .flags = SND_SOC_DAI_BFS_RATE,
24759+ .fs = 2112,
24760+ .bfs = 64,
24761+ },
24762+
24763+ /* 32k */
24764+ {
24765+ .fmt = WM8711_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
24766+ .pcmfmt = WM8711_HIFI_BITS,
24767+ .pcmrate = SNDRV_PCM_RATE_32000,
24768+ .pcmdir = WM8711_DIR,
24769+ .flags = SND_SOC_DAI_BFS_RATE,
24770+ .fs = 384,
24771+ .bfs = 64,
24772+ },
24773+ {
24774+ .fmt = WM8711_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
24775+ .pcmfmt = WM8711_HIFI_BITS,
24776+ .pcmrate = SNDRV_PCM_RATE_32000,
24777+ .pcmdir = WM8711_DIR,
24778+ .flags = SND_SOC_DAI_BFS_RATE,
24779+ .fs = 576,
24780+ .bfs = 64,
24781+ },
24782+
24783+ /* 44.1k & 48k */
24784+ {
24785+ .fmt = WM8711_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
24786+ .pcmfmt = WM8711_HIFI_BITS,
24787+ .pcmrate = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
24788+ .pcmdir = WM8711_DIR,
24789+ .flags = SND_SOC_DAI_BFS_RATE,
24790+ .fs = 256,
24791+ .bfs = 64,
24792+ },
24793+ {
24794+ .fmt = WM8711_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
24795+ .pcmfmt = WM8711_HIFI_BITS,
24796+ .pcmrate = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
24797+ .pcmdir = WM8711_DIR,
24798+ .flags = SND_SOC_DAI_BFS_RATE,
24799+ .fs = 384,
24800+ .bfs = 64,
24801+ },
24802+
24803+ /* 88.2 & 96k */
24804+ {
24805+ .fmt = WM8711_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
24806+ .pcmfmt = WM8711_HIFI_BITS,
24807+ .pcmrate = SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000,
24808+ .pcmdir = WM8711_DIR,
24809+ .flags = SND_SOC_DAI_BFS_RATE,
24810+ .fs = 128,
24811+ .bfs = 64,
24812+
24813+ },
24814+ {
24815+ .fmt = WM8711_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
24816+ .pcmfmt = WM8711_HIFI_BITS,
24817+ .pcmrate = SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000,
24818+ .pcmdir = WM8711_DIR,
24819+ .flags = SND_SOC_DAI_BFS_RATE,
24820+ .fs = 192,
24821+ .bfs = 64,
24822+ },
24823+
24824+ /* USB codec frame and clock master modes */
24825+ /* 8k */
24826+ {
24827+ .fmt = WM8711_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
24828+ .pcmfmt = WM8711_HIFI_BITS,
24829+ .pcmrate = SNDRV_PCM_RATE_8000,
24830+ .pcmdir = WM8711_DIR,
24831+ .flags = SND_SOC_DAI_BFS_DIV,
24832+ .fs = 1500,
24833+ .bfs = SND_SOC_FSBD(1),
24834+ },
24835+
24836+ /* 44.1k */
24837+ {
24838+ .fmt = WM8711_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
24839+ .pcmfmt = WM8711_HIFI_BITS,
24840+ .pcmrate = SNDRV_PCM_RATE_44100,
24841+ .pcmdir = WM8711_DIR,
24842+ .flags = SND_SOC_DAI_BFS_DIV,
24843+ .fs = 272,
24844+ .bfs = SND_SOC_FSBD(1),
24845+ },
24846+
24847+ /* 48k */
24848+ {
24849+ .fmt = WM8711_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
24850+ .pcmfmt = WM8711_HIFI_BITS,
24851+ .pcmrate = SNDRV_PCM_RATE_48000,
24852+ .pcmdir = WM8711_DIR,
24853+ .flags = SND_SOC_DAI_BFS_DIV,
24854+ .fs = 250,
24855+ .bfs = SND_SOC_FSBD(1),
24856+ },
24857+
24858+ /* 88.2k */
24859+ {
24860+ .fmt = WM8711_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
24861+ .pcmfmt = WM8711_HIFI_BITS,
24862+ .pcmrate = SNDRV_PCM_RATE_88200,
24863+ .pcmdir = WM8711_DIR,
24864+ .flags = SND_SOC_DAI_BFS_DIV,
24865+ .fs = 136,
24866+ .bfs = SND_SOC_FSBD(1),
24867+ },
24868+
24869+ /* 96k */
24870+ {
24871+ .fmt = WM8711_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
24872+ .pcmfmt = WM8711_HIFI_BITS,
24873+ .pcmrate = SNDRV_PCM_RATE_96000,
24874+ .pcmdir = WM8711_DIR,
24875+ .flags = SND_SOC_DAI_BFS_DIV,
24876+ .fs = 125,
24877+ .bfs = SND_SOC_FSBD(1),
24878+ },
24879+
24880+ /* codec frame and clock slave modes */
24881+ {
24882+ .fmt = WM8711_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
24883+ .pcmfmt = WM8711_HIFI_BITS,
24884+ .pcmrate = WM8711_RATES,
24885+ .pcmdir = WM8711_DIR,
24886+ .fs = SND_SOC_FS_ALL,
24887+ .bfs = SND_SOC_FSB_ALL,
24888+ },
24889+};
24890+
24891+/*
24892+ * read wm8711 register cache
24893+ */
24894+static inline unsigned int wm8711_read_reg_cache(struct snd_soc_codec * codec,
24895+ unsigned int reg)
24896+{
24897+ u16 *cache = codec->reg_cache;
24898+ if (reg == WM8711_RESET)
24899+ return 0;
24900+ if (reg >= WM8711_CACHEREGNUM)
24901+ return -1;
24902+ return cache[reg];
24903+}
24904+
24905+/*
24906+ * write wm8711 register cache
24907+ */
24908+static inline void wm8711_write_reg_cache(struct snd_soc_codec *codec,
24909+ u16 reg, unsigned int value)
24910+{
24911+ u16 *cache = codec->reg_cache;
24912+ if (reg >= WM8711_CACHEREGNUM)
24913+ return;
24914+ cache[reg] = value;
24915+}
24916+
24917+/*
24918+ * write to the WM8711 register space
24919+ */
24920+static int wm8711_write(struct snd_soc_codec * codec, unsigned int reg,
24921+ unsigned int value)
24922+{
24923+ u8 data[2];
24924+
24925+ /* data is
24926+ * D15..D9 WM8753 register offset
24927+ * D8...D0 register data
24928+ */
24929+ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
24930+ data[1] = value & 0x00ff;
24931+
24932+ wm8711_write_reg_cache (codec, reg, value);
24933+ if (codec->hw_write(codec->control_data, data, 2) == 2)
24934+ return 0;
24935+ else
24936+ return -EIO;
24937+}
24938+
24939+#define wm8711_reset(c) wm8711_write(c, WM8711_RESET, 0)
24940+
24941+static const struct snd_kcontrol_new wm8711_snd_controls[] = {
24942+
24943+SOC_DOUBLE_R("Master Playback Volume", WM8711_LOUT1V, WM8711_ROUT1V,
24944+ 0, 127, 0),
24945+SOC_DOUBLE_R("Master Playback ZC Switch", WM8711_LOUT1V, WM8711_ROUT1V,
24946+ 7, 1, 0),
24947+
24948+};
24949+
24950+/* add non dapm controls */
24951+static int wm8711_add_controls(struct snd_soc_codec *codec)
24952+{
24953+ int err, i;
24954+
24955+ for (i = 0; i < ARRAY_SIZE(wm8711_snd_controls); i++) {
24956+ err = snd_ctl_add(codec->card,
24957+ snd_soc_cnew(&wm8711_snd_controls[i],codec, NULL));
24958+ if (err < 0)
24959+ return err;
24960+ }
24961+
24962+ return 0;
24963+}
24964+
24965+/* Output Mixer */
24966+static const snd_kcontrol_new_t wm8711_output_mixer_controls[] = {
24967+SOC_DAPM_SINGLE("Line Bypass Switch", WM8711_APANA, 3, 1, 0),
24968+SOC_DAPM_SINGLE("HiFi Playback Switch", WM8711_APANA, 4, 1, 0),
24969+};
24970+
24971+static const struct snd_soc_dapm_widget wm8711_dapm_widgets[] = {
24972+SND_SOC_DAPM_MIXER("Output Mixer", WM8711_PWR, 4, 1,
24973+ &wm8711_output_mixer_controls[0],
24974+ ARRAY_SIZE(wm8711_output_mixer_controls)),
24975+SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8711_PWR, 3, 1),
24976+SND_SOC_DAPM_OUTPUT("LOUT"),
24977+SND_SOC_DAPM_OUTPUT("LHPOUT"),
24978+SND_SOC_DAPM_OUTPUT("ROUT"),
24979+SND_SOC_DAPM_OUTPUT("RHPOUT"),
24980+};
24981+
24982+static const char *intercon[][3] = {
24983+ /* output mixer */
24984+ {"Output Mixer", "Line Bypass Switch", "Line Input"},
24985+ {"Output Mixer", "HiFi Playback Switch", "DAC"},
24986+
24987+ /* outputs */
24988+ {"RHPOUT", NULL, "Output Mixer"},
24989+ {"ROUT", NULL, "Output Mixer"},
24990+ {"LHPOUT", NULL, "Output Mixer"},
24991+ {"LOUT", NULL, "Output Mixer"},
24992+
24993+ /* terminator */
24994+ {NULL, NULL, NULL},
24995+};
24996+
24997+static int wm8711_add_widgets(struct snd_soc_codec *codec)
24998+{
24999+ int i;
25000+
25001+ for(i = 0; i < ARRAY_SIZE(wm8711_dapm_widgets); i++) {
25002+ snd_soc_dapm_new_control(codec, &wm8711_dapm_widgets[i]);
25003+ }
25004+
25005+ /* set up audio path interconnects */
25006+ for(i = 0; intercon[i][0] != NULL; i++) {
25007+ snd_soc_dapm_connect_input(codec, intercon[i][0], intercon[i][1],
25008+ intercon[i][2]);
25009+ }
25010+
25011+ snd_soc_dapm_new_widgets(codec);
25012+ return 0;
25013+}
25014+
25015+struct _coeff_div {
25016+ u32 mclk;
25017+ u32 rate;
25018+ u16 fs;
25019+ u8 sr:4;
25020+ u8 bosr:1;
25021+ u8 usb:1;
25022+};
25023+
25024+/* codec mclk clock divider coefficients */
25025+static const struct _coeff_div coeff_div[] = {
25026+ /* 48k */
25027+ {12288000, 48000, 256, 0x0, 0x0, 0x0},
25028+ {18432000, 48000, 384, 0x0, 0x1, 0x0},
25029+ {12000000, 48000, 250, 0x0, 0x0, 0x1},
25030+
25031+ /* 32k */
25032+ {12288000, 32000, 384, 0x6, 0x0, 0x0},
25033+ {18432000, 32000, 576, 0x6, 0x1, 0x0},
25034+
25035+ /* 8k */
25036+ {12288000, 8000, 1536, 0x3, 0x0, 0x0},
25037+ {18432000, 8000, 2304, 0x3, 0x1, 0x0},
25038+ {11289600, 8000, 1408, 0xb, 0x0, 0x0},
25039+ {16934400, 8000, 2112, 0xb, 0x1, 0x0},
25040+ {12000000, 8000, 1500, 0x3, 0x0, 0x1},
25041+
25042+ /* 96k */
25043+ {12288000, 96000, 128, 0x7, 0x0, 0x0},
25044+ {18432000, 96000, 192, 0x7, 0x1, 0x0},
25045+ {12000000, 96000, 125, 0x7, 0x0, 0x1},
25046+
25047+ /* 44.1k */
25048+ {11289600, 44100, 256, 0x8, 0x0, 0x0},
25049+ {16934400, 44100, 384, 0x8, 0x1, 0x0},
25050+ {12000000, 44100, 272, 0x8, 0x1, 0x1},
25051+
25052+ /* 88.2k */
25053+ {11289600, 88200, 128, 0xf, 0x0, 0x0},
25054+ {16934400, 88200, 192, 0xf, 0x1, 0x0},
25055+ {12000000, 88200, 136, 0xf, 0x1, 0x1},
25056+};
25057+
25058+static inline int get_coeff(int mclk, int rate)
25059+{
25060+ int i;
25061+
25062+ for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
25063+ if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
25064+ return i;
25065+ }
25066+ return 0;
25067+}
25068+
25069+/* WM8711 supports numerous clocks per sample rate */
25070+static unsigned int wm8711_config_sysclk(struct snd_soc_codec_dai *dai,
25071+ struct snd_soc_clock_info *info, unsigned int clk)
25072+{
25073+ dai->mclk = 0;
25074+
25075+ /* check that the calculated FS and rate actually match a clock from
25076+ * the machine driver */
25077+ if (info->fs * info->rate == clk)
25078+ dai->mclk = clk;
25079+
25080+ return dai->mclk;
25081+}
25082+
25083+static int wm8711_pcm_prepare(struct snd_pcm_substream *substream)
25084+{
25085+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
25086+ struct snd_soc_device *socdev = rtd->socdev;
25087+ struct snd_soc_codec *codec = socdev->codec;
25088+ u16 iface = 0, srate;
25089+ int i = get_coeff(rtd->codec_dai->mclk,
25090+ snd_soc_get_rate(rtd->codec_dai->dai_runtime.pcmrate));
25091+
25092+ /* set master/slave audio interface */
25093+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
25094+ case SND_SOC_DAIFMT_CBM_CFM:
25095+ iface |= 0x0040;
25096+ break;
25097+ case SND_SOC_DAIFMT_CBS_CFS:
25098+ break;
25099+ }
25100+ srate = (coeff_div[i].sr << 2) | (coeff_div[i].bosr << 1) |
25101+ coeff_div[i].usb;
25102+ wm8711_write(codec, WM8711_SRATE, srate);
25103+
25104+ /* interface format */
25105+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
25106+ case SND_SOC_DAIFMT_I2S:
25107+ iface |= 0x0002;
25108+ break;
25109+ case SND_SOC_DAIFMT_RIGHT_J:
25110+ break;
25111+ case SND_SOC_DAIFMT_LEFT_J:
25112+ iface |= 0x0001;
25113+ break;
25114+ case SND_SOC_DAIFMT_DSP_A:
25115+ iface |= 0x0003;
25116+ break;
25117+ case SND_SOC_DAIFMT_DSP_B:
25118+ iface |= 0x0013;
25119+ break;
25120+ }
25121+
25122+ /* bit size */
25123+ switch (rtd->codec_dai->dai_runtime.pcmfmt) {
25124+ case SNDRV_PCM_FORMAT_S16_LE:
25125+ break;
25126+ case SNDRV_PCM_FORMAT_S20_3LE:
25127+ iface |= 0x0004;
25128+ break;
25129+ case SNDRV_PCM_FORMAT_S24_LE:
25130+ iface |= 0x0008;
25131+ break;
25132+ case SNDRV_PCM_FORMAT_S32_LE:
25133+ iface |= 0x000c;
25134+ break;
25135+ }
25136+
25137+ /* clock inversion */
25138+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_INV_MASK) {
25139+ case SND_SOC_DAIFMT_NB_NF:
25140+ break;
25141+ case SND_SOC_DAIFMT_IB_IF:
25142+ iface |= 0x0090;
25143+ break;
25144+ case SND_SOC_DAIFMT_IB_NF:
25145+ iface |= 0x0080;
25146+ break;
25147+ case SND_SOC_DAIFMT_NB_IF:
25148+ iface |= 0x0010;
25149+ break;
25150+ }
25151+
25152+ /* set iface */
25153+ wm8711_write(codec, WM8711_IFACE, iface);
25154+
25155+ /* set active */
25156+ wm8711_write(codec, WM8711_ACTIVE, 0x0001);
25157+ return 0;
25158+}
25159+
25160+static void wm8711_shutdown(struct snd_pcm_substream *substream)
25161+{
25162+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
25163+ struct snd_soc_device *socdev = rtd->socdev;
25164+ struct snd_soc_codec *codec = socdev->codec;
25165+
25166+ /* deactivate */
25167+ if (!codec->active) {
25168+ udelay(50);
25169+ wm8711_write(codec, WM8711_ACTIVE, 0x0);
25170+ }
25171+}
25172+
25173+static int wm8711_mute(struct snd_soc_codec *codec,
25174+ struct snd_soc_codec_dai *dai, int mute)
25175+{
25176+ u16 mute_reg = wm8711_read_reg_cache(codec, WM8711_APDIGI) & 0xfff7;
25177+ if (mute)
25178+ wm8711_write(codec, WM8711_APDIGI, mute_reg | 0x8);
25179+ else
25180+ wm8711_write(codec, WM8711_APDIGI, mute_reg);
25181+
25182+ return 0;
25183+}
25184+
25185+static int wm8711_dapm_event(struct snd_soc_codec *codec, int event)
25186+{
25187+ u16 reg = wm8711_read_reg_cache(codec, WM8711_PWR) & 0xff7f;
25188+
25189+ switch (event) {
25190+ case SNDRV_CTL_POWER_D0: /* full On */
25191+ /* vref/mid, osc on, dac unmute */
25192+ wm8711_write(codec, WM8711_PWR, reg);
25193+ break;
25194+ case SNDRV_CTL_POWER_D1: /* partial On */
25195+ case SNDRV_CTL_POWER_D2: /* partial On */
25196+ break;
25197+ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
25198+ /* everything off except vref/vmid, */
25199+ wm8711_write(codec, WM8711_PWR, reg | 0x0040);
25200+ break;
25201+ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
25202+ /* everything off, dac mute, inactive */
25203+ wm8711_write(codec, WM8711_ACTIVE, 0x0);
25204+ wm8711_write(codec, WM8711_PWR, 0xffff);
25205+ break;
25206+ }
25207+ codec->dapm_state = event;
25208+ return 0;
25209+}
25210+
25211+struct snd_soc_codec_dai wm8711_dai = {
25212+ .name = "WM8711",
25213+ .playback = {
25214+ .stream_name = "Playback",
25215+ .channels_min = 1,
25216+ .channels_max = 2,
25217+ },
25218+ .config_sysclk = wm8711_config_sysclk,
25219+ .digital_mute = wm8711_mute,
25220+ .ops = {
25221+ .prepare = wm8711_pcm_prepare,
25222+ .shutdown = wm8711_shutdown,
25223+ },
25224+ .caps = {
25225+ .num_modes = ARRAY_SIZE(wm8711_modes),
25226+ .mode = wm8711_modes,
25227+ },
25228+};
25229+EXPORT_SYMBOL_GPL(wm8711_dai);
25230+
25231+static int wm8711_suspend(struct platform_device *pdev, pm_message_t state)
25232+{
25233+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
25234+ struct snd_soc_codec *codec = socdev->codec;
25235+
25236+ wm8711_write(codec, WM8711_ACTIVE, 0x0);
25237+ wm8711_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
25238+ return 0;
25239+}
25240+
25241+static int wm8711_resume(struct platform_device *pdev)
25242+{
25243+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
25244+ struct snd_soc_codec *codec = socdev->codec;
25245+ int i;
25246+ u8 data[2];
25247+ u16 *cache = codec->reg_cache;
25248+
25249+ /* Sync reg_cache with the hardware */
25250+ for (i = 0; i < ARRAY_SIZE(wm8711_reg); i++) {
25251+ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
25252+ data[1] = cache[i] & 0x00ff;
25253+ codec->hw_write(codec->control_data, data, 2);
25254+ }
25255+ wm8711_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
25256+ wm8711_dapm_event(codec, codec->suspend_dapm_state);
25257+ return 0;
25258+}
25259+
25260+/*
25261+ * initialise the WM8711 driver
25262+ * register the mixer and dsp interfaces with the kernel
25263+ */
25264+static int wm8711_init(struct snd_soc_device* socdev)
25265+{
25266+ struct snd_soc_codec* codec = socdev->codec;
25267+ int reg, ret = 0;
25268+
25269+ codec->name = "WM8711";
25270+ codec->owner = THIS_MODULE;
25271+ codec->read = wm8711_read_reg_cache;
25272+ codec->write = wm8711_write;
25273+ codec->dapm_event = wm8711_dapm_event;
25274+ codec->dai = &wm8711_dai;
25275+ codec->num_dai = 1;
25276+ codec->reg_cache_size = ARRAY_SIZE(wm8711_reg);
25277+ codec->reg_cache =
25278+ kzalloc(sizeof(u16) * ARRAY_SIZE(wm8711_reg), GFP_KERNEL);
25279+ if (codec->reg_cache == NULL)
25280+ return -ENOMEM;
25281+ memcpy(codec->reg_cache, wm8711_reg,
25282+ sizeof(u16) * ARRAY_SIZE(wm8711_reg));
25283+ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm8711_reg);
25284+
25285+ wm8711_reset(codec);
25286+
25287+ /* register pcms */
25288+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
25289+ if (ret < 0) {
25290+ kfree(codec->reg_cache);
25291+ return ret;
25292+ }
25293+
25294+ /* power on device */
25295+ wm8711_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
25296+
25297+ /* set the update bits */
25298+ reg = wm8711_read_reg_cache(codec, WM8711_LOUT1V);
25299+ wm8711_write(codec, WM8711_LOUT1V, reg | 0x0100);
25300+ reg = wm8711_read_reg_cache(codec, WM8711_ROUT1V);
25301+ wm8711_write(codec, WM8711_ROUT1V, reg | 0x0100);
25302+
25303+ wm8711_add_controls(codec);
25304+ wm8711_add_widgets(codec);
25305+ ret = snd_soc_register_card(socdev);
25306+ if (ret < 0) {
25307+ snd_soc_free_pcms(socdev);
25308+ snd_soc_dapm_free(socdev);
25309+ }
25310+
25311+ return ret;
25312+}
25313+
25314+static struct snd_soc_device *wm8711_socdev;
25315+
25316+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
25317+
25318+/*
25319+ * WM8711 2 wire address is determined by GPIO5
25320+ * state during powerup.
25321+ * low = 0x1a
25322+ * high = 0x1b
25323+ */
25324+#define I2C_DRIVERID_WM8711 0xfefe /* liam - need a proper id */
25325+
25326+static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
25327+
25328+/* Magic definition of all other variables and things */
25329+I2C_CLIENT_INSMOD;
25330+
25331+static struct i2c_driver wm8711_i2c_driver;
25332+static struct i2c_client client_template;
25333+
25334+/* If the i2c layer weren't so broken, we could pass this kind of data
25335+ around */
25336+
25337+static int wm8711_codec_probe(struct i2c_adapter *adap, int addr, int kind)
25338+{
25339+ struct snd_soc_device *socdev = wm8711_socdev;
25340+ struct wm8711_setup_data *setup = socdev->codec_data;
25341+ struct snd_soc_codec *codec = socdev->codec;
25342+ struct i2c_client *i2c;
25343+ int ret;
25344+
25345+ if (addr != setup->i2c_address)
25346+ return -ENODEV;
25347+
25348+ client_template.adapter = adap;
25349+ client_template.addr = addr;
25350+
25351+ i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
25352+ if (i2c == NULL){
25353+ kfree(codec);
25354+ return -ENOMEM;
25355+ }
25356+ memcpy(i2c, &client_template, sizeof(struct i2c_client));
25357+
25358+ i2c_set_clientdata(i2c, codec);
25359+
25360+ codec->control_data = i2c;
25361+
25362+ ret = i2c_attach_client(i2c);
25363+ if (ret < 0) {
25364+ err("failed to attach codec at addr %x\n", addr);
25365+ goto err;
25366+ }
25367+
25368+ ret = wm8711_init(socdev);
25369+ if (ret < 0) {
25370+ err("failed to initialise WM8711\n");
25371+ goto err;
25372+ }
25373+ return ret;
25374+
25375+err:
25376+ kfree(codec);
25377+ kfree(i2c);
25378+ return ret;
25379+
25380+}
25381+
25382+static int wm8711_i2c_detach(struct i2c_client *client)
25383+{
25384+ struct snd_soc_codec* codec = i2c_get_clientdata(client);
25385+
25386+ i2c_detach_client(client);
25387+
25388+ kfree(codec->reg_cache);
25389+ kfree(client);
25390+
25391+ return 0;
25392+}
25393+
25394+static int wm8711_i2c_attach(struct i2c_adapter *adap)
25395+{
25396+ return i2c_probe(adap, &addr_data, wm8711_codec_probe);
25397+}
25398+
25399+/* corgi i2c codec control layer */
25400+static struct i2c_driver wm8711_i2c_driver = {
25401+ .driver = {
25402+ .name = "WM8711 I2C Codec",
25403+ .owner = THIS_MODULE,
25404+ },
25405+ .id = I2C_DRIVERID_WM8711,
25406+ .attach_adapter = wm8711_i2c_attach,
25407+ .detach_client = wm8711_i2c_detach,
25408+ .command = NULL,
25409+};
25410+
25411+static struct i2c_client client_template = {
25412+ .name = "WM8711",
25413+ .driver = &wm8711_i2c_driver,
25414+};
25415+#endif
25416+
25417+static int wm8711_probe(struct platform_device *pdev)
25418+{
25419+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
25420+ struct wm8711_setup_data *setup;
25421+ struct snd_soc_codec* codec;
25422+ int ret = 0;
25423+
25424+ info("WM8711 Audio Codec %s", WM8711_VERSION);
25425+
25426+ setup = socdev->codec_data;
25427+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
25428+ if (codec == NULL)
25429+ return -ENOMEM;
25430+
25431+ socdev->codec = codec;
25432+ mutex_init(&codec->mutex);
25433+ INIT_LIST_HEAD(&codec->dapm_widgets);
25434+ INIT_LIST_HEAD(&codec->dapm_paths);
25435+
25436+ wm8711_socdev = socdev;
25437+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
25438+ if (setup->i2c_address) {
25439+ normal_i2c[0] = setup->i2c_address;
25440+ codec->hw_write = (hw_write_t)i2c_master_send;
25441+ ret = i2c_add_driver(&wm8711_i2c_driver);
25442+ if (ret != 0)
25443+ printk(KERN_ERR "can't add i2c driver");
25444+ }
25445+#else
25446+ /* Add other interfaces here */
25447+#endif
25448+ return ret;
25449+}
25450+
25451+/* power down chip */
25452+static int wm8711_remove(struct platform_device *pdev)
25453+{
25454+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
25455+ struct snd_soc_codec *codec = socdev->codec;
25456+
25457+ if (codec->control_data)
25458+ wm8711_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
25459+
25460+ snd_soc_free_pcms(socdev);
25461+ snd_soc_dapm_free(socdev);
25462+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
25463+ i2c_del_driver(&wm8711_i2c_driver);
25464+#endif
25465+ kfree(codec);
25466+
25467+ return 0;
25468+}
25469+
25470+struct snd_soc_codec_device soc_codec_dev_wm8711 = {
25471+ .probe = wm8711_probe,
25472+ .remove = wm8711_remove,
25473+ .suspend = wm8711_suspend,
25474+ .resume = wm8711_resume,
25475+};
25476+
25477+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8711);
25478+
25479+MODULE_DESCRIPTION("ASoC WM8711 driver");
25480+MODULE_AUTHOR("Mike Arthur");
25481+MODULE_LICENSE("GPL");
25482Index: linux-2.6-pxa-new/sound/soc/codecs/wm8711.h
25483===================================================================
25484--- /dev/null
25485+++ linux-2.6-pxa-new/sound/soc/codecs/wm8711.h
25486@@ -0,0 +1,39 @@
25487+/*
25488+ * wm8711.h -- WM8711 Soc Audio driver
25489+ *
25490+ * Copyright 2006 Wolfson Microelectronics
25491+ *
25492+ * Author: Mike Arthur <linux@wolfsonmicro.com>
25493+ *
25494+ * Based on wm8731.h
25495+ *
25496+ * This program is free software; you can redistribute it and/or modify
25497+ * it under the terms of the GNU General Public License version 2 as
25498+ * published by the Free Software Foundation.
25499+ */
25500+
25501+#ifndef _WM8711_H
25502+#define _WM8711_H
25503+
25504+/* WM8711 register space */
25505+
25506+#define WM8711_LOUT1V 0x02
25507+#define WM8711_ROUT1V 0x03
25508+#define WM8711_APANA 0x04
25509+#define WM8711_APDIGI 0x05
25510+#define WM8711_PWR 0x06
25511+#define WM8711_IFACE 0x07
25512+#define WM8711_SRATE 0x08
25513+#define WM8711_ACTIVE 0x09
25514+#define WM8711_RESET 0x0f
25515+
25516+#define WM8711_CACHEREGNUM 8
25517+
25518+struct wm8711_setup_data {
25519+ unsigned short i2c_address;
25520+};
25521+
25522+extern struct snd_soc_codec_dai wm8711_dai;
25523+extern struct snd_soc_codec_device soc_codec_dev_wm8711;
25524+
25525+#endif
25526Index: linux-2.6-pxa-new/sound/soc/codecs/wm8980.c
25527===================================================================
25528--- /dev/null
25529+++ linux-2.6-pxa-new/sound/soc/codecs/wm8980.c
25530@@ -0,0 +1,991 @@
25531+/*
25532+ * wm8980.c -- WM8980 ALSA Soc Audio driver
25533+ *
25534+ * Copyright 2006 Wolfson Microelectronics PLC.
25535+ *
25536+ * Authors:
25537+ * Mike Arthur <linux@wolfsonmicro.com>
25538+ *
25539+ * This program is free software; you can redistribute it and/or modify
25540+ * it under the terms of the GNU General Public License version 2 as
25541+ * published by the Free Software Foundation.
25542+ */
25543+
25544+#include <linux/module.h>
25545+#include <linux/moduleparam.h>
25546+#include <linux/version.h>
25547+#include <linux/kernel.h>
25548+#include <linux/init.h>
25549+#include <linux/delay.h>
25550+#include <linux/pm.h>
25551+#include <linux/i2c.h>
25552+#include <linux/platform_device.h>
25553+#include <sound/driver.h>
25554+#include <sound/core.h>
25555+#include <sound/pcm.h>
25556+#include <sound/pcm_params.h>
25557+#include <sound/soc.h>
25558+#include <sound/soc-dapm.h>
25559+#include <sound/initval.h>
25560+
25561+#include "wm8980.h"
25562+
25563+#define AUDIO_NAME "wm8980"
25564+#define WM8980_VERSION "0.2"
25565+
25566+/*
25567+ * Debug
25568+ */
25569+
25570+#define WM8980_DEBUG 0
25571+
25572+#ifdef WM8980_DEBUG
25573+#define dbg(format, arg...) \
25574+ printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
25575+#else
25576+#define dbg(format, arg...) do {} while (0)
25577+#endif
25578+#define err(format, arg...) \
25579+ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
25580+#define info(format, arg...) \
25581+ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
25582+#define warn(format, arg...) \
25583+ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
25584+
25585+struct snd_soc_codec_device soc_codec_dev_wm8980;
25586+
25587+/*
25588+ * wm8980 register cache
25589+ * We can't read the WM8980 register space when we are
25590+ * using 2 wire for device control, so we cache them instead.
25591+ */
25592+static const u16 wm8980_reg[WM8980_CACHEREGNUM] = {
25593+ 0x0000, 0x0000, 0x0000, 0x0000,
25594+ 0x0050, 0x0000, 0x0140, 0x0000,
25595+ 0x0000, 0x0000, 0x0000, 0x00ff,
25596+ 0x00ff, 0x0000, 0x0100, 0x00ff,
25597+ 0x00ff, 0x0000, 0x012c, 0x002c,
25598+ 0x002c, 0x002c, 0x002c, 0x0000,
25599+ 0x0032, 0x0000, 0x0000, 0x0000,
25600+ 0x0000, 0x0000, 0x0000, 0x0000,
25601+ 0x0038, 0x000b, 0x0032, 0x0000,
25602+ 0x0008, 0x000c, 0x0093, 0x00e9,
25603+ 0x0000, 0x0000, 0x0000, 0x0000,
25604+ 0x0033, 0x0010, 0x0010, 0x0100,
25605+ 0x0100, 0x0002, 0x0001, 0x0001,
25606+ 0x0039, 0x0039, 0x0039, 0x0039,
25607+ 0x0001, 0x0001,
25608+};
25609+
25610+#define WM8980_DAIFMT \
25611+ (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_RIGHT_J | \
25612+ SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | \
25613+ SND_SOC_DAIFMT_NB_IF | SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_IB_IF)
25614+
25615+#define WM8980_DIR \
25616+ (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
25617+
25618+#define WM8980_RATES \
25619+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
25620+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
25621+ SNDRV_PCM_RATE_48000)
25622+
25623+#define WM8980_PCM_FORMATS \
25624+ (SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \
25625+ SNDRV_PCM_FORMAT_S24_3LE | SNDRV_PCM_FORMAT_S24_LE | \
25626+ SNDRV_PCM_FORMAT_S32_LE)
25627+
25628+#define WM8980_BCLK \
25629+ (SND_SOC_FSBD(1) | SND_SOC_FSBD(2) | SND_SOC_FSBD(4) | SND_SOC_FSBD(8) |\
25630+ SND_SOC_FSBD(16) | SND_SOC_FSBD(32))
25631+
25632+static struct snd_soc_dai_mode wm8980_modes[] = {
25633+ /* codec frame and clock master modes */
25634+ {
25635+ .fmt = WM8980_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
25636+ .pcmfmt = WM8980_PCM_FORMATS,
25637+ .pcmrate = WM8980_RATES,
25638+ .pcmdir = WM8980_DIR,
25639+ .flags = SND_SOC_DAI_BFS_DIV,
25640+ .fs = 256,
25641+ .bfs = WM8980_BCLK,
25642+ },
25643+
25644+ /* codec frame and clock slave modes */
25645+ {
25646+ .fmt = WM8980_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
25647+ .pcmfmt = WM8980_PCM_FORMATS,
25648+ .pcmrate = WM8980_RATES,
25649+ .pcmdir = WM8980_DIR,
25650+ .fs = SND_SOC_FS_ALL,
25651+ .bfs = SND_SOC_FSB_ALL,
25652+ },
25653+};
25654+
25655+/*
25656+ * read wm8980 register cache
25657+ */
25658+static inline unsigned int wm8980_read_reg_cache(struct snd_soc_codec *codec,
25659+ unsigned int reg)
25660+{
25661+ u16 *cache = codec->reg_cache;
25662+ if (reg == WM8980_RESET)
25663+ return 0;
25664+ if (reg >= WM8980_CACHEREGNUM)
25665+ return -1;
25666+ return cache[reg];
25667+}
25668+
25669+/*
25670+ * write wm8980 register cache
25671+ */
25672+static inline void wm8980_write_reg_cache(struct snd_soc_codec *codec,
25673+ u16 reg, unsigned int value)
25674+{
25675+ u16 *cache = codec->reg_cache;
25676+ if (reg >= WM8980_CACHEREGNUM)
25677+ return;
25678+ cache[reg] = value;
25679+}
25680+
25681+/*
25682+ * write to the WM8980 register space
25683+ */
25684+static int wm8980_write(struct snd_soc_codec *codec, unsigned int reg,
25685+ unsigned int value)
25686+{
25687+ u8 data[2];
25688+
25689+ /* data is
25690+ * D15..D9 WM8980 register offset
25691+ * D8...D0 register data
25692+ */
25693+ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
25694+ data[1] = value & 0x00ff;
25695+
25696+ wm8980_write_reg_cache (codec, reg, value);
25697+ if (codec->hw_write(codec->control_data, data, 2) == 2)
25698+ return 0;
25699+ else
25700+ return -1;
25701+}
25702+
25703+#define wm8980_reset(c) wm8980_write(c, WM8980_RESET, 0)
25704+
25705+static const char *wm8980_companding[] = {"Off", "NC", "u-law", "A-law" };
25706+static const char *wm8980_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz" };
25707+static const char *wm8980_eqmode[] = {"Capture", "Playback" };
25708+static const char *wm8980_bw[] = {"Narrow", "Wide" };
25709+static const char *wm8980_eq1[] = {"80Hz", "105Hz", "135Hz", "175Hz" };
25710+static const char *wm8980_eq2[] = {"230Hz", "300Hz", "385Hz", "500Hz" };
25711+static const char *wm8980_eq3[] = {"650Hz", "850Hz", "1.1kHz", "1.4kHz" };
25712+static const char *wm8980_eq4[] = {"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz" };
25713+static const char *wm8980_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz" };
25714+static const char *wm8980_alc[] =
25715+ {"ALC both on", "ALC left only", "ALC right only", "Limiter" };
25716+
25717+static const struct soc_enum wm8980_enum[] = {
25718+ SOC_ENUM_SINGLE(WM8980_COMP, 1, 4, wm8980_companding), /* adc */
25719+ SOC_ENUM_SINGLE(WM8980_COMP, 3, 4, wm8980_companding), /* dac */
25720+ SOC_ENUM_SINGLE(WM8980_DAC, 4, 4, wm8980_deemp),
25721+ SOC_ENUM_SINGLE(WM8980_EQ1, 8, 2, wm8980_eqmode),
25722+
25723+ SOC_ENUM_SINGLE(WM8980_EQ1, 5, 4, wm8980_eq1),
25724+ SOC_ENUM_SINGLE(WM8980_EQ2, 8, 2, wm8980_bw),
25725+ SOC_ENUM_SINGLE(WM8980_EQ2, 5, 4, wm8980_eq2),
25726+ SOC_ENUM_SINGLE(WM8980_EQ3, 8, 2, wm8980_bw),
25727+
25728+ SOC_ENUM_SINGLE(WM8980_EQ3, 5, 4, wm8980_eq3),
25729+ SOC_ENUM_SINGLE(WM8980_EQ4, 8, 2, wm8980_bw),
25730+ SOC_ENUM_SINGLE(WM8980_EQ4, 5, 4, wm8980_eq4),
25731+ SOC_ENUM_SINGLE(WM8980_EQ5, 8, 2, wm8980_bw),
25732+
25733+ SOC_ENUM_SINGLE(WM8980_EQ5, 5, 4, wm8980_eq5),
25734+ SOC_ENUM_SINGLE(WM8980_ALC3, 8, 2, wm8980_alc),
25735+};
25736+
25737+static const struct snd_kcontrol_new wm8980_snd_controls[] = {
25738+SOC_SINGLE("Digital Loopback Switch", WM8980_COMP, 0, 1, 0),
25739+
25740+SOC_ENUM("ADC Companding", wm8980_enum[0]),
25741+SOC_ENUM("DAC Companding", wm8980_enum[1]),
25742+
25743+SOC_SINGLE("Jack Detection Enable", WM8980_JACK1, 6, 1, 0),
25744+
25745+SOC_SINGLE("DAC Right Inversion Switch", WM8980_DAC, 1, 1, 0),
25746+SOC_SINGLE("DAC Left Inversion Switch", WM8980_DAC, 0, 1, 0),
25747+
25748+SOC_SINGLE("Left Playback Volume", WM8980_DACVOLL, 0, 127, 0),
25749+SOC_SINGLE("Right Playback Volume", WM8980_DACVOLR, 0, 127, 0),
25750+
25751+SOC_SINGLE("High Pass Filter Switch", WM8980_ADC, 8, 1, 0),
25752+SOC_SINGLE("High Pass Filter Switch", WM8980_ADC, 8, 1, 0),
25753+SOC_SINGLE("High Pass Cut Off", WM8980_ADC, 4, 7, 0),
25754+SOC_SINGLE("Right ADC Inversion Switch", WM8980_ADC, 1, 1, 0),
25755+SOC_SINGLE("Left ADC Inversion Switch", WM8980_ADC, 0, 1, 0),
25756+
25757+SOC_SINGLE("Left Capture Volume", WM8980_ADCVOLL, 0, 127, 0),
25758+SOC_SINGLE("Right Capture Volume", WM8980_ADCVOLR, 0, 127, 0),
25759+
25760+SOC_ENUM("Equaliser Function", wm8980_enum[3]),
25761+SOC_ENUM("EQ1 Cut Off", wm8980_enum[4]),
25762+SOC_SINGLE("EQ1 Volume", WM8980_EQ1, 0, 31, 1),
25763+
25764+SOC_ENUM("Equaliser EQ2 Bandwith", wm8980_enum[5]),
25765+SOC_ENUM("EQ2 Cut Off", wm8980_enum[6]),
25766+SOC_SINGLE("EQ2 Volume", WM8980_EQ2, 0, 31, 1),
25767+
25768+SOC_ENUM("Equaliser EQ3 Bandwith", wm8980_enum[7]),
25769+SOC_ENUM("EQ3 Cut Off", wm8980_enum[8]),
25770+SOC_SINGLE("EQ3 Volume", WM8980_EQ3, 0, 31, 1),
25771+
25772+SOC_ENUM("Equaliser EQ4 Bandwith", wm8980_enum[9]),
25773+SOC_ENUM("EQ4 Cut Off", wm8980_enum[10]),
25774+SOC_SINGLE("EQ4 Volume", WM8980_EQ4, 0, 31, 1),
25775+
25776+SOC_ENUM("Equaliser EQ5 Bandwith", wm8980_enum[11]),
25777+SOC_ENUM("EQ5 Cut Off", wm8980_enum[12]),
25778+SOC_SINGLE("EQ5 Volume", WM8980_EQ5, 0, 31, 1),
25779+
25780+SOC_SINGLE("DAC Playback Limiter Switch", WM8980_DACLIM1, 8, 1, 0),
25781+SOC_SINGLE("DAC Playback Limiter Decay", WM8980_DACLIM1, 4, 15, 0),
25782+SOC_SINGLE("DAC Playback Limiter Attack", WM8980_DACLIM1, 0, 15, 0),
25783+
25784+SOC_SINGLE("DAC Playback Limiter Threshold", WM8980_DACLIM2, 4, 7, 0),
25785+SOC_SINGLE("DAC Playback Limiter Boost", WM8980_DACLIM2, 0, 15, 0),
25786+
25787+SOC_SINGLE("ALC Enable Switch", WM8980_ALC1, 8, 1, 0),
25788+SOC_SINGLE("ALC Capture Max Gain", WM8980_ALC1, 3, 7, 0),
25789+SOC_SINGLE("ALC Capture Min Gain", WM8980_ALC1, 0, 7, 0),
25790+
25791+SOC_SINGLE("ALC Capture ZC Switch", WM8980_ALC2, 8, 1, 0),
25792+SOC_SINGLE("ALC Capture Hold", WM8980_ALC2, 4, 7, 0),
25793+SOC_SINGLE("ALC Capture Target", WM8980_ALC2, 0, 15, 0),
25794+
25795+SOC_ENUM("ALC Capture Mode", wm8980_enum[13]),
25796+SOC_SINGLE("ALC Capture Decay", WM8980_ALC3, 4, 15, 0),
25797+SOC_SINGLE("ALC Capture Attack", WM8980_ALC3, 0, 15, 0),
25798+
25799+SOC_SINGLE("ALC Capture Noise Gate Switch", WM8980_NGATE, 3, 1, 0),
25800+SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8980_NGATE, 0, 7, 0),
25801+
25802+SOC_SINGLE("Left Capture PGA ZC Switch", WM8980_INPPGAL, 7, 1, 0),
25803+SOC_SINGLE("Left Capture PGA Volume", WM8980_INPPGAL, 0, 63, 0),
25804+
25805+SOC_SINGLE("Right Capture PGA ZC Switch", WM8980_INPPGAR, 7, 1, 0),
25806+SOC_SINGLE("Right Capture PGA Volume", WM8980_INPPGAR, 0, 63, 0),
25807+
25808+SOC_SINGLE("Left Headphone Playback ZC Switch", WM8980_HPVOLL, 7, 1, 0),
25809+SOC_SINGLE("Left Headphone Playback Switch", WM8980_HPVOLL, 6, 1, 1),
25810+SOC_SINGLE("Left Headphone Playback Volume", WM8980_HPVOLL, 0, 63, 0),
25811+
25812+SOC_SINGLE("Right Headphone Playback ZC Switch", WM8980_HPVOLR, 7, 1, 0),
25813+SOC_SINGLE("Right Headphone Playback Switch", WM8980_HPVOLR, 6, 1, 1),
25814+SOC_SINGLE("Right Headphone Playback Volume", WM8980_HPVOLR, 0, 63, 0),
25815+
25816+SOC_SINGLE("Left Speaker Playback ZC Switch", WM8980_SPKVOLL, 7, 1, 0),
25817+SOC_SINGLE("Left Speaker Playback Switch", WM8980_SPKVOLL, 6, 1, 1),
25818+SOC_SINGLE("Left Speaker Playback Volume", WM8980_SPKVOLL, 0, 63, 0),
25819+
25820+SOC_SINGLE("Right Speaker Playback ZC Switch", WM8980_SPKVOLR, 7, 1, 0),
25821+SOC_SINGLE("Right Speaker Playback Switch", WM8980_SPKVOLR, 6, 1, 1),
25822+SOC_SINGLE("Right Speaker Playback Volume", WM8980_SPKVOLR, 0, 63, 0),
25823+
25824+SOC_DOUBLE_R("Capture Boost(+20dB)", WM8980_ADCBOOSTL, WM8980_ADCBOOSTR,
25825+ 8, 1, 0),
25826+};
25827+
25828+/* add non dapm controls */
25829+static int wm8980_add_controls(struct snd_soc_codec *codec)
25830+{
25831+ int err, i;
25832+
25833+ for (i = 0; i < ARRAY_SIZE(wm8980_snd_controls); i++) {
25834+ err = snd_ctl_add(codec->card,
25835+ snd_soc_cnew(&wm8980_snd_controls[i],codec, NULL));
25836+ if (err < 0)
25837+ return err;
25838+ }
25839+
25840+ return 0;
25841+}
25842+
25843+/* Left Output Mixer */
25844+static const snd_kcontrol_new_t wm8980_left_mixer_controls[] = {
25845+SOC_DAPM_SINGLE("Right PCM Playback Switch", WM8980_OUTPUT, 6, 1, 1),
25846+SOC_DAPM_SINGLE("Left PCM Playback Switch", WM8980_MIXL, 0, 1, 1),
25847+SOC_DAPM_SINGLE("Line Bypass Switch", WM8980_MIXL, 1, 1, 0),
25848+SOC_DAPM_SINGLE("Aux Playback Switch", WM8980_MIXL, 5, 1, 0),
25849+};
25850+
25851+/* Right Output Mixer */
25852+static const snd_kcontrol_new_t wm8980_right_mixer_controls[] = {
25853+SOC_DAPM_SINGLE("Left PCM Playback Switch", WM8980_OUTPUT, 5, 1, 1),
25854+SOC_DAPM_SINGLE("Right PCM Playback Switch", WM8980_MIXR, 0, 1, 1),
25855+SOC_DAPM_SINGLE("Line Bypass Switch", WM8980_MIXR, 1, 1, 0),
25856+SOC_DAPM_SINGLE("Aux Playback Switch", WM8980_MIXR, 5, 1, 0),
25857+};
25858+
25859+/* Left AUX Input boost vol */
25860+static const snd_kcontrol_new_t wm8980_laux_boost_controls =
25861+SOC_DAPM_SINGLE("Left Aux Volume", WM8980_ADCBOOSTL, 0, 3, 0);
25862+
25863+/* Right AUX Input boost vol */
25864+static const snd_kcontrol_new_t wm8980_raux_boost_controls =
25865+SOC_DAPM_SINGLE("Right Aux Volume", WM8980_ADCBOOSTR, 0, 3, 0);
25866+
25867+/* Left Input boost vol */
25868+static const snd_kcontrol_new_t wm8980_lmic_boost_controls =
25869+SOC_DAPM_SINGLE("Left Input Volume", WM8980_ADCBOOSTL, 4, 3, 0);
25870+
25871+/* Right Input boost vol */
25872+static const snd_kcontrol_new_t wm8980_rmic_boost_controls =
25873+SOC_DAPM_SINGLE("Right Input Volume", WM8980_ADCBOOSTR, 4, 3, 0);
25874+
25875+/* Left Aux In to PGA */
25876+static const snd_kcontrol_new_t wm8980_laux_capture_boost_controls =
25877+SOC_DAPM_SINGLE("Left Capture Switch", WM8980_ADCBOOSTL, 8, 1, 0);
25878+
25879+/* Right Aux In to PGA */
25880+static const snd_kcontrol_new_t wm8980_raux_capture_boost_controls =
25881+SOC_DAPM_SINGLE("Right Capture Switch", WM8980_ADCBOOSTR, 8, 1, 0);
25882+
25883+/* Left Input P In to PGA */
25884+static const snd_kcontrol_new_t wm8980_lmicp_capture_boost_controls =
25885+SOC_DAPM_SINGLE("Left Input P Capture Boost Switch", WM8980_INPUT, 0, 1, 0);
25886+
25887+/* Right Input P In to PGA */
25888+static const snd_kcontrol_new_t wm8980_rmicp_capture_boost_controls =
25889+SOC_DAPM_SINGLE("Right Input P Capture Boost Switch", WM8980_INPUT, 4, 1, 0);
25890+
25891+/* Left Input N In to PGA */
25892+static const snd_kcontrol_new_t wm8980_lmicn_capture_boost_controls =
25893+SOC_DAPM_SINGLE("Left Input N Capture Boost Switch", WM8980_INPUT, 1, 1, 0);
25894+
25895+/* Right Input N In to PGA */
25896+static const snd_kcontrol_new_t wm8980_rmicn_capture_boost_controls =
25897+SOC_DAPM_SINGLE("Right Input N Capture Boost Switch", WM8980_INPUT, 5, 1, 0);
25898+
25899+// TODO Widgets
25900+static const struct snd_soc_dapm_widget wm8980_dapm_widgets[] = {
25901+#if 0
25902+//SND_SOC_DAPM_MUTE("Mono Mute", WM8980_MONOMIX, 6, 0),
25903+//SND_SOC_DAPM_MUTE("Speaker Mute", WM8980_SPKMIX, 6, 0),
25904+
25905+SND_SOC_DAPM_MIXER("Speaker Mixer", WM8980_POWER3, 2, 0,
25906+ &wm8980_speaker_mixer_controls[0],
25907+ ARRAY_SIZE(wm8980_speaker_mixer_controls)),
25908+SND_SOC_DAPM_MIXER("Mono Mixer", WM8980_POWER3, 3, 0,
25909+ &wm8980_mono_mixer_controls[0],
25910+ ARRAY_SIZE(wm8980_mono_mixer_controls)),
25911+SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8980_POWER3, 0, 0),
25912+SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8980_POWER3, 0, 0),
25913+SND_SOC_DAPM_PGA("Aux Input", WM8980_POWER1, 6, 0, NULL, 0),
25914+SND_SOC_DAPM_PGA("SpkN Out", WM8980_POWER3, 5, 0, NULL, 0),
25915+SND_SOC_DAPM_PGA("SpkP Out", WM8980_POWER3, 6, 0, NULL, 0),
25916+SND_SOC_DAPM_PGA("Mono Out", WM8980_POWER3, 7, 0, NULL, 0),
25917+SND_SOC_DAPM_PGA("Mic PGA", WM8980_POWER2, 2, 0, NULL, 0),
25918+
25919+SND_SOC_DAPM_PGA("Aux Boost", SND_SOC_NOPM, 0, 0,
25920+ &wm8980_aux_boost_controls, 1),
25921+SND_SOC_DAPM_PGA("Mic Boost", SND_SOC_NOPM, 0, 0,
25922+ &wm8980_mic_boost_controls, 1),
25923+SND_SOC_DAPM_SWITCH("Capture Boost", SND_SOC_NOPM, 0, 0,
25924+ &wm8980_capture_boost_controls),
25925+
25926+SND_SOC_DAPM_MIXER("Boost Mixer", WM8980_POWER2, 4, 0, NULL, 0),
25927+
25928+SND_SOC_DAPM_MICBIAS("Mic Bias", WM8980_POWER1, 4, 0),
25929+
25930+SND_SOC_DAPM_INPUT("MICN"),
25931+SND_SOC_DAPM_INPUT("MICP"),
25932+SND_SOC_DAPM_INPUT("AUX"),
25933+SND_SOC_DAPM_OUTPUT("MONOOUT"),
25934+SND_SOC_DAPM_OUTPUT("SPKOUTP"),
25935+SND_SOC_DAPM_OUTPUT("SPKOUTN"),
25936+#endif
25937+};
25938+
25939+static const char *audio_map[][3] = {
25940+ /* Mono output mixer */
25941+ {"Mono Mixer", "PCM Playback Switch", "DAC"},
25942+ {"Mono Mixer", "Aux Playback Switch", "Aux Input"},
25943+ {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
25944+
25945+ /* Speaker output mixer */
25946+ {"Speaker Mixer", "PCM Playback Switch", "DAC"},
25947+ {"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
25948+ {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
25949+
25950+ /* Outputs */
25951+ {"Mono Out", NULL, "Mono Mixer"},
25952+ {"MONOOUT", NULL, "Mono Out"},
25953+ {"SpkN Out", NULL, "Speaker Mixer"},
25954+ {"SpkP Out", NULL, "Speaker Mixer"},
25955+ {"SPKOUTN", NULL, "SpkN Out"},
25956+ {"SPKOUTP", NULL, "SpkP Out"},
25957+
25958+ /* Boost Mixer */
25959+ {"Boost Mixer", NULL, "ADC"},
25960+ {"Capture Boost Switch", "Aux Capture Boost Switch", "AUX"},
25961+ {"Aux Boost", "Aux Volume", "Boost Mixer"},
25962+ {"Capture Boost", "Capture Switch", "Boost Mixer"},
25963+ {"Mic Boost", "Mic Volume", "Boost Mixer"},
25964+
25965+ /* Inputs */
25966+ {"MICP", NULL, "Mic Boost"},
25967+ {"MICN", NULL, "Mic PGA"},
25968+ {"Mic PGA", NULL, "Capture Boost"},
25969+ {"AUX", NULL, "Aux Input"},
25970+
25971+ /* */
25972+
25973+ /* terminator */
25974+ {NULL, NULL, NULL},
25975+};
25976+
25977+static int wm8980_add_widgets(struct snd_soc_codec *codec)
25978+{
25979+ int i;
25980+
25981+ for(i = 0; i < ARRAY_SIZE(wm8980_dapm_widgets); i++) {
25982+ snd_soc_dapm_new_control(codec, &wm8980_dapm_widgets[i]);
25983+ }
25984+
25985+ /* set up audio path map */
25986+ for(i = 0; audio_map[i][0] != NULL; i++) {
25987+ snd_soc_dapm_connect_input(codec, audio_map[i][0], audio_map[i][1],
25988+ audio_map[i][2]);
25989+ }
25990+
25991+ snd_soc_dapm_new_widgets(codec);
25992+ return 0;
25993+}
25994+
25995+struct pll_ {
25996+ unsigned int in_hz, out_hz;
25997+ unsigned int pre:4; /* prescale - 1 */
25998+ unsigned int n:4;
25999+ unsigned int k;
26000+};
26001+
26002+struct pll_ pll[] = {
26003+ {12000000, 11289600, 0, 7, 0x86c220},
26004+ {12000000, 12288000, 0, 8, 0x3126e8},
26005+ {13000000, 11289600, 0, 6, 0xf28bd4},
26006+ {13000000, 12288000, 0, 7, 0x8fd525},
26007+ {12288000, 11289600, 0, 7, 0x59999a},
26008+ {11289600, 12288000, 0, 8, 0x80dee9},
26009+ /* TODO: liam - add more entries */
26010+};
26011+
26012+static int set_pll(struct snd_soc_codec *codec, unsigned int in,
26013+ unsigned int out)
26014+{
26015+ int i;
26016+ u16 reg;
26017+
26018+ if(out == 0) {
26019+ reg = wm8980_read_reg_cache(codec, WM8980_POWER1);
26020+ wm8980_write(codec, WM8980_POWER1, reg & 0x1df);
26021+ return 0;
26022+ }
26023+
26024+ for(i = 0; i < ARRAY_SIZE(pll); i++) {
26025+ if (in == pll[i].in_hz && out == pll[i].out_hz) {
26026+ wm8980_write(codec, WM8980_PLLN, (pll[i].pre << 4) | pll[i].n);
26027+ wm8980_write(codec, WM8980_PLLK1, pll[i].k >> 18);
26028+ wm8980_write(codec, WM8980_PLLK1, (pll[i].k >> 9) && 0x1ff);
26029+ wm8980_write(codec, WM8980_PLLK1, pll[i].k && 0x1ff);
26030+ reg = wm8980_read_reg_cache(codec, WM8980_POWER1);
26031+ wm8980_write(codec, WM8980_POWER1, reg | 0x020);
26032+ return 0;
26033+ }
26034+ }
26035+ return -EINVAL;
26036+}
26037+
26038+/* mclk dividers * 2 */
26039+static unsigned char mclk_div[] = {2, 3, 4, 6, 8, 12, 16, 24};
26040+
26041+/* we need 256FS to drive the DAC's and ADC's */
26042+static unsigned int wm8980_config_sysclk(struct snd_soc_codec_dai *dai,
26043+ struct snd_soc_clock_info *info, unsigned int clk)
26044+{
26045+ int i, j, best_clk = info->fs * info->rate;
26046+
26047+ /* can we run at this clk without the PLL ? */
26048+ for (i = 0; i < ARRAY_SIZE(mclk_div); i++) {
26049+ if ((best_clk >> 1) * mclk_div[i] == clk) {
26050+ dai->pll_in = 0;
26051+ dai->clk_div = mclk_div[i];
26052+ dai->mclk = best_clk;
26053+ return dai->mclk;
26054+ }
26055+ }
26056+
26057+ /* now check for PLL support */
26058+ for (i = 0; i < ARRAY_SIZE(pll); i++) {
26059+ if (pll[i].in_hz == clk) {
26060+ for (j = 0; j < ARRAY_SIZE(mclk_div); j++) {
26061+ if (pll[i].out_hz == mclk_div[j] * (best_clk >> 1)) {
26062+ dai->pll_in = clk;
26063+ dai->pll_out = pll[i].out_hz;
26064+ dai->clk_div = mclk_div[j];
26065+ dai->mclk = best_clk;
26066+ return dai->mclk;
26067+ }
26068+ }
26069+ }
26070+ }
26071+
26072+ /* this clk is not supported */
26073+ return 0;
26074+}
26075+
26076+static int wm8980_pcm_prepare(snd_pcm_substream_t *substream)
26077+{
26078+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
26079+ struct snd_soc_device *socdev = rtd->socdev;
26080+ struct snd_soc_codec *codec = socdev->codec;
26081+ struct snd_soc_codec_dai *dai = rtd->codec_dai;
26082+ u16 iface = 0, bfs, clk = 0, adn;
26083+ int fs = 48000 << 7, i;
26084+
26085+ bfs = SND_SOC_FSBD_REAL(rtd->codec_dai->dai_runtime.bfs);
26086+ switch (bfs) {
26087+ case 2:
26088+ clk |= 0x1 << 2;
26089+ break;
26090+ case 4:
26091+ clk |= 0x2 << 2;
26092+ break;
26093+ case 8:
26094+ clk |= 0x3 << 2;
26095+ break;
26096+ case 16:
26097+ clk |= 0x4 << 2;
26098+ break;
26099+ case 32:
26100+ clk |= 0x5 << 2;
26101+ break;
26102+ }
26103+
26104+ /* set master/slave audio interface */
26105+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
26106+ case SND_SOC_DAIFMT_CBM_CFM:
26107+ clk |= 0x0001;
26108+ break;
26109+ case SND_SOC_DAIFMT_CBS_CFS:
26110+ break;
26111+ }
26112+
26113+ /* interface format */
26114+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
26115+ case SND_SOC_DAIFMT_I2S:
26116+ iface |= 0x0010;
26117+ break;
26118+ case SND_SOC_DAIFMT_RIGHT_J:
26119+ break;
26120+ case SND_SOC_DAIFMT_LEFT_J:
26121+ iface |= 0x0008;
26122+ break;
26123+ case SND_SOC_DAIFMT_DSP_A:
26124+ iface |= 0x00018;
26125+ break;
26126+ }
26127+
26128+ /* bit size */
26129+ switch (rtd->codec_dai->dai_runtime.pcmfmt) {
26130+ case SNDRV_PCM_FMTBIT_S16_LE:
26131+ break;
26132+ case SNDRV_PCM_FMTBIT_S20_3LE:
26133+ iface |= 0x0020;
26134+ break;
26135+ case SNDRV_PCM_FMTBIT_S24_LE:
26136+ iface |= 0x0040;
26137+ break;
26138+ case SNDRV_PCM_FMTBIT_S32_LE:
26139+ iface |= 0x0060;
26140+ break;
26141+ }
26142+
26143+ /* clock inversion */
26144+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_INV_MASK) {
26145+ case SND_SOC_DAIFMT_NB_NF:
26146+ break;
26147+ case SND_SOC_DAIFMT_IB_IF:
26148+ iface |= 0x0180;
26149+ break;
26150+ case SND_SOC_DAIFMT_IB_NF:
26151+ iface |= 0x0100;
26152+ break;
26153+ case SND_SOC_DAIFMT_NB_IF:
26154+ iface |= 0x0080;
26155+ break;
26156+ }
26157+
26158+ /* filter coefficient */
26159+ adn = wm8980_read_reg_cache(codec, WM8980_ADD) & 0x1f1;
26160+ switch (rtd->codec_dai->dai_runtime.pcmrate) {
26161+ case SNDRV_PCM_RATE_8000:
26162+ adn |= 0x5 << 1;
26163+ fs = 8000 << 7;
26164+ break;
26165+ case SNDRV_PCM_RATE_11025:
26166+ adn |= 0x4 << 1;
26167+ fs = 11025 << 7;
26168+ break;
26169+ case SNDRV_PCM_RATE_16000:
26170+ adn |= 0x3 << 1;
26171+ fs = 16000 << 7;
26172+ break;
26173+ case SNDRV_PCM_RATE_22050:
26174+ adn |= 0x2 << 1;
26175+ fs = 22050 << 7;
26176+ break;
26177+ case SNDRV_PCM_RATE_32000:
26178+ adn |= 0x1 << 1;
26179+ fs = 32000 << 7;
26180+ break;
26181+ case SNDRV_PCM_RATE_44100:
26182+ fs = 44100 << 7;
26183+ break;
26184+ }
26185+
26186+ /* do we need to enable the PLL */
26187+ if(dai->pll_in)
26188+ set_pll(codec, dai->pll_in, dai->pll_out);
26189+
26190+ /* divide the clock to 256 fs */
26191+ for(i = 0; i < ARRAY_SIZE(mclk_div); i++) {
26192+ if (dai->clk_div == mclk_div[i]) {
26193+ clk |= i << 5;
26194+ clk &= 0xff;
26195+ goto set;
26196+ }
26197+ }
26198+
26199+set:
26200+ /* set iface */
26201+ wm8980_write(codec, WM8980_IFACE, iface);
26202+ wm8980_write(codec, WM8980_CLOCK, clk);
26203+
26204+ return 0;
26205+}
26206+
26207+static int wm8980_hw_free(struct snd_pcm_substream *substream)
26208+{
26209+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
26210+ struct snd_soc_device *socdev = rtd->socdev;
26211+ struct snd_soc_codec *codec = socdev->codec;
26212+ set_pll(codec, 0, 0);
26213+ return 0;
26214+}
26215+
26216+static int wm8980_mute(struct snd_soc_codec *codec,
26217+ struct snd_soc_codec_dai *dai, int mute)
26218+{
26219+ u16 mute_reg = wm8980_read_reg_cache(codec, WM8980_DAC) & 0xffbf;
26220+ if(mute)
26221+ wm8980_write(codec, WM8980_DAC, mute_reg | 0x40);
26222+ else
26223+ wm8980_write(codec, WM8980_DAC, mute_reg);
26224+
26225+ return 0;
26226+}
26227+
26228+/* TODO: liam need to make this lower power with dapm */
26229+static int wm8980_dapm_event(struct snd_soc_codec *codec, int event)
26230+{
26231+
26232+ switch (event) {
26233+ case SNDRV_CTL_POWER_D0: /* full On */
26234+ /* vref/mid, clk and osc on, dac unmute, active */
26235+ wm8980_write(codec, WM8980_POWER1, 0x1ff);
26236+ wm8980_write(codec, WM8980_POWER2, 0x1ff);
26237+ wm8980_write(codec, WM8980_POWER3, 0x1ff);
26238+ break;
26239+ case SNDRV_CTL_POWER_D1: /* partial On */
26240+ case SNDRV_CTL_POWER_D2: /* partial On */
26241+ break;
26242+ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
26243+ /* everything off except vref/vmid, dac mute, inactive */
26244+
26245+ break;
26246+ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
26247+ /* everything off, dac mute, inactive */
26248+ wm8980_write(codec, WM8980_POWER1, 0x0);
26249+ wm8980_write(codec, WM8980_POWER2, 0x0);
26250+ wm8980_write(codec, WM8980_POWER3, 0x0);
26251+ break;
26252+ }
26253+ codec->dapm_state = event;
26254+ return 0;
26255+}
26256+
26257+struct snd_soc_codec_dai wm8980_dai = {
26258+ .name = "WM8980 HiFi",
26259+ .playback = {
26260+ .stream_name = "Playback",
26261+ .channels_min = 1,
26262+ .channels_max = 2,
26263+ },
26264+ .capture = {
26265+ .stream_name = "Capture",
26266+ .channels_min = 1,
26267+ .channels_max = 2,
26268+ },
26269+ .config_sysclk = wm8980_config_sysclk,
26270+ .digital_mute = wm8980_mute,
26271+ .ops = {
26272+ .prepare = wm8980_pcm_prepare,
26273+ .hw_free = wm8980_hw_free,
26274+ },
26275+ .caps = {
26276+ .num_modes = ARRAY_SIZE(wm8980_modes),
26277+ .mode = wm8980_modes,
26278+ },
26279+};
26280+EXPORT_SYMBOL_GPL(wm8980_dai);
26281+
26282+static int wm8980_suspend(struct platform_device *pdev, pm_message_t state)
26283+{
26284+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
26285+ struct snd_soc_codec *codec = socdev->codec;
26286+
26287+ wm8980_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
26288+ return 0;
26289+}
26290+
26291+static int wm8980_resume(struct platform_device *pdev)
26292+{
26293+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
26294+ struct snd_soc_codec *codec = socdev->codec;
26295+ int i;
26296+ u8 data[2];
26297+ u16 *cache = codec->reg_cache;
26298+
26299+ /* Sync reg_cache with the hardware */
26300+ for (i = 0; i < ARRAY_SIZE(wm8980_reg); i++) {
26301+ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
26302+ data[1] = cache[i] & 0x00ff;
26303+ codec->hw_write(codec->control_data, data, 2);
26304+ }
26305+ wm8980_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
26306+ wm8980_dapm_event(codec, codec->suspend_dapm_state);
26307+ return 0;
26308+}
26309+
26310+/*
26311+ * initialise the WM8980 driver
26312+ * register the mixer and dsp interfaces with the kernel
26313+ */
26314+static int wm8980_init(struct snd_soc_device* socdev)
26315+{
26316+ struct snd_soc_codec *codec = socdev->codec;
26317+ int ret = 0;
26318+
26319+ codec->name = "WM8980";
26320+ codec->owner = THIS_MODULE;
26321+ codec->read = wm8980_read_reg_cache;
26322+ codec->write = wm8980_write;
26323+ codec->dapm_event = wm8980_dapm_event;
26324+ codec->dai = &wm8980_dai;
26325+ codec->num_dai = 1;
26326+ codec->reg_cache_size = ARRAY_SIZE(wm8980_reg);
26327+ codec->reg_cache =
26328+ kzalloc(sizeof(u16) * ARRAY_SIZE(wm8980_reg), GFP_KERNEL);
26329+ if (codec->reg_cache == NULL)
26330+ return -ENOMEM;
26331+ memcpy(codec->reg_cache, wm8980_reg,
26332+ sizeof(u16) * ARRAY_SIZE(wm8980_reg));
26333+ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm8980_reg);
26334+
26335+ wm8980_reset(codec);
26336+
26337+ /* register pcms */
26338+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
26339+ if(ret < 0) {
26340+ kfree(codec->reg_cache);
26341+ return ret;
26342+ }
26343+
26344+ /* power on device */
26345+ wm8980_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
26346+ wm8980_add_controls(codec);
26347+ wm8980_add_widgets(codec);
26348+ ret = snd_soc_register_card(socdev);
26349+ if(ret < 0) {
26350+ snd_soc_free_pcms(socdev);
26351+ snd_soc_dapm_free(socdev);
26352+ }
26353+
26354+ return ret;
26355+}
26356+
26357+static struct snd_soc_device *wm8980_socdev;
26358+
26359+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
26360+
26361+/*
26362+ * WM8980 2 wire address is 0x1a
26363+ */
26364+#define I2C_DRIVERID_WM8980 0xfefe /* liam - need a proper id */
26365+
26366+static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
26367+
26368+/* Magic definition of all other variables and things */
26369+I2C_CLIENT_INSMOD;
26370+
26371+static struct i2c_driver wm8980_i2c_driver;
26372+static struct i2c_client client_template;
26373+
26374+/* If the i2c layer weren't so broken, we could pass this kind of data
26375+ around */
26376+
26377+static int wm8980_codec_probe(struct i2c_adapter *adap, int addr, int kind)
26378+{
26379+ struct snd_soc_device *socdev = wm8980_socdev;
26380+ struct wm8980_setup_data *setup = socdev->codec_data;
26381+ struct snd_soc_codec *codec = socdev->codec;
26382+ struct i2c_client *i2c;
26383+ int ret;
26384+
26385+ if (addr != setup->i2c_address)
26386+ return -ENODEV;
26387+
26388+ client_template.adapter = adap;
26389+ client_template.addr = addr;
26390+
26391+ i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
26392+ if (i2c == NULL){
26393+ kfree(codec);
26394+ return -ENOMEM;
26395+ }
26396+ memcpy(i2c, &client_template, sizeof(struct i2c_client));
26397+
26398+ i2c_set_clientdata(i2c, codec);
26399+
26400+ codec->control_data = i2c;
26401+
26402+ ret = i2c_attach_client(i2c);
26403+ if(ret < 0) {
26404+ err("failed to attach codec at addr %x\n", addr);
26405+ goto err;
26406+ }
26407+
26408+ ret = wm8980_init(socdev);
26409+ if(ret < 0) {
26410+ err("failed to initialise WM8980\n");
26411+ goto err;
26412+ }
26413+ return ret;
26414+
26415+err:
26416+ kfree(codec);
26417+ kfree(i2c);
26418+ return ret;
26419+
26420+}
26421+
26422+static int wm8980_i2c_detach(struct i2c_client *client)
26423+{
26424+ struct snd_soc_codec *codec = i2c_get_clientdata(client);
26425+
26426+ i2c_detach_client(client);
26427+
26428+ kfree(codec->reg_cache);
26429+ kfree(client);
26430+
26431+ return 0;
26432+}
26433+
26434+static int wm8980_i2c_attach(struct i2c_adapter *adap)
26435+{
26436+ return i2c_probe(adap, &addr_data, wm8980_codec_probe);
26437+}
26438+
26439+/* corgi i2c codec control layer */
26440+static struct i2c_driver wm8980_i2c_driver = {
26441+ .driver = {
26442+ .name = "WM8980 I2C Codec",
26443+ .owner = THIS_MODULE,
26444+ },
26445+ .id = I2C_DRIVERID_WM8980,
26446+ .attach_adapter = wm8980_i2c_attach,
26447+ .detach_client = wm8980_i2c_detach,
26448+ .command = NULL,
26449+};
26450+
26451+static struct i2c_client client_template = {
26452+ .name = "WM8980",
26453+ .driver = &wm8980_i2c_driver,
26454+};
26455+#endif
26456+
26457+static int wm8980_probe(struct platform_device *pdev)
26458+{
26459+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
26460+ struct wm8980_setup_data *setup;
26461+ struct snd_soc_codec *codec;
26462+ int ret = 0;
26463+
26464+ info("WM8980 Audio Codec %s", WM8980_VERSION);
26465+
26466+ setup = socdev->codec_data;
26467+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
26468+ if (codec == NULL)
26469+ return -ENOMEM;
26470+
26471+ socdev->codec = codec;
26472+ mutex_init(&codec->mutex);
26473+ INIT_LIST_HEAD(&codec->dapm_widgets);
26474+ INIT_LIST_HEAD(&codec->dapm_paths);
26475+
26476+ wm8980_socdev = socdev;
26477+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
26478+ if (setup->i2c_address) {
26479+ normal_i2c[0] = setup->i2c_address;
26480+ codec->hw_write = (hw_write_t)i2c_master_send;
26481+ ret = i2c_add_driver(&wm8980_i2c_driver);
26482+ if (ret != 0)
26483+ printk(KERN_ERR "can't add i2c driver");
26484+ }
26485+#else
26486+ /* Add other interfaces here */
26487+#endif
26488+ return ret;
26489+}
26490+
26491+/* power down chip */
26492+static int wm8980_remove(struct platform_device *pdev)
26493+{
26494+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
26495+ struct snd_soc_codec *codec = socdev->codec;
26496+
26497+ if (codec->control_data)
26498+ wm8980_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
26499+
26500+ snd_soc_free_pcms(socdev);
26501+ snd_soc_dapm_free(socdev);
26502+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
26503+ i2c_del_driver(&wm8980_i2c_driver);
26504+#endif
26505+ kfree(codec);
26506+
26507+ return 0;
26508+}
26509+
26510+struct snd_soc_codec_device soc_codec_dev_wm8980 = {
26511+ .probe = wm8980_probe,
26512+ .remove = wm8980_remove,
26513+ .suspend = wm8980_suspend,
26514+ .resume = wm8980_resume,
26515+};
26516+
26517+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8980);
26518+
26519+MODULE_DESCRIPTION("ASoC WM8980 driver");
26520+MODULE_AUTHOR("Mike Arthur");
26521+MODULE_LICENSE("GPL");
26522Index: linux-2.6-pxa-new/sound/soc/codecs/wm8980.h
26523===================================================================
26524--- /dev/null
26525+++ linux-2.6-pxa-new/sound/soc/codecs/wm8980.h
26526@@ -0,0 +1,77 @@
26527+/*
26528+ * wm8980.h -- WM8980 Soc Audio driver
26529+ *
26530+ * This program is free software; you can redistribute it and/or modify
26531+ * it under the terms of the GNU General Public License version 2 as
26532+ * published by the Free Software Foundation.
26533+ */
26534+
26535+#ifndef _WM8980_H
26536+#define _WM8980_H
26537+
26538+/* WM8980 register space */
26539+
26540+#define WM8980_RESET 0x0
26541+#define WM8980_POWER1 0x1
26542+#define WM8980_POWER2 0x2
26543+#define WM8980_POWER3 0x3
26544+#define WM8980_IFACE 0x4
26545+#define WM8980_COMP 0x5
26546+#define WM8980_CLOCK 0x6
26547+#define WM8980_ADD 0x7
26548+#define WM8980_GPIO 0x8
26549+#define WM8980_JACK1 0x9
26550+#define WM8980_DAC 0xa
26551+#define WM8980_DACVOLL 0xb
26552+#define WM8980_DACVOLR 0xc
26553+#define WM8980_JACK2 0xd
26554+#define WM8980_ADC 0xe
26555+#define WM8980_ADCVOLL 0xf
26556+#define WM8980_ADCVOLR 0x10
26557+#define WM8980_EQ1 0x12
26558+#define WM8980_EQ2 0x13
26559+#define WM8980_EQ3 0x14
26560+#define WM8980_EQ4 0x15
26561+#define WM8980_EQ5 0x16
26562+#define WM8980_DACLIM1 0x18
26563+#define WM8980_DACLIM2 0x19
26564+#define WM8980_NOTCH1 0x1b
26565+#define WM8980_NOTCH2 0x1c
26566+#define WM8980_NOTCH3 0x1d
26567+#define WM8980_NOTCH4 0x1e
26568+#define WM8980_ALC1 0x20
26569+#define WM8980_ALC2 0x21
26570+#define WM8980_ALC3 0x22
26571+#define WM8980_NGATE 0x23
26572+#define WM8980_PLLN 0x24
26573+#define WM8980_PLLK1 0x25
26574+#define WM8980_PLLK2 0x26
26575+#define WM8980_PLLK3 0x27
26576+#define WM8980_VIDEO 0x28
26577+#define WM8980_3D 0x29
26578+#define WM8980_BEEP 0x2b
26579+#define WM8980_INPUT 0x2c
26580+#define WM8980_INPPGAL 0x2d
26581+#define WM8980_INPPGAR 0x2e
26582+#define WM8980_ADCBOOSTL 0x2f
26583+#define WM8980_ADCBOOSTR 0x30
26584+#define WM8980_OUTPUT 0x31
26585+#define WM8980_MIXL 0x32
26586+#define WM8980_MIXR 0x33
26587+#define WM8980_HPVOLL 0x34
26588+#define WM8980_HPVOLR 0x35
26589+#define WM8980_SPKVOLL 0x36
26590+#define WM8980_SPKVOLR 0x37
26591+#define WM8980_OUT3MIX 0x38
26592+#define WM8980_MONOMIX 0x39
26593+
26594+#define WM8980_CACHEREGNUM 58
26595+
26596+struct wm8980_setup_data {
26597+ unsigned short i2c_address;
26598+};
26599+
26600+extern struct snd_soc_codec_dai wm8980_dai;
26601+extern struct snd_soc_codec_device soc_codec_dev_wm8980;
26602+
26603+#endif
26604Index: linux-2.6-pxa-new/sound/soc/at91/eti_b1_wm8731.c
26605===================================================================
26606--- /dev/null
26607+++ linux-2.6-pxa-new/sound/soc/at91/eti_b1_wm8731.c
26608@@ -0,0 +1,230 @@
26609+/*
26610+ * eti_b1_wm8731 -- SoC audio for Endrelia ETI_B1.
26611+ *
26612+ * Author: Frank Mandarino <fmandarino@endrelia.com>
26613+ * Endrelia Technologies Inc.
26614+ * Created: Mar 29, 2006
26615+ *
26616+ * Based on corgi.c by:
26617+ *
26618+ * Copyright 2005 Wolfson Microelectronics PLC.
26619+ * Copyright 2005 Openedhand Ltd.
26620+ *
26621+ * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
26622+ * Richard Purdie <richard@openedhand.com>
26623+ *
26624+ * This program is free software; you can redistribute it and/or modify it
26625+ * under the terms of the GNU General Public License as published by the
26626+ * Free Software Foundation; either version 2 of the License, or (at your
26627+ * option) any later version.
26628+ *
26629+ * Revision history
26630+ * 30th Nov 2005 Initial version.
26631+ *
26632+ */
26633+
26634+#include <linux/module.h>
26635+#include <linux/moduleparam.h>
26636+#include <linux/version.h>
26637+#include <linux/kernel.h>
26638+#include <linux/clk.h>
26639+#include <linux/timer.h>
26640+#include <linux/interrupt.h>
26641+#include <linux/platform_device.h>
26642+#include <sound/driver.h>
26643+#include <sound/core.h>
26644+#include <sound/pcm.h>
26645+#include <sound/soc.h>
26646+#include <sound/soc-dapm.h>
26647+
26648+#include <asm/arch/at91rm9200.h>
26649+#include <asm/arch/gpio.h>
26650+#include <asm/arch/hardware.h>
26651+
26652+#include "../codecs/wm8731.h"
26653+#include "at91rm9200-pcm.h"
26654+
26655+#if 0
26656+#define DBG(x...) printk(KERN_INFO "eti_b1_wm8731:" x)
26657+#else
26658+#define DBG(x...)
26659+#endif
26660+
26661+static struct clk *pck1_clk;
26662+static struct clk *pllb_clk;
26663+
26664+static int eti_b1_startup(snd_pcm_substream_t *substream)
26665+{
26666+ /* Start PCK1 clock. */
26667+ clk_enable(pck1_clk);
26668+ DBG("pck1 started\n");
26669+
26670+ return 0;
26671+}
26672+
26673+static void eti_b1_shutdown(snd_pcm_substream_t *substream)
26674+{
26675+ /* Stop PCK1 clock. */
26676+ clk_disable(pck1_clk);
26677+ DBG("pck1 stopped\n");
26678+}
26679+
26680+static struct snd_soc_ops eti_b1_ops = {
26681+ .startup = eti_b1_startup,
26682+ .shutdown = eti_b1_shutdown,
26683+};
26684+
26685+
26686+static const struct snd_soc_dapm_widget eti_b1_dapm_widgets[] = {
26687+ SND_SOC_DAPM_MIC("Int Mic", NULL),
26688+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
26689+};
26690+
26691+static const char *intercon[][3] = {
26692+
26693+ /* speaker connected to LHPOUT */
26694+ {"Ext Spk", NULL, "LHPOUT"},
26695+
26696+ /* mic is connected to Mic Jack, with WM8731 Mic Bias */
26697+ {"MICIN", NULL, "Mic Bias"},
26698+ {"Mic Bias", NULL, "Int Mic"},
26699+
26700+ /* terminator */
26701+ {NULL, NULL, NULL},
26702+};
26703+
26704+/*
26705+ * Logic for a wm8731 as connected on a Endrelia ETI-B1 board.
26706+ */
26707+static int eti_b1_wm8731_init(struct snd_soc_codec *codec)
26708+{
26709+ int i;
26710+
26711+ DBG("eti_b1_wm8731_init() called\n");
26712+
26713+ /* Add specific widgets */
26714+ for(i = 0; i < ARRAY_SIZE(eti_b1_dapm_widgets); i++) {
26715+ snd_soc_dapm_new_control(codec, &eti_b1_dapm_widgets[i]);
26716+ }
26717+
26718+ /* Set up specific audio path interconnects */
26719+ for(i = 0; intercon[i][0] != NULL; i++) {
26720+ snd_soc_dapm_connect_input(codec, intercon[i][0],
26721+ intercon[i][1], intercon[i][2]);
26722+ }
26723+
26724+ /* not connected */
26725+ snd_soc_dapm_set_endpoint(codec, "RLINEIN", 0);
26726+ snd_soc_dapm_set_endpoint(codec, "LLINEIN", 0);
26727+
26728+ /* always connected */
26729+ snd_soc_dapm_set_endpoint(codec, "Int Mic", 1);
26730+ snd_soc_dapm_set_endpoint(codec, "Ext Spk", 1);
26731+
26732+ snd_soc_dapm_sync_endpoints(codec);
26733+
26734+ return 0;
26735+}
26736+
26737+unsigned int eti_b1_config_sysclk(struct snd_soc_pcm_runtime *rtd,
26738+ struct snd_soc_clock_info *info)
26739+{
26740+ if(info->bclk_master & SND_SOC_DAIFMT_CBS_CFS) {
26741+ return rtd->codec_dai->config_sysclk(rtd->codec_dai, info, 12000000);
26742+ }
26743+ return 0;
26744+}
26745+
26746+static struct snd_soc_dai_link eti_b1_dai = {
26747+ .name = "WM8731",
26748+ .stream_name = "WM8731",
26749+ .cpu_dai = &at91rm9200_i2s_dai[1],
26750+ .codec_dai = &wm8731_dai,
26751+ .init = eti_b1_wm8731_init,
26752+ .config_sysclk = eti_b1_config_sysclk,
26753+};
26754+
26755+static struct snd_soc_machine snd_soc_machine_eti_b1 = {
26756+ .name = "ETI_B1",
26757+ .dai_link = &eti_b1_dai,
26758+ .num_links = 1,
26759+ .ops = &eti_b1_ops,
26760+};
26761+
26762+static struct wm8731_setup_data eti_b1_wm8731_setup = {
26763+ .i2c_address = 0x1a,
26764+};
26765+
26766+static struct snd_soc_device eti_b1_snd_devdata = {
26767+ .machine = &snd_soc_machine_eti_b1,
26768+ .platform = &at91rm9200_soc_platform,
26769+ .codec_dev = &soc_codec_dev_wm8731,
26770+ .codec_data = &eti_b1_wm8731_setup,
26771+};
26772+
26773+static struct platform_device *eti_b1_snd_device;
26774+
26775+static int __init eti_b1_init(void)
26776+{
26777+ int ret;
26778+ u32 ssc_pio_lines;
26779+
26780+ eti_b1_snd_device = platform_device_alloc("soc-audio", -1);
26781+ if (!eti_b1_snd_device)
26782+ return -ENOMEM;
26783+
26784+ platform_set_drvdata(eti_b1_snd_device, &eti_b1_snd_devdata);
26785+ eti_b1_snd_devdata.dev = &eti_b1_snd_device->dev;
26786+
26787+ ret = platform_device_add(eti_b1_snd_device);
26788+ if (ret) {
26789+ platform_device_put(eti_b1_snd_device);
26790+ return ret;
26791+ }
26792+
26793+ ssc_pio_lines = AT91_PB6_TF1 | AT91_PB7_TK1 | AT91_PB8_TD1
26794+ | AT91_PB9_RD1 /* | AT91_PB10_RK1 | AT91_PB11_RF1 */;
26795+
26796+ /* Reset all PIO registers and assign lines to peripheral A */
26797+ at91_sys_write(AT91_PIOB + PIO_PDR, ssc_pio_lines);
26798+ at91_sys_write(AT91_PIOB + PIO_ODR, ssc_pio_lines);
26799+ at91_sys_write(AT91_PIOB + PIO_IFDR, ssc_pio_lines);
26800+ at91_sys_write(AT91_PIOB + PIO_CODR, ssc_pio_lines);
26801+ at91_sys_write(AT91_PIOB + PIO_IDR, ssc_pio_lines);
26802+ at91_sys_write(AT91_PIOB + PIO_MDDR, ssc_pio_lines);
26803+ at91_sys_write(AT91_PIOB + PIO_PUDR, ssc_pio_lines);
26804+ at91_sys_write(AT91_PIOB + PIO_ASR, ssc_pio_lines);
26805+ at91_sys_write(AT91_PIOB + PIO_OWDR, ssc_pio_lines);
26806+
26807+ /*
26808+ * Set PCK1 parent to PLLB and its rate to 12 Mhz.
26809+ */
26810+ pllb_clk = clk_get(NULL, "pllb");
26811+ pck1_clk = clk_get(NULL, "pck1");
26812+
26813+ clk_set_parent(pck1_clk, pllb_clk);
26814+ clk_set_rate(pck1_clk, 12000000);
26815+
26816+ DBG("MCLK rate %luHz\n", clk_get_rate(pck1_clk));
26817+
26818+ /* assign the GPIO pin to PCK1 */
26819+ at91_set_B_periph(AT91_PIN_PA24, 0);
26820+
26821+ return ret;
26822+}
26823+
26824+static void __exit eti_b1_exit(void)
26825+{
26826+ clk_put(pck1_clk);
26827+ clk_put(pllb_clk);
26828+
26829+ platform_device_unregister(eti_b1_snd_device);
26830+}
26831+
26832+module_init(eti_b1_init);
26833+module_exit(eti_b1_exit);
26834+
26835+/* Module information */
26836+MODULE_AUTHOR("Frank Mandarino <fmandarino@endrelia.com>");
26837+MODULE_DESCRIPTION("ALSA SoC ETI-B1-WM8731");
26838+MODULE_LICENSE("GPL");
26839Index: linux-2.6-pxa-new/sound/soc/codecs/wm8510.c
26840===================================================================
26841--- /dev/null
26842+++ linux-2.6-pxa-new/sound/soc/codecs/wm8510.c
26843@@ -0,0 +1,895 @@
26844+/*
26845+ * wm8510.c -- WM8510 ALSA Soc Audio driver
26846+ *
26847+ * Copyright 2006 Wolfson Microelectronics PLC.
26848+ *
26849+ * Author: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
26850+ *
26851+ * This program is free software; you can redistribute it and/or modify
26852+ * it under the terms of the GNU General Public License version 2 as
26853+ * published by the Free Software Foundation.
26854+ */
26855+
26856+#include <linux/module.h>
26857+#include <linux/moduleparam.h>
26858+#include <linux/version.h>
26859+#include <linux/kernel.h>
26860+#include <linux/init.h>
26861+#include <linux/delay.h>
26862+#include <linux/pm.h>
26863+#include <linux/i2c.h>
26864+#include <linux/platform_device.h>
26865+#include <sound/driver.h>
26866+#include <sound/core.h>
26867+#include <sound/pcm.h>
26868+#include <sound/pcm_params.h>
26869+#include <sound/soc.h>
26870+#include <sound/soc-dapm.h>
26871+#include <sound/initval.h>
26872+
26873+#include "wm8510.h"
26874+
26875+#define AUDIO_NAME "wm8510"
26876+#define WM8510_VERSION "0.5"
26877+
26878+/*
26879+ * Debug
26880+ */
26881+
26882+#define WM8510_DEBUG 0
26883+
26884+#ifdef WM8510_DEBUG
26885+#define dbg(format, arg...) \
26886+ printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
26887+#else
26888+#define dbg(format, arg...) do {} while (0)
26889+#endif
26890+#define err(format, arg...) \
26891+ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
26892+#define info(format, arg...) \
26893+ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
26894+#define warn(format, arg...) \
26895+ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
26896+
26897+struct snd_soc_codec_device soc_codec_dev_wm8510;
26898+
26899+/*
26900+ * wm8510 register cache
26901+ * We can't read the WM8510 register space when we are
26902+ * using 2 wire for device control, so we cache them instead.
26903+ */
26904+static const u16 wm8510_reg[WM8510_CACHEREGNUM] = {
26905+ 0x0000, 0x0000, 0x0000, 0x0000,
26906+ 0x0050, 0x0000, 0x0140, 0x0000,
26907+ 0x0000, 0x0000, 0x0000, 0x00ff,
26908+ 0x0000, 0x0000, 0x0100, 0x00ff,
26909+ 0x0000, 0x0000, 0x012c, 0x002c,
26910+ 0x002c, 0x002c, 0x002c, 0x0000,
26911+ 0x0032, 0x0000, 0x0000, 0x0000,
26912+ 0x0000, 0x0000, 0x0000, 0x0000,
26913+ 0x0038, 0x000b, 0x0032, 0x0000,
26914+ 0x0008, 0x000c, 0x0093, 0x00e9,
26915+ 0x0000, 0x0000, 0x0000, 0x0000,
26916+ 0x0003, 0x0010, 0x0000, 0x0000,
26917+ 0x0000, 0x0002, 0x0000, 0x0000,
26918+ 0x0000, 0x0000, 0x0039, 0x0000,
26919+ 0x0000,
26920+};
26921+
26922+#define WM8510_DAIFMT \
26923+ (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_RIGHT_J | \
26924+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_NB_IF | SND_SOC_DAIFMT_IB_NF | \
26925+ SND_SOC_DAIFMT_IB_IF)
26926+
26927+#define WM8510_DIR \
26928+ (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
26929+
26930+#define WM8510_RATES \
26931+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
26932+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
26933+ SNDRV_PCM_RATE_48000)
26934+
26935+#define WM8794_BCLK \
26936+ (SND_SOC_FSBD(1) | SND_SOC_FSBD(2) | SND_SOC_FSBD(4) | SND_SOC_FSBD(8) |\
26937+ SND_SOC_FSBD(16) | SND_SOC_FSBD(32))
26938+
26939+#define WM8794_HIFI_BITS \
26940+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
26941+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
26942+
26943+static struct snd_soc_dai_mode wm8510_modes[] = {
26944+ /* codec frame and clock master modes */
26945+ {
26946+ .fmt = WM8510_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
26947+ .pcmfmt = WM8794_HIFI_BITS,
26948+ .pcmrate = WM8510_RATES,
26949+ .pcmdir = WM8510_DIR,
26950+ .fs = 256,
26951+ .bfs = WM8794_BCLK,
26952+ },
26953+
26954+ /* codec frame and clock slave modes */
26955+ {
26956+ .fmt = WM8510_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
26957+ .pcmfmt = WM8794_HIFI_BITS,
26958+ .pcmrate = WM8510_RATES,
26959+ .pcmdir = WM8510_DIR,
26960+ .flags = SND_SOC_DAI_BFS_DIV,
26961+ .fs = SND_SOC_FS_ALL,
26962+ .bfs = SND_SOC_FSB_ALL,
26963+ },
26964+};
26965+
26966+/*
26967+ * read wm8510 register cache
26968+ */
26969+static inline unsigned int wm8510_read_reg_cache(struct snd_soc_codec * codec,
26970+ unsigned int reg)
26971+{
26972+ u16 *cache = codec->reg_cache;
26973+ if (reg == WM8510_RESET)
26974+ return 0;
26975+ if (reg >= WM8510_CACHEREGNUM)
26976+ return -1;
26977+ return cache[reg];
26978+}
26979+
26980+/*
26981+ * write wm8510 register cache
26982+ */
26983+static inline void wm8510_write_reg_cache(struct snd_soc_codec *codec,
26984+ u16 reg, unsigned int value)
26985+{
26986+ u16 *cache = codec->reg_cache;
26987+ if (reg >= WM8510_CACHEREGNUM)
26988+ return;
26989+ cache[reg] = value;
26990+}
26991+
26992+/*
26993+ * write to the WM8510 register space
26994+ */
26995+static int wm8510_write(struct snd_soc_codec *codec, unsigned int reg,
26996+ unsigned int value)
26997+{
26998+ u8 data[2];
26999+
27000+ /* data is
27001+ * D15..D9 WM8510 register offset
27002+ * D8...D0 register data
27003+ */
27004+ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
27005+ data[1] = value & 0x00ff;
27006+
27007+ wm8510_write_reg_cache (codec, reg, value);
27008+ if (codec->hw_write(codec->control_data, data, 2) == 2)
27009+ return 0;
27010+ else
27011+ return -EIO;
27012+}
27013+
27014+#define wm8510_reset(c) wm8510_write(c, WM8510_RESET, 0)
27015+
27016+static const char *wm8510_companding[] = {"Off", "NC", "u-law", "A-law" };
27017+static const char *wm8510_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz" };
27018+static const char *wm8510_alc[] = {"ALC", "Limiter" };
27019+
27020+static const struct soc_enum wm8510_enum[] = {
27021+ SOC_ENUM_SINGLE(WM8510_COMP, 1, 4, wm8510_companding), /* adc */
27022+ SOC_ENUM_SINGLE(WM8510_COMP, 3, 4, wm8510_companding), /* dac */
27023+ SOC_ENUM_SINGLE(WM8510_DAC, 4, 4, wm8510_deemp),
27024+ SOC_ENUM_SINGLE(WM8510_ALC3, 8, 2, wm8510_alc),
27025+};
27026+
27027+static const struct snd_kcontrol_new wm8510_snd_controls[] = {
27028+
27029+SOC_SINGLE("Digital Loopback Switch", WM8510_COMP, 0, 1, 0),
27030+
27031+SOC_ENUM("DAC Companding", wm8510_enum[1]),
27032+SOC_ENUM("ADC Companding", wm8510_enum[0]),
27033+
27034+SOC_ENUM("Playback De-emphasis", wm8510_enum[2]),
27035+SOC_SINGLE("DAC Inversion Switch", WM8510_DAC, 0, 1, 0),
27036+
27037+SOC_SINGLE("Master Playback Volume", WM8510_DACVOL, 0, 127, 0),
27038+
27039+SOC_SINGLE("High Pass Filter Switch", WM8510_ADC, 8, 1, 0),
27040+SOC_SINGLE("High Pass Cut Off", WM8510_ADC, 4, 7, 0),
27041+SOC_SINGLE("ADC Inversion Switch", WM8510_COMP, 0, 1, 0),
27042+
27043+SOC_SINGLE("Capture Volume", WM8510_ADCVOL, 0, 127, 0),
27044+
27045+SOC_SINGLE("DAC Playback Limiter Switch", WM8510_DACLIM1, 8, 1, 0),
27046+SOC_SINGLE("DAC Playback Limiter Decay", WM8510_DACLIM1, 4, 15, 0),
27047+SOC_SINGLE("DAC Playback Limiter Attack", WM8510_DACLIM1, 0, 15, 0),
27048+
27049+SOC_SINGLE("DAC Playback Limiter Threshold", WM8510_DACLIM2, 4, 7, 0),
27050+SOC_SINGLE("DAC Playback Limiter Boost", WM8510_DACLIM2, 0, 15, 0),
27051+
27052+SOC_SINGLE("ALC Enable Switch", WM8510_ALC1, 8, 1, 0),
27053+SOC_SINGLE("ALC Capture Max Gain", WM8510_ALC1, 3, 7, 0),
27054+SOC_SINGLE("ALC Capture Min Gain", WM8510_ALC1, 0, 7, 0),
27055+
27056+SOC_SINGLE("ALC Capture ZC Switch", WM8510_ALC2, 8, 1, 0),
27057+SOC_SINGLE("ALC Capture Hold", WM8510_ALC2, 4, 7, 0),
27058+SOC_SINGLE("ALC Capture Target", WM8510_ALC2, 0, 15, 0),
27059+
27060+SOC_ENUM("ALC Capture Mode", wm8510_enum[3]),
27061+SOC_SINGLE("ALC Capture Decay", WM8510_ALC3, 4, 15, 0),
27062+SOC_SINGLE("ALC Capture Attack", WM8510_ALC3, 0, 15, 0),
27063+
27064+SOC_SINGLE("ALC Capture Noise Gate Switch", WM8510_NGATE, 3, 1, 0),
27065+SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8510_NGATE, 0, 7, 0),
27066+
27067+SOC_SINGLE("Capture PGA ZC Switch", WM8510_INPPGA, 7, 1, 0),
27068+SOC_SINGLE("Capture PGA Volume", WM8510_INPPGA, 0, 63, 0),
27069+
27070+SOC_SINGLE("Speaker Playback ZC Switch", WM8510_SPKVOL, 7, 1, 0),
27071+SOC_SINGLE("Speaker Playback Switch", WM8510_SPKVOL, 6, 1, 1),
27072+SOC_SINGLE("Speaker Playback Volume", WM8510_SPKVOL, 0, 63, 0),
27073+
27074+SOC_SINGLE("Capture Boost(+20dB)", WM8510_ADCBOOST, 8, 1, 0),
27075+SOC_SINGLE("Mono Playback Switch", WM8510_MONOMIX, 6, 1, 0),
27076+};
27077+
27078+/* add non dapm controls */
27079+static int wm8510_add_controls(struct snd_soc_codec *codec)
27080+{
27081+ int err, i;
27082+
27083+ for (i = 0; i < ARRAY_SIZE(wm8510_snd_controls); i++) {
27084+ err = snd_ctl_add(codec->card,
27085+ snd_soc_cnew(&wm8510_snd_controls[i],codec, NULL));
27086+ if (err < 0)
27087+ return err;
27088+ }
27089+
27090+ return 0;
27091+}
27092+
27093+/* Speaker Output Mixer */
27094+static const struct snd_kcontrol_new wm8510_speaker_mixer_controls[] = {
27095+SOC_DAPM_SINGLE("Line Bypass Switch", WM8510_SPKMIX, 1, 1, 0),
27096+SOC_DAPM_SINGLE("Aux Playback Switch", WM8510_SPKMIX, 5, 1, 0),
27097+SOC_DAPM_SINGLE("PCM Playback Switch", WM8510_SPKMIX, 0, 1, 1),
27098+};
27099+
27100+/* Mono Output Mixer */
27101+static const struct snd_kcontrol_new wm8510_mono_mixer_controls[] = {
27102+SOC_DAPM_SINGLE("Line Bypass Switch", WM8510_MONOMIX, 1, 1, 0),
27103+SOC_DAPM_SINGLE("Aux Playback Switch", WM8510_MONOMIX, 2, 1, 0),
27104+SOC_DAPM_SINGLE("PCM Playback Switch", WM8510_MONOMIX, 0, 1, 1),
27105+};
27106+
27107+/* AUX Input boost vol */
27108+static const struct snd_kcontrol_new wm8510_aux_boost_controls =
27109+SOC_DAPM_SINGLE("Aux Volume", WM8510_ADCBOOST, 0, 7, 0);
27110+
27111+/* Mic Input boost vol */
27112+static const struct snd_kcontrol_new wm8510_mic_boost_controls =
27113+SOC_DAPM_SINGLE("Mic Volume", WM8510_ADCBOOST, 4, 7, 0);
27114+
27115+/* Capture boost switch */
27116+static const struct snd_kcontrol_new wm8510_capture_boost_controls =
27117+SOC_DAPM_SINGLE("Capture Boost Switch", WM8510_INPPGA, 6, 1, 0);
27118+
27119+/* Aux In to PGA */
27120+static const struct snd_kcontrol_new wm8510_aux_capture_boost_controls =
27121+SOC_DAPM_SINGLE("Aux Capture Boost Switch", WM8510_INPPGA, 2, 1, 0);
27122+
27123+/* Mic P In to PGA */
27124+static const struct snd_kcontrol_new wm8510_micp_capture_boost_controls =
27125+SOC_DAPM_SINGLE("Mic P Capture Boost Switch", WM8510_INPPGA, 0, 1, 0);
27126+
27127+/* Mic N In to PGA */
27128+static const struct snd_kcontrol_new wm8510_micn_capture_boost_controls =
27129+SOC_DAPM_SINGLE("Mic N Capture Boost Switch", WM8510_INPPGA, 1, 1, 0);
27130+
27131+static const struct snd_soc_dapm_widget wm8510_dapm_widgets[] = {
27132+SND_SOC_DAPM_MIXER("Speaker Mixer", WM8510_POWER3, 2, 0,
27133+ &wm8510_speaker_mixer_controls[0],
27134+ ARRAY_SIZE(wm8510_speaker_mixer_controls)),
27135+SND_SOC_DAPM_MIXER("Mono Mixer", WM8510_POWER3, 3, 0,
27136+ &wm8510_mono_mixer_controls[0],
27137+ ARRAY_SIZE(wm8510_mono_mixer_controls)),
27138+SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8510_POWER3, 0, 0),
27139+SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8510_POWER3, 0, 0),
27140+SND_SOC_DAPM_PGA("Aux Input", WM8510_POWER1, 6, 0, NULL, 0),
27141+SND_SOC_DAPM_PGA("SpkN Out", WM8510_POWER3, 5, 0, NULL, 0),
27142+SND_SOC_DAPM_PGA("SpkP Out", WM8510_POWER3, 6, 0, NULL, 0),
27143+SND_SOC_DAPM_PGA("Mono Out", WM8510_POWER3, 7, 0, NULL, 0),
27144+SND_SOC_DAPM_PGA("Mic PGA", WM8510_POWER2, 2, 0, NULL, 0),
27145+
27146+SND_SOC_DAPM_PGA("Aux Boost", SND_SOC_NOPM, 0, 0,
27147+ &wm8510_aux_boost_controls, 1),
27148+SND_SOC_DAPM_PGA("Mic Boost", SND_SOC_NOPM, 0, 0,
27149+ &wm8510_mic_boost_controls, 1),
27150+SND_SOC_DAPM_SWITCH("Capture Boost", SND_SOC_NOPM, 0, 0,
27151+ &wm8510_capture_boost_controls),
27152+
27153+SND_SOC_DAPM_MIXER("Boost Mixer", WM8510_POWER2, 4, 0, NULL, 0),
27154+
27155+SND_SOC_DAPM_MICBIAS("Mic Bias", WM8510_POWER1, 4, 0),
27156+
27157+SND_SOC_DAPM_INPUT("MICN"),
27158+SND_SOC_DAPM_INPUT("MICP"),
27159+SND_SOC_DAPM_INPUT("AUX"),
27160+SND_SOC_DAPM_OUTPUT("MONOOUT"),
27161+SND_SOC_DAPM_OUTPUT("SPKOUTP"),
27162+SND_SOC_DAPM_OUTPUT("SPKOUTN"),
27163+};
27164+
27165+static const char *audio_map[][3] = {
27166+ /* Mono output mixer */
27167+ {"Mono Mixer", "PCM Playback Switch", "DAC"},
27168+ {"Mono Mixer", "Aux Playback Switch", "Aux Input"},
27169+ {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
27170+
27171+ /* Speaker output mixer */
27172+ {"Speaker Mixer", "PCM Playback Switch", "DAC"},
27173+ {"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
27174+ {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
27175+
27176+ /* Outputs */
27177+ {"Mono Out", NULL, "Mono Mixer"},
27178+ {"MONOOUT", NULL, "Mono Out"},
27179+ {"SpkN Out", NULL, "Speaker Mixer"},
27180+ {"SpkP Out", NULL, "Speaker Mixer"},
27181+ {"SPKOUTN", NULL, "SpkN Out"},
27182+ {"SPKOUTP", NULL, "SpkP Out"},
27183+
27184+ /* Boost Mixer */
27185+ {"Boost Mixer", NULL, "ADC"},
27186+ {"Capture Boost Switch", "Aux Capture Boost Switch", "AUX"},
27187+ {"Aux Boost", "Aux Volume", "Boost Mixer"},
27188+ {"Capture Boost", "Capture Switch", "Boost Mixer"},
27189+ {"Mic Boost", "Mic Volume", "Boost Mixer"},
27190+
27191+ /* Inputs */
27192+ {"MICP", NULL, "Mic Boost"},
27193+ {"MICN", NULL, "Mic PGA"},
27194+ {"Mic PGA", NULL, "Capture Boost"},
27195+ {"AUX", NULL, "Aux Input"},
27196+
27197+ /* terminator */
27198+ {NULL, NULL, NULL},
27199+};
27200+
27201+static int wm8510_add_widgets(struct snd_soc_codec *codec)
27202+{
27203+ int i;
27204+
27205+ for(i = 0; i < ARRAY_SIZE(wm8510_dapm_widgets); i++) {
27206+ snd_soc_dapm_new_control(codec, &wm8510_dapm_widgets[i]);
27207+ }
27208+
27209+ /* set up audio path audio_mapnects */
27210+ for(i = 0; audio_map[i][0] != NULL; i++) {
27211+ snd_soc_dapm_connect_input(codec, audio_map[i][0],
27212+ audio_map[i][1], audio_map[i][2]);
27213+ }
27214+
27215+ snd_soc_dapm_new_widgets(codec);
27216+ return 0;
27217+}
27218+
27219+struct pll_ {
27220+ unsigned int in_hz, out_hz;
27221+ unsigned int pre:4; /* prescale - 1 */
27222+ unsigned int n:4;
27223+ unsigned int k;
27224+};
27225+
27226+struct pll_ pll[] = {
27227+ {12000000, 11289600, 0, 7, 0x86c220},
27228+ {12000000, 12288000, 0, 8, 0x3126e8},
27229+ {13000000, 11289600, 0, 6, 0xf28bd4},
27230+ {13000000, 12288000, 0, 7, 0x8fd525},
27231+ {12288000, 11289600, 0, 7, 0x59999a},
27232+ {11289600, 12288000, 0, 8, 0x80dee9},
27233+ /* liam - add more entries */
27234+};
27235+
27236+static int set_pll(struct snd_soc_codec *codec, unsigned int in,
27237+ unsigned int out)
27238+{
27239+ int i;
27240+ u16 reg;
27241+
27242+ if(out == 0) {
27243+ reg = wm8510_read_reg_cache(codec, WM8510_POWER1);
27244+ wm8510_write(codec, WM8510_POWER1, reg & 0x1df);
27245+ return 0;
27246+ }
27247+
27248+ for(i = 0; i < ARRAY_SIZE(pll); i++) {
27249+ if (in == pll[i].in_hz && out == pll[i].out_hz) {
27250+ wm8510_write(codec, WM8510_PLLN, (pll[i].pre << 4) | pll[i].n);
27251+ wm8510_write(codec, WM8510_PLLK1, pll[i].k >> 18);
27252+ wm8510_write(codec, WM8510_PLLK1, (pll[i].k >> 9) && 0x1ff);
27253+ wm8510_write(codec, WM8510_PLLK1, pll[i].k && 0x1ff);
27254+ reg = wm8510_read_reg_cache(codec, WM8510_POWER1);
27255+ wm8510_write(codec, WM8510_POWER1, reg | 0x020);
27256+ return 0;
27257+ }
27258+ }
27259+ return -EINVAL;
27260+}
27261+
27262+/* mclk dividers * 2 */
27263+static unsigned char mclk_div[] = {2, 3, 4, 6, 8, 12, 16, 24};
27264+
27265+/* we need 256FS to drive the DAC's and ADC's */
27266+static unsigned int wm8510_config_sysclk(struct snd_soc_codec_dai *dai,
27267+ struct snd_soc_clock_info *info, unsigned int clk)
27268+{
27269+ int i, j, best_clk = info->fs * info->rate;
27270+
27271+ /* can we run at this clk without the PLL ? */
27272+ for (i = 0; i < ARRAY_SIZE(mclk_div); i++) {
27273+ if ((best_clk >> 1) * mclk_div[i] == clk) {
27274+ dai->pll_in = 0;
27275+ dai->clk_div = mclk_div[i];
27276+ dai->mclk = best_clk;
27277+ return dai->mclk;
27278+ }
27279+ }
27280+
27281+ /* now check for PLL support */
27282+ for (i = 0; i < ARRAY_SIZE(pll); i++) {
27283+ if (pll[i].in_hz == clk) {
27284+ for (j = 0; j < ARRAY_SIZE(mclk_div); j++) {
27285+ if (pll[i].out_hz == mclk_div[j] * (best_clk >> 1)) {
27286+ dai->pll_in = clk;
27287+ dai->pll_out = pll[i].out_hz;
27288+ dai->clk_div = mclk_div[j];
27289+ dai->mclk = best_clk;
27290+ return dai->mclk;
27291+ }
27292+ }
27293+ }
27294+ }
27295+
27296+ /* this clk is not supported */
27297+ return 0;
27298+}
27299+
27300+static int wm8510_pcm_prepare(struct snd_pcm_substream *substream)
27301+{
27302+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
27303+ struct snd_soc_device *socdev = rtd->socdev;
27304+ struct snd_soc_codec *codec = socdev->codec;
27305+ struct snd_soc_codec_dai *dai = rtd->codec_dai;
27306+ u16 iface = 0, bfs, clk = 0, adn;
27307+ int fs = 48000 << 7, i;
27308+
27309+ bfs = SND_SOC_FSBD_REAL(rtd->codec_dai->dai_runtime.bfs);
27310+ switch (bfs) {
27311+ case 2:
27312+ clk |= 0x1 << 2;
27313+ break;
27314+ case 4:
27315+ clk |= 0x2 << 2;
27316+ break;
27317+ case 8:
27318+ clk |= 0x3 << 2;
27319+ break;
27320+ case 16:
27321+ clk |= 0x4 << 2;
27322+ break;
27323+ case 32:
27324+ clk |= 0x5 << 2;
27325+ break;
27326+ }
27327+
27328+ /* set master/slave audio interface */
27329+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
27330+ case SND_SOC_DAIFMT_CBM_CFM:
27331+ clk |= 0x0001;
27332+ break;
27333+ case SND_SOC_DAIFMT_CBS_CFS:
27334+ break;
27335+ }
27336+
27337+ /* interface format */
27338+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
27339+ case SND_SOC_DAIFMT_I2S:
27340+ iface |= 0x0010;
27341+ break;
27342+ case SND_SOC_DAIFMT_RIGHT_J:
27343+ break;
27344+ case SND_SOC_DAIFMT_LEFT_J:
27345+ iface |= 0x0008;
27346+ break;
27347+ case SND_SOC_DAIFMT_DSP_A:
27348+ iface |= 0x00018;
27349+ break;
27350+ }
27351+
27352+ /* bit size */
27353+ switch (rtd->codec_dai->dai_runtime.pcmfmt) {
27354+ case SNDRV_PCM_FMTBIT_S16_LE:
27355+ break;
27356+ case SNDRV_PCM_FMTBIT_S20_3LE:
27357+ iface |= 0x0020;
27358+ break;
27359+ case SNDRV_PCM_FMTBIT_S24_LE:
27360+ iface |= 0x0040;
27361+ break;
27362+ case SNDRV_PCM_FMTBIT_S32_LE:
27363+ iface |= 0x0060;
27364+ break;
27365+ }
27366+
27367+ /* clock inversion */
27368+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_INV_MASK) {
27369+ case SND_SOC_DAIFMT_NB_NF:
27370+ break;
27371+ case SND_SOC_DAIFMT_IB_IF:
27372+ iface |= 0x0180;
27373+ break;
27374+ case SND_SOC_DAIFMT_IB_NF:
27375+ iface |= 0x0100;
27376+ break;
27377+ case SND_SOC_DAIFMT_NB_IF:
27378+ iface |= 0x0080;
27379+ break;
27380+ }
27381+
27382+ /* filter coefficient */
27383+ adn = wm8510_read_reg_cache(codec, WM8510_ADD) & 0x1f1;
27384+ switch (rtd->codec_dai->dai_runtime.pcmrate) {
27385+ case SNDRV_PCM_RATE_8000:
27386+ adn |= 0x5 << 1;
27387+ fs = 8000 << 7;
27388+ break;
27389+ case SNDRV_PCM_RATE_11025:
27390+ adn |= 0x4 << 1;
27391+ fs = 11025 << 7;
27392+ break;
27393+ case SNDRV_PCM_RATE_16000:
27394+ adn |= 0x3 << 1;
27395+ fs = 16000 << 7;
27396+ break;
27397+ case SNDRV_PCM_RATE_22050:
27398+ adn |= 0x2 << 1;
27399+ fs = 22050 << 7;
27400+ break;
27401+ case SNDRV_PCM_RATE_32000:
27402+ adn |= 0x1 << 1;
27403+ fs = 32000 << 7;
27404+ break;
27405+ case SNDRV_PCM_RATE_44100:
27406+ fs = 44100 << 7;
27407+ break;
27408+ }
27409+
27410+ /* do we need to enable the PLL */
27411+ if(dai->pll_in)
27412+ set_pll(codec, dai->pll_in, dai->pll_out);
27413+
27414+ /* divide the clock to 256 fs */
27415+ for(i = 0; i < ARRAY_SIZE(mclk_div); i++) {
27416+ if (dai->clk_div == mclk_div[i]) {
27417+ clk |= i << 5;
27418+ clk &= 0xff;
27419+ goto set;
27420+ }
27421+ }
27422+
27423+set:
27424+ /* set iface */
27425+ wm8510_write(codec, WM8510_IFACE, iface);
27426+ wm8510_write(codec, WM8510_CLOCK, clk);
27427+
27428+ return 0;
27429+}
27430+
27431+static int wm8510_hw_free(struct snd_pcm_substream *substream)
27432+{
27433+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
27434+ struct snd_soc_device *socdev = rtd->socdev;
27435+ struct snd_soc_codec *codec = socdev->codec;
27436+ set_pll(codec, 0, 0);
27437+ return 0;
27438+}
27439+
27440+static int wm8510_mute(struct snd_soc_codec *codec,
27441+ struct snd_soc_codec_dai *dai, int mute)
27442+{
27443+ u16 mute_reg = wm8510_read_reg_cache(codec, WM8510_DAC) & 0xffbf;
27444+ if(mute)
27445+ wm8510_write(codec, WM8510_DAC, mute_reg | 0x40);
27446+ else
27447+ wm8510_write(codec, WM8510_DAC, mute_reg);
27448+ return 0;
27449+}
27450+
27451+/* liam need to make this lower power with dapm */
27452+static int wm8510_dapm_event(struct snd_soc_codec *codec, int event)
27453+{
27454+
27455+ switch (event) {
27456+ case SNDRV_CTL_POWER_D0: /* full On */
27457+ /* vref/mid, clk and osc on, dac unmute, active */
27458+ wm8510_write(codec, WM8510_POWER1, 0x1ff);
27459+ wm8510_write(codec, WM8510_POWER2, 0x1ff);
27460+ wm8510_write(codec, WM8510_POWER3, 0x1ff);
27461+ break;
27462+ case SNDRV_CTL_POWER_D1: /* partial On */
27463+ case SNDRV_CTL_POWER_D2: /* partial On */
27464+ break;
27465+ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
27466+ /* everything off except vref/vmid, dac mute, inactive */
27467+
27468+ break;
27469+ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
27470+ /* everything off, dac mute, inactive */
27471+ wm8510_write(codec, WM8510_POWER1, 0x0);
27472+ wm8510_write(codec, WM8510_POWER2, 0x0);
27473+ wm8510_write(codec, WM8510_POWER3, 0x0);
27474+ break;
27475+ }
27476+ codec->dapm_state = event;
27477+ return 0;
27478+}
27479+
27480+struct snd_soc_codec_dai wm8510_dai = {
27481+ .name = "WM8510 HiFi",
27482+ .playback = {
27483+ .stream_name = "Playback",
27484+ .channels_min = 1,
27485+ .channels_max = 1,
27486+ },
27487+ .capture = {
27488+ .stream_name = "Capture",
27489+ .channels_min = 1,
27490+ .channels_max = 1,
27491+ },
27492+ .config_sysclk = wm8510_config_sysclk,
27493+ .digital_mute = wm8510_mute,
27494+ .ops = {
27495+ .prepare = wm8510_pcm_prepare,
27496+ .hw_free = wm8510_hw_free,
27497+ },
27498+ .caps = {
27499+ .num_modes = ARRAY_SIZE(wm8510_modes),
27500+ .mode = wm8510_modes,
27501+ },
27502+};
27503+EXPORT_SYMBOL_GPL(wm8510_dai);
27504+
27505+static int wm8510_suspend(struct platform_device *pdev, pm_message_t state)
27506+{
27507+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
27508+ struct snd_soc_codec *codec = socdev->codec;
27509+
27510+ wm8510_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
27511+ return 0;
27512+}
27513+
27514+static int wm8510_resume(struct platform_device *pdev)
27515+{
27516+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
27517+ struct snd_soc_codec *codec = socdev->codec;
27518+ int i;
27519+ u8 data[2];
27520+ u16 *cache = codec->reg_cache;
27521+
27522+ /* Sync reg_cache with the hardware */
27523+ for (i = 0; i < ARRAY_SIZE(wm8510_reg); i++) {
27524+ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
27525+ data[1] = cache[i] & 0x00ff;
27526+ codec->hw_write(codec->control_data, data, 2);
27527+ }
27528+ wm8510_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
27529+ wm8510_dapm_event(codec, codec->suspend_dapm_state);
27530+ return 0;
27531+}
27532+
27533+/*
27534+ * initialise the WM8510 driver
27535+ * register the mixer and dsp interfaces with the kernel
27536+ */
27537+static int wm8510_init(struct snd_soc_device *socdev)
27538+{
27539+ struct snd_soc_codec *codec = socdev->codec;
27540+ int ret = 0;
27541+
27542+ codec->name = "WM8510";
27543+ codec->owner = THIS_MODULE;
27544+ codec->read = wm8510_read_reg_cache;
27545+ codec->write = wm8510_write;
27546+ codec->dapm_event = wm8510_dapm_event;
27547+ codec->dai = &wm8510_dai;
27548+ codec->num_dai = 1;
27549+ codec->reg_cache_size = ARRAY_SIZE(wm8510_reg);
27550+ codec->reg_cache =
27551+ kzalloc(sizeof(u16) * ARRAY_SIZE(wm8510_reg), GFP_KERNEL);
27552+ if (codec->reg_cache == NULL)
27553+ return -ENOMEM;
27554+ memcpy(codec->reg_cache, wm8510_reg,
27555+ sizeof(u16) * ARRAY_SIZE(wm8510_reg));
27556+ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm8510_reg);
27557+
27558+ wm8510_reset(codec);
27559+
27560+ /* register pcms */
27561+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
27562+ if(ret < 0) {
27563+ kfree(codec->reg_cache);
27564+ return ret;
27565+ }
27566+
27567+ /* power on device */
27568+ wm8510_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
27569+ wm8510_add_controls(codec);
27570+ wm8510_add_widgets(codec);
27571+ ret = snd_soc_register_card(socdev);
27572+ if(ret < 0) {
27573+ snd_soc_free_pcms(socdev);
27574+ snd_soc_dapm_free(socdev);
27575+ }
27576+
27577+ return ret;
27578+}
27579+
27580+static struct snd_soc_device *wm8510_socdev;
27581+
27582+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
27583+
27584+/*
27585+ * WM8510 2 wire address is 0x1a
27586+ */
27587+#define I2C_DRIVERID_WM8510 0xfefe /* liam - need a proper id */
27588+
27589+static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
27590+
27591+/* Magic definition of all other variables and things */
27592+I2C_CLIENT_INSMOD;
27593+
27594+static struct i2c_driver wm8510_i2c_driver;
27595+static struct i2c_client client_template;
27596+
27597+/* If the i2c layer weren't so broken, we could pass this kind of data
27598+ around */
27599+
27600+static int wm8510_codec_probe(struct i2c_adapter *adap, int addr, int kind)
27601+{
27602+ struct snd_soc_device *socdev = wm8510_socdev;
27603+ struct wm8510_setup_data *setup = socdev->codec_data;
27604+ struct snd_soc_codec *codec = socdev->codec;
27605+ struct i2c_client *i2c;
27606+ int ret;
27607+
27608+ if (addr != setup->i2c_address)
27609+ return -ENODEV;
27610+
27611+ client_template.adapter = adap;
27612+ client_template.addr = addr;
27613+
27614+ i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
27615+ if (i2c == NULL){
27616+ kfree(codec);
27617+ return -ENOMEM;
27618+ }
27619+ memcpy(i2c, &client_template, sizeof(struct i2c_client));
27620+ i2c_set_clientdata(i2c, codec);
27621+ codec->control_data = i2c;
27622+
27623+ ret = i2c_attach_client(i2c);
27624+ if(ret < 0) {
27625+ err("failed to attach codec at addr %x\n", addr);
27626+ goto err;
27627+ }
27628+
27629+ ret = wm8510_init(socdev);
27630+ if(ret < 0) {
27631+ err("failed to initialise WM8510\n");
27632+ goto err;
27633+ }
27634+ return ret;
27635+
27636+err:
27637+ kfree(codec);
27638+ kfree(i2c);
27639+ return ret;
27640+}
27641+
27642+static int wm8510_i2c_detach(struct i2c_client *client)
27643+{
27644+ struct snd_soc_codec *codec = i2c_get_clientdata(client);
27645+ i2c_detach_client(client);
27646+ kfree(codec->reg_cache);
27647+ kfree(client);
27648+ return 0;
27649+}
27650+
27651+static int wm8510_i2c_attach(struct i2c_adapter *adap)
27652+{
27653+ return i2c_probe(adap, &addr_data, wm8510_codec_probe);
27654+}
27655+
27656+/* corgi i2c codec control layer */
27657+static struct i2c_driver wm8510_i2c_driver = {
27658+ .driver = {
27659+ .name = "WM8510 I2C Codec",
27660+ .owner = THIS_MODULE,
27661+ },
27662+ .id = I2C_DRIVERID_WM8510,
27663+ .attach_adapter = wm8510_i2c_attach,
27664+ .detach_client = wm8510_i2c_detach,
27665+ .command = NULL,
27666+};
27667+
27668+static struct i2c_client client_template = {
27669+ .name = "WM8510",
27670+ .driver = &wm8510_i2c_driver,
27671+};
27672+#endif
27673+
27674+static int wm8510_probe(struct platform_device *pdev)
27675+{
27676+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
27677+ struct wm8510_setup_data *setup;
27678+ struct snd_soc_codec *codec;
27679+ int ret = 0;
27680+
27681+ info("WM8510 Audio Codec %s", WM8510_VERSION);
27682+
27683+ setup = socdev->codec_data;
27684+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
27685+ if (codec == NULL)
27686+ return -ENOMEM;
27687+
27688+ socdev->codec = codec;
27689+ mutex_init(&codec->mutex);
27690+ INIT_LIST_HEAD(&codec->dapm_widgets);
27691+ INIT_LIST_HEAD(&codec->dapm_paths);
27692+
27693+ wm8510_socdev = socdev;
27694+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
27695+ if (setup->i2c_address) {
27696+ normal_i2c[0] = setup->i2c_address;
27697+ codec->hw_write = (hw_write_t)i2c_master_send;
27698+ ret = i2c_add_driver(&wm8510_i2c_driver);
27699+ if (ret != 0)
27700+ printk(KERN_ERR "can't add i2c driver");
27701+ }
27702+#else
27703+ /* Add other interfaces here */
27704+#endif
27705+ return ret;
27706+}
27707+
27708+/* power down chip */
27709+static int wm8510_remove(struct platform_device *pdev)
27710+{
27711+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
27712+ struct snd_soc_codec *codec = socdev->codec;
27713+
27714+ if (codec->control_data)
27715+ wm8510_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
27716+
27717+ snd_soc_free_pcms(socdev);
27718+ snd_soc_dapm_free(socdev);
27719+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
27720+ i2c_del_driver(&wm8510_i2c_driver);
27721+#endif
27722+ kfree(codec);
27723+
27724+ return 0;
27725+}
27726+
27727+struct snd_soc_codec_device soc_codec_dev_wm8510 = {
27728+ .probe = wm8510_probe,
27729+ .remove = wm8510_remove,
27730+ .suspend = wm8510_suspend,
27731+ .resume = wm8510_resume,
27732+};
27733+
27734+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8510);
27735+
27736+MODULE_DESCRIPTION("ASoC WM8510 driver");
27737+MODULE_AUTHOR("Liam Girdwood");
27738+MODULE_LICENSE("GPL");
27739Index: linux-2.6-pxa-new/sound/soc/codecs/wm8510.h
27740===================================================================
27741--- /dev/null
27742+++ linux-2.6-pxa-new/sound/soc/codecs/wm8510.h
27743@@ -0,0 +1,64 @@
27744+/*
27745+ * wm8510.h -- WM8510 Soc Audio driver
27746+ *
27747+ * This program is free software; you can redistribute it and/or modify
27748+ * it under the terms of the GNU General Public License version 2 as
27749+ * published by the Free Software Foundation.
27750+ */
27751+
27752+#ifndef _WM8510_H
27753+#define _WM8510_H
27754+
27755+/* WM8510 register space */
27756+
27757+#define WM8510_RESET 0x0
27758+#define WM8510_POWER1 0x1
27759+#define WM8510_POWER2 0x2
27760+#define WM8510_POWER3 0x3
27761+#define WM8510_IFACE 0x4
27762+#define WM8510_COMP 0x5
27763+#define WM8510_CLOCK 0x6
27764+#define WM8510_ADD 0x7
27765+#define WM8510_GPIO 0x8
27766+#define WM8510_DAC 0xa
27767+#define WM8510_DACVOL 0xb
27768+#define WM8510_ADC 0xe
27769+#define WM8510_ADCVOL 0xf
27770+#define WM8510_EQ1 0x12
27771+#define WM8510_EQ2 0x13
27772+#define WM8510_EQ3 0x14
27773+#define WM8510_EQ4 0x15
27774+#define WM8510_EQ5 0x16
27775+#define WM8510_DACLIM1 0x18
27776+#define WM8510_DACLIM2 0x19
27777+#define WM8510_NOTCH1 0x1b
27778+#define WM8510_NOTCH2 0x1c
27779+#define WM8510_NOTCH3 0x1d
27780+#define WM8510_NOTCH4 0x1e
27781+#define WM8510_ALC1 0x20
27782+#define WM8510_ALC2 0x21
27783+#define WM8510_ALC3 0x22
27784+#define WM8510_NGATE 0x23
27785+#define WM8510_PLLN 0x24
27786+#define WM8510_PLLK1 0x25
27787+#define WM8510_PLLK2 0x26
27788+#define WM8510_PLLK3 0x27
27789+#define WM8510_ATTEN 0x28
27790+#define WM8510_INPUT 0x2c
27791+#define WM8510_INPPGA 0x2d
27792+#define WM8510_ADCBOOST 0x2f
27793+#define WM8510_OUTPUT 0x31
27794+#define WM8510_SPKMIX 0x32
27795+#define WM8510_SPKVOL 0x36
27796+#define WM8510_MONOMIX 0x38
27797+
27798+#define WM8510_CACHEREGNUM 57
27799+
27800+struct wm8510_setup_data {
27801+ unsigned short i2c_address;
27802+};
27803+
27804+extern struct snd_soc_codec_dai wm8510_dai;
27805+extern struct snd_soc_codec_device soc_codec_dev_wm8510;
27806+
27807+#endif
27808Index: linux-2.6-pxa-new/sound/soc/imx/imx-ac97.c
27809===================================================================
27810--- /dev/null
27811+++ linux-2.6-pxa-new/sound/soc/imx/imx-ac97.c
27812@@ -0,0 +1,281 @@
27813+/*
27814+ * imx-ssi.c -- SSI driver for Freescale IMX
27815+ *
27816+ * Copyright 2006 Wolfson Microelectronics PLC.
27817+ * Author: Liam Girdwood
27818+ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
27819+ *
27820+ * Based on mxc-alsa-mc13783 (C) 2006 Freescale.
27821+ *
27822+ * This program is free software; you can redistribute it and/or modify it
27823+ * under the terms of the GNU General Public License as published by the
27824+ * Free Software Foundation; either version 2 of the License, or (at your
27825+ * option) any later version.
27826+ *
27827+ * Revision history
27828+ * 29th Aug 2006 Initial version.
27829+ *
27830+ */
27831+
27832+#define IMX_AC97_RATES \
27833+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
27834+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
27835+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
27836+ SNDRV_PCM_RATE_48000)
27837+
27838+/* may need to expand this */
27839+static struct snd_soc_dai_mode imx_ssi_ac97_modes[] = {
27840+ {0, 0, SNDRV_PCM_FMTBIT_S16_LE, IMX_AC97_RATES},
27841+ {0, 0, SNDRV_PCM_FMTBIT_S18_3LE, IMX_AC97_RATES},
27842+ {0, 0, SNDRV_PCM_FMTBIT_S20_3LE, IMX_AC97_RATES},
27843+};
27844+
27845+static imx_pcm_dma_params_t imx_ssi1_pcm_stereo_out = {
27846+ .name = "SSI1 PCM Stereo out",
27847+ .params = {
27848+ .bd_number = 1,
27849+ .transfer_type = emi_2_per,
27850+ .watermark_level = SDMA_TXFIFO_WATERMARK,
27851+ .word_size = TRANSFER_16BIT, // maybe add this in setup func
27852+ .per_address = SSI1_STX0,
27853+ .event_id = DMA_REQ_SSI1_TX1,
27854+ .peripheral_type = SSI,
27855+ },
27856+};
27857+
27858+static imx_pcm_dma_params_t imx_ssi1_pcm_stereo_in = {
27859+ .name = "SSI1 PCM Stereo in",
27860+ .params = {
27861+ .bd_number = 1,
27862+ .transfer_type = per_2_emi,
27863+ .watermark_level = SDMA_RXFIFO_WATERMARK,
27864+ .word_size = TRANSFER_16BIT, // maybe add this in setup func
27865+ .per_address = SSI1_SRX0,
27866+ .event_id = DMA_REQ_SSI1_RX1,
27867+ .peripheral_type = SSI,
27868+ },
27869+};
27870+
27871+static imx_pcm_dma_params_t imx_ssi2_pcm_stereo_out = {
27872+ .name = "SSI2 PCM Stereo out",
27873+ .params = {
27874+ .bd_number = 1,
27875+ .transfer_type = per_2_emi,
27876+ .watermark_level = SDMA_TXFIFO_WATERMARK,
27877+ .word_size = TRANSFER_16BIT, // maybe add this in setup func
27878+ .per_address = SSI2_STX0,
27879+ .event_id = DMA_REQ_SSI2_TX1,
27880+ .peripheral_type = SSI,
27881+ },
27882+};
27883+
27884+static imx_pcm_dma_params_t imx_ssi2_pcm_stereo_in = {
27885+ .name = "SSI2 PCM Stereo in",
27886+ .params = {
27887+ .bd_number = 1,
27888+ .transfer_type = per_2_emi,
27889+ .watermark_level = SDMA_RXFIFO_WATERMARK,
27890+ .word_size = TRANSFER_16BIT, // maybe add this in setup func
27891+ .per_address = SSI2_SRX0,
27892+ .event_id = DMA_REQ_SSI2_RX1,
27893+ .peripheral_type = SSI,
27894+ },
27895+};
27896+
27897+static unsigned short imx_ssi_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
27898+{
27899+}
27900+
27901+static void imx_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val)
27902+{
27903+}
27904+
27905+static void imx_ssi_ac97_warm_reset(struct snd_ac97 *ac97)
27906+{
27907+}
27908+
27909+static void imx_ssi_ac97_cold_reset(struct snd_ac97 *ac97)
27910+{
27911+}
27912+
27913+struct snd_ac97_bus_ops soc_ac97_ops = {
27914+ .read = imx_ssi_ac97_read,
27915+ .write = imx_ssi_ac97_write,
27916+ .warm_reset = imx_ssi_ac97_warm_reset,
27917+ .reset = imx_ssi_ac97_cold_reset,
27918+};
27919+
27920+
27921+static intimx_ssi1_ac97_probe(struct platform_device *pdev)
27922+{
27923+ int ret;
27924+
27925+
27926+ return ret;
27927+}
27928+
27929+static void imx_ssi1_ac97_remove(struct platform_device *pdev)
27930+{
27931+ /* shutdown SSI */
27932+ if(rtd->cpu_dai->id == 0)
27933+ SSI1_SCR &= ~SSI_SCR_SSIEN;
27934+ else
27935+ SSI2_SCR &= ~SSI_SCR_SSIEN;
27936+ }
27937+
27938+}
27939+
27940+static int imx_ssi1_ac97_prepare(struct snd_pcm_substream *substream)
27941+{
27942+ // set vra
27943+}
27944+
27945+static int imx_ssi_startup(struct snd_pcm_substream *substream)
27946+{
27947+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
27948+
27949+ if (!rtd->cpu_dai->active) {
27950+
27951+ }
27952+
27953+ return 0;
27954+}
27955+
27956+static int imx_ssi1_trigger(struct snd_pcm_substream *substream, int cmd)
27957+{
27958+ int ret = 0;
27959+
27960+ switch (cmd) {
27961+ case SNDRV_PCM_TRIGGER_START:
27962+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
27963+ SSI1_SCR |= SSI_SCR_TE;
27964+ SSI1_SIER |= SSI_SIER_TDMAE;
27965+ } else {
27966+ SSI1_SCR |= SSI_SCR_RE;
27967+ SSI1_SIER |= SSI_SIER_RDMAE;
27968+ }
27969+ SSI1_SCR |= SSI_SCR_SSIEN;
27970+
27971+ break;
27972+ case SNDRV_PCM_TRIGGER_RESUME:
27973+ break;
27974+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
27975+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
27976+ SSI1_SCR |= SSI_SCR_TE;
27977+ else
27978+ SSI1_SCR |= SSI_SCR_RE;
27979+ break
27980+ case SNDRV_PCM_TRIGGER_STOP:
27981+ case SNDRV_PCM_TRIGGER_SUSPEND:
27982+ break;
27983+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
27984+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
27985+ SSI1_SCR &= ~SSI_SCR_TE;
27986+ else
27987+ SSI1_SCR &= ~SSI_SCR_RE;
27988+ break;
27989+ default:
27990+ ret = -EINVAL;
27991+ }
27992+
27993+ return ret;
27994+}
27995+
27996+static void imx_ssi_shutdown(struct snd_pcm_substream *substream)
27997+{
27998+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
27999+
28000+
28001+}
28002+
28003+#ifdef CONFIG_PM
28004+static int imx_ssi_suspend(struct platform_device *dev,
28005+ struct snd_soc_cpu_dai *dai)
28006+{
28007+ if(!dai->active)
28008+ return 0;
28009+
28010+ if(rtd->cpu_dai->id == 0)
28011+ SSI1_SCR &= ~SSI_SCR_SSIEN;
28012+ else
28013+ SSI2_SCR &= ~SSI_SCR_SSIEN;
28014+
28015+ return 0;
28016+}
28017+
28018+static int imx_ssi_resume(struct platform_device *pdev,
28019+ struct snd_soc_cpu_dai *dai)
28020+{
28021+ if(!dai->active)
28022+ return 0;
28023+
28024+ if(rtd->cpu_dai->id == 0)
28025+ SSI1_SCR |= SSI_SCR_SSIEN;
28026+ else
28027+ SSI2_SCR |= SSI_SCR_SSIEN;
28028+
28029+ return 0;
28030+}
28031+
28032+#else
28033+#define imx_ssi_suspend NULL
28034+#define imx_ssi_resume NULL
28035+#endif
28036+
28037+static unsigned int imx_ssi_config_ac97_sysclk(struct snd_soc_cpu_dai *iface,
28038+ struct snd_soc_clock_info *info, unsigned int clk)
28039+{
28040+ return clk;
28041+}
28042+
28043+struct snd_soc_cpu_dai imx_ssi_ac97_dai = {
28044+ .name = "imx-ac97-1",
28045+ .id = 0,
28046+ .type = SND_SOC_DAI_AC97,
28047+ .suspend = imx_ssi_suspend,
28048+ .resume = imx_ssi_resume,
28049+ .config_sysclk = imx_ssi_ac97_config_sysclk,
28050+ .playback = {
28051+ .channels_min = 2,
28052+ .channels_max = 2,},
28053+ .capture = {
28054+ .channels_min = 2,
28055+ .channels_max = 2,},
28056+ .ops = {
28057+ .probe = imx_ac97_probe,
28058+ .remove = imx_ac97_shutdown,
28059+ .trigger = imx_ssi1_trigger,
28060+ .prepare = imx_ssi_ac97_prepare,},
28061+ .caps = {
28062+ .num_modes = ARRAY_SIZE(imx_ssi_ac97_modes),
28063+ .mode = imx_ssi_ac97_modes,},
28064+},
28065+{
28066+ .name = "imx-ac97-2",
28067+ .id = 1,
28068+ .type = SND_SOC_DAI_AC97,
28069+ .suspend = imx_ssi_suspend,
28070+ .resume = imx_ssi_resume,
28071+ .config_sysclk = imx_ssi_ac97_config_sysclk,
28072+ .playback = {
28073+ .channels_min = 2,
28074+ .channels_max = 2,},
28075+ .capture = {
28076+ .channels_min = 2,
28077+ .channels_max = 2,},
28078+ .ops = {
28079+ .probe = imx_ac97_probe,
28080+ .remove = imx_ac97_shutdown,
28081+ .trigger = imx_ssi1_trigger,
28082+ .prepare = imx_ssi_ac97_prepare,},
28083+ .caps = {
28084+ .num_modes = ARRAY_SIZE(imx_ssi_ac97_modes),
28085+ .mode = imx_ssi_ac97_modes,},
28086+};
28087+
28088+EXPORT_SYMBOL_GPL(imx_ssi_ac97_dai);
28089+
28090+/* Module information */
28091+MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
28092+MODULE_DESCRIPTION("i.MX ASoC AC97 driver");
28093+MODULE_LICENSE("GPL");
28094Index: linux-2.6-pxa-new/sound/soc/imx/imx-i2s.c
28095===================================================================
28096--- /dev/null
28097+++ linux-2.6-pxa-new/sound/soc/imx/imx-i2s.c
28098@@ -0,0 +1,473 @@
28099+/*
28100+ * imx-ssi.c -- SSI driver for Freescale IMX
28101+ *
28102+ * Copyright 2006 Wolfson Microelectronics PLC.
28103+ * Author: Liam Girdwood
28104+ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
28105+ *
28106+ * Based on mxc-alsa-mc13783 (C) 2006 Freescale.
28107+ *
28108+ * This program is free software; you can redistribute it and/or modify it
28109+ * under the terms of the GNU General Public License as published by the
28110+ * Free Software Foundation; either version 2 of the License, or (at your
28111+ * option) any later version.
28112+ *
28113+ * Revision history
28114+ * 29th Aug 2006 Initial version.
28115+ *
28116+ */
28117+
28118+#define IMX_SSI_DAIFMT \
28119+ (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J |\
28120+ SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_DSP__A |\
28121+ SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBS_CFS |\
28122+ SND_SOC_DAIFMT_CBM_CFS | SND_SOC_DAIFMT_CBS_CFM |\
28123+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_NB_IF)
28124+
28125+#define IMX_SSI_DIR \
28126+ (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
28127+
28128+#define IMX_SSI_RATES \
28129+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | \
28130+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
28131+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
28132+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \
28133+ SNDRV_PCM_RATE_96000)
28134+
28135+#define IMX_SSI_BITS \
28136+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
28137+ SNDRV_PCM_FMTBIT_S24_LE)
28138+
28139+static struct snd_soc_dai_mode imx_ssi_pcm_modes[] = {
28140+
28141+ /* frame master and clock slave mode */
28142+ {
28143+ .fmt = IMX_SSI_DAIFMT | SND_SOC_DAIFMT_CBM_CFS,
28144+ .tdm = SND_SOC_DAITDM_LRDW(0,0),
28145+ .pcmfmt = IMX_SSI_BITS,
28146+ .pcmrate = IMX_SSI_RATES,
28147+ .pcmdir = IMX_SSI_DIR,
28148+ .flags = SND_SOC_DAI_BFS_RCW,
28149+ .fs = SND_SOC_FS_ALL,
28150+ .bfs = SND_SOC_FSBW(1) | SND_SOC_FSBW(2),
28151+ },
28152+};
28153+
28154+static imx_pcm_dma_params_t imx_ssi1_pcm_stereo_out = {
28155+ .name = "SSI1 PCM Stereo out",
28156+ .params = {
28157+ .bd_number = 1,
28158+ .transfer_type = emi_2_per,
28159+ .watermark_level = SDMA_TXFIFO_WATERMARK,
28160+ .word_size = TRANSFER_16BIT, // maybe add this in setup func
28161+ .per_address = SSI1_STX0,
28162+ .event_id = DMA_REQ_SSI1_TX1,
28163+ .peripheral_type = SSI,
28164+ },
28165+};
28166+
28167+static imx_pcm_dma_params_t imx_ssi1_pcm_stereo_in = {
28168+ .name = "SSI1 PCM Stereo in",
28169+ .params = {
28170+ .bd_number = 1,
28171+ .transfer_type = per_2_emi,
28172+ .watermark_level = SDMA_RXFIFO_WATERMARK,
28173+ .word_size = TRANSFER_16BIT, // maybe add this in setup func
28174+ .per_address = SSI1_SRX0,
28175+ .event_id = DMA_REQ_SSI1_RX1,
28176+ .peripheral_type = SSI,
28177+ },
28178+};
28179+
28180+static imx_pcm_dma_params_t imx_ssi2_pcm_stereo_out = {
28181+ .name = "SSI2 PCM Stereo out",
28182+ .params = {
28183+ .bd_number = 1,
28184+ .transfer_type = per_2_emi,
28185+ .watermark_level = SDMA_TXFIFO_WATERMARK,
28186+ .word_size = TRANSFER_16BIT, // maybe add this in setup func
28187+ .per_address = SSI2_STX0,
28188+ .event_id = DMA_REQ_SSI2_TX1,
28189+ .peripheral_type = SSI,
28190+ },
28191+};
28192+
28193+static imx_pcm_dma_params_t imx_ssi2_pcm_stereo_in = {
28194+ .name = "SSI2 PCM Stereo in",
28195+ .params = {
28196+ .bd_number = 1,
28197+ .transfer_type = per_2_emi,
28198+ .watermark_level = SDMA_RXFIFO_WATERMARK,
28199+ .word_size = TRANSFER_16BIT, // maybe add this in setup func
28200+ .per_address = SSI2_SRX0,
28201+ .event_id = DMA_REQ_SSI2_RX1,
28202+ .peripheral_type = SSI,
28203+ },
28204+};
28205+
28206+
28207+static int imx_ssi_startup(struct snd_pcm_substream *substream)
28208+{
28209+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
28210+
28211+ if (!rtd->cpu_dai->active) {
28212+
28213+ }
28214+
28215+ return 0;
28216+}
28217+
28218+static int imx_ssi1_hw_tx_params(struct snd_pcm_substream *substream,
28219+ struct snd_pcm_hw_params *params)
28220+{
28221+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
28222+ struct snd_soc_device *socdev = rtd->socdev;
28223+ struct snd_soc_codec *codec = socdev->codec;
28224+ u16 bfs, div;
28225+
28226+ bfs = SND_SOC_FSBD_REAL(rtd->cpu_dai->dai_runtime.bfs);
28227+
28228+ SSI1_STCR = 0;
28229+ SSI1_STCCR = 0;
28230+
28231+ /* DAI mode */
28232+ switch(rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
28233+ case SND_SOC_DAIFMT_I2S:
28234+ SSI1_STCR |= SSI_STCR_TSCKP | SSI_STCR_TFSI |
28235+ SSI_STCR_TEFS | SSI_STCR_TXBIT0;
28236+ break;
28237+ case SND_SOC_DAIFMT_LEFT_J:
28238+ SSI1_STCR |= SSI_STCR_TSCKP | SSI_STCR_TFSI | SSI_STCR_TXBIT0;
28239+ break;
28240+ case SND_SOC_DAIFMT_DSP_B:
28241+ SSI1_STCR |= SSI_STCR_TEFS; // data 1 bit after sync
28242+ case SND_SOC_DAIFMT_DSP_A:
28243+ SSI1_STCR |= SSI_STCR_TFSL; // frame is 1 bclk long
28244+
28245+ /* DAI clock inversion */
28246+ switch(rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_INV_MASK) {
28247+ case SND_SOC_DAIFMT_IB_IF:
28248+ SSI1_STCR |= SSI_STCR_TFSI | SSI_STCR_TSCKP;
28249+ break;
28250+ case SND_SOC_DAIFMT_IB_NF:
28251+ SSI1_STCR |= SSI_STCR_TSCKP;
28252+ break;
28253+ case SND_SOC_DAIFMT_NB_IF:
28254+ SSI1_STCR |= SSI_STCR_TFSI;
28255+ break;
28256+ }
28257+ break;
28258+ }
28259+
28260+ /* DAI data (word) size */
28261+ switch(rtd->codec_dai->dai_runtime.pcmfmt) {
28262+ case SNDRV_PCM_FMTBIT_S16_LE:
28263+ SSI1_STCCR |= SSI_STCCR_WL(16);
28264+ break;
28265+ case SNDRV_PCM_FMTBIT_S20_3LE:
28266+ SSI1_STCCR |= SSI_STCCR_WL(20);
28267+ break;
28268+ case SNDRV_PCM_FMTBIT_S24_LE:
28269+ SSI1_STCCR |= SSI_STCCR_WL(24);
28270+ break;
28271+ }
28272+
28273+ /* DAI clock master masks */
28274+ switch(rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CLOCK_MASK){
28275+ case SND_SOC_DAIFMT_CBM_CFM:
28276+ SSI1_STCR |= SSI_STCR_TFDIR | SSI_STCR_TXDIR;
28277+ break;
28278+ case SND_SOC_DAIFMT_CBS_CFM:
28279+ SSI1_STCR |= SSI_STCR_TFDIR;
28280+ break;
28281+ case SND_SOC_DAIFMT_CBM_CFS:
28282+ SSI1_STCR |= SSI_STCR_TXDIR;
28283+ break;
28284+ }
28285+
28286+ /* DAI BCLK ratio to SYSCLK / MCLK */
28287+ /* prescaler modulus - todo */
28288+ switch (bfs) {
28289+ case 2:
28290+ break;
28291+ case 4:
28292+ break;
28293+ case 8:
28294+ break;
28295+ case 16:
28296+ break;
28297+ }
28298+
28299+ /* TDM - todo, only fifo 0 atm */
28300+ SSI1_STCR |= SSI_STCR_TFEN0;
28301+ SSI1_STCCR |= SSI_STCCR_DC(params_channels(params));
28302+
28303+ return 0;
28304+}
28305+
28306+static int imx_ssi1_hw_rx_params(struct snd_pcm_substream *substream,
28307+ struct snd_pcm_hw_params *params)
28308+{
28309+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
28310+ struct snd_soc_device *socdev = rtd->socdev;
28311+ struct snd_soc_codec *codec = socdev->codec;
28312+ u16 bfs, div;
28313+
28314+ bfs = SND_SOC_FSBD_REAL(rtd->cpu_dai->dai_runtime.bfs);
28315+
28316+ SSI1_SRCR = 0;
28317+ SSI1_SRCCR = 0;
28318+
28319+ /* DAI mode */
28320+ switch(rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
28321+ case SND_SOC_DAIFMT_I2S:
28322+ SSI1_SRCR |= SSI_SRCR_RSCKP | SSI_SRCR_RFSI |
28323+ SSI_STCR_REFS | SSI_STCR_RXBIT0;
28324+ break;
28325+ case SND_SOC_DAIFMT_LEFT_J:
28326+ SSI1_SRCR |= SSI_SRCR_RSCKP | SSI_SRCR_RFSI | SSI_SRCR_RXBIT0;
28327+ break;
28328+ case SND_SOC_DAIFMT_DSP_B:
28329+ SSI1_SRCR |= SSI_SRCR_REFS; // data 1 bit after sync
28330+ case SND_SOC_DAIFMT_DSP_A:
28331+ SSI1_SRCR |= SSI_SRCR_RFSL; // frame is 1 bclk long
28332+
28333+ /* DAI clock inversion */
28334+ switch(rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_INV_MASK) {
28335+ case SND_SOC_DAIFMT_IB_IF:
28336+ SSI1_SRCR |= SSI_SRCR_TFSI | SSI_SRCR_TSCKP;
28337+ break;
28338+ case SND_SOC_DAIFMT_IB_NF:
28339+ SSI1_SRCR |= SSI_SRCR_RSCKP;
28340+ break;
28341+ case SND_SOC_DAIFMT_NB_IF:
28342+ SSI1_SRCR |= SSI_SRCR_RFSI;
28343+ break;
28344+ }
28345+ break;
28346+ }
28347+
28348+ /* DAI data (word) size */
28349+ switch(rtd->codec_dai->dai_runtime.pcmfmt) {
28350+ case SNDRV_PCM_FMTBIT_S16_LE:
28351+ SSI1_SRCCR |= SSI_SRCCR_WL(16);
28352+ break;
28353+ case SNDRV_PCM_FMTBIT_S20_3LE:
28354+ SSI1_SRCCR |= SSI_SRCCR_WL(20);
28355+ break;
28356+ case SNDRV_PCM_FMTBIT_S24_LE:
28357+ SSI1_SRCCR |= SSI_SRCCR_WL(24);
28358+ break;
28359+ }
28360+
28361+ /* DAI clock master masks */
28362+ switch(rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CLOCK_MASK){
28363+ case SND_SOC_DAIFMT_CBM_CFM:
28364+ SSI1_SRCR |= SSI_SRCR_RFDIR | SSI_SRCR_RXDIR;
28365+ break;
28366+ case SND_SOC_DAIFMT_CBS_CFM:
28367+ SSI1_SRCR |= SSI_SRCR_RFDIR;
28368+ break;
28369+ case SND_SOC_DAIFMT_CBM_CFS:
28370+ SSI1_SRCR |= SSI_SRCR_RXDIR;
28371+ break;
28372+ }
28373+
28374+ /* DAI BCLK ratio to SYSCLK / MCLK */
28375+ /* prescaler modulus - todo */
28376+ switch (bfs) {
28377+ case 2:
28378+ break;
28379+ case 4:
28380+ break;
28381+ case 8:
28382+ break;
28383+ case 16:
28384+ break;
28385+ }
28386+
28387+ /* TDM - todo, only fifo 0 atm */
28388+ SSI1_SRCR |= SSI_SRCR_RFEN0;
28389+ SSI1_SRCCR |= SSI_SRCCR_DC(params_channels(params));
28390+
28391+ return 0;
28392+}
28393+
28394+static int imx_ssi1_hw_params(struct snd_pcm_substream *substream,
28395+ struct snd_pcm_hw_params *params)
28396+{
28397+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
28398+
28399+ /* clear register if not enabled */
28400+ if(!(SSI1_SCR & SSI_SCR_SSIEN))
28401+ SSI1_SCR = 0;
28402+
28403+ /* async */
28404+ if (rtd->cpu_dai->flags & SND_SOC_DAI_ASYNC)
28405+ SSI1_SCR |= SSI_SCR_SYN;
28406+
28407+ /* DAI mode */
28408+ switch(rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
28409+ case SND_SOC_DAIFMT_I2S:
28410+ case SND_SOC_DAIFMT_LEFT_J:
28411+ SSI1_SCR |= SSI_SCR_NET;
28412+ break;
28413+ }
28414+
28415+ /* TDM - to complete */
28416+
28417+ /* Tx/Rx config */
28418+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
28419+ return imx_ssi1_hw_tx_params(substream, params);
28420+ } else {
28421+ return imx_ssi1_hw_rx_params(substream, params);
28422+ }
28423+}
28424+
28425+
28426+
28427+static int imx_ssi1_trigger(struct snd_pcm_substream *substream, int cmd)
28428+{
28429+ int ret = 0;
28430+
28431+ switch (cmd) {
28432+ case SNDRV_PCM_TRIGGER_START:
28433+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
28434+ SSI1_SCR |= SSI_SCR_TE;
28435+ SSI1_SIER |= SSI_SIER_TDMAE;
28436+ } else {
28437+ SSI1_SCR |= SSI_SCR_RE;
28438+ SSI1_SIER |= SSI_SIER_RDMAE;
28439+ }
28440+ SSI1_SCR |= SSI_SCR_SSIEN;
28441+
28442+ break;
28443+ case SNDRV_PCM_TRIGGER_RESUME:
28444+ break;
28445+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
28446+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
28447+ SSI1_SCR |= SSI_SCR_TE;
28448+ else
28449+ SSI1_SCR |= SSI_SCR_RE;
28450+ break
28451+ case SNDRV_PCM_TRIGGER_STOP:
28452+ case SNDRV_PCM_TRIGGER_SUSPEND:
28453+ break;
28454+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
28455+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
28456+ SSI1_SCR &= ~SSI_SCR_TE;
28457+ else
28458+ SSI1_SCR &= ~SSI_SCR_RE;
28459+ break;
28460+ default:
28461+ ret = -EINVAL;
28462+ }
28463+
28464+ return ret;
28465+}
28466+
28467+static void imx_ssi_shutdown(struct snd_pcm_substream *substream)
28468+{
28469+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
28470+
28471+ /* shutdown SSI */
28472+ if (!rtd->cpu_dai->active) {
28473+ if(rtd->cpu_dai->id == 0)
28474+ SSI1_SCR &= ~SSI_SCR_SSIEN;
28475+ else
28476+ SSI2_SCR &= ~SSI_SCR_SSIEN;
28477+ }
28478+}
28479+
28480+#ifdef CONFIG_PM
28481+static int imx_ssi_suspend(struct platform_device *dev,
28482+ struct snd_soc_cpu_dai *dai)
28483+{
28484+ if(!dai->active)
28485+ return 0;
28486+
28487+ if(rtd->cpu_dai->id == 0)
28488+ SSI1_SCR &= ~SSI_SCR_SSIEN;
28489+ else
28490+ SSI2_SCR &= ~SSI_SCR_SSIEN;
28491+
28492+ return 0;
28493+}
28494+
28495+static int imx_ssi_resume(struct platform_device *pdev,
28496+ struct snd_soc_cpu_dai *dai)
28497+{
28498+ if(!dai->active)
28499+ return 0;
28500+
28501+ if(rtd->cpu_dai->id == 0)
28502+ SSI1_SCR |= SSI_SCR_SSIEN;
28503+ else
28504+ SSI2_SCR |= SSI_SCR_SSIEN;
28505+
28506+ return 0;
28507+}
28508+
28509+#else
28510+#define imx_ssi_suspend NULL
28511+#define imx_ssi_resume NULL
28512+#endif
28513+
28514+static unsigned int imx_ssi_config_pcm_sysclk(struct snd_soc_cpu_dai *iface,
28515+ struct snd_soc_clock_info *info, unsigned int clk)
28516+{
28517+ return clk;
28518+}
28519+
28520+struct snd_soc_cpu_dai imx_ssi_pcm_dai = {
28521+ .name = "imx-i2s-1",
28522+ .id = 0,
28523+ .type = SND_SOC_DAI_I2S,
28524+ .suspend = imx_ssi_suspend,
28525+ .resume = imx_ssi_resume,
28526+ .config_sysclk = imx_ssi_config_pcm_sysclk,
28527+ .playback = {
28528+ .channels_min = 1,
28529+ .channels_max = 2,},
28530+ .capture = {
28531+ .channels_min = 1,
28532+ .channels_max = 2,},
28533+ .ops = {
28534+ .startup = imx_ssi_startup,
28535+ .shutdown = imx_ssi_shutdown,
28536+ .trigger = imx_ssi1_trigger,
28537+ .hw_params = imx_ssi1_pcm_hw_params,},
28538+ .caps = {
28539+ .num_modes = ARRAY_SIZE(imx_ssi_modes),
28540+ .mode = imx_ssi_modes,},
28541+},
28542+{
28543+ .name = "imx-i2s-2",
28544+ .id = 1,
28545+ .type = SND_SOC_DAI_I2S,
28546+ .suspend = imx_ssi_suspend,
28547+ .resume = imx_ssi_resume,
28548+ .config_sysclk = imx_ssi_config_pcm_sysclk,
28549+ .playback = {
28550+ .channels_min = 1,
28551+ .channels_max = 2,},
28552+ .capture = {
28553+ .channels_min = 1,
28554+ .channels_max = 2,},
28555+ .ops = {
28556+ .startup = imx_ssi_startup,
28557+ .shutdown = imx_ssi_shutdown,
28558+ .trigger = imx_ssi1_trigger,
28559+ .hw_params = imx_ssi1_pcm_hw_params,},
28560+ .caps = {
28561+ .num_modes = ARRAY_SIZE(imx_ssi_modes),
28562+ .mode = imx_ssi_modes,},
28563+};
28564+
28565+
28566+EXPORT_SYMBOL_GPL(imx_ssi_i2s_dai);
28567+
28568+/* Module information */
28569+MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
28570+MODULE_DESCRIPTION("i.MX ASoC I2S driver");
28571+MODULE_LICENSE("GPL");
28572Index: linux-2.6-pxa-new/include/linux/i2c-id.h
28573===================================================================
28574--- linux-2.6.17/include/linux/i2c-id.h.orig 2006-11-25 00:15:33.571185017 +0100
28575+++ linux-2.6.17/include/linux/i2c-id.h 2006-11-25 00:17:31.017877925 +0100
28576@@ -113,6 +113,9 @@
28577 #define I2C_DRIVERID_PCF8563 83 /* Philips PCF8563 RTC */
28578 #define I2C_DRIVERID_RS5C372 84 /* Ricoh RS5C372 RTC */
28579
28580+#define I2C_DRIVERID_WM8731 89 /* Wolfson WM8731 audio codec */
28581+#define I2C_DRIVERID_WM8750 90 /* Wolfson WM8750 audio codec */
28582+
28583 #define I2C_DRIVERID_I2CDEV 900
28584 #define I2C_DRIVERID_ARP 902 /* SMBus ARP Client */
28585 #define I2C_DRIVERID_ALERT 903 /* SMBus Alert Responder Client */
28586Index: linux-2.6-pxa-new/sound/soc/codecs/wm8976.c
28587===================================================================
28588--- /dev/null
28589+++ linux-2.6-pxa-new/sound/soc/codecs/wm8976.c
28590@@ -0,0 +1,953 @@
28591+/*
28592+ * wm8976.c -- WM8976 ALSA Soc Audio driver
28593+ *
28594+ * Copyright 2006 Wolfson Microelectronics PLC.
28595+ *
28596+ * This program is free software; you can redistribute it and/or modify
28597+ * it under the terms of the GNU General Public License version 2 as
28598+ * published by the Free Software Foundation.
28599+ */
28600+
28601+#include <linux/module.h>
28602+#include <linux/moduleparam.h>
28603+#include <linux/version.h>
28604+#include <linux/kernel.h>
28605+#include <linux/init.h>
28606+#include <linux/delay.h>
28607+#include <linux/pm.h>
28608+#include <linux/i2c.h>
28609+#include <linux/platform_device.h>
28610+#include <sound/driver.h>
28611+#include <sound/core.h>
28612+#include <sound/pcm.h>
28613+#include <sound/pcm_params.h>
28614+#include <sound/soc.h>
28615+#include <sound/soc-dapm.h>
28616+#include <sound/initval.h>
28617+
28618+#include "wm8976.h"
28619+
28620+#define AUDIO_NAME "wm8976"
28621+#define WM8976_VERSION "0.2"
28622+
28623+/*
28624+ * Debug
28625+ */
28626+
28627+#define WM8976_DEBUG 0
28628+
28629+#ifdef WM8976_DEBUG
28630+#define dbg(format, arg...) \
28631+ printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
28632+#else
28633+#define dbg(format, arg...) do {} while (0)
28634+#endif
28635+#define err(format, arg...) \
28636+ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
28637+#define info(format, arg...) \
28638+ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
28639+#define warn(format, arg...) \
28640+ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
28641+
28642+struct snd_soc_codec_device soc_codec_dev_wm8976;
28643+
28644+/*
28645+ * wm8976 register cache
28646+ * We can't read the WM8976 register space when we are
28647+ * using 2 wire for device control, so we cache them instead.
28648+ */
28649+static const u16 wm8976_reg[WM8976_CACHEREGNUM] = {
28650+ 0x0000, 0x0000, 0x0000, 0x0000,
28651+ 0x0050, 0x0000, 0x0140, 0x0000,
28652+ 0x0000, 0x0000, 0x0000, 0x00ff,
28653+ 0x00ff, 0x0000, 0x0100, 0x00ff,
28654+ 0x00ff, 0x0000, 0x012c, 0x002c,
28655+ 0x002c, 0x002c, 0x002c, 0x0000,
28656+ 0x0032, 0x0000, 0x0000, 0x0000,
28657+ 0x0000, 0x0000, 0x0000, 0x0000,
28658+ 0x0038, 0x000b, 0x0032, 0x0000,
28659+ 0x0008, 0x000c, 0x0093, 0x00e9,
28660+ 0x0000, 0x0000, 0x0000, 0x0000,
28661+ 0x0033, 0x0010, 0x0010, 0x0100,
28662+ 0x0100, 0x0002, 0x0001, 0x0001,
28663+ 0x0039, 0x0039, 0x0039, 0x0039,
28664+ 0x0001, 0x0001,
28665+};
28666+
28667+#define WM8976_DAIFMT \
28668+ (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_RIGHT_J | \
28669+ SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | \
28670+ SND_SOC_DAIFMT_NB_IF | SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_IB_IF)
28671+
28672+#define WM8976_DIR \
28673+ (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
28674+
28675+#define WM8976_RATES \
28676+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
28677+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
28678+ SNDRV_PCM_RATE_48000)
28679+
28680+#define WM8976_PCM_FORMATS \
28681+ (SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \
28682+ SNDRV_PCM_FORMAT_S24_3LE | SNDRV_PCM_FORMAT_S24_LE | \
28683+ SNDRV_PCM_FORMAT_S32_LE)
28684+
28685+#define WM8976_BCLK \
28686+ (SND_SOC_FSBD(1) | SND_SOC_FSBD(2) | SND_SOC_FSBD(4) | SND_SOC_FSBD(8) |\
28687+ SND_SOC_FSBD(16) | SND_SOC_FSBD(32))
28688+
28689+static struct snd_soc_dai_mode wm8976_modes[] = {
28690+ /* codec frame and clock master modes */
28691+ {
28692+ .fmt = WM8976_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
28693+ .pcmfmt = WM8976_PCM_FORMATS,
28694+ .pcmrate = WM8976_RATES,
28695+ .pcmdir = WM8976_DIR,
28696+ .flags = SND_SOC_DAI_BFS_DIV,
28697+ .fs = 256,
28698+ .bfs = WM8976_BCLK,
28699+ },
28700+
28701+ /* codec frame and clock slave modes */
28702+ {
28703+ .fmt = WM8976_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
28704+ .pcmfmt = WM8976_PCM_FORMATS,
28705+ .pcmrate = WM8976_RATES,
28706+ .pcmdir = WM8976_DIR,
28707+ .fs = SND_SOC_FS_ALL,
28708+ .bfs = SND_SOC_FSB_ALL,
28709+ },
28710+};
28711+
28712+/*
28713+ * read wm8976 register cache
28714+ */
28715+static inline unsigned int wm8976_read_reg_cache(struct snd_soc_codec *codec,
28716+ unsigned int reg)
28717+{
28718+ u16 *cache = codec->reg_cache;
28719+ if (reg == WM8976_RESET)
28720+ return 0;
28721+ if (reg >= WM8976_CACHEREGNUM)
28722+ return -1;
28723+ return cache[reg];
28724+}
28725+
28726+/*
28727+ * write wm8976 register cache
28728+ */
28729+static inline void wm8976_write_reg_cache(struct snd_soc_codec *codec,
28730+ u16 reg, unsigned int value)
28731+{
28732+ u16 *cache = codec->reg_cache;
28733+ if (reg >= WM8976_CACHEREGNUM)
28734+ return;
28735+ cache[reg] = value;
28736+}
28737+
28738+/*
28739+ * write to the WM8976 register space
28740+ */
28741+static int wm8976_write(struct snd_soc_codec *codec, unsigned int reg,
28742+ unsigned int value)
28743+{
28744+ u8 data[2];
28745+
28746+ /* data is
28747+ * D15..D9 WM8976 register offset
28748+ * D8...D0 register data
28749+ */
28750+ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
28751+ data[1] = value & 0x00ff;
28752+
28753+ wm8976_write_reg_cache (codec, reg, value);
28754+ if (codec->hw_write(codec->control_data, data, 2) == 2)
28755+ return 0;
28756+ else
28757+ return -1;
28758+}
28759+
28760+#define wm8976_reset(c) wm8976_write(c, WM8976_RESET, 0)
28761+
28762+static const char *wm8976_companding[] = {"Off", "NC", "u-law", "A-law" };
28763+static const char *wm8976_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz" };
28764+static const char *wm8976_eqmode[] = {"Capture", "Playback" };
28765+static const char *wm8976_bw[] = {"Narrow", "Wide" };
28766+static const char *wm8976_eq1[] = {"80Hz", "105Hz", "135Hz", "175Hz" };
28767+static const char *wm8976_eq2[] = {"230Hz", "300Hz", "385Hz", "500Hz" };
28768+static const char *wm8976_eq3[] = {"650Hz", "850Hz", "1.1kHz", "1.4kHz" };
28769+static const char *wm8976_eq4[] = {"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz" };
28770+static const char *wm8976_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz" };
28771+static const char *wm8976_alc[] =
28772+ {"ALC both on", "ALC left only", "ALC right only", "Limiter" };
28773+
28774+static const struct soc_enum wm8976_enum[] = {
28775+ SOC_ENUM_SINGLE(WM8976_COMP, 1, 4, wm8976_companding), /* adc */
28776+ SOC_ENUM_SINGLE(WM8976_COMP, 3, 4, wm8976_companding), /* dac */
28777+ SOC_ENUM_SINGLE(WM8976_DAC, 4, 4, wm8976_deemp),
28778+ SOC_ENUM_SINGLE(WM8976_EQ1, 8, 2, wm8976_eqmode),
28779+
28780+ SOC_ENUM_SINGLE(WM8976_EQ1, 5, 4, wm8976_eq1),
28781+ SOC_ENUM_SINGLE(WM8976_EQ2, 8, 2, wm8976_bw),
28782+ SOC_ENUM_SINGLE(WM8976_EQ2, 5, 4, wm8976_eq2),
28783+ SOC_ENUM_SINGLE(WM8976_EQ3, 8, 2, wm8976_bw),
28784+
28785+ SOC_ENUM_SINGLE(WM8976_EQ3, 5, 4, wm8976_eq3),
28786+ SOC_ENUM_SINGLE(WM8976_EQ4, 8, 2, wm8976_bw),
28787+ SOC_ENUM_SINGLE(WM8976_EQ4, 5, 4, wm8976_eq4),
28788+ SOC_ENUM_SINGLE(WM8976_EQ5, 8, 2, wm8976_bw),
28789+
28790+ SOC_ENUM_SINGLE(WM8976_EQ5, 5, 4, wm8976_eq5),
28791+ SOC_ENUM_SINGLE(WM8976_ALC3, 8, 2, wm8976_alc),
28792+};
28793+
28794+static const struct snd_kcontrol_new wm8976_snd_controls[] = {
28795+SOC_SINGLE("Digital Loopback Switch", WM8976_COMP, 0, 1, 0),
28796+
28797+SOC_ENUM("ADC Companding", wm8976_enum[0]),
28798+SOC_ENUM("DAC Companding", wm8976_enum[1]),
28799+
28800+SOC_SINGLE("Jack Detection Enable", WM8976_JACK1, 6, 1, 0),
28801+
28802+SOC_DOUBLE("DAC Inversion Switch", WM8976_DAC, 0, 1, 1, 0),
28803+
28804+SOC_DOUBLE_R("Headphone Playback Volume", WM8976_DACVOLL, WM8976_DACVOLR, 0, 127, 0),
28805+
28806+SOC_SINGLE("High Pass Filter Switch", WM8976_ADC, 8, 1, 0),
28807+SOC_SINGLE("High Pass Filter Switch", WM8976_ADC, 8, 1, 0),
28808+SOC_SINGLE("High Pass Cut Off", WM8976_ADC, 4, 7, 0),
28809+
28810+SOC_DOUBLE("ADC Inversion Switch", WM8976_ADC, 0, 1, 1, 0),
28811+
28812+SOC_SINGLE("Capture Volume", WM8976_ADCVOL, 0, 127, 0),
28813+
28814+SOC_ENUM("Equaliser Function", wm8976_enum[3]),
28815+SOC_ENUM("EQ1 Cut Off", wm8976_enum[4]),
28816+SOC_SINGLE("EQ1 Volume", WM8976_EQ1, 0, 31, 1),
28817+
28818+SOC_ENUM("Equaliser EQ2 Bandwith", wm8976_enum[5]),
28819+SOC_ENUM("EQ2 Cut Off", wm8976_enum[6]),
28820+SOC_SINGLE("EQ2 Volume", WM8976_EQ2, 0, 31, 1),
28821+
28822+SOC_ENUM("Equaliser EQ3 Bandwith", wm8976_enum[7]),
28823+SOC_ENUM("EQ3 Cut Off", wm8976_enum[8]),
28824+SOC_SINGLE("EQ3 Volume", WM8976_EQ3, 0, 31, 1),
28825+
28826+SOC_ENUM("Equaliser EQ4 Bandwith", wm8976_enum[9]),
28827+SOC_ENUM("EQ4 Cut Off", wm8976_enum[10]),
28828+SOC_SINGLE("EQ4 Volume", WM8976_EQ4, 0, 31, 1),
28829+
28830+SOC_ENUM("Equaliser EQ5 Bandwith", wm8976_enum[11]),
28831+SOC_ENUM("EQ5 Cut Off", wm8976_enum[12]),
28832+SOC_SINGLE("EQ5 Volume", WM8976_EQ5, 0, 31, 1),
28833+
28834+SOC_SINGLE("DAC Playback Limiter Switch", WM8976_DACLIM1, 8, 1, 0),
28835+SOC_SINGLE("DAC Playback Limiter Decay", WM8976_DACLIM1, 4, 15, 0),
28836+SOC_SINGLE("DAC Playback Limiter Attack", WM8976_DACLIM1, 0, 15, 0),
28837+
28838+SOC_SINGLE("DAC Playback Limiter Threshold", WM8976_DACLIM2, 4, 7, 0),
28839+SOC_SINGLE("DAC Playback Limiter Boost", WM8976_DACLIM2, 0, 15, 0),
28840+
28841+SOC_SINGLE("ALC Enable Switch", WM8976_ALC1, 8, 1, 0),
28842+SOC_SINGLE("ALC Capture Max Gain", WM8976_ALC1, 3, 7, 0),
28843+SOC_SINGLE("ALC Capture Min Gain", WM8976_ALC1, 0, 7, 0),
28844+
28845+SOC_SINGLE("ALC Capture ZC Switch", WM8976_ALC2, 8, 1, 0),
28846+SOC_SINGLE("ALC Capture Hold", WM8976_ALC2, 4, 7, 0),
28847+SOC_SINGLE("ALC Capture Target", WM8976_ALC2, 0, 15, 0),
28848+
28849+SOC_ENUM("ALC Capture Mode", wm8976_enum[13]),
28850+SOC_SINGLE("ALC Capture Decay", WM8976_ALC3, 4, 15, 0),
28851+SOC_SINGLE("ALC Capture Attack", WM8976_ALC3, 0, 15, 0),
28852+
28853+SOC_SINGLE("ALC Capture Noise Gate Switch", WM8976_NGATE, 3, 1, 0),
28854+SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8976_NGATE, 0, 7, 0),
28855+
28856+SOC_SINGLE("Capture PGA ZC Switch", WM8976_INPPGA, 7, 1, 0),
28857+SOC_SINGLE("Capture PGA Volume", WM8976_INPPGA, 0, 63, 0),
28858+
28859+SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8976_HPVOLL, WM8976_HPVOLR, 7, 1, 0),
28860+SOC_DOUBLE_R("Headphone Playback Switch", WM8976_HPVOLL, WM8976_HPVOLR, 6, 1, 1),
28861+SOC_DOUBLE_R("Headphone Playback Volume", WM8976_HPVOLL, WM8976_HPVOLR, 0, 63, 0),
28862+
28863+SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8976_SPKVOLL, WM8976_SPKVOLR, 7, 1, 0),
28864+SOC_DOUBLE_R("Speaker Playback Switch", WM8976_SPKVOLL, WM8976_SPKVOLR, 6, 1, 1),
28865+SOC_DOUBLE_R("Speaker Playback Volume", WM8976_SPKVOLL, WM8976_SPKVOLR, 0, 63, 0),
28866+
28867+SOC_SINGLE("Capture Boost(+20dB)", WM8976_ADCBOOST, 8, 1, 0),
28868+};
28869+
28870+/* add non dapm controls */
28871+static int wm8976_add_controls(struct snd_soc_codec *codec)
28872+{
28873+ int err, i;
28874+
28875+ for (i = 0; i < ARRAY_SIZE(wm8976_snd_controls); i++) {
28876+ err = snd_ctl_add(codec->card,
28877+ snd_soc_cnew(&wm8976_snd_controls[i],codec, NULL));
28878+ if (err < 0)
28879+ return err;
28880+ }
28881+
28882+ return 0;
28883+}
28884+
28885+/* Left Output Mixer */
28886+static const snd_kcontrol_new_t wm8976_left_mixer_controls[] = {
28887+SOC_DAPM_SINGLE("Right PCM Playback Switch", WM8976_OUTPUT, 6, 1, 1),
28888+SOC_DAPM_SINGLE("Left PCM Playback Switch", WM8976_MIXL, 0, 1, 1),
28889+SOC_DAPM_SINGLE("Line Bypass Switch", WM8976_MIXL, 1, 1, 0),
28890+SOC_DAPM_SINGLE("Aux Playback Switch", WM8976_MIXL, 5, 1, 0),
28891+};
28892+
28893+/* Right Output Mixer */
28894+static const snd_kcontrol_new_t wm8976_right_mixer_controls[] = {
28895+SOC_DAPM_SINGLE("Left PCM Playback Switch", WM8976_OUTPUT, 5, 1, 1),
28896+SOC_DAPM_SINGLE("Right PCM Playback Switch", WM8976_MIXR, 0, 1, 1),
28897+SOC_DAPM_SINGLE("Line Bypass Switch", WM8976_MIXR, 1, 1, 0),
28898+SOC_DAPM_SINGLE("Aux Playback Switch", WM8976_MIXR, 5, 1, 0),
28899+};
28900+
28901+/* Left AUX Input boost vol */
28902+static const snd_kcontrol_new_t wm8976_laux_boost_controls =
28903+SOC_DAPM_SINGLE("Aux Volume", WM8976_ADCBOOST, 0, 3, 0);
28904+
28905+/* Left Input boost vol */
28906+static const snd_kcontrol_new_t wm8976_lmic_boost_controls =
28907+SOC_DAPM_SINGLE("Input Volume", WM8976_ADCBOOST, 4, 3, 0);
28908+
28909+/* Left Aux In to PGA */
28910+static const snd_kcontrol_new_t wm8976_laux_capture_boost_controls =
28911+SOC_DAPM_SINGLE("Capture Switch", WM8976_ADCBOOST, 8, 1, 0);
28912+
28913+/* Left Input P In to PGA */
28914+static const snd_kcontrol_new_t wm8976_lmicp_capture_boost_controls =
28915+SOC_DAPM_SINGLE("Input P Capture Boost Switch", WM8976_INPUT, 0, 1, 0);
28916+
28917+/* Left Input N In to PGA */
28918+static const snd_kcontrol_new_t wm8976_lmicn_capture_boost_controls =
28919+SOC_DAPM_SINGLE("Input N Capture Boost Switch", WM8976_INPUT, 1, 1, 0);
28920+
28921+// TODO Widgets
28922+static const struct snd_soc_dapm_widget wm8976_dapm_widgets[] = {
28923+#if 0
28924+//SND_SOC_DAPM_MUTE("Mono Mute", WM8976_MONOMIX, 6, 0),
28925+//SND_SOC_DAPM_MUTE("Speaker Mute", WM8976_SPKMIX, 6, 0),
28926+
28927+SND_SOC_DAPM_MIXER("Speaker Mixer", WM8976_POWER3, 2, 0,
28928+ &wm8976_speaker_mixer_controls[0],
28929+ ARRAY_SIZE(wm8976_speaker_mixer_controls)),
28930+SND_SOC_DAPM_MIXER("Mono Mixer", WM8976_POWER3, 3, 0,
28931+ &wm8976_mono_mixer_controls[0],
28932+ ARRAY_SIZE(wm8976_mono_mixer_controls)),
28933+SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8976_POWER3, 0, 0),
28934+SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8976_POWER3, 0, 0),
28935+SND_SOC_DAPM_PGA("Aux Input", WM8976_POWER1, 6, 0, NULL, 0),
28936+SND_SOC_DAPM_PGA("SpkN Out", WM8976_POWER3, 5, 0, NULL, 0),
28937+SND_SOC_DAPM_PGA("SpkP Out", WM8976_POWER3, 6, 0, NULL, 0),
28938+SND_SOC_DAPM_PGA("Mono Out", WM8976_POWER3, 7, 0, NULL, 0),
28939+SND_SOC_DAPM_PGA("Mic PGA", WM8976_POWER2, 2, 0, NULL, 0),
28940+
28941+SND_SOC_DAPM_PGA("Aux Boost", SND_SOC_NOPM, 0, 0,
28942+ &wm8976_aux_boost_controls, 1),
28943+SND_SOC_DAPM_PGA("Mic Boost", SND_SOC_NOPM, 0, 0,
28944+ &wm8976_mic_boost_controls, 1),
28945+SND_SOC_DAPM_SWITCH("Capture Boost", SND_SOC_NOPM, 0, 0,
28946+ &wm8976_capture_boost_controls),
28947+
28948+SND_SOC_DAPM_MIXER("Boost Mixer", WM8976_POWER2, 4, 0, NULL, 0),
28949+
28950+SND_SOC_DAPM_MICBIAS("Mic Bias", WM8976_POWER1, 4, 0),
28951+
28952+SND_SOC_DAPM_INPUT("MICN"),
28953+SND_SOC_DAPM_INPUT("MICP"),
28954+SND_SOC_DAPM_INPUT("AUX"),
28955+SND_SOC_DAPM_OUTPUT("MONOOUT"),
28956+SND_SOC_DAPM_OUTPUT("SPKOUTP"),
28957+SND_SOC_DAPM_OUTPUT("SPKOUTN"),
28958+#endif
28959+};
28960+
28961+static const char *audio_map[][3] = {
28962+ /* Mono output mixer */
28963+ {"Mono Mixer", "PCM Playback Switch", "DAC"},
28964+ {"Mono Mixer", "Aux Playback Switch", "Aux Input"},
28965+ {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
28966+
28967+ /* Speaker output mixer */
28968+ {"Speaker Mixer", "PCM Playback Switch", "DAC"},
28969+ {"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
28970+ {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
28971+
28972+ /* Outputs */
28973+ {"Mono Out", NULL, "Mono Mixer"},
28974+ {"MONOOUT", NULL, "Mono Out"},
28975+ {"SpkN Out", NULL, "Speaker Mixer"},
28976+ {"SpkP Out", NULL, "Speaker Mixer"},
28977+ {"SPKOUTN", NULL, "SpkN Out"},
28978+ {"SPKOUTP", NULL, "SpkP Out"},
28979+
28980+ /* Boost Mixer */
28981+ {"Boost Mixer", NULL, "ADC"},
28982+ {"Capture Boost Switch", "Aux Capture Boost Switch", "AUX"},
28983+ {"Aux Boost", "Aux Volume", "Boost Mixer"},
28984+ {"Capture Boost", "Capture Switch", "Boost Mixer"},
28985+ {"Mic Boost", "Mic Volume", "Boost Mixer"},
28986+
28987+ /* Inputs */
28988+ {"MICP", NULL, "Mic Boost"},
28989+ {"MICN", NULL, "Mic PGA"},
28990+ {"Mic PGA", NULL, "Capture Boost"},
28991+ {"AUX", NULL, "Aux Input"},
28992+
28993+ /* */
28994+
28995+ /* terminator */
28996+ {NULL, NULL, NULL},
28997+};
28998+
28999+static int wm8976_add_widgets(struct snd_soc_codec *codec)
29000+{
29001+ int i;
29002+
29003+ for(i = 0; i < ARRAY_SIZE(wm8976_dapm_widgets); i++) {
29004+ snd_soc_dapm_new_control(codec, &wm8976_dapm_widgets[i]);
29005+ }
29006+
29007+ /* set up audio path map */
29008+ for(i = 0; audio_map[i][0] != NULL; i++) {
29009+ snd_soc_dapm_connect_input(codec, audio_map[i][0], audio_map[i][1],
29010+ audio_map[i][2]);
29011+ }
29012+
29013+ snd_soc_dapm_new_widgets(codec);
29014+ return 0;
29015+}
29016+
29017+struct pll_ {
29018+ unsigned int in_hz, out_hz;
29019+ unsigned int pre:4; /* prescale - 1 */
29020+ unsigned int n:4;
29021+ unsigned int k;
29022+};
29023+
29024+struct pll_ pll[] = {
29025+ {12000000, 11289600, 0, 7, 0x86c220},
29026+ {12000000, 12288000, 0, 8, 0x3126e8},
29027+ {13000000, 11289600, 0, 6, 0xf28bd4},
29028+ {13000000, 12288000, 0, 7, 0x8fd525},
29029+ {12288000, 11289600, 0, 7, 0x59999a},
29030+ {11289600, 12288000, 0, 8, 0x80dee9},
29031+ /* TODO: liam - add more entries */
29032+};
29033+
29034+static int set_pll(struct snd_soc_codec *codec, unsigned int in,
29035+ unsigned int out)
29036+{
29037+ int i;
29038+ u16 reg;
29039+
29040+ if(out == 0) {
29041+ reg = wm8976_read_reg_cache(codec, WM8976_POWER1);
29042+ wm8976_write(codec, WM8976_POWER1, reg & 0x1df);
29043+ return 0;
29044+ }
29045+
29046+ for(i = 0; i < ARRAY_SIZE(pll); i++) {
29047+ if (in == pll[i].in_hz && out == pll[i].out_hz) {
29048+ wm8976_write(codec, WM8976_PLLN, (pll[i].pre << 4) | pll[i].n);
29049+ wm8976_write(codec, WM8976_PLLK1, pll[i].k >> 18);
29050+ wm8976_write(codec, WM8976_PLLK1, (pll[i].k >> 9) && 0x1ff);
29051+ wm8976_write(codec, WM8976_PLLK1, pll[i].k && 0x1ff);
29052+ reg = wm8976_read_reg_cache(codec, WM8976_POWER1);
29053+ wm8976_write(codec, WM8976_POWER1, reg | 0x020);
29054+ return 0;
29055+ }
29056+ }
29057+ return -EINVAL;
29058+}
29059+
29060+/* mclk dividers * 2 */
29061+static unsigned char mclk_div[] = {2, 3, 4, 6, 8, 12, 16, 24};
29062+
29063+/* we need 256FS to drive the DAC's and ADC's */
29064+static unsigned int wm8976_config_sysclk(struct snd_soc_codec_dai *dai,
29065+ struct snd_soc_clock_info *info, unsigned int clk)
29066+{
29067+ int i, j, best_clk = info->fs * info->rate;
29068+
29069+ /* can we run at this clk without the PLL ? */
29070+ for (i = 0; i < ARRAY_SIZE(mclk_div); i++) {
29071+ if ((best_clk >> 1) * mclk_div[i] == clk) {
29072+ dai->pll_in = 0;
29073+ dai->clk_div = mclk_div[i];
29074+ dai->mclk = best_clk;
29075+ return dai->mclk;
29076+ }
29077+ }
29078+
29079+ /* now check for PLL support */
29080+ for (i = 0; i < ARRAY_SIZE(pll); i++) {
29081+ if (pll[i].in_hz == clk) {
29082+ for (j = 0; j < ARRAY_SIZE(mclk_div); j++) {
29083+ if (pll[i].out_hz == mclk_div[j] * (best_clk >> 1)) {
29084+ dai->pll_in = clk;
29085+ dai->pll_out = pll[i].out_hz;
29086+ dai->clk_div = mclk_div[j];
29087+ dai->mclk = best_clk;
29088+ return dai->mclk;
29089+ }
29090+ }
29091+ }
29092+ }
29093+
29094+ /* this clk is not supported */
29095+ return 0;
29096+}
29097+
29098+static int wm8976_pcm_prepare(snd_pcm_substream_t *substream)
29099+{
29100+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
29101+ struct snd_soc_device *socdev = rtd->socdev;
29102+ struct snd_soc_codec *codec = socdev->codec;
29103+ struct snd_soc_codec_dai *dai = rtd->codec_dai;
29104+ u16 iface = 0, bfs, clk = 0, adn;
29105+ int fs = 48000 << 7, i;
29106+
29107+ bfs = SND_SOC_FSBD_REAL(rtd->codec_dai->dai_runtime.bfs);
29108+ switch (bfs) {
29109+ case 2:
29110+ clk |= 0x1 << 2;
29111+ break;
29112+ case 4:
29113+ clk |= 0x2 << 2;
29114+ break;
29115+ case 8:
29116+ clk |= 0x3 << 2;
29117+ break;
29118+ case 16:
29119+ clk |= 0x4 << 2;
29120+ break;
29121+ case 32:
29122+ clk |= 0x5 << 2;
29123+ break;
29124+ }
29125+
29126+ /* set master/slave audio interface */
29127+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
29128+ case SND_SOC_DAIFMT_CBM_CFM:
29129+ clk |= 0x0001;
29130+ break;
29131+ case SND_SOC_DAIFMT_CBS_CFS:
29132+ break;
29133+ }
29134+
29135+ /* interface format */
29136+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
29137+ case SND_SOC_DAIFMT_I2S:
29138+ iface |= 0x0010;
29139+ break;
29140+ case SND_SOC_DAIFMT_RIGHT_J:
29141+ break;
29142+ case SND_SOC_DAIFMT_LEFT_J:
29143+ iface |= 0x0008;
29144+ break;
29145+ case SND_SOC_DAIFMT_DSP_A:
29146+ iface |= 0x00018;
29147+ break;
29148+ }
29149+
29150+ /* bit size */
29151+ switch (rtd->codec_dai->dai_runtime.pcmfmt) {
29152+ case SNDRV_PCM_FMTBIT_S16_LE:
29153+ break;
29154+ case SNDRV_PCM_FMTBIT_S20_3LE:
29155+ iface |= 0x0020;
29156+ break;
29157+ case SNDRV_PCM_FMTBIT_S24_LE:
29158+ iface |= 0x0040;
29159+ break;
29160+ case SNDRV_PCM_FMTBIT_S32_LE:
29161+ iface |= 0x0060;
29162+ break;
29163+ }
29164+
29165+ /* clock inversion */
29166+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_INV_MASK) {
29167+ case SND_SOC_DAIFMT_NB_NF:
29168+ break;
29169+ case SND_SOC_DAIFMT_IB_IF:
29170+ iface |= 0x0180;
29171+ break;
29172+ case SND_SOC_DAIFMT_IB_NF:
29173+ iface |= 0x0100;
29174+ break;
29175+ case SND_SOC_DAIFMT_NB_IF:
29176+ iface |= 0x0080;
29177+ break;
29178+ }
29179+
29180+ /* filter coefficient */
29181+ adn = wm8976_read_reg_cache(codec, WM8976_ADD) & 0x1f1;
29182+ switch (rtd->codec_dai->dai_runtime.pcmrate) {
29183+ case SNDRV_PCM_RATE_8000:
29184+ adn |= 0x5 << 1;
29185+ fs = 8000 << 7;
29186+ break;
29187+ case SNDRV_PCM_RATE_11025:
29188+ adn |= 0x4 << 1;
29189+ fs = 11025 << 7;
29190+ break;
29191+ case SNDRV_PCM_RATE_16000:
29192+ adn |= 0x3 << 1;
29193+ fs = 16000 << 7;
29194+ break;
29195+ case SNDRV_PCM_RATE_22050:
29196+ adn |= 0x2 << 1;
29197+ fs = 22050 << 7;
29198+ break;
29199+ case SNDRV_PCM_RATE_32000:
29200+ adn |= 0x1 << 1;
29201+ fs = 32000 << 7;
29202+ break;
29203+ case SNDRV_PCM_RATE_44100:
29204+ fs = 44100 << 7;
29205+ break;
29206+ }
29207+
29208+ /* do we need to enable the PLL */
29209+ if(dai->pll_in)
29210+ set_pll(codec, dai->pll_in, dai->pll_out);
29211+
29212+ /* divide the clock to 256 fs */
29213+ for(i = 0; i < ARRAY_SIZE(mclk_div); i++) {
29214+ if (dai->clk_div == mclk_div[i]) {
29215+ clk |= i << 5;
29216+ clk &= 0xff;
29217+ goto set;
29218+ }
29219+ }
29220+
29221+set:
29222+ /* set iface */
29223+ wm8976_write(codec, WM8976_IFACE, iface);
29224+ wm8976_write(codec, WM8976_CLOCK, clk);
29225+
29226+ return 0;
29227+}
29228+
29229+static int wm8976_hw_free(struct snd_pcm_substream *substream)
29230+{
29231+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
29232+ struct snd_soc_device *socdev = rtd->socdev;
29233+ struct snd_soc_codec *codec = socdev->codec;
29234+ set_pll(codec, 0, 0);
29235+ return 0;
29236+}
29237+
29238+static int wm8976_mute(struct snd_soc_codec *codec,
29239+ struct snd_soc_codec_dai *dai, int mute)
29240+{
29241+ u16 mute_reg = wm8976_read_reg_cache(codec, WM8976_DAC) & 0xffbf;
29242+ if(mute)
29243+ wm8976_write(codec, WM8976_DAC, mute_reg | 0x40);
29244+ else
29245+ wm8976_write(codec, WM8976_DAC, mute_reg);
29246+
29247+ return 0;
29248+}
29249+
29250+/* TODO: liam need to make this lower power with dapm */
29251+static int wm8976_dapm_event(struct snd_soc_codec *codec, int event)
29252+{
29253+
29254+ switch (event) {
29255+ case SNDRV_CTL_POWER_D0: /* full On */
29256+ /* vref/mid, clk and osc on, dac unmute, active */
29257+ wm8976_write(codec, WM8976_POWER1, 0x1ff);
29258+ wm8976_write(codec, WM8976_POWER2, 0x1ff);
29259+ wm8976_write(codec, WM8976_POWER3, 0x1ff);
29260+ break;
29261+ case SNDRV_CTL_POWER_D1: /* partial On */
29262+ case SNDRV_CTL_POWER_D2: /* partial On */
29263+ break;
29264+ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
29265+ /* everything off except vref/vmid, dac mute, inactive */
29266+
29267+ break;
29268+ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
29269+ /* everything off, dac mute, inactive */
29270+ wm8976_write(codec, WM8976_POWER1, 0x0);
29271+ wm8976_write(codec, WM8976_POWER2, 0x0);
29272+ wm8976_write(codec, WM8976_POWER3, 0x0);
29273+ break;
29274+ }
29275+ codec->dapm_state = event;
29276+ return 0;
29277+}
29278+
29279+struct snd_soc_codec_dai wm8976_dai = {
29280+ .name = "WM8976 HiFi",
29281+ .playback = {
29282+ .stream_name = "Playback",
29283+ .channels_min = 1,
29284+ .channels_max = 2,
29285+ },
29286+ .capture = {
29287+ .stream_name = "Capture",
29288+ .channels_min = 1,
29289+ .channels_max = 1,
29290+ },
29291+ .config_sysclk = wm8976_config_sysclk,
29292+ .digital_mute = wm8976_mute,
29293+ .ops = {
29294+ .prepare = wm8976_pcm_prepare,
29295+ .hw_free = wm8976_hw_free,
29296+ },
29297+ .caps = {
29298+ .num_modes = ARRAY_SIZE(wm8976_modes),
29299+ .mode = wm8976_modes,
29300+ },
29301+};
29302+EXPORT_SYMBOL_GPL(wm8976_dai);
29303+
29304+static int wm8976_suspend(struct platform_device *pdev, pm_message_t state)
29305+{
29306+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
29307+ struct snd_soc_codec *codec = socdev->codec;
29308+
29309+ wm8976_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
29310+ return 0;
29311+}
29312+
29313+static int wm8976_resume(struct platform_device *pdev)
29314+{
29315+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
29316+ struct snd_soc_codec *codec = socdev->codec;
29317+ int i;
29318+ u8 data[2];
29319+ u16 *cache = codec->reg_cache;
29320+
29321+ /* Sync reg_cache with the hardware */
29322+ for (i = 0; i < ARRAY_SIZE(wm8976_reg); i++) {
29323+ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
29324+ data[1] = cache[i] & 0x00ff;
29325+ codec->hw_write(codec->control_data, data, 2);
29326+ }
29327+ wm8976_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
29328+ wm8976_dapm_event(codec, codec->suspend_dapm_state);
29329+ return 0;
29330+}
29331+
29332+/*
29333+ * initialise the WM8976 driver
29334+ * register the mixer and dsp interfaces with the kernel
29335+ */
29336+static int wm8976_init(struct snd_soc_device* socdev)
29337+{
29338+ struct snd_soc_codec *codec = socdev->codec;
29339+ int ret = 0;
29340+
29341+ codec->name = "WM8976";
29342+ codec->owner = THIS_MODULE;
29343+ codec->read = wm8976_read_reg_cache;
29344+ codec->write = wm8976_write;
29345+ codec->dapm_event = wm8976_dapm_event;
29346+ codec->dai = &wm8976_dai;
29347+ codec->num_dai = 1;
29348+ codec->reg_cache_size = ARRAY_SIZE(wm8976_reg);
29349+ codec->reg_cache =
29350+ kzalloc(sizeof(u16) * ARRAY_SIZE(wm8976_reg), GFP_KERNEL);
29351+ if (codec->reg_cache == NULL)
29352+ return -ENOMEM;
29353+ memcpy(codec->reg_cache, wm8976_reg,
29354+ sizeof(u16) * ARRAY_SIZE(wm8976_reg));
29355+ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm8976_reg);
29356+
29357+ wm8976_reset(codec);
29358+
29359+ /* register pcms */
29360+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
29361+ if(ret < 0) {
29362+ kfree(codec->reg_cache);
29363+ return ret;
29364+ }
29365+
29366+ /* power on device */
29367+ wm8976_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
29368+ wm8976_add_controls(codec);
29369+ wm8976_add_widgets(codec);
29370+ ret = snd_soc_register_card(socdev);
29371+ if(ret < 0) {
29372+ snd_soc_free_pcms(socdev);
29373+ snd_soc_dapm_free(socdev);
29374+ }
29375+
29376+ return ret;
29377+}
29378+
29379+static struct snd_soc_device *wm8976_socdev;
29380+
29381+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
29382+
29383+/*
29384+ * WM8976 2 wire address is 0x1a
29385+ */
29386+#define I2C_DRIVERID_WM8976 0xfefe /* liam - need a proper id */
29387+
29388+static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
29389+
29390+/* Magic definition of all other variables and things */
29391+I2C_CLIENT_INSMOD;
29392+
29393+static struct i2c_driver wm8976_i2c_driver;
29394+static struct i2c_client client_template;
29395+
29396+/* If the i2c layer weren't so broken, we could pass this kind of data
29397+ around */
29398+
29399+static int wm8976_codec_probe(struct i2c_adapter *adap, int addr, int kind)
29400+{
29401+ struct snd_soc_device *socdev = wm8976_socdev;
29402+ struct wm8976_setup_data *setup = socdev->codec_data;
29403+ struct snd_soc_codec *codec = socdev->codec;
29404+ struct i2c_client *i2c;
29405+ int ret;
29406+
29407+ if (addr != setup->i2c_address)
29408+ return -ENODEV;
29409+
29410+ client_template.adapter = adap;
29411+ client_template.addr = addr;
29412+
29413+ i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
29414+ if (i2c == NULL){
29415+ kfree(codec);
29416+ return -ENOMEM;
29417+ }
29418+ memcpy(i2c, &client_template, sizeof(struct i2c_client));
29419+
29420+ i2c_set_clientdata(i2c, codec);
29421+
29422+ codec->control_data = i2c;
29423+
29424+ ret = i2c_attach_client(i2c);
29425+ if(ret < 0) {
29426+ err("failed to attach codec at addr %x\n", addr);
29427+ goto err;
29428+ }
29429+
29430+ ret = wm8976_init(socdev);
29431+ if(ret < 0) {
29432+ err("failed to initialise WM8976\n");
29433+ goto err;
29434+ }
29435+ return ret;
29436+
29437+err:
29438+ kfree(codec);
29439+ kfree(i2c);
29440+ return ret;
29441+
29442+}
29443+
29444+static int wm8976_i2c_detach(struct i2c_client *client)
29445+{
29446+ struct snd_soc_codec *codec = i2c_get_clientdata(client);
29447+
29448+ i2c_detach_client(client);
29449+
29450+ kfree(codec->reg_cache);
29451+ kfree(client);
29452+
29453+ return 0;
29454+}
29455+
29456+static int wm8976_i2c_attach(struct i2c_adapter *adap)
29457+{
29458+ return i2c_probe(adap, &addr_data, wm8976_codec_probe);
29459+}
29460+
29461+/* corgi i2c codec control layer */
29462+static struct i2c_driver wm8976_i2c_driver = {
29463+ .driver = {
29464+ .name = "WM8976 I2C Codec",
29465+ .owner = THIS_MODULE,
29466+ },
29467+ .id = I2C_DRIVERID_WM8976,
29468+ .attach_adapter = wm8976_i2c_attach,
29469+ .detach_client = wm8976_i2c_detach,
29470+ .command = NULL,
29471+};
29472+
29473+static struct i2c_client client_template = {
29474+ .name = "WM8976",
29475+ .driver = &wm8976_i2c_driver,
29476+};
29477+#endif
29478+
29479+static int wm8976_probe(struct platform_device *pdev)
29480+{
29481+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
29482+ struct wm8976_setup_data *setup;
29483+ struct snd_soc_codec *codec;
29484+ int ret = 0;
29485+
29486+ info("WM8976 Audio Codec %s", WM8976_VERSION);
29487+
29488+ setup = socdev->codec_data;
29489+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
29490+ if (codec == NULL)
29491+ return -ENOMEM;
29492+
29493+ socdev->codec = codec;
29494+ mutex_init(&codec->mutex);
29495+ INIT_LIST_HEAD(&codec->dapm_widgets);
29496+ INIT_LIST_HEAD(&codec->dapm_paths);
29497+
29498+ wm8976_socdev = socdev;
29499+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
29500+ if (setup->i2c_address) {
29501+ normal_i2c[0] = setup->i2c_address;
29502+ codec->hw_write = (hw_write_t)i2c_master_send;
29503+ ret = i2c_add_driver(&wm8976_i2c_driver);
29504+ if (ret != 0)
29505+ printk(KERN_ERR "can't add i2c driver");
29506+ }
29507+#else
29508+ /* Add other interfaces here */
29509+#endif
29510+ return ret;
29511+}
29512+
29513+/* power down chip */
29514+static int wm8976_remove(struct platform_device *pdev)
29515+{
29516+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
29517+ struct snd_soc_codec *codec = socdev->codec;
29518+
29519+ if (codec->control_data)
29520+ wm8976_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
29521+
29522+ snd_soc_free_pcms(socdev);
29523+ snd_soc_dapm_free(socdev);
29524+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
29525+ i2c_del_driver(&wm8976_i2c_driver);
29526+#endif
29527+ kfree(codec);
29528+
29529+ return 0;
29530+}
29531+
29532+struct snd_soc_codec_device soc_codec_dev_wm8976 = {
29533+ .probe = wm8976_probe,
29534+ .remove = wm8976_remove,
29535+ .suspend = wm8976_suspend,
29536+ .resume = wm8976_resume,
29537+};
29538+
29539+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8976);
29540+
29541+MODULE_DESCRIPTION("ASoC WM8976 driver");
29542+MODULE_AUTHOR("Graeme Gregory");
29543+MODULE_LICENSE("GPL");
29544Index: linux-2.6-pxa-new/sound/soc/codecs/wm8976.h
29545===================================================================
29546--- /dev/null
29547+++ linux-2.6-pxa-new/sound/soc/codecs/wm8976.h
29548@@ -0,0 +1,73 @@
29549+/*
29550+ * wm8976.h -- WM8976 Soc Audio driver
29551+ *
29552+ * This program is free software; you can redistribute it and/or modify
29553+ * it under the terms of the GNU General Public License version 2 as
29554+ * published by the Free Software Foundation.
29555+ */
29556+
29557+#ifndef _WM8976_H
29558+#define _WM8976_H
29559+
29560+/* WM8976 register space */
29561+
29562+#define WM8976_RESET 0x0
29563+#define WM8976_POWER1 0x1
29564+#define WM8976_POWER2 0x2
29565+#define WM8976_POWER3 0x3
29566+#define WM8976_IFACE 0x4
29567+#define WM8976_COMP 0x5
29568+#define WM8976_CLOCK 0x6
29569+#define WM8976_ADD 0x7
29570+#define WM8976_GPIO 0x8
29571+#define WM8976_JACK1 0x9
29572+#define WM8976_DAC 0xa
29573+#define WM8976_DACVOLL 0xb
29574+#define WM8976_DACVOLR 0xc
29575+#define WM8976_JACK2 0xd
29576+#define WM8976_ADC 0xe
29577+#define WM8976_ADCVOL 0xf
29578+#define WM8976_EQ1 0x12
29579+#define WM8976_EQ2 0x13
29580+#define WM8976_EQ3 0x14
29581+#define WM8976_EQ4 0x15
29582+#define WM8976_EQ5 0x16
29583+#define WM8976_DACLIM1 0x18
29584+#define WM8976_DACLIM2 0x19
29585+#define WM8976_NOTCH1 0x1b
29586+#define WM8976_NOTCH2 0x1c
29587+#define WM8976_NOTCH3 0x1d
29588+#define WM8976_NOTCH4 0x1e
29589+#define WM8976_ALC1 0x20
29590+#define WM8976_ALC2 0x21
29591+#define WM8976_ALC3 0x22
29592+#define WM8976_NGATE 0x23
29593+#define WM8976_PLLN 0x24
29594+#define WM8976_PLLK1 0x25
29595+#define WM8976_PLLK2 0x26
29596+#define WM8976_PLLK3 0x27
29597+#define WM8976_3D 0x29
29598+#define WM8976_BEEP 0x2b
29599+#define WM8976_INPUT 0x2c
29600+#define WM8976_INPPGA 0x2d
29601+#define WM8976_ADCBOOST 0x2f
29602+#define WM8976_OUTPUT 0x31
29603+#define WM8976_MIXL 0x32
29604+#define WM8976_MIXR 0x33
29605+#define WM8976_HPVOLL 0x34
29606+#define WM8976_HPVOLR 0x35
29607+#define WM8976_SPKVOLL 0x36
29608+#define WM8976_SPKVOLR 0x37
29609+#define WM8976_OUT3MIX 0x38
29610+#define WM8976_MONOMIX 0x39
29611+
29612+#define WM8976_CACHEREGNUM 58
29613+
29614+struct wm8976_setup_data {
29615+ unsigned short i2c_address;
29616+};
29617+
29618+extern struct snd_soc_codec_dai wm8976_dai;
29619+extern struct snd_soc_codec_device soc_codec_dev_wm8976;
29620+
29621+#endif
29622Index: linux-2.6-pxa-new/sound/soc/imx/imx21-pcm.c
29623===================================================================
29624--- /dev/null
29625+++ linux-2.6-pxa-new/sound/soc/imx/imx21-pcm.c
29626@@ -0,0 +1,454 @@
29627+/*
29628+ * linux/sound/arm/mxc-pcm.c -- ALSA SoC interface for the Freescale i.MX CPU's
29629+ *
29630+ * Copyright 2006 Wolfson Microelectronics PLC.
29631+ * Author: Liam Girdwood
29632+ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
29633+ *
29634+ * Based on pxa2xx-pcm.c by Nicolas Pitre, (C) 2004 MontaVista Software, Inc.
29635+ * and on mxc-alsa-mc13783 (C) 2006 Freescale.
29636+ *
29637+ * This program is free software; you can redistribute it and/or modify
29638+ * it under the terms of the GNU General Public License version 2 as
29639+ * published by the Free Software Foundation.
29640+ *
29641+ * Revision history
29642+ * 29th Aug 2006 Initial version.
29643+ *
29644+ */
29645+
29646+#include <linux/module.h>
29647+#include <linux/init.h>
29648+#include <linux/platform_device.h>
29649+#include <linux/slab.h>
29650+#include <linux/dma-mapping.h>
29651+#include <sound/driver.h>
29652+#include <sound/core.h>
29653+#include <sound/pcm.h>
29654+#include <sound/pcm_params.h>
29655+#include <sound/soc.h>
29656+#include <asm/dma.h>
29657+#include <asm/hardware.h>
29658+
29659+#include "imx-pcm.h"
29660+
29661+/* debug */
29662+#define IMX_DEBUG 0
29663+#if IMX_DEBUG
29664+#define dbg(format, arg...) printk(format, ## arg)
29665+#else
29666+#define dbg(format, arg...)
29667+#endif
29668+
29669+static const struct snd_pcm_hardware mxc_pcm_hardware = {
29670+ .info = (SNDRV_PCM_INFO_INTERLEAVED |
29671+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
29672+ SNDRV_PCM_INFO_MMAP |
29673+ SNDRV_PCM_INFO_MMAP_VALID |
29674+ SNDRV_PCM_INFO_PAUSE |
29675+ SNDRV_PCM_INFO_RESUME),
29676+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
29677+ SNDRV_PCM_FMTBIT_S24_LE,
29678+ .buffer_bytes_max = 32 * 1024,
29679+ .period_bytes_min = 64,
29680+ .period_bytes_max = 8 * 1024,
29681+ .periods_min = 2,
29682+ .periods_max = 255,
29683+ .fifo_size = 0,
29684+};
29685+
29686+struct mxc_runtime_data {
29687+ int dma_ch;
29688+ struct mxc_pcm_dma_param *dma_params;
29689+};
29690+
29691+/*!
29692+ * This function stops the current dma transfert for playback
29693+ * and clears the dma pointers.
29694+ *
29695+ * @param substream pointer to the structure of the current stream.
29696+ *
29697+ */
29698+static void audio_stop_dma(struct snd_pcm_substream *substream)
29699+{
29700+ struct snd_pcm_runtime *runtime = substream->runtime;
29701+ struct mxc_runtime_data *prtd = runtime->private_data;
29702+ unsigned int dma_size = frames_to_bytes(runtime, runtime->period_size);
29703+ unsigned int offset dma_size * s->periods;
29704+ unsigned long flags;
29705+
29706+ spin_lock_irqsave(&prtd->dma_lock, flags);
29707+
29708+ dbg("MXC : audio_stop_dma active = 0\n");
29709+ prtd->active = 0;
29710+ prtd->period = 0;
29711+ prtd->periods = 0;
29712+
29713+ /* this stops the dma channel and clears the buffer ptrs */
29714+ mxc_dma_stop(prtd->dma_wchannel);
29715+ if(substream == SNDRV_PCM_STREAM_PLAYBACK)
29716+ dma_unmap_single(NULL, runtime->dma_addr + offset, dma_size,
29717+ DMA_TO_DEVICE);
29718+ else
29719+ dma_unmap_single(NULL, runtime->dma_addr + offset, dma_size,
29720+ DMA_FROM_DEVICE);
29721+
29722+ spin_unlock_irqrestore(&prtd->dma_lock, flags);
29723+}
29724+
29725+/*!
29726+ * This function is called whenever a new audio block needs to be
29727+ * transferred to mc13783. The function receives the address and the size
29728+ * of the new block and start a new DMA transfer.
29729+ *
29730+ * @param substream pointer to the structure of the current stream.
29731+ *
29732+ */
29733+static int dma_new_period(struct snd_pcm_substream *substream)
29734+{
29735+ struct snd_pcm_runtime *runtime = substream->runtime;
29736+ struct mxc_runtime_data *prtd = runtime->private_data;
29737+ unsigned int dma_size;
29738+ unsigned int offset;
29739+ int ret=0;
29740+ dma_request_t sdma_request;
29741+
29742+ if (prtd->active){
29743+ memset(&sdma_request, 0, sizeof(dma_request_t));
29744+ dma_size = frames_to_bytes(runtime, runtime->period_size);
29745+ dbg("s->period (%x) runtime->periods (%d)\n",
29746+ s->period,runtime->periods);
29747+ dbg("runtime->period_size (%d) dma_size (%d)\n",
29748+ (unsigned int)runtime->period_size,
29749+ runtime->dma_bytes);
29750+
29751+ offset = dma_size * prtd->period;
29752+ snd_assert(dma_size <= DMA_BUF_SIZE, );
29753+ if(substream == SNDRV_PCM_STREAM_PLAYBACK)
29754+ sdma_request.sourceAddr = (char*)(dma_map_single(NULL,
29755+ runtime->dma_area + offset, dma_size, DMA_TO_DEVICE));
29756+ else
29757+ sdma_request.destAddr = (char*)(dma_map_single(NULL,
29758+ runtime->dma_area + offset, dma_size, DMA_FROM_DEVICE));
29759+ sdma_request.count = dma_size;
29760+
29761+ dbg("MXC: Start DMA offset (%d) size (%d)\n", offset,
29762+ runtime->dma_bytes);
29763+
29764+ mxc_dma_set_config(prtd->dma_wchannel, &sdma_request, 0);
29765+ if((ret = mxc_dma_start(prtd->dma_wchannel)) < 0) {
29766+ dbg("audio_process_dma: cannot queue DMA buffer\
29767+ (%i)\n", ret);
29768+ return err;
29769+ }
29770+ prtd->tx_spin = 1; /* FGA little trick to retrieve DMA pos */
29771+ prtd->period++;
29772+ prtd->period %= runtime->periods;
29773+ }
29774+ return ret;
29775+}
29776+
29777+
29778+/*!
29779+ * This is a callback which will be called
29780+ * when a TX transfer finishes. The call occurs
29781+ * in interrupt context.
29782+ *
29783+ * @param dat pointer to the structure of the current stream.
29784+ *
29785+ */
29786+static void audio_dma_irq(void *data)
29787+{
29788+ struct snd_pcm_substream *substream;
29789+ struct snd_pcm_runtime *runtime;
29790+ struct mxc_runtime_data *prtd;
29791+ unsigned int dma_size;
29792+ unsigned int previous_period;
29793+ unsigned int offset;
29794+
29795+ substream = data;
29796+ runtime = substream->runtime;
29797+ prtd = runtime->private_data;
29798+ previous_period = prtd->periods;
29799+ dma_size = frames_to_bytes(runtime, runtime->period_size);
29800+ offset = dma_size * previous_period;
29801+
29802+ prtd->tx_spin = 0;
29803+ prtd->periods++;
29804+ prtd->periods %= runtime->periods;
29805+
29806+ /*
29807+ * Give back to the CPU the access to the non cached memory
29808+ */
29809+ if(substream == SNDRV_PCM_STREAM_PLAYBACK)
29810+ dma_unmap_single(NULL, runtime->dma_addr + offset, dma_size,
29811+ DMA_TO_DEVICE);
29812+ else
29813+ dma_unmap_single(NULL, runtime->dma_addr + offset, dma_size,
29814+ DMA_FROM_DEVICE);
29815+ /*
29816+ * If we are getting a callback for an active stream then we inform
29817+ * the PCM middle layer we've finished a period
29818+ */
29819+ if (prtd->active)
29820+ snd_pcm_period_elapsed(substream);
29821+
29822+ /*
29823+ * Trig next DMA transfer
29824+ */
29825+ dma_new_period(substream);
29826+}
29827+
29828+/*!
29829+ * This function configures the hardware to allow audio
29830+ * playback operations. It is called by ALSA framework.
29831+ *
29832+ * @param substream pointer to the structure of the current stream.
29833+ *
29834+ * @return 0 on success, -1 otherwise.
29835+ */
29836+static int
29837+snd_mxc_prepare(struct snd_pcm_substream *substream)
29838+{
29839+ struct mxc_runtime_data *prtd = runtime->private_data;
29840+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
29841+ int ret = 0;
29842+ prtd->period = 0;
29843+ prtd->periods = 0;
29844+
29845+ dma_channel_params params;
29846+ int channel = 0; // passed in ?
29847+
29848+ if ((ret = mxc_request_dma(&channel, "ALSA TX SDMA") < 0)){
29849+ dbg("error requesting a write dma channel\n");
29850+ return ret;
29851+ }
29852+
29853+ /* configure DMA params */
29854+ memset(&params, 0, sizeof(dma_channel_params));
29855+ params.bd_number = 1;
29856+ params.arg = s;
29857+ params.callback = callback;
29858+ params.transfer_type = emi_2_per;
29859+ params.watermark_level = SDMA_TXFIFO_WATERMARK;
29860+ params.word_size = TRANSFER_16BIT;
29861+ //dbg(KERN_ERR "activating connection SSI1 - SDMA\n");
29862+ params.per_address = SSI1_BASE_ADDR;
29863+ params.event_id = DMA_REQ_SSI1_TX1;
29864+ params.peripheral_type = SSI;
29865+
29866+ /* set up chn with params */
29867+ mxc_dma_setup_channel(channel, &params);
29868+ s->dma_wchannel = channel;
29869+
29870+ return ret;
29871+}
29872+
29873+static int mxc_pcm_hw_params(struct snd_pcm_substream *substream,
29874+ struct snd_pcm_hw_params *params)
29875+{
29876+ struct snd_pcm_runtime *runtime = substream->runtime;
29877+ int ret;
29878+
29879+ if((ret=snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
29880+ return ret;
29881+ runtime->dma_addr = virt_to_phys(runtime->dma_area);
29882+
29883+ return ret;
29884+}
29885+
29886+static int mxc_pcm_hw_free(struct snd_pcm_substream *substream)
29887+{
29888+ return snd_pcm_lib_free_pages(substream);
29889+}
29890+
29891+static int mxc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
29892+{
29893+ struct mxc_runtime_data *prtd = substream->runtime->private_data;
29894+ int ret = 0;
29895+
29896+ switch (cmd) {
29897+ case SNDRV_PCM_TRIGGER_START:
29898+ prtd->tx_spin = 0;
29899+ /* requested stream startup */
29900+ prtd->active = 1;
29901+ ret = dma_new_period(substream);
29902+ break;
29903+ case SNDRV_PCM_TRIGGER_STOP:
29904+ /* requested stream shutdown */
29905+ ret = audio_stop_dma(substream);
29906+ break;
29907+ case SNDRV_PCM_TRIGGER_SUSPEND:
29908+ prtd->active = 0;
29909+ prtd->periods = 0;
29910+ break;
29911+ case SNDRV_PCM_TRIGGER_RESUME:
29912+ prtd->active = 1;
29913+ prtd->tx_spin = 0;
29914+ ret = dma_new_period(substream);
29915+ break;
29916+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
29917+ prtd->active = 0;
29918+ break;
29919+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
29920+ prtd->active = 1;
29921+ if (prtd->old_offset) {
29922+ prtd->tx_spin = 0;
29923+ ret = dma_new_period(substream);
29924+ }
29925+ break;
29926+ default:
29927+ ret = -EINVAL;
29928+ break;
29929+ }
29930+
29931+ return ret;
29932+}
29933+
29934+static snd_pcm_uframes_t mxc_pcm_pointer(struct snd_pcm_substream *substream)
29935+{
29936+ struct snd_pcm_runtime *runtime = substream->runtime;
29937+ struct mxc_runtime_data *prtd = runtime->private_data;
29938+ unsigned int offset = 0;
29939+
29940+ /* tx_spin value is used here to check if a transfert is active */
29941+ if (prtd->tx_spin){
29942+ offset = (runtime->period_size * (prtd->periods)) +
29943+ (runtime->period_size >> 1);
29944+ if (offset >= runtime->buffer_size)
29945+ offset = runtime->period_size >> 1;
29946+ } else {
29947+ offset = (runtime->period_size * (s->periods));
29948+ if (offset >= runtime->buffer_size)
29949+ offset = 0;
29950+ }
29951+
29952+ return offset;
29953+}
29954+
29955+
29956+static int mxc_pcm_open(struct snd_pcm_substream *substream)
29957+{
29958+ struct snd_pcm_runtime *runtime = substream->runtime;
29959+ struct mxc_runtime_data *prtd;
29960+ int ret;
29961+
29962+ snd_soc_set_runtime_hwparams(substream, &mxc_pcm_hardware);
29963+
29964+ if ((err = snd_pcm_hw_constraint_integer(runtime,
29965+ SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
29966+ return err;
29967+ if ((err = snd_pcm_hw_constraint_list(runtime, 0,
29968+ SNDRV_PCM_HW_PARAM_RATE, &hw_playback_rates)) < 0)
29969+ return err;
29970+ msleep(10); // liam - why
29971+
29972+ /* setup DMA controller for playback */
29973+ if((err = configure_write_channel(&mxc_mc13783->s[SNDRV_PCM_STREAM_PLAYBACK],
29974+ audio_dma_irq)) < 0 )
29975+ return err;
29976+
29977+ if((prtd = kzalloc(sizeof(struct mxc_runtime_data), GFP_KERNEL)) == NULL) {
29978+ ret = -ENOMEM;
29979+ goto out;
29980+ }
29981+
29982+ runtime->private_data = prtd;
29983+ return 0;
29984+
29985+ err1:
29986+ kfree(prtd);
29987+ out:
29988+ return ret;
29989+}
29990+
29991+static int mxc_pcm_close(struct snd_pcm_substream *substream)
29992+{
29993+ struct snd_pcm_runtime *runtime = substream->runtime;
29994+ struct mxc_runtime_data *prtd = runtime->private_data;
29995+
29996+// mxc_mc13783_t *chip;
29997+ audio_stream_t *s;
29998+ device_data_t* device;
29999+ int ssi;
30000+
30001+ //chip = snd_pcm_substream_chip(substream);
30002+ s = &chip->s[substream->pstr->stream];
30003+ device = &s->stream_device;
30004+ ssi = device->ssi;
30005+
30006+ //disable_stereodac();
30007+
30008+ ssi_transmit_enable(ssi, false);
30009+ ssi_interrupt_disable(ssi, ssi_tx_dma_interrupt_enable);
30010+ ssi_tx_fifo_enable(ssi, ssi_fifo_0, false);
30011+ ssi_enable(ssi, false);
30012+
30013+ chip->s[substream->pstr->stream].stream = NULL;
30014+
30015+ return 0;
30016+}
30017+
30018+static int
30019+mxc_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma)
30020+{
30021+ struct snd_pcm_runtime *runtime = substream->runtime;
30022+ return dma_mmap_writecombine(substream->pcm->card->dev, vma,
30023+ runtime->dma_area,
30024+ runtime->dma_addr,
30025+ runtime->dma_bytes);
30026+}
30027+
30028+struct snd_pcm_ops mxc_pcm_ops = {
30029+ .open = mxc_pcm_open,
30030+ .close = mxc_pcm_close,
30031+ .ioctl = snd_pcm_lib_ioctl,
30032+ .hw_params = mxc_pcm_hw_params,
30033+ .hw_free = mxc_pcm_hw_free,
30034+ .prepare = mxc_pcm_prepare,
30035+ .trigger = mxc_pcm_trigger,
30036+ .pointer = mxc_pcm_pointer,
30037+ .mmap = mxc_pcm_mmap,
30038+};
30039+
30040+static u64 mxc_pcm_dmamask = 0xffffffff;
30041+
30042+int mxc_pcm_new(struct snd_card *card, struct snd_soc_codec_dai *dai,
30043+ struct snd_pcm *pcm)
30044+{
30045+ int ret = 0;
30046+
30047+ if (!card->dev->dma_mask)
30048+ card->dev->dma_mask = &mxc_pcm_dmamask;
30049+ if (!card->dev->coherent_dma_mask)
30050+ card->dev->coherent_dma_mask = 0xffffffff;
30051+
30052+ if (dai->playback.channels_min) {
30053+ ret = mxc_pcm_preallocate_dma_buffer(pcm,
30054+ SNDRV_PCM_STREAM_PLAYBACK);
30055+ if (ret)
30056+ goto out;
30057+ }
30058+
30059+ if (dai->capture.channels_min) {
30060+ ret = mxc_pcm_preallocate_dma_buffer(pcm,
30061+ SNDRV_PCM_STREAM_CAPTURE);
30062+ if (ret)
30063+ goto out;
30064+ }
30065+ out:
30066+ return ret;
30067+}
30068+
30069+struct snd_soc_platform mxc_soc_platform = {
30070+ .name = "mxc-audio",
30071+ .pcm_ops = &mxc_pcm_ops,
30072+ .pcm_new = mxc_pcm_new,
30073+ .pcm_free = mxc_pcm_free_dma_buffers,
30074+};
30075+
30076+EXPORT_SYMBOL_GPL(mxc_soc_platform);
30077+
30078+MODULE_AUTHOR("Liam Girdwood");
30079+MODULE_DESCRIPTION("Freescale i.MX PCM DMA module");
30080+MODULE_LICENSE("GPL");
30081Index: linux-2.6-pxa-new/sound/soc/imx/imx21-pcm.h
30082===================================================================
30083--- /dev/null
30084+++ linux-2.6-pxa-new/sound/soc/imx/imx21-pcm.h
30085@@ -0,0 +1,237 @@
30086+/*
30087+ * mxc-pcm.h :- ASoC platform header for Freescale i.MX
30088+ *
30089+ * This program is free software; you can redistribute it and/or modify
30090+ * it under the terms of the GNU General Public License version 2 as
30091+ * published by the Free Software Foundation.
30092+ */
30093+
30094+#ifndef _MXC_PCM_H
30095+#define _MXC_PCM_H
30096+
30097+struct {
30098+ char *name; /* stream identifier */
30099+ dma_channel_params dma_params;
30100+} mxc_pcm_dma_param;
30101+
30102+extern struct snd_soc_cpu_dai mxc_ssi_dai[3];
30103+
30104+/* platform data */
30105+extern struct snd_soc_platform mxc_soc_platform;
30106+extern struct snd_ac97_bus_ops mxc_ac97_ops;
30107+
30108+/* temp until imx-regs.h is up2date */
30109+#define SSI1_STX0 __REG(IMX_SSI1_BASE + 0x00)
30110+#define SSI1_STX0_PHYS __PHYS_REG(IMX_SSI1_BASE + 0x00)
30111+#define SSI1_STX1 __REG(IMX_SSI1_BASE + 0x04)
30112+#define SSI1_STX1_PHYS __PHYS_REG(IMX_SSI1_BASE + 0x04)
30113+#define SSI1_SRX0 __REG(IMX_SSI1_BASE + 0x08)
30114+#define SSI1_SRX0_PHYS __PHYS_REG(IMX_SSI1_BASE + 0x08)
30115+#define SSI1_SRX1 __REG(IMX_SSI1_BASE + 0x0c)
30116+#define SSI1_SRX1_PHYS __PHYS_REG(IMX_SSI1_BASE + 0x0c)
30117+#define SSI1_SCR __REG(IMX_SSI1_BASE + 0x10)
30118+#define SSI1_SISR __REG(IMX_SSI1_BASE + 0x14)
30119+#define SSI1_SIER __REG(IMX_SSI1_BASE + 0x18)
30120+#define SSI1_STCR __REG(IMX_SSI1_BASE + 0x1c)
30121+#define SSI1_SRCR __REG(IMX_SSI1_BASE + 0x20)
30122+#define SSI1_STCCR __REG(IMX_SSI1_BASE + 0x24)
30123+#define SSI1_SRCCR __REG(IMX_SSI1_BASE + 0x28)
30124+#define SSI1_SFCSR __REG(IMX_SSI1_BASE + 0x2c)
30125+#define SSI1_STR __REG(IMX_SSI1_BASE + 0x30)
30126+#define SSI1_SOR __REG(IMX_SSI1_BASE + 0x34)
30127+#define SSI1_SACNT __REG(IMX_SSI1_BASE + 0x38)
30128+#define SSI1_SACADD __REG(IMX_SSI1_BASE + 0x3c)
30129+#define SSI1_SACDAT __REG(IMX_SSI1_BASE + 0x40)
30130+#define SSI1_SATAG __REG(IMX_SSI1_BASE + 0x44)
30131+#define SSI1_STMSK __REG(IMX_SSI1_BASE + 0x48)
30132+#define SSI1_SRMSK __REG(IMX_SSI1_BASE + 0x4c)
30133+
30134+#define SSI2_STX0 __REG(IMX_SSI2_BASE + 0x00)
30135+#define SSI2_STX0_PHYS __PHYS_REG(IMX_SSI2_BASE + 0x00)
30136+#define SSI2_STX1 __REG(IMX_SSI2_BASE + 0x04)
30137+#define SSI2_STX1_PHYS __PHYS_REG(IMX_SSI2_BASE + 0x04)
30138+#define SSI2_SRX0 __REG(IMX_SSI2_BASE + 0x08)
30139+#define SSI2_SRX0_PHYS __PHYS_REG(IMX_SSI2_BASE + 0x08)
30140+#define SSI2_SRX1 __REG(IMX_SSI2_BASE + 0x0c)
30141+#define SSI2_SRX1_PHYS __PHYS_REG(IMX_SSI2_BASE + 0x0c)
30142+#define SSI2_SCR __REG(IMX_SSI2_BASE + 0x10)
30143+#define SSI2_SISR __REG(IMX_SSI2_BASE + 0x14)
30144+#define SSI2_SIER __REG(IMX_SSI2_BASE + 0x18)
30145+#define SSI2_STCR __REG(IMX_SSI2_BASE + 0x1c)
30146+#define SSI2_SRCR __REG(IMX_SSI2_BASE + 0x20)
30147+#define SSI2_STCCR __REG(IMX_SSI2_BASE + 0x24)
30148+#define SSI2_SRCCR __REG(IMX_SSI2_BASE + 0x28)
30149+#define SSI2_SFCSR __REG(IMX_SSI2_BASE + 0x2c)
30150+#define SSI2_STR __REG(IMX_SSI2_BASE + 0x30)
30151+#define SSI2_SOR __REG(IMX_SSI2_BASE + 0x34)
30152+#define SSI2_SACNT __REG(IMX_SSI2_BASE + 0x38)
30153+#define SSI2_SACADD __REG(IMX_SSI2_BASE + 0x3c)
30154+#define SSI2_SACDAT __REG(IMX_SSI2_BASE + 0x40)
30155+#define SSI2_SATAG __REG(IMX_SSI2_BASE + 0x44)
30156+#define SSI2_STMSK __REG(IMX_SSI2_BASE + 0x48)
30157+#define SSI2_SRMSK __REG(IMX_SSI2_BASE + 0x4c)
30158+
30159+#define SSI_SCR_CLK_IST (1 << 9)
30160+#define SSI_SCR_TCH_EN (1 << 8)
30161+#define SSI_SCR_SYS_CLK_EN (1 << 7)
30162+#define SSI_SCR_I2S_MODE_NORM (0 << 5)
30163+#define SSI_SCR_I2S_MODE_MSTR (1 << 5)
30164+#define SSI_SCR_I2S_MODE_SLAVE (2 << 5)
30165+#define SSI_SCR_SYN (1 << 4)
30166+#define SSI_SCR_NET (1 << 3)
30167+#define SSI_SCR_RE (1 << 2)
30168+#define SSI_SCR_TE (1 << 1)
30169+#define SSI_SCR_SSIEN (1 << 0)
30170+
30171+#define SSI_SISR_CMDAU (1 << 18)
30172+#define SSI_SISR_CMDDU (1 << 17)
30173+#define SSI_SISR_RXT (1 << 16)
30174+#define SSI_SISR_RDR1 (1 << 15)
30175+#define SSI_SISR_RDR0 (1 << 14)
30176+#define SSI_SISR_TDE1 (1 << 13)
30177+#define SSI_SISR_TDE0 (1 << 12)
30178+#define SSI_SISR_ROE1 (1 << 11)
30179+#define SSI_SISR_ROE0 (1 << 10)
30180+#define SSI_SISR_TUE1 (1 << 9)
30181+#define SSI_SISR_TUE0 (1 << 8)
30182+#define SSI_SISR_TFS (1 << 7)
30183+#define SSI_SISR_RFS (1 << 6)
30184+#define SSI_SISR_TLS (1 << 5)
30185+#define SSI_SISR_RLS (1 << 4)
30186+#define SSI_SISR_RFF1 (1 << 3)
30187+#define SSI_SISR_RFF0 (1 << 2)
30188+#define SSI_SISR_TFE1 (1 << 1)
30189+#define SSI_SISR_TFE0 (1 << 0)
30190+
30191+#define SSI_SIER_RDMAE (1 << 22)
30192+#define SSI_SIER_RIE (1 << 21)
30193+#define SSI_SIER_TDMAE (1 << 20)
30194+#define SSI_SIER_TIE (1 << 19)
30195+#define SSI_SIER_CMDAU_EN (1 << 18)
30196+#define SSI_SIER_CMDDU_EN (1 << 17)
30197+#define SSI_SIER_RXT_EN (1 << 16)
30198+#define SSI_SIER_RDR1_EN (1 << 15)
30199+#define SSI_SIER_RDR0_EN (1 << 14)
30200+#define SSI_SIER_TDE1_EN (1 << 13)
30201+#define SSI_SIER_TDE0_EN (1 << 12)
30202+#define SSI_SIER_ROE1_EN (1 << 11)
30203+#define SSI_SIER_ROE0_EN (1 << 10)
30204+#define SSI_SIER_TUE1_EN (1 << 9)
30205+#define SSI_SIER_TUE0_EN (1 << 8)
30206+#define SSI_SIER_TFS_EN (1 << 7)
30207+#define SSI_SIER_RFS_EN (1 << 6)
30208+#define SSI_SIER_TLS_EN (1 << 5)
30209+#define SSI_SIER_RLS_EN (1 << 4)
30210+#define SSI_SIER_RFF1_EN (1 << 3)
30211+#define SSI_SIER_RFF0_EN (1 << 2)
30212+#define SSI_SIER_TFE1_EN (1 << 1)
30213+#define SSI_SIER_TFE0_EN (1 << 0)
30214+
30215+#define SSI_STCR_TXBIT0 (1 << 9)
30216+#define SSI_STCR_TFEN1 (1 << 8)
30217+#define SSI_STCR_TFEN0 (1 << 7)
30218+#define SSI_STCR_TFDIR (1 << 6)
30219+#define SSI_STCR_TXDIR (1 << 5)
30220+#define SSI_STCR_TSHFD (1 << 4)
30221+#define SSI_STCR_TSCKP (1 << 3)
30222+#define SSI_STCR_TFSI (1 << 2)
30223+#define SSI_STCR_TFSL (1 << 1)
30224+#define SSI_STCR_TEFS (1 << 0)
30225+
30226+#define SSI_SRCR_RXBIT0 (1 << 9)
30227+#define SSI_SRCR_RFEN1 (1 << 8)
30228+#define SSI_SRCR_RFEN0 (1 << 7)
30229+#define SSI_SRCR_RFDIR (1 << 6)
30230+#define SSI_SRCR_RXDIR (1 << 5)
30231+#define SSI_SRCR_RSHFD (1 << 4)
30232+#define SSI_SRCR_RSCKP (1 << 3)
30233+#define SSI_SRCR_RFSI (1 << 2)
30234+#define SSI_SRCR_RFSL (1 << 1)
30235+#define SSI_SRCR_REFS (1 << 0)
30236+
30237+#define SSI_STCCR_DIV2 (1 << 18)
30238+#define SSI_STCCR_PSR (1 << 15)
30239+#define SSI_STCCR_WL(x) ((((x) - 2) >> 1) << 13)
30240+#define SSI_STCCR_DC(x) (((x) & 0x1f) << 8)
30241+#define SSI_STCCR_PM(x) (((x) & 0xff) << 0)
30242+
30243+#define SSI_SRCCR_DIV2 (1 << 18)
30244+#define SSI_SRCCR_PSR (1 << 15)
30245+#define SSI_SRCCR_WL(x) ((((x) - 2) >> 1) << 13)
30246+#define SSI_SRCCR_DC(x) (((x) & 0x1f) << 8)
30247+#define SSI_SRCCR_PM(x) (((x) & 0xff) << 0)
30248+
30249+
30250+#define SSI_SFCSR_RFCNT1(x) (((x) & 0xf) << 28)
30251+#define SSI_SFCSR_TFCNT1(x) (((x) & 0xf) << 24)
30252+#define SSI_SFCSR_RFWM1(x) (((x) & 0xf) << 20)
30253+#define SSI_SFCSR_TFWM1(x) (((x) & 0xf) << 16)
30254+#define SSI_SFCSR_RFCNT0(x) (((x) & 0xf) << 12)
30255+#define SSI_SFCSR_TFCNT0(x) (((x) & 0xf) << 8)
30256+#define SSI_SFCSR_RFWM0(x) (((x) & 0xf) << 4)
30257+#define SSI_SFCSR_TFWM0(x) (((x) & 0xf) << 0)
30258+
30259+#define SSI_STR_TEST (1 << 15)
30260+#define SSI_STR_RCK2TCK (1 << 14)
30261+#define SSI_STR_RFS2TFS (1 << 13)
30262+#define SSI_STR_RXSTATE(x) (((x) & 0xf) << 8)
30263+#define SSI_STR_TXD2RXD (1 << 7)
30264+#define SSI_STR_TCK2RCK (1 << 6)
30265+#define SSI_STR_TFS2RFS (1 << 5)
30266+#define SSI_STR_TXSTATE(x) (((x) & 0xf) << 0)
30267+
30268+#define SSI_SOR_CLKOFF (1 << 6)
30269+#define SSI_SOR_RX_CLR (1 << 5)
30270+#define SSI_SOR_TX_CLR (1 << 4)
30271+#define SSI_SOR_INIT (1 << 3)
30272+#define SSI_SOR_WAIT(x) (((x) & 0x3) << 1)
30273+#define SSI_SOR_SYNRST (1 << 0)
30274+
30275+#define SSI_SACNT_FRDIV(x) (((x) & 0x3f) << 5)
30276+#define SSI_SACNT_WR (x << 4)
30277+#define SSI_SACNT_RD (x << 3)
30278+#define SSI_SACNT_TIF (x << 2)
30279+#define SSI_SACNT_FV (x << 1)
30280+#define SSI_SACNT_A97EN (x << 0)
30281+
30282+
30283+/* AUDMUX registers */
30284+#define AUDMUX_HPCR1 __REG(IMX_AUDMUX_BASE + 0x00)
30285+#define AUDMUX_HPCR2 __REG(IMX_AUDMUX_BASE + 0x04)
30286+#define AUDMUX_HPCR3 __REG(IMX_AUDMUX_BASE + 0x08)
30287+#define AUDMUX_PPCR1 __REG(IMX_AUDMUX_BASE + 0x10)
30288+#define AUDMUX_PPCR2 __REG(IMX_AUDMUX_BASE + 0x14)
30289+#define AUDMUX_PPCR3 __REG(IMX_AUDMUX_BASE + 0x18)
30290+
30291+#define AUDMUX_HPCR_TFSDIR (1 << 31)
30292+#define AUDMUX_HPCR_TCLKDIR (1 << 30)
30293+#define AUDMUX_HPCR_TFCSEL_TX (0 << 26)
30294+#define AUDMUX_HPCR_TFCSEL_RX (8 << 26)
30295+#define AUDMUX_HPCR_TFCSEL(x) (((x) & 0x7) << 26)
30296+#define AUDMUX_HPCR_RFSDIR (1 << 25)
30297+#define AUDMUX_HPCR_RCLKDIR (1 << 24)
30298+#define AUDMUX_HPCR_RFCSEL_TX (0 << 20)
30299+#define AUDMUX_HPCR_RFCSEL_RX (8 << 20)
30300+#define AUDMUX_HPCR_RFCSEL(x) (((x) & 0x7) << 20)
30301+#define AUDMUX_HPCR_RXDSEL(x) (((x) & 0x7) << 13)
30302+#define AUDMUX_HPCR_SYN (1 << 12)
30303+#define AUDMUX_HPCR_TXRXEN (1 << 10)
30304+#define AUDMUX_HPCR_INMEN (1 << 8)
30305+#define AUDMUX_HPCR_INMMASK(x) (((x) & 0xff) << 0)
30306+
30307+#define AUDMUX_PPCR_TFSDIR (1 << 31)
30308+#define AUDMUX_PPCR_TCLKDIR (1 << 30)
30309+#define AUDMUX_PPCR_TFCSEL_TX (0 << 26)
30310+#define AUDMUX_PPCR_TFCSEL_RX (8 << 26)
30311+#define AUDMUX_PPCR_TFCSEL(x) (((x) & 0x7) << 26)
30312+#define AUDMUX_PPCR_RFSDIR (1 << 25)
30313+#define AUDMUX_PPCR_RCLKDIR (1 << 24)
30314+#define AUDMUX_PPCR_RFCSEL_TX (0 << 20)
30315+#define AUDMUX_PPCR_RFCSEL_RX (8 << 20)
30316+#define AUDMUX_PPCR_RFCSEL(x) (((x) & 0x7) << 20)
30317+#define AUDMUX_PPCR_RXDSEL(x) (((x) & 0x7) << 13)
30318+#define AUDMUX_PPCR_SYN (1 << 12)
30319+#define AUDMUX_PPCR_TXRXEN (1 << 10)
30320+
30321+
30322+#endif
30323Index: linux-2.6-pxa-new/sound/soc/imx/imx31-pcm.c
30324===================================================================
30325--- /dev/null
30326+++ linux-2.6-pxa-new/sound/soc/imx/imx31-pcm.c
30327@@ -0,0 +1,454 @@
30328+/*
30329+ * linux/sound/arm/mxc-pcm.c -- ALSA SoC interface for the Freescale i.MX CPU's
30330+ *
30331+ * Copyright 2006 Wolfson Microelectronics PLC.
30332+ * Author: Liam Girdwood
30333+ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
30334+ *
30335+ * Based on pxa2xx-pcm.c by Nicolas Pitre, (C) 2004 MontaVista Software, Inc.
30336+ * and on mxc-alsa-mc13783 (C) 2006 Freescale.
30337+ *
30338+ * This program is free software; you can redistribute it and/or modify
30339+ * it under the terms of the GNU General Public License version 2 as
30340+ * published by the Free Software Foundation.
30341+ *
30342+ * Revision history
30343+ * 29th Aug 2006 Initial version.
30344+ *
30345+ */
30346+
30347+#include <linux/module.h>
30348+#include <linux/init.h>
30349+#include <linux/platform_device.h>
30350+#include <linux/slab.h>
30351+#include <linux/dma-mapping.h>
30352+#include <sound/driver.h>
30353+#include <sound/core.h>
30354+#include <sound/pcm.h>
30355+#include <sound/pcm_params.h>
30356+#include <sound/soc.h>
30357+#include <asm/dma.h>
30358+#include <asm/hardware.h>
30359+
30360+#include "imx-pcm.h"
30361+
30362+/* debug */
30363+#define IMX_DEBUG 0
30364+#if IMX_DEBUG
30365+#define dbg(format, arg...) printk(format, ## arg)
30366+#else
30367+#define dbg(format, arg...)
30368+#endif
30369+
30370+static const struct snd_pcm_hardware mxc_pcm_hardware = {
30371+ .info = (SNDRV_PCM_INFO_INTERLEAVED |
30372+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
30373+ SNDRV_PCM_INFO_MMAP |
30374+ SNDRV_PCM_INFO_MMAP_VALID |
30375+ SNDRV_PCM_INFO_PAUSE |
30376+ SNDRV_PCM_INFO_RESUME),
30377+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
30378+ SNDRV_PCM_FMTBIT_S24_LE,
30379+ .buffer_bytes_max = 32 * 1024,
30380+ .period_bytes_min = 64,
30381+ .period_bytes_max = 8 * 1024,
30382+ .periods_min = 2,
30383+ .periods_max = 255,
30384+ .fifo_size = 0,
30385+};
30386+
30387+struct mxc_runtime_data {
30388+ int dma_ch;
30389+ struct mxc_pcm_dma_param *dma_params;
30390+};
30391+
30392+/*!
30393+ * This function stops the current dma transfert for playback
30394+ * and clears the dma pointers.
30395+ *
30396+ * @param substream pointer to the structure of the current stream.
30397+ *
30398+ */
30399+static void audio_stop_dma(struct snd_pcm_substream *substream)
30400+{
30401+ struct snd_pcm_runtime *runtime = substream->runtime;
30402+ struct mxc_runtime_data *prtd = runtime->private_data;
30403+ unsigned int dma_size = frames_to_bytes(runtime, runtime->period_size);
30404+ unsigned int offset dma_size * s->periods;
30405+ unsigned long flags;
30406+
30407+ spin_lock_irqsave(&prtd->dma_lock, flags);
30408+
30409+ dbg("MXC : audio_stop_dma active = 0\n");
30410+ prtd->active = 0;
30411+ prtd->period = 0;
30412+ prtd->periods = 0;
30413+
30414+ /* this stops the dma channel and clears the buffer ptrs */
30415+ mxc_dma_stop(prtd->dma_wchannel);
30416+ if(substream == SNDRV_PCM_STREAM_PLAYBACK)
30417+ dma_unmap_single(NULL, runtime->dma_addr + offset, dma_size,
30418+ DMA_TO_DEVICE);
30419+ else
30420+ dma_unmap_single(NULL, runtime->dma_addr + offset, dma_size,
30421+ DMA_FROM_DEVICE);
30422+
30423+ spin_unlock_irqrestore(&prtd->dma_lock, flags);
30424+}
30425+
30426+/*!
30427+ * This function is called whenever a new audio block needs to be
30428+ * transferred to mc13783. The function receives the address and the size
30429+ * of the new block and start a new DMA transfer.
30430+ *
30431+ * @param substream pointer to the structure of the current stream.
30432+ *
30433+ */
30434+static int dma_new_period(struct snd_pcm_substream *substream)
30435+{
30436+ struct snd_pcm_runtime *runtime = substream->runtime;
30437+ struct mxc_runtime_data *prtd = runtime->private_data;
30438+ unsigned int dma_size;
30439+ unsigned int offset;
30440+ int ret=0;
30441+ dma_request_t sdma_request;
30442+
30443+ if (prtd->active){
30444+ memset(&sdma_request, 0, sizeof(dma_request_t));
30445+ dma_size = frames_to_bytes(runtime, runtime->period_size);
30446+ dbg("s->period (%x) runtime->periods (%d)\n",
30447+ s->period,runtime->periods);
30448+ dbg("runtime->period_size (%d) dma_size (%d)\n",
30449+ (unsigned int)runtime->period_size,
30450+ runtime->dma_bytes);
30451+
30452+ offset = dma_size * prtd->period;
30453+ snd_assert(dma_size <= DMA_BUF_SIZE, );
30454+ if(substream == SNDRV_PCM_STREAM_PLAYBACK)
30455+ sdma_request.sourceAddr = (char*)(dma_map_single(NULL,
30456+ runtime->dma_area + offset, dma_size, DMA_TO_DEVICE));
30457+ else
30458+ sdma_request.destAddr = (char*)(dma_map_single(NULL,
30459+ runtime->dma_area + offset, dma_size, DMA_FROM_DEVICE));
30460+ sdma_request.count = dma_size;
30461+
30462+ dbg("MXC: Start DMA offset (%d) size (%d)\n", offset,
30463+ runtime->dma_bytes);
30464+
30465+ mxc_dma_set_config(prtd->dma_wchannel, &sdma_request, 0);
30466+ if((ret = mxc_dma_start(prtd->dma_wchannel)) < 0) {
30467+ dbg("audio_process_dma: cannot queue DMA buffer\
30468+ (%i)\n", ret);
30469+ return err;
30470+ }
30471+ prtd->tx_spin = 1; /* FGA little trick to retrieve DMA pos */
30472+ prtd->period++;
30473+ prtd->period %= runtime->periods;
30474+ }
30475+ return ret;
30476+}
30477+
30478+
30479+/*!
30480+ * This is a callback which will be called
30481+ * when a TX transfer finishes. The call occurs
30482+ * in interrupt context.
30483+ *
30484+ * @param dat pointer to the structure of the current stream.
30485+ *
30486+ */
30487+static void audio_dma_irq(void *data)
30488+{
30489+ struct snd_pcm_substream *substream;
30490+ struct snd_pcm_runtime *runtime;
30491+ struct mxc_runtime_data *prtd;
30492+ unsigned int dma_size;
30493+ unsigned int previous_period;
30494+ unsigned int offset;
30495+
30496+ substream = data;
30497+ runtime = substream->runtime;
30498+ prtd = runtime->private_data;
30499+ previous_period = prtd->periods;
30500+ dma_size = frames_to_bytes(runtime, runtime->period_size);
30501+ offset = dma_size * previous_period;
30502+
30503+ prtd->tx_spin = 0;
30504+ prtd->periods++;
30505+ prtd->periods %= runtime->periods;
30506+
30507+ /*
30508+ * Give back to the CPU the access to the non cached memory
30509+ */
30510+ if(substream == SNDRV_PCM_STREAM_PLAYBACK)
30511+ dma_unmap_single(NULL, runtime->dma_addr + offset, dma_size,
30512+ DMA_TO_DEVICE);
30513+ else
30514+ dma_unmap_single(NULL, runtime->dma_addr + offset, dma_size,
30515+ DMA_FROM_DEVICE);
30516+ /*
30517+ * If we are getting a callback for an active stream then we inform
30518+ * the PCM middle layer we've finished a period
30519+ */
30520+ if (prtd->active)
30521+ snd_pcm_period_elapsed(substream);
30522+
30523+ /*
30524+ * Trig next DMA transfer
30525+ */
30526+ dma_new_period(substream);
30527+}
30528+
30529+/*!
30530+ * This function configures the hardware to allow audio
30531+ * playback operations. It is called by ALSA framework.
30532+ *
30533+ * @param substream pointer to the structure of the current stream.
30534+ *
30535+ * @return 0 on success, -1 otherwise.
30536+ */
30537+static int
30538+snd_mxc_prepare(struct snd_pcm_substream *substream)
30539+{
30540+ struct mxc_runtime_data *prtd = runtime->private_data;
30541+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
30542+ int ret = 0;
30543+ prtd->period = 0;
30544+ prtd->periods = 0;
30545+
30546+ dma_channel_params params;
30547+ int channel = 0; // passed in ?
30548+
30549+ if ((ret = mxc_request_dma(&channel, "ALSA TX SDMA") < 0)){
30550+ dbg("error requesting a write dma channel\n");
30551+ return ret;
30552+ }
30553+
30554+ /* configure DMA params */
30555+ memset(&params, 0, sizeof(dma_channel_params));
30556+ params.bd_number = 1;
30557+ params.arg = s;
30558+ params.callback = callback;
30559+ params.transfer_type = emi_2_per;
30560+ params.watermark_level = SDMA_TXFIFO_WATERMARK;
30561+ params.word_size = TRANSFER_16BIT;
30562+ //dbg(KERN_ERR "activating connection SSI1 - SDMA\n");
30563+ params.per_address = SSI1_BASE_ADDR;
30564+ params.event_id = DMA_REQ_SSI1_TX1;
30565+ params.peripheral_type = SSI;
30566+
30567+ /* set up chn with params */
30568+ mxc_dma_setup_channel(channel, &params);
30569+ s->dma_wchannel = channel;
30570+
30571+ return ret;
30572+}
30573+
30574+static int mxc_pcm_hw_params(struct snd_pcm_substream *substream,
30575+ struct snd_pcm_hw_params *params)
30576+{
30577+ struct snd_pcm_runtime *runtime = substream->runtime;
30578+ int ret;
30579+
30580+ if((ret=snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
30581+ return ret;
30582+ runtime->dma_addr = virt_to_phys(runtime->dma_area);
30583+
30584+ return ret;
30585+}
30586+
30587+static int mxc_pcm_hw_free(struct snd_pcm_substream *substream)
30588+{
30589+ return snd_pcm_lib_free_pages(substream);
30590+}
30591+
30592+static int mxc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
30593+{
30594+ struct mxc_runtime_data *prtd = substream->runtime->private_data;
30595+ int ret = 0;
30596+
30597+ switch (cmd) {
30598+ case SNDRV_PCM_TRIGGER_START:
30599+ prtd->tx_spin = 0;
30600+ /* requested stream startup */
30601+ prtd->active = 1;
30602+ ret = dma_new_period(substream);
30603+ break;
30604+ case SNDRV_PCM_TRIGGER_STOP:
30605+ /* requested stream shutdown */
30606+ ret = audio_stop_dma(substream);
30607+ break;
30608+ case SNDRV_PCM_TRIGGER_SUSPEND:
30609+ prtd->active = 0;
30610+ prtd->periods = 0;
30611+ break;
30612+ case SNDRV_PCM_TRIGGER_RESUME:
30613+ prtd->active = 1;
30614+ prtd->tx_spin = 0;
30615+ ret = dma_new_period(substream);
30616+ break;
30617+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
30618+ prtd->active = 0;
30619+ break;
30620+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
30621+ prtd->active = 1;
30622+ if (prtd->old_offset) {
30623+ prtd->tx_spin = 0;
30624+ ret = dma_new_period(substream);
30625+ }
30626+ break;
30627+ default:
30628+ ret = -EINVAL;
30629+ break;
30630+ }
30631+
30632+ return ret;
30633+}
30634+
30635+static snd_pcm_uframes_t mxc_pcm_pointer(struct snd_pcm_substream *substream)
30636+{
30637+ struct snd_pcm_runtime *runtime = substream->runtime;
30638+ struct mxc_runtime_data *prtd = runtime->private_data;
30639+ unsigned int offset = 0;
30640+
30641+ /* tx_spin value is used here to check if a transfert is active */
30642+ if (prtd->tx_spin){
30643+ offset = (runtime->period_size * (prtd->periods)) +
30644+ (runtime->period_size >> 1);
30645+ if (offset >= runtime->buffer_size)
30646+ offset = runtime->period_size >> 1;
30647+ } else {
30648+ offset = (runtime->period_size * (s->periods));
30649+ if (offset >= runtime->buffer_size)
30650+ offset = 0;
30651+ }
30652+
30653+ return offset;
30654+}
30655+
30656+
30657+static int mxc_pcm_open(struct snd_pcm_substream *substream)
30658+{
30659+ struct snd_pcm_runtime *runtime = substream->runtime;
30660+ struct mxc_runtime_data *prtd;
30661+ int ret;
30662+
30663+ snd_soc_set_runtime_hwparams(substream, &mxc_pcm_hardware);
30664+
30665+ if ((err = snd_pcm_hw_constraint_integer(runtime,
30666+ SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
30667+ return err;
30668+ if ((err = snd_pcm_hw_constraint_list(runtime, 0,
30669+ SNDRV_PCM_HW_PARAM_RATE, &hw_playback_rates)) < 0)
30670+ return err;
30671+ msleep(10); // liam - why
30672+
30673+ /* setup DMA controller for playback */
30674+ if((err = configure_write_channel(&mxc_mc13783->s[SNDRV_PCM_STREAM_PLAYBACK],
30675+ audio_dma_irq)) < 0 )
30676+ return err;
30677+
30678+ if((prtd = kzalloc(sizeof(struct mxc_runtime_data), GFP_KERNEL)) == NULL) {
30679+ ret = -ENOMEM;
30680+ goto out;
30681+ }
30682+
30683+ runtime->private_data = prtd;
30684+ return 0;
30685+
30686+ err1:
30687+ kfree(prtd);
30688+ out:
30689+ return ret;
30690+}
30691+
30692+static int mxc_pcm_close(struct snd_pcm_substream *substream)
30693+{
30694+ struct snd_pcm_runtime *runtime = substream->runtime;
30695+ struct mxc_runtime_data *prtd = runtime->private_data;
30696+
30697+// mxc_mc13783_t *chip;
30698+ audio_stream_t *s;
30699+ device_data_t* device;
30700+ int ssi;
30701+
30702+ //chip = snd_pcm_substream_chip(substream);
30703+ s = &chip->s[substream->pstr->stream];
30704+ device = &s->stream_device;
30705+ ssi = device->ssi;
30706+
30707+ //disable_stereodac();
30708+
30709+ ssi_transmit_enable(ssi, false);
30710+ ssi_interrupt_disable(ssi, ssi_tx_dma_interrupt_enable);
30711+ ssi_tx_fifo_enable(ssi, ssi_fifo_0, false);
30712+ ssi_enable(ssi, false);
30713+
30714+ chip->s[substream->pstr->stream].stream = NULL;
30715+
30716+ return 0;
30717+}
30718+
30719+static int
30720+mxc_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma)
30721+{
30722+ struct snd_pcm_runtime *runtime = substream->runtime;
30723+ return dma_mmap_writecombine(substream->pcm->card->dev, vma,
30724+ runtime->dma_area,
30725+ runtime->dma_addr,
30726+ runtime->dma_bytes);
30727+}
30728+
30729+struct snd_pcm_ops mxc_pcm_ops = {
30730+ .open = mxc_pcm_open,
30731+ .close = mxc_pcm_close,
30732+ .ioctl = snd_pcm_lib_ioctl,
30733+ .hw_params = mxc_pcm_hw_params,
30734+ .hw_free = mxc_pcm_hw_free,
30735+ .prepare = mxc_pcm_prepare,
30736+ .trigger = mxc_pcm_trigger,
30737+ .pointer = mxc_pcm_pointer,
30738+ .mmap = mxc_pcm_mmap,
30739+};
30740+
30741+static u64 mxc_pcm_dmamask = 0xffffffff;
30742+
30743+int mxc_pcm_new(struct snd_card *card, struct snd_soc_codec_dai *dai,
30744+ struct snd_pcm *pcm)
30745+{
30746+ int ret = 0;
30747+
30748+ if (!card->dev->dma_mask)
30749+ card->dev->dma_mask = &mxc_pcm_dmamask;
30750+ if (!card->dev->coherent_dma_mask)
30751+ card->dev->coherent_dma_mask = 0xffffffff;
30752+
30753+ if (dai->playback.channels_min) {
30754+ ret = mxc_pcm_preallocate_dma_buffer(pcm,
30755+ SNDRV_PCM_STREAM_PLAYBACK);
30756+ if (ret)
30757+ goto out;
30758+ }
30759+
30760+ if (dai->capture.channels_min) {
30761+ ret = mxc_pcm_preallocate_dma_buffer(pcm,
30762+ SNDRV_PCM_STREAM_CAPTURE);
30763+ if (ret)
30764+ goto out;
30765+ }
30766+ out:
30767+ return ret;
30768+}
30769+
30770+struct snd_soc_platform mxc_soc_platform = {
30771+ .name = "mxc-audio",
30772+ .pcm_ops = &mxc_pcm_ops,
30773+ .pcm_new = mxc_pcm_new,
30774+ .pcm_free = mxc_pcm_free_dma_buffers,
30775+};
30776+
30777+EXPORT_SYMBOL_GPL(mxc_soc_platform);
30778+
30779+MODULE_AUTHOR("Liam Girdwood");
30780+MODULE_DESCRIPTION("Freescale i.MX PCM DMA module");
30781+MODULE_LICENSE("GPL");
30782Index: linux-2.6-pxa-new/sound/soc/imx/imx31-pcm.h
30783===================================================================
30784--- /dev/null
30785+++ linux-2.6-pxa-new/sound/soc/imx/imx31-pcm.h
30786@@ -0,0 +1,237 @@
30787+/*
30788+ * mxc-pcm.h :- ASoC platform header for Freescale i.MX
30789+ *
30790+ * This program is free software; you can redistribute it and/or modify
30791+ * it under the terms of the GNU General Public License version 2 as
30792+ * published by the Free Software Foundation.
30793+ */
30794+
30795+#ifndef _MXC_PCM_H
30796+#define _MXC_PCM_H
30797+
30798+struct {
30799+ char *name; /* stream identifier */
30800+ dma_channel_params dma_params;
30801+} mxc_pcm_dma_param;
30802+
30803+extern struct snd_soc_cpu_dai mxc_ssi_dai[3];
30804+
30805+/* platform data */
30806+extern struct snd_soc_platform mxc_soc_platform;
30807+extern struct snd_ac97_bus_ops mxc_ac97_ops;
30808+
30809+/* temp until imx-regs.h is up2date */
30810+#define SSI1_STX0 __REG(IMX_SSI1_BASE + 0x00)
30811+#define SSI1_STX0_PHYS __PHYS_REG(IMX_SSI1_BASE + 0x00)
30812+#define SSI1_STX1 __REG(IMX_SSI1_BASE + 0x04)
30813+#define SSI1_STX1_PHYS __PHYS_REG(IMX_SSI1_BASE + 0x04)
30814+#define SSI1_SRX0 __REG(IMX_SSI1_BASE + 0x08)
30815+#define SSI1_SRX0_PHYS __PHYS_REG(IMX_SSI1_BASE + 0x08)
30816+#define SSI1_SRX1 __REG(IMX_SSI1_BASE + 0x0c)
30817+#define SSI1_SRX1_PHYS __PHYS_REG(IMX_SSI1_BASE + 0x0c)
30818+#define SSI1_SCR __REG(IMX_SSI1_BASE + 0x10)
30819+#define SSI1_SISR __REG(IMX_SSI1_BASE + 0x14)
30820+#define SSI1_SIER __REG(IMX_SSI1_BASE + 0x18)
30821+#define SSI1_STCR __REG(IMX_SSI1_BASE + 0x1c)
30822+#define SSI1_SRCR __REG(IMX_SSI1_BASE + 0x20)
30823+#define SSI1_STCCR __REG(IMX_SSI1_BASE + 0x24)
30824+#define SSI1_SRCCR __REG(IMX_SSI1_BASE + 0x28)
30825+#define SSI1_SFCSR __REG(IMX_SSI1_BASE + 0x2c)
30826+#define SSI1_STR __REG(IMX_SSI1_BASE + 0x30)
30827+#define SSI1_SOR __REG(IMX_SSI1_BASE + 0x34)
30828+#define SSI1_SACNT __REG(IMX_SSI1_BASE + 0x38)
30829+#define SSI1_SACADD __REG(IMX_SSI1_BASE + 0x3c)
30830+#define SSI1_SACDAT __REG(IMX_SSI1_BASE + 0x40)
30831+#define SSI1_SATAG __REG(IMX_SSI1_BASE + 0x44)
30832+#define SSI1_STMSK __REG(IMX_SSI1_BASE + 0x48)
30833+#define SSI1_SRMSK __REG(IMX_SSI1_BASE + 0x4c)
30834+
30835+#define SSI2_STX0 __REG(IMX_SSI2_BASE + 0x00)
30836+#define SSI2_STX0_PHYS __PHYS_REG(IMX_SSI2_BASE + 0x00)
30837+#define SSI2_STX1 __REG(IMX_SSI2_BASE + 0x04)
30838+#define SSI2_STX1_PHYS __PHYS_REG(IMX_SSI2_BASE + 0x04)
30839+#define SSI2_SRX0 __REG(IMX_SSI2_BASE + 0x08)
30840+#define SSI2_SRX0_PHYS __PHYS_REG(IMX_SSI2_BASE + 0x08)
30841+#define SSI2_SRX1 __REG(IMX_SSI2_BASE + 0x0c)
30842+#define SSI2_SRX1_PHYS __PHYS_REG(IMX_SSI2_BASE + 0x0c)
30843+#define SSI2_SCR __REG(IMX_SSI2_BASE + 0x10)
30844+#define SSI2_SISR __REG(IMX_SSI2_BASE + 0x14)
30845+#define SSI2_SIER __REG(IMX_SSI2_BASE + 0x18)
30846+#define SSI2_STCR __REG(IMX_SSI2_BASE + 0x1c)
30847+#define SSI2_SRCR __REG(IMX_SSI2_BASE + 0x20)
30848+#define SSI2_STCCR __REG(IMX_SSI2_BASE + 0x24)
30849+#define SSI2_SRCCR __REG(IMX_SSI2_BASE + 0x28)
30850+#define SSI2_SFCSR __REG(IMX_SSI2_BASE + 0x2c)
30851+#define SSI2_STR __REG(IMX_SSI2_BASE + 0x30)
30852+#define SSI2_SOR __REG(IMX_SSI2_BASE + 0x34)
30853+#define SSI2_SACNT __REG(IMX_SSI2_BASE + 0x38)
30854+#define SSI2_SACADD __REG(IMX_SSI2_BASE + 0x3c)
30855+#define SSI2_SACDAT __REG(IMX_SSI2_BASE + 0x40)
30856+#define SSI2_SATAG __REG(IMX_SSI2_BASE + 0x44)
30857+#define SSI2_STMSK __REG(IMX_SSI2_BASE + 0x48)
30858+#define SSI2_SRMSK __REG(IMX_SSI2_BASE + 0x4c)
30859+
30860+#define SSI_SCR_CLK_IST (1 << 9)
30861+#define SSI_SCR_TCH_EN (1 << 8)
30862+#define SSI_SCR_SYS_CLK_EN (1 << 7)
30863+#define SSI_SCR_I2S_MODE_NORM (0 << 5)
30864+#define SSI_SCR_I2S_MODE_MSTR (1 << 5)
30865+#define SSI_SCR_I2S_MODE_SLAVE (2 << 5)
30866+#define SSI_SCR_SYN (1 << 4)
30867+#define SSI_SCR_NET (1 << 3)
30868+#define SSI_SCR_RE (1 << 2)
30869+#define SSI_SCR_TE (1 << 1)
30870+#define SSI_SCR_SSIEN (1 << 0)
30871+
30872+#define SSI_SISR_CMDAU (1 << 18)
30873+#define SSI_SISR_CMDDU (1 << 17)
30874+#define SSI_SISR_RXT (1 << 16)
30875+#define SSI_SISR_RDR1 (1 << 15)
30876+#define SSI_SISR_RDR0 (1 << 14)
30877+#define SSI_SISR_TDE1 (1 << 13)
30878+#define SSI_SISR_TDE0 (1 << 12)
30879+#define SSI_SISR_ROE1 (1 << 11)
30880+#define SSI_SISR_ROE0 (1 << 10)
30881+#define SSI_SISR_TUE1 (1 << 9)
30882+#define SSI_SISR_TUE0 (1 << 8)
30883+#define SSI_SISR_TFS (1 << 7)
30884+#define SSI_SISR_RFS (1 << 6)
30885+#define SSI_SISR_TLS (1 << 5)
30886+#define SSI_SISR_RLS (1 << 4)
30887+#define SSI_SISR_RFF1 (1 << 3)
30888+#define SSI_SISR_RFF0 (1 << 2)
30889+#define SSI_SISR_TFE1 (1 << 1)
30890+#define SSI_SISR_TFE0 (1 << 0)
30891+
30892+#define SSI_SIER_RDMAE (1 << 22)
30893+#define SSI_SIER_RIE (1 << 21)
30894+#define SSI_SIER_TDMAE (1 << 20)
30895+#define SSI_SIER_TIE (1 << 19)
30896+#define SSI_SIER_CMDAU_EN (1 << 18)
30897+#define SSI_SIER_CMDDU_EN (1 << 17)
30898+#define SSI_SIER_RXT_EN (1 << 16)
30899+#define SSI_SIER_RDR1_EN (1 << 15)
30900+#define SSI_SIER_RDR0_EN (1 << 14)
30901+#define SSI_SIER_TDE1_EN (1 << 13)
30902+#define SSI_SIER_TDE0_EN (1 << 12)
30903+#define SSI_SIER_ROE1_EN (1 << 11)
30904+#define SSI_SIER_ROE0_EN (1 << 10)
30905+#define SSI_SIER_TUE1_EN (1 << 9)
30906+#define SSI_SIER_TUE0_EN (1 << 8)
30907+#define SSI_SIER_TFS_EN (1 << 7)
30908+#define SSI_SIER_RFS_EN (1 << 6)
30909+#define SSI_SIER_TLS_EN (1 << 5)
30910+#define SSI_SIER_RLS_EN (1 << 4)
30911+#define SSI_SIER_RFF1_EN (1 << 3)
30912+#define SSI_SIER_RFF0_EN (1 << 2)
30913+#define SSI_SIER_TFE1_EN (1 << 1)
30914+#define SSI_SIER_TFE0_EN (1 << 0)
30915+
30916+#define SSI_STCR_TXBIT0 (1 << 9)
30917+#define SSI_STCR_TFEN1 (1 << 8)
30918+#define SSI_STCR_TFEN0 (1 << 7)
30919+#define SSI_STCR_TFDIR (1 << 6)
30920+#define SSI_STCR_TXDIR (1 << 5)
30921+#define SSI_STCR_TSHFD (1 << 4)
30922+#define SSI_STCR_TSCKP (1 << 3)
30923+#define SSI_STCR_TFSI (1 << 2)
30924+#define SSI_STCR_TFSL (1 << 1)
30925+#define SSI_STCR_TEFS (1 << 0)
30926+
30927+#define SSI_SRCR_RXBIT0 (1 << 9)
30928+#define SSI_SRCR_RFEN1 (1 << 8)
30929+#define SSI_SRCR_RFEN0 (1 << 7)
30930+#define SSI_SRCR_RFDIR (1 << 6)
30931+#define SSI_SRCR_RXDIR (1 << 5)
30932+#define SSI_SRCR_RSHFD (1 << 4)
30933+#define SSI_SRCR_RSCKP (1 << 3)
30934+#define SSI_SRCR_RFSI (1 << 2)
30935+#define SSI_SRCR_RFSL (1 << 1)
30936+#define SSI_SRCR_REFS (1 << 0)
30937+
30938+#define SSI_STCCR_DIV2 (1 << 18)
30939+#define SSI_STCCR_PSR (1 << 15)
30940+#define SSI_STCCR_WL(x) ((((x) - 2) >> 1) << 13)
30941+#define SSI_STCCR_DC(x) (((x) & 0x1f) << 8)
30942+#define SSI_STCCR_PM(x) (((x) & 0xff) << 0)
30943+
30944+#define SSI_SRCCR_DIV2 (1 << 18)
30945+#define SSI_SRCCR_PSR (1 << 15)
30946+#define SSI_SRCCR_WL(x) ((((x) - 2) >> 1) << 13)
30947+#define SSI_SRCCR_DC(x) (((x) & 0x1f) << 8)
30948+#define SSI_SRCCR_PM(x) (((x) & 0xff) << 0)
30949+
30950+
30951+#define SSI_SFCSR_RFCNT1(x) (((x) & 0xf) << 28)
30952+#define SSI_SFCSR_TFCNT1(x) (((x) & 0xf) << 24)
30953+#define SSI_SFCSR_RFWM1(x) (((x) & 0xf) << 20)
30954+#define SSI_SFCSR_TFWM1(x) (((x) & 0xf) << 16)
30955+#define SSI_SFCSR_RFCNT0(x) (((x) & 0xf) << 12)
30956+#define SSI_SFCSR_TFCNT0(x) (((x) & 0xf) << 8)
30957+#define SSI_SFCSR_RFWM0(x) (((x) & 0xf) << 4)
30958+#define SSI_SFCSR_TFWM0(x) (((x) & 0xf) << 0)
30959+
30960+#define SSI_STR_TEST (1 << 15)
30961+#define SSI_STR_RCK2TCK (1 << 14)
30962+#define SSI_STR_RFS2TFS (1 << 13)
30963+#define SSI_STR_RXSTATE(x) (((x) & 0xf) << 8)
30964+#define SSI_STR_TXD2RXD (1 << 7)
30965+#define SSI_STR_TCK2RCK (1 << 6)
30966+#define SSI_STR_TFS2RFS (1 << 5)
30967+#define SSI_STR_TXSTATE(x) (((x) & 0xf) << 0)
30968+
30969+#define SSI_SOR_CLKOFF (1 << 6)
30970+#define SSI_SOR_RX_CLR (1 << 5)
30971+#define SSI_SOR_TX_CLR (1 << 4)
30972+#define SSI_SOR_INIT (1 << 3)
30973+#define SSI_SOR_WAIT(x) (((x) & 0x3) << 1)
30974+#define SSI_SOR_SYNRST (1 << 0)
30975+
30976+#define SSI_SACNT_FRDIV(x) (((x) & 0x3f) << 5)
30977+#define SSI_SACNT_WR (x << 4)
30978+#define SSI_SACNT_RD (x << 3)
30979+#define SSI_SACNT_TIF (x << 2)
30980+#define SSI_SACNT_FV (x << 1)
30981+#define SSI_SACNT_A97EN (x << 0)
30982+
30983+
30984+/* AUDMUX registers */
30985+#define AUDMUX_HPCR1 __REG(IMX_AUDMUX_BASE + 0x00)
30986+#define AUDMUX_HPCR2 __REG(IMX_AUDMUX_BASE + 0x04)
30987+#define AUDMUX_HPCR3 __REG(IMX_AUDMUX_BASE + 0x08)
30988+#define AUDMUX_PPCR1 __REG(IMX_AUDMUX_BASE + 0x10)
30989+#define AUDMUX_PPCR2 __REG(IMX_AUDMUX_BASE + 0x14)
30990+#define AUDMUX_PPCR3 __REG(IMX_AUDMUX_BASE + 0x18)
30991+
30992+#define AUDMUX_HPCR_TFSDIR (1 << 31)
30993+#define AUDMUX_HPCR_TCLKDIR (1 << 30)
30994+#define AUDMUX_HPCR_TFCSEL_TX (0 << 26)
30995+#define AUDMUX_HPCR_TFCSEL_RX (8 << 26)
30996+#define AUDMUX_HPCR_TFCSEL(x) (((x) & 0x7) << 26)
30997+#define AUDMUX_HPCR_RFSDIR (1 << 25)
30998+#define AUDMUX_HPCR_RCLKDIR (1 << 24)
30999+#define AUDMUX_HPCR_RFCSEL_TX (0 << 20)
31000+#define AUDMUX_HPCR_RFCSEL_RX (8 << 20)
31001+#define AUDMUX_HPCR_RFCSEL(x) (((x) & 0x7) << 20)
31002+#define AUDMUX_HPCR_RXDSEL(x) (((x) & 0x7) << 13)
31003+#define AUDMUX_HPCR_SYN (1 << 12)
31004+#define AUDMUX_HPCR_TXRXEN (1 << 10)
31005+#define AUDMUX_HPCR_INMEN (1 << 8)
31006+#define AUDMUX_HPCR_INMMASK(x) (((x) & 0xff) << 0)
31007+
31008+#define AUDMUX_PPCR_TFSDIR (1 << 31)
31009+#define AUDMUX_PPCR_TCLKDIR (1 << 30)
31010+#define AUDMUX_PPCR_TFCSEL_TX (0 << 26)
31011+#define AUDMUX_PPCR_TFCSEL_RX (8 << 26)
31012+#define AUDMUX_PPCR_TFCSEL(x) (((x) & 0x7) << 26)
31013+#define AUDMUX_PPCR_RFSDIR (1 << 25)
31014+#define AUDMUX_PPCR_RCLKDIR (1 << 24)
31015+#define AUDMUX_PPCR_RFCSEL_TX (0 << 20)
31016+#define AUDMUX_PPCR_RFCSEL_RX (8 << 20)
31017+#define AUDMUX_PPCR_RFCSEL(x) (((x) & 0x7) << 20)
31018+#define AUDMUX_PPCR_RXDSEL(x) (((x) & 0x7) << 13)
31019+#define AUDMUX_PPCR_SYN (1 << 12)
31020+#define AUDMUX_PPCR_TXRXEN (1 << 10)
31021+
31022+
31023+#endif
31024Index: linux-2.6-pxa-new/sound/soc/s3c24xx/s3c24xx-i2s.c
31025===================================================================
31026--- /dev/null
31027+++ linux-2.6-pxa-new/sound/soc/s3c24xx/s3c24xx-i2s.c
31028@@ -0,0 +1,271 @@
31029+/*
31030+ * s3c24xx-i2s.c -- ALSA Soc Audio Layer
31031+ *
31032+ * Copyright 2005 Wolfson Microelectronics PLC.
31033+ * Author: Graeme Gregory
31034+ * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
31035+ *
31036+ * This program is free software; you can redistribute it and/or modify it
31037+ * under the terms of the GNU General Public License as published by the
31038+ * Free Software Foundation; either version 2 of the License, or (at your
31039+ * option) any later version.
31040+ *
31041+ * Revision history
31042+ * 10th Nov 2006 Initial version.
31043+ */
31044+
31045+#include <linux/init.h>
31046+#include <linux/module.h>
31047+#include <linux/device.h>
31048+#include <linux/delay.h>
31049+#include <linux/clk.h>
31050+#include <sound/driver.h>
31051+#include <sound/core.h>
31052+#include <sound/pcm.h>
31053+#include <sound/initval.h>
31054+#include <sound/soc.h>
31055+
31056+#include <asm/hardware.h>
31057+#include <asm/io.h>
31058+#include <asm/arch/regs-iis.h>
31059+#include <asm/arch/regs-gpio.h>
31060+#include <asm/arch/regs-clock.h>
31061+#include <asm/arch/audio.h>
31062+#include <asm/dma.h>
31063+#include <asm/arch/dma.h>
31064+
31065+#include "s3c24xx-pcm.h"
31066+
31067+/* used to disable sysclk if external crystal is used */
31068+static int extclk = 0;
31069+module_param(extclk, int, 0);
31070+MODULE_PARM_DESC(extclk, "set to 1 to disable s3c24XX i2s sysclk");
31071+
31072+#define S3C24XX_I2S_DAIFMT \
31073+ (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF)
31074+
31075+#define S3C24XX_I2S_DIR \
31076+ (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
31077+
31078+#define S3C24XX_I2S_RATES \
31079+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
31080+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
31081+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
31082+
31083+/* priv is divider */
31084+static struct snd_soc_dai_mode s3c24xx_i2s_modes[] =
31085+{
31086+ /* s3c24xx I2S frame and clock master modes */
31087+ {
31088+ .fmt = S3C24XX_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
31089+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
31090+ .pcmrate = SNDRV_PCM_RATE_44100,
31091+ .pcmdir = S3C24XX_I2S_DIR,
31092+ .flags = SND_SOC_DAI_BFS_RATE,
31093+ .fs = 384,
31094+ .bfs = 32,
31095+ .priv = 0x00
31096+ },
31097+};
31098+
31099+static struct s3c2410_dma_client s3c24xx_dma_client_out = {
31100+ .name = "I2S PCM Stereo out"
31101+};
31102+
31103+static struct s3c2410_dma_client s3c24xx_dma_client_in = {
31104+ .name = "I2S PCM Stereo in"
31105+};
31106+
31107+static s3c24xx_pcm_dma_params_t s3c24xx_i2s_pcm_stereo_out = {
31108+ .client = &s3c24xx_dma_client_out,
31109+ .channel = DMACH_I2S_OUT,
31110+ .dma_addr = S3C2410_PA_IIS+S3C2410_IISFIFO
31111+};
31112+
31113+static s3c24xx_pcm_dma_params_t s3c24xx_i2s_pcm_stereo_in = {
31114+ .client = &s3c24xx_dma_client_in,
31115+ .channel = DMACH_I2S_IN,
31116+ .dma_addr = S3C2410_PA_IIS+S3C2410_IISFIFO
31117+};
31118+
31119+
31120+struct s3c24xx_i2s_port {
31121+ int master;
31122+};
31123+static struct s3c24xx_i2s_port s3c24xx_i2s;
31124+
31125+/* Empty for the s3c24xx platforms */
31126+static int s3c24xx_i2s_startup(struct snd_pcm_substream *substream)
31127+{
31128+ return 0;
31129+}
31130+
31131+static int s3c24xx_i2s_hw_params(struct snd_pcm_substream *substream,
31132+ struct snd_pcm_hw_params *params)
31133+{
31134+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
31135+ unsigned long iiscon;
31136+ unsigned long iismod;
31137+ unsigned long iisfcon;
31138+
31139+ s3c24xx_i2s.master = 0;
31140+ if(rtd->cpu_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CBS_CFS)
31141+ s3c24xx_i2s.master = 1;
31142+
31143+ /* Configure the I2S pins in correct mode */
31144+ s3c2410_gpio_cfgpin(S3C2410_GPE0,S3C2410_GPE0_I2SLRCK);
31145+ if (s3c24xx_i2s.master && !extclk){
31146+ printk("Setting Clock Output as we are Master\n");
31147+ s3c2410_gpio_cfgpin(S3C2410_GPE1,S3C2410_GPE1_I2SSCLK);
31148+ }
31149+ s3c2410_gpio_cfgpin(S3C2410_GPE2,S3C2410_GPE2_CDCLK);
31150+ s3c2410_gpio_cfgpin(S3C2410_GPE3,S3C2410_GPE3_I2SSDI);
31151+ s3c2410_gpio_cfgpin(S3C2410_GPE4,S3C2410_GPE4_I2SSDO);
31152+
31153+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
31154+ {
31155+ rtd->cpu_dai->dma_data = &s3c24xx_i2s_pcm_stereo_out;
31156+ }
31157+ else
31158+ {
31159+ rtd->cpu_dai->dma_data = &s3c24xx_i2s_pcm_stereo_in;
31160+ }
31161+
31162+ /* Working copies of registers */
31163+ iiscon=readl(S3C24XX_VA_IIS+S3C2410_IISCON);
31164+ iismod=readl(S3C24XX_VA_IIS+S3C2410_IISMOD);
31165+ iisfcon=readl(S3C24XX_VA_IIS+S3C2410_IISFCON);
31166+ /* is port used by another stream */
31167+ if (!(iiscon & S3C2410_IISCON_IISEN)) {
31168+
31169+ /* Clear the registers */
31170+
31171+ iismod |= S3C2410_IISMOD_32FS | S3C2410_IISMOD_384FS;
31172+
31173+ if (!s3c24xx_i2s.master)
31174+ iismod |= S3C2410_IISMOD_SLAVE;
31175+
31176+ if (rtd->cpu_dai->dai_runtime.fmt & SND_SOC_DAIFMT_LEFT_J)
31177+ iismod |= S3C2410_IISMOD_MSB;
31178+ }
31179+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
31180+ {
31181+ iismod |= S3C2410_IISMOD_TXMODE;
31182+ iiscon |= S3C2410_IISCON_TXDMAEN;
31183+ iisfcon |= S3C2410_IISFCON_TXDMA | S3C2410_IISFCON_TXENABLE;
31184+ }
31185+ else
31186+ {
31187+ iismod |= S3C2410_IISMOD_RXMODE;
31188+ iiscon |= S3C2410_IISCON_RXDMAEN;
31189+ iisfcon |= S3C2410_IISFCON_RXDMA | S3C2410_IISFCON_RXENABLE;
31190+ }
31191+
31192+ writel(iiscon, S3C24XX_VA_IIS+S3C2410_IISCON);
31193+ writel(iismod, S3C24XX_VA_IIS+S3C2410_IISMOD);
31194+ writel(iisfcon, S3C24XX_VA_IIS+S3C2410_IISFCON);
31195+
31196+ printk("IISCON: %lx IISMOD: %lx", iiscon, iismod);
31197+
31198+ return 0;
31199+}
31200+
31201+static int s3c24xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
31202+{
31203+ int ret = 0;
31204+ unsigned long iiscon;
31205+
31206+ switch (cmd) {
31207+ case SNDRV_PCM_TRIGGER_START:
31208+ /* Enable the IIS unit */
31209+ iiscon = readl(S3C24XX_VA_IIS+S3C2410_IISCON);
31210+ iiscon |= S3C2410_IISCON_IISEN;
31211+ writel(iiscon, S3C24XX_VA_IIS+S3C2410_IISCON);
31212+ break;
31213+ case SNDRV_PCM_TRIGGER_RESUME:
31214+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
31215+ case SNDRV_PCM_TRIGGER_STOP:
31216+ case SNDRV_PCM_TRIGGER_SUSPEND:
31217+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
31218+ break;
31219+ default:
31220+ ret = -EINVAL;
31221+ }
31222+
31223+ return ret;
31224+}
31225+
31226+static void s3c24xx_i2s_shutdown(struct snd_pcm_substream *substream)
31227+{
31228+ unsigned long iismod, iiscon;
31229+
31230+ iismod=readl(S3C24XX_VA_IIS+S3C2410_IISMOD);
31231+
31232+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
31233+ iismod &= ~S3C2410_IISMOD_TXMODE;
31234+ } else {
31235+ iismod &= ~S3C2410_IISMOD_RXMODE;
31236+ }
31237+
31238+ writel(iismod,S3C24XX_VA_IIS+S3C2410_IISMOD);
31239+
31240+ iiscon=readl(S3C24XX_VA_IIS+S3C2410_IISCON);
31241+
31242+ if (iismod & ( S3C2410_IISMOD_TXMODE | S3C2410_IISMOD_RXMODE )) {
31243+ iiscon &= ! S3C2410_IISCON_IISEN;
31244+ writel(iiscon,S3C24XX_VA_IIS+S3C2410_IISCON);
31245+ }
31246+}
31247+
31248+#ifdef CONFIG_PM
31249+static int s3c24xx_i2s_suspend(struct platform_device *dev,
31250+ struct snd_soc_cpu_dai *dai)
31251+{
31252+}
31253+
31254+static int s3c24xx_i2s_resume(struct platform_device *pdev,
31255+ struct snd_soc_cpu_dai *dai)
31256+{
31257+}
31258+
31259+#else
31260+#define s3c24xx_i2s_suspend NULL
31261+#define s3c24xx_i2s_resume NULL
31262+#endif
31263+
31264+/* s3c24xx I2S sysclock is always 384 FS */
31265+static unsigned int s3c24xx_i2s_config_sysclk(struct snd_soc_cpu_dai *iface,
31266+ struct snd_soc_clock_info *info, unsigned int clk)
31267+{
31268+ return info->rate * 384;
31269+}
31270+
31271+struct snd_soc_cpu_dai s3c24xx_i2s_dai = {
31272+ .name = "s3c24xx-i2s",
31273+ .id = 0,
31274+ .type = SND_SOC_DAI_I2S,
31275+ .suspend = s3c24xx_i2s_suspend,
31276+ .resume = s3c24xx_i2s_resume,
31277+ .config_sysclk = s3c24xx_i2s_config_sysclk,
31278+ .playback = {
31279+ .channels_min = 2,
31280+ .channels_max = 2,},
31281+ .capture = {
31282+ .channels_min = 2,
31283+ .channels_max = 2,},
31284+ .ops = {
31285+ .startup = s3c24xx_i2s_startup,
31286+ .shutdown = s3c24xx_i2s_shutdown,
31287+ .trigger = s3c24xx_i2s_trigger,
31288+ .hw_params = s3c24xx_i2s_hw_params,},
31289+ .caps = {
31290+ .num_modes = ARRAY_SIZE(s3c24xx_i2s_modes),
31291+ .mode = s3c24xx_i2s_modes,},
31292+};
31293+
31294+EXPORT_SYMBOL_GPL(s3c24xx_i2s_dai);
31295+
31296+/* Module information */
31297+MODULE_AUTHOR("Graeme Gregory, graeme.gregory@wolfsonmicro.com, www.wolfsonmicro.com");
31298+MODULE_DESCRIPTION("s3c24xx I2S SoC Interface");
31299+MODULE_LICENSE("GPL");
31300Index: linux-2.6-pxa-new/sound/soc/s3c24xx/s3c24xx-pcm.c
31301===================================================================
31302--- /dev/null
31303+++ linux-2.6-pxa-new/sound/soc/s3c24xx/s3c24xx-pcm.c
31304@@ -0,0 +1,362 @@
31305+/*
31306+ * s3c24xx-pcm.c -- ALSA Soc Audio Layer
31307+ *
31308+ * Copyright 2005 Wolfson Microelectronics PLC.
31309+ * Author: Graeme Gregory
31310+ * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
31311+ *
31312+ * This program is free software; you can redistribute it and/or modify it
31313+ * under the terms of the GNU General Public License as published by the
31314+ * Free Software Foundation; either version 2 of the License, or (at your
31315+ * option) any later version.
31316+ *
31317+ * Revision history
31318+ * 10th Nov 2006 Initial version.
31319+ */
31320+
31321+#include <linux/module.h>
31322+#include <linux/init.h>
31323+#include <linux/platform_device.h>
31324+#include <linux/slab.h>
31325+#include <linux/dma-mapping.h>
31326+
31327+#include <sound/driver.h>
31328+#include <sound/core.h>
31329+#include <sound/pcm.h>
31330+#include <sound/pcm_params.h>
31331+#include <sound/soc.h>
31332+
31333+#include <asm/dma.h>
31334+#include <asm/io.h>
31335+#include <asm/hardware.h>
31336+#include <asm/arch/dma.h>
31337+#include <asm/arch/audio.h>
31338+
31339+#include "s3c24xx-pcm.h"
31340+
31341+static const struct snd_pcm_hardware s3c24xx_pcm_hardware = {
31342+ .info = SNDRV_PCM_INFO_MMAP |
31343+ SNDRV_PCM_INFO_MMAP_VALID |
31344+ SNDRV_PCM_INFO_INTERLEAVED |
31345+ SNDRV_PCM_INFO_PAUSE |
31346+ SNDRV_PCM_INFO_RESUME,
31347+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
31348+ .period_bytes_min = 32,
31349+ .period_bytes_max = 8192,
31350+ .periods_min = 1,
31351+ .periods_max = 8192,
31352+ .buffer_bytes_max = 256 * 1024,
31353+ .fifo_size = 32,
31354+};
31355+
31356+struct s3c24xx_runtime_data {
31357+ dma_addr_t dma_buffer;
31358+ dma_addr_t dma_buffer_end;
31359+ size_t period_size;
31360+ dma_addr_t period_ptr;
31361+ s3c24xx_pcm_dma_params_t *params;
31362+};
31363+
31364+/* Move the pointer onto the next period, dealing with wrap around.
31365+ */
31366+void static next_period(struct s3c24xx_runtime_data *prtd)
31367+{
31368+ prtd->period_ptr+=prtd->period_size;
31369+ if(prtd->period_ptr>=prtd->dma_buffer_end)
31370+ {
31371+ prtd->period_ptr=prtd->dma_buffer;
31372+ }
31373+}
31374+
31375+void s3c24xx_audio_buffdone(struct s3c2410_dma_chan *channel,
31376+ void *dev_id, int size,
31377+ enum s3c2410_dma_buffresult result)
31378+{
31379+ struct snd_pcm_substream *substream = dev_id;
31380+ struct s3c24xx_runtime_data *prtd = substream->runtime->private_data;
31381+
31382+ if(result==S3C2410_RES_OK)
31383+ {
31384+ next_period(prtd);
31385+ s3c2410_dma_enqueue(prtd->params->channel, substream, prtd->period_ptr, prtd->period_size);
31386+ }
31387+ snd_pcm_period_elapsed(substream);
31388+
31389+}
31390+
31391+static int s3c24xx_pcm_hw_params(struct snd_pcm_substream *substream,
31392+ struct snd_pcm_hw_params *params)
31393+{
31394+ struct snd_pcm_runtime *runtime = substream->runtime;
31395+ struct s3c24xx_runtime_data *prtd = runtime->private_data;
31396+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
31397+ s3c24xx_pcm_dma_params_t *dma = rtd->cpu_dai->dma_data;
31398+ int ret;
31399+
31400+ printk("Entered s3c24xx hw_params\n");
31401+
31402+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
31403+ runtime->dma_bytes = params_buffer_bytes(params);
31404+
31405+ prtd->params=dma;
31406+ if(ret=s3c2410_dma_request(prtd->params->channel,
31407+ prtd->params->client,NULL))
31408+ {
31409+ printk("Failed to get dma channel %d for %s\n",prtd->params->channel,
31410+ prtd->params->client->name);
31411+ return ret;
31412+ }
31413+
31414+ //s3c2410_dma_setflags(prtd->params->channel,S3C2410_DMAF_AUTOSTART);
31415+ if(substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
31416+ {
31417+ s3c2410_dma_devconfig(prtd->params->channel, S3C2410_DMASRC_MEM,
31418+ S3C2410_DISRCC_INC | S3C2410_DISRCC_APB,
31419+ prtd->params->dma_addr);
31420+ }
31421+ else
31422+ {
31423+ s3c2410_dma_devconfig(prtd->params->channel, S3C2410_DMASRC_HW,
31424+ S3C2410_DISRCC_INC | S3C2410_DISRCC_APB,
31425+ prtd->params->dma_addr);
31426+ }
31427+
31428+ s3c2410_dma_config(prtd->params->channel,2,S3C2410_DCON_HANDSHAKE);
31429+
31430+ s3c2410_dma_set_buffdone_fn(prtd->params->channel, s3c24xx_audio_buffdone);
31431+
31432+ prtd->dma_buffer = runtime->dma_addr;
31433+ prtd->dma_buffer_end = runtime->dma_addr + runtime->dma_bytes;
31434+ prtd->period_size = params_period_bytes(params);
31435+
31436+ return 0;
31437+}
31438+
31439+static int s3c24xx_pcm_hw_free(struct snd_pcm_substream *substream)
31440+{
31441+ struct s3c24xx_runtime_data *prtd = substream->runtime->private_data;
31442+
31443+ printk("Entered s3c24xx hw_free\n");
31444+
31445+ return 0;
31446+}
31447+
31448+static int s3c24xx_pcm_prepare(struct snd_pcm_substream *substream)
31449+{
31450+ struct s3c24xx_runtime_data *prtd = substream->runtime->private_data;
31451+
31452+ printk("Entered s3c24xx prepare\n");
31453+
31454+ /* Set the period that is to be queued in DMA */
31455+ prtd->period_ptr = prtd->dma_buffer;
31456+
31457+ /* queue the first period */
31458+ s3c2410_dma_enqueue(prtd->params->channel, substream, prtd->period_ptr, prtd->period_size);
31459+
31460+ /* Move to next period to be queued */
31461+ next_period(prtd);
31462+
31463+ /* queue the second buffer */
31464+ s3c2410_dma_enqueue(prtd->params->channel, substream, prtd->period_ptr, prtd->period_size);
31465+
31466+
31467+ return 0;
31468+}
31469+
31470+static int s3c24xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
31471+{
31472+ struct s3c24xx_runtime_data *prtd = substream->runtime->private_data;
31473+ int ret = 0;
31474+
31475+ printk("Entered s3c24xx trigger\n");
31476+ switch (cmd) {
31477+ case SNDRV_PCM_TRIGGER_START:
31478+ case SNDRV_PCM_TRIGGER_RESUME:
31479+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
31480+ s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_START);
31481+ break;
31482+
31483+ case SNDRV_PCM_TRIGGER_STOP:
31484+ case SNDRV_PCM_TRIGGER_SUSPEND:
31485+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
31486+ s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_STOP);
31487+ break;
31488+
31489+ default:
31490+ ret = -EINVAL;
31491+ }
31492+
31493+ return ret;
31494+}
31495+
31496+static snd_pcm_uframes_t s3c24xx_pcm_pointer(struct snd_pcm_substream *substream)
31497+{
31498+ struct snd_pcm_runtime *runtime = substream->runtime;
31499+ struct s3c24xx_runtime_data *prtd = runtime->private_data;
31500+ dma_addr_t dst,src;
31501+ snd_pcm_uframes_t x;
31502+
31503+ printk("Entered s3c24xx pointer\n");
31504+
31505+ s3c2410_dma_getposition(prtd->params->channel, &src, &dst);
31506+
31507+ printk("DMA Position: %lx, %lx\n", src, dst);
31508+
31509+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
31510+ {
31511+ x = bytes_to_frames(runtime, src - prtd->dma_buffer);
31512+ }
31513+ else
31514+ {
31515+ x = bytes_to_frames(runtime, dst - prtd->dma_buffer);
31516+ }
31517+
31518+ if (x == runtime->buffer_size)
31519+ x=0;
31520+ return x;
31521+
31522+}
31523+
31524+static int s3c24xx_pcm_open(struct snd_pcm_substream *substream)
31525+{
31526+ struct snd_pcm_runtime *runtime = substream->runtime;
31527+ struct s3c24xx_runtime_data *prtd;
31528+ int ret;
31529+
31530+ printk("Entered s3c24xx open\n");
31531+
31532+ snd_soc_set_runtime_hwparams(substream, &s3c24xx_pcm_hardware);
31533+
31534+ if((prtd = kzalloc(sizeof(struct s3c24xx_runtime_data), GFP_KERNEL)) == NULL)
31535+ {
31536+ ret = -ENOMEM;
31537+ goto out;
31538+ }
31539+
31540+ runtime->private_data = prtd;
31541+ return 0;
31542+
31543+out:
31544+ return ret;
31545+}
31546+
31547+static int s3c24xx_pcm_close(struct snd_pcm_substream *substream)
31548+{
31549+ struct snd_pcm_runtime *runtime = substream->runtime;
31550+ struct s3c24xx_runtime_data *prtd = runtime->private_data;
31551+
31552+ printk("Entered s3c24xx close\n");
31553+
31554+ s3c2410_dma_free(prtd->params->channel, prtd->params->client);
31555+
31556+ return 0;
31557+}
31558+
31559+static int
31560+s3c24xx_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma)
31561+{
31562+ struct snd_pcm_runtime *runtime = substream->runtime;
31563+
31564+ printk("Entered s3c24xx mmap\n");
31565+
31566+ return dma_mmap_writecombine(substream->pcm->card->dev, vma,
31567+ runtime->dma_area,
31568+ runtime->dma_addr,
31569+ runtime->dma_bytes);
31570+}
31571+
31572+struct snd_pcm_ops s3c24xx_pcm_ops = {
31573+ .open = s3c24xx_pcm_open,
31574+ .close = s3c24xx_pcm_close,
31575+ .ioctl = snd_pcm_lib_ioctl,
31576+ .hw_params = s3c24xx_pcm_hw_params,
31577+ .hw_free = s3c24xx_pcm_hw_free,
31578+ .prepare = s3c24xx_pcm_prepare,
31579+ .trigger = s3c24xx_pcm_trigger,
31580+ .pointer = s3c24xx_pcm_pointer,
31581+ .mmap = s3c24xx_pcm_mmap,
31582+};
31583+
31584+static int s3c24xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
31585+{
31586+ struct snd_pcm_substream *substream = pcm->streams[stream].substream;
31587+ struct snd_dma_buffer *buf = &substream->dma_buffer;
31588+ size_t size = s3c24xx_pcm_hardware.buffer_bytes_max;
31589+
31590+ printk("Entered s3c24xx preaccolate_dma_buffer\n");
31591+
31592+ buf->dev.type = SNDRV_DMA_TYPE_DEV;
31593+ buf->dev.dev = pcm->card->dev;
31594+ buf->private_data = NULL;
31595+ buf->area = dma_alloc_writecombine(pcm->card->dev, size,
31596+ &buf->addr, GFP_KERNEL);
31597+ if (!buf->area)
31598+ return -ENOMEM;
31599+ buf->bytes = size;
31600+ return 0;
31601+}
31602+
31603+static void s3c24xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
31604+{
31605+ struct snd_pcm_substream *substream;
31606+ struct snd_dma_buffer *buf;
31607+ int stream;
31608+
31609+ printk("Entered s3c24xx free_dma_buffers\n");
31610+
31611+ for (stream = 0; stream < 2; stream++) {
31612+ substream = pcm->streams[stream].substream;
31613+ if (!substream)
31614+ continue;
31615+
31616+ buf = &substream->dma_buffer;
31617+ if (!buf->area)
31618+ continue;
31619+
31620+ dma_free_writecombine(pcm->card->dev, buf->bytes,
31621+ buf->area, buf->addr);
31622+ buf->area = NULL;
31623+ }
31624+}
31625+
31626+static u64 s3c24xx_pcm_dmamask = 0xffffffff;
31627+
31628+int s3c24xx_pcm_new(struct snd_card *card, struct snd_soc_codec_dai *dai,
31629+ struct snd_pcm *pcm)
31630+{
31631+ int ret = 0;
31632+
31633+ printk("Entered s3c24xx new\n");
31634+
31635+ if (!card->dev->dma_mask)
31636+ card->dev->dma_mask = &s3c24xx_pcm_dmamask;
31637+ if (!card->dev->coherent_dma_mask)
31638+ card->dev->coherent_dma_mask = 0xffffffff;
31639+
31640+ if (dai->playback.channels_min) {
31641+ ret = s3c24xx_pcm_preallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK);
31642+ if (ret)
31643+ goto out;
31644+ }
31645+
31646+ if (dai->capture.channels_min) {
31647+ ret = s3c24xx_pcm_preallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_CAPTURE);
31648+ if (ret)
31649+ goto out;
31650+ }
31651+ out:
31652+ return ret;
31653+}
31654+
31655+struct snd_soc_platform s3c24xx_soc_platform = {
31656+ .name = "s3c24xx-audio",
31657+ .pcm_ops = &s3c24xx_pcm_ops,
31658+ .pcm_new = s3c24xx_pcm_new,
31659+ .pcm_free = s3c24xx_pcm_free_dma_buffers,
31660+};
31661+
31662+EXPORT_SYMBOL_GPL(s3c24xx_soc_platform);
31663+
31664+MODULE_AUTHOR("Graeme Gregory");
31665+MODULE_DESCRIPTION("Samsung S3C24XX PCM DMA module");
31666+MODULE_LICENSE("GPL");
31667Index: linux-2.6-pxa-new/sound/soc/s3c24xx/Kconfig
31668===================================================================
31669--- /dev/null
31670+++ linux-2.6-pxa-new/sound/soc/s3c24xx/Kconfig
31671@@ -0,0 +1,26 @@
31672+menu "SoC Audio for the Atmel AT91"
31673+
31674+config SND_S3C24XX_SOC
31675+ tristate "SoC Audio for the Samsung S3C24xx System-on-Chip"
31676+ depends on ARCH_S3C2410 && SND
31677+ select SND_PCM
31678+ help
31679+ Say Y or M if you want to add support for codecs attached to
31680+ the Samsung S3C24xx.
31681+
31682+config SND_S3C24XX_SOC_I2S
31683+ tristate
31684+
31685+config SND_S3C24XX_SOC_AC97
31686+ tristate
31687+
31688+# graeme - add mach dep
31689+config SND_S3C24XX_SOC_SMDK2440
31690+ tristate "SoC I2S Audio support for SMDK2440"
31691+ depends on SND_S3C24XX_SOC
31692+ select SND_S3C24XX_SOC_I2S
31693+ select SND_SOC_UDA1380
31694+ help
31695+ Say Y if you want to add support for SoC audio on
31696+
31697+endmenu
31698Index: linux-2.6-pxa-new/sound/soc/s3c24xx/Makefile
31699===================================================================
31700--- /dev/null
31701+++ linux-2.6-pxa-new/sound/soc/s3c24xx/Makefile
31702@@ -0,0 +1,11 @@
31703+# S3C24xx Platform Support
31704+snd-soc-s3c24xx-objs := s3c24xx-pcm.o
31705+snd-soc-at91-i2s-objs := s3c24xx-i2s.o
31706+
31707+obj-$(CONFIG_SND_S3C24XX_SOC) += snd-soc-s3c24xx.o
31708+obj-$(CONFIG_SND_S3C24XX_SOC_I2S) += snd-soc-s3c24xx-i2s.o
31709+
31710+# S3C24xx Machine Support
31711+snd-soc-smdk2440-uda1380-objs := smdk2440_uda1380.o
31712+
31713+obj-$(CONFIG_SND_S3C24XX_SOC_SMDK2440) += snd-soc-smdk2440-uda1380.o
diff --git a/meta/packages/linux/linux-rp-2.6.17/tosa-lcdnoise-r0.patch b/meta/packages/linux/linux-rp-2.6.17/tosa-lcdnoise-r0.patch
new file mode 100644
index 0000000000..cb014fb8bc
--- /dev/null
+++ b/meta/packages/linux/linux-rp-2.6.17/tosa-lcdnoise-r0.patch
@@ -0,0 +1,157 @@
1Index: linux-tosa/arch/arm/mach-pxa/tosa.c
2===================================================================
3--- linux-tosa.orig/arch/arm/mach-pxa/tosa.c 2006-08-29 16:52:59.000000000 +0100
4+++ linux-tosa/arch/arm/mach-pxa/tosa.c 2006-08-29 16:55:25.959706776 +0100
5@@ -2,6 +2,7 @@
6 * Support for Sharp SL-C6000x PDAs
7 * Model: (Tosa)
8 *
9+ * Copyright (c) 2006 Wolfson Microelectronics PLC.
10 * Copyright (c) 2005 Dirk Opfer
11 *
12 * Based on code written by Sharp/Lineo for 2.4 kernels
13@@ -46,6 +47,8 @@
14 #include <asm/hardware/tmio.h>
15 #include <asm/mach/sharpsl_param.h>
16
17+#include <linux/wm97xx.h>
18+
19 #include "generic.h"
20
21 /*
22@@ -428,6 +431,16 @@
23 },
24 };
25
26+
27+/*
28+ * Tosa Touchscreen device
29+ */
30+
31+static struct wm97xx_machinfo tosa_ts_machinfo = {
32+ .get_hsync_time = tosa_get_hsync_time,
33+ .wait_hsync = tosa_wait_hsync,
34+};
35+
36 /*
37 * Tosa Blueooth
38 */
39@@ -457,6 +470,7 @@
40 GPSR(TOSA_GPIO_ON_RESET) = GPIO_bit(TOSA_GPIO_ON_RESET);
41
42 mdelay(1000);
43+ wm97xx_unset_machinfo();
44 }
45
46 static void tosa_restart(void)
47@@ -501,6 +515,8 @@
48 platform_scoop_config = &tosa_pcmcia_config;
49
50 platform_add_devices(devices, ARRAY_SIZE(devices));
51+
52+ wm97xx_set_machinfo(&tosa_ts_machinfo);
53 }
54
55 static void __init fixup_tosa(struct machine_desc *desc,
56Index: linux-tosa/arch/arm/mach-pxa/tosa_lcd.c
57===================================================================
58--- linux-tosa.orig/arch/arm/mach-pxa/tosa_lcd.c 2006-08-29 16:52:59.000000000 +0100
59+++ linux-tosa/arch/arm/mach-pxa/tosa_lcd.c 2006-08-29 16:55:32.818664056 +0100
60@@ -1,6 +1,7 @@
61 /*
62 * LCD / Backlight control code for Sharp SL-6000x (tosa)
63 *
64+ * Copyright (c) 2006 Wolfson Microelectronics PLC.
65 * Copyright (c) 2005 Dirk Opfer
66 *
67 * This program is free software; you can redistribute it and/or modify
68@@ -59,6 +60,8 @@
69 static struct ssp_dev tosa_nssp_dev;
70 static struct ssp_state tosa_nssp_state;
71 static spinlock_t tosa_nssp_lock;
72+static int blanked;
73+static unsigned long hsync_time;
74
75 static unsigned short normal_i2c[] = {
76 DAC_BASE,
77@@ -130,6 +133,17 @@
78 pxa_nssp_output(TG_GPOSR,0x02); /* GPOS0=powercontrol, GPOS1=GPIO, GPOS2=TCTL */
79 }
80
81+static unsigned long calc_hsync_time(const struct fb_videomode *mode) {
82+ /* The 25 and 44 'magic numbers' are from Sharp's 2.4 patches */
83+ if (mode->yres == 640) {
84+ return 25;
85+ }
86+ if (mode->yres == 320) {
87+ return 44;
88+ }
89+ return 0;
90+}
91+
92 static void tosa_lcd_tg_on(struct device *dev, const struct fb_videomode *mode)
93 {
94 const int value = TG_REG0_COLOR | TG_REG0_UD | TG_REG0_LR;
95@@ -154,6 +168,8 @@
96 /* set common voltage */
97 i2c_smbus_write_byte_data(tosa_i2c_dac, DAC_CH1, comadj);
98
99+ blanked = 0;
100+ hsync_time = calc_hsync_time(mode);
101 }
102
103 static void tosa_lcd_tg_off(struct device *dev)
104@@ -172,6 +188,8 @@
105
106 /* L3V Off */
107 reset_scoop_gpio( &tosascoop_jc_device.dev,TOSA_SCOOP_JC_TC3693_L3V_ON);
108+
109+ blanked = 1;
110 }
111
112 static int tosa_detect_client(struct i2c_adapter* adapter, int address, int kind) {
113@@ -238,6 +256,23 @@
114 return 0;
115 }
116
117+unsigned long tosa_get_hsync_time(void)
118+{
119+/* This method should eventually contain the correct algorithm for calculating
120+ the hsync_time */
121+ if (blanked)
122+ return 0;
123+ else
124+ return hsync_time;
125+}
126+
127+void tosa_wait_hsync(void)
128+{
129+ /* Waits for a rising edge on the VGA line */
130+ while((GPLR(TOSA_GPIO_VGA_LINE) & GPIO_bit(TOSA_GPIO_VGA_LINE)) == 0);
131+ while((GPLR(TOSA_GPIO_VGA_LINE) & GPIO_bit(TOSA_GPIO_VGA_LINE)) != 0);
132+}
133+
134 static struct i2c_driver tosa_driver={
135 .id = TOSA_LCD_I2C_DEVICEID,
136 .attach_adapter = tosa_attach_adapter,
137Index: linux-tosa/include/asm-arm/arch-pxa/tosa.h
138===================================================================
139--- linux-tosa.orig/include/asm-arm/arch-pxa/tosa.h 2006-08-29 16:52:59.000000000 +0100
140+++ linux-tosa/include/asm-arm/arch-pxa/tosa.h 2006-08-29 16:55:12.442761664 +0100
141@@ -1,6 +1,7 @@
142 /*
143 * Hardware specific definitions for Sharp SL-C6000x series of PDAs
144 *
145+ * Copyright (c) 2006 Wolfson Microelectronics PLC.
146 * Copyright (c) 2005 Dirk Opfer
147 *
148 * Based on Sharp's 2.4 kernel patches
149@@ -187,4 +188,8 @@
150 extern struct platform_device tosascoop_jc_device;
151 extern struct platform_device tosascoop_device;
152 extern struct platform_device tc6393_device;
153+
154+unsigned long tosa_get_hsync_time(void);
155+void tosa_wait_hsync(void);
156+
157 #endif /* _ASM_ARCH_TOSA_H_ */
diff --git a/meta/packages/linux/linux-rp-2.6.17/wm9712-reset-loop-r2.patch b/meta/packages/linux/linux-rp-2.6.17/wm9712-reset-loop-r2.patch
new file mode 100644
index 0000000000..96919b6b02
--- /dev/null
+++ b/meta/packages/linux/linux-rp-2.6.17/wm9712-reset-loop-r2.patch
@@ -0,0 +1,44 @@
1 sound/soc/codecs/wm9712.c | 28 ++++++++++++++++++----------
2 1 file changed, 18 insertions(+), 10 deletions(-)
3
4Index: linux-2.6.18/sound/soc/codecs/wm9712.c
5===================================================================
6--- linux-2.6.18.orig/sound/soc/codecs/wm9712.c 2006-12-05 23:25:33.000000000 +0000
7+++ linux-2.6.18/sound/soc/codecs/wm9712.c 2006-12-05 23:27:20.000000000 +0000
8@@ -618,18 +618,26 @@ static int wm9712_dapm_event(struct snd_
9
10 static int wm9712_reset(struct snd_soc_codec *codec, int try_warm)
11 {
12- if (try_warm && soc_ac97_ops.warm_reset) {
13- soc_ac97_ops.warm_reset(codec->ac97);
14- if (!(ac97_read(codec, 0) & 0x8000))
15- return 1;
16- }
17+ int retry = 3;
18+
19+ while (retry--)
20+ {
21+ if(try_warm && soc_ac97_ops.warm_reset) {
22+ soc_ac97_ops.warm_reset(codec->ac97);
23+ if(ac97_read(codec, 0) & 0x8000)
24+ continue;
25+ else
26+ return 1;
27+ }
28
29- soc_ac97_ops.reset(codec->ac97);
30- if (ac97_read(codec, 0) & 0x8000)
31- goto err;
32- return 0;
33+ soc_ac97_ops.reset(codec->ac97);
34+ if(ac97_read(codec, 0) & 0x8000)
35+ continue;
36+ else
37+ return 0;
38+
39+ }
40
41-err:
42 printk(KERN_ERR "WM9712 AC97 reset failed\n");
43 return -EIO;
44 }
diff --git a/meta/packages/linux/linux-rp-2.6.17/wm9712-suspend-cold-res-r2.patch b/meta/packages/linux/linux-rp-2.6.17/wm9712-suspend-cold-res-r2.patch
new file mode 100644
index 0000000000..e91e54f963
--- /dev/null
+++ b/meta/packages/linux/linux-rp-2.6.17/wm9712-suspend-cold-res-r2.patch
@@ -0,0 +1,16 @@
1 sound/soc/codecs/wm9712.c | 2 +-
2 1 file changed, 1 insertion(+), 1 deletion(-)
3
4Index: linux-2.6.18/sound/soc/codecs/wm9712.c
5===================================================================
6--- linux-2.6.18.orig/sound/soc/codecs/wm9712.c 2006-12-05 23:19:53.000000000 +0000
7+++ linux-2.6.18/sound/soc/codecs/wm9712.c 2006-12-05 23:22:04.000000000 +0000
8@@ -651,7 +651,7 @@ static int wm9712_soc_resume(struct plat
9 int i, ret;
10 u16 *cache = codec->reg_cache;
11
12- ret = wm9712_reset(codec, 1);
13+ ret = wm9712_reset(codec, 0);
14 if (ret < 0){
15 printk(KERN_ERR "could not reset AC97 codec\n");
16 return ret;
diff --git a/meta/packages/linux/linux-rp-2.6.20/defconfig-poodle b/meta/packages/linux/linux-rp-2.6.20/defconfig-poodle
index deacd170f2..13616c08f1 100644
--- a/meta/packages/linux/linux-rp-2.6.20/defconfig-poodle
+++ b/meta/packages/linux/linux-rp-2.6.20/defconfig-poodle
@@ -1653,3 +1653,4 @@ CONFIG_CRC32=y
1653CONFIG_LIBCRC32C=m 1653CONFIG_LIBCRC32C=m
1654CONFIG_ZLIB_INFLATE=y 1654CONFIG_ZLIB_INFLATE=y
1655CONFIG_ZLIB_DEFLATE=y 1655CONFIG_ZLIB_DEFLATE=y
1656# CONFIG_SHARPSL_RC is not set
diff --git a/meta/packages/linux/linux-rp_2.6.17.bb b/meta/packages/linux/linux-rp_2.6.17.bb
index 629fe59a03..211c5a43cf 100644
--- a/meta/packages/linux/linux-rp_2.6.17.bb
+++ b/meta/packages/linux/linux-rp_2.6.17.bb
@@ -1,6 +1,6 @@
1require linux-rp.inc 1require linux-rp.inc
2 2
3PR = "r34" 3PR = "r35"
4 4
5# Handy URLs 5# Handy URLs
6# git://rsync.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git \ 6# git://rsync.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git \
@@ -27,8 +27,7 @@ SRC_URI = "${KERNELORG_MIRROR}/pub/linux/kernel/v2.6/linux-2.6.17.tar.bz2 \
27 ${RPSRC}/spectrumcs_fix-r0.patch;patch=1 \ 27 ${RPSRC}/spectrumcs_fix-r0.patch;patch=1 \
28 file://00-hostap.patch;patch=1;status=merged \ 28 file://00-hostap.patch;patch=1;status=merged \
29 file://10-pcnet.patch;patch=1;status=merged \ 29 file://10-pcnet.patch;patch=1;status=merged \
30 ${RPSRC}/alsa/asoc-v0.12.patch;patch=1 \ 30 file://asoc-v0.12.4_2.6.17.patch;patch=1 \
31 ${RPSRC}/asoc_makefile-r0.patch;patch=1 \
32 ${RPSRC}/hx2750_base-r27.patch;patch=1 \ 31 ${RPSRC}/hx2750_base-r27.patch;patch=1 \
33 ${RPSRC}/hx2750_bl-r7.patch;patch=1 \ 32 ${RPSRC}/hx2750_bl-r7.patch;patch=1 \
34 ${RPSRC}/hx2750_pcmcia-r2.patch;patch=1 \ 33 ${RPSRC}/hx2750_pcmcia-r2.patch;patch=1 \
@@ -42,12 +41,13 @@ SRC_URI = "${KERNELORG_MIRROR}/pub/linux/kernel/v2.6/linux-2.6.17.tar.bz2 \
42 ${RPSRC}/pm_changes-r1.patch;patch=1 \ 41 ${RPSRC}/pm_changes-r1.patch;patch=1 \
43 ${RPSRC}/usb_pxa27x_udc-r0.patch;patch=1 \ 42 ${RPSRC}/usb_pxa27x_udc-r0.patch;patch=1 \
44 ${RPSRC}/usb_add_epalloc-r1.patch;patch=1 \ 43 ${RPSRC}/usb_add_epalloc-r1.patch;patch=1 \
45 ${DOSRC}/kexec-arm-r3.patch;patch=1 \ 44 ${RPSRC}/kexec-arm-r3a.patch;patch=1 \
46 ${RPSRC}/locomo_kbd_tweak-r1.patch;patch=1 \ 45 ${RPSRC}/locomo_kbd_tweak-r1.patch;patch=1 \
47 ${RPSRC}/poodle_pm-r3.patch;patch=1 \ 46 ${RPSRC}/poodle_pm-r3.patch;patch=1 \
48 ${RPSRC}/pxafb_changeres-r0.patch;patch=1 \ 47 ${RPSRC}/pxafb_changeres-r0.patch;patch=1 \
49 ${RPSRC}/poodle_audio-r6.patch;patch=1 \ 48 ${RPSRC}/poodle_audio-r7.patch;patch=1 \
50 ${RPSRC}/pxa27x_overlay-r2.patch;patch=1 \ 49 ${RPSRC}/pxa27x_overlay-r2.patch;patch=1 \
50 ${RPSRC}/w100_extaccel-r0.patch;patch=1 \
51 ${RPSRC}/xscale_cache_workaround-r1.patch;patch=1 \ 51 ${RPSRC}/xscale_cache_workaround-r1.patch;patch=1 \
52 file://serial-add-support-for-non-standard-xtals-to-16c950-driver.patch;patch=1 \ 52 file://serial-add-support-for-non-standard-xtals-to-16c950-driver.patch;patch=1 \
53 file://hrw-pcmcia-ids-r5.patch;patch=1 \ 53 file://hrw-pcmcia-ids-r5.patch;patch=1 \
@@ -85,7 +85,7 @@ SRC_URI = "${KERNELORG_MIRROR}/pub/linux/kernel/v2.6/linux-2.6.17.tar.bz2 \
85 85
86# Is anything out of this still needed? Parts were commited to mainline by rmk (drivers/mfd/) 86# Is anything out of this still needed? Parts were commited to mainline by rmk (drivers/mfd/)
87# (Pavel Machek's git tree has updated versions of this?) 87# (Pavel Machek's git tree has updated versions of this?)
88# ${JLSRC}/zaurus-lcd-2.6.11.diff.gz;patch=1 88# ${JLSRC}/zaurus-lcd-2.6.11.diff.gz;patch=1
89 89
90# These patches are extracted from Pavel Machek's git tree 90# These patches are extracted from Pavel Machek's git tree
91# (diff against vanilla kernel) 91# (diff against vanilla kernel)
@@ -113,11 +113,11 @@ SRC_URI_append_tosa = "\
113 ${DOSRC}/tosa-tmio-lcd-r8.patch;patch=1 \ 113 ${DOSRC}/tosa-tmio-lcd-r8.patch;patch=1 \
114 ${DOSRC}/tosa-bluetooth-r8.patch;patch=1 \ 114 ${DOSRC}/tosa-bluetooth-r8.patch;patch=1 \
115 ${DOSRC}/wm97xx-lg7-r0.patch;patch=1 \ 115 ${DOSRC}/wm97xx-lg7-r0.patch;patch=1 \
116 ${DOSRC}/wm9712-suspend-cold-res-r1.patch;patch=1 \ 116 file://wm9712-suspend-cold-res-r2.patch;patch=1 \
117 ${DOSRC}/sharpsl-pm-postresume-r0.patch;patch=1 \ 117 ${DOSRC}/sharpsl-pm-postresume-r0.patch;patch=1 \
118 ${DOSRC}/wm97xx-dig-restore-r0.patch;patch=1 \ 118 ${DOSRC}/wm97xx-dig-restore-r0.patch;patch=1 \
119 ${DOSRC}/wm97xx-miscdevs-resume-r0.patch;patch=1 \ 119 ${DOSRC}/wm97xx-miscdevs-resume-r0.patch;patch=1 \
120 ${DOSRC}/wm9712-reset-loop-r1.patch;patch=1 \ 120 file://wm9712-reset-loop-r2.patch;patch=1 \
121 file://tosa-lcdnoise-r0.patch;patch=1 \ 121 file://tosa-lcdnoise-r0.patch;patch=1 \
122 file://wm97xx-lcdnoise-r0.patch;patch=1 " 122 file://wm97xx-lcdnoise-r0.patch;patch=1 "
123# ${DOSRC}/tosa-asoc-r1.patch;patch=1 " 123# ${DOSRC}/tosa-asoc-r1.patch;patch=1 "