Skip to content

Commit

Permalink
ALSA: hda/cs8409: Generalize volume controls
Browse files Browse the repository at this point in the history
Use amp offsets as indexes for saved volumes.
Remove dependencies on NID inside volume control functions.

Signed-off-by: Lucas Tanure <[email protected]>
Signed-off-by: Vitaly Rodionov <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Takashi Iwai <[email protected]>
  • Loading branch information
Lucas Tanure authored and tiwai committed Aug 12, 2021
1 parent a1a6c7d commit b2a8877
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 124 deletions.
37 changes: 37 additions & 0 deletions sound/pci/hda/patch_cs8409-tables.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,43 @@

#include "patch_cs8409.h"

/******************************************************************************
* CS42L42 Specific Data
*
******************************************************************************/

static const DECLARE_TLV_DB_SCALE(cs42l42_dac_db_scale,
CS8409_CS42L42_HP_VOL_REAL_MIN * 100, 100, 1);

static const DECLARE_TLV_DB_SCALE(cs42l42_adc_db_scale,
CS8409_CS42L42_AMIC_VOL_REAL_MIN * 100, 100, 1);

const struct snd_kcontrol_new cs42l42_dac_volume_mixer = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.index = 0,
.subdevice = (HDA_SUBDEV_AMP_FLAG | HDA_SUBDEV_NID_FLAG),
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.info = cs8409_cs42l42_volume_info,
.get = cs8409_cs42l42_volume_get,
.put = cs8409_cs42l42_volume_put,
.tlv = { .p = cs42l42_dac_db_scale },
.private_value = HDA_COMPOSE_AMP_VAL_OFS(CS8409_PIN_ASP1_TRANSMITTER_A, 3, 0,
HDA_OUTPUT, CS42L42_VOL_DAC) | HDA_AMP_VAL_MIN_MUTE
};

const struct snd_kcontrol_new cs42l42_adc_volume_mixer = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.index = 0,
.subdevice = (HDA_SUBDEV_AMP_FLAG | HDA_SUBDEV_NID_FLAG),
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.info = cs8409_cs42l42_volume_info,
.get = cs8409_cs42l42_volume_get,
.put = cs8409_cs42l42_volume_put,
.tlv = { .p = cs42l42_adc_db_scale },
.private_value = HDA_COMPOSE_AMP_VAL_OFS(CS8409_PIN_ASP1_RECEIVER_A, 1, 0,
HDA_INPUT, CS42L42_VOL_ADC) | HDA_AMP_VAL_MIN_MUTE
};

/* Dell Inspiron platforms
* with cs8409 bridge and cs42l42 codec
*/
Expand Down
165 changes: 49 additions & 116 deletions sound/pci/hda/patch_cs8409.c
Original file line number Diff line number Diff line change
Expand Up @@ -208,162 +208,97 @@ static int cs8409_i2c_write(struct hda_codec *codec, unsigned int i2c_address, u
return 0;
}

static int cs8409_cs42l42_volume_info(struct snd_kcontrol *kctrl, struct snd_ctl_elem_info *uinfo)
int cs8409_cs42l42_volume_info(struct snd_kcontrol *kctrl, struct snd_ctl_elem_info *uinfo)
{
u16 nid = get_amp_nid(kctrl);
unsigned int ofs = get_amp_offset(kctrl);
u8 chs = get_amp_channels(kctrl);

switch (nid) {
case CS8409_CS42L42_HP_PIN_NID:
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = chs == 3 ? 2 : 1;
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->value.integer.step = 1;
uinfo->count = chs == 3 ? 2 : 1;

switch (ofs) {
case CS42L42_VOL_DAC:
uinfo->value.integer.min = CS8409_CS42L42_HP_VOL_REAL_MIN;
uinfo->value.integer.max = CS8409_CS42L42_HP_VOL_REAL_MAX;
break;
case CS8409_CS42L42_AMIC_PIN_NID:
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = chs == 3 ? 2 : 1;
case CS42L42_VOL_ADC:
uinfo->value.integer.min = CS8409_CS42L42_AMIC_VOL_REAL_MIN;
uinfo->value.integer.max = CS8409_CS42L42_AMIC_VOL_REAL_MAX;
break;
default:
break;
}
return 0;
}

static void cs8409_cs42l42_update_volume(struct hda_codec *codec)
{
struct cs8409_spec *spec = codec->spec;
int data;

mutex_lock(&spec->cs8409_i2c_mux);
data = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_HS_VOLUME_CHA, 1);
if (data >= 0)
spec->cs42l42_hp_volume[0] = -data;
else
spec->cs42l42_hp_volume[0] = CS8409_CS42L42_HP_VOL_REAL_MIN;
data = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_HS_VOLUME_CHB, 1);
if (data >= 0)
spec->cs42l42_hp_volume[1] = -data;
else
spec->cs42l42_hp_volume[1] = CS8409_CS42L42_HP_VOL_REAL_MIN;
data = cs8409_i2c_read(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_AMIC_VOLUME, 1);
if (data >= 0)
spec->cs42l42_hs_mic_volume[0] = -data;
else
spec->cs42l42_hs_mic_volume[0] = CS8409_CS42L42_AMIC_VOL_REAL_MIN;
mutex_unlock(&spec->cs8409_i2c_mux);
spec->cs42l42_volume_init = 1;
return 0;
}

static int cs8409_cs42l42_volume_get(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl)
int cs8409_cs42l42_volume_get(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl)
{
struct hda_codec *codec = snd_kcontrol_chip(kctrl);
struct cs8409_spec *spec = codec->spec;
hda_nid_t nid = get_amp_nid(kctrl);
int chs = get_amp_channels(kctrl);
unsigned int ofs = get_amp_offset(kctrl);
long *valp = uctrl->value.integer.value;

if (!spec->cs42l42_volume_init) {
snd_hda_power_up(codec);
cs8409_cs42l42_update_volume(codec);
snd_hda_power_down(codec);
}
switch (nid) {
case CS8409_CS42L42_HP_PIN_NID:
switch (ofs) {
case CS42L42_VOL_DAC:
if (chs & BIT(0))
*valp++ = spec->cs42l42_hp_volume[0];
*valp++ = spec->vol[ofs];
if (chs & BIT(1))
*valp++ = spec->cs42l42_hp_volume[1];
*valp = spec->vol[ofs+1];
break;
case CS8409_CS42L42_AMIC_PIN_NID:
case CS42L42_VOL_ADC:
if (chs & BIT(0))
*valp++ = spec->cs42l42_hs_mic_volume[0];
*valp = spec->vol[ofs];
break;
default:
break;
}

return 0;
}

static int cs8409_cs42l42_volume_put(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl)
int cs8409_cs42l42_volume_put(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl)
{
struct hda_codec *codec = snd_kcontrol_chip(kctrl);
struct cs8409_spec *spec = codec->spec;
hda_nid_t nid = get_amp_nid(kctrl);
int chs = get_amp_channels(kctrl);
unsigned int ofs = get_amp_offset(kctrl);
long *valp = uctrl->value.integer.value;
int change = 0;
char vol;

snd_hda_power_up(codec);
switch (nid) {
case CS8409_CS42L42_HP_PIN_NID:
switch (ofs) {
case CS42L42_VOL_DAC:
mutex_lock(&spec->cs8409_i2c_mux);
if (chs & BIT(0)) {
vol = -(*valp);
change = cs8409_i2c_write(codec, CS42L42_I2C_ADDR,
CS8409_CS42L42_REG_HS_VOLUME_CHA, vol, 1);
valp++;
spec->vol[ofs] = *valp;
cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_HS_VOL_CHA,
-(spec->vol[ofs]) & CS8409_CS42L42_REG_HS_VOL_MASK, 1);
}
if (chs & BIT(1)) {
vol = -(*valp);
change |= cs8409_i2c_write(codec, CS42L42_I2C_ADDR,
CS8409_CS42L42_REG_HS_VOLUME_CHB, vol, 1);
ofs++;
valp++;
spec->vol[ofs] = *valp;
cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_HS_VOL_CHB,
-(spec->vol[ofs]) & CS8409_CS42L42_REG_HS_VOL_MASK, 1);
}
mutex_unlock(&spec->cs8409_i2c_mux);
break;
case CS8409_CS42L42_AMIC_PIN_NID:
case CS42L42_VOL_ADC:
mutex_lock(&spec->cs8409_i2c_mux);
if (chs & BIT(0)) {
change = cs8409_i2c_write(codec, CS42L42_I2C_ADDR,
CS8409_CS42L42_REG_AMIC_VOLUME, (char)*valp, 1);
valp++;
spec->vol[ofs] = *valp;
cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_AMIC_VOL,
spec->vol[ofs] & CS8409_CS42L42_REG_AMIC_VOL_MASK, 1);
}
mutex_unlock(&spec->cs8409_i2c_mux);
break;
default:
break;
}
cs8409_cs42l42_update_volume(codec);
snd_hda_power_down(codec);
return change;
}

static const DECLARE_TLV_DB_SCALE(cs8409_cs42l42_hp_db_scale,
CS8409_CS42L42_HP_VOL_REAL_MIN * 100, 100, 1);

static const DECLARE_TLV_DB_SCALE(cs8409_cs42l42_amic_db_scale,
CS8409_CS42L42_AMIC_VOL_REAL_MIN * 100, 100, 1);

static const struct snd_kcontrol_new cs8409_cs42l42_hp_volume_mixer = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.index = 0,
.name = "Headphone Playback Volume",
.subdevice = (HDA_SUBDEV_AMP_FLAG | HDA_SUBDEV_NID_FLAG),
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.info = cs8409_cs42l42_volume_info,
.get = cs8409_cs42l42_volume_get,
.put = cs8409_cs42l42_volume_put,
.tlv = { .p = cs8409_cs42l42_hp_db_scale },
.private_value = HDA_COMPOSE_AMP_VAL(CS8409_CS42L42_HP_PIN_NID, 3, 0, HDA_OUTPUT) |
HDA_AMP_VAL_MIN_MUTE
};

static const struct snd_kcontrol_new cs8409_cs42l42_amic_volume_mixer = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.index = 0,
.name = "Mic Capture Volume",
.subdevice = (HDA_SUBDEV_AMP_FLAG | HDA_SUBDEV_NID_FLAG),
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.info = cs8409_cs42l42_volume_info,
.get = cs8409_cs42l42_volume_get,
.put = cs8409_cs42l42_volume_put,
.tlv = { .p = cs8409_cs42l42_amic_db_scale },
.private_value = HDA_COMPOSE_AMP_VAL(CS8409_CS42L42_AMIC_PIN_NID, 1, 0, HDA_INPUT) |
HDA_AMP_VAL_MIN_MUTE
};
return 0;
}

/* Assert/release RTS# line to CS42L42 */
static void cs8409_cs42l42_reset(struct hda_codec *codec)
Expand Down Expand Up @@ -657,18 +592,14 @@ static void cs8409_cs42l42_hw_init(struct hda_codec *codec)
}

/* Restore Volumes after Resume */
if (spec->cs42l42_volume_init) {
mutex_lock(&spec->cs8409_i2c_mux);
cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_HS_VOLUME_CHA,
-spec->cs42l42_hp_volume[0], 1);
cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_HS_VOLUME_CHB,
-spec->cs42l42_hp_volume[1], 1);
cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_AMIC_VOLUME,
spec->cs42l42_hs_mic_volume[0], 1);
mutex_unlock(&spec->cs8409_i2c_mux);
}

cs8409_cs42l42_update_volume(codec);
mutex_lock(&spec->cs8409_i2c_mux);
cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_HS_VOL_CHA,
-(spec->vol[1]) & CS8409_CS42L42_REG_HS_VOL_MASK, 1);
cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_HS_VOL_CHB,
-(spec->vol[2]) & CS8409_CS42L42_REG_HS_VOL_MASK, 1);
cs8409_i2c_write(codec, CS42L42_I2C_ADDR, CS8409_CS42L42_REG_AMIC_VOL,
spec->vol[0] & CS8409_CS42L42_REG_AMIC_VOL_MASK, 1);
mutex_unlock(&spec->cs8409_i2c_mux);

cs8409_cs42l42_enable_jack_detect(codec);

