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