14ede028fSHans-Christian Egtvedt /* 2128ed6a9SHans-Christian Egtvedt * Driver for Atmel AC97C 34ede028fSHans-Christian Egtvedt * 44ede028fSHans-Christian Egtvedt * Copyright (C) 2005-2009 Atmel Corporation 54ede028fSHans-Christian Egtvedt * 64ede028fSHans-Christian Egtvedt * This program is free software; you can redistribute it and/or modify it 74ede028fSHans-Christian Egtvedt * under the terms of the GNU General Public License version 2 as published by 84ede028fSHans-Christian Egtvedt * the Free Software Foundation. 94ede028fSHans-Christian Egtvedt */ 104ede028fSHans-Christian Egtvedt #include <linux/clk.h> 114ede028fSHans-Christian Egtvedt #include <linux/delay.h> 124ede028fSHans-Christian Egtvedt #include <linux/bitmap.h> 13128ed6a9SHans-Christian Egtvedt #include <linux/device.h> 147177395fSSedji Gaouaou #include <linux/atmel_pdc.h> 154ede028fSHans-Christian Egtvedt #include <linux/init.h> 164ede028fSHans-Christian Egtvedt #include <linux/interrupt.h> 174ede028fSHans-Christian Egtvedt #include <linux/module.h> 184ede028fSHans-Christian Egtvedt #include <linux/platform_device.h> 194ede028fSHans-Christian Egtvedt #include <linux/mutex.h> 204ede028fSHans-Christian Egtvedt #include <linux/gpio.h> 21e2b35f3dSViresh Kumar #include <linux/types.h> 224ede028fSHans-Christian Egtvedt #include <linux/io.h> 23b2d8957fSAlexander Stein #include <linux/of.h> 24b2d8957fSAlexander Stein #include <linux/of_gpio.h> 25b2d8957fSAlexander Stein #include <linux/of_device.h> 264ede028fSHans-Christian Egtvedt 274ede028fSHans-Christian Egtvedt #include <sound/core.h> 284ede028fSHans-Christian Egtvedt #include <sound/initval.h> 294ede028fSHans-Christian Egtvedt #include <sound/pcm.h> 304ede028fSHans-Christian Egtvedt #include <sound/pcm_params.h> 314ede028fSHans-Christian Egtvedt #include <sound/ac97_codec.h> 324ede028fSHans-Christian Egtvedt #include <sound/atmel-ac97c.h> 334ede028fSHans-Christian Egtvedt #include <sound/memalloc.h> 344ede028fSHans-Christian Egtvedt 354ede028fSHans-Christian Egtvedt #include "ac97c.h" 364ede028fSHans-Christian Egtvedt 374ede028fSHans-Christian Egtvedt /* Serialize access to opened variable */ 384ede028fSHans-Christian Egtvedt static DEFINE_MUTEX(opened_mutex); 394ede028fSHans-Christian Egtvedt 404ede028fSHans-Christian Egtvedt struct atmel_ac97c { 414ede028fSHans-Christian Egtvedt struct clk *pclk; 424ede028fSHans-Christian Egtvedt struct platform_device *pdev; 434ede028fSHans-Christian Egtvedt 444ede028fSHans-Christian Egtvedt struct snd_pcm_substream *playback_substream; 454ede028fSHans-Christian Egtvedt struct snd_pcm_substream *capture_substream; 464ede028fSHans-Christian Egtvedt struct snd_card *card; 474ede028fSHans-Christian Egtvedt struct snd_pcm *pcm; 484ede028fSHans-Christian Egtvedt struct snd_ac97 *ac97; 494ede028fSHans-Christian Egtvedt struct snd_ac97_bus *ac97_bus; 504ede028fSHans-Christian Egtvedt 514ede028fSHans-Christian Egtvedt u64 cur_format; 524ede028fSHans-Christian Egtvedt unsigned int cur_rate; 537177395fSSedji Gaouaou int playback_period, capture_period; 544ede028fSHans-Christian Egtvedt /* Serialize access to opened variable */ 554ede028fSHans-Christian Egtvedt spinlock_t lock; 564ede028fSHans-Christian Egtvedt void __iomem *regs; 57df163587SHans-Christian Egtvedt int irq; 584ede028fSHans-Christian Egtvedt int opened; 594ede028fSHans-Christian Egtvedt int reset_pin; 604ede028fSHans-Christian Egtvedt }; 614ede028fSHans-Christian Egtvedt 624ede028fSHans-Christian Egtvedt #define get_chip(card) ((struct atmel_ac97c *)(card)->private_data) 634ede028fSHans-Christian Egtvedt 644ede028fSHans-Christian Egtvedt #define ac97c_writel(chip, reg, val) \ 654ede028fSHans-Christian Egtvedt __raw_writel((val), (chip)->regs + AC97C_##reg) 664ede028fSHans-Christian Egtvedt #define ac97c_readl(chip, reg) \ 674ede028fSHans-Christian Egtvedt __raw_readl((chip)->regs + AC97C_##reg) 684ede028fSHans-Christian Egtvedt 6985ab3738SBhumika Goyal static const struct snd_pcm_hardware atmel_ac97c_hw = { 704ede028fSHans-Christian Egtvedt .info = (SNDRV_PCM_INFO_MMAP 714ede028fSHans-Christian Egtvedt | SNDRV_PCM_INFO_MMAP_VALID 724ede028fSHans-Christian Egtvedt | SNDRV_PCM_INFO_INTERLEAVED 734ede028fSHans-Christian Egtvedt | SNDRV_PCM_INFO_BLOCK_TRANSFER 744ede028fSHans-Christian Egtvedt | SNDRV_PCM_INFO_JOINT_DUPLEX 754ede028fSHans-Christian Egtvedt | SNDRV_PCM_INFO_RESUME 764ede028fSHans-Christian Egtvedt | SNDRV_PCM_INFO_PAUSE), 774ede028fSHans-Christian Egtvedt .formats = (SNDRV_PCM_FMTBIT_S16_BE 784ede028fSHans-Christian Egtvedt | SNDRV_PCM_FMTBIT_S16_LE), 794ede028fSHans-Christian Egtvedt .rates = (SNDRV_PCM_RATE_CONTINUOUS), 804ede028fSHans-Christian Egtvedt .rate_min = 4000, 814ede028fSHans-Christian Egtvedt .rate_max = 48000, 824ede028fSHans-Christian Egtvedt .channels_min = 1, 834ede028fSHans-Christian Egtvedt .channels_max = 2, 84c42eec0fSHans-Christian Egtvedt .buffer_bytes_max = 2 * 2 * 64 * 2048, 854ede028fSHans-Christian Egtvedt .period_bytes_min = 4096, 864ede028fSHans-Christian Egtvedt .period_bytes_max = 4096, 87c42eec0fSHans-Christian Egtvedt .periods_min = 6, 884ede028fSHans-Christian Egtvedt .periods_max = 64, 894ede028fSHans-Christian Egtvedt }; 904ede028fSHans-Christian Egtvedt 914ede028fSHans-Christian Egtvedt static int atmel_ac97c_playback_open(struct snd_pcm_substream *substream) 924ede028fSHans-Christian Egtvedt { 934ede028fSHans-Christian Egtvedt struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); 944ede028fSHans-Christian Egtvedt struct snd_pcm_runtime *runtime = substream->runtime; 954ede028fSHans-Christian Egtvedt 964ede028fSHans-Christian Egtvedt mutex_lock(&opened_mutex); 974ede028fSHans-Christian Egtvedt chip->opened++; 984ede028fSHans-Christian Egtvedt runtime->hw = atmel_ac97c_hw; 994ede028fSHans-Christian Egtvedt if (chip->cur_rate) { 1004ede028fSHans-Christian Egtvedt runtime->hw.rate_min = chip->cur_rate; 1014ede028fSHans-Christian Egtvedt runtime->hw.rate_max = chip->cur_rate; 1024ede028fSHans-Christian Egtvedt } 1034ede028fSHans-Christian Egtvedt if (chip->cur_format) 10474c34ca1SEldad Zack runtime->hw.formats = pcm_format_to_bits(chip->cur_format); 1054ede028fSHans-Christian Egtvedt mutex_unlock(&opened_mutex); 1064ede028fSHans-Christian Egtvedt chip->playback_substream = substream; 1074ede028fSHans-Christian Egtvedt return 0; 1084ede028fSHans-Christian Egtvedt } 1094ede028fSHans-Christian Egtvedt 1104ede028fSHans-Christian Egtvedt static int atmel_ac97c_capture_open(struct snd_pcm_substream *substream) 1114ede028fSHans-Christian Egtvedt { 1124ede028fSHans-Christian Egtvedt struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); 1134ede028fSHans-Christian Egtvedt struct snd_pcm_runtime *runtime = substream->runtime; 1144ede028fSHans-Christian Egtvedt 1154ede028fSHans-Christian Egtvedt mutex_lock(&opened_mutex); 1164ede028fSHans-Christian Egtvedt chip->opened++; 1174ede028fSHans-Christian Egtvedt runtime->hw = atmel_ac97c_hw; 1184ede028fSHans-Christian Egtvedt if (chip->cur_rate) { 1194ede028fSHans-Christian Egtvedt runtime->hw.rate_min = chip->cur_rate; 1204ede028fSHans-Christian Egtvedt runtime->hw.rate_max = chip->cur_rate; 1214ede028fSHans-Christian Egtvedt } 1224ede028fSHans-Christian Egtvedt if (chip->cur_format) 12374c34ca1SEldad Zack runtime->hw.formats = pcm_format_to_bits(chip->cur_format); 1244ede028fSHans-Christian Egtvedt mutex_unlock(&opened_mutex); 1254ede028fSHans-Christian Egtvedt chip->capture_substream = substream; 1264ede028fSHans-Christian Egtvedt return 0; 1274ede028fSHans-Christian Egtvedt } 1284ede028fSHans-Christian Egtvedt 1294ede028fSHans-Christian Egtvedt static int atmel_ac97c_playback_close(struct snd_pcm_substream *substream) 1304ede028fSHans-Christian Egtvedt { 1314ede028fSHans-Christian Egtvedt struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); 1324ede028fSHans-Christian Egtvedt 1334ede028fSHans-Christian Egtvedt mutex_lock(&opened_mutex); 1344ede028fSHans-Christian Egtvedt chip->opened--; 1354ede028fSHans-Christian Egtvedt if (!chip->opened) { 1364ede028fSHans-Christian Egtvedt chip->cur_rate = 0; 1374ede028fSHans-Christian Egtvedt chip->cur_format = 0; 1384ede028fSHans-Christian Egtvedt } 1394ede028fSHans-Christian Egtvedt mutex_unlock(&opened_mutex); 1404ede028fSHans-Christian Egtvedt 1414ede028fSHans-Christian Egtvedt chip->playback_substream = NULL; 1424ede028fSHans-Christian Egtvedt 1434ede028fSHans-Christian Egtvedt return 0; 1444ede028fSHans-Christian Egtvedt } 1454ede028fSHans-Christian Egtvedt 1464ede028fSHans-Christian Egtvedt static int atmel_ac97c_capture_close(struct snd_pcm_substream *substream) 1474ede028fSHans-Christian Egtvedt { 1484ede028fSHans-Christian Egtvedt struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); 1494ede028fSHans-Christian Egtvedt 1504ede028fSHans-Christian Egtvedt mutex_lock(&opened_mutex); 1514ede028fSHans-Christian Egtvedt chip->opened--; 1524ede028fSHans-Christian Egtvedt if (!chip->opened) { 1534ede028fSHans-Christian Egtvedt chip->cur_rate = 0; 1544ede028fSHans-Christian Egtvedt chip->cur_format = 0; 1554ede028fSHans-Christian Egtvedt } 1564ede028fSHans-Christian Egtvedt mutex_unlock(&opened_mutex); 1574ede028fSHans-Christian Egtvedt 1584ede028fSHans-Christian Egtvedt chip->capture_substream = NULL; 1594ede028fSHans-Christian Egtvedt 1604ede028fSHans-Christian Egtvedt return 0; 1614ede028fSHans-Christian Egtvedt } 1624ede028fSHans-Christian Egtvedt 1634ede028fSHans-Christian Egtvedt static int atmel_ac97c_playback_hw_params(struct snd_pcm_substream *substream, 1644ede028fSHans-Christian Egtvedt struct snd_pcm_hw_params *hw_params) 1654ede028fSHans-Christian Egtvedt { 1664ede028fSHans-Christian Egtvedt struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); 1674ede028fSHans-Christian Egtvedt int retval; 1684ede028fSHans-Christian Egtvedt 1694ede028fSHans-Christian Egtvedt retval = snd_pcm_lib_malloc_pages(substream, 1704ede028fSHans-Christian Egtvedt params_buffer_bytes(hw_params)); 1714ede028fSHans-Christian Egtvedt if (retval < 0) 1724ede028fSHans-Christian Egtvedt return retval; 173020c5260SAndy Shevchenko 1744ede028fSHans-Christian Egtvedt /* Set restrictions to params. */ 1754ede028fSHans-Christian Egtvedt mutex_lock(&opened_mutex); 1764ede028fSHans-Christian Egtvedt chip->cur_rate = params_rate(hw_params); 1774ede028fSHans-Christian Egtvedt chip->cur_format = params_format(hw_params); 1784ede028fSHans-Christian Egtvedt mutex_unlock(&opened_mutex); 1794ede028fSHans-Christian Egtvedt 1804ede028fSHans-Christian Egtvedt return retval; 1814ede028fSHans-Christian Egtvedt } 1824ede028fSHans-Christian Egtvedt 1834ede028fSHans-Christian Egtvedt static int atmel_ac97c_capture_hw_params(struct snd_pcm_substream *substream, 1844ede028fSHans-Christian Egtvedt struct snd_pcm_hw_params *hw_params) 1854ede028fSHans-Christian Egtvedt { 1864ede028fSHans-Christian Egtvedt struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); 1874ede028fSHans-Christian Egtvedt int retval; 1884ede028fSHans-Christian Egtvedt 1894ede028fSHans-Christian Egtvedt retval = snd_pcm_lib_malloc_pages(substream, 1904ede028fSHans-Christian Egtvedt params_buffer_bytes(hw_params)); 1914ede028fSHans-Christian Egtvedt if (retval < 0) 1924ede028fSHans-Christian Egtvedt return retval; 1934ede028fSHans-Christian Egtvedt 1944ede028fSHans-Christian Egtvedt /* Set restrictions to params. */ 1954ede028fSHans-Christian Egtvedt mutex_lock(&opened_mutex); 1964ede028fSHans-Christian Egtvedt chip->cur_rate = params_rate(hw_params); 1974ede028fSHans-Christian Egtvedt chip->cur_format = params_format(hw_params); 1984ede028fSHans-Christian Egtvedt mutex_unlock(&opened_mutex); 1994ede028fSHans-Christian Egtvedt 2004ede028fSHans-Christian Egtvedt return retval; 2014ede028fSHans-Christian Egtvedt } 2024ede028fSHans-Christian Egtvedt 2034ede028fSHans-Christian Egtvedt static int atmel_ac97c_playback_prepare(struct snd_pcm_substream *substream) 2044ede028fSHans-Christian Egtvedt { 2054ede028fSHans-Christian Egtvedt struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); 2064ede028fSHans-Christian Egtvedt struct snd_pcm_runtime *runtime = substream->runtime; 2077177395fSSedji Gaouaou int block_size = frames_to_bytes(runtime, runtime->period_size); 208128ed6a9SHans-Christian Egtvedt unsigned long word = ac97c_readl(chip, OCA); 2094ede028fSHans-Christian Egtvedt int retval; 2104ede028fSHans-Christian Egtvedt 2117177395fSSedji Gaouaou chip->playback_period = 0; 212128ed6a9SHans-Christian Egtvedt word &= ~(AC97C_CH_MASK(PCM_LEFT) | AC97C_CH_MASK(PCM_RIGHT)); 213128ed6a9SHans-Christian Egtvedt 2144ede028fSHans-Christian Egtvedt /* assign channels to AC97C channel A */ 2154ede028fSHans-Christian Egtvedt switch (runtime->channels) { 2164ede028fSHans-Christian Egtvedt case 1: 2174ede028fSHans-Christian Egtvedt word |= AC97C_CH_ASSIGN(PCM_LEFT, A); 2184ede028fSHans-Christian Egtvedt break; 2194ede028fSHans-Christian Egtvedt case 2: 2204ede028fSHans-Christian Egtvedt word |= AC97C_CH_ASSIGN(PCM_LEFT, A) 2214ede028fSHans-Christian Egtvedt | AC97C_CH_ASSIGN(PCM_RIGHT, A); 2224ede028fSHans-Christian Egtvedt break; 2234ede028fSHans-Christian Egtvedt default: 2244ede028fSHans-Christian Egtvedt /* TODO: support more than two channels */ 2254ede028fSHans-Christian Egtvedt return -EINVAL; 2264ede028fSHans-Christian Egtvedt } 2274ede028fSHans-Christian Egtvedt ac97c_writel(chip, OCA, word); 2284ede028fSHans-Christian Egtvedt 2294ede028fSHans-Christian Egtvedt /* configure sample format and size */ 230ec2755a9SSedji Gaouaou word = ac97c_readl(chip, CAMR); 231ec2755a9SSedji Gaouaou if (chip->opened <= 1) 2324ede028fSHans-Christian Egtvedt word = AC97C_CMR_DMAEN | AC97C_CMR_SIZE_16; 233ec2755a9SSedji Gaouaou else 234ec2755a9SSedji Gaouaou word |= AC97C_CMR_DMAEN | AC97C_CMR_SIZE_16; 2354ede028fSHans-Christian Egtvedt 2364ede028fSHans-Christian Egtvedt switch (runtime->format) { 2374ede028fSHans-Christian Egtvedt case SNDRV_PCM_FORMAT_S16_LE: 2384ede028fSHans-Christian Egtvedt break; 2394ede028fSHans-Christian Egtvedt case SNDRV_PCM_FORMAT_S16_BE: /* fall through */ 2404ede028fSHans-Christian Egtvedt word &= ~(AC97C_CMR_CEM_LITTLE); 2414ede028fSHans-Christian Egtvedt break; 242128ed6a9SHans-Christian Egtvedt default: 243128ed6a9SHans-Christian Egtvedt word = ac97c_readl(chip, OCA); 244128ed6a9SHans-Christian Egtvedt word &= ~(AC97C_CH_MASK(PCM_LEFT) | AC97C_CH_MASK(PCM_RIGHT)); 245128ed6a9SHans-Christian Egtvedt ac97c_writel(chip, OCA, word); 246128ed6a9SHans-Christian Egtvedt return -EINVAL; 2474ede028fSHans-Christian Egtvedt } 2484ede028fSHans-Christian Egtvedt 249df163587SHans-Christian Egtvedt /* Enable underrun interrupt on channel A */ 250df163587SHans-Christian Egtvedt word |= AC97C_CSR_UNRUN; 251df163587SHans-Christian Egtvedt 2524ede028fSHans-Christian Egtvedt ac97c_writel(chip, CAMR, word); 2534ede028fSHans-Christian Egtvedt 254df163587SHans-Christian Egtvedt /* Enable channel A event interrupt */ 255df163587SHans-Christian Egtvedt word = ac97c_readl(chip, IMR); 256df163587SHans-Christian Egtvedt word |= AC97C_SR_CAEVT; 257df163587SHans-Christian Egtvedt ac97c_writel(chip, IER, word); 258df163587SHans-Christian Egtvedt 2594ede028fSHans-Christian Egtvedt /* set variable rate if needed */ 2604ede028fSHans-Christian Egtvedt if (runtime->rate != 48000) { 2614ede028fSHans-Christian Egtvedt word = ac97c_readl(chip, MR); 2624ede028fSHans-Christian Egtvedt word |= AC97C_MR_VRA; 2634ede028fSHans-Christian Egtvedt ac97c_writel(chip, MR, word); 2644ede028fSHans-Christian Egtvedt } else { 2654ede028fSHans-Christian Egtvedt word = ac97c_readl(chip, MR); 2664ede028fSHans-Christian Egtvedt word &= ~(AC97C_MR_VRA); 2674ede028fSHans-Christian Egtvedt ac97c_writel(chip, MR, word); 2684ede028fSHans-Christian Egtvedt } 2694ede028fSHans-Christian Egtvedt 2704ede028fSHans-Christian Egtvedt retval = snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, 2714ede028fSHans-Christian Egtvedt runtime->rate); 2724ede028fSHans-Christian Egtvedt if (retval) 2734ede028fSHans-Christian Egtvedt dev_dbg(&chip->pdev->dev, "could not set rate %d Hz\n", 2744ede028fSHans-Christian Egtvedt runtime->rate); 2754ede028fSHans-Christian Egtvedt 2767177395fSSedji Gaouaou /* Initialize and start the PDC */ 2777177395fSSedji Gaouaou writel(runtime->dma_addr, chip->regs + ATMEL_PDC_TPR); 2787177395fSSedji Gaouaou writel(block_size / 2, chip->regs + ATMEL_PDC_TCR); 279020c5260SAndy Shevchenko writel(runtime->dma_addr + block_size, chip->regs + ATMEL_PDC_TNPR); 2807177395fSSedji Gaouaou writel(block_size / 2, chip->regs + ATMEL_PDC_TNCR); 2814ede028fSHans-Christian Egtvedt 2824ede028fSHans-Christian Egtvedt return retval; 2834ede028fSHans-Christian Egtvedt } 2844ede028fSHans-Christian Egtvedt 2854ede028fSHans-Christian Egtvedt static int atmel_ac97c_capture_prepare(struct snd_pcm_substream *substream) 2864ede028fSHans-Christian Egtvedt { 2874ede028fSHans-Christian Egtvedt struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); 2884ede028fSHans-Christian Egtvedt struct snd_pcm_runtime *runtime = substream->runtime; 2897177395fSSedji Gaouaou int block_size = frames_to_bytes(runtime, runtime->period_size); 290128ed6a9SHans-Christian Egtvedt unsigned long word = ac97c_readl(chip, ICA); 2914ede028fSHans-Christian Egtvedt int retval; 2924ede028fSHans-Christian Egtvedt 2937177395fSSedji Gaouaou chip->capture_period = 0; 294128ed6a9SHans-Christian Egtvedt word &= ~(AC97C_CH_MASK(PCM_LEFT) | AC97C_CH_MASK(PCM_RIGHT)); 295128ed6a9SHans-Christian Egtvedt 2964ede028fSHans-Christian Egtvedt /* assign channels to AC97C channel A */ 2974ede028fSHans-Christian Egtvedt switch (runtime->channels) { 2984ede028fSHans-Christian Egtvedt case 1: 2994ede028fSHans-Christian Egtvedt word |= AC97C_CH_ASSIGN(PCM_LEFT, A); 3004ede028fSHans-Christian Egtvedt break; 3014ede028fSHans-Christian Egtvedt case 2: 3024ede028fSHans-Christian Egtvedt word |= AC97C_CH_ASSIGN(PCM_LEFT, A) 3034ede028fSHans-Christian Egtvedt | AC97C_CH_ASSIGN(PCM_RIGHT, A); 3044ede028fSHans-Christian Egtvedt break; 3054ede028fSHans-Christian Egtvedt default: 3064ede028fSHans-Christian Egtvedt /* TODO: support more than two channels */ 3074ede028fSHans-Christian Egtvedt return -EINVAL; 3084ede028fSHans-Christian Egtvedt } 3094ede028fSHans-Christian Egtvedt ac97c_writel(chip, ICA, word); 3104ede028fSHans-Christian Egtvedt 3114ede028fSHans-Christian Egtvedt /* configure sample format and size */ 312ec2755a9SSedji Gaouaou word = ac97c_readl(chip, CAMR); 313ec2755a9SSedji Gaouaou if (chip->opened <= 1) 3144ede028fSHans-Christian Egtvedt word = AC97C_CMR_DMAEN | AC97C_CMR_SIZE_16; 315ec2755a9SSedji Gaouaou else 316ec2755a9SSedji Gaouaou word |= AC97C_CMR_DMAEN | AC97C_CMR_SIZE_16; 3174ede028fSHans-Christian Egtvedt 3184ede028fSHans-Christian Egtvedt switch (runtime->format) { 3194ede028fSHans-Christian Egtvedt case SNDRV_PCM_FORMAT_S16_LE: 3204ede028fSHans-Christian Egtvedt break; 3214ede028fSHans-Christian Egtvedt case SNDRV_PCM_FORMAT_S16_BE: /* fall through */ 3224ede028fSHans-Christian Egtvedt word &= ~(AC97C_CMR_CEM_LITTLE); 3234ede028fSHans-Christian Egtvedt break; 324128ed6a9SHans-Christian Egtvedt default: 325128ed6a9SHans-Christian Egtvedt word = ac97c_readl(chip, ICA); 326128ed6a9SHans-Christian Egtvedt word &= ~(AC97C_CH_MASK(PCM_LEFT) | AC97C_CH_MASK(PCM_RIGHT)); 327128ed6a9SHans-Christian Egtvedt ac97c_writel(chip, ICA, word); 328128ed6a9SHans-Christian Egtvedt return -EINVAL; 3294ede028fSHans-Christian Egtvedt } 3304ede028fSHans-Christian Egtvedt 331df163587SHans-Christian Egtvedt /* Enable overrun interrupt on channel A */ 332df163587SHans-Christian Egtvedt word |= AC97C_CSR_OVRUN; 333df163587SHans-Christian Egtvedt 3344ede028fSHans-Christian Egtvedt ac97c_writel(chip, CAMR, word); 3354ede028fSHans-Christian Egtvedt 336df163587SHans-Christian Egtvedt /* Enable channel A event interrupt */ 337df163587SHans-Christian Egtvedt word = ac97c_readl(chip, IMR); 338df163587SHans-Christian Egtvedt word |= AC97C_SR_CAEVT; 339df163587SHans-Christian Egtvedt ac97c_writel(chip, IER, word); 340df163587SHans-Christian Egtvedt 3414ede028fSHans-Christian Egtvedt /* set variable rate if needed */ 3424ede028fSHans-Christian Egtvedt if (runtime->rate != 48000) { 3434ede028fSHans-Christian Egtvedt word = ac97c_readl(chip, MR); 3444ede028fSHans-Christian Egtvedt word |= AC97C_MR_VRA; 3454ede028fSHans-Christian Egtvedt ac97c_writel(chip, MR, word); 3464ede028fSHans-Christian Egtvedt } else { 3474ede028fSHans-Christian Egtvedt word = ac97c_readl(chip, MR); 3484ede028fSHans-Christian Egtvedt word &= ~(AC97C_MR_VRA); 3494ede028fSHans-Christian Egtvedt ac97c_writel(chip, MR, word); 3504ede028fSHans-Christian Egtvedt } 3514ede028fSHans-Christian Egtvedt 3524ede028fSHans-Christian Egtvedt retval = snd_ac97_set_rate(chip->ac97, AC97_PCM_LR_ADC_RATE, 3534ede028fSHans-Christian Egtvedt runtime->rate); 3544ede028fSHans-Christian Egtvedt if (retval) 3554ede028fSHans-Christian Egtvedt dev_dbg(&chip->pdev->dev, "could not set rate %d Hz\n", 3564ede028fSHans-Christian Egtvedt runtime->rate); 3574ede028fSHans-Christian Egtvedt 3587177395fSSedji Gaouaou /* Initialize and start the PDC */ 3597177395fSSedji Gaouaou writel(runtime->dma_addr, chip->regs + ATMEL_PDC_RPR); 3607177395fSSedji Gaouaou writel(block_size / 2, chip->regs + ATMEL_PDC_RCR); 361020c5260SAndy Shevchenko writel(runtime->dma_addr + block_size, chip->regs + ATMEL_PDC_RNPR); 3627177395fSSedji Gaouaou writel(block_size / 2, chip->regs + ATMEL_PDC_RNCR); 3634ede028fSHans-Christian Egtvedt 3644ede028fSHans-Christian Egtvedt return retval; 3654ede028fSHans-Christian Egtvedt } 3664ede028fSHans-Christian Egtvedt 3674ede028fSHans-Christian Egtvedt static int 3684ede028fSHans-Christian Egtvedt atmel_ac97c_playback_trigger(struct snd_pcm_substream *substream, int cmd) 3694ede028fSHans-Christian Egtvedt { 3704ede028fSHans-Christian Egtvedt struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); 3717177395fSSedji Gaouaou unsigned long camr, ptcr = 0; 3724ede028fSHans-Christian Egtvedt 3734ede028fSHans-Christian Egtvedt camr = ac97c_readl(chip, CAMR); 3744ede028fSHans-Christian Egtvedt 3754ede028fSHans-Christian Egtvedt switch (cmd) { 3764ede028fSHans-Christian Egtvedt case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */ 3774ede028fSHans-Christian Egtvedt case SNDRV_PCM_TRIGGER_RESUME: /* fall through */ 3784ede028fSHans-Christian Egtvedt case SNDRV_PCM_TRIGGER_START: 3797177395fSSedji Gaouaou ptcr = ATMEL_PDC_TXTEN; 380ec2755a9SSedji Gaouaou camr |= AC97C_CMR_CENA | AC97C_CSR_ENDTX; 3814ede028fSHans-Christian Egtvedt break; 3824ede028fSHans-Christian Egtvedt case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */ 3834ede028fSHans-Christian Egtvedt case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */ 3844ede028fSHans-Christian Egtvedt case SNDRV_PCM_TRIGGER_STOP: 3857177395fSSedji Gaouaou ptcr |= ATMEL_PDC_TXTDIS; 3864ede028fSHans-Christian Egtvedt if (chip->opened <= 1) 3874ede028fSHans-Christian Egtvedt camr &= ~AC97C_CMR_CENA; 3884ede028fSHans-Christian Egtvedt break; 3894ede028fSHans-Christian Egtvedt default: 390020c5260SAndy Shevchenko return -EINVAL; 3914ede028fSHans-Christian Egtvedt } 3924ede028fSHans-Christian Egtvedt 3934ede028fSHans-Christian Egtvedt ac97c_writel(chip, CAMR, camr); 3947177395fSSedji Gaouaou writel(ptcr, chip->regs + ATMEL_PDC_PTCR); 395020c5260SAndy Shevchenko return 0; 3964ede028fSHans-Christian Egtvedt } 3974ede028fSHans-Christian Egtvedt 3984ede028fSHans-Christian Egtvedt static int 3994ede028fSHans-Christian Egtvedt atmel_ac97c_capture_trigger(struct snd_pcm_substream *substream, int cmd) 4004ede028fSHans-Christian Egtvedt { 4014ede028fSHans-Christian Egtvedt struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); 4027177395fSSedji Gaouaou unsigned long camr, ptcr = 0; 4034ede028fSHans-Christian Egtvedt 4044ede028fSHans-Christian Egtvedt camr = ac97c_readl(chip, CAMR); 4057177395fSSedji Gaouaou ptcr = readl(chip->regs + ATMEL_PDC_PTSR); 4064ede028fSHans-Christian Egtvedt 4074ede028fSHans-Christian Egtvedt switch (cmd) { 4084ede028fSHans-Christian Egtvedt case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */ 4094ede028fSHans-Christian Egtvedt case SNDRV_PCM_TRIGGER_RESUME: /* fall through */ 4104ede028fSHans-Christian Egtvedt case SNDRV_PCM_TRIGGER_START: 4117177395fSSedji Gaouaou ptcr = ATMEL_PDC_RXTEN; 412ec2755a9SSedji Gaouaou camr |= AC97C_CMR_CENA | AC97C_CSR_ENDRX; 4134ede028fSHans-Christian Egtvedt break; 4144ede028fSHans-Christian Egtvedt case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */ 4154ede028fSHans-Christian Egtvedt case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */ 4164ede028fSHans-Christian Egtvedt case SNDRV_PCM_TRIGGER_STOP: 417020c5260SAndy Shevchenko ptcr |= ATMEL_PDC_RXTDIS; 4184ede028fSHans-Christian Egtvedt if (chip->opened <= 1) 4194ede028fSHans-Christian Egtvedt camr &= ~AC97C_CMR_CENA; 4204ede028fSHans-Christian Egtvedt break; 4214ede028fSHans-Christian Egtvedt default: 422020c5260SAndy Shevchenko return -EINVAL; 4234ede028fSHans-Christian Egtvedt } 4244ede028fSHans-Christian Egtvedt 4254ede028fSHans-Christian Egtvedt ac97c_writel(chip, CAMR, camr); 4267177395fSSedji Gaouaou writel(ptcr, chip->regs + ATMEL_PDC_PTCR); 427020c5260SAndy Shevchenko return 0; 4284ede028fSHans-Christian Egtvedt } 4294ede028fSHans-Christian Egtvedt 4304ede028fSHans-Christian Egtvedt static snd_pcm_uframes_t 4314ede028fSHans-Christian Egtvedt atmel_ac97c_playback_pointer(struct snd_pcm_substream *substream) 4324ede028fSHans-Christian Egtvedt { 4334ede028fSHans-Christian Egtvedt struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); 4344ede028fSHans-Christian Egtvedt struct snd_pcm_runtime *runtime = substream->runtime; 4354ede028fSHans-Christian Egtvedt snd_pcm_uframes_t frames; 4364ede028fSHans-Christian Egtvedt unsigned long bytes; 4374ede028fSHans-Christian Egtvedt 4387177395fSSedji Gaouaou bytes = readl(chip->regs + ATMEL_PDC_TPR); 4394ede028fSHans-Christian Egtvedt bytes -= runtime->dma_addr; 4404ede028fSHans-Christian Egtvedt 4414ede028fSHans-Christian Egtvedt frames = bytes_to_frames(runtime, bytes); 4424ede028fSHans-Christian Egtvedt if (frames >= runtime->buffer_size) 4434ede028fSHans-Christian Egtvedt frames -= runtime->buffer_size; 4444ede028fSHans-Christian Egtvedt return frames; 4454ede028fSHans-Christian Egtvedt } 4464ede028fSHans-Christian Egtvedt 4474ede028fSHans-Christian Egtvedt static snd_pcm_uframes_t 4484ede028fSHans-Christian Egtvedt atmel_ac97c_capture_pointer(struct snd_pcm_substream *substream) 4494ede028fSHans-Christian Egtvedt { 4504ede028fSHans-Christian Egtvedt struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); 4514ede028fSHans-Christian Egtvedt struct snd_pcm_runtime *runtime = substream->runtime; 4524ede028fSHans-Christian Egtvedt snd_pcm_uframes_t frames; 4534ede028fSHans-Christian Egtvedt unsigned long bytes; 4544ede028fSHans-Christian Egtvedt 4557177395fSSedji Gaouaou bytes = readl(chip->regs + ATMEL_PDC_RPR); 4564ede028fSHans-Christian Egtvedt bytes -= runtime->dma_addr; 4574ede028fSHans-Christian Egtvedt 4584ede028fSHans-Christian Egtvedt frames = bytes_to_frames(runtime, bytes); 4594ede028fSHans-Christian Egtvedt if (frames >= runtime->buffer_size) 4604ede028fSHans-Christian Egtvedt frames -= runtime->buffer_size; 4614ede028fSHans-Christian Egtvedt return frames; 4624ede028fSHans-Christian Egtvedt } 4634ede028fSHans-Christian Egtvedt 46410863737SArvind Yadav static const struct snd_pcm_ops atmel_ac97_playback_ops = { 4654ede028fSHans-Christian Egtvedt .open = atmel_ac97c_playback_open, 4664ede028fSHans-Christian Egtvedt .close = atmel_ac97c_playback_close, 4674ede028fSHans-Christian Egtvedt .ioctl = snd_pcm_lib_ioctl, 4684ede028fSHans-Christian Egtvedt .hw_params = atmel_ac97c_playback_hw_params, 469020c5260SAndy Shevchenko .hw_free = snd_pcm_lib_free_pages, 4704ede028fSHans-Christian Egtvedt .prepare = atmel_ac97c_playback_prepare, 4714ede028fSHans-Christian Egtvedt .trigger = atmel_ac97c_playback_trigger, 4724ede028fSHans-Christian Egtvedt .pointer = atmel_ac97c_playback_pointer, 4734ede028fSHans-Christian Egtvedt }; 4744ede028fSHans-Christian Egtvedt 47510863737SArvind Yadav static const struct snd_pcm_ops atmel_ac97_capture_ops = { 4764ede028fSHans-Christian Egtvedt .open = atmel_ac97c_capture_open, 4774ede028fSHans-Christian Egtvedt .close = atmel_ac97c_capture_close, 4784ede028fSHans-Christian Egtvedt .ioctl = snd_pcm_lib_ioctl, 4794ede028fSHans-Christian Egtvedt .hw_params = atmel_ac97c_capture_hw_params, 480020c5260SAndy Shevchenko .hw_free = snd_pcm_lib_free_pages, 4814ede028fSHans-Christian Egtvedt .prepare = atmel_ac97c_capture_prepare, 4824ede028fSHans-Christian Egtvedt .trigger = atmel_ac97c_capture_trigger, 4834ede028fSHans-Christian Egtvedt .pointer = atmel_ac97c_capture_pointer, 4844ede028fSHans-Christian Egtvedt }; 4854ede028fSHans-Christian Egtvedt 486df163587SHans-Christian Egtvedt static irqreturn_t atmel_ac97c_interrupt(int irq, void *dev) 487df163587SHans-Christian Egtvedt { 488df163587SHans-Christian Egtvedt struct atmel_ac97c *chip = (struct atmel_ac97c *)dev; 489df163587SHans-Christian Egtvedt irqreturn_t retval = IRQ_NONE; 490df163587SHans-Christian Egtvedt u32 sr = ac97c_readl(chip, SR); 491df163587SHans-Christian Egtvedt u32 casr = ac97c_readl(chip, CASR); 492df163587SHans-Christian Egtvedt u32 cosr = ac97c_readl(chip, COSR); 4937177395fSSedji Gaouaou u32 camr = ac97c_readl(chip, CAMR); 494df163587SHans-Christian Egtvedt 495df163587SHans-Christian Egtvedt if (sr & AC97C_SR_CAEVT) { 4967177395fSSedji Gaouaou struct snd_pcm_runtime *runtime; 4977177395fSSedji Gaouaou int offset, next_period, block_size; 498f5341163SYegor Yefremov dev_dbg(&chip->pdev->dev, "channel A event%s%s%s%s%s%s\n", 499df163587SHans-Christian Egtvedt casr & AC97C_CSR_OVRUN ? " OVRUN" : "", 500df163587SHans-Christian Egtvedt casr & AC97C_CSR_RXRDY ? " RXRDY" : "", 501df163587SHans-Christian Egtvedt casr & AC97C_CSR_UNRUN ? " UNRUN" : "", 502df163587SHans-Christian Egtvedt casr & AC97C_CSR_TXEMPTY ? " TXEMPTY" : "", 503df163587SHans-Christian Egtvedt casr & AC97C_CSR_TXRDY ? " TXRDY" : "", 504df163587SHans-Christian Egtvedt !casr ? " NONE" : ""); 5057177395fSSedji Gaouaou if ((casr & camr) & AC97C_CSR_ENDTX) { 5067177395fSSedji Gaouaou runtime = chip->playback_substream->runtime; 507020c5260SAndy Shevchenko block_size = frames_to_bytes(runtime, runtime->period_size); 5087177395fSSedji Gaouaou chip->playback_period++; 5097177395fSSedji Gaouaou 5107177395fSSedji Gaouaou if (chip->playback_period == runtime->periods) 5117177395fSSedji Gaouaou chip->playback_period = 0; 5127177395fSSedji Gaouaou next_period = chip->playback_period + 1; 5137177395fSSedji Gaouaou if (next_period == runtime->periods) 5147177395fSSedji Gaouaou next_period = 0; 5157177395fSSedji Gaouaou 5167177395fSSedji Gaouaou offset = block_size * next_period; 5177177395fSSedji Gaouaou 518020c5260SAndy Shevchenko writel(runtime->dma_addr + offset, chip->regs + ATMEL_PDC_TNPR); 519020c5260SAndy Shevchenko writel(block_size / 2, chip->regs + ATMEL_PDC_TNCR); 5207177395fSSedji Gaouaou 521020c5260SAndy Shevchenko snd_pcm_period_elapsed(chip->playback_substream); 5227177395fSSedji Gaouaou } 5237177395fSSedji Gaouaou if ((casr & camr) & AC97C_CSR_ENDRX) { 5247177395fSSedji Gaouaou runtime = chip->capture_substream->runtime; 525020c5260SAndy Shevchenko block_size = frames_to_bytes(runtime, runtime->period_size); 5267177395fSSedji Gaouaou chip->capture_period++; 5277177395fSSedji Gaouaou 5287177395fSSedji Gaouaou if (chip->capture_period == runtime->periods) 5297177395fSSedji Gaouaou chip->capture_period = 0; 5307177395fSSedji Gaouaou next_period = chip->capture_period + 1; 5317177395fSSedji Gaouaou if (next_period == runtime->periods) 5327177395fSSedji Gaouaou next_period = 0; 5337177395fSSedji Gaouaou 5347177395fSSedji Gaouaou offset = block_size * next_period; 5357177395fSSedji Gaouaou 536020c5260SAndy Shevchenko writel(runtime->dma_addr + offset, chip->regs + ATMEL_PDC_RNPR); 537020c5260SAndy Shevchenko writel(block_size / 2, chip->regs + ATMEL_PDC_RNCR); 5387177395fSSedji Gaouaou snd_pcm_period_elapsed(chip->capture_substream); 5397177395fSSedji Gaouaou } 540df163587SHans-Christian Egtvedt retval = IRQ_HANDLED; 541df163587SHans-Christian Egtvedt } 542df163587SHans-Christian Egtvedt 543df163587SHans-Christian Egtvedt if (sr & AC97C_SR_COEVT) { 544df163587SHans-Christian Egtvedt dev_info(&chip->pdev->dev, "codec channel event%s%s%s%s%s\n", 545df163587SHans-Christian Egtvedt cosr & AC97C_CSR_OVRUN ? " OVRUN" : "", 546df163587SHans-Christian Egtvedt cosr & AC97C_CSR_RXRDY ? " RXRDY" : "", 547df163587SHans-Christian Egtvedt cosr & AC97C_CSR_TXEMPTY ? " TXEMPTY" : "", 548df163587SHans-Christian Egtvedt cosr & AC97C_CSR_TXRDY ? " TXRDY" : "", 549df163587SHans-Christian Egtvedt !cosr ? " NONE" : ""); 550df163587SHans-Christian Egtvedt retval = IRQ_HANDLED; 551df163587SHans-Christian Egtvedt } 552df163587SHans-Christian Egtvedt 553df163587SHans-Christian Egtvedt if (retval == IRQ_NONE) { 554df163587SHans-Christian Egtvedt dev_err(&chip->pdev->dev, "spurious interrupt sr 0x%08x " 555df163587SHans-Christian Egtvedt "casr 0x%08x cosr 0x%08x\n", sr, casr, cosr); 556df163587SHans-Christian Egtvedt } 557df163587SHans-Christian Egtvedt 558df163587SHans-Christian Egtvedt return retval; 559df163587SHans-Christian Egtvedt } 560df163587SHans-Christian Egtvedt 56161dc674cSBill Pemberton static struct ac97_pcm at91_ac97_pcm_defs[] = { 5627177395fSSedji Gaouaou /* Playback */ 5637177395fSSedji Gaouaou { 5647177395fSSedji Gaouaou .exclusive = 1, 5657177395fSSedji Gaouaou .r = { { 5667177395fSSedji Gaouaou .slots = ((1 << AC97_SLOT_PCM_LEFT) 5677177395fSSedji Gaouaou | (1 << AC97_SLOT_PCM_RIGHT)), 5687177395fSSedji Gaouaou } }, 5697177395fSSedji Gaouaou }, 5707177395fSSedji Gaouaou /* PCM in */ 5717177395fSSedji Gaouaou { 5727177395fSSedji Gaouaou .stream = 1, 5737177395fSSedji Gaouaou .exclusive = 1, 5747177395fSSedji Gaouaou .r = { { 5757177395fSSedji Gaouaou .slots = ((1 << AC97_SLOT_PCM_LEFT) 5767177395fSSedji Gaouaou | (1 << AC97_SLOT_PCM_RIGHT)), 5777177395fSSedji Gaouaou } } 5787177395fSSedji Gaouaou }, 5797177395fSSedji Gaouaou /* Mic in */ 5807177395fSSedji Gaouaou { 5817177395fSSedji Gaouaou .stream = 1, 5827177395fSSedji Gaouaou .exclusive = 1, 5837177395fSSedji Gaouaou .r = { { 5847177395fSSedji Gaouaou .slots = (1<<AC97_SLOT_MIC), 5857177395fSSedji Gaouaou } } 5867177395fSSedji Gaouaou }, 5877177395fSSedji Gaouaou }; 5887177395fSSedji Gaouaou 58961dc674cSBill Pemberton static int atmel_ac97c_pcm_new(struct atmel_ac97c *chip) 5904ede028fSHans-Christian Egtvedt { 5914ede028fSHans-Christian Egtvedt struct snd_pcm *pcm; 5924ede028fSHans-Christian Egtvedt struct snd_pcm_hardware hw = atmel_ac97c_hw; 593020c5260SAndy Shevchenko int retval; 5944ede028fSHans-Christian Egtvedt 595020c5260SAndy Shevchenko retval = snd_ac97_pcm_assign(chip->ac97_bus, 5967177395fSSedji Gaouaou ARRAY_SIZE(at91_ac97_pcm_defs), 5977177395fSSedji Gaouaou at91_ac97_pcm_defs); 5984ede028fSHans-Christian Egtvedt if (retval) 5994ede028fSHans-Christian Egtvedt return retval; 6004ede028fSHans-Christian Egtvedt 601020c5260SAndy Shevchenko retval = snd_pcm_new(chip->card, chip->card->shortname, 0, 1, 1, &pcm); 602020c5260SAndy Shevchenko if (retval) 603020c5260SAndy Shevchenko return retval; 604020c5260SAndy Shevchenko 605020c5260SAndy Shevchenko snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &atmel_ac97_capture_ops); 606020c5260SAndy Shevchenko snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &atmel_ac97_playback_ops); 6074ede028fSHans-Christian Egtvedt 6084ede028fSHans-Christian Egtvedt retval = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, 6094ede028fSHans-Christian Egtvedt &chip->pdev->dev, hw.periods_min * hw.period_bytes_min, 6104ede028fSHans-Christian Egtvedt hw.buffer_bytes_max); 6114ede028fSHans-Christian Egtvedt if (retval) 6124ede028fSHans-Christian Egtvedt return retval; 6134ede028fSHans-Christian Egtvedt 6144ede028fSHans-Christian Egtvedt pcm->private_data = chip; 6154ede028fSHans-Christian Egtvedt pcm->info_flags = 0; 6164ede028fSHans-Christian Egtvedt strcpy(pcm->name, chip->card->shortname); 6174ede028fSHans-Christian Egtvedt chip->pcm = pcm; 6184ede028fSHans-Christian Egtvedt 6194ede028fSHans-Christian Egtvedt return 0; 6204ede028fSHans-Christian Egtvedt } 6214ede028fSHans-Christian Egtvedt 6224ede028fSHans-Christian Egtvedt static int atmel_ac97c_mixer_new(struct atmel_ac97c *chip) 6234ede028fSHans-Christian Egtvedt { 6244ede028fSHans-Christian Egtvedt struct snd_ac97_template template; 6254ede028fSHans-Christian Egtvedt memset(&template, 0, sizeof(template)); 6264ede028fSHans-Christian Egtvedt template.private_data = chip; 6274ede028fSHans-Christian Egtvedt return snd_ac97_mixer(chip->ac97_bus, &template, &chip->ac97); 6284ede028fSHans-Christian Egtvedt } 6294ede028fSHans-Christian Egtvedt 6304ede028fSHans-Christian Egtvedt static void atmel_ac97c_write(struct snd_ac97 *ac97, unsigned short reg, 6314ede028fSHans-Christian Egtvedt unsigned short val) 6324ede028fSHans-Christian Egtvedt { 6334ede028fSHans-Christian Egtvedt struct atmel_ac97c *chip = get_chip(ac97); 6344ede028fSHans-Christian Egtvedt unsigned long word; 6354ede028fSHans-Christian Egtvedt int timeout = 40; 6364ede028fSHans-Christian Egtvedt 6374ede028fSHans-Christian Egtvedt word = (reg & 0x7f) << 16 | val; 6384ede028fSHans-Christian Egtvedt 6394ede028fSHans-Christian Egtvedt do { 6404ede028fSHans-Christian Egtvedt if (ac97c_readl(chip, COSR) & AC97C_CSR_TXRDY) { 6414ede028fSHans-Christian Egtvedt ac97c_writel(chip, COTHR, word); 6424ede028fSHans-Christian Egtvedt return; 6434ede028fSHans-Christian Egtvedt } 6444ede028fSHans-Christian Egtvedt udelay(1); 6454ede028fSHans-Christian Egtvedt } while (--timeout); 6464ede028fSHans-Christian Egtvedt 6474ede028fSHans-Christian Egtvedt dev_dbg(&chip->pdev->dev, "codec write timeout\n"); 6484ede028fSHans-Christian Egtvedt } 6494ede028fSHans-Christian Egtvedt 6504ede028fSHans-Christian Egtvedt static unsigned short atmel_ac97c_read(struct snd_ac97 *ac97, 6514ede028fSHans-Christian Egtvedt unsigned short reg) 6524ede028fSHans-Christian Egtvedt { 6534ede028fSHans-Christian Egtvedt struct atmel_ac97c *chip = get_chip(ac97); 6544ede028fSHans-Christian Egtvedt unsigned long word; 6554ede028fSHans-Christian Egtvedt int timeout = 40; 6564ede028fSHans-Christian Egtvedt int write = 10; 6574ede028fSHans-Christian Egtvedt 6584ede028fSHans-Christian Egtvedt word = (0x80 | (reg & 0x7f)) << 16; 6594ede028fSHans-Christian Egtvedt 6604ede028fSHans-Christian Egtvedt if ((ac97c_readl(chip, COSR) & AC97C_CSR_RXRDY) != 0) 6614ede028fSHans-Christian Egtvedt ac97c_readl(chip, CORHR); 6624ede028fSHans-Christian Egtvedt 6634ede028fSHans-Christian Egtvedt retry_write: 6644ede028fSHans-Christian Egtvedt timeout = 40; 6654ede028fSHans-Christian Egtvedt 6664ede028fSHans-Christian Egtvedt do { 6674ede028fSHans-Christian Egtvedt if ((ac97c_readl(chip, COSR) & AC97C_CSR_TXRDY) != 0) { 6684ede028fSHans-Christian Egtvedt ac97c_writel(chip, COTHR, word); 6694ede028fSHans-Christian Egtvedt goto read_reg; 6704ede028fSHans-Christian Egtvedt } 6714ede028fSHans-Christian Egtvedt udelay(10); 6724ede028fSHans-Christian Egtvedt } while (--timeout); 6734ede028fSHans-Christian Egtvedt 6744ede028fSHans-Christian Egtvedt if (!--write) 6754ede028fSHans-Christian Egtvedt goto timed_out; 6764ede028fSHans-Christian Egtvedt goto retry_write; 6774ede028fSHans-Christian Egtvedt 6784ede028fSHans-Christian Egtvedt read_reg: 6794ede028fSHans-Christian Egtvedt do { 6804ede028fSHans-Christian Egtvedt if ((ac97c_readl(chip, COSR) & AC97C_CSR_RXRDY) != 0) { 6814ede028fSHans-Christian Egtvedt unsigned short val = ac97c_readl(chip, CORHR); 6824ede028fSHans-Christian Egtvedt return val; 6834ede028fSHans-Christian Egtvedt } 6844ede028fSHans-Christian Egtvedt udelay(10); 6854ede028fSHans-Christian Egtvedt } while (--timeout); 6864ede028fSHans-Christian Egtvedt 6874ede028fSHans-Christian Egtvedt if (!--write) 6884ede028fSHans-Christian Egtvedt goto timed_out; 6894ede028fSHans-Christian Egtvedt goto retry_write; 6904ede028fSHans-Christian Egtvedt 6914ede028fSHans-Christian Egtvedt timed_out: 6924ede028fSHans-Christian Egtvedt dev_dbg(&chip->pdev->dev, "codec read timeout\n"); 6934ede028fSHans-Christian Egtvedt return 0xffff; 6944ede028fSHans-Christian Egtvedt } 6954ede028fSHans-Christian Egtvedt 6964ede028fSHans-Christian Egtvedt static void atmel_ac97c_reset(struct atmel_ac97c *chip) 6974ede028fSHans-Christian Egtvedt { 69881baf3a7SHans-Christian Egtvedt ac97c_writel(chip, MR, 0); 69981baf3a7SHans-Christian Egtvedt ac97c_writel(chip, MR, AC97C_MR_ENA); 70081baf3a7SHans-Christian Egtvedt ac97c_writel(chip, CAMR, 0); 70181baf3a7SHans-Christian Egtvedt ac97c_writel(chip, COMR, 0); 7024ede028fSHans-Christian Egtvedt 7034ede028fSHans-Christian Egtvedt if (gpio_is_valid(chip->reset_pin)) { 7044ede028fSHans-Christian Egtvedt gpio_set_value(chip->reset_pin, 0); 7054ede028fSHans-Christian Egtvedt /* AC97 v2.2 specifications says minimum 1 us. */ 70681baf3a7SHans-Christian Egtvedt udelay(2); 7074ede028fSHans-Christian Egtvedt gpio_set_value(chip->reset_pin, 1); 7088015e3deSBo Shen } else { 7098015e3deSBo Shen ac97c_writel(chip, MR, AC97C_MR_WRST | AC97C_MR_ENA); 7108015e3deSBo Shen udelay(2); 7118015e3deSBo Shen ac97c_writel(chip, MR, AC97C_MR_ENA); 7124ede028fSHans-Christian Egtvedt } 7134ede028fSHans-Christian Egtvedt } 7144ede028fSHans-Christian Egtvedt 715b2d8957fSAlexander Stein #ifdef CONFIG_OF 716b2d8957fSAlexander Stein static const struct of_device_id atmel_ac97c_dt_ids[] = { 717b2d8957fSAlexander Stein { .compatible = "atmel,at91sam9263-ac97c", }, 718b2d8957fSAlexander Stein { } 719b2d8957fSAlexander Stein }; 720b2d8957fSAlexander Stein MODULE_DEVICE_TABLE(of, atmel_ac97c_dt_ids); 721b2d8957fSAlexander Stein 722b2d8957fSAlexander Stein static struct ac97c_platform_data *atmel_ac97c_probe_dt(struct device *dev) 723b2d8957fSAlexander Stein { 724b2d8957fSAlexander Stein struct ac97c_platform_data *pdata; 725b2d8957fSAlexander Stein struct device_node *node = dev->of_node; 726b2d8957fSAlexander Stein 727b2d8957fSAlexander Stein if (!node) { 728b2d8957fSAlexander Stein dev_err(dev, "Device does not have associated DT data\n"); 729b2d8957fSAlexander Stein return ERR_PTR(-EINVAL); 730b2d8957fSAlexander Stein } 731b2d8957fSAlexander Stein 732b2d8957fSAlexander Stein pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); 733b2d8957fSAlexander Stein if (!pdata) 734b2d8957fSAlexander Stein return ERR_PTR(-ENOMEM); 735b2d8957fSAlexander Stein 736b2d8957fSAlexander Stein pdata->reset_pin = of_get_named_gpio(dev->of_node, "ac97-gpios", 2); 737b2d8957fSAlexander Stein 738b2d8957fSAlexander Stein return pdata; 739b2d8957fSAlexander Stein } 740b2d8957fSAlexander Stein #else 741b2d8957fSAlexander Stein static struct ac97c_platform_data *atmel_ac97c_probe_dt(struct device *dev) 742b2d8957fSAlexander Stein { 743b2d8957fSAlexander Stein dev_err(dev, "no platform data defined\n"); 744b2d8957fSAlexander Stein return ERR_PTR(-ENXIO); 745b2d8957fSAlexander Stein } 746b2d8957fSAlexander Stein #endif 747b2d8957fSAlexander Stein 74861dc674cSBill Pemberton static int atmel_ac97c_probe(struct platform_device *pdev) 7494ede028fSHans-Christian Egtvedt { 7504ede028fSHans-Christian Egtvedt struct snd_card *card; 7514ede028fSHans-Christian Egtvedt struct atmel_ac97c *chip; 7524ede028fSHans-Christian Egtvedt struct resource *regs; 7534ede028fSHans-Christian Egtvedt struct ac97c_platform_data *pdata; 7544ede028fSHans-Christian Egtvedt struct clk *pclk; 7554ede028fSHans-Christian Egtvedt static struct snd_ac97_bus_ops ops = { 7564ede028fSHans-Christian Egtvedt .write = atmel_ac97c_write, 7574ede028fSHans-Christian Egtvedt .read = atmel_ac97c_read, 7584ede028fSHans-Christian Egtvedt }; 7594ede028fSHans-Christian Egtvedt int retval; 760df163587SHans-Christian Egtvedt int irq; 7614ede028fSHans-Christian Egtvedt 7624ede028fSHans-Christian Egtvedt regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); 7634ede028fSHans-Christian Egtvedt if (!regs) { 7644ede028fSHans-Christian Egtvedt dev_dbg(&pdev->dev, "no memory resource\n"); 7654ede028fSHans-Christian Egtvedt return -ENXIO; 7664ede028fSHans-Christian Egtvedt } 7674ede028fSHans-Christian Egtvedt 768b2d8957fSAlexander Stein pdata = dev_get_platdata(&pdev->dev); 7694ede028fSHans-Christian Egtvedt if (!pdata) { 770b2d8957fSAlexander Stein pdata = atmel_ac97c_probe_dt(&pdev->dev); 771b2d8957fSAlexander Stein if (IS_ERR(pdata)) 772b2d8957fSAlexander Stein return PTR_ERR(pdata); 7734ede028fSHans-Christian Egtvedt } 7744ede028fSHans-Christian Egtvedt 775df163587SHans-Christian Egtvedt irq = platform_get_irq(pdev, 0); 776df163587SHans-Christian Egtvedt if (irq < 0) { 77777201135SGustavo A. R. Silva dev_dbg(&pdev->dev, "could not get irq: %d\n", irq); 77877201135SGustavo A. R. Silva return irq; 779df163587SHans-Christian Egtvedt } 780df163587SHans-Christian Egtvedt 7817177395fSSedji Gaouaou pclk = clk_get(&pdev->dev, "ac97_clk"); 7824ede028fSHans-Christian Egtvedt if (IS_ERR(pclk)) { 7834ede028fSHans-Christian Egtvedt dev_dbg(&pdev->dev, "no peripheral clock\n"); 7844ede028fSHans-Christian Egtvedt return PTR_ERR(pclk); 7854ede028fSHans-Christian Egtvedt } 7861132015bSAlexander Stein clk_prepare_enable(pclk); 7874ede028fSHans-Christian Egtvedt 788a4f2473dSTakashi Iwai retval = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1, 789a4f2473dSTakashi Iwai SNDRV_DEFAULT_STR1, THIS_MODULE, 790a4f2473dSTakashi Iwai sizeof(struct atmel_ac97c), &card); 7914ede028fSHans-Christian Egtvedt if (retval) { 7924ede028fSHans-Christian Egtvedt dev_dbg(&pdev->dev, "could not create sound card device\n"); 7934ede028fSHans-Christian Egtvedt goto err_snd_card_new; 7944ede028fSHans-Christian Egtvedt } 7954ede028fSHans-Christian Egtvedt 7964ede028fSHans-Christian Egtvedt chip = get_chip(card); 7974ede028fSHans-Christian Egtvedt 798df163587SHans-Christian Egtvedt retval = request_irq(irq, atmel_ac97c_interrupt, 0, "AC97C", chip); 799df163587SHans-Christian Egtvedt if (retval) { 800df163587SHans-Christian Egtvedt dev_dbg(&pdev->dev, "unable to request irq %d\n", irq); 801df163587SHans-Christian Egtvedt goto err_request_irq; 802df163587SHans-Christian Egtvedt } 803df163587SHans-Christian Egtvedt chip->irq = irq; 804df163587SHans-Christian Egtvedt 8054ede028fSHans-Christian Egtvedt spin_lock_init(&chip->lock); 8064ede028fSHans-Christian Egtvedt 8074ede028fSHans-Christian Egtvedt strcpy(card->driver, "Atmel AC97C"); 8084ede028fSHans-Christian Egtvedt strcpy(card->shortname, "Atmel AC97C"); 8094ede028fSHans-Christian Egtvedt sprintf(card->longname, "Atmel AC97 controller"); 8104ede028fSHans-Christian Egtvedt 8114ede028fSHans-Christian Egtvedt chip->card = card; 8124ede028fSHans-Christian Egtvedt chip->pclk = pclk; 8134ede028fSHans-Christian Egtvedt chip->pdev = pdev; 81428f65c11SJoe Perches chip->regs = ioremap(regs->start, resource_size(regs)); 8154ede028fSHans-Christian Egtvedt 8164ede028fSHans-Christian Egtvedt if (!chip->regs) { 8174ede028fSHans-Christian Egtvedt dev_dbg(&pdev->dev, "could not remap register memory\n"); 8180c23e46eSJulia Lawall retval = -ENOMEM; 8194ede028fSHans-Christian Egtvedt goto err_ioremap; 8204ede028fSHans-Christian Egtvedt } 8214ede028fSHans-Christian Egtvedt 8224ede028fSHans-Christian Egtvedt if (gpio_is_valid(pdata->reset_pin)) { 8234ede028fSHans-Christian Egtvedt if (gpio_request(pdata->reset_pin, "reset_pin")) { 8244ede028fSHans-Christian Egtvedt dev_dbg(&pdev->dev, "reset pin not available\n"); 8254ede028fSHans-Christian Egtvedt chip->reset_pin = -ENODEV; 8264ede028fSHans-Christian Egtvedt } else { 8274ede028fSHans-Christian Egtvedt gpio_direction_output(pdata->reset_pin, 1); 8284ede028fSHans-Christian Egtvedt chip->reset_pin = pdata->reset_pin; 8294ede028fSHans-Christian Egtvedt } 830b2522f92SBo Shen } else { 831b2522f92SBo Shen chip->reset_pin = -EINVAL; 8324ede028fSHans-Christian Egtvedt } 8334ede028fSHans-Christian Egtvedt 83481baf3a7SHans-Christian Egtvedt atmel_ac97c_reset(chip); 83581baf3a7SHans-Christian Egtvedt 836df163587SHans-Christian Egtvedt /* Enable overrun interrupt from codec channel */ 837df163587SHans-Christian Egtvedt ac97c_writel(chip, COMR, AC97C_CSR_OVRUN); 838df163587SHans-Christian Egtvedt ac97c_writel(chip, IER, ac97c_readl(chip, IMR) | AC97C_SR_COEVT); 839df163587SHans-Christian Egtvedt 8404ede028fSHans-Christian Egtvedt retval = snd_ac97_bus(card, 0, &ops, chip, &chip->ac97_bus); 8414ede028fSHans-Christian Egtvedt if (retval) { 8424ede028fSHans-Christian Egtvedt dev_dbg(&pdev->dev, "could not register on ac97 bus\n"); 8434ede028fSHans-Christian Egtvedt goto err_ac97_bus; 8444ede028fSHans-Christian Egtvedt } 8454ede028fSHans-Christian Egtvedt 8464ede028fSHans-Christian Egtvedt retval = atmel_ac97c_mixer_new(chip); 8474ede028fSHans-Christian Egtvedt if (retval) { 8484ede028fSHans-Christian Egtvedt dev_dbg(&pdev->dev, "could not register ac97 mixer\n"); 8494ede028fSHans-Christian Egtvedt goto err_ac97_bus; 8504ede028fSHans-Christian Egtvedt } 8514ede028fSHans-Christian Egtvedt 8524ede028fSHans-Christian Egtvedt retval = atmel_ac97c_pcm_new(chip); 8534ede028fSHans-Christian Egtvedt if (retval) { 8544ede028fSHans-Christian Egtvedt dev_dbg(&pdev->dev, "could not register ac97 pcm device\n"); 855020c5260SAndy Shevchenko goto err_ac97_bus; 8564ede028fSHans-Christian Egtvedt } 8574ede028fSHans-Christian Egtvedt 8584ede028fSHans-Christian Egtvedt retval = snd_card_register(card); 8594ede028fSHans-Christian Egtvedt if (retval) { 8604ede028fSHans-Christian Egtvedt dev_dbg(&pdev->dev, "could not register sound card\n"); 861020c5260SAndy Shevchenko goto err_ac97_bus; 8624ede028fSHans-Christian Egtvedt } 8634ede028fSHans-Christian Egtvedt 8644ede028fSHans-Christian Egtvedt platform_set_drvdata(pdev, card); 8654ede028fSHans-Christian Egtvedt 8667177395fSSedji Gaouaou dev_info(&pdev->dev, "Atmel AC97 controller at 0x%p, irq = %d\n", 8677177395fSSedji Gaouaou chip->regs, irq); 8684ede028fSHans-Christian Egtvedt 8694ede028fSHans-Christian Egtvedt return 0; 8704ede028fSHans-Christian Egtvedt 8714ede028fSHans-Christian Egtvedt err_ac97_bus: 8724ede028fSHans-Christian Egtvedt if (gpio_is_valid(chip->reset_pin)) 8734ede028fSHans-Christian Egtvedt gpio_free(chip->reset_pin); 8744ede028fSHans-Christian Egtvedt 8754ede028fSHans-Christian Egtvedt iounmap(chip->regs); 8764ede028fSHans-Christian Egtvedt err_ioremap: 877df163587SHans-Christian Egtvedt free_irq(irq, chip); 878df163587SHans-Christian Egtvedt err_request_irq: 8794ede028fSHans-Christian Egtvedt snd_card_free(card); 8804ede028fSHans-Christian Egtvedt err_snd_card_new: 8811132015bSAlexander Stein clk_disable_unprepare(pclk); 8824ede028fSHans-Christian Egtvedt clk_put(pclk); 8834ede028fSHans-Christian Egtvedt return retval; 8844ede028fSHans-Christian Egtvedt } 8854ede028fSHans-Christian Egtvedt 886d34e4e00STakashi Iwai #ifdef CONFIG_PM_SLEEP 887284e7ca7STakashi Iwai static int atmel_ac97c_suspend(struct device *pdev) 8884ede028fSHans-Christian Egtvedt { 889284e7ca7STakashi Iwai struct snd_card *card = dev_get_drvdata(pdev); 8904ede028fSHans-Christian Egtvedt struct atmel_ac97c *chip = card->private_data; 8914ede028fSHans-Christian Egtvedt 8921132015bSAlexander Stein clk_disable_unprepare(chip->pclk); 8934ede028fSHans-Christian Egtvedt return 0; 8944ede028fSHans-Christian Egtvedt } 8954ede028fSHans-Christian Egtvedt 896284e7ca7STakashi Iwai static int atmel_ac97c_resume(struct device *pdev) 8974ede028fSHans-Christian Egtvedt { 898284e7ca7STakashi Iwai struct snd_card *card = dev_get_drvdata(pdev); 8994ede028fSHans-Christian Egtvedt struct atmel_ac97c *chip = card->private_data; 9004ede028fSHans-Christian Egtvedt 9011132015bSAlexander Stein clk_prepare_enable(chip->pclk); 9024ede028fSHans-Christian Egtvedt return 0; 9034ede028fSHans-Christian Egtvedt } 904284e7ca7STakashi Iwai 905284e7ca7STakashi Iwai static SIMPLE_DEV_PM_OPS(atmel_ac97c_pm, atmel_ac97c_suspend, atmel_ac97c_resume); 906284e7ca7STakashi Iwai #define ATMEL_AC97C_PM_OPS &atmel_ac97c_pm 9074ede028fSHans-Christian Egtvedt #else 908284e7ca7STakashi Iwai #define ATMEL_AC97C_PM_OPS NULL 9094ede028fSHans-Christian Egtvedt #endif 9104ede028fSHans-Christian Egtvedt 91161dc674cSBill Pemberton static int atmel_ac97c_remove(struct platform_device *pdev) 9124ede028fSHans-Christian Egtvedt { 9134ede028fSHans-Christian Egtvedt struct snd_card *card = platform_get_drvdata(pdev); 9144ede028fSHans-Christian Egtvedt struct atmel_ac97c *chip = get_chip(card); 9154ede028fSHans-Christian Egtvedt 9164ede028fSHans-Christian Egtvedt if (gpio_is_valid(chip->reset_pin)) 9174ede028fSHans-Christian Egtvedt gpio_free(chip->reset_pin); 9184ede028fSHans-Christian Egtvedt 919bd74a184SHans-Christian Egtvedt ac97c_writel(chip, CAMR, 0); 920bd74a184SHans-Christian Egtvedt ac97c_writel(chip, COMR, 0); 921bd74a184SHans-Christian Egtvedt ac97c_writel(chip, MR, 0); 922bd74a184SHans-Christian Egtvedt 9231132015bSAlexander Stein clk_disable_unprepare(chip->pclk); 9244ede028fSHans-Christian Egtvedt clk_put(chip->pclk); 9254ede028fSHans-Christian Egtvedt iounmap(chip->regs); 926df163587SHans-Christian Egtvedt free_irq(chip->irq, chip); 9274ede028fSHans-Christian Egtvedt 9284ede028fSHans-Christian Egtvedt snd_card_free(card); 9294ede028fSHans-Christian Egtvedt 9304ede028fSHans-Christian Egtvedt return 0; 9314ede028fSHans-Christian Egtvedt } 9324ede028fSHans-Christian Egtvedt 9334ede028fSHans-Christian Egtvedt static struct platform_driver atmel_ac97c_driver = { 9344b973ee0SAlexander Stein .probe = atmel_ac97c_probe, 93561dc674cSBill Pemberton .remove = atmel_ac97c_remove, 9364ede028fSHans-Christian Egtvedt .driver = { 9374ede028fSHans-Christian Egtvedt .name = "atmel_ac97c", 938284e7ca7STakashi Iwai .pm = ATMEL_AC97C_PM_OPS, 939b2d8957fSAlexander Stein .of_match_table = of_match_ptr(atmel_ac97c_dt_ids), 9404ede028fSHans-Christian Egtvedt }, 9414ede028fSHans-Christian Egtvedt }; 9424b973ee0SAlexander Stein module_platform_driver(atmel_ac97c_driver); 9434ede028fSHans-Christian Egtvedt 9444ede028fSHans-Christian Egtvedt MODULE_LICENSE("GPL"); 9454ede028fSHans-Christian Egtvedt MODULE_DESCRIPTION("Driver for Atmel AC97 controller"); 9460cfae7c9SHans-Christian Egtvedt MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>"); 947