Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a9430dd8 authored by Jonathan Woithe's avatar Jonathan Woithe Committed by Jaroslav Kysela
Browse files

[ALSA] hda-codec - A new model for Fujitsu S7020



Modules: HDA Codec driver

Added a new model 'fujitsu' to ALC260 config for Fujitsu S7020.

Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent dfedc5f4
Loading
Loading
Loading
Loading
+157 −3
Original line number Diff line number Diff line
@@ -57,6 +57,7 @@ enum {
enum {
	ALC260_BASIC,
	ALC260_HP,
	ALC260_FUJITSU_S702x,
	ALC260_MODEL_LAST /* last tag */
};

@@ -72,6 +73,7 @@ enum {
#define PIN_VREF50	0x21
#define PIN_OUT		0x40
#define PIN_HP		0xc0
#define PIN_HP_AMP	0x80

struct alc_spec {
	/* codec parameterization */
@@ -284,6 +286,54 @@ static int alc_bind_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u

#define ALC_BIND_MUTE(xname,nid,indices,dir) ALC_BIND_MUTE_MONO(xname,nid,3,indices,dir)

/*
 * Control of pin widget settings via the mixer.  Only boolean settings are
 * supported, so VrefEn can't be controlled using these functions as they
 * stand.
 */
static int alc_pinctl_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
{
	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
	uinfo->count = 1;
	uinfo->value.integer.min = 0;
	uinfo->value.integer.max = 1;
	return 0;
}

static int alc_pinctl_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
	hda_nid_t nid = kcontrol->private_value & 0xffff;
	long mask = (kcontrol->private_value >> 16) & 0xff;
	long *valp = ucontrol->value.integer.value;

	*valp = 0;
	if (snd_hda_codec_read(codec,nid,0,AC_VERB_GET_PIN_WIDGET_CONTROL,0x00) & mask)
		*valp = 1;
	return 0;
}

static int alc_pinctl_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
	hda_nid_t nid = kcontrol->private_value & 0xffff;
	long mask = (kcontrol->private_value >> 16) & 0xff;
	long *valp = ucontrol->value.integer.value;
	unsigned int pinctl = snd_hda_codec_read(codec,nid,0,AC_VERB_GET_PIN_WIDGET_CONTROL,0x00);
	int change = ((pinctl & mask)!=0) != *valp;

	if (change)
		snd_hda_codec_write(codec,nid,0,AC_VERB_SET_PIN_WIDGET_CONTROL,
			*valp?(pinctl|mask):(pinctl&~mask));
	return change;
}

#define ALC_PINCTL_SWITCH(xname, nid, mask) \
	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0,  \
	  .info = alc_pinctl_switch_info, \
	  .get = alc_pinctl_switch_get, \
	  .put = alc_pinctl_switch_put, \
	  .private_value = (nid) | (mask<<16) }

/*
 * ALC880 3-stack model
@@ -2205,6 +2255,17 @@ static struct hda_input_mux alc260_capture_source = {
	},
};

/* On Fujitsu S702x laptops capture only makes sense from Mic/LineIn jack
 * and the internal CD lines.
 */
static struct hda_input_mux alc260_fujitsu_capture_source = {
	.num_items = 2,
	.items = {
		{ "Mic/Line", 0x0 },
		{ "CD", 0x4 },
	},
};

/*
 * This is just place-holder, so there's something for alc_build_pcms to look
 * at when it calculates the maximum number of channels. ALC260 has no mixer
@@ -2271,6 +2332,30 @@ static snd_kcontrol_new_t alc260_hp_mixer[] = {
	{ } /* end */
};

static snd_kcontrol_new_t alc260_fujitsu_mixer[] = {
	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x08, 0x0, HDA_OUTPUT),
	ALC_BIND_MUTE("Headphone Playback Switch", 0x08, 2, HDA_INPUT),
	ALC_PINCTL_SWITCH("Headphone Amp Switch", 0x14, PIN_HP_AMP),
	HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT),
	HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT),
	HDA_CODEC_VOLUME("Mic/Line Playback Volume", 0x07, 0x0, HDA_INPUT),
	HDA_CODEC_MUTE("Mic/Line Playback Switch", 0x07, 0x0, HDA_INPUT),
	HDA_CODEC_VOLUME("Beep Playback Volume", 0x07, 0x05, HDA_INPUT),
	HDA_CODEC_MUTE("Beep Playback Switch", 0x07, 0x05, HDA_INPUT),
	HDA_CODEC_VOLUME("Internal Speaker Playback Volume", 0x09, 0x0, HDA_OUTPUT),
	ALC_BIND_MUTE("Internal Speaker Playback Switch", 0x09, 2, HDA_INPUT),
	HDA_CODEC_VOLUME("Capture Volume", 0x04, 0x0, HDA_INPUT),
	HDA_CODEC_MUTE("Capture Switch", 0x04, 0x0, HDA_INPUT),
	{
		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
		.name = "Capture Source",
		.info = alc_mux_enum_info,
		.get = alc_mux_enum_get,
		.put = alc_mux_enum_put,
	},
	{ } /* end */
};

