xref: /openbmc/linux/sound/atmel/ac97c.c (revision 8015e3de)
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>
167177395fSSedji Gaouaou #include <linux/atmel_pdc.h>
174ede028fSHans-Christian Egtvedt #include <linux/init.h>
184ede028fSHans-Christian Egtvedt #include <linux/interrupt.h>
194ede028fSHans-Christian Egtvedt #include <linux/module.h>
204ede028fSHans-Christian Egtvedt #include <linux/platform_device.h>
214ede028fSHans-Christian Egtvedt #include <linux/mutex.h>
224ede028fSHans-Christian Egtvedt #include <linux/gpio.h>
234ede028fSHans-Christian Egtvedt #include <linux/io.h>
244ede028fSHans-Christian Egtvedt 
254ede028fSHans-Christian Egtvedt #include <sound/core.h>
264ede028fSHans-Christian Egtvedt #include <sound/initval.h>
274ede028fSHans-Christian Egtvedt #include <sound/pcm.h>
284ede028fSHans-Christian Egtvedt #include <sound/pcm_params.h>
294ede028fSHans-Christian Egtvedt #include <sound/ac97_codec.h>
304ede028fSHans-Christian Egtvedt #include <sound/atmel-ac97c.h>
314ede028fSHans-Christian Egtvedt #include <sound/memalloc.h>
324ede028fSHans-Christian Egtvedt 
334ede028fSHans-Christian Egtvedt #include <linux/dw_dmac.h>
344ede028fSHans-Christian Egtvedt 
357177395fSSedji Gaouaou #include <mach/cpu.h>
367177395fSSedji Gaouaou #include <mach/gpio.h>
377177395fSSedji Gaouaou 
38fd76804fSHans-Christian Egtvedt #ifdef CONFIG_ARCH_AT91
39fd76804fSHans-Christian Egtvedt #include <mach/hardware.h>
40fd76804fSHans-Christian Egtvedt #endif
41fd76804fSHans-Christian Egtvedt 
424ede028fSHans-Christian Egtvedt #include "ac97c.h"
434ede028fSHans-Christian Egtvedt 
444ede028fSHans-Christian Egtvedt enum {
454ede028fSHans-Christian Egtvedt 	DMA_TX_READY = 0,
464ede028fSHans-Christian Egtvedt 	DMA_RX_READY,
474ede028fSHans-Christian Egtvedt 	DMA_TX_CHAN_PRESENT,
484ede028fSHans-Christian Egtvedt 	DMA_RX_CHAN_PRESENT,
494ede028fSHans-Christian Egtvedt };
504ede028fSHans-Christian Egtvedt 
514ede028fSHans-Christian Egtvedt /* Serialize access to opened variable */
524ede028fSHans-Christian Egtvedt static DEFINE_MUTEX(opened_mutex);
534ede028fSHans-Christian Egtvedt 
544ede028fSHans-Christian Egtvedt struct atmel_ac97c_dma {
554ede028fSHans-Christian Egtvedt 	struct dma_chan			*rx_chan;
564ede028fSHans-Christian Egtvedt 	struct dma_chan			*tx_chan;
574ede028fSHans-Christian Egtvedt };
584ede028fSHans-Christian Egtvedt 
594ede028fSHans-Christian Egtvedt struct atmel_ac97c {
604ede028fSHans-Christian Egtvedt 	struct clk			*pclk;
614ede028fSHans-Christian Egtvedt 	struct platform_device		*pdev;
624ede028fSHans-Christian Egtvedt 	struct atmel_ac97c_dma		dma;
634ede028fSHans-Christian Egtvedt 
644ede028fSHans-Christian Egtvedt 	struct snd_pcm_substream	*playback_substream;
654ede028fSHans-Christian Egtvedt 	struct snd_pcm_substream	*capture_substream;
664ede028fSHans-Christian Egtvedt 	struct snd_card			*card;
674ede028fSHans-Christian Egtvedt 	struct snd_pcm			*pcm;
684ede028fSHans-Christian Egtvedt 	struct snd_ac97			*ac97;
694ede028fSHans-Christian Egtvedt 	struct snd_ac97_bus		*ac97_bus;
704ede028fSHans-Christian Egtvedt 
714ede028fSHans-Christian Egtvedt 	u64				cur_format;
724ede028fSHans-Christian Egtvedt 	unsigned int			cur_rate;
734ede028fSHans-Christian Egtvedt 	unsigned long			flags;
747177395fSSedji Gaouaou 	int				playback_period, capture_period;
754ede028fSHans-Christian Egtvedt 	/* Serialize access to opened variable */
764ede028fSHans-Christian Egtvedt 	spinlock_t			lock;
774ede028fSHans-Christian Egtvedt 	void __iomem			*regs;
78df163587SHans-Christian Egtvedt 	int				irq;
794ede028fSHans-Christian Egtvedt 	int				opened;
804ede028fSHans-Christian Egtvedt 	int				reset_pin;
814ede028fSHans-Christian Egtvedt };
824ede028fSHans-Christian Egtvedt 
834ede028fSHans-Christian Egtvedt #define get_chip(card) ((struct atmel_ac97c *)(card)->private_data)
844ede028fSHans-Christian Egtvedt 
854ede028fSHans-Christian Egtvedt #define ac97c_writel(chip, reg, val)			\
864ede028fSHans-Christian Egtvedt 	__raw_writel((val), (chip)->regs + AC97C_##reg)
874ede028fSHans-Christian Egtvedt #define ac97c_readl(chip, reg)				\
884ede028fSHans-Christian Egtvedt 	__raw_readl((chip)->regs + AC97C_##reg)
894ede028fSHans-Christian Egtvedt 
904ede028fSHans-Christian Egtvedt /* This function is called by the DMA driver. */
914ede028fSHans-Christian Egtvedt static void atmel_ac97c_dma_playback_period_done(void *arg)
924ede028fSHans-Christian Egtvedt {
934ede028fSHans-Christian Egtvedt 	struct atmel_ac97c *chip = arg;
944ede028fSHans-Christian Egtvedt 	snd_pcm_period_elapsed(chip->playback_substream);
954ede028fSHans-Christian Egtvedt }
964ede028fSHans-Christian Egtvedt 
974ede028fSHans-Christian Egtvedt static void atmel_ac97c_dma_capture_period_done(void *arg)
984ede028fSHans-Christian Egtvedt {
994ede028fSHans-Christian Egtvedt 	struct atmel_ac97c *chip = arg;
1004ede028fSHans-Christian Egtvedt 	snd_pcm_period_elapsed(chip->capture_substream);
1014ede028fSHans-Christian Egtvedt }
1024ede028fSHans-Christian Egtvedt 
1034ede028fSHans-Christian Egtvedt static int atmel_ac97c_prepare_dma(struct atmel_ac97c *chip,
1044ede028fSHans-Christian Egtvedt 		struct snd_pcm_substream *substream,
1054ede028fSHans-Christian Egtvedt 		enum dma_data_direction direction)
1064ede028fSHans-Christian Egtvedt {
1074ede028fSHans-Christian Egtvedt 	struct dma_chan			*chan;
1084ede028fSHans-Christian Egtvedt 	struct dw_cyclic_desc		*cdesc;
1094ede028fSHans-Christian Egtvedt 	struct snd_pcm_runtime		*runtime = substream->runtime;
1104ede028fSHans-Christian Egtvedt 	unsigned long			buffer_len, period_len;
1114ede028fSHans-Christian Egtvedt 
1124ede028fSHans-Christian Egtvedt 	/*
1134ede028fSHans-Christian Egtvedt 	 * We don't do DMA on "complex" transfers, i.e. with
1144ede028fSHans-Christian Egtvedt 	 * non-halfword-aligned buffers or lengths.
1154ede028fSHans-Christian Egtvedt 	 */
1164ede028fSHans-Christian Egtvedt 	if (runtime->dma_addr & 1 || runtime->buffer_size & 1) {
1174ede028fSHans-Christian Egtvedt 		dev_dbg(&chip->pdev->dev, "too complex transfer\n");
1184ede028fSHans-Christian Egtvedt 		return -EINVAL;
1194ede028fSHans-Christian Egtvedt 	}
1204ede028fSHans-Christian Egtvedt 
1214ede028fSHans-Christian Egtvedt 	if (direction == DMA_TO_DEVICE)
1224ede028fSHans-Christian Egtvedt 		chan = chip->dma.tx_chan;
1234ede028fSHans-Christian Egtvedt 	else
1244ede028fSHans-Christian Egtvedt 		chan = chip->dma.rx_chan;
1254ede028fSHans-Christian Egtvedt 
1264ede028fSHans-Christian Egtvedt 	buffer_len = frames_to_bytes(runtime, runtime->buffer_size);
1274ede028fSHans-Christian Egtvedt 	period_len = frames_to_bytes(runtime, runtime->period_size);
1284ede028fSHans-Christian Egtvedt 
1294ede028fSHans-Christian Egtvedt 	cdesc = dw_dma_cyclic_prep(chan, runtime->dma_addr, buffer_len,
1304ede028fSHans-Christian Egtvedt 			period_len, direction);
1314ede028fSHans-Christian Egtvedt 	if (IS_ERR(cdesc)) {
1324ede028fSHans-Christian Egtvedt 		dev_dbg(&chip->pdev->dev, "could not prepare cyclic DMA\n");
1334ede028fSHans-Christian Egtvedt 		return PTR_ERR(cdesc);
1344ede028fSHans-Christian Egtvedt 	}
1354ede028fSHans-Christian Egtvedt 
1364ede028fSHans-Christian Egtvedt 	if (direction == DMA_TO_DEVICE) {
1374ede028fSHans-Christian Egtvedt 		cdesc->period_callback = atmel_ac97c_dma_playback_period_done;
1384ede028fSHans-Christian Egtvedt 		set_bit(DMA_TX_READY, &chip->flags);
1394ede028fSHans-Christian Egtvedt 	} else {
1404ede028fSHans-Christian Egtvedt 		cdesc->period_callback = atmel_ac97c_dma_capture_period_done;
1414ede028fSHans-Christian Egtvedt 		set_bit(DMA_RX_READY, &chip->flags);
1424ede028fSHans-Christian Egtvedt 	}
1434ede028fSHans-Christian Egtvedt 
1444ede028fSHans-Christian Egtvedt 	cdesc->period_callback_param = chip;
1454ede028fSHans-Christian Egtvedt 
1464ede028fSHans-Christian Egtvedt 	return 0;
1474ede028fSHans-Christian Egtvedt }
1484ede028fSHans-Christian Egtvedt 
1494ede028fSHans-Christian Egtvedt static struct snd_pcm_hardware atmel_ac97c_hw = {
1504ede028fSHans-Christian Egtvedt 	.info			= (SNDRV_PCM_INFO_MMAP
1514ede028fSHans-Christian Egtvedt 				  | SNDRV_PCM_INFO_MMAP_VALID
1524ede028fSHans-Christian Egtvedt 				  | SNDRV_PCM_INFO_INTERLEAVED
1534ede028fSHans-Christian Egtvedt 				  | SNDRV_PCM_INFO_BLOCK_TRANSFER
1544ede028fSHans-Christian Egtvedt 				  | SNDRV_PCM_INFO_JOINT_DUPLEX
1554ede028fSHans-Christian Egtvedt 				  | SNDRV_PCM_INFO_RESUME
1564ede028fSHans-Christian Egtvedt 				  | SNDRV_PCM_INFO_PAUSE),
1574ede028fSHans-Christian Egtvedt 	.formats		= (SNDRV_PCM_FMTBIT_S16_BE
1584ede028fSHans-Christian Egtvedt 				  | SNDRV_PCM_FMTBIT_S16_LE),
1594ede028fSHans-Christian Egtvedt 	.rates			= (SNDRV_PCM_RATE_CONTINUOUS),
1604ede028fSHans-Christian Egtvedt 	.rate_min		= 4000,
1614ede028fSHans-Christian Egtvedt 	.rate_max		= 48000,
1624ede028fSHans-Christian Egtvedt 	.channels_min		= 1,
1634ede028fSHans-Christian Egtvedt 	.channels_max		= 2,
164c42eec0fSHans-Christian Egtvedt 	.buffer_bytes_max	= 2 * 2 * 64 * 2048,
1654ede028fSHans-Christian Egtvedt 	.period_bytes_min	= 4096,
1664ede028fSHans-Christian Egtvedt 	.period_bytes_max	= 4096,
167c42eec0fSHans-Christian Egtvedt 	.periods_min		= 6,
1684ede028fSHans-Christian Egtvedt 	.periods_max		= 64,
1694ede028fSHans-Christian Egtvedt };
1704ede028fSHans-Christian Egtvedt 
1714ede028fSHans-Christian Egtvedt static int atmel_ac97c_playback_open(struct snd_pcm_substream *substream)
1724ede028fSHans-Christian Egtvedt {
1734ede028fSHans-Christian Egtvedt 	struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
1744ede028fSHans-Christian Egtvedt 	struct snd_pcm_runtime *runtime = substream->runtime;
1754ede028fSHans-Christian Egtvedt 
1764ede028fSHans-Christian Egtvedt 	mutex_lock(&opened_mutex);
1774ede028fSHans-Christian Egtvedt 	chip->opened++;
1784ede028fSHans-Christian Egtvedt 	runtime->hw = atmel_ac97c_hw;
1794ede028fSHans-Christian Egtvedt 	if (chip->cur_rate) {
1804ede028fSHans-Christian Egtvedt 		runtime->hw.rate_min = chip->cur_rate;
1814ede028fSHans-Christian Egtvedt 		runtime->hw.rate_max = chip->cur_rate;
1824ede028fSHans-Christian Egtvedt 	}
1834ede028fSHans-Christian Egtvedt 	if (chip->cur_format)
1844ede028fSHans-Christian Egtvedt 		runtime->hw.formats = (1ULL << chip->cur_format);
1854ede028fSHans-Christian Egtvedt 	mutex_unlock(&opened_mutex);
1864ede028fSHans-Christian Egtvedt 	chip->playback_substream = substream;
1874ede028fSHans-Christian Egtvedt 	return 0;
1884ede028fSHans-Christian Egtvedt }
1894ede028fSHans-Christian Egtvedt 
1904ede028fSHans-Christian Egtvedt static int atmel_ac97c_capture_open(struct snd_pcm_substream *substream)
1914ede028fSHans-Christian Egtvedt {
1924ede028fSHans-Christian Egtvedt 	struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
1934ede028fSHans-Christian Egtvedt 	struct snd_pcm_runtime *runtime = substream->runtime;
1944ede028fSHans-Christian Egtvedt 
1954ede028fSHans-Christian Egtvedt 	mutex_lock(&opened_mutex);
1964ede028fSHans-Christian Egtvedt 	chip->opened++;
1974ede028fSHans-Christian Egtvedt 	runtime->hw = atmel_ac97c_hw;
1984ede028fSHans-Christian Egtvedt 	if (chip->cur_rate) {
1994ede028fSHans-Christian Egtvedt 		runtime->hw.rate_min = chip->cur_rate;
2004ede028fSHans-Christian Egtvedt 		runtime->hw.rate_max = chip->cur_rate;
2014ede028fSHans-Christian Egtvedt 	}
2024ede028fSHans-Christian Egtvedt 	if (chip->cur_format)
2034ede028fSHans-Christian Egtvedt 		runtime->hw.formats = (1ULL << chip->cur_format);
2044ede028fSHans-Christian Egtvedt 	mutex_unlock(&opened_mutex);
2054ede028fSHans-Christian Egtvedt 	chip->capture_substream = substream;
2064ede028fSHans-Christian Egtvedt 	return 0;
2074ede028fSHans-Christian Egtvedt }
2084ede028fSHans-Christian Egtvedt 
2094ede028fSHans-Christian Egtvedt static int atmel_ac97c_playback_close(struct snd_pcm_substream *substream)
2104ede028fSHans-Christian Egtvedt {
2114ede028fSHans-Christian Egtvedt 	struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
2124ede028fSHans-Christian Egtvedt 
2134ede028fSHans-Christian Egtvedt 	mutex_lock(&opened_mutex);
2144ede028fSHans-Christian Egtvedt 	chip->opened--;
2154ede028fSHans-Christian Egtvedt 	if (!chip->opened) {
2164ede028fSHans-Christian Egtvedt 		chip->cur_rate = 0;
2174ede028fSHans-Christian Egtvedt 		chip->cur_format = 0;
2184ede028fSHans-Christian Egtvedt 	}
2194ede028fSHans-Christian Egtvedt 	mutex_unlock(&opened_mutex);
2204ede028fSHans-Christian Egtvedt 
2214ede028fSHans-Christian Egtvedt 	chip->playback_substream = NULL;
2224ede028fSHans-Christian Egtvedt 
2234ede028fSHans-Christian Egtvedt 	return 0;
2244ede028fSHans-Christian Egtvedt }
2254ede028fSHans-Christian Egtvedt 
2264ede028fSHans-Christian Egtvedt static int atmel_ac97c_capture_close(struct snd_pcm_substream *substream)
2274ede028fSHans-Christian Egtvedt {
2284ede028fSHans-Christian Egtvedt 	struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
2294ede028fSHans-Christian Egtvedt 
2304ede028fSHans-Christian Egtvedt 	mutex_lock(&opened_mutex);
2314ede028fSHans-Christian Egtvedt 	chip->opened--;
2324ede028fSHans-Christian Egtvedt 	if (!chip->opened) {
2334ede028fSHans-Christian Egtvedt 		chip->cur_rate = 0;
2344ede028fSHans-Christian Egtvedt 		chip->cur_format = 0;
2354ede028fSHans-Christian Egtvedt 	}
2364ede028fSHans-Christian Egtvedt 	mutex_unlock(&opened_mutex);
2374ede028fSHans-Christian Egtvedt 
2384ede028fSHans-Christian Egtvedt 	chip->capture_substream = NULL;
2394ede028fSHans-Christian Egtvedt 
2404ede028fSHans-Christian Egtvedt 	return 0;
2414ede028fSHans-Christian Egtvedt }
2424ede028fSHans-Christian Egtvedt 
2434ede028fSHans-Christian Egtvedt static int atmel_ac97c_playback_hw_params(struct snd_pcm_substream *substream,
2444ede028fSHans-Christian Egtvedt 		struct snd_pcm_hw_params *hw_params)
2454ede028fSHans-Christian Egtvedt {
2464ede028fSHans-Christian Egtvedt 	struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
2474ede028fSHans-Christian Egtvedt 	int retval;
2484ede028fSHans-Christian Egtvedt 
2494ede028fSHans-Christian Egtvedt 	retval = snd_pcm_lib_malloc_pages(substream,
2504ede028fSHans-Christian Egtvedt 					params_buffer_bytes(hw_params));
2514ede028fSHans-Christian Egtvedt 	if (retval < 0)
2524ede028fSHans-Christian Egtvedt 		return retval;
2534ede028fSHans-Christian Egtvedt 	/* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */
2547177395fSSedji Gaouaou 	if (cpu_is_at32ap7000()) {
2557177395fSSedji Gaouaou 		/* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */
2564ede028fSHans-Christian Egtvedt 		if (retval == 1)
2574ede028fSHans-Christian Egtvedt 			if (test_and_clear_bit(DMA_TX_READY, &chip->flags))
2584ede028fSHans-Christian Egtvedt 				dw_dma_cyclic_free(chip->dma.tx_chan);
2597177395fSSedji Gaouaou 	}
2604ede028fSHans-Christian Egtvedt 	/* Set restrictions to params. */
2614ede028fSHans-Christian Egtvedt 	mutex_lock(&opened_mutex);
2624ede028fSHans-Christian Egtvedt 	chip->cur_rate = params_rate(hw_params);
2634ede028fSHans-Christian Egtvedt 	chip->cur_format = params_format(hw_params);
2644ede028fSHans-Christian Egtvedt 	mutex_unlock(&opened_mutex);
2654ede028fSHans-Christian Egtvedt 
2664ede028fSHans-Christian Egtvedt 	return retval;
2674ede028fSHans-Christian Egtvedt }
2684ede028fSHans-Christian Egtvedt 
2694ede028fSHans-Christian Egtvedt static int atmel_ac97c_capture_hw_params(struct snd_pcm_substream *substream,
2704ede028fSHans-Christian Egtvedt 		struct snd_pcm_hw_params *hw_params)
2714ede028fSHans-Christian Egtvedt {
2724ede028fSHans-Christian Egtvedt 	struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
2734ede028fSHans-Christian Egtvedt 	int retval;
2744ede028fSHans-Christian Egtvedt 
2754ede028fSHans-Christian Egtvedt 	retval = snd_pcm_lib_malloc_pages(substream,
2764ede028fSHans-Christian Egtvedt 					params_buffer_bytes(hw_params));
2774ede028fSHans-Christian Egtvedt 	if (retval < 0)
2784ede028fSHans-Christian Egtvedt 		return retval;
2794ede028fSHans-Christian Egtvedt 	/* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */
2807177395fSSedji Gaouaou 	if (cpu_is_at32ap7000()) {
2817177395fSSedji Gaouaou 		if (retval < 0)
2827177395fSSedji Gaouaou 			return retval;
2837177395fSSedji Gaouaou 		/* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */
2844ede028fSHans-Christian Egtvedt 		if (retval == 1)
2854ede028fSHans-Christian Egtvedt 			if (test_and_clear_bit(DMA_RX_READY, &chip->flags))
2864ede028fSHans-Christian Egtvedt 				dw_dma_cyclic_free(chip->dma.rx_chan);
2877177395fSSedji Gaouaou 	}
2884ede028fSHans-Christian Egtvedt 
2894ede028fSHans-Christian Egtvedt 	/* Set restrictions to params. */
2904ede028fSHans-Christian Egtvedt 	mutex_lock(&opened_mutex);
2914ede028fSHans-Christian Egtvedt 	chip->cur_rate = params_rate(hw_params);
2924ede028fSHans-Christian Egtvedt 	chip->cur_format = params_format(hw_params);
2934ede028fSHans-Christian Egtvedt 	mutex_unlock(&opened_mutex);
2944ede028fSHans-Christian Egtvedt 
2954ede028fSHans-Christian Egtvedt 	return retval;
2964ede028fSHans-Christian Egtvedt }
2974ede028fSHans-Christian Egtvedt 
2984ede028fSHans-Christian Egtvedt static int atmel_ac97c_playback_hw_free(struct snd_pcm_substream *substream)
2994ede028fSHans-Christian Egtvedt {
3004ede028fSHans-Christian Egtvedt 	struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
3017177395fSSedji Gaouaou 	if (cpu_is_at32ap7000()) {
3024ede028fSHans-Christian Egtvedt 		if (test_and_clear_bit(DMA_TX_READY, &chip->flags))
3034ede028fSHans-Christian Egtvedt 			dw_dma_cyclic_free(chip->dma.tx_chan);
3047177395fSSedji Gaouaou 	}
3054ede028fSHans-Christian Egtvedt 	return snd_pcm_lib_free_pages(substream);
3064ede028fSHans-Christian Egtvedt }
3074ede028fSHans-Christian Egtvedt 
3084ede028fSHans-Christian Egtvedt static int atmel_ac97c_capture_hw_free(struct snd_pcm_substream *substream)
3094ede028fSHans-Christian Egtvedt {
3104ede028fSHans-Christian Egtvedt 	struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
3117177395fSSedji Gaouaou 	if (cpu_is_at32ap7000()) {
3124ede028fSHans-Christian Egtvedt 		if (test_and_clear_bit(DMA_RX_READY, &chip->flags))
3134ede028fSHans-Christian Egtvedt 			dw_dma_cyclic_free(chip->dma.rx_chan);
3147177395fSSedji Gaouaou 	}
3154ede028fSHans-Christian Egtvedt 	return snd_pcm_lib_free_pages(substream);
3164ede028fSHans-Christian Egtvedt }
3174ede028fSHans-Christian Egtvedt 
3184ede028fSHans-Christian Egtvedt static int atmel_ac97c_playback_prepare(struct snd_pcm_substream *substream)
3194ede028fSHans-Christian Egtvedt {
3204ede028fSHans-Christian Egtvedt 	struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
3214ede028fSHans-Christian Egtvedt 	struct snd_pcm_runtime *runtime = substream->runtime;
3227177395fSSedji Gaouaou 	int block_size = frames_to_bytes(runtime, runtime->period_size);
323128ed6a9SHans-Christian Egtvedt 	unsigned long word = ac97c_readl(chip, OCA);
3244ede028fSHans-Christian Egtvedt 	int retval;
3254ede028fSHans-Christian Egtvedt 
3267177395fSSedji Gaouaou 	chip->playback_period = 0;
327128ed6a9SHans-Christian Egtvedt 	word &= ~(AC97C_CH_MASK(PCM_LEFT) | AC97C_CH_MASK(PCM_RIGHT));
328128ed6a9SHans-Christian Egtvedt 
3294ede028fSHans-Christian Egtvedt 	/* assign channels to AC97C channel A */
3304ede028fSHans-Christian Egtvedt 	switch (runtime->channels) {
3314ede028fSHans-Christian Egtvedt 	case 1:
3324ede028fSHans-Christian Egtvedt 		word |= AC97C_CH_ASSIGN(PCM_LEFT, A);
3334ede028fSHans-Christian Egtvedt 		break;
3344ede028fSHans-Christian Egtvedt 	case 2:
3354ede028fSHans-Christian Egtvedt 		word |= AC97C_CH_ASSIGN(PCM_LEFT, A)
3364ede028fSHans-Christian Egtvedt 			| AC97C_CH_ASSIGN(PCM_RIGHT, A);
3374ede028fSHans-Christian Egtvedt 		break;
3384ede028fSHans-Christian Egtvedt 	default:
3394ede028fSHans-Christian Egtvedt 		/* TODO: support more than two channels */
3404ede028fSHans-Christian Egtvedt 		return -EINVAL;
3414ede028fSHans-Christian Egtvedt 	}
3424ede028fSHans-Christian Egtvedt 	ac97c_writel(chip, OCA, word);
3434ede028fSHans-Christian Egtvedt 
3444ede028fSHans-Christian Egtvedt 	/* configure sample format and size */
345ec2755a9SSedji Gaouaou 	word = ac97c_readl(chip, CAMR);
346ec2755a9SSedji Gaouaou 	if (chip->opened <= 1)
3474ede028fSHans-Christian Egtvedt 		word = AC97C_CMR_DMAEN | AC97C_CMR_SIZE_16;
348ec2755a9SSedji Gaouaou 	else
349ec2755a9SSedji Gaouaou 		word |= AC97C_CMR_DMAEN | AC97C_CMR_SIZE_16;
3504ede028fSHans-Christian Egtvedt 
3514ede028fSHans-Christian Egtvedt 	switch (runtime->format) {
3524ede028fSHans-Christian Egtvedt 	case SNDRV_PCM_FORMAT_S16_LE:
3537177395fSSedji Gaouaou 		if (cpu_is_at32ap7000())
3544ede028fSHans-Christian Egtvedt 			word |= AC97C_CMR_CEM_LITTLE;
3554ede028fSHans-Christian Egtvedt 		break;
3564ede028fSHans-Christian Egtvedt 	case SNDRV_PCM_FORMAT_S16_BE: /* fall through */
3574ede028fSHans-Christian Egtvedt 		word &= ~(AC97C_CMR_CEM_LITTLE);
3584ede028fSHans-Christian Egtvedt 		break;
359128ed6a9SHans-Christian Egtvedt 	default:
360128ed6a9SHans-Christian Egtvedt 		word = ac97c_readl(chip, OCA);
361128ed6a9SHans-Christian Egtvedt 		word &= ~(AC97C_CH_MASK(PCM_LEFT) | AC97C_CH_MASK(PCM_RIGHT));
362128ed6a9SHans-Christian Egtvedt 		ac97c_writel(chip, OCA, word);
363128ed6a9SHans-Christian Egtvedt 		return -EINVAL;
3644ede028fSHans-Christian Egtvedt 	}
3654ede028fSHans-Christian Egtvedt 
366df163587SHans-Christian Egtvedt 	/* Enable underrun interrupt on channel A */
367df163587SHans-Christian Egtvedt 	word |= AC97C_CSR_UNRUN;
368df163587SHans-Christian Egtvedt 
3694ede028fSHans-Christian Egtvedt 	ac97c_writel(chip, CAMR, word);
3704ede028fSHans-Christian Egtvedt 
371df163587SHans-Christian Egtvedt 	/* Enable channel A event interrupt */
372df163587SHans-Christian Egtvedt 	word = ac97c_readl(chip, IMR);
373df163587SHans-Christian Egtvedt 	word |= AC97C_SR_CAEVT;
374df163587SHans-Christian Egtvedt 	ac97c_writel(chip, IER, word);
375df163587SHans-Christian Egtvedt 
3764ede028fSHans-Christian Egtvedt 	/* set variable rate if needed */
3774ede028fSHans-Christian Egtvedt 	if (runtime->rate != 48000) {
3784ede028fSHans-Christian Egtvedt 		word = ac97c_readl(chip, MR);
3794ede028fSHans-Christian Egtvedt 		word |= AC97C_MR_VRA;
3804ede028fSHans-Christian Egtvedt 		ac97c_writel(chip, MR, word);
3814ede028fSHans-Christian Egtvedt 	} else {
3824ede028fSHans-Christian Egtvedt 		word = ac97c_readl(chip, MR);
3834ede028fSHans-Christian Egtvedt 		word &= ~(AC97C_MR_VRA);
3844ede028fSHans-Christian Egtvedt 		ac97c_writel(chip, MR, word);
3854ede028fSHans-Christian Egtvedt 	}
3864ede028fSHans-Christian Egtvedt 
3874ede028fSHans-Christian Egtvedt 	retval = snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE,
3884ede028fSHans-Christian Egtvedt 			runtime->rate);
3894ede028fSHans-Christian Egtvedt 	if (retval)
3904ede028fSHans-Christian Egtvedt 		dev_dbg(&chip->pdev->dev, "could not set rate %d Hz\n",
3914ede028fSHans-Christian Egtvedt 				runtime->rate);
3924ede028fSHans-Christian Egtvedt 
3937177395fSSedji Gaouaou 	if (cpu_is_at32ap7000()) {
3944ede028fSHans-Christian Egtvedt 		if (!test_bit(DMA_TX_READY, &chip->flags))
3954ede028fSHans-Christian Egtvedt 			retval = atmel_ac97c_prepare_dma(chip, substream,
3964ede028fSHans-Christian Egtvedt 					DMA_TO_DEVICE);
3977177395fSSedji Gaouaou 	} else {
3987177395fSSedji Gaouaou 		/* Initialize and start the PDC */
3997177395fSSedji Gaouaou 		writel(runtime->dma_addr, chip->regs + ATMEL_PDC_TPR);
4007177395fSSedji Gaouaou 		writel(block_size / 2, chip->regs + ATMEL_PDC_TCR);
4017177395fSSedji Gaouaou 		writel(runtime->dma_addr + block_size,
4027177395fSSedji Gaouaou 				chip->regs + ATMEL_PDC_TNPR);
4037177395fSSedji Gaouaou 		writel(block_size / 2, chip->regs + ATMEL_PDC_TNCR);
4047177395fSSedji Gaouaou 	}
4054ede028fSHans-Christian Egtvedt 
4064ede028fSHans-Christian Egtvedt 	return retval;
4074ede028fSHans-Christian Egtvedt }
4084ede028fSHans-Christian Egtvedt 
4094ede028fSHans-Christian Egtvedt static int atmel_ac97c_capture_prepare(struct snd_pcm_substream *substream)
4104ede028fSHans-Christian Egtvedt {
4114ede028fSHans-Christian Egtvedt 	struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
4124ede028fSHans-Christian Egtvedt 	struct snd_pcm_runtime *runtime = substream->runtime;
4137177395fSSedji Gaouaou 	int block_size = frames_to_bytes(runtime, runtime->period_size);
414128ed6a9SHans-Christian Egtvedt 	unsigned long word = ac97c_readl(chip, ICA);
4154ede028fSHans-Christian Egtvedt 	int retval;
4164ede028fSHans-Christian Egtvedt 
4177177395fSSedji Gaouaou 	chip->capture_period = 0;
418128ed6a9SHans-Christian Egtvedt 	word &= ~(AC97C_CH_MASK(PCM_LEFT) | AC97C_CH_MASK(PCM_RIGHT));
419128ed6a9SHans-Christian Egtvedt 
4204ede028fSHans-Christian Egtvedt 	/* assign channels to AC97C channel A */
4214ede028fSHans-Christian Egtvedt 	switch (runtime->channels) {
4224ede028fSHans-Christian Egtvedt 	case 1:
4234ede028fSHans-Christian Egtvedt 		word |= AC97C_CH_ASSIGN(PCM_LEFT, A);
4244ede028fSHans-Christian Egtvedt 		break;
4254ede028fSHans-Christian Egtvedt 	case 2:
4264ede028fSHans-Christian Egtvedt 		word |= AC97C_CH_ASSIGN(PCM_LEFT, A)
4274ede028fSHans-Christian Egtvedt 			| AC97C_CH_ASSIGN(PCM_RIGHT, A);
4284ede028fSHans-Christian Egtvedt 		break;
4294ede028fSHans-Christian Egtvedt 	default:
4304ede028fSHans-Christian Egtvedt 		/* TODO: support more than two channels */
4314ede028fSHans-Christian Egtvedt 		return -EINVAL;
4324ede028fSHans-Christian Egtvedt 	}
4334ede028fSHans-Christian Egtvedt 	ac97c_writel(chip, ICA, word);
4344ede028fSHans-Christian Egtvedt 
4354ede028fSHans-Christian Egtvedt 	/* configure sample format and size */
436ec2755a9SSedji Gaouaou 	word = ac97c_readl(chip, CAMR);
437ec2755a9SSedji Gaouaou 	if (chip->opened <= 1)
4384ede028fSHans-Christian Egtvedt 		word = AC97C_CMR_DMAEN | AC97C_CMR_SIZE_16;
439ec2755a9SSedji Gaouaou 	else
440ec2755a9SSedji Gaouaou 		word |= AC97C_CMR_DMAEN | AC97C_CMR_SIZE_16;
4414ede028fSHans-Christian Egtvedt 
4424ede028fSHans-Christian Egtvedt 	switch (runtime->format) {
4434ede028fSHans-Christian Egtvedt 	case SNDRV_PCM_FORMAT_S16_LE:
4447177395fSSedji Gaouaou 		if (cpu_is_at32ap7000())
4454ede028fSHans-Christian Egtvedt 			word |= AC97C_CMR_CEM_LITTLE;
4464ede028fSHans-Christian Egtvedt 		break;
4474ede028fSHans-Christian Egtvedt 	case SNDRV_PCM_FORMAT_S16_BE: /* fall through */
4484ede028fSHans-Christian Egtvedt 		word &= ~(AC97C_CMR_CEM_LITTLE);
4494ede028fSHans-Christian Egtvedt 		break;
450128ed6a9SHans-Christian Egtvedt 	default:
451128ed6a9SHans-Christian Egtvedt 		word = ac97c_readl(chip, ICA);
452128ed6a9SHans-Christian Egtvedt 		word &= ~(AC97C_CH_MASK(PCM_LEFT) | AC97C_CH_MASK(PCM_RIGHT));
453128ed6a9SHans-Christian Egtvedt 		ac97c_writel(chip, ICA, word);
454128ed6a9SHans-Christian Egtvedt 		return -EINVAL;
4554ede028fSHans-Christian Egtvedt 	}
4564ede028fSHans-Christian Egtvedt 
457df163587SHans-Christian Egtvedt 	/* Enable overrun interrupt on channel A */
458df163587SHans-Christian Egtvedt 	word |= AC97C_CSR_OVRUN;
459df163587SHans-Christian Egtvedt 
4604ede028fSHans-Christian Egtvedt 	ac97c_writel(chip, CAMR, word);
4614ede028fSHans-Christian Egtvedt 
462df163587SHans-Christian Egtvedt 	/* Enable channel A event interrupt */
463df163587SHans-Christian Egtvedt 	word = ac97c_readl(chip, IMR);
464df163587SHans-Christian Egtvedt 	word |= AC97C_SR_CAEVT;
465df163587SHans-Christian Egtvedt 	ac97c_writel(chip, IER, word);
466df163587SHans-Christian Egtvedt 
4674ede028fSHans-Christian Egtvedt 	/* set variable rate if needed */
4684ede028fSHans-Christian Egtvedt 	if (runtime->rate != 48000) {
4694ede028fSHans-Christian Egtvedt 		word = ac97c_readl(chip, MR);
4704ede028fSHans-Christian Egtvedt 		word |= AC97C_MR_VRA;
4714ede028fSHans-Christian Egtvedt 		ac97c_writel(chip, MR, word);
4724ede028fSHans-Christian Egtvedt 	} else {
4734ede028fSHans-Christian Egtvedt 		word = ac97c_readl(chip, MR);
4744ede028fSHans-Christian Egtvedt 		word &= ~(AC97C_MR_VRA);
4754ede028fSHans-Christian Egtvedt 		ac97c_writel(chip, MR, word);
4764ede028fSHans-Christian Egtvedt 	}
4774ede028fSHans-Christian Egtvedt 
4784ede028fSHans-Christian Egtvedt 	retval = snd_ac97_set_rate(chip->ac97, AC97_PCM_LR_ADC_RATE,
4794ede028fSHans-Christian Egtvedt 			runtime->rate);
4804ede028fSHans-Christian Egtvedt 	if (retval)
4814ede028fSHans-Christian Egtvedt 		dev_dbg(&chip->pdev->dev, "could not set rate %d Hz\n",
4824ede028fSHans-Christian Egtvedt 				runtime->rate);
4834ede028fSHans-Christian Egtvedt 
4847177395fSSedji Gaouaou 	if (cpu_is_at32ap7000()) {
4854ede028fSHans-Christian Egtvedt 		if (!test_bit(DMA_RX_READY, &chip->flags))
4864ede028fSHans-Christian Egtvedt 			retval = atmel_ac97c_prepare_dma(chip, substream,
4874ede028fSHans-Christian Egtvedt 					DMA_FROM_DEVICE);
4887177395fSSedji Gaouaou 	} else {
4897177395fSSedji Gaouaou 		/* Initialize and start the PDC */
4907177395fSSedji Gaouaou 		writel(runtime->dma_addr, chip->regs + ATMEL_PDC_RPR);
4917177395fSSedji Gaouaou 		writel(block_size / 2, chip->regs + ATMEL_PDC_RCR);
4927177395fSSedji Gaouaou 		writel(runtime->dma_addr + block_size,
4937177395fSSedji Gaouaou 				chip->regs + ATMEL_PDC_RNPR);
4947177395fSSedji Gaouaou 		writel(block_size / 2, chip->regs + ATMEL_PDC_RNCR);
4957177395fSSedji Gaouaou 	}
4964ede028fSHans-Christian Egtvedt 
4974ede028fSHans-Christian Egtvedt 	return retval;
4984ede028fSHans-Christian Egtvedt }
4994ede028fSHans-Christian Egtvedt 
5004ede028fSHans-Christian Egtvedt static int
5014ede028fSHans-Christian Egtvedt atmel_ac97c_playback_trigger(struct snd_pcm_substream *substream, int cmd)
5024ede028fSHans-Christian Egtvedt {
5034ede028fSHans-Christian Egtvedt 	struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
5047177395fSSedji Gaouaou 	unsigned long camr, ptcr = 0;
5054ede028fSHans-Christian Egtvedt 	int retval = 0;
5064ede028fSHans-Christian Egtvedt 
5074ede028fSHans-Christian Egtvedt 	camr = ac97c_readl(chip, CAMR);
5084ede028fSHans-Christian Egtvedt 
5094ede028fSHans-Christian Egtvedt 	switch (cmd) {
5104ede028fSHans-Christian Egtvedt 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */
5114ede028fSHans-Christian Egtvedt 	case SNDRV_PCM_TRIGGER_RESUME: /* fall through */
5124ede028fSHans-Christian Egtvedt 	case SNDRV_PCM_TRIGGER_START:
5137177395fSSedji Gaouaou 		if (cpu_is_at32ap7000()) {
5144ede028fSHans-Christian Egtvedt 			retval = dw_dma_cyclic_start(chip->dma.tx_chan);
5154ede028fSHans-Christian Egtvedt 			if (retval)
5164ede028fSHans-Christian Egtvedt 				goto out;
5177177395fSSedji Gaouaou 		} else {
5187177395fSSedji Gaouaou 			ptcr = ATMEL_PDC_TXTEN;
5197177395fSSedji Gaouaou 		}
520ec2755a9SSedji Gaouaou 		camr |= AC97C_CMR_CENA | AC97C_CSR_ENDTX;
5214ede028fSHans-Christian Egtvedt 		break;
5224ede028fSHans-Christian Egtvedt 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */
5234ede028fSHans-Christian Egtvedt 	case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */
5244ede028fSHans-Christian Egtvedt 	case SNDRV_PCM_TRIGGER_STOP:
5257177395fSSedji Gaouaou 		if (cpu_is_at32ap7000())
5264ede028fSHans-Christian Egtvedt 			dw_dma_cyclic_stop(chip->dma.tx_chan);
5277177395fSSedji Gaouaou 		else
5287177395fSSedji Gaouaou 			ptcr |= ATMEL_PDC_TXTDIS;
5294ede028fSHans-Christian Egtvedt 		if (chip->opened <= 1)
5304ede028fSHans-Christian Egtvedt 			camr &= ~AC97C_CMR_CENA;
5314ede028fSHans-Christian Egtvedt 		break;
5324ede028fSHans-Christian Egtvedt 	default:
5334ede028fSHans-Christian Egtvedt 		retval = -EINVAL;
5344ede028fSHans-Christian Egtvedt 		goto out;
5354ede028fSHans-Christian Egtvedt 	}
5364ede028fSHans-Christian Egtvedt 
5374ede028fSHans-Christian Egtvedt 	ac97c_writel(chip, CAMR, camr);
5387177395fSSedji Gaouaou 	if (!cpu_is_at32ap7000())
5397177395fSSedji Gaouaou 		writel(ptcr, chip->regs + ATMEL_PDC_PTCR);
5404ede028fSHans-Christian Egtvedt out:
5414ede028fSHans-Christian Egtvedt 	return retval;
5424ede028fSHans-Christian Egtvedt }
5434ede028fSHans-Christian Egtvedt 
5444ede028fSHans-Christian Egtvedt static int
5454ede028fSHans-Christian Egtvedt atmel_ac97c_capture_trigger(struct snd_pcm_substream *substream, int cmd)
5464ede028fSHans-Christian Egtvedt {
5474ede028fSHans-Christian Egtvedt 	struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
5487177395fSSedji Gaouaou 	unsigned long camr, ptcr = 0;
5494ede028fSHans-Christian Egtvedt 	int retval = 0;
5504ede028fSHans-Christian Egtvedt 
5514ede028fSHans-Christian Egtvedt 	camr = ac97c_readl(chip, CAMR);
5527177395fSSedji Gaouaou 	ptcr = readl(chip->regs + ATMEL_PDC_PTSR);
5534ede028fSHans-Christian Egtvedt 
5544ede028fSHans-Christian Egtvedt 	switch (cmd) {
5554ede028fSHans-Christian Egtvedt 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */
5564ede028fSHans-Christian Egtvedt 	case SNDRV_PCM_TRIGGER_RESUME: /* fall through */
5574ede028fSHans-Christian Egtvedt 	case SNDRV_PCM_TRIGGER_START:
5587177395fSSedji Gaouaou 		if (cpu_is_at32ap7000()) {
5594ede028fSHans-Christian Egtvedt 			retval = dw_dma_cyclic_start(chip->dma.rx_chan);
5604ede028fSHans-Christian Egtvedt 			if (retval)
5614ede028fSHans-Christian Egtvedt 				goto out;
5627177395fSSedji Gaouaou 		} else {
5637177395fSSedji Gaouaou 			ptcr = ATMEL_PDC_RXTEN;
5647177395fSSedji Gaouaou 		}
565ec2755a9SSedji Gaouaou 		camr |= AC97C_CMR_CENA | AC97C_CSR_ENDRX;
5664ede028fSHans-Christian Egtvedt 		break;
5674ede028fSHans-Christian Egtvedt 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */
5684ede028fSHans-Christian Egtvedt 	case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */
5694ede028fSHans-Christian Egtvedt 	case SNDRV_PCM_TRIGGER_STOP:
5707177395fSSedji Gaouaou 		if (cpu_is_at32ap7000())
5714ede028fSHans-Christian Egtvedt 			dw_dma_cyclic_stop(chip->dma.rx_chan);
5727177395fSSedji Gaouaou 		else
5737177395fSSedji Gaouaou 			ptcr |= (ATMEL_PDC_RXTDIS);
5744ede028fSHans-Christian Egtvedt 		if (chip->opened <= 1)
5754ede028fSHans-Christian Egtvedt 			camr &= ~AC97C_CMR_CENA;
5764ede028fSHans-Christian Egtvedt 		break;
5774ede028fSHans-Christian Egtvedt 	default:
5784ede028fSHans-Christian Egtvedt 		retval = -EINVAL;
5794ede028fSHans-Christian Egtvedt 		break;
5804ede028fSHans-Christian Egtvedt 	}
5814ede028fSHans-Christian Egtvedt 
5824ede028fSHans-Christian Egtvedt 	ac97c_writel(chip, CAMR, camr);
5837177395fSSedji Gaouaou 	if (!cpu_is_at32ap7000())
5847177395fSSedji Gaouaou 		writel(ptcr, chip->regs + ATMEL_PDC_PTCR);
5854ede028fSHans-Christian Egtvedt out:
5864ede028fSHans-Christian Egtvedt 	return retval;
5874ede028fSHans-Christian Egtvedt }
5884ede028fSHans-Christian Egtvedt 
5894ede028fSHans-Christian Egtvedt static snd_pcm_uframes_t
5904ede028fSHans-Christian Egtvedt atmel_ac97c_playback_pointer(struct snd_pcm_substream *substream)
5914ede028fSHans-Christian Egtvedt {
5924ede028fSHans-Christian Egtvedt 	struct atmel_ac97c	*chip = snd_pcm_substream_chip(substream);
5934ede028fSHans-Christian Egtvedt 	struct snd_pcm_runtime	*runtime = substream->runtime;
5944ede028fSHans-Christian Egtvedt 	snd_pcm_uframes_t	frames;
5954ede028fSHans-Christian Egtvedt 	unsigned long		bytes;
5964ede028fSHans-Christian Egtvedt 
5977177395fSSedji Gaouaou 	if (cpu_is_at32ap7000())
5984ede028fSHans-Christian Egtvedt 		bytes = dw_dma_get_src_addr(chip->dma.tx_chan);
5997177395fSSedji Gaouaou 	else
6007177395fSSedji Gaouaou 		bytes = readl(chip->regs + ATMEL_PDC_TPR);
6014ede028fSHans-Christian Egtvedt 	bytes -= runtime->dma_addr;
6024ede028fSHans-Christian Egtvedt 
6034ede028fSHans-Christian Egtvedt 	frames = bytes_to_frames(runtime, bytes);
6044ede028fSHans-Christian Egtvedt 	if (frames >= runtime->buffer_size)
6054ede028fSHans-Christian Egtvedt 		frames -= runtime->buffer_size;
6064ede028fSHans-Christian Egtvedt 	return frames;
6074ede028fSHans-Christian Egtvedt }
6084ede028fSHans-Christian Egtvedt 
6094ede028fSHans-Christian Egtvedt static snd_pcm_uframes_t
6104ede028fSHans-Christian Egtvedt atmel_ac97c_capture_pointer(struct snd_pcm_substream *substream)
6114ede028fSHans-Christian Egtvedt {
6124ede028fSHans-Christian Egtvedt 	struct atmel_ac97c	*chip = snd_pcm_substream_chip(substream);
6134ede028fSHans-Christian Egtvedt 	struct snd_pcm_runtime	*runtime = substream->runtime;
6144ede028fSHans-Christian Egtvedt 	snd_pcm_uframes_t	frames;
6154ede028fSHans-Christian Egtvedt 	unsigned long		bytes;
6164ede028fSHans-Christian Egtvedt 
6177177395fSSedji Gaouaou 	if (cpu_is_at32ap7000())
6184ede028fSHans-Christian Egtvedt 		bytes = dw_dma_get_dst_addr(chip->dma.rx_chan);
6197177395fSSedji Gaouaou 	else
6207177395fSSedji Gaouaou 		bytes = readl(chip->regs + ATMEL_PDC_RPR);
6214ede028fSHans-Christian Egtvedt 	bytes -= runtime->dma_addr;
6224ede028fSHans-Christian Egtvedt 
6234ede028fSHans-Christian Egtvedt 	frames = bytes_to_frames(runtime, bytes);
6244ede028fSHans-Christian Egtvedt 	if (frames >= runtime->buffer_size)
6254ede028fSHans-Christian Egtvedt 		frames -= runtime->buffer_size;
6264ede028fSHans-Christian Egtvedt 	return frames;
6274ede028fSHans-Christian Egtvedt }
6284ede028fSHans-Christian Egtvedt 
6294ede028fSHans-Christian Egtvedt static struct snd_pcm_ops atmel_ac97_playback_ops = {
6304ede028fSHans-Christian Egtvedt 	.open		= atmel_ac97c_playback_open,
6314ede028fSHans-Christian Egtvedt 	.close		= atmel_ac97c_playback_close,
6324ede028fSHans-Christian Egtvedt 	.ioctl		= snd_pcm_lib_ioctl,
6334ede028fSHans-Christian Egtvedt 	.hw_params	= atmel_ac97c_playback_hw_params,
6344ede028fSHans-Christian Egtvedt 	.hw_free	= atmel_ac97c_playback_hw_free,
6354ede028fSHans-Christian Egtvedt 	.prepare	= atmel_ac97c_playback_prepare,
6364ede028fSHans-Christian Egtvedt 	.trigger	= atmel_ac97c_playback_trigger,
6374ede028fSHans-Christian Egtvedt 	.pointer	= atmel_ac97c_playback_pointer,
6384ede028fSHans-Christian Egtvedt };
6394ede028fSHans-Christian Egtvedt 
6404ede028fSHans-Christian Egtvedt static struct snd_pcm_ops atmel_ac97_capture_ops = {
6414ede028fSHans-Christian Egtvedt 	.open		= atmel_ac97c_capture_open,
6424ede028fSHans-Christian Egtvedt 	.close		= atmel_ac97c_capture_close,
6434ede028fSHans-Christian Egtvedt 	.ioctl		= snd_pcm_lib_ioctl,
6444ede028fSHans-Christian Egtvedt 	.hw_params	= atmel_ac97c_capture_hw_params,
6454ede028fSHans-Christian Egtvedt 	.hw_free	= atmel_ac97c_capture_hw_free,
6464ede028fSHans-Christian Egtvedt 	.prepare	= atmel_ac97c_capture_prepare,
6474ede028fSHans-Christian Egtvedt 	.trigger	= atmel_ac97c_capture_trigger,
6484ede028fSHans-Christian Egtvedt 	.pointer	= atmel_ac97c_capture_pointer,
6494ede028fSHans-Christian Egtvedt };
6504ede028fSHans-Christian Egtvedt 
651df163587SHans-Christian Egtvedt static irqreturn_t atmel_ac97c_interrupt(int irq, void *dev)
652df163587SHans-Christian Egtvedt {
653df163587SHans-Christian Egtvedt 	struct atmel_ac97c	*chip  = (struct atmel_ac97c *)dev;
654df163587SHans-Christian Egtvedt 	irqreturn_t		retval = IRQ_NONE;
655df163587SHans-Christian Egtvedt 	u32			sr     = ac97c_readl(chip, SR);
656df163587SHans-Christian Egtvedt 	u32			casr   = ac97c_readl(chip, CASR);
657df163587SHans-Christian Egtvedt 	u32			cosr   = ac97c_readl(chip, COSR);
6587177395fSSedji Gaouaou 	u32			camr   = ac97c_readl(chip, CAMR);
659df163587SHans-Christian Egtvedt 
660df163587SHans-Christian Egtvedt 	if (sr & AC97C_SR_CAEVT) {
6617177395fSSedji Gaouaou 		struct snd_pcm_runtime *runtime;
6627177395fSSedji Gaouaou 		int offset, next_period, block_size;
663f5341163SYegor Yefremov 		dev_dbg(&chip->pdev->dev, "channel A event%s%s%s%s%s%s\n",
664df163587SHans-Christian Egtvedt 				casr & AC97C_CSR_OVRUN   ? " OVRUN"   : "",
665df163587SHans-Christian Egtvedt 				casr & AC97C_CSR_RXRDY   ? " RXRDY"   : "",
666df163587SHans-Christian Egtvedt 				casr & AC97C_CSR_UNRUN   ? " UNRUN"   : "",
667df163587SHans-Christian Egtvedt 				casr & AC97C_CSR_TXEMPTY ? " TXEMPTY" : "",
668df163587SHans-Christian Egtvedt 				casr & AC97C_CSR_TXRDY   ? " TXRDY"   : "",
669df163587SHans-Christian Egtvedt 				!casr                    ? " NONE"    : "");
6707177395fSSedji Gaouaou 		if (!cpu_is_at32ap7000()) {
6717177395fSSedji Gaouaou 			if ((casr & camr) & AC97C_CSR_ENDTX) {
6727177395fSSedji Gaouaou 				runtime = chip->playback_substream->runtime;
6737177395fSSedji Gaouaou 				block_size = frames_to_bytes(runtime,
6747177395fSSedji Gaouaou 						runtime->period_size);
6757177395fSSedji Gaouaou 				chip->playback_period++;
6767177395fSSedji Gaouaou 
6777177395fSSedji Gaouaou 				if (chip->playback_period == runtime->periods)
6787177395fSSedji Gaouaou 					chip->playback_period = 0;
6797177395fSSedji Gaouaou 				next_period = chip->playback_period + 1;
6807177395fSSedji Gaouaou 				if (next_period == runtime->periods)
6817177395fSSedji Gaouaou 					next_period = 0;
6827177395fSSedji Gaouaou 
6837177395fSSedji Gaouaou 				offset = block_size * next_period;
6847177395fSSedji Gaouaou 
6857177395fSSedji Gaouaou 				writel(runtime->dma_addr + offset,
6867177395fSSedji Gaouaou 						chip->regs + ATMEL_PDC_TNPR);
6877177395fSSedji Gaouaou 				writel(block_size / 2,
6887177395fSSedji Gaouaou 						chip->regs + ATMEL_PDC_TNCR);
6897177395fSSedji Gaouaou 
6907177395fSSedji Gaouaou 				snd_pcm_period_elapsed(
6917177395fSSedji Gaouaou 						chip->playback_substream);
6927177395fSSedji Gaouaou 			}
6937177395fSSedji Gaouaou 			if ((casr & camr) & AC97C_CSR_ENDRX) {
6947177395fSSedji Gaouaou 				runtime = chip->capture_substream->runtime;
6957177395fSSedji Gaouaou 				block_size = frames_to_bytes(runtime,
6967177395fSSedji Gaouaou 						runtime->period_size);
6977177395fSSedji Gaouaou 				chip->capture_period++;
6987177395fSSedji Gaouaou 
6997177395fSSedji Gaouaou 				if (chip->capture_period == runtime->periods)
7007177395fSSedji Gaouaou 					chip->capture_period = 0;
7017177395fSSedji Gaouaou 				next_period = chip->capture_period + 1;
7027177395fSSedji Gaouaou 				if (next_period == runtime->periods)
7037177395fSSedji Gaouaou 					next_period = 0;
7047177395fSSedji Gaouaou 
7057177395fSSedji Gaouaou 				offset = block_size * next_period;
7067177395fSSedji Gaouaou 
7077177395fSSedji Gaouaou 				writel(runtime->dma_addr + offset,
7087177395fSSedji Gaouaou 						chip->regs + ATMEL_PDC_RNPR);
7097177395fSSedji Gaouaou 				writel(block_size / 2,
7107177395fSSedji Gaouaou 						chip->regs + ATMEL_PDC_RNCR);
7117177395fSSedji Gaouaou 				snd_pcm_period_elapsed(chip->capture_substream);
7127177395fSSedji Gaouaou 			}
7137177395fSSedji Gaouaou 		}
714df163587SHans-Christian Egtvedt 		retval = IRQ_HANDLED;
715df163587SHans-Christian Egtvedt 	}
716df163587SHans-Christian Egtvedt 
717df163587SHans-Christian Egtvedt 	if (sr & AC97C_SR_COEVT) {
718df163587SHans-Christian Egtvedt 		dev_info(&chip->pdev->dev, "codec channel event%s%s%s%s%s\n",
719df163587SHans-Christian Egtvedt 				cosr & AC97C_CSR_OVRUN   ? " OVRUN"   : "",
720df163587SHans-Christian Egtvedt 				cosr & AC97C_CSR_RXRDY   ? " RXRDY"   : "",
721df163587SHans-Christian Egtvedt 				cosr & AC97C_CSR_TXEMPTY ? " TXEMPTY" : "",
722df163587SHans-Christian Egtvedt 				cosr & AC97C_CSR_TXRDY   ? " TXRDY"   : "",
723df163587SHans-Christian Egtvedt 				!cosr                    ? " NONE"    : "");
724df163587SHans-Christian Egtvedt 		retval = IRQ_HANDLED;
725df163587SHans-Christian Egtvedt 	}
726df163587SHans-Christian Egtvedt 
727df163587SHans-Christian Egtvedt 	if (retval == IRQ_NONE) {
728df163587SHans-Christian Egtvedt 		dev_err(&chip->pdev->dev, "spurious interrupt sr 0x%08x "
729df163587SHans-Christian Egtvedt 				"casr 0x%08x cosr 0x%08x\n", sr, casr, cosr);
730df163587SHans-Christian Egtvedt 	}
731df163587SHans-Christian Egtvedt 
732df163587SHans-Christian Egtvedt 	return retval;
733df163587SHans-Christian Egtvedt }
734df163587SHans-Christian Egtvedt 
7357177395fSSedji Gaouaou static struct ac97_pcm at91_ac97_pcm_defs[] __devinitdata = {
7367177395fSSedji Gaouaou 	/* Playback */
7377177395fSSedji Gaouaou 	{
7387177395fSSedji Gaouaou 		.exclusive = 1,
7397177395fSSedji Gaouaou 		.r = { {
7407177395fSSedji Gaouaou 			.slots = ((1 << AC97_SLOT_PCM_LEFT)
7417177395fSSedji Gaouaou 				  | (1 << AC97_SLOT_PCM_RIGHT)),
7427177395fSSedji Gaouaou 		} },
7437177395fSSedji Gaouaou 	},
7447177395fSSedji Gaouaou 	/* PCM in */
7457177395fSSedji Gaouaou 	{
7467177395fSSedji Gaouaou 		.stream = 1,
7477177395fSSedji Gaouaou 		.exclusive = 1,
7487177395fSSedji Gaouaou 		.r = { {
7497177395fSSedji Gaouaou 			.slots = ((1 << AC97_SLOT_PCM_LEFT)
7507177395fSSedji Gaouaou 					| (1 << AC97_SLOT_PCM_RIGHT)),
7517177395fSSedji Gaouaou 		} }
7527177395fSSedji Gaouaou 	},
7537177395fSSedji Gaouaou 	/* Mic in */
7547177395fSSedji Gaouaou 	{
7557177395fSSedji Gaouaou 		.stream = 1,
7567177395fSSedji Gaouaou 		.exclusive = 1,
7577177395fSSedji Gaouaou 		.r = { {
7587177395fSSedji Gaouaou 			.slots = (1<<AC97_SLOT_MIC),
7597177395fSSedji Gaouaou 		} }
7607177395fSSedji Gaouaou 	},
7617177395fSSedji Gaouaou };
7627177395fSSedji Gaouaou 
7634ede028fSHans-Christian Egtvedt static int __devinit atmel_ac97c_pcm_new(struct atmel_ac97c *chip)
7644ede028fSHans-Christian Egtvedt {
7654ede028fSHans-Christian Egtvedt 	struct snd_pcm		*pcm;
7664ede028fSHans-Christian Egtvedt 	struct snd_pcm_hardware	hw = atmel_ac97c_hw;
7677177395fSSedji Gaouaou 	int			capture, playback, retval, err;
7684ede028fSHans-Christian Egtvedt 
7694ede028fSHans-Christian Egtvedt 	capture = test_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
7704ede028fSHans-Christian Egtvedt 	playback = test_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
7714ede028fSHans-Christian Egtvedt 
7727177395fSSedji Gaouaou 	if (!cpu_is_at32ap7000()) {
7737177395fSSedji Gaouaou 		err = snd_ac97_pcm_assign(chip->ac97_bus,
7747177395fSSedji Gaouaou 				ARRAY_SIZE(at91_ac97_pcm_defs),
7757177395fSSedji Gaouaou 				at91_ac97_pcm_defs);
7767177395fSSedji Gaouaou 		if (err)
7777177395fSSedji Gaouaou 			return err;
7787177395fSSedji Gaouaou 	}
7794ede028fSHans-Christian Egtvedt 	retval = snd_pcm_new(chip->card, chip->card->shortname,
7804ede028fSHans-Christian Egtvedt 			chip->pdev->id, playback, capture, &pcm);
7814ede028fSHans-Christian Egtvedt 	if (retval)
7824ede028fSHans-Christian Egtvedt 		return retval;
7834ede028fSHans-Christian Egtvedt 
7844ede028fSHans-Christian Egtvedt 	if (capture)
7854ede028fSHans-Christian Egtvedt 		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
7864ede028fSHans-Christian Egtvedt 				&atmel_ac97_capture_ops);
7874ede028fSHans-Christian Egtvedt 	if (playback)
7884ede028fSHans-Christian Egtvedt 		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
7894ede028fSHans-Christian Egtvedt 				&atmel_ac97_playback_ops);
7904ede028fSHans-Christian Egtvedt 
7914ede028fSHans-Christian Egtvedt 	retval = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
7924ede028fSHans-Christian Egtvedt 			&chip->pdev->dev, hw.periods_min * hw.period_bytes_min,
7934ede028fSHans-Christian Egtvedt 			hw.buffer_bytes_max);
7944ede028fSHans-Christian Egtvedt 	if (retval)
7954ede028fSHans-Christian Egtvedt 		return retval;
7964ede028fSHans-Christian Egtvedt 
7974ede028fSHans-Christian Egtvedt 	pcm->private_data = chip;
7984ede028fSHans-Christian Egtvedt 	pcm->info_flags = 0;
7994ede028fSHans-Christian Egtvedt 	strcpy(pcm->name, chip->card->shortname);
8004ede028fSHans-Christian Egtvedt 	chip->pcm = pcm;
8014ede028fSHans-Christian Egtvedt 
8024ede028fSHans-Christian Egtvedt 	return 0;
8034ede028fSHans-Christian Egtvedt }
8044ede028fSHans-Christian Egtvedt 
8054ede028fSHans-Christian Egtvedt static int atmel_ac97c_mixer_new(struct atmel_ac97c *chip)
8064ede028fSHans-Christian Egtvedt {
8074ede028fSHans-Christian Egtvedt 	struct snd_ac97_template template;
8084ede028fSHans-Christian Egtvedt 	memset(&template, 0, sizeof(template));
8094ede028fSHans-Christian Egtvedt 	template.private_data = chip;
8104ede028fSHans-Christian Egtvedt 	return snd_ac97_mixer(chip->ac97_bus, &template, &chip->ac97);
8114ede028fSHans-Christian Egtvedt }
8124ede028fSHans-Christian Egtvedt 
8134ede028fSHans-Christian Egtvedt static void atmel_ac97c_write(struct snd_ac97 *ac97, unsigned short reg,
8144ede028fSHans-Christian Egtvedt 		unsigned short val)
8154ede028fSHans-Christian Egtvedt {
8164ede028fSHans-Christian Egtvedt 	struct atmel_ac97c *chip = get_chip(ac97);
8174ede028fSHans-Christian Egtvedt 	unsigned long word;
8184ede028fSHans-Christian Egtvedt 	int timeout = 40;
8194ede028fSHans-Christian Egtvedt 
8204ede028fSHans-Christian Egtvedt 	word = (reg & 0x7f) << 16 | val;
8214ede028fSHans-Christian Egtvedt 
8224ede028fSHans-Christian Egtvedt 	do {
8234ede028fSHans-Christian Egtvedt 		if (ac97c_readl(chip, COSR) & AC97C_CSR_TXRDY) {
8244ede028fSHans-Christian Egtvedt 			ac97c_writel(chip, COTHR, word);
8254ede028fSHans-Christian Egtvedt 			return;
8264ede028fSHans-Christian Egtvedt 		}
8274ede028fSHans-Christian Egtvedt 		udelay(1);
8284ede028fSHans-Christian Egtvedt 	} while (--timeout);
8294ede028fSHans-Christian Egtvedt 
8304ede028fSHans-Christian Egtvedt 	dev_dbg(&chip->pdev->dev, "codec write timeout\n");
8314ede028fSHans-Christian Egtvedt }
8324ede028fSHans-Christian Egtvedt 
8334ede028fSHans-Christian Egtvedt static unsigned short atmel_ac97c_read(struct snd_ac97 *ac97,
8344ede028fSHans-Christian Egtvedt 		unsigned short reg)
8354ede028fSHans-Christian Egtvedt {
8364ede028fSHans-Christian Egtvedt 	struct atmel_ac97c *chip = get_chip(ac97);
8374ede028fSHans-Christian Egtvedt 	unsigned long word;
8384ede028fSHans-Christian Egtvedt 	int timeout = 40;
8394ede028fSHans-Christian Egtvedt 	int write = 10;
8404ede028fSHans-Christian Egtvedt 
8414ede028fSHans-Christian Egtvedt 	word = (0x80 | (reg & 0x7f)) << 16;
8424ede028fSHans-Christian Egtvedt 
8434ede028fSHans-Christian Egtvedt 	if ((ac97c_readl(chip, COSR) & AC97C_CSR_RXRDY) != 0)
8444ede028fSHans-Christian Egtvedt 		ac97c_readl(chip, CORHR);
8454ede028fSHans-Christian Egtvedt 
8464ede028fSHans-Christian Egtvedt retry_write:
8474ede028fSHans-Christian Egtvedt 	timeout = 40;
8484ede028fSHans-Christian Egtvedt 
8494ede028fSHans-Christian Egtvedt 	do {
8504ede028fSHans-Christian Egtvedt 		if ((ac97c_readl(chip, COSR) & AC97C_CSR_TXRDY) != 0) {
8514ede028fSHans-Christian Egtvedt 			ac97c_writel(chip, COTHR, word);
8524ede028fSHans-Christian Egtvedt 			goto read_reg;
8534ede028fSHans-Christian Egtvedt 		}
8544ede028fSHans-Christian Egtvedt 		udelay(10);
8554ede028fSHans-Christian Egtvedt 	} while (--timeout);
8564ede028fSHans-Christian Egtvedt 
8574ede028fSHans-Christian Egtvedt 	if (!--write)
8584ede028fSHans-Christian Egtvedt 		goto timed_out;
8594ede028fSHans-Christian Egtvedt 	goto retry_write;
8604ede028fSHans-Christian Egtvedt 
8614ede028fSHans-Christian Egtvedt read_reg:
8624ede028fSHans-Christian Egtvedt 	do {
8634ede028fSHans-Christian Egtvedt 		if ((ac97c_readl(chip, COSR) & AC97C_CSR_RXRDY) != 0) {
8644ede028fSHans-Christian Egtvedt 			unsigned short val = ac97c_readl(chip, CORHR);
8654ede028fSHans-Christian Egtvedt 			return val;
8664ede028fSHans-Christian Egtvedt 		}
8674ede028fSHans-Christian Egtvedt 		udelay(10);
8684ede028fSHans-Christian Egtvedt 	} while (--timeout);
8694ede028fSHans-Christian Egtvedt 
8704ede028fSHans-Christian Egtvedt 	if (!--write)
8714ede028fSHans-Christian Egtvedt 		goto timed_out;
8724ede028fSHans-Christian Egtvedt 	goto retry_write;
8734ede028fSHans-Christian Egtvedt 
8744ede028fSHans-Christian Egtvedt timed_out:
8754ede028fSHans-Christian Egtvedt 	dev_dbg(&chip->pdev->dev, "codec read timeout\n");
8764ede028fSHans-Christian Egtvedt 	return 0xffff;
8774ede028fSHans-Christian Egtvedt }
8784ede028fSHans-Christian Egtvedt 
8794ede028fSHans-Christian Egtvedt static bool filter(struct dma_chan *chan, void *slave)
8804ede028fSHans-Christian Egtvedt {
8814ede028fSHans-Christian Egtvedt 	struct dw_dma_slave *dws = slave;
8824ede028fSHans-Christian Egtvedt 
8834ede028fSHans-Christian Egtvedt 	if (dws->dma_dev == chan->device->dev) {
8844ede028fSHans-Christian Egtvedt 		chan->private = dws;
8854ede028fSHans-Christian Egtvedt 		return true;
8864ede028fSHans-Christian Egtvedt 	} else
8874ede028fSHans-Christian Egtvedt 		return false;
8884ede028fSHans-Christian Egtvedt }
8894ede028fSHans-Christian Egtvedt 
8904ede028fSHans-Christian Egtvedt static void atmel_ac97c_reset(struct atmel_ac97c *chip)
8914ede028fSHans-Christian Egtvedt {
89281baf3a7SHans-Christian Egtvedt 	ac97c_writel(chip, MR,   0);
89381baf3a7SHans-Christian Egtvedt 	ac97c_writel(chip, MR,   AC97C_MR_ENA);
89481baf3a7SHans-Christian Egtvedt 	ac97c_writel(chip, CAMR, 0);
89581baf3a7SHans-Christian Egtvedt 	ac97c_writel(chip, COMR, 0);
8964ede028fSHans-Christian Egtvedt 
8974ede028fSHans-Christian Egtvedt 	if (gpio_is_valid(chip->reset_pin)) {
8984ede028fSHans-Christian Egtvedt 		gpio_set_value(chip->reset_pin, 0);
8994ede028fSHans-Christian Egtvedt 		/* AC97 v2.2 specifications says minimum 1 us. */
90081baf3a7SHans-Christian Egtvedt 		udelay(2);
9014ede028fSHans-Christian Egtvedt 		gpio_set_value(chip->reset_pin, 1);
9028015e3deSBo Shen 	} else {
9038015e3deSBo Shen 		ac97c_writel(chip, MR, AC97C_MR_WRST | AC97C_MR_ENA);
9048015e3deSBo Shen 		udelay(2);
9058015e3deSBo Shen 		ac97c_writel(chip, MR, AC97C_MR_ENA);
9064ede028fSHans-Christian Egtvedt 	}
9074ede028fSHans-Christian Egtvedt }
9084ede028fSHans-Christian Egtvedt 
9094ede028fSHans-Christian Egtvedt static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
9104ede028fSHans-Christian Egtvedt {
9114ede028fSHans-Christian Egtvedt 	struct snd_card			*card;
9124ede028fSHans-Christian Egtvedt 	struct atmel_ac97c		*chip;
9134ede028fSHans-Christian Egtvedt 	struct resource			*regs;
9144ede028fSHans-Christian Egtvedt 	struct ac97c_platform_data	*pdata;
9154ede028fSHans-Christian Egtvedt 	struct clk			*pclk;
9164ede028fSHans-Christian Egtvedt 	static struct snd_ac97_bus_ops	ops = {
9174ede028fSHans-Christian Egtvedt 		.write	= atmel_ac97c_write,
9184ede028fSHans-Christian Egtvedt 		.read	= atmel_ac97c_read,
9194ede028fSHans-Christian Egtvedt 	};
9204ede028fSHans-Christian Egtvedt 	int				retval;
921df163587SHans-Christian Egtvedt 	int				irq;
9224ede028fSHans-Christian Egtvedt 
9234ede028fSHans-Christian Egtvedt 	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
9244ede028fSHans-Christian Egtvedt 	if (!regs) {
9254ede028fSHans-Christian Egtvedt 		dev_dbg(&pdev->dev, "no memory resource\n");
9264ede028fSHans-Christian Egtvedt 		return -ENXIO;
9274ede028fSHans-Christian Egtvedt 	}
9284ede028fSHans-Christian Egtvedt 
9294ede028fSHans-Christian Egtvedt 	pdata = pdev->dev.platform_data;
9304ede028fSHans-Christian Egtvedt 	if (!pdata) {
9314ede028fSHans-Christian Egtvedt 		dev_dbg(&pdev->dev, "no platform data\n");
9324ede028fSHans-Christian Egtvedt 		return -ENXIO;
9334ede028fSHans-Christian Egtvedt 	}
9344ede028fSHans-Christian Egtvedt 
935df163587SHans-Christian Egtvedt 	irq = platform_get_irq(pdev, 0);
936df163587SHans-Christian Egtvedt 	if (irq < 0) {
937df163587SHans-Christian Egtvedt 		dev_dbg(&pdev->dev, "could not get irq\n");
938df163587SHans-Christian Egtvedt 		return -ENXIO;
939df163587SHans-Christian Egtvedt 	}
940df163587SHans-Christian Egtvedt 
9417177395fSSedji Gaouaou 	if (cpu_is_at32ap7000()) {
9424ede028fSHans-Christian Egtvedt 		pclk = clk_get(&pdev->dev, "pclk");
9437177395fSSedji Gaouaou 	} else {
9447177395fSSedji Gaouaou 		pclk = clk_get(&pdev->dev, "ac97_clk");
9457177395fSSedji Gaouaou 	}
9467177395fSSedji Gaouaou 
9474ede028fSHans-Christian Egtvedt 	if (IS_ERR(pclk)) {
9484ede028fSHans-Christian Egtvedt 		dev_dbg(&pdev->dev, "no peripheral clock\n");
9494ede028fSHans-Christian Egtvedt 		return PTR_ERR(pclk);
9504ede028fSHans-Christian Egtvedt 	}
9514ede028fSHans-Christian Egtvedt 	clk_enable(pclk);
9524ede028fSHans-Christian Egtvedt 
9534ede028fSHans-Christian Egtvedt 	retval = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
9544ede028fSHans-Christian Egtvedt 			THIS_MODULE, sizeof(struct atmel_ac97c), &card);
9554ede028fSHans-Christian Egtvedt 	if (retval) {
9564ede028fSHans-Christian Egtvedt 		dev_dbg(&pdev->dev, "could not create sound card device\n");
9574ede028fSHans-Christian Egtvedt 		goto err_snd_card_new;
9584ede028fSHans-Christian Egtvedt 	}
9594ede028fSHans-Christian Egtvedt 
9604ede028fSHans-Christian Egtvedt 	chip = get_chip(card);
9614ede028fSHans-Christian Egtvedt 
962df163587SHans-Christian Egtvedt 	retval = request_irq(irq, atmel_ac97c_interrupt, 0, "AC97C", chip);
963df163587SHans-Christian Egtvedt 	if (retval) {
964df163587SHans-Christian Egtvedt 		dev_dbg(&pdev->dev, "unable to request irq %d\n", irq);
965df163587SHans-Christian Egtvedt 		goto err_request_irq;
966df163587SHans-Christian Egtvedt 	}
967df163587SHans-Christian Egtvedt 	chip->irq = irq;
968df163587SHans-Christian Egtvedt 
9694ede028fSHans-Christian Egtvedt 	spin_lock_init(&chip->lock);
9704ede028fSHans-Christian Egtvedt 
9714ede028fSHans-Christian Egtvedt 	strcpy(card->driver, "Atmel AC97C");
9724ede028fSHans-Christian Egtvedt 	strcpy(card->shortname, "Atmel AC97C");
9734ede028fSHans-Christian Egtvedt 	sprintf(card->longname, "Atmel AC97 controller");
9744ede028fSHans-Christian Egtvedt 
9754ede028fSHans-Christian Egtvedt 	chip->card = card;
9764ede028fSHans-Christian Egtvedt 	chip->pclk = pclk;
9774ede028fSHans-Christian Egtvedt 	chip->pdev = pdev;
97828f65c11SJoe Perches 	chip->regs = ioremap(regs->start, resource_size(regs));
9794ede028fSHans-Christian Egtvedt 
9804ede028fSHans-Christian Egtvedt 	if (!chip->regs) {
9814ede028fSHans-Christian Egtvedt 		dev_dbg(&pdev->dev, "could not remap register memory\n");
9824ede028fSHans-Christian Egtvedt 		goto err_ioremap;
9834ede028fSHans-Christian Egtvedt 	}
9844ede028fSHans-Christian Egtvedt 
9854ede028fSHans-Christian Egtvedt 	if (gpio_is_valid(pdata->reset_pin)) {
9864ede028fSHans-Christian Egtvedt 		if (gpio_request(pdata->reset_pin, "reset_pin")) {
9874ede028fSHans-Christian Egtvedt 			dev_dbg(&pdev->dev, "reset pin not available\n");
9884ede028fSHans-Christian Egtvedt 			chip->reset_pin = -ENODEV;
9894ede028fSHans-Christian Egtvedt 		} else {
9904ede028fSHans-Christian Egtvedt 			gpio_direction_output(pdata->reset_pin, 1);
9914ede028fSHans-Christian Egtvedt 			chip->reset_pin = pdata->reset_pin;
9924ede028fSHans-Christian Egtvedt 		}
9934ede028fSHans-Christian Egtvedt 	}
9944ede028fSHans-Christian Egtvedt 
9954ede028fSHans-Christian Egtvedt 	snd_card_set_dev(card, &pdev->dev);
9964ede028fSHans-Christian Egtvedt 
99781baf3a7SHans-Christian Egtvedt 	atmel_ac97c_reset(chip);
99881baf3a7SHans-Christian Egtvedt 
999df163587SHans-Christian Egtvedt 	/* Enable overrun interrupt from codec channel */
1000df163587SHans-Christian Egtvedt 	ac97c_writel(chip, COMR, AC97C_CSR_OVRUN);
1001df163587SHans-Christian Egtvedt 	ac97c_writel(chip, IER, ac97c_readl(chip, IMR) | AC97C_SR_COEVT);
1002df163587SHans-Christian Egtvedt 
10034ede028fSHans-Christian Egtvedt 	retval = snd_ac97_bus(card, 0, &ops, chip, &chip->ac97_bus);
10044ede028fSHans-Christian Egtvedt 	if (retval) {
10054ede028fSHans-Christian Egtvedt 		dev_dbg(&pdev->dev, "could not register on ac97 bus\n");
10064ede028fSHans-Christian Egtvedt 		goto err_ac97_bus;
10074ede028fSHans-Christian Egtvedt 	}
10084ede028fSHans-Christian Egtvedt 
10094ede028fSHans-Christian Egtvedt 	retval = atmel_ac97c_mixer_new(chip);
10104ede028fSHans-Christian Egtvedt 	if (retval) {
10114ede028fSHans-Christian Egtvedt 		dev_dbg(&pdev->dev, "could not register ac97 mixer\n");
10124ede028fSHans-Christian Egtvedt 		goto err_ac97_bus;
10134ede028fSHans-Christian Egtvedt 	}
10144ede028fSHans-Christian Egtvedt 
10157177395fSSedji Gaouaou 	if (cpu_is_at32ap7000()) {
10164ede028fSHans-Christian Egtvedt 		if (pdata->rx_dws.dma_dev) {
10174ede028fSHans-Christian Egtvedt 			struct dw_dma_slave *dws = &pdata->rx_dws;
10184ede028fSHans-Christian Egtvedt 			dma_cap_mask_t mask;
10194ede028fSHans-Christian Egtvedt 
10204ede028fSHans-Christian Egtvedt 			dws->rx_reg = regs->start + AC97C_CARHR + 2;
10214ede028fSHans-Christian Egtvedt 
10224ede028fSHans-Christian Egtvedt 			dma_cap_zero(mask);
10234ede028fSHans-Christian Egtvedt 			dma_cap_set(DMA_SLAVE, mask);
10244ede028fSHans-Christian Egtvedt 
10257177395fSSedji Gaouaou 			chip->dma.rx_chan = dma_request_channel(mask, filter,
10267177395fSSedji Gaouaou 								dws);
10274ede028fSHans-Christian Egtvedt 
10284ede028fSHans-Christian Egtvedt 			dev_info(&chip->pdev->dev, "using %s for DMA RX\n",
102923572856SHans-Christian Egtvedt 				dev_name(&chip->dma.rx_chan->dev->device));
10304ede028fSHans-Christian Egtvedt 			set_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
10314ede028fSHans-Christian Egtvedt 		}
10324ede028fSHans-Christian Egtvedt 
10334ede028fSHans-Christian Egtvedt 		if (pdata->tx_dws.dma_dev) {
10344ede028fSHans-Christian Egtvedt 			struct dw_dma_slave *dws = &pdata->tx_dws;
10354ede028fSHans-Christian Egtvedt 			dma_cap_mask_t mask;
10364ede028fSHans-Christian Egtvedt 
10374ede028fSHans-Christian Egtvedt 			dws->tx_reg = regs->start + AC97C_CATHR + 2;
10384ede028fSHans-Christian Egtvedt 
10394ede028fSHans-Christian Egtvedt 			dma_cap_zero(mask);
10404ede028fSHans-Christian Egtvedt 			dma_cap_set(DMA_SLAVE, mask);
10414ede028fSHans-Christian Egtvedt 
10427177395fSSedji Gaouaou 			chip->dma.tx_chan = dma_request_channel(mask, filter,
10437177395fSSedji Gaouaou 								dws);
10444ede028fSHans-Christian Egtvedt 
10454ede028fSHans-Christian Egtvedt 			dev_info(&chip->pdev->dev, "using %s for DMA TX\n",
104623572856SHans-Christian Egtvedt 				dev_name(&chip->dma.tx_chan->dev->device));
10474ede028fSHans-Christian Egtvedt 			set_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
10484ede028fSHans-Christian Egtvedt 		}
10494ede028fSHans-Christian Egtvedt 
10504ede028fSHans-Christian Egtvedt 		if (!test_bit(DMA_RX_CHAN_PRESENT, &chip->flags) &&
10514ede028fSHans-Christian Egtvedt 				!test_bit(DMA_TX_CHAN_PRESENT, &chip->flags)) {
10524ede028fSHans-Christian Egtvedt 			dev_dbg(&pdev->dev, "DMA not available\n");
10534ede028fSHans-Christian Egtvedt 			retval = -ENODEV;
10544ede028fSHans-Christian Egtvedt 			goto err_dma;
10554ede028fSHans-Christian Egtvedt 		}
10567177395fSSedji Gaouaou 	} else {
10577177395fSSedji Gaouaou 		/* Just pretend that we have DMA channel(for at91 i is actually
10587177395fSSedji Gaouaou 		 * the PDC) */
10597177395fSSedji Gaouaou 		set_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
10607177395fSSedji Gaouaou 		set_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
10617177395fSSedji Gaouaou 	}
10624ede028fSHans-Christian Egtvedt 
10634ede028fSHans-Christian Egtvedt 	retval = atmel_ac97c_pcm_new(chip);
10644ede028fSHans-Christian Egtvedt 	if (retval) {
10654ede028fSHans-Christian Egtvedt 		dev_dbg(&pdev->dev, "could not register ac97 pcm device\n");
10664ede028fSHans-Christian Egtvedt 		goto err_dma;
10674ede028fSHans-Christian Egtvedt 	}
10684ede028fSHans-Christian Egtvedt 
10694ede028fSHans-Christian Egtvedt 	retval = snd_card_register(card);
10704ede028fSHans-Christian Egtvedt 	if (retval) {
10714ede028fSHans-Christian Egtvedt 		dev_dbg(&pdev->dev, "could not register sound card\n");
1072df163587SHans-Christian Egtvedt 		goto err_dma;
10734ede028fSHans-Christian Egtvedt 	}
10744ede028fSHans-Christian Egtvedt 
10754ede028fSHans-Christian Egtvedt 	platform_set_drvdata(pdev, card);
10764ede028fSHans-Christian Egtvedt 
10777177395fSSedji Gaouaou 	dev_info(&pdev->dev, "Atmel AC97 controller at 0x%p, irq = %d\n",
10787177395fSSedji Gaouaou 			chip->regs, irq);
10794ede028fSHans-Christian Egtvedt 
10804ede028fSHans-Christian Egtvedt 	return 0;
10814ede028fSHans-Christian Egtvedt 
10824ede028fSHans-Christian Egtvedt err_dma:
10837177395fSSedji Gaouaou 	if (cpu_is_at32ap7000()) {
10844ede028fSHans-Christian Egtvedt 		if (test_bit(DMA_RX_CHAN_PRESENT, &chip->flags))
10854ede028fSHans-Christian Egtvedt 			dma_release_channel(chip->dma.rx_chan);
10864ede028fSHans-Christian Egtvedt 		if (test_bit(DMA_TX_CHAN_PRESENT, &chip->flags))
10874ede028fSHans-Christian Egtvedt 			dma_release_channel(chip->dma.tx_chan);
10884ede028fSHans-Christian Egtvedt 		clear_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
10894ede028fSHans-Christian Egtvedt 		clear_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
10904ede028fSHans-Christian Egtvedt 		chip->dma.rx_chan = NULL;
10914ede028fSHans-Christian Egtvedt 		chip->dma.tx_chan = NULL;
10927177395fSSedji Gaouaou 	}
10934ede028fSHans-Christian Egtvedt err_ac97_bus:
10944ede028fSHans-Christian Egtvedt 	snd_card_set_dev(card, NULL);
10954ede028fSHans-Christian Egtvedt 
10964ede028fSHans-Christian Egtvedt 	if (gpio_is_valid(chip->reset_pin))
10974ede028fSHans-Christian Egtvedt 		gpio_free(chip->reset_pin);
10984ede028fSHans-Christian Egtvedt 
10994ede028fSHans-Christian Egtvedt 	iounmap(chip->regs);
11004ede028fSHans-Christian Egtvedt err_ioremap:
1101df163587SHans-Christian Egtvedt 	free_irq(irq, chip);
1102df163587SHans-Christian Egtvedt err_request_irq:
11034ede028fSHans-Christian Egtvedt 	snd_card_free(card);
11044ede028fSHans-Christian Egtvedt err_snd_card_new:
11054ede028fSHans-Christian Egtvedt 	clk_disable(pclk);
11064ede028fSHans-Christian Egtvedt 	clk_put(pclk);
11074ede028fSHans-Christian Egtvedt 	return retval;
11084ede028fSHans-Christian Egtvedt }
11094ede028fSHans-Christian Egtvedt 
11104ede028fSHans-Christian Egtvedt #ifdef CONFIG_PM
11114ede028fSHans-Christian Egtvedt static int atmel_ac97c_suspend(struct platform_device *pdev, pm_message_t msg)
11124ede028fSHans-Christian Egtvedt {
11134ede028fSHans-Christian Egtvedt 	struct snd_card *card = platform_get_drvdata(pdev);
11144ede028fSHans-Christian Egtvedt 	struct atmel_ac97c *chip = card->private_data;
11154ede028fSHans-Christian Egtvedt 
11167177395fSSedji Gaouaou 	if (cpu_is_at32ap7000()) {
11174ede028fSHans-Christian Egtvedt 		if (test_bit(DMA_RX_READY, &chip->flags))
11184ede028fSHans-Christian Egtvedt 			dw_dma_cyclic_stop(chip->dma.rx_chan);
11194ede028fSHans-Christian Egtvedt 		if (test_bit(DMA_TX_READY, &chip->flags))
11204ede028fSHans-Christian Egtvedt 			dw_dma_cyclic_stop(chip->dma.tx_chan);
11217177395fSSedji Gaouaou 	}
11224ede028fSHans-Christian Egtvedt 	clk_disable(chip->pclk);
11234ede028fSHans-Christian Egtvedt 
11244ede028fSHans-Christian Egtvedt 	return 0;
11254ede028fSHans-Christian Egtvedt }
11264ede028fSHans-Christian Egtvedt 
11274ede028fSHans-Christian Egtvedt static int atmel_ac97c_resume(struct platform_device *pdev)
11284ede028fSHans-Christian Egtvedt {
11294ede028fSHans-Christian Egtvedt 	struct snd_card *card = platform_get_drvdata(pdev);
11304ede028fSHans-Christian Egtvedt 	struct atmel_ac97c *chip = card->private_data;
11314ede028fSHans-Christian Egtvedt 
11324ede028fSHans-Christian Egtvedt 	clk_enable(chip->pclk);
11337177395fSSedji Gaouaou 	if (cpu_is_at32ap7000()) {
11344ede028fSHans-Christian Egtvedt 		if (test_bit(DMA_RX_READY, &chip->flags))
11354ede028fSHans-Christian Egtvedt 			dw_dma_cyclic_start(chip->dma.rx_chan);
11364ede028fSHans-Christian Egtvedt 		if (test_bit(DMA_TX_READY, &chip->flags))
11374ede028fSHans-Christian Egtvedt 			dw_dma_cyclic_start(chip->dma.tx_chan);
11387177395fSSedji Gaouaou 	}
11394ede028fSHans-Christian Egtvedt 	return 0;
11404ede028fSHans-Christian Egtvedt }
11414ede028fSHans-Christian Egtvedt #else
11424ede028fSHans-Christian Egtvedt #define atmel_ac97c_suspend NULL
11434ede028fSHans-Christian Egtvedt #define atmel_ac97c_resume NULL
11444ede028fSHans-Christian Egtvedt #endif
11454ede028fSHans-Christian Egtvedt 
11464ede028fSHans-Christian Egtvedt static int __devexit atmel_ac97c_remove(struct platform_device *pdev)
11474ede028fSHans-Christian Egtvedt {
11484ede028fSHans-Christian Egtvedt 	struct snd_card *card = platform_get_drvdata(pdev);
11494ede028fSHans-Christian Egtvedt 	struct atmel_ac97c *chip = get_chip(card);
11504ede028fSHans-Christian Egtvedt 
11514ede028fSHans-Christian Egtvedt 	if (gpio_is_valid(chip->reset_pin))
11524ede028fSHans-Christian Egtvedt 		gpio_free(chip->reset_pin);
11534ede028fSHans-Christian Egtvedt 
1154bd74a184SHans-Christian Egtvedt 	ac97c_writel(chip, CAMR, 0);
1155bd74a184SHans-Christian Egtvedt 	ac97c_writel(chip, COMR, 0);
1156bd74a184SHans-Christian Egtvedt 	ac97c_writel(chip, MR,   0);
1157bd74a184SHans-Christian Egtvedt 
11584ede028fSHans-Christian Egtvedt 	clk_disable(chip->pclk);
11594ede028fSHans-Christian Egtvedt 	clk_put(chip->pclk);
11604ede028fSHans-Christian Egtvedt 	iounmap(chip->regs);
1161df163587SHans-Christian Egtvedt 	free_irq(chip->irq, chip);
11624ede028fSHans-Christian Egtvedt 
11637177395fSSedji Gaouaou 	if (cpu_is_at32ap7000()) {
11644ede028fSHans-Christian Egtvedt 		if (test_bit(DMA_RX_CHAN_PRESENT, &chip->flags))
11654ede028fSHans-Christian Egtvedt 			dma_release_channel(chip->dma.rx_chan);
11664ede028fSHans-Christian Egtvedt 		if (test_bit(DMA_TX_CHAN_PRESENT, &chip->flags))
11674ede028fSHans-Christian Egtvedt 			dma_release_channel(chip->dma.tx_chan);
11684ede028fSHans-Christian Egtvedt 		clear_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
11694ede028fSHans-Christian Egtvedt 		clear_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
11704ede028fSHans-Christian Egtvedt 		chip->dma.rx_chan = NULL;
11714ede028fSHans-Christian Egtvedt 		chip->dma.tx_chan = NULL;
11727177395fSSedji Gaouaou 	}
11734ede028fSHans-Christian Egtvedt 
11744ede028fSHans-Christian Egtvedt 	snd_card_set_dev(card, NULL);
11754ede028fSHans-Christian Egtvedt 	snd_card_free(card);
11764ede028fSHans-Christian Egtvedt 
11774ede028fSHans-Christian Egtvedt 	platform_set_drvdata(pdev, NULL);
11784ede028fSHans-Christian Egtvedt 
11794ede028fSHans-Christian Egtvedt 	return 0;
11804ede028fSHans-Christian Egtvedt }
11814ede028fSHans-Christian Egtvedt 
11824ede028fSHans-Christian Egtvedt static struct platform_driver atmel_ac97c_driver = {
11834ede028fSHans-Christian Egtvedt 	.remove		= __devexit_p(atmel_ac97c_remove),
11844ede028fSHans-Christian Egtvedt 	.driver		= {
11854ede028fSHans-Christian Egtvedt 		.name	= "atmel_ac97c",
11864ede028fSHans-Christian Egtvedt 	},
11874ede028fSHans-Christian Egtvedt 	.suspend	= atmel_ac97c_suspend,
11884ede028fSHans-Christian Egtvedt 	.resume		= atmel_ac97c_resume,
11894ede028fSHans-Christian Egtvedt };
11904ede028fSHans-Christian Egtvedt 
11914ede028fSHans-Christian Egtvedt static int __init atmel_ac97c_init(void)
11924ede028fSHans-Christian Egtvedt {
11934ede028fSHans-Christian Egtvedt 	return platform_driver_probe(&atmel_ac97c_driver,
11944ede028fSHans-Christian Egtvedt 			atmel_ac97c_probe);
11954ede028fSHans-Christian Egtvedt }
11964ede028fSHans-Christian Egtvedt module_init(atmel_ac97c_init);
11974ede028fSHans-Christian Egtvedt 
11984ede028fSHans-Christian Egtvedt static void __exit atmel_ac97c_exit(void)
11994ede028fSHans-Christian Egtvedt {
12004ede028fSHans-Christian Egtvedt 	platform_driver_unregister(&atmel_ac97c_driver);
12014ede028fSHans-Christian Egtvedt }
12024ede028fSHans-Christian Egtvedt module_exit(atmel_ac97c_exit);
12034ede028fSHans-Christian Egtvedt 
12044ede028fSHans-Christian Egtvedt MODULE_LICENSE("GPL");
12054ede028fSHans-Christian Egtvedt MODULE_DESCRIPTION("Driver for Atmel AC97 controller");
12060cfae7c9SHans-Christian Egtvedt MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>");
1207