Path: blob/master/sound/isa/msnd/msnd_pinnacle_mixer.c
10817 views
/***************************************************************************1msnd_pinnacle_mixer.c - description2-------------------3begin : Fre Jun 7 20024copyright : (C) 2002 by karsten wiese5email : [email protected]6***************************************************************************/78/***************************************************************************9* *10* This program is free software; you can redistribute it and/or modify *11* it under the terms of the GNU General Public License as published by *12* the Free Software Foundation; either version 2 of the License, or *13* (at your option) any later version. *14* *15***************************************************************************/1617#include <linux/io.h>1819#include <sound/core.h>20#include <sound/control.h>21#include "msnd.h"22#include "msnd_pinnacle.h"232425#define MSND_MIXER_VOLUME 026#define MSND_MIXER_PCM 127#define MSND_MIXER_AUX 2 /* Input source 1 (aux1) */28#define MSND_MIXER_IMIX 3 /* Recording monitor */29#define MSND_MIXER_SYNTH 430#define MSND_MIXER_SPEAKER 531#define MSND_MIXER_LINE 632#define MSND_MIXER_MIC 733#define MSND_MIXER_RECLEV 11 /* Recording level */34#define MSND_MIXER_IGAIN 12 /* Input gain */35#define MSND_MIXER_OGAIN 13 /* Output gain */36#define MSND_MIXER_DIGITAL 17 /* Digital (input) 1 */3738/* Device mask bits */3940#define MSND_MASK_VOLUME (1 << MSND_MIXER_VOLUME)41#define MSND_MASK_SYNTH (1 << MSND_MIXER_SYNTH)42#define MSND_MASK_PCM (1 << MSND_MIXER_PCM)43#define MSND_MASK_SPEAKER (1 << MSND_MIXER_SPEAKER)44#define MSND_MASK_LINE (1 << MSND_MIXER_LINE)45#define MSND_MASK_MIC (1 << MSND_MIXER_MIC)46#define MSND_MASK_IMIX (1 << MSND_MIXER_IMIX)47#define MSND_MASK_RECLEV (1 << MSND_MIXER_RECLEV)48#define MSND_MASK_IGAIN (1 << MSND_MIXER_IGAIN)49#define MSND_MASK_OGAIN (1 << MSND_MIXER_OGAIN)50#define MSND_MASK_AUX (1 << MSND_MIXER_AUX)51#define MSND_MASK_DIGITAL (1 << MSND_MIXER_DIGITAL)5253static int snd_msndmix_info_mux(struct snd_kcontrol *kcontrol,54struct snd_ctl_elem_info *uinfo)55{56static char *texts[3] = {57"Analog", "MASS", "SPDIF",58};59struct snd_msnd *chip = snd_kcontrol_chip(kcontrol);60unsigned items = test_bit(F_HAVEDIGITAL, &chip->flags) ? 3 : 2;6162uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;63uinfo->count = 1;64uinfo->value.enumerated.items = items;65if (uinfo->value.enumerated.item >= items)66uinfo->value.enumerated.item = items - 1;67strcpy(uinfo->value.enumerated.name,68texts[uinfo->value.enumerated.item]);69return 0;70}7172static int snd_msndmix_get_mux(struct snd_kcontrol *kcontrol,73struct snd_ctl_elem_value *ucontrol)74{75struct snd_msnd *chip = snd_kcontrol_chip(kcontrol);76/* MSND_MASK_IMIX is the default */77ucontrol->value.enumerated.item[0] = 0;7879if (chip->recsrc & MSND_MASK_SYNTH) {80ucontrol->value.enumerated.item[0] = 1;81} else if ((chip->recsrc & MSND_MASK_DIGITAL) &&82test_bit(F_HAVEDIGITAL, &chip->flags)) {83ucontrol->value.enumerated.item[0] = 2;84}858687return 0;88}8990static int snd_msndmix_set_mux(struct snd_msnd *chip, int val)91{92unsigned newrecsrc;93int change;94unsigned char msndbyte;9596switch (val) {97case 0:98newrecsrc = MSND_MASK_IMIX;99msndbyte = HDEXAR_SET_ANA_IN;100break;101case 1:102newrecsrc = MSND_MASK_SYNTH;103msndbyte = HDEXAR_SET_SYNTH_IN;104break;105case 2:106newrecsrc = MSND_MASK_DIGITAL;107msndbyte = HDEXAR_SET_DAT_IN;108break;109default:110return -EINVAL;111}112change = newrecsrc != chip->recsrc;113if (change) {114change = 0;115if (!snd_msnd_send_word(chip, 0, 0, msndbyte))116if (!snd_msnd_send_dsp_cmd(chip, HDEX_AUX_REQ)) {117chip->recsrc = newrecsrc;118change = 1;119}120}121return change;122}123124static int snd_msndmix_put_mux(struct snd_kcontrol *kcontrol,125struct snd_ctl_elem_value *ucontrol)126{127struct snd_msnd *msnd = snd_kcontrol_chip(kcontrol);128return snd_msndmix_set_mux(msnd, ucontrol->value.enumerated.item[0]);129}130131132static int snd_msndmix_volume_info(struct snd_kcontrol *kcontrol,133struct snd_ctl_elem_info *uinfo)134{135uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;136uinfo->count = 2;137uinfo->value.integer.min = 0;138uinfo->value.integer.max = 100;139return 0;140}141142static int snd_msndmix_volume_get(struct snd_kcontrol *kcontrol,143struct snd_ctl_elem_value *ucontrol)144{145struct snd_msnd *msnd = snd_kcontrol_chip(kcontrol);146int addr = kcontrol->private_value;147unsigned long flags;148149spin_lock_irqsave(&msnd->mixer_lock, flags);150ucontrol->value.integer.value[0] = msnd->left_levels[addr] * 100;151ucontrol->value.integer.value[0] /= 0xFFFF;152ucontrol->value.integer.value[1] = msnd->right_levels[addr] * 100;153ucontrol->value.integer.value[1] /= 0xFFFF;154spin_unlock_irqrestore(&msnd->mixer_lock, flags);155return 0;156}157158#define update_volm(a, b) \159do { \160writew((dev->left_levels[a] >> 1) * \161readw(dev->SMA + SMA_wCurrMastVolLeft) / 0xffff, \162dev->SMA + SMA_##b##Left); \163writew((dev->right_levels[a] >> 1) * \164readw(dev->SMA + SMA_wCurrMastVolRight) / 0xffff, \165dev->SMA + SMA_##b##Right); \166} while (0);167168#define update_potm(d, s, ar) \169do { \170writeb((dev->left_levels[d] >> 8) * \171readw(dev->SMA + SMA_wCurrMastVolLeft) / 0xffff, \172dev->SMA + SMA_##s##Left); \173writeb((dev->right_levels[d] >> 8) * \174readw(dev->SMA + SMA_wCurrMastVolRight) / 0xffff, \175dev->SMA + SMA_##s##Right); \176if (snd_msnd_send_word(dev, 0, 0, ar) == 0) \177snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ); \178} while (0);179180#define update_pot(d, s, ar) \181do { \182writeb(dev->left_levels[d] >> 8, \183dev->SMA + SMA_##s##Left); \184writeb(dev->right_levels[d] >> 8, \185dev->SMA + SMA_##s##Right); \186if (snd_msnd_send_word(dev, 0, 0, ar) == 0) \187snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ); \188} while (0);189190191static int snd_msndmix_set(struct snd_msnd *dev, int d, int left, int right)192{193int bLeft, bRight;194int wLeft, wRight;195int updatemaster = 0;196197if (d >= LEVEL_ENTRIES)198return -EINVAL;199200bLeft = left * 0xff / 100;201wLeft = left * 0xffff / 100;202203bRight = right * 0xff / 100;204wRight = right * 0xffff / 100;205206dev->left_levels[d] = wLeft;207dev->right_levels[d] = wRight;208209switch (d) {210/* master volume unscaled controls */211case MSND_MIXER_LINE: /* line pot control */212/* scaled by IMIX in digital mix */213writeb(bLeft, dev->SMA + SMA_bInPotPosLeft);214writeb(bRight, dev->SMA + SMA_bInPotPosRight);215if (snd_msnd_send_word(dev, 0, 0, HDEXAR_IN_SET_POTS) == 0)216snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ);217break;218case MSND_MIXER_MIC: /* mic pot control */219if (dev->type == msndClassic)220return -EINVAL;221/* scaled by IMIX in digital mix */222writeb(bLeft, dev->SMA + SMA_bMicPotPosLeft);223writeb(bRight, dev->SMA + SMA_bMicPotPosRight);224if (snd_msnd_send_word(dev, 0, 0, HDEXAR_MIC_SET_POTS) == 0)225snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ);226break;227case MSND_MIXER_VOLUME: /* master volume */228writew(wLeft, dev->SMA + SMA_wCurrMastVolLeft);229writew(wRight, dev->SMA + SMA_wCurrMastVolRight);230/* fall through */231232case MSND_MIXER_AUX: /* aux pot control */233/* scaled by master volume */234/* fall through */235236/* digital controls */237case MSND_MIXER_SYNTH: /* synth vol (dsp mix) */238case MSND_MIXER_PCM: /* pcm vol (dsp mix) */239case MSND_MIXER_IMIX: /* input monitor (dsp mix) */240/* scaled by master volume */241updatemaster = 1;242break;243244default:245return -EINVAL;246}247248if (updatemaster) {249/* update master volume scaled controls */250update_volm(MSND_MIXER_PCM, wCurrPlayVol);251update_volm(MSND_MIXER_IMIX, wCurrInVol);252if (dev->type == msndPinnacle)253update_volm(MSND_MIXER_SYNTH, wCurrMHdrVol);254update_potm(MSND_MIXER_AUX, bAuxPotPos, HDEXAR_AUX_SET_POTS);255}256257return 0;258}259260static int snd_msndmix_volume_put(struct snd_kcontrol *kcontrol,261struct snd_ctl_elem_value *ucontrol)262{263struct snd_msnd *msnd = snd_kcontrol_chip(kcontrol);264int change, addr = kcontrol->private_value;265int left, right;266unsigned long flags;267268left = ucontrol->value.integer.value[0] % 101;269right = ucontrol->value.integer.value[1] % 101;270spin_lock_irqsave(&msnd->mixer_lock, flags);271change = msnd->left_levels[addr] != left272|| msnd->right_levels[addr] != right;273snd_msndmix_set(msnd, addr, left, right);274spin_unlock_irqrestore(&msnd->mixer_lock, flags);275return change;276}277278279#define DUMMY_VOLUME(xname, xindex, addr) \280{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \281.info = snd_msndmix_volume_info, \282.get = snd_msndmix_volume_get, .put = snd_msndmix_volume_put, \283.private_value = addr }284285286static struct snd_kcontrol_new snd_msnd_controls[] = {287DUMMY_VOLUME("Master Volume", 0, MSND_MIXER_VOLUME),288DUMMY_VOLUME("PCM Volume", 0, MSND_MIXER_PCM),289DUMMY_VOLUME("Aux Volume", 0, MSND_MIXER_AUX),290DUMMY_VOLUME("Line Volume", 0, MSND_MIXER_LINE),291DUMMY_VOLUME("Mic Volume", 0, MSND_MIXER_MIC),292DUMMY_VOLUME("Monitor", 0, MSND_MIXER_IMIX),293{294.iface = SNDRV_CTL_ELEM_IFACE_MIXER,295.name = "Capture Source",296.info = snd_msndmix_info_mux,297.get = snd_msndmix_get_mux,298.put = snd_msndmix_put_mux,299}300};301302303int __devinit snd_msndmix_new(struct snd_card *card)304{305struct snd_msnd *chip = card->private_data;306unsigned int idx;307int err;308309if (snd_BUG_ON(!chip))310return -EINVAL;311spin_lock_init(&chip->mixer_lock);312strcpy(card->mixername, "MSND Pinnacle Mixer");313314for (idx = 0; idx < ARRAY_SIZE(snd_msnd_controls); idx++)315err = snd_ctl_add(card,316snd_ctl_new1(snd_msnd_controls + idx, chip));317if (err < 0)318return err;319320return 0;321}322EXPORT_SYMBOL(snd_msndmix_new);323324void snd_msndmix_setup(struct snd_msnd *dev)325{326update_pot(MSND_MIXER_LINE, bInPotPos, HDEXAR_IN_SET_POTS);327update_potm(MSND_MIXER_AUX, bAuxPotPos, HDEXAR_AUX_SET_POTS);328update_volm(MSND_MIXER_PCM, wCurrPlayVol);329update_volm(MSND_MIXER_IMIX, wCurrInVol);330if (dev->type == msndPinnacle) {331update_pot(MSND_MIXER_MIC, bMicPotPos, HDEXAR_MIC_SET_POTS);332update_volm(MSND_MIXER_SYNTH, wCurrMHdrVol);333}334}335EXPORT_SYMBOL(snd_msndmix_setup);336337int snd_msndmix_force_recsrc(struct snd_msnd *dev, int recsrc)338{339dev->recsrc = -1;340return snd_msndmix_set_mux(dev, recsrc);341}342EXPORT_SYMBOL(snd_msndmix_force_recsrc);343344345