root/drivers/media/usb/tm6000/tm6000-alsa.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. _tm6000_start_audio_dma
  2. _tm6000_stop_audio_dma
  3. dsp_buffer_free
  4. dsp_buffer_alloc
  5. snd_tm6000_pcm_open
  6. snd_tm6000_close
  7. tm6000_fillbuf
  8. snd_tm6000_hw_params
  9. snd_tm6000_hw_free
  10. snd_tm6000_prepare
  11. audio_trigger
  12. snd_tm6000_card_trigger
  13. snd_tm6000_pointer
  14. snd_pcm_get_vmalloc_page
  15. tm6000_audio_init
  16. tm6000_audio_fini
  17. tm6000_alsa_register
  18. tm6000_alsa_unregister

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

/* [<][>][^][v][top][bottom][index][help] */