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


#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/clk.h>
#include <linux/log2.h>
#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/consumer.h>
#include <sound/core.h>
#include <sound/tlv.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/initval.h>

#include "sgtl5000.h"

#define SGTL5000_DAP_REG_OFFSET	0x0100
#define SGTL5000_MAX_REG_OFFSET	0x013A

/* Delay for the VAG ramp up */
#define SGTL5000_VAG_POWERUP_DELAY 500 /* ms */
/* Delay for the VAG ramp down */
#define SGTL5000_VAG_POWERDOWN_DELAY 500 /* ms */

#define SGTL5000_OUTPUTS_MUTE (SGTL5000_HP_MUTE | SGTL5000_LINE_OUT_MUTE)

/* default value of sgtl5000 registers */
static const struct reg_default sgtl5000_reg_defaults[] = {
	{ SGTL5000_CHIP_DIG_POWER,		0x0000 },
	{ SGTL5000_CHIP_I2S_CTRL,		0x0010 },
	{ SGTL5000_CHIP_SSS_CTRL,		0x0010 },
	{ SGTL5000_CHIP_ADCDAC_CTRL,		0x020c },
	{ SGTL5000_CHIP_DAC_VOL,		0x3c3c },
	{ SGTL5000_CHIP_PAD_STRENGTH,		0x015f },
	{ SGTL5000_CHIP_ANA_ADC_CTRL,		0x0000 },
	{ SGTL5000_CHIP_ANA_HP_CTRL,		0x1818 },
	{ SGTL5000_CHIP_ANA_CTRL,		0x0111 },
	{ SGTL5000_CHIP_REF_CTRL,		0x0000 },
	{ SGTL5000_CHIP_MIC_CTRL,		0x0000 },
	{ SGTL5000_CHIP_LINE_OUT_CTRL,		0x0000 },
	{ SGTL5000_CHIP_LINE_OUT_VOL,		0x0404 },
	{ SGTL5000_CHIP_PLL_CTRL,		0x5000 },
	{ SGTL5000_CHIP_CLK_TOP_CTRL,		0x0000 },
	{ SGTL5000_CHIP_ANA_STATUS,		0x0000 },
	{ SGTL5000_CHIP_SHORT_CTRL,		0x0000 },
	{ SGTL5000_CHIP_ANA_TEST2,		0x0000 },
	{ SGTL5000_DAP_CTRL,			0x0000 },
	{ SGTL5000_DAP_PEQ,			0x0000 },
	{ SGTL5000_DAP_BASS_ENHANCE,		0x0040 },
	{ SGTL5000_DAP_BASS_ENHANCE_CTRL,	0x051f },
	{ SGTL5000_DAP_AUDIO_EQ,		0x0000 },
	{ SGTL5000_DAP_SURROUND,		0x0040 },
	{ SGTL5000_DAP_EQ_BASS_BAND0,		0x002f },
	{ SGTL5000_DAP_EQ_BASS_BAND1,		0x002f },
	{ SGTL5000_DAP_EQ_BASS_BAND2,		0x002f },
	{ SGTL5000_DAP_EQ_BASS_BAND3,		0x002f },
	{ SGTL5000_DAP_EQ_BASS_BAND4,		0x002f },
	{ SGTL5000_DAP_MAIN_CHAN,		0x8000 },
	{ SGTL5000_DAP_MIX_CHAN,		0x0000 },
	{ SGTL5000_DAP_AVC_CTRL,		0x5100 },
	{ SGTL5000_DAP_AVC_THRESHOLD,		0x1473 },
	{ SGTL5000_DAP_AVC_ATTACK,		0x0028 },
	{ SGTL5000_DAP_AVC_DECAY,		0x0050 },
};

