#include <linux/module.h>
MODULE_AUTHOR(…) …;
MODULE_LICENSE(…) …;
MODULE_DESCRIPTION(…) …;
MODULE_DEVICE_TABLE(pci, snd_echo_ids);
static int index[SNDRV_CARDS] = …;
static char *id[SNDRV_CARDS] = …;
static bool enable[SNDRV_CARDS] = …;
module_param_array(…);
MODULE_PARM_DESC(…) …;
module_param_array(…);
MODULE_PARM_DESC(…) …;
module_param_array(…);
MODULE_PARM_DESC(…) …;
static const unsigned int channels_list[10] = …;
static const DECLARE_TLV_DB_SCALE(db_scale_output_gain, -12800, 100, 1);
static int get_firmware(const struct firmware **fw_entry,
struct echoaudio *chip, const short fw_index)
{ … }
static void free_firmware(const struct firmware *fw_entry,
struct echoaudio *chip)
{ … }
static void free_firmware_cache(struct echoaudio *chip)
{ … }
static void audiopipe_free(struct snd_pcm_runtime *runtime)
{ … }
static int hw_rule_capture_format_by_channels(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{ … }
static int hw_rule_capture_channels_by_format(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{ … }
static int hw_rule_playback_format_by_channels(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{ … }
static int hw_rule_playback_channels_by_format(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{ … }
static int hw_rule_sample_rate(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{ … }
static int pcm_open(struct snd_pcm_substream *substream,
signed char max_channels)
{ … }
static int pcm_analog_in_open(struct snd_pcm_substream *substream)
{ … }
static int pcm_analog_out_open(struct snd_pcm_substream *substream)
{ … }
#ifdef ECHOCARD_HAS_DIGITAL_IO
static int pcm_digital_in_open(struct snd_pcm_substream *substream)
{
struct echoaudio *chip = snd_pcm_substream_chip(substream);
int err, max_channels;
max_channels = num_digital_busses_in(chip) - substream->number;
mutex_lock(&chip->mode_mutex);
if (chip->digital_mode == DIGITAL_MODE_ADAT)
err = pcm_open(substream, max_channels);
else
err = pcm_open(substream, max_channels - ECHOCARD_HAS_ADAT);
if (err < 0)
goto din_exit;
err = snd_pcm_hw_rule_add(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_CHANNELS,
hw_rule_capture_channels_by_format, NULL,
SNDRV_PCM_HW_PARAM_FORMAT, -1);
if (err < 0)
goto din_exit;
err = snd_pcm_hw_rule_add(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_FORMAT,
hw_rule_capture_format_by_channels, NULL,
SNDRV_PCM_HW_PARAM_CHANNELS, -1);
if (err < 0)
goto din_exit;
din_exit:
mutex_unlock(&chip->mode_mutex);
return err;
}
#ifndef ECHOCARD_HAS_VMIXER
static int pcm_digital_out_open(struct snd_pcm_substream *substream)
{
struct echoaudio *chip = snd_pcm_substream_chip(substream);
int err, max_channels;
max_channels = num_digital_busses_out(chip) - substream->number;
mutex_lock(&chip->mode_mutex);
if (chip->digital_mode == DIGITAL_MODE_ADAT)
err = pcm_open(substream, max_channels);
else
err = pcm_open(substream, max_channels - ECHOCARD_HAS_ADAT);
if (err < 0)
goto dout_exit;
err = snd_pcm_hw_rule_add(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_CHANNELS,
hw_rule_playback_channels_by_format,
NULL, SNDRV_PCM_HW_PARAM_FORMAT,
-1);
if (err < 0)
goto dout_exit;
err = snd_pcm_hw_rule_add(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_FORMAT,
hw_rule_playback_format_by_channels,
NULL, SNDRV_PCM_HW_PARAM_CHANNELS,
-1);
if (err < 0)
goto dout_exit;
dout_exit:
mutex_unlock(&chip->mode_mutex);
return err;
}
#endif
#endif
static int pcm_close(struct snd_pcm_substream *substream)
{ … }
static int init_engine(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params,
int pipe_index, int interleave)
{ … }
static int pcm_analog_in_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{ … }
static int pcm_analog_out_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{ … }
#ifdef ECHOCARD_HAS_DIGITAL_IO
static int pcm_digital_in_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct echoaudio *chip = snd_pcm_substream_chip(substream);
return init_engine(substream, hw_params, px_digital_in(chip) +
substream->number, params_channels(hw_params));
}
#ifndef ECHOCARD_HAS_VMIXER
static int pcm_digital_out_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct echoaudio *chip = snd_pcm_substream_chip(substream);
return init_engine(substream, hw_params, px_digital_out(chip) +
substream->number, params_channels(hw_params));
}
#endif
#endif
static int pcm_hw_free(struct snd_pcm_substream *substream)
{ … }
static int pcm_prepare(struct snd_pcm_substream *substream)
{ … }
static int pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{ … }
static snd_pcm_uframes_t pcm_pointer(struct snd_pcm_substream *substream)
{ … }
static const struct snd_pcm_ops analog_playback_ops = …;
static const struct snd_pcm_ops analog_capture_ops = …;
#ifdef ECHOCARD_HAS_DIGITAL_IO
#ifndef ECHOCARD_HAS_VMIXER
static const struct snd_pcm_ops digital_playback_ops = {
.open = pcm_digital_out_open,
.close = pcm_close,
.hw_params = pcm_digital_out_hw_params,
.hw_free = pcm_hw_free,
.prepare = pcm_prepare,
.trigger = pcm_trigger,
.pointer = pcm_pointer,
};
#endif
static const struct snd_pcm_ops digital_capture_ops = {
.open = pcm_digital_in_open,
.close = pcm_close,
.hw_params = pcm_digital_in_hw_params,
.hw_free = pcm_hw_free,
.prepare = pcm_prepare,
.trigger = pcm_trigger,
.pointer = pcm_pointer,
};
#endif
static void snd_echo_preallocate_pages(struct snd_pcm *pcm, struct device *dev)
{ … }
static int snd_echo_new_pcm(struct echoaudio *chip)
{ … }
#if !defined(ECHOCARD_HAS_VMIXER) || defined(ECHOCARD_HAS_LINE_OUT_GAIN)
static int snd_echo_output_gain_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{ … }
static int snd_echo_output_gain_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{ … }
static int snd_echo_output_gain_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{ … }
#ifdef ECHOCARD_HAS_LINE_OUT_GAIN
static const struct snd_kcontrol_new snd_echo_line_output_gain = {
.name = "Line Playback Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ,
.info = snd_echo_output_gain_info,
.get = snd_echo_output_gain_get,
.put = snd_echo_output_gain_put,
.tlv = {.p = db_scale_output_gain},
};
#else
static const struct snd_kcontrol_new snd_echo_pcm_output_gain = …;
#endif
#endif
#ifdef ECHOCARD_HAS_INPUT_GAIN
static int snd_echo_input_gain_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct echoaudio *chip;
chip = snd_kcontrol_chip(kcontrol);
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = num_analog_busses_in(chip);
uinfo->value.integer.min = ECHOGAIN_MININP;
uinfo->value.integer.max = ECHOGAIN_MAXINP;
return 0;
}
static int snd_echo_input_gain_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct echoaudio *chip;
int c;
chip = snd_kcontrol_chip(kcontrol);
for (c = 0; c < num_analog_busses_in(chip); c++)
ucontrol->value.integer.value[c] = chip->input_gain[c];
return 0;
}
static int snd_echo_input_gain_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct echoaudio *chip;
int c, gain, changed;
changed = 0;
chip = snd_kcontrol_chip(kcontrol);
spin_lock_irq(&chip->lock);
for (c = 0; c < num_analog_busses_in(chip); c++) {
gain = ucontrol->value.integer.value[c];
if (gain < ECHOGAIN_MININP || gain > ECHOGAIN_MAXINP)
continue;
if (chip->input_gain[c] != gain) {
set_input_gain(chip, c, gain);
changed = 1;
}
}
if (changed)
update_input_line_level(chip);
spin_unlock_irq(&chip->lock);
return changed;
}
static const DECLARE_TLV_DB_SCALE(db_scale_input_gain, -2500, 50, 0);
static const struct snd_kcontrol_new snd_echo_line_input_gain = {
.name = "Line Capture Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,
.info = snd_echo_input_gain_info,
.get = snd_echo_input_gain_get,
.put = snd_echo_input_gain_put,
.tlv = {.p = db_scale_input_gain},
};
#endif
#ifdef ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL
static int snd_echo_output_nominal_info (struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct echoaudio *chip;
chip = snd_kcontrol_chip(kcontrol);
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->count = num_analog_busses_out(chip);
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 1;
return 0;
}
static int snd_echo_output_nominal_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct echoaudio *chip;
int c;
chip = snd_kcontrol_chip(kcontrol);
for (c = 0; c < num_analog_busses_out(chip); c++)
ucontrol->value.integer.value[c] = chip->nominal_level[c];
return 0;
}
static int snd_echo_output_nominal_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct echoaudio *chip;
int c, changed;
changed = 0;
chip = snd_kcontrol_chip(kcontrol);
spin_lock_irq(&chip->lock);
for (c = 0; c < num_analog_busses_out(chip); c++) {
if (chip->nominal_level[c] != ucontrol->value.integer.value[c]) {
set_nominal_level(chip, c,
ucontrol->value.integer.value[c]);
changed = 1;
}
}
if (changed)
update_output_line_level(chip);
spin_unlock_irq(&chip->lock);
return changed;
}
static const struct snd_kcontrol_new snd_echo_output_nominal_level = {
.name = "Line Playback Switch (-10dBV)",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.info = snd_echo_output_nominal_info,
.get = snd_echo_output_nominal_get,
.put = snd_echo_output_nominal_put,
};
#endif
#ifdef ECHOCARD_HAS_INPUT_NOMINAL_LEVEL
static int snd_echo_input_nominal_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct echoaudio *chip;
chip = snd_kcontrol_chip(kcontrol);
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->count = num_analog_busses_in(chip);
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 1;
return 0;
}
static int snd_echo_input_nominal_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct echoaudio *chip;
int c;
chip = snd_kcontrol_chip(kcontrol);
for (c = 0; c < num_analog_busses_in(chip); c++)
ucontrol->value.integer.value[c] =
chip->nominal_level[bx_analog_in(chip) + c];
return 0;
}
static int snd_echo_input_nominal_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct echoaudio *chip;
int c, changed;
changed = 0;
chip = snd_kcontrol_chip(kcontrol);
spin_lock_irq(&chip->lock);
for (c = 0; c < num_analog_busses_in(chip); c++) {
if (chip->nominal_level[bx_analog_in(chip) + c] !=
ucontrol->value.integer.value[c]) {
set_nominal_level(chip, bx_analog_in(chip) + c,
ucontrol->value.integer.value[c]);
changed = 1;
}
}
if (changed)
update_output_line_level(chip);
spin_unlock_irq(&chip->lock);
return changed;
}
static const struct snd_kcontrol_new snd_echo_intput_nominal_level = {
.name = "Line Capture Switch (-10dBV)",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.info = snd_echo_input_nominal_info,
.get = snd_echo_input_nominal_get,
.put = snd_echo_input_nominal_put,
};
#endif
#ifdef ECHOCARD_HAS_MONITOR
static int snd_echo_mixer_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{ … }
static int snd_echo_mixer_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{ … }
static int snd_echo_mixer_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{ … }
static struct snd_kcontrol_new snd_echo_monitor_mixer = …;
#endif
#ifdef ECHOCARD_HAS_VMIXER
static int snd_echo_vmixer_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 = ECHOGAIN_MINOUT;
uinfo->value.integer.max = ECHOGAIN_MAXOUT;
return 0;
}
static int snd_echo_vmixer_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct echoaudio *chip;
chip = snd_kcontrol_chip(kcontrol);
ucontrol->value.integer.value[0] =
chip->vmixer_gain[ucontrol->id.index / num_pipes_out(chip)]
[ucontrol->id.index % num_pipes_out(chip)];
return 0;
}
static int snd_echo_vmixer_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct echoaudio *chip;
int gain, changed;
short vch, out;
changed = 0;
chip = snd_kcontrol_chip(kcontrol);
out = ucontrol->id.index / num_pipes_out(chip);
vch = ucontrol->id.index % num_pipes_out(chip);
gain = ucontrol->value.integer.value[0];
if (gain < ECHOGAIN_MINOUT || gain > ECHOGAIN_MAXOUT)
return -EINVAL;
if (chip->vmixer_gain[out][vch] != ucontrol->value.integer.value[0]) {
spin_lock_irq(&chip->lock);
set_vmixer_gain(chip, out, vch, ucontrol->value.integer.value[0]);
update_vmixer_level(chip);
spin_unlock_irq(&chip->lock);
changed = 1;
}
return changed;
}
static struct snd_kcontrol_new snd_echo_vmixer = {
.name = "VMixer Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,
.info = snd_echo_vmixer_info,
.get = snd_echo_vmixer_get,
.put = snd_echo_vmixer_put,
.tlv = {.p = db_scale_output_gain},
};
#endif
#ifdef ECHOCARD_HAS_DIGITAL_MODE_SWITCH
static int snd_echo_digital_mode_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static const char * const names[4] = {
"S/PDIF Coaxial", "S/PDIF Optical", "ADAT Optical",
"S/PDIF Cdrom"
};
struct echoaudio *chip;
chip = snd_kcontrol_chip(kcontrol);
return snd_ctl_enum_info(uinfo, 1, chip->num_digital_modes, names);
}
static int snd_echo_digital_mode_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct echoaudio *chip;
int i, mode;
chip = snd_kcontrol_chip(kcontrol);
mode = chip->digital_mode;
for (i = chip->num_digital_modes - 1; i >= 0; i--)
if (mode == chip->digital_mode_list[i]) {
ucontrol->value.enumerated.item[0] = i;
break;
}
return 0;
}
static int snd_echo_digital_mode_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct echoaudio *chip;
int changed;
unsigned short emode, dmode;
changed = 0;
chip = snd_kcontrol_chip(kcontrol);
emode = ucontrol->value.enumerated.item[0];
if (emode >= chip->num_digital_modes)
return -EINVAL;
dmode = chip->digital_mode_list[emode];
if (dmode != chip->digital_mode) {
mutex_lock(&chip->mode_mutex);
if (chip->opencount) {
changed = -EAGAIN;
} else {
changed = set_digital_mode(chip, dmode);
if (changed > 0 && chip->clock_src_ctl) {
snd_ctl_notify(chip->card,
SNDRV_CTL_EVENT_MASK_VALUE,
&chip->clock_src_ctl->id);
dev_dbg(chip->card->dev,
"SDM() =%d\n", changed);
}
if (changed >= 0)
changed = 1;
}
mutex_unlock(&chip->mode_mutex);
}
return changed;
}
static const struct snd_kcontrol_new snd_echo_digital_mode_switch = {
.name = "Digital mode Switch",
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.info = snd_echo_digital_mode_info,
.get = snd_echo_digital_mode_get,
.put = snd_echo_digital_mode_put,
};
#endif
#ifdef ECHOCARD_HAS_DIGITAL_IO
static int snd_echo_spdif_mode_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static const char * const names[2] = {"Consumer", "Professional"};
return snd_ctl_enum_info(uinfo, 1, 2, names);
}
static int snd_echo_spdif_mode_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct echoaudio *chip;
chip = snd_kcontrol_chip(kcontrol);
ucontrol->value.enumerated.item[0] = !!chip->professional_spdif;
return 0;
}
static int snd_echo_spdif_mode_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct echoaudio *chip;
int mode;
chip = snd_kcontrol_chip(kcontrol);
mode = !!ucontrol->value.enumerated.item[0];
if (mode != chip->professional_spdif) {
spin_lock_irq(&chip->lock);
set_professional_spdif(chip, mode);
spin_unlock_irq(&chip->lock);
return 1;
}
return 0;
}
static const struct snd_kcontrol_new snd_echo_spdif_mode_switch = {
.name = "S/PDIF mode Switch",
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.info = snd_echo_spdif_mode_info,
.get = snd_echo_spdif_mode_get,
.put = snd_echo_spdif_mode_put,
};
#endif
#ifdef ECHOCARD_HAS_EXTERNAL_CLOCK
static int snd_echo_clock_source_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static const char * const names[8] = {
"Internal", "Word", "Super", "S/PDIF", "ADAT", "ESync",
"ESync96", "MTC"
};
struct echoaudio *chip;
chip = snd_kcontrol_chip(kcontrol);
return snd_ctl_enum_info(uinfo, 1, chip->num_clock_sources, names);
}
static int snd_echo_clock_source_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct echoaudio *chip;
int i, clock;
chip = snd_kcontrol_chip(kcontrol);
clock = chip->input_clock;
for (i = 0; i < chip->num_clock_sources; i++)
if (clock == chip->clock_source_list[i])
ucontrol->value.enumerated.item[0] = i;
return 0;
}
static int snd_echo_clock_source_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct echoaudio *chip;
int changed;
unsigned int eclock, dclock;
changed = 0;
chip = snd_kcontrol_chip(kcontrol);
eclock = ucontrol->value.enumerated.item[0];
if (eclock >= chip->input_clock_types)
return -EINVAL;
dclock = chip->clock_source_list[eclock];
if (chip->input_clock != dclock) {
mutex_lock(&chip->mode_mutex);
spin_lock_irq(&chip->lock);
changed = set_input_clock(chip, dclock);
if (!changed)
changed = 1;
spin_unlock_irq(&chip->lock);
mutex_unlock(&chip->mode_mutex);
}
if (changed < 0)
dev_dbg(chip->card->dev,
"seticlk val%d err 0x%x\n", dclock, changed);
return changed;
}
static const struct snd_kcontrol_new snd_echo_clock_source_switch = {
.name = "Sample Clock Source",
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.info = snd_echo_clock_source_info,
.get = snd_echo_clock_source_get,
.put = snd_echo_clock_source_put,
};
#endif
#ifdef ECHOCARD_HAS_PHANTOM_POWER
#define snd_echo_phantom_power_info …
static int snd_echo_phantom_power_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct echoaudio *chip = snd_kcontrol_chip(kcontrol);
ucontrol->value.integer.value[0] = chip->phantom_power;
return 0;
}
static int snd_echo_phantom_power_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct echoaudio *chip = snd_kcontrol_chip(kcontrol);
int power, changed = 0;
power = !!ucontrol->value.integer.value[0];
if (chip->phantom_power != power) {
spin_lock_irq(&chip->lock);
changed = set_phantom_power(chip, power);
spin_unlock_irq(&chip->lock);
if (changed == 0)
changed = 1;
}
return changed;
}
static const struct snd_kcontrol_new snd_echo_phantom_power_switch = {
.name = "Phantom power Switch",
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.info = snd_echo_phantom_power_info,
.get = snd_echo_phantom_power_get,
.put = snd_echo_phantom_power_put,
};
#endif
#ifdef ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE
#define snd_echo_automute_info …
static int snd_echo_automute_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct echoaudio *chip = snd_kcontrol_chip(kcontrol);
ucontrol->value.integer.value[0] = chip->digital_in_automute;
return 0;
}
static int snd_echo_automute_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct echoaudio *chip = snd_kcontrol_chip(kcontrol);
int automute, changed = 0;
automute = !!ucontrol->value.integer.value[0];
if (chip->digital_in_automute != automute) {
spin_lock_irq(&chip->lock);
changed = set_input_auto_mute(chip, automute);
spin_unlock_irq(&chip->lock);
if (changed == 0)
changed = 1;
}
return changed;
}
static const struct snd_kcontrol_new snd_echo_automute_switch = {
.name = "Digital Capture Switch (automute)",
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.info = snd_echo_automute_info,
.get = snd_echo_automute_get,
.put = snd_echo_automute_put,
};
#endif
#define snd_echo_vumeters_switch_info …
static int snd_echo_vumeters_switch_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{ … }
static const struct snd_kcontrol_new snd_echo_vumeters_switch = …;
static int snd_echo_vumeters_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{ … }
static int snd_echo_vumeters_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{ … }
static const struct snd_kcontrol_new snd_echo_vumeters = …;
static int snd_echo_channels_info_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{ … }
static int snd_echo_channels_info_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{ … }
static const struct snd_kcontrol_new snd_echo_channels_info = …;
static bool period_has_elapsed(struct snd_pcm_substream *substream)
{ … }
static irqreturn_t snd_echo_interrupt(int irq, void *dev_id)
{ … }
static void snd_echo_free(struct snd_card *card)
{ … }
static int snd_echo_create(struct snd_card *card,
struct pci_dev *pci)
{ … }
static int __snd_echo_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{ … }
static int snd_echo_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{ … }
static int snd_echo_suspend(struct device *dev)
{ … }
static int snd_echo_resume(struct device *dev)
{ … }
static DEFINE_SIMPLE_DEV_PM_OPS(snd_echo_pm, snd_echo_suspend, snd_echo_resume);
static struct pci_driver echo_driver = …;
module_pci_driver(…) …;