xref: /linux/drivers/media/usb/cx231xx/cx231xx-audio.c (revision aa53bf0b03ae5e276c2714dc68e375d032bc9110)
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 
19589dadf2SMauro Carvalho Chehab #include "cx231xx.h"
200c0d06caSMauro Carvalho Chehab #include <linux/kernel.h>
210c0d06caSMauro Carvalho Chehab #include <linux/init.h>
220c0d06caSMauro Carvalho Chehab #include <linux/sound.h>
230c0d06caSMauro Carvalho Chehab #include <linux/spinlock.h>
240c0d06caSMauro Carvalho Chehab #include <linux/soundcard.h>
250c0d06caSMauro Carvalho Chehab #include <linux/slab.h>
260c0d06caSMauro Carvalho Chehab #include <linux/vmalloc.h>
270c0d06caSMauro Carvalho Chehab #include <linux/proc_fs.h>
280c0d06caSMauro Carvalho Chehab #include <linux/module.h>
290c0d06caSMauro Carvalho Chehab #include <sound/core.h>
300c0d06caSMauro Carvalho Chehab #include <sound/pcm.h>
310c0d06caSMauro Carvalho Chehab #include <sound/pcm_params.h>
320c0d06caSMauro Carvalho Chehab #include <sound/info.h>
330c0d06caSMauro Carvalho Chehab #include <sound/initval.h>
340c0d06caSMauro Carvalho Chehab #include <sound/control.h>
350c0d06caSMauro Carvalho Chehab #include <media/v4l2-common.h>
360c0d06caSMauro Carvalho Chehab 
370c0d06caSMauro Carvalho Chehab static int debug;
380c0d06caSMauro Carvalho Chehab module_param(debug, int, 0644);
390c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(debug, "activates debug info");
400c0d06caSMauro Carvalho Chehab 
410c0d06caSMauro Carvalho Chehab static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
420c0d06caSMauro Carvalho Chehab 
430c0d06caSMauro Carvalho Chehab static int cx231xx_isoc_audio_deinit(struct cx231xx *dev)
440c0d06caSMauro Carvalho Chehab {
450c0d06caSMauro Carvalho Chehab 	int i;
460c0d06caSMauro Carvalho Chehab 
47336fea92SMauro Carvalho Chehab 	dev_dbg(dev->dev, "Stopping isoc\n");
480c0d06caSMauro Carvalho Chehab 
490c0d06caSMauro Carvalho Chehab 	for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
500c0d06caSMauro Carvalho Chehab 		if (dev->adev.urb[i]) {
510c0d06caSMauro Carvalho Chehab 			if (!irqs_disabled())
520c0d06caSMauro Carvalho Chehab 				usb_kill_urb(dev->adev.urb[i]);
530c0d06caSMauro Carvalho Chehab 			else
540c0d06caSMauro Carvalho Chehab 				usb_unlink_urb(dev->adev.urb[i]);
550c0d06caSMauro Carvalho Chehab 
560c0d06caSMauro Carvalho Chehab 			usb_free_urb(dev->adev.urb[i]);
570c0d06caSMauro Carvalho Chehab 			dev->adev.urb[i] = NULL;
580c0d06caSMauro Carvalho Chehab 
590c0d06caSMauro Carvalho Chehab 			kfree(dev->adev.transfer_buffer[i]);
600c0d06caSMauro Carvalho Chehab 			dev->adev.transfer_buffer[i] = NULL;
610c0d06caSMauro Carvalho Chehab 		}
620c0d06caSMauro Carvalho Chehab 	}
630c0d06caSMauro Carvalho Chehab 
640c0d06caSMauro Carvalho Chehab 	return 0;
650c0d06caSMauro Carvalho Chehab }
660c0d06caSMauro Carvalho Chehab 
670c0d06caSMauro Carvalho Chehab static int cx231xx_bulk_audio_deinit(struct cx231xx *dev)
680c0d06caSMauro Carvalho Chehab {
690c0d06caSMauro Carvalho Chehab 	int i;
700c0d06caSMauro Carvalho Chehab 
71336fea92SMauro Carvalho Chehab 	dev_dbg(dev->dev, "Stopping bulk\n");
720c0d06caSMauro Carvalho Chehab 
730c0d06caSMauro Carvalho Chehab 	for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
740c0d06caSMauro Carvalho Chehab 		if (dev->adev.urb[i]) {
750c0d06caSMauro Carvalho Chehab 			if (!irqs_disabled())
760c0d06caSMauro Carvalho Chehab 				usb_kill_urb(dev->adev.urb[i]);
770c0d06caSMauro Carvalho Chehab 			else
780c0d06caSMauro Carvalho Chehab 				usb_unlink_urb(dev->adev.urb[i]);
790c0d06caSMauro Carvalho Chehab 
800c0d06caSMauro Carvalho Chehab 			usb_free_urb(dev->adev.urb[i]);
810c0d06caSMauro Carvalho Chehab 			dev->adev.urb[i] = NULL;
820c0d06caSMauro Carvalho Chehab 
830c0d06caSMauro Carvalho Chehab 			kfree(dev->adev.transfer_buffer[i]);
840c0d06caSMauro Carvalho Chehab 			dev->adev.transfer_buffer[i] = NULL;
850c0d06caSMauro Carvalho Chehab 		}
860c0d06caSMauro Carvalho Chehab 	}
870c0d06caSMauro Carvalho Chehab 
880c0d06caSMauro Carvalho Chehab 	return 0;
890c0d06caSMauro Carvalho Chehab }
900c0d06caSMauro Carvalho Chehab 
910c0d06caSMauro Carvalho Chehab static void cx231xx_audio_isocirq(struct urb *urb)
920c0d06caSMauro Carvalho Chehab {
930c0d06caSMauro Carvalho Chehab 	struct cx231xx *dev = urb->context;
940c0d06caSMauro Carvalho Chehab 	int i;
950c0d06caSMauro Carvalho Chehab 	unsigned int oldptr;
960c0d06caSMauro Carvalho Chehab 	int period_elapsed = 0;
970c0d06caSMauro Carvalho Chehab 	int status;
980c0d06caSMauro Carvalho Chehab 	unsigned char *cp;
990c0d06caSMauro Carvalho Chehab 	unsigned int stride;
1000c0d06caSMauro Carvalho Chehab 	struct snd_pcm_substream *substream;
1010c0d06caSMauro Carvalho Chehab 	struct snd_pcm_runtime *runtime;
1020c0d06caSMauro Carvalho Chehab 
1030c0d06caSMauro Carvalho Chehab 	if (dev->state & DEV_DISCONNECTED)
1040c0d06caSMauro Carvalho Chehab 		return;
1050c0d06caSMauro Carvalho Chehab 
1060c0d06caSMauro Carvalho Chehab 	switch (urb->status) {
1070c0d06caSMauro Carvalho Chehab 	case 0:		/* success */
1080c0d06caSMauro Carvalho Chehab 	case -ETIMEDOUT:	/* NAK */
1090c0d06caSMauro Carvalho Chehab 		break;
1100c0d06caSMauro Carvalho Chehab 	case -ECONNRESET:	/* kill */
1110c0d06caSMauro Carvalho Chehab 	case -ENOENT:
1120c0d06caSMauro Carvalho Chehab 	case -ESHUTDOWN:
1130c0d06caSMauro Carvalho Chehab 		return;
1140c0d06caSMauro Carvalho Chehab 	default:		/* error */
115336fea92SMauro Carvalho Chehab 		dev_dbg(dev->dev, "urb completition error %d.\n",
11656d8a3b0SMauro Carvalho Chehab 			urb->status);
1170c0d06caSMauro Carvalho Chehab 		break;
1180c0d06caSMauro Carvalho Chehab 	}
1190c0d06caSMauro Carvalho Chehab 
1200c0d06caSMauro Carvalho Chehab 	if (atomic_read(&dev->stream_started) == 0)
1210c0d06caSMauro Carvalho Chehab 		return;
1220c0d06caSMauro Carvalho Chehab 
1230c0d06caSMauro Carvalho Chehab 	if (dev->adev.capture_pcm_substream) {
1240c0d06caSMauro Carvalho Chehab 		substream = dev->adev.capture_pcm_substream;
1250c0d06caSMauro Carvalho Chehab 		runtime = substream->runtime;
1260c0d06caSMauro Carvalho Chehab 		stride = runtime->frame_bits >> 3;
1270c0d06caSMauro Carvalho Chehab 
1280c0d06caSMauro Carvalho Chehab 		for (i = 0; i < urb->number_of_packets; i++) {
129*aa53bf0bSSebastian Andrzej Siewior 			unsigned long flags;
1300c0d06caSMauro Carvalho Chehab 			int length = urb->iso_frame_desc[i].actual_length /
1310c0d06caSMauro Carvalho Chehab 				     stride;
1320c0d06caSMauro Carvalho Chehab 			cp = (unsigned char *)urb->transfer_buffer +
1330c0d06caSMauro Carvalho Chehab 					      urb->iso_frame_desc[i].offset;
1340c0d06caSMauro Carvalho Chehab 
1350c0d06caSMauro Carvalho Chehab 			if (!length)
1360c0d06caSMauro Carvalho Chehab 				continue;
1370c0d06caSMauro Carvalho Chehab 
1380c0d06caSMauro Carvalho Chehab 			oldptr = dev->adev.hwptr_done_capture;
1390c0d06caSMauro Carvalho Chehab 			if (oldptr + length >= runtime->buffer_size) {
1400c0d06caSMauro Carvalho Chehab 				unsigned int cnt;
1410c0d06caSMauro Carvalho Chehab 
1420c0d06caSMauro Carvalho Chehab 				cnt = runtime->buffer_size - oldptr;
1430c0d06caSMauro Carvalho Chehab 				memcpy(runtime->dma_area + oldptr * stride, cp,
1440c0d06caSMauro Carvalho Chehab 				       cnt * stride);
1450c0d06caSMauro Carvalho Chehab 				memcpy(runtime->dma_area, cp + cnt * stride,
1460c0d06caSMauro Carvalho Chehab 				       length * stride - cnt * stride);
1470c0d06caSMauro Carvalho Chehab 			} else {
1480c0d06caSMauro Carvalho Chehab 				memcpy(runtime->dma_area + oldptr * stride, cp,
1490c0d06caSMauro Carvalho Chehab 				       length * stride);
1500c0d06caSMauro Carvalho Chehab 			}
1510c0d06caSMauro Carvalho Chehab 
152*aa53bf0bSSebastian Andrzej Siewior 			snd_pcm_stream_lock_irqsave(substream, flags);
1530c0d06caSMauro Carvalho Chehab 
1540c0d06caSMauro Carvalho Chehab 			dev->adev.hwptr_done_capture += length;
1550c0d06caSMauro Carvalho Chehab 			if (dev->adev.hwptr_done_capture >=
1560c0d06caSMauro Carvalho Chehab 						runtime->buffer_size)
1570c0d06caSMauro Carvalho Chehab 				dev->adev.hwptr_done_capture -=
1580c0d06caSMauro Carvalho Chehab 						runtime->buffer_size;
1590c0d06caSMauro Carvalho Chehab 
1600c0d06caSMauro Carvalho Chehab 			dev->adev.capture_transfer_done += length;
1610c0d06caSMauro Carvalho Chehab 			if (dev->adev.capture_transfer_done >=
1620c0d06caSMauro Carvalho Chehab 				runtime->period_size) {
1630c0d06caSMauro Carvalho Chehab 				dev->adev.capture_transfer_done -=
1640c0d06caSMauro Carvalho Chehab 						runtime->period_size;
1650c0d06caSMauro Carvalho Chehab 				period_elapsed = 1;
1660c0d06caSMauro Carvalho Chehab 			}
167*aa53bf0bSSebastian Andrzej Siewior 			snd_pcm_stream_unlock_irqrestore(substream, flags);
1680c0d06caSMauro Carvalho Chehab 		}
1690c0d06caSMauro Carvalho Chehab 		if (period_elapsed)
1700c0d06caSMauro Carvalho Chehab 			snd_pcm_period_elapsed(substream);
1710c0d06caSMauro Carvalho Chehab 	}
1720c0d06caSMauro Carvalho Chehab 	urb->status = 0;
1730c0d06caSMauro Carvalho Chehab 
1740c0d06caSMauro Carvalho Chehab 	status = usb_submit_urb(urb, GFP_ATOMIC);
1750c0d06caSMauro Carvalho Chehab 	if (status < 0) {
176336fea92SMauro Carvalho Chehab 		dev_err(dev->dev,
177b7085c08SMauro Carvalho Chehab 			"resubmit of audio urb failed (error=%i)\n",
1780c0d06caSMauro Carvalho Chehab 			status);
1790c0d06caSMauro Carvalho Chehab 	}
1800c0d06caSMauro Carvalho Chehab 	return;
1810c0d06caSMauro Carvalho Chehab }
1820c0d06caSMauro Carvalho Chehab 
1830c0d06caSMauro Carvalho Chehab static void cx231xx_audio_bulkirq(struct urb *urb)
1840c0d06caSMauro Carvalho Chehab {
1850c0d06caSMauro Carvalho Chehab 	struct cx231xx *dev = urb->context;
1860c0d06caSMauro Carvalho Chehab 	unsigned int oldptr;
1870c0d06caSMauro Carvalho Chehab 	int period_elapsed = 0;
1880c0d06caSMauro Carvalho Chehab 	int status;
1890c0d06caSMauro Carvalho Chehab 	unsigned char *cp;
1900c0d06caSMauro Carvalho Chehab 	unsigned int stride;
1910c0d06caSMauro Carvalho Chehab 	struct snd_pcm_substream *substream;
1920c0d06caSMauro Carvalho Chehab 	struct snd_pcm_runtime *runtime;
1930c0d06caSMauro Carvalho Chehab 
1940c0d06caSMauro Carvalho Chehab 	if (dev->state & DEV_DISCONNECTED)
1950c0d06caSMauro Carvalho Chehab 		return;
1960c0d06caSMauro Carvalho Chehab 
1970c0d06caSMauro Carvalho Chehab 	switch (urb->status) {
1980c0d06caSMauro Carvalho Chehab 	case 0:		/* success */
1990c0d06caSMauro Carvalho Chehab 	case -ETIMEDOUT:	/* NAK */
2000c0d06caSMauro Carvalho Chehab 		break;
2010c0d06caSMauro Carvalho Chehab 	case -ECONNRESET:	/* kill */
2020c0d06caSMauro Carvalho Chehab 	case -ENOENT:
2030c0d06caSMauro Carvalho Chehab 	case -ESHUTDOWN:
2040c0d06caSMauro Carvalho Chehab 		return;
2050c0d06caSMauro Carvalho Chehab 	default:		/* error */
206336fea92SMauro Carvalho Chehab 		dev_dbg(dev->dev, "urb completition error %d.\n",
20756d8a3b0SMauro Carvalho Chehab 			urb->status);
2080c0d06caSMauro Carvalho Chehab 		break;
2090c0d06caSMauro Carvalho Chehab 	}
2100c0d06caSMauro Carvalho Chehab 
2110c0d06caSMauro Carvalho Chehab 	if (atomic_read(&dev->stream_started) == 0)
2120c0d06caSMauro Carvalho Chehab 		return;
2130c0d06caSMauro Carvalho Chehab 
2140c0d06caSMauro Carvalho Chehab 	if (dev->adev.capture_pcm_substream) {
2150c0d06caSMauro Carvalho Chehab 		substream = dev->adev.capture_pcm_substream;
2160c0d06caSMauro Carvalho Chehab 		runtime = substream->runtime;
2170c0d06caSMauro Carvalho Chehab 		stride = runtime->frame_bits >> 3;
2180c0d06caSMauro Carvalho Chehab 
2190c0d06caSMauro Carvalho Chehab 		if (1) {
220*aa53bf0bSSebastian Andrzej Siewior 			unsigned long flags;
2210c0d06caSMauro Carvalho Chehab 			int length = urb->actual_length /
2220c0d06caSMauro Carvalho Chehab 				     stride;
2230c0d06caSMauro Carvalho Chehab 			cp = (unsigned char *)urb->transfer_buffer;
2240c0d06caSMauro Carvalho Chehab 
2250c0d06caSMauro Carvalho Chehab 			oldptr = dev->adev.hwptr_done_capture;
2260c0d06caSMauro Carvalho Chehab 			if (oldptr + length >= runtime->buffer_size) {
2270c0d06caSMauro Carvalho Chehab 				unsigned int cnt;
2280c0d06caSMauro Carvalho Chehab 
2290c0d06caSMauro Carvalho Chehab 				cnt = runtime->buffer_size - oldptr;
2300c0d06caSMauro Carvalho Chehab 				memcpy(runtime->dma_area + oldptr * stride, cp,
2310c0d06caSMauro Carvalho Chehab 				       cnt * stride);
2320c0d06caSMauro Carvalho Chehab 				memcpy(runtime->dma_area, cp + cnt * stride,
2330c0d06caSMauro Carvalho Chehab 				       length * stride - cnt * stride);
2340c0d06caSMauro Carvalho Chehab 			} else {
2350c0d06caSMauro Carvalho Chehab 				memcpy(runtime->dma_area + oldptr * stride, cp,
2360c0d06caSMauro Carvalho Chehab 				       length * stride);
2370c0d06caSMauro Carvalho Chehab 			}
2380c0d06caSMauro Carvalho Chehab 
239*aa53bf0bSSebastian Andrzej Siewior 			snd_pcm_stream_lock_irqsave(substream, flags);
2400c0d06caSMauro Carvalho Chehab 
2410c0d06caSMauro Carvalho Chehab 			dev->adev.hwptr_done_capture += length;
2420c0d06caSMauro Carvalho Chehab 			if (dev->adev.hwptr_done_capture >=
2430c0d06caSMauro Carvalho Chehab 						runtime->buffer_size)
2440c0d06caSMauro Carvalho Chehab 				dev->adev.hwptr_done_capture -=
2450c0d06caSMauro Carvalho Chehab 						runtime->buffer_size;
2460c0d06caSMauro Carvalho Chehab 
2470c0d06caSMauro Carvalho Chehab 			dev->adev.capture_transfer_done += length;
2480c0d06caSMauro Carvalho Chehab 			if (dev->adev.capture_transfer_done >=
2490c0d06caSMauro Carvalho Chehab 				runtime->period_size) {
2500c0d06caSMauro Carvalho Chehab 				dev->adev.capture_transfer_done -=
2510c0d06caSMauro Carvalho Chehab 						runtime->period_size;
2520c0d06caSMauro Carvalho Chehab 				period_elapsed = 1;
2530c0d06caSMauro Carvalho Chehab 			}
254*aa53bf0bSSebastian Andrzej Siewior 			snd_pcm_stream_unlock_irqrestore(substream, flags);
2550c0d06caSMauro Carvalho Chehab 		}
2560c0d06caSMauro Carvalho Chehab 		if (period_elapsed)
2570c0d06caSMauro Carvalho Chehab 			snd_pcm_period_elapsed(substream);
2580c0d06caSMauro Carvalho Chehab 	}
2590c0d06caSMauro Carvalho Chehab 	urb->status = 0;
2600c0d06caSMauro Carvalho Chehab 
2610c0d06caSMauro Carvalho Chehab 	status = usb_submit_urb(urb, GFP_ATOMIC);
2620c0d06caSMauro Carvalho Chehab 	if (status < 0) {
263336fea92SMauro Carvalho Chehab 		dev_err(dev->dev,
264b7085c08SMauro Carvalho Chehab 			"resubmit of audio urb failed (error=%i)\n",
2650c0d06caSMauro Carvalho Chehab 			status);
2660c0d06caSMauro Carvalho Chehab 	}
2670c0d06caSMauro Carvalho Chehab 	return;
2680c0d06caSMauro Carvalho Chehab }
2690c0d06caSMauro Carvalho Chehab 
2700c0d06caSMauro Carvalho Chehab static int cx231xx_init_audio_isoc(struct cx231xx *dev)
2710c0d06caSMauro Carvalho Chehab {
2720c0d06caSMauro Carvalho Chehab 	int i, errCode;
2730c0d06caSMauro Carvalho Chehab 	int sb_size;
2740c0d06caSMauro Carvalho Chehab 
275336fea92SMauro Carvalho Chehab 	dev_dbg(dev->dev,
276b7085c08SMauro Carvalho Chehab 		"%s: Starting ISO AUDIO transfers\n", __func__);
2770c0d06caSMauro Carvalho Chehab 
2780c0d06caSMauro Carvalho Chehab 	if (dev->state & DEV_DISCONNECTED)
2790c0d06caSMauro Carvalho Chehab 		return -ENODEV;
2800c0d06caSMauro Carvalho Chehab 
2810c0d06caSMauro Carvalho Chehab 	sb_size = CX231XX_ISO_NUM_AUDIO_PACKETS * dev->adev.max_pkt_size;
2820c0d06caSMauro Carvalho Chehab 
2830c0d06caSMauro Carvalho Chehab 	for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
2840c0d06caSMauro Carvalho Chehab 		struct urb *urb;
2850c0d06caSMauro Carvalho Chehab 		int j, k;
2860c0d06caSMauro Carvalho Chehab 
2870c0d06caSMauro Carvalho Chehab 		dev->adev.transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC);
2880c0d06caSMauro Carvalho Chehab 		if (!dev->adev.transfer_buffer[i])
2890c0d06caSMauro Carvalho Chehab 			return -ENOMEM;
2900c0d06caSMauro Carvalho Chehab 
2910c0d06caSMauro Carvalho Chehab 		memset(dev->adev.transfer_buffer[i], 0x80, sb_size);
2920c0d06caSMauro Carvalho Chehab 		urb = usb_alloc_urb(CX231XX_ISO_NUM_AUDIO_PACKETS, GFP_ATOMIC);
2930c0d06caSMauro Carvalho Chehab 		if (!urb) {
2940c0d06caSMauro Carvalho Chehab 			for (j = 0; j < i; j++) {
2950c0d06caSMauro Carvalho Chehab 				usb_free_urb(dev->adev.urb[j]);
2960c0d06caSMauro Carvalho Chehab 				kfree(dev->adev.transfer_buffer[j]);
2970c0d06caSMauro Carvalho Chehab 			}
2980c0d06caSMauro Carvalho Chehab 			return -ENOMEM;
2990c0d06caSMauro Carvalho Chehab 		}
3000c0d06caSMauro Carvalho Chehab 
3010c0d06caSMauro Carvalho Chehab 		urb->dev = dev->udev;
3020c0d06caSMauro Carvalho Chehab 		urb->context = dev;
3030c0d06caSMauro Carvalho Chehab 		urb->pipe = usb_rcvisocpipe(dev->udev,
3040c0d06caSMauro Carvalho Chehab 						dev->adev.end_point_addr);
3050c0d06caSMauro Carvalho Chehab 		urb->transfer_flags = URB_ISO_ASAP;
3060c0d06caSMauro Carvalho Chehab 		urb->transfer_buffer = dev->adev.transfer_buffer[i];
3070c0d06caSMauro Carvalho Chehab 		urb->interval = 1;
3080c0d06caSMauro Carvalho Chehab 		urb->complete = cx231xx_audio_isocirq;
3090c0d06caSMauro Carvalho Chehab 		urb->number_of_packets = CX231XX_ISO_NUM_AUDIO_PACKETS;
3100c0d06caSMauro Carvalho Chehab 		urb->transfer_buffer_length = sb_size;
3110c0d06caSMauro Carvalho Chehab 
3120c0d06caSMauro Carvalho Chehab 		for (j = k = 0; j < CX231XX_ISO_NUM_AUDIO_PACKETS;
3130c0d06caSMauro Carvalho Chehab 			j++, k += dev->adev.max_pkt_size) {
3140c0d06caSMauro Carvalho Chehab 			urb->iso_frame_desc[j].offset = k;
3150c0d06caSMauro Carvalho Chehab 			urb->iso_frame_desc[j].length = dev->adev.max_pkt_size;
3160c0d06caSMauro Carvalho Chehab 		}
3170c0d06caSMauro Carvalho Chehab 		dev->adev.urb[i] = urb;
3180c0d06caSMauro Carvalho Chehab 	}
3190c0d06caSMauro Carvalho Chehab 
3200c0d06caSMauro Carvalho Chehab 	for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
3210c0d06caSMauro Carvalho Chehab 		errCode = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC);
3220c0d06caSMauro Carvalho Chehab 		if (errCode < 0) {
3230c0d06caSMauro Carvalho Chehab 			cx231xx_isoc_audio_deinit(dev);
3240c0d06caSMauro Carvalho Chehab 			return errCode;
3250c0d06caSMauro Carvalho Chehab 		}
3260c0d06caSMauro Carvalho Chehab 	}
3270c0d06caSMauro Carvalho Chehab 
3280c0d06caSMauro Carvalho Chehab 	return errCode;
3290c0d06caSMauro Carvalho Chehab }
3300c0d06caSMauro Carvalho Chehab 
3310c0d06caSMauro Carvalho Chehab static int cx231xx_init_audio_bulk(struct cx231xx *dev)
3320c0d06caSMauro Carvalho Chehab {
3330c0d06caSMauro Carvalho Chehab 	int i, errCode;
3340c0d06caSMauro Carvalho Chehab 	int sb_size;
3350c0d06caSMauro Carvalho Chehab 
336336fea92SMauro Carvalho Chehab 	dev_dbg(dev->dev,
337b7085c08SMauro Carvalho Chehab 		"%s: Starting BULK AUDIO transfers\n", __func__);
3380c0d06caSMauro Carvalho Chehab 
3390c0d06caSMauro Carvalho Chehab 	if (dev->state & DEV_DISCONNECTED)
3400c0d06caSMauro Carvalho Chehab 		return -ENODEV;
3410c0d06caSMauro Carvalho Chehab 
3420c0d06caSMauro Carvalho Chehab 	sb_size = CX231XX_NUM_AUDIO_PACKETS * dev->adev.max_pkt_size;
3430c0d06caSMauro Carvalho Chehab 
3440c0d06caSMauro Carvalho Chehab 	for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
3450c0d06caSMauro Carvalho Chehab 		struct urb *urb;
3460c0d06caSMauro Carvalho Chehab 		int j;
3470c0d06caSMauro Carvalho Chehab 
3480c0d06caSMauro Carvalho Chehab 		dev->adev.transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC);
3490c0d06caSMauro Carvalho Chehab 		if (!dev->adev.transfer_buffer[i])
3500c0d06caSMauro Carvalho Chehab 			return -ENOMEM;
3510c0d06caSMauro Carvalho Chehab 
3520c0d06caSMauro Carvalho Chehab 		memset(dev->adev.transfer_buffer[i], 0x80, sb_size);
3530c0d06caSMauro Carvalho Chehab 		urb = usb_alloc_urb(CX231XX_NUM_AUDIO_PACKETS, GFP_ATOMIC);
3540c0d06caSMauro Carvalho Chehab 		if (!urb) {
3550c0d06caSMauro Carvalho Chehab 			for (j = 0; j < i; j++) {
3560c0d06caSMauro Carvalho Chehab 				usb_free_urb(dev->adev.urb[j]);
3570c0d06caSMauro Carvalho Chehab 				kfree(dev->adev.transfer_buffer[j]);
3580c0d06caSMauro Carvalho Chehab 			}
3590c0d06caSMauro Carvalho Chehab 			return -ENOMEM;
3600c0d06caSMauro Carvalho Chehab 		}
3610c0d06caSMauro Carvalho Chehab 
3620c0d06caSMauro Carvalho Chehab 		urb->dev = dev->udev;
3630c0d06caSMauro Carvalho Chehab 		urb->context = dev;
3640c0d06caSMauro Carvalho Chehab 		urb->pipe = usb_rcvbulkpipe(dev->udev,
3650c0d06caSMauro Carvalho Chehab 						dev->adev.end_point_addr);
3660c0d06caSMauro Carvalho Chehab 		urb->transfer_flags = 0;
3670c0d06caSMauro Carvalho Chehab 		urb->transfer_buffer = dev->adev.transfer_buffer[i];
3680c0d06caSMauro Carvalho Chehab 		urb->complete = cx231xx_audio_bulkirq;
3690c0d06caSMauro Carvalho Chehab 		urb->transfer_buffer_length = sb_size;
3700c0d06caSMauro Carvalho Chehab 
3710c0d06caSMauro Carvalho Chehab 		dev->adev.urb[i] = urb;
3720c0d06caSMauro Carvalho Chehab 
3730c0d06caSMauro Carvalho Chehab 	}
3740c0d06caSMauro Carvalho Chehab 
3750c0d06caSMauro Carvalho Chehab 	for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
3760c0d06caSMauro Carvalho Chehab 		errCode = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC);
3770c0d06caSMauro Carvalho Chehab 		if (errCode < 0) {
3780c0d06caSMauro Carvalho Chehab 			cx231xx_bulk_audio_deinit(dev);
3790c0d06caSMauro Carvalho Chehab 			return errCode;
3800c0d06caSMauro Carvalho Chehab 		}
3810c0d06caSMauro Carvalho Chehab 	}
3820c0d06caSMauro Carvalho Chehab 
3830c0d06caSMauro Carvalho Chehab 	return errCode;
3840c0d06caSMauro Carvalho Chehab }
3850c0d06caSMauro Carvalho Chehab 
3860c0d06caSMauro Carvalho Chehab static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs,
3870c0d06caSMauro Carvalho Chehab 					size_t size)
3880c0d06caSMauro Carvalho Chehab {
3890c0d06caSMauro Carvalho Chehab 	struct snd_pcm_runtime *runtime = subs->runtime;
39056d8a3b0SMauro Carvalho Chehab 	struct cx231xx *dev = snd_pcm_substream_chip(subs);
3910c0d06caSMauro Carvalho Chehab 
392336fea92SMauro Carvalho Chehab 	dev_dbg(dev->dev, "Allocating vbuffer\n");
3930c0d06caSMauro Carvalho Chehab 	if (runtime->dma_area) {
3940c0d06caSMauro Carvalho Chehab 		if (runtime->dma_bytes > size)
3950c0d06caSMauro Carvalho Chehab 			return 0;
3960c0d06caSMauro Carvalho Chehab 
3970c0d06caSMauro Carvalho Chehab 		vfree(runtime->dma_area);
3980c0d06caSMauro Carvalho Chehab 	}
3990c0d06caSMauro Carvalho Chehab 	runtime->dma_area = vmalloc(size);
4000c0d06caSMauro Carvalho Chehab 	if (!runtime->dma_area)
4010c0d06caSMauro Carvalho Chehab 		return -ENOMEM;
4020c0d06caSMauro Carvalho Chehab 
4030c0d06caSMauro Carvalho Chehab 	runtime->dma_bytes = size;
4040c0d06caSMauro Carvalho Chehab 
4050c0d06caSMauro Carvalho Chehab 	return 0;
4060c0d06caSMauro Carvalho Chehab }
4070c0d06caSMauro Carvalho Chehab 
40838c4f03aSBhumika Goyal static const struct snd_pcm_hardware snd_cx231xx_hw_capture = {
4090c0d06caSMauro Carvalho Chehab 	.info = SNDRV_PCM_INFO_BLOCK_TRANSFER	|
4100c0d06caSMauro Carvalho Chehab 	    SNDRV_PCM_INFO_MMAP			|
4110c0d06caSMauro Carvalho Chehab 	    SNDRV_PCM_INFO_INTERLEAVED		|
4120c0d06caSMauro Carvalho Chehab 	    SNDRV_PCM_INFO_MMAP_VALID,
4130c0d06caSMauro Carvalho Chehab 
4140c0d06caSMauro Carvalho Chehab 	.formats = SNDRV_PCM_FMTBIT_S16_LE,
4150c0d06caSMauro Carvalho Chehab 
4160c0d06caSMauro Carvalho Chehab 	.rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT,
4170c0d06caSMauro Carvalho Chehab 
4180c0d06caSMauro Carvalho Chehab 	.rate_min = 48000,
4190c0d06caSMauro Carvalho Chehab 	.rate_max = 48000,
4200c0d06caSMauro Carvalho Chehab 	.channels_min = 2,
4210c0d06caSMauro Carvalho Chehab 	.channels_max = 2,
4220c0d06caSMauro Carvalho Chehab 	.buffer_bytes_max = 62720 * 8,	/* just about the value in usbaudio.c */
4230c0d06caSMauro Carvalho Chehab 	.period_bytes_min = 64,		/* 12544/2, */
4240c0d06caSMauro Carvalho Chehab 	.period_bytes_max = 12544,
4250c0d06caSMauro Carvalho Chehab 	.periods_min = 2,
4260c0d06caSMauro Carvalho Chehab 	.periods_max = 98,		/* 12544, */
4270c0d06caSMauro Carvalho Chehab };
4280c0d06caSMauro Carvalho Chehab 
4290c0d06caSMauro Carvalho Chehab static int snd_cx231xx_capture_open(struct snd_pcm_substream *substream)
4300c0d06caSMauro Carvalho Chehab {
4310c0d06caSMauro Carvalho Chehab 	struct cx231xx *dev = snd_pcm_substream_chip(substream);
4320c0d06caSMauro Carvalho Chehab 	struct snd_pcm_runtime *runtime = substream->runtime;
4330c0d06caSMauro Carvalho Chehab 	int ret = 0;
4340c0d06caSMauro Carvalho Chehab 
435336fea92SMauro Carvalho Chehab 	dev_dbg(dev->dev,
43656d8a3b0SMauro Carvalho Chehab 		"opening device and trying to acquire exclusive lock\n");
4370c0d06caSMauro Carvalho Chehab 
4380c0d06caSMauro Carvalho Chehab 	if (dev->state & DEV_DISCONNECTED) {
439336fea92SMauro Carvalho Chehab 		dev_err(dev->dev,
440b7085c08SMauro Carvalho Chehab 			"Can't open. the device was removed.\n");
4410c0d06caSMauro Carvalho Chehab 		return -ENODEV;
4420c0d06caSMauro Carvalho Chehab 	}
4430c0d06caSMauro Carvalho Chehab 
4440c0d06caSMauro Carvalho Chehab 	/* set alternate setting for audio interface */
4450c0d06caSMauro Carvalho Chehab 	/* 1 - 48000 samples per sec */
4460c0d06caSMauro Carvalho Chehab 	mutex_lock(&dev->lock);
4470c0d06caSMauro Carvalho Chehab 	if (dev->USE_ISO)
4480c0d06caSMauro Carvalho Chehab 		ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 1);
4490c0d06caSMauro Carvalho Chehab 	else
4500c0d06caSMauro Carvalho Chehab 		ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 0);
4510c0d06caSMauro Carvalho Chehab 	mutex_unlock(&dev->lock);
4520c0d06caSMauro Carvalho Chehab 	if (ret < 0) {
453336fea92SMauro Carvalho Chehab 		dev_err(dev->dev,
454b7085c08SMauro Carvalho Chehab 			"failed to set alternate setting !\n");
4550c0d06caSMauro Carvalho Chehab 
4560c0d06caSMauro Carvalho Chehab 		return ret;
4570c0d06caSMauro Carvalho Chehab 	}
4580c0d06caSMauro Carvalho Chehab 
4590c0d06caSMauro Carvalho Chehab 	runtime->hw = snd_cx231xx_hw_capture;
4600c0d06caSMauro Carvalho Chehab 
4610c0d06caSMauro Carvalho Chehab 	mutex_lock(&dev->lock);
4620c0d06caSMauro Carvalho Chehab 	/* inform hardware to start streaming */
4630c0d06caSMauro Carvalho Chehab 	ret = cx231xx_capture_start(dev, 1, Audio);
4640c0d06caSMauro Carvalho Chehab 
4650c0d06caSMauro Carvalho Chehab 	dev->adev.users++;
4660c0d06caSMauro Carvalho Chehab 	mutex_unlock(&dev->lock);
4670c0d06caSMauro Carvalho Chehab 
4680c0d06caSMauro Carvalho Chehab 	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
4690c0d06caSMauro Carvalho Chehab 	dev->adev.capture_pcm_substream = substream;
4700c0d06caSMauro Carvalho Chehab 	runtime->private_data = dev;
4710c0d06caSMauro Carvalho Chehab 
4720c0d06caSMauro Carvalho Chehab 	return 0;
4730c0d06caSMauro Carvalho Chehab }
4740c0d06caSMauro Carvalho Chehab 
4750c0d06caSMauro Carvalho Chehab static int snd_cx231xx_pcm_close(struct snd_pcm_substream *substream)
4760c0d06caSMauro Carvalho Chehab {
4770c0d06caSMauro Carvalho Chehab 	int ret;
4780c0d06caSMauro Carvalho Chehab 	struct cx231xx *dev = snd_pcm_substream_chip(substream);
4790c0d06caSMauro Carvalho Chehab 
480336fea92SMauro Carvalho Chehab 	dev_dbg(dev->dev, "closing device\n");
4810c0d06caSMauro Carvalho Chehab 
4820c0d06caSMauro Carvalho Chehab 	/* inform hardware to stop streaming */
4830c0d06caSMauro Carvalho Chehab 	mutex_lock(&dev->lock);
4840c0d06caSMauro Carvalho Chehab 	ret = cx231xx_capture_start(dev, 0, Audio);
4850c0d06caSMauro Carvalho Chehab 
4860c0d06caSMauro Carvalho Chehab 	/* set alternate setting for audio interface */
4870c0d06caSMauro Carvalho Chehab 	/* 1 - 48000 samples per sec */
4880c0d06caSMauro Carvalho Chehab 	ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 0);
4890c0d06caSMauro Carvalho Chehab 	if (ret < 0) {
490336fea92SMauro Carvalho Chehab 		dev_err(dev->dev,
491b7085c08SMauro Carvalho Chehab 			"failed to set alternate setting !\n");
4920c0d06caSMauro Carvalho Chehab 
4930c0d06caSMauro Carvalho Chehab 		mutex_unlock(&dev->lock);
4940c0d06caSMauro Carvalho Chehab 		return ret;
4950c0d06caSMauro Carvalho Chehab 	}
4960c0d06caSMauro Carvalho Chehab 
4970c0d06caSMauro Carvalho Chehab 	dev->adev.users--;
4989dd73448SJean-Baptiste Theou 	if (substream->runtime->dma_area) {
4999dd73448SJean-Baptiste Theou 		dev_dbg(dev->dev, "freeing\n");
5009dd73448SJean-Baptiste Theou 		vfree(substream->runtime->dma_area);
5019dd73448SJean-Baptiste Theou 		substream->runtime->dma_area = NULL;
5029dd73448SJean-Baptiste Theou 	}
5030c0d06caSMauro Carvalho Chehab 	mutex_unlock(&dev->lock);
5040c0d06caSMauro Carvalho Chehab 
5050c0d06caSMauro Carvalho Chehab 	if (dev->adev.users == 0 && dev->adev.shutdown == 1) {
506336fea92SMauro Carvalho Chehab 		dev_dbg(dev->dev, "audio users: %d\n", dev->adev.users);
507336fea92SMauro Carvalho Chehab 		dev_dbg(dev->dev, "disabling audio stream!\n");
5080c0d06caSMauro Carvalho Chehab 		dev->adev.shutdown = 0;
509336fea92SMauro Carvalho Chehab 		dev_dbg(dev->dev, "released lock\n");
5100c0d06caSMauro Carvalho Chehab 		if (atomic_read(&dev->stream_started) > 0) {
5110c0d06caSMauro Carvalho Chehab 			atomic_set(&dev->stream_started, 0);
5120c0d06caSMauro Carvalho Chehab 			schedule_work(&dev->wq_trigger);
5130c0d06caSMauro Carvalho Chehab 		}
5140c0d06caSMauro Carvalho Chehab 	}
5150c0d06caSMauro Carvalho Chehab 	return 0;
5160c0d06caSMauro Carvalho Chehab }
5170c0d06caSMauro Carvalho Chehab 
5180c0d06caSMauro Carvalho Chehab static int snd_cx231xx_hw_capture_params(struct snd_pcm_substream *substream,
5190c0d06caSMauro Carvalho Chehab 					 struct snd_pcm_hw_params *hw_params)
5200c0d06caSMauro Carvalho Chehab {
52156d8a3b0SMauro Carvalho Chehab 	struct cx231xx *dev = snd_pcm_substream_chip(substream);
5220c0d06caSMauro Carvalho Chehab 	int ret;
5230c0d06caSMauro Carvalho Chehab 
524336fea92SMauro Carvalho Chehab 	dev_dbg(dev->dev, "Setting capture parameters\n");
5250c0d06caSMauro Carvalho Chehab 
5260c0d06caSMauro Carvalho Chehab 	ret = snd_pcm_alloc_vmalloc_buffer(substream,
5270c0d06caSMauro Carvalho Chehab 					   params_buffer_bytes(hw_params));
5280c0d06caSMauro Carvalho Chehab #if 0
5290c0d06caSMauro Carvalho Chehab 	/* TODO: set up cx231xx audio chip to deliver the correct audio format,
5300c0d06caSMauro Carvalho Chehab 	   current default is 48000hz multiplexed => 96000hz mono
5310c0d06caSMauro Carvalho Chehab 	   which shouldn't matter since analogue TV only supports mono */
5320c0d06caSMauro Carvalho Chehab 	unsigned int channels, rate, format;
5330c0d06caSMauro Carvalho Chehab 
5340c0d06caSMauro Carvalho Chehab 	format = params_format(hw_params);
5350c0d06caSMauro Carvalho Chehab 	rate = params_rate(hw_params);
5360c0d06caSMauro Carvalho Chehab 	channels = params_channels(hw_params);
5370c0d06caSMauro Carvalho Chehab #endif
5380c0d06caSMauro Carvalho Chehab 
5390c0d06caSMauro Carvalho Chehab 	return ret;
5400c0d06caSMauro Carvalho Chehab }
5410c0d06caSMauro Carvalho Chehab 
5420c0d06caSMauro Carvalho Chehab static int snd_cx231xx_hw_capture_free(struct snd_pcm_substream *substream)
5430c0d06caSMauro Carvalho Chehab {
5440c0d06caSMauro Carvalho Chehab 	struct cx231xx *dev = snd_pcm_substream_chip(substream);
5450c0d06caSMauro Carvalho Chehab 
546336fea92SMauro Carvalho Chehab 	dev_dbg(dev->dev, "Stop capture, if needed\n");
5470c0d06caSMauro Carvalho Chehab 
5480c0d06caSMauro Carvalho Chehab 	if (atomic_read(&dev->stream_started) > 0) {
5490c0d06caSMauro Carvalho Chehab 		atomic_set(&dev->stream_started, 0);
5500c0d06caSMauro Carvalho Chehab 		schedule_work(&dev->wq_trigger);
5510c0d06caSMauro Carvalho Chehab 	}
5520c0d06caSMauro Carvalho Chehab 
5530c0d06caSMauro Carvalho Chehab 	return 0;
5540c0d06caSMauro Carvalho Chehab }
5550c0d06caSMauro Carvalho Chehab 
5560c0d06caSMauro Carvalho Chehab static int snd_cx231xx_prepare(struct snd_pcm_substream *substream)
5570c0d06caSMauro Carvalho Chehab {
5580c0d06caSMauro Carvalho Chehab 	struct cx231xx *dev = snd_pcm_substream_chip(substream);
5590c0d06caSMauro Carvalho Chehab 
5600c0d06caSMauro Carvalho Chehab 	dev->adev.hwptr_done_capture = 0;
5610c0d06caSMauro Carvalho Chehab 	dev->adev.capture_transfer_done = 0;
5620c0d06caSMauro Carvalho Chehab 
5630c0d06caSMauro Carvalho Chehab 	return 0;
5640c0d06caSMauro Carvalho Chehab }
5650c0d06caSMauro Carvalho Chehab 
5660c0d06caSMauro Carvalho Chehab static void audio_trigger(struct work_struct *work)
5670c0d06caSMauro Carvalho Chehab {
5680c0d06caSMauro Carvalho Chehab 	struct cx231xx *dev = container_of(work, struct cx231xx, wq_trigger);
5690c0d06caSMauro Carvalho Chehab 
5700c0d06caSMauro Carvalho Chehab 	if (atomic_read(&dev->stream_started)) {
571336fea92SMauro Carvalho Chehab 		dev_dbg(dev->dev, "starting capture");
5720c0d06caSMauro Carvalho Chehab 		if (is_fw_load(dev) == 0)
5730c0d06caSMauro Carvalho Chehab 			cx25840_call(dev, core, load_fw);
5740c0d06caSMauro Carvalho Chehab 		if (dev->USE_ISO)
5750c0d06caSMauro Carvalho Chehab 			cx231xx_init_audio_isoc(dev);
5760c0d06caSMauro Carvalho Chehab 		else
5770c0d06caSMauro Carvalho Chehab 			cx231xx_init_audio_bulk(dev);
5780c0d06caSMauro Carvalho Chehab 	} else {
579336fea92SMauro Carvalho Chehab 		dev_dbg(dev->dev, "stopping capture");
5800c0d06caSMauro Carvalho Chehab 		cx231xx_isoc_audio_deinit(dev);
5810c0d06caSMauro Carvalho Chehab 	}
5820c0d06caSMauro Carvalho Chehab }
5830c0d06caSMauro Carvalho Chehab 
5840c0d06caSMauro Carvalho Chehab static int snd_cx231xx_capture_trigger(struct snd_pcm_substream *substream,
5850c0d06caSMauro Carvalho Chehab 				       int cmd)
5860c0d06caSMauro Carvalho Chehab {
5870c0d06caSMauro Carvalho Chehab 	struct cx231xx *dev = snd_pcm_substream_chip(substream);
5880c0d06caSMauro Carvalho Chehab 	int retval = 0;
5890c0d06caSMauro Carvalho Chehab 
5900c0d06caSMauro Carvalho Chehab 	if (dev->state & DEV_DISCONNECTED)
5910c0d06caSMauro Carvalho Chehab 		return -ENODEV;
5920c0d06caSMauro Carvalho Chehab 
5930c0d06caSMauro Carvalho Chehab 	spin_lock(&dev->adev.slock);
5940c0d06caSMauro Carvalho Chehab 	switch (cmd) {
5950c0d06caSMauro Carvalho Chehab 	case SNDRV_PCM_TRIGGER_START:
5960c0d06caSMauro Carvalho Chehab 		atomic_set(&dev->stream_started, 1);
5970c0d06caSMauro Carvalho Chehab 		break;
5980c0d06caSMauro Carvalho Chehab 	case SNDRV_PCM_TRIGGER_STOP:
5990c0d06caSMauro Carvalho Chehab 		atomic_set(&dev->stream_started, 0);
6000c0d06caSMauro Carvalho Chehab 		break;
6010c0d06caSMauro Carvalho Chehab 	default:
6020c0d06caSMauro Carvalho Chehab 		retval = -EINVAL;
6030c0d06caSMauro Carvalho Chehab 		break;
6040c0d06caSMauro Carvalho Chehab 	}
6050c0d06caSMauro Carvalho Chehab 	spin_unlock(&dev->adev.slock);
6060c0d06caSMauro Carvalho Chehab 
6070c0d06caSMauro Carvalho Chehab 	schedule_work(&dev->wq_trigger);
6080c0d06caSMauro Carvalho Chehab 
6090c0d06caSMauro Carvalho Chehab 	return retval;
6100c0d06caSMauro Carvalho Chehab }
6110c0d06caSMauro Carvalho Chehab 
6120c0d06caSMauro Carvalho Chehab static snd_pcm_uframes_t snd_cx231xx_capture_pointer(struct snd_pcm_substream
6130c0d06caSMauro Carvalho Chehab 						     *substream)
6140c0d06caSMauro Carvalho Chehab {
6150c0d06caSMauro Carvalho Chehab 	struct cx231xx *dev;
6160c0d06caSMauro Carvalho Chehab 	unsigned long flags;
6170c0d06caSMauro Carvalho Chehab 	snd_pcm_uframes_t hwptr_done;
6180c0d06caSMauro Carvalho Chehab 
6190c0d06caSMauro Carvalho Chehab 	dev = snd_pcm_substream_chip(substream);
6200c0d06caSMauro Carvalho Chehab 
6210c0d06caSMauro Carvalho Chehab 	spin_lock_irqsave(&dev->adev.slock, flags);
6220c0d06caSMauro Carvalho Chehab 	hwptr_done = dev->adev.hwptr_done_capture;
6230c0d06caSMauro Carvalho Chehab 	spin_unlock_irqrestore(&dev->adev.slock, flags);
6240c0d06caSMauro Carvalho Chehab 
6250c0d06caSMauro Carvalho Chehab 	return hwptr_done;
6260c0d06caSMauro Carvalho Chehab }
6270c0d06caSMauro Carvalho Chehab 
6280c0d06caSMauro Carvalho Chehab static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs,
6290c0d06caSMauro Carvalho Chehab 					     unsigned long offset)
6300c0d06caSMauro Carvalho Chehab {
6310c0d06caSMauro Carvalho Chehab 	void *pageptr = subs->runtime->dma_area + offset;
6320c0d06caSMauro Carvalho Chehab 
6330c0d06caSMauro Carvalho Chehab 	return vmalloc_to_page(pageptr);
6340c0d06caSMauro Carvalho Chehab }
6350c0d06caSMauro Carvalho Chehab 
63622511cfaSJulia Lawall static const struct snd_pcm_ops snd_cx231xx_pcm_capture = {
6370c0d06caSMauro Carvalho Chehab 	.open = snd_cx231xx_capture_open,
6380c0d06caSMauro Carvalho Chehab 	.close = snd_cx231xx_pcm_close,
6390c0d06caSMauro Carvalho Chehab 	.ioctl = snd_pcm_lib_ioctl,
6400c0d06caSMauro Carvalho Chehab 	.hw_params = snd_cx231xx_hw_capture_params,
6410c0d06caSMauro Carvalho Chehab 	.hw_free = snd_cx231xx_hw_capture_free,
6420c0d06caSMauro Carvalho Chehab 	.prepare = snd_cx231xx_prepare,
6430c0d06caSMauro Carvalho Chehab 	.trigger = snd_cx231xx_capture_trigger,
6440c0d06caSMauro Carvalho Chehab 	.pointer = snd_cx231xx_capture_pointer,
6450c0d06caSMauro Carvalho Chehab 	.page = snd_pcm_get_vmalloc_page,
6460c0d06caSMauro Carvalho Chehab };
6470c0d06caSMauro Carvalho Chehab 
6480c0d06caSMauro Carvalho Chehab static int cx231xx_audio_init(struct cx231xx *dev)
6490c0d06caSMauro Carvalho Chehab {
6500c0d06caSMauro Carvalho Chehab 	struct cx231xx_audio *adev = &dev->adev;
6510c0d06caSMauro Carvalho Chehab 	struct snd_pcm *pcm;
6520c0d06caSMauro Carvalho Chehab 	struct snd_card *card;
6530c0d06caSMauro Carvalho Chehab 	static int devnr;
6540c0d06caSMauro Carvalho Chehab 	int err;
6550c0d06caSMauro Carvalho Chehab 	struct usb_interface *uif;
6560c0d06caSMauro Carvalho Chehab 	int i, isoc_pipe = 0;
6570c0d06caSMauro Carvalho Chehab 
6580c0d06caSMauro Carvalho Chehab 	if (dev->has_alsa_audio != 1) {
6590c0d06caSMauro Carvalho Chehab 		/* This device does not support the extension (in this case
6600c0d06caSMauro Carvalho Chehab 		   the device is expecting the snd-usb-audio module or
6610c0d06caSMauro Carvalho Chehab 		   doesn't have analog audio support at all) */
6620c0d06caSMauro Carvalho Chehab 		return 0;
6630c0d06caSMauro Carvalho Chehab 	}
6640c0d06caSMauro Carvalho Chehab 
665336fea92SMauro Carvalho Chehab 	dev_dbg(dev->dev,
666b7085c08SMauro Carvalho Chehab 		"probing for cx231xx non standard usbaudio\n");
6670c0d06caSMauro Carvalho Chehab 
668336fea92SMauro Carvalho Chehab 	err = snd_card_new(dev->dev, index[devnr], "Cx231xx Audio",
669e7356888STakashi Iwai 			   THIS_MODULE, 0, &card);
6700c0d06caSMauro Carvalho Chehab 	if (err < 0)
6710c0d06caSMauro Carvalho Chehab 		return err;
6720c0d06caSMauro Carvalho Chehab 
6730c0d06caSMauro Carvalho Chehab 	spin_lock_init(&adev->slock);
6740c0d06caSMauro Carvalho Chehab 	err = snd_pcm_new(card, "Cx231xx Audio", 0, 0, 1, &pcm);
675fff1abc4SJohan Hovold 	if (err < 0)
676fff1abc4SJohan Hovold 		goto err_free_card;
6770c0d06caSMauro Carvalho Chehab 
6780c0d06caSMauro Carvalho Chehab 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
6790c0d06caSMauro Carvalho Chehab 			&snd_cx231xx_pcm_capture);
6800c0d06caSMauro Carvalho Chehab 	pcm->info_flags = 0;
6810c0d06caSMauro Carvalho Chehab 	pcm->private_data = dev;
6820c0d06caSMauro Carvalho Chehab 	strcpy(pcm->name, "Conexant cx231xx Capture");
6830c0d06caSMauro Carvalho Chehab 	strcpy(card->driver, "Cx231xx-Audio");
6840c0d06caSMauro Carvalho Chehab 	strcpy(card->shortname, "Cx231xx Audio");
6850c0d06caSMauro Carvalho Chehab 	strcpy(card->longname, "Conexant cx231xx Audio");
6860c0d06caSMauro Carvalho Chehab 
6870c0d06caSMauro Carvalho Chehab 	INIT_WORK(&dev->wq_trigger, audio_trigger);
6880c0d06caSMauro Carvalho Chehab 
6890c0d06caSMauro Carvalho Chehab 	err = snd_card_register(card);
690fff1abc4SJohan Hovold 	if (err < 0)
691fff1abc4SJohan Hovold 		goto err_free_card;
692fff1abc4SJohan Hovold 
6930c0d06caSMauro Carvalho Chehab 	adev->sndcard = card;
6940c0d06caSMauro Carvalho Chehab 	adev->udev = dev->udev;
6950c0d06caSMauro Carvalho Chehab 
6960c0d06caSMauro Carvalho Chehab 	/* compute alternate max packet sizes for Audio */
6970c0d06caSMauro Carvalho Chehab 	uif =
6980c0d06caSMauro Carvalho Chehab 	    dev->udev->actconfig->interface[dev->current_pcb_config.
6990c0d06caSMauro Carvalho Chehab 					    hs_config_info[0].interface_info.
7000c0d06caSMauro Carvalho Chehab 					    audio_index + 1];
7010c0d06caSMauro Carvalho Chehab 
70265f92164SJohan Hovold 	if (uif->altsetting[0].desc.bNumEndpoints < isoc_pipe + 1) {
70365f92164SJohan Hovold 		err = -ENODEV;
70465f92164SJohan Hovold 		goto err_free_card;
70565f92164SJohan Hovold 	}
70665f92164SJohan Hovold 
7070c0d06caSMauro Carvalho Chehab 	adev->end_point_addr =
70869a11a32SHans Verkuil 	    uif->altsetting[0].endpoint[isoc_pipe].desc.
70969a11a32SHans Verkuil 			bEndpointAddress;
7100c0d06caSMauro Carvalho Chehab 
7110c0d06caSMauro Carvalho Chehab 	adev->num_alt = uif->num_altsetting;
712336fea92SMauro Carvalho Chehab 	dev_info(dev->dev,
713b7085c08SMauro Carvalho Chehab 		"audio EndPoint Addr 0x%x, Alternate settings: %i\n",
7140c0d06caSMauro Carvalho Chehab 		adev->end_point_addr, adev->num_alt);
7156da2ec56SKees Cook 	adev->alt_max_pkt_size = kmalloc_array(32, adev->num_alt, GFP_KERNEL);
716fff1abc4SJohan Hovold 	if (!adev->alt_max_pkt_size) {
717fff1abc4SJohan Hovold 		err = -ENOMEM;
718fff1abc4SJohan Hovold 		goto err_free_card;
719fff1abc4SJohan Hovold 	}
7200c0d06caSMauro Carvalho Chehab 
7210c0d06caSMauro Carvalho Chehab 	for (i = 0; i < adev->num_alt; i++) {
72265f92164SJohan Hovold 		u16 tmp;
72365f92164SJohan Hovold 
72465f92164SJohan Hovold 		if (uif->altsetting[i].desc.bNumEndpoints < isoc_pipe + 1) {
72565f92164SJohan Hovold 			err = -ENODEV;
72665f92164SJohan Hovold 			goto err_free_pkt_size;
72765f92164SJohan Hovold 		}
72865f92164SJohan Hovold 
72965f92164SJohan Hovold 		tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc.
7300c0d06caSMauro Carvalho Chehab 				wMaxPacketSize);
7310c0d06caSMauro Carvalho Chehab 		adev->alt_max_pkt_size[i] =
7320c0d06caSMauro Carvalho Chehab 		    (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
733336fea92SMauro Carvalho Chehab 		dev_dbg(dev->dev,
734b7085c08SMauro Carvalho Chehab 			"audio alternate setting %i, max size= %i\n", i,
7350c0d06caSMauro Carvalho Chehab 			adev->alt_max_pkt_size[i]);
7360c0d06caSMauro Carvalho Chehab 	}
7370c0d06caSMauro Carvalho Chehab 
7380c0d06caSMauro Carvalho Chehab 	return 0;
739fff1abc4SJohan Hovold 
74065f92164SJohan Hovold err_free_pkt_size:
74165f92164SJohan Hovold 	kfree(adev->alt_max_pkt_size);
742fff1abc4SJohan Hovold err_free_card:
743fff1abc4SJohan Hovold 	snd_card_free(card);
744fff1abc4SJohan Hovold 
745fff1abc4SJohan Hovold 	return err;
7460c0d06caSMauro Carvalho Chehab }
7470c0d06caSMauro Carvalho Chehab 
7480c0d06caSMauro Carvalho Chehab static int cx231xx_audio_fini(struct cx231xx *dev)
7490c0d06caSMauro Carvalho Chehab {
7500c0d06caSMauro Carvalho Chehab 	if (dev == NULL)
7510c0d06caSMauro Carvalho Chehab 		return 0;
7520c0d06caSMauro Carvalho Chehab 
7530c0d06caSMauro Carvalho Chehab 	if (dev->has_alsa_audio != 1) {
7540c0d06caSMauro Carvalho Chehab 		/* This device does not support the extension (in this case
7550c0d06caSMauro Carvalho Chehab 		   the device is expecting the snd-usb-audio module or
7560c0d06caSMauro Carvalho Chehab 		   doesn't have analog audio support at all) */
7570c0d06caSMauro Carvalho Chehab 		return 0;
7580c0d06caSMauro Carvalho Chehab 	}
7590c0d06caSMauro Carvalho Chehab 
7600c0d06caSMauro Carvalho Chehab 	if (dev->adev.sndcard) {
7610c0d06caSMauro Carvalho Chehab 		snd_card_free(dev->adev.sndcard);
7620c0d06caSMauro Carvalho Chehab 		kfree(dev->adev.alt_max_pkt_size);
7630c0d06caSMauro Carvalho Chehab 		dev->adev.sndcard = NULL;
7640c0d06caSMauro Carvalho Chehab 	}
7650c0d06caSMauro Carvalho Chehab 
7660c0d06caSMauro Carvalho Chehab 	return 0;
7670c0d06caSMauro Carvalho Chehab }
7680c0d06caSMauro Carvalho Chehab 
7690c0d06caSMauro Carvalho Chehab static struct cx231xx_ops audio_ops = {
7700c0d06caSMauro Carvalho Chehab 	.id = CX231XX_AUDIO,
7710c0d06caSMauro Carvalho Chehab 	.name = "Cx231xx Audio Extension",
7720c0d06caSMauro Carvalho Chehab 	.init = cx231xx_audio_init,
7730c0d06caSMauro Carvalho Chehab 	.fini = cx231xx_audio_fini,
7740c0d06caSMauro Carvalho Chehab };
7750c0d06caSMauro Carvalho Chehab 
7760c0d06caSMauro Carvalho Chehab static int __init cx231xx_alsa_register(void)
7770c0d06caSMauro Carvalho Chehab {
7780c0d06caSMauro Carvalho Chehab 	return cx231xx_register_extension(&audio_ops);
7790c0d06caSMauro Carvalho Chehab }
7800c0d06caSMauro Carvalho Chehab 
7810c0d06caSMauro Carvalho Chehab static void __exit cx231xx_alsa_unregister(void)
7820c0d06caSMauro Carvalho Chehab {
7830c0d06caSMauro Carvalho Chehab 	cx231xx_unregister_extension(&audio_ops);
7840c0d06caSMauro Carvalho Chehab }
7850c0d06caSMauro Carvalho Chehab 
7860c0d06caSMauro Carvalho Chehab MODULE_LICENSE("GPL");
7870c0d06caSMauro Carvalho Chehab MODULE_AUTHOR("Srinivasa Deevi <srinivasa.deevi@conexant.com>");
7880c0d06caSMauro Carvalho Chehab MODULE_DESCRIPTION("Cx231xx Audio driver");
7890c0d06caSMauro Carvalho Chehab 
7900c0d06caSMauro Carvalho Chehab module_init(cx231xx_alsa_register);
7910c0d06caSMauro Carvalho Chehab module_exit(cx231xx_alsa_unregister);
792