<commit_before>/*
 * wm8996.c - WM8996 audio codec interface
 *
 * Copyright 2011 Wolfson Microelectronics PLC.
 * Author: <NAME> <<EMAIL>>
 *
 *  This program is free software; you can redistribute  it and/or modify it
 *  under  the terms of  the GNU General  Public License as published by the
 *  Free Software Foundation;  either version 2 of the  License, or (at your
 *  option) any later version.
 */

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/gcd.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <sound/core.h>
#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include <trace/events/asoc.h>

#include <sound/wm8996.h>
#include "wm8996.h"

#define WM8996_AIFS 2

#define HPOUT1L 1
#define HPOUT1R 2
#define HPOUT2L 4
#define HPOUT2R 8

#define WM8996_NUM_SUPPLIES 4
static const char *wm8996_supply_names[WM8996_NUM_SUPPLIES] = {
	"DBVDD",
	"AVDD1",
	"AVDD2",
	"CPVDD",
};

struct wm8996_priv {
	struct snd_soc_codec *codec;

	int ldo1ena;

	int sysclk;
	int sysclk_src;

	int fll_src;
	int fll_fref;
	int fll_fout;

	struct completion fll_lock;

	u16 dcs_pending;
	struct completion dcs_done;

	u16 hpout_ena;
	u16 hpout_pending;

	struct regulator_bulk_data supplies[WM8996_NUM_SUPPLIES];
	struct notifier_block disable_nb[WM8996_NUM_SUPPLIES];

	struct wm8996_pdata pdata;

	int rx_rate[WM8996_AIFS];
	int bclk_rate[WM8996_AIFS];

	/* Platform dependant ReTune mobile configuration */
	int num_retune_mobile_texts;
	const char **retune_mobile_texts;
	int retune_mobile_cfg[2];
	struct soc_enum retune_mobile_enum;

	struct snd_soc_jack *jack;
	bool detecting;
	bool jack_mic;
	wm8996_polarity_fn polarity_cb;

#ifdef CONFIG_GPIOLIB
	struct gpio_chip gpio_chip;
#endif
};

/* We can't use the same notifier block for more than one supply and
 * there's no way I can see to get from a callback to the caller
 * except container_of().
 */
#define WM8996_REGULATOR_EVENT(n) \
static int wm8996_regulator_event_##n(struct notifier_block *nb, \
				    unsigned long event, void *data)	\
{ \
	struct wm8996_priv *wm8996 = container_of(nb, struct wm8996_priv, \
						  disable_nb[n]); \
	if (event & REGULATOR_EVENT_DISABLE) { \
		wm8996->codec->cache_sync = 1; \
	} \
	return 0; \
}

WM8996_REGULATOR_EVENT(0)
WM8996_REGULATOR_EVENT(1)
WM8996_REGULATOR_EVENT(2)
WM8996_REGULATOR_EVENT(3)

static const u16 wm8996_reg[WM8996_MAX_REGISTER] = {
	[WM8996_SOFTWARE_RESET] = 0x8996,
	[WM8996_POWER_MANAGEMENT_7] = 0x10,
	[WM8996_DAC1_HPOUT1_VOLUME] = 0x88,
	[WM8996_DAC2_HPOUT2_VOLUME] = 0x88,
	[WM8996_DAC1_LEFT_VOLUME] = 0x2c0,
	[WM8996_DAC1_RIGHT_VOLUME] = 0x2c0,
	[WM8996_DAC2_LEFT_VOLUME] = 0x2c0,
	[WM8996_DAC2_RIGHT_VOLUME] = 0x2c0,
	[WM8996_OUTPUT1_LEFT_VOLUME] = 0x80,
	[WM8996_OUTPUT1_RIGHT_VOLUME] = 0x80,
	[WM8996_OUTPUT2_LEFT_VOLUME] = 0x80,
	[WM8996_OUTPUT2_RIGHT_VOLUME] = 0x80,
	[WM8996_MICBIAS_1] = 0x39,
	[WM8996_MICBIAS_2] = 0x39,
	[WM8996_LDO_1] = 0x3,
	[WM8996_LDO_2] = 0x13,
	[WM8996_ACCESSORY_DETECT_MODE_1] = 0x4,
	[WM8996_HEADPHONE_DETECT_1] = 0x20,
	[WM8996_MIC_DETECT_1] = 0x7600,
	[WM8996_MIC_DETECT_2] = 0xbf,
	[WM8996_CHARGE_PUMP_1] = 0x1f25,
	[WM8996_CHARGE_PUMP_2] = 0xab19,
	[WM8996_DC_SERVO_5] = 0x2a2a,
	[WM8996_CONTROL_INTERFACE_1] = 0x8004,
	[WM8996_CLOCKING_1] = 0x10,
	[WM8996_AIF_RATE] = 0x83,
	[WM8996_FLL_CONTROL_4] = 0x5dc0,
	[WM8996_FLL_CONTROL_5] = 0xc84,
	[WM8996_FLL_EFS_2] = 0x2,
	[WM8996_AIF1_TX_LRCLK_1] = 0x80,
	[WM8996_AIF1_TX_LRCLK_2] = 0x8,
	[WM8996_AIF1_RX_LRCLK_1] = 0x80,
	[WM8996_AIF1TX_DATA_CONFIGURATION_1] = 0x1818,
	[WM8996_AIF1RX_DATA_CONFIGURATION] = 0x1818,
	[WM8996_AIF1TX_TEST] = 0x7,
	[WM8996_AIF2_TX_LRCLK_1] = 0x80,
	[WM8996_AIF2_TX_LRCLK_2] = 0x8,
	[WM8996_AIF2_RX_LRCLK_1] = 0x80,
	[WM8996_AIF2TX_DATA_CONFIGURATION_1] = 0x1818,
	[WM8996_AIF2RX_DATA_CONFIGURATION] = 0x1818,
	[WM8996_AIF2TX_TEST] = 0x1,
	[WM8996_DSP1_TX_LEFT_VOLUME] = 0xc0,
	[WM8996_DSP1_TX_RIGHT_VOLUME] = 0xc0,
	[WM8996_DSP1_RX_LEFT_VOLUME] = 0xc0,
	[WM8996_DSP1_RX_RIGHT_VOLUME] = 0xc0,
	[WM8996_DSP1_TX_FILTERS] = 0x2000,
	[WM8996_DSP1_RX_FILTERS_1] = 0x200,
	[WM8996_DSP1_RX_FILTERS_2] = 0x10,
	[WM8996_DSP1_DRC_1] = 0x98,
	[WM8996_DSP1_DRC_2] = 0x845,
	[WM8996_DSP1_RX_EQ_GAINS_1] = 0x6318,
	[WM8996_DSP1_RX_EQ_GAINS_2] = 0x6300,
	[WM8996_DSP1_RX_EQ_BAND_1_A] = 0xfca,
	[WM8996_DSP1_RX_EQ_BAND_1_B] = 0x400,
	[WM8996_DSP1_RX_EQ_BAND_1_PG] = 0xd8,
	[WM8996_DSP1_RX_EQ_BAND_2_A] = 0x1eb5,
	[WM8996_DSP1_RX_EQ_BAND_2_B] = 0xf145,
	[WM8996_DSP1_RX_EQ_BAND_2_C] = 0xb75,
	[WM8996_DSP1_RX_EQ_BAND_2_PG] = 0x1c5,
	[WM8996_DSP1_RX_EQ_BAND_3_A] = 0x1c58,
	[WM8996_DSP1_RX_EQ_BAND_3_B] = 0xf373,
	[WM8996_DSP1_RX_EQ_BAND_3_C] = 0xa54,
	[WM8996_DSP1_RX_EQ_BAND_3_PG] = 0x558,
	[WM8996_DSP1_RX_EQ_BAND_4_A] = 0x168e,
	[WM8996_DSP1_RX_EQ_BAND_4_B] = 0xf829,
	[WM8996_DSP1_RX_EQ_BAND_4_C] = 0x7ad,
	[WM8996_DSP1_RX_EQ_BAND_4_PG] = 0x1103,
	[WM8996_DSP1_RX_EQ_BAND_5_A] = 0x564,
	[WM8996_DSP1_RX_EQ_BAND_5_B] = 0x559,
	[WM8996_DSP1_RX_EQ_BAND_5_PG] = 0x4000,
	[WM8996_DSP2_TX_LEFT_VOLUME] = 0xc0,
	[WM8996_DSP2_TX_RIGHT_VOLUME] = 0xc0,
	[WM8996_DSP2_RX_LEFT_VOLUME] = 0xc0,
	[WM8996_DSP2_RX_RIGHT_VOLUME] = 0xc0,
	[WM8996_DSP2_TX_FILTERS] = 0x2000,
	[WM8996_DSP2_RX_FILTERS_1] = 0x200,
	[WM8996_DSP2_RX_FILTERS_2] = 0x10,
	[WM8996_DSP2_DRC_1] = 0x98,
	[WM8996_DSP2_DRC_2] = 0x845,
	[WM8996_DSP2_RX_EQ_GAINS_1] = 0x6318,
	[WM8996_DSP2_RX_EQ_GAINS_2] = 0x6300,
	[WM8996_DSP2_RX_EQ_BAND_1_A] = 0xfca,
	[WM8996_DSP2_RX_EQ_BAND_1_B] = 0x400,
	[WM8996_DSP2_RX_EQ_BAND_1_PG] = 0xd8,
	[WM8996_DSP2_RX_EQ_BAND_2_A] = 0x1eb5,
	[WM8996_DSP2_RX_EQ_BAND_2_B] = 0xf145,
	[WM8996_DSP2_RX_EQ_BAND_2_C] = 0xb75,
	[WM8996_DSP2_RX_EQ_BAND_2_PG] = 0x1c5,
	[WM8996_DSP2_RX_EQ_BAND_3_A] = 0x1c58,
	[WM8996_DSP2_RX_EQ_BAND_3_B] = 0xf373,
	[WM8996_DSP2_RX_EQ_BAND_3_C] = 0xa54,
	[WM8996_DSP2_RX_EQ_BAND_3_PG] = 0x558,
	[WM8996_DSP2_RX_EQ_BAND_4_A] = 0x168e,
	[WM8996_DSP2_RX_EQ_BAND_4_B] = 0xf829,
	[WM8996_DSP2_RX_EQ_BAND_4_C] = 0x7ad,
	[WM8996_DSP2_RX_EQ_BAND_4_PG] = 0x1103,
	[WM8996_DSP2_RX_EQ_BAND_5_A] = 0x564,
	[WM8996_DSP2_RX_EQ_BAND_5_B] = 0x559,
	[WM8996_DSP2_RX_EQ_BAND_5_PG] = 0x4000,
	[WM8996_OVERSAMPLING] = 0xd,
	[WM8996_SIDETONE] = 0x1040,
	[WM8996_GPIO_1] = 0xa101,
	[WM8996_GPIO_2] = 0xa101,
	[WM8996_GPIO_3] = 0xa101,
	[WM8996_GPIO_4] = 0xa101,
	[WM8996_GPIO_5] = 0xa101,
	[WM8996_PULL_CONTROL_2] = 0x140,
	[WM8996_INTERRUPT_STATUS_1_MASK] = 0x1f,
	[WM8996_INTERRUPT_STATUS_2_MASK] = 0x1ecf,
	[WM8996_RIGHT_PDM_SPEAKER] = 0x1,
	[WM8996_PDM_SPEAKER_MUTE_SEQUENCE] = 0x69,
	[WM8996_PDM_SPEAKER_VOLUME] = 0x66,
	[WM8996_WRITE_SEQUENCER_0] = 0x1,
	[WM8996_WRITE_SEQUENCER_1] = 0x1,
	[WM8996_WRITE_SEQUENCER_3] = 0x6,
	[WM8996_WRITE_SEQUENCER_4] = 0x40,
	[WM8996_WRITE_SEQUENCER_5] = 0x1,
	[WM8996_WRITE_SEQUENCER_6] = 0xf,
	[WM8996_WRITE_SEQUENCER_7] = 0x6,
	[WM8996_WRITE_SEQUENCER_8] = 0x1,
	[WM8996_WRITE_SEQUENCER_9] = 0x3,
	[WM8996_WRITE_SEQUENCER_10] = 0x104,
	[WM8996_WRITE_SEQUENCER_12] = 0x60,
	[WM8996_WRITE_SEQUENCER_13] = 0x11,
	[WM8996_WRITE_SEQUENCER_14] = 0x401,
	[WM8996_WRITE_SEQUENCER_16] = 0x50,
	[WM8996_WRITE_SEQUENCER_17] = 0x3,
	[WM8996_WRITE_SEQUENCER_18] = 0x100,
	[WM8996_WRITE_SEQUENCER_20] = 0x51,
	[WM8996_WRITE_SEQUENCER_21] = 0x3,
	[WM8996_WRITE_SEQUENCER_22] = 0x104,
	[WM8996_WRITE_SEQUENCER_23] = 0xa,
	[WM8996_WRITE_SEQUENCER_24] = 0x60,
	[WM8996_WRITE_SEQUENCER_25] = 0x3b,
	[WM8996_WRITE_SEQUENCER_26] = 0x502,
	[WM8996_WRITE_SEQUENCER_27] = 0x100,
	[WM8996_WRITE_SEQUENCER_28] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_32] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_36] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_40] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_44] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_48] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_52] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_56] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_60] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_64] = 0x1,
	[WM8996_WRITE_SEQUENCER_65] = 0x1,
	[WM8996_WRITE_SEQUENCER_67] = 0x6,
	[WM8996_WRITE_SEQUENCER_68] = 0x40,
	[WM8996_WRITE_SEQUENCER_69] = 0x1,
	[WM8996_WRITE_SEQUENCER_70] = 0xf,
	[WM8996_WRITE_SEQUENCER_71] = 0x6,
	[WM8996_WRITE_SEQUENCER_72] = 0x1,
	[WM8996_WRITE_SEQUENCER_73] = 0x3,
	[WM8996_WRITE_SEQUENCER_74] = 0x104,
	[WM8996_WRITE_SEQUENCER_76] = 0x60,
	[WM8996_WRITE_SEQUENCER_77] = 0x11,
	[WM8996_WRITE_SEQUENCER_78] = 0x401,
	[WM8996_WRITE_SEQUENCER_80] = 0x50,
	[WM8996_WRITE_SEQUENCER_81] = 0x3,
	[WM8996_WRITE_SEQUENCER_82] = 0x100,
	[WM8996_WRITE_SEQUENCER_84] = 0x60,
	[WM8996_WRITE_SEQUENCER_85] = 0x3b,
	[WM8996_WRITE_SEQUENCER_86] = 0x502,
	[WM8996_WRITE_SEQUENCER_87] = 0x100,
	[WM8996_WRITE_SEQUENCER_88] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_92] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_96] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_100] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_104] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_108] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_112] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_116] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_120] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_124] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_128] = 0x1,
	[WM8996_WRITE_SEQUENCER_129] = 0x1,
	[WM8996_WRITE_SEQUENCER_131] = 0x6,
	[WM8996_WRITE_SEQUENCER_132] = 0x40,
	[WM8996_WRITE_SEQUENCER_133] = 0x1,
	[WM8996_WRITE_SEQUENCER_134] = 0xf,
	[WM8996_WRITE_SEQUENCER_135] = 0x6,
	[WM8996_WRITE_SEQUENCER_136] = 0x1,
	[WM8996_WRITE_SEQUENCER_137] = 0x3,
	[WM8996_WRITE_SEQUENCER_138] = 0x106,
	[WM8996_WRITE_SEQUENCER_140] = 0x61,
	[WM8996_WRITE_SEQUENCER_141] = 0x11,
	[WM8996_WRITE_SEQUENCER_142] = 0x401,
	[WM8996_WRITE_SEQUENCER_144] = 0x50,
	[WM8996_WRITE_SEQUENCER_145] = 0x3,
	[WM8996_WRITE_SEQUENCER_146] = 0x102,
	[WM8996_WRITE_SEQUENCER_148] = 0x51,
	[WM8996_WRITE_SEQUENCER_149] = 0x3,
	[WM8996_WRITE_SEQUENCER_150] = 0x106,
	[WM8996_WRITE_SEQUENCER_151] = 0xa,
	[WM8996_WRITE_SEQUENCER_152] = 0x61,
	[WM8996_WRITE_SEQUENCER_153] = 0x3b,
	[WM8996_WRITE_SEQUENCER_154] = 0x502,
	[WM8996_WRITE_SEQUENCER_155] = 0x100,
	[WM8996_WRITE_SEQUENCER_156] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_160] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_164] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_168] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_172] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_176] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_180] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_184] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_188] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_192] = 0x1,
	[WM8996_WRITE_SEQUENCER_193] = 0x1,
	[WM8996_WRITE_SEQUENCER_195] = 0x6,
	[WM8996_WRITE_SEQUENCER_196] = 0x40,
	[WM8996_WRITE_SEQUENCER_197] = 0x1,
	[WM8996_WRITE_SEQUENCER_198] = 0xf,
	[WM8996_WRITE_SEQUENCER_199] = 0x6,
	[WM8996_WRITE_SEQUENCER_200] = 0x1,
	[WM8996_WRITE_SEQUENCER_201] = 0x3,
	[WM8996_WRITE_SEQUENCER_202] = 0x106,
	[WM8996_WRITE_SEQUENCER_204] = 0x61,
	[WM8996_WRITE_SEQUENCER_205] = 0x11,
	[WM8996_WRITE_SEQUENCER_206] = 0x401,
	[WM8996_WRITE_SEQUENCER_208] = 0x50,
	[WM8996_WRITE_SEQUENCER_209] = 0x3,
	[WM8996_WRITE_SEQUENCER_210] = 0x102,
	[WM8996_WRITE_SEQUENCER_212] = 0x61,
	[WM8996_WRITE_SEQUENCER_213] = 0x3b,
	[WM8996_WRITE_SEQUENCER_214] = 0x502,
	[WM8996_WRITE_SEQUENCER_215] = 0x100,
	[WM8996_WRITE_SEQUENCER_216] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_220] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_224] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_228] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_232] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_236] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_240] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_244] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_248] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_252] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_256] = 0x60,
	[WM8996_WRITE_SEQUENCER_258] = 0x601,
	[WM8996_WRITE_SEQUENCER_260] = 0x50,
	[WM8996_WRITE_SEQUENCER_262] = 0x100,
	[WM8996_WRITE_SEQUENCER_264] = 0x1,
	[WM8996_WRITE_SEQUENCER_266] = 0x104,
	[WM8996_WRITE_SEQUENCER_267] = 0x100,
	[WM8996_WRITE_SEQUENCER_268] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_272] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_276] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_280] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_284] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_288] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_292] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_296] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_300] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_304] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_308] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_312] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_316] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_320] = 0x61,
	[WM8996_WRITE_SEQUENCER_322] = 0x601,
	[WM8996_WRITE_SEQUENCER_324] = 0x50,
	[WM8996_WRITE_SEQUENCER_326] = 0x102,
	[WM8996_WRITE_SEQUENCER_328] = 0x1,
	[WM8996_WRITE_SEQUENCER_330] = 0x106,
	[WM8996_WRITE_SEQUENCER_331] = 0x100,
	[WM8996_WRITE_SEQUENCER_332] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_336] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_340] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_344] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_348] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_352] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_356] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_360] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_364] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_368] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_372] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_376] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_380] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_384] = 0x60,
	[WM8996_WRITE_SEQUENCER_386] = 0x601,
	[WM8996_WRITE_SEQUENCER_388] = 0x61,
	[WM8996_WRITE_SEQUENCER_390] = 0x601,
	[WM8996_WRITE_SEQUENCER_392] = 0x50,
	[WM8996_WRITE_SEQUENCER_394] = 0x300,
	[WM8996_WRITE_SEQUENCER_396] = 0x1,
	[WM8996_WRITE_SEQUENCER_398] = 0x304,
	[WM8996_WRITE_SEQUENCER_400] = 0x40,
	[WM8996_WRITE_SEQUENCER_402] = 0xf,
	[WM8996_WRITE_SEQUENCER_404] = 0x1,
	[WM8996_WRITE_SEQUENCER_407] = 0x100,
};

