diff options
Diffstat (limited to 'sound/pcmcia/vx/vxp_ops.c')
-rw-r--r-- | sound/pcmcia/vx/vxp_ops.c | 614 |
1 files changed, 614 insertions, 0 deletions
diff --git a/sound/pcmcia/vx/vxp_ops.c b/sound/pcmcia/vx/vxp_ops.c new file mode 100644 index 000000000000..ef6734271607 --- /dev/null +++ b/sound/pcmcia/vx/vxp_ops.c | |||
@@ -0,0 +1,614 @@ | |||
1 | /* | ||
2 | * Driver for Digigram VXpocket soundcards | ||
3 | * | ||
4 | * lowlevel routines for VXpocket soundcards | ||
5 | * | ||
6 | * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | */ | ||
22 | |||
23 | #include <sound/driver.h> | ||
24 | #include <linux/delay.h> | ||
25 | #include <linux/device.h> | ||
26 | #include <linux/firmware.h> | ||
27 | #include <sound/core.h> | ||
28 | #include <asm/io.h> | ||
29 | #include "vxpocket.h" | ||
30 | |||
31 | |||
32 | static int vxp_reg_offset[VX_REG_MAX] = { | ||
33 | [VX_ICR] = 0x00, // ICR | ||
34 | [VX_CVR] = 0x01, // CVR | ||
35 | [VX_ISR] = 0x02, // ISR | ||
36 | [VX_IVR] = 0x03, // IVR | ||
37 | [VX_RXH] = 0x05, // RXH | ||
38 | [VX_RXM] = 0x06, // RXM | ||
39 | [VX_RXL] = 0x07, // RXL | ||
40 | [VX_DMA] = 0x04, // DMA | ||
41 | [VX_CDSP] = 0x08, // CDSP | ||
42 | [VX_LOFREQ] = 0x09, // LFREQ | ||
43 | [VX_HIFREQ] = 0x0a, // HFREQ | ||
44 | [VX_DATA] = 0x0b, // DATA | ||
45 | [VX_MICRO] = 0x0c, // MICRO | ||
46 | [VX_DIALOG] = 0x0d, // DIALOG | ||
47 | [VX_CSUER] = 0x0e, // CSUER | ||
48 | [VX_RUER] = 0x0f, // RUER | ||
49 | }; | ||
50 | |||
51 | |||
52 | inline static unsigned long vxp_reg_addr(vx_core_t *_chip, int reg) | ||
53 | { | ||
54 | struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; | ||
55 | return chip->port + vxp_reg_offset[reg]; | ||
56 | } | ||
57 | |||
58 | /* | ||
59 | * snd_vx_inb - read a byte from the register | ||
60 | * @offset: register offset | ||
61 | */ | ||
62 | static unsigned char vxp_inb(vx_core_t *chip, int offset) | ||
63 | { | ||
64 | return inb(vxp_reg_addr(chip, offset)); | ||
65 | } | ||
66 | |||
67 | /* | ||
68 | * snd_vx_outb - write a byte on the register | ||
69 | * @offset: the register offset | ||
70 | * @val: the value to write | ||
71 | */ | ||
72 | static void vxp_outb(vx_core_t *chip, int offset, unsigned char val) | ||
73 | { | ||
74 | outb(val, vxp_reg_addr(chip, offset)); | ||
75 | } | ||
76 | |||
77 | /* | ||
78 | * redefine macros to call directly | ||
79 | */ | ||
80 | #undef vx_inb | ||
81 | #define vx_inb(chip,reg) vxp_inb((vx_core_t*)(chip), VX_##reg) | ||
82 | #undef vx_outb | ||
83 | #define vx_outb(chip,reg,val) vxp_outb((vx_core_t*)(chip), VX_##reg,val) | ||
84 | |||
85 | |||
86 | /* | ||
87 | * vx_check_magic - check the magic word on xilinx | ||
88 | * | ||
89 | * returns zero if a magic word is detected, or a negative error code. | ||
90 | */ | ||
91 | static int vx_check_magic(vx_core_t *chip) | ||
92 | { | ||
93 | unsigned long end_time = jiffies + HZ / 5; | ||
94 | int c; | ||
95 | do { | ||
96 | c = vx_inb(chip, CDSP); | ||
97 | if (c == CDSP_MAGIC) | ||
98 | return 0; | ||
99 | snd_vx_delay(chip, 10); | ||
100 | } while (time_after_eq(end_time, jiffies)); | ||
101 | snd_printk(KERN_ERR "cannot find xilinx magic word (%x)\n", c); | ||
102 | return -EIO; | ||
103 | } | ||
104 | |||
105 | |||
106 | /* | ||
107 | * vx_reset_dsp - reset the DSP | ||
108 | */ | ||
109 | |||
110 | #define XX_DSP_RESET_WAIT_TIME 2 /* ms */ | ||
111 | |||
112 | static void vxp_reset_dsp(vx_core_t *_chip) | ||
113 | { | ||
114 | struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; | ||
115 | |||
116 | /* set the reset dsp bit to 1 */ | ||
117 | vx_outb(chip, CDSP, chip->regCDSP | VXP_CDSP_DSP_RESET_MASK); | ||
118 | vx_inb(chip, CDSP); | ||
119 | mdelay(XX_DSP_RESET_WAIT_TIME); | ||
120 | /* reset the bit */ | ||
121 | chip->regCDSP &= ~VXP_CDSP_DSP_RESET_MASK; | ||
122 | vx_outb(chip, CDSP, chip->regCDSP); | ||
123 | vx_inb(chip, CDSP); | ||
124 | mdelay(XX_DSP_RESET_WAIT_TIME); | ||
125 | } | ||
126 | |||
127 | /* | ||
128 | * reset codec bit | ||
129 | */ | ||
130 | static void vxp_reset_codec(vx_core_t *_chip) | ||
131 | { | ||
132 | struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; | ||
133 | |||
134 | /* Set the reset CODEC bit to 1. */ | ||
135 | vx_outb(chip, CDSP, chip->regCDSP | VXP_CDSP_CODEC_RESET_MASK); | ||
136 | vx_inb(chip, CDSP); | ||
137 | snd_vx_delay(_chip, 10); | ||
138 | /* Set the reset CODEC bit to 0. */ | ||
139 | chip->regCDSP &= ~VXP_CDSP_CODEC_RESET_MASK; | ||
140 | vx_outb(chip, CDSP, chip->regCDSP); | ||
141 | vx_inb(chip, CDSP); | ||
142 | snd_vx_delay(_chip, 1); | ||
143 | } | ||
144 | |||
145 | /* | ||
146 | * vx_load_xilinx_binary - load the xilinx binary image | ||
147 | * the binary image is the binary array converted from the bitstream file. | ||
148 | */ | ||
149 | static int vxp_load_xilinx_binary(vx_core_t *_chip, const struct firmware *fw) | ||
150 | { | ||
151 | struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; | ||
152 | unsigned int i; | ||
153 | int c; | ||
154 | int regCSUER, regRUER; | ||
155 | unsigned char *image; | ||
156 | unsigned char data; | ||
157 | |||
158 | /* Switch to programmation mode */ | ||
159 | chip->regDIALOG |= VXP_DLG_XILINX_REPROG_MASK; | ||
160 | vx_outb(chip, DIALOG, chip->regDIALOG); | ||
161 | |||
162 | /* Save register CSUER and RUER */ | ||
163 | regCSUER = vx_inb(chip, CSUER); | ||
164 | regRUER = vx_inb(chip, RUER); | ||
165 | |||
166 | /* reset HF0 and HF1 */ | ||
167 | vx_outb(chip, ICR, 0); | ||
168 | |||
169 | /* Wait for answer HF2 equal to 1 */ | ||
170 | snd_printdd(KERN_DEBUG "check ISR_HF2\n"); | ||
171 | if (vx_check_isr(_chip, ISR_HF2, ISR_HF2, 20) < 0) | ||
172 | goto _error; | ||
173 | |||
174 | /* set HF1 for loading xilinx binary */ | ||
175 | vx_outb(chip, ICR, ICR_HF1); | ||
176 | image = fw->data; | ||
177 | for (i = 0; i < fw->size; i++, image++) { | ||
178 | data = *image; | ||
179 | if (vx_wait_isr_bit(_chip, ISR_TX_EMPTY) < 0) | ||
180 | goto _error; | ||
181 | vx_outb(chip, TXL, data); | ||
182 | /* wait for reading */ | ||
183 | if (vx_wait_for_rx_full(_chip) < 0) | ||
184 | goto _error; | ||
185 | c = vx_inb(chip, RXL); | ||
186 | if (c != (int)data) | ||
187 | snd_printk(KERN_ERR "vxpocket: load xilinx mismatch at %d: 0x%x != 0x%x\n", i, c, (int)data); | ||
188 | } | ||
189 | |||
190 | /* reset HF1 */ | ||
191 | vx_outb(chip, ICR, 0); | ||
192 | |||
193 | /* wait for HF3 */ | ||
194 | if (vx_check_isr(_chip, ISR_HF3, ISR_HF3, 20) < 0) | ||
195 | goto _error; | ||
196 | |||
197 | /* read the number of bytes received */ | ||
198 | if (vx_wait_for_rx_full(_chip) < 0) | ||
199 | goto _error; | ||
200 | |||
201 | c = (int)vx_inb(chip, RXH) << 16; | ||
202 | c |= (int)vx_inb(chip, RXM) << 8; | ||
203 | c |= vx_inb(chip, RXL); | ||
204 | |||
205 | snd_printdd(KERN_DEBUG "xilinx: dsp size received 0x%x, orig 0x%x\n", c, fw->size); | ||
206 | |||
207 | vx_outb(chip, ICR, ICR_HF0); | ||
208 | |||
209 | /* TEMPO 250ms : wait until Xilinx is downloaded */ | ||
210 | snd_vx_delay(_chip, 300); | ||
211 | |||
212 | /* test magical word */ | ||
213 | if (vx_check_magic(_chip) < 0) | ||
214 | goto _error; | ||
215 | |||
216 | /* Restore register 0x0E and 0x0F (thus replacing COR and FCSR) */ | ||
217 | vx_outb(chip, CSUER, regCSUER); | ||
218 | vx_outb(chip, RUER, regRUER); | ||
219 | |||
220 | /* Reset the Xilinx's signal enabling IO access */ | ||
221 | chip->regDIALOG |= VXP_DLG_XILINX_REPROG_MASK; | ||
222 | vx_outb(chip, DIALOG, chip->regDIALOG); | ||
223 | vx_inb(chip, DIALOG); | ||
224 | snd_vx_delay(_chip, 10); | ||
225 | chip->regDIALOG &= ~VXP_DLG_XILINX_REPROG_MASK; | ||
226 | vx_outb(chip, DIALOG, chip->regDIALOG); | ||
227 | vx_inb(chip, DIALOG); | ||
228 | |||
229 | /* Reset of the Codec */ | ||
230 | vxp_reset_codec(_chip); | ||
231 | vx_reset_dsp(_chip); | ||
232 | |||
233 | return 0; | ||
234 | |||
235 | _error: | ||
236 | vx_outb(chip, CSUER, regCSUER); | ||
237 | vx_outb(chip, RUER, regRUER); | ||
238 | chip->regDIALOG &= ~VXP_DLG_XILINX_REPROG_MASK; | ||
239 | vx_outb(chip, DIALOG, chip->regDIALOG); | ||
240 | return -EIO; | ||
241 | } | ||
242 | |||
243 | |||
244 | /* | ||
245 | * vxp_load_dsp - load_dsp callback | ||
246 | */ | ||
247 | static int vxp_load_dsp(vx_core_t *vx, int index, const struct firmware *fw) | ||
248 | { | ||
249 | int err; | ||
250 | |||
251 | switch (index) { | ||
252 | case 0: | ||
253 | /* xilinx boot */ | ||
254 | if ((err = vx_check_magic(vx)) < 0) | ||
255 | return err; | ||
256 | if ((err = snd_vx_load_boot_image(vx, fw)) < 0) | ||
257 | return err; | ||
258 | return 0; | ||
259 | case 1: | ||
260 | /* xilinx image */ | ||
261 | return vxp_load_xilinx_binary(vx, fw); | ||
262 | case 2: | ||
263 | /* DSP boot */ | ||
264 | return snd_vx_dsp_boot(vx, fw); | ||
265 | case 3: | ||
266 | /* DSP image */ | ||
267 | return snd_vx_dsp_load(vx, fw); | ||
268 | default: | ||
269 | snd_BUG(); | ||
270 | return -EINVAL; | ||
271 | } | ||
272 | } | ||
273 | |||
274 | |||
275 | /* | ||
276 | * vx_test_and_ack - test and acknowledge interrupt | ||
277 | * | ||
278 | * called from irq hander, too | ||
279 | * | ||
280 | * spinlock held! | ||
281 | */ | ||
282 | static int vxp_test_and_ack(vx_core_t *_chip) | ||
283 | { | ||
284 | struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; | ||
285 | |||
286 | /* not booted yet? */ | ||
287 | if (! (_chip->chip_status & VX_STAT_XILINX_LOADED)) | ||
288 | return -ENXIO; | ||
289 | |||
290 | if (! (vx_inb(chip, DIALOG) & VXP_DLG_MEMIRQ_MASK)) | ||
291 | return -EIO; | ||
292 | |||
293 | /* ok, interrupts generated, now ack it */ | ||
294 | /* set ACQUIT bit up and down */ | ||
295 | vx_outb(chip, DIALOG, chip->regDIALOG | VXP_DLG_ACK_MEMIRQ_MASK); | ||
296 | /* useless read just to spend some time and maintain | ||
297 | * the ACQUIT signal up for a while ( a bus cycle ) | ||
298 | */ | ||
299 | vx_inb(chip, DIALOG); | ||
300 | vx_outb(chip, DIALOG, chip->regDIALOG & ~VXP_DLG_ACK_MEMIRQ_MASK); | ||
301 | |||
302 | return 0; | ||
303 | } | ||
304 | |||
305 | |||
306 | /* | ||
307 | * vx_validate_irq - enable/disable IRQ | ||
308 | */ | ||
309 | static void vxp_validate_irq(vx_core_t *_chip, int enable) | ||
310 | { | ||
311 | struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; | ||
312 | |||
313 | /* Set the interrupt enable bit to 1 in CDSP register */ | ||
314 | if (enable) | ||
315 | chip->regCDSP |= VXP_CDSP_VALID_IRQ_MASK; | ||
316 | else | ||
317 | chip->regCDSP &= ~VXP_CDSP_VALID_IRQ_MASK; | ||
318 | vx_outb(chip, CDSP, chip->regCDSP); | ||
319 | } | ||
320 | |||
321 | /* | ||
322 | * vx_setup_pseudo_dma - set up the pseudo dma read/write mode. | ||
323 | * @do_write: 0 = read, 1 = set up for DMA write | ||
324 | */ | ||
325 | static void vx_setup_pseudo_dma(vx_core_t *_chip, int do_write) | ||
326 | { | ||
327 | struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; | ||
328 | |||
329 | /* Interrupt mode and HREQ pin enabled for host transmit / receive data transfers */ | ||
330 | vx_outb(chip, ICR, do_write ? ICR_TREQ : ICR_RREQ); | ||
331 | /* Reset the pseudo-dma register */ | ||
332 | vx_inb(chip, ISR); | ||
333 | vx_outb(chip, ISR, 0); | ||
334 | |||
335 | /* Select DMA in read/write transfer mode and in 16-bit accesses */ | ||
336 | chip->regDIALOG |= VXP_DLG_DMA16_SEL_MASK; | ||
337 | chip->regDIALOG |= do_write ? VXP_DLG_DMAWRITE_SEL_MASK : VXP_DLG_DMAREAD_SEL_MASK; | ||
338 | vx_outb(chip, DIALOG, chip->regDIALOG); | ||
339 | |||
340 | } | ||
341 | |||
342 | /* | ||
343 | * vx_release_pseudo_dma - disable the pseudo-DMA mode | ||
344 | */ | ||
345 | static void vx_release_pseudo_dma(vx_core_t *_chip) | ||
346 | { | ||
347 | struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; | ||
348 | |||
349 | /* Disable DMA and 16-bit accesses */ | ||
350 | chip->regDIALOG &= ~(VXP_DLG_DMAWRITE_SEL_MASK| | ||
351 | VXP_DLG_DMAREAD_SEL_MASK| | ||
352 | VXP_DLG_DMA16_SEL_MASK); | ||
353 | vx_outb(chip, DIALOG, chip->regDIALOG); | ||
354 | /* HREQ pin disabled. */ | ||
355 | vx_outb(chip, ICR, 0); | ||
356 | } | ||
357 | |||
358 | /* | ||
359 | * vx_pseudo_dma_write - write bulk data on pseudo-DMA mode | ||
360 | * @count: data length to transfer in bytes | ||
361 | * | ||
362 | * data size must be aligned to 6 bytes to ensure the 24bit alignment on DSP. | ||
363 | * NB: call with a certain lock! | ||
364 | */ | ||
365 | static void vxp_dma_write(vx_core_t *chip, snd_pcm_runtime_t *runtime, | ||
366 | vx_pipe_t *pipe, int count) | ||
367 | { | ||
368 | long port = vxp_reg_addr(chip, VX_DMA); | ||
369 | int offset = pipe->hw_ptr; | ||
370 | unsigned short *addr = (unsigned short *)(runtime->dma_area + offset); | ||
371 | |||
372 | vx_setup_pseudo_dma(chip, 1); | ||
373 | if (offset + count > pipe->buffer_bytes) { | ||
374 | int length = pipe->buffer_bytes - offset; | ||
375 | count -= length; | ||
376 | length >>= 1; /* in 16bit words */ | ||
377 | /* Transfer using pseudo-dma. */ | ||
378 | while (length-- > 0) { | ||
379 | outw(cpu_to_le16(*addr), port); | ||
380 | addr++; | ||
381 | } | ||
382 | addr = (unsigned short *)runtime->dma_area; | ||
383 | pipe->hw_ptr = 0; | ||
384 | } | ||
385 | pipe->hw_ptr += count; | ||
386 | count >>= 1; /* in 16bit words */ | ||
387 | /* Transfer using pseudo-dma. */ | ||
388 | while (count-- > 0) { | ||
389 | outw(cpu_to_le16(*addr), port); | ||
390 | addr++; | ||
391 | } | ||
392 | vx_release_pseudo_dma(chip); | ||
393 | } | ||
394 | |||
395 | |||
396 | /* | ||
397 | * vx_pseudo_dma_read - read bulk data on pseudo DMA mode | ||
398 | * @offset: buffer offset in bytes | ||
399 | * @count: data length to transfer in bytes | ||
400 | * | ||
401 | * the read length must be aligned to 6 bytes, as well as write. | ||
402 | * NB: call with a certain lock! | ||
403 | */ | ||
404 | static void vxp_dma_read(vx_core_t *chip, snd_pcm_runtime_t *runtime, | ||
405 | vx_pipe_t *pipe, int count) | ||
406 | { | ||
407 | struct snd_vxpocket *pchip = (struct snd_vxpocket *)chip; | ||
408 | long port = vxp_reg_addr(chip, VX_DMA); | ||
409 | int offset = pipe->hw_ptr; | ||
410 | unsigned short *addr = (unsigned short *)(runtime->dma_area + offset); | ||
411 | |||
412 | snd_assert(count % 2 == 0, return); | ||
413 | vx_setup_pseudo_dma(chip, 0); | ||
414 | if (offset + count > pipe->buffer_bytes) { | ||
415 | int length = pipe->buffer_bytes - offset; | ||
416 | count -= length; | ||
417 | length >>= 1; /* in 16bit words */ | ||
418 | /* Transfer using pseudo-dma. */ | ||
419 | while (length-- > 0) | ||
420 | *addr++ = le16_to_cpu(inw(port)); | ||
421 | addr = (unsigned short *)runtime->dma_area; | ||
422 | pipe->hw_ptr = 0; | ||
423 | } | ||
424 | pipe->hw_ptr += count; | ||
425 | count >>= 1; /* in 16bit words */ | ||
426 | /* Transfer using pseudo-dma. */ | ||
427 | while (count-- > 1) | ||
428 | *addr++ = le16_to_cpu(inw(port)); | ||
429 | /* Disable DMA */ | ||
430 | pchip->regDIALOG &= ~VXP_DLG_DMAREAD_SEL_MASK; | ||
431 | vx_outb(chip, DIALOG, pchip->regDIALOG); | ||
432 | /* Read the last word (16 bits) */ | ||
433 | *addr = le16_to_cpu(inw(port)); | ||
434 | /* Disable 16-bit accesses */ | ||
435 | pchip->regDIALOG &= ~VXP_DLG_DMA16_SEL_MASK; | ||
436 | vx_outb(chip, DIALOG, pchip->regDIALOG); | ||
437 | /* HREQ pin disabled. */ | ||
438 | vx_outb(chip, ICR, 0); | ||
439 | } | ||
440 | |||
441 | |||
442 | /* | ||
443 | * write a codec data (24bit) | ||
444 | */ | ||
445 | static void vxp_write_codec_reg(vx_core_t *chip, int codec, unsigned int data) | ||
446 | { | ||
447 | int i; | ||
448 | |||
449 | /* Activate access to the corresponding codec register */ | ||
450 | if (! codec) | ||
451 | vx_inb(chip, LOFREQ); | ||
452 | else | ||
453 | vx_inb(chip, CODEC2); | ||
454 | |||
455 | /* We have to send 24 bits (3 x 8 bits). Start with most signif. Bit */ | ||
456 | for (i = 0; i < 24; i++, data <<= 1) | ||
457 | vx_outb(chip, DATA, ((data & 0x800000) ? VX_DATA_CODEC_MASK : 0)); | ||
458 | |||
459 | /* Terminate access to codec registers */ | ||
460 | vx_inb(chip, HIFREQ); | ||
461 | } | ||
462 | |||
463 | |||
464 | /* | ||
465 | * vx_set_mic_boost - set mic boost level (on vxp440 only) | ||
466 | * @boost: 0 = 20dB, 1 = +38dB | ||
467 | */ | ||
468 | void vx_set_mic_boost(vx_core_t *chip, int boost) | ||
469 | { | ||
470 | struct snd_vxpocket *pchip = (struct snd_vxpocket *)chip; | ||
471 | unsigned long flags; | ||
472 | |||
473 | if (chip->chip_status & VX_STAT_IS_STALE) | ||
474 | return; | ||
475 | |||
476 | spin_lock_irqsave(&chip->lock, flags); | ||
477 | if (pchip->regCDSP & P24_CDSP_MICS_SEL_MASK) { | ||
478 | if (boost) { | ||
479 | /* boost: 38 dB */ | ||
480 | pchip->regCDSP &= ~P24_CDSP_MIC20_SEL_MASK; | ||
481 | pchip->regCDSP |= P24_CDSP_MIC38_SEL_MASK; | ||
482 | } else { | ||
483 | /* minimum value: 20 dB */ | ||
484 | pchip->regCDSP |= P24_CDSP_MIC20_SEL_MASK; | ||
485 | pchip->regCDSP &= ~P24_CDSP_MIC38_SEL_MASK; | ||
486 | } | ||
487 | vx_outb(chip, CDSP, pchip->regCDSP); | ||
488 | } | ||
489 | spin_unlock_irqrestore(&chip->lock, flags); | ||
490 | } | ||
491 | |||
492 | /* | ||
493 | * remap the linear value (0-8) to the actual value (0-15) | ||
494 | */ | ||
495 | static int vx_compute_mic_level(int level) | ||
496 | { | ||
497 | switch (level) { | ||
498 | case 5: level = 6 ; break; | ||
499 | case 6: level = 8 ; break; | ||
500 | case 7: level = 11; break; | ||
501 | case 8: level = 15; break; | ||
502 | default: break ; | ||
503 | } | ||
504 | return level; | ||
505 | } | ||
506 | |||
507 | /* | ||
508 | * vx_set_mic_level - set mic level (on vxpocket only) | ||
509 | * @level: the mic level = 0 - 8 (max) | ||
510 | */ | ||
511 | void vx_set_mic_level(vx_core_t *chip, int level) | ||
512 | { | ||
513 | struct snd_vxpocket *pchip = (struct snd_vxpocket *)chip; | ||
514 | unsigned long flags; | ||
515 | |||
516 | if (chip->chip_status & VX_STAT_IS_STALE) | ||
517 | return; | ||
518 | |||
519 | spin_lock_irqsave(&chip->lock, flags); | ||
520 | if (pchip->regCDSP & VXP_CDSP_MIC_SEL_MASK) { | ||
521 | level = vx_compute_mic_level(level); | ||
522 | vx_outb(chip, MICRO, level); | ||
523 | } | ||
524 | spin_unlock_irqrestore(&chip->lock, flags); | ||
525 | } | ||
526 | |||
527 | |||
528 | /* | ||
529 | * change the input audio source | ||
530 | */ | ||
531 | static void vxp_change_audio_source(vx_core_t *_chip, int src) | ||
532 | { | ||
533 | struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; | ||
534 | |||
535 | switch (src) { | ||
536 | case VX_AUDIO_SRC_DIGITAL: | ||
537 | chip->regCDSP |= VXP_CDSP_DATAIN_SEL_MASK; | ||
538 | vx_outb(chip, CDSP, chip->regCDSP); | ||
539 | break; | ||
540 | case VX_AUDIO_SRC_LINE: | ||
541 | chip->regCDSP &= ~VXP_CDSP_DATAIN_SEL_MASK; | ||
542 | if (_chip->type == VX_TYPE_VXP440) | ||
543 | chip->regCDSP &= ~P24_CDSP_MICS_SEL_MASK; | ||
544 | else | ||
545 | chip->regCDSP &= ~VXP_CDSP_MIC_SEL_MASK; | ||
546 | vx_outb(chip, CDSP, chip->regCDSP); | ||
547 | break; | ||
548 | case VX_AUDIO_SRC_MIC: | ||
549 | chip->regCDSP &= ~VXP_CDSP_DATAIN_SEL_MASK; | ||
550 | /* reset mic levels */ | ||
551 | if (_chip->type == VX_TYPE_VXP440) { | ||
552 | chip->regCDSP &= ~P24_CDSP_MICS_SEL_MASK; | ||
553 | if (chip->mic_level) | ||
554 | chip->regCDSP |= P24_CDSP_MIC38_SEL_MASK; | ||
555 | else | ||
556 | chip->regCDSP |= P24_CDSP_MIC20_SEL_MASK; | ||
557 | vx_outb(chip, CDSP, chip->regCDSP); | ||
558 | } else { | ||
559 | chip->regCDSP |= VXP_CDSP_MIC_SEL_MASK; | ||
560 | vx_outb(chip, CDSP, chip->regCDSP); | ||
561 | vx_outb(chip, MICRO, vx_compute_mic_level(chip->mic_level)); | ||
562 | } | ||
563 | break; | ||
564 | } | ||
565 | } | ||
566 | |||
567 | /* | ||
568 | * change the clock source | ||
569 | * source = INTERNAL_QUARTZ or UER_SYNC | ||
570 | */ | ||
571 | static void vxp_set_clock_source(vx_core_t *_chip, int source) | ||
572 | { | ||
573 | struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; | ||
574 | |||
575 | if (source == INTERNAL_QUARTZ) | ||
576 | chip->regCDSP &= ~VXP_CDSP_CLOCKIN_SEL_MASK; | ||
577 | else | ||
578 | chip->regCDSP |= VXP_CDSP_CLOCKIN_SEL_MASK; | ||
579 | vx_outb(chip, CDSP, chip->regCDSP); | ||
580 | } | ||
581 | |||
582 | |||
583 | /* | ||
584 | * reset the board | ||
585 | */ | ||
586 | static void vxp_reset_board(vx_core_t *_chip, int cold_reset) | ||
587 | { | ||
588 | struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; | ||
589 | |||
590 | chip->regCDSP = 0; | ||
591 | chip->regDIALOG = 0; | ||
592 | } | ||
593 | |||
594 | |||
595 | /* | ||
596 | * callbacks | ||
597 | */ | ||
598 | /* exported */ | ||
599 | struct snd_vx_ops snd_vxpocket_ops = { | ||
600 | .in8 = vxp_inb, | ||
601 | .out8 = vxp_outb, | ||
602 | .test_and_ack = vxp_test_and_ack, | ||
603 | .validate_irq = vxp_validate_irq, | ||
604 | .write_codec = vxp_write_codec_reg, | ||
605 | .reset_codec = vxp_reset_codec, | ||
606 | .change_audio_source = vxp_change_audio_source, | ||
607 | .set_clock_source = vxp_set_clock_source, | ||
608 | .load_dsp = vxp_load_dsp, | ||
609 | .add_controls = vxp_add_mic_controls, | ||
610 | .reset_dsp = vxp_reset_dsp, | ||
611 | .reset_board = vxp_reset_board, | ||
612 | .dma_write = vxp_dma_write, | ||
613 | .dma_read = vxp_dma_read, | ||
614 | }; | ||