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

Cache object: c2a23b219996bf37d9e266feedae41d6


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