~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/pci/pcxhr/pcxhr_core.c

Version: ~ [ linux-5.16 ] ~ [ linux-5.15.13 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.90 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.170 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.224 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.261 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.296 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.298 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.18.140 ] ~ [ linux-3.16.85 ] ~ [ linux-3.14.79 ] ~ [ linux-3.12.74 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2  * Driver for Digigram pcxhr compatible soundcards
  3  *
  4  * low level interface with interrupt and message handling implementation
  5  *
  6  * Copyright (c) 2004 by Digigram <alsa@digigram.com>
  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 <linux/delay.h>
 24 #include <linux/firmware.h>
 25 #include <linux/interrupt.h>
 26 #include <linux/pci.h>
 27 #include <linux/io.h>
 28 #include <sound/core.h>
 29 #include "pcxhr.h"
 30 #include "pcxhr_mixer.h"
 31 #include "pcxhr_hwdep.h"
 32 #include "pcxhr_core.h"
 33 
 34 
 35 /* registers used on the PLX (port 1) */
 36 #define PCXHR_PLX_OFFSET_MIN    0x40
 37 #define PCXHR_PLX_MBOX0         0x40
 38 #define PCXHR_PLX_MBOX1         0x44
 39 #define PCXHR_PLX_MBOX2         0x48
 40 #define PCXHR_PLX_MBOX3         0x4C
 41 #define PCXHR_PLX_MBOX4         0x50
 42 #define PCXHR_PLX_MBOX5         0x54
 43 #define PCXHR_PLX_MBOX6         0x58
 44 #define PCXHR_PLX_MBOX7         0x5C
 45 #define PCXHR_PLX_L2PCIDB       0x64
 46 #define PCXHR_PLX_IRQCS         0x68
 47 #define PCXHR_PLX_CHIPSC        0x6C
 48 
 49 /* registers used on the DSP (port 2) */
 50 #define PCXHR_DSP_ICR           0x00
 51 #define PCXHR_DSP_CVR           0x04
 52 #define PCXHR_DSP_ISR           0x08
 53 #define PCXHR_DSP_IVR           0x0C
 54 #define PCXHR_DSP_RXH           0x14
 55 #define PCXHR_DSP_TXH           0x14
 56 #define PCXHR_DSP_RXM           0x18
 57 #define PCXHR_DSP_TXM           0x18
 58 #define PCXHR_DSP_RXL           0x1C
 59 #define PCXHR_DSP_TXL           0x1C
 60 #define PCXHR_DSP_RESET         0x20
 61 #define PCXHR_DSP_OFFSET_MAX    0x20
 62 
 63 /* access to the card */
 64 #define PCXHR_PLX 1
 65 #define PCXHR_DSP 2
 66 
 67 #if (PCXHR_DSP_OFFSET_MAX > PCXHR_PLX_OFFSET_MIN)
 68 #undef  PCXHR_REG_TO_PORT(x)
 69 #else
 70 #define PCXHR_REG_TO_PORT(x)    ((x)>PCXHR_DSP_OFFSET_MAX ? PCXHR_PLX : PCXHR_DSP)
 71 #endif
 72 #define PCXHR_INPB(mgr,x)       inb((mgr)->port[PCXHR_REG_TO_PORT(x)] + (x))
 73 #define PCXHR_INPL(mgr,x)       inl((mgr)->port[PCXHR_REG_TO_PORT(x)] + (x))
 74 #define PCXHR_OUTPB(mgr,x,data) outb((data), (mgr)->port[PCXHR_REG_TO_PORT(x)] + (x))
 75 #define PCXHR_OUTPL(mgr,x,data) outl((data), (mgr)->port[PCXHR_REG_TO_PORT(x)] + (x))
 76 /* attention : access the PCXHR_DSP_* registers with inb and outb only ! */
 77 
 78 /* params used with PCXHR_PLX_MBOX0 */
 79 #define PCXHR_MBOX0_HF5                 (1 << 0)
 80 #define PCXHR_MBOX0_HF4                 (1 << 1)
 81 #define PCXHR_MBOX0_BOOT_HERE           (1 << 23)
 82 /* params used with PCXHR_PLX_IRQCS */
 83 #define PCXHR_IRQCS_ENABLE_PCIIRQ       (1 << 8)
 84 #define PCXHR_IRQCS_ENABLE_PCIDB        (1 << 9)
 85 #define PCXHR_IRQCS_ACTIVE_PCIDB        (1 << 13)
 86 /* params used with PCXHR_PLX_CHIPSC */
 87 #define PCXHR_CHIPSC_INIT_VALUE         0x100D767E
 88 #define PCXHR_CHIPSC_RESET_XILINX       (1 << 16)
 89 #define PCXHR_CHIPSC_GPI_USERI          (1 << 17)
 90 #define PCXHR_CHIPSC_DATA_CLK           (1 << 24)
 91 #define PCXHR_CHIPSC_DATA_IN            (1 << 26)
 92 
 93 /* params used with PCXHR_DSP_ICR */
 94 #define PCXHR_ICR_HI08_RREQ             0x01
 95 #define PCXHR_ICR_HI08_TREQ             0x02
 96 #define PCXHR_ICR_HI08_HDRQ             0x04
 97 #define PCXHR_ICR_HI08_HF0              0x08
 98 #define PCXHR_ICR_HI08_HF1              0x10
 99 #define PCXHR_ICR_HI08_HLEND            0x20
100 #define PCXHR_ICR_HI08_INIT             0x80
101 /* params used with PCXHR_DSP_CVR */
102 #define PCXHR_CVR_HI08_HC               0x80
103 /* params used with PCXHR_DSP_ISR */
104 #define PCXHR_ISR_HI08_RXDF             0x01
105 #define PCXHR_ISR_HI08_TXDE             0x02
106 #define PCXHR_ISR_HI08_TRDY             0x04
107 #define PCXHR_ISR_HI08_ERR              0x08
108 #define PCXHR_ISR_HI08_CHK              0x10
109 #define PCXHR_ISR_HI08_HREQ             0x80
110 
111 
112 /* constants used for delay in msec */
113 #define PCXHR_WAIT_DEFAULT              2
114 #define PCXHR_WAIT_IT                   25
115 #define PCXHR_WAIT_IT_EXTRA             65
116 
117 /*
118  * pcxhr_check_reg_bit - wait for the specified bit is set/reset on a register
119  * @reg: register to check
120  * @mask: bit mask
121  * @bit: resultant bit to be checked
122  * @time: time-out of loop in msec
123  *
124  * returns zero if a bit matches, or a negative error code.
125  */
126 static int pcxhr_check_reg_bit(struct pcxhr_mgr *mgr, unsigned int reg,
127                                unsigned char mask, unsigned char bit, int time,
128                                unsigned char* read)
129 {
130         int i = 0;
131         unsigned long end_time = jiffies + (time * HZ + 999) / 1000;
132         do {
133                 *read = PCXHR_INPB(mgr, reg);
134                 if ((*read & mask) == bit) {
135                         if (i > 100)
136                                 dev_dbg(&mgr->pci->dev,
137                                         "ATTENTION! check_reg(%x) loopcount=%d\n",
138                                             reg, i);
139                         return 0;
140                 }
141                 i++;
142         } while (time_after_eq(end_time, jiffies));
143         dev_err(&mgr->pci->dev,
144                    "pcxhr_check_reg_bit: timeout, reg=%x, mask=0x%x, val=%x\n",
145                    reg, mask, *read);
146         return -EIO;
147 }
148 
149 /* constants used with pcxhr_check_reg_bit() */
150 #define PCXHR_TIMEOUT_DSP               200
151 
152 
153 #define PCXHR_MASK_EXTRA_INFO           0x0000FE
154 #define PCXHR_MASK_IT_HF0               0x000100
155 #define PCXHR_MASK_IT_HF1               0x000200
156 #define PCXHR_MASK_IT_NO_HF0_HF1        0x000400
157 #define PCXHR_MASK_IT_MANAGE_HF5        0x000800
158 #define PCXHR_MASK_IT_WAIT              0x010000
159 #define PCXHR_MASK_IT_WAIT_EXTRA        0x020000
160 
161 #define PCXHR_IT_SEND_BYTE_XILINX       (0x0000003C | PCXHR_MASK_IT_HF0)
162 #define PCXHR_IT_TEST_XILINX            (0x0000003C | PCXHR_MASK_IT_HF1 | \
163                                          PCXHR_MASK_IT_MANAGE_HF5)
164 #define PCXHR_IT_DOWNLOAD_BOOT          (0x0000000C | PCXHR_MASK_IT_HF1 | \
165                                          PCXHR_MASK_IT_MANAGE_HF5 | \
166                                          PCXHR_MASK_IT_WAIT)
167 #define PCXHR_IT_RESET_BOARD_FUNC       (0x0000000C | PCXHR_MASK_IT_HF0 | \
168                                          PCXHR_MASK_IT_MANAGE_HF5 | \
169                                          PCXHR_MASK_IT_WAIT_EXTRA)
170 #define PCXHR_IT_DOWNLOAD_DSP           (0x0000000C | \
171                                          PCXHR_MASK_IT_MANAGE_HF5 | \
172                                          PCXHR_MASK_IT_WAIT)
173 #define PCXHR_IT_DEBUG                  (0x0000005A | PCXHR_MASK_IT_NO_HF0_HF1)
174 #define PCXHR_IT_RESET_SEMAPHORE        (0x0000005C | PCXHR_MASK_IT_NO_HF0_HF1)
175 #define PCXHR_IT_MESSAGE                (0x00000074 | PCXHR_MASK_IT_NO_HF0_HF1)
176 #define PCXHR_IT_RESET_CHK              (0x00000076 | PCXHR_MASK_IT_NO_HF0_HF1)
177 #define PCXHR_IT_UPDATE_RBUFFER         (0x00000078 | PCXHR_MASK_IT_NO_HF0_HF1)
178 
179 static int pcxhr_send_it_dsp(struct pcxhr_mgr *mgr,
180                              unsigned int itdsp, int atomic)
181 {
182         int err;
183         unsigned char reg;
184 
185         if (itdsp & PCXHR_MASK_IT_MANAGE_HF5) {
186                 /* clear hf5 bit */
187                 PCXHR_OUTPL(mgr, PCXHR_PLX_MBOX0,
188                             PCXHR_INPL(mgr, PCXHR_PLX_MBOX0) &
189                             ~PCXHR_MBOX0_HF5);
190         }
191         if ((itdsp & PCXHR_MASK_IT_NO_HF0_HF1) == 0) {
192                 reg = (PCXHR_ICR_HI08_RREQ |
193                        PCXHR_ICR_HI08_TREQ |
194                        PCXHR_ICR_HI08_HDRQ);
195                 if (itdsp & PCXHR_MASK_IT_HF0)
196                         reg |= PCXHR_ICR_HI08_HF0;
197                 if (itdsp & PCXHR_MASK_IT_HF1)
198                         reg |= PCXHR_ICR_HI08_HF1;
199                 PCXHR_OUTPB(mgr, PCXHR_DSP_ICR, reg);
200         }
201         reg = (unsigned char)(((itdsp & PCXHR_MASK_EXTRA_INFO) >> 1) |
202                               PCXHR_CVR_HI08_HC);
203         PCXHR_OUTPB(mgr, PCXHR_DSP_CVR, reg);
204         if (itdsp & PCXHR_MASK_IT_WAIT) {
205                 if (atomic)
206                         mdelay(PCXHR_WAIT_IT);
207                 else
208                         msleep(PCXHR_WAIT_IT);
209         }
210         if (itdsp & PCXHR_MASK_IT_WAIT_EXTRA) {
211                 if (atomic)
212                         mdelay(PCXHR_WAIT_IT_EXTRA);
213                 else
214                         msleep(PCXHR_WAIT_IT);
215         }
216         /* wait for CVR_HI08_HC == 0 */
217         err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_CVR,  PCXHR_CVR_HI08_HC, 0,
218                                   PCXHR_TIMEOUT_DSP, &reg);
219         if (err) {
220                 dev_err(&mgr->pci->dev, "pcxhr_send_it_dsp : TIMEOUT CVR\n");
221                 return err;
222         }
223         if (itdsp & PCXHR_MASK_IT_MANAGE_HF5) {
224                 /* wait for hf5 bit */
225                 err = pcxhr_check_reg_bit(mgr, PCXHR_PLX_MBOX0,
226                                           PCXHR_MBOX0_HF5,
227                                           PCXHR_MBOX0_HF5,
228                                           PCXHR_TIMEOUT_DSP,
229                                           &reg);
230                 if (err) {
231                         dev_err(&mgr->pci->dev,
232                                    "pcxhr_send_it_dsp : TIMEOUT HF5\n");
233                         return err;
234                 }
235         }
236         return 0; /* retry not handled here */
237 }
238 
239 void pcxhr_reset_xilinx_com(struct pcxhr_mgr *mgr)
240 {
241         /* reset second xilinx */
242         PCXHR_OUTPL(mgr, PCXHR_PLX_CHIPSC,
243                     PCXHR_CHIPSC_INIT_VALUE & ~PCXHR_CHIPSC_RESET_XILINX);
244 }
245 
246 static void pcxhr_enable_irq(struct pcxhr_mgr *mgr, int enable)
247 {
248         unsigned int reg = PCXHR_INPL(mgr, PCXHR_PLX_IRQCS);
249         /* enable/disable interrupts */
250         if (enable)
251                 reg |=  (PCXHR_IRQCS_ENABLE_PCIIRQ | PCXHR_IRQCS_ENABLE_PCIDB);
252         else
253                 reg &= ~(PCXHR_IRQCS_ENABLE_PCIIRQ | PCXHR_IRQCS_ENABLE_PCIDB);
254         PCXHR_OUTPL(mgr, PCXHR_PLX_IRQCS, reg);
255 }
256 
257 void pcxhr_reset_dsp(struct pcxhr_mgr *mgr)
258 {
259         /* disable interrupts */
260         pcxhr_enable_irq(mgr, 0);
261 
262         /* let's reset the DSP */
263         PCXHR_OUTPB(mgr, PCXHR_DSP_RESET, 0);
264         msleep( PCXHR_WAIT_DEFAULT ); /* wait 2 msec */
265         PCXHR_OUTPB(mgr, PCXHR_DSP_RESET, 3);
266         msleep( PCXHR_WAIT_DEFAULT ); /* wait 2 msec */
267 
268         /* reset mailbox */
269         PCXHR_OUTPL(mgr, PCXHR_PLX_MBOX0, 0);
270 }
271 
272 void pcxhr_enable_dsp(struct pcxhr_mgr *mgr)
273 {
274         /* enable interrupts */
275         pcxhr_enable_irq(mgr, 1);
276 }
277 
278 /*
279  * load the xilinx image
280  */
281 int pcxhr_load_xilinx_binary(struct pcxhr_mgr *mgr,
282                              const struct firmware *xilinx, int second)
283 {
284         unsigned int i;
285         unsigned int chipsc;
286         unsigned char data;
287         unsigned char mask;
288         const unsigned char *image;
289 
290         /* test first xilinx */
291         chipsc = PCXHR_INPL(mgr, PCXHR_PLX_CHIPSC);
292         /* REV01 cards do not support the PCXHR_CHIPSC_GPI_USERI bit anymore */
293         /* this bit will always be 1;
294          * no possibility to test presence of first xilinx
295          */
296         if(second) {
297                 if ((chipsc & PCXHR_CHIPSC_GPI_USERI) == 0) {
298                         dev_err(&mgr->pci->dev, "error loading first xilinx\n");
299                         return -EINVAL;
300                 }
301                 /* activate second xilinx */
302                 chipsc |= PCXHR_CHIPSC_RESET_XILINX;
303                 PCXHR_OUTPL(mgr, PCXHR_PLX_CHIPSC, chipsc);
304                 msleep( PCXHR_WAIT_DEFAULT ); /* wait 2 msec */
305         }
306         image = xilinx->data;
307         for (i = 0; i < xilinx->size; i++, image++) {
308                 data = *image;
309                 mask = 0x80;
310                 while (mask) {
311                         chipsc &= ~(PCXHR_CHIPSC_DATA_CLK |
312                                     PCXHR_CHIPSC_DATA_IN);
313                         if (data & mask)
314                                 chipsc |= PCXHR_CHIPSC_DATA_IN;
315                         PCXHR_OUTPL(mgr, PCXHR_PLX_CHIPSC, chipsc);
316                         chipsc |= PCXHR_CHIPSC_DATA_CLK;
317                         PCXHR_OUTPL(mgr, PCXHR_PLX_CHIPSC, chipsc);
318                         mask >>= 1;
319                 }
320                 /* don't take too much time in this loop... */
321                 cond_resched();
322         }
323         chipsc &= ~(PCXHR_CHIPSC_DATA_CLK | PCXHR_CHIPSC_DATA_IN);
324         PCXHR_OUTPL(mgr, PCXHR_PLX_CHIPSC, chipsc);
325         /* wait 2 msec (time to boot the xilinx before any access) */
326         msleep( PCXHR_WAIT_DEFAULT );
327         return 0;
328 }
329 
330 /*
331  * send an executable file to the DSP
332  */
333 static int pcxhr_download_dsp(struct pcxhr_mgr *mgr, const struct firmware *dsp)
334 {
335         int err;
336         unsigned int i;
337         unsigned int len;
338         const unsigned char *data;
339         unsigned char dummy;
340         /* check the length of boot image */
341         if (dsp->size <= 0)
342                 return -EINVAL;
343         if (dsp->size % 3)
344                 return -EINVAL;
345         if (snd_BUG_ON(!dsp->data))
346                 return -EINVAL;
347         /* transfert data buffer from PC to DSP */
348         for (i = 0; i < dsp->size; i += 3) {
349                 data = dsp->data + i;
350                 if (i == 0) {
351                         /* test data header consistency */
352                         len = (unsigned int)((data[0]<<16) +
353                                              (data[1]<<8) +
354                                              data[2]);
355                         if (len && (dsp->size != (len + 2) * 3))
356                                 return -EINVAL;
357                 }
358                 /* wait DSP ready for new transfer */
359                 err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR,
360                                           PCXHR_ISR_HI08_TRDY,
361                                           PCXHR_ISR_HI08_TRDY,
362                                           PCXHR_TIMEOUT_DSP, &dummy);
363                 if (err) {
364                         dev_err(&mgr->pci->dev,
365                                    "dsp loading error at position %d\n", i);
366                         return err;
367                 }
368                 /* send host data */
369                 PCXHR_OUTPB(mgr, PCXHR_DSP_TXH, data[0]);
370                 PCXHR_OUTPB(mgr, PCXHR_DSP_TXM, data[1]);
371                 PCXHR_OUTPB(mgr, PCXHR_DSP_TXL, data[2]);
372 
373                 /* don't take too much time in this loop... */
374                 cond_resched();
375         }
376         /* give some time to boot the DSP */
377         msleep(PCXHR_WAIT_DEFAULT);
378         return 0;
379 }
380 
381 /*
382  * load the eeprom image
383  */
384 int pcxhr_load_eeprom_binary(struct pcxhr_mgr *mgr,
385                              const struct firmware *eeprom)
386 {
387         int err;
388         unsigned char reg;
389 
390         /* init value of the ICR register */
391         reg = PCXHR_ICR_HI08_RREQ | PCXHR_ICR_HI08_TREQ | PCXHR_ICR_HI08_HDRQ;
392         if (PCXHR_INPL(mgr, PCXHR_PLX_MBOX0) & PCXHR_MBOX0_BOOT_HERE) {
393                 /* no need to load the eeprom binary,
394                  * but init the HI08 interface
395                  */
396                 PCXHR_OUTPB(mgr, PCXHR_DSP_ICR, reg | PCXHR_ICR_HI08_INIT);
397                 msleep(PCXHR_WAIT_DEFAULT);
398                 PCXHR_OUTPB(mgr, PCXHR_DSP_ICR, reg);
399                 msleep(PCXHR_WAIT_DEFAULT);
400                 dev_dbg(&mgr->pci->dev, "no need to load eeprom boot\n");
401                 return 0;
402         }
403         PCXHR_OUTPB(mgr, PCXHR_DSP_ICR, reg);
404 
405         err = pcxhr_download_dsp(mgr, eeprom);
406         if (err)
407                 return err;
408         /* wait for chk bit */
409         return pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR, PCXHR_ISR_HI08_CHK,
410                                    PCXHR_ISR_HI08_CHK, PCXHR_TIMEOUT_DSP, &reg);
411 }
412 
413 /*
414  * load the boot image
415  */
416 int pcxhr_load_boot_binary(struct pcxhr_mgr *mgr, const struct firmware *boot)
417 {
418         int err;
419         unsigned int physaddr = mgr->hostport.addr;
420         unsigned char dummy;
421 
422         /* send the hostport address to the DSP (only the upper 24 bit !) */
423         if (snd_BUG_ON(physaddr & 0xff))
424                 return -EINVAL;
425         PCXHR_OUTPL(mgr, PCXHR_PLX_MBOX1, (physaddr >> 8));
426 
427         err = pcxhr_send_it_dsp(mgr, PCXHR_IT_DOWNLOAD_BOOT, 0);
428         if (err)
429                 return err;
430         /* clear hf5 bit */
431         PCXHR_OUTPL(mgr, PCXHR_PLX_MBOX0,
432                     PCXHR_INPL(mgr, PCXHR_PLX_MBOX0) & ~PCXHR_MBOX0_HF5);
433 
434         err = pcxhr_download_dsp(mgr, boot);
435         if (err)
436                 return err;
437         /* wait for hf5 bit */
438         return pcxhr_check_reg_bit(mgr, PCXHR_PLX_MBOX0, PCXHR_MBOX0_HF5,
439                                    PCXHR_MBOX0_HF5, PCXHR_TIMEOUT_DSP, &dummy);
440 }
441 
442 /*
443  * load the final dsp image
444  */
445 int pcxhr_load_dsp_binary(struct pcxhr_mgr *mgr, const struct firmware *dsp)
446 {
447         int err;
448         unsigned char dummy;
449         err = pcxhr_send_it_dsp(mgr, PCXHR_IT_RESET_BOARD_FUNC, 0);
450         if (err)
451                 return err;
452         err = pcxhr_send_it_dsp(mgr, PCXHR_IT_DOWNLOAD_DSP, 0);
453         if (err)
454                 return err;
455         err = pcxhr_download_dsp(mgr, dsp);
456         if (err)
457                 return err;
458         /* wait for chk bit */
459         return pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR,
460                                    PCXHR_ISR_HI08_CHK,
461                                    PCXHR_ISR_HI08_CHK,
462                                    PCXHR_TIMEOUT_DSP, &dummy);
463 }
464 
465 
466 struct pcxhr_cmd_info {
467         u32 opcode;             /* command word */
468         u16 st_length;          /* status length */
469         u16 st_type;            /* status type (RMH_SSIZE_XXX) */
470 };
471 
472 /* RMH status type */
473 enum {
474         RMH_SSIZE_FIXED = 0,    /* status size fix (st_length = 0..x) */
475         RMH_SSIZE_ARG = 1,      /* status size given in the LSB byte */
476         RMH_SSIZE_MASK = 2,     /* status size given in bitmask */
477 };
478 
479 /*
480  * Array of DSP commands
481  */
482 static struct pcxhr_cmd_info pcxhr_dsp_cmds[] = {
483 [CMD_VERSION] =                         { 0x010000, 1, RMH_SSIZE_FIXED },
484 [CMD_SUPPORTED] =                       { 0x020000, 4, RMH_SSIZE_FIXED },
485 [CMD_TEST_IT] =                         { 0x040000, 1, RMH_SSIZE_FIXED },
486 [CMD_SEND_IRQA] =                       { 0x070001, 0, RMH_SSIZE_FIXED },
487 [CMD_ACCESS_IO_WRITE] =                 { 0x090000, 1, RMH_SSIZE_ARG },
488 [CMD_ACCESS_IO_READ] =                  { 0x094000, 1, RMH_SSIZE_ARG },
489 [CMD_ASYNC] =                           { 0x0a0000, 1, RMH_SSIZE_ARG },
490 [CMD_MODIFY_CLOCK] =                    { 0x0d0000, 0, RMH_SSIZE_FIXED },
491 [CMD_RESYNC_AUDIO_INPUTS] =             { 0x0e0000, 0, RMH_SSIZE_FIXED },
492 [CMD_GET_DSP_RESOURCES] =               { 0x100000, 4, RMH_SSIZE_FIXED },
493 [CMD_SET_TIMER_INTERRUPT] =             { 0x110000, 0, RMH_SSIZE_FIXED },
494 [CMD_RES_PIPE] =                        { 0x400000, 0, RMH_SSIZE_FIXED },
495 [CMD_FREE_PIPE] =                       { 0x410000, 0, RMH_SSIZE_FIXED },
496 [CMD_CONF_PIPE] =                       { 0x422101, 0, RMH_SSIZE_FIXED },
497 [CMD_STOP_PIPE] =                       { 0x470004, 0, RMH_SSIZE_FIXED },
498 [CMD_PIPE_SAMPLE_COUNT] =               { 0x49a000, 2, RMH_SSIZE_FIXED },
499 [CMD_CAN_START_PIPE] =                  { 0x4b0000, 1, RMH_SSIZE_FIXED },
500 [CMD_START_STREAM] =                    { 0x802000, 0, RMH_SSIZE_FIXED },
501 [CMD_STREAM_OUT_LEVEL_ADJUST] =         { 0x822000, 0, RMH_SSIZE_FIXED },
502 [CMD_STOP_STREAM] =                     { 0x832000, 0, RMH_SSIZE_FIXED },
503 [CMD_UPDATE_R_BUFFERS] =                { 0x840000, 0, RMH_SSIZE_FIXED },
504 [CMD_FORMAT_STREAM_OUT] =               { 0x860000, 0, RMH_SSIZE_FIXED },
505 [CMD_FORMAT_STREAM_IN] =                { 0x870000, 0, RMH_SSIZE_FIXED },
506 [CMD_STREAM_SAMPLE_COUNT] =             { 0x902000, 2, RMH_SSIZE_FIXED },
507 [CMD_AUDIO_LEVEL_ADJUST] =              { 0xc22000, 0, RMH_SSIZE_FIXED },
508 [CMD_GET_TIME_CODE] =                   { 0x060000, 5, RMH_SSIZE_FIXED },
509 [CMD_MANAGE_SIGNAL] =                   { 0x0f0000, 0, RMH_SSIZE_FIXED },
510 };
511 
512 #ifdef CONFIG_SND_DEBUG_VERBOSE
513 static char* cmd_names[] = {
514 [CMD_VERSION] =                         "CMD_VERSION",
515 [CMD_SUPPORTED] =                       "CMD_SUPPORTED",
516 [CMD_TEST_IT] =                         "CMD_TEST_IT",
517 [CMD_SEND_IRQA] =                       "CMD_SEND_IRQA",
518 [CMD_ACCESS_IO_WRITE] =                 "CMD_ACCESS_IO_WRITE",
519 [CMD_ACCESS_IO_READ] =                  "CMD_ACCESS_IO_READ",
520 [CMD_ASYNC] =                           "CMD_ASYNC",
521 [CMD_MODIFY_CLOCK] =                    "CMD_MODIFY_CLOCK",
522 [CMD_RESYNC_AUDIO_INPUTS] =             "CMD_RESYNC_AUDIO_INPUTS",
523 [CMD_GET_DSP_RESOURCES] =               "CMD_GET_DSP_RESOURCES",
524 [CMD_SET_TIMER_INTERRUPT] =             "CMD_SET_TIMER_INTERRUPT",
525 [CMD_RES_PIPE] =                        "CMD_RES_PIPE",
526 [CMD_FREE_PIPE] =                       "CMD_FREE_PIPE",
527 [CMD_CONF_PIPE] =                       "CMD_CONF_PIPE",
528 [CMD_STOP_PIPE] =                       "CMD_STOP_PIPE",
529 [CMD_PIPE_SAMPLE_COUNT] =               "CMD_PIPE_SAMPLE_COUNT",
530 [CMD_CAN_START_PIPE] =                  "CMD_CAN_START_PIPE",
531 [CMD_START_STREAM] =                    "CMD_START_STREAM",
532 [CMD_STREAM_OUT_LEVEL_ADJUST] =         "CMD_STREAM_OUT_LEVEL_ADJUST",
533 [CMD_STOP_STREAM] =                     "CMD_STOP_STREAM",
534 [CMD_UPDATE_R_BUFFERS] =                "CMD_UPDATE_R_BUFFERS",
535 [CMD_FORMAT_STREAM_OUT] =               "CMD_FORMAT_STREAM_OUT",
536 [CMD_FORMAT_STREAM_IN] =                "CMD_FORMAT_STREAM_IN",
537 [CMD_STREAM_SAMPLE_COUNT] =             "CMD_STREAM_SAMPLE_COUNT",
538 [CMD_AUDIO_LEVEL_ADJUST] =              "CMD_AUDIO_LEVEL_ADJUST",
539 [CMD_GET_TIME_CODE] =                   "CMD_GET_TIME_CODE",
540 [CMD_MANAGE_SIGNAL] =                   "CMD_MANAGE_SIGNAL",
541 };
542 #endif
543 
544 
545 static int pcxhr_read_rmh_status(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
546 {
547         int err;
548         int i;
549         u32 data;
550         u32 size_mask;
551         unsigned char reg;
552         int max_stat_len;
553 
554         if (rmh->stat_len < PCXHR_SIZE_MAX_STATUS)
555                 max_stat_len = PCXHR_SIZE_MAX_STATUS;
556         else    max_stat_len = rmh->stat_len;
557 
558         for (i = 0; i < rmh->stat_len; i++) {
559                 /* wait for receiver full */
560                 err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR,
561                                           PCXHR_ISR_HI08_RXDF,
562                                           PCXHR_ISR_HI08_RXDF,
563                                           PCXHR_TIMEOUT_DSP, &reg);
564                 if (err) {
565                         dev_err(&mgr->pci->dev,
566                                 "ERROR RMH stat: ISR:RXDF=1 (ISR = %x; i=%d )\n",
567                                 reg, i);
568                         return err;
569                 }
570                 /* read data */
571                 data  = PCXHR_INPB(mgr, PCXHR_DSP_TXH) << 16;
572                 data |= PCXHR_INPB(mgr, PCXHR_DSP_TXM) << 8;
573                 data |= PCXHR_INPB(mgr, PCXHR_DSP_TXL);
574 
575                 /* need to update rmh->stat_len on the fly ?? */
576                 if (!i) {
577                         if (rmh->dsp_stat != RMH_SSIZE_FIXED) {
578                                 if (rmh->dsp_stat == RMH_SSIZE_ARG) {
579                                         rmh->stat_len = (data & 0x0000ff) + 1;
580                                         data &= 0xffff00;
581                                 } else {
582                                         /* rmh->dsp_stat == RMH_SSIZE_MASK */
583                                         rmh->stat_len = 1;
584                                         size_mask = data;
585                                         while (size_mask) {
586                                                 if (size_mask & 1)
587                                                         rmh->stat_len++;
588                                                 size_mask >>= 1;
589                                         }
590                                 }
591                         }
592                 }
593 #ifdef CONFIG_SND_DEBUG_VERBOSE
594                 if (rmh->cmd_idx < CMD_LAST_INDEX)
595                         dev_dbg(&mgr->pci->dev, "    stat[%d]=%x\n", i, data);
596 #endif
597                 if (i < max_stat_len)
598                         rmh->stat[i] = data;
599         }
600         if (rmh->stat_len > max_stat_len) {
601                 dev_dbg(&mgr->pci->dev, "PCXHR : rmh->stat_len=%x too big\n",
602                             rmh->stat_len);
603                 rmh->stat_len = max_stat_len;
604         }
605         return 0;
606 }
607 
608 static int pcxhr_send_msg_nolock(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
609 {
610         int err;
611         int i;
612         u32 data;
613         unsigned char reg;
614 
615         if (snd_BUG_ON(rmh->cmd_len >= PCXHR_SIZE_MAX_CMD))
616                 return -EINVAL;
617         err = pcxhr_send_it_dsp(mgr, PCXHR_IT_MESSAGE, 1);
618         if (err) {
619                 dev_err(&mgr->pci->dev,
620                         "pcxhr_send_message : ED_DSP_CRASHED\n");
621                 return err;
622         }
623         /* wait for chk bit */
624         err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR, PCXHR_ISR_HI08_CHK,
625                                   PCXHR_ISR_HI08_CHK, PCXHR_TIMEOUT_DSP, &reg);
626         if (err)
627                 return err;
628         /* reset irq chk */
629         err = pcxhr_send_it_dsp(mgr, PCXHR_IT_RESET_CHK, 1);
630         if (err)
631                 return err;
632         /* wait for chk bit == 0*/
633         err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR, PCXHR_ISR_HI08_CHK, 0,
634                                   PCXHR_TIMEOUT_DSP, &reg);
635         if (err)
636                 return err;
637 
638         data = rmh->cmd[0];
639 
640         if (rmh->cmd_len > 1)
641                 data |= 0x008000;       /* MASK_MORE_THAN_1_WORD_COMMAND */
642         else
643                 data &= 0xff7fff;       /* MASK_1_WORD_COMMAND */
644 #ifdef CONFIG_SND_DEBUG_VERBOSE
645         if (rmh->cmd_idx < CMD_LAST_INDEX)
646                 dev_dbg(&mgr->pci->dev, "MSG cmd[0]=%x (%s)\n",
647                             data, cmd_names[rmh->cmd_idx]);
648 #endif
649 
650         err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR, PCXHR_ISR_HI08_TRDY,
651                                   PCXHR_ISR_HI08_TRDY, PCXHR_TIMEOUT_DSP, &reg);
652         if (err)
653                 return err;
654         PCXHR_OUTPB(mgr, PCXHR_DSP_TXH, (data>>16)&0xFF);
655         PCXHR_OUTPB(mgr, PCXHR_DSP_TXM, (data>>8)&0xFF);
656         PCXHR_OUTPB(mgr, PCXHR_DSP_TXL, (data&0xFF));
657 
658         if (rmh->cmd_len > 1) {
659                 /* send length */
660                 data = rmh->cmd_len - 1;
661                 err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR,
662                                           PCXHR_ISR_HI08_TRDY,
663                                           PCXHR_ISR_HI08_TRDY,
664                                           PCXHR_TIMEOUT_DSP, &reg);
665                 if (err)
666                         return err;
667                 PCXHR_OUTPB(mgr, PCXHR_DSP_TXH, (data>>16)&0xFF);
668                 PCXHR_OUTPB(mgr, PCXHR_DSP_TXM, (data>>8)&0xFF);
669                 PCXHR_OUTPB(mgr, PCXHR_DSP_TXL, (data&0xFF));
670 
671                 for (i=1; i < rmh->cmd_len; i++) {
672                         /* send other words */
673                         data = rmh->cmd[i];
674 #ifdef CONFIG_SND_DEBUG_VERBOSE
675                         if (rmh->cmd_idx < CMD_LAST_INDEX)
676                                 dev_dbg(&mgr->pci->dev,
677                                         "    cmd[%d]=%x\n", i, data);
678 #endif
679                         err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR,
680                                                   PCXHR_ISR_HI08_TRDY,
681                                                   PCXHR_ISR_HI08_TRDY,
682                                                   PCXHR_TIMEOUT_DSP, &reg);
683                         if (err)
684                                 return err;
685                         PCXHR_OUTPB(mgr, PCXHR_DSP_TXH, (data>>16)&0xFF);
686                         PCXHR_OUTPB(mgr, PCXHR_DSP_TXM, (data>>8)&0xFF);
687                         PCXHR_OUTPB(mgr, PCXHR_DSP_TXL, (data&0xFF));
688                 }
689         }
690         /* wait for chk bit */
691         err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR, PCXHR_ISR_HI08_CHK,
692                                   PCXHR_ISR_HI08_CHK, PCXHR_TIMEOUT_DSP, &reg);
693         if (err)
694                 return err;
695         /* test status ISR */
696         if (reg & PCXHR_ISR_HI08_ERR) {
697                 /* ERROR, wait for receiver full */
698                 err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR,
699                                           PCXHR_ISR_HI08_RXDF,
700                                           PCXHR_ISR_HI08_RXDF,
701                                           PCXHR_TIMEOUT_DSP, &reg);
702                 if (err) {
703                         dev_err(&mgr->pci->dev,
704                                 "ERROR RMH: ISR:RXDF=1 (ISR = %x)\n", reg);
705                         return err;
706                 }
707                 /* read error code */
708                 data  = PCXHR_INPB(mgr, PCXHR_DSP_TXH) << 16;
709                 data |= PCXHR_INPB(mgr, PCXHR_DSP_TXM) << 8;
710                 data |= PCXHR_INPB(mgr, PCXHR_DSP_TXL);
711                 dev_err(&mgr->pci->dev, "ERROR RMH(%d): 0x%x\n",
712                            rmh->cmd_idx, data);
713                 err = -EINVAL;
714         } else {
715                 /* read the response data */
716                 err = pcxhr_read_rmh_status(mgr, rmh);
717         }
718         /* reset semaphore */
719         if (pcxhr_send_it_dsp(mgr, PCXHR_IT_RESET_SEMAPHORE, 1) < 0)
720                 return -EIO;
721         return err;
722 }
723 
724 
725 /**
726  * pcxhr_init_rmh - initialize the RMH instance
727  * @rmh: the rmh pointer to be initialized
728  * @cmd: the rmh command to be set
729  */
730 void pcxhr_init_rmh(struct pcxhr_rmh *rmh, int cmd)
731 {
732         if (snd_BUG_ON(cmd >= CMD_LAST_INDEX))
733                 return;
734         rmh->cmd[0] = pcxhr_dsp_cmds[cmd].opcode;
735         rmh->cmd_len = 1;
736         rmh->stat_len = pcxhr_dsp_cmds[cmd].st_length;
737         rmh->dsp_stat = pcxhr_dsp_cmds[cmd].st_type;
738         rmh->cmd_idx = cmd;
739 }
740 
741 
742 void pcxhr_set_pipe_cmd_params(struct pcxhr_rmh *rmh, int capture,
743                                unsigned int param1, unsigned int param2,
744                                unsigned int param3)
745 {
746         snd_BUG_ON(param1 > MASK_FIRST_FIELD);
747         if (capture)
748                 rmh->cmd[0] |= 0x800;           /* COMMAND_RECORD_MASK */
749         if (param1)
750                 rmh->cmd[0] |= (param1 << FIELD_SIZE);
751         if (param2) {
752                 snd_BUG_ON(param2 > MASK_FIRST_FIELD);
753                 rmh->cmd[0] |= param2;
754         }
755         if(param3) {
756                 snd_BUG_ON(param3 > MASK_DSP_WORD);
757                 rmh->cmd[1] = param3;
758                 rmh->cmd_len = 2;
759         }
760 }
761 
762 /*
763  * pcxhr_send_msg - send a DSP message with spinlock
764  * @rmh: the rmh record to send and receive
765  *
766  * returns 0 if successful, or a negative error code.
767  */
768 int pcxhr_send_msg(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
769 {
770         int err;
771 
772         mutex_lock(&mgr->msg_lock);
773         err = pcxhr_send_msg_nolock(mgr, rmh);
774         mutex_unlock(&mgr->msg_lock);
775         return err;
776 }
777 
778 static inline int pcxhr_pipes_running(struct pcxhr_mgr *mgr)
779 {
780         int start_mask = PCXHR_INPL(mgr, PCXHR_PLX_MBOX2);
781         /* least segnificant 12 bits are the pipe states
782          * for the playback audios
783          * next 12 bits are the pipe states for the capture audios
784          * (PCXHR_PIPE_STATE_CAPTURE_OFFSET)
785          */
786         start_mask &= 0xffffff;
787         dev_dbg(&mgr->pci->dev, "CMD_PIPE_STATE MBOX2=0x%06x\n", start_mask);
788         return start_mask;
789 }
790 
791 #define PCXHR_PIPE_STATE_CAPTURE_OFFSET         12
792 #define MAX_WAIT_FOR_DSP                        20
793 
794 static int pcxhr_prepair_pipe_start(struct pcxhr_mgr *mgr,
795                                     int audio_mask, int *retry)
796 {
797         struct pcxhr_rmh rmh;
798         int err;
799         int audio = 0;
800 
801         *retry = 0;
802         while (audio_mask) {
803                 if (audio_mask & 1) {
804                         pcxhr_init_rmh(&rmh, CMD_CAN_START_PIPE);
805                         if (audio < PCXHR_PIPE_STATE_CAPTURE_OFFSET) {
806                                 /* can start playback pipe */
807                                 pcxhr_set_pipe_cmd_params(&rmh, 0, audio, 0, 0);
808                         } else {
809                                 /* can start capture pipe */
810                                 pcxhr_set_pipe_cmd_params(&rmh, 1, audio -
811                                                 PCXHR_PIPE_STATE_CAPTURE_OFFSET,
812                                                 0, 0);
813                         }
814                         err = pcxhr_send_msg(mgr, &rmh);
815                         if (err) {
816                                 dev_err(&mgr->pci->dev,
817                                            "error pipe start "
818                                            "(CMD_CAN_START_PIPE) err=%x!\n",
819                                            err);
820                                 return err;
821                         }
822                         /* if the pipe couldn't be prepaired for start,
823                          * retry it later
824                          */
825                         if (rmh.stat[0] == 0)
826                                 *retry |= (1<<audio);
827                 }
828                 audio_mask>>=1;
829                 audio++;
830         }
831         return 0;
832 }
833 
834 static int pcxhr_stop_pipes(struct pcxhr_mgr *mgr, int audio_mask)
835 {
836         struct pcxhr_rmh rmh;
837         int err;
838         int audio = 0;
839 
840         while (audio_mask) {
841                 if (audio_mask & 1) {
842                         pcxhr_init_rmh(&rmh, CMD_STOP_PIPE);
843                         if (audio < PCXHR_PIPE_STATE_CAPTURE_OFFSET) {
844                                 /* stop playback pipe */
845                                 pcxhr_set_pipe_cmd_params(&rmh, 0, audio, 0, 0);
846                         } else {
847                                 /* stop capture pipe */
848                                 pcxhr_set_pipe_cmd_params(&rmh, 1, audio -
849                                                 PCXHR_PIPE_STATE_CAPTURE_OFFSET,
850                                                 0, 0);
851                         }
852                         err = pcxhr_send_msg(mgr, &rmh);
853                         if (err) {
854                                 dev_err(&mgr->pci->dev,
855                                            "error pipe stop "
856                                            "(CMD_STOP_PIPE) err=%x!\n", err);
857                                 return err;
858                         }
859                 }
860                 audio_mask>>=1;
861                 audio++;
862         }
863         return 0;
864 }
865 
866 static int pcxhr_toggle_pipes(struct pcxhr_mgr *mgr, int audio_mask)
867 {
868         struct pcxhr_rmh rmh;
869         int err;
870         int audio = 0;
871 
872         while (audio_mask) {
873                 if (audio_mask & 1) {
874                         pcxhr_init_rmh(&rmh, CMD_CONF_PIPE);
875                         if (audio < PCXHR_PIPE_STATE_CAPTURE_OFFSET)
876                                 pcxhr_set_pipe_cmd_params(&rmh, 0, 0, 0,
877                                                           1 << audio);
878                         else
879                                 pcxhr_set_pipe_cmd_params(&rmh, 1, 0, 0,
880                                                           1 << (audio - PCXHR_PIPE_STATE_CAPTURE_OFFSET));
881                         err = pcxhr_send_msg(mgr, &rmh);
882                         if (err) {
883                                 dev_err(&mgr->pci->dev,
884                                            "error pipe start "
885                                            "(CMD_CONF_PIPE) err=%x!\n", err);
886                                 return err;
887                         }
888                 }
889                 audio_mask>>=1;
890                 audio++;
891         }
892         /* now fire the interrupt on the card */
893         pcxhr_init_rmh(&rmh, CMD_SEND_IRQA);
894         err = pcxhr_send_msg(mgr, &rmh);
895         if (err) {
896                 dev_err(&mgr->pci->dev,
897                            "error pipe start (CMD_SEND_IRQA) err=%x!\n",
898                            err);
899                 return err;
900         }
901         return 0;
902 }
903 
904 
905 
906 int pcxhr_set_pipe_state(struct pcxhr_mgr *mgr, int playback_mask,
907                          int capture_mask, int start)
908 {
909         int state, i, err;
910         int audio_mask;
911 
912 #ifdef CONFIG_SND_DEBUG_VERBOSE
913         ktime_t start_time, stop_time, diff_time;
914 
915         start_time = ktime_get();
916 #endif
917         audio_mask = (playback_mask |
918                       (capture_mask << PCXHR_PIPE_STATE_CAPTURE_OFFSET));
919         /* current pipe state (playback + record) */
920         state = pcxhr_pipes_running(mgr);
921         dev_dbg(&mgr->pci->dev,
922                 "pcxhr_set_pipe_state %s (mask %x current %x)\n",
923                     start ? "START" : "STOP", audio_mask, state);
924         if (start) {
925                 /* start only pipes that are not yet started */
926                 audio_mask &= ~state;
927                 state = audio_mask;
928                 for (i = 0; i < MAX_WAIT_FOR_DSP; i++) {
929                         err = pcxhr_prepair_pipe_start(mgr, state, &state);
930                         if (err)
931                                 return err;
932                         if (state == 0)
933                                 break;  /* success, all pipes prepaired */
934                         mdelay(1);      /* wait 1 millisecond and retry */
935                 }
936         } else {
937                 audio_mask &= state;    /* stop only pipes that are started */
938         }
939         if (audio_mask == 0)
940                 return 0;
941 
942         err = pcxhr_toggle_pipes(mgr, audio_mask);
943         if (err)
944                 return err;
945 
946         i = 0;
947         while (1) {
948                 state = pcxhr_pipes_running(mgr);
949                 /* have all pipes the new state ? */
950                 if ((state & audio_mask) == (start ? audio_mask : 0))
951                         break;
952                 if (++i >= MAX_WAIT_FOR_DSP * 100) {
953                         dev_err(&mgr->pci->dev, "error pipe start/stop\n");
954                         return -EBUSY;
955                 }
956                 udelay(10);                     /* wait 10 microseconds */
957         }
958         if (!start) {
959                 err = pcxhr_stop_pipes(mgr, audio_mask);
960                 if (err)
961                         return err;
962         }
963 #ifdef CONFIG_SND_DEBUG_VERBOSE
964         stop_time = ktime_get();
965         diff_time = ktime_sub(stop_time, start_time);
966         dev_dbg(&mgr->pci->dev, "***SET PIPE STATE*** TIME = %ld (err = %x)\n",
967                         (long)(ktime_to_ns(diff_time)), err);
968 #endif
969         return 0;
970 }
971 
972 int pcxhr_write_io_num_reg_cont(struct pcxhr_mgr *mgr, unsigned int mask,
973                                 unsigned int value, int *changed)
974 {
975         struct pcxhr_rmh rmh;
976         int err;
977 
978         mutex_lock(&mgr->msg_lock);
979         if ((mgr->io_num_reg_cont & mask) == value) {
980                 dev_dbg(&mgr->pci->dev,
981                         "IO_NUM_REG_CONT mask %x already is set to %x\n",
982                             mask, value);
983                 if (changed)
984                         *changed = 0;
985                 mutex_unlock(&mgr->msg_lock);
986                 return 0;       /* already programmed */
987         }
988         pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE);
989         rmh.cmd[0] |= IO_NUM_REG_CONT;
990         rmh.cmd[1]  = mask;
991         rmh.cmd[2]  = value;
992         rmh.cmd_len = 3;
993         err = pcxhr_send_msg_nolock(mgr, &rmh);
994         if (err == 0) {
995                 mgr->io_num_reg_cont &= ~mask;
996                 mgr->io_num_reg_cont |= value;
997                 if (changed)
998                         *changed = 1;
999         }
1000         mutex_unlock(&mgr->msg_lock);
1001         return err;
1002 }
1003 
1004 #define PCXHR_IRQ_TIMER         0x000300
1005 #define PCXHR_IRQ_FREQ_CHANGE   0x000800
1006 #define PCXHR_IRQ_TIME_CODE     0x001000
1007 #define PCXHR_IRQ_NOTIFY        0x002000
1008 #define PCXHR_IRQ_ASYNC         0x008000
1009 #define PCXHR_IRQ_MASK          0x00bb00
1010 #define PCXHR_FATAL_DSP_ERR     0xff0000
1011 
1012 enum pcxhr_async_err_src {
1013         PCXHR_ERR_PIPE,
1014         PCXHR_ERR_STREAM,
1015         PCXHR_ERR_AUDIO
1016 };
1017 
1018 static int pcxhr_handle_async_err(struct pcxhr_mgr *mgr, u32 err,
1019                                   enum pcxhr_async_err_src err_src, int pipe,
1020                                   int is_capture)
1021 {
1022         static char* err_src_name[] = {
1023                 [PCXHR_ERR_PIPE]        = "Pipe",
1024                 [PCXHR_ERR_STREAM]      = "Stream",
1025                 [PCXHR_ERR_AUDIO]       = "Audio"
1026         };
1027 
1028         if (err & 0xfff)
1029                 err &= 0xfff;
1030         else
1031                 err = ((err >> 12) & 0xfff);
1032         if (!err)
1033                 return 0;
1034         dev_dbg(&mgr->pci->dev, "CMD_ASYNC : Error %s %s Pipe %d err=%x\n",
1035                     err_src_name[err_src],
1036                     is_capture ? "Record" : "Play", pipe, err);
1037         if (err == 0xe01)
1038                 mgr->async_err_stream_xrun++;
1039         else if (err == 0xe10)
1040                 mgr->async_err_pipe_xrun++;
1041         else
1042                 mgr->async_err_other_last = (int)err;
1043         return 1;
1044 }
1045 
1046 
1047 static void pcxhr_msg_thread(struct pcxhr_mgr *mgr)
1048 {
1049         struct pcxhr_rmh *prmh = mgr->prmh;
1050         int err;
1051         int i, j;
1052 
1053         if (mgr->src_it_dsp & PCXHR_IRQ_FREQ_CHANGE)
1054                 dev_dbg(&mgr->pci->dev,
1055                         "PCXHR_IRQ_FREQ_CHANGE event occurred\n");
1056         if (mgr->src_it_dsp & PCXHR_IRQ_TIME_CODE)
1057                 dev_dbg(&mgr->pci->dev,
1058                         "PCXHR_IRQ_TIME_CODE event occurred\n");
1059         if (mgr->src_it_dsp & PCXHR_IRQ_NOTIFY)
1060                 dev_dbg(&mgr->pci->dev,
1061                         "PCXHR_IRQ_NOTIFY event occurred\n");
1062         if (mgr->src_it_dsp & (PCXHR_IRQ_FREQ_CHANGE | PCXHR_IRQ_TIME_CODE)) {
1063                 /* clear events FREQ_CHANGE and TIME_CODE */
1064                 pcxhr_init_rmh(prmh, CMD_TEST_IT);
1065                 err = pcxhr_send_msg(mgr, prmh);
1066                 dev_dbg(&mgr->pci->dev, "CMD_TEST_IT : err=%x, stat=%x\n",
1067                             err, prmh->stat[0]);
1068         }
1069         if (mgr->src_it_dsp & PCXHR_IRQ_ASYNC) {
1070                 dev_dbg(&mgr->pci->dev,
1071                         "PCXHR_IRQ_ASYNC event occurred\n");
1072 
1073                 pcxhr_init_rmh(prmh, CMD_ASYNC);
1074                 prmh->cmd[0] |= 1;      /* add SEL_ASYNC_EVENTS */
1075                 /* this is the only one extra long response command */
1076                 prmh->stat_len = PCXHR_SIZE_MAX_LONG_STATUS;
1077                 err = pcxhr_send_msg(mgr, prmh);
1078                 if (err)
1079                         dev_err(&mgr->pci->dev, "ERROR pcxhr_msg_thread=%x;\n",
1080                                    err);
1081                 i = 1;
1082                 while (i < prmh->stat_len) {
1083                         int nb_audio = ((prmh->stat[i] >> FIELD_SIZE) &
1084                                         MASK_FIRST_FIELD);
1085                         int nb_stream = ((prmh->stat[i] >> (2*FIELD_SIZE)) &
1086                                          MASK_FIRST_FIELD);
1087                         int pipe = prmh->stat[i] & MASK_FIRST_FIELD;
1088                         int is_capture = prmh->stat[i] & 0x400000;
1089                         u32 err2;
1090 
1091                         if (prmh->stat[i] & 0x800000) { /* if BIT_END */
1092                                 dev_dbg(&mgr->pci->dev,
1093                                         "TASKLET : End%sPipe %d\n",
1094                                             is_capture ? "Record" : "Play",
1095                                             pipe);
1096                         }
1097                         i++;
1098                         err2 = prmh->stat[i] ? prmh->stat[i] : prmh->stat[i+1];
1099                         if (err2)
1100                                 pcxhr_handle_async_err(mgr, err2,
1101                                                        PCXHR_ERR_PIPE,
1102                                                        pipe, is_capture);
1103                         i += 2;
1104                         for (j = 0; j < nb_stream; j++) {
1105                                 err2 = prmh->stat[i] ?
1106                                         prmh->stat[i] : prmh->stat[i+1];
1107                                 if (err2)
1108                                         pcxhr_handle_async_err(mgr, err2,
1109                                                                PCXHR_ERR_STREAM,
1110                                                                pipe,
1111                                                                is_capture);
1112                                 i += 2;
1113                         }
1114                         for (j = 0; j < nb_audio; j++) {
1115                                 err2 = prmh->stat[i] ?
1116                                         prmh->stat[i] : prmh->stat[i+1];
1117                                 if (err2)
1118                                         pcxhr_handle_async_err(mgr, err2,
1119                                                                PCXHR_ERR_AUDIO,
1120                                                                pipe,
1121                                                                is_capture);
1122                                 i += 2;
1123                         }
1124                 }
1125         }
1126 }
1127 
1128 static u_int64_t pcxhr_stream_read_position(struct pcxhr_mgr *mgr,
1129                                             struct pcxhr_stream *stream)
1130 {
1131         u_int64_t hw_sample_count;
1132         struct pcxhr_rmh rmh;
1133         int err, stream_mask;
1134 
1135         stream_mask = stream->pipe->is_capture ? 1 : 1<<stream->substream->number;
1136 
1137         /* get sample count for one stream */
1138         pcxhr_init_rmh(&rmh, CMD_STREAM_SAMPLE_COUNT);
1139         pcxhr_set_pipe_cmd_params(&rmh, stream->pipe->is_capture,
1140                                   stream->pipe->first_audio, 0, stream_mask);
1141         /* rmh.stat_len = 2; */ /* 2 resp data for each stream of the pipe */
1142 
1143         err = pcxhr_send_msg(mgr, &rmh);
1144         if (err)
1145                 return 0;
1146 
1147         hw_sample_count = ((u_int64_t)rmh.stat[0]) << 24;
1148         hw_sample_count += (u_int64_t)rmh.stat[1];
1149 
1150         dev_dbg(&mgr->pci->dev,
1151                 "stream %c%d : abs samples real(%llu) timer(%llu)\n",
1152                     stream->pipe->is_capture ? 'C' : 'P',
1153                     stream->substream->number,
1154                     hw_sample_count,
1155                     stream->timer_abs_periods + stream->timer_period_frag +
1156                                                 mgr->granularity);
1157         return hw_sample_count;
1158 }
1159 
1160 static void pcxhr_update_timer_pos(struct pcxhr_mgr *mgr,
1161                                    struct pcxhr_stream *stream,
1162                                    int samples_to_add)
1163 {
1164         if (stream->substream &&
1165             (stream->status == PCXHR_STREAM_STATUS_RUNNING)) {
1166                 u_int64_t new_sample_count;
1167                 int elapsed = 0;
1168                 int hardware_read = 0;
1169                 struct snd_pcm_runtime *runtime = stream->substream->runtime;
1170 
1171                 if (samples_to_add < 0) {
1172                         stream->timer_is_synced = 0;
1173                         /* add default if no hardware_read possible */
1174                         samples_to_add = mgr->granularity;
1175                 }
1176 
1177                 if (!stream->timer_is_synced) {
1178                         if ((stream->timer_abs_periods != 0) ||
1179                             ((stream->timer_period_frag + samples_to_add) >=
1180                             runtime->period_size)) {
1181                                 new_sample_count =
1182                                   pcxhr_stream_read_position(mgr, stream);
1183                                 hardware_read = 1;
1184                                 if (new_sample_count >= mgr->granularity) {
1185                                         /* sub security offset because of
1186                                          * jitter and finer granularity of
1187                                          * dsp time (MBOX4)
1188                                          */
1189                                         new_sample_count -= mgr->granularity;
1190                                         stream->timer_is_synced = 1;
1191                                 }
1192                         }
1193                 }
1194                 if (!hardware_read) {
1195                         /* if we didn't try to sync the position, increment it
1196                          * by PCXHR_GRANULARITY every timer interrupt
1197                          */
1198                         new_sample_count = stream->timer_abs_periods +
1199                                 stream->timer_period_frag + samples_to_add;
1200                 }
1201                 while (1) {
1202                         u_int64_t new_elapse_pos = stream->timer_abs_periods +
1203                                 runtime->period_size;
1204                         if (new_elapse_pos > new_sample_count)
1205                                 break;
1206                         elapsed = 1;
1207                         stream->timer_buf_periods++;
1208                         if (stream->timer_buf_periods >= runtime->periods)
1209                                 stream->timer_buf_periods = 0;
1210                         stream->timer_abs_periods = new_elapse_pos;
1211                 }
1212                 if (new_sample_count >= stream->timer_abs_periods) {
1213                         stream->timer_period_frag =
1214                                 (u_int32_t)(new_sample_count -
1215                                             stream->timer_abs_periods);
1216                 } else {
1217                         dev_err(&mgr->pci->dev,
1218                                    "ERROR new_sample_count too small ??? %ld\n",
1219                                    (long unsigned int)new_sample_count);
1220                 }
1221 
1222                 if (elapsed) {
1223                         mutex_unlock(&mgr->lock);
1224                         snd_pcm_period_elapsed(stream->substream);
1225                         mutex_lock(&mgr->lock);
1226                 }
1227         }
1228 }
1229 
1230 irqreturn_t pcxhr_interrupt(int irq, void *dev_id)
1231 {
1232         struct pcxhr_mgr *mgr = dev_id;
1233         unsigned int reg;
1234         bool wake_thread = false;
1235 
1236         reg = PCXHR_INPL(mgr, PCXHR_PLX_IRQCS);
1237         if (! (reg & PCXHR_IRQCS_ACTIVE_PCIDB)) {
1238                 /* this device did not cause the interrupt */
1239                 return IRQ_NONE;
1240         }
1241 
1242         /* clear interrupt */
1243         reg = PCXHR_INPL(mgr, PCXHR_PLX_L2PCIDB);
1244         PCXHR_OUTPL(mgr, PCXHR_PLX_L2PCIDB, reg);
1245 
1246         /* timer irq occurred */
1247         if (reg & PCXHR_IRQ_TIMER) {
1248                 int timer_toggle = reg & PCXHR_IRQ_TIMER;
1249                 if (timer_toggle == mgr->timer_toggle) {
1250                         dev_dbg(&mgr->pci->dev, "ERROR TIMER TOGGLE\n");
1251                         mgr->dsp_time_err++;
1252                 }
1253 
1254                 mgr->timer_toggle = timer_toggle;
1255                 mgr->src_it_dsp = reg;
1256                 wake_thread = true;
1257         }
1258 
1259         /* other irq's handled in the thread */
1260         if (reg & PCXHR_IRQ_MASK) {
1261                 if (reg & PCXHR_IRQ_ASYNC) {
1262                         /* as we didn't request any async notifications,
1263                          * some kind of xrun error will probably occurred
1264                          */
1265                         /* better resynchronize all streams next interrupt : */
1266                         mgr->dsp_time_last = PCXHR_DSP_TIME_INVALID;
1267                 }
1268                 mgr->src_it_dsp = reg;
1269                 wake_thread = true;
1270         }
1271 #ifdef CONFIG_SND_DEBUG_VERBOSE
1272         if (reg & PCXHR_FATAL_DSP_ERR)
1273                 dev_dbg(&mgr->pci->dev, "FATAL DSP ERROR : %x\n", reg);
1274 #endif
1275 
1276         return wake_thread ? IRQ_WAKE_THREAD : IRQ_HANDLED;
1277 }
1278 
1279 irqreturn_t pcxhr_threaded_irq(int irq, void *dev_id)
1280 {
1281         struct pcxhr_mgr *mgr = dev_id;
1282         int i, j;
1283         struct snd_pcxhr *chip;
1284 
1285         mutex_lock(&mgr->lock);
1286         if (mgr->src_it_dsp & PCXHR_IRQ_TIMER) {
1287                 /* is a 24 bit counter */
1288                 int dsp_time_new =
1289                         PCXHR_INPL(mgr, PCXHR_PLX_MBOX4) & PCXHR_DSP_TIME_MASK;
1290                 int dsp_time_diff = dsp_time_new - mgr->dsp_time_last;
1291 
1292                 if ((dsp_time_diff < 0) &&
1293                     (mgr->dsp_time_last != PCXHR_DSP_TIME_INVALID)) {
1294                         /* handle dsp counter wraparound without resync */
1295                         int tmp_diff = dsp_time_diff + PCXHR_DSP_TIME_MASK + 1;
1296                         dev_dbg(&mgr->pci->dev,
1297                                 "WARNING DSP timestamp old(%d) new(%d)",
1298                                     mgr->dsp_time_last, dsp_time_new);
1299                         if (tmp_diff > 0 && tmp_diff <= (2*mgr->granularity)) {
1300                                 dev_dbg(&mgr->pci->dev,
1301                                         "-> timestamp wraparound OK: "
1302                                             "diff=%d\n", tmp_diff);
1303                                 dsp_time_diff = tmp_diff;
1304                         } else {
1305                                 dev_dbg(&mgr->pci->dev,
1306                                         "-> resynchronize all streams\n");
1307                                 mgr->dsp_time_err++;
1308                         }
1309                 }
1310 #ifdef CONFIG_SND_DEBUG_VERBOSE
1311                 if (dsp_time_diff == 0)
1312                         dev_dbg(&mgr->pci->dev,
1313                                 "ERROR DSP TIME NO DIFF time(%d)\n",
1314                                     dsp_time_new);
1315                 else if (dsp_time_diff >= (2*mgr->granularity))
1316                         dev_dbg(&mgr->pci->dev,
1317                                 "ERROR DSP TIME TOO BIG old(%d) add(%d)\n",
1318                                     mgr->dsp_time_last,
1319                                     dsp_time_new - mgr->dsp_time_last);
1320                 else if (dsp_time_diff % mgr->granularity)
1321                         dev_dbg(&mgr->pci->dev,
1322                                 "ERROR DSP TIME increased by %d\n",
1323                                     dsp_time_diff);
1324 #endif
1325                 mgr->dsp_time_last = dsp_time_new;
1326 
1327                 for (i = 0; i < mgr->num_cards; i++) {
1328                         chip = mgr->chip[i];
1329                         for (j = 0; j < chip->nb_streams_capt; j++)
1330                                 pcxhr_update_timer_pos(mgr,
1331                                                 &chip->capture_stream[j],
1332                                                 dsp_time_diff);
1333                 }
1334                 for (i = 0; i < mgr->num_cards; i++) {
1335                         chip = mgr->chip[i];
1336                         for (j = 0; j < chip->nb_streams_play; j++)
1337                                 pcxhr_update_timer_pos(mgr,
1338                                                 &chip->playback_stream[j],
1339                                                 dsp_time_diff);
1340                 }
1341         }
1342 
1343         pcxhr_msg_thread(mgr);
1344         mutex_unlock(&mgr->lock);
1345         return IRQ_HANDLED;
1346 }
1347 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp