📄 ad1816a_lib.c
字号:
/* 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 + -