| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * ALSA SoC TWL4030 codec driver | 
 | 3 |  * | 
 | 4 |  * Author:      Steve Sakoman, <steve@sakoman.com> | 
 | 5 |  * | 
 | 6 |  * This program is free software; you can redistribute it and/or | 
 | 7 |  * modify it under the terms of the GNU General Public License | 
 | 8 |  * version 2 as published by the Free Software Foundation. | 
 | 9 |  * | 
 | 10 |  * This program is distributed in the hope that it will be useful, but | 
 | 11 |  * WITHOUT ANY WARRANTY; without even the implied warranty of | 
 | 12 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
 | 13 |  * General Public License for more details. | 
 | 14 |  * | 
 | 15 |  * You should have received a copy of the GNU General Public License | 
 | 16 |  * along with this program; if not, write to the Free Software | 
 | 17 |  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | 
 | 18 |  * 02110-1301 USA | 
 | 19 |  * | 
 | 20 |  */ | 
 | 21 |  | 
 | 22 | #include <linux/module.h> | 
 | 23 | #include <linux/moduleparam.h> | 
 | 24 | #include <linux/init.h> | 
 | 25 | #include <linux/delay.h> | 
 | 26 | #include <linux/pm.h> | 
 | 27 | #include <linux/i2c.h> | 
 | 28 | #include <linux/platform_device.h> | 
| Andres Salomon | 0638d56 | 2011-02-17 19:07:20 -0800 | [diff] [blame] | 29 | #include <linux/mfd/core.h> | 
| Santosh Shilimkar | b07682b | 2009-12-13 20:05:51 +0100 | [diff] [blame] | 30 | #include <linux/i2c/twl.h> | 
| Tejun Heo | 5a0e3ad | 2010-03-24 17:04:11 +0900 | [diff] [blame] | 31 | #include <linux/slab.h> | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 32 | #include <sound/core.h> | 
 | 33 | #include <sound/pcm.h> | 
 | 34 | #include <sound/pcm_params.h> | 
 | 35 | #include <sound/soc.h> | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 36 | #include <sound/initval.h> | 
| Peter Ujfalusi | c10b82c | 2008-11-24 13:49:35 +0200 | [diff] [blame] | 37 | #include <sound/tlv.h> | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 38 |  | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 39 | /* Register descriptions are here */ | 
 | 40 | #include <linux/mfd/twl4030-codec.h> | 
 | 41 |  | 
 | 42 | /* Shadow register used by the audio driver */ | 
 | 43 | #define TWL4030_REG_SW_SHADOW		0x4A | 
 | 44 | #define TWL4030_CACHEREGNUM	(TWL4030_REG_SW_SHADOW + 1) | 
 | 45 |  | 
 | 46 | /* TWL4030_REG_SW_SHADOW (0x4A) Fields */ | 
 | 47 | #define TWL4030_HFL_EN			0x01 | 
 | 48 | #define TWL4030_HFR_EN			0x02 | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 49 |  | 
 | 50 | /* | 
 | 51 |  * twl4030 register cache & default register settings | 
 | 52 |  */ | 
 | 53 | static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = { | 
 | 54 | 	0x00, /* this register not used		*/ | 
| Peter Ujfalusi | 33f92ed | 2010-05-26 11:38:14 +0300 | [diff] [blame] | 55 | 	0x00, /* REG_CODEC_MODE		(0x1)	*/ | 
| Peter Ujfalusi | ee4ccac7 | 2010-05-26 11:38:17 +0300 | [diff] [blame] | 56 | 	0x00, /* REG_OPTION		(0x2)	*/ | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 57 | 	0x00, /* REG_UNKNOWN		(0x3)	*/ | 
 | 58 | 	0x00, /* REG_MICBIAS_CTL	(0x4)	*/ | 
| Peter Ujfalusi | 979bb1f | 2010-05-26 11:38:16 +0300 | [diff] [blame] | 59 | 	0x00, /* REG_ANAMICL		(0x5)	*/ | 
| Grazvydas Ignotas | 5920b45 | 2008-12-02 20:48:58 +0200 | [diff] [blame] | 60 | 	0x00, /* REG_ANAMICR		(0x6)	*/ | 
 | 61 | 	0x00, /* REG_AVADC_CTL		(0x7)	*/ | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 62 | 	0x00, /* REG_ADCMICSEL		(0x8)	*/ | 
 | 63 | 	0x00, /* REG_DIGMIXING		(0x9)	*/ | 
| Peter Ujfalusi | 33f92ed | 2010-05-26 11:38:14 +0300 | [diff] [blame] | 64 | 	0x0f, /* REG_ATXL1PGA		(0xA)	*/ | 
 | 65 | 	0x0f, /* REG_ATXR1PGA		(0xB)	*/ | 
 | 66 | 	0x0f, /* REG_AVTXL2PGA		(0xC)	*/ | 
 | 67 | 	0x0f, /* REG_AVTXR2PGA		(0xD)	*/ | 
| Peter Ujfalusi | c42a59e | 2010-02-09 15:24:04 +0200 | [diff] [blame] | 68 | 	0x00, /* REG_AUDIO_IF		(0xE)	*/ | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 69 | 	0x00, /* REG_VOICE_IF		(0xF)	*/ | 
| Peter Ujfalusi | 33f92ed | 2010-05-26 11:38:14 +0300 | [diff] [blame] | 70 | 	0x3f, /* REG_ARXR1PGA		(0x10)	*/ | 
 | 71 | 	0x3f, /* REG_ARXL1PGA		(0x11)	*/ | 
 | 72 | 	0x3f, /* REG_ARXR2PGA		(0x12)	*/ | 
 | 73 | 	0x3f, /* REG_ARXL2PGA		(0x13)	*/ | 
 | 74 | 	0x25, /* REG_VRXPGA		(0x14)	*/ | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 75 | 	0x00, /* REG_VSTPGA		(0x15)	*/ | 
 | 76 | 	0x00, /* REG_VRX2ARXPGA		(0x16)	*/ | 
| Peter Ujfalusi | c812459 | 2010-01-28 15:57:04 +0200 | [diff] [blame] | 77 | 	0x00, /* REG_AVDAC_CTL		(0x17)	*/ | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 78 | 	0x00, /* REG_ARX2VTXPGA		(0x18)	*/ | 
| Peter Ujfalusi | 33f92ed | 2010-05-26 11:38:14 +0300 | [diff] [blame] | 79 | 	0x32, /* REG_ARXL1_APGA_CTL	(0x19)	*/ | 
 | 80 | 	0x32, /* REG_ARXR1_APGA_CTL	(0x1A)	*/ | 
 | 81 | 	0x32, /* REG_ARXL2_APGA_CTL	(0x1B)	*/ | 
 | 82 | 	0x32, /* REG_ARXR2_APGA_CTL	(0x1C)	*/ | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 83 | 	0x00, /* REG_ATX2ARXPGA		(0x1D)	*/ | 
 | 84 | 	0x00, /* REG_BT_IF		(0x1E)	*/ | 
| Peter Ujfalusi | 33f92ed | 2010-05-26 11:38:14 +0300 | [diff] [blame] | 85 | 	0x55, /* REG_BTPGA		(0x1F)	*/ | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 86 | 	0x00, /* REG_BTSTPGA		(0x20)	*/ | 
 | 87 | 	0x00, /* REG_EAR_CTL		(0x21)	*/ | 
| Peter Ujfalusi | e47c796 | 2010-02-17 09:49:54 +0200 | [diff] [blame] | 88 | 	0x00, /* REG_HS_SEL		(0x22)	*/ | 
 | 89 | 	0x00, /* REG_HS_GAIN_SET	(0x23)	*/ | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 90 | 	0x00, /* REG_HS_POPN_SET	(0x24)	*/ | 
 | 91 | 	0x00, /* REG_PREDL_CTL		(0x25)	*/ | 
 | 92 | 	0x00, /* REG_PREDR_CTL		(0x26)	*/ | 
 | 93 | 	0x00, /* REG_PRECKL_CTL		(0x27)	*/ | 
 | 94 | 	0x00, /* REG_PRECKR_CTL		(0x28)	*/ | 
 | 95 | 	0x00, /* REG_HFL_CTL		(0x29)	*/ | 
 | 96 | 	0x00, /* REG_HFR_CTL		(0x2A)	*/ | 
| Peter Ujfalusi | 33f92ed | 2010-05-26 11:38:14 +0300 | [diff] [blame] | 97 | 	0x05, /* REG_ALC_CTL		(0x2B)	*/ | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 98 | 	0x00, /* REG_ALC_SET1		(0x2C)	*/ | 
 | 99 | 	0x00, /* REG_ALC_SET2		(0x2D)	*/ | 
 | 100 | 	0x00, /* REG_BOOST_CTL		(0x2E)	*/ | 
| Peter Ujfalusi | f8d05bd | 2008-11-24 08:25:45 +0200 | [diff] [blame] | 101 | 	0x00, /* REG_SOFTVOL_CTL	(0x2F)	*/ | 
| Peter Ujfalusi | 33f92ed | 2010-05-26 11:38:14 +0300 | [diff] [blame] | 102 | 	0x13, /* REG_DTMF_FREQSEL	(0x30)	*/ | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 103 | 	0x00, /* REG_DTMF_TONEXT1H	(0x31)	*/ | 
 | 104 | 	0x00, /* REG_DTMF_TONEXT1L	(0x32)	*/ | 
 | 105 | 	0x00, /* REG_DTMF_TONEXT2H	(0x33)	*/ | 
 | 106 | 	0x00, /* REG_DTMF_TONEXT2L	(0x34)	*/ | 
| Peter Ujfalusi | 33f92ed | 2010-05-26 11:38:14 +0300 | [diff] [blame] | 107 | 	0x79, /* REG_DTMF_TONOFF	(0x35)	*/ | 
 | 108 | 	0x11, /* REG_DTMF_WANONOFF	(0x36)	*/ | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 109 | 	0x00, /* REG_I2S_RX_SCRAMBLE_H	(0x37)	*/ | 
 | 110 | 	0x00, /* REG_I2S_RX_SCRAMBLE_M	(0x38)	*/ | 
 | 111 | 	0x00, /* REG_I2S_RX_SCRAMBLE_L	(0x39)	*/ | 
| Peter Ujfalusi | c812459 | 2010-01-28 15:57:04 +0200 | [diff] [blame] | 112 | 	0x06, /* REG_APLL_CTL		(0x3A)	*/ | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 113 | 	0x00, /* REG_DTMF_CTL		(0x3B)	*/ | 
| Peter Ujfalusi | 33f92ed | 2010-05-26 11:38:14 +0300 | [diff] [blame] | 114 | 	0x44, /* REG_DTMF_PGA_CTL2	(0x3C)	*/ | 
 | 115 | 	0x69, /* REG_DTMF_PGA_CTL1	(0x3D)	*/ | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 116 | 	0x00, /* REG_MISC_SET_1		(0x3E)	*/ | 
 | 117 | 	0x00, /* REG_PCMBTMUX		(0x3F)	*/ | 
 | 118 | 	0x00, /* not used		(0x40)	*/ | 
 | 119 | 	0x00, /* not used		(0x41)	*/ | 
 | 120 | 	0x00, /* not used		(0x42)	*/ | 
 | 121 | 	0x00, /* REG_RX_PATH_SEL	(0x43)	*/ | 
| Peter Ujfalusi | 33f92ed | 2010-05-26 11:38:14 +0300 | [diff] [blame] | 122 | 	0x32, /* REG_VDL_APGA_CTL	(0x44)	*/ | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 123 | 	0x00, /* REG_VIBRA_CTL		(0x45)	*/ | 
 | 124 | 	0x00, /* REG_VIBRA_SET		(0x46)	*/ | 
 | 125 | 	0x00, /* REG_VIBRA_PWM_SET	(0x47)	*/ | 
 | 126 | 	0x00, /* REG_ANAMIC_GAIN	(0x48)	*/ | 
 | 127 | 	0x00, /* REG_MISC_SET_2		(0x49)	*/ | 
| Peter Ujfalusi | f3b5d30 | 2009-05-25 11:12:12 +0300 | [diff] [blame] | 128 | 	0x00, /* REG_SW_SHADOW		(0x4A)	- Shadow, non HW register */ | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 129 | }; | 
 | 130 |  | 
| Peter Ujfalusi | 7393958 | 2009-01-29 14:57:50 +0200 | [diff] [blame] | 131 | /* codec private data */ | 
 | 132 | struct twl4030_priv { | 
| Peter Ujfalusi | 7a1fecf | 2009-10-22 13:26:48 +0300 | [diff] [blame] | 133 | 	struct snd_soc_codec codec; | 
 | 134 |  | 
| Peter Ujfalusi | 7393958 | 2009-01-29 14:57:50 +0200 | [diff] [blame] | 135 | 	unsigned int codec_powered; | 
| Peter Ujfalusi | 7b4c734 | 2010-04-29 10:58:08 +0300 | [diff] [blame] | 136 |  | 
 | 137 | 	/* reference counts of AIF/APLL users */ | 
| Peter Ujfalusi | 2845fa1 | 2009-10-28 10:57:05 +0200 | [diff] [blame] | 138 | 	unsigned int apll_enabled; | 
| Peter Ujfalusi | 7220b9f | 2009-03-27 10:39:08 +0200 | [diff] [blame] | 139 |  | 
 | 140 | 	struct snd_pcm_substream *master_substream; | 
 | 141 | 	struct snd_pcm_substream *slave_substream; | 
| Peter Ujfalusi | 6b87a91 | 2009-04-17 15:55:08 +0300 | [diff] [blame] | 142 |  | 
 | 143 | 	unsigned int configured; | 
 | 144 | 	unsigned int rate; | 
 | 145 | 	unsigned int sample_bits; | 
 | 146 | 	unsigned int channels; | 
| Peter Ujfalusi | 6943c92 | 2009-05-18 16:02:05 +0300 | [diff] [blame] | 147 |  | 
 | 148 | 	unsigned int sysclk; | 
 | 149 |  | 
| Peter Ujfalusi | c96907f | 2010-03-22 17:46:37 +0200 | [diff] [blame] | 150 | 	/* Output (with associated amp) states */ | 
 | 151 | 	u8 hsl_enabled, hsr_enabled; | 
 | 152 | 	u8 earpiece_enabled; | 
 | 153 | 	u8 predrivel_enabled, predriver_enabled; | 
 | 154 | 	u8 carkitl_enabled, carkitr_enabled; | 
| Peter Ujfalusi | 01ea6ba | 2010-07-20 15:49:09 +0300 | [diff] [blame] | 155 |  | 
 | 156 | 	/* Delay needed after enabling the digimic interface */ | 
 | 157 | 	unsigned int digimic_delay; | 
| Peter Ujfalusi | 7393958 | 2009-01-29 14:57:50 +0200 | [diff] [blame] | 158 | }; | 
 | 159 |  | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 160 | /* | 
 | 161 |  * read twl4030 register cache | 
 | 162 |  */ | 
 | 163 | static inline unsigned int twl4030_read_reg_cache(struct snd_soc_codec *codec, | 
 | 164 | 	unsigned int reg) | 
 | 165 | { | 
| Takashi Iwai | d08664f | 2009-06-04 09:58:18 +0200 | [diff] [blame] | 166 | 	u8 *cache = codec->reg_cache; | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 167 |  | 
| Ian Molton | 91432e9 | 2009-01-17 17:44:23 +0000 | [diff] [blame] | 168 | 	if (reg >= TWL4030_CACHEREGNUM) | 
 | 169 | 		return -EIO; | 
 | 170 |  | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 171 | 	return cache[reg]; | 
 | 172 | } | 
 | 173 |  | 
 | 174 | /* | 
 | 175 |  * write twl4030 register cache | 
 | 176 |  */ | 
 | 177 | static inline void twl4030_write_reg_cache(struct snd_soc_codec *codec, | 
 | 178 | 						u8 reg, u8 value) | 
 | 179 | { | 
 | 180 | 	u8 *cache = codec->reg_cache; | 
 | 181 |  | 
 | 182 | 	if (reg >= TWL4030_CACHEREGNUM) | 
 | 183 | 		return; | 
 | 184 | 	cache[reg] = value; | 
 | 185 | } | 
 | 186 |  | 
 | 187 | /* | 
 | 188 |  * write to the twl4030 register space | 
 | 189 |  */ | 
 | 190 | static int twl4030_write(struct snd_soc_codec *codec, | 
 | 191 | 			unsigned int reg, unsigned int value) | 
 | 192 | { | 
| Mark Brown | b2c812e | 2010-04-14 15:35:19 +0900 | [diff] [blame] | 193 | 	struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); | 
| Peter Ujfalusi | c96907f | 2010-03-22 17:46:37 +0200 | [diff] [blame] | 194 | 	int write_to_reg = 0; | 
 | 195 |  | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 196 | 	twl4030_write_reg_cache(codec, reg, value); | 
| Peter Ujfalusi | c96907f | 2010-03-22 17:46:37 +0200 | [diff] [blame] | 197 | 	if (likely(reg < TWL4030_REG_SW_SHADOW)) { | 
 | 198 | 		/* Decide if the given register can be written */ | 
 | 199 | 		switch (reg) { | 
 | 200 | 		case TWL4030_REG_EAR_CTL: | 
 | 201 | 			if (twl4030->earpiece_enabled) | 
 | 202 | 				write_to_reg = 1; | 
 | 203 | 			break; | 
 | 204 | 		case TWL4030_REG_PREDL_CTL: | 
 | 205 | 			if (twl4030->predrivel_enabled) | 
 | 206 | 				write_to_reg = 1; | 
 | 207 | 			break; | 
 | 208 | 		case TWL4030_REG_PREDR_CTL: | 
 | 209 | 			if (twl4030->predriver_enabled) | 
 | 210 | 				write_to_reg = 1; | 
 | 211 | 			break; | 
 | 212 | 		case TWL4030_REG_PRECKL_CTL: | 
 | 213 | 			if (twl4030->carkitl_enabled) | 
 | 214 | 				write_to_reg = 1; | 
 | 215 | 			break; | 
 | 216 | 		case TWL4030_REG_PRECKR_CTL: | 
 | 217 | 			if (twl4030->carkitr_enabled) | 
 | 218 | 				write_to_reg = 1; | 
 | 219 | 			break; | 
 | 220 | 		case TWL4030_REG_HS_GAIN_SET: | 
 | 221 | 			if (twl4030->hsl_enabled || twl4030->hsr_enabled) | 
 | 222 | 				write_to_reg = 1; | 
 | 223 | 			break; | 
 | 224 | 		default: | 
 | 225 | 			/* All other register can be written */ | 
 | 226 | 			write_to_reg = 1; | 
 | 227 | 			break; | 
 | 228 | 		} | 
 | 229 | 		if (write_to_reg) | 
 | 230 | 			return twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, | 
 | 231 | 						    value, reg); | 
 | 232 | 	} | 
 | 233 | 	return 0; | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 234 | } | 
 | 235 |  | 
| Peter Ujfalusi | 7e6120c | 2010-10-25 11:34:23 +0300 | [diff] [blame] | 236 | static inline void twl4030_wait_ms(int time) | 
 | 237 | { | 
 | 238 | 	if (time < 60) { | 
 | 239 | 		time *= 1000; | 
 | 240 | 		usleep_range(time, time + 500); | 
 | 241 | 	} else { | 
 | 242 | 		msleep(time); | 
 | 243 | 	} | 
 | 244 | } | 
 | 245 |  | 
| Peter Ujfalusi | db04e2c | 2009-01-27 11:29:40 +0200 | [diff] [blame] | 246 | static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable) | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 247 | { | 
| Mark Brown | b2c812e | 2010-04-14 15:35:19 +0900 | [diff] [blame] | 248 | 	struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); | 
| Peter Ujfalusi | 7a1fecf | 2009-10-22 13:26:48 +0300 | [diff] [blame] | 249 | 	int mode; | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 250 |  | 
| Peter Ujfalusi | 7393958 | 2009-01-29 14:57:50 +0200 | [diff] [blame] | 251 | 	if (enable == twl4030->codec_powered) | 
 | 252 | 		return; | 
 | 253 |  | 
| Peter Ujfalusi | db04e2c | 2009-01-27 11:29:40 +0200 | [diff] [blame] | 254 | 	if (enable) | 
| Peter Ujfalusi | 7a1fecf | 2009-10-22 13:26:48 +0300 | [diff] [blame] | 255 | 		mode = twl4030_codec_enable_resource(TWL4030_CODEC_RES_POWER); | 
| Peter Ujfalusi | db04e2c | 2009-01-27 11:29:40 +0200 | [diff] [blame] | 256 | 	else | 
| Peter Ujfalusi | 7a1fecf | 2009-10-22 13:26:48 +0300 | [diff] [blame] | 257 | 		mode = twl4030_codec_disable_resource(TWL4030_CODEC_RES_POWER); | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 258 |  | 
| Peter Ujfalusi | 7a1fecf | 2009-10-22 13:26:48 +0300 | [diff] [blame] | 259 | 	if (mode >= 0) { | 
 | 260 | 		twl4030_write_reg_cache(codec, TWL4030_REG_CODEC_MODE, mode); | 
 | 261 | 		twl4030->codec_powered = enable; | 
 | 262 | 	} | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 263 |  | 
 | 264 | 	/* REVISIT: this delay is present in TI sample drivers */ | 
 | 265 | 	/* but there seems to be no TRM requirement for it     */ | 
 | 266 | 	udelay(10); | 
 | 267 | } | 
 | 268 |  | 
| Peter Ujfalusi | 9fdcc0f | 2010-05-26 11:38:18 +0300 | [diff] [blame] | 269 | static inline void twl4030_check_defaults(struct snd_soc_codec *codec) | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 270 | { | 
| Peter Ujfalusi | 9fdcc0f | 2010-05-26 11:38:18 +0300 | [diff] [blame] | 271 | 	int i, difference = 0; | 
 | 272 | 	u8 val; | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 273 |  | 
| Peter Ujfalusi | 9fdcc0f | 2010-05-26 11:38:18 +0300 | [diff] [blame] | 274 | 	dev_dbg(codec->dev, "Checking TWL audio default configuration\n"); | 
 | 275 | 	for (i = 1; i <= TWL4030_REG_MISC_SET_2; i++) { | 
 | 276 | 		twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val, i); | 
 | 277 | 		if (val != twl4030_reg[i]) { | 
 | 278 | 			difference++; | 
 | 279 | 			dev_dbg(codec->dev, | 
 | 280 | 				 "Reg 0x%02x: chip: 0x%02x driver: 0x%02x\n", | 
 | 281 | 				 i, val, twl4030_reg[i]); | 
 | 282 | 		} | 
 | 283 | 	} | 
 | 284 | 	dev_dbg(codec->dev, "Found %d non maching registers. %s\n", | 
 | 285 | 		 difference, difference ? "Not OK" : "OK"); | 
 | 286 | } | 
 | 287 |  | 
| Peter Ujfalusi | a3a29b5 | 2010-05-26 11:38:21 +0300 | [diff] [blame] | 288 | static inline void twl4030_reset_registers(struct snd_soc_codec *codec) | 
 | 289 | { | 
 | 290 | 	int i; | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 291 |  | 
 | 292 | 	/* set all audio section registers to reasonable defaults */ | 
 | 293 | 	for (i = TWL4030_REG_OPTION; i <= TWL4030_REG_MISC_SET_2; i++) | 
| Peter Ujfalusi | 68d0195 | 2009-11-04 09:58:20 +0200 | [diff] [blame] | 294 | 		if (i != TWL4030_REG_APLL_CTL) | 
| Peter Ujfalusi | a3a29b5 | 2010-05-26 11:38:21 +0300 | [diff] [blame] | 295 | 			twl4030_write(codec, i, twl4030_reg[i]); | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 296 |  | 
 | 297 | } | 
 | 298 |  | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 299 | static void twl4030_init_chip(struct snd_soc_codec *codec) | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 300 | { | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 301 | 	struct twl4030_codec_audio_data *pdata = dev_get_platdata(codec->dev); | 
| Peter Ujfalusi | ee4ccac7 | 2010-05-26 11:38:17 +0300 | [diff] [blame] | 302 | 	struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); | 
 | 303 | 	u8 reg, byte; | 
 | 304 | 	int i = 0; | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 305 |  | 
| Peter Ujfalusi | 9fdcc0f | 2010-05-26 11:38:18 +0300 | [diff] [blame] | 306 | 	/* Check defaults, if instructed before anything else */ | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 307 | 	if (pdata && pdata->check_defaults) | 
| Peter Ujfalusi | 9fdcc0f | 2010-05-26 11:38:18 +0300 | [diff] [blame] | 308 | 		twl4030_check_defaults(codec); | 
 | 309 |  | 
| Peter Ujfalusi | a3a29b5 | 2010-05-26 11:38:21 +0300 | [diff] [blame] | 310 | 	/* Reset registers, if no setup data or if instructed to do so */ | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 311 | 	if (!pdata || (pdata && pdata->reset_registers)) | 
| Peter Ujfalusi | a3a29b5 | 2010-05-26 11:38:21 +0300 | [diff] [blame] | 312 | 		twl4030_reset_registers(codec); | 
 | 313 |  | 
| Peter Ujfalusi | ee4ccac7 | 2010-05-26 11:38:17 +0300 | [diff] [blame] | 314 | 	/* Refresh APLL_CTL register from HW */ | 
| Peter Ujfalusi | 9fdcc0f | 2010-05-26 11:38:18 +0300 | [diff] [blame] | 315 | 	twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte, | 
| Peter Ujfalusi | ee4ccac7 | 2010-05-26 11:38:17 +0300 | [diff] [blame] | 316 | 			    TWL4030_REG_APLL_CTL); | 
 | 317 | 	twl4030_write_reg_cache(codec, TWL4030_REG_APLL_CTL, byte); | 
 | 318 |  | 
 | 319 | 	/* anti-pop when changing analog gain */ | 
 | 320 | 	reg = twl4030_read_reg_cache(codec, TWL4030_REG_MISC_SET_1); | 
 | 321 | 	twl4030_write(codec, TWL4030_REG_MISC_SET_1, | 
 | 322 | 		reg | TWL4030_SMOOTH_ANAVOL_EN); | 
 | 323 |  | 
 | 324 | 	twl4030_write(codec, TWL4030_REG_OPTION, | 
 | 325 | 		TWL4030_ATXL1_EN | TWL4030_ATXR1_EN | | 
 | 326 | 		TWL4030_ARXL2_EN | TWL4030_ARXR2_EN); | 
 | 327 |  | 
| Peter Ujfalusi | 3c36cc6 | 2010-05-26 11:38:19 +0300 | [diff] [blame] | 328 | 	/* REG_ARXR2_APGA_CTL reset according to the TRM: 0dB, DA_EN */ | 
 | 329 | 	twl4030_write(codec, TWL4030_REG_ARXR2_APGA_CTL, 0x32); | 
 | 330 |  | 
| Peter Ujfalusi | ee4ccac7 | 2010-05-26 11:38:17 +0300 | [diff] [blame] | 331 | 	/* Machine dependent setup */ | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 332 | 	if (!pdata) | 
| Peter Ujfalusi | ee4ccac7 | 2010-05-26 11:38:17 +0300 | [diff] [blame] | 333 | 		return; | 
 | 334 |  | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 335 | 	twl4030->digimic_delay = pdata->digimic_delay; | 
| Peter Ujfalusi | ee4ccac7 | 2010-05-26 11:38:17 +0300 | [diff] [blame] | 336 |  | 
 | 337 | 	reg = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET); | 
 | 338 | 	reg &= ~TWL4030_RAMP_DELAY; | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 339 | 	reg |= (pdata->ramp_delay_value << 2); | 
| Peter Ujfalusi | ee4ccac7 | 2010-05-26 11:38:17 +0300 | [diff] [blame] | 340 | 	twl4030_write_reg_cache(codec, TWL4030_REG_HS_POPN_SET, reg); | 
 | 341 |  | 
 | 342 | 	/* initiate offset cancellation */ | 
 | 343 | 	twl4030_codec_enable(codec, 1); | 
 | 344 |  | 
 | 345 | 	reg = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL); | 
 | 346 | 	reg &= ~TWL4030_OFFSET_CNCL_SEL; | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 347 | 	reg |= pdata->offset_cncl_path; | 
| Peter Ujfalusi | ee4ccac7 | 2010-05-26 11:38:17 +0300 | [diff] [blame] | 348 | 	twl4030_write(codec, TWL4030_REG_ANAMICL, | 
 | 349 | 		reg | TWL4030_CNCL_OFFSET_START); | 
 | 350 |  | 
| Peter Ujfalusi | 7e6120c | 2010-10-25 11:34:23 +0300 | [diff] [blame] | 351 | 	/* | 
 | 352 | 	 * Wait for offset cancellation to complete. | 
 | 353 | 	 * Since this takes a while, do not slam the i2c. | 
 | 354 | 	 * Start polling the status after ~20ms. | 
 | 355 | 	 */ | 
 | 356 | 	msleep(20); | 
| Peter Ujfalusi | ee4ccac7 | 2010-05-26 11:38:17 +0300 | [diff] [blame] | 357 | 	do { | 
| Peter Ujfalusi | 7e6120c | 2010-10-25 11:34:23 +0300 | [diff] [blame] | 358 | 		usleep_range(1000, 2000); | 
| Peter Ujfalusi | ee4ccac7 | 2010-05-26 11:38:17 +0300 | [diff] [blame] | 359 | 		twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte, | 
 | 360 | 				    TWL4030_REG_ANAMICL); | 
 | 361 | 	} while ((i++ < 100) && | 
 | 362 | 		 ((byte & TWL4030_CNCL_OFFSET_START) == | 
 | 363 | 		  TWL4030_CNCL_OFFSET_START)); | 
 | 364 |  | 
 | 365 | 	/* Make sure that the reg_cache has the same value as the HW */ | 
 | 366 | 	twl4030_write_reg_cache(codec, TWL4030_REG_ANAMICL, byte); | 
 | 367 |  | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 368 | 	twl4030_codec_enable(codec, 0); | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 369 | } | 
 | 370 |  | 
| Peter Ujfalusi | 2845fa1 | 2009-10-28 10:57:05 +0200 | [diff] [blame] | 371 | static void twl4030_apll_enable(struct snd_soc_codec *codec, int enable) | 
| Peter Ujfalusi | 7393958 | 2009-01-29 14:57:50 +0200 | [diff] [blame] | 372 | { | 
| Mark Brown | b2c812e | 2010-04-14 15:35:19 +0900 | [diff] [blame] | 373 | 	struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); | 
| Peter Ujfalusi | 7b4c734 | 2010-04-29 10:58:08 +0300 | [diff] [blame] | 374 | 	int status = -1; | 
| Peter Ujfalusi | 7393958 | 2009-01-29 14:57:50 +0200 | [diff] [blame] | 375 |  | 
| Peter Ujfalusi | 7b4c734 | 2010-04-29 10:58:08 +0300 | [diff] [blame] | 376 | 	if (enable) { | 
 | 377 | 		twl4030->apll_enabled++; | 
 | 378 | 		if (twl4030->apll_enabled == 1) | 
 | 379 | 			status = twl4030_codec_enable_resource( | 
 | 380 | 							TWL4030_CODEC_RES_APLL); | 
 | 381 | 	} else { | 
 | 382 | 		twl4030->apll_enabled--; | 
 | 383 | 		if (!twl4030->apll_enabled) | 
 | 384 | 			status = twl4030_codec_disable_resource( | 
 | 385 | 							TWL4030_CODEC_RES_APLL); | 
 | 386 | 	} | 
| Peter Ujfalusi | 7a1fecf | 2009-10-22 13:26:48 +0300 | [diff] [blame] | 387 |  | 
 | 388 | 	if (status >= 0) | 
 | 389 | 		twl4030_write_reg_cache(codec, TWL4030_REG_APLL_CTL, status); | 
| Peter Ujfalusi | 7393958 | 2009-01-29 14:57:50 +0200 | [diff] [blame] | 390 | } | 
 | 391 |  | 
| Peter Ujfalusi | 5e98a46 | 2008-12-09 12:35:47 +0200 | [diff] [blame] | 392 | /* Earpiece */ | 
| Joonyoung Shim | 1a787e7 | 2009-04-22 13:13:34 +0900 | [diff] [blame] | 393 | static const struct snd_kcontrol_new twl4030_dapm_earpiece_controls[] = { | 
 | 394 | 	SOC_DAPM_SINGLE("Voice", TWL4030_REG_EAR_CTL, 0, 1, 0), | 
 | 395 | 	SOC_DAPM_SINGLE("AudioL1", TWL4030_REG_EAR_CTL, 1, 1, 0), | 
 | 396 | 	SOC_DAPM_SINGLE("AudioL2", TWL4030_REG_EAR_CTL, 2, 1, 0), | 
 | 397 | 	SOC_DAPM_SINGLE("AudioR1", TWL4030_REG_EAR_CTL, 3, 1, 0), | 
 | 398 | }; | 
| Peter Ujfalusi | 5e98a46 | 2008-12-09 12:35:47 +0200 | [diff] [blame] | 399 |  | 
| Peter Ujfalusi | 2a6f5c58 | 2008-12-09 12:35:48 +0200 | [diff] [blame] | 400 | /* PreDrive Left */ | 
| Joonyoung Shim | 1a787e7 | 2009-04-22 13:13:34 +0900 | [diff] [blame] | 401 | static const struct snd_kcontrol_new twl4030_dapm_predrivel_controls[] = { | 
 | 402 | 	SOC_DAPM_SINGLE("Voice", TWL4030_REG_PREDL_CTL, 0, 1, 0), | 
 | 403 | 	SOC_DAPM_SINGLE("AudioL1", TWL4030_REG_PREDL_CTL, 1, 1, 0), | 
 | 404 | 	SOC_DAPM_SINGLE("AudioL2", TWL4030_REG_PREDL_CTL, 2, 1, 0), | 
 | 405 | 	SOC_DAPM_SINGLE("AudioR2", TWL4030_REG_PREDL_CTL, 3, 1, 0), | 
 | 406 | }; | 
| Peter Ujfalusi | 2a6f5c58 | 2008-12-09 12:35:48 +0200 | [diff] [blame] | 407 |  | 
 | 408 | /* PreDrive Right */ | 
| Joonyoung Shim | 1a787e7 | 2009-04-22 13:13:34 +0900 | [diff] [blame] | 409 | static const struct snd_kcontrol_new twl4030_dapm_predriver_controls[] = { | 
 | 410 | 	SOC_DAPM_SINGLE("Voice", TWL4030_REG_PREDR_CTL, 0, 1, 0), | 
 | 411 | 	SOC_DAPM_SINGLE("AudioR1", TWL4030_REG_PREDR_CTL, 1, 1, 0), | 
 | 412 | 	SOC_DAPM_SINGLE("AudioR2", TWL4030_REG_PREDR_CTL, 2, 1, 0), | 
 | 413 | 	SOC_DAPM_SINGLE("AudioL2", TWL4030_REG_PREDR_CTL, 3, 1, 0), | 
 | 414 | }; | 
| Peter Ujfalusi | 2a6f5c58 | 2008-12-09 12:35:48 +0200 | [diff] [blame] | 415 |  | 
| Peter Ujfalusi | dfad21a | 2008-12-09 12:35:49 +0200 | [diff] [blame] | 416 | /* Headset Left */ | 
| Joonyoung Shim | 1a787e7 | 2009-04-22 13:13:34 +0900 | [diff] [blame] | 417 | static const struct snd_kcontrol_new twl4030_dapm_hsol_controls[] = { | 
 | 418 | 	SOC_DAPM_SINGLE("Voice", TWL4030_REG_HS_SEL, 0, 1, 0), | 
 | 419 | 	SOC_DAPM_SINGLE("AudioL1", TWL4030_REG_HS_SEL, 1, 1, 0), | 
 | 420 | 	SOC_DAPM_SINGLE("AudioL2", TWL4030_REG_HS_SEL, 2, 1, 0), | 
 | 421 | }; | 
| Peter Ujfalusi | dfad21a | 2008-12-09 12:35:49 +0200 | [diff] [blame] | 422 |  | 
 | 423 | /* Headset Right */ | 
| Joonyoung Shim | 1a787e7 | 2009-04-22 13:13:34 +0900 | [diff] [blame] | 424 | static const struct snd_kcontrol_new twl4030_dapm_hsor_controls[] = { | 
 | 425 | 	SOC_DAPM_SINGLE("Voice", TWL4030_REG_HS_SEL, 3, 1, 0), | 
 | 426 | 	SOC_DAPM_SINGLE("AudioR1", TWL4030_REG_HS_SEL, 4, 1, 0), | 
 | 427 | 	SOC_DAPM_SINGLE("AudioR2", TWL4030_REG_HS_SEL, 5, 1, 0), | 
 | 428 | }; | 
| Peter Ujfalusi | dfad21a | 2008-12-09 12:35:49 +0200 | [diff] [blame] | 429 |  | 
| Peter Ujfalusi | 5152d8c | 2008-12-09 12:35:50 +0200 | [diff] [blame] | 430 | /* Carkit Left */ | 
| Joonyoung Shim | 1a787e7 | 2009-04-22 13:13:34 +0900 | [diff] [blame] | 431 | static const struct snd_kcontrol_new twl4030_dapm_carkitl_controls[] = { | 
 | 432 | 	SOC_DAPM_SINGLE("Voice", TWL4030_REG_PRECKL_CTL, 0, 1, 0), | 
 | 433 | 	SOC_DAPM_SINGLE("AudioL1", TWL4030_REG_PRECKL_CTL, 1, 1, 0), | 
 | 434 | 	SOC_DAPM_SINGLE("AudioL2", TWL4030_REG_PRECKL_CTL, 2, 1, 0), | 
 | 435 | }; | 
| Peter Ujfalusi | 5152d8c | 2008-12-09 12:35:50 +0200 | [diff] [blame] | 436 |  | 
 | 437 | /* Carkit Right */ | 
| Joonyoung Shim | 1a787e7 | 2009-04-22 13:13:34 +0900 | [diff] [blame] | 438 | static const struct snd_kcontrol_new twl4030_dapm_carkitr_controls[] = { | 
 | 439 | 	SOC_DAPM_SINGLE("Voice", TWL4030_REG_PRECKR_CTL, 0, 1, 0), | 
 | 440 | 	SOC_DAPM_SINGLE("AudioR1", TWL4030_REG_PRECKR_CTL, 1, 1, 0), | 
 | 441 | 	SOC_DAPM_SINGLE("AudioR2", TWL4030_REG_PRECKR_CTL, 2, 1, 0), | 
 | 442 | }; | 
| Peter Ujfalusi | 5152d8c | 2008-12-09 12:35:50 +0200 | [diff] [blame] | 443 |  | 
| Peter Ujfalusi | df33980 | 2008-12-09 12:35:51 +0200 | [diff] [blame] | 444 | /* Handsfree Left */ | 
 | 445 | static const char *twl4030_handsfreel_texts[] = | 
| Joonyoung Shim | 1a787e7 | 2009-04-22 13:13:34 +0900 | [diff] [blame] | 446 | 		{"Voice", "AudioL1", "AudioL2", "AudioR2"}; | 
| Peter Ujfalusi | df33980 | 2008-12-09 12:35:51 +0200 | [diff] [blame] | 447 |  | 
 | 448 | static const struct soc_enum twl4030_handsfreel_enum = | 
 | 449 | 	SOC_ENUM_SINGLE(TWL4030_REG_HFL_CTL, 0, | 
 | 450 | 			ARRAY_SIZE(twl4030_handsfreel_texts), | 
 | 451 | 			twl4030_handsfreel_texts); | 
 | 452 |  | 
 | 453 | static const struct snd_kcontrol_new twl4030_dapm_handsfreel_control = | 
 | 454 | SOC_DAPM_ENUM("Route", twl4030_handsfreel_enum); | 
 | 455 |  | 
| Peter Ujfalusi | 0f89bdc | 2009-05-25 11:12:13 +0300 | [diff] [blame] | 456 | /* Handsfree Left virtual mute */ | 
 | 457 | static const struct snd_kcontrol_new twl4030_dapm_handsfreelmute_control = | 
 | 458 | 	SOC_DAPM_SINGLE("Switch", TWL4030_REG_SW_SHADOW, 0, 1, 0); | 
 | 459 |  | 
| Peter Ujfalusi | df33980 | 2008-12-09 12:35:51 +0200 | [diff] [blame] | 460 | /* Handsfree Right */ | 
 | 461 | static const char *twl4030_handsfreer_texts[] = | 
| Joonyoung Shim | 1a787e7 | 2009-04-22 13:13:34 +0900 | [diff] [blame] | 462 | 		{"Voice", "AudioR1", "AudioR2", "AudioL2"}; | 
| Peter Ujfalusi | df33980 | 2008-12-09 12:35:51 +0200 | [diff] [blame] | 463 |  | 
 | 464 | static const struct soc_enum twl4030_handsfreer_enum = | 
 | 465 | 	SOC_ENUM_SINGLE(TWL4030_REG_HFR_CTL, 0, | 
 | 466 | 			ARRAY_SIZE(twl4030_handsfreer_texts), | 
 | 467 | 			twl4030_handsfreer_texts); | 
 | 468 |  | 
 | 469 | static const struct snd_kcontrol_new twl4030_dapm_handsfreer_control = | 
 | 470 | SOC_DAPM_ENUM("Route", twl4030_handsfreer_enum); | 
 | 471 |  | 
| Peter Ujfalusi | 0f89bdc | 2009-05-25 11:12:13 +0300 | [diff] [blame] | 472 | /* Handsfree Right virtual mute */ | 
 | 473 | static const struct snd_kcontrol_new twl4030_dapm_handsfreermute_control = | 
 | 474 | 	SOC_DAPM_SINGLE("Switch", TWL4030_REG_SW_SHADOW, 1, 1, 0); | 
 | 475 |  | 
| Peter Ujfalusi | 376f783 | 2009-05-05 08:55:47 +0300 | [diff] [blame] | 476 | /* Vibra */ | 
 | 477 | /* Vibra audio path selection */ | 
 | 478 | static const char *twl4030_vibra_texts[] = | 
 | 479 | 		{"AudioL1", "AudioR1", "AudioL2", "AudioR2"}; | 
 | 480 |  | 
 | 481 | static const struct soc_enum twl4030_vibra_enum = | 
 | 482 | 	SOC_ENUM_SINGLE(TWL4030_REG_VIBRA_CTL, 2, | 
 | 483 | 			ARRAY_SIZE(twl4030_vibra_texts), | 
 | 484 | 			twl4030_vibra_texts); | 
 | 485 |  | 
 | 486 | static const struct snd_kcontrol_new twl4030_dapm_vibra_control = | 
 | 487 | SOC_DAPM_ENUM("Route", twl4030_vibra_enum); | 
 | 488 |  | 
 | 489 | /* Vibra path selection: local vibrator (PWM) or audio driven */ | 
 | 490 | static const char *twl4030_vibrapath_texts[] = | 
 | 491 | 		{"Local vibrator", "Audio"}; | 
 | 492 |  | 
 | 493 | static const struct soc_enum twl4030_vibrapath_enum = | 
 | 494 | 	SOC_ENUM_SINGLE(TWL4030_REG_VIBRA_CTL, 4, | 
 | 495 | 			ARRAY_SIZE(twl4030_vibrapath_texts), | 
 | 496 | 			twl4030_vibrapath_texts); | 
 | 497 |  | 
 | 498 | static const struct snd_kcontrol_new twl4030_dapm_vibrapath_control = | 
 | 499 | SOC_DAPM_ENUM("Route", twl4030_vibrapath_enum); | 
 | 500 |  | 
| Peter Ujfalusi | 276c622 | 2008-12-31 10:08:38 +0200 | [diff] [blame] | 501 | /* Left analog microphone selection */ | 
| Joonyoung Shim | 97b8096 | 2009-05-11 20:36:08 +0900 | [diff] [blame] | 502 | static const struct snd_kcontrol_new twl4030_dapm_analoglmic_controls[] = { | 
| Peter Ujfalusi | 9028935 | 2009-08-14 08:44:00 +0300 | [diff] [blame] | 503 | 	SOC_DAPM_SINGLE("Main Mic Capture Switch", | 
 | 504 | 			TWL4030_REG_ANAMICL, 0, 1, 0), | 
 | 505 | 	SOC_DAPM_SINGLE("Headset Mic Capture Switch", | 
 | 506 | 			TWL4030_REG_ANAMICL, 1, 1, 0), | 
 | 507 | 	SOC_DAPM_SINGLE("AUXL Capture Switch", | 
 | 508 | 			TWL4030_REG_ANAMICL, 2, 1, 0), | 
 | 509 | 	SOC_DAPM_SINGLE("Carkit Mic Capture Switch", | 
 | 510 | 			TWL4030_REG_ANAMICL, 3, 1, 0), | 
| Joonyoung Shim | 97b8096 | 2009-05-11 20:36:08 +0900 | [diff] [blame] | 511 | }; | 
| Peter Ujfalusi | 276c622 | 2008-12-31 10:08:38 +0200 | [diff] [blame] | 512 |  | 
 | 513 | /* Right analog microphone selection */ | 
| Joonyoung Shim | 97b8096 | 2009-05-11 20:36:08 +0900 | [diff] [blame] | 514 | static const struct snd_kcontrol_new twl4030_dapm_analogrmic_controls[] = { | 
| Peter Ujfalusi | 9028935 | 2009-08-14 08:44:00 +0300 | [diff] [blame] | 515 | 	SOC_DAPM_SINGLE("Sub Mic Capture Switch", TWL4030_REG_ANAMICR, 0, 1, 0), | 
 | 516 | 	SOC_DAPM_SINGLE("AUXR Capture Switch", TWL4030_REG_ANAMICR, 2, 1, 0), | 
| Joonyoung Shim | 97b8096 | 2009-05-11 20:36:08 +0900 | [diff] [blame] | 517 | }; | 
| Peter Ujfalusi | 276c622 | 2008-12-31 10:08:38 +0200 | [diff] [blame] | 518 |  | 
 | 519 | /* TX1 L/R Analog/Digital microphone selection */ | 
 | 520 | static const char *twl4030_micpathtx1_texts[] = | 
 | 521 | 		{"Analog", "Digimic0"}; | 
 | 522 |  | 
 | 523 | static const struct soc_enum twl4030_micpathtx1_enum = | 
 | 524 | 	SOC_ENUM_SINGLE(TWL4030_REG_ADCMICSEL, 0, | 
 | 525 | 			ARRAY_SIZE(twl4030_micpathtx1_texts), | 
 | 526 | 			twl4030_micpathtx1_texts); | 
 | 527 |  | 
 | 528 | static const struct snd_kcontrol_new twl4030_dapm_micpathtx1_control = | 
 | 529 | SOC_DAPM_ENUM("Route", twl4030_micpathtx1_enum); | 
 | 530 |  | 
 | 531 | /* TX2 L/R Analog/Digital microphone selection */ | 
 | 532 | static const char *twl4030_micpathtx2_texts[] = | 
 | 533 | 		{"Analog", "Digimic1"}; | 
 | 534 |  | 
 | 535 | static const struct soc_enum twl4030_micpathtx2_enum = | 
 | 536 | 	SOC_ENUM_SINGLE(TWL4030_REG_ADCMICSEL, 2, | 
 | 537 | 			ARRAY_SIZE(twl4030_micpathtx2_texts), | 
 | 538 | 			twl4030_micpathtx2_texts); | 
 | 539 |  | 
 | 540 | static const struct snd_kcontrol_new twl4030_dapm_micpathtx2_control = | 
 | 541 | SOC_DAPM_ENUM("Route", twl4030_micpathtx2_enum); | 
 | 542 |  | 
| Peter Ujfalusi | 7393958 | 2009-01-29 14:57:50 +0200 | [diff] [blame] | 543 | /* Analog bypass for AudioR1 */ | 
 | 544 | static const struct snd_kcontrol_new twl4030_dapm_abypassr1_control = | 
 | 545 | 	SOC_DAPM_SINGLE("Switch", TWL4030_REG_ARXR1_APGA_CTL, 2, 1, 0); | 
 | 546 |  | 
 | 547 | /* Analog bypass for AudioL1 */ | 
 | 548 | static const struct snd_kcontrol_new twl4030_dapm_abypassl1_control = | 
 | 549 | 	SOC_DAPM_SINGLE("Switch", TWL4030_REG_ARXL1_APGA_CTL, 2, 1, 0); | 
 | 550 |  | 
 | 551 | /* Analog bypass for AudioR2 */ | 
 | 552 | static const struct snd_kcontrol_new twl4030_dapm_abypassr2_control = | 
 | 553 | 	SOC_DAPM_SINGLE("Switch", TWL4030_REG_ARXR2_APGA_CTL, 2, 1, 0); | 
 | 554 |  | 
 | 555 | /* Analog bypass for AudioL2 */ | 
 | 556 | static const struct snd_kcontrol_new twl4030_dapm_abypassl2_control = | 
 | 557 | 	SOC_DAPM_SINGLE("Switch", TWL4030_REG_ARXL2_APGA_CTL, 2, 1, 0); | 
 | 558 |  | 
| Lopez Cruz, Misael | fcd274a | 2009-04-30 21:47:22 -0500 | [diff] [blame] | 559 | /* Analog bypass for Voice */ | 
 | 560 | static const struct snd_kcontrol_new twl4030_dapm_abypassv_control = | 
 | 561 | 	SOC_DAPM_SINGLE("Switch", TWL4030_REG_VDL_APGA_CTL, 2, 1, 0); | 
 | 562 |  | 
| Peter Ujfalusi | 8b0d315 | 2010-07-12 11:50:06 +0300 | [diff] [blame] | 563 | /* Digital bypass gain, mute instead of -30dB */ | 
| Peter Ujfalusi | 6bab83f | 2009-02-18 14:39:05 +0200 | [diff] [blame] | 564 | static const unsigned int twl4030_dapm_dbypass_tlv[] = { | 
| Peter Ujfalusi | 8b0d315 | 2010-07-12 11:50:06 +0300 | [diff] [blame] | 565 | 	TLV_DB_RANGE_HEAD(3), | 
 | 566 | 	0, 1, TLV_DB_SCALE_ITEM(-3000, 600, 1), | 
 | 567 | 	2, 3, TLV_DB_SCALE_ITEM(-2400, 0, 0), | 
| Peter Ujfalusi | 6bab83f | 2009-02-18 14:39:05 +0200 | [diff] [blame] | 568 | 	4, 7, TLV_DB_SCALE_ITEM(-1800, 600, 0), | 
 | 569 | }; | 
 | 570 |  | 
 | 571 | /* Digital bypass left (TX1L -> RX2L) */ | 
 | 572 | static const struct snd_kcontrol_new twl4030_dapm_dbypassl_control = | 
 | 573 | 	SOC_DAPM_SINGLE_TLV("Volume", | 
 | 574 | 			TWL4030_REG_ATX2ARXPGA, 3, 7, 0, | 
 | 575 | 			twl4030_dapm_dbypass_tlv); | 
 | 576 |  | 
 | 577 | /* Digital bypass right (TX1R -> RX2R) */ | 
 | 578 | static const struct snd_kcontrol_new twl4030_dapm_dbypassr_control = | 
 | 579 | 	SOC_DAPM_SINGLE_TLV("Volume", | 
 | 580 | 			TWL4030_REG_ATX2ARXPGA, 0, 7, 0, | 
 | 581 | 			twl4030_dapm_dbypass_tlv); | 
 | 582 |  | 
| Lopez Cruz, Misael | ee8f689 | 2009-04-30 21:48:08 -0500 | [diff] [blame] | 583 | /* | 
 | 584 |  * Voice Sidetone GAIN volume control: | 
 | 585 |  * from -51 to -10 dB in 1 dB steps (mute instead of -51 dB) | 
 | 586 |  */ | 
 | 587 | static DECLARE_TLV_DB_SCALE(twl4030_dapm_dbypassv_tlv, -5100, 100, 1); | 
 | 588 |  | 
 | 589 | /* Digital bypass voice: sidetone (VUL -> VDL)*/ | 
 | 590 | static const struct snd_kcontrol_new twl4030_dapm_dbypassv_control = | 
 | 591 | 	SOC_DAPM_SINGLE_TLV("Volume", | 
 | 592 | 			TWL4030_REG_VSTPGA, 0, 0x29, 0, | 
 | 593 | 			twl4030_dapm_dbypassv_tlv); | 
 | 594 |  | 
