root/drivers/sound/pas2_mixer.c

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

DEFINITIONS

This source file includes following definitions.
  1. mixer_output
  2. set_mode
  3. pas_mixer_set
  4. mixer_set_levels
  5. mixer_set_params
  6. getmixer
  7. mixer_get_levels
  8. mixer_get_params
  9. pas_mixer_reset
  10. pas_mixer_ioctl
  11. pas_init_mixer

   1 #define _PAS2_MIXER_C_
   2 
   3 /*
   4  * sound/pas2_mixer.c
   5  * 
   6  * Mixer routines for the Pro Audio Spectrum cards.
   7  * 
   8  * Copyright by Hannu Savolainen 1993
   9  * 
  10  * Redistribution and use in source and binary forms, with or without
  11  * modification, are permitted provided that the following conditions are
  12  * met: 1. Redistributions of source code must retain the above copyright
  13  * notice, this list of conditions and the following disclaimer. 2.
  14  * Redistributions in binary form must reproduce the above copyright notice,
  15  * this list of conditions and the following disclaimer in the documentation
  16  * and/or other materials provided with the distribution.
  17  * 
  18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
  19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  21  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
  22  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  24  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  25  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  28  * SUCH DAMAGE.
  29  * 
  30  */
  31 
  32 #include "sound_config.h"
  33 
  34 #if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_PAS)
  35 
  36 #include "pas.h"
  37 
  38 #define TRACE(what)             /* (what) */
  39 
  40 extern int      translat_code;
  41 
  42 static int      rec_devices = (SOUND_MASK_MIC); /* Default recording source */
  43 static int      mode_control = 0;
  44 
  45 #define POSSIBLE_RECORDING_DEVICES      (SOUND_MASK_SYNTH | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \
  46                                          SOUND_MASK_CD | SOUND_MASK_ALTPCM)
  47 
  48 #define SUPPORTED_MIXER_DEVICES         (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \
  49                                          SOUND_MASK_CD | SOUND_MASK_ALTPCM | SOUND_MASK_IMIX | \
  50                                          SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_RECLEV | \
  51                                          SOUND_MASK_MUTE | SOUND_MASK_ENHANCE | SOUND_MASK_LOUD)
  52 
  53 static unsigned short levels[SOUND_MIXER_NRDEVICES] =
  54 {
  55   0x3232,                       /* Master Volume */
  56   0x3232,                       /* Bass */
  57   0x3232,                       /* Treble */
  58   0x5050,                       /* FM */
  59   0x4b4b,                       /* PCM */
  60   0x3232,                       /* PC Speaker */
  61   0x4b4b,                       /* Ext Line */
  62   0x4b4b,                       /* Mic */
  63   0x4b4b,                       /* CD */
  64   0x6464,                       /* Recording monitor */
  65   0x4b4b,                       /* SB PCM */
  66   0x6464};                      /* Recording level */
  67 
  68 static int
  69 mixer_output (int right_vol, int left_vol, int div, int bits,
     /* [previous][next][first][last][top][bottom][index][help] */
  70               int mixer /* Input or output mixer */ )
  71 {
  72   int             left = left_vol * div / 100;
  73   int             right = right_vol * div / 100;
  74 
  75   /*
  76    * The Revision D cards have a problem with their MVA508 interface. The
  77    * kludge-o-rama fix is to make a 16-bit quantity with identical LSB and
  78    * MSBs out of the output byte and to do a 16-bit out to the mixer port -
  79    * 1. We don't need to do this because the call to pas_write more than
  80    * compensates for the timing problems.
  81    */
  82 
  83   if (bits & P_M_MV508_MIXER)
  84     {                           /* Select input or output mixer */
  85       left |= mixer;
  86       right |= mixer;
  87     }
  88 
  89   if (bits == P_M_MV508_BASS || bits == P_M_MV508_TREBLE)
  90     {                           /* Bass and trebble are mono devices     */
  91       pas_write (P_M_MV508_ADDRESS | bits, PARALLEL_MIXER);
  92       pas_write (left, PARALLEL_MIXER);
  93       right_vol = left_vol;
  94     }
  95   else
  96     {
  97       pas_write (P_M_MV508_ADDRESS | P_M_MV508_LEFT | bits, PARALLEL_MIXER);
  98       pas_write (left, PARALLEL_MIXER);
  99       pas_write (P_M_MV508_ADDRESS | P_M_MV508_RIGHT | bits, PARALLEL_MIXER);
 100       pas_write (right, PARALLEL_MIXER);
 101     }
 102 
 103   return (left_vol | (right_vol << 8));
 104 }
 105 
 106 void
 107 set_mode (int new_mode)
     /* [previous][next][first][last][top][bottom][index][help] */
 108 {
 109   pas_write (P_M_MV508_ADDRESS | P_M_MV508_MODE, PARALLEL_MIXER);
 110   pas_write (new_mode, PARALLEL_MIXER);
 111 
 112   mode_control = new_mode;
 113 }
 114 
 115 static int
 116 pas_mixer_set (int whichDev, unsigned int level)
     /* [previous][next][first][last][top][bottom][index][help] */
 117 {
 118   int             left, right, devmask, changed, i, mixer = 0;
 119 
 120   TRACE (printk ("static int pas_mixer_set(int whichDev = %d, unsigned int level = %X)\n", whichDev, level));
 121 
 122   left = level & 0x7f;
 123   right = (level & 0x7f00) >> 8;
 124 
 125   if (whichDev < SOUND_MIXER_NRDEVICES)
 126     if ((1 << whichDev) & rec_devices)
 127       mixer = P_M_MV508_INPUTMIX;
 128     else
 129       mixer = P_M_MV508_OUTPUTMIX;
 130 
 131   switch (whichDev)
 132     {
 133     case SOUND_MIXER_VOLUME:    /* Master volume (0-63) */
 134       levels[whichDev] = mixer_output (right, left, 63, P_M_MV508_MASTER_A, 0);
 135       break;
 136 
 137       /*
 138        * Note! Bass and Treble are mono devices. Will use just the left
 139        * channel.
 140        */
 141     case SOUND_MIXER_BASS:      /* Bass (0-12) */
 142       levels[whichDev] = mixer_output (right, left, 12, P_M_MV508_BASS, 0);
 143       break;
 144     case SOUND_MIXER_TREBLE:    /* Treble (0-12) */
 145       levels[whichDev] = mixer_output (right, left, 12, P_M_MV508_TREBLE, 0);
 146       break;
 147 
 148     case SOUND_MIXER_SYNTH:     /* Internal synthesizer (0-31) */
 149       levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_FM, mixer);
 150       break;
 151     case SOUND_MIXER_PCM:       /* PAS PCM (0-31) */
 152       levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_PCM, mixer);
 153       break;
 154     case SOUND_MIXER_ALTPCM:    /* SB PCM (0-31) */
 155       levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_SB, mixer);
 156       break;
 157     case SOUND_MIXER_SPEAKER:   /* PC speaker (0-31) */
 158       levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_SPEAKER, mixer);
 159       break;
 160     case SOUND_MIXER_LINE:      /* External line (0-31) */
 161       levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_LINE, mixer);
 162       break;
 163     case SOUND_MIXER_CD:        /* CD (0-31) */
 164       levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_CDROM, mixer);
 165       break;
 166     case SOUND_MIXER_MIC:       /* External microphone (0-31) */
 167       levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_MIC, mixer);
 168       break;
 169     case SOUND_MIXER_IMIX:      /* Recording monitor (0-31) (Only available
 170                                  * on the Output Mixer) */
 171       levels[whichDev] = mixer_output (right, left, 31, P_M_MV508_MIXER | P_M_MV508_IMIXER,
 172                                        P_M_MV508_OUTPUTMIX);
 173       break;
 174     case SOUND_MIXER_RECLEV:    /* Recording level (0-15) */
 175       levels[whichDev] = mixer_output (right, left, 15, P_M_MV508_MASTER_B, 0);
 176       break;
 177 
 178     case SOUND_MIXER_MUTE:
 179       return 0;
 180       break;
 181 
 182     case SOUND_MIXER_ENHANCE:
 183       i = 0;
 184       level &= 0x7f;
 185       if (level)
 186         i = (level / 20) - 1;
 187 
 188       mode_control &= ~P_M_MV508_ENHANCE_BITS;
 189       mode_control |= P_M_MV508_ENHANCE_BITS;
 190       set_mode (mode_control);
 191 
 192       if (i)
 193         i = (i + 1) * 20;
 194       return i;
 195       break;
 196 
 197     case SOUND_MIXER_LOUD:
 198       mode_control &= ~P_M_MV508_LOUDNESS;
 199       if (level)
 200         mode_control |= P_M_MV508_LOUDNESS;
 201       set_mode (mode_control);
 202       return !!level;           /* 0 or 1 */
 203       break;
 204 
 205     case SOUND_MIXER_RECSRC:
 206       devmask = level & POSSIBLE_RECORDING_DEVICES;
 207 
 208       changed = devmask ^ rec_devices;
 209       rec_devices = devmask;
 210 
 211       for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
 212         if (changed & (1 << i))
 213           {
 214             pas_mixer_set (i, levels[i]);
 215           }
 216       return rec_devices;
 217       break;
 218 
 219     default:
 220       return RET_ERROR (EINVAL);
 221     }
 222 
 223   return (levels[whichDev]);
 224 }
 225 
 226 /*****/
 227 
 228 static int
 229 mixer_set_levels (struct sb_mixer_levels *user_l)
     /* [previous][next][first][last][top][bottom][index][help] */
 230 {
 231 #define cmix(v) ((((v.r*100+7)/15)<<8)| ((v.l*100+7)/15))
 232 
 233   struct sb_mixer_levels l;
 234 
 235   IOCTL_FROM_USER ((char *) &l, (char *) user_l, 0, sizeof (l));
 236 
 237   if (l.master.l & ~0xF || l.master.r & ~0xF
 238       || l.line.l & ~0xF || l.line.r & ~0xF
 239       || l.voc.l & ~0xF || l.voc.r & ~0xF
 240       || l.fm.l & ~0xF || l.fm.r & ~0xF
 241       || l.cd.l & ~0xF || l.cd.r & ~0xF
 242       || l.mic & ~0x7)
 243     return (RET_ERROR (EINVAL));
 244 
 245   pas_mixer_set (SOUND_MIXER_VOLUME, cmix (l.master));
 246   pas_mixer_set (SOUND_MIXER_LINE, cmix (l.line));
 247   pas_mixer_set (SOUND_MIXER_PCM, cmix (l.voc));
 248   pas_mixer_set (SOUND_MIXER_ALTPCM, cmix (l.voc));
 249   pas_mixer_set (SOUND_MIXER_SYNTH, cmix (l.fm));
 250   pas_mixer_set (SOUND_MIXER_CD, cmix (l.cd));
 251   pas_mixer_set (SOUND_MIXER_MIC, ((l.mic * 100 + 3) / 7) | (((l.mic * 100 + 3) / 7) << 8));
 252   return (0);
 253 }
 254 
 255 /*
 256  * This sets aspects of the Mixer that are not volume levels. (Recording
 257  * source, filter level, I/O filtering, and stereo.)
 258  */
 259 static int
 260 mixer_set_params (struct sb_mixer_params *user_p)
     /* [previous][next][first][last][top][bottom][index][help] */
 261 {
 262   struct sb_mixer_params p;
 263   S_BYTE          val;
 264   int             src;
 265   unsigned long   flags;
 266 
 267   IOCTL_FROM_USER ((char *) &p, (char *) user_p, 0, sizeof (p));
 268 
 269   if (p.record_source != SRC_MIC
 270       && p.record_source != SRC_CD
 271       && p.record_source != SRC_LINE)
 272     return (RET_ERROR (EINVAL));
 273 
 274   /*
 275    * I'm not sure if this is The Right Thing.  Should stereo be entirely
 276    * under control of DSP?  I like being able to toggle it while a sound is
 277    * playing, so I do this... because I can.
 278    */
 279 
 280   DISABLE_INTR (flags);
 281 
 282   val = (pas_read (PCM_CONTROL) & ~P_C_MIXER_CROSS_FIELD) | P_C_MIXER_CROSS_R_TO_R | P_C_MIXER_CROSS_L_TO_L;
 283   if (!p.dsp_stereo)
 284     val |= (P_C_MIXER_CROSS_R_TO_L | P_C_MIXER_CROSS_L_TO_R);   /* Mono */
 285   pas_write (val, PCM_CONTROL);
 286 
 287   RESTORE_INTR (flags);
 288 
 289   switch (p.record_source)
 290     {
 291     case SRC_CD:
 292       src = SOUND_MASK_CD;
 293       break;
 294 
 295     case SRC_LINE:
 296       src = SOUND_MASK_LINE;
 297       break;
 298 
 299     default:
 300       src = SOUND_MASK_MIC;
 301       break;
 302     }
 303 
 304   pas_mixer_set (SOUND_MIXER_RECSRC, src);
 305 
 306   /*
 307    * setmixer (OUT_FILTER, ((dsp_stereo ? STEREO_DAC : MONO_DAC) |
 308    * (p.filter_output ? FILT_ON : FILT_OFF)));
 309    */
 310   return (0);
 311 }
 312 
 313 static int
 314 getmixer (int dev, int chn)
     /* [previous][next][first][last][top][bottom][index][help] */
 315 {
 316   if (chn == P_M_MV508_RIGHT)
 317     {
 318       return (levels[dev] >> 8) & 0x7f;
 319     }
 320   else
 321     {
 322       return levels[dev] & 0x7f;
 323     }
 324 }
 325 
 326 /* Read the current mixer level settings into the user's struct. */
 327 static int
 328 mixer_get_levels (struct sb_mixer_levels *user_l)
     /* [previous][next][first][last][top][bottom][index][help] */
 329 {
 330 
 331   struct sb_mixer_levels l;
 332 
 333   l.master.r = ((((levels[SOUND_MIXER_VOLUME] >> 8) & 0x7f) * 15) + 50) / 100;  /* Master */
 334   l.master.l = (((levels[SOUND_MIXER_VOLUME] & 0x7f) * 15) + 50) / 100; /* Master */
 335 
 336   l.line.r = ((getmixer (SOUND_MIXER_LINE, P_M_MV508_RIGHT) * 15) + 50) / 100;  /* Line */
 337   l.line.l = ((getmixer (SOUND_MIXER_LINE, P_M_MV508_LEFT) * 15) + 50) / 100;
 338 
 339   l.voc.r = ((getmixer (SOUND_MIXER_PCM, P_M_MV508_RIGHT) * 15) + 50) / 100;    /* DAC */
 340   l.voc.l = ((getmixer (SOUND_MIXER_PCM, P_M_MV508_LEFT) * 15) + 50) / 100;
 341 
 342   l.fm.r = ((getmixer (SOUND_MIXER_SYNTH, P_M_MV508_RIGHT) * 15) + 50) / 100;   /* FM */
 343   l.fm.l = ((getmixer (SOUND_MIXER_SYNTH, P_M_MV508_LEFT) * 15) + 50) / 100;
 344 
 345   l.cd.r = ((getmixer (SOUND_MIXER_CD, P_M_MV508_RIGHT) * 15) + 50) / 100;      /* CD */
 346   l.cd.l = ((getmixer (SOUND_MIXER_CD, P_M_MV508_LEFT) * 15) + 50) / 100;
 347 
 348   l.mic = ((getmixer (SOUND_MIXER_MIC, P_M_MV508_LEFT) * 7) + 50) / 100;        /* Microphone */
 349 
 350   IOCTL_TO_USER ((char *) user_l, 0, (char *) &l, sizeof (l));
 351   return (0);
 352 }
 353 
 354 /* Read the current mixer parameters into the user's struct. */
 355 static int
 356 mixer_get_params (struct sb_mixer_params *user_params)
     /* [previous][next][first][last][top][bottom][index][help] */
 357 {
 358   S_BYTE          val;
 359   struct sb_mixer_params params;
 360 
 361   switch (rec_devices)
 362     {
 363     case SOUND_MASK_CD:
 364       params.record_source = SRC_CD;
 365       break;
 366 
 367     case SOUND_MASK_LINE:
 368       params.record_source = SRC_LINE;
 369       break;
 370 
 371     case SOUND_MASK_MIC:
 372       params.record_source = SRC_MIC;
 373       break;
 374 
 375     default:
 376       params.record_source = SRC_MIC;
 377       pas_mixer_set (SOUND_MIXER_RECSRC, SOUND_MASK_MIC);       /* Adjust */
 378     }
 379 
 380   params.hifreq_filter = OFF;
 381   params.filter_input = OFF;
 382   params.filter_output = OFF;
 383 
 384   val = INB (PCM_CONTROL);
 385   params.dsp_stereo = ((val & P_C_MIXER_CROSS_FIELD) == (P_C_MIXER_CROSS_L_TO_L | P_C_MIXER_CROSS_R_TO_R));
 386 
 387   IOCTL_TO_USER ((char *) user_params, 0, (char *) &params, sizeof (params));
 388   return (0);
 389 }
 390 
 391 /*****/
 392 
 393 static void
 394 pas_mixer_reset (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 395 {
 396   int             foo;
 397 
 398   TRACE (printk ("pas2_mixer.c: void pas_mixer_reset(void)\n"));
 399 
 400   for (foo = 0; foo < SOUND_MIXER_NRDEVICES; foo++)
 401     pas_mixer_set (foo, levels[foo]);
 402 
 403   set_mode (P_M_MV508_LOUDNESS | P_M_MV508_ENHANCE_40);
 404 }
 405 
 406 int
 407 pas_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg)
     /* [previous][next][first][last][top][bottom][index][help] */
 408 {
 409   TRACE (printk ("pas2_mixer.c: int pas_mixer_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg));
 410 
 411   if (((cmd >> 8) & 0xff) == 'M')
 412     {
 413       if (cmd & IOC_IN)
 414         return IOCTL_OUT (arg, pas_mixer_set (cmd & 0xff, IOCTL_IN (arg)));
 415       else
 416         {                       /* Read parameters */
 417 
 418           switch (cmd & 0xff)
 419             {
 420 
 421             case SOUND_MIXER_RECSRC:
 422               return IOCTL_OUT (arg, rec_devices);
 423               break;
 424 
 425             case SOUND_MIXER_STEREODEVS:
 426               return IOCTL_OUT (arg, SUPPORTED_MIXER_DEVICES & ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE));
 427               break;
 428 
 429             case SOUND_MIXER_DEVMASK:
 430               return IOCTL_OUT (arg, SUPPORTED_MIXER_DEVICES);
 431               break;
 432 
 433             case SOUND_MIXER_RECMASK:
 434               return IOCTL_OUT (arg, POSSIBLE_RECORDING_DEVICES & SUPPORTED_MIXER_DEVICES);
 435               break;
 436 
 437             case SOUND_MIXER_CAPS:
 438               return IOCTL_OUT (arg, 0);        /* No special capabilities */
 439               break;
 440 
 441             case SOUND_MIXER_MUTE:
 442               return IOCTL_OUT (arg, 0);        /* No mute yet */
 443               break;
 444 
 445             case SOUND_MIXER_ENHANCE:
 446               if (!(mode_control & P_M_MV508_ENHANCE_BITS))
 447                 return IOCTL_OUT (arg, 0);
 448               return IOCTL_OUT (arg, ((mode_control & P_M_MV508_ENHANCE_BITS) + 1) * 20);
 449               break;
 450 
 451             case SOUND_MIXER_LOUD:
 452               if (mode_control & P_M_MV508_LOUDNESS)
 453                 return IOCTL_OUT (arg, 1);
 454               return IOCTL_OUT (arg, 0);
 455               break;
 456 
 457             default:
 458               return IOCTL_OUT (arg, levels[cmd & 0xff]);
 459             }
 460         }
 461     }
 462   else
 463     {
 464       switch (cmd)
 465         {
 466         case MIXER_IOCTL_SET_LEVELS:
 467           mixer_set_levels ((struct sb_mixer_levels *) arg);
 468           return mixer_get_levels ((struct sb_mixer_levels *) arg);
 469         case MIXER_IOCTL_SET_PARAMS:
 470           mixer_set_params ((struct sb_mixer_params *) arg);
 471           return mixer_get_params ((struct sb_mixer_params *) arg);
 472         case MIXER_IOCTL_READ_LEVELS:
 473           return mixer_get_levels ((struct sb_mixer_levels *) arg);
 474         case MIXER_IOCTL_READ_PARAMS:
 475           return mixer_get_params ((struct sb_mixer_params *) arg);
 476         case MIXER_IOCTL_RESET:
 477           pas_mixer_reset ();
 478           return (0);
 479         default:
 480           return RET_ERROR (EINVAL);
 481         }
 482     }
 483   return RET_ERROR (EINVAL);
 484 }
 485 
 486 static struct mixer_operations pas_mixer_operations =
 487 {
 488   pas_mixer_ioctl
 489 };
 490 
 491 int
 492 pas_init_mixer (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 493 {
 494   pas_mixer_reset ();
 495 
 496   mixer_devs[num_mixers++] = &pas_mixer_operations;
 497   return 1;
 498 }
 499 
 500 #endif

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