static const DECLARE_TLV_DB_SCALE(inpga_tlv, 0, 100, 0);
static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -3600, 150, 0);
static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1);
static const DECLARE_TLV_DB_SCALE(out_digital_tlv, -1200, 150, 0);
static const DECLARE_TLV_DB_SCALE(out_tlv, -900, 75, 0);
static const DECLARE_TLV_DB_SCALE(spk_tlv, -900, 150, 0);
static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);

static const char *sidetone_hpf_text[] = {
	"2.9kHz", "1.5kHz", "735Hz", "403Hz", "196Hz", "98Hz", "49Hz"
};

static const struct soc_enum sidetone_hpf =
	SOC_ENUM_SINGLE(WM8996_SIDETONE, 7, 6, sidetone_hpf_text);

static const char *hpf_mode_text[] = {
	"HiFi", "Custom", "Voice"
};

static const struct soc_enum dsp1tx_hpf_mode =
	SOC_ENUM_SINGLE(WM8996_DSP1_TX_FILTERS, 3, 3, hpf_mode_text);

static const struct soc_enum dsp2tx_hpf_mode =
	SOC_ENUM_SINGLE(WM8996_DSP2_TX_FILTERS, 3, 3, hpf_mode_text);

static const char *hpf_cutoff_text[] = {
	"50Hz", "75Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz"
};

static const struct soc_enum dsp1tx_hpf_cutoff =
	SOC_ENUM_SINGLE(WM8996_DSP1_TX_FILTERS, 0, 7, hpf_cutoff_text);

static const struct soc_enum dsp2tx_hpf_cutoff =
	SOC_ENUM_SINGLE(WM8996_DSP2_TX_FILTERS, 0, 7, hpf_cutoff_text);

static void wm8996_set_retune_mobile(struct snd_soc_codec *codec, int block)
{
	struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
	struct wm8996_pdata *pdata = &wm8996->pdata;
	int base, best, best_val, save, i, cfg, iface;

	if (!wm8996->num_retune_mobile_texts)
		return;

	switch (block) {
	case 0:
		base = WM8996_DSP1_RX_EQ_GAINS_1;
		if (snd_soc_read(codec, WM8996_POWER_MANAGEMENT_8) &
		    WM8996_DSP1RX_SRC)
			iface = 1;
		else
			iface = 0;
		break;
	case 1:
		base = WM8996_DSP1_RX_EQ_GAINS_2;
		if (snd_soc_read(codec, WM8996_POWER_MANAGEMENT_8) &
		    WM8996_DSP2RX_SRC)
			iface = 1;
		else
			iface = 0;
		break;
	default:
		return;
	}

	/* Find the version of the currently selected configuration
	 * with the nearest sample rate. */
	cfg = wm8996->retune_mobile_cfg[block];
	best = 0;
	best_val = INT_MAX;
	for (i = 0; i < pdata->num_retune_mobile_cfgs; i++) {
		if (strcmp(pdata->retune_mobile_cfgs[i].name,
			   wm8996->retune_mobile_texts[cfg]) == 0 &&
		    abs(pdata->retune_mobile_cfgs[i].rate
			- wm8996->rx_rate[iface]) < best_val) {
			best = i;
			best_val = abs(pdata->retune_mobile_cfgs[i].rate
				       - wm8996->rx_rate[iface]);
		}
	}

	dev_dbg(codec->dev, "ReTune Mobile %d %s/%dHz for %dHz sample rate\n",
		block,
		pdata->retune_mobile_cfgs[best].name,
		pdata->retune_mobile_cfgs[best].rate,
		wm8996->rx_rate[iface]);

	/* The EQ will be disabled while reconfiguring it, remember the
	 * current configuration. 
	 */
	save = snd_soc_read(codec, base);
	save &= WM8996_DSP1RX_EQ_ENA;

	for (i = 0; i < ARRAY_SIZE(pdata->retune_mobile_cfgs[best].regs); i++)
		snd_soc_update_bits(codec, base + i, 0xffff,
				    pdata->retune_mobile_cfgs[best].regs[i]);

	snd_soc_update_bits(codec, base, WM8996_DSP1RX_EQ_ENA, save);
}

/* Icky as hell but saves code duplication */
static int wm8996_get_retune_mobile_block(const char *name)
{
	if (strcmp(name, "DSP1 EQ Mode") == 0)
		return 0;
	if (strcmp(name, "DSP2 EQ Mode") == 0)
		return 1;
	return -EINVAL;
}

static int wm8996_put_retune_mobile_enum(struct snd_kcontrol *kcontrol,
					 struct snd_ctl_elem_value *ucontrol)
{
	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
	struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
	struct wm8996_pdata *pdata = &wm8996->pdata;
	int block = wm8996_get_retune_mobile_block(kcontrol->id.name);
	int value = ucontrol->value.integer.value[0];

	if (block < 0)
		return block;

	if (value >= pdata->num_retune_mobile_cfgs)
		return -EINVAL;

	wm8996->retune_mobile_cfg[block] = value;

	wm8996_set_retune_mobile(codec, block);

	return 0;
}

static int wm8996_get_retune_mobile_enum(struct snd_kcontrol *kcontrol,
					 struct snd_ctl_elem_value *ucontrol)
{
	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
	struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
	int block = wm8996_get_retune_mobile_block(kcontrol->id.name);

	ucontrol->value.enumerated.item[0] = wm8996->retune_mobile_cfg[block];

	return 0;
}

static const struct snd_kcontrol_new wm8996_snd_controls[] = {
SOC_DOUBLE_R_TLV("Capture Volume", WM8996_LEFT_LINE_INPUT_VOLUME,
		 WM8996_RIGHT_LINE_INPUT_VOLUME, 0, 31, 0, inpga_tlv),
SOC_DOUBLE_R("Capture ZC Switch", WM8996_LEFT_LINE_INPUT_VOLUME,
	     WM8996_RIGHT_LINE_INPUT_VOLUME, 5, 1, 0),

SOC_DOUBLE_TLV("DAC1 Sidetone Volume", WM8996_DAC1_MIXER_VOLUMES,
	       0, 5, 24, 0, sidetone_tlv),
SOC_DOUBLE_TLV("DAC2 Sidetone Volume", WM8996_DAC2_MIXER_VOLUMES,
	       0, 5, 24, 0, sidetone_tlv),
SOC_SINGLE("Sidetone LPF Switch", WM8996_SIDETONE, 12, 1, 0),
SOC_ENUM("Sidetone HPF Cut-off", sidetone_hpf),
SOC_SINGLE("Sidetone HPF Switch", WM8996_SIDETONE, 6, 1, 0),

SOC_DOUBLE_R_TLV("DSP1 Capture Volume", WM8996_DSP1_TX_LEFT_VOLUME,
		 WM8996_DSP1_TX_RIGHT_VOLUME, 1, 96, 0, digital_tlv),
SOC_DOUBLE_R_TLV("DSP2 Capture Volume", WM8996_DSP2_TX_LEFT_VOLUME,
		 WM8996_DSP2_TX_RIGHT_VOLUME, 1, 96, 0, digital_tlv),

SOC_SINGLE("DSP1 Capture Notch Filter Switch", WM8996_DSP1_TX_FILTERS,
	   13, 1, 0),
SOC_DOUBLE("DSP1 Capture HPF Switch", WM8996_DSP1_TX_FILTERS, 12, 11, 1, 0),
SOC_ENUM("DSP1 Capture HPF Mode", dsp1tx_hpf_mode),
SOC_ENUM("DSP1 Capture HPF Cutoff", dsp1tx_hpf_cutoff),

SOC_SINGLE("DSP2 Capture Notch Filter Switch", WM8996_DSP2_TX_FILTERS,
	   13, 1, 0),
SOC_DOUBLE("DSP2 Capture HPF Switch", WM8996_DSP2_TX_FILTERS, 12, 11, 1, 0),
SOC_ENUM("DSP2 Capture HPF Mode", dsp2tx_hpf_mode),
SOC_ENUM("DSP2 Capture HPF Cutoff", dsp2tx_hpf_cutoff),

SOC_DOUBLE_R_TLV("DSP1 Playback Volume", WM8996_DSP1_RX_LEFT_VOLUME,
		 WM8996_DSP1_RX_RIGHT_VOLUME, 1, 112, 0, digital_tlv),
SOC_SINGLE("DSP1 Playback Switch", WM8996_DSP1_RX_FILTERS_1, 9, 1, 1),

SOC_DOUBLE_R_TLV("DSP2 Playback Volume", WM8996_DSP2_RX_LEFT_VOLUME,
		 WM8996_DSP2_RX_RIGHT_VOLUME, 1, 112, 0, digital_tlv),
SOC_SINGLE("DSP2 Playback Switch", WM8996_DSP2_RX_FILTERS_1, 9, 1, 1),

SOC_DOUBLE_R_TLV("DAC1 Volume", WM8996_DAC1_LEFT_VOLUME,
		 WM8996_DAC1_RIGHT_VOLUME, 1, 112, 0, digital_tlv),
SOC_DOUBLE_R("DAC1 Switch", WM8996_DAC1_LEFT_VOLUME,
	     WM8996_DAC1_RIGHT_VOLUME, 9, 1, 1),

SOC_DOUBLE_R_TLV("DAC2 Volume", WM8996_DAC2_LEFT_VOLUME,
		 WM8996_DAC2_RIGHT_VOLUME, 1, 112, 0, digital_tlv),
SOC_DOUBLE_R("DAC2 Switch", WM8996_DAC2_LEFT_VOLUME,
	     WM8996_DAC2_RIGHT_VOLUME, 9, 1, 1),

SOC_SINGLE("Speaker High Performance Switch", WM8996_OVERSAMPLING, 3, 1, 0),
SOC_SINGLE("DMIC High Performance Switch", WM8996_OVERSAMPLING, 2, 1, 0),
SOC_SINGLE("ADC High Performance Switch", WM8996_OVERSAMPLING, 1, 1, 0),
SOC_SINGLE("DAC High Performance Switch", WM8996_OVERSAMPLING, 0, 1, 0),

SOC_SINGLE("DAC Soft Mute Switch", WM8996_DAC_SOFTMUTE, 1, 1, 0),
SOC_SINGLE("DAC Slow Soft Mute Switch", WM8996_DAC_SOFTMUTE, 0, 1, 0),

SOC_DOUBLE_TLV("Digital Output 1 Volume", WM8996_DAC1_HPOUT1_VOLUME, 0, 4,
	       8, 0, out_digital_tlv),
SOC_DOUBLE_TLV("Digital Output 2 Volume", WM8996_DAC2_HPOUT2_VOLUME, 0, 4,
	       8, 0, out_digital_tlv),

SOC_DOUBLE_R_TLV("Output 1 Volume", WM8996_OUTPUT1_LEFT_VOLUME,
		 WM8996_OUTPUT1_RIGHT_VOLUME, 0, 12, 0, out_tlv),
SOC_DOUBLE_R("Output 1 ZC Switch",  WM8996_OUTPUT1_LEFT_VOLUME,
	     WM8996_OUTPUT1_RIGHT_VOLUME, 7, 1, 0),

SOC_DOUBLE_R_TLV("Output 2 Volume", WM8996_OUTPUT2_LEFT_VOLUME,
		 WM8996_OUTPUT2_RIGHT_VOLUME, 0, 12, 0, out_tlv),
SOC_DOUBLE_R("Output 2 ZC Switch",  WM8996_OUTPUT2_LEFT_VOLUME,
	     WM8996_OUTPUT2_RIGHT_VOLUME, 7, 1, 0),

SOC_DOUBLE_TLV("Speaker Volume", WM8996_PDM_SPEAKER_VOLUME, 0, 4, 8, 0,
	       spk_tlv),
SOC_DOUBLE_R("Speaker Switch", WM8996_LEFT_PDM_SPEAKER,
	     WM8996_RIGHT_PDM_SPEAKER, 3, 1, 1),
SOC_DOUBLE_R("Speaker ZC Switch", WM8996_LEFT_PDM_SPEAKER,
	     WM8996_RIGHT_PDM_SPEAKER, 2, 1, 0),

SOC_SINGLE("DSP1 EQ Switch", WM8996_DSP1_RX_EQ_GAINS_1, 0, 1, 0),
SOC_SINGLE("DSP2 EQ Switch", WM8996_DSP2_RX_EQ_GAINS_1, 0, 1, 0),
};

static const struct snd_kcontrol_new wm8996_eq_controls[] = {
SOC_SINGLE_TLV("DSP1 EQ B1 Volume", WM8996_DSP1_RX_EQ_GAINS_1, 11, 31, 0,
	       eq_tlv),
SOC_SINGLE_TLV("DSP1 EQ B2 Volume", WM8996_DSP1_RX_EQ_GAINS_1, 6, 31, 0,
	       eq_tlv),
SOC_SINGLE_TLV("DSP1 EQ B3 Volume", WM8996_DSP1_RX_EQ_GAINS_1, 1, 31, 0,
	       eq_tlv),
SOC_SINGLE_TLV("DSP1 EQ B4 Volume", WM8996_DSP1_RX_EQ_GAINS_2, 11, 31, 0,
	       eq_tlv),
SOC_SINGLE_TLV("DSP1 EQ B5 Volume", WM8996_DSP1_RX_EQ_GAINS_2, 6, 31, 0,
	       eq_tlv),

SOC_SINGLE_TLV("DSP2 EQ B1 Volume", WM8996_DSP2_RX_EQ_GAINS_1, 11, 31, 0,
	       eq_tlv),
SOC_SINGLE_TLV("DSP2 EQ B2 Volume", WM8996_DSP2_RX_EQ_GAINS_1, 6, 31, 0,
	       eq_tlv),
SOC_SINGLE_TLV("DSP2 EQ B3 Volume", WM8996_DSP2_RX_EQ_GAINS_1, 1, 31, 0,
	       eq_tlv),
SOC_SINGLE_TLV("DSP2 EQ B4 Volume", WM8996_DSP2_RX_EQ_GAINS_2, 11, 31, 0,
	       eq_tlv),
SOC_SINGLE_TLV("DSP2 EQ B5 Volume", WM8996_DSP2_RX_EQ_GAINS_2, 6, 31, 0,
	       eq_tlv),
};

static int cp_event(struct snd_soc_dapm_widget *w,
		    struct snd_kcontrol *kcontrol, int event)
{
	switch (event) {
	case SND_SOC_DAPM_POST_PMU:
		msleep(5);
		break;
	default:
		BUG();
		return -EINVAL;
	}

	return 0;
}

static int rmv_short_event(struct snd_soc_dapm_widget *w,
			   struct snd_kcontrol *kcontrol, int event)
{
	struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(w->codec);

	/* Record which outputs we enabled */
	switch (event) {
	case SND_SOC_DAPM_PRE_PMD:
		wm8996->hpout_pending &= ~w->shift;
		break;
	case SND_SOC_DAPM_PRE_PMU:
		wm8996->hpout_pending |= w->shift;
		break;
	default:
		BUG();
		return -EINVAL;
	}

	return 0;
}

static void wait_for_dc_servo(struct snd_soc_codec *codec, u16 mask)
{
	struct i2c_client *i2c = to_i2c_client(codec->dev);
	struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
	int i, ret;
	unsigned long timeout = 200;

	snd_soc_write(codec, WM8996_DC_SERVO_2, mask);

	/* Use the interrupt if possible */
	do {
		if (i2c->irq) {
			timeout = wait_for_completion_timeout(&wm8996->dcs_done,
							      msecs_to_jiffies(200));
			if (timeout == 0)
				dev_err(codec->dev, "DC servo timed out\n");

		} else {
			msleep(1);
			if (--i) {
				timeout = 0;
				break;
			}
		}

		ret = snd_soc_read(codec, WM8996_DC_SERVO_2);
		dev_dbg(codec->dev, "DC servo state: %x\n", ret);
	} while (ret & mask);

	if (timeout == 0)
		dev_err(codec->dev, "DC servo timed out for %x\n", mask);
	else
		dev_dbg(codec->dev, "DC servo complete for %x\n", mask);
}

static void wm8996_seq_notifier(struct snd_soc_dapm_context *dapm,
				enum snd_soc_dapm_type event, int subseq)
{
	struct snd_soc_codec *codec = container_of(dapm,
						   struct snd_soc_codec, dapm);
	struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
	u16 val, mask;

	/* Complete any pending DC servo starts */
	if (wm8996->dcs_pending) {
		dev_dbg(codec->dev, "Starting DC servo for %x\n",
			wm8996->dcs_pending);

		/* Trigger a startup sequence */
		wait_for_dc_servo(codec, wm8996->dcs_pending
				         << WM8996_DCS_TRIG_STARTUP_0_SHIFT);

		wm8996->dcs_pending = 0;
	}

	if (wm8996->hpout_pending != wm8996->hpout_ena) {
		dev_dbg(codec->dev, "Applying RMV_SHORTs %x->%x\n",
			wm8996->hpout_ena, wm8996->hpout_pending);

		val = 0;
		mask = 0;
		if (wm8996->hpout_pending & HPOUT1L) {
			val |= WM8996_HPOUT1L_RMV_SHORT;
			mask |= WM8996_HPOUT1L_RMV_SHORT;
		} else {
			mask |= WM8996_HPOUT1L_RMV_SHORT |
				WM8996_HPOUT1L_OUTP |
				WM8996_HPOUT1L_DLY;
		}

		if (wm8996->hpout_pending & HPOUT1R) {
			val |= WM8996_HPOUT1R_RMV_SHORT;
			mask |= WM8996_HPOUT1R_RMV_SHORT;
		} else {
			mask |= WM8996_HPOUT1R_RMV_SHORT |
				WM8996_HPOUT1R_OUTP |
				WM8996_HPOUT1R_DLY;
		}

		snd_soc_update_bits(codec, WM8996_ANALOGUE_HP_1, mask, val);

		val = 0;
		mask = 0;
		if (wm8996->hpout_pending & HPOUT2L) {
			val |= WM8996_HPOUT2L_RMV_SHORT;
			mask |= WM8996_HPOUT2L_RMV_SHORT;
		} else {
			mask |= WM8996_HPOUT2L_RMV_SHORT |
				WM8996_HPOUT2L_OUTP |
				WM8996_HPOUT2L_DLY;
		}

		if (wm8996->hpout_pending & HPOUT2R) {
			val |= WM8996_HPOUT2R_RMV_SHORT;
			mask |= WM8996_HPOUT2R_RMV_SHORT;
		} else {
			mask |= WM8996_HPOUT2R_RMV_SHORT |
				WM8996_HPOUT2R_OUTP |
				WM8996_HPOUT2R_DLY;
		}

		snd_soc_update_bits(codec, WM8996_ANALOGUE_HP_2, mask, val);

		wm8996->hpout_ena = wm8996->hpout_pending;
	}
}

static int dcs_start(struct snd_soc_dapm_widget *w,
		     struct snd_kcontrol *kcontrol, int event)
{
	struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(w->codec);

	switch (event) {
	case SND_SOC_DAPM_POST_PMU:
		wm8996->dcs_pending |= 1 << w->shift;
		break;
	default:
		BUG();
		return -EINVAL;
	}

	return 0;
}

static const char *sidetone_text[] = {
	"IN1", "IN2",
};

static const struct soc_enum left_sidetone_enum =
	SOC_ENUM_SINGLE(WM8996_SIDETONE, 0, 2, sidetone_text);

static const struct snd_kcontrol_new left_sidetone =
	SOC_DAPM_ENUM("Left Sidetone", left_sidetone_enum);

static const struct soc_enum right_sidetone_enum =
	SOC_ENUM_SINGLE(WM8996_SIDETONE, 1, 2, sidetone_text);

static const struct snd_kcontrol_new right_sidetone =
	SOC_DAPM_ENUM("Right Sidetone", right_sidetone_enum);

static const char *spk_text[] = {
	"DAC1L", "DAC1R", "DAC2L", "DAC2R"
};

static const struct soc_enum spkl_enum =
	SOC_ENUM_SINGLE(WM8996_LEFT_PDM_SPEAKER, 0, 4, spk_text);

static const struct snd_kcontrol_new spkl_mux =
	SOC_DAPM_ENUM("SPKL", spkl_enum);

static const struct soc_enum spkr_enum =
	SOC_ENUM_SINGLE(WM8996_RIGHT_PDM_SPEAKER, 0, 4, spk_text);

static const struct snd_kcontrol_new spkr_mux =
	SOC_DAPM_ENUM("SPKR", spkr_enum);

static const char *dsp1rx_text[] = {
	"AIF1", "AIF2"
};

static const struct soc_enum dsp1rx_enum =
	SOC_ENUM_SINGLE(WM8996_POWER_MANAGEMENT_8, 0, 2, dsp1rx_text);

static const struct snd_kcontrol_new dsp1rx =
	SOC_DAPM_ENUM("DSP1RX", dsp1rx_enum);

static const char *dsp2rx_text[] = {
	 "AIF2", "AIF1"
};

static const struct soc_enum dsp2rx_enum =
	SOC_ENUM_SINGLE(WM8996_POWER_MANAGEMENT_8, 4, 2, dsp2rx_text);

static const struct snd_kcontrol_new dsp2rx =
	SOC_DAPM_ENUM("DSP2RX", dsp2rx_enum);

static const char *aif2tx_text[] = {
	"DSP2", "DSP1", "AIF1"
};

static const struct soc_enum aif2tx_enum =
	SOC_ENUM_SINGLE(WM8996_POWER_MANAGEMENT_8, 6, 3, aif2tx_text);

static const struct snd_kcontrol_new aif2tx =
	SOC_DAPM_ENUM("AIF2TX", aif2tx_enum);

static const char *inmux_text[] = {
	"ADC", "DMIC1", "DMIC2"
};

static const struct soc_enum in1_enum =
	SOC_ENUM_SINGLE(WM8996_POWER_MANAGEMENT_7, 0, 3, inmux_text);

static const struct snd_kcontrol_new in1_mux =
	SOC_DAPM_ENUM("IN1 Mux", in1_enum);

static const struct soc_enum in2_enum =
	SOC_ENUM_SINGLE(WM8996_POWER_MANAGEMENT_7, 4, 3, inmux_text);

static const struct snd_kcontrol_new in2_mux =
	SOC_DAPM_ENUM("IN2 Mux", in2_enum);

static const struct snd_kcontrol_new dac2r_mix[] = {
SOC_DAPM_SINGLE("Right Sidetone Switch", WM8996_DAC2_RIGHT_MIXER_ROUTING,
		5, 1, 0),
SOC_DAPM_SINGLE("Left Sidetone Switch", WM8996_DAC2_RIGHT_MIXER_ROUTING,
		4, 1, 0),
SOC_DAPM_SINGLE("DSP2 Switch", WM8996_DAC2_RIGHT_MIXER_ROUTING, 1, 1, 0),
SOC_DAPM_SINGLE("DSP1 Switch", WM8996_DAC2_RIGHT_MIXER_ROUTING, 0, 1, 0),
};

static const struct snd_kcontrol_new dac2l_mix[] = {
SOC_DAPM_SINGLE("Right Sidetone Switch", WM8996_DAC2_LEFT_MIXER_ROUTING,
		5, 1, 0),
SOC_DAPM_SINGLE("Left Sidetone Switch", WM8996_DAC2_LEFT_MIXER_ROUTING,
		4, 1, 0),
SOC_DAPM_SINGLE("DSP2 Switch", WM8996_DAC2_LEFT_MIXER_ROUTING, 1, 1, 0),
SOC_DAPM_SINGLE("DSP1 Switch", WM8996_DAC2_LEFT_MIXER_ROUTING, 0, 1, 0),
};

static const struct snd_kcontrol_new dac1r_mix[] = {
SOC_DAPM_SINGLE("Right Sidetone Switch", WM8996_DAC1_RIGHT_MIXER_ROUTING,
		5, 1, 0),
SOC_DAPM_SINGLE("Left Sidetone Switch", WM8996_DAC1_RIGHT_MIXER_ROUTING,
		4, 1, 0),
SOC_DAPM_SINGLE("DSP2 Switch", WM8996_DAC1_RIGHT_MIXER_ROUTING, 1, 1, 0),
SOC_DAPM_SINGLE("DSP1 Switch", WM8996_DAC1_RIGHT_MIXER_ROUTING, 0, 1, 0),
};

static const struct snd_kcontrol_new dac1l_mix[] = {
SOC_DAPM_SINGLE("Right Sidetone Switch", WM8996_DAC1_LEFT_MIXER_ROUTING,
		5, 1, 0),
SOC_DAPM_SINGLE("Left Sidetone Switch", WM8996_DAC1_LEFT_MIXER_ROUTING,
		4, 1, 0),
SOC_DAPM_SINGLE("DSP2 Switch", WM8996_DAC1_LEFT_MIXER_ROUTING, 1, 1, 0),
SOC_DAPM_SINGLE("DSP1 Switch", WM8996_DAC1_LEFT_MIXER_ROUTING, 0, 1, 0),
};

static const struct snd_kcontrol_new dsp1txl[] = {
SOC_DAPM_SINGLE("IN1 Switch", WM8996_DSP1_TX_LEFT_MIXER_ROUTING,
		1, 1, 0),
SOC_DAPM_SINGLE("DAC Switch", WM8996_DSP1_TX_LEFT_MIXER_ROUTING,
		0, 1, 0),
};

static const struct snd_kcontrol_new dsp1txr[] = {
SOC_DAPM_SINGLE("IN1 Switch", WM8996_DSP1_TX_RIGHT_MIXER_ROUTING,
		1, 1, 0),
SOC_DAPM_SINGLE("DAC Switch", WM8996_DSP1_TX_RIGHT_MIXER_ROUTING,
		0, 1, 0),
};

static const struct snd_kcontrol_new dsp2txl[] = {
SOC_DAPM_SINGLE("IN1 Switch", WM8996_DSP2_TX_LEFT_MIXER_ROUTING,
		1, 1, 0),
SOC_DAPM_SINGLE("DAC Switch", WM8996_DSP2_TX_LEFT_MIXER_ROUTING,
		0, 1, 0),
};

static const struct snd_kcontrol_new dsp2txr[] = {
SOC_DAPM_SINGLE("IN1 Switch", WM8996_DSP2_TX_RIGHT_MIXER_ROUTING,
		1, 1, 0),
SOC_DAPM_SINGLE("DAC Switch", WM8996_DSP2_TX_RIGHT_MIXER_ROUTING,
		0, 1, 0),
};


static const struct snd_soc_dapm_widget wm8996_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("IN1LN"),
SND_SOC_DAPM_INPUT("IN1LP"),
SND_SOC_DAPM_INPUT("IN1RN"),
SND_SOC_DAPM_INPUT("IN1RP"),

SND_SOC_DAPM_INPUT("IN2LN"),
SND_SOC_DAPM_INPUT("IN2LP"),
SND_SOC_DAPM_INPUT("IN2RN"),
SND_SOC_DAPM_INPUT("IN2RP"),

SND_SOC_DAPM_INPUT("DMIC1DAT"),
SND_SOC_DAPM_INPUT("DMIC2DAT"),

SND_SOC_DAPM_SUPPLY_S("SYSCLK", 1, WM8996_AIF_CLOCKING_1, 0, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("SYSDSPCLK", 2, WM8996_CLOCKING_1, 1, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("AIFCLK", 2, WM8996_CLOCKING_1, 2, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("Charge Pump", 2, WM8996_CHARGE_PUMP_1, 15, 0, cp_event,
		      SND_SOC_DAPM_POST_PMU),

SND_SOC_DAPM_SUPPLY("LDO2", WM8996_POWER_MANAGEMENT_2, 1, 0, NULL, 0),
SND_SOC_DAPM_MICBIAS("MICB2", WM8996_POWER_MANAGEMENT_1, 9, 0),
SND_SOC_DAPM_MICBIAS("MICB1", WM8996_POWER_MANAGEMENT_1, 8, 0),

SND_SOC_DAPM_PGA("IN1L PGA", WM8996_POWER_MANAGEMENT_2, 5, 0, NULL, 0),
SND_SOC_DAPM_PGA("IN1R PGA", WM8996_POWER_MANAGEMENT_2, 4, 0, NULL, 0),

SND_SOC_DAPM_MUX("IN1L Mux", SND_SOC_NOPM, 0, 0, &in1_mux),
SND_SOC_DAPM_MUX("IN1R Mux", SND_SOC_NOPM, 0, 0, &in1_mux),
SND_SOC_DAPM_MUX("IN2L Mux", SND_SOC_NOPM, 0, 0, &in2_mux),
SND_SOC_DAPM_MUX("IN2R Mux", SND_SOC_NOPM, 0, 0, &in2_mux),

SND_SOC_DAPM_PGA("IN1L", WM8996_POWER_MANAGEMENT_7, 2, 0, NULL, 0),
SND_SOC_DAPM_PGA("IN1R", WM8996_POWER_MANAGEMENT_7, 3, 0, NULL, 0),
SND_SOC_DAPM_PGA("IN2L", WM8996_POWER_MANAGEMENT_7, 6, 0, NULL, 0),
SND_SOC_DAPM_PGA("IN2R", WM8996_POWER_MANAGEMENT_7, 7, 0, NULL, 0),

SND_SOC_DAPM_SUPPLY("DMIC2", WM8996_POWER_MANAGEMENT_7, 9, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("DMIC1", WM8996_POWER_MANAGEMENT_7, 8, 0, NULL, 0),

SND_SOC_DAPM_ADC("DMIC2L", NULL, WM8996_POWER_MANAGEMENT_3, 5, 0),
SND_SOC_DAPM_ADC("DMIC2R", NULL, WM8996_POWER_MANAGEMENT_3, 4, 0),
SND_SOC_DAPM_ADC("DMIC1L", NULL, WM8996_POWER_MANAGEMENT_3, 3, 0),
SND_SOC_DAPM_ADC("DMIC1R", NULL, WM8996_POWER_MANAGEMENT_3, 2, 0),

SND_SOC_DAPM_ADC("ADCL", NULL, WM8996_POWER_MANAGEMENT_3, 1, 0),
SND_SOC_DAPM_ADC("ADCR", NULL, WM8996_POWER_MANAGEMENT_3, 0, 0),

SND_SOC_DAPM_MUX("Left Sidetone", SND_SOC_NOPM, 0, 0, &left_sidetone),
SND_SOC_DAPM_MUX("Right Sidetone", SND_SOC_NOPM, 0, 0, &right_sidetone),

SND_SOC_DAPM_AIF_IN("DSP2RXL", NULL, 0, WM8996_POWER_MANAGEMENT_3, 11, 0),
SND_SOC_DAPM_AIF_IN("DSP2RXR", NULL, 1, WM8996_POWER_MANAGEMENT_3, 10, 0),
SND_SOC_DAPM_AIF_IN("DSP1RXL", NULL, 0, WM8996_POWER_MANAGEMENT_3, 9, 0),
SND_SOC_DAPM_AIF_IN("DSP1RXR", NULL, 1, WM8996_POWER_MANAGEMENT_3, 8, 0),

SND_SOC_DAPM_MIXER("DSP2TXL", WM8996_POWER_MANAGEMENT_5, 11, 0,
		   dsp2txl, ARRAY_SIZE(dsp2txl)),
SND_SOC_DAPM_MIXER("DSP2TXR", WM8996_POWER_MANAGEMENT_5, 10, 0,
		   dsp2txr, ARRAY_SIZE(dsp2txr)),
SND_SOC_DAPM_MIXER("DSP1TXL", WM8996_POWER_MANAGEMENT_5, 9, 0,
		   dsp1txl, ARRAY_SIZE(dsp1txl)),
SND_SOC_DAPM_MIXER("DSP1TXR", WM8996_POWER_MANAGEMENT_5, 8, 0,
		   dsp1txr, ARRAY_SIZE(dsp1txr)),

SND_SOC_DAPM_MIXER("DAC2L Mixer", SND_SOC_NOPM, 0, 0,
		   dac2l_mix, ARRAY_SIZE(dac2l_mix)),
SND_SOC_DAPM_MIXER("DAC2R Mixer", SND_SOC_NOPM, 0, 0,
		   dac2r_mix, ARRAY_SIZE(dac2r_mix)),
SND_SOC_DAPM_MIXER("DAC1L Mixer", SND_SOC_NOPM, 0, 0,
		   dac1l_mix, ARRAY_SIZE(dac1l_mix)),
SND_SOC_DAPM_MIXER("DAC1R Mixer", SND_SOC_NOPM, 0, 0,
		   dac1r_mix, ARRAY_SIZE(dac1r_mix)),

SND_SOC_DAPM_DAC("DAC2L", NULL, WM8996_POWER_MANAGEMENT_5, 3, 0),
SND_SOC_DAPM_DAC("DAC2R", NULL, WM8996_POWER_MANAGEMENT_5, 2, 0),
SND_SOC_DAPM_DAC("DAC1L", NULL, WM8996_POWER_MANAGEMENT_5, 1, 0),
SND_SOC_DAPM_DAC("DAC1R", NULL, WM8996_POWER_MANAGEMENT_5, 0, 0),

SND_SOC_DAPM_AIF_IN("AIF2RX1", "AIF2 Playback", 1,
		    WM8996_POWER_MANAGEMENT_4, 9, 0),
SND_SOC_DAPM_AIF_IN("AIF2RX0", "AIF2 Playback", 2,
		    WM8996_POWER_MANAGEMENT_4, 8, 0),

SND_SOC_DAPM_AIF_IN("AIF2TX1", "AIF2 Capture", 1,
		    WM8996_POWER_MANAGEMENT_6, 9, 0),
SND_SOC_DAPM_AIF_IN("AIF2TX0", "AIF2 Capture", 2,
		    WM8996_POWER_MANAGEMENT_6, 8, 0),

SND_SOC_DAPM_AIF_IN("AIF1RX5", "AIF1 Playback", 5,
		    WM8996_POWER_MANAGEMENT_4, 5, 0),
SND_SOC_DAPM_AIF_IN("AIF1RX4", "AIF1 Playback", 4,
		    WM8996_POWER_MANAGEMENT_4, 4, 0),
SND_SOC_DAPM_AIF_IN("AIF1RX3", "AIF1 Playback", 3,
		    WM8996_POWER_MANAGEMENT_4, 3, 0),
SND_SOC_DAPM_AIF_IN("AIF1RX2", "AIF1 Playback", 2,
		    WM8996_POWER_MANAGEMENT_4, 2, 0),
SND_SOC_DAPM_AIF_IN("AIF1RX1", "AIF1 Playback", 1,
		    WM8996_POWER_MANAGEMENT_4, 1, 0),
SND_SOC_DAPM_AIF_IN("AIF1RX0", "AIF1 Playback", 0,
		    WM8996_POWER_MANAGEMENT_4, 0, 0),

SND_SOC_DAPM_AIF_OUT("AIF1TX5", "AIF1 Capture", 5,
		     WM8996_POWER_MANAGEMENT_6, 5, 0),
SND_SOC_DAPM_AIF_OUT("AIF1TX4", "AIF1 Capture", 4,
		     WM8996_POWER_MANAGEMENT_6, 4, 0),
SND_SOC_DAPM_AIF_OUT("AIF1TX3", "AIF1 Capture", 3,
		     WM8996_POWER_MANAGEMENT_6, 3, 0),
SND_SOC_DAPM_AIF_OUT("AIF1TX2", "AIF1 Capture", 2,
		     WM8996_POWER_MANAGEMENT_6, 2, 0),
SND_SOC_DAPM_AIF_OUT("AIF1TX1", "AIF1 Capture", 1,
		     WM8996_POWER_MANAGEMENT_6, 1, 0),
SND_SOC_DAPM_AIF_OUT("AIF1TX0", "AIF1 Capture", 0,
		     WM8996_POWER_MANAGEMENT_6, 0, 0),

/* We route as stereo pairs so define some dummy widgets to squash
 * things down for now.  RXA = 0,1, RXB = 2,3 and so on */
SND_SOC_DAPM_PGA("AIF1RXA", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("AIF1RXB", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("AIF1RXC", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("AIF2RX", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("DSP2TX", SND_SOC_NOPM, 0, 0, NULL, 0),

SND_SOC_DAPM_MUX("DSP1RX", SND_SOC_NOPM, 0, 0, &dsp1rx),
SND_SOC_DAPM_MUX("DSP2RX", SND_SOC_NOPM, 0, 0, &dsp2rx),
SND_SOC_DAPM_MUX("AIF2TX", SND_SOC_NOPM, 0, 0, &aif2tx),

SND_SOC_DAPM_MUX("SPKL", SND_SOC_NOPM, 0, 0, &spkl_mux),
SND_SOC_DAPM_MUX("SPKR", SND_SOC_NOPM, 0, 0, &spkr_mux),
SND_SOC_DAPM_PGA("SPKL PGA", WM8996_LEFT_PDM_SPEAKER, 4, 0, NULL, 0),
SND_SOC_DAPM_PGA("SPKR PGA", WM8996_RIGHT_PDM_SPEAKER, 4, 0, NULL, 0),

SND_SOC_DAPM_PGA_S("HPOUT2L PGA", 0, WM8996_POWER_MANAGEMENT_1, 7, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("HPOUT2L_DLY", 1, WM8996_ANALOGUE_HP_2, 5, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("HPOUT2L_DCS", 2, WM8996_DC_SERVO_1, 2, 0, dcs_start,
		   SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_S("HPOUT2L_OUTP", 3, WM8996_ANALOGUE_HP_2, 6, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("HPOUT2L_RMV_SHORT", 3, SND_SOC_NOPM, HPOUT2L, 0,
		   rmv_short_event,
		   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),

SND_SOC_DAPM_PGA_S("HPOUT2R PGA", 0, WM8996_POWER_MANAGEMENT_1, 6, 0,NULL, 0),
SND_SOC_DAPM_PGA_S("HPOUT2R_DLY", 1, WM8996_ANALOGUE_HP_2, 1, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("HPOUT2R_DCS", 2, WM8996_DC_SERVO_1, 3, 0, dcs_start,
		   SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_S("HPOUT2R_OUTP", 3, WM8996_ANALOGUE_HP_2, 2, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("HPOUT2R_RMV_SHORT", 3, SND_SOC_NOPM, HPOUT2R, 0,
		   rmv_short_event,
		   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),

SND_SOC_DAPM_PGA_S("HPOUT1L PGA", 0, WM8996_POWER_MANAGEMENT_1, 5, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("HPOUT1L_DLY", 1, WM8996_ANALOGUE_HP_1, 5, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("HPOUT1L_DCS", 2, WM8996_DC_SERVO_1, 0, 0, dcs_start,
		   SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_S("HPOUT1L_OUTP", 3, WM8996_ANALOGUE_HP_1, 6, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("HPOUT1L_RMV_SHORT", 3, SND_SOC_NOPM, HPOUT1L, 0,
		   rmv_short_event,
		   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),

SND_SOC_DAPM_PGA_S("HPOUT1R PGA", 0, WM8996_POWER_MANAGEMENT_1, 4, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("HPOUT1R_DLY", 1, WM8996_ANALOGUE_HP_1, 1, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("HPOUT1R_DCS", 2, WM8996_DC_SERVO_1, 1, 0, dcs_start,
		   SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_S("HPOUT1R_OUTP", 3, WM8996_ANALOGUE_HP_1, 2, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("HPOUT1R_RMV_SHORT", 3, SND_SOC_NOPM, HPOUT1R, 0,
		   rmv_short_event,
		   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),

SND_SOC_DAPM_OUTPUT("HPOUT1L"),
SND_SOC_DAPM_OUTPUT("HPOUT1R"),
SND_SOC_DAPM_OUTPUT("HPOUT2L"),
SND_SOC_DAPM_OUTPUT("HPOUT2R"),
SND_SOC_DAPM_OUTPUT("SPKDAT"),
};

static const struct snd_soc_dapm_route wm8996_dapm_routes[] = {
	{ "AIFCLK", NULL, "SYSCLK" },
	{ "SYSDSPCLK", NULL, "SYSCLK" },
	{ "Charge Pump", NULL, "SYSCLK" },

	{ "MICB1", NULL, "LDO2" },
	{ "MICB2", NULL, "LDO2" },

	{ "IN1L PGA", NULL, "IN2LN" },
	{ "IN1L PGA", NULL, "IN2LP" },
	{ "IN1L PGA", NULL, "IN1LN" },
	{ "IN1L PGA", NULL, "IN1LP" },

	{ "IN1R PGA", NULL, "IN2RN" },
	{ "IN1R PGA", NULL, "IN2RP" },
	{ "IN1R PGA", NULL, "IN1RN" },
	{ "IN1R PGA", NULL, "IN1RP" },

	{ "ADCL", NULL, "IN1L PGA" },

	{ "ADCR", NULL, "IN1R PGA" },

	{ "DMIC1L", NULL, "DMIC1DAT" },
	{ "DMIC1R", NULL, "DMIC1DAT" },
	{ "DMIC2L", NULL, "DMIC2DAT" },
	{ "DMIC2R", NULL, "DMIC2DAT" },

	{ "DMIC2L", NULL, "DMIC2" },<commit_msg>ASoC: Automatically manage WM8996 MICBIAS regulating mode<commit_after>/*
 * wm8996.c - WM8996 audio codec interface
 *
 * Copyright 2011 Wolfson Microelectronics PLC.
 * Author: <NAME> <<EMAIL>>
 *
 *  This program is free software; you can redistribute  it and/or modify it
 *  under  the terms of  the GNU General  Public License as published by the
 *  Free Software Foundation;  either version 2 of the  License, or (at your
 *  option) any later version.
 */

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/gcd.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <sound/core.h>
#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include <trace/events/asoc.h>

#include <sound/wm8996.h>
#include "wm8996.h"

#define WM8996_AIFS 2

#define HPOUT1L 1
#define HPOUT1R 2
#define HPOUT2L 4
#define HPOUT2R 8

#define WM8996_NUM_SUPPLIES 4
static const char *wm8996_supply_names[WM8996_NUM_SUPPLIES] = {
	"DBVDD",
	"AVDD1",
	"AVDD2",
	"CPVDD",
};

struct wm8996_priv {
	struct snd_soc_codec *codec;

	int ldo1ena;

	int sysclk;
	int sysclk_src;

	int fll_src;
	int fll_fref;
	int fll_fout;

	struct completion fll_lock;

	u16 dcs_pending;
	struct completion dcs_done;

	u16 hpout_ena;
	u16 hpout_pending;

	struct regulator_bulk_data supplies[WM8996_NUM_SUPPLIES];
	struct notifier_block disable_nb[WM8996_NUM_SUPPLIES];

	struct wm8996_pdata pdata;

	int rx_rate[WM8996_AIFS];
	int bclk_rate[WM8996_AIFS];

	/* Platform dependant ReTune mobile configuration */
	int num_retune_mobile_texts;
	const char **retune_mobile_texts;
	int retune_mobile_cfg[2];
	struct soc_enum retune_mobile_enum;

	struct snd_soc_jack *jack;
	bool detecting;
	bool jack_mic;
	wm8996_polarity_fn polarity_cb;

#ifdef CONFIG_GPIOLIB
	struct gpio_chip gpio_chip;
#endif
};

/* We can't use the same notifier block for more than one supply and
 * there's no way I can see to get from a callback to the caller
 * except container_of().
 */
#define WM8996_REGULATOR_EVENT(n) \
static int wm8996_regulator_event_##n(struct notifier_block *nb, \
				    unsigned long event, void *data)	\
{ \
	struct wm8996_priv *wm8996 = container_of(nb, struct wm8996_priv, \
						  disable_nb[n]); \
	if (event & REGULATOR_EVENT_DISABLE) { \
		wm8996->codec->cache_sync = 1; \
	} \
	return 0; \
}

WM8996_REGULATOR_EVENT(0)
WM8996_REGULATOR_EVENT(1)
WM8996_REGULATOR_EVENT(2)
WM8996_REGULATOR_EVENT(3)

static const u16 wm8996_reg[WM8996_MAX_REGISTER] = {
	[WM8996_SOFTWARE_RESET] = 0x8996,
	[WM8996_POWER_MANAGEMENT_7] = 0x10,
	[WM8996_DAC1_HPOUT1_VOLUME] = 0x88,
	[WM8996_DAC2_HPOUT2_VOLUME] = 0x88,
	[WM8996_DAC1_LEFT_VOLUME] = 0x2c0,
	[WM8996_DAC1_RIGHT_VOLUME] = 0x2c0,
	[WM8996_DAC2_LEFT_VOLUME] = 0x2c0,
	[WM8996_DAC2_RIGHT_VOLUME] = 0x2c0,
	[WM8996_OUTPUT1_LEFT_VOLUME] = 0x80,
	[WM8996_OUTPUT1_RIGHT_VOLUME] = 0x80,
	[WM8996_OUTPUT2_LEFT_VOLUME] = 0x80,
	[WM8996_OUTPUT2_RIGHT_VOLUME] = 0x80,
	[WM8996_MICBIAS_1] = 0x39,
	[WM8996_MICBIAS_2] = 0x39,
	[WM8996_LDO_1] = 0x3,
	[WM8996_LDO_2] = 0x13,
	[WM8996_ACCESSORY_DETECT_MODE_1] = 0x4,
	[WM8996_HEADPHONE_DETECT_1] = 0x20,
	[WM8996_MIC_DETECT_1] = 0x7600,
	[WM8996_MIC_DETECT_2] = 0xbf,
	[WM8996_CHARGE_PUMP_1] = 0x1f25,
	[WM8996_CHARGE_PUMP_2] = 0xab19,
	[WM8996_DC_SERVO_5] = 0x2a2a,
	[WM8996_CONTROL_INTERFACE_1] = 0x8004,
	[WM8996_CLOCKING_1] = 0x10,
	[WM8996_AIF_RATE] = 0x83,
	[WM8996_FLL_CONTROL_4] = 0x5dc0,
	[WM8996_FLL_CONTROL_5] = 0xc84,
	[WM8996_FLL_EFS_2] = 0x2,
	[WM8996_AIF1_TX_LRCLK_1] = 0x80,
	[WM8996_AIF1_TX_LRCLK_2] = 0x8,
	[WM8996_AIF1_RX_LRCLK_1] = 0x80,
	[WM8996_AIF1TX_DATA_CONFIGURATION_1] = 0x1818,
	[WM8996_AIF1RX_DATA_CONFIGURATION] = 0x1818,
	[WM8996_AIF1TX_TEST] = 0x7,
	[WM8996_AIF2_TX_LRCLK_1] = 0x80,
	[WM8996_AIF2_TX_LRCLK_2] = 0x8,
	[WM8996_AIF2_RX_LRCLK_1] = 0x80,
	[WM8996_AIF2TX_DATA_CONFIGURATION_1] = 0x1818,
	[WM8996_AIF2RX_DATA_CONFIGURATION] = 0x1818,
	[WM8996_AIF2TX_TEST] = 0x1,
	[WM8996_DSP1_TX_LEFT_VOLUME] = 0xc0,
	[WM8996_DSP1_TX_RIGHT_VOLUME] = 0xc0,
	[WM8996_DSP1_RX_LEFT_VOLUME] = 0xc0,
	[WM8996_DSP1_RX_RIGHT_VOLUME] = 0xc0,
	[WM8996_DSP1_TX_FILTERS] = 0x2000,
	[WM8996_DSP1_RX_FILTERS_1] = 0x200,
	[WM8996_DSP1_RX_FILTERS_2] = 0x10,
	[WM8996_DSP1_DRC_1] = 0x98,
	[WM8996_DSP1_DRC_2] = 0x845,
	[WM8996_DSP1_RX_EQ_GAINS_1] = 0x6318,
	[WM8996_DSP1_RX_EQ_GAINS_2] = 0x6300,
	[WM8996_DSP1_RX_EQ_BAND_1_A] = 0xfca,
	[WM8996_DSP1_RX_EQ_BAND_1_B] = 0x400,
	[WM8996_DSP1_RX_EQ_BAND_1_PG] = 0xd8,
	[WM8996_DSP1_RX_EQ_BAND_2_A] = 0x1eb5,
	[WM8996_DSP1_RX_EQ_BAND_2_B] = 0xf145,
	[WM8996_DSP1_RX_EQ_BAND_2_C] = 0xb75,
	[WM8996_DSP1_RX_EQ_BAND_2_PG] = 0x1c5,
	[WM8996_DSP1_RX_EQ_BAND_3_A] = 0x1c58,
	[WM8996_DSP1_RX_EQ_BAND_3_B] = 0xf373,
	[WM8996_DSP1_RX_EQ_BAND_3_C] = 0xa54,
	[WM8996_DSP1_RX_EQ_BAND_3_PG] = 0x558,
	[WM8996_DSP1_RX_EQ_BAND_4_A] = 0x168e,
	[WM8996_DSP1_RX_EQ_BAND_4_B] = 0xf829,
	[WM8996_DSP1_RX_EQ_BAND_4_C] = 0x7ad,
	[WM8996_DSP1_RX_EQ_BAND_4_PG] = 0x1103,
	[WM8996_DSP1_RX_EQ_BAND_5_A] = 0x564,
	[WM8996_DSP1_RX_EQ_BAND_5_B] = 0x559,
	[WM8996_DSP1_RX_EQ_BAND_5_PG] = 0x4000,
	[WM8996_DSP2_TX_LEFT_VOLUME] = 0xc0,
	[WM8996_DSP2_TX_RIGHT_VOLUME] = 0xc0,
	[WM8996_DSP2_RX_LEFT_VOLUME] = 0xc0,
	[WM8996_DSP2_RX_RIGHT_VOLUME] = 0xc0,
	[WM8996_DSP2_TX_FILTERS] = 0x2000,
	[WM8996_DSP2_RX_FILTERS_1] = 0x200,
	[WM8996_DSP2_RX_FILTERS_2] = 0x10,
	[WM8996_DSP2_DRC_1] = 0x98,
	[WM8996_DSP2_DRC_2] = 0x845,
	[WM8996_DSP2_RX_EQ_GAINS_1] = 0x6318,
	[WM8996_DSP2_RX_EQ_GAINS_2] = 0x6300,
	[WM8996_DSP2_RX_EQ_BAND_1_A] = 0xfca,
	[WM8996_DSP2_RX_EQ_BAND_1_B] = 0x400,
	[WM8996_DSP2_RX_EQ_BAND_1_PG] = 0xd8,
	[WM8996_DSP2_RX_EQ_BAND_2_A] = 0x1eb5,
	[WM8996_DSP2_RX_EQ_BAND_2_B] = 0xf145,
	[WM8996_DSP2_RX_EQ_BAND_2_C] = 0xb75,
	[WM8996_DSP2_RX_EQ_BAND_2_PG] = 0x1c5,
	[WM8996_DSP2_RX_EQ_BAND_3_A] = 0x1c58,
	[WM8996_DSP2_RX_EQ_BAND_3_B] = 0xf373,
	[WM8996_DSP2_RX_EQ_BAND_3_C] = 0xa54,
	[WM8996_DSP2_RX_EQ_BAND_3_PG] = 0x558,
	[WM8996_DSP2_RX_EQ_BAND_4_A] = 0x168e,
	[WM8996_DSP2_RX_EQ_BAND_4_B] = 0xf829,
	[WM8996_DSP2_RX_EQ_BAND_4_C] = 0x7ad,
	[WM8996_DSP2_RX_EQ_BAND_4_PG] = 0x1103,
	[WM8996_DSP2_RX_EQ_BAND_5_A] = 0x564,
	[WM8996_DSP2_RX_EQ_BAND_5_B] = 0x559,
	[WM8996_DSP2_RX_EQ_BAND_5_PG] = 0x4000,
	[WM8996_OVERSAMPLING] = 0xd,
	[WM8996_SIDETONE] = 0x1040,
	[WM8996_GPIO_1] = 0xa101,
	[WM8996_GPIO_2] = 0xa101,
	[WM8996_GPIO_3] = 0xa101,
	[WM8996_GPIO_4] = 0xa101,
	[WM8996_GPIO_5] = 0xa101,
	[WM8996_PULL_CONTROL_2] = 0x140,
	[WM8996_INTERRUPT_STATUS_1_MASK] = 0x1f,
	[WM8996_INTERRUPT_STATUS_2_MASK] = 0x1ecf,
	[WM8996_RIGHT_PDM_SPEAKER] = 0x1,
	[WM8996_PDM_SPEAKER_MUTE_SEQUENCE] = 0x69,
	[WM8996_PDM_SPEAKER_VOLUME] = 0x66,
	[WM8996_WRITE_SEQUENCER_0] = 0x1,
	[WM8996_WRITE_SEQUENCER_1] = 0x1,
	[WM8996_WRITE_SEQUENCER_3] = 0x6,
	[WM8996_WRITE_SEQUENCER_4] = 0x40,
	[WM8996_WRITE_SEQUENCER_5] = 0x1,
	[WM8996_WRITE_SEQUENCER_6] = 0xf,
	[WM8996_WRITE_SEQUENCER_7] = 0x6,
	[WM8996_WRITE_SEQUENCER_8] = 0x1,
	[WM8996_WRITE_SEQUENCER_9] = 0x3,
	[WM8996_WRITE_SEQUENCER_10] = 0x104,
	[WM8996_WRITE_SEQUENCER_12] = 0x60,
	[WM8996_WRITE_SEQUENCER_13] = 0x11,
	[WM8996_WRITE_SEQUENCER_14] = 0x401,
	[WM8996_WRITE_SEQUENCER_16] = 0x50,
	[WM8996_WRITE_SEQUENCER_17] = 0x3,
	[WM8996_WRITE_SEQUENCER_18] = 0x100,
	[WM8996_WRITE_SEQUENCER_20] = 0x51,
	[WM8996_WRITE_SEQUENCER_21] = 0x3,
	[WM8996_WRITE_SEQUENCER_22] = 0x104,
	[WM8996_WRITE_SEQUENCER_23] = 0xa,
	[WM8996_WRITE_SEQUENCER_24] = 0x60,
	[WM8996_WRITE_SEQUENCER_25] = 0x3b,
	[WM8996_WRITE_SEQUENCER_26] = 0x502,
	[WM8996_WRITE_SEQUENCER_27] = 0x100,
	[WM8996_WRITE_SEQUENCER_28] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_32] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_36] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_40] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_44] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_48] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_52] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_56] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_60] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_64] = 0x1,
	[WM8996_WRITE_SEQUENCER_65] = 0x1,
	[WM8996_WRITE_SEQUENCER_67] = 0x6,
	[WM8996_WRITE_SEQUENCER_68] = 0x40,
	[WM8996_WRITE_SEQUENCER_69] = 0x1,
	[WM8996_WRITE_SEQUENCER_70] = 0xf,
	[WM8996_WRITE_SEQUENCER_71] = 0x6,
	[WM8996_WRITE_SEQUENCER_72] = 0x1,
	[WM8996_WRITE_SEQUENCER_73] = 0x3,
	[WM8996_WRITE_SEQUENCER_74] = 0x104,
	[WM8996_WRITE_SEQUENCER_76] = 0x60,
	[WM8996_WRITE_SEQUENCER_77] = 0x11,
	[WM8996_WRITE_SEQUENCER_78] = 0x401,
	[WM8996_WRITE_SEQUENCER_80] = 0x50,
	[WM8996_WRITE_SEQUENCER_81] = 0x3,
	[WM8996_WRITE_SEQUENCER_82] = 0x100,
	[WM8996_WRITE_SEQUENCER_84] = 0x60,
	[WM8996_WRITE_SEQUENCER_85] = 0x3b,
	[WM8996_WRITE_SEQUENCER_86] = 0x502,
	[WM8996_WRITE_SEQUENCER_87] = 0x100,
	[WM8996_WRITE_SEQUENCER_88] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_92] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_96] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_100] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_104] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_108] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_112] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_116] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_120] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_124] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_128] = 0x1,
	[WM8996_WRITE_SEQUENCER_129] = 0x1,
	[WM8996_WRITE_SEQUENCER_131] = 0x6,
	[WM8996_WRITE_SEQUENCER_132] = 0x40,
	[WM8996_WRITE_SEQUENCER_133] = 0x1,
	[WM8996_WRITE_SEQUENCER_134] = 0xf,
	[WM8996_WRITE_SEQUENCER_135] = 0x6,
	[WM8996_WRITE_SEQUENCER_136] = 0x1,
	[WM8996_WRITE_SEQUENCER_137] = 0x3,
	[WM8996_WRITE_SEQUENCER_138] = 0x106,
	[WM8996_WRITE_SEQUENCER_140] = 0x61,
	[WM8996_WRITE_SEQUENCER_141] = 0x11,
	[WM8996_WRITE_SEQUENCER_142] = 0x401,
	[WM8996_WRITE_SEQUENCER_144] = 0x50,
	[WM8996_WRITE_SEQUENCER_145] = 0x3,
	[WM8996_WRITE_SEQUENCER_146] = 0x102,
	[WM8996_WRITE_SEQUENCER_148] = 0x51,
	[WM8996_WRITE_SEQUENCER_149] = 0x3,
	[WM8996_WRITE_SEQUENCER_150] = 0x106,
	[WM8996_WRITE_SEQUENCER_151] = 0xa,
	[WM8996_WRITE_SEQUENCER_152] = 0x61,
	[WM8996_WRITE_SEQUENCER_153] = 0x3b,
	[WM8996_WRITE_SEQUENCER_154] = 0x502,
	[WM8996_WRITE_SEQUENCER_155] = 0x100,
	[WM8996_WRITE_SEQUENCER_156] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_160] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_164] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_168] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_172] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_176] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_180] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_184] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_188] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_192] = 0x1,
	[WM8996_WRITE_SEQUENCER_193] = 0x1,
	[WM8996_WRITE_SEQUENCER_195] = 0x6,
	[WM8996_WRITE_SEQUENCER_196] = 0x40,
	[WM8996_WRITE_SEQUENCER_197] = 0x1,
	[WM8996_WRITE_SEQUENCER_198] = 0xf,
	[WM8996_WRITE_SEQUENCER_199] = 0x6,
	[WM8996_WRITE_SEQUENCER_200] = 0x1,
	[WM8996_WRITE_SEQUENCER_201] = 0x3,
	[WM8996_WRITE_SEQUENCER_202] = 0x106,
	[WM8996_WRITE_SEQUENCER_204] = 0x61,
	[WM8996_WRITE_SEQUENCER_205] = 0x11,
	[WM8996_WRITE_SEQUENCER_206] = 0x401,
	[WM8996_WRITE_SEQUENCER_208] = 0x50,
	[WM8996_WRITE_SEQUENCER_209] = 0x3,
	[WM8996_WRITE_SEQUENCER_210] = 0x102,
	[WM8996_WRITE_SEQUENCER_212] = 0x61,
	[WM8996_WRITE_SEQUENCER_213] = 0x3b,
	[WM8996_WRITE_SEQUENCER_214] = 0x502,
	[WM8996_WRITE_SEQUENCER_215] = 0x100,
	[WM8996_WRITE_SEQUENCER_216] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_220] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_224] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_228] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_232] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_236] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_240] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_244] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_248] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_252] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_256] = 0x60,
	[WM8996_WRITE_SEQUENCER_258] = 0x601,
	[WM8996_WRITE_SEQUENCER_260] = 0x50,
	[WM8996_WRITE_SEQUENCER_262] = 0x100,
	[WM8996_WRITE_SEQUENCER_264] = 0x1,
	[WM8996_WRITE_SEQUENCER_266] = 0x104,
	[WM8996_WRITE_SEQUENCER_267] = 0x100,
	[WM8996_WRITE_SEQUENCER_268] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_272] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_276] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_280] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_284] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_288] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_292] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_296] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_300] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_304] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_308] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_312] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_316] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_320] = 0x61,
	[WM8996_WRITE_SEQUENCER_322] = 0x601,
	[WM8996_WRITE_SEQUENCER_324] = 0x50,
	[WM8996_WRITE_SEQUENCER_326] = 0x102,
	[WM8996_WRITE_SEQUENCER_328] = 0x1,
	[WM8996_WRITE_SEQUENCER_330] = 0x106,
	[WM8996_WRITE_SEQUENCER_331] = 0x100,
	[WM8996_WRITE_SEQUENCER_332] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_336] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_340] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_344] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_348] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_352] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_356] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_360] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_364] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_368] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_372] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_376] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_380] = 0x2fff,
	[WM8996_WRITE_SEQUENCER_384] = 0x60,
	[WM8996_WRITE_SEQUENCER_386] = 0x601,
	[WM8996_WRITE_SEQUENCER_388] = 0x61,
	[WM8996_WRITE_SEQUENCER_390] = 0x601,
	[WM8996_WRITE_SEQUENCER_392] = 0x50,
	[WM8996_WRITE_SEQUENCER_394] = 0x300,
	[WM8996_WRITE_SEQUENCER_396] = 0x1,
	[WM8996_WRITE_SEQUENCER_398] = 0x304,
	[WM8996_WRITE_SEQUENCER_400] = 0x40,
	[WM8996_WRITE_SEQUENCER_402] = 0xf,
	[WM8996_WRITE_SEQUENCER_404] = 0x1,
	[WM8996_WRITE_SEQUENCER_407] = 0x100,
};

static const DECLARE_TLV_DB_SCALE(inpga_tlv, 0, 100, 0);
static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -3600, 150, 0);
static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1);
static const DECLARE_TLV_DB_SCALE(out_digital_tlv, -1200, 150, 0);
static const DECLARE_TLV_DB_SCALE(out_tlv, -900, 75, 0);
static const DECLARE_TLV_DB_SCALE(spk_tlv, -900, 150, 0);
static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);

static const char *sidetone_hpf_text[] = {
	"2.9kHz", "1.5kHz", "735Hz", "403Hz", "196Hz", "98Hz", "49Hz"
};

static const struct soc_enum sidetone_hpf =
	SOC_ENUM_SINGLE(WM8996_SIDETONE, 7, 6, sidetone_hpf_text);

static const char *hpf_mode_text[] = {
	"HiFi", "Custom", "Voice"
};

static const struct soc_enum dsp1tx_hpf_mode =
	SOC_ENUM_SINGLE(WM8996_DSP1_TX_FILTERS, 3, 3, hpf_mode_text);

static const struct soc_enum dsp2tx_hpf_mode =
	SOC_ENUM_SINGLE(WM8996_DSP2_TX_FILTERS, 3, 3, hpf_mode_text);

static const char *hpf_cutoff_text[] = {
	"50Hz", "75Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz"
};