| Peter Ujfalusi | 9008adf | 2009-08-13 15:59:34 +0300 | [diff] [blame] | 595 | /* | 
 | 596 |  * Output PGA builder: | 
 | 597 |  * Handle the muting and unmuting of the given output (turning off the | 
 | 598 |  * amplifier associated with the output pin) | 
| Peter Ujfalusi | c96907f | 2010-03-22 17:46:37 +0200 | [diff] [blame] | 599 |  * On mute bypass the reg_cache and write 0 to the register | 
 | 600 |  * On unmute: restore the register content from the reg_cache | 
| Peter Ujfalusi | 9008adf | 2009-08-13 15:59:34 +0300 | [diff] [blame] | 601 |  * Outputs handled in this way:  Earpiece, PreDrivL/R, CarkitL/R | 
 | 602 |  */ | 
 | 603 | #define TWL4030_OUTPUT_PGA(pin_name, reg, mask)				\ | 
 | 604 | static int pin_name##pga_event(struct snd_soc_dapm_widget *w,		\ | 
 | 605 | 		struct snd_kcontrol *kcontrol, int event)		\ | 
 | 606 | {									\ | 
| Mark Brown | b2c812e | 2010-04-14 15:35:19 +0900 | [diff] [blame] | 607 | 	struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(w->codec); \ | 
| Peter Ujfalusi | 9008adf | 2009-08-13 15:59:34 +0300 | [diff] [blame] | 608 | 									\ | 
 | 609 | 	switch (event) {						\ | 
 | 610 | 	case SND_SOC_DAPM_POST_PMU:					\ | 
| Peter Ujfalusi | c96907f | 2010-03-22 17:46:37 +0200 | [diff] [blame] | 611 | 		twl4030->pin_name##_enabled = 1;			\ | 
| Peter Ujfalusi | 9008adf | 2009-08-13 15:59:34 +0300 | [diff] [blame] | 612 | 		twl4030_write(w->codec, reg,				\ | 
 | 613 | 			twl4030_read_reg_cache(w->codec, reg));		\ | 
 | 614 | 		break;							\ | 
 | 615 | 	case SND_SOC_DAPM_POST_PMD:					\ | 
| Peter Ujfalusi | c96907f | 2010-03-22 17:46:37 +0200 | [diff] [blame] | 616 | 		twl4030->pin_name##_enabled = 0;			\ | 
 | 617 | 		twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,		\ | 
 | 618 | 					0, reg);			\ | 
| Peter Ujfalusi | 9008adf | 2009-08-13 15:59:34 +0300 | [diff] [blame] | 619 | 		break;							\ | 
 | 620 | 	}								\ | 
 | 621 | 	return 0;							\ | 
 | 622 | } | 
 | 623 |  | 
 | 624 | TWL4030_OUTPUT_PGA(earpiece, TWL4030_REG_EAR_CTL, TWL4030_EAR_GAIN); | 
 | 625 | TWL4030_OUTPUT_PGA(predrivel, TWL4030_REG_PREDL_CTL, TWL4030_PREDL_GAIN); | 
 | 626 | TWL4030_OUTPUT_PGA(predriver, TWL4030_REG_PREDR_CTL, TWL4030_PREDR_GAIN); | 
 | 627 | TWL4030_OUTPUT_PGA(carkitl, TWL4030_REG_PRECKL_CTL, TWL4030_PRECKL_GAIN); | 
 | 628 | TWL4030_OUTPUT_PGA(carkitr, TWL4030_REG_PRECKR_CTL, TWL4030_PRECKR_GAIN); | 
 | 629 |  | 
| Peter Ujfalusi | 5a2e9a4 | 2009-05-25 11:12:11 +0300 | [diff] [blame] | 630 | static void handsfree_ramp(struct snd_soc_codec *codec, int reg, int ramp) | 
| Stanley.Miao | 49d92c7 | 2008-12-11 23:28:10 +0800 | [diff] [blame] | 631 | { | 
| Stanley.Miao | 49d92c7 | 2008-12-11 23:28:10 +0800 | [diff] [blame] | 632 | 	unsigned char hs_ctl; | 
 | 633 |  | 
| Peter Ujfalusi | 5a2e9a4 | 2009-05-25 11:12:11 +0300 | [diff] [blame] | 634 | 	hs_ctl = twl4030_read_reg_cache(codec, reg); | 
| Stanley.Miao | 49d92c7 | 2008-12-11 23:28:10 +0800 | [diff] [blame] | 635 |  | 
| Peter Ujfalusi | 5a2e9a4 | 2009-05-25 11:12:11 +0300 | [diff] [blame] | 636 | 	if (ramp) { | 
 | 637 | 		/* HF ramp-up */ | 
 | 638 | 		hs_ctl |= TWL4030_HF_CTL_REF_EN; | 
 | 639 | 		twl4030_write(codec, reg, hs_ctl); | 
 | 640 | 		udelay(10); | 
| Stanley.Miao | 49d92c7 | 2008-12-11 23:28:10 +0800 | [diff] [blame] | 641 | 		hs_ctl |= TWL4030_HF_CTL_RAMP_EN; | 
| Peter Ujfalusi | 5a2e9a4 | 2009-05-25 11:12:11 +0300 | [diff] [blame] | 642 | 		twl4030_write(codec, reg, hs_ctl); | 
 | 643 | 		udelay(40); | 
| Stanley.Miao | 49d92c7 | 2008-12-11 23:28:10 +0800 | [diff] [blame] | 644 | 		hs_ctl |= TWL4030_HF_CTL_LOOP_EN; | 
| Stanley.Miao | 49d92c7 | 2008-12-11 23:28:10 +0800 | [diff] [blame] | 645 | 		hs_ctl |= TWL4030_HF_CTL_HB_EN; | 
| Peter Ujfalusi | 5a2e9a4 | 2009-05-25 11:12:11 +0300 | [diff] [blame] | 646 | 		twl4030_write(codec, reg, hs_ctl); | 
| Stanley.Miao | 49d92c7 | 2008-12-11 23:28:10 +0800 | [diff] [blame] | 647 | 	} else { | 
| Peter Ujfalusi | 5a2e9a4 | 2009-05-25 11:12:11 +0300 | [diff] [blame] | 648 | 		/* HF ramp-down */ | 
 | 649 | 		hs_ctl &= ~TWL4030_HF_CTL_LOOP_EN; | 
 | 650 | 		hs_ctl &= ~TWL4030_HF_CTL_HB_EN; | 
 | 651 | 		twl4030_write(codec, reg, hs_ctl); | 
 | 652 | 		hs_ctl &= ~TWL4030_HF_CTL_RAMP_EN; | 
 | 653 | 		twl4030_write(codec, reg, hs_ctl); | 
 | 654 | 		udelay(40); | 
 | 655 | 		hs_ctl &= ~TWL4030_HF_CTL_REF_EN; | 
 | 656 | 		twl4030_write(codec, reg, hs_ctl); | 
| Stanley.Miao | 49d92c7 | 2008-12-11 23:28:10 +0800 | [diff] [blame] | 657 | 	} | 
| Peter Ujfalusi | 5a2e9a4 | 2009-05-25 11:12:11 +0300 | [diff] [blame] | 658 | } | 
| Stanley.Miao | 49d92c7 | 2008-12-11 23:28:10 +0800 | [diff] [blame] | 659 |  | 
| Peter Ujfalusi | 5a2e9a4 | 2009-05-25 11:12:11 +0300 | [diff] [blame] | 660 | static int handsfreelpga_event(struct snd_soc_dapm_widget *w, | 
 | 661 | 		struct snd_kcontrol *kcontrol, int event) | 
 | 662 | { | 
 | 663 | 	switch (event) { | 
 | 664 | 	case SND_SOC_DAPM_POST_PMU: | 
 | 665 | 		handsfree_ramp(w->codec, TWL4030_REG_HFL_CTL, 1); | 
 | 666 | 		break; | 
 | 667 | 	case SND_SOC_DAPM_POST_PMD: | 
 | 668 | 		handsfree_ramp(w->codec, TWL4030_REG_HFL_CTL, 0); | 
 | 669 | 		break; | 
 | 670 | 	} | 
 | 671 | 	return 0; | 
 | 672 | } | 
 | 673 |  | 
 | 674 | static int handsfreerpga_event(struct snd_soc_dapm_widget *w, | 
 | 675 | 		struct snd_kcontrol *kcontrol, int event) | 
 | 676 | { | 
 | 677 | 	switch (event) { | 
 | 678 | 	case SND_SOC_DAPM_POST_PMU: | 
 | 679 | 		handsfree_ramp(w->codec, TWL4030_REG_HFR_CTL, 1); | 
 | 680 | 		break; | 
 | 681 | 	case SND_SOC_DAPM_POST_PMD: | 
 | 682 | 		handsfree_ramp(w->codec, TWL4030_REG_HFR_CTL, 0); | 
 | 683 | 		break; | 
 | 684 | 	} | 
| Stanley.Miao | 49d92c7 | 2008-12-11 23:28:10 +0800 | [diff] [blame] | 685 | 	return 0; | 
 | 686 | } | 
 | 687 |  | 
| Jari Vanhala | 86139a1 | 2009-10-29 11:58:09 +0200 | [diff] [blame] | 688 | static int vibramux_event(struct snd_soc_dapm_widget *w, | 
 | 689 | 		struct snd_kcontrol *kcontrol, int event) | 
 | 690 | { | 
 | 691 | 	twl4030_write(w->codec, TWL4030_REG_VIBRA_SET, 0xff); | 
 | 692 | 	return 0; | 
 | 693 | } | 
 | 694 |  | 
| Peter Ujfalusi | 7729cf7 | 2009-10-29 11:58:10 +0200 | [diff] [blame] | 695 | static int apll_event(struct snd_soc_dapm_widget *w, | 
 | 696 | 		struct snd_kcontrol *kcontrol, int event) | 
 | 697 | { | 
 | 698 | 	switch (event) { | 
 | 699 | 	case SND_SOC_DAPM_PRE_PMU: | 
 | 700 | 		twl4030_apll_enable(w->codec, 1); | 
 | 701 | 		break; | 
 | 702 | 	case SND_SOC_DAPM_POST_PMD: | 
 | 703 | 		twl4030_apll_enable(w->codec, 0); | 
 | 704 | 		break; | 
 | 705 | 	} | 
 | 706 | 	return 0; | 
 | 707 | } | 
 | 708 |  | 
| Peter Ujfalusi | 7b4c734 | 2010-04-29 10:58:08 +0300 | [diff] [blame] | 709 | static int aif_event(struct snd_soc_dapm_widget *w, | 
 | 710 | 		struct snd_kcontrol *kcontrol, int event) | 
 | 711 | { | 
 | 712 | 	u8 audio_if; | 
 | 713 |  | 
 | 714 | 	audio_if = twl4030_read_reg_cache(w->codec, TWL4030_REG_AUDIO_IF); | 
 | 715 | 	switch (event) { | 
 | 716 | 	case SND_SOC_DAPM_PRE_PMU: | 
 | 717 | 		/* Enable AIF */ | 
 | 718 | 		/* enable the PLL before we use it to clock the DAI */ | 
 | 719 | 		twl4030_apll_enable(w->codec, 1); | 
 | 720 |  | 
 | 721 | 		twl4030_write(w->codec, TWL4030_REG_AUDIO_IF, | 
 | 722 | 						audio_if | TWL4030_AIF_EN); | 
 | 723 | 		break; | 
 | 724 | 	case SND_SOC_DAPM_POST_PMD: | 
 | 725 | 		/* disable the DAI before we stop it's source PLL */ | 
 | 726 | 		twl4030_write(w->codec, TWL4030_REG_AUDIO_IF, | 
 | 727 | 						audio_if &  ~TWL4030_AIF_EN); | 
 | 728 | 		twl4030_apll_enable(w->codec, 0); | 
 | 729 | 		break; | 
 | 730 | 	} | 
 | 731 | 	return 0; | 
 | 732 | } | 
 | 733 |  | 
| Peter Ujfalusi | 6943c92 | 2009-05-18 16:02:05 +0300 | [diff] [blame] | 734 | static void headset_ramp(struct snd_soc_codec *codec, int ramp) | 
| Peter Ujfalusi | aad749e | 2009-01-27 11:29:41 +0200 | [diff] [blame] | 735 | { | 
| Andres Salomon | 0638d56 | 2011-02-17 19:07:20 -0800 | [diff] [blame] | 736 | 	struct twl4030_codec_audio_data *pdata = | 
 | 737 | 			mfd_get_data(to_platform_device(codec->dev)); | 
| Peter Ujfalusi | aad749e | 2009-01-27 11:29:41 +0200 | [diff] [blame] | 738 | 	unsigned char hs_gain, hs_pop; | 
| Mark Brown | b2c812e | 2010-04-14 15:35:19 +0900 | [diff] [blame] | 739 | 	struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); | 
| Peter Ujfalusi | 6943c92 | 2009-05-18 16:02:05 +0300 | [diff] [blame] | 740 | 	/* Base values for ramp delay calculation: 2^19 - 2^26 */ | 
 | 741 | 	unsigned int ramp_base[] = {524288, 1048576, 2097152, 4194304, | 
 | 742 | 				    8388608, 16777216, 33554432, 67108864}; | 
| Peter Ujfalusi | 7e6120c | 2010-10-25 11:34:23 +0300 | [diff] [blame] | 743 | 	unsigned int delay; | 
| Peter Ujfalusi | aad749e | 2009-01-27 11:29:41 +0200 | [diff] [blame] | 744 |  | 
| Peter Ujfalusi | 6943c92 | 2009-05-18 16:02:05 +0300 | [diff] [blame] | 745 | 	hs_gain = twl4030_read_reg_cache(codec, TWL4030_REG_HS_GAIN_SET); | 
 | 746 | 	hs_pop = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET); | 
| Peter Ujfalusi | 7e6120c | 2010-10-25 11:34:23 +0300 | [diff] [blame] | 747 | 	delay = (ramp_base[(hs_pop & TWL4030_RAMP_DELAY) >> 2] / | 
 | 748 | 		twl4030->sysclk) + 1; | 
| Peter Ujfalusi | aad749e | 2009-01-27 11:29:41 +0200 | [diff] [blame] | 749 |  | 
| Candelaria Villareal, Jorge | 4e49ffd1 | 2009-07-01 19:17:43 -0500 | [diff] [blame] | 750 | 	/* Enable external mute control, this dramatically reduces | 
 | 751 | 	 * the pop-noise */ | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 752 | 	if (pdata && pdata->hs_extmute) { | 
 | 753 | 		if (pdata->set_hs_extmute) { | 
 | 754 | 			pdata->set_hs_extmute(1); | 
| Candelaria Villareal, Jorge | 4e49ffd1 | 2009-07-01 19:17:43 -0500 | [diff] [blame] | 755 | 		} else { | 
 | 756 | 			hs_pop |= TWL4030_EXTMUTE; | 
 | 757 | 			twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop); | 
 | 758 | 		} | 
 | 759 | 	} | 
 | 760 |  | 
| Peter Ujfalusi | 6943c92 | 2009-05-18 16:02:05 +0300 | [diff] [blame] | 761 | 	if (ramp) { | 
 | 762 | 		/* Headset ramp-up according to the TRM */ | 
| Peter Ujfalusi | aad749e | 2009-01-27 11:29:41 +0200 | [diff] [blame] | 763 | 		hs_pop |= TWL4030_VMID_EN; | 
| Peter Ujfalusi | 6943c92 | 2009-05-18 16:02:05 +0300 | [diff] [blame] | 764 | 		twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop); | 
| Peter Ujfalusi | c96907f | 2010-03-22 17:46:37 +0200 | [diff] [blame] | 765 | 		/* Actually write to the register */ | 
 | 766 | 		twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, | 
 | 767 | 					hs_gain, | 
 | 768 | 					TWL4030_REG_HS_GAIN_SET); | 
| Peter Ujfalusi | aad749e | 2009-01-27 11:29:41 +0200 | [diff] [blame] | 769 | 		hs_pop |= TWL4030_RAMP_EN; | 
| Peter Ujfalusi | 6943c92 | 2009-05-18 16:02:05 +0300 | [diff] [blame] | 770 | 		twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop); | 
| Candelaria Villareal, Jorge | 4e49ffd1 | 2009-07-01 19:17:43 -0500 | [diff] [blame] | 771 | 		/* Wait ramp delay time + 1, so the VMID can settle */ | 
| Peter Ujfalusi | 7e6120c | 2010-10-25 11:34:23 +0300 | [diff] [blame] | 772 | 		twl4030_wait_ms(delay); | 
| Peter Ujfalusi | 6943c92 | 2009-05-18 16:02:05 +0300 | [diff] [blame] | 773 | 	} else { | 
 | 774 | 		/* Headset ramp-down _not_ according to | 
 | 775 | 		 * the TRM, but in a way that it is working */ | 
| Peter Ujfalusi | aad749e | 2009-01-27 11:29:41 +0200 | [diff] [blame] | 776 | 		hs_pop &= ~TWL4030_RAMP_EN; | 
| Peter Ujfalusi | 6943c92 | 2009-05-18 16:02:05 +0300 | [diff] [blame] | 777 | 		twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop); | 
 | 778 | 		/* Wait ramp delay time + 1, so the VMID can settle */ | 
| Peter Ujfalusi | 7e6120c | 2010-10-25 11:34:23 +0300 | [diff] [blame] | 779 | 		twl4030_wait_ms(delay); | 
| Peter Ujfalusi | aad749e | 2009-01-27 11:29:41 +0200 | [diff] [blame] | 780 | 		/* Bypass the reg_cache to mute the headset */ | 
| Balaji T K | fc7b92f | 2009-12-13 21:23:33 +0100 | [diff] [blame] | 781 | 		twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, | 
| Peter Ujfalusi | aad749e | 2009-01-27 11:29:41 +0200 | [diff] [blame] | 782 | 					hs_gain & (~0x0f), | 
 | 783 | 					TWL4030_REG_HS_GAIN_SET); | 
| Peter Ujfalusi | 6943c92 | 2009-05-18 16:02:05 +0300 | [diff] [blame] | 784 |  | 
| Peter Ujfalusi | aad749e | 2009-01-27 11:29:41 +0200 | [diff] [blame] | 785 | 		hs_pop &= ~TWL4030_VMID_EN; | 
| Peter Ujfalusi | 6943c92 | 2009-05-18 16:02:05 +0300 | [diff] [blame] | 786 | 		twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop); | 
 | 787 | 	} | 
| Candelaria Villareal, Jorge | 4e49ffd1 | 2009-07-01 19:17:43 -0500 | [diff] [blame] | 788 |  | 
 | 789 | 	/* Disable external mute */ | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 790 | 	if (pdata && pdata->hs_extmute) { | 
 | 791 | 		if (pdata->set_hs_extmute) { | 
 | 792 | 			pdata->set_hs_extmute(0); | 
| Candelaria Villareal, Jorge | 4e49ffd1 | 2009-07-01 19:17:43 -0500 | [diff] [blame] | 793 | 		} else { | 
 | 794 | 			hs_pop &= ~TWL4030_EXTMUTE; | 
 | 795 | 			twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop); | 
 | 796 | 		} | 
 | 797 | 	} | 
| Peter Ujfalusi | 6943c92 | 2009-05-18 16:02:05 +0300 | [diff] [blame] | 798 | } | 
 | 799 |  | 
 | 800 | static int headsetlpga_event(struct snd_soc_dapm_widget *w, | 
 | 801 | 		struct snd_kcontrol *kcontrol, int event) | 
 | 802 | { | 
| Mark Brown | b2c812e | 2010-04-14 15:35:19 +0900 | [diff] [blame] | 803 | 	struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(w->codec); | 
| Peter Ujfalusi | 6943c92 | 2009-05-18 16:02:05 +0300 | [diff] [blame] | 804 |  | 
 | 805 | 	switch (event) { | 
 | 806 | 	case SND_SOC_DAPM_POST_PMU: | 
 | 807 | 		/* Do the ramp-up only once */ | 
 | 808 | 		if (!twl4030->hsr_enabled) | 
 | 809 | 			headset_ramp(w->codec, 1); | 
 | 810 |  | 
 | 811 | 		twl4030->hsl_enabled = 1; | 
 | 812 | 		break; | 
 | 813 | 	case SND_SOC_DAPM_POST_PMD: | 
 | 814 | 		/* Do the ramp-down only if both headsetL/R is disabled */ | 
 | 815 | 		if (!twl4030->hsr_enabled) | 
 | 816 | 			headset_ramp(w->codec, 0); | 
 | 817 |  | 
 | 818 | 		twl4030->hsl_enabled = 0; | 
 | 819 | 		break; | 
 | 820 | 	} | 
 | 821 | 	return 0; | 
 | 822 | } | 
 | 823 |  | 
 | 824 | static int headsetrpga_event(struct snd_soc_dapm_widget *w, | 
 | 825 | 		struct snd_kcontrol *kcontrol, int event) | 
 | 826 | { | 
| Mark Brown | b2c812e | 2010-04-14 15:35:19 +0900 | [diff] [blame] | 827 | 	struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(w->codec); | 
| Peter Ujfalusi | 6943c92 | 2009-05-18 16:02:05 +0300 | [diff] [blame] | 828 |  | 
 | 829 | 	switch (event) { | 
 | 830 | 	case SND_SOC_DAPM_POST_PMU: | 
 | 831 | 		/* Do the ramp-up only once */ | 
 | 832 | 		if (!twl4030->hsl_enabled) | 
 | 833 | 			headset_ramp(w->codec, 1); | 
 | 834 |  | 
 | 835 | 		twl4030->hsr_enabled = 1; | 
 | 836 | 		break; | 
 | 837 | 	case SND_SOC_DAPM_POST_PMD: | 
 | 838 | 		/* Do the ramp-down only if both headsetL/R is disabled */ | 
 | 839 | 		if (!twl4030->hsl_enabled) | 
 | 840 | 			headset_ramp(w->codec, 0); | 
 | 841 |  | 
 | 842 | 		twl4030->hsr_enabled = 0; | 
| Peter Ujfalusi | aad749e | 2009-01-27 11:29:41 +0200 | [diff] [blame] | 843 | 		break; | 
 | 844 | 	} | 
 | 845 | 	return 0; | 
 | 846 | } | 
 | 847 |  | 
| Peter Ujfalusi | 01ea6ba | 2010-07-20 15:49:09 +0300 | [diff] [blame] | 848 | static int digimic_event(struct snd_soc_dapm_widget *w, | 
 | 849 | 		struct snd_kcontrol *kcontrol, int event) | 
 | 850 | { | 
 | 851 | 	struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(w->codec); | 
 | 852 |  | 
 | 853 | 	if (twl4030->digimic_delay) | 
| Peter Ujfalusi | 7e6120c | 2010-10-25 11:34:23 +0300 | [diff] [blame] | 854 | 		twl4030_wait_ms(twl4030->digimic_delay); | 
| Peter Ujfalusi | 01ea6ba | 2010-07-20 15:49:09 +0300 | [diff] [blame] | 855 | 	return 0; | 
 | 856 | } | 
 | 857 |  | 
| Peter Ujfalusi | c10b82c | 2008-11-24 13:49:35 +0200 | [diff] [blame] | 858 | /* | 
| Peter Ujfalusi | b0bd53a | 2008-11-24 13:49:38 +0200 | [diff] [blame] | 859 |  * Some of the gain controls in TWL (mostly those which are associated with | 
 | 860 |  * the outputs) are implemented in an interesting way: | 
 | 861 |  * 0x0 : Power down (mute) | 
 | 862 |  * 0x1 : 6dB | 
 | 863 |  * 0x2 : 0 dB | 
 | 864 |  * 0x3 : -6 dB | 
 | 865 |  * Inverting not going to help with these. | 
 | 866 |  * Custom volsw and volsw_2r get/put functions to handle these gain bits. | 
 | 867 |  */ | 
 | 868 | #define SOC_DOUBLE_TLV_TWL4030(xname, xreg, shift_left, shift_right, xmax,\ | 
 | 869 | 			       xinvert, tlv_array) \ | 
 | 870 | {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ | 
 | 871 | 	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ | 
 | 872 | 		 SNDRV_CTL_ELEM_ACCESS_READWRITE,\ | 
 | 873 | 	.tlv.p = (tlv_array), \ | 
 | 874 | 	.info = snd_soc_info_volsw, \ | 
 | 875 | 	.get = snd_soc_get_volsw_twl4030, \ | 
 | 876 | 	.put = snd_soc_put_volsw_twl4030, \ | 
 | 877 | 	.private_value = (unsigned long)&(struct soc_mixer_control) \ | 
 | 878 | 		{.reg = xreg, .shift = shift_left, .rshift = shift_right,\ | 
 | 879 | 		 .max = xmax, .invert = xinvert} } | 
 | 880 | #define SOC_DOUBLE_R_TLV_TWL4030(xname, reg_left, reg_right, xshift, xmax,\ | 
 | 881 | 				 xinvert, tlv_array) \ | 
 | 882 | {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ | 
 | 883 | 	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ | 
 | 884 | 		 SNDRV_CTL_ELEM_ACCESS_READWRITE,\ | 
 | 885 | 	.tlv.p = (tlv_array), \ | 
 | 886 | 	.info = snd_soc_info_volsw_2r, \ | 
 | 887 | 	.get = snd_soc_get_volsw_r2_twl4030,\ | 
 | 888 | 	.put = snd_soc_put_volsw_r2_twl4030, \ | 
 | 889 | 	.private_value = (unsigned long)&(struct soc_mixer_control) \ | 
 | 890 | 		{.reg = reg_left, .rreg = reg_right, .shift = xshift, \ | 
| Mark Brown | 64089b8 | 2008-12-08 19:17:58 +0000 | [diff] [blame] | 891 | 		 .rshift = xshift, .max = xmax, .invert = xinvert} } | 
| Peter Ujfalusi | b0bd53a | 2008-11-24 13:49:38 +0200 | [diff] [blame] | 892 | #define SOC_SINGLE_TLV_TWL4030(xname, xreg, xshift, xmax, xinvert, tlv_array) \ | 
 | 893 | 	SOC_DOUBLE_TLV_TWL4030(xname, xreg, xshift, xshift, xmax, \ | 
 | 894 | 			       xinvert, tlv_array) | 
 | 895 |  | 
 | 896 | static int snd_soc_get_volsw_twl4030(struct snd_kcontrol *kcontrol, | 
 | 897 | 	struct snd_ctl_elem_value *ucontrol) | 
 | 898 | { | 
 | 899 | 	struct soc_mixer_control *mc = | 
 | 900 | 		(struct soc_mixer_control *)kcontrol->private_value; | 
 | 901 | 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | 
 | 902 | 	unsigned int reg = mc->reg; | 
 | 903 | 	unsigned int shift = mc->shift; | 
 | 904 | 	unsigned int rshift = mc->rshift; | 
 | 905 | 	int max = mc->max; | 
 | 906 | 	int mask = (1 << fls(max)) - 1; | 
 | 907 |  | 
 | 908 | 	ucontrol->value.integer.value[0] = | 
 | 909 | 		(snd_soc_read(codec, reg) >> shift) & mask; | 
 | 910 | 	if (ucontrol->value.integer.value[0]) | 
 | 911 | 		ucontrol->value.integer.value[0] = | 
 | 912 | 			max + 1 - ucontrol->value.integer.value[0]; | 
 | 913 |  | 
 | 914 | 	if (shift != rshift) { | 
 | 915 | 		ucontrol->value.integer.value[1] = | 
 | 916 | 			(snd_soc_read(codec, reg) >> rshift) & mask; | 
 | 917 | 		if (ucontrol->value.integer.value[1]) | 
 | 918 | 			ucontrol->value.integer.value[1] = | 
 | 919 | 				max + 1 - ucontrol->value.integer.value[1]; | 
 | 920 | 	} | 
 | 921 |  | 
 | 922 | 	return 0; | 
 | 923 | } | 
 | 924 |  | 
 | 925 | static int snd_soc_put_volsw_twl4030(struct snd_kcontrol *kcontrol, | 
 | 926 | 	struct snd_ctl_elem_value *ucontrol) | 
 | 927 | { | 
 | 928 | 	struct soc_mixer_control *mc = | 
 | 929 | 		(struct soc_mixer_control *)kcontrol->private_value; | 
 | 930 | 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | 
 | 931 | 	unsigned int reg = mc->reg; | 
 | 932 | 	unsigned int shift = mc->shift; | 
 | 933 | 	unsigned int rshift = mc->rshift; | 
 | 934 | 	int max = mc->max; | 
 | 935 | 	int mask = (1 << fls(max)) - 1; | 
 | 936 | 	unsigned short val, val2, val_mask; | 
 | 937 |  | 
 | 938 | 	val = (ucontrol->value.integer.value[0] & mask); | 
 | 939 |  | 
 | 940 | 	val_mask = mask << shift; | 
 | 941 | 	if (val) | 
 | 942 | 		val = max + 1 - val; | 
 | 943 | 	val = val << shift; | 
 | 944 | 	if (shift != rshift) { | 
 | 945 | 		val2 = (ucontrol->value.integer.value[1] & mask); | 
 | 946 | 		val_mask |= mask << rshift; | 
 | 947 | 		if (val2) | 
 | 948 | 			val2 = max + 1 - val2; | 
 | 949 | 		val |= val2 << rshift; | 
 | 950 | 	} | 
 | 951 | 	return snd_soc_update_bits(codec, reg, val_mask, val); | 
 | 952 | } | 
 | 953 |  | 
 | 954 | static int snd_soc_get_volsw_r2_twl4030(struct snd_kcontrol *kcontrol, | 
 | 955 | 	struct snd_ctl_elem_value *ucontrol) | 
 | 956 | { | 
 | 957 | 	struct soc_mixer_control *mc = | 
 | 958 | 		(struct soc_mixer_control *)kcontrol->private_value; | 
 | 959 | 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | 
 | 960 | 	unsigned int reg = mc->reg; | 
 | 961 | 	unsigned int reg2 = mc->rreg; | 
 | 962 | 	unsigned int shift = mc->shift; | 
 | 963 | 	int max = mc->max; | 
 | 964 | 	int mask = (1<<fls(max))-1; | 
 | 965 |  | 
 | 966 | 	ucontrol->value.integer.value[0] = | 
 | 967 | 		(snd_soc_read(codec, reg) >> shift) & mask; | 
 | 968 | 	ucontrol->value.integer.value[1] = | 
 | 969 | 		(snd_soc_read(codec, reg2) >> shift) & mask; | 
 | 970 |  | 
 | 971 | 	if (ucontrol->value.integer.value[0]) | 
 | 972 | 		ucontrol->value.integer.value[0] = | 
 | 973 | 			max + 1 - ucontrol->value.integer.value[0]; | 
 | 974 | 	if (ucontrol->value.integer.value[1]) | 
 | 975 | 		ucontrol->value.integer.value[1] = | 
 | 976 | 			max + 1 - ucontrol->value.integer.value[1]; | 
 | 977 |  | 
 | 978 | 	return 0; | 
 | 979 | } | 
 | 980 |  | 
 | 981 | static int snd_soc_put_volsw_r2_twl4030(struct snd_kcontrol *kcontrol, | 
 | 982 | 	struct snd_ctl_elem_value *ucontrol) | 
 | 983 | { | 
 | 984 | 	struct soc_mixer_control *mc = | 
 | 985 | 		(struct soc_mixer_control *)kcontrol->private_value; | 
 | 986 | 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | 
 | 987 | 	unsigned int reg = mc->reg; | 
 | 988 | 	unsigned int reg2 = mc->rreg; | 
 | 989 | 	unsigned int shift = mc->shift; | 
 | 990 | 	int max = mc->max; | 
 | 991 | 	int mask = (1 << fls(max)) - 1; | 
 | 992 | 	int err; | 
 | 993 | 	unsigned short val, val2, val_mask; | 
 | 994 |  | 
 | 995 | 	val_mask = mask << shift; | 
 | 996 | 	val = (ucontrol->value.integer.value[0] & mask); | 
 | 997 | 	val2 = (ucontrol->value.integer.value[1] & mask); | 
 | 998 |  | 
 | 999 | 	if (val) | 
 | 1000 | 		val = max + 1 - val; | 
 | 1001 | 	if (val2) | 
 | 1002 | 		val2 = max + 1 - val2; | 
 | 1003 |  | 
 | 1004 | 	val = val << shift; | 
 | 1005 | 	val2 = val2 << shift; | 
 | 1006 |  | 
 | 1007 | 	err = snd_soc_update_bits(codec, reg, val_mask, val); | 
 | 1008 | 	if (err < 0) | 
 | 1009 | 		return err; | 
 | 1010 |  | 
 | 1011 | 	err = snd_soc_update_bits(codec, reg2, val_mask, val2); | 
 | 1012 | 	return err; | 
 | 1013 | } | 
 | 1014 |  | 
| Lopez Cruz, Misael | b74bd40 | 2009-05-18 11:52:55 -0500 | [diff] [blame] | 1015 | /* Codec operation modes */ | 
 | 1016 | static const char *twl4030_op_modes_texts[] = { | 
 | 1017 | 	"Option 2 (voice/audio)", "Option 1 (audio)" | 
 | 1018 | }; | 
 | 1019 |  | 
 | 1020 | static const struct soc_enum twl4030_op_modes_enum = | 
 | 1021 | 	SOC_ENUM_SINGLE(TWL4030_REG_CODEC_MODE, 0, | 
 | 1022 | 			ARRAY_SIZE(twl4030_op_modes_texts), | 
 | 1023 | 			twl4030_op_modes_texts); | 
 | 1024 |  | 
| Mark Brown | 423c238 | 2009-06-20 13:54:02 +0100 | [diff] [blame] | 1025 | static int snd_soc_put_twl4030_opmode_enum_double(struct snd_kcontrol *kcontrol, | 
| Lopez Cruz, Misael | b74bd40 | 2009-05-18 11:52:55 -0500 | [diff] [blame] | 1026 | 	struct snd_ctl_elem_value *ucontrol) | 
 | 1027 | { | 
 | 1028 | 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | 
| Mark Brown | b2c812e | 2010-04-14 15:35:19 +0900 | [diff] [blame] | 1029 | 	struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); | 
| Lopez Cruz, Misael | b74bd40 | 2009-05-18 11:52:55 -0500 | [diff] [blame] | 1030 | 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; | 
 | 1031 | 	unsigned short val; | 
 | 1032 | 	unsigned short mask, bitmask; | 
 | 1033 |  | 
 | 1034 | 	if (twl4030->configured) { | 
 | 1035 | 		printk(KERN_ERR "twl4030 operation mode cannot be " | 
 | 1036 | 			"changed on-the-fly\n"); | 
 | 1037 | 		return -EBUSY; | 
 | 1038 | 	} | 
 | 1039 |  | 
 | 1040 | 	for (bitmask = 1; bitmask < e->max; bitmask <<= 1) | 
 | 1041 | 		; | 
 | 1042 | 	if (ucontrol->value.enumerated.item[0] > e->max - 1) | 
 | 1043 | 		return -EINVAL; | 
 | 1044 |  | 
 | 1045 | 	val = ucontrol->value.enumerated.item[0] << e->shift_l; | 
 | 1046 | 	mask = (bitmask - 1) << e->shift_l; | 
 | 1047 | 	if (e->shift_l != e->shift_r) { | 
 | 1048 | 		if (ucontrol->value.enumerated.item[1] > e->max - 1) | 
 | 1049 | 			return -EINVAL; | 
 | 1050 | 		val |= ucontrol->value.enumerated.item[1] << e->shift_r; | 
 | 1051 | 		mask |= (bitmask - 1) << e->shift_r; | 
 | 1052 | 	} | 
 | 1053 |  | 
 | 1054 | 	return snd_soc_update_bits(codec, e->reg, mask, val); | 
 | 1055 | } | 
 | 1056 |  | 
| Peter Ujfalusi | b0bd53a | 2008-11-24 13:49:38 +0200 | [diff] [blame] | 1057 | /* | 
| Peter Ujfalusi | c10b82c | 2008-11-24 13:49:35 +0200 | [diff] [blame] | 1058 |  * FGAIN volume control: | 
 | 1059 |  * from -62 to 0 dB in 1 dB steps (mute instead of -63 dB) | 
 | 1060 |  */ | 
| Peter Ujfalusi | d889a72 | 2008-12-01 10:03:46 +0200 | [diff] [blame] | 1061 | static DECLARE_TLV_DB_SCALE(digital_fine_tlv, -6300, 100, 1); | 
| Peter Ujfalusi | c10b82c | 2008-11-24 13:49:35 +0200 | [diff] [blame] | 1062 |  | 
| Peter Ujfalusi | 0d33ea0 | 2008-11-24 13:49:36 +0200 | [diff] [blame] | 1063 | /* | 
 | 1064 |  * CGAIN volume control: | 
 | 1065 |  * 0 dB to 12 dB in 6 dB steps | 
 | 1066 |  * value 2 and 3 means 12 dB | 
 | 1067 |  */ | 
| Peter Ujfalusi | d889a72 | 2008-12-01 10:03:46 +0200 | [diff] [blame] | 1068 | static DECLARE_TLV_DB_SCALE(digital_coarse_tlv, 0, 600, 0); | 
 | 1069 |  | 
 | 1070 | /* | 
| Joonyoung Shim | 1a787e7 | 2009-04-22 13:13:34 +0900 | [diff] [blame] | 1071 |  * Voice Downlink GAIN volume control: | 
 | 1072 |  * from -37 to 12 dB in 1 dB steps (mute instead of -37 dB) | 
 | 1073 |  */ | 
 | 1074 | static DECLARE_TLV_DB_SCALE(digital_voice_downlink_tlv, -3700, 100, 1); | 
 | 1075 |  | 
 | 1076 | /* | 
| Peter Ujfalusi | d889a72 | 2008-12-01 10:03:46 +0200 | [diff] [blame] | 1077 |  * Analog playback gain | 
 | 1078 |  * -24 dB to 12 dB in 2 dB steps | 
 | 1079 |  */ | 
 | 1080 | static DECLARE_TLV_DB_SCALE(analog_tlv, -2400, 200, 0); | 
| Peter Ujfalusi | 0d33ea0 | 2008-11-24 13:49:36 +0200 | [diff] [blame] | 1081 |  | 
| Peter Ujfalusi | 381a22b | 2008-12-01 10:03:45 +0200 | [diff] [blame] | 1082 | /* | 
| Peter Ujfalusi | 4290239 | 2008-12-01 10:03:47 +0200 | [diff] [blame] | 1083 |  * Gain controls tied to outputs | 
 | 1084 |  * -6 dB to 6 dB in 6 dB steps (mute instead of -12) | 
 | 1085 |  */ | 
 | 1086 | static DECLARE_TLV_DB_SCALE(output_tvl, -1200, 600, 1); | 
 | 1087 |  | 
 | 1088 | /* | 
| Joonyoung Shim | 18cc8d8 | 2009-04-28 18:18:05 +0900 | [diff] [blame] | 1089 |  * Gain control for earpiece amplifier | 
 | 1090 |  * 0 dB to 12 dB in 6 dB steps (mute instead of -6) | 
 | 1091 |  */ | 
 | 1092 | static DECLARE_TLV_DB_SCALE(output_ear_tvl, -600, 600, 1); | 
 | 1093 |  | 
 | 1094 | /* | 
| Peter Ujfalusi | 381a22b | 2008-12-01 10:03:45 +0200 | [diff] [blame] | 1095 |  * Capture gain after the ADCs | 
 | 1096 |  * from 0 dB to 31 dB in 1 dB steps | 
 | 1097 |  */ | 
 | 1098 | static DECLARE_TLV_DB_SCALE(digital_capture_tlv, 0, 100, 0); | 
 | 1099 |  | 
| Grazvydas Ignotas | 5920b45 | 2008-12-02 20:48:58 +0200 | [diff] [blame] | 1100 | /* | 
 | 1101 |  * Gain control for input amplifiers | 
 | 1102 |  * 0 dB to 30 dB in 6 dB steps | 
 | 1103 |  */ | 
 | 1104 | static DECLARE_TLV_DB_SCALE(input_gain_tlv, 0, 600, 0); | 
 | 1105 |  | 
| Lopez Cruz, Misael | 328d0a1 | 2009-06-22 10:51:52 -0500 | [diff] [blame] | 1106 | /* AVADC clock priority */ | 
 | 1107 | static const char *twl4030_avadc_clk_priority_texts[] = { | 
 | 1108 | 	"Voice high priority", "HiFi high priority" | 
 | 1109 | }; | 
 | 1110 |  | 
 | 1111 | static const struct soc_enum twl4030_avadc_clk_priority_enum = | 
 | 1112 | 	SOC_ENUM_SINGLE(TWL4030_REG_AVADC_CTL, 2, | 
 | 1113 | 			ARRAY_SIZE(twl4030_avadc_clk_priority_texts), | 
 | 1114 | 			twl4030_avadc_clk_priority_texts); | 
 | 1115 |  | 
| Peter Ujfalusi | 89492be | 2009-03-05 12:48:49 +0200 | [diff] [blame] | 1116 | static const char *twl4030_rampdelay_texts[] = { | 
 | 1117 | 	"27/20/14 ms", "55/40/27 ms", "109/81/55 ms", "218/161/109 ms", | 
 | 1118 | 	"437/323/218 ms", "874/645/437 ms", "1748/1291/874 ms", | 
 | 1119 | 	"3495/2581/1748 ms" | 
 | 1120 | }; | 
 | 1121 |  | 
 | 1122 | static const struct soc_enum twl4030_rampdelay_enum = | 
 | 1123 | 	SOC_ENUM_SINGLE(TWL4030_REG_HS_POPN_SET, 2, | 
 | 1124 | 			ARRAY_SIZE(twl4030_rampdelay_texts), | 
 | 1125 | 			twl4030_rampdelay_texts); | 
 | 1126 |  | 
| Peter Ujfalusi | 376f783 | 2009-05-05 08:55:47 +0300 | [diff] [blame] | 1127 | /* Vibra H-bridge direction mode */ | 
 | 1128 | static const char *twl4030_vibradirmode_texts[] = { | 
 | 1129 | 	"Vibra H-bridge direction", "Audio data MSB", | 
 | 1130 | }; | 
 | 1131 |  | 
 | 1132 | static const struct soc_enum twl4030_vibradirmode_enum = | 
 | 1133 | 	SOC_ENUM_SINGLE(TWL4030_REG_VIBRA_CTL, 5, | 
 | 1134 | 			ARRAY_SIZE(twl4030_vibradirmode_texts), | 
 | 1135 | 			twl4030_vibradirmode_texts); | 
 | 1136 |  | 
 | 1137 | /* Vibra H-bridge direction */ | 
 | 1138 | static const char *twl4030_vibradir_texts[] = { | 
 | 1139 | 	"Positive polarity", "Negative polarity", | 
 | 1140 | }; | 
 | 1141 |  | 
 | 1142 | static const struct soc_enum twl4030_vibradir_enum = | 
 | 1143 | 	SOC_ENUM_SINGLE(TWL4030_REG_VIBRA_CTL, 1, | 
 | 1144 | 			ARRAY_SIZE(twl4030_vibradir_texts), | 
 | 1145 | 			twl4030_vibradir_texts); | 
 | 1146 |  | 
| Peter Ujfalusi | 36aeff6 | 2010-05-12 10:35:36 +0300 | [diff] [blame] | 1147 | /* Digimic Left and right swapping */ | 
 | 1148 | static const char *twl4030_digimicswap_texts[] = { | 
 | 1149 | 	"Not swapped", "Swapped", | 
 | 1150 | }; | 
 | 1151 |  | 
 | 1152 | static const struct soc_enum twl4030_digimicswap_enum = | 
 | 1153 | 	SOC_ENUM_SINGLE(TWL4030_REG_MISC_SET_1, 0, | 
 | 1154 | 			ARRAY_SIZE(twl4030_digimicswap_texts), | 
 | 1155 | 			twl4030_digimicswap_texts); | 
 | 1156 |  | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 1157 | static const struct snd_kcontrol_new twl4030_snd_controls[] = { | 
| Lopez Cruz, Misael | b74bd40 | 2009-05-18 11:52:55 -0500 | [diff] [blame] | 1158 | 	/* Codec operation mode control */ | 
 | 1159 | 	SOC_ENUM_EXT("Codec Operation Mode", twl4030_op_modes_enum, | 
 | 1160 | 		snd_soc_get_enum_double, | 
 | 1161 | 		snd_soc_put_twl4030_opmode_enum_double), | 
 | 1162 |  | 
| Peter Ujfalusi | d889a72 | 2008-12-01 10:03:46 +0200 | [diff] [blame] | 1163 | 	/* Common playback gain controls */ | 
 | 1164 | 	SOC_DOUBLE_R_TLV("DAC1 Digital Fine Playback Volume", | 
 | 1165 | 		TWL4030_REG_ARXL1PGA, TWL4030_REG_ARXR1PGA, | 
 | 1166 | 		0, 0x3f, 0, digital_fine_tlv), | 
 | 1167 | 	SOC_DOUBLE_R_TLV("DAC2 Digital Fine Playback Volume", | 
 | 1168 | 		TWL4030_REG_ARXL2PGA, TWL4030_REG_ARXR2PGA, | 
 | 1169 | 		0, 0x3f, 0, digital_fine_tlv), | 
 | 1170 |  | 
 | 1171 | 	SOC_DOUBLE_R_TLV("DAC1 Digital Coarse Playback Volume", | 
 | 1172 | 		TWL4030_REG_ARXL1PGA, TWL4030_REG_ARXR1PGA, | 
 | 1173 | 		6, 0x2, 0, digital_coarse_tlv), | 
 | 1174 | 	SOC_DOUBLE_R_TLV("DAC2 Digital Coarse Playback Volume", | 
 | 1175 | 		TWL4030_REG_ARXL2PGA, TWL4030_REG_ARXR2PGA, | 
 | 1176 | 		6, 0x2, 0, digital_coarse_tlv), | 
 | 1177 |  | 
 | 1178 | 	SOC_DOUBLE_R_TLV("DAC1 Analog Playback Volume", | 
 | 1179 | 		TWL4030_REG_ARXL1_APGA_CTL, TWL4030_REG_ARXR1_APGA_CTL, | 
 | 1180 | 		3, 0x12, 1, analog_tlv), | 
 | 1181 | 	SOC_DOUBLE_R_TLV("DAC2 Analog Playback Volume", | 
 | 1182 | 		TWL4030_REG_ARXL2_APGA_CTL, TWL4030_REG_ARXR2_APGA_CTL, | 
 | 1183 | 		3, 0x12, 1, analog_tlv), | 
| Peter Ujfalusi | 44c5587 | 2008-12-09 08:45:44 +0200 | [diff] [blame] | 1184 | 	SOC_DOUBLE_R("DAC1 Analog Playback Switch", | 
 | 1185 | 		TWL4030_REG_ARXL1_APGA_CTL, TWL4030_REG_ARXR1_APGA_CTL, | 
 | 1186 | 		1, 1, 0), | 
 | 1187 | 	SOC_DOUBLE_R("DAC2 Analog Playback Switch", | 
 | 1188 | 		TWL4030_REG_ARXL2_APGA_CTL, TWL4030_REG_ARXR2_APGA_CTL, | 
 | 1189 | 		1, 1, 0), | 
| Peter Ujfalusi | 381a22b | 2008-12-01 10:03:45 +0200 | [diff] [blame] | 1190 |  | 
| Joonyoung Shim | 1a787e7 | 2009-04-22 13:13:34 +0900 | [diff] [blame] | 1191 | 	/* Common voice downlink gain controls */ | 
 | 1192 | 	SOC_SINGLE_TLV("DAC Voice Digital Downlink Volume", | 
 | 1193 | 		TWL4030_REG_VRXPGA, 0, 0x31, 0, digital_voice_downlink_tlv), | 
 | 1194 |  | 
 | 1195 | 	SOC_SINGLE_TLV("DAC Voice Analog Downlink Volume", | 
 | 1196 | 		TWL4030_REG_VDL_APGA_CTL, 3, 0x12, 1, analog_tlv), | 
 | 1197 |  | 
 | 1198 | 	SOC_SINGLE("DAC Voice Analog Downlink Switch", | 
 | 1199 | 		TWL4030_REG_VDL_APGA_CTL, 1, 1, 0), | 
 | 1200 |  | 
| Peter Ujfalusi | 4290239 | 2008-12-01 10:03:47 +0200 | [diff] [blame] | 1201 | 	/* Separate output gain controls */ | 
 | 1202 | 	SOC_DOUBLE_R_TLV_TWL4030("PreDriv Playback Volume", | 
 | 1203 | 		TWL4030_REG_PREDL_CTL, TWL4030_REG_PREDR_CTL, | 
 | 1204 | 		4, 3, 0, output_tvl), | 
 | 1205 |  | 
 | 1206 | 	SOC_DOUBLE_TLV_TWL4030("Headset Playback Volume", | 
 | 1207 | 		TWL4030_REG_HS_GAIN_SET, 0, 2, 3, 0, output_tvl), | 
 | 1208 |  | 
 | 1209 | 	SOC_DOUBLE_R_TLV_TWL4030("Carkit Playback Volume", | 
 | 1210 | 		TWL4030_REG_PRECKL_CTL, TWL4030_REG_PRECKR_CTL, | 
 | 1211 | 		4, 3, 0, output_tvl), | 
 | 1212 |  | 
 | 1213 | 	SOC_SINGLE_TLV_TWL4030("Earpiece Playback Volume", | 
| Joonyoung Shim | 18cc8d8 | 2009-04-28 18:18:05 +0900 | [diff] [blame] | 1214 | 		TWL4030_REG_EAR_CTL, 4, 3, 0, output_ear_tvl), | 
| Peter Ujfalusi | 4290239 | 2008-12-01 10:03:47 +0200 | [diff] [blame] | 1215 |  | 
| Peter Ujfalusi | 381a22b | 2008-12-01 10:03:45 +0200 | [diff] [blame] | 1216 | 	/* Common capture gain controls */ | 
| Peter Ujfalusi | 276c622 | 2008-12-31 10:08:38 +0200 | [diff] [blame] | 1217 | 	SOC_DOUBLE_R_TLV("TX1 Digital Capture Volume", | 
| Peter Ujfalusi | 381a22b | 2008-12-01 10:03:45 +0200 | [diff] [blame] | 1218 | 		TWL4030_REG_ATXL1PGA, TWL4030_REG_ATXR1PGA, | 
 | 1219 | 		0, 0x1f, 0, digital_capture_tlv), | 
| Peter Ujfalusi | 276c622 | 2008-12-31 10:08:38 +0200 | [diff] [blame] | 1220 | 	SOC_DOUBLE_R_TLV("TX2 Digital Capture Volume", | 
 | 1221 | 		TWL4030_REG_AVTXL2PGA, TWL4030_REG_AVTXR2PGA, | 
 | 1222 | 		0, 0x1f, 0, digital_capture_tlv), | 
| Grazvydas Ignotas | 5920b45 | 2008-12-02 20:48:58 +0200 | [diff] [blame] | 1223 |  | 
| Peter Ujfalusi | 276c622 | 2008-12-31 10:08:38 +0200 | [diff] [blame] | 1224 | 	SOC_DOUBLE_TLV("Analog Capture Volume", TWL4030_REG_ANAMIC_GAIN, | 
| Grazvydas Ignotas | 5920b45 | 2008-12-02 20:48:58 +0200 | [diff] [blame] | 1225 | 		0, 3, 5, 0, input_gain_tlv), | 
| Peter Ujfalusi | 89492be | 2009-03-05 12:48:49 +0200 | [diff] [blame] | 1226 |  | 
| Lopez Cruz, Misael | 328d0a1 | 2009-06-22 10:51:52 -0500 | [diff] [blame] | 1227 | 	SOC_ENUM("AVADC Clock Priority", twl4030_avadc_clk_priority_enum), | 
 | 1228 |  | 
| Peter Ujfalusi | 89492be | 2009-03-05 12:48:49 +0200 | [diff] [blame] | 1229 | 	SOC_ENUM("HS ramp delay", twl4030_rampdelay_enum), | 
| Peter Ujfalusi | 376f783 | 2009-05-05 08:55:47 +0300 | [diff] [blame] | 1230 |  | 
 | 1231 | 	SOC_ENUM("Vibra H-bridge mode", twl4030_vibradirmode_enum), | 
 | 1232 | 	SOC_ENUM("Vibra H-bridge direction", twl4030_vibradir_enum), | 
| Peter Ujfalusi | 36aeff6 | 2010-05-12 10:35:36 +0300 | [diff] [blame] | 1233 |  | 
 | 1234 | 	SOC_ENUM("Digimic LR Swap", twl4030_digimicswap_enum), | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 1235 | }; | 
 | 1236 |  | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 1237 | static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { | 
| Peter Ujfalusi | 276c622 | 2008-12-31 10:08:38 +0200 | [diff] [blame] | 1238 | 	/* Left channel inputs */ | 
 | 1239 | 	SND_SOC_DAPM_INPUT("MAINMIC"), | 
 | 1240 | 	SND_SOC_DAPM_INPUT("HSMIC"), | 
 | 1241 | 	SND_SOC_DAPM_INPUT("AUXL"), | 
 | 1242 | 	SND_SOC_DAPM_INPUT("CARKITMIC"), | 
 | 1243 | 	/* Right channel inputs */ | 
 | 1244 | 	SND_SOC_DAPM_INPUT("SUBMIC"), | 
 | 1245 | 	SND_SOC_DAPM_INPUT("AUXR"), | 
 | 1246 | 	/* Digital microphones (Stereo) */ | 
 | 1247 | 	SND_SOC_DAPM_INPUT("DIGIMIC0"), | 
 | 1248 | 	SND_SOC_DAPM_INPUT("DIGIMIC1"), | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 1249 |  | 
| Peter Ujfalusi | 276c622 | 2008-12-31 10:08:38 +0200 | [diff] [blame] | 1250 | 	/* Outputs */ | 
| Peter Ujfalusi | 5e98a46 | 2008-12-09 12:35:47 +0200 | [diff] [blame] | 1251 | 	SND_SOC_DAPM_OUTPUT("EARPIECE"), | 
| Peter Ujfalusi | 2a6f5c58 | 2008-12-09 12:35:48 +0200 | [diff] [blame] | 1252 | 	SND_SOC_DAPM_OUTPUT("PREDRIVEL"), | 
 | 1253 | 	SND_SOC_DAPM_OUTPUT("PREDRIVER"), | 
| Peter Ujfalusi | dfad21a | 2008-12-09 12:35:49 +0200 | [diff] [blame] | 1254 | 	SND_SOC_DAPM_OUTPUT("HSOL"), | 
 | 1255 | 	SND_SOC_DAPM_OUTPUT("HSOR"), | 
| Peter Ujfalusi | 6a1bee4 | 2008-12-10 12:51:46 +0200 | [diff] [blame] | 1256 | 	SND_SOC_DAPM_OUTPUT("CARKITL"), | 
 | 1257 | 	SND_SOC_DAPM_OUTPUT("CARKITR"), | 
| Peter Ujfalusi | df33980 | 2008-12-09 12:35:51 +0200 | [diff] [blame] | 1258 | 	SND_SOC_DAPM_OUTPUT("HFL"), | 
 | 1259 | 	SND_SOC_DAPM_OUTPUT("HFR"), | 
| Peter Ujfalusi | 376f783 | 2009-05-05 08:55:47 +0300 | [diff] [blame] | 1260 | 	SND_SOC_DAPM_OUTPUT("VIBRA"), | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 1261 |  | 
| Peter Ujfalusi | 7b4c734 | 2010-04-29 10:58:08 +0300 | [diff] [blame] | 1262 | 	/* AIF and APLL clocks for running DAIs (including loopback) */ | 
 | 1263 | 	SND_SOC_DAPM_OUTPUT("Virtual HiFi OUT"), | 
 | 1264 | 	SND_SOC_DAPM_INPUT("Virtual HiFi IN"), | 
 | 1265 | 	SND_SOC_DAPM_OUTPUT("Virtual Voice OUT"), | 
 | 1266 |  | 
| Peter Ujfalusi | 53b5047 | 2008-12-09 08:45:43 +0200 | [diff] [blame] | 1267 | 	/* DACs */ | 
| Peter Ujfalusi | b4852b7 | 2009-05-22 15:12:15 +0300 | [diff] [blame] | 1268 | 	SND_SOC_DAPM_DAC("DAC Right1", "Right Front HiFi Playback", | 
| Peter Ujfalusi | 7393958 | 2009-01-29 14:57:50 +0200 | [diff] [blame] | 1269 | 			SND_SOC_NOPM, 0, 0), | 
| Peter Ujfalusi | b4852b7 | 2009-05-22 15:12:15 +0300 | [diff] [blame] | 1270 | 	SND_SOC_DAPM_DAC("DAC Left1", "Left Front HiFi Playback", | 
| Peter Ujfalusi | 7393958 | 2009-01-29 14:57:50 +0200 | [diff] [blame] | 1271 | 			SND_SOC_NOPM, 0, 0), | 
| Peter Ujfalusi | b4852b7 | 2009-05-22 15:12:15 +0300 | [diff] [blame] | 1272 | 	SND_SOC_DAPM_DAC("DAC Right2", "Right Rear HiFi Playback", | 
| Peter Ujfalusi | 7393958 | 2009-01-29 14:57:50 +0200 | [diff] [blame] | 1273 | 			SND_SOC_NOPM, 0, 0), | 
| Peter Ujfalusi | b4852b7 | 2009-05-22 15:12:15 +0300 | [diff] [blame] | 1274 | 	SND_SOC_DAPM_DAC("DAC Left2", "Left Rear HiFi Playback", | 
| Peter Ujfalusi | 7393958 | 2009-01-29 14:57:50 +0200 | [diff] [blame] | 1275 | 			SND_SOC_NOPM, 0, 0), | 
| Joonyoung Shim | 1a787e7 | 2009-04-22 13:13:34 +0900 | [diff] [blame] | 1276 | 	SND_SOC_DAPM_DAC("DAC Voice", "Voice Playback", | 
| Lopez Cruz, Misael | fcd274a | 2009-04-30 21:47:22 -0500 | [diff] [blame] | 1277 | 			SND_SOC_NOPM, 0, 0), | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 1278 |  | 
| Peter Ujfalusi | 7393958 | 2009-01-29 14:57:50 +0200 | [diff] [blame] | 1279 | 	/* Analog bypasses */ | 
| Peter Ujfalusi | 78e08e2 | 2009-10-28 10:57:04 +0200 | [diff] [blame] | 1280 | 	SND_SOC_DAPM_SWITCH("Right1 Analog Loopback", SND_SOC_NOPM, 0, 0, | 
 | 1281 | 			&twl4030_dapm_abypassr1_control), | 
 | 1282 | 	SND_SOC_DAPM_SWITCH("Left1 Analog Loopback", SND_SOC_NOPM, 0, 0, | 
 | 1283 | 			&twl4030_dapm_abypassl1_control), | 
 | 1284 | 	SND_SOC_DAPM_SWITCH("Right2 Analog Loopback", SND_SOC_NOPM, 0, 0, | 
 | 1285 | 			&twl4030_dapm_abypassr2_control), | 
 | 1286 | 	SND_SOC_DAPM_SWITCH("Left2 Analog Loopback", SND_SOC_NOPM, 0, 0, | 
 | 1287 | 			&twl4030_dapm_abypassl2_control), | 
 | 1288 | 	SND_SOC_DAPM_SWITCH("Voice Analog Loopback", SND_SOC_NOPM, 0, 0, | 
 | 1289 | 			&twl4030_dapm_abypassv_control), | 
 | 1290 |  | 
 | 1291 | 	/* Master analog loopback switch */ | 
 | 1292 | 	SND_SOC_DAPM_SUPPLY("FM Loop Enable", TWL4030_REG_MISC_SET_1, 5, 0, | 
 | 1293 | 			    NULL, 0), | 
| Peter Ujfalusi | 7393958 | 2009-01-29 14:57:50 +0200 | [diff] [blame] | 1294 |  | 
| Peter Ujfalusi | 6bab83f | 2009-02-18 14:39:05 +0200 | [diff] [blame] | 1295 | 	/* Digital bypasses */ | 
| Peter Ujfalusi | 78e08e2 | 2009-10-28 10:57:04 +0200 | [diff] [blame] | 1296 | 	SND_SOC_DAPM_SWITCH("Left Digital Loopback", SND_SOC_NOPM, 0, 0, | 
 | 1297 | 			&twl4030_dapm_dbypassl_control), | 
 | 1298 | 	SND_SOC_DAPM_SWITCH("Right Digital Loopback", SND_SOC_NOPM, 0, 0, | 
 | 1299 | 			&twl4030_dapm_dbypassr_control), | 
 | 1300 | 	SND_SOC_DAPM_SWITCH("Voice Digital Loopback", SND_SOC_NOPM, 0, 0, | 
 | 1301 | 			&twl4030_dapm_dbypassv_control), | 
| Peter Ujfalusi | 6bab83f | 2009-02-18 14:39:05 +0200 | [diff] [blame] | 1302 |  | 
| Peter Ujfalusi | 4005d39 | 2009-05-18 16:02:04 +0300 | [diff] [blame] | 1303 | 	/* Digital mixers, power control for the physical DACs */ | 
 | 1304 | 	SND_SOC_DAPM_MIXER("Digital R1 Playback Mixer", | 
 | 1305 | 			TWL4030_REG_AVDAC_CTL, 0, 0, NULL, 0), | 
 | 1306 | 	SND_SOC_DAPM_MIXER("Digital L1 Playback Mixer", | 
 | 1307 | 			TWL4030_REG_AVDAC_CTL, 1, 0, NULL, 0), | 
 | 1308 | 	SND_SOC_DAPM_MIXER("Digital R2 Playback Mixer", | 
 | 1309 | 			TWL4030_REG_AVDAC_CTL, 2, 0, NULL, 0), | 
 | 1310 | 	SND_SOC_DAPM_MIXER("Digital L2 Playback Mixer", | 
 | 1311 | 			TWL4030_REG_AVDAC_CTL, 3, 0, NULL, 0), | 
 | 1312 | 	SND_SOC_DAPM_MIXER("Digital Voice Playback Mixer", | 
 | 1313 | 			TWL4030_REG_AVDAC_CTL, 4, 0, NULL, 0), | 
 | 1314 |  | 
 | 1315 | 	/* Analog mixers, power control for the physical PGAs */ | 
 | 1316 | 	SND_SOC_DAPM_MIXER("Analog R1 Playback Mixer", | 
 | 1317 | 			TWL4030_REG_ARXR1_APGA_CTL, 0, 0, NULL, 0), | 
 | 1318 | 	SND_SOC_DAPM_MIXER("Analog L1 Playback Mixer", | 
 | 1319 | 			TWL4030_REG_ARXL1_APGA_CTL, 0, 0, NULL, 0), | 
 | 1320 | 	SND_SOC_DAPM_MIXER("Analog R2 Playback Mixer", | 
 | 1321 | 			TWL4030_REG_ARXR2_APGA_CTL, 0, 0, NULL, 0), | 
 | 1322 | 	SND_SOC_DAPM_MIXER("Analog L2 Playback Mixer", | 
 | 1323 | 			TWL4030_REG_ARXL2_APGA_CTL, 0, 0, NULL, 0), | 
 | 1324 | 	SND_SOC_DAPM_MIXER("Analog Voice Playback Mixer", | 
 | 1325 | 			TWL4030_REG_VDL_APGA_CTL, 0, 0, NULL, 0), | 
| Peter Ujfalusi | 7393958 | 2009-01-29 14:57:50 +0200 | [diff] [blame] | 1326 |  | 
| Peter Ujfalusi | 7729cf7 | 2009-10-29 11:58:10 +0200 | [diff] [blame] | 1327 | 	SND_SOC_DAPM_SUPPLY("APLL Enable", SND_SOC_NOPM, 0, 0, apll_event, | 
 | 1328 | 			    SND_SOC_DAPM_PRE_PMU|SND_SOC_DAPM_POST_PMD), | 
 | 1329 |  | 
| Peter Ujfalusi | 7b4c734 | 2010-04-29 10:58:08 +0300 | [diff] [blame] | 1330 | 	SND_SOC_DAPM_SUPPLY("AIF Enable", SND_SOC_NOPM, 0, 0, aif_event, | 
 | 1331 | 			    SND_SOC_DAPM_PRE_PMU|SND_SOC_DAPM_POST_PMD), | 
| Peter Ujfalusi | c42a59e | 2010-02-09 15:24:04 +0200 | [diff] [blame] | 1332 |  | 
| Joonyoung Shim | 1a787e7 | 2009-04-22 13:13:34 +0900 | [diff] [blame] | 1333 | 	/* Output MIXER controls */ | 
| Peter Ujfalusi | 5e98a46 | 2008-12-09 12:35:47 +0200 | [diff] [blame] | 1334 | 	/* Earpiece */ | 
| Joonyoung Shim | 1a787e7 | 2009-04-22 13:13:34 +0900 | [diff] [blame] | 1335 | 	SND_SOC_DAPM_MIXER("Earpiece Mixer", SND_SOC_NOPM, 0, 0, | 
 | 1336 | 			&twl4030_dapm_earpiece_controls[0], | 
 | 1337 | 			ARRAY_SIZE(twl4030_dapm_earpiece_controls)), | 
| Peter Ujfalusi | 9008adf | 2009-08-13 15:59:34 +0300 | [diff] [blame] | 1338 | 	SND_SOC_DAPM_PGA_E("Earpiece PGA", SND_SOC_NOPM, | 
 | 1339 | 			0, 0, NULL, 0, earpiecepga_event, | 
 | 1340 | 			SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), | 
| Peter Ujfalusi | 2a6f5c58 | 2008-12-09 12:35:48 +0200 | [diff] [blame] | 1341 | 	/* PreDrivL/R */ | 
| Joonyoung Shim | 1a787e7 | 2009-04-22 13:13:34 +0900 | [diff] [blame] | 1342 | 	SND_SOC_DAPM_MIXER("PredriveL Mixer", SND_SOC_NOPM, 0, 0, | 
 | 1343 | 			&twl4030_dapm_predrivel_controls[0], | 
 | 1344 | 			ARRAY_SIZE(twl4030_dapm_predrivel_controls)), | 
| Peter Ujfalusi | 9008adf | 2009-08-13 15:59:34 +0300 | [diff] [blame] | 1345 | 	SND_SOC_DAPM_PGA_E("PredriveL PGA", SND_SOC_NOPM, | 
 | 1346 | 			0, 0, NULL, 0, predrivelpga_event, | 
 | 1347 | 			SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), | 
| Joonyoung Shim | 1a787e7 | 2009-04-22 13:13:34 +0900 | [diff] [blame] | 1348 | 	SND_SOC_DAPM_MIXER("PredriveR Mixer", SND_SOC_NOPM, 0, 0, | 
 | 1349 | 			&twl4030_dapm_predriver_controls[0], | 
 | 1350 | 			ARRAY_SIZE(twl4030_dapm_predriver_controls)), | 
| Peter Ujfalusi | 9008adf | 2009-08-13 15:59:34 +0300 | [diff] [blame] | 1351 | 	SND_SOC_DAPM_PGA_E("PredriveR PGA", SND_SOC_NOPM, | 
 | 1352 | 			0, 0, NULL, 0, predriverpga_event, | 
 | 1353 | 			SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), | 
| Peter Ujfalusi | dfad21a | 2008-12-09 12:35:49 +0200 | [diff] [blame] | 1354 | 	/* HeadsetL/R */ | 
| Peter Ujfalusi | 6943c92 | 2009-05-18 16:02:05 +0300 | [diff] [blame] | 1355 | 	SND_SOC_DAPM_MIXER("HeadsetL Mixer", SND_SOC_NOPM, 0, 0, | 
| Joonyoung Shim | 1a787e7 | 2009-04-22 13:13:34 +0900 | [diff] [blame] | 1356 | 			&twl4030_dapm_hsol_controls[0], | 
| Peter Ujfalusi | 6943c92 | 2009-05-18 16:02:05 +0300 | [diff] [blame] | 1357 | 			ARRAY_SIZE(twl4030_dapm_hsol_controls)), | 
 | 1358 | 	SND_SOC_DAPM_PGA_E("HeadsetL PGA", SND_SOC_NOPM, | 
 | 1359 | 			0, 0, NULL, 0, headsetlpga_event, | 
| Joonyoung Shim | 1a787e7 | 2009-04-22 13:13:34 +0900 | [diff] [blame] | 1360 | 			SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), | 
 | 1361 | 	SND_SOC_DAPM_MIXER("HeadsetR Mixer", SND_SOC_NOPM, 0, 0, | 
 | 1362 | 			&twl4030_dapm_hsor_controls[0], | 
 | 1363 | 			ARRAY_SIZE(twl4030_dapm_hsor_controls)), | 
| Peter Ujfalusi | 6943c92 | 2009-05-18 16:02:05 +0300 | [diff] [blame] | 1364 | 	SND_SOC_DAPM_PGA_E("HeadsetR PGA", SND_SOC_NOPM, | 
 | 1365 | 			0, 0, NULL, 0, headsetrpga_event, | 
 | 1366 | 			SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), | 
| Peter Ujfalusi | 5152d8c | 2008-12-09 12:35:50 +0200 | [diff] [blame] | 1367 | 	/* CarkitL/R */ | 
| Joonyoung Shim | 1a787e7 | 2009-04-22 13:13:34 +0900 | [diff] [blame] | 1368 | 	SND_SOC_DAPM_MIXER("CarkitL Mixer", SND_SOC_NOPM, 0, 0, | 
 | 1369 | 			&twl4030_dapm_carkitl_controls[0], | 
 | 1370 | 			ARRAY_SIZE(twl4030_dapm_carkitl_controls)), | 
| Peter Ujfalusi | 9008adf | 2009-08-13 15:59:34 +0300 | [diff] [blame] | 1371 | 	SND_SOC_DAPM_PGA_E("CarkitL PGA", SND_SOC_NOPM, | 
 | 1372 | 			0, 0, NULL, 0, carkitlpga_event, | 
 | 1373 | 			SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), | 
| Joonyoung Shim | 1a787e7 | 2009-04-22 13:13:34 +0900 | [diff] [blame] | 1374 | 	SND_SOC_DAPM_MIXER("CarkitR Mixer", SND_SOC_NOPM, 0, 0, | 
 | 1375 | 			&twl4030_dapm_carkitr_controls[0], | 
 | 1376 | 			ARRAY_SIZE(twl4030_dapm_carkitr_controls)), | 
| Peter Ujfalusi | 9008adf | 2009-08-13 15:59:34 +0300 | [diff] [blame] | 1377 | 	SND_SOC_DAPM_PGA_E("CarkitR PGA", SND_SOC_NOPM, | 
 | 1378 | 			0, 0, NULL, 0, carkitrpga_event, | 
 | 1379 | 			SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), | 
| Joonyoung Shim | 1a787e7 | 2009-04-22 13:13:34 +0900 | [diff] [blame] | 1380 |  | 
 | 1381 | 	/* Output MUX controls */ | 
| Peter Ujfalusi | df33980 | 2008-12-09 12:35:51 +0200 | [diff] [blame] | 1382 | 	/* HandsfreeL/R */ | 
| Peter Ujfalusi | 5a2e9a4 | 2009-05-25 11:12:11 +0300 | [diff] [blame] | 1383 | 	SND_SOC_DAPM_MUX("HandsfreeL Mux", SND_SOC_NOPM, 0, 0, | 
 | 1384 | 		&twl4030_dapm_handsfreel_control), | 
| Lopez Cruz, Misael | e3c7dbb | 2009-06-25 12:36:14 -0500 | [diff] [blame] | 1385 | 	SND_SOC_DAPM_SWITCH("HandsfreeL", SND_SOC_NOPM, 0, 0, | 
| Peter Ujfalusi | 0f89bdc | 2009-05-25 11:12:13 +0300 | [diff] [blame] | 1386 | 			&twl4030_dapm_handsfreelmute_control), | 
| Peter Ujfalusi | 5a2e9a4 | 2009-05-25 11:12:11 +0300 | [diff] [blame] | 1387 | 	SND_SOC_DAPM_PGA_E("HandsfreeL PGA", SND_SOC_NOPM, | 
 | 1388 | 			0, 0, NULL, 0, handsfreelpga_event, | 
 | 1389 | 			SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), | 
 | 1390 | 	SND_SOC_DAPM_MUX("HandsfreeR Mux", SND_SOC_NOPM, 5, 0, | 
 | 1391 | 		&twl4030_dapm_handsfreer_control), | 
| Lopez Cruz, Misael | e3c7dbb | 2009-06-25 12:36:14 -0500 | [diff] [blame] | 1392 | 	SND_SOC_DAPM_SWITCH("HandsfreeR", SND_SOC_NOPM, 0, 0, | 
| Peter Ujfalusi | 0f89bdc | 2009-05-25 11:12:13 +0300 | [diff] [blame] | 1393 | 			&twl4030_dapm_handsfreermute_control), | 
| Peter Ujfalusi | 5a2e9a4 | 2009-05-25 11:12:11 +0300 | [diff] [blame] | 1394 | 	SND_SOC_DAPM_PGA_E("HandsfreeR PGA", SND_SOC_NOPM, | 
 | 1395 | 			0, 0, NULL, 0, handsfreerpga_event, | 
 | 1396 | 			SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), | 
| Peter Ujfalusi | 376f783 | 2009-05-05 08:55:47 +0300 | [diff] [blame] | 1397 | 	/* Vibra */ | 
| Jari Vanhala | 86139a1 | 2009-10-29 11:58:09 +0200 | [diff] [blame] | 1398 | 	SND_SOC_DAPM_MUX_E("Vibra Mux", TWL4030_REG_VIBRA_CTL, 0, 0, | 
 | 1399 | 			   &twl4030_dapm_vibra_control, vibramux_event, | 
 | 1400 | 			   SND_SOC_DAPM_PRE_PMU), | 
| Peter Ujfalusi | 376f783 | 2009-05-05 08:55:47 +0300 | [diff] [blame] | 1401 | 	SND_SOC_DAPM_MUX("Vibra Route", SND_SOC_NOPM, 0, 0, | 
 | 1402 | 		&twl4030_dapm_vibrapath_control), | 
| Peter Ujfalusi | 5e98a46 | 2008-12-09 12:35:47 +0200 | [diff] [blame] | 1403 |  | 
| Peter Ujfalusi | 276c622 | 2008-12-31 10:08:38 +0200 | [diff] [blame] | 1404 | 	/* Introducing four virtual ADC, since TWL4030 have four channel for | 
 | 1405 | 	   capture */ | 
 | 1406 | 	SND_SOC_DAPM_ADC("ADC Virtual Left1", "Left Front Capture", | 
 | 1407 | 		SND_SOC_NOPM, 0, 0), | 
 | 1408 | 	SND_SOC_DAPM_ADC("ADC Virtual Right1", "Right Front Capture", | 
 | 1409 | 		SND_SOC_NOPM, 0, 0), | 
 | 1410 | 	SND_SOC_DAPM_ADC("ADC Virtual Left2", "Left Rear Capture", | 
 | 1411 | 		SND_SOC_NOPM, 0, 0), | 
 | 1412 | 	SND_SOC_DAPM_ADC("ADC Virtual Right2", "Right Rear Capture", | 
 | 1413 | 		SND_SOC_NOPM, 0, 0), | 
 | 1414 |  | 
 | 1415 | 	/* Analog/Digital mic path selection. | 
 | 1416 | 	   TX1 Left/Right: either analog Left/Right or Digimic0 | 
 | 1417 | 	   TX2 Left/Right: either analog Left/Right or Digimic1 */ | 
| Peter Ujfalusi | bda7d2a | 2010-08-03 12:01:01 +0300 | [diff] [blame] | 1418 | 	SND_SOC_DAPM_MUX("TX1 Capture Route", SND_SOC_NOPM, 0, 0, | 
 | 1419 | 		&twl4030_dapm_micpathtx1_control), | 
 | 1420 | 	SND_SOC_DAPM_MUX("TX2 Capture Route", SND_SOC_NOPM, 0, 0, | 
 | 1421 | 		&twl4030_dapm_micpathtx2_control), | 
| Peter Ujfalusi | 276c622 | 2008-12-31 10:08:38 +0200 | [diff] [blame] | 1422 |  | 
| Joonyoung Shim | 97b8096 | 2009-05-11 20:36:08 +0900 | [diff] [blame] | 1423 | 	/* Analog input mixers for the capture amplifiers */ | 
| Peter Ujfalusi | 9028935 | 2009-08-14 08:44:00 +0300 | [diff] [blame] | 1424 | 	SND_SOC_DAPM_MIXER("Analog Left", | 
| Joonyoung Shim | 97b8096 | 2009-05-11 20:36:08 +0900 | [diff] [blame] | 1425 | 		TWL4030_REG_ANAMICL, 4, 0, | 
 | 1426 | 		&twl4030_dapm_analoglmic_controls[0], | 
 | 1427 | 		ARRAY_SIZE(twl4030_dapm_analoglmic_controls)), | 
| Peter Ujfalusi | 9028935 | 2009-08-14 08:44:00 +0300 | [diff] [blame] | 1428 | 	SND_SOC_DAPM_MIXER("Analog Right", | 
| Joonyoung Shim | 97b8096 | 2009-05-11 20:36:08 +0900 | [diff] [blame] | 1429 | 		TWL4030_REG_ANAMICR, 4, 0, | 
 | 1430 | 		&twl4030_dapm_analogrmic_controls[0], | 
 | 1431 | 		ARRAY_SIZE(twl4030_dapm_analogrmic_controls)), | 
| Peter Ujfalusi | 276c622 | 2008-12-31 10:08:38 +0200 | [diff] [blame] | 1432 |  | 
| Peter Ujfalusi | fb2a2f8 | 2009-01-27 11:29:42 +0200 | [diff] [blame] | 1433 | 	SND_SOC_DAPM_PGA("ADC Physical Left", | 
 | 1434 | 		TWL4030_REG_AVADC_CTL, 3, 0, NULL, 0), | 
 | 1435 | 	SND_SOC_DAPM_PGA("ADC Physical Right", | 
 | 1436 | 		TWL4030_REG_AVADC_CTL, 1, 0, NULL, 0), | 
| Peter Ujfalusi | 276c622 | 2008-12-31 10:08:38 +0200 | [diff] [blame] | 1437 |  | 
| Peter Ujfalusi | 01ea6ba | 2010-07-20 15:49:09 +0300 | [diff] [blame] | 1438 | 	SND_SOC_DAPM_PGA_E("Digimic0 Enable", | 
 | 1439 | 		TWL4030_REG_ADCMICSEL, 1, 0, NULL, 0, | 
 | 1440 | 		digimic_event, SND_SOC_DAPM_POST_PMU), | 
 | 1441 | 	SND_SOC_DAPM_PGA_E("Digimic1 Enable", | 
 | 1442 | 		TWL4030_REG_ADCMICSEL, 3, 0, NULL, 0, | 
 | 1443 | 		digimic_event, SND_SOC_DAPM_POST_PMU), | 
| Peter Ujfalusi | 276c622 | 2008-12-31 10:08:38 +0200 | [diff] [blame] | 1444 |  | 
| Peter Ujfalusi | bda7d2a | 2010-08-03 12:01:01 +0300 | [diff] [blame] | 1445 | 	SND_SOC_DAPM_SUPPLY("micbias1 select", TWL4030_REG_MICBIAS_CTL, 5, 0, | 
 | 1446 | 			    NULL, 0), | 
 | 1447 | 	SND_SOC_DAPM_SUPPLY("micbias2 select", TWL4030_REG_MICBIAS_CTL, 6, 0, | 
 | 1448 | 			    NULL, 0), | 
 | 1449 |  | 
| Peter Ujfalusi | 276c622 | 2008-12-31 10:08:38 +0200 | [diff] [blame] | 1450 | 	SND_SOC_DAPM_MICBIAS("Mic Bias 1", TWL4030_REG_MICBIAS_CTL, 0, 0), | 
 | 1451 | 	SND_SOC_DAPM_MICBIAS("Mic Bias 2", TWL4030_REG_MICBIAS_CTL, 1, 0), | 
 | 1452 | 	SND_SOC_DAPM_MICBIAS("Headset Mic Bias", TWL4030_REG_MICBIAS_CTL, 2, 0), | 
| Peter Ujfalusi | 7393958 | 2009-01-29 14:57:50 +0200 | [diff] [blame] | 1453 |  | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 1454 | }; | 
 | 1455 |  | 
 | 1456 | static const struct snd_soc_dapm_route intercon[] = { | 
| Peter Ujfalusi | 4005d39 | 2009-05-18 16:02:04 +0300 | [diff] [blame] | 1457 | 	{"Digital L1 Playback Mixer", NULL, "DAC Left1"}, | 
 | 1458 | 	{"Digital R1 Playback Mixer", NULL, "DAC Right1"}, | 
 | 1459 | 	{"Digital L2 Playback Mixer", NULL, "DAC Left2"}, | 
 | 1460 | 	{"Digital R2 Playback Mixer", NULL, "DAC Right2"}, | 
 | 1461 | 	{"Digital Voice Playback Mixer", NULL, "DAC Voice"}, | 
| Peter Ujfalusi | 7393958 | 2009-01-29 14:57:50 +0200 | [diff] [blame] | 1462 |  | 
| Peter Ujfalusi | 7729cf7 | 2009-10-29 11:58:10 +0200 | [diff] [blame] | 1463 | 	/* Supply for the digital part (APLL) */ | 
| Peter Ujfalusi | 7729cf7 | 2009-10-29 11:58:10 +0200 | [diff] [blame] | 1464 | 	{"Digital Voice Playback Mixer", NULL, "APLL Enable"}, | 
 | 1465 |  | 
| Peter Ujfalusi | 27eeb1f | 2010-07-13 12:07:44 +0300 | [diff] [blame] | 1466 | 	{"DAC Left1", NULL, "AIF Enable"}, | 
 | 1467 | 	{"DAC Right1", NULL, "AIF Enable"}, | 
 | 1468 | 	{"DAC Left2", NULL, "AIF Enable"}, | 
 | 1469 | 	{"DAC Right1", NULL, "AIF Enable"}, | 
 | 1470 |  | 
| Peter Ujfalusi | c42a59e | 2010-02-09 15:24:04 +0200 | [diff] [blame] | 1471 | 	{"Digital R2 Playback Mixer", NULL, "AIF Enable"}, | 
 | 1472 | 	{"Digital L2 Playback Mixer", NULL, "AIF Enable"}, | 
 | 1473 |  | 
| Peter Ujfalusi | 4005d39 | 2009-05-18 16:02:04 +0300 | [diff] [blame] | 1474 | 	{"Analog L1 Playback Mixer", NULL, "Digital L1 Playback Mixer"}, | 
 | 1475 | 	{"Analog R1 Playback Mixer", NULL, "Digital R1 Playback Mixer"}, | 
 | 1476 | 	{"Analog L2 Playback Mixer", NULL, "Digital L2 Playback Mixer"}, | 
 | 1477 | 	{"Analog R2 Playback Mixer", NULL, "Digital R2 Playback Mixer"}, | 
 | 1478 | 	{"Analog Voice Playback Mixer", NULL, "Digital Voice Playback Mixer"}, | 
| Joonyoung Shim | 1a787e7 | 2009-04-22 13:13:34 +0900 | [diff] [blame] | 1479 |  | 
| Peter Ujfalusi | 5e98a46 | 2008-12-09 12:35:47 +0200 | [diff] [blame] | 1480 | 	/* Internal playback routings */ | 
 | 1481 | 	/* Earpiece */ | 
| Peter Ujfalusi | 4005d39 | 2009-05-18 16:02:04 +0300 | [diff] [blame] | 1482 | 	{"Earpiece Mixer", "Voice", "Analog Voice Playback Mixer"}, | 
 | 1483 | 	{"Earpiece Mixer", "AudioL1", "Analog L1 Playback Mixer"}, | 
 | 1484 | 	{"Earpiece Mixer", "AudioL2", "Analog L2 Playback Mixer"}, | 
 | 1485 | 	{"Earpiece Mixer", "AudioR1", "Analog R1 Playback Mixer"}, | 
| Peter Ujfalusi | 9008adf | 2009-08-13 15:59:34 +0300 | [diff] [blame] | 1486 | 	{"Earpiece PGA", NULL, "Earpiece Mixer"}, | 
| Peter Ujfalusi | 2a6f5c58 | 2008-12-09 12:35:48 +0200 | [diff] [blame] | 1487 | 	/* PreDrivL */ | 
| Peter Ujfalusi | 4005d39 | 2009-05-18 16:02:04 +0300 | [diff] [blame] | 1488 | 	{"PredriveL Mixer", "Voice", "Analog Voice Playback Mixer"}, | 
 | 1489 | 	{"PredriveL Mixer", "AudioL1", "Analog L1 Playback Mixer"}, | 
 | 1490 | 	{"PredriveL Mixer", "AudioL2", "Analog L2 Playback Mixer"}, | 
 | 1491 | 	{"PredriveL Mixer", "AudioR2", "Analog R2 Playback Mixer"}, | 
| Peter Ujfalusi | 9008adf | 2009-08-13 15:59:34 +0300 | [diff] [blame] | 1492 | 	{"PredriveL PGA", NULL, "PredriveL Mixer"}, | 
| Peter Ujfalusi | 2a6f5c58 | 2008-12-09 12:35:48 +0200 | [diff] [blame] | 1493 | 	/* PreDrivR */ | 
| Peter Ujfalusi | 4005d39 | 2009-05-18 16:02:04 +0300 | [diff] [blame] | 1494 | 	{"PredriveR Mixer", "Voice", "Analog Voice Playback Mixer"}, | 
 | 1495 | 	{"PredriveR Mixer", "AudioR1", "Analog R1 Playback Mixer"}, | 
 | 1496 | 	{"PredriveR Mixer", "AudioR2", "Analog R2 Playback Mixer"}, | 
 | 1497 | 	{"PredriveR Mixer", "AudioL2", "Analog L2 Playback Mixer"}, | 
| Peter Ujfalusi | 9008adf | 2009-08-13 15:59:34 +0300 | [diff] [blame] | 1498 | 	{"PredriveR PGA", NULL, "PredriveR Mixer"}, | 
| Peter Ujfalusi | dfad21a | 2008-12-09 12:35:49 +0200 | [diff] [blame] | 1499 | 	/* HeadsetL */ | 
| Peter Ujfalusi | 4005d39 | 2009-05-18 16:02:04 +0300 | [diff] [blame] | 1500 | 	{"HeadsetL Mixer", "Voice", "Analog Voice Playback Mixer"}, | 
 | 1501 | 	{"HeadsetL Mixer", "AudioL1", "Analog L1 Playback Mixer"}, | 
 | 1502 | 	{"HeadsetL Mixer", "AudioL2", "Analog L2 Playback Mixer"}, | 
| Peter Ujfalusi | 6943c92 | 2009-05-18 16:02:05 +0300 | [diff] [blame] | 1503 | 	{"HeadsetL PGA", NULL, "HeadsetL Mixer"}, | 
| Peter Ujfalusi | dfad21a | 2008-12-09 12:35:49 +0200 | [diff] [blame] | 1504 | 	/* HeadsetR */ | 
| Peter Ujfalusi | 4005d39 | 2009-05-18 16:02:04 +0300 | [diff] [blame] | 1505 | 	{"HeadsetR Mixer", "Voice", "Analog Voice Playback Mixer"}, | 
 | 1506 | 	{"HeadsetR Mixer", "AudioR1", "Analog R1 Playback Mixer"}, | 
 | 1507 | 	{"HeadsetR Mixer", "AudioR2", "Analog R2 Playback Mixer"}, | 
| Peter Ujfalusi | 6943c92 | 2009-05-18 16:02:05 +0300 | [diff] [blame] | 1508 | 	{"HeadsetR PGA", NULL, "HeadsetR Mixer"}, | 
| Peter Ujfalusi | 5152d8c | 2008-12-09 12:35:50 +0200 | [diff] [blame] | 1509 | 	/* CarkitL */ | 
| Peter Ujfalusi | 4005d39 | 2009-05-18 16:02:04 +0300 | [diff] [blame] | 1510 | 	{"CarkitL Mixer", "Voice", "Analog Voice Playback Mixer"}, | 
 | 1511 | 	{"CarkitL Mixer", "AudioL1", "Analog L1 Playback Mixer"}, | 
 | 1512 | 	{"CarkitL Mixer", "AudioL2", "Analog L2 Playback Mixer"}, | 
| Peter Ujfalusi | 9008adf | 2009-08-13 15:59:34 +0300 | [diff] [blame] | 1513 | 	{"CarkitL PGA", NULL, "CarkitL Mixer"}, | 
| Peter Ujfalusi | 5152d8c | 2008-12-09 12:35:50 +0200 | [diff] [blame] | 1514 | 	/* CarkitR */ | 
| Peter Ujfalusi | 4005d39 | 2009-05-18 16:02:04 +0300 | [diff] [blame] | 1515 | 	{"CarkitR Mixer", "Voice", "Analog Voice Playback Mixer"}, | 
 | 1516 | 	{"CarkitR Mixer", "AudioR1", "Analog R1 Playback Mixer"}, | 
 | 1517 | 	{"CarkitR Mixer", "AudioR2", "Analog R2 Playback Mixer"}, | 
| Peter Ujfalusi | 9008adf | 2009-08-13 15:59:34 +0300 | [diff] [blame] | 1518 | 	{"CarkitR PGA", NULL, "CarkitR Mixer"}, | 
| Peter Ujfalusi | df33980 | 2008-12-09 12:35:51 +0200 | [diff] [blame] | 1519 | 	/* HandsfreeL */ | 
| Peter Ujfalusi | 4005d39 | 2009-05-18 16:02:04 +0300 | [diff] [blame] | 1520 | 	{"HandsfreeL Mux", "Voice", "Analog Voice Playback Mixer"}, | 
 | 1521 | 	{"HandsfreeL Mux", "AudioL1", "Analog L1 Playback Mixer"}, | 
 | 1522 | 	{"HandsfreeL Mux", "AudioL2", "Analog L2 Playback Mixer"}, | 
 | 1523 | 	{"HandsfreeL Mux", "AudioR2", "Analog R2 Playback Mixer"}, | 
| Lopez Cruz, Misael | e3c7dbb | 2009-06-25 12:36:14 -0500 | [diff] [blame] | 1524 | 	{"HandsfreeL", "Switch", "HandsfreeL Mux"}, | 
 | 1525 | 	{"HandsfreeL PGA", NULL, "HandsfreeL"}, | 
| Peter Ujfalusi | df33980 | 2008-12-09 12:35:51 +0200 | [diff] [blame] | 1526 | 	/* HandsfreeR */ | 
| Peter Ujfalusi | 4005d39 | 2009-05-18 16:02:04 +0300 | [diff] [blame] | 1527 | 	{"HandsfreeR Mux", "Voice", "Analog Voice Playback Mixer"}, | 
 | 1528 | 	{"HandsfreeR Mux", "AudioR1", "Analog R1 Playback Mixer"}, | 
 | 1529 | 	{"HandsfreeR Mux", "AudioR2", "Analog R2 Playback Mixer"}, | 
 | 1530 | 	{"HandsfreeR Mux", "AudioL2", "Analog L2 Playback Mixer"}, | 
| Lopez Cruz, Misael | e3c7dbb | 2009-06-25 12:36:14 -0500 | [diff] [blame] | 1531 | 	{"HandsfreeR", "Switch", "HandsfreeR Mux"}, | 
 | 1532 | 	{"HandsfreeR PGA", NULL, "HandsfreeR"}, | 
| Peter Ujfalusi | 376f783 | 2009-05-05 08:55:47 +0300 | [diff] [blame] | 1533 | 	/* Vibra */ | 
 | 1534 | 	{"Vibra Mux", "AudioL1", "DAC Left1"}, | 
 | 1535 | 	{"Vibra Mux", "AudioR1", "DAC Right1"}, | 
 | 1536 | 	{"Vibra Mux", "AudioL2", "DAC Left2"}, | 
 | 1537 | 	{"Vibra Mux", "AudioR2", "DAC Right2"}, | 
| Peter Ujfalusi | 5e98a46 | 2008-12-09 12:35:47 +0200 | [diff] [blame] | 1538 |  | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 1539 | 	/* outputs */ | 
| Peter Ujfalusi | 7b4c734 | 2010-04-29 10:58:08 +0300 | [diff] [blame] | 1540 | 	/* Must be always connected (for AIF and APLL) */ | 
| Peter Ujfalusi | 27eeb1f | 2010-07-13 12:07:44 +0300 | [diff] [blame] | 1541 | 	{"Virtual HiFi OUT", NULL, "DAC Left1"}, | 
 | 1542 | 	{"Virtual HiFi OUT", NULL, "DAC Right1"}, | 
 | 1543 | 	{"Virtual HiFi OUT", NULL, "DAC Left2"}, | 
 | 1544 | 	{"Virtual HiFi OUT", NULL, "DAC Right2"}, | 
| Peter Ujfalusi | 7b4c734 | 2010-04-29 10:58:08 +0300 | [diff] [blame] | 1545 | 	/* Must be always connected (for APLL) */ | 
 | 1546 | 	{"Virtual Voice OUT", NULL, "Digital Voice Playback Mixer"}, | 
 | 1547 | 	/* Physical outputs */ | 
| Peter Ujfalusi | 9008adf | 2009-08-13 15:59:34 +0300 | [diff] [blame] | 1548 | 	{"EARPIECE", NULL, "Earpiece PGA"}, | 
 | 1549 | 	{"PREDRIVEL", NULL, "PredriveL PGA"}, | 
 | 1550 | 	{"PREDRIVER", NULL, "PredriveR PGA"}, | 
| Peter Ujfalusi | 6943c92 | 2009-05-18 16:02:05 +0300 | [diff] [blame] | 1551 | 	{"HSOL", NULL, "HeadsetL PGA"}, | 
 | 1552 | 	{"HSOR", NULL, "HeadsetR PGA"}, | 
| Peter Ujfalusi | 9008adf | 2009-08-13 15:59:34 +0300 | [diff] [blame] | 1553 | 	{"CARKITL", NULL, "CarkitL PGA"}, | 
 | 1554 | 	{"CARKITR", NULL, "CarkitR PGA"}, | 
| Peter Ujfalusi | 5a2e9a4 | 2009-05-25 11:12:11 +0300 | [diff] [blame] | 1555 | 	{"HFL", NULL, "HandsfreeL PGA"}, | 
 | 1556 | 	{"HFR", NULL, "HandsfreeR PGA"}, | 
| Peter Ujfalusi | 376f783 | 2009-05-05 08:55:47 +0300 | [diff] [blame] | 1557 | 	{"Vibra Route", "Audio", "Vibra Mux"}, | 
 | 1558 | 	{"VIBRA", NULL, "Vibra Route"}, | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 1559 |  | 
| Peter Ujfalusi | 276c622 | 2008-12-31 10:08:38 +0200 | [diff] [blame] | 1560 | 	/* Capture path */ | 
| Peter Ujfalusi | 7b4c734 | 2010-04-29 10:58:08 +0300 | [diff] [blame] | 1561 | 	/* Must be always connected (for AIF and APLL) */ | 
 | 1562 | 	{"ADC Virtual Left1", NULL, "Virtual HiFi IN"}, | 
 | 1563 | 	{"ADC Virtual Right1", NULL, "Virtual HiFi IN"}, | 
 | 1564 | 	{"ADC Virtual Left2", NULL, "Virtual HiFi IN"}, | 
 | 1565 | 	{"ADC Virtual Right2", NULL, "Virtual HiFi IN"}, | 
 | 1566 | 	/* Physical inputs */ | 
| Peter Ujfalusi | 9028935 | 2009-08-14 08:44:00 +0300 | [diff] [blame] | 1567 | 	{"Analog Left", "Main Mic Capture Switch", "MAINMIC"}, | 
 | 1568 | 	{"Analog Left", "Headset Mic Capture Switch", "HSMIC"}, | 
 | 1569 | 	{"Analog Left", "AUXL Capture Switch", "AUXL"}, | 
 | 1570 | 	{"Analog Left", "Carkit Mic Capture Switch", "CARKITMIC"}, | 
| Peter Ujfalusi | 276c622 | 2008-12-31 10:08:38 +0200 | [diff] [blame] | 1571 |  | 
| Peter Ujfalusi | 9028935 | 2009-08-14 08:44:00 +0300 | [diff] [blame] | 1572 | 	{"Analog Right", "Sub Mic Capture Switch", "SUBMIC"}, | 
 | 1573 | 	{"Analog Right", "AUXR Capture Switch", "AUXR"}, | 
| Peter Ujfalusi | 276c622 | 2008-12-31 10:08:38 +0200 | [diff] [blame] | 1574 |  | 
| Peter Ujfalusi | 9028935 | 2009-08-14 08:44:00 +0300 | [diff] [blame] | 1575 | 	{"ADC Physical Left", NULL, "Analog Left"}, | 
 | 1576 | 	{"ADC Physical Right", NULL, "Analog Right"}, | 
| Peter Ujfalusi | 276c622 | 2008-12-31 10:08:38 +0200 | [diff] [blame] | 1577 |  | 
 | 1578 | 	{"Digimic0 Enable", NULL, "DIGIMIC0"}, | 
 | 1579 | 	{"Digimic1 Enable", NULL, "DIGIMIC1"}, | 
 | 1580 |  | 
| Peter Ujfalusi | bda7d2a | 2010-08-03 12:01:01 +0300 | [diff] [blame] | 1581 | 	{"DIGIMIC0", NULL, "micbias1 select"}, | 
 | 1582 | 	{"DIGIMIC1", NULL, "micbias2 select"}, | 
 | 1583 |  | 
| Peter Ujfalusi | 276c622 | 2008-12-31 10:08:38 +0200 | [diff] [blame] | 1584 | 	/* TX1 Left capture path */ | 
| Peter Ujfalusi | fb2a2f8 | 2009-01-27 11:29:42 +0200 | [diff] [blame] | 1585 | 	{"TX1 Capture Route", "Analog", "ADC Physical Left"}, | 
| Peter Ujfalusi | 276c622 | 2008-12-31 10:08:38 +0200 | [diff] [blame] | 1586 | 	{"TX1 Capture Route", "Digimic0", "Digimic0 Enable"}, | 
 | 1587 | 	/* TX1 Right capture path */ | 
| Peter Ujfalusi | fb2a2f8 | 2009-01-27 11:29:42 +0200 | [diff] [blame] | 1588 | 	{"TX1 Capture Route", "Analog", "ADC Physical Right"}, | 
| Peter Ujfalusi | 276c622 | 2008-12-31 10:08:38 +0200 | [diff] [blame] | 1589 | 	{"TX1 Capture Route", "Digimic0", "Digimic0 Enable"}, | 
 | 1590 | 	/* TX2 Left capture path */ | 
| Peter Ujfalusi | fb2a2f8 | 2009-01-27 11:29:42 +0200 | [diff] [blame] | 1591 | 	{"TX2 Capture Route", "Analog", "ADC Physical Left"}, | 
| Peter Ujfalusi | 276c622 | 2008-12-31 10:08:38 +0200 | [diff] [blame] | 1592 | 	{"TX2 Capture Route", "Digimic1", "Digimic1 Enable"}, | 
 | 1593 | 	/* TX2 Right capture path */ | 
| Peter Ujfalusi | fb2a2f8 | 2009-01-27 11:29:42 +0200 | [diff] [blame] | 1594 | 	{"TX2 Capture Route", "Analog", "ADC Physical Right"}, | 
| Peter Ujfalusi | 276c622 | 2008-12-31 10:08:38 +0200 | [diff] [blame] | 1595 | 	{"TX2 Capture Route", "Digimic1", "Digimic1 Enable"}, | 
 | 1596 |  | 
 | 1597 | 	{"ADC Virtual Left1", NULL, "TX1 Capture Route"}, | 
 | 1598 | 	{"ADC Virtual Right1", NULL, "TX1 Capture Route"}, | 
 | 1599 | 	{"ADC Virtual Left2", NULL, "TX2 Capture Route"}, | 
 | 1600 | 	{"ADC Virtual Right2", NULL, "TX2 Capture Route"}, | 
 | 1601 |  | 
| Peter Ujfalusi | c42a59e | 2010-02-09 15:24:04 +0200 | [diff] [blame] | 1602 | 	{"ADC Virtual Left1", NULL, "AIF Enable"}, | 
 | 1603 | 	{"ADC Virtual Right1", NULL, "AIF Enable"}, | 
 | 1604 | 	{"ADC Virtual Left2", NULL, "AIF Enable"}, | 
 | 1605 | 	{"ADC Virtual Right2", NULL, "AIF Enable"}, | 
 | 1606 |  | 
| Peter Ujfalusi | 7393958 | 2009-01-29 14:57:50 +0200 | [diff] [blame] | 1607 | 	/* Analog bypass routes */ | 
| Peter Ujfalusi | 9028935 | 2009-08-14 08:44:00 +0300 | [diff] [blame] | 1608 | 	{"Right1 Analog Loopback", "Switch", "Analog Right"}, | 
 | 1609 | 	{"Left1 Analog Loopback", "Switch", "Analog Left"}, | 
 | 1610 | 	{"Right2 Analog Loopback", "Switch", "Analog Right"}, | 
 | 1611 | 	{"Left2 Analog Loopback", "Switch", "Analog Left"}, | 
 | 1612 | 	{"Voice Analog Loopback", "Switch", "Analog Left"}, | 
| Peter Ujfalusi | 7393958 | 2009-01-29 14:57:50 +0200 | [diff] [blame] | 1613 |  | 
| Peter Ujfalusi | 78e08e2 | 2009-10-28 10:57:04 +0200 | [diff] [blame] | 1614 | 	/* Supply for the Analog loopbacks */ | 
 | 1615 | 	{"Right1 Analog Loopback", NULL, "FM Loop Enable"}, | 
 | 1616 | 	{"Left1 Analog Loopback", NULL, "FM Loop Enable"}, | 
 | 1617 | 	{"Right2 Analog Loopback", NULL, "FM Loop Enable"}, | 
 | 1618 | 	{"Left2 Analog Loopback", NULL, "FM Loop Enable"}, | 
 | 1619 | 	{"Voice Analog Loopback", NULL, "FM Loop Enable"}, | 
 | 1620 |  | 
| Peter Ujfalusi | 7393958 | 2009-01-29 14:57:50 +0200 | [diff] [blame] | 1621 | 	{"Analog R1 Playback Mixer", NULL, "Right1 Analog Loopback"}, | 
 | 1622 | 	{"Analog L1 Playback Mixer", NULL, "Left1 Analog Loopback"}, | 
 | 1623 | 	{"Analog R2 Playback Mixer", NULL, "Right2 Analog Loopback"}, | 
 | 1624 | 	{"Analog L2 Playback Mixer", NULL, "Left2 Analog Loopback"}, | 
| Lopez Cruz, Misael | fcd274a | 2009-04-30 21:47:22 -0500 | [diff] [blame] | 1625 | 	{"Analog Voice Playback Mixer", NULL, "Voice Analog Loopback"}, | 
| Peter Ujfalusi | 7393958 | 2009-01-29 14:57:50 +0200 | [diff] [blame] | 1626 |  | 
| Peter Ujfalusi | 6bab83f | 2009-02-18 14:39:05 +0200 | [diff] [blame] | 1627 | 	/* Digital bypass routes */ | 
 | 1628 | 	{"Right Digital Loopback", "Volume", "TX1 Capture Route"}, | 
 | 1629 | 	{"Left Digital Loopback", "Volume", "TX1 Capture Route"}, | 
| Lopez Cruz, Misael | ee8f689 | 2009-04-30 21:48:08 -0500 | [diff] [blame] | 1630 | 	{"Voice Digital Loopback", "Volume", "TX2 Capture Route"}, | 
| Peter Ujfalusi | 6bab83f | 2009-02-18 14:39:05 +0200 | [diff] [blame] | 1631 |  | 
| Peter Ujfalusi | 4005d39 | 2009-05-18 16:02:04 +0300 | [diff] [blame] | 1632 | 	{"Digital R2 Playback Mixer", NULL, "Right Digital Loopback"}, | 
 | 1633 | 	{"Digital L2 Playback Mixer", NULL, "Left Digital Loopback"}, | 
 | 1634 | 	{"Digital Voice Playback Mixer", NULL, "Voice Digital Loopback"}, | 
| Peter Ujfalusi | 6bab83f | 2009-02-18 14:39:05 +0200 | [diff] [blame] | 1635 |  | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 1636 | }; | 
 | 1637 |  | 
 | 1638 | static int twl4030_add_widgets(struct snd_soc_codec *codec) | 
 | 1639 | { | 
| Liam Girdwood | ce6120c | 2010-11-05 15:53:46 +0200 | [diff] [blame] | 1640 | 	struct snd_soc_dapm_context *dapm = &codec->dapm; | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 1641 |  | 
| Liam Girdwood | ce6120c | 2010-11-05 15:53:46 +0200 | [diff] [blame] | 1642 | 	snd_soc_dapm_new_controls(dapm, twl4030_dapm_widgets, | 
 | 1643 | 				 ARRAY_SIZE(twl4030_dapm_widgets)); | 
 | 1644 | 	snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 1645 |  | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 1646 | 	return 0; | 
 | 1647 | } | 
 | 1648 |  | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 1649 | static int twl4030_set_bias_level(struct snd_soc_codec *codec, | 
 | 1650 | 				  enum snd_soc_bias_level level) | 
 | 1651 | { | 
 | 1652 | 	switch (level) { | 
 | 1653 | 	case SND_SOC_BIAS_ON: | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 1654 | 		break; | 
 | 1655 | 	case SND_SOC_BIAS_PREPARE: | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 1656 | 		break; | 
 | 1657 | 	case SND_SOC_BIAS_STANDBY: | 
| Liam Girdwood | ce6120c | 2010-11-05 15:53:46 +0200 | [diff] [blame] | 1658 | 		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) | 
| Peter Ujfalusi | ee4ccac7 | 2010-05-26 11:38:17 +0300 | [diff] [blame] | 1659 | 			twl4030_codec_enable(codec, 1); | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 1660 | 		break; | 
 | 1661 | 	case SND_SOC_BIAS_OFF: | 
| Peter Ujfalusi | cbd2db1 | 2010-05-26 11:38:15 +0300 | [diff] [blame] | 1662 | 		twl4030_codec_enable(codec, 0); | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 1663 | 		break; | 
 | 1664 | 	} | 
