diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /sound/isa/ad1816a/ad1816a_lib.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'sound/isa/ad1816a/ad1816a_lib.c')
-rw-r--r-- | sound/isa/ad1816a/ad1816a_lib.c | 974 |
1 files changed, 974 insertions, 0 deletions
diff --git a/sound/isa/ad1816a/ad1816a_lib.c b/sound/isa/ad1816a/ad1816a_lib.c new file mode 100644 index 000000000000..625b2eff14a1 --- /dev/null +++ b/sound/isa/ad1816a/ad1816a_lib.c | |||
@@ -0,0 +1,974 @@ | |||
1 | |||
2 | /* | ||
3 | ad1816a.c - lowlevel code for Analog Devices AD1816A chip. | ||
4 | Copyright (C) 1999-2000 by Massimo Piccioni <dafastidio@libero.it> | ||
5 | |||
6 | This program is free software; you can redistribute it and/or modify | ||
7 | it under the terms of the GNU General Public License as published by | ||
8 | the Free Software Foundation; either version 2 of the License, or | ||
9 | (at your option) any later version. | ||
10 | |||
11 | This program is distributed in the hope that it will be useful, | ||
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | GNU General Public License for more details. | ||
15 | |||
16 | You should have received a copy of the GNU General Public License | ||
17 | along with this program; if not, write to the Free Software | ||
18 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | */ | ||
20 | |||
21 | #include <sound/driver.h> | ||
22 | #include <linux/delay.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/interrupt.h> | ||
25 | #include <linux/slab.h> | ||
26 | #include <linux/ioport.h> | ||
27 | #include <sound/core.h> | ||
28 | #include <sound/ad1816a.h> | ||
29 | |||
30 | #include <asm/io.h> | ||
31 | #include <asm/dma.h> | ||
32 | |||
33 | MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>"); | ||
34 | MODULE_DESCRIPTION("lowlevel code for Analog Devices AD1816A chip"); | ||
35 | MODULE_LICENSE("GPL"); | ||
36 | |||
37 | static inline int snd_ad1816a_busy_wait(ad1816a_t *chip) | ||
38 | { | ||
39 | int timeout; | ||
40 | |||
41 | for (timeout = 1000; timeout-- > 0; udelay(10)) | ||
42 | if (inb(AD1816A_REG(AD1816A_CHIP_STATUS)) & AD1816A_READY) | ||
43 | return 0; | ||
44 | |||
45 | snd_printk("chip busy.\n"); | ||
46 | return -EBUSY; | ||
47 | } | ||
48 | |||
49 | static inline unsigned char snd_ad1816a_in(ad1816a_t *chip, unsigned char reg) | ||
50 | { | ||
51 | snd_ad1816a_busy_wait(chip); | ||
52 | return inb(AD1816A_REG(reg)); | ||
53 | } | ||
54 | |||
55 | static inline void snd_ad1816a_out(ad1816a_t *chip, unsigned char reg, | ||
56 | unsigned char value) | ||
57 | { | ||
58 | snd_ad1816a_busy_wait(chip); | ||
59 | outb(value, AD1816A_REG(reg)); | ||
60 | } | ||
61 | |||
62 | static inline void snd_ad1816a_out_mask(ad1816a_t *chip, unsigned char reg, | ||
63 | unsigned char mask, unsigned char value) | ||
64 | { | ||
65 | snd_ad1816a_out(chip, reg, | ||
66 | (value & mask) | (snd_ad1816a_in(chip, reg) & ~mask)); | ||
67 | } | ||
68 | |||
69 | static unsigned short snd_ad1816a_read(ad1816a_t *chip, unsigned char reg) | ||
70 | { | ||
71 | snd_ad1816a_out(chip, AD1816A_INDIR_ADDR, reg & 0x3f); | ||
72 | return snd_ad1816a_in(chip, AD1816A_INDIR_DATA_LOW) | | ||
73 | (snd_ad1816a_in(chip, AD1816A_INDIR_DATA_HIGH) << 8); | ||
74 | } | ||
75 | |||
76 | static void snd_ad1816a_write(ad1816a_t *chip, unsigned char reg, | ||
77 | unsigned short value) | ||
78 | { | ||
79 | snd_ad1816a_out(chip, AD1816A_INDIR_ADDR, reg & 0x3f); | ||
80 | snd_ad1816a_out(chip, AD1816A_INDIR_DATA_LOW, value & 0xff); | ||
81 | snd_ad1816a_out(chip, AD1816A_INDIR_DATA_HIGH, (value >> 8) & 0xff); | ||
82 | } | ||
83 | |||
84 | static void snd_ad1816a_write_mask(ad1816a_t *chip, unsigned char reg, | ||
85 | unsigned short mask, unsigned short value) | ||
86 | { | ||
87 | snd_ad1816a_write(chip, reg, | ||
88 | (value & mask) | (snd_ad1816a_read(chip, reg) & ~mask)); | ||
89 | } | ||
90 | |||
91 | |||
92 | static unsigned char snd_ad1816a_get_format(ad1816a_t *chip, | ||
93 | unsigned int format, int channels) | ||
94 | { | ||
95 | unsigned char retval = AD1816A_FMT_LINEAR_8; | ||
96 | |||
97 | switch (format) { | ||
98 | case SNDRV_PCM_FORMAT_MU_LAW: | ||
99 | retval = AD1816A_FMT_ULAW_8; | ||
100 | break; | ||
101 | case SNDRV_PCM_FORMAT_A_LAW: | ||
102 | retval = AD1816A_FMT_ALAW_8; | ||
103 | break; | ||
104 | case SNDRV_PCM_FORMAT_S16_LE: | ||
105 | retval = AD1816A_FMT_LINEAR_16_LIT; | ||
106 | break; | ||
107 | case SNDRV_PCM_FORMAT_S16_BE: | ||
108 | retval = AD1816A_FMT_LINEAR_16_BIG; | ||
109 | } | ||
110 | return (channels > 1) ? (retval | AD1816A_FMT_STEREO) : retval; | ||
111 | } | ||
112 | |||
113 | static int snd_ad1816a_open(ad1816a_t *chip, unsigned int mode) | ||
114 | { | ||
115 | unsigned long flags; | ||
116 | |||
117 | spin_lock_irqsave(&chip->lock, flags); | ||
118 | |||
119 | if (chip->mode & mode) { | ||
120 | spin_unlock_irqrestore(&chip->lock, flags); | ||
121 | return -EAGAIN; | ||
122 | } | ||
123 | |||
124 | switch ((mode &= AD1816A_MODE_OPEN)) { | ||
125 | case AD1816A_MODE_PLAYBACK: | ||
126 | snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, | ||
127 | AD1816A_PLAYBACK_IRQ_PENDING, 0x00); | ||
128 | snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, | ||
129 | AD1816A_PLAYBACK_IRQ_ENABLE, 0xffff); | ||
130 | break; | ||
131 | case AD1816A_MODE_CAPTURE: | ||
132 | snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, | ||
133 | AD1816A_CAPTURE_IRQ_PENDING, 0x00); | ||
134 | snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, | ||
135 | AD1816A_CAPTURE_IRQ_ENABLE, 0xffff); | ||
136 | break; | ||
137 | case AD1816A_MODE_TIMER: | ||
138 | snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, | ||
139 | AD1816A_TIMER_IRQ_PENDING, 0x00); | ||
140 | snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, | ||
141 | AD1816A_TIMER_IRQ_ENABLE, 0xffff); | ||
142 | } | ||
143 | chip->mode |= mode; | ||
144 | |||
145 | spin_unlock_irqrestore(&chip->lock, flags); | ||
146 | return 0; | ||
147 | } | ||
148 | |||
149 | static void snd_ad1816a_close(ad1816a_t *chip, unsigned int mode) | ||
150 | { | ||
151 | unsigned long flags; | ||
152 | |||
153 | spin_lock_irqsave(&chip->lock, flags); | ||
154 | |||
155 | switch ((mode &= AD1816A_MODE_OPEN)) { | ||
156 | case AD1816A_MODE_PLAYBACK: | ||
157 | snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, | ||
158 | AD1816A_PLAYBACK_IRQ_PENDING, 0x00); | ||
159 | snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, | ||
160 | AD1816A_PLAYBACK_IRQ_ENABLE, 0x0000); | ||
161 | break; | ||
162 | case AD1816A_MODE_CAPTURE: | ||
163 | snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, | ||
164 | AD1816A_CAPTURE_IRQ_PENDING, 0x00); | ||
165 | snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, | ||
166 | AD1816A_CAPTURE_IRQ_ENABLE, 0x0000); | ||
167 | break; | ||
168 | case AD1816A_MODE_TIMER: | ||
169 | snd_ad1816a_out_mask(chip, AD1816A_INTERRUPT_STATUS, | ||
170 | AD1816A_TIMER_IRQ_PENDING, 0x00); | ||
171 | snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, | ||
172 | AD1816A_TIMER_IRQ_ENABLE, 0x0000); | ||
173 | } | ||
174 | if (!((chip->mode &= ~mode) & AD1816A_MODE_OPEN)) | ||
175 | chip->mode = 0; | ||
176 | |||
177 | spin_unlock_irqrestore(&chip->lock, flags); | ||
178 | } | ||
179 | |||
180 | |||
181 | static int snd_ad1816a_trigger(ad1816a_t *chip, unsigned char what, | ||
182 | int channel, int cmd) | ||
183 | { | ||
184 | int error = 0; | ||
185 | |||
186 | switch (cmd) { | ||
187 | case SNDRV_PCM_TRIGGER_START: | ||
188 | case SNDRV_PCM_TRIGGER_STOP: | ||
189 | spin_lock(&chip->lock); | ||
190 | cmd = (cmd == SNDRV_PCM_TRIGGER_START) ? 0xff: 0x00; | ||
191 | if (what & AD1816A_PLAYBACK_ENABLE) | ||
192 | snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG, | ||
193 | AD1816A_PLAYBACK_ENABLE, cmd); | ||
194 | if (what & AD1816A_CAPTURE_ENABLE) | ||
195 | snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG, | ||
196 | AD1816A_CAPTURE_ENABLE, cmd); | ||
197 | spin_unlock(&chip->lock); | ||
198 | break; | ||
199 | default: | ||
200 | snd_printk("invalid trigger mode 0x%x.\n", what); | ||
201 | error = -EINVAL; | ||
202 | } | ||
203 | |||
204 | return error; | ||
205 | } | ||
206 | |||
207 | static int snd_ad1816a_playback_trigger(snd_pcm_substream_t *substream, int cmd) | ||
208 | { | ||
209 | ad1816a_t *chip = snd_pcm_substream_chip(substream); | ||
210 | return snd_ad1816a_trigger(chip, AD1816A_PLAYBACK_ENABLE, | ||
211 | SNDRV_PCM_STREAM_PLAYBACK, cmd); | ||
212 | } | ||
213 | |||
214 | static int snd_ad1816a_capture_trigger(snd_pcm_substream_t *substream, int cmd) | ||
215 | { | ||
216 | ad1816a_t *chip = snd_pcm_substream_chip(substream); | ||
217 | return snd_ad1816a_trigger(chip, AD1816A_CAPTURE_ENABLE, | ||
218 | SNDRV_PCM_STREAM_CAPTURE, cmd); | ||
219 | } | ||
220 | |||
221 | static int snd_ad1816a_hw_params(snd_pcm_substream_t * substream, | ||
222 | snd_pcm_hw_params_t * hw_params) | ||
223 | { | ||
224 | return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); | ||
225 | } | ||
226 | |||
227 | static int snd_ad1816a_hw_free(snd_pcm_substream_t * substream) | ||
228 | { | ||
229 | return snd_pcm_lib_free_pages(substream); | ||
230 | } | ||
231 | |||
232 | static int snd_ad1816a_playback_prepare(snd_pcm_substream_t *substream) | ||
233 | { | ||
234 | ad1816a_t *chip = snd_pcm_substream_chip(substream); | ||
235 | unsigned long flags; | ||
236 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
237 | unsigned int size; | ||
238 | |||
239 | spin_lock_irqsave(&chip->lock, flags); | ||
240 | |||
241 | chip->p_dma_size = size = snd_pcm_lib_buffer_bytes(substream); | ||
242 | snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG, | ||
243 | AD1816A_PLAYBACK_ENABLE | AD1816A_PLAYBACK_PIO, 0x00); | ||
244 | |||
245 | snd_dma_program(chip->dma1, runtime->dma_addr, size, | ||
246 | DMA_MODE_WRITE | DMA_AUTOINIT); | ||
247 | |||
248 | snd_ad1816a_write(chip, AD1816A_PLAYBACK_SAMPLE_RATE, runtime->rate); | ||
249 | snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG, | ||
250 | AD1816A_FMT_ALL | AD1816A_FMT_STEREO, | ||
251 | snd_ad1816a_get_format(chip, runtime->format, | ||
252 | runtime->channels)); | ||
253 | |||
254 | snd_ad1816a_write(chip, AD1816A_PLAYBACK_BASE_COUNT, | ||
255 | snd_pcm_lib_period_bytes(substream) / 4 - 1); | ||
256 | |||
257 | spin_unlock_irqrestore(&chip->lock, flags); | ||
258 | return 0; | ||
259 | } | ||
260 | |||
261 | static int snd_ad1816a_capture_prepare(snd_pcm_substream_t *substream) | ||
262 | { | ||
263 | ad1816a_t *chip = snd_pcm_substream_chip(substream); | ||
264 | unsigned long flags; | ||
265 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
266 | unsigned int size; | ||
267 | |||
268 | spin_lock_irqsave(&chip->lock, flags); | ||
269 | |||
270 | chip->c_dma_size = size = snd_pcm_lib_buffer_bytes(substream); | ||
271 | snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG, | ||
272 | AD1816A_CAPTURE_ENABLE | AD1816A_CAPTURE_PIO, 0x00); | ||
273 | |||
274 | snd_dma_program(chip->dma2, runtime->dma_addr, size, | ||
275 | DMA_MODE_READ | DMA_AUTOINIT); | ||
276 | |||
277 | snd_ad1816a_write(chip, AD1816A_CAPTURE_SAMPLE_RATE, runtime->rate); | ||
278 | snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG, | ||
279 | AD1816A_FMT_ALL | AD1816A_FMT_STEREO, | ||
280 | snd_ad1816a_get_format(chip, runtime->format, | ||
281 | runtime->channels)); | ||
282 | |||
283 | snd_ad1816a_write(chip, AD1816A_CAPTURE_BASE_COUNT, | ||
284 | snd_pcm_lib_period_bytes(substream) / 4 - 1); | ||
285 | |||
286 | spin_unlock_irqrestore(&chip->lock, flags); | ||
287 | return 0; | ||
288 | } | ||
289 | |||
290 | |||
291 | static snd_pcm_uframes_t snd_ad1816a_playback_pointer(snd_pcm_substream_t *substream) | ||
292 | { | ||
293 | ad1816a_t *chip = snd_pcm_substream_chip(substream); | ||
294 | size_t ptr; | ||
295 | if (!(chip->mode & AD1816A_MODE_PLAYBACK)) | ||
296 | return 0; | ||
297 | ptr = snd_dma_pointer(chip->dma1, chip->p_dma_size); | ||
298 | return bytes_to_frames(substream->runtime, ptr); | ||
299 | } | ||
300 | |||
301 | static snd_pcm_uframes_t snd_ad1816a_capture_pointer(snd_pcm_substream_t *substream) | ||
302 | { | ||
303 | ad1816a_t *chip = snd_pcm_substream_chip(substream); | ||
304 | size_t ptr; | ||
305 | if (!(chip->mode & AD1816A_MODE_CAPTURE)) | ||
306 | return 0; | ||
307 | ptr = snd_dma_pointer(chip->dma2, chip->c_dma_size); | ||
308 | return bytes_to_frames(substream->runtime, ptr); | ||
309 | } | ||
310 | |||
311 | |||
312 | static irqreturn_t snd_ad1816a_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
313 | { | ||
314 | ad1816a_t *chip = dev_id; | ||
315 | unsigned char status; | ||
316 | |||
317 | spin_lock(&chip->lock); | ||
318 | status = snd_ad1816a_in(chip, AD1816A_INTERRUPT_STATUS); | ||
319 | spin_unlock(&chip->lock); | ||
320 | |||
321 | if ((status & AD1816A_PLAYBACK_IRQ_PENDING) && chip->playback_substream) | ||
322 | snd_pcm_period_elapsed(chip->playback_substream); | ||
323 | |||
324 | if ((status & AD1816A_CAPTURE_IRQ_PENDING) && chip->capture_substream) | ||
325 | snd_pcm_period_elapsed(chip->capture_substream); | ||
326 | |||
327 | if ((status & AD1816A_TIMER_IRQ_PENDING) && chip->timer) | ||
328 | snd_timer_interrupt(chip->timer, chip->timer->sticks); | ||
329 | |||
330 | spin_lock(&chip->lock); | ||
331 | snd_ad1816a_out(chip, AD1816A_INTERRUPT_STATUS, 0x00); | ||
332 | spin_unlock(&chip->lock); | ||
333 | return IRQ_HANDLED; | ||
334 | } | ||
335 | |||
336 | |||
337 | static snd_pcm_hardware_t snd_ad1816a_playback = { | ||
338 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||
339 | SNDRV_PCM_INFO_MMAP_VALID), | ||
340 | .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | | ||
341 | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | | ||
342 | SNDRV_PCM_FMTBIT_S16_BE), | ||
343 | .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, | ||
344 | .rate_min = 4000, | ||
345 | .rate_max = 55200, | ||
346 | .channels_min = 1, | ||
347 | .channels_max = 2, | ||
348 | .buffer_bytes_max = (128*1024), | ||
349 | .period_bytes_min = 64, | ||
350 | .period_bytes_max = (128*1024), | ||
351 | .periods_min = 1, | ||
352 | .periods_max = 1024, | ||
353 | .fifo_size = 0, | ||
354 | }; | ||
355 | |||
356 | static snd_pcm_hardware_t snd_ad1816a_capture = { | ||
357 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||
358 | SNDRV_PCM_INFO_MMAP_VALID), | ||
359 | .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | | ||
360 | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | | ||
361 | SNDRV_PCM_FMTBIT_S16_BE), | ||
362 | .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, | ||
363 | .rate_min = 4000, | ||
364 | .rate_max = 55200, | ||
365 | .channels_min = 1, | ||
366 | .channels_max = 2, | ||
367 | .buffer_bytes_max = (128*1024), | ||
368 | .period_bytes_min = 64, | ||
369 | .period_bytes_max = (128*1024), | ||
370 | .periods_min = 1, | ||
371 | .periods_max = 1024, | ||
372 | .fifo_size = 0, | ||
373 | }; | ||
374 | |||
375 | #if 0 /* not used now */ | ||
376 | static int snd_ad1816a_timer_close(snd_timer_t *timer) | ||
377 | { | ||
378 | ad1816a_t *chip = snd_timer_chip(timer); | ||
379 | snd_ad1816a_close(chip, AD1816A_MODE_TIMER); | ||
380 | return 0; | ||
381 | } | ||
382 | |||
383 | static int snd_ad1816a_timer_open(snd_timer_t *timer) | ||
384 | { | ||
385 | ad1816a_t *chip = snd_timer_chip(timer); | ||
386 | snd_ad1816a_open(chip, AD1816A_MODE_TIMER); | ||
387 | return 0; | ||
388 | } | ||
389 | |||
390 | static unsigned long snd_ad1816a_timer_resolution(snd_timer_t *timer) | ||
391 | { | ||
392 | snd_assert(timer != NULL, return 0); | ||
393 | |||
394 | return 10000; | ||
395 | } | ||
396 | |||
397 | static int snd_ad1816a_timer_start(snd_timer_t *timer) | ||
398 | { | ||
399 | unsigned short bits; | ||
400 | unsigned long flags; | ||
401 | ad1816a_t *chip = snd_timer_chip(timer); | ||
402 | spin_lock_irqsave(&chip->lock, flags); | ||
403 | bits = snd_ad1816a_read(chip, AD1816A_INTERRUPT_ENABLE); | ||
404 | |||
405 | if (!(bits & AD1816A_TIMER_ENABLE)) { | ||
406 | snd_ad1816a_write(chip, AD1816A_TIMER_BASE_COUNT, | ||
407 | timer->sticks & 0xffff); | ||
408 | |||
409 | snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, | ||
410 | AD1816A_TIMER_ENABLE, 0xffff); | ||
411 | } | ||
412 | spin_unlock_irqrestore(&chip->lock, flags); | ||
413 | return 0; | ||
414 | } | ||
415 | |||
416 | static int snd_ad1816a_timer_stop(snd_timer_t *timer) | ||
417 | { | ||
418 | unsigned long flags; | ||
419 | ad1816a_t *chip = snd_timer_chip(timer); | ||
420 | spin_lock_irqsave(&chip->lock, flags); | ||
421 | |||
422 | snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE, | ||
423 | AD1816A_TIMER_ENABLE, 0x0000); | ||
424 | |||
425 | spin_unlock_irqrestore(&chip->lock, flags); | ||
426 | return 0; | ||
427 | } | ||
428 | |||
429 | static struct _snd_timer_hardware snd_ad1816a_timer_table = { | ||
430 | .flags = SNDRV_TIMER_HW_AUTO, | ||
431 | .resolution = 10000, | ||
432 | .ticks = 65535, | ||
433 | .open = snd_ad1816a_timer_open, | ||
434 | .close = snd_ad1816a_timer_close, | ||
435 | .c_resolution = snd_ad1816a_timer_resolution, | ||
436 | .start = snd_ad1816a_timer_start, | ||
437 | .stop = snd_ad1816a_timer_stop, | ||
438 | }; | ||
439 | #endif /* not used now */ | ||
440 | |||
441 | |||
442 | static int snd_ad1816a_playback_open(snd_pcm_substream_t *substream) | ||
443 | { | ||
444 | ad1816a_t *chip = snd_pcm_substream_chip(substream); | ||
445 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
446 | int error; | ||
447 | |||
448 | if ((error = snd_ad1816a_open(chip, AD1816A_MODE_PLAYBACK)) < 0) | ||
449 | return error; | ||
450 | snd_pcm_set_sync(substream); | ||
451 | runtime->hw = snd_ad1816a_playback; | ||
452 | snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.buffer_bytes_max); | ||
453 | snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.period_bytes_max); | ||
454 | chip->playback_substream = substream; | ||
455 | return 0; | ||
456 | } | ||
457 | |||
458 | static int snd_ad1816a_capture_open(snd_pcm_substream_t *substream) | ||
459 | { | ||
460 | ad1816a_t *chip = snd_pcm_substream_chip(substream); | ||
461 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
462 | int error; | ||
463 | |||
464 | if ((error = snd_ad1816a_open(chip, AD1816A_MODE_CAPTURE)) < 0) | ||
465 | return error; | ||
466 | snd_pcm_set_sync(substream); | ||
467 | runtime->hw = snd_ad1816a_capture; | ||
468 | snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.buffer_bytes_max); | ||
469 | snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.period_bytes_max); | ||
470 | chip->capture_substream = substream; | ||
471 | return 0; | ||
472 | } | ||
473 | |||
474 | static int snd_ad1816a_playback_close(snd_pcm_substream_t *substream) | ||
475 | { | ||
476 | ad1816a_t *chip = snd_pcm_substream_chip(substream); | ||
477 | |||
478 | chip->playback_substream = NULL; | ||
479 | snd_ad1816a_close(chip, AD1816A_MODE_PLAYBACK); | ||
480 | return 0; | ||
481 | } | ||
482 | |||
483 | static int snd_ad1816a_capture_close(snd_pcm_substream_t *substream) | ||
484 | { | ||
485 | ad1816a_t *chip = snd_pcm_substream_chip(substream); | ||
486 | |||
487 | chip->capture_substream = NULL; | ||
488 | snd_ad1816a_close(chip, AD1816A_MODE_CAPTURE); | ||
489 | return 0; | ||
490 | } | ||
491 | |||
492 | |||
493 | static void snd_ad1816a_init(ad1816a_t *chip) | ||
494 | { | ||
495 | unsigned long flags; | ||
496 | |||
497 | spin_lock_irqsave(&chip->lock, flags); | ||
498 | |||
499 | snd_ad1816a_out(chip, AD1816A_INTERRUPT_STATUS, 0x00); | ||
500 | snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG, | ||
501 | AD1816A_PLAYBACK_ENABLE | AD1816A_PLAYBACK_PIO, 0x00); | ||
502 | snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG, | ||
503 | AD1816A_CAPTURE_ENABLE | AD1816A_CAPTURE_PIO, 0x00); | ||
504 | snd_ad1816a_write(chip, AD1816A_INTERRUPT_ENABLE, 0x0000); | ||
505 | snd_ad1816a_write_mask(chip, AD1816A_CHIP_CONFIG, | ||
506 | AD1816A_CAPTURE_NOT_EQUAL | AD1816A_WSS_ENABLE, 0xffff); | ||
507 | snd_ad1816a_write(chip, AD1816A_DSP_CONFIG, 0x0000); | ||
508 | snd_ad1816a_write(chip, AD1816A_POWERDOWN_CTRL, 0x0000); | ||
509 | |||
510 | spin_unlock_irqrestore(&chip->lock, flags); | ||
511 | } | ||
512 | |||
513 | static int snd_ad1816a_probe(ad1816a_t *chip) | ||
514 | { | ||
515 | unsigned long flags; | ||
516 | |||
517 | spin_lock_irqsave(&chip->lock, flags); | ||
518 | |||
519 | switch (chip->version = snd_ad1816a_read(chip, AD1816A_VERSION_ID)) { | ||
520 | case 0: | ||
521 | chip->hardware = AD1816A_HW_AD1815; | ||
522 | break; | ||
523 | case 1: | ||
524 | chip->hardware = AD1816A_HW_AD18MAX10; | ||
525 | break; | ||
526 | case 3: | ||
527 | chip->hardware = AD1816A_HW_AD1816A; | ||
528 | break; | ||
529 | default: | ||
530 | chip->hardware = AD1816A_HW_AUTO; | ||
531 | } | ||
532 | |||
533 | spin_unlock_irqrestore(&chip->lock, flags); | ||
534 | return 0; | ||
535 | } | ||
536 | |||
537 | static int snd_ad1816a_free(ad1816a_t *chip) | ||
538 | { | ||
539 | if (chip->res_port) { | ||
540 | release_resource(chip->res_port); | ||
541 | kfree_nocheck(chip->res_port); | ||
542 | } | ||
543 | if (chip->irq >= 0) | ||
544 | free_irq(chip->irq, (void *) chip); | ||
545 | if (chip->dma1 >= 0) { | ||
546 | snd_dma_disable(chip->dma1); | ||
547 | free_dma(chip->dma1); | ||
548 | } | ||
549 | if (chip->dma2 >= 0) { | ||
550 | snd_dma_disable(chip->dma2); | ||
551 | free_dma(chip->dma2); | ||
552 | } | ||
553 | kfree(chip); | ||
554 | return 0; | ||
555 | } | ||
556 | |||
557 | static int snd_ad1816a_dev_free(snd_device_t *device) | ||
558 | { | ||
559 | ad1816a_t *chip = device->device_data; | ||
560 | return snd_ad1816a_free(chip); | ||
561 | } | ||
562 | |||
563 | static const char *snd_ad1816a_chip_id(ad1816a_t *chip) | ||
564 | { | ||
565 | switch (chip->hardware) { | ||
566 | case AD1816A_HW_AD1816A: return "AD1816A"; | ||
567 | case AD1816A_HW_AD1815: return "AD1815"; | ||
568 | case AD1816A_HW_AD18MAX10: return "AD18max10"; | ||
569 | default: | ||
570 | snd_printk("Unknown chip version %d:%d.\n", | ||
571 | chip->version, chip->hardware); | ||
572 | return "AD1816A - unknown"; | ||
573 | } | ||
574 | } | ||
575 | |||
576 | int snd_ad1816a_create(snd_card_t *card, | ||
577 | unsigned long port, int irq, int dma1, int dma2, | ||
578 | ad1816a_t **rchip) | ||
579 | { | ||
580 | static snd_device_ops_t ops = { | ||
581 | .dev_free = snd_ad1816a_dev_free, | ||
582 | }; | ||
583 | int error; | ||
584 | ad1816a_t *chip; | ||
585 | |||
586 | *rchip = NULL; | ||
587 | |||
588 | chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); | ||
589 | if (chip == NULL) | ||
590 | return -ENOMEM; | ||
591 | chip->irq = -1; | ||
592 | chip->dma1 = -1; | ||
593 | chip->dma2 = -1; | ||
594 | |||
595 | if ((chip->res_port = request_region(port, 16, "AD1816A")) == NULL) { | ||
596 | snd_printk(KERN_ERR "ad1816a: can't grab port 0x%lx\n", port); | ||
597 | snd_ad1816a_free(chip); | ||
598 | return -EBUSY; | ||
599 | } | ||
600 | if (request_irq(irq, snd_ad1816a_interrupt, SA_INTERRUPT, "AD1816A", (void *) chip)) { | ||
601 | snd_printk(KERN_ERR "ad1816a: can't grab IRQ %d\n", irq); | ||
602 | snd_ad1816a_free(chip); | ||
603 | return -EBUSY; | ||
604 | } | ||
605 | chip->irq = irq; | ||
606 | if (request_dma(dma1, "AD1816A - 1")) { | ||
607 | snd_printk(KERN_ERR "ad1816a: can't grab DMA1 %d\n", dma1); | ||
608 | snd_ad1816a_free(chip); | ||
609 | return -EBUSY; | ||
610 | } | ||
611 | chip->dma1 = dma1; | ||
612 | if (request_dma(dma2, "AD1816A - 2")) { | ||
613 | snd_printk(KERN_ERR "ad1816a: can't grab DMA2 %d\n", dma2); | ||
614 | snd_ad1816a_free(chip); | ||
615 | return -EBUSY; | ||
616 | } | ||
617 | chip->dma2 = dma2; | ||
618 | |||
619 | chip->card = card; | ||
620 | chip->port = port; | ||
621 | spin_lock_init(&chip->lock); | ||
622 | |||
623 | if ((error = snd_ad1816a_probe(chip))) { | ||
624 | snd_ad1816a_free(chip); | ||
625 | return error; | ||
626 | } | ||
627 | |||
628 | snd_ad1816a_init(chip); | ||
629 | |||
630 | /* Register device */ | ||
631 | if ((error = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { | ||
632 | snd_ad1816a_free(chip); | ||
633 | return error; | ||
634 | } | ||
635 | |||
636 | *rchip = chip; | ||
637 | return 0; | ||
638 | } | ||
639 | |||
640 | static snd_pcm_ops_t snd_ad1816a_playback_ops = { | ||
641 | .open = snd_ad1816a_playback_open, | ||
642 | .close = snd_ad1816a_playback_close, | ||
643 | .ioctl = snd_pcm_lib_ioctl, | ||
644 | .hw_params = snd_ad1816a_hw_params, | ||
645 | .hw_free = snd_ad1816a_hw_free, | ||
646 | .prepare = snd_ad1816a_playback_prepare, | ||
647 | .trigger = snd_ad1816a_playback_trigger, | ||
648 | .pointer = snd_ad1816a_playback_pointer, | ||
649 | }; | ||
650 | |||
651 | static snd_pcm_ops_t snd_ad1816a_capture_ops = { | ||
652 | .open = snd_ad1816a_capture_open, | ||
653 | .close = snd_ad1816a_capture_close, | ||
654 | .ioctl = snd_pcm_lib_ioctl, | ||
655 | .hw_params = snd_ad1816a_hw_params, | ||
656 | .hw_free = snd_ad1816a_hw_free, | ||
657 | .prepare = snd_ad1816a_capture_prepare, | ||
658 | .trigger = snd_ad1816a_capture_trigger, | ||
659 | .pointer = snd_ad1816a_capture_pointer, | ||
660 | }; | ||
661 | |||
662 | static void snd_ad1816a_pcm_free(snd_pcm_t *pcm) | ||
663 | { | ||
664 | ad1816a_t *chip = pcm->private_data; | ||
665 | chip->pcm = NULL; | ||
666 | snd_pcm_lib_preallocate_free_for_all(pcm); | ||
667 | } | ||
668 | |||
669 | int snd_ad1816a_pcm(ad1816a_t *chip, int device, snd_pcm_t **rpcm) | ||
670 | { | ||
671 | int error; | ||
672 | snd_pcm_t *pcm; | ||
673 | |||
674 | if ((error = snd_pcm_new(chip->card, "AD1816A", device, 1, 1, &pcm))) | ||
675 | return error; | ||
676 | |||
677 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ad1816a_playback_ops); | ||
678 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ad1816a_capture_ops); | ||
679 | |||
680 | pcm->private_data = chip; | ||
681 | pcm->private_free = snd_ad1816a_pcm_free; | ||
682 | pcm->info_flags = (chip->dma1 == chip->dma2 ) ? SNDRV_PCM_INFO_JOINT_DUPLEX : 0; | ||
683 | |||
684 | strcpy(pcm->name, snd_ad1816a_chip_id(chip)); | ||
685 | snd_ad1816a_init(chip); | ||
686 | |||
687 | snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, | ||
688 | snd_dma_isa_data(), | ||
689 | 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024); | ||
690 | |||
691 | chip->pcm = pcm; | ||
692 | if (rpcm) | ||
693 | *rpcm = pcm; | ||
694 | return 0; | ||
695 | } | ||
696 | |||
697 | #if 0 /* not used now */ | ||
698 | static void snd_ad1816a_timer_free(snd_timer_t *timer) | ||
699 | { | ||
700 | ad1816a_t *chip = timer->private_data; | ||
701 | chip->timer = NULL; | ||
702 | } | ||
703 | |||
704 | int snd_ad1816a_timer(ad1816a_t *chip, int device, snd_timer_t **rtimer) | ||
705 | { | ||
706 | snd_timer_t *timer; | ||
707 | snd_timer_id_t tid; | ||
708 | int error; | ||
709 | |||
710 | tid.dev_class = SNDRV_TIMER_CLASS_CARD; | ||
711 | tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; | ||
712 | tid.card = chip->card->number; | ||
713 | tid.device = device; | ||
714 | tid.subdevice = 0; | ||
715 | if ((error = snd_timer_new(chip->card, "AD1816A", &tid, &timer)) < 0) | ||
716 | return error; | ||
717 | strcpy(timer->name, snd_ad1816a_chip_id(chip)); | ||
718 | timer->private_data = chip; | ||
719 | timer->private_free = snd_ad1816a_timer_free; | ||
720 | chip->timer = timer; | ||
721 | timer->hw = snd_ad1816a_timer_table; | ||
722 | if (rtimer) | ||
723 | *rtimer = timer; | ||
724 | return 0; | ||
725 | } | ||
726 | #endif /* not used now */ | ||
727 | |||
728 | /* | ||
729 | * | ||
730 | */ | ||
731 | |||
732 | static int snd_ad1816a_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
733 | { | ||
734 | static char *texts[8] = { | ||
735 | "Line", "Mix", "CD", "Synth", "Video", | ||
736 | "Mic", "Phone", | ||
737 | }; | ||
738 | |||
739 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | ||
740 | uinfo->count = 2; | ||
741 | uinfo->value.enumerated.items = 7; | ||
742 | if (uinfo->value.enumerated.item > 6) | ||
743 | uinfo->value.enumerated.item = 6; | ||
744 | strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); | ||
745 | return 0; | ||
746 | } | ||
747 | |||
748 | static int snd_ad1816a_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
749 | { | ||
750 | ad1816a_t *chip = snd_kcontrol_chip(kcontrol); | ||
751 | unsigned long flags; | ||
752 | unsigned short val; | ||
753 | |||
754 | spin_lock_irqsave(&chip->lock, flags); | ||
755 | val = snd_ad1816a_read(chip, AD1816A_ADC_SOURCE_SEL); | ||
756 | spin_unlock_irqrestore(&chip->lock, flags); | ||
757 | ucontrol->value.enumerated.item[0] = (val >> 12) & 7; | ||
758 | ucontrol->value.enumerated.item[1] = (val >> 4) & 7; | ||
759 | return 0; | ||
760 | } | ||
761 | |||
762 | static int snd_ad1816a_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
763 | { | ||
764 | ad1816a_t *chip = snd_kcontrol_chip(kcontrol); | ||
765 | unsigned long flags; | ||
766 | unsigned short val; | ||
767 | int change; | ||
768 | |||
769 | if (ucontrol->value.enumerated.item[0] > 6 || | ||
770 | ucontrol->value.enumerated.item[1] > 6) | ||
771 | return -EINVAL; | ||
772 | val = (ucontrol->value.enumerated.item[0] << 12) | | ||
773 | (ucontrol->value.enumerated.item[1] << 4); | ||
774 | spin_lock_irqsave(&chip->lock, flags); | ||
775 | change = snd_ad1816a_read(chip, AD1816A_ADC_SOURCE_SEL) != val; | ||
776 | snd_ad1816a_write(chip, AD1816A_ADC_SOURCE_SEL, val); | ||
777 | spin_unlock_irqrestore(&chip->lock, flags); | ||
778 | return change; | ||
779 | } | ||
780 | |||
781 | #define AD1816A_SINGLE(xname, reg, shift, mask, invert) \ | ||
782 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ad1816a_info_single, \ | ||
783 | .get = snd_ad1816a_get_single, .put = snd_ad1816a_put_single, \ | ||
784 | .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } | ||
785 | |||
786 | static int snd_ad1816a_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
787 | { | ||
788 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
789 | |||
790 | uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
791 | uinfo->count = 1; | ||
792 | uinfo->value.integer.min = 0; | ||
793 | uinfo->value.integer.max = mask; | ||
794 | return 0; | ||
795 | } | ||
796 | |||
797 | static int snd_ad1816a_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
798 | { | ||
799 | ad1816a_t *chip = snd_kcontrol_chip(kcontrol); | ||
800 | unsigned long flags; | ||
801 | int reg = kcontrol->private_value & 0xff; | ||
802 | int shift = (kcontrol->private_value >> 8) & 0xff; | ||
803 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
804 | int invert = (kcontrol->private_value >> 24) & 0xff; | ||
805 | |||
806 | spin_lock_irqsave(&chip->lock, flags); | ||
807 | ucontrol->value.integer.value[0] = (snd_ad1816a_read(chip, reg) >> shift) & mask; | ||
808 | spin_unlock_irqrestore(&chip->lock, flags); | ||
809 | if (invert) | ||
810 | ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; | ||
811 | return 0; | ||
812 | } | ||
813 | |||
814 | static int snd_ad1816a_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
815 | { | ||
816 | ad1816a_t *chip = snd_kcontrol_chip(kcontrol); | ||
817 | unsigned long flags; | ||
818 | int reg = kcontrol->private_value & 0xff; | ||
819 | int shift = (kcontrol->private_value >> 8) & 0xff; | ||
820 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
821 | int invert = (kcontrol->private_value >> 24) & 0xff; | ||
822 | int change; | ||
823 | unsigned short old_val, val; | ||
824 | |||
825 | val = (ucontrol->value.integer.value[0] & mask); | ||
826 | if (invert) | ||
827 | val = mask - val; | ||
828 | val <<= shift; | ||
829 | spin_lock_irqsave(&chip->lock, flags); | ||
830 | old_val = snd_ad1816a_read(chip, reg); | ||
831 | val = (old_val & ~(mask << shift)) | val; | ||
832 | change = val != old_val; | ||
833 | snd_ad1816a_write(chip, reg, val); | ||
834 | spin_unlock_irqrestore(&chip->lock, flags); | ||
835 | return change; | ||
836 | } | ||
837 | |||
838 | #define AD1816A_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \ | ||
839 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ad1816a_info_double, \ | ||
840 | .get = snd_ad1816a_get_double, .put = snd_ad1816a_put_double, \ | ||
841 | .private_value = reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24) } | ||
842 | |||
843 | static int snd_ad1816a_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
844 | { | ||
845 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
846 | |||
847 | uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
848 | uinfo->count = 2; | ||
849 | uinfo->value.integer.min = 0; | ||
850 | uinfo->value.integer.max = mask; | ||
851 | return 0; | ||
852 | } | ||
853 | |||
854 | static int snd_ad1816a_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
855 | { | ||
856 | ad1816a_t *chip = snd_kcontrol_chip(kcontrol); | ||
857 | unsigned long flags; | ||
858 | int reg = kcontrol->private_value & 0xff; | ||
859 | int shift_left = (kcontrol->private_value >> 8) & 0x0f; | ||
860 | int shift_right = (kcontrol->private_value >> 12) & 0x0f; | ||
861 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
862 | int invert = (kcontrol->private_value >> 24) & 0xff; | ||
863 | unsigned short val; | ||
864 | |||
865 | spin_lock_irqsave(&chip->lock, flags); | ||
866 | val = snd_ad1816a_read(chip, reg); | ||
867 | ucontrol->value.integer.value[0] = (val >> shift_left) & mask; | ||
868 | ucontrol->value.integer.value[1] = (val >> shift_right) & mask; | ||
869 | spin_unlock_irqrestore(&chip->lock, flags); | ||
870 | if (invert) { | ||
871 | ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; | ||
872 | ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; | ||
873 | } | ||
874 | return 0; | ||
875 | } | ||
876 | |||
877 | static int snd_ad1816a_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
878 | { | ||
879 | ad1816a_t *chip = snd_kcontrol_chip(kcontrol); | ||
880 | unsigned long flags; | ||
881 | int reg = kcontrol->private_value & 0xff; | ||
882 | int shift_left = (kcontrol->private_value >> 8) & 0x0f; | ||
883 | int shift_right = (kcontrol->private_value >> 12) & 0x0f; | ||
884 | int mask = (kcontrol->private_value >> 16) & 0xff; | ||
885 | int invert = (kcontrol->private_value >> 24) & 0xff; | ||
886 | int change; | ||
887 | unsigned short old_val, val1, val2; | ||
888 | |||
889 | val1 = ucontrol->value.integer.value[0] & mask; | ||
890 | val2 = ucontrol->value.integer.value[1] & mask; | ||
891 | if (invert) { | ||
892 | val1 = mask - val1; | ||
893 | val2 = mask - val2; | ||
894 | } | ||
895 | val1 <<= shift_left; | ||
896 | val2 <<= shift_right; | ||
897 | spin_lock_irqsave(&chip->lock, flags); | ||
898 | old_val = snd_ad1816a_read(chip, reg); | ||
899 | val1 = (old_val & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2; | ||
900 | change = val1 != old_val; | ||
901 | snd_ad1816a_write(chip, reg, val1); | ||
902 | spin_unlock_irqrestore(&chip->lock, flags); | ||
903 | return change; | ||
904 | } | ||
905 | |||
906 | static snd_kcontrol_new_t snd_ad1816a_controls[] = { | ||
907 | AD1816A_DOUBLE("Master Playback Switch", AD1816A_MASTER_ATT, 15, 7, 1, 1), | ||
908 | AD1816A_DOUBLE("Master Playback Volume", AD1816A_MASTER_ATT, 8, 0, 31, 1), | ||
909 | AD1816A_DOUBLE("PCM Playback Switch", AD1816A_VOICE_ATT, 15, 7, 1, 1), | ||
910 | AD1816A_DOUBLE("PCM Playback Volume", AD1816A_VOICE_ATT, 8, 0, 63, 1), | ||
911 | AD1816A_DOUBLE("Line Playback Switch", AD1816A_LINE_GAIN_ATT, 15, 7, 1, 1), | ||
912 | AD1816A_DOUBLE("Line Playback Volume", AD1816A_LINE_GAIN_ATT, 8, 0, 31, 1), | ||
913 | AD1816A_DOUBLE("CD Playback Switch", AD1816A_CD_GAIN_ATT, 15, 7, 1, 1), | ||
914 | AD1816A_DOUBLE("CD Playback Volume", AD1816A_CD_GAIN_ATT, 8, 0, 31, 1), | ||
915 | AD1816A_DOUBLE("Synth Playback Switch", AD1816A_SYNTH_GAIN_ATT, 15, 7, 1, 1), | ||
916 | AD1816A_DOUBLE("Synth Playback Volume", AD1816A_SYNTH_GAIN_ATT, 8, 0, 31, 1), | ||
917 | AD1816A_DOUBLE("FM Playback Switch", AD1816A_FM_ATT, 15, 7, 1, 1), | ||
918 | AD1816A_DOUBLE("FM Playback Volume", AD1816A_FM_ATT, 8, 0, 63, 1), | ||
919 | AD1816A_SINGLE("Mic Playback Switch", AD1816A_MIC_GAIN_ATT, 15, 1, 1), | ||
920 | AD1816A_SINGLE("Mic Playback Volume", AD1816A_MIC_GAIN_ATT, 8, 31, 1), | ||
921 | AD1816A_SINGLE("Mic Boost", AD1816A_MIC_GAIN_ATT, 14, 1, 0), | ||
922 | AD1816A_DOUBLE("Video Playback Switch", AD1816A_VID_GAIN_ATT, 15, 7, 1, 1), | ||
923 | AD1816A_DOUBLE("Video Playback Volume", AD1816A_VID_GAIN_ATT, 8, 0, 31, 1), | ||
924 | AD1816A_SINGLE("Phone Capture Switch", AD1816A_PHONE_IN_GAIN_ATT, 15, 1, 1), | ||
925 | AD1816A_SINGLE("Phone Capture Volume", AD1816A_PHONE_IN_GAIN_ATT, 0, 15, 1), | ||
926 | AD1816A_SINGLE("Phone Playback Switch", AD1816A_PHONE_OUT_ATT, 7, 1, 1), | ||
927 | AD1816A_SINGLE("Phone Playback Volume", AD1816A_PHONE_OUT_ATT, 0, 31, 1), | ||
928 | { | ||
929 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
930 | .name = "Capture Source", | ||
931 | .info = snd_ad1816a_info_mux, | ||
932 | .get = snd_ad1816a_get_mux, | ||
933 | .put = snd_ad1816a_put_mux, | ||
934 | }, | ||
935 | AD1816A_DOUBLE("Capture Switch", AD1816A_ADC_PGA, 15, 7, 1, 1), | ||
936 | AD1816A_DOUBLE("Capture Volume", AD1816A_ADC_PGA, 8, 0, 15, 0), | ||
937 | AD1816A_SINGLE("3D Control - Switch", AD1816A_3D_PHAT_CTRL, 15, 1, 1), | ||
938 | AD1816A_SINGLE("3D Control - Level", AD1816A_3D_PHAT_CTRL, 0, 15, 0), | ||
939 | }; | ||
940 | |||
941 | int snd_ad1816a_mixer(ad1816a_t *chip) | ||
942 | { | ||
943 | snd_card_t *card; | ||
944 | unsigned int idx; | ||
945 | int err; | ||
946 | |||
947 | snd_assert(chip != NULL && chip->card != NULL, return -EINVAL); | ||
948 | |||
949 | card = chip->card; | ||
950 | |||
951 | strcpy(card->mixername, snd_ad1816a_chip_id(chip)); | ||
952 | |||
953 | for (idx = 0; idx < ARRAY_SIZE(snd_ad1816a_controls); idx++) { | ||
954 | if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ad1816a_controls[idx], chip))) < 0) | ||
955 | return err; | ||
956 | } | ||
957 | return 0; | ||
958 | } | ||
959 | |||
960 | EXPORT_SYMBOL(snd_ad1816a_create); | ||
961 | EXPORT_SYMBOL(snd_ad1816a_pcm); | ||
962 | EXPORT_SYMBOL(snd_ad1816a_mixer); | ||
963 | |||
964 | static int __init alsa_ad1816a_init(void) | ||
965 | { | ||
966 | return 0; | ||
967 | } | ||
968 | |||
969 | static void __exit alsa_ad1816a_exit(void) | ||
970 | { | ||
971 | } | ||
972 | |||
973 | module_init(alsa_ad1816a_init) | ||
974 | module_exit(alsa_ad1816a_exit) | ||