18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Conexant Cx231xx audio extension 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2008 <srinivasa.deevi at conexant dot com> 68c2ecf20Sopenharmony_ci * Based on em28xx driver 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include "cx231xx.h" 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/sound.h> 138c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 148c2ecf20Sopenharmony_ci#include <linux/soundcard.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <sound/core.h> 188c2ecf20Sopenharmony_ci#include <sound/pcm.h> 198c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 208c2ecf20Sopenharmony_ci#include <sound/info.h> 218c2ecf20Sopenharmony_ci#include <sound/initval.h> 228c2ecf20Sopenharmony_ci#include <sound/control.h> 238c2ecf20Sopenharmony_ci#include <media/v4l2-common.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic int debug; 268c2ecf20Sopenharmony_cimodule_param(debug, int, 0644); 278c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "activates debug info"); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic int cx231xx_isoc_audio_deinit(struct cx231xx *dev) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci int i; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci dev_dbg(dev->dev, "Stopping isoc\n"); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci for (i = 0; i < CX231XX_AUDIO_BUFS; i++) { 388c2ecf20Sopenharmony_ci if (dev->adev.urb[i]) { 398c2ecf20Sopenharmony_ci if (!irqs_disabled()) 408c2ecf20Sopenharmony_ci usb_kill_urb(dev->adev.urb[i]); 418c2ecf20Sopenharmony_ci else 428c2ecf20Sopenharmony_ci usb_unlink_urb(dev->adev.urb[i]); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci usb_free_urb(dev->adev.urb[i]); 458c2ecf20Sopenharmony_ci dev->adev.urb[i] = NULL; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci kfree(dev->adev.transfer_buffer[i]); 488c2ecf20Sopenharmony_ci dev->adev.transfer_buffer[i] = NULL; 498c2ecf20Sopenharmony_ci } 508c2ecf20Sopenharmony_ci } 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci return 0; 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic int cx231xx_bulk_audio_deinit(struct cx231xx *dev) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci int i; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci dev_dbg(dev->dev, "Stopping bulk\n"); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci for (i = 0; i < CX231XX_AUDIO_BUFS; i++) { 628c2ecf20Sopenharmony_ci if (dev->adev.urb[i]) { 638c2ecf20Sopenharmony_ci if (!irqs_disabled()) 648c2ecf20Sopenharmony_ci usb_kill_urb(dev->adev.urb[i]); 658c2ecf20Sopenharmony_ci else 668c2ecf20Sopenharmony_ci usb_unlink_urb(dev->adev.urb[i]); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci usb_free_urb(dev->adev.urb[i]); 698c2ecf20Sopenharmony_ci dev->adev.urb[i] = NULL; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci kfree(dev->adev.transfer_buffer[i]); 728c2ecf20Sopenharmony_ci dev->adev.transfer_buffer[i] = NULL; 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci return 0; 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic void cx231xx_audio_isocirq(struct urb *urb) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci struct cx231xx *dev = urb->context; 828c2ecf20Sopenharmony_ci int i; 838c2ecf20Sopenharmony_ci unsigned int oldptr; 848c2ecf20Sopenharmony_ci int period_elapsed = 0; 858c2ecf20Sopenharmony_ci int status; 868c2ecf20Sopenharmony_ci unsigned char *cp; 878c2ecf20Sopenharmony_ci unsigned int stride; 888c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream; 898c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci if (dev->state & DEV_DISCONNECTED) 928c2ecf20Sopenharmony_ci return; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci switch (urb->status) { 958c2ecf20Sopenharmony_ci case 0: /* success */ 968c2ecf20Sopenharmony_ci case -ETIMEDOUT: /* NAK */ 978c2ecf20Sopenharmony_ci break; 988c2ecf20Sopenharmony_ci case -ECONNRESET: /* kill */ 998c2ecf20Sopenharmony_ci case -ENOENT: 1008c2ecf20Sopenharmony_ci case -ESHUTDOWN: 1018c2ecf20Sopenharmony_ci return; 1028c2ecf20Sopenharmony_ci default: /* error */ 1038c2ecf20Sopenharmony_ci dev_dbg(dev->dev, "urb completion error %d.\n", 1048c2ecf20Sopenharmony_ci urb->status); 1058c2ecf20Sopenharmony_ci break; 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (atomic_read(&dev->stream_started) == 0) 1098c2ecf20Sopenharmony_ci return; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (dev->adev.capture_pcm_substream) { 1128c2ecf20Sopenharmony_ci substream = dev->adev.capture_pcm_substream; 1138c2ecf20Sopenharmony_ci runtime = substream->runtime; 1148c2ecf20Sopenharmony_ci stride = runtime->frame_bits >> 3; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci for (i = 0; i < urb->number_of_packets; i++) { 1178c2ecf20Sopenharmony_ci unsigned long flags; 1188c2ecf20Sopenharmony_ci int length = urb->iso_frame_desc[i].actual_length / 1198c2ecf20Sopenharmony_ci stride; 1208c2ecf20Sopenharmony_ci cp = (unsigned char *)urb->transfer_buffer + 1218c2ecf20Sopenharmony_ci urb->iso_frame_desc[i].offset; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci if (!length) 1248c2ecf20Sopenharmony_ci continue; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci oldptr = dev->adev.hwptr_done_capture; 1278c2ecf20Sopenharmony_ci if (oldptr + length >= runtime->buffer_size) { 1288c2ecf20Sopenharmony_ci unsigned int cnt; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci cnt = runtime->buffer_size - oldptr; 1318c2ecf20Sopenharmony_ci memcpy(runtime->dma_area + oldptr * stride, cp, 1328c2ecf20Sopenharmony_ci cnt * stride); 1338c2ecf20Sopenharmony_ci memcpy(runtime->dma_area, cp + cnt * stride, 1348c2ecf20Sopenharmony_ci length * stride - cnt * stride); 1358c2ecf20Sopenharmony_ci } else { 1368c2ecf20Sopenharmony_ci memcpy(runtime->dma_area + oldptr * stride, cp, 1378c2ecf20Sopenharmony_ci length * stride); 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci snd_pcm_stream_lock_irqsave(substream, flags); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci dev->adev.hwptr_done_capture += length; 1438c2ecf20Sopenharmony_ci if (dev->adev.hwptr_done_capture >= 1448c2ecf20Sopenharmony_ci runtime->buffer_size) 1458c2ecf20Sopenharmony_ci dev->adev.hwptr_done_capture -= 1468c2ecf20Sopenharmony_ci runtime->buffer_size; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci dev->adev.capture_transfer_done += length; 1498c2ecf20Sopenharmony_ci if (dev->adev.capture_transfer_done >= 1508c2ecf20Sopenharmony_ci runtime->period_size) { 1518c2ecf20Sopenharmony_ci dev->adev.capture_transfer_done -= 1528c2ecf20Sopenharmony_ci runtime->period_size; 1538c2ecf20Sopenharmony_ci period_elapsed = 1; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci snd_pcm_stream_unlock_irqrestore(substream, flags); 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci if (period_elapsed) 1588c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(substream); 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci urb->status = 0; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci status = usb_submit_urb(urb, GFP_ATOMIC); 1638c2ecf20Sopenharmony_ci if (status < 0) { 1648c2ecf20Sopenharmony_ci dev_err(dev->dev, 1658c2ecf20Sopenharmony_ci "resubmit of audio urb failed (error=%i)\n", 1668c2ecf20Sopenharmony_ci status); 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci return; 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic void cx231xx_audio_bulkirq(struct urb *urb) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci struct cx231xx *dev = urb->context; 1748c2ecf20Sopenharmony_ci unsigned int oldptr; 1758c2ecf20Sopenharmony_ci int period_elapsed = 0; 1768c2ecf20Sopenharmony_ci int status; 1778c2ecf20Sopenharmony_ci unsigned char *cp; 1788c2ecf20Sopenharmony_ci unsigned int stride; 1798c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream; 1808c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (dev->state & DEV_DISCONNECTED) 1838c2ecf20Sopenharmony_ci return; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci switch (urb->status) { 1868c2ecf20Sopenharmony_ci case 0: /* success */ 1878c2ecf20Sopenharmony_ci case -ETIMEDOUT: /* NAK */ 1888c2ecf20Sopenharmony_ci break; 1898c2ecf20Sopenharmony_ci case -ECONNRESET: /* kill */ 1908c2ecf20Sopenharmony_ci case -ENOENT: 1918c2ecf20Sopenharmony_ci case -ESHUTDOWN: 1928c2ecf20Sopenharmony_ci return; 1938c2ecf20Sopenharmony_ci default: /* error */ 1948c2ecf20Sopenharmony_ci dev_dbg(dev->dev, "urb completion error %d.\n", 1958c2ecf20Sopenharmony_ci urb->status); 1968c2ecf20Sopenharmony_ci break; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (atomic_read(&dev->stream_started) == 0) 2008c2ecf20Sopenharmony_ci return; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (dev->adev.capture_pcm_substream) { 2038c2ecf20Sopenharmony_ci substream = dev->adev.capture_pcm_substream; 2048c2ecf20Sopenharmony_ci runtime = substream->runtime; 2058c2ecf20Sopenharmony_ci stride = runtime->frame_bits >> 3; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci if (1) { 2088c2ecf20Sopenharmony_ci unsigned long flags; 2098c2ecf20Sopenharmony_ci int length = urb->actual_length / 2108c2ecf20Sopenharmony_ci stride; 2118c2ecf20Sopenharmony_ci cp = (unsigned char *)urb->transfer_buffer; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci oldptr = dev->adev.hwptr_done_capture; 2148c2ecf20Sopenharmony_ci if (oldptr + length >= runtime->buffer_size) { 2158c2ecf20Sopenharmony_ci unsigned int cnt; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci cnt = runtime->buffer_size - oldptr; 2188c2ecf20Sopenharmony_ci memcpy(runtime->dma_area + oldptr * stride, cp, 2198c2ecf20Sopenharmony_ci cnt * stride); 2208c2ecf20Sopenharmony_ci memcpy(runtime->dma_area, cp + cnt * stride, 2218c2ecf20Sopenharmony_ci length * stride - cnt * stride); 2228c2ecf20Sopenharmony_ci } else { 2238c2ecf20Sopenharmony_ci memcpy(runtime->dma_area + oldptr * stride, cp, 2248c2ecf20Sopenharmony_ci length * stride); 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci snd_pcm_stream_lock_irqsave(substream, flags); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci dev->adev.hwptr_done_capture += length; 2308c2ecf20Sopenharmony_ci if (dev->adev.hwptr_done_capture >= 2318c2ecf20Sopenharmony_ci runtime->buffer_size) 2328c2ecf20Sopenharmony_ci dev->adev.hwptr_done_capture -= 2338c2ecf20Sopenharmony_ci runtime->buffer_size; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci dev->adev.capture_transfer_done += length; 2368c2ecf20Sopenharmony_ci if (dev->adev.capture_transfer_done >= 2378c2ecf20Sopenharmony_ci runtime->period_size) { 2388c2ecf20Sopenharmony_ci dev->adev.capture_transfer_done -= 2398c2ecf20Sopenharmony_ci runtime->period_size; 2408c2ecf20Sopenharmony_ci period_elapsed = 1; 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci snd_pcm_stream_unlock_irqrestore(substream, flags); 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci if (period_elapsed) 2458c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(substream); 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci urb->status = 0; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci status = usb_submit_urb(urb, GFP_ATOMIC); 2508c2ecf20Sopenharmony_ci if (status < 0) { 2518c2ecf20Sopenharmony_ci dev_err(dev->dev, 2528c2ecf20Sopenharmony_ci "resubmit of audio urb failed (error=%i)\n", 2538c2ecf20Sopenharmony_ci status); 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci return; 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic int cx231xx_init_audio_isoc(struct cx231xx *dev) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci int i, errCode; 2618c2ecf20Sopenharmony_ci int sb_size; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci dev_dbg(dev->dev, 2648c2ecf20Sopenharmony_ci "%s: Starting ISO AUDIO transfers\n", __func__); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci if (dev->state & DEV_DISCONNECTED) 2678c2ecf20Sopenharmony_ci return -ENODEV; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci sb_size = CX231XX_ISO_NUM_AUDIO_PACKETS * dev->adev.max_pkt_size; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci for (i = 0; i < CX231XX_AUDIO_BUFS; i++) { 2728c2ecf20Sopenharmony_ci struct urb *urb; 2738c2ecf20Sopenharmony_ci int j, k; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci dev->adev.transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC); 2768c2ecf20Sopenharmony_ci if (!dev->adev.transfer_buffer[i]) 2778c2ecf20Sopenharmony_ci return -ENOMEM; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci memset(dev->adev.transfer_buffer[i], 0x80, sb_size); 2808c2ecf20Sopenharmony_ci urb = usb_alloc_urb(CX231XX_ISO_NUM_AUDIO_PACKETS, GFP_ATOMIC); 2818c2ecf20Sopenharmony_ci if (!urb) { 2828c2ecf20Sopenharmony_ci for (j = 0; j < i; j++) { 2838c2ecf20Sopenharmony_ci usb_free_urb(dev->adev.urb[j]); 2848c2ecf20Sopenharmony_ci kfree(dev->adev.transfer_buffer[j]); 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci return -ENOMEM; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci urb->dev = dev->udev; 2908c2ecf20Sopenharmony_ci urb->context = dev; 2918c2ecf20Sopenharmony_ci urb->pipe = usb_rcvisocpipe(dev->udev, 2928c2ecf20Sopenharmony_ci dev->adev.end_point_addr); 2938c2ecf20Sopenharmony_ci urb->transfer_flags = URB_ISO_ASAP; 2948c2ecf20Sopenharmony_ci urb->transfer_buffer = dev->adev.transfer_buffer[i]; 2958c2ecf20Sopenharmony_ci urb->interval = 1; 2968c2ecf20Sopenharmony_ci urb->complete = cx231xx_audio_isocirq; 2978c2ecf20Sopenharmony_ci urb->number_of_packets = CX231XX_ISO_NUM_AUDIO_PACKETS; 2988c2ecf20Sopenharmony_ci urb->transfer_buffer_length = sb_size; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci for (j = k = 0; j < CX231XX_ISO_NUM_AUDIO_PACKETS; 3018c2ecf20Sopenharmony_ci j++, k += dev->adev.max_pkt_size) { 3028c2ecf20Sopenharmony_ci urb->iso_frame_desc[j].offset = k; 3038c2ecf20Sopenharmony_ci urb->iso_frame_desc[j].length = dev->adev.max_pkt_size; 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci dev->adev.urb[i] = urb; 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci for (i = 0; i < CX231XX_AUDIO_BUFS; i++) { 3098c2ecf20Sopenharmony_ci errCode = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC); 3108c2ecf20Sopenharmony_ci if (errCode < 0) { 3118c2ecf20Sopenharmony_ci cx231xx_isoc_audio_deinit(dev); 3128c2ecf20Sopenharmony_ci return errCode; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci return errCode; 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cistatic int cx231xx_init_audio_bulk(struct cx231xx *dev) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci int i, errCode; 3228c2ecf20Sopenharmony_ci int sb_size; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci dev_dbg(dev->dev, 3258c2ecf20Sopenharmony_ci "%s: Starting BULK AUDIO transfers\n", __func__); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci if (dev->state & DEV_DISCONNECTED) 3288c2ecf20Sopenharmony_ci return -ENODEV; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci sb_size = CX231XX_NUM_AUDIO_PACKETS * dev->adev.max_pkt_size; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci for (i = 0; i < CX231XX_AUDIO_BUFS; i++) { 3338c2ecf20Sopenharmony_ci struct urb *urb; 3348c2ecf20Sopenharmony_ci int j; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci dev->adev.transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC); 3378c2ecf20Sopenharmony_ci if (!dev->adev.transfer_buffer[i]) 3388c2ecf20Sopenharmony_ci return -ENOMEM; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci memset(dev->adev.transfer_buffer[i], 0x80, sb_size); 3418c2ecf20Sopenharmony_ci urb = usb_alloc_urb(CX231XX_NUM_AUDIO_PACKETS, GFP_ATOMIC); 3428c2ecf20Sopenharmony_ci if (!urb) { 3438c2ecf20Sopenharmony_ci for (j = 0; j < i; j++) { 3448c2ecf20Sopenharmony_ci usb_free_urb(dev->adev.urb[j]); 3458c2ecf20Sopenharmony_ci kfree(dev->adev.transfer_buffer[j]); 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci return -ENOMEM; 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci urb->dev = dev->udev; 3518c2ecf20Sopenharmony_ci urb->context = dev; 3528c2ecf20Sopenharmony_ci urb->pipe = usb_rcvbulkpipe(dev->udev, 3538c2ecf20Sopenharmony_ci dev->adev.end_point_addr); 3548c2ecf20Sopenharmony_ci urb->transfer_flags = 0; 3558c2ecf20Sopenharmony_ci urb->transfer_buffer = dev->adev.transfer_buffer[i]; 3568c2ecf20Sopenharmony_ci urb->complete = cx231xx_audio_bulkirq; 3578c2ecf20Sopenharmony_ci urb->transfer_buffer_length = sb_size; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci dev->adev.urb[i] = urb; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci for (i = 0; i < CX231XX_AUDIO_BUFS; i++) { 3648c2ecf20Sopenharmony_ci errCode = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC); 3658c2ecf20Sopenharmony_ci if (errCode < 0) { 3668c2ecf20Sopenharmony_ci cx231xx_bulk_audio_deinit(dev); 3678c2ecf20Sopenharmony_ci return errCode; 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci return errCode; 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_cx231xx_hw_capture = { 3758c2ecf20Sopenharmony_ci .info = SNDRV_PCM_INFO_BLOCK_TRANSFER | 3768c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP | 3778c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 3788c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID, 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT, 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci .rate_min = 48000, 3858c2ecf20Sopenharmony_ci .rate_max = 48000, 3868c2ecf20Sopenharmony_ci .channels_min = 2, 3878c2ecf20Sopenharmony_ci .channels_max = 2, 3888c2ecf20Sopenharmony_ci .buffer_bytes_max = 62720 * 8, /* just about the value in usbaudio.c */ 3898c2ecf20Sopenharmony_ci .period_bytes_min = 64, /* 12544/2, */ 3908c2ecf20Sopenharmony_ci .period_bytes_max = 12544, 3918c2ecf20Sopenharmony_ci .periods_min = 2, 3928c2ecf20Sopenharmony_ci .periods_max = 98, /* 12544, */ 3938c2ecf20Sopenharmony_ci}; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cistatic int snd_cx231xx_capture_open(struct snd_pcm_substream *substream) 3968c2ecf20Sopenharmony_ci{ 3978c2ecf20Sopenharmony_ci struct cx231xx *dev = snd_pcm_substream_chip(substream); 3988c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 3998c2ecf20Sopenharmony_ci int ret = 0; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci dev_dbg(dev->dev, 4028c2ecf20Sopenharmony_ci "opening device and trying to acquire exclusive lock\n"); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci if (dev->state & DEV_DISCONNECTED) { 4058c2ecf20Sopenharmony_ci dev_err(dev->dev, 4068c2ecf20Sopenharmony_ci "Can't open. the device was removed.\n"); 4078c2ecf20Sopenharmony_ci return -ENODEV; 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci /* set alternate setting for audio interface */ 4118c2ecf20Sopenharmony_ci /* 1 - 48000 samples per sec */ 4128c2ecf20Sopenharmony_ci mutex_lock(&dev->lock); 4138c2ecf20Sopenharmony_ci if (dev->USE_ISO) 4148c2ecf20Sopenharmony_ci ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 1); 4158c2ecf20Sopenharmony_ci else 4168c2ecf20Sopenharmony_ci ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 0); 4178c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 4188c2ecf20Sopenharmony_ci if (ret < 0) { 4198c2ecf20Sopenharmony_ci dev_err(dev->dev, 4208c2ecf20Sopenharmony_ci "failed to set alternate setting !\n"); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci return ret; 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci runtime->hw = snd_cx231xx_hw_capture; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci mutex_lock(&dev->lock); 4288c2ecf20Sopenharmony_ci /* inform hardware to start streaming */ 4298c2ecf20Sopenharmony_ci ret = cx231xx_capture_start(dev, 1, Audio); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci dev->adev.users++; 4328c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); 4358c2ecf20Sopenharmony_ci dev->adev.capture_pcm_substream = substream; 4368c2ecf20Sopenharmony_ci runtime->private_data = dev; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci return 0; 4398c2ecf20Sopenharmony_ci} 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_cistatic int snd_cx231xx_pcm_close(struct snd_pcm_substream *substream) 4428c2ecf20Sopenharmony_ci{ 4438c2ecf20Sopenharmony_ci int ret; 4448c2ecf20Sopenharmony_ci struct cx231xx *dev = snd_pcm_substream_chip(substream); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci dev_dbg(dev->dev, "closing device\n"); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci /* inform hardware to stop streaming */ 4498c2ecf20Sopenharmony_ci mutex_lock(&dev->lock); 4508c2ecf20Sopenharmony_ci ret = cx231xx_capture_start(dev, 0, Audio); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci /* set alternate setting for audio interface */ 4538c2ecf20Sopenharmony_ci /* 1 - 48000 samples per sec */ 4548c2ecf20Sopenharmony_ci ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 0); 4558c2ecf20Sopenharmony_ci if (ret < 0) { 4568c2ecf20Sopenharmony_ci dev_err(dev->dev, 4578c2ecf20Sopenharmony_ci "failed to set alternate setting !\n"); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 4608c2ecf20Sopenharmony_ci return ret; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci dev->adev.users--; 4648c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci if (dev->adev.users == 0 && dev->adev.shutdown == 1) { 4678c2ecf20Sopenharmony_ci dev_dbg(dev->dev, "audio users: %d\n", dev->adev.users); 4688c2ecf20Sopenharmony_ci dev_dbg(dev->dev, "disabling audio stream!\n"); 4698c2ecf20Sopenharmony_ci dev->adev.shutdown = 0; 4708c2ecf20Sopenharmony_ci dev_dbg(dev->dev, "released lock\n"); 4718c2ecf20Sopenharmony_ci if (atomic_read(&dev->stream_started) > 0) { 4728c2ecf20Sopenharmony_ci atomic_set(&dev->stream_started, 0); 4738c2ecf20Sopenharmony_ci schedule_work(&dev->wq_trigger); 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci return 0; 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_cistatic int snd_cx231xx_prepare(struct snd_pcm_substream *substream) 4808c2ecf20Sopenharmony_ci{ 4818c2ecf20Sopenharmony_ci struct cx231xx *dev = snd_pcm_substream_chip(substream); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci dev->adev.hwptr_done_capture = 0; 4848c2ecf20Sopenharmony_ci dev->adev.capture_transfer_done = 0; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci return 0; 4878c2ecf20Sopenharmony_ci} 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_cistatic void audio_trigger(struct work_struct *work) 4908c2ecf20Sopenharmony_ci{ 4918c2ecf20Sopenharmony_ci struct cx231xx *dev = container_of(work, struct cx231xx, wq_trigger); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci if (atomic_read(&dev->stream_started)) { 4948c2ecf20Sopenharmony_ci dev_dbg(dev->dev, "starting capture"); 4958c2ecf20Sopenharmony_ci if (is_fw_load(dev) == 0) 4968c2ecf20Sopenharmony_ci cx25840_call(dev, core, load_fw); 4978c2ecf20Sopenharmony_ci if (dev->USE_ISO) 4988c2ecf20Sopenharmony_ci cx231xx_init_audio_isoc(dev); 4998c2ecf20Sopenharmony_ci else 5008c2ecf20Sopenharmony_ci cx231xx_init_audio_bulk(dev); 5018c2ecf20Sopenharmony_ci } else { 5028c2ecf20Sopenharmony_ci dev_dbg(dev->dev, "stopping capture"); 5038c2ecf20Sopenharmony_ci cx231xx_isoc_audio_deinit(dev); 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic int snd_cx231xx_capture_trigger(struct snd_pcm_substream *substream, 5088c2ecf20Sopenharmony_ci int cmd) 5098c2ecf20Sopenharmony_ci{ 5108c2ecf20Sopenharmony_ci struct cx231xx *dev = snd_pcm_substream_chip(substream); 5118c2ecf20Sopenharmony_ci int retval = 0; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci if (dev->state & DEV_DISCONNECTED) 5148c2ecf20Sopenharmony_ci return -ENODEV; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci spin_lock(&dev->adev.slock); 5178c2ecf20Sopenharmony_ci switch (cmd) { 5188c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 5198c2ecf20Sopenharmony_ci atomic_set(&dev->stream_started, 1); 5208c2ecf20Sopenharmony_ci break; 5218c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 5228c2ecf20Sopenharmony_ci atomic_set(&dev->stream_started, 0); 5238c2ecf20Sopenharmony_ci break; 5248c2ecf20Sopenharmony_ci default: 5258c2ecf20Sopenharmony_ci retval = -EINVAL; 5268c2ecf20Sopenharmony_ci break; 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci spin_unlock(&dev->adev.slock); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci schedule_work(&dev->wq_trigger); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci return retval; 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_cx231xx_capture_pointer(struct snd_pcm_substream 5368c2ecf20Sopenharmony_ci *substream) 5378c2ecf20Sopenharmony_ci{ 5388c2ecf20Sopenharmony_ci struct cx231xx *dev; 5398c2ecf20Sopenharmony_ci unsigned long flags; 5408c2ecf20Sopenharmony_ci snd_pcm_uframes_t hwptr_done; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci dev = snd_pcm_substream_chip(substream); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->adev.slock, flags); 5458c2ecf20Sopenharmony_ci hwptr_done = dev->adev.hwptr_done_capture; 5468c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->adev.slock, flags); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci return hwptr_done; 5498c2ecf20Sopenharmony_ci} 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_cx231xx_pcm_capture = { 5528c2ecf20Sopenharmony_ci .open = snd_cx231xx_capture_open, 5538c2ecf20Sopenharmony_ci .close = snd_cx231xx_pcm_close, 5548c2ecf20Sopenharmony_ci .prepare = snd_cx231xx_prepare, 5558c2ecf20Sopenharmony_ci .trigger = snd_cx231xx_capture_trigger, 5568c2ecf20Sopenharmony_ci .pointer = snd_cx231xx_capture_pointer, 5578c2ecf20Sopenharmony_ci}; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_cistatic int cx231xx_audio_init(struct cx231xx *dev) 5608c2ecf20Sopenharmony_ci{ 5618c2ecf20Sopenharmony_ci struct cx231xx_audio *adev = &dev->adev; 5628c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 5638c2ecf20Sopenharmony_ci struct snd_card *card; 5648c2ecf20Sopenharmony_ci static int devnr; 5658c2ecf20Sopenharmony_ci int err; 5668c2ecf20Sopenharmony_ci struct usb_interface *uif; 5678c2ecf20Sopenharmony_ci int i, isoc_pipe = 0; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci if (dev->has_alsa_audio != 1) { 5708c2ecf20Sopenharmony_ci /* This device does not support the extension (in this case 5718c2ecf20Sopenharmony_ci the device is expecting the snd-usb-audio module or 5728c2ecf20Sopenharmony_ci doesn't have analog audio support at all) */ 5738c2ecf20Sopenharmony_ci return 0; 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci dev_dbg(dev->dev, 5778c2ecf20Sopenharmony_ci "probing for cx231xx non standard usbaudio\n"); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci err = snd_card_new(dev->dev, index[devnr], "Cx231xx Audio", 5808c2ecf20Sopenharmony_ci THIS_MODULE, 0, &card); 5818c2ecf20Sopenharmony_ci if (err < 0) 5828c2ecf20Sopenharmony_ci return err; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci spin_lock_init(&adev->slock); 5858c2ecf20Sopenharmony_ci err = snd_pcm_new(card, "Cx231xx Audio", 0, 0, 1, &pcm); 5868c2ecf20Sopenharmony_ci if (err < 0) 5878c2ecf20Sopenharmony_ci goto err_free_card; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, 5908c2ecf20Sopenharmony_ci &snd_cx231xx_pcm_capture); 5918c2ecf20Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0); 5928c2ecf20Sopenharmony_ci pcm->info_flags = 0; 5938c2ecf20Sopenharmony_ci pcm->private_data = dev; 5948c2ecf20Sopenharmony_ci strscpy(pcm->name, "Conexant cx231xx Capture", sizeof(pcm->name)); 5958c2ecf20Sopenharmony_ci strscpy(card->driver, "Cx231xx-Audio", sizeof(card->driver)); 5968c2ecf20Sopenharmony_ci strscpy(card->shortname, "Cx231xx Audio", sizeof(card->shortname)); 5978c2ecf20Sopenharmony_ci strscpy(card->longname, "Conexant cx231xx Audio", sizeof(card->longname)); 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci INIT_WORK(&dev->wq_trigger, audio_trigger); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci err = snd_card_register(card); 6028c2ecf20Sopenharmony_ci if (err < 0) 6038c2ecf20Sopenharmony_ci goto err_free_card; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci adev->sndcard = card; 6068c2ecf20Sopenharmony_ci adev->udev = dev->udev; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci /* compute alternate max packet sizes for Audio */ 6098c2ecf20Sopenharmony_ci uif = 6108c2ecf20Sopenharmony_ci dev->udev->actconfig->interface[dev->current_pcb_config. 6118c2ecf20Sopenharmony_ci hs_config_info[0].interface_info. 6128c2ecf20Sopenharmony_ci audio_index + 1]; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci if (uif->altsetting[0].desc.bNumEndpoints < isoc_pipe + 1) { 6158c2ecf20Sopenharmony_ci err = -ENODEV; 6168c2ecf20Sopenharmony_ci goto err_free_card; 6178c2ecf20Sopenharmony_ci } 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci adev->end_point_addr = 6208c2ecf20Sopenharmony_ci uif->altsetting[0].endpoint[isoc_pipe].desc. 6218c2ecf20Sopenharmony_ci bEndpointAddress; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci adev->num_alt = uif->num_altsetting; 6248c2ecf20Sopenharmony_ci dev_info(dev->dev, 6258c2ecf20Sopenharmony_ci "audio EndPoint Addr 0x%x, Alternate settings: %i\n", 6268c2ecf20Sopenharmony_ci adev->end_point_addr, adev->num_alt); 6278c2ecf20Sopenharmony_ci adev->alt_max_pkt_size = kmalloc_array(32, adev->num_alt, GFP_KERNEL); 6288c2ecf20Sopenharmony_ci if (!adev->alt_max_pkt_size) { 6298c2ecf20Sopenharmony_ci err = -ENOMEM; 6308c2ecf20Sopenharmony_ci goto err_free_card; 6318c2ecf20Sopenharmony_ci } 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci for (i = 0; i < adev->num_alt; i++) { 6348c2ecf20Sopenharmony_ci u16 tmp; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci if (uif->altsetting[i].desc.bNumEndpoints < isoc_pipe + 1) { 6378c2ecf20Sopenharmony_ci err = -ENODEV; 6388c2ecf20Sopenharmony_ci goto err_free_pkt_size; 6398c2ecf20Sopenharmony_ci } 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc. 6428c2ecf20Sopenharmony_ci wMaxPacketSize); 6438c2ecf20Sopenharmony_ci adev->alt_max_pkt_size[i] = 6448c2ecf20Sopenharmony_ci (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); 6458c2ecf20Sopenharmony_ci dev_dbg(dev->dev, 6468c2ecf20Sopenharmony_ci "audio alternate setting %i, max size= %i\n", i, 6478c2ecf20Sopenharmony_ci adev->alt_max_pkt_size[i]); 6488c2ecf20Sopenharmony_ci } 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci return 0; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_cierr_free_pkt_size: 6538c2ecf20Sopenharmony_ci kfree(adev->alt_max_pkt_size); 6548c2ecf20Sopenharmony_cierr_free_card: 6558c2ecf20Sopenharmony_ci snd_card_free(card); 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci return err; 6588c2ecf20Sopenharmony_ci} 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_cistatic int cx231xx_audio_fini(struct cx231xx *dev) 6618c2ecf20Sopenharmony_ci{ 6628c2ecf20Sopenharmony_ci if (dev == NULL) 6638c2ecf20Sopenharmony_ci return 0; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci if (dev->has_alsa_audio != 1) { 6668c2ecf20Sopenharmony_ci /* This device does not support the extension (in this case 6678c2ecf20Sopenharmony_ci the device is expecting the snd-usb-audio module or 6688c2ecf20Sopenharmony_ci doesn't have analog audio support at all) */ 6698c2ecf20Sopenharmony_ci return 0; 6708c2ecf20Sopenharmony_ci } 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci if (dev->adev.sndcard) { 6738c2ecf20Sopenharmony_ci snd_card_free(dev->adev.sndcard); 6748c2ecf20Sopenharmony_ci kfree(dev->adev.alt_max_pkt_size); 6758c2ecf20Sopenharmony_ci dev->adev.sndcard = NULL; 6768c2ecf20Sopenharmony_ci } 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci return 0; 6798c2ecf20Sopenharmony_ci} 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_cistatic struct cx231xx_ops audio_ops = { 6828c2ecf20Sopenharmony_ci .id = CX231XX_AUDIO, 6838c2ecf20Sopenharmony_ci .name = "Cx231xx Audio Extension", 6848c2ecf20Sopenharmony_ci .init = cx231xx_audio_init, 6858c2ecf20Sopenharmony_ci .fini = cx231xx_audio_fini, 6868c2ecf20Sopenharmony_ci}; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_cistatic int __init cx231xx_alsa_register(void) 6898c2ecf20Sopenharmony_ci{ 6908c2ecf20Sopenharmony_ci return cx231xx_register_extension(&audio_ops); 6918c2ecf20Sopenharmony_ci} 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_cistatic void __exit cx231xx_alsa_unregister(void) 6948c2ecf20Sopenharmony_ci{ 6958c2ecf20Sopenharmony_ci cx231xx_unregister_extension(&audio_ops); 6968c2ecf20Sopenharmony_ci} 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 6998c2ecf20Sopenharmony_ciMODULE_AUTHOR("Srinivasa Deevi <srinivasa.deevi@conexant.com>"); 7008c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Cx231xx Audio driver"); 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_cimodule_init(cx231xx_alsa_register); 7038c2ecf20Sopenharmony_cimodule_exit(cx231xx_alsa_unregister); 704