aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ts_fsm.c
blob: af575b61526b97749c31bb65221af72eb457353c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
/*
 * lib/ts_fsm.c	   A naive finite state machine text search approach
 *
 *		This program is free software; you can redistribute it and/or
 *		modify it under the terms of the GNU General Public License
 *		as published by the Free Software Foundation; either version
 *		2 of the License, or (at your option) any later version.
 *
 * Authors:	Thomas Graf <tgraf@suug.ch>
 *
 * ==========================================================================
 *
 *   A finite state machine consists of n states (struct ts_fsm_token)
 *   representing the pattern as a finite automation. The data is read
 *   sequentially on an octet basis. Every state token specifies the number
 *   of recurrences and the type of value accepted which can be either a
 *   specific character or ctype based set of characters. The available
 *   type of recurrences include 1, (0|1), [0 n], and [1 n].
 *
 *   The algorithm differs between strict/non-strict mode specifying
 *   whether the pattern has to start at the first octet. Strict mode
 *   is enabled by default and can be disabled by inserting
 *   TS_FSM_HEAD_IGNORE as the first token in the chain.
 *
 *   The runtime performance of the algorithm should be around O(n),
 *   however while in strict mode the average runtime can be better.
 */

#include <linux/module.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/textsearch.h>
#include <linux/textsearch_fsm.h>

struct ts_fsm
{
	unsigned int		ntokens;
	struct ts_fsm_token	tokens[0];
};

/* other values derived from ctype.h */
#define _A		0x100 /* ascii */
#define _W		0x200 /* wildcard */

/* Map to _ctype flags and some magic numbers */
static const u16 token_map[TS_FSM_TYPE_MAX+1] = {
	[TS_FSM_SPECIFIC] = 0,
	[TS_FSM_WILDCARD] = _W,
	[TS_FSM_CNTRL]	  = _C,
	[TS_FSM_LOWER]	  = _L,
	[TS_FSM_UPPER]	  = _U,
	[TS_FSM_PUNCT]	  = _P,
	[TS_FSM_SPACE]	  = _S,
	[TS_FSM_DIGIT]	  = _D,
	[TS_FSM_XDIGIT]	  = _D | _X,
	[TS_FSM_ALPHA]	  = _U | _L,
	[TS_FSM_ALNUM]	  = _U | _L | _D,
	[TS_FSM_PRINT]	  = _P | _U | _L | _D | _SP,
	[TS_FSM_GRAPH]	  = _P | _U | _L | _D,
	[TS_FSM_ASCII]	  = _A,
};

static const u16 token_lookup_tbl[256] = {
_W|_A|_C,      _W|_A|_C,     _W|_A|_C,     _W|_A|_C,		/*   0-  3 */
_W|_A|_C,      _W|_A|_C,     _W|_A|_C,     _W|_A|_C,		/*   4-  7 */
_W|_A|_C,      _W|_A|_C|_S,  _W|_A|_C|_S,  _W|_A|_C|_S,		/*   8- 11 */
_W|_A|_C|_S,   _W|_A|_C|_S,  _W|_A|_C,     _W|_A|_C,		/*  12- 15 */
_W|_A|_C,      _W|_A|_C,     _W|_A|_C,     _W|_A|_C,		/*  16- 19 */
_W|_A|_C,      _W|_A|_C,     _W|_A|_C,     _W|_A|_C,		/*  20- 23 */
_W|_A|_C,      _W|_A|_C,     _W|_A|_C,     _W|_A|_C,		/*  24- 27 */
_W|_A|_C,      _W|_A|_C,     _W|_A|_C,     _W|_A|_C,		/*  28- 31 */
_W|_A|_S|_SP,  _W|_A|_P,     _W|_A|_P,     _W|_A|_P,		/*  32- 35 */
_W|_A|_P,      _W|_A|_P,     _W|_A|_P,     _W|_A|_P,		/*  36- 39 */
_W|_A|_P,      _W|_A|_P,     _W|_A|_P,     _W|_A|_P,		/*  40- 43 */
_W|_A|_P,      _W|_A|_P,     _W|_A|_P,     _W|_A|_P,		/*  44- 47 */
_W|_A|_D,      _W|_A|_D,     _W|_A|_D,     _W|_A|_D,		/*  48- 51 */
_W|_A|_D,      _W|_A|_D,     _W|_A|_D,     _W|_A|_D,		/*  52- 55 */
_W|_A|_D,      _W|_A|_D,     _W|_A|_P,     _W|_A|_P,		/*  56- 59 */
_W|_A|_P,      _W|_A|_P,     _W|_A|_P,     _W|_A|_P,		/*  60- 63 */
_W|_A|_P,      _W|_A|_U|_X,  _W|_A|_U|_X,  _W|_A|_U|_X,		/*  64- 67 */
_W|_A|_U|_X,   _W|_A|_U|_X,  _W|_A|_U|_X,  _W|_A|_U,		/*  68- 71 */
_W|_A|_U,      _W|_A|_U,     _W|_A|_U,     _W|_A|_U,		/*  72- 75 */
_W|_A|_U,      _W|_A|_U,     _W|_A|_U,     _W|_A|_U,		/*  76- 79 */
_W|_A|_U,      _W|_A|_U,     _W|_A|_U,     _W|_A|_U,		/*  80- 83 */
_W|_A|_U,      _W|_A|_U,     _W|_A|_U,     _W|_A|_U,		/*  84- 87 */
_W|_A|_U,      _W|_A|_U,     _W|_A|_U,     _W|_A|_P,		/*  88- 91 */
_W|_A|_P,      _W|_A|_P,     _W|_A|_P,     _W|_A|_P,		/*  92- 95 */
_W|_A|_P,      _W|_A|_L|_X,  _W|_A|_L|_X,  _W|_A|_L|_X,		/*  96- 99 */
_W|_A|_L|_X,   _W|_A|_L|_X,  _W|_A|_L|_X,  _W|_A|_L,		/* 100-103 */
_W|_A|_L,      _W|_A|_L,     _W|_A|_L,     _W|_A|_L,		/* 104-107 */
_W|_A|_L,      _W|_A|_L,     _W|_A|_L,     _W|_A|_L,		/* 108-111 */
_W|_A|_L,      _W|_A|_L,     _W|_A|_L,     _W|_A|_L,		/* 112-115 */
_W|_A|_L,      _W|_A|_L,     _W|_A|_L,     _W|_A|_L,		/* 116-119 */
_W|_A|_L,      _W|_A|_L,     _W|_A|_L,     _W|_A|_P,		/* 120-123 */
_W|_A|_P,      _W|_A|_P,     _W|_A|_P,     _W|_A|_C,		/* 124-127 */
_W,            _W,           _W,           _W,			/* 128-131 */
_W,            _W,           _W,           _W,			/* 132-135 */
_W,            _W,           _W,           _W,			/* 136-139 */
_W,            _W,           _W,           _W,			/* 140-143 */
_W,            _W,           _W,           _W,			/* 144-147 */
_W,            _W,           _W,           _W,			/* 148-151 */
_W,            _W,           _W,           _W,			/* 152-155 */
_W,            _W,           _W,           _W,			/* 156-159 */
_W|_S|_SP,     _W|_P,        _W|_P,        _W|_P,		/* 160-163 */
_W|_P,         _W|_P,        _W|_P,        _W|_P,		/* 164-167 */
_W|_P,         _W|_P,        _W|_P,        _W|_P,		/* 168-171 */
_W|_P,         _W|_P,        _W|_P,        _W|_P,		/* 172-175 */
_W|_P,         _W|_P,        _W|_P,        _W|_P,		/* 176-179 */
_W|_P,         _W|_P,        _W|_P,        _W|_P,		/* 180-183 */
_W|_P,         _W|_P,        _W|_P,        _W|_P,		/* 184-187 */
_W|_P,         _W|_P,        _W|_P,        _W|_P,		/* 188-191 */
_W|_U,         _W|_U,        _W|_U,        _W|_U,		/* 192-195 */
_W|_U,         _W|_U,        _W|_U,        _W|_U,		/* 196-199 */
_W|_U,         _W|_U,        _W|_U,        _W|_U,		/* 200-203 */
_W|_U,         _W|_U,        _W|_U,        _W|_U,		/* 204-207 */
_W|_U,         _W|_U,        _W|_U,        _W|_U,		/* 208-211 */
_W|_U,         _W|_U,        _W|_U,        _W|_P,		/* 212-215 */
_W|_U,         _W|_U,        _W|_U,        _W|_U,		/* 216-219 */
_W|_U,         _W|_U,        _W|_U,        _W|_L,		/* 220-223 */
_W|_L,         _W|_L,        _W|_L,        _W|_L,		/* 224-227 */
_W|_L,         _W|_L,        _W|_L,        _W|_L,		/* 228-231 */
_W|_L,         _W|_L,        _W|_L,        _W|_L,		/* 232-235 */
_W|_L,         _W|_L,        _W|_L,        _W|_L,		/* 236-239 */
_W|_L,         _W|_L,        _W|_L,        _W|_L,		/* 240-243 */
_W|_L,         _W|_L,        _W|_L,        _W|_P,		/* 244-247 */
_W|_L,         _W|_L,        _W|_L,        _W|_L,		/* 248-251 */
_W|_L,         _W|_L,        _W|_L,        _W|_L};		/* 252-255 */

static inline int match_token(struct ts_fsm_token *t, u8 d)
{
	if (t->type)
		return (token_lookup_tbl[d] & t->type) != 0;
	else
		return t->value == d;
}

static unsigned int fsm_find(struct ts_config *conf, struct ts_state *state)
{
	struct ts_fsm *fsm = ts_config_priv(conf);
	struct ts_fsm_token *cur = NULL, *next;
	unsigned int match_start, block_idx = 0, tok_idx;
	unsigned block_len = 0, strict, consumed = state->offset;
	const u8 *data;

#define GET_NEXT_BLOCK()		\
({	consumed += block_idx;		\
	block_idx = 0;			\
	block_len = conf->get_next_block(consumed, &data, conf, state); })

#define TOKEN_MISMATCH()		\
	do {				\
		if (strict)		\
			goto no_match;	\
		block_idx++;		\
		goto startover;		\
	} while(0)

#define end_of_data() unlikely(block_idx >= block_len && !GET_NEXT_BLOCK())

	if (end_of_data())
		goto no_match;

	strict = fsm->tokens[0].recur != TS_FSM_HEAD_IGNORE;

startover:
	match_start = consumed + block_idx;

	for (tok_idx = 0; tok_idx < fsm->ntokens; tok_idx++) {
		cur = &fsm->tokens[tok_idx];

		if (likely(tok_idx < (fsm->ntokens - 1)))
			next = &fsm->tokens[tok_idx + 1];
		else
			next = NULL;

		switch (cur->recur) {
		case TS_FSM_SINGLE:
			if (end_of_data())
				goto no_match;

			if (!match_token(cur, data[block_idx]))
				TOKEN_MISMATCH();
			break;

		case TS_FSM_PERHAPS:
			if (end_of_data() ||
			    !match_token(cur, data[block_idx]))
				continue;
			break;

		case TS_FSM_MULTI:
			if (end_of_data())
				goto no_match;

			if (!match_token(cur, data[block_idx]))
				TOKEN_MISMATCH();

			block_idx++;
			/* fall through */

		case TS_FSM_ANY:
			if (next == NULL)
				goto found_match;

			if (end_of_data())
				continue;

			while (!match_token(next, data[block_idx])) {
				if (!match_token(cur, data[block_idx]))
					TOKEN_MISMATCH();
				block_idx++;
				if (end_of_data())
					goto no_match;
			}
			continue;

		/*
		 * Optimization: Prefer small local loop over jumping
		 * back and forth until garbage at head is munched.
		 */
		case TS_FSM_HEAD_IGNORE:
			if (end_of_data())
				continue;

			while (!match_token(next, data[block_idx])) {
				/*
				 * Special case, don't start over upon
				 * a mismatch, give the user the
				 * chance to specify the type of data
				 * allowed to be ignored.
				 */
				if (!match_token(cur, data[block_idx]))
					goto no_match;

				block_idx++;
				if (end_of_data())
					goto no_match;
			}

			match_start = consumed + block_idx;
			continue;
		}

		block_idx++;
	}

	if (end_of_data())
		goto found_match;

no_match:
	return UINT_MAX;

found_match:
	state->offset = consumed + block_idx;
	return match_start;
}

static struct ts_config *fsm_init(const void *pattern, unsigned int len,
				     gfp_t gfp_mask)
{
	int i, err = -EINVAL;
	struct ts_config *conf;
	struct ts_fsm *fsm;
	struct ts_fsm_token *tokens = (struct ts_fsm_token *) pattern;
	unsigned int ntokens = len / sizeof(*tokens);
	size_t priv_size = sizeof(*fsm) + len;

	if (len  % sizeof(struct ts_fsm_token) || ntokens < 1)
		goto errout;

	for (i = 0; i < ntokens; i++) {
		struct ts_fsm_token *t = &tokens[i];

		if (t->type > TS_FSM_TYPE_MAX || t->recur > TS_FSM_RECUR_MAX)
			goto errout;

		if (t->recur == TS_FSM_HEAD_IGNORE &&
		    (i != 0 || i == (ntokens - 1)))
			goto errout;
	}

	conf = alloc_ts_config(priv_size, gfp_mask);
	if (IS_ERR(conf))
		return conf;

	fsm = ts_config_priv(conf);
	fsm->ntokens = ntokens;
	memcpy(fsm->tokens, pattern, len);

	for (i = 0; i < fsm->ntokens; i++) {
		struct ts_fsm_token *t = &fsm->tokens[i];
		t->type = token_map[t->type];
	}

	return conf;

errout:
	return ERR_PTR(err);
}

static void *fsm_get_pattern(struct ts_config *conf)
{
	struct ts_fsm *fsm = ts_config_priv(conf);
	return fsm->tokens;
}

static unsigned int fsm_get_pattern_len(struct ts_config *conf)
{
	struct ts_fsm *fsm = ts_config_priv(conf);
	return fsm->ntokens * sizeof(struct ts_fsm_token);
}

static struct ts_ops fsm_ops = {
	.name		  = "fsm",
	.find		  = fsm_find,
	.init		  = fsm_init,
	.get_pattern	  = fsm_get_pattern,
	.get_pattern_len  = fsm_get_pattern_len,
	.owner		  = THIS_MODULE,
	.list		  = LIST_HEAD_INIT(fsm_ops.list)
};

static int __init init_fsm(void)
{
	return textsearch_register(&fsm_ops);
}

static void __exit exit_fsm(void)
{
	textsearch_unregister(&fsm_ops);
}

MODULE_LICENSE("GPL");

module_init(init_fsm);
module_exit(exit_fsm);
696' href='#n1696'>1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118
/*
 * wm8996.c - WM8996 audio codec interface
 *
 * Copyright 2011-2 Wolfson Microelectronics PLC.
 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
 *
 *  This program is free software; you can redistribute  it and/or modify it
 *  under  the terms of  the GNU General  Public License as published by the
 *  Free Software Foundation;  either version 2 of the  License, or (at your
 *  option) any later version.
 */

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

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

