2019-05-27 09:55:05 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2005-12-06 15:55:26 +03:00
/*
* Driver for Digigram pcxhr compatible soundcards
*
* main file with alsa callbacks
*
* Copyright ( c ) 2004 by Digigram < alsa @ digigram . com >
*/
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/slab.h>
# include <linux/pci.h>
2006-03-22 12:53:19 +03:00
# include <linux/dma-mapping.h>
2005-12-06 15:55:26 +03:00
# include <linux/delay.h>
2011-07-15 21:13:37 +04:00
# include <linux/module.h>
2006-01-16 18:34:20 +03:00
# include <linux/mutex.h>
2005-12-06 15:55:26 +03:00
# include <sound/core.h>
# include <sound/initval.h>
# include <sound/info.h>
# include <sound/control.h>
# include <sound/pcm.h>
# include <sound/pcm_params.h>
# include "pcxhr.h"
# include "pcxhr_mixer.h"
# include "pcxhr_hwdep.h"
# include "pcxhr_core.h"
2008-11-25 14:24:54 +03:00
# include "pcxhr_mix22.h"
2005-12-06 15:55:26 +03:00
# define DRIVER_NAME "pcxhr"
2008-11-25 14:24:54 +03:00
MODULE_AUTHOR ( " Markus Bollinger <bollinger@digigram.com>, "
" Marc Titinger <titinger@digigram.com> " ) ;
2005-12-06 15:55:26 +03:00
MODULE_DESCRIPTION ( " Digigram " DRIVER_NAME " " PCXHR_DRIVER_VERSION_STRING ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_SUPPORTED_DEVICE ( " {{Digigram, " DRIVER_NAME " }} " ) ;
2008-11-25 14:24:54 +03:00
static int index [ SNDRV_CARDS ] = SNDRV_DEFAULT_IDX ; /* Index 0-MAX */
static char * id [ SNDRV_CARDS ] = SNDRV_DEFAULT_STR ; /* ID for this card */
2011-12-15 07:19:36 +04:00
static bool enable [ SNDRV_CARDS ] = SNDRV_DEFAULT_ENABLE_PNP ; /* Enable this card */
static bool mono [ SNDRV_CARDS ] ; /* capture mono only */
2005-12-06 15:55:26 +03:00
module_param_array ( index , int , NULL , 0444 ) ;
MODULE_PARM_DESC ( index , " Index value for Digigram " DRIVER_NAME " soundcard " ) ;
module_param_array ( id , charp , NULL , 0444 ) ;
MODULE_PARM_DESC ( id , " ID string for Digigram " DRIVER_NAME " soundcard " ) ;
module_param_array ( enable , bool , NULL , 0444 ) ;
MODULE_PARM_DESC ( enable , " Enable Digigram " DRIVER_NAME " soundcard " ) ;
module_param_array ( mono , bool , NULL , 0444 ) ;
MODULE_PARM_DESC ( mono , " Mono capture mode (default is stereo) " ) ;
enum {
PCI_ID_VX882HR ,
PCI_ID_PCX882HR ,
PCI_ID_VX881HR ,
PCI_ID_PCX881HR ,
2008-11-25 14:24:54 +03:00
PCI_ID_VX882E ,
PCI_ID_PCX882E ,
PCI_ID_VX881E ,
PCI_ID_PCX881E ,
PCI_ID_VX1222HR ,
2005-12-06 15:55:26 +03:00
PCI_ID_PCX1222HR ,
2008-11-25 14:24:54 +03:00
PCI_ID_VX1221HR ,
2005-12-06 15:55:26 +03:00
PCI_ID_PCX1221HR ,
2008-11-25 14:24:54 +03:00
PCI_ID_VX1222E ,
PCI_ID_PCX1222E ,
PCI_ID_VX1221E ,
PCI_ID_PCX1221E ,
PCI_ID_VX222HR ,
PCI_ID_VX222E ,
PCI_ID_PCX22HR ,
PCI_ID_PCX22E ,
PCI_ID_VX222HRMIC ,
PCI_ID_VX222E_MIC ,
PCI_ID_PCX924HR ,
PCI_ID_PCX924E ,
PCI_ID_PCX924HRMIC ,
PCI_ID_PCX924E_MIC ,
2012-08-24 16:54:57 +04:00
PCI_ID_VX442HR ,
PCI_ID_PCX442HR ,
PCI_ID_VX442E ,
PCI_ID_PCX442E ,
PCI_ID_VX822HR ,
PCI_ID_PCX822HR ,
PCI_ID_VX822E ,
PCI_ID_PCX822E ,
2005-12-06 15:55:26 +03:00
PCI_ID_LAST
} ;
2014-08-08 17:56:03 +04:00
static const struct pci_device_id pcxhr_ids [ ] = {
2008-11-25 14:24:54 +03:00
{ 0x10b5 , 0x9656 , 0x1369 , 0xb001 , 0 , 0 , PCI_ID_VX882HR , } ,
{ 0x10b5 , 0x9656 , 0x1369 , 0xb101 , 0 , 0 , PCI_ID_PCX882HR , } ,
{ 0x10b5 , 0x9656 , 0x1369 , 0xb201 , 0 , 0 , PCI_ID_VX881HR , } ,
{ 0x10b5 , 0x9656 , 0x1369 , 0xb301 , 0 , 0 , PCI_ID_PCX881HR , } ,
{ 0x10b5 , 0x9056 , 0x1369 , 0xb021 , 0 , 0 , PCI_ID_VX882E , } ,
{ 0x10b5 , 0x9056 , 0x1369 , 0xb121 , 0 , 0 , PCI_ID_PCX882E , } ,
{ 0x10b5 , 0x9056 , 0x1369 , 0xb221 , 0 , 0 , PCI_ID_VX881E , } ,
{ 0x10b5 , 0x9056 , 0x1369 , 0xb321 , 0 , 0 , PCI_ID_PCX881E , } ,
{ 0x10b5 , 0x9656 , 0x1369 , 0xb401 , 0 , 0 , PCI_ID_VX1222HR , } ,
{ 0x10b5 , 0x9656 , 0x1369 , 0xb501 , 0 , 0 , PCI_ID_PCX1222HR , } ,
{ 0x10b5 , 0x9656 , 0x1369 , 0xb601 , 0 , 0 , PCI_ID_VX1221HR , } ,
{ 0x10b5 , 0x9656 , 0x1369 , 0xb701 , 0 , 0 , PCI_ID_PCX1221HR , } ,
{ 0x10b5 , 0x9056 , 0x1369 , 0xb421 , 0 , 0 , PCI_ID_VX1222E , } ,
{ 0x10b5 , 0x9056 , 0x1369 , 0xb521 , 0 , 0 , PCI_ID_PCX1222E , } ,
{ 0x10b5 , 0x9056 , 0x1369 , 0xb621 , 0 , 0 , PCI_ID_VX1221E , } ,
{ 0x10b5 , 0x9056 , 0x1369 , 0xb721 , 0 , 0 , PCI_ID_PCX1221E , } ,
{ 0x10b5 , 0x9056 , 0x1369 , 0xba01 , 0 , 0 , PCI_ID_VX222HR , } ,
{ 0x10b5 , 0x9056 , 0x1369 , 0xba21 , 0 , 0 , PCI_ID_VX222E , } ,
{ 0x10b5 , 0x9056 , 0x1369 , 0xbd01 , 0 , 0 , PCI_ID_PCX22HR , } ,
{ 0x10b5 , 0x9056 , 0x1369 , 0xbd21 , 0 , 0 , PCI_ID_PCX22E , } ,
{ 0x10b5 , 0x9056 , 0x1369 , 0xbc01 , 0 , 0 , PCI_ID_VX222HRMIC , } ,
{ 0x10b5 , 0x9056 , 0x1369 , 0xbc21 , 0 , 0 , PCI_ID_VX222E_MIC , } ,
{ 0x10b5 , 0x9056 , 0x1369 , 0xbb01 , 0 , 0 , PCI_ID_PCX924HR , } ,
{ 0x10b5 , 0x9056 , 0x1369 , 0xbb21 , 0 , 0 , PCI_ID_PCX924E , } ,
{ 0x10b5 , 0x9056 , 0x1369 , 0xbf01 , 0 , 0 , PCI_ID_PCX924HRMIC , } ,
{ 0x10b5 , 0x9056 , 0x1369 , 0xbf21 , 0 , 0 , PCI_ID_PCX924E_MIC , } ,
2012-08-24 16:54:57 +04:00
{ 0x10b5 , 0x9656 , 0x1369 , 0xd001 , 0 , 0 , PCI_ID_VX442HR , } ,
{ 0x10b5 , 0x9656 , 0x1369 , 0xd101 , 0 , 0 , PCI_ID_PCX442HR , } ,
{ 0x10b5 , 0x9056 , 0x1369 , 0xd021 , 0 , 0 , PCI_ID_VX442E , } ,
{ 0x10b5 , 0x9056 , 0x1369 , 0xd121 , 0 , 0 , PCI_ID_PCX442E , } ,
{ 0x10b5 , 0x9656 , 0x1369 , 0xd201 , 0 , 0 , PCI_ID_VX822HR , } ,
{ 0x10b5 , 0x9656 , 0x1369 , 0xd301 , 0 , 0 , PCI_ID_PCX822HR , } ,
{ 0x10b5 , 0x9056 , 0x1369 , 0xd221 , 0 , 0 , PCI_ID_VX822E , } ,
{ 0x10b5 , 0x9056 , 0x1369 , 0xd321 , 0 , 0 , PCI_ID_PCX822E , } ,
2005-12-06 15:55:26 +03:00
{ 0 , }
} ;
MODULE_DEVICE_TABLE ( pci , pcxhr_ids ) ;
struct board_parameters {
char * board_name ;
short playback_chips ;
short capture_chips ;
2008-11-25 14:24:54 +03:00
short fw_file_set ;
2005-12-06 15:55:26 +03:00
short firmware_num ;
} ;
2020-01-05 17:47:38 +03:00
static const struct board_parameters pcxhr_board_params [ ] = {
2008-11-25 14:24:54 +03:00
[ PCI_ID_VX882HR ] = { " VX882HR " , 4 , 4 , 0 , 41 } ,
[ PCI_ID_PCX882HR ] = { " PCX882HR " , 4 , 4 , 0 , 41 } ,
[ PCI_ID_VX881HR ] = { " VX881HR " , 4 , 4 , 0 , 41 } ,
[ PCI_ID_PCX881HR ] = { " PCX881HR " , 4 , 4 , 0 , 41 } ,
[ PCI_ID_VX882E ] = { " VX882e " , 4 , 4 , 1 , 41 } ,
[ PCI_ID_PCX882E ] = { " PCX882e " , 4 , 4 , 1 , 41 } ,
[ PCI_ID_VX881E ] = { " VX881e " , 4 , 4 , 1 , 41 } ,
[ PCI_ID_PCX881E ] = { " PCX881e " , 4 , 4 , 1 , 41 } ,
[ PCI_ID_VX1222HR ] = { " VX1222HR " , 6 , 1 , 2 , 42 } ,
[ PCI_ID_PCX1222HR ] = { " PCX1222HR " , 6 , 1 , 2 , 42 } ,
[ PCI_ID_VX1221HR ] = { " VX1221HR " , 6 , 1 , 2 , 42 } ,
[ PCI_ID_PCX1221HR ] = { " PCX1221HR " , 6 , 1 , 2 , 42 } ,
[ PCI_ID_VX1222E ] = { " VX1222e " , 6 , 1 , 3 , 42 } ,
[ PCI_ID_PCX1222E ] = { " PCX1222e " , 6 , 1 , 3 , 42 } ,
[ PCI_ID_VX1221E ] = { " VX1221e " , 6 , 1 , 3 , 42 } ,
[ PCI_ID_PCX1221E ] = { " PCX1221e " , 6 , 1 , 3 , 42 } ,
[ PCI_ID_VX222HR ] = { " VX222HR " , 1 , 1 , 4 , 44 } ,
[ PCI_ID_VX222E ] = { " VX222e " , 1 , 1 , 4 , 44 } ,
[ PCI_ID_PCX22HR ] = { " PCX22HR " , 1 , 0 , 4 , 44 } ,
[ PCI_ID_PCX22E ] = { " PCX22e " , 1 , 0 , 4 , 44 } ,
[ PCI_ID_VX222HRMIC ] = { " VX222HR-Mic " , 1 , 1 , 5 , 44 } ,
[ PCI_ID_VX222E_MIC ] = { " VX222e-Mic " , 1 , 1 , 5 , 44 } ,
[ PCI_ID_PCX924HR ] = { " PCX924HR " , 1 , 1 , 5 , 44 } ,
[ PCI_ID_PCX924E ] = { " PCX924e " , 1 , 1 , 5 , 44 } ,
[ PCI_ID_PCX924HRMIC ] = { " PCX924HR-Mic " , 1 , 1 , 5 , 44 } ,
[ PCI_ID_PCX924E_MIC ] = { " PCX924e-Mic " , 1 , 1 , 5 , 44 } ,
2012-08-24 16:54:57 +04:00
[ PCI_ID_VX442HR ] = { " VX442HR " , 2 , 2 , 0 , 41 } ,
[ PCI_ID_PCX442HR ] = { " PCX442HR " , 2 , 2 , 0 , 41 } ,
[ PCI_ID_VX442E ] = { " VX442e " , 2 , 2 , 1 , 41 } ,
[ PCI_ID_PCX442E ] = { " PCX442e " , 2 , 2 , 1 , 41 } ,
[ PCI_ID_VX822HR ] = { " VX822HR " , 4 , 1 , 2 , 42 } ,
[ PCI_ID_PCX822HR ] = { " PCX822HR " , 4 , 1 , 2 , 42 } ,
[ PCI_ID_VX822E ] = { " VX822e " , 4 , 1 , 3 , 42 } ,
[ PCI_ID_PCX822E ] = { " PCX822e " , 4 , 1 , 3 , 42 } ,
2005-12-06 15:55:26 +03:00
} ;
2008-11-25 14:24:54 +03:00
/* boards without hw AES1 and SRC onboard are all using fw_file_set==4 */
/* VX222HR, VX222e, PCX22HR and PCX22e */
# define PCXHR_BOARD_HAS_AES1(x) (x->fw_file_set != 4)
/* some boards do not support 192kHz on digital AES input plugs */
# define PCXHR_BOARD_AESIN_NO_192K(x) ((x->capture_chips == 0) || \
( x - > fw_file_set = = 0 ) | | \
( x - > fw_file_set = = 2 ) )
2005-12-06 15:55:26 +03:00
static int pcxhr_pll_freq_register ( unsigned int freq , unsigned int * pllreg ,
unsigned int * realfreq )
{
unsigned int reg ;
2008-11-25 14:24:54 +03:00
if ( freq < 6900 | | freq > 110000 )
2005-12-06 15:55:26 +03:00
return - EINVAL ;
2008-11-25 14:24:54 +03:00
reg = ( 28224000 * 2 ) / freq ;
reg = ( reg - 1 ) / 2 ;
2005-12-06 15:55:26 +03:00
if ( reg < 0x200 )
* pllreg = reg + 0x800 ;
else if ( reg < 0x400 )
* pllreg = reg & 0x1ff ;
else if ( reg < 0x800 ) {
* pllreg = ( ( reg > > 1 ) & 0x1ff ) + 0x200 ;
reg & = ~ 1 ;
} else {
* pllreg = ( ( reg > > 2 ) & 0x1ff ) + 0x400 ;
reg & = ~ 3 ;
}
if ( realfreq )
2008-11-25 14:24:54 +03:00
* realfreq = ( 28224000 / ( reg + 1 ) ) ;
2005-12-06 15:55:26 +03:00
return 0 ;
}
# define PCXHR_FREQ_REG_MASK 0x1f
# define PCXHR_FREQ_QUARTZ_48000 0x00
# define PCXHR_FREQ_QUARTZ_24000 0x01
# define PCXHR_FREQ_QUARTZ_12000 0x09
# define PCXHR_FREQ_QUARTZ_32000 0x08
# define PCXHR_FREQ_QUARTZ_16000 0x04
# define PCXHR_FREQ_QUARTZ_8000 0x0c
# define PCXHR_FREQ_QUARTZ_44100 0x02
# define PCXHR_FREQ_QUARTZ_22050 0x0a
# define PCXHR_FREQ_QUARTZ_11025 0x06
# define PCXHR_FREQ_PLL 0x05
# define PCXHR_FREQ_QUARTZ_192000 0x10
# define PCXHR_FREQ_QUARTZ_96000 0x18
# define PCXHR_FREQ_QUARTZ_176400 0x14
# define PCXHR_FREQ_QUARTZ_88200 0x1c
# define PCXHR_FREQ_QUARTZ_128000 0x12
# define PCXHR_FREQ_QUARTZ_64000 0x1a
# define PCXHR_FREQ_WORD_CLOCK 0x0f
# define PCXHR_FREQ_SYNC_AES 0x0e
# define PCXHR_FREQ_AES_1 0x07
# define PCXHR_FREQ_AES_2 0x0b
# define PCXHR_FREQ_AES_3 0x03
# define PCXHR_FREQ_AES_4 0x0d
static int pcxhr_get_clock_reg ( struct pcxhr_mgr * mgr , unsigned int rate ,
unsigned int * reg , unsigned int * freq )
{
unsigned int val , realfreq , pllreg ;
struct pcxhr_rmh rmh ;
int err ;
realfreq = rate ;
switch ( mgr - > use_clock_type ) {
case PCXHR_CLOCK_TYPE_INTERNAL : /* clock by quartz or pll */
switch ( rate ) {
case 48000 : val = PCXHR_FREQ_QUARTZ_48000 ; break ;
case 24000 : val = PCXHR_FREQ_QUARTZ_24000 ; break ;
case 12000 : val = PCXHR_FREQ_QUARTZ_12000 ; break ;
case 32000 : val = PCXHR_FREQ_QUARTZ_32000 ; break ;
case 16000 : val = PCXHR_FREQ_QUARTZ_16000 ; break ;
case 8000 : val = PCXHR_FREQ_QUARTZ_8000 ; break ;
case 44100 : val = PCXHR_FREQ_QUARTZ_44100 ; break ;
case 22050 : val = PCXHR_FREQ_QUARTZ_22050 ; break ;
case 11025 : val = PCXHR_FREQ_QUARTZ_11025 ; break ;
case 192000 : val = PCXHR_FREQ_QUARTZ_192000 ; break ;
case 96000 : val = PCXHR_FREQ_QUARTZ_96000 ; break ;
case 176400 : val = PCXHR_FREQ_QUARTZ_176400 ; break ;
case 88200 : val = PCXHR_FREQ_QUARTZ_88200 ; break ;
case 128000 : val = PCXHR_FREQ_QUARTZ_128000 ; break ;
case 64000 : val = PCXHR_FREQ_QUARTZ_64000 ; break ;
default :
val = PCXHR_FREQ_PLL ;
/* get the value for the pll register */
err = pcxhr_pll_freq_register ( rate , & pllreg , & realfreq ) ;
if ( err )
return err ;
pcxhr_init_rmh ( & rmh , CMD_ACCESS_IO_WRITE ) ;
rmh . cmd [ 0 ] | = IO_NUM_REG_GENCLK ;
rmh . cmd [ 1 ] = pllreg & MASK_DSP_WORD ;
rmh . cmd [ 2 ] = pllreg > > 24 ;
rmh . cmd_len = 3 ;
err = pcxhr_send_msg ( mgr , & rmh ) ;
if ( err < 0 ) {
2014-02-26 14:47:55 +04:00
dev_err ( & mgr - > pci - > dev ,
2008-11-25 14:24:54 +03:00
" error CMD_ACCESS_IO_WRITE "
" for PLL register : %x! \n " , err ) ;
2005-12-06 15:55:26 +03:00
return err ;
}
}
break ;
2008-11-25 14:24:54 +03:00
case PCXHR_CLOCK_TYPE_WORD_CLOCK :
val = PCXHR_FREQ_WORD_CLOCK ;
break ;
case PCXHR_CLOCK_TYPE_AES_SYNC :
val = PCXHR_FREQ_SYNC_AES ;
break ;
case PCXHR_CLOCK_TYPE_AES_1 :
val = PCXHR_FREQ_AES_1 ;
break ;
case PCXHR_CLOCK_TYPE_AES_2 :
val = PCXHR_FREQ_AES_2 ;
break ;
case PCXHR_CLOCK_TYPE_AES_3 :
val = PCXHR_FREQ_AES_3 ;
break ;
case PCXHR_CLOCK_TYPE_AES_4 :
val = PCXHR_FREQ_AES_4 ;
break ;
default :
return - EINVAL ;
2005-12-06 15:55:26 +03:00
}
* reg = val ;
* freq = realfreq ;
return 0 ;
}
2008-11-25 14:24:54 +03:00
static int pcxhr_sub_set_clock ( struct pcxhr_mgr * mgr ,
unsigned int rate ,
int * changed )
2005-12-06 15:55:26 +03:00
{
unsigned int val , realfreq , speed ;
struct pcxhr_rmh rmh ;
2008-11-25 14:24:54 +03:00
int err ;
2005-12-06 15:55:26 +03:00
err = pcxhr_get_clock_reg ( mgr , rate , & val , & realfreq ) ;
if ( err )
return err ;
/* codec speed modes */
if ( rate < 55000 )
speed = 0 ; /* single speed */
else if ( rate < 100000 )
speed = 1 ; /* dual speed */
else
speed = 2 ; /* quad speed */
if ( mgr - > codec_speed ! = speed ) {
2008-11-25 14:24:54 +03:00
pcxhr_init_rmh ( & rmh , CMD_ACCESS_IO_WRITE ) ; /* mute outputs */
2005-12-06 15:55:26 +03:00
rmh . cmd [ 0 ] | = IO_NUM_REG_MUTE_OUT ;
2008-11-25 14:24:54 +03:00
if ( DSP_EXT_CMD_SET ( mgr ) ) {
rmh . cmd [ 1 ] = 1 ;
rmh . cmd_len = 2 ;
}
2005-12-06 15:55:26 +03:00
err = pcxhr_send_msg ( mgr , & rmh ) ;
if ( err )
return err ;
2008-11-25 14:24:54 +03:00
pcxhr_init_rmh ( & rmh , CMD_ACCESS_IO_WRITE ) ; /* set speed ratio */
2005-12-06 15:55:26 +03:00
rmh . cmd [ 0 ] | = IO_NUM_SPEED_RATIO ;
rmh . cmd [ 1 ] = speed ;
rmh . cmd_len = 2 ;
err = pcxhr_send_msg ( mgr , & rmh ) ;
if ( err )
return err ;
}
/* set the new frequency */
2014-02-26 14:47:55 +04:00
dev_dbg ( & mgr - > pci - > dev , " clock register : set %x \n " , val ) ;
2008-11-25 14:24:54 +03:00
err = pcxhr_write_io_num_reg_cont ( mgr , PCXHR_FREQ_REG_MASK ,
val , changed ) ;
2005-12-06 15:55:26 +03:00
if ( err )
return err ;
2008-11-25 14:24:54 +03:00
2005-12-06 15:55:26 +03:00
mgr - > sample_rate_real = realfreq ;
mgr - > cur_clock_type = mgr - > use_clock_type ;
/* unmute after codec speed modes */
if ( mgr - > codec_speed ! = speed ) {
2008-11-25 14:24:54 +03:00
pcxhr_init_rmh ( & rmh , CMD_ACCESS_IO_READ ) ; /* unmute outputs */
2005-12-06 15:55:26 +03:00
rmh . cmd [ 0 ] | = IO_NUM_REG_MUTE_OUT ;
2008-11-25 14:24:54 +03:00
if ( DSP_EXT_CMD_SET ( mgr ) ) {
rmh . cmd [ 1 ] = 1 ;
rmh . cmd_len = 2 ;
}
2005-12-06 15:55:26 +03:00
err = pcxhr_send_msg ( mgr , & rmh ) ;
if ( err )
return err ;
2008-11-25 14:24:54 +03:00
mgr - > codec_speed = speed ; /* save new codec speed */
2005-12-06 15:55:26 +03:00
}
2014-02-26 14:47:55 +04:00
dev_dbg ( & mgr - > pci - > dev , " pcxhr_sub_set_clock to %dHz (realfreq=%d) \n " ,
2008-11-25 14:24:54 +03:00
rate , realfreq ) ;
return 0 ;
}
# define PCXHR_MODIFY_CLOCK_S_BIT 0x04
# define PCXHR_IRQ_TIMER_FREQ 92000
# define PCXHR_IRQ_TIMER_PERIOD 48
int pcxhr_set_clock ( struct pcxhr_mgr * mgr , unsigned int rate )
{
struct pcxhr_rmh rmh ;
int err , changed ;
if ( rate = = 0 )
return 0 ; /* nothing to do */
if ( mgr - > is_hr_stereo )
err = hr222_sub_set_clock ( mgr , rate , & changed ) ;
else
err = pcxhr_sub_set_clock ( mgr , rate , & changed ) ;
if ( err )
return err ;
2005-12-06 15:55:26 +03:00
if ( changed ) {
pcxhr_init_rmh ( & rmh , CMD_MODIFY_CLOCK ) ;
2008-11-25 14:24:54 +03:00
rmh . cmd [ 0 ] | = PCXHR_MODIFY_CLOCK_S_BIT ; /* resync fifos */
2005-12-06 15:55:26 +03:00
if ( rate < PCXHR_IRQ_TIMER_FREQ )
rmh . cmd [ 1 ] = PCXHR_IRQ_TIMER_PERIOD ;
else
rmh . cmd [ 1 ] = PCXHR_IRQ_TIMER_PERIOD * 2 ;
rmh . cmd [ 2 ] = rate ;
rmh . cmd_len = 3 ;
err = pcxhr_send_msg ( mgr , & rmh ) ;
if ( err )
return err ;
}
return 0 ;
}
2008-11-25 14:24:54 +03:00
static int pcxhr_sub_get_external_clock ( struct pcxhr_mgr * mgr ,
enum pcxhr_clock_type clock_type ,
int * sample_rate )
2005-12-06 15:55:26 +03:00
{
struct pcxhr_rmh rmh ;
unsigned char reg ;
int err , rate ;
switch ( clock_type ) {
2008-11-25 14:24:54 +03:00
case PCXHR_CLOCK_TYPE_WORD_CLOCK :
reg = REG_STATUS_WORD_CLOCK ;
break ;
case PCXHR_CLOCK_TYPE_AES_SYNC :
reg = REG_STATUS_AES_SYNC ;
break ;
case PCXHR_CLOCK_TYPE_AES_1 :
reg = REG_STATUS_AES_1 ;
break ;
case PCXHR_CLOCK_TYPE_AES_2 :
reg = REG_STATUS_AES_2 ;
break ;
case PCXHR_CLOCK_TYPE_AES_3 :
reg = REG_STATUS_AES_3 ;
break ;
case PCXHR_CLOCK_TYPE_AES_4 :
reg = REG_STATUS_AES_4 ;
break ;
default :
return - EINVAL ;
2005-12-06 15:55:26 +03:00
}
pcxhr_init_rmh ( & rmh , CMD_ACCESS_IO_READ ) ;
rmh . cmd_len = 2 ;
rmh . cmd [ 0 ] | = IO_NUM_REG_STATUS ;
if ( mgr - > last_reg_stat ! = reg ) {
rmh . cmd [ 1 ] = reg ;
err = pcxhr_send_msg ( mgr , & rmh ) ;
if ( err )
return err ;
2008-11-25 14:24:54 +03:00
udelay ( 100 ) ; /* wait minimum 2 sample_frames at 32kHz ! */
2005-12-06 15:55:26 +03:00
mgr - > last_reg_stat = reg ;
}
rmh . cmd [ 1 ] = REG_STATUS_CURRENT ;
err = pcxhr_send_msg ( mgr , & rmh ) ;
if ( err )
return err ;
switch ( rmh . stat [ 1 ] & 0x0f ) {
case REG_STATUS_SYNC_32000 : rate = 32000 ; break ;
case REG_STATUS_SYNC_44100 : rate = 44100 ; break ;
case REG_STATUS_SYNC_48000 : rate = 48000 ; break ;
case REG_STATUS_SYNC_64000 : rate = 64000 ; break ;
case REG_STATUS_SYNC_88200 : rate = 88200 ; break ;
case REG_STATUS_SYNC_96000 : rate = 96000 ; break ;
case REG_STATUS_SYNC_128000 : rate = 128000 ; break ;
case REG_STATUS_SYNC_176400 : rate = 176400 ; break ;
case REG_STATUS_SYNC_192000 : rate = 192000 ; break ;
default : rate = 0 ;
}
2014-02-26 14:47:55 +04:00
dev_dbg ( & mgr - > pci - > dev , " External clock is at %d Hz \n " , rate ) ;
2005-12-06 15:55:26 +03:00
* sample_rate = rate ;
return 0 ;
}
2008-11-25 14:24:54 +03:00
int pcxhr_get_external_clock ( struct pcxhr_mgr * mgr ,
enum pcxhr_clock_type clock_type ,
int * sample_rate )
{
if ( mgr - > is_hr_stereo )
return hr222_get_external_clock ( mgr , clock_type ,
sample_rate ) ;
else
return pcxhr_sub_get_external_clock ( mgr , clock_type ,
sample_rate ) ;
}
2005-12-06 15:55:26 +03:00
/*
* start or stop playback / capture substream
*/
2014-10-28 19:22:45 +03:00
static int pcxhr_set_stream_state ( struct snd_pcxhr * chip ,
struct pcxhr_stream * stream )
2005-12-06 15:55:26 +03:00
{
int err ;
struct pcxhr_rmh rmh ;
int stream_mask , start ;
if ( stream - > status = = PCXHR_STREAM_STATUS_SCHEDULE_RUN )
start = 1 ;
else {
if ( stream - > status ! = PCXHR_STREAM_STATUS_SCHEDULE_STOP ) {
2014-10-28 19:22:45 +03:00
dev_err ( chip - > card - > dev ,
" pcxhr_set_stream_state CANNOT be stopped \n " ) ;
2005-12-06 15:55:26 +03:00
return - EINVAL ;
}
start = 0 ;
}
if ( ! stream - > substream )
return - EINVAL ;
stream - > timer_abs_periods = 0 ;
2008-11-25 14:24:54 +03:00
stream - > timer_period_frag = 0 ; /* reset theoretical stream pos */
2005-12-06 15:55:26 +03:00
stream - > timer_buf_periods = 0 ;
stream - > timer_is_synced = 0 ;
2008-11-25 14:24:54 +03:00
stream_mask =
stream - > pipe - > is_capture ? 1 : 1 < < stream - > substream - > number ;
2005-12-06 15:55:26 +03:00
pcxhr_init_rmh ( & rmh , start ? CMD_START_STREAM : CMD_STOP_STREAM ) ;
pcxhr_set_pipe_cmd_params ( & rmh , stream - > pipe - > is_capture ,
stream - > pipe - > first_audio , 0 , stream_mask ) ;
chip = snd_pcm_substream_chip ( stream - > substream ) ;
err = pcxhr_send_msg ( chip - > mgr , & rmh ) ;
if ( err )
2014-02-26 14:47:55 +04:00
dev_err ( chip - > card - > dev ,
" ERROR pcxhr_set_stream_state err=%x; \n " , err ) ;
2008-11-25 14:24:54 +03:00
stream - > status =
start ? PCXHR_STREAM_STATUS_STARTED : PCXHR_STREAM_STATUS_STOPPED ;
2005-12-06 15:55:26 +03:00
return err ;
}
# define HEADER_FMT_BASE_LIN 0xfed00000
# define HEADER_FMT_BASE_FLOAT 0xfad00000
# define HEADER_FMT_INTEL 0x00008000
# define HEADER_FMT_24BITS 0x00004000
# define HEADER_FMT_16BITS 0x00002000
# define HEADER_FMT_UPTO11 0x00000200
# define HEADER_FMT_UPTO32 0x00000100
# define HEADER_FMT_MONO 0x00000080
static int pcxhr_set_format ( struct pcxhr_stream * stream )
{
int err , is_capture , sample_rate , stream_num ;
struct snd_pcxhr * chip ;
struct pcxhr_rmh rmh ;
unsigned int header ;
2014-10-28 19:22:45 +03:00
chip = snd_pcm_substream_chip ( stream - > substream ) ;
2005-12-06 15:55:26 +03:00
switch ( stream - > format ) {
case SNDRV_PCM_FORMAT_U8 :
header = HEADER_FMT_BASE_LIN ;
break ;
case SNDRV_PCM_FORMAT_S16_LE :
2008-11-25 14:24:54 +03:00
header = HEADER_FMT_BASE_LIN |
HEADER_FMT_16BITS | HEADER_FMT_INTEL ;
2005-12-06 15:55:26 +03:00
break ;
case SNDRV_PCM_FORMAT_S16_BE :
header = HEADER_FMT_BASE_LIN | HEADER_FMT_16BITS ;
break ;
case SNDRV_PCM_FORMAT_S24_3LE :
2008-11-25 14:24:54 +03:00
header = HEADER_FMT_BASE_LIN |
HEADER_FMT_24BITS | HEADER_FMT_INTEL ;
2005-12-06 15:55:26 +03:00
break ;
case SNDRV_PCM_FORMAT_S24_3BE :
header = HEADER_FMT_BASE_LIN | HEADER_FMT_24BITS ;
break ;
case SNDRV_PCM_FORMAT_FLOAT_LE :
header = HEADER_FMT_BASE_FLOAT | HEADER_FMT_INTEL ;
break ;
default :
2014-10-28 19:22:45 +03:00
dev_err ( chip - > card - > dev ,
" error pcxhr_set_format() : unknown format \n " ) ;
2005-12-06 15:55:26 +03:00
return - EINVAL ;
}
sample_rate = chip - > mgr - > sample_rate ;
if ( sample_rate < = 32000 & & sample_rate ! = 0 ) {
if ( sample_rate < = 11025 )
header | = HEADER_FMT_UPTO11 ;
else
header | = HEADER_FMT_UPTO32 ;
}
if ( stream - > channels = = 1 )
header | = HEADER_FMT_MONO ;
is_capture = stream - > pipe - > is_capture ;
stream_num = is_capture ? 0 : stream - > substream - > number ;
2008-11-25 14:24:54 +03:00
pcxhr_init_rmh ( & rmh , is_capture ?
CMD_FORMAT_STREAM_IN : CMD_FORMAT_STREAM_OUT ) ;
pcxhr_set_pipe_cmd_params ( & rmh , is_capture , stream - > pipe - > first_audio ,
stream_num , 0 ) ;
if ( is_capture ) {
/* bug with old dsp versions: */
/* bit 12 also sets the format of the playback stream */
if ( DSP_EXT_CMD_SET ( chip - > mgr ) )
rmh . cmd [ 0 ] | = 1 < < 10 ;
else
rmh . cmd [ 0 ] | = 1 < < 12 ;
}
2005-12-06 15:55:26 +03:00
rmh . cmd [ 1 ] = 0 ;
2008-11-25 14:24:54 +03:00
rmh . cmd_len = 2 ;
if ( DSP_EXT_CMD_SET ( chip - > mgr ) ) {
/* add channels and set bit 19 if channels>2 */
rmh . cmd [ 1 ] = stream - > channels ;
if ( ! is_capture ) {
/* playback : add channel mask to command */
rmh . cmd [ 2 ] = ( stream - > channels = = 1 ) ? 0x01 : 0x03 ;
rmh . cmd_len = 3 ;
}
}
rmh . cmd [ rmh . cmd_len + + ] = header > > 8 ;
rmh . cmd [ rmh . cmd_len + + ] = ( header & 0xff ) < < 16 ;
2005-12-06 15:55:26 +03:00
err = pcxhr_send_msg ( chip - > mgr , & rmh ) ;
if ( err )
2014-02-26 14:47:55 +04:00
dev_err ( chip - > card - > dev ,
" ERROR pcxhr_set_format err=%x; \n " , err ) ;
2005-12-06 15:55:26 +03:00
return err ;
}
static int pcxhr_update_r_buffer ( struct pcxhr_stream * stream )
{
int err , is_capture , stream_num ;
struct pcxhr_rmh rmh ;
struct snd_pcm_substream * subs = stream - > substream ;
struct snd_pcxhr * chip = snd_pcm_substream_chip ( subs ) ;
is_capture = ( subs - > stream = = SNDRV_PCM_STREAM_CAPTURE ) ;
stream_num = is_capture ? 0 : subs - > number ;
2014-10-28 19:22:45 +03:00
dev_dbg ( chip - > card - > dev ,
" pcxhr_update_r_buffer(pcm%c%d) : addr(%p) bytes(%zx) subs(%d) \n " ,
is_capture ? ' c ' : ' p ' ,
chip - > chip_idx , ( void * ) ( long ) subs - > runtime - > dma_addr ,
subs - > runtime - > dma_bytes , subs - > number ) ;
2005-12-06 15:55:26 +03:00
pcxhr_init_rmh ( & rmh , CMD_UPDATE_R_BUFFERS ) ;
2008-11-25 14:24:54 +03:00
pcxhr_set_pipe_cmd_params ( & rmh , is_capture , stream - > pipe - > first_audio ,
stream_num , 0 ) ;
2005-12-06 15:55:26 +03:00
2008-08-08 19:12:14 +04:00
/* max buffer size is 2 MByte */
snd_BUG_ON ( subs - > runtime - > dma_bytes > = 0x200000 ) ;
2008-11-25 14:24:54 +03:00
/* size in bits */
rmh . cmd [ 1 ] = subs - > runtime - > dma_bytes * 8 ;
/* most significant byte */
rmh . cmd [ 2 ] = subs - > runtime - > dma_addr > > 24 ;
/* this is a circular buffer */
rmh . cmd [ 2 ] | = 1 < < 19 ;
/* least 3 significant bytes */
rmh . cmd [ 3 ] = subs - > runtime - > dma_addr & MASK_DSP_WORD ;
2005-12-06 15:55:26 +03:00
rmh . cmd_len = 4 ;
err = pcxhr_send_msg ( chip - > mgr , & rmh ) ;
if ( err )
2014-02-26 14:47:55 +04:00
dev_err ( chip - > card - > dev ,
2008-11-25 14:24:54 +03:00
" ERROR CMD_UPDATE_R_BUFFERS err=%x; \n " , err ) ;
2005-12-06 15:55:26 +03:00
return err ;
}
#if 0
2008-11-25 14:24:54 +03:00
static int pcxhr_pipe_sample_count ( struct pcxhr_stream * stream ,
snd_pcm_uframes_t * sample_count )
2005-12-06 15:55:26 +03:00
{
struct pcxhr_rmh rmh ;
int err ;
pcxhr_t * chip = snd_pcm_substream_chip ( stream - > substream ) ;
pcxhr_init_rmh ( & rmh , CMD_PIPE_SAMPLE_COUNT ) ;
pcxhr_set_pipe_cmd_params ( & rmh , stream - > pipe - > is_capture , 0 , 0 ,
1 < < stream - > pipe - > first_audio ) ;
err = pcxhr_send_msg ( chip - > mgr , & rmh ) ;
if ( err = = 0 ) {
* sample_count = ( ( snd_pcm_uframes_t ) rmh . stat [ 0 ] ) < < 24 ;
* sample_count + = ( snd_pcm_uframes_t ) rmh . stat [ 1 ] ;
}
2014-10-28 19:22:45 +03:00
dev_dbg ( chip - > card - > dev , " PIPE_SAMPLE_COUNT = %lx \n " , * sample_count ) ;
2005-12-06 15:55:26 +03:00
return err ;
}
# endif
static inline int pcxhr_stream_scheduled_get_pipe ( struct pcxhr_stream * stream ,
struct pcxhr_pipe * * pipe )
{
if ( stream - > status = = PCXHR_STREAM_STATUS_SCHEDULE_RUN ) {
* pipe = stream - > pipe ;
return 1 ;
}
return 0 ;
}
2014-09-10 14:21:38 +04:00
static void pcxhr_start_linked_stream ( struct pcxhr_mgr * mgr )
2005-12-06 15:55:26 +03:00
{
int i , j , err ;
struct pcxhr_pipe * pipe ;
struct snd_pcxhr * chip ;
int capture_mask = 0 ;
int playback_mask = 0 ;
2008-05-20 14:15:15 +04:00
# ifdef CONFIG_SND_DEBUG_VERBOSE
2014-10-28 15:27:44 +03:00
ktime_t start_time , stop_time , diff_time ;
start_time = ktime_get ( ) ;
2005-12-06 15:55:26 +03:00
# endif
2006-01-16 18:34:20 +03:00
mutex_lock ( & mgr - > setup_mutex ) ;
2005-12-06 15:55:26 +03:00
/* check the pipes concerned and build pipe_array */
for ( i = 0 ; i < mgr - > num_cards ; i + + ) {
chip = mgr - > chip [ i ] ;
for ( j = 0 ; j < chip - > nb_streams_capt ; j + + ) {
if ( pcxhr_stream_scheduled_get_pipe ( & chip - > capture_stream [ j ] , & pipe ) )
capture_mask | = ( 1 < < pipe - > first_audio ) ;
}
for ( j = 0 ; j < chip - > nb_streams_play ; j + + ) {
if ( pcxhr_stream_scheduled_get_pipe ( & chip - > playback_stream [ j ] , & pipe ) ) {
playback_mask | = ( 1 < < pipe - > first_audio ) ;
2008-11-25 14:24:54 +03:00
break ; /* add only once, as all playback
* streams of one chip use the same pipe
2005-12-06 15:55:26 +03:00
*/
}
}
}
if ( capture_mask = = 0 & & playback_mask = = 0 ) {
2006-01-16 18:34:20 +03:00
mutex_unlock ( & mgr - > setup_mutex ) ;
2014-09-10 14:21:38 +04:00
dev_err ( & mgr - > pci - > dev , " pcxhr_start_linked_stream : no pipes \n " ) ;
2005-12-06 15:55:26 +03:00
return ;
}
2014-09-10 14:21:38 +04:00
dev_dbg ( & mgr - > pci - > dev , " pcxhr_start_linked_stream : "
2008-11-25 14:24:54 +03:00
" playback_mask=%x capture_mask=%x \n " ,
2005-12-06 15:55:26 +03:00
playback_mask , capture_mask ) ;
/* synchronous stop of all the pipes concerned */
err = pcxhr_set_pipe_state ( mgr , playback_mask , capture_mask , 0 ) ;
if ( err ) {
2006-01-16 18:34:20 +03:00
mutex_unlock ( & mgr - > setup_mutex ) ;
2014-09-10 14:21:38 +04:00
dev_err ( & mgr - > pci - > dev , " pcxhr_start_linked_stream : "
2008-11-25 14:24:54 +03:00
" error stop pipes (P%x C%x) \n " ,
2005-12-06 15:55:26 +03:00
playback_mask , capture_mask ) ;
return ;
}
2008-11-25 14:24:54 +03:00
/* the dsp lost format and buffer info with the stop pipe */
2005-12-06 15:55:26 +03:00
for ( i = 0 ; i < mgr - > num_cards ; i + + ) {
struct pcxhr_stream * stream ;
chip = mgr - > chip [ i ] ;
for ( j = 0 ; j < chip - > nb_streams_capt ; j + + ) {
stream = & chip - > capture_stream [ j ] ;
if ( pcxhr_stream_scheduled_get_pipe ( stream , & pipe ) ) {
err = pcxhr_set_format ( stream ) ;
err = pcxhr_update_r_buffer ( stream ) ;
}
}
for ( j = 0 ; j < chip - > nb_streams_play ; j + + ) {
stream = & chip - > playback_stream [ j ] ;
if ( pcxhr_stream_scheduled_get_pipe ( stream , & pipe ) ) {
err = pcxhr_set_format ( stream ) ;
err = pcxhr_update_r_buffer ( stream ) ;
}
}
}
/* start all the streams */
for ( i = 0 ; i < mgr - > num_cards ; i + + ) {
struct pcxhr_stream * stream ;
chip = mgr - > chip [ i ] ;
for ( j = 0 ; j < chip - > nb_streams_capt ; j + + ) {
stream = & chip - > capture_stream [ j ] ;
if ( pcxhr_stream_scheduled_get_pipe ( stream , & pipe ) )
2014-10-28 19:22:45 +03:00
err = pcxhr_set_stream_state ( chip , stream ) ;
2005-12-06 15:55:26 +03:00
}
for ( j = 0 ; j < chip - > nb_streams_play ; j + + ) {
stream = & chip - > playback_stream [ j ] ;
if ( pcxhr_stream_scheduled_get_pipe ( stream , & pipe ) )
2014-10-28 19:22:45 +03:00
err = pcxhr_set_stream_state ( chip , stream ) ;
2005-12-06 15:55:26 +03:00
}
}
/* synchronous start of all the pipes concerned */
err = pcxhr_set_pipe_state ( mgr , playback_mask , capture_mask , 1 ) ;
if ( err ) {
2006-01-16 18:34:20 +03:00
mutex_unlock ( & mgr - > setup_mutex ) ;
2014-09-10 14:21:38 +04:00
dev_err ( & mgr - > pci - > dev , " pcxhr_start_linked_stream : "
2008-11-25 14:24:54 +03:00
" error start pipes (P%x C%x) \n " ,
2005-12-06 15:55:26 +03:00
playback_mask , capture_mask ) ;
return ;
}
2008-11-25 14:24:54 +03:00
/* put the streams into the running state now
* ( increment pointer by interrupt )
*/
2014-09-10 14:21:38 +04:00
mutex_lock ( & mgr - > lock ) ;
2005-12-06 15:55:26 +03:00
for ( i = 0 ; i < mgr - > num_cards ; i + + ) {
struct pcxhr_stream * stream ;
chip = mgr - > chip [ i ] ;
for ( j = 0 ; j < chip - > nb_streams_capt ; j + + ) {
stream = & chip - > capture_stream [ j ] ;
if ( stream - > status = = PCXHR_STREAM_STATUS_STARTED )
stream - > status = PCXHR_STREAM_STATUS_RUNNING ;
}
for ( j = 0 ; j < chip - > nb_streams_play ; j + + ) {
stream = & chip - > playback_stream [ j ] ;
if ( stream - > status = = PCXHR_STREAM_STATUS_STARTED ) {
/* playback will already have advanced ! */
2008-11-25 14:24:54 +03:00
stream - > timer_period_frag + = mgr - > granularity ;
2005-12-06 15:55:26 +03:00
stream - > status = PCXHR_STREAM_STATUS_RUNNING ;
}
}
}
2014-09-10 14:21:38 +04:00
mutex_unlock ( & mgr - > lock ) ;
2005-12-06 15:55:26 +03:00
2006-01-16 18:34:20 +03:00
mutex_unlock ( & mgr - > setup_mutex ) ;
2005-12-06 15:55:26 +03:00
2008-05-20 14:15:15 +04:00
# ifdef CONFIG_SND_DEBUG_VERBOSE
2014-10-28 15:27:44 +03:00
stop_time = ktime_get ( ) ;
diff_time = ktime_sub ( stop_time , start_time ) ;
2014-09-10 14:21:38 +04:00
dev_dbg ( & mgr - > pci - > dev , " ***TRIGGER START*** TIME = %ld (err = %x) \n " ,
2014-10-28 15:27:44 +03:00
( long ) ( ktime_to_ns ( diff_time ) ) , err ) ;
2005-12-06 15:55:26 +03:00
# endif
}
/*
* trigger callback
*/
static int pcxhr_trigger ( struct snd_pcm_substream * subs , int cmd )
{
struct pcxhr_stream * stream ;
struct snd_pcm_substream * s ;
2014-10-28 19:22:45 +03:00
struct snd_pcxhr * chip = snd_pcm_substream_chip ( subs ) ;
2005-12-06 15:55:26 +03:00
switch ( cmd ) {
case SNDRV_PCM_TRIGGER_START :
2014-10-28 19:22:45 +03:00
dev_dbg ( chip - > card - > dev , " SNDRV_PCM_TRIGGER_START \n " ) ;
2007-03-28 19:19:29 +04:00
if ( snd_pcm_stream_linked ( subs ) ) {
snd_pcm_group_for_each_entry ( s , subs ) {
2007-07-30 10:14:31 +04:00
if ( snd_pcm_substream_chip ( s ) ! = chip )
continue ;
2007-03-28 19:19:29 +04:00
stream = s - > runtime - > private_data ;
stream - > status =
PCXHR_STREAM_STATUS_SCHEDULE_RUN ;
snd_pcm_trigger_done ( s , subs ) ;
}
2014-09-10 14:21:38 +04:00
pcxhr_start_linked_stream ( chip - > mgr ) ;
2007-03-28 19:19:29 +04:00
} else {
stream = subs - > runtime - > private_data ;
2014-10-28 19:22:45 +03:00
dev_dbg ( chip - > card - > dev , " Only one Substream %c %d \n " ,
2005-12-06 15:55:26 +03:00
stream - > pipe - > is_capture ? ' C ' : ' P ' ,
stream - > pipe - > first_audio ) ;
if ( pcxhr_set_format ( stream ) )
return - EINVAL ;
if ( pcxhr_update_r_buffer ( stream ) )
return - EINVAL ;
2007-09-11 23:28:50 +04:00
stream - > status = PCXHR_STREAM_STATUS_SCHEDULE_RUN ;
2014-10-28 19:22:45 +03:00
if ( pcxhr_set_stream_state ( chip , stream ) )
2005-12-06 15:55:26 +03:00
return - EINVAL ;
stream - > status = PCXHR_STREAM_STATUS_RUNNING ;
}
break ;
case SNDRV_PCM_TRIGGER_STOP :
2014-10-28 19:22:45 +03:00
dev_dbg ( chip - > card - > dev , " SNDRV_PCM_TRIGGER_STOP \n " ) ;
2007-02-22 14:52:53 +03:00
snd_pcm_group_for_each_entry ( s , subs ) {
2005-12-06 15:55:26 +03:00
stream = s - > runtime - > private_data ;
stream - > status = PCXHR_STREAM_STATUS_SCHEDULE_STOP ;
2014-10-28 19:22:45 +03:00
if ( pcxhr_set_stream_state ( chip , stream ) )
2005-12-06 15:55:26 +03:00
return - EINVAL ;
snd_pcm_trigger_done ( s , subs ) ;
}
break ;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH :
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE :
/* TODO */
default :
return - EINVAL ;
}
return 0 ;
}
static int pcxhr_hardware_timer ( struct pcxhr_mgr * mgr , int start )
{
struct pcxhr_rmh rmh ;
int err ;
pcxhr_init_rmh ( & rmh , CMD_SET_TIMER_INTERRUPT ) ;
if ( start ) {
2008-11-25 14:24:54 +03:00
/* last dsp time invalid */
mgr - > dsp_time_last = PCXHR_DSP_TIME_INVALID ;
rmh . cmd [ 0 ] | = mgr - > granularity ;
2005-12-06 15:55:26 +03:00
}
err = pcxhr_send_msg ( mgr , & rmh ) ;
if ( err < 0 )
2014-02-26 14:47:55 +04:00
dev_err ( & mgr - > pci - > dev , " error pcxhr_hardware_timer err(%x) \n " ,
2008-11-25 14:24:54 +03:00
err ) ;
2005-12-06 15:55:26 +03:00
return err ;
}
/*
* prepare callback for all pcms
*/
static int pcxhr_prepare ( struct snd_pcm_substream * subs )
{
struct snd_pcxhr * chip = snd_pcm_substream_chip ( subs ) ;
struct pcxhr_mgr * mgr = chip - > mgr ;
int err = 0 ;
2014-02-26 14:47:55 +04:00
dev_dbg ( chip - > card - > dev ,
" pcxhr_prepare : period_size(%lx) periods(%x) buffer_size(%lx) \n " ,
2005-12-06 15:55:26 +03:00
subs - > runtime - > period_size , subs - > runtime - > periods ,
subs - > runtime - > buffer_size ) ;
2006-01-16 18:34:20 +03:00
mutex_lock ( & mgr - > setup_mutex ) ;
2005-12-06 15:55:26 +03:00
do {
/* only the first stream can choose the sample rate */
/* set the clock only once (first stream) */
2006-01-10 13:14:49 +03:00
if ( mgr - > sample_rate ! = subs - > runtime - > rate ) {
2005-12-06 15:55:26 +03:00
err = pcxhr_set_clock ( mgr , subs - > runtime - > rate ) ;
if ( err )
break ;
2006-01-10 13:14:49 +03:00
if ( mgr - > sample_rate = = 0 )
/* start the DSP-timer */
err = pcxhr_hardware_timer ( mgr , 1 ) ;
2005-12-06 15:55:26 +03:00
mgr - > sample_rate = subs - > runtime - > rate ;
}
} while ( 0 ) ; /* do only once (so we can use break instead of goto) */
2006-01-16 18:34:20 +03:00
mutex_unlock ( & mgr - > setup_mutex ) ;
2005-12-06 15:55:26 +03:00
return err ;
}
/*
* HW_PARAMS callback for all pcms
*/
static int pcxhr_hw_params ( struct snd_pcm_substream * subs ,
struct snd_pcm_hw_params * hw )
{
struct snd_pcxhr * chip = snd_pcm_substream_chip ( subs ) ;
struct pcxhr_mgr * mgr = chip - > mgr ;
struct pcxhr_stream * stream = subs - > runtime - > private_data ;
2006-01-16 18:34:20 +03:00
mutex_lock ( & mgr - > setup_mutex ) ;
2005-12-06 15:55:26 +03:00
2019-12-09 12:49:21 +03:00
/* set up channels */
stream - > channels = params_channels ( hw ) ;
/* set up format for the stream */
stream - > format = params_format ( hw ) ;
2005-12-06 15:55:26 +03:00
2006-01-16 18:34:20 +03:00
mutex_unlock ( & mgr - > setup_mutex ) ;
2005-12-06 15:55:26 +03:00
return 0 ;
}
/*
* CONFIGURATION SPACE for all pcms , mono pcm must update channels_max
*/
2017-08-12 18:31:23 +03:00
static const struct snd_pcm_hardware pcxhr_caps =
2005-12-06 15:55:26 +03:00
{
2008-11-25 14:24:54 +03:00
. info = ( SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_SYNC_START ) ,
. formats = ( SNDRV_PCM_FMTBIT_U8 |
SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S16_BE |
SNDRV_PCM_FMTBIT_S24_3LE |
SNDRV_PCM_FMTBIT_S24_3BE |
SNDRV_PCM_FMTBIT_FLOAT_LE ) ,
. rates = ( SNDRV_PCM_RATE_CONTINUOUS |
SNDRV_PCM_RATE_8000_192000 ) ,
2005-12-06 15:55:26 +03:00
. rate_min = 8000 ,
. rate_max = 192000 ,
. channels_min = 1 ,
. channels_max = 2 ,
. buffer_bytes_max = ( 32 * 1024 ) ,
/* 1 byte == 1 frame U8 mono (PCXHR_GRANULARITY is frames!) */
. period_bytes_min = ( 2 * PCXHR_GRANULARITY ) ,
. period_bytes_max = ( 16 * 1024 ) ,
. periods_min = 2 ,
. periods_max = ( 32 * 1024 / PCXHR_GRANULARITY ) ,
} ;
static int pcxhr_open ( struct snd_pcm_substream * subs )
{
struct snd_pcxhr * chip = snd_pcm_substream_chip ( subs ) ;
struct pcxhr_mgr * mgr = chip - > mgr ;
struct snd_pcm_runtime * runtime = subs - > runtime ;
struct pcxhr_stream * stream ;
2008-11-25 14:24:54 +03:00
int err ;
2005-12-06 15:55:26 +03:00
2006-01-16 18:34:20 +03:00
mutex_lock ( & mgr - > setup_mutex ) ;
2005-12-06 15:55:26 +03:00
/* copy the struct snd_pcm_hardware struct */
runtime - > hw = pcxhr_caps ;
if ( subs - > stream = = SNDRV_PCM_STREAM_PLAYBACK ) {
2014-02-26 14:47:55 +04:00
dev_dbg ( chip - > card - > dev , " pcxhr_open playback chip%d subs%d \n " ,
2005-12-06 15:55:26 +03:00
chip - > chip_idx , subs - > number ) ;
stream = & chip - > playback_stream [ subs - > number ] ;
} else {
2014-02-26 14:47:55 +04:00
dev_dbg ( chip - > card - > dev , " pcxhr_open capture chip%d subs%d \n " ,
2005-12-06 15:55:26 +03:00
chip - > chip_idx , subs - > number ) ;
if ( mgr - > mono_capture )
runtime - > hw . channels_max = 1 ;
else
runtime - > hw . channels_min = 2 ;
stream = & chip - > capture_stream [ subs - > number ] ;
}
if ( stream - > status ! = PCXHR_STREAM_STATUS_FREE ) {
/* streams in use */
2014-02-26 14:47:55 +04:00
dev_err ( chip - > card - > dev , " pcxhr_open chip%d subs%d in use \n " ,
2005-12-06 15:55:26 +03:00
chip - > chip_idx , subs - > number ) ;
2006-01-16 18:34:20 +03:00
mutex_unlock ( & mgr - > setup_mutex ) ;
2005-12-06 15:55:26 +03:00
return - EBUSY ;
}
2008-11-25 14:24:54 +03:00
/* float format support is in some cases buggy on stereo cards */
if ( mgr - > is_hr_stereo )
runtime - > hw . formats & = ~ SNDRV_PCM_FMTBIT_FLOAT_LE ;
/* buffer-size should better be multiple of period-size */
err = snd_pcm_hw_constraint_integer ( runtime ,
SNDRV_PCM_HW_PARAM_PERIODS ) ;
if ( err < 0 ) {
mutex_unlock ( & mgr - > setup_mutex ) ;
return err ;
}
2005-12-06 15:55:26 +03:00
/* if a sample rate is already used or fixed by external clock,
* the stream cannot change
*/
if ( mgr - > sample_rate )
runtime - > hw . rate_min = runtime - > hw . rate_max = mgr - > sample_rate ;
else {
if ( mgr - > use_clock_type ! = PCXHR_CLOCK_TYPE_INTERNAL ) {
int external_rate ;
if ( pcxhr_get_external_clock ( mgr , mgr - > use_clock_type ,
& external_rate ) | |
external_rate = = 0 ) {
/* cannot detect the external clock rate */
2006-01-16 18:34:20 +03:00
mutex_unlock ( & mgr - > setup_mutex ) ;
2005-12-06 15:55:26 +03:00
return - EBUSY ;
}
2008-11-25 14:24:54 +03:00
runtime - > hw . rate_min = external_rate ;
runtime - > hw . rate_max = external_rate ;
2005-12-06 15:55:26 +03:00
}
}
stream - > status = PCXHR_STREAM_STATUS_OPEN ;
stream - > substream = subs ;
stream - > channels = 0 ; /* not configured yet */
runtime - > private_data = stream ;
2008-11-25 14:24:54 +03:00
/* better get a divisor of granularity values (96 or 192) */
snd_pcm_hw_constraint_step ( runtime , 0 ,
SNDRV_PCM_HW_PARAM_BUFFER_SIZE , 32 ) ;
snd_pcm_hw_constraint_step ( runtime , 0 ,
SNDRV_PCM_HW_PARAM_PERIOD_SIZE , 32 ) ;
2007-08-13 19:37:55 +04:00
snd_pcm_set_sync ( subs ) ;
2005-12-06 15:55:26 +03:00
mgr - > ref_count_rate + + ;
2006-01-16 18:34:20 +03:00
mutex_unlock ( & mgr - > setup_mutex ) ;
2005-12-06 15:55:26 +03:00
return 0 ;
}
static int pcxhr_close ( struct snd_pcm_substream * subs )
{
struct snd_pcxhr * chip = snd_pcm_substream_chip ( subs ) ;
struct pcxhr_mgr * mgr = chip - > mgr ;
struct pcxhr_stream * stream = subs - > runtime - > private_data ;
2006-01-16 18:34:20 +03:00
mutex_lock ( & mgr - > setup_mutex ) ;
2005-12-06 15:55:26 +03:00
2014-02-26 14:47:55 +04:00
dev_dbg ( chip - > card - > dev , " pcxhr_close chip%d subs%d \n " ,
2008-11-25 14:24:54 +03:00
chip - > chip_idx , subs - > number ) ;
2005-12-06 15:55:26 +03:00
/* sample rate released */
if ( - - mgr - > ref_count_rate = = 0 ) {
2008-11-25 14:24:54 +03:00
mgr - > sample_rate = 0 ; /* the sample rate is no more locked */
2005-12-06 15:55:26 +03:00
pcxhr_hardware_timer ( mgr , 0 ) ; /* stop the DSP-timer */
}
stream - > status = PCXHR_STREAM_STATUS_FREE ;
stream - > substream = NULL ;
2006-01-16 18:34:20 +03:00
mutex_unlock ( & mgr - > setup_mutex ) ;
2005-12-06 15:55:26 +03:00
return 0 ;
}
static snd_pcm_uframes_t pcxhr_stream_pointer ( struct snd_pcm_substream * subs )
{
u_int32_t timer_period_frag ;
int timer_buf_periods ;
struct snd_pcxhr * chip = snd_pcm_substream_chip ( subs ) ;
struct snd_pcm_runtime * runtime = subs - > runtime ;
struct pcxhr_stream * stream = runtime - > private_data ;
2014-09-10 14:21:38 +04:00
mutex_lock ( & chip - > mgr - > lock ) ;
2005-12-06 15:55:26 +03:00
/* get the period fragment and the nb of periods in the buffer */
timer_period_frag = stream - > timer_period_frag ;
timer_buf_periods = stream - > timer_buf_periods ;
2014-09-10 14:21:38 +04:00
mutex_unlock ( & chip - > mgr - > lock ) ;
2005-12-06 15:55:26 +03:00
return ( snd_pcm_uframes_t ) ( ( timer_buf_periods * runtime - > period_size ) +
timer_period_frag ) ;
}
2016-09-02 01:13:10 +03:00
static const struct snd_pcm_ops pcxhr_ops = {
2005-12-06 15:55:26 +03:00
. open = pcxhr_open ,
. close = pcxhr_close ,
. prepare = pcxhr_prepare ,
. hw_params = pcxhr_hw_params ,
. trigger = pcxhr_trigger ,
. pointer = pcxhr_stream_pointer ,
} ;
/*
*/
int pcxhr_create_pcm ( struct snd_pcxhr * chip )
{
int err ;
struct snd_pcm * pcm ;
char name [ 32 ] ;
2017-07-18 14:48:10 +03:00
snprintf ( name , sizeof ( name ) , " pcxhr %d " , chip - > chip_idx ) ;
2005-12-06 15:55:26 +03:00
if ( ( err = snd_pcm_new ( chip - > card , name , 0 ,
chip - > nb_streams_play ,
chip - > nb_streams_capt , & pcm ) ) < 0 ) {
2014-02-26 14:47:55 +04:00
dev_err ( chip - > card - > dev , " cannot create pcm %s \n " , name ) ;
2005-12-06 15:55:26 +03:00
return err ;
}
pcm - > private_data = chip ;
if ( chip - > nb_streams_play )
snd_pcm_set_ops ( pcm , SNDRV_PCM_STREAM_PLAYBACK , & pcxhr_ops ) ;
if ( chip - > nb_streams_capt )
snd_pcm_set_ops ( pcm , SNDRV_PCM_STREAM_CAPTURE , & pcxhr_ops ) ;
pcm - > info_flags = 0 ;
2014-09-10 14:21:38 +04:00
pcm - > nonatomic = true ;
2005-12-06 15:55:26 +03:00
strcpy ( pcm - > name , name ) ;
2019-12-09 12:49:21 +03:00
snd_pcm_set_managed_buffer_all ( pcm , SNDRV_DMA_TYPE_DEV ,
& chip - > mgr - > pci - > dev ,
32 * 1024 , 32 * 1024 ) ;
2005-12-06 15:55:26 +03:00
chip - > pcm = pcm ;
return 0 ;
}
static int pcxhr_chip_free ( struct snd_pcxhr * chip )
{
kfree ( chip ) ;
return 0 ;
}
static int pcxhr_chip_dev_free ( struct snd_device * device )
{
struct snd_pcxhr * chip = device - > device_data ;
return pcxhr_chip_free ( chip ) ;
}
/*
*/
2012-12-06 21:35:10 +04:00
static int pcxhr_create ( struct pcxhr_mgr * mgr ,
struct snd_card * card , int idx )
2005-12-06 15:55:26 +03:00
{
int err ;
struct snd_pcxhr * chip ;
2020-01-03 11:16:25 +03:00
static const struct snd_device_ops ops = {
2005-12-06 15:55:26 +03:00
. dev_free = pcxhr_chip_dev_free ,
} ;
ALSA: sound/pci/mixart/mixart.c: Add missing snd_card_free
The function snd_mixart_create creates a link between mgr and card that
allows snd_mixart_free to free card as well. But if snd_mixart_create
fails, then the link has not been created and card has to be freed explicitly.
The semantic match that finds the problem is as follows:
(http://www.emn.fr/x-info/coccinelle/)
// <smpl>
@r exists@
local idexpression x;
statement S,S1;
position p1,p2,p3;
expression E,E1;
type T,T1;
expression *ptr != NULL;
@@
(
if ((x@p1 = snd_card_new(...)) == NULL) S
|
x@p1 = snd_card_new(...);
)
... when != snd_card_free(...,(T)x,...)
when != if (...) { <+... snd_card_free(...,(T)x,...) ...+> }
when != true x == NULL || ...
when != x = E
when != E = (T)x
when any
(
if (x == NULL || ...) S1
|
if@p2 (...) {
... when != snd_card_free(...,(T1)x,...)
when != if (...) { <+... snd_card_free(...,(T1)x,...) ...+> }
when != x = E1
when != E1 = (T1)x
(
return \(0\|<+...x...+>\|ptr\);
|
return@p3 ...;
)
}
)
@ script:python @
p1 << r.p1;
p3 << r.p3;
@@
print "* file: %s snd_card_new: %s return: %s" % (p1[0].file,p1[0].line,p3[0].line)
// </smpl>
Signed-off-by: Julia Lawall <julia@diku.dk>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2008-11-27 17:40:40 +03:00
chip = kzalloc ( sizeof ( * chip ) , GFP_KERNEL ) ;
2017-08-12 19:14:54 +03:00
if ( ! chip )
2005-12-06 15:55:26 +03:00
return - ENOMEM ;
chip - > card = card ;
chip - > chip_idx = idx ;
chip - > mgr = mgr ;
2019-12-10 09:34:50 +03:00
card - > sync_irq = mgr - > irq ;
2005-12-06 15:55:26 +03:00
if ( idx < mgr - > playback_chips )
/* stereo or mono streams */
chip - > nb_streams_play = PCXHR_PLAYBACK_STREAMS ;
if ( idx < mgr - > capture_chips ) {
if ( mgr - > mono_capture )
2008-11-25 14:24:54 +03:00
chip - > nb_streams_capt = 2 ; /* 2 mono streams */
2005-12-06 15:55:26 +03:00
else
chip - > nb_streams_capt = 1 ; /* or 1 stereo stream */
}
if ( ( err = snd_device_new ( card , SNDRV_DEV_LOWLEVEL , chip , & ops ) ) < 0 ) {
pcxhr_chip_free ( chip ) ;
return err ;
}
ALSA: sound/pci/mixart/mixart.c: Add missing snd_card_free
The function snd_mixart_create creates a link between mgr and card that
allows snd_mixart_free to free card as well. But if snd_mixart_create
fails, then the link has not been created and card has to be freed explicitly.
The semantic match that finds the problem is as follows:
(http://www.emn.fr/x-info/coccinelle/)
// <smpl>
@r exists@
local idexpression x;
statement S,S1;
position p1,p2,p3;
expression E,E1;
type T,T1;
expression *ptr != NULL;
@@
(
if ((x@p1 = snd_card_new(...)) == NULL) S
|
x@p1 = snd_card_new(...);
)
... when != snd_card_free(...,(T)x,...)
when != if (...) { <+... snd_card_free(...,(T)x,...) ...+> }
when != true x == NULL || ...
when != x = E
when != E = (T)x
when any
(
if (x == NULL || ...) S1
|
if@p2 (...) {
... when != snd_card_free(...,(T1)x,...)
when != if (...) { <+... snd_card_free(...,(T1)x,...) ...+> }
when != x = E1
when != E1 = (T1)x
(
return \(0\|<+...x...+>\|ptr\);
|
return@p3 ...;
)
}
)
@ script:python @
p1 << r.p1;
p3 << r.p3;
@@
print "* file: %s snd_card_new: %s return: %s" % (p1[0].file,p1[0].line,p3[0].line)
// </smpl>
Signed-off-by: Julia Lawall <julia@diku.dk>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2008-11-27 17:40:40 +03:00
mgr - > chip [ idx ] = chip ;
2005-12-06 15:55:26 +03:00
return 0 ;
}
/* proc interface */
2008-11-25 14:24:54 +03:00
static void pcxhr_proc_info ( struct snd_info_entry * entry ,
struct snd_info_buffer * buffer )
2005-12-06 15:55:26 +03:00
{
struct snd_pcxhr * chip = entry - > private_data ;
struct pcxhr_mgr * mgr = chip - > mgr ;
2017-07-18 14:48:10 +03:00
snd_iprintf ( buffer , " \n %s \n " , mgr - > name ) ;
2005-12-06 15:55:26 +03:00
/* stats available when embedded DSP is running */
if ( mgr - > dsp_loaded & ( 1 < < PCXHR_FIRMWARE_DSP_MAIN_INDEX ) ) {
struct pcxhr_rmh rmh ;
short ver_maj = ( mgr - > dsp_version > > 16 ) & 0xff ;
short ver_min = ( mgr - > dsp_version > > 8 ) & 0xff ;
short ver_build = mgr - > dsp_version & 0xff ;
2008-11-25 14:24:54 +03:00
snd_iprintf ( buffer , " module version %s \n " ,
PCXHR_DRIVER_VERSION_STRING ) ;
snd_iprintf ( buffer , " dsp version %d.%d.%d \n " ,
ver_maj , ver_min , ver_build ) ;
2005-12-06 15:55:26 +03:00
if ( mgr - > board_has_analog )
snd_iprintf ( buffer , " analog io available \n " ) ;
else
snd_iprintf ( buffer , " digital only board \n " ) ;
/* calc cpu load of the dsp */
pcxhr_init_rmh ( & rmh , CMD_GET_DSP_RESOURCES ) ;
if ( ! pcxhr_send_msg ( mgr , & rmh ) ) {
int cur = rmh . stat [ 0 ] ;
int ref = rmh . stat [ 1 ] ;
if ( ref > 0 ) {
if ( mgr - > sample_rate_real ! = 0 & &
mgr - > sample_rate_real ! = 48000 ) {
2008-11-25 14:24:54 +03:00
ref = ( ref * 48000 ) /
mgr - > sample_rate_real ;
if ( mgr - > sample_rate_real > =
PCXHR_IRQ_TIMER_FREQ )
2005-12-06 15:55:26 +03:00
ref * = 2 ;
}
cur = 100 - ( 100 * cur ) / ref ;
snd_iprintf ( buffer , " cpu load %d%% \n " , cur ) ;
2008-11-25 14:24:54 +03:00
snd_iprintf ( buffer , " buffer pool %d/%d \n " ,
2005-12-06 15:55:26 +03:00
rmh . stat [ 2 ] , rmh . stat [ 3 ] ) ;
}
}
2008-11-25 14:24:54 +03:00
snd_iprintf ( buffer , " dma granularity : %d \n " ,
mgr - > granularity ) ;
snd_iprintf ( buffer , " dsp time errors : %d \n " ,
mgr - > dsp_time_err ) ;
2005-12-06 15:55:26 +03:00
snd_iprintf ( buffer , " dsp async pipe xrun errors : %d \n " ,
mgr - > async_err_pipe_xrun ) ;
snd_iprintf ( buffer , " dsp async stream xrun errors : %d \n " ,
mgr - > async_err_stream_xrun ) ;
snd_iprintf ( buffer , " dsp async last other error : %x \n " ,
mgr - > async_err_other_last ) ;
/* debug zone dsp */
rmh . cmd [ 0 ] = 0x4200 + PCXHR_SIZE_MAX_STATUS ;
rmh . cmd_len = 1 ;
rmh . stat_len = PCXHR_SIZE_MAX_STATUS ;
rmh . dsp_stat = 0 ;
rmh . cmd_idx = CMD_LAST_INDEX ;
if ( ! pcxhr_send_msg ( mgr , & rmh ) ) {
int i ;
2008-11-25 14:24:54 +03:00
if ( rmh . stat_len > 8 )
rmh . stat_len = 8 ;
2005-12-06 15:55:26 +03:00
for ( i = 0 ; i < rmh . stat_len ; i + + )
2008-11-25 14:24:54 +03:00
snd_iprintf ( buffer , " debug[%02d] = %06x \n " ,
i , rmh . stat [ i ] ) ;
2005-12-06 15:55:26 +03:00
}
} else
snd_iprintf ( buffer , " no firmware loaded \n " ) ;
snd_iprintf ( buffer , " \n " ) ;
}
2008-11-25 14:24:54 +03:00
static void pcxhr_proc_sync ( struct snd_info_entry * entry ,
struct snd_info_buffer * buffer )
2005-12-06 15:55:26 +03:00
{
struct snd_pcxhr * chip = entry - > private_data ;
struct pcxhr_mgr * mgr = chip - > mgr ;
2008-11-25 14:24:54 +03:00
static const char * textsHR22 [ 3 ] = {
" Internal " , " AES Sync " , " AES 1 "
} ;
static const char * textsPCXHR [ 7 ] = {
" Internal " , " Word " , " AES Sync " ,
" AES 1 " , " AES 2 " , " AES 3 " , " AES 4 "
2005-12-06 15:55:26 +03:00
} ;
2008-11-25 14:24:54 +03:00
const char * * texts ;
int max_clock ;
if ( mgr - > is_hr_stereo ) {
texts = textsHR22 ;
max_clock = HR22_CLOCK_TYPE_MAX ;
} else {
texts = textsPCXHR ;
max_clock = PCXHR_CLOCK_TYPE_MAX ;
}
2005-12-06 15:55:26 +03:00
2017-07-18 14:48:10 +03:00
snd_iprintf ( buffer , " \n %s \n " , mgr - > name ) ;
2008-11-25 14:24:54 +03:00
snd_iprintf ( buffer , " Current Sample Clock \t : %s \n " ,
texts [ mgr - > cur_clock_type ] ) ;
snd_iprintf ( buffer , " Current Sample Rate \t = %d \n " ,
mgr - > sample_rate_real ) ;
2005-12-06 15:55:26 +03:00
/* commands available when embedded DSP is running */
if ( mgr - > dsp_loaded & ( 1 < < PCXHR_FIRMWARE_DSP_MAIN_INDEX ) ) {
int i , err , sample_rate ;
2008-11-25 14:24:54 +03:00
for ( i = 1 ; i < = max_clock ; i + + ) {
2005-12-06 15:55:26 +03:00
err = pcxhr_get_external_clock ( mgr , i , & sample_rate ) ;
if ( err )
break ;
2008-11-25 14:24:54 +03:00
snd_iprintf ( buffer , " %s Clock \t \t = %d \n " ,
texts [ i ] , sample_rate ) ;
2005-12-06 15:55:26 +03:00
}
} else
snd_iprintf ( buffer , " no firmware loaded \n " ) ;
snd_iprintf ( buffer , " \n " ) ;
}
2009-01-23 16:45:41 +03:00
static void pcxhr_proc_gpio_read ( struct snd_info_entry * entry ,
struct snd_info_buffer * buffer )
{
struct snd_pcxhr * chip = entry - > private_data ;
struct pcxhr_mgr * mgr = chip - > mgr ;
/* commands available when embedded DSP is running */
if ( mgr - > dsp_loaded & ( 1 < < PCXHR_FIRMWARE_DSP_MAIN_INDEX ) ) {
/* gpio ports on stereo boards only available */
int value = 0 ;
hr222_read_gpio ( mgr , 1 , & value ) ; /* GPI */
snd_iprintf ( buffer , " GPI: 0x%x \n " , value ) ;
hr222_read_gpio ( mgr , 0 , & value ) ; /* GP0 */
snd_iprintf ( buffer , " GPO: 0x%x \n " , value ) ;
} else
snd_iprintf ( buffer , " no firmware loaded \n " ) ;
snd_iprintf ( buffer , " \n " ) ;
}
static void pcxhr_proc_gpo_write ( struct snd_info_entry * entry ,
struct snd_info_buffer * buffer )
{
struct snd_pcxhr * chip = entry - > private_data ;
struct pcxhr_mgr * mgr = chip - > mgr ;
char line [ 64 ] ;
int value ;
/* commands available when embedded DSP is running */
if ( ! ( mgr - > dsp_loaded & ( 1 < < PCXHR_FIRMWARE_DSP_MAIN_INDEX ) ) )
return ;
while ( ! snd_info_get_line ( buffer , line , sizeof ( line ) ) ) {
if ( sscanf ( line , " GPO: 0x%x " , & value ) ! = 1 )
continue ;
hr222_write_gpo ( mgr , value ) ; /* GP0 */
}
}
2012-06-20 10:34:40 +04:00
/* Access to the results of the CMD_GET_TIME_CODE RMH */
# define TIME_CODE_VALID_MASK 0x00800000
# define TIME_CODE_NEW_MASK 0x00400000
# define TIME_CODE_BACK_MASK 0x00200000
# define TIME_CODE_WAIT_MASK 0x00100000
/* Values for the CMD_MANAGE_SIGNAL RMH */
# define MANAGE_SIGNAL_TIME_CODE 0x01
# define MANAGE_SIGNAL_MIDI 0x02
/* linear time code read proc*/
static void pcxhr_proc_ltc ( struct snd_info_entry * entry ,
struct snd_info_buffer * buffer )
{
struct snd_pcxhr * chip = entry - > private_data ;
struct pcxhr_mgr * mgr = chip - > mgr ;
struct pcxhr_rmh rmh ;
unsigned int ltcHrs , ltcMin , ltcSec , ltcFrm ;
int err ;
/* commands available when embedded DSP is running */
if ( ! ( mgr - > dsp_loaded & ( 1 < < PCXHR_FIRMWARE_DSP_MAIN_INDEX ) ) ) {
snd_iprintf ( buffer , " no firmware loaded \n " ) ;
return ;
}
if ( ! mgr - > capture_ltc ) {
pcxhr_init_rmh ( & rmh , CMD_MANAGE_SIGNAL ) ;
rmh . cmd [ 0 ] | = MANAGE_SIGNAL_TIME_CODE ;
err = pcxhr_send_msg ( mgr , & rmh ) ;
if ( err ) {
snd_iprintf ( buffer , " ltc not activated (%d) \n " , err ) ;
return ;
}
if ( mgr - > is_hr_stereo )
hr222_manage_timecode ( mgr , 1 ) ;
else
pcxhr_write_io_num_reg_cont ( mgr , REG_CONT_VALSMPTE ,
REG_CONT_VALSMPTE , NULL ) ;
mgr - > capture_ltc = 1 ;
}
pcxhr_init_rmh ( & rmh , CMD_GET_TIME_CODE ) ;
err = pcxhr_send_msg ( mgr , & rmh ) ;
if ( err ) {
snd_iprintf ( buffer , " ltc read error (err=%d) \n " , err ) ;
return ;
}
ltcHrs = 10 * ( ( rmh . stat [ 0 ] > > 8 ) & 0x3 ) + ( rmh . stat [ 0 ] & 0xf ) ;
ltcMin = 10 * ( ( rmh . stat [ 1 ] > > 16 ) & 0x7 ) + ( ( rmh . stat [ 1 ] > > 8 ) & 0xf ) ;
ltcSec = 10 * ( rmh . stat [ 1 ] & 0x7 ) + ( ( rmh . stat [ 2 ] > > 16 ) & 0xf ) ;
ltcFrm = 10 * ( ( rmh . stat [ 2 ] > > 8 ) & 0x3 ) + ( rmh . stat [ 2 ] & 0xf ) ;
snd_iprintf ( buffer , " timecode: %02u:%02u:%02u-%02u \n " ,
ltcHrs , ltcMin , ltcSec , ltcFrm ) ;
snd_iprintf ( buffer , " raw: 0x%04x%06x%06x \n " , rmh . stat [ 0 ] & 0x00ffff ,
rmh . stat [ 1 ] & 0xffffff , rmh . stat [ 2 ] & 0xffffff ) ;
/*snd_iprintf(buffer, "dsp ref time: 0x%06x%06x\n",
rmh . stat [ 3 ] & 0xffffff , rmh . stat [ 4 ] & 0xffffff ) ; */
if ( ! ( rmh . stat [ 0 ] & TIME_CODE_VALID_MASK ) ) {
snd_iprintf ( buffer , " warning: linear timecode not valid \n " ) ;
}
}
2012-12-06 21:35:10 +04:00
static void pcxhr_proc_init ( struct snd_pcxhr * chip )
2005-12-06 15:55:26 +03:00
{
2019-02-04 18:01:39 +03:00
snd_card_ro_proc_new ( chip - > card , " info " , chip , pcxhr_proc_info ) ;
snd_card_ro_proc_new ( chip - > card , " sync " , chip , pcxhr_proc_sync ) ;
2009-01-23 16:45:41 +03:00
/* gpio available on stereo sound cards only */
2019-02-04 18:01:39 +03:00
if ( chip - > mgr - > is_hr_stereo )
snd_card_rw_proc_new ( chip - > card , " gpio " , chip ,
pcxhr_proc_gpio_read ,
pcxhr_proc_gpo_write ) ;
snd_card_ro_proc_new ( chip - > card , " ltc " , chip , pcxhr_proc_ltc ) ;
2005-12-06 15:55:26 +03:00
}
/* end of proc interface */
/*
* release all the cards assigned to a manager instance
*/
static int pcxhr_free ( struct pcxhr_mgr * mgr )
{
unsigned int i ;
for ( i = 0 ; i < mgr - > num_cards ; i + + ) {
if ( mgr - > chip [ i ] )
snd_card_free ( mgr - > chip [ i ] - > card ) ;
}
/* reset board if some firmware was loaded */
if ( mgr - > dsp_loaded ) {
pcxhr_reset_board ( mgr ) ;
2014-02-26 14:47:55 +04:00
dev_dbg ( & mgr - > pci - > dev , " reset pcxhr ! \n " ) ;
2005-12-06 15:55:26 +03:00
}
/* release irq */
if ( mgr - > irq > = 0 )
free_irq ( mgr - > irq , mgr ) ;
pci_release_regions ( mgr - > pci ) ;
/* free hostport purgebuffer */
if ( mgr - > hostport . area ) {
snd_dma_free_pages ( & mgr - > hostport ) ;
mgr - > hostport . area = NULL ;
}
kfree ( mgr - > prmh ) ;
pci_disable_device ( mgr - > pci ) ;
kfree ( mgr ) ;
return 0 ;
}
/*
* probe function - creates the card manager
*/
2012-12-06 21:35:10 +04:00
static int pcxhr_probe ( struct pci_dev * pci ,
const struct pci_device_id * pci_id )
2005-12-06 15:55:26 +03:00
{
static int dev ;
struct pcxhr_mgr * mgr ;
unsigned int i ;
int err ;
size_t size ;
char * card_name ;
if ( dev > = SNDRV_CARDS )
return - ENODEV ;
if ( ! enable [ dev ] ) {
dev + + ;
return - ENOENT ;
}
/* enable PCI device */
if ( ( err = pci_enable_device ( pci ) ) < 0 )
return err ;
pci_set_master ( pci ) ;
/* check if we can restrict PCI DMA transfers to 32 bits */
ALSA: remove deprecated use of pci api
Replace occurences of the pci api by appropriate call to the dma api.
A simplified version of the semantic patch that finds this problem is as
follows: (http://coccinelle.lip6.fr)
@deprecated@
idexpression id;
position p;
@@
(
pci_dma_supported@p ( id, ...)
|
pci_alloc_consistent@p ( id, ...)
)
@bad1@
idexpression id;
position deprecated.p;
@@
...when != &id->dev
when != pci_get_drvdata ( id )
when != pci_enable_device ( id )
(
pci_dma_supported@p ( id, ...)
|
pci_alloc_consistent@p ( id, ...)
)
@depends on !bad1@
idexpression id;
expression direction;
position deprecated.p;
@@
(
- pci_dma_supported@p ( id,
+ dma_supported ( &id->dev,
...
+ , GFP_ATOMIC
)
|
- pci_alloc_consistent@p ( id,
+ dma_alloc_coherent ( &id->dev,
...
+ , GFP_ATOMIC
)
)
Signed-off-by: Quentin Lambert <lambert.quentin@gmail.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2015-04-15 17:10:17 +03:00
if ( dma_set_mask ( & pci - > dev , DMA_BIT_MASK ( 32 ) ) < 0 ) {
2014-02-26 14:47:55 +04:00
dev_err ( & pci - > dev ,
" architecture does not support 32bit PCI busmaster DMA \n " ) ;
2005-12-06 15:55:26 +03:00
pci_disable_device ( pci ) ;
return - ENXIO ;
}
/* alloc card manager */
mgr = kzalloc ( sizeof ( * mgr ) , GFP_KERNEL ) ;
if ( ! mgr ) {
pci_disable_device ( pci ) ;
return - ENOMEM ;
}
2008-11-14 21:08:18 +03:00
if ( snd_BUG_ON ( pci_id - > driver_data > = PCI_ID_LAST ) ) {
kfree ( mgr ) ;
pci_disable_device ( pci ) ;
2008-08-08 19:12:14 +04:00
return - ENODEV ;
2008-11-14 21:08:18 +03:00
}
2008-11-25 14:24:54 +03:00
card_name =
pcxhr_board_params [ pci_id - > driver_data ] . board_name ;
mgr - > playback_chips =
pcxhr_board_params [ pci_id - > driver_data ] . playback_chips ;
mgr - > capture_chips =
pcxhr_board_params [ pci_id - > driver_data ] . capture_chips ;
mgr - > fw_file_set =
pcxhr_board_params [ pci_id - > driver_data ] . fw_file_set ;
mgr - > firmware_num =
pcxhr_board_params [ pci_id - > driver_data ] . firmware_num ;
2005-12-06 15:55:26 +03:00
mgr - > mono_capture = mono [ dev ] ;
2008-11-25 14:24:54 +03:00
mgr - > is_hr_stereo = ( mgr - > playback_chips = = 1 ) ;
mgr - > board_has_aes1 = PCXHR_BOARD_HAS_AES1 ( mgr ) ;
mgr - > board_aes_in_192k = ! PCXHR_BOARD_AESIN_NO_192K ( mgr ) ;
if ( mgr - > is_hr_stereo )
mgr - > granularity = PCXHR_GRANULARITY_HR22 ;
else
mgr - > granularity = PCXHR_GRANULARITY ;
2005-12-06 15:55:26 +03:00
/* resource assignment */
if ( ( err = pci_request_regions ( pci , card_name ) ) < 0 ) {
kfree ( mgr ) ;
pci_disable_device ( pci ) ;
return err ;
}
for ( i = 0 ; i < 3 ; i + + )
mgr - > port [ i ] = pci_resource_start ( pci , i ) ;
mgr - > pci = pci ;
mgr - > irq = - 1 ;
2014-09-10 14:21:38 +04:00
if ( request_threaded_irq ( pci - > irq , pcxhr_interrupt ,
pcxhr_threaded_irq , IRQF_SHARED ,
KBUILD_MODNAME , mgr ) ) {
2014-02-26 14:47:55 +04:00
dev_err ( & pci - > dev , " unable to grab IRQ %d \n " , pci - > irq ) ;
2005-12-06 15:55:26 +03:00
pcxhr_free ( mgr ) ;
return - EBUSY ;
}
mgr - > irq = pci - > irq ;
2017-07-18 14:48:10 +03:00
snprintf ( mgr - > name , sizeof ( mgr - > name ) ,
" Digigram at 0x%lx & 0x%lx, 0x%lx irq %i " ,
mgr - > port [ 0 ] , mgr - > port [ 1 ] , mgr - > port [ 2 ] , mgr - > irq ) ;
2005-12-06 15:55:26 +03:00
2014-09-10 14:21:38 +04:00
/* ISR lock */
mutex_init ( & mgr - > lock ) ;
mutex_init ( & mgr - > msg_lock ) ;
2005-12-06 15:55:26 +03:00
/* init setup mutex*/
2006-01-16 18:34:20 +03:00
mutex_init ( & mgr - > setup_mutex ) ;
2005-12-06 15:55:26 +03:00
mgr - > prmh = kmalloc ( sizeof ( * mgr - > prmh ) +
2008-11-25 14:24:54 +03:00
sizeof ( u32 ) * ( PCXHR_SIZE_MAX_LONG_STATUS -
PCXHR_SIZE_MAX_STATUS ) ,
2005-12-06 15:55:26 +03:00
GFP_KERNEL ) ;
if ( ! mgr - > prmh ) {
pcxhr_free ( mgr ) ;
return - ENOMEM ;
}
for ( i = 0 ; i < PCXHR_MAX_CARDS ; i + + ) {
struct snd_card * card ;
char tmpid [ 16 ] ;
int idx ;
if ( i > = max ( mgr - > playback_chips , mgr - > capture_chips ) )
break ;
mgr - > num_cards + + ;
if ( index [ dev ] < 0 )
idx = index [ dev ] ;
else
idx = index [ dev ] + i ;
2008-11-25 14:24:54 +03:00
snprintf ( tmpid , sizeof ( tmpid ) , " %s-%d " ,
id [ dev ] ? id [ dev ] : card_name , i ) ;
2014-01-29 17:20:19 +04:00
err = snd_card_new ( & pci - > dev , idx , tmpid , THIS_MODULE ,
0 , & card ) ;
2005-12-06 15:55:26 +03:00
2008-12-28 18:44:30 +03:00
if ( err < 0 ) {
2014-12-10 16:26:21 +03:00
dev_err ( & pci - > dev , " cannot allocate the card %d \n " , i ) ;
2005-12-06 15:55:26 +03:00
pcxhr_free ( mgr ) ;
2008-12-28 18:44:30 +03:00
return err ;
2005-12-06 15:55:26 +03:00
}
strcpy ( card - > driver , DRIVER_NAME ) ;
2017-07-18 14:48:10 +03:00
snprintf ( card - > shortname , sizeof ( card - > shortname ) ,
" Digigram [PCM #%d] " , i ) ;
snprintf ( card - > longname , sizeof ( card - > longname ) ,
" %s [PCM #%d] " , mgr - > name , i ) ;
2005-12-06 15:55:26 +03:00
if ( ( err = pcxhr_create ( mgr , card , i ) ) < 0 ) {
ALSA: sound/pci/mixart/mixart.c: Add missing snd_card_free
The function snd_mixart_create creates a link between mgr and card that
allows snd_mixart_free to free card as well. But if snd_mixart_create
fails, then the link has not been created and card has to be freed explicitly.
The semantic match that finds the problem is as follows:
(http://www.emn.fr/x-info/coccinelle/)
// <smpl>
@r exists@
local idexpression x;
statement S,S1;
position p1,p2,p3;
expression E,E1;
type T,T1;
expression *ptr != NULL;
@@
(
if ((x@p1 = snd_card_new(...)) == NULL) S
|
x@p1 = snd_card_new(...);
)
... when != snd_card_free(...,(T)x,...)
when != if (...) { <+... snd_card_free(...,(T)x,...) ...+> }
when != true x == NULL || ...
when != x = E
when != E = (T)x
when any
(
if (x == NULL || ...) S1
|
if@p2 (...) {
... when != snd_card_free(...,(T1)x,...)
when != if (...) { <+... snd_card_free(...,(T1)x,...) ...+> }
when != x = E1
when != E1 = (T1)x
(
return \(0\|<+...x...+>\|ptr\);
|
return@p3 ...;
)
}
)
@ script:python @
p1 << r.p1;
p3 << r.p3;
@@
print "* file: %s snd_card_new: %s return: %s" % (p1[0].file,p1[0].line,p3[0].line)
// </smpl>
Signed-off-by: Julia Lawall <julia@diku.dk>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2008-11-27 17:40:40 +03:00
snd_card_free ( card ) ;
2005-12-06 15:55:26 +03:00
pcxhr_free ( mgr ) ;
return err ;
}
if ( i = = 0 )
/* init proc interface only for chip0 */
pcxhr_proc_init ( mgr - > chip [ i ] ) ;
if ( ( err = snd_card_register ( card ) ) < 0 ) {
pcxhr_free ( mgr ) ;
return err ;
}
}
/* create hostport purgebuffer */
size = PAGE_ALIGN ( sizeof ( struct pcxhr_hostport ) ) ;
2019-11-05 18:18:55 +03:00
if ( snd_dma_alloc_pages ( SNDRV_DMA_TYPE_DEV , & pci - > dev ,
2005-12-06 15:55:26 +03:00
size , & mgr - > hostport ) < 0 ) {
pcxhr_free ( mgr ) ;
return - ENOMEM ;
}
/* init purgebuffer */
memset ( mgr - > hostport . area , 0 , size ) ;
/* create a DSP loader */
err = pcxhr_setup_firmware ( mgr ) ;
if ( err < 0 ) {
pcxhr_free ( mgr ) ;
return err ;
}
pci_set_drvdata ( pci , mgr ) ;
dev + + ;
return 0 ;
}
2012-12-06 21:35:10 +04:00
static void pcxhr_remove ( struct pci_dev * pci )
2005-12-06 15:55:26 +03:00
{
pcxhr_free ( pci_get_drvdata ( pci ) ) ;
}
2012-04-24 14:25:00 +04:00
static struct pci_driver pcxhr_driver = {
2011-06-10 18:20:20 +04:00
. name = KBUILD_MODNAME ,
2005-12-06 15:55:26 +03:00
. id_table = pcxhr_ids ,
. probe = pcxhr_probe ,
2012-12-06 21:35:10 +04:00
. remove = pcxhr_remove ,
2005-12-06 15:55:26 +03:00
} ;
2012-04-24 14:25:00 +04:00
module_pci_driver ( pcxhr_driver ) ;