2007-12-23 19:50:57 +01:00
/*
* C - Media CMI8788 driver - PCM code
*
* Copyright ( c ) Clemens Ladisch < clemens @ ladisch . de >
*
*
* This driver is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License , version 2.
*
* This driver 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 driver ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include <linux/pci.h>
# include <sound/control.h>
# include <sound/core.h>
# include <sound/pcm.h>
# include <sound/pcm_params.h>
# include "oxygen.h"
2008-05-13 09:25:39 +02:00
/* most DMA channels have a 16-bit counter for 32-bit words */
# define BUFFER_BYTES_MAX ((1 << 16) * 4)
/* the multichannel DMA channel has a 24-bit counter */
# define BUFFER_BYTES_MAX_MULTICH ((1 << 24) * 4)
2014-09-21 22:52:46 +02:00
# define FIFO_BYTES 256
# define FIFO_BYTES_MULTICH 1024
2008-05-13 09:25:39 +02:00
# define PERIOD_BYTES_MIN 64
# define DEFAULT_BUFFER_BYTES (BUFFER_BYTES_MAX / 2)
# define DEFAULT_BUFFER_BYTES_MULTICH (1024 * 1024)
2008-01-21 08:54:06 +01:00
static const struct snd_pcm_hardware oxygen_stereo_hardware = {
. info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE |
2010-11-15 10:50:37 +01:00
SNDRV_PCM_INFO_SYNC_START |
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP ,
2008-01-21 08:54:06 +01:00
. formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S32_LE ,
. rates = SNDRV_PCM_RATE_32000 |
SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_64000 |
SNDRV_PCM_RATE_88200 |
SNDRV_PCM_RATE_96000 |
SNDRV_PCM_RATE_176400 |
SNDRV_PCM_RATE_192000 ,
. rate_min = 32000 ,
. rate_max = 192000 ,
. channels_min = 2 ,
. channels_max = 2 ,
2008-05-13 09:25:39 +02:00
. buffer_bytes_max = BUFFER_BYTES_MAX ,
. period_bytes_min = PERIOD_BYTES_MIN ,
2010-10-06 10:57:11 +02:00
. period_bytes_max = BUFFER_BYTES_MAX ,
. periods_min = 1 ,
2008-05-13 09:25:39 +02:00
. periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN ,
2014-09-21 22:52:46 +02:00
. fifo_size = FIFO_BYTES ,
2008-01-21 08:54:06 +01:00
} ;
static const struct snd_pcm_hardware oxygen_multichannel_hardware = {
. info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE |
2010-11-15 10:50:37 +01:00
SNDRV_PCM_INFO_SYNC_START |
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP ,
2008-01-21 08:54:06 +01:00
. formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S32_LE ,
. rates = SNDRV_PCM_RATE_32000 |
SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_64000 |
SNDRV_PCM_RATE_88200 |
SNDRV_PCM_RATE_96000 |
SNDRV_PCM_RATE_176400 |
SNDRV_PCM_RATE_192000 ,
. rate_min = 32000 ,
. rate_max = 192000 ,
. channels_min = 2 ,
. channels_max = 8 ,
2008-05-13 09:25:39 +02:00
. buffer_bytes_max = BUFFER_BYTES_MAX_MULTICH ,
. period_bytes_min = PERIOD_BYTES_MIN ,
2010-10-06 10:57:11 +02:00
. period_bytes_max = BUFFER_BYTES_MAX_MULTICH ,
. periods_min = 1 ,
2008-05-13 09:25:39 +02:00
. periods_max = BUFFER_BYTES_MAX_MULTICH / PERIOD_BYTES_MIN ,
2014-09-21 22:52:46 +02:00
. fifo_size = FIFO_BYTES_MULTICH ,
2008-01-21 08:54:06 +01:00
} ;
static const struct snd_pcm_hardware oxygen_ac97_hardware = {
. info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE |
2010-11-15 10:50:37 +01:00
SNDRV_PCM_INFO_SYNC_START |
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP ,
2008-01-21 08:54:06 +01:00
. formats = SNDRV_PCM_FMTBIT_S16_LE ,
. rates = SNDRV_PCM_RATE_48000 ,
. rate_min = 48000 ,
. rate_max = 48000 ,
. channels_min = 2 ,
. channels_max = 2 ,
2008-05-13 09:25:39 +02:00
. buffer_bytes_max = BUFFER_BYTES_MAX ,
. period_bytes_min = PERIOD_BYTES_MIN ,
2010-10-06 10:57:11 +02:00
. period_bytes_max = BUFFER_BYTES_MAX ,
. periods_min = 1 ,
2008-05-13 09:25:39 +02:00
. periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN ,
2014-09-21 22:52:46 +02:00
. fifo_size = FIFO_BYTES ,
2008-01-21 08:54:06 +01:00
} ;
static const struct snd_pcm_hardware * const oxygen_hardware [ PCM_COUNT ] = {
[ PCM_A ] = & oxygen_stereo_hardware ,
[ PCM_B ] = & oxygen_stereo_hardware ,
[ PCM_C ] = & oxygen_stereo_hardware ,
[ PCM_SPDIF ] = & oxygen_stereo_hardware ,
[ PCM_MULTICH ] = & oxygen_multichannel_hardware ,
[ PCM_AC97 ] = & oxygen_ac97_hardware ,
2007-12-23 19:50:57 +01:00
} ;
2008-01-04 09:22:20 +01:00
static inline unsigned int
oxygen_substream_channel ( struct snd_pcm_substream * substream )
{
return ( unsigned int ) ( uintptr_t ) substream - > runtime - > private_data ;
}
2007-12-23 19:50:57 +01:00
static int oxygen_open ( struct snd_pcm_substream * substream ,
unsigned int channel )
{
struct oxygen * chip = snd_pcm_substream_chip ( substream ) ;
struct snd_pcm_runtime * runtime = substream - > runtime ;
int err ;
2008-01-04 09:22:20 +01:00
runtime - > private_data = ( void * ) ( uintptr_t ) channel ;
2008-01-28 08:35:47 +01:00
if ( channel = = PCM_B & & chip - > has_ac97_1 & &
2008-09-22 09:02:08 +02:00
( chip - > model . device_config & CAPTURE_2_FROM_AC97_1 ) )
2008-01-28 08:35:47 +01:00
runtime - > hw = oxygen_ac97_hardware ;
else
runtime - > hw = * oxygen_hardware [ channel ] ;
2008-01-25 08:37:49 +01:00
switch ( channel ) {
case PCM_C :
2015-01-16 22:15:13 +01:00
if ( chip - > model . device_config & CAPTURE_1_FROM_SPDIF ) {
runtime - > hw . rates & = ~ ( SNDRV_PCM_RATE_32000 |
SNDRV_PCM_RATE_64000 ) ;
runtime - > hw . rate_min = 44100 ;
}
2014-09-21 22:52:46 +02:00
/* fall through */
case PCM_A :
case PCM_B :
runtime - > hw . fifo_size = 0 ;
2008-01-25 08:37:49 +01:00
break ;
case PCM_MULTICH :
2011-01-10 15:59:38 +01:00
runtime - > hw . channels_max = chip - > model . dac_channels_pcm ;
2008-01-25 08:37:49 +01:00
break ;
2008-01-24 08:43:16 +01:00
}
2008-09-22 08:55:19 +02:00
if ( chip - > model . pcm_hardware_filter )
chip - > model . pcm_hardware_filter ( channel , & runtime - > hw ) ;
2007-12-23 19:50:57 +01:00
err = snd_pcm_hw_constraint_step ( runtime , 0 ,
SNDRV_PCM_HW_PARAM_PERIOD_BYTES , 32 ) ;
if ( err < 0 )
return err ;
err = snd_pcm_hw_constraint_step ( runtime , 0 ,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES , 32 ) ;
if ( err < 0 )
return err ;
if ( runtime - > hw . formats & SNDRV_PCM_FMTBIT_S32_LE ) {
err = snd_pcm_hw_constraint_msbits ( runtime , 0 , 32 , 24 ) ;
if ( err < 0 )
return err ;
}
if ( runtime - > hw . channels_max > 2 ) {
err = snd_pcm_hw_constraint_step ( runtime , 0 ,
SNDRV_PCM_HW_PARAM_CHANNELS ,
2 ) ;
if ( err < 0 )
2008-05-13 09:26:01 +02:00
return err ;
}
2007-12-23 19:50:57 +01:00
snd_pcm_set_sync ( substream ) ;
chip - > streams [ channel ] = substream ;
mutex_lock ( & chip - > mutex ) ;
chip - > pcm_active | = 1 < < channel ;
if ( channel = = PCM_SPDIF ) {
chip - > spdif_pcm_bits = chip - > spdif_bits ;
2008-01-14 08:56:01 +01:00
chip - > controls [ CONTROL_SPDIF_PCM ] - > vd [ 0 ] . access & =
2007-12-23 19:50:57 +01:00
~ SNDRV_CTL_ELEM_ACCESS_INACTIVE ;
snd_ctl_notify ( chip - > card , SNDRV_CTL_EVENT_MASK_VALUE |
SNDRV_CTL_EVENT_MASK_INFO ,
2008-01-14 08:56:01 +01:00
& chip - > controls [ CONTROL_SPDIF_PCM ] - > id ) ;
2007-12-23 19:50:57 +01:00
}
mutex_unlock ( & chip - > mutex ) ;
return 0 ;
}
static int oxygen_rec_a_open ( struct snd_pcm_substream * substream )
{
return oxygen_open ( substream , PCM_A ) ;
}
static int oxygen_rec_b_open ( struct snd_pcm_substream * substream )
{
return oxygen_open ( substream , PCM_B ) ;
}
static int oxygen_rec_c_open ( struct snd_pcm_substream * substream )
{
return oxygen_open ( substream , PCM_C ) ;
}
static int oxygen_spdif_open ( struct snd_pcm_substream * substream )
{
return oxygen_open ( substream , PCM_SPDIF ) ;
}
static int oxygen_multich_open ( struct snd_pcm_substream * substream )
{
return oxygen_open ( substream , PCM_MULTICH ) ;
}
static int oxygen_ac97_open ( struct snd_pcm_substream * substream )
{
return oxygen_open ( substream , PCM_AC97 ) ;
}
static int oxygen_close ( struct snd_pcm_substream * substream )
{
struct oxygen * chip = snd_pcm_substream_chip ( substream ) ;
2008-01-04 09:22:20 +01:00
unsigned int channel = oxygen_substream_channel ( substream ) ;
2007-12-23 19:50:57 +01:00
mutex_lock ( & chip - > mutex ) ;
chip - > pcm_active & = ~ ( 1 < < channel ) ;
if ( channel = = PCM_SPDIF ) {
2008-01-14 08:56:01 +01:00
chip - > controls [ CONTROL_SPDIF_PCM ] - > vd [ 0 ] . access | =
2007-12-23 19:50:57 +01:00
SNDRV_CTL_ELEM_ACCESS_INACTIVE ;
snd_ctl_notify ( chip - > card , SNDRV_CTL_EVENT_MASK_VALUE |
SNDRV_CTL_EVENT_MASK_INFO ,
2008-01-14 08:56:01 +01:00
& chip - > controls [ CONTROL_SPDIF_PCM ] - > id ) ;
2007-12-23 19:50:57 +01:00
}
if ( channel = = PCM_SPDIF | | channel = = PCM_MULTICH )
oxygen_update_spdif_source ( chip ) ;
mutex_unlock ( & chip - > mutex ) ;
chip - > streams [ channel ] = NULL ;
return 0 ;
}
static unsigned int oxygen_format ( struct snd_pcm_hw_params * hw_params )
{
if ( params_format ( hw_params ) = = SNDRV_PCM_FORMAT_S32_LE )
return OXYGEN_FORMAT_24 ;
else
return OXYGEN_FORMAT_16 ;
}
static unsigned int oxygen_rate ( struct snd_pcm_hw_params * hw_params )
{
switch ( params_rate ( hw_params ) ) {
case 32000 :
return OXYGEN_RATE_32000 ;
case 44100 :
return OXYGEN_RATE_44100 ;
default : /* 48000 */
return OXYGEN_RATE_48000 ;
case 64000 :
return OXYGEN_RATE_64000 ;
case 88200 :
return OXYGEN_RATE_88200 ;
case 96000 :
return OXYGEN_RATE_96000 ;
case 176400 :
return OXYGEN_RATE_176400 ;
case 192000 :
return OXYGEN_RATE_192000 ;
}
}
2008-01-17 09:05:09 +01:00
static unsigned int oxygen_i2s_bits ( struct snd_pcm_hw_params * hw_params )
2007-12-23 19:50:57 +01:00
{
if ( params_format ( hw_params ) = = SNDRV_PCM_FORMAT_S32_LE )
2008-01-17 09:05:09 +01:00
return OXYGEN_I2S_BITS_24 ;
2007-12-23 19:50:57 +01:00
else
2008-01-17 09:05:09 +01:00
return OXYGEN_I2S_BITS_16 ;
2007-12-23 19:50:57 +01:00
}
static unsigned int oxygen_play_channels ( struct snd_pcm_hw_params * hw_params )
{
switch ( params_channels ( hw_params ) ) {
default : /* 2 */
return OXYGEN_PLAY_CHANNELS_2 ;
case 4 :
return OXYGEN_PLAY_CHANNELS_4 ;
case 6 :
return OXYGEN_PLAY_CHANNELS_6 ;
case 8 :
return OXYGEN_PLAY_CHANNELS_8 ;
}
}
static const unsigned int channel_base_registers [ PCM_COUNT ] = {
[ PCM_A ] = OXYGEN_DMA_A_ADDRESS ,
[ PCM_B ] = OXYGEN_DMA_B_ADDRESS ,
[ PCM_C ] = OXYGEN_DMA_C_ADDRESS ,
[ PCM_SPDIF ] = OXYGEN_DMA_SPDIF_ADDRESS ,
[ PCM_MULTICH ] = OXYGEN_DMA_MULTICH_ADDRESS ,
[ PCM_AC97 ] = OXYGEN_DMA_AC97_ADDRESS ,
} ;
static int oxygen_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * hw_params )
{
struct oxygen * chip = snd_pcm_substream_chip ( substream ) ;
2008-01-04 09:22:20 +01:00
unsigned int channel = oxygen_substream_channel ( substream ) ;
2007-12-23 19:50:57 +01:00
int err ;
err = snd_pcm_lib_malloc_pages ( substream ,
params_buffer_bytes ( hw_params ) ) ;
if ( err < 0 )
return err ;
oxygen_write32 ( chip , channel_base_registers [ channel ] ,
( u32 ) substream - > runtime - > dma_addr ) ;
if ( channel = = PCM_MULTICH ) {
oxygen_write32 ( chip , OXYGEN_DMA_MULTICH_COUNT ,
params_buffer_bytes ( hw_params ) / 4 - 1 ) ;
oxygen_write32 ( chip , OXYGEN_DMA_MULTICH_TCOUNT ,
params_period_bytes ( hw_params ) / 4 - 1 ) ;
} else {
oxygen_write16 ( chip , channel_base_registers [ channel ] + 4 ,
params_buffer_bytes ( hw_params ) / 4 - 1 ) ;
oxygen_write16 ( chip , channel_base_registers [ channel ] + 6 ,
params_period_bytes ( hw_params ) / 4 - 1 ) ;
}
return 0 ;
}
2011-01-10 16:14:52 +01:00
static u16 get_mclk ( struct oxygen * chip , unsigned int channel ,
struct snd_pcm_hw_params * params )
{
unsigned int mclks , shift ;
if ( channel = = PCM_MULTICH )
mclks = chip - > model . dac_mclks ;
else
mclks = chip - > model . adc_mclks ;
if ( params_rate ( params ) < = 48000 )
shift = 0 ;
else if ( params_rate ( params ) < = 96000 )
shift = 2 ;
else
shift = 4 ;
return OXYGEN_I2S_MCLK ( mclks > > shift ) ;
}
2007-12-23 19:50:57 +01:00
static int oxygen_rec_a_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * hw_params )
{
struct oxygen * chip = snd_pcm_substream_chip ( substream ) ;
int err ;
err = oxygen_hw_params ( substream , hw_params ) ;
if ( err < 0 )
return err ;
spin_lock_irq ( & chip - > reg_lock ) ;
oxygen_write8_masked ( chip , OXYGEN_REC_FORMAT ,
oxygen_format ( hw_params ) < < OXYGEN_REC_FORMAT_A_SHIFT ,
OXYGEN_REC_FORMAT_A_MASK ) ;
2008-01-17 09:05:09 +01:00
oxygen_write16_masked ( chip , OXYGEN_I2S_A_FORMAT ,
oxygen_rate ( hw_params ) |
2008-09-22 08:55:19 +02:00
chip - > model . adc_i2s_format |
2011-01-10 16:14:52 +01:00
get_mclk ( chip , PCM_A , hw_params ) |
2008-01-17 09:05:09 +01:00
oxygen_i2s_bits ( hw_params ) ,
OXYGEN_I2S_RATE_MASK |
OXYGEN_I2S_FORMAT_MASK |
2008-01-18 09:17:53 +01:00
OXYGEN_I2S_MCLK_MASK |
2008-01-17 09:05:09 +01:00
OXYGEN_I2S_BITS_MASK ) ;
2007-12-23 19:50:57 +01:00
spin_unlock_irq ( & chip - > reg_lock ) ;
mutex_lock ( & chip - > mutex ) ;
2008-09-22 08:55:19 +02:00
chip - > model . set_adc_params ( chip , hw_params ) ;
2007-12-23 19:50:57 +01:00
mutex_unlock ( & chip - > mutex ) ;
return 0 ;
}
static int oxygen_rec_b_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * hw_params )
{
struct oxygen * chip = snd_pcm_substream_chip ( substream ) ;
2008-01-28 08:35:47 +01:00
int is_ac97 ;
2007-12-23 19:50:57 +01:00
int err ;
err = oxygen_hw_params ( substream , hw_params ) ;
if ( err < 0 )
return err ;
2008-01-28 08:35:47 +01:00
is_ac97 = chip - > has_ac97_1 & &
2008-09-22 09:02:08 +02:00
( chip - > model . device_config & CAPTURE_2_FROM_AC97_1 ) ;
2008-01-28 08:35:47 +01:00
2007-12-23 19:50:57 +01:00
spin_lock_irq ( & chip - > reg_lock ) ;
oxygen_write8_masked ( chip , OXYGEN_REC_FORMAT ,
oxygen_format ( hw_params ) < < OXYGEN_REC_FORMAT_B_SHIFT ,
OXYGEN_REC_FORMAT_B_MASK ) ;
2008-01-28 08:35:47 +01:00
if ( ! is_ac97 )
oxygen_write16_masked ( chip , OXYGEN_I2S_B_FORMAT ,
oxygen_rate ( hw_params ) |
2008-09-22 08:55:19 +02:00
chip - > model . adc_i2s_format |
2011-01-10 16:14:52 +01:00
get_mclk ( chip , PCM_B , hw_params ) |
2008-01-28 08:35:47 +01:00
oxygen_i2s_bits ( hw_params ) ,
OXYGEN_I2S_RATE_MASK |
OXYGEN_I2S_FORMAT_MASK |
OXYGEN_I2S_MCLK_MASK |
OXYGEN_I2S_BITS_MASK ) ;
2007-12-23 19:50:57 +01:00
spin_unlock_irq ( & chip - > reg_lock ) ;
2008-01-28 08:35:47 +01:00
if ( ! is_ac97 ) {
mutex_lock ( & chip - > mutex ) ;
2008-09-22 08:55:19 +02:00
chip - > model . set_adc_params ( chip , hw_params ) ;
2008-01-28 08:35:47 +01:00
mutex_unlock ( & chip - > mutex ) ;
}
2007-12-23 19:50:57 +01:00
return 0 ;
}
static int oxygen_rec_c_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * hw_params )
{
struct oxygen * chip = snd_pcm_substream_chip ( substream ) ;
2015-01-16 22:15:13 +01:00
bool is_spdif ;
2007-12-23 19:50:57 +01:00
int err ;
err = oxygen_hw_params ( substream , hw_params ) ;
if ( err < 0 )
return err ;
2015-01-16 22:15:13 +01:00
is_spdif = chip - > model . device_config & CAPTURE_1_FROM_SPDIF ;
2007-12-23 19:50:57 +01:00
spin_lock_irq ( & chip - > reg_lock ) ;
oxygen_write8_masked ( chip , OXYGEN_REC_FORMAT ,
oxygen_format ( hw_params ) < < OXYGEN_REC_FORMAT_C_SHIFT ,
OXYGEN_REC_FORMAT_C_MASK ) ;
2015-01-16 22:15:13 +01:00
if ( ! is_spdif )
oxygen_write16_masked ( chip , OXYGEN_I2S_C_FORMAT ,
oxygen_rate ( hw_params ) |
chip - > model . adc_i2s_format |
get_mclk ( chip , PCM_B , hw_params ) |
oxygen_i2s_bits ( hw_params ) ,
OXYGEN_I2S_RATE_MASK |
OXYGEN_I2S_FORMAT_MASK |
OXYGEN_I2S_MCLK_MASK |
OXYGEN_I2S_BITS_MASK ) ;
2007-12-23 19:50:57 +01:00
spin_unlock_irq ( & chip - > reg_lock ) ;
2015-01-16 22:15:13 +01:00
if ( ! is_spdif ) {
mutex_lock ( & chip - > mutex ) ;
chip - > model . set_adc_params ( chip , hw_params ) ;
mutex_unlock ( & chip - > mutex ) ;
}
2007-12-23 19:50:57 +01:00
return 0 ;
}
static int oxygen_spdif_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * hw_params )
{
struct oxygen * chip = snd_pcm_substream_chip ( substream ) ;
int err ;
err = oxygen_hw_params ( substream , hw_params ) ;
if ( err < 0 )
return err ;
2009-09-28 11:16:41 +02:00
mutex_lock ( & chip - > mutex ) ;
2007-12-23 19:50:57 +01:00
spin_lock_irq ( & chip - > reg_lock ) ;
oxygen_clear_bits32 ( chip , OXYGEN_SPDIF_CONTROL ,
OXYGEN_SPDIF_OUT_ENABLE ) ;
oxygen_write8_masked ( chip , OXYGEN_PLAY_FORMAT ,
oxygen_format ( hw_params ) < < OXYGEN_SPDIF_FORMAT_SHIFT ,
OXYGEN_SPDIF_FORMAT_MASK ) ;
oxygen_write32_masked ( chip , OXYGEN_SPDIF_CONTROL ,
oxygen_rate ( hw_params ) < < OXYGEN_SPDIF_OUT_RATE_SHIFT ,
OXYGEN_SPDIF_OUT_RATE_MASK ) ;
oxygen_update_spdif_source ( chip ) ;
spin_unlock_irq ( & chip - > reg_lock ) ;
2009-09-28 11:16:41 +02:00
mutex_unlock ( & chip - > mutex ) ;
2007-12-23 19:50:57 +01:00
return 0 ;
}
static int oxygen_multich_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * hw_params )
{
struct oxygen * chip = snd_pcm_substream_chip ( substream ) ;
int err ;
err = oxygen_hw_params ( substream , hw_params ) ;
if ( err < 0 )
return err ;
2009-09-28 11:16:41 +02:00
mutex_lock ( & chip - > mutex ) ;
2007-12-23 19:50:57 +01:00
spin_lock_irq ( & chip - > reg_lock ) ;
oxygen_write8_masked ( chip , OXYGEN_PLAY_CHANNELS ,
oxygen_play_channels ( hw_params ) ,
OXYGEN_PLAY_CHANNELS_MASK ) ;
oxygen_write8_masked ( chip , OXYGEN_PLAY_FORMAT ,
oxygen_format ( hw_params ) < < OXYGEN_MULTICH_FORMAT_SHIFT ,
OXYGEN_MULTICH_FORMAT_MASK ) ;
oxygen_write16_masked ( chip , OXYGEN_I2S_MULTICH_FORMAT ,
2008-01-17 09:05:09 +01:00
oxygen_rate ( hw_params ) |
2008-09-22 08:55:19 +02:00
chip - > model . dac_i2s_format |
2011-01-10 16:14:52 +01:00
get_mclk ( chip , PCM_MULTICH , hw_params ) |
2008-01-17 09:05:09 +01:00
oxygen_i2s_bits ( hw_params ) ,
OXYGEN_I2S_RATE_MASK |
OXYGEN_I2S_FORMAT_MASK |
2009-09-01 08:23:58 +02:00
OXYGEN_I2S_MCLK_MASK |
2008-01-17 09:05:09 +01:00
OXYGEN_I2S_BITS_MASK ) ;
2007-12-23 19:50:57 +01:00
oxygen_update_spdif_source ( chip ) ;
spin_unlock_irq ( & chip - > reg_lock ) ;
2008-09-22 08:55:19 +02:00
chip - > model . set_dac_params ( chip , hw_params ) ;
2009-09-28 11:16:41 +02:00
oxygen_update_dac_routing ( chip ) ;
2007-12-23 19:50:57 +01:00
mutex_unlock ( & chip - > mutex ) ;
return 0 ;
}
static int oxygen_hw_free ( struct snd_pcm_substream * substream )
{
struct oxygen * chip = snd_pcm_substream_chip ( substream ) ;
2008-01-04 09:22:20 +01:00
unsigned int channel = oxygen_substream_channel ( substream ) ;
2009-05-25 10:05:00 +02:00
unsigned int channel_mask = 1 < < channel ;
2007-12-23 19:50:57 +01:00
spin_lock_irq ( & chip - > reg_lock ) ;
2009-05-25 10:05:00 +02:00
chip - > interrupt_mask & = ~ channel_mask ;
2007-12-23 19:50:57 +01:00
oxygen_write16 ( chip , OXYGEN_INTERRUPT_MASK , chip - > interrupt_mask ) ;
2009-05-25 10:05:00 +02:00
oxygen_set_bits8 ( chip , OXYGEN_DMA_FLUSH , channel_mask ) ;
oxygen_clear_bits8 ( chip , OXYGEN_DMA_FLUSH , channel_mask ) ;
2007-12-23 19:50:57 +01:00
spin_unlock_irq ( & chip - > reg_lock ) ;
return snd_pcm_lib_free_pages ( substream ) ;
}
static int oxygen_spdif_hw_free ( struct snd_pcm_substream * substream )
{
struct oxygen * chip = snd_pcm_substream_chip ( substream ) ;
spin_lock_irq ( & chip - > reg_lock ) ;
oxygen_clear_bits32 ( chip , OXYGEN_SPDIF_CONTROL ,
OXYGEN_SPDIF_OUT_ENABLE ) ;
spin_unlock_irq ( & chip - > reg_lock ) ;
return oxygen_hw_free ( substream ) ;
}
static int oxygen_prepare ( struct snd_pcm_substream * substream )
{
struct oxygen * chip = snd_pcm_substream_chip ( substream ) ;
2008-01-04 09:22:20 +01:00
unsigned int channel = oxygen_substream_channel ( substream ) ;
2007-12-23 19:50:57 +01:00
unsigned int channel_mask = 1 < < channel ;
spin_lock_irq ( & chip - > reg_lock ) ;
oxygen_set_bits8 ( chip , OXYGEN_DMA_FLUSH , channel_mask ) ;
oxygen_clear_bits8 ( chip , OXYGEN_DMA_FLUSH , channel_mask ) ;
2010-11-15 10:50:37 +01:00
if ( substream - > runtime - > no_period_wakeup )
chip - > interrupt_mask & = ~ channel_mask ;
else
chip - > interrupt_mask | = channel_mask ;
2007-12-23 19:50:57 +01:00
oxygen_write16 ( chip , OXYGEN_INTERRUPT_MASK , chip - > interrupt_mask ) ;
spin_unlock_irq ( & chip - > reg_lock ) ;
return 0 ;
}
static int oxygen_trigger ( struct snd_pcm_substream * substream , int cmd )
{
struct oxygen * chip = snd_pcm_substream_chip ( substream ) ;
struct snd_pcm_substream * s ;
unsigned int mask = 0 ;
2008-01-21 08:44:52 +01:00
int pausing ;
2007-12-23 19:50:57 +01:00
switch ( cmd ) {
case SNDRV_PCM_TRIGGER_STOP :
case SNDRV_PCM_TRIGGER_START :
2008-05-13 09:24:39 +02:00
case SNDRV_PCM_TRIGGER_SUSPEND :
2008-01-21 08:44:52 +01:00
pausing = 0 ;
break ;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH :
2007-12-23 19:50:57 +01:00
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE :
2008-01-21 08:44:52 +01:00
pausing = 1 ;
2007-12-23 19:50:57 +01:00
break ;
default :
return - EINVAL ;
}
snd_pcm_group_for_each_entry ( s , substream ) {
if ( snd_pcm_substream_chip ( s ) = = chip ) {
2008-01-04 09:22:20 +01:00
mask | = 1 < < oxygen_substream_channel ( s ) ;
2007-12-23 19:50:57 +01:00
snd_pcm_trigger_done ( s , substream ) ;
}
}
spin_lock ( & chip - > reg_lock ) ;
2008-01-21 08:44:52 +01:00
if ( ! pausing ) {
if ( cmd = = SNDRV_PCM_TRIGGER_START )
chip - > pcm_running | = mask ;
else
chip - > pcm_running & = ~ mask ;
oxygen_write8 ( chip , OXYGEN_DMA_STATUS , chip - > pcm_running ) ;
} else {
if ( cmd = = SNDRV_PCM_TRIGGER_PAUSE_PUSH )
oxygen_set_bits8 ( chip , OXYGEN_DMA_PAUSE , mask ) ;
else
oxygen_clear_bits8 ( chip , OXYGEN_DMA_PAUSE , mask ) ;
}
2007-12-23 19:50:57 +01:00
spin_unlock ( & chip - > reg_lock ) ;
return 0 ;
}
static snd_pcm_uframes_t oxygen_pointer ( struct snd_pcm_substream * substream )
{
struct oxygen * chip = snd_pcm_substream_chip ( substream ) ;
struct snd_pcm_runtime * runtime = substream - > runtime ;
2008-01-04 09:22:20 +01:00
unsigned int channel = oxygen_substream_channel ( substream ) ;
2007-12-23 19:50:57 +01:00
u32 curr_addr ;
/* no spinlock, this read should be atomic */
curr_addr = oxygen_read32 ( chip , channel_base_registers [ channel ] ) ;
return bytes_to_frames ( runtime , curr_addr - ( u32 ) runtime - > dma_addr ) ;
}
2016-09-02 00:13:13 +02:00
static const struct snd_pcm_ops oxygen_rec_a_ops = {
2007-12-23 19:50:57 +01:00
. open = oxygen_rec_a_open ,
. close = oxygen_close ,
. ioctl = snd_pcm_lib_ioctl ,
. hw_params = oxygen_rec_a_hw_params ,
. hw_free = oxygen_hw_free ,
. prepare = oxygen_prepare ,
. trigger = oxygen_trigger ,
. pointer = oxygen_pointer ,
} ;
2016-09-02 00:13:13 +02:00
static const struct snd_pcm_ops oxygen_rec_b_ops = {
2007-12-23 19:50:57 +01:00
. open = oxygen_rec_b_open ,
. close = oxygen_close ,
. ioctl = snd_pcm_lib_ioctl ,
. hw_params = oxygen_rec_b_hw_params ,
. hw_free = oxygen_hw_free ,
. prepare = oxygen_prepare ,
. trigger = oxygen_trigger ,
. pointer = oxygen_pointer ,
} ;
2016-09-02 00:13:13 +02:00
static const struct snd_pcm_ops oxygen_rec_c_ops = {
2007-12-23 19:50:57 +01:00
. open = oxygen_rec_c_open ,
. close = oxygen_close ,
. ioctl = snd_pcm_lib_ioctl ,
. hw_params = oxygen_rec_c_hw_params ,
. hw_free = oxygen_hw_free ,
. prepare = oxygen_prepare ,
. trigger = oxygen_trigger ,
. pointer = oxygen_pointer ,
} ;
2016-09-02 00:13:13 +02:00
static const struct snd_pcm_ops oxygen_spdif_ops = {
2007-12-23 19:50:57 +01:00
. open = oxygen_spdif_open ,
. close = oxygen_close ,
. ioctl = snd_pcm_lib_ioctl ,
. hw_params = oxygen_spdif_hw_params ,
. hw_free = oxygen_spdif_hw_free ,
. prepare = oxygen_prepare ,
. trigger = oxygen_trigger ,
. pointer = oxygen_pointer ,
} ;
2016-09-02 00:13:13 +02:00
static const struct snd_pcm_ops oxygen_multich_ops = {
2007-12-23 19:50:57 +01:00
. open = oxygen_multich_open ,
. close = oxygen_close ,
. ioctl = snd_pcm_lib_ioctl ,
. hw_params = oxygen_multich_hw_params ,
. hw_free = oxygen_hw_free ,
. prepare = oxygen_prepare ,
. trigger = oxygen_trigger ,
. pointer = oxygen_pointer ,
} ;
2016-09-02 00:13:13 +02:00
static const struct snd_pcm_ops oxygen_ac97_ops = {
2007-12-23 19:50:57 +01:00
. open = oxygen_ac97_open ,
. close = oxygen_close ,
. ioctl = snd_pcm_lib_ioctl ,
2008-01-18 09:17:53 +01:00
. hw_params = oxygen_hw_params ,
2007-12-23 19:50:57 +01:00
. hw_free = oxygen_hw_free ,
. prepare = oxygen_prepare ,
. trigger = oxygen_trigger ,
. pointer = oxygen_pointer ,
} ;
2008-02-22 18:35:22 +01:00
int oxygen_pcm_init ( struct oxygen * chip )
2007-12-23 19:50:57 +01:00
{
struct snd_pcm * pcm ;
2008-01-16 08:30:38 +01:00
int outs , ins ;
2007-12-23 19:50:57 +01:00
int err ;
2008-09-22 09:02:08 +02:00
outs = ! ! ( chip - > model . device_config & PLAYBACK_0_TO_I2S ) ;
ins = ! ! ( chip - > model . device_config & ( CAPTURE_0_FROM_I2S_1 |
CAPTURE_0_FROM_I2S_2 ) ) ;
2008-03-19 08:19:41 +01:00
if ( outs | ins ) {
2008-09-22 09:07:53 +02:00
err = snd_pcm_new ( chip - > card , " Multichannel " ,
0 , outs , ins , & pcm ) ;
2008-03-19 08:19:41 +01:00
if ( err < 0 )
return err ;
if ( outs )
snd_pcm_set_ops ( pcm , SNDRV_PCM_STREAM_PLAYBACK ,
& oxygen_multich_ops ) ;
2008-09-22 09:02:08 +02:00
if ( chip - > model . device_config & CAPTURE_0_FROM_I2S_1 )
2008-03-19 08:19:41 +01:00
snd_pcm_set_ops ( pcm , SNDRV_PCM_STREAM_CAPTURE ,
& oxygen_rec_a_ops ) ;
2008-09-22 09:02:08 +02:00
else if ( chip - > model . device_config & CAPTURE_0_FROM_I2S_2 )
2008-03-19 08:19:41 +01:00
snd_pcm_set_ops ( pcm , SNDRV_PCM_STREAM_CAPTURE ,
& oxygen_rec_b_ops ) ;
pcm - > private_data = chip ;
2008-09-22 09:07:53 +02:00
strcpy ( pcm - > name , " Multichannel " ) ;
2008-03-19 08:19:41 +01:00
if ( outs )
snd_pcm_lib_preallocate_pages ( pcm - > streams [ SNDRV_PCM_STREAM_PLAYBACK ] . substream ,
SNDRV_DMA_TYPE_DEV ,
snd_dma_pci_data ( chip - > pci ) ,
2008-05-13 09:25:39 +02:00
DEFAULT_BUFFER_BYTES_MULTICH ,
BUFFER_BYTES_MAX_MULTICH ) ;
2008-03-19 08:19:41 +01:00
if ( ins )
snd_pcm_lib_preallocate_pages ( pcm - > streams [ SNDRV_PCM_STREAM_CAPTURE ] . substream ,
SNDRV_DMA_TYPE_DEV ,
snd_dma_pci_data ( chip - > pci ) ,
2008-05-13 09:25:39 +02:00
DEFAULT_BUFFER_BYTES ,
BUFFER_BYTES_MAX ) ;
2008-03-19 08:19:41 +01:00
}
2008-09-22 09:02:08 +02:00
outs = ! ! ( chip - > model . device_config & PLAYBACK_1_TO_SPDIF ) ;
ins = ! ! ( chip - > model . device_config & CAPTURE_1_FROM_SPDIF ) ;
2008-01-16 08:30:38 +01:00
if ( outs | ins ) {
err = snd_pcm_new ( chip - > card , " Digital " , 1 , outs , ins , & pcm ) ;
if ( err < 0 )
return err ;
if ( outs )
snd_pcm_set_ops ( pcm , SNDRV_PCM_STREAM_PLAYBACK ,
& oxygen_spdif_ops ) ;
if ( ins )
snd_pcm_set_ops ( pcm , SNDRV_PCM_STREAM_CAPTURE ,
& oxygen_rec_c_ops ) ;
pcm - > private_data = chip ;
strcpy ( pcm - > name , " Digital " ) ;
snd_pcm_lib_preallocate_pages_for_all ( pcm , SNDRV_DMA_TYPE_DEV ,
snd_dma_pci_data ( chip - > pci ) ,
2008-05-13 09:25:39 +02:00
DEFAULT_BUFFER_BYTES ,
BUFFER_BYTES_MAX ) ;
2008-01-16 08:30:38 +01:00
}
2008-03-19 08:19:41 +01:00
if ( chip - > has_ac97_1 ) {
2008-09-22 09:02:08 +02:00
outs = ! ! ( chip - > model . device_config & PLAYBACK_2_TO_AC97_1 ) ;
ins = ! ! ( chip - > model . device_config & CAPTURE_2_FROM_AC97_1 ) ;
2008-03-19 08:19:41 +01:00
} else {
outs = 0 ;
2008-09-22 09:02:08 +02:00
ins = ! ! ( chip - > model . device_config & CAPTURE_2_FROM_I2S_2 ) ;
2008-03-19 08:19:41 +01:00
}
2008-01-16 08:30:38 +01:00
if ( outs | ins ) {
2008-01-28 08:35:47 +01:00
err = snd_pcm_new ( chip - > card , outs ? " AC97 " : " Analog2 " ,
2008-01-16 08:30:38 +01:00
2 , outs , ins , & pcm ) ;
2007-12-23 19:50:57 +01:00
if ( err < 0 )
return err ;
2008-01-28 08:35:47 +01:00
if ( outs ) {
2008-01-16 08:30:38 +01:00
snd_pcm_set_ops ( pcm , SNDRV_PCM_STREAM_PLAYBACK ,
& oxygen_ac97_ops ) ;
2008-01-28 08:35:47 +01:00
oxygen_write8_masked ( chip , OXYGEN_REC_ROUTING ,
OXYGEN_REC_B_ROUTE_AC97_1 ,
OXYGEN_REC_B_ROUTE_MASK ) ;
}
2008-01-16 08:30:38 +01:00
if ( ins )
snd_pcm_set_ops ( pcm , SNDRV_PCM_STREAM_CAPTURE ,
& oxygen_rec_b_ops ) ;
2007-12-23 19:50:57 +01:00
pcm - > private_data = chip ;
2008-01-28 08:35:47 +01:00
strcpy ( pcm - > name , outs ? " Front Panel " : " Analog 2 " ) ;
2007-12-23 19:50:57 +01:00
snd_pcm_lib_preallocate_pages_for_all ( pcm , SNDRV_DMA_TYPE_DEV ,
snd_dma_pci_data ( chip - > pci ) ,
2008-05-13 09:25:39 +02:00
DEFAULT_BUFFER_BYTES ,
BUFFER_BYTES_MAX ) ;
2007-12-23 19:50:57 +01:00
}
2015-01-16 22:15:13 +01:00
ins = ! ! ( chip - > model . device_config & CAPTURE_3_FROM_I2S_3 ) ;
if ( ins ) {
err = snd_pcm_new ( chip - > card , " Analog3 " , 3 , 0 , ins , & pcm ) ;
if ( err < 0 )
return err ;
snd_pcm_set_ops ( pcm , SNDRV_PCM_STREAM_CAPTURE ,
& oxygen_rec_c_ops ) ;
oxygen_write8_masked ( chip , OXYGEN_REC_ROUTING ,
OXYGEN_REC_C_ROUTE_I2S_ADC_3 ,
OXYGEN_REC_C_ROUTE_MASK ) ;
pcm - > private_data = chip ;
strcpy ( pcm - > name , " Analog 3 " ) ;
snd_pcm_lib_preallocate_pages_for_all ( pcm , SNDRV_DMA_TYPE_DEV ,
snd_dma_pci_data ( chip - > pci ) ,
DEFAULT_BUFFER_BYTES ,
BUFFER_BYTES_MAX ) ;
}
2007-12-23 19:50:57 +01:00
return 0 ;
}