2008-01-05 09:55:47 -03:00
/*
* Empiatech em28x1 audio extension
*
* Copyright ( C ) 2006 Markus Rechberger < mrechberger @ gmail . com >
*
2011-06-17 15:15:12 -03:00
* Copyright ( C ) 2007 - 2011 Mauro Carvalho Chehab < mchehab @ redhat . com >
2008-01-05 09:57:31 -03:00
* - Port to work with the in - kernel driver
2011-06-17 15:15:12 -03:00
* - Cleanups , fixes , alsa - controls , etc .
2008-01-05 09:57:31 -03:00
*
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>
2011-06-19 13:06:40 -03:00
# include <sound/tlv.h>
2008-01-05 09:55:47 -03:00
# 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
2009-02-08 13:09:11 -03:00
static int em28xx_deinit_isoc_audio ( 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 " ) ;
2009-02-08 10:45:34 -03:00
for ( i = 0 ; i < EM28XX_AUDIO_BUFS ; i + + ) {
2009-01-25 13:08:07 -03:00
if ( ! irqs_disabled ( ) )
usb_kill_urb ( dev - > adev . urb [ i ] ) ;
else
usb_unlink_urb ( dev - > adev . urb [ i ] ) ;
2009-02-08 13:09:11 -03:00
2008-12-31 09:37:33 -03:00
usb_free_urb ( dev - > adev . urb [ i ] ) ;
dev - > adev . urb [ i ] = NULL ;
2009-01-16 11:23:25 -03:00
2009-01-25 13:08:07 -03:00
kfree ( dev - > adev . transfer_buffer [ i ] ) ;
dev - > adev . transfer_buffer [ 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 ;
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 ;
2009-02-08 13:09:11 -03:00
switch ( urb - > status ) {
case 0 : /* success */
case - ETIMEDOUT : /* NAK */
break ;
case - ECONNRESET : /* kill */
case - ENOENT :
case - ESHUTDOWN :
return ;
default : /* error */
dprintk ( " urb completition error %d. \n " , urb - > status ) ;
break ;
}
2010-10-09 15:53:58 -03:00
if ( atomic_read ( & dev - > stream_started ) = = 0 )
return ;
2008-12-31 09:37:33 -03:00
if ( dev - > adev . capture_pcm_substream ) {
substream = dev - > adev . capture_pcm_substream ;
2008-01-05 09:56:24 -03:00
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 ;
2008-12-31 09:37:33 -03:00
oldptr = dev - > adev . hwptr_done_capture ;
2008-11-24 08:45:57 -03:00
if ( oldptr + length > = runtime - > buffer_size ) {
unsigned int cnt =
runtime - > buffer_size - oldptr ;
memcpy ( runtime - > dma_area + oldptr * stride , cp ,
cnt * stride ) ;
memcpy ( runtime - > dma_area , cp + cnt * stride ,
length * stride - cnt * stride ) ;
} else {
memcpy ( runtime - > dma_area + oldptr * stride , cp ,
length * stride ) ;
}
snd_pcm_stream_lock ( substream ) ;
2008-12-31 09:37:33 -03:00
dev - > adev . hwptr_done_capture + = length ;
if ( dev - > adev . hwptr_done_capture > =
2008-01-05 09:56:24 -03:00
runtime - > buffer_size )
2008-12-31 09:37:33 -03:00
dev - > adev . hwptr_done_capture - =
2008-01-05 09:56:24 -03:00
runtime - > buffer_size ;
2008-01-05 09:55:47 -03:00
2008-12-31 09:37:33 -03:00
dev - > adev . capture_transfer_done + = length ;
if ( dev - > adev . capture_transfer_done > =
2008-01-05 09:56:24 -03:00
runtime - > period_size ) {
2008-12-31 09:37:33 -03:00
dev - > adev . capture_transfer_done - =
2008-01-05 09:56:24 -03:00
runtime - > period_size ;
period_elapsed = 1 ;
2008-01-05 09:55:47 -03:00
}
2008-01-05 09:57:31 -03:00
2008-11-24 08:45:57 -03:00
snd_pcm_stream_unlock ( substream ) ;
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
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-12-31 09:37:33 -03:00
dev - > adev . transfer_buffer [ i ] = kmalloc ( sb_size , GFP_ATOMIC ) ;
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-12-31 09:37:33 -03:00
memset ( dev - > adev . transfer_buffer [ i ] , 0x80 , sb_size ) ;
2008-01-05 09:56:24 -03:00
urb = usb_alloc_urb ( EM28XX_NUM_AUDIO_PACKETS , GFP_ATOMIC ) ;
2008-09-04 11:20:20 -03:00
if ( ! urb ) {
em28xx_errdev ( " usb_alloc_urb failed! \n " ) ;
for ( j = 0 ; j < i ; j + + ) {
2008-12-31 09:37:33 -03:00
usb_free_urb ( dev - > adev . urb [ j ] ) ;
kfree ( dev - > adev . transfer_buffer [ j ] ) ;
2008-09-04 11:20:20 -03:00
}
2008-01-05 09:55:47 -03:00
return - ENOMEM ;
2008-09-04 11:20:20 -03:00
}
2008-01-05 09:57:31 -03:00
urb - > dev = dev - > udev ;
urb - > context = dev ;
2011-12-28 18:55:41 -03:00
urb - > pipe = usb_rcvisocpipe ( dev - > udev , EM28XX_EP_AUDIO ) ;
2008-01-05 09:57:31 -03:00
urb - > transfer_flags = URB_ISO_ASAP ;
2008-12-31 09:37:33 -03:00
urb - > transfer_buffer = dev - > adev . transfer_buffer [ i ] ;
2008-01-05 09:57:31 -03:00
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-12-31 09:37:33 -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-12-31 09:37:33 -03:00
errCode = usb_submit_urb ( dev - > adev . urb [ i ] , GFP_ATOMIC ) ;
2008-01-05 09:56:24 -03:00
if ( errCode ) {
2011-06-19 10:15:35 -03:00
em28xx_errdev ( " submit of audio urb failed \n " ) ;
2009-02-08 13:09:11 -03:00
em28xx_deinit_isoc_audio ( dev ) ;
2011-06-19 10:15:35 -03:00
atomic_set ( & dev - > stream_started , 0 ) ;
2008-01-05 09:55:47 -03:00
return errCode ;
}
2011-06-19 10:15:35 -03:00
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 ;
}
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 ;
2009-02-10 23:28:24 -03:00
dprintk ( " Allocating vbuffer \n " ) ;
2008-01-05 09:57:31 -03:00
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 |
2011-06-19 13:39:31 -03:00
SNDRV_PCM_INFO_BATCH |
2008-01-05 09:57:31 -03:00
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 " ) ;
2008-06-14 09:41:18 -03:00
if ( ! dev ) {
2009-11-04 15:31:25 -03:00
em28xx_err ( " BUG: em28xx can't find device struct. "
2008-06-14 09:41:18 -03:00
" Can't proceed with open \n " ) ;
return - ENODEV ;
}
2011-06-17 15:15:12 -03:00
runtime - > hw = snd_em28xx_hw_capture ;
if ( ( dev - > alt = = 0 | | dev - > audio_ifnum ) & & dev - > adev . users = = 0 ) {
if ( dev - > audio_ifnum )
dev - > alt = 1 ;
else
dev - > alt = 7 ;
2008-02-06 18:52:15 -03:00
2011-06-17 15:15:12 -03:00
dprintk ( " changing alternate number on interface %d to %d \n " ,
dev - > audio_ifnum , dev - > alt ) ;
usb_set_interface ( dev - > udev , dev - > audio_ifnum , dev - > alt ) ;
2008-01-05 09:57:31 -03:00
2011-06-17 15:15:12 -03:00
/* Sets volume, mute, etc */
dev - > mute = 0 ;
mutex_lock ( & dev - > lock ) ;
ret = em28xx_audio_analog_set ( dev ) ;
if ( ret < 0 )
goto err ;
2008-01-05 09:57:31 -03:00
2011-06-17 15:15:12 -03:00
dev - > adev . users + + ;
mutex_unlock ( & dev - > lock ) ;
}
2008-01-05 09:57:31 -03:00
snd_pcm_hw_constraint_integer ( runtime , SNDRV_PCM_HW_PARAM_PERIODS ) ;
2008-12-31 09:37:33 -03:00
dev - > adev . capture_pcm_substream = substream ;
2008-01-05 09:57:31 -03:00
runtime - > private_data = dev ;
return 0 ;
err :
2010-10-09 15:53:58 -03:00
mutex_unlock ( & dev - > lock ) ;
2009-11-04 15:31:25 -03:00
em28xx_err ( " Error while configuring em28xx mixer \n " ) ;
2008-01-05 09:57:31 -03:00
return ret ;
}
static int snd_em28xx_pcm_close ( struct snd_pcm_substream * substream )
{
struct em28xx * dev = snd_pcm_substream_chip ( substream ) ;
dprintk ( " closing device \n " ) ;
dev - > mute = 1 ;
2008-02-06 18:52:15 -03:00
mutex_lock ( & dev - > lock ) ;
2009-02-08 01:11:13 -03:00
dev - > adev . users - - ;
2010-10-09 15:53:58 -03:00
if ( atomic_read ( & dev - > stream_started ) > 0 ) {
atomic_set ( & dev - > stream_started , 0 ) ;
schedule_work ( & dev - > wq_trigger ) ;
}
2008-01-05 09:57:31 -03:00
em28xx_audio_analog_set ( dev ) ;
2009-05-28 11:16:19 -03:00
if ( substream - > runtime - > dma_area ) {
dprintk ( " freeing \n " ) ;
vfree ( substream - > runtime - > dma_area ) ;
substream - > runtime - > dma_area = NULL ;
}
2008-02-06 18:52:15 -03:00
mutex_unlock ( & dev - > lock ) ;
2008-01-05 09:57:31 -03:00
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 ) ) ;
2011-06-17 20:28:51 -03:00
if ( ret < 0 )
return ret ;
2008-01-05 09:57:31 -03:00
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 " ) ;
2010-10-09 15:53:58 -03:00
if ( atomic_read ( & dev - > stream_started ) > 0 ) {
atomic_set ( & dev - > stream_started , 0 ) ;
schedule_work ( & dev - > wq_trigger ) ;
}
2008-01-05 09:57:31 -03:00
return 0 ;
}
static int snd_em28xx_prepare ( struct snd_pcm_substream * substream )
{
2009-10-15 01:14:34 -03:00
struct em28xx * dev = snd_pcm_substream_chip ( substream ) ;
dev - > adev . hwptr_done_capture = 0 ;
dev - > adev . capture_transfer_done = 0 ;
2008-01-05 09:57:31 -03:00
return 0 ;
}
2010-10-09 15:53:58 -03:00
static void audio_trigger ( struct work_struct * work )
{
struct em28xx * dev = container_of ( work , struct em28xx , wq_trigger ) ;
if ( atomic_read ( & dev - > stream_started ) ) {
dprintk ( " starting capture " ) ;
em28xx_init_audio_isoc ( dev ) ;
} else {
dprintk ( " stopping capture " ) ;
em28xx_deinit_isoc_audio ( dev ) ;
}
}
2008-01-05 09:57:31 -03:00
static int snd_em28xx_capture_trigger ( struct snd_pcm_substream * substream ,
int cmd )
{
struct em28xx * dev = snd_pcm_substream_chip ( substream ) ;
2011-06-17 20:28:51 -03:00
int retval = 0 ;
2008-01-05 09:57:31 -03:00
switch ( cmd ) {
2011-06-19 13:39:31 -03:00
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE : /* fall through */
case SNDRV_PCM_TRIGGER_RESUME : /* fall through */
2008-01-05 09:57:31 -03:00
case SNDRV_PCM_TRIGGER_START :
2010-10-09 15:53:58 -03:00
atomic_set ( & dev - > stream_started , 1 ) ;
2009-02-08 14:17:15 -03:00
break ;
2011-06-19 13:39:31 -03:00
case SNDRV_PCM_TRIGGER_PAUSE_PUSH : /* fall through */
case SNDRV_PCM_TRIGGER_SUSPEND : /* fall through */
2008-01-05 09:57:31 -03:00
case SNDRV_PCM_TRIGGER_STOP :
2011-06-19 13:39:31 -03:00
atomic_set ( & dev - > stream_started , 0 ) ;
2009-02-08 14:17:15 -03:00
break ;
2008-01-05 09:57:31 -03:00
default :
2009-02-08 14:17:15 -03:00
retval = - EINVAL ;
2008-01-05 09:57:31 -03:00
}
2010-10-09 15:53:58 -03:00
schedule_work ( & dev - > wq_trigger ) ;
2011-06-17 20:28:51 -03:00
return retval ;
2008-01-05 09:57:31 -03:00
}
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
{
2009-02-10 23:28:24 -03:00
unsigned long flags ;
2009-01-16 11:23:25 -03:00
struct em28xx * dev ;
2008-01-05 09:55:47 -03:00
snd_pcm_uframes_t hwptr_done ;
2009-01-16 11:23:25 -03:00
2008-01-05 09:55:47 -03:00
dev = snd_pcm_substream_chip ( substream ) ;
2009-01-25 15:12:29 -03:00
spin_lock_irqsave ( & dev - > adev . slock , flags ) ;
2008-12-31 09:37:33 -03:00
hwptr_done = dev - > adev . hwptr_done_capture ;
2009-01-25 15:12:29 -03:00
spin_unlock_irqrestore ( & dev - > adev . slock , flags ) ;
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 ) ;
}
2011-06-19 13:06:40 -03:00
/*
* AC97 volume control support
*/
static int em28xx_vol_info ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_info * info )
{
info - > type = SNDRV_CTL_ELEM_TYPE_INTEGER ;
info - > count = 2 ;
info - > value . integer . min = 0 ;
info - > value . integer . max = 0x1f ;
return 0 ;
}
static int em28xx_vol_put ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * value )
{
struct em28xx * dev = snd_kcontrol_chip ( kcontrol ) ;
2011-06-19 13:14:07 -03:00
u16 val = ( 0x1f - ( value - > value . integer . value [ 0 ] & 0x1f ) ) |
( 0x1f - ( value - > value . integer . value [ 1 ] & 0x1f ) ) < < 8 ;
2011-06-19 13:06:40 -03:00
int rc ;
mutex_lock ( & dev - > lock ) ;
rc = em28xx_read_ac97 ( dev , kcontrol - > private_value ) ;
if ( rc < 0 )
goto err ;
2011-06-19 13:08:46 -03:00
val | = rc & 0x8000 ; /* Preserve the mute flag */
2011-06-19 13:06:40 -03:00
rc = em28xx_write_ac97 ( dev , kcontrol - > private_value , val ) ;
2011-06-18 11:00:20 -03:00
if ( rc < 0 )
goto err ;
dprintk ( " %sleft vol %d, right vol %d (0x%04x) to ac97 volume control 0x%04x \n " ,
( val & 0x8000 ) ? " muted " : " " ,
0x1f - ( ( val > > 8 ) & 0x1f ) , 0x1f - ( val & 0x1f ) ,
val , ( int ) kcontrol - > private_value ) ;
2011-06-19 13:06:40 -03:00
err :
mutex_unlock ( & dev - > lock ) ;
return rc ;
}
static int em28xx_vol_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * value )
{
struct em28xx * dev = snd_kcontrol_chip ( kcontrol ) ;
int val ;
mutex_lock ( & dev - > lock ) ;
val = em28xx_read_ac97 ( dev , kcontrol - > private_value ) ;
mutex_unlock ( & dev - > lock ) ;
if ( val < 0 )
return val ;
2011-06-18 11:00:20 -03:00
dprintk ( " %sleft vol %d, right vol %d (0x%04x) from ac97 volume control 0x%04x \n " ,
( val & 0x8000 ) ? " muted " : " " ,
0x1f - ( ( val > > 8 ) & 0x1f ) , 0x1f - ( val & 0x1f ) ,
val , ( int ) kcontrol - > private_value ) ;
2011-06-19 13:14:07 -03:00
value - > value . integer . value [ 0 ] = 0x1f - ( val & 0x1f ) ;
value - > value . integer . value [ 1 ] = 0x1f - ( ( val < < 8 ) & 0x1f ) ;
2011-06-19 13:06:40 -03:00
return 0 ;
}
2011-06-19 13:08:46 -03:00
static int em28xx_vol_put_mute ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * value )
{
struct em28xx * dev = snd_kcontrol_chip ( kcontrol ) ;
u16 val = value - > value . integer . value [ 0 ] ;
int rc ;
mutex_lock ( & dev - > lock ) ;
rc = em28xx_read_ac97 ( dev , kcontrol - > private_value ) ;
if ( rc < 0 )
goto err ;
if ( val )
2011-06-19 13:14:07 -03:00
rc & = 0x1f1f ;
2011-06-19 13:08:46 -03:00
else
2011-06-19 13:14:07 -03:00
rc | = 0x8000 ;
2011-06-19 13:08:46 -03:00
rc = em28xx_write_ac97 ( dev , kcontrol - > private_value , rc ) ;
2011-06-18 11:00:20 -03:00
if ( rc < 0 )
goto err ;
dprintk ( " %sleft vol %d, right vol %d (0x%04x) to ac97 volume control 0x%04x \n " ,
( val & 0x8000 ) ? " muted " : " " ,
0x1f - ( ( val > > 8 ) & 0x1f ) , 0x1f - ( val & 0x1f ) ,
val , ( int ) kcontrol - > private_value ) ;
2011-06-19 13:08:46 -03:00
err :
mutex_unlock ( & dev - > lock ) ;
return rc ;
}
static int em28xx_vol_get_mute ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * value )
{
struct em28xx * dev = snd_kcontrol_chip ( kcontrol ) ;
int val ;
mutex_lock ( & dev - > lock ) ;
val = em28xx_read_ac97 ( dev , kcontrol - > private_value ) ;
mutex_unlock ( & dev - > lock ) ;
if ( val < 0 )
return val ;
if ( val & 0x8000 )
value - > value . integer . value [ 0 ] = 0 ;
2011-06-19 13:14:07 -03:00
else
value - > value . integer . value [ 0 ] = 1 ;
2011-06-19 13:08:46 -03:00
2011-06-18 11:00:20 -03:00
dprintk ( " %sleft vol %d, right vol %d (0x%04x) from ac97 volume control 0x%04x \n " ,
( val & 0x8000 ) ? " muted " : " " ,
0x1f - ( ( val > > 8 ) & 0x1f ) , 0x1f - ( val & 0x1f ) ,
val , ( int ) kcontrol - > private_value ) ;
2011-06-19 13:08:46 -03:00
return 0 ;
}
2011-06-19 13:06:40 -03:00
static const DECLARE_TLV_DB_SCALE ( em28xx_db_scale , - 3450 , 150 , 0 ) ;
static int em28xx_cvol_new ( struct snd_card * card , struct em28xx * dev ,
char * name , int id )
{
int err ;
2011-06-19 13:08:46 -03:00
char ctl_name [ 44 ] ;
2011-06-19 13:06:40 -03:00
struct snd_kcontrol * kctl ;
2011-06-19 13:08:46 -03:00
struct snd_kcontrol_new tmp ;
memset ( & tmp , 0 , sizeof ( tmp ) ) ;
tmp . iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
tmp . private_value = id ,
tmp . name = ctl_name ,
/* Add Mute Control */
sprintf ( ctl_name , " %s Switch " , name ) ;
tmp . get = em28xx_vol_get_mute ;
tmp . put = em28xx_vol_put_mute ;
tmp . info = snd_ctl_boolean_mono_info ;
2011-06-19 13:06:40 -03:00
kctl = snd_ctl_new1 ( & tmp , dev ) ;
2011-06-19 13:08:46 -03:00
err = snd_ctl_add ( card , kctl ) ;
if ( err < 0 )
return err ;
2011-06-18 11:00:20 -03:00
dprintk ( " Added control %s for ac97 volume control 0x%04x \n " ,
ctl_name , id ) ;
2011-06-19 13:06:40 -03:00
2011-06-19 13:08:46 -03:00
memset ( & tmp , 0 , sizeof ( tmp ) ) ;
tmp . iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
tmp . private_value = id ,
tmp . name = ctl_name ,
/* Add Volume Control */
sprintf ( ctl_name , " %s Volume " , name ) ;
tmp . get = em28xx_vol_get ;
tmp . put = em28xx_vol_put ;
tmp . info = em28xx_vol_info ;
tmp . tlv . p = em28xx_db_scale ,
kctl = snd_ctl_new1 ( & tmp , dev ) ;
2011-06-19 13:06:40 -03:00
err = snd_ctl_add ( card , kctl ) ;
if ( err < 0 )
return err ;
2011-06-18 11:00:20 -03:00
dprintk ( " Added control %s for ac97 volume control 0x%04x \n " ,
ctl_name , id ) ;
2011-06-19 13:06:40 -03:00
return 0 ;
}
/*
* register / unregister code and data
*/
2008-01-05 09:55:47 -03:00
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 )
{
2008-12-31 09:37:33 -03:00
struct em28xx_audio * adev = & dev - > adev ;
2008-01-05 09:57:31 -03:00
struct snd_pcm * pcm ;
struct snd_card * card ;
static int devnr ;
2008-11-12 02:04:53 -03:00
int err ;
2008-01-05 09:57:31 -03:00
2011-06-17 15:15:12 -03:00
if ( ! dev - > has_alsa_audio | | dev - > audio_ifnum < 0 ) {
2008-06-10 12:34:35 -03:00
/* This device does not support the extension (in this case
2008-11-12 02:05:19 -03:00
the device is expecting the snd - usb - audio module or
doesn ' t have analog audio support at all ) */
2008-06-10 12:34:35 -03:00
return 0 ;
}
2011-06-17 15:15:12 -03:00
printk ( KERN_INFO " em28xx-audio.c: probing for em28xx Audio Vendor Class \n " ) ;
2008-01-05 09:57:31 -03:00
printk ( KERN_INFO " em28xx-audio.c: Copyright (C) 2006 Markus "
" Rechberger \n " ) ;
2011-06-17 15:15:12 -03:00
printk ( KERN_INFO " em28xx-audio.c: Copyright (C) 2007-2011 Mauro Carvalho Chehab \n " ) ;
2008-01-05 09:57:31 -03:00
2009-01-12 15:17:09 +01:00
err = snd_card_create ( index [ devnr ] , " Em28xx Audio " , THIS_MODULE , 0 ,
& card ) ;
if ( err < 0 )
return err ;
2008-01-05 09:55:47 -03:00
spin_lock_init ( & adev - > slock ) ;
2008-11-07 14:24:18 -03:00
err = snd_pcm_new ( card , " Em28xx Audio " , 0 , 0 , 1 , & pcm ) ;
if ( err < 0 ) {
snd_card_free ( card ) ;
return err ;
}
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 " ) ;
2009-02-19 13:41:56 -03:00
snd_card_set_dev ( card , & dev - > udev - > dev ) ;
2010-03-23 08:40:43 -03:00
strcpy ( card - > driver , " Em28xx-Audio " ) ;
2008-01-05 09:55:47 -03:00
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
2010-10-09 15:53:58 -03:00
INIT_WORK ( & dev - > wq_trigger , audio_trigger ) ;
2011-06-19 13:06:40 -03:00
if ( dev - > audio_mode . ac97 ! = EM28XX_NO_AC97 ) {
em28xx_cvol_new ( card , dev , " Video " , AC97_VIDEO_VOL ) ;
em28xx_cvol_new ( card , dev , " Line In " , AC97_LINEIN_VOL ) ;
em28xx_cvol_new ( card , dev , " Phone " , AC97_PHONE_VOL ) ;
em28xx_cvol_new ( card , dev , " Microphone " , AC97_PHONE_VOL ) ;
em28xx_cvol_new ( card , dev , " CD " , AC97_CD_VOL ) ;
em28xx_cvol_new ( card , dev , " AUX " , AC97_AUX_VOL ) ;
em28xx_cvol_new ( card , dev , " PCM " , AC97_PCM_OUT_VOL ) ;
em28xx_cvol_new ( card , dev , " Master " , AC97_MASTER_VOL ) ;
em28xx_cvol_new ( card , dev , " Line " , AC97_LINE_LEVEL_VOL ) ;
em28xx_cvol_new ( card , dev , " Mono " , AC97_MASTER_MONO_VOL ) ;
em28xx_cvol_new ( card , dev , " LFE " , AC97_LFE_MASTER_VOL ) ;
em28xx_cvol_new ( card , dev , " Surround " , AC97_SURR_MASTER_VOL ) ;
}
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 ) ;
2008-11-07 14:24:18 -03:00
return err ;
2008-01-05 09:55:47 -03:00
}
2008-01-05 09:56:24 -03:00
adev - > sndcard = card ;
adev - > udev = dev - > udev ;
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-11-12 02:05:19 -03:00
if ( dev - > has_alsa_audio ! = 1 ) {
2008-06-10 12:34:35 -03:00
/* This device does not support the extension (in this case
2008-11-12 02:05:19 -03:00
the device is expecting the snd - usb - audio module or
doesn ' t have analog audio support at all ) */
2008-06-10 12:34:35 -03:00
return 0 ;
}
2008-12-31 09:37:33 -03:00
if ( dev - > adev . sndcard ) {
snd_card_free ( dev - > adev . sndcard ) ;
dev - > adev . sndcard = 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> " ) ;
2011-06-17 15:15:12 -03:00
MODULE_AUTHOR ( " Mauro Carvalho Chehab <mchehab@redhat.com> " ) ;
2008-01-05 09:55:47 -03:00
MODULE_DESCRIPTION ( " Em28xx Audio driver " ) ;
module_init ( em28xx_alsa_register ) ;
module_exit ( em28xx_alsa_unregister ) ;