Path: blob/master/drivers/media/video/cx231xx/cx231xx-audio.c
17956 views
/*1* Conexant Cx231xx audio extension2*3* Copyright (C) 2008 <srinivasa.deevi at conexant dot com>4* Based on em28xx driver5*6*7* This program is free software; you can redistribute it and/or modify8* it under the terms of the GNU General Public License as published by9* the Free Software Foundation; either version 2 of the License, or10* (at your option) any later version.11*12* This program is distributed in the hope that it will be useful,13* but WITHOUT ANY WARRANTY; without even the implied warranty of14* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the15* GNU General Public License for more details.16*17* You should have received a copy of the GNU General Public License18* along with this program; if not, write to the Free Software19* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.20*/2122#include <linux/kernel.h>23#include <linux/usb.h>24#include <linux/init.h>25#include <linux/sound.h>26#include <linux/spinlock.h>27#include <linux/soundcard.h>28#include <linux/slab.h>29#include <linux/vmalloc.h>30#include <linux/proc_fs.h>31#include <linux/module.h>32#include <sound/core.h>33#include <sound/pcm.h>34#include <sound/pcm_params.h>35#include <sound/info.h>36#include <sound/initval.h>37#include <sound/control.h>38#include <media/v4l2-common.h>39#include "cx231xx.h"4041static int debug;42module_param(debug, int, 0644);43MODULE_PARM_DESC(debug, "activates debug info");4445#define dprintk(fmt, arg...) do { \46if (debug) \47printk(KERN_INFO "cx231xx-audio %s: " fmt, \48__func__, ##arg); \49} while (0)5051static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;5253static int cx231xx_isoc_audio_deinit(struct cx231xx *dev)54{55int i;5657dprintk("Stopping isoc\n");5859for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {60if (dev->adev.urb[i]) {61if (!irqs_disabled())62usb_kill_urb(dev->adev.urb[i]);63else64usb_unlink_urb(dev->adev.urb[i]);6566usb_free_urb(dev->adev.urb[i]);67dev->adev.urb[i] = NULL;6869kfree(dev->adev.transfer_buffer[i]);70dev->adev.transfer_buffer[i] = NULL;71}72}7374return 0;75}7677static int cx231xx_bulk_audio_deinit(struct cx231xx *dev)78{79int i;8081dprintk("Stopping bulk\n");8283for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {84if (dev->adev.urb[i]) {85if (!irqs_disabled())86usb_kill_urb(dev->adev.urb[i]);87else88usb_unlink_urb(dev->adev.urb[i]);8990usb_free_urb(dev->adev.urb[i]);91dev->adev.urb[i] = NULL;9293kfree(dev->adev.transfer_buffer[i]);94dev->adev.transfer_buffer[i] = NULL;95}96}9798return 0;99}100101static void cx231xx_audio_isocirq(struct urb *urb)102{103struct cx231xx *dev = urb->context;104int i;105unsigned int oldptr;106int period_elapsed = 0;107int status;108unsigned char *cp;109unsigned int stride;110struct snd_pcm_substream *substream;111struct snd_pcm_runtime *runtime;112113switch (urb->status) {114case 0: /* success */115case -ETIMEDOUT: /* NAK */116break;117case -ECONNRESET: /* kill */118case -ENOENT:119case -ESHUTDOWN:120return;121default: /* error */122dprintk("urb completition error %d.\n", urb->status);123break;124}125126if (atomic_read(&dev->stream_started) == 0)127return;128129if (dev->adev.capture_pcm_substream) {130substream = dev->adev.capture_pcm_substream;131runtime = substream->runtime;132stride = runtime->frame_bits >> 3;133134for (i = 0; i < urb->number_of_packets; i++) {135int length = urb->iso_frame_desc[i].actual_length /136stride;137cp = (unsigned char *)urb->transfer_buffer +138urb->iso_frame_desc[i].offset;139140if (!length)141continue;142143oldptr = dev->adev.hwptr_done_capture;144if (oldptr + length >= runtime->buffer_size) {145unsigned int cnt;146147cnt = runtime->buffer_size - oldptr;148memcpy(runtime->dma_area + oldptr * stride, cp,149cnt * stride);150memcpy(runtime->dma_area, cp + cnt * stride,151length * stride - cnt * stride);152} else {153memcpy(runtime->dma_area + oldptr * stride, cp,154length * stride);155}156157snd_pcm_stream_lock(substream);158159dev->adev.hwptr_done_capture += length;160if (dev->adev.hwptr_done_capture >=161runtime->buffer_size)162dev->adev.hwptr_done_capture -=163runtime->buffer_size;164165dev->adev.capture_transfer_done += length;166if (dev->adev.capture_transfer_done >=167runtime->period_size) {168dev->adev.capture_transfer_done -=169runtime->period_size;170period_elapsed = 1;171}172snd_pcm_stream_unlock(substream);173}174if (period_elapsed)175snd_pcm_period_elapsed(substream);176}177urb->status = 0;178179status = usb_submit_urb(urb, GFP_ATOMIC);180if (status < 0) {181cx231xx_errdev("resubmit of audio urb failed (error=%i)\n",182status);183}184return;185}186187static void cx231xx_audio_bulkirq(struct urb *urb)188{189struct cx231xx *dev = urb->context;190unsigned int oldptr;191int period_elapsed = 0;192int status;193unsigned char *cp;194unsigned int stride;195struct snd_pcm_substream *substream;196struct snd_pcm_runtime *runtime;197198switch (urb->status) {199case 0: /* success */200case -ETIMEDOUT: /* NAK */201break;202case -ECONNRESET: /* kill */203case -ENOENT:204case -ESHUTDOWN:205return;206default: /* error */207dprintk("urb completition error %d.\n", urb->status);208break;209}210211if (atomic_read(&dev->stream_started) == 0)212return;213214if (dev->adev.capture_pcm_substream) {215substream = dev->adev.capture_pcm_substream;216runtime = substream->runtime;217stride = runtime->frame_bits >> 3;218219if (1) {220int length = urb->actual_length /221stride;222cp = (unsigned char *)urb->transfer_buffer;223224oldptr = dev->adev.hwptr_done_capture;225if (oldptr + length >= runtime->buffer_size) {226unsigned int cnt;227228cnt = runtime->buffer_size - oldptr;229memcpy(runtime->dma_area + oldptr * stride, cp,230cnt * stride);231memcpy(runtime->dma_area, cp + cnt * stride,232length * stride - cnt * stride);233} else {234memcpy(runtime->dma_area + oldptr * stride, cp,235length * stride);236}237238snd_pcm_stream_lock(substream);239240dev->adev.hwptr_done_capture += length;241if (dev->adev.hwptr_done_capture >=242runtime->buffer_size)243dev->adev.hwptr_done_capture -=244runtime->buffer_size;245246dev->adev.capture_transfer_done += length;247if (dev->adev.capture_transfer_done >=248runtime->period_size) {249dev->adev.capture_transfer_done -=250runtime->period_size;251period_elapsed = 1;252}253snd_pcm_stream_unlock(substream);254}255if (period_elapsed)256snd_pcm_period_elapsed(substream);257}258urb->status = 0;259260status = usb_submit_urb(urb, GFP_ATOMIC);261if (status < 0) {262cx231xx_errdev("resubmit of audio urb failed (error=%i)\n",263status);264}265return;266}267268static int cx231xx_init_audio_isoc(struct cx231xx *dev)269{270int i, errCode;271int sb_size;272273cx231xx_info("%s: Starting ISO AUDIO transfers\n", __func__);274275sb_size = CX231XX_ISO_NUM_AUDIO_PACKETS * dev->adev.max_pkt_size;276277for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {278struct urb *urb;279int j, k;280281dev->adev.transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC);282if (!dev->adev.transfer_buffer[i])283return -ENOMEM;284285memset(dev->adev.transfer_buffer[i], 0x80, sb_size);286urb = usb_alloc_urb(CX231XX_ISO_NUM_AUDIO_PACKETS, GFP_ATOMIC);287if (!urb) {288cx231xx_errdev("usb_alloc_urb failed!\n");289for (j = 0; j < i; j++) {290usb_free_urb(dev->adev.urb[j]);291kfree(dev->adev.transfer_buffer[j]);292}293return -ENOMEM;294}295296urb->dev = dev->udev;297urb->context = dev;298urb->pipe = usb_rcvisocpipe(dev->udev,299dev->adev.end_point_addr);300urb->transfer_flags = URB_ISO_ASAP;301urb->transfer_buffer = dev->adev.transfer_buffer[i];302urb->interval = 1;303urb->complete = cx231xx_audio_isocirq;304urb->number_of_packets = CX231XX_ISO_NUM_AUDIO_PACKETS;305urb->transfer_buffer_length = sb_size;306307for (j = k = 0; j < CX231XX_ISO_NUM_AUDIO_PACKETS;308j++, k += dev->adev.max_pkt_size) {309urb->iso_frame_desc[j].offset = k;310urb->iso_frame_desc[j].length = dev->adev.max_pkt_size;311}312dev->adev.urb[i] = urb;313}314315for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {316errCode = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC);317if (errCode < 0) {318cx231xx_isoc_audio_deinit(dev);319return errCode;320}321}322323return errCode;324}325326static int cx231xx_init_audio_bulk(struct cx231xx *dev)327{328int i, errCode;329int sb_size;330331cx231xx_info("%s: Starting BULK AUDIO transfers\n", __func__);332333sb_size = CX231XX_NUM_AUDIO_PACKETS * dev->adev.max_pkt_size;334335for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {336struct urb *urb;337int j;338339dev->adev.transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC);340if (!dev->adev.transfer_buffer[i])341return -ENOMEM;342343memset(dev->adev.transfer_buffer[i], 0x80, sb_size);344urb = usb_alloc_urb(CX231XX_NUM_AUDIO_PACKETS, GFP_ATOMIC);345if (!urb) {346cx231xx_errdev("usb_alloc_urb failed!\n");347for (j = 0; j < i; j++) {348usb_free_urb(dev->adev.urb[j]);349kfree(dev->adev.transfer_buffer[j]);350}351return -ENOMEM;352}353354urb->dev = dev->udev;355urb->context = dev;356urb->pipe = usb_rcvbulkpipe(dev->udev,357dev->adev.end_point_addr);358urb->transfer_flags = 0;359urb->transfer_buffer = dev->adev.transfer_buffer[i];360urb->complete = cx231xx_audio_bulkirq;361urb->transfer_buffer_length = sb_size;362363dev->adev.urb[i] = urb;364365}366367for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {368errCode = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC);369if (errCode < 0) {370cx231xx_bulk_audio_deinit(dev);371return errCode;372}373}374375return errCode;376}377378static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs,379size_t size)380{381struct snd_pcm_runtime *runtime = subs->runtime;382383dprintk("Allocating vbuffer\n");384if (runtime->dma_area) {385if (runtime->dma_bytes > size)386return 0;387388vfree(runtime->dma_area);389}390runtime->dma_area = vmalloc(size);391if (!runtime->dma_area)392return -ENOMEM;393394runtime->dma_bytes = size;395396return 0;397}398399static struct snd_pcm_hardware snd_cx231xx_hw_capture = {400.info = SNDRV_PCM_INFO_BLOCK_TRANSFER |401SNDRV_PCM_INFO_MMAP |402SNDRV_PCM_INFO_INTERLEAVED |403SNDRV_PCM_INFO_MMAP_VALID,404405.formats = SNDRV_PCM_FMTBIT_S16_LE,406407.rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT,408409.rate_min = 48000,410.rate_max = 48000,411.channels_min = 2,412.channels_max = 2,413.buffer_bytes_max = 62720 * 8, /* just about the value in usbaudio.c */414.period_bytes_min = 64, /* 12544/2, */415.period_bytes_max = 12544,416.periods_min = 2,417.periods_max = 98, /* 12544, */418};419420static int snd_cx231xx_capture_open(struct snd_pcm_substream *substream)421{422struct cx231xx *dev = snd_pcm_substream_chip(substream);423struct snd_pcm_runtime *runtime = substream->runtime;424int ret = 0;425426dprintk("opening device and trying to acquire exclusive lock\n");427428if (!dev) {429cx231xx_errdev("BUG: cx231xx can't find device struct."430" Can't proceed with open\n");431return -ENODEV;432}433434/* Sets volume, mute, etc */435dev->mute = 0;436437/* set alternate setting for audio interface */438/* 1 - 48000 samples per sec */439mutex_lock(&dev->lock);440if (dev->USE_ISO)441ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 1);442else443ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 0);444mutex_unlock(&dev->lock);445if (ret < 0) {446cx231xx_errdev("failed to set alternate setting !\n");447448return ret;449}450451runtime->hw = snd_cx231xx_hw_capture;452453mutex_lock(&dev->lock);454/* inform hardware to start streaming */455ret = cx231xx_capture_start(dev, 1, Audio);456457dev->adev.users++;458mutex_unlock(&dev->lock);459460snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);461dev->adev.capture_pcm_substream = substream;462runtime->private_data = dev;463464return 0;465}466467static int snd_cx231xx_pcm_close(struct snd_pcm_substream *substream)468{469int ret;470struct cx231xx *dev = snd_pcm_substream_chip(substream);471472dprintk("closing device\n");473474/* inform hardware to stop streaming */475mutex_lock(&dev->lock);476ret = cx231xx_capture_start(dev, 0, Audio);477478/* set alternate setting for audio interface */479/* 1 - 48000 samples per sec */480ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 0);481if (ret < 0) {482cx231xx_errdev("failed to set alternate setting !\n");483484mutex_unlock(&dev->lock);485return ret;486}487488dev->mute = 1;489dev->adev.users--;490mutex_unlock(&dev->lock);491492if (dev->adev.users == 0 && dev->adev.shutdown == 1) {493dprintk("audio users: %d\n", dev->adev.users);494dprintk("disabling audio stream!\n");495dev->adev.shutdown = 0;496dprintk("released lock\n");497if (atomic_read(&dev->stream_started) > 0) {498atomic_set(&dev->stream_started, 0);499schedule_work(&dev->wq_trigger);500}501}502return 0;503}504505static int snd_cx231xx_hw_capture_params(struct snd_pcm_substream *substream,506struct snd_pcm_hw_params *hw_params)507{508unsigned int channels, rate, format;509int ret;510511dprintk("Setting capture parameters\n");512513ret = snd_pcm_alloc_vmalloc_buffer(substream,514params_buffer_bytes(hw_params));515format = params_format(hw_params);516rate = params_rate(hw_params);517channels = params_channels(hw_params);518519/* TODO: set up cx231xx audio chip to deliver the correct audio format,520current default is 48000hz multiplexed => 96000hz mono521which shouldn't matter since analogue TV only supports mono */522return 0;523}524525static int snd_cx231xx_hw_capture_free(struct snd_pcm_substream *substream)526{527struct cx231xx *dev = snd_pcm_substream_chip(substream);528529dprintk("Stop capture, if needed\n");530531if (atomic_read(&dev->stream_started) > 0) {532atomic_set(&dev->stream_started, 0);533schedule_work(&dev->wq_trigger);534}535536return 0;537}538539static int snd_cx231xx_prepare(struct snd_pcm_substream *substream)540{541struct cx231xx *dev = snd_pcm_substream_chip(substream);542543dev->adev.hwptr_done_capture = 0;544dev->adev.capture_transfer_done = 0;545546return 0;547}548549static void audio_trigger(struct work_struct *work)550{551struct cx231xx *dev = container_of(work, struct cx231xx, wq_trigger);552553if (atomic_read(&dev->stream_started)) {554dprintk("starting capture");555if (is_fw_load(dev) == 0)556cx25840_call(dev, core, load_fw);557if (dev->USE_ISO)558cx231xx_init_audio_isoc(dev);559else560cx231xx_init_audio_bulk(dev);561} else {562dprintk("stopping capture");563cx231xx_isoc_audio_deinit(dev);564}565}566567static int snd_cx231xx_capture_trigger(struct snd_pcm_substream *substream,568int cmd)569{570struct cx231xx *dev = snd_pcm_substream_chip(substream);571int retval;572573spin_lock(&dev->adev.slock);574switch (cmd) {575case SNDRV_PCM_TRIGGER_START:576atomic_set(&dev->stream_started, 1);577break;578case SNDRV_PCM_TRIGGER_STOP:579atomic_set(&dev->stream_started, 0);580break;581default:582retval = -EINVAL;583}584spin_unlock(&dev->adev.slock);585586schedule_work(&dev->wq_trigger);587588return 0;589}590591static snd_pcm_uframes_t snd_cx231xx_capture_pointer(struct snd_pcm_substream592*substream)593{594struct cx231xx *dev;595unsigned long flags;596snd_pcm_uframes_t hwptr_done;597598dev = snd_pcm_substream_chip(substream);599600spin_lock_irqsave(&dev->adev.slock, flags);601hwptr_done = dev->adev.hwptr_done_capture;602spin_unlock_irqrestore(&dev->adev.slock, flags);603604return hwptr_done;605}606607static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs,608unsigned long offset)609{610void *pageptr = subs->runtime->dma_area + offset;611612return vmalloc_to_page(pageptr);613}614615static struct snd_pcm_ops snd_cx231xx_pcm_capture = {616.open = snd_cx231xx_capture_open,617.close = snd_cx231xx_pcm_close,618.ioctl = snd_pcm_lib_ioctl,619.hw_params = snd_cx231xx_hw_capture_params,620.hw_free = snd_cx231xx_hw_capture_free,621.prepare = snd_cx231xx_prepare,622.trigger = snd_cx231xx_capture_trigger,623.pointer = snd_cx231xx_capture_pointer,624.page = snd_pcm_get_vmalloc_page,625};626627static int cx231xx_audio_init(struct cx231xx *dev)628{629struct cx231xx_audio *adev = &dev->adev;630struct snd_pcm *pcm;631struct snd_card *card;632static int devnr;633int err;634struct usb_interface *uif;635int i, isoc_pipe = 0;636637if (dev->has_alsa_audio != 1) {638/* This device does not support the extension (in this case639the device is expecting the snd-usb-audio module or640doesn't have analog audio support at all) */641return 0;642}643644cx231xx_info("cx231xx-audio.c: probing for cx231xx "645"non standard usbaudio\n");646647err = snd_card_create(index[devnr], "Cx231xx Audio", THIS_MODULE,6480, &card);649if (err < 0)650return err;651652spin_lock_init(&adev->slock);653err = snd_pcm_new(card, "Cx231xx Audio", 0, 0, 1, &pcm);654if (err < 0) {655snd_card_free(card);656return err;657}658659snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,660&snd_cx231xx_pcm_capture);661pcm->info_flags = 0;662pcm->private_data = dev;663strcpy(pcm->name, "Conexant cx231xx Capture");664snd_card_set_dev(card, &dev->udev->dev);665strcpy(card->driver, "Cx231xx-Audio");666strcpy(card->shortname, "Cx231xx Audio");667strcpy(card->longname, "Conexant cx231xx Audio");668669INIT_WORK(&dev->wq_trigger, audio_trigger);670671err = snd_card_register(card);672if (err < 0) {673snd_card_free(card);674return err;675}676adev->sndcard = card;677adev->udev = dev->udev;678679/* compute alternate max packet sizes for Audio */680uif =681dev->udev->actconfig->interface[dev->current_pcb_config.682hs_config_info[0].interface_info.683audio_index + 1];684685adev->end_point_addr =686le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe].desc.687bEndpointAddress);688689adev->num_alt = uif->num_altsetting;690cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n",691adev->end_point_addr, adev->num_alt);692adev->alt_max_pkt_size = kmalloc(32 * adev->num_alt, GFP_KERNEL);693694if (adev->alt_max_pkt_size == NULL) {695cx231xx_errdev("out of memory!\n");696return -ENOMEM;697}698699for (i = 0; i < adev->num_alt; i++) {700u16 tmp =701le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc.702wMaxPacketSize);703adev->alt_max_pkt_size[i] =704(tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);705cx231xx_info("Alternate setting %i, max size= %i\n", i,706adev->alt_max_pkt_size[i]);707}708709return 0;710}711712static int cx231xx_audio_fini(struct cx231xx *dev)713{714if (dev == NULL)715return 0;716717if (dev->has_alsa_audio != 1) {718/* This device does not support the extension (in this case719the device is expecting the snd-usb-audio module or720doesn't have analog audio support at all) */721return 0;722}723724if (dev->adev.sndcard) {725snd_card_free(dev->adev.sndcard);726kfree(dev->adev.alt_max_pkt_size);727dev->adev.sndcard = NULL;728}729730return 0;731}732733static struct cx231xx_ops audio_ops = {734.id = CX231XX_AUDIO,735.name = "Cx231xx Audio Extension",736.init = cx231xx_audio_init,737.fini = cx231xx_audio_fini,738};739740static int __init cx231xx_alsa_register(void)741{742return cx231xx_register_extension(&audio_ops);743}744745static void __exit cx231xx_alsa_unregister(void)746{747cx231xx_unregister_extension(&audio_ops);748}749750MODULE_LICENSE("GPL");751MODULE_AUTHOR("Srinivasa Deevi <[email protected]>");752MODULE_DESCRIPTION("Cx231xx Audio driver");753754module_init(cx231xx_alsa_register);755module_exit(cx231xx_alsa_unregister);756757758