#define WM8996_AIFS 2

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

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

struct wm8996_priv {
	struct device *dev;
	struct regmap *regmap;
	struct snd_soc_codec *codec;

	int ldo1ena;

	int sysclk;
	int sysclk_src;

	int fll_src;
	int fll_fref;
	int fll_fout;

	struct completion fll_lock;

	u16 dcs_pending;
	struct completion dcs_done;

	u16 hpout_ena;
	u16 hpout_pending;

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

	struct wm8996_pdata pdata;

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

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

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

#ifdef CONFIG_GPIOLIB
	struct gpio_chip gpio_chip;
#endif
};

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

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

static struct reg_default wm8996_reg[] = {
	{ WM8996_POWER_MANAGEMENT_1, 0x0 },
	{ WM8996_POWER_MANAGEMENT_2, 0x0 },
	{ WM8996_POWER_MANAGEMENT_3, 0x0 },
	{ WM8996_POWER_MANAGEMENT_4, 0x0 },
	{ WM8996_POWER_MANAGEMENT_5, 0x0 },
	{ WM8996_POWER_MANAGEMENT_6, 0x0 },
	{ WM8996_POWER_MANAGEMENT_7, 0x10 },
	{ WM8996_POWER_MANAGEMENT_8, 0x0 },
	{ WM8996_LEFT_LINE_INPUT_VOLUME, 0x0 },
	{ WM8996_RIGHT_LINE_INPUT_VOLUME, 0x0 },
	{ WM8996_LINE_INPUT_CONTROL, 0x0 },
	{ WM8996_DAC1_HPOUT1_VOLUME, 0x88 },
	{ WM8996_DAC2_HPOUT2_VOLUME, 0x88 },
	{ WM8996_DAC1_LEFT_VOLUME, 0x2c0 },
	{ WM8996_DAC1_RIGHT_VOLUME, 0x2c0 },
	{ WM8996_DAC2_LEFT_VOLUME, 0x2c0 },
	{ WM8996_DAC2_RIGHT_VOLUME, 0x2c0 },
	{ WM8996_OUTPUT1_LEFT_VOLUME, 0x80 },
	{ WM8996_OUTPUT1_RIGHT_VOLUME, 0x80 },
	{ WM8996_OUTPUT2_LEFT_VOLUME, 0x80 },
	{ WM8996_OUTPUT2_RIGHT_VOLUME, 0x80 },
	{ WM8996_MICBIAS_1, 0x39 },
	{ WM8996_MICBIAS_2, 0x39 },
	{ WM8996_LDO_1, 0x3 },
	{ WM8996_LDO_2, 0x13 },
	{ WM8996_ACCESSORY_DETECT_MODE_1, 0x4 },
	{ WM8996_ACCESSORY_DETECT_MODE_2, 0x0 },
	{ WM8996_HEADPHONE_DETECT_1, 0x20 },
	{ WM8996_HEADPHONE_DETECT_2, 0x0 },
	{ WM8996_MIC_DETECT_1, 0x7600 },
	{ WM8996_MIC_DETECT_2, 0xbf },
	{ WM8996_CHARGE_PUMP_1, 0x1f25 },
	{ WM8996_CHARGE_PUMP_2, 0xab19 },
	{ WM8996_DC_SERVO_1, 0x0 },
	{ WM8996_DC_SERVO_3, 0x0 },
	{ WM8996_DC_SERVO_5, 0x2a2a },
	{ WM8996_DC_SERVO_6, 0x0 },
	{ WM8996_DC_SERVO_7, 0x0 },
	{ WM8996_ANALOGUE_HP_1, 0x0 },
	{ WM8996_ANALOGUE_HP_2, 0x0 },
	{ WM8996_CONTROL_INTERFACE_1, 0x8004 },
	{ WM8996_WRITE_SEQUENCER_CTRL_1, 0x0 },
	{ WM8996_WRITE_SEQUENCER_CTRL_2, 0x0 },
	{ WM8996_AIF_CLOCKING_1, 0x0 },
	{ WM8996_AIF_CLOCKING_2, 0x0 },
	{ WM8996_CLOCKING_1, 0x10 },
	{ WM8996_CLOCKING_2, 0x0 },
	{ WM8996_AIF_RATE, 0x83 },
	{ WM8996_FLL_CONTROL_1, 0x0 },
	{ WM8996_FLL_CONTROL_2, 0x0 },
	{ WM8996_FLL_CONTROL_3, 0x0 },
	{ WM8996_FLL_CONTROL_4, 0x5dc0 },
	{ WM8996_FLL_CONTROL_5, 0xc84 },
	{ WM8996_FLL_EFS_1, 0x0 },
	{ WM8996_FLL_EFS_2, 0x2 },
	{ WM8996_AIF1_CONTROL, 0x0 },
	{ WM8996_AIF1_BCLK, 0x0 },
	{ WM8996_AIF1_TX_LRCLK_1, 0x80 },
	{ WM8996_AIF1_TX_LRCLK_2, 0x8 },
	{ WM8996_AIF1_RX_LRCLK_1, 0x80 },
	{ WM8996_AIF1_RX_LRCLK_2, 0x0 },
	{ WM8996_AIF1TX_DATA_CONFIGURATION_1, 0x1818 },
	{ WM8996_AIF1TX_DATA_CONFIGURATION_2, 0 },
	{ WM8996_AIF1RX_DATA_CONFIGURATION, 0x1818 },
	{ WM8996_AIF1TX_CHANNEL_0_CONFIGURATION, 0x0 },
	{ WM8996_AIF1TX_CHANNEL_1_CONFIGURATION, 0x0 },
	{ WM8996_AIF1TX_CHANNEL_2_CONFIGURATION, 0x0 },
	{ WM8996_AIF1TX_CHANNEL_3_CONFIGURATION, 0x0 },
	{ WM8996_AIF1TX_CHANNEL_4_CONFIGURATION, 0x0 },
	{ WM8996_AIF1TX_CHANNEL_5_CONFIGURATION, 0x0 },
	{ WM8996_AIF1RX_CHANNEL_0_CONFIGURATION, 0x0 },
	{ WM8996_AIF1RX_CHANNEL_1_CONFIGURATION, 0x0 },
	{ WM8996_AIF1RX_CHANNEL_2_CONFIGURATION, 0x0 },
	{ WM8996_AIF1RX_CHANNEL_3_CONFIGURATION, 0x0 },
	{ WM8996_AIF1RX_CHANNEL_4_CONFIGURATION, 0x0 },
	{ WM8996_AIF1RX_CHANNEL_5_CONFIGURATION, 0x0 },
	{ WM8996_AIF1RX_MONO_CONFIGURATION, 0x0 },
	{ WM8996_AIF1TX_TEST, 0x7 },
	{ WM8996_AIF2_CONTROL, 0x0 },
	{ WM8996_AIF2_BCLK, 0x0 },
	{ WM8996_AIF2_TX_LRCLK_1, 0x80 },
	{ WM8996_AIF2_TX_LRCLK_2, 0x8 },
	{ WM8996_AIF2_RX_LRCLK_1, 0x80 },
	{ WM8996_AIF2_RX_LRCLK_2, 0x0 },
	{ WM8996_AIF2TX_DATA_CONFIGURATION_1, 0x1818 },
	{ WM8996_AIF2RX_DATA_CONFIGURATION, 0x1818 },
	{ WM8996_AIF2RX_DATA_CONFIGURATION, 0x0 },
	{ WM8996_AIF2TX_CHANNEL_0_CONFIGURATION, 0x0 },
	{ WM8996_AIF2TX_CHANNEL_1_CONFIGURATION, 0x0 },
	{ WM8996_AIF2RX_CHANNEL_0_CONFIGURATION, 0x0 },
	{ WM8996_AIF2RX_CHANNEL_1_CONFIGURATION, 0x0 },
	{ WM8996_AIF2RX_MONO_CONFIGURATION, 0x0 },
	{ WM8996_AIF2TX_TEST, 0x1 },
	{ WM8996_DSP1_TX_LEFT_VOLUME, 0xc0 },
	{ WM8996_DSP1_TX_RIGHT_VOLUME, 0xc0 },
	{ WM8996_DSP1_RX_LEFT_VOLUME, 0xc0 },
	{ WM8996_DSP1_RX_RIGHT_VOLUME, 0xc0 },
	{ WM8996_DSP1_TX_FILTERS, 0x2000 },
	{ WM8996_DSP1_RX_FILTERS_1, 0x200 },
	{ WM8996_DSP1_RX_FILTERS_2, 0x10 },
	{ WM8996_DSP1_DRC_1, 0x98 },
	{ WM8996_DSP1_DRC_2, 0x845 },
	{ WM8996_DSP1_RX_EQ_GAINS_1, 0x6318 },
	{ WM8996_DSP1_RX_EQ_GAINS_2, 0x6300 },
	{ WM8996_DSP1_RX_EQ_BAND_1_A, 0xfca },
	{ WM8996_DSP1_RX_EQ_BAND_1_B, 0x400 },
	{ WM8996_DSP1_RX_EQ_BAND_1_PG, 0xd8 },
	{ WM8996_DSP1_RX_EQ_BAND_2_A, 0x1eb5 },
	{ WM8996_DSP1_RX_EQ_BAND_2_B, 0xf145 },
	{ WM8996_DSP1_RX_EQ_BAND_2_C, 0xb75 },
	{ WM8996_DSP1_RX_EQ_BAND_2_PG, 0x1c5 },
	{ WM8996_DSP1_RX_EQ_BAND_3_A, 0x1c58 },
	{ WM8996_DSP1_RX_EQ_BAND_3_B, 0xf373 },
	{ WM8996_DSP1_RX_EQ_BAND_3_C, 0xa54 },
	{ WM8996_DSP1_RX_EQ_BAND_3_PG, 0x558 },
	{ WM8996_DSP1_RX_EQ_BAND_4_A, 0x168e },
	{ WM8996_DSP1_RX_EQ_BAND_4_B, 0xf829 },
	{ WM8996_DSP1_RX_EQ_BAND_4_C, 0x7ad },
	{ WM8996_DSP1_RX_EQ_BAND_4_PG, 0x1103 },
	{ WM8996_DSP1_RX_EQ_BAND_5_A, 0x564 },
	{ WM8996_DSP1_RX_EQ_BAND_5_B, 0x559 },
	{ WM8996_DSP1_RX_EQ_BAND_5_PG, 0x4000 },
	{ WM8996_DSP2_TX_LEFT_VOLUME, 0xc0 },
	{ WM8996_DSP2_TX_RIGHT_VOLUME, 0xc0 },
	{ WM8996_DSP2_RX_LEFT_VOLUME, 0xc0 },
	{ WM8996_DSP2_RX_RIGHT_VOLUME, 0xc0 },
	{ WM8996_DSP2_TX_FILTERS, 0x2000 },
	{ WM8996_DSP2_RX_FILTERS_1, 0x200 },
	{ WM8996_DSP2_RX_FILTERS_2, 0x10 },
	{ WM8996_DSP2_DRC_1, 0x98 },
	{ WM8996_DSP2_DRC_2, 0x845 },
	{ WM8996_DSP2_RX_EQ_GAINS_1, 0x6318 },
	{ WM8996_DSP2_RX_EQ_GAINS_2, 0x6300 },
	{ WM8996_DSP2_RX_EQ_BAND_1_A, 0xfca },
	{ WM8996_DSP2_RX_EQ_BAND_1_B, 0x400 },
	{ WM8996_DSP2_RX_EQ_BAND_1_PG, 0xd8 },
	{ WM8996_DSP2_RX_EQ_BAND_2_A, 0x1eb5 },
	{ WM8996_DSP2_RX_EQ_BAND_2_B, 0xf145 },
	{ WM8996_DSP2_RX_EQ_BAND_2_C, 0xb75 },
	{ WM8996_DSP2_RX_EQ_BAND_2_PG, 0x1c5 },
	{ WM8996_DSP2_RX_EQ_BAND_3_A, 0x1c58 },
	{ WM8996_DSP2_RX_EQ_BAND_3_B, 0xf373 },
	{ WM8996_DSP2_RX_EQ_BAND_3_C, 0xa54 },
	{ WM8996_DSP2_RX_EQ_BAND_3_PG, 0x558 },
	{ WM8996_DSP2_RX_EQ_BAND_4_A, 0x168e },
	{ WM8996_DSP2_RX_EQ_BAND_4_B, 0xf829 },
	{ WM8996_DSP2_RX_EQ_BAND_4_C, 0x7ad },
	{ WM8996_DSP2_RX_EQ_BAND_4_PG, 0x1103 },
	{ WM8996_DSP2_RX_EQ_BAND_5_A, 0x564 },
	{ WM8996_DSP2_RX_EQ_BAND_5_B, 0x559 },
	{ WM8996_DSP2_RX_EQ_BAND_5_PG, 0x4000 },
	{ WM8996_DAC1_MIXER_VOLUMES, 0x0 },
	{ WM8996_DAC1_LEFT_MIXER_ROUTING, 0x0 },
	{ WM8996_DAC1_RIGHT_MIXER_ROUTING, 0x0 },
	{ WM8996_DAC2_MIXER_VOLUMES, 0x0 },
	{ WM8996_DAC2_LEFT_MIXER_ROUTING, 0x0 },
	{ WM8996_DAC2_RIGHT_MIXER_ROUTING, 0x0 },
	{ WM8996_DSP1_TX_LEFT_MIXER_ROUTING, 0x0 },
	{ WM8996_DSP1_TX_RIGHT_MIXER_ROUTING, 0x0 },
	{ WM8996_DSP2_TX_LEFT_MIXER_ROUTING, 0x0 },
	{ WM8996_DSP2_TX_RIGHT_MIXER_ROUTING, 0x0 },
	{ WM8996_DSP_TX_MIXER_SELECT, 0x0 },
	{ WM8996_DAC_SOFTMUTE, 0x0 },
	{ WM8996_OVERSAMPLING, 0xd },
	{ WM8996_SIDETONE, 0x1040 },
	{ WM8996_GPIO_1, 0xa101 },
	{ WM8996_GPIO_2, 0xa101 },
	{ WM8996_GPIO_3, 0xa101 },
	{ WM8996_GPIO_4, 0xa101 },
	{ WM8996_GPIO_5, 0xa101 },
	{ WM8996_PULL_CONTROL_1, 0x0 },
	{ WM8996_PULL_CONTROL_2, 0x140 },
	{ WM8996_INTERRUPT_STATUS_1_MASK, 0x1f },
	{ WM8996_INTERRUPT_STATUS_2_MASK, 0x1ecf },
	{ WM8996_LEFT_PDM_SPEAKER, 0x0 },
	{ WM8996_RIGHT_PDM_SPEAKER, 0x1 },
	{ WM8996_PDM_SPEAKER_MUTE_SEQUENCE, 0x69 },
	{ WM8996_PDM_SPEAKER_VOLUME, 0x66 },
};

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

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

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

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

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

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

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

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

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

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

	if (!wm8996->num_retune_mobile_texts)
		return;

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

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

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

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

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

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

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

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

	if (block < 0)
		return block;

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

	wm8996->retune_mobile_cfg[block] = value;

	wm8996_set_retune_mobile(codec, block);

	return 0;
}

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

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

	return 0;
}

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

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

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

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

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

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

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

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

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

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

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