static const struct soc_enum dsp1tx_hpf_cutoff =
	SOC_ENUM_SINGLE(WM8996_DSP1_TX_FILTERS, 0, 7, hpf_cutoff_text);

static const struct soc_enum dsp2tx_hpf_cutoff =
	SOC_ENUM_SINGLE(WM8996_DSP2_TX_FILTERS, 0, 7, hpf_cutoff_text);

static void wm8996_set_retune_mobile(struct snd_soc_codec *codec, int block)
{
	struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
	struct wm8996_pdata *pdata = &wm8996->pdata;
	int base, best, best_val, save, i, cfg, iface;

	if (!wm8996->num_retune_mobile_texts)
		return;

	switch (block) {
	case 0:
		base = WM8996_DSP1_RX_EQ_GAINS_1;
		if (snd_soc_read(codec, WM8996_POWER_MANAGEMENT_8) &
		    WM8996_DSP1RX_SRC)
			iface = 1;
		else
			iface = 0;
		break;
	case 1:
		base = WM8996_DSP1_RX_EQ_GAINS_2;
		if (snd_soc_read(codec, WM8996_POWER_MANAGEMENT_8) &
		    WM8996_DSP2RX_SRC)
			iface = 1;
		else
			iface = 0;
		break;
	default:
		return;
	}

	/* Find the version of the currently selected configuration
	 * with the nearest sample rate. */
	cfg = wm8996->retune_mobile_cfg[block];
	best = 0;
	best_val = INT_MAX;
	for (i = 0; i < pdata->num_retune_mobile_cfgs; i++) {
		if (strcmp(pdata->retune_mobile_cfgs[i].name,
			   wm8996->retune_mobile_texts[cfg]) == 0 &&
		    abs(pdata->retune_mobile_cfgs[i].rate
			- wm8996->rx_rate[iface]) < best_val) {
			best = i;
			best_val = abs(pdata->retune_mobile_cfgs[i].rate
				       - wm8996->rx_rate[iface]);
		}
	}

	dev_dbg(codec->dev, "ReTune Mobile %d %s/%dHz for %dHz sample rate\n",
		block,
		pdata->retune_mobile_cfgs[best].name,
		pdata->retune_mobile_cfgs[best].rate,
		wm8996->rx_rate[iface]);

	/* The EQ will be disabled while reconfiguring it, remember the
	 * current configuration. 
	 */
	save = snd_soc_read(codec, base);
	save &= WM8996_DSP1RX_EQ_ENA;

	for (i = 0; i < ARRAY_SIZE(pdata->retune_mobile_cfgs[best].regs); i++)
		snd_soc_update_bits(codec, base + i, 0xffff,
				    pdata->retune_mobile_cfgs[best].regs[i]);

	snd_soc_update_bits(codec, base, WM8996_DSP1RX_EQ_ENA, save);
}

/* Icky as hell but saves code duplication */
static int wm8996_get_retune_mobile_block(const char *name)
{
	if (strcmp(name, "DSP1 EQ Mode") == 0)
		return 0;
	if (strcmp(name, "DSP2 EQ Mode") == 0)
		return 1;
	return -EINVAL;
}

static int wm8996_put_retune_mobile_enum(struct snd_kcontrol *kcontrol,
					 struct snd_ctl_elem_value *ucontrol)
{
	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
	struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
	struct wm8996_pdata *pdata = &wm8996->pdata;
	int block = wm8996_get_retune_mobile_block(kcontrol->id.name);
	int value = ucontrol->value.integer.value[0];

	if (block < 0)
		return block;

	if (value >= pdata->num_retune_mobile_cfgs)
		return -EINVAL;

	wm8996->retune_mobile_cfg[block] = value;

	wm8996_set_retune_mobile(codec, block);

	return 0;
}

static int wm8996_get_retune_mobile_enum(struct snd_kcontrol *kcontrol,
					 struct snd_ctl_elem_value *ucontrol)
{
	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
	struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
	int block = wm8996_get_retune_mobile_block(kcontrol->id.name);

	ucontrol->value.enumerated.item[0] = wm8996->retune_mobile_cfg[block];

	return 0;
}

static const struct snd_kcontrol_new wm8996_snd_controls[] = {
SOC_DOUBLE_R_TLV("Capture Volume", WM8996_LEFT_LINE_INPUT_VOLUME,
		 WM8996_RIGHT_LINE_INPUT_VOLUME, 0, 31, 0, inpga_tlv),
SOC_DOUBLE_R("Capture ZC Switch", WM8996_LEFT_LINE_INPUT_VOLUME,
	     WM8996_RIGHT_LINE_INPUT_VOLUME, 5, 1, 0),

SOC_DOUBLE_TLV("DAC1 Sidetone Volume", WM8996_DAC1_MIXER_VOLUMES,
	       0, 5, 24, 0, sidetone_tlv),
SOC_DOUBLE_TLV("DAC2 Sidetone Volume", WM8996_DAC2_MIXER_VOLUMES,
	       0, 5, 24, 0, sidetone_tlv),
SOC_SINGLE("Sidetone LPF Switch", WM8996_SIDETONE, 12, 1, 0),
SOC_ENUM("Sidetone HPF Cut-off", sidetone_hpf),
SOC_SINGLE("Sidetone HPF Switch", WM8996_SIDETONE, 6, 1, 0),

SOC_DOUBLE_R_TLV("DSP1 Capture Volume", WM8996_DSP1_TX_LEFT_VOLUME,
		 WM8996_DSP1_TX_RIGHT_VOLUME, 1, 96, 0, digital_tlv),
SOC_DOUBLE_R_TLV("DSP2 Capture Volume", WM8996_DSP2_TX_LEFT_VOLUME,
		 WM8996_DSP2_TX_RIGHT_VOLUME, 1, 96, 0, digital_tlv),

SOC_SINGLE("DSP1 Capture Notch Filter Switch", WM8996_DSP1_TX_FILTERS,
	   13, 1, 0),
SOC_DOUBLE("DSP1 Capture HPF Switch", WM8996_DSP1_TX_FILTERS, 12, 11, 1, 0),
SOC_ENUM("DSP1 Capture HPF Mode", dsp1tx_hpf_mode),
SOC_ENUM("DSP1 Capture HPF Cutoff", dsp1tx_hpf_cutoff),

SOC_SINGLE("DSP2 Capture Notch Filter Switch", WM8996_DSP2_TX_FILTERS,
	   13, 1, 0),
SOC_DOUBLE("DSP2 Capture HPF Switch", WM8996_DSP2_TX_FILTERS, 12, 11, 1, 0),
SOC_ENUM("DSP2 Capture HPF Mode", dsp2tx_hpf_mode),
SOC_ENUM("DSP2 Capture HPF Cutoff", dsp2tx_hpf_cutoff),

SOC_DOUBLE_R_TLV("DSP1 Playback Volume", WM8996_DSP1_RX_LEFT_VOLUME,
		 WM8996_DSP1_RX_RIGHT_VOLUME, 1, 112, 0, digital_tlv),
SOC_SINGLE("DSP1 Playback Switch", WM8996_DSP1_RX_FILTERS_1, 9, 1, 1),

SOC_DOUBLE_R_TLV("DSP2 Playback Volume", WM8996_DSP2_RX_LEFT_VOLUME,
		 WM8996_DSP2_RX_RIGHT_VOLUME, 1, 112, 0, digital_tlv),
SOC_SINGLE("DSP2 Playback Switch", WM8996_DSP2_RX_FILTERS_1, 9, 1, 1),

SOC_DOUBLE_R_TLV("DAC1 Volume", WM8996_DAC1_LEFT_VOLUME,
		 WM8996_DAC1_RIGHT_VOLUME, 1, 112, 0, digital_tlv),
SOC_DOUBLE_R("DAC1 Switch", WM8996_DAC1_LEFT_VOLUME,
	     WM8996_DAC1_RIGHT_VOLUME, 9, 1, 1),

SOC_DOUBLE_R_TLV("DAC2 Volume", WM8996_DAC2_LEFT_VOLUME,
		 WM8996_DAC2_RIGHT_VOLUME, 1, 112, 0, digital_tlv),
SOC_DOUBLE_R("DAC2 Switch", WM8996_DAC2_LEFT_VOLUME,
	     WM8996_DAC2_RIGHT_VOLUME, 9, 1, 1),

SOC_SINGLE("Speaker High Performance Switch", WM8996_OVERSAMPLING, 3, 1, 0),
SOC_SINGLE("DMIC High Performance Switch", WM8996_OVERSAMPLING, 2, 1, 0),
SOC_SINGLE("ADC High Performance Switch", WM8996_OVERSAMPLING, 1, 1, 0),
SOC_SINGLE("DAC High Performance Switch", WM8996_OVERSAMPLING, 0, 1, 0),

SOC_SINGLE("DAC Soft Mute Switch", WM8996_DAC_SOFTMUTE, 1, 1, 0),
SOC_SINGLE("DAC Slow Soft Mute Switch", WM8996_DAC_SOFTMUTE, 0, 1, 0),

SOC_DOUBLE_TLV("Digital Output 1 Volume", WM8996_DAC1_HPOUT1_VOLUME, 0, 4,
	       8, 0, out_digital_tlv),
SOC_DOUBLE_TLV("Digital Output 2 Volume", WM8996_DAC2_HPOUT2_VOLUME, 0, 4,
	       8, 0, out_digital_tlv),

SOC_DOUBLE_R_TLV("Output 1 Volume", WM8996_OUTPUT1_LEFT_VOLUME,
		 WM8996_OUTPUT1_RIGHT_VOLUME, 0, 12, 0, out_tlv),
SOC_DOUBLE_R("Output 1 ZC Switch",  WM8996_OUTPUT1_LEFT_VOLUME,
	     WM8996_OUTPUT1_RIGHT_VOLUME, 7, 1, 0),

SOC_DOUBLE_R_TLV("Output 2 Volume", WM8996_OUTPUT2_LEFT_VOLUME,
		 WM8996_OUTPUT2_RIGHT_VOLUME, 0, 12, 0, out_tlv),
SOC_DOUBLE_R("Output 2 ZC Switch",  WM8996_OUTPUT2_LEFT_VOLUME,
	     WM8996_OUTPUT2_RIGHT_VOLUME, 7, 1, 0),

SOC_DOUBLE_TLV("Speaker Volume", WM8996_PDM_SPEAKER_VOLUME, 0, 4, 8, 0,
	       spk_tlv),
SOC_DOUBLE_R("Speaker Switch", WM8996_LEFT_PDM_SPEAKER,
	     WM8996_RIGHT_PDM_SPEAKER, 3, 1, 1),
SOC_DOUBLE_R("Speaker ZC Switch", WM8996_LEFT_PDM_SPEAKER,
	     WM8996_RIGHT_PDM_SPEAKER, 2, 1, 0),

SOC_SINGLE("DSP1 EQ Switch", WM8996_DSP1_RX_EQ_GAINS_1, 0, 1, 0),
SOC_SINGLE("DSP2 EQ Switch", WM8996_DSP2_RX_EQ_GAINS_1, 0, 1, 0),
};

