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> 144ede028fSHans-Christian Egtvedt #include <linux/dmaengine.h> 154ede028fSHans-Christian Egtvedt #include <linux/dma-mapping.h> 164ede028fSHans-Christian Egtvedt #include <linux/init.h> 174ede028fSHans-Christian Egtvedt #include <linux/interrupt.h> 184ede028fSHans-Christian Egtvedt #include <linux/module.h> 194ede028fSHans-Christian Egtvedt #include <linux/platform_device.h> 204ede028fSHans-Christian Egtvedt #include <linux/mutex.h> 214ede028fSHans-Christian Egtvedt #include <linux/gpio.h> 224ede028fSHans-Christian Egtvedt #include <linux/io.h> 234ede028fSHans-Christian Egtvedt 244ede028fSHans-Christian Egtvedt #include <sound/core.h> 254ede028fSHans-Christian Egtvedt #include <sound/initval.h> 264ede028fSHans-Christian Egtvedt #include <sound/pcm.h> 274ede028fSHans-Christian Egtvedt #include <sound/pcm_params.h> 284ede028fSHans-Christian Egtvedt #include <sound/ac97_codec.h> 294ede028fSHans-Christian Egtvedt #include <sound/atmel-ac97c.h> 304ede028fSHans-Christian Egtvedt #include <sound/memalloc.h> 314ede028fSHans-Christian Egtvedt 324ede028fSHans-Christian Egtvedt #include <linux/dw_dmac.h> 334ede028fSHans-Christian Egtvedt 344ede028fSHans-Christian Egtvedt #include "ac97c.h" 354ede028fSHans-Christian Egtvedt 364ede028fSHans-Christian Egtvedt enum { 374ede028fSHans-Christian Egtvedt DMA_TX_READY = 0, 384ede028fSHans-Christian Egtvedt DMA_RX_READY, 394ede028fSHans-Christian Egtvedt DMA_TX_CHAN_PRESENT, 404ede028fSHans-Christian Egtvedt DMA_RX_CHAN_PRESENT, 414ede028fSHans-Christian Egtvedt }; 424ede028fSHans-Christian Egtvedt 434ede028fSHans-Christian Egtvedt /* Serialize access to opened variable */ 444ede028fSHans-Christian Egtvedt static DEFINE_MUTEX(opened_mutex); 454ede028fSHans-Christian Egtvedt 464ede028fSHans-Christian Egtvedt struct atmel_ac97c_dma { 474ede028fSHans-Christian Egtvedt struct dma_chan *rx_chan; 484ede028fSHans-Christian Egtvedt struct dma_chan *tx_chan; 494ede028fSHans-Christian Egtvedt }; 504ede028fSHans-Christian Egtvedt 514ede028fSHans-Christian Egtvedt struct atmel_ac97c { 524ede028fSHans-Christian Egtvedt struct clk *pclk; 534ede028fSHans-Christian Egtvedt struct platform_device *pdev; 544ede028fSHans-Christian Egtvedt struct atmel_ac97c_dma dma; 554ede028fSHans-Christian Egtvedt 564ede028fSHans-Christian Egtvedt struct snd_pcm_substream *playback_substream; 574ede028fSHans-Christian Egtvedt struct snd_pcm_substream *capture_substream; 584ede028fSHans-Christian Egtvedt struct snd_card *card; 594ede028fSHans-Christian Egtvedt struct snd_pcm *pcm; 604ede028fSHans-Christian Egtvedt struct snd_ac97 *ac97; 614ede028fSHans-Christian Egtvedt struct snd_ac97_bus *ac97_bus; 624ede028fSHans-Christian Egtvedt 634ede028fSHans-Christian Egtvedt u64 cur_format; 644ede028fSHans-Christian Egtvedt unsigned int cur_rate; 654ede028fSHans-Christian Egtvedt unsigned long flags; 664ede028fSHans-Christian Egtvedt /* Serialize access to opened variable */ 674ede028fSHans-Christian Egtvedt spinlock_t lock; 684ede028fSHans-Christian Egtvedt void __iomem *regs; 69df163587SHans-Christian Egtvedt int irq; 704ede028fSHans-Christian Egtvedt int opened; 714ede028fSHans-Christian Egtvedt int reset_pin; 724ede028fSHans-Christian Egtvedt }; 734ede028fSHans-Christian Egtvedt 744ede028fSHans-Christian Egtvedt #define get_chip(card) ((struct atmel_ac97c *)(card)->private_data) 754ede028fSHans-Christian Egtvedt 764ede028fSHans-Christian Egtvedt #define ac97c_writel(chip, reg, val) \ 774ede028fSHans-Christian Egtvedt __raw_writel((val), (chip)->regs + AC97C_##reg) 784ede028fSHans-Christian Egtvedt #define ac97c_readl(chip, reg) \ 794ede028fSHans-Christian Egtvedt __raw_readl((chip)->regs + AC97C_##reg) 804ede028fSHans-Christian Egtvedt 814ede028fSHans-Christian Egtvedt /* This function is called by the DMA driver. */ 824ede028fSHans-Christian Egtvedt static void atmel_ac97c_dma_playback_period_done(void *arg) 834ede028fSHans-Christian Egtvedt { 844ede028fSHans-Christian Egtvedt struct atmel_ac97c *chip = arg; 854ede028fSHans-Christian Egtvedt snd_pcm_period_elapsed(chip->playback_substream); 864ede028fSHans-Christian Egtvedt } 874ede028fSHans-Christian Egtvedt 884ede028fSHans-Christian Egtvedt static void atmel_ac97c_dma_capture_period_done(void *arg) 894ede028fSHans-Christian Egtvedt { 904ede028fSHans-Christian Egtvedt struct atmel_ac97c *chip = arg; 914ede028fSHans-Christian Egtvedt snd_pcm_period_elapsed(chip->capture_substream); 924ede028fSHans-Christian Egtvedt } 934ede028fSHans-Christian Egtvedt 944ede028fSHans-Christian Egtvedt static int atmel_ac97c_prepare_dma(struct atmel_ac97c *chip, 954ede028fSHans-Christian Egtvedt struct snd_pcm_substream *substream, 964ede028fSHans-Christian Egtvedt enum dma_data_direction direction) 974ede028fSHans-Christian Egtvedt { 984ede028fSHans-Christian Egtvedt struct dma_chan *chan; 994ede028fSHans-Christian Egtvedt struct dw_cyclic_desc *cdesc; 1004ede028fSHans-Christian Egtvedt struct snd_pcm_runtime *runtime = substream->runtime; 1014ede028fSHans-Christian Egtvedt unsigned long buffer_len, period_len; 1024ede028fSHans-Christian Egtvedt 1034ede028fSHans-Christian Egtvedt /* 1044ede028fSHans-Christian Egtvedt * We don't do DMA on "complex" transfers, i.e. with 1054ede028fSHans-Christian Egtvedt * non-halfword-aligned buffers or lengths. 1064ede028fSHans-Christian Egtvedt */ 1074ede028fSHans-Christian Egtvedt if (runtime->dma_addr & 1 || runtime->buffer_size & 1) { 1084ede028fSHans-Christian Egtvedt dev_dbg(&chip->pdev->dev, "too complex transfer\n"); 1094ede028fSHans-Christian Egtvedt return -EINVAL; 1104ede028fSHans-Christian Egtvedt } 1114ede028fSHans-Christian Egtvedt 1124ede028fSHans-Christian Egtvedt if (direction == DMA_TO_DEVICE) 1134ede028fSHans-Christian Egtvedt chan = chip->dma.tx_chan; 1144ede028fSHans-Christian Egtvedt else 1154ede028fSHans-Christian Egtvedt chan = chip->dma.rx_chan; 1164ede028fSHans-Christian Egtvedt 1174ede028fSHans-Christian Egtvedt buffer_len = frames_to_bytes(runtime, runtime->buffer_size); 1184ede028fSHans-Christian Egtvedt period_len = frames_to_bytes(runtime, runtime->period_size); 1194ede028fSHans-Christian Egtvedt 1204ede028fSHans-Christian Egtvedt cdesc = dw_dma_cyclic_prep(chan, runtime->dma_addr, buffer_len, 1214ede028fSHans-Christian Egtvedt period_len, direction); 1224ede028fSHans-Christian Egtvedt if (IS_ERR(cdesc)) { 1234ede028fSHans-Christian Egtvedt dev_dbg(&chip->pdev->dev, "could not prepare cyclic DMA\n"); 1244ede028fSHans-Christian Egtvedt return PTR_ERR(cdesc); 1254ede028fSHans-Christian Egtvedt } 1264ede028fSHans-Christian Egtvedt 1274ede028fSHans-Christian Egtvedt if (direction == DMA_TO_DEVICE) { 1284ede028fSHans-Christian Egtvedt cdesc->period_callback = atmel_ac97c_dma_playback_period_done; 1294ede028fSHans-Christian Egtvedt set_bit(DMA_TX_READY, &chip->flags); 1304ede028fSHans-Christian Egtvedt } else { 1314ede028fSHans-Christian Egtvedt cdesc->period_callback = atmel_ac97c_dma_capture_period_done; 1324ede028fSHans-Christian Egtvedt set_bit(DMA_RX_READY, &chip->flags); 1334ede028fSHans-Christian Egtvedt } 1344ede028fSHans-Christian Egtvedt 1354ede028fSHans-Christian Egtvedt cdesc->period_callback_param = chip; 1364ede028fSHans-Christian Egtvedt 1374ede028fSHans-Christian Egtvedt return 0; 1384ede028fSHans-Christian Egtvedt } 1394ede028fSHans-Christian Egtvedt 1404ede028fSHans-Christian Egtvedt static struct snd_pcm_hardware atmel_ac97c_hw = { 1414ede028fSHans-Christian Egtvedt .info = (SNDRV_PCM_INFO_MMAP 1424ede028fSHans-Christian Egtvedt | SNDRV_PCM_INFO_MMAP_VALID 1434ede028fSHans-Christian Egtvedt | SNDRV_PCM_INFO_INTERLEAVED 1444ede028fSHans-Christian Egtvedt | SNDRV_PCM_INFO_BLOCK_TRANSFER 1454ede028fSHans-Christian Egtvedt | SNDRV_PCM_INFO_JOINT_DUPLEX 1464ede028fSHans-Christian Egtvedt | SNDRV_PCM_INFO_RESUME 1474ede028fSHans-Christian Egtvedt | SNDRV_PCM_INFO_PAUSE), 1484ede028fSHans-Christian Egtvedt .formats = (SNDRV_PCM_FMTBIT_S16_BE 1494ede028fSHans-Christian Egtvedt | SNDRV_PCM_FMTBIT_S16_LE), 1504ede028fSHans-Christian Egtvedt .rates = (SNDRV_PCM_RATE_CONTINUOUS), 1514ede028fSHans-Christian Egtvedt .rate_min = 4000, 1524ede028fSHans-Christian Egtvedt .rate_max = 48000, 1534ede028fSHans-Christian Egtvedt .channels_min = 1, 1544ede028fSHans-Christian Egtvedt .channels_max = 2, 155c42eec0fSHans-Christian Egtvedt .buffer_bytes_max = 2 * 2 * 64 * 2048, 1564ede028fSHans-Christian Egtvedt .period_bytes_min = 4096, 1574ede028fSHans-Christian Egtvedt .period_bytes_max = 4096, 158c42eec0fSHans-Christian Egtvedt .periods_min = 6, 1594ede028fSHans-Christian Egtvedt .periods_max = 64, 1604ede028fSHans-Christian Egtvedt }; 1614ede028fSHans-Christian Egtvedt 1624ede028fSHans-Christian Egtvedt static int atmel_ac97c_playback_open(struct snd_pcm_substream *substream) 1634ede028fSHans-Christian Egtvedt { 1644ede028fSHans-Christian Egtvedt struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); 1654ede028fSHans-Christian Egtvedt struct snd_pcm_runtime *runtime = substream->runtime; 1664ede028fSHans-Christian Egtvedt 1674ede028fSHans-Christian Egtvedt mutex_lock(&opened_mutex); 1684ede028fSHans-Christian Egtvedt chip->opened++; 1694ede028fSHans-Christian Egtvedt runtime->hw = atmel_ac97c_hw; 1704ede028fSHans-Christian Egtvedt if (chip->cur_rate) { 1714ede028fSHans-Christian Egtvedt runtime->hw.rate_min = chip->cur_rate; 1724ede028fSHans-Christian Egtvedt runtime->hw.rate_max = chip->cur_rate; 1734ede028fSHans-Christian Egtvedt } 1744ede028fSHans-Christian Egtvedt if (chip->cur_format) 1754ede028fSHans-Christian Egtvedt runtime->hw.formats = (1ULL << chip->cur_format); 1764ede028fSHans-Christian Egtvedt mutex_unlock(&opened_mutex); 1774ede028fSHans-Christian Egtvedt chip->playback_substream = substream; 1784ede028fSHans-Christian Egtvedt return 0; 1794ede028fSHans-Christian Egtvedt } 1804ede028fSHans-Christian Egtvedt 1814ede028fSHans-Christian Egtvedt static int atmel_ac97c_capture_open(struct snd_pcm_substream *substream) 1824ede028fSHans-Christian Egtvedt { 1834ede028fSHans-Christian Egtvedt struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); 1844ede028fSHans-Christian Egtvedt struct snd_pcm_runtime *runtime = substream->runtime; 1854ede028fSHans-Christian Egtvedt 1864ede028fSHans-Christian Egtvedt mutex_lock(&opened_mutex); 1874ede028fSHans-Christian Egtvedt chip->opened++; 1884ede028fSHans-Christian Egtvedt runtime->hw = atmel_ac97c_hw; 1894ede028fSHans-Christian Egtvedt if (chip->cur_rate) { 1904ede028fSHans-Christian Egtvedt runtime->hw.rate_min = chip->cur_rate; 1914ede028fSHans-Christian Egtvedt runtime->hw.rate_max = chip->cur_rate; 1924ede028fSHans-Christian Egtvedt } 1934ede028fSHans-Christian Egtvedt if (chip->cur_format) 1944ede028fSHans-Christian Egtvedt runtime->hw.formats = (1ULL << chip->cur_format); 1954ede028fSHans-Christian Egtvedt mutex_unlock(&opened_mutex); 1964ede028fSHans-Christian Egtvedt chip->capture_substream = substream; 1974ede028fSHans-Christian Egtvedt return 0; 1984ede028fSHans-Christian Egtvedt } 1994ede028fSHans-Christian Egtvedt 2004ede028fSHans-Christian Egtvedt static int atmel_ac97c_playback_close(struct snd_pcm_substream *substream) 2014ede028fSHans-Christian Egtvedt { 2024ede028fSHans-Christian Egtvedt struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); 2034ede028fSHans-Christian Egtvedt 2044ede028fSHans-Christian Egtvedt mutex_lock(&opened_mutex); 2054ede028fSHans-Christian Egtvedt chip->opened--; 2064ede028fSHans-Christian Egtvedt if (!chip->opened) { 2074ede028fSHans-Christian Egtvedt chip->cur_rate = 0; 2084ede028fSHans-Christian Egtvedt chip->cur_format = 0; 2094ede028fSHans-Christian Egtvedt } 2104ede028fSHans-Christian Egtvedt mutex_unlock(&opened_mutex); 2114ede028fSHans-Christian Egtvedt 2124ede028fSHans-Christian Egtvedt chip->playback_substream = NULL; 2134ede028fSHans-Christian Egtvedt 2144ede028fSHans-Christian Egtvedt return 0; 2154ede028fSHans-Christian Egtvedt } 2164ede028fSHans-Christian Egtvedt 2174ede028fSHans-Christian Egtvedt static int atmel_ac97c_capture_close(struct snd_pcm_substream *substream) 2184ede028fSHans-Christian Egtvedt { 2194ede028fSHans-Christian Egtvedt struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); 2204ede028fSHans-Christian Egtvedt 2214ede028fSHans-Christian Egtvedt mutex_lock(&opened_mutex); 2224ede028fSHans-Christian Egtvedt chip->opened--; 2234ede028fSHans-Christian Egtvedt if (!chip->opened) { 2244ede028fSHans-Christian Egtvedt chip->cur_rate = 0; 2254ede028fSHans-Christian Egtvedt chip->cur_format = 0; 2264ede028fSHans-Christian Egtvedt } 2274ede028fSHans-Christian Egtvedt mutex_unlock(&opened_mutex); 2284ede028fSHans-Christian Egtvedt 2294ede028fSHans-Christian Egtvedt chip->capture_substream = NULL; 2304ede028fSHans-Christian Egtvedt 2314ede028fSHans-Christian Egtvedt return 0; 2324ede028fSHans-Christian Egtvedt } 2334ede028fSHans-Christian Egtvedt 2344ede028fSHans-Christian Egtvedt static int atmel_ac97c_playback_hw_params(struct snd_pcm_substream *substream, 2354ede028fSHans-Christian Egtvedt struct snd_pcm_hw_params *hw_params) 2364ede028fSHans-Christian Egtvedt { 2374ede028fSHans-Christian Egtvedt struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); 2384ede028fSHans-Christian Egtvedt int retval; 2394ede028fSHans-Christian Egtvedt 2404ede028fSHans-Christian Egtvedt retval = snd_pcm_lib_malloc_pages(substream, 2414ede028fSHans-Christian Egtvedt params_buffer_bytes(hw_params)); 2424ede028fSHans-Christian Egtvedt if (retval < 0) 2434ede028fSHans-Christian Egtvedt return retval; 2444ede028fSHans-Christian Egtvedt /* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */ 2454ede028fSHans-Christian Egtvedt if (retval == 1) 2464ede028fSHans-Christian Egtvedt if (test_and_clear_bit(DMA_TX_READY, &chip->flags)) 2474ede028fSHans-Christian Egtvedt dw_dma_cyclic_free(chip->dma.tx_chan); 2484ede028fSHans-Christian Egtvedt 2494ede028fSHans-Christian Egtvedt /* Set restrictions to params. */ 2504ede028fSHans-Christian Egtvedt mutex_lock(&opened_mutex); 2514ede028fSHans-Christian Egtvedt chip->cur_rate = params_rate(hw_params); 2524ede028fSHans-Christian Egtvedt chip->cur_format = params_format(hw_params); 2534ede028fSHans-Christian Egtvedt mutex_unlock(&opened_mutex); 2544ede028fSHans-Christian Egtvedt 2554ede028fSHans-Christian Egtvedt return retval; 2564ede028fSHans-Christian Egtvedt } 2574ede028fSHans-Christian Egtvedt 2584ede028fSHans-Christian Egtvedt static int atmel_ac97c_capture_hw_params(struct snd_pcm_substream *substream, 2594ede028fSHans-Christian Egtvedt struct snd_pcm_hw_params *hw_params) 2604ede028fSHans-Christian Egtvedt { 2614ede028fSHans-Christian Egtvedt struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); 2624ede028fSHans-Christian Egtvedt int retval; 2634ede028fSHans-Christian Egtvedt 2644ede028fSHans-Christian Egtvedt retval = snd_pcm_lib_malloc_pages(substream, 2654ede028fSHans-Christian Egtvedt params_buffer_bytes(hw_params)); 2664ede028fSHans-Christian Egtvedt if (retval < 0) 2674ede028fSHans-Christian Egtvedt return retval; 2684ede028fSHans-Christian Egtvedt /* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */ 2694ede028fSHans-Christian Egtvedt if (retval == 1) 2704ede028fSHans-Christian Egtvedt if (test_and_clear_bit(DMA_RX_READY, &chip->flags)) 2714ede028fSHans-Christian Egtvedt dw_dma_cyclic_free(chip->dma.rx_chan); 2724ede028fSHans-Christian Egtvedt 2734ede028fSHans-Christian Egtvedt /* Set restrictions to params. */ 2744ede028fSHans-Christian Egtvedt mutex_lock(&opened_mutex); 2754ede028fSHans-Christian Egtvedt chip->cur_rate = params_rate(hw_params); 2764ede028fSHans-Christian Egtvedt chip->cur_format = params_format(hw_params); 2774ede028fSHans-Christian Egtvedt mutex_unlock(&opened_mutex); 2784ede028fSHans-Christian Egtvedt 2794ede028fSHans-Christian Egtvedt return retval; 2804ede028fSHans-Christian Egtvedt } 2814ede028fSHans-Christian Egtvedt 2824ede028fSHans-Christian Egtvedt static int atmel_ac97c_playback_hw_free(struct snd_pcm_substream *substream) 2834ede028fSHans-Christian Egtvedt { 2844ede028fSHans-Christian Egtvedt struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); 2854ede028fSHans-Christian Egtvedt if (test_and_clear_bit(DMA_TX_READY, &chip->flags)) 2864ede028fSHans-Christian Egtvedt dw_dma_cyclic_free(chip->dma.tx_chan); 2874ede028fSHans-Christian Egtvedt return snd_pcm_lib_free_pages(substream); 2884ede028fSHans-Christian Egtvedt } 2894ede028fSHans-Christian Egtvedt 2904ede028fSHans-Christian Egtvedt static int atmel_ac97c_capture_hw_free(struct snd_pcm_substream *substream) 2914ede028fSHans-Christian Egtvedt { 2924ede028fSHans-Christian Egtvedt struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); 2934ede028fSHans-Christian Egtvedt if (test_and_clear_bit(DMA_RX_READY, &chip->flags)) 2944ede028fSHans-Christian Egtvedt dw_dma_cyclic_free(chip->dma.rx_chan); 2954ede028fSHans-Christian Egtvedt return snd_pcm_lib_free_pages(substream); 2964ede028fSHans-Christian Egtvedt } 2974ede028fSHans-Christian Egtvedt 2984ede028fSHans-Christian Egtvedt static int atmel_ac97c_playback_prepare(struct snd_pcm_substream *substream) 2994ede028fSHans-Christian Egtvedt { 3004ede028fSHans-Christian Egtvedt struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); 3014ede028fSHans-Christian Egtvedt struct snd_pcm_runtime *runtime = substream->runtime; 302128ed6a9SHans-Christian Egtvedt unsigned long word = ac97c_readl(chip, OCA); 3034ede028fSHans-Christian Egtvedt int retval; 3044ede028fSHans-Christian Egtvedt 305128ed6a9SHans-Christian Egtvedt word &= ~(AC97C_CH_MASK(PCM_LEFT) | AC97C_CH_MASK(PCM_RIGHT)); 306128ed6a9SHans-Christian Egtvedt 3074ede028fSHans-Christian Egtvedt /* assign channels to AC97C channel A */ 3084ede028fSHans-Christian Egtvedt switch (runtime->channels) { 3094ede028fSHans-Christian Egtvedt case 1: 3104ede028fSHans-Christian Egtvedt word |= AC97C_CH_ASSIGN(PCM_LEFT, A); 3114ede028fSHans-Christian Egtvedt break; 3124ede028fSHans-Christian Egtvedt case 2: 3134ede028fSHans-Christian Egtvedt word |= AC97C_CH_ASSIGN(PCM_LEFT, A) 3144ede028fSHans-Christian Egtvedt | AC97C_CH_ASSIGN(PCM_RIGHT, A); 3154ede028fSHans-Christian Egtvedt break; 3164ede028fSHans-Christian Egtvedt default: 3174ede028fSHans-Christian Egtvedt /* TODO: support more than two channels */ 3184ede028fSHans-Christian Egtvedt return -EINVAL; 3194ede028fSHans-Christian Egtvedt } 3204ede028fSHans-Christian Egtvedt ac97c_writel(chip, OCA, word); 3214ede028fSHans-Christian Egtvedt 3224ede028fSHans-Christian Egtvedt /* configure sample format and size */ 3234ede028fSHans-Christian Egtvedt word = AC97C_CMR_DMAEN | AC97C_CMR_SIZE_16; 3244ede028fSHans-Christian Egtvedt 3254ede028fSHans-Christian Egtvedt switch (runtime->format) { 3264ede028fSHans-Christian Egtvedt case SNDRV_PCM_FORMAT_S16_LE: 3274ede028fSHans-Christian Egtvedt word |= AC97C_CMR_CEM_LITTLE; 3284ede028fSHans-Christian Egtvedt break; 3294ede028fSHans-Christian Egtvedt case SNDRV_PCM_FORMAT_S16_BE: /* fall through */ 3304ede028fSHans-Christian Egtvedt word &= ~(AC97C_CMR_CEM_LITTLE); 3314ede028fSHans-Christian Egtvedt break; 332128ed6a9SHans-Christian Egtvedt default: 333128ed6a9SHans-Christian Egtvedt word = ac97c_readl(chip, OCA); 334128ed6a9SHans-Christian Egtvedt word &= ~(AC97C_CH_MASK(PCM_LEFT) | AC97C_CH_MASK(PCM_RIGHT)); 335128ed6a9SHans-Christian Egtvedt ac97c_writel(chip, OCA, word); 336128ed6a9SHans-Christian Egtvedt return -EINVAL; 3374ede028fSHans-Christian Egtvedt } 3384ede028fSHans-Christian Egtvedt 339df163587SHans-Christian Egtvedt /* Enable underrun interrupt on channel A */ 340df163587SHans-Christian Egtvedt word |= AC97C_CSR_UNRUN; 341df163587SHans-Christian Egtvedt 3424ede028fSHans-Christian Egtvedt ac97c_writel(chip, CAMR, word); 3434ede028fSHans-Christian Egtvedt 344df163587SHans-Christian Egtvedt /* Enable channel A event interrupt */ 345df163587SHans-Christian Egtvedt word = ac97c_readl(chip, IMR); 346df163587SHans-Christian Egtvedt word |= AC97C_SR_CAEVT; 347df163587SHans-Christian Egtvedt ac97c_writel(chip, IER, word); 348df163587SHans-Christian Egtvedt 3494ede028fSHans-Christian Egtvedt /* set variable rate if needed */ 3504ede028fSHans-Christian Egtvedt if (runtime->rate != 48000) { 3514ede028fSHans-Christian Egtvedt word = ac97c_readl(chip, MR); 3524ede028fSHans-Christian Egtvedt word |= AC97C_MR_VRA; 3534ede028fSHans-Christian Egtvedt ac97c_writel(chip, MR, word); 3544ede028fSHans-Christian Egtvedt } else { 3554ede028fSHans-Christian Egtvedt word = ac97c_readl(chip, MR); 3564ede028fSHans-Christian Egtvedt word &= ~(AC97C_MR_VRA); 3574ede028fSHans-Christian Egtvedt ac97c_writel(chip, MR, word); 3584ede028fSHans-Christian Egtvedt } 3594ede028fSHans-Christian Egtvedt 3604ede028fSHans-Christian Egtvedt retval = snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, 3614ede028fSHans-Christian Egtvedt runtime->rate); 3624ede028fSHans-Christian Egtvedt if (retval) 3634ede028fSHans-Christian Egtvedt dev_dbg(&chip->pdev->dev, "could not set rate %d Hz\n", 3644ede028fSHans-Christian Egtvedt runtime->rate); 3654ede028fSHans-Christian Egtvedt 3664ede028fSHans-Christian Egtvedt if (!test_bit(DMA_TX_READY, &chip->flags)) 3674ede028fSHans-Christian Egtvedt retval = atmel_ac97c_prepare_dma(chip, substream, 3684ede028fSHans-Christian Egtvedt DMA_TO_DEVICE); 3694ede028fSHans-Christian Egtvedt 3704ede028fSHans-Christian Egtvedt return retval; 3714ede028fSHans-Christian Egtvedt } 3724ede028fSHans-Christian Egtvedt 3734ede028fSHans-Christian Egtvedt static int atmel_ac97c_capture_prepare(struct snd_pcm_substream *substream) 3744ede028fSHans-Christian Egtvedt { 3754ede028fSHans-Christian Egtvedt struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); 3764ede028fSHans-Christian Egtvedt struct snd_pcm_runtime *runtime = substream->runtime; 377128ed6a9SHans-Christian Egtvedt unsigned long word = ac97c_readl(chip, ICA); 3784ede028fSHans-Christian Egtvedt int retval; 3794ede028fSHans-Christian Egtvedt 380128ed6a9SHans-Christian Egtvedt word &= ~(AC97C_CH_MASK(PCM_LEFT) | AC97C_CH_MASK(PCM_RIGHT)); 381128ed6a9SHans-Christian Egtvedt 3824ede028fSHans-Christian Egtvedt /* assign channels to AC97C channel A */ 3834ede028fSHans-Christian Egtvedt switch (runtime->channels) { 3844ede028fSHans-Christian Egtvedt case 1: 3854ede028fSHans-Christian Egtvedt word |= AC97C_CH_ASSIGN(PCM_LEFT, A); 3864ede028fSHans-Christian Egtvedt break; 3874ede028fSHans-Christian Egtvedt case 2: 3884ede028fSHans-Christian Egtvedt word |= AC97C_CH_ASSIGN(PCM_LEFT, A) 3894ede028fSHans-Christian Egtvedt | AC97C_CH_ASSIGN(PCM_RIGHT, A); 3904ede028fSHans-Christian Egtvedt break; 3914ede028fSHans-Christian Egtvedt default: 3924ede028fSHans-Christian Egtvedt /* TODO: support more than two channels */ 3934ede028fSHans-Christian Egtvedt return -EINVAL; 3944ede028fSHans-Christian Egtvedt } 3954ede028fSHans-Christian Egtvedt ac97c_writel(chip, ICA, word); 3964ede028fSHans-Christian Egtvedt 3974ede028fSHans-Christian Egtvedt /* configure sample format and size */ 3984ede028fSHans-Christian Egtvedt word = AC97C_CMR_DMAEN | AC97C_CMR_SIZE_16; 3994ede028fSHans-Christian Egtvedt 4004ede028fSHans-Christian Egtvedt switch (runtime->format) { 4014ede028fSHans-Christian Egtvedt case SNDRV_PCM_FORMAT_S16_LE: 4024ede028fSHans-Christian Egtvedt word |= AC97C_CMR_CEM_LITTLE; 4034ede028fSHans-Christian Egtvedt break; 4044ede028fSHans-Christian Egtvedt case SNDRV_PCM_FORMAT_S16_BE: /* fall through */ 4054ede028fSHans-Christian Egtvedt word &= ~(AC97C_CMR_CEM_LITTLE); 4064ede028fSHans-Christian Egtvedt break; 407128ed6a9SHans-Christian Egtvedt default: 408128ed6a9SHans-Christian Egtvedt word = ac97c_readl(chip, ICA); 409128ed6a9SHans-Christian Egtvedt word &= ~(AC97C_CH_MASK(PCM_LEFT) | AC97C_CH_MASK(PCM_RIGHT)); 410128ed6a9SHans-Christian Egtvedt ac97c_writel(chip, ICA, word); 411128ed6a9SHans-Christian Egtvedt return -EINVAL; 4124ede028fSHans-Christian Egtvedt } 4134ede028fSHans-Christian Egtvedt 414df163587SHans-Christian Egtvedt /* Enable overrun interrupt on channel A */ 415df163587SHans-Christian Egtvedt word |= AC97C_CSR_OVRUN; 416df163587SHans-Christian Egtvedt 4174ede028fSHans-Christian Egtvedt ac97c_writel(chip, CAMR, word); 4184ede028fSHans-Christian Egtvedt 419df163587SHans-Christian Egtvedt /* Enable channel A event interrupt */ 420df163587SHans-Christian Egtvedt word = ac97c_readl(chip, IMR); 421df163587SHans-Christian Egtvedt word |= AC97C_SR_CAEVT; 422df163587SHans-Christian Egtvedt ac97c_writel(chip, IER, word); 423df163587SHans-Christian Egtvedt 4244ede028fSHans-Christian Egtvedt /* set variable rate if needed */ 4254ede028fSHans-Christian Egtvedt if (runtime->rate != 48000) { 4264ede028fSHans-Christian Egtvedt word = ac97c_readl(chip, MR); 4274ede028fSHans-Christian Egtvedt word |= AC97C_MR_VRA; 4284ede028fSHans-Christian Egtvedt ac97c_writel(chip, MR, word); 4294ede028fSHans-Christian Egtvedt } else { 4304ede028fSHans-Christian Egtvedt word = ac97c_readl(chip, MR); 4314ede028fSHans-Christian Egtvedt word &= ~(AC97C_MR_VRA); 4324ede028fSHans-Christian Egtvedt ac97c_writel(chip, MR, word); 4334ede028fSHans-Christian Egtvedt } 4344ede028fSHans-Christian Egtvedt 4354ede028fSHans-Christian Egtvedt retval = snd_ac97_set_rate(chip->ac97, AC97_PCM_LR_ADC_RATE, 4364ede028fSHans-Christian Egtvedt runtime->rate); 4374ede028fSHans-Christian Egtvedt if (retval) 4384ede028fSHans-Christian Egtvedt dev_dbg(&chip->pdev->dev, "could not set rate %d Hz\n", 4394ede028fSHans-Christian Egtvedt runtime->rate); 4404ede028fSHans-Christian Egtvedt 4414ede028fSHans-Christian Egtvedt if (!test_bit(DMA_RX_READY, &chip->flags)) 4424ede028fSHans-Christian Egtvedt retval = atmel_ac97c_prepare_dma(chip, substream, 4434ede028fSHans-Christian Egtvedt DMA_FROM_DEVICE); 4444ede028fSHans-Christian Egtvedt 4454ede028fSHans-Christian Egtvedt return retval; 4464ede028fSHans-Christian Egtvedt } 4474ede028fSHans-Christian Egtvedt 4484ede028fSHans-Christian Egtvedt static int 4494ede028fSHans-Christian Egtvedt atmel_ac97c_playback_trigger(struct snd_pcm_substream *substream, int cmd) 4504ede028fSHans-Christian Egtvedt { 4514ede028fSHans-Christian Egtvedt struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); 4524ede028fSHans-Christian Egtvedt unsigned long camr; 4534ede028fSHans-Christian Egtvedt int retval = 0; 4544ede028fSHans-Christian Egtvedt 4554ede028fSHans-Christian Egtvedt camr = ac97c_readl(chip, CAMR); 4564ede028fSHans-Christian Egtvedt 4574ede028fSHans-Christian Egtvedt switch (cmd) { 4584ede028fSHans-Christian Egtvedt case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */ 4594ede028fSHans-Christian Egtvedt case SNDRV_PCM_TRIGGER_RESUME: /* fall through */ 4604ede028fSHans-Christian Egtvedt case SNDRV_PCM_TRIGGER_START: 4614ede028fSHans-Christian Egtvedt retval = dw_dma_cyclic_start(chip->dma.tx_chan); 4624ede028fSHans-Christian Egtvedt if (retval) 4634ede028fSHans-Christian Egtvedt goto out; 4644ede028fSHans-Christian Egtvedt camr |= AC97C_CMR_CENA; 4654ede028fSHans-Christian Egtvedt break; 4664ede028fSHans-Christian Egtvedt case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */ 4674ede028fSHans-Christian Egtvedt case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */ 4684ede028fSHans-Christian Egtvedt case SNDRV_PCM_TRIGGER_STOP: 4694ede028fSHans-Christian Egtvedt dw_dma_cyclic_stop(chip->dma.tx_chan); 4704ede028fSHans-Christian Egtvedt if (chip->opened <= 1) 4714ede028fSHans-Christian Egtvedt camr &= ~AC97C_CMR_CENA; 4724ede028fSHans-Christian Egtvedt break; 4734ede028fSHans-Christian Egtvedt default: 4744ede028fSHans-Christian Egtvedt retval = -EINVAL; 4754ede028fSHans-Christian Egtvedt goto out; 4764ede028fSHans-Christian Egtvedt } 4774ede028fSHans-Christian Egtvedt 4784ede028fSHans-Christian Egtvedt ac97c_writel(chip, CAMR, camr); 4794ede028fSHans-Christian Egtvedt out: 4804ede028fSHans-Christian Egtvedt return retval; 4814ede028fSHans-Christian Egtvedt } 4824ede028fSHans-Christian Egtvedt 4834ede028fSHans-Christian Egtvedt static int 4844ede028fSHans-Christian Egtvedt atmel_ac97c_capture_trigger(struct snd_pcm_substream *substream, int cmd) 4854ede028fSHans-Christian Egtvedt { 4864ede028fSHans-Christian Egtvedt struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); 4874ede028fSHans-Christian Egtvedt unsigned long camr; 4884ede028fSHans-Christian Egtvedt int retval = 0; 4894ede028fSHans-Christian Egtvedt 4904ede028fSHans-Christian Egtvedt camr = ac97c_readl(chip, CAMR); 4914ede028fSHans-Christian Egtvedt 4924ede028fSHans-Christian Egtvedt switch (cmd) { 4934ede028fSHans-Christian Egtvedt case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */ 4944ede028fSHans-Christian Egtvedt case SNDRV_PCM_TRIGGER_RESUME: /* fall through */ 4954ede028fSHans-Christian Egtvedt case SNDRV_PCM_TRIGGER_START: 4964ede028fSHans-Christian Egtvedt retval = dw_dma_cyclic_start(chip->dma.rx_chan); 4974ede028fSHans-Christian Egtvedt if (retval) 4984ede028fSHans-Christian Egtvedt goto out; 4994ede028fSHans-Christian Egtvedt camr |= AC97C_CMR_CENA; 5004ede028fSHans-Christian Egtvedt break; 5014ede028fSHans-Christian Egtvedt case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */ 5024ede028fSHans-Christian Egtvedt case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */ 5034ede028fSHans-Christian Egtvedt case SNDRV_PCM_TRIGGER_STOP: 5044ede028fSHans-Christian Egtvedt dw_dma_cyclic_stop(chip->dma.rx_chan); 5054ede028fSHans-Christian Egtvedt if (chip->opened <= 1) 5064ede028fSHans-Christian Egtvedt camr &= ~AC97C_CMR_CENA; 5074ede028fSHans-Christian Egtvedt break; 5084ede028fSHans-Christian Egtvedt default: 5094ede028fSHans-Christian Egtvedt retval = -EINVAL; 5104ede028fSHans-Christian Egtvedt break; 5114ede028fSHans-Christian Egtvedt } 5124ede028fSHans-Christian Egtvedt 5134ede028fSHans-Christian Egtvedt ac97c_writel(chip, CAMR, camr); 5144ede028fSHans-Christian Egtvedt out: 5154ede028fSHans-Christian Egtvedt return retval; 5164ede028fSHans-Christian Egtvedt } 5174ede028fSHans-Christian Egtvedt 5184ede028fSHans-Christian Egtvedt static snd_pcm_uframes_t 5194ede028fSHans-Christian Egtvedt atmel_ac97c_playback_pointer(struct snd_pcm_substream *substream) 5204ede028fSHans-Christian Egtvedt { 5214ede028fSHans-Christian Egtvedt struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); 5224ede028fSHans-Christian Egtvedt struct snd_pcm_runtime *runtime = substream->runtime; 5234ede028fSHans-Christian Egtvedt snd_pcm_uframes_t frames; 5244ede028fSHans-Christian Egtvedt unsigned long bytes; 5254ede028fSHans-Christian Egtvedt 5264ede028fSHans-Christian Egtvedt bytes = dw_dma_get_src_addr(chip->dma.tx_chan); 5274ede028fSHans-Christian Egtvedt bytes -= runtime->dma_addr; 5284ede028fSHans-Christian Egtvedt 5294ede028fSHans-Christian Egtvedt frames = bytes_to_frames(runtime, bytes); 5304ede028fSHans-Christian Egtvedt if (frames >= runtime->buffer_size) 5314ede028fSHans-Christian Egtvedt frames -= runtime->buffer_size; 5324ede028fSHans-Christian Egtvedt return frames; 5334ede028fSHans-Christian Egtvedt } 5344ede028fSHans-Christian Egtvedt 5354ede028fSHans-Christian Egtvedt static snd_pcm_uframes_t 5364ede028fSHans-Christian Egtvedt atmel_ac97c_capture_pointer(struct snd_pcm_substream *substream) 5374ede028fSHans-Christian Egtvedt { 5384ede028fSHans-Christian Egtvedt struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); 5394ede028fSHans-Christian Egtvedt struct snd_pcm_runtime *runtime = substream->runtime; 5404ede028fSHans-Christian Egtvedt snd_pcm_uframes_t frames; 5414ede028fSHans-Christian Egtvedt unsigned long bytes; 5424ede028fSHans-Christian Egtvedt 5434ede028fSHans-Christian Egtvedt bytes = dw_dma_get_dst_addr(chip->dma.rx_chan); 5444ede028fSHans-Christian Egtvedt bytes -= runtime->dma_addr; 5454ede028fSHans-Christian Egtvedt 5464ede028fSHans-Christian Egtvedt frames = bytes_to_frames(runtime, bytes); 5474ede028fSHans-Christian Egtvedt if (frames >= runtime->buffer_size) 5484ede028fSHans-Christian Egtvedt frames -= runtime->buffer_size; 5494ede028fSHans-Christian Egtvedt return frames; 5504ede028fSHans-Christian Egtvedt } 5514ede028fSHans-Christian Egtvedt 5524ede028fSHans-Christian Egtvedt static struct snd_pcm_ops atmel_ac97_playback_ops = { 5534ede028fSHans-Christian Egtvedt .open = atmel_ac97c_playback_open, 5544ede028fSHans-Christian Egtvedt .close = atmel_ac97c_playback_close, 5554ede028fSHans-Christian Egtvedt .ioctl = snd_pcm_lib_ioctl, 5564ede028fSHans-Christian Egtvedt .hw_params = atmel_ac97c_playback_hw_params, 5574ede028fSHans-Christian Egtvedt .hw_free = atmel_ac97c_playback_hw_free, 5584ede028fSHans-Christian Egtvedt .prepare = atmel_ac97c_playback_prepare, 5594ede028fSHans-Christian Egtvedt .trigger = atmel_ac97c_playback_trigger, 5604ede028fSHans-Christian Egtvedt .pointer = atmel_ac97c_playback_pointer, 5614ede028fSHans-Christian Egtvedt }; 5624ede028fSHans-Christian Egtvedt 5634ede028fSHans-Christian Egtvedt static struct snd_pcm_ops atmel_ac97_capture_ops = { 5644ede028fSHans-Christian Egtvedt .open = atmel_ac97c_capture_open, 5654ede028fSHans-Christian Egtvedt .close = atmel_ac97c_capture_close, 5664ede028fSHans-Christian Egtvedt .ioctl = snd_pcm_lib_ioctl, 5674ede028fSHans-Christian Egtvedt .hw_params = atmel_ac97c_capture_hw_params, 5684ede028fSHans-Christian Egtvedt .hw_free = atmel_ac97c_capture_hw_free, 5694ede028fSHans-Christian Egtvedt .prepare = atmel_ac97c_capture_prepare, 5704ede028fSHans-Christian Egtvedt .trigger = atmel_ac97c_capture_trigger, 5714ede028fSHans-Christian Egtvedt .pointer = atmel_ac97c_capture_pointer, 5724ede028fSHans-Christian Egtvedt }; 5734ede028fSHans-Christian Egtvedt 574df163587SHans-Christian Egtvedt static irqreturn_t atmel_ac97c_interrupt(int irq, void *dev) 575df163587SHans-Christian Egtvedt { 576df163587SHans-Christian Egtvedt struct atmel_ac97c *chip = (struct atmel_ac97c *)dev; 577df163587SHans-Christian Egtvedt irqreturn_t retval = IRQ_NONE; 578df163587SHans-Christian Egtvedt u32 sr = ac97c_readl(chip, SR); 579df163587SHans-Christian Egtvedt u32 casr = ac97c_readl(chip, CASR); 580df163587SHans-Christian Egtvedt u32 cosr = ac97c_readl(chip, COSR); 581df163587SHans-Christian Egtvedt 582df163587SHans-Christian Egtvedt if (sr & AC97C_SR_CAEVT) { 583df163587SHans-Christian Egtvedt dev_info(&chip->pdev->dev, "channel A event%s%s%s%s%s%s\n", 584df163587SHans-Christian Egtvedt casr & AC97C_CSR_OVRUN ? " OVRUN" : "", 585df163587SHans-Christian Egtvedt casr & AC97C_CSR_RXRDY ? " RXRDY" : "", 586df163587SHans-Christian Egtvedt casr & AC97C_CSR_UNRUN ? " UNRUN" : "", 587df163587SHans-Christian Egtvedt casr & AC97C_CSR_TXEMPTY ? " TXEMPTY" : "", 588df163587SHans-Christian Egtvedt casr & AC97C_CSR_TXRDY ? " TXRDY" : "", 589df163587SHans-Christian Egtvedt !casr ? " NONE" : ""); 590df163587SHans-Christian Egtvedt retval = IRQ_HANDLED; 591df163587SHans-Christian Egtvedt } 592df163587SHans-Christian Egtvedt 593df163587SHans-Christian Egtvedt if (sr & AC97C_SR_COEVT) { 594df163587SHans-Christian Egtvedt dev_info(&chip->pdev->dev, "codec channel event%s%s%s%s%s\n", 595df163587SHans-Christian Egtvedt cosr & AC97C_CSR_OVRUN ? " OVRUN" : "", 596df163587SHans-Christian Egtvedt cosr & AC97C_CSR_RXRDY ? " RXRDY" : "", 597df163587SHans-Christian Egtvedt cosr & AC97C_CSR_TXEMPTY ? " TXEMPTY" : "", 598df163587SHans-Christian Egtvedt cosr & AC97C_CSR_TXRDY ? " TXRDY" : "", 599df163587SHans-Christian Egtvedt !cosr ? " NONE" : ""); 600df163587SHans-Christian Egtvedt retval = IRQ_HANDLED; 601df163587SHans-Christian Egtvedt } 602df163587SHans-Christian Egtvedt 603df163587SHans-Christian Egtvedt if (retval == IRQ_NONE) { 604df163587SHans-Christian Egtvedt dev_err(&chip->pdev->dev, "spurious interrupt sr 0x%08x " 605df163587SHans-Christian Egtvedt "casr 0x%08x cosr 0x%08x\n", sr, casr, cosr); 606df163587SHans-Christian Egtvedt } 607df163587SHans-Christian Egtvedt 608df163587SHans-Christian Egtvedt return retval; 609df163587SHans-Christian Egtvedt } 610df163587SHans-Christian Egtvedt 6114ede028fSHans-Christian Egtvedt static int __devinit atmel_ac97c_pcm_new(struct atmel_ac97c *chip) 6124ede028fSHans-Christian Egtvedt { 6134ede028fSHans-Christian Egtvedt struct snd_pcm *pcm; 6144ede028fSHans-Christian Egtvedt struct snd_pcm_hardware hw = atmel_ac97c_hw; 6154ede028fSHans-Christian Egtvedt int capture, playback, retval; 6164ede028fSHans-Christian Egtvedt 6174ede028fSHans-Christian Egtvedt capture = test_bit(DMA_RX_CHAN_PRESENT, &chip->flags); 6184ede028fSHans-Christian Egtvedt playback = test_bit(DMA_TX_CHAN_PRESENT, &chip->flags); 6194ede028fSHans-Christian Egtvedt 6204ede028fSHans-Christian Egtvedt retval = snd_pcm_new(chip->card, chip->card->shortname, 6214ede028fSHans-Christian Egtvedt chip->pdev->id, playback, capture, &pcm); 6224ede028fSHans-Christian Egtvedt if (retval) 6234ede028fSHans-Christian Egtvedt return retval; 6244ede028fSHans-Christian Egtvedt 6254ede028fSHans-Christian Egtvedt if (capture) 6264ede028fSHans-Christian Egtvedt snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, 6274ede028fSHans-Christian Egtvedt &atmel_ac97_capture_ops); 6284ede028fSHans-Christian Egtvedt if (playback) 6294ede028fSHans-Christian Egtvedt snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, 6304ede028fSHans-Christian Egtvedt &atmel_ac97_playback_ops); 6314ede028fSHans-Christian Egtvedt 6324ede028fSHans-Christian Egtvedt retval = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, 6334ede028fSHans-Christian Egtvedt &chip->pdev->dev, hw.periods_min * hw.period_bytes_min, 6344ede028fSHans-Christian Egtvedt hw.buffer_bytes_max); 6354ede028fSHans-Christian Egtvedt if (retval) 6364ede028fSHans-Christian Egtvedt return retval; 6374ede028fSHans-Christian Egtvedt 6384ede028fSHans-Christian Egtvedt pcm->private_data = chip; 6394ede028fSHans-Christian Egtvedt pcm->info_flags = 0; 6404ede028fSHans-Christian Egtvedt strcpy(pcm->name, chip->card->shortname); 6414ede028fSHans-Christian Egtvedt chip->pcm = pcm; 6424ede028fSHans-Christian Egtvedt 6434ede028fSHans-Christian Egtvedt return 0; 6444ede028fSHans-Christian Egtvedt } 6454ede028fSHans-Christian Egtvedt 6464ede028fSHans-Christian Egtvedt static int atmel_ac97c_mixer_new(struct atmel_ac97c *chip) 6474ede028fSHans-Christian Egtvedt { 6484ede028fSHans-Christian Egtvedt struct snd_ac97_template template; 6494ede028fSHans-Christian Egtvedt memset(&template, 0, sizeof(template)); 6504ede028fSHans-Christian Egtvedt template.private_data = chip; 6514ede028fSHans-Christian Egtvedt return snd_ac97_mixer(chip->ac97_bus, &template, &chip->ac97); 6524ede028fSHans-Christian Egtvedt } 6534ede028fSHans-Christian Egtvedt 6544ede028fSHans-Christian Egtvedt static void atmel_ac97c_write(struct snd_ac97 *ac97, unsigned short reg, 6554ede028fSHans-Christian Egtvedt unsigned short val) 6564ede028fSHans-Christian Egtvedt { 6574ede028fSHans-Christian Egtvedt struct atmel_ac97c *chip = get_chip(ac97); 6584ede028fSHans-Christian Egtvedt unsigned long word; 6594ede028fSHans-Christian Egtvedt int timeout = 40; 6604ede028fSHans-Christian Egtvedt 6614ede028fSHans-Christian Egtvedt word = (reg & 0x7f) << 16 | val; 6624ede028fSHans-Christian Egtvedt 6634ede028fSHans-Christian Egtvedt do { 6644ede028fSHans-Christian Egtvedt if (ac97c_readl(chip, COSR) & AC97C_CSR_TXRDY) { 6654ede028fSHans-Christian Egtvedt ac97c_writel(chip, COTHR, word); 6664ede028fSHans-Christian Egtvedt return; 6674ede028fSHans-Christian Egtvedt } 6684ede028fSHans-Christian Egtvedt udelay(1); 6694ede028fSHans-Christian Egtvedt } while (--timeout); 6704ede028fSHans-Christian Egtvedt 6714ede028fSHans-Christian Egtvedt dev_dbg(&chip->pdev->dev, "codec write timeout\n"); 6724ede028fSHans-Christian Egtvedt } 6734ede028fSHans-Christian Egtvedt 6744ede028fSHans-Christian Egtvedt static unsigned short atmel_ac97c_read(struct snd_ac97 *ac97, 6754ede028fSHans-Christian Egtvedt unsigned short reg) 6764ede028fSHans-Christian Egtvedt { 6774ede028fSHans-Christian Egtvedt struct atmel_ac97c *chip = get_chip(ac97); 6784ede028fSHans-Christian Egtvedt unsigned long word; 6794ede028fSHans-Christian Egtvedt int timeout = 40; 6804ede028fSHans-Christian Egtvedt int write = 10; 6814ede028fSHans-Christian Egtvedt 6824ede028fSHans-Christian Egtvedt word = (0x80 | (reg & 0x7f)) << 16; 6834ede028fSHans-Christian Egtvedt 6844ede028fSHans-Christian Egtvedt if ((ac97c_readl(chip, COSR) & AC97C_CSR_RXRDY) != 0) 6854ede028fSHans-Christian Egtvedt ac97c_readl(chip, CORHR); 6864ede028fSHans-Christian Egtvedt 6874ede028fSHans-Christian Egtvedt retry_write: 6884ede028fSHans-Christian Egtvedt timeout = 40; 6894ede028fSHans-Christian Egtvedt 6904ede028fSHans-Christian Egtvedt do { 6914ede028fSHans-Christian Egtvedt if ((ac97c_readl(chip, COSR) & AC97C_CSR_TXRDY) != 0) { 6924ede028fSHans-Christian Egtvedt ac97c_writel(chip, COTHR, word); 6934ede028fSHans-Christian Egtvedt goto read_reg; 6944ede028fSHans-Christian Egtvedt } 6954ede028fSHans-Christian Egtvedt udelay(10); 6964ede028fSHans-Christian Egtvedt } while (--timeout); 6974ede028fSHans-Christian Egtvedt 6984ede028fSHans-Christian Egtvedt if (!--write) 6994ede028fSHans-Christian Egtvedt goto timed_out; 7004ede028fSHans-Christian Egtvedt goto retry_write; 7014ede028fSHans-Christian Egtvedt 7024ede028fSHans-Christian Egtvedt read_reg: 7034ede028fSHans-Christian Egtvedt do { 7044ede028fSHans-Christian Egtvedt if ((ac97c_readl(chip, COSR) & AC97C_CSR_RXRDY) != 0) { 7054ede028fSHans-Christian Egtvedt unsigned short val = ac97c_readl(chip, CORHR); 7064ede028fSHans-Christian Egtvedt return val; 7074ede028fSHans-Christian Egtvedt } 7084ede028fSHans-Christian Egtvedt udelay(10); 7094ede028fSHans-Christian Egtvedt } while (--timeout); 7104ede028fSHans-Christian Egtvedt 7114ede028fSHans-Christian Egtvedt if (!--write) 7124ede028fSHans-Christian Egtvedt goto timed_out; 7134ede028fSHans-Christian Egtvedt goto retry_write; 7144ede028fSHans-Christian Egtvedt 7154ede028fSHans-Christian Egtvedt timed_out: 7164ede028fSHans-Christian Egtvedt dev_dbg(&chip->pdev->dev, "codec read timeout\n"); 7174ede028fSHans-Christian Egtvedt return 0xffff; 7184ede028fSHans-Christian Egtvedt } 7194ede028fSHans-Christian Egtvedt 7204ede028fSHans-Christian Egtvedt static bool filter(struct dma_chan *chan, void *slave) 7214ede028fSHans-Christian Egtvedt { 7224ede028fSHans-Christian Egtvedt struct dw_dma_slave *dws = slave; 7234ede028fSHans-Christian Egtvedt 7244ede028fSHans-Christian Egtvedt if (dws->dma_dev == chan->device->dev) { 7254ede028fSHans-Christian Egtvedt chan->private = dws; 7264ede028fSHans-Christian Egtvedt return true; 7274ede028fSHans-Christian Egtvedt } else 7284ede028fSHans-Christian Egtvedt return false; 7294ede028fSHans-Christian Egtvedt } 7304ede028fSHans-Christian Egtvedt 7314ede028fSHans-Christian Egtvedt static void atmel_ac97c_reset(struct atmel_ac97c *chip) 7324ede028fSHans-Christian Egtvedt { 73381baf3a7SHans-Christian Egtvedt ac97c_writel(chip, MR, 0); 73481baf3a7SHans-Christian Egtvedt ac97c_writel(chip, MR, AC97C_MR_ENA); 73581baf3a7SHans-Christian Egtvedt ac97c_writel(chip, CAMR, 0); 73681baf3a7SHans-Christian Egtvedt ac97c_writel(chip, COMR, 0); 7374ede028fSHans-Christian Egtvedt 7384ede028fSHans-Christian Egtvedt if (gpio_is_valid(chip->reset_pin)) { 7394ede028fSHans-Christian Egtvedt gpio_set_value(chip->reset_pin, 0); 7404ede028fSHans-Christian Egtvedt /* AC97 v2.2 specifications says minimum 1 us. */ 74181baf3a7SHans-Christian Egtvedt udelay(2); 7424ede028fSHans-Christian Egtvedt gpio_set_value(chip->reset_pin, 1); 7434ede028fSHans-Christian Egtvedt } 7444ede028fSHans-Christian Egtvedt } 7454ede028fSHans-Christian Egtvedt 7464ede028fSHans-Christian Egtvedt static int __devinit atmel_ac97c_probe(struct platform_device *pdev) 7474ede028fSHans-Christian Egtvedt { 7484ede028fSHans-Christian Egtvedt struct snd_card *card; 7494ede028fSHans-Christian Egtvedt struct atmel_ac97c *chip; 7504ede028fSHans-Christian Egtvedt struct resource *regs; 7514ede028fSHans-Christian Egtvedt struct ac97c_platform_data *pdata; 7524ede028fSHans-Christian Egtvedt struct clk *pclk; 7534ede028fSHans-Christian Egtvedt static struct snd_ac97_bus_ops ops = { 7544ede028fSHans-Christian Egtvedt .write = atmel_ac97c_write, 7554ede028fSHans-Christian Egtvedt .read = atmel_ac97c_read, 7564ede028fSHans-Christian Egtvedt }; 7574ede028fSHans-Christian Egtvedt int retval; 758df163587SHans-Christian Egtvedt int irq; 7594ede028fSHans-Christian Egtvedt 7604ede028fSHans-Christian Egtvedt regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); 7614ede028fSHans-Christian Egtvedt if (!regs) { 7624ede028fSHans-Christian Egtvedt dev_dbg(&pdev->dev, "no memory resource\n"); 7634ede028fSHans-Christian Egtvedt return -ENXIO; 7644ede028fSHans-Christian Egtvedt } 7654ede028fSHans-Christian Egtvedt 7664ede028fSHans-Christian Egtvedt pdata = pdev->dev.platform_data; 7674ede028fSHans-Christian Egtvedt if (!pdata) { 7684ede028fSHans-Christian Egtvedt dev_dbg(&pdev->dev, "no platform data\n"); 7694ede028fSHans-Christian Egtvedt return -ENXIO; 7704ede028fSHans-Christian Egtvedt } 7714ede028fSHans-Christian Egtvedt 772df163587SHans-Christian Egtvedt irq = platform_get_irq(pdev, 0); 773df163587SHans-Christian Egtvedt if (irq < 0) { 774df163587SHans-Christian Egtvedt dev_dbg(&pdev->dev, "could not get irq\n"); 775df163587SHans-Christian Egtvedt return -ENXIO; 776df163587SHans-Christian Egtvedt } 777df163587SHans-Christian Egtvedt 7784ede028fSHans-Christian Egtvedt pclk = clk_get(&pdev->dev, "pclk"); 7794ede028fSHans-Christian Egtvedt if (IS_ERR(pclk)) { 7804ede028fSHans-Christian Egtvedt dev_dbg(&pdev->dev, "no peripheral clock\n"); 7814ede028fSHans-Christian Egtvedt return PTR_ERR(pclk); 7824ede028fSHans-Christian Egtvedt } 7834ede028fSHans-Christian Egtvedt clk_enable(pclk); 7844ede028fSHans-Christian Egtvedt 7854ede028fSHans-Christian Egtvedt retval = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, 7864ede028fSHans-Christian Egtvedt THIS_MODULE, sizeof(struct atmel_ac97c), &card); 7874ede028fSHans-Christian Egtvedt if (retval) { 7884ede028fSHans-Christian Egtvedt dev_dbg(&pdev->dev, "could not create sound card device\n"); 7894ede028fSHans-Christian Egtvedt goto err_snd_card_new; 7904ede028fSHans-Christian Egtvedt } 7914ede028fSHans-Christian Egtvedt 7924ede028fSHans-Christian Egtvedt chip = get_chip(card); 7934ede028fSHans-Christian Egtvedt 794df163587SHans-Christian Egtvedt retval = request_irq(irq, atmel_ac97c_interrupt, 0, "AC97C", chip); 795df163587SHans-Christian Egtvedt if (retval) { 796df163587SHans-Christian Egtvedt dev_dbg(&pdev->dev, "unable to request irq %d\n", irq); 797df163587SHans-Christian Egtvedt goto err_request_irq; 798df163587SHans-Christian Egtvedt } 799df163587SHans-Christian Egtvedt chip->irq = irq; 800df163587SHans-Christian Egtvedt 8014ede028fSHans-Christian Egtvedt spin_lock_init(&chip->lock); 8024ede028fSHans-Christian Egtvedt 8034ede028fSHans-Christian Egtvedt strcpy(card->driver, "Atmel AC97C"); 8044ede028fSHans-Christian Egtvedt strcpy(card->shortname, "Atmel AC97C"); 8054ede028fSHans-Christian Egtvedt sprintf(card->longname, "Atmel AC97 controller"); 8064ede028fSHans-Christian Egtvedt 8074ede028fSHans-Christian Egtvedt chip->card = card; 8084ede028fSHans-Christian Egtvedt chip->pclk = pclk; 8094ede028fSHans-Christian Egtvedt chip->pdev = pdev; 8104ede028fSHans-Christian Egtvedt chip->regs = ioremap(regs->start, regs->end - regs->start + 1); 8114ede028fSHans-Christian Egtvedt 8124ede028fSHans-Christian Egtvedt if (!chip->regs) { 8134ede028fSHans-Christian Egtvedt dev_dbg(&pdev->dev, "could not remap register memory\n"); 8144ede028fSHans-Christian Egtvedt goto err_ioremap; 8154ede028fSHans-Christian Egtvedt } 8164ede028fSHans-Christian Egtvedt 8174ede028fSHans-Christian Egtvedt if (gpio_is_valid(pdata->reset_pin)) { 8184ede028fSHans-Christian Egtvedt if (gpio_request(pdata->reset_pin, "reset_pin")) { 8194ede028fSHans-Christian Egtvedt dev_dbg(&pdev->dev, "reset pin not available\n"); 8204ede028fSHans-Christian Egtvedt chip->reset_pin = -ENODEV; 8214ede028fSHans-Christian Egtvedt } else { 8224ede028fSHans-Christian Egtvedt gpio_direction_output(pdata->reset_pin, 1); 8234ede028fSHans-Christian Egtvedt chip->reset_pin = pdata->reset_pin; 8244ede028fSHans-Christian Egtvedt } 8254ede028fSHans-Christian Egtvedt } 8264ede028fSHans-Christian Egtvedt 8274ede028fSHans-Christian Egtvedt snd_card_set_dev(card, &pdev->dev); 8284ede028fSHans-Christian Egtvedt 82981baf3a7SHans-Christian Egtvedt atmel_ac97c_reset(chip); 83081baf3a7SHans-Christian Egtvedt 831df163587SHans-Christian Egtvedt /* Enable overrun interrupt from codec channel */ 832df163587SHans-Christian Egtvedt ac97c_writel(chip, COMR, AC97C_CSR_OVRUN); 833df163587SHans-Christian Egtvedt ac97c_writel(chip, IER, ac97c_readl(chip, IMR) | AC97C_SR_COEVT); 834df163587SHans-Christian Egtvedt 8354ede028fSHans-Christian Egtvedt retval = snd_ac97_bus(card, 0, &ops, chip, &chip->ac97_bus); 8364ede028fSHans-Christian Egtvedt if (retval) { 8374ede028fSHans-Christian Egtvedt dev_dbg(&pdev->dev, "could not register on ac97 bus\n"); 8384ede028fSHans-Christian Egtvedt goto err_ac97_bus; 8394ede028fSHans-Christian Egtvedt } 8404ede028fSHans-Christian Egtvedt 8414ede028fSHans-Christian Egtvedt retval = atmel_ac97c_mixer_new(chip); 8424ede028fSHans-Christian Egtvedt if (retval) { 8434ede028fSHans-Christian Egtvedt dev_dbg(&pdev->dev, "could not register ac97 mixer\n"); 8444ede028fSHans-Christian Egtvedt goto err_ac97_bus; 8454ede028fSHans-Christian Egtvedt } 8464ede028fSHans-Christian Egtvedt 8474ede028fSHans-Christian Egtvedt if (pdata->rx_dws.dma_dev) { 8484ede028fSHans-Christian Egtvedt struct dw_dma_slave *dws = &pdata->rx_dws; 8494ede028fSHans-Christian Egtvedt dma_cap_mask_t mask; 8504ede028fSHans-Christian Egtvedt 8514ede028fSHans-Christian Egtvedt dws->rx_reg = regs->start + AC97C_CARHR + 2; 8524ede028fSHans-Christian Egtvedt 8534ede028fSHans-Christian Egtvedt dma_cap_zero(mask); 8544ede028fSHans-Christian Egtvedt dma_cap_set(DMA_SLAVE, mask); 8554ede028fSHans-Christian Egtvedt 8564ede028fSHans-Christian Egtvedt chip->dma.rx_chan = dma_request_channel(mask, filter, dws); 8574ede028fSHans-Christian Egtvedt 8584ede028fSHans-Christian Egtvedt dev_info(&chip->pdev->dev, "using %s for DMA RX\n", 85923572856SHans-Christian Egtvedt dev_name(&chip->dma.rx_chan->dev->device)); 8604ede028fSHans-Christian Egtvedt set_bit(DMA_RX_CHAN_PRESENT, &chip->flags); 8614ede028fSHans-Christian Egtvedt } 8624ede028fSHans-Christian Egtvedt 8634ede028fSHans-Christian Egtvedt if (pdata->tx_dws.dma_dev) { 8644ede028fSHans-Christian Egtvedt struct dw_dma_slave *dws = &pdata->tx_dws; 8654ede028fSHans-Christian Egtvedt dma_cap_mask_t mask; 8664ede028fSHans-Christian Egtvedt 8674ede028fSHans-Christian Egtvedt dws->tx_reg = regs->start + AC97C_CATHR + 2; 8684ede028fSHans-Christian Egtvedt 8694ede028fSHans-Christian Egtvedt dma_cap_zero(mask); 8704ede028fSHans-Christian Egtvedt dma_cap_set(DMA_SLAVE, mask); 8714ede028fSHans-Christian Egtvedt 8724ede028fSHans-Christian Egtvedt chip->dma.tx_chan = dma_request_channel(mask, filter, dws); 8734ede028fSHans-Christian Egtvedt 8744ede028fSHans-Christian Egtvedt dev_info(&chip->pdev->dev, "using %s for DMA TX\n", 87523572856SHans-Christian Egtvedt dev_name(&chip->dma.tx_chan->dev->device)); 8764ede028fSHans-Christian Egtvedt set_bit(DMA_TX_CHAN_PRESENT, &chip->flags); 8774ede028fSHans-Christian Egtvedt } 8784ede028fSHans-Christian Egtvedt 8794ede028fSHans-Christian Egtvedt if (!test_bit(DMA_RX_CHAN_PRESENT, &chip->flags) && 8804ede028fSHans-Christian Egtvedt !test_bit(DMA_TX_CHAN_PRESENT, &chip->flags)) { 8814ede028fSHans-Christian Egtvedt dev_dbg(&pdev->dev, "DMA not available\n"); 8824ede028fSHans-Christian Egtvedt retval = -ENODEV; 8834ede028fSHans-Christian Egtvedt goto err_dma; 8844ede028fSHans-Christian Egtvedt } 8854ede028fSHans-Christian Egtvedt 8864ede028fSHans-Christian Egtvedt retval = atmel_ac97c_pcm_new(chip); 8874ede028fSHans-Christian Egtvedt if (retval) { 8884ede028fSHans-Christian Egtvedt dev_dbg(&pdev->dev, "could not register ac97 pcm device\n"); 8894ede028fSHans-Christian Egtvedt goto err_dma; 8904ede028fSHans-Christian Egtvedt } 8914ede028fSHans-Christian Egtvedt 8924ede028fSHans-Christian Egtvedt retval = snd_card_register(card); 8934ede028fSHans-Christian Egtvedt if (retval) { 8944ede028fSHans-Christian Egtvedt dev_dbg(&pdev->dev, "could not register sound card\n"); 895df163587SHans-Christian Egtvedt goto err_dma; 8964ede028fSHans-Christian Egtvedt } 8974ede028fSHans-Christian Egtvedt 8984ede028fSHans-Christian Egtvedt platform_set_drvdata(pdev, card); 8994ede028fSHans-Christian Egtvedt 9004ede028fSHans-Christian Egtvedt dev_info(&pdev->dev, "Atmel AC97 controller at 0x%p\n", 9014ede028fSHans-Christian Egtvedt chip->regs); 9024ede028fSHans-Christian Egtvedt 9034ede028fSHans-Christian Egtvedt return 0; 9044ede028fSHans-Christian Egtvedt 9054ede028fSHans-Christian Egtvedt err_dma: 9064ede028fSHans-Christian Egtvedt if (test_bit(DMA_RX_CHAN_PRESENT, &chip->flags)) 9074ede028fSHans-Christian Egtvedt dma_release_channel(chip->dma.rx_chan); 9084ede028fSHans-Christian Egtvedt if (test_bit(DMA_TX_CHAN_PRESENT, &chip->flags)) 9094ede028fSHans-Christian Egtvedt dma_release_channel(chip->dma.tx_chan); 9104ede028fSHans-Christian Egtvedt clear_bit(DMA_RX_CHAN_PRESENT, &chip->flags); 9114ede028fSHans-Christian Egtvedt clear_bit(DMA_TX_CHAN_PRESENT, &chip->flags); 9124ede028fSHans-Christian Egtvedt chip->dma.rx_chan = NULL; 9134ede028fSHans-Christian Egtvedt chip->dma.tx_chan = NULL; 9144ede028fSHans-Christian Egtvedt err_ac97_bus: 9154ede028fSHans-Christian Egtvedt snd_card_set_dev(card, NULL); 9164ede028fSHans-Christian Egtvedt 9174ede028fSHans-Christian Egtvedt if (gpio_is_valid(chip->reset_pin)) 9184ede028fSHans-Christian Egtvedt gpio_free(chip->reset_pin); 9194ede028fSHans-Christian Egtvedt 9204ede028fSHans-Christian Egtvedt iounmap(chip->regs); 9214ede028fSHans-Christian Egtvedt err_ioremap: 922df163587SHans-Christian Egtvedt free_irq(irq, chip); 923df163587SHans-Christian Egtvedt err_request_irq: 9244ede028fSHans-Christian Egtvedt snd_card_free(card); 9254ede028fSHans-Christian Egtvedt err_snd_card_new: 9264ede028fSHans-Christian Egtvedt clk_disable(pclk); 9274ede028fSHans-Christian Egtvedt clk_put(pclk); 9284ede028fSHans-Christian Egtvedt return retval; 9294ede028fSHans-Christian Egtvedt } 9304ede028fSHans-Christian Egtvedt 9314ede028fSHans-Christian Egtvedt #ifdef CONFIG_PM 9324ede028fSHans-Christian Egtvedt static int atmel_ac97c_suspend(struct platform_device *pdev, pm_message_t msg) 9334ede028fSHans-Christian Egtvedt { 9344ede028fSHans-Christian Egtvedt struct snd_card *card = platform_get_drvdata(pdev); 9354ede028fSHans-Christian Egtvedt struct atmel_ac97c *chip = card->private_data; 9364ede028fSHans-Christian Egtvedt 9374ede028fSHans-Christian Egtvedt if (test_bit(DMA_RX_READY, &chip->flags)) 9384ede028fSHans-Christian Egtvedt dw_dma_cyclic_stop(chip->dma.rx_chan); 9394ede028fSHans-Christian Egtvedt if (test_bit(DMA_TX_READY, &chip->flags)) 9404ede028fSHans-Christian Egtvedt dw_dma_cyclic_stop(chip->dma.tx_chan); 9414ede028fSHans-Christian Egtvedt clk_disable(chip->pclk); 9424ede028fSHans-Christian Egtvedt 9434ede028fSHans-Christian Egtvedt return 0; 9444ede028fSHans-Christian Egtvedt } 9454ede028fSHans-Christian Egtvedt 9464ede028fSHans-Christian Egtvedt static int atmel_ac97c_resume(struct platform_device *pdev) 9474ede028fSHans-Christian Egtvedt { 9484ede028fSHans-Christian Egtvedt struct snd_card *card = platform_get_drvdata(pdev); 9494ede028fSHans-Christian Egtvedt struct atmel_ac97c *chip = card->private_data; 9504ede028fSHans-Christian Egtvedt 9514ede028fSHans-Christian Egtvedt clk_enable(chip->pclk); 9524ede028fSHans-Christian Egtvedt if (test_bit(DMA_RX_READY, &chip->flags)) 9534ede028fSHans-Christian Egtvedt dw_dma_cyclic_start(chip->dma.rx_chan); 9544ede028fSHans-Christian Egtvedt if (test_bit(DMA_TX_READY, &chip->flags)) 9554ede028fSHans-Christian Egtvedt dw_dma_cyclic_start(chip->dma.tx_chan); 9564ede028fSHans-Christian Egtvedt 9574ede028fSHans-Christian Egtvedt return 0; 9584ede028fSHans-Christian Egtvedt } 9594ede028fSHans-Christian Egtvedt #else 9604ede028fSHans-Christian Egtvedt #define atmel_ac97c_suspend NULL 9614ede028fSHans-Christian Egtvedt #define atmel_ac97c_resume NULL 9624ede028fSHans-Christian Egtvedt #endif 9634ede028fSHans-Christian Egtvedt 9644ede028fSHans-Christian Egtvedt static int __devexit atmel_ac97c_remove(struct platform_device *pdev) 9654ede028fSHans-Christian Egtvedt { 9664ede028fSHans-Christian Egtvedt struct snd_card *card = platform_get_drvdata(pdev); 9674ede028fSHans-Christian Egtvedt struct atmel_ac97c *chip = get_chip(card); 9684ede028fSHans-Christian Egtvedt 9694ede028fSHans-Christian Egtvedt if (gpio_is_valid(chip->reset_pin)) 9704ede028fSHans-Christian Egtvedt gpio_free(chip->reset_pin); 9714ede028fSHans-Christian Egtvedt 972bd74a184SHans-Christian Egtvedt ac97c_writel(chip, CAMR, 0); 973bd74a184SHans-Christian Egtvedt ac97c_writel(chip, COMR, 0); 974bd74a184SHans-Christian Egtvedt ac97c_writel(chip, MR, 0); 975bd74a184SHans-Christian Egtvedt 9764ede028fSHans-Christian Egtvedt clk_disable(chip->pclk); 9774ede028fSHans-Christian Egtvedt clk_put(chip->pclk); 9784ede028fSHans-Christian Egtvedt iounmap(chip->regs); 979df163587SHans-Christian Egtvedt free_irq(chip->irq, chip); 9804ede028fSHans-Christian Egtvedt 9814ede028fSHans-Christian Egtvedt if (test_bit(DMA_RX_CHAN_PRESENT, &chip->flags)) 9824ede028fSHans-Christian Egtvedt dma_release_channel(chip->dma.rx_chan); 9834ede028fSHans-Christian Egtvedt if (test_bit(DMA_TX_CHAN_PRESENT, &chip->flags)) 9844ede028fSHans-Christian Egtvedt dma_release_channel(chip->dma.tx_chan); 9854ede028fSHans-Christian Egtvedt clear_bit(DMA_RX_CHAN_PRESENT, &chip->flags); 9864ede028fSHans-Christian Egtvedt clear_bit(DMA_TX_CHAN_PRESENT, &chip->flags); 9874ede028fSHans-Christian Egtvedt chip->dma.rx_chan = NULL; 9884ede028fSHans-Christian Egtvedt chip->dma.tx_chan = NULL; 9894ede028fSHans-Christian Egtvedt 9904ede028fSHans-Christian Egtvedt snd_card_set_dev(card, NULL); 9914ede028fSHans-Christian Egtvedt snd_card_free(card); 9924ede028fSHans-Christian Egtvedt 9934ede028fSHans-Christian Egtvedt platform_set_drvdata(pdev, NULL); 9944ede028fSHans-Christian Egtvedt 9954ede028fSHans-Christian Egtvedt return 0; 9964ede028fSHans-Christian Egtvedt } 9974ede028fSHans-Christian Egtvedt 9984ede028fSHans-Christian Egtvedt static struct platform_driver atmel_ac97c_driver = { 9994ede028fSHans-Christian Egtvedt .remove = __devexit_p(atmel_ac97c_remove), 10004ede028fSHans-Christian Egtvedt .driver = { 10014ede028fSHans-Christian Egtvedt .name = "atmel_ac97c", 10024ede028fSHans-Christian Egtvedt }, 10034ede028fSHans-Christian Egtvedt .suspend = atmel_ac97c_suspend, 10044ede028fSHans-Christian Egtvedt .resume = atmel_ac97c_resume, 10054ede028fSHans-Christian Egtvedt }; 10064ede028fSHans-Christian Egtvedt 10074ede028fSHans-Christian Egtvedt static int __init atmel_ac97c_init(void) 10084ede028fSHans-Christian Egtvedt { 10094ede028fSHans-Christian Egtvedt return platform_driver_probe(&atmel_ac97c_driver, 10104ede028fSHans-Christian Egtvedt atmel_ac97c_probe); 10114ede028fSHans-Christian Egtvedt } 10124ede028fSHans-Christian Egtvedt module_init(atmel_ac97c_init); 10134ede028fSHans-Christian Egtvedt 10144ede028fSHans-Christian Egtvedt static void __exit atmel_ac97c_exit(void) 10154ede028fSHans-Christian Egtvedt { 10164ede028fSHans-Christian Egtvedt platform_driver_unregister(&atmel_ac97c_driver); 10174ede028fSHans-Christian Egtvedt } 10184ede028fSHans-Christian Egtvedt module_exit(atmel_ac97c_exit); 10194ede028fSHans-Christian Egtvedt 10204ede028fSHans-Christian Egtvedt MODULE_LICENSE("GPL"); 10214ede028fSHans-Christian Egtvedt MODULE_DESCRIPTION("Driver for Atmel AC97 controller"); 10224ede028fSHans-Christian Egtvedt MODULE_AUTHOR("Hans-Christian Egtvedt <hans-christian.egtvedt@atmel.com>"); 1023