diff options
author | Mijhail Moreyra <mijhail.moreyra@gmail.com> | 2011-10-10 10:09:52 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2011-10-14 15:44:54 -0400 |
commit | 9e44d63246a9c884900e56e2aa16fba94dee5f0c (patch) | |
tree | ec804ca3a11d9eba24972aa646945685cb52b62d | |
parent | 873c07da6abd5519a7850caa90f226d3b15e3fba (diff) |
[media] cx23885: Add ALSA support
[stoth@kernellabs.com: add it to the makefile and fix snd_card binding]
[liplianin@netup.ru: videobuf: Remove the videobuf_sg_dma_map/unmap functions]
Signed-off-by: Mijhail Moreyra <mijhail.moreyra@gmail.com>
Signed-off-by: Steven Toth <stoth@kernellabs.com>
Signed-off-by: Igor M. Liplianin <liplianin@netup.ru>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r-- | drivers/media/video/cx23885/Makefile | 2 | ||||
-rw-r--r-- | drivers/media/video/cx23885/cx23885-alsa.c | 535 | ||||
-rw-r--r-- | drivers/media/video/cx23885/cx23885-core.c | 48 | ||||
-rw-r--r-- | drivers/media/video/cx23885/cx23885.h | 42 |
4 files changed, 611 insertions, 16 deletions
diff --git a/drivers/media/video/cx23885/Makefile b/drivers/media/video/cx23885/Makefile index 185cc019d3db..f81f2796a0f9 100644 --- a/drivers/media/video/cx23885/Makefile +++ b/drivers/media/video/cx23885/Makefile | |||
@@ -2,7 +2,7 @@ cx23885-objs := cx23885-cards.o cx23885-video.o cx23885-vbi.o \ | |||
2 | cx23885-core.o cx23885-i2c.o cx23885-dvb.o cx23885-417.o \ | 2 | cx23885-core.o cx23885-i2c.o cx23885-dvb.o cx23885-417.o \ |
3 | cx23885-ioctl.o cx23885-ir.o cx23885-av.o cx23885-input.o \ | 3 | cx23885-ioctl.o cx23885-ir.o cx23885-av.o cx23885-input.o \ |
4 | cx23888-ir.o netup-init.o cimax2.o netup-eeprom.o \ | 4 | cx23888-ir.o netup-init.o cimax2.o netup-eeprom.o \ |
5 | cx23885-f300.o | 5 | cx23885-f300.o cx23885-alsa.o |
6 | 6 | ||
7 | obj-$(CONFIG_VIDEO_CX23885) += cx23885.o | 7 | obj-$(CONFIG_VIDEO_CX23885) += cx23885.o |
8 | obj-$(CONFIG_MEDIA_ALTERA_CI) += altera-ci.o | 8 | obj-$(CONFIG_MEDIA_ALTERA_CI) += altera-ci.o |
diff --git a/drivers/media/video/cx23885/cx23885-alsa.c b/drivers/media/video/cx23885/cx23885-alsa.c new file mode 100644 index 000000000000..31a89b3ac1d0 --- /dev/null +++ b/drivers/media/video/cx23885/cx23885-alsa.c | |||
@@ -0,0 +1,535 @@ | |||
1 | /* | ||
2 | * | ||
3 | * Support for CX23885 analog audio capture | ||
4 | * | ||
5 | * (c) 2008 Mijhail Moreyra <mijhail.moreyra@gmail.com> | ||
6 | * Adapted from cx88-alsa.c | ||
7 | * (c) 2009 Steven Toth <stoth@kernellabs.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation; either version 2 of the License, or | ||
12 | * (at your option) any later version. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
22 | */ | ||
23 | |||
24 | #include <linux/module.h> | ||
25 | #include <linux/init.h> | ||
26 | #include <linux/device.h> | ||
27 | #include <linux/interrupt.h> | ||
28 | #include <linux/vmalloc.h> | ||
29 | #include <linux/dma-mapping.h> | ||
30 | #include <linux/pci.h> | ||
31 | |||
32 | #include <asm/delay.h> | ||
33 | |||
34 | #include <sound/core.h> | ||
35 | #include <sound/pcm.h> | ||
36 | #include <sound/pcm_params.h> | ||
37 | #include <sound/control.h> | ||
38 | #include <sound/initval.h> | ||
39 | |||
40 | #include <sound/tlv.h> | ||
41 | |||
42 | |||
43 | #include "cx23885.h" | ||
44 | #include "cx23885-reg.h" | ||
45 | |||
46 | #define AUDIO_SRAM_CHANNEL SRAM_CH07 | ||
47 | |||
48 | #define dprintk(level, fmt, arg...) if (audio_debug >= level) \ | ||
49 | printk(KERN_INFO "%s/1: " fmt, chip->dev->name , ## arg) | ||
50 | |||
51 | #define dprintk_core(level, fmt, arg...) if (audio_debug >= level) \ | ||
52 | printk(KERN_DEBUG "%s/1: " fmt, chip->dev->name , ## arg) | ||
53 | |||
54 | /**************************************************************************** | ||
55 | Module global static vars | ||
56 | ****************************************************************************/ | ||
57 | |||
58 | static unsigned int disable_analog_audio; | ||
59 | module_param(disable_analog_audio, int, 0644); | ||
60 | MODULE_PARM_DESC(disable_analog_audio, "disable analog audio ALSA driver"); | ||
61 | |||
62 | static unsigned int audio_debug; | ||
63 | module_param(audio_debug, int, 0644); | ||
64 | MODULE_PARM_DESC(audio_debug, "enable debug messages [analog audio]"); | ||
65 | |||
66 | /**************************************************************************** | ||
67 | Board specific funtions | ||
68 | ****************************************************************************/ | ||
69 | |||
70 | /* Constants taken from cx88-reg.h */ | ||
71 | #define AUD_INT_DN_RISCI1 (1 << 0) | ||
72 | #define AUD_INT_UP_RISCI1 (1 << 1) | ||
73 | #define AUD_INT_RDS_DN_RISCI1 (1 << 2) | ||
74 | #define AUD_INT_DN_RISCI2 (1 << 4) /* yes, 3 is skipped */ | ||
75 | #define AUD_INT_UP_RISCI2 (1 << 5) | ||
76 | #define AUD_INT_RDS_DN_RISCI2 (1 << 6) | ||
77 | #define AUD_INT_DN_SYNC (1 << 12) | ||
78 | #define AUD_INT_UP_SYNC (1 << 13) | ||
79 | #define AUD_INT_RDS_DN_SYNC (1 << 14) | ||
80 | #define AUD_INT_OPC_ERR (1 << 16) | ||
81 | #define AUD_INT_BER_IRQ (1 << 20) | ||
82 | #define AUD_INT_MCHG_IRQ (1 << 21) | ||
83 | #define GP_COUNT_CONTROL_RESET 0x3 | ||
84 | |||
85 | /* | ||
86 | * BOARD Specific: Sets audio DMA | ||
87 | */ | ||
88 | |||
89 | static int cx23885_start_audio_dma(struct cx23885_audio_dev *chip) | ||
90 | { | ||
91 | struct cx23885_audio_buffer *buf = chip->buf; | ||
92 | struct cx23885_dev *dev = chip->dev; | ||
93 | struct sram_channel *audio_ch = | ||
94 | &dev->sram_channels[AUDIO_SRAM_CHANNEL]; | ||
95 | |||
96 | dprintk(1, "%s()\n", __func__); | ||
97 | |||
98 | /* Make sure RISC/FIFO are off before changing FIFO/RISC settings */ | ||
99 | cx_clear(AUD_INT_DMA_CTL, 0x11); | ||
100 | |||
101 | /* setup fifo + format - out channel */ | ||
102 | cx23885_sram_channel_setup(chip->dev, audio_ch, buf->bpl, | ||
103 | buf->risc.dma); | ||
104 | |||
105 | /* sets bpl size */ | ||
106 | cx_write(AUD_INT_A_LNGTH, buf->bpl); | ||
107 | |||
108 | /* This is required to get good audio (1 seems to be ok) */ | ||
109 | cx_write(AUD_INT_A_MODE, 1); | ||
110 | |||
111 | /* reset counter */ | ||
112 | cx_write(AUD_INT_A_GPCNT_CTL, GP_COUNT_CONTROL_RESET); | ||
113 | atomic_set(&chip->count, 0); | ||
114 | |||
115 | dprintk(1, "Start audio DMA, %d B/line, %d lines/FIFO, %d periods, %d " | ||
116 | "byte buffer\n", buf->bpl, cx_read(audio_ch->cmds_start+12)>>1, | ||
117 | chip->num_periods, buf->bpl * chip->num_periods); | ||
118 | |||
119 | /* Enables corresponding bits at AUD_INT_STAT */ | ||
120 | cx_write(AUDIO_INT_INT_MSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC | | ||
121 | AUD_INT_DN_RISCI1); | ||
122 | |||
123 | /* Clean any pending interrupt bits already set */ | ||
124 | cx_write(AUDIO_INT_INT_STAT, ~0); | ||
125 | |||
126 | /* enable audio irqs */ | ||
127 | cx_set(PCI_INT_MSK, chip->dev->pci_irqmask | PCI_MSK_AUD_INT); | ||
128 | |||
129 | /* start dma */ | ||
130 | cx_set(DEV_CNTRL2, (1<<5)); /* Enables Risc Processor */ | ||
131 | cx_set(AUD_INT_DMA_CTL, 0x11); /* audio downstream FIFO and | ||
132 | RISC enable */ | ||
133 | if (audio_debug) | ||
134 | cx23885_sram_channel_dump(chip->dev, audio_ch); | ||
135 | |||
136 | return 0; | ||
137 | } | ||
138 | |||
139 | /* | ||
140 | * BOARD Specific: Resets audio DMA | ||
141 | */ | ||
142 | static int cx23885_stop_audio_dma(struct cx23885_audio_dev *chip) | ||
143 | { | ||
144 | struct cx23885_dev *dev = chip->dev; | ||
145 | dprintk(1, "Stopping audio DMA\n"); | ||
146 | |||
147 | /* stop dma */ | ||
148 | cx_clear(AUD_INT_DMA_CTL, 0x11); | ||
149 | |||
150 | /* disable irqs */ | ||
151 | cx_clear(PCI_INT_MSK, PCI_MSK_AUD_INT); | ||
152 | cx_clear(AUDIO_INT_INT_MSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC | | ||
153 | AUD_INT_DN_RISCI1); | ||
154 | |||
155 | if (audio_debug) | ||
156 | cx23885_sram_channel_dump(chip->dev, | ||
157 | &dev->sram_channels[AUDIO_SRAM_CHANNEL]); | ||
158 | |||
159 | return 0; | ||
160 | } | ||
161 | |||
162 | /* | ||
163 | * BOARD Specific: Handles audio IRQ | ||
164 | */ | ||
165 | int cx23885_audio_irq(struct cx23885_dev *dev, u32 status, u32 mask) | ||
166 | { | ||
167 | struct cx23885_audio_dev *chip = dev->audio_dev; | ||
168 | |||
169 | if (0 == (status & mask)) | ||
170 | return 0; | ||
171 | |||
172 | cx_write(AUDIO_INT_INT_STAT, status); | ||
173 | |||
174 | /* risc op code error */ | ||
175 | if (status & AUD_INT_OPC_ERR) { | ||
176 | printk(KERN_WARNING "%s/1: Audio risc op code error\n", | ||
177 | dev->name); | ||
178 | cx_clear(AUD_INT_DMA_CTL, 0x11); | ||
179 | cx23885_sram_channel_dump(dev, | ||
180 | &dev->sram_channels[AUDIO_SRAM_CHANNEL]); | ||
181 | } | ||
182 | if (status & AUD_INT_DN_SYNC) { | ||
183 | dprintk(1, "Downstream sync error\n"); | ||
184 | cx_write(AUD_INT_A_GPCNT_CTL, GP_COUNT_CONTROL_RESET); | ||
185 | return 1; | ||
186 | } | ||
187 | /* risc1 downstream */ | ||
188 | if (status & AUD_INT_DN_RISCI1) { | ||
189 | atomic_set(&chip->count, cx_read(AUD_INT_A_GPCNT)); | ||
190 | snd_pcm_period_elapsed(chip->substream); | ||
191 | } | ||
192 | /* FIXME: Any other status should deserve a special handling? */ | ||
193 | |||
194 | return 1; | ||
195 | } | ||
196 | |||
197 | static int dsp_buffer_free(struct cx23885_audio_dev *chip) | ||
198 | { | ||
199 | BUG_ON(!chip->dma_size); | ||
200 | |||
201 | dprintk(2, "Freeing buffer\n"); | ||
202 | videobuf_dma_unmap(&chip->pci->dev, chip->dma_risc); | ||
203 | videobuf_dma_free(chip->dma_risc); | ||
204 | btcx_riscmem_free(chip->pci, &chip->buf->risc); | ||
205 | kfree(chip->buf); | ||
206 | |||
207 | chip->dma_risc = NULL; | ||
208 | chip->dma_size = 0; | ||
209 | |||
210 | return 0; | ||
211 | } | ||
212 | |||
213 | /**************************************************************************** | ||
214 | ALSA PCM Interface | ||
215 | ****************************************************************************/ | ||
216 | |||
217 | /* | ||
218 | * Digital hardware definition | ||
219 | */ | ||
220 | #define DEFAULT_FIFO_SIZE 4096 | ||
221 | |||
222 | static struct snd_pcm_hardware snd_cx23885_digital_hw = { | ||
223 | .info = SNDRV_PCM_INFO_MMAP | | ||
224 | SNDRV_PCM_INFO_INTERLEAVED | | ||
225 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | ||
226 | SNDRV_PCM_INFO_MMAP_VALID, | ||
227 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
228 | |||
229 | .rates = SNDRV_PCM_RATE_48000, | ||
230 | .rate_min = 48000, | ||
231 | .rate_max = 48000, | ||
232 | .channels_min = 2, | ||
233 | .channels_max = 2, | ||
234 | /* Analog audio output will be full of clicks and pops if there | ||
235 | are not exactly four lines in the SRAM FIFO buffer. */ | ||
236 | .period_bytes_min = DEFAULT_FIFO_SIZE/4, | ||
237 | .period_bytes_max = DEFAULT_FIFO_SIZE/4, | ||
238 | .periods_min = 1, | ||
239 | .periods_max = 1024, | ||
240 | .buffer_bytes_max = (1024*1024), | ||
241 | }; | ||
242 | |||
243 | /* | ||
244 | * audio pcm capture open callback | ||
245 | */ | ||
246 | static int snd_cx23885_pcm_open(struct snd_pcm_substream *substream) | ||
247 | { | ||
248 | struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream); | ||
249 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
250 | int err; | ||
251 | |||
252 | if (!chip) { | ||
253 | printk(KERN_ERR "BUG: cx23885 can't find device struct." | ||
254 | " Can't proceed with open\n"); | ||
255 | return -ENODEV; | ||
256 | } | ||
257 | |||
258 | err = snd_pcm_hw_constraint_pow2(runtime, 0, | ||
259 | SNDRV_PCM_HW_PARAM_PERIODS); | ||
260 | if (err < 0) | ||
261 | goto _error; | ||
262 | |||
263 | chip->substream = substream; | ||
264 | |||
265 | runtime->hw = snd_cx23885_digital_hw; | ||
266 | |||
267 | if (chip->dev->sram_channels[AUDIO_SRAM_CHANNEL].fifo_size != | ||
268 | DEFAULT_FIFO_SIZE) { | ||
269 | unsigned int bpl = chip->dev-> | ||
270 | sram_channels[AUDIO_SRAM_CHANNEL].fifo_size / 4; | ||
271 | bpl &= ~7; /* must be multiple of 8 */ | ||
272 | runtime->hw.period_bytes_min = bpl; | ||
273 | runtime->hw.period_bytes_max = bpl; | ||
274 | } | ||
275 | |||
276 | return 0; | ||
277 | _error: | ||
278 | dprintk(1, "Error opening PCM!\n"); | ||
279 | return err; | ||
280 | } | ||
281 | |||
282 | /* | ||
283 | * audio close callback | ||
284 | */ | ||
285 | static int snd_cx23885_close(struct snd_pcm_substream *substream) | ||
286 | { | ||
287 | return 0; | ||
288 | } | ||
289 | |||
290 | /* | ||
291 | * hw_params callback | ||
292 | */ | ||
293 | static int snd_cx23885_hw_params(struct snd_pcm_substream *substream, | ||
294 | struct snd_pcm_hw_params *hw_params) | ||
295 | { | ||
296 | struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream); | ||
297 | struct videobuf_dmabuf *dma; | ||
298 | |||
299 | struct cx23885_audio_buffer *buf; | ||
300 | int ret; | ||
301 | |||
302 | if (substream->runtime->dma_area) { | ||
303 | dsp_buffer_free(chip); | ||
304 | substream->runtime->dma_area = NULL; | ||
305 | } | ||
306 | |||
307 | chip->period_size = params_period_bytes(hw_params); | ||
308 | chip->num_periods = params_periods(hw_params); | ||
309 | chip->dma_size = chip->period_size * params_periods(hw_params); | ||
310 | |||
311 | BUG_ON(!chip->dma_size); | ||
312 | BUG_ON(chip->num_periods & (chip->num_periods-1)); | ||
313 | |||
314 | buf = kzalloc(sizeof(*buf), GFP_KERNEL); | ||
315 | if (NULL == buf) | ||
316 | return -ENOMEM; | ||
317 | |||
318 | buf->bpl = chip->period_size; | ||
319 | |||
320 | dma = &buf->dma; | ||
321 | videobuf_dma_init(dma); | ||
322 | ret = videobuf_dma_init_kernel(dma, PCI_DMA_FROMDEVICE, | ||
323 | (PAGE_ALIGN(chip->dma_size) >> PAGE_SHIFT)); | ||
324 | if (ret < 0) | ||
325 | goto error; | ||
326 | |||
327 | ret = videobuf_dma_map(&chip->pci->dev, dma); | ||
328 | if (ret < 0) | ||
329 | goto error; | ||
330 | |||
331 | ret = cx23885_risc_databuffer(chip->pci, &buf->risc, dma->sglist, | ||
332 | chip->period_size, chip->num_periods, 1); | ||
333 | if (ret < 0) | ||
334 | goto error; | ||
335 | |||
336 | /* Loop back to start of program */ | ||
337 | buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP|RISC_IRQ1|RISC_CNT_INC); | ||
338 | buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma); | ||
339 | buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ | ||
340 | |||
341 | chip->buf = buf; | ||
342 | chip->dma_risc = dma; | ||
343 | |||
344 | substream->runtime->dma_area = chip->dma_risc->vaddr; | ||
345 | substream->runtime->dma_bytes = chip->dma_size; | ||
346 | substream->runtime->dma_addr = 0; | ||
347 | |||
348 | return 0; | ||
349 | |||
350 | error: | ||
351 | kfree(buf); | ||
352 | return ret; | ||
353 | } | ||
354 | |||
355 | /* | ||
356 | * hw free callback | ||
357 | */ | ||
358 | static int snd_cx23885_hw_free(struct snd_pcm_substream *substream) | ||
359 | { | ||
360 | |||
361 | struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream); | ||
362 | |||
363 | if (substream->runtime->dma_area) { | ||
364 | dsp_buffer_free(chip); | ||
365 | substream->runtime->dma_area = NULL; | ||
366 | } | ||
367 | |||
368 | return 0; | ||
369 | } | ||
370 | |||
371 | /* | ||
372 | * prepare callback | ||
373 | */ | ||
374 | static int snd_cx23885_prepare(struct snd_pcm_substream *substream) | ||
375 | { | ||
376 | return 0; | ||
377 | } | ||
378 | |||
379 | /* | ||
380 | * trigger callback | ||
381 | */ | ||
382 | static int snd_cx23885_card_trigger(struct snd_pcm_substream *substream, | ||
383 | int cmd) | ||
384 | { | ||
385 | struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream); | ||
386 | int err; | ||
387 | |||
388 | /* Local interrupts are already disabled by ALSA */ | ||
389 | spin_lock(&chip->lock); | ||
390 | |||
391 | switch (cmd) { | ||
392 | case SNDRV_PCM_TRIGGER_START: | ||
393 | err = cx23885_start_audio_dma(chip); | ||
394 | break; | ||
395 | case SNDRV_PCM_TRIGGER_STOP: | ||
396 | err = cx23885_stop_audio_dma(chip); | ||
397 | break; | ||
398 | default: | ||
399 | err = -EINVAL; | ||
400 | break; | ||
401 | } | ||
402 | |||
403 | spin_unlock(&chip->lock); | ||
404 | |||
405 | return err; | ||
406 | } | ||
407 | |||
408 | /* | ||
409 | * pointer callback | ||
410 | */ | ||
411 | static snd_pcm_uframes_t snd_cx23885_pointer( | ||
412 | struct snd_pcm_substream *substream) | ||
413 | { | ||
414 | struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream); | ||
415 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
416 | u16 count; | ||
417 | |||
418 | count = atomic_read(&chip->count); | ||
419 | |||
420 | return runtime->period_size * (count & (runtime->periods-1)); | ||
421 | } | ||
422 | |||
423 | /* | ||
424 | * page callback (needed for mmap) | ||
425 | */ | ||
426 | static struct page *snd_cx23885_page(struct snd_pcm_substream *substream, | ||
427 | unsigned long offset) | ||
428 | { | ||
429 | void *pageptr = substream->runtime->dma_area + offset; | ||
430 | return vmalloc_to_page(pageptr); | ||
431 | } | ||
432 | |||
433 | /* | ||
434 | * operators | ||
435 | */ | ||
436 | static struct snd_pcm_ops snd_cx23885_pcm_ops = { | ||
437 | .open = snd_cx23885_pcm_open, | ||
438 | .close = snd_cx23885_close, | ||
439 | .ioctl = snd_pcm_lib_ioctl, | ||
440 | .hw_params = snd_cx23885_hw_params, | ||
441 | .hw_free = snd_cx23885_hw_free, | ||
442 | .prepare = snd_cx23885_prepare, | ||
443 | .trigger = snd_cx23885_card_trigger, | ||
444 | .pointer = snd_cx23885_pointer, | ||
445 | .page = snd_cx23885_page, | ||
446 | }; | ||
447 | |||
448 | /* | ||
449 | * create a PCM device | ||
450 | */ | ||
451 | static int snd_cx23885_pcm(struct cx23885_audio_dev *chip, int device, | ||
452 | char *name) | ||
453 | { | ||
454 | int err; | ||
455 | struct snd_pcm *pcm; | ||
456 | |||
457 | err = snd_pcm_new(chip->card, name, device, 0, 1, &pcm); | ||
458 | if (err < 0) | ||
459 | return err; | ||
460 | pcm->private_data = chip; | ||
461 | strcpy(pcm->name, name); | ||
462 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cx23885_pcm_ops); | ||
463 | |||
464 | return 0; | ||
465 | } | ||
466 | |||
467 | /**************************************************************************** | ||
468 | Basic Flow for Sound Devices | ||
469 | ****************************************************************************/ | ||
470 | |||
471 | /* | ||
472 | * Alsa Constructor - Component probe | ||
473 | */ | ||
474 | |||
475 | struct cx23885_audio_dev *cx23885_audio_initdev(struct cx23885_dev *dev) | ||
476 | { | ||
477 | struct snd_card *card; | ||
478 | struct cx23885_audio_dev *chip; | ||
479 | int err; | ||
480 | |||
481 | if (disable_analog_audio) | ||
482 | return NULL; | ||
483 | |||
484 | if (dev->sram_channels[AUDIO_SRAM_CHANNEL].cmds_start == 0) { | ||
485 | printk(KERN_WARNING "%s(): Missing SRAM channel configuration " | ||
486 | "for analog TV Audio\n", __func__); | ||
487 | return NULL; | ||
488 | } | ||
489 | |||
490 | err = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, | ||
491 | THIS_MODULE, sizeof(struct cx23885_audio_dev), &card); | ||
492 | if (err < 0) | ||
493 | goto error; | ||
494 | |||
495 | chip = (struct cx23885_audio_dev *) card->private_data; | ||
496 | chip->dev = dev; | ||
497 | chip->pci = dev->pci; | ||
498 | chip->card = card; | ||
499 | spin_lock_init(&chip->lock); | ||
500 | |||
501 | snd_card_set_dev(card, &dev->pci->dev); | ||
502 | |||
503 | err = snd_cx23885_pcm(chip, 0, "CX23885 Digital"); | ||
504 | if (err < 0) | ||
505 | goto error; | ||
506 | |||
507 | strcpy(card->driver, "CX23885"); | ||
508 | sprintf(card->shortname, "Conexant CX23885"); | ||
509 | sprintf(card->longname, "%s at %s", card->shortname, dev->name); | ||
510 | |||
511 | err = snd_card_register(card); | ||
512 | if (err < 0) | ||
513 | goto error; | ||
514 | |||
515 | dprintk(0, "registered ALSA audio device\n"); | ||
516 | |||
517 | return chip; | ||
518 | |||
519 | error: | ||
520 | snd_card_free(card); | ||
521 | printk(KERN_ERR "%s(): Failed to register analog " | ||
522 | "audio adapter\n", __func__); | ||
523 | |||
524 | return NULL; | ||
525 | } | ||
526 | |||
527 | /* | ||
528 | * ALSA destructor | ||
529 | */ | ||
530 | void cx23885_audio_finidev(struct cx23885_dev *dev) | ||
531 | { | ||
532 | struct cx23885_audio_dev *chip = dev->audio_dev; | ||
533 | |||
534 | snd_card_free(chip->card); | ||
535 | } | ||
diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c index ee41a8882f58..d8dfa40b4af9 100644 --- a/drivers/media/video/cx23885/cx23885-core.c +++ b/drivers/media/video/cx23885/cx23885-core.c | |||
@@ -156,11 +156,12 @@ static struct sram_channel cx23885_sram_channels[] = { | |||
156 | }, | 156 | }, |
157 | [SRAM_CH07] = { | 157 | [SRAM_CH07] = { |
158 | .name = "ch7", | 158 | .name = "ch7", |
159 | .cmds_start = 0x0, | 159 | .name = "TV Audio", |
160 | .ctrl_start = 0x0, | 160 | .cmds_start = 0x10190, |
161 | .cdt = 0x0, | 161 | .ctrl_start = 0x10480, |
162 | .fifo_start = 0x0, | 162 | .cdt = 0x10a00, |
163 | .fifo_size = 0x0, | 163 | .fifo_start = 0x7000, |
164 | .fifo_size = 0x1000, | ||
164 | .ptr1_reg = DMA6_PTR1, | 165 | .ptr1_reg = DMA6_PTR1, |
165 | .ptr2_reg = DMA6_PTR2, | 166 | .ptr2_reg = DMA6_PTR2, |
166 | .cnt1_reg = DMA6_CNT1, | 167 | .cnt1_reg = DMA6_CNT1, |
@@ -1082,10 +1083,10 @@ static void cx23885_dev_unregister(struct cx23885_dev *dev) | |||
1082 | static __le32 *cx23885_risc_field(__le32 *rp, struct scatterlist *sglist, | 1083 | static __le32 *cx23885_risc_field(__le32 *rp, struct scatterlist *sglist, |
1083 | unsigned int offset, u32 sync_line, | 1084 | unsigned int offset, u32 sync_line, |
1084 | unsigned int bpl, unsigned int padding, | 1085 | unsigned int bpl, unsigned int padding, |
1085 | unsigned int lines) | 1086 | unsigned int lines, unsigned int lpi) |
1086 | { | 1087 | { |
1087 | struct scatterlist *sg; | 1088 | struct scatterlist *sg; |
1088 | unsigned int line, todo; | 1089 | unsigned int line, todo, sol; |
1089 | 1090 | ||
1090 | /* sync instruction */ | 1091 | /* sync instruction */ |
1091 | if (sync_line != NO_SYNC_LINE) | 1092 | if (sync_line != NO_SYNC_LINE) |
@@ -1098,16 +1099,22 @@ static __le32 *cx23885_risc_field(__le32 *rp, struct scatterlist *sglist, | |||
1098 | offset -= sg_dma_len(sg); | 1099 | offset -= sg_dma_len(sg); |
1099 | sg++; | 1100 | sg++; |
1100 | } | 1101 | } |
1102 | |||
1103 | if (lpi && line > 0 && !(line % lpi)) | ||
1104 | sol = RISC_SOL | RISC_IRQ1 | RISC_CNT_INC; | ||
1105 | else | ||
1106 | sol = RISC_SOL; | ||
1107 | |||
1101 | if (bpl <= sg_dma_len(sg)-offset) { | 1108 | if (bpl <= sg_dma_len(sg)-offset) { |
1102 | /* fits into current chunk */ | 1109 | /* fits into current chunk */ |
1103 | *(rp++) = cpu_to_le32(RISC_WRITE|RISC_SOL|RISC_EOL|bpl); | 1110 | *(rp++) = cpu_to_le32(RISC_WRITE|sol|RISC_EOL|bpl); |
1104 | *(rp++) = cpu_to_le32(sg_dma_address(sg)+offset); | 1111 | *(rp++) = cpu_to_le32(sg_dma_address(sg)+offset); |
1105 | *(rp++) = cpu_to_le32(0); /* bits 63-32 */ | 1112 | *(rp++) = cpu_to_le32(0); /* bits 63-32 */ |
1106 | offset += bpl; | 1113 | offset += bpl; |
1107 | } else { | 1114 | } else { |
1108 | /* scanline needs to be split */ | 1115 | /* scanline needs to be split */ |
1109 | todo = bpl; | 1116 | todo = bpl; |
1110 | *(rp++) = cpu_to_le32(RISC_WRITE|RISC_SOL| | 1117 | *(rp++) = cpu_to_le32(RISC_WRITE|sol| |
1111 | (sg_dma_len(sg)-offset)); | 1118 | (sg_dma_len(sg)-offset)); |
1112 | *(rp++) = cpu_to_le32(sg_dma_address(sg)+offset); | 1119 | *(rp++) = cpu_to_le32(sg_dma_address(sg)+offset); |
1113 | *(rp++) = cpu_to_le32(0); /* bits 63-32 */ | 1120 | *(rp++) = cpu_to_le32(0); /* bits 63-32 */ |
@@ -1164,10 +1171,10 @@ int cx23885_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc, | |||
1164 | rp = risc->cpu; | 1171 | rp = risc->cpu; |
1165 | if (UNSET != top_offset) | 1172 | if (UNSET != top_offset) |
1166 | rp = cx23885_risc_field(rp, sglist, top_offset, 0, | 1173 | rp = cx23885_risc_field(rp, sglist, top_offset, 0, |
1167 | bpl, padding, lines); | 1174 | bpl, padding, lines, 0); |
1168 | if (UNSET != bottom_offset) | 1175 | if (UNSET != bottom_offset) |
1169 | rp = cx23885_risc_field(rp, sglist, bottom_offset, 0x200, | 1176 | rp = cx23885_risc_field(rp, sglist, bottom_offset, 0x200, |
1170 | bpl, padding, lines); | 1177 | bpl, padding, lines, 0); |
1171 | 1178 | ||
1172 | /* save pointer to jmp instruction address */ | 1179 | /* save pointer to jmp instruction address */ |
1173 | risc->jmp = rp; | 1180 | risc->jmp = rp; |
@@ -1175,11 +1182,11 @@ int cx23885_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc, | |||
1175 | return 0; | 1182 | return 0; |
1176 | } | 1183 | } |
1177 | 1184 | ||
1178 | static int cx23885_risc_databuffer(struct pci_dev *pci, | 1185 | int cx23885_risc_databuffer(struct pci_dev *pci, |
1179 | struct btcx_riscmem *risc, | 1186 | struct btcx_riscmem *risc, |
1180 | struct scatterlist *sglist, | 1187 | struct scatterlist *sglist, |
1181 | unsigned int bpl, | 1188 | unsigned int bpl, |
1182 | unsigned int lines) | 1189 | unsigned int lines, unsigned int lpi) |
1183 | { | 1190 | { |
1184 | u32 instructions; | 1191 | u32 instructions; |
1185 | __le32 *rp; | 1192 | __le32 *rp; |
@@ -1199,7 +1206,8 @@ static int cx23885_risc_databuffer(struct pci_dev *pci, | |||
1199 | 1206 | ||
1200 | /* write risc instructions */ | 1207 | /* write risc instructions */ |
1201 | rp = risc->cpu; | 1208 | rp = risc->cpu; |
1202 | rp = cx23885_risc_field(rp, sglist, 0, NO_SYNC_LINE, bpl, 0, lines); | 1209 | rp = cx23885_risc_field(rp, sglist, 0, NO_SYNC_LINE, |
1210 | bpl, 0, lines, lpi); | ||
1203 | 1211 | ||
1204 | /* save pointer to jmp instruction address */ | 1212 | /* save pointer to jmp instruction address */ |
1205 | risc->jmp = rp; | 1213 | risc->jmp = rp; |
@@ -1517,7 +1525,7 @@ int cx23885_buf_prepare(struct videobuf_queue *q, struct cx23885_tsport *port, | |||
1517 | goto fail; | 1525 | goto fail; |
1518 | cx23885_risc_databuffer(dev->pci, &buf->risc, | 1526 | cx23885_risc_databuffer(dev->pci, &buf->risc, |
1519 | videobuf_to_dma(&buf->vb)->sglist, | 1527 | videobuf_to_dma(&buf->vb)->sglist, |
1520 | buf->vb.width, buf->vb.height); | 1528 | buf->vb.width, buf->vb.height, 0); |
1521 | } | 1529 | } |
1522 | buf->vb.state = VIDEOBUF_PREPARED; | 1530 | buf->vb.state = VIDEOBUF_PREPARED; |
1523 | return 0; | 1531 | return 0; |
@@ -1741,15 +1749,19 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id) | |||
1741 | struct cx23885_tsport *ts2 = &dev->ts2; | 1749 | struct cx23885_tsport *ts2 = &dev->ts2; |
1742 | u32 pci_status, pci_mask; | 1750 | u32 pci_status, pci_mask; |
1743 | u32 vida_status, vida_mask; | 1751 | u32 vida_status, vida_mask; |
1752 | u32 audint_status, audint_mask; | ||
1744 | u32 ts1_status, ts1_mask; | 1753 | u32 ts1_status, ts1_mask; |
1745 | u32 ts2_status, ts2_mask; | 1754 | u32 ts2_status, ts2_mask; |
1746 | int vida_count = 0, ts1_count = 0, ts2_count = 0, handled = 0; | 1755 | int vida_count = 0, ts1_count = 0, ts2_count = 0, handled = 0; |
1756 | int audint_count = 0; | ||
1747 | bool subdev_handled; | 1757 | bool subdev_handled; |
1748 | 1758 | ||
1749 | pci_status = cx_read(PCI_INT_STAT); | 1759 | pci_status = cx_read(PCI_INT_STAT); |
1750 | pci_mask = cx23885_irq_get_mask(dev); | 1760 | pci_mask = cx23885_irq_get_mask(dev); |
1751 | vida_status = cx_read(VID_A_INT_STAT); | 1761 | vida_status = cx_read(VID_A_INT_STAT); |
1752 | vida_mask = cx_read(VID_A_INT_MSK); | 1762 | vida_mask = cx_read(VID_A_INT_MSK); |
1763 | audint_status = cx_read(AUDIO_INT_INT_STAT); | ||
1764 | audint_mask = cx_read(AUDIO_INT_INT_MSK); | ||
1753 | ts1_status = cx_read(VID_B_INT_STAT); | 1765 | ts1_status = cx_read(VID_B_INT_STAT); |
1754 | ts1_mask = cx_read(VID_B_INT_MSK); | 1766 | ts1_mask = cx_read(VID_B_INT_MSK); |
1755 | ts2_status = cx_read(VID_C_INT_STAT); | 1767 | ts2_status = cx_read(VID_C_INT_STAT); |
@@ -1759,12 +1771,15 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id) | |||
1759 | goto out; | 1771 | goto out; |
1760 | 1772 | ||
1761 | vida_count = cx_read(VID_A_GPCNT); | 1773 | vida_count = cx_read(VID_A_GPCNT); |
1774 | audint_count = cx_read(AUD_INT_A_GPCNT); | ||
1762 | ts1_count = cx_read(ts1->reg_gpcnt); | 1775 | ts1_count = cx_read(ts1->reg_gpcnt); |
1763 | ts2_count = cx_read(ts2->reg_gpcnt); | 1776 | ts2_count = cx_read(ts2->reg_gpcnt); |
1764 | dprintk(7, "pci_status: 0x%08x pci_mask: 0x%08x\n", | 1777 | dprintk(7, "pci_status: 0x%08x pci_mask: 0x%08x\n", |
1765 | pci_status, pci_mask); | 1778 | pci_status, pci_mask); |
1766 | dprintk(7, "vida_status: 0x%08x vida_mask: 0x%08x count: 0x%x\n", | 1779 | dprintk(7, "vida_status: 0x%08x vida_mask: 0x%08x count: 0x%x\n", |
1767 | vida_status, vida_mask, vida_count); | 1780 | vida_status, vida_mask, vida_count); |
1781 | dprintk(7, "audint_status: 0x%08x audint_mask: 0x%08x count: 0x%x\n", | ||
1782 | audint_status, audint_mask, audint_count); | ||
1768 | dprintk(7, "ts1_status: 0x%08x ts1_mask: 0x%08x count: 0x%x\n", | 1783 | dprintk(7, "ts1_status: 0x%08x ts1_mask: 0x%08x count: 0x%x\n", |
1769 | ts1_status, ts1_mask, ts1_count); | 1784 | ts1_status, ts1_mask, ts1_count); |
1770 | dprintk(7, "ts2_status: 0x%08x ts2_mask: 0x%08x count: 0x%x\n", | 1785 | dprintk(7, "ts2_status: 0x%08x ts2_mask: 0x%08x count: 0x%x\n", |
@@ -1861,6 +1876,9 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id) | |||
1861 | if (vida_status) | 1876 | if (vida_status) |
1862 | handled += cx23885_video_irq(dev, vida_status); | 1877 | handled += cx23885_video_irq(dev, vida_status); |
1863 | 1878 | ||
1879 | if (audint_status) | ||
1880 | handled += cx23885_audio_irq(dev, audint_status, audint_mask); | ||
1881 | |||
1864 | if (pci_status & PCI_MSK_IR) { | 1882 | if (pci_status & PCI_MSK_IR) { |
1865 | subdev_handled = false; | 1883 | subdev_handled = false; |
1866 | v4l2_subdev_call(dev->sd_ir, core, interrupt_service_routine, | 1884 | v4l2_subdev_call(dev->sd_ir, core, interrupt_service_routine, |
diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h index d86bc0b1317b..abeba7a35cea 100644 --- a/drivers/media/video/cx23885/cx23885.h +++ b/drivers/media/video/cx23885/cx23885.h | |||
@@ -318,6 +318,34 @@ struct cx23885_kernel_ir { | |||
318 | struct rc_dev *rc; | 318 | struct rc_dev *rc; |
319 | }; | 319 | }; |
320 | 320 | ||
321 | struct cx23885_audio_buffer { | ||
322 | unsigned int bpl; | ||
323 | struct btcx_riscmem risc; | ||
324 | struct videobuf_dmabuf dma; | ||
325 | }; | ||
326 | |||
327 | struct cx23885_audio_dev { | ||
328 | struct cx23885_dev *dev; | ||
329 | |||
330 | struct pci_dev *pci; | ||
331 | |||
332 | struct snd_card *card; | ||
333 | |||
334 | spinlock_t lock; | ||
335 | |||
336 | atomic_t count; | ||
337 | |||
338 | unsigned int dma_size; | ||
339 | unsigned int period_size; | ||
340 | unsigned int num_periods; | ||
341 | |||
342 | struct videobuf_dmabuf *dma_risc; | ||
343 | |||
344 | struct cx23885_audio_buffer *buf; | ||
345 | |||
346 | struct snd_pcm_substream *substream; | ||
347 | }; | ||
348 | |||
321 | struct cx23885_dev { | 349 | struct cx23885_dev { |
322 | atomic_t refcount; | 350 | atomic_t refcount; |
323 | struct v4l2_device v4l2_dev; | 351 | struct v4l2_device v4l2_dev; |
@@ -400,6 +428,9 @@ struct cx23885_dev { | |||
400 | atomic_t v4l_reader_count; | 428 | atomic_t v4l_reader_count; |
401 | struct cx23885_tvnorm encodernorm; | 429 | struct cx23885_tvnorm encodernorm; |
402 | 430 | ||
431 | /* Analog raw audio */ | ||
432 | struct cx23885_audio_dev *audio_dev; | ||
433 | |||
403 | }; | 434 | }; |
404 | 435 | ||
405 | static inline struct cx23885_dev *to_cx23885(struct v4l2_device *v4l2_dev) | 436 | static inline struct cx23885_dev *to_cx23885(struct v4l2_device *v4l2_dev) |
@@ -563,6 +594,17 @@ extern void mc417_gpio_set(struct cx23885_dev *dev, u32 mask); | |||
563 | extern void mc417_gpio_clear(struct cx23885_dev *dev, u32 mask); | 594 | extern void mc417_gpio_clear(struct cx23885_dev *dev, u32 mask); |
564 | extern void mc417_gpio_enable(struct cx23885_dev *dev, u32 mask, int asoutput); | 595 | extern void mc417_gpio_enable(struct cx23885_dev *dev, u32 mask, int asoutput); |
565 | 596 | ||
597 | /* ----------------------------------------------------------- */ | ||
598 | /* cx23885-alsa.c */ | ||
599 | extern struct cx23885_audio_dev *cx23885_audio_initdev(struct cx23885_dev *dev); | ||
600 | extern void cx23885_audio_finidev(struct cx23885_dev *dev); | ||
601 | extern int cx23885_audio_irq(struct cx23885_dev *dev, u32 status, u32 mask); | ||
602 | extern int cx23885_risc_databuffer(struct pci_dev *pci, | ||
603 | struct btcx_riscmem *risc, | ||
604 | struct scatterlist *sglist, | ||
605 | unsigned int bpl, | ||
606 | unsigned int lines, | ||
607 | unsigned int lpi); | ||
566 | 608 | ||
567 | /* ----------------------------------------------------------- */ | 609 | /* ----------------------------------------------------------- */ |
568 | /* tv norms */ | 610 | /* tv norms */ |