| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1 | /* | 
 | 2 |  * wm8903.c  --  WM8903 ALSA SoC Audio driver | 
 | 3 |  * | 
 | 4 |  * Copyright 2008 Wolfson Microelectronics | 
 | 5 |  * | 
 | 6 |  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> | 
 | 7 |  * | 
 | 8 |  * This program is free software; you can redistribute it and/or modify | 
 | 9 |  * it under the terms of the GNU General Public License version 2 as | 
 | 10 |  * published by the Free Software Foundation. | 
 | 11 |  * | 
 | 12 |  * TODO: | 
 | 13 |  *  - TDM mode configuration. | 
 | 14 |  *  - Mic detect. | 
 | 15 |  *  - Digital microphone support. | 
 | 16 |  *  - Interrupt support (mic detect and sequencer). | 
 | 17 |  */ | 
 | 18 |  | 
 | 19 | #include <linux/module.h> | 
 | 20 | #include <linux/moduleparam.h> | 
 | 21 | #include <linux/init.h> | 
 | 22 | #include <linux/delay.h> | 
 | 23 | #include <linux/pm.h> | 
 | 24 | #include <linux/i2c.h> | 
 | 25 | #include <linux/platform_device.h> | 
 | 26 | #include <sound/core.h> | 
 | 27 | #include <sound/pcm.h> | 
 | 28 | #include <sound/pcm_params.h> | 
 | 29 | #include <sound/tlv.h> | 
 | 30 | #include <sound/soc.h> | 
 | 31 | #include <sound/soc-dapm.h> | 
 | 32 | #include <sound/initval.h> | 
 | 33 |  | 
 | 34 | #include "wm8903.h" | 
 | 35 |  | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 36 | /* Register defaults at reset */ | 
 | 37 | static u16 wm8903_reg_defaults[] = { | 
 | 38 | 	0x8903,     /* R0   - SW Reset and ID */ | 
 | 39 | 	0x0000,     /* R1   - Revision Number */ | 
 | 40 | 	0x0000,     /* R2 */ | 
 | 41 | 	0x0000,     /* R3 */ | 
 | 42 | 	0x0018,     /* R4   - Bias Control 0 */ | 
 | 43 | 	0x0000,     /* R5   - VMID Control 0 */ | 
 | 44 | 	0x0000,     /* R6   - Mic Bias Control 0 */ | 
 | 45 | 	0x0000,     /* R7 */ | 
 | 46 | 	0x0001,     /* R8   - Analogue DAC 0 */ | 
 | 47 | 	0x0000,     /* R9 */ | 
 | 48 | 	0x0001,     /* R10  - Analogue ADC 0 */ | 
 | 49 | 	0x0000,     /* R11 */ | 
 | 50 | 	0x0000,     /* R12  - Power Management 0 */ | 
 | 51 | 	0x0000,     /* R13  - Power Management 1 */ | 
 | 52 | 	0x0000,     /* R14  - Power Management 2 */ | 
 | 53 | 	0x0000,     /* R15  - Power Management 3 */ | 
 | 54 | 	0x0000,     /* R16  - Power Management 4 */ | 
 | 55 | 	0x0000,     /* R17  - Power Management 5 */ | 
 | 56 | 	0x0000,     /* R18  - Power Management 6 */ | 
 | 57 | 	0x0000,     /* R19 */ | 
 | 58 | 	0x0400,     /* R20  - Clock Rates 0 */ | 
 | 59 | 	0x0D07,     /* R21  - Clock Rates 1 */ | 
 | 60 | 	0x0000,     /* R22  - Clock Rates 2 */ | 
 | 61 | 	0x0000,     /* R23 */ | 
 | 62 | 	0x0050,     /* R24  - Audio Interface 0 */ | 
 | 63 | 	0x0242,     /* R25  - Audio Interface 1 */ | 
 | 64 | 	0x0008,     /* R26  - Audio Interface 2 */ | 
 | 65 | 	0x0022,     /* R27  - Audio Interface 3 */ | 
 | 66 | 	0x0000,     /* R28 */ | 
 | 67 | 	0x0000,     /* R29 */ | 
 | 68 | 	0x00C0,     /* R30  - DAC Digital Volume Left */ | 
 | 69 | 	0x00C0,     /* R31  - DAC Digital Volume Right */ | 
 | 70 | 	0x0000,     /* R32  - DAC Digital 0 */ | 
 | 71 | 	0x0000,     /* R33  - DAC Digital 1 */ | 
 | 72 | 	0x0000,     /* R34 */ | 
 | 73 | 	0x0000,     /* R35 */ | 
 | 74 | 	0x00C0,     /* R36  - ADC Digital Volume Left */ | 
 | 75 | 	0x00C0,     /* R37  - ADC Digital Volume Right */ | 
 | 76 | 	0x0000,     /* R38  - ADC Digital 0 */ | 
 | 77 | 	0x0073,     /* R39  - Digital Microphone 0 */ | 
 | 78 | 	0x09BF,     /* R40  - DRC 0 */ | 
 | 79 | 	0x3241,     /* R41  - DRC 1 */ | 
 | 80 | 	0x0020,     /* R42  - DRC 2 */ | 
 | 81 | 	0x0000,     /* R43  - DRC 3 */ | 
 | 82 | 	0x0085,     /* R44  - Analogue Left Input 0 */ | 
 | 83 | 	0x0085,     /* R45  - Analogue Right Input 0 */ | 
 | 84 | 	0x0044,     /* R46  - Analogue Left Input 1 */ | 
 | 85 | 	0x0044,     /* R47  - Analogue Right Input 1 */ | 
 | 86 | 	0x0000,     /* R48 */ | 
 | 87 | 	0x0000,     /* R49 */ | 
 | 88 | 	0x0008,     /* R50  - Analogue Left Mix 0 */ | 
 | 89 | 	0x0004,     /* R51  - Analogue Right Mix 0 */ | 
 | 90 | 	0x0000,     /* R52  - Analogue Spk Mix Left 0 */ | 
 | 91 | 	0x0000,     /* R53  - Analogue Spk Mix Left 1 */ | 
 | 92 | 	0x0000,     /* R54  - Analogue Spk Mix Right 0 */ | 
 | 93 | 	0x0000,     /* R55  - Analogue Spk Mix Right 1 */ | 
 | 94 | 	0x0000,     /* R56 */ | 
 | 95 | 	0x002D,     /* R57  - Analogue OUT1 Left */ | 
 | 96 | 	0x002D,     /* R58  - Analogue OUT1 Right */ | 
 | 97 | 	0x0039,     /* R59  - Analogue OUT2 Left */ | 
 | 98 | 	0x0039,     /* R60  - Analogue OUT2 Right */ | 
 | 99 | 	0x0100,     /* R61 */ | 
 | 100 | 	0x0139,     /* R62  - Analogue OUT3 Left */ | 
 | 101 | 	0x0139,     /* R63  - Analogue OUT3 Right */ | 
 | 102 | 	0x0000,     /* R64 */ | 
 | 103 | 	0x0000,     /* R65  - Analogue SPK Output Control 0 */ | 
 | 104 | 	0x0000,     /* R66 */ | 
 | 105 | 	0x0010,     /* R67  - DC Servo 0 */ | 
 | 106 | 	0x0100,     /* R68 */ | 
 | 107 | 	0x00A4,     /* R69  - DC Servo 2 */ | 
 | 108 | 	0x0807,     /* R70 */ | 
 | 109 | 	0x0000,     /* R71 */ | 
 | 110 | 	0x0000,     /* R72 */ | 
 | 111 | 	0x0000,     /* R73 */ | 
 | 112 | 	0x0000,     /* R74 */ | 
 | 113 | 	0x0000,     /* R75 */ | 
 | 114 | 	0x0000,     /* R76 */ | 
 | 115 | 	0x0000,     /* R77 */ | 
 | 116 | 	0x0000,     /* R78 */ | 
 | 117 | 	0x000E,     /* R79 */ | 
 | 118 | 	0x0000,     /* R80 */ | 
 | 119 | 	0x0000,     /* R81 */ | 
 | 120 | 	0x0000,     /* R82 */ | 
 | 121 | 	0x0000,     /* R83 */ | 
 | 122 | 	0x0000,     /* R84 */ | 
 | 123 | 	0x0000,     /* R85 */ | 
 | 124 | 	0x0000,     /* R86 */ | 
 | 125 | 	0x0006,     /* R87 */ | 
 | 126 | 	0x0000,     /* R88 */ | 
 | 127 | 	0x0000,     /* R89 */ | 
 | 128 | 	0x0000,     /* R90  - Analogue HP 0 */ | 
 | 129 | 	0x0060,     /* R91 */ | 
 | 130 | 	0x0000,     /* R92 */ | 
 | 131 | 	0x0000,     /* R93 */ | 
 | 132 | 	0x0000,     /* R94  - Analogue Lineout 0 */ | 
 | 133 | 	0x0060,     /* R95 */ | 
 | 134 | 	0x0000,     /* R96 */ | 
 | 135 | 	0x0000,     /* R97 */ | 
 | 136 | 	0x0000,     /* R98  - Charge Pump 0 */ | 
 | 137 | 	0x1F25,     /* R99 */ | 
 | 138 | 	0x2B19,     /* R100 */ | 
 | 139 | 	0x01C0,     /* R101 */ | 
 | 140 | 	0x01EF,     /* R102 */ | 
 | 141 | 	0x2B00,     /* R103 */ | 
 | 142 | 	0x0000,     /* R104 - Class W 0 */ | 
 | 143 | 	0x01C0,     /* R105 */ | 
 | 144 | 	0x1C10,     /* R106 */ | 
 | 145 | 	0x0000,     /* R107 */ | 
 | 146 | 	0x0000,     /* R108 - Write Sequencer 0 */ | 
 | 147 | 	0x0000,     /* R109 - Write Sequencer 1 */ | 
 | 148 | 	0x0000,     /* R110 - Write Sequencer 2 */ | 
 | 149 | 	0x0000,     /* R111 - Write Sequencer 3 */ | 
 | 150 | 	0x0000,     /* R112 - Write Sequencer 4 */ | 
 | 151 | 	0x0000,     /* R113 */ | 
 | 152 | 	0x0000,     /* R114 - Control Interface */ | 
 | 153 | 	0x0000,     /* R115 */ | 
 | 154 | 	0x00A8,     /* R116 - GPIO Control 1 */ | 
 | 155 | 	0x00A8,     /* R117 - GPIO Control 2 */ | 
 | 156 | 	0x00A8,     /* R118 - GPIO Control 3 */ | 
 | 157 | 	0x0220,     /* R119 - GPIO Control 4 */ | 
 | 158 | 	0x01A0,     /* R120 - GPIO Control 5 */ | 
 | 159 | 	0x0000,     /* R121 - Interrupt Status 1 */ | 
 | 160 | 	0xFFFF,     /* R122 - Interrupt Status 1 Mask */ | 
 | 161 | 	0x0000,     /* R123 - Interrupt Polarity 1 */ | 
 | 162 | 	0x0000,     /* R124 */ | 
 | 163 | 	0x0003,     /* R125 */ | 
 | 164 | 	0x0000,     /* R126 - Interrupt Control */ | 
 | 165 | 	0x0000,     /* R127 */ | 
 | 166 | 	0x0005,     /* R128 */ | 
 | 167 | 	0x0000,     /* R129 - Control Interface Test 1 */ | 
 | 168 | 	0x0000,     /* R130 */ | 
 | 169 | 	0x0000,     /* R131 */ | 
 | 170 | 	0x0000,     /* R132 */ | 
 | 171 | 	0x0000,     /* R133 */ | 
 | 172 | 	0x0000,     /* R134 */ | 
 | 173 | 	0x03FF,     /* R135 */ | 
 | 174 | 	0x0007,     /* R136 */ | 
 | 175 | 	0x0040,     /* R137 */ | 
 | 176 | 	0x0000,     /* R138 */ | 
 | 177 | 	0x0000,     /* R139 */ | 
 | 178 | 	0x0000,     /* R140 */ | 
 | 179 | 	0x0000,     /* R141 */ | 
 | 180 | 	0x0000,     /* R142 */ | 
 | 181 | 	0x0000,     /* R143 */ | 
 | 182 | 	0x0000,     /* R144 */ | 
 | 183 | 	0x0000,     /* R145 */ | 
 | 184 | 	0x0000,     /* R146 */ | 
 | 185 | 	0x0000,     /* R147 */ | 
 | 186 | 	0x4000,     /* R148 */ | 
 | 187 | 	0x6810,     /* R149 - Charge Pump Test 1 */ | 
 | 188 | 	0x0004,     /* R150 */ | 
 | 189 | 	0x0000,     /* R151 */ | 
 | 190 | 	0x0000,     /* R152 */ | 
 | 191 | 	0x0000,     /* R153 */ | 
 | 192 | 	0x0000,     /* R154 */ | 
 | 193 | 	0x0000,     /* R155 */ | 
 | 194 | 	0x0000,     /* R156 */ | 
 | 195 | 	0x0000,     /* R157 */ | 
 | 196 | 	0x0000,     /* R158 */ | 
 | 197 | 	0x0000,     /* R159 */ | 
 | 198 | 	0x0000,     /* R160 */ | 
 | 199 | 	0x0000,     /* R161 */ | 
 | 200 | 	0x0000,     /* R162 */ | 
 | 201 | 	0x0000,     /* R163 */ | 
 | 202 | 	0x0028,     /* R164 - Clock Rate Test 4 */ | 
 | 203 | 	0x0004,     /* R165 */ | 
 | 204 | 	0x0000,     /* R166 */ | 
 | 205 | 	0x0060,     /* R167 */ | 
 | 206 | 	0x0000,     /* R168 */ | 
 | 207 | 	0x0000,     /* R169 */ | 
 | 208 | 	0x0000,     /* R170 */ | 
 | 209 | 	0x0000,     /* R171 */ | 
 | 210 | 	0x0000,     /* R172 - Analogue Output Bias 0 */ | 
 | 211 | }; | 
 | 212 |  | 