/* AVC: Threshold dB -> register: pre-calculated values */
static const u16 avc_thr_db2reg[97] = {
	0x5168, 0x488E, 0x40AA, 0x39A1, 0x335D, 0x2DC7, 0x28CC, 0x245D, 0x2068,
	0x1CE2, 0x19BE, 0x16F1, 0x1472, 0x1239, 0x103E, 0x0E7A, 0x0CE6, 0x0B7F,
	0x0A3F, 0x0922, 0x0824, 0x0741, 0x0677, 0x05C3, 0x0522, 0x0493, 0x0414,
	0x03A2, 0x033D, 0x02E3, 0x0293, 0x024B, 0x020B, 0x01D2, 0x019F, 0x0172,
	0x014A, 0x0126, 0x0106, 0x00E9, 0x00D0, 0x00B9, 0x00A5, 0x0093, 0x0083,
	0x0075, 0x0068, 0x005D, 0x0052, 0x0049, 0x0041, 0x003A, 0x0034, 0x002E,
	0x0029, 0x0025, 0x0021, 0x001D, 0x001A, 0x0017, 0x0014, 0x0012, 0x0010,
	0x000E, 0x000D, 0x000B, 0x000A, 0x0009, 0x0008, 0x0007, 0x0006, 0x0005,
	0x0005, 0x0004, 0x0004, 0x0003, 0x0003, 0x0002, 0x0002, 0x0002, 0x0002,
	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0000,
	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000};

/* regulator supplies for sgtl5000, VDDD is an optional external supply */
enum sgtl5000_regulator_supplies {
	VDDA,
	VDDIO,
	VDDD,
	SGTL5000_SUPPLY_NUM
};

/* vddd is optional supply */
static const char *supply_names[SGTL5000_SUPPLY_NUM] = {
	"VDDA",
	"VDDIO",
	"VDDD"
};

#define LDO_VOLTAGE		1200000
#define LINREG_VDDD	((1600 - LDO_VOLTAGE / 1000) / 50)

enum sgtl5000_micbias_resistor {
	SGTL5000_MICBIAS_OFF = 0,
	SGTL5000_MICBIAS_2K = 2,
	SGTL5000_MICBIAS_4K = 4,
	SGTL5000_MICBIAS_8K = 8,
};

enum  {
	I2S_LRCLK_STRENGTH_DISABLE,
	I2S_LRCLK_STRENGTH_LOW,
	I2S_LRCLK_STRENGTH_MEDIUM,
	I2S_LRCLK_STRENGTH_HIGH,
};

enum  {
	I2S_SCLK_STRENGTH_DISABLE,
	I2S_SCLK_STRENGTH_LOW,
	I2S_SCLK_STRENGTH_MEDIUM,
	I2S_SCLK_STRENGTH_HIGH,
};

enum {
	HP_POWER_EVENT,
	DAC_POWER_EVENT,
	ADC_POWER_EVENT,
	LAST_POWER_EVENT = ADC_POWER_EVENT
};

/* sgtl5000 private structure in codec */
struct sgtl5000_priv {
	int sysclk;	/* sysclk rate */
	int master;	/* i2s master or not */
	int fmt;	/* i2s data format */
	struct regulator_bulk_data supplies[SGTL5000_SUPPLY_NUM];
	int num_supplies;
	struct regmap *regmap;
	struct clk *mclk;
	int revision;
	u8 micbias_resistor;
	u8 micbias_voltage;
	u8 lrclk_strength;
	u8 sclk_strength;
	u16 mute_state[LAST_POWER_EVENT + 1];
};

static inline int hp_sel_input(struct snd_soc_component *component)
{
	return (snd_soc_component_read(component, SGTL5000_CHIP_ANA_CTRL) &
		SGTL5000_HP_SEL_MASK) >> SGTL5000_HP_SEL_SHIFT;
}

static inline u16 mute_output(struct snd_soc_component *component,
			      u16 mute_mask)
{
	u16 mute_reg = snd_soc_component_read(component,
					      SGTL5000_CHIP_ANA_CTRL);

	snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_CTRL,
			    mute_mask, mute_mask);
	return mute_reg;
}

