V4L/DVB: tm6000: Add a callback code for buffer fill
[linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git] / drivers / staging / tm6000 / tm6000-alsa.c
blobb14c5b780c962299338e4a9d55efd71a15fcc4c2
1 /*
3 * Support for audio capture for tm5600/6000/6010
4 * (c) 2007-2008 Mauro Carvalho Chehab <mchehab@redhat.com>
6 * Based on cx88-alsa.c
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
13 #include <linux/module.h>
14 #include <linux/init.h>
15 #include <linux/device.h>
16 #include <linux/interrupt.h>
17 #include <linux/usb.h>
18 #include <linux/slab.h>
19 #include <linux/vmalloc.h>
21 #include <asm/delay.h>
22 #include <sound/core.h>
23 #include <sound/pcm.h>
24 #include <sound/pcm_params.h>
25 #include <sound/control.h>
26 #include <sound/initval.h>
29 #include "tm6000.h"
30 #include "tm6000-regs.h"
32 #undef dprintk
34 #define dprintk(level, fmt, arg...) do { \
35 if (debug >= level) \
36 printk(KERN_INFO "%s/1: " fmt, chip->core->name , ## arg); \
37 } while (0)
39 /****************************************************************************
40 Module global static vars
41 ****************************************************************************/
43 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
45 static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 1};
47 module_param_array(enable, bool, NULL, 0444);
48 MODULE_PARM_DESC(enable, "Enable tm6000x soundcard. default enabled.");
50 module_param_array(index, int, NULL, 0444);
51 MODULE_PARM_DESC(index, "Index value for tm6000x capture interface(s).");
54 /****************************************************************************
55 Module macros
56 ****************************************************************************/
58 MODULE_DESCRIPTION("ALSA driver module for tm5600/tm6000/tm6010 based TV cards");
59 MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
60 MODULE_LICENSE("GPL");
61 MODULE_SUPPORTED_DEVICE("{{Trident,tm5600},"
62 "{{Trident,tm6000},"
63 "{{Trident,tm6010}");
64 static unsigned int debug;
65 module_param(debug, int, 0644);
66 MODULE_PARM_DESC(debug, "enable debug messages");
68 /****************************************************************************
69 Module specific funtions
70 ****************************************************************************/
73 * BOARD Specific: Sets audio DMA
76 static int _tm6000_start_audio_dma(struct snd_tm6000_card *chip)
78 struct tm6000_core *core = chip->core;
79 int val;
81 /* Enables audio */
82 val = tm6000_get_reg(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0x0);
83 val |= 0x20;
84 tm6000_set_reg(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, val);
86 tm6000_set_reg(core, TM6010_REQ08_R01_A_INIT, 0x80);
88 return 0;
92 * BOARD Specific: Resets audio DMA
94 static int _tm6000_stop_audio_dma(struct snd_tm6000_card *chip)
96 struct tm6000_core *core = chip->core;
97 int val;
98 dprintk(1, "Stopping audio DMA\n");
100 /* Enables audio */
101 val = tm6000_get_reg(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0x0);
102 val &= ~0x20;
103 tm6000_set_reg(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, val);
105 tm6000_set_reg(core, TM6010_REQ08_R01_A_INIT, 0);
107 return 0;
110 static void dsp_buffer_free(struct snd_pcm_substream *substream)
112 struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
114 dprintk(2, "Freeing buffer\n");
116 vfree(substream->runtime->dma_area);
117 substream->runtime->dma_area = NULL;
118 substream->runtime->dma_bytes = 0;
121 static int dsp_buffer_alloc(struct snd_pcm_substream *substream, int size)
123 struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
125 dprintk(2, "Allocating buffer\n");
127 if (substream->runtime->dma_area) {
128 if (substream->runtime->dma_bytes > size)
129 return 0;
130 dsp_buffer_free(substream);
133 substream->runtime->dma_area = vmalloc(size);
134 if (!substream->runtime->dma_area)
135 return -ENOMEM;
137 substream->runtime->dma_bytes = size;
139 return 0;
143 /****************************************************************************
144 ALSA PCM Interface
145 ****************************************************************************/
148 * Digital hardware definition
150 #define DEFAULT_FIFO_SIZE 4096
152 static struct snd_pcm_hardware snd_tm6000_digital_hw = {
153 .info = SNDRV_PCM_INFO_MMAP |
154 SNDRV_PCM_INFO_INTERLEAVED |
155 SNDRV_PCM_INFO_BLOCK_TRANSFER |
156 SNDRV_PCM_INFO_MMAP_VALID,
157 .formats = SNDRV_PCM_FMTBIT_S16_LE,
159 .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
160 .rate_min = 44100,
161 .rate_max = 48000,
162 .channels_min = 2,
163 .channels_max = 2,
164 .period_bytes_min = DEFAULT_FIFO_SIZE/4,
165 .period_bytes_max = DEFAULT_FIFO_SIZE/4,
166 .periods_min = 1,
167 .periods_max = 1024,
168 .buffer_bytes_max = (1024*1024),
172 * audio pcm capture open callback
174 static int snd_tm6000_pcm_open(struct snd_pcm_substream *substream)
176 struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
177 struct snd_pcm_runtime *runtime = substream->runtime;
178 int err;
180 err = snd_pcm_hw_constraint_pow2(runtime, 0,
181 SNDRV_PCM_HW_PARAM_PERIODS);
182 if (err < 0)
183 goto _error;
185 chip->substream = substream;
187 runtime->hw = snd_tm6000_digital_hw;
189 return 0;
190 _error:
191 dprintk(1, "Error opening PCM!\n");
192 return err;
196 * audio close callback
198 static int snd_tm6000_close(struct snd_pcm_substream *substream)
200 return 0;
203 static int tm6000_fillbuf(struct tm6000_core *core, char *buf, int size)
205 int i;
207 /* Need to add a real code to copy audio buffer */
208 printk("Audio (%i bytes): ", size);
209 for (i = 0; i < size - 3; i +=4)
210 printk("(0x%04x, 0x%04x), ",
211 *(u16 *)(buf + i), *(u16 *)(buf + i + 2));
213 printk("\n");
215 return 0;
219 * hw_params callback
221 static int snd_tm6000_hw_params(struct snd_pcm_substream *substream,
222 struct snd_pcm_hw_params *hw_params)
224 int size, rc;
226 size = params_period_bytes(hw_params) * params_periods(hw_params);
228 rc = dsp_buffer_alloc(substream, size);
229 if (rc < 0)
230 return rc;
232 return 0;
236 * hw free callback
238 static int snd_tm6000_hw_free(struct snd_pcm_substream *substream)
240 dsp_buffer_free(substream);
242 return 0;
246 * prepare callback
248 static int snd_tm6000_prepare(struct snd_pcm_substream *substream)
250 return 0;
255 * trigger callback
257 static int snd_tm6000_card_trigger(struct snd_pcm_substream *substream, int cmd)
259 struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
260 int err;
262 spin_lock(&chip->reg_lock);
264 switch (cmd) {
265 case SNDRV_PCM_TRIGGER_START:
266 err = _tm6000_start_audio_dma(chip);
267 break;
268 case SNDRV_PCM_TRIGGER_STOP:
269 err = _tm6000_stop_audio_dma(chip);
270 break;
271 default:
272 err = -EINVAL;
273 break;
276 spin_unlock(&chip->reg_lock);
278 return err;
282 * pointer callback
284 static snd_pcm_uframes_t snd_tm6000_pointer(struct snd_pcm_substream *substream)
286 struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
287 struct snd_pcm_runtime *runtime = substream->runtime;
288 u16 count;
290 count = atomic_read(&chip->count);
292 return runtime->period_size * (count & (runtime->periods-1));
296 * operators
298 static struct snd_pcm_ops snd_tm6000_pcm_ops = {
299 .open = snd_tm6000_pcm_open,
300 .close = snd_tm6000_close,
301 .ioctl = snd_pcm_lib_ioctl,
302 .hw_params = snd_tm6000_hw_params,
303 .hw_free = snd_tm6000_hw_free,
304 .prepare = snd_tm6000_prepare,
305 .trigger = snd_tm6000_card_trigger,
306 .pointer = snd_tm6000_pointer,
310 * create a PCM device
313 /* FIXME: Control interface - How to control volume/mute? */
315 /****************************************************************************
316 Basic Flow for Sound Devices
317 ****************************************************************************/
320 * Alsa Constructor - Component probe
322 int tm6000_audio_init(struct tm6000_core *dev)
324 struct snd_card *card;
325 struct snd_tm6000_card *chip;
326 int rc;
327 static int devnr;
328 char component[14];
329 struct snd_pcm *pcm;
331 if (!dev)
332 return 0;
334 if (devnr >= SNDRV_CARDS)
335 return -ENODEV;
337 if (!enable[devnr])
338 return -ENOENT;
340 rc = snd_card_create(index[devnr], "tm6000", THIS_MODULE, 0, &card);
341 if (rc < 0) {
342 snd_printk(KERN_ERR "cannot create card instance %d\n", devnr);
343 return rc;
346 chip = kzalloc(sizeof(struct snd_tm6000_card), GFP_KERNEL);
347 if (!chip) {
348 rc = -ENOMEM;
349 goto error;
352 sprintf(component, "USB%04x:%04x",
353 le16_to_cpu(dev->udev->descriptor.idVendor),
354 le16_to_cpu(dev->udev->descriptor.idProduct));
355 snd_component_add(card, component);
357 spin_lock_init(&chip->reg_lock);
358 rc = snd_pcm_new(card, "TM6000 Audio", 0, 0, 1, &pcm);
359 if (rc < 0)
360 goto error;
362 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_tm6000_pcm_ops);
363 pcm->info_flags = 0;
364 pcm->private_data = dev;
365 strcpy(pcm->name, "Trident TM5600/60x0");
366 strcpy(card->driver, "tm6000-alsa");
367 strcpy(card->shortname, "TM5600/60x0");
368 sprintf(card->longname, "TM5600/60x0 Audio at bus %d device %d",
369 dev->udev->bus->busnum, dev->udev->devnum);
371 snd_card_set_dev(card, &dev->udev->dev);
373 rc = snd_card_register(card);
374 if (rc < 0)
375 goto error;
377 chip->core = dev;
378 chip->card = card;
379 dev->adev = chip;
381 return 0;
383 error:
384 snd_card_free(card);
385 return rc;
388 static int tm6000_audio_fini(struct tm6000_core *dev)
390 struct snd_tm6000_card *chip = dev->adev;
392 if (!dev)
393 return 0;
395 if (!chip)
396 return 0;
398 if (!chip->card)
399 return 0;
401 snd_card_free(chip->card);
402 chip->card = NULL;
403 kfree(chip);
404 dev->adev = NULL;
406 return 0;
409 struct tm6000_ops audio_ops = {
410 .type = TM6000_AUDIO,
411 .name = "TM6000 Audio Extension",
412 .init = tm6000_audio_init,
413 .fini = tm6000_audio_fini,
414 .fillbuf = tm6000_fillbuf,
417 static int __init tm6000_alsa_register(void)
419 return tm6000_register_extension(&audio_ops);
422 static void __exit tm6000_alsa_unregister(void)
424 tm6000_unregister_extension(&audio_ops);
427 module_init(tm6000_alsa_register);
428 module_exit(tm6000_alsa_unregister);