📄 s3c24xx-uda1341.c
字号:
static const char *AGC_level[] = {
"-9.0", "-11.5", "-15.0", "-17.5",
};
static const char *ig_small_value[] = {
"-3.0", "-2.5", "-2.0", "-1.5", "-1.0", "-0.5",
};
/*
* this was computed as peak_value[i] = pow((63-i)*1.42,1.013)
*
* UDA1341 datasheet on page 21: Peak value (dB) = (Peak level - 63.5)*5*log2
* There is an table with these values [level]=value: [3]=-90.31, [7]=-84.29
* [61]=-2.78, [62] = -1.48, [63] = 0.0
* I tried to compute it, but using but even using logarithm with base either 10 or 2
* i was'n able to get values in the table from the formula. So I constructed another
* formula (see above) to interpolate the values as good as possible. If there is some
* mistake, please contact me on tomas.kasparek@seznam.cz. Thanks.
* UDA1341TS datasheet is available at:
* http://www-us9.semiconductors.com/acrobat/datasheets/UDA1341TS_3.pdf
*/
static const char *peak_value[] = {
"-INF dB", "N.A.", "N.A", "90.31 dB", "N.A.", "N.A.", "N.A.", "-84.29 dB",
"-82.65 dB", "-81.13 dB", "-79.61 dB", "-78.09 dB", "-76.57 dB", "-75.05 dB", "-73.53 dB",
"-72.01 dB", "-70.49 dB", "-68.97 dB", "-67.45 dB", "-65.93 dB", "-64.41 dB", "-62.90 dB",
"-61.38 dB", "-59.86 dB", "-58.35 dB", "-56.83 dB", "-55.32 dB", "-53.80 dB", "-52.29 dB",
"-50.78 dB", "-49.26 dB", "-47.75 dB", "-46.24 dB", "-44.73 dB", "-43.22 dB", "-41.71 dB",
"-40.20 dB", "-38.69 dB", "-37.19 dB", "-35.68 dB", "-34.17 dB", "-32.67 dB", "-31.17 dB",
"-29.66 dB", "-28.16 dB", "-26.66 dB", "-25.16 dB", "-23.66 dB", "-22.16 dB", "-20.67 dB",
"-19.17 dB", "-17.68 dB", "-16.19 dB", "-14.70 dB", "-13.21 dB", "-11.72 dB", "-10.24 dB",
"-8.76 dB", "-7.28 dB", "-5.81 dB", "-4.34 dB", "-2.88 dB", "-1.43 dB", "0.00 dB",
};
static void snd_uda1341_proc_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct s3c24xx_uda1341 *chip = entry -> private_data;
struct uda1341 *uda = chip -> chip_data;
int peak;
peak = snd_uda1341_codec_read( chip, UDA1341_REG_DATA1 );
if (peak < 0)
peak = 0;
snd_iprintf(buffer, "%s\n\n", chip -> card -> longname);
// for information about computed values see UDA1341TS datasheet pages 15 - 21
snd_iprintf(buffer, "DAC power : %s\n", uda->cfg[CMD_DAC] ? "on" : "off");
snd_iprintf(buffer, "ADC power : %s\n", uda->cfg[CMD_ADC] ? "on" : "off");
snd_iprintf(buffer, "Clock frequency : %s\n", fs_names[uda->cfg[CMD_FS]]);
snd_iprintf(buffer, "Data format : %s\n\n", format_names[uda->cfg[CMD_FORMAT]]);
snd_iprintf(buffer, "Filter mode : %s\n", filter_names[uda->cfg[CMD_FILTER]]);
snd_iprintf(buffer, "Mixer mode : %s\n", mixer_names[uda->cfg[CMD_MIXER]]);
snd_iprintf(buffer, "De-emphasis : %s\n", deemp_names[uda->cfg[CMD_DEEMP]]);
snd_iprintf(buffer, "Peak detection pos. : %s\n", uda->cfg[CMD_PEAK] ? "after" : "before");
snd_iprintf(buffer, "Peak value : %s\n\n", peak_value[peak]);
snd_iprintf(buffer, "Automatic Gain Ctrl : %s\n", uda->cfg[CMD_AGC] ? "on" : "off");
snd_iprintf(buffer, "AGC attack time : %d ms\n", AGC_atime[uda->cfg[CMD_AGC_TIME]]);
snd_iprintf(buffer, "AGC decay time : %d ms\n", AGC_dtime[uda->cfg[CMD_AGC_TIME]]);
snd_iprintf(buffer, "AGC output level : %s dB\n\n", AGC_level[uda->cfg[CMD_AGC_LEVEL]]);
snd_iprintf(buffer, "Mute : %s\n", uda->cfg[CMD_MUTE] ? "on" : "off");
if (uda->cfg[CMD_VOLUME] == 0)
snd_iprintf(buffer, "Volume : 0 dB\n");
else if (uda->cfg[CMD_VOLUME] < 62)
snd_iprintf(buffer, "Volume : %d dB\n", -1*uda->cfg[CMD_VOLUME] +1);
else
snd_iprintf(buffer, "Volume : -INF dB\n");
snd_iprintf(buffer, "Bass : %s\n", bass_values[uda->cfg[CMD_FILTER]][uda->cfg[CMD_BASS]]);
snd_iprintf(buffer, "Trebble : %d dB\n", uda->cfg[CMD_FILTER] ? 2*uda->cfg[CMD_TREBBLE] : 0);
snd_iprintf(buffer, "Input Gain (6dB) : %s\n", uda->cfg[CMD_IGAIN] ? "on" : "off");
snd_iprintf(buffer, "Output Gain (6dB) : %s\n", uda->cfg[CMD_OGAIN] ? "on" : "off");
snd_iprintf(buffer, "Mic sensitivity : %s\n", mic_sens_value[uda->cfg[CMD_MIC]]);
if(uda->cfg[CMD_CH1] < 31)
snd_iprintf(buffer, "Mixer gain channel 1: -%d.%c dB\n",
((uda->cfg[CMD_CH1] >> 1) * 3) + (uda->cfg[CMD_CH1] & 1),
uda->cfg[CMD_CH1] & 1 ? '5' : '0');
else
snd_iprintf(buffer, "Mixer gain channel 1: -INF dB\n");
if(uda->cfg[CMD_CH2] < 31)
snd_iprintf(buffer, "Mixer gain channel 2: -%d.%c dB\n",
((uda->cfg[CMD_CH2] >> 1) * 3) + (uda->cfg[CMD_CH2] & 1),
uda->cfg[CMD_CH2] & 1 ? '5' : '0');
else
snd_iprintf(buffer, "Mixer gain channel 2: -INF dB\n");
if(uda->cfg[CMD_IG] > 5)
snd_iprintf(buffer, "Input Amp. Gain ch 2: %d.%c dB\n",
(uda->cfg[CMD_IG] >> 1) -3, uda->cfg[CMD_IG] & 1 ? '5' : '0');
else
snd_iprintf(buffer, "Input Amp. Gain ch 2: %s dB\n", ig_small_value[uda->cfg[CMD_IG]]);
}
static void snd_uda1341_proc_regs_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct s3c24xx_uda1341 *chip = entry -> private_data;
struct uda1341 *uda = chip -> chip_data;
int reg;
char buf[12];
for (reg = 0; reg < uda1341_reg_last; reg ++) {
if (reg == empty)
continue;
int2str_bin8(uda->regs[reg], buf);
snd_iprintf(buffer, "%s = %s\n", uda1341_reg_names[reg], buf);
}
#if 0
int2str_bin8(snd_uda1341_codec_read( chip, UDA1341_REG_DATA1 ), buf);
snd_iprintf(buffer, "DATA1 = %s\n", buf);
#endif
}
static void __devinit snd_chip_uda1341_proc_init( struct snd_card *card, struct s3c24xx_uda1341 *chip )
{
struct snd_info_entry *entry;
if ( ! snd_card_proc_new( card, "uda1341", &entry ) )
{
snd_info_set_text_ops( entry, chip, snd_uda1341_proc_read );
}
if ( ! snd_card_proc_new( card, "uda1341-regs", &entry ) )
{
snd_info_set_text_ops( entry, chip, snd_uda1341_proc_regs_read );
}
}
#endif /* CONFIG_PROC_FS */
/*
* S3C24XX DMA Controller Material
*/
#if 1
static void audio_dma_done_callback ( s3c2410_dma_chan_t *ch, void *buf, int size, s3c2410_dma_buffresult_t result );
static int audio_init_dma ( audio_stream_t * s, char *desc )
{
int ret ;
s3c2410_dmasrc_t source;
int hwcfg;
unsigned long devaddr;
int dcon;
unsigned int flags = 0;
int reqret;
if ( s->dma_ch == DMA_CH2 )
{
source = S3C2410_DMASRC_MEM;
hwcfg = 3;
devaddr = 0x55000010;
dcon = S3C2410_DCON_HANDSHAKE | S3C2410_DCON_SYNC_PCLK | S3C2410_DCON_CH2_I2SSDO ;
flags = S3C2410_DMAF_AUTOSTART;
reqret = s3c2410_dma_request ( s->dma_ch, &s3c24xxiis_dma_client_out, NULL );
if( !reqret )
{
dbg( dbg_err, "failed to get dma channel\n" );
return reqret;
}
ret = s3c2410_dma_devconfig ( s->dma_ch, source, hwcfg, devaddr );
ret = s3c2410_dma_config ( s->dma_ch, 2, dcon );
ret = s3c2410_dma_set_buffdone_fn ( s->dma_ch, audio_dma_done_callback );
ret = s3c2410_dma_setflags ( s->dma_ch, flags );
return reqret;
}
else if ( s->dma_ch == DMA_CH1 )
{
source = S3C2410_DMASRC_HW;
hwcfg = 3;
devaddr = 0x55000010;
dcon = S3C2410_DCON_HANDSHAKE | S3C2410_DCON_CH1_I2SSDI | S3C2410_DCON_HWTRIG;
flags = S3C2410_DMAF_AUTOSTART;
reqret = s3c2410_dma_request ( s->dma_ch, &s3c24xxiis_dma_client_in, NULL );
if( !reqret )
{
dbg( dbg_err, "failed to get dma channel\n" );
return reqret;
}
ret = s3c2410_dma_devconfig ( s->dma_ch, source, hwcfg, devaddr );
ret = s3c2410_dma_config ( s->dma_ch, 2, dcon );
ret = s3c2410_dma_set_buffdone_fn ( s->dma_ch, audio_dma_done_callback );
ret = s3c2410_dma_setflags ( s->dma_ch, flags );
return reqret ;
}
else
return 1;
}
static int audio_clear_dma ( audio_stream_t * s, s3c2410_dma_client_t *client )
{
s3c2410_dma_set_buffdone_fn( s->dma_ch, NULL );
s3c2410_dma_free( s -> dma_ch, client );
s -> dma_ch = -1;
return 0;
}
static u_int audio_get_dma_pos( struct audio_stream *s )
{
struct snd_pcm_substream *substream = s -> stream;
struct snd_pcm_runtime *runtime = substream -> runtime;
int stream_id = substream -> pstr -> stream;
unsigned int offset;
unsigned long flags;
dma_addr_t srtaddr, dstaddr;
/*
* FIXME Is it neccessary to call interrupts locked
*/
spin_lock_irqsave( &s -> dma_lock, flags );
s3c2410_dma_getposition( s -> dma_ch, &srtaddr, &dstaddr );
if ( stream_id == SNDRV_PCM_STREAM_PLAYBACK )
{
offset = srtaddr - runtime -> dma_addr;
}
else
{
offset = dstaddr - runtime -> dma_addr;
}
dbg( dbg_debug, PFX "audio_get_dma_pos srtaddr = 0x%x, dstaddr = 0x%x, runtime -> dma_addr = 0x%x, offset = 0x%x\n", srtaddr, dstaddr, runtime -> dma_addr, offset );
spin_unlock_irqrestore( &s -> dma_lock, flags );
offset = bytes_to_frames( runtime, offset );
if ( offset >= runtime -> buffer_size )
{
offset = 0;
}
return offset;
}
#if 0
static void dumpstream( struct snd_pcm_substream *substream , unsigned int offset , unsigned int size )
{
struct snd_dma_buffer *buffer = &substream -> dma_buffer;
unsigned int i;
for( i = offset; i < offset + size; i += 4 )
{
dbg( dbg_info, PFX "dump dma_buffer 0x%02x%02x%02x%02x\n", buffer -> area[ i ], buffer -> area[ i + 1 ], buffer -> area[ i + 2 ], buffer -> area[ i + 3 ] );
}
}
#endif
static void audio_process_dma( struct audio_stream *s )
{
struct snd_pcm_substream *substream = s -> stream;
struct snd_pcm_runtime *runtime;
unsigned int dma_size;
unsigned int offset;
/* FIXME I wonder the reason */
if ( s -> tx_spin )
{
printk("It gets here\n");
snd_assert( s -> stream_id == SNDRV_PCM_STREAM_PLAYBACK, return );
return;
}
runtime = substream -> runtime;
//dbg( dbg_debug, PFX "audio_process_dma start... runtime -> periods = %d, runtime -> dma_addr = 0x%x\n", runtime -> periods, runtime -> dma_addr );
while ( s -> active && s -> periods < runtime -> periods )
{
dma_size = frames_to_bytes( runtime, runtime -> period_size );
dbg( dbg_debug, PFX "audio_process_dma while... runtime -> period_size = %d, s -> period = %d\n", dma_size, s -> period );
if ( s -> old_offset )
{
offset = frames_to_bytes( runtime, s -> old_offset - 1 );
s -> old_offset = 0;
s -> periods = 0;
s -> period = offset / dma_size;
offset %= dma_size;
dma_size -= offset;
if ( !dma_size )
{
continue;
}
}
else
{
offset = dma_size * s -> period;
snd_assert( dma_size <= DMA_BUF_SIZE, );
}
//dumpstream( substream, offset, dma_size );
/* submit data to the DMA Engine */
s3c2410_dma_enqueue( s -> dma_ch, ( void * ) s, runtime -> dma_addr + offset, dma_size );
s -> period ++;
s -> period %= runtime -> periods;
s -> periods ++;
}
//dbg( dbg_debug, PFX "audio_process_dma exit... s -> periods = %d\n", s -> periods );
}
static void audio_dma_done_callback ( s3c2410_dma_chan_t *ch, void *buf, int size, s3c2410_dma_buffresult_t result )
{
struct audio_stream *s = ( struct audio_stream * )buf;
if ( s -> active )
{
snd_pcm_period_elapsed( s -> stream );
}
spin_lock( &s -> dma_lock );
//dbg( dbg_debug, PFX "audio_dma_done_callback s -> periods = %d\n", s -> periods );
if ( s -> active && ! s -> tx_spin && s -> periods > 0 )
{
s -> periods --;
audio_process_dma( s );
}
spin_unlock( &s -> dma_lock );
}
#endif
static struct snd_pcm_hardware snd_s3c24xx_uda1341_capture =
{
.info = (SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |\
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
SNDRV_PCM_RATE_KNOT),
.rate_min = 8000,
.rate_max = 48000,
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = 64*1024,
.period_bytes_min = 64,
.period_bytes_max = DMA_BUF_SIZE,
.periods_min = 2,
.periods_max = 255,
.fifo_size = 0,
};
static struct snd_pcm_hardware snd_s3c24xx_uda1341_playback =
{
.info = (SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |\
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
SNDRV_PCM_RATE_KNOT),
.rate_min = 8000,
.rate_max = 48000,
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = 64*1024,
.period_bytes_min = 64,
.period_bytes_max = DMA_BUF_SIZE,
.periods_min = 2,
.periods_max = 255,
.fifo_size = 0,
};
static int snd_card_s3c24xx_uda1341_open( struct snd_pcm_substream *substream )
{
struct s3c24xx_uda1341 *chip = snd_pcm_substream_chip( substream );
struct snd_pcm_runtime *runtime = substream -> runtime;
int stream_id = substream -> pstr -> stream;
int err;
chip -> s[ stream_id ].stream = substream;
if ( stream_id == SNDRV_PCM_STREAM_PLAYBACK )
{
runtime -> hw = snd_s3c24xx_uda1341_playback;
}
else
{
runtime -> hw = snd_s3c24xx_uda1341_capture;
}
if ( ( err = snd_pcm_hw_constraint_integer( runtime, SNDRV_PCM_HW_PARAM_PERIODS ) ) < 0 )
{
dbg( dbg_err, PFX "failed to set hw constraint integer\n" );
return err;
}
if ( ( err = snd_pcm_hw_constraint_list( runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates ) ) < 0 )
{
dbg( dbg_err, PFX "failed to set hw constraint list\n" );
return err;
}
return 0;
}
static int snd_card_s3c24xx_uda1341_close( struct snd_pcm_substream *substream )
{
struct s3c24xx_uda1341 *chip = snd_pcm_substream_chip( substream );
chip -> s[ substream -> pstr -> stream ].stream = NULL;
return 0;
}
/*
* HW params & free
*/
static int snd_s3c24xx_uda1341_hw_params( struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params )
{
return snd_pcm_lib_malloc_pages( substream, params_buffer_bytes( hw_params ) );
}
static int snd_s3c24xx_uda1341_hw_free( struct snd_pcm_substream *substream )
{
return snd_pcm_lib_free_pages( substream );
}
static int snd_s3c24xx_uda1341_prepare( struct snd_pcm_substream *substream )
{
struct s3c24xx_uda1341 *chip = snd_pcm_substream_chip( substream );
struct snd_pcm_runtime *runtime = substream -> runtime;
int stream_id = substream -> pstr -> stream;
struct audio_stream *s = &chip -> s[ stream_id ];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -