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

Commit 36633237 authored by Vinod Koul's avatar Vinod Koul Committed by Mark Brown
Browse files

ASoC: sn95031: Add support for reading mic bias



This patch adds support to read the mic bias voltage
when a jack is inserted. It uses ADC to measure.

Signed-off-by: default avatarVinod Koul <vinod.koul@intel.com>
Signed-off-by: default avatarHarsha Priya <priya.harsha@intel.com>
Acked-by: default avatarLiam Girdwood <lrg@slimlogic.co.uk>
Signed-off-by: default avatarMark Brown <broonie@opensource.wolfsonmicro.com>
parent 42aee9b4
Loading
Loading
Loading
Loading
+123 −4
Original line number Diff line number Diff line
@@ -40,12 +40,129 @@
#define SN95031_RATES (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100)
#define SN95031_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)

/* adc helper functions */

/* enables mic bias voltage */
static void sn95031_enable_mic_bias(struct snd_soc_codec *codec)
{
	snd_soc_write(codec, SN95031_VAUD, BIT(2)|BIT(1)|BIT(0));
	snd_soc_update_bits(codec, SN95031_MICBIAS, BIT(2), BIT(2));
}

/* Enable/Disable the ADC depending on the argument */
static void configure_adc(struct snd_soc_codec *sn95031_codec, int val)
{
	int value = snd_soc_read(sn95031_codec, SN95031_ADC1CNTL1);

	if (val) {
		/* Enable and start the ADC */
		value |= (SN95031_ADC_ENBL | SN95031_ADC_START);
		value &= (~SN95031_ADC_NO_LOOP);
	} else {
		/* Just stop the ADC */
		value &= (~SN95031_ADC_START);
	}
	snd_soc_write(sn95031_codec, SN95031_ADC1CNTL1, value);
}

/*
 * todo:
 * capture paths
 * jack detection
 * PM functions
 * finds an empty channel for conversion
 * If the ADC is not enabled then start using 0th channel
 * itself. Otherwise find an empty channel by looking for a
 * channel in which the stopbit is set to 1. returns the index
 * of the first free channel if succeeds or an error code.
 *
 * Context: can sleep
 *
 */
static int find_free_channel(struct snd_soc_codec *sn95031_codec)
{
	int ret = 0, i, value;

	/* check whether ADC is enabled */
	value = snd_soc_read(sn95031_codec, SN95031_ADC1CNTL1);

	if ((value & SN95031_ADC_ENBL) == 0)
		return 0;

	/* ADC is already enabled; Looking for an empty channel */
	for (i = 0; i <	SN95031_ADC_CHANLS_MAX; i++) {
		value = snd_soc_read(sn95031_codec,
				SN95031_ADC_CHNL_START_ADDR + i);
		if (value & SN95031_STOPBIT_MASK) {
			ret = i;
			break;
		}
	}
	return (ret > SN95031_ADC_LOOP_MAX) ? (-EINVAL) : ret;
}

/* Initialize the ADC for reading micbias values. Can sleep. */
static int sn95031_initialize_adc(struct snd_soc_codec *sn95031_codec)
{
	int base_addr, chnl_addr;
	int value;
	static int channel_index;

	/* Index of the first channel in which the stop bit is set */
	channel_index = find_free_channel(sn95031_codec);
	if (channel_index < 0) {
		pr_err("No free ADC channels");
		return channel_index;
	}

	base_addr = SN95031_ADC_CHNL_START_ADDR + channel_index;

	if (!(channel_index == 0 || channel_index ==  SN95031_ADC_LOOP_MAX)) {
		/* Reset stop bit for channels other than 0 and 12 */
		value = snd_soc_read(sn95031_codec, base_addr);
		/* Set the stop bit to zero */
		snd_soc_write(sn95031_codec, base_addr, value & 0xEF);
		/* Index of the first free channel */
		base_addr++;
		channel_index++;
	}

	/* Since this is the last channel, set the stop bit
	   to 1 by ORing the DIE_SENSOR_CODE with 0x10 */
	snd_soc_write(sn95031_codec, base_addr,
				SN95031_AUDIO_DETECT_CODE | 0x10);

	chnl_addr = SN95031_ADC_DATA_START_ADDR + 2 * channel_index;
	pr_debug("mid_initialize : %x", chnl_addr);
	configure_adc(sn95031_codec, 1);
	return chnl_addr;
}


/* reads the ADC registers and gets the mic bias value in mV. */
static unsigned int sn95031_get_mic_bias(struct snd_soc_codec *codec)
{
	u16 adc_adr = sn95031_initialize_adc(codec);
	u16 adc_val1, adc_val2;
	unsigned int mic_bias;

	sn95031_enable_mic_bias(codec);

	/* Enable the sound card for conversion before reading */
	snd_soc_write(codec, SN95031_ADC1CNTL3, 0x05);
	/* Re-toggle the RRDATARD bit */
	snd_soc_write(codec, SN95031_ADC1CNTL3, 0x04);

	/* Read the higher bits of data */
	msleep(1000);
	adc_val1 = snd_soc_read(codec, adc_adr);
	adc_adr++;
	adc_val2 = snd_soc_read(codec, adc_adr);

	/* Adding lower two bits to the higher bits */
	mic_bias = (adc_val1 << 2) + (adc_val2 & 3);
	mic_bias = (mic_bias * SN95031_ADC_ONE_LSB_MULTIPLIER) / 1000;
	pr_debug("mic bias = %dmV\n", mic_bias);
	return mic_bias;
}
EXPORT_SYMBOL_GPL(sn95031_get_mic_bias);
/*end - adc helper functions */

static inline unsigned int sn95031_read(struct snd_soc_codec *codec,
			unsigned int reg)
@@ -663,6 +780,8 @@ static inline void sn95031_enable_jack_btn(struct snd_soc_codec *codec)

static int sn95031_get_headset_state(struct snd_soc_jack *mfld_jack)
{
	int micbias = sn95031_get_mic_bias(mfld_jack->codec);

	/* Defaulting to HEADSET for now.
	 * will change after adding soc-jack detection apis */
	int jack_type = SND_JACK_HEADSET;
+24 −0
Original line number Diff line number Diff line
@@ -96,7 +96,31 @@
#define SN95031_SSR5			0x384
#define SN95031_SSR6			0x385

/* ADC registers */

#define SN95031_ADC1CNTL1 0x1C0
#define SN95031_ADC_ENBL 0x10
#define SN95031_ADC_START 0x08
#define SN95031_ADC1CNTL3 0x1C2
#define SN95031_ADCTHERM_ENBL 0x04
#define SN95031_ADCRRDATA_ENBL 0x05
#define SN95031_STOPBIT_MASK 16
#define SN95031_ADCTHERM_MASK 4
#define SN95031_ADC_CHANLS_MAX 15 /* Number of ADC channels */
#define SN95031_ADC_LOOP_MAX (SN95031_ADC_CHANLS_MAX - 1)
#define SN95031_ADC_NO_LOOP 0x07
#define SN95031_AUDIO_GPIO_CTRL 0x070

/* ADC channel code values */
#define SN95031_AUDIO_DETECT_CODE 0x06

/* ADC base addresses */
#define SN95031_ADC_CHNL_START_ADDR 0x1C5 /* increments by 1 */
#define SN95031_ADC_DATA_START_ADDR 0x1D4  /* increments by 2 */
/* multipier to convert to mV */
#define SN95031_ADC_ONE_LSB_MULTIPLIER 2346


struct mfld_jack_data {
	int intr_id;
	int micbias_vol;