SOC_SINGLE("DSP1 3D Stereo Switch", WM8996_DSP1_RX_FILTERS_2, 8, 1, 0),
SOC_SINGLE("DSP2 3D Stereo Switch", WM8996_DSP2_RX_FILTERS_2, 8, 1, 0),

SOC_SINGLE_TLV("DSP1 3D Stereo Volume", WM8996_DSP1_RX_FILTERS_2, 10, 15,
		0, threedstereo_tlv),
SOC_SINGLE_TLV("DSP2 3D Stereo Volume", WM8996_DSP2_RX_FILTERS_2, 10, 15,
		0, threedstereo_tlv),

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

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

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

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

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

SOC_SINGLE("DSP1 DRC TXL Switch", WM8996_DSP1_DRC_1, 0, 1, 0),
SOC_SINGLE("DSP1 DRC TXR Switch", WM8996_DSP1_DRC_1, 1, 1, 0),
SOC_SINGLE("DSP1 DRC RX Switch", WM8996_DSP1_DRC_1, 2, 1, 0),
SND_SOC_BYTES_MASK("DSP1 DRC", WM8996_DSP1_DRC_1, 5,
		   WM8996_DSP1RX_DRC_ENA | WM8996_DSP1TXL_DRC_ENA |
		   WM8996_DSP1TXR_DRC_ENA),

SOC_SINGLE("DSP2 DRC TXL Switch", WM8996_DSP2_DRC_1, 0, 1, 0),
SOC_SINGLE("DSP2 DRC TXR Switch", WM8996_DSP2_DRC_1, 1, 1, 0),
SOC_SINGLE("DSP2 DRC RX Switch", WM8996_DSP2_DRC_1, 2, 1, 0),
SND_SOC_BYTES_MASK("DSP2 DRC", WM8996_DSP2_DRC_1, 5,
		   WM8996_DSP2RX_DRC_ENA | WM8996_DSP2TXL_DRC_ENA |
		   WM8996_DSP2TXR_DRC_ENA),
};

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

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

static void wm8996_bg_enable(struct snd_soc_codec *codec)
{
	struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);

	wm8996->bg_ena++;
	if (wm8996->bg_ena == 1) {
		snd_soc_update_bits(codec, WM8996_POWER_MANAGEMENT_1,
				    WM8996_BG_ENA, WM8996_BG_ENA);
		msleep(2);
	}
}

static void wm8996_bg_disable(struct snd_soc_codec *codec)
{
	struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);

	wm8996->bg_ena--;
	if (!wm8996->bg_ena)
		snd_soc_update_bits(codec, WM8996_POWER_MANAGEMENT_1,
				    WM8996_BG_ENA, 0);
}

static int bg_event(struct snd_soc_dapm_widget *w,
		    struct snd_kcontrol *kcontrol, int event)
{
	struct snd_soc_codec *codec = w->codec;
	int ret = 0;

	switch (event) {
	case SND_SOC_DAPM_PRE_PMU:
		wm8996_bg_enable(codec);
		break;
	case SND_SOC_DAPM_POST_PMD:
		wm8996_bg_disable(codec);
		break;
	default:
		BUG();
		ret = -EINVAL;
	}

	return ret;
}

static int cp_event(struct snd_soc_dapm_widget *w,
		    struct snd_kcontrol *kcontrol, int event)
{
	int ret = 0;

	switch (event) {
	case SND_SOC_DAPM_POST_PMU:
		msleep(5);
		break;
	default:
		BUG();
		ret = -EINVAL;
	}

	return 0;
}

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

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

	return 0;
}

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

	snd_soc_write(codec, WM8996_DC_SERVO_2, mask);

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

		} else {
			msleep(1);
			timeout--;
		}

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

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

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

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

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

		wm8996->dcs_pending = 0;
	}

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

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

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

		snd_soc_update_bits(codec, WM8996_ANALOGUE_HP_1, mask, val);

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

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

		snd_soc_update_bits(codec, WM8996_ANALOGUE_HP_2, mask, val);

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

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

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

	return 0;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20, 0),
