Path: blob/master/drivers/media/video/em28xx/em28xx-audio.c
17887 views
/*1* Empiatech em28x1 audio extension2*3* Copyright (C) 2006 Markus Rechberger <[email protected]>4*5* Copyright (C) 2007 Mauro Carvalho Chehab <[email protected]>6* - Port to work with the in-kernel driver7* - Several cleanups8*9* This driver is based on my previous au600 usb pstn audio driver10* and inherits all the copyrights11*12* This program is free software; you can redistribute it and/or modify13* it under the terms of the GNU General Public License as published by14* the Free Software Foundation; either version 2 of the License, or15* (at your option) any later version.16*17* This program is distributed in the hope that it will be useful,18* but WITHOUT ANY WARRANTY; without even the implied warranty of19* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the20* GNU General Public License for more details.21*22* You should have received a copy of the GNU General Public License23* along with this program; if not, write to the Free Software24* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.25*/2627#include <linux/kernel.h>28#include <linux/usb.h>29#include <linux/init.h>30#include <linux/sound.h>31#include <linux/spinlock.h>32#include <linux/soundcard.h>33#include <linux/slab.h>34#include <linux/vmalloc.h>35#include <linux/proc_fs.h>36#include <linux/module.h>37#include <sound/core.h>38#include <sound/pcm.h>39#include <sound/pcm_params.h>40#include <sound/info.h>41#include <sound/initval.h>42#include <sound/control.h>43#include <media/v4l2-common.h>44#include "em28xx.h"4546static int debug;47module_param(debug, int, 0644);48MODULE_PARM_DESC(debug, "activates debug info");4950#define dprintk(fmt, arg...) do { \51if (debug) \52printk(KERN_INFO "em28xx-audio %s: " fmt, \53__func__, ##arg); \54} while (0)5556static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;5758static int em28xx_deinit_isoc_audio(struct em28xx *dev)59{60int i;6162dprintk("Stopping isoc\n");63for (i = 0; i < EM28XX_AUDIO_BUFS; i++) {64if (!irqs_disabled())65usb_kill_urb(dev->adev.urb[i]);66else67usb_unlink_urb(dev->adev.urb[i]);6869usb_free_urb(dev->adev.urb[i]);70dev->adev.urb[i] = NULL;7172kfree(dev->adev.transfer_buffer[i]);73dev->adev.transfer_buffer[i] = NULL;74}7576return 0;77}7879static void em28xx_audio_isocirq(struct urb *urb)80{81struct em28xx *dev = urb->context;82int i;83unsigned int oldptr;84int period_elapsed = 0;85int status;86unsigned char *cp;87unsigned int stride;88struct snd_pcm_substream *substream;89struct snd_pcm_runtime *runtime;9091switch (urb->status) {92case 0: /* success */93case -ETIMEDOUT: /* NAK */94break;95case -ECONNRESET: /* kill */96case -ENOENT:97case -ESHUTDOWN:98return;99default: /* error */100dprintk("urb completition error %d.\n", urb->status);101break;102}103104if (atomic_read(&dev->stream_started) == 0)105return;106107if (dev->adev.capture_pcm_substream) {108substream = dev->adev.capture_pcm_substream;109runtime = substream->runtime;110stride = runtime->frame_bits >> 3;111112for (i = 0; i < urb->number_of_packets; i++) {113int length =114urb->iso_frame_desc[i].actual_length / stride;115cp = (unsigned char *)urb->transfer_buffer +116urb->iso_frame_desc[i].offset;117118if (!length)119continue;120121oldptr = dev->adev.hwptr_done_capture;122if (oldptr + length >= runtime->buffer_size) {123unsigned int cnt =124runtime->buffer_size - oldptr;125memcpy(runtime->dma_area + oldptr * stride, cp,126cnt * stride);127memcpy(runtime->dma_area, cp + cnt * stride,128length * stride - cnt * stride);129} else {130memcpy(runtime->dma_area + oldptr * stride, cp,131length * stride);132}133134snd_pcm_stream_lock(substream);135136dev->adev.hwptr_done_capture += length;137if (dev->adev.hwptr_done_capture >=138runtime->buffer_size)139dev->adev.hwptr_done_capture -=140runtime->buffer_size;141142dev->adev.capture_transfer_done += length;143if (dev->adev.capture_transfer_done >=144runtime->period_size) {145dev->adev.capture_transfer_done -=146runtime->period_size;147period_elapsed = 1;148}149150snd_pcm_stream_unlock(substream);151}152if (period_elapsed)153snd_pcm_period_elapsed(substream);154}155urb->status = 0;156157status = usb_submit_urb(urb, GFP_ATOMIC);158if (status < 0) {159em28xx_errdev("resubmit of audio urb failed (error=%i)\n",160status);161}162return;163}164165static int em28xx_init_audio_isoc(struct em28xx *dev)166{167int i, errCode;168const int sb_size = EM28XX_NUM_AUDIO_PACKETS *169EM28XX_AUDIO_MAX_PACKET_SIZE;170171dprintk("Starting isoc transfers\n");172173for (i = 0; i < EM28XX_AUDIO_BUFS; i++) {174struct urb *urb;175int j, k;176177dev->adev.transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC);178if (!dev->adev.transfer_buffer[i])179return -ENOMEM;180181memset(dev->adev.transfer_buffer[i], 0x80, sb_size);182urb = usb_alloc_urb(EM28XX_NUM_AUDIO_PACKETS, GFP_ATOMIC);183if (!urb) {184em28xx_errdev("usb_alloc_urb failed!\n");185for (j = 0; j < i; j++) {186usb_free_urb(dev->adev.urb[j]);187kfree(dev->adev.transfer_buffer[j]);188}189return -ENOMEM;190}191192urb->dev = dev->udev;193urb->context = dev;194urb->pipe = usb_rcvisocpipe(dev->udev, 0x83);195urb->transfer_flags = URB_ISO_ASAP;196urb->transfer_buffer = dev->adev.transfer_buffer[i];197urb->interval = 1;198urb->complete = em28xx_audio_isocirq;199urb->number_of_packets = EM28XX_NUM_AUDIO_PACKETS;200urb->transfer_buffer_length = sb_size;201202for (j = k = 0; j < EM28XX_NUM_AUDIO_PACKETS;203j++, k += EM28XX_AUDIO_MAX_PACKET_SIZE) {204urb->iso_frame_desc[j].offset = k;205urb->iso_frame_desc[j].length =206EM28XX_AUDIO_MAX_PACKET_SIZE;207}208dev->adev.urb[i] = urb;209}210211for (i = 0; i < EM28XX_AUDIO_BUFS; i++) {212errCode = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC);213if (errCode) {214em28xx_deinit_isoc_audio(dev);215return errCode;216}217}218219return 0;220}221222static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs,223size_t size)224{225struct snd_pcm_runtime *runtime = subs->runtime;226227dprintk("Allocating vbuffer\n");228if (runtime->dma_area) {229if (runtime->dma_bytes > size)230return 0;231232vfree(runtime->dma_area);233}234runtime->dma_area = vmalloc(size);235if (!runtime->dma_area)236return -ENOMEM;237238runtime->dma_bytes = size;239240return 0;241}242243static struct snd_pcm_hardware snd_em28xx_hw_capture = {244.info = SNDRV_PCM_INFO_BLOCK_TRANSFER |245SNDRV_PCM_INFO_MMAP |246SNDRV_PCM_INFO_INTERLEAVED |247SNDRV_PCM_INFO_MMAP_VALID,248249.formats = SNDRV_PCM_FMTBIT_S16_LE,250251.rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT,252253.rate_min = 48000,254.rate_max = 48000,255.channels_min = 2,256.channels_max = 2,257.buffer_bytes_max = 62720 * 8, /* just about the value in usbaudio.c */258.period_bytes_min = 64, /* 12544/2, */259.period_bytes_max = 12544,260.periods_min = 2,261.periods_max = 98, /* 12544, */262};263264static int snd_em28xx_capture_open(struct snd_pcm_substream *substream)265{266struct em28xx *dev = snd_pcm_substream_chip(substream);267struct snd_pcm_runtime *runtime = substream->runtime;268int ret = 0;269270dprintk("opening device and trying to acquire exclusive lock\n");271272if (!dev) {273em28xx_err("BUG: em28xx can't find device struct."274" Can't proceed with open\n");275return -ENODEV;276}277278/* Sets volume, mute, etc */279280dev->mute = 0;281mutex_lock(&dev->lock);282ret = em28xx_audio_analog_set(dev);283if (ret < 0)284goto err;285286runtime->hw = snd_em28xx_hw_capture;287if (dev->alt == 0 && dev->adev.users == 0) {288int errCode;289dev->alt = 7;290dprintk("changing alternate number to 7\n");291errCode = usb_set_interface(dev->udev, 0, 7);292}293294dev->adev.users++;295mutex_unlock(&dev->lock);296297snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);298dev->adev.capture_pcm_substream = substream;299runtime->private_data = dev;300301return 0;302err:303mutex_unlock(&dev->lock);304305em28xx_err("Error while configuring em28xx mixer\n");306return ret;307}308309static int snd_em28xx_pcm_close(struct snd_pcm_substream *substream)310{311struct em28xx *dev = snd_pcm_substream_chip(substream);312313dprintk("closing device\n");314315dev->mute = 1;316mutex_lock(&dev->lock);317dev->adev.users--;318if (atomic_read(&dev->stream_started) > 0) {319atomic_set(&dev->stream_started, 0);320schedule_work(&dev->wq_trigger);321}322323em28xx_audio_analog_set(dev);324if (substream->runtime->dma_area) {325dprintk("freeing\n");326vfree(substream->runtime->dma_area);327substream->runtime->dma_area = NULL;328}329mutex_unlock(&dev->lock);330331return 0;332}333334static int snd_em28xx_hw_capture_params(struct snd_pcm_substream *substream,335struct snd_pcm_hw_params *hw_params)336{337unsigned int channels, rate, format;338int ret;339340dprintk("Setting capture parameters\n");341342ret = snd_pcm_alloc_vmalloc_buffer(substream,343params_buffer_bytes(hw_params));344format = params_format(hw_params);345rate = params_rate(hw_params);346channels = params_channels(hw_params);347348/* TODO: set up em28xx audio chip to deliver the correct audio format,349current default is 48000hz multiplexed => 96000hz mono350which shouldn't matter since analogue TV only supports mono */351return 0;352}353354static int snd_em28xx_hw_capture_free(struct snd_pcm_substream *substream)355{356struct em28xx *dev = snd_pcm_substream_chip(substream);357358dprintk("Stop capture, if needed\n");359360if (atomic_read(&dev->stream_started) > 0) {361atomic_set(&dev->stream_started, 0);362schedule_work(&dev->wq_trigger);363}364365return 0;366}367368static int snd_em28xx_prepare(struct snd_pcm_substream *substream)369{370struct em28xx *dev = snd_pcm_substream_chip(substream);371372dev->adev.hwptr_done_capture = 0;373dev->adev.capture_transfer_done = 0;374375return 0;376}377378static void audio_trigger(struct work_struct *work)379{380struct em28xx *dev = container_of(work, struct em28xx, wq_trigger);381382if (atomic_read(&dev->stream_started)) {383dprintk("starting capture");384em28xx_init_audio_isoc(dev);385} else {386dprintk("stopping capture");387em28xx_deinit_isoc_audio(dev);388}389}390391static int snd_em28xx_capture_trigger(struct snd_pcm_substream *substream,392int cmd)393{394struct em28xx *dev = snd_pcm_substream_chip(substream);395int retval;396397switch (cmd) {398case SNDRV_PCM_TRIGGER_START:399atomic_set(&dev->stream_started, 1);400break;401case SNDRV_PCM_TRIGGER_STOP:402atomic_set(&dev->stream_started, 1);403break;404default:405retval = -EINVAL;406}407schedule_work(&dev->wq_trigger);408return 0;409}410411static snd_pcm_uframes_t snd_em28xx_capture_pointer(struct snd_pcm_substream412*substream)413{414unsigned long flags;415struct em28xx *dev;416snd_pcm_uframes_t hwptr_done;417418dev = snd_pcm_substream_chip(substream);419spin_lock_irqsave(&dev->adev.slock, flags);420hwptr_done = dev->adev.hwptr_done_capture;421spin_unlock_irqrestore(&dev->adev.slock, flags);422423return hwptr_done;424}425426static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs,427unsigned long offset)428{429void *pageptr = subs->runtime->dma_area + offset;430431return vmalloc_to_page(pageptr);432}433434static struct snd_pcm_ops snd_em28xx_pcm_capture = {435.open = snd_em28xx_capture_open,436.close = snd_em28xx_pcm_close,437.ioctl = snd_pcm_lib_ioctl,438.hw_params = snd_em28xx_hw_capture_params,439.hw_free = snd_em28xx_hw_capture_free,440.prepare = snd_em28xx_prepare,441.trigger = snd_em28xx_capture_trigger,442.pointer = snd_em28xx_capture_pointer,443.page = snd_pcm_get_vmalloc_page,444};445446static int em28xx_audio_init(struct em28xx *dev)447{448struct em28xx_audio *adev = &dev->adev;449struct snd_pcm *pcm;450struct snd_card *card;451static int devnr;452int err;453454if (dev->has_alsa_audio != 1) {455/* This device does not support the extension (in this case456the device is expecting the snd-usb-audio module or457doesn't have analog audio support at all) */458return 0;459}460461printk(KERN_INFO "em28xx-audio.c: probing for em28x1 "462"non standard usbaudio\n");463printk(KERN_INFO "em28xx-audio.c: Copyright (C) 2006 Markus "464"Rechberger\n");465466err = snd_card_create(index[devnr], "Em28xx Audio", THIS_MODULE, 0,467&card);468if (err < 0)469return err;470471spin_lock_init(&adev->slock);472err = snd_pcm_new(card, "Em28xx Audio", 0, 0, 1, &pcm);473if (err < 0) {474snd_card_free(card);475return err;476}477478snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_em28xx_pcm_capture);479pcm->info_flags = 0;480pcm->private_data = dev;481strcpy(pcm->name, "Empia 28xx Capture");482483snd_card_set_dev(card, &dev->udev->dev);484strcpy(card->driver, "Em28xx-Audio");485strcpy(card->shortname, "Em28xx Audio");486strcpy(card->longname, "Empia Em28xx Audio");487488INIT_WORK(&dev->wq_trigger, audio_trigger);489490err = snd_card_register(card);491if (err < 0) {492snd_card_free(card);493return err;494}495adev->sndcard = card;496adev->udev = dev->udev;497498return 0;499}500501static int em28xx_audio_fini(struct em28xx *dev)502{503if (dev == NULL)504return 0;505506if (dev->has_alsa_audio != 1) {507/* This device does not support the extension (in this case508the device is expecting the snd-usb-audio module or509doesn't have analog audio support at all) */510return 0;511}512513if (dev->adev.sndcard) {514snd_card_free(dev->adev.sndcard);515dev->adev.sndcard = NULL;516}517518return 0;519}520521static struct em28xx_ops audio_ops = {522.id = EM28XX_AUDIO,523.name = "Em28xx Audio Extension",524.init = em28xx_audio_init,525.fini = em28xx_audio_fini,526};527528static int __init em28xx_alsa_register(void)529{530return em28xx_register_extension(&audio_ops);531}532533static void __exit em28xx_alsa_unregister(void)534{535em28xx_unregister_extension(&audio_ops);536}537538MODULE_LICENSE("GPL");539MODULE_AUTHOR("Markus Rechberger <[email protected]>");540MODULE_AUTHOR("Mauro Carvalho Chehab <[email protected]>");541MODULE_DESCRIPTION("Em28xx Audio driver");542543module_init(em28xx_alsa_register);544module_exit(em28xx_alsa_unregister);545546547