Загрузка данных


static int vag_and_mute_control(struct snd_soc_component *component,
				 int event, int event_source)
{
	static const u16 mute_mask[] = {
		/*
		 * Mask for HP_POWER_EVENT.
		 * Muxing Headphones have to be wrapped with mute/unmute
		 * headphones only.
		 */
		SGTL5000_HP_MUTE,
		/*
		 * Masks for DAC_POWER_EVENT/ADC_POWER_EVENT.
		 * Muxing DAC or ADC block have to wrapped with mute/unmute
		 * both headphones and line-out.
		 */
		SGTL5000_OUTPUTS_MUTE,
		SGTL5000_OUTPUTS_MUTE
	};

	struct sgtl5000_priv *sgtl5000 =
		snd_soc_component_get_drvdata(component);

	switch (event) {
	case SND_SOC_DAPM_PRE_PMU:
		sgtl5000->mute_state[event_source] =
			mute_output(component, mute_mask[event_source]);
		break;
	case SND_SOC_DAPM_POST_PMU:
		vag_power_on(component, event_source);
		restore_output(component, mute_mask[event_source],
			       sgtl5000->mute_state[event_source]);
		break;
	case SND_SOC_DAPM_PRE_PMD:
		sgtl5000->mute_state[event_source] =
			mute_output(component, mute_mask[event_source]);
		vag_power_off(component, event_source);
		break;
	case SND_SOC_DAPM_POST_PMD:
		restore_output(component, mute_mask[event_source],
			       sgtl5000->mute_state[event_source]);
		break;
	default:
		break;
	}

	return 0;
}

/*
 * Mute Headphone when power it up/down.
 * Control VAG power on HP power path.
 */
static int headphone_pga_event(struct snd_soc_dapm_widget *w,
	struct snd_kcontrol *kcontrol, int event)
{
	struct snd_soc_component *component =
		snd_soc_dapm_to_component(w->dapm);

	return vag_and_mute_control(component, event, HP_POWER_EVENT);
}

/* As manual describes, ADC/DAC powering up/down requires
 * to mute outputs to avoid pops.
 * Control VAG power on ADC/DAC power path.
 */
static int adc_updown_depop(struct snd_soc_dapm_widget *w,
	struct snd_kcontrol *kcontrol, int event)
{
	struct snd_soc_component *component =
		snd_soc_dapm_to_component(w->dapm);

	return vag_and_mute_control(component, event, ADC_POWER_EVENT);
}

static int dac_updown_depop(struct snd_soc_dapm_widget *w,
	struct snd_kcontrol *kcontrol, int event)
{
	struct snd_soc_component *component =
		snd_soc_dapm_to_component(w->dapm);

	return vag_and_mute_control(component, event, DAC_POWER_EVENT);
}

/* input sources for ADC */
static const char *adc_mux_text[] = {
	"MIC_IN", "LINE_IN"
};

static SOC_ENUM_SINGLE_DECL(adc_enum,
			    SGTL5000_CHIP_ANA_CTRL, 2,
			    adc_mux_text);

static const struct snd_kcontrol_new adc_mux =
SOC_DAPM_ENUM("Capture Mux", adc_enum);

/* input sources for headphone */
static const char *hp_mux_text[] = {
	"DAC", "LINE_IN"
};

static SOC_ENUM_SINGLE_DECL(hp_enum,
			    SGTL5000_CHIP_ANA_CTRL, 6,
			    hp_mux_text);

static const struct snd_kcontrol_new hp_mux =
SOC_DAPM_ENUM("Headphone Mux", hp_enum);

/* input sources for DAC */
static const char *dac_mux_text[] = {
	"ADC", "I2S", "Rsvrd", "DAP"
};

static SOC_ENUM_SINGLE_DECL(dac_enum,
			    SGTL5000_CHIP_SSS_CTRL, SGTL5000_DAC_SEL_SHIFT,
			    dac_mux_text);

static const struct snd_kcontrol_new dac_mux =
SOC_DAPM_ENUM("Digital Input Mux", dac_enum);

/* input sources for DAP */
static const char *dap_mux_text[] = {
	"ADC", "I2S"
};

static SOC_ENUM_SINGLE_DECL(dap_enum,
			    SGTL5000_CHIP_SSS_CTRL, SGTL5000_DAP_SEL_SHIFT,
			    dap_mux_text);

static const struct snd_kcontrol_new dap_mux =
SOC_DAPM_ENUM("DAP Mux", dap_enum);

/* input sources for DAP mix */
static const char *dapmix_mux_text[] = {
	"ADC", "I2S"
};

static SOC_ENUM_SINGLE_DECL(dapmix_enum,
			    SGTL5000_CHIP_SSS_CTRL, SGTL5000_DAP_MIX_SEL_SHIFT,
			    dapmix_mux_text);

static const struct snd_kcontrol_new dapmix_mux =
SOC_DAPM_ENUM("DAP MIX Mux", dapmix_enum);


static const struct snd_soc_dapm_widget sgtl5000_dapm_widgets[] = {
	SND_SOC_DAPM_INPUT("LINE_IN"),
	SND_SOC_DAPM_INPUT("MIC_IN"),

	SND_SOC_DAPM_OUTPUT("HP_OUT"),
	SND_SOC_DAPM_OUTPUT("LINE_OUT"),

	SND_SOC_DAPM_SUPPLY("Mic Bias", SGTL5000_CHIP_MIC_CTRL, 8, 0,
			    mic_bias_event,
			    SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),

	SND_SOC_DAPM_PGA_E("HP", SGTL5000_CHIP_ANA_POWER, 4, 0, NULL, 0,
			   headphone_pga_event,
			   SND_SOC_DAPM_PRE_POST_PMU |
			   SND_SOC_DAPM_PRE_POST_PMD),
	SND_SOC_DAPM_PGA("LO", SGTL5000_CHIP_ANA_POWER, 0, 0, NULL, 0),

	SND_SOC_DAPM_MUX("Capture Mux", SND_SOC_NOPM, 0, 0, &adc_mux),
	SND_SOC_DAPM_MUX("Headphone Mux", SND_SOC_NOPM, 0, 0, &hp_mux),
	SND_SOC_DAPM_MUX("Digital Input Mux", SND_SOC_NOPM, 0, 0, &dac_mux),
	SND_SOC_DAPM_MUX("DAP Mux", SGTL5000_DAP_CTRL, 0, 0, &dap_mux),
	SND_SOC_DAPM_MUX("DAP MIX Mux", SGTL5000_DAP_CTRL, 4, 0, &dapmix_mux),
	SND_SOC_DAPM_MIXER("DAP", SGTL5000_CHIP_DIG_POWER, 4, 0, NULL, 0),


	/* aif for i2s input */
	SND_SOC_DAPM_AIF_IN("AIFIN", "Playback",
				0, SGTL5000_CHIP_DIG_POWER,
				0, 0),

	/* aif for i2s output */
	SND_SOC_DAPM_AIF_OUT("AIFOUT", "Capture",
				0, SGTL5000_CHIP_DIG_POWER,
				1, 0),

	SND_SOC_DAPM_ADC_E("ADC", "Capture", SGTL5000_CHIP_ANA_POWER, 1, 0,
			   adc_updown_depop, SND_SOC_DAPM_PRE_POST_PMU |
			   SND_SOC_DAPM_PRE_POST_PMD),
	SND_SOC_DAPM_DAC_E("DAC", "Playback", SGTL5000_CHIP_ANA_POWER, 3, 0,
			   dac_updown_depop, SND_SOC_DAPM_PRE_POST_PMU |
			   SND_SOC_DAPM_PRE_POST_PMD),
};

/* routes for sgtl5000 */
static const struct snd_soc_dapm_route sgtl5000_dapm_routes[] = {
	{"Capture Mux", "LINE_IN", "LINE_IN"},	/* line_in --> adc_mux */
	{"Capture Mux", "MIC_IN", "MIC_IN"},	/* mic_in --> adc_mux */

	{"ADC", NULL, "Capture Mux"},		/* adc_mux --> adc */
	{"AIFOUT", NULL, "ADC"},		/* adc --> i2s_out */

	{"DAP Mux", "ADC", "ADC"},		/* adc --> DAP mux */
	{"DAP Mux", NULL, "AIFIN"},		/* i2s --> DAP mux */
	{"DAP", NULL, "DAP Mux"},		/* DAP mux --> dap */

	{"DAP MIX Mux", "ADC", "ADC"},		/* adc --> DAP MIX mux */
	{"DAP MIX Mux", NULL, "AIFIN"},		/* i2s --> DAP MIX mux */
	{"DAP", NULL, "DAP MIX Mux"},		/* DAP MIX mux --> dap */

	{"Digital Input Mux", "ADC", "ADC"},	/* adc --> audio mux */
	{"Digital Input Mux", NULL, "AIFIN"},	/* i2s --> audio mux */
	{"Digital Input Mux", NULL, "DAP"},	/* dap --> audio mux */
	{"DAC", NULL, "Digital Input Mux"},	/* audio mux --> dac */

	{"Headphone Mux", "DAC", "DAC"},	/* dac --> hp_mux */
	{"LO", NULL, "DAC"},			/* dac --> line_out */

	{"Headphone Mux", "LINE_IN", "LINE_IN"},/* line_in --> hp_mux */
	{"HP", NULL, "Headphone Mux"},		/* hp_mux --> hp */

	{"LINE_OUT", NULL, "LO"},
	{"HP_OUT", NULL, "HP"},
};

/* custom function to fetch info of PCM playback volume */
static int dac_info_volsw(struct snd_kcontrol *kcontrol,
			  struct snd_ctl_elem_info *uinfo)
{
	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
	uinfo->count = 2;
	uinfo->value.integer.min = 0;
	uinfo->value.integer.max = 0xfc - 0x3c;
	return 0;
}