SND_SOC_DAPM_SUPPLY_S("SYSCLK", 1, WM8996_AIF_CLOCKING_1, 0, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("SYSDSPCLK", 2, WM8996_CLOCKING_1, 1, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("AIFCLK", 2, WM8996_CLOCKING_1, 2, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("Charge Pump", 2, WM8996_CHARGE_PUMP_1, 15, 0, cp_event,
		      SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_SUPPLY("Bandgap", SND_SOC_NOPM, 0, 0, bg_event,
		    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("LDO2", WM8996_POWER_MANAGEMENT_2, 1, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("MICB1 Audio", WM8996_MICBIAS_1, 4, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("MICB2 Audio", WM8996_MICBIAS_2, 4, 1, NULL, 0),
SND_SOC_DAPM_MICBIAS("MICB2", WM8996_POWER_MANAGEMENT_1, 9, 0),
SND_SOC_DAPM_MICBIAS("MICB1", WM8996_POWER_MANAGEMENT_1, 8, 0),

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

SND_SOC_DAPM_MUX("IN1L Mux", WM8996_POWER_MANAGEMENT_7, 2, 0, &in1_mux),
SND_SOC_DAPM_MUX("IN1R Mux", WM8996_POWER_MANAGEMENT_7, 3, 0, &in1_mux),
SND_SOC_DAPM_MUX("IN2L Mux", WM8996_POWER_MANAGEMENT_7, 6, 0, &in2_mux),
SND_SOC_DAPM_MUX("IN2R Mux", WM8996_POWER_MANAGEMENT_7, 7, 0, &in2_mux),

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

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

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

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

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

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

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

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

SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0, WM8996_POWER_MANAGEMENT_4, 9, 0),
SND_SOC_DAPM_AIF_IN("AIF2RX0", NULL, 1, WM8996_POWER_MANAGEMENT_4, 8, 0),

SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0, WM8996_POWER_MANAGEMENT_6, 9, 0),
SND_SOC_DAPM_AIF_OUT("AIF2TX0", NULL, 1, WM8996_POWER_MANAGEMENT_6, 8, 0),

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

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

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

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

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

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

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

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

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

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

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

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

	{ "AIF1RX0", NULL, "AIF1 Playback" },
	{ "AIF1RX1", NULL, "AIF1 Playback" },
	{ "AIF1RX2", NULL, "AIF1 Playback" },
	{ "AIF1RX3", NULL, "AIF1 Playback" },
	{ "AIF1RX4", NULL, "AIF1 Playback" },
	{ "AIF1RX5", NULL, "AIF1 Playback" },

	{ "AIF2RX0", NULL, "AIF2 Playback" },
	{ "AIF2RX1", NULL, "AIF2 Playback" },

	{ "AIF1 Capture", NULL, "AIF1TX0" },
	{ "AIF1 Capture", NULL, "AIF1TX1" },
	{ "AIF1 Capture", NULL, "AIF1TX2" },
	{ "AIF1 Capture", NULL, "AIF1TX3" },
	{ "AIF1 Capture", NULL, "AIF1TX4" },
	{ "AIF1 Capture", NULL, "AIF1TX5" },

	{ "AIF2 Capture", NULL, "AIF2TX0" },
	{ "AIF2 Capture", NULL, "AIF2TX1" },

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

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

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

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

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

	{ "DMIC2L", NULL, "DMIC2" },
	{ "DMIC2R", NULL, "DMIC2" },
	{ "DMIC1L", NULL, "DMIC1" },
	{ "DMIC1R", NULL, "DMIC1" },

	{ "IN1L Mux", "ADC", "ADCL" },
	{ "IN1L Mux", "DMIC1", "DMIC1L" },
	{ "IN1L Mux", "DMIC2", "DMIC2L" },

	{ "IN1R Mux", "ADC", "ADCR" },
	{ "IN1R Mux", "DMIC1", "DMIC1R" },
	{ "IN1R Mux", "DMIC2", "DMIC2R" },

	{ "IN2L Mux", "ADC", "ADCL" },
	{ "IN2L Mux", "DMIC1", "DMIC1L" },
	{ "IN2L Mux", "DMIC2", "DMIC2L" },

	{ "IN2R Mux", "ADC", "ADCR" },
	{ "IN2R Mux", "DMIC1", "DMIC1R" },
	{ "IN2R Mux", "DMIC2", "DMIC2R" },

	{ "Left Sidetone", "IN1", "IN1L Mux" },
	{ "Left Sidetone", "IN2", "IN2L Mux" },

	{ "Right Sidetone", "IN1", "IN1R Mux" },
	{ "Right Sidetone", "IN2", "IN2R Mux" },

	{ "DSP1TXL", "IN1 Switch", "IN1L Mux" },
	{ "DSP1TXR", "IN1 Switch", "IN1R Mux" },

	{ "DSP2TXL", "IN1 Switch", "IN2L Mux" },
	{ "DSP2TXR", "IN1 Switch", "IN2R Mux" },

	{ "AIF1TX0", NULL, "DSP1TXL" },
	{ "AIF1TX1", NULL, "DSP1TXR" },
	{ "AIF1TX2", NULL, "DSP2TXL" },
	{ "AIF1TX3", NULL, "DSP2TXR" },
	{ "AIF1TX4", NULL, "AIF2RX0" },
	{ "AIF1TX5", NULL, "AIF2RX1" },

	{ "AIF1RX0", NULL, "AIFCLK" },
	{ "AIF1RX1", NULL, "AIFCLK" },
	{ "AIF1RX2", NULL, "AIFCLK" },
	{ "AIF1RX3", NULL, "AIFCLK" },
	{ "AIF1RX4", NULL, "AIFCLK" },
	{ "AIF1RX5", NULL, "AIFCLK" },

	{ "AIF2RX0", NULL, "AIFCLK" },
	{ "AIF2RX1", NULL, "AIFCLK" },

	{ "AIF1TX0", NULL, "AIFCLK" },
	{ "AIF1TX1", NULL, "AIFCLK" },
	{ "AIF1TX2", NULL, "AIFCLK" },
	{ "AIF1TX3", NULL, "AIFCLK" },
	{ "AIF1TX4", NULL, "AIFCLK" },
	{ "AIF1TX5", NULL, "AIFCLK" },

	{ "AIF2TX0", NULL, "AIFCLK" },
	{ "AIF2TX1", NULL, "AIFCLK" },

	{ "DSP1RXL", NULL, "SYSDSPCLK" },
	{ "DSP1RXR", NULL, "SYSDSPCLK" },
	{ "DSP2RXL", NULL, "SYSDSPCLK" },
	{ "DSP2RXR", NULL, "SYSDSPCLK" },
	{ "DSP1TXL", NULL, "SYSDSPCLK" },
	{ "DSP1TXR", NULL, "SYSDSPCLK" },
	{ "DSP2TXL", NULL, "SYSDSPCLK" },
	{ "DSP2TXR", NULL, "SYSDSPCLK" },

	{ "AIF1RXA", NULL, "AIF1RX0" },
	{ "AIF1RXA", NULL, "AIF1RX1" },
	{ "AIF1RXB", NULL, "AIF1RX2" },
	{ "AIF1RXB", NULL, "AIF1RX3" },
	{ "AIF1RXC", NULL, "AIF1RX4" },
	{ "AIF1RXC", NULL, "AIF1RX5" },

	{ "AIF2RX", NULL, "AIF2RX0" },
	{ "AIF2RX", NULL, "AIF2RX1" },

	{ "AIF2TX", "DSP2", "DSP2TX" },
	{ "AIF2TX", "DSP1", "DSP1RX" },
	{ "AIF2TX", "AIF1", "AIF1RXC" },

	{ "DSP1RXL", NULL, "DSP1RX" },
	{ "DSP1RXR", NULL, "DSP1RX" },
	{ "DSP2RXL", NULL, "DSP2RX" },
	{ "DSP2RXR", NULL, "DSP2RX" },

	{ "DSP2TX", NULL, "DSP2TXL" },
	{ "DSP2TX", NULL, "DSP2TXR" },

	{ "DSP1RX", "AIF1", "AIF1RXA" },
	{ "DSP1RX", "AIF2", "AIF2RX" },

	{ "DSP2RX", "AIF1", "AIF1RXB" },
	{ "DSP2RX", "AIF2", "AIF2RX" },

	{ "DAC2L Mixer", "DSP2 Switch", "DSP2RXL" },
	{ "DAC2L Mixer", "DSP1 Switch", "DSP1RXL" },
	{ "DAC2L Mixer", "Right Sidetone Switch", "Right Sidetone" },
	{ "DAC2L Mixer", "Left Sidetone Switch", "Left Sidetone" },

	{ "DAC2R Mixer", "DSP2 Switch", "DSP2RXR" },
	{ "DAC2R Mixer", "DSP1 Switch", "DSP1RXR" },
	{ "DAC2R Mixer", "Right Sidetone Switch", "Right Sidetone" },
	{ "DAC2R Mixer", "Left Sidetone Switch", "Left Sidetone" },

	{ "DAC1L Mixer", "DSP2 Switch", "DSP2RXL" },
	{ "DAC1L Mixer", "DSP1 Switch", "DSP1RXL" },
	{ "DAC1L Mixer", "Right Sidetone Switch", "Right Sidetone" },
	{ "DAC1L Mixer", "Left Sidetone Switch", "Left Sidetone" },

	{ "DAC1R Mixer", "DSP2 Switch", "DSP2RXR" },
	{ "DAC1R Mixer", "DSP1 Switch", "DSP1RXR" },
	{ "DAC1R Mixer", "Right Sidetone Switch", "Right Sidetone" },
	{ "DAC1R Mixer", "Left Sidetone Switch", "Left Sidetone" },

	{ "DAC1L", NULL, "DAC1L Mixer" },
	{ "DAC1R", NULL, "DAC1R Mixer" },
	{ "DAC2L", NULL, "DAC2L Mixer" },
	{ "DAC2R", NULL, "DAC2R Mixer" },

	{ "HPOUT2L PGA", NULL, "Charge Pump" },
	{ "HPOUT2L PGA", NULL, "Bandgap" },
	{ "HPOUT2L PGA", NULL, "DAC2L" },
	{ "HPOUT2L_DLY", NULL, "HPOUT2L PGA" },
	{ "HPOUT2L_DCS", NULL, "HPOUT2L_DLY" },
	{ "HPOUT2L_RMV_SHORT", NULL, "HPOUT2L_DCS" },

	{ "HPOUT2R PGA", NULL, "Charge Pump" },
	{ "HPOUT2R PGA", NULL, "Bandgap" },
	{ "HPOUT2R PGA", NULL, "DAC2R" },
	{ "HPOUT2R_DLY", NULL, "HPOUT2R PGA" },
	{ "HPOUT2R_DCS", NULL, "HPOUT2R_DLY" },
	{ "HPOUT2R_RMV_SHORT", NULL, "HPOUT2R_DCS" },

	{ "HPOUT1L PGA", NULL, "Charge Pump" },
	{ "HPOUT1L PGA", NULL, "Bandgap" },
	{ "HPOUT1L PGA", NULL, "DAC1L" },
	{ "HPOUT1L_DLY", NULL, "HPOUT1L PGA" },
	{ "HPOUT1L_DCS", NULL, "HPOUT1L_DLY" },
	{ "HPOUT1L_RMV_SHORT", NULL, "HPOUT1L_DCS" },

	{ "HPOUT1R PGA", NULL, "Charge Pump" },
	{ "HPOUT1R PGA", NULL, "Bandgap" },
	{ "HPOUT1R PGA", NULL, "DAC1R" },
	{ "HPOUT1R_DLY", NULL, "HPOUT1R PGA" },
	{ "HPOUT1R_DCS", NULL, "HPOUT1R_DLY" },
	{ "HPOUT1R_RMV_SHORT", NULL, "HPOUT1R_DCS" },

	{ "HPOUT2L", NULL, "HPOUT2L_RMV_SHORT" },
	{ "HPOUT2R", NULL, "HPOUT2R_RMV_SHORT" },
	{ "HPOUT1L", NULL, "HPOUT1L_RMV_SHORT" },
	{ "HPOUT1R", NULL, "HPOUT1R_RMV_SHORT" },

	{ "SPKL", "DAC1L", "DAC1L" },
	{ "SPKL", "DAC1R", "DAC1R" },
	{ "SPKL", "DAC2L", "DAC2L" },
	{ "SPKL", "DAC2R", "DAC2R" },

	{ "SPKR", "DAC1L", "DAC1L" },
	{ "SPKR", "DAC1R", "DAC1R" },
	{ "SPKR", "DAC2L", "DAC2L" },
	{ "SPKR", "DAC2R", "DAC2R" },

	{ "SPKL PGA", NULL, "SPKL" },
	{ "SPKR PGA", NULL, "SPKR" },

	{ "SPKDAT", NULL, "SPKL PGA" },
	{ "SPKDAT", NULL, "SPKR PGA" },
};

static bool wm8996_readable_register(struct device *dev, unsigned int reg)
{
	/* Due to the sparseness of the register map the compiler
	 * output from an explicit switch statement ends up being much
	 * more efficient than a table.
	 */
	switch (reg) {
	case WM8996_SOFTWARE_RESET:
	case WM8996_POWER_MANAGEMENT_1:
	case WM8996_POWER_MANAGEMENT_2:
	case WM8996_POWER_MANAGEMENT_3:
	case WM8996_POWER_MANAGEMENT_4:
	case WM8996_POWER_MANAGEMENT_5:
	case WM8996_POWER_MANAGEMENT_6:
	case WM8996_POWER_MANAGEMENT_7:
	case WM8996_POWER_MANAGEMENT_8:
	case WM8996_LEFT_LINE_INPUT_VOLUME:
	case WM8996_RIGHT_LINE_INPUT_VOLUME:
	case WM8996_LINE_INPUT_CONTROL:
	case WM8996_DAC1_HPOUT1_VOLUME:
	case WM8996_DAC2_HPOUT2_VOLUME:
	case WM8996_DAC1_LEFT_VOLUME:
	case WM8996_DAC1_RIGHT_VOLUME:
	case WM8996_DAC2_LEFT_VOLUME:
	case WM8996_DAC2_RIGHT_VOLUME:
	case WM8996_OUTPUT1_LEFT_VOLUME:
	case WM8996_OUTPUT1_RIGHT_VOLUME:
	case WM8996_OUTPUT2_LEFT_VOLUME:
	case WM8996_OUTPUT2_RIGHT_VOLUME:
	case WM8996_MICBIAS_1:
	case WM8996_MICBIAS_2:
	case WM8996_LDO_1:
	case WM8996_LDO_2:
	case WM8996_ACCESSORY_DETECT_MODE_1:
	case WM8996_ACCESSORY_DETECT_MODE_2:
	case WM8996_HEADPHONE_DETECT_1:
	case WM8996_HEADPHONE_DETECT_2:
	case WM8996_MIC_DETECT_1:
	case WM8996_MIC_DETECT_2:
	case WM8996_MIC_DETECT_3:
	case WM8996_CHARGE_PUMP_1:
	case WM8996_CHARGE_PUMP_2:
	case WM8996_DC_SERVO_1:
	case WM8996_DC_SERVO_2:
	case WM8996_DC_SERVO_3:
	case WM8996_DC_SERVO_5:
	case WM8996_DC_SERVO_6:
	case WM8996_DC_SERVO_7:
	case WM8996_DC_SERVO_READBACK_0:
	case WM8996_ANALOGUE_HP_1:
	case WM8996_ANALOGUE_HP_2:
	case WM8996_CHIP_REVISION:
	case WM8996_CONTROL_INTERFACE_1:
	case WM8996_WRITE_SEQUENCER_CTRL_1:
	case WM8996_WRITE_SEQUENCER_CTRL_2:
	case WM8996_AIF_CLOCKING_1:
	case WM8996_AIF_CLOCKING_2:
	case WM8996_CLOCKING_1:
	case WM8996_CLOCKING_2:
	case WM8996_AIF_RATE:
	case WM8996_FLL_CONTROL_1:
	case WM8996_FLL_CONTROL_2:
	case WM8996_FLL_CONTROL_3:
	case WM8996_FLL_CONTROL_4:
	case WM8996_FLL_CONTROL_5:
	case WM8996_FLL_CONTROL_6:
	case WM8996_FLL_EFS_1:
	case WM8996_FLL_EFS_2:
	case WM8996_AIF1_CONTROL:
	case WM8996_AIF1_BCLK:
	case WM8996_AIF1_TX_LRCLK_1:
	case WM8996_AIF1_TX_LRCLK_2:
	case WM8996_AIF1_RX_LRCLK_1:
	case WM8996_AIF1_RX_LRCLK_2:
	case WM8996_AIF1TX_DATA_CONFIGURATION_1:
	case WM8996_AIF1TX_DATA_CONFIGURATION_2:
	case WM8996_AIF1RX_DATA_CONFIGURATION:
	case WM8996_AIF1TX_CHANNEL_0_CONFIGURATION:
	case WM8996_AIF1TX_CHANNEL_1_CONFIGURATION:
	case WM8996_AIF1TX_CHANNEL_2_CONFIGURATION:
	case WM8996_AIF1TX_CHANNEL_3_CONFIGURATION:
	case WM8996_AIF1TX_CHANNEL_4_CONFIGURATION:
	case WM8996_AIF1TX_CHANNEL_5_CONFIGURATION:
	case WM8996_AIF1RX_CHANNEL_0_CONFIGURATION:
	case WM8996_AIF1RX_CHANNEL_1_CONFIGURATION:
	case WM8996_AIF1RX_CHANNEL_2_CONFIGURATION:
	case WM8996_AIF1RX_CHANNEL_3_CONFIGURATION:
	case WM8996_AIF1RX_CHANNEL_4_CONFIGURATION:
	case WM8996_AIF1RX_CHANNEL_5_CONFIGURATION:
	case WM8996_AIF1RX_MONO_CONFIGURATION:
	case WM8996_AIF1TX_TEST:
	case WM8996_AIF2_CONTROL:
	case WM8996_AIF2_BCLK:
	case WM8996_AIF2_TX_LRCLK_1:
	case WM8996_AIF2_TX_LRCLK_2:
	case WM8996_AIF2_RX_LRCLK_1:
	case WM8996_AIF2_RX_LRCLK_2:
	case WM8996_AIF2TX_DATA_CONFIGURATION_1:
	case WM8996_AIF2TX_DATA_CONFIGURATION_2:
	case WM8996_AIF2RX_DATA_CONFIGURATION:
	case WM8996_AIF2TX_CHANNEL_0_CONFIGURATION:
	case WM8996_AIF2TX_CHANNEL_1_CONFIGURATION:
	case WM8996_AIF2RX_CHANNEL_0_CONFIGURATION:
	case WM8996_AIF2RX_CHANNEL_1_CONFIGURATION:
	case WM8996_AIF2RX_MONO_CONFIGURATION:
	case WM8996_AIF2TX_TEST:
	case WM8996_DSP1_TX_LEFT_VOLUME:
	case WM8996_DSP1_TX_RIGHT_VOLUME:
	case WM8996_DSP1_RX_LEFT_VOLUME:
	case WM8996_DSP1_RX_RIGHT_VOLUME:
	case WM8996_DSP1_TX_FILTERS:
	case WM8996_DSP1_RX_FILTERS_1:
	case WM8996_DSP1_RX_FILTERS_2:
	case WM8996_DSP1_DRC_1:
	case WM8996_DSP1_DRC_2:
	case WM8996_DSP1_DRC_3:
	case WM8996_DSP1_DRC_4:
	case WM8996_DSP1_DRC_5:
	case WM8996_DSP1_RX_EQ_GAINS_1:
	case WM8996_DSP1_RX_EQ_GAINS_2:
	case WM8996_DSP1_RX_EQ_BAND_1_A:
	case WM8996_DSP1_RX_EQ_BAND_1_B:
	case WM8996_DSP1_RX_EQ_BAND_1_PG:
	case WM8996_DSP1_RX_EQ_BAND_2_A:
	case WM8996_DSP1_RX_EQ_BAND_2_B:
	case WM8996_DSP1_RX_EQ_BAND_2_C:
	case WM8996_DSP1_RX_EQ_BAND_2_PG:
	case WM8996_DSP1_RX_EQ_BAND_3_A:
	case WM8996_DSP1_RX_EQ_BAND_3_B:
	case WM8996_DSP1_RX_EQ_BAND_3_C:
	case WM8996_DSP1_RX_EQ_BAND_3_PG:
	case WM8996_DSP1_RX_EQ_BAND_4_A:
	case WM8996_DSP1_RX_EQ_BAND_4_B:
	case WM8996_DSP1_RX_EQ_BAND_4_C:
	case WM8996_DSP1_RX_EQ_BAND_4_PG:
	case WM8996_DSP1_RX_EQ_BAND_5_A:
	case WM8996_DSP1_RX_EQ_BAND_5_B:
	case WM8996_DSP1_RX_EQ_BAND_5_PG:
	case WM8996_DSP2_TX_LEFT_VOLUME:
	case WM8996_DSP2_TX_RIGHT_VOLUME:
	case WM8996_DSP2_RX_LEFT_VOLUME:
	case WM8996_DSP2_RX_RIGHT_VOLUME:
	case WM8996_DSP2_TX_FILTERS:
	case WM8996_DSP2_RX_FILTERS_1:
	case WM8996_DSP2_RX_FILTERS_2:
	case WM8996_DSP2_DRC_1:
	case WM8996_DSP2_DRC_2:
	case WM8996_DSP2_DRC_3:
	case WM8996_DSP2_DRC_4:
	case WM8996_DSP2_DRC_5:
	case WM8996_DSP2_RX_EQ_GAINS_1:
	case WM8996_DSP2_RX_EQ_GAINS_2:
	case WM8996_DSP2_RX_EQ_BAND_1_A:
	case WM8996_DSP2_RX_EQ_BAND_1_B:
	case WM8996_DSP2_RX_EQ_BAND_1_PG:
	case WM8996_DSP2_RX_EQ_BAND_2_A:
	case WM8996_DSP2_RX_EQ_BAND_2_B:
	case WM8996_DSP2_RX_EQ_BAND_2_C:
	case WM8996_DSP2_RX_EQ_BAND_2_PG:
	case WM8996_DSP2_RX_EQ_BAND_3_A:
	case WM8996_DSP2_RX_EQ_BAND_3_B:
	case WM8996_DSP2_RX_EQ_BAND_3_C:
	case WM8996_DSP2_RX_EQ_BAND_3_PG:
	case WM8996_DSP2_RX_EQ_BAND_4_A:
	case WM8996_DSP2_RX_EQ_BAND_4_B:
	case WM8996_DSP2_RX_EQ_BAND_4_C:
	case WM8996_DSP2_RX_EQ_BAND_4_PG:
	case WM8996_DSP2_RX_EQ_BAND_5_A:
	case WM8996_DSP2_RX_EQ_BAND_5_B:
	case WM8996_DSP2_RX_EQ_BAND_5_PG:
	case WM8996_DAC1_MIXER_VOLUMES:
	case WM8996_DAC1_LEFT_MIXER_ROUTING:
	case WM8996_DAC1_RIGHT_MIXER_ROUTING:
	case WM8996_DAC2_MIXER_VOLUMES:
	case WM8996_DAC2_LEFT_MIXER_ROUTING:
	case WM8996_DAC2_RIGHT_MIXER_ROUTING:
	case WM8996_DSP1_TX_LEFT_MIXER_ROUTING:
	case WM8996_DSP1_TX_RIGHT_MIXER_ROUTING:
	case WM8996_DSP2_TX_LEFT_MIXER_ROUTING:
	case WM8996_DSP2_TX_RIGHT_MIXER_ROUTING:
	case WM8996_DSP_TX_MIXER_SELECT:
	case WM8996_DAC_SOFTMUTE:
	case WM8996_OVERSAMPLING:
	case WM8996_SIDETONE:
	case WM8996_GPIO_1:
	case WM8996_GPIO_2:
	case WM8996_GPIO_3:
	case WM8996_GPIO_4:
	case WM8996_GPIO_5:
	case WM8996_PULL_CONTROL_1:
	case WM8996_PULL_CONTROL_2:
	case WM8996_INTERRUPT_STATUS_1:
	case WM8996_INTERRUPT_STATUS_2:
	case WM8996_INTERRUPT_RAW_STATUS_2:
	case WM8996_INTERRUPT_STATUS_1_MASK:
	case WM8996_INTERRUPT_STATUS_2_MASK:
	case WM8996_INTERRUPT_CONTROL:
	case WM8996_LEFT_PDM_SPEAKER:
	case WM8996_RIGHT_PDM_SPEAKER:
	case WM8996_PDM_SPEAKER_MUTE_SEQUENCE:
	case WM8996_PDM_SPEAKER_VOLUME:
		return 1;
	default:
		return 0;
	}
}

static bool wm8996_volatile_register(struct device *dev, unsigned int reg)
{
	switch (reg) {
	case WM8996_SOFTWARE_RESET:
	case WM8996_CHIP_REVISION:
	case WM8996_LDO_1:
	case WM8996_LDO_2:
	case WM8996_INTERRUPT_STATUS_1:
	case WM8996_INTERRUPT_STATUS_2:
	case WM8996_INTERRUPT_RAW_STATUS_2:
	case WM8996_DC_SERVO_READBACK_0:
	case WM8996_DC_SERVO_2:
	case WM8996_DC_SERVO_6:
	case WM8996_DC_SERVO_7:
	case WM8996_FLL_CONTROL_6:
	case WM8996_MIC_DETECT_3:
	case WM8996_HEADPHONE_DETECT_1:
	case WM8996_HEADPHONE_DETECT_2:
		return 1;
	default:
		return 0;
	}
}

static const int bclk_divs[] = {
	1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96
};

static void wm8996_update_bclk(struct snd_soc_codec *codec)
{
	struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
	int aif, best, cur_val, bclk_rate, bclk_reg, i;

	/* Don't bother if we're in a low frequency idle mode that
	 * can't support audio.
	 */
	if (wm8996->sysclk < 64000)
		return;

	for (aif = 0; aif < WM8996_AIFS; aif++) {
		switch (aif) {
		case 0:
			bclk_reg = WM8996_AIF1_BCLK;
			break;
		case 1:
			bclk_reg = WM8996_AIF2_BCLK;
			break;
		}

		bclk_rate = wm8996->bclk_rate[aif];

		/* Pick a divisor for BCLK as close as we can get to ideal */
		best = 0;
		for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) {
			cur_val = (wm8996->sysclk / bclk_divs[i]) - bclk_rate;
			if (cur_val < 0) /* BCLK table is sorted */
				break;
			best = i;
		}
		bclk_rate = wm8996->sysclk / bclk_divs[best];
		dev_dbg(codec->dev, "Using BCLK_DIV %d for actual BCLK %dHz\n",
			bclk_divs[best], bclk_rate);

		snd_soc_update_bits(codec, bclk_reg,
				    WM8996_AIF1_BCLK_DIV_MASK, best);
	}
}

static int wm8996_set_bias_level(struct snd_soc_codec *codec,
				 enum snd_soc_bias_level level)
{
	struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
	int ret;

	switch (level) {
	case SND_SOC_BIAS_ON:
		break;
	case SND_SOC_BIAS_PREPARE:
		/* Put the MICBIASes into regulating mode */
		snd_soc_update_bits(codec, WM8996_MICBIAS_1,
				    WM8996_MICB1_MODE, 0);
		snd_soc_update_bits(codec, WM8996_MICBIAS_2,
				    WM8996_MICB2_MODE, 0);
		break;

	case SND_SOC_BIAS_STANDBY:
		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
			ret = regulator_bulk_enable(ARRAY_SIZE(wm8996->supplies),
						    wm8996->supplies);
			if (ret != 0) {
				dev_err(codec->dev,
					"Failed to enable supplies: %d\n",
					ret);
				return ret;
			}

			if (wm8996->pdata.ldo_ena >= 0) {
				gpio_set_value_cansleep(wm8996->pdata.ldo_ena,
							1);
				msleep(5);
			}

			regcache_cache_only(codec->control_data, false);
			regcache_sync(codec->control_data);
		}

		/* Bypass the MICBIASes for lowest power */
		snd_soc_update_bits(codec, WM8996_MICBIAS_1,
				    WM8996_MICB1_MODE, WM8996_MICB1_MODE);
		snd_soc_update_bits(codec, WM8996_MICBIAS_2,
				    WM8996_MICB2_MODE, WM8996_MICB2_MODE);
		break;

	case SND_SOC_BIAS_OFF:
		regcache_cache_only(codec->control_data, true);
		if (wm8996->pdata.ldo_ena >= 0) {
			gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0);
			regcache_cache_only(codec->control_data, true);
		}
		regulator_bulk_disable(ARRAY_SIZE(wm8996->supplies),
				       wm8996->supplies);
		break;
	}

	codec->dapm.bias_level = level;

	return 0;
}

static int wm8996_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
	struct snd_soc_codec *codec = dai->codec;
	int aifctrl = 0;
	int bclk = 0;
	int lrclk_tx = 0;
	int lrclk_rx = 0;
	int aifctrl_reg, bclk_reg, lrclk_tx_reg, lrclk_rx_reg;

	switch (dai->id) {
	case 0:
		aifctrl_reg = WM8996_AIF1_CONTROL;
		bclk_reg = WM8996_AIF1_BCLK;
		lrclk_tx_reg = WM8996_AIF1_TX_LRCLK_2;
		lrclk_rx_reg = WM8996_AIF1_RX_LRCLK_2;
		break;
	case 1:
		aifctrl_reg = WM8996_AIF2_CONTROL;
		bclk_reg = WM8996_AIF2_BCLK;
		lrclk_tx_reg = WM8996_AIF2_TX_LRCLK_2;
		lrclk_rx_reg = WM8996_AIF2_RX_LRCLK_2;
		break;
	default:
		BUG();
		return -EINVAL;
	}

	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
	case SND_SOC_DAIFMT_NB_NF:
		break;
	case SND_SOC_DAIFMT_IB_NF:
		bclk |= WM8996_AIF1_BCLK_INV;
		break;
	case SND_SOC_DAIFMT_NB_IF:
		lrclk_tx |= WM8996_AIF1TX_LRCLK_INV;
		lrclk_rx |= WM8996_AIF1RX_LRCLK_INV;
		break;
	case SND_SOC_DAIFMT_IB_IF:
		bclk |= WM8996_AIF1_BCLK_INV;
		lrclk_tx |= WM8996_AIF1TX_LRCLK_INV;
		lrclk_rx |= WM8996_AIF1RX_LRCLK_INV;
		break;
	}

	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
	case SND_SOC_DAIFMT_CBS_CFS:
		break;
	case SND_SOC_DAIFMT_CBS_CFM:
		lrclk_tx |= WM8996_AIF1TX_LRCLK_MSTR;
		lrclk_rx |= WM8996_AIF1RX_LRCLK_MSTR;
		break;
	case SND_SOC_DAIFMT_CBM_CFS:
		bclk |= WM8996_AIF1_BCLK_MSTR;
		break;
	case SND_SOC_DAIFMT_CBM_CFM:
		bclk |= WM8996_AIF1_BCLK_MSTR;
		lrclk_tx |= WM8996_AIF1TX_LRCLK_MSTR;
		lrclk_rx |= WM8996_AIF1RX_LRCLK_MSTR;
		break;
	default:
		return -EINVAL;
	}

	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
	case SND_SOC_DAIFMT_DSP_A:
		break;
	case SND_SOC_DAIFMT_DSP_B:
		aifctrl |= 1;
		break;
	case SND_SOC_DAIFMT_I2S:
		aifctrl |= 2;
		break;
	case SND_SOC_DAIFMT_LEFT_J:
		aifctrl |= 3;
		break;
	default:
		return -EINVAL;
	}

	snd_soc_update_bits(codec, aifctrl_reg, WM8996_AIF1_FMT_MASK, aifctrl);
	snd_soc_update_bits(codec, bclk_reg,
			    WM8996_AIF1_BCLK_INV | WM8996_AIF1_BCLK_MSTR,
			    bclk);
	snd_soc_update_bits(codec, lrclk_tx_reg,
			    WM8996_AIF1TX_LRCLK_INV |
			    WM8996_AIF1TX_LRCLK_MSTR,
			    lrclk_tx);
	snd_soc_update_bits(codec, lrclk_rx_reg,
			    WM8996_AIF1RX_LRCLK_INV |
			    WM8996_AIF1RX_LRCLK_MSTR,
			    lrclk_rx);

	return 0;
}

