18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// Support for audio capture for tm5600/6000/6010 38c2ecf20Sopenharmony_ci// Copyright (c) 2007-2008 Mauro Carvalho Chehab <mchehab@kernel.org> 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci// Based on cx88-alsa.c 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/module.h> 88c2ecf20Sopenharmony_ci#include <linux/init.h> 98c2ecf20Sopenharmony_ci#include <linux/device.h> 108c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 118c2ecf20Sopenharmony_ci#include <linux/usb.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/delay.h> 158c2ecf20Sopenharmony_ci#include <sound/core.h> 168c2ecf20Sopenharmony_ci#include <sound/pcm.h> 178c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 188c2ecf20Sopenharmony_ci#include <sound/control.h> 198c2ecf20Sopenharmony_ci#include <sound/initval.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include "tm6000.h" 238c2ecf20Sopenharmony_ci#include "tm6000-regs.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#undef dprintk 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define dprintk(level, fmt, arg...) do { \ 288c2ecf20Sopenharmony_ci if (debug >= level) \ 298c2ecf20Sopenharmony_ci printk(KERN_INFO "%s/1: " fmt, chip->core->name , ## arg); \ 308c2ecf20Sopenharmony_ci } while (0) 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/**************************************************************************** 338c2ecf20Sopenharmony_ci Module global static vars 348c2ecf20Sopenharmony_ci ****************************************************************************/ 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444); 418c2ecf20Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable tm6000x soundcard. default enabled."); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cimodule_param_array(index, int, NULL, 0444); 448c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for tm6000x capture interface(s)."); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/**************************************************************************** 488c2ecf20Sopenharmony_ci Module macros 498c2ecf20Sopenharmony_ci ****************************************************************************/ 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ALSA driver module for tm5600/tm6000/tm6010 based TV cards"); 528c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mauro Carvalho Chehab"); 538c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 548c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{{Trident,tm5600},{{Trident,tm6000},{{Trident,tm6010}"); 558c2ecf20Sopenharmony_cistatic unsigned int debug; 568c2ecf20Sopenharmony_cimodule_param(debug, int, 0644); 578c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "enable debug messages"); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/**************************************************************************** 608c2ecf20Sopenharmony_ci Module specific functions 618c2ecf20Sopenharmony_ci ****************************************************************************/ 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci/* 648c2ecf20Sopenharmony_ci * BOARD Specific: Sets audio DMA 658c2ecf20Sopenharmony_ci */ 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic int _tm6000_start_audio_dma(struct snd_tm6000_card *chip) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci struct tm6000_core *core = chip->core; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci dprintk(1, "Starting audio DMA\n"); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci /* Enables audio */ 748c2ecf20Sopenharmony_ci tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_IF, 0x40, 0x40); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci tm6000_set_audio_bitrate(core, 48000); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci return 0; 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/* 828c2ecf20Sopenharmony_ci * BOARD Specific: Resets audio DMA 838c2ecf20Sopenharmony_ci */ 848c2ecf20Sopenharmony_cistatic int _tm6000_stop_audio_dma(struct snd_tm6000_card *chip) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci struct tm6000_core *core = chip->core; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci dprintk(1, "Stopping audio DMA\n"); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* Disables audio */ 918c2ecf20Sopenharmony_ci tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_IF, 0x00, 0x40); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci return 0; 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci/**************************************************************************** 978c2ecf20Sopenharmony_ci ALSA PCM Interface 988c2ecf20Sopenharmony_ci ****************************************************************************/ 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci/* 1018c2ecf20Sopenharmony_ci * Digital hardware definition 1028c2ecf20Sopenharmony_ci */ 1038c2ecf20Sopenharmony_ci#define DEFAULT_FIFO_SIZE 4096 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_tm6000_digital_hw = { 1068c2ecf20Sopenharmony_ci .info = SNDRV_PCM_INFO_BATCH | 1078c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP | 1088c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 1098c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 1108c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID, 1118c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT, 1148c2ecf20Sopenharmony_ci .rate_min = 48000, 1158c2ecf20Sopenharmony_ci .rate_max = 48000, 1168c2ecf20Sopenharmony_ci .channels_min = 2, 1178c2ecf20Sopenharmony_ci .channels_max = 2, 1188c2ecf20Sopenharmony_ci .period_bytes_min = 64, 1198c2ecf20Sopenharmony_ci .period_bytes_max = 12544, 1208c2ecf20Sopenharmony_ci .periods_min = 2, 1218c2ecf20Sopenharmony_ci .periods_max = 98, 1228c2ecf20Sopenharmony_ci .buffer_bytes_max = 62720 * 8, 1238c2ecf20Sopenharmony_ci}; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci/* 1268c2ecf20Sopenharmony_ci * audio pcm capture open callback 1278c2ecf20Sopenharmony_ci */ 1288c2ecf20Sopenharmony_cistatic int snd_tm6000_pcm_open(struct snd_pcm_substream *substream) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); 1318c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 1328c2ecf20Sopenharmony_ci int err; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci err = snd_pcm_hw_constraint_pow2(runtime, 0, 1358c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_PERIODS); 1368c2ecf20Sopenharmony_ci if (err < 0) 1378c2ecf20Sopenharmony_ci goto _error; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci chip->substream = substream; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci runtime->hw = snd_tm6000_digital_hw; 1428c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci return 0; 1458c2ecf20Sopenharmony_ci_error: 1468c2ecf20Sopenharmony_ci dprintk(1, "Error opening PCM!\n"); 1478c2ecf20Sopenharmony_ci return err; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci/* 1518c2ecf20Sopenharmony_ci * audio close callback 1528c2ecf20Sopenharmony_ci */ 1538c2ecf20Sopenharmony_cistatic int snd_tm6000_close(struct snd_pcm_substream *substream) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); 1568c2ecf20Sopenharmony_ci struct tm6000_core *core = chip->core; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci if (atomic_read(&core->stream_started) > 0) { 1598c2ecf20Sopenharmony_ci atomic_set(&core->stream_started, 0); 1608c2ecf20Sopenharmony_ci schedule_work(&core->wq_trigger); 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci return 0; 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic int tm6000_fillbuf(struct tm6000_core *core, char *buf, int size) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci struct snd_tm6000_card *chip = core->adev; 1698c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream = chip->substream; 1708c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime; 1718c2ecf20Sopenharmony_ci int period_elapsed = 0; 1728c2ecf20Sopenharmony_ci unsigned int stride, buf_pos; 1738c2ecf20Sopenharmony_ci int length; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (atomic_read(&core->stream_started) == 0) 1768c2ecf20Sopenharmony_ci return 0; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (!size || !substream) { 1798c2ecf20Sopenharmony_ci dprintk(1, "substream was NULL\n"); 1808c2ecf20Sopenharmony_ci return -EINVAL; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci runtime = substream->runtime; 1848c2ecf20Sopenharmony_ci if (!runtime || !runtime->dma_area) { 1858c2ecf20Sopenharmony_ci dprintk(1, "runtime was NULL\n"); 1868c2ecf20Sopenharmony_ci return -EINVAL; 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci buf_pos = chip->buf_pos; 1908c2ecf20Sopenharmony_ci stride = runtime->frame_bits >> 3; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci if (stride == 0) { 1938c2ecf20Sopenharmony_ci dprintk(1, "stride is zero\n"); 1948c2ecf20Sopenharmony_ci return -EINVAL; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci length = size / stride; 1988c2ecf20Sopenharmony_ci if (length == 0) { 1998c2ecf20Sopenharmony_ci dprintk(1, "%s: length was zero\n", __func__); 2008c2ecf20Sopenharmony_ci return -EINVAL; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci dprintk(1, "Copying %d bytes at %p[%d] - buf size=%d x %d\n", size, 2048c2ecf20Sopenharmony_ci runtime->dma_area, buf_pos, 2058c2ecf20Sopenharmony_ci (unsigned int)runtime->buffer_size, stride); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci if (buf_pos + length >= runtime->buffer_size) { 2088c2ecf20Sopenharmony_ci unsigned int cnt = runtime->buffer_size - buf_pos; 2098c2ecf20Sopenharmony_ci memcpy(runtime->dma_area + buf_pos * stride, buf, cnt * stride); 2108c2ecf20Sopenharmony_ci memcpy(runtime->dma_area, buf + cnt * stride, 2118c2ecf20Sopenharmony_ci length * stride - cnt * stride); 2128c2ecf20Sopenharmony_ci } else 2138c2ecf20Sopenharmony_ci memcpy(runtime->dma_area + buf_pos * stride, buf, 2148c2ecf20Sopenharmony_ci length * stride); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci snd_pcm_stream_lock(substream); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci chip->buf_pos += length; 2198c2ecf20Sopenharmony_ci if (chip->buf_pos >= runtime->buffer_size) 2208c2ecf20Sopenharmony_ci chip->buf_pos -= runtime->buffer_size; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci chip->period_pos += length; 2238c2ecf20Sopenharmony_ci if (chip->period_pos >= runtime->period_size) { 2248c2ecf20Sopenharmony_ci chip->period_pos -= runtime->period_size; 2258c2ecf20Sopenharmony_ci period_elapsed = 1; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci snd_pcm_stream_unlock(substream); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (period_elapsed) 2318c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(substream); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci return 0; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci/* 2378c2ecf20Sopenharmony_ci * prepare callback 2388c2ecf20Sopenharmony_ci */ 2398c2ecf20Sopenharmony_cistatic int snd_tm6000_prepare(struct snd_pcm_substream *substream) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci chip->buf_pos = 0; 2448c2ecf20Sopenharmony_ci chip->period_pos = 0; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci return 0; 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci/* 2518c2ecf20Sopenharmony_ci * trigger callback 2528c2ecf20Sopenharmony_ci */ 2538c2ecf20Sopenharmony_cistatic void audio_trigger(struct work_struct *work) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci struct tm6000_core *core = container_of(work, struct tm6000_core, 2568c2ecf20Sopenharmony_ci wq_trigger); 2578c2ecf20Sopenharmony_ci struct snd_tm6000_card *chip = core->adev; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (atomic_read(&core->stream_started)) { 2608c2ecf20Sopenharmony_ci dprintk(1, "starting capture"); 2618c2ecf20Sopenharmony_ci _tm6000_start_audio_dma(chip); 2628c2ecf20Sopenharmony_ci } else { 2638c2ecf20Sopenharmony_ci dprintk(1, "stopping capture"); 2648c2ecf20Sopenharmony_ci _tm6000_stop_audio_dma(chip); 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic int snd_tm6000_card_trigger(struct snd_pcm_substream *substream, int cmd) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); 2718c2ecf20Sopenharmony_ci struct tm6000_core *core = chip->core; 2728c2ecf20Sopenharmony_ci int err = 0; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci switch (cmd) { 2758c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 2768c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 2778c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 2788c2ecf20Sopenharmony_ci atomic_set(&core->stream_started, 1); 2798c2ecf20Sopenharmony_ci break; 2808c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 2818c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 2828c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 2838c2ecf20Sopenharmony_ci atomic_set(&core->stream_started, 0); 2848c2ecf20Sopenharmony_ci break; 2858c2ecf20Sopenharmony_ci default: 2868c2ecf20Sopenharmony_ci err = -EINVAL; 2878c2ecf20Sopenharmony_ci break; 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci schedule_work(&core->wq_trigger); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci return err; 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci/* 2948c2ecf20Sopenharmony_ci * pointer callback 2958c2ecf20Sopenharmony_ci */ 2968c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_tm6000_pointer(struct snd_pcm_substream *substream) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci return chip->buf_pos; 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci/* 3048c2ecf20Sopenharmony_ci * operators 3058c2ecf20Sopenharmony_ci */ 3068c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_tm6000_pcm_ops = { 3078c2ecf20Sopenharmony_ci .open = snd_tm6000_pcm_open, 3088c2ecf20Sopenharmony_ci .close = snd_tm6000_close, 3098c2ecf20Sopenharmony_ci .prepare = snd_tm6000_prepare, 3108c2ecf20Sopenharmony_ci .trigger = snd_tm6000_card_trigger, 3118c2ecf20Sopenharmony_ci .pointer = snd_tm6000_pointer, 3128c2ecf20Sopenharmony_ci}; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci/* 3158c2ecf20Sopenharmony_ci * create a PCM device 3168c2ecf20Sopenharmony_ci */ 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci/* FIXME: Control interface - How to control volume/mute? */ 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci/**************************************************************************** 3218c2ecf20Sopenharmony_ci Basic Flow for Sound Devices 3228c2ecf20Sopenharmony_ci ****************************************************************************/ 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci/* 3258c2ecf20Sopenharmony_ci * Alsa Constructor - Component probe 3268c2ecf20Sopenharmony_ci */ 3278c2ecf20Sopenharmony_cistatic int tm6000_audio_init(struct tm6000_core *dev) 3288c2ecf20Sopenharmony_ci{ 3298c2ecf20Sopenharmony_ci struct snd_card *card; 3308c2ecf20Sopenharmony_ci struct snd_tm6000_card *chip; 3318c2ecf20Sopenharmony_ci int rc; 3328c2ecf20Sopenharmony_ci static int devnr; 3338c2ecf20Sopenharmony_ci char component[14]; 3348c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci if (!dev) 3378c2ecf20Sopenharmony_ci return 0; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci if (devnr >= SNDRV_CARDS) 3408c2ecf20Sopenharmony_ci return -ENODEV; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci if (!enable[devnr]) 3438c2ecf20Sopenharmony_ci return -ENOENT; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci rc = snd_card_new(&dev->udev->dev, index[devnr], "tm6000", 3468c2ecf20Sopenharmony_ci THIS_MODULE, 0, &card); 3478c2ecf20Sopenharmony_ci if (rc < 0) { 3488c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "cannot create card instance %d\n", devnr); 3498c2ecf20Sopenharmony_ci return rc; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci strscpy(card->driver, "tm6000-alsa", sizeof(card->driver)); 3528c2ecf20Sopenharmony_ci strscpy(card->shortname, "TM5600/60x0", sizeof(card->shortname)); 3538c2ecf20Sopenharmony_ci sprintf(card->longname, "TM5600/60x0 Audio at bus %d device %d", 3548c2ecf20Sopenharmony_ci dev->udev->bus->busnum, dev->udev->devnum); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci sprintf(component, "USB%04x:%04x", 3578c2ecf20Sopenharmony_ci le16_to_cpu(dev->udev->descriptor.idVendor), 3588c2ecf20Sopenharmony_ci le16_to_cpu(dev->udev->descriptor.idProduct)); 3598c2ecf20Sopenharmony_ci snd_component_add(card, component); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci chip = kzalloc(sizeof(struct snd_tm6000_card), GFP_KERNEL); 3628c2ecf20Sopenharmony_ci if (!chip) { 3638c2ecf20Sopenharmony_ci rc = -ENOMEM; 3648c2ecf20Sopenharmony_ci goto error; 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci chip->core = dev; 3688c2ecf20Sopenharmony_ci chip->card = card; 3698c2ecf20Sopenharmony_ci dev->adev = chip; 3708c2ecf20Sopenharmony_ci spin_lock_init(&chip->reg_lock); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci rc = snd_pcm_new(card, "TM6000 Audio", 0, 0, 1, &pcm); 3738c2ecf20Sopenharmony_ci if (rc < 0) 3748c2ecf20Sopenharmony_ci goto error_chip; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci pcm->info_flags = 0; 3778c2ecf20Sopenharmony_ci pcm->private_data = chip; 3788c2ecf20Sopenharmony_ci strscpy(pcm->name, "Trident TM5600/60x0", sizeof(pcm->name)); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_tm6000_pcm_ops); 3818c2ecf20Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci INIT_WORK(&dev->wq_trigger, audio_trigger); 3848c2ecf20Sopenharmony_ci rc = snd_card_register(card); 3858c2ecf20Sopenharmony_ci if (rc < 0) 3868c2ecf20Sopenharmony_ci goto error_chip; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci dprintk(1, "Registered audio driver for %s\n", card->longname); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci return 0; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_cierror_chip: 3938c2ecf20Sopenharmony_ci kfree(chip); 3948c2ecf20Sopenharmony_ci dev->adev = NULL; 3958c2ecf20Sopenharmony_cierror: 3968c2ecf20Sopenharmony_ci snd_card_free(card); 3978c2ecf20Sopenharmony_ci return rc; 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistatic int tm6000_audio_fini(struct tm6000_core *dev) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci struct snd_tm6000_card *chip; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci if (!dev) 4058c2ecf20Sopenharmony_ci return 0; 4068c2ecf20Sopenharmony_ci chip = dev->adev; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if (!chip) 4098c2ecf20Sopenharmony_ci return 0; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci if (!chip->card) 4128c2ecf20Sopenharmony_ci return 0; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci snd_card_free(chip->card); 4158c2ecf20Sopenharmony_ci chip->card = NULL; 4168c2ecf20Sopenharmony_ci kfree(chip); 4178c2ecf20Sopenharmony_ci dev->adev = NULL; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci return 0; 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cistatic struct tm6000_ops audio_ops = { 4238c2ecf20Sopenharmony_ci .type = TM6000_AUDIO, 4248c2ecf20Sopenharmony_ci .name = "TM6000 Audio Extension", 4258c2ecf20Sopenharmony_ci .init = tm6000_audio_init, 4268c2ecf20Sopenharmony_ci .fini = tm6000_audio_fini, 4278c2ecf20Sopenharmony_ci .fillbuf = tm6000_fillbuf, 4288c2ecf20Sopenharmony_ci}; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cistatic int __init tm6000_alsa_register(void) 4318c2ecf20Sopenharmony_ci{ 4328c2ecf20Sopenharmony_ci return tm6000_register_extension(&audio_ops); 4338c2ecf20Sopenharmony_ci} 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_cistatic void __exit tm6000_alsa_unregister(void) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci tm6000_unregister_extension(&audio_ops); 4388c2ecf20Sopenharmony_ci} 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_cimodule_init(tm6000_alsa_register); 4418c2ecf20Sopenharmony_cimodule_exit(tm6000_alsa_unregister); 442