-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ALSA: oxygen: Xonar DG(X): move the mixer code into another file
Moving the mixer code away makes things easier. The mixer will control the driver, so the functions of the driver need to be non-static. Signed-off-by: Roman Volkov <v1ron@mail.ru> Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
- Loading branch information
Roman Volkov
authored and
Clemens Ladisch
committed
Jan 29, 2014
1 parent
06f70d0
commit 041f26b
Showing
4 changed files
with
411 additions
and
364 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,381 @@ | ||
/* | ||
* Mixer controls for the Xonar DG/DGX | ||
* | ||
* Copyright (c) Clemens Ladisch <clemens@ladisch.de> | ||
* Copyright (c) Roman Volkov <v1ron@mail.ru> | ||
* | ||
* This driver is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License, version 2. | ||
* | ||
* This driver is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this driver; if not, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
#include <linux/pci.h> | ||
#include <linux/delay.h> | ||
#include <sound/control.h> | ||
#include <sound/core.h> | ||
#include <sound/info.h> | ||
#include <sound/pcm.h> | ||
#include <sound/tlv.h> | ||
#include "oxygen.h" | ||
#include "xonar_dg.h" | ||
#include "cs4245.h" | ||
|
||
static int output_switch_info(struct snd_kcontrol *ctl, | ||
struct snd_ctl_elem_info *info) | ||
{ | ||
static const char *const names[3] = { | ||
"Speakers", "Headphones", "FP Headphones" | ||
}; | ||
|
||
return snd_ctl_enum_info(info, 1, 3, names); | ||
} | ||
|
||
static int output_switch_get(struct snd_kcontrol *ctl, | ||
struct snd_ctl_elem_value *value) | ||
{ | ||
struct oxygen *chip = ctl->private_data; | ||
struct dg *data = chip->model_data; | ||
|
||
mutex_lock(&chip->mutex); | ||
value->value.enumerated.item[0] = data->output_sel; | ||
mutex_unlock(&chip->mutex); | ||
return 0; | ||
} | ||
|
||
static int output_switch_put(struct snd_kcontrol *ctl, | ||
struct snd_ctl_elem_value *value) | ||
{ | ||
struct oxygen *chip = ctl->private_data; | ||
struct dg *data = chip->model_data; | ||
u8 reg; | ||
int changed; | ||
|
||
if (value->value.enumerated.item[0] > 2) | ||
return -EINVAL; | ||
|
||
mutex_lock(&chip->mutex); | ||
changed = value->value.enumerated.item[0] != data->output_sel; | ||
if (changed) { | ||
data->output_sel = value->value.enumerated.item[0]; | ||
|
||
reg = data->cs4245_shadow[CS4245_SIGNAL_SEL] & | ||
~CS4245_A_OUT_SEL_MASK; | ||
reg |= data->output_sel == 2 ? | ||
CS4245_A_OUT_SEL_DAC : CS4245_A_OUT_SEL_HIZ; | ||
cs4245_write_cached(chip, CS4245_SIGNAL_SEL, reg); | ||
|
||
cs4245_write_cached(chip, CS4245_DAC_A_CTRL, | ||
data->output_sel ? data->hp_vol_att : 0); | ||
cs4245_write_cached(chip, CS4245_DAC_B_CTRL, | ||
data->output_sel ? data->hp_vol_att : 0); | ||
|
||
oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, | ||
data->output_sel == 1 ? GPIO_HP_REAR : 0, | ||
GPIO_HP_REAR); | ||
} | ||
mutex_unlock(&chip->mutex); | ||
return changed; | ||
} | ||
|
||
static int hp_volume_offset_info(struct snd_kcontrol *ctl, | ||
struct snd_ctl_elem_info *info) | ||
{ | ||
static const char *const names[3] = { | ||
"< 64 ohms", "64-150 ohms", "150-300 ohms" | ||
}; | ||
|
||
return snd_ctl_enum_info(info, 1, 3, names); | ||
} | ||
|
||
static int hp_volume_offset_get(struct snd_kcontrol *ctl, | ||
struct snd_ctl_elem_value *value) | ||
{ | ||
struct oxygen *chip = ctl->private_data; | ||
struct dg *data = chip->model_data; | ||
|
||
mutex_lock(&chip->mutex); | ||
if (data->hp_vol_att > 2 * 7) | ||
value->value.enumerated.item[0] = 0; | ||
else if (data->hp_vol_att > 0) | ||
value->value.enumerated.item[0] = 1; | ||
else | ||
value->value.enumerated.item[0] = 2; | ||
mutex_unlock(&chip->mutex); | ||
return 0; | ||
} | ||
|
||
static int hp_volume_offset_put(struct snd_kcontrol *ctl, | ||
struct snd_ctl_elem_value *value) | ||
{ | ||
static const s8 atts[3] = { 2 * 16, 2 * 7, 0 }; | ||
struct oxygen *chip = ctl->private_data; | ||
struct dg *data = chip->model_data; | ||
s8 att; | ||
int changed; | ||
|
||
if (value->value.enumerated.item[0] > 2) | ||
return -EINVAL; | ||
att = atts[value->value.enumerated.item[0]]; | ||
mutex_lock(&chip->mutex); | ||
changed = att != data->hp_vol_att; | ||
if (changed) { | ||
data->hp_vol_att = att; | ||
if (data->output_sel) { | ||
cs4245_write_cached(chip, CS4245_DAC_A_CTRL, att); | ||
cs4245_write_cached(chip, CS4245_DAC_B_CTRL, att); | ||
} | ||
} | ||
mutex_unlock(&chip->mutex); | ||
return changed; | ||
} | ||
|
||
static int input_vol_info(struct snd_kcontrol *ctl, | ||
struct snd_ctl_elem_info *info) | ||
{ | ||
info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
info->count = 2; | ||
info->value.integer.min = 2 * -12; | ||
info->value.integer.max = 2 * 12; | ||
return 0; | ||
} | ||
|
||
static int input_vol_get(struct snd_kcontrol *ctl, | ||
struct snd_ctl_elem_value *value) | ||
{ | ||
struct oxygen *chip = ctl->private_data; | ||
struct dg *data = chip->model_data; | ||
unsigned int idx = ctl->private_value; | ||
|
||
mutex_lock(&chip->mutex); | ||
value->value.integer.value[0] = data->input_vol[idx][0]; | ||
value->value.integer.value[1] = data->input_vol[idx][1]; | ||
mutex_unlock(&chip->mutex); | ||
return 0; | ||
} | ||
|
||
static int input_vol_put(struct snd_kcontrol *ctl, | ||
struct snd_ctl_elem_value *value) | ||
{ | ||
struct oxygen *chip = ctl->private_data; | ||
struct dg *data = chip->model_data; | ||
unsigned int idx = ctl->private_value; | ||
int changed = 0; | ||
|
||
if (value->value.integer.value[0] < 2 * -12 || | ||
value->value.integer.value[0] > 2 * 12 || | ||
value->value.integer.value[1] < 2 * -12 || | ||
value->value.integer.value[1] > 2 * 12) | ||
return -EINVAL; | ||
mutex_lock(&chip->mutex); | ||
changed = data->input_vol[idx][0] != value->value.integer.value[0] || | ||
data->input_vol[idx][1] != value->value.integer.value[1]; | ||
if (changed) { | ||
data->input_vol[idx][0] = value->value.integer.value[0]; | ||
data->input_vol[idx][1] = value->value.integer.value[1]; | ||
if (idx == data->input_sel) { | ||
cs4245_write_cached(chip, CS4245_PGA_A_CTRL, | ||
data->input_vol[idx][0]); | ||
cs4245_write_cached(chip, CS4245_PGA_B_CTRL, | ||
data->input_vol[idx][1]); | ||
} | ||
} | ||
mutex_unlock(&chip->mutex); | ||
return changed; | ||
} | ||
|
||
static DECLARE_TLV_DB_SCALE(cs4245_pga_db_scale, -1200, 50, 0); | ||
|
||
static int input_sel_info(struct snd_kcontrol *ctl, | ||
struct snd_ctl_elem_info *info) | ||
{ | ||
static const char *const names[4] = { | ||
"Mic", "Aux", "Front Mic", "Line" | ||
}; | ||
|
||
return snd_ctl_enum_info(info, 1, 4, names); | ||
} | ||
|
||
static int input_sel_get(struct snd_kcontrol *ctl, | ||
struct snd_ctl_elem_value *value) | ||
{ | ||
struct oxygen *chip = ctl->private_data; | ||
struct dg *data = chip->model_data; | ||
|
||
mutex_lock(&chip->mutex); | ||
value->value.enumerated.item[0] = data->input_sel; | ||
mutex_unlock(&chip->mutex); | ||
return 0; | ||
} | ||
|
||
static int input_sel_put(struct snd_kcontrol *ctl, | ||
struct snd_ctl_elem_value *value) | ||
{ | ||
static const u8 sel_values[4] = { | ||
CS4245_SEL_MIC, | ||
CS4245_SEL_INPUT_1, | ||
CS4245_SEL_INPUT_2, | ||
CS4245_SEL_INPUT_4 | ||
}; | ||
struct oxygen *chip = ctl->private_data; | ||
struct dg *data = chip->model_data; | ||
int changed; | ||
|
||
if (value->value.enumerated.item[0] > 3) | ||
return -EINVAL; | ||
|
||
mutex_lock(&chip->mutex); | ||
changed = value->value.enumerated.item[0] != data->input_sel; | ||
if (changed) { | ||
data->input_sel = value->value.enumerated.item[0]; | ||
|
||
cs4245_write(chip, CS4245_ANALOG_IN, | ||
(data->cs4245_shadow[CS4245_ANALOG_IN] & | ||
~CS4245_SEL_MASK) | | ||
sel_values[data->input_sel]); | ||
|
||
cs4245_write_cached(chip, CS4245_PGA_A_CTRL, | ||
data->input_vol[data->input_sel][0]); | ||
cs4245_write_cached(chip, CS4245_PGA_B_CTRL, | ||
data->input_vol[data->input_sel][1]); | ||
|
||
oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, | ||
data->input_sel ? 0 : GPIO_INPUT_ROUTE, | ||
GPIO_INPUT_ROUTE); | ||
} | ||
mutex_unlock(&chip->mutex); | ||
return changed; | ||
} | ||
|
||
static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) | ||
{ | ||
static const char *const names[2] = { "Active", "Frozen" }; | ||
|
||
return snd_ctl_enum_info(info, 1, 2, names); | ||
} | ||
|
||
static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) | ||
{ | ||
struct oxygen *chip = ctl->private_data; | ||
struct dg *data = chip->model_data; | ||
|
||
value->value.enumerated.item[0] = | ||
!!(data->cs4245_shadow[CS4245_ADC_CTRL] & CS4245_HPF_FREEZE); | ||
return 0; | ||
} | ||
|
||
static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) | ||
{ | ||
struct oxygen *chip = ctl->private_data; | ||
struct dg *data = chip->model_data; | ||
u8 reg; | ||
int changed; | ||
|
||
mutex_lock(&chip->mutex); | ||
reg = data->cs4245_shadow[CS4245_ADC_CTRL] & ~CS4245_HPF_FREEZE; | ||
if (value->value.enumerated.item[0]) | ||
reg |= CS4245_HPF_FREEZE; | ||
changed = reg != data->cs4245_shadow[CS4245_ADC_CTRL]; | ||
if (changed) | ||
cs4245_write(chip, CS4245_ADC_CTRL, reg); | ||
mutex_unlock(&chip->mutex); | ||
return changed; | ||
} | ||
|
||
#define INPUT_VOLUME(xname, index) { \ | ||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ | ||
.name = xname, \ | ||
.info = input_vol_info, \ | ||
.get = input_vol_get, \ | ||
.put = input_vol_put, \ | ||
.tlv = { .p = cs4245_pga_db_scale }, \ | ||
.private_value = index, \ | ||
} | ||
static const struct snd_kcontrol_new dg_controls[] = { | ||
{ | ||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
.name = "Analog Output Playback Enum", | ||
.info = output_switch_info, | ||
.get = output_switch_get, | ||
.put = output_switch_put, | ||
}, | ||
{ | ||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
.name = "Headphones Impedance Playback Enum", | ||
.info = hp_volume_offset_info, | ||
.get = hp_volume_offset_get, | ||
.put = hp_volume_offset_put, | ||
}, | ||
INPUT_VOLUME("Mic Capture Volume", 0), | ||
INPUT_VOLUME("Aux Capture Volume", 1), | ||
INPUT_VOLUME("Front Mic Capture Volume", 2), | ||
INPUT_VOLUME("Line Capture Volume", 3), | ||
{ | ||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
.name = "Capture Source", | ||
.info = input_sel_info, | ||
.get = input_sel_get, | ||
.put = input_sel_put, | ||
}, | ||
{ | ||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
.name = "ADC High-pass Filter Capture Enum", | ||
.info = hpf_info, | ||
.get = hpf_get, | ||
.put = hpf_put, | ||
}, | ||
}; | ||
|
||
static int dg_control_filter(struct snd_kcontrol_new *template) | ||
{ | ||
if (!strncmp(template->name, "Master Playback ", 16)) | ||
return 1; | ||
return 0; | ||
} | ||
|
||
static int dg_mixer_init(struct oxygen *chip) | ||
{ | ||
unsigned int i; | ||
int err; | ||
|
||
for (i = 0; i < ARRAY_SIZE(dg_controls); ++i) { | ||
err = snd_ctl_add(chip->card, | ||
snd_ctl_new1(&dg_controls[i], chip)); | ||
if (err < 0) | ||
return err; | ||
} | ||
return 0; | ||
} | ||
|
||
struct oxygen_model model_xonar_dg = { | ||
.longname = "C-Media Oxygen HD Audio", | ||
.chip = "CMI8786", | ||
.init = dg_init, | ||
.control_filter = dg_control_filter, | ||
.mixer_init = dg_mixer_init, | ||
.cleanup = dg_cleanup, | ||
.suspend = dg_suspend, | ||
.resume = dg_resume, | ||
.set_dac_params = set_cs4245_dac_params, | ||
.set_adc_params = set_cs4245_adc_params, | ||
.adjust_dac_routing = adjust_dg_dac_routing, | ||
.dump_registers = dump_cs4245_registers, | ||
.model_data_size = sizeof(struct dg), | ||
.device_config = PLAYBACK_0_TO_I2S | | ||
PLAYBACK_1_TO_SPDIF | | ||
CAPTURE_0_FROM_I2S_2 | | ||
CAPTURE_1_FROM_SPDIF, | ||
.dac_channels_pcm = 6, | ||
.dac_channels_mixer = 0, | ||
.function_flags = OXYGEN_FUNCTION_SPI, | ||
.dac_mclks = OXYGEN_MCLKS(256, 128, 128), | ||
.adc_mclks = OXYGEN_MCLKS(256, 128, 128), | ||
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, | ||
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, | ||
}; |