static inline void restore_output(struct snd_soc_component *component,
				  u16 mute_mask, u16 mute_reg)
{
	snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_CTRL,
		mute_mask, mute_reg);
}

static void vag_power_on(struct snd_soc_component *component, u32 source)
{
	if (snd_soc_component_read(component, SGTL5000_CHIP_ANA_POWER) &
	    SGTL5000_VAG_POWERUP)
		return;

	snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_POWER,
			    SGTL5000_VAG_POWERUP, SGTL5000_VAG_POWERUP);

	/* When VAG powering on to get local loop from Line-In, the sleep
	 * is required to avoid loud pop.
	 */
	if (hp_sel_input(component) == SGTL5000_HP_SEL_LINE_IN &&
	    source == HP_POWER_EVENT)
		msleep(SGTL5000_VAG_POWERUP_DELAY);
}

static int vag_power_consumers(struct snd_soc_component *component,
			       u16 ana_pwr_reg, u32 source)
{
	int consumers = 0;

	/* count dac/adc consumers unconditional */
	if (ana_pwr_reg & SGTL5000_DAC_POWERUP)
		consumers++;
	if (ana_pwr_reg & SGTL5000_ADC_POWERUP)
		consumers++;

	/*
	 * If the event comes from HP and Line-In is selected,
	 * current action is 'DAC to be powered down'.
	 * As HP_POWERUP is not set when HP muxed to line-in,
	 * we need to keep VAG power ON.
	 */
	if (source == HP_POWER_EVENT) {
		if (hp_sel_input(component) == SGTL5000_HP_SEL_LINE_IN)
			consumers++;
	} else {
		if (ana_pwr_reg & SGTL5000_HP_POWERUP)
			consumers++;
	}

	return consumers;
}

static void vag_power_off(struct snd_soc_component *component, u32 source)
{
	u16 ana_pwr = snd_soc_component_read(component,
					     SGTL5000_CHIP_ANA_POWER);

	if (!(ana_pwr & SGTL5000_VAG_POWERUP))
		return;

	/*
	 * This function calls when any of VAG power consumers is disappearing.
	 * Thus, if there is more than one consumer at the moment, as minimum
	 * one consumer will definitely stay after the end of the current
	 * event.
	 * Don't clear VAG_POWERUP if 2 or more consumers of VAG present:
	 * - LINE_IN (for HP events) / HP (for DAC/ADC events)
	 * - DAC
	 * - ADC
	 * (the current consumer is disappearing right now)
	 */
	if (vag_power_consumers(component, ana_pwr, source) >= 2)
		return;

	snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_POWER,
		SGTL5000_VAG_POWERUP, 0);
	/* In power down case, we need wait 400-1000 ms
	 * when VAG fully ramped down.
	 * As longer we wait, as smaller pop we've got.
	 */
	msleep(SGTL5000_VAG_POWERDOWN_DELAY);
}

/*
 * mic_bias power on/off share the same register bits with
 * output impedance of mic bias, when power on mic bias, we
 * need reclaim it to impedance value.
 * 0x0 = Powered off
 * 0x1 = 2Kohm
 * 0x2 = 4Kohm
 * 0x3 = 8Kohm
 */
static int mic_bias_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);
	struct sgtl5000_priv *sgtl5000 = snd_soc_component_get_drvdata(component);

	switch (event) {
	case SND_SOC_DAPM_POST_PMU:
		/* change mic bias resistor */
		snd_soc_component_update_bits(component, SGTL5000_CHIP_MIC_CTRL,
			SGTL5000_BIAS_R_MASK,
			sgtl5000->micbias_resistor << SGTL5000_BIAS_R_SHIFT);
		break;

	case SND_SOC_DAPM_PRE_PMD:
		snd_soc_component_update_bits(component, SGTL5000_CHIP_MIC_CTRL,
				SGTL5000_BIAS_R_MASK, 0);
		break;
	}
	return 0;
}