1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
20c0d06caSMauro Carvalho Chehab /*
30c0d06caSMauro Carvalho Chehab * Conexant Cx231xx audio extension
40c0d06caSMauro Carvalho Chehab *
50c0d06caSMauro Carvalho Chehab * Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
60c0d06caSMauro Carvalho Chehab * Based on em28xx driver
70c0d06caSMauro Carvalho Chehab */
80c0d06caSMauro Carvalho Chehab
9589dadf2SMauro Carvalho Chehab #include "cx231xx.h"
100c0d06caSMauro Carvalho Chehab #include <linux/kernel.h>
110c0d06caSMauro Carvalho Chehab #include <linux/init.h>
120c0d06caSMauro Carvalho Chehab #include <linux/sound.h>
130c0d06caSMauro Carvalho Chehab #include <linux/spinlock.h>
140c0d06caSMauro Carvalho Chehab #include <linux/soundcard.h>
150c0d06caSMauro Carvalho Chehab #include <linux/slab.h>
160c0d06caSMauro Carvalho Chehab #include <linux/module.h>
170c0d06caSMauro Carvalho Chehab #include <sound/core.h>
180c0d06caSMauro Carvalho Chehab #include <sound/pcm.h>
190c0d06caSMauro Carvalho Chehab #include <sound/pcm_params.h>
200c0d06caSMauro Carvalho Chehab #include <sound/info.h>
210c0d06caSMauro Carvalho Chehab #include <sound/initval.h>
220c0d06caSMauro Carvalho Chehab #include <sound/control.h>
230c0d06caSMauro Carvalho Chehab #include <media/v4l2-common.h>
240c0d06caSMauro Carvalho Chehab
250c0d06caSMauro Carvalho Chehab static int debug;
260c0d06caSMauro Carvalho Chehab module_param(debug, int, 0644);
270c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(debug, "activates debug info");
280c0d06caSMauro Carvalho Chehab
290c0d06caSMauro Carvalho Chehab static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
300c0d06caSMauro Carvalho Chehab
cx231xx_isoc_audio_deinit(struct cx231xx * dev)310c0d06caSMauro Carvalho Chehab static int cx231xx_isoc_audio_deinit(struct cx231xx *dev)
320c0d06caSMauro Carvalho Chehab {
330c0d06caSMauro Carvalho Chehab int i;
340c0d06caSMauro Carvalho Chehab
35336fea92SMauro Carvalho Chehab dev_dbg(dev->dev, "Stopping isoc\n");
360c0d06caSMauro Carvalho Chehab
370c0d06caSMauro Carvalho Chehab for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
380c0d06caSMauro Carvalho Chehab if (dev->adev.urb[i]) {
390c0d06caSMauro Carvalho Chehab if (!irqs_disabled())
400c0d06caSMauro Carvalho Chehab usb_kill_urb(dev->adev.urb[i]);
410c0d06caSMauro Carvalho Chehab else
420c0d06caSMauro Carvalho Chehab usb_unlink_urb(dev->adev.urb[i]);
430c0d06caSMauro Carvalho Chehab
440c0d06caSMauro Carvalho Chehab usb_free_urb(dev->adev.urb[i]);
450c0d06caSMauro Carvalho Chehab dev->adev.urb[i] = NULL;
460c0d06caSMauro Carvalho Chehab
470c0d06caSMauro Carvalho Chehab kfree(dev->adev.transfer_buffer[i]);
480c0d06caSMauro Carvalho Chehab dev->adev.transfer_buffer[i] = NULL;
490c0d06caSMauro Carvalho Chehab }
500c0d06caSMauro Carvalho Chehab }
510c0d06caSMauro Carvalho Chehab
520c0d06caSMauro Carvalho Chehab return 0;
530c0d06caSMauro Carvalho Chehab }
540c0d06caSMauro Carvalho Chehab
cx231xx_bulk_audio_deinit(struct cx231xx * dev)550c0d06caSMauro Carvalho Chehab static int cx231xx_bulk_audio_deinit(struct cx231xx *dev)
560c0d06caSMauro Carvalho Chehab {
570c0d06caSMauro Carvalho Chehab int i;
580c0d06caSMauro Carvalho Chehab
59336fea92SMauro Carvalho Chehab dev_dbg(dev->dev, "Stopping bulk\n");
600c0d06caSMauro Carvalho Chehab
610c0d06caSMauro Carvalho Chehab for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
620c0d06caSMauro Carvalho Chehab if (dev->adev.urb[i]) {
630c0d06caSMauro Carvalho Chehab if (!irqs_disabled())
640c0d06caSMauro Carvalho Chehab usb_kill_urb(dev->adev.urb[i]);
650c0d06caSMauro Carvalho Chehab else
660c0d06caSMauro Carvalho Chehab usb_unlink_urb(dev->adev.urb[i]);
670c0d06caSMauro Carvalho Chehab
680c0d06caSMauro Carvalho Chehab usb_free_urb(dev->adev.urb[i]);
690c0d06caSMauro Carvalho Chehab dev->adev.urb[i] = NULL;
700c0d06caSMauro Carvalho Chehab
710c0d06caSMauro Carvalho Chehab kfree(dev->adev.transfer_buffer[i]);
720c0d06caSMauro Carvalho Chehab dev->adev.transfer_buffer[i] = NULL;
730c0d06caSMauro Carvalho Chehab }
740c0d06caSMauro Carvalho Chehab }
750c0d06caSMauro Carvalho Chehab
760c0d06caSMauro Carvalho Chehab return 0;
770c0d06caSMauro Carvalho Chehab }
780c0d06caSMauro Carvalho Chehab
cx231xx_audio_isocirq(struct urb * urb)790c0d06caSMauro Carvalho Chehab static void cx231xx_audio_isocirq(struct urb *urb)
800c0d06caSMauro Carvalho Chehab {
810c0d06caSMauro Carvalho Chehab struct cx231xx *dev = urb->context;
820c0d06caSMauro Carvalho Chehab int i;
830c0d06caSMauro Carvalho Chehab unsigned int oldptr;
840c0d06caSMauro Carvalho Chehab int period_elapsed = 0;
850c0d06caSMauro Carvalho Chehab int status;
860c0d06caSMauro Carvalho Chehab unsigned char *cp;
870c0d06caSMauro Carvalho Chehab unsigned int stride;
880c0d06caSMauro Carvalho Chehab struct snd_pcm_substream *substream;
890c0d06caSMauro Carvalho Chehab struct snd_pcm_runtime *runtime;
900c0d06caSMauro Carvalho Chehab
910c0d06caSMauro Carvalho Chehab if (dev->state & DEV_DISCONNECTED)
920c0d06caSMauro Carvalho Chehab return;
930c0d06caSMauro Carvalho Chehab
940c0d06caSMauro Carvalho Chehab switch (urb->status) {
950c0d06caSMauro Carvalho Chehab case 0: /* success */
960c0d06caSMauro Carvalho Chehab case -ETIMEDOUT: /* NAK */
970c0d06caSMauro Carvalho Chehab break;
980c0d06caSMauro Carvalho Chehab case -ECONNRESET: /* kill */
990c0d06caSMauro Carvalho Chehab case -ENOENT:
1000c0d06caSMauro Carvalho Chehab case -ESHUTDOWN:
1010c0d06caSMauro Carvalho Chehab return;
1020c0d06caSMauro Carvalho Chehab default: /* error */
103854bb4ecSColin Ian King dev_dbg(dev->dev, "urb completion error %d.\n",
10456d8a3b0SMauro Carvalho Chehab urb->status);
1050c0d06caSMauro Carvalho Chehab break;
1060c0d06caSMauro Carvalho Chehab }
1070c0d06caSMauro Carvalho Chehab
1080c0d06caSMauro Carvalho Chehab if (atomic_read(&dev->stream_started) == 0)
1090c0d06caSMauro Carvalho Chehab return;
1100c0d06caSMauro Carvalho Chehab
1110c0d06caSMauro Carvalho Chehab if (dev->adev.capture_pcm_substream) {
1120c0d06caSMauro Carvalho Chehab substream = dev->adev.capture_pcm_substream;
1130c0d06caSMauro Carvalho Chehab runtime = substream->runtime;
1140c0d06caSMauro Carvalho Chehab stride = runtime->frame_bits >> 3;
1150c0d06caSMauro Carvalho Chehab
1160c0d06caSMauro Carvalho Chehab for (i = 0; i < urb->number_of_packets; i++) {
117aa53bf0bSSebastian Andrzej Siewior unsigned long flags;
1180c0d06caSMauro Carvalho Chehab int length = urb->iso_frame_desc[i].actual_length /
1190c0d06caSMauro Carvalho Chehab stride;
1200c0d06caSMauro Carvalho Chehab cp = (unsigned char *)urb->transfer_buffer +
1210c0d06caSMauro Carvalho Chehab urb->iso_frame_desc[i].offset;
1220c0d06caSMauro Carvalho Chehab
1230c0d06caSMauro Carvalho Chehab if (!length)
1240c0d06caSMauro Carvalho Chehab continue;
1250c0d06caSMauro Carvalho Chehab
1260c0d06caSMauro Carvalho Chehab oldptr = dev->adev.hwptr_done_capture;
1270c0d06caSMauro Carvalho Chehab if (oldptr + length >= runtime->buffer_size) {
1280c0d06caSMauro Carvalho Chehab unsigned int cnt;
1290c0d06caSMauro Carvalho Chehab
1300c0d06caSMauro Carvalho Chehab cnt = runtime->buffer_size - oldptr;
1310c0d06caSMauro Carvalho Chehab memcpy(runtime->dma_area + oldptr * stride, cp,
1320c0d06caSMauro Carvalho Chehab cnt * stride);
1330c0d06caSMauro Carvalho Chehab memcpy(runtime->dma_area, cp + cnt * stride,
1340c0d06caSMauro Carvalho Chehab length * stride - cnt * stride);
1350c0d06caSMauro Carvalho Chehab } else {
1360c0d06caSMauro Carvalho Chehab memcpy(runtime->dma_area + oldptr * stride, cp,
1370c0d06caSMauro Carvalho Chehab length * stride);
1380c0d06caSMauro Carvalho Chehab }
1390c0d06caSMauro Carvalho Chehab
140aa53bf0bSSebastian Andrzej Siewior snd_pcm_stream_lock_irqsave(substream, flags);
1410c0d06caSMauro Carvalho Chehab
1420c0d06caSMauro Carvalho Chehab dev->adev.hwptr_done_capture += length;
1430c0d06caSMauro Carvalho Chehab if (dev->adev.hwptr_done_capture >=
1440c0d06caSMauro Carvalho Chehab runtime->buffer_size)
1450c0d06caSMauro Carvalho Chehab dev->adev.hwptr_done_capture -=
1460c0d06caSMauro Carvalho Chehab runtime->buffer_size;
1470c0d06caSMauro Carvalho Chehab
1480c0d06caSMauro Carvalho Chehab dev->adev.capture_transfer_done += length;
1490c0d06caSMauro Carvalho Chehab if (dev->adev.capture_transfer_done >=
1500c0d06caSMauro Carvalho Chehab runtime->period_size) {
1510c0d06caSMauro Carvalho Chehab dev->adev.capture_transfer_done -=
1520c0d06caSMauro Carvalho Chehab runtime->period_size;
1530c0d06caSMauro Carvalho Chehab period_elapsed = 1;
1540c0d06caSMauro Carvalho Chehab }
155aa53bf0bSSebastian Andrzej Siewior snd_pcm_stream_unlock_irqrestore(substream, flags);
1560c0d06caSMauro Carvalho Chehab }
1570c0d06caSMauro Carvalho Chehab if (period_elapsed)
1580c0d06caSMauro Carvalho Chehab snd_pcm_period_elapsed(substream);
1590c0d06caSMauro Carvalho Chehab }
1600c0d06caSMauro Carvalho Chehab urb->status = 0;
1610c0d06caSMauro Carvalho Chehab
1620c0d06caSMauro Carvalho Chehab status = usb_submit_urb(urb, GFP_ATOMIC);
1630c0d06caSMauro Carvalho Chehab if (status < 0) {
164336fea92SMauro Carvalho Chehab dev_err(dev->dev,
165b7085c08SMauro Carvalho Chehab "resubmit of audio urb failed (error=%i)\n",
1660c0d06caSMauro Carvalho Chehab status);
1670c0d06caSMauro Carvalho Chehab }
1680c0d06caSMauro Carvalho Chehab return;
1690c0d06caSMauro Carvalho Chehab }
1700c0d06caSMauro Carvalho Chehab
cx231xx_audio_bulkirq(struct urb * urb)1710c0d06caSMauro Carvalho Chehab static void cx231xx_audio_bulkirq(struct urb *urb)
1720c0d06caSMauro Carvalho Chehab {
1730c0d06caSMauro Carvalho Chehab struct cx231xx *dev = urb->context;
1740c0d06caSMauro Carvalho Chehab unsigned int oldptr;
1750c0d06caSMauro Carvalho Chehab int period_elapsed = 0;
1760c0d06caSMauro Carvalho Chehab int status;
1770c0d06caSMauro Carvalho Chehab unsigned char *cp;
1780c0d06caSMauro Carvalho Chehab unsigned int stride;
1790c0d06caSMauro Carvalho Chehab struct snd_pcm_substream *substream;
1800c0d06caSMauro Carvalho Chehab struct snd_pcm_runtime *runtime;
1810c0d06caSMauro Carvalho Chehab
1820c0d06caSMauro Carvalho Chehab if (dev->state & DEV_DISCONNECTED)
1830c0d06caSMauro Carvalho Chehab return;
1840c0d06caSMauro Carvalho Chehab
1850c0d06caSMauro Carvalho Chehab switch (urb->status) {
1860c0d06caSMauro Carvalho Chehab case 0: /* success */
1870c0d06caSMauro Carvalho Chehab case -ETIMEDOUT: /* NAK */
1880c0d06caSMauro Carvalho Chehab break;
1890c0d06caSMauro Carvalho Chehab case -ECONNRESET: /* kill */
1900c0d06caSMauro Carvalho Chehab case -ENOENT:
1910c0d06caSMauro Carvalho Chehab case -ESHUTDOWN:
1920c0d06caSMauro Carvalho Chehab return;
1930c0d06caSMauro Carvalho Chehab default: /* error */
194854bb4ecSColin Ian King dev_dbg(dev->dev, "urb completion error %d.\n",
19556d8a3b0SMauro Carvalho Chehab urb->status);
1960c0d06caSMauro Carvalho Chehab break;
1970c0d06caSMauro Carvalho Chehab }
1980c0d06caSMauro Carvalho Chehab
1990c0d06caSMauro Carvalho Chehab if (atomic_read(&dev->stream_started) == 0)
2000c0d06caSMauro Carvalho Chehab return;
2010c0d06caSMauro Carvalho Chehab
2020c0d06caSMauro Carvalho Chehab if (dev->adev.capture_pcm_substream) {
2030c0d06caSMauro Carvalho Chehab substream = dev->adev.capture_pcm_substream;
2040c0d06caSMauro Carvalho Chehab runtime = substream->runtime;
2050c0d06caSMauro Carvalho Chehab stride = runtime->frame_bits >> 3;
2060c0d06caSMauro Carvalho Chehab
2070c0d06caSMauro Carvalho Chehab if (1) {
208aa53bf0bSSebastian Andrzej Siewior unsigned long flags;
2090c0d06caSMauro Carvalho Chehab int length = urb->actual_length /
2100c0d06caSMauro Carvalho Chehab stride;
2110c0d06caSMauro Carvalho Chehab cp = (unsigned char *)urb->transfer_buffer;
2120c0d06caSMauro Carvalho Chehab
2130c0d06caSMauro Carvalho Chehab oldptr = dev->adev.hwptr_done_capture;
2140c0d06caSMauro Carvalho Chehab if (oldptr + length >= runtime->buffer_size) {
2150c0d06caSMauro Carvalho Chehab unsigned int cnt;
2160c0d06caSMauro Carvalho Chehab
2170c0d06caSMauro Carvalho Chehab cnt = runtime->buffer_size - oldptr;
2180c0d06caSMauro Carvalho Chehab memcpy(runtime->dma_area + oldptr * stride, cp,
2190c0d06caSMauro Carvalho Chehab cnt * stride);
2200c0d06caSMauro Carvalho Chehab memcpy(runtime->dma_area, cp + cnt * stride,
2210c0d06caSMauro Carvalho Chehab length * stride - cnt * stride);
2220c0d06caSMauro Carvalho Chehab } else {
2230c0d06caSMauro Carvalho Chehab memcpy(runtime->dma_area + oldptr * stride, cp,
2240c0d06caSMauro Carvalho Chehab length * stride);
2250c0d06caSMauro Carvalho Chehab }
2260c0d06caSMauro Carvalho Chehab
227aa53bf0bSSebastian Andrzej Siewior snd_pcm_stream_lock_irqsave(substream, flags);
2280c0d06caSMauro Carvalho Chehab
2290c0d06caSMauro Carvalho Chehab dev->adev.hwptr_done_capture += length;
2300c0d06caSMauro Carvalho Chehab if (dev->adev.hwptr_done_capture >=
2310c0d06caSMauro Carvalho Chehab runtime->buffer_size)
2320c0d06caSMauro Carvalho Chehab dev->adev.hwptr_done_capture -=
2330c0d06caSMauro Carvalho Chehab runtime->buffer_size;
2340c0d06caSMauro Carvalho Chehab
2350c0d06caSMauro Carvalho Chehab dev->adev.capture_transfer_done += length;
2360c0d06caSMauro Carvalho Chehab if (dev->adev.capture_transfer_done >=
2370c0d06caSMauro Carvalho Chehab runtime->period_size) {
2380c0d06caSMauro Carvalho Chehab dev->adev.capture_transfer_done -=
2390c0d06caSMauro Carvalho Chehab runtime->period_size;
2400c0d06caSMauro Carvalho Chehab period_elapsed = 1;
2410c0d06caSMauro Carvalho Chehab }
242aa53bf0bSSebastian Andrzej Siewior snd_pcm_stream_unlock_irqrestore(substream, flags);
2430c0d06caSMauro Carvalho Chehab }
2440c0d06caSMauro Carvalho Chehab if (period_elapsed)
2450c0d06caSMauro Carvalho Chehab snd_pcm_period_elapsed(substream);
2460c0d06caSMauro Carvalho Chehab }
2470c0d06caSMauro Carvalho Chehab urb->status = 0;
2480c0d06caSMauro Carvalho Chehab
2490c0d06caSMauro Carvalho Chehab status = usb_submit_urb(urb, GFP_ATOMIC);
2500c0d06caSMauro Carvalho Chehab if (status < 0) {
251336fea92SMauro Carvalho Chehab dev_err(dev->dev,
252b7085c08SMauro Carvalho Chehab "resubmit of audio urb failed (error=%i)\n",
2530c0d06caSMauro Carvalho Chehab status);
2540c0d06caSMauro Carvalho Chehab }
2550c0d06caSMauro Carvalho Chehab return;
2560c0d06caSMauro Carvalho Chehab }
2570c0d06caSMauro Carvalho Chehab
cx231xx_init_audio_isoc(struct cx231xx * dev)2580c0d06caSMauro Carvalho Chehab static int cx231xx_init_audio_isoc(struct cx231xx *dev)
2590c0d06caSMauro Carvalho Chehab {
2600c0d06caSMauro Carvalho Chehab int i, errCode;
2610c0d06caSMauro Carvalho Chehab int sb_size;
2620c0d06caSMauro Carvalho Chehab
263336fea92SMauro Carvalho Chehab dev_dbg(dev->dev,
264b7085c08SMauro Carvalho Chehab "%s: Starting ISO AUDIO transfers\n", __func__);
2650c0d06caSMauro Carvalho Chehab
2660c0d06caSMauro Carvalho Chehab if (dev->state & DEV_DISCONNECTED)
2670c0d06caSMauro Carvalho Chehab return -ENODEV;
2680c0d06caSMauro Carvalho Chehab
2690c0d06caSMauro Carvalho Chehab sb_size = CX231XX_ISO_NUM_AUDIO_PACKETS * dev->adev.max_pkt_size;
2700c0d06caSMauro Carvalho Chehab
2710c0d06caSMauro Carvalho Chehab for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
2720c0d06caSMauro Carvalho Chehab struct urb *urb;
2730c0d06caSMauro Carvalho Chehab int j, k;
2740c0d06caSMauro Carvalho Chehab
2750c0d06caSMauro Carvalho Chehab dev->adev.transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC);
2760c0d06caSMauro Carvalho Chehab if (!dev->adev.transfer_buffer[i])
2770c0d06caSMauro Carvalho Chehab return -ENOMEM;
2780c0d06caSMauro Carvalho Chehab
2790c0d06caSMauro Carvalho Chehab memset(dev->adev.transfer_buffer[i], 0x80, sb_size);
2800c0d06caSMauro Carvalho Chehab urb = usb_alloc_urb(CX231XX_ISO_NUM_AUDIO_PACKETS, GFP_ATOMIC);
2810c0d06caSMauro Carvalho Chehab if (!urb) {
2820c0d06caSMauro Carvalho Chehab for (j = 0; j < i; j++) {
2830c0d06caSMauro Carvalho Chehab usb_free_urb(dev->adev.urb[j]);
2840c0d06caSMauro Carvalho Chehab kfree(dev->adev.transfer_buffer[j]);
2850c0d06caSMauro Carvalho Chehab }
2860c0d06caSMauro Carvalho Chehab return -ENOMEM;
2870c0d06caSMauro Carvalho Chehab }
2880c0d06caSMauro Carvalho Chehab
2890c0d06caSMauro Carvalho Chehab urb->dev = dev->udev;
2900c0d06caSMauro Carvalho Chehab urb->context = dev;
2910c0d06caSMauro Carvalho Chehab urb->pipe = usb_rcvisocpipe(dev->udev,
2920c0d06caSMauro Carvalho Chehab dev->adev.end_point_addr);
2930c0d06caSMauro Carvalho Chehab urb->transfer_flags = URB_ISO_ASAP;
2940c0d06caSMauro Carvalho Chehab urb->transfer_buffer = dev->adev.transfer_buffer[i];
2950c0d06caSMauro Carvalho Chehab urb->interval = 1;
2960c0d06caSMauro Carvalho Chehab urb->complete = cx231xx_audio_isocirq;
2970c0d06caSMauro Carvalho Chehab urb->number_of_packets = CX231XX_ISO_NUM_AUDIO_PACKETS;
2980c0d06caSMauro Carvalho Chehab urb->transfer_buffer_length = sb_size;
2990c0d06caSMauro Carvalho Chehab
3000c0d06caSMauro Carvalho Chehab for (j = k = 0; j < CX231XX_ISO_NUM_AUDIO_PACKETS;
3010c0d06caSMauro Carvalho Chehab j++, k += dev->adev.max_pkt_size) {
3020c0d06caSMauro Carvalho Chehab urb->iso_frame_desc[j].offset = k;
3030c0d06caSMauro Carvalho Chehab urb->iso_frame_desc[j].length = dev->adev.max_pkt_size;
3040c0d06caSMauro Carvalho Chehab }
3050c0d06caSMauro Carvalho Chehab dev->adev.urb[i] = urb;
3060c0d06caSMauro Carvalho Chehab }
3070c0d06caSMauro Carvalho Chehab
3080c0d06caSMauro Carvalho Chehab for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
3090c0d06caSMauro Carvalho Chehab errCode = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC);
3100c0d06caSMauro Carvalho Chehab if (errCode < 0) {
3110c0d06caSMauro Carvalho Chehab cx231xx_isoc_audio_deinit(dev);
3120c0d06caSMauro Carvalho Chehab return errCode;
3130c0d06caSMauro Carvalho Chehab }
3140c0d06caSMauro Carvalho Chehab }
3150c0d06caSMauro Carvalho Chehab
3160c0d06caSMauro Carvalho Chehab return errCode;
3170c0d06caSMauro Carvalho Chehab }
3180c0d06caSMauro Carvalho Chehab
cx231xx_init_audio_bulk(struct cx231xx * dev)3190c0d06caSMauro Carvalho Chehab static int cx231xx_init_audio_bulk(struct cx231xx *dev)
3200c0d06caSMauro Carvalho Chehab {
3210c0d06caSMauro Carvalho Chehab int i, errCode;
3220c0d06caSMauro Carvalho Chehab int sb_size;
3230c0d06caSMauro Carvalho Chehab
324336fea92SMauro Carvalho Chehab dev_dbg(dev->dev,
325b7085c08SMauro Carvalho Chehab "%s: Starting BULK AUDIO transfers\n", __func__);
3260c0d06caSMauro Carvalho Chehab
3270c0d06caSMauro Carvalho Chehab if (dev->state & DEV_DISCONNECTED)
3280c0d06caSMauro Carvalho Chehab return -ENODEV;
3290c0d06caSMauro Carvalho Chehab
3300c0d06caSMauro Carvalho Chehab sb_size = CX231XX_NUM_AUDIO_PACKETS * dev->adev.max_pkt_size;
3310c0d06caSMauro Carvalho Chehab
3320c0d06caSMauro Carvalho Chehab for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
3330c0d06caSMauro Carvalho Chehab struct urb *urb;
3340c0d06caSMauro Carvalho Chehab int j;
3350c0d06caSMauro Carvalho Chehab
3360c0d06caSMauro Carvalho Chehab dev->adev.transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC);
3370c0d06caSMauro Carvalho Chehab if (!dev->adev.transfer_buffer[i])
3380c0d06caSMauro Carvalho Chehab return -ENOMEM;
3390c0d06caSMauro Carvalho Chehab
3400c0d06caSMauro Carvalho Chehab memset(dev->adev.transfer_buffer[i], 0x80, sb_size);
3410c0d06caSMauro Carvalho Chehab urb = usb_alloc_urb(CX231XX_NUM_AUDIO_PACKETS, GFP_ATOMIC);
3420c0d06caSMauro Carvalho Chehab if (!urb) {
3430c0d06caSMauro Carvalho Chehab for (j = 0; j < i; j++) {
3440c0d06caSMauro Carvalho Chehab usb_free_urb(dev->adev.urb[j]);
3450c0d06caSMauro Carvalho Chehab kfree(dev->adev.transfer_buffer[j]);
3460c0d06caSMauro Carvalho Chehab }
3470c0d06caSMauro Carvalho Chehab return -ENOMEM;
3480c0d06caSMauro Carvalho Chehab }
3490c0d06caSMauro Carvalho Chehab
3500c0d06caSMauro Carvalho Chehab urb->dev = dev->udev;
3510c0d06caSMauro Carvalho Chehab urb->context = dev;
3520c0d06caSMauro Carvalho Chehab urb->pipe = usb_rcvbulkpipe(dev->udev,
3530c0d06caSMauro Carvalho Chehab dev->adev.end_point_addr);
3540c0d06caSMauro Carvalho Chehab urb->transfer_flags = 0;
3550c0d06caSMauro Carvalho Chehab urb->transfer_buffer = dev->adev.transfer_buffer[i];
3560c0d06caSMauro Carvalho Chehab urb->complete = cx231xx_audio_bulkirq;
3570c0d06caSMauro Carvalho Chehab urb->transfer_buffer_length = sb_size;
3580c0d06caSMauro Carvalho Chehab
3590c0d06caSMauro Carvalho Chehab dev->adev.urb[i] = urb;
3600c0d06caSMauro Carvalho Chehab
3610c0d06caSMauro Carvalho Chehab }
3620c0d06caSMauro Carvalho Chehab
3630c0d06caSMauro Carvalho Chehab for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
3640c0d06caSMauro Carvalho Chehab errCode = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC);
3650c0d06caSMauro Carvalho Chehab if (errCode < 0) {
3660c0d06caSMauro Carvalho Chehab cx231xx_bulk_audio_deinit(dev);
3670c0d06caSMauro Carvalho Chehab return errCode;
3680c0d06caSMauro Carvalho Chehab }
3690c0d06caSMauro Carvalho Chehab }
3700c0d06caSMauro Carvalho Chehab
3710c0d06caSMauro Carvalho Chehab return errCode;
3720c0d06caSMauro Carvalho Chehab }
3730c0d06caSMauro Carvalho Chehab
37438c4f03aSBhumika Goyal static const struct snd_pcm_hardware snd_cx231xx_hw_capture = {
3750c0d06caSMauro Carvalho Chehab .info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
3760c0d06caSMauro Carvalho Chehab SNDRV_PCM_INFO_MMAP |
3770c0d06caSMauro Carvalho Chehab SNDRV_PCM_INFO_INTERLEAVED |
3780c0d06caSMauro Carvalho Chehab SNDRV_PCM_INFO_MMAP_VALID,
3790c0d06caSMauro Carvalho Chehab
3800c0d06caSMauro Carvalho Chehab .formats = SNDRV_PCM_FMTBIT_S16_LE,
3810c0d06caSMauro Carvalho Chehab
3820c0d06caSMauro Carvalho Chehab .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT,
3830c0d06caSMauro Carvalho Chehab
3840c0d06caSMauro Carvalho Chehab .rate_min = 48000,
3850c0d06caSMauro Carvalho Chehab .rate_max = 48000,
3860c0d06caSMauro Carvalho Chehab .channels_min = 2,
3870c0d06caSMauro Carvalho Chehab .channels_max = 2,
3880c0d06caSMauro Carvalho Chehab .buffer_bytes_max = 62720 * 8, /* just about the value in usbaudio.c */
3890c0d06caSMauro Carvalho Chehab .period_bytes_min = 64, /* 12544/2, */
3900c0d06caSMauro Carvalho Chehab .period_bytes_max = 12544,
3910c0d06caSMauro Carvalho Chehab .periods_min = 2,
3920c0d06caSMauro Carvalho Chehab .periods_max = 98, /* 12544, */
3930c0d06caSMauro Carvalho Chehab };
3940c0d06caSMauro Carvalho Chehab
snd_cx231xx_capture_open(struct snd_pcm_substream * substream)3950c0d06caSMauro Carvalho Chehab static int snd_cx231xx_capture_open(struct snd_pcm_substream *substream)
3960c0d06caSMauro Carvalho Chehab {
3970c0d06caSMauro Carvalho Chehab struct cx231xx *dev = snd_pcm_substream_chip(substream);
3980c0d06caSMauro Carvalho Chehab struct snd_pcm_runtime *runtime = substream->runtime;
3990c0d06caSMauro Carvalho Chehab int ret = 0;
4000c0d06caSMauro Carvalho Chehab
401336fea92SMauro Carvalho Chehab dev_dbg(dev->dev,
40256d8a3b0SMauro Carvalho Chehab "opening device and trying to acquire exclusive lock\n");
4030c0d06caSMauro Carvalho Chehab
4040c0d06caSMauro Carvalho Chehab if (dev->state & DEV_DISCONNECTED) {
405336fea92SMauro Carvalho Chehab dev_err(dev->dev,
406b7085c08SMauro Carvalho Chehab "Can't open. the device was removed.\n");
4070c0d06caSMauro Carvalho Chehab return -ENODEV;
4080c0d06caSMauro Carvalho Chehab }
4090c0d06caSMauro Carvalho Chehab
4100c0d06caSMauro Carvalho Chehab /* set alternate setting for audio interface */
4110c0d06caSMauro Carvalho Chehab /* 1 - 48000 samples per sec */
4120c0d06caSMauro Carvalho Chehab mutex_lock(&dev->lock);
4130c0d06caSMauro Carvalho Chehab if (dev->USE_ISO)
4140c0d06caSMauro Carvalho Chehab ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 1);
4150c0d06caSMauro Carvalho Chehab else
4160c0d06caSMauro Carvalho Chehab ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 0);
4170c0d06caSMauro Carvalho Chehab mutex_unlock(&dev->lock);
4180c0d06caSMauro Carvalho Chehab if (ret < 0) {
419336fea92SMauro Carvalho Chehab dev_err(dev->dev,
420b7085c08SMauro Carvalho Chehab "failed to set alternate setting !\n");
4210c0d06caSMauro Carvalho Chehab
4220c0d06caSMauro Carvalho Chehab return ret;
4230c0d06caSMauro Carvalho Chehab }
4240c0d06caSMauro Carvalho Chehab
4250c0d06caSMauro Carvalho Chehab runtime->hw = snd_cx231xx_hw_capture;
4260c0d06caSMauro Carvalho Chehab
4270c0d06caSMauro Carvalho Chehab mutex_lock(&dev->lock);
4280c0d06caSMauro Carvalho Chehab /* inform hardware to start streaming */
4290c0d06caSMauro Carvalho Chehab ret = cx231xx_capture_start(dev, 1, Audio);
4300c0d06caSMauro Carvalho Chehab
4310c0d06caSMauro Carvalho Chehab dev->adev.users++;
4320c0d06caSMauro Carvalho Chehab mutex_unlock(&dev->lock);
4330c0d06caSMauro Carvalho Chehab
4340c0d06caSMauro Carvalho Chehab snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
4350c0d06caSMauro Carvalho Chehab dev->adev.capture_pcm_substream = substream;
4360c0d06caSMauro Carvalho Chehab runtime->private_data = dev;
4370c0d06caSMauro Carvalho Chehab
4380c0d06caSMauro Carvalho Chehab return 0;
4390c0d06caSMauro Carvalho Chehab }
4400c0d06caSMauro Carvalho Chehab
snd_cx231xx_pcm_close(struct snd_pcm_substream * substream)4410c0d06caSMauro Carvalho Chehab static int snd_cx231xx_pcm_close(struct snd_pcm_substream *substream)
4420c0d06caSMauro Carvalho Chehab {
4430c0d06caSMauro Carvalho Chehab int ret;
4440c0d06caSMauro Carvalho Chehab struct cx231xx *dev = snd_pcm_substream_chip(substream);
4450c0d06caSMauro Carvalho Chehab
446336fea92SMauro Carvalho Chehab dev_dbg(dev->dev, "closing device\n");
4470c0d06caSMauro Carvalho Chehab
4480c0d06caSMauro Carvalho Chehab /* inform hardware to stop streaming */
4490c0d06caSMauro Carvalho Chehab mutex_lock(&dev->lock);
4500c0d06caSMauro Carvalho Chehab ret = cx231xx_capture_start(dev, 0, Audio);
4510c0d06caSMauro Carvalho Chehab
4520c0d06caSMauro Carvalho Chehab /* set alternate setting for audio interface */
4530c0d06caSMauro Carvalho Chehab /* 1 - 48000 samples per sec */
4540c0d06caSMauro Carvalho Chehab ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 0);
4550c0d06caSMauro Carvalho Chehab if (ret < 0) {
456336fea92SMauro Carvalho Chehab dev_err(dev->dev,
457b7085c08SMauro Carvalho Chehab "failed to set alternate setting !\n");
4580c0d06caSMauro Carvalho Chehab
4590c0d06caSMauro Carvalho Chehab mutex_unlock(&dev->lock);
4600c0d06caSMauro Carvalho Chehab return ret;
4610c0d06caSMauro Carvalho Chehab }
4620c0d06caSMauro Carvalho Chehab
4630c0d06caSMauro Carvalho Chehab dev->adev.users--;
4640c0d06caSMauro Carvalho Chehab mutex_unlock(&dev->lock);
4650c0d06caSMauro Carvalho Chehab
4660c0d06caSMauro Carvalho Chehab if (dev->adev.users == 0 && dev->adev.shutdown == 1) {
467336fea92SMauro Carvalho Chehab dev_dbg(dev->dev, "audio users: %d\n", dev->adev.users);
468336fea92SMauro Carvalho Chehab dev_dbg(dev->dev, "disabling audio stream!\n");
4690c0d06caSMauro Carvalho Chehab dev->adev.shutdown = 0;
470336fea92SMauro Carvalho Chehab dev_dbg(dev->dev, "released lock\n");
4710c0d06caSMauro Carvalho Chehab if (atomic_read(&dev->stream_started) > 0) {
4720c0d06caSMauro Carvalho Chehab atomic_set(&dev->stream_started, 0);
4730c0d06caSMauro Carvalho Chehab schedule_work(&dev->wq_trigger);
4740c0d06caSMauro Carvalho Chehab }
4750c0d06caSMauro Carvalho Chehab }
4760c0d06caSMauro Carvalho Chehab return 0;
4770c0d06caSMauro Carvalho Chehab }
4780c0d06caSMauro Carvalho Chehab
snd_cx231xx_prepare(struct snd_pcm_substream * substream)4790c0d06caSMauro Carvalho Chehab static int snd_cx231xx_prepare(struct snd_pcm_substream *substream)
4800c0d06caSMauro Carvalho Chehab {
4810c0d06caSMauro Carvalho Chehab struct cx231xx *dev = snd_pcm_substream_chip(substream);
4820c0d06caSMauro Carvalho Chehab
4830c0d06caSMauro Carvalho Chehab dev->adev.hwptr_done_capture = 0;
4840c0d06caSMauro Carvalho Chehab dev->adev.capture_transfer_done = 0;
4850c0d06caSMauro Carvalho Chehab
4860c0d06caSMauro Carvalho Chehab return 0;
4870c0d06caSMauro Carvalho Chehab }
4880c0d06caSMauro Carvalho Chehab
audio_trigger(struct work_struct * work)4890c0d06caSMauro Carvalho Chehab static void audio_trigger(struct work_struct *work)
4900c0d06caSMauro Carvalho Chehab {
4910c0d06caSMauro Carvalho Chehab struct cx231xx *dev = container_of(work, struct cx231xx, wq_trigger);
4920c0d06caSMauro Carvalho Chehab
4930c0d06caSMauro Carvalho Chehab if (atomic_read(&dev->stream_started)) {
494336fea92SMauro Carvalho Chehab dev_dbg(dev->dev, "starting capture");
4950c0d06caSMauro Carvalho Chehab if (is_fw_load(dev) == 0)
4960c0d06caSMauro Carvalho Chehab cx25840_call(dev, core, load_fw);
4970c0d06caSMauro Carvalho Chehab if (dev->USE_ISO)
4980c0d06caSMauro Carvalho Chehab cx231xx_init_audio_isoc(dev);
4990c0d06caSMauro Carvalho Chehab else
5000c0d06caSMauro Carvalho Chehab cx231xx_init_audio_bulk(dev);
5010c0d06caSMauro Carvalho Chehab } else {
502336fea92SMauro Carvalho Chehab dev_dbg(dev->dev, "stopping capture");
5030c0d06caSMauro Carvalho Chehab cx231xx_isoc_audio_deinit(dev);
5040c0d06caSMauro Carvalho Chehab }
5050c0d06caSMauro Carvalho Chehab }
5060c0d06caSMauro Carvalho Chehab
snd_cx231xx_capture_trigger(struct snd_pcm_substream * substream,int cmd)5070c0d06caSMauro Carvalho Chehab static int snd_cx231xx_capture_trigger(struct snd_pcm_substream *substream,
5080c0d06caSMauro Carvalho Chehab int cmd)
5090c0d06caSMauro Carvalho Chehab {
5100c0d06caSMauro Carvalho Chehab struct cx231xx *dev = snd_pcm_substream_chip(substream);
5110c0d06caSMauro Carvalho Chehab int retval = 0;
5120c0d06caSMauro Carvalho Chehab
5130c0d06caSMauro Carvalho Chehab if (dev->state & DEV_DISCONNECTED)
5140c0d06caSMauro Carvalho Chehab return -ENODEV;
5150c0d06caSMauro Carvalho Chehab
5160c0d06caSMauro Carvalho Chehab spin_lock(&dev->adev.slock);
5170c0d06caSMauro Carvalho Chehab switch (cmd) {
5180c0d06caSMauro Carvalho Chehab case SNDRV_PCM_TRIGGER_START:
5190c0d06caSMauro Carvalho Chehab atomic_set(&dev->stream_started, 1);
5200c0d06caSMauro Carvalho Chehab break;
5210c0d06caSMauro Carvalho Chehab case SNDRV_PCM_TRIGGER_STOP:
5220c0d06caSMauro Carvalho Chehab atomic_set(&dev->stream_started, 0);
5230c0d06caSMauro Carvalho Chehab break;
5240c0d06caSMauro Carvalho Chehab default:
5250c0d06caSMauro Carvalho Chehab retval = -EINVAL;
5260c0d06caSMauro Carvalho Chehab break;
5270c0d06caSMauro Carvalho Chehab }
5280c0d06caSMauro Carvalho Chehab spin_unlock(&dev->adev.slock);
5290c0d06caSMauro Carvalho Chehab
5300c0d06caSMauro Carvalho Chehab schedule_work(&dev->wq_trigger);
5310c0d06caSMauro Carvalho Chehab
5320c0d06caSMauro Carvalho Chehab return retval;
5330c0d06caSMauro Carvalho Chehab }
5340c0d06caSMauro Carvalho Chehab
snd_cx231xx_capture_pointer(struct snd_pcm_substream * substream)5350c0d06caSMauro Carvalho Chehab static snd_pcm_uframes_t snd_cx231xx_capture_pointer(struct snd_pcm_substream
5360c0d06caSMauro Carvalho Chehab *substream)
5370c0d06caSMauro Carvalho Chehab {
5380c0d06caSMauro Carvalho Chehab struct cx231xx *dev;
5390c0d06caSMauro Carvalho Chehab unsigned long flags;
5400c0d06caSMauro Carvalho Chehab snd_pcm_uframes_t hwptr_done;
5410c0d06caSMauro Carvalho Chehab
5420c0d06caSMauro Carvalho Chehab dev = snd_pcm_substream_chip(substream);
5430c0d06caSMauro Carvalho Chehab
5440c0d06caSMauro Carvalho Chehab spin_lock_irqsave(&dev->adev.slock, flags);
5450c0d06caSMauro Carvalho Chehab hwptr_done = dev->adev.hwptr_done_capture;
5460c0d06caSMauro Carvalho Chehab spin_unlock_irqrestore(&dev->adev.slock, flags);
5470c0d06caSMauro Carvalho Chehab
5480c0d06caSMauro Carvalho Chehab return hwptr_done;
5490c0d06caSMauro Carvalho Chehab }
5500c0d06caSMauro Carvalho Chehab
55122511cfaSJulia Lawall static const struct snd_pcm_ops snd_cx231xx_pcm_capture = {
5520c0d06caSMauro Carvalho Chehab .open = snd_cx231xx_capture_open,
5530c0d06caSMauro Carvalho Chehab .close = snd_cx231xx_pcm_close,
5540c0d06caSMauro Carvalho Chehab .prepare = snd_cx231xx_prepare,
5550c0d06caSMauro Carvalho Chehab .trigger = snd_cx231xx_capture_trigger,
5560c0d06caSMauro Carvalho Chehab .pointer = snd_cx231xx_capture_pointer,
5570c0d06caSMauro Carvalho Chehab };
5580c0d06caSMauro Carvalho Chehab
cx231xx_audio_init(struct cx231xx * dev)5590c0d06caSMauro Carvalho Chehab static int cx231xx_audio_init(struct cx231xx *dev)
5600c0d06caSMauro Carvalho Chehab {
5610c0d06caSMauro Carvalho Chehab struct cx231xx_audio *adev = &dev->adev;
5620c0d06caSMauro Carvalho Chehab struct snd_pcm *pcm;
5630c0d06caSMauro Carvalho Chehab struct snd_card *card;
5640c0d06caSMauro Carvalho Chehab static int devnr;
5650c0d06caSMauro Carvalho Chehab int err;
5660c0d06caSMauro Carvalho Chehab struct usb_interface *uif;
5670c0d06caSMauro Carvalho Chehab int i, isoc_pipe = 0;
5680c0d06caSMauro Carvalho Chehab
5690c0d06caSMauro Carvalho Chehab if (dev->has_alsa_audio != 1) {
5700c0d06caSMauro Carvalho Chehab /* This device does not support the extension (in this case
5710c0d06caSMauro Carvalho Chehab the device is expecting the snd-usb-audio module or
5720c0d06caSMauro Carvalho Chehab doesn't have analog audio support at all) */
5730c0d06caSMauro Carvalho Chehab return 0;
5740c0d06caSMauro Carvalho Chehab }
5750c0d06caSMauro Carvalho Chehab
576336fea92SMauro Carvalho Chehab dev_dbg(dev->dev,
577b7085c08SMauro Carvalho Chehab "probing for cx231xx non standard usbaudio\n");
5780c0d06caSMauro Carvalho Chehab
579336fea92SMauro Carvalho Chehab err = snd_card_new(dev->dev, index[devnr], "Cx231xx Audio",
580e7356888STakashi Iwai THIS_MODULE, 0, &card);
5810c0d06caSMauro Carvalho Chehab if (err < 0)
5820c0d06caSMauro Carvalho Chehab return err;
5830c0d06caSMauro Carvalho Chehab
5840c0d06caSMauro Carvalho Chehab spin_lock_init(&adev->slock);
5850c0d06caSMauro Carvalho Chehab err = snd_pcm_new(card, "Cx231xx Audio", 0, 0, 1, &pcm);
586fff1abc4SJohan Hovold if (err < 0)
587fff1abc4SJohan Hovold goto err_free_card;
5880c0d06caSMauro Carvalho Chehab
5890c0d06caSMauro Carvalho Chehab snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
5900c0d06caSMauro Carvalho Chehab &snd_cx231xx_pcm_capture);
591355b9a0cSTakashi Iwai snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
5920c0d06caSMauro Carvalho Chehab pcm->info_flags = 0;
5930c0d06caSMauro Carvalho Chehab pcm->private_data = dev;
594cc1e6315SMauro Carvalho Chehab strscpy(pcm->name, "Conexant cx231xx Capture", sizeof(pcm->name));
595cc1e6315SMauro Carvalho Chehab strscpy(card->driver, "Cx231xx-Audio", sizeof(card->driver));
596cc1e6315SMauro Carvalho Chehab strscpy(card->shortname, "Cx231xx Audio", sizeof(card->shortname));
597cc1e6315SMauro Carvalho Chehab strscpy(card->longname, "Conexant cx231xx Audio", sizeof(card->longname));
5980c0d06caSMauro Carvalho Chehab
5990c0d06caSMauro Carvalho Chehab INIT_WORK(&dev->wq_trigger, audio_trigger);
6000c0d06caSMauro Carvalho Chehab
6010c0d06caSMauro Carvalho Chehab err = snd_card_register(card);
602fff1abc4SJohan Hovold if (err < 0)
603fff1abc4SJohan Hovold goto err_free_card;
604fff1abc4SJohan Hovold
6050c0d06caSMauro Carvalho Chehab adev->sndcard = card;
6060c0d06caSMauro Carvalho Chehab adev->udev = dev->udev;
6070c0d06caSMauro Carvalho Chehab
6080c0d06caSMauro Carvalho Chehab /* compute alternate max packet sizes for Audio */
6090c0d06caSMauro Carvalho Chehab uif =
6100c0d06caSMauro Carvalho Chehab dev->udev->actconfig->interface[dev->current_pcb_config.
6110c0d06caSMauro Carvalho Chehab hs_config_info[0].interface_info.
6120c0d06caSMauro Carvalho Chehab audio_index + 1];
6130c0d06caSMauro Carvalho Chehab
61465f92164SJohan Hovold if (uif->altsetting[0].desc.bNumEndpoints < isoc_pipe + 1) {
61565f92164SJohan Hovold err = -ENODEV;
61665f92164SJohan Hovold goto err_free_card;
61765f92164SJohan Hovold }
61865f92164SJohan Hovold
6190c0d06caSMauro Carvalho Chehab adev->end_point_addr =
62069a11a32SHans Verkuil uif->altsetting[0].endpoint[isoc_pipe].desc.
62169a11a32SHans Verkuil bEndpointAddress;
6220c0d06caSMauro Carvalho Chehab
6230c0d06caSMauro Carvalho Chehab adev->num_alt = uif->num_altsetting;
624336fea92SMauro Carvalho Chehab dev_info(dev->dev,
625b7085c08SMauro Carvalho Chehab "audio EndPoint Addr 0x%x, Alternate settings: %i\n",
6260c0d06caSMauro Carvalho Chehab adev->end_point_addr, adev->num_alt);
6276da2ec56SKees Cook adev->alt_max_pkt_size = kmalloc_array(32, adev->num_alt, GFP_KERNEL);
628fff1abc4SJohan Hovold if (!adev->alt_max_pkt_size) {
629fff1abc4SJohan Hovold err = -ENOMEM;
630fff1abc4SJohan Hovold goto err_free_card;
631fff1abc4SJohan Hovold }
6320c0d06caSMauro Carvalho Chehab
6330c0d06caSMauro Carvalho Chehab for (i = 0; i < adev->num_alt; i++) {
63465f92164SJohan Hovold u16 tmp;
63565f92164SJohan Hovold
63665f92164SJohan Hovold if (uif->altsetting[i].desc.bNumEndpoints < isoc_pipe + 1) {
63765f92164SJohan Hovold err = -ENODEV;
63865f92164SJohan Hovold goto err_free_pkt_size;
63965f92164SJohan Hovold }
64065f92164SJohan Hovold
64165f92164SJohan Hovold tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc.
6420c0d06caSMauro Carvalho Chehab wMaxPacketSize);
6430c0d06caSMauro Carvalho Chehab adev->alt_max_pkt_size[i] =
6440c0d06caSMauro Carvalho Chehab (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
645336fea92SMauro Carvalho Chehab dev_dbg(dev->dev,
646b7085c08SMauro Carvalho Chehab "audio alternate setting %i, max size= %i\n", i,
6470c0d06caSMauro Carvalho Chehab adev->alt_max_pkt_size[i]);
6480c0d06caSMauro Carvalho Chehab }
6490c0d06caSMauro Carvalho Chehab
6500c0d06caSMauro Carvalho Chehab return 0;
651fff1abc4SJohan Hovold
65265f92164SJohan Hovold err_free_pkt_size:
65365f92164SJohan Hovold kfree(adev->alt_max_pkt_size);
654fff1abc4SJohan Hovold err_free_card:
655fff1abc4SJohan Hovold snd_card_free(card);
656fff1abc4SJohan Hovold
657fff1abc4SJohan Hovold return err;
6580c0d06caSMauro Carvalho Chehab }
6590c0d06caSMauro Carvalho Chehab
cx231xx_audio_fini(struct cx231xx * dev)6600c0d06caSMauro Carvalho Chehab static int cx231xx_audio_fini(struct cx231xx *dev)
6610c0d06caSMauro Carvalho Chehab {
6620c0d06caSMauro Carvalho Chehab if (dev == NULL)
6630c0d06caSMauro Carvalho Chehab return 0;
6640c0d06caSMauro Carvalho Chehab
6650c0d06caSMauro Carvalho Chehab if (dev->has_alsa_audio != 1) {
6660c0d06caSMauro Carvalho Chehab /* This device does not support the extension (in this case
6670c0d06caSMauro Carvalho Chehab the device is expecting the snd-usb-audio module or
6680c0d06caSMauro Carvalho Chehab doesn't have analog audio support at all) */
6690c0d06caSMauro Carvalho Chehab return 0;
6700c0d06caSMauro Carvalho Chehab }
6710c0d06caSMauro Carvalho Chehab
6720c0d06caSMauro Carvalho Chehab if (dev->adev.sndcard) {
673*88c66084SZebediah Figura snd_card_free_when_closed(dev->adev.sndcard);
6740c0d06caSMauro Carvalho Chehab kfree(dev->adev.alt_max_pkt_size);
6750c0d06caSMauro Carvalho Chehab dev->adev.sndcard = NULL;
6760c0d06caSMauro Carvalho Chehab }
6770c0d06caSMauro Carvalho Chehab
6780c0d06caSMauro Carvalho Chehab return 0;
6790c0d06caSMauro Carvalho Chehab }
6800c0d06caSMauro Carvalho Chehab
6810c0d06caSMauro Carvalho Chehab static struct cx231xx_ops audio_ops = {
6820c0d06caSMauro Carvalho Chehab .id = CX231XX_AUDIO,
6830c0d06caSMauro Carvalho Chehab .name = "Cx231xx Audio Extension",
6840c0d06caSMauro Carvalho Chehab .init = cx231xx_audio_init,
6850c0d06caSMauro Carvalho Chehab .fini = cx231xx_audio_fini,
6860c0d06caSMauro Carvalho Chehab };
6870c0d06caSMauro Carvalho Chehab
cx231xx_alsa_register(void)6880c0d06caSMauro Carvalho Chehab static int __init cx231xx_alsa_register(void)
6890c0d06caSMauro Carvalho Chehab {
6900c0d06caSMauro Carvalho Chehab return cx231xx_register_extension(&audio_ops);
6910c0d06caSMauro Carvalho Chehab }
6920c0d06caSMauro Carvalho Chehab
cx231xx_alsa_unregister(void)6930c0d06caSMauro Carvalho Chehab static void __exit cx231xx_alsa_unregister(void)
6940c0d06caSMauro Carvalho Chehab {
6950c0d06caSMauro Carvalho Chehab cx231xx_unregister_extension(&audio_ops);
6960c0d06caSMauro Carvalho Chehab }
6970c0d06caSMauro Carvalho Chehab
6980c0d06caSMauro Carvalho Chehab MODULE_LICENSE("GPL");
6990c0d06caSMauro Carvalho Chehab MODULE_AUTHOR("Srinivasa Deevi <srinivasa.deevi@conexant.com>");
7000c0d06caSMauro Carvalho Chehab MODULE_DESCRIPTION("Cx231xx Audio driver");
7010c0d06caSMauro Carvalho Chehab
7020c0d06caSMauro Carvalho Chehab module_init(cx231xx_alsa_register);
7030c0d06caSMauro Carvalho Chehab module_exit(cx231xx_alsa_unregister);
704