xref: /openbmc/linux/sound/atmel/ac97c.c (revision 23572856)
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