static struct hda_verb alc260_init_verbs[] = {
	/* Line In pin widget for input */
	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
@@ -2332,6 +2417,60 @@ static struct hda_verb alc260_init_verbs[] = {
	{ }
};

/* Initialisation sequence for ALC260 as configured in Fujitsu S702x
 * laptops.
 */
static struct hda_verb alc260_fujitsu_init_verbs[] = {
	/* Disable all GPIOs */
	{0x01, AC_VERB_SET_GPIO_MASK, 0},
	/* Internal speaker is connected to headphone pin */
	{0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
	/* Headphone/Line-out jack connects to Line1 pin; make it an output */
	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
        /* Mic/Line-in jack is connected to mic1 pin, so make it an input */
        {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
        /* Ensure all other unused pins are disabled and muted.
	 * Note: trying to set widget 0x15 to anything blocks all audio
	 * output for some reason, so just leave that at the default.
	 */
        {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
        {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
	{0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
        {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
	{0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
        {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
        /* Disable digital (SPDIF) pins */
        {0x03, AC_VERB_SET_DIGI_CONVERT_1, 0},
        {0x06, AC_VERB_SET_DIGI_CONVERT_1, 0},

        /* Start with mixer outputs muted */
        {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
        {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
        {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},

        /* Unmute HP pin widget amp left and right (no equiv mixer ctrl) */
        {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
        /* Unmute Line1 pin widget amp left and right (no equiv mixer ctrl) */
        {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
	/* Unmute pin widget used for Line-in (no equiv mixer ctrl) */
        {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},

        /* Mute capture amp left and right */
        {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
        /* Set ADC connection select to line in (on mic1 pin) */
        {0x04, AC_VERB_SET_CONNECT_SEL, 0x00},

        /* Mute all inputs to mixer widget (even unconnected ones) */
        {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */
        {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */
        {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */
        {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */
        {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */
        {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */
        {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */
        {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */
};

static struct hda_pcm_stream alc260_pcm_analog_playback = {
	.substreams = 1,
	.channels_min = 2,
@@ -2347,6 +2486,8 @@ static struct hda_pcm_stream alc260_pcm_analog_capture = {
static struct hda_board_config alc260_cfg_tbl[] = {
	{ .modelname = "hp", .config = ALC260_HP },
	{ .pci_subvendor = 0x103c, .config = ALC260_HP },
	{ .modelname = "fujitsu", .config = ALC260_FUJITSU_S702x },
	{ .pci_subvendor = 0x10cf, .pci_subdevice = 0x1326, .config = ALC260_FUJITSU_S702x },
	{}
};

@@ -2373,14 +2514,23 @@ static int patch_alc260(struct hda_codec *codec)
		spec->mixers[spec->num_mixers] = alc260_hp_mixer;
		spec->num_mixers++;
		break;
	case ALC260_FUJITSU_S702x:
		spec->mixers[spec->num_mixers] = alc260_fujitsu_mixer;
		spec->num_mixers++;
		break;
	default:
		spec->mixers[spec->num_mixers] = alc260_base_mixer;
		spec->num_mixers++;
		break;
	}

	if (board_config != ALC260_FUJITSU_S702x) {
		spec->init_verbs[0] = alc260_init_verbs;
		spec->num_init_verbs = 1;
	} else {
		spec->init_verbs[0] = alc260_fujitsu_init_verbs;
		spec->num_init_verbs = 1;
	}

	spec->channel_mode = alc260_modes;
	spec->num_channel_mode = ARRAY_SIZE(alc260_modes);
@@ -2393,7 +2543,11 @@ static int patch_alc260(struct hda_codec *codec)
	spec->multiout.num_dacs = ARRAY_SIZE(alc260_dac_nids);
	spec->multiout.dac_nids = alc260_dac_nids;

	if (board_config != ALC260_FUJITSU_S702x) {
		spec->input_mux = &alc260_capture_source;
	} else {
		spec->input_mux = &alc260_fujitsu_capture_source;
	}
	switch (board_config) {
	case ALC260_HP:
		spec->num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids);