1f22e9e71SMauro Carvalho Chehab // SPDX-License-Identifier: GPL-2.0+
2f22e9e71SMauro Carvalho Chehab //
3f22e9e71SMauro Carvalho Chehab // Empiatech em28x1 audio extension
4f22e9e71SMauro Carvalho Chehab //
5f22e9e71SMauro Carvalho Chehab // Copyright (C) 2006 Markus Rechberger <mrechberger@gmail.com>
6f22e9e71SMauro Carvalho Chehab //
7f22e9e71SMauro Carvalho Chehab // Copyright (C) 2007-2016 Mauro Carvalho Chehab
8f22e9e71SMauro Carvalho Chehab //	- Port to work with the in-kernel driver
9f22e9e71SMauro Carvalho Chehab //	- Cleanups, fixes, alsa-controls, etc.
10f22e9e71SMauro Carvalho Chehab //
11f22e9e71SMauro Carvalho Chehab // This driver is based on my previous au600 usb pstn audio driver
12f22e9e71SMauro Carvalho Chehab // and inherits all the copyrights
130c0d06caSMauro Carvalho Chehab 
148314d402SMauro Carvalho Chehab #include "em28xx.h"
158314d402SMauro Carvalho Chehab 
160c0d06caSMauro Carvalho Chehab #include <linux/kernel.h>
170c0d06caSMauro Carvalho Chehab #include <linux/usb.h>
180c0d06caSMauro Carvalho Chehab #include <linux/init.h>
190c0d06caSMauro Carvalho Chehab #include <linux/sound.h>
200c0d06caSMauro Carvalho Chehab #include <linux/spinlock.h>
210c0d06caSMauro Carvalho Chehab #include <linux/soundcard.h>
220c0d06caSMauro Carvalho Chehab #include <linux/slab.h>
230c0d06caSMauro Carvalho Chehab #include <linux/module.h>
240c0d06caSMauro Carvalho Chehab #include <sound/core.h>
250c0d06caSMauro Carvalho Chehab #include <sound/pcm.h>
260c0d06caSMauro Carvalho Chehab #include <sound/pcm_params.h>
270c0d06caSMauro Carvalho Chehab #include <sound/info.h>
280c0d06caSMauro Carvalho Chehab #include <sound/initval.h>
290c0d06caSMauro Carvalho Chehab #include <sound/control.h>
300c0d06caSMauro Carvalho Chehab #include <sound/tlv.h>
310c0d06caSMauro Carvalho Chehab #include <sound/ac97_codec.h>
320c0d06caSMauro Carvalho Chehab #include <media/v4l2-common.h>
330c0d06caSMauro Carvalho Chehab 
340c0d06caSMauro Carvalho Chehab static int debug;
350c0d06caSMauro Carvalho Chehab module_param(debug, int, 0644);
360c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(debug, "activates debug info");
370c0d06caSMauro Carvalho Chehab 
381b3fd2d3SMauro Carvalho Chehab #define EM28XX_MAX_AUDIO_BUFS		5
391b3fd2d3SMauro Carvalho Chehab #define EM28XX_MIN_AUDIO_PACKETS	64
4049677aefSMauro Carvalho Chehab 
410c0d06caSMauro Carvalho Chehab #define dprintk(fmt, arg...) do {					\
420c0d06caSMauro Carvalho Chehab 	if (debug)						\
4329b05e22SMauro Carvalho Chehab 		dev_printk(KERN_DEBUG, &dev->intf->dev,			\
44ce8591ffSMauro Carvalho Chehab 			   "video: %s: " fmt, __func__, ## arg);	\
45ce8591ffSMauro Carvalho Chehab } while (0)
460c0d06caSMauro Carvalho Chehab 
470c0d06caSMauro Carvalho Chehab static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
480c0d06caSMauro Carvalho Chehab 
em28xx_deinit_isoc_audio(struct em28xx * dev)490c0d06caSMauro Carvalho Chehab static int em28xx_deinit_isoc_audio(struct em28xx *dev)
500c0d06caSMauro Carvalho Chehab {
510c0d06caSMauro Carvalho Chehab 	int i;
520c0d06caSMauro Carvalho Chehab 
530c0d06caSMauro Carvalho Chehab 	dprintk("Stopping isoc\n");
541b3fd2d3SMauro Carvalho Chehab 	for (i = 0; i < dev->adev.num_urb; i++) {
5546c704d7SMauro Carvalho Chehab 		struct urb *urb = dev->adev.urb[i];
5646c704d7SMauro Carvalho Chehab 
570c0d06caSMauro Carvalho Chehab 		if (!irqs_disabled())
5846c704d7SMauro Carvalho Chehab 			usb_kill_urb(urb);
590c0d06caSMauro Carvalho Chehab 		else
6046c704d7SMauro Carvalho Chehab 			usb_unlink_urb(urb);
610c0d06caSMauro Carvalho Chehab 	}
620c0d06caSMauro Carvalho Chehab 
630c0d06caSMauro Carvalho Chehab 	return 0;
640c0d06caSMauro Carvalho Chehab }
650c0d06caSMauro Carvalho Chehab 
em28xx_audio_isocirq(struct urb * urb)660c0d06caSMauro Carvalho Chehab static void em28xx_audio_isocirq(struct urb *urb)
670c0d06caSMauro Carvalho Chehab {
680c0d06caSMauro Carvalho Chehab 	struct em28xx            *dev = urb->context;
690c0d06caSMauro Carvalho Chehab 	int                      i;
700c0d06caSMauro Carvalho Chehab 	unsigned int             oldptr;
710c0d06caSMauro Carvalho Chehab 	int                      period_elapsed = 0;
720c0d06caSMauro Carvalho Chehab 	int                      status;
730c0d06caSMauro Carvalho Chehab 	unsigned char            *cp;
740c0d06caSMauro Carvalho Chehab 	unsigned int             stride;
750c0d06caSMauro Carvalho Chehab 	struct snd_pcm_substream *substream;
760c0d06caSMauro Carvalho Chehab 	struct snd_pcm_runtime   *runtime;
770c0d06caSMauro Carvalho Chehab 
788b1fa579SMauro Carvalho Chehab 	if (dev->disconnected) {
79ce8591ffSMauro Carvalho Chehab 		dprintk("device disconnected while streaming. URB status=%d.\n",
80ce8591ffSMauro Carvalho Chehab 			urb->status);
81a5c075cfSFrank Schaefer 		atomic_set(&dev->adev.stream_started, 0);
828b1fa579SMauro Carvalho Chehab 		return;
838b1fa579SMauro Carvalho Chehab 	}
848b1fa579SMauro Carvalho Chehab 
850c0d06caSMauro Carvalho Chehab 	switch (urb->status) {
860c0d06caSMauro Carvalho Chehab 	case 0:             /* success */
870c0d06caSMauro Carvalho Chehab 	case -ETIMEDOUT:    /* NAK */
880c0d06caSMauro Carvalho Chehab 		break;
890c0d06caSMauro Carvalho Chehab 	case -ECONNRESET:   /* kill */
900c0d06caSMauro Carvalho Chehab 	case -ENOENT:
910c0d06caSMauro Carvalho Chehab 	case -ESHUTDOWN:
920c0d06caSMauro Carvalho Chehab 		return;
930c0d06caSMauro Carvalho Chehab 	default:            /* error */
949f90f537SMauro Carvalho Chehab 		dprintk("urb completion error %d.\n", urb->status);
950c0d06caSMauro Carvalho Chehab 		break;
960c0d06caSMauro Carvalho Chehab 	}
970c0d06caSMauro Carvalho Chehab 
98a5c075cfSFrank Schaefer 	if (atomic_read(&dev->adev.stream_started) == 0)
990c0d06caSMauro Carvalho Chehab 		return;
1000c0d06caSMauro Carvalho Chehab 
1010c0d06caSMauro Carvalho Chehab 	if (dev->adev.capture_pcm_substream) {
1020c0d06caSMauro Carvalho Chehab 		substream = dev->adev.capture_pcm_substream;
1030c0d06caSMauro Carvalho Chehab 		runtime = substream->runtime;
1040c0d06caSMauro Carvalho Chehab 		stride = runtime->frame_bits >> 3;
1050c0d06caSMauro Carvalho Chehab 
1060c0d06caSMauro Carvalho Chehab 		for (i = 0; i < urb->number_of_packets; i++) {
107273925c7SSebastian Andrzej Siewior 			unsigned long flags;
1080c0d06caSMauro Carvalho Chehab 			int length =
1090c0d06caSMauro Carvalho Chehab 			    urb->iso_frame_desc[i].actual_length / stride;
1100c0d06caSMauro Carvalho Chehab 			cp = (unsigned char *)urb->transfer_buffer +
1110c0d06caSMauro Carvalho Chehab 			    urb->iso_frame_desc[i].offset;
1120c0d06caSMauro Carvalho Chehab 
1130c0d06caSMauro Carvalho Chehab 			if (!length)
1140c0d06caSMauro Carvalho Chehab 				continue;
1150c0d06caSMauro Carvalho Chehab 
1160c0d06caSMauro Carvalho Chehab 			oldptr = dev->adev.hwptr_done_capture;
1170c0d06caSMauro Carvalho Chehab 			if (oldptr + length >= runtime->buffer_size) {
1180c0d06caSMauro Carvalho Chehab 				unsigned int cnt =
1190c0d06caSMauro Carvalho Chehab 				    runtime->buffer_size - oldptr;
1200c0d06caSMauro Carvalho Chehab 				memcpy(runtime->dma_area + oldptr * stride, cp,
1210c0d06caSMauro Carvalho Chehab 				       cnt * stride);
1220c0d06caSMauro Carvalho Chehab 				memcpy(runtime->dma_area, cp + cnt * stride,
1230c0d06caSMauro Carvalho Chehab 				       length * stride - cnt * stride);
1240c0d06caSMauro Carvalho Chehab 			} else {
1250c0d06caSMauro Carvalho Chehab 				memcpy(runtime->dma_area + oldptr * stride, cp,
1260c0d06caSMauro Carvalho Chehab 				       length * stride);
1270c0d06caSMauro Carvalho Chehab 			}
1280c0d06caSMauro Carvalho Chehab 
129273925c7SSebastian Andrzej Siewior 			snd_pcm_stream_lock_irqsave(substream, flags);
1300c0d06caSMauro Carvalho Chehab 
1310c0d06caSMauro Carvalho Chehab 			dev->adev.hwptr_done_capture += length;
1320c0d06caSMauro Carvalho Chehab 			if (dev->adev.hwptr_done_capture >=
1330c0d06caSMauro Carvalho Chehab 			    runtime->buffer_size)
1340c0d06caSMauro Carvalho Chehab 				dev->adev.hwptr_done_capture -=
1350c0d06caSMauro Carvalho Chehab 				    runtime->buffer_size;
1360c0d06caSMauro Carvalho Chehab 
1370c0d06caSMauro Carvalho Chehab 			dev->adev.capture_transfer_done += length;
1380c0d06caSMauro Carvalho Chehab 			if (dev->adev.capture_transfer_done >=
1390c0d06caSMauro Carvalho Chehab 			    runtime->period_size) {
1400c0d06caSMauro Carvalho Chehab 				dev->adev.capture_transfer_done -=
1410c0d06caSMauro Carvalho Chehab 				    runtime->period_size;
1420c0d06caSMauro Carvalho Chehab 				period_elapsed = 1;
1430c0d06caSMauro Carvalho Chehab 			}
1440c0d06caSMauro Carvalho Chehab 
145273925c7SSebastian Andrzej Siewior 			snd_pcm_stream_unlock_irqrestore(substream, flags);
1460c0d06caSMauro Carvalho Chehab 		}
1470c0d06caSMauro Carvalho Chehab 		if (period_elapsed)
1480c0d06caSMauro Carvalho Chehab 			snd_pcm_period_elapsed(substream);
1490c0d06caSMauro Carvalho Chehab 	}
1500c0d06caSMauro Carvalho Chehab 	urb->status = 0;
1510c0d06caSMauro Carvalho Chehab 
1520c0d06caSMauro Carvalho Chehab 	status = usb_submit_urb(urb, GFP_ATOMIC);
153bf6e8aaaSMauro Carvalho Chehab 	if (status < 0)
15429b05e22SMauro Carvalho Chehab 		dev_err(&dev->intf->dev,
155ce8591ffSMauro Carvalho Chehab 			"resubmit of audio urb failed (error=%i)\n",
1560c0d06caSMauro Carvalho Chehab 			status);
1570c0d06caSMauro Carvalho Chehab }
1580c0d06caSMauro Carvalho Chehab 
em28xx_init_audio_isoc(struct em28xx * dev)1590c0d06caSMauro Carvalho Chehab static int em28xx_init_audio_isoc(struct em28xx *dev)
1600c0d06caSMauro Carvalho Chehab {
1619f90f537SMauro Carvalho Chehab 	int       i, err;
1620c0d06caSMauro Carvalho Chehab 
1630c0d06caSMauro Carvalho Chehab 	dprintk("Starting isoc transfers\n");
1640c0d06caSMauro Carvalho Chehab 
165f5f894d1SMauro Carvalho Chehab 	/* Start streaming */
1661b3fd2d3SMauro Carvalho Chehab 	for (i = 0; i < dev->adev.num_urb; i++) {
167f5f894d1SMauro Carvalho Chehab 		memset(dev->adev.transfer_buffer[i], 0x80,
168f5f894d1SMauro Carvalho Chehab 		       dev->adev.urb[i]->transfer_buffer_length);
1690c0d06caSMauro Carvalho Chehab 
1709f90f537SMauro Carvalho Chehab 		err = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC);
1719f90f537SMauro Carvalho Chehab 		if (err) {
17229b05e22SMauro Carvalho Chehab 			dev_err(&dev->intf->dev,
173ce8591ffSMauro Carvalho Chehab 				"submit of audio urb failed (error=%i)\n",
1749f90f537SMauro Carvalho Chehab 				err);
1750c0d06caSMauro Carvalho Chehab 			em28xx_deinit_isoc_audio(dev);
176a5c075cfSFrank Schaefer 			atomic_set(&dev->adev.stream_started, 0);
1779f90f537SMauro Carvalho Chehab 			return err;
1780c0d06caSMauro Carvalho Chehab 		}
1790c0d06caSMauro Carvalho Chehab 	}
1800c0d06caSMauro Carvalho Chehab 
1810c0d06caSMauro Carvalho Chehab 	return 0;
1820c0d06caSMauro Carvalho Chehab }
1830c0d06caSMauro Carvalho Chehab 
18438c4f03aSBhumika Goyal static const struct snd_pcm_hardware snd_em28xx_hw_capture = {
1850c0d06caSMauro Carvalho Chehab 	.info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
1860c0d06caSMauro Carvalho Chehab 		SNDRV_PCM_INFO_MMAP           |
1870c0d06caSMauro Carvalho Chehab 		SNDRV_PCM_INFO_INTERLEAVED    |
1880c0d06caSMauro Carvalho Chehab 		SNDRV_PCM_INFO_BATCH	      |
1890c0d06caSMauro Carvalho Chehab 		SNDRV_PCM_INFO_MMAP_VALID,
1900c0d06caSMauro Carvalho Chehab 
1910c0d06caSMauro Carvalho Chehab 	.formats = SNDRV_PCM_FMTBIT_S16_LE,
1920c0d06caSMauro Carvalho Chehab 
19349677aefSMauro Carvalho Chehab 	.rates = SNDRV_PCM_RATE_48000,
1940c0d06caSMauro Carvalho Chehab 
1950c0d06caSMauro Carvalho Chehab 	.rate_min = 48000,
1960c0d06caSMauro Carvalho Chehab 	.rate_max = 48000,
1970c0d06caSMauro Carvalho Chehab 	.channels_min = 2,
1980c0d06caSMauro Carvalho Chehab 	.channels_max = 2,
1990c0d06caSMauro Carvalho Chehab 	.buffer_bytes_max = 62720 * 8,	/* just about the value in usbaudio.c */
20049677aefSMauro Carvalho Chehab 
20149677aefSMauro Carvalho Chehab 	/*
20249677aefSMauro Carvalho Chehab 	 * The period is 12.288 bytes. Allow a 10% of variation along its
20349677aefSMauro Carvalho Chehab 	 * value, in order to avoid overruns/underruns due to some clock
20449677aefSMauro Carvalho Chehab 	 * drift.
20549677aefSMauro Carvalho Chehab 	 *
20649677aefSMauro Carvalho Chehab 	 * FIXME: This period assumes 64 packets, and a 48000 PCM rate.
20749677aefSMauro Carvalho Chehab 	 * Calculate it dynamically.
20849677aefSMauro Carvalho Chehab 	 */
20949677aefSMauro Carvalho Chehab 	.period_bytes_min = 11059,
21049677aefSMauro Carvalho Chehab 	.period_bytes_max = 13516,
21149677aefSMauro Carvalho Chehab 
2120c0d06caSMauro Carvalho Chehab 	.periods_min = 2,
2130c0d06caSMauro Carvalho Chehab 	.periods_max = 98,		/* 12544, */
2140c0d06caSMauro Carvalho Chehab };
2150c0d06caSMauro Carvalho Chehab 
snd_em28xx_capture_open(struct snd_pcm_substream * substream)2160c0d06caSMauro Carvalho Chehab static int snd_em28xx_capture_open(struct snd_pcm_substream *substream)
2170c0d06caSMauro Carvalho Chehab {
2180c0d06caSMauro Carvalho Chehab 	struct em28xx *dev = snd_pcm_substream_chip(substream);
2190c0d06caSMauro Carvalho Chehab 	struct snd_pcm_runtime *runtime = substream->runtime;
22001ae3b51SFrank Schaefer 	int nonblock, ret = 0;
2210c0d06caSMauro Carvalho Chehab 
2220c0d06caSMauro Carvalho Chehab 	if (!dev) {
223257e29f8SMauro Carvalho Chehab 		pr_err("em28xx-audio: BUG: em28xx can't find device struct. Can't proceed with open\n");
2240c0d06caSMauro Carvalho Chehab 		return -ENODEV;
2250c0d06caSMauro Carvalho Chehab 	}
2260c0d06caSMauro Carvalho Chehab 
2278b1fa579SMauro Carvalho Chehab 	if (dev->disconnected)
2288b1fa579SMauro Carvalho Chehab 		return -ENODEV;
2298b1fa579SMauro Carvalho Chehab 
2308b1fa579SMauro Carvalho Chehab 	dprintk("opening device and trying to acquire exclusive lock\n");
2318b1fa579SMauro Carvalho Chehab 
23201ae3b51SFrank Schaefer 	nonblock = !!(substream->f_flags & O_NONBLOCK);
23334906633SMauro Carvalho Chehab 	if (nonblock) {
23434906633SMauro Carvalho Chehab 		if (!mutex_trylock(&dev->lock))
23534906633SMauro Carvalho Chehab 			return -EAGAIN;
2369f90f537SMauro Carvalho Chehab 	} else {
23734906633SMauro Carvalho Chehab 		mutex_lock(&dev->lock);
2389f90f537SMauro Carvalho Chehab 	}
23901ae3b51SFrank Schaefer 
24001ae3b51SFrank Schaefer 	runtime->hw = snd_em28xx_hw_capture;
241ba35ca07SFrank Schaefer 
242ba35ca07SFrank Schaefer 	if (dev->adev.users == 0) {
2439f90f537SMauro Carvalho Chehab 		if (!dev->alt || dev->is_audio_only) {
2449f90f537SMauro Carvalho Chehab 			struct usb_device *udev;
2459f90f537SMauro Carvalho Chehab 
2469f90f537SMauro Carvalho Chehab 			udev = interface_to_usbdev(dev->intf);
247c6d48134SMauro Carvalho Chehab 
2480191a2a2SFrank Schaefer 			if (dev->is_audio_only)
249ba35ca07SFrank Schaefer 				/* audio is on a separate interface */
2500c0d06caSMauro Carvalho Chehab 				dev->alt = 1;
2510c0d06caSMauro Carvalho Chehab 			else
252ba35ca07SFrank Schaefer 				/* audio is on the same interface as video */
2530c0d06caSMauro Carvalho Chehab 				dev->alt = 7;
2540191a2a2SFrank Schaefer 				/*
255ba35ca07SFrank Schaefer 				 * FIXME: The intention seems to be to select
256ba35ca07SFrank Schaefer 				 * the alt setting with the largest
257ba35ca07SFrank Schaefer 				 * wMaxPacketSize for the video endpoint.
258ba35ca07SFrank Schaefer 				 * At least dev->alt should be used instead, but
259ba35ca07SFrank Schaefer 				 * we should probably not touch it at all if it
260ba35ca07SFrank Schaefer 				 * is already >0, because wMaxPacketSize of the
261ba35ca07SFrank Schaefer 				 * audio endpoints seems to be the same for all.
2620191a2a2SFrank Schaefer 				 */
2630c0d06caSMauro Carvalho Chehab 			dprintk("changing alternate number on interface %d to %d\n",
264961717b4SFrank Schaefer 				dev->ifnum, dev->alt);
265c6d48134SMauro Carvalho Chehab 			usb_set_interface(udev, dev->ifnum, dev->alt);
266ba35ca07SFrank Schaefer 		}
2670c0d06caSMauro Carvalho Chehab 
2680c0d06caSMauro Carvalho Chehab 		/* Sets volume, mute, etc */
2690c0d06caSMauro Carvalho Chehab 		dev->mute = 0;
2700c0d06caSMauro Carvalho Chehab 		ret = em28xx_audio_analog_set(dev);
2710c0d06caSMauro Carvalho Chehab 		if (ret < 0)
2720c0d06caSMauro Carvalho Chehab 			goto err;
27301ae3b51SFrank Schaefer 	}
2740c0d06caSMauro Carvalho Chehab 
27547677e51SMauro Carvalho Chehab 	kref_get(&dev->ref);
2760c0d06caSMauro Carvalho Chehab 	dev->adev.users++;
2770c0d06caSMauro Carvalho Chehab 	mutex_unlock(&dev->lock);
2780c0d06caSMauro Carvalho Chehab 
279a02b9c23SMauro Carvalho Chehab 	/* Dynamically adjust the period size */
2800c0d06caSMauro Carvalho Chehab 	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
281a02b9c23SMauro Carvalho Chehab 	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
282a02b9c23SMauro Carvalho Chehab 				     dev->adev.period * 95 / 100,
283a02b9c23SMauro Carvalho Chehab 				     dev->adev.period * 105 / 100);
284a02b9c23SMauro Carvalho Chehab 
2850c0d06caSMauro Carvalho Chehab 	dev->adev.capture_pcm_substream = substream;
2860c0d06caSMauro Carvalho Chehab 
2870c0d06caSMauro Carvalho Chehab 	return 0;
2880c0d06caSMauro Carvalho Chehab err:
2890c0d06caSMauro Carvalho Chehab 	mutex_unlock(&dev->lock);
2900c0d06caSMauro Carvalho Chehab 
29129b05e22SMauro Carvalho Chehab 	dev_err(&dev->intf->dev,
292ce8591ffSMauro Carvalho Chehab 		"Error while configuring em28xx mixer\n");
2930c0d06caSMauro Carvalho Chehab 	return ret;
2940c0d06caSMauro Carvalho Chehab }
2950c0d06caSMauro Carvalho Chehab 
snd_em28xx_pcm_close(struct snd_pcm_substream * substream)2960c0d06caSMauro Carvalho Chehab static int snd_em28xx_pcm_close(struct snd_pcm_substream *substream)
2970c0d06caSMauro Carvalho Chehab {
2980c0d06caSMauro Carvalho Chehab 	struct em28xx *dev = snd_pcm_substream_chip(substream);
2990c0d06caSMauro Carvalho Chehab 
3000c0d06caSMauro Carvalho Chehab 	dprintk("closing device\n");
3010c0d06caSMauro Carvalho Chehab 
3020c0d06caSMauro Carvalho Chehab 	dev->mute = 1;
3030c0d06caSMauro Carvalho Chehab 	mutex_lock(&dev->lock);
3040c0d06caSMauro Carvalho Chehab 	dev->adev.users--;
305a5c075cfSFrank Schaefer 	if (atomic_read(&dev->adev.stream_started) > 0) {
306a5c075cfSFrank Schaefer 		atomic_set(&dev->adev.stream_started, 0);
307a5c075cfSFrank Schaefer 		schedule_work(&dev->adev.wq_trigger);
3080c0d06caSMauro Carvalho Chehab 	}
3090c0d06caSMauro Carvalho Chehab 
3100c0d06caSMauro Carvalho Chehab 	em28xx_audio_analog_set(dev);
3110c0d06caSMauro Carvalho Chehab 	mutex_unlock(&dev->lock);
31247677e51SMauro Carvalho Chehab 	kref_put(&dev->ref, em28xx_free_device);
3130c0d06caSMauro Carvalho Chehab 
3140c0d06caSMauro Carvalho Chehab 	return 0;
3150c0d06caSMauro Carvalho Chehab }
3160c0d06caSMauro Carvalho Chehab 
snd_em28xx_prepare(struct snd_pcm_substream * substream)3170c0d06caSMauro Carvalho Chehab static int snd_em28xx_prepare(struct snd_pcm_substream *substream)
3180c0d06caSMauro Carvalho Chehab {
3190c0d06caSMauro Carvalho Chehab 	struct em28xx *dev = snd_pcm_substream_chip(substream);
3200c0d06caSMauro Carvalho Chehab 
3218b1fa579SMauro Carvalho Chehab 	if (dev->disconnected)
3228b1fa579SMauro Carvalho Chehab 		return -ENODEV;
3238b1fa579SMauro Carvalho Chehab 
3240c0d06caSMauro Carvalho Chehab 	dev->adev.hwptr_done_capture = 0;
3250c0d06caSMauro Carvalho Chehab 	dev->adev.capture_transfer_done = 0;
3260c0d06caSMauro Carvalho Chehab 
3270c0d06caSMauro Carvalho Chehab 	return 0;
3280c0d06caSMauro Carvalho Chehab }
3290c0d06caSMauro Carvalho Chehab 
audio_trigger(struct work_struct * work)3300c0d06caSMauro Carvalho Chehab static void audio_trigger(struct work_struct *work)
3310c0d06caSMauro Carvalho Chehab {
332a5c075cfSFrank Schaefer 	struct em28xx_audio *adev =
333a5c075cfSFrank Schaefer 			    container_of(work, struct em28xx_audio, wq_trigger);
334a5c075cfSFrank Schaefer 	struct em28xx *dev = container_of(adev, struct em28xx, adev);
3350c0d06caSMauro Carvalho Chehab 
336a5c075cfSFrank Schaefer 	if (atomic_read(&adev->stream_started)) {
3370c0d06caSMauro Carvalho Chehab 		dprintk("starting capture");
3380c0d06caSMauro Carvalho Chehab 		em28xx_init_audio_isoc(dev);
3390c0d06caSMauro Carvalho Chehab 	} else {
3400c0d06caSMauro Carvalho Chehab 		dprintk("stopping capture");
3410c0d06caSMauro Carvalho Chehab 		em28xx_deinit_isoc_audio(dev);
3420c0d06caSMauro Carvalho Chehab 	}
3430c0d06caSMauro Carvalho Chehab }
3440c0d06caSMauro Carvalho Chehab 
snd_em28xx_capture_trigger(struct snd_pcm_substream * substream,int cmd)3450c0d06caSMauro Carvalho Chehab static int snd_em28xx_capture_trigger(struct snd_pcm_substream *substream,
3460c0d06caSMauro Carvalho Chehab 				      int cmd)
3470c0d06caSMauro Carvalho Chehab {
3480c0d06caSMauro Carvalho Chehab 	struct em28xx *dev = snd_pcm_substream_chip(substream);
3490c0d06caSMauro Carvalho Chehab 	int retval = 0;
3500c0d06caSMauro Carvalho Chehab 
3518b1fa579SMauro Carvalho Chehab 	if (dev->disconnected)
3528b1fa579SMauro Carvalho Chehab 		return -ENODEV;
3538b1fa579SMauro Carvalho Chehab 
3540c0d06caSMauro Carvalho Chehab 	switch (cmd) {
3551771e9fbSGustavo A. R. Silva 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
3561771e9fbSGustavo A. R. Silva 	case SNDRV_PCM_TRIGGER_RESUME:
3570c0d06caSMauro Carvalho Chehab 	case SNDRV_PCM_TRIGGER_START:
358a5c075cfSFrank Schaefer 		atomic_set(&dev->adev.stream_started, 1);
3590c0d06caSMauro Carvalho Chehab 		break;
3601771e9fbSGustavo A. R. Silva 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
3611771e9fbSGustavo A. R. Silva 	case SNDRV_PCM_TRIGGER_SUSPEND:
3620c0d06caSMauro Carvalho Chehab 	case SNDRV_PCM_TRIGGER_STOP:
363a5c075cfSFrank Schaefer 		atomic_set(&dev->adev.stream_started, 0);
3640c0d06caSMauro Carvalho Chehab 		break;
3650c0d06caSMauro Carvalho Chehab 	default:
3660c0d06caSMauro Carvalho Chehab 		retval = -EINVAL;
3670c0d06caSMauro Carvalho Chehab 	}
368a5c075cfSFrank Schaefer 	schedule_work(&dev->adev.wq_trigger);
3690c0d06caSMauro Carvalho Chehab 	return retval;
3700c0d06caSMauro Carvalho Chehab }
3710c0d06caSMauro Carvalho Chehab 
snd_em28xx_capture_pointer(struct snd_pcm_substream * substream)3720c0d06caSMauro Carvalho Chehab static snd_pcm_uframes_t snd_em28xx_capture_pointer(struct snd_pcm_substream
3730c0d06caSMauro Carvalho Chehab 						    *substream)
3740c0d06caSMauro Carvalho Chehab {
3750c0d06caSMauro Carvalho Chehab 	unsigned long flags;
3760c0d06caSMauro Carvalho Chehab 	struct em28xx *dev;
3770c0d06caSMauro Carvalho Chehab 	snd_pcm_uframes_t hwptr_done;
3780c0d06caSMauro Carvalho Chehab 
3790c0d06caSMauro Carvalho Chehab 	dev = snd_pcm_substream_chip(substream);
3808b1fa579SMauro Carvalho Chehab 	if (dev->disconnected)
381229295f1SMauro Carvalho Chehab 		return SNDRV_PCM_POS_XRUN;
3828b1fa579SMauro Carvalho Chehab 
3830c0d06caSMauro Carvalho Chehab 	spin_lock_irqsave(&dev->adev.slock, flags);
3840c0d06caSMauro Carvalho Chehab 	hwptr_done = dev->adev.hwptr_done_capture;
3850c0d06caSMauro Carvalho Chehab 	spin_unlock_irqrestore(&dev->adev.slock, flags);
3860c0d06caSMauro Carvalho Chehab 
3870c0d06caSMauro Carvalho Chehab 	return hwptr_done;
3880c0d06caSMauro Carvalho Chehab }
3890c0d06caSMauro Carvalho Chehab 
3900c0d06caSMauro Carvalho Chehab /*
3910c0d06caSMauro Carvalho Chehab  * AC97 volume control support
3920c0d06caSMauro Carvalho Chehab  */
em28xx_vol_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * info)3930c0d06caSMauro Carvalho Chehab static int em28xx_vol_info(struct snd_kcontrol *kcontrol,
3940c0d06caSMauro Carvalho Chehab 			   struct snd_ctl_elem_info *info)
3950c0d06caSMauro Carvalho Chehab {
3968b1fa579SMauro Carvalho Chehab 	struct em28xx *dev = snd_kcontrol_chip(kcontrol);
3978b1fa579SMauro Carvalho Chehab 
3988b1fa579SMauro Carvalho Chehab 	if (dev->disconnected)
3998b1fa579SMauro Carvalho Chehab 		return -ENODEV;
4008b1fa579SMauro Carvalho Chehab 
4010c0d06caSMauro Carvalho Chehab 	info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
4020c0d06caSMauro Carvalho Chehab 	info->count = 2;
4030c0d06caSMauro Carvalho Chehab 	info->value.integer.min = 0;
4040c0d06caSMauro Carvalho Chehab 	info->value.integer.max = 0x1f;
4050c0d06caSMauro Carvalho Chehab 
4060c0d06caSMauro Carvalho Chehab 	return 0;
4070c0d06caSMauro Carvalho Chehab }
4080c0d06caSMauro Carvalho Chehab 
em28xx_vol_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * value)4090c0d06caSMauro Carvalho Chehab static int em28xx_vol_put(struct snd_kcontrol *kcontrol,
4100c0d06caSMauro Carvalho Chehab 			  struct snd_ctl_elem_value *value)
4110c0d06caSMauro Carvalho Chehab {
4120c0d06caSMauro Carvalho Chehab 	struct em28xx *dev = snd_kcontrol_chip(kcontrol);
41334906633SMauro Carvalho Chehab 	struct snd_pcm_substream *substream = dev->adev.capture_pcm_substream;
4140c0d06caSMauro Carvalho Chehab 	u16 val = (0x1f - (value->value.integer.value[0] & 0x1f)) |
4150c0d06caSMauro Carvalho Chehab 		  (0x1f - (value->value.integer.value[1] & 0x1f)) << 8;
41634906633SMauro Carvalho Chehab 	int nonblock = 0;
4170c0d06caSMauro Carvalho Chehab 	int rc;
4180c0d06caSMauro Carvalho Chehab 
4198b1fa579SMauro Carvalho Chehab 	if (dev->disconnected)
4208b1fa579SMauro Carvalho Chehab 		return -ENODEV;
4218b1fa579SMauro Carvalho Chehab 
42234906633SMauro Carvalho Chehab 	if (substream)
42334906633SMauro Carvalho Chehab 		nonblock = !!(substream->f_flags & O_NONBLOCK);
42434906633SMauro Carvalho Chehab 	if (nonblock) {
42534906633SMauro Carvalho Chehab 		if (!mutex_trylock(&dev->lock))
42634906633SMauro Carvalho Chehab 			return -EAGAIN;
4279f90f537SMauro Carvalho Chehab 	} else {
4280c0d06caSMauro Carvalho Chehab 		mutex_lock(&dev->lock);
4299f90f537SMauro Carvalho Chehab 	}
4300c0d06caSMauro Carvalho Chehab 	rc = em28xx_read_ac97(dev, kcontrol->private_value);
4310c0d06caSMauro Carvalho Chehab 	if (rc < 0)
4320c0d06caSMauro Carvalho Chehab 		goto err;
4330c0d06caSMauro Carvalho Chehab 
4340c0d06caSMauro Carvalho Chehab 	val |= rc & 0x8000;	/* Preserve the mute flag */
4350c0d06caSMauro Carvalho Chehab 
4360c0d06caSMauro Carvalho Chehab 	rc = em28xx_write_ac97(dev, kcontrol->private_value, val);
4370c0d06caSMauro Carvalho Chehab 	if (rc < 0)
4380c0d06caSMauro Carvalho Chehab 		goto err;
4390c0d06caSMauro Carvalho Chehab 
4400c0d06caSMauro Carvalho Chehab 	dprintk("%sleft vol %d, right vol %d (0x%04x) to ac97 volume control 0x%04x\n",
4410c0d06caSMauro Carvalho Chehab 		(val & 0x8000) ? "muted " : "",
4420c0d06caSMauro Carvalho Chehab 		0x1f - ((val >> 8) & 0x1f), 0x1f - (val & 0x1f),
4430c0d06caSMauro Carvalho Chehab 		val, (int)kcontrol->private_value);
4440c0d06caSMauro Carvalho Chehab 
4450c0d06caSMauro Carvalho Chehab err:
4460c0d06caSMauro Carvalho Chehab 	mutex_unlock(&dev->lock);
4470c0d06caSMauro Carvalho Chehab 	return rc;
4480c0d06caSMauro Carvalho Chehab }
4490c0d06caSMauro Carvalho Chehab 
em28xx_vol_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * value)4500c0d06caSMauro Carvalho Chehab static int em28xx_vol_get(struct snd_kcontrol *kcontrol,
4510c0d06caSMauro Carvalho Chehab 			  struct snd_ctl_elem_value *value)
4520c0d06caSMauro Carvalho Chehab {
4530c0d06caSMauro Carvalho Chehab 	struct em28xx *dev = snd_kcontrol_chip(kcontrol);
45434906633SMauro Carvalho Chehab 	struct snd_pcm_substream *substream = dev->adev.capture_pcm_substream;
45534906633SMauro Carvalho Chehab 	int nonblock = 0;
4560c0d06caSMauro Carvalho Chehab 	int val;
4570c0d06caSMauro Carvalho Chehab 
4588b1fa579SMauro Carvalho Chehab 	if (dev->disconnected)
4598b1fa579SMauro Carvalho Chehab 		return -ENODEV;
4608b1fa579SMauro Carvalho Chehab 
46134906633SMauro Carvalho Chehab 	if (substream)
46234906633SMauro Carvalho Chehab 		nonblock = !!(substream->f_flags & O_NONBLOCK);
46334906633SMauro Carvalho Chehab 	if (nonblock) {
46434906633SMauro Carvalho Chehab 		if (!mutex_trylock(&dev->lock))
46534906633SMauro Carvalho Chehab 			return -EAGAIN;
4669f90f537SMauro Carvalho Chehab 	} else {
4670c0d06caSMauro Carvalho Chehab 		mutex_lock(&dev->lock);
4689f90f537SMauro Carvalho Chehab 	}
4690c0d06caSMauro Carvalho Chehab 	val = em28xx_read_ac97(dev, kcontrol->private_value);
4700c0d06caSMauro Carvalho Chehab 	mutex_unlock(&dev->lock);
4710c0d06caSMauro Carvalho Chehab 	if (val < 0)
4720c0d06caSMauro Carvalho Chehab 		return val;
4730c0d06caSMauro Carvalho Chehab 
4740c0d06caSMauro Carvalho Chehab 	dprintk("%sleft vol %d, right vol %d (0x%04x) from ac97 volume control 0x%04x\n",
4750c0d06caSMauro Carvalho Chehab 		(val & 0x8000) ? "muted " : "",
4760c0d06caSMauro Carvalho Chehab 		0x1f - ((val >> 8) & 0x1f), 0x1f - (val & 0x1f),
4770c0d06caSMauro Carvalho Chehab 		val, (int)kcontrol->private_value);
4780c0d06caSMauro Carvalho Chehab 
4790c0d06caSMauro Carvalho Chehab 	value->value.integer.value[0] = 0x1f - (val & 0x1f);
480801e3659SColin Ian King 	value->value.integer.value[1] = 0x1f - ((val >> 8) & 0x1f);
4810c0d06caSMauro Carvalho Chehab 
4820c0d06caSMauro Carvalho Chehab 	return 0;
4830c0d06caSMauro Carvalho Chehab }
4840c0d06caSMauro Carvalho Chehab 
em28xx_vol_put_mute(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * value)4850c0d06caSMauro Carvalho Chehab static int em28xx_vol_put_mute(struct snd_kcontrol *kcontrol,
4860c0d06caSMauro Carvalho Chehab 			       struct snd_ctl_elem_value *value)
4870c0d06caSMauro Carvalho Chehab {
4880c0d06caSMauro Carvalho Chehab 	struct em28xx *dev = snd_kcontrol_chip(kcontrol);
4890c0d06caSMauro Carvalho Chehab 	u16 val = value->value.integer.value[0];
49034906633SMauro Carvalho Chehab 	struct snd_pcm_substream *substream = dev->adev.capture_pcm_substream;
49134906633SMauro Carvalho Chehab 	int nonblock = 0;
4920c0d06caSMauro Carvalho Chehab 	int rc;
4930c0d06caSMauro Carvalho Chehab 
4948b1fa579SMauro Carvalho Chehab 	if (dev->disconnected)
4958b1fa579SMauro Carvalho Chehab 		return -ENODEV;
4968b1fa579SMauro Carvalho Chehab 
49734906633SMauro Carvalho Chehab 	if (substream)
49834906633SMauro Carvalho Chehab 		nonblock = !!(substream->f_flags & O_NONBLOCK);
49934906633SMauro Carvalho Chehab 	if (nonblock) {
50034906633SMauro Carvalho Chehab 		if (!mutex_trylock(&dev->lock))
50134906633SMauro Carvalho Chehab 			return -EAGAIN;
5029f90f537SMauro Carvalho Chehab 	} else {
5030c0d06caSMauro Carvalho Chehab 		mutex_lock(&dev->lock);
5049f90f537SMauro Carvalho Chehab 	}
5050c0d06caSMauro Carvalho Chehab 	rc = em28xx_read_ac97(dev, kcontrol->private_value);
5060c0d06caSMauro Carvalho Chehab 	if (rc < 0)
5070c0d06caSMauro Carvalho Chehab 		goto err;
5080c0d06caSMauro Carvalho Chehab 
5090c0d06caSMauro Carvalho Chehab 	if (val)
5100c0d06caSMauro Carvalho Chehab 		rc &= 0x1f1f;
5110c0d06caSMauro Carvalho Chehab 	else
5120c0d06caSMauro Carvalho Chehab 		rc |= 0x8000;
5130c0d06caSMauro Carvalho Chehab 
5140c0d06caSMauro Carvalho Chehab 	rc = em28xx_write_ac97(dev, kcontrol->private_value, rc);
5150c0d06caSMauro Carvalho Chehab 	if (rc < 0)
5160c0d06caSMauro Carvalho Chehab 		goto err;
5170c0d06caSMauro Carvalho Chehab 
5180c0d06caSMauro Carvalho Chehab 	dprintk("%sleft vol %d, right vol %d (0x%04x) to ac97 volume control 0x%04x\n",
5190c0d06caSMauro Carvalho Chehab 		(val & 0x8000) ? "muted " : "",
5200c0d06caSMauro Carvalho Chehab 		0x1f - ((val >> 8) & 0x1f), 0x1f - (val & 0x1f),
5210c0d06caSMauro Carvalho Chehab 		val, (int)kcontrol->private_value);
5220c0d06caSMauro Carvalho Chehab 
5230c0d06caSMauro Carvalho Chehab err:
5240c0d06caSMauro Carvalho Chehab 	mutex_unlock(&dev->lock);
5250c0d06caSMauro Carvalho Chehab 	return rc;
5260c0d06caSMauro Carvalho Chehab }
5270c0d06caSMauro Carvalho Chehab 
em28xx_vol_get_mute(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * value)5280c0d06caSMauro Carvalho Chehab static int em28xx_vol_get_mute(struct snd_kcontrol *kcontrol,
5290c0d06caSMauro Carvalho Chehab 			       struct snd_ctl_elem_value *value)
5300c0d06caSMauro Carvalho Chehab {
5310c0d06caSMauro Carvalho Chehab 	struct em28xx *dev = snd_kcontrol_chip(kcontrol);
53234906633SMauro Carvalho Chehab 	struct snd_pcm_substream *substream = dev->adev.capture_pcm_substream;
53334906633SMauro Carvalho Chehab 	int nonblock = 0;
5340c0d06caSMauro Carvalho Chehab 	int val;
5350c0d06caSMauro Carvalho Chehab 
5368b1fa579SMauro Carvalho Chehab 	if (dev->disconnected)
5378b1fa579SMauro Carvalho Chehab 		return -ENODEV;
5388b1fa579SMauro Carvalho Chehab 
53934906633SMauro Carvalho Chehab 	if (substream)
54034906633SMauro Carvalho Chehab 		nonblock = !!(substream->f_flags & O_NONBLOCK);
54134906633SMauro Carvalho Chehab 	if (nonblock) {
54234906633SMauro Carvalho Chehab 		if (!mutex_trylock(&dev->lock))
54334906633SMauro Carvalho Chehab 			return -EAGAIN;
5449f90f537SMauro Carvalho Chehab 	} else {
5450c0d06caSMauro Carvalho Chehab 		mutex_lock(&dev->lock);
5469f90f537SMauro Carvalho Chehab 	}
5470c0d06caSMauro Carvalho Chehab 	val = em28xx_read_ac97(dev, kcontrol->private_value);
5480c0d06caSMauro Carvalho Chehab 	mutex_unlock(&dev->lock);
5490c0d06caSMauro Carvalho Chehab 	if (val < 0)
5500c0d06caSMauro Carvalho Chehab 		return val;
5510c0d06caSMauro Carvalho Chehab 
5520c0d06caSMauro Carvalho Chehab 	if (val & 0x8000)
5530c0d06caSMauro Carvalho Chehab 		value->value.integer.value[0] = 0;
5540c0d06caSMauro Carvalho Chehab 	else
5550c0d06caSMauro Carvalho Chehab 		value->value.integer.value[0] = 1;
5560c0d06caSMauro Carvalho Chehab 
5570c0d06caSMauro Carvalho Chehab 	dprintk("%sleft vol %d, right vol %d (0x%04x) from ac97 volume control 0x%04x\n",
5580c0d06caSMauro Carvalho Chehab 		(val & 0x8000) ? "muted " : "",
5590c0d06caSMauro Carvalho Chehab 		0x1f - ((val >> 8) & 0x1f), 0x1f - (val & 0x1f),
5600c0d06caSMauro Carvalho Chehab 		val, (int)kcontrol->private_value);
5610c0d06caSMauro Carvalho Chehab 
5620c0d06caSMauro Carvalho Chehab 	return 0;
5630c0d06caSMauro Carvalho Chehab }
5640c0d06caSMauro Carvalho Chehab 
5650c0d06caSMauro Carvalho Chehab static const DECLARE_TLV_DB_SCALE(em28xx_db_scale, -3450, 150, 0);
5660c0d06caSMauro Carvalho Chehab 
em28xx_cvol_new(struct snd_card * card,struct em28xx * dev,char * name,int id)5670c0d06caSMauro Carvalho Chehab static int em28xx_cvol_new(struct snd_card *card, struct em28xx *dev,
5680c0d06caSMauro Carvalho Chehab 			   char *name, int id)
5690c0d06caSMauro Carvalho Chehab {
5700c0d06caSMauro Carvalho Chehab 	int err;
5710c0d06caSMauro Carvalho Chehab 	char ctl_name[44];
5720c0d06caSMauro Carvalho Chehab 	struct snd_kcontrol *kctl;
5730c0d06caSMauro Carvalho Chehab 	struct snd_kcontrol_new tmp;
5740c0d06caSMauro Carvalho Chehab 
5750c0d06caSMauro Carvalho Chehab 	memset(&tmp, 0, sizeof(tmp));
576*973c960dSJulia Lawall 	tmp.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
577*973c960dSJulia Lawall 	tmp.private_value = id;
578*973c960dSJulia Lawall 	tmp.name  = ctl_name;
5790c0d06caSMauro Carvalho Chehab 
5800c0d06caSMauro Carvalho Chehab 	/* Add Mute Control */
5810c0d06caSMauro Carvalho Chehab 	sprintf(ctl_name, "%s Switch", name);
5820c0d06caSMauro Carvalho Chehab 	tmp.get  = em28xx_vol_get_mute;
5830c0d06caSMauro Carvalho Chehab 	tmp.put  = em28xx_vol_put_mute;
5840c0d06caSMauro Carvalho Chehab 	tmp.info = snd_ctl_boolean_mono_info;
5850c0d06caSMauro Carvalho Chehab 	kctl = snd_ctl_new1(&tmp, dev);
5860c0d06caSMauro Carvalho Chehab 	err = snd_ctl_add(card, kctl);
5870c0d06caSMauro Carvalho Chehab 	if (err < 0)
5880c0d06caSMauro Carvalho Chehab 		return err;
5890c0d06caSMauro Carvalho Chehab 	dprintk("Added control %s for ac97 volume control 0x%04x\n",
5900c0d06caSMauro Carvalho Chehab 		ctl_name, id);
5910c0d06caSMauro Carvalho Chehab 
5920c0d06caSMauro Carvalho Chehab 	memset(&tmp, 0, sizeof(tmp));
593*973c960dSJulia Lawall 	tmp.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
594*973c960dSJulia Lawall 	tmp.private_value = id;
595*973c960dSJulia Lawall 	tmp.name  = ctl_name;
5960c0d06caSMauro Carvalho Chehab 
5970c0d06caSMauro Carvalho Chehab 	/* Add Volume Control */
5980c0d06caSMauro Carvalho Chehab 	sprintf(ctl_name, "%s Volume", name);
5990c0d06caSMauro Carvalho Chehab 	tmp.get   = em28xx_vol_get;
6000c0d06caSMauro Carvalho Chehab 	tmp.put   = em28xx_vol_put;
6010c0d06caSMauro Carvalho Chehab 	tmp.info  = em28xx_vol_info;
602*973c960dSJulia Lawall 	tmp.tlv.p = em28xx_db_scale;
6030c0d06caSMauro Carvalho Chehab 	kctl = snd_ctl_new1(&tmp, dev);
6040c0d06caSMauro Carvalho Chehab 	err = snd_ctl_add(card, kctl);
6050c0d06caSMauro Carvalho Chehab 	if (err < 0)
6060c0d06caSMauro Carvalho Chehab 		return err;
6070c0d06caSMauro Carvalho Chehab 	dprintk("Added control %s for ac97 volume control 0x%04x\n",
6080c0d06caSMauro Carvalho Chehab 		ctl_name, id);
6090c0d06caSMauro Carvalho Chehab 
6100c0d06caSMauro Carvalho Chehab 	return 0;
6110c0d06caSMauro Carvalho Chehab }
6120c0d06caSMauro Carvalho Chehab 
6130c0d06caSMauro Carvalho Chehab /*
6140c0d06caSMauro Carvalho Chehab  * register/unregister code and data
6150c0d06caSMauro Carvalho Chehab  */
61622511cfaSJulia Lawall static const struct snd_pcm_ops snd_em28xx_pcm_capture = {
6170c0d06caSMauro Carvalho Chehab 	.open      = snd_em28xx_capture_open,
6180c0d06caSMauro Carvalho Chehab 	.close     = snd_em28xx_pcm_close,
6190c0d06caSMauro Carvalho Chehab 	.prepare   = snd_em28xx_prepare,
6200c0d06caSMauro Carvalho Chehab 	.trigger   = snd_em28xx_capture_trigger,
6210c0d06caSMauro Carvalho Chehab 	.pointer   = snd_em28xx_capture_pointer,
6220c0d06caSMauro Carvalho Chehab };
6230c0d06caSMauro Carvalho Chehab 
em28xx_audio_free_urb(struct em28xx * dev)624f5f894d1SMauro Carvalho Chehab static void em28xx_audio_free_urb(struct em28xx *dev)
625f5f894d1SMauro Carvalho Chehab {
626c6d48134SMauro Carvalho Chehab 	struct usb_device *udev = interface_to_usbdev(dev->intf);
627f5f894d1SMauro Carvalho Chehab 	int i;
628f5f894d1SMauro Carvalho Chehab 
6291b3fd2d3SMauro Carvalho Chehab 	for (i = 0; i < dev->adev.num_urb; i++) {
630f5f894d1SMauro Carvalho Chehab 		struct urb *urb = dev->adev.urb[i];
631f5f894d1SMauro Carvalho Chehab 
6321b3fd2d3SMauro Carvalho Chehab 		if (!urb)
633f5f894d1SMauro Carvalho Chehab 			continue;
634f5f894d1SMauro Carvalho Chehab 
635c6d48134SMauro Carvalho Chehab 		usb_free_coherent(udev, urb->transfer_buffer_length,
636f5f894d1SMauro Carvalho Chehab 				  dev->adev.transfer_buffer[i],
637f5f894d1SMauro Carvalho Chehab 				  urb->transfer_dma);
638f5f894d1SMauro Carvalho Chehab 
639f5f894d1SMauro Carvalho Chehab 		usb_free_urb(urb);
640f5f894d1SMauro Carvalho Chehab 	}
6411b3fd2d3SMauro Carvalho Chehab 	kfree(dev->adev.urb);
6421b3fd2d3SMauro Carvalho Chehab 	kfree(dev->adev.transfer_buffer);
6431b3fd2d3SMauro Carvalho Chehab 	dev->adev.num_urb = 0;
6441b3fd2d3SMauro Carvalho Chehab }
6451b3fd2d3SMauro Carvalho Chehab 
6461b3fd2d3SMauro Carvalho Chehab /* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */
em28xx_audio_ep_packet_size(struct usb_device * udev,struct usb_endpoint_descriptor * e)6471b3fd2d3SMauro Carvalho Chehab static int em28xx_audio_ep_packet_size(struct usb_device *udev,
6481b3fd2d3SMauro Carvalho Chehab 				       struct usb_endpoint_descriptor *e)
6491b3fd2d3SMauro Carvalho Chehab {
6501b3fd2d3SMauro Carvalho Chehab 	int size = le16_to_cpu(e->wMaxPacketSize);
6511b3fd2d3SMauro Carvalho Chehab 
6521b3fd2d3SMauro Carvalho Chehab 	if (udev->speed == USB_SPEED_HIGH)
6531b3fd2d3SMauro Carvalho Chehab 		return (size & 0x7ff) *  (1 + (((size) >> 11) & 0x03));
6541b3fd2d3SMauro Carvalho Chehab 
6551b3fd2d3SMauro Carvalho Chehab 	return size & 0x7ff;
656f5f894d1SMauro Carvalho Chehab }
657f5f894d1SMauro Carvalho Chehab 
em28xx_audio_urb_init(struct em28xx * dev)658966f4163SMauro Carvalho Chehab static int em28xx_audio_urb_init(struct em28xx *dev)
659966f4163SMauro Carvalho Chehab {
660966f4163SMauro Carvalho Chehab 	struct usb_interface *intf;
661966f4163SMauro Carvalho Chehab 	struct usb_endpoint_descriptor *e, *ep = NULL;
662c6d48134SMauro Carvalho Chehab 	struct usb_device *udev = interface_to_usbdev(dev->intf);
663966f4163SMauro Carvalho Chehab 	int                 i, ep_size, interval, num_urb, npackets;
664966f4163SMauro Carvalho Chehab 	int		    urb_size, bytes_per_transfer;
665966f4163SMauro Carvalho Chehab 	u8 alt;
666966f4163SMauro Carvalho Chehab 
667961717b4SFrank Schaefer 	if (dev->ifnum)
668966f4163SMauro Carvalho Chehab 		alt = 1;
669966f4163SMauro Carvalho Chehab 	else
670966f4163SMauro Carvalho Chehab 		alt = 7;
671966f4163SMauro Carvalho Chehab 
672c6d48134SMauro Carvalho Chehab 	intf = usb_ifnum_to_if(udev, dev->ifnum);
673966f4163SMauro Carvalho Chehab 
674966f4163SMauro Carvalho Chehab 	if (intf->num_altsetting <= alt) {
67529b05e22SMauro Carvalho Chehab 		dev_err(&dev->intf->dev, "alt %d doesn't exist on interface %d\n",
676961717b4SFrank Schaefer 			dev->ifnum, alt);
677966f4163SMauro Carvalho Chehab 		return -ENODEV;
678966f4163SMauro Carvalho Chehab 	}
679966f4163SMauro Carvalho Chehab 
680966f4163SMauro Carvalho Chehab 	for (i = 0; i < intf->altsetting[alt].desc.bNumEndpoints; i++) {
681966f4163SMauro Carvalho Chehab 		e = &intf->altsetting[alt].endpoint[i].desc;
682966f4163SMauro Carvalho Chehab 		if (!usb_endpoint_dir_in(e))
683966f4163SMauro Carvalho Chehab 			continue;
684966f4163SMauro Carvalho Chehab 		if (e->bEndpointAddress == EM28XX_EP_AUDIO) {
685966f4163SMauro Carvalho Chehab 			ep = e;
686966f4163SMauro Carvalho Chehab 			break;
687966f4163SMauro Carvalho Chehab 		}
688966f4163SMauro Carvalho Chehab 	}
689966f4163SMauro Carvalho Chehab 
690966f4163SMauro Carvalho Chehab 	if (!ep) {
69129b05e22SMauro Carvalho Chehab 		dev_err(&dev->intf->dev, "Couldn't find an audio endpoint");
692966f4163SMauro Carvalho Chehab 		return -ENODEV;
693966f4163SMauro Carvalho Chehab 	}
694966f4163SMauro Carvalho Chehab 
695c6d48134SMauro Carvalho Chehab 	ep_size = em28xx_audio_ep_packet_size(udev, ep);
696966f4163SMauro Carvalho Chehab 	interval = 1 << (ep->bInterval - 1);
697966f4163SMauro Carvalho Chehab 
69829b05e22SMauro Carvalho Chehab 	dev_info(&dev->intf->dev,
699ce8591ffSMauro Carvalho Chehab 		 "Endpoint 0x%02x %s on intf %d alt %d interval = %d, size %d\n",
700c6d48134SMauro Carvalho Chehab 		 EM28XX_EP_AUDIO, usb_speed_string(udev->speed),
701ce8591ffSMauro Carvalho Chehab 		 dev->ifnum, alt, interval, ep_size);
702966f4163SMauro Carvalho Chehab 
703966f4163SMauro Carvalho Chehab 	/* Calculate the number and size of URBs to better fit the audio samples */
704966f4163SMauro Carvalho Chehab 
705966f4163SMauro Carvalho Chehab 	/*
706966f4163SMauro Carvalho Chehab 	 * Estimate the number of bytes per DMA transfer.
707966f4163SMauro Carvalho Chehab 	 *
708966f4163SMauro Carvalho Chehab 	 * This is given by the bit rate (for now, only 48000 Hz) multiplied
709966f4163SMauro Carvalho Chehab 	 * by 2 channels and 2 bytes/sample divided by the number of microframe
710966f4163SMauro Carvalho Chehab 	 * intervals and by the microframe rate (125 us)
711966f4163SMauro Carvalho Chehab 	 */
712966f4163SMauro Carvalho Chehab 	bytes_per_transfer = DIV_ROUND_UP(48000 * 2 * 2, 125 * interval);
713966f4163SMauro Carvalho Chehab 
714966f4163SMauro Carvalho Chehab 	/*
715966f4163SMauro Carvalho Chehab 	 * Estimate the number of transfer URBs. Don't let it go past the
716966f4163SMauro Carvalho Chehab 	 * maximum number of URBs that is known to be supported by the device.
717966f4163SMauro Carvalho Chehab 	 */
718966f4163SMauro Carvalho Chehab 	num_urb = DIV_ROUND_UP(bytes_per_transfer, ep_size);
719966f4163SMauro Carvalho Chehab 	if (num_urb > EM28XX_MAX_AUDIO_BUFS)
720966f4163SMauro Carvalho Chehab 		num_urb = EM28XX_MAX_AUDIO_BUFS;
721966f4163SMauro Carvalho Chehab 
722966f4163SMauro Carvalho Chehab 	/*
723966f4163SMauro Carvalho Chehab 	 * Now that we know the number of bytes per transfer and the number of
724966f4163SMauro Carvalho Chehab 	 * URBs, estimate the typical size of an URB, in order to adjust the
725966f4163SMauro Carvalho Chehab 	 * minimal number of packets.
726966f4163SMauro Carvalho Chehab 	 */
727966f4163SMauro Carvalho Chehab 	urb_size = bytes_per_transfer / num_urb;
728966f4163SMauro Carvalho Chehab 
729966f4163SMauro Carvalho Chehab 	/*
730966f4163SMauro Carvalho Chehab 	 * Now, calculate the amount of audio packets to be filled on each
731966f4163SMauro Carvalho Chehab 	 * URB. In order to preserve the old behaviour, use a minimal
732966f4163SMauro Carvalho Chehab 	 * threshold for this value.
733966f4163SMauro Carvalho Chehab 	 */
734966f4163SMauro Carvalho Chehab 	npackets = EM28XX_MIN_AUDIO_PACKETS;
735966f4163SMauro Carvalho Chehab 	if (urb_size > ep_size * npackets)
736966f4163SMauro Carvalho Chehab 		npackets = DIV_ROUND_UP(urb_size, ep_size);
737966f4163SMauro Carvalho Chehab 
73829b05e22SMauro Carvalho Chehab 	dev_info(&dev->intf->dev,
739ce8591ffSMauro Carvalho Chehab 		 "Number of URBs: %d, with %d packets and %d size\n",
740966f4163SMauro Carvalho Chehab 		 num_urb, npackets, urb_size);
741966f4163SMauro Carvalho Chehab 
742a02b9c23SMauro Carvalho Chehab 	/* Estimate the bytes per period */
743a02b9c23SMauro Carvalho Chehab 	dev->adev.period = urb_size * npackets;
744a02b9c23SMauro Carvalho Chehab 
745966f4163SMauro Carvalho Chehab 	/* Allocate space to store the number of URBs to be used */
746966f4163SMauro Carvalho Chehab 
747966f4163SMauro Carvalho Chehab 	dev->adev.transfer_buffer = kcalloc(num_urb,
748966f4163SMauro Carvalho Chehab 					    sizeof(*dev->adev.transfer_buffer),
749fc8af4fbSSebastian Andrzej Siewior 					    GFP_KERNEL);
7509f90f537SMauro Carvalho Chehab 	if (!dev->adev.transfer_buffer)
751966f4163SMauro Carvalho Chehab 		return -ENOMEM;
752966f4163SMauro Carvalho Chehab 
753fc8af4fbSSebastian Andrzej Siewior 	dev->adev.urb = kcalloc(num_urb, sizeof(*dev->adev.urb), GFP_KERNEL);
754966f4163SMauro Carvalho Chehab 	if (!dev->adev.urb) {
755966f4163SMauro Carvalho Chehab 		kfree(dev->adev.transfer_buffer);
756966f4163SMauro Carvalho Chehab 		return -ENOMEM;
757966f4163SMauro Carvalho Chehab 	}
758966f4163SMauro Carvalho Chehab 
759966f4163SMauro Carvalho Chehab 	/* Alloc memory for each URB and for each transfer buffer */
760966f4163SMauro Carvalho Chehab 	dev->adev.num_urb = num_urb;
761966f4163SMauro Carvalho Chehab 	for (i = 0; i < num_urb; i++) {
762966f4163SMauro Carvalho Chehab 		struct urb *urb;
763966f4163SMauro Carvalho Chehab 		int j, k;
764966f4163SMauro Carvalho Chehab 		void *buf;
765966f4163SMauro Carvalho Chehab 
766fc8af4fbSSebastian Andrzej Siewior 		urb = usb_alloc_urb(npackets, GFP_KERNEL);
767966f4163SMauro Carvalho Chehab 		if (!urb) {
768966f4163SMauro Carvalho Chehab 			em28xx_audio_free_urb(dev);
769966f4163SMauro Carvalho Chehab 			return -ENOMEM;
770966f4163SMauro Carvalho Chehab 		}
771966f4163SMauro Carvalho Chehab 		dev->adev.urb[i] = urb;
772966f4163SMauro Carvalho Chehab 
773fc8af4fbSSebastian Andrzej Siewior 		buf = usb_alloc_coherent(udev, npackets * ep_size, GFP_KERNEL,
774966f4163SMauro Carvalho Chehab 					 &urb->transfer_dma);
775966f4163SMauro Carvalho Chehab 		if (!buf) {
77629b05e22SMauro Carvalho Chehab 			dev_err(&dev->intf->dev,
777ce8591ffSMauro Carvalho Chehab 				"usb_alloc_coherent failed!\n");
778966f4163SMauro Carvalho Chehab 			em28xx_audio_free_urb(dev);
779966f4163SMauro Carvalho Chehab 			return -ENOMEM;
780966f4163SMauro Carvalho Chehab 		}
781966f4163SMauro Carvalho Chehab 		dev->adev.transfer_buffer[i] = buf;
782966f4163SMauro Carvalho Chehab 
783c6d48134SMauro Carvalho Chehab 		urb->dev = udev;
784966f4163SMauro Carvalho Chehab 		urb->context = dev;
785c6d48134SMauro Carvalho Chehab 		urb->pipe = usb_rcvisocpipe(udev, EM28XX_EP_AUDIO);
786966f4163SMauro Carvalho Chehab 		urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
787966f4163SMauro Carvalho Chehab 		urb->transfer_buffer = buf;
788966f4163SMauro Carvalho Chehab 		urb->interval = interval;
789966f4163SMauro Carvalho Chehab 		urb->complete = em28xx_audio_isocirq;
790966f4163SMauro Carvalho Chehab 		urb->number_of_packets = npackets;
791966f4163SMauro Carvalho Chehab 		urb->transfer_buffer_length = ep_size * npackets;
792966f4163SMauro Carvalho Chehab 
793966f4163SMauro Carvalho Chehab 		for (j = k = 0; j < npackets; j++, k += ep_size) {
794966f4163SMauro Carvalho Chehab 			urb->iso_frame_desc[j].offset = k;
795966f4163SMauro Carvalho Chehab 			urb->iso_frame_desc[j].length = ep_size;
796966f4163SMauro Carvalho Chehab 		}
797966f4163SMauro Carvalho Chehab 	}
798966f4163SMauro Carvalho Chehab 
799966f4163SMauro Carvalho Chehab 	return 0;
800966f4163SMauro Carvalho Chehab }
801966f4163SMauro Carvalho Chehab 
em28xx_audio_init(struct em28xx * dev)8020c0d06caSMauro Carvalho Chehab static int em28xx_audio_init(struct em28xx *dev)
8030c0d06caSMauro Carvalho Chehab {
8040c0d06caSMauro Carvalho Chehab 	struct em28xx_audio *adev = &dev->adev;
805c6d48134SMauro Carvalho Chehab 	struct usb_device *udev = interface_to_usbdev(dev->intf);
8060c0d06caSMauro Carvalho Chehab 	struct snd_pcm      *pcm;
8070c0d06caSMauro Carvalho Chehab 	struct snd_card     *card;
8080c0d06caSMauro Carvalho Chehab 	static int          devnr;
809966f4163SMauro Carvalho Chehab 	int		    err;
8100c0d06caSMauro Carvalho Chehab 
811c5874208SFrank Schaefer 	if (dev->usb_audio_type != EM28XX_USB_AUDIO_VENDOR) {
8129f90f537SMauro Carvalho Chehab 		/*
8139f90f537SMauro Carvalho Chehab 		 * This device does not support the extension (in this case
8149f90f537SMauro Carvalho Chehab 		 * the device is expecting the snd-usb-audio module or
8159f90f537SMauro Carvalho Chehab 		 * doesn't have analog audio support at all)
8169f90f537SMauro Carvalho Chehab 		 */
8170c0d06caSMauro Carvalho Chehab 		return 0;
8180c0d06caSMauro Carvalho Chehab 	}
8190c0d06caSMauro Carvalho Chehab 
82029b05e22SMauro Carvalho Chehab 	dev_info(&dev->intf->dev, "Binding audio extension\n");
8219634614fSMauro Carvalho Chehab 
82247677e51SMauro Carvalho Chehab 	kref_get(&dev->ref);
82347677e51SMauro Carvalho Chehab 
82429b05e22SMauro Carvalho Chehab 	dev_info(&dev->intf->dev,
825ce8591ffSMauro Carvalho Chehab 		 "em28xx-audio.c: Copyright (C) 2006 Markus Rechberger\n");
82629b05e22SMauro Carvalho Chehab 	dev_info(&dev->intf->dev,
827ce8591ffSMauro Carvalho Chehab 		 "em28xx-audio.c: Copyright (C) 2007-2016 Mauro Carvalho Chehab\n");
8280c0d06caSMauro Carvalho Chehab 
82929b05e22SMauro Carvalho Chehab 	err = snd_card_new(&dev->intf->dev, index[devnr], "Em28xx Audio",
830e7356888STakashi Iwai 			   THIS_MODULE, 0, &card);
8310c0d06caSMauro Carvalho Chehab 	if (err < 0)
8320c0d06caSMauro Carvalho Chehab 		return err;
8330c0d06caSMauro Carvalho Chehab 
8340c0d06caSMauro Carvalho Chehab 	spin_lock_init(&adev->slock);
8351b3fd2d3SMauro Carvalho Chehab 	adev->sndcard = card;
836c6d48134SMauro Carvalho Chehab 	adev->udev = udev;
8371b3fd2d3SMauro Carvalho Chehab 
8380c0d06caSMauro Carvalho Chehab 	err = snd_pcm_new(card, "Em28xx Audio", 0, 0, 1, &pcm);
8390cd03a0cSMauro Carvalho Chehab 	if (err < 0)
8400cd03a0cSMauro Carvalho Chehab 		goto card_free;
8410c0d06caSMauro Carvalho Chehab 
8420c0d06caSMauro Carvalho Chehab 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_em28xx_pcm_capture);
8432abb1b2dSTakashi Iwai 	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
8440c0d06caSMauro Carvalho Chehab 	pcm->info_flags = 0;
8450c0d06caSMauro Carvalho Chehab 	pcm->private_data = dev;
846cc1e6315SMauro Carvalho Chehab 	strscpy(pcm->name, "Empia 28xx Capture", sizeof(pcm->name));
8470c0d06caSMauro Carvalho Chehab 
848cc1e6315SMauro Carvalho Chehab 	strscpy(card->driver, "Em28xx-Audio", sizeof(card->driver));
849cc1e6315SMauro Carvalho Chehab 	strscpy(card->shortname, "Em28xx Audio", sizeof(card->shortname));
850cc1e6315SMauro Carvalho Chehab 	strscpy(card->longname, "Empia Em28xx Audio", sizeof(card->longname));
8510c0d06caSMauro Carvalho Chehab 
852a5c075cfSFrank Schaefer 	INIT_WORK(&adev->wq_trigger, audio_trigger);
8530c0d06caSMauro Carvalho Chehab 
8540c0d06caSMauro Carvalho Chehab 	if (dev->audio_mode.ac97 != EM28XX_NO_AC97) {
8550c0d06caSMauro Carvalho Chehab 		em28xx_cvol_new(card, dev, "Video", AC97_VIDEO);
8560c0d06caSMauro Carvalho Chehab 		em28xx_cvol_new(card, dev, "Line In", AC97_LINE);
8570c0d06caSMauro Carvalho Chehab 		em28xx_cvol_new(card, dev, "Phone", AC97_PHONE);
8580c0d06caSMauro Carvalho Chehab 		em28xx_cvol_new(card, dev, "Microphone", AC97_MIC);
8590c0d06caSMauro Carvalho Chehab 		em28xx_cvol_new(card, dev, "CD", AC97_CD);
8600c0d06caSMauro Carvalho Chehab 		em28xx_cvol_new(card, dev, "AUX", AC97_AUX);
8610c0d06caSMauro Carvalho Chehab 		em28xx_cvol_new(card, dev, "PCM", AC97_PCM);
8620c0d06caSMauro Carvalho Chehab 
8630c0d06caSMauro Carvalho Chehab 		em28xx_cvol_new(card, dev, "Master", AC97_MASTER);
8640c0d06caSMauro Carvalho Chehab 		em28xx_cvol_new(card, dev, "Line", AC97_HEADPHONE);
8650c0d06caSMauro Carvalho Chehab 		em28xx_cvol_new(card, dev, "Mono", AC97_MASTER_MONO);
8660c0d06caSMauro Carvalho Chehab 		em28xx_cvol_new(card, dev, "LFE", AC97_CENTER_LFE_MASTER);
8670c0d06caSMauro Carvalho Chehab 		em28xx_cvol_new(card, dev, "Surround", AC97_SURROUND_MASTER);
8680c0d06caSMauro Carvalho Chehab 	}
8690c0d06caSMauro Carvalho Chehab 
870966f4163SMauro Carvalho Chehab 	err = em28xx_audio_urb_init(dev);
8710cd03a0cSMauro Carvalho Chehab 	if (err)
8720cd03a0cSMauro Carvalho Chehab 		goto card_free;
873d2849fa5SMauro Carvalho Chehab 
8740c0d06caSMauro Carvalho Chehab 	err = snd_card_register(card);
8750cd03a0cSMauro Carvalho Chehab 	if (err < 0)
8760cd03a0cSMauro Carvalho Chehab 		goto urb_free;
8770c0d06caSMauro Carvalho Chehab 
87829b05e22SMauro Carvalho Chehab 	dev_info(&dev->intf->dev, "Audio extension successfully initialized\n");
8790c0d06caSMauro Carvalho Chehab 	return 0;
8800cd03a0cSMauro Carvalho Chehab 
8810cd03a0cSMauro Carvalho Chehab urb_free:
8820cd03a0cSMauro Carvalho Chehab 	em28xx_audio_free_urb(dev);
8830cd03a0cSMauro Carvalho Chehab 
8840cd03a0cSMauro Carvalho Chehab card_free:
8850cd03a0cSMauro Carvalho Chehab 	snd_card_free(card);
886452f236fSMauro Carvalho Chehab 	adev->sndcard = NULL;
8870cd03a0cSMauro Carvalho Chehab 
8880cd03a0cSMauro Carvalho Chehab 	return err;
8890c0d06caSMauro Carvalho Chehab }
8900c0d06caSMauro Carvalho Chehab 
em28xx_audio_fini(struct em28xx * dev)8910c0d06caSMauro Carvalho Chehab static int em28xx_audio_fini(struct em28xx *dev)
8920c0d06caSMauro Carvalho Chehab {
8939f90f537SMauro Carvalho Chehab 	if (!dev)
8940c0d06caSMauro Carvalho Chehab 		return 0;
8950c0d06caSMauro Carvalho Chehab 
896c5874208SFrank Schaefer 	if (dev->usb_audio_type != EM28XX_USB_AUDIO_VENDOR) {
8979f90f537SMauro Carvalho Chehab 		/*
8989f90f537SMauro Carvalho Chehab 		 * This device does not support the extension (in this case
8999f90f537SMauro Carvalho Chehab 		 * the device is expecting the snd-usb-audio module or
9009f90f537SMauro Carvalho Chehab 		 * doesn't have analog audio support at all)
9019f90f537SMauro Carvalho Chehab 		 */
9020c0d06caSMauro Carvalho Chehab 		return 0;
9030c0d06caSMauro Carvalho Chehab 	}
9040c0d06caSMauro Carvalho Chehab 
90529b05e22SMauro Carvalho Chehab 	dev_info(&dev->intf->dev, "Closing audio extension\n");
906aa929ad7SMauro Carvalho Chehab 
907452f236fSMauro Carvalho Chehab 	if (dev->adev.sndcard) {
9081fe2e3bfSMauro Carvalho Chehab 		snd_card_disconnect(dev->adev.sndcard);
909a5c075cfSFrank Schaefer 		flush_work(&dev->adev.wq_trigger);
910b49eb2bdSMauro Carvalho Chehab 
911f5f894d1SMauro Carvalho Chehab 		em28xx_audio_free_urb(dev);
912f5f894d1SMauro Carvalho Chehab 
9130c0d06caSMauro Carvalho Chehab 		snd_card_free(dev->adev.sndcard);
9140c0d06caSMauro Carvalho Chehab 		dev->adev.sndcard = NULL;
9150c0d06caSMauro Carvalho Chehab 	}
9160c0d06caSMauro Carvalho Chehab 
91747677e51SMauro Carvalho Chehab 	kref_put(&dev->ref, em28xx_free_device);
9180c0d06caSMauro Carvalho Chehab 	return 0;
9190c0d06caSMauro Carvalho Chehab }
9200c0d06caSMauro Carvalho Chehab 
em28xx_audio_suspend(struct em28xx * dev)9216d746f91SShuah Khan static int em28xx_audio_suspend(struct em28xx *dev)
9226d746f91SShuah Khan {
9239f90f537SMauro Carvalho Chehab 	if (!dev)
9246d746f91SShuah Khan 		return 0;
9256d746f91SShuah Khan 
926c5874208SFrank Schaefer 	if (dev->usb_audio_type != EM28XX_USB_AUDIO_VENDOR)
9276d746f91SShuah Khan 		return 0;
9286d746f91SShuah Khan 
92929b05e22SMauro Carvalho Chehab 	dev_info(&dev->intf->dev, "Suspending audio extension\n");
9306d746f91SShuah Khan 	em28xx_deinit_isoc_audio(dev);
931a5c075cfSFrank Schaefer 	atomic_set(&dev->adev.stream_started, 0);
9326d746f91SShuah Khan 	return 0;
9336d746f91SShuah Khan }
9346d746f91SShuah Khan 
em28xx_audio_resume(struct em28xx * dev)9356d746f91SShuah Khan static int em28xx_audio_resume(struct em28xx *dev)
9366d746f91SShuah Khan {
9379f90f537SMauro Carvalho Chehab 	if (!dev)
9386d746f91SShuah Khan 		return 0;
9396d746f91SShuah Khan 
940c5874208SFrank Schaefer 	if (dev->usb_audio_type != EM28XX_USB_AUDIO_VENDOR)
9416d746f91SShuah Khan 		return 0;
9426d746f91SShuah Khan 
94329b05e22SMauro Carvalho Chehab 	dev_info(&dev->intf->dev, "Resuming audio extension\n");
9446d746f91SShuah Khan 	/* Nothing to do other than schedule_work() ?? */
945a5c075cfSFrank Schaefer 	schedule_work(&dev->adev.wq_trigger);
9460c0d06caSMauro Carvalho Chehab 	return 0;
9470c0d06caSMauro Carvalho Chehab }
9480c0d06caSMauro Carvalho Chehab 
9490c0d06caSMauro Carvalho Chehab static struct em28xx_ops audio_ops = {
9500c0d06caSMauro Carvalho Chehab 	.id   = EM28XX_AUDIO,
9510c0d06caSMauro Carvalho Chehab 	.name = "Em28xx Audio Extension",
9520c0d06caSMauro Carvalho Chehab 	.init = em28xx_audio_init,
9530c0d06caSMauro Carvalho Chehab 	.fini = em28xx_audio_fini,
9546d746f91SShuah Khan 	.suspend = em28xx_audio_suspend,
9556d746f91SShuah Khan 	.resume = em28xx_audio_resume,
9560c0d06caSMauro Carvalho Chehab };
9570c0d06caSMauro Carvalho Chehab 
em28xx_alsa_register(void)9580c0d06caSMauro Carvalho Chehab static int __init em28xx_alsa_register(void)
9590c0d06caSMauro Carvalho Chehab {
9600c0d06caSMauro Carvalho Chehab 	return em28xx_register_extension(&audio_ops);
9610c0d06caSMauro Carvalho Chehab }
9620c0d06caSMauro Carvalho Chehab 
em28xx_alsa_unregister(void)9630c0d06caSMauro Carvalho Chehab static void __exit em28xx_alsa_unregister(void)
9640c0d06caSMauro Carvalho Chehab {
9650c0d06caSMauro Carvalho Chehab 	em28xx_unregister_extension(&audio_ops);
9660c0d06caSMauro Carvalho Chehab }
9670c0d06caSMauro Carvalho Chehab 
968f22e9e71SMauro Carvalho Chehab MODULE_LICENSE("GPL v2");
9690c0d06caSMauro Carvalho Chehab MODULE_AUTHOR("Markus Rechberger <mrechberger@gmail.com>");
97037e59f87SMauro Carvalho Chehab MODULE_AUTHOR("Mauro Carvalho Chehab");
971d8992b09SMauro Carvalho Chehab MODULE_DESCRIPTION(DRIVER_DESC " - audio interface");
972d8992b09SMauro Carvalho Chehab MODULE_VERSION(EM28XX_VERSION);
9730c0d06caSMauro Carvalho Chehab 
9740c0d06caSMauro Carvalho Chehab module_init(em28xx_alsa_register);
9750c0d06caSMauro Carvalho Chehab module_exit(em28xx_alsa_unregister);
976