| Liam Girdwood | ce6120c | 2010-11-05 15:53:46 +0200 | [diff] [blame] | 1665 | 	codec->dapm.bias_level = level; | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 1666 |  | 
 | 1667 | 	return 0; | 
 | 1668 | } | 
 | 1669 |  | 
| Peter Ujfalusi | 6b87a91 | 2009-04-17 15:55:08 +0300 | [diff] [blame] | 1670 | static void twl4030_constraints(struct twl4030_priv *twl4030, | 
 | 1671 | 				struct snd_pcm_substream *mst_substream) | 
 | 1672 | { | 
 | 1673 | 	struct snd_pcm_substream *slv_substream; | 
 | 1674 |  | 
 | 1675 | 	/* Pick the stream, which need to be constrained */ | 
 | 1676 | 	if (mst_substream == twl4030->master_substream) | 
 | 1677 | 		slv_substream = twl4030->slave_substream; | 
 | 1678 | 	else if (mst_substream == twl4030->slave_substream) | 
 | 1679 | 		slv_substream = twl4030->master_substream; | 
 | 1680 | 	else /* This should not happen.. */ | 
 | 1681 | 		return; | 
 | 1682 |  | 
 | 1683 | 	/* Set the constraints according to the already configured stream */ | 
 | 1684 | 	snd_pcm_hw_constraint_minmax(slv_substream->runtime, | 
 | 1685 | 				SNDRV_PCM_HW_PARAM_RATE, | 
 | 1686 | 				twl4030->rate, | 
 | 1687 | 				twl4030->rate); | 
 | 1688 |  | 
 | 1689 | 	snd_pcm_hw_constraint_minmax(slv_substream->runtime, | 
 | 1690 | 				SNDRV_PCM_HW_PARAM_SAMPLE_BITS, | 
 | 1691 | 				twl4030->sample_bits, | 
 | 1692 | 				twl4030->sample_bits); | 
 | 1693 |  | 
 | 1694 | 	snd_pcm_hw_constraint_minmax(slv_substream->runtime, | 
 | 1695 | 				SNDRV_PCM_HW_PARAM_CHANNELS, | 
 | 1696 | 				twl4030->channels, | 
 | 1697 | 				twl4030->channels); | 
 | 1698 | } | 
 | 1699 |  | 
