162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Conexant Cx231xx audio extension 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2008 <srinivasa.deevi at conexant dot com> 662306a36Sopenharmony_ci * Based on em28xx driver 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include "cx231xx.h" 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/init.h> 1262306a36Sopenharmony_ci#include <linux/sound.h> 1362306a36Sopenharmony_ci#include <linux/spinlock.h> 1462306a36Sopenharmony_ci#include <linux/soundcard.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <sound/core.h> 1862306a36Sopenharmony_ci#include <sound/pcm.h> 1962306a36Sopenharmony_ci#include <sound/pcm_params.h> 2062306a36Sopenharmony_ci#include <sound/info.h> 2162306a36Sopenharmony_ci#include <sound/initval.h> 2262306a36Sopenharmony_ci#include <sound/control.h> 2362306a36Sopenharmony_ci#include <media/v4l2-common.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic int debug; 2662306a36Sopenharmony_cimodule_param(debug, int, 0644); 2762306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "activates debug info"); 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic int cx231xx_isoc_audio_deinit(struct cx231xx *dev) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci int i; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci dev_dbg(dev->dev, "Stopping isoc\n"); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci for (i = 0; i < CX231XX_AUDIO_BUFS; i++) { 3862306a36Sopenharmony_ci if (dev->adev.urb[i]) { 3962306a36Sopenharmony_ci if (!irqs_disabled()) 4062306a36Sopenharmony_ci usb_kill_urb(dev->adev.urb[i]); 4162306a36Sopenharmony_ci else 4262306a36Sopenharmony_ci usb_unlink_urb(dev->adev.urb[i]); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci usb_free_urb(dev->adev.urb[i]); 4562306a36Sopenharmony_ci dev->adev.urb[i] = NULL; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci kfree(dev->adev.transfer_buffer[i]); 4862306a36Sopenharmony_ci dev->adev.transfer_buffer[i] = NULL; 4962306a36Sopenharmony_ci } 5062306a36Sopenharmony_ci } 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci return 0; 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic int cx231xx_bulk_audio_deinit(struct cx231xx *dev) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci int i; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci dev_dbg(dev->dev, "Stopping bulk\n"); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci for (i = 0; i < CX231XX_AUDIO_BUFS; i++) { 6262306a36Sopenharmony_ci if (dev->adev.urb[i]) { 6362306a36Sopenharmony_ci if (!irqs_disabled()) 6462306a36Sopenharmony_ci usb_kill_urb(dev->adev.urb[i]); 6562306a36Sopenharmony_ci else 6662306a36Sopenharmony_ci usb_unlink_urb(dev->adev.urb[i]); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci usb_free_urb(dev->adev.urb[i]); 6962306a36Sopenharmony_ci dev->adev.urb[i] = NULL; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci kfree(dev->adev.transfer_buffer[i]); 7262306a36Sopenharmony_ci dev->adev.transfer_buffer[i] = NULL; 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci return 0; 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic void cx231xx_audio_isocirq(struct urb *urb) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci struct cx231xx *dev = urb->context; 8262306a36Sopenharmony_ci int i; 8362306a36Sopenharmony_ci unsigned int oldptr; 8462306a36Sopenharmony_ci int period_elapsed = 0; 8562306a36Sopenharmony_ci int status; 8662306a36Sopenharmony_ci unsigned char *cp; 8762306a36Sopenharmony_ci unsigned int stride; 8862306a36Sopenharmony_ci struct snd_pcm_substream *substream; 8962306a36Sopenharmony_ci struct snd_pcm_runtime *runtime; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (dev->state & DEV_DISCONNECTED) 9262306a36Sopenharmony_ci return; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci switch (urb->status) { 9562306a36Sopenharmony_ci case 0: /* success */ 9662306a36Sopenharmony_ci case -ETIMEDOUT: /* NAK */ 9762306a36Sopenharmony_ci break; 9862306a36Sopenharmony_ci case -ECONNRESET: /* kill */ 9962306a36Sopenharmony_ci case -ENOENT: 10062306a36Sopenharmony_ci case -ESHUTDOWN: 10162306a36Sopenharmony_ci return; 10262306a36Sopenharmony_ci default: /* error */ 10362306a36Sopenharmony_ci dev_dbg(dev->dev, "urb completion error %d.\n", 10462306a36Sopenharmony_ci urb->status); 10562306a36Sopenharmony_ci break; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (atomic_read(&dev->stream_started) == 0) 10962306a36Sopenharmony_ci return; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci if (dev->adev.capture_pcm_substream) { 11262306a36Sopenharmony_ci substream = dev->adev.capture_pcm_substream; 11362306a36Sopenharmony_ci runtime = substream->runtime; 11462306a36Sopenharmony_ci stride = runtime->frame_bits >> 3; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci for (i = 0; i < urb->number_of_packets; i++) { 11762306a36Sopenharmony_ci unsigned long flags; 11862306a36Sopenharmony_ci int length = urb->iso_frame_desc[i].actual_length / 11962306a36Sopenharmony_ci stride; 12062306a36Sopenharmony_ci cp = (unsigned char *)urb->transfer_buffer + 12162306a36Sopenharmony_ci urb->iso_frame_desc[i].offset; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (!length) 12462306a36Sopenharmony_ci continue; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci oldptr = dev->adev.hwptr_done_capture; 12762306a36Sopenharmony_ci if (oldptr + length >= runtime->buffer_size) { 12862306a36Sopenharmony_ci unsigned int cnt; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci cnt = runtime->buffer_size - oldptr; 13162306a36Sopenharmony_ci memcpy(runtime->dma_area + oldptr * stride, cp, 13262306a36Sopenharmony_ci cnt * stride); 13362306a36Sopenharmony_ci memcpy(runtime->dma_area, cp + cnt * stride, 13462306a36Sopenharmony_ci length * stride - cnt * stride); 13562306a36Sopenharmony_ci } else { 13662306a36Sopenharmony_ci memcpy(runtime->dma_area + oldptr * stride, cp, 13762306a36Sopenharmony_ci length * stride); 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci snd_pcm_stream_lock_irqsave(substream, flags); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci dev->adev.hwptr_done_capture += length; 14362306a36Sopenharmony_ci if (dev->adev.hwptr_done_capture >= 14462306a36Sopenharmony_ci runtime->buffer_size) 14562306a36Sopenharmony_ci dev->adev.hwptr_done_capture -= 14662306a36Sopenharmony_ci runtime->buffer_size; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci dev->adev.capture_transfer_done += length; 14962306a36Sopenharmony_ci if (dev->adev.capture_transfer_done >= 15062306a36Sopenharmony_ci runtime->period_size) { 15162306a36Sopenharmony_ci dev->adev.capture_transfer_done -= 15262306a36Sopenharmony_ci runtime->period_size; 15362306a36Sopenharmony_ci period_elapsed = 1; 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci snd_pcm_stream_unlock_irqrestore(substream, flags); 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci if (period_elapsed) 15862306a36Sopenharmony_ci snd_pcm_period_elapsed(substream); 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci urb->status = 0; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci status = usb_submit_urb(urb, GFP_ATOMIC); 16362306a36Sopenharmony_ci if (status < 0) { 16462306a36Sopenharmony_ci dev_err(dev->dev, 16562306a36Sopenharmony_ci "resubmit of audio urb failed (error=%i)\n", 16662306a36Sopenharmony_ci status); 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci return; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic void cx231xx_audio_bulkirq(struct urb *urb) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci struct cx231xx *dev = urb->context; 17462306a36Sopenharmony_ci unsigned int oldptr; 17562306a36Sopenharmony_ci int period_elapsed = 0; 17662306a36Sopenharmony_ci int status; 17762306a36Sopenharmony_ci unsigned char *cp; 17862306a36Sopenharmony_ci unsigned int stride; 17962306a36Sopenharmony_ci struct snd_pcm_substream *substream; 18062306a36Sopenharmony_ci struct snd_pcm_runtime *runtime; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if (dev->state & DEV_DISCONNECTED) 18362306a36Sopenharmony_ci return; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci switch (urb->status) { 18662306a36Sopenharmony_ci case 0: /* success */ 18762306a36Sopenharmony_ci case -ETIMEDOUT: /* NAK */ 18862306a36Sopenharmony_ci break; 18962306a36Sopenharmony_ci case -ECONNRESET: /* kill */ 19062306a36Sopenharmony_ci case -ENOENT: 19162306a36Sopenharmony_ci case -ESHUTDOWN: 19262306a36Sopenharmony_ci return; 19362306a36Sopenharmony_ci default: /* error */ 19462306a36Sopenharmony_ci dev_dbg(dev->dev, "urb completion error %d.\n", 19562306a36Sopenharmony_ci urb->status); 19662306a36Sopenharmony_ci break; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (atomic_read(&dev->stream_started) == 0) 20062306a36Sopenharmony_ci return; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (dev->adev.capture_pcm_substream) { 20362306a36Sopenharmony_ci substream = dev->adev.capture_pcm_substream; 20462306a36Sopenharmony_ci runtime = substream->runtime; 20562306a36Sopenharmony_ci stride = runtime->frame_bits >> 3; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (1) { 20862306a36Sopenharmony_ci unsigned long flags; 20962306a36Sopenharmony_ci int length = urb->actual_length / 21062306a36Sopenharmony_ci stride; 21162306a36Sopenharmony_ci cp = (unsigned char *)urb->transfer_buffer; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci oldptr = dev->adev.hwptr_done_capture; 21462306a36Sopenharmony_ci if (oldptr + length >= runtime->buffer_size) { 21562306a36Sopenharmony_ci unsigned int cnt; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci cnt = runtime->buffer_size - oldptr; 21862306a36Sopenharmony_ci memcpy(runtime->dma_area + oldptr * stride, cp, 21962306a36Sopenharmony_ci cnt * stride); 22062306a36Sopenharmony_ci memcpy(runtime->dma_area, cp + cnt * stride, 22162306a36Sopenharmony_ci length * stride - cnt * stride); 22262306a36Sopenharmony_ci } else { 22362306a36Sopenharmony_ci memcpy(runtime->dma_area + oldptr * stride, cp, 22462306a36Sopenharmony_ci length * stride); 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci snd_pcm_stream_lock_irqsave(substream, flags); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci dev->adev.hwptr_done_capture += length; 23062306a36Sopenharmony_ci if (dev->adev.hwptr_done_capture >= 23162306a36Sopenharmony_ci runtime->buffer_size) 23262306a36Sopenharmony_ci dev->adev.hwptr_done_capture -= 23362306a36Sopenharmony_ci runtime->buffer_size; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci dev->adev.capture_transfer_done += length; 23662306a36Sopenharmony_ci if (dev->adev.capture_transfer_done >= 23762306a36Sopenharmony_ci runtime->period_size) { 23862306a36Sopenharmony_ci dev->adev.capture_transfer_done -= 23962306a36Sopenharmony_ci runtime->period_size; 24062306a36Sopenharmony_ci period_elapsed = 1; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci snd_pcm_stream_unlock_irqrestore(substream, flags); 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci if (period_elapsed) 24562306a36Sopenharmony_ci snd_pcm_period_elapsed(substream); 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci urb->status = 0; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci status = usb_submit_urb(urb, GFP_ATOMIC); 25062306a36Sopenharmony_ci if (status < 0) { 25162306a36Sopenharmony_ci dev_err(dev->dev, 25262306a36Sopenharmony_ci "resubmit of audio urb failed (error=%i)\n", 25362306a36Sopenharmony_ci status); 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci return; 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_cistatic int cx231xx_init_audio_isoc(struct cx231xx *dev) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci int i, errCode; 26162306a36Sopenharmony_ci int sb_size; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci dev_dbg(dev->dev, 26462306a36Sopenharmony_ci "%s: Starting ISO AUDIO transfers\n", __func__); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (dev->state & DEV_DISCONNECTED) 26762306a36Sopenharmony_ci return -ENODEV; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci sb_size = CX231XX_ISO_NUM_AUDIO_PACKETS * dev->adev.max_pkt_size; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci for (i = 0; i < CX231XX_AUDIO_BUFS; i++) { 27262306a36Sopenharmony_ci struct urb *urb; 27362306a36Sopenharmony_ci int j, k; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci dev->adev.transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC); 27662306a36Sopenharmony_ci if (!dev->adev.transfer_buffer[i]) 27762306a36Sopenharmony_ci return -ENOMEM; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci memset(dev->adev.transfer_buffer[i], 0x80, sb_size); 28062306a36Sopenharmony_ci urb = usb_alloc_urb(CX231XX_ISO_NUM_AUDIO_PACKETS, GFP_ATOMIC); 28162306a36Sopenharmony_ci if (!urb) { 28262306a36Sopenharmony_ci for (j = 0; j < i; j++) { 28362306a36Sopenharmony_ci usb_free_urb(dev->adev.urb[j]); 28462306a36Sopenharmony_ci kfree(dev->adev.transfer_buffer[j]); 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci return -ENOMEM; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci urb->dev = dev->udev; 29062306a36Sopenharmony_ci urb->context = dev; 29162306a36Sopenharmony_ci urb->pipe = usb_rcvisocpipe(dev->udev, 29262306a36Sopenharmony_ci dev->adev.end_point_addr); 29362306a36Sopenharmony_ci urb->transfer_flags = URB_ISO_ASAP; 29462306a36Sopenharmony_ci urb->transfer_buffer = dev->adev.transfer_buffer[i]; 29562306a36Sopenharmony_ci urb->interval = 1; 29662306a36Sopenharmony_ci urb->complete = cx231xx_audio_isocirq; 29762306a36Sopenharmony_ci urb->number_of_packets = CX231XX_ISO_NUM_AUDIO_PACKETS; 29862306a36Sopenharmony_ci urb->transfer_buffer_length = sb_size; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci for (j = k = 0; j < CX231XX_ISO_NUM_AUDIO_PACKETS; 30162306a36Sopenharmony_ci j++, k += dev->adev.max_pkt_size) { 30262306a36Sopenharmony_ci urb->iso_frame_desc[j].offset = k; 30362306a36Sopenharmony_ci urb->iso_frame_desc[j].length = dev->adev.max_pkt_size; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci dev->adev.urb[i] = urb; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci for (i = 0; i < CX231XX_AUDIO_BUFS; i++) { 30962306a36Sopenharmony_ci errCode = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC); 31062306a36Sopenharmony_ci if (errCode < 0) { 31162306a36Sopenharmony_ci cx231xx_isoc_audio_deinit(dev); 31262306a36Sopenharmony_ci return errCode; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci return errCode; 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_cistatic int cx231xx_init_audio_bulk(struct cx231xx *dev) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci int i, errCode; 32262306a36Sopenharmony_ci int sb_size; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci dev_dbg(dev->dev, 32562306a36Sopenharmony_ci "%s: Starting BULK AUDIO transfers\n", __func__); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci if (dev->state & DEV_DISCONNECTED) 32862306a36Sopenharmony_ci return -ENODEV; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci sb_size = CX231XX_NUM_AUDIO_PACKETS * dev->adev.max_pkt_size; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci for (i = 0; i < CX231XX_AUDIO_BUFS; i++) { 33362306a36Sopenharmony_ci struct urb *urb; 33462306a36Sopenharmony_ci int j; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci dev->adev.transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC); 33762306a36Sopenharmony_ci if (!dev->adev.transfer_buffer[i]) 33862306a36Sopenharmony_ci return -ENOMEM; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci memset(dev->adev.transfer_buffer[i], 0x80, sb_size); 34162306a36Sopenharmony_ci urb = usb_alloc_urb(CX231XX_NUM_AUDIO_PACKETS, GFP_ATOMIC); 34262306a36Sopenharmony_ci if (!urb) { 34362306a36Sopenharmony_ci for (j = 0; j < i; j++) { 34462306a36Sopenharmony_ci usb_free_urb(dev->adev.urb[j]); 34562306a36Sopenharmony_ci kfree(dev->adev.transfer_buffer[j]); 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci return -ENOMEM; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci urb->dev = dev->udev; 35162306a36Sopenharmony_ci urb->context = dev; 35262306a36Sopenharmony_ci urb->pipe = usb_rcvbulkpipe(dev->udev, 35362306a36Sopenharmony_ci dev->adev.end_point_addr); 35462306a36Sopenharmony_ci urb->transfer_flags = 0; 35562306a36Sopenharmony_ci urb->transfer_buffer = dev->adev.transfer_buffer[i]; 35662306a36Sopenharmony_ci urb->complete = cx231xx_audio_bulkirq; 35762306a36Sopenharmony_ci urb->transfer_buffer_length = sb_size; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci dev->adev.urb[i] = urb; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci for (i = 0; i < CX231XX_AUDIO_BUFS; i++) { 36462306a36Sopenharmony_ci errCode = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC); 36562306a36Sopenharmony_ci if (errCode < 0) { 36662306a36Sopenharmony_ci cx231xx_bulk_audio_deinit(dev); 36762306a36Sopenharmony_ci return errCode; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci return errCode; 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_cx231xx_hw_capture = { 37562306a36Sopenharmony_ci .info = SNDRV_PCM_INFO_BLOCK_TRANSFER | 37662306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP | 37762306a36Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 37862306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID, 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT, 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci .rate_min = 48000, 38562306a36Sopenharmony_ci .rate_max = 48000, 38662306a36Sopenharmony_ci .channels_min = 2, 38762306a36Sopenharmony_ci .channels_max = 2, 38862306a36Sopenharmony_ci .buffer_bytes_max = 62720 * 8, /* just about the value in usbaudio.c */ 38962306a36Sopenharmony_ci .period_bytes_min = 64, /* 12544/2, */ 39062306a36Sopenharmony_ci .period_bytes_max = 12544, 39162306a36Sopenharmony_ci .periods_min = 2, 39262306a36Sopenharmony_ci .periods_max = 98, /* 12544, */ 39362306a36Sopenharmony_ci}; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_cistatic int snd_cx231xx_capture_open(struct snd_pcm_substream *substream) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci struct cx231xx *dev = snd_pcm_substream_chip(substream); 39862306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 39962306a36Sopenharmony_ci int ret = 0; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci dev_dbg(dev->dev, 40262306a36Sopenharmony_ci "opening device and trying to acquire exclusive lock\n"); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci if (dev->state & DEV_DISCONNECTED) { 40562306a36Sopenharmony_ci dev_err(dev->dev, 40662306a36Sopenharmony_ci "Can't open. the device was removed.\n"); 40762306a36Sopenharmony_ci return -ENODEV; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci /* set alternate setting for audio interface */ 41162306a36Sopenharmony_ci /* 1 - 48000 samples per sec */ 41262306a36Sopenharmony_ci mutex_lock(&dev->lock); 41362306a36Sopenharmony_ci if (dev->USE_ISO) 41462306a36Sopenharmony_ci ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 1); 41562306a36Sopenharmony_ci else 41662306a36Sopenharmony_ci ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 0); 41762306a36Sopenharmony_ci mutex_unlock(&dev->lock); 41862306a36Sopenharmony_ci if (ret < 0) { 41962306a36Sopenharmony_ci dev_err(dev->dev, 42062306a36Sopenharmony_ci "failed to set alternate setting !\n"); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci return ret; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci runtime->hw = snd_cx231xx_hw_capture; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci mutex_lock(&dev->lock); 42862306a36Sopenharmony_ci /* inform hardware to start streaming */ 42962306a36Sopenharmony_ci ret = cx231xx_capture_start(dev, 1, Audio); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci dev->adev.users++; 43262306a36Sopenharmony_ci mutex_unlock(&dev->lock); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); 43562306a36Sopenharmony_ci dev->adev.capture_pcm_substream = substream; 43662306a36Sopenharmony_ci runtime->private_data = dev; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci return 0; 43962306a36Sopenharmony_ci} 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_cistatic int snd_cx231xx_pcm_close(struct snd_pcm_substream *substream) 44262306a36Sopenharmony_ci{ 44362306a36Sopenharmony_ci int ret; 44462306a36Sopenharmony_ci struct cx231xx *dev = snd_pcm_substream_chip(substream); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci dev_dbg(dev->dev, "closing device\n"); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci /* inform hardware to stop streaming */ 44962306a36Sopenharmony_ci mutex_lock(&dev->lock); 45062306a36Sopenharmony_ci ret = cx231xx_capture_start(dev, 0, Audio); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci /* set alternate setting for audio interface */ 45362306a36Sopenharmony_ci /* 1 - 48000 samples per sec */ 45462306a36Sopenharmony_ci ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 0); 45562306a36Sopenharmony_ci if (ret < 0) { 45662306a36Sopenharmony_ci dev_err(dev->dev, 45762306a36Sopenharmony_ci "failed to set alternate setting !\n"); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci mutex_unlock(&dev->lock); 46062306a36Sopenharmony_ci return ret; 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci dev->adev.users--; 46462306a36Sopenharmony_ci mutex_unlock(&dev->lock); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci if (dev->adev.users == 0 && dev->adev.shutdown == 1) { 46762306a36Sopenharmony_ci dev_dbg(dev->dev, "audio users: %d\n", dev->adev.users); 46862306a36Sopenharmony_ci dev_dbg(dev->dev, "disabling audio stream!\n"); 46962306a36Sopenharmony_ci dev->adev.shutdown = 0; 47062306a36Sopenharmony_ci dev_dbg(dev->dev, "released lock\n"); 47162306a36Sopenharmony_ci if (atomic_read(&dev->stream_started) > 0) { 47262306a36Sopenharmony_ci atomic_set(&dev->stream_started, 0); 47362306a36Sopenharmony_ci schedule_work(&dev->wq_trigger); 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci return 0; 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic int snd_cx231xx_prepare(struct snd_pcm_substream *substream) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci struct cx231xx *dev = snd_pcm_substream_chip(substream); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci dev->adev.hwptr_done_capture = 0; 48462306a36Sopenharmony_ci dev->adev.capture_transfer_done = 0; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci return 0; 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cistatic void audio_trigger(struct work_struct *work) 49062306a36Sopenharmony_ci{ 49162306a36Sopenharmony_ci struct cx231xx *dev = container_of(work, struct cx231xx, wq_trigger); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci if (atomic_read(&dev->stream_started)) { 49462306a36Sopenharmony_ci dev_dbg(dev->dev, "starting capture"); 49562306a36Sopenharmony_ci if (is_fw_load(dev) == 0) 49662306a36Sopenharmony_ci cx25840_call(dev, core, load_fw); 49762306a36Sopenharmony_ci if (dev->USE_ISO) 49862306a36Sopenharmony_ci cx231xx_init_audio_isoc(dev); 49962306a36Sopenharmony_ci else 50062306a36Sopenharmony_ci cx231xx_init_audio_bulk(dev); 50162306a36Sopenharmony_ci } else { 50262306a36Sopenharmony_ci dev_dbg(dev->dev, "stopping capture"); 50362306a36Sopenharmony_ci cx231xx_isoc_audio_deinit(dev); 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci} 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_cistatic int snd_cx231xx_capture_trigger(struct snd_pcm_substream *substream, 50862306a36Sopenharmony_ci int cmd) 50962306a36Sopenharmony_ci{ 51062306a36Sopenharmony_ci struct cx231xx *dev = snd_pcm_substream_chip(substream); 51162306a36Sopenharmony_ci int retval = 0; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci if (dev->state & DEV_DISCONNECTED) 51462306a36Sopenharmony_ci return -ENODEV; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci spin_lock(&dev->adev.slock); 51762306a36Sopenharmony_ci switch (cmd) { 51862306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 51962306a36Sopenharmony_ci atomic_set(&dev->stream_started, 1); 52062306a36Sopenharmony_ci break; 52162306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 52262306a36Sopenharmony_ci atomic_set(&dev->stream_started, 0); 52362306a36Sopenharmony_ci break; 52462306a36Sopenharmony_ci default: 52562306a36Sopenharmony_ci retval = -EINVAL; 52662306a36Sopenharmony_ci break; 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci spin_unlock(&dev->adev.slock); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci schedule_work(&dev->wq_trigger); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci return retval; 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_cx231xx_capture_pointer(struct snd_pcm_substream 53662306a36Sopenharmony_ci *substream) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci struct cx231xx *dev; 53962306a36Sopenharmony_ci unsigned long flags; 54062306a36Sopenharmony_ci snd_pcm_uframes_t hwptr_done; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci dev = snd_pcm_substream_chip(substream); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci spin_lock_irqsave(&dev->adev.slock, flags); 54562306a36Sopenharmony_ci hwptr_done = dev->adev.hwptr_done_capture; 54662306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->adev.slock, flags); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci return hwptr_done; 54962306a36Sopenharmony_ci} 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_cx231xx_pcm_capture = { 55262306a36Sopenharmony_ci .open = snd_cx231xx_capture_open, 55362306a36Sopenharmony_ci .close = snd_cx231xx_pcm_close, 55462306a36Sopenharmony_ci .prepare = snd_cx231xx_prepare, 55562306a36Sopenharmony_ci .trigger = snd_cx231xx_capture_trigger, 55662306a36Sopenharmony_ci .pointer = snd_cx231xx_capture_pointer, 55762306a36Sopenharmony_ci}; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_cistatic int cx231xx_audio_init(struct cx231xx *dev) 56062306a36Sopenharmony_ci{ 56162306a36Sopenharmony_ci struct cx231xx_audio *adev = &dev->adev; 56262306a36Sopenharmony_ci struct snd_pcm *pcm; 56362306a36Sopenharmony_ci struct snd_card *card; 56462306a36Sopenharmony_ci static int devnr; 56562306a36Sopenharmony_ci int err; 56662306a36Sopenharmony_ci struct usb_interface *uif; 56762306a36Sopenharmony_ci int i, isoc_pipe = 0; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci if (dev->has_alsa_audio != 1) { 57062306a36Sopenharmony_ci /* This device does not support the extension (in this case 57162306a36Sopenharmony_ci the device is expecting the snd-usb-audio module or 57262306a36Sopenharmony_ci doesn't have analog audio support at all) */ 57362306a36Sopenharmony_ci return 0; 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci dev_dbg(dev->dev, 57762306a36Sopenharmony_ci "probing for cx231xx non standard usbaudio\n"); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci err = snd_card_new(dev->dev, index[devnr], "Cx231xx Audio", 58062306a36Sopenharmony_ci THIS_MODULE, 0, &card); 58162306a36Sopenharmony_ci if (err < 0) 58262306a36Sopenharmony_ci return err; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci spin_lock_init(&adev->slock); 58562306a36Sopenharmony_ci err = snd_pcm_new(card, "Cx231xx Audio", 0, 0, 1, &pcm); 58662306a36Sopenharmony_ci if (err < 0) 58762306a36Sopenharmony_ci goto err_free_card; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, 59062306a36Sopenharmony_ci &snd_cx231xx_pcm_capture); 59162306a36Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0); 59262306a36Sopenharmony_ci pcm->info_flags = 0; 59362306a36Sopenharmony_ci pcm->private_data = dev; 59462306a36Sopenharmony_ci strscpy(pcm->name, "Conexant cx231xx Capture", sizeof(pcm->name)); 59562306a36Sopenharmony_ci strscpy(card->driver, "Cx231xx-Audio", sizeof(card->driver)); 59662306a36Sopenharmony_ci strscpy(card->shortname, "Cx231xx Audio", sizeof(card->shortname)); 59762306a36Sopenharmony_ci strscpy(card->longname, "Conexant cx231xx Audio", sizeof(card->longname)); 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci INIT_WORK(&dev->wq_trigger, audio_trigger); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci err = snd_card_register(card); 60262306a36Sopenharmony_ci if (err < 0) 60362306a36Sopenharmony_ci goto err_free_card; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci adev->sndcard = card; 60662306a36Sopenharmony_ci adev->udev = dev->udev; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci /* compute alternate max packet sizes for Audio */ 60962306a36Sopenharmony_ci uif = 61062306a36Sopenharmony_ci dev->udev->actconfig->interface[dev->current_pcb_config. 61162306a36Sopenharmony_ci hs_config_info[0].interface_info. 61262306a36Sopenharmony_ci audio_index + 1]; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci if (uif->altsetting[0].desc.bNumEndpoints < isoc_pipe + 1) { 61562306a36Sopenharmony_ci err = -ENODEV; 61662306a36Sopenharmony_ci goto err_free_card; 61762306a36Sopenharmony_ci } 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci adev->end_point_addr = 62062306a36Sopenharmony_ci uif->altsetting[0].endpoint[isoc_pipe].desc. 62162306a36Sopenharmony_ci bEndpointAddress; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci adev->num_alt = uif->num_altsetting; 62462306a36Sopenharmony_ci dev_info(dev->dev, 62562306a36Sopenharmony_ci "audio EndPoint Addr 0x%x, Alternate settings: %i\n", 62662306a36Sopenharmony_ci adev->end_point_addr, adev->num_alt); 62762306a36Sopenharmony_ci adev->alt_max_pkt_size = kmalloc_array(32, adev->num_alt, GFP_KERNEL); 62862306a36Sopenharmony_ci if (!adev->alt_max_pkt_size) { 62962306a36Sopenharmony_ci err = -ENOMEM; 63062306a36Sopenharmony_ci goto err_free_card; 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci for (i = 0; i < adev->num_alt; i++) { 63462306a36Sopenharmony_ci u16 tmp; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci if (uif->altsetting[i].desc.bNumEndpoints < isoc_pipe + 1) { 63762306a36Sopenharmony_ci err = -ENODEV; 63862306a36Sopenharmony_ci goto err_free_pkt_size; 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc. 64262306a36Sopenharmony_ci wMaxPacketSize); 64362306a36Sopenharmony_ci adev->alt_max_pkt_size[i] = 64462306a36Sopenharmony_ci (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); 64562306a36Sopenharmony_ci dev_dbg(dev->dev, 64662306a36Sopenharmony_ci "audio alternate setting %i, max size= %i\n", i, 64762306a36Sopenharmony_ci adev->alt_max_pkt_size[i]); 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci return 0; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_cierr_free_pkt_size: 65362306a36Sopenharmony_ci kfree(adev->alt_max_pkt_size); 65462306a36Sopenharmony_cierr_free_card: 65562306a36Sopenharmony_ci snd_card_free(card); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci return err; 65862306a36Sopenharmony_ci} 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_cistatic int cx231xx_audio_fini(struct cx231xx *dev) 66162306a36Sopenharmony_ci{ 66262306a36Sopenharmony_ci if (dev == NULL) 66362306a36Sopenharmony_ci return 0; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci if (dev->has_alsa_audio != 1) { 66662306a36Sopenharmony_ci /* This device does not support the extension (in this case 66762306a36Sopenharmony_ci the device is expecting the snd-usb-audio module or 66862306a36Sopenharmony_ci doesn't have analog audio support at all) */ 66962306a36Sopenharmony_ci return 0; 67062306a36Sopenharmony_ci } 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci if (dev->adev.sndcard) { 67362306a36Sopenharmony_ci snd_card_free_when_closed(dev->adev.sndcard); 67462306a36Sopenharmony_ci kfree(dev->adev.alt_max_pkt_size); 67562306a36Sopenharmony_ci dev->adev.sndcard = NULL; 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci return 0; 67962306a36Sopenharmony_ci} 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_cistatic struct cx231xx_ops audio_ops = { 68262306a36Sopenharmony_ci .id = CX231XX_AUDIO, 68362306a36Sopenharmony_ci .name = "Cx231xx Audio Extension", 68462306a36Sopenharmony_ci .init = cx231xx_audio_init, 68562306a36Sopenharmony_ci .fini = cx231xx_audio_fini, 68662306a36Sopenharmony_ci}; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_cistatic int __init cx231xx_alsa_register(void) 68962306a36Sopenharmony_ci{ 69062306a36Sopenharmony_ci return cx231xx_register_extension(&audio_ops); 69162306a36Sopenharmony_ci} 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_cistatic void __exit cx231xx_alsa_unregister(void) 69462306a36Sopenharmony_ci{ 69562306a36Sopenharmony_ci cx231xx_unregister_extension(&audio_ops); 69662306a36Sopenharmony_ci} 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 69962306a36Sopenharmony_ciMODULE_AUTHOR("Srinivasa Deevi <srinivasa.deevi@conexant.com>"); 70062306a36Sopenharmony_ciMODULE_DESCRIPTION("Cx231xx Audio driver"); 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_cimodule_init(cx231xx_alsa_register); 70362306a36Sopenharmony_cimodule_exit(cx231xx_alsa_unregister); 704