Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/dev/pci Fix yds_intr. It has not worked well since netb...



details:   https://anonhg.NetBSD.org/src/rev/cd87ae3c4fb3
branches:  trunk
changeset: 451479:cd87ae3c4fb3
user:      isaki <isaki%NetBSD.org@localhost>
date:      Sat May 25 04:25:30 2019 +0000

description:
Fix yds_intr.  It has not worked well since netbsd-8.
- Read data offset before set ACTV2.
- The interrupts occur every hardware block, not blocksize specified
  by round_blocksize.  The hardware block may span (upper layer's)
  two blocks.  So bus_dmamap_sync'ing each (upper layer's) block is
  not enough.
  XXX To fix this, Use only 48kHz and drop other frequencies. It can
  make blocksize fixed.  Then the hardware block aligns to upper layer's
  block alignment.
Analyzed by Y.Sugahara.

diffstat:

 sys/dev/pci/yds.c |  100 +++++++++++++++++++++++++++++++----------------------
 1 files changed, 59 insertions(+), 41 deletions(-)

diffs (166 lines):

diff -r af2b2f2dc9ba -r cd87ae3c4fb3 sys/dev/pci/yds.c
--- a/sys/dev/pci/yds.c Sat May 25 04:25:14 2019 +0000
+++ b/sys/dev/pci/yds.c Sat May 25 04:25:30 2019 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: yds.c,v 1.62 2019/05/08 13:40:19 isaki Exp $   */
+/*     $NetBSD: yds.c,v 1.63 2019/05/25 04:25:30 isaki Exp $   */
 
 /*
  * Copyright (c) 2000, 2001 Kazuki Sakamoto and Minoura Makoto.
@@ -39,7 +39,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: yds.c,v 1.62 2019/05/08 13:40:19 isaki Exp $");
+__KERNEL_RCSID(0, "$NetBSD: yds.c,v 1.63 2019/05/25 04:25:30 isaki Exp $");
 
 #include "mpu.h"
 
@@ -1065,19 +1065,32 @@
                printf ("yds_intr: timeout!\n");
        }
 
+       /*
+        * XXX
+        * An interrupt in YMF754 occurs when next hardware frame is
+        * requested, not when current hardware frame processing is
+        * completed.  According to the datasheet, only access to the
+        * inactive bank is permitted, but in fact, fields in inactive
+        * bank that the chip should write to may or may not be filled
+        * at that time.  On the other hand, both the CPU and the device
+        * must guarantee that the fields in active bank are determined
+        * at the beginning of the interrupt.
+        * Therefore, we read active bank.
+        */
+
        if (status & YDS_STAT_INT) {
                int nbank;
+               u_int pdma = 0;
+               u_int rdma = 0;
 
-               nbank = (YREAD4(sc, YDS_CONTROL_SELECT) == 0);
+               /* nbank is bank number that YDS is processing now. */
+               nbank = YREAD4(sc, YDS_CONTROL_SELECT) & 1;
+
                /* Clear interrupt flag */
                YWRITE4(sc, YDS_STATUS, YDS_STAT_INT);
 
-               /* Buffer for the next frame is always ready. */
-               YWRITE4(sc, YDS_MODE, YREAD4(sc, YDS_MODE) | YDS_MODE_ACTV2);
-
+               /* Read current data offset before ACTV2 */
                if (sc->sc_play.intr) {
-                       u_int dma, ccpu, blk, len;
-
                        /* Sync play slot control data */
                        bus_dmamap_sync(sc->sc_dmatag, sc->sc_ctrldata.map,
                                        sc->pbankoff,
@@ -1086,23 +1099,42 @@
                                            N_PLAY_SLOT_CTRL_BANK,
                                        BUS_DMASYNC_POSTWRITE|
                                        BUS_DMASYNC_POSTREAD);
-                       dma = le32toh(sc->pbankp[nbank]->pgstart) * sc->sc_play.factor;
-                       ccpu = sc->sc_play.offset;
-                       blk = sc->sc_play.blksize;
-                       len = sc->sc_play.length;
+                       /* start offset of current processing bank */
+                       pdma = le32toh(sc->pbankp[nbank]->pgstart) *
+                           sc->sc_play.factor;
+               }
 
-                       if (((dma > ccpu) && (dma - ccpu > blk * 2)) ||
-                           ((ccpu > dma) && (dma + len - ccpu > blk * 2))) {
+               if (sc->sc_rec.intr) {
+                       /* Sync rec slot control data */
+                       bus_dmamap_sync(sc->sc_dmatag, sc->sc_ctrldata.map,
+                                       sc->rbankoff,
+                                       sizeof(struct rec_slot_ctrl_bank)*
+                                           N_REC_SLOT_CTRL*
+                                           N_REC_SLOT_CTRL_BANK,
+                                       BUS_DMASYNC_POSTWRITE|
+                                       BUS_DMASYNC_POSTREAD);
+                       /* start offset of current processing bank */
+                       rdma = le32toh(
+                           sc->rbank[YDS_INPUT_SLOT * 2 + nbank].pgstartadr);
+               }
+
+               /* Buffer for the next frame is always ready. */
+               YWRITE4(sc, YDS_MODE, YREAD4(sc, YDS_MODE) | YDS_MODE_ACTV2);
+
+               if (sc->sc_play.intr) {
+                       if (pdma < sc->sc_play.offset)
+                               pdma += sc->sc_play.length;
+                       if (pdma >= sc->sc_play.offset + sc->sc_play.blksize) {
                                /* We can fill the next block */
                                /* Sync ring buffer for previous write */
                                bus_dmamap_sync(sc->sc_dmatag,
                                                sc->sc_play.dma->map,
-                                               ccpu, blk,
+                                               0, sc->sc_play.length,
                                                BUS_DMASYNC_POSTWRITE);
                                sc->sc_play.intr(sc->sc_play.intr_arg);
-                               sc->sc_play.offset += blk;
-                               if (sc->sc_play.offset >= len) {
-                                       sc->sc_play.offset -= len;
+                               sc->sc_play.offset += sc->sc_play.blksize;
+                               if (sc->sc_play.offset >= sc->sc_play.length) {
+                                       sc->sc_play.offset -= sc->sc_play.length;
 #ifdef DIAGNOSTIC
                                        if (sc->sc_play.offset != 0)
                                                printf ("Audio ringbuffer botch\n");
@@ -1111,38 +1143,24 @@
                                /* Sync ring buffer for next write */
                                bus_dmamap_sync(sc->sc_dmatag,
                                                sc->sc_play.dma->map,
-                                               ccpu, blk,
+                                               0, sc->sc_play.length,
                                                BUS_DMASYNC_PREWRITE);
                        }
                }
                if (sc->sc_rec.intr) {
-                       u_int dma, ccpu, blk, len;
-
-                       /* Sync rec slot control data */
-                       bus_dmamap_sync(sc->sc_dmatag, sc->sc_ctrldata.map,
-                                       sc->rbankoff,
-                                       sizeof(struct rec_slot_ctrl_bank)*
-                                           N_REC_SLOT_CTRL*
-                                           N_REC_SLOT_CTRL_BANK,
-                                       BUS_DMASYNC_POSTWRITE|
-                                       BUS_DMASYNC_POSTREAD);
-                       dma = le32toh(sc->rbank[YDS_INPUT_SLOT*2 + nbank].pgstartadr);
-                       ccpu = sc->sc_rec.offset;
-                       blk = sc->sc_rec.blksize;
-                       len = sc->sc_rec.length;
-
-                       if (((dma > ccpu) && (dma - ccpu > blk * 2)) ||
-                           ((ccpu > dma) && (dma + len - ccpu > blk * 2))) {
+                       if (rdma < sc->sc_rec.offset)
+                               rdma += sc->sc_rec.length;
+                       if (rdma >= sc->sc_rec.offset + sc->sc_rec.blksize) {
                                /* We can drain the current block */
                                /* Sync ring buffer first */
                                bus_dmamap_sync(sc->sc_dmatag,
                                                sc->sc_rec.dma->map,
-                                               ccpu, blk,
+                                               0, sc->sc_rec.length,
                                                BUS_DMASYNC_POSTREAD);
                                sc->sc_rec.intr(sc->sc_rec.intr_arg);
-                               sc->sc_rec.offset += blk;
-                               if (sc->sc_rec.offset >= len) {
-                                       sc->sc_rec.offset -= len;
+                               sc->sc_rec.offset += sc->sc_rec.blksize;
+                               if (sc->sc_rec.offset >= sc->sc_rec.length) {
+                                       sc->sc_rec.offset -= sc->sc_rec.length;
 #ifdef DIAGNOSTIC
                                        if (sc->sc_rec.offset != 0)
                                                printf ("Audio ringbuffer botch\n");
@@ -1151,7 +1169,7 @@
                                /* Sync ring buffer for next read */
                                bus_dmamap_sync(sc->sc_dmatag,
                                                sc->sc_rec.dma->map,
-                                               ccpu, blk,
+                                               0, sc->sc_rec.length,
                                                BUS_DMASYNC_PREREAD);
                        }
                }



Home | Main Index | Thread Index | Old Index