2008-01-11 10:28:50 -03:00
/*
*
2010-02-22 06:32:15 -03:00
* Support for audio capture for tm5600 / 6000 / 6010
2014-02-07 08:03:07 -02:00
* ( c ) 2007 - 2008 Mauro Carvalho Chehab
2008-01-11 10:28:50 -03:00
*
2008-01-11 13:19:09 -03:00
* Based on cx88 - alsa . c
2008-01-11 10:28:50 -03:00
*
* This program is free software ; you can redistribute it and / or modify
2008-01-11 13:19:09 -03:00
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
2008-01-11 10:28:50 -03:00
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/device.h>
# include <linux/interrupt.h>
2008-01-11 13:51:45 -03:00
# include <linux/usb.h>
2010-03-30 02:52:33 +09:00
# include <linux/slab.h>
2010-06-03 16:24:19 -03:00
# include <linux/vmalloc.h>
2008-01-11 10:28:50 -03:00
2011-06-02 20:33:34 -04:00
# include <linux/delay.h>
2008-01-11 10:28:50 -03:00
# include <sound/core.h>
# include <sound/pcm.h>
# include <sound/pcm_params.h>
# include <sound/control.h>
# include <sound/initval.h>
2008-01-11 13:19:09 -03:00
# include "tm6000.h"
# include "tm6000-regs.h"
# undef dprintk
2008-01-11 10:28:50 -03:00
2008-01-11 13:19:09 -03:00
# define dprintk(level, fmt, arg...) do { \
if ( debug > = level ) \
printk ( KERN_INFO " %s/1: " fmt , chip - > core - > name , # # arg ) ; \
} while ( 0 )
2008-01-11 10:28:50 -03:00
/****************************************************************************
Module global static vars
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int index [ SNDRV_CARDS ] = SNDRV_DEFAULT_IDX ; /* Index 0-MAX */
2010-06-03 13:45:41 -03:00
2012-01-13 09:32:20 +10:30
static bool enable [ SNDRV_CARDS ] = { 1 , [ 1 . . . ( SNDRV_CARDS - 1 ) ] = 1 } ;
2008-01-11 10:28:50 -03:00
module_param_array ( enable , bool , NULL , 0444 ) ;
2008-01-11 13:19:09 -03:00
MODULE_PARM_DESC ( enable , " Enable tm6000x soundcard. default enabled. " ) ;
2008-01-11 10:28:50 -03:00
module_param_array ( index , int , NULL , 0444 ) ;
2008-01-11 13:19:09 -03:00
MODULE_PARM_DESC ( index , " Index value for tm6000x capture interface(s). " ) ;
2008-01-11 10:28:50 -03:00
/****************************************************************************
Module macros
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2010-02-22 06:32:15 -03:00
MODULE_DESCRIPTION ( " ALSA driver module for tm5600/tm6000/tm6010 based TV cards " ) ;
2014-02-07 08:03:07 -02:00
MODULE_AUTHOR ( " Mauro Carvalho Chehab " ) ;
2008-01-11 10:28:50 -03:00
MODULE_LICENSE ( " GPL " ) ;
2008-01-11 13:19:09 -03:00
MODULE_SUPPORTED_DEVICE ( " {{Trident,tm5600}, "
2010-02-22 06:32:15 -03:00
" {{Trident,tm6000}, "
" {{Trident,tm6010} " ) ;
2008-01-11 10:28:50 -03:00
static unsigned int debug ;
2008-01-11 13:19:09 -03:00
module_param ( debug , int , 0644 ) ;
MODULE_PARM_DESC ( debug , " enable debug messages " ) ;
2008-01-11 10:28:50 -03:00
/****************************************************************************
Module specific funtions
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
* BOARD Specific : Sets audio DMA
*/
2008-01-11 13:19:09 -03:00
static int _tm6000_start_audio_dma ( struct snd_tm6000_card * chip )
2008-01-11 10:28:50 -03:00
{
2008-01-11 13:19:09 -03:00
struct tm6000_core * core = chip - > core ;
2008-01-11 10:28:50 -03:00
2010-06-05 15:10:36 -03:00
dprintk ( 1 , " Starting audio DMA \n " ) ;
2008-01-11 13:19:09 -03:00
/* Enables audio */
2011-08-04 04:14:07 -03:00
tm6000_set_reg_mask ( core , TM6010_REQ07_RCC_ACTIVE_IF , 0x40 , 0x40 ) ;
2008-01-11 10:28:50 -03:00
2010-06-07 11:57:01 -03:00
tm6000_set_audio_bitrate ( core , 48000 ) ;
2008-01-11 10:28:50 -03:00
return 0 ;
}
/*
* BOARD Specific : Resets audio DMA
*/
2008-01-11 13:19:09 -03:00
static int _tm6000_stop_audio_dma ( struct snd_tm6000_card * chip )
2008-01-11 10:28:50 -03:00
{
2008-01-11 13:19:09 -03:00
struct tm6000_core * core = chip - > core ;
2011-02-17 22:11:05 -03:00
2008-01-11 10:28:50 -03:00
dprintk ( 1 , " Stopping audio DMA \n " ) ;
2011-02-17 22:11:05 -03:00
/* Disables audio */
2011-08-04 04:14:07 -03:00
tm6000_set_reg_mask ( core , TM6010_REQ07_RCC_ACTIVE_IF , 0x00 , 0x40 ) ;
2008-01-11 10:28:50 -03:00
return 0 ;
}
2010-06-03 16:24:19 -03:00
static void dsp_buffer_free ( struct snd_pcm_substream * substream )
2008-01-11 10:28:50 -03:00
{
2010-06-03 16:24:19 -03:00
struct snd_tm6000_card * chip = snd_pcm_substream_chip ( substream ) ;
2008-01-11 10:28:50 -03:00
2008-01-11 13:19:09 -03:00
dprintk ( 2 , " Freeing buffer \n " ) ;
2008-01-11 10:28:50 -03:00
2010-06-03 16:24:19 -03:00
vfree ( substream - > runtime - > dma_area ) ;
substream - > runtime - > dma_area = NULL ;
substream - > runtime - > dma_bytes = 0 ;
}
static int dsp_buffer_alloc ( struct snd_pcm_substream * substream , int size )
{
struct snd_tm6000_card * chip = snd_pcm_substream_chip ( substream ) ;
dprintk ( 2 , " Allocating buffer \n " ) ;
2008-01-11 10:28:50 -03:00
2010-06-03 16:24:19 -03:00
if ( substream - > runtime - > dma_area ) {
if ( substream - > runtime - > dma_bytes > size )
return 0 ;
2011-06-23 11:57:45 -03:00
2010-06-03 16:24:19 -03:00
dsp_buffer_free ( substream ) ;
}
substream - > runtime - > dma_area = vmalloc ( size ) ;
if ( ! substream - > runtime - > dma_area )
return - ENOMEM ;
2008-01-11 10:28:50 -03:00
2010-06-03 16:24:19 -03:00
substream - > runtime - > dma_bytes = size ;
return 0 ;
2008-01-11 10:28:50 -03:00
}
2010-06-03 16:24:19 -03:00
2008-01-11 10:28:50 -03:00
/****************************************************************************
ALSA PCM Interface
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
* Digital hardware definition
*/
# define DEFAULT_FIFO_SIZE 4096
2008-01-11 13:19:09 -03:00
static struct snd_pcm_hardware snd_tm6000_digital_hw = {
2011-11-28 19:38:54 -02:00
. info = SNDRV_PCM_INFO_BATCH |
SNDRV_PCM_INFO_MMAP |
2008-01-11 10:28:50 -03:00
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID ,
. formats = SNDRV_PCM_FMTBIT_S16_LE ,
2011-11-28 19:38:54 -02:00
. rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT ,
2011-06-23 11:57:45 -03:00
. rate_min = 48000 ,
. rate_max = 48000 ,
2008-01-11 10:28:50 -03:00
. channels_min = 2 ,
. channels_max = 2 ,
2010-09-20 17:07:15 -03:00
. period_bytes_min = 64 ,
. period_bytes_max = 12544 ,
2011-11-28 19:38:54 -02:00
. periods_min = 2 ,
2010-09-20 17:07:15 -03:00
. periods_max = 98 ,
2010-06-05 19:21:34 -03:00
. buffer_bytes_max = 62720 * 8 ,
2008-01-11 10:28:50 -03:00
} ;
/*
* audio pcm capture open callback
*/
2008-01-11 13:19:09 -03:00
static int snd_tm6000_pcm_open ( struct snd_pcm_substream * substream )
2008-01-11 10:28:50 -03:00
{
2008-01-11 13:19:09 -03:00
struct snd_tm6000_card * chip = snd_pcm_substream_chip ( substream ) ;
2008-01-11 10:28:50 -03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
int err ;
2008-01-11 13:19:09 -03:00
err = snd_pcm_hw_constraint_pow2 ( runtime , 0 ,
SNDRV_PCM_HW_PARAM_PERIODS ) ;
2008-01-11 10:28:50 -03:00
if ( err < 0 )
goto _error ;
chip - > substream = substream ;
2008-01-11 13:19:09 -03:00
runtime - > hw = snd_tm6000_digital_hw ;
2011-11-28 19:38:54 -02:00
snd_pcm_hw_constraint_integer ( runtime , SNDRV_PCM_HW_PARAM_PERIODS ) ;
2008-01-11 10:28:50 -03:00
return 0 ;
_error :
2008-01-11 13:19:09 -03:00
dprintk ( 1 , " Error opening PCM! \n " ) ;
2008-01-11 10:28:50 -03:00
return err ;
}
/*
* audio close callback
*/
2008-01-11 13:19:09 -03:00
static int snd_tm6000_close ( struct snd_pcm_substream * substream )
2008-01-11 10:28:50 -03:00
{
2010-10-09 21:15:33 -03:00
struct snd_tm6000_card * chip = snd_pcm_substream_chip ( substream ) ;
struct tm6000_core * core = chip - > core ;
if ( atomic_read ( & core - > stream_started ) > 0 ) {
atomic_set ( & core - > stream_started , 0 ) ;
schedule_work ( & core - > wq_trigger ) ;
}
2008-01-11 10:28:50 -03:00
return 0 ;
}
2010-06-03 17:16:28 -03:00
static int tm6000_fillbuf ( struct tm6000_core * core , char * buf , int size )
{
2010-06-05 19:21:34 -03:00
struct snd_tm6000_card * chip = core - > adev ;
struct snd_pcm_substream * substream = chip - > substream ;
struct snd_pcm_runtime * runtime ;
int period_elapsed = 0 ;
unsigned int stride , buf_pos ;
2010-09-20 17:07:15 -03:00
int length ;
2010-06-05 19:21:34 -03:00
2010-10-09 21:15:33 -03:00
if ( atomic_read ( & core - > stream_started ) = = 0 )
return 0 ;
2010-09-20 17:07:15 -03:00
if ( ! size | | ! substream ) {
dprintk ( 1 , " substream was NULL \n " ) ;
2010-06-05 19:21:34 -03:00
return - EINVAL ;
2010-09-20 17:07:15 -03:00
}
2010-06-05 19:21:34 -03:00
runtime = substream - > runtime ;
2010-09-20 17:07:15 -03:00
if ( ! runtime | | ! runtime - > dma_area ) {
dprintk ( 1 , " runtime was NULL \n " ) ;
2010-06-05 19:21:34 -03:00
return - EINVAL ;
2010-09-20 17:07:15 -03:00
}
2010-06-05 19:21:34 -03:00
buf_pos = chip - > buf_pos ;
stride = runtime - > frame_bits > > 3 ;
2010-09-20 17:07:15 -03:00
if ( stride = = 0 ) {
dprintk ( 1 , " stride is zero \n " ) ;
return - EINVAL ;
}
length = size / stride ;
if ( length = = 0 ) {
dprintk ( 1 , " %s: length was zero \n " , __func__ ) ;
return - EINVAL ;
}
2010-06-05 19:21:34 -03:00
dprintk ( 1 , " Copying %d bytes at %p[%d] - buf size=%d x %d \n " , size ,
runtime - > dma_area , buf_pos ,
( unsigned int ) runtime - > buffer_size , stride ) ;
2010-09-20 17:07:15 -03:00
if ( buf_pos + length > = runtime - > buffer_size ) {
unsigned int cnt = runtime - > buffer_size - buf_pos ;
memcpy ( runtime - > dma_area + buf_pos * stride , buf , cnt * stride ) ;
memcpy ( runtime - > dma_area , buf + cnt * stride ,
length * stride - cnt * stride ) ;
2010-06-05 19:21:34 -03:00
} else
2010-09-20 17:07:15 -03:00
memcpy ( runtime - > dma_area + buf_pos * stride , buf ,
length * stride ) ;
2010-06-05 19:21:34 -03:00
2011-06-02 20:33:34 -04:00
snd_pcm_stream_lock ( substream ) ;
2010-06-05 19:21:34 -03:00
2010-09-20 17:07:15 -03:00
chip - > buf_pos + = length ;
if ( chip - > buf_pos > = runtime - > buffer_size )
chip - > buf_pos - = runtime - > buffer_size ;
chip - > period_pos + = length ;
2010-06-05 19:21:34 -03:00
if ( chip - > period_pos > = runtime - > period_size ) {
chip - > period_pos - = runtime - > period_size ;
period_elapsed = 1 ;
}
2010-06-03 17:16:28 -03:00
2011-06-02 20:33:34 -04:00
snd_pcm_stream_unlock ( substream ) ;
2010-09-20 17:07:15 -03:00
2010-06-05 19:21:34 -03:00
if ( period_elapsed )
snd_pcm_period_elapsed ( substream ) ;
2010-06-03 17:16:28 -03:00
return 0 ;
}
2008-01-11 10:28:50 -03:00
/*
* hw_params callback
*/
2008-01-11 13:19:09 -03:00
static int snd_tm6000_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * hw_params )
2008-01-11 10:28:50 -03:00
{
2010-06-03 16:24:19 -03:00
int size , rc ;
2008-01-11 13:19:09 -03:00
2010-06-03 16:24:19 -03:00
size = params_period_bytes ( hw_params ) * params_periods ( hw_params ) ;
2008-01-11 13:19:09 -03:00
2010-06-03 16:24:19 -03:00
rc = dsp_buffer_alloc ( substream , size ) ;
if ( rc < 0 )
return rc ;
2008-01-11 13:19:09 -03:00
return 0 ;
2008-01-11 10:28:50 -03:00
}
/*
* hw free callback
*/
2008-01-11 13:19:09 -03:00
static int snd_tm6000_hw_free ( struct snd_pcm_substream * substream )
2008-01-11 10:28:50 -03:00
{
2010-06-05 15:10:36 -03:00
struct snd_tm6000_card * chip = snd_pcm_substream_chip ( substream ) ;
2010-10-09 21:15:33 -03:00
struct tm6000_core * core = chip - > core ;
2010-06-05 15:10:36 -03:00
2010-10-09 21:15:33 -03:00
if ( atomic_read ( & core - > stream_started ) > 0 ) {
atomic_set ( & core - > stream_started , 0 ) ;
schedule_work ( & core - > wq_trigger ) ;
}
2008-01-11 10:28:50 -03:00
2011-08-04 04:14:16 -03:00
dsp_buffer_free ( substream ) ;
2008-01-11 10:28:50 -03:00
return 0 ;
}
/*
* prepare callback
*/
2008-01-11 13:19:09 -03:00
static int snd_tm6000_prepare ( struct snd_pcm_substream * substream )
2008-01-11 10:28:50 -03:00
{
2010-06-05 15:10:36 -03:00
struct snd_tm6000_card * chip = snd_pcm_substream_chip ( substream ) ;
chip - > buf_pos = 0 ;
chip - > period_pos = 0 ;
2008-01-11 10:28:50 -03:00
return 0 ;
}
2008-01-11 13:19:09 -03:00
2008-01-11 10:28:50 -03:00
/*
* trigger callback
*/
2010-10-09 21:15:33 -03:00
static void audio_trigger ( struct work_struct * work )
{
struct tm6000_core * core = container_of ( work , struct tm6000_core ,
wq_trigger ) ;
struct snd_tm6000_card * chip = core - > adev ;
if ( atomic_read ( & core - > stream_started ) ) {
dprintk ( 1 , " starting capture " ) ;
_tm6000_start_audio_dma ( chip ) ;
} else {
dprintk ( 1 , " stopping capture " ) ;
_tm6000_stop_audio_dma ( chip ) ;
}
}
2008-01-11 13:19:09 -03:00
static int snd_tm6000_card_trigger ( struct snd_pcm_substream * substream , int cmd )
2008-01-11 10:28:50 -03:00
{
2008-01-11 13:19:09 -03:00
struct snd_tm6000_card * chip = snd_pcm_substream_chip ( substream ) ;
2010-10-09 21:15:33 -03:00
struct tm6000_core * core = chip - > core ;
int err = 0 ;
2008-01-11 10:28:50 -03:00
switch ( cmd ) {
2011-11-28 19:38:54 -02:00
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE : /* fall through */
case SNDRV_PCM_TRIGGER_RESUME : /* fall through */
2008-01-11 10:28:50 -03:00
case SNDRV_PCM_TRIGGER_START :
2010-10-09 21:15:33 -03:00
atomic_set ( & core - > stream_started , 1 ) ;
2008-01-11 10:28:50 -03:00
break ;
2011-11-28 19:38:54 -02:00
case SNDRV_PCM_TRIGGER_PAUSE_PUSH : /* fall through */
case SNDRV_PCM_TRIGGER_SUSPEND : /* fall through */
2008-01-11 10:28:50 -03:00
case SNDRV_PCM_TRIGGER_STOP :
2010-10-09 21:15:33 -03:00
atomic_set ( & core - > stream_started , 0 ) ;
2008-01-11 10:28:50 -03:00
break ;
default :
2008-01-11 13:19:09 -03:00
err = - EINVAL ;
2008-01-11 10:28:50 -03:00
break ;
}
2010-10-09 21:15:33 -03:00
schedule_work ( & core - > wq_trigger ) ;
2008-01-11 10:28:50 -03:00
return err ;
}
/*
* pointer callback
*/
2008-01-11 13:19:09 -03:00
static snd_pcm_uframes_t snd_tm6000_pointer ( struct snd_pcm_substream * substream )
2008-01-11 10:28:50 -03:00
{
2008-01-11 13:19:09 -03:00
struct snd_tm6000_card * chip = snd_pcm_substream_chip ( substream ) ;
2008-01-11 10:28:50 -03:00
2010-06-05 15:10:36 -03:00
return chip - > buf_pos ;
2008-01-11 10:28:50 -03:00
}
2011-11-28 19:38:54 -02:00
static struct page * snd_pcm_get_vmalloc_page ( struct snd_pcm_substream * subs ,
unsigned long offset )
{
void * pageptr = subs - > runtime - > dma_area + offset ;
return vmalloc_to_page ( pageptr ) ;
}
2008-01-11 10:28:50 -03:00
/*
* operators
*/
2008-01-11 13:19:09 -03:00
static struct snd_pcm_ops snd_tm6000_pcm_ops = {
. open = snd_tm6000_pcm_open ,
. close = snd_tm6000_close ,
2008-01-11 10:28:50 -03:00
. ioctl = snd_pcm_lib_ioctl ,
2008-01-11 13:19:09 -03:00
. hw_params = snd_tm6000_hw_params ,
. hw_free = snd_tm6000_hw_free ,
. prepare = snd_tm6000_prepare ,
. trigger = snd_tm6000_card_trigger ,
. pointer = snd_tm6000_pointer ,
2011-11-28 19:38:54 -02:00
. page = snd_pcm_get_vmalloc_page ,
2008-01-11 10:28:50 -03:00
} ;
/*
* create a PCM device
*/
2008-01-11 13:19:09 -03:00
/* FIXME: Control interface - How to control volume/mute? */
2008-01-11 10:28:50 -03:00
/****************************************************************************
Basic Flow for Sound Devices
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
* Alsa Constructor - Component probe
*/
2011-08-04 04:14:01 -03:00
static int tm6000_audio_init ( struct tm6000_core * dev )
2008-01-11 10:28:50 -03:00
{
2010-05-30 09:19:02 -03:00
struct snd_card * card ;
struct snd_tm6000_card * chip ;
int rc ;
static int devnr ;
char component [ 14 ] ;
struct snd_pcm * pcm ;
if ( ! dev )
return 0 ;
2008-01-11 13:51:45 -03:00
2010-05-30 09:19:02 -03:00
if ( devnr > = SNDRV_CARDS )
2008-01-11 13:51:45 -03:00
return - ENODEV ;
2010-05-30 09:19:02 -03:00
if ( ! enable [ devnr ] )
2008-01-11 13:51:45 -03:00
return - ENOENT ;
2014-01-29 14:48:43 +01:00
rc = snd_card_new ( & dev - > udev - > dev , index [ devnr ] , " tm6000 " ,
THIS_MODULE , 0 , & card ) ;
2009-09-14 16:37:13 -03:00
if ( rc < 0 ) {
2010-05-30 09:19:02 -03:00
snd_printk ( KERN_ERR " cannot create card instance %d \n " , devnr ) ;
2009-09-14 16:37:13 -03:00
return rc ;
2008-01-11 13:51:45 -03:00
}
2010-06-05 15:10:36 -03:00
strcpy ( card - > driver , " tm6000-alsa " ) ;
strcpy ( card - > shortname , " TM5600/60x0 " ) ;
sprintf ( card - > longname , " TM5600/60x0 Audio at bus %d device %d " ,
dev - > udev - > bus - > busnum , dev - > udev - > devnum ) ;
sprintf ( component , " USB%04x:%04x " ,
le16_to_cpu ( dev - > udev - > descriptor . idVendor ) ,
le16_to_cpu ( dev - > udev - > descriptor . idProduct ) ) ;
snd_component_add ( card , component ) ;
2008-01-11 13:51:45 -03:00
2010-05-30 09:19:02 -03:00
chip = kzalloc ( sizeof ( struct snd_tm6000_card ) , GFP_KERNEL ) ;
2008-01-11 13:51:45 -03:00
if ( ! chip ) {
rc = - ENOMEM ;
goto error ;
}
2010-06-05 15:10:36 -03:00
chip - > core = dev ;
chip - > card = card ;
dev - > adev = chip ;
2010-05-30 09:19:02 -03:00
spin_lock_init ( & chip - > reg_lock ) ;
2010-06-05 15:10:36 -03:00
2010-05-30 09:19:02 -03:00
rc = snd_pcm_new ( card , " TM6000 Audio " , 0 , 0 , 1 , & pcm ) ;
2008-01-11 13:51:45 -03:00
if ( rc < 0 )
2010-08-11 12:01:19 +04:00
goto error_chip ;
2008-01-11 13:51:45 -03:00
2010-05-30 09:19:02 -03:00
pcm - > info_flags = 0 ;
2010-06-05 15:10:36 -03:00
pcm - > private_data = chip ;
2010-05-30 09:19:02 -03:00
strcpy ( pcm - > name , " Trident TM5600/60x0 " ) ;
2010-06-05 15:10:36 -03:00
snd_pcm_set_ops ( pcm , SNDRV_PCM_STREAM_CAPTURE , & snd_tm6000_pcm_ops ) ;
2010-05-30 09:19:02 -03:00
2010-10-09 21:15:33 -03:00
INIT_WORK ( & dev - > wq_trigger , audio_trigger ) ;
2008-01-11 13:51:45 -03:00
rc = snd_card_register ( card ) ;
if ( rc < 0 )
2010-08-11 12:01:19 +04:00
goto error_chip ;
2008-01-11 13:51:45 -03:00
2011-06-02 20:33:34 -04:00
dprintk ( 1 , " Registered audio driver for %s \n " , card - > longname ) ;
2008-01-11 10:28:50 -03:00
return 0 ;
2010-08-11 12:01:19 +04:00
error_chip :
kfree ( chip ) ;
dev - > adev = NULL ;
2008-01-11 13:51:45 -03:00
error :
snd_card_free ( card ) ;
return rc ;
2008-01-11 10:28:50 -03:00
}
2010-05-10 13:22:51 -03:00
static int tm6000_audio_fini ( struct tm6000_core * dev )
{
2012-08-14 11:49:46 -03:00
struct snd_tm6000_card * chip ;
2010-05-30 09:19:02 -03:00
if ( ! dev )
return 0 ;
2012-08-14 11:49:46 -03:00
chip = dev - > adev ;
2010-05-30 09:19:02 -03:00
if ( ! chip )
return 0 ;
if ( ! chip - > card )
return 0 ;
snd_card_free ( chip - > card ) ;
chip - > card = NULL ;
kfree ( chip ) ;
dev - > adev = NULL ;
2010-05-10 13:22:51 -03:00
return 0 ;
}
2011-08-04 04:14:01 -03:00
static struct tm6000_ops audio_ops = {
2010-06-03 16:31:20 -03:00
. type = TM6000_AUDIO ,
2010-05-10 13:22:51 -03:00
. name = " TM6000 Audio Extension " ,
. init = tm6000_audio_init ,
. fini = tm6000_audio_fini ,
2010-06-03 17:16:28 -03:00
. fillbuf = tm6000_fillbuf ,
2010-05-10 13:22:51 -03:00
} ;
static int __init tm6000_alsa_register ( void )
{
return tm6000_register_extension ( & audio_ops ) ;
}
static void __exit tm6000_alsa_unregister ( void )
{
tm6000_unregister_extension ( & audio_ops ) ;
}
module_init ( tm6000_alsa_register ) ;
module_exit ( tm6000_alsa_unregister ) ;