| Peter Ujfalusi | 8a1f936 | 2009-04-23 14:36:49 +0300 | [diff] [blame] | 1700 | /* In case of 4 channel mode, the RX1 L/R for playback and the TX2 L/R for | 
 | 1701 |  * capture has to be enabled/disabled. */ | 
 | 1702 | static void twl4030_tdm_enable(struct snd_soc_codec *codec, int direction, | 
 | 1703 | 				int enable) | 
 | 1704 | { | 
 | 1705 | 	u8 reg, mask; | 
 | 1706 |  | 
 | 1707 | 	reg = twl4030_read_reg_cache(codec, TWL4030_REG_OPTION); | 
 | 1708 |  | 
 | 1709 | 	if (direction == SNDRV_PCM_STREAM_PLAYBACK) | 
 | 1710 | 		mask = TWL4030_ARXL1_VRX_EN | TWL4030_ARXR1_EN; | 
 | 1711 | 	else | 
 | 1712 | 		mask = TWL4030_ATXL2_VTXL_EN | TWL4030_ATXR2_VTXR_EN; | 
 | 1713 |  | 
 | 1714 | 	if (enable) | 
 | 1715 | 		reg |= mask; | 
 | 1716 | 	else | 
 | 1717 | 		reg &= ~mask; | 
 | 1718 |  | 
 | 1719 | 	twl4030_write(codec, TWL4030_REG_OPTION, reg); | 
 | 1720 | } | 
 | 1721 |  | 
| Peter Ujfalusi | d6648da | 2009-04-07 09:14:00 +0300 | [diff] [blame] | 1722 | static int twl4030_startup(struct snd_pcm_substream *substream, | 
 | 1723 | 			   struct snd_soc_dai *dai) | 
| Peter Ujfalusi | 7220b9f | 2009-03-27 10:39:08 +0200 | [diff] [blame] | 1724 | { | 
 | 1725 | 	struct snd_soc_pcm_runtime *rtd = substream->private_data; | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 1726 | 	struct snd_soc_codec *codec = rtd->codec; | 
| Mark Brown | b2c812e | 2010-04-14 15:35:19 +0900 | [diff] [blame] | 1727 | 	struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); | 
| Peter Ujfalusi | 7220b9f | 2009-03-27 10:39:08 +0200 | [diff] [blame] | 1728 |  | 
| Peter Ujfalusi | dcdeda4 | 2010-12-14 13:45:29 +0200 | [diff] [blame] | 1729 | 	snd_pcm_hw_constraint_msbits(substream->runtime, 0, 32, 24); | 
| Peter Ujfalusi | 7220b9f | 2009-03-27 10:39:08 +0200 | [diff] [blame] | 1730 | 	if (twl4030->master_substream) { | 
| Peter Ujfalusi | 7220b9f | 2009-03-27 10:39:08 +0200 | [diff] [blame] | 1731 | 		twl4030->slave_substream = substream; | 
| Peter Ujfalusi | 6b87a91 | 2009-04-17 15:55:08 +0300 | [diff] [blame] | 1732 | 		/* The DAI has one configuration for playback and capture, so | 
 | 1733 | 		 * if the DAI has been already configured then constrain this | 
 | 1734 | 		 * substream to match it. */ | 
 | 1735 | 		if (twl4030->configured) | 
 | 1736 | 			twl4030_constraints(twl4030, twl4030->master_substream); | 
 | 1737 | 	} else { | 
| Peter Ujfalusi | 8a1f936 | 2009-04-23 14:36:49 +0300 | [diff] [blame] | 1738 | 		if (!(twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE) & | 
 | 1739 | 			TWL4030_OPTION_1)) { | 
 | 1740 | 			/* In option2 4 channel is not supported, set the | 
 | 1741 | 			 * constraint for the first stream for channels, the | 
 | 1742 | 			 * second stream will 'inherit' this cosntraint */ | 
 | 1743 | 			snd_pcm_hw_constraint_minmax(substream->runtime, | 
 | 1744 | 						SNDRV_PCM_HW_PARAM_CHANNELS, | 
 | 1745 | 						2, 2); | 
 | 1746 | 		} | 
| Peter Ujfalusi | 7220b9f | 2009-03-27 10:39:08 +0200 | [diff] [blame] | 1747 | 		twl4030->master_substream = substream; | 
| Peter Ujfalusi | 6b87a91 | 2009-04-17 15:55:08 +0300 | [diff] [blame] | 1748 | 	} | 
| Peter Ujfalusi | 7220b9f | 2009-03-27 10:39:08 +0200 | [diff] [blame] | 1749 |  | 
 | 1750 | 	return 0; | 
 | 1751 | } | 
 | 1752 |  | 
| Peter Ujfalusi | d6648da | 2009-04-07 09:14:00 +0300 | [diff] [blame] | 1753 | static void twl4030_shutdown(struct snd_pcm_substream *substream, | 
 | 1754 | 			     struct snd_soc_dai *dai) | 
| Peter Ujfalusi | 7220b9f | 2009-03-27 10:39:08 +0200 | [diff] [blame] | 1755 | { | 
 | 1756 | 	struct snd_soc_pcm_runtime *rtd = substream->private_data; | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 1757 | 	struct snd_soc_codec *codec = rtd->codec; | 
| Mark Brown | b2c812e | 2010-04-14 15:35:19 +0900 | [diff] [blame] | 1758 | 	struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); | 
| Peter Ujfalusi | 7220b9f | 2009-03-27 10:39:08 +0200 | [diff] [blame] | 1759 |  | 
 | 1760 | 	if (twl4030->master_substream == substream) | 
 | 1761 | 		twl4030->master_substream = twl4030->slave_substream; | 
 | 1762 |  | 
 | 1763 | 	twl4030->slave_substream = NULL; | 
| Peter Ujfalusi | 6b87a91 | 2009-04-17 15:55:08 +0300 | [diff] [blame] | 1764 |  | 
 | 1765 | 	/* If all streams are closed, or the remaining stream has not yet | 
 | 1766 | 	 * been configured than set the DAI as not configured. */ | 
 | 1767 | 	if (!twl4030->master_substream) | 
 | 1768 | 		twl4030->configured = 0; | 
 | 1769 | 	 else if (!twl4030->master_substream->runtime->channels) | 
 | 1770 | 		twl4030->configured = 0; | 
| Peter Ujfalusi | 8a1f936 | 2009-04-23 14:36:49 +0300 | [diff] [blame] | 1771 |  | 
 | 1772 | 	 /* If the closing substream had 4 channel, do the necessary cleanup */ | 
 | 1773 | 	if (substream->runtime->channels == 4) | 
 | 1774 | 		twl4030_tdm_enable(codec, substream->stream, 0); | 
| Peter Ujfalusi | 7220b9f | 2009-03-27 10:39:08 +0200 | [diff] [blame] | 1775 | } | 
 | 1776 |  | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 1777 | static int twl4030_hw_params(struct snd_pcm_substream *substream, | 
| Mark Brown | dee89c4 | 2008-11-18 22:11:38 +0000 | [diff] [blame] | 1778 | 			   struct snd_pcm_hw_params *params, | 
 | 1779 | 			   struct snd_soc_dai *dai) | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 1780 | { | 
 | 1781 | 	struct snd_soc_pcm_runtime *rtd = substream->private_data; | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 1782 | 	struct snd_soc_codec *codec = rtd->codec; | 
| Mark Brown | b2c812e | 2010-04-14 15:35:19 +0900 | [diff] [blame] | 1783 | 	struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 1784 | 	u8 mode, old_mode, format, old_format; | 
 | 1785 |  | 
| Peter Ujfalusi | 8a1f936 | 2009-04-23 14:36:49 +0300 | [diff] [blame] | 1786 | 	 /* If the substream has 4 channel, do the necessary setup */ | 
 | 1787 | 	if (params_channels(params) == 4) { | 
| Peter Ujfalusi | eaf1ac8 | 2009-06-01 14:06:40 +0300 | [diff] [blame] | 1788 | 		format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF); | 
 | 1789 | 		mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE); | 
 | 1790 |  | 
 | 1791 | 		/* Safety check: are we in the correct operating mode and | 
 | 1792 | 		 * the interface is in TDM mode? */ | 
 | 1793 | 		if ((mode & TWL4030_OPTION_1) && | 
 | 1794 | 		    ((format & TWL4030_AIF_FORMAT) == TWL4030_AIF_FORMAT_TDM)) | 
| Peter Ujfalusi | 8a1f936 | 2009-04-23 14:36:49 +0300 | [diff] [blame] | 1795 | 			twl4030_tdm_enable(codec, substream->stream, 1); | 
 | 1796 | 		else | 
 | 1797 | 			return -EINVAL; | 
 | 1798 | 	} | 
 | 1799 |  | 
| Peter Ujfalusi | 6b87a91 | 2009-04-17 15:55:08 +0300 | [diff] [blame] | 1800 | 	if (twl4030->configured) | 
 | 1801 | 		/* Ignoring hw_params for already configured DAI */ | 
| Peter Ujfalusi | 7220b9f | 2009-03-27 10:39:08 +0200 | [diff] [blame] | 1802 | 		return 0; | 
 | 1803 |  | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 1804 | 	/* bit rate */ | 
 | 1805 | 	old_mode = twl4030_read_reg_cache(codec, | 
 | 1806 | 			TWL4030_REG_CODEC_MODE) & ~TWL4030_CODECPDZ; | 
 | 1807 | 	mode = old_mode & ~TWL4030_APLL_RATE; | 
 | 1808 |  | 
 | 1809 | 	switch (params_rate(params)) { | 
 | 1810 | 	case 8000: | 
 | 1811 | 		mode |= TWL4030_APLL_RATE_8000; | 
 | 1812 | 		break; | 
 | 1813 | 	case 11025: | 
 | 1814 | 		mode |= TWL4030_APLL_RATE_11025; | 
 | 1815 | 		break; | 
 | 1816 | 	case 12000: | 
 | 1817 | 		mode |= TWL4030_APLL_RATE_12000; | 
 | 1818 | 		break; | 
 | 1819 | 	case 16000: | 
 | 1820 | 		mode |= TWL4030_APLL_RATE_16000; | 
 | 1821 | 		break; | 
 | 1822 | 	case 22050: | 
 | 1823 | 		mode |= TWL4030_APLL_RATE_22050; | 
 | 1824 | 		break; | 
 | 1825 | 	case 24000: | 
 | 1826 | 		mode |= TWL4030_APLL_RATE_24000; | 
 | 1827 | 		break; | 
 | 1828 | 	case 32000: | 
 | 1829 | 		mode |= TWL4030_APLL_RATE_32000; | 
 | 1830 | 		break; | 
 | 1831 | 	case 44100: | 
 | 1832 | 		mode |= TWL4030_APLL_RATE_44100; | 
 | 1833 | 		break; | 
 | 1834 | 	case 48000: | 
 | 1835 | 		mode |= TWL4030_APLL_RATE_48000; | 
 | 1836 | 		break; | 
| Peter Ujfalusi | 103f211 | 2009-04-03 14:39:05 +0300 | [diff] [blame] | 1837 | 	case 96000: | 
 | 1838 | 		mode |= TWL4030_APLL_RATE_96000; | 
 | 1839 | 		break; | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 1840 | 	default: | 
 | 1841 | 		printk(KERN_ERR "TWL4030 hw params: unknown rate %d\n", | 
 | 1842 | 			params_rate(params)); | 
 | 1843 | 		return -EINVAL; | 
 | 1844 | 	} | 
 | 1845 |  | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 1846 | 	/* sample size */ | 
 | 1847 | 	old_format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF); | 
 | 1848 | 	format = old_format; | 
 | 1849 | 	format &= ~TWL4030_DATA_WIDTH; | 
 | 1850 | 	switch (params_format(params)) { | 
 | 1851 | 	case SNDRV_PCM_FORMAT_S16_LE: | 
 | 1852 | 		format |= TWL4030_DATA_WIDTH_16S_16W; | 
 | 1853 | 		break; | 
| Peter Ujfalusi | dcdeda4 | 2010-12-14 13:45:29 +0200 | [diff] [blame] | 1854 | 	case SNDRV_PCM_FORMAT_S32_LE: | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 1855 | 		format |= TWL4030_DATA_WIDTH_32S_24W; | 
 | 1856 | 		break; | 
 | 1857 | 	default: | 
 | 1858 | 		printk(KERN_ERR "TWL4030 hw params: unknown format %d\n", | 
 | 1859 | 			params_format(params)); | 
 | 1860 | 		return -EINVAL; | 
 | 1861 | 	} | 
 | 1862 |  | 
| Peter Ujfalusi | 2046f17 | 2010-05-26 11:38:20 +0300 | [diff] [blame] | 1863 | 	if (format != old_format || mode != old_mode) { | 
 | 1864 | 		if (twl4030->codec_powered) { | 
 | 1865 | 			/* | 
 | 1866 | 			 * If the codec is powered, than we need to toggle the | 
 | 1867 | 			 * codec power. | 
 | 1868 | 			 */ | 
 | 1869 | 			twl4030_codec_enable(codec, 0); | 
 | 1870 | 			twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode); | 
 | 1871 | 			twl4030_write(codec, TWL4030_REG_AUDIO_IF, format); | 
 | 1872 | 			twl4030_codec_enable(codec, 1); | 
 | 1873 | 		} else { | 
 | 1874 | 			twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode); | 
 | 1875 | 			twl4030_write(codec, TWL4030_REG_AUDIO_IF, format); | 
 | 1876 | 		} | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 1877 | 	} | 
| Peter Ujfalusi | 6b87a91 | 2009-04-17 15:55:08 +0300 | [diff] [blame] | 1878 |  | 
 | 1879 | 	/* Store the important parameters for the DAI configuration and set | 
 | 1880 | 	 * the DAI as configured */ | 
 | 1881 | 	twl4030->configured = 1; | 
 | 1882 | 	twl4030->rate = params_rate(params); | 
 | 1883 | 	twl4030->sample_bits = hw_param_interval(params, | 
 | 1884 | 					SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min; | 
 | 1885 | 	twl4030->channels = params_channels(params); | 
 | 1886 |  | 
 | 1887 | 	/* If both playback and capture streams are open, and one of them | 
 | 1888 | 	 * is setting the hw parameters right now (since we are here), set | 
 | 1889 | 	 * constraints to the other stream to match the current one. */ | 
 | 1890 | 	if (twl4030->slave_substream) | 
 | 1891 | 		twl4030_constraints(twl4030, substream); | 
 | 1892 |  | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 1893 | 	return 0; | 
 | 1894 | } | 
 | 1895 |  | 
 | 1896 | static int twl4030_set_dai_sysclk(struct snd_soc_dai *codec_dai, | 
 | 1897 | 		int clk_id, unsigned int freq, int dir) | 
 | 1898 | { | 
 | 1899 | 	struct snd_soc_codec *codec = codec_dai->codec; | 
| Mark Brown | b2c812e | 2010-04-14 15:35:19 +0900 | [diff] [blame] | 1900 | 	struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 1901 |  | 
 | 1902 | 	switch (freq) { | 
 | 1903 | 	case 19200000: | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 1904 | 	case 26000000: | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 1905 | 	case 38400000: | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 1906 | 		break; | 
 | 1907 | 	default: | 
| Peter Ujfalusi | 68d0195 | 2009-11-04 09:58:20 +0200 | [diff] [blame] | 1908 | 		dev_err(codec->dev, "Unsupported APLL mclk: %u\n", freq); | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 1909 | 		return -EINVAL; | 
 | 1910 | 	} | 
 | 1911 |  | 
| Peter Ujfalusi | 68d0195 | 2009-11-04 09:58:20 +0200 | [diff] [blame] | 1912 | 	if ((freq / 1000) != twl4030->sysclk) { | 
 | 1913 | 		dev_err(codec->dev, | 
 | 1914 | 			"Mismatch in APLL mclk: %u (configured: %u)\n", | 
 | 1915 | 			freq, twl4030->sysclk * 1000); | 
 | 1916 | 		return -EINVAL; | 
 | 1917 | 	} | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 1918 |  | 
 | 1919 | 	return 0; | 
 | 1920 | } | 
 | 1921 |  | 
 | 1922 | static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai, | 
 | 1923 | 			     unsigned int fmt) | 
 | 1924 | { | 
 | 1925 | 	struct snd_soc_codec *codec = codec_dai->codec; | 
| Peter Ujfalusi | 2046f17 | 2010-05-26 11:38:20 +0300 | [diff] [blame] | 1926 | 	struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 1927 | 	u8 old_format, format; | 
 | 1928 |  | 
 | 1929 | 	/* get format */ | 
 | 1930 | 	old_format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF); | 
 | 1931 | 	format = old_format; | 
 | 1932 |  | 
 | 1933 | 	/* set master/slave audio interface */ | 
 | 1934 | 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | 
 | 1935 | 	case SND_SOC_DAIFMT_CBM_CFM: | 
 | 1936 | 		format &= ~(TWL4030_AIF_SLAVE_EN); | 
| Grazvydas Ignotas | e18c94d | 2008-11-05 23:51:05 +0200 | [diff] [blame] | 1937 | 		format &= ~(TWL4030_CLK256FS_EN); | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 1938 | 		break; | 
 | 1939 | 	case SND_SOC_DAIFMT_CBS_CFS: | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 1940 | 		format |= TWL4030_AIF_SLAVE_EN; | 
| Grazvydas Ignotas | e18c94d | 2008-11-05 23:51:05 +0200 | [diff] [blame] | 1941 | 		format |= TWL4030_CLK256FS_EN; | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 1942 | 		break; | 
 | 1943 | 	default: | 
 | 1944 | 		return -EINVAL; | 
 | 1945 | 	} | 
 | 1946 |  | 
 | 1947 | 	/* interface format */ | 
 | 1948 | 	format &= ~TWL4030_AIF_FORMAT; | 
 | 1949 | 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | 
 | 1950 | 	case SND_SOC_DAIFMT_I2S: | 
 | 1951 | 		format |= TWL4030_AIF_FORMAT_CODEC; | 
 | 1952 | 		break; | 
| Peter Ujfalusi | 8a1f936 | 2009-04-23 14:36:49 +0300 | [diff] [blame] | 1953 | 	case SND_SOC_DAIFMT_DSP_A: | 
 | 1954 | 		format |= TWL4030_AIF_FORMAT_TDM; | 
 | 1955 | 		break; | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 1956 | 	default: | 
 | 1957 | 		return -EINVAL; | 
 | 1958 | 	} | 
 | 1959 |  | 
 | 1960 | 	if (format != old_format) { | 
| Peter Ujfalusi | 2046f17 | 2010-05-26 11:38:20 +0300 | [diff] [blame] | 1961 | 		if (twl4030->codec_powered) { | 
 | 1962 | 			/* | 
 | 1963 | 			 * If the codec is powered, than we need to toggle the | 
 | 1964 | 			 * codec power. | 
 | 1965 | 			 */ | 
 | 1966 | 			twl4030_codec_enable(codec, 0); | 
 | 1967 | 			twl4030_write(codec, TWL4030_REG_AUDIO_IF, format); | 
 | 1968 | 			twl4030_codec_enable(codec, 1); | 
 | 1969 | 		} else { | 
 | 1970 | 			twl4030_write(codec, TWL4030_REG_AUDIO_IF, format); | 
 | 1971 | 		} | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 1972 | 	} | 
 | 1973 |  | 
 | 1974 | 	return 0; | 
 | 1975 | } | 
 | 1976 |  | 
| Lopez Cruz, Misael | 6814044 | 2009-07-03 02:21:39 -0500 | [diff] [blame] | 1977 | static int twl4030_set_tristate(struct snd_soc_dai *dai, int tristate) | 
 | 1978 | { | 
 | 1979 | 	struct snd_soc_codec *codec = dai->codec; | 
 | 1980 | 	u8 reg = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF); | 
 | 1981 |  | 
 | 1982 | 	if (tristate) | 
 | 1983 | 		reg |= TWL4030_AIF_TRI_EN; | 
 | 1984 | 	else | 
 | 1985 | 		reg &= ~TWL4030_AIF_TRI_EN; | 
 | 1986 |  | 
 | 1987 | 	return twl4030_write(codec, TWL4030_REG_AUDIO_IF, reg); | 
 | 1988 | } | 
 | 1989 |  | 
| Misael Lopez Cruz | b7a755a | 2009-05-17 20:02:31 -0500 | [diff] [blame] | 1990 | /* In case of voice mode, the RX1 L(VRX) for downlink and the TX2 L/R | 
 | 1991 |  * (VTXL, VTXR) for uplink has to be enabled/disabled. */ | 
 | 1992 | static void twl4030_voice_enable(struct snd_soc_codec *codec, int direction, | 
 | 1993 | 				int enable) | 
 | 1994 | { | 
 | 1995 | 	u8 reg, mask; | 
 | 1996 |  | 
 | 1997 | 	reg = twl4030_read_reg_cache(codec, TWL4030_REG_OPTION); | 
 | 1998 |  | 
 | 1999 | 	if (direction == SNDRV_PCM_STREAM_PLAYBACK) | 
 | 2000 | 		mask = TWL4030_ARXL1_VRX_EN; | 
 | 2001 | 	else | 
 | 2002 | 		mask = TWL4030_ATXL2_VTXL_EN | TWL4030_ATXR2_VTXR_EN; | 
 | 2003 |  | 
 | 2004 | 	if (enable) | 
 | 2005 | 		reg |= mask; | 
 | 2006 | 	else | 
 | 2007 | 		reg &= ~mask; | 
 | 2008 |  | 
 | 2009 | 	twl4030_write(codec, TWL4030_REG_OPTION, reg); | 
 | 2010 | } | 
 | 2011 |  | 
