blob: 8094282c2ae1992d76f588018b28ad2390d44e3e [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
3 * Routines for control of AD1848/AD1847/CS4248
4 *
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
22#define SNDRV_MAIN_OBJECT_FILE
23#include <sound/driver.h>
24#include <linux/delay.h>
25#include <linux/init.h>
26#include <linux/interrupt.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070027#include <linux/slab.h>
28#include <linux/ioport.h>
29#include <sound/core.h>
30#include <sound/ad1848.h>
31#include <sound/control.h>
Takashi Iwaieac06a12006-08-22 13:16:25 +020032#include <sound/tlv.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070033#include <sound/pcm_params.h>
34
35#include <asm/io.h>
36#include <asm/dma.h>
37
38MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
39MODULE_DESCRIPTION("Routines for control of AD1848/AD1847/CS4248");
40MODULE_LICENSE("GPL");
41
42#if 0
43#define SNDRV_DEBUG_MCE
44#endif
45
46/*
47 * Some variables
48 */
49
50static unsigned char freq_bits[14] = {
51 /* 5510 */ 0x00 | AD1848_XTAL2,
52 /* 6620 */ 0x0E | AD1848_XTAL2,
53 /* 8000 */ 0x00 | AD1848_XTAL1,
54 /* 9600 */ 0x0E | AD1848_XTAL1,
55 /* 11025 */ 0x02 | AD1848_XTAL2,
56 /* 16000 */ 0x02 | AD1848_XTAL1,
57 /* 18900 */ 0x04 | AD1848_XTAL2,
58 /* 22050 */ 0x06 | AD1848_XTAL2,
59 /* 27042 */ 0x04 | AD1848_XTAL1,
60 /* 32000 */ 0x06 | AD1848_XTAL1,
61 /* 33075 */ 0x0C | AD1848_XTAL2,
62 /* 37800 */ 0x08 | AD1848_XTAL2,
63 /* 44100 */ 0x0A | AD1848_XTAL2,
64 /* 48000 */ 0x0C | AD1848_XTAL1
65};
66
67static unsigned int rates[14] = {
68 5510, 6620, 8000, 9600, 11025, 16000, 18900, 22050,
69 27042, 32000, 33075, 37800, 44100, 48000
70};
71
Takashi Iwaic8ff6642005-11-17 14:29:37 +010072static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070073 .count = 14,
74 .list = rates,
75 .mask = 0,
76};
77
78static unsigned char snd_ad1848_original_image[16] =
79{
80 0x00, /* 00 - lic */
81 0x00, /* 01 - ric */
82 0x9f, /* 02 - la1ic */
83 0x9f, /* 03 - ra1ic */
84 0x9f, /* 04 - la2ic */
85 0x9f, /* 05 - ra2ic */
86 0xbf, /* 06 - loc */
87 0xbf, /* 07 - roc */
88 0x20, /* 08 - dfr */
89 AD1848_AUTOCALIB, /* 09 - ic */
90 0x00, /* 0a - pc */
91 0x00, /* 0b - ti */
92 0x00, /* 0c - mi */
93 0x00, /* 0d - lbc */
94 0x00, /* 0e - dru */
95 0x00, /* 0f - drl */
96};
97
98/*
99 * Basic I/O functions
100 */
101
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100102void snd_ad1848_out(struct snd_ad1848 *chip,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103 unsigned char reg,
104 unsigned char value)
105{
106 int timeout;
107
108 for (timeout = 250; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--)
109 udelay(100);
110#ifdef CONFIG_SND_DEBUG
111 if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT)
Takashi Iwai99b359b2005-10-20 18:26:44 +0200112 snd_printk(KERN_WARNING "auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113#endif
114 outb(chip->mce_bit | reg, AD1848P(chip, REGSEL));
115 outb(chip->image[reg] = value, AD1848P(chip, REG));
116 mb();
117#if 0
118 printk("codec out - reg 0x%x = 0x%x\n", chip->mce_bit | reg, value);
119#endif
120}
121
Takashi Iwaieac06a12006-08-22 13:16:25 +0200122EXPORT_SYMBOL(snd_ad1848_out);
123
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100124static void snd_ad1848_dout(struct snd_ad1848 *chip,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125 unsigned char reg, unsigned char value)
126{
127 int timeout;
128
129 for (timeout = 250; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--)
130 udelay(100);
131 outb(chip->mce_bit | reg, AD1848P(chip, REGSEL));
132 outb(value, AD1848P(chip, REG));
133 mb();
134}
135
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100136static unsigned char snd_ad1848_in(struct snd_ad1848 *chip, unsigned char reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137{
138 int timeout;
139
140 for (timeout = 250; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--)
141 udelay(100);
142#ifdef CONFIG_SND_DEBUG
143 if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT)
Takashi Iwai99b359b2005-10-20 18:26:44 +0200144 snd_printk(KERN_WARNING "auto calibration time out - reg = 0x%x\n", reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145#endif
146 outb(chip->mce_bit | reg, AD1848P(chip, REGSEL));
147 mb();
148 return inb(AD1848P(chip, REG));
149}
150
151#if 0
152
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100153static void snd_ad1848_debug(struct snd_ad1848 *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154{
155 printk("AD1848 REGS: INDEX = 0x%02x ", inb(AD1848P(chip, REGSEL)));
156 printk(" STATUS = 0x%02x\n", inb(AD1848P(chip, STATUS)));
157 printk(" 0x00: left input = 0x%02x ", snd_ad1848_in(chip, 0x00));
158 printk(" 0x08: playback format = 0x%02x\n", snd_ad1848_in(chip, 0x08));
159 printk(" 0x01: right input = 0x%02x ", snd_ad1848_in(chip, 0x01));
160 printk(" 0x09: iface (CFIG 1) = 0x%02x\n", snd_ad1848_in(chip, 0x09));
161 printk(" 0x02: AUXA left = 0x%02x ", snd_ad1848_in(chip, 0x02));
162 printk(" 0x0a: pin control = 0x%02x\n", snd_ad1848_in(chip, 0x0a));
163 printk(" 0x03: AUXA right = 0x%02x ", snd_ad1848_in(chip, 0x03));
164 printk(" 0x0b: init & status = 0x%02x\n", snd_ad1848_in(chip, 0x0b));
165 printk(" 0x04: AUXB left = 0x%02x ", snd_ad1848_in(chip, 0x04));
166 printk(" 0x0c: revision & mode = 0x%02x\n", snd_ad1848_in(chip, 0x0c));
167 printk(" 0x05: AUXB right = 0x%02x ", snd_ad1848_in(chip, 0x05));
168 printk(" 0x0d: loopback = 0x%02x\n", snd_ad1848_in(chip, 0x0d));
169 printk(" 0x06: left output = 0x%02x ", snd_ad1848_in(chip, 0x06));
170 printk(" 0x0e: data upr count = 0x%02x\n", snd_ad1848_in(chip, 0x0e));
171 printk(" 0x07: right output = 0x%02x ", snd_ad1848_in(chip, 0x07));
172 printk(" 0x0f: data lwr count = 0x%02x\n", snd_ad1848_in(chip, 0x0f));
173}
174
175#endif
176
177/*
178 * AD1848 detection / MCE routines
179 */
180
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100181static void snd_ad1848_mce_up(struct snd_ad1848 *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182{
183 unsigned long flags;
184 int timeout;
185
186 for (timeout = 250; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--)
187 udelay(100);
188#ifdef CONFIG_SND_DEBUG
189 if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT)
Takashi Iwai99b359b2005-10-20 18:26:44 +0200190 snd_printk(KERN_WARNING "mce_up - auto calibration time out (0)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191#endif
192 spin_lock_irqsave(&chip->reg_lock, flags);
193 chip->mce_bit |= AD1848_MCE;
194 timeout = inb(AD1848P(chip, REGSEL));
195 if (timeout == 0x80)
Takashi Iwai99b359b2005-10-20 18:26:44 +0200196 snd_printk(KERN_WARNING "mce_up [0x%lx]: serious init problem - codec still busy\n", chip->port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197 if (!(timeout & AD1848_MCE))
198 outb(chip->mce_bit | (timeout & 0x1f), AD1848P(chip, REGSEL));
199 spin_unlock_irqrestore(&chip->reg_lock, flags);
200}
201
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100202static void snd_ad1848_mce_down(struct snd_ad1848 *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203{
204 unsigned long flags;
205 int timeout;
206 signed long time;
207
208 spin_lock_irqsave(&chip->reg_lock, flags);
209 for (timeout = 5; timeout > 0; timeout--)
210 inb(AD1848P(chip, REGSEL));
211 /* end of cleanup sequence */
212 for (timeout = 12000; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--)
213 udelay(100);
214#if 0
215 printk("(1) timeout = %i\n", timeout);
216#endif
217#ifdef CONFIG_SND_DEBUG
218 if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT)
Takashi Iwai99b359b2005-10-20 18:26:44 +0200219 snd_printk(KERN_WARNING "mce_down [0x%lx] - auto calibration time out (0)\n", AD1848P(chip, REGSEL));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220#endif
221 chip->mce_bit &= ~AD1848_MCE;
222 timeout = inb(AD1848P(chip, REGSEL));
223 outb(chip->mce_bit | (timeout & 0x1f), AD1848P(chip, REGSEL));
224 if (timeout == 0x80)
Takashi Iwai99b359b2005-10-20 18:26:44 +0200225 snd_printk(KERN_WARNING "mce_down [0x%lx]: serious init problem - codec still busy\n", chip->port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226 if ((timeout & AD1848_MCE) == 0) {
227 spin_unlock_irqrestore(&chip->reg_lock, flags);
228 return;
229 }
230 /* calibration process */
231
232 for (timeout = 500; timeout > 0 && (snd_ad1848_in(chip, AD1848_TEST_INIT) & AD1848_CALIB_IN_PROGRESS) == 0; timeout--);
233 if ((snd_ad1848_in(chip, AD1848_TEST_INIT) & AD1848_CALIB_IN_PROGRESS) == 0) {
234 snd_printd("mce_down - auto calibration time out (1)\n");
235 spin_unlock_irqrestore(&chip->reg_lock, flags);
236 return;
237 }
238#if 0
239 printk("(2) timeout = %i, jiffies = %li\n", timeout, jiffies);
240#endif
241 time = HZ / 4;
242 while (snd_ad1848_in(chip, AD1848_TEST_INIT) & AD1848_CALIB_IN_PROGRESS) {
243 spin_unlock_irqrestore(&chip->reg_lock, flags);
244 if (time <= 0) {
Takashi Iwai99b359b2005-10-20 18:26:44 +0200245 snd_printk(KERN_ERR "mce_down - auto calibration time out (2)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246 return;
247 }
Nishanth Aravamudan8433a502005-10-24 15:02:37 +0200248 time = schedule_timeout_interruptible(time);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249 spin_lock_irqsave(&chip->reg_lock, flags);
250 }
251#if 0
252 printk("(3) jiffies = %li\n", jiffies);
253#endif
254 time = HZ / 10;
255 while (inb(AD1848P(chip, REGSEL)) & AD1848_INIT) {
256 spin_unlock_irqrestore(&chip->reg_lock, flags);
257 if (time <= 0) {
Takashi Iwai99b359b2005-10-20 18:26:44 +0200258 snd_printk(KERN_ERR "mce_down - auto calibration time out (3)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259 return;
260 }
Nishanth Aravamudan8433a502005-10-24 15:02:37 +0200261 time = schedule_timeout_interruptible(time);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262 spin_lock_irqsave(&chip->reg_lock, flags);
263 }
264 spin_unlock_irqrestore(&chip->reg_lock, flags);
265#if 0
266 printk("(4) jiffies = %li\n", jiffies);
267 snd_printk("mce_down - exit = 0x%x\n", inb(AD1848P(chip, REGSEL)));
268#endif
269}
270
271static unsigned int snd_ad1848_get_count(unsigned char format,
272 unsigned int size)
273{
274 switch (format & 0xe0) {
275 case AD1848_LINEAR_16:
276 size >>= 1;
277 break;
278 }
279 if (format & AD1848_STEREO)
280 size >>= 1;
281 return size;
282}
283
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100284static int snd_ad1848_trigger(struct snd_ad1848 *chip, unsigned char what,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 int channel, int cmd)
286{
287 int result = 0;
288
289#if 0
290 printk("codec trigger!!! - what = %i, enable = %i, status = 0x%x\n", what, enable, inb(AD1848P(card, STATUS)));
291#endif
292 spin_lock(&chip->reg_lock);
293 if (cmd == SNDRV_PCM_TRIGGER_START) {
294 if (chip->image[AD1848_IFACE_CTRL] & what) {
295 spin_unlock(&chip->reg_lock);
296 return 0;
297 }
298 snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL] |= what);
299 chip->mode |= AD1848_MODE_RUNNING;
300 } else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
301 if (!(chip->image[AD1848_IFACE_CTRL] & what)) {
302 spin_unlock(&chip->reg_lock);
303 return 0;
304 }
305 snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL] &= ~what);
306 chip->mode &= ~AD1848_MODE_RUNNING;
307 } else {
308 result = -EINVAL;
309 }
310 spin_unlock(&chip->reg_lock);
311 return result;
312}
313
314/*
315 * CODEC I/O
316 */
317
318static unsigned char snd_ad1848_get_rate(unsigned int rate)
319{
320 int i;
321
322 for (i = 0; i < 14; i++)
323 if (rate == rates[i])
324 return freq_bits[i];
325 snd_BUG();
326 return freq_bits[13];
327}
328
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100329static int snd_ad1848_ioctl(struct snd_pcm_substream *substream,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330 unsigned int cmd, void *arg)
331{
332 return snd_pcm_lib_ioctl(substream, cmd, arg);
333}
334
335static unsigned char snd_ad1848_get_format(int format, int channels)
336{
337 unsigned char rformat;
338
339 rformat = AD1848_LINEAR_8;
340 switch (format) {
341 case SNDRV_PCM_FORMAT_A_LAW: rformat = AD1848_ALAW_8; break;
342 case SNDRV_PCM_FORMAT_MU_LAW: rformat = AD1848_ULAW_8; break;
343 case SNDRV_PCM_FORMAT_S16_LE: rformat = AD1848_LINEAR_16; break;
344 }
345 if (channels > 1)
346 rformat |= AD1848_STEREO;
347#if 0
348 snd_printk("get_format: 0x%x (mode=0x%x)\n", format, mode);
349#endif
350 return rformat;
351}
352
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100353static void snd_ad1848_calibrate_mute(struct snd_ad1848 *chip, int mute)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354{
355 unsigned long flags;
356
357 mute = mute ? 1 : 0;
358 spin_lock_irqsave(&chip->reg_lock, flags);
359 if (chip->calibrate_mute == mute) {
360 spin_unlock_irqrestore(&chip->reg_lock, flags);
361 return;
362 }
363 if (!mute) {
364 snd_ad1848_dout(chip, AD1848_LEFT_INPUT, chip->image[AD1848_LEFT_INPUT]);
365 snd_ad1848_dout(chip, AD1848_RIGHT_INPUT, chip->image[AD1848_RIGHT_INPUT]);
366 }
367 snd_ad1848_dout(chip, AD1848_AUX1_LEFT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX1_LEFT_INPUT]);
368 snd_ad1848_dout(chip, AD1848_AUX1_RIGHT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX1_RIGHT_INPUT]);
369 snd_ad1848_dout(chip, AD1848_AUX2_LEFT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX2_LEFT_INPUT]);
370 snd_ad1848_dout(chip, AD1848_AUX2_RIGHT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX2_RIGHT_INPUT]);
371 snd_ad1848_dout(chip, AD1848_LEFT_OUTPUT, mute ? 0x80 : chip->image[AD1848_LEFT_OUTPUT]);
372 snd_ad1848_dout(chip, AD1848_RIGHT_OUTPUT, mute ? 0x80 : chip->image[AD1848_RIGHT_OUTPUT]);
373 chip->calibrate_mute = mute;
374 spin_unlock_irqrestore(&chip->reg_lock, flags);
375}
376
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100377static void snd_ad1848_set_data_format(struct snd_ad1848 *chip, struct snd_pcm_hw_params *hw_params)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378{
379 if (hw_params == NULL) {
380 chip->image[AD1848_DATA_FORMAT] = 0x20;
381 } else {
382 chip->image[AD1848_DATA_FORMAT] =
383 snd_ad1848_get_format(params_format(hw_params), params_channels(hw_params)) |
384 snd_ad1848_get_rate(params_rate(hw_params));
385 }
386 // snd_printk(">>> pmode = 0x%x, dfr = 0x%x\n", pstr->mode, chip->image[AD1848_DATA_FORMAT]);
387}
388
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100389static int snd_ad1848_open(struct snd_ad1848 *chip, unsigned int mode)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390{
391 unsigned long flags;
392
Ingo Molnar8b7547f2006-01-16 16:33:08 +0100393 mutex_lock(&chip->open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394 if (chip->mode & AD1848_MODE_OPEN) {
Ingo Molnar8b7547f2006-01-16 16:33:08 +0100395 mutex_unlock(&chip->open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396 return -EAGAIN;
397 }
398 snd_ad1848_mce_down(chip);
399
400#ifdef SNDRV_DEBUG_MCE
401 snd_printk("open: (1)\n");
402#endif
403 snd_ad1848_mce_up(chip);
404 spin_lock_irqsave(&chip->reg_lock, flags);
405 chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_PLAYBACK_ENABLE | AD1848_PLAYBACK_PIO |
406 AD1848_CAPTURE_ENABLE | AD1848_CAPTURE_PIO |
407 AD1848_CALIB_MODE);
408 chip->image[AD1848_IFACE_CTRL] |= AD1848_AUTOCALIB;
409 snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL]);
410 spin_unlock_irqrestore(&chip->reg_lock, flags);
411 snd_ad1848_mce_down(chip);
412
413#ifdef SNDRV_DEBUG_MCE
414 snd_printk("open: (2)\n");
415#endif
416
417 snd_ad1848_set_data_format(chip, NULL);
418
419 snd_ad1848_mce_up(chip);
420 spin_lock_irqsave(&chip->reg_lock, flags);
421 snd_ad1848_out(chip, AD1848_DATA_FORMAT, chip->image[AD1848_DATA_FORMAT]);
422 spin_unlock_irqrestore(&chip->reg_lock, flags);
423 snd_ad1848_mce_down(chip);
424
425#ifdef SNDRV_DEBUG_MCE
426 snd_printk("open: (3)\n");
427#endif
428
429 /* ok. now enable and ack CODEC IRQ */
430 spin_lock_irqsave(&chip->reg_lock, flags);
431 outb(0, AD1848P(chip, STATUS)); /* clear IRQ */
432 outb(0, AD1848P(chip, STATUS)); /* clear IRQ */
433 chip->image[AD1848_PIN_CTRL] |= AD1848_IRQ_ENABLE;
434 snd_ad1848_out(chip, AD1848_PIN_CTRL, chip->image[AD1848_PIN_CTRL]);
435 spin_unlock_irqrestore(&chip->reg_lock, flags);
436
437 chip->mode = mode;
Ingo Molnar8b7547f2006-01-16 16:33:08 +0100438 mutex_unlock(&chip->open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439
440 return 0;
441}
442
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100443static void snd_ad1848_close(struct snd_ad1848 *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444{
445 unsigned long flags;
446
Ingo Molnar8b7547f2006-01-16 16:33:08 +0100447 mutex_lock(&chip->open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448 if (!chip->mode) {
Ingo Molnar8b7547f2006-01-16 16:33:08 +0100449 mutex_unlock(&chip->open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450 return;
451 }
452 /* disable IRQ */
453 spin_lock_irqsave(&chip->reg_lock, flags);
454 outb(0, AD1848P(chip, STATUS)); /* clear IRQ */
455 outb(0, AD1848P(chip, STATUS)); /* clear IRQ */
456 chip->image[AD1848_PIN_CTRL] &= ~AD1848_IRQ_ENABLE;
457 snd_ad1848_out(chip, AD1848_PIN_CTRL, chip->image[AD1848_PIN_CTRL]);
458 spin_unlock_irqrestore(&chip->reg_lock, flags);
459
460 /* now disable capture & playback */
461
462 snd_ad1848_mce_up(chip);
463 spin_lock_irqsave(&chip->reg_lock, flags);
464 chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_PLAYBACK_ENABLE | AD1848_PLAYBACK_PIO |
465 AD1848_CAPTURE_ENABLE | AD1848_CAPTURE_PIO);
466 snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL]);
467 spin_unlock_irqrestore(&chip->reg_lock, flags);
468 snd_ad1848_mce_down(chip);
469
470 /* clear IRQ again */
471 spin_lock_irqsave(&chip->reg_lock, flags);
472 outb(0, AD1848P(chip, STATUS)); /* clear IRQ */
473 outb(0, AD1848P(chip, STATUS)); /* clear IRQ */
474 spin_unlock_irqrestore(&chip->reg_lock, flags);
475
476 chip->mode = 0;
Ingo Molnar8b7547f2006-01-16 16:33:08 +0100477 mutex_unlock(&chip->open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478}
479
480/*
481 * ok.. exported functions..
482 */
483
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100484static int snd_ad1848_playback_trigger(struct snd_pcm_substream *substream,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485 int cmd)
486{
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100487 struct snd_ad1848 *chip = snd_pcm_substream_chip(substream);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488 return snd_ad1848_trigger(chip, AD1848_PLAYBACK_ENABLE, SNDRV_PCM_STREAM_PLAYBACK, cmd);
489}
490
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100491static int snd_ad1848_capture_trigger(struct snd_pcm_substream *substream,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492 int cmd)
493{
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100494 struct snd_ad1848 *chip = snd_pcm_substream_chip(substream);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495 return snd_ad1848_trigger(chip, AD1848_CAPTURE_ENABLE, SNDRV_PCM_STREAM_CAPTURE, cmd);
496}
497
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100498static int snd_ad1848_playback_hw_params(struct snd_pcm_substream *substream,
499 struct snd_pcm_hw_params *hw_params)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500{
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100501 struct snd_ad1848 *chip = snd_pcm_substream_chip(substream);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502 unsigned long flags;
503 int err;
504
505 if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
506 return err;
507 snd_ad1848_calibrate_mute(chip, 1);
508 snd_ad1848_set_data_format(chip, hw_params);
509 snd_ad1848_mce_up(chip);
510 spin_lock_irqsave(&chip->reg_lock, flags);
511 snd_ad1848_out(chip, AD1848_DATA_FORMAT, chip->image[AD1848_DATA_FORMAT]);
512 spin_unlock_irqrestore(&chip->reg_lock, flags);
513 snd_ad1848_mce_down(chip);
514 snd_ad1848_calibrate_mute(chip, 0);
515 return 0;
516}
517
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100518static int snd_ad1848_playback_hw_free(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519{
520 return snd_pcm_lib_free_pages(substream);
521}
522
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100523static int snd_ad1848_playback_prepare(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524{
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100525 struct snd_ad1848 *chip = snd_pcm_substream_chip(substream);
526 struct snd_pcm_runtime *runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527 unsigned long flags;
528 unsigned int size = snd_pcm_lib_buffer_bytes(substream);
529 unsigned int count = snd_pcm_lib_period_bytes(substream);
530
531 chip->dma_size = size;
532 chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_PLAYBACK_ENABLE | AD1848_PLAYBACK_PIO);
533 snd_dma_program(chip->dma, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT);
534 count = snd_ad1848_get_count(chip->image[AD1848_DATA_FORMAT], count) - 1;
535 spin_lock_irqsave(&chip->reg_lock, flags);
536 snd_ad1848_out(chip, AD1848_DATA_LWR_CNT, (unsigned char) count);
537 snd_ad1848_out(chip, AD1848_DATA_UPR_CNT, (unsigned char) (count >> 8));
538 spin_unlock_irqrestore(&chip->reg_lock, flags);
539 return 0;
540}
541
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100542static int snd_ad1848_capture_hw_params(struct snd_pcm_substream *substream,
543 struct snd_pcm_hw_params *hw_params)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544{
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100545 struct snd_ad1848 *chip = snd_pcm_substream_chip(substream);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546 unsigned long flags;
547 int err;
548
549 if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
550 return err;
551 snd_ad1848_calibrate_mute(chip, 1);
552 snd_ad1848_set_data_format(chip, hw_params);
553 snd_ad1848_mce_up(chip);
554 spin_lock_irqsave(&chip->reg_lock, flags);
555 snd_ad1848_out(chip, AD1848_DATA_FORMAT, chip->image[AD1848_DATA_FORMAT]);
556 spin_unlock_irqrestore(&chip->reg_lock, flags);
557 snd_ad1848_mce_down(chip);
558 snd_ad1848_calibrate_mute(chip, 0);
559 return 0;
560}
561
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100562static int snd_ad1848_capture_hw_free(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563{
564 return snd_pcm_lib_free_pages(substream);
565}
566
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100567static int snd_ad1848_capture_prepare(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568{
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100569 struct snd_ad1848 *chip = snd_pcm_substream_chip(substream);
570 struct snd_pcm_runtime *runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571 unsigned long flags;
572 unsigned int size = snd_pcm_lib_buffer_bytes(substream);
573 unsigned int count = snd_pcm_lib_period_bytes(substream);
574
575 chip->dma_size = size;
576 chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_CAPTURE_ENABLE | AD1848_CAPTURE_PIO);
577 snd_dma_program(chip->dma, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT);
578 count = snd_ad1848_get_count(chip->image[AD1848_DATA_FORMAT], count) - 1;
579 spin_lock_irqsave(&chip->reg_lock, flags);
580 snd_ad1848_out(chip, AD1848_DATA_LWR_CNT, (unsigned char) count);
581 snd_ad1848_out(chip, AD1848_DATA_UPR_CNT, (unsigned char) (count >> 8));
582 spin_unlock_irqrestore(&chip->reg_lock, flags);
583 return 0;
584}
585
David Howells7d12e782006-10-05 14:55:46 +0100586static irqreturn_t snd_ad1848_interrupt(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587{
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100588 struct snd_ad1848 *chip = dev_id;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589
590 if ((chip->mode & AD1848_MODE_PLAY) && chip->playback_substream &&
591 (chip->mode & AD1848_MODE_RUNNING))
592 snd_pcm_period_elapsed(chip->playback_substream);
593 if ((chip->mode & AD1848_MODE_CAPTURE) && chip->capture_substream &&
594 (chip->mode & AD1848_MODE_RUNNING))
595 snd_pcm_period_elapsed(chip->capture_substream);
596 outb(0, AD1848P(chip, STATUS)); /* clear global interrupt bit */
597 return IRQ_HANDLED;
598}
599
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100600static snd_pcm_uframes_t snd_ad1848_playback_pointer(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601{
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100602 struct snd_ad1848 *chip = snd_pcm_substream_chip(substream);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603 size_t ptr;
604
605 if (!(chip->image[AD1848_IFACE_CTRL] & AD1848_PLAYBACK_ENABLE))
606 return 0;
607 ptr = snd_dma_pointer(chip->dma, chip->dma_size);
608 return bytes_to_frames(substream->runtime, ptr);
609}
610
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100611static snd_pcm_uframes_t snd_ad1848_capture_pointer(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612{
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100613 struct snd_ad1848 *chip = snd_pcm_substream_chip(substream);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614 size_t ptr;
615
616 if (!(chip->image[AD1848_IFACE_CTRL] & AD1848_CAPTURE_ENABLE))
617 return 0;
618 ptr = snd_dma_pointer(chip->dma, chip->dma_size);
619 return bytes_to_frames(substream->runtime, ptr);
620}
621
622/*
623
624 */
625
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100626static void snd_ad1848_thinkpad_twiddle(struct snd_ad1848 *chip, int on) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627
628 int tmp;
629
630 if (!chip->thinkpad_flag) return;
631
632 outb(0x1c, AD1848_THINKPAD_CTL_PORT1);
633 tmp = inb(AD1848_THINKPAD_CTL_PORT2);
634
635 if (on)
636 /* turn it on */
637 tmp |= AD1848_THINKPAD_CS4248_ENABLE_BIT;
638 else
639 /* turn it off */
640 tmp &= ~AD1848_THINKPAD_CS4248_ENABLE_BIT;
641
642 outb(tmp, AD1848_THINKPAD_CTL_PORT2);
643
644}
645
646#ifdef CONFIG_PM
Takashi Iwaic66d7f72005-11-17 16:57:48 +0100647static void snd_ad1848_suspend(struct snd_ad1848 *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649 snd_pcm_suspend_all(chip->pcm);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650 if (chip->thinkpad_flag)
651 snd_ad1848_thinkpad_twiddle(chip, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652}
653
Takashi Iwaic66d7f72005-11-17 16:57:48 +0100654static void snd_ad1848_resume(struct snd_ad1848 *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655{
Takashi Iwaic66d7f72005-11-17 16:57:48 +0100656 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657
658 if (chip->thinkpad_flag)
659 snd_ad1848_thinkpad_twiddle(chip, 1);
660
Takashi Iwaic66d7f72005-11-17 16:57:48 +0100661 /* clear any pendings IRQ */
662 inb(AD1848P(chip, STATUS));
663 outb(0, AD1848P(chip, STATUS));
664 mb();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665
Takashi Iwaic66d7f72005-11-17 16:57:48 +0100666 snd_ad1848_mce_down(chip);
667 for (i = 0; i < 16; i++)
668 snd_ad1848_out(chip, i, chip->image[i]);
669 snd_ad1848_mce_up(chip);
670 snd_ad1848_mce_down(chip);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671}
672#endif /* CONFIG_PM */
673
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100674static int snd_ad1848_probe(struct snd_ad1848 * chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675{
676 unsigned long flags;
677 int i, id, rev, ad1847;
678 unsigned char *ptr;
679
680#if 0
681 snd_ad1848_debug(chip);
682#endif
683 id = ad1847 = 0;
684 for (i = 0; i < 1000; i++) {
685 mb();
686 if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT)
687 udelay(500);
688 else {
689 spin_lock_irqsave(&chip->reg_lock, flags);
690 snd_ad1848_out(chip, AD1848_MISC_INFO, 0x00);
691 snd_ad1848_out(chip, AD1848_LEFT_INPUT, 0xaa);
692 snd_ad1848_out(chip, AD1848_RIGHT_INPUT, 0x45);
693 rev = snd_ad1848_in(chip, AD1848_RIGHT_INPUT);
694 if (rev == 0x65) {
695 spin_unlock_irqrestore(&chip->reg_lock, flags);
696 id = 1;
697 ad1847 = 1;
698 break;
699 }
700 if (snd_ad1848_in(chip, AD1848_LEFT_INPUT) == 0xaa && rev == 0x45) {
701 spin_unlock_irqrestore(&chip->reg_lock, flags);
702 id = 1;
703 break;
704 }
705 spin_unlock_irqrestore(&chip->reg_lock, flags);
706 }
707 }
708 if (id != 1)
709 return -ENODEV; /* no valid device found */
710 if (chip->hardware == AD1848_HW_DETECT) {
711 if (ad1847) {
712 chip->hardware = AD1848_HW_AD1847;
713 } else {
714 chip->hardware = AD1848_HW_AD1848;
715 rev = snd_ad1848_in(chip, AD1848_MISC_INFO);
716 if (rev & 0x80) {
717 chip->hardware = AD1848_HW_CS4248;
718 } else if ((rev & 0x0f) == 0x0a) {
719 snd_ad1848_out(chip, AD1848_MISC_INFO, 0x40);
720 for (i = 0; i < 16; ++i) {
721 if (snd_ad1848_in(chip, i) != snd_ad1848_in(chip, i + 16)) {
722 chip->hardware = AD1848_HW_CMI8330;
723 break;
724 }
725 }
726 snd_ad1848_out(chip, AD1848_MISC_INFO, 0x00);
727 }
728 }
729 }
730 spin_lock_irqsave(&chip->reg_lock, flags);
731 inb(AD1848P(chip, STATUS)); /* clear any pendings IRQ */
732 outb(0, AD1848P(chip, STATUS));
733 mb();
734 spin_unlock_irqrestore(&chip->reg_lock, flags);
735
736 chip->image[AD1848_MISC_INFO] = 0x00;
737 chip->image[AD1848_IFACE_CTRL] =
738 (chip->image[AD1848_IFACE_CTRL] & ~AD1848_SINGLE_DMA) | AD1848_SINGLE_DMA;
739 ptr = (unsigned char *) &chip->image;
740 snd_ad1848_mce_down(chip);
741 spin_lock_irqsave(&chip->reg_lock, flags);
742 for (i = 0; i < 16; i++) /* ok.. fill all AD1848 registers */
743 snd_ad1848_out(chip, i, *ptr++);
744 spin_unlock_irqrestore(&chip->reg_lock, flags);
745 snd_ad1848_mce_up(chip);
746 snd_ad1848_mce_down(chip);
747 return 0; /* all things are ok.. */
748}
749
750/*
751
752 */
753
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100754static struct snd_pcm_hardware snd_ad1848_playback =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755{
756 .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
757 SNDRV_PCM_INFO_MMAP_VALID),
758 .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW |
759 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE),
760 .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
761 .rate_min = 5510,
762 .rate_max = 48000,
763 .channels_min = 1,
764 .channels_max = 2,
765 .buffer_bytes_max = (128*1024),
766 .period_bytes_min = 64,
767 .period_bytes_max = (128*1024),
768 .periods_min = 1,
769 .periods_max = 1024,
770 .fifo_size = 0,
771};
772
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100773static struct snd_pcm_hardware snd_ad1848_capture =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774{
775 .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
776 SNDRV_PCM_INFO_MMAP_VALID),
777 .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW |
778 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE),
779 .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
780 .rate_min = 5510,
781 .rate_max = 48000,
782 .channels_min = 1,
783 .channels_max = 2,
784 .buffer_bytes_max = (128*1024),
785 .period_bytes_min = 64,
786 .period_bytes_max = (128*1024),
787 .periods_min = 1,
788 .periods_max = 1024,
789 .fifo_size = 0,
790};
791
792/*
793
794 */
795
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100796static int snd_ad1848_playback_open(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797{
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100798 struct snd_ad1848 *chip = snd_pcm_substream_chip(substream);
799 struct snd_pcm_runtime *runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800 int err;
801
802 if ((err = snd_ad1848_open(chip, AD1848_MODE_PLAY)) < 0)
803 return err;
804 chip->playback_substream = substream;
805 runtime->hw = snd_ad1848_playback;
806 snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.buffer_bytes_max);
807 snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.period_bytes_max);
808 snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
809 return 0;
810}
811
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100812static int snd_ad1848_capture_open(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813{
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100814 struct snd_ad1848 *chip = snd_pcm_substream_chip(substream);
815 struct snd_pcm_runtime *runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 int err;
817
818 if ((err = snd_ad1848_open(chip, AD1848_MODE_CAPTURE)) < 0)
819 return err;
820 chip->capture_substream = substream;
821 runtime->hw = snd_ad1848_capture;
822 snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.buffer_bytes_max);
823 snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.period_bytes_max);
824 snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
825 return 0;
826}
827
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100828static int snd_ad1848_playback_close(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829{
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100830 struct snd_ad1848 *chip = snd_pcm_substream_chip(substream);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831
832 chip->mode &= ~AD1848_MODE_PLAY;
833 chip->playback_substream = NULL;
834 snd_ad1848_close(chip);
835 return 0;
836}
837
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100838static int snd_ad1848_capture_close(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839{
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100840 struct snd_ad1848 *chip = snd_pcm_substream_chip(substream);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841
842 chip->mode &= ~AD1848_MODE_CAPTURE;
843 chip->capture_substream = NULL;
844 snd_ad1848_close(chip);
845 return 0;
846}
847
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100848static int snd_ad1848_free(struct snd_ad1848 *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849{
Takashi Iwaib1d57762005-10-10 11:56:31 +0200850 release_and_free_resource(chip->res_port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851 if (chip->irq >= 0)
852 free_irq(chip->irq, (void *) chip);
853 if (chip->dma >= 0) {
854 snd_dma_disable(chip->dma);
855 free_dma(chip->dma);
856 }
857 kfree(chip);
858 return 0;
859}
860
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100861static int snd_ad1848_dev_free(struct snd_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862{
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100863 struct snd_ad1848 *chip = device->device_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864 return snd_ad1848_free(chip);
865}
866
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100867static const char *snd_ad1848_chip_id(struct snd_ad1848 *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868{
869 switch (chip->hardware) {
870 case AD1848_HW_AD1847: return "AD1847";
871 case AD1848_HW_AD1848: return "AD1848";
872 case AD1848_HW_CS4248: return "CS4248";
873 case AD1848_HW_CMI8330: return "CMI8330/C3D";
874 default: return "???";
875 }
876}
877
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100878int snd_ad1848_create(struct snd_card *card,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700879 unsigned long port,
880 int irq, int dma,
881 unsigned short hardware,
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100882 struct snd_ad1848 ** rchip)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883{
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100884 static struct snd_device_ops ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700885 .dev_free = snd_ad1848_dev_free,
886 };
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100887 struct snd_ad1848 *chip;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888 int err;
889
890 *rchip = NULL;
Takashi Iwai9e76a762005-09-09 14:21:17 +0200891 chip = kzalloc(sizeof(*chip), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892 if (chip == NULL)
893 return -ENOMEM;
894 spin_lock_init(&chip->reg_lock);
Ingo Molnar8b7547f2006-01-16 16:33:08 +0100895 mutex_init(&chip->open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896 chip->card = card;
897 chip->port = port;
898 chip->irq = -1;
899 chip->dma = -1;
900 chip->hardware = hardware;
901 memcpy(&chip->image, &snd_ad1848_original_image, sizeof(snd_ad1848_original_image));
902
903 if ((chip->res_port = request_region(port, 4, "AD1848")) == NULL) {
904 snd_printk(KERN_ERR "ad1848: can't grab port 0x%lx\n", port);
905 snd_ad1848_free(chip);
906 return -EBUSY;
907 }
Thomas Gleixner65ca68b2006-07-01 19:29:46 -0700908 if (request_irq(irq, snd_ad1848_interrupt, IRQF_DISABLED, "AD1848", (void *) chip)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700909 snd_printk(KERN_ERR "ad1848: can't grab IRQ %d\n", irq);
910 snd_ad1848_free(chip);
911 return -EBUSY;
912 }
913 chip->irq = irq;
914 if (request_dma(dma, "AD1848")) {
915 snd_printk(KERN_ERR "ad1848: can't grab DMA %d\n", dma);
916 snd_ad1848_free(chip);
917 return -EBUSY;
918 }
919 chip->dma = dma;
920
921 if (hardware == AD1848_HW_THINKPAD) {
922 chip->thinkpad_flag = 1;
923 chip->hardware = AD1848_HW_DETECT; /* reset */
924 snd_ad1848_thinkpad_twiddle(chip, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925 }
926
927 if (snd_ad1848_probe(chip) < 0) {
928 snd_ad1848_free(chip);
929 return -ENODEV;
930 }
931
932 /* Register device */
933 if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
934 snd_ad1848_free(chip);
935 return err;
936 }
937
Takashi Iwai9d70d912005-11-30 10:12:26 +0100938#ifdef CONFIG_PM
Takashi Iwaic66d7f72005-11-17 16:57:48 +0100939 chip->suspend = snd_ad1848_suspend;
940 chip->resume = snd_ad1848_resume;
Takashi Iwai9d70d912005-11-30 10:12:26 +0100941#endif
Takashi Iwaic66d7f72005-11-17 16:57:48 +0100942
Linus Torvalds1da177e2005-04-16 15:20:36 -0700943 *rchip = chip;
944 return 0;
945}
946
Takashi Iwaieac06a12006-08-22 13:16:25 +0200947EXPORT_SYMBOL(snd_ad1848_create);
948
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100949static struct snd_pcm_ops snd_ad1848_playback_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950 .open = snd_ad1848_playback_open,
951 .close = snd_ad1848_playback_close,
952 .ioctl = snd_ad1848_ioctl,
953 .hw_params = snd_ad1848_playback_hw_params,
954 .hw_free = snd_ad1848_playback_hw_free,
955 .prepare = snd_ad1848_playback_prepare,
956 .trigger = snd_ad1848_playback_trigger,
957 .pointer = snd_ad1848_playback_pointer,
958};
959
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100960static struct snd_pcm_ops snd_ad1848_capture_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700961 .open = snd_ad1848_capture_open,
962 .close = snd_ad1848_capture_close,
963 .ioctl = snd_ad1848_ioctl,
964 .hw_params = snd_ad1848_capture_hw_params,
965 .hw_free = snd_ad1848_capture_hw_free,
966 .prepare = snd_ad1848_capture_prepare,
967 .trigger = snd_ad1848_capture_trigger,
968 .pointer = snd_ad1848_capture_pointer,
969};
970
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100971int snd_ad1848_pcm(struct snd_ad1848 *chip, int device, struct snd_pcm **rpcm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972{
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100973 struct snd_pcm *pcm;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974 int err;
975
976 if ((err = snd_pcm_new(chip->card, "AD1848", device, 1, 1, &pcm)) < 0)
977 return err;
978
979 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ad1848_playback_ops);
980 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ad1848_capture_ops);
981
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982 pcm->private_data = chip;
983 pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX;
984 strcpy(pcm->name, snd_ad1848_chip_id(chip));
985
986 snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
987 snd_dma_isa_data(),
988 64*1024, chip->dma > 3 ? 128*1024 : 64*1024);
989
990 chip->pcm = pcm;
991 if (rpcm)
992 *rpcm = pcm;
993 return 0;
994}
995
Takashi Iwaieac06a12006-08-22 13:16:25 +0200996EXPORT_SYMBOL(snd_ad1848_pcm);
997
Takashi Iwaic8ff6642005-11-17 14:29:37 +0100998const struct snd_pcm_ops *snd_ad1848_get_pcm_ops(int direction)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999{
1000 return direction == SNDRV_PCM_STREAM_PLAYBACK ?
1001 &snd_ad1848_playback_ops : &snd_ad1848_capture_ops;
1002}
1003
Takashi Iwaieac06a12006-08-22 13:16:25 +02001004EXPORT_SYMBOL(snd_ad1848_get_pcm_ops);
1005
Linus Torvalds1da177e2005-04-16 15:20:36 -07001006/*
1007 * MIXER part
1008 */
1009
Takashi Iwaic8ff6642005-11-17 14:29:37 +01001010static int snd_ad1848_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011{
1012 static char *texts[4] = {
1013 "Line", "Aux", "Mic", "Mix"
1014 };
1015
1016 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1017 uinfo->count = 2;
1018 uinfo->value.enumerated.items = 4;
1019 if (uinfo->value.enumerated.item > 3)
1020 uinfo->value.enumerated.item = 3;
1021 strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
1022 return 0;
1023}
1024
Takashi Iwaic8ff6642005-11-17 14:29:37 +01001025static int snd_ad1848_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001026{
Takashi Iwaic8ff6642005-11-17 14:29:37 +01001027 struct snd_ad1848 *chip = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028 unsigned long flags;
1029
1030 spin_lock_irqsave(&chip->reg_lock, flags);
1031 ucontrol->value.enumerated.item[0] = (chip->image[AD1848_LEFT_INPUT] & AD1848_MIXS_ALL) >> 6;
1032 ucontrol->value.enumerated.item[1] = (chip->image[AD1848_RIGHT_INPUT] & AD1848_MIXS_ALL) >> 6;
1033 spin_unlock_irqrestore(&chip->reg_lock, flags);
1034 return 0;
1035}
1036
Takashi Iwaic8ff6642005-11-17 14:29:37 +01001037static int snd_ad1848_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038{
Takashi Iwaic8ff6642005-11-17 14:29:37 +01001039 struct snd_ad1848 *chip = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001040 unsigned long flags;
1041 unsigned short left, right;
1042 int change;
1043
1044 if (ucontrol->value.enumerated.item[0] > 3 ||
1045 ucontrol->value.enumerated.item[1] > 3)
1046 return -EINVAL;
1047 left = ucontrol->value.enumerated.item[0] << 6;
1048 right = ucontrol->value.enumerated.item[1] << 6;
1049 spin_lock_irqsave(&chip->reg_lock, flags);
1050 left = (chip->image[AD1848_LEFT_INPUT] & ~AD1848_MIXS_ALL) | left;
1051 right = (chip->image[AD1848_RIGHT_INPUT] & ~AD1848_MIXS_ALL) | right;
1052 change = left != chip->image[AD1848_LEFT_INPUT] ||
1053 right != chip->image[AD1848_RIGHT_INPUT];
1054 snd_ad1848_out(chip, AD1848_LEFT_INPUT, left);
1055 snd_ad1848_out(chip, AD1848_RIGHT_INPUT, right);
1056 spin_unlock_irqrestore(&chip->reg_lock, flags);
1057 return change;
1058}
1059
Takashi Iwaic8ff6642005-11-17 14:29:37 +01001060static int snd_ad1848_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061{
1062 int mask = (kcontrol->private_value >> 16) & 0xff;
1063
1064 uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
1065 uinfo->count = 1;
1066 uinfo->value.integer.min = 0;
1067 uinfo->value.integer.max = mask;
1068 return 0;
1069}
1070
Takashi Iwaic8ff6642005-11-17 14:29:37 +01001071static int snd_ad1848_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072{
Takashi Iwaic8ff6642005-11-17 14:29:37 +01001073 struct snd_ad1848 *chip = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074 unsigned long flags;
1075 int reg = kcontrol->private_value & 0xff;
1076 int shift = (kcontrol->private_value >> 8) & 0xff;
1077 int mask = (kcontrol->private_value >> 16) & 0xff;
1078 int invert = (kcontrol->private_value >> 24) & 0xff;
1079
1080 spin_lock_irqsave(&chip->reg_lock, flags);
1081 ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask;
1082 spin_unlock_irqrestore(&chip->reg_lock, flags);
1083 if (invert)
1084 ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
1085 return 0;
1086}
1087
Takashi Iwaic8ff6642005-11-17 14:29:37 +01001088static int snd_ad1848_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089{
Takashi Iwaic8ff6642005-11-17 14:29:37 +01001090 struct snd_ad1848 *chip = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091 unsigned long flags;
1092 int reg = kcontrol->private_value & 0xff;
1093 int shift = (kcontrol->private_value >> 8) & 0xff;
1094 int mask = (kcontrol->private_value >> 16) & 0xff;
1095 int invert = (kcontrol->private_value >> 24) & 0xff;
1096 int change;
1097 unsigned short val;
1098
1099 val = (ucontrol->value.integer.value[0] & mask);
1100 if (invert)
1101 val = mask - val;
1102 val <<= shift;
1103 spin_lock_irqsave(&chip->reg_lock, flags);
1104 val = (chip->image[reg] & ~(mask << shift)) | val;
1105 change = val != chip->image[reg];
1106 snd_ad1848_out(chip, reg, val);
1107 spin_unlock_irqrestore(&chip->reg_lock, flags);
1108 return change;
1109}
1110
Takashi Iwaic8ff6642005-11-17 14:29:37 +01001111static int snd_ad1848_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001112{
1113 int mask = (kcontrol->private_value >> 24) & 0xff;
1114
1115 uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
1116 uinfo->count = 2;
1117 uinfo->value.integer.min = 0;
1118 uinfo->value.integer.max = mask;
1119 return 0;
1120}
1121
Takashi Iwaic8ff6642005-11-17 14:29:37 +01001122static int snd_ad1848_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001123{
Takashi Iwaic8ff6642005-11-17 14:29:37 +01001124 struct snd_ad1848 *chip = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125 unsigned long flags;
1126 int left_reg = kcontrol->private_value & 0xff;
1127 int right_reg = (kcontrol->private_value >> 8) & 0xff;
1128 int shift_left = (kcontrol->private_value >> 16) & 0x07;
1129 int shift_right = (kcontrol->private_value >> 19) & 0x07;
1130 int mask = (kcontrol->private_value >> 24) & 0xff;
1131 int invert = (kcontrol->private_value >> 22) & 1;
1132
1133 spin_lock_irqsave(&chip->reg_lock, flags);
1134 ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask;
1135 ucontrol->value.integer.value[1] = (chip->image[right_reg] >> shift_right) & mask;
1136 spin_unlock_irqrestore(&chip->reg_lock, flags);
1137 if (invert) {
1138 ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
1139 ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
1140 }
1141 return 0;
1142}
1143
Takashi Iwaic8ff6642005-11-17 14:29:37 +01001144static int snd_ad1848_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001145{
Takashi Iwaic8ff6642005-11-17 14:29:37 +01001146 struct snd_ad1848 *chip = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147 unsigned long flags;
1148 int left_reg = kcontrol->private_value & 0xff;
1149 int right_reg = (kcontrol->private_value >> 8) & 0xff;
1150 int shift_left = (kcontrol->private_value >> 16) & 0x07;
1151 int shift_right = (kcontrol->private_value >> 19) & 0x07;
1152 int mask = (kcontrol->private_value >> 24) & 0xff;
1153 int invert = (kcontrol->private_value >> 22) & 1;
1154 int change;
1155 unsigned short val1, val2;
1156
1157 val1 = ucontrol->value.integer.value[0] & mask;
1158 val2 = ucontrol->value.integer.value[1] & mask;
1159 if (invert) {
1160 val1 = mask - val1;
1161 val2 = mask - val2;
1162 }
1163 val1 <<= shift_left;
1164 val2 <<= shift_right;
1165 spin_lock_irqsave(&chip->reg_lock, flags);
1166 if (left_reg != right_reg) {
1167 val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1;
1168 val2 = (chip->image[right_reg] & ~(mask << shift_right)) | val2;
1169 change = val1 != chip->image[left_reg] || val2 != chip->image[right_reg];
1170 snd_ad1848_out(chip, left_reg, val1);
1171 snd_ad1848_out(chip, right_reg, val2);
1172 } else {
1173 val1 = (chip->image[left_reg] & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2;
1174 change = val1 != chip->image[left_reg];
1175 snd_ad1848_out(chip, left_reg, val1);
1176 }
1177 spin_unlock_irqrestore(&chip->reg_lock, flags);
1178 return change;
1179}
1180
1181/*
1182 */
Takashi Iwaieac06a12006-08-22 13:16:25 +02001183int snd_ad1848_add_ctl_elem(struct snd_ad1848 *chip,
1184 const struct ad1848_mix_elem *c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001185{
Takashi Iwaic8ff6642005-11-17 14:29:37 +01001186 static struct snd_kcontrol_new newctls[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001187 [AD1848_MIX_SINGLE] = {
1188 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1189 .info = snd_ad1848_info_single,
1190 .get = snd_ad1848_get_single,
1191 .put = snd_ad1848_put_single,
1192 },
1193 [AD1848_MIX_DOUBLE] = {
1194 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1195 .info = snd_ad1848_info_double,
1196 .get = snd_ad1848_get_double,
1197 .put = snd_ad1848_put_double,
1198 },
1199 [AD1848_MIX_CAPTURE] = {
Clemens Ladisch67ed4162005-07-29 15:32:58 +02001200 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001201 .info = snd_ad1848_info_mux,
1202 .get = snd_ad1848_get_mux,
1203 .put = snd_ad1848_put_mux,
1204 },
1205 };
Takashi Iwaic8ff6642005-11-17 14:29:37 +01001206 struct snd_kcontrol *ctl;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001207 int err;
1208
Takashi Iwaieac06a12006-08-22 13:16:25 +02001209 ctl = snd_ctl_new1(&newctls[c->type], chip);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210 if (! ctl)
1211 return -ENOMEM;
Takashi Iwaieac06a12006-08-22 13:16:25 +02001212 strlcpy(ctl->id.name, c->name, sizeof(ctl->id.name));
1213 ctl->id.index = c->index;
1214 ctl->private_value = c->private_value;
1215 if (c->tlv) {
1216 ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
1217 ctl->tlv.p = c->tlv;
1218 }
Dave Jones3de44142006-03-06 13:31:18 +01001219 if ((err = snd_ctl_add(chip->card, ctl)) < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001220 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001221 return 0;
1222}
1223
Takashi Iwaieac06a12006-08-22 13:16:25 +02001224EXPORT_SYMBOL(snd_ad1848_add_ctl_elem);
1225
Takashi Iwai0cb29ea2007-01-29 15:33:49 +01001226static const DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0);
1227static const DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
1228static const DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001229
1230static struct ad1848_mix_elem snd_ad1848_controls[] = {
1231AD1848_DOUBLE("PCM Playback Switch", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 7, 7, 1, 1),
Takashi Iwaieac06a12006-08-22 13:16:25 +02001232AD1848_DOUBLE_TLV("PCM Playback Volume", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 0, 0, 63, 1,
1233 db_scale_6bit),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001234AD1848_DOUBLE("Aux Playback Switch", 0, AD1848_AUX1_LEFT_INPUT, AD1848_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
Takashi Iwaieac06a12006-08-22 13:16:25 +02001235AD1848_DOUBLE_TLV("Aux Playback Volume", 0, AD1848_AUX1_LEFT_INPUT, AD1848_AUX1_RIGHT_INPUT, 0, 0, 31, 1,
1236 db_scale_5bit_12db_max),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001237AD1848_DOUBLE("Aux Playback Switch", 1, AD1848_AUX2_LEFT_INPUT, AD1848_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
Takashi Iwaieac06a12006-08-22 13:16:25 +02001238AD1848_DOUBLE_TLV("Aux Playback Volume", 1, AD1848_AUX2_LEFT_INPUT, AD1848_AUX2_RIGHT_INPUT, 0, 0, 31, 1,
1239 db_scale_5bit_12db_max),
1240AD1848_DOUBLE_TLV("Capture Volume", 0, AD1848_LEFT_INPUT, AD1848_RIGHT_INPUT, 0, 0, 15, 0,
1241 db_scale_rec_gain),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001242{
1243 .name = "Capture Source",
1244 .type = AD1848_MIX_CAPTURE,
1245},
1246AD1848_SINGLE("Loopback Capture Switch", 0, AD1848_LOOPBACK, 0, 1, 0),
Takashi Iwaieac06a12006-08-22 13:16:25 +02001247AD1848_SINGLE_TLV("Loopback Capture Volume", 0, AD1848_LOOPBACK, 1, 63, 0,
1248 db_scale_6bit),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001249};
1250
Takashi Iwaic8ff6642005-11-17 14:29:37 +01001251int snd_ad1848_mixer(struct snd_ad1848 *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001252{
Takashi Iwaic8ff6642005-11-17 14:29:37 +01001253 struct snd_card *card;
1254 struct snd_pcm *pcm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255 unsigned int idx;
1256 int err;
1257
1258 snd_assert(chip != NULL && chip->pcm != NULL, return -EINVAL);
1259
1260 pcm = chip->pcm;
1261 card = chip->card;
1262
1263 strcpy(card->mixername, pcm->name);
1264
1265 for (idx = 0; idx < ARRAY_SIZE(snd_ad1848_controls); idx++)
1266 if ((err = snd_ad1848_add_ctl_elem(chip, &snd_ad1848_controls[idx])) < 0)
1267 return err;
1268
1269 return 0;
1270}
1271
Linus Torvalds1da177e2005-04-16 15:20:36 -07001272EXPORT_SYMBOL(snd_ad1848_mixer);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001273
1274/*
1275 * INIT part
1276 */
1277
1278static int __init alsa_ad1848_init(void)
1279{
1280 return 0;
1281}
1282
1283static void __exit alsa_ad1848_exit(void)
1284{
1285}
1286
1287module_init(alsa_ad1848_init)
1288module_exit(alsa_ad1848_exit)