2008-01-05 09:55:47 -03:00
/*
* Empiatech em28x1 audio extension
*
* Copyright ( C ) 2006 Markus Rechberger < mrechberger @ gmail . com >
*
2008-01-05 09:57:31 -03:00
* Copyright ( C ) 2007 Mauro Carvalho Chehab < mchehab @ infradead . org >
* - Port to work with the in - kernel driver
* - Several cleanups
*
2008-01-05 09:55:47 -03:00
* This driver is based on my previous au600 usb pstn audio driver
* and inherits all the copyrights
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include <linux/kernel.h>
# include <linux/usb.h>
# include <linux/init.h>
# include <linux/sound.h>
# include <linux/spinlock.h>
# include <linux/soundcard.h>
# include <linux/slab.h>
# include <linux/vmalloc.h>
# include <linux/proc_fs.h>
2008-01-05 09:57:31 -03:00
# include <linux/module.h>
2008-01-05 09:55:47 -03:00
# include <sound/core.h>
# include <sound/pcm.h>
# include <sound/pcm_params.h>
# include <sound/info.h>
# include <sound/initval.h>
# include <sound/control.h>
# include <media/v4l2-common.h>
# include "em28xx.h"
2008-01-05 09:57:31 -03:00
static int debug ;
module_param ( debug , int , 0644 ) ;
MODULE_PARM_DESC ( debug , " activates debug info " ) ;
2008-01-05 09:55:47 -03:00
2008-01-05 09:57:31 -03:00
# define dprintk(fmt, arg...) do { \
if ( debug ) \
printk ( KERN_INFO " em28xx-audio %s: " fmt , \
2008-04-08 23:20:00 -03:00
__func__ , # # arg ) ; \
2008-01-05 09:57:31 -03:00
} while ( 0 )
2008-01-05 09:55:47 -03:00
2008-01-05 09:57:31 -03:00
static int index [ SNDRV_CARDS ] = SNDRV_DEFAULT_IDX ;
2008-01-05 09:55:47 -03:00
2008-01-05 09:57:31 -03:00
static int em28xx_isoc_audio_deinit ( struct em28xx * dev )
2008-01-05 09:55:47 -03:00
{
2008-01-05 09:57:31 -03:00
int i ;
2008-01-05 09:55:47 -03:00
2008-01-05 09:57:31 -03:00
dprintk ( " Stopping isoc \n " ) ;
for ( i = 0 ; i < EM28XX_AUDIO_BUFS ; i + + ) {
usb_kill_urb ( dev - > adev - > urb [ i ] ) ;
usb_free_urb ( dev - > adev - > urb [ i ] ) ;
dev - > adev - > urb [ i ] = NULL ;
2008-01-05 09:55:47 -03:00
}
return 0 ;
}
static void em28xx_audio_isocirq ( struct urb * urb )
{
2008-01-05 09:57:31 -03:00
struct em28xx * dev = urb - > context ;
int i ;
unsigned int oldptr ;
unsigned long flags ;
int period_elapsed = 0 ;
int status ;
unsigned char * cp ;
unsigned int stride ;
2008-01-05 09:55:47 -03:00
struct snd_pcm_substream * substream ;
2008-01-05 09:57:31 -03:00
struct snd_pcm_runtime * runtime ;
2008-01-05 09:56:24 -03:00
if ( dev - > adev - > capture_pcm_substream ) {
substream = dev - > adev - > capture_pcm_substream ;
runtime = substream - > runtime ;
2008-01-05 09:55:47 -03:00
stride = runtime - > frame_bits > > 3 ;
2008-01-05 09:57:31 -03:00
2008-01-05 09:56:24 -03:00
for ( i = 0 ; i < urb - > number_of_packets ; i + + ) {
int length =
urb - > iso_frame_desc [ i ] . actual_length / stride ;
cp = ( unsigned char * ) urb - > transfer_buffer +
urb - > iso_frame_desc [ i ] . offset ;
2008-01-05 09:55:47 -03:00
2008-01-05 09:56:24 -03:00
if ( ! length )
2008-01-05 09:55:47 -03:00
continue ;
spin_lock_irqsave ( & dev - > adev - > slock , flags ) ;
2008-01-05 09:57:31 -03:00
2008-01-05 09:55:47 -03:00
oldptr = dev - > adev - > hwptr_done_capture ;
2008-01-05 09:56:24 -03:00
dev - > adev - > hwptr_done_capture + = length ;
if ( dev - > adev - > hwptr_done_capture > =
runtime - > buffer_size )
dev - > adev - > hwptr_done_capture - =
runtime - > buffer_size ;
2008-01-05 09:55:47 -03:00
dev - > adev - > capture_transfer_done + = length ;
2008-01-05 09:56:24 -03:00
if ( dev - > adev - > capture_transfer_done > =
runtime - > period_size ) {
dev - > adev - > capture_transfer_done - =
runtime - > period_size ;
period_elapsed = 1 ;
2008-01-05 09:55:47 -03:00
}
2008-01-05 09:57:31 -03:00
2008-01-05 09:55:47 -03:00
spin_unlock_irqrestore ( & dev - > adev - > slock , flags ) ;
2008-01-05 09:56:24 -03:00
if ( oldptr + length > = runtime - > buffer_size ) {
unsigned int cnt =
runtime - > buffer_size - oldptr - 1 ;
memcpy ( runtime - > dma_area + oldptr * stride , cp ,
cnt * stride ) ;
memcpy ( runtime - > dma_area , cp + cnt ,
length * stride - cnt * stride ) ;
2008-01-05 09:55:47 -03:00
} else {
2008-01-05 09:56:24 -03:00
memcpy ( runtime - > dma_area + oldptr * stride , cp ,
length * stride ) ;
2008-01-05 09:55:47 -03:00
}
}
2008-01-05 09:57:31 -03:00
if ( period_elapsed )
2008-01-05 09:55:47 -03:00
snd_pcm_period_elapsed ( substream ) ;
}
urb - > status = 0 ;
2008-01-05 09:56:24 -03:00
if ( dev - > adev - > shutdown )
2008-01-05 09:55:47 -03:00
return ;
2008-01-05 09:57:31 -03:00
status = usb_submit_urb ( urb , GFP_ATOMIC ) ;
if ( status < 0 ) {
2008-01-05 09:56:24 -03:00
em28xx_errdev ( " resubmit of audio urb failed (error=%i) \n " ,
status ) ;
2008-01-05 09:55:47 -03:00
}
return ;
}
static int em28xx_init_audio_isoc ( struct em28xx * dev )
{
2008-01-05 09:57:31 -03:00
int i , errCode ;
const int sb_size = EM28XX_NUM_AUDIO_PACKETS *
EM28XX_AUDIO_MAX_PACKET_SIZE ;
dprintk ( " Starting isoc transfers \n " ) ;
2008-01-05 09:55:47 -03:00
2008-01-05 09:56:24 -03:00
for ( i = 0 ; i < EM28XX_AUDIO_BUFS ; i + + ) {
2008-01-05 09:55:47 -03:00
struct urb * urb ;
2008-01-05 09:56:24 -03:00
int j , k ;
2008-01-05 09:57:31 -03:00
2008-01-05 09:56:24 -03:00
dev - > adev - > transfer_buffer [ i ] = kmalloc ( sb_size , GFP_ATOMIC ) ;
2008-01-05 09:57:31 -03:00
if ( ! dev - > adev - > transfer_buffer [ i ] )
2008-01-05 09:55:47 -03:00
return - ENOMEM ;
2008-01-05 09:57:31 -03:00
2008-01-05 09:56:24 -03:00
memset ( dev - > adev - > transfer_buffer [ i ] , 0x80 , sb_size ) ;
urb = usb_alloc_urb ( EM28XX_NUM_AUDIO_PACKETS , GFP_ATOMIC ) ;
2008-01-05 09:57:31 -03:00
if ( ! urb )
2008-01-05 09:55:47 -03:00
return - ENOMEM ;
2008-01-05 09:57:31 -03:00
urb - > dev = dev - > udev ;
urb - > context = dev ;
urb - > pipe = usb_rcvisocpipe ( dev - > udev , 0x83 ) ;
urb - > transfer_flags = URB_ISO_ASAP ;
urb - > transfer_buffer = dev - > adev - > transfer_buffer [ i ] ;
urb - > interval = 1 ;
urb - > complete = em28xx_audio_isocirq ;
urb - > number_of_packets = EM28XX_NUM_AUDIO_PACKETS ;
urb - > transfer_buffer_length = sb_size ;
for ( j = k = 0 ; j < EM28XX_NUM_AUDIO_PACKETS ;
j + + , k + = EM28XX_AUDIO_MAX_PACKET_SIZE ) {
urb - > iso_frame_desc [ j ] . offset = k ;
urb - > iso_frame_desc [ j ] . length =
EM28XX_AUDIO_MAX_PACKET_SIZE ;
2008-01-05 09:55:47 -03:00
}
2008-01-05 09:57:31 -03:00
dev - > adev - > urb [ i ] = urb ;
2008-01-05 09:55:47 -03:00
}
2008-01-05 09:57:31 -03:00
2008-01-05 09:56:24 -03:00
for ( i = 0 ; i < EM28XX_AUDIO_BUFS ; i + + ) {
2008-01-05 09:55:47 -03:00
errCode = usb_submit_urb ( dev - > adev - > urb [ i ] , GFP_ATOMIC ) ;
2008-01-05 09:56:24 -03:00
if ( errCode ) {
2008-01-05 09:55:47 -03:00
em28xx_isoc_audio_deinit ( dev ) ;
2008-01-05 09:57:31 -03:00
2008-01-05 09:55:47 -03:00
return errCode ;
}
}
2008-01-05 09:57:31 -03:00
2008-01-05 09:55:47 -03:00
return 0 ;
}
2008-01-05 09:56:24 -03:00
static int em28xx_cmd ( struct em28xx * dev , int cmd , int arg )
2008-01-05 09:55:47 -03:00
{
2008-01-05 09:57:31 -03:00
dprintk ( " %s transfer \n " , ( dev - > adev - > capture_stream = = STREAM_ON ) ?
" stop " : " start " ) ;
2008-01-05 09:56:24 -03:00
switch ( cmd ) {
case EM28XX_CAPTURE_STREAM_EN :
if ( dev - > adev - > capture_stream = = STREAM_OFF & & arg = = 1 ) {
dev - > adev - > capture_stream = STREAM_ON ;
em28xx_init_audio_isoc ( dev ) ;
} else if ( dev - > adev - > capture_stream = = STREAM_ON & & arg = = 0 ) {
dev - > adev - > capture_stream = STREAM_OFF ;
em28xx_isoc_audio_deinit ( dev ) ;
} else {
2008-01-05 09:57:31 -03:00
printk ( KERN_ERR " An underrun very likely occurred. "
" Ignoring it. \n " ) ;
2008-01-05 09:56:24 -03:00
}
return 0 ;
default :
return - EINVAL ;
2008-01-05 09:55:47 -03:00
}
}
2008-01-05 09:57:31 -03:00
static int snd_pcm_alloc_vmalloc_buffer ( struct snd_pcm_substream * subs ,
size_t size )
{
struct snd_pcm_runtime * runtime = subs - > runtime ;
dprintk ( " Alocating vbuffer \n " ) ;
if ( runtime - > dma_area ) {
if ( runtime - > dma_bytes > size )
return 0 ;
vfree ( runtime - > dma_area ) ;
}
runtime - > dma_area = vmalloc ( size ) ;
if ( ! runtime - > dma_area )
return - ENOMEM ;
runtime - > dma_bytes = size ;
return 0 ;
}
static struct snd_pcm_hardware snd_em28xx_hw_capture = {
. info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID ,
. formats = SNDRV_PCM_FMTBIT_S16_LE ,
. rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT ,
. rate_min = 48000 ,
. rate_max = 48000 ,
. channels_min = 2 ,
. channels_max = 2 ,
. buffer_bytes_max = 62720 * 8 , /* just about the value in usbaudio.c */
. period_bytes_min = 64 , /* 12544/2, */
. period_bytes_max = 12544 ,
. periods_min = 2 ,
. periods_max = 98 , /* 12544, */
} ;
static int snd_em28xx_capture_open ( struct snd_pcm_substream * substream )
{
struct em28xx * dev = snd_pcm_substream_chip ( substream ) ;
struct snd_pcm_runtime * runtime = substream - > runtime ;
int ret = 0 ;
dprintk ( " opening device and trying to acquire exclusive lock \n " ) ;
/* Sets volume, mute, etc */
2008-02-06 18:52:15 -03:00
2008-01-05 09:57:31 -03:00
dev - > mute = 0 ;
2008-02-06 18:52:15 -03:00
mutex_lock ( & dev - > lock ) ;
2008-01-05 09:57:31 -03:00
ret = em28xx_audio_analog_set ( dev ) ;
2008-02-06 18:52:15 -03:00
mutex_unlock ( & dev - > lock ) ;
2008-01-05 09:57:31 -03:00
if ( ret < 0 )
goto err ;
runtime - > hw = snd_em28xx_hw_capture ;
if ( dev - > alt = = 0 & & dev - > adev - > users = = 0 ) {
int errCode ;
dev - > alt = 7 ;
errCode = usb_set_interface ( dev - > udev , 0 , 7 ) ;
dprintk ( " changing alternate number to 7 \n " ) ;
}
dev - > adev - > users + + ;
snd_pcm_hw_constraint_integer ( runtime , SNDRV_PCM_HW_PARAM_PERIODS ) ;
dev - > adev - > capture_pcm_substream = substream ;
runtime - > private_data = dev ;
return 0 ;
err :
printk ( KERN_ERR " Error while configuring em28xx mixer \n " ) ;
return ret ;
}
static int snd_em28xx_pcm_close ( struct snd_pcm_substream * substream )
{
struct em28xx * dev = snd_pcm_substream_chip ( substream ) ;
dev - > adev - > users - - ;
dprintk ( " closing device \n " ) ;
dev - > mute = 1 ;
2008-02-06 18:52:15 -03:00
mutex_lock ( & dev - > lock ) ;
2008-01-05 09:57:31 -03:00
em28xx_audio_analog_set ( dev ) ;
2008-02-06 18:52:15 -03:00
mutex_unlock ( & dev - > lock ) ;
2008-01-05 09:57:31 -03:00
if ( dev - > adev - > users = = 0 & & dev - > adev - > shutdown = = 1 ) {
dprintk ( " audio users: %d \n " , dev - > adev - > users ) ;
dprintk ( " disabling audio stream! \n " ) ;
dev - > adev - > shutdown = 0 ;
dprintk ( " released lock \n " ) ;
em28xx_cmd ( dev , EM28XX_CAPTURE_STREAM_EN , 0 ) ;
}
return 0 ;
}
static int snd_em28xx_hw_capture_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * hw_params )
{
unsigned int channels , rate , format ;
int ret ;
dprintk ( " Setting capture parameters \n " ) ;
ret = snd_pcm_alloc_vmalloc_buffer ( substream ,
params_buffer_bytes ( hw_params ) ) ;
format = params_format ( hw_params ) ;
rate = params_rate ( hw_params ) ;
channels = params_channels ( hw_params ) ;
/* TODO: set up em28xx audio chip to deliver the correct audio format,
current default is 48000 hz multiplexed = > 96000 hz mono
which shouldn ' t matter since analogue TV only supports mono */
return 0 ;
}
static int snd_em28xx_hw_capture_free ( struct snd_pcm_substream * substream )
{
struct em28xx * dev = snd_pcm_substream_chip ( substream ) ;
dprintk ( " Stop capture, if needed \n " ) ;
if ( dev - > adev - > capture_stream = = STREAM_ON )
em28xx_cmd ( dev , EM28XX_CAPTURE_STREAM_EN , 0 ) ;
return 0 ;
}
static int snd_em28xx_prepare ( struct snd_pcm_substream * substream )
{
return 0 ;
}
static int snd_em28xx_capture_trigger ( struct snd_pcm_substream * substream ,
int cmd )
{
struct em28xx * dev = snd_pcm_substream_chip ( substream ) ;
dprintk ( " Should %s capture \n " , ( cmd = = SNDRV_PCM_TRIGGER_START ) ?
" start " : " stop " ) ;
switch ( cmd ) {
case SNDRV_PCM_TRIGGER_START :
em28xx_cmd ( dev , EM28XX_CAPTURE_STREAM_EN , 1 ) ;
return 0 ;
case SNDRV_PCM_TRIGGER_STOP :
dev - > adev - > shutdown = 1 ;
return 0 ;
default :
return - EINVAL ;
}
}
2008-01-05 09:56:24 -03:00
static snd_pcm_uframes_t snd_em28xx_capture_pointer ( struct snd_pcm_substream
* substream )
2008-01-05 09:55:47 -03:00
{
struct em28xx * dev ;
2008-01-05 09:57:31 -03:00
2008-01-05 09:55:47 -03:00
snd_pcm_uframes_t hwptr_done ;
dev = snd_pcm_substream_chip ( substream ) ;
hwptr_done = dev - > adev - > hwptr_done_capture ;
2008-01-05 09:57:31 -03:00
2008-01-05 09:55:47 -03:00
return hwptr_done ;
}
static struct page * snd_pcm_get_vmalloc_page ( struct snd_pcm_substream * subs ,
unsigned long offset )
{
void * pageptr = subs - > runtime - > dma_area + offset ;
2008-01-05 09:57:31 -03:00
2008-01-05 09:55:47 -03:00
return vmalloc_to_page ( pageptr ) ;
}
static struct snd_pcm_ops snd_em28xx_pcm_capture = {
2008-01-05 09:57:31 -03:00
. open = snd_em28xx_capture_open ,
. close = snd_em28xx_pcm_close ,
. ioctl = snd_pcm_lib_ioctl ,
2008-01-05 09:55:47 -03:00
. hw_params = snd_em28xx_hw_capture_params ,
2008-01-05 09:57:31 -03:00
. hw_free = snd_em28xx_hw_capture_free ,
. prepare = snd_em28xx_prepare ,
. trigger = snd_em28xx_capture_trigger ,
. pointer = snd_em28xx_capture_pointer ,
. page = snd_pcm_get_vmalloc_page ,
2008-01-05 09:55:47 -03:00
} ;
static int em28xx_audio_init ( struct em28xx * dev )
{
struct em28xx_audio * adev ;
2008-01-05 09:57:31 -03:00
struct snd_pcm * pcm ;
struct snd_card * card ;
static int devnr ;
int ret , err ;
2008-06-10 12:34:35 -03:00
if ( dev - > has_audio_class ) {
/* This device does not support the extension (in this case
the device is expecting the snd - usb - audio module */
return 0 ;
}
2008-01-05 09:57:31 -03:00
printk ( KERN_INFO " em28xx-audio.c: probing for em28x1 "
" non standard usbaudio \n " ) ;
printk ( KERN_INFO " em28xx-audio.c: Copyright (C) 2006 Markus "
" Rechberger \n " ) ;
2008-01-05 09:56:24 -03:00
adev = kzalloc ( sizeof ( * adev ) , GFP_KERNEL ) ;
if ( ! adev ) {
2008-01-05 09:57:31 -03:00
printk ( KERN_ERR " em28xx-audio.c: out of memory \n " ) ;
2008-01-05 09:55:47 -03:00
return - 1 ;
}
2008-01-05 09:56:24 -03:00
card = snd_card_new ( index [ devnr ] , " Em28xx Audio " , THIS_MODULE , 0 ) ;
if ( card = = NULL ) {
2008-01-05 09:55:47 -03:00
kfree ( adev ) ;
return - ENOMEM ;
}
spin_lock_init ( & adev - > slock ) ;
2008-01-05 09:56:24 -03:00
ret = snd_pcm_new ( card , " Em28xx Audio " , 0 , 0 , 1 , & pcm ) ;
2008-01-05 09:55:47 -03:00
snd_pcm_set_ops ( pcm , SNDRV_PCM_STREAM_CAPTURE , & snd_em28xx_pcm_capture ) ;
pcm - > info_flags = 0 ;
pcm - > private_data = dev ;
2008-01-05 09:56:24 -03:00
strcpy ( pcm - > name , " Empia 28xx Capture " ) ;
2008-01-05 09:55:47 -03:00
strcpy ( card - > driver , " Empia Em28xx Audio " ) ;
strcpy ( card - > shortname , " Em28xx Audio " ) ;
2008-01-05 09:56:24 -03:00
strcpy ( card - > longname , " Empia Em28xx Audio " ) ;
2008-01-05 09:55:47 -03:00
2008-01-05 09:57:31 -03:00
err = snd_card_register ( card ) ;
if ( err < 0 ) {
2008-01-05 09:55:47 -03:00
snd_card_free ( card ) ;
return - ENOMEM ;
}
2008-01-05 09:56:24 -03:00
adev - > sndcard = card ;
adev - > udev = dev - > udev ;
dev - > adev = adev ;
2008-01-05 09:57:31 -03:00
2008-01-05 09:55:47 -03:00
return 0 ;
}
static int em28xx_audio_fini ( struct em28xx * dev )
{
2008-01-05 09:56:24 -03:00
if ( dev = = NULL )
2008-01-05 09:55:47 -03:00
return 0 ;
2008-01-05 09:57:31 -03:00
2008-06-10 12:34:35 -03:00
if ( dev - > has_audio_class ) {
/* This device does not support the extension (in this case
the device is expecting the snd - usb - audio module */
return 0 ;
}
2008-01-05 09:56:24 -03:00
if ( dev - > adev ) {
2008-01-05 09:55:47 -03:00
snd_card_free ( dev - > adev - > sndcard ) ;
kfree ( dev - > adev ) ;
2008-01-05 09:56:24 -03:00
dev - > adev = NULL ;
2008-01-05 09:55:47 -03:00
}
2008-01-05 09:57:31 -03:00
2008-01-05 09:55:47 -03:00
return 0 ;
}
static struct em28xx_ops audio_ops = {
2008-01-05 09:57:31 -03:00
. id = EM28XX_AUDIO ,
2008-01-05 09:56:24 -03:00
. name = " Em28xx Audio Extension " ,
. init = em28xx_audio_init ,
. fini = em28xx_audio_fini ,
2008-01-05 09:55:47 -03:00
} ;
static int __init em28xx_alsa_register ( void )
{
return em28xx_register_extension ( & audio_ops ) ;
}
static void __exit em28xx_alsa_unregister ( void )
{
em28xx_unregister_extension ( & audio_ops ) ;
}
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Markus Rechberger <mrechberger@gmail.com> " ) ;
2008-01-05 09:57:31 -03:00
MODULE_AUTHOR ( " Mauro Carvalho Chehab <mchehab@infradead.org> " ) ;
2008-01-05 09:55:47 -03:00
MODULE_DESCRIPTION ( " Em28xx Audio driver " ) ;
module_init ( em28xx_alsa_register ) ;
module_exit ( em28xx_alsa_unregister ) ;