static const int dsp_divs[] = {
	48000, 32000, 16000, 8000
};

static int wm8996_hw_params(struct snd_pcm_substream *substream,
			    struct snd_pcm_hw_params *params,
			    struct snd_soc_dai *dai)
{
	struct snd_soc_codec *codec = dai->codec;
	struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
	int bits, i, bclk_rate, best;
	int aifdata = 0;
	int lrclk = 0;
	int dsp = 0;
	int aifdata_reg, lrclk_reg, dsp_shift;

	switch (dai->id) {
	case 0:
		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ||
		    (snd_soc_read(codec, WM8996_GPIO_1)) & WM8996_GP1_FN_MASK) {
			aifdata_reg = WM8996_AIF1RX_DATA_CONFIGURATION;
			lrclk_reg = WM8996_AIF1_RX_LRCLK_1;
		} else {
			aifdata_reg = WM8996_AIF1TX_DATA_CONFIGURATION_1;
			lrclk_reg = WM8996_AIF1_TX_LRCLK_1;
		}
		dsp_shift = 0;
		break;
	case 1:
		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ||
		    (snd_soc_read(codec, WM8996_GPIO_2)) & WM8996_GP2_FN_MASK) {
			aifdata_reg = WM8996_AIF2RX_DATA_CONFIGURATION;
			lrclk_reg = WM8996_AIF2_RX_LRCLK_1;
		} else {
			aifdata_reg = WM8996_AIF2TX_DATA_CONFIGURATION_1;
			lrclk_reg = WM8996_AIF2_TX_LRCLK_1;
		}
		dsp_shift = WM8996_DSP2_DIV_SHIFT;
		break;
	default:
		BUG();
		return -EINVAL;
	}

	bclk_rate = snd_soc_params_to_bclk(params);
	if (bclk_rate < 0) {
		dev_err(codec->dev, "Unsupported BCLK rate: %d\n", bclk_rate);
		return bclk_rate;
	}

	wm8996->bclk_rate[dai->id] = bclk_rate;
	wm8996->rx_rate[dai->id] = params_rate(params);

	/* Needs looking at for TDM */
	bits = snd_pcm_format_width(params_format(params));
	if (bits < 0)
		return bits;
	aifdata |= (bits << WM8996_AIF1TX_WL_SHIFT) | bits;

	best = 0;
	for (i = 0; i < ARRAY_SIZE(dsp_divs); i++) {
		if (abs(dsp_divs[i] - params_rate(params)) <
		    abs(dsp_divs[best] - params_rate(params)))
			best = i;
	}
	dsp |= i << dsp_shift;

	wm8996_update_bclk(codec);

	lrclk = bclk_rate / params_rate(params);
	dev_dbg(dai->dev, "Using LRCLK rate %d for actual LRCLK %dHz\n",
		lrclk, bclk_rate / lrclk);

	snd_soc_update_bits(codec, aifdata_reg,
			    WM8996_AIF1TX_WL_MASK |
			    WM8996_AIF1TX_SLOT_LEN_MASK,
			    aifdata);
	snd_soc_update_bits(codec, lrclk_reg, WM8996_AIF1RX_RATE_MASK,
			    lrclk);
	snd_soc_update_bits(codec, WM8996_AIF_CLOCKING_2,
			    WM8996_DSP1_DIV_MASK << dsp_shift, dsp);

	return 0;
}

static int wm8996_set_sysclk(struct snd_soc_dai *dai,
		int clk_id, unsigned int freq, int dir)
{
	struct snd_soc_codec *codec = dai->codec;
	struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
	int lfclk = 0;
	int ratediv = 0;
	int sync = WM8996_REG_SYNC;
	int src;
	int old;

	if (freq == wm8996->sysclk && clk_id == wm8996->sysclk_src)
		return 0;

	/* Disable SYSCLK while we reconfigure */
	old = snd_soc_read(codec, WM8996_AIF_CLOCKING_1) & WM8996_SYSCLK_ENA;
	snd_soc_update_bits(codec, WM8996_AIF_CLOCKING_1,
			    WM8996_SYSCLK_ENA, 0);

	switch (clk_id) {
	case WM8996_SYSCLK_MCLK1:
		wm8996->sysclk = freq;
		src = 0;
		break;
	case WM8996_SYSCLK_MCLK2:
		wm8996->sysclk = freq;
		src = 1;
		break;
	case WM8996_SYSCLK_FLL:
		wm8996->sysclk = freq;
		src = 2;
		break;
	default:
		dev_err(codec->dev, "Unsupported clock source %d\n", clk_id);
		return -EINVAL;
	}

	switch (wm8996->sysclk) {
	case 5644800:
	case 6144000:
		snd_soc_update_bits(codec, WM8996_AIF_RATE,
				    WM8996_SYSCLK_RATE, 0);
		break;
	case 22579200:
	case 24576000:
		ratediv = WM8996_SYSCLK_DIV;
		wm8996->sysclk /= 2;
	case 11289600:
	case 12288000:
		snd_soc_update_bits(codec, WM8996_AIF_RATE,
				    WM8996_SYSCLK_RATE, WM8996_SYSCLK_RATE);
		break;
	case 32000:
	case 32768:
		lfclk = WM8996_LFCLK_ENA;
		sync = 0;
		break;
	default:
		dev_warn(codec->dev, "Unsupported clock rate %dHz\n",
			 wm8996->sysclk);
		return -EINVAL;
	}

	wm8996_update_bclk(codec);

	snd_soc_update_bits(codec, WM8996_AIF_CLOCKING_1,
			    WM8996_SYSCLK_SRC_MASK | WM8996_SYSCLK_DIV_MASK,
			    src << WM8996_SYSCLK_SRC_SHIFT | ratediv);
	snd_soc_update_bits(codec, WM8996_CLOCKING_1, WM8996_LFCLK_ENA, lfclk);
	snd_soc_update_bits(codec, WM8996_CONTROL_INTERFACE_1,
			    WM8996_REG_SYNC, sync);
	snd_soc_update_bits(codec, WM8996_AIF_CLOCKING_1,
			    WM8996_SYSCLK_ENA, old);

	wm8996->sysclk_src = clk_id;

	return 0;
}

struct _fll_div {
	u16 fll_fratio;
	u16 fll_outdiv;
	u16 fll_refclk_div;
	u16 fll_loop_gain;
	u16 fll_ref_freq;
	u16 n;
	u16 theta;
	u16 lambda;
};

static struct {
	unsigned int min;
	unsigned int max;
	u16 fll_fratio;
	int ratio;
} fll_fratios[] = {
	{       0,    64000, 4, 16 },
	{   64000,   128000, 3,  8 },
	{  128000,   256000, 2,  4 },
	{  256000,  1000000, 1,  2 },
	{ 1000000, 13500000, 0,  1 },
};

static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
		       unsigned int Fout)
{
	unsigned int target;
	unsigned int div;
	unsigned int fratio, gcd_fll;
	int i;

	/* Fref must be <=13.5MHz */
	div = 1;
	fll_div->fll_refclk_div = 0;
	while ((Fref / div) > 13500000) {
		div *= 2;
		fll_div->fll_refclk_div++;

		if (div > 8) {
			pr_err("Can't scale %dMHz input down to <=13.5MHz\n",
			       Fref);
			return -EINVAL;
		}
	}

	pr_debug("FLL Fref=%u Fout=%u\n", Fref, Fout);

	/* Apply the division for our remaining calculations */
	Fref /= div;

	if (Fref >= 3000000)
		fll_div->fll_loop_gain = 5;
	else
		fll_div->fll_loop_gain = 0;

	if (Fref >= 48000)
		fll_div->fll_ref_freq = 0;
	else
		fll_div->fll_ref_freq = 1;

	/* Fvco should be 90-100MHz; don't check the upper bound */
	div = 2;
	while (Fout * div < 90000000) {
		div++;
		if (div > 64) {
			pr_err("Unable to find FLL_OUTDIV for Fout=%uHz\n",
			       Fout);
			return -EINVAL;
		}
	}
	target = Fout * div;
	fll_div->fll_outdiv = div - 1;

	pr_debug("FLL Fvco=%dHz\n", target);

	/* Find an appropraite FLL_FRATIO and factor it out of the target */
	for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) {
		if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) {
			fll_div->fll_fratio = fll_fratios[i].fll_fratio;
			fratio = fll_fratios[i].ratio;
			break;
		}
	}
	if (i == ARRAY_SIZE(fll_fratios)) {
		pr_err("Unable to find FLL_FRATIO for Fref=%uHz\n", Fref);
		return -EINVAL;
	}

	fll_div->n = target / (fratio * Fref);

	if (target % Fref == 0) {
		fll_div->theta = 0;
		fll_div->lambda = 0;
	} else {
		gcd_fll = gcd(target, fratio * Fref);

		fll_div->theta = (target - (fll_div->n * fratio * Fref))
			/ gcd_fll;
		fll_div->lambda = (fratio * Fref) / gcd_fll;
	}

	pr_debug("FLL N=%x THETA=%x LAMBDA=%x\n",
		 fll_div->n, fll_div->theta, fll_div->lambda);
	pr_debug("FLL_FRATIO=%x FLL_OUTDIV=%x FLL_REFCLK_DIV=%x\n",
		 fll_div->fll_fratio, fll_div->fll_outdiv,
		 fll_div->fll_refclk_div);

	return 0;
}

