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

Commit c155b914 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6:
  ALSA: ice1724 - Fix a typo in IEC958 PCM name
  ASoC: fix davinci-sffsdr buglet
  ALSA: sound/usb: Use negated usb_endpoint_xfer_control, etc
  ALSA: hda - cxt5051 report jack state
  ALSA: hda - add basic jack reporting functions to patch_conexant.c
  ALSA: Use usb_set/get_intfdata
  ASoC: Clean up kerneldoc warnings
  ASoC: Fix pxa2xx-pcm checks for invalid DMA channels
  LSA: hda - Add HP Acacia detection
  ALSA: hda - fix name for ALC1200
  ALSA: sound/usb: use USB API functions rather than constants
  ASoC: TWL4030: DAPM based capture implementation
  ASoC: TWL4030: Make the enum filter generic for twl4030
parents e9af797d 3a5e1d17
Loading
Loading
Loading
Loading
+111 −3
Original line number Diff line number Diff line
@@ -25,6 +25,8 @@
#include <linux/slab.h>
#include <linux/pci.h>
#include <sound/core.h>
#include <sound/jack.h>

#include "hda_codec.h"
#include "hda_local.h"

@@ -37,7 +39,20 @@
#define CONEXANT_HP_EVENT	0x37
#define CONEXANT_MIC_EVENT	0x38

/* Conexant 5051 specific */

#define CXT5051_SPDIF_OUT	0x1C
#define CXT5051_PORTB_EVENT	0x38
#define CXT5051_PORTC_EVENT	0x39


struct conexant_jack {

	hda_nid_t nid;
	int type;
	struct snd_jack *jack;

};

