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