static int wm8996_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
			  unsigned int Fref, unsigned int Fout)
{
	struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
	struct i2c_client *i2c = to_i2c_client(codec->dev);
	struct _fll_div fll_div;
	unsigned long timeout;
	int ret, reg, retry;

	/* Any change? */
	if (source == wm8996->fll_src && Fref == wm8996->fll_fref &&
	    Fout == wm8996->fll_fout)
		return 0;

	if (Fout == 0) {
		dev_dbg(codec->dev, "FLL disabled\n");

		wm8996->fll_fref = 0;
		wm8996->fll_fout = 0;

		snd_soc_update_bits(codec, WM8996_FLL_CONTROL_1,
				    WM8996_FLL_ENA, 0);

		wm8996_bg_disable(codec);

		return 0;
	}

	ret = fll_factors(&fll_div, Fref, Fout);
	if (ret != 0)
		return ret;

	switch (source) {
	case WM8996_FLL_MCLK1:
		reg = 0;
		break;
	case WM8996_FLL_MCLK2:
		reg = 1;
		break;
	case WM8996_FLL_DACLRCLK1:
		reg = 2;
		break;
	case WM8996_FLL_BCLK1:
		reg = 3;
		break;
	default:
		dev_err(codec->dev, "Unknown FLL source %d\n", ret);
		return -EINVAL;
	}

	reg |= fll_div.fll_refclk_div << WM8996_FLL_REFCLK_DIV_SHIFT;
	reg |= fll_div.fll_ref_freq << WM8996_FLL_REF_FREQ_SHIFT;

	snd_soc_update_bits(codec, WM8996_FLL_CONTROL_5,
			    WM8996_FLL_REFCLK_DIV_MASK | WM8996_FLL_REF_FREQ |
			    WM8996_FLL_REFCLK_SRC_MASK, reg);

	reg = 0;
	if (fll_div.theta || fll_div.lambda)
		reg |= WM8996_FLL_EFS_ENA | (3 << WM8996_FLL_LFSR_SEL_SHIFT);
	else
		reg |= 1 << WM8996_FLL_LFSR_SEL_SHIFT;
	snd_soc_write(codec, WM8996_FLL_EFS_2, reg);

	snd_soc_update_bits(codec, WM8996_FLL_CONTROL_2,
			    WM8996_FLL_OUTDIV_MASK |
			    WM8996_FLL_FRATIO_MASK,
			    (fll_div.fll_outdiv << WM8996_FLL_OUTDIV_SHIFT) |
			    (fll_div.fll_fratio));

	snd_soc_write(codec, WM8996_FLL_CONTROL_3, fll_div.theta);

	snd_soc_update_bits(codec, WM8996_FLL_CONTROL_4,
			    WM8996_FLL_N_MASK | WM8996_FLL_LOOP_GAIN_MASK,
			    (fll_div.n << WM8996_FLL_N_SHIFT) |
			    fll_div.fll_loop_gain);

	snd_soc_write(codec, WM8996_FLL_EFS_1, fll_div.lambda);

	/* Enable the bandgap if it's not already enabled */
	ret = snd_soc_read(codec, WM8996_FLL_CONTROL_1);
	if (!(ret & WM8996_FLL_ENA))
		wm8996_bg_enable(codec);

	/* Clear any pending completions (eg, from failed startups) */
	try_wait_for_completion(&wm8996->fll_lock);

	snd_soc_update_bits(codec, WM8996_FLL_CONTROL_1,
			    WM8996_FLL_ENA, WM8996_FLL_ENA);

	/* The FLL supports live reconfiguration - kick that in case we were
	 * already enabled.
	 */
	snd_soc_write(codec, WM8996_FLL_CONTROL_6, WM8996_FLL_SWITCH_CLK);

	/* Wait for the FLL to lock, using the interrupt if possible */
	if (Fref > 1000000)
		timeout = usecs_to_jiffies(300);
	else
		timeout = msecs_to_jiffies(2);

	/* Allow substantially longer if we've actually got the IRQ, poll
	 * at a slightly higher rate if we don't.
	 */
	if (i2c->irq)
		timeout *= 10;
	else
		timeout /= 2;

	for (retry = 0; retry < 10; retry++) {
		ret = wait_for_completion_timeout(&wm8996->fll_lock,
						  timeout);
		if (ret != 0) {
			WARN_ON(!i2c->irq);
			break;
		}

		ret = snd_soc_read(codec, WM8996_INTERRUPT_RAW_STATUS_2);
		if (ret & WM8996_FLL_LOCK_STS)
			break;
	}
	if (retry == 10) {
		dev_err(codec->dev, "Timed out waiting for FLL\n");
		ret = -ETIMEDOUT;
	}

	dev_dbg(codec->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout);

	wm8996->fll_fref = Fref;
	wm8996->fll_fout = Fout;
	wm8996->fll_src = source;

	return ret;
}

#ifdef CONFIG_GPIOLIB
static inline struct wm8996_priv *gpio_to_wm8996(struct gpio_chip *chip)
{
	return container_of(chip, struct wm8996_priv, gpio_chip);
}

static void wm8996_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
	struct wm8996_priv *wm8996 = gpio_to_wm8996(chip);

	regmap_update_bits(wm8996->regmap, WM8996_GPIO_1 + offset,
			   WM8996_GP1_LVL, !!value << WM8996_GP1_LVL_SHIFT);
}

static int wm8996_gpio_direction_out(struct gpio_chip *chip,
				     unsigned offset, int value)
{
	struct wm8996_priv *wm8996 = gpio_to_wm8996(chip);
	int val;

	val = (1 << WM8996_GP1_FN_SHIFT) | (!!value << WM8996_GP1_LVL_SHIFT);

	return regmap_update_bits(wm8996->regmap, WM8996_GPIO_1 + offset,
				  WM8996_GP1_FN_MASK | WM8996_GP1_DIR |
				  WM8996_GP1_LVL, val);
}

static int wm8996_gpio_get(struct gpio_chip *chip, unsigned offset)
{
	struct wm8996_priv *wm8996 = gpio_to_wm8996(chip);
	unsigned int reg;
	int ret;

	ret = regmap_read(wm8996->regmap, WM8996_GPIO_1 + offset, &reg);
	if (ret < 0)
		return ret;

	return (reg & WM8996_GP1_LVL) != 0;
}

static int wm8996_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
{
	struct wm8996_priv *wm8996 = gpio_to_wm8996(chip);

	return regmap_update_bits(wm8996->regmap, WM8996_GPIO_1 + offset,
				  WM8996_GP1_FN_MASK | WM8996_GP1_DIR,
				  (1 << WM8996_GP1_FN_SHIFT) |
				  (1 << WM8996_GP1_DIR_SHIFT));
}

static struct gpio_chip wm8996_template_chip = {
	.label			= "wm8996",
	.owner			= THIS_MODULE,
	.direction_output	= wm8996_gpio_direction_out,
	.set			= wm8996_gpio_set,
	.direction_input	= wm8996_gpio_direction_in,
	.get			= wm8996_gpio_get,
	.can_sleep		= 1,
};

static void wm8996_init_gpio(struct wm8996_priv *wm8996)
{
	int ret;

	wm8996->gpio_chip = wm8996_template_chip;
	wm8996->gpio_chip.ngpio = 5;
	wm8996->gpio_chip.dev = wm8996->dev;

	if (wm8996->pdata.gpio_base)
		wm8996->gpio_chip.base = wm8996->pdata.gpio_base;
	else
		wm8996->gpio_chip.base = -1;

	ret = gpiochip_add(&wm8996->gpio_chip);
	if (ret != 0)
		dev_err(wm8996->dev, "Failed to add GPIOs: %d\n", ret);
}

static void wm8996_free_gpio(struct wm8996_priv *wm8996)
{
	int ret;

	ret = gpiochip_remove(&wm8996->gpio_chip);
	if (ret != 0)
		dev_err(wm8996->dev, "Failed to remove GPIOs: %d\n", ret);
}
#else
static void wm8996_init_gpio(struct wm8996_priv *wm8996)
{
}

static void wm8996_free_gpio(struct wm8996_priv *wm8996)
{
}
#endif

/**
 * wm8996_detect - Enable default WM8996 jack detection
 *
 * The WM8996 has advanced accessory detection support for headsets.
 * This function provides a default implementation which integrates
 * the majority of this functionality with minimal user configuration.
 *
 * This will detect headset, headphone and short circuit button and
 * will also detect inverted microphone ground connections and update
 * the polarity of the connections.
 */
int wm8996_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
		  wm8996_polarity_fn polarity_cb)
{
	struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);

	wm8996->jack = jack;
	wm8996->detecting = true;
	wm8996->polarity_cb = polarity_cb;
	wm8996->jack_flips = 0;

	if (wm8996->polarity_cb)
		wm8996->polarity_cb(codec, 0);

	/* Clear discarge to avoid noise during detection */
	snd_soc_update_bits(codec, WM8996_MICBIAS_1,
			    WM8996_MICB1_DISCH, 0);
	snd_soc_update_bits(codec, WM8996_MICBIAS_2,
			    WM8996_MICB2_DISCH, 0);

	/* LDO2 powers the microphones, SYSCLK clocks detection */
	snd_soc_dapm_force_enable_pin(&codec->dapm, "LDO2");
	snd_soc_dapm_force_enable_pin(&codec->dapm, "SYSCLK");

	/* We start off just enabling microphone detection - even a
	 * plain headphone will trigger detection.
	 */
	snd_soc_update_bits(codec, WM8996_MIC_DETECT_1,
			    WM8996_MICD_ENA, WM8996_MICD_ENA);

	/* Slowest detection rate, gives debounce for initial detection */
	snd_soc_update_bits(codec, WM8996_MIC_DETECT_1,
			    WM8996_MICD_RATE_MASK,
			    WM8996_MICD_RATE_MASK);

	/* Enable interrupts and we're off */
	snd_soc_update_bits(codec, WM8996_INTERRUPT_STATUS_2_MASK,
			    WM8996_IM_MICD_EINT | WM8996_HP_DONE_EINT, 0);

	return 0;
}
EXPORT_SYMBOL_GPL(wm8996_detect);

static void wm8996_hpdet_irq(struct snd_soc_codec *codec)
{
	struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
	int val, reg, report;

	/* Assume headphone in error conditions; we need to report
	 * something or we stall our state machine.
	 */
	report = SND_JACK_HEADPHONE;

	reg = snd_soc_read(codec, WM8996_HEADPHONE_DETECT_2);
	if (reg < 0) {
		dev_err(codec->dev, "Failed to read HPDET status\n");
		goto out;
	}

	if (!(reg & WM8996_HP_DONE)) {
		dev_err(codec->dev, "Got HPDET IRQ but HPDET is busy\n");
		goto out;
	}

	val = reg & WM8996_HP_LVL_MASK;

	dev_dbg(codec->dev, "HPDET measured %d ohms\n", val);

	/* If we've got high enough impedence then report as line,
	 * otherwise assume headphone.
	 */
	if (val >= 126)
		report = SND_JACK_LINEOUT;
	else
		report = SND_JACK_HEADPHONE;

out:
	if (wm8996->jack_mic)
		report |= SND_JACK_MICROPHONE;

	snd_soc_jack_report(wm8996->jack, report,
			    SND_JACK_LINEOUT | SND_JACK_HEADSET);

	wm8996->detecting = false;

	/* If the output isn't running re-clamp it */
	if (!(snd_soc_read(codec, WM8996_POWER_MANAGEMENT_1) &
	      (WM8996_HPOUT1L_ENA | WM8996_HPOUT1R_RMV_SHORT)))
		snd_soc_update_bits(codec, WM8996_ANALOGUE_HP_1,
				    WM8996_HPOUT1L_RMV_SHORT |
				    WM8996_HPOUT1R_RMV_SHORT, 0);

	/* Go back to looking at the microphone */
	snd_soc_update_bits(codec, WM8996_ACCESSORY_DETECT_MODE_1,
			    WM8996_JD_MODE_MASK, 0);
	snd_soc_update_bits(codec, WM8996_MIC_DETECT_1, WM8996_MICD_ENA,
			    WM8996_MICD_ENA);

	snd_soc_dapm_disable_pin(&codec->dapm, "Bandgap");
	snd_soc_dapm_sync(&codec->dapm);
}

static void wm8996_hpdet_start(struct snd_soc_codec *codec)
{
	/* Unclamp the output, we can't measure while we're shorting it */
	snd_soc_update_bits(codec, WM8996_ANALOGUE_HP_1,
			    WM8996_HPOUT1L_RMV_SHORT |
			    WM8996_HPOUT1R_RMV_SHORT,
			    WM8996_HPOUT1L_RMV_SHORT |
			    WM8996_HPOUT1R_RMV_SHORT);

	/* We need bandgap for HPDET */
	snd_soc_dapm_force_enable_pin(&codec->dapm, "Bandgap");
	snd_soc_dapm_sync(&codec->dapm);

	/* Go into headphone detect left mode */
	snd_soc_update_bits(codec, WM8996_MIC_DETECT_1, WM8996_MICD_ENA, 0);
	snd_soc_update_bits(codec, WM8996_ACCESSORY_DETECT_MODE_1,
			    WM8996_JD_MODE_MASK, 1);

	/* Trigger a measurement */
	snd_soc_update_bits(codec, WM8996_HEADPHONE_DETECT_1,
			    WM8996_HP_POLL, WM8996_HP_POLL);
}

static void wm8996_report_headphone(struct snd_soc_codec *codec)
{
	dev_dbg(codec->dev, "Headphone detected\n");
	wm8996_hpdet_start(codec);

	/* Increase the detection rate a bit for responsiveness. */
	snd_soc_update_bits(codec, WM8996_MIC_DETECT_1,
			    WM8996_MICD_RATE_MASK |
			    WM8996_MICD_BIAS_STARTTIME_MASK,
			    7 << WM8996_MICD_RATE_SHIFT |
			    7 << WM8996_MICD_BIAS_STARTTIME_SHIFT);
}

