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/hdspe-pcm.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) 2012-2021 Ruslan Bukin <br@bsdpad.com>
    5  * All rights reserved.
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions
    9  * are met:
   10  * 1. Redistributions of source code must retain the above copyright
   11  *    notice, this list of conditions and the following disclaimer.
   12  * 2. Redistributions in binary form must reproduce the above copyright
   13  *    notice, this list of conditions and the following disclaimer in the
   14  *    documentation and/or other materials provided with the distribution.
   15  *
   16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   26  * SUCH DAMAGE.
   27  */
   28 
   29 /*
   30  * RME HDSPe driver for FreeBSD (pcm-part).
   31  * Supported cards: AIO, RayDAT.
   32  */
   33 
   34 #include <dev/sound/pcm/sound.h>
   35 #include <dev/sound/pci/hdspe.h>
   36 #include <dev/sound/chip.h>
   37 
   38 #include <dev/pci/pcireg.h>
   39 #include <dev/pci/pcivar.h>
   40 
   41 #include <mixer_if.h>
   42 
   43 SND_DECLARE_FILE("$FreeBSD$");
   44 
   45 struct hdspe_latency {
   46         uint32_t n;
   47         uint32_t period;
   48         float ms;
   49 };
   50 
   51 static struct hdspe_latency latency_map[] = {
   52         { 7,   32, 0.7 },
   53         { 0,   64, 1.5 },
   54         { 1,  128,   3 },
   55         { 2,  256,   6 },
   56         { 3,  512,  12 },
   57         { 4, 1024,  23 },
   58         { 5, 2048,  46 },
   59         { 6, 4096,  93 },
   60 
   61         { 0,    0,   0 },
   62 };
   63 
   64 struct hdspe_rate {
   65         uint32_t speed;
   66         uint32_t reg;
   67 };
   68 
   69 static struct hdspe_rate rate_map[] = {
   70         {  32000, (HDSPE_FREQ_32000) },
   71         {  44100, (HDSPE_FREQ_44100) },
   72         {  48000, (HDSPE_FREQ_48000) },
   73         {  64000, (HDSPE_FREQ_32000 | HDSPE_FREQ_DOUBLE) },
   74         {  88200, (HDSPE_FREQ_44100 | HDSPE_FREQ_DOUBLE) },
   75         {  96000, (HDSPE_FREQ_48000 | HDSPE_FREQ_DOUBLE) },
   76         { 128000, (HDSPE_FREQ_32000 | HDSPE_FREQ_QUAD)   },
   77         { 176400, (HDSPE_FREQ_44100 | HDSPE_FREQ_QUAD)   },
   78         { 192000, (HDSPE_FREQ_48000 | HDSPE_FREQ_QUAD)   },
   79 
   80         { 0, 0 },
   81 };
   82 
   83 static int
   84 hdspe_hw_mixer(struct sc_chinfo *ch, unsigned int dst,
   85     unsigned int src, unsigned short data)
   86 {
   87         struct sc_pcminfo *scp;
   88         struct sc_info *sc;
   89         int offs;
   90 
   91         scp = ch->parent;
   92         sc = scp->sc;
   93 
   94         offs = 0;
   95         if (ch->dir == PCMDIR_PLAY)
   96                 offs = 64;
   97 
   98         hdspe_write_4(sc, HDSPE_MIXER_BASE +
   99             ((offs + src + 128 * dst) * sizeof(uint32_t)),
  100             data & 0xFFFF);
  101 
  102         return (0);
  103 };
  104 
  105 static int
  106 hdspechan_setgain(struct sc_chinfo *ch)
  107 {
  108 
  109         hdspe_hw_mixer(ch, ch->lslot, ch->lslot,
  110             ch->lvol * HDSPE_MAX_GAIN / 100);
  111         hdspe_hw_mixer(ch, ch->rslot, ch->rslot,
  112             ch->rvol * HDSPE_MAX_GAIN / 100);
  113 
  114         return (0);
  115 }
  116 
  117 static int
  118 hdspemixer_init(struct snd_mixer *m)
  119 {
  120         struct sc_pcminfo *scp;
  121         struct sc_info *sc;
  122         int mask;
  123 
  124         scp = mix_getdevinfo(m);
  125         sc = scp->sc;
  126         if (sc == NULL)
  127                 return (-1);
  128 
  129         mask = SOUND_MASK_PCM;
  130 
  131         if (scp->hc->play)
  132                 mask |= SOUND_MASK_VOLUME;
  133 
  134         if (scp->hc->rec)
  135                 mask |= SOUND_MASK_RECLEV;
  136 
  137         snd_mtxlock(sc->lock);
  138         pcm_setflags(scp->dev, pcm_getflags(scp->dev) | SD_F_SOFTPCMVOL);
  139         mix_setdevs(m, mask);
  140         snd_mtxunlock(sc->lock);
  141 
  142         return (0);
  143 }
  144 
  145 static int
  146 hdspemixer_set(struct snd_mixer *m, unsigned dev,
  147     unsigned left, unsigned right)
  148 {
  149         struct sc_pcminfo *scp;
  150         struct sc_chinfo *ch;
  151         int i;
  152 
  153         scp = mix_getdevinfo(m);
  154 
  155 #if 0
  156         device_printf(scp->dev, "hdspemixer_set() %d %d\n",
  157             left, right);
  158 #endif
  159 
  160         for (i = 0; i < scp->chnum; i++) {
  161                 ch = &scp->chan[i];
  162                 if ((dev == SOUND_MIXER_VOLUME && ch->dir == PCMDIR_PLAY) ||
  163                     (dev == SOUND_MIXER_RECLEV && ch->dir == PCMDIR_REC)) {
  164                         ch->lvol = left;
  165                         ch->rvol = right;
  166                         if (ch->run)
  167                                 hdspechan_setgain(ch);
  168                 }
  169         }
  170 
  171         return (0);
  172 }
  173 
  174 static kobj_method_t hdspemixer_methods[] = {
  175         KOBJMETHOD(mixer_init,      hdspemixer_init),
  176         KOBJMETHOD(mixer_set,       hdspemixer_set),
  177         KOBJMETHOD_END
  178 };
  179 MIXER_DECLARE(hdspemixer);
  180 
  181 static void
  182 hdspechan_enable(struct sc_chinfo *ch, int value)
  183 {
  184         struct sc_pcminfo *scp;
  185         struct sc_info *sc;
  186         int reg;
  187 
  188         scp = ch->parent;
  189         sc = scp->sc;
  190 
  191         if (ch->dir == PCMDIR_PLAY)
  192                 reg = HDSPE_OUT_ENABLE_BASE;
  193         else
  194                 reg = HDSPE_IN_ENABLE_BASE;
  195 
  196         ch->run = value;
  197 
  198         hdspe_write_1(sc, reg + (4 * ch->lslot), value);
  199         if (AFMT_CHANNEL(ch->format) == 2)
  200                 hdspe_write_1(sc, reg + (4 * ch->rslot), value);
  201 }
  202 
  203 static int
  204 hdspe_running(struct sc_info *sc)
  205 {
  206         struct sc_pcminfo *scp;
  207         struct sc_chinfo *ch;
  208         device_t *devlist;
  209         int devcount;
  210         int i, j;
  211         int err;
  212 
  213         if ((err = device_get_children(sc->dev, &devlist, &devcount)) != 0)
  214                 goto bad;
  215 
  216         for (i = 0; i < devcount; i++) {
  217                 scp = device_get_ivars(devlist[i]);
  218                 for (j = 0; j < scp->chnum; j++) {
  219                         ch = &scp->chan[j];
  220                         if (ch->run)
  221                                 goto bad;
  222                 }
  223         }
  224 
  225         free(devlist, M_TEMP);
  226 
  227         return (0);
  228 bad:
  229 
  230 #if 0
  231         device_printf(sc->dev, "hdspe is running\n");
  232 #endif
  233 
  234         free(devlist, M_TEMP);
  235 
  236         return (1);
  237 }
  238 
  239 static void
  240 hdspe_start_audio(struct sc_info *sc)
  241 {
  242 
  243         sc->ctrl_register |= (HDSPE_AUDIO_INT_ENABLE | HDSPE_ENABLE);
  244         hdspe_write_4(sc, HDSPE_CONTROL_REG, sc->ctrl_register);
  245 }
  246 
  247 static void
  248 hdspe_stop_audio(struct sc_info *sc)
  249 {
  250 
  251         if (hdspe_running(sc) == 1)
  252                 return;
  253 
  254         sc->ctrl_register &= ~(HDSPE_AUDIO_INT_ENABLE | HDSPE_ENABLE);
  255         hdspe_write_4(sc, HDSPE_CONTROL_REG, sc->ctrl_register);
  256 }
  257 
  258 /* Multiplex / demultiplex: 2.0 <-> 2 x 1.0. */
  259 static void
  260 buffer_copy(struct sc_chinfo *ch)
  261 {
  262         struct sc_pcminfo *scp;
  263         struct sc_info *sc;
  264         int ssize, dsize;
  265         int src, dst;
  266         int n;
  267         int i;
  268 
  269         scp = ch->parent;
  270         sc = scp->sc;
  271 
  272         n = AFMT_CHANNEL(ch->format); /* n channels */
  273 
  274         if (ch->dir == PCMDIR_PLAY) {
  275                 src = sndbuf_getreadyptr(ch->buffer);
  276         } else {
  277                 src = sndbuf_getfreeptr(ch->buffer);
  278         }
  279 
  280         src /= 4; /* Bytes per sample. */
  281         dst = src / n; /* Destination buffer n-times smaller. */
  282 
  283         ssize = ch->size / 4;
  284         dsize = ch->size / (4 * n);
  285 
  286         /*
  287          * Use two fragment buffer to avoid sound clipping.
  288          */
  289 
  290         for (i = 0; i < sc->period * 2 /* fragments */; i++) {
  291                 if (ch->dir == PCMDIR_PLAY) {
  292                         sc->pbuf[dst + HDSPE_CHANBUF_SAMPLES * ch->lslot] =
  293                             ch->data[src];
  294                         sc->pbuf[dst + HDSPE_CHANBUF_SAMPLES * ch->rslot] =
  295                             ch->data[src + 1];
  296 
  297                 } else {
  298                         ch->data[src] =
  299                             sc->rbuf[dst + HDSPE_CHANBUF_SAMPLES * ch->lslot];
  300                         ch->data[src+1] =
  301                             sc->rbuf[dst + HDSPE_CHANBUF_SAMPLES * ch->rslot];
  302                 }
  303 
  304                 dst+=1;
  305                 dst %= dsize;
  306                 src += n;
  307                 src %= ssize;
  308         }
  309 }
  310 
  311 static int
  312 clean(struct sc_chinfo *ch)
  313 {
  314         struct sc_pcminfo *scp;
  315         struct sc_info *sc;
  316         uint32_t *buf;
  317 
  318         scp = ch->parent;
  319         sc = scp->sc;
  320         buf = sc->rbuf;
  321 
  322         if (ch->dir == PCMDIR_PLAY) {
  323                 buf = sc->pbuf;
  324         }
  325 
  326         bzero(buf + HDSPE_CHANBUF_SAMPLES * ch->lslot, HDSPE_CHANBUF_SIZE);
  327         bzero(buf + HDSPE_CHANBUF_SAMPLES * ch->rslot, HDSPE_CHANBUF_SIZE);
  328 
  329         return (0);
  330 }
  331 
  332 /* Channel interface. */
  333 static void *
  334 hdspechan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
  335     struct pcm_channel *c, int dir)
  336 {
  337         struct sc_pcminfo *scp;
  338         struct sc_chinfo *ch;
  339         struct sc_info *sc;
  340         int num;
  341 
  342         scp = devinfo;
  343         sc = scp->sc;
  344 
  345         snd_mtxlock(sc->lock);
  346         num = scp->chnum;
  347 
  348         ch = &scp->chan[num];
  349         ch->lslot = scp->hc->left;
  350         ch->rslot = scp->hc->right;
  351         ch->run = 0;
  352         ch->lvol = 0;
  353         ch->rvol = 0;
  354 
  355         ch->size = HDSPE_CHANBUF_SIZE * 2; /* max size */
  356         ch->data = malloc(ch->size, M_HDSPE, M_NOWAIT);
  357 
  358         ch->buffer = b;
  359         ch->channel = c;
  360         ch->parent = scp;
  361 
  362         ch->dir = dir;
  363 
  364         snd_mtxunlock(sc->lock);
  365 
  366         if (sndbuf_setup(ch->buffer, ch->data, ch->size) != 0) {
  367                 device_printf(scp->dev, "Can't setup sndbuf.\n");
  368                 return (NULL);
  369         }
  370 
  371         return (ch);
  372 }
  373 
  374 static int
  375 hdspechan_trigger(kobj_t obj, void *data, int go)
  376 {
  377         struct sc_pcminfo *scp;
  378         struct sc_chinfo *ch;
  379         struct sc_info *sc;
  380 
  381         ch = data;
  382         scp = ch->parent;
  383         sc = scp->sc;
  384 
  385         snd_mtxlock(sc->lock);
  386         switch (go) {
  387         case PCMTRIG_START:
  388 #if 0
  389                 device_printf(scp->dev, "hdspechan_trigger(): start\n");
  390 #endif
  391                 hdspechan_enable(ch, 1);
  392                 hdspechan_setgain(ch);
  393                 hdspe_start_audio(sc);
  394                 break;
  395 
  396         case PCMTRIG_STOP:
  397         case PCMTRIG_ABORT:
  398 #if 0
  399                 device_printf(scp->dev, "hdspechan_trigger(): stop or abort\n");
  400 #endif
  401                 clean(ch);
  402                 hdspechan_enable(ch, 0);
  403                 hdspe_stop_audio(sc);
  404                 break;
  405 
  406         case PCMTRIG_EMLDMAWR:
  407         case PCMTRIG_EMLDMARD:
  408                 if(ch->run)
  409                         buffer_copy(ch);
  410                 break;
  411         }
  412 
  413         snd_mtxunlock(sc->lock);
  414 
  415         return (0);
  416 }
  417 
  418 static uint32_t
  419 hdspechan_getptr(kobj_t obj, void *data)
  420 {
  421         struct sc_pcminfo *scp;
  422         struct sc_chinfo *ch;
  423         struct sc_info *sc;
  424         uint32_t ret, pos;
  425 
  426         ch = data;
  427         scp = ch->parent;
  428         sc = scp->sc;
  429 
  430         snd_mtxlock(sc->lock);
  431         ret = hdspe_read_2(sc, HDSPE_STATUS_REG);
  432         snd_mtxunlock(sc->lock);
  433 
  434         pos = ret & HDSPE_BUF_POSITION_MASK;
  435         if (AFMT_CHANNEL(ch->format) == 2)
  436                 pos *= 2; /* Hardbuf twice bigger. */
  437 
  438         return (pos);
  439 }
  440 
  441 static int
  442 hdspechan_free(kobj_t obj, void *data)
  443 {
  444         struct sc_pcminfo *scp;
  445         struct sc_chinfo *ch;
  446         struct sc_info *sc;
  447 
  448         ch = data;
  449         scp = ch->parent;
  450         sc = scp->sc;
  451 
  452 #if 0
  453         device_printf(scp->dev, "hdspechan_free()\n");
  454 #endif
  455 
  456         snd_mtxlock(sc->lock);
  457         if (ch->data != NULL) {
  458                 free(ch->data, M_HDSPE);
  459                 ch->data = NULL;
  460         }
  461         snd_mtxunlock(sc->lock);
  462 
  463         return (0);
  464 }
  465 
  466 static int
  467 hdspechan_setformat(kobj_t obj, void *data, uint32_t format)
  468 {
  469         struct sc_chinfo *ch;
  470 
  471         ch = data;
  472 
  473 #if 0
  474         struct sc_pcminfo *scp = ch->parent;
  475         device_printf(scp->dev, "hdspechan_setformat(%d)\n", format);
  476 #endif
  477 
  478         ch->format = format;
  479 
  480         return (0);
  481 }
  482 
  483 static uint32_t
  484 hdspechan_setspeed(kobj_t obj, void *data, uint32_t speed)
  485 {
  486         struct sc_pcminfo *scp;
  487         struct hdspe_rate *hr;
  488         struct sc_chinfo *ch;
  489         struct sc_info *sc;
  490         long long period;
  491         int threshold;
  492         int i;
  493 
  494         ch = data;
  495         scp = ch->parent;
  496         sc = scp->sc;
  497         hr = NULL;
  498 
  499 #if 0
  500         device_printf(scp->dev, "hdspechan_setspeed(%d)\n", speed);
  501 #endif
  502 
  503         if (hdspe_running(sc) == 1)
  504                 goto end;
  505 
  506         /* First look for equal frequency. */
  507         for (i = 0; rate_map[i].speed != 0; i++) {
  508                 if (rate_map[i].speed == speed)
  509                         hr = &rate_map[i];
  510         }
  511 
  512         /* If no match, just find nearest. */
  513         if (hr == NULL) {
  514                 for (i = 0; rate_map[i].speed != 0; i++) {
  515                         hr = &rate_map[i];
  516                         threshold = hr->speed + ((rate_map[i + 1].speed != 0) ?
  517                             ((rate_map[i + 1].speed - hr->speed) >> 1) : 0);
  518                         if (speed < threshold)
  519                                 break;
  520                 }
  521         }
  522 
  523         switch (sc->type) {
  524         case RAYDAT:
  525         case AIO:
  526                 period = HDSPE_FREQ_AIO;
  527                 break;
  528         default:
  529                 /* Unsupported card. */
  530                 goto end;
  531         }
  532 
  533         /* Write frequency on the device. */
  534         sc->ctrl_register &= ~HDSPE_FREQ_MASK;
  535         sc->ctrl_register |= hr->reg;
  536         hdspe_write_4(sc, HDSPE_CONTROL_REG, sc->ctrl_register);
  537 
  538         speed = hr->speed;
  539         if (speed > 96000)
  540                 speed /= 4;
  541         else if (speed > 48000)
  542                 speed /= 2;
  543 
  544         /* Set DDS value. */
  545         period /= speed;
  546         hdspe_write_4(sc, HDSPE_FREQ_REG, period);
  547 
  548         sc->speed = hr->speed;
  549 end:
  550 
  551         return (sc->speed);
  552 }
  553 
  554 static uint32_t
  555 hdspechan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
  556 {
  557         struct hdspe_latency *hl;
  558         struct sc_pcminfo *scp;
  559         struct sc_chinfo *ch;
  560         struct sc_info *sc;
  561         int threshold;
  562         int i;
  563 
  564         ch = data;
  565         scp = ch->parent;
  566         sc = scp->sc;
  567         hl = NULL;
  568 
  569 #if 0
  570         device_printf(scp->dev, "hdspechan_setblocksize(%d)\n", blocksize);
  571 #endif
  572 
  573         if (hdspe_running(sc) == 1)
  574                 goto end;
  575 
  576         if (blocksize > HDSPE_LAT_BYTES_MAX)
  577                 blocksize = HDSPE_LAT_BYTES_MAX;
  578         else if (blocksize < HDSPE_LAT_BYTES_MIN)
  579                 blocksize = HDSPE_LAT_BYTES_MIN;
  580 
  581         blocksize /= 4 /* samples */;
  582 
  583         /* First look for equal latency. */
  584         for (i = 0; latency_map[i].period != 0; i++) {
  585                 if (latency_map[i].period == blocksize) {
  586                         hl = &latency_map[i];
  587                 }
  588         }
  589 
  590         /* If no match, just find nearest. */
  591         if (hl == NULL) {
  592                 for (i = 0; latency_map[i].period != 0; i++) {
  593                         hl = &latency_map[i];
  594                         threshold = hl->period + ((latency_map[i + 1].period != 0) ?
  595                             ((latency_map[i + 1].period - hl->period) >> 1) : 0);
  596                         if (blocksize < threshold)
  597                                 break;
  598                 }
  599         }
  600 
  601         snd_mtxlock(sc->lock);
  602         sc->ctrl_register &= ~HDSPE_LAT_MASK;
  603         sc->ctrl_register |= hdspe_encode_latency(hl->n);
  604         hdspe_write_4(sc, HDSPE_CONTROL_REG, sc->ctrl_register);
  605         sc->period = hl->period;
  606         snd_mtxunlock(sc->lock);
  607 
  608 #if 0
  609         device_printf(scp->dev, "New period=%d\n", sc->period);
  610 #endif
  611 
  612         sndbuf_resize(ch->buffer,
  613             (HDSPE_CHANBUF_SIZE * AFMT_CHANNEL(ch->format)) / (sc->period * 4),
  614             (sc->period * 4));
  615 end:
  616 
  617         return (sndbuf_getblksz(ch->buffer));
  618 }
  619 
  620 static uint32_t hdspe_rfmt[] = {
  621         SND_FORMAT(AFMT_S32_LE, 2, 0),
  622         0
  623 };
  624 
  625 static struct pcmchan_caps hdspe_rcaps = {32000, 192000, hdspe_rfmt, 0};
  626 
  627 static uint32_t hdspe_pfmt[] = {
  628         SND_FORMAT(AFMT_S32_LE, 1, 0),
  629         SND_FORMAT(AFMT_S32_LE, 2, 0),
  630         0
  631 };
  632 
  633 static struct pcmchan_caps hdspe_pcaps = {32000, 192000, hdspe_pfmt, 0};
  634 
  635 static struct pcmchan_caps *
  636 hdspechan_getcaps(kobj_t obj, void *data)
  637 {
  638         struct sc_chinfo *ch;
  639 
  640         ch = data;
  641 
  642 #if 0
  643         struct sc_pcminfo *scl = ch->parent;
  644         device_printf(scp->dev, "hdspechan_getcaps()\n");
  645 #endif
  646 
  647         return ((ch->dir == PCMDIR_PLAY) ?
  648             &hdspe_pcaps : &hdspe_rcaps);
  649 }
  650 
  651 static kobj_method_t hdspechan_methods[] = {
  652         KOBJMETHOD(channel_init,         hdspechan_init),
  653         KOBJMETHOD(channel_free,         hdspechan_free),
  654         KOBJMETHOD(channel_setformat,    hdspechan_setformat),
  655         KOBJMETHOD(channel_setspeed,     hdspechan_setspeed),
  656         KOBJMETHOD(channel_setblocksize, hdspechan_setblocksize),
  657         KOBJMETHOD(channel_trigger,      hdspechan_trigger),
  658         KOBJMETHOD(channel_getptr,       hdspechan_getptr),
  659         KOBJMETHOD(channel_getcaps,      hdspechan_getcaps),
  660         KOBJMETHOD_END
  661 };
  662 CHANNEL_DECLARE(hdspechan);
  663 
  664 static int
  665 hdspe_pcm_probe(device_t dev)
  666 {
  667 
  668 #if 0
  669         device_printf(dev,"hdspe_pcm_probe()\n");
  670 #endif
  671 
  672         return (0);
  673 }
  674 
  675 static uint32_t
  676 hdspe_pcm_intr(struct sc_pcminfo *scp)
  677 {
  678         struct sc_chinfo *ch;
  679         struct sc_info *sc;
  680         int i;
  681 
  682         sc = scp->sc;
  683 
  684         for (i = 0; i < scp->chnum; i++) {
  685                 ch = &scp->chan[i];
  686                 snd_mtxunlock(sc->lock);
  687                 chn_intr(ch->channel);
  688                 snd_mtxlock(sc->lock);
  689         }
  690 
  691         return (0);
  692 }
  693 
  694 static int
  695 hdspe_pcm_attach(device_t dev)
  696 {
  697         char status[SND_STATUSLEN];
  698         struct sc_pcminfo *scp;
  699         char desc[64];
  700         int i, err;
  701 
  702         scp = device_get_ivars(dev);
  703         scp->ih = &hdspe_pcm_intr;
  704 
  705         bzero(desc, sizeof(desc));
  706         snprintf(desc, sizeof(desc), "HDSPe AIO [%s]", scp->hc->descr);
  707         device_set_desc_copy(dev, desc);
  708 
  709         /*
  710          * We don't register interrupt handler with snd_setup_intr
  711          * in pcm device. Mark pcm device as MPSAFE manually.
  712          */
  713         pcm_setflags(dev, pcm_getflags(dev) | SD_F_MPSAFE);
  714 
  715         err = pcm_register(dev, scp, scp->hc->play, scp->hc->rec);
  716         if (err) {
  717                 device_printf(dev, "Can't register pcm.\n");
  718                 return (ENXIO);
  719         }
  720 
  721         scp->chnum = 0;
  722         for (i = 0; i < scp->hc->play; i++) {
  723                 pcm_addchan(dev, PCMDIR_PLAY, &hdspechan_class, scp);
  724                 scp->chnum++;
  725         }
  726 
  727         for (i = 0; i < scp->hc->rec; i++) {
  728                 pcm_addchan(dev, PCMDIR_REC, &hdspechan_class, scp);
  729                 scp->chnum++;
  730         }
  731 
  732         snprintf(status, SND_STATUSLEN, "at io 0x%jx irq %jd %s",
  733             rman_get_start(scp->sc->cs),
  734             rman_get_start(scp->sc->irq),
  735             PCM_KLDSTRING(snd_hdspe));
  736         pcm_setstatus(dev, status);
  737 
  738         mixer_init(dev, &hdspemixer_class, scp);
  739 
  740         return (0);
  741 }
  742 
  743 static int
  744 hdspe_pcm_detach(device_t dev)
  745 {
  746         int err;
  747 
  748         err = pcm_unregister(dev);
  749         if (err) {
  750                 device_printf(dev, "Can't unregister device.\n");
  751                 return (err);
  752         }
  753 
  754         return (0);
  755 }
  756 
  757 static device_method_t hdspe_pcm_methods[] = {
  758         DEVMETHOD(device_probe,     hdspe_pcm_probe),
  759         DEVMETHOD(device_attach,    hdspe_pcm_attach),
  760         DEVMETHOD(device_detach,    hdspe_pcm_detach),
  761         { 0, 0 }
  762 };
  763 
  764 static driver_t hdspe_pcm_driver = {
  765         "pcm",
  766         hdspe_pcm_methods,
  767         PCM_SOFTC_SIZE,
  768 };
  769 
  770 DRIVER_MODULE(snd_hdspe_pcm, hdspe, hdspe_pcm_driver, 0, 0);
  771 MODULE_DEPEND(snd_hdspe, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
  772 MODULE_VERSION(snd_hdspe, 1);

Cache object: 83cb1303354b45e3cdbb808f037e49a2


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