diff options
author | Cedric Bregardis <cedric.bregardis@free.fr> | 2008-02-20 06:05:13 -0500 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2008-04-24 06:00:13 -0400 |
commit | 98f2a97f207a776603173ee96327d977e592579d (patch) | |
tree | f7e313b4a33b7ebe2a493e009f53eecb446cd676 /sound/pci/aw2/aw2-saa7146.c | |
parent | 67ebcb0311110dc7268bb5b135bf437d8033337e (diff) |
[ALSA] Emagic Audiowerk 2 ALSA driver.
Signed-off-by: Cedric Bregardis <cedric.bregardis@free.fr>
Signed-off-by: Jean-Christian Hassler <jhassler@free.fr>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/aw2/aw2-saa7146.c')
-rw-r--r-- | sound/pci/aw2/aw2-saa7146.c | 464 |
1 files changed, 464 insertions, 0 deletions
diff --git a/sound/pci/aw2/aw2-saa7146.c b/sound/pci/aw2/aw2-saa7146.c new file mode 100644 index 000000000000..f20f213489a3 --- /dev/null +++ b/sound/pci/aw2/aw2-saa7146.c | |||
@@ -0,0 +1,464 @@ | |||
1 | /***************************************************************************** | ||
2 | * | ||
3 | * Copyright (C) 2008 Cedric Bregardis <cedric.bregardis@free.fr> and | ||
4 | * Jean-Christian Hassler <jhassler@free.fr> | ||
5 | * | ||
6 | * This file is part of the Audiowerk2 ALSA driver | ||
7 | * | ||
8 | * The Audiowerk2 ALSA driver is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; version 2. | ||
11 | * | ||
12 | * The Audiowerk2 ALSA driver is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with the Audiowerk2 ALSA driver; if not, write to the Free Software | ||
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, | ||
20 | * USA. | ||
21 | * | ||
22 | *****************************************************************************/ | ||
23 | |||
24 | #define AW2_SAA7146_M | ||
25 | |||
26 | #include <linux/init.h> | ||
27 | #include <linux/pci.h> | ||
28 | #include <linux/slab.h> | ||
29 | #include <linux/interrupt.h> | ||
30 | #include <linux/delay.h> | ||
31 | #include <asm/system.h> | ||
32 | #include <asm/io.h> | ||
33 | #include <sound/core.h> | ||
34 | #include <sound/initval.h> | ||
35 | #include <sound/pcm.h> | ||
36 | #include <sound/pcm_params.h> | ||
37 | |||
38 | #include "aw2-tsl.h" | ||
39 | #include "saa7146.h" | ||
40 | #include "aw2-saa7146.h" | ||
41 | |||
42 | #define WRITEREG(value, addr) writel((value), chip->base_addr + (addr)) | ||
43 | #define READREG(addr) readl(chip->base_addr + (addr)) | ||
44 | |||
45 | static struct snd_aw2_saa7146_cb_param | ||
46 | arr_substream_it_playback_cb[NB_STREAM_PLAYBACK]; | ||
47 | static struct snd_aw2_saa7146_cb_param | ||
48 | arr_substream_it_capture_cb[NB_STREAM_CAPTURE]; | ||
49 | |||
50 | static int snd_aw2_saa7146_get_limit(int size); | ||
51 | |||
52 | /* chip-specific destructor */ | ||
53 | int snd_aw2_saa7146_free(struct snd_aw2_saa7146 *chip) | ||
54 | { | ||
55 | /* disable all irqs */ | ||
56 | WRITEREG(0, IER); | ||
57 | |||
58 | /* reset saa7146 */ | ||
59 | WRITEREG((MRST_N << 16), MC1); | ||
60 | |||
61 | /* Unset base addr */ | ||
62 | chip->base_addr = NULL; | ||
63 | |||
64 | return 0; | ||
65 | } | ||
66 | |||
67 | void snd_aw2_saa7146_setup(struct snd_aw2_saa7146 *chip, | ||
68 | void __iomem *pci_base_addr) | ||
69 | { | ||
70 | /* set PCI burst/threshold | ||
71 | |||
72 | Burst length definition | ||
73 | VALUE BURST LENGTH | ||
74 | 000 1 Dword | ||
75 | 001 2 Dwords | ||
76 | 010 4 Dwords | ||
77 | 011 8 Dwords | ||
78 | 100 16 Dwords | ||
79 | 101 32 Dwords | ||
80 | 110 64 Dwords | ||
81 | 111 128 Dwords | ||
82 | |||
83 | Threshold definition | ||
84 | VALUE WRITE MODE READ MODE | ||
85 | 00 1 Dword of valid data 1 empty Dword | ||
86 | 01 4 Dwords of valid data 4 empty Dwords | ||
87 | 10 8 Dwords of valid data 8 empty Dwords | ||
88 | 11 16 Dwords of valid data 16 empty Dwords */ | ||
89 | |||
90 | unsigned int acon2; | ||
91 | unsigned int acon1 = 0; | ||
92 | int i; | ||
93 | |||
94 | /* Set base addr */ | ||
95 | chip->base_addr = pci_base_addr; | ||
96 | |||
97 | /* disable all irqs */ | ||
98 | WRITEREG(0, IER); | ||
99 | |||
100 | /* reset saa7146 */ | ||
101 | WRITEREG((MRST_N << 16), MC1); | ||
102 | |||
103 | /* enable audio interface */ | ||
104 | #ifdef __BIG_ENDIAN | ||
105 | acon1 |= A1_SWAP; | ||
106 | acon1 |= A2_SWAP; | ||
107 | #endif | ||
108 | /* WS0_CTRL, WS0_SYNC: input TSL1, I2S */ | ||
109 | |||
110 | /* At initialization WS1 and WS2 are disbaled (configured as input */ | ||
111 | acon1 |= 0 * WS1_CTRL; | ||
112 | acon1 |= 0 * WS2_CTRL; | ||
113 | |||
114 | /* WS4 is not used. So it must not restart A2. | ||
115 | This is why it is configured as output (force to low) */ | ||
116 | acon1 |= 3 * WS4_CTRL; | ||
117 | |||
118 | /* WS3_CTRL, WS3_SYNC: output TSL2, I2S */ | ||
119 | acon1 |= 2 * WS3_CTRL; | ||
120 | |||
121 | /* A1 and A2 are active and asynchronous */ | ||
122 | acon1 |= 3 * AUDIO_MODE; | ||
123 | WRITEREG(acon1, ACON1); | ||
124 | |||
125 | /* The following comes from original windows driver. | ||
126 | It is needed to have a correct behavior of input and output | ||
127 | simultenously, but I don't know why ! */ | ||
128 | WRITEREG(3 * (BurstA1_in) + 3 * (ThreshA1_in) + | ||
129 | 3 * (BurstA1_out) + 3 * (ThreshA1_out) + | ||
130 | 3 * (BurstA2_out) + 3 * (ThreshA2_out), PCI_BT_A); | ||
131 | |||
132 | /* enable audio port pins */ | ||
133 | WRITEREG((EAP << 16) | EAP, MC1); | ||
134 | |||
135 | /* enable I2C */ | ||
136 | WRITEREG((EI2C << 16) | EI2C, MC1); | ||
137 | /* enable interrupts */ | ||
138 | WRITEREG(A1_out | A2_out | A1_in | IIC_S | IIC_E, IER); | ||
139 | |||
140 | /* audio configuration */ | ||
141 | acon2 = A2_CLKSRC | BCLK1_OEN; | ||
142 | WRITEREG(acon2, ACON2); | ||
143 | |||
144 | /* By default use analog input */ | ||
145 | snd_aw2_saa7146_use_digital_input(chip, 0); | ||
146 | |||
147 | /* TSL setup */ | ||
148 | for (i = 0; i < 8; ++i) { | ||
149 | WRITEREG(tsl1[i], TSL1 + (i * 4)); | ||
150 | WRITEREG(tsl2[i], TSL2 + (i * 4)); | ||
151 | } | ||
152 | |||
153 | } | ||
154 | |||
155 | void snd_aw2_saa7146_pcm_init_playback(struct snd_aw2_saa7146 *chip, | ||
156 | int stream_number, | ||
157 | unsigned long dma_addr, | ||
158 | unsigned long period_size, | ||
159 | unsigned long buffer_size) | ||
160 | { | ||
161 | unsigned long dw_page, dw_limit; | ||
162 | |||
163 | /* Configure DMA for substream | ||
164 | Configuration informations: ALSA has allocated continuous memory | ||
165 | pages. So we don't need to use MMU of saa7146. | ||
166 | */ | ||
167 | |||
168 | /* No MMU -> nothing to do with PageA1, we only configure the limit of | ||
169 | PageAx_out register */ | ||
170 | /* Disable MMU */ | ||
171 | dw_page = (0L << 11); | ||
172 | |||
173 | /* Configure Limit for DMA access. | ||
174 | The limit register defines an address limit, which generates | ||
175 | an interrupt if passed by the actual PCI address pointer. | ||
176 | '0001' means an interrupt will be generated if the lower | ||
177 | 6 bits (64 bytes) of the PCI address are zero. '0010' | ||
178 | defines a limit of 128 bytes, '0011' one of 256 bytes, and | ||
179 | so on up to 1 Mbyte defined by '1111'. This interrupt range | ||
180 | can be calculated as follows: | ||
181 | Range = 2^(5 + Limit) bytes. | ||
182 | */ | ||
183 | dw_limit = snd_aw2_saa7146_get_limit(period_size); | ||
184 | dw_page |= (dw_limit << 4); | ||
185 | |||
186 | if (stream_number == 0) { | ||
187 | WRITEREG(dw_page, PageA2_out); | ||
188 | |||
189 | /* Base address for DMA transfert. */ | ||
190 | /* This address has been reserved by ALSA. */ | ||
191 | /* This is a physical address */ | ||
192 | WRITEREG(dma_addr, BaseA2_out); | ||
193 | |||
194 | /* Define upper limit for DMA access */ | ||
195 | WRITEREG(dma_addr + buffer_size, ProtA2_out); | ||
196 | |||
197 | } else if (stream_number == 1) { | ||
198 | WRITEREG(dw_page, PageA1_out); | ||
199 | |||
200 | /* Base address for DMA transfert. */ | ||
201 | /* This address has been reserved by ALSA. */ | ||
202 | /* This is a physical address */ | ||
203 | WRITEREG(dma_addr, BaseA1_out); | ||
204 | |||
205 | /* Define upper limit for DMA access */ | ||
206 | WRITEREG(dma_addr + buffer_size, ProtA1_out); | ||
207 | } else { | ||
208 | printk(KERN_ERR | ||
209 | "aw2: snd_aw2_saa7146_pcm_init_playback: " | ||
210 | "Substream number is not 0 or 1 -> not managed\n"); | ||
211 | } | ||
212 | } | ||
213 | |||
214 | void snd_aw2_saa7146_pcm_init_capture(struct snd_aw2_saa7146 *chip, | ||
215 | int stream_number, unsigned long dma_addr, | ||
216 | unsigned long period_size, | ||
217 | unsigned long buffer_size) | ||
218 | { | ||
219 | unsigned long dw_page, dw_limit; | ||
220 | |||
221 | /* Configure DMA for substream | ||
222 | Configuration informations: ALSA has allocated continuous memory | ||
223 | pages. So we don't need to use MMU of saa7146. | ||
224 | */ | ||
225 | |||
226 | /* No MMU -> nothing to do with PageA1, we only configure the limit of | ||
227 | PageAx_out register */ | ||
228 | /* Disable MMU */ | ||
229 | dw_page = (0L << 11); | ||
230 | |||
231 | /* Configure Limit for DMA access. | ||
232 | The limit register defines an address limit, which generates | ||
233 | an interrupt if passed by the actual PCI address pointer. | ||
234 | '0001' means an interrupt will be generated if the lower | ||
235 | 6 bits (64 bytes) of the PCI address are zero. '0010' | ||
236 | defines a limit of 128 bytes, '0011' one of 256 bytes, and | ||
237 | so on up to 1 Mbyte defined by '1111'. This interrupt range | ||
238 | can be calculated as follows: | ||
239 | Range = 2^(5 + Limit) bytes. | ||
240 | */ | ||
241 | dw_limit = snd_aw2_saa7146_get_limit(period_size); | ||
242 | dw_page |= (dw_limit << 4); | ||
243 | |||
244 | if (stream_number == 0) { | ||
245 | WRITEREG(dw_page, PageA1_in); | ||
246 | |||
247 | /* Base address for DMA transfert. */ | ||
248 | /* This address has been reserved by ALSA. */ | ||
249 | /* This is a physical address */ | ||
250 | WRITEREG(dma_addr, BaseA1_in); | ||
251 | |||
252 | /* Define upper limit for DMA access */ | ||
253 | WRITEREG(dma_addr + buffer_size, ProtA1_in); | ||
254 | } else { | ||
255 | printk(KERN_ERR | ||
256 | "aw2: snd_aw2_saa7146_pcm_init_capture: " | ||
257 | "Substream number is not 0 -> not managed\n"); | ||
258 | } | ||
259 | } | ||
260 | |||
261 | void snd_aw2_saa7146_define_it_playback_callback(unsigned int stream_number, | ||
262 | snd_aw2_saa7146_it_cb | ||
263 | p_it_callback, | ||
264 | void *p_callback_param) | ||
265 | { | ||
266 | if (stream_number < NB_STREAM_PLAYBACK) { | ||
267 | arr_substream_it_playback_cb[stream_number].p_it_callback = | ||
268 | (snd_aw2_saa7146_it_cb) p_it_callback; | ||
269 | arr_substream_it_playback_cb[stream_number].p_callback_param = | ||
270 | (void *)p_callback_param; | ||
271 | } | ||
272 | } | ||
273 | |||
274 | void snd_aw2_saa7146_define_it_capture_callback(unsigned int stream_number, | ||
275 | snd_aw2_saa7146_it_cb | ||
276 | p_it_callback, | ||
277 | void *p_callback_param) | ||
278 | { | ||
279 | if (stream_number < NB_STREAM_CAPTURE) { | ||
280 | arr_substream_it_capture_cb[stream_number].p_it_callback = | ||
281 | (snd_aw2_saa7146_it_cb) p_it_callback; | ||
282 | arr_substream_it_capture_cb[stream_number].p_callback_param = | ||
283 | (void *)p_callback_param; | ||
284 | } | ||
285 | } | ||
286 | |||
287 | void snd_aw2_saa7146_pcm_trigger_start_playback(struct snd_aw2_saa7146 *chip, | ||
288 | int stream_number) | ||
289 | { | ||
290 | unsigned int acon1 = 0; | ||
291 | /* In aw8 driver, dma transfert is always active. It is | ||
292 | started and stopped in a larger "space" */ | ||
293 | acon1 = READREG(ACON1); | ||
294 | if (stream_number == 0) { | ||
295 | WRITEREG((TR_E_A2_OUT << 16) | TR_E_A2_OUT, MC1); | ||
296 | |||
297 | /* WS2_CTRL, WS2_SYNC: output TSL2, I2S */ | ||
298 | acon1 |= 2 * WS2_CTRL; | ||
299 | WRITEREG(acon1, ACON1); | ||
300 | |||
301 | } else if (stream_number == 1) { | ||
302 | WRITEREG((TR_E_A1_OUT << 16) | TR_E_A1_OUT, MC1); | ||
303 | |||
304 | /* WS1_CTRL, WS1_SYNC: output TSL1, I2S */ | ||
305 | acon1 |= 1 * WS1_CTRL; | ||
306 | WRITEREG(acon1, ACON1); | ||
307 | } | ||
308 | } | ||
309 | |||
310 | void snd_aw2_saa7146_pcm_trigger_stop_playback(struct snd_aw2_saa7146 *chip, | ||
311 | int stream_number) | ||
312 | { | ||
313 | unsigned int acon1 = 0; | ||
314 | acon1 = READREG(ACON1); | ||
315 | if (stream_number == 0) { | ||
316 | /* WS2_CTRL, WS2_SYNC: output TSL2, I2S */ | ||
317 | acon1 &= ~(3 * WS2_CTRL); | ||
318 | WRITEREG(acon1, ACON1); | ||
319 | |||
320 | WRITEREG((TR_E_A2_OUT << 16), MC1); | ||
321 | } else if (stream_number == 1) { | ||
322 | /* WS1_CTRL, WS1_SYNC: output TSL1, I2S */ | ||
323 | acon1 &= ~(3 * WS1_CTRL); | ||
324 | WRITEREG(acon1, ACON1); | ||
325 | |||
326 | WRITEREG((TR_E_A1_OUT << 16), MC1); | ||
327 | } | ||
328 | } | ||
329 | |||
330 | void snd_aw2_saa7146_pcm_trigger_start_capture(struct snd_aw2_saa7146 *chip, | ||
331 | int stream_number) | ||
332 | { | ||
333 | /* In aw8 driver, dma transfert is always active. It is | ||
334 | started and stopped in a larger "space" */ | ||
335 | if (stream_number == 0) | ||
336 | WRITEREG((TR_E_A1_IN << 16) | TR_E_A1_IN, MC1); | ||
337 | } | ||
338 | |||
339 | void snd_aw2_saa7146_pcm_trigger_stop_capture(struct snd_aw2_saa7146 *chip, | ||
340 | int stream_number) | ||
341 | { | ||
342 | if (stream_number == 0) | ||
343 | WRITEREG((TR_E_A1_IN << 16), MC1); | ||
344 | } | ||
345 | |||
346 | irqreturn_t snd_aw2_saa7146_interrupt(int irq, void *dev_id) | ||
347 | { | ||
348 | unsigned int isr; | ||
349 | unsigned int iicsta; | ||
350 | struct snd_aw2_saa7146 *chip = dev_id; | ||
351 | |||
352 | isr = READREG(ISR); | ||
353 | if (!isr) | ||
354 | return IRQ_NONE; | ||
355 | |||
356 | WRITEREG(isr, ISR); | ||
357 | |||
358 | if (isr & (IIC_S | IIC_E)) { | ||
359 | iicsta = READREG(IICSTA); | ||
360 | WRITEREG(0x100, IICSTA); | ||
361 | } | ||
362 | |||
363 | if (isr & A1_out) { | ||
364 | if (arr_substream_it_playback_cb[1].p_it_callback != NULL) { | ||
365 | arr_substream_it_playback_cb[1]. | ||
366 | p_it_callback(arr_substream_it_playback_cb[1]. | ||
367 | p_callback_param); | ||
368 | } | ||
369 | } | ||
370 | if (isr & A2_out) { | ||
371 | if (arr_substream_it_playback_cb[0].p_it_callback != NULL) { | ||
372 | arr_substream_it_playback_cb[0]. | ||
373 | p_it_callback(arr_substream_it_playback_cb[0]. | ||
374 | p_callback_param); | ||
375 | } | ||
376 | |||
377 | } | ||
378 | if (isr & A1_in) { | ||
379 | if (arr_substream_it_capture_cb[0].p_it_callback != NULL) { | ||
380 | arr_substream_it_capture_cb[0]. | ||
381 | p_it_callback(arr_substream_it_capture_cb[0]. | ||
382 | p_callback_param); | ||
383 | } | ||
384 | } | ||
385 | return IRQ_HANDLED; | ||
386 | } | ||
387 | |||
388 | unsigned int snd_aw2_saa7146_get_hw_ptr_playback(struct snd_aw2_saa7146 *chip, | ||
389 | int stream_number, | ||
390 | unsigned char *start_addr, | ||
391 | unsigned int buffer_size) | ||
392 | { | ||
393 | long pci_adp = 0; | ||
394 | size_t ptr = 0; | ||
395 | |||
396 | if (stream_number == 0) { | ||
397 | pci_adp = READREG(PCI_ADP3); | ||
398 | ptr = pci_adp - (long)start_addr; | ||
399 | |||
400 | if (ptr == buffer_size) | ||
401 | ptr = 0; | ||
402 | } | ||
403 | if (stream_number == 1) { | ||
404 | pci_adp = READREG(PCI_ADP1); | ||
405 | ptr = pci_adp - (size_t) start_addr; | ||
406 | |||
407 | if (ptr == buffer_size) | ||
408 | ptr = 0; | ||
409 | } | ||
410 | return ptr; | ||
411 | } | ||
412 | |||
413 | unsigned int snd_aw2_saa7146_get_hw_ptr_capture(struct snd_aw2_saa7146 *chip, | ||
414 | int stream_number, | ||
415 | unsigned char *start_addr, | ||
416 | unsigned int buffer_size) | ||
417 | { | ||
418 | size_t pci_adp = 0; | ||
419 | size_t ptr = 0; | ||
420 | if (stream_number == 0) { | ||
421 | pci_adp = READREG(PCI_ADP2); | ||
422 | ptr = pci_adp - (size_t) start_addr; | ||
423 | |||
424 | if (ptr == buffer_size) | ||
425 | ptr = 0; | ||
426 | } | ||
427 | return ptr; | ||
428 | } | ||
429 | |||
430 | void snd_aw2_saa7146_use_digital_input(struct snd_aw2_saa7146 *chip, | ||
431 | int use_digital) | ||
432 | { | ||
433 | /* FIXME: switch between analog and digital input does not always work. | ||
434 | It can produce a kind of white noise. It seams that received data | ||
435 | are inverted sometime (endian inversion). Why ? I don't know, maybe | ||
436 | a problem of synchronization... However for the time being I have | ||
437 | not found the problem. Workaround: switch again (and again) between | ||
438 | digital and analog input until it works. */ | ||
439 | if (use_digital) | ||
440 | WRITEREG(0x40, GPIO_CTRL); | ||
441 | else | ||
442 | WRITEREG(0x50, GPIO_CTRL); | ||
443 | } | ||
444 | |||
445 | int snd_aw2_saa7146_is_using_digital_input(struct snd_aw2_saa7146 *chip) | ||
446 | { | ||
447 | unsigned int reg_val = READREG(GPIO_CTRL); | ||
448 | if ((reg_val & 0xFF) == 0x40) | ||
449 | return 1; | ||
450 | else | ||
451 | return 0; | ||
452 | } | ||
453 | |||
454 | |||
455 | static int snd_aw2_saa7146_get_limit(int size) | ||
456 | { | ||
457 | int limitsize = 32; | ||
458 | int limit = 0; | ||
459 | while (limitsize < size) { | ||
460 | limitsize *= 2; | ||
461 | limit++; | ||
462 | } | ||
463 | return limit; | ||
464 | } | ||