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

Cache object: 891950fbb8de0e04637f7e879291cb6c


[ 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.