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