The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/sound/pci/solo.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
    3  *
    4  * Redistribution and use in source and binary forms, with or without
    5  * modification, are permitted provided that the following conditions
    6  * are met:
    7  * 1. Redistributions of source code must retain the above copyright
    8  *    notice, this list of conditions and the following disclaimer.
    9  * 2. Redistributions in binary form must reproduce the above copyright
   10  *    notice, this list of conditions and the following disclaimer in the
   11  *    documentation and/or other materials provided with the distribution.
   12  *
   13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   23  * SUCH DAMAGE.
   24  */
   25 
   26 #include <dev/sound/pcm/sound.h>
   27 
   28 #include <dev/pci/pcireg.h>
   29 #include <dev/pci/pcivar.h>
   30 
   31 #include  <dev/sound/isa/sb.h>
   32 #include  <dev/sound/chip.h>
   33 
   34 #include "mixer_if.h"
   35 
   36 SND_DECLARE_FILE("$FreeBSD$");
   37 
   38 #define SOLO_DEFAULT_BUFSZ 16384
   39 #define ABS(x) (((x) < 0)? -(x) : (x))
   40 
   41 /* if defined, playback always uses the 2nd channel and full duplex works */
   42 #undef ESS18XX_DUPLEX
   43 
   44 /* more accurate clocks and split audio1/audio2 rates */
   45 #define ESS18XX_NEWSPEED
   46 
   47 static u_int32_t ess_playfmt[] = {
   48         AFMT_U8,
   49         AFMT_STEREO | AFMT_U8,
   50         AFMT_S8,
   51         AFMT_STEREO | AFMT_S8,
   52         AFMT_S16_LE,
   53         AFMT_STEREO | AFMT_S16_LE,
   54         AFMT_U16_LE,
   55         AFMT_STEREO | AFMT_U16_LE,
   56         0
   57 };
   58 static struct pcmchan_caps ess_playcaps = {5000, 49000, ess_playfmt, 0};
   59 
   60 /*
   61  * Recording output is byte-swapped
   62  */
   63 static u_int32_t ess_recfmt[] = {
   64         AFMT_U8,
   65         AFMT_STEREO | AFMT_U8,
   66         AFMT_S8,
   67         AFMT_STEREO | AFMT_S8,
   68         AFMT_S16_BE,
   69         AFMT_STEREO | AFMT_S16_BE,
   70         AFMT_U16_BE,
   71         AFMT_STEREO | AFMT_U16_BE,
   72         0
   73 };
   74 static struct pcmchan_caps ess_reccaps = {5000, 49000, ess_recfmt, 0};
   75 
   76 struct ess_info;
   77 
   78 struct ess_chinfo {
   79         struct ess_info *parent;
   80         struct pcm_channel *channel;
   81         struct snd_dbuf *buffer;
   82         int dir, hwch, stopping;
   83         u_int32_t fmt, spd, blksz;
   84 };
   85 
   86 struct ess_info {
   87         struct resource *io, *sb, *vc, *mpu, *gp;       /* I/O address for the board */
   88         struct resource *irq;
   89         void            *ih;
   90         bus_dma_tag_t parent_dmat;
   91 
   92         int simplex_dir, type, duplex:1, newspeed:1, dmasz[2];
   93         unsigned int bufsz;
   94 
   95         struct ess_chinfo pch, rch;
   96 };
   97 
   98 static int ess_rd(struct ess_info *sc, int reg);
   99 static void ess_wr(struct ess_info *sc, int reg, u_int8_t val);
  100 static int ess_dspready(struct ess_info *sc);
  101 static int ess_cmd(struct ess_info *sc, u_char val);
  102 static int ess_cmd1(struct ess_info *sc, u_char cmd, int val);
  103 static int ess_get_byte(struct ess_info *sc);
  104 static void ess_setmixer(struct ess_info *sc, u_int port, u_int value);
  105 static int ess_getmixer(struct ess_info *sc, u_int port);
  106 static int ess_reset_dsp(struct ess_info *sc);
  107 
  108 static int ess_write(struct ess_info *sc, u_char reg, int val);
  109 static int ess_read(struct ess_info *sc, u_char reg);
  110 
  111 static void ess_intr(void *arg);
  112 static int ess_setupch(struct ess_info *sc, int ch, int dir, int spd, u_int32_t fmt, int len);
  113 static int ess_start(struct ess_chinfo *ch);
  114 static int ess_stop(struct ess_chinfo *ch);
  115 
  116 static int ess_dmasetup(struct ess_info *sc, int ch, u_int32_t base, u_int16_t cnt, int dir);
  117 static int ess_dmapos(struct ess_info *sc, int ch);
  118 static int ess_dmatrigger(struct ess_info *sc, int ch, int go);
  119 
  120 /*
  121  * Common code for the midi and pcm functions
  122  *
  123  * ess_cmd write a single byte to the CMD port.
  124  * ess_cmd1 write a CMD + 1 byte arg
  125  * ess_cmd2 write a CMD + 2 byte arg
  126  * ess_get_byte returns a single byte from the DSP data port
  127  *
  128  * ess_write is actually ess_cmd1
  129  * ess_read access ext. regs via ess_cmd(0xc0, reg) followed by ess_get_byte
  130  */
  131 
  132 static int
  133 port_rd(struct resource *port, int regno, int size)
  134 {
  135         bus_space_tag_t st = rman_get_bustag(port);
  136         bus_space_handle_t sh = rman_get_bushandle(port);
  137 
  138         switch (size) {
  139         case 1:
  140                 return bus_space_read_1(st, sh, regno);
  141         case 2:
  142                 return bus_space_read_2(st, sh, regno);
  143         case 4:
  144                 return bus_space_read_4(st, sh, regno);
  145         default:
  146                 return 0xffffffff;
  147         }
  148 }
  149 
  150 static void
  151 port_wr(struct resource *port, int regno, u_int32_t data, int size)
  152 {
  153         bus_space_tag_t st = rman_get_bustag(port);
  154         bus_space_handle_t sh = rman_get_bushandle(port);
  155 
  156         switch (size) {
  157         case 1:
  158                 bus_space_write_1(st, sh, regno, data);
  159                 break;
  160         case 2:
  161                 bus_space_write_2(st, sh, regno, data);
  162                 break;
  163         case 4:
  164                 bus_space_write_4(st, sh, regno, data);
  165                 break;
  166         }
  167 }
  168 
  169 static int
  170 ess_rd(struct ess_info *sc, int reg)
  171 {
  172         return port_rd(sc->sb, reg, 1);
  173 }
  174 
  175 static void
  176 ess_wr(struct ess_info *sc, int reg, u_int8_t val)
  177 {
  178         port_wr(sc->sb, reg, val, 1);
  179 }
  180 
  181 static int
  182 ess_dspready(struct ess_info *sc)
  183 {
  184         return ((ess_rd(sc, SBDSP_STATUS) & 0x80) == 0);
  185 }
  186 
  187 static int
  188 ess_dspwr(struct ess_info *sc, u_char val)
  189 {
  190         int  i;
  191 
  192         for (i = 0; i < 1000; i++) {
  193                 if (ess_dspready(sc)) {
  194                         ess_wr(sc, SBDSP_CMD, val);
  195                         return 1;
  196                 }
  197                 if (i > 10) DELAY((i > 100)? 1000 : 10);
  198         }
  199         printf("ess_dspwr(0x%02x) timed out.\n", val);
  200         return 0;
  201 }
  202 
  203 static int
  204 ess_cmd(struct ess_info *sc, u_char val)
  205 {
  206         DEB(printf("ess_cmd: %x\n", val));
  207         return ess_dspwr(sc, val);
  208 }
  209 
  210 static int
  211 ess_cmd1(struct ess_info *sc, u_char cmd, int val)
  212 {
  213         DEB(printf("ess_cmd1: %x, %x\n", cmd, val));
  214         if (ess_dspwr(sc, cmd)) {
  215                 return ess_dspwr(sc, val & 0xff);
  216         } else return 0;
  217 }
  218 
  219 static void
  220 ess_setmixer(struct ess_info *sc, u_int port, u_int value)
  221 {
  222         u_long   flags;
  223 
  224         DEB(printf("ess_setmixer: reg=%x, val=%x\n", port, value);)
  225         flags = spltty();
  226         ess_wr(sc, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */
  227         DELAY(10);
  228         ess_wr(sc, SB_MIX_DATA, (u_char) (value & 0xff));
  229         DELAY(10);
  230         splx(flags);
  231 }
  232 
  233 static int
  234 ess_getmixer(struct ess_info *sc, u_int port)
  235 {
  236         int val;
  237         u_long flags;
  238 
  239         flags = spltty();
  240         ess_wr(sc, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */
  241         DELAY(10);
  242         val = ess_rd(sc, SB_MIX_DATA);
  243         DELAY(10);
  244         splx(flags);
  245 
  246         return val;
  247 }
  248 
  249 static int
  250 ess_get_byte(struct ess_info *sc)
  251 {
  252         int i;
  253 
  254         for (i = 1000; i > 0; i--) {
  255                 if (ess_rd(sc, 0xc) & 0x40)
  256                         return ess_rd(sc, DSP_READ);
  257                 else
  258                         DELAY(20);
  259         }
  260         return -1;
  261 }
  262 
  263 static int
  264 ess_write(struct ess_info *sc, u_char reg, int val)
  265 {
  266         return ess_cmd1(sc, reg, val);
  267 }
  268 
  269 static int
  270 ess_read(struct ess_info *sc, u_char reg)
  271 {
  272         return (ess_cmd(sc, 0xc0) && ess_cmd(sc, reg))? ess_get_byte(sc) : -1;
  273 }
  274 
  275 static int
  276 ess_reset_dsp(struct ess_info *sc)
  277 {
  278         DEB(printf("ess_reset_dsp\n"));
  279         ess_wr(sc, SBDSP_RST, 3);
  280         DELAY(100);
  281         ess_wr(sc, SBDSP_RST, 0);
  282         if (ess_get_byte(sc) != 0xAA) {
  283                 DEB(printf("ess_reset_dsp failed\n"));
  284 /*
  285                            rman_get_start(d->io_base)));
  286 */
  287                 return ENXIO;   /* Sorry */
  288         }
  289         ess_cmd(sc, 0xc6);
  290         return 0;
  291 }
  292 
  293 static void
  294 ess_intr(void *arg)
  295 {
  296         struct ess_info *sc = (struct ess_info *)arg;
  297         int src, pirq = 0, rirq = 0;
  298 
  299         src = 0;
  300         if (ess_getmixer(sc, 0x7a) & 0x80)
  301                 src |= 2;
  302         if (ess_rd(sc, 0x0c) & 0x01)
  303                 src |= 1;
  304 
  305         if (src == 0)
  306                 return;
  307 
  308         if (sc->duplex) {
  309                 pirq = (src & sc->pch.hwch)? 1 : 0;
  310                 rirq = (src & sc->rch.hwch)? 1 : 0;
  311         } else {
  312                 if (sc->simplex_dir == PCMDIR_PLAY)
  313                         pirq = 1;
  314                 if (sc->simplex_dir == PCMDIR_REC)
  315                         rirq = 1;
  316                 if (!pirq && !rirq)
  317                         printf("solo: IRQ neither playback nor rec!\n");
  318         }
  319 
  320         DEB(printf("ess_intr: pirq:%d rirq:%d\n",pirq,rirq));
  321 
  322         if (pirq) {
  323                 if (sc->pch.stopping) {
  324                         ess_dmatrigger(sc, sc->pch.hwch, 0);
  325                         sc->pch.stopping = 0;
  326                         if (sc->pch.hwch == 1)
  327                                 ess_write(sc, 0xb8, ess_read(sc, 0xb8) & ~0x01);
  328                         else
  329                                 ess_setmixer(sc, 0x78, ess_getmixer(sc, 0x78) & ~0x03);
  330                 }
  331                 chn_intr(sc->pch.channel);
  332         }
  333 
  334         if (rirq) {
  335                 if (sc->rch.stopping) {
  336                         ess_dmatrigger(sc, sc->rch.hwch, 0);
  337                         sc->rch.stopping = 0;
  338                         /* XXX: will this stop audio2? */
  339                         ess_write(sc, 0xb8, ess_read(sc, 0xb8) & ~0x01);
  340                 }
  341                 chn_intr(sc->rch.channel);
  342         }
  343 
  344         if (src & 2)
  345                 ess_setmixer(sc, 0x7a, ess_getmixer(sc, 0x7a) & ~0x80);
  346         if (src & 1)
  347                 ess_rd(sc, DSP_DATA_AVAIL);
  348 }
  349 
  350 /* utility functions for ESS */
  351 static u_int8_t
  352 ess_calcspeed8(int *spd)
  353 {
  354         int speed = *spd;
  355         u_int32_t t;
  356 
  357         if (speed > 22000) {
  358                 t = (795500 + speed / 2) / speed;
  359                 speed = (795500 + t / 2) / t;
  360                 t = (256 - t) | 0x80;
  361         } else {
  362                 t = (397700 + speed / 2) / speed;
  363                 speed = (397700 + t / 2) / t;
  364                 t = 128 - t;
  365         }
  366         *spd = speed;
  367         return t & 0x000000ff;
  368 }
  369 
  370 static u_int8_t
  371 ess_calcspeed9(int *spd)
  372 {
  373         int speed, s0, s1, use0;
  374         u_int8_t t0, t1;
  375 
  376         /* rate = source / (256 - divisor) */
  377         /* divisor = 256 - (source / rate) */
  378         speed = *spd;
  379         t0 = 128 - (793800 / speed);
  380         s0 = 793800 / (128 - t0);
  381 
  382         t1 = 128 - (768000 / speed);
  383         s1 = 768000 / (128 - t1);
  384         t1 |= 0x80;
  385 
  386         use0 = (ABS(speed - s0) < ABS(speed - s1))? 1 : 0;
  387 
  388         *spd = use0? s0 : s1;
  389         return use0? t0 : t1;
  390 }
  391 
  392 static u_int8_t
  393 ess_calcfilter(int spd)
  394 {
  395         int cutoff;
  396 
  397         /* cutoff = 7160000 / (256 - divisor) */
  398         /* divisor = 256 - (7160000 / cutoff) */
  399         cutoff = (spd * 9 * 82) / 20;
  400         return (256 - (7160000 / cutoff));
  401 }
  402 
  403 static int
  404 ess_setupch(struct ess_info *sc, int ch, int dir, int spd, u_int32_t fmt, int len)
  405 {
  406         int play = (dir == PCMDIR_PLAY)? 1 : 0;
  407         int b16 = (fmt & AFMT_16BIT)? 1 : 0;
  408         int stereo = (fmt & AFMT_STEREO)? 1 : 0;
  409         int unsign = (fmt == AFMT_U8 || fmt == AFMT_U16_LE || fmt == AFMT_U16_BE)? 1 : 0;
  410         u_int8_t spdval, fmtval;
  411 
  412         DEB(printf("ess_setupch\n"));
  413         spdval = (sc->newspeed)? ess_calcspeed9(&spd) : ess_calcspeed8(&spd);
  414 
  415         sc->simplex_dir = play ? PCMDIR_PLAY : PCMDIR_REC ;
  416 
  417         if (ch == 1) {
  418                 KASSERT((dir == PCMDIR_PLAY) || (dir == PCMDIR_REC), ("ess_setupch: dir1 bad"));
  419                 len = -len;
  420                 /* transfer length low */
  421                 ess_write(sc, 0xa4, len & 0x00ff);
  422                 /* transfer length high */
  423                 ess_write(sc, 0xa5, (len & 0xff00) >> 8);
  424                 /* autoinit, dma dir */
  425                 ess_write(sc, 0xb8, 0x04 | (play? 0x00 : 0x0a));
  426                 /* mono/stereo */
  427                 ess_write(sc, 0xa8, (ess_read(sc, 0xa8) & ~0x03) | (stereo? 0x01 : 0x02));
  428                 /* demand mode, 4 bytes/xfer */
  429                 ess_write(sc, 0xb9, 0x02);
  430                 /* sample rate */
  431                 ess_write(sc, 0xa1, spdval);
  432                 /* filter cutoff */
  433                 ess_write(sc, 0xa2, ess_calcfilter(spd));
  434                 /* setup dac/adc */
  435                 /*
  436                 if (play)
  437                         ess_write(sc, 0xb6, unsign? 0x80 : 0x00);
  438                 */
  439                 /* mono, b16: signed, load signal */
  440                 /*
  441                 ess_write(sc, 0xb7, 0x51 | (unsign? 0x00 : 0x20));
  442                 */
  443                 /* setup fifo */
  444                 ess_write(sc, 0xb7, 0x91 | (unsign? 0x00 : 0x20) |
  445                                            (b16? 0x04 : 0x00) |
  446                                            (stereo? 0x08 : 0x40));
  447                 /* irq control */
  448                 ess_write(sc, 0xb1, (ess_read(sc, 0xb1) & 0x0f) | 0x50);
  449                 /* drq control */
  450                 ess_write(sc, 0xb2, (ess_read(sc, 0xb2) & 0x0f) | 0x50);
  451         } else if (ch == 2) {
  452                 KASSERT(dir == PCMDIR_PLAY, ("ess_setupch: dir2 bad"));
  453                 len >>= 1;
  454                 len = -len;
  455                 /* transfer length low */
  456                 ess_setmixer(sc, 0x74, len & 0x00ff);
  457                 /* transfer length high */
  458                 ess_setmixer(sc, 0x76, (len & 0xff00) >> 8);
  459                 /* autoinit, 4 bytes/req */
  460                 ess_setmixer(sc, 0x78, 0x10);
  461                 fmtval = b16 | (stereo << 1) | ((!unsign) << 2);
  462                 /* enable irq, set format */
  463                 ess_setmixer(sc, 0x7a, 0x40 | fmtval);
  464                 if (sc->newspeed) {
  465                         /* sample rate */
  466                         ess_setmixer(sc, 0x70, spdval);
  467                         /* filter cutoff */
  468                         ess_setmixer(sc, 0x72, ess_calcfilter(spd));
  469                 }
  470 
  471         }
  472         return 0;
  473 }
  474 static int
  475 ess_start(struct ess_chinfo *ch)
  476 {
  477         struct ess_info *sc = ch->parent;
  478 
  479         DEB(printf("ess_start\n"););
  480         ess_setupch(sc, ch->hwch, ch->dir, ch->spd, ch->fmt, ch->blksz);
  481         ch->stopping = 0;
  482         if (ch->hwch == 1) {
  483                 ess_write(sc, 0xb8, ess_read(sc, 0xb8) | 0x01);
  484                 if (ch->dir == PCMDIR_PLAY) {
  485 #if 0
  486                         DELAY(100000); /* 100 ms */
  487 #endif
  488                         ess_cmd(sc, 0xd1);
  489                 }
  490         } else
  491                 ess_setmixer(sc, 0x78, ess_getmixer(sc, 0x78) | 0x03);
  492         return 0;
  493 }
  494 
  495 static int
  496 ess_stop(struct ess_chinfo *ch)
  497 {
  498         struct ess_info *sc = ch->parent;
  499 
  500         DEB(printf("ess_stop\n"));
  501         ch->stopping = 1;
  502         if (ch->hwch == 1)
  503                 ess_write(sc, 0xb8, ess_read(sc, 0xb8) & ~0x04);
  504         else
  505                 ess_setmixer(sc, 0x78, ess_getmixer(sc, 0x78) & ~0x10);
  506         DEB(printf("done with stop\n"));
  507         return 0;
  508 }
  509 
  510 /* -------------------------------------------------------------------- */
  511 /* channel interface for ESS18xx */
  512 static void *
  513 esschan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
  514 {
  515         struct ess_info *sc = devinfo;
  516         struct ess_chinfo *ch = (dir == PCMDIR_PLAY)? &sc->pch : &sc->rch;
  517 
  518         DEB(printf("esschan_init\n"));
  519         ch->parent = sc;
  520         ch->channel = c;
  521         ch->buffer = b;
  522         ch->dir = dir;
  523         if (sndbuf_alloc(ch->buffer, sc->parent_dmat, sc->bufsz) != 0)
  524                 return NULL;
  525         ch->hwch = 1;
  526         if ((dir == PCMDIR_PLAY) && (sc->duplex))
  527                 ch->hwch = 2;
  528         return ch;
  529 }
  530 
  531 static int
  532 esschan_setformat(kobj_t obj, void *data, u_int32_t format)
  533 {
  534         struct ess_chinfo *ch = data;
  535 
  536         ch->fmt = format;
  537         return 0;
  538 }
  539 
  540 static int
  541 esschan_setspeed(kobj_t obj, void *data, u_int32_t speed)
  542 {
  543         struct ess_chinfo *ch = data;
  544         struct ess_info *sc = ch->parent;
  545 
  546         ch->spd = speed;
  547         if (sc->newspeed)
  548                 ess_calcspeed9(&ch->spd);
  549         else
  550                 ess_calcspeed8(&ch->spd);
  551         return ch->spd;
  552 }
  553 
  554 static int
  555 esschan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
  556 {
  557         struct ess_chinfo *ch = data;
  558 
  559         ch->blksz = blocksize;
  560         return ch->blksz;
  561 }
  562 
  563 static int
  564 esschan_trigger(kobj_t obj, void *data, int go)
  565 {
  566         struct ess_chinfo *ch = data;
  567         struct ess_info *sc = ch->parent;
  568 
  569         DEB(printf("esschan_trigger: %d\n",go));
  570         if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD)
  571                 return 0;
  572 
  573         switch (go) {
  574         case PCMTRIG_START:
  575                 ess_dmasetup(sc, ch->hwch, sndbuf_getbufaddr(ch->buffer), sndbuf_getsize(ch->buffer), ch->dir);
  576                 ess_dmatrigger(sc, ch->hwch, 1);
  577                 ess_start(ch);
  578                 break;
  579 
  580         case PCMTRIG_STOP:
  581         case PCMTRIG_ABORT:
  582         default:
  583                 ess_stop(ch);
  584                 break;
  585         }
  586         return 0;
  587 }
  588 
  589 static int
  590 esschan_getptr(kobj_t obj, void *data)
  591 {
  592         struct ess_chinfo *ch = data;
  593         struct ess_info *sc = ch->parent;
  594 
  595         return ess_dmapos(sc, ch->hwch);
  596 }
  597 
  598 static struct pcmchan_caps *
  599 esschan_getcaps(kobj_t obj, void *data)
  600 {
  601         struct ess_chinfo *ch = data;
  602 
  603         return (ch->dir == PCMDIR_PLAY)? &ess_playcaps : &ess_reccaps;
  604 }
  605 
  606 static kobj_method_t esschan_methods[] = {
  607         KOBJMETHOD(channel_init,                esschan_init),
  608         KOBJMETHOD(channel_setformat,           esschan_setformat),
  609         KOBJMETHOD(channel_setspeed,            esschan_setspeed),
  610         KOBJMETHOD(channel_setblocksize,        esschan_setblocksize),
  611         KOBJMETHOD(channel_trigger,             esschan_trigger),
  612         KOBJMETHOD(channel_getptr,              esschan_getptr),
  613         KOBJMETHOD(channel_getcaps,             esschan_getcaps),
  614         { 0, 0 }
  615 };
  616 CHANNEL_DECLARE(esschan);
  617 
  618 /************************************************************/
  619 
  620 static int
  621 essmix_init(struct snd_mixer *m)
  622 {
  623         struct ess_info *sc = mix_getdevinfo(m);
  624 
  625         mix_setrecdevs(m, SOUND_MASK_CD | SOUND_MASK_MIC | SOUND_MASK_LINE |
  626                           SOUND_MASK_IMIX);
  627 
  628         mix_setdevs(m, SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE |
  629                        SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_VOLUME |
  630                        SOUND_MASK_LINE1);
  631 
  632         ess_setmixer(sc, 0, 0); /* reset */
  633 
  634         return 0;
  635 }
  636 
  637 static int
  638 essmix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
  639 {
  640         struct ess_info *sc = mix_getdevinfo(m);
  641         int preg = 0, rreg = 0, l, r;
  642 
  643         l = (left * 15) / 100;
  644         r = (right * 15) / 100;
  645         switch (dev) {
  646         case SOUND_MIXER_SYNTH:
  647                 preg = 0x36;
  648                 rreg = 0x6b;
  649                 break;
  650 
  651         case SOUND_MIXER_PCM:
  652                 preg = 0x14;
  653                 rreg = 0x7c;
  654                 break;
  655 
  656         case SOUND_MIXER_LINE:
  657                 preg = 0x3e;
  658                 rreg = 0x6e;
  659                 break;
  660 
  661         case SOUND_MIXER_MIC:
  662                 preg = 0x1a;
  663                 rreg = 0x68;
  664                 break;
  665 
  666         case SOUND_MIXER_LINE1:
  667                 preg = 0x3a;
  668                 rreg = 0x6c;
  669                 break;
  670 
  671         case SOUND_MIXER_CD:
  672                 preg = 0x38;
  673                 rreg = 0x6a;
  674                 break;
  675 
  676         case SOUND_MIXER_VOLUME:
  677                 l = left? (left * 63) / 100 : 64;
  678                 r = right? (right * 63) / 100 : 64;
  679                 ess_setmixer(sc, 0x60, l);
  680                 ess_setmixer(sc, 0x62, r);
  681                 left = (l == 64)? 0 : (l * 100) / 63;
  682                 right = (r == 64)? 0 : (r * 100) / 63;
  683                 return left | (right << 8);
  684         }
  685 
  686         if (preg)
  687                 ess_setmixer(sc, preg, (l << 4) | r);
  688         if (rreg)
  689                 ess_setmixer(sc, rreg, (l << 4) | r);
  690 
  691         left = (l * 100) / 15;
  692         right = (r * 100) / 15;
  693 
  694         return left | (right << 8);
  695 }
  696 
  697 static int
  698 essmix_setrecsrc(struct snd_mixer *m, u_int32_t src)
  699 {
  700         struct ess_info *sc = mix_getdevinfo(m);
  701         u_char recdev;
  702 
  703         switch (src) {
  704         case SOUND_MASK_CD:
  705                 recdev = 0x02;
  706                 break;
  707 
  708         case SOUND_MASK_LINE:
  709                 recdev = 0x06;
  710                 break;
  711 
  712         case SOUND_MASK_IMIX:
  713                 recdev = 0x05;
  714                 break;
  715 
  716         case SOUND_MASK_MIC:
  717         default:
  718                 recdev = 0x00;
  719                 src = SOUND_MASK_MIC;
  720                 break;
  721         }
  722 
  723         ess_setmixer(sc, 0x1c, recdev);
  724 
  725         return src;
  726 }
  727 
  728 static kobj_method_t solomixer_methods[] = {
  729         KOBJMETHOD(mixer_init,          essmix_init),
  730         KOBJMETHOD(mixer_set,           essmix_set),
  731         KOBJMETHOD(mixer_setrecsrc,     essmix_setrecsrc),
  732         { 0, 0 }
  733 };
  734 MIXER_DECLARE(solomixer);
  735 
  736 /************************************************************/
  737 
  738 static int
  739 ess_dmasetup(struct ess_info *sc, int ch, u_int32_t base, u_int16_t cnt, int dir)
  740 {
  741         KASSERT(ch == 1 || ch == 2, ("bad ch"));
  742         sc->dmasz[ch - 1] = cnt;
  743         if (ch == 1) {
  744                 port_wr(sc->vc, 0x8, 0xc4, 1); /* command */
  745                 port_wr(sc->vc, 0xd, 0xff, 1); /* reset */
  746                 port_wr(sc->vc, 0xf, 0x01, 1); /* mask */
  747                 port_wr(sc->vc, 0xb, dir == PCMDIR_PLAY? 0x58 : 0x54, 1); /* mode */
  748                 port_wr(sc->vc, 0x0, base, 4);
  749                 port_wr(sc->vc, 0x4, cnt - 1, 2);
  750 
  751         } else if (ch == 2) {
  752                 port_wr(sc->io, 0x6, 0x08, 1); /* autoinit */
  753                 port_wr(sc->io, 0x0, base, 4);
  754                 port_wr(sc->io, 0x4, cnt, 2);
  755         }
  756         return 0;
  757 }
  758 
  759 static int
  760 ess_dmapos(struct ess_info *sc, int ch)
  761 {
  762         int p = 0, i = 0, j = 0;
  763         u_long flags;
  764 
  765         KASSERT(ch == 1 || ch == 2, ("bad ch"));
  766         flags = spltty();
  767         if (ch == 1) {
  768 
  769 /*
  770  * During recording, this register is known to give back
  771  * garbage if it's not quiescent while being read. That's
  772  * why we spl, stop the DMA, and try over and over until
  773  * adjacent reads are "close", in the right order and not
  774  * bigger than is otherwise possible.
  775  */
  776                 ess_dmatrigger(sc, ch, 0);
  777                 DELAY(20);
  778                 do {
  779                         DELAY(10);
  780                         if (j > 1)
  781                                 printf("DMA count reg bogus: %04x & %04x\n",
  782                                         i, p);
  783                         i = port_rd(sc->vc, 0x4, 2) + 1;
  784                         p = port_rd(sc->vc, 0x4, 2) + 1;
  785                 } while ((p > sc->dmasz[ch - 1] || i < p || (p - i) > 0x8) && j++ < 1000);
  786                 ess_dmatrigger(sc, ch, 1);
  787         }
  788         else if (ch == 2)
  789                 p = port_rd(sc->io, 0x4, 2);
  790         splx(flags);
  791         return sc->dmasz[ch - 1] - p;
  792 }
  793 
  794 static int
  795 ess_dmatrigger(struct ess_info *sc, int ch, int go)
  796 {
  797         KASSERT(ch == 1 || ch == 2, ("bad ch"));
  798         if (ch == 1)
  799                 port_wr(sc->vc, 0xf, go? 0x00 : 0x01, 1); /* mask */
  800         else if (ch == 2)
  801                 port_wr(sc->io, 0x6, 0x08 | (go? 0x02 : 0x00), 1); /* autoinit */
  802         return 0;
  803 }
  804 
  805 static void
  806 ess_release_resources(struct ess_info *sc, device_t dev)
  807 {
  808         if (sc->irq) {
  809                 if (sc->ih)
  810                         bus_teardown_intr(dev, sc->irq, sc->ih);
  811                 bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq);
  812                 sc->irq = 0;
  813         }
  814         if (sc->io) {
  815                 bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(0), sc->io);
  816                 sc->io = 0;
  817         }
  818 
  819         if (sc->sb) {
  820                 bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(1), sc->sb);
  821                 sc->sb = 0;
  822         }
  823 
  824         if (sc->vc) {
  825                 bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(2), sc->vc);
  826                 sc->vc = 0;
  827         }
  828 
  829         if (sc->mpu) {
  830                 bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(3), sc->mpu);
  831                 sc->mpu = 0;
  832         }
  833 
  834         if (sc->gp) {
  835                 bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(4), sc->gp);
  836                 sc->gp = 0;
  837         }
  838 
  839         if (sc->parent_dmat) {
  840                 bus_dma_tag_destroy(sc->parent_dmat);
  841                 sc->parent_dmat = 0;
  842         }
  843 
  844         free(sc, M_DEVBUF);
  845 }
  846 
  847 static int
  848 ess_alloc_resources(struct ess_info *sc, device_t dev)
  849 {
  850         int rid;
  851 
  852         rid = PCIR_BAR(0);
  853         sc->io = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE);
  854 
  855         rid = PCIR_BAR(1);
  856         sc->sb = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE);
  857 
  858         rid = PCIR_BAR(2);
  859         sc->vc = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE);
  860 
  861         rid = PCIR_BAR(3);
  862         sc->mpu = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE);
  863 
  864         rid = PCIR_BAR(4);
  865         sc->gp = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE);
  866 
  867         rid = 0;
  868         sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
  869                 RF_ACTIVE | RF_SHAREABLE);
  870 
  871         return (sc->irq && sc->io && sc->sb && sc->vc && sc->mpu && sc->gp)? 0 : ENXIO;
  872 }
  873 
  874 static int
  875 ess_probe(device_t dev)
  876 {
  877         char *s = NULL;
  878         u_int32_t subdev;
  879 
  880         subdev = (pci_get_subdevice(dev) << 16) | pci_get_subvendor(dev);
  881         switch (pci_get_devid(dev)) {
  882         case 0x1969125d:
  883                 if (subdev == 0x8888125d)
  884                         s = "ESS Solo-1E";
  885                 else if (subdev == 0x1818125d)
  886                         s = "ESS Solo-1";
  887                 else
  888                         s = "ESS Solo-1 (unknown vendor)";
  889                 break;
  890         }
  891 
  892         if (s)
  893                 device_set_desc(dev, s);
  894         return s? 0 : ENXIO;
  895 }
  896 
  897 #define ESS_PCI_LEGACYCONTROL       0x40
  898 #define ESS_PCI_CONFIG              0x50
  899 #define ESS_PCI_DDMACONTROL             0x60
  900 
  901 static int 
  902 ess_suspend(device_t dev)
  903 {
  904   return 0;
  905 }
  906 
  907 static int 
  908 ess_resume(device_t dev)
  909 {
  910         uint16_t ddma;
  911         uint32_t data;
  912         struct ess_info *sc = pcm_getdevinfo(dev);
  913         
  914         data = pci_read_config(dev, PCIR_COMMAND, 2);
  915         data |= PCIM_CMD_PORTEN | PCIM_CMD_BUSMASTEREN;
  916         pci_write_config(dev, PCIR_COMMAND, data, 2);
  917         data = pci_read_config(dev, PCIR_COMMAND, 2);
  918 
  919         ddma = rman_get_start(sc->vc) | 1;
  920         pci_write_config(dev, ESS_PCI_LEGACYCONTROL, 0x805f, 2);
  921         pci_write_config(dev, ESS_PCI_DDMACONTROL, ddma, 2);
  922         pci_write_config(dev, ESS_PCI_CONFIG, 0, 2);
  923 
  924         if (ess_reset_dsp(sc))
  925                 goto no;
  926         if (mixer_reinit(dev))
  927                 goto no;
  928         if (sc->newspeed)
  929                 ess_setmixer(sc, 0x71, 0x2a);
  930 
  931         port_wr(sc->io, 0x7, 0xb0, 1); /* enable irqs */
  932 
  933         return 0;
  934  no:
  935         return EIO;
  936 }
  937 
  938 static int
  939 ess_attach(device_t dev)
  940 {
  941         struct ess_info *sc;
  942         char status[SND_STATUSLEN];
  943         u_int16_t ddma;
  944         u_int32_t data;
  945 
  946         sc = (struct ess_info *)malloc(sizeof *sc, M_DEVBUF, M_NOWAIT | M_ZERO);
  947         if (!sc)
  948                 return ENXIO;
  949 
  950         data = pci_read_config(dev, PCIR_COMMAND, 2);
  951         data |= PCIM_CMD_PORTEN | PCIM_CMD_BUSMASTEREN;
  952         pci_write_config(dev, PCIR_COMMAND, data, 2);
  953         data = pci_read_config(dev, PCIR_COMMAND, 2);
  954 
  955         if (ess_alloc_resources(sc, dev))
  956                 goto no;
  957 
  958         sc->bufsz = pcm_getbuffersize(dev, 4096, SOLO_DEFAULT_BUFSZ, 65536);
  959 
  960         ddma = rman_get_start(sc->vc) | 1;
  961         pci_write_config(dev, ESS_PCI_LEGACYCONTROL, 0x805f, 2);
  962         pci_write_config(dev, ESS_PCI_DDMACONTROL, ddma, 2);
  963         pci_write_config(dev, ESS_PCI_CONFIG, 0, 2);
  964 
  965         if (ess_reset_dsp(sc))
  966                 goto no;
  967         if (mixer_init(dev, &solomixer_class, sc))
  968                 goto no;
  969 
  970         port_wr(sc->io, 0x7, 0xb0, 1); /* enable irqs */
  971 #ifdef ESS18XX_DUPLEX
  972         sc->duplex = 1;
  973 #else
  974         sc->duplex = 0;
  975 #endif
  976 
  977 #ifdef ESS18XX_NEWSPEED
  978         sc->newspeed = 1;
  979 #else
  980         sc->newspeed = 0;
  981 #endif
  982         if (sc->newspeed)
  983                 ess_setmixer(sc, 0x71, 0x2a);
  984 
  985         snd_setup_intr(dev, sc->irq, 0, ess_intr, sc, &sc->ih);
  986         if (!sc->duplex)
  987                 pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX);
  988 
  989         if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/65536, /*boundary*/0,
  990                         /*lowaddr*/BUS_SPACE_MAXADDR_24BIT,
  991                         /*highaddr*/BUS_SPACE_MAXADDR,
  992                         /*filter*/NULL, /*filterarg*/NULL,
  993                         /*maxsize*/sc->bufsz, /*nsegments*/1,
  994                         /*maxsegz*/0x3ffff,
  995                         /*flags*/0, /*lockfunc*/busdma_lock_mutex,
  996                         /*lockarg*/&Giant, &sc->parent_dmat) != 0) {
  997                 device_printf(dev, "unable to create dma tag\n");
  998                 goto no;
  999         }
 1000 
 1001         snprintf(status, SND_STATUSLEN, "at io 0x%lx,0x%lx,0x%lx irq %ld %s",
 1002                 rman_get_start(sc->io), rman_get_start(sc->sb), rman_get_start(sc->vc),
 1003                 rman_get_start(sc->irq),PCM_KLDSTRING(snd_solo));
 1004 
 1005         if (pcm_register(dev, sc, 1, 1))
 1006                 goto no;
 1007         pcm_addchan(dev, PCMDIR_REC, &esschan_class, sc);
 1008         pcm_addchan(dev, PCMDIR_PLAY, &esschan_class, sc);
 1009         pcm_setstatus(dev, status);
 1010 
 1011         return 0;
 1012 
 1013 no:
 1014         ess_release_resources(sc, dev);
 1015         return ENXIO;
 1016 }
 1017 
 1018 static int
 1019 ess_detach(device_t dev)
 1020 {
 1021         int r;
 1022         struct ess_info *sc;
 1023 
 1024         r = pcm_unregister(dev);
 1025         if (r)
 1026                 return r;
 1027 
 1028         sc = pcm_getdevinfo(dev);
 1029         ess_release_resources(sc, dev);
 1030         return 0;
 1031 }
 1032 
 1033 static device_method_t ess_methods[] = {
 1034         /* Device interface */
 1035         DEVMETHOD(device_probe,         ess_probe),
 1036         DEVMETHOD(device_attach,        ess_attach),
 1037         DEVMETHOD(device_detach,        ess_detach),
 1038         DEVMETHOD(device_resume,        ess_resume),
 1039         DEVMETHOD(device_suspend,       ess_suspend),
 1040 
 1041         { 0, 0 }
 1042 };
 1043 
 1044 static driver_t ess_driver = {
 1045         "pcm",
 1046         ess_methods,
 1047         PCM_SOFTC_SIZE,
 1048 };
 1049 
 1050 DRIVER_MODULE(snd_solo, pci, ess_driver, pcm_devclass, 0, 0);
 1051 MODULE_DEPEND(snd_solo, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
 1052 MODULE_VERSION(snd_solo, 1);
 1053 
 1054 
 1055 

Cache object: fd943b8608fff2289ad7e101d6edcdda


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.