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