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/fm801.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) 2000 Dmitry Dicky diwil@dataart.com
    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, WHETHER IN 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 THE POSSIBILITY OF
   24  * SUCH DAMAGE.
   25  */
   26 
   27 #include <dev/sound/pcm/sound.h>
   28 #include <dev/sound/pcm/ac97.h>
   29 #include <dev/pci/pcireg.h>
   30 #include <dev/pci/pcivar.h>
   31 
   32 SND_DECLARE_FILE("$FreeBSD$");
   33 
   34 #define PCI_VENDOR_FORTEMEDIA   0x1319
   35 #define PCI_DEVICE_FORTEMEDIA1  0x08011319
   36 #define PCI_DEVICE_FORTEMEDIA2  0x08021319      /* ??? have no idea what's this... */
   37 
   38 #define FM_PCM_VOLUME           0x00
   39 #define FM_FM_VOLUME            0x02
   40 #define FM_I2S_VOLUME           0x04
   41 #define FM_RECORD_SOURCE        0x06
   42 
   43 #define FM_PLAY_CTL             0x08
   44 #define  FM_PLAY_RATE_MASK              0x0f00
   45 #define  FM_PLAY_BUF1_LAST              0x0001
   46 #define  FM_PLAY_BUF2_LAST              0x0002
   47 #define  FM_PLAY_START                  0x0020
   48 #define  FM_PLAY_PAUSE                  0x0040
   49 #define  FM_PLAY_STOPNOW                0x0080
   50 #define  FM_PLAY_16BIT                  0x4000
   51 #define  FM_PLAY_STEREO                 0x8000
   52 
   53 #define FM_PLAY_DMALEN          0x0a
   54 #define FM_PLAY_DMABUF1         0x0c
   55 #define FM_PLAY_DMABUF2         0x10
   56 
   57 
   58 #define FM_REC_CTL              0x14
   59 #define  FM_REC_RATE_MASK               0x0f00
   60 #define  FM_REC_BUF1_LAST               0x0001
   61 #define  FM_REC_BUF2_LAST               0x0002
   62 #define  FM_REC_START                   0x0020
   63 #define  FM_REC_PAUSE                   0x0040
   64 #define  FM_REC_STOPNOW                 0x0080
   65 #define  FM_REC_16BIT                   0x4000
   66 #define  FM_REC_STEREO                  0x8000
   67 
   68 
   69 #define FM_REC_DMALEN           0x16
   70 #define FM_REC_DMABUF1          0x18
   71 #define FM_REC_DMABUF2          0x1c
   72 
   73 #define FM_CODEC_CTL            0x22
   74 #define FM_VOLUME               0x26
   75 #define  FM_VOLUME_MUTE                 0x8000
   76 
   77 #define FM_CODEC_CMD            0x2a
   78 #define  FM_CODEC_CMD_READ              0x0080
   79 #define  FM_CODEC_CMD_VALID             0x0100
   80 #define  FM_CODEC_CMD_BUSY              0x0200
   81 
   82 #define FM_CODEC_DATA           0x2c
   83 
   84 #define FM_IO_CTL               0x52
   85 #define FM_CARD_CTL             0x54
   86 
   87 #define FM_INTMASK              0x56
   88 #define  FM_INTMASK_PLAY                0x0001
   89 #define  FM_INTMASK_REC                 0x0002
   90 #define  FM_INTMASK_VOL                 0x0040
   91 #define  FM_INTMASK_MPU                 0x0080
   92 
   93 #define FM_INTSTATUS            0x5a
   94 #define  FM_INTSTATUS_PLAY              0x0100
   95 #define  FM_INTSTATUS_REC               0x0200
   96 #define  FM_INTSTATUS_VOL               0x4000
   97 #define  FM_INTSTATUS_MPU               0x8000
   98 
   99 #define FM801_DEFAULT_BUFSZ     4096    /* Other values do not work!!! */
  100 
  101 /* debug purposes */
  102 #define DPRINT   if(0) printf
  103 
  104 /*
  105 static int fm801ch_setup(struct pcm_channel *c);
  106 */
  107 
  108 static u_int32_t fmts[] = {
  109         AFMT_U8,
  110         AFMT_STEREO | AFMT_U8,
  111         AFMT_S16_LE,
  112         AFMT_STEREO | AFMT_S16_LE,
  113         0
  114 };
  115 
  116 static struct pcmchan_caps fm801ch_caps = {
  117         4000, 48000,
  118         fmts, 0
  119 };
  120 
  121 struct fm801_info;
  122 
  123 struct fm801_chinfo {
  124         struct fm801_info       *parent;
  125         struct pcm_channel      *channel;
  126         struct snd_dbuf         *buffer;
  127         u_int32_t               spd, dir, fmt;  /* speed, direction, format */
  128         u_int32_t               shift;
  129 };
  130 
  131 struct fm801_info {
  132         int                     type;
  133         bus_space_tag_t         st;
  134         bus_space_handle_t      sh;
  135         bus_dma_tag_t           parent_dmat;
  136 
  137         device_t                dev;
  138         int                     num;
  139         u_int32_t               unit;
  140 
  141         struct resource         *reg, *irq;
  142         int                     regtype, regid, irqid;
  143         void                    *ih;
  144 
  145         u_int32_t               play_flip,
  146                                 play_nextblk,
  147                                 play_start,
  148                                 play_blksize,
  149                                 play_fmt,
  150                                 play_shift,
  151                                 play_size;
  152 
  153         u_int32_t               rec_flip,
  154                                 rec_nextblk,
  155                                 rec_start,
  156                                 rec_blksize,
  157                                 rec_fmt,
  158                                 rec_shift,
  159                                 rec_size;
  160 
  161         unsigned int            bufsz;
  162 
  163         struct fm801_chinfo     pch, rch;
  164 
  165         device_t                radio;
  166 };
  167 
  168 /* Bus Read / Write routines */
  169 static u_int32_t
  170 fm801_rd(struct fm801_info *fm801, int regno, int size)
  171 {
  172         switch(size) {
  173         case 1:
  174                 return (bus_space_read_1(fm801->st, fm801->sh, regno));
  175         case 2:
  176                 return (bus_space_read_2(fm801->st, fm801->sh, regno));
  177         case 4:
  178                 return (bus_space_read_4(fm801->st, fm801->sh, regno));
  179         default:
  180                 return 0xffffffff;
  181         }
  182 }
  183 
  184 static void
  185 fm801_wr(struct fm801_info *fm801, int regno, u_int32_t data, int size)
  186 {
  187 
  188         switch(size) {
  189         case 1:
  190                 bus_space_write_1(fm801->st, fm801->sh, regno, data);
  191                 break;
  192         case 2:
  193                 bus_space_write_2(fm801->st, fm801->sh, regno, data);
  194                 break;
  195         case 4:
  196                 bus_space_write_4(fm801->st, fm801->sh, regno, data);
  197                 break;
  198         }
  199 }
  200 
  201 /* -------------------------------------------------------------------- */
  202 /*
  203  *  ac97 codec routines
  204  */
  205 #define TIMO 50
  206 static int
  207 fm801_rdcd(kobj_t obj, void *devinfo, int regno)
  208 {
  209         struct fm801_info *fm801 = (struct fm801_info *)devinfo;
  210         int i;
  211 
  212         for (i = 0; i < TIMO && fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_BUSY; i++) {
  213                 DELAY(10000);
  214                 DPRINT("fm801 rdcd: 1 - DELAY\n");
  215         }
  216         if (i >= TIMO) {
  217                 printf("fm801 rdcd: codec busy\n");
  218                 return 0;
  219         }
  220 
  221         fm801_wr(fm801,FM_CODEC_CMD, regno|FM_CODEC_CMD_READ,2);
  222 
  223         for (i = 0; i < TIMO && !(fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_VALID); i++)
  224         {
  225                 DELAY(10000);
  226                 DPRINT("fm801 rdcd: 2 - DELAY\n");
  227         }
  228         if (i >= TIMO) {
  229                 printf("fm801 rdcd: write codec invalid\n");
  230                 return 0;
  231         }
  232 
  233         return fm801_rd(fm801,FM_CODEC_DATA,2);
  234 }
  235 
  236 static int
  237 fm801_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data)
  238 {
  239         struct fm801_info *fm801 = (struct fm801_info *)devinfo;
  240         int i;
  241 
  242         DPRINT("fm801_wrcd reg 0x%x val 0x%x\n",regno, data);
  243 /*
  244         if(regno == AC97_REG_RECSEL)    return;
  245 */
  246         /* Poll until codec is ready */
  247         for (i = 0; i < TIMO && fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_BUSY; i++) {
  248                 DELAY(10000);
  249                 DPRINT("fm801 rdcd: 1 - DELAY\n");
  250         }
  251         if (i >= TIMO) {
  252                 printf("fm801 wrcd: read codec busy\n");
  253                 return -1;
  254         }
  255 
  256         fm801_wr(fm801,FM_CODEC_DATA,data, 2);
  257         fm801_wr(fm801,FM_CODEC_CMD, regno,2);
  258 
  259         /* wait until codec is ready */
  260         for (i = 0; i < TIMO && fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_BUSY; i++) {
  261                 DELAY(10000);
  262                 DPRINT("fm801 wrcd: 2 - DELAY\n");
  263         }
  264         if (i >= TIMO) {
  265                 printf("fm801 wrcd: read codec busy\n");
  266                 return -1;
  267         }
  268         DPRINT("fm801 wrcd release reg 0x%x val 0x%x\n",regno, data);
  269         return 0;
  270 }
  271 
  272 static kobj_method_t fm801_ac97_methods[] = {
  273         KOBJMETHOD(ac97_read,           fm801_rdcd),
  274         KOBJMETHOD(ac97_write,          fm801_wrcd),
  275         { 0, 0 }
  276 };
  277 AC97_DECLARE(fm801_ac97);
  278 
  279 /* -------------------------------------------------------------------- */
  280 
  281 /*
  282  * The interrupt handler
  283  */
  284 static void
  285 fm801_intr(void *p)
  286 {
  287         struct fm801_info       *fm801 = (struct fm801_info *)p;
  288         u_int32_t               intsrc = fm801_rd(fm801, FM_INTSTATUS, 2);
  289 
  290         DPRINT("\nfm801_intr intsrc 0x%x ", intsrc);
  291 
  292         if(intsrc & FM_INTSTATUS_PLAY) {
  293                 fm801->play_flip++;
  294                 if(fm801->play_flip & 1) {
  295                         fm801_wr(fm801, FM_PLAY_DMABUF1, fm801->play_start,4);
  296                 } else
  297                         fm801_wr(fm801, FM_PLAY_DMABUF2, fm801->play_nextblk,4);
  298                 chn_intr(fm801->pch.channel);
  299         }
  300 
  301         if(intsrc & FM_INTSTATUS_REC) {
  302                 fm801->rec_flip++;
  303                 if(fm801->rec_flip & 1) {
  304                         fm801_wr(fm801, FM_REC_DMABUF1, fm801->rec_start,4);
  305                 } else
  306                         fm801_wr(fm801, FM_REC_DMABUF2, fm801->rec_nextblk,4);
  307                 chn_intr(fm801->rch.channel);
  308         }
  309 
  310         if ( intsrc & FM_INTSTATUS_MPU ) {
  311                 /* This is a TODOish thing... */
  312                 fm801_wr(fm801, FM_INTSTATUS, intsrc & FM_INTSTATUS_MPU,2);
  313         }
  314 
  315         if ( intsrc & FM_INTSTATUS_VOL ) {
  316                 /* This is a TODOish thing... */
  317                 fm801_wr(fm801, FM_INTSTATUS, intsrc & FM_INTSTATUS_VOL,2);
  318         }
  319 
  320         DPRINT("fm801_intr clear\n\n");
  321         fm801_wr(fm801, FM_INTSTATUS, intsrc & (FM_INTSTATUS_PLAY | FM_INTSTATUS_REC), 2);
  322 }
  323 
  324 /* -------------------------------------------------------------------- */
  325 /* channel interface */
  326 static void *
  327 fm801ch_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
  328 {
  329         struct fm801_info *fm801 = (struct fm801_info *)devinfo;
  330         struct fm801_chinfo *ch = (dir == PCMDIR_PLAY)? &fm801->pch : &fm801->rch;
  331 
  332         DPRINT("fm801ch_init, direction = %d\n", dir);
  333         ch->parent = fm801;
  334         ch->channel = c;
  335         ch->buffer = b;
  336         ch->dir = dir;
  337         if (sndbuf_alloc(ch->buffer, fm801->parent_dmat, fm801->bufsz) != 0)
  338                 return NULL;
  339         return (void *)ch;
  340 }
  341 
  342 static int
  343 fm801ch_setformat(kobj_t obj, void *data, u_int32_t format)
  344 {
  345         struct fm801_chinfo *ch = data;
  346         struct fm801_info *fm801 = ch->parent;
  347 
  348         DPRINT("fm801ch_setformat 0x%x : %s, %s, %s, %s\n", format,
  349                 (format & AFMT_STEREO)?"stereo":"mono",
  350                 (format & (AFMT_S16_LE | AFMT_S16_BE | AFMT_U16_LE | AFMT_U16_BE)) ? "16bit":"8bit",
  351                 (format & AFMT_SIGNED)? "signed":"unsigned",
  352                 (format & AFMT_BIGENDIAN)?"bigendiah":"littleendian" );
  353 
  354         if(ch->dir == PCMDIR_PLAY) {
  355                 fm801->play_fmt =  (format & AFMT_STEREO)? FM_PLAY_STEREO : 0;
  356                 fm801->play_fmt |= (format & AFMT_16BIT) ? FM_PLAY_16BIT : 0;
  357                 return 0;
  358         }
  359 
  360         if(ch->dir == PCMDIR_REC ) {
  361                 fm801->rec_fmt = (format & AFMT_STEREO)? FM_REC_STEREO:0;
  362                 fm801->rec_fmt |= (format & AFMT_16BIT) ? FM_PLAY_16BIT : 0;
  363                 return 0;
  364         }
  365 
  366         return 0;
  367 }
  368 
  369 struct {
  370         int limit;
  371         int rate;
  372 } fm801_rates[11] = {
  373         {  6600,  5500 },
  374         {  8750,  8000 },
  375         { 10250,  9600 },
  376         { 13200, 11025 },
  377         { 17500, 16000 },
  378         { 20500, 19200 },
  379         { 26500, 22050 },
  380         { 35000, 32000 },
  381         { 41000, 38400 },
  382         { 46000, 44100 },
  383         { 48000, 48000 },
  384 /* anything above -> 48000 */
  385 };
  386 
  387 static int
  388 fm801ch_setspeed(kobj_t obj, void *data, u_int32_t speed)
  389 {
  390         struct fm801_chinfo *ch = data;
  391         struct fm801_info *fm801 = ch->parent;
  392         register int i;
  393 
  394 
  395         for (i = 0; i < 10 && fm801_rates[i].limit <= speed; i++) ;
  396 
  397         if(ch->dir == PCMDIR_PLAY) {
  398                 fm801->pch.spd = fm801_rates[i].rate;
  399                 fm801->play_shift = (i<<8);
  400                 fm801->play_shift &= FM_PLAY_RATE_MASK;
  401         }
  402 
  403         if(ch->dir == PCMDIR_REC ) {
  404                 fm801->rch.spd = fm801_rates[i].rate;
  405                 fm801->rec_shift = (i<<8);
  406                 fm801->rec_shift &= FM_REC_RATE_MASK;
  407         }
  408 
  409         ch->spd = fm801_rates[i].rate;
  410 
  411         return fm801_rates[i].rate;
  412 }
  413 
  414 static int
  415 fm801ch_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
  416 {
  417         struct fm801_chinfo *ch = data;
  418         struct fm801_info *fm801 = ch->parent;
  419 
  420         if(ch->dir == PCMDIR_PLAY) {
  421                 if(fm801->play_flip) return fm801->play_blksize;
  422                 fm801->play_blksize = blocksize;
  423         }
  424 
  425         if(ch->dir == PCMDIR_REC) {
  426                 if(fm801->rec_flip) return fm801->rec_blksize;
  427                 fm801->rec_blksize = blocksize;
  428         }
  429 
  430         DPRINT("fm801ch_setblocksize %d (dir %d)\n",blocksize, ch->dir);
  431 
  432         return blocksize;
  433 }
  434 
  435 static int
  436 fm801ch_trigger(kobj_t obj, void *data, int go)
  437 {
  438         struct fm801_chinfo *ch = data;
  439         struct fm801_info *fm801 = ch->parent;
  440         u_int32_t baseaddr = sndbuf_getbufaddr(ch->buffer);
  441         u_int32_t k1;
  442 
  443         DPRINT("fm801ch_trigger go %d , ", go);
  444 
  445         if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) {
  446                 return 0;
  447         }
  448 
  449         if (ch->dir == PCMDIR_PLAY) {
  450                 if (go == PCMTRIG_START) {
  451 
  452                         fm801->play_start = baseaddr;
  453                         fm801->play_nextblk = fm801->play_start + fm801->play_blksize;
  454                         fm801->play_flip = 0;
  455                         fm801_wr(fm801, FM_PLAY_DMALEN, fm801->play_blksize - 1, 2);
  456                         fm801_wr(fm801, FM_PLAY_DMABUF1,fm801->play_start,4);
  457                         fm801_wr(fm801, FM_PLAY_DMABUF2,fm801->play_nextblk,4);
  458                         fm801_wr(fm801, FM_PLAY_CTL,
  459                                         FM_PLAY_START | FM_PLAY_STOPNOW | fm801->play_fmt | fm801->play_shift,
  460                                         2 );
  461                         } else {
  462                         fm801->play_flip = 0;
  463                         k1 = fm801_rd(fm801, FM_PLAY_CTL,2);
  464                         fm801_wr(fm801, FM_PLAY_CTL,
  465                                 (k1 & ~(FM_PLAY_STOPNOW | FM_PLAY_START)) |
  466                                 FM_PLAY_BUF1_LAST | FM_PLAY_BUF2_LAST, 2 );
  467                 }
  468         } else if(ch->dir == PCMDIR_REC) {
  469                 if (go == PCMTRIG_START) {
  470                         fm801->rec_start = baseaddr;
  471                         fm801->rec_nextblk = fm801->rec_start + fm801->rec_blksize;
  472                         fm801->rec_flip = 0;
  473                         fm801_wr(fm801, FM_REC_DMALEN, fm801->rec_blksize - 1, 2);
  474                         fm801_wr(fm801, FM_REC_DMABUF1,fm801->rec_start,4);
  475                         fm801_wr(fm801, FM_REC_DMABUF2,fm801->rec_nextblk,4);
  476                         fm801_wr(fm801, FM_REC_CTL,
  477                                         FM_REC_START | FM_REC_STOPNOW | fm801->rec_fmt | fm801->rec_shift,
  478                                         2 );
  479                         } else {
  480                         fm801->rec_flip = 0;
  481                         k1 = fm801_rd(fm801, FM_REC_CTL,2);
  482                         fm801_wr(fm801, FM_REC_CTL,
  483                                 (k1 & ~(FM_REC_STOPNOW | FM_REC_START)) |
  484                                 FM_REC_BUF1_LAST | FM_REC_BUF2_LAST, 2);
  485                 }
  486         }
  487 
  488         return 0;
  489 }
  490 
  491 /* Almost ALSA copy */
  492 static int
  493 fm801ch_getptr(kobj_t obj, void *data)
  494 {
  495         struct fm801_chinfo *ch = data;
  496         struct fm801_info *fm801 = ch->parent;
  497         int result = 0;
  498 
  499         if (ch->dir == PCMDIR_PLAY) {
  500                 result = fm801_rd(fm801,
  501                         (fm801->play_flip&1) ?
  502                         FM_PLAY_DMABUF2:FM_PLAY_DMABUF1, 4) - fm801->play_start;
  503         }
  504 
  505         if (ch->dir == PCMDIR_REC) {
  506                 result = fm801_rd(fm801,
  507                         (fm801->rec_flip&1) ?
  508                         FM_REC_DMABUF2:FM_REC_DMABUF1, 4) - fm801->rec_start;
  509         }
  510 
  511         return result;
  512 }
  513 
  514 static struct pcmchan_caps *
  515 fm801ch_getcaps(kobj_t obj, void *data)
  516 {
  517         return &fm801ch_caps;
  518 }
  519 
  520 static kobj_method_t fm801ch_methods[] = {
  521         KOBJMETHOD(channel_init,                fm801ch_init),
  522         KOBJMETHOD(channel_setformat,           fm801ch_setformat),
  523         KOBJMETHOD(channel_setspeed,            fm801ch_setspeed),
  524         KOBJMETHOD(channel_setblocksize,        fm801ch_setblocksize),
  525         KOBJMETHOD(channel_trigger,             fm801ch_trigger),
  526         KOBJMETHOD(channel_getptr,              fm801ch_getptr),
  527         KOBJMETHOD(channel_getcaps,             fm801ch_getcaps),
  528         { 0, 0 }
  529 };
  530 CHANNEL_DECLARE(fm801ch);
  531 
  532 /* -------------------------------------------------------------------- */
  533 
  534 /*
  535  *  Init routine is taken from an original NetBSD driver
  536  */
  537 static int
  538 fm801_init(struct fm801_info *fm801)
  539 {
  540         u_int32_t k1;
  541 
  542         /* reset codec */
  543         fm801_wr(fm801, FM_CODEC_CTL, 0x0020,2);
  544         DELAY(100000);
  545         fm801_wr(fm801, FM_CODEC_CTL, 0x0000,2);
  546         DELAY(100000);
  547 
  548         fm801_wr(fm801, FM_PCM_VOLUME, 0x0808,2);
  549         fm801_wr(fm801, FM_FM_VOLUME, 0x0808,2);
  550         fm801_wr(fm801, FM_I2S_VOLUME, 0x0808,2);
  551         fm801_wr(fm801, 0x40,0x107f,2); /* enable legacy audio */
  552 
  553         fm801_wr((void *)fm801, FM_RECORD_SOURCE, 0x0000,2);
  554 
  555         /* Unmask playback, record and mpu interrupts, mask the rest */
  556         k1 = fm801_rd((void *)fm801, FM_INTMASK,2);
  557         fm801_wr(fm801, FM_INTMASK,
  558                 (k1 & ~(FM_INTMASK_PLAY | FM_INTMASK_REC | FM_INTMASK_MPU)) |
  559                 FM_INTMASK_VOL,2);
  560         fm801_wr(fm801, FM_INTSTATUS,
  561                 FM_INTSTATUS_PLAY | FM_INTSTATUS_REC | FM_INTSTATUS_MPU |
  562                 FM_INTSTATUS_VOL,2);
  563 
  564         DPRINT("FM801 init Ok\n");
  565         return 0;
  566 }
  567 
  568 static int
  569 fm801_pci_attach(device_t dev)
  570 {
  571         u_int32_t               data;
  572         struct ac97_info        *codec = 0;
  573         struct fm801_info       *fm801;
  574         int                     i;
  575         int                     mapped = 0;
  576         char                    status[SND_STATUSLEN];
  577 
  578         if ((fm801 = (struct fm801_info *)malloc(sizeof(*fm801), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) {
  579                 device_printf(dev, "cannot allocate softc\n");
  580                 return ENXIO;
  581         }
  582 
  583         fm801->type = pci_get_devid(dev);
  584 
  585         data = pci_read_config(dev, PCIR_COMMAND, 2);
  586         data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN);
  587         pci_write_config(dev, PCIR_COMMAND, data, 2);
  588         data = pci_read_config(dev, PCIR_COMMAND, 2);
  589 
  590         for (i = 0; (mapped == 0) && (i < PCI_MAXMAPS_0); i++) {
  591                 fm801->regid = PCIR_BAR(i);
  592                 fm801->regtype = SYS_RES_MEMORY;
  593                 fm801->reg = bus_alloc_resource_any(dev, fm801->regtype,
  594                                                     &fm801->regid, RF_ACTIVE);
  595                 if(!fm801->reg)
  596                 {
  597                         fm801->regtype = SYS_RES_IOPORT;
  598                         fm801->reg = bus_alloc_resource_any(dev, 
  599                                                             fm801->regtype,
  600                                                             &fm801->regid,
  601                                                             RF_ACTIVE);
  602                 }
  603 
  604                 if(fm801->reg) {
  605                         fm801->st = rman_get_bustag(fm801->reg);
  606                         fm801->sh = rman_get_bushandle(fm801->reg);
  607                         mapped++;
  608                 }
  609         }
  610 
  611         if (mapped == 0) {
  612                 device_printf(dev, "unable to map register space\n");
  613                 goto oops;
  614         }
  615 
  616         fm801->bufsz = pcm_getbuffersize(dev, 4096, FM801_DEFAULT_BUFSZ, 65536);
  617 
  618         fm801_init(fm801);
  619 
  620         codec = AC97_CREATE(dev, fm801, fm801_ac97);
  621         if (codec == NULL) goto oops;
  622 
  623         if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) goto oops;
  624 
  625         fm801->irqid = 0;
  626         fm801->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &fm801->irqid,
  627                                             RF_ACTIVE | RF_SHAREABLE);
  628         if (!fm801->irq || snd_setup_intr(dev, fm801->irq, 0, fm801_intr, fm801, &fm801->ih)) {
  629                 device_printf(dev, "unable to map interrupt\n");
  630                 goto oops;
  631         }
  632 
  633         if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0,
  634                 /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
  635                 /*highaddr*/BUS_SPACE_MAXADDR,
  636                 /*filter*/NULL, /*filterarg*/NULL,
  637                 /*maxsize*/fm801->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff,
  638                 /*flags*/0, /*lockfunc*/busdma_lock_mutex,
  639                 /*lockarg*/&Giant, &fm801->parent_dmat) != 0) {
  640                 device_printf(dev, "unable to create dma tag\n");
  641                 goto oops;
  642         }
  643 
  644         snprintf(status, 64, "at %s 0x%lx irq %ld %s",
  645                 (fm801->regtype == SYS_RES_IOPORT)? "io" : "memory",
  646                 rman_get_start(fm801->reg), rman_get_start(fm801->irq),PCM_KLDSTRING(snd_fm801));
  647 
  648 #define FM801_MAXPLAYCH 1
  649         if (pcm_register(dev, fm801, FM801_MAXPLAYCH, 1)) goto oops;
  650         pcm_addchan(dev, PCMDIR_PLAY, &fm801ch_class, fm801);
  651         pcm_addchan(dev, PCMDIR_REC, &fm801ch_class, fm801);
  652         pcm_setstatus(dev, status);
  653 
  654         fm801->radio = device_add_child(dev, "radio", -1);
  655         bus_generic_attach(dev);
  656 
  657         return 0;
  658 
  659 oops:
  660         if (codec) ac97_destroy(codec);
  661         if (fm801->reg) bus_release_resource(dev, fm801->regtype, fm801->regid, fm801->reg);
  662         if (fm801->ih) bus_teardown_intr(dev, fm801->irq, fm801->ih);
  663         if (fm801->irq) bus_release_resource(dev, SYS_RES_IRQ, fm801->irqid, fm801->irq);
  664         if (fm801->parent_dmat) bus_dma_tag_destroy(fm801->parent_dmat);
  665         free(fm801, M_DEVBUF);
  666         return ENXIO;
  667 }
  668 
  669 static int
  670 fm801_pci_detach(device_t dev)
  671 {
  672         int r;
  673         struct fm801_info *fm801;
  674 
  675         DPRINT("Forte Media FM801 detach\n");
  676 
  677         fm801 = pcm_getdevinfo(dev);
  678 
  679         r = bus_generic_detach(dev);
  680         if (r)
  681                 return r;
  682         if (fm801->radio != NULL) {
  683                 r = device_delete_child(dev, fm801->radio);
  684                 if (r)
  685                         return r;
  686                 fm801->radio = NULL;
  687         }
  688 
  689         r = pcm_unregister(dev);
  690         if (r)
  691                 return r;
  692 
  693         bus_release_resource(dev, fm801->regtype, fm801->regid, fm801->reg);
  694         bus_teardown_intr(dev, fm801->irq, fm801->ih);
  695         bus_release_resource(dev, SYS_RES_IRQ, fm801->irqid, fm801->irq);
  696         bus_dma_tag_destroy(fm801->parent_dmat);
  697         free(fm801, M_DEVBUF);
  698         return 0;
  699 }
  700 
  701 static int
  702 fm801_pci_probe( device_t dev )
  703 {
  704         int id;
  705 
  706         if ((id = pci_get_devid(dev)) == PCI_DEVICE_FORTEMEDIA1 ) {
  707                 device_set_desc(dev, "Forte Media FM801 Audio Controller");
  708                 return 0;
  709         }
  710 /*
  711         if ((id = pci_get_devid(dev)) == PCI_DEVICE_FORTEMEDIA2 ) {
  712                 device_set_desc(dev, "Forte Media FM801 Joystick (Not Supported)");
  713                 return ENXIO;
  714         }
  715 */
  716         return ENXIO;
  717 }
  718 
  719 static struct resource *
  720 fm801_alloc_resource(device_t bus, device_t child, int type, int *rid,
  721                      u_long start, u_long end, u_long count, u_int flags)
  722 {
  723         struct fm801_info *fm801;
  724 
  725         fm801 = pcm_getdevinfo(bus);
  726 
  727         if (type == SYS_RES_IOPORT && *rid == PCIR_BAR(0))
  728                 return (fm801->reg);
  729 
  730         return (NULL);
  731 }
  732 
  733 static int
  734 fm801_release_resource(device_t bus, device_t child, int type, int rid,
  735                        struct resource *r)
  736 {
  737         return (0);
  738 }
  739 
  740 static device_method_t fm801_methods[] = {
  741         /* Device interface */
  742         DEVMETHOD(device_probe,         fm801_pci_probe),
  743         DEVMETHOD(device_attach,        fm801_pci_attach),
  744         DEVMETHOD(device_detach,        fm801_pci_detach),
  745         DEVMETHOD(device_shutdown,      bus_generic_shutdown),
  746         DEVMETHOD(device_suspend,       bus_generic_suspend),
  747         DEVMETHOD(device_resume,        bus_generic_resume),
  748 
  749         /* Bus interface */
  750         DEVMETHOD(bus_print_child,      bus_generic_print_child),
  751         DEVMETHOD(bus_alloc_resource,   fm801_alloc_resource),
  752         DEVMETHOD(bus_release_resource, fm801_release_resource),
  753         DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
  754         DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
  755         { 0, 0}
  756 };
  757 
  758 static driver_t fm801_driver = {
  759         "pcm",
  760         fm801_methods,
  761         PCM_SOFTC_SIZE,
  762 };
  763 
  764 DRIVER_MODULE(snd_fm801, pci, fm801_driver, pcm_devclass, 0, 0);
  765 MODULE_DEPEND(snd_fm801, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
  766 MODULE_VERSION(snd_fm801, 1);

Cache object: 81a519aff3dd634c75e0701f950aa946


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