xref: /OK3568_Linux_fs/kernel/drivers/media/usb/tm6000/tm6000-alsa.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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