linux/sound/isa/msnd/msnd_pinnacle_mixer.c
<<
>>
Prefs
   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
  52static 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
  64static 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
  82static 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
 116static 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
 124static 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
 134static 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
 183static 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                fallthrough;
 223        case MSND_MIXER_AUX:                    /* aux pot control */
 224                /* scaled by master volume */
 225
 226                /* digital controls */
 227        case MSND_MIXER_SYNTH:                  /* synth vol (dsp mix) */
 228        case MSND_MIXER_PCM:                    /* pcm vol (dsp mix) */
 229        case MSND_MIXER_IMIX:                   /* input monitor (dsp mix) */
 230                /* scaled by master volume */
 231                updatemaster = 1;
 232                break;
 233
 234        default:
 235                return -EINVAL;
 236        }
 237
 238        if (updatemaster) {
 239                /* update master volume scaled controls */
 240                update_volm(MSND_MIXER_PCM, wCurrPlayVol);
 241                update_volm(MSND_MIXER_IMIX, wCurrInVol);
 242                if (dev->type == msndPinnacle)
 243                        update_volm(MSND_MIXER_SYNTH, wCurrMHdrVol);
 244                update_potm(MSND_MIXER_AUX, bAuxPotPos, HDEXAR_AUX_SET_POTS);
 245        }
 246
 247        return 0;
 248}
 249
 250static int snd_msndmix_volume_put(struct snd_kcontrol *kcontrol,
 251                                  struct snd_ctl_elem_value *ucontrol)
 252{
 253        struct snd_msnd *msnd = snd_kcontrol_chip(kcontrol);
 254        int change, addr = kcontrol->private_value;
 255        int left, right;
 256        unsigned long flags;
 257
 258        left = ucontrol->value.integer.value[0] % 101;
 259        right = ucontrol->value.integer.value[1] % 101;
 260        spin_lock_irqsave(&msnd->mixer_lock, flags);
 261        change = msnd->left_levels[addr] != left
 262                || msnd->right_levels[addr] != right;
 263        snd_msndmix_set(msnd, addr, left, right);
 264        spin_unlock_irqrestore(&msnd->mixer_lock, flags);
 265        return change;
 266}
 267
 268
 269#define DUMMY_VOLUME(xname, xindex, addr) \
 270{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
 271  .info = snd_msndmix_volume_info, \
 272  .get = snd_msndmix_volume_get, .put = snd_msndmix_volume_put, \
 273  .private_value = addr }
 274
 275
 276static const struct snd_kcontrol_new snd_msnd_controls[] = {
 277DUMMY_VOLUME("Master Volume", 0, MSND_MIXER_VOLUME),
 278DUMMY_VOLUME("PCM Volume", 0, MSND_MIXER_PCM),
 279DUMMY_VOLUME("Aux Volume", 0, MSND_MIXER_AUX),
 280DUMMY_VOLUME("Line Volume", 0, MSND_MIXER_LINE),
 281DUMMY_VOLUME("Mic Volume", 0, MSND_MIXER_MIC),
 282DUMMY_VOLUME("Monitor", 0, MSND_MIXER_IMIX),
 283{
 284        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 285        .name = "Capture Source",
 286        .info = snd_msndmix_info_mux,
 287        .get = snd_msndmix_get_mux,
 288        .put = snd_msndmix_put_mux,
 289}
 290};
 291
 292
 293int snd_msndmix_new(struct snd_card *card)
 294{
 295        struct snd_msnd *chip = card->private_data;
 296        unsigned int idx;
 297        int err;
 298
 299        if (snd_BUG_ON(!chip))
 300                return -EINVAL;
 301        spin_lock_init(&chip->mixer_lock);
 302        strcpy(card->mixername, "MSND Pinnacle Mixer");
 303
 304        for (idx = 0; idx < ARRAY_SIZE(snd_msnd_controls); idx++) {
 305                err = snd_ctl_add(card,
 306                                  snd_ctl_new1(snd_msnd_controls + idx, chip));
 307                if (err < 0)
 308                        return err;
 309        }
 310
 311        return 0;
 312}
 313EXPORT_SYMBOL(snd_msndmix_new);
 314
 315void snd_msndmix_setup(struct snd_msnd *dev)
 316{
 317        update_pot(MSND_MIXER_LINE, bInPotPos, HDEXAR_IN_SET_POTS);
 318        update_potm(MSND_MIXER_AUX, bAuxPotPos, HDEXAR_AUX_SET_POTS);
 319        update_volm(MSND_MIXER_PCM, wCurrPlayVol);
 320        update_volm(MSND_MIXER_IMIX, wCurrInVol);
 321        if (dev->type == msndPinnacle) {
 322                update_pot(MSND_MIXER_MIC, bMicPotPos, HDEXAR_MIC_SET_POTS);
 323                update_volm(MSND_MIXER_SYNTH, wCurrMHdrVol);
 324        }
 325}
 326EXPORT_SYMBOL(snd_msndmix_setup);
 327
 328int snd_msndmix_force_recsrc(struct snd_msnd *dev, int recsrc)
 329{
 330        dev->recsrc = -1;
 331        return snd_msndmix_set_mux(dev, recsrc);
 332}
 333EXPORT_SYMBOL(snd_msndmix_force_recsrc);
 334