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