162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci ad1816a.c - lowlevel code for Analog Devices AD1816A chip. 462306a36Sopenharmony_ci Copyright (C) 1999-2000 by Massimo Piccioni <dafastidio@libero.it> 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci*/ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/delay.h> 962306a36Sopenharmony_ci#include <linux/init.h> 1062306a36Sopenharmony_ci#include <linux/interrupt.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/ioport.h> 1362306a36Sopenharmony_ci#include <linux/io.h> 1462306a36Sopenharmony_ci#include <sound/core.h> 1562306a36Sopenharmony_ci#include <sound/tlv.h> 1662306a36Sopenharmony_ci#include <sound/ad1816a.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <asm/dma.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistatic inline int snd_ad1816a_busy_wait(struct snd_ad1816a *chip) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci int timeout; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci for (timeout = 1000; timeout-- > 0; udelay(10)) 2562306a36Sopenharmony_ci if (inb(AD1816A_REG(AD1816A_CHIP_STATUS)) & AD1816A_READY) 2662306a36Sopenharmony_ci return 0; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci snd_printk(KERN_WARNING "chip busy.\n"); 2962306a36Sopenharmony_ci return -EBUSY; 3062306a36Sopenharmony_ci} 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic inline unsigned char snd_ad1816a_in(struct snd_ad1816a *chip, unsigned char reg) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci snd_ad1816a_busy_wait(chip); 3562306a36Sopenharmony_ci return inb(AD1816A_REG(reg)); 3662306a36Sopenharmony_ci} 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic inline void snd_ad1816a_out(struct snd_ad1816a *chip, unsigned char reg, 3962306a36Sopenharmony_ci unsigned char value) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci snd_ad1816a_busy_wait(chip); 4262306a36Sopenharmony_ci outb(value, AD1816A_REG(reg)); 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic inline void snd_ad1816a_out_mask(struct snd_ad1816a *chip, unsigned char reg, 4662306a36Sopenharmony_ci unsigned char mask, unsigned char value) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci snd_ad1816a_out(chip, reg, 4962306a36Sopenharmony_ci (value & mask) | (snd_ad1816a_in(chip, reg) & ~mask)); 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic unsigned short snd_ad1816a_read(struct snd_ad1816a *chip, unsigned char reg) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci snd_ad1816a_out(chip, AD1816A_INDIR_ADDR, reg & 0x3f); 5562306a36Sopenharmony_ci return snd_ad1816a_in(chip, AD1816A_INDIR_DATA_LOW) | 5662306a36Sopenharmony_ci (snd_ad1816a_in(chip, AD1816A_INDIR_DATA_HIGH) << 8); 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic void snd_ad1816a_write(struct snd_ad1816a *chip, unsigned char reg, 6062306a36Sopenharmony_ci unsigned short value) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci snd_ad1816a_out(chip, AD1816A_INDIR_ADDR, reg & 0x3f); 6362306a36Sopenharmony_ci snd_ad1816a_out(chip, AD1816A_INDIR_DATA_LOW, value & 0xff); 6462306a36Sopenharmony_ci snd_ad1816a_out(chip, AD1816A_INDIR_DATA_HIGH, (value >> 8) & 0xff); 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic void snd_ad1816a_write_mask(struct snd_ad1816a *chip, unsigned char reg, 6862306a36Sopenharmony_ci unsigned short mask, unsigned short value) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci snd_ad1816a_write(chip, reg, 7162306a36Sopenharmony_ci (value & mask) | (snd_ad1816a_read(chip, reg) & ~mask)); 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic unsigned char snd_ad1816a_get_format(struct snd_ad1816a *chip, 7662306a36Sopenharmony_ci snd_pcm_format_t format, 7762306a36Sopenharmony_ci int channels) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci unsigned char retval = AD1816A_FMT_LINEAR_8; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci switch (format) { 8262306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_MU_LAW: 8362306a36Sopenharmony_ci retval = AD1816A_FMT_ULAW_8; 8462306a36Sopenharmony_ci break; 8562306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_A_LAW: 8662306a36Sopenharmony_ci retval = AD1816A_FMT_ALAW_8; 8762306a36Sopenharmony_ci break; 8862306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 8962306a36Sopenharmony_ci retval = AD1816A_FMT_LINEAR_16_LIT; 9062306a36Sopenharmony_ci break; 9162306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_BE: 9262306a36Sopenharmony_ci retval = AD1816A_FMT_LINEAR_16_BIG; 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci return (channels > 1) ? (retval | AD1816A_FMT_STEREO) : retval; 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic int snd_ad1816a_open(struct snd_ad1816a *chip, unsigned int mode) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci unsigned long flags; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci spin_lock_irqsave(&chip->lock, flags); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci if (chip->mode & mode) { 10462306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->lock, flags); 10562306a36Sopenharmony_ci return -EAGAIN; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci switch ((mode &= AD1816A_MODE_OPEN)) { 10962306a36Sopenharmony_ci case AD1816A_MODE_PLAYBACK: 11062306a36Sopenharmony_ci snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, 11162306a36Sopenharmony_ci AD1816A_PLAYBACK_IRQ_PENDING, 0x00); 11262306a36Sopenharmony_ci snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, 11362306a36Sopenharmony_ci AD1816A_PLAYBACK_IRQ_ENABLE, 0xffff); 11462306a36Sopenharmony_ci break; 11562306a36Sopenharmony_ci case AD1816A_MODE_CAPTURE: 11662306a36Sopenharmony_ci snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, 11762306a36Sopenharmony_ci AD1816A_CAPTURE_IRQ_PENDING, 0x00); 11862306a36Sopenharmony_ci snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, 11962306a36Sopenharmony_ci AD1816A_CAPTURE_IRQ_ENABLE, 0xffff); 12062306a36Sopenharmony_ci break; 12162306a36Sopenharmony_ci case AD1816A_MODE_TIMER: 12262306a36Sopenharmony_ci snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, 12362306a36Sopenharmony_ci AD1816A_TIMER_IRQ_PENDING, 0x00); 12462306a36Sopenharmony_ci snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, 12562306a36Sopenharmony_ci AD1816A_TIMER_IRQ_ENABLE, 0xffff); 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci chip->mode |= mode; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->lock, flags); 13062306a36Sopenharmony_ci return 0; 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic void snd_ad1816a_close(struct snd_ad1816a *chip, unsigned int mode) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci unsigned long flags; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci spin_lock_irqsave(&chip->lock, flags); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci switch ((mode &= AD1816A_MODE_OPEN)) { 14062306a36Sopenharmony_ci case AD1816A_MODE_PLAYBACK: 14162306a36Sopenharmony_ci snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, 14262306a36Sopenharmony_ci AD1816A_PLAYBACK_IRQ_PENDING, 0x00); 14362306a36Sopenharmony_ci snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, 14462306a36Sopenharmony_ci AD1816A_PLAYBACK_IRQ_ENABLE, 0x0000); 14562306a36Sopenharmony_ci break; 14662306a36Sopenharmony_ci case AD1816A_MODE_CAPTURE: 14762306a36Sopenharmony_ci snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, 14862306a36Sopenharmony_ci AD1816A_CAPTURE_IRQ_PENDING, 0x00); 14962306a36Sopenharmony_ci snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, 15062306a36Sopenharmony_ci AD1816A_CAPTURE_IRQ_ENABLE, 0x0000); 15162306a36Sopenharmony_ci break; 15262306a36Sopenharmony_ci case AD1816A_MODE_TIMER: 15362306a36Sopenharmony_ci snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, 15462306a36Sopenharmony_ci AD1816A_TIMER_IRQ_PENDING, 0x00); 15562306a36Sopenharmony_ci snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, 15662306a36Sopenharmony_ci AD1816A_TIMER_IRQ_ENABLE, 0x0000); 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci chip->mode &= ~mode; 15962306a36Sopenharmony_ci if (!(chip->mode & AD1816A_MODE_OPEN)) 16062306a36Sopenharmony_ci chip->mode = 0; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->lock, flags); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic int snd_ad1816a_trigger(struct snd_ad1816a *chip, unsigned char what, 16762306a36Sopenharmony_ci int channel, int cmd, int iscapture) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci int error = 0; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci switch (cmd) { 17262306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 17362306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 17462306a36Sopenharmony_ci spin_lock(&chip->lock); 17562306a36Sopenharmony_ci cmd = (cmd == SNDRV_PCM_TRIGGER_START) ? 0xff: 0x00; 17662306a36Sopenharmony_ci /* if (what & AD1816A_PLAYBACK_ENABLE) */ 17762306a36Sopenharmony_ci /* That is not valid, because playback and capture enable 17862306a36Sopenharmony_ci * are the same bit pattern, just to different addresses 17962306a36Sopenharmony_ci */ 18062306a36Sopenharmony_ci if (! iscapture) 18162306a36Sopenharmony_ci snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG, 18262306a36Sopenharmony_ci AD1816A_PLAYBACK_ENABLE, cmd); 18362306a36Sopenharmony_ci else 18462306a36Sopenharmony_ci snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG, 18562306a36Sopenharmony_ci AD1816A_CAPTURE_ENABLE, cmd); 18662306a36Sopenharmony_ci spin_unlock(&chip->lock); 18762306a36Sopenharmony_ci break; 18862306a36Sopenharmony_ci default: 18962306a36Sopenharmony_ci snd_printk(KERN_WARNING "invalid trigger mode 0x%x.\n", what); 19062306a36Sopenharmony_ci error = -EINVAL; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci return error; 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic int snd_ad1816a_playback_trigger(struct snd_pcm_substream *substream, int cmd) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci struct snd_ad1816a *chip = snd_pcm_substream_chip(substream); 19962306a36Sopenharmony_ci return snd_ad1816a_trigger(chip, AD1816A_PLAYBACK_ENABLE, 20062306a36Sopenharmony_ci SNDRV_PCM_STREAM_PLAYBACK, cmd, 0); 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic int snd_ad1816a_capture_trigger(struct snd_pcm_substream *substream, int cmd) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci struct snd_ad1816a *chip = snd_pcm_substream_chip(substream); 20662306a36Sopenharmony_ci return snd_ad1816a_trigger(chip, AD1816A_CAPTURE_ENABLE, 20762306a36Sopenharmony_ci SNDRV_PCM_STREAM_CAPTURE, cmd, 1); 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic int snd_ad1816a_playback_prepare(struct snd_pcm_substream *substream) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci struct snd_ad1816a *chip = snd_pcm_substream_chip(substream); 21362306a36Sopenharmony_ci unsigned long flags; 21462306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 21562306a36Sopenharmony_ci unsigned int size, rate; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci spin_lock_irqsave(&chip->lock, flags); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci chip->p_dma_size = size = snd_pcm_lib_buffer_bytes(substream); 22062306a36Sopenharmony_ci snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG, 22162306a36Sopenharmony_ci AD1816A_PLAYBACK_ENABLE | AD1816A_PLAYBACK_PIO, 0x00); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci snd_dma_program(chip->dma1, runtime->dma_addr, size, 22462306a36Sopenharmony_ci DMA_MODE_WRITE | DMA_AUTOINIT); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci rate = runtime->rate; 22762306a36Sopenharmony_ci if (chip->clock_freq) 22862306a36Sopenharmony_ci rate = (rate * 33000) / chip->clock_freq; 22962306a36Sopenharmony_ci snd_ad1816a_write(chip, AD1816A_PLAYBACK_SAMPLE_RATE, rate); 23062306a36Sopenharmony_ci snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG, 23162306a36Sopenharmony_ci AD1816A_FMT_ALL | AD1816A_FMT_STEREO, 23262306a36Sopenharmony_ci snd_ad1816a_get_format(chip, runtime->format, 23362306a36Sopenharmony_ci runtime->channels)); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci snd_ad1816a_write(chip, AD1816A_PLAYBACK_BASE_COUNT, 23662306a36Sopenharmony_ci snd_pcm_lib_period_bytes(substream) / 4 - 1); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->lock, flags); 23962306a36Sopenharmony_ci return 0; 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_cistatic int snd_ad1816a_capture_prepare(struct snd_pcm_substream *substream) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci struct snd_ad1816a *chip = snd_pcm_substream_chip(substream); 24562306a36Sopenharmony_ci unsigned long flags; 24662306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 24762306a36Sopenharmony_ci unsigned int size, rate; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci spin_lock_irqsave(&chip->lock, flags); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci chip->c_dma_size = size = snd_pcm_lib_buffer_bytes(substream); 25262306a36Sopenharmony_ci snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG, 25362306a36Sopenharmony_ci AD1816A_CAPTURE_ENABLE | AD1816A_CAPTURE_PIO, 0x00); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci snd_dma_program(chip->dma2, runtime->dma_addr, size, 25662306a36Sopenharmony_ci DMA_MODE_READ | DMA_AUTOINIT); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci rate = runtime->rate; 25962306a36Sopenharmony_ci if (chip->clock_freq) 26062306a36Sopenharmony_ci rate = (rate * 33000) / chip->clock_freq; 26162306a36Sopenharmony_ci snd_ad1816a_write(chip, AD1816A_CAPTURE_SAMPLE_RATE, rate); 26262306a36Sopenharmony_ci snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG, 26362306a36Sopenharmony_ci AD1816A_FMT_ALL | AD1816A_FMT_STEREO, 26462306a36Sopenharmony_ci snd_ad1816a_get_format(chip, runtime->format, 26562306a36Sopenharmony_ci runtime->channels)); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci snd_ad1816a_write(chip, AD1816A_CAPTURE_BASE_COUNT, 26862306a36Sopenharmony_ci snd_pcm_lib_period_bytes(substream) / 4 - 1); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->lock, flags); 27162306a36Sopenharmony_ci return 0; 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_ad1816a_playback_pointer(struct snd_pcm_substream *substream) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci struct snd_ad1816a *chip = snd_pcm_substream_chip(substream); 27862306a36Sopenharmony_ci size_t ptr; 27962306a36Sopenharmony_ci if (!(chip->mode & AD1816A_MODE_PLAYBACK)) 28062306a36Sopenharmony_ci return 0; 28162306a36Sopenharmony_ci ptr = snd_dma_pointer(chip->dma1, chip->p_dma_size); 28262306a36Sopenharmony_ci return bytes_to_frames(substream->runtime, ptr); 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_ad1816a_capture_pointer(struct snd_pcm_substream *substream) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci struct snd_ad1816a *chip = snd_pcm_substream_chip(substream); 28862306a36Sopenharmony_ci size_t ptr; 28962306a36Sopenharmony_ci if (!(chip->mode & AD1816A_MODE_CAPTURE)) 29062306a36Sopenharmony_ci return 0; 29162306a36Sopenharmony_ci ptr = snd_dma_pointer(chip->dma2, chip->c_dma_size); 29262306a36Sopenharmony_ci return bytes_to_frames(substream->runtime, ptr); 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cistatic irqreturn_t snd_ad1816a_interrupt(int irq, void *dev_id) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci struct snd_ad1816a *chip = dev_id; 29962306a36Sopenharmony_ci unsigned char status; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci spin_lock(&chip->lock); 30262306a36Sopenharmony_ci status = snd_ad1816a_in(chip, AD1816A_INTERRUPT_STATUS); 30362306a36Sopenharmony_ci spin_unlock(&chip->lock); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if ((status & AD1816A_PLAYBACK_IRQ_PENDING) && chip->playback_substream) 30662306a36Sopenharmony_ci snd_pcm_period_elapsed(chip->playback_substream); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci if ((status & AD1816A_CAPTURE_IRQ_PENDING) && chip->capture_substream) 30962306a36Sopenharmony_ci snd_pcm_period_elapsed(chip->capture_substream); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if ((status & AD1816A_TIMER_IRQ_PENDING) && chip->timer) 31262306a36Sopenharmony_ci snd_timer_interrupt(chip->timer, chip->timer->sticks); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci spin_lock(&chip->lock); 31562306a36Sopenharmony_ci snd_ad1816a_out(chip, AD1816A_INTERRUPT_STATUS, 0x00); 31662306a36Sopenharmony_ci spin_unlock(&chip->lock); 31762306a36Sopenharmony_ci return IRQ_HANDLED; 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_ad1816a_playback = { 32262306a36Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 32362306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID), 32462306a36Sopenharmony_ci .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | 32562306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | 32662306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S16_BE), 32762306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, 32862306a36Sopenharmony_ci .rate_min = 4000, 32962306a36Sopenharmony_ci .rate_max = 55200, 33062306a36Sopenharmony_ci .channels_min = 1, 33162306a36Sopenharmony_ci .channels_max = 2, 33262306a36Sopenharmony_ci .buffer_bytes_max = (128*1024), 33362306a36Sopenharmony_ci .period_bytes_min = 64, 33462306a36Sopenharmony_ci .period_bytes_max = (128*1024), 33562306a36Sopenharmony_ci .periods_min = 1, 33662306a36Sopenharmony_ci .periods_max = 1024, 33762306a36Sopenharmony_ci .fifo_size = 0, 33862306a36Sopenharmony_ci}; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_ad1816a_capture = { 34162306a36Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 34262306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID), 34362306a36Sopenharmony_ci .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | 34462306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | 34562306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S16_BE), 34662306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, 34762306a36Sopenharmony_ci .rate_min = 4000, 34862306a36Sopenharmony_ci .rate_max = 55200, 34962306a36Sopenharmony_ci .channels_min = 1, 35062306a36Sopenharmony_ci .channels_max = 2, 35162306a36Sopenharmony_ci .buffer_bytes_max = (128*1024), 35262306a36Sopenharmony_ci .period_bytes_min = 64, 35362306a36Sopenharmony_ci .period_bytes_max = (128*1024), 35462306a36Sopenharmony_ci .periods_min = 1, 35562306a36Sopenharmony_ci .periods_max = 1024, 35662306a36Sopenharmony_ci .fifo_size = 0, 35762306a36Sopenharmony_ci}; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cistatic int snd_ad1816a_timer_close(struct snd_timer *timer) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci struct snd_ad1816a *chip = snd_timer_chip(timer); 36262306a36Sopenharmony_ci snd_ad1816a_close(chip, AD1816A_MODE_TIMER); 36362306a36Sopenharmony_ci return 0; 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic int snd_ad1816a_timer_open(struct snd_timer *timer) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci struct snd_ad1816a *chip = snd_timer_chip(timer); 36962306a36Sopenharmony_ci snd_ad1816a_open(chip, AD1816A_MODE_TIMER); 37062306a36Sopenharmony_ci return 0; 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cistatic unsigned long snd_ad1816a_timer_resolution(struct snd_timer *timer) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci if (snd_BUG_ON(!timer)) 37662306a36Sopenharmony_ci return 0; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci return 10000; 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_cistatic int snd_ad1816a_timer_start(struct snd_timer *timer) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci unsigned short bits; 38462306a36Sopenharmony_ci unsigned long flags; 38562306a36Sopenharmony_ci struct snd_ad1816a *chip = snd_timer_chip(timer); 38662306a36Sopenharmony_ci spin_lock_irqsave(&chip->lock, flags); 38762306a36Sopenharmony_ci bits = snd_ad1816a_read(chip, AD1816A_INTERRUPT_ENABLE); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci if (!(bits & AD1816A_TIMER_ENABLE)) { 39062306a36Sopenharmony_ci snd_ad1816a_write(chip, AD1816A_TIMER_BASE_COUNT, 39162306a36Sopenharmony_ci timer->sticks & 0xffff); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, 39462306a36Sopenharmony_ci AD1816A_TIMER_ENABLE, 0xffff); 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->lock, flags); 39762306a36Sopenharmony_ci return 0; 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic int snd_ad1816a_timer_stop(struct snd_timer *timer) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci unsigned long flags; 40362306a36Sopenharmony_ci struct snd_ad1816a *chip = snd_timer_chip(timer); 40462306a36Sopenharmony_ci spin_lock_irqsave(&chip->lock, flags); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, 40762306a36Sopenharmony_ci AD1816A_TIMER_ENABLE, 0x0000); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->lock, flags); 41062306a36Sopenharmony_ci return 0; 41162306a36Sopenharmony_ci} 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_cistatic const struct snd_timer_hardware snd_ad1816a_timer_table = { 41462306a36Sopenharmony_ci .flags = SNDRV_TIMER_HW_AUTO, 41562306a36Sopenharmony_ci .resolution = 10000, 41662306a36Sopenharmony_ci .ticks = 65535, 41762306a36Sopenharmony_ci .open = snd_ad1816a_timer_open, 41862306a36Sopenharmony_ci .close = snd_ad1816a_timer_close, 41962306a36Sopenharmony_ci .c_resolution = snd_ad1816a_timer_resolution, 42062306a36Sopenharmony_ci .start = snd_ad1816a_timer_start, 42162306a36Sopenharmony_ci .stop = snd_ad1816a_timer_stop, 42262306a36Sopenharmony_ci}; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic int snd_ad1816a_playback_open(struct snd_pcm_substream *substream) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci struct snd_ad1816a *chip = snd_pcm_substream_chip(substream); 42762306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 42862306a36Sopenharmony_ci int error; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci error = snd_ad1816a_open(chip, AD1816A_MODE_PLAYBACK); 43162306a36Sopenharmony_ci if (error < 0) 43262306a36Sopenharmony_ci return error; 43362306a36Sopenharmony_ci runtime->hw = snd_ad1816a_playback; 43462306a36Sopenharmony_ci snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.buffer_bytes_max); 43562306a36Sopenharmony_ci snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.period_bytes_max); 43662306a36Sopenharmony_ci chip->playback_substream = substream; 43762306a36Sopenharmony_ci return 0; 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cistatic int snd_ad1816a_capture_open(struct snd_pcm_substream *substream) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci struct snd_ad1816a *chip = snd_pcm_substream_chip(substream); 44362306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 44462306a36Sopenharmony_ci int error; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci error = snd_ad1816a_open(chip, AD1816A_MODE_CAPTURE); 44762306a36Sopenharmony_ci if (error < 0) 44862306a36Sopenharmony_ci return error; 44962306a36Sopenharmony_ci runtime->hw = snd_ad1816a_capture; 45062306a36Sopenharmony_ci snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.buffer_bytes_max); 45162306a36Sopenharmony_ci snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.period_bytes_max); 45262306a36Sopenharmony_ci chip->capture_substream = substream; 45362306a36Sopenharmony_ci return 0; 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_cistatic int snd_ad1816a_playback_close(struct snd_pcm_substream *substream) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci struct snd_ad1816a *chip = snd_pcm_substream_chip(substream); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci chip->playback_substream = NULL; 46162306a36Sopenharmony_ci snd_ad1816a_close(chip, AD1816A_MODE_PLAYBACK); 46262306a36Sopenharmony_ci return 0; 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_cistatic int snd_ad1816a_capture_close(struct snd_pcm_substream *substream) 46662306a36Sopenharmony_ci{ 46762306a36Sopenharmony_ci struct snd_ad1816a *chip = snd_pcm_substream_chip(substream); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci chip->capture_substream = NULL; 47062306a36Sopenharmony_ci snd_ad1816a_close(chip, AD1816A_MODE_CAPTURE); 47162306a36Sopenharmony_ci return 0; 47262306a36Sopenharmony_ci} 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_cistatic void snd_ad1816a_init(struct snd_ad1816a *chip) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci unsigned long flags; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci spin_lock_irqsave(&chip->lock, flags); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci snd_ad1816a_out(chip, AD1816A_INTERRUPT_STATUS, 0x00); 48262306a36Sopenharmony_ci snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG, 48362306a36Sopenharmony_ci AD1816A_PLAYBACK_ENABLE | AD1816A_PLAYBACK_PIO, 0x00); 48462306a36Sopenharmony_ci snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG, 48562306a36Sopenharmony_ci AD1816A_CAPTURE_ENABLE | AD1816A_CAPTURE_PIO, 0x00); 48662306a36Sopenharmony_ci snd_ad1816a_write(chip, AD1816A_INTERRUPT_ENABLE, 0x0000); 48762306a36Sopenharmony_ci snd_ad1816a_write_mask(chip, AD1816A_CHIP_CONFIG, 48862306a36Sopenharmony_ci AD1816A_CAPTURE_NOT_EQUAL | AD1816A_WSS_ENABLE, 0xffff); 48962306a36Sopenharmony_ci snd_ad1816a_write(chip, AD1816A_DSP_CONFIG, 0x0000); 49062306a36Sopenharmony_ci snd_ad1816a_write(chip, AD1816A_POWERDOWN_CTRL, 0x0000); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->lock, flags); 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci#ifdef CONFIG_PM 49662306a36Sopenharmony_civoid snd_ad1816a_suspend(struct snd_ad1816a *chip) 49762306a36Sopenharmony_ci{ 49862306a36Sopenharmony_ci int reg; 49962306a36Sopenharmony_ci unsigned long flags; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci spin_lock_irqsave(&chip->lock, flags); 50262306a36Sopenharmony_ci for (reg = 0; reg < 48; reg++) 50362306a36Sopenharmony_ci chip->image[reg] = snd_ad1816a_read(chip, reg); 50462306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->lock, flags); 50562306a36Sopenharmony_ci} 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_civoid snd_ad1816a_resume(struct snd_ad1816a *chip) 50862306a36Sopenharmony_ci{ 50962306a36Sopenharmony_ci int reg; 51062306a36Sopenharmony_ci unsigned long flags; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci snd_ad1816a_init(chip); 51362306a36Sopenharmony_ci spin_lock_irqsave(&chip->lock, flags); 51462306a36Sopenharmony_ci for (reg = 0; reg < 48; reg++) 51562306a36Sopenharmony_ci snd_ad1816a_write(chip, reg, chip->image[reg]); 51662306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->lock, flags); 51762306a36Sopenharmony_ci} 51862306a36Sopenharmony_ci#endif 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_cistatic int snd_ad1816a_probe(struct snd_ad1816a *chip) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci unsigned long flags; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci spin_lock_irqsave(&chip->lock, flags); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci switch (chip->version = snd_ad1816a_read(chip, AD1816A_VERSION_ID)) { 52762306a36Sopenharmony_ci case 0: 52862306a36Sopenharmony_ci chip->hardware = AD1816A_HW_AD1815; 52962306a36Sopenharmony_ci break; 53062306a36Sopenharmony_ci case 1: 53162306a36Sopenharmony_ci chip->hardware = AD1816A_HW_AD18MAX10; 53262306a36Sopenharmony_ci break; 53362306a36Sopenharmony_ci case 3: 53462306a36Sopenharmony_ci chip->hardware = AD1816A_HW_AD1816A; 53562306a36Sopenharmony_ci break; 53662306a36Sopenharmony_ci default: 53762306a36Sopenharmony_ci chip->hardware = AD1816A_HW_AUTO; 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->lock, flags); 54162306a36Sopenharmony_ci return 0; 54262306a36Sopenharmony_ci} 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_cistatic const char *snd_ad1816a_chip_id(struct snd_ad1816a *chip) 54562306a36Sopenharmony_ci{ 54662306a36Sopenharmony_ci switch (chip->hardware) { 54762306a36Sopenharmony_ci case AD1816A_HW_AD1816A: return "AD1816A"; 54862306a36Sopenharmony_ci case AD1816A_HW_AD1815: return "AD1815"; 54962306a36Sopenharmony_ci case AD1816A_HW_AD18MAX10: return "AD18max10"; 55062306a36Sopenharmony_ci default: 55162306a36Sopenharmony_ci snd_printk(KERN_WARNING "Unknown chip version %d:%d.\n", 55262306a36Sopenharmony_ci chip->version, chip->hardware); 55362306a36Sopenharmony_ci return "AD1816A - unknown"; 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci} 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ciint snd_ad1816a_create(struct snd_card *card, 55862306a36Sopenharmony_ci unsigned long port, int irq, int dma1, int dma2, 55962306a36Sopenharmony_ci struct snd_ad1816a *chip) 56062306a36Sopenharmony_ci{ 56162306a36Sopenharmony_ci int error; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci chip->irq = -1; 56462306a36Sopenharmony_ci chip->dma1 = -1; 56562306a36Sopenharmony_ci chip->dma2 = -1; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci chip->res_port = devm_request_region(card->dev, port, 16, "AD1816A"); 56862306a36Sopenharmony_ci if (!chip->res_port) { 56962306a36Sopenharmony_ci snd_printk(KERN_ERR "ad1816a: can't grab port 0x%lx\n", port); 57062306a36Sopenharmony_ci return -EBUSY; 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci if (devm_request_irq(card->dev, irq, snd_ad1816a_interrupt, 0, 57362306a36Sopenharmony_ci "AD1816A", (void *) chip)) { 57462306a36Sopenharmony_ci snd_printk(KERN_ERR "ad1816a: can't grab IRQ %d\n", irq); 57562306a36Sopenharmony_ci return -EBUSY; 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci chip->irq = irq; 57862306a36Sopenharmony_ci card->sync_irq = chip->irq; 57962306a36Sopenharmony_ci if (snd_devm_request_dma(card->dev, dma1, "AD1816A - 1")) { 58062306a36Sopenharmony_ci snd_printk(KERN_ERR "ad1816a: can't grab DMA1 %d\n", dma1); 58162306a36Sopenharmony_ci return -EBUSY; 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci chip->dma1 = dma1; 58462306a36Sopenharmony_ci if (snd_devm_request_dma(card->dev, dma2, "AD1816A - 2")) { 58562306a36Sopenharmony_ci snd_printk(KERN_ERR "ad1816a: can't grab DMA2 %d\n", dma2); 58662306a36Sopenharmony_ci return -EBUSY; 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci chip->dma2 = dma2; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci chip->card = card; 59162306a36Sopenharmony_ci chip->port = port; 59262306a36Sopenharmony_ci spin_lock_init(&chip->lock); 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci error = snd_ad1816a_probe(chip); 59562306a36Sopenharmony_ci if (error) 59662306a36Sopenharmony_ci return error; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci snd_ad1816a_init(chip); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci return 0; 60162306a36Sopenharmony_ci} 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_ad1816a_playback_ops = { 60462306a36Sopenharmony_ci .open = snd_ad1816a_playback_open, 60562306a36Sopenharmony_ci .close = snd_ad1816a_playback_close, 60662306a36Sopenharmony_ci .prepare = snd_ad1816a_playback_prepare, 60762306a36Sopenharmony_ci .trigger = snd_ad1816a_playback_trigger, 60862306a36Sopenharmony_ci .pointer = snd_ad1816a_playback_pointer, 60962306a36Sopenharmony_ci}; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_ad1816a_capture_ops = { 61262306a36Sopenharmony_ci .open = snd_ad1816a_capture_open, 61362306a36Sopenharmony_ci .close = snd_ad1816a_capture_close, 61462306a36Sopenharmony_ci .prepare = snd_ad1816a_capture_prepare, 61562306a36Sopenharmony_ci .trigger = snd_ad1816a_capture_trigger, 61662306a36Sopenharmony_ci .pointer = snd_ad1816a_capture_pointer, 61762306a36Sopenharmony_ci}; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ciint snd_ad1816a_pcm(struct snd_ad1816a *chip, int device) 62062306a36Sopenharmony_ci{ 62162306a36Sopenharmony_ci int error; 62262306a36Sopenharmony_ci struct snd_pcm *pcm; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci error = snd_pcm_new(chip->card, "AD1816A", device, 1, 1, &pcm); 62562306a36Sopenharmony_ci if (error) 62662306a36Sopenharmony_ci return error; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ad1816a_playback_ops); 62962306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ad1816a_capture_ops); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci pcm->private_data = chip; 63262306a36Sopenharmony_ci pcm->info_flags = (chip->dma1 == chip->dma2 ) ? SNDRV_PCM_INFO_JOINT_DUPLEX : 0; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci strcpy(pcm->name, snd_ad1816a_chip_id(chip)); 63562306a36Sopenharmony_ci snd_ad1816a_init(chip); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, chip->card->dev, 63862306a36Sopenharmony_ci 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci chip->pcm = pcm; 64162306a36Sopenharmony_ci return 0; 64262306a36Sopenharmony_ci} 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ciint snd_ad1816a_timer(struct snd_ad1816a *chip, int device) 64562306a36Sopenharmony_ci{ 64662306a36Sopenharmony_ci struct snd_timer *timer; 64762306a36Sopenharmony_ci struct snd_timer_id tid; 64862306a36Sopenharmony_ci int error; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci tid.dev_class = SNDRV_TIMER_CLASS_CARD; 65162306a36Sopenharmony_ci tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; 65262306a36Sopenharmony_ci tid.card = chip->card->number; 65362306a36Sopenharmony_ci tid.device = device; 65462306a36Sopenharmony_ci tid.subdevice = 0; 65562306a36Sopenharmony_ci error = snd_timer_new(chip->card, "AD1816A", &tid, &timer); 65662306a36Sopenharmony_ci if (error < 0) 65762306a36Sopenharmony_ci return error; 65862306a36Sopenharmony_ci strcpy(timer->name, snd_ad1816a_chip_id(chip)); 65962306a36Sopenharmony_ci timer->private_data = chip; 66062306a36Sopenharmony_ci chip->timer = timer; 66162306a36Sopenharmony_ci timer->hw = snd_ad1816a_timer_table; 66262306a36Sopenharmony_ci return 0; 66362306a36Sopenharmony_ci} 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci/* 66662306a36Sopenharmony_ci * 66762306a36Sopenharmony_ci */ 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_cistatic int snd_ad1816a_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 67062306a36Sopenharmony_ci{ 67162306a36Sopenharmony_ci static const char * const texts[8] = { 67262306a36Sopenharmony_ci "Line", "Mix", "CD", "Synth", "Video", 67362306a36Sopenharmony_ci "Mic", "Phone", 67462306a36Sopenharmony_ci }; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci return snd_ctl_enum_info(uinfo, 2, 7, texts); 67762306a36Sopenharmony_ci} 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_cistatic int snd_ad1816a_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 68062306a36Sopenharmony_ci{ 68162306a36Sopenharmony_ci struct snd_ad1816a *chip = snd_kcontrol_chip(kcontrol); 68262306a36Sopenharmony_ci unsigned long flags; 68362306a36Sopenharmony_ci unsigned short val; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci spin_lock_irqsave(&chip->lock, flags); 68662306a36Sopenharmony_ci val = snd_ad1816a_read(chip, AD1816A_ADC_SOURCE_SEL); 68762306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->lock, flags); 68862306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = (val >> 12) & 7; 68962306a36Sopenharmony_ci ucontrol->value.enumerated.item[1] = (val >> 4) & 7; 69062306a36Sopenharmony_ci return 0; 69162306a36Sopenharmony_ci} 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_cistatic int snd_ad1816a_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 69462306a36Sopenharmony_ci{ 69562306a36Sopenharmony_ci struct snd_ad1816a *chip = snd_kcontrol_chip(kcontrol); 69662306a36Sopenharmony_ci unsigned long flags; 69762306a36Sopenharmony_ci unsigned short val; 69862306a36Sopenharmony_ci int change; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci if (ucontrol->value.enumerated.item[0] > 6 || 70162306a36Sopenharmony_ci ucontrol->value.enumerated.item[1] > 6) 70262306a36Sopenharmony_ci return -EINVAL; 70362306a36Sopenharmony_ci val = (ucontrol->value.enumerated.item[0] << 12) | 70462306a36Sopenharmony_ci (ucontrol->value.enumerated.item[1] << 4); 70562306a36Sopenharmony_ci spin_lock_irqsave(&chip->lock, flags); 70662306a36Sopenharmony_ci change = snd_ad1816a_read(chip, AD1816A_ADC_SOURCE_SEL) != val; 70762306a36Sopenharmony_ci snd_ad1816a_write(chip, AD1816A_ADC_SOURCE_SEL, val); 70862306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->lock, flags); 70962306a36Sopenharmony_ci return change; 71062306a36Sopenharmony_ci} 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci#define AD1816A_SINGLE_TLV(xname, reg, shift, mask, invert, xtlv) \ 71362306a36Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ 71462306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ 71562306a36Sopenharmony_ci .name = xname, .info = snd_ad1816a_info_single, \ 71662306a36Sopenharmony_ci .get = snd_ad1816a_get_single, .put = snd_ad1816a_put_single, \ 71762306a36Sopenharmony_ci .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24), \ 71862306a36Sopenharmony_ci .tlv = { .p = (xtlv) } } 71962306a36Sopenharmony_ci#define AD1816A_SINGLE(xname, reg, shift, mask, invert) \ 72062306a36Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ad1816a_info_single, \ 72162306a36Sopenharmony_ci .get = snd_ad1816a_get_single, .put = snd_ad1816a_put_single, \ 72262306a36Sopenharmony_ci .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_cistatic int snd_ad1816a_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 72562306a36Sopenharmony_ci{ 72662306a36Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; 72962306a36Sopenharmony_ci uinfo->count = 1; 73062306a36Sopenharmony_ci uinfo->value.integer.min = 0; 73162306a36Sopenharmony_ci uinfo->value.integer.max = mask; 73262306a36Sopenharmony_ci return 0; 73362306a36Sopenharmony_ci} 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_cistatic int snd_ad1816a_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 73662306a36Sopenharmony_ci{ 73762306a36Sopenharmony_ci struct snd_ad1816a *chip = snd_kcontrol_chip(kcontrol); 73862306a36Sopenharmony_ci unsigned long flags; 73962306a36Sopenharmony_ci int reg = kcontrol->private_value & 0xff; 74062306a36Sopenharmony_ci int shift = (kcontrol->private_value >> 8) & 0xff; 74162306a36Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 74262306a36Sopenharmony_ci int invert = (kcontrol->private_value >> 24) & 0xff; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci spin_lock_irqsave(&chip->lock, flags); 74562306a36Sopenharmony_ci ucontrol->value.integer.value[0] = (snd_ad1816a_read(chip, reg) >> shift) & mask; 74662306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->lock, flags); 74762306a36Sopenharmony_ci if (invert) 74862306a36Sopenharmony_ci ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; 74962306a36Sopenharmony_ci return 0; 75062306a36Sopenharmony_ci} 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_cistatic int snd_ad1816a_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 75362306a36Sopenharmony_ci{ 75462306a36Sopenharmony_ci struct snd_ad1816a *chip = snd_kcontrol_chip(kcontrol); 75562306a36Sopenharmony_ci unsigned long flags; 75662306a36Sopenharmony_ci int reg = kcontrol->private_value & 0xff; 75762306a36Sopenharmony_ci int shift = (kcontrol->private_value >> 8) & 0xff; 75862306a36Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 75962306a36Sopenharmony_ci int invert = (kcontrol->private_value >> 24) & 0xff; 76062306a36Sopenharmony_ci int change; 76162306a36Sopenharmony_ci unsigned short old_val, val; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci val = (ucontrol->value.integer.value[0] & mask); 76462306a36Sopenharmony_ci if (invert) 76562306a36Sopenharmony_ci val = mask - val; 76662306a36Sopenharmony_ci val <<= shift; 76762306a36Sopenharmony_ci spin_lock_irqsave(&chip->lock, flags); 76862306a36Sopenharmony_ci old_val = snd_ad1816a_read(chip, reg); 76962306a36Sopenharmony_ci val = (old_val & ~(mask << shift)) | val; 77062306a36Sopenharmony_ci change = val != old_val; 77162306a36Sopenharmony_ci snd_ad1816a_write(chip, reg, val); 77262306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->lock, flags); 77362306a36Sopenharmony_ci return change; 77462306a36Sopenharmony_ci} 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci#define AD1816A_DOUBLE_TLV(xname, reg, shift_left, shift_right, mask, invert, xtlv) \ 77762306a36Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ 77862306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ 77962306a36Sopenharmony_ci .name = xname, .info = snd_ad1816a_info_double, \ 78062306a36Sopenharmony_ci .get = snd_ad1816a_get_double, .put = snd_ad1816a_put_double, \ 78162306a36Sopenharmony_ci .private_value = reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24), \ 78262306a36Sopenharmony_ci .tlv = { .p = (xtlv) } } 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci#define AD1816A_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \ 78562306a36Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ad1816a_info_double, \ 78662306a36Sopenharmony_ci .get = snd_ad1816a_get_double, .put = snd_ad1816a_put_double, \ 78762306a36Sopenharmony_ci .private_value = reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24) } 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_cistatic int snd_ad1816a_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 79062306a36Sopenharmony_ci{ 79162306a36Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; 79462306a36Sopenharmony_ci uinfo->count = 2; 79562306a36Sopenharmony_ci uinfo->value.integer.min = 0; 79662306a36Sopenharmony_ci uinfo->value.integer.max = mask; 79762306a36Sopenharmony_ci return 0; 79862306a36Sopenharmony_ci} 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_cistatic int snd_ad1816a_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 80162306a36Sopenharmony_ci{ 80262306a36Sopenharmony_ci struct snd_ad1816a *chip = snd_kcontrol_chip(kcontrol); 80362306a36Sopenharmony_ci unsigned long flags; 80462306a36Sopenharmony_ci int reg = kcontrol->private_value & 0xff; 80562306a36Sopenharmony_ci int shift_left = (kcontrol->private_value >> 8) & 0x0f; 80662306a36Sopenharmony_ci int shift_right = (kcontrol->private_value >> 12) & 0x0f; 80762306a36Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 80862306a36Sopenharmony_ci int invert = (kcontrol->private_value >> 24) & 0xff; 80962306a36Sopenharmony_ci unsigned short val; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci spin_lock_irqsave(&chip->lock, flags); 81262306a36Sopenharmony_ci val = snd_ad1816a_read(chip, reg); 81362306a36Sopenharmony_ci ucontrol->value.integer.value[0] = (val >> shift_left) & mask; 81462306a36Sopenharmony_ci ucontrol->value.integer.value[1] = (val >> shift_right) & mask; 81562306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->lock, flags); 81662306a36Sopenharmony_ci if (invert) { 81762306a36Sopenharmony_ci ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; 81862306a36Sopenharmony_ci ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; 81962306a36Sopenharmony_ci } 82062306a36Sopenharmony_ci return 0; 82162306a36Sopenharmony_ci} 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_cistatic int snd_ad1816a_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 82462306a36Sopenharmony_ci{ 82562306a36Sopenharmony_ci struct snd_ad1816a *chip = snd_kcontrol_chip(kcontrol); 82662306a36Sopenharmony_ci unsigned long flags; 82762306a36Sopenharmony_ci int reg = kcontrol->private_value & 0xff; 82862306a36Sopenharmony_ci int shift_left = (kcontrol->private_value >> 8) & 0x0f; 82962306a36Sopenharmony_ci int shift_right = (kcontrol->private_value >> 12) & 0x0f; 83062306a36Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 83162306a36Sopenharmony_ci int invert = (kcontrol->private_value >> 24) & 0xff; 83262306a36Sopenharmony_ci int change; 83362306a36Sopenharmony_ci unsigned short old_val, val1, val2; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci val1 = ucontrol->value.integer.value[0] & mask; 83662306a36Sopenharmony_ci val2 = ucontrol->value.integer.value[1] & mask; 83762306a36Sopenharmony_ci if (invert) { 83862306a36Sopenharmony_ci val1 = mask - val1; 83962306a36Sopenharmony_ci val2 = mask - val2; 84062306a36Sopenharmony_ci } 84162306a36Sopenharmony_ci val1 <<= shift_left; 84262306a36Sopenharmony_ci val2 <<= shift_right; 84362306a36Sopenharmony_ci spin_lock_irqsave(&chip->lock, flags); 84462306a36Sopenharmony_ci old_val = snd_ad1816a_read(chip, reg); 84562306a36Sopenharmony_ci val1 = (old_val & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2; 84662306a36Sopenharmony_ci change = val1 != old_val; 84762306a36Sopenharmony_ci snd_ad1816a_write(chip, reg, val1); 84862306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->lock, flags); 84962306a36Sopenharmony_ci return change; 85062306a36Sopenharmony_ci} 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_4bit, -4500, 300, 0); 85362306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_5bit, -4650, 150, 0); 85462306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0); 85562306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0); 85662306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0); 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_ad1816a_controls[] = { 85962306a36Sopenharmony_ciAD1816A_DOUBLE("Master Playback Switch", AD1816A_MASTER_ATT, 15, 7, 1, 1), 86062306a36Sopenharmony_ciAD1816A_DOUBLE_TLV("Master Playback Volume", AD1816A_MASTER_ATT, 8, 0, 31, 1, 86162306a36Sopenharmony_ci db_scale_5bit), 86262306a36Sopenharmony_ciAD1816A_DOUBLE("PCM Playback Switch", AD1816A_VOICE_ATT, 15, 7, 1, 1), 86362306a36Sopenharmony_ciAD1816A_DOUBLE_TLV("PCM Playback Volume", AD1816A_VOICE_ATT, 8, 0, 63, 1, 86462306a36Sopenharmony_ci db_scale_6bit), 86562306a36Sopenharmony_ciAD1816A_DOUBLE("Line Playback Switch", AD1816A_LINE_GAIN_ATT, 15, 7, 1, 1), 86662306a36Sopenharmony_ciAD1816A_DOUBLE_TLV("Line Playback Volume", AD1816A_LINE_GAIN_ATT, 8, 0, 31, 1, 86762306a36Sopenharmony_ci db_scale_5bit_12db_max), 86862306a36Sopenharmony_ciAD1816A_DOUBLE("CD Playback Switch", AD1816A_CD_GAIN_ATT, 15, 7, 1, 1), 86962306a36Sopenharmony_ciAD1816A_DOUBLE_TLV("CD Playback Volume", AD1816A_CD_GAIN_ATT, 8, 0, 31, 1, 87062306a36Sopenharmony_ci db_scale_5bit_12db_max), 87162306a36Sopenharmony_ciAD1816A_DOUBLE("Synth Playback Switch", AD1816A_SYNTH_GAIN_ATT, 15, 7, 1, 1), 87262306a36Sopenharmony_ciAD1816A_DOUBLE_TLV("Synth Playback Volume", AD1816A_SYNTH_GAIN_ATT, 8, 0, 31, 1, 87362306a36Sopenharmony_ci db_scale_5bit_12db_max), 87462306a36Sopenharmony_ciAD1816A_DOUBLE("FM Playback Switch", AD1816A_FM_ATT, 15, 7, 1, 1), 87562306a36Sopenharmony_ciAD1816A_DOUBLE_TLV("FM Playback Volume", AD1816A_FM_ATT, 8, 0, 63, 1, 87662306a36Sopenharmony_ci db_scale_6bit), 87762306a36Sopenharmony_ciAD1816A_SINGLE("Mic Playback Switch", AD1816A_MIC_GAIN_ATT, 15, 1, 1), 87862306a36Sopenharmony_ciAD1816A_SINGLE_TLV("Mic Playback Volume", AD1816A_MIC_GAIN_ATT, 8, 31, 1, 87962306a36Sopenharmony_ci db_scale_5bit_12db_max), 88062306a36Sopenharmony_ciAD1816A_SINGLE("Mic Boost", AD1816A_MIC_GAIN_ATT, 14, 1, 0), 88162306a36Sopenharmony_ciAD1816A_DOUBLE("Video Playback Switch", AD1816A_VID_GAIN_ATT, 15, 7, 1, 1), 88262306a36Sopenharmony_ciAD1816A_DOUBLE_TLV("Video Playback Volume", AD1816A_VID_GAIN_ATT, 8, 0, 31, 1, 88362306a36Sopenharmony_ci db_scale_5bit_12db_max), 88462306a36Sopenharmony_ciAD1816A_SINGLE("Phone Capture Switch", AD1816A_PHONE_IN_GAIN_ATT, 15, 1, 1), 88562306a36Sopenharmony_ciAD1816A_SINGLE_TLV("Phone Capture Volume", AD1816A_PHONE_IN_GAIN_ATT, 0, 15, 1, 88662306a36Sopenharmony_ci db_scale_4bit), 88762306a36Sopenharmony_ciAD1816A_SINGLE("Phone Playback Switch", AD1816A_PHONE_OUT_ATT, 7, 1, 1), 88862306a36Sopenharmony_ciAD1816A_SINGLE_TLV("Phone Playback Volume", AD1816A_PHONE_OUT_ATT, 0, 31, 1, 88962306a36Sopenharmony_ci db_scale_5bit), 89062306a36Sopenharmony_ci{ 89162306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 89262306a36Sopenharmony_ci .name = "Capture Source", 89362306a36Sopenharmony_ci .info = snd_ad1816a_info_mux, 89462306a36Sopenharmony_ci .get = snd_ad1816a_get_mux, 89562306a36Sopenharmony_ci .put = snd_ad1816a_put_mux, 89662306a36Sopenharmony_ci}, 89762306a36Sopenharmony_ciAD1816A_DOUBLE("Capture Switch", AD1816A_ADC_PGA, 15, 7, 1, 1), 89862306a36Sopenharmony_ciAD1816A_DOUBLE_TLV("Capture Volume", AD1816A_ADC_PGA, 8, 0, 15, 0, 89962306a36Sopenharmony_ci db_scale_rec_gain), 90062306a36Sopenharmony_ciAD1816A_SINGLE("3D Control - Switch", AD1816A_3D_PHAT_CTRL, 15, 1, 1), 90162306a36Sopenharmony_ciAD1816A_SINGLE("3D Control - Level", AD1816A_3D_PHAT_CTRL, 0, 15, 0), 90262306a36Sopenharmony_ci}; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ciint snd_ad1816a_mixer(struct snd_ad1816a *chip) 90562306a36Sopenharmony_ci{ 90662306a36Sopenharmony_ci struct snd_card *card; 90762306a36Sopenharmony_ci unsigned int idx; 90862306a36Sopenharmony_ci int err; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci if (snd_BUG_ON(!chip || !chip->card)) 91162306a36Sopenharmony_ci return -EINVAL; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci card = chip->card; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci strcpy(card->mixername, snd_ad1816a_chip_id(chip)); 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_ad1816a_controls); idx++) { 91862306a36Sopenharmony_ci err = snd_ctl_add(card, snd_ctl_new1(&snd_ad1816a_controls[idx], chip)); 91962306a36Sopenharmony_ci if (err < 0) 92062306a36Sopenharmony_ci return err; 92162306a36Sopenharmony_ci } 92262306a36Sopenharmony_ci return 0; 92362306a36Sopenharmony_ci} 924