static void wm8996_micd(struct snd_soc_codec *codec)
{
	struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
	int val, reg;

	val = snd_soc_read(codec, WM8996_MIC_DETECT_3);

	dev_dbg(codec->dev, "Microphone event: %x\n", val);

	if (!(val & WM8996_MICD_VALID)) {
		dev_warn(codec->dev, "Microphone detection state invalid\n");
		return;
	}

	/* No accessory, reset everything and report removal */
	if (!(val & WM8996_MICD_STS)) {
		dev_dbg(codec->dev, "Jack removal detected\n");
		wm8996->jack_mic = false;
		wm8996->detecting = true;
		wm8996->jack_flips = 0;
		snd_soc_jack_report(wm8996->jack, 0,
				    SND_JACK_LINEOUT | SND_JACK_HEADSET |
				    SND_JACK_BTN_0);

		snd_soc_update_bits(codec, WM8996_MIC_DETECT_1,
				    WM8996_MICD_RATE_MASK |
				    WM8996_MICD_BIAS_STARTTIME_MASK,
				    WM8996_MICD_RATE_MASK |
				    9 << WM8996_MICD_BIAS_STARTTIME_SHIFT);
		return;
	}

	/* If the measurement is very high we've got a microphone,
	 * either we just detected one or if we already reported then
	 * we've got a button release event.
	 */
	if (val & 0x400) {
		if (wm8996->detecting) {
			dev_dbg(codec->dev, "Microphone detected\n");
			wm8996->jack_mic = true;
			wm8996_hpdet_start(codec);

			/* Increase poll rate to give better responsiveness
			 * for buttons */
			snd_soc_update_bits(codec, WM8996_MIC_DETECT_1,
					    WM8996_MICD_RATE_MASK |
					    WM8996_MICD_BIAS_STARTTIME_MASK,
					    5 << WM8996_MICD_RATE_SHIFT |
					    7 << WM8996_MICD_BIAS_STARTTIME_SHIFT);
		} else {
			dev_dbg(codec->dev, "Mic button up\n");
			snd_soc_jack_report(wm8996->jack, 0, SND_JACK_BTN_0);
		}

		return;
	}

	/* If we detected a lower impedence during initial startup
	 * then we probably have the wrong polarity, flip it.  Don't
	 * do this for the lowest impedences to speed up detection of
	 * plain headphones.  If both polarities report a low
	 * impedence then give up and report headphones.
	 */
	if (wm8996->detecting && (val & 0x3f0)) {
		wm8996->jack_flips++;

		if (wm8996->jack_flips > 1) {
			wm8996_report_headphone(codec);
			return;
		}

		reg = snd_soc_read(codec, WM8996_ACCESSORY_DETECT_MODE_2);
		reg ^= WM8996_HPOUT1FB_SRC | WM8996_MICD_SRC |
			WM8996_MICD_BIAS_SRC;
		snd_soc_update_bits(codec, WM8996_ACCESSORY_DETECT_MODE_2,
				    WM8996_HPOUT1FB_SRC | WM8996_MICD_SRC |
				    WM8996_MICD_BIAS_SRC, reg);

		if (wm8996->polarity_cb)
			wm8996->polarity_cb(codec,
					    (reg & WM8996_MICD_SRC) != 0);

		dev_dbg(codec->dev, "Set microphone polarity to %d\n",
			(reg & WM8996_MICD_SRC) != 0);

		return;
	}

	/* Don't distinguish between buttons, just report any low
	 * impedence as BTN_0.
	 */
	if (val & 0x3fc) {
		if (wm8996->jack_mic) {
			dev_dbg(codec->dev, "Mic button detected\n");
			snd_soc_jack_report(wm8996->jack, SND_JACK_BTN_0,
					    SND_JACK_BTN_0);
		} else if (wm8996->detecting) {
			wm8996_report_headphone(codec);
		}
	}
}

static irqreturn_t wm8996_irq(int irq, void *data)
{
	struct snd_soc_codec *codec = data;
	struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
	int irq_val;

	irq_val = snd_soc_read(codec, WM8996_INTERRUPT_STATUS_2);
	if (irq_val < 0) {
		dev_err(codec->dev, "Failed to read IRQ status: %d\n",
			irq_val);
		return IRQ_NONE;
	}
	irq_val &= ~snd_soc_read(codec, WM8996_INTERRUPT_STATUS_2_MASK);

	if (!irq_val)
		return IRQ_NONE;

	snd_soc_write(codec, WM8996_INTERRUPT_STATUS_2, irq_val);

	if (irq_val & (WM8996_DCS_DONE_01_EINT | WM8996_DCS_DONE_23_EINT)) {
		dev_dbg(codec->dev, "DC servo IRQ\n");
		complete(&wm8996->dcs_done);
	}

	if (irq_val & WM8996_FIFOS_ERR_EINT)
		dev_err(codec->dev, "Digital core FIFO error\n");

	if (irq_val & WM8996_FLL_LOCK_EINT) {
		dev_dbg(codec->dev, "FLL locked\n");
		complete(&wm8996->fll_lock);
	}

	if (irq_val & WM8996_MICD_EINT)
		wm8996_micd(codec);

	if (irq_val & WM8996_HP_DONE_EINT)
		wm8996_hpdet_irq(codec);

	return IRQ_HANDLED;
}

static irqreturn_t wm8996_edge_irq(int irq, void *data)
{
	irqreturn_t ret = IRQ_NONE;
	irqreturn_t val;

	do {
		val = wm8996_irq(irq, data);
		if (val != IRQ_NONE)
			ret = val;
	} while (val != IRQ_NONE);

	return ret;
}

static void wm8996_retune_mobile_pdata(struct snd_soc_codec *codec)
{
	struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
	struct wm8996_pdata *pdata = &wm8996->pdata;

	struct snd_kcontrol_new controls[] = {
		SOC_ENUM_EXT("DSP1 EQ Mode",
			     wm8996->retune_mobile_enum,
			     wm8996_get_retune_mobile_enum,
			     wm8996_put_retune_mobile_enum),
		SOC_ENUM_EXT("DSP2 EQ Mode",
			     wm8996->retune_mobile_enum,
			     wm8996_get_retune_mobile_enum,
			     wm8996_put_retune_mobile_enum),
	};
	int ret, i, j;
	const char **t;

	/* We need an array of texts for the enum API but the number
	 * of texts is likely to be less than the number of
	 * configurations due to the sample rate dependency of the
	 * configurations. */
	wm8996->num_retune_mobile_texts = 0;
	wm8996->retune_mobile_texts = NULL;
	for (i = 0; i < pdata->num_retune_mobile_cfgs; i++) {
		for (j = 0; j < wm8996->num_retune_mobile_texts; j++) {
			if (strcmp(pdata->retune_mobile_cfgs[i].name,
				   wm8996->retune_mobile_texts[j]) == 0)
				break;
		}

		if (j != wm8996->num_retune_mobile_texts)
			continue;

		/* Expand the array... */
		t = krealloc(wm8996->retune_mobile_texts,
			     sizeof(char *) * 
			     (wm8996->num_retune_mobile_texts + 1),
			     GFP_KERNEL);
		if (t == NULL)
			continue;

		/* ...store the new entry... */
		t[wm8996->num_retune_mobile_texts] = 
			pdata->retune_mobile_cfgs[i].name;

		/* ...and remember the new version. */
		wm8996->num_retune_mobile_texts++;
		wm8996->retune_mobile_texts = t;
	}

	dev_dbg(codec->dev, "Allocated %d unique ReTune Mobile names\n",
		wm8996->num_retune_mobile_texts);

	wm8996->retune_mobile_enum.max = wm8996->num_retune_mobile_texts;
	wm8996->retune_mobile_enum.texts = wm8996->retune_mobile_texts;

	ret = snd_soc_add_codec_controls(codec, controls, ARRAY_SIZE(controls));
	if (ret != 0)
		dev_err(codec->dev,
			"Failed to add ReTune Mobile controls: %d\n", ret);
}

static const struct regmap_config wm8996_regmap = {
	.reg_bits = 16,
	.val_bits = 16,

	.max_register = WM8996_MAX_REGISTER,
	.reg_defaults = wm8996_reg,
	.num_reg_defaults = ARRAY_SIZE(wm8996_reg),
	.volatile_reg = wm8996_volatile_register,
	.readable_reg = wm8996_readable_register,
	.cache_type = REGCACHE_RBTREE,
};

static int wm8996_probe(struct snd_soc_codec *codec)
{
	int ret;
	struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
	struct i2c_client *i2c = to_i2c_client(codec->dev);
	int irq_flags;

	wm8996->codec = codec;

	init_completion(&wm8996->dcs_done);
	init_completion(&wm8996->fll_lock);

	codec->control_data = wm8996->regmap;

	ret = snd_soc_codec_set_cache_io(codec, 16, 16, SND_SOC_REGMAP);
	if (ret != 0) {
		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
		goto err;
	}

	if (wm8996->pdata.num_retune_mobile_cfgs)
		wm8996_retune_mobile_pdata(codec);
	else
		snd_soc_add_codec_controls(codec, wm8996_eq_controls,
				     ARRAY_SIZE(wm8996_eq_controls));

	if (i2c->irq) {
		if (wm8996->pdata.irq_flags)
			irq_flags = wm8996->pdata.irq_flags;
		else
			irq_flags = IRQF_TRIGGER_LOW;

		irq_flags |= IRQF_ONESHOT;

		if (irq_flags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING))
			ret = request_threaded_irq(i2c->irq, NULL,
						   wm8996_edge_irq,
						   irq_flags, "wm8996", codec);
		else
			ret = request_threaded_irq(i2c->irq, NULL, wm8996_irq,
						   irq_flags, "wm8996", codec);

		if (ret == 0) {
			/* Unmask the interrupt */
			snd_soc_update_bits(codec, WM8996_INTERRUPT_CONTROL,
					    WM8996_IM_IRQ, 0);

			/* Enable error reporting and DC servo status */
			snd_soc_update_bits(codec,
					    WM8996_INTERRUPT_STATUS_2_MASK,
					    WM8996_IM_DCS_DONE_23_EINT |
					    WM8996_IM_DCS_DONE_01_EINT |
					    WM8996_IM_FLL_LOCK_EINT |
					    WM8996_IM_FIFOS_ERR_EINT,
					    0);
		} else {
			dev_err(codec->dev, "Failed to request IRQ: %d\n",
				ret);
		}
	}

	return 0;

err:
	return ret;
}

static int wm8996_remove(struct snd_soc_codec *codec)
{
	struct i2c_client *i2c = to_i2c_client(codec->dev);

	snd_soc_update_bits(codec, WM8996_INTERRUPT_CONTROL,
			    WM8996_IM_IRQ, WM8996_IM_IRQ);

	if (i2c->irq)
		free_irq(i2c->irq, codec);

	return 0;
}

static struct snd_soc_codec_driver soc_codec_dev_wm8996 = {
	.probe =	wm8996_probe,
	.remove =	wm8996_remove,
	.set_bias_level = wm8996_set_bias_level,
	.idle_bias_off	= true,
	.seq_notifier = wm8996_seq_notifier,
	.controls = wm8996_snd_controls,
	.num_controls = ARRAY_SIZE(wm8996_snd_controls),
	.dapm_widgets = wm8996_dapm_widgets,
	.num_dapm_widgets = ARRAY_SIZE(wm8996_dapm_widgets),
	.dapm_routes = wm8996_dapm_routes,
	.num_dapm_routes = ARRAY_SIZE(wm8996_dapm_routes),
	.set_pll = wm8996_set_fll,
};

#define WM8996_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
		      SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
		      SNDRV_PCM_RATE_48000)
#define WM8996_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\
			SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE |\
			SNDRV_PCM_FMTBIT_S32_LE)

static const struct snd_soc_dai_ops wm8996_dai_ops = {
	.set_fmt = wm8996_set_fmt,
	.hw_params = wm8996_hw_params,
	.set_sysclk = wm8996_set_sysclk,
};

static struct snd_soc_dai_driver wm8996_dai[] = {
	{
		.name = "wm8996-aif1",
		.playback = {
			.stream_name = "AIF1 Playback",
			.channels_min = 1,
			.channels_max = 6,
			.rates = WM8996_RATES,
			.formats = WM8996_FORMATS,
			.sig_bits = 24,
		},
		.capture = {
			 .stream_name = "AIF1 Capture",
			 .channels_min = 1,
			 .channels_max = 6,
			 .rates = WM8996_RATES,
			 .formats = WM8996_FORMATS,
			 .sig_bits = 24,
		 },
		.ops = &wm8996_dai_ops,
	},
	{
		.name = "wm8996-aif2",
		.playback = {
			.stream_name = "AIF2 Playback",
			.channels_min = 1,
			.channels_max = 2,
			.rates = WM8996_RATES,
			.formats = WM8996_FORMATS,
			.sig_bits = 24,
		},
		.capture = {
			 .stream_name = "AIF2 Capture",
			 .channels_min = 1,
			 .channels_max = 2,
			 .rates = WM8996_RATES,
			 .formats = WM8996_FORMATS,
			.sig_bits = 24,
		 },
		.ops = &wm8996_dai_ops,
	},
};

