mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-14 15:54:15 +08:00
2d3f481088
The FPGA access through the GPIO port does not interfere with other sound processor register access, so there is no need to subject it to emu_lock. And after moving all FPGA access out of the interrupt handler, it does not need to be IRQ-safe, either. What's more, attaching the dock causes a firmware upload, which takes several seconds. We really don't want to disable IRQs for this long, and even less also have someone else spin with IRQs disabled waiting for us. Therefore, use a mutex for FPGA access locking. This makes the code somewhat more noisy, as we need to wrap bigger sections into the mutex, as it needs to enclose the spinlocks. The latter has the "side effect" of fixing dock FPGA programming in a corner case: a really badly timed mixer access right between entering FPGA programming mode and uploading the netlist would mess up the protocol. Signed-off-by: Oswald Buddenhagen <oswald.buddenhagen@gmx.de> Signed-off-by: Takashi Iwai <tiwai@suse.de> Message-ID: <20240428093716.3198666-5-oswald.buddenhagen@gmx.de>
2391 lines
70 KiB
C
2391 lines
70 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>,
|
|
* Takashi Iwai <tiwai@suse.de>
|
|
* Lee Revell <rlrevell@joe-job.com>
|
|
* James Courtier-Dutton <James@superbug.co.uk>
|
|
* Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
|
|
* Creative Labs, Inc.
|
|
*
|
|
* Routines for control of EMU10K1 chips / mixer routines
|
|
*/
|
|
|
|
#include <linux/time.h>
|
|
#include <linux/init.h>
|
|
#include <sound/core.h>
|
|
#include <sound/emu10k1.h>
|
|
#include <linux/delay.h>
|
|
#include <sound/tlv.h>
|
|
|
|
#include "p17v.h"
|
|
|
|
#define AC97_ID_STAC9758 0x83847658
|
|
|
|
static const DECLARE_TLV_DB_SCALE(snd_audigy_db_scale2, -10350, 50, 1); /* WM8775 gain scale */
|
|
|
|
|
|
static int add_ctls(struct snd_emu10k1 *emu, const struct snd_kcontrol_new *tpl,
|
|
const char * const *ctls, unsigned nctls)
|
|
{
|
|
struct snd_kcontrol_new kctl = *tpl;
|
|
int err;
|
|
|
|
for (unsigned i = 0; i < nctls; i++) {
|
|
kctl.name = ctls[i];
|
|
kctl.private_value = i;
|
|
err = snd_ctl_add(emu->card, snd_ctl_new1(&kctl, emu));
|
|
if (err < 0)
|
|
return err;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int snd_emu10k1_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
|
|
{
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
|
|
uinfo->count = 1;
|
|
return 0;
|
|
}
|
|
|
|
static int snd_emu10k1_spdif_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
|
|
|
|
/* Limit: emu->spdif_bits */
|
|
if (idx >= 3)
|
|
return -EINVAL;
|
|
ucontrol->value.iec958.status[0] = (emu->spdif_bits[idx] >> 0) & 0xff;
|
|
ucontrol->value.iec958.status[1] = (emu->spdif_bits[idx] >> 8) & 0xff;
|
|
ucontrol->value.iec958.status[2] = (emu->spdif_bits[idx] >> 16) & 0xff;
|
|
ucontrol->value.iec958.status[3] = (emu->spdif_bits[idx] >> 24) & 0xff;
|
|
return 0;
|
|
}
|
|
|
|
static int snd_emu10k1_spdif_get_mask(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
ucontrol->value.iec958.status[0] = 0xff;
|
|
ucontrol->value.iec958.status[1] = 0xff;
|
|
ucontrol->value.iec958.status[2] = 0xff;
|
|
ucontrol->value.iec958.status[3] = 0xff;
|
|
return 0;
|
|
}
|
|
|
|
#define PAIR_PS(base, one, two, sfx) base " " one sfx, base " " two sfx
|
|
#define LR_PS(base, sfx) PAIR_PS(base, "Left", "Right", sfx)
|
|
|
|
#define ADAT_PS(pfx, sfx) \
|
|
pfx "ADAT 0" sfx, pfx "ADAT 1" sfx, pfx "ADAT 2" sfx, pfx "ADAT 3" sfx, \
|
|
pfx "ADAT 4" sfx, pfx "ADAT 5" sfx, pfx "ADAT 6" sfx, pfx "ADAT 7" sfx
|
|
|
|
#define PAIR_REGS(base, one, two) \
|
|
base ## one ## 1, \
|
|
base ## two ## 1
|
|
|
|
#define LR_REGS(base) PAIR_REGS(base, _LEFT, _RIGHT)
|
|
|
|
#define ADAT_REGS(base) \
|
|
base+0, base+1, base+2, base+3, base+4, base+5, base+6, base+7
|
|
|
|
/*
|
|
* List of data sources available for each destination
|
|
*/
|
|
|
|
#define DSP_TEXTS \
|
|
"DSP 0", "DSP 1", "DSP 2", "DSP 3", "DSP 4", "DSP 5", "DSP 6", "DSP 7", \
|
|
"DSP 8", "DSP 9", "DSP 10", "DSP 11", "DSP 12", "DSP 13", "DSP 14", "DSP 15", \
|
|
"DSP 16", "DSP 17", "DSP 18", "DSP 19", "DSP 20", "DSP 21", "DSP 22", "DSP 23", \
|
|
"DSP 24", "DSP 25", "DSP 26", "DSP 27", "DSP 28", "DSP 29", "DSP 30", "DSP 31"
|
|
|
|
#define PAIR_TEXTS(base, one, two) PAIR_PS(base, one, two, "")
|
|
#define LR_TEXTS(base) LR_PS(base, "")
|
|
#define ADAT_TEXTS(pfx) ADAT_PS(pfx, "")
|
|
|
|
#define EMU32_SRC_REGS \
|
|
EMU_SRC_ALICE_EMU32A, \
|
|
EMU_SRC_ALICE_EMU32A+1, \
|
|
EMU_SRC_ALICE_EMU32A+2, \
|
|
EMU_SRC_ALICE_EMU32A+3, \
|
|
EMU_SRC_ALICE_EMU32A+4, \
|
|
EMU_SRC_ALICE_EMU32A+5, \
|
|
EMU_SRC_ALICE_EMU32A+6, \
|
|
EMU_SRC_ALICE_EMU32A+7, \
|
|
EMU_SRC_ALICE_EMU32A+8, \
|
|
EMU_SRC_ALICE_EMU32A+9, \
|
|
EMU_SRC_ALICE_EMU32A+0xa, \
|
|
EMU_SRC_ALICE_EMU32A+0xb, \
|
|
EMU_SRC_ALICE_EMU32A+0xc, \
|
|
EMU_SRC_ALICE_EMU32A+0xd, \
|
|
EMU_SRC_ALICE_EMU32A+0xe, \
|
|
EMU_SRC_ALICE_EMU32A+0xf, \
|
|
EMU_SRC_ALICE_EMU32B, \
|
|
EMU_SRC_ALICE_EMU32B+1, \
|
|
EMU_SRC_ALICE_EMU32B+2, \
|
|
EMU_SRC_ALICE_EMU32B+3, \
|
|
EMU_SRC_ALICE_EMU32B+4, \
|
|
EMU_SRC_ALICE_EMU32B+5, \
|
|
EMU_SRC_ALICE_EMU32B+6, \
|
|
EMU_SRC_ALICE_EMU32B+7, \
|
|
EMU_SRC_ALICE_EMU32B+8, \
|
|
EMU_SRC_ALICE_EMU32B+9, \
|
|
EMU_SRC_ALICE_EMU32B+0xa, \
|
|
EMU_SRC_ALICE_EMU32B+0xb, \
|
|
EMU_SRC_ALICE_EMU32B+0xc, \
|
|
EMU_SRC_ALICE_EMU32B+0xd, \
|
|
EMU_SRC_ALICE_EMU32B+0xe, \
|
|
EMU_SRC_ALICE_EMU32B+0xf
|
|
|
|
/* 1010 rev1 */
|
|
|
|
#define EMU1010_COMMON_TEXTS \
|
|
"Silence", \
|
|
PAIR_TEXTS("Dock Mic", "A", "B"), \
|
|
LR_TEXTS("Dock ADC1"), \
|
|
LR_TEXTS("Dock ADC2"), \
|
|
LR_TEXTS("Dock ADC3"), \
|
|
LR_TEXTS("0202 ADC"), \
|
|
LR_TEXTS("1010 SPDIF"), \
|
|
ADAT_TEXTS("1010 ")
|
|
|
|
static const char * const emu1010_src_texts[] = {
|
|
EMU1010_COMMON_TEXTS,
|
|
DSP_TEXTS,
|
|
};
|
|
|
|
static const unsigned short emu1010_src_regs[] = {
|
|
EMU_SRC_SILENCE,
|
|
PAIR_REGS(EMU_SRC_DOCK_MIC, _A, _B),
|
|
LR_REGS(EMU_SRC_DOCK_ADC1),
|
|
LR_REGS(EMU_SRC_DOCK_ADC2),
|
|
LR_REGS(EMU_SRC_DOCK_ADC3),
|
|
LR_REGS(EMU_SRC_HAMOA_ADC),
|
|
LR_REGS(EMU_SRC_HANA_SPDIF),
|
|
ADAT_REGS(EMU_SRC_HANA_ADAT),
|
|
EMU32_SRC_REGS,
|
|
};
|
|
static_assert(ARRAY_SIZE(emu1010_src_regs) == ARRAY_SIZE(emu1010_src_texts));
|
|
|
|
/* 1010 rev2 */
|
|
|
|
#define EMU1010b_COMMON_TEXTS \
|
|
"Silence", \
|
|
PAIR_TEXTS("Dock Mic", "A", "B"), \
|
|
LR_TEXTS("Dock ADC1"), \
|
|
LR_TEXTS("Dock ADC2"), \
|
|
LR_TEXTS("0202 ADC"), \
|
|
LR_TEXTS("Dock SPDIF"), \
|
|
LR_TEXTS("1010 SPDIF"), \
|
|
ADAT_TEXTS("Dock "), \
|
|
ADAT_TEXTS("1010 ")
|
|
|
|
static const char * const emu1010b_src_texts[] = {
|
|
EMU1010b_COMMON_TEXTS,
|
|
DSP_TEXTS,
|
|
};
|
|
|
|
static const unsigned short emu1010b_src_regs[] = {
|
|
EMU_SRC_SILENCE,
|
|
PAIR_REGS(EMU_SRC_DOCK_MIC, _A, _B),
|
|
LR_REGS(EMU_SRC_DOCK_ADC1),
|
|
LR_REGS(EMU_SRC_DOCK_ADC2),
|
|
LR_REGS(EMU_SRC_HAMOA_ADC),
|
|
LR_REGS(EMU_SRC_MDOCK_SPDIF),
|
|
LR_REGS(EMU_SRC_HANA_SPDIF),
|
|
ADAT_REGS(EMU_SRC_MDOCK_ADAT),
|
|
ADAT_REGS(EMU_SRC_HANA_ADAT),
|
|
EMU32_SRC_REGS,
|
|
};
|
|
static_assert(ARRAY_SIZE(emu1010b_src_regs) == ARRAY_SIZE(emu1010b_src_texts));
|
|
|
|
/* 1616(m) cardbus */
|
|
|
|
#define EMU1616_COMMON_TEXTS \
|
|
"Silence", \
|
|
PAIR_TEXTS("Mic", "A", "B"), \
|
|
LR_TEXTS("ADC1"), \
|
|
LR_TEXTS("ADC2"), \
|
|
LR_TEXTS("SPDIF"), \
|
|
ADAT_TEXTS("")
|
|
|
|
static const char * const emu1616_src_texts[] = {
|
|
EMU1616_COMMON_TEXTS,
|
|
DSP_TEXTS,
|
|
};
|
|
|
|
static const unsigned short emu1616_src_regs[] = {
|
|
EMU_SRC_SILENCE,
|
|
PAIR_REGS(EMU_SRC_DOCK_MIC, _A, _B),
|
|
LR_REGS(EMU_SRC_DOCK_ADC1),
|
|
LR_REGS(EMU_SRC_DOCK_ADC2),
|
|
LR_REGS(EMU_SRC_MDOCK_SPDIF),
|
|
ADAT_REGS(EMU_SRC_MDOCK_ADAT),
|
|
EMU32_SRC_REGS,
|
|
};
|
|
static_assert(ARRAY_SIZE(emu1616_src_regs) == ARRAY_SIZE(emu1616_src_texts));
|
|
|
|
/* 0404 rev1 & rev2 */
|
|
|
|
#define EMU0404_COMMON_TEXTS \
|
|
"Silence", \
|
|
LR_TEXTS("ADC"), \
|
|
LR_TEXTS("SPDIF")
|
|
|
|
static const char * const emu0404_src_texts[] = {
|
|
EMU0404_COMMON_TEXTS,
|
|
DSP_TEXTS,
|
|
};
|
|
|
|
static const unsigned short emu0404_src_regs[] = {
|
|
EMU_SRC_SILENCE,
|
|
LR_REGS(EMU_SRC_HAMOA_ADC),
|
|
LR_REGS(EMU_SRC_HANA_SPDIF),
|
|
EMU32_SRC_REGS,
|
|
};
|
|
static_assert(ARRAY_SIZE(emu0404_src_regs) == ARRAY_SIZE(emu0404_src_texts));
|
|
|
|
/*
|
|
* Data destinations - physical EMU outputs.
|
|
* Each destination has an enum mixer control to choose a data source
|
|
*/
|
|
|
|
#define LR_CTLS(base) LR_PS(base, " Playback Enum")
|
|
#define ADAT_CTLS(pfx) ADAT_PS(pfx, " Playback Enum")
|
|
|
|
/* 1010 rev1 */
|
|
|
|
static const char * const emu1010_output_texts[] = {
|
|
LR_CTLS("Dock DAC1"),
|
|
LR_CTLS("Dock DAC2"),
|
|
LR_CTLS("Dock DAC3"),
|
|
LR_CTLS("Dock DAC4"),
|
|
LR_CTLS("Dock Phones"),
|
|
LR_CTLS("Dock SPDIF"),
|
|
LR_CTLS("0202 DAC"),
|
|
LR_CTLS("1010 SPDIF"),
|
|
ADAT_CTLS("1010 "),
|
|
};
|
|
static_assert(ARRAY_SIZE(emu1010_output_texts) <= NUM_OUTPUT_DESTS);
|
|
|
|
static const unsigned short emu1010_output_dst[] = {
|
|
LR_REGS(EMU_DST_DOCK_DAC1),
|
|
LR_REGS(EMU_DST_DOCK_DAC2),
|
|
LR_REGS(EMU_DST_DOCK_DAC3),
|
|
LR_REGS(EMU_DST_DOCK_DAC4),
|
|
LR_REGS(EMU_DST_DOCK_PHONES),
|
|
LR_REGS(EMU_DST_DOCK_SPDIF),
|
|
LR_REGS(EMU_DST_HAMOA_DAC),
|
|
LR_REGS(EMU_DST_HANA_SPDIF),
|
|
ADAT_REGS(EMU_DST_HANA_ADAT),
|
|
};
|
|
static_assert(ARRAY_SIZE(emu1010_output_dst) == ARRAY_SIZE(emu1010_output_texts));
|
|
|
|
static const unsigned short emu1010_output_dflt[] = {
|
|
EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
|
|
EMU_SRC_ALICE_EMU32A+2, EMU_SRC_ALICE_EMU32A+3,
|
|
EMU_SRC_ALICE_EMU32A+4, EMU_SRC_ALICE_EMU32A+5,
|
|
EMU_SRC_ALICE_EMU32A+6, EMU_SRC_ALICE_EMU32A+7,
|
|
EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
|
|
EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
|
|
EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
|
|
EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
|
|
EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1, EMU_SRC_ALICE_EMU32A+2, EMU_SRC_ALICE_EMU32A+3,
|
|
EMU_SRC_ALICE_EMU32A+4, EMU_SRC_ALICE_EMU32A+5, EMU_SRC_ALICE_EMU32A+6, EMU_SRC_ALICE_EMU32A+7,
|
|
};
|
|
static_assert(ARRAY_SIZE(emu1010_output_dflt) == ARRAY_SIZE(emu1010_output_dst));
|
|
|
|
/* 1010 rev2 */
|
|
|
|
static const char * const snd_emu1010b_output_texts[] = {
|
|
LR_CTLS("Dock DAC1"),
|
|
LR_CTLS("Dock DAC2"),
|
|
LR_CTLS("Dock DAC3"),
|
|
LR_CTLS("Dock SPDIF"),
|
|
ADAT_CTLS("Dock "),
|
|
LR_CTLS("0202 DAC"),
|
|
LR_CTLS("1010 SPDIF"),
|
|
ADAT_CTLS("1010 "),
|
|
};
|
|
static_assert(ARRAY_SIZE(snd_emu1010b_output_texts) <= NUM_OUTPUT_DESTS);
|
|
|
|
static const unsigned short emu1010b_output_dst[] = {
|
|
LR_REGS(EMU_DST_DOCK_DAC1),
|
|
LR_REGS(EMU_DST_DOCK_DAC2),
|
|
LR_REGS(EMU_DST_DOCK_DAC3),
|
|
LR_REGS(EMU_DST_MDOCK_SPDIF),
|
|
ADAT_REGS(EMU_DST_MDOCK_ADAT),
|
|
LR_REGS(EMU_DST_HAMOA_DAC),
|
|
LR_REGS(EMU_DST_HANA_SPDIF),
|
|
ADAT_REGS(EMU_DST_HANA_ADAT),
|
|
};
|
|
static_assert(ARRAY_SIZE(emu1010b_output_dst) == ARRAY_SIZE(snd_emu1010b_output_texts));
|
|
|
|
static const unsigned short emu1010b_output_dflt[] = {
|
|
EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
|
|
EMU_SRC_ALICE_EMU32A+2, EMU_SRC_ALICE_EMU32A+3,
|
|
EMU_SRC_ALICE_EMU32A+4, EMU_SRC_ALICE_EMU32A+5,
|
|
EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
|
|
EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1, EMU_SRC_ALICE_EMU32A+2, EMU_SRC_ALICE_EMU32A+3,
|
|
EMU_SRC_ALICE_EMU32A+4, EMU_SRC_ALICE_EMU32A+5, EMU_SRC_ALICE_EMU32A+6, EMU_SRC_ALICE_EMU32A+7,
|
|
EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
|
|
EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
|
|
EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1, EMU_SRC_ALICE_EMU32A+2, EMU_SRC_ALICE_EMU32A+3,
|
|
EMU_SRC_ALICE_EMU32A+4, EMU_SRC_ALICE_EMU32A+5, EMU_SRC_ALICE_EMU32A+6, EMU_SRC_ALICE_EMU32A+7,
|
|
};
|
|
|
|
/* 1616(m) cardbus */
|
|
|
|
static const char * const snd_emu1616_output_texts[] = {
|
|
LR_CTLS("Dock DAC1"),
|
|
LR_CTLS("Dock DAC2"),
|
|
LR_CTLS("Dock DAC3"),
|
|
LR_CTLS("Dock SPDIF"),
|
|
ADAT_CTLS("Dock "),
|
|
LR_CTLS("Mana DAC"),
|
|
};
|
|
static_assert(ARRAY_SIZE(snd_emu1616_output_texts) <= NUM_OUTPUT_DESTS);
|
|
|
|
static const unsigned short emu1616_output_dst[] = {
|
|
LR_REGS(EMU_DST_DOCK_DAC1),
|
|
LR_REGS(EMU_DST_DOCK_DAC2),
|
|
LR_REGS(EMU_DST_DOCK_DAC3),
|
|
LR_REGS(EMU_DST_MDOCK_SPDIF),
|
|
ADAT_REGS(EMU_DST_MDOCK_ADAT),
|
|
EMU_DST_MANA_DAC_LEFT, EMU_DST_MANA_DAC_RIGHT,
|
|
};
|
|
static_assert(ARRAY_SIZE(emu1616_output_dst) == ARRAY_SIZE(snd_emu1616_output_texts));
|
|
|
|
static const unsigned short emu1616_output_dflt[] = {
|
|
EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
|
|
EMU_SRC_ALICE_EMU32A+2, EMU_SRC_ALICE_EMU32A+3,
|
|
EMU_SRC_ALICE_EMU32A+4, EMU_SRC_ALICE_EMU32A+5,
|
|
EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
|
|
EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1, EMU_SRC_ALICE_EMU32A+2, EMU_SRC_ALICE_EMU32A+3,
|
|
EMU_SRC_ALICE_EMU32A+4, EMU_SRC_ALICE_EMU32A+5, EMU_SRC_ALICE_EMU32A+6, EMU_SRC_ALICE_EMU32A+7,
|
|
EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
|
|
};
|
|
static_assert(ARRAY_SIZE(emu1616_output_dflt) == ARRAY_SIZE(emu1616_output_dst));
|
|
|
|
/* 0404 rev1 & rev2 */
|
|
|
|
static const char * const snd_emu0404_output_texts[] = {
|
|
LR_CTLS("DAC"),
|
|
LR_CTLS("SPDIF"),
|
|
};
|
|
static_assert(ARRAY_SIZE(snd_emu0404_output_texts) <= NUM_OUTPUT_DESTS);
|
|
|
|
static const unsigned short emu0404_output_dst[] = {
|
|
LR_REGS(EMU_DST_HAMOA_DAC),
|
|
LR_REGS(EMU_DST_HANA_SPDIF),
|
|
};
|
|
static_assert(ARRAY_SIZE(emu0404_output_dst) == ARRAY_SIZE(snd_emu0404_output_texts));
|
|
|
|
static const unsigned short emu0404_output_dflt[] = {
|
|
EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
|
|
EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
|
|
};
|
|
static_assert(ARRAY_SIZE(emu0404_output_dflt) == ARRAY_SIZE(emu0404_output_dst));
|
|
|
|
/*
|
|
* Data destinations - FPGA outputs going to Alice2 (Audigy) for
|
|
* capture (EMU32 + I2S links)
|
|
* Each destination has an enum mixer control to choose a data source
|
|
*/
|
|
|
|
static const char * const emu1010_input_texts[] = {
|
|
"DSP 0 Capture Enum",
|
|
"DSP 1 Capture Enum",
|
|
"DSP 2 Capture Enum",
|
|
"DSP 3 Capture Enum",
|
|
"DSP 4 Capture Enum",
|
|
"DSP 5 Capture Enum",
|
|
"DSP 6 Capture Enum",
|
|
"DSP 7 Capture Enum",
|
|
"DSP 8 Capture Enum",
|
|
"DSP 9 Capture Enum",
|
|
"DSP A Capture Enum",
|
|
"DSP B Capture Enum",
|
|
"DSP C Capture Enum",
|
|
"DSP D Capture Enum",
|
|
"DSP E Capture Enum",
|
|
"DSP F Capture Enum",
|
|
/* These exist only on rev1 EMU1010 cards. */
|
|
"DSP 10 Capture Enum",
|
|
"DSP 11 Capture Enum",
|
|
"DSP 12 Capture Enum",
|
|
"DSP 13 Capture Enum",
|
|
"DSP 14 Capture Enum",
|
|
"DSP 15 Capture Enum",
|
|
};
|
|
static_assert(ARRAY_SIZE(emu1010_input_texts) <= NUM_INPUT_DESTS);
|
|
|
|
static const unsigned short emu1010_input_dst[] = {
|
|
EMU_DST_ALICE2_EMU32_0,
|
|
EMU_DST_ALICE2_EMU32_1,
|
|
EMU_DST_ALICE2_EMU32_2,
|
|
EMU_DST_ALICE2_EMU32_3,
|
|
EMU_DST_ALICE2_EMU32_4,
|
|
EMU_DST_ALICE2_EMU32_5,
|
|
EMU_DST_ALICE2_EMU32_6,
|
|
EMU_DST_ALICE2_EMU32_7,
|
|
EMU_DST_ALICE2_EMU32_8,
|
|
EMU_DST_ALICE2_EMU32_9,
|
|
EMU_DST_ALICE2_EMU32_A,
|
|
EMU_DST_ALICE2_EMU32_B,
|
|
EMU_DST_ALICE2_EMU32_C,
|
|
EMU_DST_ALICE2_EMU32_D,
|
|
EMU_DST_ALICE2_EMU32_E,
|
|
EMU_DST_ALICE2_EMU32_F,
|
|
/* These exist only on rev1 EMU1010 cards. */
|
|
EMU_DST_ALICE_I2S0_LEFT,
|
|
EMU_DST_ALICE_I2S0_RIGHT,
|
|
EMU_DST_ALICE_I2S1_LEFT,
|
|
EMU_DST_ALICE_I2S1_RIGHT,
|
|
EMU_DST_ALICE_I2S2_LEFT,
|
|
EMU_DST_ALICE_I2S2_RIGHT,
|
|
};
|
|
static_assert(ARRAY_SIZE(emu1010_input_dst) == ARRAY_SIZE(emu1010_input_texts));
|
|
|
|
static const unsigned short emu1010_input_dflt[] = {
|
|
EMU_SRC_DOCK_MIC_A1,
|
|
EMU_SRC_DOCK_MIC_B1,
|
|
EMU_SRC_HAMOA_ADC_LEFT1,
|
|
EMU_SRC_HAMOA_ADC_RIGHT1,
|
|
EMU_SRC_DOCK_ADC1_LEFT1,
|
|
EMU_SRC_DOCK_ADC1_RIGHT1,
|
|
EMU_SRC_DOCK_ADC2_LEFT1,
|
|
EMU_SRC_DOCK_ADC2_RIGHT1,
|
|
/* Pavel Hofman - setting defaults for all capture channels.
|
|
* Defaults only, users will set their own values anyways, let's
|
|
* just copy/paste. */
|
|
EMU_SRC_DOCK_MIC_A1,
|
|
EMU_SRC_DOCK_MIC_B1,
|
|
EMU_SRC_HAMOA_ADC_LEFT1,
|
|
EMU_SRC_HAMOA_ADC_RIGHT1,
|
|
EMU_SRC_DOCK_ADC1_LEFT1,
|
|
EMU_SRC_DOCK_ADC1_RIGHT1,
|
|
EMU_SRC_DOCK_ADC2_LEFT1,
|
|
EMU_SRC_DOCK_ADC2_RIGHT1,
|
|
|
|
EMU_SRC_DOCK_ADC1_LEFT1,
|
|
EMU_SRC_DOCK_ADC1_RIGHT1,
|
|
EMU_SRC_DOCK_ADC2_LEFT1,
|
|
EMU_SRC_DOCK_ADC2_RIGHT1,
|
|
EMU_SRC_DOCK_ADC3_LEFT1,
|
|
EMU_SRC_DOCK_ADC3_RIGHT1,
|
|
};
|
|
static_assert(ARRAY_SIZE(emu1010_input_dflt) == ARRAY_SIZE(emu1010_input_dst));
|
|
|
|
static const unsigned short emu0404_input_dflt[] = {
|
|
EMU_SRC_HAMOA_ADC_LEFT1,
|
|
EMU_SRC_HAMOA_ADC_RIGHT1,
|
|
EMU_SRC_SILENCE,
|
|
EMU_SRC_SILENCE,
|
|
EMU_SRC_SILENCE,
|
|
EMU_SRC_SILENCE,
|
|
EMU_SRC_SILENCE,
|
|
EMU_SRC_SILENCE,
|
|
EMU_SRC_HANA_SPDIF_LEFT1,
|
|
EMU_SRC_HANA_SPDIF_RIGHT1,
|
|
EMU_SRC_SILENCE,
|
|
EMU_SRC_SILENCE,
|
|
EMU_SRC_SILENCE,
|
|
EMU_SRC_SILENCE,
|
|
EMU_SRC_SILENCE,
|
|
EMU_SRC_SILENCE,
|
|
};
|
|
|
|
struct snd_emu1010_routing_info {
|
|
const char * const *src_texts;
|
|
const char * const *out_texts;
|
|
const unsigned short *src_regs;
|
|
const unsigned short *out_regs;
|
|
const unsigned short *in_regs;
|
|
const unsigned short *out_dflts;
|
|
const unsigned short *in_dflts;
|
|
unsigned n_srcs;
|
|
unsigned n_outs;
|
|
unsigned n_ins;
|
|
};
|
|
|
|
static const struct snd_emu1010_routing_info emu1010_routing_info[] = {
|
|
{
|
|
/* rev1 1010 */
|
|
.src_regs = emu1010_src_regs,
|
|
.src_texts = emu1010_src_texts,
|
|
.n_srcs = ARRAY_SIZE(emu1010_src_texts),
|
|
|
|
.out_dflts = emu1010_output_dflt,
|
|
.out_regs = emu1010_output_dst,
|
|
.out_texts = emu1010_output_texts,
|
|
.n_outs = ARRAY_SIZE(emu1010_output_dst),
|
|
|
|
.in_dflts = emu1010_input_dflt,
|
|
.in_regs = emu1010_input_dst,
|
|
.n_ins = ARRAY_SIZE(emu1010_input_dst),
|
|
},
|
|
{
|
|
/* rev2 1010 */
|
|
.src_regs = emu1010b_src_regs,
|
|
.src_texts = emu1010b_src_texts,
|
|
.n_srcs = ARRAY_SIZE(emu1010b_src_texts),
|
|
|
|
.out_dflts = emu1010b_output_dflt,
|
|
.out_regs = emu1010b_output_dst,
|
|
.out_texts = snd_emu1010b_output_texts,
|
|
.n_outs = ARRAY_SIZE(emu1010b_output_dst),
|
|
|
|
.in_dflts = emu1010_input_dflt,
|
|
.in_regs = emu1010_input_dst,
|
|
.n_ins = ARRAY_SIZE(emu1010_input_dst) - 6,
|
|
},
|
|
{
|
|
/* 1616(m) cardbus */
|
|
.src_regs = emu1616_src_regs,
|
|
.src_texts = emu1616_src_texts,
|
|
.n_srcs = ARRAY_SIZE(emu1616_src_texts),
|
|
|
|
.out_dflts = emu1616_output_dflt,
|
|
.out_regs = emu1616_output_dst,
|
|
.out_texts = snd_emu1616_output_texts,
|
|
.n_outs = ARRAY_SIZE(emu1616_output_dst),
|
|
|
|
.in_dflts = emu1010_input_dflt,
|
|
.in_regs = emu1010_input_dst,
|
|
.n_ins = ARRAY_SIZE(emu1010_input_dst) - 6,
|
|
},
|
|
{
|
|
/* 0404 */
|
|
.src_regs = emu0404_src_regs,
|
|
.src_texts = emu0404_src_texts,
|
|
.n_srcs = ARRAY_SIZE(emu0404_src_texts),
|
|
|
|
.out_dflts = emu0404_output_dflt,
|
|
.out_regs = emu0404_output_dst,
|
|
.out_texts = snd_emu0404_output_texts,
|
|
.n_outs = ARRAY_SIZE(emu0404_output_dflt),
|
|
|
|
.in_dflts = emu0404_input_dflt,
|
|
.in_regs = emu1010_input_dst,
|
|
.n_ins = ARRAY_SIZE(emu1010_input_dst) - 6,
|
|
},
|
|
};
|
|
|
|
static unsigned emu1010_idx(struct snd_emu10k1 *emu)
|
|
{
|
|
return emu->card_capabilities->emu_model - 1;
|
|
}
|
|
|
|
static void snd_emu1010_output_source_apply(struct snd_emu10k1 *emu,
|
|
int channel, int src)
|
|
{
|
|
const struct snd_emu1010_routing_info *emu_ri =
|
|
&emu1010_routing_info[emu1010_idx(emu)];
|
|
|
|
snd_emu1010_fpga_link_dst_src_write(emu,
|
|
emu_ri->out_regs[channel], emu_ri->src_regs[src]);
|
|
}
|
|
|
|
static void snd_emu1010_input_source_apply(struct snd_emu10k1 *emu,
|
|
int channel, int src)
|
|
{
|
|
const struct snd_emu1010_routing_info *emu_ri =
|
|
&emu1010_routing_info[emu1010_idx(emu)];
|
|
|
|
snd_emu1010_fpga_link_dst_src_write(emu,
|
|
emu_ri->in_regs[channel], emu_ri->src_regs[src]);
|
|
}
|
|
|
|
static void snd_emu1010_apply_sources(struct snd_emu10k1 *emu)
|
|
{
|
|
const struct snd_emu1010_routing_info *emu_ri =
|
|
&emu1010_routing_info[emu1010_idx(emu)];
|
|
|
|
for (unsigned i = 0; i < emu_ri->n_outs; i++)
|
|
snd_emu1010_output_source_apply(
|
|
emu, i, emu->emu1010.output_source[i]);
|
|
for (unsigned i = 0; i < emu_ri->n_ins; i++)
|
|
snd_emu1010_input_source_apply(
|
|
emu, i, emu->emu1010.input_source[i]);
|
|
}
|
|
|
|
static u8 emu1010_map_source(const struct snd_emu1010_routing_info *emu_ri,
|
|
unsigned val)
|
|
{
|
|
for (unsigned i = 0; i < emu_ri->n_srcs; i++)
|
|
if (val == emu_ri->src_regs[i])
|
|
return i;
|
|
return 0;
|
|
}
|
|
|
|
static int snd_emu1010_input_output_source_info(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_info *uinfo)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
const struct snd_emu1010_routing_info *emu_ri =
|
|
&emu1010_routing_info[emu1010_idx(emu)];
|
|
|
|
return snd_ctl_enum_info(uinfo, 1, emu_ri->n_srcs, emu_ri->src_texts);
|
|
}
|
|
|
|
static int snd_emu1010_output_source_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
const struct snd_emu1010_routing_info *emu_ri =
|
|
&emu1010_routing_info[emu1010_idx(emu)];
|
|
unsigned channel = kcontrol->private_value;
|
|
|
|
if (channel >= emu_ri->n_outs)
|
|
return -EINVAL;
|
|
ucontrol->value.enumerated.item[0] = emu->emu1010.output_source[channel];
|
|
return 0;
|
|
}
|
|
|
|
static int snd_emu1010_output_source_put(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
const struct snd_emu1010_routing_info *emu_ri =
|
|
&emu1010_routing_info[emu1010_idx(emu)];
|
|
unsigned val = ucontrol->value.enumerated.item[0];
|
|
unsigned channel = kcontrol->private_value;
|
|
int change;
|
|
|
|
if (val >= emu_ri->n_srcs)
|
|
return -EINVAL;
|
|
if (channel >= emu_ri->n_outs)
|
|
return -EINVAL;
|
|
change = (emu->emu1010.output_source[channel] != val);
|
|
if (change) {
|
|
emu->emu1010.output_source[channel] = val;
|
|
snd_emu1010_fpga_lock(emu);
|
|
snd_emu1010_output_source_apply(emu, channel, val);
|
|
snd_emu1010_fpga_unlock(emu);
|
|
}
|
|
return change;
|
|
}
|
|
|
|
static const struct snd_kcontrol_new emu1010_output_source_ctl = {
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
|
|
.info = snd_emu1010_input_output_source_info,
|
|
.get = snd_emu1010_output_source_get,
|
|
.put = snd_emu1010_output_source_put
|
|
};
|
|
|
|
static int snd_emu1010_input_source_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
const struct snd_emu1010_routing_info *emu_ri =
|
|
&emu1010_routing_info[emu1010_idx(emu)];
|
|
unsigned channel = kcontrol->private_value;
|
|
|
|
if (channel >= emu_ri->n_ins)
|
|
return -EINVAL;
|
|
ucontrol->value.enumerated.item[0] = emu->emu1010.input_source[channel];
|
|
return 0;
|
|
}
|
|
|
|
static int snd_emu1010_input_source_put(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
const struct snd_emu1010_routing_info *emu_ri =
|
|
&emu1010_routing_info[emu1010_idx(emu)];
|
|
unsigned val = ucontrol->value.enumerated.item[0];
|
|
unsigned channel = kcontrol->private_value;
|
|
int change;
|
|
|
|
if (val >= emu_ri->n_srcs)
|
|
return -EINVAL;
|
|
if (channel >= emu_ri->n_ins)
|
|
return -EINVAL;
|
|
change = (emu->emu1010.input_source[channel] != val);
|
|
if (change) {
|
|
emu->emu1010.input_source[channel] = val;
|
|
snd_emu1010_fpga_lock(emu);
|
|
snd_emu1010_input_source_apply(emu, channel, val);
|
|
snd_emu1010_fpga_unlock(emu);
|
|
}
|
|
return change;
|
|
}
|
|
|
|
static const struct snd_kcontrol_new emu1010_input_source_ctl = {
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
|
|
.info = snd_emu1010_input_output_source_info,
|
|
.get = snd_emu1010_input_source_get,
|
|
.put = snd_emu1010_input_source_put
|
|
};
|
|
|
|
static int add_emu1010_source_mixers(struct snd_emu10k1 *emu)
|
|
{
|
|
const struct snd_emu1010_routing_info *emu_ri =
|
|
&emu1010_routing_info[emu1010_idx(emu)];
|
|
int err;
|
|
|
|
err = add_ctls(emu, &emu1010_output_source_ctl,
|
|
emu_ri->out_texts, emu_ri->n_outs);
|
|
if (err < 0)
|
|
return err;
|
|
err = add_ctls(emu, &emu1010_input_source_ctl,
|
|
emu1010_input_texts, emu_ri->n_ins);
|
|
return err;
|
|
}
|
|
|
|
|
|
static const char * const snd_emu1010_adc_pads[] = {
|
|
"ADC1 14dB PAD 0202 Capture Switch",
|
|
"ADC1 14dB PAD Audio Dock Capture Switch",
|
|
"ADC2 14dB PAD Audio Dock Capture Switch",
|
|
"ADC3 14dB PAD Audio Dock Capture Switch",
|
|
};
|
|
|
|
static const unsigned short snd_emu1010_adc_pad_regs[] = {
|
|
EMU_HANA_0202_ADC_PAD1,
|
|
EMU_HANA_DOCK_ADC_PAD1,
|
|
EMU_HANA_DOCK_ADC_PAD2,
|
|
EMU_HANA_DOCK_ADC_PAD3,
|
|
};
|
|
|
|
#define snd_emu1010_adc_pads_info snd_ctl_boolean_mono_info
|
|
|
|
static int snd_emu1010_adc_pads_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
unsigned int mask = snd_emu1010_adc_pad_regs[kcontrol->private_value];
|
|
|
|
ucontrol->value.integer.value[0] = (emu->emu1010.adc_pads & mask) ? 1 : 0;
|
|
return 0;
|
|
}
|
|
|
|
static int snd_emu1010_adc_pads_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
unsigned int mask = snd_emu1010_adc_pad_regs[kcontrol->private_value];
|
|
unsigned int val, cache;
|
|
int change;
|
|
|
|
val = ucontrol->value.integer.value[0];
|
|
cache = emu->emu1010.adc_pads;
|
|
if (val == 1)
|
|
cache = cache | mask;
|
|
else
|
|
cache = cache & ~mask;
|
|
change = (cache != emu->emu1010.adc_pads);
|
|
if (change) {
|
|
snd_emu1010_fpga_write_lock(emu, EMU_HANA_ADC_PADS, cache );
|
|
emu->emu1010.adc_pads = cache;
|
|
}
|
|
|
|
return change;
|
|
}
|
|
|
|
static const struct snd_kcontrol_new emu1010_adc_pads_ctl = {
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
|
|
.info = snd_emu1010_adc_pads_info,
|
|
.get = snd_emu1010_adc_pads_get,
|
|
.put = snd_emu1010_adc_pads_put
|
|
};
|
|
|
|
|
|
static const char * const snd_emu1010_dac_pads[] = {
|
|
"DAC1 0202 14dB PAD Playback Switch",
|
|
"DAC1 Audio Dock 14dB PAD Playback Switch",
|
|
"DAC2 Audio Dock 14dB PAD Playback Switch",
|
|
"DAC3 Audio Dock 14dB PAD Playback Switch",
|
|
"DAC4 Audio Dock 14dB PAD Playback Switch",
|
|
};
|
|
|
|
static const unsigned short snd_emu1010_dac_regs[] = {
|
|
EMU_HANA_0202_DAC_PAD1,
|
|
EMU_HANA_DOCK_DAC_PAD1,
|
|
EMU_HANA_DOCK_DAC_PAD2,
|
|
EMU_HANA_DOCK_DAC_PAD3,
|
|
EMU_HANA_DOCK_DAC_PAD4,
|
|
};
|
|
|
|
#define snd_emu1010_dac_pads_info snd_ctl_boolean_mono_info
|
|
|
|
static int snd_emu1010_dac_pads_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
unsigned int mask = snd_emu1010_dac_regs[kcontrol->private_value];
|
|
|
|
ucontrol->value.integer.value[0] = (emu->emu1010.dac_pads & mask) ? 1 : 0;
|
|
return 0;
|
|
}
|
|
|
|
static int snd_emu1010_dac_pads_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
unsigned int mask = snd_emu1010_dac_regs[kcontrol->private_value];
|
|
unsigned int val, cache;
|
|
int change;
|
|
|
|
val = ucontrol->value.integer.value[0];
|
|
cache = emu->emu1010.dac_pads;
|
|
if (val == 1)
|
|
cache = cache | mask;
|
|
else
|
|
cache = cache & ~mask;
|
|
change = (cache != emu->emu1010.dac_pads);
|
|
if (change) {
|
|
snd_emu1010_fpga_write_lock(emu, EMU_HANA_DAC_PADS, cache );
|
|
emu->emu1010.dac_pads = cache;
|
|
}
|
|
|
|
return change;
|
|
}
|
|
|
|
static const struct snd_kcontrol_new emu1010_dac_pads_ctl = {
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
|
|
.info = snd_emu1010_dac_pads_info,
|
|
.get = snd_emu1010_dac_pads_get,
|
|
.put = snd_emu1010_dac_pads_put
|
|
};
|
|
|
|
|
|
struct snd_emu1010_pads_info {
|
|
const char * const *adc_ctls, * const *dac_ctls;
|
|
unsigned n_adc_ctls, n_dac_ctls;
|
|
};
|
|
|
|
static const struct snd_emu1010_pads_info emu1010_pads_info[] = {
|
|
{
|
|
/* rev1 1010 */
|
|
.adc_ctls = snd_emu1010_adc_pads,
|
|
.n_adc_ctls = ARRAY_SIZE(snd_emu1010_adc_pads),
|
|
.dac_ctls = snd_emu1010_dac_pads,
|
|
.n_dac_ctls = ARRAY_SIZE(snd_emu1010_dac_pads),
|
|
},
|
|
{
|
|
/* rev2 1010 */
|
|
.adc_ctls = snd_emu1010_adc_pads,
|
|
.n_adc_ctls = ARRAY_SIZE(snd_emu1010_adc_pads) - 1,
|
|
.dac_ctls = snd_emu1010_dac_pads,
|
|
.n_dac_ctls = ARRAY_SIZE(snd_emu1010_dac_pads) - 1,
|
|
},
|
|
{
|
|
/* 1616(m) cardbus */
|
|
.adc_ctls = snd_emu1010_adc_pads + 1,
|
|
.n_adc_ctls = ARRAY_SIZE(snd_emu1010_adc_pads) - 2,
|
|
.dac_ctls = snd_emu1010_dac_pads + 1,
|
|
.n_dac_ctls = ARRAY_SIZE(snd_emu1010_dac_pads) - 2,
|
|
},
|
|
{
|
|
/* 0404 */
|
|
.adc_ctls = NULL,
|
|
.n_adc_ctls = 0,
|
|
.dac_ctls = NULL,
|
|
.n_dac_ctls = 0,
|
|
},
|
|
};
|
|
|
|
static const char * const emu1010_clock_texts[] = {
|
|
"44100", "48000", "SPDIF", "ADAT", "Dock", "BNC"
|
|
};
|
|
|
|
static const u8 emu1010_clock_vals[] = {
|
|
EMU_HANA_WCLOCK_INT_44_1K,
|
|
EMU_HANA_WCLOCK_INT_48K,
|
|
EMU_HANA_WCLOCK_HANA_SPDIF_IN,
|
|
EMU_HANA_WCLOCK_HANA_ADAT_IN,
|
|
EMU_HANA_WCLOCK_2ND_HANA,
|
|
EMU_HANA_WCLOCK_SYNC_BNC,
|
|
};
|
|
|
|
static const char * const emu0404_clock_texts[] = {
|
|
"44100", "48000", "SPDIF", "BNC"
|
|
};
|
|
|
|
static const u8 emu0404_clock_vals[] = {
|
|
EMU_HANA_WCLOCK_INT_44_1K,
|
|
EMU_HANA_WCLOCK_INT_48K,
|
|
EMU_HANA_WCLOCK_HANA_SPDIF_IN,
|
|
EMU_HANA_WCLOCK_SYNC_BNC,
|
|
};
|
|
|
|
struct snd_emu1010_clock_info {
|
|
const char * const *texts;
|
|
const u8 *vals;
|
|
unsigned num;
|
|
};
|
|
|
|
static const struct snd_emu1010_clock_info emu1010_clock_info[] = {
|
|
{
|
|
// rev1 1010
|
|
.texts = emu1010_clock_texts,
|
|
.vals = emu1010_clock_vals,
|
|
.num = ARRAY_SIZE(emu1010_clock_vals),
|
|
},
|
|
{
|
|
// rev2 1010
|
|
.texts = emu1010_clock_texts,
|
|
.vals = emu1010_clock_vals,
|
|
.num = ARRAY_SIZE(emu1010_clock_vals) - 1,
|
|
},
|
|
{
|
|
// 1616(m) CardBus
|
|
.texts = emu1010_clock_texts,
|
|
// TODO: determine what is actually available.
|
|
// Pedantically, *every* source comes from the 2nd FPGA, as the
|
|
// card itself has no own (digital) audio ports. The user manual
|
|
// claims that ADAT and S/PDIF clock sources are separate, which
|
|
// can mean two things: either E-MU mapped the dock's sources to
|
|
// the primary ones, or they determine the meaning of the "Dock"
|
|
// source depending on how the ports are actually configured
|
|
// (which the 2nd FPGA must be doing anyway).
|
|
.vals = emu1010_clock_vals,
|
|
.num = ARRAY_SIZE(emu1010_clock_vals),
|
|
},
|
|
{
|
|
// 0404
|
|
.texts = emu0404_clock_texts,
|
|
.vals = emu0404_clock_vals,
|
|
.num = ARRAY_SIZE(emu0404_clock_vals),
|
|
},
|
|
};
|
|
|
|
static int snd_emu1010_clock_source_info(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_info *uinfo)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
const struct snd_emu1010_clock_info *emu_ci =
|
|
&emu1010_clock_info[emu1010_idx(emu)];
|
|
|
|
return snd_ctl_enum_info(uinfo, 1, emu_ci->num, emu_ci->texts);
|
|
}
|
|
|
|
static int snd_emu1010_clock_source_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
|
|
ucontrol->value.enumerated.item[0] = emu->emu1010.clock_source;
|
|
return 0;
|
|
}
|
|
|
|
static int snd_emu1010_clock_source_put(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
const struct snd_emu1010_clock_info *emu_ci =
|
|
&emu1010_clock_info[emu1010_idx(emu)];
|
|
unsigned int val;
|
|
int change = 0;
|
|
|
|
val = ucontrol->value.enumerated.item[0] ;
|
|
if (val >= emu_ci->num)
|
|
return -EINVAL;
|
|
snd_emu1010_fpga_lock(emu);
|
|
spin_lock_irq(&emu->reg_lock);
|
|
change = (emu->emu1010.clock_source != val);
|
|
if (change) {
|
|
emu->emu1010.clock_source = val;
|
|
emu->emu1010.wclock = emu_ci->vals[val];
|
|
snd_emu1010_update_clock(emu);
|
|
|
|
snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_MUTE);
|
|
snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, emu->emu1010.wclock);
|
|
spin_unlock_irq(&emu->reg_lock);
|
|
|
|
msleep(10); // Allow DLL to settle
|
|
snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE);
|
|
} else {
|
|
spin_unlock_irq(&emu->reg_lock);
|
|
}
|
|
snd_emu1010_fpga_unlock(emu);
|
|
return change;
|
|
}
|
|
|
|
static const struct snd_kcontrol_new snd_emu1010_clock_source =
|
|
{
|
|
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
.name = "Clock Source",
|
|
.count = 1,
|
|
.info = snd_emu1010_clock_source_info,
|
|
.get = snd_emu1010_clock_source_get,
|
|
.put = snd_emu1010_clock_source_put
|
|
};
|
|
|
|
static int snd_emu1010_clock_fallback_info(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_info *uinfo)
|
|
{
|
|
static const char * const texts[2] = {
|
|
"44100", "48000"
|
|
};
|
|
|
|
return snd_ctl_enum_info(uinfo, 1, 2, texts);
|
|
}
|
|
|
|
static int snd_emu1010_clock_fallback_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
|
|
ucontrol->value.enumerated.item[0] = emu->emu1010.clock_fallback;
|
|
return 0;
|
|
}
|
|
|
|
static int snd_emu1010_clock_fallback_put(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
unsigned int val = ucontrol->value.enumerated.item[0];
|
|
int change;
|
|
|
|
if (val >= 2)
|
|
return -EINVAL;
|
|
change = (emu->emu1010.clock_fallback != val);
|
|
if (change) {
|
|
emu->emu1010.clock_fallback = val;
|
|
snd_emu1010_fpga_write_lock(emu, EMU_HANA_DEFCLOCK, 1 - val);
|
|
}
|
|
return change;
|
|
}
|
|
|
|
static const struct snd_kcontrol_new snd_emu1010_clock_fallback =
|
|
{
|
|
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
.name = "Clock Fallback",
|
|
.count = 1,
|
|
.info = snd_emu1010_clock_fallback_info,
|
|
.get = snd_emu1010_clock_fallback_get,
|
|
.put = snd_emu1010_clock_fallback_put
|
|
};
|
|
|
|
static int snd_emu1010_optical_out_info(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_info *uinfo)
|
|
{
|
|
static const char * const texts[2] = {
|
|
"SPDIF", "ADAT"
|
|
};
|
|
|
|
return snd_ctl_enum_info(uinfo, 1, 2, texts);
|
|
}
|
|
|
|
static int snd_emu1010_optical_out_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
|
|
ucontrol->value.enumerated.item[0] = emu->emu1010.optical_out;
|
|
return 0;
|
|
}
|
|
|
|
static int snd_emu1010_optical_out_put(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
unsigned int val;
|
|
u32 tmp;
|
|
int change = 0;
|
|
|
|
val = ucontrol->value.enumerated.item[0];
|
|
/* Limit: uinfo->value.enumerated.items = 2; */
|
|
if (val >= 2)
|
|
return -EINVAL;
|
|
change = (emu->emu1010.optical_out != val);
|
|
if (change) {
|
|
emu->emu1010.optical_out = val;
|
|
tmp = (emu->emu1010.optical_in ? EMU_HANA_OPTICAL_IN_ADAT : EMU_HANA_OPTICAL_IN_SPDIF) |
|
|
(emu->emu1010.optical_out ? EMU_HANA_OPTICAL_OUT_ADAT : EMU_HANA_OPTICAL_OUT_SPDIF);
|
|
snd_emu1010_fpga_write_lock(emu, EMU_HANA_OPTICAL_TYPE, tmp);
|
|
}
|
|
return change;
|
|
}
|
|
|
|
static const struct snd_kcontrol_new snd_emu1010_optical_out = {
|
|
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
.name = "Optical Output Mode",
|
|
.count = 1,
|
|
.info = snd_emu1010_optical_out_info,
|
|
.get = snd_emu1010_optical_out_get,
|
|
.put = snd_emu1010_optical_out_put
|
|
};
|
|
|
|
static int snd_emu1010_optical_in_info(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_info *uinfo)
|
|
{
|
|
static const char * const texts[2] = {
|
|
"SPDIF", "ADAT"
|
|
};
|
|
|
|
return snd_ctl_enum_info(uinfo, 1, 2, texts);
|
|
}
|
|
|
|
static int snd_emu1010_optical_in_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
|
|
ucontrol->value.enumerated.item[0] = emu->emu1010.optical_in;
|
|
return 0;
|
|
}
|
|
|
|
static int snd_emu1010_optical_in_put(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
unsigned int val;
|
|
u32 tmp;
|
|
int change = 0;
|
|
|
|
val = ucontrol->value.enumerated.item[0];
|
|
/* Limit: uinfo->value.enumerated.items = 2; */
|
|
if (val >= 2)
|
|
return -EINVAL;
|
|
change = (emu->emu1010.optical_in != val);
|
|
if (change) {
|
|
emu->emu1010.optical_in = val;
|
|
tmp = (emu->emu1010.optical_in ? EMU_HANA_OPTICAL_IN_ADAT : EMU_HANA_OPTICAL_IN_SPDIF) |
|
|
(emu->emu1010.optical_out ? EMU_HANA_OPTICAL_OUT_ADAT : EMU_HANA_OPTICAL_OUT_SPDIF);
|
|
snd_emu1010_fpga_write_lock(emu, EMU_HANA_OPTICAL_TYPE, tmp);
|
|
}
|
|
return change;
|
|
}
|
|
|
|
static const struct snd_kcontrol_new snd_emu1010_optical_in = {
|
|
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
.name = "Optical Input Mode",
|
|
.count = 1,
|
|
.info = snd_emu1010_optical_in_info,
|
|
.get = snd_emu1010_optical_in_get,
|
|
.put = snd_emu1010_optical_in_put
|
|
};
|
|
|
|
static int snd_audigy_i2c_capture_source_info(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_info *uinfo)
|
|
{
|
|
#if 0
|
|
static const char * const texts[4] = {
|
|
"Unknown1", "Unknown2", "Mic", "Line"
|
|
};
|
|
#endif
|
|
static const char * const texts[2] = {
|
|
"Mic", "Line"
|
|
};
|
|
|
|
return snd_ctl_enum_info(uinfo, 1, 2, texts);
|
|
}
|
|
|
|
static int snd_audigy_i2c_capture_source_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
|
|
ucontrol->value.enumerated.item[0] = emu->i2c_capture_source;
|
|
return 0;
|
|
}
|
|
|
|
static int snd_audigy_i2c_capture_source_put(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
unsigned int source_id;
|
|
unsigned int ngain, ogain;
|
|
u16 gpio;
|
|
int change = 0;
|
|
u32 source;
|
|
/* If the capture source has changed,
|
|
* update the capture volume from the cached value
|
|
* for the particular source.
|
|
*/
|
|
source_id = ucontrol->value.enumerated.item[0];
|
|
/* Limit: uinfo->value.enumerated.items = 2; */
|
|
/* emu->i2c_capture_volume */
|
|
if (source_id >= 2)
|
|
return -EINVAL;
|
|
change = (emu->i2c_capture_source != source_id);
|
|
if (change) {
|
|
snd_emu10k1_i2c_write(emu, ADC_MUX, 0); /* Mute input */
|
|
spin_lock_irq(&emu->emu_lock);
|
|
gpio = inw(emu->port + A_IOCFG);
|
|
if (source_id==0)
|
|
outw(gpio | 0x4, emu->port + A_IOCFG);
|
|
else
|
|
outw(gpio & ~0x4, emu->port + A_IOCFG);
|
|
spin_unlock_irq(&emu->emu_lock);
|
|
|
|
ngain = emu->i2c_capture_volume[source_id][0]; /* Left */
|
|
ogain = emu->i2c_capture_volume[emu->i2c_capture_source][0]; /* Left */
|
|
if (ngain != ogain)
|
|
snd_emu10k1_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff));
|
|
ngain = emu->i2c_capture_volume[source_id][1]; /* Right */
|
|
ogain = emu->i2c_capture_volume[emu->i2c_capture_source][1]; /* Right */
|
|
if (ngain != ogain)
|
|
snd_emu10k1_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff));
|
|
|
|
source = 1 << (source_id + 2);
|
|
snd_emu10k1_i2c_write(emu, ADC_MUX, source); /* Set source */
|
|
emu->i2c_capture_source = source_id;
|
|
}
|
|
return change;
|
|
}
|
|
|
|
static const struct snd_kcontrol_new snd_audigy_i2c_capture_source =
|
|
{
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
.name = "Capture Source",
|
|
.info = snd_audigy_i2c_capture_source_info,
|
|
.get = snd_audigy_i2c_capture_source_get,
|
|
.put = snd_audigy_i2c_capture_source_put
|
|
};
|
|
|
|
static int snd_audigy_i2c_volume_info(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_info *uinfo)
|
|
{
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
|
uinfo->count = 2;
|
|
uinfo->value.integer.min = 0;
|
|
uinfo->value.integer.max = 255;
|
|
return 0;
|
|
}
|
|
|
|
static int snd_audigy_i2c_volume_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
unsigned int source_id;
|
|
|
|
source_id = kcontrol->private_value;
|
|
/* Limit: emu->i2c_capture_volume */
|
|
/* capture_source: uinfo->value.enumerated.items = 2 */
|
|
if (source_id >= 2)
|
|
return -EINVAL;
|
|
|
|
ucontrol->value.integer.value[0] = emu->i2c_capture_volume[source_id][0];
|
|
ucontrol->value.integer.value[1] = emu->i2c_capture_volume[source_id][1];
|
|
return 0;
|
|
}
|
|
|
|
static int snd_audigy_i2c_volume_put(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
unsigned int ogain;
|
|
unsigned int ngain0, ngain1;
|
|
unsigned int source_id;
|
|
int change = 0;
|
|
|
|
source_id = kcontrol->private_value;
|
|
/* Limit: emu->i2c_capture_volume */
|
|
/* capture_source: uinfo->value.enumerated.items = 2 */
|
|
if (source_id >= 2)
|
|
return -EINVAL;
|
|
ngain0 = ucontrol->value.integer.value[0];
|
|
ngain1 = ucontrol->value.integer.value[1];
|
|
if (ngain0 > 0xff)
|
|
return -EINVAL;
|
|
if (ngain1 > 0xff)
|
|
return -EINVAL;
|
|
ogain = emu->i2c_capture_volume[source_id][0]; /* Left */
|
|
if (ogain != ngain0) {
|
|
if (emu->i2c_capture_source == source_id)
|
|
snd_emu10k1_i2c_write(emu, ADC_ATTEN_ADCL, ngain0);
|
|
emu->i2c_capture_volume[source_id][0] = ngain0;
|
|
change = 1;
|
|
}
|
|
ogain = emu->i2c_capture_volume[source_id][1]; /* Right */
|
|
if (ogain != ngain1) {
|
|
if (emu->i2c_capture_source == source_id)
|
|
snd_emu10k1_i2c_write(emu, ADC_ATTEN_ADCR, ngain1);
|
|
emu->i2c_capture_volume[source_id][1] = ngain1;
|
|
change = 1;
|
|
}
|
|
|
|
return change;
|
|
}
|
|
|
|
static const struct snd_kcontrol_new i2c_volume_ctl = {
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
|
|
SNDRV_CTL_ELEM_ACCESS_TLV_READ,
|
|
.info = snd_audigy_i2c_volume_info,
|
|
.get = snd_audigy_i2c_volume_get,
|
|
.put = snd_audigy_i2c_volume_put,
|
|
.tlv = { .p = snd_audigy_db_scale2 }
|
|
};
|
|
|
|
static const char * const snd_audigy_i2c_volume_ctls[] = {
|
|
"Mic Capture Volume",
|
|
"Line Capture Volume",
|
|
};
|
|
|
|
#if 0
|
|
static int snd_audigy_spdif_output_rate_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
|
|
{
|
|
static const char * const texts[] = {"44100", "48000", "96000"};
|
|
|
|
return snd_ctl_enum_info(uinfo, 1, 3, texts);
|
|
}
|
|
|
|
static int snd_audigy_spdif_output_rate_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
unsigned int tmp;
|
|
|
|
tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0);
|
|
switch (tmp & A_SPDIF_RATE_MASK) {
|
|
case A_SPDIF_44100:
|
|
ucontrol->value.enumerated.item[0] = 0;
|
|
break;
|
|
case A_SPDIF_48000:
|
|
ucontrol->value.enumerated.item[0] = 1;
|
|
break;
|
|
case A_SPDIF_96000:
|
|
ucontrol->value.enumerated.item[0] = 2;
|
|
break;
|
|
default:
|
|
ucontrol->value.enumerated.item[0] = 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int snd_audigy_spdif_output_rate_put(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
int change;
|
|
unsigned int reg, val, tmp;
|
|
|
|
switch(ucontrol->value.enumerated.item[0]) {
|
|
case 0:
|
|
val = A_SPDIF_44100;
|
|
break;
|
|
case 1:
|
|
val = A_SPDIF_48000;
|
|
break;
|
|
case 2:
|
|
val = A_SPDIF_96000;
|
|
break;
|
|
default:
|
|
val = A_SPDIF_48000;
|
|
break;
|
|
}
|
|
|
|
|
|
spin_lock_irq(&emu->reg_lock);
|
|
reg = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0);
|
|
tmp = reg & ~A_SPDIF_RATE_MASK;
|
|
tmp |= val;
|
|
change = (tmp != reg);
|
|
if (change)
|
|
snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, 0, tmp);
|
|
spin_unlock_irq(&emu->reg_lock);
|
|
return change;
|
|
}
|
|
|
|
static const struct snd_kcontrol_new snd_audigy_spdif_output_rate =
|
|
{
|
|
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
.name = "Audigy SPDIF Output Sample Rate",
|
|
.count = 1,
|
|
.info = snd_audigy_spdif_output_rate_info,
|
|
.get = snd_audigy_spdif_output_rate_get,
|
|
.put = snd_audigy_spdif_output_rate_put
|
|
};
|
|
#endif
|
|
|
|
static int snd_emu10k1_spdif_put(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
|
|
int change;
|
|
unsigned int val;
|
|
|
|
/* Limit: emu->spdif_bits */
|
|
if (idx >= 3)
|
|
return -EINVAL;
|
|
val = (ucontrol->value.iec958.status[0] << 0) |
|
|
(ucontrol->value.iec958.status[1] << 8) |
|
|
(ucontrol->value.iec958.status[2] << 16) |
|
|
(ucontrol->value.iec958.status[3] << 24);
|
|
change = val != emu->spdif_bits[idx];
|
|
if (change) {
|
|
snd_emu10k1_ptr_write(emu, SPCS0 + idx, 0, val);
|
|
emu->spdif_bits[idx] = val;
|
|
}
|
|
return change;
|
|
}
|
|
|
|
static const struct snd_kcontrol_new snd_emu10k1_spdif_mask_control =
|
|
{
|
|
.access = SNDRV_CTL_ELEM_ACCESS_READ,
|
|
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
|
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
|
|
.count = 3,
|
|
.info = snd_emu10k1_spdif_info,
|
|
.get = snd_emu10k1_spdif_get_mask
|
|
};
|
|
|
|
static const struct snd_kcontrol_new snd_emu10k1_spdif_control =
|
|
{
|
|
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
|
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
|
|
.count = 3,
|
|
.info = snd_emu10k1_spdif_info,
|
|
.get = snd_emu10k1_spdif_get,
|
|
.put = snd_emu10k1_spdif_put
|
|
};
|
|
|
|
|
|
static void update_emu10k1_fxrt(struct snd_emu10k1 *emu, int voice, unsigned char *route)
|
|
{
|
|
if (emu->audigy) {
|
|
snd_emu10k1_ptr_write_multiple(emu, voice,
|
|
A_FXRT1, snd_emu10k1_compose_audigy_fxrt1(route),
|
|
A_FXRT2, snd_emu10k1_compose_audigy_fxrt2(route),
|
|
REGLIST_END);
|
|
} else {
|
|
snd_emu10k1_ptr_write(emu, FXRT, voice,
|
|
snd_emu10k1_compose_send_routing(route));
|
|
}
|
|
}
|
|
|
|
static void update_emu10k1_send_volume(struct snd_emu10k1 *emu, int voice, unsigned char *volume)
|
|
{
|
|
snd_emu10k1_ptr_write(emu, PTRX_FXSENDAMOUNT_A, voice, volume[0]);
|
|
snd_emu10k1_ptr_write(emu, PTRX_FXSENDAMOUNT_B, voice, volume[1]);
|
|
snd_emu10k1_ptr_write(emu, PSST_FXSENDAMOUNT_C, voice, volume[2]);
|
|
snd_emu10k1_ptr_write(emu, DSL_FXSENDAMOUNT_D, voice, volume[3]);
|
|
if (emu->audigy) {
|
|
snd_emu10k1_ptr_write(emu, A_SENDAMOUNTS, voice,
|
|
snd_emu10k1_compose_audigy_sendamounts(volume));
|
|
}
|
|
}
|
|
|
|
/* PCM stream controls */
|
|
|
|
static int snd_emu10k1_send_routing_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
|
uinfo->count = emu->audigy ? 3*8 : 3*4;
|
|
uinfo->value.integer.min = 0;
|
|
uinfo->value.integer.max = emu->audigy ? 0x3f : 0x0f;
|
|
return 0;
|
|
}
|
|
|
|
static int snd_emu10k1_send_routing_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
struct snd_emu10k1_pcm_mixer *mix =
|
|
&emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
|
|
int voice, idx;
|
|
int num_efx = emu->audigy ? 8 : 4;
|
|
int mask = emu->audigy ? 0x3f : 0x0f;
|
|
|
|
for (voice = 0; voice < 3; voice++)
|
|
for (idx = 0; idx < num_efx; idx++)
|
|
ucontrol->value.integer.value[(voice * num_efx) + idx] =
|
|
mix->send_routing[voice][idx] & mask;
|
|
return 0;
|
|
}
|
|
|
|
static int snd_emu10k1_send_routing_put(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
struct snd_emu10k1_pcm_mixer *mix =
|
|
&emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
|
|
int change = 0, voice, idx, val;
|
|
int num_efx = emu->audigy ? 8 : 4;
|
|
int mask = emu->audigy ? 0x3f : 0x0f;
|
|
|
|
spin_lock_irq(&emu->reg_lock);
|
|
for (voice = 0; voice < 3; voice++)
|
|
for (idx = 0; idx < num_efx; idx++) {
|
|
val = ucontrol->value.integer.value[(voice * num_efx) + idx] & mask;
|
|
if (mix->send_routing[voice][idx] != val) {
|
|
mix->send_routing[voice][idx] = val;
|
|
change = 1;
|
|
}
|
|
}
|
|
if (change && mix->epcm && mix->epcm->voices[0]) {
|
|
if (!mix->epcm->voices[0]->last) {
|
|
update_emu10k1_fxrt(emu, mix->epcm->voices[0]->number,
|
|
&mix->send_routing[1][0]);
|
|
update_emu10k1_fxrt(emu, mix->epcm->voices[0]->number + 1,
|
|
&mix->send_routing[2][0]);
|
|
} else {
|
|
update_emu10k1_fxrt(emu, mix->epcm->voices[0]->number,
|
|
&mix->send_routing[0][0]);
|
|
}
|
|
}
|
|
spin_unlock_irq(&emu->reg_lock);
|
|
return change;
|
|
}
|
|
|
|
static const struct snd_kcontrol_new snd_emu10k1_send_routing_control =
|
|
{
|
|
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
|
|
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
|
.name = "EMU10K1 PCM Send Routing",
|
|
.count = 32,
|
|
.info = snd_emu10k1_send_routing_info,
|
|
.get = snd_emu10k1_send_routing_get,
|
|
.put = snd_emu10k1_send_routing_put
|
|
};
|
|
|
|
static int snd_emu10k1_send_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
|
uinfo->count = emu->audigy ? 3*8 : 3*4;
|
|
uinfo->value.integer.min = 0;
|
|
uinfo->value.integer.max = 255;
|
|
return 0;
|
|
}
|
|
|
|
static int snd_emu10k1_send_volume_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
struct snd_emu10k1_pcm_mixer *mix =
|
|
&emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
|
|
int idx;
|
|
int num_efx = emu->audigy ? 8 : 4;
|
|
|
|
for (idx = 0; idx < 3*num_efx; idx++)
|
|
ucontrol->value.integer.value[idx] = mix->send_volume[idx/num_efx][idx%num_efx];
|
|
return 0;
|
|
}
|
|
|
|
static int snd_emu10k1_send_volume_put(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
struct snd_emu10k1_pcm_mixer *mix =
|
|
&emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
|
|
int change = 0, idx, val;
|
|
int num_efx = emu->audigy ? 8 : 4;
|
|
|
|
spin_lock_irq(&emu->reg_lock);
|
|
for (idx = 0; idx < 3*num_efx; idx++) {
|
|
val = ucontrol->value.integer.value[idx] & 255;
|
|
if (mix->send_volume[idx/num_efx][idx%num_efx] != val) {
|
|
mix->send_volume[idx/num_efx][idx%num_efx] = val;
|
|
change = 1;
|
|
}
|
|
}
|
|
if (change && mix->epcm && mix->epcm->voices[0]) {
|
|
if (!mix->epcm->voices[0]->last) {
|
|
update_emu10k1_send_volume(emu, mix->epcm->voices[0]->number,
|
|
&mix->send_volume[1][0]);
|
|
update_emu10k1_send_volume(emu, mix->epcm->voices[0]->number + 1,
|
|
&mix->send_volume[2][0]);
|
|
} else {
|
|
update_emu10k1_send_volume(emu, mix->epcm->voices[0]->number,
|
|
&mix->send_volume[0][0]);
|
|
}
|
|
}
|
|
spin_unlock_irq(&emu->reg_lock);
|
|
return change;
|
|
}
|
|
|
|
static const struct snd_kcontrol_new snd_emu10k1_send_volume_control =
|
|
{
|
|
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
|
|
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
|
.name = "EMU10K1 PCM Send Volume",
|
|
.count = 32,
|
|
.info = snd_emu10k1_send_volume_info,
|
|
.get = snd_emu10k1_send_volume_get,
|
|
.put = snd_emu10k1_send_volume_put
|
|
};
|
|
|
|
static int snd_emu10k1_attn_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
|
|
{
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
|
uinfo->count = 3;
|
|
uinfo->value.integer.min = 0;
|
|
uinfo->value.integer.max = 0x1fffd;
|
|
return 0;
|
|
}
|
|
|
|
static int snd_emu10k1_attn_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
struct snd_emu10k1_pcm_mixer *mix =
|
|
&emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
|
|
int idx;
|
|
|
|
for (idx = 0; idx < 3; idx++)
|
|
ucontrol->value.integer.value[idx] = mix->attn[idx] * 0xffffU / 0x8000U;
|
|
return 0;
|
|
}
|
|
|
|
static int snd_emu10k1_attn_put(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
struct snd_emu10k1_pcm_mixer *mix =
|
|
&emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
|
|
int change = 0, idx, val;
|
|
|
|
spin_lock_irq(&emu->reg_lock);
|
|
for (idx = 0; idx < 3; idx++) {
|
|
unsigned uval = ucontrol->value.integer.value[idx] & 0x1ffff;
|
|
val = uval * 0x8000U / 0xffffU;
|
|
if (mix->attn[idx] != val) {
|
|
mix->attn[idx] = val;
|
|
change = 1;
|
|
}
|
|
}
|
|
if (change && mix->epcm && mix->epcm->voices[0]) {
|
|
if (!mix->epcm->voices[0]->last) {
|
|
snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[0]->number, mix->attn[1]);
|
|
snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[0]->number + 1, mix->attn[2]);
|
|
} else {
|
|
snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[0]->number, mix->attn[0]);
|
|
}
|
|
}
|
|
spin_unlock_irq(&emu->reg_lock);
|
|
return change;
|
|
}
|
|
|
|
static const struct snd_kcontrol_new snd_emu10k1_attn_control =
|
|
{
|
|
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
|
|
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
|
.name = "EMU10K1 PCM Volume",
|
|
.count = 32,
|
|
.info = snd_emu10k1_attn_info,
|
|
.get = snd_emu10k1_attn_get,
|
|
.put = snd_emu10k1_attn_put
|
|
};
|
|
|
|
/* Mutichannel PCM stream controls */
|
|
|
|
static int snd_emu10k1_efx_send_routing_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
|
uinfo->count = emu->audigy ? 8 : 4;
|
|
uinfo->value.integer.min = 0;
|
|
uinfo->value.integer.max = emu->audigy ? 0x3f : 0x0f;
|
|
return 0;
|
|
}
|
|
|
|
static int snd_emu10k1_efx_send_routing_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
struct snd_emu10k1_pcm_mixer *mix =
|
|
&emu->efx_pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
|
|
int idx;
|
|
int num_efx = emu->audigy ? 8 : 4;
|
|
int mask = emu->audigy ? 0x3f : 0x0f;
|
|
|
|
for (idx = 0; idx < num_efx; idx++)
|
|
ucontrol->value.integer.value[idx] =
|
|
mix->send_routing[0][idx] & mask;
|
|
return 0;
|
|
}
|
|
|
|
static int snd_emu10k1_efx_send_routing_put(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
int ch = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
|
|
struct snd_emu10k1_pcm_mixer *mix = &emu->efx_pcm_mixer[ch];
|
|
int change = 0, idx, val;
|
|
int num_efx = emu->audigy ? 8 : 4;
|
|
int mask = emu->audigy ? 0x3f : 0x0f;
|
|
|
|
spin_lock_irq(&emu->reg_lock);
|
|
for (idx = 0; idx < num_efx; idx++) {
|
|
val = ucontrol->value.integer.value[idx] & mask;
|
|
if (mix->send_routing[0][idx] != val) {
|
|
mix->send_routing[0][idx] = val;
|
|
change = 1;
|
|
}
|
|
}
|
|
|
|
if (change && mix->epcm) {
|
|
if (mix->epcm->voices[ch]) {
|
|
update_emu10k1_fxrt(emu, mix->epcm->voices[ch]->number,
|
|
&mix->send_routing[0][0]);
|
|
}
|
|
}
|
|
spin_unlock_irq(&emu->reg_lock);
|
|
return change;
|
|
}
|
|
|
|
static const struct snd_kcontrol_new snd_emu10k1_efx_send_routing_control =
|
|
{
|
|
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
|
|
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
|
.name = "Multichannel PCM Send Routing",
|
|
.count = 16,
|
|
.info = snd_emu10k1_efx_send_routing_info,
|
|
.get = snd_emu10k1_efx_send_routing_get,
|
|
.put = snd_emu10k1_efx_send_routing_put
|
|
};
|
|
|
|
static int snd_emu10k1_efx_send_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
|
uinfo->count = emu->audigy ? 8 : 4;
|
|
uinfo->value.integer.min = 0;
|
|
uinfo->value.integer.max = 255;
|
|
return 0;
|
|
}
|
|
|
|
static int snd_emu10k1_efx_send_volume_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
struct snd_emu10k1_pcm_mixer *mix =
|
|
&emu->efx_pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
|
|
int idx;
|
|
int num_efx = emu->audigy ? 8 : 4;
|
|
|
|
for (idx = 0; idx < num_efx; idx++)
|
|
ucontrol->value.integer.value[idx] = mix->send_volume[0][idx];
|
|
return 0;
|
|
}
|
|
|
|
static int snd_emu10k1_efx_send_volume_put(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
int ch = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
|
|
struct snd_emu10k1_pcm_mixer *mix = &emu->efx_pcm_mixer[ch];
|
|
int change = 0, idx, val;
|
|
int num_efx = emu->audigy ? 8 : 4;
|
|
|
|
spin_lock_irq(&emu->reg_lock);
|
|
for (idx = 0; idx < num_efx; idx++) {
|
|
val = ucontrol->value.integer.value[idx] & 255;
|
|
if (mix->send_volume[0][idx] != val) {
|
|
mix->send_volume[0][idx] = val;
|
|
change = 1;
|
|
}
|
|
}
|
|
if (change && mix->epcm) {
|
|
if (mix->epcm->voices[ch]) {
|
|
update_emu10k1_send_volume(emu, mix->epcm->voices[ch]->number,
|
|
&mix->send_volume[0][0]);
|
|
}
|
|
}
|
|
spin_unlock_irq(&emu->reg_lock);
|
|
return change;
|
|
}
|
|
|
|
|
|
static const struct snd_kcontrol_new snd_emu10k1_efx_send_volume_control =
|
|
{
|
|
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
|
|
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
|
.name = "Multichannel PCM Send Volume",
|
|
.count = 16,
|
|
.info = snd_emu10k1_efx_send_volume_info,
|
|
.get = snd_emu10k1_efx_send_volume_get,
|
|
.put = snd_emu10k1_efx_send_volume_put
|
|
};
|
|
|
|
static int snd_emu10k1_efx_attn_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
|
|
{
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
|
uinfo->count = 1;
|
|
uinfo->value.integer.min = 0;
|
|
uinfo->value.integer.max = 0x1fffd;
|
|
return 0;
|
|
}
|
|
|
|
static int snd_emu10k1_efx_attn_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
struct snd_emu10k1_pcm_mixer *mix =
|
|
&emu->efx_pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
|
|
|
|
ucontrol->value.integer.value[0] = mix->attn[0] * 0xffffU / 0x8000U;
|
|
return 0;
|
|
}
|
|
|
|
static int snd_emu10k1_efx_attn_put(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
int ch = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
|
|
struct snd_emu10k1_pcm_mixer *mix = &emu->efx_pcm_mixer[ch];
|
|
int change = 0, val;
|
|
unsigned uval;
|
|
|
|
spin_lock_irq(&emu->reg_lock);
|
|
uval = ucontrol->value.integer.value[0] & 0x1ffff;
|
|
val = uval * 0x8000U / 0xffffU;
|
|
if (mix->attn[0] != val) {
|
|
mix->attn[0] = val;
|
|
change = 1;
|
|
}
|
|
if (change && mix->epcm) {
|
|
if (mix->epcm->voices[ch]) {
|
|
snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[ch]->number, mix->attn[0]);
|
|
}
|
|
}
|
|
spin_unlock_irq(&emu->reg_lock);
|
|
return change;
|
|
}
|
|
|
|
static const struct snd_kcontrol_new snd_emu10k1_efx_attn_control =
|
|
{
|
|
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
|
|
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
|
.name = "Multichannel PCM Volume",
|
|
.count = 16,
|
|
.info = snd_emu10k1_efx_attn_info,
|
|
.get = snd_emu10k1_efx_attn_get,
|
|
.put = snd_emu10k1_efx_attn_put
|
|
};
|
|
|
|
#define snd_emu10k1_shared_spdif_info snd_ctl_boolean_mono_info
|
|
|
|
static int snd_emu10k1_shared_spdif_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
|
|
if (emu->audigy)
|
|
ucontrol->value.integer.value[0] = inw(emu->port + A_IOCFG) & A_IOCFG_GPOUT0 ? 1 : 0;
|
|
else
|
|
ucontrol->value.integer.value[0] = inl(emu->port + HCFG) & HCFG_GPOUT0 ? 1 : 0;
|
|
if (emu->card_capabilities->invert_shared_spdif)
|
|
ucontrol->value.integer.value[0] =
|
|
!ucontrol->value.integer.value[0];
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int snd_emu10k1_shared_spdif_put(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
unsigned int reg, val, sw;
|
|
int change = 0;
|
|
|
|
sw = ucontrol->value.integer.value[0];
|
|
if (emu->card_capabilities->invert_shared_spdif)
|
|
sw = !sw;
|
|
spin_lock_irq(&emu->emu_lock);
|
|
if ( emu->card_capabilities->i2c_adc) {
|
|
/* Do nothing for Audigy 2 ZS Notebook */
|
|
} else if (emu->audigy) {
|
|
reg = inw(emu->port + A_IOCFG);
|
|
val = sw ? A_IOCFG_GPOUT0 : 0;
|
|
change = (reg & A_IOCFG_GPOUT0) != val;
|
|
if (change) {
|
|
reg &= ~A_IOCFG_GPOUT0;
|
|
reg |= val;
|
|
outw(reg | val, emu->port + A_IOCFG);
|
|
}
|
|
}
|
|
reg = inl(emu->port + HCFG);
|
|
val = sw ? HCFG_GPOUT0 : 0;
|
|
change |= (reg & HCFG_GPOUT0) != val;
|
|
if (change) {
|
|
reg &= ~HCFG_GPOUT0;
|
|
reg |= val;
|
|
outl(reg | val, emu->port + HCFG);
|
|
}
|
|
spin_unlock_irq(&emu->emu_lock);
|
|
return change;
|
|
}
|
|
|
|
static const struct snd_kcontrol_new snd_emu10k1_shared_spdif =
|
|
{
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
.name = "SB Live Analog/Digital Output Jack",
|
|
.info = snd_emu10k1_shared_spdif_info,
|
|
.get = snd_emu10k1_shared_spdif_get,
|
|
.put = snd_emu10k1_shared_spdif_put
|
|
};
|
|
|
|
static const struct snd_kcontrol_new snd_audigy_shared_spdif =
|
|
{
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
.name = "Audigy Analog/Digital Output Jack",
|
|
.info = snd_emu10k1_shared_spdif_info,
|
|
.get = snd_emu10k1_shared_spdif_get,
|
|
.put = snd_emu10k1_shared_spdif_put
|
|
};
|
|
|
|
/* workaround for too low volume on Audigy due to 16bit/24bit conversion */
|
|
|
|
#define snd_audigy_capture_boost_info snd_ctl_boolean_mono_info
|
|
|
|
static int snd_audigy_capture_boost_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
unsigned int val;
|
|
|
|
/* FIXME: better to use a cached version */
|
|
val = snd_ac97_read(emu->ac97, AC97_REC_GAIN);
|
|
ucontrol->value.integer.value[0] = !!val;
|
|
return 0;
|
|
}
|
|
|
|
static int snd_audigy_capture_boost_put(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
|
unsigned int val;
|
|
|
|
if (ucontrol->value.integer.value[0])
|
|
val = 0x0f0f;
|
|
else
|
|
val = 0;
|
|
return snd_ac97_update(emu->ac97, AC97_REC_GAIN, val);
|
|
}
|
|
|
|
static const struct snd_kcontrol_new snd_audigy_capture_boost =
|
|
{
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
.name = "Mic Extra Boost",
|
|
.info = snd_audigy_capture_boost_info,
|
|
.get = snd_audigy_capture_boost_get,
|
|
.put = snd_audigy_capture_boost_put
|
|
};
|
|
|
|
|
|
/*
|
|
*/
|
|
static void snd_emu10k1_mixer_free_ac97(struct snd_ac97 *ac97)
|
|
{
|
|
struct snd_emu10k1 *emu = ac97->private_data;
|
|
emu->ac97 = NULL;
|
|
}
|
|
|
|
/*
|
|
*/
|
|
static int remove_ctl(struct snd_card *card, const char *name)
|
|
{
|
|
struct snd_ctl_elem_id id;
|
|
memset(&id, 0, sizeof(id));
|
|
strcpy(id.name, name);
|
|
id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
|
|
return snd_ctl_remove_id(card, &id);
|
|
}
|
|
|
|
static int rename_ctl(struct snd_card *card, const char *src, const char *dst)
|
|
{
|
|
struct snd_kcontrol *kctl = snd_ctl_find_id_mixer(card, src);
|
|
if (kctl) {
|
|
snd_ctl_rename(card, kctl, dst);
|
|
return 0;
|
|
}
|
|
return -ENOENT;
|
|
}
|
|
|
|
int snd_emu10k1_mixer(struct snd_emu10k1 *emu,
|
|
int pcm_device, int multi_device)
|
|
{
|
|
int err;
|
|
struct snd_kcontrol *kctl;
|
|
struct snd_card *card = emu->card;
|
|
const char * const *c;
|
|
static const char * const emu10k1_remove_ctls[] = {
|
|
/* no AC97 mono, surround, center/lfe */
|
|
"Master Mono Playback Switch",
|
|
"Master Mono Playback Volume",
|
|
"PCM Out Path & Mute",
|
|
"Mono Output Select",
|
|
"Surround Playback Switch",
|
|
"Surround Playback Volume",
|
|
"Center Playback Switch",
|
|
"Center Playback Volume",
|
|
"LFE Playback Switch",
|
|
"LFE Playback Volume",
|
|
NULL
|
|
};
|
|
static const char * const emu10k1_rename_ctls[] = {
|
|
"Surround Digital Playback Volume", "Surround Playback Volume",
|
|
"Center Digital Playback Volume", "Center Playback Volume",
|
|
"LFE Digital Playback Volume", "LFE Playback Volume",
|
|
NULL
|
|
};
|
|
static const char * const audigy_remove_ctls[] = {
|
|
/* Master/PCM controls on ac97 of Audigy has no effect */
|
|
/* On the Audigy2 the AC97 playback is piped into
|
|
* the Philips ADC for 24bit capture */
|
|
"PCM Playback Switch",
|
|
"PCM Playback Volume",
|
|
"Master Playback Switch",
|
|
"Master Playback Volume",
|
|
"PCM Out Path & Mute",
|
|
"Mono Output Select",
|
|
/* remove unused AC97 capture controls */
|
|
"Capture Source",
|
|
"Capture Switch",
|
|
"Capture Volume",
|
|
"Mic Select",
|
|
"Headphone Playback Switch",
|
|
"Headphone Playback Volume",
|
|
"3D Control - Center",
|
|
"3D Control - Depth",
|
|
"3D Control - Switch",
|
|
"Video Playback Switch",
|
|
"Video Playback Volume",
|
|
"Mic Playback Switch",
|
|
"Mic Playback Volume",
|
|
"External Amplifier",
|
|
NULL
|
|
};
|
|
static const char * const audigy_rename_ctls[] = {
|
|
/* use conventional names */
|
|
"Wave Playback Volume", "PCM Playback Volume",
|
|
/* "Wave Capture Volume", "PCM Capture Volume", */
|
|
"Wave Master Playback Volume", "Master Playback Volume",
|
|
"AMic Playback Volume", "Mic Playback Volume",
|
|
"Master Mono Playback Switch", "Phone Output Playback Switch",
|
|
"Master Mono Playback Volume", "Phone Output Playback Volume",
|
|
NULL
|
|
};
|
|
static const char * const audigy_rename_ctls_i2c_adc[] = {
|
|
//"Analog Mix Capture Volume","OLD Analog Mix Capture Volume",
|
|
"Line Capture Volume", "Analog Mix Capture Volume",
|
|
"Wave Playback Volume", "OLD PCM Playback Volume",
|
|
"Wave Master Playback Volume", "Master Playback Volume",
|
|
"AMic Playback Volume", "Old Mic Playback Volume",
|
|
"CD Capture Volume", "IEC958 Optical Capture Volume",
|
|
NULL
|
|
};
|
|
static const char * const audigy_remove_ctls_i2c_adc[] = {
|
|
/* On the Audigy2 ZS Notebook
|
|
* Capture via WM8775 */
|
|
"Mic Capture Volume",
|
|
"Analog Mix Capture Volume",
|
|
"Aux Capture Volume",
|
|
"IEC958 Optical Capture Volume",
|
|
NULL
|
|
};
|
|
static const char * const audigy_remove_ctls_1361t_adc[] = {
|
|
/* On the Audigy2 the AC97 playback is piped into
|
|
* the Philips ADC for 24bit capture */
|
|
"PCM Playback Switch",
|
|
"PCM Playback Volume",
|
|
"Capture Source",
|
|
"Capture Switch",
|
|
"Capture Volume",
|
|
"Mic Capture Volume",
|
|
"Headphone Playback Switch",
|
|
"Headphone Playback Volume",
|
|
"3D Control - Center",
|
|
"3D Control - Depth",
|
|
"3D Control - Switch",
|
|
"Line2 Playback Volume",
|
|
"Line2 Capture Volume",
|
|
NULL
|
|
};
|
|
static const char * const audigy_rename_ctls_1361t_adc[] = {
|
|
"Master Playback Switch", "Master Capture Switch",
|
|
"Master Playback Volume", "Master Capture Volume",
|
|
"Wave Master Playback Volume", "Master Playback Volume",
|
|
"Beep Playback Switch", "Beep Capture Switch",
|
|
"Beep Playback Volume", "Beep Capture Volume",
|
|
"Phone Playback Switch", "Phone Capture Switch",
|
|
"Phone Playback Volume", "Phone Capture Volume",
|
|
"Mic Playback Switch", "Mic Capture Switch",
|
|
"Mic Playback Volume", "Mic Capture Volume",
|
|
"Line Playback Switch", "Line Capture Switch",
|
|
"Line Playback Volume", "Line Capture Volume",
|
|
"CD Playback Switch", "CD Capture Switch",
|
|
"CD Playback Volume", "CD Capture Volume",
|
|
"Aux Playback Switch", "Aux Capture Switch",
|
|
"Aux Playback Volume", "Aux Capture Volume",
|
|
"Video Playback Switch", "Video Capture Switch",
|
|
"Video Playback Volume", "Video Capture Volume",
|
|
"Master Mono Playback Switch", "Phone Output Playback Switch",
|
|
"Master Mono Playback Volume", "Phone Output Playback Volume",
|
|
NULL
|
|
};
|
|
|
|
if (emu->card_capabilities->ac97_chip) {
|
|
struct snd_ac97_bus *pbus;
|
|
struct snd_ac97_template ac97;
|
|
static const struct snd_ac97_bus_ops ops = {
|
|
.write = snd_emu10k1_ac97_write,
|
|
.read = snd_emu10k1_ac97_read,
|
|
};
|
|
|
|
err = snd_ac97_bus(emu->card, 0, &ops, NULL, &pbus);
|
|
if (err < 0)
|
|
return err;
|
|
pbus->no_vra = 1; /* we don't need VRA */
|
|
|
|
memset(&ac97, 0, sizeof(ac97));
|
|
ac97.private_data = emu;
|
|
ac97.private_free = snd_emu10k1_mixer_free_ac97;
|
|
ac97.scaps = AC97_SCAP_NO_SPDIF;
|
|
err = snd_ac97_mixer(pbus, &ac97, &emu->ac97);
|
|
if (err < 0) {
|
|
if (emu->card_capabilities->ac97_chip == 1)
|
|
return err;
|
|
dev_info(emu->card->dev,
|
|
"AC97 is optional on this board\n");
|
|
dev_info(emu->card->dev,
|
|
"Proceeding without ac97 mixers...\n");
|
|
snd_device_free(emu->card, pbus);
|
|
goto no_ac97; /* FIXME: get rid of ugly gotos.. */
|
|
}
|
|
if (emu->audigy) {
|
|
/* set master volume to 0 dB */
|
|
snd_ac97_write_cache(emu->ac97, AC97_MASTER, 0x0000);
|
|
/* set capture source to mic */
|
|
snd_ac97_write_cache(emu->ac97, AC97_REC_SEL, 0x0000);
|
|
/* set mono output (TAD) to mic */
|
|
snd_ac97_update_bits(emu->ac97, AC97_GENERAL_PURPOSE,
|
|
0x0200, 0x0200);
|
|
if (emu->card_capabilities->adc_1361t)
|
|
c = audigy_remove_ctls_1361t_adc;
|
|
else
|
|
c = audigy_remove_ctls;
|
|
} else {
|
|
/*
|
|
* Credits for cards based on STAC9758:
|
|
* James Courtier-Dutton <James@superbug.demon.co.uk>
|
|
* Voluspa <voluspa@comhem.se>
|
|
*/
|
|
if (emu->ac97->id == AC97_ID_STAC9758) {
|
|
emu->rear_ac97 = 1;
|
|
snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE|AC97SLOT_REAR_LEFT|AC97SLOT_REAR_RIGHT);
|
|
snd_ac97_write_cache(emu->ac97, AC97_HEADPHONE, 0x0202);
|
|
remove_ctl(card,"Front Playback Volume");
|
|
remove_ctl(card,"Front Playback Switch");
|
|
}
|
|
/* remove unused AC97 controls */
|
|
snd_ac97_write_cache(emu->ac97, AC97_SURROUND_MASTER, 0x0202);
|
|
snd_ac97_write_cache(emu->ac97, AC97_CENTER_LFE_MASTER, 0x0202);
|
|
c = emu10k1_remove_ctls;
|
|
}
|
|
for (; *c; c++)
|
|
remove_ctl(card, *c);
|
|
} else if (emu->card_capabilities->i2c_adc) {
|
|
c = audigy_remove_ctls_i2c_adc;
|
|
for (; *c; c++)
|
|
remove_ctl(card, *c);
|
|
} else {
|
|
no_ac97:
|
|
if (emu->card_capabilities->ecard)
|
|
strcpy(emu->card->mixername, "EMU APS");
|
|
else if (emu->audigy)
|
|
strcpy(emu->card->mixername, "SB Audigy");
|
|
else
|
|
strcpy(emu->card->mixername, "Emu10k1");
|
|
}
|
|
|
|
if (emu->audigy)
|
|
if (emu->card_capabilities->adc_1361t)
|
|
c = audigy_rename_ctls_1361t_adc;
|
|
else if (emu->card_capabilities->i2c_adc)
|
|
c = audigy_rename_ctls_i2c_adc;
|
|
else
|
|
c = audigy_rename_ctls;
|
|
else
|
|
c = emu10k1_rename_ctls;
|
|
for (; *c; c += 2)
|
|
rename_ctl(card, c[0], c[1]);
|
|
|
|
if (emu->card_capabilities->subsystem == 0x80401102) { /* SB Live! Platinum CT4760P */
|
|
remove_ctl(card, "Center Playback Volume");
|
|
remove_ctl(card, "LFE Playback Volume");
|
|
remove_ctl(card, "Wave Center Playback Volume");
|
|
remove_ctl(card, "Wave LFE Playback Volume");
|
|
}
|
|
if (emu->card_capabilities->subsystem == 0x20071102) { /* Audigy 4 Pro */
|
|
rename_ctl(card, "Line2 Capture Volume", "Line1/Mic Capture Volume");
|
|
rename_ctl(card, "Analog Mix Capture Volume", "Line2 Capture Volume");
|
|
rename_ctl(card, "Aux2 Capture Volume", "Line3 Capture Volume");
|
|
rename_ctl(card, "Mic Capture Volume", "Unknown1 Capture Volume");
|
|
}
|
|
kctl = emu->ctl_send_routing = snd_ctl_new1(&snd_emu10k1_send_routing_control, emu);
|
|
if (!kctl)
|
|
return -ENOMEM;
|
|
kctl->id.device = pcm_device;
|
|
err = snd_ctl_add(card, kctl);
|
|
if (err)
|
|
return err;
|
|
kctl = emu->ctl_send_volume = snd_ctl_new1(&snd_emu10k1_send_volume_control, emu);
|
|
if (!kctl)
|
|
return -ENOMEM;
|
|
kctl->id.device = pcm_device;
|
|
err = snd_ctl_add(card, kctl);
|
|
if (err)
|
|
return err;
|
|
kctl = emu->ctl_attn = snd_ctl_new1(&snd_emu10k1_attn_control, emu);
|
|
if (!kctl)
|
|
return -ENOMEM;
|
|
kctl->id.device = pcm_device;
|
|
err = snd_ctl_add(card, kctl);
|
|
if (err)
|
|
return err;
|
|
|
|
kctl = emu->ctl_efx_send_routing = snd_ctl_new1(&snd_emu10k1_efx_send_routing_control, emu);
|
|
if (!kctl)
|
|
return -ENOMEM;
|
|
kctl->id.device = multi_device;
|
|
err = snd_ctl_add(card, kctl);
|
|
if (err)
|
|
return err;
|
|
|
|
kctl = emu->ctl_efx_send_volume = snd_ctl_new1(&snd_emu10k1_efx_send_volume_control, emu);
|
|
if (!kctl)
|
|
return -ENOMEM;
|
|
kctl->id.device = multi_device;
|
|
err = snd_ctl_add(card, kctl);
|
|
if (err)
|
|
return err;
|
|
|
|
kctl = emu->ctl_efx_attn = snd_ctl_new1(&snd_emu10k1_efx_attn_control, emu);
|
|
if (!kctl)
|
|
return -ENOMEM;
|
|
kctl->id.device = multi_device;
|
|
err = snd_ctl_add(card, kctl);
|
|
if (err)
|
|
return err;
|
|
|
|
if (!emu->card_capabilities->ecard && !emu->card_capabilities->emu_model) {
|
|
/* sb live! and audigy */
|
|
kctl = snd_ctl_new1(&snd_emu10k1_spdif_mask_control, emu);
|
|
if (!kctl)
|
|
return -ENOMEM;
|
|
if (!emu->audigy)
|
|
kctl->id.device = emu->pcm_efx->device;
|
|
err = snd_ctl_add(card, kctl);
|
|
if (err)
|
|
return err;
|
|
kctl = snd_ctl_new1(&snd_emu10k1_spdif_control, emu);
|
|
if (!kctl)
|
|
return -ENOMEM;
|
|
if (!emu->audigy)
|
|
kctl->id.device = emu->pcm_efx->device;
|
|
err = snd_ctl_add(card, kctl);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
if (emu->card_capabilities->emu_model) {
|
|
; /* Disable the snd_audigy_spdif_shared_spdif */
|
|
} else if (emu->audigy) {
|
|
kctl = snd_ctl_new1(&snd_audigy_shared_spdif, emu);
|
|
if (!kctl)
|
|
return -ENOMEM;
|
|
err = snd_ctl_add(card, kctl);
|
|
if (err)
|
|
return err;
|
|
#if 0
|
|
kctl = snd_ctl_new1(&snd_audigy_spdif_output_rate, emu);
|
|
if (!kctl)
|
|
return -ENOMEM;
|
|
err = snd_ctl_add(card, kctl);
|
|
if (err)
|
|
return err;
|
|
#endif
|
|
} else if (! emu->card_capabilities->ecard) {
|
|
/* sb live! */
|
|
kctl = snd_ctl_new1(&snd_emu10k1_shared_spdif, emu);
|
|
if (!kctl)
|
|
return -ENOMEM;
|
|
err = snd_ctl_add(card, kctl);
|
|
if (err)
|
|
return err;
|
|
}
|
|
if (emu->card_capabilities->ca0151_chip) { /* P16V */
|
|
err = snd_p16v_mixer(emu);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
if (emu->card_capabilities->emu_model) {
|
|
unsigned i, emu_idx = emu1010_idx(emu);
|
|
const struct snd_emu1010_routing_info *emu_ri =
|
|
&emu1010_routing_info[emu_idx];
|
|
const struct snd_emu1010_pads_info *emu_pi = &emu1010_pads_info[emu_idx];
|
|
|
|
for (i = 0; i < emu_ri->n_ins; i++)
|
|
emu->emu1010.input_source[i] =
|
|
emu1010_map_source(emu_ri, emu_ri->in_dflts[i]);
|
|
for (i = 0; i < emu_ri->n_outs; i++)
|
|
emu->emu1010.output_source[i] =
|
|
emu1010_map_source(emu_ri, emu_ri->out_dflts[i]);
|
|
snd_emu1010_fpga_lock(emu);
|
|
snd_emu1010_apply_sources(emu);
|
|
snd_emu1010_fpga_unlock(emu);
|
|
|
|
kctl = emu->ctl_clock_source = snd_ctl_new1(&snd_emu1010_clock_source, emu);
|
|
err = snd_ctl_add(card, kctl);
|
|
if (err < 0)
|
|
return err;
|
|
err = snd_ctl_add(card,
|
|
snd_ctl_new1(&snd_emu1010_clock_fallback, emu));
|
|
if (err < 0)
|
|
return err;
|
|
|
|
err = add_ctls(emu, &emu1010_adc_pads_ctl,
|
|
emu_pi->adc_ctls, emu_pi->n_adc_ctls);
|
|
if (err < 0)
|
|
return err;
|
|
err = add_ctls(emu, &emu1010_dac_pads_ctl,
|
|
emu_pi->dac_ctls, emu_pi->n_dac_ctls);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
if (!emu->card_capabilities->no_adat) {
|
|
err = snd_ctl_add(card,
|
|
snd_ctl_new1(&snd_emu1010_optical_out, emu));
|
|
if (err < 0)
|
|
return err;
|
|
err = snd_ctl_add(card,
|
|
snd_ctl_new1(&snd_emu1010_optical_in, emu));
|
|
if (err < 0)
|
|
return err;
|
|
}
|
|
|
|
err = add_emu1010_source_mixers(emu);
|
|
if (err < 0)
|
|
return err;
|
|
}
|
|
|
|
if ( emu->card_capabilities->i2c_adc) {
|
|
err = snd_ctl_add(card, snd_ctl_new1(&snd_audigy_i2c_capture_source, emu));
|
|
if (err < 0)
|
|
return err;
|
|
|
|
err = add_ctls(emu, &i2c_volume_ctl,
|
|
snd_audigy_i2c_volume_ctls,
|
|
ARRAY_SIZE(snd_audigy_i2c_volume_ctls));
|
|
if (err < 0)
|
|
return err;
|
|
}
|
|
|
|
if (emu->card_capabilities->ac97_chip && emu->audigy) {
|
|
err = snd_ctl_add(card, snd_ctl_new1(&snd_audigy_capture_boost,
|
|
emu));
|
|
if (err < 0)
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|