diff options
author | Mauro Carvalho Chehab <mchehab@redhat.com> | 2012-08-14 11:13:30 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2012-08-15 15:36:22 -0400 |
commit | b285192a43f0432d82c2c10974204e78af0da596 (patch) | |
tree | 618aa87e760c9c949eca9e4df6ae0eeffa11dcfc /drivers/media/video/cx88/cx88-alsa.c | |
parent | 68de959f773a1d49096835c411390bceff5d1549 (diff) |
[media] rename most media/video pci drivers to media/pci
Rename all PCI drivers with their own directory under
drivers/media/video into drivers/media/pci and update the
building system.
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/video/cx88/cx88-alsa.c')
-rw-r--r-- | drivers/media/video/cx88/cx88-alsa.c | 975 |
1 files changed, 0 insertions, 975 deletions
diff --git a/drivers/media/video/cx88/cx88-alsa.c b/drivers/media/video/cx88/cx88-alsa.c deleted file mode 100644 index 3aa6856ead3b..000000000000 --- a/drivers/media/video/cx88/cx88-alsa.c +++ /dev/null | |||
@@ -1,975 +0,0 @@ | |||
1 | /* | ||
2 | * | ||
3 | * Support for audio capture | ||
4 | * PCI function #1 of the cx2388x. | ||
5 | * | ||
6 | * (c) 2007 Trent Piepho <xyzzy@speakeasy.org> | ||
7 | * (c) 2005,2006 Ricardo Cerqueira <v4l@cerqueira.org> | ||
8 | * (c) 2005 Mauro Carvalho Chehab <mchehab@infradead.org> | ||
9 | * Based on a dummy cx88 module by Gerd Knorr <kraxel@bytesex.org> | ||
10 | * Based on dummy.c by Jaroslav Kysela <perex@perex.cz> | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License as published by | ||
14 | * the Free Software Foundation; either version 2 of the License, or | ||
15 | * (at your option) any later version. | ||
16 | * | ||
17 | * This program is distributed in the hope that it will be useful, | ||
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
20 | * GNU General Public License for more details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
25 | */ | ||
26 | |||
27 | #include <linux/module.h> | ||
28 | #include <linux/init.h> | ||
29 | #include <linux/device.h> | ||
30 | #include <linux/interrupt.h> | ||
31 | #include <linux/vmalloc.h> | ||
32 | #include <linux/dma-mapping.h> | ||
33 | #include <linux/pci.h> | ||
34 | #include <linux/slab.h> | ||
35 | |||
36 | #include <asm/delay.h> | ||
37 | #include <sound/core.h> | ||
38 | #include <sound/pcm.h> | ||
39 | #include <sound/pcm_params.h> | ||
40 | #include <sound/control.h> | ||
41 | #include <sound/initval.h> | ||
42 | #include <sound/tlv.h> | ||
43 | #include <media/wm8775.h> | ||
44 | |||
45 | #include "cx88.h" | ||
46 | #include "cx88-reg.h" | ||
47 | |||
48 | #define dprintk(level,fmt, arg...) if (debug >= level) \ | ||
49 | printk(KERN_INFO "%s/1: " fmt, chip->core->name , ## arg) | ||
50 | |||
51 | #define dprintk_core(level,fmt, arg...) if (debug >= level) \ | ||
52 | printk(KERN_DEBUG "%s/1: " fmt, chip->core->name , ## arg) | ||
53 | |||
54 | /**************************************************************************** | ||
55 | Data type declarations - Can be moded to a header file later | ||
56 | ****************************************************************************/ | ||
57 | |||
58 | struct cx88_audio_buffer { | ||
59 | unsigned int bpl; | ||
60 | struct btcx_riscmem risc; | ||
61 | struct videobuf_dmabuf dma; | ||
62 | }; | ||
63 | |||
64 | struct cx88_audio_dev { | ||
65 | struct cx88_core *core; | ||
66 | struct cx88_dmaqueue q; | ||
67 | |||
68 | /* pci i/o */ | ||
69 | struct pci_dev *pci; | ||
70 | |||
71 | /* audio controls */ | ||
72 | int irq; | ||
73 | |||
74 | struct snd_card *card; | ||
75 | |||
76 | spinlock_t reg_lock; | ||
77 | atomic_t count; | ||
78 | |||
79 | unsigned int dma_size; | ||
80 | unsigned int period_size; | ||
81 | unsigned int num_periods; | ||
82 | |||
83 | struct videobuf_dmabuf *dma_risc; | ||
84 | |||
85 | struct cx88_audio_buffer *buf; | ||
86 | |||
87 | struct snd_pcm_substream *substream; | ||
88 | }; | ||
89 | typedef struct cx88_audio_dev snd_cx88_card_t; | ||
90 | |||
91 | |||
92 | |||
93 | /**************************************************************************** | ||
94 | Module global static vars | ||
95 | ****************************************************************************/ | ||
96 | |||
97 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ | ||
98 | static const char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | ||
99 | static bool enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 1}; | ||
100 | |||
101 | module_param_array(enable, bool, NULL, 0444); | ||
102 | MODULE_PARM_DESC(enable, "Enable cx88x soundcard. default enabled."); | ||
103 | |||
104 | module_param_array(index, int, NULL, 0444); | ||
105 | MODULE_PARM_DESC(index, "Index value for cx88x capture interface(s)."); | ||
106 | |||
107 | |||
108 | /**************************************************************************** | ||
109 | Module macros | ||
110 | ****************************************************************************/ | ||
111 | |||
112 | MODULE_DESCRIPTION("ALSA driver module for cx2388x based TV cards"); | ||
113 | MODULE_AUTHOR("Ricardo Cerqueira"); | ||
114 | MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>"); | ||
115 | MODULE_LICENSE("GPL"); | ||
116 | MODULE_VERSION(CX88_VERSION); | ||
117 | |||
118 | MODULE_SUPPORTED_DEVICE("{{Conexant,23881}," | ||
119 | "{{Conexant,23882}," | ||
120 | "{{Conexant,23883}"); | ||
121 | static unsigned int debug; | ||
122 | module_param(debug,int,0644); | ||
123 | MODULE_PARM_DESC(debug,"enable debug messages"); | ||
124 | |||
125 | /**************************************************************************** | ||
126 | Module specific funtions | ||
127 | ****************************************************************************/ | ||
128 | |||
129 | /* | ||
130 | * BOARD Specific: Sets audio DMA | ||
131 | */ | ||
132 | |||
133 | static int _cx88_start_audio_dma(snd_cx88_card_t *chip) | ||
134 | { | ||
135 | struct cx88_audio_buffer *buf = chip->buf; | ||
136 | struct cx88_core *core=chip->core; | ||
137 | const struct sram_channel *audio_ch = &cx88_sram_channels[SRAM_CH25]; | ||
138 | |||
139 | /* Make sure RISC/FIFO are off before changing FIFO/RISC settings */ | ||
140 | cx_clear(MO_AUD_DMACNTRL, 0x11); | ||
141 | |||
142 | /* setup fifo + format - out channel */ | ||
143 | cx88_sram_channel_setup(chip->core, audio_ch, buf->bpl, buf->risc.dma); | ||
144 | |||
145 | /* sets bpl size */ | ||
146 | cx_write(MO_AUDD_LNGTH, buf->bpl); | ||
147 | |||
148 | /* reset counter */ | ||
149 | cx_write(MO_AUDD_GPCNTRL, GP_COUNT_CONTROL_RESET); | ||
150 | atomic_set(&chip->count, 0); | ||
151 | |||
152 | dprintk(1, "Start audio DMA, %d B/line, %d lines/FIFO, %d periods, %d " | ||
153 | "byte buffer\n", buf->bpl, cx_read(audio_ch->cmds_start + 8)>>1, | ||
154 | chip->num_periods, buf->bpl * chip->num_periods); | ||
155 | |||
156 | /* Enables corresponding bits at AUD_INT_STAT */ | ||
157 | cx_write(MO_AUD_INTMSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC | | ||
158 | AUD_INT_DN_RISCI2 | AUD_INT_DN_RISCI1); | ||
159 | |||
160 | /* Clean any pending interrupt bits already set */ | ||
161 | cx_write(MO_AUD_INTSTAT, ~0); | ||
162 | |||
163 | /* enable audio irqs */ | ||
164 | cx_set(MO_PCI_INTMSK, chip->core->pci_irqmask | PCI_INT_AUDINT); | ||
165 | |||
166 | /* start dma */ | ||
167 | cx_set(MO_DEV_CNTRL2, (1<<5)); /* Enables Risc Processor */ | ||
168 | cx_set(MO_AUD_DMACNTRL, 0x11); /* audio downstream FIFO and RISC enable */ | ||
169 | |||
170 | if (debug) | ||
171 | cx88_sram_channel_dump(chip->core, audio_ch); | ||
172 | |||
173 | return 0; | ||
174 | } | ||
175 | |||
176 | /* | ||
177 | * BOARD Specific: Resets audio DMA | ||
178 | */ | ||
179 | static int _cx88_stop_audio_dma(snd_cx88_card_t *chip) | ||
180 | { | ||
181 | struct cx88_core *core=chip->core; | ||
182 | dprintk(1, "Stopping audio DMA\n"); | ||
183 | |||
184 | /* stop dma */ | ||
185 | cx_clear(MO_AUD_DMACNTRL, 0x11); | ||
186 | |||
187 | /* disable irqs */ | ||
188 | cx_clear(MO_PCI_INTMSK, PCI_INT_AUDINT); | ||
189 | cx_clear(MO_AUD_INTMSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC | | ||
190 | AUD_INT_DN_RISCI2 | AUD_INT_DN_RISCI1); | ||
191 | |||
192 | if (debug) | ||
193 | cx88_sram_channel_dump(chip->core, &cx88_sram_channels[SRAM_CH25]); | ||
194 | |||
195 | return 0; | ||
196 | } | ||
197 | |||
198 | #define MAX_IRQ_LOOP 50 | ||
199 | |||
200 | /* | ||
201 | * BOARD Specific: IRQ dma bits | ||
202 | */ | ||
203 | static const char *cx88_aud_irqs[32] = { | ||
204 | "dn_risci1", "up_risci1", "rds_dn_risc1", /* 0-2 */ | ||
205 | NULL, /* reserved */ | ||
206 | "dn_risci2", "up_risci2", "rds_dn_risc2", /* 4-6 */ | ||
207 | NULL, /* reserved */ | ||
208 | "dnf_of", "upf_uf", "rds_dnf_uf", /* 8-10 */ | ||
209 | NULL, /* reserved */ | ||
210 | "dn_sync", "up_sync", "rds_dn_sync", /* 12-14 */ | ||
211 | NULL, /* reserved */ | ||
212 | "opc_err", "par_err", "rip_err", /* 16-18 */ | ||
213 | "pci_abort", "ber_irq", "mchg_irq" /* 19-21 */ | ||
214 | }; | ||
215 | |||
216 | /* | ||
217 | * BOARD Specific: Threats IRQ audio specific calls | ||
218 | */ | ||
219 | static void cx8801_aud_irq(snd_cx88_card_t *chip) | ||
220 | { | ||
221 | struct cx88_core *core = chip->core; | ||
222 | u32 status, mask; | ||
223 | |||
224 | status = cx_read(MO_AUD_INTSTAT); | ||
225 | mask = cx_read(MO_AUD_INTMSK); | ||
226 | if (0 == (status & mask)) | ||
227 | return; | ||
228 | cx_write(MO_AUD_INTSTAT, status); | ||
229 | if (debug > 1 || (status & mask & ~0xff)) | ||
230 | cx88_print_irqbits(core->name, "irq aud", | ||
231 | cx88_aud_irqs, ARRAY_SIZE(cx88_aud_irqs), | ||
232 | status, mask); | ||
233 | /* risc op code error */ | ||
234 | if (status & AUD_INT_OPC_ERR) { | ||
235 | printk(KERN_WARNING "%s/1: Audio risc op code error\n",core->name); | ||
236 | cx_clear(MO_AUD_DMACNTRL, 0x11); | ||
237 | cx88_sram_channel_dump(core, &cx88_sram_channels[SRAM_CH25]); | ||
238 | } | ||
239 | if (status & AUD_INT_DN_SYNC) { | ||
240 | dprintk(1, "Downstream sync error\n"); | ||
241 | cx_write(MO_AUDD_GPCNTRL, GP_COUNT_CONTROL_RESET); | ||
242 | return; | ||
243 | } | ||
244 | /* risc1 downstream */ | ||
245 | if (status & AUD_INT_DN_RISCI1) { | ||
246 | atomic_set(&chip->count, cx_read(MO_AUDD_GPCNT)); | ||
247 | snd_pcm_period_elapsed(chip->substream); | ||
248 | } | ||
249 | /* FIXME: Any other status should deserve a special handling? */ | ||
250 | } | ||
251 | |||
252 | /* | ||
253 | * BOARD Specific: Handles IRQ calls | ||
254 | */ | ||
255 | static irqreturn_t cx8801_irq(int irq, void *dev_id) | ||
256 | { | ||
257 | snd_cx88_card_t *chip = dev_id; | ||
258 | struct cx88_core *core = chip->core; | ||
259 | u32 status; | ||
260 | int loop, handled = 0; | ||
261 | |||
262 | for (loop = 0; loop < MAX_IRQ_LOOP; loop++) { | ||
263 | status = cx_read(MO_PCI_INTSTAT) & | ||
264 | (core->pci_irqmask | PCI_INT_AUDINT); | ||
265 | if (0 == status) | ||
266 | goto out; | ||
267 | dprintk(3, "cx8801_irq loop %d/%d, status %x\n", | ||
268 | loop, MAX_IRQ_LOOP, status); | ||
269 | handled = 1; | ||
270 | cx_write(MO_PCI_INTSTAT, status); | ||
271 | |||
272 | if (status & core->pci_irqmask) | ||
273 | cx88_core_irq(core, status); | ||
274 | if (status & PCI_INT_AUDINT) | ||
275 | cx8801_aud_irq(chip); | ||
276 | } | ||
277 | |||
278 | if (MAX_IRQ_LOOP == loop) { | ||
279 | printk(KERN_ERR | ||
280 | "%s/1: IRQ loop detected, disabling interrupts\n", | ||
281 | core->name); | ||
282 | cx_clear(MO_PCI_INTMSK, PCI_INT_AUDINT); | ||
283 | } | ||
284 | |||
285 | out: | ||
286 | return IRQ_RETVAL(handled); | ||
287 | } | ||
288 | |||
289 | |||
290 | static int dsp_buffer_free(snd_cx88_card_t *chip) | ||
291 | { | ||
292 | BUG_ON(!chip->dma_size); | ||
293 | |||
294 | dprintk(2,"Freeing buffer\n"); | ||
295 | videobuf_dma_unmap(&chip->pci->dev, chip->dma_risc); | ||
296 | videobuf_dma_free(chip->dma_risc); | ||
297 | btcx_riscmem_free(chip->pci,&chip->buf->risc); | ||
298 | kfree(chip->buf); | ||
299 | |||
300 | chip->dma_risc = NULL; | ||
301 | chip->dma_size = 0; | ||
302 | |||
303 | return 0; | ||
304 | } | ||
305 | |||
306 | /**************************************************************************** | ||
307 | ALSA PCM Interface | ||
308 | ****************************************************************************/ | ||
309 | |||
310 | /* | ||
311 | * Digital hardware definition | ||
312 | */ | ||
313 | #define DEFAULT_FIFO_SIZE 4096 | ||
314 | static const struct snd_pcm_hardware snd_cx88_digital_hw = { | ||
315 | .info = SNDRV_PCM_INFO_MMAP | | ||
316 | SNDRV_PCM_INFO_INTERLEAVED | | ||
317 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | ||
318 | SNDRV_PCM_INFO_MMAP_VALID, | ||
319 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
320 | |||
321 | .rates = SNDRV_PCM_RATE_48000, | ||
322 | .rate_min = 48000, | ||
323 | .rate_max = 48000, | ||
324 | .channels_min = 2, | ||
325 | .channels_max = 2, | ||
326 | /* Analog audio output will be full of clicks and pops if there | ||
327 | are not exactly four lines in the SRAM FIFO buffer. */ | ||
328 | .period_bytes_min = DEFAULT_FIFO_SIZE/4, | ||
329 | .period_bytes_max = DEFAULT_FIFO_SIZE/4, | ||
330 | .periods_min = 1, | ||
331 | .periods_max = 1024, | ||
332 | .buffer_bytes_max = (1024*1024), | ||
333 | }; | ||
334 | |||
335 | /* | ||
336 | * audio pcm capture open callback | ||
337 | */ | ||
338 | static int snd_cx88_pcm_open(struct snd_pcm_substream *substream) | ||
339 | { | ||
340 | snd_cx88_card_t *chip = snd_pcm_substream_chip(substream); | ||
341 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
342 | int err; | ||
343 | |||
344 | if (!chip) { | ||
345 | printk(KERN_ERR "BUG: cx88 can't find device struct." | ||
346 | " Can't proceed with open\n"); | ||
347 | return -ENODEV; | ||
348 | } | ||
349 | |||
350 | err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIODS); | ||
351 | if (err < 0) | ||
352 | goto _error; | ||
353 | |||
354 | chip->substream = substream; | ||
355 | |||
356 | runtime->hw = snd_cx88_digital_hw; | ||
357 | |||
358 | if (cx88_sram_channels[SRAM_CH25].fifo_size != DEFAULT_FIFO_SIZE) { | ||
359 | unsigned int bpl = cx88_sram_channels[SRAM_CH25].fifo_size / 4; | ||
360 | bpl &= ~7; /* must be multiple of 8 */ | ||
361 | runtime->hw.period_bytes_min = bpl; | ||
362 | runtime->hw.period_bytes_max = bpl; | ||
363 | } | ||
364 | |||
365 | return 0; | ||
366 | _error: | ||
367 | dprintk(1,"Error opening PCM!\n"); | ||
368 | return err; | ||
369 | } | ||
370 | |||
371 | /* | ||
372 | * audio close callback | ||
373 | */ | ||
374 | static int snd_cx88_close(struct snd_pcm_substream *substream) | ||
375 | { | ||
376 | return 0; | ||
377 | } | ||
378 | |||
379 | /* | ||
380 | * hw_params callback | ||
381 | */ | ||
382 | static int snd_cx88_hw_params(struct snd_pcm_substream * substream, | ||
383 | struct snd_pcm_hw_params * hw_params) | ||
384 | { | ||
385 | snd_cx88_card_t *chip = snd_pcm_substream_chip(substream); | ||
386 | struct videobuf_dmabuf *dma; | ||
387 | |||
388 | struct cx88_audio_buffer *buf; | ||
389 | int ret; | ||
390 | |||
391 | if (substream->runtime->dma_area) { | ||
392 | dsp_buffer_free(chip); | ||
393 | substream->runtime->dma_area = NULL; | ||
394 | } | ||
395 | |||
396 | chip->period_size = params_period_bytes(hw_params); | ||
397 | chip->num_periods = params_periods(hw_params); | ||
398 | chip->dma_size = chip->period_size * params_periods(hw_params); | ||
399 | |||
400 | BUG_ON(!chip->dma_size); | ||
401 | BUG_ON(chip->num_periods & (chip->num_periods-1)); | ||
402 | |||
403 | buf = kzalloc(sizeof(*buf), GFP_KERNEL); | ||
404 | if (NULL == buf) | ||
405 | return -ENOMEM; | ||
406 | |||
407 | buf->bpl = chip->period_size; | ||
408 | |||
409 | dma = &buf->dma; | ||
410 | videobuf_dma_init(dma); | ||
411 | ret = videobuf_dma_init_kernel(dma, PCI_DMA_FROMDEVICE, | ||
412 | (PAGE_ALIGN(chip->dma_size) >> PAGE_SHIFT)); | ||
413 | if (ret < 0) | ||
414 | goto error; | ||
415 | |||
416 | ret = videobuf_dma_map(&chip->pci->dev, dma); | ||
417 | if (ret < 0) | ||
418 | goto error; | ||
419 | |||
420 | ret = cx88_risc_databuffer(chip->pci, &buf->risc, dma->sglist, | ||
421 | chip->period_size, chip->num_periods, 1); | ||
422 | if (ret < 0) | ||
423 | goto error; | ||
424 | |||
425 | /* Loop back to start of program */ | ||
426 | buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP|RISC_IRQ1|RISC_CNT_INC); | ||
427 | buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma); | ||
428 | |||
429 | chip->buf = buf; | ||
430 | chip->dma_risc = dma; | ||
431 | |||
432 | substream->runtime->dma_area = chip->dma_risc->vaddr; | ||
433 | substream->runtime->dma_bytes = chip->dma_size; | ||
434 | substream->runtime->dma_addr = 0; | ||
435 | return 0; | ||
436 | |||
437 | error: | ||
438 | kfree(buf); | ||
439 | return ret; | ||
440 | } | ||
441 | |||
442 | /* | ||
443 | * hw free callback | ||
444 | */ | ||
445 | static int snd_cx88_hw_free(struct snd_pcm_substream * substream) | ||
446 | { | ||
447 | |||
448 | snd_cx88_card_t *chip = snd_pcm_substream_chip(substream); | ||
449 | |||
450 | if (substream->runtime->dma_area) { | ||
451 | dsp_buffer_free(chip); | ||
452 | substream->runtime->dma_area = NULL; | ||
453 | } | ||
454 | |||
455 | return 0; | ||
456 | } | ||
457 | |||
458 | /* | ||
459 | * prepare callback | ||
460 | */ | ||
461 | static int snd_cx88_prepare(struct snd_pcm_substream *substream) | ||
462 | { | ||
463 | return 0; | ||
464 | } | ||
465 | |||
466 | /* | ||
467 | * trigger callback | ||
468 | */ | ||
469 | static int snd_cx88_card_trigger(struct snd_pcm_substream *substream, int cmd) | ||
470 | { | ||
471 | snd_cx88_card_t *chip = snd_pcm_substream_chip(substream); | ||
472 | int err; | ||
473 | |||
474 | /* Local interrupts are already disabled by ALSA */ | ||
475 | spin_lock(&chip->reg_lock); | ||
476 | |||
477 | switch (cmd) { | ||
478 | case SNDRV_PCM_TRIGGER_START: | ||
479 | err=_cx88_start_audio_dma(chip); | ||
480 | break; | ||
481 | case SNDRV_PCM_TRIGGER_STOP: | ||
482 | err=_cx88_stop_audio_dma(chip); | ||
483 | break; | ||
484 | default: | ||
485 | err=-EINVAL; | ||
486 | break; | ||
487 | } | ||
488 | |||
489 | spin_unlock(&chip->reg_lock); | ||
490 | |||
491 | return err; | ||
492 | } | ||
493 | |||
494 | /* | ||
495 | * pointer callback | ||
496 | */ | ||
497 | static snd_pcm_uframes_t snd_cx88_pointer(struct snd_pcm_substream *substream) | ||
498 | { | ||
499 | snd_cx88_card_t *chip = snd_pcm_substream_chip(substream); | ||
500 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
501 | u16 count; | ||
502 | |||
503 | count = atomic_read(&chip->count); | ||
504 | |||
505 | // dprintk(2, "%s - count %d (+%u), period %d, frame %lu\n", __func__, | ||
506 | // count, new, count & (runtime->periods-1), | ||
507 | // runtime->period_size * (count & (runtime->periods-1))); | ||
508 | return runtime->period_size * (count & (runtime->periods-1)); | ||
509 | } | ||
510 | |||
511 | /* | ||
512 | * page callback (needed for mmap) | ||
513 | */ | ||
514 | static struct page *snd_cx88_page(struct snd_pcm_substream *substream, | ||
515 | unsigned long offset) | ||
516 | { | ||
517 | void *pageptr = substream->runtime->dma_area + offset; | ||
518 | return vmalloc_to_page(pageptr); | ||
519 | } | ||
520 | |||
521 | /* | ||
522 | * operators | ||
523 | */ | ||
524 | static struct snd_pcm_ops snd_cx88_pcm_ops = { | ||
525 | .open = snd_cx88_pcm_open, | ||
526 | .close = snd_cx88_close, | ||
527 | .ioctl = snd_pcm_lib_ioctl, | ||
528 | .hw_params = snd_cx88_hw_params, | ||
529 | .hw_free = snd_cx88_hw_free, | ||
530 | .prepare = snd_cx88_prepare, | ||
531 | .trigger = snd_cx88_card_trigger, | ||
532 | .pointer = snd_cx88_pointer, | ||
533 | .page = snd_cx88_page, | ||
534 | }; | ||
535 | |||
536 | /* | ||
537 | * create a PCM device | ||
538 | */ | ||
539 | static int __devinit snd_cx88_pcm(snd_cx88_card_t *chip, int device, const char *name) | ||
540 | { | ||
541 | int err; | ||
542 | struct snd_pcm *pcm; | ||
543 | |||
544 | err = snd_pcm_new(chip->card, name, device, 0, 1, &pcm); | ||
545 | if (err < 0) | ||
546 | return err; | ||
547 | pcm->private_data = chip; | ||
548 | strcpy(pcm->name, name); | ||
549 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cx88_pcm_ops); | ||
550 | |||
551 | return 0; | ||
552 | } | ||
553 | |||
554 | /**************************************************************************** | ||
555 | CONTROL INTERFACE | ||
556 | ****************************************************************************/ | ||
557 | static int snd_cx88_volume_info(struct snd_kcontrol *kcontrol, | ||
558 | struct snd_ctl_elem_info *info) | ||
559 | { | ||
560 | info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
561 | info->count = 2; | ||
562 | info->value.integer.min = 0; | ||
563 | info->value.integer.max = 0x3f; | ||
564 | |||
565 | return 0; | ||
566 | } | ||
567 | |||
568 | static int snd_cx88_volume_get(struct snd_kcontrol *kcontrol, | ||
569 | struct snd_ctl_elem_value *value) | ||
570 | { | ||
571 | snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); | ||
572 | struct cx88_core *core=chip->core; | ||
573 | int vol = 0x3f - (cx_read(AUD_VOL_CTL) & 0x3f), | ||
574 | bal = cx_read(AUD_BAL_CTL); | ||
575 | |||
576 | value->value.integer.value[(bal & 0x40) ? 0 : 1] = vol; | ||
577 | vol -= (bal & 0x3f); | ||
578 | value->value.integer.value[(bal & 0x40) ? 1 : 0] = vol < 0 ? 0 : vol; | ||
579 | |||
580 | return 0; | ||
581 | } | ||
582 | |||
583 | static void snd_cx88_wm8775_volume_put(struct snd_kcontrol *kcontrol, | ||
584 | struct snd_ctl_elem_value *value) | ||
585 | { | ||
586 | snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); | ||
587 | struct cx88_core *core = chip->core; | ||
588 | int left = value->value.integer.value[0]; | ||
589 | int right = value->value.integer.value[1]; | ||
590 | int v, b; | ||
591 | |||
592 | /* Pass volume & balance onto any WM8775 */ | ||
593 | if (left >= right) { | ||
594 | v = left << 10; | ||
595 | b = left ? (0x8000 * right) / left : 0x8000; | ||
596 | } else { | ||
597 | v = right << 10; | ||
598 | b = right ? 0xffff - (0x8000 * left) / right : 0x8000; | ||
599 | } | ||
600 | wm8775_s_ctrl(core, V4L2_CID_AUDIO_VOLUME, v); | ||
601 | wm8775_s_ctrl(core, V4L2_CID_AUDIO_BALANCE, b); | ||
602 | } | ||
603 | |||
604 | /* OK - TODO: test it */ | ||
605 | static int snd_cx88_volume_put(struct snd_kcontrol *kcontrol, | ||
606 | struct snd_ctl_elem_value *value) | ||
607 | { | ||
608 | snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); | ||
609 | struct cx88_core *core=chip->core; | ||
610 | int left, right, v, b; | ||
611 | int changed = 0; | ||
612 | u32 old; | ||
613 | |||
614 | if (core->board.audio_chip == V4L2_IDENT_WM8775) | ||
615 | snd_cx88_wm8775_volume_put(kcontrol, value); | ||
616 | |||
617 | left = value->value.integer.value[0] & 0x3f; | ||
618 | right = value->value.integer.value[1] & 0x3f; | ||
619 | b = right - left; | ||
620 | if (b < 0) { | ||
621 | v = 0x3f - left; | ||
622 | b = (-b) | 0x40; | ||
623 | } else { | ||
624 | v = 0x3f - right; | ||
625 | } | ||
626 | /* Do we really know this will always be called with IRQs on? */ | ||
627 | spin_lock_irq(&chip->reg_lock); | ||
628 | old = cx_read(AUD_VOL_CTL); | ||
629 | if (v != (old & 0x3f)) { | ||
630 | cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, (old & ~0x3f) | v); | ||
631 | changed = 1; | ||
632 | } | ||
633 | if ((cx_read(AUD_BAL_CTL) & 0x7f) != b) { | ||
634 | cx_write(AUD_BAL_CTL, b); | ||
635 | changed = 1; | ||
636 | } | ||
637 | spin_unlock_irq(&chip->reg_lock); | ||
638 | |||
639 | return changed; | ||
640 | } | ||
641 | |||
642 | static const DECLARE_TLV_DB_SCALE(snd_cx88_db_scale, -6300, 100, 0); | ||
643 | |||
644 | static const struct snd_kcontrol_new snd_cx88_volume = { | ||
645 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
646 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | | ||
647 | SNDRV_CTL_ELEM_ACCESS_TLV_READ, | ||
648 | .name = "Analog-TV Volume", | ||
649 | .info = snd_cx88_volume_info, | ||
650 | .get = snd_cx88_volume_get, | ||
651 | .put = snd_cx88_volume_put, | ||
652 | .tlv.p = snd_cx88_db_scale, | ||
653 | }; | ||
654 | |||
655 | static int snd_cx88_switch_get(struct snd_kcontrol *kcontrol, | ||
656 | struct snd_ctl_elem_value *value) | ||
657 | { | ||
658 | snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); | ||
659 | struct cx88_core *core = chip->core; | ||
660 | u32 bit = kcontrol->private_value; | ||
661 | |||
662 | value->value.integer.value[0] = !(cx_read(AUD_VOL_CTL) & bit); | ||
663 | return 0; | ||
664 | } | ||
665 | |||
666 | static int snd_cx88_switch_put(struct snd_kcontrol *kcontrol, | ||
667 | struct snd_ctl_elem_value *value) | ||
668 | { | ||
669 | snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); | ||
670 | struct cx88_core *core = chip->core; | ||
671 | u32 bit = kcontrol->private_value; | ||
672 | int ret = 0; | ||
673 | u32 vol; | ||
674 | |||
675 | spin_lock_irq(&chip->reg_lock); | ||
676 | vol = cx_read(AUD_VOL_CTL); | ||
677 | if (value->value.integer.value[0] != !(vol & bit)) { | ||
678 | vol ^= bit; | ||
679 | cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, vol); | ||
680 | /* Pass mute onto any WM8775 */ | ||
681 | if ((core->board.audio_chip == V4L2_IDENT_WM8775) && | ||
682 | ((1<<6) == bit)) | ||
683 | wm8775_s_ctrl(core, V4L2_CID_AUDIO_MUTE, 0 != (vol & bit)); | ||
684 | ret = 1; | ||
685 | } | ||
686 | spin_unlock_irq(&chip->reg_lock); | ||
687 | return ret; | ||
688 | } | ||
689 | |||
690 | static const struct snd_kcontrol_new snd_cx88_dac_switch = { | ||
691 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
692 | .name = "Audio-Out Switch", | ||
693 | .info = snd_ctl_boolean_mono_info, | ||
694 | .get = snd_cx88_switch_get, | ||
695 | .put = snd_cx88_switch_put, | ||
696 | .private_value = (1<<8), | ||
697 | }; | ||
698 | |||
699 | static const struct snd_kcontrol_new snd_cx88_source_switch = { | ||
700 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
701 | .name = "Analog-TV Switch", | ||
702 | .info = snd_ctl_boolean_mono_info, | ||
703 | .get = snd_cx88_switch_get, | ||
704 | .put = snd_cx88_switch_put, | ||
705 | .private_value = (1<<6), | ||
706 | }; | ||
707 | |||
708 | static int snd_cx88_alc_get(struct snd_kcontrol *kcontrol, | ||
709 | struct snd_ctl_elem_value *value) | ||
710 | { | ||
711 | snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); | ||
712 | struct cx88_core *core = chip->core; | ||
713 | s32 val; | ||
714 | |||
715 | val = wm8775_g_ctrl(core, V4L2_CID_AUDIO_LOUDNESS); | ||
716 | value->value.integer.value[0] = val ? 1 : 0; | ||
717 | return 0; | ||
718 | } | ||
719 | |||
720 | static int snd_cx88_alc_put(struct snd_kcontrol *kcontrol, | ||
721 | struct snd_ctl_elem_value *value) | ||
722 | { | ||
723 | snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); | ||
724 | struct cx88_core *core = chip->core; | ||
725 | struct v4l2_control client_ctl; | ||
726 | |||
727 | memset(&client_ctl, 0, sizeof(client_ctl)); | ||
728 | client_ctl.value = 0 != value->value.integer.value[0]; | ||
729 | client_ctl.id = V4L2_CID_AUDIO_LOUDNESS; | ||
730 | call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl); | ||
731 | |||
732 | return 0; | ||
733 | } | ||
734 | |||
735 | static struct snd_kcontrol_new snd_cx88_alc_switch = { | ||
736 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
737 | .name = "Line-In ALC Switch", | ||
738 | .info = snd_ctl_boolean_mono_info, | ||
739 | .get = snd_cx88_alc_get, | ||
740 | .put = snd_cx88_alc_put, | ||
741 | }; | ||
742 | |||
743 | /**************************************************************************** | ||
744 | Basic Flow for Sound Devices | ||
745 | ****************************************************************************/ | ||
746 | |||
747 | /* | ||
748 | * PCI ID Table - 14f1:8801 and 14f1:8811 means function 1: Audio | ||
749 | * Only boards with eeprom and byte 1 at eeprom=1 have it | ||
750 | */ | ||
751 | |||
752 | static const struct pci_device_id cx88_audio_pci_tbl[] __devinitdata = { | ||
753 | {0x14f1,0x8801,PCI_ANY_ID,PCI_ANY_ID,0,0,0}, | ||
754 | {0x14f1,0x8811,PCI_ANY_ID,PCI_ANY_ID,0,0,0}, | ||
755 | {0, } | ||
756 | }; | ||
757 | MODULE_DEVICE_TABLE(pci, cx88_audio_pci_tbl); | ||
758 | |||
759 | /* | ||
760 | * Chip-specific destructor | ||
761 | */ | ||
762 | |||
763 | static int snd_cx88_free(snd_cx88_card_t *chip) | ||
764 | { | ||
765 | |||
766 | if (chip->irq >= 0) | ||
767 | free_irq(chip->irq, chip); | ||
768 | |||
769 | cx88_core_put(chip->core,chip->pci); | ||
770 | |||
771 | pci_disable_device(chip->pci); | ||
772 | return 0; | ||
773 | } | ||
774 | |||
775 | /* | ||
776 | * Component Destructor | ||
777 | */ | ||
778 | static void snd_cx88_dev_free(struct snd_card * card) | ||
779 | { | ||
780 | snd_cx88_card_t *chip = card->private_data; | ||
781 | |||
782 | snd_cx88_free(chip); | ||
783 | } | ||
784 | |||
785 | |||
786 | /* | ||
787 | * Alsa Constructor - Component probe | ||
788 | */ | ||
789 | |||
790 | static int devno; | ||
791 | static int __devinit snd_cx88_create(struct snd_card *card, | ||
792 | struct pci_dev *pci, | ||
793 | snd_cx88_card_t **rchip, | ||
794 | struct cx88_core **core_ptr) | ||
795 | { | ||
796 | snd_cx88_card_t *chip; | ||
797 | struct cx88_core *core; | ||
798 | int err; | ||
799 | unsigned char pci_lat; | ||
800 | |||
801 | *rchip = NULL; | ||
802 | |||
803 | err = pci_enable_device(pci); | ||
804 | if (err < 0) | ||
805 | return err; | ||
806 | |||
807 | pci_set_master(pci); | ||
808 | |||
809 | chip = card->private_data; | ||
810 | |||
811 | core = cx88_core_get(pci); | ||
812 | if (NULL == core) { | ||
813 | err = -EINVAL; | ||
814 | return err; | ||
815 | } | ||
816 | |||
817 | if (!pci_dma_supported(pci,DMA_BIT_MASK(32))) { | ||
818 | dprintk(0, "%s/1: Oops: no 32bit PCI DMA ???\n",core->name); | ||
819 | err = -EIO; | ||
820 | cx88_core_put(core, pci); | ||
821 | return err; | ||
822 | } | ||
823 | |||
824 | |||
825 | /* pci init */ | ||
826 | chip->card = card; | ||
827 | chip->pci = pci; | ||
828 | chip->irq = -1; | ||
829 | spin_lock_init(&chip->reg_lock); | ||
830 | |||
831 | chip->core = core; | ||
832 | |||
833 | /* get irq */ | ||
834 | err = request_irq(chip->pci->irq, cx8801_irq, | ||
835 | IRQF_SHARED | IRQF_DISABLED, chip->core->name, chip); | ||
836 | if (err < 0) { | ||
837 | dprintk(0, "%s: can't get IRQ %d\n", | ||
838 | chip->core->name, chip->pci->irq); | ||
839 | return err; | ||
840 | } | ||
841 | |||
842 | /* print pci info */ | ||
843 | pci_read_config_byte(pci, PCI_LATENCY_TIMER, &pci_lat); | ||
844 | |||
845 | dprintk(1,"ALSA %s/%i: found at %s, rev: %d, irq: %d, " | ||
846 | "latency: %d, mmio: 0x%llx\n", core->name, devno, | ||
847 | pci_name(pci), pci->revision, pci->irq, | ||
848 | pci_lat, (unsigned long long)pci_resource_start(pci,0)); | ||
849 | |||
850 | chip->irq = pci->irq; | ||
851 | synchronize_irq(chip->irq); | ||
852 | |||
853 | snd_card_set_dev(card, &pci->dev); | ||
854 | |||
855 | *rchip = chip; | ||
856 | *core_ptr = core; | ||
857 | |||
858 | return 0; | ||
859 | } | ||
860 | |||
861 | static int __devinit cx88_audio_initdev(struct pci_dev *pci, | ||
862 | const struct pci_device_id *pci_id) | ||
863 | { | ||
864 | struct snd_card *card; | ||
865 | snd_cx88_card_t *chip; | ||
866 | struct cx88_core *core = NULL; | ||
867 | int err; | ||
868 | |||
869 | if (devno >= SNDRV_CARDS) | ||
870 | return (-ENODEV); | ||
871 | |||
872 | if (!enable[devno]) { | ||
873 | ++devno; | ||
874 | return (-ENOENT); | ||
875 | } | ||
876 | |||
877 | err = snd_card_create(index[devno], id[devno], THIS_MODULE, | ||
878 | sizeof(snd_cx88_card_t), &card); | ||
879 | if (err < 0) | ||
880 | return err; | ||
881 | |||
882 | card->private_free = snd_cx88_dev_free; | ||
883 | |||
884 | err = snd_cx88_create(card, pci, &chip, &core); | ||
885 | if (err < 0) | ||
886 | goto error; | ||
887 | |||
888 | err = snd_cx88_pcm(chip, 0, "CX88 Digital"); | ||
889 | if (err < 0) | ||
890 | goto error; | ||
891 | |||
892 | err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_volume, chip)); | ||
893 | if (err < 0) | ||
894 | goto error; | ||
895 | err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_dac_switch, chip)); | ||
896 | if (err < 0) | ||
897 | goto error; | ||
898 | err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_source_switch, chip)); | ||
899 | if (err < 0) | ||
900 | goto error; | ||
901 | |||
902 | /* If there's a wm8775 then add a Line-In ALC switch */ | ||
903 | if (core->board.audio_chip == V4L2_IDENT_WM8775) | ||
904 | snd_ctl_add(card, snd_ctl_new1(&snd_cx88_alc_switch, chip)); | ||
905 | |||
906 | strcpy (card->driver, "CX88x"); | ||
907 | sprintf(card->shortname, "Conexant CX%x", pci->device); | ||
908 | sprintf(card->longname, "%s at %#llx", | ||
909 | card->shortname,(unsigned long long)pci_resource_start(pci, 0)); | ||
910 | strcpy (card->mixername, "CX88"); | ||
911 | |||
912 | dprintk (0, "%s/%i: ALSA support for cx2388x boards\n", | ||
913 | card->driver,devno); | ||
914 | |||
915 | err = snd_card_register(card); | ||
916 | if (err < 0) | ||
917 | goto error; | ||
918 | pci_set_drvdata(pci,card); | ||
919 | |||
920 | devno++; | ||
921 | return 0; | ||
922 | |||
923 | error: | ||
924 | snd_card_free(card); | ||
925 | return err; | ||
926 | } | ||
927 | /* | ||
928 | * ALSA destructor | ||
929 | */ | ||
930 | static void __devexit cx88_audio_finidev(struct pci_dev *pci) | ||
931 | { | ||
932 | struct cx88_audio_dev *card = pci_get_drvdata(pci); | ||
933 | |||
934 | snd_card_free((void *)card); | ||
935 | |||
936 | pci_set_drvdata(pci, NULL); | ||
937 | |||
938 | devno--; | ||
939 | } | ||
940 | |||
941 | /* | ||
942 | * PCI driver definition | ||
943 | */ | ||
944 | |||
945 | static struct pci_driver cx88_audio_pci_driver = { | ||
946 | .name = "cx88_audio", | ||
947 | .id_table = cx88_audio_pci_tbl, | ||
948 | .probe = cx88_audio_initdev, | ||
949 | .remove = __devexit_p(cx88_audio_finidev), | ||
950 | }; | ||
951 | |||
952 | /**************************************************************************** | ||
953 | LINUX MODULE INIT | ||
954 | ****************************************************************************/ | ||
955 | |||
956 | /* | ||
957 | * module init | ||
958 | */ | ||
959 | static int __init cx88_audio_init(void) | ||
960 | { | ||
961 | printk(KERN_INFO "cx2388x alsa driver version %s loaded\n", | ||
962 | CX88_VERSION); | ||
963 | return pci_register_driver(&cx88_audio_pci_driver); | ||
964 | } | ||
965 | |||
966 | /* | ||
967 | * module remove | ||
968 | */ | ||
969 | static void __exit cx88_audio_fini(void) | ||
970 | { | ||
971 | pci_unregister_driver(&cx88_audio_pci_driver); | ||
972 | } | ||
973 | |||
974 | module_init(cx88_audio_init); | ||
975 | module_exit(cx88_audio_fini); | ||