162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver for Atmel AC97C 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2005-2009 Atmel Corporation 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#include <linux/clk.h> 862306a36Sopenharmony_ci#include <linux/delay.h> 962306a36Sopenharmony_ci#include <linux/bitmap.h> 1062306a36Sopenharmony_ci#include <linux/device.h> 1162306a36Sopenharmony_ci#include <linux/atmel_pdc.h> 1262306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 1362306a36Sopenharmony_ci#include <linux/init.h> 1462306a36Sopenharmony_ci#include <linux/interrupt.h> 1562306a36Sopenharmony_ci#include <linux/mod_devicetable.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/platform_device.h> 1862306a36Sopenharmony_ci#include <linux/mutex.h> 1962306a36Sopenharmony_ci#include <linux/types.h> 2062306a36Sopenharmony_ci#include <linux/io.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <sound/core.h> 2362306a36Sopenharmony_ci#include <sound/initval.h> 2462306a36Sopenharmony_ci#include <sound/pcm.h> 2562306a36Sopenharmony_ci#include <sound/pcm_params.h> 2662306a36Sopenharmony_ci#include <sound/ac97_codec.h> 2762306a36Sopenharmony_ci#include <sound/memalloc.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include "ac97c.h" 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* Serialize access to opened variable */ 3262306a36Sopenharmony_cistatic DEFINE_MUTEX(opened_mutex); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistruct atmel_ac97c { 3562306a36Sopenharmony_ci struct clk *pclk; 3662306a36Sopenharmony_ci struct platform_device *pdev; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci struct snd_pcm_substream *playback_substream; 3962306a36Sopenharmony_ci struct snd_pcm_substream *capture_substream; 4062306a36Sopenharmony_ci struct snd_card *card; 4162306a36Sopenharmony_ci struct snd_pcm *pcm; 4262306a36Sopenharmony_ci struct snd_ac97 *ac97; 4362306a36Sopenharmony_ci struct snd_ac97_bus *ac97_bus; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci u64 cur_format; 4662306a36Sopenharmony_ci unsigned int cur_rate; 4762306a36Sopenharmony_ci int playback_period, capture_period; 4862306a36Sopenharmony_ci /* Serialize access to opened variable */ 4962306a36Sopenharmony_ci spinlock_t lock; 5062306a36Sopenharmony_ci void __iomem *regs; 5162306a36Sopenharmony_ci int irq; 5262306a36Sopenharmony_ci int opened; 5362306a36Sopenharmony_ci struct gpio_desc *reset_pin; 5462306a36Sopenharmony_ci}; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci#define get_chip(card) ((struct atmel_ac97c *)(card)->private_data) 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#define ac97c_writel(chip, reg, val) \ 5962306a36Sopenharmony_ci __raw_writel((val), (chip)->regs + AC97C_##reg) 6062306a36Sopenharmony_ci#define ac97c_readl(chip, reg) \ 6162306a36Sopenharmony_ci __raw_readl((chip)->regs + AC97C_##reg) 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic const struct snd_pcm_hardware atmel_ac97c_hw = { 6462306a36Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP 6562306a36Sopenharmony_ci | SNDRV_PCM_INFO_MMAP_VALID 6662306a36Sopenharmony_ci | SNDRV_PCM_INFO_INTERLEAVED 6762306a36Sopenharmony_ci | SNDRV_PCM_INFO_BLOCK_TRANSFER 6862306a36Sopenharmony_ci | SNDRV_PCM_INFO_JOINT_DUPLEX 6962306a36Sopenharmony_ci | SNDRV_PCM_INFO_RESUME 7062306a36Sopenharmony_ci | SNDRV_PCM_INFO_PAUSE), 7162306a36Sopenharmony_ci .formats = (SNDRV_PCM_FMTBIT_S16_BE 7262306a36Sopenharmony_ci | SNDRV_PCM_FMTBIT_S16_LE), 7362306a36Sopenharmony_ci .rates = (SNDRV_PCM_RATE_CONTINUOUS), 7462306a36Sopenharmony_ci .rate_min = 4000, 7562306a36Sopenharmony_ci .rate_max = 48000, 7662306a36Sopenharmony_ci .channels_min = 1, 7762306a36Sopenharmony_ci .channels_max = 2, 7862306a36Sopenharmony_ci .buffer_bytes_max = 2 * 2 * 64 * 2048, 7962306a36Sopenharmony_ci .period_bytes_min = 4096, 8062306a36Sopenharmony_ci .period_bytes_max = 4096, 8162306a36Sopenharmony_ci .periods_min = 6, 8262306a36Sopenharmony_ci .periods_max = 64, 8362306a36Sopenharmony_ci}; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic int atmel_ac97c_playback_open(struct snd_pcm_substream *substream) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); 8862306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci mutex_lock(&opened_mutex); 9162306a36Sopenharmony_ci chip->opened++; 9262306a36Sopenharmony_ci runtime->hw = atmel_ac97c_hw; 9362306a36Sopenharmony_ci if (chip->cur_rate) { 9462306a36Sopenharmony_ci runtime->hw.rate_min = chip->cur_rate; 9562306a36Sopenharmony_ci runtime->hw.rate_max = chip->cur_rate; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci if (chip->cur_format) 9862306a36Sopenharmony_ci runtime->hw.formats = pcm_format_to_bits(chip->cur_format); 9962306a36Sopenharmony_ci mutex_unlock(&opened_mutex); 10062306a36Sopenharmony_ci chip->playback_substream = substream; 10162306a36Sopenharmony_ci return 0; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic int atmel_ac97c_capture_open(struct snd_pcm_substream *substream) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); 10762306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci mutex_lock(&opened_mutex); 11062306a36Sopenharmony_ci chip->opened++; 11162306a36Sopenharmony_ci runtime->hw = atmel_ac97c_hw; 11262306a36Sopenharmony_ci if (chip->cur_rate) { 11362306a36Sopenharmony_ci runtime->hw.rate_min = chip->cur_rate; 11462306a36Sopenharmony_ci runtime->hw.rate_max = chip->cur_rate; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci if (chip->cur_format) 11762306a36Sopenharmony_ci runtime->hw.formats = pcm_format_to_bits(chip->cur_format); 11862306a36Sopenharmony_ci mutex_unlock(&opened_mutex); 11962306a36Sopenharmony_ci chip->capture_substream = substream; 12062306a36Sopenharmony_ci return 0; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic int atmel_ac97c_playback_close(struct snd_pcm_substream *substream) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci mutex_lock(&opened_mutex); 12862306a36Sopenharmony_ci chip->opened--; 12962306a36Sopenharmony_ci if (!chip->opened) { 13062306a36Sopenharmony_ci chip->cur_rate = 0; 13162306a36Sopenharmony_ci chip->cur_format = 0; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci mutex_unlock(&opened_mutex); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci chip->playback_substream = NULL; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci return 0; 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic int atmel_ac97c_capture_close(struct snd_pcm_substream *substream) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci mutex_lock(&opened_mutex); 14562306a36Sopenharmony_ci chip->opened--; 14662306a36Sopenharmony_ci if (!chip->opened) { 14762306a36Sopenharmony_ci chip->cur_rate = 0; 14862306a36Sopenharmony_ci chip->cur_format = 0; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci mutex_unlock(&opened_mutex); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci chip->capture_substream = NULL; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci return 0; 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic int atmel_ac97c_playback_hw_params(struct snd_pcm_substream *substream, 15862306a36Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci /* Set restrictions to params. */ 16362306a36Sopenharmony_ci mutex_lock(&opened_mutex); 16462306a36Sopenharmony_ci chip->cur_rate = params_rate(hw_params); 16562306a36Sopenharmony_ci chip->cur_format = params_format(hw_params); 16662306a36Sopenharmony_ci mutex_unlock(&opened_mutex); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci return 0; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic int atmel_ac97c_capture_hw_params(struct snd_pcm_substream *substream, 17262306a36Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci /* Set restrictions to params. */ 17762306a36Sopenharmony_ci mutex_lock(&opened_mutex); 17862306a36Sopenharmony_ci chip->cur_rate = params_rate(hw_params); 17962306a36Sopenharmony_ci chip->cur_format = params_format(hw_params); 18062306a36Sopenharmony_ci mutex_unlock(&opened_mutex); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci return 0; 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic int atmel_ac97c_playback_prepare(struct snd_pcm_substream *substream) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); 18862306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 18962306a36Sopenharmony_ci int block_size = frames_to_bytes(runtime, runtime->period_size); 19062306a36Sopenharmony_ci unsigned long word = ac97c_readl(chip, OCA); 19162306a36Sopenharmony_ci int retval; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci chip->playback_period = 0; 19462306a36Sopenharmony_ci word &= ~(AC97C_CH_MASK(PCM_LEFT) | AC97C_CH_MASK(PCM_RIGHT)); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci /* assign channels to AC97C channel A */ 19762306a36Sopenharmony_ci switch (runtime->channels) { 19862306a36Sopenharmony_ci case 1: 19962306a36Sopenharmony_ci word |= AC97C_CH_ASSIGN(PCM_LEFT, A); 20062306a36Sopenharmony_ci break; 20162306a36Sopenharmony_ci case 2: 20262306a36Sopenharmony_ci word |= AC97C_CH_ASSIGN(PCM_LEFT, A) 20362306a36Sopenharmony_ci | AC97C_CH_ASSIGN(PCM_RIGHT, A); 20462306a36Sopenharmony_ci break; 20562306a36Sopenharmony_ci default: 20662306a36Sopenharmony_ci /* TODO: support more than two channels */ 20762306a36Sopenharmony_ci return -EINVAL; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci ac97c_writel(chip, OCA, word); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci /* configure sample format and size */ 21262306a36Sopenharmony_ci word = ac97c_readl(chip, CAMR); 21362306a36Sopenharmony_ci if (chip->opened <= 1) 21462306a36Sopenharmony_ci word = AC97C_CMR_DMAEN | AC97C_CMR_SIZE_16; 21562306a36Sopenharmony_ci else 21662306a36Sopenharmony_ci word |= AC97C_CMR_DMAEN | AC97C_CMR_SIZE_16; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci switch (runtime->format) { 21962306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 22062306a36Sopenharmony_ci break; 22162306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_BE: 22262306a36Sopenharmony_ci word &= ~(AC97C_CMR_CEM_LITTLE); 22362306a36Sopenharmony_ci break; 22462306a36Sopenharmony_ci default: 22562306a36Sopenharmony_ci word = ac97c_readl(chip, OCA); 22662306a36Sopenharmony_ci word &= ~(AC97C_CH_MASK(PCM_LEFT) | AC97C_CH_MASK(PCM_RIGHT)); 22762306a36Sopenharmony_ci ac97c_writel(chip, OCA, word); 22862306a36Sopenharmony_ci return -EINVAL; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci /* Enable underrun interrupt on channel A */ 23262306a36Sopenharmony_ci word |= AC97C_CSR_UNRUN; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci ac97c_writel(chip, CAMR, word); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci /* Enable channel A event interrupt */ 23762306a36Sopenharmony_ci word = ac97c_readl(chip, IMR); 23862306a36Sopenharmony_ci word |= AC97C_SR_CAEVT; 23962306a36Sopenharmony_ci ac97c_writel(chip, IER, word); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci /* set variable rate if needed */ 24262306a36Sopenharmony_ci if (runtime->rate != 48000) { 24362306a36Sopenharmony_ci word = ac97c_readl(chip, MR); 24462306a36Sopenharmony_ci word |= AC97C_MR_VRA; 24562306a36Sopenharmony_ci ac97c_writel(chip, MR, word); 24662306a36Sopenharmony_ci } else { 24762306a36Sopenharmony_ci word = ac97c_readl(chip, MR); 24862306a36Sopenharmony_ci word &= ~(AC97C_MR_VRA); 24962306a36Sopenharmony_ci ac97c_writel(chip, MR, word); 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci retval = snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, 25362306a36Sopenharmony_ci runtime->rate); 25462306a36Sopenharmony_ci if (retval) 25562306a36Sopenharmony_ci dev_dbg(&chip->pdev->dev, "could not set rate %d Hz\n", 25662306a36Sopenharmony_ci runtime->rate); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* Initialize and start the PDC */ 25962306a36Sopenharmony_ci writel(runtime->dma_addr, chip->regs + ATMEL_PDC_TPR); 26062306a36Sopenharmony_ci writel(block_size / 2, chip->regs + ATMEL_PDC_TCR); 26162306a36Sopenharmony_ci writel(runtime->dma_addr + block_size, chip->regs + ATMEL_PDC_TNPR); 26262306a36Sopenharmony_ci writel(block_size / 2, chip->regs + ATMEL_PDC_TNCR); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci return retval; 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic int atmel_ac97c_capture_prepare(struct snd_pcm_substream *substream) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); 27062306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 27162306a36Sopenharmony_ci int block_size = frames_to_bytes(runtime, runtime->period_size); 27262306a36Sopenharmony_ci unsigned long word = ac97c_readl(chip, ICA); 27362306a36Sopenharmony_ci int retval; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci chip->capture_period = 0; 27662306a36Sopenharmony_ci word &= ~(AC97C_CH_MASK(PCM_LEFT) | AC97C_CH_MASK(PCM_RIGHT)); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* assign channels to AC97C channel A */ 27962306a36Sopenharmony_ci switch (runtime->channels) { 28062306a36Sopenharmony_ci case 1: 28162306a36Sopenharmony_ci word |= AC97C_CH_ASSIGN(PCM_LEFT, A); 28262306a36Sopenharmony_ci break; 28362306a36Sopenharmony_ci case 2: 28462306a36Sopenharmony_ci word |= AC97C_CH_ASSIGN(PCM_LEFT, A) 28562306a36Sopenharmony_ci | AC97C_CH_ASSIGN(PCM_RIGHT, A); 28662306a36Sopenharmony_ci break; 28762306a36Sopenharmony_ci default: 28862306a36Sopenharmony_ci /* TODO: support more than two channels */ 28962306a36Sopenharmony_ci return -EINVAL; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci ac97c_writel(chip, ICA, word); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci /* configure sample format and size */ 29462306a36Sopenharmony_ci word = ac97c_readl(chip, CAMR); 29562306a36Sopenharmony_ci if (chip->opened <= 1) 29662306a36Sopenharmony_ci word = AC97C_CMR_DMAEN | AC97C_CMR_SIZE_16; 29762306a36Sopenharmony_ci else 29862306a36Sopenharmony_ci word |= AC97C_CMR_DMAEN | AC97C_CMR_SIZE_16; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci switch (runtime->format) { 30162306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 30262306a36Sopenharmony_ci break; 30362306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_BE: 30462306a36Sopenharmony_ci word &= ~(AC97C_CMR_CEM_LITTLE); 30562306a36Sopenharmony_ci break; 30662306a36Sopenharmony_ci default: 30762306a36Sopenharmony_ci word = ac97c_readl(chip, ICA); 30862306a36Sopenharmony_ci word &= ~(AC97C_CH_MASK(PCM_LEFT) | AC97C_CH_MASK(PCM_RIGHT)); 30962306a36Sopenharmony_ci ac97c_writel(chip, ICA, word); 31062306a36Sopenharmony_ci return -EINVAL; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci /* Enable overrun interrupt on channel A */ 31462306a36Sopenharmony_ci word |= AC97C_CSR_OVRUN; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci ac97c_writel(chip, CAMR, word); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci /* Enable channel A event interrupt */ 31962306a36Sopenharmony_ci word = ac97c_readl(chip, IMR); 32062306a36Sopenharmony_ci word |= AC97C_SR_CAEVT; 32162306a36Sopenharmony_ci ac97c_writel(chip, IER, word); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci /* set variable rate if needed */ 32462306a36Sopenharmony_ci if (runtime->rate != 48000) { 32562306a36Sopenharmony_ci word = ac97c_readl(chip, MR); 32662306a36Sopenharmony_ci word |= AC97C_MR_VRA; 32762306a36Sopenharmony_ci ac97c_writel(chip, MR, word); 32862306a36Sopenharmony_ci } else { 32962306a36Sopenharmony_ci word = ac97c_readl(chip, MR); 33062306a36Sopenharmony_ci word &= ~(AC97C_MR_VRA); 33162306a36Sopenharmony_ci ac97c_writel(chip, MR, word); 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci retval = snd_ac97_set_rate(chip->ac97, AC97_PCM_LR_ADC_RATE, 33562306a36Sopenharmony_ci runtime->rate); 33662306a36Sopenharmony_ci if (retval) 33762306a36Sopenharmony_ci dev_dbg(&chip->pdev->dev, "could not set rate %d Hz\n", 33862306a36Sopenharmony_ci runtime->rate); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci /* Initialize and start the PDC */ 34162306a36Sopenharmony_ci writel(runtime->dma_addr, chip->regs + ATMEL_PDC_RPR); 34262306a36Sopenharmony_ci writel(block_size / 2, chip->regs + ATMEL_PDC_RCR); 34362306a36Sopenharmony_ci writel(runtime->dma_addr + block_size, chip->regs + ATMEL_PDC_RNPR); 34462306a36Sopenharmony_ci writel(block_size / 2, chip->regs + ATMEL_PDC_RNCR); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci return retval; 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistatic int 35062306a36Sopenharmony_ciatmel_ac97c_playback_trigger(struct snd_pcm_substream *substream, int cmd) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); 35362306a36Sopenharmony_ci unsigned long camr, ptcr = 0; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci camr = ac97c_readl(chip, CAMR); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci switch (cmd) { 35862306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 35962306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 36062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 36162306a36Sopenharmony_ci ptcr = ATMEL_PDC_TXTEN; 36262306a36Sopenharmony_ci camr |= AC97C_CMR_CENA | AC97C_CSR_ENDTX; 36362306a36Sopenharmony_ci break; 36462306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 36562306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 36662306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 36762306a36Sopenharmony_ci ptcr |= ATMEL_PDC_TXTDIS; 36862306a36Sopenharmony_ci if (chip->opened <= 1) 36962306a36Sopenharmony_ci camr &= ~AC97C_CMR_CENA; 37062306a36Sopenharmony_ci break; 37162306a36Sopenharmony_ci default: 37262306a36Sopenharmony_ci return -EINVAL; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci ac97c_writel(chip, CAMR, camr); 37662306a36Sopenharmony_ci writel(ptcr, chip->regs + ATMEL_PDC_PTCR); 37762306a36Sopenharmony_ci return 0; 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_cistatic int 38162306a36Sopenharmony_ciatmel_ac97c_capture_trigger(struct snd_pcm_substream *substream, int cmd) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); 38462306a36Sopenharmony_ci unsigned long camr, ptcr = 0; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci camr = ac97c_readl(chip, CAMR); 38762306a36Sopenharmony_ci ptcr = readl(chip->regs + ATMEL_PDC_PTSR); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci switch (cmd) { 39062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 39162306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 39262306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 39362306a36Sopenharmony_ci ptcr = ATMEL_PDC_RXTEN; 39462306a36Sopenharmony_ci camr |= AC97C_CMR_CENA | AC97C_CSR_ENDRX; 39562306a36Sopenharmony_ci break; 39662306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 39762306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 39862306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 39962306a36Sopenharmony_ci ptcr |= ATMEL_PDC_RXTDIS; 40062306a36Sopenharmony_ci if (chip->opened <= 1) 40162306a36Sopenharmony_ci camr &= ~AC97C_CMR_CENA; 40262306a36Sopenharmony_ci break; 40362306a36Sopenharmony_ci default: 40462306a36Sopenharmony_ci return -EINVAL; 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci ac97c_writel(chip, CAMR, camr); 40862306a36Sopenharmony_ci writel(ptcr, chip->regs + ATMEL_PDC_PTCR); 40962306a36Sopenharmony_ci return 0; 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cistatic snd_pcm_uframes_t 41362306a36Sopenharmony_ciatmel_ac97c_playback_pointer(struct snd_pcm_substream *substream) 41462306a36Sopenharmony_ci{ 41562306a36Sopenharmony_ci struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); 41662306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 41762306a36Sopenharmony_ci snd_pcm_uframes_t frames; 41862306a36Sopenharmony_ci unsigned long bytes; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci bytes = readl(chip->regs + ATMEL_PDC_TPR); 42162306a36Sopenharmony_ci bytes -= runtime->dma_addr; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci frames = bytes_to_frames(runtime, bytes); 42462306a36Sopenharmony_ci if (frames >= runtime->buffer_size) 42562306a36Sopenharmony_ci frames -= runtime->buffer_size; 42662306a36Sopenharmony_ci return frames; 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cistatic snd_pcm_uframes_t 43062306a36Sopenharmony_ciatmel_ac97c_capture_pointer(struct snd_pcm_substream *substream) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); 43362306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 43462306a36Sopenharmony_ci snd_pcm_uframes_t frames; 43562306a36Sopenharmony_ci unsigned long bytes; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci bytes = readl(chip->regs + ATMEL_PDC_RPR); 43862306a36Sopenharmony_ci bytes -= runtime->dma_addr; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci frames = bytes_to_frames(runtime, bytes); 44162306a36Sopenharmony_ci if (frames >= runtime->buffer_size) 44262306a36Sopenharmony_ci frames -= runtime->buffer_size; 44362306a36Sopenharmony_ci return frames; 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cistatic const struct snd_pcm_ops atmel_ac97_playback_ops = { 44762306a36Sopenharmony_ci .open = atmel_ac97c_playback_open, 44862306a36Sopenharmony_ci .close = atmel_ac97c_playback_close, 44962306a36Sopenharmony_ci .hw_params = atmel_ac97c_playback_hw_params, 45062306a36Sopenharmony_ci .prepare = atmel_ac97c_playback_prepare, 45162306a36Sopenharmony_ci .trigger = atmel_ac97c_playback_trigger, 45262306a36Sopenharmony_ci .pointer = atmel_ac97c_playback_pointer, 45362306a36Sopenharmony_ci}; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_cistatic const struct snd_pcm_ops atmel_ac97_capture_ops = { 45662306a36Sopenharmony_ci .open = atmel_ac97c_capture_open, 45762306a36Sopenharmony_ci .close = atmel_ac97c_capture_close, 45862306a36Sopenharmony_ci .hw_params = atmel_ac97c_capture_hw_params, 45962306a36Sopenharmony_ci .prepare = atmel_ac97c_capture_prepare, 46062306a36Sopenharmony_ci .trigger = atmel_ac97c_capture_trigger, 46162306a36Sopenharmony_ci .pointer = atmel_ac97c_capture_pointer, 46262306a36Sopenharmony_ci}; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_cistatic irqreturn_t atmel_ac97c_interrupt(int irq, void *dev) 46562306a36Sopenharmony_ci{ 46662306a36Sopenharmony_ci struct atmel_ac97c *chip = (struct atmel_ac97c *)dev; 46762306a36Sopenharmony_ci irqreturn_t retval = IRQ_NONE; 46862306a36Sopenharmony_ci u32 sr = ac97c_readl(chip, SR); 46962306a36Sopenharmony_ci u32 casr = ac97c_readl(chip, CASR); 47062306a36Sopenharmony_ci u32 cosr = ac97c_readl(chip, COSR); 47162306a36Sopenharmony_ci u32 camr = ac97c_readl(chip, CAMR); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci if (sr & AC97C_SR_CAEVT) { 47462306a36Sopenharmony_ci struct snd_pcm_runtime *runtime; 47562306a36Sopenharmony_ci int offset, next_period, block_size; 47662306a36Sopenharmony_ci dev_dbg(&chip->pdev->dev, "channel A event%s%s%s%s%s%s\n", 47762306a36Sopenharmony_ci (casr & AC97C_CSR_OVRUN) ? " OVRUN" : "", 47862306a36Sopenharmony_ci (casr & AC97C_CSR_RXRDY) ? " RXRDY" : "", 47962306a36Sopenharmony_ci (casr & AC97C_CSR_UNRUN) ? " UNRUN" : "", 48062306a36Sopenharmony_ci (casr & AC97C_CSR_TXEMPTY) ? " TXEMPTY" : "", 48162306a36Sopenharmony_ci (casr & AC97C_CSR_TXRDY) ? " TXRDY" : "", 48262306a36Sopenharmony_ci !casr ? " NONE" : ""); 48362306a36Sopenharmony_ci if ((casr & camr) & AC97C_CSR_ENDTX) { 48462306a36Sopenharmony_ci runtime = chip->playback_substream->runtime; 48562306a36Sopenharmony_ci block_size = frames_to_bytes(runtime, runtime->period_size); 48662306a36Sopenharmony_ci chip->playback_period++; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci if (chip->playback_period == runtime->periods) 48962306a36Sopenharmony_ci chip->playback_period = 0; 49062306a36Sopenharmony_ci next_period = chip->playback_period + 1; 49162306a36Sopenharmony_ci if (next_period == runtime->periods) 49262306a36Sopenharmony_ci next_period = 0; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci offset = block_size * next_period; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci writel(runtime->dma_addr + offset, chip->regs + ATMEL_PDC_TNPR); 49762306a36Sopenharmony_ci writel(block_size / 2, chip->regs + ATMEL_PDC_TNCR); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci snd_pcm_period_elapsed(chip->playback_substream); 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci if ((casr & camr) & AC97C_CSR_ENDRX) { 50262306a36Sopenharmony_ci runtime = chip->capture_substream->runtime; 50362306a36Sopenharmony_ci block_size = frames_to_bytes(runtime, runtime->period_size); 50462306a36Sopenharmony_ci chip->capture_period++; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci if (chip->capture_period == runtime->periods) 50762306a36Sopenharmony_ci chip->capture_period = 0; 50862306a36Sopenharmony_ci next_period = chip->capture_period + 1; 50962306a36Sopenharmony_ci if (next_period == runtime->periods) 51062306a36Sopenharmony_ci next_period = 0; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci offset = block_size * next_period; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci writel(runtime->dma_addr + offset, chip->regs + ATMEL_PDC_RNPR); 51562306a36Sopenharmony_ci writel(block_size / 2, chip->regs + ATMEL_PDC_RNCR); 51662306a36Sopenharmony_ci snd_pcm_period_elapsed(chip->capture_substream); 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci retval = IRQ_HANDLED; 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci if (sr & AC97C_SR_COEVT) { 52262306a36Sopenharmony_ci dev_info(&chip->pdev->dev, "codec channel event%s%s%s%s%s\n", 52362306a36Sopenharmony_ci (cosr & AC97C_CSR_OVRUN) ? " OVRUN" : "", 52462306a36Sopenharmony_ci (cosr & AC97C_CSR_RXRDY) ? " RXRDY" : "", 52562306a36Sopenharmony_ci (cosr & AC97C_CSR_TXEMPTY) ? " TXEMPTY" : "", 52662306a36Sopenharmony_ci (cosr & AC97C_CSR_TXRDY) ? " TXRDY" : "", 52762306a36Sopenharmony_ci !cosr ? " NONE" : ""); 52862306a36Sopenharmony_ci retval = IRQ_HANDLED; 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci if (retval == IRQ_NONE) { 53262306a36Sopenharmony_ci dev_err(&chip->pdev->dev, "spurious interrupt sr 0x%08x " 53362306a36Sopenharmony_ci "casr 0x%08x cosr 0x%08x\n", sr, casr, cosr); 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci return retval; 53762306a36Sopenharmony_ci} 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_cistatic const struct ac97_pcm at91_ac97_pcm_defs[] = { 54062306a36Sopenharmony_ci /* Playback */ 54162306a36Sopenharmony_ci { 54262306a36Sopenharmony_ci .exclusive = 1, 54362306a36Sopenharmony_ci .r = { { 54462306a36Sopenharmony_ci .slots = ((1 << AC97_SLOT_PCM_LEFT) 54562306a36Sopenharmony_ci | (1 << AC97_SLOT_PCM_RIGHT)), 54662306a36Sopenharmony_ci } }, 54762306a36Sopenharmony_ci }, 54862306a36Sopenharmony_ci /* PCM in */ 54962306a36Sopenharmony_ci { 55062306a36Sopenharmony_ci .stream = 1, 55162306a36Sopenharmony_ci .exclusive = 1, 55262306a36Sopenharmony_ci .r = { { 55362306a36Sopenharmony_ci .slots = ((1 << AC97_SLOT_PCM_LEFT) 55462306a36Sopenharmony_ci | (1 << AC97_SLOT_PCM_RIGHT)), 55562306a36Sopenharmony_ci } } 55662306a36Sopenharmony_ci }, 55762306a36Sopenharmony_ci /* Mic in */ 55862306a36Sopenharmony_ci { 55962306a36Sopenharmony_ci .stream = 1, 56062306a36Sopenharmony_ci .exclusive = 1, 56162306a36Sopenharmony_ci .r = { { 56262306a36Sopenharmony_ci .slots = (1<<AC97_SLOT_MIC), 56362306a36Sopenharmony_ci } } 56462306a36Sopenharmony_ci }, 56562306a36Sopenharmony_ci}; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cistatic int atmel_ac97c_pcm_new(struct atmel_ac97c *chip) 56862306a36Sopenharmony_ci{ 56962306a36Sopenharmony_ci struct snd_pcm *pcm; 57062306a36Sopenharmony_ci struct snd_pcm_hardware hw = atmel_ac97c_hw; 57162306a36Sopenharmony_ci int retval; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci retval = snd_ac97_pcm_assign(chip->ac97_bus, 57462306a36Sopenharmony_ci ARRAY_SIZE(at91_ac97_pcm_defs), 57562306a36Sopenharmony_ci at91_ac97_pcm_defs); 57662306a36Sopenharmony_ci if (retval) 57762306a36Sopenharmony_ci return retval; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci retval = snd_pcm_new(chip->card, chip->card->shortname, 0, 1, 1, &pcm); 58062306a36Sopenharmony_ci if (retval) 58162306a36Sopenharmony_ci return retval; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &atmel_ac97_capture_ops); 58462306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &atmel_ac97_playback_ops); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, 58762306a36Sopenharmony_ci &chip->pdev->dev, hw.periods_min * hw.period_bytes_min, 58862306a36Sopenharmony_ci hw.buffer_bytes_max); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci pcm->private_data = chip; 59162306a36Sopenharmony_ci pcm->info_flags = 0; 59262306a36Sopenharmony_ci strcpy(pcm->name, chip->card->shortname); 59362306a36Sopenharmony_ci chip->pcm = pcm; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci return 0; 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_cistatic int atmel_ac97c_mixer_new(struct atmel_ac97c *chip) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci struct snd_ac97_template template; 60162306a36Sopenharmony_ci memset(&template, 0, sizeof(template)); 60262306a36Sopenharmony_ci template.private_data = chip; 60362306a36Sopenharmony_ci return snd_ac97_mixer(chip->ac97_bus, &template, &chip->ac97); 60462306a36Sopenharmony_ci} 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_cistatic void atmel_ac97c_write(struct snd_ac97 *ac97, unsigned short reg, 60762306a36Sopenharmony_ci unsigned short val) 60862306a36Sopenharmony_ci{ 60962306a36Sopenharmony_ci struct atmel_ac97c *chip = get_chip(ac97); 61062306a36Sopenharmony_ci unsigned long word; 61162306a36Sopenharmony_ci int timeout = 40; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci word = (reg & 0x7f) << 16 | val; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci do { 61662306a36Sopenharmony_ci if (ac97c_readl(chip, COSR) & AC97C_CSR_TXRDY) { 61762306a36Sopenharmony_ci ac97c_writel(chip, COTHR, word); 61862306a36Sopenharmony_ci return; 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci udelay(1); 62162306a36Sopenharmony_ci } while (--timeout); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci dev_dbg(&chip->pdev->dev, "codec write timeout\n"); 62462306a36Sopenharmony_ci} 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_cistatic unsigned short atmel_ac97c_read(struct snd_ac97 *ac97, 62762306a36Sopenharmony_ci unsigned short reg) 62862306a36Sopenharmony_ci{ 62962306a36Sopenharmony_ci struct atmel_ac97c *chip = get_chip(ac97); 63062306a36Sopenharmony_ci unsigned long word; 63162306a36Sopenharmony_ci int timeout = 40; 63262306a36Sopenharmony_ci int write = 10; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci word = (0x80 | (reg & 0x7f)) << 16; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci if ((ac97c_readl(chip, COSR) & AC97C_CSR_RXRDY) != 0) 63762306a36Sopenharmony_ci ac97c_readl(chip, CORHR); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ciretry_write: 64062306a36Sopenharmony_ci timeout = 40; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci do { 64362306a36Sopenharmony_ci if ((ac97c_readl(chip, COSR) & AC97C_CSR_TXRDY) != 0) { 64462306a36Sopenharmony_ci ac97c_writel(chip, COTHR, word); 64562306a36Sopenharmony_ci goto read_reg; 64662306a36Sopenharmony_ci } 64762306a36Sopenharmony_ci udelay(10); 64862306a36Sopenharmony_ci } while (--timeout); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci if (!--write) 65162306a36Sopenharmony_ci goto timed_out; 65262306a36Sopenharmony_ci goto retry_write; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ciread_reg: 65562306a36Sopenharmony_ci do { 65662306a36Sopenharmony_ci if ((ac97c_readl(chip, COSR) & AC97C_CSR_RXRDY) != 0) { 65762306a36Sopenharmony_ci unsigned short val = ac97c_readl(chip, CORHR); 65862306a36Sopenharmony_ci return val; 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci udelay(10); 66162306a36Sopenharmony_ci } while (--timeout); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci if (!--write) 66462306a36Sopenharmony_ci goto timed_out; 66562306a36Sopenharmony_ci goto retry_write; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_citimed_out: 66862306a36Sopenharmony_ci dev_dbg(&chip->pdev->dev, "codec read timeout\n"); 66962306a36Sopenharmony_ci return 0xffff; 67062306a36Sopenharmony_ci} 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_cistatic void atmel_ac97c_reset(struct atmel_ac97c *chip) 67362306a36Sopenharmony_ci{ 67462306a36Sopenharmony_ci ac97c_writel(chip, MR, 0); 67562306a36Sopenharmony_ci ac97c_writel(chip, MR, AC97C_MR_ENA); 67662306a36Sopenharmony_ci ac97c_writel(chip, CAMR, 0); 67762306a36Sopenharmony_ci ac97c_writel(chip, COMR, 0); 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci if (!IS_ERR(chip->reset_pin)) { 68062306a36Sopenharmony_ci gpiod_set_value(chip->reset_pin, 0); 68162306a36Sopenharmony_ci /* AC97 v2.2 specifications says minimum 1 us. */ 68262306a36Sopenharmony_ci udelay(2); 68362306a36Sopenharmony_ci gpiod_set_value(chip->reset_pin, 1); 68462306a36Sopenharmony_ci } else { 68562306a36Sopenharmony_ci ac97c_writel(chip, MR, AC97C_MR_WRST | AC97C_MR_ENA); 68662306a36Sopenharmony_ci udelay(2); 68762306a36Sopenharmony_ci ac97c_writel(chip, MR, AC97C_MR_ENA); 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci} 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_cistatic const struct of_device_id atmel_ac97c_dt_ids[] = { 69262306a36Sopenharmony_ci { .compatible = "atmel,at91sam9263-ac97c", }, 69362306a36Sopenharmony_ci { } 69462306a36Sopenharmony_ci}; 69562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, atmel_ac97c_dt_ids); 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_cistatic int atmel_ac97c_probe(struct platform_device *pdev) 69862306a36Sopenharmony_ci{ 69962306a36Sopenharmony_ci struct device *dev = &pdev->dev; 70062306a36Sopenharmony_ci struct snd_card *card; 70162306a36Sopenharmony_ci struct atmel_ac97c *chip; 70262306a36Sopenharmony_ci struct resource *regs; 70362306a36Sopenharmony_ci struct clk *pclk; 70462306a36Sopenharmony_ci static const struct snd_ac97_bus_ops ops = { 70562306a36Sopenharmony_ci .write = atmel_ac97c_write, 70662306a36Sopenharmony_ci .read = atmel_ac97c_read, 70762306a36Sopenharmony_ci }; 70862306a36Sopenharmony_ci int retval; 70962306a36Sopenharmony_ci int irq; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); 71262306a36Sopenharmony_ci if (!regs) { 71362306a36Sopenharmony_ci dev_dbg(&pdev->dev, "no memory resource\n"); 71462306a36Sopenharmony_ci return -ENXIO; 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 71862306a36Sopenharmony_ci if (irq < 0) { 71962306a36Sopenharmony_ci dev_dbg(&pdev->dev, "could not get irq: %d\n", irq); 72062306a36Sopenharmony_ci return irq; 72162306a36Sopenharmony_ci } 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci pclk = clk_get(&pdev->dev, "ac97_clk"); 72462306a36Sopenharmony_ci if (IS_ERR(pclk)) { 72562306a36Sopenharmony_ci dev_dbg(&pdev->dev, "no peripheral clock\n"); 72662306a36Sopenharmony_ci return PTR_ERR(pclk); 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci retval = clk_prepare_enable(pclk); 72962306a36Sopenharmony_ci if (retval) 73062306a36Sopenharmony_ci goto err_prepare_enable; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci retval = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1, 73362306a36Sopenharmony_ci SNDRV_DEFAULT_STR1, THIS_MODULE, 73462306a36Sopenharmony_ci sizeof(struct atmel_ac97c), &card); 73562306a36Sopenharmony_ci if (retval) { 73662306a36Sopenharmony_ci dev_dbg(&pdev->dev, "could not create sound card device\n"); 73762306a36Sopenharmony_ci goto err_snd_card_new; 73862306a36Sopenharmony_ci } 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci chip = get_chip(card); 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci retval = request_irq(irq, atmel_ac97c_interrupt, 0, "AC97C", chip); 74362306a36Sopenharmony_ci if (retval) { 74462306a36Sopenharmony_ci dev_dbg(&pdev->dev, "unable to request irq %d\n", irq); 74562306a36Sopenharmony_ci goto err_request_irq; 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci chip->irq = irq; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci spin_lock_init(&chip->lock); 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci strcpy(card->driver, "Atmel AC97C"); 75262306a36Sopenharmony_ci strcpy(card->shortname, "Atmel AC97C"); 75362306a36Sopenharmony_ci sprintf(card->longname, "Atmel AC97 controller"); 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci chip->card = card; 75662306a36Sopenharmony_ci chip->pclk = pclk; 75762306a36Sopenharmony_ci chip->pdev = pdev; 75862306a36Sopenharmony_ci chip->regs = ioremap(regs->start, resource_size(regs)); 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci if (!chip->regs) { 76162306a36Sopenharmony_ci dev_dbg(&pdev->dev, "could not remap register memory\n"); 76262306a36Sopenharmony_ci retval = -ENOMEM; 76362306a36Sopenharmony_ci goto err_ioremap; 76462306a36Sopenharmony_ci } 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci chip->reset_pin = devm_gpiod_get_index(dev, "ac97", 2, GPIOD_OUT_HIGH); 76762306a36Sopenharmony_ci if (IS_ERR(chip->reset_pin)) 76862306a36Sopenharmony_ci dev_dbg(dev, "reset pin not available\n"); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci atmel_ac97c_reset(chip); 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci /* Enable overrun interrupt from codec channel */ 77362306a36Sopenharmony_ci ac97c_writel(chip, COMR, AC97C_CSR_OVRUN); 77462306a36Sopenharmony_ci ac97c_writel(chip, IER, ac97c_readl(chip, IMR) | AC97C_SR_COEVT); 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci retval = snd_ac97_bus(card, 0, &ops, chip, &chip->ac97_bus); 77762306a36Sopenharmony_ci if (retval) { 77862306a36Sopenharmony_ci dev_dbg(&pdev->dev, "could not register on ac97 bus\n"); 77962306a36Sopenharmony_ci goto err_ac97_bus; 78062306a36Sopenharmony_ci } 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci retval = atmel_ac97c_mixer_new(chip); 78362306a36Sopenharmony_ci if (retval) { 78462306a36Sopenharmony_ci dev_dbg(&pdev->dev, "could not register ac97 mixer\n"); 78562306a36Sopenharmony_ci goto err_ac97_bus; 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci retval = atmel_ac97c_pcm_new(chip); 78962306a36Sopenharmony_ci if (retval) { 79062306a36Sopenharmony_ci dev_dbg(&pdev->dev, "could not register ac97 pcm device\n"); 79162306a36Sopenharmony_ci goto err_ac97_bus; 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci retval = snd_card_register(card); 79562306a36Sopenharmony_ci if (retval) { 79662306a36Sopenharmony_ci dev_dbg(&pdev->dev, "could not register sound card\n"); 79762306a36Sopenharmony_ci goto err_ac97_bus; 79862306a36Sopenharmony_ci } 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci platform_set_drvdata(pdev, card); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci dev_info(&pdev->dev, "Atmel AC97 controller at 0x%p, irq = %d\n", 80362306a36Sopenharmony_ci chip->regs, irq); 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci return 0; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_cierr_ac97_bus: 80862306a36Sopenharmony_ci iounmap(chip->regs); 80962306a36Sopenharmony_cierr_ioremap: 81062306a36Sopenharmony_ci free_irq(irq, chip); 81162306a36Sopenharmony_cierr_request_irq: 81262306a36Sopenharmony_ci snd_card_free(card); 81362306a36Sopenharmony_cierr_snd_card_new: 81462306a36Sopenharmony_ci clk_disable_unprepare(pclk); 81562306a36Sopenharmony_cierr_prepare_enable: 81662306a36Sopenharmony_ci clk_put(pclk); 81762306a36Sopenharmony_ci return retval; 81862306a36Sopenharmony_ci} 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 82162306a36Sopenharmony_cistatic int atmel_ac97c_suspend(struct device *pdev) 82262306a36Sopenharmony_ci{ 82362306a36Sopenharmony_ci struct snd_card *card = dev_get_drvdata(pdev); 82462306a36Sopenharmony_ci struct atmel_ac97c *chip = card->private_data; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci clk_disable_unprepare(chip->pclk); 82762306a36Sopenharmony_ci return 0; 82862306a36Sopenharmony_ci} 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_cistatic int atmel_ac97c_resume(struct device *pdev) 83162306a36Sopenharmony_ci{ 83262306a36Sopenharmony_ci struct snd_card *card = dev_get_drvdata(pdev); 83362306a36Sopenharmony_ci struct atmel_ac97c *chip = card->private_data; 83462306a36Sopenharmony_ci int ret = clk_prepare_enable(chip->pclk); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci return ret; 83762306a36Sopenharmony_ci} 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(atmel_ac97c_pm, atmel_ac97c_suspend, atmel_ac97c_resume); 84062306a36Sopenharmony_ci#define ATMEL_AC97C_PM_OPS &atmel_ac97c_pm 84162306a36Sopenharmony_ci#else 84262306a36Sopenharmony_ci#define ATMEL_AC97C_PM_OPS NULL 84362306a36Sopenharmony_ci#endif 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_cistatic void atmel_ac97c_remove(struct platform_device *pdev) 84662306a36Sopenharmony_ci{ 84762306a36Sopenharmony_ci struct snd_card *card = platform_get_drvdata(pdev); 84862306a36Sopenharmony_ci struct atmel_ac97c *chip = get_chip(card); 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci ac97c_writel(chip, CAMR, 0); 85162306a36Sopenharmony_ci ac97c_writel(chip, COMR, 0); 85262306a36Sopenharmony_ci ac97c_writel(chip, MR, 0); 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci clk_disable_unprepare(chip->pclk); 85562306a36Sopenharmony_ci clk_put(chip->pclk); 85662306a36Sopenharmony_ci iounmap(chip->regs); 85762306a36Sopenharmony_ci free_irq(chip->irq, chip); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci snd_card_free(card); 86062306a36Sopenharmony_ci} 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_cistatic struct platform_driver atmel_ac97c_driver = { 86362306a36Sopenharmony_ci .probe = atmel_ac97c_probe, 86462306a36Sopenharmony_ci .remove_new = atmel_ac97c_remove, 86562306a36Sopenharmony_ci .driver = { 86662306a36Sopenharmony_ci .name = "atmel_ac97c", 86762306a36Sopenharmony_ci .pm = ATMEL_AC97C_PM_OPS, 86862306a36Sopenharmony_ci .of_match_table = atmel_ac97c_dt_ids, 86962306a36Sopenharmony_ci }, 87062306a36Sopenharmony_ci}; 87162306a36Sopenharmony_cimodule_platform_driver(atmel_ac97c_driver); 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 87462306a36Sopenharmony_ciMODULE_DESCRIPTION("Driver for Atmel AC97 controller"); 87562306a36Sopenharmony_ciMODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>"); 876