static const struct snd_kcontrol_new wm8996_eq_controls[] = {
SOC_SINGLE_TLV("DSP1 EQ B1 Volume", WM8996_DSP1_RX_EQ_GAINS_1, 11, 31, 0,
	       eq_tlv),
SOC_SINGLE_TLV("DSP1 EQ B2 Volume", WM8996_DSP1_RX_EQ_GAINS_1, 6, 31, 0,
	       eq_tlv),
SOC_SINGLE_TLV("DSP1 EQ B3 Volume", WM8996_DSP1_RX_EQ_GAINS_1, 1, 31, 0,
	       eq_tlv),
SOC_SINGLE_TLV("DSP1 EQ B4 Volume", WM8996_DSP1_RX_EQ_GAINS_2, 11, 31, 0,
	       eq_tlv),
SOC_SINGLE_TLV("DSP1 EQ B5 Volume", WM8996_DSP1_RX_EQ_GAINS_2, 6, 31, 0,
	       eq_tlv),

SOC_SINGLE_TLV("DSP2 EQ B1 Volume", WM8996_DSP2_RX_EQ_GAINS_1, 11, 31, 0,
	       eq_tlv),
SOC_SINGLE_TLV("DSP2 EQ B2 Volume", WM8996_DSP2_RX_EQ_GAINS_1, 6, 31, 0,
	       eq_tlv),
SOC_SINGLE_TLV("DSP2 EQ B3 Volume", WM8996_DSP2_RX_EQ_GAINS_1, 1, 31, 0,
	       eq_tlv),
SOC_SINGLE_TLV("DSP2 EQ B4 Volume", WM8996_DSP2_RX_EQ_GAINS_2, 11, 31, 0,
	       eq_tlv),
SOC_SINGLE_TLV("DSP2 EQ B5 Volume", WM8996_DSP2_RX_EQ_GAINS_2, 6, 31, 0,
	       eq_tlv),
};

static int cp_event(struct snd_soc_dapm_widget *w,
		    struct snd_kcontrol *kcontrol, int event)
{
	switch (event) {
	case SND_SOC_DAPM_POST_PMU:
		msleep(5);
		break;
	default:
		BUG();
		return -EINVAL;
	}

	return 0;
}

static int rmv_short_event(struct snd_soc_dapm_widget *w,
			   struct snd_kcontrol *kcontrol, int event)
{
	struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(w->codec);

	/* Record which outputs we enabled */
	switch (event) {
	case SND_SOC_DAPM_PRE_PMD:
		wm8996->hpout_pending &= ~w->shift;
		break;
	case SND_SOC_DAPM_PRE_PMU:
		wm8996->hpout_pending |= w->shift;
		break;
	default:
		BUG();
		return -EINVAL;
	}

	return 0;
}

static void wait_for_dc_servo(struct snd_soc_codec *codec, u16 mask)
{
	struct i2c_client *i2c = to_i2c_client(codec->dev);
	struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
	int i, ret;
	unsigned long timeout = 200;

	snd_soc_write(codec, WM8996_DC_SERVO_2, mask);

	/* Use the interrupt if possible */
	do {
		if (i2c->irq) {
			timeout = wait_for_completion_timeout(&wm8996->dcs_done,
							      msecs_to_jiffies(200));
			if (timeout == 0)
				dev_err(codec->dev, "DC servo timed out\n");

		} else {
			msleep(1);
			if (--i) {
				timeout = 0;
				break;
			}
		}

		ret = snd_soc_read(codec, WM8996_DC_SERVO_2);
		dev_dbg(codec->dev, "DC servo state: %x\n", ret);
	} while (ret & mask);

	if (timeout == 0)
		dev_err(codec->dev, "DC servo timed out for %x\n", mask);
	else
		dev_dbg(codec->dev, "DC servo complete for %x\n", mask);
}

static void wm8996_seq_notifier(struct snd_soc_dapm_context *dapm,
				enum snd_soc_dapm_type event, int subseq)
{
	struct snd_soc_codec *codec = container_of(dapm,
						   struct snd_soc_codec, dapm);
	struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
	u16 val, mask;

	/* Complete any pending DC servo starts */
	if (wm8996->dcs_pending) {
		dev_dbg(codec->dev, "Starting DC servo for %x\n",
			wm8996->dcs_pending);

		/* Trigger a startup sequence */
		wait_for_dc_servo(codec, wm8996->dcs_pending
				         << WM8996_DCS_TRIG_STARTUP_0_SHIFT);

		wm8996->dcs_pending = 0;
	}

	if (wm8996->hpout_pending != wm8996->hpout_ena) {
		dev_dbg(codec->dev, "Applying RMV_SHORTs %x->%x\n",
			wm8996->hpout_ena, wm8996->hpout_pending);

		val = 0;
		mask = 0;
		if (wm8996->hpout_pending & HPOUT1L) {
			val |= WM8996_HPOUT1L_RMV_SHORT;
			mask |= WM8996_HPOUT1L_RMV_SHORT;
		} else {
			mask |= WM8996_HPOUT1L_RMV_SHORT |
				WM8996_HPOUT1L_OUTP |
				WM8996_HPOUT1L_DLY;
		}

		if (wm8996->hpout_pending & HPOUT1R) {
			val |= WM8996_HPOUT1R_RMV_SHORT;
			mask |= WM8996_HPOUT1R_RMV_SHORT;
		} else {
			mask |= WM8996_HPOUT1R_RMV_SHORT |
				WM8996_HPOUT1R_OUTP |
				WM8996_HPOUT1R_DLY;
		}

		snd_soc_update_bits(codec, WM8996_ANALOGUE_HP_1, mask, val);

		val = 0;
		mask = 0;
		if (wm8996->hpout_pending & HPOUT2L) {
			val |= WM8996_HPOUT2L_RMV_SHORT;
			mask |= WM8996_HPOUT2L_RMV_SHORT;
		} else {
			mask |= WM8996_HPOUT2L_RMV_SHORT |
				WM8996_HPOUT2L_OUTP |
				WM8996_HPOUT2L_DLY;
		}

		if (wm8996->hpout_pending & HPOUT2R) {
			val |= WM8996_HPOUT2R_RMV_SHORT;
			mask |= WM8996_HPOUT2R_RMV_SHORT;
		} else {
			mask |= WM8996_HPOUT2R_RMV_SHORT |
				WM8996_HPOUT2R_OUTP |
				WM8996_HPOUT2R_DLY;
		}

		snd_soc_update_bits(codec, WM8996_ANALOGUE_HP_2, mask, val);

		wm8996->hpout_ena = wm8996->hpout_pending;
	}
}

static int dcs_start(struct snd_soc_dapm_widget *w,
		     struct snd_kcontrol *kcontrol, int event)
{
	struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(w->codec);

	switch (event) {
	case SND_SOC_DAPM_POST_PMU:
		wm8996->dcs_pending |= 1 << w->shift;
		break;
	default:
		BUG();
		return -EINVAL;
	}

	return 0;
}

static const char *sidetone_text[] = {
	"IN1", "IN2",
};

static const struct soc_enum left_sidetone_enum =
	SOC_ENUM_SINGLE(WM8996_SIDETONE, 0, 2, sidetone_text);

static const struct snd_kcontrol_new left_sidetone =
	SOC_DAPM_ENUM("Left Sidetone", left_sidetone_enum);

static const struct soc_enum right_sidetone_enum =
	SOC_ENUM_SINGLE(WM8996_SIDETONE, 1, 2, sidetone_text);

static const struct snd_kcontrol_new right_sidetone =
	SOC_DAPM_ENUM("Right Sidetone", right_sidetone_enum);

static const char *spk_text[] = {
	"DAC1L", "DAC1R", "DAC2L", "DAC2R"
};

static const struct soc_enum spkl_enum =
	SOC_ENUM_SINGLE(WM8996_LEFT_PDM_SPEAKER, 0, 4, spk_text);

static const struct snd_kcontrol_new spkl_mux =
	SOC_DAPM_ENUM("SPKL", spkl_enum);

static const struct soc_enum spkr_enum =
	SOC_ENUM_SINGLE(WM8996_RIGHT_PDM_SPEAKER, 0, 4, spk_text);

static const struct snd_kcontrol_new spkr_mux =
	SOC_DAPM_ENUM("SPKR", spkr_enum);

static const char *dsp1rx_text[] = {
	"AIF1", "AIF2"
};

static const struct soc_enum dsp1rx_enum =
	SOC_ENUM_SINGLE(WM8996_POWER_MANAGEMENT_8, 0, 2, dsp1rx_text);

static const struct snd_kcontrol_new dsp1rx =
	SOC_DAPM_ENUM("DSP1RX", dsp1rx_enum);

static const char *dsp2rx_text[] = {
	 "AIF2", "AIF1"
};

static const struct soc_enum dsp2rx_enum =
	SOC_ENUM_SINGLE(WM8996_POWER_MANAGEMENT_8, 4, 2, dsp2rx_text);

static const struct snd_kcontrol_new dsp2rx =
	SOC_DAPM_ENUM("DSP2RX", dsp2rx_enum);

static const char *aif2tx_text[] = {
	"DSP2", "DSP1", "AIF1"
};

static const struct soc_enum aif2tx_enum =
	SOC_ENUM_SINGLE(WM8996_POWER_MANAGEMENT_8, 6, 3, aif2tx_text);

static const struct snd_kcontrol_new aif2tx =
	SOC_DAPM_ENUM("AIF2TX", aif2tx_enum);

static const char *inmux_text[] = {
	"ADC", "DMIC1", "DMIC2"
};

static const struct soc_enum in1_enum =
	SOC_ENUM_SINGLE(WM8996_POWER_MANAGEMENT_7, 0, 3, inmux_text);

static const struct snd_kcontrol_new in1_mux =
	SOC_DAPM_ENUM("IN1 Mux", in1_enum);

static const struct soc_enum in2_enum =
	SOC_ENUM_SINGLE(WM8996_POWER_MANAGEMENT_7, 4, 3, inmux_text);

static const struct snd_kcontrol_new in2_mux =
	SOC_DAPM_ENUM("IN2 Mux", in2_enum);

static const struct snd_kcontrol_new dac2r_mix[] = {
SOC_DAPM_SINGLE("Right Sidetone Switch", WM8996_DAC2_RIGHT_MIXER_ROUTING,
		5, 1, 0),
SOC_DAPM_SINGLE("Left Sidetone Switch", WM8996_DAC2_RIGHT_MIXER_ROUTING,
		4, 1, 0),
SOC_DAPM_SINGLE("DSP2 Switch", WM8996_DAC2_RIGHT_MIXER_ROUTING, 1, 1, 0),
SOC_DAPM_SINGLE("DSP1 Switch", WM8996_DAC2_RIGHT_MIXER_ROUTING, 0, 1, 0),
};

static const struct snd_kcontrol_new dac2l_mix[] = {
SOC_DAPM_SINGLE("Right Sidetone Switch", WM8996_DAC2_LEFT_MIXER_ROUTING,
		5, 1, 0),
SOC_DAPM_SINGLE("Left Sidetone Switch", WM8996_DAC2_LEFT_MIXER_ROUTING,
		4, 1, 0),
SOC_DAPM_SINGLE("DSP2 Switch", WM8996_DAC2_LEFT_MIXER_ROUTING, 1, 1, 0),
SOC_DAPM_SINGLE("DSP1 Switch", WM8996_DAC2_LEFT_MIXER_ROUTING, 0, 1, 0),
};

static const struct snd_kcontrol_new dac1r_mix[] = {
SOC_DAPM_SINGLE("Right Sidetone Switch", WM8996_DAC1_RIGHT_MIXER_ROUTING,
		5, 1, 0),
SOC_DAPM_SINGLE("Left Sidetone Switch", WM8996_DAC1_RIGHT_MIXER_ROUTING,
		4, 1, 0),
SOC_DAPM_SINGLE("DSP2 Switch", WM8996_DAC1_RIGHT_MIXER_ROUTING, 1, 1, 0),
SOC_DAPM_SINGLE("DSP1 Switch", WM8996_DAC1_RIGHT_MIXER_ROUTING, 0, 1, 0),
};

static const struct snd_kcontrol_new dac1l_mix[] = {
SOC_DAPM_SINGLE("Right Sidetone Switch", WM8996_DAC1_LEFT_MIXER_ROUTING,
		5, 1, 0),
SOC_DAPM_SINGLE("Left Sidetone Switch", WM8996_DAC1_LEFT_MIXER_ROUTING,
		4, 1, 0),
SOC_DAPM_SINGLE("DSP2 Switch", WM8996_DAC1_LEFT_MIXER_ROUTING, 1, 1, 0),
SOC_DAPM_SINGLE("DSP1 Switch", WM8996_DAC1_LEFT_MIXER_ROUTING, 0, 1, 0),
};

static const struct snd_kcontrol_new dsp1txl[] = {
SOC_DAPM_SINGLE("IN1 Switch", WM8996_DSP1_TX_LEFT_MIXER_ROUTING,
		1, 1, 0),
SOC_DAPM_SINGLE("DAC Switch", WM8996_DSP1_TX_LEFT_MIXER_ROUTING,
		0, 1, 0),
};

static const struct snd_kcontrol_new dsp1txr[] = {
SOC_DAPM_SINGLE("IN1 Switch", WM8996_DSP1_TX_RIGHT_MIXER_ROUTING,
		1, 1, 0),
SOC_DAPM_SINGLE("DAC Switch", WM8996_DSP1_TX_RIGHT_MIXER_ROUTING,
		0, 1, 0),
};

static const struct snd_kcontrol_new dsp2txl[] = {
SOC_DAPM_SINGLE("IN1 Switch", WM8996_DSP2_TX_LEFT_MIXER_ROUTING,
		1, 1, 0),
SOC_DAPM_SINGLE("DAC Switch", WM8996_DSP2_TX_LEFT_MIXER_ROUTING,
		0, 1, 0),
};

static const struct snd_kcontrol_new dsp2txr[] = {
SOC_DAPM_SINGLE("IN1 Switch", WM8996_DSP2_TX_RIGHT_MIXER_ROUTING,
		1, 1, 0),
SOC_DAPM_SINGLE("DAC Switch", WM8996_DSP2_TX_RIGHT_MIXER_ROUTING,
		0, 1, 0),
};