| Joonyoung Shim | 7154b3e | 2009-04-20 19:21:35 +0900 | [diff] [blame] | 2012 | static int twl4030_voice_startup(struct snd_pcm_substream *substream, | 
 | 2013 | 		struct snd_soc_dai *dai) | 
 | 2014 | { | 
 | 2015 | 	struct snd_soc_pcm_runtime *rtd = substream->private_data; | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 2016 | 	struct snd_soc_codec *codec = rtd->codec; | 
| Mark Brown | b2c812e | 2010-04-14 15:35:19 +0900 | [diff] [blame] | 2017 | 	struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); | 
| Joonyoung Shim | 7154b3e | 2009-04-20 19:21:35 +0900 | [diff] [blame] | 2018 | 	u8 mode; | 
 | 2019 |  | 
 | 2020 | 	/* If the system master clock is not 26MHz, the voice PCM interface is | 
 | 2021 | 	 * not avilable. | 
 | 2022 | 	 */ | 
| Peter Ujfalusi | 68d0195 | 2009-11-04 09:58:20 +0200 | [diff] [blame] | 2023 | 	if (twl4030->sysclk != 26000) { | 
 | 2024 | 		dev_err(codec->dev, "The board is configured for %u Hz, while" | 
 | 2025 | 			"the Voice interface needs 26MHz APLL mclk\n", | 
 | 2026 | 			twl4030->sysclk * 1000); | 
| Joonyoung Shim | 7154b3e | 2009-04-20 19:21:35 +0900 | [diff] [blame] | 2027 | 		return -EINVAL; | 
 | 2028 | 	} | 
 | 2029 |  | 
 | 2030 | 	/* If the codec mode is not option2, the voice PCM interface is not | 
 | 2031 | 	 * avilable. | 
 | 2032 | 	 */ | 
 | 2033 | 	mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE) | 
 | 2034 | 		& TWL4030_OPT_MODE; | 
 | 2035 |  | 
 | 2036 | 	if (mode != TWL4030_OPTION_2) { | 
 | 2037 | 		printk(KERN_ERR "TWL4030 voice startup: " | 
 | 2038 | 			"the codec mode is not option2\n"); | 
 | 2039 | 		return -EINVAL; | 
 | 2040 | 	} | 
 | 2041 |  | 
 | 2042 | 	return 0; | 
 | 2043 | } | 
 | 2044 |  | 
| Misael Lopez Cruz | b7a755a | 2009-05-17 20:02:31 -0500 | [diff] [blame] | 2045 | static void twl4030_voice_shutdown(struct snd_pcm_substream *substream, | 
 | 2046 | 				struct snd_soc_dai *dai) | 
 | 2047 | { | 
 | 2048 | 	struct snd_soc_pcm_runtime *rtd = substream->private_data; | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 2049 | 	struct snd_soc_codec *codec = rtd->codec; | 
| Misael Lopez Cruz | b7a755a | 2009-05-17 20:02:31 -0500 | [diff] [blame] | 2050 |  | 
 | 2051 | 	/* Enable voice digital filters */ | 
 | 2052 | 	twl4030_voice_enable(codec, substream->stream, 0); | 
 | 2053 | } | 
 | 2054 |  | 
| Joonyoung Shim | 7154b3e | 2009-04-20 19:21:35 +0900 | [diff] [blame] | 2055 | static int twl4030_voice_hw_params(struct snd_pcm_substream *substream, | 
 | 2056 | 		struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) | 
 | 2057 | { | 
 | 2058 | 	struct snd_soc_pcm_runtime *rtd = substream->private_data; | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 2059 | 	struct snd_soc_codec *codec = rtd->codec; | 
| Peter Ujfalusi | 2046f17 | 2010-05-26 11:38:20 +0300 | [diff] [blame] | 2060 | 	struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); | 
| Joonyoung Shim | 7154b3e | 2009-04-20 19:21:35 +0900 | [diff] [blame] | 2061 | 	u8 old_mode, mode; | 
 | 2062 |  | 
| Misael Lopez Cruz | b7a755a | 2009-05-17 20:02:31 -0500 | [diff] [blame] | 2063 | 	/* Enable voice digital filters */ | 
 | 2064 | 	twl4030_voice_enable(codec, substream->stream, 1); | 
 | 2065 |  | 
| Joonyoung Shim | 7154b3e | 2009-04-20 19:21:35 +0900 | [diff] [blame] | 2066 | 	/* bit rate */ | 
 | 2067 | 	old_mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE) | 
 | 2068 | 		& ~(TWL4030_CODECPDZ); | 
 | 2069 | 	mode = old_mode; | 
 | 2070 |  | 
 | 2071 | 	switch (params_rate(params)) { | 
 | 2072 | 	case 8000: | 
 | 2073 | 		mode &= ~(TWL4030_SEL_16K); | 
 | 2074 | 		break; | 
 | 2075 | 	case 16000: | 
 | 2076 | 		mode |= TWL4030_SEL_16K; | 
 | 2077 | 		break; | 
 | 2078 | 	default: | 
 | 2079 | 		printk(KERN_ERR "TWL4030 voice hw params: unknown rate %d\n", | 
 | 2080 | 			params_rate(params)); | 
 | 2081 | 		return -EINVAL; | 
 | 2082 | 	} | 
 | 2083 |  | 
 | 2084 | 	if (mode != old_mode) { | 
| Peter Ujfalusi | 2046f17 | 2010-05-26 11:38:20 +0300 | [diff] [blame] | 2085 | 		if (twl4030->codec_powered) { | 
 | 2086 | 			/* | 
 | 2087 | 			 * If the codec is powered, than we need to toggle the | 
 | 2088 | 			 * codec power. | 
 | 2089 | 			 */ | 
 | 2090 | 			twl4030_codec_enable(codec, 0); | 
 | 2091 | 			twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode); | 
 | 2092 | 			twl4030_codec_enable(codec, 1); | 
 | 2093 | 		} else { | 
 | 2094 | 			twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode); | 
 | 2095 | 		} | 
| Joonyoung Shim | 7154b3e | 2009-04-20 19:21:35 +0900 | [diff] [blame] | 2096 | 	} | 
 | 2097 |  | 
 | 2098 | 	return 0; | 
 | 2099 | } | 
 | 2100 |  | 
 | 2101 | static int twl4030_voice_set_dai_sysclk(struct snd_soc_dai *codec_dai, | 
 | 2102 | 		int clk_id, unsigned int freq, int dir) | 
 | 2103 | { | 
 | 2104 | 	struct snd_soc_codec *codec = codec_dai->codec; | 
| Takashi Iwai | d4a8ca2 | 2010-04-20 08:20:31 +0200 | [diff] [blame] | 2105 | 	struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); | 
| Joonyoung Shim | 7154b3e | 2009-04-20 19:21:35 +0900 | [diff] [blame] | 2106 |  | 
| Peter Ujfalusi | 68d0195 | 2009-11-04 09:58:20 +0200 | [diff] [blame] | 2107 | 	if (freq != 26000000) { | 
 | 2108 | 		dev_err(codec->dev, "Unsupported APLL mclk: %u, the Voice" | 
 | 2109 | 			"interface needs 26MHz APLL mclk\n", freq); | 
| Joonyoung Shim | 7154b3e | 2009-04-20 19:21:35 +0900 | [diff] [blame] | 2110 | 		return -EINVAL; | 
 | 2111 | 	} | 
| Peter Ujfalusi | 68d0195 | 2009-11-04 09:58:20 +0200 | [diff] [blame] | 2112 | 	if ((freq / 1000) != twl4030->sysclk) { | 
 | 2113 | 		dev_err(codec->dev, | 
 | 2114 | 			"Mismatch in APLL mclk: %u (configured: %u)\n", | 
 | 2115 | 			freq, twl4030->sysclk * 1000); | 
 | 2116 | 		return -EINVAL; | 
 | 2117 | 	} | 
| Joonyoung Shim | 7154b3e | 2009-04-20 19:21:35 +0900 | [diff] [blame] | 2118 | 	return 0; | 
 | 2119 | } | 
 | 2120 |  | 
 | 2121 | static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai, | 
 | 2122 | 		unsigned int fmt) | 
 | 2123 | { | 
 | 2124 | 	struct snd_soc_codec *codec = codec_dai->codec; | 
| Peter Ujfalusi | 2046f17 | 2010-05-26 11:38:20 +0300 | [diff] [blame] | 2125 | 	struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); | 
| Joonyoung Shim | 7154b3e | 2009-04-20 19:21:35 +0900 | [diff] [blame] | 2126 | 	u8 old_format, format; | 
 | 2127 |  | 
 | 2128 | 	/* get format */ | 
 | 2129 | 	old_format = twl4030_read_reg_cache(codec, TWL4030_REG_VOICE_IF); | 
 | 2130 | 	format = old_format; | 
 | 2131 |  | 
 | 2132 | 	/* set master/slave audio interface */ | 
 | 2133 | 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | 
| Lopez Cruz, Misael | c264301 | 2009-06-19 03:23:42 -0500 | [diff] [blame] | 2134 | 	case SND_SOC_DAIFMT_CBM_CFM: | 
| Joonyoung Shim | 7154b3e | 2009-04-20 19:21:35 +0900 | [diff] [blame] | 2135 | 		format &= ~(TWL4030_VIF_SLAVE_EN); | 
 | 2136 | 		break; | 
 | 2137 | 	case SND_SOC_DAIFMT_CBS_CFS: | 
 | 2138 | 		format |= TWL4030_VIF_SLAVE_EN; | 
 | 2139 | 		break; | 
 | 2140 | 	default: | 
 | 2141 | 		return -EINVAL; | 
 | 2142 | 	} | 
 | 2143 |  | 
 | 2144 | 	/* clock inversion */ | 
 | 2145 | 	switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | 
 | 2146 | 	case SND_SOC_DAIFMT_IB_NF: | 
 | 2147 | 		format &= ~(TWL4030_VIF_FORMAT); | 
 | 2148 | 		break; | 
 | 2149 | 	case SND_SOC_DAIFMT_NB_IF: | 
 | 2150 | 		format |= TWL4030_VIF_FORMAT; | 
 | 2151 | 		break; | 
 | 2152 | 	default: | 
 | 2153 | 		return -EINVAL; | 
 | 2154 | 	} | 
 | 2155 |  | 
 | 2156 | 	if (format != old_format) { | 
| Peter Ujfalusi | 2046f17 | 2010-05-26 11:38:20 +0300 | [diff] [blame] | 2157 | 		if (twl4030->codec_powered) { | 
 | 2158 | 			/* | 
 | 2159 | 			 * If the codec is powered, than we need to toggle the | 
 | 2160 | 			 * codec power. | 
 | 2161 | 			 */ | 
 | 2162 | 			twl4030_codec_enable(codec, 0); | 
 | 2163 | 			twl4030_write(codec, TWL4030_REG_VOICE_IF, format); | 
 | 2164 | 			twl4030_codec_enable(codec, 1); | 
 | 2165 | 		} else { | 
 | 2166 | 			twl4030_write(codec, TWL4030_REG_VOICE_IF, format); | 
 | 2167 | 		} | 
| Joonyoung Shim | 7154b3e | 2009-04-20 19:21:35 +0900 | [diff] [blame] | 2168 | 	} | 
 | 2169 |  | 
 | 2170 | 	return 0; | 
 | 2171 | } | 
 | 2172 |  | 
| Lopez Cruz, Misael | 6814044 | 2009-07-03 02:21:39 -0500 | [diff] [blame] | 2173 | static int twl4030_voice_set_tristate(struct snd_soc_dai *dai, int tristate) | 
 | 2174 | { | 
 | 2175 | 	struct snd_soc_codec *codec = dai->codec; | 
 | 2176 | 	u8 reg = twl4030_read_reg_cache(codec, TWL4030_REG_VOICE_IF); | 
 | 2177 |  | 
 | 2178 | 	if (tristate) | 
 | 2179 | 		reg |= TWL4030_VIF_TRI_EN; | 
 | 2180 | 	else | 
 | 2181 | 		reg &= ~TWL4030_VIF_TRI_EN; | 
 | 2182 |  | 
 | 2183 | 	return twl4030_write(codec, TWL4030_REG_VOICE_IF, reg); | 
 | 2184 | } | 
 | 2185 |  | 
| Jarkko Nikula | bbba944 | 2008-11-12 17:05:41 +0200 | [diff] [blame] | 2186 | #define TWL4030_RATES	 (SNDRV_PCM_RATE_8000_48000) | 
| Peter Ujfalusi | dcdeda4 | 2010-12-14 13:45:29 +0200 | [diff] [blame] | 2187 | #define TWL4030_FORMATS	 (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE) | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 2188 |  | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 2189 | static struct snd_soc_dai_ops twl4030_dai_hifi_ops = { | 
| Peter Ujfalusi | 7220b9f | 2009-03-27 10:39:08 +0200 | [diff] [blame] | 2190 | 	.startup	= twl4030_startup, | 
 | 2191 | 	.shutdown	= twl4030_shutdown, | 
| Joonyoung Shim | 10d9e3d | 2009-03-16 21:23:35 +0900 | [diff] [blame] | 2192 | 	.hw_params	= twl4030_hw_params, | 
 | 2193 | 	.set_sysclk	= twl4030_set_dai_sysclk, | 
 | 2194 | 	.set_fmt	= twl4030_set_dai_fmt, | 
| Lopez Cruz, Misael | 6814044 | 2009-07-03 02:21:39 -0500 | [diff] [blame] | 2195 | 	.set_tristate	= twl4030_set_tristate, | 
| Joonyoung Shim | 10d9e3d | 2009-03-16 21:23:35 +0900 | [diff] [blame] | 2196 | }; | 
 | 2197 |  | 
| Joonyoung Shim | 7154b3e | 2009-04-20 19:21:35 +0900 | [diff] [blame] | 2198 | static struct snd_soc_dai_ops twl4030_dai_voice_ops = { | 
 | 2199 | 	.startup	= twl4030_voice_startup, | 
| Misael Lopez Cruz | b7a755a | 2009-05-17 20:02:31 -0500 | [diff] [blame] | 2200 | 	.shutdown	= twl4030_voice_shutdown, | 
| Joonyoung Shim | 7154b3e | 2009-04-20 19:21:35 +0900 | [diff] [blame] | 2201 | 	.hw_params	= twl4030_voice_hw_params, | 
 | 2202 | 	.set_sysclk	= twl4030_voice_set_dai_sysclk, | 
 | 2203 | 	.set_fmt	= twl4030_voice_set_dai_fmt, | 
| Lopez Cruz, Misael | 6814044 | 2009-07-03 02:21:39 -0500 | [diff] [blame] | 2204 | 	.set_tristate	= twl4030_voice_set_tristate, | 
| Joonyoung Shim | 7154b3e | 2009-04-20 19:21:35 +0900 | [diff] [blame] | 2205 | }; | 
 | 2206 |  | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 2207 | static struct snd_soc_dai_driver twl4030_dai[] = { | 
| Joonyoung Shim | 7154b3e | 2009-04-20 19:21:35 +0900 | [diff] [blame] | 2208 | { | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 2209 | 	.name = "twl4030-hifi", | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 2210 | 	.playback = { | 
| Peter Ujfalusi | b4852b7 | 2009-05-22 15:12:15 +0300 | [diff] [blame] | 2211 | 		.stream_name = "HiFi Playback", | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 2212 | 		.channels_min = 2, | 
| Peter Ujfalusi | 8a1f936 | 2009-04-23 14:36:49 +0300 | [diff] [blame] | 2213 | 		.channels_max = 4, | 
| Peter Ujfalusi | 31ad0f3 | 2009-03-27 10:39:07 +0200 | [diff] [blame] | 2214 | 		.rates = TWL4030_RATES | SNDRV_PCM_RATE_96000, | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 2215 | 		.formats = TWL4030_FORMATS,}, | 
 | 2216 | 	.capture = { | 
 | 2217 | 		.stream_name = "Capture", | 
 | 2218 | 		.channels_min = 2, | 
| Peter Ujfalusi | 8a1f936 | 2009-04-23 14:36:49 +0300 | [diff] [blame] | 2219 | 		.channels_max = 4, | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 2220 | 		.rates = TWL4030_RATES, | 
 | 2221 | 		.formats = TWL4030_FORMATS,}, | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 2222 | 	.ops = &twl4030_dai_hifi_ops, | 
| Joonyoung Shim | 7154b3e | 2009-04-20 19:21:35 +0900 | [diff] [blame] | 2223 | }, | 
 | 2224 | { | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 2225 | 	.name = "twl4030-voice", | 
| Joonyoung Shim | 7154b3e | 2009-04-20 19:21:35 +0900 | [diff] [blame] | 2226 | 	.playback = { | 
| Peter Ujfalusi | b4852b7 | 2009-05-22 15:12:15 +0300 | [diff] [blame] | 2227 | 		.stream_name = "Voice Playback", | 
| Joonyoung Shim | 7154b3e | 2009-04-20 19:21:35 +0900 | [diff] [blame] | 2228 | 		.channels_min = 1, | 
 | 2229 | 		.channels_max = 1, | 
 | 2230 | 		.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, | 
 | 2231 | 		.formats = SNDRV_PCM_FMTBIT_S16_LE,}, | 
 | 2232 | 	.capture = { | 
 | 2233 | 		.stream_name = "Capture", | 
 | 2234 | 		.channels_min = 1, | 
 | 2235 | 		.channels_max = 2, | 
 | 2236 | 		.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, | 
 | 2237 | 		.formats = SNDRV_PCM_FMTBIT_S16_LE,}, | 
 | 2238 | 	.ops = &twl4030_dai_voice_ops, | 
 | 2239 | }, | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 2240 | }; | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 2241 |  | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 2242 | static int twl4030_soc_suspend(struct snd_soc_codec *codec, pm_message_t state) | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 2243 | { | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 2244 | 	twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF); | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 2245 | 	return 0; | 
 | 2246 | } | 
 | 2247 |  | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 2248 | static int twl4030_soc_resume(struct snd_soc_codec *codec) | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 2249 | { | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 2250 | 	twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 2251 | 	return 0; | 
 | 2252 | } | 
 | 2253 |  | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 2254 | static int twl4030_soc_probe(struct snd_soc_codec *codec) | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 2255 | { | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 2256 | 	struct twl4030_priv *twl4030; | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 2257 |  | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 2258 | 	twl4030 = kzalloc(sizeof(struct twl4030_priv), GFP_KERNEL); | 
 | 2259 | 	if (twl4030 == NULL) { | 
 | 2260 | 		printk("Can not allocate memroy\n"); | 
 | 2261 | 		return -ENOMEM; | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 2262 | 	} | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 2263 | 	snd_soc_codec_set_drvdata(codec, twl4030); | 
 | 2264 | 	/* Set the defaults, and power up the codec */ | 
 | 2265 | 	twl4030->sysclk = twl4030_codec_get_mclk() / 1000; | 
| Liam Girdwood | ce6120c | 2010-11-05 15:53:46 +0200 | [diff] [blame] | 2266 | 	codec->dapm.idle_bias_off = 1; | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 2267 |  | 
 | 2268 | 	twl4030_init_chip(codec); | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 2269 |  | 
| Ian Molton | 3e8e195 | 2009-01-09 00:23:21 +0000 | [diff] [blame] | 2270 | 	snd_soc_add_controls(codec, twl4030_snd_controls, | 
 | 2271 | 				ARRAY_SIZE(twl4030_snd_controls)); | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 2272 | 	twl4030_add_widgets(codec); | 
| Peter Ujfalusi | 7a1fecf | 2009-10-22 13:26:48 +0300 | [diff] [blame] | 2273 | 	return 0; | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 2274 | } | 
 | 2275 |  | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 2276 | static int twl4030_soc_remove(struct snd_soc_codec *codec) | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 2277 | { | 
| Axel Lin | 5b3b0fa | 2010-11-19 17:31:08 +0800 | [diff] [blame] | 2278 | 	struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); | 
 | 2279 |  | 
| Peter Ujfalusi | 5dcba5d | 2010-08-12 09:29:52 +0300 | [diff] [blame] | 2280 | 	/* Reset registers to their chip default before leaving */ | 
 | 2281 | 	twl4030_reset_registers(codec); | 
| Peter Ujfalusi | 7393958 | 2009-01-29 14:57:50 +0200 | [diff] [blame] | 2282 | 	twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF); | 
| Axel Lin | 5b3b0fa | 2010-11-19 17:31:08 +0800 | [diff] [blame] | 2283 | 	kfree(twl4030); | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 2284 | 	return 0; | 
 | 2285 | } | 
 | 2286 |  | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 2287 | static struct snd_soc_codec_driver soc_codec_dev_twl4030 = { | 
 | 2288 | 	.probe = twl4030_soc_probe, | 
 | 2289 | 	.remove = twl4030_soc_remove, | 
 | 2290 | 	.suspend = twl4030_soc_suspend, | 
 | 2291 | 	.resume = twl4030_soc_resume, | 
 | 2292 | 	.read = twl4030_read_reg_cache, | 
 | 2293 | 	.write = twl4030_write, | 
 | 2294 | 	.set_bias_level = twl4030_set_bias_level, | 
 | 2295 | 	.reg_cache_size = sizeof(twl4030_reg), | 
 | 2296 | 	.reg_word_size = sizeof(u8), | 
 | 2297 | 	.reg_cache_default = twl4030_reg, | 
 | 2298 | }; | 
 | 2299 |  | 
| Peter Ujfalusi | 7a1fecf | 2009-10-22 13:26:48 +0300 | [diff] [blame] | 2300 | static int __devinit twl4030_codec_probe(struct platform_device *pdev) | 
 | 2301 | { | 
| Andres Salomon | 0638d56 | 2011-02-17 19:07:20 -0800 | [diff] [blame] | 2302 | 	struct twl4030_codec_audio_data *pdata = mfd_get_data(pdev); | 
| Peter Ujfalusi | 7a1fecf | 2009-10-22 13:26:48 +0300 | [diff] [blame] | 2303 |  | 
| Peter Ujfalusi | 68d0195 | 2009-11-04 09:58:20 +0200 | [diff] [blame] | 2304 | 	if (!pdata) { | 
 | 2305 | 		dev_err(&pdev->dev, "platform_data is missing\n"); | 
| Peter Ujfalusi | 7a1fecf | 2009-10-22 13:26:48 +0300 | [diff] [blame] | 2306 | 		return -EINVAL; | 
 | 2307 | 	} | 
 | 2308 |  | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 2309 | 	return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_twl4030, | 
 | 2310 | 			twl4030_dai, ARRAY_SIZE(twl4030_dai)); | 
| Peter Ujfalusi | 7a1fecf | 2009-10-22 13:26:48 +0300 | [diff] [blame] | 2311 | } | 
 | 2312 |  | 
 | 2313 | static int __devexit twl4030_codec_remove(struct platform_device *pdev) | 
 | 2314 | { | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 2315 | 	snd_soc_unregister_codec(&pdev->dev); | 
| Peter Ujfalusi | 7a1fecf | 2009-10-22 13:26:48 +0300 | [diff] [blame] | 2316 | 	return 0; | 
 | 2317 | } | 
 | 2318 |  | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 2319 | MODULE_ALIAS("platform:twl4030-codec"); | 
| Peter Ujfalusi | 7a1fecf | 2009-10-22 13:26:48 +0300 | [diff] [blame] | 2320 |  | 
 | 2321 | static struct platform_driver twl4030_codec_driver = { | 
 | 2322 | 	.probe		= twl4030_codec_probe, | 
 | 2323 | 	.remove		= __devexit_p(twl4030_codec_remove), | 
 | 2324 | 	.driver		= { | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 2325 | 		.name	= "twl4030-codec", | 
| Peter Ujfalusi | 7a1fecf | 2009-10-22 13:26:48 +0300 | [diff] [blame] | 2326 | 		.owner	= THIS_MODULE, | 
 | 2327 | 	}, | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 2328 | }; | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 2329 |  | 
| Takashi Iwai | 24e07db | 2008-12-10 07:40:24 +0100 | [diff] [blame] | 2330 | static int __init twl4030_modinit(void) | 
| Mark Brown | 64089b8 | 2008-12-08 19:17:58 +0000 | [diff] [blame] | 2331 | { | 
| Peter Ujfalusi | 7a1fecf | 2009-10-22 13:26:48 +0300 | [diff] [blame] | 2332 | 	return platform_driver_register(&twl4030_codec_driver); | 
| Mark Brown | 64089b8 | 2008-12-08 19:17:58 +0000 | [diff] [blame] | 2333 | } | 
| Takashi Iwai | 24e07db | 2008-12-10 07:40:24 +0100 | [diff] [blame] | 2334 | module_init(twl4030_modinit); | 
| Mark Brown | 64089b8 | 2008-12-08 19:17:58 +0000 | [diff] [blame] | 2335 |  | 
 | 2336 | static void __exit twl4030_exit(void) | 
 | 2337 | { | 
| Peter Ujfalusi | 7a1fecf | 2009-10-22 13:26:48 +0300 | [diff] [blame] | 2338 | 	platform_driver_unregister(&twl4030_codec_driver); | 
| Mark Brown | 64089b8 | 2008-12-08 19:17:58 +0000 | [diff] [blame] | 2339 | } | 
 | 2340 | module_exit(twl4030_exit); | 
 | 2341 |  | 
| Steve Sakoman | cc17557 | 2008-10-30 21:35:26 -0700 | [diff] [blame] | 2342 | MODULE_DESCRIPTION("ASoC TWL4030 codec driver"); | 
 | 2343 | MODULE_AUTHOR("Steve Sakoman"); | 
 | 2344 | MODULE_LICENSE("GPL"); |