⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 s3c24xx-uda1341.c

📁 扬创2440平台上的ALSA音频驱动程序( for Linux-2.6.26.5 )。驱动使用说明和ALSA Lib的编译过程都在压缩包内
💻 C
📖 第 1 页 / 共 5 页
字号:

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 + -