Commit 4ede028f8716523fc31e0d3d01b81405613dfb8f
Committed by
Takashi Iwai
1 parent
e4967d6016
Exists in
master
and in
20 other branches
ALSA: Add ALSA driver for Atmel AC97 controller
This patch adds ALSA support for the AC97 controller found on Atmel AVR32 devices. Tested on ATSTK1006 + ATSTK1000 with a development board with a AC97 codec. Signed-off-by: Hans-Christian Egtvedt <hans-christian.egtvedt@atmel.com> Signed-off-by: Takashi Iwai <tiwai@suse.de>
Showing 5 changed files with 1053 additions and 0 deletions Side-by-side Diff
include/sound/atmel-ac97c.h
1 | +/* | |
2 | + * Driver for the Atmel AC97C controller | |
3 | + * | |
4 | + * Copyright (C) 2005-2009 Atmel Corporation | |
5 | + * | |
6 | + * This program is free software; you can redistribute it and/or modify it | |
7 | + * under the terms of the GNU General Public License version 2 as published | |
8 | + * by the Free Software Foundation. | |
9 | + */ | |
10 | +#ifndef __INCLUDE_SOUND_ATMEL_AC97C_H | |
11 | +#define __INCLUDE_SOUND_ATMEL_AC97C_H | |
12 | + | |
13 | +#include <linux/dw_dmac.h> | |
14 | + | |
15 | +#define AC97C_CAPTURE 0x01 | |
16 | +#define AC97C_PLAYBACK 0x02 | |
17 | +#define AC97C_BOTH (AC97C_CAPTURE | AC97C_PLAYBACK) | |
18 | + | |
19 | +/** | |
20 | + * struct atmel_ac97c_pdata - board specific AC97C configuration | |
21 | + * @rx_dws: DMA slave interface to use for sound capture. | |
22 | + * @tx_dws: DMA slave interface to use for sound playback. | |
23 | + * @reset_pin: GPIO pin wired to the reset input on the external AC97 codec, | |
24 | + * optional to use, set to -ENODEV if not in use. AC97 layer will | |
25 | + * try to do a software reset of the external codec anyway. | |
26 | + * @flags: Flags for which directions should be enabled. | |
27 | + * | |
28 | + * If the user do not want to use a DMA channel for playback or capture, i.e. | |
29 | + * only one feature is required on the board. The slave for playback or capture | |
30 | + * can be set to NULL. The AC97C driver will take use of this when setting up | |
31 | + * the sound streams. | |
32 | + */ | |
33 | +struct ac97c_platform_data { | |
34 | + struct dw_dma_slave rx_dws; | |
35 | + struct dw_dma_slave tx_dws; | |
36 | + unsigned int flags; | |
37 | + int reset_pin; | |
38 | +}; | |
39 | + | |
40 | +#endif /* __INCLUDE_SOUND_ATMEL_AC97C_H */ |
sound/atmel/Kconfig
... | ... | @@ -8,5 +8,13 @@ |
8 | 8 | help |
9 | 9 | ALSA sound driver for the Atmel Audio Bitstream DAC (ABDAC). |
10 | 10 | |
11 | +config SND_ATMEL_AC97C | |
12 | + tristate "Atmel AC97 Controller (AC97C) driver" | |
13 | + select SND_PCM | |
14 | + select SND_AC97_CODEC | |
15 | + depends on DW_DMAC && AVR32 | |
16 | + help | |
17 | + ALSA sound driver for the Atmel AC97 controller. | |
18 | + | |
11 | 19 | endmenu |
sound/atmel/Makefile
sound/atmel/ac97c.c
1 | +/* | |
2 | + * Driver for the Atmel AC97C controller | |
3 | + * | |
4 | + * Copyright (C) 2005-2009 Atmel Corporation | |
5 | + * | |
6 | + * This program is free software; you can redistribute it and/or modify it | |
7 | + * under the terms of the GNU General Public License version 2 as published by | |
8 | + * the Free Software Foundation. | |
9 | + */ | |
10 | +#include <linux/clk.h> | |
11 | +#include <linux/delay.h> | |
12 | +#include <linux/bitmap.h> | |
13 | +#include <linux/dmaengine.h> | |
14 | +#include <linux/dma-mapping.h> | |
15 | +#include <linux/init.h> | |
16 | +#include <linux/interrupt.h> | |
17 | +#include <linux/module.h> | |
18 | +#include <linux/platform_device.h> | |
19 | +#include <linux/mutex.h> | |
20 | +#include <linux/gpio.h> | |
21 | +#include <linux/io.h> | |
22 | + | |
23 | +#include <sound/core.h> | |
24 | +#include <sound/initval.h> | |
25 | +#include <sound/pcm.h> | |
26 | +#include <sound/pcm_params.h> | |
27 | +#include <sound/ac97_codec.h> | |
28 | +#include <sound/atmel-ac97c.h> | |
29 | +#include <sound/memalloc.h> | |
30 | + | |
31 | +#include <linux/dw_dmac.h> | |
32 | + | |
33 | +#include "ac97c.h" | |
34 | + | |
35 | +enum { | |
36 | + DMA_TX_READY = 0, | |
37 | + DMA_RX_READY, | |
38 | + DMA_TX_CHAN_PRESENT, | |
39 | + DMA_RX_CHAN_PRESENT, | |
40 | +}; | |
41 | + | |
42 | +/* Serialize access to opened variable */ | |
43 | +static DEFINE_MUTEX(opened_mutex); | |
44 | + | |
45 | +struct atmel_ac97c_dma { | |
46 | + struct dma_chan *rx_chan; | |
47 | + struct dma_chan *tx_chan; | |
48 | +}; | |
49 | + | |
50 | +struct atmel_ac97c { | |
51 | + struct clk *pclk; | |
52 | + struct platform_device *pdev; | |
53 | + struct atmel_ac97c_dma dma; | |
54 | + | |
55 | + struct snd_pcm_substream *playback_substream; | |
56 | + struct snd_pcm_substream *capture_substream; | |
57 | + struct snd_card *card; | |
58 | + struct snd_pcm *pcm; | |
59 | + struct snd_ac97 *ac97; | |
60 | + struct snd_ac97_bus *ac97_bus; | |
61 | + | |
62 | + u64 cur_format; | |
63 | + unsigned int cur_rate; | |
64 | + unsigned long flags; | |
65 | + /* Serialize access to opened variable */ | |
66 | + spinlock_t lock; | |
67 | + void __iomem *regs; | |
68 | + int opened; | |
69 | + int reset_pin; | |
70 | +}; | |
71 | + | |
72 | +#define get_chip(card) ((struct atmel_ac97c *)(card)->private_data) | |
73 | + | |
74 | +#define ac97c_writel(chip, reg, val) \ | |
75 | + __raw_writel((val), (chip)->regs + AC97C_##reg) | |
76 | +#define ac97c_readl(chip, reg) \ | |
77 | + __raw_readl((chip)->regs + AC97C_##reg) | |
78 | + | |
79 | +/* This function is called by the DMA driver. */ | |
80 | +static void atmel_ac97c_dma_playback_period_done(void *arg) | |
81 | +{ | |
82 | + struct atmel_ac97c *chip = arg; | |
83 | + snd_pcm_period_elapsed(chip->playback_substream); | |
84 | +} | |
85 | + | |
86 | +static void atmel_ac97c_dma_capture_period_done(void *arg) | |
87 | +{ | |
88 | + struct atmel_ac97c *chip = arg; | |
89 | + snd_pcm_period_elapsed(chip->capture_substream); | |
90 | +} | |
91 | + | |
92 | +static int atmel_ac97c_prepare_dma(struct atmel_ac97c *chip, | |
93 | + struct snd_pcm_substream *substream, | |
94 | + enum dma_data_direction direction) | |
95 | +{ | |
96 | + struct dma_chan *chan; | |
97 | + struct dw_cyclic_desc *cdesc; | |
98 | + struct snd_pcm_runtime *runtime = substream->runtime; | |
99 | + unsigned long buffer_len, period_len; | |
100 | + | |
101 | + /* | |
102 | + * We don't do DMA on "complex" transfers, i.e. with | |
103 | + * non-halfword-aligned buffers or lengths. | |
104 | + */ | |
105 | + if (runtime->dma_addr & 1 || runtime->buffer_size & 1) { | |
106 | + dev_dbg(&chip->pdev->dev, "too complex transfer\n"); | |
107 | + return -EINVAL; | |
108 | + } | |
109 | + | |
110 | + if (direction == DMA_TO_DEVICE) | |
111 | + chan = chip->dma.tx_chan; | |
112 | + else | |
113 | + chan = chip->dma.rx_chan; | |
114 | + | |
115 | + buffer_len = frames_to_bytes(runtime, runtime->buffer_size); | |
116 | + period_len = frames_to_bytes(runtime, runtime->period_size); | |
117 | + | |
118 | + cdesc = dw_dma_cyclic_prep(chan, runtime->dma_addr, buffer_len, | |
119 | + period_len, direction); | |
120 | + if (IS_ERR(cdesc)) { | |
121 | + dev_dbg(&chip->pdev->dev, "could not prepare cyclic DMA\n"); | |
122 | + return PTR_ERR(cdesc); | |
123 | + } | |
124 | + | |
125 | + if (direction == DMA_TO_DEVICE) { | |
126 | + cdesc->period_callback = atmel_ac97c_dma_playback_period_done; | |
127 | + set_bit(DMA_TX_READY, &chip->flags); | |
128 | + } else { | |
129 | + cdesc->period_callback = atmel_ac97c_dma_capture_period_done; | |
130 | + set_bit(DMA_RX_READY, &chip->flags); | |
131 | + } | |
132 | + | |
133 | + cdesc->period_callback_param = chip; | |
134 | + | |
135 | + return 0; | |
136 | +} | |
137 | + | |
138 | +static struct snd_pcm_hardware atmel_ac97c_hw = { | |
139 | + .info = (SNDRV_PCM_INFO_MMAP | |
140 | + | SNDRV_PCM_INFO_MMAP_VALID | |
141 | + | SNDRV_PCM_INFO_INTERLEAVED | |
142 | + | SNDRV_PCM_INFO_BLOCK_TRANSFER | |
143 | + | SNDRV_PCM_INFO_JOINT_DUPLEX | |
144 | + | SNDRV_PCM_INFO_RESUME | |
145 | + | SNDRV_PCM_INFO_PAUSE), | |
146 | + .formats = (SNDRV_PCM_FMTBIT_S16_BE | |
147 | + | SNDRV_PCM_FMTBIT_S16_LE), | |
148 | + .rates = (SNDRV_PCM_RATE_CONTINUOUS), | |
149 | + .rate_min = 4000, | |
150 | + .rate_max = 48000, | |
151 | + .channels_min = 1, | |
152 | + .channels_max = 2, | |
153 | + .buffer_bytes_max = 64 * 4096, | |
154 | + .period_bytes_min = 4096, | |
155 | + .period_bytes_max = 4096, | |
156 | + .periods_min = 4, | |
157 | + .periods_max = 64, | |
158 | +}; | |
159 | + | |
160 | +static int atmel_ac97c_playback_open(struct snd_pcm_substream *substream) | |
161 | +{ | |
162 | + struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); | |
163 | + struct snd_pcm_runtime *runtime = substream->runtime; | |
164 | + | |
165 | + mutex_lock(&opened_mutex); | |
166 | + chip->opened++; | |
167 | + runtime->hw = atmel_ac97c_hw; | |
168 | + if (chip->cur_rate) { | |
169 | + runtime->hw.rate_min = chip->cur_rate; | |
170 | + runtime->hw.rate_max = chip->cur_rate; | |
171 | + } | |
172 | + if (chip->cur_format) | |
173 | + runtime->hw.formats = (1ULL << chip->cur_format); | |
174 | + mutex_unlock(&opened_mutex); | |
175 | + chip->playback_substream = substream; | |
176 | + return 0; | |
177 | +} | |
178 | + | |
179 | +static int atmel_ac97c_capture_open(struct snd_pcm_substream *substream) | |
180 | +{ | |
181 | + struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); | |
182 | + struct snd_pcm_runtime *runtime = substream->runtime; | |
183 | + | |
184 | + mutex_lock(&opened_mutex); | |
185 | + chip->opened++; | |
186 | + runtime->hw = atmel_ac97c_hw; | |
187 | + if (chip->cur_rate) { | |
188 | + runtime->hw.rate_min = chip->cur_rate; | |
189 | + runtime->hw.rate_max = chip->cur_rate; | |
190 | + } | |
191 | + if (chip->cur_format) | |
192 | + runtime->hw.formats = (1ULL << chip->cur_format); | |
193 | + mutex_unlock(&opened_mutex); | |
194 | + chip->capture_substream = substream; | |
195 | + return 0; | |
196 | +} | |
197 | + | |
198 | +static int atmel_ac97c_playback_close(struct snd_pcm_substream *substream) | |
199 | +{ | |
200 | + struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); | |
201 | + | |
202 | + mutex_lock(&opened_mutex); | |
203 | + chip->opened--; | |
204 | + if (!chip->opened) { | |
205 | + chip->cur_rate = 0; | |
206 | + chip->cur_format = 0; | |
207 | + } | |
208 | + mutex_unlock(&opened_mutex); | |
209 | + | |
210 | + chip->playback_substream = NULL; | |
211 | + | |
212 | + return 0; | |
213 | +} | |
214 | + | |
215 | +static int atmel_ac97c_capture_close(struct snd_pcm_substream *substream) | |
216 | +{ | |
217 | + struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); | |
218 | + | |
219 | + mutex_lock(&opened_mutex); | |
220 | + chip->opened--; | |
221 | + if (!chip->opened) { | |
222 | + chip->cur_rate = 0; | |
223 | + chip->cur_format = 0; | |
224 | + } | |
225 | + mutex_unlock(&opened_mutex); | |
226 | + | |
227 | + chip->capture_substream = NULL; | |
228 | + | |
229 | + return 0; | |
230 | +} | |
231 | + | |
232 | +static int atmel_ac97c_playback_hw_params(struct snd_pcm_substream *substream, | |
233 | + struct snd_pcm_hw_params *hw_params) | |
234 | +{ | |
235 | + struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); | |
236 | + int retval; | |
237 | + | |
238 | + retval = snd_pcm_lib_malloc_pages(substream, | |
239 | + params_buffer_bytes(hw_params)); | |
240 | + if (retval < 0) | |
241 | + return retval; | |
242 | + /* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */ | |
243 | + if (retval == 1) | |
244 | + if (test_and_clear_bit(DMA_TX_READY, &chip->flags)) | |
245 | + dw_dma_cyclic_free(chip->dma.tx_chan); | |
246 | + | |
247 | + /* Set restrictions to params. */ | |
248 | + mutex_lock(&opened_mutex); | |
249 | + chip->cur_rate = params_rate(hw_params); | |
250 | + chip->cur_format = params_format(hw_params); | |
251 | + mutex_unlock(&opened_mutex); | |
252 | + | |
253 | + return retval; | |
254 | +} | |
255 | + | |
256 | +static int atmel_ac97c_capture_hw_params(struct snd_pcm_substream *substream, | |
257 | + struct snd_pcm_hw_params *hw_params) | |
258 | +{ | |
259 | + struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); | |
260 | + int retval; | |
261 | + | |
262 | + retval = snd_pcm_lib_malloc_pages(substream, | |
263 | + params_buffer_bytes(hw_params)); | |
264 | + if (retval < 0) | |
265 | + return retval; | |
266 | + /* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */ | |
267 | + if (retval == 1) | |
268 | + if (test_and_clear_bit(DMA_RX_READY, &chip->flags)) | |
269 | + dw_dma_cyclic_free(chip->dma.rx_chan); | |
270 | + | |
271 | + /* Set restrictions to params. */ | |
272 | + mutex_lock(&opened_mutex); | |
273 | + chip->cur_rate = params_rate(hw_params); | |
274 | + chip->cur_format = params_format(hw_params); | |
275 | + mutex_unlock(&opened_mutex); | |
276 | + | |
277 | + return retval; | |
278 | +} | |
279 | + | |
280 | +static int atmel_ac97c_playback_hw_free(struct snd_pcm_substream *substream) | |
281 | +{ | |
282 | + struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); | |
283 | + if (test_and_clear_bit(DMA_TX_READY, &chip->flags)) | |
284 | + dw_dma_cyclic_free(chip->dma.tx_chan); | |
285 | + return snd_pcm_lib_free_pages(substream); | |
286 | +} | |
287 | + | |
288 | +static int atmel_ac97c_capture_hw_free(struct snd_pcm_substream *substream) | |
289 | +{ | |
290 | + struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); | |
291 | + if (test_and_clear_bit(DMA_RX_READY, &chip->flags)) | |
292 | + dw_dma_cyclic_free(chip->dma.rx_chan); | |
293 | + return snd_pcm_lib_free_pages(substream); | |
294 | +} | |
295 | + | |
296 | +static int atmel_ac97c_playback_prepare(struct snd_pcm_substream *substream) | |
297 | +{ | |
298 | + struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); | |
299 | + struct snd_pcm_runtime *runtime = substream->runtime; | |
300 | + unsigned long word = 0; | |
301 | + int retval; | |
302 | + | |
303 | + /* assign channels to AC97C channel A */ | |
304 | + switch (runtime->channels) { | |
305 | + case 1: | |
306 | + word |= AC97C_CH_ASSIGN(PCM_LEFT, A); | |
307 | + break; | |
308 | + case 2: | |
309 | + word |= AC97C_CH_ASSIGN(PCM_LEFT, A) | |
310 | + | AC97C_CH_ASSIGN(PCM_RIGHT, A); | |
311 | + break; | |
312 | + default: | |
313 | + /* TODO: support more than two channels */ | |
314 | + return -EINVAL; | |
315 | + break; | |
316 | + } | |
317 | + ac97c_writel(chip, OCA, word); | |
318 | + | |
319 | + /* configure sample format and size */ | |
320 | + word = AC97C_CMR_DMAEN | AC97C_CMR_SIZE_16; | |
321 | + | |
322 | + switch (runtime->format) { | |
323 | + case SNDRV_PCM_FORMAT_S16_LE: | |
324 | + word |= AC97C_CMR_CEM_LITTLE; | |
325 | + break; | |
326 | + case SNDRV_PCM_FORMAT_S16_BE: /* fall through */ | |
327 | + default: | |
328 | + word &= ~(AC97C_CMR_CEM_LITTLE); | |
329 | + break; | |
330 | + } | |
331 | + | |
332 | + ac97c_writel(chip, CAMR, word); | |
333 | + | |
334 | + /* set variable rate if needed */ | |
335 | + if (runtime->rate != 48000) { | |
336 | + word = ac97c_readl(chip, MR); | |
337 | + word |= AC97C_MR_VRA; | |
338 | + ac97c_writel(chip, MR, word); | |
339 | + } else { | |
340 | + word = ac97c_readl(chip, MR); | |
341 | + word &= ~(AC97C_MR_VRA); | |
342 | + ac97c_writel(chip, MR, word); | |
343 | + } | |
344 | + | |
345 | + retval = snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, | |
346 | + runtime->rate); | |
347 | + if (retval) | |
348 | + dev_dbg(&chip->pdev->dev, "could not set rate %d Hz\n", | |
349 | + runtime->rate); | |
350 | + | |
351 | + if (!test_bit(DMA_TX_READY, &chip->flags)) | |
352 | + retval = atmel_ac97c_prepare_dma(chip, substream, | |
353 | + DMA_TO_DEVICE); | |
354 | + | |
355 | + return retval; | |
356 | +} | |
357 | + | |
358 | +static int atmel_ac97c_capture_prepare(struct snd_pcm_substream *substream) | |
359 | +{ | |
360 | + struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); | |
361 | + struct snd_pcm_runtime *runtime = substream->runtime; | |
362 | + unsigned long word = 0; | |
363 | + int retval; | |
364 | + | |
365 | + /* assign channels to AC97C channel A */ | |
366 | + switch (runtime->channels) { | |
367 | + case 1: | |
368 | + word |= AC97C_CH_ASSIGN(PCM_LEFT, A); | |
369 | + break; | |
370 | + case 2: | |
371 | + word |= AC97C_CH_ASSIGN(PCM_LEFT, A) | |
372 | + | AC97C_CH_ASSIGN(PCM_RIGHT, A); | |
373 | + break; | |
374 | + default: | |
375 | + /* TODO: support more than two channels */ | |
376 | + return -EINVAL; | |
377 | + break; | |
378 | + } | |
379 | + ac97c_writel(chip, ICA, word); | |
380 | + | |
381 | + /* configure sample format and size */ | |
382 | + word = AC97C_CMR_DMAEN | AC97C_CMR_SIZE_16; | |
383 | + | |
384 | + switch (runtime->format) { | |
385 | + case SNDRV_PCM_FORMAT_S16_LE: | |
386 | + word |= AC97C_CMR_CEM_LITTLE; | |
387 | + break; | |
388 | + case SNDRV_PCM_FORMAT_S16_BE: /* fall through */ | |
389 | + default: | |
390 | + word &= ~(AC97C_CMR_CEM_LITTLE); | |
391 | + break; | |
392 | + } | |
393 | + | |
394 | + ac97c_writel(chip, CAMR, word); | |
395 | + | |
396 | + /* set variable rate if needed */ | |
397 | + if (runtime->rate != 48000) { | |
398 | + word = ac97c_readl(chip, MR); | |
399 | + word |= AC97C_MR_VRA; | |
400 | + ac97c_writel(chip, MR, word); | |
401 | + } else { | |
402 | + word = ac97c_readl(chip, MR); | |
403 | + word &= ~(AC97C_MR_VRA); | |
404 | + ac97c_writel(chip, MR, word); | |
405 | + } | |
406 | + | |
407 | + retval = snd_ac97_set_rate(chip->ac97, AC97_PCM_LR_ADC_RATE, | |
408 | + runtime->rate); | |
409 | + if (retval) | |
410 | + dev_dbg(&chip->pdev->dev, "could not set rate %d Hz\n", | |
411 | + runtime->rate); | |
412 | + | |
413 | + if (!test_bit(DMA_RX_READY, &chip->flags)) | |
414 | + retval = atmel_ac97c_prepare_dma(chip, substream, | |
415 | + DMA_FROM_DEVICE); | |
416 | + | |
417 | + return retval; | |
418 | +} | |
419 | + | |
420 | +static int | |
421 | +atmel_ac97c_playback_trigger(struct snd_pcm_substream *substream, int cmd) | |
422 | +{ | |
423 | + struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); | |
424 | + unsigned long camr; | |
425 | + int retval = 0; | |
426 | + | |
427 | + camr = ac97c_readl(chip, CAMR); | |
428 | + | |
429 | + switch (cmd) { | |
430 | + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */ | |
431 | + case SNDRV_PCM_TRIGGER_RESUME: /* fall through */ | |
432 | + case SNDRV_PCM_TRIGGER_START: | |
433 | + retval = dw_dma_cyclic_start(chip->dma.tx_chan); | |
434 | + if (retval) | |
435 | + goto out; | |
436 | + camr |= AC97C_CMR_CENA; | |
437 | + break; | |
438 | + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */ | |
439 | + case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */ | |
440 | + case SNDRV_PCM_TRIGGER_STOP: | |
441 | + dw_dma_cyclic_stop(chip->dma.tx_chan); | |
442 | + if (chip->opened <= 1) | |
443 | + camr &= ~AC97C_CMR_CENA; | |
444 | + break; | |
445 | + default: | |
446 | + retval = -EINVAL; | |
447 | + goto out; | |
448 | + } | |
449 | + | |
450 | + ac97c_writel(chip, CAMR, camr); | |
451 | +out: | |
452 | + return retval; | |
453 | +} | |
454 | + | |
455 | +static int | |
456 | +atmel_ac97c_capture_trigger(struct snd_pcm_substream *substream, int cmd) | |
457 | +{ | |
458 | + struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); | |
459 | + unsigned long camr; | |
460 | + int retval = 0; | |
461 | + | |
462 | + camr = ac97c_readl(chip, CAMR); | |
463 | + | |
464 | + switch (cmd) { | |
465 | + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */ | |
466 | + case SNDRV_PCM_TRIGGER_RESUME: /* fall through */ | |
467 | + case SNDRV_PCM_TRIGGER_START: | |
468 | + retval = dw_dma_cyclic_start(chip->dma.rx_chan); | |
469 | + if (retval) | |
470 | + goto out; | |
471 | + camr |= AC97C_CMR_CENA; | |
472 | + break; | |
473 | + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */ | |
474 | + case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */ | |
475 | + case SNDRV_PCM_TRIGGER_STOP: | |
476 | + dw_dma_cyclic_stop(chip->dma.rx_chan); | |
477 | + if (chip->opened <= 1) | |
478 | + camr &= ~AC97C_CMR_CENA; | |
479 | + break; | |
480 | + default: | |
481 | + retval = -EINVAL; | |
482 | + break; | |
483 | + } | |
484 | + | |
485 | + ac97c_writel(chip, CAMR, camr); | |
486 | +out: | |
487 | + return retval; | |
488 | +} | |
489 | + | |
490 | +static snd_pcm_uframes_t | |
491 | +atmel_ac97c_playback_pointer(struct snd_pcm_substream *substream) | |
492 | +{ | |
493 | + struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); | |
494 | + struct snd_pcm_runtime *runtime = substream->runtime; | |
495 | + snd_pcm_uframes_t frames; | |
496 | + unsigned long bytes; | |
497 | + | |
498 | + bytes = dw_dma_get_src_addr(chip->dma.tx_chan); | |
499 | + bytes -= runtime->dma_addr; | |
500 | + | |
501 | + frames = bytes_to_frames(runtime, bytes); | |
502 | + if (frames >= runtime->buffer_size) | |
503 | + frames -= runtime->buffer_size; | |
504 | + return frames; | |
505 | +} | |
506 | + | |
507 | +static snd_pcm_uframes_t | |
508 | +atmel_ac97c_capture_pointer(struct snd_pcm_substream *substream) | |
509 | +{ | |
510 | + struct atmel_ac97c *chip = snd_pcm_substream_chip(substream); | |
511 | + struct snd_pcm_runtime *runtime = substream->runtime; | |
512 | + snd_pcm_uframes_t frames; | |
513 | + unsigned long bytes; | |
514 | + | |
515 | + bytes = dw_dma_get_dst_addr(chip->dma.rx_chan); | |
516 | + bytes -= runtime->dma_addr; | |
517 | + | |
518 | + frames = bytes_to_frames(runtime, bytes); | |
519 | + if (frames >= runtime->buffer_size) | |
520 | + frames -= runtime->buffer_size; | |
521 | + return frames; | |
522 | +} | |
523 | + | |
524 | +static struct snd_pcm_ops atmel_ac97_playback_ops = { | |
525 | + .open = atmel_ac97c_playback_open, | |
526 | + .close = atmel_ac97c_playback_close, | |
527 | + .ioctl = snd_pcm_lib_ioctl, | |
528 | + .hw_params = atmel_ac97c_playback_hw_params, | |
529 | + .hw_free = atmel_ac97c_playback_hw_free, | |
530 | + .prepare = atmel_ac97c_playback_prepare, | |
531 | + .trigger = atmel_ac97c_playback_trigger, | |
532 | + .pointer = atmel_ac97c_playback_pointer, | |
533 | +}; | |
534 | + | |
535 | +static struct snd_pcm_ops atmel_ac97_capture_ops = { | |
536 | + .open = atmel_ac97c_capture_open, | |
537 | + .close = atmel_ac97c_capture_close, | |
538 | + .ioctl = snd_pcm_lib_ioctl, | |
539 | + .hw_params = atmel_ac97c_capture_hw_params, | |
540 | + .hw_free = atmel_ac97c_capture_hw_free, | |
541 | + .prepare = atmel_ac97c_capture_prepare, | |
542 | + .trigger = atmel_ac97c_capture_trigger, | |
543 | + .pointer = atmel_ac97c_capture_pointer, | |
544 | +}; | |
545 | + | |
546 | +static int __devinit atmel_ac97c_pcm_new(struct atmel_ac97c *chip) | |
547 | +{ | |
548 | + struct snd_pcm *pcm; | |
549 | + struct snd_pcm_hardware hw = atmel_ac97c_hw; | |
550 | + int capture, playback, retval; | |
551 | + | |
552 | + capture = test_bit(DMA_RX_CHAN_PRESENT, &chip->flags); | |
553 | + playback = test_bit(DMA_TX_CHAN_PRESENT, &chip->flags); | |
554 | + | |
555 | + retval = snd_pcm_new(chip->card, chip->card->shortname, | |
556 | + chip->pdev->id, playback, capture, &pcm); | |
557 | + if (retval) | |
558 | + return retval; | |
559 | + | |
560 | + if (capture) | |
561 | + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, | |
562 | + &atmel_ac97_capture_ops); | |
563 | + if (playback) | |
564 | + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, | |
565 | + &atmel_ac97_playback_ops); | |
566 | + | |
567 | + retval = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, | |
568 | + &chip->pdev->dev, hw.periods_min * hw.period_bytes_min, | |
569 | + hw.buffer_bytes_max); | |
570 | + if (retval) | |
571 | + return retval; | |
572 | + | |
573 | + pcm->private_data = chip; | |
574 | + pcm->info_flags = 0; | |
575 | + strcpy(pcm->name, chip->card->shortname); | |
576 | + chip->pcm = pcm; | |
577 | + | |
578 | + return 0; | |
579 | +} | |
580 | + | |
581 | +static int atmel_ac97c_mixer_new(struct atmel_ac97c *chip) | |
582 | +{ | |
583 | + struct snd_ac97_template template; | |
584 | + memset(&template, 0, sizeof(template)); | |
585 | + template.private_data = chip; | |
586 | + return snd_ac97_mixer(chip->ac97_bus, &template, &chip->ac97); | |
587 | +} | |
588 | + | |
589 | +static void atmel_ac97c_write(struct snd_ac97 *ac97, unsigned short reg, | |
590 | + unsigned short val) | |
591 | +{ | |
592 | + struct atmel_ac97c *chip = get_chip(ac97); | |
593 | + unsigned long word; | |
594 | + int timeout = 40; | |
595 | + | |
596 | + word = (reg & 0x7f) << 16 | val; | |
597 | + | |
598 | + do { | |
599 | + if (ac97c_readl(chip, COSR) & AC97C_CSR_TXRDY) { | |
600 | + ac97c_writel(chip, COTHR, word); | |
601 | + return; | |
602 | + } | |
603 | + udelay(1); | |
604 | + } while (--timeout); | |
605 | + | |
606 | + dev_dbg(&chip->pdev->dev, "codec write timeout\n"); | |
607 | +} | |
608 | + | |
609 | +static unsigned short atmel_ac97c_read(struct snd_ac97 *ac97, | |
610 | + unsigned short reg) | |
611 | +{ | |
612 | + struct atmel_ac97c *chip = get_chip(ac97); | |
613 | + unsigned long word; | |
614 | + int timeout = 40; | |
615 | + int write = 10; | |
616 | + | |
617 | + word = (0x80 | (reg & 0x7f)) << 16; | |
618 | + | |
619 | + if ((ac97c_readl(chip, COSR) & AC97C_CSR_RXRDY) != 0) | |
620 | + ac97c_readl(chip, CORHR); | |
621 | + | |
622 | +retry_write: | |
623 | + timeout = 40; | |
624 | + | |
625 | + do { | |
626 | + if ((ac97c_readl(chip, COSR) & AC97C_CSR_TXRDY) != 0) { | |
627 | + ac97c_writel(chip, COTHR, word); | |
628 | + goto read_reg; | |
629 | + } | |
630 | + udelay(10); | |
631 | + } while (--timeout); | |
632 | + | |
633 | + if (!--write) | |
634 | + goto timed_out; | |
635 | + goto retry_write; | |
636 | + | |
637 | +read_reg: | |
638 | + do { | |
639 | + if ((ac97c_readl(chip, COSR) & AC97C_CSR_RXRDY) != 0) { | |
640 | + unsigned short val = ac97c_readl(chip, CORHR); | |
641 | + return val; | |
642 | + } | |
643 | + udelay(10); | |
644 | + } while (--timeout); | |
645 | + | |
646 | + if (!--write) | |
647 | + goto timed_out; | |
648 | + goto retry_write; | |
649 | + | |
650 | +timed_out: | |
651 | + dev_dbg(&chip->pdev->dev, "codec read timeout\n"); | |
652 | + return 0xffff; | |
653 | +} | |
654 | + | |
655 | +static bool filter(struct dma_chan *chan, void *slave) | |
656 | +{ | |
657 | + struct dw_dma_slave *dws = slave; | |
658 | + | |
659 | + if (dws->dma_dev == chan->device->dev) { | |
660 | + chan->private = dws; | |
661 | + return true; | |
662 | + } else | |
663 | + return false; | |
664 | +} | |
665 | + | |
666 | +static void atmel_ac97c_reset(struct atmel_ac97c *chip) | |
667 | +{ | |
668 | + ac97c_writel(chip, MR, AC97C_MR_WRST); | |
669 | + | |
670 | + if (gpio_is_valid(chip->reset_pin)) { | |
671 | + gpio_set_value(chip->reset_pin, 0); | |
672 | + /* AC97 v2.2 specifications says minimum 1 us. */ | |
673 | + udelay(10); | |
674 | + gpio_set_value(chip->reset_pin, 1); | |
675 | + } | |
676 | + | |
677 | + udelay(1); | |
678 | + ac97c_writel(chip, MR, AC97C_MR_ENA); | |
679 | +} | |
680 | + | |
681 | +static int __devinit atmel_ac97c_probe(struct platform_device *pdev) | |
682 | +{ | |
683 | + struct snd_card *card; | |
684 | + struct atmel_ac97c *chip; | |
685 | + struct resource *regs; | |
686 | + struct ac97c_platform_data *pdata; | |
687 | + struct clk *pclk; | |
688 | + static struct snd_ac97_bus_ops ops = { | |
689 | + .write = atmel_ac97c_write, | |
690 | + .read = atmel_ac97c_read, | |
691 | + }; | |
692 | + int retval; | |
693 | + | |
694 | + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
695 | + if (!regs) { | |
696 | + dev_dbg(&pdev->dev, "no memory resource\n"); | |
697 | + return -ENXIO; | |
698 | + } | |
699 | + | |
700 | + pdata = pdev->dev.platform_data; | |
701 | + if (!pdata) { | |
702 | + dev_dbg(&pdev->dev, "no platform data\n"); | |
703 | + return -ENXIO; | |
704 | + } | |
705 | + | |
706 | + pclk = clk_get(&pdev->dev, "pclk"); | |
707 | + if (IS_ERR(pclk)) { | |
708 | + dev_dbg(&pdev->dev, "no peripheral clock\n"); | |
709 | + return PTR_ERR(pclk); | |
710 | + } | |
711 | + clk_enable(pclk); | |
712 | + | |
713 | + retval = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, | |
714 | + THIS_MODULE, sizeof(struct atmel_ac97c), &card); | |
715 | + if (retval) { | |
716 | + dev_dbg(&pdev->dev, "could not create sound card device\n"); | |
717 | + goto err_snd_card_new; | |
718 | + } | |
719 | + | |
720 | + chip = get_chip(card); | |
721 | + | |
722 | + spin_lock_init(&chip->lock); | |
723 | + | |
724 | + strcpy(card->driver, "Atmel AC97C"); | |
725 | + strcpy(card->shortname, "Atmel AC97C"); | |
726 | + sprintf(card->longname, "Atmel AC97 controller"); | |
727 | + | |
728 | + chip->card = card; | |
729 | + chip->pclk = pclk; | |
730 | + chip->pdev = pdev; | |
731 | + chip->regs = ioremap(regs->start, regs->end - regs->start + 1); | |
732 | + | |
733 | + if (!chip->regs) { | |
734 | + dev_dbg(&pdev->dev, "could not remap register memory\n"); | |
735 | + goto err_ioremap; | |
736 | + } | |
737 | + | |
738 | + if (gpio_is_valid(pdata->reset_pin)) { | |
739 | + if (gpio_request(pdata->reset_pin, "reset_pin")) { | |
740 | + dev_dbg(&pdev->dev, "reset pin not available\n"); | |
741 | + chip->reset_pin = -ENODEV; | |
742 | + } else { | |
743 | + gpio_direction_output(pdata->reset_pin, 1); | |
744 | + chip->reset_pin = pdata->reset_pin; | |
745 | + } | |
746 | + } | |
747 | + | |
748 | + snd_card_set_dev(card, &pdev->dev); | |
749 | + | |
750 | + retval = snd_ac97_bus(card, 0, &ops, chip, &chip->ac97_bus); | |
751 | + if (retval) { | |
752 | + dev_dbg(&pdev->dev, "could not register on ac97 bus\n"); | |
753 | + goto err_ac97_bus; | |
754 | + } | |
755 | + | |
756 | + atmel_ac97c_reset(chip); | |
757 | + | |
758 | + retval = atmel_ac97c_mixer_new(chip); | |
759 | + if (retval) { | |
760 | + dev_dbg(&pdev->dev, "could not register ac97 mixer\n"); | |
761 | + goto err_ac97_bus; | |
762 | + } | |
763 | + | |
764 | + if (pdata->rx_dws.dma_dev) { | |
765 | + struct dw_dma_slave *dws = &pdata->rx_dws; | |
766 | + dma_cap_mask_t mask; | |
767 | + | |
768 | + dws->rx_reg = regs->start + AC97C_CARHR + 2; | |
769 | + | |
770 | + dma_cap_zero(mask); | |
771 | + dma_cap_set(DMA_SLAVE, mask); | |
772 | + | |
773 | + chip->dma.rx_chan = dma_request_channel(mask, filter, dws); | |
774 | + | |
775 | + dev_info(&chip->pdev->dev, "using %s for DMA RX\n", | |
776 | + chip->dma.rx_chan->dev->device.bus_id); | |
777 | + set_bit(DMA_RX_CHAN_PRESENT, &chip->flags); | |
778 | + } | |
779 | + | |
780 | + if (pdata->tx_dws.dma_dev) { | |
781 | + struct dw_dma_slave *dws = &pdata->tx_dws; | |
782 | + dma_cap_mask_t mask; | |
783 | + | |
784 | + dws->tx_reg = regs->start + AC97C_CATHR + 2; | |
785 | + | |
786 | + dma_cap_zero(mask); | |
787 | + dma_cap_set(DMA_SLAVE, mask); | |
788 | + | |
789 | + chip->dma.tx_chan = dma_request_channel(mask, filter, dws); | |
790 | + | |
791 | + dev_info(&chip->pdev->dev, "using %s for DMA TX\n", | |
792 | + chip->dma.tx_chan->dev->device.bus_id); | |
793 | + set_bit(DMA_TX_CHAN_PRESENT, &chip->flags); | |
794 | + } | |
795 | + | |
796 | + if (!test_bit(DMA_RX_CHAN_PRESENT, &chip->flags) && | |
797 | + !test_bit(DMA_TX_CHAN_PRESENT, &chip->flags)) { | |
798 | + dev_dbg(&pdev->dev, "DMA not available\n"); | |
799 | + retval = -ENODEV; | |
800 | + goto err_dma; | |
801 | + } | |
802 | + | |
803 | + retval = atmel_ac97c_pcm_new(chip); | |
804 | + if (retval) { | |
805 | + dev_dbg(&pdev->dev, "could not register ac97 pcm device\n"); | |
806 | + goto err_dma; | |
807 | + } | |
808 | + | |
809 | + retval = snd_card_register(card); | |
810 | + if (retval) { | |
811 | + dev_dbg(&pdev->dev, "could not register sound card\n"); | |
812 | + goto err_ac97_bus; | |
813 | + } | |
814 | + | |
815 | + platform_set_drvdata(pdev, card); | |
816 | + | |
817 | + dev_info(&pdev->dev, "Atmel AC97 controller at 0x%p\n", | |
818 | + chip->regs); | |
819 | + | |
820 | + return 0; | |
821 | + | |
822 | +err_dma: | |
823 | + if (test_bit(DMA_RX_CHAN_PRESENT, &chip->flags)) | |
824 | + dma_release_channel(chip->dma.rx_chan); | |
825 | + if (test_bit(DMA_TX_CHAN_PRESENT, &chip->flags)) | |
826 | + dma_release_channel(chip->dma.tx_chan); | |
827 | + clear_bit(DMA_RX_CHAN_PRESENT, &chip->flags); | |
828 | + clear_bit(DMA_TX_CHAN_PRESENT, &chip->flags); | |
829 | + chip->dma.rx_chan = NULL; | |
830 | + chip->dma.tx_chan = NULL; | |
831 | +err_ac97_bus: | |
832 | + snd_card_set_dev(card, NULL); | |
833 | + | |
834 | + if (gpio_is_valid(chip->reset_pin)) | |
835 | + gpio_free(chip->reset_pin); | |
836 | + | |
837 | + iounmap(chip->regs); | |
838 | +err_ioremap: | |
839 | + snd_card_free(card); | |
840 | +err_snd_card_new: | |
841 | + clk_disable(pclk); | |
842 | + clk_put(pclk); | |
843 | + return retval; | |
844 | +} | |
845 | + | |
846 | +#ifdef CONFIG_PM | |
847 | +static int atmel_ac97c_suspend(struct platform_device *pdev, pm_message_t msg) | |
848 | +{ | |
849 | + struct snd_card *card = platform_get_drvdata(pdev); | |
850 | + struct atmel_ac97c *chip = card->private_data; | |
851 | + | |
852 | + if (test_bit(DMA_RX_READY, &chip->flags)) | |
853 | + dw_dma_cyclic_stop(chip->dma.rx_chan); | |
854 | + if (test_bit(DMA_TX_READY, &chip->flags)) | |
855 | + dw_dma_cyclic_stop(chip->dma.tx_chan); | |
856 | + clk_disable(chip->pclk); | |
857 | + | |
858 | + return 0; | |
859 | +} | |
860 | + | |
861 | +static int atmel_ac97c_resume(struct platform_device *pdev) | |
862 | +{ | |
863 | + struct snd_card *card = platform_get_drvdata(pdev); | |
864 | + struct atmel_ac97c *chip = card->private_data; | |
865 | + | |
866 | + clk_enable(chip->pclk); | |
867 | + if (test_bit(DMA_RX_READY, &chip->flags)) | |
868 | + dw_dma_cyclic_start(chip->dma.rx_chan); | |
869 | + if (test_bit(DMA_TX_READY, &chip->flags)) | |
870 | + dw_dma_cyclic_start(chip->dma.tx_chan); | |
871 | + | |
872 | + return 0; | |
873 | +} | |
874 | +#else | |
875 | +#define atmel_ac97c_suspend NULL | |
876 | +#define atmel_ac97c_resume NULL | |
877 | +#endif | |
878 | + | |
879 | +static int __devexit atmel_ac97c_remove(struct platform_device *pdev) | |
880 | +{ | |
881 | + struct snd_card *card = platform_get_drvdata(pdev); | |
882 | + struct atmel_ac97c *chip = get_chip(card); | |
883 | + | |
884 | + if (gpio_is_valid(chip->reset_pin)) | |
885 | + gpio_free(chip->reset_pin); | |
886 | + | |
887 | + clk_disable(chip->pclk); | |
888 | + clk_put(chip->pclk); | |
889 | + iounmap(chip->regs); | |
890 | + | |
891 | + if (test_bit(DMA_RX_CHAN_PRESENT, &chip->flags)) | |
892 | + dma_release_channel(chip->dma.rx_chan); | |
893 | + if (test_bit(DMA_TX_CHAN_PRESENT, &chip->flags)) | |
894 | + dma_release_channel(chip->dma.tx_chan); | |
895 | + clear_bit(DMA_RX_CHAN_PRESENT, &chip->flags); | |
896 | + clear_bit(DMA_TX_CHAN_PRESENT, &chip->flags); | |
897 | + chip->dma.rx_chan = NULL; | |
898 | + chip->dma.tx_chan = NULL; | |
899 | + | |
900 | + snd_card_set_dev(card, NULL); | |
901 | + snd_card_free(card); | |
902 | + | |
903 | + platform_set_drvdata(pdev, NULL); | |
904 | + | |
905 | + return 0; | |
906 | +} | |
907 | + | |
908 | +static struct platform_driver atmel_ac97c_driver = { | |
909 | + .remove = __devexit_p(atmel_ac97c_remove), | |
910 | + .driver = { | |
911 | + .name = "atmel_ac97c", | |
912 | + }, | |
913 | + .suspend = atmel_ac97c_suspend, | |
914 | + .resume = atmel_ac97c_resume, | |
915 | +}; | |
916 | + | |
917 | +static int __init atmel_ac97c_init(void) | |
918 | +{ | |
919 | + return platform_driver_probe(&atmel_ac97c_driver, | |
920 | + atmel_ac97c_probe); | |
921 | +} | |
922 | +module_init(atmel_ac97c_init); | |
923 | + | |
924 | +static void __exit atmel_ac97c_exit(void) | |
925 | +{ | |
926 | + platform_driver_unregister(&atmel_ac97c_driver); | |
927 | +} | |
928 | +module_exit(atmel_ac97c_exit); | |
929 | + | |
930 | +MODULE_LICENSE("GPL"); | |
931 | +MODULE_DESCRIPTION("Driver for Atmel AC97 controller"); | |
932 | +MODULE_AUTHOR("Hans-Christian Egtvedt <hans-christian.egtvedt@atmel.com>"); |
sound/atmel/ac97c.h
1 | +/* | |
2 | + * Register definitions for the Atmel AC97C controller | |
3 | + * | |
4 | + * Copyright (C) 2005-2009 Atmel Corporation | |
5 | + * | |
6 | + * This program is free software; you can redistribute it and/or modify it | |
7 | + * under the terms of the GNU General Public License version 2 as published | |
8 | + * by the Free Software Foundation. | |
9 | + */ | |
10 | +#ifndef __SOUND_ATMEL_AC97C_H | |
11 | +#define __SOUND_ATMEL_AC97C_H | |
12 | + | |
13 | +#define AC97C_MR 0x08 | |
14 | +#define AC97C_ICA 0x10 | |
15 | +#define AC97C_OCA 0x14 | |
16 | +#define AC97C_CARHR 0x20 | |
17 | +#define AC97C_CATHR 0x24 | |
18 | +#define AC97C_CASR 0x28 | |
19 | +#define AC97C_CAMR 0x2c | |
20 | +#define AC97C_CBRHR 0x30 | |
21 | +#define AC97C_CBTHR 0x34 | |
22 | +#define AC97C_CBSR 0x38 | |
23 | +#define AC97C_CBMR 0x3c | |
24 | +#define AC97C_CORHR 0x40 | |
25 | +#define AC97C_COTHR 0x44 | |
26 | +#define AC97C_COSR 0x48 | |
27 | +#define AC97C_COMR 0x4c | |
28 | +#define AC97C_SR 0x50 | |
29 | +#define AC97C_IER 0x54 | |
30 | +#define AC97C_IDR 0x58 | |
31 | +#define AC97C_IMR 0x5c | |
32 | +#define AC97C_VERSION 0xfc | |
33 | + | |
34 | +#define AC97C_CATPR PDC_TPR | |
35 | +#define AC97C_CATCR PDC_TCR | |
36 | +#define AC97C_CATNPR PDC_TNPR | |
37 | +#define AC97C_CATNCR PDC_TNCR | |
38 | +#define AC97C_CARPR PDC_RPR | |
39 | +#define AC97C_CARCR PDC_RCR | |
40 | +#define AC97C_CARNPR PDC_RNPR | |
41 | +#define AC97C_CARNCR PDC_RNCR | |
42 | +#define AC97C_PTCR PDC_PTCR | |
43 | + | |
44 | +#define AC97C_MR_ENA (1 << 0) | |
45 | +#define AC97C_MR_WRST (1 << 1) | |
46 | +#define AC97C_MR_VRA (1 << 2) | |
47 | + | |
48 | +#define AC97C_CSR_TXRDY (1 << 0) | |
49 | +#define AC97C_CSR_UNRUN (1 << 2) | |
50 | +#define AC97C_CSR_RXRDY (1 << 4) | |
51 | +#define AC97C_CSR_ENDTX (1 << 10) | |
52 | +#define AC97C_CSR_ENDRX (1 << 14) | |
53 | + | |
54 | +#define AC97C_CMR_SIZE_20 (0 << 16) | |
55 | +#define AC97C_CMR_SIZE_18 (1 << 16) | |
56 | +#define AC97C_CMR_SIZE_16 (2 << 16) | |
57 | +#define AC97C_CMR_SIZE_10 (3 << 16) | |
58 | +#define AC97C_CMR_CEM_LITTLE (1 << 18) | |
59 | +#define AC97C_CMR_CEM_BIG (0 << 18) | |
60 | +#define AC97C_CMR_CENA (1 << 21) | |
61 | +#define AC97C_CMR_DMAEN (1 << 22) | |
62 | + | |
63 | +#define AC97C_SR_CAEVT (1 << 3) | |
64 | + | |
65 | +#define AC97C_CH_ASSIGN(slot, channel) \ | |
66 | + (AC97C_CHANNEL_##channel << (3 * (AC97_SLOT_##slot - 3))) | |
67 | +#define AC97C_CHANNEL_NONE 0x0 | |
68 | +#define AC97C_CHANNEL_A 0x1 | |
69 | +#define AC97C_CHANNEL_B 0x2 | |
70 | + | |
71 | +#endif /* __SOUND_ATMEL_AC97C_H */ |