| Mark Brown | d58d5d5 | 2008-12-10 18:36:42 +0000 | [diff] [blame] | 213 | struct wm8903_priv { | 
 | 214 | 	struct snd_soc_codec codec; | 
 | 215 | 	u16 reg_cache[ARRAY_SIZE(wm8903_reg_defaults)]; | 
 | 216 |  | 
 | 217 | 	int sysclk; | 
 | 218 |  | 
 | 219 | 	/* Reference counts */ | 
| Mark Brown | d58d5d5 | 2008-12-10 18:36:42 +0000 | [diff] [blame] | 220 | 	int class_w_users; | 
 | 221 | 	int playback_active; | 
 | 222 | 	int capture_active; | 
 | 223 |  | 
 | 224 | 	struct snd_pcm_substream *master_substream; | 
 | 225 | 	struct snd_pcm_substream *slave_substream; | 
 | 226 | }; | 
 | 227 |  | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 228 | static int wm8903_volatile_register(unsigned int reg) | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 229 | { | 
 | 230 | 	switch (reg) { | 
 | 231 | 	case WM8903_SW_RESET_AND_ID: | 
 | 232 | 	case WM8903_REVISION_NUMBER: | 
 | 233 | 	case WM8903_INTERRUPT_STATUS_1: | 
 | 234 | 	case WM8903_WRITE_SEQUENCER_4: | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 235 | 		return 1; | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 236 |  | 
 | 237 | 	default: | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 238 | 		return 0; | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 239 | 	} | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 240 | } | 
 | 241 |  | 
 | 242 | static int wm8903_run_sequence(struct snd_soc_codec *codec, unsigned int start) | 
 | 243 | { | 
 | 244 | 	u16 reg[5]; | 
 | 245 | 	struct i2c_client *i2c = codec->control_data; | 
 | 246 |  | 
 | 247 | 	BUG_ON(start > 48); | 
 | 248 |  | 
 | 249 | 	/* Enable the sequencer */ | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 250 | 	reg[0] = snd_soc_read(codec, WM8903_WRITE_SEQUENCER_0); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 251 | 	reg[0] |= WM8903_WSEQ_ENA; | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 252 | 	snd_soc_write(codec, WM8903_WRITE_SEQUENCER_0, reg[0]); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 253 |  | 
 | 254 | 	dev_dbg(&i2c->dev, "Starting sequence at %d\n", start); | 
 | 255 |  | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 256 | 	snd_soc_write(codec, WM8903_WRITE_SEQUENCER_3, | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 257 | 		     start | WM8903_WSEQ_START); | 
 | 258 |  | 
 | 259 | 	/* Wait for it to complete.  If we have the interrupt wired up then | 
 | 260 | 	 * we could block waiting for an interrupt, though polling may still | 
 | 261 | 	 * be desirable for diagnostic purposes. | 
 | 262 | 	 */ | 
 | 263 | 	do { | 
 | 264 | 		msleep(10); | 
 | 265 |  | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 266 | 		reg[4] = snd_soc_read(codec, WM8903_WRITE_SEQUENCER_4); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 267 | 	} while (reg[4] & WM8903_WSEQ_BUSY); | 
 | 268 |  | 
 | 269 | 	dev_dbg(&i2c->dev, "Sequence complete\n"); | 
 | 270 |  | 
 | 271 | 	/* Disable the sequencer again */ | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 272 | 	snd_soc_write(codec, WM8903_WRITE_SEQUENCER_0, | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 273 | 		     reg[0] & ~WM8903_WSEQ_ENA); | 
 | 274 |  | 
 | 275 | 	return 0; | 
 | 276 | } | 
 | 277 |  | 
 | 278 | static void wm8903_sync_reg_cache(struct snd_soc_codec *codec, u16 *cache) | 
 | 279 | { | 
 | 280 | 	int i; | 
 | 281 |  | 
 | 282 | 	/* There really ought to be something better we can do here :/ */ | 
 | 283 | 	for (i = 0; i < ARRAY_SIZE(wm8903_reg_defaults); i++) | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 284 | 		cache[i] = codec->hw_read(codec, i); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 285 | } | 
 | 286 |  | 
 | 287 | static void wm8903_reset(struct snd_soc_codec *codec) | 
 | 288 | { | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 289 | 	snd_soc_write(codec, WM8903_SW_RESET_AND_ID, 0); | 
| Mark Brown | d58d5d5 | 2008-12-10 18:36:42 +0000 | [diff] [blame] | 290 | 	memcpy(codec->reg_cache, wm8903_reg_defaults, | 
 | 291 | 	       sizeof(wm8903_reg_defaults)); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 292 | } | 
 | 293 |  | 
 | 294 | #define WM8903_OUTPUT_SHORT 0x8 | 
 | 295 | #define WM8903_OUTPUT_OUT   0x4 | 
 | 296 | #define WM8903_OUTPUT_INT   0x2 | 
 | 297 | #define WM8903_OUTPUT_IN    0x1 | 
 | 298 |  | 
| Mark Brown | 42768a1 | 2009-04-22 18:39:39 +0100 | [diff] [blame] | 299 | static int wm8903_cp_event(struct snd_soc_dapm_widget *w, | 
 | 300 | 			   struct snd_kcontrol *kcontrol, int event) | 
 | 301 | { | 
 | 302 | 	WARN_ON(event != SND_SOC_DAPM_POST_PMU); | 
 | 303 | 	mdelay(4); | 
 | 304 |  | 
 | 305 | 	return 0; | 
 | 306 | } | 
 | 307 |  | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 308 | /* | 
 | 309 |  * Event for headphone and line out amplifier power changes.  Special | 
 | 310 |  * power up/down sequences are required in order to maximise pop/click | 
 | 311 |  * performance. | 
 | 312 |  */ | 
 | 313 | static int wm8903_output_event(struct snd_soc_dapm_widget *w, | 
 | 314 | 			       struct snd_kcontrol *kcontrol, int event) | 
 | 315 | { | 
 | 316 | 	struct snd_soc_codec *codec = w->codec; | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 317 | 	u16 val; | 
| Takashi Iwai | 0bc286e | 2008-12-01 19:59:35 +0100 | [diff] [blame] | 318 | 	u16 reg; | 
| Mark Brown | d7d5c54 | 2009-04-22 21:03:50 +0100 | [diff] [blame] | 319 | 	u16 dcs_reg; | 
 | 320 | 	u16 dcs_bit; | 
| Takashi Iwai | 0bc286e | 2008-12-01 19:59:35 +0100 | [diff] [blame] | 321 | 	int shift; | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 322 |  | 
 | 323 | 	switch (w->reg) { | 
 | 324 | 	case WM8903_POWER_MANAGEMENT_2: | 
 | 325 | 		reg = WM8903_ANALOGUE_HP_0; | 
| Mark Brown | d7d5c54 | 2009-04-22 21:03:50 +0100 | [diff] [blame] | 326 | 		dcs_bit = 0 + w->shift; | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 327 | 		break; | 
 | 328 | 	case WM8903_POWER_MANAGEMENT_3: | 
 | 329 | 		reg = WM8903_ANALOGUE_LINEOUT_0; | 
| Mark Brown | d7d5c54 | 2009-04-22 21:03:50 +0100 | [diff] [blame] | 330 | 		dcs_bit = 2 + w->shift; | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 331 | 		break; | 
 | 332 | 	default: | 
 | 333 | 		BUG(); | 
| Mark Brown | 1e297a1 | 2008-12-10 11:08:33 +0000 | [diff] [blame] | 334 | 		return -EINVAL;  /* Spurious warning from some compilers */ | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 335 | 	} | 
 | 336 |  | 
 | 337 | 	switch (w->shift) { | 
 | 338 | 	case 0: | 
 | 339 | 		shift = 0; | 
 | 340 | 		break; | 
 | 341 | 	case 1: | 
 | 342 | 		shift = 4; | 
 | 343 | 		break; | 
 | 344 | 	default: | 
 | 345 | 		BUG(); | 
| Mark Brown | 1e297a1 | 2008-12-10 11:08:33 +0000 | [diff] [blame] | 346 | 		return -EINVAL;  /* Spurious warning from some compilers */ | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 347 | 	} | 
 | 348 |  | 
 | 349 | 	if (event & SND_SOC_DAPM_PRE_PMU) { | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 350 | 		val = snd_soc_read(codec, reg); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 351 |  | 
 | 352 | 		/* Short the output */ | 
 | 353 | 		val &= ~(WM8903_OUTPUT_SHORT << shift); | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 354 | 		snd_soc_write(codec, reg, val); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 355 | 	} | 
 | 356 |  | 
 | 357 | 	if (event & SND_SOC_DAPM_POST_PMU) { | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 358 | 		val = snd_soc_read(codec, reg); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 359 |  | 
 | 360 | 		val |= (WM8903_OUTPUT_IN << shift); | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 361 | 		snd_soc_write(codec, reg, val); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 362 |  | 
 | 363 | 		val |= (WM8903_OUTPUT_INT << shift); | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 364 | 		snd_soc_write(codec, reg, val); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 365 |  | 
 | 366 | 		/* Turn on the output ENA_OUTP */ | 
 | 367 | 		val |= (WM8903_OUTPUT_OUT << shift); | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 368 | 		snd_soc_write(codec, reg, val); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 369 |  | 
| Mark Brown | d7d5c54 | 2009-04-22 21:03:50 +0100 | [diff] [blame] | 370 | 		/* Enable the DC servo */ | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 371 | 		dcs_reg = snd_soc_read(codec, WM8903_DC_SERVO_0); | 
| Mark Brown | d7d5c54 | 2009-04-22 21:03:50 +0100 | [diff] [blame] | 372 | 		dcs_reg |= dcs_bit; | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 373 | 		snd_soc_write(codec, WM8903_DC_SERVO_0, dcs_reg); | 
| Mark Brown | d7d5c54 | 2009-04-22 21:03:50 +0100 | [diff] [blame] | 374 |  | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 375 | 		/* Remove the short */ | 
 | 376 | 		val |= (WM8903_OUTPUT_SHORT << shift); | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 377 | 		snd_soc_write(codec, reg, val); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 378 | 	} | 
 | 379 |  | 
 | 380 | 	if (event & SND_SOC_DAPM_PRE_PMD) { | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 381 | 		val = snd_soc_read(codec, reg); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 382 |  | 
 | 383 | 		/* Short the output */ | 
 | 384 | 		val &= ~(WM8903_OUTPUT_SHORT << shift); | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 385 | 		snd_soc_write(codec, reg, val); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 386 |  | 
| Mark Brown | d7d5c54 | 2009-04-22 21:03:50 +0100 | [diff] [blame] | 387 | 		/* Disable the DC servo */ | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 388 | 		dcs_reg = snd_soc_read(codec, WM8903_DC_SERVO_0); | 
| Mark Brown | d7d5c54 | 2009-04-22 21:03:50 +0100 | [diff] [blame] | 389 | 		dcs_reg &= ~dcs_bit; | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 390 | 		snd_soc_write(codec, WM8903_DC_SERVO_0, dcs_reg); | 
| Mark Brown | d7d5c54 | 2009-04-22 21:03:50 +0100 | [diff] [blame] | 391 |  | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 392 | 		/* Then disable the intermediate and output stages */ | 
 | 393 | 		val &= ~((WM8903_OUTPUT_OUT | WM8903_OUTPUT_INT | | 
 | 394 | 			  WM8903_OUTPUT_IN) << shift); | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 395 | 		snd_soc_write(codec, reg, val); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 396 | 	} | 
 | 397 |  | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 398 | 	return 0; | 
 | 399 | } | 
 | 400 |  | 
 | 401 | /* | 
 | 402 |  * When used with DAC outputs only the WM8903 charge pump supports | 
 | 403 |  * operation in class W mode, providing very low power consumption | 
 | 404 |  * when used with digital sources.  Enable and disable this mode | 
 | 405 |  * automatically depending on the mixer configuration. | 
 | 406 |  * | 
 | 407 |  * All the relevant controls are simple switches. | 
 | 408 |  */ | 
 | 409 | static int wm8903_class_w_put(struct snd_kcontrol *kcontrol, | 
 | 410 | 			      struct snd_ctl_elem_value *ucontrol) | 
 | 411 | { | 
 | 412 | 	struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); | 
 | 413 | 	struct snd_soc_codec *codec = widget->codec; | 
 | 414 | 	struct wm8903_priv *wm8903 = codec->private_data; | 
 | 415 | 	struct i2c_client *i2c = codec->control_data; | 
 | 416 | 	u16 reg; | 
 | 417 | 	int ret; | 
 | 418 |  | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 419 | 	reg = snd_soc_read(codec, WM8903_CLASS_W_0); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 420 |  | 
 | 421 | 	/* Turn it off if we're about to enable bypass */ | 
 | 422 | 	if (ucontrol->value.integer.value[0]) { | 
 | 423 | 		if (wm8903->class_w_users == 0) { | 
 | 424 | 			dev_dbg(&i2c->dev, "Disabling Class W\n"); | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 425 | 			snd_soc_write(codec, WM8903_CLASS_W_0, reg & | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 426 | 				     ~(WM8903_CP_DYN_FREQ | WM8903_CP_DYN_V)); | 
 | 427 | 		} | 
 | 428 | 		wm8903->class_w_users++; | 
 | 429 | 	} | 
 | 430 |  | 
 | 431 | 	/* Implement the change */ | 
 | 432 | 	ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol); | 
 | 433 |  | 
 | 434 | 	/* If we've just disabled the last bypass path turn Class W on */ | 
 | 435 | 	if (!ucontrol->value.integer.value[0]) { | 
 | 436 | 		if (wm8903->class_w_users == 1) { | 
 | 437 | 			dev_dbg(&i2c->dev, "Enabling Class W\n"); | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 438 | 			snd_soc_write(codec, WM8903_CLASS_W_0, reg | | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 439 | 				     WM8903_CP_DYN_FREQ | WM8903_CP_DYN_V); | 
 | 440 | 		} | 
 | 441 | 		wm8903->class_w_users--; | 
 | 442 | 	} | 
 | 443 |  | 
 | 444 | 	dev_dbg(&i2c->dev, "Bypass use count now %d\n", | 
 | 445 | 		wm8903->class_w_users); | 
 | 446 |  | 
 | 447 | 	return ret; | 
 | 448 | } | 
 | 449 |  | 
 | 450 | #define SOC_DAPM_SINGLE_W(xname, reg, shift, max, invert) \ | 
 | 451 | {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ | 
 | 452 | 	.info = snd_soc_info_volsw, \ | 
 | 453 | 	.get = snd_soc_dapm_get_volsw, .put = wm8903_class_w_put, \ | 
 | 454 | 	.private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) } | 
 | 455 |  | 
 | 456 |  | 
 | 457 | /* ALSA can only do steps of .01dB */ | 
 | 458 | static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1); | 
 | 459 |  | 