static int wm8996_i2c_probe(struct i2c_client *i2c,
			    const struct i2c_device_id *id)
{
	struct wm8996_priv *wm8996;
	int ret, i;
	unsigned int reg;

	wm8996 = devm_kzalloc(&i2c->dev, sizeof(struct wm8996_priv),
			      GFP_KERNEL);
	if (wm8996 == NULL)
		return -ENOMEM;

	i2c_set_clientdata(i2c, wm8996);
	wm8996->dev = &i2c->dev;

	if (dev_get_platdata(&i2c->dev))
		memcpy(&wm8996->pdata, dev_get_platdata(&i2c->dev),
		       sizeof(wm8996->pdata));

	if (wm8996->pdata.ldo_ena > 0) {
		ret = gpio_request_one(wm8996->pdata.ldo_ena,
				       GPIOF_OUT_INIT_LOW, "WM8996 ENA");
		if (ret < 0) {
			dev_err(&i2c->dev, "Failed to request GPIO %d: %d\n",
				wm8996->pdata.ldo_ena, ret);
			goto err;
		}
	}

	for (i = 0; i < ARRAY_SIZE(wm8996->supplies); i++)
		wm8996->supplies[i].supply = wm8996_supply_names[i];

	ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8996->supplies),
				      wm8996->supplies);
	if (ret != 0) {
		dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
		goto err_gpio;
	}

	wm8996->disable_nb[0].notifier_call = wm8996_regulator_event_0;
	wm8996->disable_nb[1].notifier_call = wm8996_regulator_event_1;
	wm8996->disable_nb[2].notifier_call = wm8996_regulator_event_2;

	/* This should really be moved into the regulator core */
	for (i = 0; i < ARRAY_SIZE(wm8996->supplies); i++) {
		ret = regulator_register_notifier(wm8996->supplies[i].consumer,
						  &wm8996->disable_nb[i]);
		if (ret != 0) {
			dev_err(&i2c->dev,
				"Failed to register regulator notifier: %d\n",
				ret);
		}
	}

	ret = regulator_bulk_enable(ARRAY_SIZE(wm8996->supplies),
				    wm8996->supplies);
	if (ret != 0) {
		dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret);
		goto err_gpio;
	}

	if (wm8996->pdata.ldo_ena > 0) {
		gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 1);
		msleep(5);
	}

	wm8996->regmap = devm_regmap_init_i2c(i2c, &wm8996_regmap);
	if (IS_ERR(wm8996->regmap)) {
		ret = PTR_ERR(wm8996->regmap);
		dev_err(&i2c->dev, "regmap_init() failed: %d\n", ret);
		goto err_enable;
	}

	ret = regmap_read(wm8996->regmap, WM8996_SOFTWARE_RESET, &reg);
	if (ret < 0) {
		dev_err(&i2c->dev, "Failed to read ID register: %d\n", ret);
		goto err_regmap;
	}
	if (reg != 0x8915) {
		dev_err(&i2c->dev, "Device is not a WM8996, ID %x\n", reg);
		ret = -EINVAL;
		goto err_regmap;
	}

	ret = regmap_read(wm8996->regmap, WM8996_CHIP_REVISION, &reg);
	if (ret < 0) {
		dev_err(&i2c->dev, "Failed to read device revision: %d\n",
			ret);
		goto err_regmap;
	}

	dev_info(&i2c->dev, "revision %c\n",
		 (reg & WM8996_CHIP_REV_MASK) + 'A');

	if (wm8996->pdata.ldo_ena > 0) {
		gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0);
		regcache_cache_only(wm8996->regmap, true);
	} else {
		ret = regmap_write(wm8996->regmap, WM8996_SOFTWARE_RESET,
				   0x8915);
		if (ret != 0) {
			dev_err(&i2c->dev, "Failed to issue reset: %d\n", ret);
			goto err_regmap;
		}
	}

	regulator_bulk_disable(ARRAY_SIZE(wm8996->supplies), wm8996->supplies);

	/* Apply platform data settings */
	regmap_update_bits(wm8996->regmap, WM8996_LINE_INPUT_CONTROL,
			   WM8996_INL_MODE_MASK | WM8996_INR_MODE_MASK,
			   wm8996->pdata.inl_mode << WM8996_INL_MODE_SHIFT |
			   wm8996->pdata.inr_mode);

	for (i = 0; i < ARRAY_SIZE(wm8996->pdata.gpio_default); i++) {
		if (!wm8996->pdata.gpio_default[i])
			continue;

		regmap_write(wm8996->regmap, WM8996_GPIO_1 + i,
			     wm8996->pdata.gpio_default[i] & 0xffff);
	}

	if (wm8996->pdata.spkmute_seq)
		regmap_update_bits(wm8996->regmap,
				   WM8996_PDM_SPEAKER_MUTE_SEQUENCE,
				   WM8996_SPK_MUTE_ENDIAN |
				   WM8996_SPK_MUTE_SEQ1_MASK,
				   wm8996->pdata.spkmute_seq);

	regmap_update_bits(wm8996->regmap, WM8996_ACCESSORY_DETECT_MODE_2,
			   WM8996_MICD_BIAS_SRC | WM8996_HPOUT1FB_SRC |
			   WM8996_MICD_SRC, wm8996->pdata.micdet_def);

	/* Latch volume update bits */
	regmap_update_bits(wm8996->regmap, WM8996_LEFT_LINE_INPUT_VOLUME,
			   WM8996_IN1_VU, WM8996_IN1_VU);
	regmap_update_bits(wm8996->regmap, WM8996_RIGHT_LINE_INPUT_VOLUME,
			   WM8996_IN1_VU, WM8996_IN1_VU);

	regmap_update_bits(wm8996->regmap, WM8996_DAC1_LEFT_VOLUME,
			   WM8996_DAC1_VU, WM8996_DAC1_VU);
	regmap_update_bits(wm8996->regmap, WM8996_DAC1_RIGHT_VOLUME,
			   WM8996_DAC1_VU, WM8996_DAC1_VU);
	regmap_update_bits(wm8996->regmap, WM8996_DAC2_LEFT_VOLUME,
			   WM8996_DAC2_VU, WM8996_DAC2_VU);
	regmap_update_bits(wm8996->regmap, WM8996_DAC2_RIGHT_VOLUME,
			   WM8996_DAC2_VU, WM8996_DAC2_VU);

	regmap_update_bits(wm8996->regmap, WM8996_OUTPUT1_LEFT_VOLUME,
			   WM8996_DAC1_VU, WM8996_DAC1_VU);
	regmap_update_bits(wm8996->regmap, WM8996_OUTPUT1_RIGHT_VOLUME,
			   WM8996_DAC1_VU, WM8996_DAC1_VU);
	regmap_update_bits(wm8996->regmap, WM8996_OUTPUT2_LEFT_VOLUME,
			   WM8996_DAC2_VU, WM8996_DAC2_VU);
	regmap_update_bits(wm8996->regmap, WM8996_OUTPUT2_RIGHT_VOLUME,
			   WM8996_DAC2_VU, WM8996_DAC2_VU);

	regmap_update_bits(wm8996->regmap, WM8996_DSP1_TX_LEFT_VOLUME,
			   WM8996_DSP1TX_VU, WM8996_DSP1TX_VU);
	regmap_update_bits(wm8996->regmap, WM8996_DSP1_TX_RIGHT_VOLUME,
			   WM8996_DSP1TX_VU, WM8996_DSP1TX_VU);
	regmap_update_bits(wm8996->regmap, WM8996_DSP2_TX_LEFT_VOLUME,
			   WM8996_DSP2TX_VU, WM8996_DSP2TX_VU);
	regmap_update_bits(wm8996->regmap, WM8996_DSP2_TX_RIGHT_VOLUME,
			   WM8996_DSP2TX_VU, WM8996_DSP2TX_VU);

	regmap_update_bits(wm8996->regmap, WM8996_DSP1_RX_LEFT_VOLUME,
			   WM8996_DSP1RX_VU, WM8996_DSP1RX_VU);
	regmap_update_bits(wm8996->regmap, WM8996_DSP1_RX_RIGHT_VOLUME,
			   WM8996_DSP1RX_VU, WM8996_DSP1RX_VU);
	regmap_update_bits(wm8996->regmap, WM8996_DSP2_RX_LEFT_VOLUME,
			   WM8996_DSP2RX_VU, WM8996_DSP2RX_VU);
	regmap_update_bits(wm8996->regmap, WM8996_DSP2_RX_RIGHT_VOLUME,
			   WM8996_DSP2RX_VU, WM8996_DSP2RX_VU);

	/* No support currently for the underclocked TDM modes and
	 * pick a default TDM layout with each channel pair working with
	 * slots 0 and 1. */
	regmap_update_bits(wm8996->regmap,
			   WM8996_AIF1RX_CHANNEL_0_CONFIGURATION,
			   WM8996_AIF1RX_CHAN0_SLOTS_MASK |
			   WM8996_AIF1RX_CHAN0_START_SLOT_MASK,
			   1 << WM8996_AIF1RX_CHAN0_SLOTS_SHIFT | 0);
	regmap_update_bits(wm8996->regmap,
			   WM8996_AIF1RX_CHANNEL_1_CONFIGURATION,
			   WM8996_AIF1RX_CHAN1_SLOTS_MASK |
			   WM8996_AIF1RX_CHAN1_START_SLOT_MASK,
			   1 << WM8996_AIF1RX_CHAN1_SLOTS_SHIFT | 1);
	regmap_update_bits(wm8996->regmap,
			   WM8996_AIF1RX_CHANNEL_2_CONFIGURATION,
			   WM8996_AIF1RX_CHAN2_SLOTS_MASK |
			   WM8996_AIF1RX_CHAN2_START_SLOT_MASK,
			   1 << WM8996_AIF1RX_CHAN2_SLOTS_SHIFT | 0);
	regmap_update_bits(wm8996->regmap,
			   WM8996_AIF1RX_CHANNEL_3_CONFIGURATION,
			   WM8996_AIF1RX_CHAN3_SLOTS_MASK |
			   WM8996_AIF1RX_CHAN0_START_SLOT_MASK,
			   1 << WM8996_AIF1RX_CHAN3_SLOTS_SHIFT | 1);
	regmap_update_bits(wm8996->regmap,
			   WM8996_AIF1RX_CHANNEL_4_CONFIGURATION,
			   WM8996_AIF1RX_CHAN4_SLOTS_MASK |
			   WM8996_AIF1RX_CHAN0_START_SLOT_MASK,
			   1 << WM8996_AIF1RX_CHAN4_SLOTS_SHIFT | 0);
	regmap_update_bits(wm8996->regmap,
			   WM8996_AIF1RX_CHANNEL_5_CONFIGURATION,
			   WM8996_AIF1RX_CHAN5_SLOTS_MASK |
			   WM8996_AIF1RX_CHAN0_START_SLOT_MASK,
			   1 << WM8996_AIF1RX_CHAN5_SLOTS_SHIFT | 1);

	regmap_update_bits(wm8996->regmap,
			   WM8996_AIF2RX_CHANNEL_0_CONFIGURATION,
			   WM8996_AIF2RX_CHAN0_SLOTS_MASK |
			   WM8996_AIF2RX_CHAN0_START_SLOT_MASK,
			   1 << WM8996_AIF2RX_CHAN0_SLOTS_SHIFT | 0);
	regmap_update_bits(wm8996->regmap,
			   WM8996_AIF2RX_CHANNEL_1_CONFIGURATION,
			   WM8996_AIF2RX_CHAN1_SLOTS_MASK |
			   WM8996_AIF2RX_CHAN1_START_SLOT_MASK,
			   1 << WM8996_AIF2RX_CHAN1_SLOTS_SHIFT | 1);

	regmap_update_bits(wm8996->regmap,
			   WM8996_AIF1TX_CHANNEL_0_CONFIGURATION,
			   WM8996_AIF1TX_CHAN0_SLOTS_MASK |
			   WM8996_AIF1TX_CHAN0_START_SLOT_MASK,
			   1 << WM8996_AIF1TX_CHAN0_SLOTS_SHIFT | 0);
	regmap_update_bits(wm8996->regmap,
			   WM8996_AIF1TX_CHANNEL_1_CONFIGURATION,
			   WM8996_AIF1TX_CHAN1_SLOTS_MASK |
			   WM8996_AIF1TX_CHAN0_START_SLOT_MASK,
			   1 << WM8996_AIF1TX_CHAN1_SLOTS_SHIFT | 1);
	regmap_update_bits(wm8996->regmap,
			   WM8996_AIF1TX_CHANNEL_2_CONFIGURATION,
			   WM8996_AIF1TX_CHAN2_SLOTS_MASK |
			   WM8996_AIF1TX_CHAN0_START_SLOT_MASK,
			   1 << WM8996_AIF1TX_CHAN2_SLOTS_SHIFT | 0);
	regmap_update_bits(wm8996->regmap,
			   WM8996_AIF1TX_CHANNEL_3_CONFIGURATION,
			   WM8996_AIF1TX_CHAN3_SLOTS_MASK |
			   WM8996_AIF1TX_CHAN0_START_SLOT_MASK,
			   1 << WM8996_AIF1TX_CHAN3_SLOTS_SHIFT | 1);
	regmap_update_bits(wm8996->regmap,
			   WM8996_AIF1TX_CHANNEL_4_CONFIGURATION,
			   WM8996_AIF1TX_CHAN4_SLOTS_MASK |
			   WM8996_AIF1TX_CHAN0_START_SLOT_MASK,
			   1 << WM8996_AIF1TX_CHAN4_SLOTS_SHIFT | 0);
	regmap_update_bits(wm8996->regmap,
			   WM8996_AIF1TX_CHANNEL_5_CONFIGURATION,
			   WM8996_AIF1TX_CHAN5_SLOTS_MASK |
			   WM8996_AIF1TX_CHAN0_START_SLOT_MASK,
			   1 << WM8996_AIF1TX_CHAN5_SLOTS_SHIFT | 1);

	regmap_update_bits(wm8996->regmap,
			   WM8996_AIF2TX_CHANNEL_0_CONFIGURATION,
			   WM8996_AIF2TX_CHAN0_SLOTS_MASK |
			   WM8996_AIF2TX_CHAN0_START_SLOT_MASK,
			   1 << WM8996_AIF2TX_CHAN0_SLOTS_SHIFT | 0);
	regmap_update_bits(wm8996->regmap,
			   WM8996_AIF1TX_CHANNEL_1_CONFIGURATION,
			   WM8996_AIF2TX_CHAN1_SLOTS_MASK |
			   WM8996_AIF2TX_CHAN1_START_SLOT_MASK,
			   1 << WM8996_AIF1TX_CHAN1_SLOTS_SHIFT | 1);

	/* If the TX LRCLK pins are not in LRCLK mode configure the
	 * AIFs to source their clocks from the RX LRCLKs.
	 */
	ret = regmap_read(wm8996->regmap, WM8996_GPIO_1, &reg);
	if (ret != 0) {
		dev_err(&i2c->dev, "Failed to read GPIO1: %d\n", ret);
		goto err_regmap;
	}

	if (reg & WM8996_GP1_FN_MASK)
		regmap_update_bits(wm8996->regmap, WM8996_AIF1_TX_LRCLK_2,
				   WM8996_AIF1TX_LRCLK_MODE,
				   WM8996_AIF1TX_LRCLK_MODE);

	ret = regmap_read(wm8996->regmap, WM8996_GPIO_2, &reg);
	if (ret != 0) {
		dev_err(&i2c->dev, "Failed to read GPIO2: %d\n", ret);
		goto err_regmap;
	}

	if (reg & WM8996_GP2_FN_MASK)
		regmap_update_bits(wm8996->regmap, WM8996_AIF2_TX_LRCLK_2,
				   WM8996_AIF2TX_LRCLK_MODE,
				   WM8996_AIF2TX_LRCLK_MODE);

	wm8996_init_gpio(wm8996);

	ret = snd_soc_register_codec(&i2c->dev,
				     &soc_codec_dev_wm8996, wm8996_dai,
				     ARRAY_SIZE(wm8996_dai));
	if (ret < 0)
		goto err_gpiolib;

	return ret;

err_gpiolib:
	wm8996_free_gpio(wm8996);
err_regmap:
err_enable:
	if (wm8996->pdata.ldo_ena > 0)
		gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0);
	regulator_bulk_disable(ARRAY_SIZE(wm8996->supplies), wm8996->supplies);
err_gpio:
	if (wm8996->pdata.ldo_ena > 0)
		gpio_free(wm8996->pdata.ldo_ena);
err:

	return ret;
}

static int wm8996_i2c_remove(struct i2c_client *client)
{
	struct wm8996_priv *wm8996 = i2c_get_clientdata(client);
	int i;

	snd_soc_unregister_codec(&client->dev);
	wm8996_free_gpio(wm8996);
	if (wm8996->pdata.ldo_ena > 0) {
		gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0);