Expand Down Expand Up @@ -811,8 +742,10 @@ void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix,
/* Set initial DMIC volume to -26 dB */
snd_hda_codec_amp_init_stereo(codec, CS8409_CS42L42_DMIC_ADC_PIN_NID,
HDA_INPUT, 0, 0xff, 0x19);
snd_hda_gen_add_kctl(&spec->gen, NULL, &cs8409_cs42l42_hp_volume_mixer);
snd_hda_gen_add_kctl(&spec->gen, NULL, &cs8409_cs42l42_amic_volume_mixer);
snd_hda_gen_add_kctl(&spec->gen, "Headphone Playback Volume",
&cs42l42_dac_volume_mixer);
snd_hda_gen_add_kctl(&spec->gen, "Mic Capture Volume",
&cs42l42_adc_volume_mixer);
/* Disable Unsolicited Response during boot */
cs8409_enable_ur(codec, 0);
cs8409_cs42l42_hw_init(codec);
Expand Down
27 changes: 19 additions & 8 deletions sound/pci/hda/patch_cs8409.h
Original file line number Diff line number Diff line change
Expand Up @@ -215,16 +215,17 @@ enum cs8409_coefficient_index_registers {

/* CS42L42 Specific Definitions */

#define CS42L42_HP_CH (2U)
#define CS42L42_HS_MIC_CH (1U)
#define CS42L42_VOLUMES (4U)

#define CS8409_CS42L42_HP_VOL_REAL_MIN (-63)
#define CS8409_CS42L42_HP_VOL_REAL_MAX (0)
#define CS8409_CS42L42_AMIC_VOL_REAL_MIN (-97)
#define CS8409_CS42L42_AMIC_VOL_REAL_MAX (12)
#define CS8409_CS42L42_REG_HS_VOLUME_CHA (0x2301)
#define CS8409_CS42L42_REG_HS_VOLUME_CHB (0x2303)
#define CS8409_CS42L42_REG_AMIC_VOLUME (0x1D03)
#define CS8409_CS42L42_REG_HS_VOL_CHA (0x2301)
#define CS8409_CS42L42_REG_HS_VOL_CHB (0x2303)
#define CS8409_CS42L42_REG_HS_VOL_MASK (0x003F)
#define CS8409_CS42L42_REG_AMIC_VOL (0x1D03)
#define CS8409_CS42L42_REG_AMIC_VOL_MASK (0x00FF)
#define CS42L42_HSDET_AUTO_DONE (0x02)
#define CS42L42_HSTYPE_MASK (0x03)
#define CS42L42_JACK_INSERTED (0x0C)
Expand All @@ -248,6 +249,11 @@ enum {
CS8409_FIXUPS,
};

enum {
CS42L42_VOL_ADC,
CS42L42_VOL_DAC,
};

struct cs8409_i2c_param {
unsigned int addr;
unsigned int reg;
Expand All @@ -268,10 +274,8 @@ struct cs8409_spec {

unsigned int cs42l42_hp_jack_in:1;
unsigned int cs42l42_mic_jack_in:1;
unsigned int cs42l42_volume_init:1;
unsigned int cs42l42_suspended:1;
char cs42l42_hp_volume[CS42L42_HP_CH];
char cs42l42_hs_mic_volume[CS42L42_HS_MIC_CH];
s8 vol[CS42L42_VOLUMES];

struct mutex cs8409_i2c_mux;

Expand All @@ -280,6 +284,13 @@ struct cs8409_spec {
unsigned int *res);
};

extern const struct snd_kcontrol_new cs42l42_dac_volume_mixer;
extern const struct snd_kcontrol_new cs42l42_adc_volume_mixer;

int cs8409_cs42l42_volume_info(struct snd_kcontrol *kctrl, struct snd_ctl_elem_info *uinfo);
int cs8409_cs42l42_volume_get(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl);
int cs8409_cs42l42_volume_put(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl);

extern const struct snd_pci_quirk cs8409_fixup_tbl[];
extern const struct hda_model_fixup cs8409_models[];
extern const struct hda_fixup cs8409_fixups[];
Expand Down

0 comments on commit b2a8877

Please sign in to comment.