static const struct snd_soc_dapm_widget wm8996_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("IN1LN"),
SND_SOC_DAPM_INPUT("IN1LP"),
SND_SOC_DAPM_INPUT("IN1RN"),
SND_SOC_DAPM_INPUT("IN1RP"),

SND_SOC_DAPM_INPUT("IN2LN"),
SND_SOC_DAPM_INPUT("IN2LP"),
SND_SOC_DAPM_INPUT("IN2RN"),
SND_SOC_DAPM_INPUT("IN2RP"),

SND_SOC_DAPM_INPUT("DMIC1DAT"),
SND_SOC_DAPM_INPUT("DMIC2DAT"),

SND_SOC_DAPM_SUPPLY_S("SYSCLK", 1, WM8996_AIF_CLOCKING_1, 0, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("SYSDSPCLK", 2, WM8996_CLOCKING_1, 1, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("AIFCLK", 2, WM8996_CLOCKING_1, 2, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("Charge Pump", 2, WM8996_CHARGE_PUMP_1, 15, 0, cp_event,
		      SND_SOC_DAPM_POST_PMU),

SND_SOC_DAPM_SUPPLY("LDO2", WM8996_POWER_MANAGEMENT_2, 1, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("MICB1 Audio", WM8996_MICBIAS_1, 4, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("MICB2 Audio", WM8996_MICBIAS_2, 4, 1, NULL, 0),
SND_SOC_DAPM_MICBIAS("MICB2", WM8996_POWER_MANAGEMENT_1, 9, 0),
SND_SOC_DAPM_MICBIAS("MICB1", WM8996_POWER_MANAGEMENT_1, 8, 0),

SND_SOC_DAPM_PGA("IN1L PGA", WM8996_POWER_MANAGEMENT_2, 5, 0, NULL, 0),
SND_SOC_DAPM_PGA("IN1R PGA", WM8996_POWER_MANAGEMENT_2, 4, 0, NULL, 0),

SND_SOC_DAPM_MUX("IN1L Mux", SND_SOC_NOPM, 0, 0, &in1_mux),
SND_SOC_DAPM_MUX("IN1R Mux", SND_SOC_NOPM, 0, 0, &in1_mux),
SND_SOC_DAPM_MUX("IN2L Mux", SND_SOC_NOPM, 0, 0, &in2_mux),
SND_SOC_DAPM_MUX("IN2R Mux", SND_SOC_NOPM, 0, 0, &in2_mux),

SND_SOC_DAPM_PGA("IN1L", WM8996_POWER_MANAGEMENT_7, 2, 0, NULL, 0),
SND_SOC_DAPM_PGA("IN1R", WM8996_POWER_MANAGEMENT_7, 3, 0, NULL, 0),
SND_SOC_DAPM_PGA("IN2L", WM8996_POWER_MANAGEMENT_7, 6, 0, NULL, 0),
SND_SOC_DAPM_PGA("IN2R", WM8996_POWER_MANAGEMENT_7, 7, 0, NULL, 0),

SND_SOC_DAPM_SUPPLY("DMIC2", WM8996_POWER_MANAGEMENT_7, 9, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("DMIC1", WM8996_POWER_MANAGEMENT_7, 8, 0, NULL, 0),

SND_SOC_DAPM_ADC("DMIC2L", NULL, WM8996_POWER_MANAGEMENT_3, 5, 0),
SND_SOC_DAPM_ADC("DMIC2R", NULL, WM8996_POWER_MANAGEMENT_3, 4, 0),
SND_SOC_DAPM_ADC("DMIC1L", NULL, WM8996_POWER_MANAGEMENT_3, 3, 0),
SND_SOC_DAPM_ADC("DMIC1R", NULL, WM8996_POWER_MANAGEMENT_3, 2, 0),

SND_SOC_DAPM_ADC("ADCL", NULL, WM8996_POWER_MANAGEMENT_3, 1, 0),
SND_SOC_DAPM_ADC("ADCR", NULL, WM8996_POWER_MANAGEMENT_3, 0, 0),

SND_SOC_DAPM_MUX("Left Sidetone", SND_SOC_NOPM, 0, 0, &left_sidetone),
SND_SOC_DAPM_MUX("Right Sidetone", SND_SOC_NOPM, 0, 0, &right_sidetone),

SND_SOC_DAPM_AIF_IN("DSP2RXL", NULL, 0, WM8996_POWER_MANAGEMENT_3, 11, 0),
SND_SOC_DAPM_AIF_IN("DSP2RXR", NULL, 1, WM8996_POWER_MANAGEMENT_3, 10, 0),
SND_SOC_DAPM_AIF_IN("DSP1RXL", NULL, 0, WM8996_POWER_MANAGEMENT_3, 9, 0),
SND_SOC_DAPM_AIF_IN("DSP1RXR", NULL, 1, WM8996_POWER_MANAGEMENT_3, 8, 0),

SND_SOC_DAPM_MIXER("DSP2TXL", WM8996_POWER_MANAGEMENT_5, 11, 0,
		   dsp2txl, ARRAY_SIZE(dsp2txl)),
SND_SOC_DAPM_MIXER("DSP2TXR", WM8996_POWER_MANAGEMENT_5, 10, 0,
		   dsp2txr, ARRAY_SIZE(dsp2txr)),
SND_SOC_DAPM_MIXER("DSP1TXL", WM8996_POWER_MANAGEMENT_5, 9, 0,
		   dsp1txl, ARRAY_SIZE(dsp1txl)),
SND_SOC_DAPM_MIXER("DSP1TXR", WM8996_POWER_MANAGEMENT_5, 8, 0,
		   dsp1txr, ARRAY_SIZE(dsp1txr)),

SND_SOC_DAPM_MIXER("DAC2L Mixer", SND_SOC_NOPM, 0, 0,
		   dac2l_mix, ARRAY_SIZE(dac2l_mix)),
SND_SOC_DAPM_MIXER("DAC2R Mixer", SND_SOC_NOPM, 0, 0,
		   dac2r_mix, ARRAY_SIZE(dac2r_mix)),
SND_SOC_DAPM_MIXER("DAC1L Mixer", SND_SOC_NOPM, 0, 0,
		   dac1l_mix, ARRAY_SIZE(dac1l_mix)),
SND_SOC_DAPM_MIXER("DAC1R Mixer", SND_SOC_NOPM, 0, 0,
		   dac1r_mix, ARRAY_SIZE(dac1r_mix)),

SND_SOC_DAPM_DAC("DAC2L", NULL, WM8996_POWER_MANAGEMENT_5, 3, 0),
SND_SOC_DAPM_DAC("DAC2R", NULL, WM8996_POWER_MANAGEMENT_5, 2, 0),
SND_SOC_DAPM_DAC("DAC1L", NULL, WM8996_POWER_MANAGEMENT_5, 1, 0),
SND_SOC_DAPM_DAC("DAC1R", NULL, WM8996_POWER_MANAGEMENT_5, 0, 0),

SND_SOC_DAPM_AIF_IN("AIF2RX1", "AIF2 Playback", 1,
		    WM8996_POWER_MANAGEMENT_4, 9, 0),
SND_SOC_DAPM_AIF_IN("AIF2RX0", "AIF2 Playback", 2,
		    WM8996_POWER_MANAGEMENT_4, 8, 0),

SND_SOC_DAPM_AIF_IN("AIF2TX1", "AIF2 Capture", 1,
		    WM8996_POWER_MANAGEMENT_6, 9, 0),
SND_SOC_DAPM_AIF_IN("AIF2TX0", "AIF2 Capture", 2,
		    WM8996_POWER_MANAGEMENT_6, 8, 0),

SND_SOC_DAPM_AIF_IN("AIF1RX5", "AIF1 Playback", 5,
		    WM8996_POWER_MANAGEMENT_4, 5, 0),
SND_SOC_DAPM_AIF_IN("AIF1RX4", "AIF1 Playback", 4,
		    WM8996_POWER_MANAGEMENT_4, 4, 0),
SND_SOC_DAPM_AIF_IN("AIF1RX3", "AIF1 Playback", 3,
		    WM8996_POWER_MANAGEMENT_4, 3, 0),
SND_SOC_DAPM_AIF_IN("AIF1RX2", "AIF1 Playback", 2,
		    WM8996_POWER_MANAGEMENT_4, 2, 0),
SND_SOC_DAPM_AIF_IN("AIF1RX1", "AIF1 Playback", 1,
		    WM8996_POWER_MANAGEMENT_4, 1, 0),
SND_SOC_DAPM_AIF_IN("AIF1RX0", "AIF1 Playback", 0,
		    WM8996_POWER_MANAGEMENT_4, 0, 0),

SND_SOC_DAPM_AIF_OUT("AIF1TX5", "AIF1 Capture", 5,
		     WM8996_POWER_MANAGEMENT_6, 5, 0),
SND_SOC_DAPM_AIF_OUT("AIF1TX4", "AIF1 Capture", 4,
		     WM8996_POWER_MANAGEMENT_6, 4, 0),
SND_SOC_DAPM_AIF_OUT("AIF1TX3", "AIF1 Capture", 3,
		     WM8996_POWER_MANAGEMENT_6, 3, 0),
SND_SOC_DAPM_AIF_OUT("AIF1TX2", "AIF1 Capture", 2,
		     WM8996_POWER_MANAGEMENT_6, 2, 0),
SND_SOC_DAPM_AIF_OUT("AIF1TX1", "AIF1 Capture", 1,
		     WM8996_POWER_MANAGEMENT_6, 1, 0),
SND_SOC_DAPM_AIF_OUT("AIF1TX0", "AIF1 Capture", 0,
		     WM8996_POWER_MANAGEMENT_6, 0, 0),

/* We route as stereo pairs so define some dummy widgets to squash
 * things down for now.  RXA = 0,1, RXB = 2,3 and so on */
SND_SOC_DAPM_PGA("AIF1RXA", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("AIF1RXB", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("AIF1RXC", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("AIF2RX", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("DSP2TX", SND_SOC_NOPM, 0, 0, NULL, 0),

SND_SOC_DAPM_MUX("DSP1RX", SND_SOC_NOPM, 0, 0, &dsp1rx),
SND_SOC_DAPM_MUX("DSP2RX", SND_SOC_NOPM, 0, 0, &dsp2rx),
SND_SOC_DAPM_MUX("AIF2TX", SND_SOC_NOPM, 0, 0, &aif2tx),

SND_SOC_DAPM_MUX("SPKL", SND_SOC_NOPM, 0, 0, &spkl_mux),
SND_SOC_DAPM_MUX("SPKR", SND_SOC_NOPM, 0, 0, &spkr_mux),
SND_SOC_DAPM_PGA("SPKL PGA", WM8996_LEFT_PDM_SPEAKER, 4, 0, NULL, 0),
SND_SOC_DAPM_PGA("SPKR PGA", WM8996_RIGHT_PDM_SPEAKER, 4, 0, NULL, 0),

SND_SOC_DAPM_PGA_S("HPOUT2L PGA", 0, WM8996_POWER_MANAGEMENT_1, 7, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("HPOUT2L_DLY", 1, WM8996_ANALOGUE_HP_2, 5, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("HPOUT2L_DCS", 2, WM8996_DC_SERVO_1, 2, 0, dcs_start,
		   SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_S("HPOUT2L_OUTP", 3, WM8996_ANALOGUE_HP_2, 6, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("HPOUT2L_RMV_SHORT", 3, SND_SOC_NOPM, HPOUT2L, 0,
		   rmv_short_event,
		   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),

SND_SOC_DAPM_PGA_S("HPOUT2R PGA", 0, WM8996_POWER_MANAGEMENT_1, 6, 0,NULL, 0),
SND_SOC_DAPM_PGA_S("HPOUT2R_DLY", 1, WM8996_ANALOGUE_HP_2, 1, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("HPOUT2R_DCS", 2, WM8996_DC_SERVO_1, 3, 0, dcs_start,
		   SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_S("HPOUT2R_OUTP", 3, WM8996_ANALOGUE_HP_2, 2, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("HPOUT2R_RMV_SHORT", 3, SND_SOC_NOPM, HPOUT2R, 0,
		   rmv_short_event,
		   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),

SND_SOC_DAPM_PGA_S("HPOUT1L PGA", 0, WM8996_POWER_MANAGEMENT_1, 5, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("HPOUT1L_DLY", 1, WM8996_ANALOGUE_HP_1, 5, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("HPOUT1L_DCS", 2, WM8996_DC_SERVO_1, 0, 0, dcs_start,
		   SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_S("HPOUT1L_OUTP", 3, WM8996_ANALOGUE_HP_1, 6, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("HPOUT1L_RMV_SHORT", 3, SND_SOC_NOPM, HPOUT1L, 0,
		   rmv_short_event,
		   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),

SND_SOC_DAPM_PGA_S("HPOUT1R PGA", 0, WM8996_POWER_MANAGEMENT_1, 4, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("HPOUT1R_DLY", 1, WM8996_ANALOGUE_HP_1, 1, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("HPOUT1R_DCS", 2, WM8996_DC_SERVO_1, 1, 0, dcs_start,
		   SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_S("HPOUT1R_OUTP", 3, WM8996_ANALOGUE_HP_1, 2, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("HPOUT1R_RMV_SHORT", 3, SND_SOC_NOPM, HPOUT1R, 0,
		   rmv_short_event,
		   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),

SND_SOC_DAPM_OUTPUT("HPOUT1L"),
SND_SOC_DAPM_OUTPUT("HPOUT1R"),
SND_SOC_DAPM_OUTPUT("HPOUT2L"),
SND_SOC_DAPM_OUTPUT("HPOUT2R"),
SND_SOC_DAPM_OUTPUT("SPKDAT"),
};

static const struct snd_soc_dapm_route wm8996_dapm_routes[] = {
	{ "AIFCLK", NULL, "SYSCLK" },
	{ "SYSDSPCLK", NULL, "SYSCLK" },
	{ "Charge Pump", NULL, "SYSCLK" },

	{ "MICB1", NULL, "LDO2" },
	{ "MICB1", NULL, "MICB1 Audio" },
	{ "MICB2", NULL, "LDO2" },
	{ "MICB2", NULL, "MICB2 Audio" },

	{ "IN1L PGA", NULL, "IN2LN" },
	{ "IN1L PGA", NULL, "IN2LP" },
	{ "IN1L PGA", NULL, "IN1LN" },
	{ "IN1L PGA", NULL, "IN1LP" },

	{ "IN1R PGA", NULL, "IN2RN" },
	{ "IN1R PGA", NULL, "IN2RP" },
	{ "IN1R PGA", NULL, "IN1RN" },
	{ "IN1R PGA", NULL, "IN1RP" },

	{ "ADCL", NULL, "IN1L PGA" },

	{ "ADCR", NULL, "IN1R PGA" },

	{ "DMIC1L", NULL, "DMIC1DAT" },
	{ "DMIC1R", NULL, "DMIC1DAT" },
	{ "DMIC2L", NULL, "DMIC2DAT" },
	{ "DMIC2R", NULL, "DMIC2DAT" },

	{ "DMIC2L", NULL, "DMIC2" },
{'fraction_non_alphanumeric': 0.11708037675816267, 'fraction_numerical': 0.12565339259109617, 'mean_word_length': 6.475507314771119, 'pattern_counts': {'":': 0, '<': 67, '<?xml version=': 0, '>': 169, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2612194', 'n_tokens_mistral': 45605, 'n_tokens_neox': 40006, 'n_words': 6018}