]> bbs.cooldavid.org Git - net-next-2.6.git/blame - drivers/staging/tm6000/tm6000-alsa.c
V4L/DVB: tm6000-alsa: Fix several bugs at the driver initialization code
[net-next-2.6.git] / drivers / staging / tm6000 / tm6000-alsa.c
CommitLineData
d72d5067
MCC
1/*
2 *
e28f49b0 3 * Support for audio capture for tm5600/6000/6010
7ca30c52 4 * (c) 2007-2008 Mauro Carvalho Chehab <mchehab@redhat.com>
d72d5067 5 *
7ca30c52 6 * Based on cx88-alsa.c
d72d5067
MCC
7 *
8 * This program is free software; you can redistribute it and/or modify
7ca30c52
MCC
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
d72d5067
MCC
11 */
12
13#include <linux/module.h>
14#include <linux/init.h>
15#include <linux/device.h>
16#include <linux/interrupt.h>
6ad6e88a 17#include <linux/usb.h>
4ef09889 18#include <linux/slab.h>
54b78608 19#include <linux/vmalloc.h>
d72d5067
MCC
20
21#include <asm/delay.h>
d72d5067
MCC
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>
d72d5067 27
d72d5067 28
7ca30c52
MCC
29#include "tm6000.h"
30#include "tm6000-regs.h"
31
32#undef dprintk
d72d5067 33
7ca30c52
MCC
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)
d72d5067 38
d72d5067
MCC
39/****************************************************************************
40 Module global static vars
41 ****************************************************************************/
42
43static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
0e57ff8d 44
d72d5067
MCC
45static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 1};
46
47module_param_array(enable, bool, NULL, 0444);
7ca30c52 48MODULE_PARM_DESC(enable, "Enable tm6000x soundcard. default enabled.");
d72d5067
MCC
49
50module_param_array(index, int, NULL, 0444);
7ca30c52 51MODULE_PARM_DESC(index, "Index value for tm6000x capture interface(s).");
d72d5067
MCC
52
53
54/****************************************************************************
55 Module macros
56 ****************************************************************************/
57
e28f49b0 58MODULE_DESCRIPTION("ALSA driver module for tm5600/tm6000/tm6010 based TV cards");
d72d5067
MCC
59MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
60MODULE_LICENSE("GPL");
7ca30c52 61MODULE_SUPPORTED_DEVICE("{{Trident,tm5600},"
e28f49b0
DB
62 "{{Trident,tm6000},"
63 "{{Trident,tm6010}");
d72d5067 64static unsigned int debug;
7ca30c52
MCC
65module_param(debug, int, 0644);
66MODULE_PARM_DESC(debug, "enable debug messages");
d72d5067
MCC
67
68/****************************************************************************
69 Module specific funtions
70 ****************************************************************************/
71
72/*
73 * BOARD Specific: Sets audio DMA
74 */
75
7ca30c52 76static int _tm6000_start_audio_dma(struct snd_tm6000_card *chip)
d72d5067 77{
7ca30c52
MCC
78 struct tm6000_core *core = chip->core;
79 int val;
d72d5067 80
3f23a81a
MCC
81 dprintk(1, "Starting audio DMA\n");
82
7ca30c52 83 /* Enables audio */
9afec493 84 val = tm6000_get_reg(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0x0);
7ca30c52 85 val |= 0x20;
9afec493 86 tm6000_set_reg(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, val);
d72d5067 87
9afec493 88 tm6000_set_reg(core, TM6010_REQ08_R01_A_INIT, 0x80);
d72d5067
MCC
89
90 return 0;
91}
92
93/*
94 * BOARD Specific: Resets audio DMA
95 */
7ca30c52 96static int _tm6000_stop_audio_dma(struct snd_tm6000_card *chip)
d72d5067 97{
7ca30c52
MCC
98 struct tm6000_core *core = chip->core;
99 int val;
d72d5067
MCC
100 dprintk(1, "Stopping audio DMA\n");
101
7ca30c52 102 /* Enables audio */
9afec493 103 val = tm6000_get_reg(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0x0);
7ca30c52 104 val &= ~0x20;
9afec493 105 tm6000_set_reg(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, val);
d72d5067 106
9afec493 107 tm6000_set_reg(core, TM6010_REQ08_R01_A_INIT, 0);
d72d5067
MCC
108
109 return 0;
110}
111
54b78608 112static void dsp_buffer_free(struct snd_pcm_substream *substream)
d72d5067 113{
54b78608 114 struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
d72d5067 115
7ca30c52 116 dprintk(2, "Freeing buffer\n");
d72d5067 117
54b78608
MCC
118 vfree(substream->runtime->dma_area);
119 substream->runtime->dma_area = NULL;
120 substream->runtime->dma_bytes = 0;
121}
122
123static int dsp_buffer_alloc(struct snd_pcm_substream *substream, int size)
124{
125 struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
126
127 dprintk(2, "Allocating buffer\n");
d72d5067 128
54b78608
MCC
129 if (substream->runtime->dma_area) {
130 if (substream->runtime->dma_bytes > size)
131 return 0;
132 dsp_buffer_free(substream);
133 }
134
135 substream->runtime->dma_area = vmalloc(size);
136 if (!substream->runtime->dma_area)
137 return -ENOMEM;
d72d5067 138
54b78608
MCC
139 substream->runtime->dma_bytes = size;
140
141 return 0;
d72d5067
MCC
142}
143
54b78608 144
d72d5067
MCC
145/****************************************************************************
146 ALSA PCM Interface
147 ****************************************************************************/
148
149/*
150 * Digital hardware definition
151 */
152#define DEFAULT_FIFO_SIZE 4096
7ca30c52
MCC
153
154static struct snd_pcm_hardware snd_tm6000_digital_hw = {
d72d5067
MCC
155 .info = SNDRV_PCM_INFO_MMAP |
156 SNDRV_PCM_INFO_INTERLEAVED |
157 SNDRV_PCM_INFO_BLOCK_TRANSFER |
158 SNDRV_PCM_INFO_MMAP_VALID,
159 .formats = SNDRV_PCM_FMTBIT_S16_LE,
160
7ca30c52
MCC
161 .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
162 .rate_min = 44100,
d72d5067
MCC
163 .rate_max = 48000,
164 .channels_min = 2,
165 .channels_max = 2,
d72d5067
MCC
166 .period_bytes_min = DEFAULT_FIFO_SIZE/4,
167 .period_bytes_max = DEFAULT_FIFO_SIZE/4,
168 .periods_min = 1,
169 .periods_max = 1024,
170 .buffer_bytes_max = (1024*1024),
171};
172
173/*
174 * audio pcm capture open callback
175 */
7ca30c52 176static int snd_tm6000_pcm_open(struct snd_pcm_substream *substream)
d72d5067 177{
7ca30c52 178 struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
d72d5067
MCC
179 struct snd_pcm_runtime *runtime = substream->runtime;
180 int err;
181
7ca30c52
MCC
182 err = snd_pcm_hw_constraint_pow2(runtime, 0,
183 SNDRV_PCM_HW_PARAM_PERIODS);
d72d5067
MCC
184 if (err < 0)
185 goto _error;
186
187 chip->substream = substream;
188
7ca30c52 189 runtime->hw = snd_tm6000_digital_hw;
d72d5067
MCC
190
191 return 0;
192_error:
7ca30c52 193 dprintk(1, "Error opening PCM!\n");
d72d5067
MCC
194 return err;
195}
196
197/*
198 * audio close callback
199 */
7ca30c52 200static int snd_tm6000_close(struct snd_pcm_substream *substream)
d72d5067
MCC
201{
202 return 0;
203}
204
b17b8699
MCC
205static int tm6000_fillbuf(struct tm6000_core *core, char *buf, int size)
206{
207 int i;
208
209 /* Need to add a real code to copy audio buffer */
210 printk("Audio (%i bytes): ", size);
211 for (i = 0; i < size - 3; i +=4)
212 printk("(0x%04x, 0x%04x), ",
213 *(u16 *)(buf + i), *(u16 *)(buf + i + 2));
214
215 printk("\n");
216
217 return 0;
218}
219
d72d5067
MCC
220/*
221 * hw_params callback
222 */
7ca30c52
MCC
223static int snd_tm6000_hw_params(struct snd_pcm_substream *substream,
224 struct snd_pcm_hw_params *hw_params)
d72d5067 225{
54b78608 226 int size, rc;
7ca30c52 227
54b78608 228 size = params_period_bytes(hw_params) * params_periods(hw_params);
7ca30c52 229
54b78608
MCC
230 rc = dsp_buffer_alloc(substream, size);
231 if (rc < 0)
232 return rc;
7ca30c52
MCC
233
234 return 0;
d72d5067
MCC
235}
236
237/*
238 * hw free callback
239 */
7ca30c52 240static int snd_tm6000_hw_free(struct snd_pcm_substream *substream)
d72d5067 241{
3f23a81a
MCC
242 struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
243
244 _tm6000_stop_audio_dma(chip);
d72d5067
MCC
245
246 return 0;
247}
248
249/*
250 * prepare callback
251 */
7ca30c52 252static int snd_tm6000_prepare(struct snd_pcm_substream *substream)
d72d5067 253{
3f23a81a
MCC
254 struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
255
256 chip->buf_pos = 0;
257 chip->period_pos = 0;
258
d72d5067
MCC
259 return 0;
260}
261
7ca30c52 262
d72d5067
MCC
263/*
264 * trigger callback
265 */
7ca30c52 266static int snd_tm6000_card_trigger(struct snd_pcm_substream *substream, int cmd)
d72d5067 267{
7ca30c52 268 struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
d72d5067
MCC
269 int err;
270
d72d5067
MCC
271 spin_lock(&chip->reg_lock);
272
273 switch (cmd) {
274 case SNDRV_PCM_TRIGGER_START:
7ca30c52 275 err = _tm6000_start_audio_dma(chip);
d72d5067
MCC
276 break;
277 case SNDRV_PCM_TRIGGER_STOP:
7ca30c52 278 err = _tm6000_stop_audio_dma(chip);
d72d5067
MCC
279 break;
280 default:
7ca30c52 281 err = -EINVAL;
d72d5067
MCC
282 break;
283 }
284
285 spin_unlock(&chip->reg_lock);
286
287 return err;
288}
289
290/*
291 * pointer callback
292 */
7ca30c52 293static snd_pcm_uframes_t snd_tm6000_pointer(struct snd_pcm_substream *substream)
d72d5067 294{
7ca30c52 295 struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
d72d5067 296
3f23a81a 297 return chip->buf_pos;
d72d5067
MCC
298}
299
d72d5067
MCC
300/*
301 * operators
302 */
7ca30c52
MCC
303static struct snd_pcm_ops snd_tm6000_pcm_ops = {
304 .open = snd_tm6000_pcm_open,
305 .close = snd_tm6000_close,
d72d5067 306 .ioctl = snd_pcm_lib_ioctl,
7ca30c52
MCC
307 .hw_params = snd_tm6000_hw_params,
308 .hw_free = snd_tm6000_hw_free,
309 .prepare = snd_tm6000_prepare,
310 .trigger = snd_tm6000_card_trigger,
311 .pointer = snd_tm6000_pointer,
d72d5067
MCC
312};
313
314/*
315 * create a PCM device
316 */
d72d5067 317
7ca30c52 318/* FIXME: Control interface - How to control volume/mute? */
d72d5067
MCC
319
320/****************************************************************************
321 Basic Flow for Sound Devices
322 ****************************************************************************/
323
d72d5067
MCC
324/*
325 * Alsa Constructor - Component probe
326 */
d77057f2 327int tm6000_audio_init(struct tm6000_core *dev)
d72d5067 328{
d77057f2
SR
329 struct snd_card *card;
330 struct snd_tm6000_card *chip;
331 int rc;
332 static int devnr;
333 char component[14];
334 struct snd_pcm *pcm;
335
336 if (!dev)
337 return 0;
6ad6e88a 338
d77057f2 339 if (devnr >= SNDRV_CARDS)
6ad6e88a
MCC
340 return -ENODEV;
341
d77057f2 342 if (!enable[devnr])
6ad6e88a
MCC
343 return -ENOENT;
344
0e57ff8d 345 rc = snd_card_create(index[devnr], "tm6000", THIS_MODULE, 0, &card);
427f7fac 346 if (rc < 0) {
d77057f2 347 snd_printk(KERN_ERR "cannot create card instance %d\n", devnr);
427f7fac 348 return rc;
6ad6e88a 349 }
3f23a81a
MCC
350 strcpy(card->driver, "tm6000-alsa");
351 strcpy(card->shortname, "TM5600/60x0");
352 sprintf(card->longname, "TM5600/60x0 Audio at bus %d device %d",
353 dev->udev->bus->busnum, dev->udev->devnum);
354
355 sprintf(component, "USB%04x:%04x",
356 le16_to_cpu(dev->udev->descriptor.idVendor),
357 le16_to_cpu(dev->udev->descriptor.idProduct));
358 snd_component_add(card, component);
359 snd_card_set_dev(card, &dev->udev->dev);
6ad6e88a 360
d77057f2 361 chip = kzalloc(sizeof(struct snd_tm6000_card), GFP_KERNEL);
6ad6e88a
MCC
362 if (!chip) {
363 rc = -ENOMEM;
364 goto error;
365 }
366
3f23a81a
MCC
367 chip->core = dev;
368 chip->card = card;
369 dev->adev = chip;
d77057f2 370 spin_lock_init(&chip->reg_lock);
3f23a81a 371
d77057f2 372 rc = snd_pcm_new(card, "TM6000 Audio", 0, 0, 1, &pcm);
6ad6e88a
MCC
373 if (rc < 0)
374 goto error;
375
d77057f2 376 pcm->info_flags = 0;
3f23a81a 377 pcm->private_data = chip;
d77057f2 378 strcpy(pcm->name, "Trident TM5600/60x0");
d77057f2 379
3f23a81a 380 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_tm6000_pcm_ops);
d77057f2 381
6ad6e88a
MCC
382 rc = snd_card_register(card);
383 if (rc < 0)
384 goto error;
385
3f23a81a 386 dprintk(1,"Registered audio driver for %s\n", card->longname);
d72d5067 387
d72d5067 388 return 0;
d72d5067 389
6ad6e88a
MCC
390error:
391 snd_card_free(card);
392 return rc;
d72d5067
MCC
393}
394
0439db75
SR
395static int tm6000_audio_fini(struct tm6000_core *dev)
396{
d77057f2
SR
397 struct snd_tm6000_card *chip = dev->adev;
398
399 if (!dev)
400 return 0;
401
402 if (!chip)
403 return 0;
404
405 if (!chip->card)
406 return 0;
407
408 snd_card_free(chip->card);
409 chip->card = NULL;
410 kfree(chip);
411 dev->adev = NULL;
412
0439db75
SR
413 return 0;
414}
415
416struct tm6000_ops audio_ops = {
39e1256b 417 .type = TM6000_AUDIO,
0439db75
SR
418 .name = "TM6000 Audio Extension",
419 .init = tm6000_audio_init,
420 .fini = tm6000_audio_fini,
b17b8699 421 .fillbuf = tm6000_fillbuf,
0439db75
SR
422};
423
424static int __init tm6000_alsa_register(void)
425{
426 return tm6000_register_extension(&audio_ops);
427}
428
429static void __exit tm6000_alsa_unregister(void)
430{
431 tm6000_unregister_extension(&audio_ops);
432}
433
434module_init(tm6000_alsa_register);
435module_exit(tm6000_alsa_unregister);