struct conexant_spec {

@@ -83,6 +98,9 @@ struct conexant_spec {

	unsigned int spdif_route;

	/* jack detection */
	struct snd_array jacks;

	/* dynamic controls, init_verbs and input_mux */
	struct auto_pin_cfg autocfg;
	struct hda_input_mux private_imux;
@@ -329,6 +347,86 @@ static int conexant_mux_enum_put(struct snd_kcontrol *kcontrol,
				     &spec->cur_mux[adc_idx]);
}

static int conexant_add_jack(struct hda_codec *codec,
		hda_nid_t nid, int type)
{
	struct conexant_spec *spec;
	struct conexant_jack *jack;
	const char *name;

	spec = codec->spec;
	snd_array_init(&spec->jacks, sizeof(*jack), 32);
	jack = snd_array_new(&spec->jacks);
	name = (type == SND_JACK_HEADPHONE) ? "Headphone" : "Mic" ;

	if (!jack)
		return -ENOMEM;

	jack->nid = nid;
	jack->type = type;

	return snd_jack_new(codec->bus->card, name, type, &jack->jack);
}

static void conexant_report_jack(struct hda_codec *codec, hda_nid_t nid)
{
	struct conexant_spec *spec = codec->spec;
	struct conexant_jack *jacks = spec->jacks.list;

	if (jacks) {
		int i;
		for (i = 0; i < spec->jacks.used; i++) {
			if (jacks->nid == nid) {
				unsigned int present;
				present = snd_hda_codec_read(codec, nid, 0,
						AC_VERB_GET_PIN_SENSE, 0) &
					AC_PINSENSE_PRESENCE;

				present = (present) ? jacks->type : 0 ;

				snd_jack_report(jacks->jack,
						present);
			}
			jacks++;
		}
	}
}

static int conexant_init_jacks(struct hda_codec *codec)
{
#ifdef CONFIG_SND_JACK
	struct conexant_spec *spec = codec->spec;
	int i;

	for (i = 0; i < spec->num_init_verbs; i++) {
		const struct hda_verb *hv;

		hv = spec->init_verbs[i];
		while (hv->nid) {
			int err = 0;
			switch (hv->param ^ AC_USRSP_EN) {
			case CONEXANT_HP_EVENT:
				err = conexant_add_jack(codec, hv->nid,
						SND_JACK_HEADPHONE);
				conexant_report_jack(codec, hv->nid);
				break;
			case CXT5051_PORTC_EVENT:
			case CONEXANT_MIC_EVENT:
				err = conexant_add_jack(codec, hv->nid,
						SND_JACK_MICROPHONE);
				conexant_report_jack(codec, hv->nid);
				break;
			}
			if (err < 0)
				return err;
			++hv;
		}
	}
#endif
	return 0;

}

static int conexant_init(struct hda_codec *codec)
{
	struct conexant_spec *spec = codec->spec;
@@ -341,6 +439,16 @@ static int conexant_init(struct hda_codec *codec)

static void conexant_free(struct hda_codec *codec)
{
#ifdef CONFIG_SND_JACK
	struct conexant_spec *spec = codec->spec;
	if (spec->jacks.list) {
		struct conexant_jack *jacks = spec->jacks.list;
		int i;
		for (i = 0; i < spec->jacks.used; i++)
			snd_device_free(codec->bus->card, &jacks[i].jack);
		snd_array_free(&spec->jacks);
	}
#endif
	kfree(codec->spec);
}

@@ -1526,9 +1634,6 @@ static int patch_cxt5047(struct hda_codec *codec)
/* Conexant 5051 specific */
static hda_nid_t cxt5051_dac_nids[1] = { 0x10 };
static hda_nid_t cxt5051_adc_nids[2] = { 0x14, 0x15 };
#define CXT5051_SPDIF_OUT	0x1C
#define CXT5051_PORTB_EVENT	0x38
#define CXT5051_PORTC_EVENT	0x39

static struct hda_channel_mode cxt5051_modes[1] = {
	{ 2, NULL },
@@ -1608,6 +1713,7 @@ static void cxt5051_hp_automute(struct hda_codec *codec)
static void cxt5051_hp_unsol_event(struct hda_codec *codec,
				   unsigned int res)
{
	int nid = (res & AC_UNSOL_RES_SUBTAG) >> 20;
	switch (res >> 26) {
	case CONEXANT_HP_EVENT:
		cxt5051_hp_automute(codec);
@@ -1619,6 +1725,7 @@ static void cxt5051_hp_unsol_event(struct hda_codec *codec,
		cxt5051_portc_automic(codec);
		break;
	}
	conexant_report_jack(codec, nid);
}

static struct snd_kcontrol_new cxt5051_mixers[] = {
@@ -1693,6 +1800,7 @@ static struct hda_verb cxt5051_init_verbs[] = {
static int cxt5051_init(struct hda_codec *codec)
{
	conexant_init(codec);
	conexant_init_jacks(codec);
	if (codec->patch_ops.unsol_event) {
		cxt5051_hp_automute(codec);
		cxt5051_portb_automic(codec);
+2 −1
Original line number Diff line number Diff line
@@ -8467,6 +8467,7 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = {
	SND_PCI_QUIRK(0x103c, 0x2a4f, "HP Samba", ALC888_3ST_HP),
	SND_PCI_QUIRK(0x103c, 0x2a60, "HP Lucknow", ALC888_3ST_HP),
	SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC883_6ST_DIG),
	SND_PCI_QUIRK(0x103c, 0x2a66, "HP Acacia", ALC888_3ST_HP),
	SND_PCI_QUIRK(0x1043, 0x1873, "Asus M90V", ALC888_ASUS_M90V),
	SND_PCI_QUIRK(0x1043, 0x8249, "Asus M2A-VM HDMI", ALC883_3ST_6ch_DIG),
	SND_PCI_QUIRK(0x1043, 0x82fe, "Asus P5Q-EM HDMI", ALC1200_ASUS_P5Q),
@@ -16638,9 +16639,9 @@ static struct hda_codec_preset snd_hda_preset_realtek[] = {
	  .patch = patch_alc882 }, /* should be patch_alc883() in future */
	{ .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 },
	{ .id = 0x10ec0887, .name = "ALC887", .patch = patch_alc883 },
	{ .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc883 },
	{ .id = 0x10ec0888, .rev = 0x100101, .name = "ALC1200",
	  .patch = patch_alc883 },
	{ .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc883 },
	{ .id = 0x10ec0889, .name = "ALC889", .patch = patch_alc883 },
	{} /* terminator */
};
+1 −1
Original line number Diff line number Diff line
@@ -1239,7 +1239,7 @@ static int __devinit snd_vt1724_pcm_spdif(struct snd_ice1712 *ice, int device)
	if (ice->force_pdma4 || ice->force_rdma1)
		name = "ICE1724 Secondary";
	else
		name = "IEC1724 IEC958";
		name = "ICE1724 IEC958";
	err = snd_pcm_new(ice->card, name, device, play, capt, &pcm);
	if (err < 0)
		return err;
+189 −188
Original line number Diff line number Diff line
@@ -298,30 +298,107 @@ static const struct soc_enum twl4030_handsfreer_enum =
static const struct snd_kcontrol_new twl4030_dapm_handsfreer_control =
SOC_DAPM_ENUM("Route", twl4030_handsfreer_enum);

static int outmixer_event(struct snd_soc_dapm_widget *w,
/* Left analog microphone selection */
static const char *twl4030_analoglmic_texts[] =
		{"Off", "Main mic", "Headset mic", "Invalid", "AUXL",
		 "Invalid", "Invalid", "Invalid", "Carkit mic"};

static const struct soc_enum twl4030_analoglmic_enum =
	SOC_ENUM_SINGLE(TWL4030_REG_ANAMICL, 0,
			ARRAY_SIZE(twl4030_analoglmic_texts),
			twl4030_analoglmic_texts);

static const struct snd_kcontrol_new twl4030_dapm_analoglmic_control =
SOC_DAPM_ENUM("Route", twl4030_analoglmic_enum);

/* Right analog microphone selection */
static const char *twl4030_analogrmic_texts[] =
		{"Off", "Sub mic", "Invalid", "Invalid", "AUXR"};

static const struct soc_enum twl4030_analogrmic_enum =
	SOC_ENUM_SINGLE(TWL4030_REG_ANAMICR, 0,
			ARRAY_SIZE(twl4030_analogrmic_texts),
			twl4030_analogrmic_texts);

static const struct snd_kcontrol_new twl4030_dapm_analogrmic_control =
SOC_DAPM_ENUM("Route", twl4030_analogrmic_enum);

/* TX1 L/R Analog/Digital microphone selection */
static const char *twl4030_micpathtx1_texts[] =
		{"Analog", "Digimic0"};

static const struct soc_enum twl4030_micpathtx1_enum =
	SOC_ENUM_SINGLE(TWL4030_REG_ADCMICSEL, 0,
			ARRAY_SIZE(twl4030_micpathtx1_texts),
			twl4030_micpathtx1_texts);

static const struct snd_kcontrol_new twl4030_dapm_micpathtx1_control =
SOC_DAPM_ENUM("Route", twl4030_micpathtx1_enum);

/* TX2 L/R Analog/Digital microphone selection */
static const char *twl4030_micpathtx2_texts[] =
		{"Analog", "Digimic1"};

static const struct soc_enum twl4030_micpathtx2_enum =
	SOC_ENUM_SINGLE(TWL4030_REG_ADCMICSEL, 2,
			ARRAY_SIZE(twl4030_micpathtx2_texts),
			twl4030_micpathtx2_texts);

static const struct snd_kcontrol_new twl4030_dapm_micpathtx2_control =
SOC_DAPM_ENUM("Route", twl4030_micpathtx2_enum);

/*
 * This function filters out the non valid mux settings, named as "Invalid"
 * in the enum texts.
 * Just refuse to set an invalid mux mode.
 */
static int twl4030_enum_event(struct snd_soc_dapm_widget *w,
	struct snd_kcontrol *kcontrol, int event)
{
	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
	int ret = 0;
	int val;

	switch (e->reg) {
	case TWL4030_REG_PREDL_CTL:
	case TWL4030_REG_PREDR_CTL:
	case TWL4030_REG_EAR_CTL:
	val = w->value >> e->shift_l;
		if (val == 3) {
			printk(KERN_WARNING
			"Invalid MUX setting for register 0x%02x (%d)\n",
	if (!strcmp("Invalid", e->texts[val])) {
		printk(KERN_WARNING "Invalid MUX setting on 0x%02x (%d)\n",
			e->reg, val);
		ret = -1;
	}
		break;
	}

	return ret;
}

static int micpath_event(struct snd_soc_dapm_widget *w,
	struct snd_kcontrol *kcontrol, int event)
{
	struct soc_enum *e = (struct soc_enum *)w->kcontrols->private_value;
	unsigned char adcmicsel, micbias_ctl;

	adcmicsel = twl4030_read_reg_cache(w->codec, TWL4030_REG_ADCMICSEL);
	micbias_ctl = twl4030_read_reg_cache(w->codec, TWL4030_REG_MICBIAS_CTL);
	/* Prepare the bits for the given TX path:
	 * shift_l == 0: TX1 microphone path
	 * shift_l == 2: TX2 microphone path */
	if (e->shift_l) {
		/* TX2 microphone path */
		if (adcmicsel & TWL4030_TX2IN_SEL)
			micbias_ctl |= TWL4030_MICBIAS2_CTL; /* digimic */
		else
			micbias_ctl &= ~TWL4030_MICBIAS2_CTL;
	} else {
		/* TX1 microphone path */
		if (adcmicsel & TWL4030_TX1IN_SEL)
			micbias_ctl |= TWL4030_MICBIAS1_CTL; /* digimic */
		else
			micbias_ctl &= ~TWL4030_MICBIAS1_CTL;
	}

	twl4030_write(w->codec, TWL4030_REG_MICBIAS_CTL, micbias_ctl);

	return 0;
}

static int handsfree_event(struct snd_soc_dapm_widget *w,
		struct snd_kcontrol *kcontrol, int event)
{
@@ -503,162 +580,6 @@ static int snd_soc_put_volsw_r2_twl4030(struct snd_kcontrol *kcontrol,
	return err;
}

static int twl4030_get_left_input(struct snd_kcontrol *kcontrol,
	struct snd_ctl_elem_value *ucontrol)
{
	struct snd_soc_codec *codec = kcontrol->private_data;
	u8 reg = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL);
	int result = 0;

	/* one bit must be set a time */
	reg &= TWL4030_CKMIC_EN | TWL4030_AUXL_EN | TWL4030_HSMIC_EN
			| TWL4030_MAINMIC_EN;
	if (reg != 0) {
		result++;
		while ((reg & 1) == 0) {
			result++;
			reg >>= 1;
		}
	}

	ucontrol->value.integer.value[0] = result;
	return 0;
}

static int twl4030_put_left_input(struct snd_kcontrol *kcontrol,
	struct snd_ctl_elem_value *ucontrol)
{
	struct snd_soc_codec *codec = kcontrol->private_data;
	int value = ucontrol->value.integer.value[0];
	u8 anamicl, micbias, avadc_ctl;

	anamicl = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL);
	anamicl &= ~(TWL4030_CKMIC_EN | TWL4030_AUXL_EN | TWL4030_HSMIC_EN
			| TWL4030_MAINMIC_EN);
	micbias = twl4030_read_reg_cache(codec, TWL4030_REG_MICBIAS_CTL);
	micbias &= ~(TWL4030_HSMICBIAS_EN | TWL4030_MICBIAS1_EN);
	avadc_ctl = twl4030_read_reg_cache(codec, TWL4030_REG_AVADC_CTL);

	switch (value) {
	case 1:
		anamicl |= TWL4030_MAINMIC_EN;
		micbias |= TWL4030_MICBIAS1_EN;
		break;
	case 2:
		anamicl |= TWL4030_HSMIC_EN;
		micbias |= TWL4030_HSMICBIAS_EN;
		break;
	case 3:
		anamicl |= TWL4030_AUXL_EN;
		break;
	case 4:
		anamicl |= TWL4030_CKMIC_EN;
		break;
	default:
		break;
	}

	/* If some input is selected, enable amp and ADC */
	if (value != 0) {
		anamicl |= TWL4030_MICAMPL_EN;
		avadc_ctl |= TWL4030_ADCL_EN;
	} else {
		anamicl &= ~TWL4030_MICAMPL_EN;
		avadc_ctl &= ~TWL4030_ADCL_EN;
	}

	twl4030_write(codec, TWL4030_REG_ANAMICL, anamicl);
	twl4030_write(codec, TWL4030_REG_MICBIAS_CTL, micbias);
	twl4030_write(codec, TWL4030_REG_AVADC_CTL, avadc_ctl);

	return 1;
}

static int twl4030_get_right_input(struct snd_kcontrol *kcontrol,
	struct snd_ctl_elem_value *ucontrol)
{
	struct snd_soc_codec *codec = kcontrol->private_data;
	u8 reg = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICR);
	int value = 0;

	reg &= TWL4030_SUBMIC_EN|TWL4030_AUXR_EN;
	switch (reg) {
	case TWL4030_SUBMIC_EN:
		value = 1;
		break;
	case TWL4030_AUXR_EN:
		value = 2;
		break;
	default:
		break;
	}

	ucontrol->value.integer.value[0] = value;
	return 0;
}

static int twl4030_put_right_input(struct snd_kcontrol *kcontrol,
	struct snd_ctl_elem_value *ucontrol)
{
	struct snd_soc_codec *codec = kcontrol->private_data;
	int value = ucontrol->value.integer.value[0];
	u8 anamicr, micbias, avadc_ctl;

	anamicr = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICR);
	anamicr &= ~(TWL4030_SUBMIC_EN|TWL4030_AUXR_EN);
	micbias = twl4030_read_reg_cache(codec, TWL4030_REG_MICBIAS_CTL);
	micbias &= ~TWL4030_MICBIAS2_EN;
	avadc_ctl = twl4030_read_reg_cache(codec, TWL4030_REG_AVADC_CTL);

	switch (value) {
	case 1:
		anamicr |= TWL4030_SUBMIC_EN;
		micbias |= TWL4030_MICBIAS2_EN;
		break;
	case 2:
		anamicr |= TWL4030_AUXR_EN;
		break;
	default:
		break;
	}

	if (value != 0) {
		anamicr |= TWL4030_MICAMPR_EN;
		avadc_ctl |= TWL4030_ADCR_EN;
	} else {
		anamicr &= ~TWL4030_MICAMPR_EN;
		avadc_ctl &= ~TWL4030_ADCR_EN;
	}

	twl4030_write(codec, TWL4030_REG_ANAMICR, anamicr);
	twl4030_write(codec, TWL4030_REG_MICBIAS_CTL, micbias);
	twl4030_write(codec, TWL4030_REG_AVADC_CTL, avadc_ctl);

	return 1;
}

static const char *twl4030_left_in_sel[] = {
	"None",
	"Main Mic",
	"Headset Mic",
	"Line In",
	"Carkit Mic",
};

static const char *twl4030_right_in_sel[] = {
	"None",
	"Sub Mic",
	"Line In",
};

static const struct soc_enum twl4030_left_input_mux =
	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(twl4030_left_in_sel),
		twl4030_left_in_sel);

static const struct soc_enum twl4030_right_input_mux =
	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(twl4030_right_in_sel),
		twl4030_right_in_sel);

/*
 * FGAIN volume control:
 * from -62 to 0 dB in 1 dB steps (mute instead of -63 dB)
@@ -741,18 +662,15 @@ static const struct snd_kcontrol_new twl4030_snd_controls[] = {
		TWL4030_REG_EAR_CTL, 4, 3, 0, output_tvl),

	/* Common capture gain controls */
	SOC_DOUBLE_R_TLV("Capture Volume",
	SOC_DOUBLE_R_TLV("TX1 Digital Capture Volume",
		TWL4030_REG_ATXL1PGA, TWL4030_REG_ATXR1PGA,
		0, 0x1f, 0, digital_capture_tlv),
	SOC_DOUBLE_R_TLV("TX2 Digital Capture Volume",
		TWL4030_REG_AVTXL2PGA, TWL4030_REG_AVTXR2PGA,
		0, 0x1f, 0, digital_capture_tlv),

	SOC_DOUBLE_TLV("Input Boost Volume", TWL4030_REG_ANAMIC_GAIN,
	SOC_DOUBLE_TLV("Analog Capture Volume", TWL4030_REG_ANAMIC_GAIN,
		0, 3, 5, 0, input_gain_tlv),

	/* Input source controls */
	SOC_ENUM_EXT("Left Input Source", twl4030_left_input_mux,
		twl4030_get_left_input, twl4030_put_left_input),
	SOC_ENUM_EXT("Right Input Source", twl4030_right_input_mux,
		twl4030_get_right_input, twl4030_put_right_input),
};

/* add non dapm controls */
@@ -772,9 +690,19 @@ static int twl4030_add_controls(struct snd_soc_codec *codec)
}

static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
	SND_SOC_DAPM_INPUT("INL"),
	SND_SOC_DAPM_INPUT("INR"),

	/* Left channel inputs */
	SND_SOC_DAPM_INPUT("MAINMIC"),
	SND_SOC_DAPM_INPUT("HSMIC"),
	SND_SOC_DAPM_INPUT("AUXL"),
	SND_SOC_DAPM_INPUT("CARKITMIC"),
	/* Right channel inputs */
	SND_SOC_DAPM_INPUT("SUBMIC"),
	SND_SOC_DAPM_INPUT("AUXR"),
	/* Digital microphones (Stereo) */
	SND_SOC_DAPM_INPUT("DIGIMIC0"),
	SND_SOC_DAPM_INPUT("DIGIMIC1"),

	/* Outputs */
	SND_SOC_DAPM_OUTPUT("OUTL"),
	SND_SOC_DAPM_OUTPUT("OUTR"),
	SND_SOC_DAPM_OUTPUT("EARPIECE"),
@@ -810,14 +738,14 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
	/* Output MUX controls */
	/* Earpiece */
	SND_SOC_DAPM_MUX_E("Earpiece Mux", SND_SOC_NOPM, 0, 0,
		&twl4030_dapm_earpiece_control, outmixer_event,
		&twl4030_dapm_earpiece_control, twl4030_enum_event,
		SND_SOC_DAPM_PRE_REG),
	/* PreDrivL/R */
	SND_SOC_DAPM_MUX_E("PredriveL Mux", SND_SOC_NOPM, 0, 0,
		&twl4030_dapm_predrivel_control, outmixer_event,
		&twl4030_dapm_predrivel_control, twl4030_enum_event,
		SND_SOC_DAPM_PRE_REG),
	SND_SOC_DAPM_MUX_E("PredriveR Mux", SND_SOC_NOPM, 0, 0,
		&twl4030_dapm_predriver_control, outmixer_event,
		&twl4030_dapm_predriver_control, twl4030_enum_event,
		SND_SOC_DAPM_PRE_REG),
	/* HeadsetL/R */
	SND_SOC_DAPM_MUX("HeadsetL Mux", SND_SOC_NOPM, 0, 0,
@@ -837,8 +765,50 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
		&twl4030_dapm_handsfreer_control, handsfree_event,
		SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),

	SND_SOC_DAPM_ADC("ADCL", "Left Capture", SND_SOC_NOPM, 0, 0),
	SND_SOC_DAPM_ADC("ADCR", "Right Capture", SND_SOC_NOPM, 0, 0),
	/* Introducing four virtual ADC, since TWL4030 have four channel for
	   capture */
	SND_SOC_DAPM_ADC("ADC Virtual Left1", "Left Front Capture",
		SND_SOC_NOPM, 0, 0),
	SND_SOC_DAPM_ADC("ADC Virtual Right1", "Right Front Capture",
		SND_SOC_NOPM, 0, 0),
	SND_SOC_DAPM_ADC("ADC Virtual Left2", "Left Rear Capture",
		SND_SOC_NOPM, 0, 0),
	SND_SOC_DAPM_ADC("ADC Virtual Right2", "Right Rear Capture",
		SND_SOC_NOPM, 0, 0),

	/* Analog/Digital mic path selection.
	   TX1 Left/Right: either analog Left/Right or Digimic0
	   TX2 Left/Right: either analog Left/Right or Digimic1 */
	SND_SOC_DAPM_MUX_E("TX1 Capture Route", SND_SOC_NOPM, 0, 0,
		&twl4030_dapm_micpathtx1_control, micpath_event,
		SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD|
		SND_SOC_DAPM_POST_REG),
	SND_SOC_DAPM_MUX_E("TX2 Capture Route", SND_SOC_NOPM, 0, 0,
		&twl4030_dapm_micpathtx2_control, micpath_event,
		SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD|
		SND_SOC_DAPM_POST_REG),

	/* Analog input muxes with power switch for the physical ADCL/R */
	SND_SOC_DAPM_MUX_E("Analog Left Capture Route",
		TWL4030_REG_AVADC_CTL, 3, 0, &twl4030_dapm_analoglmic_control,
		twl4030_enum_event, SND_SOC_DAPM_PRE_REG),
	SND_SOC_DAPM_MUX_E("Analog Right Capture Route",
		TWL4030_REG_AVADC_CTL, 1, 0, &twl4030_dapm_analogrmic_control,
		twl4030_enum_event, SND_SOC_DAPM_PRE_REG),

	SND_SOC_DAPM_PGA("Analog Left Amplifier",
		TWL4030_REG_ANAMICL, 4, 0, NULL, 0),
	SND_SOC_DAPM_PGA("Analog Right Amplifier",
		TWL4030_REG_ANAMICR, 4, 0, NULL, 0),

	SND_SOC_DAPM_PGA("Digimic0 Enable",
		TWL4030_REG_ADCMICSEL, 1, 0, NULL, 0),
	SND_SOC_DAPM_PGA("Digimic1 Enable",
		TWL4030_REG_ADCMICSEL, 3, 0, NULL, 0),

	SND_SOC_DAPM_MICBIAS("Mic Bias 1", TWL4030_REG_MICBIAS_CTL, 0, 0),
	SND_SOC_DAPM_MICBIAS("Mic Bias 2", TWL4030_REG_MICBIAS_CTL, 1, 0),
	SND_SOC_DAPM_MICBIAS("Headset Mic Bias", TWL4030_REG_MICBIAS_CTL, 2, 0),
};

static const struct snd_soc_dapm_route intercon[] = {
@@ -894,9 +864,39 @@ static const struct snd_soc_dapm_route intercon[] = {
	{"HFL", NULL, "HandsfreeL Mux"},
	{"HFR", NULL, "HandsfreeR Mux"},

	/* inputs */
	{"ADCL", NULL, "INL"},
	{"ADCR", NULL, "INR"},
	/* Capture path */
	{"Analog Left Capture Route", "Main mic", "MAINMIC"},
	{"Analog Left Capture Route", "Headset mic", "HSMIC"},
	{"Analog Left Capture Route", "AUXL", "AUXL"},
	{"Analog Left Capture Route", "Carkit mic", "CARKITMIC"},

	{"Analog Right Capture Route", "Sub mic", "SUBMIC"},
	{"Analog Right Capture Route", "AUXR", "AUXR"},

	{"Analog Left Amplifier", NULL, "Analog Left Capture Route"},
	{"Analog Right Amplifier", NULL, "Analog Right Capture Route"},

	{"Digimic0 Enable", NULL, "DIGIMIC0"},
	{"Digimic1 Enable", NULL, "DIGIMIC1"},

	/* TX1 Left capture path */
	{"TX1 Capture Route", "Analog", "Analog Left Amplifier"},
	{"TX1 Capture Route", "Digimic0", "Digimic0 Enable"},
	/* TX1 Right capture path */
	{"TX1 Capture Route", "Analog", "Analog Right Amplifier"},
	{"TX1 Capture Route", "Digimic0", "Digimic0 Enable"},
	/* TX2 Left capture path */
	{"TX2 Capture Route", "Analog", "Analog Left Amplifier"},
	{"TX2 Capture Route", "Digimic1", "Digimic1 Enable"},
	/* TX2 Right capture path */
	{"TX2 Capture Route", "Analog", "Analog Right Amplifier"},
	{"TX2 Capture Route", "Digimic1", "Digimic1 Enable"},

	{"ADC Virtual Left1", NULL, "TX1 Capture Route"},
	{"ADC Virtual Right1", NULL, "TX1 Capture Route"},
	{"ADC Virtual Left2", NULL, "TX2 Capture Route"},
	{"ADC Virtual Right2", NULL, "TX2 Capture Route"},

};

static int twl4030_add_widgets(struct snd_soc_codec *codec)
@@ -923,6 +923,7 @@ static void twl4030_power_up(struct snd_soc_codec *codec)
	twl4030_write(codec, TWL4030_REG_ANAMICL,
		anamicl | TWL4030_CNCL_OFFSET_START);


	/* wait for offset cancellation to complete */
	do {
		/* this takes a little while, so don't slam i2c */
+7 −0
Original line number Diff line number Diff line
@@ -147,6 +147,13 @@
#define TWL4030_AVADC_CLK_PRIORITY	0x04
#define TWL4030_ADCR_EN			0x02

/* TWL4030_REG_ADCMICSEL (0x08) Fields */

#define TWL4030_DIGMIC1_EN		0x08
#define TWL4030_TX2IN_SEL		0x04
#define TWL4030_DIGMIC0_EN		0x02
#define TWL4030_TX1IN_SEL		0x01

/* AUDIO_IF (0x0E) Fields */

#define TWL4030_AIF_SLAVE_EN		0x80
Loading