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