diff options
Diffstat (limited to 'sound/isa/ad1848/ad1848_lib.c')
-rw-r--r-- | sound/isa/ad1848/ad1848_lib.c | 1279 |
1 files changed, 1279 insertions, 0 deletions
diff --git a/sound/isa/ad1848/ad1848_lib.c b/sound/isa/ad1848/ad1848_lib.c new file mode 100644 index 000000000000..8fb3db103e48 --- /dev/null +++ b/sound/isa/ad1848/ad1848_lib.c | |||
@@ -0,0 +1,1279 @@ | |||
1 | /* | ||
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> | ||
27 | #include <linux/pm.h> | ||
28 | #include <linux/slab.h> | ||
29 | #include <linux/ioport.h> | ||
30 | #include <sound/core.h> | ||
31 | #include <sound/ad1848.h> | ||
32 | #include <sound/control.h> | ||
33 | #include <sound/pcm_params.h> | ||
34 | |||
35 | #include <asm/io.h> | ||
36 | #include <asm/dma.h> | ||
37 | |||
38 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
39 | MODULE_DESCRIPTION("Routines for control of AD1848/AD1847/CS4248"); | ||
40 | MODULE_LICENSE("GPL"); | ||
41 | |||
42 | #if 0 | ||
43 | #define SNDRV_DEBUG_MCE | ||
44 | #endif | ||
45 | |||
46 | /* | ||
47 | * Some variables | ||
48 | */ | ||
49 | |||
50 | static 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 | |||
67 | static unsigned int rates[14] = { | ||
68 | 5510, 6620, 8000, 9600, 11025, 16000, 18900, 22050, | ||
69 | 27042, 32000, 33075, 37800, 44100, 48000 | ||
70 | }; | ||
71 | |||
72 | static snd_pcm_hw_constraint_list_t hw_constraints_rates = { | ||
73 | .count = 14, | ||
74 | .list = rates, | ||
75 | .mask = 0, | ||
76 | }; | ||
77 | |||
78 | static 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 | |||
102 | void snd_ad1848_out(ad1848_t *chip, | ||
103 | 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) | ||
112 | snd_printk("auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value); | ||
113 | #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 | |||
122 | static void snd_ad1848_dout(ad1848_t *chip, | ||
123 | unsigned char reg, unsigned char value) | ||
124 | { | ||
125 | int timeout; | ||
126 | |||
127 | for (timeout = 250; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--) | ||
128 | udelay(100); | ||
129 | outb(chip->mce_bit | reg, AD1848P(chip, REGSEL)); | ||
130 | outb(value, AD1848P(chip, REG)); | ||
131 | mb(); | ||
132 | } | ||
133 | |||
134 | static unsigned char snd_ad1848_in(ad1848_t *chip, unsigned char reg) | ||
135 | { | ||
136 | int timeout; | ||
137 | |||
138 | for (timeout = 250; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--) | ||
139 | udelay(100); | ||
140 | #ifdef CONFIG_SND_DEBUG | ||
141 | if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT) | ||
142 | snd_printk("auto calibration time out - reg = 0x%x\n", reg); | ||
143 | #endif | ||
144 | outb(chip->mce_bit | reg, AD1848P(chip, REGSEL)); | ||
145 | mb(); | ||
146 | return inb(AD1848P(chip, REG)); | ||
147 | } | ||
148 | |||
149 | #if 0 | ||
150 | |||
151 | static void snd_ad1848_debug(ad1848_t *chip) | ||
152 | { | ||
153 | printk("AD1848 REGS: INDEX = 0x%02x ", inb(AD1848P(chip, REGSEL))); | ||
154 | printk(" STATUS = 0x%02x\n", inb(AD1848P(chip, STATUS))); | ||
155 | printk(" 0x00: left input = 0x%02x ", snd_ad1848_in(chip, 0x00)); | ||
156 | printk(" 0x08: playback format = 0x%02x\n", snd_ad1848_in(chip, 0x08)); | ||
157 | printk(" 0x01: right input = 0x%02x ", snd_ad1848_in(chip, 0x01)); | ||
158 | printk(" 0x09: iface (CFIG 1) = 0x%02x\n", snd_ad1848_in(chip, 0x09)); | ||
159 | printk(" 0x02: AUXA left = 0x%02x ", snd_ad1848_in(chip, 0x02)); | ||
160 | printk(" 0x0a: pin control = 0x%02x\n", snd_ad1848_in(chip, 0x0a)); | ||
161 | printk(" 0x03: AUXA right = 0x%02x ", snd_ad1848_in(chip, 0x03)); | ||
162 | printk(" 0x0b: init & status = 0x%02x\n", snd_ad1848_in(chip, 0x0b)); | ||
163 | printk(" 0x04: AUXB left = 0x%02x ", snd_ad1848_in(chip, 0x04)); | ||
164 | printk(" 0x0c: revision & mode = 0x%02x\n", snd_ad1848_in(chip, 0x0c)); | ||
165 | printk(" 0x05: AUXB right = 0x%02x ", snd_ad1848_in(chip, 0x05)); | ||
166 | printk(" 0x0d: loopback = 0x%02x\n", snd_ad1848_in(chip, 0x0d)); | ||
167 | printk(" 0x06: left output = 0x%02x ", snd_ad1848_in(chip, 0x06)); | ||
168 | printk(" 0x0e: data upr count = 0x%02x\n", snd_ad1848_in(chip, 0x0e)); | ||
169 | printk(" 0x07: right output = 0x%02x ", snd_ad1848_in(chip, 0x07)); | ||
170 | printk(" 0x0f: data lwr count = 0x%02x\n", snd_ad1848_in(chip, 0x0f)); | ||
171 | } | ||
172 | |||
173 | #endif | ||
174 | |||
175 | /* | ||
176 | * AD1848 detection / MCE routines | ||
177 | */ | ||
178 | |||
179 | static void snd_ad1848_mce_up(ad1848_t *chip) | ||
180 | { | ||
181 | unsigned long flags; | ||
182 | int timeout; | ||
183 | |||
184 | for (timeout = 250; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--) | ||
185 | udelay(100); | ||
186 | #ifdef CONFIG_SND_DEBUG | ||
187 | if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT) | ||
188 | snd_printk("mce_up - auto calibration time out (0)\n"); | ||
189 | #endif | ||
190 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
191 | chip->mce_bit |= AD1848_MCE; | ||
192 | timeout = inb(AD1848P(chip, REGSEL)); | ||
193 | if (timeout == 0x80) | ||
194 | snd_printk("mce_up [0x%lx]: serious init problem - codec still busy\n", chip->port); | ||
195 | if (!(timeout & AD1848_MCE)) | ||
196 | outb(chip->mce_bit | (timeout & 0x1f), AD1848P(chip, REGSEL)); | ||
197 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
198 | } | ||
199 | |||
200 | static void snd_ad1848_mce_down(ad1848_t *chip) | ||
201 | { | ||
202 | unsigned long flags; | ||
203 | int timeout; | ||
204 | signed long time; | ||
205 | |||
206 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
207 | for (timeout = 5; timeout > 0; timeout--) | ||
208 | inb(AD1848P(chip, REGSEL)); | ||
209 | /* end of cleanup sequence */ | ||
210 | for (timeout = 12000; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--) | ||
211 | udelay(100); | ||
212 | #if 0 | ||
213 | printk("(1) timeout = %i\n", timeout); | ||
214 | #endif | ||
215 | #ifdef CONFIG_SND_DEBUG | ||
216 | if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT) | ||
217 | snd_printk("mce_down [0x%lx] - auto calibration time out (0)\n", AD1848P(chip, REGSEL)); | ||
218 | #endif | ||
219 | chip->mce_bit &= ~AD1848_MCE; | ||
220 | timeout = inb(AD1848P(chip, REGSEL)); | ||
221 | outb(chip->mce_bit | (timeout & 0x1f), AD1848P(chip, REGSEL)); | ||
222 | if (timeout == 0x80) | ||
223 | snd_printk("mce_down [0x%lx]: serious init problem - codec still busy\n", chip->port); | ||
224 | if ((timeout & AD1848_MCE) == 0) { | ||
225 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
226 | return; | ||
227 | } | ||
228 | /* calibration process */ | ||
229 | |||
230 | for (timeout = 500; timeout > 0 && (snd_ad1848_in(chip, AD1848_TEST_INIT) & AD1848_CALIB_IN_PROGRESS) == 0; timeout--); | ||
231 | if ((snd_ad1848_in(chip, AD1848_TEST_INIT) & AD1848_CALIB_IN_PROGRESS) == 0) { | ||
232 | snd_printd("mce_down - auto calibration time out (1)\n"); | ||
233 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
234 | return; | ||
235 | } | ||
236 | #if 0 | ||
237 | printk("(2) timeout = %i, jiffies = %li\n", timeout, jiffies); | ||
238 | #endif | ||
239 | time = HZ / 4; | ||
240 | while (snd_ad1848_in(chip, AD1848_TEST_INIT) & AD1848_CALIB_IN_PROGRESS) { | ||
241 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
242 | if (time <= 0) { | ||
243 | snd_printk("mce_down - auto calibration time out (2)\n"); | ||
244 | return; | ||
245 | } | ||
246 | set_current_state(TASK_INTERRUPTIBLE); | ||
247 | time = schedule_timeout(time); | ||
248 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
249 | } | ||
250 | #if 0 | ||
251 | printk("(3) jiffies = %li\n", jiffies); | ||
252 | #endif | ||
253 | time = HZ / 10; | ||
254 | while (inb(AD1848P(chip, REGSEL)) & AD1848_INIT) { | ||
255 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
256 | if (time <= 0) { | ||
257 | snd_printk("mce_down - auto calibration time out (3)\n"); | ||
258 | return; | ||
259 | } | ||
260 | set_current_state(TASK_INTERRUPTIBLE); | ||
261 | time = schedule_timeout(time); | ||
262 | 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 | |||
271 | static 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 | |||
284 | static int snd_ad1848_trigger(ad1848_t *chip, unsigned char what, | ||
285 | 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 | |||
318 | static 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 | |||
329 | static int snd_ad1848_ioctl(snd_pcm_substream_t * substream, | ||
330 | unsigned int cmd, void *arg) | ||
331 | { | ||
332 | return snd_pcm_lib_ioctl(substream, cmd, arg); | ||
333 | } | ||
334 | |||
335 | static 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 | |||
353 | static void snd_ad1848_calibrate_mute(ad1848_t *chip, int mute) | ||
354 | { | ||
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 | |||
377 | static void snd_ad1848_set_data_format(ad1848_t *chip, snd_pcm_hw_params_t *hw_params) | ||
378 | { | ||
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 | |||
389 | static int snd_ad1848_open(ad1848_t *chip, unsigned int mode) | ||
390 | { | ||
391 | unsigned long flags; | ||
392 | |||
393 | down(&chip->open_mutex); | ||
394 | if (chip->mode & AD1848_MODE_OPEN) { | ||
395 | up(&chip->open_mutex); | ||
396 | 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; | ||
438 | up(&chip->open_mutex); | ||
439 | |||
440 | return 0; | ||
441 | } | ||
442 | |||
443 | static void snd_ad1848_close(ad1848_t *chip) | ||
444 | { | ||
445 | unsigned long flags; | ||
446 | |||
447 | down(&chip->open_mutex); | ||
448 | if (!chip->mode) { | ||
449 | up(&chip->open_mutex); | ||
450 | 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; | ||
477 | up(&chip->open_mutex); | ||
478 | } | ||
479 | |||
480 | /* | ||
481 | * ok.. exported functions.. | ||
482 | */ | ||
483 | |||
484 | static int snd_ad1848_playback_trigger(snd_pcm_substream_t * substream, | ||
485 | int cmd) | ||
486 | { | ||
487 | ad1848_t *chip = snd_pcm_substream_chip(substream); | ||
488 | return snd_ad1848_trigger(chip, AD1848_PLAYBACK_ENABLE, SNDRV_PCM_STREAM_PLAYBACK, cmd); | ||
489 | } | ||
490 | |||
491 | static int snd_ad1848_capture_trigger(snd_pcm_substream_t * substream, | ||
492 | int cmd) | ||
493 | { | ||
494 | ad1848_t *chip = snd_pcm_substream_chip(substream); | ||
495 | return snd_ad1848_trigger(chip, AD1848_CAPTURE_ENABLE, SNDRV_PCM_STREAM_CAPTURE, cmd); | ||
496 | } | ||
497 | |||
498 | static int snd_ad1848_playback_hw_params(snd_pcm_substream_t * substream, | ||
499 | snd_pcm_hw_params_t * hw_params) | ||
500 | { | ||
501 | ad1848_t *chip = snd_pcm_substream_chip(substream); | ||
502 | 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 | |||
518 | static int snd_ad1848_playback_hw_free(snd_pcm_substream_t * substream) | ||
519 | { | ||
520 | return snd_pcm_lib_free_pages(substream); | ||
521 | } | ||
522 | |||
523 | static int snd_ad1848_playback_prepare(snd_pcm_substream_t * substream) | ||
524 | { | ||
525 | ad1848_t *chip = snd_pcm_substream_chip(substream); | ||
526 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
527 | 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 | |||
542 | static int snd_ad1848_capture_hw_params(snd_pcm_substream_t * substream, | ||
543 | snd_pcm_hw_params_t * hw_params) | ||
544 | { | ||
545 | ad1848_t *chip = snd_pcm_substream_chip(substream); | ||
546 | 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 | |||
562 | static int snd_ad1848_capture_hw_free(snd_pcm_substream_t * substream) | ||
563 | { | ||
564 | return snd_pcm_lib_free_pages(substream); | ||
565 | } | ||
566 | |||
567 | static int snd_ad1848_capture_prepare(snd_pcm_substream_t * substream) | ||
568 | { | ||
569 | ad1848_t *chip = snd_pcm_substream_chip(substream); | ||
570 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
571 | 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 | |||
586 | static irqreturn_t snd_ad1848_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
587 | { | ||
588 | ad1848_t *chip = dev_id; | ||
589 | |||
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 | |||
600 | static snd_pcm_uframes_t snd_ad1848_playback_pointer(snd_pcm_substream_t * substream) | ||
601 | { | ||
602 | ad1848_t *chip = snd_pcm_substream_chip(substream); | ||
603 | 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 | |||
611 | static snd_pcm_uframes_t snd_ad1848_capture_pointer(snd_pcm_substream_t * substream) | ||
612 | { | ||
613 | ad1848_t *chip = snd_pcm_substream_chip(substream); | ||
614 | 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 | |||
626 | static void snd_ad1848_thinkpad_twiddle(ad1848_t *chip, int on) { | ||
627 | |||
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 | ||
647 | static int snd_ad1848_suspend(snd_card_t *card, pm_message_t state) | ||
648 | { | ||
649 | ad1848_t *chip = card->pm_private_data; | ||
650 | |||
651 | snd_pcm_suspend_all(chip->pcm); | ||
652 | /* FIXME: save registers? */ | ||
653 | |||
654 | if (chip->thinkpad_flag) | ||
655 | snd_ad1848_thinkpad_twiddle(chip, 0); | ||
656 | |||
657 | return 0; | ||
658 | } | ||
659 | |||
660 | static int snd_ad1848_resume(snd_card_t *card) | ||
661 | { | ||
662 | ad1848_t *chip = card->pm_private_data; | ||
663 | |||
664 | if (chip->thinkpad_flag) | ||
665 | snd_ad1848_thinkpad_twiddle(chip, 1); | ||
666 | |||
667 | /* FIXME: restore registers? */ | ||
668 | |||
669 | return 0; | ||
670 | } | ||
671 | #endif /* CONFIG_PM */ | ||
672 | |||
673 | static int snd_ad1848_probe(ad1848_t * chip) | ||
674 | { | ||
675 | unsigned long flags; | ||
676 | int i, id, rev, ad1847; | ||
677 | unsigned char *ptr; | ||
678 | |||
679 | #if 0 | ||
680 | snd_ad1848_debug(chip); | ||
681 | #endif | ||
682 | id = ad1847 = 0; | ||
683 | for (i = 0; i < 1000; i++) { | ||
684 | mb(); | ||
685 | if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT) | ||
686 | udelay(500); | ||
687 | else { | ||
688 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
689 | snd_ad1848_out(chip, AD1848_MISC_INFO, 0x00); | ||
690 | snd_ad1848_out(chip, AD1848_LEFT_INPUT, 0xaa); | ||
691 | snd_ad1848_out(chip, AD1848_RIGHT_INPUT, 0x45); | ||
692 | rev = snd_ad1848_in(chip, AD1848_RIGHT_INPUT); | ||
693 | if (rev == 0x65) { | ||
694 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
695 | id = 1; | ||
696 | ad1847 = 1; | ||
697 | break; | ||
698 | } | ||
699 | if (snd_ad1848_in(chip, AD1848_LEFT_INPUT) == 0xaa && rev == 0x45) { | ||
700 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
701 | id = 1; | ||
702 | break; | ||
703 | } | ||
704 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
705 | } | ||
706 | } | ||
707 | if (id != 1) | ||
708 | return -ENODEV; /* no valid device found */ | ||
709 | if (chip->hardware == AD1848_HW_DETECT) { | ||
710 | if (ad1847) { | ||
711 | chip->hardware = AD1848_HW_AD1847; | ||
712 | } else { | ||
713 | chip->hardware = AD1848_HW_AD1848; | ||
714 | rev = snd_ad1848_in(chip, AD1848_MISC_INFO); | ||
715 | if (rev & 0x80) { | ||
716 | chip->hardware = AD1848_HW_CS4248; | ||
717 | } else if ((rev & 0x0f) == 0x0a) { | ||
718 | snd_ad1848_out(chip, AD1848_MISC_INFO, 0x40); | ||
719 | for (i = 0; i < 16; ++i) { | ||
720 | if (snd_ad1848_in(chip, i) != snd_ad1848_in(chip, i + 16)) { | ||
721 | chip->hardware = AD1848_HW_CMI8330; | ||
722 | break; | ||
723 | } | ||
724 | } | ||
725 | snd_ad1848_out(chip, AD1848_MISC_INFO, 0x00); | ||
726 | } | ||
727 | } | ||
728 | } | ||
729 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
730 | inb(AD1848P(chip, STATUS)); /* clear any pendings IRQ */ | ||
731 | outb(0, AD1848P(chip, STATUS)); | ||
732 | mb(); | ||
733 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
734 | |||
735 | chip->image[AD1848_MISC_INFO] = 0x00; | ||
736 | chip->image[AD1848_IFACE_CTRL] = | ||
737 | (chip->image[AD1848_IFACE_CTRL] & ~AD1848_SINGLE_DMA) | AD1848_SINGLE_DMA; | ||
738 | ptr = (unsigned char *) &chip->image; | ||
739 | snd_ad1848_mce_down(chip); | ||
740 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
741 | for (i = 0; i < 16; i++) /* ok.. fill all AD1848 registers */ | ||
742 | snd_ad1848_out(chip, i, *ptr++); | ||
743 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
744 | snd_ad1848_mce_up(chip); | ||
745 | snd_ad1848_mce_down(chip); | ||
746 | return 0; /* all things are ok.. */ | ||
747 | } | ||
748 | |||
749 | /* | ||
750 | |||
751 | */ | ||
752 | |||
753 | static snd_pcm_hardware_t snd_ad1848_playback = | ||
754 | { | ||
755 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||
756 | SNDRV_PCM_INFO_MMAP_VALID), | ||
757 | .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | | ||
758 | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE), | ||
759 | .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, | ||
760 | .rate_min = 5510, | ||
761 | .rate_max = 48000, | ||
762 | .channels_min = 1, | ||
763 | .channels_max = 2, | ||
764 | .buffer_bytes_max = (128*1024), | ||
765 | .period_bytes_min = 64, | ||
766 | .period_bytes_max = (128*1024), | ||
767 | .periods_min = 1, | ||
768 | .periods_max = 1024, | ||
769 | .fifo_size = 0, | ||
770 | }; | ||
771 | |||
772 | static snd_pcm_hardware_t snd_ad1848_capture = | ||
773 | { | ||
774 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||
775 | SNDRV_PCM_INFO_MMAP_VALID), | ||
776 | .formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | | ||
777 | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE), | ||
778 | .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, | ||
779 | .rate_min = 5510, | ||
780 | .rate_max = 48000, | ||
781 | .channels_min = 1, | ||
782 | .channels_max = 2, | ||
783 | .buffer_bytes_max = (128*1024), | ||
784 | .period_bytes_min = 64, | ||
785 | .period_bytes_max = (128*1024), | ||
786 | .periods_min = 1, | ||
787 | .periods_max = 1024, | ||
788 | .fifo_size = 0, | ||
789 | }; | ||
790 | |||
791 | /* | ||
792 | |||
793 | */ | ||
794 | |||
795 | static int snd_ad1848_playback_open(snd_pcm_substream_t * substream) | ||
796 | { | ||
797 | ad1848_t *chip = snd_pcm_substream_chip(substream); | ||
798 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
799 | int err; | ||
800 | |||
801 | if ((err = snd_ad1848_open(chip, AD1848_MODE_PLAY)) < 0) | ||
802 | return err; | ||
803 | chip->playback_substream = substream; | ||
804 | runtime->hw = snd_ad1848_playback; | ||
805 | snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.buffer_bytes_max); | ||
806 | snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.period_bytes_max); | ||
807 | snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); | ||
808 | return 0; | ||
809 | } | ||
810 | |||
811 | static int snd_ad1848_capture_open(snd_pcm_substream_t * substream) | ||
812 | { | ||
813 | ad1848_t *chip = snd_pcm_substream_chip(substream); | ||
814 | snd_pcm_runtime_t *runtime = substream->runtime; | ||
815 | int err; | ||
816 | |||
817 | if ((err = snd_ad1848_open(chip, AD1848_MODE_CAPTURE)) < 0) | ||
818 | return err; | ||
819 | chip->capture_substream = substream; | ||
820 | runtime->hw = snd_ad1848_capture; | ||
821 | snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.buffer_bytes_max); | ||
822 | snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.period_bytes_max); | ||
823 | snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); | ||
824 | return 0; | ||
825 | } | ||
826 | |||
827 | static int snd_ad1848_playback_close(snd_pcm_substream_t * substream) | ||
828 | { | ||
829 | ad1848_t *chip = snd_pcm_substream_chip(substream); | ||
830 | |||
831 | chip->mode &= ~AD1848_MODE_PLAY; | ||
832 | chip->playback_substream = NULL; | ||
833 | snd_ad1848_close(chip); | ||
834 | return 0; | ||
835 | } | ||
836 | |||
837 | static int snd_ad1848_capture_close(snd_pcm_substream_t * substream) | ||
838 | { | ||
839 | ad1848_t *chip = snd_pcm_substream_chip(substream); | ||
840 | |||
841 | chip->mode &= ~AD1848_MODE_CAPTURE; | ||
842 | chip->capture_substream = NULL; | ||
843 | snd_ad1848_close(chip); | ||
844 | return 0; | ||
845 | } | ||
846 | |||
847 | static int snd_ad1848_free(ad1848_t *chip) | ||
848 | { | ||
849 | if (chip->res_port) { | ||
850 | release_resource(chip->res_port); | ||
851 | kfree_nocheck(chip->res_port); | ||
852 | } | ||
853 | if (chip->irq >= 0) | ||
854 | free_irq(chip->irq, (void *) chip); | ||
855 | if (chip->dma >= 0) { | ||
856 | snd_dma_disable(chip->dma); | ||
857 | free_dma(chip->dma); | ||
858 | } | ||
859 | kfree(chip); | ||
860 | return 0; | ||
861 | } | ||
862 | |||
863 | static int snd_ad1848_dev_free(snd_device_t *device) | ||
864 | { | ||
865 | ad1848_t *chip = device->device_data; | ||
866 | return snd_ad1848_free(chip); | ||
867 | } | ||
868 | |||
869 | static const char *snd_ad1848_chip_id(ad1848_t *chip) | ||
870 | { | ||
871 | switch (chip->hardware) { | ||
872 | case AD1848_HW_AD1847: return "AD1847"; | ||
873 | case AD1848_HW_AD1848: return "AD1848"; | ||
874 | case AD1848_HW_CS4248: return "CS4248"; | ||
875 | case AD1848_HW_CMI8330: return "CMI8330/C3D"; | ||
876 | default: return "???"; | ||
877 | } | ||
878 | } | ||
879 | |||
880 | int snd_ad1848_create(snd_card_t * card, | ||
881 | unsigned long port, | ||
882 | int irq, int dma, | ||
883 | unsigned short hardware, | ||
884 | ad1848_t ** rchip) | ||
885 | { | ||
886 | static snd_device_ops_t ops = { | ||
887 | .dev_free = snd_ad1848_dev_free, | ||
888 | }; | ||
889 | ad1848_t *chip; | ||
890 | int err; | ||
891 | |||
892 | *rchip = NULL; | ||
893 | chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); | ||
894 | if (chip == NULL) | ||
895 | return -ENOMEM; | ||
896 | spin_lock_init(&chip->reg_lock); | ||
897 | init_MUTEX(&chip->open_mutex); | ||
898 | chip->card = card; | ||
899 | chip->port = port; | ||
900 | chip->irq = -1; | ||
901 | chip->dma = -1; | ||
902 | chip->hardware = hardware; | ||
903 | memcpy(&chip->image, &snd_ad1848_original_image, sizeof(snd_ad1848_original_image)); | ||
904 | |||
905 | if ((chip->res_port = request_region(port, 4, "AD1848")) == NULL) { | ||
906 | snd_printk(KERN_ERR "ad1848: can't grab port 0x%lx\n", port); | ||
907 | snd_ad1848_free(chip); | ||
908 | return -EBUSY; | ||
909 | } | ||
910 | if (request_irq(irq, snd_ad1848_interrupt, SA_INTERRUPT, "AD1848", (void *) chip)) { | ||
911 | snd_printk(KERN_ERR "ad1848: can't grab IRQ %d\n", irq); | ||
912 | snd_ad1848_free(chip); | ||
913 | return -EBUSY; | ||
914 | } | ||
915 | chip->irq = irq; | ||
916 | if (request_dma(dma, "AD1848")) { | ||
917 | snd_printk(KERN_ERR "ad1848: can't grab DMA %d\n", dma); | ||
918 | snd_ad1848_free(chip); | ||
919 | return -EBUSY; | ||
920 | } | ||
921 | chip->dma = dma; | ||
922 | |||
923 | if (hardware == AD1848_HW_THINKPAD) { | ||
924 | chip->thinkpad_flag = 1; | ||
925 | chip->hardware = AD1848_HW_DETECT; /* reset */ | ||
926 | snd_ad1848_thinkpad_twiddle(chip, 1); | ||
927 | snd_card_set_isa_pm_callback(card, snd_ad1848_suspend, snd_ad1848_resume, chip); | ||
928 | } | ||
929 | |||
930 | if (snd_ad1848_probe(chip) < 0) { | ||
931 | snd_ad1848_free(chip); | ||
932 | return -ENODEV; | ||
933 | } | ||
934 | |||
935 | /* Register device */ | ||
936 | if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { | ||
937 | snd_ad1848_free(chip); | ||
938 | return err; | ||
939 | } | ||
940 | |||
941 | *rchip = chip; | ||
942 | return 0; | ||
943 | } | ||
944 | |||
945 | static snd_pcm_ops_t snd_ad1848_playback_ops = { | ||
946 | .open = snd_ad1848_playback_open, | ||
947 | .close = snd_ad1848_playback_close, | ||
948 | .ioctl = snd_ad1848_ioctl, | ||
949 | .hw_params = snd_ad1848_playback_hw_params, | ||
950 | .hw_free = snd_ad1848_playback_hw_free, | ||
951 | .prepare = snd_ad1848_playback_prepare, | ||
952 | .trigger = snd_ad1848_playback_trigger, | ||
953 | .pointer = snd_ad1848_playback_pointer, | ||
954 | }; | ||
955 | |||
956 | static snd_pcm_ops_t snd_ad1848_capture_ops = { | ||
957 | .open = snd_ad1848_capture_open, | ||
958 | .close = snd_ad1848_capture_close, | ||
959 | .ioctl = snd_ad1848_ioctl, | ||
960 | .hw_params = snd_ad1848_capture_hw_params, | ||
961 | .hw_free = snd_ad1848_capture_hw_free, | ||
962 | .prepare = snd_ad1848_capture_prepare, | ||
963 | .trigger = snd_ad1848_capture_trigger, | ||
964 | .pointer = snd_ad1848_capture_pointer, | ||
965 | }; | ||
966 | |||
967 | static void snd_ad1848_pcm_free(snd_pcm_t *pcm) | ||
968 | { | ||
969 | ad1848_t *chip = pcm->private_data; | ||
970 | chip->pcm = NULL; | ||
971 | snd_pcm_lib_preallocate_free_for_all(pcm); | ||
972 | } | ||
973 | |||
974 | int snd_ad1848_pcm(ad1848_t *chip, int device, snd_pcm_t **rpcm) | ||
975 | { | ||
976 | snd_pcm_t *pcm; | ||
977 | int err; | ||
978 | |||
979 | if ((err = snd_pcm_new(chip->card, "AD1848", device, 1, 1, &pcm)) < 0) | ||
980 | return err; | ||
981 | |||
982 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ad1848_playback_ops); | ||
983 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ad1848_capture_ops); | ||
984 | |||
985 | pcm->private_free = snd_ad1848_pcm_free; | ||
986 | pcm->private_data = chip; | ||
987 | pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; | ||
988 | strcpy(pcm->name, snd_ad1848_chip_id(chip)); | ||
989 | |||
990 | snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, | ||
991 | snd_dma_isa_data(), | ||
992 | 64*1024, chip->dma > 3 ? 128*1024 : 64*1024); | ||
993 | |||
994 | chip->pcm = pcm; | ||
995 | if (rpcm) | ||
996 | *rpcm = pcm; | ||
997 | return 0; | ||
998 | } | ||
999 | |||
1000 | const snd_pcm_ops_t *snd_ad1848_get_pcm_ops(int direction) | ||
1001 | { | ||
1002 | return direction == SNDRV_PCM_STREAM_PLAYBACK ? | ||
1003 | &snd_ad1848_playback_ops : &snd_ad1848_capture_ops; | ||
1004 | } | ||
1005 | |||
1006 | /* | ||
1007 | * MIXER part | ||
1008 | */ | ||
1009 | |||
1010 | static int snd_ad1848_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
1011 | { | ||
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 | |||
1025 | static int snd_ad1848_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
1026 | { | ||
1027 | ad1848_t *chip = snd_kcontrol_chip(kcontrol); | ||
1028 | 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 | |||
1037 | static int snd_ad1848_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
1038 | { | ||
1039 | ad1848_t *chip = snd_kcontrol_chip(kcontrol); | ||
1040 | 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 | |||
1060 | static int snd_ad1848_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
1061 | { | ||
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 | |||
1071 | static int snd_ad1848_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
1072 | { | ||
1073 | ad1848_t *chip = snd_kcontrol_chip(kcontrol); | ||
1074 | 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 | |||
1088 | static int snd_ad1848_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
1089 | { | ||
1090 | ad1848_t *chip = snd_kcontrol_chip(kcontrol); | ||
1091 | 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 | |||
1111 | static int snd_ad1848_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) | ||
1112 | { | ||
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 | |||
1122 | static int snd_ad1848_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
1123 | { | ||
1124 | ad1848_t *chip = snd_kcontrol_chip(kcontrol); | ||
1125 | 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 | |||
1144 | static int snd_ad1848_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) | ||
1145 | { | ||
1146 | ad1848_t *chip = snd_kcontrol_chip(kcontrol); | ||
1147 | 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 | */ | ||
1183 | int snd_ad1848_add_ctl(ad1848_t *chip, const char *name, int index, int type, unsigned long value) | ||
1184 | { | ||
1185 | static snd_kcontrol_new_t newctls[] = { | ||
1186 | [AD1848_MIX_SINGLE] = { | ||
1187 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
1188 | .info = snd_ad1848_info_single, | ||
1189 | .get = snd_ad1848_get_single, | ||
1190 | .put = snd_ad1848_put_single, | ||
1191 | }, | ||
1192 | [AD1848_MIX_DOUBLE] = { | ||
1193 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
1194 | .info = snd_ad1848_info_double, | ||
1195 | .get = snd_ad1848_get_double, | ||
1196 | .put = snd_ad1848_put_double, | ||
1197 | }, | ||
1198 | [AD1848_MIX_CAPTURE] = { | ||
1199 | .info = snd_ad1848_info_mux, | ||
1200 | .get = snd_ad1848_get_mux, | ||
1201 | .put = snd_ad1848_put_mux, | ||
1202 | }, | ||
1203 | }; | ||
1204 | snd_kcontrol_t *ctl; | ||
1205 | int err; | ||
1206 | |||
1207 | ctl = snd_ctl_new1(&newctls[type], chip); | ||
1208 | if (! ctl) | ||
1209 | return -ENOMEM; | ||
1210 | strlcpy(ctl->id.name, name, sizeof(ctl->id.name)); | ||
1211 | ctl->id.index = index; | ||
1212 | ctl->private_value = value; | ||
1213 | if ((err = snd_ctl_add(chip->card, ctl)) < 0) { | ||
1214 | snd_ctl_free_one(ctl); | ||
1215 | return err; | ||
1216 | } | ||
1217 | return 0; | ||
1218 | } | ||
1219 | |||
1220 | |||
1221 | static struct ad1848_mix_elem snd_ad1848_controls[] = { | ||
1222 | AD1848_DOUBLE("PCM Playback Switch", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 7, 7, 1, 1), | ||
1223 | AD1848_DOUBLE("PCM Playback Volume", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 0, 0, 63, 1), | ||
1224 | AD1848_DOUBLE("Aux Playback Switch", 0, AD1848_AUX1_LEFT_INPUT, AD1848_AUX1_RIGHT_INPUT, 7, 7, 1, 1), | ||
1225 | AD1848_DOUBLE("Aux Playback Volume", 0, AD1848_AUX1_LEFT_INPUT, AD1848_AUX1_RIGHT_INPUT, 0, 0, 31, 1), | ||
1226 | AD1848_DOUBLE("Aux Playback Switch", 1, AD1848_AUX2_LEFT_INPUT, AD1848_AUX2_RIGHT_INPUT, 7, 7, 1, 1), | ||
1227 | AD1848_DOUBLE("Aux Playback Volume", 1, AD1848_AUX2_LEFT_INPUT, AD1848_AUX2_RIGHT_INPUT, 0, 0, 31, 1), | ||
1228 | AD1848_DOUBLE("Capture Volume", 0, AD1848_LEFT_INPUT, AD1848_RIGHT_INPUT, 0, 0, 15, 0), | ||
1229 | { | ||
1230 | .name = "Capture Source", | ||
1231 | .type = AD1848_MIX_CAPTURE, | ||
1232 | }, | ||
1233 | AD1848_SINGLE("Loopback Capture Switch", 0, AD1848_LOOPBACK, 0, 1, 0), | ||
1234 | AD1848_SINGLE("Loopback Capture Volume", 0, AD1848_LOOPBACK, 1, 63, 0) | ||
1235 | }; | ||
1236 | |||
1237 | int snd_ad1848_mixer(ad1848_t *chip) | ||
1238 | { | ||
1239 | snd_card_t *card; | ||
1240 | snd_pcm_t *pcm; | ||
1241 | unsigned int idx; | ||
1242 | int err; | ||
1243 | |||
1244 | snd_assert(chip != NULL && chip->pcm != NULL, return -EINVAL); | ||
1245 | |||
1246 | pcm = chip->pcm; | ||
1247 | card = chip->card; | ||
1248 | |||
1249 | strcpy(card->mixername, pcm->name); | ||
1250 | |||
1251 | for (idx = 0; idx < ARRAY_SIZE(snd_ad1848_controls); idx++) | ||
1252 | if ((err = snd_ad1848_add_ctl_elem(chip, &snd_ad1848_controls[idx])) < 0) | ||
1253 | return err; | ||
1254 | |||
1255 | return 0; | ||
1256 | } | ||
1257 | |||
1258 | EXPORT_SYMBOL(snd_ad1848_out); | ||
1259 | EXPORT_SYMBOL(snd_ad1848_create); | ||
1260 | EXPORT_SYMBOL(snd_ad1848_pcm); | ||
1261 | EXPORT_SYMBOL(snd_ad1848_get_pcm_ops); | ||
1262 | EXPORT_SYMBOL(snd_ad1848_mixer); | ||
1263 | EXPORT_SYMBOL(snd_ad1848_add_ctl); | ||
1264 | |||
1265 | /* | ||
1266 | * INIT part | ||
1267 | */ | ||
1268 | |||
1269 | static int __init alsa_ad1848_init(void) | ||
1270 | { | ||
1271 | return 0; | ||
1272 | } | ||
1273 | |||
1274 | static void __exit alsa_ad1848_exit(void) | ||
1275 | { | ||
1276 | } | ||
1277 | |||
1278 | module_init(alsa_ad1848_init) | ||
1279 | module_exit(alsa_ad1848_exit) | ||