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

📄 ad1816a_lib.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 2 页
字号:
/*    ad1816a.c - lowlevel code for Analog Devices AD1816A chip.    Copyright (C) 1999-2000 by Massimo Piccioni <dafastidio@libero.it>    This program is free software; you can redistribute it and/or modify    it under the terms of the GNU General Public License as published by    the Free Software Foundation; either version 2 of the License, or    (at your option) any later version.    This program 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 program; if not, write to the Free Software    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA*/#include <sound/driver.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/slab.h>#include <linux/ioport.h>#include <sound/core.h>#include <sound/ad1816a.h>#include <asm/io.h>#include <asm/dma.h>MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");MODULE_DESCRIPTION("lowlevel code for Analog Devices AD1816A chip");MODULE_LICENSE("GPL");static inline int snd_ad1816a_busy_wait(ad1816a_t *chip){	int timeout;	for (timeout = 1000; timeout-- > 0; udelay(10))		if (inb(AD1816A_REG(AD1816A_CHIP_STATUS)) & AD1816A_READY)			return 0;	snd_printk("chip busy.\n");	return -EBUSY;}static inline unsigned char snd_ad1816a_in(ad1816a_t *chip, unsigned char reg){	snd_ad1816a_busy_wait(chip);	return inb(AD1816A_REG(reg));}static inline void snd_ad1816a_out(ad1816a_t *chip, unsigned char reg,			    unsigned char value){	snd_ad1816a_busy_wait(chip);	outb(value, AD1816A_REG(reg));}static inline void snd_ad1816a_out_mask(ad1816a_t *chip, unsigned char reg,				 unsigned char mask, unsigned char value){	snd_ad1816a_out(chip, reg,		(value & mask) | (snd_ad1816a_in(chip, reg) & ~mask));}static unsigned short snd_ad1816a_read(ad1816a_t *chip, unsigned char reg){	snd_ad1816a_out(chip, AD1816A_INDIR_ADDR, reg & 0x3f);	return snd_ad1816a_in(chip, AD1816A_INDIR_DATA_LOW) |		(snd_ad1816a_in(chip, AD1816A_INDIR_DATA_HIGH) << 8);}static void snd_ad1816a_write(ad1816a_t *chip, unsigned char reg,			      unsigned short value){	snd_ad1816a_out(chip, AD1816A_INDIR_ADDR, reg & 0x3f);	snd_ad1816a_out(chip, AD1816A_INDIR_DATA_LOW, value & 0xff);	snd_ad1816a_out(chip, AD1816A_INDIR_DATA_HIGH, (value >> 8) & 0xff);}static void snd_ad1816a_write_mask(ad1816a_t *chip, unsigned char reg,				   unsigned short mask, unsigned short value){	snd_ad1816a_write(chip, reg,		(value & mask) | (snd_ad1816a_read(chip, reg) & ~mask));}static unsigned char snd_ad1816a_get_format(ad1816a_t *chip,					    unsigned int format, int channels){	unsigned char retval = AD1816A_FMT_LINEAR_8;	switch (format) {	case SNDRV_PCM_FORMAT_MU_LAW:		retval = AD1816A_FMT_ULAW_8;		break;	case SNDRV_PCM_FORMAT_A_LAW:		retval = AD1816A_FMT_ALAW_8;		break;	case SNDRV_PCM_FORMAT_S16_LE:		retval = AD1816A_FMT_LINEAR_16_LIT;		break;	case SNDRV_PCM_FORMAT_S16_BE:		retval = AD1816A_FMT_LINEAR_16_BIG;	}	return (channels > 1) ? (retval | AD1816A_FMT_STEREO) : retval;}static int snd_ad1816a_open(ad1816a_t *chip, unsigned int mode){	unsigned long flags;	spin_lock_irqsave(&chip->lock, flags);	if (chip->mode & mode) {		spin_unlock_irqrestore(&chip->lock, flags);		return -EAGAIN;	}	switch ((mode &= AD1816A_MODE_OPEN)) {	case AD1816A_MODE_PLAYBACK:		snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS,			AD1816A_PLAYBACK_IRQ_PENDING, 0x00);		snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE,			AD1816A_PLAYBACK_IRQ_ENABLE, 0xffff);		break;	case AD1816A_MODE_CAPTURE:		snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS,			AD1816A_CAPTURE_IRQ_PENDING, 0x00);		snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE,			AD1816A_CAPTURE_IRQ_ENABLE, 0xffff);		break;	case AD1816A_MODE_TIMER:		snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS,			AD1816A_TIMER_IRQ_PENDING, 0x00);		snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE,			AD1816A_TIMER_IRQ_ENABLE, 0xffff);	}	chip->mode |= mode;	spin_unlock_irqrestore(&chip->lock, flags);	return 0;}static void snd_ad1816a_close(ad1816a_t *chip, unsigned int mode){	unsigned long flags;	spin_lock_irqsave(&chip->lock, flags);	switch ((mode &= AD1816A_MODE_OPEN)) {	case AD1816A_MODE_PLAYBACK:		snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS,			AD1816A_PLAYBACK_IRQ_PENDING, 0x00);		snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE,			AD1816A_PLAYBACK_IRQ_ENABLE, 0x0000);		break;	case AD1816A_MODE_CAPTURE:		snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS,			AD1816A_CAPTURE_IRQ_PENDING, 0x00);		snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE,			AD1816A_CAPTURE_IRQ_ENABLE, 0x0000);		break;	case AD1816A_MODE_TIMER:		snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS,			AD1816A_TIMER_IRQ_PENDING, 0x00);		snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE,			AD1816A_TIMER_IRQ_ENABLE, 0x0000);	}	if (!((chip->mode &= ~mode) & AD1816A_MODE_OPEN))		chip->mode = 0;	spin_unlock_irqrestore(&chip->lock, flags);}static int snd_ad1816a_trigger(ad1816a_t *chip, unsigned char what,			       int channel, int cmd){	int error = 0;	switch (cmd) {	case SNDRV_PCM_TRIGGER_START:	case SNDRV_PCM_TRIGGER_STOP:		spin_lock(&chip->lock);		cmd = (cmd == SNDRV_PCM_TRIGGER_START) ? 0xff: 0x00;		if (what & AD1816A_PLAYBACK_ENABLE)			snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG,				AD1816A_PLAYBACK_ENABLE, cmd);		if (what & AD1816A_CAPTURE_ENABLE)			snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG,				AD1816A_CAPTURE_ENABLE, cmd);		spin_unlock(&chip->lock);		break;	default:		snd_printk("invalid trigger mode 0x%x.\n", what);		error = -EINVAL;	}	return error;}static int snd_ad1816a_playback_trigger(snd_pcm_substream_t *substream, int cmd){	ad1816a_t *chip = snd_pcm_substream_chip(substream);	return snd_ad1816a_trigger(chip, AD1816A_PLAYBACK_ENABLE,		SNDRV_PCM_STREAM_PLAYBACK, cmd);}static int snd_ad1816a_capture_trigger(snd_pcm_substream_t *substream, int cmd){	ad1816a_t *chip = snd_pcm_substream_chip(substream);	return snd_ad1816a_trigger(chip, AD1816A_CAPTURE_ENABLE,		SNDRV_PCM_STREAM_CAPTURE, cmd);}static int snd_ad1816a_hw_params(snd_pcm_substream_t * substream,				 snd_pcm_hw_params_t * hw_params){	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));}static int snd_ad1816a_hw_free(snd_pcm_substream_t * substream){	return snd_pcm_lib_free_pages(substream);}static int snd_ad1816a_playback_prepare(snd_pcm_substream_t *substream){	ad1816a_t *chip = snd_pcm_substream_chip(substream);	unsigned long flags;	snd_pcm_runtime_t *runtime = substream->runtime;	unsigned int size, rate;	spin_lock_irqsave(&chip->lock, flags);	chip->p_dma_size = size = snd_pcm_lib_buffer_bytes(substream);	snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG,		AD1816A_PLAYBACK_ENABLE | AD1816A_PLAYBACK_PIO, 0x00);	snd_dma_program(chip->dma1, runtime->dma_addr, size,			DMA_MODE_WRITE | DMA_AUTOINIT);	rate = runtime->rate;	if (chip->clock_freq)		rate = (rate * 33000) / chip->clock_freq;	snd_ad1816a_write(chip, AD1816A_PLAYBACK_SAMPLE_RATE, rate);	snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG,		AD1816A_FMT_ALL | AD1816A_FMT_STEREO,		snd_ad1816a_get_format(chip, runtime->format,			runtime->channels));	snd_ad1816a_write(chip, AD1816A_PLAYBACK_BASE_COUNT,		snd_pcm_lib_period_bytes(substream) / 4 - 1);	spin_unlock_irqrestore(&chip->lock, flags);	return 0;}static int snd_ad1816a_capture_prepare(snd_pcm_substream_t *substream){	ad1816a_t *chip = snd_pcm_substream_chip(substream);	unsigned long flags;	snd_pcm_runtime_t *runtime = substream->runtime;	unsigned int size, rate;	spin_lock_irqsave(&chip->lock, flags);	chip->c_dma_size = size = snd_pcm_lib_buffer_bytes(substream);	snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG,		AD1816A_CAPTURE_ENABLE | AD1816A_CAPTURE_PIO, 0x00);	snd_dma_program(chip->dma2, runtime->dma_addr, size,			DMA_MODE_READ | DMA_AUTOINIT);	rate = runtime->rate;	if (chip->clock_freq)		rate = (rate * 33000) / chip->clock_freq;	snd_ad1816a_write(chip, AD1816A_CAPTURE_SAMPLE_RATE, rate);	snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG,		AD1816A_FMT_ALL | AD1816A_FMT_STEREO,		snd_ad1816a_get_format(chip, runtime->format,			runtime->channels));	snd_ad1816a_write(chip, AD1816A_CAPTURE_BASE_COUNT,		snd_pcm_lib_period_bytes(substream) / 4 - 1);	spin_unlock_irqrestore(&chip->lock, flags);	return 0;}static snd_pcm_uframes_t snd_ad1816a_playback_pointer(snd_pcm_substream_t *substream){	ad1816a_t *chip = snd_pcm_substream_chip(substream);	size_t ptr;	if (!(chip->mode & AD1816A_MODE_PLAYBACK))		return 0;	ptr = snd_dma_pointer(chip->dma1, chip->p_dma_size);	return bytes_to_frames(substream->runtime, ptr);}static snd_pcm_uframes_t snd_ad1816a_capture_pointer(snd_pcm_substream_t *substream){	ad1816a_t *chip = snd_pcm_substream_chip(substream);	size_t ptr;	if (!(chip->mode & AD1816A_MODE_CAPTURE))		return 0;	ptr = snd_dma_pointer(chip->dma2, chip->c_dma_size);	return bytes_to_frames(substream->runtime, ptr);}static irqreturn_t snd_ad1816a_interrupt(int irq, void *dev_id, struct pt_regs *regs){	ad1816a_t *chip = dev_id;	unsigned char status;	spin_lock(&chip->lock);	status = snd_ad1816a_in(chip, AD1816A_INTERRUPT_STATUS);	spin_unlock(&chip->lock);	if ((status & AD1816A_PLAYBACK_IRQ_PENDING) && chip->playback_substream)		snd_pcm_period_elapsed(chip->playback_substream);	if ((status & AD1816A_CAPTURE_IRQ_PENDING) && chip->capture_substream)		snd_pcm_period_elapsed(chip->capture_substream);	if ((status & AD1816A_TIMER_IRQ_PENDING) && chip->timer)		snd_timer_interrupt(chip->timer, chip->timer->sticks);	spin_lock(&chip->lock);	snd_ad1816a_out(chip, AD1816A_INTERRUPT_STATUS, 0x00);	spin_unlock(&chip->lock);	return IRQ_HANDLED;}static snd_pcm_hardware_t snd_ad1816a_playback = {	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |				 SNDRV_PCM_INFO_MMAP_VALID),	.formats =		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW |				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |				 SNDRV_PCM_FMTBIT_S16_BE),	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,	.rate_min =		4000,	.rate_max =		55200,	.channels_min =		1,	.channels_max =		2,	.buffer_bytes_max =	(128*1024),	.period_bytes_min =	64,	.period_bytes_max =	(128*1024),	.periods_min =		1,	.periods_max =		1024,	.fifo_size =		0,};static snd_pcm_hardware_t snd_ad1816a_capture = {	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |				 SNDRV_PCM_INFO_MMAP_VALID),	.formats =		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW |				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |				 SNDRV_PCM_FMTBIT_S16_BE),	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,	.rate_min =		4000,	.rate_max =		55200,	.channels_min =		1,	.channels_max =		2,	.buffer_bytes_max =	(128*1024),	.period_bytes_min =	64,	.period_bytes_max =	(128*1024),	.periods_min =		1,	.periods_max =		1024,	.fifo_size =		0,};#if 0 /* not used now */static int snd_ad1816a_timer_close(snd_timer_t *timer){	ad1816a_t *chip = snd_timer_chip(timer);	snd_ad1816a_close(chip, AD1816A_MODE_TIMER);	return 0;}static int snd_ad1816a_timer_open(snd_timer_t *timer){	ad1816a_t *chip = snd_timer_chip(timer);	snd_ad1816a_open(chip, AD1816A_MODE_TIMER);	return 0;}static unsigned long snd_ad1816a_timer_resolution(snd_timer_t *timer){	snd_assert(timer != NULL, return 0);	return 10000;}static int snd_ad1816a_timer_start(snd_timer_t *timer){	unsigned short bits;	unsigned long flags;	ad1816a_t *chip = snd_timer_chip(timer);	spin_lock_irqsave(&chip->lock, flags);	bits = snd_ad1816a_read(chip, AD1816A_INTERRUPT_ENABLE);	if (!(bits & AD1816A_TIMER_ENABLE)) {		snd_ad1816a_write(chip, AD1816A_TIMER_BASE_COUNT,			timer->sticks & 0xffff);		snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE,			AD1816A_TIMER_ENABLE, 0xffff);	}	spin_unlock_irqrestore(&chip->lock, flags);	return 0;}static int snd_ad1816a_timer_stop(snd_timer_t *timer){	unsigned long flags;	ad1816a_t *chip = snd_timer_chip(timer);	spin_lock_irqsave(&chip->lock, flags);	snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE,		AD1816A_TIMER_ENABLE, 0x0000);	spin_unlock_irqrestore(&chip->lock, flags);	return 0;}static struct _snd_timer_hardware snd_ad1816a_timer_table = {	.flags =	SNDRV_TIMER_HW_AUTO,	.resolution =	10000,	.ticks =	65535,	.open =		snd_ad1816a_timer_open,	.close =	snd_ad1816a_timer_close,	.c_resolution =	snd_ad1816a_timer_resolution,	.start =	snd_ad1816a_timer_start,	.stop =		snd_ad1816a_timer_stop,};#endif /* not used now */static int snd_ad1816a_playback_open(snd_pcm_substream_t *substream){	ad1816a_t *chip = snd_pcm_substream_chip(substream);	snd_pcm_runtime_t *runtime = substream->runtime;	int error;	if ((error = snd_ad1816a_open(chip, AD1816A_MODE_PLAYBACK)) < 0)		return error;	snd_pcm_set_sync(substream);	runtime->hw = snd_ad1816a_playback;	snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.buffer_bytes_max);	snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.period_bytes_max);	chip->playback_substream = substream;	return 0;}static int snd_ad1816a_capture_open(snd_pcm_substream_t *substream){	ad1816a_t *chip = snd_pcm_substream_chip(substream);	snd_pcm_runtime_t *runtime = substream->runtime;	int error;	if ((error = snd_ad1816a_open(chip, AD1816A_MODE_CAPTURE)) < 0)		return error;	snd_pcm_set_sync(substream);	runtime->hw = snd_ad1816a_capture;	snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.buffer_bytes_max);	snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.period_bytes_max);	chip->capture_substream = substream;	return 0;}static int snd_ad1816a_playback_close(snd_pcm_substream_t *substream){	ad1816a_t *chip = snd_pcm_substream_chip(substream);	chip->playback_substream = NULL;	snd_ad1816a_close(chip, AD1816A_MODE_PLAYBACK);	return 0;}static int snd_ad1816a_capture_close(snd_pcm_substream_t *substream)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -