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/als4000.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) 2001 Orion Hodson <oho@acm.org>
    3  * All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice, this list of conditions and the following disclaimer.
   10  * 2. Redistributions in binary form must reproduce the above copyright
   11  *    notice, this list of conditions and the following disclaimer in the
   12  *    documentation and/or other materials provided with the distribution.
   13  *
   14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT
   22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF
   24  * SUCH DAMAGE.
   25  */
   26 
   27 /*
   28  * als4000.c - driver for the Avance Logic ALS 4000 chipset.
   29  *
   30  * The ALS4000 is effectively an SB16 with a PCI interface.
   31  *
   32  * This driver derives from ALS4000a.PDF, Bart Hartgers alsa driver, and
   33  * SB16 register descriptions.
   34  */
   35 
   36 #include <dev/sound/pcm/sound.h>
   37 #include <dev/sound/isa/sb.h>
   38 #include <dev/sound/pci/als4000.h>
   39 
   40 #include <dev/pci/pcireg.h>
   41 #include <dev/pci/pcivar.h>
   42 
   43 #include "mixer_if.h"
   44 
   45 SND_DECLARE_FILE("$FreeBSD$");
   46 
   47 /* Debugging macro's */
   48 #undef DEB
   49 #ifndef DEB
   50 #define DEB(x)  /* x */
   51 #endif /* DEB */
   52 
   53 #define ALS_DEFAULT_BUFSZ 16384
   54 
   55 /* ------------------------------------------------------------------------- */
   56 /* Structures */
   57 
   58 struct sc_info;
   59 
   60 struct sc_chinfo {
   61         struct sc_info          *parent;
   62         struct pcm_channel      *channel;
   63         struct snd_dbuf         *buffer;
   64         u_int32_t               format, speed, phys_buf, bps;
   65         u_int32_t               dma_active:1, dma_was_active:1;
   66         u_int8_t                gcr_fifo_status;
   67         int                     dir;
   68 };
   69 
   70 struct sc_info {
   71         device_t                dev;
   72         bus_space_tag_t         st;
   73         bus_space_handle_t      sh;
   74         bus_dma_tag_t           parent_dmat;
   75         struct resource         *reg, *irq;
   76         int                     regid, irqid;
   77         void                    *ih;
   78 
   79         unsigned int            bufsz;
   80         struct sc_chinfo        pch, rch;
   81 };
   82 
   83 /* Channel caps */
   84 
   85 static u_int32_t als_format[] = {
   86         AFMT_U8,
   87         AFMT_STEREO | AFMT_U8,
   88         AFMT_S16_LE,
   89         AFMT_STEREO | AFMT_S16_LE,
   90         0
   91 };
   92 
   93 static struct pcmchan_caps als_caps = { 4000, 48000, als_format, 0 };
   94 
   95 /* ------------------------------------------------------------------------- */
   96 /* Register Utilities */
   97 
   98 static u_int32_t
   99 als_gcr_rd(struct sc_info *sc, int index)
  100 {
  101         bus_space_write_1(sc->st, sc->sh, ALS_GCR_INDEX, index);
  102         return bus_space_read_4(sc->st, sc->sh, ALS_GCR_DATA);
  103 }
  104 
  105 static void
  106 als_gcr_wr(struct sc_info *sc, int index, int data)
  107 {
  108         bus_space_write_1(sc->st, sc->sh, ALS_GCR_INDEX, index);
  109         bus_space_write_4(sc->st, sc->sh, ALS_GCR_DATA, data);
  110 }
  111 
  112 static u_int8_t
  113 als_intr_rd(struct sc_info *sc)
  114 {
  115         return bus_space_read_1(sc->st, sc->sh, ALS_SB_MPU_IRQ);
  116 }
  117 
  118 static void
  119 als_intr_wr(struct sc_info *sc, u_int8_t data)
  120 {
  121         bus_space_write_1(sc->st, sc->sh, ALS_SB_MPU_IRQ, data);
  122 }
  123 
  124 static u_int8_t
  125 als_mix_rd(struct sc_info *sc, u_int8_t index)
  126 {
  127         bus_space_write_1(sc->st, sc->sh, ALS_MIXER_INDEX, index);
  128         return bus_space_read_1(sc->st, sc->sh, ALS_MIXER_DATA);
  129 }
  130 
  131 static void
  132 als_mix_wr(struct sc_info *sc, u_int8_t index, u_int8_t data)
  133 {
  134         bus_space_write_1(sc->st, sc->sh, ALS_MIXER_INDEX, index);
  135         bus_space_write_1(sc->st, sc->sh, ALS_MIXER_DATA, data);
  136 }
  137 
  138 static void
  139 als_esp_wr(struct sc_info *sc, u_int8_t data)
  140 {
  141         u_int32_t       tries, v;
  142 
  143         tries = 1000;
  144         do {
  145                 v = bus_space_read_1(sc->st, sc->sh, ALS_ESP_WR_STATUS);
  146                 if (~v & 0x80)
  147                         break;
  148                 DELAY(20);
  149         } while (--tries != 0);
  150 
  151         if (tries == 0)
  152                 device_printf(sc->dev, "als_esp_wr timeout");
  153 
  154         bus_space_write_1(sc->st, sc->sh, ALS_ESP_WR_DATA, data);
  155 }
  156 
  157 static int
  158 als_esp_reset(struct sc_info *sc)
  159 {
  160         u_int32_t       tries, u, v;
  161 
  162         bus_space_write_1(sc->st, sc->sh, ALS_ESP_RST, 1);
  163         DELAY(10);
  164         bus_space_write_1(sc->st, sc->sh, ALS_ESP_RST, 0);
  165         DELAY(30);
  166 
  167         tries = 1000;
  168         do {
  169                 u = bus_space_read_1(sc->st, sc->sh, ALS_ESP_RD_STATUS8);
  170                 if (u & 0x80) {
  171                         v = bus_space_read_1(sc->st, sc->sh, ALS_ESP_RD_DATA);
  172                         if (v == 0xaa)
  173                                 return 0;
  174                         else
  175                                 break;
  176                 }
  177                 DELAY(20);
  178         } while (--tries != 0);
  179 
  180         if (tries == 0)
  181                 device_printf(sc->dev, "als_esp_reset timeout");
  182         return 1;
  183 }
  184 
  185 static u_int8_t
  186 als_ack_read(struct sc_info *sc, u_int8_t addr)
  187 {
  188         u_int8_t r = bus_space_read_1(sc->st, sc->sh, addr);
  189         return r;
  190 }
  191 
  192 /* ------------------------------------------------------------------------- */
  193 /* Common pcm channel implementation */
  194 
  195 static void *
  196 alschan_init(kobj_t obj, void *devinfo,
  197              struct snd_dbuf *b, struct pcm_channel *c, int dir)
  198 {
  199         struct  sc_info *sc = devinfo;
  200         struct  sc_chinfo *ch;
  201 
  202         if (dir == PCMDIR_PLAY) {
  203                 ch = &sc->pch;
  204                 ch->gcr_fifo_status = ALS_GCR_FIFO0_STATUS;
  205         } else {
  206                 ch = &sc->rch;
  207                 ch->gcr_fifo_status = ALS_GCR_FIFO1_STATUS;
  208         }
  209         ch->dir = dir;
  210         ch->parent = sc;
  211         ch->channel = c;
  212         ch->bps = 1;
  213         ch->format = AFMT_U8;
  214         ch->speed = DSP_DEFAULT_SPEED;
  215         ch->buffer = b;
  216         if (sndbuf_alloc(ch->buffer, sc->parent_dmat, sc->bufsz) != 0) {
  217                 return NULL;
  218         }
  219         return ch;
  220 }
  221 
  222 static int
  223 alschan_setformat(kobj_t obj, void *data, u_int32_t format)
  224 {
  225         struct  sc_chinfo *ch = data;
  226 
  227         ch->format = format;
  228         return 0;
  229 }
  230 
  231 static int
  232 alschan_setspeed(kobj_t obj, void *data, u_int32_t speed)
  233 {
  234         struct  sc_chinfo *ch = data, *other;
  235         struct  sc_info *sc = ch->parent;
  236 
  237         other = (ch->dir == PCMDIR_PLAY) ? &sc->rch : &sc->pch;
  238 
  239         /* Deny request if other dma channel is active */
  240         if (other->dma_active) {
  241                 ch->speed = other->speed;
  242                 return other->speed;
  243         }
  244 
  245         ch->speed = speed;
  246         return speed;
  247 }
  248 
  249 static int
  250 alschan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
  251 {
  252         struct  sc_chinfo *ch = data;
  253         struct  sc_info *sc = ch->parent;
  254 
  255         if (blocksize > sc->bufsz / 2) {
  256                 blocksize = sc->bufsz / 2;
  257         }
  258         sndbuf_resize(ch->buffer, 2, blocksize);
  259         return blocksize;
  260 }
  261 
  262 static int
  263 alschan_getptr(kobj_t obj, void *data)
  264 {
  265         struct sc_chinfo *ch = data;
  266         int32_t pos, sz;
  267 
  268         pos = als_gcr_rd(ch->parent, ch->gcr_fifo_status) & 0xffff;
  269         sz  = sndbuf_getsize(ch->buffer);
  270         return (2 * sz - pos - 1) % sz;
  271 }
  272 
  273 static struct pcmchan_caps*
  274 alschan_getcaps(kobj_t obj, void *data)
  275 {
  276         return &als_caps;
  277 }
  278 
  279 static void
  280 als_set_speed(struct sc_chinfo *ch)
  281 {
  282         struct sc_info *sc = ch->parent;
  283         struct sc_chinfo *other;
  284 
  285         other = (ch->dir == PCMDIR_PLAY) ? &sc->rch : &sc->pch;
  286         if (other->dma_active == 0) {
  287                 als_esp_wr(sc, ALS_ESP_SAMPLE_RATE);
  288                 als_esp_wr(sc, ch->speed >> 8);
  289                 als_esp_wr(sc, ch->speed & 0xff);
  290         } else {
  291                 DEB(printf("speed locked at %d (tried %d)\n",
  292                            other->speed, ch->speed));
  293         }
  294 }
  295 
  296 /* ------------------------------------------------------------------------- */
  297 /* Playback channel implementation */
  298 
  299 #define ALS_8BIT_CMD(x, y)  { (x), (y), DSP_DMA8,  DSP_CMD_DMAPAUSE_8  }
  300 #define ALS_16BIT_CMD(x, y) { (x), (y), DSP_DMA16, DSP_CMD_DMAPAUSE_16 }
  301 
  302 struct playback_command {
  303         u_int32_t pcm_format;   /* newpcm format */
  304         u_int8_t  format_val;   /* sb16 format value */
  305         u_int8_t  dma_prog;     /* sb16 dma program */
  306         u_int8_t  dma_stop;     /* sb16 stop register */
  307 } static const playback_cmds[] = {
  308         ALS_8BIT_CMD(AFMT_U8, DSP_MODE_U8MONO),
  309         ALS_8BIT_CMD(AFMT_U8 | AFMT_STEREO, DSP_MODE_U8STEREO),
  310         ALS_16BIT_CMD(AFMT_S16_LE, DSP_MODE_S16MONO),
  311         ALS_16BIT_CMD(AFMT_S16_LE | AFMT_STEREO, DSP_MODE_S16STEREO),
  312 };
  313 
  314 static const struct playback_command*
  315 als_get_playback_command(u_int32_t format)
  316 {
  317         u_int32_t i, n;
  318 
  319         n = sizeof(playback_cmds) / sizeof(playback_cmds[0]);
  320         for (i = 0; i < n; i++) {
  321                 if (playback_cmds[i].pcm_format == format) {
  322                         return &playback_cmds[i];
  323                 }
  324         }
  325         DEB(printf("als_get_playback_command: invalid format 0x%08x\n",
  326                    format));
  327         return &playback_cmds[0];
  328 }
  329 
  330 static void
  331 als_playback_start(struct sc_chinfo *ch)
  332 {
  333         const struct playback_command *p;
  334         struct  sc_info *sc = ch->parent;
  335         u_int32_t       buf, bufsz, count, dma_prog;
  336 
  337         buf = sndbuf_getbufaddr(ch->buffer);
  338         bufsz = sndbuf_getsize(ch->buffer);
  339         count = bufsz / 2;
  340         if (ch->format & AFMT_16BIT)
  341                 count /= 2;
  342         count--;
  343 
  344         als_esp_wr(sc, DSP_CMD_SPKON);
  345         als_set_speed(ch);
  346 
  347         als_gcr_wr(sc, ALS_GCR_DMA0_START, buf);
  348         als_gcr_wr(sc, ALS_GCR_DMA0_MODE, (bufsz - 1) | 0x180000);
  349 
  350         p = als_get_playback_command(ch->format);
  351         dma_prog = p->dma_prog | DSP_F16_DAC | DSP_F16_AUTO | DSP_F16_FIFO_ON;
  352 
  353         als_esp_wr(sc, dma_prog);
  354         als_esp_wr(sc, p->format_val);
  355         als_esp_wr(sc, count & 0xff);
  356         als_esp_wr(sc, count >> 8);
  357 
  358         ch->dma_active = 1;
  359 }
  360 
  361 static int
  362 als_playback_stop(struct sc_chinfo *ch)
  363 {
  364         const struct playback_command *p;
  365         struct sc_info *sc = ch->parent;
  366         u_int32_t active;
  367 
  368         active = ch->dma_active;
  369         if (active) {
  370                 p = als_get_playback_command(ch->format);
  371                 als_esp_wr(sc, p->dma_stop);
  372         }
  373         ch->dma_active = 0;
  374         return active;
  375 }
  376 
  377 static int
  378 alspchan_trigger(kobj_t obj, void *data, int go)
  379 {
  380         struct  sc_chinfo *ch = data;
  381 
  382         switch(go) {
  383         case PCMTRIG_START:
  384                 als_playback_start(ch);
  385                 break;
  386         case PCMTRIG_ABORT:
  387                 als_playback_stop(ch);
  388                 break;
  389         }
  390         return 0;
  391 }
  392 
  393 static kobj_method_t alspchan_methods[] = {
  394         KOBJMETHOD(channel_init,                alschan_init),
  395         KOBJMETHOD(channel_setformat,           alschan_setformat),
  396         KOBJMETHOD(channel_setspeed,            alschan_setspeed),
  397         KOBJMETHOD(channel_setblocksize,        alschan_setblocksize),
  398         KOBJMETHOD(channel_trigger,             alspchan_trigger),
  399         KOBJMETHOD(channel_getptr,              alschan_getptr),
  400         KOBJMETHOD(channel_getcaps,             alschan_getcaps),
  401         { 0, 0 }
  402 };
  403 CHANNEL_DECLARE(alspchan);
  404 
  405 /* ------------------------------------------------------------------------- */
  406 /* Capture channel implementation */
  407 
  408 static u_int8_t
  409 als_get_fifo_format(struct sc_info *sc, u_int32_t format)
  410 {
  411         switch (format) {
  412         case AFMT_U8:
  413                 return ALS_FIFO1_8BIT;
  414         case AFMT_U8 | AFMT_STEREO:
  415                 return ALS_FIFO1_8BIT | ALS_FIFO1_STEREO;
  416         case AFMT_S16_LE:
  417                 return ALS_FIFO1_SIGNED;
  418         case AFMT_S16_LE | AFMT_STEREO:
  419                 return ALS_FIFO1_SIGNED | ALS_FIFO1_STEREO;
  420         }
  421         device_printf(sc->dev, "format not found: 0x%08x\n", format);
  422         return ALS_FIFO1_8BIT;
  423 }
  424 
  425 static void
  426 als_capture_start(struct sc_chinfo *ch)
  427 {
  428         struct  sc_info *sc = ch->parent;
  429         u_int32_t       buf, bufsz, count, dma_prog;
  430 
  431         buf = sndbuf_getbufaddr(ch->buffer);
  432         bufsz = sndbuf_getsize(ch->buffer);
  433         count = bufsz / 2;
  434         if (ch->format & AFMT_16BIT)
  435                 count /= 2;
  436         count--;
  437 
  438         als_esp_wr(sc, DSP_CMD_SPKON);
  439         als_set_speed(ch);
  440 
  441         als_gcr_wr(sc, ALS_GCR_FIFO1_START, buf);
  442         als_gcr_wr(sc, ALS_GCR_FIFO1_COUNT, (bufsz - 1));
  443 
  444         als_mix_wr(sc, ALS_FIFO1_LENGTH_LO, count & 0xff);
  445         als_mix_wr(sc, ALS_FIFO1_LENGTH_HI, count >> 8);
  446 
  447         dma_prog = ALS_FIFO1_RUN | als_get_fifo_format(sc, ch->format);
  448         als_mix_wr(sc, ALS_FIFO1_CONTROL, dma_prog);
  449 
  450         ch->dma_active = 1;
  451 }
  452 
  453 static int
  454 als_capture_stop(struct sc_chinfo *ch)
  455 {
  456         struct sc_info *sc = ch->parent;
  457         u_int32_t active;
  458 
  459         active = ch->dma_active;
  460         if (active) {
  461                 als_mix_wr(sc, ALS_FIFO1_CONTROL, ALS_FIFO1_STOP);
  462         }
  463         ch->dma_active = 0;
  464         return active;
  465 }
  466 
  467 static int
  468 alsrchan_trigger(kobj_t obj, void *data, int go)
  469 {
  470         struct  sc_chinfo *ch = data;
  471 
  472         switch(go) {
  473         case PCMTRIG_START:
  474                 als_capture_start(ch);
  475                 break;
  476         case PCMTRIG_ABORT:
  477                 als_capture_stop(ch);
  478                 break;
  479         }
  480         return 0;
  481 }
  482 
  483 static kobj_method_t alsrchan_methods[] = {
  484         KOBJMETHOD(channel_init,                alschan_init),
  485         KOBJMETHOD(channel_setformat,           alschan_setformat),
  486         KOBJMETHOD(channel_setspeed,            alschan_setspeed),
  487         KOBJMETHOD(channel_setblocksize,        alschan_setblocksize),
  488         KOBJMETHOD(channel_trigger,             alsrchan_trigger),
  489         KOBJMETHOD(channel_getptr,              alschan_getptr),
  490         KOBJMETHOD(channel_getcaps,             alschan_getcaps),
  491         { 0, 0 }
  492 };
  493 CHANNEL_DECLARE(alsrchan);
  494 
  495 /* ------------------------------------------------------------------------- */
  496 /* Mixer related */
  497 
  498 /*
  499  * ALS4000 has an sb16 mixer, with some additional controls that we do
  500  * not yet a means to support.
  501  */
  502 
  503 struct sb16props {
  504         u_int8_t lreg;
  505         u_int8_t rreg;
  506         u_int8_t bits;
  507         u_int8_t oselect;
  508         u_int8_t iselect; /* left input mask */
  509 } static const amt[SOUND_MIXER_NRDEVICES] = {
  510         [SOUND_MIXER_VOLUME]  = { 0x30, 0x31, 5, 0x00, 0x00 },
  511         [SOUND_MIXER_PCM]     = { 0x32, 0x33, 5, 0x00, 0x00 },
  512         [SOUND_MIXER_SYNTH]   = { 0x34, 0x35, 5, 0x60, 0x40 },
  513         [SOUND_MIXER_CD]      = { 0x36, 0x37, 5, 0x06, 0x04 },
  514         [SOUND_MIXER_LINE]    = { 0x38, 0x39, 5, 0x18, 0x10 },
  515         [SOUND_MIXER_MIC]     = { 0x3a, 0x00, 5, 0x01, 0x01 },
  516         [SOUND_MIXER_SPEAKER] = { 0x3b, 0x00, 2, 0x00, 0x00 },
  517         [SOUND_MIXER_IGAIN]   = { 0x3f, 0x40, 2, 0x00, 0x00 },
  518         [SOUND_MIXER_OGAIN]   = { 0x41, 0x42, 2, 0x00, 0x00 },
  519         /* The following have register values but no h/w implementation */
  520         [SOUND_MIXER_TREBLE]  = { 0x44, 0x45, 4, 0x00, 0x00 },
  521         [SOUND_MIXER_BASS]    = { 0x46, 0x47, 4, 0x00, 0x00 }
  522 };
  523 
  524 static int
  525 alsmix_init(struct snd_mixer *m)
  526 {
  527         u_int32_t i, v;
  528 
  529         for (i = v = 0; i < SOUND_MIXER_NRDEVICES; i++) {
  530                 if (amt[i].bits) v |= 1 << i;
  531         }
  532         mix_setdevs(m, v);
  533 
  534         for (i = v = 0; i < SOUND_MIXER_NRDEVICES; i++) {
  535                 if (amt[i].iselect) v |= 1 << i;
  536         }
  537         mix_setrecdevs(m, v);
  538         return 0;
  539 }
  540 
  541 static int
  542 alsmix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
  543 {
  544         struct sc_info *sc = mix_getdevinfo(m);
  545         u_int32_t r, l, v, mask;
  546 
  547         /* Fill upper n bits in mask with 1's */
  548         mask = ((1 << amt[dev].bits) - 1) << (8 - amt[dev].bits);
  549 
  550         l = (left * mask / 100) & mask;
  551         v = als_mix_rd(sc, amt[dev].lreg) & ~mask;
  552         als_mix_wr(sc, amt[dev].lreg, l | v);
  553 
  554         if (amt[dev].rreg) {
  555                 r = (right * mask / 100) & mask;
  556                 v = als_mix_rd(sc, amt[dev].rreg) & ~mask;
  557                 als_mix_wr(sc, amt[dev].rreg, r | v);
  558         } else {
  559                 r = 0;
  560         }
  561 
  562         /* Zero gain does not mute channel from output, but this does. */
  563         v = als_mix_rd(sc, SB16_OMASK);
  564         if (l == 0 && r == 0) {
  565                 v &= ~amt[dev].oselect;
  566         } else {
  567                 v |= amt[dev].oselect;
  568         }
  569         als_mix_wr(sc, SB16_OMASK, v);
  570         return 0;
  571 }
  572 
  573 static int
  574 alsmix_setrecsrc(struct snd_mixer *m, u_int32_t src)
  575 {
  576         struct sc_info *sc = mix_getdevinfo(m);
  577         u_int32_t i, l, r;
  578 
  579         for (i = l = r = 0; i < SOUND_MIXER_NRDEVICES; i++) {
  580                 if (src & (1 << i)) {
  581                         l |= amt[i].iselect;
  582                         r |= amt[i].iselect << 1;
  583                 }
  584         }
  585 
  586         als_mix_wr(sc, SB16_IMASK_L, l);
  587         als_mix_wr(sc, SB16_IMASK_R, r);
  588         return src;
  589 }
  590 
  591 static kobj_method_t als_mixer_methods[] = {
  592         KOBJMETHOD(mixer_init,          alsmix_init),
  593         KOBJMETHOD(mixer_set,           alsmix_set),
  594         KOBJMETHOD(mixer_setrecsrc,     alsmix_setrecsrc),
  595         { 0, 0 }
  596 };
  597 MIXER_DECLARE(als_mixer);
  598 
  599 /* ------------------------------------------------------------------------- */
  600 /* Interrupt Handler */
  601 
  602 static void
  603 als_intr(void *p)
  604 {
  605         struct sc_info *sc = (struct sc_info *)p;
  606         u_int8_t intr, sb_status;
  607 
  608         intr = als_intr_rd(sc);
  609 
  610         if (intr & 0x80)
  611                 chn_intr(sc->pch.channel);
  612 
  613         if (intr & 0x40)
  614                 chn_intr(sc->rch.channel);
  615 
  616         /* ACK interrupt in PCI core */
  617         als_intr_wr(sc, intr);
  618 
  619         /* ACK interrupt in SB core */
  620         sb_status = als_mix_rd(sc, IRQ_STAT);
  621 
  622         if (sb_status & ALS_IRQ_STATUS8)
  623                 als_ack_read(sc, ALS_ESP_RD_STATUS8);
  624         if (sb_status & ALS_IRQ_STATUS16)
  625                 als_ack_read(sc, ALS_ESP_RD_STATUS16);
  626         if (sb_status & ALS_IRQ_MPUIN)
  627                 als_ack_read(sc, ALS_MIDI_DATA);
  628         if (sb_status & ALS_IRQ_CR1E)
  629                 als_ack_read(sc, ALS_CR1E_ACK_PORT);
  630         return;
  631 }
  632 
  633 /* ------------------------------------------------------------------------- */
  634 /* H/W initialization */
  635 
  636 static int
  637 als_init(struct sc_info *sc)
  638 {
  639         u_int32_t i, v;
  640 
  641         /* Reset Chip */
  642         if (als_esp_reset(sc)) {
  643                 return 1;
  644         }
  645 
  646         /* Enable write on DMA_SETUP register */
  647         v = als_mix_rd(sc, ALS_SB16_CONFIG);
  648         als_mix_wr(sc, ALS_SB16_CONFIG, v | 0x80);
  649 
  650         /* Select DMA0 */
  651         als_mix_wr(sc, ALS_SB16_DMA_SETUP, 0x01);
  652 
  653         /* Disable write on DMA_SETUP register */
  654         als_mix_wr(sc, ALS_SB16_CONFIG, v & 0x7f);
  655 
  656         /* Enable interrupts */
  657         v  = als_gcr_rd(sc, ALS_GCR_MISC);
  658         als_gcr_wr(sc, ALS_GCR_MISC, v | 0x28000);
  659 
  660         /* Black out GCR DMA registers */
  661         for (i = 0x91; i <= 0x96; i++) {
  662                 als_gcr_wr(sc, i, 0);
  663         }
  664 
  665         /* Emulation mode */
  666         v = als_gcr_rd(sc, ALS_GCR_DMA_EMULATION);
  667         als_gcr_wr(sc, ALS_GCR_DMA_EMULATION, v);
  668         DEB(printf("GCR_DMA_EMULATION 0x%08x\n", v));
  669         return 0;
  670 }
  671 
  672 static void
  673 als_uninit(struct sc_info *sc)
  674 {
  675         /* Disable interrupts */
  676         als_gcr_wr(sc, ALS_GCR_MISC, 0);
  677 }
  678 
  679 /* ------------------------------------------------------------------------- */
  680 /* Probe and attach card */
  681 
  682 static int
  683 als_pci_probe(device_t dev)
  684 {
  685         if (pci_get_devid(dev) == ALS_PCI_ID0) {
  686                 device_set_desc(dev, "Avance Logic ALS4000");
  687                 return 0;
  688         }
  689         return ENXIO;
  690 }
  691 
  692 static void
  693 als_resource_free(device_t dev, struct sc_info *sc)
  694 {
  695         if (sc->reg) {
  696                 bus_release_resource(dev, SYS_RES_IOPORT, sc->regid, sc->reg);
  697                 sc->reg = 0;
  698         }
  699         if (sc->ih) {
  700                 bus_teardown_intr(dev, sc->irq, sc->ih);
  701                 sc->ih = 0;
  702         }
  703         if (sc->irq) {
  704                 bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq);
  705                 sc->irq = 0;
  706         }
  707         if (sc->parent_dmat) {
  708                 bus_dma_tag_destroy(sc->parent_dmat);
  709                 sc->parent_dmat = 0;
  710         }
  711 }
  712 
  713 static int
  714 als_resource_grab(device_t dev, struct sc_info *sc)
  715 {
  716         sc->regid = PCIR_BAR(0);
  717         sc->reg = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->regid, 0, ~0,
  718                                      ALS_CONFIG_SPACE_BYTES, RF_ACTIVE);
  719         if (sc->reg == 0) {
  720                 device_printf(dev, "unable to allocate register space\n");
  721                 goto bad;
  722         }
  723         sc->st = rman_get_bustag(sc->reg);
  724         sc->sh = rman_get_bushandle(sc->reg);
  725 
  726         sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqid,
  727                                          RF_ACTIVE | RF_SHAREABLE);
  728         if (sc->irq == 0) {
  729                 device_printf(dev, "unable to allocate interrupt\n");
  730                 goto bad;
  731         }
  732 
  733         if (bus_setup_intr(dev, sc->irq, INTR_TYPE_AV, als_intr,
  734                            sc, &sc->ih)) {
  735                 device_printf(dev, "unable to setup interrupt\n");
  736                 goto bad;
  737         }
  738 
  739         sc->bufsz = pcm_getbuffersize(dev, 4096, ALS_DEFAULT_BUFSZ, 65536);
  740 
  741         if (bus_dma_tag_create(/*parent*/NULL,
  742                                /*alignment*/2, /*boundary*/0,
  743                                /*lowaddr*/BUS_SPACE_MAXADDR_24BIT,
  744                                /*highaddr*/BUS_SPACE_MAXADDR,
  745                                /*filter*/NULL, /*filterarg*/NULL,
  746                                /*maxsize*/sc->bufsz,
  747                                /*nsegments*/1, /*maxsegz*/0x3ffff,
  748                                /*flags*/0, /*lockfunc*/busdma_lock_mutex,
  749                                /*lockarg*/&Giant, &sc->parent_dmat) != 0) {
  750                 device_printf(dev, "unable to create dma tag\n");
  751                 goto bad;
  752         }
  753         return 0;
  754  bad:
  755         als_resource_free(dev, sc);
  756         return ENXIO;
  757 }
  758 
  759 static int
  760 als_pci_attach(device_t dev)
  761 {
  762         struct sc_info *sc;
  763         u_int32_t data;
  764         char status[SND_STATUSLEN];
  765 
  766         if ((sc = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) {
  767                 device_printf(dev, "cannot allocate softc\n");
  768                 return ENXIO;
  769         }
  770 
  771         sc->dev = dev;
  772 
  773         data = pci_read_config(dev, PCIR_COMMAND, 2);
  774         data |= (PCIM_CMD_PORTEN | PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN);
  775         pci_write_config(dev, PCIR_COMMAND, data, 2);
  776         /*
  777          * By default the power to the various components on the
  778          * ALS4000 is entirely controlled by the pci powerstate.  We
  779          * could attempt finer grained control by setting GCR6.31.
  780          */
  781 #if __FreeBSD_version > 500000
  782         if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) {
  783                 /* Reset the power state. */
  784                 device_printf(dev, "chip is in D%d power mode "
  785                               "-- setting to D0\n", pci_get_powerstate(dev));
  786                 pci_set_powerstate(dev, PCI_POWERSTATE_D0);
  787         }
  788 #else
  789         data = pci_read_config(dev, ALS_PCI_POWERREG, 2);
  790         if ((data & 0x03) != 0) {
  791                 device_printf(dev, "chip is in D%d power mode "
  792                               "-- setting to D0\n", data & 0x03);
  793                 data &= ~0x03;
  794                 pci_write_config(dev, ALS_PCI_POWERREG, data, 2);
  795         }
  796 #endif
  797 
  798         if (als_resource_grab(dev, sc)) {
  799                 device_printf(dev, "failed to allocate resources\n");
  800                 goto bad_attach;
  801         }
  802 
  803         if (als_init(sc)) {
  804                 device_printf(dev, "failed to initialize hardware\n");
  805                 goto bad_attach;
  806         }
  807 
  808         if (mixer_init(dev, &als_mixer_class, sc)) {
  809                 device_printf(dev, "failed to initialize mixer\n");
  810                 goto bad_attach;
  811         }
  812 
  813         if (pcm_register(dev, sc, 1, 1)) {
  814                 device_printf(dev, "failed to register pcm entries\n");
  815                 goto bad_attach;
  816         }
  817 
  818         pcm_addchan(dev, PCMDIR_PLAY, &alspchan_class, sc);
  819         pcm_addchan(dev, PCMDIR_REC,  &alsrchan_class, sc);
  820 
  821         snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld %s",
  822                  rman_get_start(sc->reg), rman_get_start(sc->irq),PCM_KLDSTRING(snd_als4000));
  823         pcm_setstatus(dev, status);
  824         return 0;
  825 
  826  bad_attach:
  827         als_resource_free(dev, sc);
  828         free(sc, M_DEVBUF);
  829         return ENXIO;
  830 }
  831 
  832 static int
  833 als_pci_detach(device_t dev)
  834 {
  835         struct sc_info *sc;
  836         int r;
  837 
  838         r = pcm_unregister(dev);
  839         if (r)
  840                 return r;
  841 
  842         sc = pcm_getdevinfo(dev);
  843         als_uninit(sc);
  844         als_resource_free(dev, sc);
  845         free(sc, M_DEVBUF);
  846         return 0;
  847 }
  848 
  849 static int
  850 als_pci_suspend(device_t dev)
  851 {
  852         struct sc_info *sc = pcm_getdevinfo(dev);
  853 
  854         sc->pch.dma_was_active = als_playback_stop(&sc->pch);
  855         sc->rch.dma_was_active = als_capture_stop(&sc->rch);
  856         als_uninit(sc);
  857         return 0;
  858 }
  859 
  860 static int
  861 als_pci_resume(device_t dev)
  862 {
  863         struct sc_info *sc = pcm_getdevinfo(dev);
  864 
  865         if (als_init(sc) != 0) {
  866                 device_printf(dev, "unable to reinitialize the card\n");
  867                 return ENXIO;
  868         }
  869 
  870         if (mixer_reinit(dev) != 0) {
  871                 device_printf(dev, "unable to reinitialize the mixer\n");
  872                 return ENXIO;
  873         }
  874 
  875         if (sc->pch.dma_was_active) {
  876                 als_playback_start(&sc->pch);
  877         }
  878 
  879         if (sc->rch.dma_was_active) {
  880                 als_capture_start(&sc->rch);
  881         }
  882         return 0;
  883 }
  884 
  885 static device_method_t als_methods[] = {
  886         /* Device interface */
  887         DEVMETHOD(device_probe,         als_pci_probe),
  888         DEVMETHOD(device_attach,        als_pci_attach),
  889         DEVMETHOD(device_detach,        als_pci_detach),
  890         DEVMETHOD(device_suspend,       als_pci_suspend),
  891         DEVMETHOD(device_resume,        als_pci_resume),
  892         { 0, 0 }
  893 };
  894 
  895 static driver_t als_driver = {
  896         "pcm",
  897         als_methods,
  898         PCM_SOFTC_SIZE,
  899 };
  900 
  901 DRIVER_MODULE(snd_als4000, pci, als_driver, pcm_devclass, 0, 0);
  902 MODULE_DEPEND(snd_als4000, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
  903 MODULE_VERSION(snd_als4000, 1);

Cache object: 78e40a7f35287b038fcc1a8b1213a0c2


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