| Mark Brown | 291ce18 | 2009-04-22 21:36:14 +0100 | [diff] [blame] | 460 | static const DECLARE_TLV_DB_SCALE(digital_sidetone_tlv, -3600, 300, 0); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 461 | static const DECLARE_TLV_DB_SCALE(out_tlv, -5700, 100, 0); | 
 | 462 |  | 
 | 463 | static const DECLARE_TLV_DB_SCALE(drc_tlv_thresh, 0, 75, 0); | 
 | 464 | static const DECLARE_TLV_DB_SCALE(drc_tlv_amp, -2250, 75, 0); | 
 | 465 | static const DECLARE_TLV_DB_SCALE(drc_tlv_min, 0, 600, 0); | 
 | 466 | static const DECLARE_TLV_DB_SCALE(drc_tlv_max, 1200, 600, 0); | 
 | 467 | static const DECLARE_TLV_DB_SCALE(drc_tlv_startup, -300, 50, 0); | 
 | 468 |  | 
 | 469 | static const char *drc_slope_text[] = { | 
 | 470 | 	"1", "1/2", "1/4", "1/8", "1/16", "0" | 
 | 471 | }; | 
 | 472 |  | 
 | 473 | static const struct soc_enum drc_slope_r0 = | 
 | 474 | 	SOC_ENUM_SINGLE(WM8903_DRC_2, 3, 6, drc_slope_text); | 
 | 475 |  | 
 | 476 | static const struct soc_enum drc_slope_r1 = | 
 | 477 | 	SOC_ENUM_SINGLE(WM8903_DRC_2, 0, 6, drc_slope_text); | 
 | 478 |  | 
 | 479 | static const char *drc_attack_text[] = { | 
 | 480 | 	"instantaneous", | 
 | 481 | 	"363us", "762us", "1.45ms", "2.9ms", "5.8ms", "11.6ms", "23.2ms", | 
 | 482 | 	"46.4ms", "92.8ms", "185.6ms" | 
 | 483 | }; | 
 | 484 |  | 
 | 485 | static const struct soc_enum drc_attack = | 
 | 486 | 	SOC_ENUM_SINGLE(WM8903_DRC_1, 12, 11, drc_attack_text); | 
 | 487 |  | 
 | 488 | static const char *drc_decay_text[] = { | 
 | 489 | 	"186ms", "372ms", "743ms", "1.49s", "2.97s", "5.94s", "11.89s", | 
 | 490 | 	"23.87s", "47.56s" | 
 | 491 | }; | 
 | 492 |  | 
 | 493 | static const struct soc_enum drc_decay = | 
 | 494 | 	SOC_ENUM_SINGLE(WM8903_DRC_1, 8, 9, drc_decay_text); | 
 | 495 |  | 
 | 496 | static const char *drc_ff_delay_text[] = { | 
 | 497 | 	"5 samples", "9 samples" | 
 | 498 | }; | 
 | 499 |  | 
 | 500 | static const struct soc_enum drc_ff_delay = | 
 | 501 | 	SOC_ENUM_SINGLE(WM8903_DRC_0, 5, 2, drc_ff_delay_text); | 
 | 502 |  | 
 | 503 | static const char *drc_qr_decay_text[] = { | 
 | 504 | 	"0.725ms", "1.45ms", "5.8ms" | 
 | 505 | }; | 
 | 506 |  | 
 | 507 | static const struct soc_enum drc_qr_decay = | 
 | 508 | 	SOC_ENUM_SINGLE(WM8903_DRC_1, 4, 3, drc_qr_decay_text); | 
 | 509 |  | 
 | 510 | static const char *drc_smoothing_text[] = { | 
 | 511 | 	"Low", "Medium", "High" | 
 | 512 | }; | 
 | 513 |  | 
 | 514 | static const struct soc_enum drc_smoothing = | 
 | 515 | 	SOC_ENUM_SINGLE(WM8903_DRC_0, 11, 3, drc_smoothing_text); | 
 | 516 |  | 
 | 517 | static const char *soft_mute_text[] = { | 
 | 518 | 	"Fast (fs/2)", "Slow (fs/32)" | 
 | 519 | }; | 
 | 520 |  | 
 | 521 | static const struct soc_enum soft_mute = | 
 | 522 | 	SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_1, 10, 2, soft_mute_text); | 
 | 523 |  | 
 | 524 | static const char *mute_mode_text[] = { | 
 | 525 | 	"Hard", "Soft" | 
 | 526 | }; | 
 | 527 |  | 
 | 528 | static const struct soc_enum mute_mode = | 
 | 529 | 	SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_1, 9, 2, mute_mode_text); | 
 | 530 |  | 
 | 531 | static const char *dac_deemphasis_text[] = { | 
 | 532 | 	"Disabled", "32kHz", "44.1kHz", "48kHz" | 
 | 533 | }; | 
 | 534 |  | 
 | 535 | static const struct soc_enum dac_deemphasis = | 
 | 536 | 	SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_1, 1, 4, dac_deemphasis_text); | 
 | 537 |  | 
 | 538 | static const char *companding_text[] = { | 
 | 539 | 	"ulaw", "alaw" | 
 | 540 | }; | 
 | 541 |  | 
 | 542 | static const struct soc_enum dac_companding = | 
 | 543 | 	SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 0, 2, companding_text); | 
 | 544 |  | 
 | 545 | static const struct soc_enum adc_companding = | 
 | 546 | 	SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 2, 2, companding_text); | 
 | 547 |  | 
 | 548 | static const char *input_mode_text[] = { | 
 | 549 | 	"Single-Ended", "Differential Line", "Differential Mic" | 
 | 550 | }; | 
 | 551 |  | 
 | 552 | static const struct soc_enum linput_mode_enum = | 
 | 553 | 	SOC_ENUM_SINGLE(WM8903_ANALOGUE_LEFT_INPUT_1, 0, 3, input_mode_text); | 
 | 554 |  | 
 | 555 | static const struct soc_enum rinput_mode_enum = | 
 | 556 | 	SOC_ENUM_SINGLE(WM8903_ANALOGUE_RIGHT_INPUT_1, 0, 3, input_mode_text); | 
 | 557 |  | 
 | 558 | static const char *linput_mux_text[] = { | 
 | 559 | 	"IN1L", "IN2L", "IN3L" | 
 | 560 | }; | 
 | 561 |  | 
 | 562 | static const struct soc_enum linput_enum = | 
 | 563 | 	SOC_ENUM_SINGLE(WM8903_ANALOGUE_LEFT_INPUT_1, 2, 3, linput_mux_text); | 
 | 564 |  | 
 | 565 | static const struct soc_enum linput_inv_enum = | 
 | 566 | 	SOC_ENUM_SINGLE(WM8903_ANALOGUE_LEFT_INPUT_1, 4, 3, linput_mux_text); | 
 | 567 |  | 
 | 568 | static const char *rinput_mux_text[] = { | 
 | 569 | 	"IN1R", "IN2R", "IN3R" | 
 | 570 | }; | 
 | 571 |  | 
 | 572 | static const struct soc_enum rinput_enum = | 
 | 573 | 	SOC_ENUM_SINGLE(WM8903_ANALOGUE_RIGHT_INPUT_1, 2, 3, rinput_mux_text); | 
 | 574 |  | 
 | 575 | static const struct soc_enum rinput_inv_enum = | 
 | 576 | 	SOC_ENUM_SINGLE(WM8903_ANALOGUE_RIGHT_INPUT_1, 4, 3, rinput_mux_text); | 
 | 577 |  | 
 | 578 |  | 
| Mark Brown | 291ce18 | 2009-04-22 21:36:14 +0100 | [diff] [blame] | 579 | static const char *sidetone_text[] = { | 
 | 580 | 	"None", "Left", "Right" | 
 | 581 | }; | 
 | 582 |  | 
 | 583 | static const struct soc_enum lsidetone_enum = | 
 | 584 | 	SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_0, 2, 3, sidetone_text); | 
 | 585 |  | 
 | 586 | static const struct soc_enum rsidetone_enum = | 
 | 587 | 	SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_0, 0, 3, sidetone_text); | 
 | 588 |  | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 589 | static const struct snd_kcontrol_new wm8903_snd_controls[] = { | 
 | 590 |  | 
 | 591 | /* Input PGAs - No TLV since the scale depends on PGA mode */ | 
 | 592 | SOC_SINGLE("Left Input PGA Switch", WM8903_ANALOGUE_LEFT_INPUT_0, | 
| Mark Brown | 5715952 | 2008-09-24 10:47:02 +0100 | [diff] [blame] | 593 | 	   7, 1, 1), | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 594 | SOC_SINGLE("Left Input PGA Volume", WM8903_ANALOGUE_LEFT_INPUT_0, | 
 | 595 | 	   0, 31, 0), | 
 | 596 | SOC_SINGLE("Left Input PGA Common Mode Switch", WM8903_ANALOGUE_LEFT_INPUT_1, | 
 | 597 | 	   6, 1, 0), | 
 | 598 |  | 
 | 599 | SOC_SINGLE("Right Input PGA Switch", WM8903_ANALOGUE_RIGHT_INPUT_0, | 
| Mark Brown | 5715952 | 2008-09-24 10:47:02 +0100 | [diff] [blame] | 600 | 	   7, 1, 1), | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 601 | SOC_SINGLE("Right Input PGA Volume", WM8903_ANALOGUE_RIGHT_INPUT_0, | 
 | 602 | 	   0, 31, 0), | 
 | 603 | SOC_SINGLE("Right Input PGA Common Mode Switch", WM8903_ANALOGUE_RIGHT_INPUT_1, | 
 | 604 | 	   6, 1, 0), | 
 | 605 |  | 
 | 606 | /* ADCs */ | 
 | 607 | SOC_SINGLE("DRC Switch", WM8903_DRC_0, 15, 1, 0), | 
 | 608 | SOC_ENUM("DRC Compressor Slope R0", drc_slope_r0), | 
 | 609 | SOC_ENUM("DRC Compressor Slope R1", drc_slope_r1), | 
 | 610 | SOC_SINGLE_TLV("DRC Compressor Threashold Volume", WM8903_DRC_3, 5, 124, 1, | 
 | 611 | 	       drc_tlv_thresh), | 
 | 612 | SOC_SINGLE_TLV("DRC Volume", WM8903_DRC_3, 0, 30, 1, drc_tlv_amp), | 
 | 613 | SOC_SINGLE_TLV("DRC Minimum Gain Volume", WM8903_DRC_1, 2, 3, 1, drc_tlv_min), | 
 | 614 | SOC_SINGLE_TLV("DRC Maximum Gain Volume", WM8903_DRC_1, 0, 3, 0, drc_tlv_max), | 
 | 615 | SOC_ENUM("DRC Attack Rate", drc_attack), | 
 | 616 | SOC_ENUM("DRC Decay Rate", drc_decay), | 
 | 617 | SOC_ENUM("DRC FF Delay", drc_ff_delay), | 
 | 618 | SOC_SINGLE("DRC Anticlip Switch", WM8903_DRC_0, 1, 1, 0), | 
 | 619 | SOC_SINGLE("DRC QR Switch", WM8903_DRC_0, 2, 1, 0), | 
 | 620 | SOC_SINGLE_TLV("DRC QR Threashold Volume", WM8903_DRC_0, 6, 3, 0, drc_tlv_max), | 
 | 621 | SOC_ENUM("DRC QR Decay Rate", drc_qr_decay), | 
 | 622 | SOC_SINGLE("DRC Smoothing Switch", WM8903_DRC_0, 3, 1, 0), | 
 | 623 | SOC_SINGLE("DRC Smoothing Hysteresis Switch", WM8903_DRC_0, 0, 1, 0), | 
 | 624 | SOC_ENUM("DRC Smoothing Threashold", drc_smoothing), | 
 | 625 | SOC_SINGLE_TLV("DRC Startup Volume", WM8903_DRC_0, 6, 18, 0, drc_tlv_startup), | 
 | 626 |  | 
 | 627 | SOC_DOUBLE_R_TLV("Digital Capture Volume", WM8903_ADC_DIGITAL_VOLUME_LEFT, | 
 | 628 | 		 WM8903_ADC_DIGITAL_VOLUME_RIGHT, 1, 96, 0, digital_tlv), | 
 | 629 | SOC_ENUM("ADC Companding Mode", adc_companding), | 
 | 630 | SOC_SINGLE("ADC Companding Switch", WM8903_AUDIO_INTERFACE_0, 3, 1, 0), | 
 | 631 |  | 
| Mark Brown | 291ce18 | 2009-04-22 21:36:14 +0100 | [diff] [blame] | 632 | SOC_DOUBLE_TLV("Digital Sidetone Volume", WM8903_DAC_DIGITAL_0, 4, 8, | 
 | 633 | 	       12, 0, digital_sidetone_tlv), | 
 | 634 |  | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 635 | /* DAC */ | 
 | 636 | SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8903_DAC_DIGITAL_VOLUME_LEFT, | 
 | 637 | 		 WM8903_DAC_DIGITAL_VOLUME_RIGHT, 1, 120, 0, digital_tlv), | 
 | 638 | SOC_ENUM("DAC Soft Mute Rate", soft_mute), | 
 | 639 | SOC_ENUM("DAC Mute Mode", mute_mode), | 
 | 640 | SOC_SINGLE("DAC Mono Switch", WM8903_DAC_DIGITAL_1, 12, 1, 0), | 
 | 641 | SOC_ENUM("DAC De-emphasis", dac_deemphasis), | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 642 | SOC_ENUM("DAC Companding Mode", dac_companding), | 
 | 643 | SOC_SINGLE("DAC Companding Switch", WM8903_AUDIO_INTERFACE_0, 1, 1, 0), | 
 | 644 |  | 
 | 645 | /* Headphones */ | 
 | 646 | SOC_DOUBLE_R("Headphone Switch", | 
 | 647 | 	     WM8903_ANALOGUE_OUT1_LEFT, WM8903_ANALOGUE_OUT1_RIGHT, | 
 | 648 | 	     8, 1, 1), | 
 | 649 | SOC_DOUBLE_R("Headphone ZC Switch", | 
 | 650 | 	     WM8903_ANALOGUE_OUT1_LEFT, WM8903_ANALOGUE_OUT1_RIGHT, | 
 | 651 | 	     6, 1, 0), | 
 | 652 | SOC_DOUBLE_R_TLV("Headphone Volume", | 
 | 653 | 		 WM8903_ANALOGUE_OUT1_LEFT, WM8903_ANALOGUE_OUT1_RIGHT, | 
 | 654 | 		 0, 63, 0, out_tlv), | 
 | 655 |  | 
 | 656 | /* Line out */ | 
 | 657 | SOC_DOUBLE_R("Line Out Switch", | 
 | 658 | 	     WM8903_ANALOGUE_OUT2_LEFT, WM8903_ANALOGUE_OUT2_RIGHT, | 
 | 659 | 	     8, 1, 1), | 
 | 660 | SOC_DOUBLE_R("Line Out ZC Switch", | 
 | 661 | 	     WM8903_ANALOGUE_OUT2_LEFT, WM8903_ANALOGUE_OUT2_RIGHT, | 
 | 662 | 	     6, 1, 0), | 
 | 663 | SOC_DOUBLE_R_TLV("Line Out Volume", | 
 | 664 | 		 WM8903_ANALOGUE_OUT2_LEFT, WM8903_ANALOGUE_OUT2_RIGHT, | 
 | 665 | 		 0, 63, 0, out_tlv), | 
 | 666 |  | 
 | 667 | /* Speaker */ | 
 | 668 | SOC_DOUBLE_R("Speaker Switch", | 
 | 669 | 	     WM8903_ANALOGUE_OUT3_LEFT, WM8903_ANALOGUE_OUT3_RIGHT, 8, 1, 1), | 
 | 670 | SOC_DOUBLE_R("Speaker ZC Switch", | 
 | 671 | 	     WM8903_ANALOGUE_OUT3_LEFT, WM8903_ANALOGUE_OUT3_RIGHT, 6, 1, 0), | 
 | 672 | SOC_DOUBLE_R_TLV("Speaker Volume", | 
 | 673 | 		 WM8903_ANALOGUE_OUT3_LEFT, WM8903_ANALOGUE_OUT3_RIGHT, | 
 | 674 | 		 0, 63, 0, out_tlv), | 
 | 675 | }; | 
 | 676 |  | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 677 | static const struct snd_kcontrol_new linput_mode_mux = | 
 | 678 | 	SOC_DAPM_ENUM("Left Input Mode Mux", linput_mode_enum); | 
 | 679 |  | 
 | 680 | static const struct snd_kcontrol_new rinput_mode_mux = | 
 | 681 | 	SOC_DAPM_ENUM("Right Input Mode Mux", rinput_mode_enum); | 
 | 682 |  | 
 | 683 | static const struct snd_kcontrol_new linput_mux = | 
 | 684 | 	SOC_DAPM_ENUM("Left Input Mux", linput_enum); | 
 | 685 |  | 
 | 686 | static const struct snd_kcontrol_new linput_inv_mux = | 
 | 687 | 	SOC_DAPM_ENUM("Left Inverting Input Mux", linput_inv_enum); | 
 | 688 |  | 
 | 689 | static const struct snd_kcontrol_new rinput_mux = | 
 | 690 | 	SOC_DAPM_ENUM("Right Input Mux", rinput_enum); | 
 | 691 |  | 
 | 692 | static const struct snd_kcontrol_new rinput_inv_mux = | 
 | 693 | 	SOC_DAPM_ENUM("Right Inverting Input Mux", rinput_inv_enum); | 
 | 694 |  | 
| Mark Brown | 291ce18 | 2009-04-22 21:36:14 +0100 | [diff] [blame] | 695 | static const struct snd_kcontrol_new lsidetone_mux = | 
 | 696 | 	SOC_DAPM_ENUM("DACL Sidetone Mux", lsidetone_enum); | 
 | 697 |  | 
 | 698 | static const struct snd_kcontrol_new rsidetone_mux = | 
 | 699 | 	SOC_DAPM_ENUM("DACR Sidetone Mux", rsidetone_enum); | 
 | 700 |  | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 701 | static const struct snd_kcontrol_new left_output_mixer[] = { | 
 | 702 | SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_LEFT_MIX_0, 3, 1, 0), | 
 | 703 | SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_LEFT_MIX_0, 2, 1, 0), | 
 | 704 | SOC_DAPM_SINGLE_W("Left Bypass Switch", WM8903_ANALOGUE_LEFT_MIX_0, 1, 1, 0), | 
| Mark Brown | 4b4fffd | 2008-12-03 11:21:08 +0000 | [diff] [blame] | 705 | SOC_DAPM_SINGLE_W("Right Bypass Switch", WM8903_ANALOGUE_LEFT_MIX_0, 0, 1, 0), | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 706 | }; | 
 | 707 |  | 
 | 708 | static const struct snd_kcontrol_new right_output_mixer[] = { | 
 | 709 | SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 3, 1, 0), | 
 | 710 | SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 2, 1, 0), | 
 | 711 | SOC_DAPM_SINGLE_W("Left Bypass Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 1, 1, 0), | 
| Mark Brown | 4b4fffd | 2008-12-03 11:21:08 +0000 | [diff] [blame] | 712 | SOC_DAPM_SINGLE_W("Right Bypass Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 0, 1, 0), | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 713 | }; | 
 | 714 |  | 
 | 715 | static const struct snd_kcontrol_new left_speaker_mixer[] = { | 
 | 716 | SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0, 3, 1, 0), | 
 | 717 | SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0, 2, 1, 0), | 
 | 718 | SOC_DAPM_SINGLE("Left Bypass Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0, 1, 1, 0), | 
 | 719 | SOC_DAPM_SINGLE("Right Bypass Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0, | 
| Mark Brown | 4b4fffd | 2008-12-03 11:21:08 +0000 | [diff] [blame] | 720 | 		0, 1, 0), | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 721 | }; | 
 | 722 |  | 
 | 723 | static const struct snd_kcontrol_new right_speaker_mixer[] = { | 
 | 724 | SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0, 3, 1, 0), | 
 | 725 | SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0, 2, 1, 0), | 
 | 726 | SOC_DAPM_SINGLE("Left Bypass Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0, | 
 | 727 | 		1, 1, 0), | 
 | 728 | SOC_DAPM_SINGLE("Right Bypass Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0, | 
| Mark Brown | 4b4fffd | 2008-12-03 11:21:08 +0000 | [diff] [blame] | 729 | 		0, 1, 0), | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 730 | }; | 
 | 731 |  | 
 | 732 | static const struct snd_soc_dapm_widget wm8903_dapm_widgets[] = { | 
 | 733 | SND_SOC_DAPM_INPUT("IN1L"), | 
 | 734 | SND_SOC_DAPM_INPUT("IN1R"), | 
 | 735 | SND_SOC_DAPM_INPUT("IN2L"), | 
 | 736 | SND_SOC_DAPM_INPUT("IN2R"), | 
 | 737 | SND_SOC_DAPM_INPUT("IN3L"), | 
 | 738 | SND_SOC_DAPM_INPUT("IN3R"), | 
 | 739 |  | 
 | 740 | SND_SOC_DAPM_OUTPUT("HPOUTL"), | 
 | 741 | SND_SOC_DAPM_OUTPUT("HPOUTR"), | 
 | 742 | SND_SOC_DAPM_OUTPUT("LINEOUTL"), | 
 | 743 | SND_SOC_DAPM_OUTPUT("LINEOUTR"), | 
 | 744 | SND_SOC_DAPM_OUTPUT("LOP"), | 
 | 745 | SND_SOC_DAPM_OUTPUT("LON"), | 
 | 746 | SND_SOC_DAPM_OUTPUT("ROP"), | 
 | 747 | SND_SOC_DAPM_OUTPUT("RON"), | 
 | 748 |  | 
 | 749 | SND_SOC_DAPM_MICBIAS("Mic Bias", WM8903_MIC_BIAS_CONTROL_0, 0, 0), | 
 | 750 |  | 
 | 751 | SND_SOC_DAPM_MUX("Left Input Mux", SND_SOC_NOPM, 0, 0, &linput_mux), | 
 | 752 | SND_SOC_DAPM_MUX("Left Input Inverting Mux", SND_SOC_NOPM, 0, 0, | 
 | 753 | 		 &linput_inv_mux), | 
 | 754 | SND_SOC_DAPM_MUX("Left Input Mode Mux", SND_SOC_NOPM, 0, 0, &linput_mode_mux), | 
 | 755 |  | 
 | 756 | SND_SOC_DAPM_MUX("Right Input Mux", SND_SOC_NOPM, 0, 0, &rinput_mux), | 
 | 757 | SND_SOC_DAPM_MUX("Right Input Inverting Mux", SND_SOC_NOPM, 0, 0, | 
 | 758 | 		 &rinput_inv_mux), | 
 | 759 | SND_SOC_DAPM_MUX("Right Input Mode Mux", SND_SOC_NOPM, 0, 0, &rinput_mode_mux), | 
 | 760 |  | 
 | 761 | SND_SOC_DAPM_PGA("Left Input PGA", WM8903_POWER_MANAGEMENT_0, 1, 0, NULL, 0), | 
 | 762 | SND_SOC_DAPM_PGA("Right Input PGA", WM8903_POWER_MANAGEMENT_0, 0, 0, NULL, 0), | 
 | 763 |  | 
 | 764 | SND_SOC_DAPM_ADC("ADCL", "Left HiFi Capture", WM8903_POWER_MANAGEMENT_6, 1, 0), | 
 | 765 | SND_SOC_DAPM_ADC("ADCR", "Right HiFi Capture", WM8903_POWER_MANAGEMENT_6, 0, 0), | 
 | 766 |  | 
| Mark Brown | 291ce18 | 2009-04-22 21:36:14 +0100 | [diff] [blame] | 767 | SND_SOC_DAPM_MUX("DACL Sidetone", SND_SOC_NOPM, 0, 0, &lsidetone_mux), | 
 | 768 | SND_SOC_DAPM_MUX("DACR Sidetone", SND_SOC_NOPM, 0, 0, &rsidetone_mux), | 
 | 769 |  | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 770 | SND_SOC_DAPM_DAC("DACL", "Left Playback", WM8903_POWER_MANAGEMENT_6, 3, 0), | 
 | 771 | SND_SOC_DAPM_DAC("DACR", "Right Playback", WM8903_POWER_MANAGEMENT_6, 2, 0), | 
 | 772 |  | 
 | 773 | SND_SOC_DAPM_MIXER("Left Output Mixer", WM8903_POWER_MANAGEMENT_1, 1, 0, | 
 | 774 | 		   left_output_mixer, ARRAY_SIZE(left_output_mixer)), | 
 | 775 | SND_SOC_DAPM_MIXER("Right Output Mixer", WM8903_POWER_MANAGEMENT_1, 0, 0, | 
 | 776 | 		   right_output_mixer, ARRAY_SIZE(right_output_mixer)), | 
 | 777 |  | 
 | 778 | SND_SOC_DAPM_MIXER("Left Speaker Mixer", WM8903_POWER_MANAGEMENT_4, 1, 0, | 
 | 779 | 		   left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)), | 
 | 780 | SND_SOC_DAPM_MIXER("Right Speaker Mixer", WM8903_POWER_MANAGEMENT_4, 0, 0, | 
 | 781 | 		   right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer)), | 
 | 782 |  | 
 | 783 | SND_SOC_DAPM_PGA_E("Left Headphone Output PGA", WM8903_POWER_MANAGEMENT_2, | 
 | 784 | 		   1, 0, NULL, 0, wm8903_output_event, | 
 | 785 | 		   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | | 
| Mark Brown | 42768a1 | 2009-04-22 18:39:39 +0100 | [diff] [blame] | 786 | 		   SND_SOC_DAPM_PRE_PMD), | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 787 | SND_SOC_DAPM_PGA_E("Right Headphone Output PGA", WM8903_POWER_MANAGEMENT_2, | 
 | 788 | 		   0, 0, NULL, 0, wm8903_output_event, | 
 | 789 | 		   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | | 
| Mark Brown | 42768a1 | 2009-04-22 18:39:39 +0100 | [diff] [blame] | 790 | 		   SND_SOC_DAPM_PRE_PMD), | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 791 |  | 
 | 792 | SND_SOC_DAPM_PGA_E("Left Line Output PGA", WM8903_POWER_MANAGEMENT_3, 1, 0, | 
 | 793 | 		   NULL, 0, wm8903_output_event, | 
 | 794 | 		   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | | 
| Mark Brown | 42768a1 | 2009-04-22 18:39:39 +0100 | [diff] [blame] | 795 | 		   SND_SOC_DAPM_PRE_PMD), | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 796 | SND_SOC_DAPM_PGA_E("Right Line Output PGA", WM8903_POWER_MANAGEMENT_3, 0, 0, | 
 | 797 | 		   NULL, 0, wm8903_output_event, | 
 | 798 | 		   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | | 
| Mark Brown | 42768a1 | 2009-04-22 18:39:39 +0100 | [diff] [blame] | 799 | 		   SND_SOC_DAPM_PRE_PMD), | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 800 |  | 
 | 801 | SND_SOC_DAPM_PGA("Left Speaker PGA", WM8903_POWER_MANAGEMENT_5, 1, 0, | 
 | 802 | 		 NULL, 0), | 
 | 803 | SND_SOC_DAPM_PGA("Right Speaker PGA", WM8903_POWER_MANAGEMENT_5, 0, 0, | 
 | 804 | 		 NULL, 0), | 
 | 805 |  | 
| Mark Brown | 42768a1 | 2009-04-22 18:39:39 +0100 | [diff] [blame] | 806 | SND_SOC_DAPM_SUPPLY("Charge Pump", WM8903_CHARGE_PUMP_0, 0, 0, | 
 | 807 | 		    wm8903_cp_event, SND_SOC_DAPM_POST_PMU), | 
| Mark Brown | c2aef4f | 2009-04-22 20:04:44 +0100 | [diff] [blame] | 808 | SND_SOC_DAPM_SUPPLY("CLK_DSP", WM8903_CLOCK_RATES_2, 1, 0, NULL, 0), | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 809 | }; | 
 | 810 |  | 
 | 811 | static const struct snd_soc_dapm_route intercon[] = { | 
 | 812 |  | 
 | 813 | 	{ "Left Input Mux", "IN1L", "IN1L" }, | 
 | 814 | 	{ "Left Input Mux", "IN2L", "IN2L" }, | 
 | 815 | 	{ "Left Input Mux", "IN3L", "IN3L" }, | 
 | 816 |  | 
 | 817 | 	{ "Left Input Inverting Mux", "IN1L", "IN1L" }, | 
 | 818 | 	{ "Left Input Inverting Mux", "IN2L", "IN2L" }, | 
 | 819 | 	{ "Left Input Inverting Mux", "IN3L", "IN3L" }, | 
 | 820 |  | 
 | 821 | 	{ "Right Input Mux", "IN1R", "IN1R" }, | 
 | 822 | 	{ "Right Input Mux", "IN2R", "IN2R" }, | 
 | 823 | 	{ "Right Input Mux", "IN3R", "IN3R" }, | 
 | 824 |  | 
 | 825 | 	{ "Right Input Inverting Mux", "IN1R", "IN1R" }, | 
 | 826 | 	{ "Right Input Inverting Mux", "IN2R", "IN2R" }, | 
 | 827 | 	{ "Right Input Inverting Mux", "IN3R", "IN3R" }, | 
 | 828 |  | 
 | 829 | 	{ "Left Input Mode Mux", "Single-Ended", "Left Input Inverting Mux" }, | 
 | 830 | 	{ "Left Input Mode Mux", "Differential Line", | 
 | 831 | 	  "Left Input Mux" }, | 
 | 832 | 	{ "Left Input Mode Mux", "Differential Line", | 
 | 833 | 	  "Left Input Inverting Mux" }, | 
 | 834 | 	{ "Left Input Mode Mux", "Differential Mic", | 
 | 835 | 	  "Left Input Mux" }, | 
 | 836 | 	{ "Left Input Mode Mux", "Differential Mic", | 
 | 837 | 	  "Left Input Inverting Mux" }, | 
 | 838 |  | 
 | 839 | 	{ "Right Input Mode Mux", "Single-Ended", | 
 | 840 | 	  "Right Input Inverting Mux" }, | 
 | 841 | 	{ "Right Input Mode Mux", "Differential Line", | 
 | 842 | 	  "Right Input Mux" }, | 
 | 843 | 	{ "Right Input Mode Mux", "Differential Line", | 
 | 844 | 	  "Right Input Inverting Mux" }, | 
 | 845 | 	{ "Right Input Mode Mux", "Differential Mic", | 
 | 846 | 	  "Right Input Mux" }, | 
 | 847 | 	{ "Right Input Mode Mux", "Differential Mic", | 
 | 848 | 	  "Right Input Inverting Mux" }, | 
 | 849 |  | 
 | 850 | 	{ "Left Input PGA", NULL, "Left Input Mode Mux" }, | 
 | 851 | 	{ "Right Input PGA", NULL, "Right Input Mode Mux" }, | 
 | 852 |  | 
 | 853 | 	{ "ADCL", NULL, "Left Input PGA" }, | 
| Mark Brown | c2aef4f | 2009-04-22 20:04:44 +0100 | [diff] [blame] | 854 | 	{ "ADCL", NULL, "CLK_DSP" }, | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 855 | 	{ "ADCR", NULL, "Right Input PGA" }, | 
| Mark Brown | c2aef4f | 2009-04-22 20:04:44 +0100 | [diff] [blame] | 856 | 	{ "ADCR", NULL, "CLK_DSP" }, | 
 | 857 |  | 
| Mark Brown | 291ce18 | 2009-04-22 21:36:14 +0100 | [diff] [blame] | 858 | 	{ "DACL Sidetone", "Left", "ADCL" }, | 
 | 859 | 	{ "DACL Sidetone", "Right", "ADCR" }, | 
 | 860 | 	{ "DACR Sidetone", "Left", "ADCL" }, | 
 | 861 | 	{ "DACR Sidetone", "Right", "ADCR" }, | 
 | 862 |  | 
 | 863 | 	{ "DACL", NULL, "DACL Sidetone" }, | 
| Mark Brown | c2aef4f | 2009-04-22 20:04:44 +0100 | [diff] [blame] | 864 | 	{ "DACL", NULL, "CLK_DSP" }, | 
| Mark Brown | 291ce18 | 2009-04-22 21:36:14 +0100 | [diff] [blame] | 865 | 	{ "DACR", NULL, "DACR Sidetone" }, | 
| Mark Brown | c2aef4f | 2009-04-22 20:04:44 +0100 | [diff] [blame] | 866 | 	{ "DACR", NULL, "CLK_DSP" }, | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 867 |  | 
 | 868 | 	{ "Left Output Mixer", "Left Bypass Switch", "Left Input PGA" }, | 
 | 869 | 	{ "Left Output Mixer", "Right Bypass Switch", "Right Input PGA" }, | 
 | 870 | 	{ "Left Output Mixer", "DACL Switch", "DACL" }, | 
 | 871 | 	{ "Left Output Mixer", "DACR Switch", "DACR" }, | 
 | 872 |  | 
 | 873 | 	{ "Right Output Mixer", "Left Bypass Switch", "Left Input PGA" }, | 
 | 874 | 	{ "Right Output Mixer", "Right Bypass Switch", "Right Input PGA" }, | 
 | 875 | 	{ "Right Output Mixer", "DACL Switch", "DACL" }, | 
 | 876 | 	{ "Right Output Mixer", "DACR Switch", "DACR" }, | 
 | 877 |  | 
 | 878 | 	{ "Left Speaker Mixer", "Left Bypass Switch", "Left Input PGA" }, | 
 | 879 | 	{ "Left Speaker Mixer", "Right Bypass Switch", "Right Input PGA" }, | 
 | 880 | 	{ "Left Speaker Mixer", "DACL Switch", "DACL" }, | 
 | 881 | 	{ "Left Speaker Mixer", "DACR Switch", "DACR" }, | 
 | 882 |  | 
 | 883 | 	{ "Right Speaker Mixer", "Left Bypass Switch", "Left Input PGA" }, | 
 | 884 | 	{ "Right Speaker Mixer", "Right Bypass Switch", "Right Input PGA" }, | 
 | 885 | 	{ "Right Speaker Mixer", "DACL Switch", "DACL" }, | 
 | 886 | 	{ "Right Speaker Mixer", "DACR Switch", "DACR" }, | 
 | 887 |  | 
 | 888 | 	{ "Left Line Output PGA", NULL, "Left Output Mixer" }, | 
 | 889 | 	{ "Right Line Output PGA", NULL, "Right Output Mixer" }, | 
 | 890 |  | 
 | 891 | 	{ "Left Headphone Output PGA", NULL, "Left Output Mixer" }, | 
 | 892 | 	{ "Right Headphone Output PGA", NULL, "Right Output Mixer" }, | 
 | 893 |  | 
 | 894 | 	{ "Left Speaker PGA", NULL, "Left Speaker Mixer" }, | 
 | 895 | 	{ "Right Speaker PGA", NULL, "Right Speaker Mixer" }, | 
 | 896 |  | 
 | 897 | 	{ "HPOUTL", NULL, "Left Headphone Output PGA" }, | 
 | 898 | 	{ "HPOUTR", NULL, "Right Headphone Output PGA" }, | 
 | 899 |  | 
 | 900 | 	{ "LINEOUTL", NULL, "Left Line Output PGA" }, | 
 | 901 | 	{ "LINEOUTR", NULL, "Right Line Output PGA" }, | 
 | 902 |  | 
 | 903 | 	{ "LOP", NULL, "Left Speaker PGA" }, | 
 | 904 | 	{ "LON", NULL, "Left Speaker PGA" }, | 
 | 905 |  | 
 | 906 | 	{ "ROP", NULL, "Right Speaker PGA" }, | 
 | 907 | 	{ "RON", NULL, "Right Speaker PGA" }, | 
| Mark Brown | 42768a1 | 2009-04-22 18:39:39 +0100 | [diff] [blame] | 908 |  | 
 | 909 | 	{ "Left Headphone Output PGA", NULL, "Charge Pump" }, | 
 | 910 | 	{ "Right Headphone Output PGA", NULL, "Charge Pump" }, | 
 | 911 | 	{ "Left Line Output PGA", NULL, "Charge Pump" }, | 
 | 912 | 	{ "Right Line Output PGA", NULL, "Charge Pump" }, | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 913 | }; | 
 | 914 |  | 
 | 915 | static int wm8903_add_widgets(struct snd_soc_codec *codec) | 
 | 916 | { | 
 | 917 | 	snd_soc_dapm_new_controls(codec, wm8903_dapm_widgets, | 
 | 918 | 				  ARRAY_SIZE(wm8903_dapm_widgets)); | 
 | 919 |  | 
 | 920 | 	snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); | 
 | 921 |  | 
 | 922 | 	snd_soc_dapm_new_widgets(codec); | 
 | 923 |  | 
 | 924 | 	return 0; | 
 | 925 | } | 
 | 926 |  | 
 | 927 | static int wm8903_set_bias_level(struct snd_soc_codec *codec, | 
 | 928 | 				 enum snd_soc_bias_level level) | 
 | 929 | { | 
 | 930 | 	struct i2c_client *i2c = codec->control_data; | 
 | 931 | 	u16 reg, reg2; | 
 | 932 |  | 
 | 933 | 	switch (level) { | 
 | 934 | 	case SND_SOC_BIAS_ON: | 
 | 935 | 	case SND_SOC_BIAS_PREPARE: | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 936 | 		reg = snd_soc_read(codec, WM8903_VMID_CONTROL_0); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 937 | 		reg &= ~(WM8903_VMID_RES_MASK); | 
 | 938 | 		reg |= WM8903_VMID_RES_50K; | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 939 | 		snd_soc_write(codec, WM8903_VMID_CONTROL_0, reg); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 940 | 		break; | 
 | 941 |  | 
 | 942 | 	case SND_SOC_BIAS_STANDBY: | 
 | 943 | 		if (codec->bias_level == SND_SOC_BIAS_OFF) { | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 944 | 			snd_soc_write(codec, WM8903_CLOCK_RATES_2, | 
| Mark Brown | 3b1228a | 2008-12-10 19:27:10 +0000 | [diff] [blame] | 945 | 				     WM8903_CLK_SYS_ENA); | 
 | 946 |  | 
| Mark Brown | 4dbfe80 | 2009-04-22 20:32:40 +0100 | [diff] [blame] | 947 | 			/* Change DC servo dither level in startup sequence */ | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 948 | 			snd_soc_write(codec, WM8903_WRITE_SEQUENCER_0, 0x11); | 
 | 949 | 			snd_soc_write(codec, WM8903_WRITE_SEQUENCER_1, 0x1257); | 
 | 950 | 			snd_soc_write(codec, WM8903_WRITE_SEQUENCER_2, 0x2); | 
| Mark Brown | 4dbfe80 | 2009-04-22 20:32:40 +0100 | [diff] [blame] | 951 |  | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 952 | 			wm8903_run_sequence(codec, 0); | 
 | 953 | 			wm8903_sync_reg_cache(codec, codec->reg_cache); | 
 | 954 |  | 
 | 955 | 			/* Enable low impedence charge pump output */ | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 956 | 			reg = snd_soc_read(codec, | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 957 | 					  WM8903_CONTROL_INTERFACE_TEST_1); | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 958 | 			snd_soc_write(codec, WM8903_CONTROL_INTERFACE_TEST_1, | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 959 | 				     reg | WM8903_TEST_KEY); | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 960 | 			reg2 = snd_soc_read(codec, WM8903_CHARGE_PUMP_TEST_1); | 
 | 961 | 			snd_soc_write(codec, WM8903_CHARGE_PUMP_TEST_1, | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 962 | 				     reg2 | WM8903_CP_SW_KELVIN_MODE_MASK); | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 963 | 			snd_soc_write(codec, WM8903_CONTROL_INTERFACE_TEST_1, | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 964 | 				     reg); | 
 | 965 |  | 
 | 966 | 			/* By default no bypass paths are enabled so | 
 | 967 | 			 * enable Class W support. | 
 | 968 | 			 */ | 
 | 969 | 			dev_dbg(&i2c->dev, "Enabling Class W\n"); | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 970 | 			snd_soc_write(codec, WM8903_CLASS_W_0, reg | | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 971 | 				     WM8903_CP_DYN_FREQ | WM8903_CP_DYN_V); | 
 | 972 | 		} | 
 | 973 |  | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 974 | 		reg = snd_soc_read(codec, WM8903_VMID_CONTROL_0); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 975 | 		reg &= ~(WM8903_VMID_RES_MASK); | 
 | 976 | 		reg |= WM8903_VMID_RES_250K; | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 977 | 		snd_soc_write(codec, WM8903_VMID_CONTROL_0, reg); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 978 | 		break; | 
 | 979 |  | 
 | 980 | 	case SND_SOC_BIAS_OFF: | 
 | 981 | 		wm8903_run_sequence(codec, 32); | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 982 | 		reg = snd_soc_read(codec, WM8903_CLOCK_RATES_2); | 
| Mark Brown | 3b1228a | 2008-12-10 19:27:10 +0000 | [diff] [blame] | 983 | 		reg &= ~WM8903_CLK_SYS_ENA; | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 984 | 		snd_soc_write(codec, WM8903_CLOCK_RATES_2, reg); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 985 | 		break; | 
 | 986 | 	} | 
 | 987 |  | 
 | 988 | 	codec->bias_level = level; | 
 | 989 |  | 
 | 990 | 	return 0; | 
 | 991 | } | 
 | 992 |  | 
 | 993 | static int wm8903_set_dai_sysclk(struct snd_soc_dai *codec_dai, | 
 | 994 | 				 int clk_id, unsigned int freq, int dir) | 
 | 995 | { | 
 | 996 | 	struct snd_soc_codec *codec = codec_dai->codec; | 
 | 997 | 	struct wm8903_priv *wm8903 = codec->private_data; | 
 | 998 |  | 
 | 999 | 	wm8903->sysclk = freq; | 
 | 1000 |  | 
 | 1001 | 	return 0; | 
 | 1002 | } | 
 | 1003 |  | 
 | 1004 | static int wm8903_set_dai_fmt(struct snd_soc_dai *codec_dai, | 
 | 1005 | 			      unsigned int fmt) | 
 | 1006 | { | 
 | 1007 | 	struct snd_soc_codec *codec = codec_dai->codec; | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 1008 | 	u16 aif1 = snd_soc_read(codec, WM8903_AUDIO_INTERFACE_1); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1009 |  | 
 | 1010 | 	aif1 &= ~(WM8903_LRCLK_DIR | WM8903_BCLK_DIR | WM8903_AIF_FMT_MASK | | 
 | 1011 | 		  WM8903_AIF_LRCLK_INV | WM8903_AIF_BCLK_INV); | 
 | 1012 |  | 
 | 1013 | 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | 
 | 1014 | 	case SND_SOC_DAIFMT_CBS_CFS: | 
 | 1015 | 		break; | 
 | 1016 | 	case SND_SOC_DAIFMT_CBS_CFM: | 
 | 1017 | 		aif1 |= WM8903_LRCLK_DIR; | 
 | 1018 | 		break; | 
 | 1019 | 	case SND_SOC_DAIFMT_CBM_CFM: | 
 | 1020 | 		aif1 |= WM8903_LRCLK_DIR | WM8903_BCLK_DIR; | 
 | 1021 | 		break; | 
 | 1022 | 	case SND_SOC_DAIFMT_CBM_CFS: | 
 | 1023 | 		aif1 |= WM8903_BCLK_DIR; | 
 | 1024 | 		break; | 
 | 1025 | 	default: | 
 | 1026 | 		return -EINVAL; | 
 | 1027 | 	} | 
 | 1028 |  | 
 | 1029 | 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | 
 | 1030 | 	case SND_SOC_DAIFMT_DSP_A: | 
 | 1031 | 		aif1 |= 0x3; | 
 | 1032 | 		break; | 
 | 1033 | 	case SND_SOC_DAIFMT_DSP_B: | 
 | 1034 | 		aif1 |= 0x3 | WM8903_AIF_LRCLK_INV; | 
 | 1035 | 		break; | 
 | 1036 | 	case SND_SOC_DAIFMT_I2S: | 
 | 1037 | 		aif1 |= 0x2; | 
 | 1038 | 		break; | 
 | 1039 | 	case SND_SOC_DAIFMT_RIGHT_J: | 
 | 1040 | 		aif1 |= 0x1; | 
 | 1041 | 		break; | 
 | 1042 | 	case SND_SOC_DAIFMT_LEFT_J: | 
 | 1043 | 		break; | 
 | 1044 | 	default: | 
 | 1045 | 		return -EINVAL; | 
 | 1046 | 	} | 
 | 1047 |  | 
 | 1048 | 	/* Clock inversion */ | 
 | 1049 | 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | 
 | 1050 | 	case SND_SOC_DAIFMT_DSP_A: | 
 | 1051 | 	case SND_SOC_DAIFMT_DSP_B: | 
 | 1052 | 		/* frame inversion not valid for DSP modes */ | 
 | 1053 | 		switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | 
 | 1054 | 		case SND_SOC_DAIFMT_NB_NF: | 
 | 1055 | 			break; | 
 | 1056 | 		case SND_SOC_DAIFMT_IB_NF: | 
 | 1057 | 			aif1 |= WM8903_AIF_BCLK_INV; | 
 | 1058 | 			break; | 
 | 1059 | 		default: | 
 | 1060 | 			return -EINVAL; | 
 | 1061 | 		} | 
 | 1062 | 		break; | 
 | 1063 | 	case SND_SOC_DAIFMT_I2S: | 
 | 1064 | 	case SND_SOC_DAIFMT_RIGHT_J: | 
 | 1065 | 	case SND_SOC_DAIFMT_LEFT_J: | 
 | 1066 | 		switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | 
 | 1067 | 		case SND_SOC_DAIFMT_NB_NF: | 
 | 1068 | 			break; | 
 | 1069 | 		case SND_SOC_DAIFMT_IB_IF: | 
 | 1070 | 			aif1 |= WM8903_AIF_BCLK_INV | WM8903_AIF_LRCLK_INV; | 
 | 1071 | 			break; | 
 | 1072 | 		case SND_SOC_DAIFMT_IB_NF: | 
 | 1073 | 			aif1 |= WM8903_AIF_BCLK_INV; | 
 | 1074 | 			break; | 
 | 1075 | 		case SND_SOC_DAIFMT_NB_IF: | 
 | 1076 | 			aif1 |= WM8903_AIF_LRCLK_INV; | 
 | 1077 | 			break; | 
 | 1078 | 		default: | 
 | 1079 | 			return -EINVAL; | 
 | 1080 | 		} | 
 | 1081 | 		break; | 
 | 1082 | 	default: | 
 | 1083 | 		return -EINVAL; | 
 | 1084 | 	} | 
 | 1085 |  | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 1086 | 	snd_soc_write(codec, WM8903_AUDIO_INTERFACE_1, aif1); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1087 |  | 
 | 1088 | 	return 0; | 
 | 1089 | } | 
 | 1090 |  | 
 | 1091 | static int wm8903_digital_mute(struct snd_soc_dai *codec_dai, int mute) | 
 | 1092 | { | 
 | 1093 | 	struct snd_soc_codec *codec = codec_dai->codec; | 
 | 1094 | 	u16 reg; | 
 | 1095 |  | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 1096 | 	reg = snd_soc_read(codec, WM8903_DAC_DIGITAL_1); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1097 |  | 
 | 1098 | 	if (mute) | 
 | 1099 | 		reg |= WM8903_DAC_MUTE; | 
 | 1100 | 	else | 
 | 1101 | 		reg &= ~WM8903_DAC_MUTE; | 
 | 1102 |  | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 1103 | 	snd_soc_write(codec, WM8903_DAC_DIGITAL_1, reg); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1104 |  | 
 | 1105 | 	return 0; | 
 | 1106 | } | 
 | 1107 |  | 
 | 1108 | /* Lookup table for CLK_SYS/fs ratio.  256fs or more is recommended | 
 | 1109 |  * for optimal performance so we list the lower rates first and match | 
 | 1110 |  * on the last match we find. */ | 
 | 1111 | static struct { | 
 | 1112 | 	int div; | 
 | 1113 | 	int rate; | 
 | 1114 | 	int mode; | 
 | 1115 | 	int mclk_div; | 
 | 1116 | } clk_sys_ratios[] = { | 
 | 1117 | 	{   64, 0x0, 0x0, 1 }, | 
 | 1118 | 	{   68, 0x0, 0x1, 1 }, | 
 | 1119 | 	{  125, 0x0, 0x2, 1 }, | 
 | 1120 | 	{  128, 0x1, 0x0, 1 }, | 
 | 1121 | 	{  136, 0x1, 0x1, 1 }, | 
 | 1122 | 	{  192, 0x2, 0x0, 1 }, | 
 | 1123 | 	{  204, 0x2, 0x1, 1 }, | 
 | 1124 |  | 
 | 1125 | 	{   64, 0x0, 0x0, 2 }, | 
 | 1126 | 	{   68, 0x0, 0x1, 2 }, | 
 | 1127 | 	{  125, 0x0, 0x2, 2 }, | 
 | 1128 | 	{  128, 0x1, 0x0, 2 }, | 
 | 1129 | 	{  136, 0x1, 0x1, 2 }, | 
 | 1130 | 	{  192, 0x2, 0x0, 2 }, | 
 | 1131 | 	{  204, 0x2, 0x1, 2 }, | 
 | 1132 |  | 
 | 1133 | 	{  250, 0x2, 0x2, 1 }, | 
 | 1134 | 	{  256, 0x3, 0x0, 1 }, | 
 | 1135 | 	{  272, 0x3, 0x1, 1 }, | 
 | 1136 | 	{  384, 0x4, 0x0, 1 }, | 
 | 1137 | 	{  408, 0x4, 0x1, 1 }, | 
 | 1138 | 	{  375, 0x4, 0x2, 1 }, | 
 | 1139 | 	{  512, 0x5, 0x0, 1 }, | 
 | 1140 | 	{  544, 0x5, 0x1, 1 }, | 
 | 1141 | 	{  500, 0x5, 0x2, 1 }, | 
 | 1142 | 	{  768, 0x6, 0x0, 1 }, | 
 | 1143 | 	{  816, 0x6, 0x1, 1 }, | 
 | 1144 | 	{  750, 0x6, 0x2, 1 }, | 
 | 1145 | 	{ 1024, 0x7, 0x0, 1 }, | 
 | 1146 | 	{ 1088, 0x7, 0x1, 1 }, | 
 | 1147 | 	{ 1000, 0x7, 0x2, 1 }, | 
 | 1148 | 	{ 1408, 0x8, 0x0, 1 }, | 
 | 1149 | 	{ 1496, 0x8, 0x1, 1 }, | 
 | 1150 | 	{ 1536, 0x9, 0x0, 1 }, | 
 | 1151 | 	{ 1632, 0x9, 0x1, 1 }, | 
 | 1152 | 	{ 1500, 0x9, 0x2, 1 }, | 
 | 1153 |  | 
 | 1154 | 	{  250, 0x2, 0x2, 2 }, | 
 | 1155 | 	{  256, 0x3, 0x0, 2 }, | 
 | 1156 | 	{  272, 0x3, 0x1, 2 }, | 
 | 1157 | 	{  384, 0x4, 0x0, 2 }, | 
 | 1158 | 	{  408, 0x4, 0x1, 2 }, | 
 | 1159 | 	{  375, 0x4, 0x2, 2 }, | 
 | 1160 | 	{  512, 0x5, 0x0, 2 }, | 
 | 1161 | 	{  544, 0x5, 0x1, 2 }, | 
 | 1162 | 	{  500, 0x5, 0x2, 2 }, | 
 | 1163 | 	{  768, 0x6, 0x0, 2 }, | 
 | 1164 | 	{  816, 0x6, 0x1, 2 }, | 
 | 1165 | 	{  750, 0x6, 0x2, 2 }, | 
 | 1166 | 	{ 1024, 0x7, 0x0, 2 }, | 
 | 1167 | 	{ 1088, 0x7, 0x1, 2 }, | 
 | 1168 | 	{ 1000, 0x7, 0x2, 2 }, | 
 | 1169 | 	{ 1408, 0x8, 0x0, 2 }, | 
 | 1170 | 	{ 1496, 0x8, 0x1, 2 }, | 
 | 1171 | 	{ 1536, 0x9, 0x0, 2 }, | 
 | 1172 | 	{ 1632, 0x9, 0x1, 2 }, | 
 | 1173 | 	{ 1500, 0x9, 0x2, 2 }, | 
 | 1174 | }; | 
 | 1175 |  | 
 | 1176 | /* CLK_SYS/BCLK ratios - multiplied by 10 due to .5s */ | 
 | 1177 | static struct { | 
 | 1178 | 	int ratio; | 
 | 1179 | 	int div; | 
 | 1180 | } bclk_divs[] = { | 
 | 1181 | 	{  10,  0 }, | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1182 | 	{  20,  2 }, | 
 | 1183 | 	{  30,  3 }, | 
 | 1184 | 	{  40,  4 }, | 
 | 1185 | 	{  50,  5 }, | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1186 | 	{  60,  7 }, | 
 | 1187 | 	{  80,  8 }, | 
 | 1188 | 	{ 100,  9 }, | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1189 | 	{ 120, 11 }, | 
 | 1190 | 	{ 160, 12 }, | 
 | 1191 | 	{ 200, 13 }, | 
 | 1192 | 	{ 220, 14 }, | 
 | 1193 | 	{ 240, 15 }, | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1194 | 	{ 300, 17 }, | 
 | 1195 | 	{ 320, 18 }, | 
 | 1196 | 	{ 440, 19 }, | 
 | 1197 | 	{ 480, 20 }, | 
 | 1198 | }; | 
 | 1199 |  | 
 | 1200 | /* Sample rates for DSP */ | 
 | 1201 | static struct { | 
 | 1202 | 	int rate; | 
 | 1203 | 	int value; | 
 | 1204 | } sample_rates[] = { | 
 | 1205 | 	{  8000,  0 }, | 
 | 1206 | 	{ 11025,  1 }, | 
 | 1207 | 	{ 12000,  2 }, | 
 | 1208 | 	{ 16000,  3 }, | 
 | 1209 | 	{ 22050,  4 }, | 
 | 1210 | 	{ 24000,  5 }, | 
 | 1211 | 	{ 32000,  6 }, | 
 | 1212 | 	{ 44100,  7 }, | 
 | 1213 | 	{ 48000,  8 }, | 
 | 1214 | 	{ 88200,  9 }, | 
 | 1215 | 	{ 96000, 10 }, | 
 | 1216 | 	{ 0,      0 }, | 
 | 1217 | }; | 
 | 1218 |  | 
| Mark Brown | dee89c4 | 2008-11-18 22:11:38 +0000 | [diff] [blame] | 1219 | static int wm8903_startup(struct snd_pcm_substream *substream, | 
 | 1220 | 			  struct snd_soc_dai *dai) | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1221 | { | 
 | 1222 | 	struct snd_soc_pcm_runtime *rtd = substream->private_data; | 
 | 1223 | 	struct snd_soc_device *socdev = rtd->socdev; | 
| Mark Brown | 6627a65 | 2009-01-23 22:55:23 +0000 | [diff] [blame] | 1224 | 	struct snd_soc_codec *codec = socdev->card->codec; | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1225 | 	struct wm8903_priv *wm8903 = codec->private_data; | 
 | 1226 | 	struct i2c_client *i2c = codec->control_data; | 
 | 1227 | 	struct snd_pcm_runtime *master_runtime; | 
 | 1228 |  | 
 | 1229 | 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 
 | 1230 | 		wm8903->playback_active++; | 
 | 1231 | 	else | 
 | 1232 | 		wm8903->capture_active++; | 
 | 1233 |  | 
 | 1234 | 	/* The DAI has shared clocks so if we already have a playback or | 
 | 1235 | 	 * capture going then constrain this substream to match it. | 
 | 1236 | 	 */ | 
 | 1237 | 	if (wm8903->master_substream) { | 
 | 1238 | 		master_runtime = wm8903->master_substream->runtime; | 
 | 1239 |  | 
| Mark Brown | 727fb90 | 2009-04-22 21:06:14 +0100 | [diff] [blame] | 1240 | 		dev_dbg(&i2c->dev, "Constraining to %d bits\n", | 
 | 1241 | 			master_runtime->sample_bits); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1242 |  | 
 | 1243 | 		snd_pcm_hw_constraint_minmax(substream->runtime, | 
 | 1244 | 					     SNDRV_PCM_HW_PARAM_SAMPLE_BITS, | 
 | 1245 | 					     master_runtime->sample_bits, | 
 | 1246 | 					     master_runtime->sample_bits); | 
 | 1247 |  | 
 | 1248 | 		wm8903->slave_substream = substream; | 
 | 1249 | 	} else | 
 | 1250 | 		wm8903->master_substream = substream; | 
 | 1251 |  | 
 | 1252 | 	return 0; | 
 | 1253 | } | 
 | 1254 |  | 
| Mark Brown | dee89c4 | 2008-11-18 22:11:38 +0000 | [diff] [blame] | 1255 | static void wm8903_shutdown(struct snd_pcm_substream *substream, | 
 | 1256 | 			    struct snd_soc_dai *dai) | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1257 | { | 
 | 1258 | 	struct snd_soc_pcm_runtime *rtd = substream->private_data; | 
 | 1259 | 	struct snd_soc_device *socdev = rtd->socdev; | 
| Mark Brown | 6627a65 | 2009-01-23 22:55:23 +0000 | [diff] [blame] | 1260 | 	struct snd_soc_codec *codec = socdev->card->codec; | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1261 | 	struct wm8903_priv *wm8903 = codec->private_data; | 
 | 1262 |  | 
 | 1263 | 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 
 | 1264 | 		wm8903->playback_active--; | 
 | 1265 | 	else | 
 | 1266 | 		wm8903->capture_active--; | 
 | 1267 |  | 
 | 1268 | 	if (wm8903->master_substream == substream) | 
 | 1269 | 		wm8903->master_substream = wm8903->slave_substream; | 
 | 1270 |  | 
 | 1271 | 	wm8903->slave_substream = NULL; | 
 | 1272 | } | 
 | 1273 |  | 
 | 1274 | static int wm8903_hw_params(struct snd_pcm_substream *substream, | 
| Mark Brown | dee89c4 | 2008-11-18 22:11:38 +0000 | [diff] [blame] | 1275 | 			    struct snd_pcm_hw_params *params, | 
 | 1276 | 			    struct snd_soc_dai *dai) | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1277 | { | 
 | 1278 | 	struct snd_soc_pcm_runtime *rtd = substream->private_data; | 
 | 1279 | 	struct snd_soc_device *socdev = rtd->socdev; | 
| Mark Brown | 6627a65 | 2009-01-23 22:55:23 +0000 | [diff] [blame] | 1280 | 	struct snd_soc_codec *codec = socdev->card->codec; | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1281 | 	struct wm8903_priv *wm8903 = codec->private_data; | 
 | 1282 | 	struct i2c_client *i2c = codec->control_data; | 
 | 1283 | 	int fs = params_rate(params); | 
 | 1284 | 	int bclk; | 
 | 1285 | 	int bclk_div; | 
 | 1286 | 	int i; | 
 | 1287 | 	int dsp_config; | 
 | 1288 | 	int clk_config; | 
 | 1289 | 	int best_val; | 
 | 1290 | 	int cur_val; | 
 | 1291 | 	int clk_sys; | 
 | 1292 |  | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 1293 | 	u16 aif1 = snd_soc_read(codec, WM8903_AUDIO_INTERFACE_1); | 
 | 1294 | 	u16 aif2 = snd_soc_read(codec, WM8903_AUDIO_INTERFACE_2); | 
 | 1295 | 	u16 aif3 = snd_soc_read(codec, WM8903_AUDIO_INTERFACE_3); | 
 | 1296 | 	u16 clock0 = snd_soc_read(codec, WM8903_CLOCK_RATES_0); | 
 | 1297 | 	u16 clock1 = snd_soc_read(codec, WM8903_CLOCK_RATES_1); | 
 | 1298 | 	u16 dac_digital1 = snd_soc_read(codec, WM8903_DAC_DIGITAL_1); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1299 |  | 
 | 1300 | 	if (substream == wm8903->slave_substream) { | 
 | 1301 | 		dev_dbg(&i2c->dev, "Ignoring hw_params for slave substream\n"); | 
 | 1302 | 		return 0; | 
 | 1303 | 	} | 
 | 1304 |  | 
| Mark Brown | 9e79261 | 2009-06-12 17:27:07 +0100 | [diff] [blame] | 1305 | 	/* Enable sloping stopband filter for low sample rates */ | 
 | 1306 | 	if (fs <= 24000) | 
 | 1307 | 		dac_digital1 |= WM8903_DAC_SB_FILT; | 
 | 1308 | 	else | 
 | 1309 | 		dac_digital1 &= ~WM8903_DAC_SB_FILT; | 
 | 1310 |  | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1311 | 	/* Configure sample rate logic for DSP - choose nearest rate */ | 
 | 1312 | 	dsp_config = 0; | 
 | 1313 | 	best_val = abs(sample_rates[dsp_config].rate - fs); | 
 | 1314 | 	for (i = 1; i < ARRAY_SIZE(sample_rates); i++) { | 
 | 1315 | 		cur_val = abs(sample_rates[i].rate - fs); | 
 | 1316 | 		if (cur_val <= best_val) { | 
 | 1317 | 			dsp_config = i; | 
 | 1318 | 			best_val = cur_val; | 
 | 1319 | 		} | 
 | 1320 | 	} | 
 | 1321 |  | 
 | 1322 | 	/* Constraints should stop us hitting this but let's make sure */ | 
 | 1323 | 	if (wm8903->capture_active) | 
 | 1324 | 		switch (sample_rates[dsp_config].rate) { | 
 | 1325 | 		case 88200: | 
 | 1326 | 		case 96000: | 
 | 1327 | 			dev_err(&i2c->dev, "%dHz unsupported by ADC\n", | 
 | 1328 | 				fs); | 
 | 1329 | 			return -EINVAL; | 
 | 1330 |  | 
 | 1331 | 		default: | 
 | 1332 | 			break; | 
 | 1333 | 		} | 
 | 1334 |  | 
 | 1335 | 	dev_dbg(&i2c->dev, "DSP fs = %dHz\n", sample_rates[dsp_config].rate); | 
 | 1336 | 	clock1 &= ~WM8903_SAMPLE_RATE_MASK; | 
 | 1337 | 	clock1 |= sample_rates[dsp_config].value; | 
 | 1338 |  | 
 | 1339 | 	aif1 &= ~WM8903_AIF_WL_MASK; | 
 | 1340 | 	bclk = 2 * fs; | 
 | 1341 | 	switch (params_format(params)) { | 
 | 1342 | 	case SNDRV_PCM_FORMAT_S16_LE: | 
 | 1343 | 		bclk *= 16; | 
 | 1344 | 		break; | 
 | 1345 | 	case SNDRV_PCM_FORMAT_S20_3LE: | 
 | 1346 | 		bclk *= 20; | 
 | 1347 | 		aif1 |= 0x4; | 
 | 1348 | 		break; | 
 | 1349 | 	case SNDRV_PCM_FORMAT_S24_LE: | 
 | 1350 | 		bclk *= 24; | 
 | 1351 | 		aif1 |= 0x8; | 
 | 1352 | 		break; | 
 | 1353 | 	case SNDRV_PCM_FORMAT_S32_LE: | 
 | 1354 | 		bclk *= 32; | 
 | 1355 | 		aif1 |= 0xc; | 
 | 1356 | 		break; | 
 | 1357 | 	default: | 
 | 1358 | 		return -EINVAL; | 
 | 1359 | 	} | 
 | 1360 |  | 
 | 1361 | 	dev_dbg(&i2c->dev, "MCLK = %dHz, target sample rate = %dHz\n", | 
 | 1362 | 		wm8903->sysclk, fs); | 
 | 1363 |  | 
 | 1364 | 	/* We may not have an MCLK which allows us to generate exactly | 
 | 1365 | 	 * the clock we want, particularly with USB derived inputs, so | 
 | 1366 | 	 * approximate. | 
 | 1367 | 	 */ | 
 | 1368 | 	clk_config = 0; | 
 | 1369 | 	best_val = abs((wm8903->sysclk / | 
 | 1370 | 			(clk_sys_ratios[0].mclk_div * | 
 | 1371 | 			 clk_sys_ratios[0].div)) - fs); | 
 | 1372 | 	for (i = 1; i < ARRAY_SIZE(clk_sys_ratios); i++) { | 
 | 1373 | 		cur_val = abs((wm8903->sysclk / | 
 | 1374 | 			       (clk_sys_ratios[i].mclk_div * | 
 | 1375 | 				clk_sys_ratios[i].div)) - fs); | 
 | 1376 |  | 
 | 1377 | 		if (cur_val <= best_val) { | 
 | 1378 | 			clk_config = i; | 
 | 1379 | 			best_val = cur_val; | 
 | 1380 | 		} | 
 | 1381 | 	} | 
 | 1382 |  | 
 | 1383 | 	if (clk_sys_ratios[clk_config].mclk_div == 2) { | 
 | 1384 | 		clock0 |= WM8903_MCLKDIV2; | 
 | 1385 | 		clk_sys = wm8903->sysclk / 2; | 
 | 1386 | 	} else { | 
 | 1387 | 		clock0 &= ~WM8903_MCLKDIV2; | 
 | 1388 | 		clk_sys = wm8903->sysclk; | 
 | 1389 | 	} | 
 | 1390 |  | 
 | 1391 | 	clock1 &= ~(WM8903_CLK_SYS_RATE_MASK | | 
 | 1392 | 		    WM8903_CLK_SYS_MODE_MASK); | 
 | 1393 | 	clock1 |= clk_sys_ratios[clk_config].rate << WM8903_CLK_SYS_RATE_SHIFT; | 
 | 1394 | 	clock1 |= clk_sys_ratios[clk_config].mode << WM8903_CLK_SYS_MODE_SHIFT; | 
 | 1395 |  | 
 | 1396 | 	dev_dbg(&i2c->dev, "CLK_SYS_RATE=%x, CLK_SYS_MODE=%x div=%d\n", | 
 | 1397 | 		clk_sys_ratios[clk_config].rate, | 
 | 1398 | 		clk_sys_ratios[clk_config].mode, | 
 | 1399 | 		clk_sys_ratios[clk_config].div); | 
 | 1400 |  | 
 | 1401 | 	dev_dbg(&i2c->dev, "Actual CLK_SYS = %dHz\n", clk_sys); | 
 | 1402 |  | 
 | 1403 | 	/* We may not get quite the right frequency if using | 
 | 1404 | 	 * approximate clocks so look for the closest match that is | 
 | 1405 | 	 * higher than the target (we need to ensure that there enough | 
 | 1406 | 	 * BCLKs to clock out the samples). | 
 | 1407 | 	 */ | 
 | 1408 | 	bclk_div = 0; | 
 | 1409 | 	best_val = ((clk_sys * 10) / bclk_divs[0].ratio) - bclk; | 
 | 1410 | 	i = 1; | 
 | 1411 | 	while (i < ARRAY_SIZE(bclk_divs)) { | 
 | 1412 | 		cur_val = ((clk_sys * 10) / bclk_divs[i].ratio) - bclk; | 
 | 1413 | 		if (cur_val < 0) /* BCLK table is sorted */ | 
 | 1414 | 			break; | 
 | 1415 | 		bclk_div = i; | 
 | 1416 | 		best_val = cur_val; | 
 | 1417 | 		i++; | 
 | 1418 | 	} | 
 | 1419 |  | 
 | 1420 | 	aif2 &= ~WM8903_BCLK_DIV_MASK; | 
 | 1421 | 	aif3 &= ~WM8903_LRCLK_RATE_MASK; | 
 | 1422 |  | 
 | 1423 | 	dev_dbg(&i2c->dev, "BCLK ratio %d for %dHz - actual BCLK = %dHz\n", | 
 | 1424 | 		bclk_divs[bclk_div].ratio / 10, bclk, | 
 | 1425 | 		(clk_sys * 10) / bclk_divs[bclk_div].ratio); | 
 | 1426 |  | 
 | 1427 | 	aif2 |= bclk_divs[bclk_div].div; | 
 | 1428 | 	aif3 |= bclk / fs; | 
 | 1429 |  | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 1430 | 	snd_soc_write(codec, WM8903_CLOCK_RATES_0, clock0); | 
 | 1431 | 	snd_soc_write(codec, WM8903_CLOCK_RATES_1, clock1); | 
 | 1432 | 	snd_soc_write(codec, WM8903_AUDIO_INTERFACE_1, aif1); | 
 | 1433 | 	snd_soc_write(codec, WM8903_AUDIO_INTERFACE_2, aif2); | 
 | 1434 | 	snd_soc_write(codec, WM8903_AUDIO_INTERFACE_3, aif3); | 
 | 1435 | 	snd_soc_write(codec, WM8903_DAC_DIGITAL_1, dac_digital1); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1436 |  | 
 | 1437 | 	return 0; | 
 | 1438 | } | 
 | 1439 |  | 
 | 1440 | #define WM8903_PLAYBACK_RATES (SNDRV_PCM_RATE_8000 |\ | 
 | 1441 | 			       SNDRV_PCM_RATE_11025 |	\ | 
 | 1442 | 			       SNDRV_PCM_RATE_16000 |	\ | 
 | 1443 | 			       SNDRV_PCM_RATE_22050 |	\ | 
 | 1444 | 			       SNDRV_PCM_RATE_32000 |	\ | 
 | 1445 | 			       SNDRV_PCM_RATE_44100 |	\ | 
 | 1446 | 			       SNDRV_PCM_RATE_48000 |	\ | 
 | 1447 | 			       SNDRV_PCM_RATE_88200 |	\ | 
 | 1448 | 			       SNDRV_PCM_RATE_96000) | 
 | 1449 |  | 
 | 1450 | #define WM8903_CAPTURE_RATES (SNDRV_PCM_RATE_8000 |\ | 
 | 1451 | 			      SNDRV_PCM_RATE_11025 |	\ | 
 | 1452 | 			      SNDRV_PCM_RATE_16000 |	\ | 
 | 1453 | 			      SNDRV_PCM_RATE_22050 |	\ | 
 | 1454 | 			      SNDRV_PCM_RATE_32000 |	\ | 
 | 1455 | 			      SNDRV_PCM_RATE_44100 |	\ | 
 | 1456 | 			      SNDRV_PCM_RATE_48000) | 
 | 1457 |  | 
 | 1458 | #define WM8903_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ | 
 | 1459 | 			SNDRV_PCM_FMTBIT_S20_3LE |\ | 
 | 1460 | 			SNDRV_PCM_FMTBIT_S24_LE) | 
 | 1461 |  | 
| Eric Miao | 6335d05 | 2009-03-03 09:41:00 +0800 | [diff] [blame] | 1462 | static struct snd_soc_dai_ops wm8903_dai_ops = { | 
 | 1463 | 	.startup	= wm8903_startup, | 
 | 1464 | 	.shutdown	= wm8903_shutdown, | 
 | 1465 | 	.hw_params	= wm8903_hw_params, | 
 | 1466 | 	.digital_mute	= wm8903_digital_mute, | 
 | 1467 | 	.set_fmt	= wm8903_set_dai_fmt, | 
 | 1468 | 	.set_sysclk	= wm8903_set_dai_sysclk, | 
 | 1469 | }; | 
 | 1470 |  | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1471 | struct snd_soc_dai wm8903_dai = { | 
 | 1472 | 	.name = "WM8903", | 
 | 1473 | 	.playback = { | 
 | 1474 | 		.stream_name = "Playback", | 
 | 1475 | 		.channels_min = 2, | 
 | 1476 | 		.channels_max = 2, | 
 | 1477 | 		.rates = WM8903_PLAYBACK_RATES, | 
 | 1478 | 		.formats = WM8903_FORMATS, | 
 | 1479 | 	}, | 
 | 1480 | 	.capture = { | 
 | 1481 | 		 .stream_name = "Capture", | 
 | 1482 | 		 .channels_min = 2, | 
 | 1483 | 		 .channels_max = 2, | 
 | 1484 | 		 .rates = WM8903_CAPTURE_RATES, | 
 | 1485 | 		 .formats = WM8903_FORMATS, | 
 | 1486 | 	 }, | 
| Eric Miao | 6335d05 | 2009-03-03 09:41:00 +0800 | [diff] [blame] | 1487 | 	.ops = &wm8903_dai_ops, | 
| Mark Brown | 0d960e8 | 2009-04-16 10:08:39 +0100 | [diff] [blame] | 1488 | 	.symmetric_rates = 1, | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1489 | }; | 
 | 1490 | EXPORT_SYMBOL_GPL(wm8903_dai); | 
 | 1491 |  | 
 | 1492 | static int wm8903_suspend(struct platform_device *pdev, pm_message_t state) | 
 | 1493 | { | 
 | 1494 | 	struct snd_soc_device *socdev = platform_get_drvdata(pdev); | 
| Mark Brown | 6627a65 | 2009-01-23 22:55:23 +0000 | [diff] [blame] | 1495 | 	struct snd_soc_codec *codec = socdev->card->codec; | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1496 |  | 
 | 1497 | 	wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF); | 
 | 1498 |  | 
 | 1499 | 	return 0; | 
 | 1500 | } | 
 | 1501 |  | 
 | 1502 | static int wm8903_resume(struct platform_device *pdev) | 
 | 1503 | { | 
 | 1504 | 	struct snd_soc_device *socdev = platform_get_drvdata(pdev); | 
| Mark Brown | 6627a65 | 2009-01-23 22:55:23 +0000 | [diff] [blame] | 1505 | 	struct snd_soc_codec *codec = socdev->card->codec; | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1506 | 	struct i2c_client *i2c = codec->control_data; | 
 | 1507 | 	int i; | 
 | 1508 | 	u16 *reg_cache = codec->reg_cache; | 
 | 1509 | 	u16 *tmp_cache = kmemdup(codec->reg_cache, sizeof(wm8903_reg_defaults), | 
 | 1510 | 				 GFP_KERNEL); | 
 | 1511 |  | 
 | 1512 | 	/* Bring the codec back up to standby first to minimise pop/clicks */ | 
 | 1513 | 	wm8903_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | 
 | 1514 | 	wm8903_set_bias_level(codec, codec->suspend_bias_level); | 
 | 1515 |  | 
 | 1516 | 	/* Sync back everything else */ | 
 | 1517 | 	if (tmp_cache) { | 
 | 1518 | 		for (i = 2; i < ARRAY_SIZE(wm8903_reg_defaults); i++) | 
 | 1519 | 			if (tmp_cache[i] != reg_cache[i]) | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 1520 | 				snd_soc_write(codec, i, tmp_cache[i]); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1521 | 	} else { | 
 | 1522 | 		dev_err(&i2c->dev, "Failed to allocate temporary cache\n"); | 
 | 1523 | 	} | 
 | 1524 |  | 
 | 1525 | 	return 0; | 
 | 1526 | } | 
 | 1527 |  | 
| Mark Brown | d58d5d5 | 2008-12-10 18:36:42 +0000 | [diff] [blame] | 1528 | static struct snd_soc_codec *wm8903_codec; | 
 | 1529 |  | 
| Mark Brown | c6f2981 | 2009-02-18 21:25:40 +0000 | [diff] [blame] | 1530 | static __devinit int wm8903_i2c_probe(struct i2c_client *i2c, | 
 | 1531 | 				      const struct i2c_device_id *id) | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1532 | { | 
| Mark Brown | d58d5d5 | 2008-12-10 18:36:42 +0000 | [diff] [blame] | 1533 | 	struct wm8903_priv *wm8903; | 
 | 1534 | 	struct snd_soc_codec *codec; | 
 | 1535 | 	int ret; | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1536 | 	u16 val; | 
 | 1537 |  | 
| Mark Brown | d58d5d5 | 2008-12-10 18:36:42 +0000 | [diff] [blame] | 1538 | 	wm8903 = kzalloc(sizeof(struct wm8903_priv), GFP_KERNEL); | 
 | 1539 | 	if (wm8903 == NULL) | 
 | 1540 | 		return -ENOMEM; | 
 | 1541 |  | 
 | 1542 | 	codec = &wm8903->codec; | 
 | 1543 |  | 
 | 1544 | 	mutex_init(&codec->mutex); | 
 | 1545 | 	INIT_LIST_HEAD(&codec->dapm_widgets); | 
 | 1546 | 	INIT_LIST_HEAD(&codec->dapm_paths); | 
 | 1547 |  | 
 | 1548 | 	codec->dev = &i2c->dev; | 
 | 1549 | 	codec->name = "WM8903"; | 
 | 1550 | 	codec->owner = THIS_MODULE; | 
| Mark Brown | d58d5d5 | 2008-12-10 18:36:42 +0000 | [diff] [blame] | 1551 | 	codec->bias_level = SND_SOC_BIAS_OFF; | 
 | 1552 | 	codec->set_bias_level = wm8903_set_bias_level; | 
 | 1553 | 	codec->dai = &wm8903_dai; | 
 | 1554 | 	codec->num_dai = 1; | 
 | 1555 | 	codec->reg_cache_size = ARRAY_SIZE(wm8903->reg_cache); | 
 | 1556 | 	codec->reg_cache = &wm8903->reg_cache[0]; | 
 | 1557 | 	codec->private_data = wm8903; | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 1558 | 	codec->volatile_register = wm8903_volatile_register; | 
| Mark Brown | d58d5d5 | 2008-12-10 18:36:42 +0000 | [diff] [blame] | 1559 |  | 
 | 1560 | 	i2c_set_clientdata(i2c, codec); | 
 | 1561 | 	codec->control_data = i2c; | 
 | 1562 |  | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 1563 | 	ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C); | 
 | 1564 | 	if (ret != 0) { | 
 | 1565 | 		dev_err(&i2c->dev, "Failed to set cache I/O: %d\n", ret); | 
 | 1566 | 		goto err; | 
 | 1567 | 	} | 
 | 1568 |  | 
 | 1569 | 	val = snd_soc_read(codec, WM8903_SW_RESET_AND_ID); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1570 | 	if (val != wm8903_reg_defaults[WM8903_SW_RESET_AND_ID]) { | 
 | 1571 | 		dev_err(&i2c->dev, | 
 | 1572 | 			"Device with ID register %x is not a WM8903\n", val); | 
 | 1573 | 		return -ENODEV; | 
 | 1574 | 	} | 
 | 1575 |  | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 1576 | 	val = snd_soc_read(codec, WM8903_REVISION_NUMBER); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1577 | 	dev_info(&i2c->dev, "WM8903 revision %d\n", | 
 | 1578 | 		 val & WM8903_CHIP_REV_MASK); | 
 | 1579 |  | 
 | 1580 | 	wm8903_reset(codec); | 
 | 1581 |  | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1582 | 	/* power on device */ | 
 | 1583 | 	wm8903_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | 
 | 1584 |  | 
 | 1585 | 	/* Latch volume update bits */ | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 1586 | 	val = snd_soc_read(codec, WM8903_ADC_DIGITAL_VOLUME_LEFT); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1587 | 	val |= WM8903_ADCVU; | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 1588 | 	snd_soc_write(codec, WM8903_ADC_DIGITAL_VOLUME_LEFT, val); | 
 | 1589 | 	snd_soc_write(codec, WM8903_ADC_DIGITAL_VOLUME_RIGHT, val); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1590 |  | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 1591 | 	val = snd_soc_read(codec, WM8903_DAC_DIGITAL_VOLUME_LEFT); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1592 | 	val |= WM8903_DACVU; | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 1593 | 	snd_soc_write(codec, WM8903_DAC_DIGITAL_VOLUME_LEFT, val); | 
 | 1594 | 	snd_soc_write(codec, WM8903_DAC_DIGITAL_VOLUME_RIGHT, val); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1595 |  | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 1596 | 	val = snd_soc_read(codec, WM8903_ANALOGUE_OUT1_LEFT); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1597 | 	val |= WM8903_HPOUTVU; | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 1598 | 	snd_soc_write(codec, WM8903_ANALOGUE_OUT1_LEFT, val); | 
 | 1599 | 	snd_soc_write(codec, WM8903_ANALOGUE_OUT1_RIGHT, val); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1600 |  | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 1601 | 	val = snd_soc_read(codec, WM8903_ANALOGUE_OUT2_LEFT); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1602 | 	val |= WM8903_LINEOUTVU; | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 1603 | 	snd_soc_write(codec, WM8903_ANALOGUE_OUT2_LEFT, val); | 
 | 1604 | 	snd_soc_write(codec, WM8903_ANALOGUE_OUT2_RIGHT, val); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1605 |  | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 1606 | 	val = snd_soc_read(codec, WM8903_ANALOGUE_OUT3_LEFT); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1607 | 	val |= WM8903_SPKVU; | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 1608 | 	snd_soc_write(codec, WM8903_ANALOGUE_OUT3_LEFT, val); | 
 | 1609 | 	snd_soc_write(codec, WM8903_ANALOGUE_OUT3_RIGHT, val); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1610 |  | 
 | 1611 | 	/* Enable DAC soft mute by default */ | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 1612 | 	val = snd_soc_read(codec, WM8903_DAC_DIGITAL_1); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1613 | 	val |= WM8903_DAC_MUTEMODE; | 
| Mark Brown | 8d50e44 | 2009-07-10 23:12:01 +0100 | [diff] [blame] | 1614 | 	snd_soc_write(codec, WM8903_DAC_DIGITAL_1, val); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1615 |  | 
| Mark Brown | d58d5d5 | 2008-12-10 18:36:42 +0000 | [diff] [blame] | 1616 | 	wm8903_dai.dev = &i2c->dev; | 
 | 1617 | 	wm8903_codec = codec; | 
 | 1618 |  | 
 | 1619 | 	ret = snd_soc_register_codec(codec); | 
 | 1620 | 	if (ret != 0) { | 
 | 1621 | 		dev_err(&i2c->dev, "Failed to register codec: %d\n", ret); | 
 | 1622 | 		goto err; | 
 | 1623 | 	} | 
 | 1624 |  | 
 | 1625 | 	ret = snd_soc_register_dai(&wm8903_dai); | 
 | 1626 | 	if (ret != 0) { | 
 | 1627 | 		dev_err(&i2c->dev, "Failed to register DAI: %d\n", ret); | 
 | 1628 | 		goto err_codec; | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1629 | 	} | 
 | 1630 |  | 
 | 1631 | 	return ret; | 
 | 1632 |  | 
| Mark Brown | d58d5d5 | 2008-12-10 18:36:42 +0000 | [diff] [blame] | 1633 | err_codec: | 
 | 1634 | 	snd_soc_unregister_codec(codec); | 
 | 1635 | err: | 
 | 1636 | 	wm8903_codec = NULL; | 
 | 1637 | 	kfree(wm8903); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1638 | 	return ret; | 
 | 1639 | } | 
 | 1640 |  | 
| Mark Brown | c6f2981 | 2009-02-18 21:25:40 +0000 | [diff] [blame] | 1641 | static __devexit int wm8903_i2c_remove(struct i2c_client *client) | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1642 | { | 
 | 1643 | 	struct snd_soc_codec *codec = i2c_get_clientdata(client); | 
| Mark Brown | d58d5d5 | 2008-12-10 18:36:42 +0000 | [diff] [blame] | 1644 |  | 
 | 1645 | 	snd_soc_unregister_dai(&wm8903_dai); | 
 | 1646 | 	snd_soc_unregister_codec(codec); | 
 | 1647 |  | 
 | 1648 | 	wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF); | 
 | 1649 |  | 
 | 1650 | 	kfree(codec->private_data); | 
 | 1651 |  | 
 | 1652 | 	wm8903_codec = NULL; | 
 | 1653 | 	wm8903_dai.dev = NULL; | 
 | 1654 |  | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1655 | 	return 0; | 
 | 1656 | } | 
 | 1657 |  | 
| Mark Brown | b3b50b3 | 2009-06-13 22:30:18 +0100 | [diff] [blame] | 1658 | #ifdef CONFIG_PM | 
 | 1659 | static int wm8903_i2c_suspend(struct i2c_client *client, pm_message_t msg) | 
 | 1660 | { | 
 | 1661 | 	return snd_soc_suspend_device(&client->dev); | 
 | 1662 | } | 
 | 1663 |  | 
 | 1664 | static int wm8903_i2c_resume(struct i2c_client *client) | 
 | 1665 | { | 
 | 1666 | 	return snd_soc_resume_device(&client->dev); | 
 | 1667 | } | 
 | 1668 | #else | 
 | 1669 | #define wm8903_i2c_suspend NULL | 
 | 1670 | #define wm8903_i2c_resume NULL | 
 | 1671 | #endif | 
 | 1672 |  | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1673 | /* i2c codec control layer */ | 
 | 1674 | static const struct i2c_device_id wm8903_i2c_id[] = { | 
 | 1675 |        { "wm8903", 0 }, | 
 | 1676 |        { } | 
 | 1677 | }; | 
 | 1678 | MODULE_DEVICE_TABLE(i2c, wm8903_i2c_id); | 
 | 1679 |  | 
 | 1680 | static struct i2c_driver wm8903_i2c_driver = { | 
 | 1681 | 	.driver = { | 
 | 1682 | 		.name = "WM8903", | 
 | 1683 | 		.owner = THIS_MODULE, | 
 | 1684 | 	}, | 
 | 1685 | 	.probe    = wm8903_i2c_probe, | 
| Mark Brown | c6f2981 | 2009-02-18 21:25:40 +0000 | [diff] [blame] | 1686 | 	.remove   = __devexit_p(wm8903_i2c_remove), | 
| Mark Brown | b3b50b3 | 2009-06-13 22:30:18 +0100 | [diff] [blame] | 1687 | 	.suspend  = wm8903_i2c_suspend, | 
 | 1688 | 	.resume   = wm8903_i2c_resume, | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1689 | 	.id_table = wm8903_i2c_id, | 
 | 1690 | }; | 
 | 1691 |  | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1692 | static int wm8903_probe(struct platform_device *pdev) | 
 | 1693 | { | 
 | 1694 | 	struct snd_soc_device *socdev = platform_get_drvdata(pdev); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1695 | 	int ret = 0; | 
 | 1696 |  | 
| Mark Brown | d58d5d5 | 2008-12-10 18:36:42 +0000 | [diff] [blame] | 1697 | 	if (!wm8903_codec) { | 
 | 1698 | 		dev_err(&pdev->dev, "I2C device not yet probed\n"); | 
 | 1699 | 		goto err; | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1700 | 	} | 
 | 1701 |  | 
| Mark Brown | 6627a65 | 2009-01-23 22:55:23 +0000 | [diff] [blame] | 1702 | 	socdev->card->codec = wm8903_codec; | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1703 |  | 
| Mark Brown | d58d5d5 | 2008-12-10 18:36:42 +0000 | [diff] [blame] | 1704 | 	/* register pcms */ | 
 | 1705 | 	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); | 
 | 1706 | 	if (ret < 0) { | 
 | 1707 | 		dev_err(&pdev->dev, "failed to create pcms\n"); | 
 | 1708 | 		goto err; | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1709 | 	} | 
 | 1710 |  | 
| Mark Brown | 6627a65 | 2009-01-23 22:55:23 +0000 | [diff] [blame] | 1711 | 	snd_soc_add_controls(socdev->card->codec, wm8903_snd_controls, | 
| Ian Molton | 3e8e195 | 2009-01-09 00:23:21 +0000 | [diff] [blame] | 1712 | 				ARRAY_SIZE(wm8903_snd_controls)); | 
| Mark Brown | 6627a65 | 2009-01-23 22:55:23 +0000 | [diff] [blame] | 1713 | 	wm8903_add_widgets(socdev->card->codec); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1714 |  | 
| Mark Brown | d58d5d5 | 2008-12-10 18:36:42 +0000 | [diff] [blame] | 1715 | 	ret = snd_soc_init_card(socdev); | 
 | 1716 | 	if (ret < 0) { | 
 | 1717 | 		dev_err(&pdev->dev, "wm8903: failed to register card\n"); | 
 | 1718 | 		goto card_err; | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1719 | 	} | 
 | 1720 |  | 
 | 1721 | 	return ret; | 
 | 1722 |  | 
| Mark Brown | d58d5d5 | 2008-12-10 18:36:42 +0000 | [diff] [blame] | 1723 | card_err: | 
 | 1724 | 	snd_soc_free_pcms(socdev); | 
 | 1725 | 	snd_soc_dapm_free(socdev); | 
 | 1726 | err: | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1727 | 	return ret; | 
 | 1728 | } | 
 | 1729 |  | 
 | 1730 | /* power down chip */ | 
 | 1731 | static int wm8903_remove(struct platform_device *pdev) | 
 | 1732 | { | 
 | 1733 | 	struct snd_soc_device *socdev = platform_get_drvdata(pdev); | 
| Mark Brown | 6627a65 | 2009-01-23 22:55:23 +0000 | [diff] [blame] | 1734 | 	struct snd_soc_codec *codec = socdev->card->codec; | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1735 |  | 
 | 1736 | 	if (codec->control_data) | 
 | 1737 | 		wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF); | 
 | 1738 |  | 
 | 1739 | 	snd_soc_free_pcms(socdev); | 
 | 1740 | 	snd_soc_dapm_free(socdev); | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1741 |  | 
 | 1742 | 	return 0; | 
 | 1743 | } | 
 | 1744 |  | 
 | 1745 | struct snd_soc_codec_device soc_codec_dev_wm8903 = { | 
 | 1746 | 	.probe = 	wm8903_probe, | 
 | 1747 | 	.remove = 	wm8903_remove, | 
 | 1748 | 	.suspend = 	wm8903_suspend, | 
 | 1749 | 	.resume =	wm8903_resume, | 
 | 1750 | }; | 
 | 1751 | EXPORT_SYMBOL_GPL(soc_codec_dev_wm8903); | 
 | 1752 |  | 
| Takashi Iwai | c9b3a40 | 2008-12-10 07:47:22 +0100 | [diff] [blame] | 1753 | static int __init wm8903_modinit(void) | 
| Mark Brown | 64089b8 | 2008-12-08 19:17:58 +0000 | [diff] [blame] | 1754 | { | 
| Mark Brown | d58d5d5 | 2008-12-10 18:36:42 +0000 | [diff] [blame] | 1755 | 	return i2c_add_driver(&wm8903_i2c_driver); | 
| Mark Brown | 64089b8 | 2008-12-08 19:17:58 +0000 | [diff] [blame] | 1756 | } | 
 | 1757 | module_init(wm8903_modinit); | 
 | 1758 |  | 
 | 1759 | static void __exit wm8903_exit(void) | 
 | 1760 | { | 
| Mark Brown | d58d5d5 | 2008-12-10 18:36:42 +0000 | [diff] [blame] | 1761 | 	i2c_del_driver(&wm8903_i2c_driver); | 
| Mark Brown | 64089b8 | 2008-12-08 19:17:58 +0000 | [diff] [blame] | 1762 | } | 
 | 1763 | module_exit(wm8903_exit); | 
 | 1764 |  | 
| Mark Brown | f1c0a02 | 2008-08-26 13:05:27 +0100 | [diff] [blame] | 1765 | MODULE_DESCRIPTION("ASoC WM8903 driver"); | 
 | 1766 | MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.cm>"); | 
 | 1767 | MODULE_LICENSE("GPL"); |