root/sound/isa/msnd/msnd_pinnacle_mixer.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. snd_msndmix_info_mux
  2. snd_msndmix_get_mux
  3. snd_msndmix_set_mux
  4. snd_msndmix_put_mux
  5. snd_msndmix_volume_info
  6. snd_msndmix_volume_get
  7. snd_msndmix_set
  8. snd_msndmix_volume_put
  9. snd_msndmix_new
  10. snd_msndmix_setup
  11. snd_msndmix_force_recsrc

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /***************************************************************************
   3                           msnd_pinnacle_mixer.c  -  description
   4                              -------------------
   5     begin               : Fre Jun 7 2002
   6     copyright           : (C) 2002 by karsten wiese
   7     email               : annabellesgarden@yahoo.de
   8  ***************************************************************************/
   9 
  10 /***************************************************************************
  11  *                                                                         *
  12  *                                                                         *
  13  ***************************************************************************/
  14 
  15 #include <linux/io.h>
  16 #include <linux/export.h>
  17 
  18 #include <sound/core.h>
  19 #include <sound/control.h>
  20 #include "msnd.h"
  21 #include "msnd_pinnacle.h"
  22 
  23 
  24 #define MSND_MIXER_VOLUME       0
  25 #define MSND_MIXER_PCM          1
  26 #define MSND_MIXER_AUX          2       /* Input source 1  (aux1) */
  27 #define MSND_MIXER_IMIX         3       /*  Recording monitor  */
  28 #define MSND_MIXER_SYNTH        4
  29 #define MSND_MIXER_SPEAKER      5
  30 #define MSND_MIXER_LINE         6
  31 #define MSND_MIXER_MIC          7
  32 #define MSND_MIXER_RECLEV       11      /* Recording level */
  33 #define MSND_MIXER_IGAIN        12      /* Input gain */
  34 #define MSND_MIXER_OGAIN        13      /* Output gain */
  35 #define MSND_MIXER_DIGITAL      17      /* Digital (input) 1 */
  36 
  37 /*      Device mask bits        */
  38 
  39 #define MSND_MASK_VOLUME        (1 << MSND_MIXER_VOLUME)
  40 #define MSND_MASK_SYNTH         (1 << MSND_MIXER_SYNTH)
  41 #define MSND_MASK_PCM           (1 << MSND_MIXER_PCM)
  42 #define MSND_MASK_SPEAKER       (1 << MSND_MIXER_SPEAKER)
  43 #define MSND_MASK_LINE          (1 << MSND_MIXER_LINE)
  44 #define MSND_MASK_MIC           (1 << MSND_MIXER_MIC)
  45 #define MSND_MASK_IMIX          (1 << MSND_MIXER_IMIX)
  46 #define MSND_MASK_RECLEV        (1 << MSND_MIXER_RECLEV)
  47 #define MSND_MASK_IGAIN         (1 << MSND_MIXER_IGAIN)
  48 #define MSND_MASK_OGAIN         (1 << MSND_MIXER_OGAIN)
  49 #define MSND_MASK_AUX           (1 << MSND_MIXER_AUX)
  50 #define MSND_MASK_DIGITAL       (1 << MSND_MIXER_DIGITAL)
  51 
  52 static int snd_msndmix_info_mux(struct snd_kcontrol *kcontrol,
  53                                 struct snd_ctl_elem_info *uinfo)
  54 {
  55         static const char * const texts[3] = {
  56                 "Analog", "MASS", "SPDIF",
  57         };
  58         struct snd_msnd *chip = snd_kcontrol_chip(kcontrol);
  59         unsigned items = test_bit(F_HAVEDIGITAL, &chip->flags) ? 3 : 2;
  60 
  61         return snd_ctl_enum_info(uinfo, 1, items, texts);
  62 }
  63 
  64 static int snd_msndmix_get_mux(struct snd_kcontrol *kcontrol,
  65                                 struct snd_ctl_elem_value *ucontrol)
  66 {
  67         struct snd_msnd *chip = snd_kcontrol_chip(kcontrol);
  68         /* MSND_MASK_IMIX is the default */
  69         ucontrol->value.enumerated.item[0] = 0;
  70 
  71         if (chip->recsrc & MSND_MASK_SYNTH) {
  72                 ucontrol->value.enumerated.item[0] = 1;
  73         } else if ((chip->recsrc & MSND_MASK_DIGITAL) &&
  74                  test_bit(F_HAVEDIGITAL, &chip->flags)) {
  75                 ucontrol->value.enumerated.item[0] = 2;
  76         }
  77 
  78 
  79         return 0;
  80 }
  81 
  82 static int snd_msndmix_set_mux(struct snd_msnd *chip, int val)
  83 {
  84         unsigned newrecsrc;
  85         int change;
  86         unsigned char msndbyte;
  87 
  88         switch (val) {
  89         case 0:
  90                 newrecsrc = MSND_MASK_IMIX;
  91                 msndbyte = HDEXAR_SET_ANA_IN;
  92                 break;
  93         case 1:
  94                 newrecsrc = MSND_MASK_SYNTH;
  95                 msndbyte = HDEXAR_SET_SYNTH_IN;
  96                 break;
  97         case 2:
  98                 newrecsrc = MSND_MASK_DIGITAL;
  99                 msndbyte = HDEXAR_SET_DAT_IN;
 100                 break;
 101         default:
 102                 return -EINVAL;
 103         }
 104         change  = newrecsrc != chip->recsrc;
 105         if (change) {
 106                 change = 0;
 107                 if (!snd_msnd_send_word(chip, 0, 0, msndbyte))
 108                         if (!snd_msnd_send_dsp_cmd(chip, HDEX_AUX_REQ)) {
 109                                 chip->recsrc = newrecsrc;
 110                                 change = 1;
 111                         }
 112         }
 113         return change;
 114 }
 115 
 116 static int snd_msndmix_put_mux(struct snd_kcontrol *kcontrol,
 117                                 struct snd_ctl_elem_value *ucontrol)
 118 {
 119         struct snd_msnd *msnd = snd_kcontrol_chip(kcontrol);
 120         return snd_msndmix_set_mux(msnd, ucontrol->value.enumerated.item[0]);
 121 }
 122 
 123 
 124 static int snd_msndmix_volume_info(struct snd_kcontrol *kcontrol,
 125                                    struct snd_ctl_elem_info *uinfo)
 126 {
 127         uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
 128         uinfo->count = 2;
 129         uinfo->value.integer.min = 0;
 130         uinfo->value.integer.max = 100;
 131         return 0;
 132 }
 133 
 134 static int snd_msndmix_volume_get(struct snd_kcontrol *kcontrol,
 135                                   struct snd_ctl_elem_value *ucontrol)
 136 {
 137         struct snd_msnd *msnd = snd_kcontrol_chip(kcontrol);
 138         int addr = kcontrol->private_value;
 139         unsigned long flags;
 140 
 141         spin_lock_irqsave(&msnd->mixer_lock, flags);
 142         ucontrol->value.integer.value[0] = msnd->left_levels[addr] * 100;
 143         ucontrol->value.integer.value[0] /= 0xFFFF;
 144         ucontrol->value.integer.value[1] = msnd->right_levels[addr] * 100;
 145         ucontrol->value.integer.value[1] /= 0xFFFF;
 146         spin_unlock_irqrestore(&msnd->mixer_lock, flags);
 147         return 0;
 148 }
 149 
 150 #define update_volm(a, b)                                               \
 151         do {                                                            \
 152                 writew((dev->left_levels[a] >> 1) *                     \
 153                        readw(dev->SMA + SMA_wCurrMastVolLeft) / 0xffff, \
 154                        dev->SMA + SMA_##b##Left);                       \
 155                 writew((dev->right_levels[a] >> 1)  *                   \
 156                        readw(dev->SMA + SMA_wCurrMastVolRight) / 0xffff, \
 157                        dev->SMA + SMA_##b##Right);                      \
 158         } while (0);
 159 
 160 #define update_potm(d, s, ar)                                           \
 161         do {                                                            \
 162                 writeb((dev->left_levels[d] >> 8) *                     \
 163                        readw(dev->SMA + SMA_wCurrMastVolLeft) / 0xffff, \
 164                        dev->SMA + SMA_##s##Left);                       \
 165                 writeb((dev->right_levels[d] >> 8) *                    \
 166                        readw(dev->SMA + SMA_wCurrMastVolRight) / 0xffff, \
 167                        dev->SMA + SMA_##s##Right);                      \
 168                 if (snd_msnd_send_word(dev, 0, 0, ar) == 0)             \
 169                         snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ);       \
 170         } while (0);
 171 
 172 #define update_pot(d, s, ar)                                            \
 173         do {                                                            \
 174                 writeb(dev->left_levels[d] >> 8,                        \
 175                        dev->SMA + SMA_##s##Left);                       \
 176                 writeb(dev->right_levels[d] >> 8,                       \
 177                        dev->SMA + SMA_##s##Right);                      \
 178                 if (snd_msnd_send_word(dev, 0, 0, ar) == 0)             \
 179                         snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ);       \
 180         } while (0);
 181 
 182 
 183 static int snd_msndmix_set(struct snd_msnd *dev, int d, int left, int right)
 184 {
 185         int bLeft, bRight;
 186         int wLeft, wRight;
 187         int updatemaster = 0;
 188 
 189         if (d >= LEVEL_ENTRIES)
 190                 return -EINVAL;
 191 
 192         bLeft = left * 0xff / 100;
 193         wLeft = left * 0xffff / 100;
 194 
 195         bRight = right * 0xff / 100;
 196         wRight = right * 0xffff / 100;
 197 
 198         dev->left_levels[d] = wLeft;
 199         dev->right_levels[d] = wRight;
 200 
 201         switch (d) {
 202                 /* master volume unscaled controls */
 203         case MSND_MIXER_LINE:                   /* line pot control */
 204                 /* scaled by IMIX in digital mix */
 205                 writeb(bLeft, dev->SMA + SMA_bInPotPosLeft);
 206                 writeb(bRight, dev->SMA + SMA_bInPotPosRight);
 207                 if (snd_msnd_send_word(dev, 0, 0, HDEXAR_IN_SET_POTS) == 0)
 208                         snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ);
 209                 break;
 210         case MSND_MIXER_MIC:                    /* mic pot control */
 211                 if (dev->type == msndClassic)
 212                         return -EINVAL;
 213                 /* scaled by IMIX in digital mix */
 214                 writeb(bLeft, dev->SMA + SMA_bMicPotPosLeft);
 215                 writeb(bRight, dev->SMA + SMA_bMicPotPosRight);
 216                 if (snd_msnd_send_word(dev, 0, 0, HDEXAR_MIC_SET_POTS) == 0)
 217                         snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ);
 218                 break;
 219         case MSND_MIXER_VOLUME:         /* master volume */
 220                 writew(wLeft, dev->SMA + SMA_wCurrMastVolLeft);
 221                 writew(wRight, dev->SMA + SMA_wCurrMastVolRight);
 222                 /* fall through */
 223 
 224         case MSND_MIXER_AUX:                    /* aux pot control */
 225                 /* scaled by master volume */
 226                 /* fall through */
 227 
 228                 /* digital controls */
 229         case MSND_MIXER_SYNTH:                  /* synth vol (dsp mix) */
 230         case MSND_MIXER_PCM:                    /* pcm vol (dsp mix) */
 231         case MSND_MIXER_IMIX:                   /* input monitor (dsp mix) */
 232                 /* scaled by master volume */
 233                 updatemaster = 1;
 234                 break;
 235 
 236         default:
 237                 return -EINVAL;
 238         }
 239 
 240         if (updatemaster) {
 241                 /* update master volume scaled controls */
 242                 update_volm(MSND_MIXER_PCM, wCurrPlayVol);
 243                 update_volm(MSND_MIXER_IMIX, wCurrInVol);
 244                 if (dev->type == msndPinnacle)
 245                         update_volm(MSND_MIXER_SYNTH, wCurrMHdrVol);
 246                 update_potm(MSND_MIXER_AUX, bAuxPotPos, HDEXAR_AUX_SET_POTS);
 247         }
 248 
 249         return 0;
 250 }
 251 
 252 static int snd_msndmix_volume_put(struct snd_kcontrol *kcontrol,
 253                                   struct snd_ctl_elem_value *ucontrol)
 254 {
 255         struct snd_msnd *msnd = snd_kcontrol_chip(kcontrol);
 256         int change, addr = kcontrol->private_value;
 257         int left, right;
 258         unsigned long flags;
 259 
 260         left = ucontrol->value.integer.value[0] % 101;
 261         right = ucontrol->value.integer.value[1] % 101;
 262         spin_lock_irqsave(&msnd->mixer_lock, flags);
 263         change = msnd->left_levels[addr] != left
 264                 || msnd->right_levels[addr] != right;
 265         snd_msndmix_set(msnd, addr, left, right);
 266         spin_unlock_irqrestore(&msnd->mixer_lock, flags);
 267         return change;
 268 }
 269 
 270 
 271 #define DUMMY_VOLUME(xname, xindex, addr) \
 272 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
 273   .info = snd_msndmix_volume_info, \
 274   .get = snd_msndmix_volume_get, .put = snd_msndmix_volume_put, \
 275   .private_value = addr }
 276 
 277 
 278 static struct snd_kcontrol_new snd_msnd_controls[] = {
 279 DUMMY_VOLUME("Master Volume", 0, MSND_MIXER_VOLUME),
 280 DUMMY_VOLUME("PCM Volume", 0, MSND_MIXER_PCM),
 281 DUMMY_VOLUME("Aux Volume", 0, MSND_MIXER_AUX),
 282 DUMMY_VOLUME("Line Volume", 0, MSND_MIXER_LINE),
 283 DUMMY_VOLUME("Mic Volume", 0, MSND_MIXER_MIC),
 284 DUMMY_VOLUME("Monitor", 0, MSND_MIXER_IMIX),
 285 {
 286         .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 287         .name = "Capture Source",
 288         .info = snd_msndmix_info_mux,
 289         .get = snd_msndmix_get_mux,
 290         .put = snd_msndmix_put_mux,
 291 }
 292 };
 293 
 294 
 295 int snd_msndmix_new(struct snd_card *card)
 296 {
 297         struct snd_msnd *chip = card->private_data;
 298         unsigned int idx;
 299         int err;
 300 
 301         if (snd_BUG_ON(!chip))
 302                 return -EINVAL;
 303         spin_lock_init(&chip->mixer_lock);
 304         strcpy(card->mixername, "MSND Pinnacle Mixer");
 305 
 306         for (idx = 0; idx < ARRAY_SIZE(snd_msnd_controls); idx++) {
 307                 err = snd_ctl_add(card,
 308                                   snd_ctl_new1(snd_msnd_controls + idx, chip));
 309                 if (err < 0)
 310                         return err;
 311         }
 312 
 313         return 0;
 314 }
 315 EXPORT_SYMBOL(snd_msndmix_new);
 316 
 317 void snd_msndmix_setup(struct snd_msnd *dev)
 318 {
 319         update_pot(MSND_MIXER_LINE, bInPotPos, HDEXAR_IN_SET_POTS);
 320         update_potm(MSND_MIXER_AUX, bAuxPotPos, HDEXAR_AUX_SET_POTS);
 321         update_volm(MSND_MIXER_PCM, wCurrPlayVol);
 322         update_volm(MSND_MIXER_IMIX, wCurrInVol);
 323         if (dev->type == msndPinnacle) {
 324                 update_pot(MSND_MIXER_MIC, bMicPotPos, HDEXAR_MIC_SET_POTS);
 325                 update_volm(MSND_MIXER_SYNTH, wCurrMHdrVol);
 326         }
 327 }
 328 EXPORT_SYMBOL(snd_msndmix_setup);
 329 
 330 int snd_msndmix_force_recsrc(struct snd_msnd *dev, int recsrc)
 331 {
 332         dev->recsrc = -1;
 333         return snd_msndmix_set_mux(dev, recsrc);
 334 }
 335 EXPORT_SYMBOL(snd_msndmix_force_recsrc);

/* [<][>][^][v][top][bottom][index][help] */