root/drivers/sound/sb_mixer.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. sb_setmixer
  2. sb_getmixer
  3. sb_mixer_set_stereo
  4. detect_mixer
  5. change_bits
  6. sb_mixer_get
  7. sb_mixer_set
  8. set_recsrc
  9. set_recmask
  10. sb_mixer_ioctl
  11. sb_mixer_reset
  12. sb_mixer_init

   1 
   2 /*
   3  * sound/sb_mixer.c
   4  *
   5  * The low level mixer driver for the SoundBlaster Pro and SB16 cards.
   6  *
   7  * Copyright by Hannu Savolainen 1994
   8  *
   9  * Redistribution and use in source and binary forms, with or without
  10  * modification, are permitted provided that the following conditions are
  11  * met: 1. Redistributions of source code must retain the above copyright
  12  * notice, this list of conditions and the following disclaimer. 2.
  13  * Redistributions in binary form must reproduce the above copyright notice,
  14  * this list of conditions and the following disclaimer in the documentation
  15  * and/or other materials provided with the distribution.
  16  *
  17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
  18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  20  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
  21  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  23  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  24  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  27  * SUCH DAMAGE.
  28  *
  29  * Modified:
  30  *      Hunyue Yau      Jan 6 1994
  31  *      Added code to support the Sound Galaxy NX Pro mixer.
  32  *
  33  */
  34 
  35 #include "sound_config.h"
  36 
  37 #if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB) && !defined(EXCLUDE_SBPRO)
  38 #define __SB_MIXER_C__
  39 
  40 #include "sb.h"
  41 #include "sb_mixer.h"
  42 #undef SB_TEST_IRQ
  43 
  44 extern int      sbc_base;
  45 
  46 static int      mixer_initialized = 0;
  47 
  48 static int      supported_rec_devices;
  49 static int      supported_devices;
  50 static int      recmask = 0;
  51 static int      mixer_model;
  52 static int      mixer_caps;
  53 static mixer_tab *iomap;
  54 
  55 void
  56 sb_setmixer (unsigned int port, unsigned int value)
     /* [previous][next][first][last][top][bottom][index][help] */
  57 {
  58   unsigned long   flags;
  59 
  60   DISABLE_INTR (flags);
  61   OUTB ((unsigned char) (port & 0xff), MIXER_ADDR);     /*
  62                                                          * Select register
  63                                                          */
  64   tenmicrosec ();
  65   OUTB ((unsigned char) (value & 0xff), MIXER_DATA);
  66   tenmicrosec ();
  67   RESTORE_INTR (flags);
  68 }
  69 
  70 int
  71 sb_getmixer (unsigned int port)
     /* [previous][next][first][last][top][bottom][index][help] */
  72 {
  73   int             val;
  74   unsigned long   flags;
  75 
  76   DISABLE_INTR (flags);
  77   OUTB ((unsigned char) (port & 0xff), MIXER_ADDR);     /*
  78                                                          * Select register
  79                                                          */
  80   tenmicrosec ();
  81   val = INB (MIXER_DATA);
  82   tenmicrosec ();
  83   RESTORE_INTR (flags);
  84 
  85   return val;
  86 }
  87 
  88 void
  89 sb_mixer_set_stereo (int mode)
     /* [previous][next][first][last][top][bottom][index][help] */
  90 {
  91   if (!mixer_initialized)
  92     return;
  93 
  94   sb_setmixer (OUT_FILTER, ((sb_getmixer (OUT_FILTER) & ~STEREO_DAC)
  95                             | (mode ? STEREO_DAC : MONO_DAC)));
  96 }
  97 
  98 /*
  99  * Returns:
 100  *      0       No mixer detected.
 101  *      1       Only a plain Sound Blaster Pro style mixer detected.
 102  *      2       The Sound Galaxy NX Pro mixer detected.
 103  */
 104 static int
 105 detect_mixer (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 106 {
 107 #ifdef __SGNXPRO__
 108   int             oldbass, oldtreble;
 109 
 110 #endif
 111   int             retcode = 1;
 112 
 113   /*
 114    * Detect the mixer by changing parameters of two volume channels. If the
 115    * values read back match with the values written, the mixer is there (is
 116    * it?)
 117    */
 118   sb_setmixer (FM_VOL, 0xff);
 119   sb_setmixer (VOC_VOL, 0x33);
 120 
 121   if (sb_getmixer (FM_VOL) != 0xff)
 122     return 0;                   /*
 123                                  * No match
 124                                  */
 125   if (sb_getmixer (VOC_VOL) != 0x33)
 126     return 0;
 127 
 128 #ifdef __SGNXPRO__
 129   /* Attempt to detect the SG NX Pro by check for valid bass/treble
 130  * registers.
 131  */
 132   oldbass = sb_getmixer (BASS_LVL);
 133   oldtreble = sb_getmixer (TREBLE_LVL);
 134 
 135   sb_setmixer (BASS_LVL, 0xaa);
 136   sb_setmixer (TREBLE_LVL, 0x55);
 137 
 138   if ((sb_getmixer (BASS_LVL) != 0xaa) ||
 139       (sb_getmixer (TREBLE_LVL) != 0x55))
 140     {
 141       retcode = 1;              /* 1 == Only SB Pro detected */
 142     }
 143   else
 144     retcode = 2;                /* 2 == SG NX Pro detected */
 145   /* Restore register in either case since SG NX Pro has EEPROM with
 146    * 'preferred' values stored.
 147    */
 148   sb_setmixer (BASS_LVL, oldbass);
 149   sb_setmixer (TREBLE_LVL, oldtreble);
 150 #endif
 151   return retcode;
 152 }
 153 
 154 static void
 155 change_bits (unsigned char *regval, int dev, int chn, int newval)
     /* [previous][next][first][last][top][bottom][index][help] */
 156 {
 157   unsigned char   mask;
 158   int             shift;
 159 
 160   mask = (1 << (*iomap)[dev][chn].nbits) - 1;
 161   newval = (int) ((newval * mask) + 50) / 100;  /*
 162                                                  * Scale it
 163                                                  */
 164 
 165   shift = (*iomap)[dev][chn].bitoffs - (*iomap)[dev][LEFT_CHN].nbits + 1;
 166 
 167   *regval &= ~(mask << shift);  /*
 168                                  * Filter out the previous value
 169                                  */
 170   *regval |= (newval & mask) << shift;  /*
 171                                          * Set the new value
 172                                          */
 173 }
 174 
 175 static int
 176 sb_mixer_get (int dev)
     /* [previous][next][first][last][top][bottom][index][help] */
 177 {
 178   if (!((1 << dev) & supported_devices))
 179     return RET_ERROR (EINVAL);
 180 
 181   return levels[dev];
 182 }
 183 
 184 static int
 185 sb_mixer_set (int dev, int value)
     /* [previous][next][first][last][top][bottom][index][help] */
 186 {
 187   int             left = value & 0x000000ff;
 188   int             right = (value & 0x0000ff00) >> 8;
 189 
 190   int             regoffs;
 191   unsigned char   val;
 192 
 193   if (left > 100)
 194     left = 100;
 195   if (right > 100)
 196     right = 100;
 197 
 198   if (dev > 31)
 199     return RET_ERROR (EINVAL);
 200 
 201   if (!(supported_devices & (1 << dev)))        /*
 202                                                  * Not supported
 203                                                  */
 204     return RET_ERROR (EINVAL);
 205 
 206   regoffs = (*iomap)[dev][LEFT_CHN].regno;
 207 
 208   if (regoffs == 0)
 209     return RET_ERROR (EINVAL);
 210 
 211   val = sb_getmixer (regoffs);
 212   change_bits (&val, dev, LEFT_CHN, left);
 213 
 214   levels[dev] = left | (left << 8);
 215 
 216   if ((*iomap)[dev][RIGHT_CHN].regno != regoffs)        /*
 217                                                          * Change register
 218                                                          */
 219     {
 220       sb_setmixer (regoffs, val);       /*
 221                                          * Save the old one
 222                                          */
 223       regoffs = (*iomap)[dev][RIGHT_CHN].regno;
 224 
 225       if (regoffs == 0)
 226         return left | (left << 8);      /*
 227                                          * Just left channel present
 228                                          */
 229 
 230       val = sb_getmixer (regoffs);      /*
 231                                          * Read the new one
 232                                          */
 233     }
 234 
 235   change_bits (&val, dev, RIGHT_CHN, right);
 236   sb_setmixer (regoffs, val);
 237 
 238   levels[dev] = left | (right << 8);
 239   return left | (right << 8);
 240 }
 241 
 242 static void
 243 set_recsrc (int src)
     /* [previous][next][first][last][top][bottom][index][help] */
 244 {
 245   sb_setmixer (RECORD_SRC, (sb_getmixer (RECORD_SRC) & ~7) | (src & 0x7));
 246 }
 247 
 248 static int
 249 set_recmask (int mask)
     /* [previous][next][first][last][top][bottom][index][help] */
 250 {
 251   int             devmask, i;
 252   unsigned char   regimageL, regimageR;
 253 
 254   devmask = mask & supported_rec_devices;
 255 
 256   switch (mixer_model)
 257     {
 258     case 3:
 259 
 260       if (devmask != SOUND_MASK_MIC &&
 261           devmask != SOUND_MASK_LINE &&
 262           devmask != SOUND_MASK_CD)
 263         {                       /*
 264                                  * More than one devices selected. Drop the *
 265                                  * previous selection
 266                                  */
 267           devmask &= ~recmask;
 268         }
 269 
 270       if (devmask != SOUND_MASK_MIC &&
 271           devmask != SOUND_MASK_LINE &&
 272           devmask != SOUND_MASK_CD)
 273         {                       /*
 274                                  * More than one devices selected. Default to
 275                                  * * mic
 276                                  */
 277           devmask = SOUND_MASK_MIC;
 278         }
 279 
 280 
 281       if (devmask ^ recmask)    /*
 282                                  * Input source changed
 283                                  */
 284         {
 285           switch (devmask)
 286             {
 287 
 288             case SOUND_MASK_MIC:
 289               set_recsrc (SRC_MIC);
 290               break;
 291 
 292             case SOUND_MASK_LINE:
 293               set_recsrc (SRC_LINE);
 294               break;
 295 
 296             case SOUND_MASK_CD:
 297               set_recsrc (SRC_CD);
 298               break;
 299 
 300             default:
 301               set_recsrc (SRC_MIC);
 302             }
 303         }
 304 
 305       break;
 306 
 307     case 4:
 308       if (!devmask)
 309         devmask = SOUND_MASK_MIC;
 310 
 311       regimageL = regimageR = 0;
 312       for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
 313         if ((1 << i) & devmask)
 314           {
 315             regimageL |= sb16_recmasks_L[i];
 316             regimageR |= sb16_recmasks_R[i];
 317           }
 318       sb_setmixer (SB16_IMASK_L, regimageL);
 319       sb_setmixer (SB16_IMASK_R, regimageR);
 320       break;
 321     }
 322 
 323   recmask = devmask;
 324   return recmask;
 325 }
 326 
 327 static int
 328 sb_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg)
     /* [previous][next][first][last][top][bottom][index][help] */
 329 {
 330   if (((cmd >> 8) & 0xff) == 'M')
 331     {
 332       if (cmd & IOC_IN)
 333         switch (cmd & 0xff)
 334           {
 335           case SOUND_MIXER_RECSRC:
 336             return IOCTL_OUT (arg, set_recmask (IOCTL_IN (arg)));
 337             break;
 338 
 339           default:
 340             return IOCTL_OUT (arg, sb_mixer_set (cmd & 0xff, IOCTL_IN (arg)));
 341           }
 342       else
 343         switch (cmd & 0xff)     /*
 344                                  * Return parameters
 345                                  */
 346           {
 347 
 348           case SOUND_MIXER_RECSRC:
 349             return IOCTL_OUT (arg, recmask);
 350             break;
 351 
 352           case SOUND_MIXER_DEVMASK:
 353             return IOCTL_OUT (arg, supported_devices);
 354             break;
 355 
 356           case SOUND_MIXER_STEREODEVS:
 357             return IOCTL_OUT (arg, supported_devices &
 358                               ~(SOUND_MASK_MIC | SOUND_MASK_SPEAKER));
 359             break;
 360 
 361           case SOUND_MIXER_RECMASK:
 362             return IOCTL_OUT (arg, supported_rec_devices);
 363             break;
 364 
 365           case SOUND_MIXER_CAPS:
 366             return IOCTL_OUT (arg, mixer_caps);
 367             break;
 368 
 369           default:
 370             return IOCTL_OUT (arg, sb_mixer_get (cmd & 0xff));
 371           }
 372     }
 373   else
 374     return RET_ERROR (EINVAL);
 375 }
 376 
 377 static struct mixer_operations sb_mixer_operations =
 378 {
 379   sb_mixer_ioctl
 380 };
 381 
 382 static void
 383 sb_mixer_reset (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 384 {
 385   int             i;
 386 
 387   for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
 388     sb_mixer_set (i, levels[i]);
 389   set_recmask (SOUND_MASK_MIC);
 390 }
 391 
 392 /*
 393  * Returns a code depending on whether a SG NX Pro was detected.
 394  * 1 == Plain SB Pro
 395  * 2 == SG NX Pro detected.
 396  * 3 == SB16
 397  *
 398  * Used to update message.
 399  */
 400 int
 401 sb_mixer_init (int major_model)
     /* [previous][next][first][last][top][bottom][index][help] */
 402 {
 403   int             mixer_type = 0;
 404 
 405   sb_setmixer (0x00, 0);        /* Reset mixer */
 406 
 407   if (!(mixer_type = detect_mixer ()))
 408     return 0;                   /* No mixer. Why? */
 409 
 410   mixer_initialized = 1;
 411   mixer_model = major_model;
 412 
 413   switch (major_model)
 414     {
 415     case 3:
 416       mixer_caps = SOUND_CAP_EXCL_INPUT;
 417 #ifdef __SGNXPRO__
 418       if (mixer_type == 2)      /* A SGNXPRO was detected */
 419         {
 420           supported_devices = SGNXPRO_MIXER_DEVICES;
 421           supported_rec_devices = SGNXPRO_RECORDING_DEVICES;
 422           iomap = &sgnxpro_mix;
 423         }
 424       else
 425 #endif
 426         {
 427           supported_devices = SBPRO_MIXER_DEVICES;
 428           supported_rec_devices = SBPRO_RECORDING_DEVICES;
 429           iomap = &sbpro_mix;
 430           mixer_type = 1;
 431         }
 432       break;
 433 
 434     case 4:
 435       mixer_caps = 0;
 436       supported_devices = SB16_MIXER_DEVICES;
 437       supported_rec_devices = SB16_RECORDING_DEVICES;
 438       iomap = &sb16_mix;
 439       mixer_type = 3;
 440       break;
 441 
 442     default:
 443       printk ("SB Warning: Unsupported mixer type\n");
 444       return 0;
 445     }
 446 
 447   if (num_mixers < MAX_MIXER_DEV)
 448     mixer_devs[num_mixers++] = &sb_mixer_operations;
 449   sb_mixer_reset ();
 450   return mixer_type;
 451 }
 452 
 453 #endif

/* [previous][next][first][last][top][bottom][index][help] */