xref: /linux/drivers/media/usb/cx231xx/cx231xx-audio.c (revision 9dd73448c6a98b1971e626627e4b020b8898a3f6)
10c0d06caSMauro Carvalho Chehab /*
20c0d06caSMauro Carvalho Chehab  *  Conexant Cx231xx audio extension
30c0d06caSMauro Carvalho Chehab  *
40c0d06caSMauro Carvalho Chehab  *  Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
50c0d06caSMauro Carvalho Chehab  *       Based on em28xx driver
60c0d06caSMauro Carvalho Chehab  *
70c0d06caSMauro Carvalho Chehab  *
80c0d06caSMauro Carvalho Chehab  *  This program is free software; you can redistribute it and/or modify
90c0d06caSMauro Carvalho Chehab  *  it under the terms of the GNU General Public License as published by
100c0d06caSMauro Carvalho Chehab  *  the Free Software Foundation; either version 2 of the License, or
110c0d06caSMauro Carvalho Chehab  *  (at your option) any later version.
120c0d06caSMauro Carvalho Chehab  *
130c0d06caSMauro Carvalho Chehab  *  This program is distributed in the hope that it will be useful,
140c0d06caSMauro Carvalho Chehab  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
150c0d06caSMauro Carvalho Chehab  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
160c0d06caSMauro Carvalho Chehab  *  GNU General Public License for more details.
170c0d06caSMauro Carvalho Chehab  *
180c0d06caSMauro Carvalho Chehab  *  You should have received a copy of the GNU General Public License
190c0d06caSMauro Carvalho Chehab  *  along with this program; if not, write to the Free Software
200c0d06caSMauro Carvalho Chehab  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
210c0d06caSMauro Carvalho Chehab  */
220c0d06caSMauro Carvalho Chehab 
23589dadf2SMauro Carvalho Chehab #include "cx231xx.h"
240c0d06caSMauro Carvalho Chehab #include <linux/kernel.h>
250c0d06caSMauro Carvalho Chehab #include <linux/init.h>
260c0d06caSMauro Carvalho Chehab #include <linux/sound.h>
270c0d06caSMauro Carvalho Chehab #include <linux/spinlock.h>
280c0d06caSMauro Carvalho Chehab #include <linux/soundcard.h>
290c0d06caSMauro Carvalho Chehab #include <linux/slab.h>
300c0d06caSMauro Carvalho Chehab #include <linux/vmalloc.h>
310c0d06caSMauro Carvalho Chehab #include <linux/proc_fs.h>
320c0d06caSMauro Carvalho Chehab #include <linux/module.h>
330c0d06caSMauro Carvalho Chehab #include <sound/core.h>
340c0d06caSMauro Carvalho Chehab #include <sound/pcm.h>
350c0d06caSMauro Carvalho Chehab #include <sound/pcm_params.h>
360c0d06caSMauro Carvalho Chehab #include <sound/info.h>
370c0d06caSMauro Carvalho Chehab #include <sound/initval.h>
380c0d06caSMauro Carvalho Chehab #include <sound/control.h>
390c0d06caSMauro Carvalho Chehab #include <media/v4l2-common.h>
400c0d06caSMauro Carvalho Chehab 
410c0d06caSMauro Carvalho Chehab static int debug;
420c0d06caSMauro Carvalho Chehab module_param(debug, int, 0644);
430c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(debug, "activates debug info");
440c0d06caSMauro Carvalho Chehab 
450c0d06caSMauro Carvalho Chehab static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
460c0d06caSMauro Carvalho Chehab 
470c0d06caSMauro Carvalho Chehab static int cx231xx_isoc_audio_deinit(struct cx231xx *dev)
480c0d06caSMauro Carvalho Chehab {
490c0d06caSMauro Carvalho Chehab 	int i;
500c0d06caSMauro Carvalho Chehab 
51336fea92SMauro Carvalho Chehab 	dev_dbg(dev->dev, "Stopping isoc\n");
520c0d06caSMauro Carvalho Chehab 
530c0d06caSMauro Carvalho Chehab 	for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
540c0d06caSMauro Carvalho Chehab 		if (dev->adev.urb[i]) {
550c0d06caSMauro Carvalho Chehab 			if (!irqs_disabled())
560c0d06caSMauro Carvalho Chehab 				usb_kill_urb(dev->adev.urb[i]);
570c0d06caSMauro Carvalho Chehab 			else
580c0d06caSMauro Carvalho Chehab 				usb_unlink_urb(dev->adev.urb[i]);
590c0d06caSMauro Carvalho Chehab 
600c0d06caSMauro Carvalho Chehab 			usb_free_urb(dev->adev.urb[i]);
610c0d06caSMauro Carvalho Chehab 			dev->adev.urb[i] = NULL;
620c0d06caSMauro Carvalho Chehab 
630c0d06caSMauro Carvalho Chehab 			kfree(dev->adev.transfer_buffer[i]);
640c0d06caSMauro Carvalho Chehab 			dev->adev.transfer_buffer[i] = NULL;
650c0d06caSMauro Carvalho Chehab 		}
660c0d06caSMauro Carvalho Chehab 	}
670c0d06caSMauro Carvalho Chehab 
680c0d06caSMauro Carvalho Chehab 	return 0;
690c0d06caSMauro Carvalho Chehab }
700c0d06caSMauro Carvalho Chehab 
710c0d06caSMauro Carvalho Chehab static int cx231xx_bulk_audio_deinit(struct cx231xx *dev)
720c0d06caSMauro Carvalho Chehab {
730c0d06caSMauro Carvalho Chehab 	int i;
740c0d06caSMauro Carvalho Chehab 
75336fea92SMauro Carvalho Chehab 	dev_dbg(dev->dev, "Stopping bulk\n");
760c0d06caSMauro Carvalho Chehab 
770c0d06caSMauro Carvalho Chehab 	for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
780c0d06caSMauro Carvalho Chehab 		if (dev->adev.urb[i]) {
790c0d06caSMauro Carvalho Chehab 			if (!irqs_disabled())
800c0d06caSMauro Carvalho Chehab 				usb_kill_urb(dev->adev.urb[i]);
810c0d06caSMauro Carvalho Chehab 			else
820c0d06caSMauro Carvalho Chehab 				usb_unlink_urb(dev->adev.urb[i]);
830c0d06caSMauro Carvalho Chehab 
840c0d06caSMauro Carvalho Chehab 			usb_free_urb(dev->adev.urb[i]);
850c0d06caSMauro Carvalho Chehab 			dev->adev.urb[i] = NULL;
860c0d06caSMauro Carvalho Chehab 
870c0d06caSMauro Carvalho Chehab 			kfree(dev->adev.transfer_buffer[i]);
880c0d06caSMauro Carvalho Chehab 			dev->adev.transfer_buffer[i] = NULL;
890c0d06caSMauro Carvalho Chehab 		}
900c0d06caSMauro Carvalho Chehab 	}
910c0d06caSMauro Carvalho Chehab 
920c0d06caSMauro Carvalho Chehab 	return 0;
930c0d06caSMauro Carvalho Chehab }
940c0d06caSMauro Carvalho Chehab 
950c0d06caSMauro Carvalho Chehab static void cx231xx_audio_isocirq(struct urb *urb)
960c0d06caSMauro Carvalho Chehab {
970c0d06caSMauro Carvalho Chehab 	struct cx231xx *dev = urb->context;
980c0d06caSMauro Carvalho Chehab 	int i;
990c0d06caSMauro Carvalho Chehab 	unsigned int oldptr;
1000c0d06caSMauro Carvalho Chehab 	int period_elapsed = 0;
1010c0d06caSMauro Carvalho Chehab 	int status;
1020c0d06caSMauro Carvalho Chehab 	unsigned char *cp;
1030c0d06caSMauro Carvalho Chehab 	unsigned int stride;
1040c0d06caSMauro Carvalho Chehab 	struct snd_pcm_substream *substream;
1050c0d06caSMauro Carvalho Chehab 	struct snd_pcm_runtime *runtime;
1060c0d06caSMauro Carvalho Chehab 
1070c0d06caSMauro Carvalho Chehab 	if (dev->state & DEV_DISCONNECTED)
1080c0d06caSMauro Carvalho Chehab 		return;
1090c0d06caSMauro Carvalho Chehab 
1100c0d06caSMauro Carvalho Chehab 	switch (urb->status) {
1110c0d06caSMauro Carvalho Chehab 	case 0:		/* success */
1120c0d06caSMauro Carvalho Chehab 	case -ETIMEDOUT:	/* NAK */
1130c0d06caSMauro Carvalho Chehab 		break;
1140c0d06caSMauro Carvalho Chehab 	case -ECONNRESET:	/* kill */
1150c0d06caSMauro Carvalho Chehab 	case -ENOENT:
1160c0d06caSMauro Carvalho Chehab 	case -ESHUTDOWN:
1170c0d06caSMauro Carvalho Chehab 		return;
1180c0d06caSMauro Carvalho Chehab 	default:		/* error */
119336fea92SMauro Carvalho Chehab 		dev_dbg(dev->dev, "urb completition error %d.\n",
12056d8a3b0SMauro Carvalho Chehab 			urb->status);
1210c0d06caSMauro Carvalho Chehab 		break;
1220c0d06caSMauro Carvalho Chehab 	}
1230c0d06caSMauro Carvalho Chehab 
1240c0d06caSMauro Carvalho Chehab 	if (atomic_read(&dev->stream_started) == 0)
1250c0d06caSMauro Carvalho Chehab 		return;
1260c0d06caSMauro Carvalho Chehab 
1270c0d06caSMauro Carvalho Chehab 	if (dev->adev.capture_pcm_substream) {
1280c0d06caSMauro Carvalho Chehab 		substream = dev->adev.capture_pcm_substream;
1290c0d06caSMauro Carvalho Chehab 		runtime = substream->runtime;
1300c0d06caSMauro Carvalho Chehab 		stride = runtime->frame_bits >> 3;
1310c0d06caSMauro Carvalho Chehab 
1320c0d06caSMauro Carvalho Chehab 		for (i = 0; i < urb->number_of_packets; i++) {
1330c0d06caSMauro Carvalho Chehab 			int length = urb->iso_frame_desc[i].actual_length /
1340c0d06caSMauro Carvalho Chehab 				     stride;
1350c0d06caSMauro Carvalho Chehab 			cp = (unsigned char *)urb->transfer_buffer +
1360c0d06caSMauro Carvalho Chehab 					      urb->iso_frame_desc[i].offset;
1370c0d06caSMauro Carvalho Chehab 
1380c0d06caSMauro Carvalho Chehab 			if (!length)
1390c0d06caSMauro Carvalho Chehab 				continue;
1400c0d06caSMauro Carvalho Chehab 
1410c0d06caSMauro Carvalho Chehab 			oldptr = dev->adev.hwptr_done_capture;
1420c0d06caSMauro Carvalho Chehab 			if (oldptr + length >= runtime->buffer_size) {
1430c0d06caSMauro Carvalho Chehab 				unsigned int cnt;
1440c0d06caSMauro Carvalho Chehab 
1450c0d06caSMauro Carvalho Chehab 				cnt = runtime->buffer_size - oldptr;
1460c0d06caSMauro Carvalho Chehab 				memcpy(runtime->dma_area + oldptr * stride, cp,
1470c0d06caSMauro Carvalho Chehab 				       cnt * stride);
1480c0d06caSMauro Carvalho Chehab 				memcpy(runtime->dma_area, cp + cnt * stride,
1490c0d06caSMauro Carvalho Chehab 				       length * stride - cnt * stride);
1500c0d06caSMauro Carvalho Chehab 			} else {
1510c0d06caSMauro Carvalho Chehab 				memcpy(runtime->dma_area + oldptr * stride, cp,
1520c0d06caSMauro Carvalho Chehab 				       length * stride);
1530c0d06caSMauro Carvalho Chehab 			}
1540c0d06caSMauro Carvalho Chehab 
1550c0d06caSMauro Carvalho Chehab 			snd_pcm_stream_lock(substream);
1560c0d06caSMauro Carvalho Chehab 
1570c0d06caSMauro Carvalho Chehab 			dev->adev.hwptr_done_capture += length;
1580c0d06caSMauro Carvalho Chehab 			if (dev->adev.hwptr_done_capture >=
1590c0d06caSMauro Carvalho Chehab 						runtime->buffer_size)
1600c0d06caSMauro Carvalho Chehab 				dev->adev.hwptr_done_capture -=
1610c0d06caSMauro Carvalho Chehab 						runtime->buffer_size;
1620c0d06caSMauro Carvalho Chehab 
1630c0d06caSMauro Carvalho Chehab 			dev->adev.capture_transfer_done += length;
1640c0d06caSMauro Carvalho Chehab 			if (dev->adev.capture_transfer_done >=
1650c0d06caSMauro Carvalho Chehab 				runtime->period_size) {
1660c0d06caSMauro Carvalho Chehab 				dev->adev.capture_transfer_done -=
1670c0d06caSMauro Carvalho Chehab 						runtime->period_size;
1680c0d06caSMauro Carvalho Chehab 				period_elapsed = 1;
1690c0d06caSMauro Carvalho Chehab 			}
1700c0d06caSMauro Carvalho Chehab 			snd_pcm_stream_unlock(substream);
1710c0d06caSMauro Carvalho Chehab 		}
1720c0d06caSMauro Carvalho Chehab 		if (period_elapsed)
1730c0d06caSMauro Carvalho Chehab 			snd_pcm_period_elapsed(substream);
1740c0d06caSMauro Carvalho Chehab 	}
1750c0d06caSMauro Carvalho Chehab 	urb->status = 0;
1760c0d06caSMauro Carvalho Chehab 
1770c0d06caSMauro Carvalho Chehab 	status = usb_submit_urb(urb, GFP_ATOMIC);
1780c0d06caSMauro Carvalho Chehab 	if (status < 0) {
179336fea92SMauro Carvalho Chehab 		dev_err(dev->dev,
180b7085c08SMauro Carvalho Chehab 			"resubmit of audio urb failed (error=%i)\n",
1810c0d06caSMauro Carvalho Chehab 			status);
1820c0d06caSMauro Carvalho Chehab 	}
1830c0d06caSMauro Carvalho Chehab 	return;
1840c0d06caSMauro Carvalho Chehab }
1850c0d06caSMauro Carvalho Chehab 
1860c0d06caSMauro Carvalho Chehab static void cx231xx_audio_bulkirq(struct urb *urb)
1870c0d06caSMauro Carvalho Chehab {
1880c0d06caSMauro Carvalho Chehab 	struct cx231xx *dev = urb->context;
1890c0d06caSMauro Carvalho Chehab 	unsigned int oldptr;
1900c0d06caSMauro Carvalho Chehab 	int period_elapsed = 0;
1910c0d06caSMauro Carvalho Chehab 	int status;
1920c0d06caSMauro Carvalho Chehab 	unsigned char *cp;
1930c0d06caSMauro Carvalho Chehab 	unsigned int stride;
1940c0d06caSMauro Carvalho Chehab 	struct snd_pcm_substream *substream;
1950c0d06caSMauro Carvalho Chehab 	struct snd_pcm_runtime *runtime;
1960c0d06caSMauro Carvalho Chehab 
1970c0d06caSMauro Carvalho Chehab 	if (dev->state & DEV_DISCONNECTED)
1980c0d06caSMauro Carvalho Chehab 		return;
1990c0d06caSMauro Carvalho Chehab 
2000c0d06caSMauro Carvalho Chehab 	switch (urb->status) {
2010c0d06caSMauro Carvalho Chehab 	case 0:		/* success */
2020c0d06caSMauro Carvalho Chehab 	case -ETIMEDOUT:	/* NAK */
2030c0d06caSMauro Carvalho Chehab 		break;
2040c0d06caSMauro Carvalho Chehab 	case -ECONNRESET:	/* kill */
2050c0d06caSMauro Carvalho Chehab 	case -ENOENT:
2060c0d06caSMauro Carvalho Chehab 	case -ESHUTDOWN:
2070c0d06caSMauro Carvalho Chehab 		return;
2080c0d06caSMauro Carvalho Chehab 	default:		/* error */
209336fea92SMauro Carvalho Chehab 		dev_dbg(dev->dev, "urb completition error %d.\n",
21056d8a3b0SMauro Carvalho Chehab 			urb->status);
2110c0d06caSMauro Carvalho Chehab 		break;
2120c0d06caSMauro Carvalho Chehab 	}
2130c0d06caSMauro Carvalho Chehab 
2140c0d06caSMauro Carvalho Chehab 	if (atomic_read(&dev->stream_started) == 0)
2150c0d06caSMauro Carvalho Chehab 		return;
2160c0d06caSMauro Carvalho Chehab 
2170c0d06caSMauro Carvalho Chehab 	if (dev->adev.capture_pcm_substream) {
2180c0d06caSMauro Carvalho Chehab 		substream = dev->adev.capture_pcm_substream;
2190c0d06caSMauro Carvalho Chehab 		runtime = substream->runtime;
2200c0d06caSMauro Carvalho Chehab 		stride = runtime->frame_bits >> 3;
2210c0d06caSMauro Carvalho Chehab 
2220c0d06caSMauro Carvalho Chehab 		if (1) {
2230c0d06caSMauro Carvalho Chehab 			int length = urb->actual_length /
2240c0d06caSMauro Carvalho Chehab 				     stride;
2250c0d06caSMauro Carvalho Chehab 			cp = (unsigned char *)urb->transfer_buffer;
2260c0d06caSMauro Carvalho Chehab 
2270c0d06caSMauro Carvalho Chehab 			oldptr = dev->adev.hwptr_done_capture;
2280c0d06caSMauro Carvalho Chehab 			if (oldptr + length >= runtime->buffer_size) {
2290c0d06caSMauro Carvalho Chehab 				unsigned int cnt;
2300c0d06caSMauro Carvalho Chehab 
2310c0d06caSMauro Carvalho Chehab 				cnt = runtime->buffer_size - oldptr;
2320c0d06caSMauro Carvalho Chehab 				memcpy(runtime->dma_area + oldptr * stride, cp,
2330c0d06caSMauro Carvalho Chehab 				       cnt * stride);
2340c0d06caSMauro Carvalho Chehab 				memcpy(runtime->dma_area, cp + cnt * stride,
2350c0d06caSMauro Carvalho Chehab 				       length * stride - cnt * stride);
2360c0d06caSMauro Carvalho Chehab 			} else {
2370c0d06caSMauro Carvalho Chehab 				memcpy(runtime->dma_area + oldptr * stride, cp,
2380c0d06caSMauro Carvalho Chehab 				       length * stride);
2390c0d06caSMauro Carvalho Chehab 			}
2400c0d06caSMauro Carvalho Chehab 
2410c0d06caSMauro Carvalho Chehab 			snd_pcm_stream_lock(substream);
2420c0d06caSMauro Carvalho Chehab 
2430c0d06caSMauro Carvalho Chehab 			dev->adev.hwptr_done_capture += length;
2440c0d06caSMauro Carvalho Chehab 			if (dev->adev.hwptr_done_capture >=
2450c0d06caSMauro Carvalho Chehab 						runtime->buffer_size)
2460c0d06caSMauro Carvalho Chehab 				dev->adev.hwptr_done_capture -=
2470c0d06caSMauro Carvalho Chehab 						runtime->buffer_size;
2480c0d06caSMauro Carvalho Chehab 
2490c0d06caSMauro Carvalho Chehab 			dev->adev.capture_transfer_done += length;
2500c0d06caSMauro Carvalho Chehab 			if (dev->adev.capture_transfer_done >=
2510c0d06caSMauro Carvalho Chehab 				runtime->period_size) {
2520c0d06caSMauro Carvalho Chehab 				dev->adev.capture_transfer_done -=
2530c0d06caSMauro Carvalho Chehab 						runtime->period_size;
2540c0d06caSMauro Carvalho Chehab 				period_elapsed = 1;
2550c0d06caSMauro Carvalho Chehab 			}
2560c0d06caSMauro Carvalho Chehab 			snd_pcm_stream_unlock(substream);
2570c0d06caSMauro Carvalho Chehab 		}
2580c0d06caSMauro Carvalho Chehab 		if (period_elapsed)
2590c0d06caSMauro Carvalho Chehab 			snd_pcm_period_elapsed(substream);
2600c0d06caSMauro Carvalho Chehab 	}
2610c0d06caSMauro Carvalho Chehab 	urb->status = 0;
2620c0d06caSMauro Carvalho Chehab 
2630c0d06caSMauro Carvalho Chehab 	status = usb_submit_urb(urb, GFP_ATOMIC);
2640c0d06caSMauro Carvalho Chehab 	if (status < 0) {
265336fea92SMauro Carvalho Chehab 		dev_err(dev->dev,
266b7085c08SMauro Carvalho Chehab 			"resubmit of audio urb failed (error=%i)\n",
2670c0d06caSMauro Carvalho Chehab 			status);
2680c0d06caSMauro Carvalho Chehab 	}
2690c0d06caSMauro Carvalho Chehab 	return;
2700c0d06caSMauro Carvalho Chehab }
2710c0d06caSMauro Carvalho Chehab 
2720c0d06caSMauro Carvalho Chehab static int cx231xx_init_audio_isoc(struct cx231xx *dev)
2730c0d06caSMauro Carvalho Chehab {
2740c0d06caSMauro Carvalho Chehab 	int i, errCode;
2750c0d06caSMauro Carvalho Chehab 	int sb_size;
2760c0d06caSMauro Carvalho Chehab 
277336fea92SMauro Carvalho Chehab 	dev_dbg(dev->dev,
278b7085c08SMauro Carvalho Chehab 		"%s: Starting ISO AUDIO transfers\n", __func__);
2790c0d06caSMauro Carvalho Chehab 
2800c0d06caSMauro Carvalho Chehab 	if (dev->state & DEV_DISCONNECTED)
2810c0d06caSMauro Carvalho Chehab 		return -ENODEV;
2820c0d06caSMauro Carvalho Chehab 
2830c0d06caSMauro Carvalho Chehab 	sb_size = CX231XX_ISO_NUM_AUDIO_PACKETS * dev->adev.max_pkt_size;
2840c0d06caSMauro Carvalho Chehab 
2850c0d06caSMauro Carvalho Chehab 	for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
2860c0d06caSMauro Carvalho Chehab 		struct urb *urb;
2870c0d06caSMauro Carvalho Chehab 		int j, k;
2880c0d06caSMauro Carvalho Chehab 
2890c0d06caSMauro Carvalho Chehab 		dev->adev.transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC);
2900c0d06caSMauro Carvalho Chehab 		if (!dev->adev.transfer_buffer[i])
2910c0d06caSMauro Carvalho Chehab 			return -ENOMEM;
2920c0d06caSMauro Carvalho Chehab 
2930c0d06caSMauro Carvalho Chehab 		memset(dev->adev.transfer_buffer[i], 0x80, sb_size);
2940c0d06caSMauro Carvalho Chehab 		urb = usb_alloc_urb(CX231XX_ISO_NUM_AUDIO_PACKETS, GFP_ATOMIC);
2950c0d06caSMauro Carvalho Chehab 		if (!urb) {
296336fea92SMauro Carvalho Chehab 			dev_err(dev->dev, "usb_alloc_urb failed!\n");
2970c0d06caSMauro Carvalho Chehab 			for (j = 0; j < i; j++) {
2980c0d06caSMauro Carvalho Chehab 				usb_free_urb(dev->adev.urb[j]);
2990c0d06caSMauro Carvalho Chehab 				kfree(dev->adev.transfer_buffer[j]);
3000c0d06caSMauro Carvalho Chehab 			}
3010c0d06caSMauro Carvalho Chehab 			return -ENOMEM;
3020c0d06caSMauro Carvalho Chehab 		}
3030c0d06caSMauro Carvalho Chehab 
3040c0d06caSMauro Carvalho Chehab 		urb->dev = dev->udev;
3050c0d06caSMauro Carvalho Chehab 		urb->context = dev;
3060c0d06caSMauro Carvalho Chehab 		urb->pipe = usb_rcvisocpipe(dev->udev,
3070c0d06caSMauro Carvalho Chehab 						dev->adev.end_point_addr);
3080c0d06caSMauro Carvalho Chehab 		urb->transfer_flags = URB_ISO_ASAP;
3090c0d06caSMauro Carvalho Chehab 		urb->transfer_buffer = dev->adev.transfer_buffer[i];
3100c0d06caSMauro Carvalho Chehab 		urb->interval = 1;
3110c0d06caSMauro Carvalho Chehab 		urb->complete = cx231xx_audio_isocirq;
3120c0d06caSMauro Carvalho Chehab 		urb->number_of_packets = CX231XX_ISO_NUM_AUDIO_PACKETS;
3130c0d06caSMauro Carvalho Chehab 		urb->transfer_buffer_length = sb_size;
3140c0d06caSMauro Carvalho Chehab 
3150c0d06caSMauro Carvalho Chehab 		for (j = k = 0; j < CX231XX_ISO_NUM_AUDIO_PACKETS;
3160c0d06caSMauro Carvalho Chehab 			j++, k += dev->adev.max_pkt_size) {
3170c0d06caSMauro Carvalho Chehab 			urb->iso_frame_desc[j].offset = k;
3180c0d06caSMauro Carvalho Chehab 			urb->iso_frame_desc[j].length = dev->adev.max_pkt_size;
3190c0d06caSMauro Carvalho Chehab 		}
3200c0d06caSMauro Carvalho Chehab 		dev->adev.urb[i] = urb;
3210c0d06caSMauro Carvalho Chehab 	}
3220c0d06caSMauro Carvalho Chehab 
3230c0d06caSMauro Carvalho Chehab 	for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
3240c0d06caSMauro Carvalho Chehab 		errCode = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC);
3250c0d06caSMauro Carvalho Chehab 		if (errCode < 0) {
3260c0d06caSMauro Carvalho Chehab 			cx231xx_isoc_audio_deinit(dev);
3270c0d06caSMauro Carvalho Chehab 			return errCode;
3280c0d06caSMauro Carvalho Chehab 		}
3290c0d06caSMauro Carvalho Chehab 	}
3300c0d06caSMauro Carvalho Chehab 
3310c0d06caSMauro Carvalho Chehab 	return errCode;
3320c0d06caSMauro Carvalho Chehab }
3330c0d06caSMauro Carvalho Chehab 
3340c0d06caSMauro Carvalho Chehab static int cx231xx_init_audio_bulk(struct cx231xx *dev)
3350c0d06caSMauro Carvalho Chehab {
3360c0d06caSMauro Carvalho Chehab 	int i, errCode;
3370c0d06caSMauro Carvalho Chehab 	int sb_size;
3380c0d06caSMauro Carvalho Chehab 
339336fea92SMauro Carvalho Chehab 	dev_dbg(dev->dev,
340b7085c08SMauro Carvalho Chehab 		"%s: Starting BULK AUDIO transfers\n", __func__);
3410c0d06caSMauro Carvalho Chehab 
3420c0d06caSMauro Carvalho Chehab 	if (dev->state & DEV_DISCONNECTED)
3430c0d06caSMauro Carvalho Chehab 		return -ENODEV;
3440c0d06caSMauro Carvalho Chehab 
3450c0d06caSMauro Carvalho Chehab 	sb_size = CX231XX_NUM_AUDIO_PACKETS * dev->adev.max_pkt_size;
3460c0d06caSMauro Carvalho Chehab 
3470c0d06caSMauro Carvalho Chehab 	for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
3480c0d06caSMauro Carvalho Chehab 		struct urb *urb;
3490c0d06caSMauro Carvalho Chehab 		int j;
3500c0d06caSMauro Carvalho Chehab 
3510c0d06caSMauro Carvalho Chehab 		dev->adev.transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC);
3520c0d06caSMauro Carvalho Chehab 		if (!dev->adev.transfer_buffer[i])
3530c0d06caSMauro Carvalho Chehab 			return -ENOMEM;
3540c0d06caSMauro Carvalho Chehab 
3550c0d06caSMauro Carvalho Chehab 		memset(dev->adev.transfer_buffer[i], 0x80, sb_size);
3560c0d06caSMauro Carvalho Chehab 		urb = usb_alloc_urb(CX231XX_NUM_AUDIO_PACKETS, GFP_ATOMIC);
3570c0d06caSMauro Carvalho Chehab 		if (!urb) {
358336fea92SMauro Carvalho Chehab 			dev_err(dev->dev, "usb_alloc_urb failed!\n");
3590c0d06caSMauro Carvalho Chehab 			for (j = 0; j < i; j++) {
3600c0d06caSMauro Carvalho Chehab 				usb_free_urb(dev->adev.urb[j]);
3610c0d06caSMauro Carvalho Chehab 				kfree(dev->adev.transfer_buffer[j]);
3620c0d06caSMauro Carvalho Chehab 			}
3630c0d06caSMauro Carvalho Chehab 			return -ENOMEM;
3640c0d06caSMauro Carvalho Chehab 		}
3650c0d06caSMauro Carvalho Chehab 
3660c0d06caSMauro Carvalho Chehab 		urb->dev = dev->udev;
3670c0d06caSMauro Carvalho Chehab 		urb->context = dev;
3680c0d06caSMauro Carvalho Chehab 		urb->pipe = usb_rcvbulkpipe(dev->udev,
3690c0d06caSMauro Carvalho Chehab 						dev->adev.end_point_addr);
3700c0d06caSMauro Carvalho Chehab 		urb->transfer_flags = 0;
3710c0d06caSMauro Carvalho Chehab 		urb->transfer_buffer = dev->adev.transfer_buffer[i];
3720c0d06caSMauro Carvalho Chehab 		urb->complete = cx231xx_audio_bulkirq;
3730c0d06caSMauro Carvalho Chehab 		urb->transfer_buffer_length = sb_size;
3740c0d06caSMauro Carvalho Chehab 
3750c0d06caSMauro Carvalho Chehab 		dev->adev.urb[i] = urb;
3760c0d06caSMauro Carvalho Chehab 
3770c0d06caSMauro Carvalho Chehab 	}
3780c0d06caSMauro Carvalho Chehab 
3790c0d06caSMauro Carvalho Chehab 	for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
3800c0d06caSMauro Carvalho Chehab 		errCode = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC);
3810c0d06caSMauro Carvalho Chehab 		if (errCode < 0) {
3820c0d06caSMauro Carvalho Chehab 			cx231xx_bulk_audio_deinit(dev);
3830c0d06caSMauro Carvalho Chehab 			return errCode;
3840c0d06caSMauro Carvalho Chehab 		}
3850c0d06caSMauro Carvalho Chehab 	}
3860c0d06caSMauro Carvalho Chehab 
3870c0d06caSMauro Carvalho Chehab 	return errCode;
3880c0d06caSMauro Carvalho Chehab }
3890c0d06caSMauro Carvalho Chehab 
3900c0d06caSMauro Carvalho Chehab static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs,
3910c0d06caSMauro Carvalho Chehab 					size_t size)
3920c0d06caSMauro Carvalho Chehab {
3930c0d06caSMauro Carvalho Chehab 	struct snd_pcm_runtime *runtime = subs->runtime;
39456d8a3b0SMauro Carvalho Chehab 	struct cx231xx *dev = snd_pcm_substream_chip(subs);
3950c0d06caSMauro Carvalho Chehab 
396336fea92SMauro Carvalho Chehab 	dev_dbg(dev->dev, "Allocating vbuffer\n");
3970c0d06caSMauro Carvalho Chehab 	if (runtime->dma_area) {
3980c0d06caSMauro Carvalho Chehab 		if (runtime->dma_bytes > size)
3990c0d06caSMauro Carvalho Chehab 			return 0;
4000c0d06caSMauro Carvalho Chehab 
4010c0d06caSMauro Carvalho Chehab 		vfree(runtime->dma_area);
4020c0d06caSMauro Carvalho Chehab 	}
4030c0d06caSMauro Carvalho Chehab 	runtime->dma_area = vmalloc(size);
4040c0d06caSMauro Carvalho Chehab 	if (!runtime->dma_area)
4050c0d06caSMauro Carvalho Chehab 		return -ENOMEM;
4060c0d06caSMauro Carvalho Chehab 
4070c0d06caSMauro Carvalho Chehab 	runtime->dma_bytes = size;
4080c0d06caSMauro Carvalho Chehab 
4090c0d06caSMauro Carvalho Chehab 	return 0;
4100c0d06caSMauro Carvalho Chehab }
4110c0d06caSMauro Carvalho Chehab 
4120c0d06caSMauro Carvalho Chehab static struct snd_pcm_hardware snd_cx231xx_hw_capture = {
4130c0d06caSMauro Carvalho Chehab 	.info = SNDRV_PCM_INFO_BLOCK_TRANSFER 	|
4140c0d06caSMauro Carvalho Chehab 	    SNDRV_PCM_INFO_MMAP 		|
4150c0d06caSMauro Carvalho Chehab 	    SNDRV_PCM_INFO_INTERLEAVED 		|
4160c0d06caSMauro Carvalho Chehab 	    SNDRV_PCM_INFO_MMAP_VALID,
4170c0d06caSMauro Carvalho Chehab 
4180c0d06caSMauro Carvalho Chehab 	.formats = SNDRV_PCM_FMTBIT_S16_LE,
4190c0d06caSMauro Carvalho Chehab 
4200c0d06caSMauro Carvalho Chehab 	.rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT,
4210c0d06caSMauro Carvalho Chehab 
4220c0d06caSMauro Carvalho Chehab 	.rate_min = 48000,
4230c0d06caSMauro Carvalho Chehab 	.rate_max = 48000,
4240c0d06caSMauro Carvalho Chehab 	.channels_min = 2,
4250c0d06caSMauro Carvalho Chehab 	.channels_max = 2,
4260c0d06caSMauro Carvalho Chehab 	.buffer_bytes_max = 62720 * 8,	/* just about the value in usbaudio.c */
4270c0d06caSMauro Carvalho Chehab 	.period_bytes_min = 64,		/* 12544/2, */
4280c0d06caSMauro Carvalho Chehab 	.period_bytes_max = 12544,
4290c0d06caSMauro Carvalho Chehab 	.periods_min = 2,
4300c0d06caSMauro Carvalho Chehab 	.periods_max = 98,		/* 12544, */
4310c0d06caSMauro Carvalho Chehab };
4320c0d06caSMauro Carvalho Chehab 
4330c0d06caSMauro Carvalho Chehab static int snd_cx231xx_capture_open(struct snd_pcm_substream *substream)
4340c0d06caSMauro Carvalho Chehab {
4350c0d06caSMauro Carvalho Chehab 	struct cx231xx *dev = snd_pcm_substream_chip(substream);
4360c0d06caSMauro Carvalho Chehab 	struct snd_pcm_runtime *runtime = substream->runtime;
4370c0d06caSMauro Carvalho Chehab 	int ret = 0;
4380c0d06caSMauro Carvalho Chehab 
439336fea92SMauro Carvalho Chehab 	dev_dbg(dev->dev,
44056d8a3b0SMauro Carvalho Chehab 		"opening device and trying to acquire exclusive lock\n");
4410c0d06caSMauro Carvalho Chehab 
4420c0d06caSMauro Carvalho Chehab 	if (dev->state & DEV_DISCONNECTED) {
443336fea92SMauro Carvalho Chehab 		dev_err(dev->dev,
444b7085c08SMauro Carvalho Chehab 			"Can't open. the device was removed.\n");
4450c0d06caSMauro Carvalho Chehab 		return -ENODEV;
4460c0d06caSMauro Carvalho Chehab 	}
4470c0d06caSMauro Carvalho Chehab 
4480c0d06caSMauro Carvalho Chehab 	/* set alternate setting for audio interface */
4490c0d06caSMauro Carvalho Chehab 	/* 1 - 48000 samples per sec */
4500c0d06caSMauro Carvalho Chehab 	mutex_lock(&dev->lock);
4510c0d06caSMauro Carvalho Chehab 	if (dev->USE_ISO)
4520c0d06caSMauro Carvalho Chehab 		ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 1);
4530c0d06caSMauro Carvalho Chehab 	else
4540c0d06caSMauro Carvalho Chehab 		ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 0);
4550c0d06caSMauro Carvalho Chehab 	mutex_unlock(&dev->lock);
4560c0d06caSMauro Carvalho Chehab 	if (ret < 0) {
457336fea92SMauro Carvalho Chehab 		dev_err(dev->dev,
458b7085c08SMauro Carvalho Chehab 			"failed to set alternate setting !\n");
4590c0d06caSMauro Carvalho Chehab 
4600c0d06caSMauro Carvalho Chehab 		return ret;
4610c0d06caSMauro Carvalho Chehab 	}
4620c0d06caSMauro Carvalho Chehab 
4630c0d06caSMauro Carvalho Chehab 	runtime->hw = snd_cx231xx_hw_capture;
4640c0d06caSMauro Carvalho Chehab 
4650c0d06caSMauro Carvalho Chehab 	mutex_lock(&dev->lock);
4660c0d06caSMauro Carvalho Chehab 	/* inform hardware to start streaming */
4670c0d06caSMauro Carvalho Chehab 	ret = cx231xx_capture_start(dev, 1, Audio);
4680c0d06caSMauro Carvalho Chehab 
4690c0d06caSMauro Carvalho Chehab 	dev->adev.users++;
4700c0d06caSMauro Carvalho Chehab 	mutex_unlock(&dev->lock);
4710c0d06caSMauro Carvalho Chehab 
4720c0d06caSMauro Carvalho Chehab 	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
4730c0d06caSMauro Carvalho Chehab 	dev->adev.capture_pcm_substream = substream;
4740c0d06caSMauro Carvalho Chehab 	runtime->private_data = dev;
4750c0d06caSMauro Carvalho Chehab 
4760c0d06caSMauro Carvalho Chehab 	return 0;
4770c0d06caSMauro Carvalho Chehab }
4780c0d06caSMauro Carvalho Chehab 
4790c0d06caSMauro Carvalho Chehab static int snd_cx231xx_pcm_close(struct snd_pcm_substream *substream)
4800c0d06caSMauro Carvalho Chehab {
4810c0d06caSMauro Carvalho Chehab 	int ret;
4820c0d06caSMauro Carvalho Chehab 	struct cx231xx *dev = snd_pcm_substream_chip(substream);
4830c0d06caSMauro Carvalho Chehab 
484336fea92SMauro Carvalho Chehab 	dev_dbg(dev->dev, "closing device\n");
4850c0d06caSMauro Carvalho Chehab 
4860c0d06caSMauro Carvalho Chehab 	/* inform hardware to stop streaming */
4870c0d06caSMauro Carvalho Chehab 	mutex_lock(&dev->lock);
4880c0d06caSMauro Carvalho Chehab 	ret = cx231xx_capture_start(dev, 0, Audio);
4890c0d06caSMauro Carvalho Chehab 
4900c0d06caSMauro Carvalho Chehab 	/* set alternate setting for audio interface */
4910c0d06caSMauro Carvalho Chehab 	/* 1 - 48000 samples per sec */
4920c0d06caSMauro Carvalho Chehab 	ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 0);
4930c0d06caSMauro Carvalho Chehab 	if (ret < 0) {
494336fea92SMauro Carvalho Chehab 		dev_err(dev->dev,
495b7085c08SMauro Carvalho Chehab 			"failed to set alternate setting !\n");
4960c0d06caSMauro Carvalho Chehab 
4970c0d06caSMauro Carvalho Chehab 		mutex_unlock(&dev->lock);
4980c0d06caSMauro Carvalho Chehab 		return ret;
4990c0d06caSMauro Carvalho Chehab 	}
5000c0d06caSMauro Carvalho Chehab 
5010c0d06caSMauro Carvalho Chehab 	dev->adev.users--;
502*9dd73448SJean-Baptiste Theou 	if (substream->runtime->dma_area) {
503*9dd73448SJean-Baptiste Theou 		dev_dbg(dev->dev, "freeing\n");
504*9dd73448SJean-Baptiste Theou 		vfree(substream->runtime->dma_area);
505*9dd73448SJean-Baptiste Theou 		substream->runtime->dma_area = NULL;
506*9dd73448SJean-Baptiste Theou 	}
5070c0d06caSMauro Carvalho Chehab 	mutex_unlock(&dev->lock);
5080c0d06caSMauro Carvalho Chehab 
5090c0d06caSMauro Carvalho Chehab 	if (dev->adev.users == 0 && dev->adev.shutdown == 1) {
510336fea92SMauro Carvalho Chehab 		dev_dbg(dev->dev, "audio users: %d\n", dev->adev.users);
511336fea92SMauro Carvalho Chehab 		dev_dbg(dev->dev, "disabling audio stream!\n");
5120c0d06caSMauro Carvalho Chehab 		dev->adev.shutdown = 0;
513336fea92SMauro Carvalho Chehab 		dev_dbg(dev->dev, "released lock\n");
5140c0d06caSMauro Carvalho Chehab 		if (atomic_read(&dev->stream_started) > 0) {
5150c0d06caSMauro Carvalho Chehab 			atomic_set(&dev->stream_started, 0);
5160c0d06caSMauro Carvalho Chehab 			schedule_work(&dev->wq_trigger);
5170c0d06caSMauro Carvalho Chehab 		}
5180c0d06caSMauro Carvalho Chehab 	}
5190c0d06caSMauro Carvalho Chehab 	return 0;
5200c0d06caSMauro Carvalho Chehab }
5210c0d06caSMauro Carvalho Chehab 
5220c0d06caSMauro Carvalho Chehab static int snd_cx231xx_hw_capture_params(struct snd_pcm_substream *substream,
5230c0d06caSMauro Carvalho Chehab 					 struct snd_pcm_hw_params *hw_params)
5240c0d06caSMauro Carvalho Chehab {
52556d8a3b0SMauro Carvalho Chehab 	struct cx231xx *dev = snd_pcm_substream_chip(substream);
5260c0d06caSMauro Carvalho Chehab 	int ret;
5270c0d06caSMauro Carvalho Chehab 
528336fea92SMauro Carvalho Chehab 	dev_dbg(dev->dev, "Setting capture parameters\n");
5290c0d06caSMauro Carvalho Chehab 
5300c0d06caSMauro Carvalho Chehab 	ret = snd_pcm_alloc_vmalloc_buffer(substream,
5310c0d06caSMauro Carvalho Chehab 					   params_buffer_bytes(hw_params));
5320c0d06caSMauro Carvalho Chehab #if 0
5330c0d06caSMauro Carvalho Chehab 	/* TODO: set up cx231xx audio chip to deliver the correct audio format,
5340c0d06caSMauro Carvalho Chehab 	   current default is 48000hz multiplexed => 96000hz mono
5350c0d06caSMauro Carvalho Chehab 	   which shouldn't matter since analogue TV only supports mono */
5360c0d06caSMauro Carvalho Chehab 	unsigned int channels, rate, format;
5370c0d06caSMauro Carvalho Chehab 
5380c0d06caSMauro Carvalho Chehab 	format = params_format(hw_params);
5390c0d06caSMauro Carvalho Chehab 	rate = params_rate(hw_params);
5400c0d06caSMauro Carvalho Chehab 	channels = params_channels(hw_params);
5410c0d06caSMauro Carvalho Chehab #endif
5420c0d06caSMauro Carvalho Chehab 
5430c0d06caSMauro Carvalho Chehab 	return ret;
5440c0d06caSMauro Carvalho Chehab }
5450c0d06caSMauro Carvalho Chehab 
5460c0d06caSMauro Carvalho Chehab static int snd_cx231xx_hw_capture_free(struct snd_pcm_substream *substream)
5470c0d06caSMauro Carvalho Chehab {
5480c0d06caSMauro Carvalho Chehab 	struct cx231xx *dev = snd_pcm_substream_chip(substream);
5490c0d06caSMauro Carvalho Chehab 
550336fea92SMauro Carvalho Chehab 	dev_dbg(dev->dev, "Stop capture, if needed\n");
5510c0d06caSMauro Carvalho Chehab 
5520c0d06caSMauro Carvalho Chehab 	if (atomic_read(&dev->stream_started) > 0) {
5530c0d06caSMauro Carvalho Chehab 		atomic_set(&dev->stream_started, 0);
5540c0d06caSMauro Carvalho Chehab 		schedule_work(&dev->wq_trigger);
5550c0d06caSMauro Carvalho Chehab 	}
5560c0d06caSMauro Carvalho Chehab 
5570c0d06caSMauro Carvalho Chehab 	return 0;
5580c0d06caSMauro Carvalho Chehab }
5590c0d06caSMauro Carvalho Chehab 
5600c0d06caSMauro Carvalho Chehab static int snd_cx231xx_prepare(struct snd_pcm_substream *substream)
5610c0d06caSMauro Carvalho Chehab {
5620c0d06caSMauro Carvalho Chehab 	struct cx231xx *dev = snd_pcm_substream_chip(substream);
5630c0d06caSMauro Carvalho Chehab 
5640c0d06caSMauro Carvalho Chehab 	dev->adev.hwptr_done_capture = 0;
5650c0d06caSMauro Carvalho Chehab 	dev->adev.capture_transfer_done = 0;
5660c0d06caSMauro Carvalho Chehab 
5670c0d06caSMauro Carvalho Chehab 	return 0;
5680c0d06caSMauro Carvalho Chehab }
5690c0d06caSMauro Carvalho Chehab 
5700c0d06caSMauro Carvalho Chehab static void audio_trigger(struct work_struct *work)
5710c0d06caSMauro Carvalho Chehab {
5720c0d06caSMauro Carvalho Chehab 	struct cx231xx *dev = container_of(work, struct cx231xx, wq_trigger);
5730c0d06caSMauro Carvalho Chehab 
5740c0d06caSMauro Carvalho Chehab 	if (atomic_read(&dev->stream_started)) {
575336fea92SMauro Carvalho Chehab 		dev_dbg(dev->dev, "starting capture");
5760c0d06caSMauro Carvalho Chehab 		if (is_fw_load(dev) == 0)
5770c0d06caSMauro Carvalho Chehab 			cx25840_call(dev, core, load_fw);
5780c0d06caSMauro Carvalho Chehab 		if (dev->USE_ISO)
5790c0d06caSMauro Carvalho Chehab 			cx231xx_init_audio_isoc(dev);
5800c0d06caSMauro Carvalho Chehab 		else
5810c0d06caSMauro Carvalho Chehab 			cx231xx_init_audio_bulk(dev);
5820c0d06caSMauro Carvalho Chehab 	} else {
583336fea92SMauro Carvalho Chehab 		dev_dbg(dev->dev, "stopping capture");
5840c0d06caSMauro Carvalho Chehab 		cx231xx_isoc_audio_deinit(dev);
5850c0d06caSMauro Carvalho Chehab 	}
5860c0d06caSMauro Carvalho Chehab }
5870c0d06caSMauro Carvalho Chehab 
5880c0d06caSMauro Carvalho Chehab static int snd_cx231xx_capture_trigger(struct snd_pcm_substream *substream,
5890c0d06caSMauro Carvalho Chehab 				       int cmd)
5900c0d06caSMauro Carvalho Chehab {
5910c0d06caSMauro Carvalho Chehab 	struct cx231xx *dev = snd_pcm_substream_chip(substream);
5920c0d06caSMauro Carvalho Chehab 	int retval = 0;
5930c0d06caSMauro Carvalho Chehab 
5940c0d06caSMauro Carvalho Chehab 	if (dev->state & DEV_DISCONNECTED)
5950c0d06caSMauro Carvalho Chehab 		return -ENODEV;
5960c0d06caSMauro Carvalho Chehab 
5970c0d06caSMauro Carvalho Chehab 	spin_lock(&dev->adev.slock);
5980c0d06caSMauro Carvalho Chehab 	switch (cmd) {
5990c0d06caSMauro Carvalho Chehab 	case SNDRV_PCM_TRIGGER_START:
6000c0d06caSMauro Carvalho Chehab 		atomic_set(&dev->stream_started, 1);
6010c0d06caSMauro Carvalho Chehab 		break;
6020c0d06caSMauro Carvalho Chehab 	case SNDRV_PCM_TRIGGER_STOP:
6030c0d06caSMauro Carvalho Chehab 		atomic_set(&dev->stream_started, 0);
6040c0d06caSMauro Carvalho Chehab 		break;
6050c0d06caSMauro Carvalho Chehab 	default:
6060c0d06caSMauro Carvalho Chehab 		retval = -EINVAL;
6070c0d06caSMauro Carvalho Chehab 		break;
6080c0d06caSMauro Carvalho Chehab 	}
6090c0d06caSMauro Carvalho Chehab 	spin_unlock(&dev->adev.slock);
6100c0d06caSMauro Carvalho Chehab 
6110c0d06caSMauro Carvalho Chehab 	schedule_work(&dev->wq_trigger);
6120c0d06caSMauro Carvalho Chehab 
6130c0d06caSMauro Carvalho Chehab 	return retval;
6140c0d06caSMauro Carvalho Chehab }
6150c0d06caSMauro Carvalho Chehab 
6160c0d06caSMauro Carvalho Chehab static snd_pcm_uframes_t snd_cx231xx_capture_pointer(struct snd_pcm_substream
6170c0d06caSMauro Carvalho Chehab 						     *substream)
6180c0d06caSMauro Carvalho Chehab {
6190c0d06caSMauro Carvalho Chehab 	struct cx231xx *dev;
6200c0d06caSMauro Carvalho Chehab 	unsigned long flags;
6210c0d06caSMauro Carvalho Chehab 	snd_pcm_uframes_t hwptr_done;
6220c0d06caSMauro Carvalho Chehab 
6230c0d06caSMauro Carvalho Chehab 	dev = snd_pcm_substream_chip(substream);
6240c0d06caSMauro Carvalho Chehab 
6250c0d06caSMauro Carvalho Chehab 	spin_lock_irqsave(&dev->adev.slock, flags);
6260c0d06caSMauro Carvalho Chehab 	hwptr_done = dev->adev.hwptr_done_capture;
6270c0d06caSMauro Carvalho Chehab 	spin_unlock_irqrestore(&dev->adev.slock, flags);
6280c0d06caSMauro Carvalho Chehab 
6290c0d06caSMauro Carvalho Chehab 	return hwptr_done;
6300c0d06caSMauro Carvalho Chehab }
6310c0d06caSMauro Carvalho Chehab 
6320c0d06caSMauro Carvalho Chehab static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs,
6330c0d06caSMauro Carvalho Chehab 					     unsigned long offset)
6340c0d06caSMauro Carvalho Chehab {
6350c0d06caSMauro Carvalho Chehab 	void *pageptr = subs->runtime->dma_area + offset;
6360c0d06caSMauro Carvalho Chehab 
6370c0d06caSMauro Carvalho Chehab 	return vmalloc_to_page(pageptr);
6380c0d06caSMauro Carvalho Chehab }
6390c0d06caSMauro Carvalho Chehab 
6400c0d06caSMauro Carvalho Chehab static struct snd_pcm_ops snd_cx231xx_pcm_capture = {
6410c0d06caSMauro Carvalho Chehab 	.open = snd_cx231xx_capture_open,
6420c0d06caSMauro Carvalho Chehab 	.close = snd_cx231xx_pcm_close,
6430c0d06caSMauro Carvalho Chehab 	.ioctl = snd_pcm_lib_ioctl,
6440c0d06caSMauro Carvalho Chehab 	.hw_params = snd_cx231xx_hw_capture_params,
6450c0d06caSMauro Carvalho Chehab 	.hw_free = snd_cx231xx_hw_capture_free,
6460c0d06caSMauro Carvalho Chehab 	.prepare = snd_cx231xx_prepare,
6470c0d06caSMauro Carvalho Chehab 	.trigger = snd_cx231xx_capture_trigger,
6480c0d06caSMauro Carvalho Chehab 	.pointer = snd_cx231xx_capture_pointer,
6490c0d06caSMauro Carvalho Chehab 	.page = snd_pcm_get_vmalloc_page,
6500c0d06caSMauro Carvalho Chehab };
6510c0d06caSMauro Carvalho Chehab 
6520c0d06caSMauro Carvalho Chehab static int cx231xx_audio_init(struct cx231xx *dev)
6530c0d06caSMauro Carvalho Chehab {
6540c0d06caSMauro Carvalho Chehab 	struct cx231xx_audio *adev = &dev->adev;
6550c0d06caSMauro Carvalho Chehab 	struct snd_pcm *pcm;
6560c0d06caSMauro Carvalho Chehab 	struct snd_card *card;
6570c0d06caSMauro Carvalho Chehab 	static int devnr;
6580c0d06caSMauro Carvalho Chehab 	int err;
6590c0d06caSMauro Carvalho Chehab 	struct usb_interface *uif;
6600c0d06caSMauro Carvalho Chehab 	int i, isoc_pipe = 0;
6610c0d06caSMauro Carvalho Chehab 
6620c0d06caSMauro Carvalho Chehab 	if (dev->has_alsa_audio != 1) {
6630c0d06caSMauro Carvalho Chehab 		/* This device does not support the extension (in this case
6640c0d06caSMauro Carvalho Chehab 		   the device is expecting the snd-usb-audio module or
6650c0d06caSMauro Carvalho Chehab 		   doesn't have analog audio support at all) */
6660c0d06caSMauro Carvalho Chehab 		return 0;
6670c0d06caSMauro Carvalho Chehab 	}
6680c0d06caSMauro Carvalho Chehab 
669336fea92SMauro Carvalho Chehab 	dev_dbg(dev->dev,
670b7085c08SMauro Carvalho Chehab 		"probing for cx231xx non standard usbaudio\n");
6710c0d06caSMauro Carvalho Chehab 
672336fea92SMauro Carvalho Chehab 	err = snd_card_new(dev->dev, index[devnr], "Cx231xx Audio",
673e7356888STakashi Iwai 			   THIS_MODULE, 0, &card);
6740c0d06caSMauro Carvalho Chehab 	if (err < 0)
6750c0d06caSMauro Carvalho Chehab 		return err;
6760c0d06caSMauro Carvalho Chehab 
6770c0d06caSMauro Carvalho Chehab 	spin_lock_init(&adev->slock);
6780c0d06caSMauro Carvalho Chehab 	err = snd_pcm_new(card, "Cx231xx Audio", 0, 0, 1, &pcm);
6790c0d06caSMauro Carvalho Chehab 	if (err < 0) {
6800c0d06caSMauro Carvalho Chehab 		snd_card_free(card);
6810c0d06caSMauro Carvalho Chehab 		return err;
6820c0d06caSMauro Carvalho Chehab 	}
6830c0d06caSMauro Carvalho Chehab 
6840c0d06caSMauro Carvalho Chehab 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
6850c0d06caSMauro Carvalho Chehab 			&snd_cx231xx_pcm_capture);
6860c0d06caSMauro Carvalho Chehab 	pcm->info_flags = 0;
6870c0d06caSMauro Carvalho Chehab 	pcm->private_data = dev;
6880c0d06caSMauro Carvalho Chehab 	strcpy(pcm->name, "Conexant cx231xx Capture");
6890c0d06caSMauro Carvalho Chehab 	strcpy(card->driver, "Cx231xx-Audio");
6900c0d06caSMauro Carvalho Chehab 	strcpy(card->shortname, "Cx231xx Audio");
6910c0d06caSMauro Carvalho Chehab 	strcpy(card->longname, "Conexant cx231xx Audio");
6920c0d06caSMauro Carvalho Chehab 
6930c0d06caSMauro Carvalho Chehab 	INIT_WORK(&dev->wq_trigger, audio_trigger);
6940c0d06caSMauro Carvalho Chehab 
6950c0d06caSMauro Carvalho Chehab 	err = snd_card_register(card);
6960c0d06caSMauro Carvalho Chehab 	if (err < 0) {
6970c0d06caSMauro Carvalho Chehab 		snd_card_free(card);
6980c0d06caSMauro Carvalho Chehab 		return err;
6990c0d06caSMauro Carvalho Chehab 	}
7000c0d06caSMauro Carvalho Chehab 	adev->sndcard = card;
7010c0d06caSMauro Carvalho Chehab 	adev->udev = dev->udev;
7020c0d06caSMauro Carvalho Chehab 
7030c0d06caSMauro Carvalho Chehab 	/* compute alternate max packet sizes for Audio */
7040c0d06caSMauro Carvalho Chehab 	uif =
7050c0d06caSMauro Carvalho Chehab 	    dev->udev->actconfig->interface[dev->current_pcb_config.
7060c0d06caSMauro Carvalho Chehab 					    hs_config_info[0].interface_info.
7070c0d06caSMauro Carvalho Chehab 					    audio_index + 1];
7080c0d06caSMauro Carvalho Chehab 
7090c0d06caSMauro Carvalho Chehab 	adev->end_point_addr =
71069a11a32SHans Verkuil 	    uif->altsetting[0].endpoint[isoc_pipe].desc.
71169a11a32SHans Verkuil 			bEndpointAddress;
7120c0d06caSMauro Carvalho Chehab 
7130c0d06caSMauro Carvalho Chehab 	adev->num_alt = uif->num_altsetting;
714336fea92SMauro Carvalho Chehab 	dev_info(dev->dev,
715b7085c08SMauro Carvalho Chehab 		"audio EndPoint Addr 0x%x, Alternate settings: %i\n",
7160c0d06caSMauro Carvalho Chehab 		adev->end_point_addr, adev->num_alt);
7170c0d06caSMauro Carvalho Chehab 	adev->alt_max_pkt_size = kmalloc(32 * adev->num_alt, GFP_KERNEL);
7180c0d06caSMauro Carvalho Chehab 
719ed0e3729SMauro Carvalho Chehab 	if (adev->alt_max_pkt_size == NULL)
7200c0d06caSMauro Carvalho Chehab 		return -ENOMEM;
7210c0d06caSMauro Carvalho Chehab 
7220c0d06caSMauro Carvalho Chehab 	for (i = 0; i < adev->num_alt; i++) {
7230c0d06caSMauro Carvalho Chehab 		u16 tmp =
7240c0d06caSMauro Carvalho Chehab 		    le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc.
7250c0d06caSMauro Carvalho Chehab 				wMaxPacketSize);
7260c0d06caSMauro Carvalho Chehab 		adev->alt_max_pkt_size[i] =
7270c0d06caSMauro Carvalho Chehab 		    (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
728336fea92SMauro Carvalho Chehab 		dev_dbg(dev->dev,
729b7085c08SMauro Carvalho Chehab 			"audio alternate setting %i, max size= %i\n", i,
7300c0d06caSMauro Carvalho Chehab 			adev->alt_max_pkt_size[i]);
7310c0d06caSMauro Carvalho Chehab 	}
7320c0d06caSMauro Carvalho Chehab 
7330c0d06caSMauro Carvalho Chehab 	return 0;
7340c0d06caSMauro Carvalho Chehab }
7350c0d06caSMauro Carvalho Chehab 
7360c0d06caSMauro Carvalho Chehab static int cx231xx_audio_fini(struct cx231xx *dev)
7370c0d06caSMauro Carvalho Chehab {
7380c0d06caSMauro Carvalho Chehab 	if (dev == NULL)
7390c0d06caSMauro Carvalho Chehab 		return 0;
7400c0d06caSMauro Carvalho Chehab 
7410c0d06caSMauro Carvalho Chehab 	if (dev->has_alsa_audio != 1) {
7420c0d06caSMauro Carvalho Chehab 		/* This device does not support the extension (in this case
7430c0d06caSMauro Carvalho Chehab 		   the device is expecting the snd-usb-audio module or
7440c0d06caSMauro Carvalho Chehab 		   doesn't have analog audio support at all) */
7450c0d06caSMauro Carvalho Chehab 		return 0;
7460c0d06caSMauro Carvalho Chehab 	}
7470c0d06caSMauro Carvalho Chehab 
7480c0d06caSMauro Carvalho Chehab 	if (dev->adev.sndcard) {
7490c0d06caSMauro Carvalho Chehab 		snd_card_free(dev->adev.sndcard);
7500c0d06caSMauro Carvalho Chehab 		kfree(dev->adev.alt_max_pkt_size);
7510c0d06caSMauro Carvalho Chehab 		dev->adev.sndcard = NULL;
7520c0d06caSMauro Carvalho Chehab 	}
7530c0d06caSMauro Carvalho Chehab 
7540c0d06caSMauro Carvalho Chehab 	return 0;
7550c0d06caSMauro Carvalho Chehab }
7560c0d06caSMauro Carvalho Chehab 
7570c0d06caSMauro Carvalho Chehab static struct cx231xx_ops audio_ops = {
7580c0d06caSMauro Carvalho Chehab 	.id = CX231XX_AUDIO,
7590c0d06caSMauro Carvalho Chehab 	.name = "Cx231xx Audio Extension",
7600c0d06caSMauro Carvalho Chehab 	.init = cx231xx_audio_init,
7610c0d06caSMauro Carvalho Chehab 	.fini = cx231xx_audio_fini,
7620c0d06caSMauro Carvalho Chehab };
7630c0d06caSMauro Carvalho Chehab 
7640c0d06caSMauro Carvalho Chehab static int __init cx231xx_alsa_register(void)
7650c0d06caSMauro Carvalho Chehab {
7660c0d06caSMauro Carvalho Chehab 	return cx231xx_register_extension(&audio_ops);
7670c0d06caSMauro Carvalho Chehab }
7680c0d06caSMauro Carvalho Chehab 
7690c0d06caSMauro Carvalho Chehab static void __exit cx231xx_alsa_unregister(void)
7700c0d06caSMauro Carvalho Chehab {
7710c0d06caSMauro Carvalho Chehab 	cx231xx_unregister_extension(&audio_ops);
7720c0d06caSMauro Carvalho Chehab }
7730c0d06caSMauro Carvalho Chehab 
7740c0d06caSMauro Carvalho Chehab MODULE_LICENSE("GPL");
7750c0d06caSMauro Carvalho Chehab MODULE_AUTHOR("Srinivasa Deevi <srinivasa.deevi@conexant.com>");
7760c0d06caSMauro Carvalho Chehab MODULE_DESCRIPTION("Cx231xx Audio driver");
7770c0d06caSMauro Carvalho Chehab 
7780c0d06caSMauro Carvalho Chehab module_init(cx231xx_alsa_register);
7790c0d06caSMauro Carvalho Chehab module_exit(cx231xx_alsa_unregister);
780