12b27bdccSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2cc17557eSSteve Sakoman /*
3cc17557eSSteve Sakoman * ALSA SoC TWL4030 codec driver
4cc17557eSSteve Sakoman *
5cc17557eSSteve Sakoman * Author: Steve Sakoman, <steve@sakoman.com>
6cc17557eSSteve Sakoman */
7cc17557eSSteve Sakoman
8cc17557eSSteve Sakoman #include <linux/module.h>
9cc17557eSSteve Sakoman #include <linux/moduleparam.h>
10cc17557eSSteve Sakoman #include <linux/init.h>
11cc17557eSSteve Sakoman #include <linux/delay.h>
12cc17557eSSteve Sakoman #include <linux/pm.h>
13cc17557eSSteve Sakoman #include <linux/i2c.h>
14cc17557eSSteve Sakoman #include <linux/platform_device.h>
152d6d649aSPeter Ujfalusi #include <linux/of.h>
162d6d649aSPeter Ujfalusi #include <linux/of_gpio.h>
17a2054256SWolfram Sang #include <linux/mfd/twl.h>
185a0e3ad6STejun Heo #include <linux/slab.h>
19281ecd16SPeter Ujfalusi #include <linux/gpio.h>
20cc17557eSSteve Sakoman #include <sound/core.h>
21cc17557eSSteve Sakoman #include <sound/pcm.h>
22cc17557eSSteve Sakoman #include <sound/pcm_params.h>
23cc17557eSSteve Sakoman #include <sound/soc.h>
24cc17557eSSteve Sakoman #include <sound/initval.h>
25c10b82cfSPeter Ujfalusi #include <sound/tlv.h>
26cc17557eSSteve Sakoman
27f0fba2adSLiam Girdwood /* Register descriptions are here */
2857fe7251SPeter Ujfalusi #include <linux/mfd/twl4030-audio.h>
29f0fba2adSLiam Girdwood
305712ded9SPeter Ujfalusi /* TWL4030 PMBR1 Register */
315712ded9SPeter Ujfalusi #define TWL4030_PMBR1_REG 0x0D
325712ded9SPeter Ujfalusi /* TWL4030 PMBR1 Register GPIO6 mux bits */
335712ded9SPeter Ujfalusi #define TWL4030_GPIO6_PWM0_MUTE(value) ((value & 0x03) << 2)
345712ded9SPeter Ujfalusi
35052901f4SLars-Peter Clausen #define TWL4030_CACHEREGNUM (TWL4030_REG_MISC_SET_2 + 1)
36cc17557eSSteve Sakoman
377adadfb0SPeter Ujfalusi struct twl4030_board_params {
387adadfb0SPeter Ujfalusi unsigned int digimic_delay; /* in ms */
397adadfb0SPeter Ujfalusi unsigned int ramp_delay_value;
407adadfb0SPeter Ujfalusi unsigned int offset_cncl_path;
417adadfb0SPeter Ujfalusi unsigned int hs_extmute:1;
427adadfb0SPeter Ujfalusi int hs_extmute_gpio;
437adadfb0SPeter Ujfalusi };
447adadfb0SPeter Ujfalusi
457393958fSPeter Ujfalusi /* codec private data */
467393958fSPeter Ujfalusi struct twl4030_priv {
477393958fSPeter Ujfalusi unsigned int codec_powered;
487b4c734eSPeter Ujfalusi
497b4c734eSPeter Ujfalusi /* reference counts of AIF/APLL users */
502845fa13SPeter Ujfalusi unsigned int apll_enabled;
517220b9f4SPeter Ujfalusi
527220b9f4SPeter Ujfalusi struct snd_pcm_substream *master_substream;
537220b9f4SPeter Ujfalusi struct snd_pcm_substream *slave_substream;
546b87a91fSPeter Ujfalusi
556b87a91fSPeter Ujfalusi unsigned int configured;
566b87a91fSPeter Ujfalusi unsigned int rate;
576b87a91fSPeter Ujfalusi unsigned int sample_bits;
586b87a91fSPeter Ujfalusi unsigned int channels;
596943c92eSPeter Ujfalusi
606943c92eSPeter Ujfalusi unsigned int sysclk;
616943c92eSPeter Ujfalusi
62c96907f2SPeter Ujfalusi /* Output (with associated amp) states */
63c96907f2SPeter Ujfalusi u8 hsl_enabled, hsr_enabled;
64c96907f2SPeter Ujfalusi u8 earpiece_enabled;
65c96907f2SPeter Ujfalusi u8 predrivel_enabled, predriver_enabled;
66c96907f2SPeter Ujfalusi u8 carkitl_enabled, carkitr_enabled;
678b3bca29SPeter Ujfalusi u8 ctl_cache[TWL4030_REG_PRECKR_CTL - TWL4030_REG_EAR_CTL + 1];
6801ea6ba2SPeter Ujfalusi
697adadfb0SPeter Ujfalusi struct twl4030_board_params *board_params;
707393958fSPeter Ujfalusi };
717393958fSPeter Ujfalusi
tw4030_init_ctl_cache(struct twl4030_priv * twl4030)728b3bca29SPeter Ujfalusi static void tw4030_init_ctl_cache(struct twl4030_priv *twl4030)
738b3bca29SPeter Ujfalusi {
748b3bca29SPeter Ujfalusi int i;
758b3bca29SPeter Ujfalusi u8 byte;
768b3bca29SPeter Ujfalusi
778b3bca29SPeter Ujfalusi for (i = TWL4030_REG_EAR_CTL; i <= TWL4030_REG_PRECKR_CTL; i++) {
788b3bca29SPeter Ujfalusi twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte, i);
798b3bca29SPeter Ujfalusi twl4030->ctl_cache[i - TWL4030_REG_EAR_CTL] = byte;
808b3bca29SPeter Ujfalusi }
818b3bca29SPeter Ujfalusi }
828b3bca29SPeter Ujfalusi
twl4030_read(struct snd_soc_component * component,unsigned int reg)83c68e7f5bSKuninori Morimoto static unsigned int twl4030_read(struct snd_soc_component *component, unsigned int reg)
84efc8acffSPeter Ujfalusi {
85c68e7f5bSKuninori Morimoto struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
86efc8acffSPeter Ujfalusi u8 value = 0;
87cc17557eSSteve Sakoman
8891432e97SIan Molton if (reg >= TWL4030_CACHEREGNUM)
8991432e97SIan Molton return -EIO;
9091432e97SIan Molton
91efc8acffSPeter Ujfalusi switch (reg) {
92efc8acffSPeter Ujfalusi case TWL4030_REG_EAR_CTL:
93efc8acffSPeter Ujfalusi case TWL4030_REG_PREDL_CTL:
94efc8acffSPeter Ujfalusi case TWL4030_REG_PREDR_CTL:
95efc8acffSPeter Ujfalusi case TWL4030_REG_PRECKL_CTL:
96efc8acffSPeter Ujfalusi case TWL4030_REG_PRECKR_CTL:
97efc8acffSPeter Ujfalusi case TWL4030_REG_HS_GAIN_SET:
98efc8acffSPeter Ujfalusi value = twl4030->ctl_cache[reg - TWL4030_REG_EAR_CTL];
99efc8acffSPeter Ujfalusi break;
100efc8acffSPeter Ujfalusi default:
101efc8acffSPeter Ujfalusi twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &value, reg);
102efc8acffSPeter Ujfalusi break;
103cc17557eSSteve Sakoman }
104cc17557eSSteve Sakoman
105efc8acffSPeter Ujfalusi return value;
106cc17557eSSteve Sakoman }
107cc17557eSSteve Sakoman
twl4030_can_write_to_chip(struct twl4030_priv * twl4030,unsigned int reg)108b703b504SPeter Ujfalusi static bool twl4030_can_write_to_chip(struct twl4030_priv *twl4030,
109a8fc415cSPeter Ujfalusi unsigned int reg)
110a8fc415cSPeter Ujfalusi {
111a8fc415cSPeter Ujfalusi bool write_to_reg = false;
112a8fc415cSPeter Ujfalusi
113a8fc415cSPeter Ujfalusi /* Decide if the given register can be written */
114a8fc415cSPeter Ujfalusi switch (reg) {
115a8fc415cSPeter Ujfalusi case TWL4030_REG_EAR_CTL:
116a8fc415cSPeter Ujfalusi if (twl4030->earpiece_enabled)
117a8fc415cSPeter Ujfalusi write_to_reg = true;
118a8fc415cSPeter Ujfalusi break;
119a8fc415cSPeter Ujfalusi case TWL4030_REG_PREDL_CTL:
120a8fc415cSPeter Ujfalusi if (twl4030->predrivel_enabled)
121a8fc415cSPeter Ujfalusi write_to_reg = true;
122a8fc415cSPeter Ujfalusi break;
123a8fc415cSPeter Ujfalusi case TWL4030_REG_PREDR_CTL:
124a8fc415cSPeter Ujfalusi if (twl4030->predriver_enabled)
125a8fc415cSPeter Ujfalusi write_to_reg = true;
126a8fc415cSPeter Ujfalusi break;
127a8fc415cSPeter Ujfalusi case TWL4030_REG_PRECKL_CTL:
128a8fc415cSPeter Ujfalusi if (twl4030->carkitl_enabled)
129a8fc415cSPeter Ujfalusi write_to_reg = true;
130a8fc415cSPeter Ujfalusi break;
131a8fc415cSPeter Ujfalusi case TWL4030_REG_PRECKR_CTL:
132a8fc415cSPeter Ujfalusi if (twl4030->carkitr_enabled)
133a8fc415cSPeter Ujfalusi write_to_reg = true;
134a8fc415cSPeter Ujfalusi break;
135a8fc415cSPeter Ujfalusi case TWL4030_REG_HS_GAIN_SET:
136a8fc415cSPeter Ujfalusi if (twl4030->hsl_enabled || twl4030->hsr_enabled)
137a8fc415cSPeter Ujfalusi write_to_reg = true;
138a8fc415cSPeter Ujfalusi break;
139a8fc415cSPeter Ujfalusi default:
140a8fc415cSPeter Ujfalusi /* All other register can be written */
141a8fc415cSPeter Ujfalusi write_to_reg = true;
142a8fc415cSPeter Ujfalusi break;
143a8fc415cSPeter Ujfalusi }
144a8fc415cSPeter Ujfalusi
145a8fc415cSPeter Ujfalusi return write_to_reg;
146a8fc415cSPeter Ujfalusi }
147a8fc415cSPeter Ujfalusi
twl4030_write(struct snd_soc_component * component,unsigned int reg,unsigned int value)148c68e7f5bSKuninori Morimoto static int twl4030_write(struct snd_soc_component *component, unsigned int reg,
1497ded5fe0SPeter Ujfalusi unsigned int value)
150cc17557eSSteve Sakoman {
151c68e7f5bSKuninori Morimoto struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
152a450aa6fSPeter Ujfalusi
153a450aa6fSPeter Ujfalusi /* Update the ctl cache */
154a450aa6fSPeter Ujfalusi switch (reg) {
155a450aa6fSPeter Ujfalusi case TWL4030_REG_EAR_CTL:
156a450aa6fSPeter Ujfalusi case TWL4030_REG_PREDL_CTL:
157a450aa6fSPeter Ujfalusi case TWL4030_REG_PREDR_CTL:
158a450aa6fSPeter Ujfalusi case TWL4030_REG_PRECKL_CTL:
159a450aa6fSPeter Ujfalusi case TWL4030_REG_PRECKR_CTL:
160a450aa6fSPeter Ujfalusi case TWL4030_REG_HS_GAIN_SET:
161a450aa6fSPeter Ujfalusi twl4030->ctl_cache[reg - TWL4030_REG_EAR_CTL] = value;
162a450aa6fSPeter Ujfalusi break;
163a450aa6fSPeter Ujfalusi default:
164a450aa6fSPeter Ujfalusi break;
165a450aa6fSPeter Ujfalusi }
166a450aa6fSPeter Ujfalusi
167b703b504SPeter Ujfalusi if (twl4030_can_write_to_chip(twl4030, reg))
168a8fc415cSPeter Ujfalusi return twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, value, reg);
169052901f4SLars-Peter Clausen
170f3b5d300SPeter Ujfalusi return 0;
171cc17557eSSteve Sakoman }
172cc17557eSSteve Sakoman
twl4030_wait_ms(int time)1737e6120c5SPeter Ujfalusi static inline void twl4030_wait_ms(int time)
1747e6120c5SPeter Ujfalusi {
1757e6120c5SPeter Ujfalusi if (time < 60) {
1767e6120c5SPeter Ujfalusi time *= 1000;
1777e6120c5SPeter Ujfalusi usleep_range(time, time + 500);
1787e6120c5SPeter Ujfalusi } else {
1797e6120c5SPeter Ujfalusi msleep(time);
1807e6120c5SPeter Ujfalusi }
1817e6120c5SPeter Ujfalusi }
1827e6120c5SPeter Ujfalusi
twl4030_codec_enable(struct snd_soc_component * component,int enable)183c68e7f5bSKuninori Morimoto static void twl4030_codec_enable(struct snd_soc_component *component, int enable)
184cc17557eSSteve Sakoman {
185c68e7f5bSKuninori Morimoto struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
1867a1fecf5SPeter Ujfalusi int mode;
187cc17557eSSteve Sakoman
1887393958fSPeter Ujfalusi if (enable == twl4030->codec_powered)
1897393958fSPeter Ujfalusi return;
1907393958fSPeter Ujfalusi
191db04e2c5SPeter Ujfalusi if (enable)
19257fe7251SPeter Ujfalusi mode = twl4030_audio_enable_resource(TWL4030_AUDIO_RES_POWER);
193db04e2c5SPeter Ujfalusi else
19457fe7251SPeter Ujfalusi mode = twl4030_audio_disable_resource(TWL4030_AUDIO_RES_POWER);
195cc17557eSSteve Sakoman
196efc8acffSPeter Ujfalusi if (mode >= 0)
1977393958fSPeter Ujfalusi twl4030->codec_powered = enable;
198cc17557eSSteve Sakoman
199cc17557eSSteve Sakoman /* REVISIT: this delay is present in TI sample drivers */
200cc17557eSSteve Sakoman /* but there seems to be no TRM requirement for it */
201cc17557eSSteve Sakoman udelay(10);
202cc17557eSSteve Sakoman }
203cc17557eSSteve Sakoman
2047adadfb0SPeter Ujfalusi static void
twl4030_get_board_param_values(struct twl4030_board_params * board_params,struct device_node * node)2057adadfb0SPeter Ujfalusi twl4030_get_board_param_values(struct twl4030_board_params *board_params,
2062d6d649aSPeter Ujfalusi struct device_node *node)
2072d6d649aSPeter Ujfalusi {
2082d6d649aSPeter Ujfalusi int value;
2092d6d649aSPeter Ujfalusi
2107adadfb0SPeter Ujfalusi of_property_read_u32(node, "ti,digimic_delay", &board_params->digimic_delay);
2117adadfb0SPeter Ujfalusi of_property_read_u32(node, "ti,ramp_delay_value", &board_params->ramp_delay_value);
2127adadfb0SPeter Ujfalusi of_property_read_u32(node, "ti,offset_cncl_path", &board_params->offset_cncl_path);
2132d6d649aSPeter Ujfalusi if (!of_property_read_u32(node, "ti,hs_extmute", &value))
2147adadfb0SPeter Ujfalusi board_params->hs_extmute = value;
2152d6d649aSPeter Ujfalusi
2167adadfb0SPeter Ujfalusi board_params->hs_extmute_gpio = of_get_named_gpio(node, "ti,hs_extmute_gpio", 0);
2177adadfb0SPeter Ujfalusi if (gpio_is_valid(board_params->hs_extmute_gpio))
2187adadfb0SPeter Ujfalusi board_params->hs_extmute = 1;
2192d6d649aSPeter Ujfalusi }
2202d6d649aSPeter Ujfalusi
2217adadfb0SPeter Ujfalusi static struct twl4030_board_params*
twl4030_get_board_params(struct snd_soc_component * component)2227adadfb0SPeter Ujfalusi twl4030_get_board_params(struct snd_soc_component *component)
223cc17557eSSteve Sakoman {
2247adadfb0SPeter Ujfalusi struct twl4030_board_params *board_params = NULL;
2252d6d649aSPeter Ujfalusi struct device_node *twl4030_codec_node = NULL;
2262d6d649aSPeter Ujfalusi
227c68e7f5bSKuninori Morimoto twl4030_codec_node = of_get_child_by_name(component->dev->parent->of_node,
2282d6d649aSPeter Ujfalusi "codec");
2292d6d649aSPeter Ujfalusi
2307adadfb0SPeter Ujfalusi if (twl4030_codec_node) {
2317adadfb0SPeter Ujfalusi board_params = devm_kzalloc(component->dev,
2327adadfb0SPeter Ujfalusi sizeof(struct twl4030_board_params),
2332d6d649aSPeter Ujfalusi GFP_KERNEL);
2347adadfb0SPeter Ujfalusi if (!board_params) {
23515f8c5f2SJohan Hovold of_node_put(twl4030_codec_node);
2362d6d649aSPeter Ujfalusi return NULL;
2372d6d649aSPeter Ujfalusi }
2387adadfb0SPeter Ujfalusi twl4030_get_board_param_values(board_params, twl4030_codec_node);
23915f8c5f2SJohan Hovold of_node_put(twl4030_codec_node);
2402d6d649aSPeter Ujfalusi }
2412d6d649aSPeter Ujfalusi
2427adadfb0SPeter Ujfalusi return board_params;
2432d6d649aSPeter Ujfalusi }
2442d6d649aSPeter Ujfalusi
twl4030_init_chip(struct snd_soc_component * component)245c68e7f5bSKuninori Morimoto static void twl4030_init_chip(struct snd_soc_component *component)
2462d6d649aSPeter Ujfalusi {
2477adadfb0SPeter Ujfalusi struct twl4030_board_params *board_params;
248c68e7f5bSKuninori Morimoto struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
249ee4ccac7SPeter Ujfalusi u8 reg, byte;
250ee4ccac7SPeter Ujfalusi int i = 0;
251cc17557eSSteve Sakoman
2527adadfb0SPeter Ujfalusi board_params = twl4030_get_board_params(component);
2532d6d649aSPeter Ujfalusi
2547adadfb0SPeter Ujfalusi if (board_params && board_params->hs_extmute) {
2557adadfb0SPeter Ujfalusi if (gpio_is_valid(board_params->hs_extmute_gpio)) {
256281ecd16SPeter Ujfalusi int ret;
257281ecd16SPeter Ujfalusi
2587adadfb0SPeter Ujfalusi if (!board_params->hs_extmute_gpio)
259c68e7f5bSKuninori Morimoto dev_warn(component->dev,
260281ecd16SPeter Ujfalusi "Extmute GPIO is 0 is this correct?\n");
261281ecd16SPeter Ujfalusi
2627adadfb0SPeter Ujfalusi ret = gpio_request_one(board_params->hs_extmute_gpio,
2635712ded9SPeter Ujfalusi GPIOF_OUT_INIT_LOW,
2645712ded9SPeter Ujfalusi "hs_extmute");
265281ecd16SPeter Ujfalusi if (ret) {
266c68e7f5bSKuninori Morimoto dev_err(component->dev,
2675712ded9SPeter Ujfalusi "Failed to get hs_extmute GPIO\n");
2687adadfb0SPeter Ujfalusi board_params->hs_extmute_gpio = -1;
269281ecd16SPeter Ujfalusi }
2705712ded9SPeter Ujfalusi } else {
2715712ded9SPeter Ujfalusi u8 pin_mux;
2725712ded9SPeter Ujfalusi
2735712ded9SPeter Ujfalusi /* Set TWL4030 GPIO6 as EXTMUTE signal */
2745712ded9SPeter Ujfalusi twl_i2c_read_u8(TWL4030_MODULE_INTBR, &pin_mux,
2755712ded9SPeter Ujfalusi TWL4030_PMBR1_REG);
2765712ded9SPeter Ujfalusi pin_mux &= ~TWL4030_GPIO6_PWM0_MUTE(0x03);
2775712ded9SPeter Ujfalusi pin_mux |= TWL4030_GPIO6_PWM0_MUTE(0x02);
2785712ded9SPeter Ujfalusi twl_i2c_write_u8(TWL4030_MODULE_INTBR, pin_mux,
2795712ded9SPeter Ujfalusi TWL4030_PMBR1_REG);
2805712ded9SPeter Ujfalusi }
281281ecd16SPeter Ujfalusi }
282281ecd16SPeter Ujfalusi
2838b3bca29SPeter Ujfalusi /* Initialize the local ctl register cache */
2848b3bca29SPeter Ujfalusi tw4030_init_ctl_cache(twl4030);
2858b3bca29SPeter Ujfalusi
286ee4ccac7SPeter Ujfalusi /* anti-pop when changing analog gain */
287c68e7f5bSKuninori Morimoto reg = twl4030_read(component, TWL4030_REG_MISC_SET_1);
288c68e7f5bSKuninori Morimoto twl4030_write(component, TWL4030_REG_MISC_SET_1,
289ee4ccac7SPeter Ujfalusi reg | TWL4030_SMOOTH_ANAVOL_EN);
290ee4ccac7SPeter Ujfalusi
291c68e7f5bSKuninori Morimoto twl4030_write(component, TWL4030_REG_OPTION,
292ee4ccac7SPeter Ujfalusi TWL4030_ATXL1_EN | TWL4030_ATXR1_EN |
293ee4ccac7SPeter Ujfalusi TWL4030_ARXL2_EN | TWL4030_ARXR2_EN);
294ee4ccac7SPeter Ujfalusi
2953c36cc68SPeter Ujfalusi /* REG_ARXR2_APGA_CTL reset according to the TRM: 0dB, DA_EN */
296c68e7f5bSKuninori Morimoto twl4030_write(component, TWL4030_REG_ARXR2_APGA_CTL, 0x32);
2973c36cc68SPeter Ujfalusi
298ee4ccac7SPeter Ujfalusi /* Machine dependent setup */
2997adadfb0SPeter Ujfalusi if (!board_params)
300ee4ccac7SPeter Ujfalusi return;
301ee4ccac7SPeter Ujfalusi
3027adadfb0SPeter Ujfalusi twl4030->board_params = board_params;
303ee4ccac7SPeter Ujfalusi
304c68e7f5bSKuninori Morimoto reg = twl4030_read(component, TWL4030_REG_HS_POPN_SET);
305ee4ccac7SPeter Ujfalusi reg &= ~TWL4030_RAMP_DELAY;
3067adadfb0SPeter Ujfalusi reg |= (board_params->ramp_delay_value << 2);
307c68e7f5bSKuninori Morimoto twl4030_write(component, TWL4030_REG_HS_POPN_SET, reg);
308ee4ccac7SPeter Ujfalusi
309ee4ccac7SPeter Ujfalusi /* initiate offset cancellation */
310c68e7f5bSKuninori Morimoto twl4030_codec_enable(component, 1);
311ee4ccac7SPeter Ujfalusi
312c68e7f5bSKuninori Morimoto reg = twl4030_read(component, TWL4030_REG_ANAMICL);
313ee4ccac7SPeter Ujfalusi reg &= ~TWL4030_OFFSET_CNCL_SEL;
3147adadfb0SPeter Ujfalusi reg |= board_params->offset_cncl_path;
315c68e7f5bSKuninori Morimoto twl4030_write(component, TWL4030_REG_ANAMICL,
316ee4ccac7SPeter Ujfalusi reg | TWL4030_CNCL_OFFSET_START);
317ee4ccac7SPeter Ujfalusi
3187e6120c5SPeter Ujfalusi /*
3197e6120c5SPeter Ujfalusi * Wait for offset cancellation to complete.
3207e6120c5SPeter Ujfalusi * Since this takes a while, do not slam the i2c.
3217e6120c5SPeter Ujfalusi * Start polling the status after ~20ms.
3227e6120c5SPeter Ujfalusi */
3237e6120c5SPeter Ujfalusi msleep(20);
324ee4ccac7SPeter Ujfalusi do {
3257e6120c5SPeter Ujfalusi usleep_range(1000, 2000);
326efc8acffSPeter Ujfalusi twl_set_regcache_bypass(TWL4030_MODULE_AUDIO_VOICE, true);
327ee4ccac7SPeter Ujfalusi twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte,
328ee4ccac7SPeter Ujfalusi TWL4030_REG_ANAMICL);
329efc8acffSPeter Ujfalusi twl_set_regcache_bypass(TWL4030_MODULE_AUDIO_VOICE, false);
330ee4ccac7SPeter Ujfalusi } while ((i++ < 100) &&
331ee4ccac7SPeter Ujfalusi ((byte & TWL4030_CNCL_OFFSET_START) ==
332ee4ccac7SPeter Ujfalusi TWL4030_CNCL_OFFSET_START));
333ee4ccac7SPeter Ujfalusi
334c68e7f5bSKuninori Morimoto twl4030_codec_enable(component, 0);
335cc17557eSSteve Sakoman }
336cc17557eSSteve Sakoman
twl4030_apll_enable(struct snd_soc_component * component,int enable)337c68e7f5bSKuninori Morimoto static void twl4030_apll_enable(struct snd_soc_component *component, int enable)
3387393958fSPeter Ujfalusi {
339c68e7f5bSKuninori Morimoto struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
3407393958fSPeter Ujfalusi
3417b4c734eSPeter Ujfalusi if (enable) {
3427b4c734eSPeter Ujfalusi twl4030->apll_enabled++;
3437b4c734eSPeter Ujfalusi if (twl4030->apll_enabled == 1)
344bb17bc78SSachin Kamat twl4030_audio_enable_resource(
34557fe7251SPeter Ujfalusi TWL4030_AUDIO_RES_APLL);
3467b4c734eSPeter Ujfalusi } else {
3477b4c734eSPeter Ujfalusi twl4030->apll_enabled--;
3487b4c734eSPeter Ujfalusi if (!twl4030->apll_enabled)
349bb17bc78SSachin Kamat twl4030_audio_disable_resource(
35057fe7251SPeter Ujfalusi TWL4030_AUDIO_RES_APLL);
3517b4c734eSPeter Ujfalusi }
3527393958fSPeter Ujfalusi }
3537393958fSPeter Ujfalusi
3545e98a464SPeter Ujfalusi /* Earpiece */
3551a787e7aSJoonyoung Shim static const struct snd_kcontrol_new twl4030_dapm_earpiece_controls[] = {
3561a787e7aSJoonyoung Shim SOC_DAPM_SINGLE("Voice", TWL4030_REG_EAR_CTL, 0, 1, 0),
3571a787e7aSJoonyoung Shim SOC_DAPM_SINGLE("AudioL1", TWL4030_REG_EAR_CTL, 1, 1, 0),
3581a787e7aSJoonyoung Shim SOC_DAPM_SINGLE("AudioL2", TWL4030_REG_EAR_CTL, 2, 1, 0),
3591a787e7aSJoonyoung Shim SOC_DAPM_SINGLE("AudioR1", TWL4030_REG_EAR_CTL, 3, 1, 0),
3601a787e7aSJoonyoung Shim };
3615e98a464SPeter Ujfalusi
3622a6f5c58SPeter Ujfalusi /* PreDrive Left */
3631a787e7aSJoonyoung Shim static const struct snd_kcontrol_new twl4030_dapm_predrivel_controls[] = {
3641a787e7aSJoonyoung Shim SOC_DAPM_SINGLE("Voice", TWL4030_REG_PREDL_CTL, 0, 1, 0),
3651a787e7aSJoonyoung Shim SOC_DAPM_SINGLE("AudioL1", TWL4030_REG_PREDL_CTL, 1, 1, 0),
3661a787e7aSJoonyoung Shim SOC_DAPM_SINGLE("AudioL2", TWL4030_REG_PREDL_CTL, 2, 1, 0),
3671a787e7aSJoonyoung Shim SOC_DAPM_SINGLE("AudioR2", TWL4030_REG_PREDL_CTL, 3, 1, 0),
3681a787e7aSJoonyoung Shim };
3692a6f5c58SPeter Ujfalusi
3702a6f5c58SPeter Ujfalusi /* PreDrive Right */
3711a787e7aSJoonyoung Shim static const struct snd_kcontrol_new twl4030_dapm_predriver_controls[] = {
3721a787e7aSJoonyoung Shim SOC_DAPM_SINGLE("Voice", TWL4030_REG_PREDR_CTL, 0, 1, 0),
3731a787e7aSJoonyoung Shim SOC_DAPM_SINGLE("AudioR1", TWL4030_REG_PREDR_CTL, 1, 1, 0),
3741a787e7aSJoonyoung Shim SOC_DAPM_SINGLE("AudioR2", TWL4030_REG_PREDR_CTL, 2, 1, 0),
3751a787e7aSJoonyoung Shim SOC_DAPM_SINGLE("AudioL2", TWL4030_REG_PREDR_CTL, 3, 1, 0),
3761a787e7aSJoonyoung Shim };
3772a6f5c58SPeter Ujfalusi
378dfad21a2SPeter Ujfalusi /* Headset Left */
3791a787e7aSJoonyoung Shim static const struct snd_kcontrol_new twl4030_dapm_hsol_controls[] = {
3801a787e7aSJoonyoung Shim SOC_DAPM_SINGLE("Voice", TWL4030_REG_HS_SEL, 0, 1, 0),
3811a787e7aSJoonyoung Shim SOC_DAPM_SINGLE("AudioL1", TWL4030_REG_HS_SEL, 1, 1, 0),
3821a787e7aSJoonyoung Shim SOC_DAPM_SINGLE("AudioL2", TWL4030_REG_HS_SEL, 2, 1, 0),
3831a787e7aSJoonyoung Shim };
384dfad21a2SPeter Ujfalusi
385dfad21a2SPeter Ujfalusi /* Headset Right */
3861a787e7aSJoonyoung Shim static const struct snd_kcontrol_new twl4030_dapm_hsor_controls[] = {
3871a787e7aSJoonyoung Shim SOC_DAPM_SINGLE("Voice", TWL4030_REG_HS_SEL, 3, 1, 0),
3881a787e7aSJoonyoung Shim SOC_DAPM_SINGLE("AudioR1", TWL4030_REG_HS_SEL, 4, 1, 0),
3891a787e7aSJoonyoung Shim SOC_DAPM_SINGLE("AudioR2", TWL4030_REG_HS_SEL, 5, 1, 0),
3901a787e7aSJoonyoung Shim };
391dfad21a2SPeter Ujfalusi
3925152d8c2SPeter Ujfalusi /* Carkit Left */
3931a787e7aSJoonyoung Shim static const struct snd_kcontrol_new twl4030_dapm_carkitl_controls[] = {
3941a787e7aSJoonyoung Shim SOC_DAPM_SINGLE("Voice", TWL4030_REG_PRECKL_CTL, 0, 1, 0),
3951a787e7aSJoonyoung Shim SOC_DAPM_SINGLE("AudioL1", TWL4030_REG_PRECKL_CTL, 1, 1, 0),
3961a787e7aSJoonyoung Shim SOC_DAPM_SINGLE("AudioL2", TWL4030_REG_PRECKL_CTL, 2, 1, 0),
3971a787e7aSJoonyoung Shim };
3985152d8c2SPeter Ujfalusi
3995152d8c2SPeter Ujfalusi /* Carkit Right */
4001a787e7aSJoonyoung Shim static const struct snd_kcontrol_new twl4030_dapm_carkitr_controls[] = {
4011a787e7aSJoonyoung Shim SOC_DAPM_SINGLE("Voice", TWL4030_REG_PRECKR_CTL, 0, 1, 0),
4021a787e7aSJoonyoung Shim SOC_DAPM_SINGLE("AudioR1", TWL4030_REG_PRECKR_CTL, 1, 1, 0),
4031a787e7aSJoonyoung Shim SOC_DAPM_SINGLE("AudioR2", TWL4030_REG_PRECKR_CTL, 2, 1, 0),
4041a787e7aSJoonyoung Shim };
4055152d8c2SPeter Ujfalusi
406df339804SPeter Ujfalusi /* Handsfree Left */
407df339804SPeter Ujfalusi static const char *twl4030_handsfreel_texts[] =
4081a787e7aSJoonyoung Shim {"Voice", "AudioL1", "AudioL2", "AudioR2"};
409df339804SPeter Ujfalusi
4109f04fba7STakashi Iwai static SOC_ENUM_SINGLE_DECL(twl4030_handsfreel_enum,
4119f04fba7STakashi Iwai TWL4030_REG_HFL_CTL, 0,
412df339804SPeter Ujfalusi twl4030_handsfreel_texts);
413df339804SPeter Ujfalusi
414df339804SPeter Ujfalusi static const struct snd_kcontrol_new twl4030_dapm_handsfreel_control =
415df339804SPeter Ujfalusi SOC_DAPM_ENUM("Route", twl4030_handsfreel_enum);
416df339804SPeter Ujfalusi
4170f89bdcaSPeter Ujfalusi /* Handsfree Left virtual mute */
4180f89bdcaSPeter Ujfalusi static const struct snd_kcontrol_new twl4030_dapm_handsfreelmute_control =
419052901f4SLars-Peter Clausen SOC_DAPM_SINGLE_VIRT("Switch", 1);
4200f89bdcaSPeter Ujfalusi
421df339804SPeter Ujfalusi /* Handsfree Right */
422df339804SPeter Ujfalusi static const char *twl4030_handsfreer_texts[] =
4231a787e7aSJoonyoung Shim {"Voice", "AudioR1", "AudioR2", "AudioL2"};
424df339804SPeter Ujfalusi
4259f04fba7STakashi Iwai static SOC_ENUM_SINGLE_DECL(twl4030_handsfreer_enum,
4269f04fba7STakashi Iwai TWL4030_REG_HFR_CTL, 0,
427df339804SPeter Ujfalusi twl4030_handsfreer_texts);
428df339804SPeter Ujfalusi
429df339804SPeter Ujfalusi static const struct snd_kcontrol_new twl4030_dapm_handsfreer_control =
430df339804SPeter Ujfalusi SOC_DAPM_ENUM("Route", twl4030_handsfreer_enum);
431df339804SPeter Ujfalusi
4320f89bdcaSPeter Ujfalusi /* Handsfree Right virtual mute */
4330f89bdcaSPeter Ujfalusi static const struct snd_kcontrol_new twl4030_dapm_handsfreermute_control =
434052901f4SLars-Peter Clausen SOC_DAPM_SINGLE_VIRT("Switch", 1);
4350f89bdcaSPeter Ujfalusi
436376f7839SPeter Ujfalusi /* Vibra */
437376f7839SPeter Ujfalusi /* Vibra audio path selection */
438376f7839SPeter Ujfalusi static const char *twl4030_vibra_texts[] =
439376f7839SPeter Ujfalusi {"AudioL1", "AudioR1", "AudioL2", "AudioR2"};
440376f7839SPeter Ujfalusi
4419f04fba7STakashi Iwai static SOC_ENUM_SINGLE_DECL(twl4030_vibra_enum,
4429f04fba7STakashi Iwai TWL4030_REG_VIBRA_CTL, 2,
443376f7839SPeter Ujfalusi twl4030_vibra_texts);
444376f7839SPeter Ujfalusi
445376f7839SPeter Ujfalusi static const struct snd_kcontrol_new twl4030_dapm_vibra_control =
446376f7839SPeter Ujfalusi SOC_DAPM_ENUM("Route", twl4030_vibra_enum);
447376f7839SPeter Ujfalusi
448376f7839SPeter Ujfalusi /* Vibra path selection: local vibrator (PWM) or audio driven */
449376f7839SPeter Ujfalusi static const char *twl4030_vibrapath_texts[] =
450376f7839SPeter Ujfalusi {"Local vibrator", "Audio"};
451376f7839SPeter Ujfalusi
4529f04fba7STakashi Iwai static SOC_ENUM_SINGLE_DECL(twl4030_vibrapath_enum,
4539f04fba7STakashi Iwai TWL4030_REG_VIBRA_CTL, 4,
454376f7839SPeter Ujfalusi twl4030_vibrapath_texts);
455376f7839SPeter Ujfalusi
456376f7839SPeter Ujfalusi static const struct snd_kcontrol_new twl4030_dapm_vibrapath_control =
457376f7839SPeter Ujfalusi SOC_DAPM_ENUM("Route", twl4030_vibrapath_enum);
458376f7839SPeter Ujfalusi
459276c6222SPeter Ujfalusi /* Left analog microphone selection */
46097b8096dSJoonyoung Shim static const struct snd_kcontrol_new twl4030_dapm_analoglmic_controls[] = {
4619028935dSPeter Ujfalusi SOC_DAPM_SINGLE("Main Mic Capture Switch",
4629028935dSPeter Ujfalusi TWL4030_REG_ANAMICL, 0, 1, 0),
4639028935dSPeter Ujfalusi SOC_DAPM_SINGLE("Headset Mic Capture Switch",
4649028935dSPeter Ujfalusi TWL4030_REG_ANAMICL, 1, 1, 0),
4659028935dSPeter Ujfalusi SOC_DAPM_SINGLE("AUXL Capture Switch",
4669028935dSPeter Ujfalusi TWL4030_REG_ANAMICL, 2, 1, 0),
4679028935dSPeter Ujfalusi SOC_DAPM_SINGLE("Carkit Mic Capture Switch",
4689028935dSPeter Ujfalusi TWL4030_REG_ANAMICL, 3, 1, 0),
46997b8096dSJoonyoung Shim };
470276c6222SPeter Ujfalusi
471276c6222SPeter Ujfalusi /* Right analog microphone selection */
47297b8096dSJoonyoung Shim static const struct snd_kcontrol_new twl4030_dapm_analogrmic_controls[] = {
4739028935dSPeter Ujfalusi SOC_DAPM_SINGLE("Sub Mic Capture Switch", TWL4030_REG_ANAMICR, 0, 1, 0),
4749028935dSPeter Ujfalusi SOC_DAPM_SINGLE("AUXR Capture Switch", TWL4030_REG_ANAMICR, 2, 1, 0),
47597b8096dSJoonyoung Shim };
476276c6222SPeter Ujfalusi
477276c6222SPeter Ujfalusi /* TX1 L/R Analog/Digital microphone selection */
478276c6222SPeter Ujfalusi static const char *twl4030_micpathtx1_texts[] =
479276c6222SPeter Ujfalusi {"Analog", "Digimic0"};
480276c6222SPeter Ujfalusi
4819f04fba7STakashi Iwai static SOC_ENUM_SINGLE_DECL(twl4030_micpathtx1_enum,
4829f04fba7STakashi Iwai TWL4030_REG_ADCMICSEL, 0,
483276c6222SPeter Ujfalusi twl4030_micpathtx1_texts);
484276c6222SPeter Ujfalusi
485276c6222SPeter Ujfalusi static const struct snd_kcontrol_new twl4030_dapm_micpathtx1_control =
486276c6222SPeter Ujfalusi SOC_DAPM_ENUM("Route", twl4030_micpathtx1_enum);
487276c6222SPeter Ujfalusi
488276c6222SPeter Ujfalusi /* TX2 L/R Analog/Digital microphone selection */
489276c6222SPeter Ujfalusi static const char *twl4030_micpathtx2_texts[] =
490276c6222SPeter Ujfalusi {"Analog", "Digimic1"};
491276c6222SPeter Ujfalusi
4929f04fba7STakashi Iwai static SOC_ENUM_SINGLE_DECL(twl4030_micpathtx2_enum,
4939f04fba7STakashi Iwai TWL4030_REG_ADCMICSEL, 2,
494276c6222SPeter Ujfalusi twl4030_micpathtx2_texts);
495276c6222SPeter Ujfalusi
496276c6222SPeter Ujfalusi static const struct snd_kcontrol_new twl4030_dapm_micpathtx2_control =
497276c6222SPeter Ujfalusi SOC_DAPM_ENUM("Route", twl4030_micpathtx2_enum);
498276c6222SPeter Ujfalusi
4997393958fSPeter Ujfalusi /* Analog bypass for AudioR1 */
5007393958fSPeter Ujfalusi static const struct snd_kcontrol_new twl4030_dapm_abypassr1_control =
5017393958fSPeter Ujfalusi SOC_DAPM_SINGLE("Switch", TWL4030_REG_ARXR1_APGA_CTL, 2, 1, 0);
5027393958fSPeter Ujfalusi
5037393958fSPeter Ujfalusi /* Analog bypass for AudioL1 */
5047393958fSPeter Ujfalusi static const struct snd_kcontrol_new twl4030_dapm_abypassl1_control =
5057393958fSPeter Ujfalusi SOC_DAPM_SINGLE("Switch", TWL4030_REG_ARXL1_APGA_CTL, 2, 1, 0);
5067393958fSPeter Ujfalusi
5077393958fSPeter Ujfalusi /* Analog bypass for AudioR2 */
5087393958fSPeter Ujfalusi static const struct snd_kcontrol_new twl4030_dapm_abypassr2_control =
5097393958fSPeter Ujfalusi SOC_DAPM_SINGLE("Switch", TWL4030_REG_ARXR2_APGA_CTL, 2, 1, 0);
5107393958fSPeter Ujfalusi
5117393958fSPeter Ujfalusi /* Analog bypass for AudioL2 */
5127393958fSPeter Ujfalusi static const struct snd_kcontrol_new twl4030_dapm_abypassl2_control =
5137393958fSPeter Ujfalusi SOC_DAPM_SINGLE("Switch", TWL4030_REG_ARXL2_APGA_CTL, 2, 1, 0);
5147393958fSPeter Ujfalusi
515fcd274a3SLopez Cruz, Misael /* Analog bypass for Voice */
516fcd274a3SLopez Cruz, Misael static const struct snd_kcontrol_new twl4030_dapm_abypassv_control =
517fcd274a3SLopez Cruz, Misael SOC_DAPM_SINGLE("Switch", TWL4030_REG_VDL_APGA_CTL, 2, 1, 0);
518fcd274a3SLopez Cruz, Misael
5198b0d3153SPeter Ujfalusi /* Digital bypass gain, mute instead of -30dB */
5202524911eSLars-Peter Clausen static const DECLARE_TLV_DB_RANGE(twl4030_dapm_dbypass_tlv,
5218b0d3153SPeter Ujfalusi 0, 1, TLV_DB_SCALE_ITEM(-3000, 600, 1),
5228b0d3153SPeter Ujfalusi 2, 3, TLV_DB_SCALE_ITEM(-2400, 0, 0),
5232524911eSLars-Peter Clausen 4, 7, TLV_DB_SCALE_ITEM(-1800, 600, 0)
5242524911eSLars-Peter Clausen );
5256bab83fdSPeter Ujfalusi
5266bab83fdSPeter Ujfalusi /* Digital bypass left (TX1L -> RX2L) */
5276bab83fdSPeter Ujfalusi static const struct snd_kcontrol_new twl4030_dapm_dbypassl_control =
5286bab83fdSPeter Ujfalusi SOC_DAPM_SINGLE_TLV("Volume",
5296bab83fdSPeter Ujfalusi TWL4030_REG_ATX2ARXPGA, 3, 7, 0,
5306bab83fdSPeter Ujfalusi twl4030_dapm_dbypass_tlv);
5316bab83fdSPeter Ujfalusi
5326bab83fdSPeter Ujfalusi /* Digital bypass right (TX1R -> RX2R) */
5336bab83fdSPeter Ujfalusi static const struct snd_kcontrol_new twl4030_dapm_dbypassr_control =
5346bab83fdSPeter Ujfalusi SOC_DAPM_SINGLE_TLV("Volume",
5356bab83fdSPeter Ujfalusi TWL4030_REG_ATX2ARXPGA, 0, 7, 0,
5366bab83fdSPeter Ujfalusi twl4030_dapm_dbypass_tlv);
5376bab83fdSPeter Ujfalusi
538ee8f6894SLopez Cruz, Misael /*
539ee8f6894SLopez Cruz, Misael * Voice Sidetone GAIN volume control:
540ee8f6894SLopez Cruz, Misael * from -51 to -10 dB in 1 dB steps (mute instead of -51 dB)
541ee8f6894SLopez Cruz, Misael */
542ee8f6894SLopez Cruz, Misael static DECLARE_TLV_DB_SCALE(twl4030_dapm_dbypassv_tlv, -5100, 100, 1);
543ee8f6894SLopez Cruz, Misael
544ee8f6894SLopez Cruz, Misael /* Digital bypass voice: sidetone (VUL -> VDL)*/
545ee8f6894SLopez Cruz, Misael static const struct snd_kcontrol_new twl4030_dapm_dbypassv_control =
546ee8f6894SLopez Cruz, Misael SOC_DAPM_SINGLE_TLV("Volume",
547ee8f6894SLopez Cruz, Misael TWL4030_REG_VSTPGA, 0, 0x29, 0,
548ee8f6894SLopez Cruz, Misael twl4030_dapm_dbypassv_tlv);
549ee8f6894SLopez Cruz, Misael
5509008adf9SPeter Ujfalusi /*
5519008adf9SPeter Ujfalusi * Output PGA builder:
5529008adf9SPeter Ujfalusi * Handle the muting and unmuting of the given output (turning off the
5539008adf9SPeter Ujfalusi * amplifier associated with the output pin)
554c96907f2SPeter Ujfalusi * On mute bypass the reg_cache and write 0 to the register
555c96907f2SPeter Ujfalusi * On unmute: restore the register content from the reg_cache
5569008adf9SPeter Ujfalusi * Outputs handled in this way: Earpiece, PreDrivL/R, CarkitL/R
5579008adf9SPeter Ujfalusi */
5589008adf9SPeter Ujfalusi #define TWL4030_OUTPUT_PGA(pin_name, reg, mask) \
5599008adf9SPeter Ujfalusi static int pin_name##pga_event(struct snd_soc_dapm_widget *w, \
5609008adf9SPeter Ujfalusi struct snd_kcontrol *kcontrol, int event) \
5619008adf9SPeter Ujfalusi { \
562c68e7f5bSKuninori Morimoto struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); \
563c68e7f5bSKuninori Morimoto struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component); \
5649008adf9SPeter Ujfalusi \
5659008adf9SPeter Ujfalusi switch (event) { \
5669008adf9SPeter Ujfalusi case SND_SOC_DAPM_POST_PMU: \
567c96907f2SPeter Ujfalusi twl4030->pin_name##_enabled = 1; \
568c68e7f5bSKuninori Morimoto twl4030_write(component, reg, twl4030_read(component, reg)); \
5699008adf9SPeter Ujfalusi break; \
5709008adf9SPeter Ujfalusi case SND_SOC_DAPM_POST_PMD: \
571c96907f2SPeter Ujfalusi twl4030->pin_name##_enabled = 0; \
5727ded5fe0SPeter Ujfalusi twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, 0, reg); \
5739008adf9SPeter Ujfalusi break; \
5749008adf9SPeter Ujfalusi } \
5759008adf9SPeter Ujfalusi return 0; \
5769008adf9SPeter Ujfalusi }
5779008adf9SPeter Ujfalusi
5789008adf9SPeter Ujfalusi TWL4030_OUTPUT_PGA(earpiece, TWL4030_REG_EAR_CTL, TWL4030_EAR_GAIN);
5799008adf9SPeter Ujfalusi TWL4030_OUTPUT_PGA(predrivel, TWL4030_REG_PREDL_CTL, TWL4030_PREDL_GAIN);
5809008adf9SPeter Ujfalusi TWL4030_OUTPUT_PGA(predriver, TWL4030_REG_PREDR_CTL, TWL4030_PREDR_GAIN);
5819008adf9SPeter Ujfalusi TWL4030_OUTPUT_PGA(carkitl, TWL4030_REG_PRECKL_CTL, TWL4030_PRECKL_GAIN);
5829008adf9SPeter Ujfalusi TWL4030_OUTPUT_PGA(carkitr, TWL4030_REG_PRECKR_CTL, TWL4030_PRECKR_GAIN);
5839008adf9SPeter Ujfalusi
handsfree_ramp(struct snd_soc_component * component,int reg,int ramp)584c68e7f5bSKuninori Morimoto static void handsfree_ramp(struct snd_soc_component *component, int reg, int ramp)
58549d92c7dSStanley.Miao {
58649d92c7dSStanley.Miao unsigned char hs_ctl;
58749d92c7dSStanley.Miao
588c68e7f5bSKuninori Morimoto hs_ctl = twl4030_read(component, reg);
58949d92c7dSStanley.Miao
5905a2e9a48SPeter Ujfalusi if (ramp) {
5915a2e9a48SPeter Ujfalusi /* HF ramp-up */
5925a2e9a48SPeter Ujfalusi hs_ctl |= TWL4030_HF_CTL_REF_EN;
593c68e7f5bSKuninori Morimoto twl4030_write(component, reg, hs_ctl);
5945a2e9a48SPeter Ujfalusi udelay(10);
59549d92c7dSStanley.Miao hs_ctl |= TWL4030_HF_CTL_RAMP_EN;
596c68e7f5bSKuninori Morimoto twl4030_write(component, reg, hs_ctl);
5975a2e9a48SPeter Ujfalusi udelay(40);
59849d92c7dSStanley.Miao hs_ctl |= TWL4030_HF_CTL_LOOP_EN;
59949d92c7dSStanley.Miao hs_ctl |= TWL4030_HF_CTL_HB_EN;
600c68e7f5bSKuninori Morimoto twl4030_write(component, reg, hs_ctl);
60149d92c7dSStanley.Miao } else {
6025a2e9a48SPeter Ujfalusi /* HF ramp-down */
6035a2e9a48SPeter Ujfalusi hs_ctl &= ~TWL4030_HF_CTL_LOOP_EN;
6045a2e9a48SPeter Ujfalusi hs_ctl &= ~TWL4030_HF_CTL_HB_EN;
605c68e7f5bSKuninori Morimoto twl4030_write(component, reg, hs_ctl);
6065a2e9a48SPeter Ujfalusi hs_ctl &= ~TWL4030_HF_CTL_RAMP_EN;
607c68e7f5bSKuninori Morimoto twl4030_write(component, reg, hs_ctl);
6085a2e9a48SPeter Ujfalusi udelay(40);
6095a2e9a48SPeter Ujfalusi hs_ctl &= ~TWL4030_HF_CTL_REF_EN;
610c68e7f5bSKuninori Morimoto twl4030_write(component, reg, hs_ctl);
6115a2e9a48SPeter Ujfalusi }
61249d92c7dSStanley.Miao }
61349d92c7dSStanley.Miao
handsfreelpga_event(struct snd_soc_dapm_widget * w,struct snd_kcontrol * kcontrol,int event)6145a2e9a48SPeter Ujfalusi static int handsfreelpga_event(struct snd_soc_dapm_widget *w,
6155a2e9a48SPeter Ujfalusi struct snd_kcontrol *kcontrol, int event)
6165a2e9a48SPeter Ujfalusi {
617c68e7f5bSKuninori Morimoto struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
618a36ac9b3SLars-Peter Clausen
6195a2e9a48SPeter Ujfalusi switch (event) {
6205a2e9a48SPeter Ujfalusi case SND_SOC_DAPM_POST_PMU:
621c68e7f5bSKuninori Morimoto handsfree_ramp(component, TWL4030_REG_HFL_CTL, 1);
6225a2e9a48SPeter Ujfalusi break;
6235a2e9a48SPeter Ujfalusi case SND_SOC_DAPM_POST_PMD:
624c68e7f5bSKuninori Morimoto handsfree_ramp(component, TWL4030_REG_HFL_CTL, 0);
6255a2e9a48SPeter Ujfalusi break;
6265a2e9a48SPeter Ujfalusi }
6275a2e9a48SPeter Ujfalusi return 0;
6285a2e9a48SPeter Ujfalusi }
6295a2e9a48SPeter Ujfalusi
handsfreerpga_event(struct snd_soc_dapm_widget * w,struct snd_kcontrol * kcontrol,int event)6305a2e9a48SPeter Ujfalusi static int handsfreerpga_event(struct snd_soc_dapm_widget *w,
6315a2e9a48SPeter Ujfalusi struct snd_kcontrol *kcontrol, int event)
6325a2e9a48SPeter Ujfalusi {
633c68e7f5bSKuninori Morimoto struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
634a36ac9b3SLars-Peter Clausen
6355a2e9a48SPeter Ujfalusi switch (event) {
6365a2e9a48SPeter Ujfalusi case SND_SOC_DAPM_POST_PMU:
637c68e7f5bSKuninori Morimoto handsfree_ramp(component, TWL4030_REG_HFR_CTL, 1);
6385a2e9a48SPeter Ujfalusi break;
6395a2e9a48SPeter Ujfalusi case SND_SOC_DAPM_POST_PMD:
640c68e7f5bSKuninori Morimoto handsfree_ramp(component, TWL4030_REG_HFR_CTL, 0);
6415a2e9a48SPeter Ujfalusi break;
6425a2e9a48SPeter Ujfalusi }
64349d92c7dSStanley.Miao return 0;
64449d92c7dSStanley.Miao }
64549d92c7dSStanley.Miao
vibramux_event(struct snd_soc_dapm_widget * w,struct snd_kcontrol * kcontrol,int event)64686139a13SJari Vanhala static int vibramux_event(struct snd_soc_dapm_widget *w,
64786139a13SJari Vanhala struct snd_kcontrol *kcontrol, int event)
64886139a13SJari Vanhala {
649c68e7f5bSKuninori Morimoto struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
650a36ac9b3SLars-Peter Clausen
651c68e7f5bSKuninori Morimoto twl4030_write(component, TWL4030_REG_VIBRA_SET, 0xff);
65286139a13SJari Vanhala return 0;
65386139a13SJari Vanhala }
65486139a13SJari Vanhala
apll_event(struct snd_soc_dapm_widget * w,struct snd_kcontrol * kcontrol,int event)6557729cf74SPeter Ujfalusi static int apll_event(struct snd_soc_dapm_widget *w,
6567729cf74SPeter Ujfalusi struct snd_kcontrol *kcontrol, int event)
6577729cf74SPeter Ujfalusi {
658c68e7f5bSKuninori Morimoto struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
659a36ac9b3SLars-Peter Clausen
6607729cf74SPeter Ujfalusi switch (event) {
6617729cf74SPeter Ujfalusi case SND_SOC_DAPM_PRE_PMU:
662c68e7f5bSKuninori Morimoto twl4030_apll_enable(component, 1);
6637729cf74SPeter Ujfalusi break;
6647729cf74SPeter Ujfalusi case SND_SOC_DAPM_POST_PMD:
665c68e7f5bSKuninori Morimoto twl4030_apll_enable(component, 0);
6667729cf74SPeter Ujfalusi break;
6677729cf74SPeter Ujfalusi }
6687729cf74SPeter Ujfalusi return 0;
6697729cf74SPeter Ujfalusi }
6707729cf74SPeter Ujfalusi
aif_event(struct snd_soc_dapm_widget * w,struct snd_kcontrol * kcontrol,int event)6717b4c734eSPeter Ujfalusi static int aif_event(struct snd_soc_dapm_widget *w,
6727b4c734eSPeter Ujfalusi struct snd_kcontrol *kcontrol, int event)
6737b4c734eSPeter Ujfalusi {
674c68e7f5bSKuninori Morimoto struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
6757b4c734eSPeter Ujfalusi u8 audio_if;
6767b4c734eSPeter Ujfalusi
677c68e7f5bSKuninori Morimoto audio_if = twl4030_read(component, TWL4030_REG_AUDIO_IF);
6787b4c734eSPeter Ujfalusi switch (event) {
6797b4c734eSPeter Ujfalusi case SND_SOC_DAPM_PRE_PMU:
6807b4c734eSPeter Ujfalusi /* Enable AIF */
6817b4c734eSPeter Ujfalusi /* enable the PLL before we use it to clock the DAI */
682c68e7f5bSKuninori Morimoto twl4030_apll_enable(component, 1);
6837b4c734eSPeter Ujfalusi
684c68e7f5bSKuninori Morimoto twl4030_write(component, TWL4030_REG_AUDIO_IF,
6857b4c734eSPeter Ujfalusi audio_if | TWL4030_AIF_EN);
6867b4c734eSPeter Ujfalusi break;
6877b4c734eSPeter Ujfalusi case SND_SOC_DAPM_POST_PMD:
6887b4c734eSPeter Ujfalusi /* disable the DAI before we stop it's source PLL */
689c68e7f5bSKuninori Morimoto twl4030_write(component, TWL4030_REG_AUDIO_IF,
6907b4c734eSPeter Ujfalusi audio_if & ~TWL4030_AIF_EN);
691c68e7f5bSKuninori Morimoto twl4030_apll_enable(component, 0);
6927b4c734eSPeter Ujfalusi break;
6937b4c734eSPeter Ujfalusi }
6947b4c734eSPeter Ujfalusi return 0;
6957b4c734eSPeter Ujfalusi }
6967b4c734eSPeter Ujfalusi
headset_ramp(struct snd_soc_component * component,int ramp)697c68e7f5bSKuninori Morimoto static void headset_ramp(struct snd_soc_component *component, int ramp)
698aad749e5SPeter Ujfalusi {
699aad749e5SPeter Ujfalusi unsigned char hs_gain, hs_pop;
700c68e7f5bSKuninori Morimoto struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
7017adadfb0SPeter Ujfalusi struct twl4030_board_params *board_params = twl4030->board_params;
7026943c92eSPeter Ujfalusi /* Base values for ramp delay calculation: 2^19 - 2^26 */
703*67860d2aSColin Ian King static const unsigned int ramp_base[] = {
704*67860d2aSColin Ian King 524288, 1048576, 2097152, 4194304,
705*67860d2aSColin Ian King 8388608, 16777216, 33554432, 67108864
706*67860d2aSColin Ian King };
7077e6120c5SPeter Ujfalusi unsigned int delay;
708aad749e5SPeter Ujfalusi
709c68e7f5bSKuninori Morimoto hs_gain = twl4030_read(component, TWL4030_REG_HS_GAIN_SET);
710c68e7f5bSKuninori Morimoto hs_pop = twl4030_read(component, TWL4030_REG_HS_POPN_SET);
7117e6120c5SPeter Ujfalusi delay = (ramp_base[(hs_pop & TWL4030_RAMP_DELAY) >> 2] /
7127e6120c5SPeter Ujfalusi twl4030->sysclk) + 1;
713aad749e5SPeter Ujfalusi
7144e49ffd1SCandelaria Villareal, Jorge /* Enable external mute control, this dramatically reduces
7154e49ffd1SCandelaria Villareal, Jorge * the pop-noise */
7167adadfb0SPeter Ujfalusi if (board_params && board_params->hs_extmute) {
7177adadfb0SPeter Ujfalusi if (gpio_is_valid(board_params->hs_extmute_gpio)) {
7187adadfb0SPeter Ujfalusi gpio_set_value(board_params->hs_extmute_gpio, 1);
7194e49ffd1SCandelaria Villareal, Jorge } else {
7204e49ffd1SCandelaria Villareal, Jorge hs_pop |= TWL4030_EXTMUTE;
721c68e7f5bSKuninori Morimoto twl4030_write(component, TWL4030_REG_HS_POPN_SET, hs_pop);
7224e49ffd1SCandelaria Villareal, Jorge }
7234e49ffd1SCandelaria Villareal, Jorge }
7244e49ffd1SCandelaria Villareal, Jorge
7256943c92eSPeter Ujfalusi if (ramp) {
7266943c92eSPeter Ujfalusi /* Headset ramp-up according to the TRM */
727aad749e5SPeter Ujfalusi hs_pop |= TWL4030_VMID_EN;
728c68e7f5bSKuninori Morimoto twl4030_write(component, TWL4030_REG_HS_POPN_SET, hs_pop);
729c96907f2SPeter Ujfalusi /* Actually write to the register */
7307ded5fe0SPeter Ujfalusi twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, hs_gain,
731c96907f2SPeter Ujfalusi TWL4030_REG_HS_GAIN_SET);
732aad749e5SPeter Ujfalusi hs_pop |= TWL4030_RAMP_EN;
733c68e7f5bSKuninori Morimoto twl4030_write(component, TWL4030_REG_HS_POPN_SET, hs_pop);
7344e49ffd1SCandelaria Villareal, Jorge /* Wait ramp delay time + 1, so the VMID can settle */
7357e6120c5SPeter Ujfalusi twl4030_wait_ms(delay);
7366943c92eSPeter Ujfalusi } else {
7376943c92eSPeter Ujfalusi /* Headset ramp-down _not_ according to
7386943c92eSPeter Ujfalusi * the TRM, but in a way that it is working */
739aad749e5SPeter Ujfalusi hs_pop &= ~TWL4030_RAMP_EN;
740c68e7f5bSKuninori Morimoto twl4030_write(component, TWL4030_REG_HS_POPN_SET, hs_pop);
7416943c92eSPeter Ujfalusi /* Wait ramp delay time + 1, so the VMID can settle */
7427e6120c5SPeter Ujfalusi twl4030_wait_ms(delay);
743aad749e5SPeter Ujfalusi /* Bypass the reg_cache to mute the headset */
7447ded5fe0SPeter Ujfalusi twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, hs_gain & (~0x0f),
745aad749e5SPeter Ujfalusi TWL4030_REG_HS_GAIN_SET);
7466943c92eSPeter Ujfalusi
747aad749e5SPeter Ujfalusi hs_pop &= ~TWL4030_VMID_EN;
748c68e7f5bSKuninori Morimoto twl4030_write(component, TWL4030_REG_HS_POPN_SET, hs_pop);
7496943c92eSPeter Ujfalusi }
7504e49ffd1SCandelaria Villareal, Jorge
7514e49ffd1SCandelaria Villareal, Jorge /* Disable external mute */
7527adadfb0SPeter Ujfalusi if (board_params && board_params->hs_extmute) {
7537adadfb0SPeter Ujfalusi if (gpio_is_valid(board_params->hs_extmute_gpio)) {
7547adadfb0SPeter Ujfalusi gpio_set_value(board_params->hs_extmute_gpio, 0);
7554e49ffd1SCandelaria Villareal, Jorge } else {
7564e49ffd1SCandelaria Villareal, Jorge hs_pop &= ~TWL4030_EXTMUTE;
757c68e7f5bSKuninori Morimoto twl4030_write(component, TWL4030_REG_HS_POPN_SET, hs_pop);
7584e49ffd1SCandelaria Villareal, Jorge }
7594e49ffd1SCandelaria Villareal, Jorge }
7606943c92eSPeter Ujfalusi }
7616943c92eSPeter Ujfalusi
headsetlpga_event(struct snd_soc_dapm_widget * w,struct snd_kcontrol * kcontrol,int event)7626943c92eSPeter Ujfalusi static int headsetlpga_event(struct snd_soc_dapm_widget *w,
7636943c92eSPeter Ujfalusi struct snd_kcontrol *kcontrol, int event)
7646943c92eSPeter Ujfalusi {
765c68e7f5bSKuninori Morimoto struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
766c68e7f5bSKuninori Morimoto struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
7676943c92eSPeter Ujfalusi
7686943c92eSPeter Ujfalusi switch (event) {
7696943c92eSPeter Ujfalusi case SND_SOC_DAPM_POST_PMU:
7706943c92eSPeter Ujfalusi /* Do the ramp-up only once */
7716943c92eSPeter Ujfalusi if (!twl4030->hsr_enabled)
772c68e7f5bSKuninori Morimoto headset_ramp(component, 1);
7736943c92eSPeter Ujfalusi
7746943c92eSPeter Ujfalusi twl4030->hsl_enabled = 1;
7756943c92eSPeter Ujfalusi break;
7766943c92eSPeter Ujfalusi case SND_SOC_DAPM_POST_PMD:
7776943c92eSPeter Ujfalusi /* Do the ramp-down only if both headsetL/R is disabled */
7786943c92eSPeter Ujfalusi if (!twl4030->hsr_enabled)
779c68e7f5bSKuninori Morimoto headset_ramp(component, 0);
7806943c92eSPeter Ujfalusi
7816943c92eSPeter Ujfalusi twl4030->hsl_enabled = 0;
7826943c92eSPeter Ujfalusi break;
7836943c92eSPeter Ujfalusi }
7846943c92eSPeter Ujfalusi return 0;
7856943c92eSPeter Ujfalusi }
7866943c92eSPeter Ujfalusi
headsetrpga_event(struct snd_soc_dapm_widget * w,struct snd_kcontrol * kcontrol,int event)7876943c92eSPeter Ujfalusi static int headsetrpga_event(struct snd_soc_dapm_widget *w,
7886943c92eSPeter Ujfalusi struct snd_kcontrol *kcontrol, int event)
7896943c92eSPeter Ujfalusi {
790c68e7f5bSKuninori Morimoto struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
791c68e7f5bSKuninori Morimoto struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
7926943c92eSPeter Ujfalusi
7936943c92eSPeter Ujfalusi switch (event) {
7946943c92eSPeter Ujfalusi case SND_SOC_DAPM_POST_PMU:
7956943c92eSPeter Ujfalusi /* Do the ramp-up only once */
7966943c92eSPeter Ujfalusi if (!twl4030->hsl_enabled)
797c68e7f5bSKuninori Morimoto headset_ramp(component, 1);
7986943c92eSPeter Ujfalusi
7996943c92eSPeter Ujfalusi twl4030->hsr_enabled = 1;
8006943c92eSPeter Ujfalusi break;
8016943c92eSPeter Ujfalusi case SND_SOC_DAPM_POST_PMD:
8026943c92eSPeter Ujfalusi /* Do the ramp-down only if both headsetL/R is disabled */
8036943c92eSPeter Ujfalusi if (!twl4030->hsl_enabled)
804c68e7f5bSKuninori Morimoto headset_ramp(component, 0);
8056943c92eSPeter Ujfalusi
8066943c92eSPeter Ujfalusi twl4030->hsr_enabled = 0;
807aad749e5SPeter Ujfalusi break;
808aad749e5SPeter Ujfalusi }
809aad749e5SPeter Ujfalusi return 0;
810aad749e5SPeter Ujfalusi }
811aad749e5SPeter Ujfalusi
digimic_event(struct snd_soc_dapm_widget * w,struct snd_kcontrol * kcontrol,int event)81201ea6ba2SPeter Ujfalusi static int digimic_event(struct snd_soc_dapm_widget *w,
81301ea6ba2SPeter Ujfalusi struct snd_kcontrol *kcontrol, int event)
81401ea6ba2SPeter Ujfalusi {
815c68e7f5bSKuninori Morimoto struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
816c68e7f5bSKuninori Morimoto struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
8177adadfb0SPeter Ujfalusi struct twl4030_board_params *board_params = twl4030->board_params;
81801ea6ba2SPeter Ujfalusi
8197adadfb0SPeter Ujfalusi if (board_params && board_params->digimic_delay)
8207adadfb0SPeter Ujfalusi twl4030_wait_ms(board_params->digimic_delay);
82101ea6ba2SPeter Ujfalusi return 0;
82201ea6ba2SPeter Ujfalusi }
82301ea6ba2SPeter Ujfalusi
824c10b82cfSPeter Ujfalusi /*
825b0bd53a7SPeter Ujfalusi * Some of the gain controls in TWL (mostly those which are associated with
826b0bd53a7SPeter Ujfalusi * the outputs) are implemented in an interesting way:
827b0bd53a7SPeter Ujfalusi * 0x0 : Power down (mute)
828b0bd53a7SPeter Ujfalusi * 0x1 : 6dB
829b0bd53a7SPeter Ujfalusi * 0x2 : 0 dB
830b0bd53a7SPeter Ujfalusi * 0x3 : -6 dB
831b0bd53a7SPeter Ujfalusi * Inverting not going to help with these.
832b0bd53a7SPeter Ujfalusi * Custom volsw and volsw_2r get/put functions to handle these gain bits.
833b0bd53a7SPeter Ujfalusi */
snd_soc_get_volsw_twl4030(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)834b0bd53a7SPeter Ujfalusi static int snd_soc_get_volsw_twl4030(struct snd_kcontrol *kcontrol,
835b0bd53a7SPeter Ujfalusi struct snd_ctl_elem_value *ucontrol)
836b0bd53a7SPeter Ujfalusi {
837b0bd53a7SPeter Ujfalusi struct soc_mixer_control *mc =
838b0bd53a7SPeter Ujfalusi (struct soc_mixer_control *)kcontrol->private_value;
839c68e7f5bSKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
840b0bd53a7SPeter Ujfalusi unsigned int reg = mc->reg;
841b0bd53a7SPeter Ujfalusi unsigned int shift = mc->shift;
842b0bd53a7SPeter Ujfalusi unsigned int rshift = mc->rshift;
843b0bd53a7SPeter Ujfalusi int max = mc->max;
844b0bd53a7SPeter Ujfalusi int mask = (1 << fls(max)) - 1;
845b0bd53a7SPeter Ujfalusi
846b0bd53a7SPeter Ujfalusi ucontrol->value.integer.value[0] =
847c68e7f5bSKuninori Morimoto (twl4030_read(component, reg) >> shift) & mask;
848b0bd53a7SPeter Ujfalusi if (ucontrol->value.integer.value[0])
849b0bd53a7SPeter Ujfalusi ucontrol->value.integer.value[0] =
850b0bd53a7SPeter Ujfalusi max + 1 - ucontrol->value.integer.value[0];
851b0bd53a7SPeter Ujfalusi
852b0bd53a7SPeter Ujfalusi if (shift != rshift) {
853b0bd53a7SPeter Ujfalusi ucontrol->value.integer.value[1] =
854c68e7f5bSKuninori Morimoto (twl4030_read(component, reg) >> rshift) & mask;
855b0bd53a7SPeter Ujfalusi if (ucontrol->value.integer.value[1])
856b0bd53a7SPeter Ujfalusi ucontrol->value.integer.value[1] =
857b0bd53a7SPeter Ujfalusi max + 1 - ucontrol->value.integer.value[1];
858b0bd53a7SPeter Ujfalusi }
859b0bd53a7SPeter Ujfalusi
860b0bd53a7SPeter Ujfalusi return 0;
861b0bd53a7SPeter Ujfalusi }
862b0bd53a7SPeter Ujfalusi
snd_soc_put_volsw_twl4030(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)863b0bd53a7SPeter Ujfalusi static int snd_soc_put_volsw_twl4030(struct snd_kcontrol *kcontrol,
864b0bd53a7SPeter Ujfalusi struct snd_ctl_elem_value *ucontrol)
865b0bd53a7SPeter Ujfalusi {
866b0bd53a7SPeter Ujfalusi struct soc_mixer_control *mc =
867b0bd53a7SPeter Ujfalusi (struct soc_mixer_control *)kcontrol->private_value;
868c68e7f5bSKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
869b0bd53a7SPeter Ujfalusi unsigned int reg = mc->reg;
870b0bd53a7SPeter Ujfalusi unsigned int shift = mc->shift;
871b0bd53a7SPeter Ujfalusi unsigned int rshift = mc->rshift;
872b0bd53a7SPeter Ujfalusi int max = mc->max;
873b0bd53a7SPeter Ujfalusi int mask = (1 << fls(max)) - 1;
874b0bd53a7SPeter Ujfalusi unsigned short val, val2, val_mask;
875b0bd53a7SPeter Ujfalusi
876b0bd53a7SPeter Ujfalusi val = (ucontrol->value.integer.value[0] & mask);
877b0bd53a7SPeter Ujfalusi
878b0bd53a7SPeter Ujfalusi val_mask = mask << shift;
879b0bd53a7SPeter Ujfalusi if (val)
880b0bd53a7SPeter Ujfalusi val = max + 1 - val;
881b0bd53a7SPeter Ujfalusi val = val << shift;
882b0bd53a7SPeter Ujfalusi if (shift != rshift) {
883b0bd53a7SPeter Ujfalusi val2 = (ucontrol->value.integer.value[1] & mask);
884b0bd53a7SPeter Ujfalusi val_mask |= mask << rshift;
885b0bd53a7SPeter Ujfalusi if (val2)
886b0bd53a7SPeter Ujfalusi val2 = max + 1 - val2;
887b0bd53a7SPeter Ujfalusi val |= val2 << rshift;
888b0bd53a7SPeter Ujfalusi }
889c68e7f5bSKuninori Morimoto return snd_soc_component_update_bits(component, reg, val_mask, val);
890b0bd53a7SPeter Ujfalusi }
891b0bd53a7SPeter Ujfalusi
snd_soc_get_volsw_r2_twl4030(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)892b0bd53a7SPeter Ujfalusi static int snd_soc_get_volsw_r2_twl4030(struct snd_kcontrol *kcontrol,
893b0bd53a7SPeter Ujfalusi struct snd_ctl_elem_value *ucontrol)
894b0bd53a7SPeter Ujfalusi {
895b0bd53a7SPeter Ujfalusi struct soc_mixer_control *mc =
896b0bd53a7SPeter Ujfalusi (struct soc_mixer_control *)kcontrol->private_value;
897c68e7f5bSKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
898b0bd53a7SPeter Ujfalusi unsigned int reg = mc->reg;
899b0bd53a7SPeter Ujfalusi unsigned int reg2 = mc->rreg;
900b0bd53a7SPeter Ujfalusi unsigned int shift = mc->shift;
901b0bd53a7SPeter Ujfalusi int max = mc->max;
902b0bd53a7SPeter Ujfalusi int mask = (1<<fls(max))-1;
903b0bd53a7SPeter Ujfalusi
904b0bd53a7SPeter Ujfalusi ucontrol->value.integer.value[0] =
905c68e7f5bSKuninori Morimoto (twl4030_read(component, reg) >> shift) & mask;
906b0bd53a7SPeter Ujfalusi ucontrol->value.integer.value[1] =
907c68e7f5bSKuninori Morimoto (twl4030_read(component, reg2) >> shift) & mask;
908b0bd53a7SPeter Ujfalusi
909b0bd53a7SPeter Ujfalusi if (ucontrol->value.integer.value[0])
910b0bd53a7SPeter Ujfalusi ucontrol->value.integer.value[0] =
911b0bd53a7SPeter Ujfalusi max + 1 - ucontrol->value.integer.value[0];
912b0bd53a7SPeter Ujfalusi if (ucontrol->value.integer.value[1])
913b0bd53a7SPeter Ujfalusi ucontrol->value.integer.value[1] =
914b0bd53a7SPeter Ujfalusi max + 1 - ucontrol->value.integer.value[1];
915b0bd53a7SPeter Ujfalusi
916b0bd53a7SPeter Ujfalusi return 0;
917b0bd53a7SPeter Ujfalusi }
918b0bd53a7SPeter Ujfalusi
snd_soc_put_volsw_r2_twl4030(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)919b0bd53a7SPeter Ujfalusi static int snd_soc_put_volsw_r2_twl4030(struct snd_kcontrol *kcontrol,
920b0bd53a7SPeter Ujfalusi struct snd_ctl_elem_value *ucontrol)
921b0bd53a7SPeter Ujfalusi {
922b0bd53a7SPeter Ujfalusi struct soc_mixer_control *mc =
923b0bd53a7SPeter Ujfalusi (struct soc_mixer_control *)kcontrol->private_value;
924c68e7f5bSKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
925b0bd53a7SPeter Ujfalusi unsigned int reg = mc->reg;
926b0bd53a7SPeter Ujfalusi unsigned int reg2 = mc->rreg;
927b0bd53a7SPeter Ujfalusi unsigned int shift = mc->shift;
928b0bd53a7SPeter Ujfalusi int max = mc->max;
929b0bd53a7SPeter Ujfalusi int mask = (1 << fls(max)) - 1;
930b0bd53a7SPeter Ujfalusi int err;
931b0bd53a7SPeter Ujfalusi unsigned short val, val2, val_mask;
932b0bd53a7SPeter Ujfalusi
933b0bd53a7SPeter Ujfalusi val_mask = mask << shift;
934b0bd53a7SPeter Ujfalusi val = (ucontrol->value.integer.value[0] & mask);
935b0bd53a7SPeter Ujfalusi val2 = (ucontrol->value.integer.value[1] & mask);
936b0bd53a7SPeter Ujfalusi
937b0bd53a7SPeter Ujfalusi if (val)
938b0bd53a7SPeter Ujfalusi val = max + 1 - val;
939b0bd53a7SPeter Ujfalusi if (val2)
940b0bd53a7SPeter Ujfalusi val2 = max + 1 - val2;
941b0bd53a7SPeter Ujfalusi
942b0bd53a7SPeter Ujfalusi val = val << shift;
943b0bd53a7SPeter Ujfalusi val2 = val2 << shift;
944b0bd53a7SPeter Ujfalusi
945c68e7f5bSKuninori Morimoto err = snd_soc_component_update_bits(component, reg, val_mask, val);
946b0bd53a7SPeter Ujfalusi if (err < 0)
947b0bd53a7SPeter Ujfalusi return err;
948b0bd53a7SPeter Ujfalusi
949c68e7f5bSKuninori Morimoto err = snd_soc_component_update_bits(component, reg2, val_mask, val2);
950b0bd53a7SPeter Ujfalusi return err;
951b0bd53a7SPeter Ujfalusi }
952b0bd53a7SPeter Ujfalusi
953b74bd40fSLopez Cruz, Misael /* Codec operation modes */
954b74bd40fSLopez Cruz, Misael static const char *twl4030_op_modes_texts[] = {
955b74bd40fSLopez Cruz, Misael "Option 2 (voice/audio)", "Option 1 (audio)"
956b74bd40fSLopez Cruz, Misael };
957b74bd40fSLopez Cruz, Misael
9589f04fba7STakashi Iwai static SOC_ENUM_SINGLE_DECL(twl4030_op_modes_enum,
9599f04fba7STakashi Iwai TWL4030_REG_CODEC_MODE, 0,
960b74bd40fSLopez Cruz, Misael twl4030_op_modes_texts);
961b74bd40fSLopez Cruz, Misael
snd_soc_put_twl4030_opmode_enum_double(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)962423c238dSMark Brown static int snd_soc_put_twl4030_opmode_enum_double(struct snd_kcontrol *kcontrol,
963b74bd40fSLopez Cruz, Misael struct snd_ctl_elem_value *ucontrol)
964b74bd40fSLopez Cruz, Misael {
965c68e7f5bSKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
966c68e7f5bSKuninori Morimoto struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
967b74bd40fSLopez Cruz, Misael
968b74bd40fSLopez Cruz, Misael if (twl4030->configured) {
969c68e7f5bSKuninori Morimoto dev_err(component->dev,
9703b8a0795SPeter Ujfalusi "operation mode cannot be changed on-the-fly\n");
971b74bd40fSLopez Cruz, Misael return -EBUSY;
972b74bd40fSLopez Cruz, Misael }
973b74bd40fSLopez Cruz, Misael
9746b207c0fSTakashi Iwai return snd_soc_put_enum_double(kcontrol, ucontrol);
975b74bd40fSLopez Cruz, Misael }
976b74bd40fSLopez Cruz, Misael
977b0bd53a7SPeter Ujfalusi /*
978c10b82cfSPeter Ujfalusi * FGAIN volume control:
979c10b82cfSPeter Ujfalusi * from -62 to 0 dB in 1 dB steps (mute instead of -63 dB)
980c10b82cfSPeter Ujfalusi */
981d889a72cSPeter Ujfalusi static DECLARE_TLV_DB_SCALE(digital_fine_tlv, -6300, 100, 1);
982c10b82cfSPeter Ujfalusi
9830d33ea0bSPeter Ujfalusi /*
9840d33ea0bSPeter Ujfalusi * CGAIN volume control:
9850d33ea0bSPeter Ujfalusi * 0 dB to 12 dB in 6 dB steps
9860d33ea0bSPeter Ujfalusi * value 2 and 3 means 12 dB
9870d33ea0bSPeter Ujfalusi */
988d889a72cSPeter Ujfalusi static DECLARE_TLV_DB_SCALE(digital_coarse_tlv, 0, 600, 0);
989d889a72cSPeter Ujfalusi
990d889a72cSPeter Ujfalusi /*
9911a787e7aSJoonyoung Shim * Voice Downlink GAIN volume control:
9921a787e7aSJoonyoung Shim * from -37 to 12 dB in 1 dB steps (mute instead of -37 dB)
9931a787e7aSJoonyoung Shim */
9941a787e7aSJoonyoung Shim static DECLARE_TLV_DB_SCALE(digital_voice_downlink_tlv, -3700, 100, 1);
9951a787e7aSJoonyoung Shim
9961a787e7aSJoonyoung Shim /*
997d889a72cSPeter Ujfalusi * Analog playback gain
998d889a72cSPeter Ujfalusi * -24 dB to 12 dB in 2 dB steps
999d889a72cSPeter Ujfalusi */
1000d889a72cSPeter Ujfalusi static DECLARE_TLV_DB_SCALE(analog_tlv, -2400, 200, 0);
10010d33ea0bSPeter Ujfalusi
1002381a22b5SPeter Ujfalusi /*
10034290239cSPeter Ujfalusi * Gain controls tied to outputs
10044290239cSPeter Ujfalusi * -6 dB to 6 dB in 6 dB steps (mute instead of -12)
10054290239cSPeter Ujfalusi */
10064290239cSPeter Ujfalusi static DECLARE_TLV_DB_SCALE(output_tvl, -1200, 600, 1);
10074290239cSPeter Ujfalusi
10084290239cSPeter Ujfalusi /*
100918cc8d8dSJoonyoung Shim * Gain control for earpiece amplifier
101018cc8d8dSJoonyoung Shim * 0 dB to 12 dB in 6 dB steps (mute instead of -6)
101118cc8d8dSJoonyoung Shim */
101218cc8d8dSJoonyoung Shim static DECLARE_TLV_DB_SCALE(output_ear_tvl, -600, 600, 1);
101318cc8d8dSJoonyoung Shim
101418cc8d8dSJoonyoung Shim /*
1015381a22b5SPeter Ujfalusi * Capture gain after the ADCs
1016381a22b5SPeter Ujfalusi * from 0 dB to 31 dB in 1 dB steps
1017381a22b5SPeter Ujfalusi */
1018381a22b5SPeter Ujfalusi static DECLARE_TLV_DB_SCALE(digital_capture_tlv, 0, 100, 0);
1019381a22b5SPeter Ujfalusi
10205920b453SGrazvydas Ignotas /*
10215920b453SGrazvydas Ignotas * Gain control for input amplifiers
10225920b453SGrazvydas Ignotas * 0 dB to 30 dB in 6 dB steps
10235920b453SGrazvydas Ignotas */
10245920b453SGrazvydas Ignotas static DECLARE_TLV_DB_SCALE(input_gain_tlv, 0, 600, 0);
10255920b453SGrazvydas Ignotas
1026328d0a13SLopez Cruz, Misael /* AVADC clock priority */
1027328d0a13SLopez Cruz, Misael static const char *twl4030_avadc_clk_priority_texts[] = {
1028328d0a13SLopez Cruz, Misael "Voice high priority", "HiFi high priority"
1029328d0a13SLopez Cruz, Misael };
1030328d0a13SLopez Cruz, Misael
10319f04fba7STakashi Iwai static SOC_ENUM_SINGLE_DECL(twl4030_avadc_clk_priority_enum,
10329f04fba7STakashi Iwai TWL4030_REG_AVADC_CTL, 2,
1033328d0a13SLopez Cruz, Misael twl4030_avadc_clk_priority_texts);
1034328d0a13SLopez Cruz, Misael
103589492be8SPeter Ujfalusi static const char *twl4030_rampdelay_texts[] = {
103689492be8SPeter Ujfalusi "27/20/14 ms", "55/40/27 ms", "109/81/55 ms", "218/161/109 ms",
103789492be8SPeter Ujfalusi "437/323/218 ms", "874/645/437 ms", "1748/1291/874 ms",
103889492be8SPeter Ujfalusi "3495/2581/1748 ms"
103989492be8SPeter Ujfalusi };
104089492be8SPeter Ujfalusi
10419f04fba7STakashi Iwai static SOC_ENUM_SINGLE_DECL(twl4030_rampdelay_enum,
10429f04fba7STakashi Iwai TWL4030_REG_HS_POPN_SET, 2,
104389492be8SPeter Ujfalusi twl4030_rampdelay_texts);
104489492be8SPeter Ujfalusi
1045376f7839SPeter Ujfalusi /* Vibra H-bridge direction mode */
1046376f7839SPeter Ujfalusi static const char *twl4030_vibradirmode_texts[] = {
1047376f7839SPeter Ujfalusi "Vibra H-bridge direction", "Audio data MSB",
1048376f7839SPeter Ujfalusi };
1049376f7839SPeter Ujfalusi
10509f04fba7STakashi Iwai static SOC_ENUM_SINGLE_DECL(twl4030_vibradirmode_enum,
10519f04fba7STakashi Iwai TWL4030_REG_VIBRA_CTL, 5,
1052376f7839SPeter Ujfalusi twl4030_vibradirmode_texts);
1053376f7839SPeter Ujfalusi
1054376f7839SPeter Ujfalusi /* Vibra H-bridge direction */
1055376f7839SPeter Ujfalusi static const char *twl4030_vibradir_texts[] = {
1056376f7839SPeter Ujfalusi "Positive polarity", "Negative polarity",
1057376f7839SPeter Ujfalusi };
1058376f7839SPeter Ujfalusi
10599f04fba7STakashi Iwai static SOC_ENUM_SINGLE_DECL(twl4030_vibradir_enum,
10609f04fba7STakashi Iwai TWL4030_REG_VIBRA_CTL, 1,
1061376f7839SPeter Ujfalusi twl4030_vibradir_texts);
1062376f7839SPeter Ujfalusi
106336aeff61SPeter Ujfalusi /* Digimic Left and right swapping */
106436aeff61SPeter Ujfalusi static const char *twl4030_digimicswap_texts[] = {
106536aeff61SPeter Ujfalusi "Not swapped", "Swapped",
106636aeff61SPeter Ujfalusi };
106736aeff61SPeter Ujfalusi
10689f04fba7STakashi Iwai static SOC_ENUM_SINGLE_DECL(twl4030_digimicswap_enum,
10699f04fba7STakashi Iwai TWL4030_REG_MISC_SET_1, 0,
107036aeff61SPeter Ujfalusi twl4030_digimicswap_texts);
107136aeff61SPeter Ujfalusi
1072cc17557eSSteve Sakoman static const struct snd_kcontrol_new twl4030_snd_controls[] = {
1073b74bd40fSLopez Cruz, Misael /* Codec operation mode control */
1074b74bd40fSLopez Cruz, Misael SOC_ENUM_EXT("Codec Operation Mode", twl4030_op_modes_enum,
1075b74bd40fSLopez Cruz, Misael snd_soc_get_enum_double,
1076b74bd40fSLopez Cruz, Misael snd_soc_put_twl4030_opmode_enum_double),
1077b74bd40fSLopez Cruz, Misael
1078d889a72cSPeter Ujfalusi /* Common playback gain controls */
1079d889a72cSPeter Ujfalusi SOC_DOUBLE_R_TLV("DAC1 Digital Fine Playback Volume",
1080d889a72cSPeter Ujfalusi TWL4030_REG_ARXL1PGA, TWL4030_REG_ARXR1PGA,
1081d889a72cSPeter Ujfalusi 0, 0x3f, 0, digital_fine_tlv),
1082d889a72cSPeter Ujfalusi SOC_DOUBLE_R_TLV("DAC2 Digital Fine Playback Volume",
1083cc17557eSSteve Sakoman TWL4030_REG_ARXL2PGA, TWL4030_REG_ARXR2PGA,
1084d889a72cSPeter Ujfalusi 0, 0x3f, 0, digital_fine_tlv),
1085d889a72cSPeter Ujfalusi
1086d889a72cSPeter Ujfalusi SOC_DOUBLE_R_TLV("DAC1 Digital Coarse Playback Volume",
1087d889a72cSPeter Ujfalusi TWL4030_REG_ARXL1PGA, TWL4030_REG_ARXR1PGA,
1088d889a72cSPeter Ujfalusi 6, 0x2, 0, digital_coarse_tlv),
1089d889a72cSPeter Ujfalusi SOC_DOUBLE_R_TLV("DAC2 Digital Coarse Playback Volume",
10900d33ea0bSPeter Ujfalusi TWL4030_REG_ARXL2PGA, TWL4030_REG_ARXR2PGA,
1091d889a72cSPeter Ujfalusi 6, 0x2, 0, digital_coarse_tlv),
1092d889a72cSPeter Ujfalusi
1093d889a72cSPeter Ujfalusi SOC_DOUBLE_R_TLV("DAC1 Analog Playback Volume",
1094d889a72cSPeter Ujfalusi TWL4030_REG_ARXL1_APGA_CTL, TWL4030_REG_ARXR1_APGA_CTL,
1095d889a72cSPeter Ujfalusi 3, 0x12, 1, analog_tlv),
1096d889a72cSPeter Ujfalusi SOC_DOUBLE_R_TLV("DAC2 Analog Playback Volume",
1097d889a72cSPeter Ujfalusi TWL4030_REG_ARXL2_APGA_CTL, TWL4030_REG_ARXR2_APGA_CTL,
1098d889a72cSPeter Ujfalusi 3, 0x12, 1, analog_tlv),
109944c55870SPeter Ujfalusi SOC_DOUBLE_R("DAC1 Analog Playback Switch",
110044c55870SPeter Ujfalusi TWL4030_REG_ARXL1_APGA_CTL, TWL4030_REG_ARXR1_APGA_CTL,
110144c55870SPeter Ujfalusi 1, 1, 0),
110244c55870SPeter Ujfalusi SOC_DOUBLE_R("DAC2 Analog Playback Switch",
110344c55870SPeter Ujfalusi TWL4030_REG_ARXL2_APGA_CTL, TWL4030_REG_ARXR2_APGA_CTL,
110444c55870SPeter Ujfalusi 1, 1, 0),
1105381a22b5SPeter Ujfalusi
11061a787e7aSJoonyoung Shim /* Common voice downlink gain controls */
11071a787e7aSJoonyoung Shim SOC_SINGLE_TLV("DAC Voice Digital Downlink Volume",
11081a787e7aSJoonyoung Shim TWL4030_REG_VRXPGA, 0, 0x31, 0, digital_voice_downlink_tlv),
11091a787e7aSJoonyoung Shim
11101a787e7aSJoonyoung Shim SOC_SINGLE_TLV("DAC Voice Analog Downlink Volume",
11111a787e7aSJoonyoung Shim TWL4030_REG_VDL_APGA_CTL, 3, 0x12, 1, analog_tlv),
11121a787e7aSJoonyoung Shim
11131a787e7aSJoonyoung Shim SOC_SINGLE("DAC Voice Analog Downlink Switch",
11141a787e7aSJoonyoung Shim TWL4030_REG_VDL_APGA_CTL, 1, 1, 0),
11151a787e7aSJoonyoung Shim
11164290239cSPeter Ujfalusi /* Separate output gain controls */
11170f9887d1SPeter Ujfalusi SOC_DOUBLE_R_EXT_TLV("PreDriv Playback Volume",
11184290239cSPeter Ujfalusi TWL4030_REG_PREDL_CTL, TWL4030_REG_PREDR_CTL,
11190f9887d1SPeter Ujfalusi 4, 3, 0, snd_soc_get_volsw_r2_twl4030,
11200f9887d1SPeter Ujfalusi snd_soc_put_volsw_r2_twl4030, output_tvl),
11214290239cSPeter Ujfalusi
11220f9887d1SPeter Ujfalusi SOC_DOUBLE_EXT_TLV("Headset Playback Volume",
11230f9887d1SPeter Ujfalusi TWL4030_REG_HS_GAIN_SET, 0, 2, 3, 0, snd_soc_get_volsw_twl4030,
11240f9887d1SPeter Ujfalusi snd_soc_put_volsw_twl4030, output_tvl),
11254290239cSPeter Ujfalusi
11260f9887d1SPeter Ujfalusi SOC_DOUBLE_R_EXT_TLV("Carkit Playback Volume",
11274290239cSPeter Ujfalusi TWL4030_REG_PRECKL_CTL, TWL4030_REG_PRECKR_CTL,
11280f9887d1SPeter Ujfalusi 4, 3, 0, snd_soc_get_volsw_r2_twl4030,
11290f9887d1SPeter Ujfalusi snd_soc_put_volsw_r2_twl4030, output_tvl),
11304290239cSPeter Ujfalusi
11310f9887d1SPeter Ujfalusi SOC_SINGLE_EXT_TLV("Earpiece Playback Volume",
11320f9887d1SPeter Ujfalusi TWL4030_REG_EAR_CTL, 4, 3, 0, snd_soc_get_volsw_twl4030,
11330f9887d1SPeter Ujfalusi snd_soc_put_volsw_twl4030, output_ear_tvl),
11344290239cSPeter Ujfalusi
1135381a22b5SPeter Ujfalusi /* Common capture gain controls */
1136276c6222SPeter Ujfalusi SOC_DOUBLE_R_TLV("TX1 Digital Capture Volume",
1137cc17557eSSteve Sakoman TWL4030_REG_ATXL1PGA, TWL4030_REG_ATXR1PGA,
1138381a22b5SPeter Ujfalusi 0, 0x1f, 0, digital_capture_tlv),
1139276c6222SPeter Ujfalusi SOC_DOUBLE_R_TLV("TX2 Digital Capture Volume",
1140276c6222SPeter Ujfalusi TWL4030_REG_AVTXL2PGA, TWL4030_REG_AVTXR2PGA,
1141276c6222SPeter Ujfalusi 0, 0x1f, 0, digital_capture_tlv),
11425920b453SGrazvydas Ignotas
1143276c6222SPeter Ujfalusi SOC_DOUBLE_TLV("Analog Capture Volume", TWL4030_REG_ANAMIC_GAIN,
11445920b453SGrazvydas Ignotas 0, 3, 5, 0, input_gain_tlv),
114589492be8SPeter Ujfalusi
1146328d0a13SLopez Cruz, Misael SOC_ENUM("AVADC Clock Priority", twl4030_avadc_clk_priority_enum),
1147328d0a13SLopez Cruz, Misael
114889492be8SPeter Ujfalusi SOC_ENUM("HS ramp delay", twl4030_rampdelay_enum),
1149376f7839SPeter Ujfalusi
1150376f7839SPeter Ujfalusi SOC_ENUM("Vibra H-bridge mode", twl4030_vibradirmode_enum),
1151376f7839SPeter Ujfalusi SOC_ENUM("Vibra H-bridge direction", twl4030_vibradir_enum),
115236aeff61SPeter Ujfalusi
115336aeff61SPeter Ujfalusi SOC_ENUM("Digimic LR Swap", twl4030_digimicswap_enum),
1154cc17557eSSteve Sakoman };
1155cc17557eSSteve Sakoman
1156cc17557eSSteve Sakoman static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
1157276c6222SPeter Ujfalusi /* Left channel inputs */
1158276c6222SPeter Ujfalusi SND_SOC_DAPM_INPUT("MAINMIC"),
1159276c6222SPeter Ujfalusi SND_SOC_DAPM_INPUT("HSMIC"),
1160276c6222SPeter Ujfalusi SND_SOC_DAPM_INPUT("AUXL"),
1161276c6222SPeter Ujfalusi SND_SOC_DAPM_INPUT("CARKITMIC"),
1162276c6222SPeter Ujfalusi /* Right channel inputs */
1163276c6222SPeter Ujfalusi SND_SOC_DAPM_INPUT("SUBMIC"),
1164276c6222SPeter Ujfalusi SND_SOC_DAPM_INPUT("AUXR"),
1165276c6222SPeter Ujfalusi /* Digital microphones (Stereo) */
1166276c6222SPeter Ujfalusi SND_SOC_DAPM_INPUT("DIGIMIC0"),
1167276c6222SPeter Ujfalusi SND_SOC_DAPM_INPUT("DIGIMIC1"),
1168cc17557eSSteve Sakoman
1169276c6222SPeter Ujfalusi /* Outputs */
11705e98a464SPeter Ujfalusi SND_SOC_DAPM_OUTPUT("EARPIECE"),
11712a6f5c58SPeter Ujfalusi SND_SOC_DAPM_OUTPUT("PREDRIVEL"),
11722a6f5c58SPeter Ujfalusi SND_SOC_DAPM_OUTPUT("PREDRIVER"),
1173dfad21a2SPeter Ujfalusi SND_SOC_DAPM_OUTPUT("HSOL"),
1174dfad21a2SPeter Ujfalusi SND_SOC_DAPM_OUTPUT("HSOR"),
11756a1bee4aSPeter Ujfalusi SND_SOC_DAPM_OUTPUT("CARKITL"),
11766a1bee4aSPeter Ujfalusi SND_SOC_DAPM_OUTPUT("CARKITR"),
1177df339804SPeter Ujfalusi SND_SOC_DAPM_OUTPUT("HFL"),
1178df339804SPeter Ujfalusi SND_SOC_DAPM_OUTPUT("HFR"),
1179376f7839SPeter Ujfalusi SND_SOC_DAPM_OUTPUT("VIBRA"),
1180cc17557eSSteve Sakoman
11817b4c734eSPeter Ujfalusi /* AIF and APLL clocks for running DAIs (including loopback) */
11827b4c734eSPeter Ujfalusi SND_SOC_DAPM_OUTPUT("Virtual HiFi OUT"),
11837b4c734eSPeter Ujfalusi SND_SOC_DAPM_INPUT("Virtual HiFi IN"),
11847b4c734eSPeter Ujfalusi SND_SOC_DAPM_OUTPUT("Virtual Voice OUT"),
11857b4c734eSPeter Ujfalusi
118653b5047dSPeter Ujfalusi /* DACs */
11877f51e7d3SPeter Ujfalusi SND_SOC_DAPM_DAC("DAC Right1", NULL, SND_SOC_NOPM, 0, 0),
11887f51e7d3SPeter Ujfalusi SND_SOC_DAPM_DAC("DAC Left1", NULL, SND_SOC_NOPM, 0, 0),
11897f51e7d3SPeter Ujfalusi SND_SOC_DAPM_DAC("DAC Right2", NULL, SND_SOC_NOPM, 0, 0),
11907f51e7d3SPeter Ujfalusi SND_SOC_DAPM_DAC("DAC Left2", NULL, SND_SOC_NOPM, 0, 0),
11917f51e7d3SPeter Ujfalusi SND_SOC_DAPM_DAC("DAC Voice", NULL, SND_SOC_NOPM, 0, 0),
1192cc17557eSSteve Sakoman
1193927a7747SPeter Ujfalusi SND_SOC_DAPM_AIF_IN("VAIFIN", "Voice Playback", 0,
1194927a7747SPeter Ujfalusi TWL4030_REG_VOICE_IF, 6, 0),
1195927a7747SPeter Ujfalusi
11967393958fSPeter Ujfalusi /* Analog bypasses */
119778e08e2fSPeter Ujfalusi SND_SOC_DAPM_SWITCH("Right1 Analog Loopback", SND_SOC_NOPM, 0, 0,
119878e08e2fSPeter Ujfalusi &twl4030_dapm_abypassr1_control),
119978e08e2fSPeter Ujfalusi SND_SOC_DAPM_SWITCH("Left1 Analog Loopback", SND_SOC_NOPM, 0, 0,
120078e08e2fSPeter Ujfalusi &twl4030_dapm_abypassl1_control),
120178e08e2fSPeter Ujfalusi SND_SOC_DAPM_SWITCH("Right2 Analog Loopback", SND_SOC_NOPM, 0, 0,
120278e08e2fSPeter Ujfalusi &twl4030_dapm_abypassr2_control),
120378e08e2fSPeter Ujfalusi SND_SOC_DAPM_SWITCH("Left2 Analog Loopback", SND_SOC_NOPM, 0, 0,
120478e08e2fSPeter Ujfalusi &twl4030_dapm_abypassl2_control),
120578e08e2fSPeter Ujfalusi SND_SOC_DAPM_SWITCH("Voice Analog Loopback", SND_SOC_NOPM, 0, 0,
120678e08e2fSPeter Ujfalusi &twl4030_dapm_abypassv_control),
120778e08e2fSPeter Ujfalusi
120878e08e2fSPeter Ujfalusi /* Master analog loopback switch */
120978e08e2fSPeter Ujfalusi SND_SOC_DAPM_SUPPLY("FM Loop Enable", TWL4030_REG_MISC_SET_1, 5, 0,
121078e08e2fSPeter Ujfalusi NULL, 0),
12117393958fSPeter Ujfalusi
12126bab83fdSPeter Ujfalusi /* Digital bypasses */
121378e08e2fSPeter Ujfalusi SND_SOC_DAPM_SWITCH("Left Digital Loopback", SND_SOC_NOPM, 0, 0,
121478e08e2fSPeter Ujfalusi &twl4030_dapm_dbypassl_control),
121578e08e2fSPeter Ujfalusi SND_SOC_DAPM_SWITCH("Right Digital Loopback", SND_SOC_NOPM, 0, 0,
121678e08e2fSPeter Ujfalusi &twl4030_dapm_dbypassr_control),
121778e08e2fSPeter Ujfalusi SND_SOC_DAPM_SWITCH("Voice Digital Loopback", SND_SOC_NOPM, 0, 0,
121878e08e2fSPeter Ujfalusi &twl4030_dapm_dbypassv_control),
12196bab83fdSPeter Ujfalusi
12204005d39aSPeter Ujfalusi /* Digital mixers, power control for the physical DACs */
12214005d39aSPeter Ujfalusi SND_SOC_DAPM_MIXER("Digital R1 Playback Mixer",
12224005d39aSPeter Ujfalusi TWL4030_REG_AVDAC_CTL, 0, 0, NULL, 0),
12234005d39aSPeter Ujfalusi SND_SOC_DAPM_MIXER("Digital L1 Playback Mixer",
12244005d39aSPeter Ujfalusi TWL4030_REG_AVDAC_CTL, 1, 0, NULL, 0),
12254005d39aSPeter Ujfalusi SND_SOC_DAPM_MIXER("Digital R2 Playback Mixer",
12264005d39aSPeter Ujfalusi TWL4030_REG_AVDAC_CTL, 2, 0, NULL, 0),
12274005d39aSPeter Ujfalusi SND_SOC_DAPM_MIXER("Digital L2 Playback Mixer",
12284005d39aSPeter Ujfalusi TWL4030_REG_AVDAC_CTL, 3, 0, NULL, 0),
12294005d39aSPeter Ujfalusi SND_SOC_DAPM_MIXER("Digital Voice Playback Mixer",
12304005d39aSPeter Ujfalusi TWL4030_REG_AVDAC_CTL, 4, 0, NULL, 0),
12314005d39aSPeter Ujfalusi
12324005d39aSPeter Ujfalusi /* Analog mixers, power control for the physical PGAs */
12334005d39aSPeter Ujfalusi SND_SOC_DAPM_MIXER("Analog R1 Playback Mixer",
12344005d39aSPeter Ujfalusi TWL4030_REG_ARXR1_APGA_CTL, 0, 0, NULL, 0),
12354005d39aSPeter Ujfalusi SND_SOC_DAPM_MIXER("Analog L1 Playback Mixer",
12364005d39aSPeter Ujfalusi TWL4030_REG_ARXL1_APGA_CTL, 0, 0, NULL, 0),
12374005d39aSPeter Ujfalusi SND_SOC_DAPM_MIXER("Analog R2 Playback Mixer",
12384005d39aSPeter Ujfalusi TWL4030_REG_ARXR2_APGA_CTL, 0, 0, NULL, 0),
12394005d39aSPeter Ujfalusi SND_SOC_DAPM_MIXER("Analog L2 Playback Mixer",
12404005d39aSPeter Ujfalusi TWL4030_REG_ARXL2_APGA_CTL, 0, 0, NULL, 0),
12414005d39aSPeter Ujfalusi SND_SOC_DAPM_MIXER("Analog Voice Playback Mixer",
12424005d39aSPeter Ujfalusi TWL4030_REG_VDL_APGA_CTL, 0, 0, NULL, 0),
12437393958fSPeter Ujfalusi
12447729cf74SPeter Ujfalusi SND_SOC_DAPM_SUPPLY("APLL Enable", SND_SOC_NOPM, 0, 0, apll_event,
12457729cf74SPeter Ujfalusi SND_SOC_DAPM_PRE_PMU|SND_SOC_DAPM_POST_PMD),
12467729cf74SPeter Ujfalusi
12477b4c734eSPeter Ujfalusi SND_SOC_DAPM_SUPPLY("AIF Enable", SND_SOC_NOPM, 0, 0, aif_event,
12487b4c734eSPeter Ujfalusi SND_SOC_DAPM_PRE_PMU|SND_SOC_DAPM_POST_PMD),
1249c42a59eaSPeter Ujfalusi
12501a787e7aSJoonyoung Shim /* Output MIXER controls */
12515e98a464SPeter Ujfalusi /* Earpiece */
12521a787e7aSJoonyoung Shim SND_SOC_DAPM_MIXER("Earpiece Mixer", SND_SOC_NOPM, 0, 0,
12531a787e7aSJoonyoung Shim &twl4030_dapm_earpiece_controls[0],
12541a787e7aSJoonyoung Shim ARRAY_SIZE(twl4030_dapm_earpiece_controls)),
12559008adf9SPeter Ujfalusi SND_SOC_DAPM_PGA_E("Earpiece PGA", SND_SOC_NOPM,
12569008adf9SPeter Ujfalusi 0, 0, NULL, 0, earpiecepga_event,
12579008adf9SPeter Ujfalusi SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
12582a6f5c58SPeter Ujfalusi /* PreDrivL/R */
12591a787e7aSJoonyoung Shim SND_SOC_DAPM_MIXER("PredriveL Mixer", SND_SOC_NOPM, 0, 0,
12601a787e7aSJoonyoung Shim &twl4030_dapm_predrivel_controls[0],
12611a787e7aSJoonyoung Shim ARRAY_SIZE(twl4030_dapm_predrivel_controls)),
12629008adf9SPeter Ujfalusi SND_SOC_DAPM_PGA_E("PredriveL PGA", SND_SOC_NOPM,
12639008adf9SPeter Ujfalusi 0, 0, NULL, 0, predrivelpga_event,
12649008adf9SPeter Ujfalusi SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
12651a787e7aSJoonyoung Shim SND_SOC_DAPM_MIXER("PredriveR Mixer", SND_SOC_NOPM, 0, 0,
12661a787e7aSJoonyoung Shim &twl4030_dapm_predriver_controls[0],
12671a787e7aSJoonyoung Shim ARRAY_SIZE(twl4030_dapm_predriver_controls)),
12689008adf9SPeter Ujfalusi SND_SOC_DAPM_PGA_E("PredriveR PGA", SND_SOC_NOPM,
12699008adf9SPeter Ujfalusi 0, 0, NULL, 0, predriverpga_event,
12709008adf9SPeter Ujfalusi SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
1271dfad21a2SPeter Ujfalusi /* HeadsetL/R */
12726943c92eSPeter Ujfalusi SND_SOC_DAPM_MIXER("HeadsetL Mixer", SND_SOC_NOPM, 0, 0,
12731a787e7aSJoonyoung Shim &twl4030_dapm_hsol_controls[0],
12746943c92eSPeter Ujfalusi ARRAY_SIZE(twl4030_dapm_hsol_controls)),
12756943c92eSPeter Ujfalusi SND_SOC_DAPM_PGA_E("HeadsetL PGA", SND_SOC_NOPM,
12766943c92eSPeter Ujfalusi 0, 0, NULL, 0, headsetlpga_event,
1277aad749e5SPeter Ujfalusi SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
12781a787e7aSJoonyoung Shim SND_SOC_DAPM_MIXER("HeadsetR Mixer", SND_SOC_NOPM, 0, 0,
12791a787e7aSJoonyoung Shim &twl4030_dapm_hsor_controls[0],
12801a787e7aSJoonyoung Shim ARRAY_SIZE(twl4030_dapm_hsor_controls)),
12816943c92eSPeter Ujfalusi SND_SOC_DAPM_PGA_E("HeadsetR PGA", SND_SOC_NOPM,
12826943c92eSPeter Ujfalusi 0, 0, NULL, 0, headsetrpga_event,
12836943c92eSPeter Ujfalusi SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
12845152d8c2SPeter Ujfalusi /* CarkitL/R */
12851a787e7aSJoonyoung Shim SND_SOC_DAPM_MIXER("CarkitL Mixer", SND_SOC_NOPM, 0, 0,
12861a787e7aSJoonyoung Shim &twl4030_dapm_carkitl_controls[0],
12871a787e7aSJoonyoung Shim ARRAY_SIZE(twl4030_dapm_carkitl_controls)),
12889008adf9SPeter Ujfalusi SND_SOC_DAPM_PGA_E("CarkitL PGA", SND_SOC_NOPM,
12899008adf9SPeter Ujfalusi 0, 0, NULL, 0, carkitlpga_event,
12909008adf9SPeter Ujfalusi SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
12911a787e7aSJoonyoung Shim SND_SOC_DAPM_MIXER("CarkitR Mixer", SND_SOC_NOPM, 0, 0,
12921a787e7aSJoonyoung Shim &twl4030_dapm_carkitr_controls[0],
12931a787e7aSJoonyoung Shim ARRAY_SIZE(twl4030_dapm_carkitr_controls)),
12949008adf9SPeter Ujfalusi SND_SOC_DAPM_PGA_E("CarkitR PGA", SND_SOC_NOPM,
12959008adf9SPeter Ujfalusi 0, 0, NULL, 0, carkitrpga_event,
12969008adf9SPeter Ujfalusi SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
12971a787e7aSJoonyoung Shim
12981a787e7aSJoonyoung Shim /* Output MUX controls */
1299df339804SPeter Ujfalusi /* HandsfreeL/R */
13005a2e9a48SPeter Ujfalusi SND_SOC_DAPM_MUX("HandsfreeL Mux", SND_SOC_NOPM, 0, 0,
13015a2e9a48SPeter Ujfalusi &twl4030_dapm_handsfreel_control),
1302e3c7dbb0SLopez Cruz, Misael SND_SOC_DAPM_SWITCH("HandsfreeL", SND_SOC_NOPM, 0, 0,
13030f89bdcaSPeter Ujfalusi &twl4030_dapm_handsfreelmute_control),
13045a2e9a48SPeter Ujfalusi SND_SOC_DAPM_PGA_E("HandsfreeL PGA", SND_SOC_NOPM,
13055a2e9a48SPeter Ujfalusi 0, 0, NULL, 0, handsfreelpga_event,
130649d92c7dSStanley.Miao SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
13075a2e9a48SPeter Ujfalusi SND_SOC_DAPM_MUX("HandsfreeR Mux", SND_SOC_NOPM, 5, 0,
13085a2e9a48SPeter Ujfalusi &twl4030_dapm_handsfreer_control),
1309e3c7dbb0SLopez Cruz, Misael SND_SOC_DAPM_SWITCH("HandsfreeR", SND_SOC_NOPM, 0, 0,
13100f89bdcaSPeter Ujfalusi &twl4030_dapm_handsfreermute_control),
13115a2e9a48SPeter Ujfalusi SND_SOC_DAPM_PGA_E("HandsfreeR PGA", SND_SOC_NOPM,
13125a2e9a48SPeter Ujfalusi 0, 0, NULL, 0, handsfreerpga_event,
131349d92c7dSStanley.Miao SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
1314376f7839SPeter Ujfalusi /* Vibra */
131586139a13SJari Vanhala SND_SOC_DAPM_MUX_E("Vibra Mux", TWL4030_REG_VIBRA_CTL, 0, 0,
131686139a13SJari Vanhala &twl4030_dapm_vibra_control, vibramux_event,
131786139a13SJari Vanhala SND_SOC_DAPM_PRE_PMU),
1318376f7839SPeter Ujfalusi SND_SOC_DAPM_MUX("Vibra Route", SND_SOC_NOPM, 0, 0,
1319376f7839SPeter Ujfalusi &twl4030_dapm_vibrapath_control),
13205e98a464SPeter Ujfalusi
1321276c6222SPeter Ujfalusi /* Introducing four virtual ADC, since TWL4030 have four channel for
1322276c6222SPeter Ujfalusi capture */
13237f51e7d3SPeter Ujfalusi SND_SOC_DAPM_ADC("ADC Virtual Left1", NULL, SND_SOC_NOPM, 0, 0),
13247f51e7d3SPeter Ujfalusi SND_SOC_DAPM_ADC("ADC Virtual Right1", NULL, SND_SOC_NOPM, 0, 0),
13257f51e7d3SPeter Ujfalusi SND_SOC_DAPM_ADC("ADC Virtual Left2", NULL, SND_SOC_NOPM, 0, 0),
13267f51e7d3SPeter Ujfalusi SND_SOC_DAPM_ADC("ADC Virtual Right2", NULL, SND_SOC_NOPM, 0, 0),
1327276c6222SPeter Ujfalusi
1328927a7747SPeter Ujfalusi SND_SOC_DAPM_AIF_OUT("VAIFOUT", "Voice Capture", 0,
1329927a7747SPeter Ujfalusi TWL4030_REG_VOICE_IF, 5, 0),
1330927a7747SPeter Ujfalusi
1331276c6222SPeter Ujfalusi /* Analog/Digital mic path selection.
1332276c6222SPeter Ujfalusi TX1 Left/Right: either analog Left/Right or Digimic0
1333276c6222SPeter Ujfalusi TX2 Left/Right: either analog Left/Right or Digimic1 */
1334bda7d2a8SPeter Ujfalusi SND_SOC_DAPM_MUX("TX1 Capture Route", SND_SOC_NOPM, 0, 0,
1335bda7d2a8SPeter Ujfalusi &twl4030_dapm_micpathtx1_control),
1336bda7d2a8SPeter Ujfalusi SND_SOC_DAPM_MUX("TX2 Capture Route", SND_SOC_NOPM, 0, 0,
1337bda7d2a8SPeter Ujfalusi &twl4030_dapm_micpathtx2_control),
1338276c6222SPeter Ujfalusi
133997b8096dSJoonyoung Shim /* Analog input mixers for the capture amplifiers */
13409028935dSPeter Ujfalusi SND_SOC_DAPM_MIXER("Analog Left",
134197b8096dSJoonyoung Shim TWL4030_REG_ANAMICL, 4, 0,
134297b8096dSJoonyoung Shim &twl4030_dapm_analoglmic_controls[0],
134397b8096dSJoonyoung Shim ARRAY_SIZE(twl4030_dapm_analoglmic_controls)),
13449028935dSPeter Ujfalusi SND_SOC_DAPM_MIXER("Analog Right",
134597b8096dSJoonyoung Shim TWL4030_REG_ANAMICR, 4, 0,
134697b8096dSJoonyoung Shim &twl4030_dapm_analogrmic_controls[0],
134797b8096dSJoonyoung Shim ARRAY_SIZE(twl4030_dapm_analogrmic_controls)),
1348276c6222SPeter Ujfalusi
1349fb2a2f84SPeter Ujfalusi SND_SOC_DAPM_PGA("ADC Physical Left",
1350fb2a2f84SPeter Ujfalusi TWL4030_REG_AVADC_CTL, 3, 0, NULL, 0),
1351fb2a2f84SPeter Ujfalusi SND_SOC_DAPM_PGA("ADC Physical Right",
1352fb2a2f84SPeter Ujfalusi TWL4030_REG_AVADC_CTL, 1, 0, NULL, 0),
1353276c6222SPeter Ujfalusi
135401ea6ba2SPeter Ujfalusi SND_SOC_DAPM_PGA_E("Digimic0 Enable",
135501ea6ba2SPeter Ujfalusi TWL4030_REG_ADCMICSEL, 1, 0, NULL, 0,
135601ea6ba2SPeter Ujfalusi digimic_event, SND_SOC_DAPM_POST_PMU),
135701ea6ba2SPeter Ujfalusi SND_SOC_DAPM_PGA_E("Digimic1 Enable",
135801ea6ba2SPeter Ujfalusi TWL4030_REG_ADCMICSEL, 3, 0, NULL, 0,
135901ea6ba2SPeter Ujfalusi digimic_event, SND_SOC_DAPM_POST_PMU),
1360276c6222SPeter Ujfalusi
1361bda7d2a8SPeter Ujfalusi SND_SOC_DAPM_SUPPLY("micbias1 select", TWL4030_REG_MICBIAS_CTL, 5, 0,
1362bda7d2a8SPeter Ujfalusi NULL, 0),
1363bda7d2a8SPeter Ujfalusi SND_SOC_DAPM_SUPPLY("micbias2 select", TWL4030_REG_MICBIAS_CTL, 6, 0,
1364bda7d2a8SPeter Ujfalusi NULL, 0),
1365bda7d2a8SPeter Ujfalusi
1366e04d6e55SPeter Ujfalusi /* Microphone bias */
1367e04d6e55SPeter Ujfalusi SND_SOC_DAPM_SUPPLY("Mic Bias 1",
1368e04d6e55SPeter Ujfalusi TWL4030_REG_MICBIAS_CTL, 0, 0, NULL, 0),
1369e04d6e55SPeter Ujfalusi SND_SOC_DAPM_SUPPLY("Mic Bias 2",
1370e04d6e55SPeter Ujfalusi TWL4030_REG_MICBIAS_CTL, 1, 0, NULL, 0),
1371e04d6e55SPeter Ujfalusi SND_SOC_DAPM_SUPPLY("Headset Mic Bias",
1372e04d6e55SPeter Ujfalusi TWL4030_REG_MICBIAS_CTL, 2, 0, NULL, 0),
13737393958fSPeter Ujfalusi
1374927a7747SPeter Ujfalusi SND_SOC_DAPM_SUPPLY("VIF Enable", TWL4030_REG_VOICE_IF, 0, 0, NULL, 0),
1375cc17557eSSteve Sakoman };
1376cc17557eSSteve Sakoman
1377cc17557eSSteve Sakoman static const struct snd_soc_dapm_route intercon[] = {
13787f51e7d3SPeter Ujfalusi /* Stream -> DAC mapping */
13797f51e7d3SPeter Ujfalusi {"DAC Right1", NULL, "HiFi Playback"},
13807f51e7d3SPeter Ujfalusi {"DAC Left1", NULL, "HiFi Playback"},
13817f51e7d3SPeter Ujfalusi {"DAC Right2", NULL, "HiFi Playback"},
13827f51e7d3SPeter Ujfalusi {"DAC Left2", NULL, "HiFi Playback"},
1383927a7747SPeter Ujfalusi {"DAC Voice", NULL, "VAIFIN"},
13847f51e7d3SPeter Ujfalusi
13857f51e7d3SPeter Ujfalusi /* ADC -> Stream mapping */
13867f51e7d3SPeter Ujfalusi {"HiFi Capture", NULL, "ADC Virtual Left1"},
13877f51e7d3SPeter Ujfalusi {"HiFi Capture", NULL, "ADC Virtual Right1"},
13887f51e7d3SPeter Ujfalusi {"HiFi Capture", NULL, "ADC Virtual Left2"},
13897f51e7d3SPeter Ujfalusi {"HiFi Capture", NULL, "ADC Virtual Right2"},
1390927a7747SPeter Ujfalusi {"VAIFOUT", NULL, "ADC Virtual Left2"},
1391927a7747SPeter Ujfalusi {"VAIFOUT", NULL, "ADC Virtual Right2"},
1392927a7747SPeter Ujfalusi {"VAIFOUT", NULL, "VIF Enable"},
13937f51e7d3SPeter Ujfalusi
13944005d39aSPeter Ujfalusi {"Digital L1 Playback Mixer", NULL, "DAC Left1"},
13954005d39aSPeter Ujfalusi {"Digital R1 Playback Mixer", NULL, "DAC Right1"},
13964005d39aSPeter Ujfalusi {"Digital L2 Playback Mixer", NULL, "DAC Left2"},
13974005d39aSPeter Ujfalusi {"Digital R2 Playback Mixer", NULL, "DAC Right2"},
13984005d39aSPeter Ujfalusi {"Digital Voice Playback Mixer", NULL, "DAC Voice"},
13997393958fSPeter Ujfalusi
14007729cf74SPeter Ujfalusi /* Supply for the digital part (APLL) */
14017729cf74SPeter Ujfalusi {"Digital Voice Playback Mixer", NULL, "APLL Enable"},
14027729cf74SPeter Ujfalusi
140327eeb1feSPeter Ujfalusi {"DAC Left1", NULL, "AIF Enable"},
140427eeb1feSPeter Ujfalusi {"DAC Right1", NULL, "AIF Enable"},
140527eeb1feSPeter Ujfalusi {"DAC Left2", NULL, "AIF Enable"},
140627eeb1feSPeter Ujfalusi {"DAC Right1", NULL, "AIF Enable"},
1407927a7747SPeter Ujfalusi {"DAC Voice", NULL, "VIF Enable"},
140827eeb1feSPeter Ujfalusi
1409c42a59eaSPeter Ujfalusi {"Digital R2 Playback Mixer", NULL, "AIF Enable"},
1410c42a59eaSPeter Ujfalusi {"Digital L2 Playback Mixer", NULL, "AIF Enable"},
1411c42a59eaSPeter Ujfalusi
14124005d39aSPeter Ujfalusi {"Analog L1 Playback Mixer", NULL, "Digital L1 Playback Mixer"},
14134005d39aSPeter Ujfalusi {"Analog R1 Playback Mixer", NULL, "Digital R1 Playback Mixer"},
14144005d39aSPeter Ujfalusi {"Analog L2 Playback Mixer", NULL, "Digital L2 Playback Mixer"},
14154005d39aSPeter Ujfalusi {"Analog R2 Playback Mixer", NULL, "Digital R2 Playback Mixer"},
14164005d39aSPeter Ujfalusi {"Analog Voice Playback Mixer", NULL, "Digital Voice Playback Mixer"},
14171a787e7aSJoonyoung Shim
14185e98a464SPeter Ujfalusi /* Internal playback routings */
14195e98a464SPeter Ujfalusi /* Earpiece */
14204005d39aSPeter Ujfalusi {"Earpiece Mixer", "Voice", "Analog Voice Playback Mixer"},
14214005d39aSPeter Ujfalusi {"Earpiece Mixer", "AudioL1", "Analog L1 Playback Mixer"},
14224005d39aSPeter Ujfalusi {"Earpiece Mixer", "AudioL2", "Analog L2 Playback Mixer"},
14234005d39aSPeter Ujfalusi {"Earpiece Mixer", "AudioR1", "Analog R1 Playback Mixer"},
14249008adf9SPeter Ujfalusi {"Earpiece PGA", NULL, "Earpiece Mixer"},
14252a6f5c58SPeter Ujfalusi /* PreDrivL */
14264005d39aSPeter Ujfalusi {"PredriveL Mixer", "Voice", "Analog Voice Playback Mixer"},
14274005d39aSPeter Ujfalusi {"PredriveL Mixer", "AudioL1", "Analog L1 Playback Mixer"},
14284005d39aSPeter Ujfalusi {"PredriveL Mixer", "AudioL2", "Analog L2 Playback Mixer"},
14294005d39aSPeter Ujfalusi {"PredriveL Mixer", "AudioR2", "Analog R2 Playback Mixer"},
14309008adf9SPeter Ujfalusi {"PredriveL PGA", NULL, "PredriveL Mixer"},
14312a6f5c58SPeter Ujfalusi /* PreDrivR */
14324005d39aSPeter Ujfalusi {"PredriveR Mixer", "Voice", "Analog Voice Playback Mixer"},
14334005d39aSPeter Ujfalusi {"PredriveR Mixer", "AudioR1", "Analog R1 Playback Mixer"},
14344005d39aSPeter Ujfalusi {"PredriveR Mixer", "AudioR2", "Analog R2 Playback Mixer"},
14354005d39aSPeter Ujfalusi {"PredriveR Mixer", "AudioL2", "Analog L2 Playback Mixer"},
14369008adf9SPeter Ujfalusi {"PredriveR PGA", NULL, "PredriveR Mixer"},
1437dfad21a2SPeter Ujfalusi /* HeadsetL */
14384005d39aSPeter Ujfalusi {"HeadsetL Mixer", "Voice", "Analog Voice Playback Mixer"},
14394005d39aSPeter Ujfalusi {"HeadsetL Mixer", "AudioL1", "Analog L1 Playback Mixer"},
14404005d39aSPeter Ujfalusi {"HeadsetL Mixer", "AudioL2", "Analog L2 Playback Mixer"},
14416943c92eSPeter Ujfalusi {"HeadsetL PGA", NULL, "HeadsetL Mixer"},
1442dfad21a2SPeter Ujfalusi /* HeadsetR */
14434005d39aSPeter Ujfalusi {"HeadsetR Mixer", "Voice", "Analog Voice Playback Mixer"},
14444005d39aSPeter Ujfalusi {"HeadsetR Mixer", "AudioR1", "Analog R1 Playback Mixer"},
14454005d39aSPeter Ujfalusi {"HeadsetR Mixer", "AudioR2", "Analog R2 Playback Mixer"},
14466943c92eSPeter Ujfalusi {"HeadsetR PGA", NULL, "HeadsetR Mixer"},
14475152d8c2SPeter Ujfalusi /* CarkitL */
14484005d39aSPeter Ujfalusi {"CarkitL Mixer", "Voice", "Analog Voice Playback Mixer"},
14494005d39aSPeter Ujfalusi {"CarkitL Mixer", "AudioL1", "Analog L1 Playback Mixer"},
14504005d39aSPeter Ujfalusi {"CarkitL Mixer", "AudioL2", "Analog L2 Playback Mixer"},
14519008adf9SPeter Ujfalusi {"CarkitL PGA", NULL, "CarkitL Mixer"},
14525152d8c2SPeter Ujfalusi /* CarkitR */
14534005d39aSPeter Ujfalusi {"CarkitR Mixer", "Voice", "Analog Voice Playback Mixer"},
14544005d39aSPeter Ujfalusi {"CarkitR Mixer", "AudioR1", "Analog R1 Playback Mixer"},
14554005d39aSPeter Ujfalusi {"CarkitR Mixer", "AudioR2", "Analog R2 Playback Mixer"},
14569008adf9SPeter Ujfalusi {"CarkitR PGA", NULL, "CarkitR Mixer"},
1457df339804SPeter Ujfalusi /* HandsfreeL */
14584005d39aSPeter Ujfalusi {"HandsfreeL Mux", "Voice", "Analog Voice Playback Mixer"},
14594005d39aSPeter Ujfalusi {"HandsfreeL Mux", "AudioL1", "Analog L1 Playback Mixer"},
14604005d39aSPeter Ujfalusi {"HandsfreeL Mux", "AudioL2", "Analog L2 Playback Mixer"},
14614005d39aSPeter Ujfalusi {"HandsfreeL Mux", "AudioR2", "Analog R2 Playback Mixer"},
1462e3c7dbb0SLopez Cruz, Misael {"HandsfreeL", "Switch", "HandsfreeL Mux"},
1463e3c7dbb0SLopez Cruz, Misael {"HandsfreeL PGA", NULL, "HandsfreeL"},
1464df339804SPeter Ujfalusi /* HandsfreeR */
14654005d39aSPeter Ujfalusi {"HandsfreeR Mux", "Voice", "Analog Voice Playback Mixer"},
14664005d39aSPeter Ujfalusi {"HandsfreeR Mux", "AudioR1", "Analog R1 Playback Mixer"},
14674005d39aSPeter Ujfalusi {"HandsfreeR Mux", "AudioR2", "Analog R2 Playback Mixer"},
14684005d39aSPeter Ujfalusi {"HandsfreeR Mux", "AudioL2", "Analog L2 Playback Mixer"},
1469e3c7dbb0SLopez Cruz, Misael {"HandsfreeR", "Switch", "HandsfreeR Mux"},
1470e3c7dbb0SLopez Cruz, Misael {"HandsfreeR PGA", NULL, "HandsfreeR"},
1471376f7839SPeter Ujfalusi /* Vibra */
1472376f7839SPeter Ujfalusi {"Vibra Mux", "AudioL1", "DAC Left1"},
1473376f7839SPeter Ujfalusi {"Vibra Mux", "AudioR1", "DAC Right1"},
1474376f7839SPeter Ujfalusi {"Vibra Mux", "AudioL2", "DAC Left2"},
1475376f7839SPeter Ujfalusi {"Vibra Mux", "AudioR2", "DAC Right2"},
14765e98a464SPeter Ujfalusi
1477cc17557eSSteve Sakoman /* outputs */
14787b4c734eSPeter Ujfalusi /* Must be always connected (for AIF and APLL) */
147927eeb1feSPeter Ujfalusi {"Virtual HiFi OUT", NULL, "DAC Left1"},
148027eeb1feSPeter Ujfalusi {"Virtual HiFi OUT", NULL, "DAC Right1"},
148127eeb1feSPeter Ujfalusi {"Virtual HiFi OUT", NULL, "DAC Left2"},
148227eeb1feSPeter Ujfalusi {"Virtual HiFi OUT", NULL, "DAC Right2"},
14837b4c734eSPeter Ujfalusi /* Must be always connected (for APLL) */
14847b4c734eSPeter Ujfalusi {"Virtual Voice OUT", NULL, "Digital Voice Playback Mixer"},
14857b4c734eSPeter Ujfalusi /* Physical outputs */
14869008adf9SPeter Ujfalusi {"EARPIECE", NULL, "Earpiece PGA"},
14879008adf9SPeter Ujfalusi {"PREDRIVEL", NULL, "PredriveL PGA"},
14889008adf9SPeter Ujfalusi {"PREDRIVER", NULL, "PredriveR PGA"},
14896943c92eSPeter Ujfalusi {"HSOL", NULL, "HeadsetL PGA"},
14906943c92eSPeter Ujfalusi {"HSOR", NULL, "HeadsetR PGA"},
14919008adf9SPeter Ujfalusi {"CARKITL", NULL, "CarkitL PGA"},
14929008adf9SPeter Ujfalusi {"CARKITR", NULL, "CarkitR PGA"},
14935a2e9a48SPeter Ujfalusi {"HFL", NULL, "HandsfreeL PGA"},
14945a2e9a48SPeter Ujfalusi {"HFR", NULL, "HandsfreeR PGA"},
1495376f7839SPeter Ujfalusi {"Vibra Route", "Audio", "Vibra Mux"},
1496376f7839SPeter Ujfalusi {"VIBRA", NULL, "Vibra Route"},
1497cc17557eSSteve Sakoman
1498276c6222SPeter Ujfalusi /* Capture path */
14997b4c734eSPeter Ujfalusi /* Must be always connected (for AIF and APLL) */
15007b4c734eSPeter Ujfalusi {"ADC Virtual Left1", NULL, "Virtual HiFi IN"},
15017b4c734eSPeter Ujfalusi {"ADC Virtual Right1", NULL, "Virtual HiFi IN"},
15027b4c734eSPeter Ujfalusi {"ADC Virtual Left2", NULL, "Virtual HiFi IN"},
15037b4c734eSPeter Ujfalusi {"ADC Virtual Right2", NULL, "Virtual HiFi IN"},
15047b4c734eSPeter Ujfalusi /* Physical inputs */
15059028935dSPeter Ujfalusi {"Analog Left", "Main Mic Capture Switch", "MAINMIC"},
15069028935dSPeter Ujfalusi {"Analog Left", "Headset Mic Capture Switch", "HSMIC"},
15079028935dSPeter Ujfalusi {"Analog Left", "AUXL Capture Switch", "AUXL"},
15089028935dSPeter Ujfalusi {"Analog Left", "Carkit Mic Capture Switch", "CARKITMIC"},
1509276c6222SPeter Ujfalusi
15109028935dSPeter Ujfalusi {"Analog Right", "Sub Mic Capture Switch", "SUBMIC"},
15119028935dSPeter Ujfalusi {"Analog Right", "AUXR Capture Switch", "AUXR"},
1512276c6222SPeter Ujfalusi
15139028935dSPeter Ujfalusi {"ADC Physical Left", NULL, "Analog Left"},
15149028935dSPeter Ujfalusi {"ADC Physical Right", NULL, "Analog Right"},
1515276c6222SPeter Ujfalusi
1516276c6222SPeter Ujfalusi {"Digimic0 Enable", NULL, "DIGIMIC0"},
1517276c6222SPeter Ujfalusi {"Digimic1 Enable", NULL, "DIGIMIC1"},
1518276c6222SPeter Ujfalusi
1519bda7d2a8SPeter Ujfalusi {"DIGIMIC0", NULL, "micbias1 select"},
1520bda7d2a8SPeter Ujfalusi {"DIGIMIC1", NULL, "micbias2 select"},
1521bda7d2a8SPeter Ujfalusi
1522276c6222SPeter Ujfalusi /* TX1 Left capture path */
1523fb2a2f84SPeter Ujfalusi {"TX1 Capture Route", "Analog", "ADC Physical Left"},
1524276c6222SPeter Ujfalusi {"TX1 Capture Route", "Digimic0", "Digimic0 Enable"},
1525276c6222SPeter Ujfalusi /* TX1 Right capture path */
1526fb2a2f84SPeter Ujfalusi {"TX1 Capture Route", "Analog", "ADC Physical Right"},
1527276c6222SPeter Ujfalusi {"TX1 Capture Route", "Digimic0", "Digimic0 Enable"},
1528276c6222SPeter Ujfalusi /* TX2 Left capture path */
1529fb2a2f84SPeter Ujfalusi {"TX2 Capture Route", "Analog", "ADC Physical Left"},
1530276c6222SPeter Ujfalusi {"TX2 Capture Route", "Digimic1", "Digimic1 Enable"},
1531276c6222SPeter Ujfalusi /* TX2 Right capture path */
1532fb2a2f84SPeter Ujfalusi {"TX2 Capture Route", "Analog", "ADC Physical Right"},
1533276c6222SPeter Ujfalusi {"TX2 Capture Route", "Digimic1", "Digimic1 Enable"},
1534276c6222SPeter Ujfalusi
1535276c6222SPeter Ujfalusi {"ADC Virtual Left1", NULL, "TX1 Capture Route"},
1536276c6222SPeter Ujfalusi {"ADC Virtual Right1", NULL, "TX1 Capture Route"},
1537276c6222SPeter Ujfalusi {"ADC Virtual Left2", NULL, "TX2 Capture Route"},
1538276c6222SPeter Ujfalusi {"ADC Virtual Right2", NULL, "TX2 Capture Route"},
1539276c6222SPeter Ujfalusi
1540c42a59eaSPeter Ujfalusi {"ADC Virtual Left1", NULL, "AIF Enable"},
1541c42a59eaSPeter Ujfalusi {"ADC Virtual Right1", NULL, "AIF Enable"},
1542c42a59eaSPeter Ujfalusi {"ADC Virtual Left2", NULL, "AIF Enable"},
1543c42a59eaSPeter Ujfalusi {"ADC Virtual Right2", NULL, "AIF Enable"},
1544c42a59eaSPeter Ujfalusi
15457393958fSPeter Ujfalusi /* Analog bypass routes */
15469028935dSPeter Ujfalusi {"Right1 Analog Loopback", "Switch", "Analog Right"},
15479028935dSPeter Ujfalusi {"Left1 Analog Loopback", "Switch", "Analog Left"},
15489028935dSPeter Ujfalusi {"Right2 Analog Loopback", "Switch", "Analog Right"},
15499028935dSPeter Ujfalusi {"Left2 Analog Loopback", "Switch", "Analog Left"},
15509028935dSPeter Ujfalusi {"Voice Analog Loopback", "Switch", "Analog Left"},
15517393958fSPeter Ujfalusi
155278e08e2fSPeter Ujfalusi /* Supply for the Analog loopbacks */
155378e08e2fSPeter Ujfalusi {"Right1 Analog Loopback", NULL, "FM Loop Enable"},
155478e08e2fSPeter Ujfalusi {"Left1 Analog Loopback", NULL, "FM Loop Enable"},
155578e08e2fSPeter Ujfalusi {"Right2 Analog Loopback", NULL, "FM Loop Enable"},
155678e08e2fSPeter Ujfalusi {"Left2 Analog Loopback", NULL, "FM Loop Enable"},
155778e08e2fSPeter Ujfalusi {"Voice Analog Loopback", NULL, "FM Loop Enable"},
155878e08e2fSPeter Ujfalusi
15597393958fSPeter Ujfalusi {"Analog R1 Playback Mixer", NULL, "Right1 Analog Loopback"},
15607393958fSPeter Ujfalusi {"Analog L1 Playback Mixer", NULL, "Left1 Analog Loopback"},
15617393958fSPeter Ujfalusi {"Analog R2 Playback Mixer", NULL, "Right2 Analog Loopback"},
15627393958fSPeter Ujfalusi {"Analog L2 Playback Mixer", NULL, "Left2 Analog Loopback"},
1563fcd274a3SLopez Cruz, Misael {"Analog Voice Playback Mixer", NULL, "Voice Analog Loopback"},
15647393958fSPeter Ujfalusi
15656bab83fdSPeter Ujfalusi /* Digital bypass routes */
15666bab83fdSPeter Ujfalusi {"Right Digital Loopback", "Volume", "TX1 Capture Route"},
15676bab83fdSPeter Ujfalusi {"Left Digital Loopback", "Volume", "TX1 Capture Route"},
1568ee8f6894SLopez Cruz, Misael {"Voice Digital Loopback", "Volume", "TX2 Capture Route"},
15696bab83fdSPeter Ujfalusi
15704005d39aSPeter Ujfalusi {"Digital R2 Playback Mixer", NULL, "Right Digital Loopback"},
15714005d39aSPeter Ujfalusi {"Digital L2 Playback Mixer", NULL, "Left Digital Loopback"},
15724005d39aSPeter Ujfalusi {"Digital Voice Playback Mixer", NULL, "Voice Digital Loopback"},
15736bab83fdSPeter Ujfalusi
1574cc17557eSSteve Sakoman };
1575cc17557eSSteve Sakoman
twl4030_set_bias_level(struct snd_soc_component * component,enum snd_soc_bias_level level)1576c68e7f5bSKuninori Morimoto static int twl4030_set_bias_level(struct snd_soc_component *component,
1577cc17557eSSteve Sakoman enum snd_soc_bias_level level)
1578cc17557eSSteve Sakoman {
1579cc17557eSSteve Sakoman switch (level) {
1580cc17557eSSteve Sakoman case SND_SOC_BIAS_ON:
1581cc17557eSSteve Sakoman break;
1582cc17557eSSteve Sakoman case SND_SOC_BIAS_PREPARE:
1583cc17557eSSteve Sakoman break;
1584cc17557eSSteve Sakoman case SND_SOC_BIAS_STANDBY:
1585c68e7f5bSKuninori Morimoto if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)
1586c68e7f5bSKuninori Morimoto twl4030_codec_enable(component, 1);
1587cc17557eSSteve Sakoman break;
1588cc17557eSSteve Sakoman case SND_SOC_BIAS_OFF:
1589c68e7f5bSKuninori Morimoto twl4030_codec_enable(component, 0);
1590cc17557eSSteve Sakoman break;
1591cc17557eSSteve Sakoman }
1592cc17557eSSteve Sakoman
1593cc17557eSSteve Sakoman return 0;
1594cc17557eSSteve Sakoman }
1595cc17557eSSteve Sakoman
twl4030_constraints(struct twl4030_priv * twl4030,struct snd_pcm_substream * mst_substream)15966b87a91fSPeter Ujfalusi static void twl4030_constraints(struct twl4030_priv *twl4030,
15976b87a91fSPeter Ujfalusi struct snd_pcm_substream *mst_substream)
15986b87a91fSPeter Ujfalusi {
15996b87a91fSPeter Ujfalusi struct snd_pcm_substream *slv_substream;
16006b87a91fSPeter Ujfalusi
16016b87a91fSPeter Ujfalusi /* Pick the stream, which need to be constrained */
16026b87a91fSPeter Ujfalusi if (mst_substream == twl4030->master_substream)
16036b87a91fSPeter Ujfalusi slv_substream = twl4030->slave_substream;
16046b87a91fSPeter Ujfalusi else if (mst_substream == twl4030->slave_substream)
16056b87a91fSPeter Ujfalusi slv_substream = twl4030->master_substream;
16066b87a91fSPeter Ujfalusi else /* This should not happen.. */
16076b87a91fSPeter Ujfalusi return;
16086b87a91fSPeter Ujfalusi
16096b87a91fSPeter Ujfalusi /* Set the constraints according to the already configured stream */
1610e795d831SLars-Peter Clausen snd_pcm_hw_constraint_single(slv_substream->runtime,
16116b87a91fSPeter Ujfalusi SNDRV_PCM_HW_PARAM_RATE,
16126b87a91fSPeter Ujfalusi twl4030->rate);
16136b87a91fSPeter Ujfalusi
1614e795d831SLars-Peter Clausen snd_pcm_hw_constraint_single(slv_substream->runtime,
16156b87a91fSPeter Ujfalusi SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
16166b87a91fSPeter Ujfalusi twl4030->sample_bits);
16176b87a91fSPeter Ujfalusi
1618e795d831SLars-Peter Clausen snd_pcm_hw_constraint_single(slv_substream->runtime,
16196b87a91fSPeter Ujfalusi SNDRV_PCM_HW_PARAM_CHANNELS,
16206b87a91fSPeter Ujfalusi twl4030->channels);
16216b87a91fSPeter Ujfalusi }
16226b87a91fSPeter Ujfalusi
16238a1f936aSPeter Ujfalusi /* In case of 4 channel mode, the RX1 L/R for playback and the TX2 L/R for
16248a1f936aSPeter Ujfalusi * capture has to be enabled/disabled. */
twl4030_tdm_enable(struct snd_soc_component * component,int direction,int enable)1625c68e7f5bSKuninori Morimoto static void twl4030_tdm_enable(struct snd_soc_component *component, int direction,
16268a1f936aSPeter Ujfalusi int enable)
16278a1f936aSPeter Ujfalusi {
16288a1f936aSPeter Ujfalusi u8 reg, mask;
16298a1f936aSPeter Ujfalusi
1630c68e7f5bSKuninori Morimoto reg = twl4030_read(component, TWL4030_REG_OPTION);
16318a1f936aSPeter Ujfalusi
16328a1f936aSPeter Ujfalusi if (direction == SNDRV_PCM_STREAM_PLAYBACK)
16338a1f936aSPeter Ujfalusi mask = TWL4030_ARXL1_VRX_EN | TWL4030_ARXR1_EN;
16348a1f936aSPeter Ujfalusi else
16358a1f936aSPeter Ujfalusi mask = TWL4030_ATXL2_VTXL_EN | TWL4030_ATXR2_VTXR_EN;
16368a1f936aSPeter Ujfalusi
16378a1f936aSPeter Ujfalusi if (enable)
16388a1f936aSPeter Ujfalusi reg |= mask;
16398a1f936aSPeter Ujfalusi else
16408a1f936aSPeter Ujfalusi reg &= ~mask;
16418a1f936aSPeter Ujfalusi
1642c68e7f5bSKuninori Morimoto twl4030_write(component, TWL4030_REG_OPTION, reg);
16438a1f936aSPeter Ujfalusi }
16448a1f936aSPeter Ujfalusi
twl4030_startup(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)1645d6648da1SPeter Ujfalusi static int twl4030_startup(struct snd_pcm_substream *substream,
1646d6648da1SPeter Ujfalusi struct snd_soc_dai *dai)
16477220b9f4SPeter Ujfalusi {
1648c68e7f5bSKuninori Morimoto struct snd_soc_component *component = dai->component;
1649c68e7f5bSKuninori Morimoto struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
16507220b9f4SPeter Ujfalusi
16517220b9f4SPeter Ujfalusi if (twl4030->master_substream) {
16527220b9f4SPeter Ujfalusi twl4030->slave_substream = substream;
16536b87a91fSPeter Ujfalusi /* The DAI has one configuration for playback and capture, so
16546b87a91fSPeter Ujfalusi * if the DAI has been already configured then constrain this
16556b87a91fSPeter Ujfalusi * substream to match it. */
16566b87a91fSPeter Ujfalusi if (twl4030->configured)
16576b87a91fSPeter Ujfalusi twl4030_constraints(twl4030, twl4030->master_substream);
16586b87a91fSPeter Ujfalusi } else {
1659c68e7f5bSKuninori Morimoto if (!(twl4030_read(component, TWL4030_REG_CODEC_MODE) &
16608a1f936aSPeter Ujfalusi TWL4030_OPTION_1)) {
16618a1f936aSPeter Ujfalusi /* In option2 4 channel is not supported, set the
16628a1f936aSPeter Ujfalusi * constraint for the first stream for channels, the
16638a1f936aSPeter Ujfalusi * second stream will 'inherit' this cosntraint */
1664e795d831SLars-Peter Clausen snd_pcm_hw_constraint_single(substream->runtime,
16658a1f936aSPeter Ujfalusi SNDRV_PCM_HW_PARAM_CHANNELS,
1666e795d831SLars-Peter Clausen 2);
16678a1f936aSPeter Ujfalusi }
16687220b9f4SPeter Ujfalusi twl4030->master_substream = substream;
16696b87a91fSPeter Ujfalusi }
16707220b9f4SPeter Ujfalusi
16717220b9f4SPeter Ujfalusi return 0;
16727220b9f4SPeter Ujfalusi }
16737220b9f4SPeter Ujfalusi
twl4030_shutdown(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)1674d6648da1SPeter Ujfalusi static void twl4030_shutdown(struct snd_pcm_substream *substream,
1675d6648da1SPeter Ujfalusi struct snd_soc_dai *dai)
16767220b9f4SPeter Ujfalusi {
1677c68e7f5bSKuninori Morimoto struct snd_soc_component *component = dai->component;
1678c68e7f5bSKuninori Morimoto struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
16797220b9f4SPeter Ujfalusi
16807220b9f4SPeter Ujfalusi if (twl4030->master_substream == substream)
16817220b9f4SPeter Ujfalusi twl4030->master_substream = twl4030->slave_substream;
16827220b9f4SPeter Ujfalusi
16837220b9f4SPeter Ujfalusi twl4030->slave_substream = NULL;
16846b87a91fSPeter Ujfalusi
16856b87a91fSPeter Ujfalusi /* If all streams are closed, or the remaining stream has not yet
16866b87a91fSPeter Ujfalusi * been configured than set the DAI as not configured. */
16876b87a91fSPeter Ujfalusi if (!twl4030->master_substream)
16886b87a91fSPeter Ujfalusi twl4030->configured = 0;
16896b87a91fSPeter Ujfalusi else if (!twl4030->master_substream->runtime->channels)
16906b87a91fSPeter Ujfalusi twl4030->configured = 0;
16918a1f936aSPeter Ujfalusi
16928a1f936aSPeter Ujfalusi /* If the closing substream had 4 channel, do the necessary cleanup */
16938a1f936aSPeter Ujfalusi if (substream->runtime->channels == 4)
1694c68e7f5bSKuninori Morimoto twl4030_tdm_enable(component, substream->stream, 0);
16957220b9f4SPeter Ujfalusi }
16967220b9f4SPeter Ujfalusi
twl4030_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)1697cc17557eSSteve Sakoman static int twl4030_hw_params(struct snd_pcm_substream *substream,
1698dee89c4dSMark Brown struct snd_pcm_hw_params *params,
1699dee89c4dSMark Brown struct snd_soc_dai *dai)
1700cc17557eSSteve Sakoman {
1701c68e7f5bSKuninori Morimoto struct snd_soc_component *component = dai->component;
1702c68e7f5bSKuninori Morimoto struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
1703cc17557eSSteve Sakoman u8 mode, old_mode, format, old_format;
1704cc17557eSSteve Sakoman
17058a1f936aSPeter Ujfalusi /* If the substream has 4 channel, do the necessary setup */
17068a1f936aSPeter Ujfalusi if (params_channels(params) == 4) {
1707c68e7f5bSKuninori Morimoto format = twl4030_read(component, TWL4030_REG_AUDIO_IF);
1708c68e7f5bSKuninori Morimoto mode = twl4030_read(component, TWL4030_REG_CODEC_MODE);
1709eaf1ac8bSPeter Ujfalusi
1710eaf1ac8bSPeter Ujfalusi /* Safety check: are we in the correct operating mode and
1711eaf1ac8bSPeter Ujfalusi * the interface is in TDM mode? */
1712eaf1ac8bSPeter Ujfalusi if ((mode & TWL4030_OPTION_1) &&
1713eaf1ac8bSPeter Ujfalusi ((format & TWL4030_AIF_FORMAT) == TWL4030_AIF_FORMAT_TDM))
1714c68e7f5bSKuninori Morimoto twl4030_tdm_enable(component, substream->stream, 1);
17158a1f936aSPeter Ujfalusi else
17168a1f936aSPeter Ujfalusi return -EINVAL;
17178a1f936aSPeter Ujfalusi }
17188a1f936aSPeter Ujfalusi
17196b87a91fSPeter Ujfalusi if (twl4030->configured)
17206b87a91fSPeter Ujfalusi /* Ignoring hw_params for already configured DAI */
17217220b9f4SPeter Ujfalusi return 0;
17227220b9f4SPeter Ujfalusi
1723cc17557eSSteve Sakoman /* bit rate */
1724c68e7f5bSKuninori Morimoto old_mode = twl4030_read(component,
1725cc17557eSSteve Sakoman TWL4030_REG_CODEC_MODE) & ~TWL4030_CODECPDZ;
1726cc17557eSSteve Sakoman mode = old_mode & ~TWL4030_APLL_RATE;
1727cc17557eSSteve Sakoman
1728cc17557eSSteve Sakoman switch (params_rate(params)) {
1729cc17557eSSteve Sakoman case 8000:
1730cc17557eSSteve Sakoman mode |= TWL4030_APLL_RATE_8000;
1731cc17557eSSteve Sakoman break;
1732cc17557eSSteve Sakoman case 11025:
1733cc17557eSSteve Sakoman mode |= TWL4030_APLL_RATE_11025;
1734cc17557eSSteve Sakoman break;
1735cc17557eSSteve Sakoman case 12000:
1736cc17557eSSteve Sakoman mode |= TWL4030_APLL_RATE_12000;
1737cc17557eSSteve Sakoman break;
1738cc17557eSSteve Sakoman case 16000:
1739cc17557eSSteve Sakoman mode |= TWL4030_APLL_RATE_16000;
1740cc17557eSSteve Sakoman break;
1741cc17557eSSteve Sakoman case 22050:
1742cc17557eSSteve Sakoman mode |= TWL4030_APLL_RATE_22050;
1743cc17557eSSteve Sakoman break;
1744cc17557eSSteve Sakoman case 24000:
1745cc17557eSSteve Sakoman mode |= TWL4030_APLL_RATE_24000;
1746cc17557eSSteve Sakoman break;
1747cc17557eSSteve Sakoman case 32000:
1748cc17557eSSteve Sakoman mode |= TWL4030_APLL_RATE_32000;
1749cc17557eSSteve Sakoman break;
1750cc17557eSSteve Sakoman case 44100:
1751cc17557eSSteve Sakoman mode |= TWL4030_APLL_RATE_44100;
1752cc17557eSSteve Sakoman break;
1753cc17557eSSteve Sakoman case 48000:
1754cc17557eSSteve Sakoman mode |= TWL4030_APLL_RATE_48000;
1755cc17557eSSteve Sakoman break;
1756103f211dSPeter Ujfalusi case 96000:
1757103f211dSPeter Ujfalusi mode |= TWL4030_APLL_RATE_96000;
1758103f211dSPeter Ujfalusi break;
1759cc17557eSSteve Sakoman default:
1760c68e7f5bSKuninori Morimoto dev_err(component->dev, "%s: unknown rate %d\n", __func__,
1761cc17557eSSteve Sakoman params_rate(params));
1762cc17557eSSteve Sakoman return -EINVAL;
1763cc17557eSSteve Sakoman }
1764cc17557eSSteve Sakoman
1765cc17557eSSteve Sakoman /* sample size */
1766c68e7f5bSKuninori Morimoto old_format = twl4030_read(component, TWL4030_REG_AUDIO_IF);
1767cc17557eSSteve Sakoman format = old_format;
1768cc17557eSSteve Sakoman format &= ~TWL4030_DATA_WIDTH;
176904f630d8SMark Brown switch (params_width(params)) {
177004f630d8SMark Brown case 16:
1771cc17557eSSteve Sakoman format |= TWL4030_DATA_WIDTH_16S_16W;
1772cc17557eSSteve Sakoman break;
177304f630d8SMark Brown case 32:
1774cc17557eSSteve Sakoman format |= TWL4030_DATA_WIDTH_32S_24W;
1775cc17557eSSteve Sakoman break;
1776cc17557eSSteve Sakoman default:
1777c68e7f5bSKuninori Morimoto dev_err(component->dev, "%s: unsupported bits/sample %d\n",
177804f630d8SMark Brown __func__, params_width(params));
1779cc17557eSSteve Sakoman return -EINVAL;
1780cc17557eSSteve Sakoman }
1781cc17557eSSteve Sakoman
17822046f175SPeter Ujfalusi if (format != old_format || mode != old_mode) {
17832046f175SPeter Ujfalusi if (twl4030->codec_powered) {
17842046f175SPeter Ujfalusi /*
17852046f175SPeter Ujfalusi * If the codec is powered, than we need to toggle the
17862046f175SPeter Ujfalusi * codec power.
17872046f175SPeter Ujfalusi */
1788c68e7f5bSKuninori Morimoto twl4030_codec_enable(component, 0);
1789c68e7f5bSKuninori Morimoto twl4030_write(component, TWL4030_REG_CODEC_MODE, mode);
1790c68e7f5bSKuninori Morimoto twl4030_write(component, TWL4030_REG_AUDIO_IF, format);
1791c68e7f5bSKuninori Morimoto twl4030_codec_enable(component, 1);
17922046f175SPeter Ujfalusi } else {
1793c68e7f5bSKuninori Morimoto twl4030_write(component, TWL4030_REG_CODEC_MODE, mode);
1794c68e7f5bSKuninori Morimoto twl4030_write(component, TWL4030_REG_AUDIO_IF, format);
17952046f175SPeter Ujfalusi }
1796cc17557eSSteve Sakoman }
17976b87a91fSPeter Ujfalusi
17986b87a91fSPeter Ujfalusi /* Store the important parameters for the DAI configuration and set
17996b87a91fSPeter Ujfalusi * the DAI as configured */
18006b87a91fSPeter Ujfalusi twl4030->configured = 1;
18016b87a91fSPeter Ujfalusi twl4030->rate = params_rate(params);
18026b87a91fSPeter Ujfalusi twl4030->sample_bits = hw_param_interval(params,
18036b87a91fSPeter Ujfalusi SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min;
18046b87a91fSPeter Ujfalusi twl4030->channels = params_channels(params);
18056b87a91fSPeter Ujfalusi
18066b87a91fSPeter Ujfalusi /* If both playback and capture streams are open, and one of them
18076b87a91fSPeter Ujfalusi * is setting the hw parameters right now (since we are here), set
18086b87a91fSPeter Ujfalusi * constraints to the other stream to match the current one. */
18096b87a91fSPeter Ujfalusi if (twl4030->slave_substream)
18106b87a91fSPeter Ujfalusi twl4030_constraints(twl4030, substream);
18116b87a91fSPeter Ujfalusi
1812cc17557eSSteve Sakoman return 0;
1813cc17557eSSteve Sakoman }
1814cc17557eSSteve Sakoman
twl4030_set_dai_sysclk(struct snd_soc_dai * codec_dai,int clk_id,unsigned int freq,int dir)18157ded5fe0SPeter Ujfalusi static int twl4030_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id,
18167ded5fe0SPeter Ujfalusi unsigned int freq, int dir)
1817cc17557eSSteve Sakoman {
1818c68e7f5bSKuninori Morimoto struct snd_soc_component *component = codec_dai->component;
1819c68e7f5bSKuninori Morimoto struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
1820cc17557eSSteve Sakoman
1821cc17557eSSteve Sakoman switch (freq) {
1822cc17557eSSteve Sakoman case 19200000:
1823cc17557eSSteve Sakoman case 26000000:
1824cc17557eSSteve Sakoman case 38400000:
1825cc17557eSSteve Sakoman break;
1826cc17557eSSteve Sakoman default:
1827c68e7f5bSKuninori Morimoto dev_err(component->dev, "Unsupported HFCLKIN: %u\n", freq);
1828cc17557eSSteve Sakoman return -EINVAL;
1829cc17557eSSteve Sakoman }
1830cc17557eSSteve Sakoman
183168d01955SPeter Ujfalusi if ((freq / 1000) != twl4030->sysclk) {
1832c68e7f5bSKuninori Morimoto dev_err(component->dev,
18333b8a0795SPeter Ujfalusi "Mismatch in HFCLKIN: %u (configured: %u)\n",
183468d01955SPeter Ujfalusi freq, twl4030->sysclk * 1000);
183568d01955SPeter Ujfalusi return -EINVAL;
183668d01955SPeter Ujfalusi }
1837cc17557eSSteve Sakoman
1838cc17557eSSteve Sakoman return 0;
1839cc17557eSSteve Sakoman }
1840cc17557eSSteve Sakoman
twl4030_set_dai_fmt(struct snd_soc_dai * codec_dai,unsigned int fmt)18417ded5fe0SPeter Ujfalusi static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
1842cc17557eSSteve Sakoman {
1843c68e7f5bSKuninori Morimoto struct snd_soc_component *component = codec_dai->component;
1844c68e7f5bSKuninori Morimoto struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
1845cc17557eSSteve Sakoman u8 old_format, format;
1846cc17557eSSteve Sakoman
1847cc17557eSSteve Sakoman /* get format */
1848c68e7f5bSKuninori Morimoto old_format = twl4030_read(component, TWL4030_REG_AUDIO_IF);
1849cc17557eSSteve Sakoman format = old_format;
1850cc17557eSSteve Sakoman
18517effe2cdSMark Brown switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
18527effe2cdSMark Brown case SND_SOC_DAIFMT_CBP_CFP:
1853cc17557eSSteve Sakoman format &= ~(TWL4030_AIF_SLAVE_EN);
1854e18c94d2SGrazvydas Ignotas format &= ~(TWL4030_CLK256FS_EN);
1855cc17557eSSteve Sakoman break;
18567effe2cdSMark Brown case SND_SOC_DAIFMT_CBC_CFC:
1857cc17557eSSteve Sakoman format |= TWL4030_AIF_SLAVE_EN;
1858e18c94d2SGrazvydas Ignotas format |= TWL4030_CLK256FS_EN;
1859cc17557eSSteve Sakoman break;
1860cc17557eSSteve Sakoman default:
1861cc17557eSSteve Sakoman return -EINVAL;
1862cc17557eSSteve Sakoman }
1863cc17557eSSteve Sakoman
1864cc17557eSSteve Sakoman /* interface format */
1865cc17557eSSteve Sakoman format &= ~TWL4030_AIF_FORMAT;
1866cc17557eSSteve Sakoman switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
1867cc17557eSSteve Sakoman case SND_SOC_DAIFMT_I2S:
1868cc17557eSSteve Sakoman format |= TWL4030_AIF_FORMAT_CODEC;
1869cc17557eSSteve Sakoman break;
18708a1f936aSPeter Ujfalusi case SND_SOC_DAIFMT_DSP_A:
18718a1f936aSPeter Ujfalusi format |= TWL4030_AIF_FORMAT_TDM;
18728a1f936aSPeter Ujfalusi break;
1873cc17557eSSteve Sakoman default:
1874cc17557eSSteve Sakoman return -EINVAL;
1875cc17557eSSteve Sakoman }
1876cc17557eSSteve Sakoman
1877cc17557eSSteve Sakoman if (format != old_format) {
18782046f175SPeter Ujfalusi if (twl4030->codec_powered) {
18792046f175SPeter Ujfalusi /*
18802046f175SPeter Ujfalusi * If the codec is powered, than we need to toggle the
18812046f175SPeter Ujfalusi * codec power.
18822046f175SPeter Ujfalusi */
1883c68e7f5bSKuninori Morimoto twl4030_codec_enable(component, 0);
1884c68e7f5bSKuninori Morimoto twl4030_write(component, TWL4030_REG_AUDIO_IF, format);
1885c68e7f5bSKuninori Morimoto twl4030_codec_enable(component, 1);
18862046f175SPeter Ujfalusi } else {
1887c68e7f5bSKuninori Morimoto twl4030_write(component, TWL4030_REG_AUDIO_IF, format);
18882046f175SPeter Ujfalusi }
1889cc17557eSSteve Sakoman }
1890cc17557eSSteve Sakoman
1891cc17557eSSteve Sakoman return 0;
1892cc17557eSSteve Sakoman }
1893cc17557eSSteve Sakoman
twl4030_set_tristate(struct snd_soc_dai * dai,int tristate)189468140443SLopez Cruz, Misael static int twl4030_set_tristate(struct snd_soc_dai *dai, int tristate)
189568140443SLopez Cruz, Misael {
1896c68e7f5bSKuninori Morimoto struct snd_soc_component *component = dai->component;
1897c68e7f5bSKuninori Morimoto u8 reg = twl4030_read(component, TWL4030_REG_AUDIO_IF);
189868140443SLopez Cruz, Misael
189968140443SLopez Cruz, Misael if (tristate)
190068140443SLopez Cruz, Misael reg |= TWL4030_AIF_TRI_EN;
190168140443SLopez Cruz, Misael else
190268140443SLopez Cruz, Misael reg &= ~TWL4030_AIF_TRI_EN;
190368140443SLopez Cruz, Misael
1904c68e7f5bSKuninori Morimoto return twl4030_write(component, TWL4030_REG_AUDIO_IF, reg);
190568140443SLopez Cruz, Misael }
190668140443SLopez Cruz, Misael
1907b7a755a8SMisael Lopez Cruz /* In case of voice mode, the RX1 L(VRX) for downlink and the TX2 L/R
1908b7a755a8SMisael Lopez Cruz * (VTXL, VTXR) for uplink has to be enabled/disabled. */
twl4030_voice_enable(struct snd_soc_component * component,int direction,int enable)1909c68e7f5bSKuninori Morimoto static void twl4030_voice_enable(struct snd_soc_component *component, int direction,
1910b7a755a8SMisael Lopez Cruz int enable)
1911b7a755a8SMisael Lopez Cruz {
1912b7a755a8SMisael Lopez Cruz u8 reg, mask;
1913b7a755a8SMisael Lopez Cruz
1914c68e7f5bSKuninori Morimoto reg = twl4030_read(component, TWL4030_REG_OPTION);
1915b7a755a8SMisael Lopez Cruz
1916b7a755a8SMisael Lopez Cruz if (direction == SNDRV_PCM_STREAM_PLAYBACK)
1917b7a755a8SMisael Lopez Cruz mask = TWL4030_ARXL1_VRX_EN;
1918b7a755a8SMisael Lopez Cruz else
1919b7a755a8SMisael Lopez Cruz mask = TWL4030_ATXL2_VTXL_EN | TWL4030_ATXR2_VTXR_EN;
1920b7a755a8SMisael Lopez Cruz
1921b7a755a8SMisael Lopez Cruz if (enable)
1922b7a755a8SMisael Lopez Cruz reg |= mask;
1923b7a755a8SMisael Lopez Cruz else
1924b7a755a8SMisael Lopez Cruz reg &= ~mask;
1925b7a755a8SMisael Lopez Cruz
1926c68e7f5bSKuninori Morimoto twl4030_write(component, TWL4030_REG_OPTION, reg);
1927b7a755a8SMisael Lopez Cruz }
1928b7a755a8SMisael Lopez Cruz
twl4030_voice_startup(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)19297154b3e8SJoonyoung Shim static int twl4030_voice_startup(struct snd_pcm_substream *substream,
19307154b3e8SJoonyoung Shim struct snd_soc_dai *dai)
19317154b3e8SJoonyoung Shim {
1932c68e7f5bSKuninori Morimoto struct snd_soc_component *component = dai->component;
1933c68e7f5bSKuninori Morimoto struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
19347154b3e8SJoonyoung Shim u8 mode;
19357154b3e8SJoonyoung Shim
19367154b3e8SJoonyoung Shim /* If the system master clock is not 26MHz, the voice PCM interface is
193725985edcSLucas De Marchi * not available.
19387154b3e8SJoonyoung Shim */
193968d01955SPeter Ujfalusi if (twl4030->sysclk != 26000) {
1940c68e7f5bSKuninori Morimoto dev_err(component->dev,
19413b8a0795SPeter Ujfalusi "%s: HFCLKIN is %u KHz, voice interface needs 26MHz\n",
19423b8a0795SPeter Ujfalusi __func__, twl4030->sysclk);
19437154b3e8SJoonyoung Shim return -EINVAL;
19447154b3e8SJoonyoung Shim }
19457154b3e8SJoonyoung Shim
19467154b3e8SJoonyoung Shim /* If the codec mode is not option2, the voice PCM interface is not
194725985edcSLucas De Marchi * available.
19487154b3e8SJoonyoung Shim */
1949c68e7f5bSKuninori Morimoto mode = twl4030_read(component, TWL4030_REG_CODEC_MODE)
19507154b3e8SJoonyoung Shim & TWL4030_OPT_MODE;
19517154b3e8SJoonyoung Shim
19527154b3e8SJoonyoung Shim if (mode != TWL4030_OPTION_2) {
1953c68e7f5bSKuninori Morimoto dev_err(component->dev, "%s: the codec mode is not option2\n",
19543b8a0795SPeter Ujfalusi __func__);
19557154b3e8SJoonyoung Shim return -EINVAL;
19567154b3e8SJoonyoung Shim }
19577154b3e8SJoonyoung Shim
19587154b3e8SJoonyoung Shim return 0;
19597154b3e8SJoonyoung Shim }
19607154b3e8SJoonyoung Shim
twl4030_voice_shutdown(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)1961b7a755a8SMisael Lopez Cruz static void twl4030_voice_shutdown(struct snd_pcm_substream *substream,
1962b7a755a8SMisael Lopez Cruz struct snd_soc_dai *dai)
1963b7a755a8SMisael Lopez Cruz {
1964c68e7f5bSKuninori Morimoto struct snd_soc_component *component = dai->component;
1965b7a755a8SMisael Lopez Cruz
1966b7a755a8SMisael Lopez Cruz /* Enable voice digital filters */
1967c68e7f5bSKuninori Morimoto twl4030_voice_enable(component, substream->stream, 0);
1968b7a755a8SMisael Lopez Cruz }
1969b7a755a8SMisael Lopez Cruz
twl4030_voice_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)19707154b3e8SJoonyoung Shim static int twl4030_voice_hw_params(struct snd_pcm_substream *substream,
19717ded5fe0SPeter Ujfalusi struct snd_pcm_hw_params *params,
19727ded5fe0SPeter Ujfalusi struct snd_soc_dai *dai)
19737154b3e8SJoonyoung Shim {
1974c68e7f5bSKuninori Morimoto struct snd_soc_component *component = dai->component;
1975c68e7f5bSKuninori Morimoto struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
19767154b3e8SJoonyoung Shim u8 old_mode, mode;
19777154b3e8SJoonyoung Shim
1978b7a755a8SMisael Lopez Cruz /* Enable voice digital filters */
1979c68e7f5bSKuninori Morimoto twl4030_voice_enable(component, substream->stream, 1);
1980b7a755a8SMisael Lopez Cruz
19817154b3e8SJoonyoung Shim /* bit rate */
1982c68e7f5bSKuninori Morimoto old_mode = twl4030_read(component,
19837ded5fe0SPeter Ujfalusi TWL4030_REG_CODEC_MODE) & ~TWL4030_CODECPDZ;
19847154b3e8SJoonyoung Shim mode = old_mode;
19857154b3e8SJoonyoung Shim
19867154b3e8SJoonyoung Shim switch (params_rate(params)) {
19877154b3e8SJoonyoung Shim case 8000:
19887154b3e8SJoonyoung Shim mode &= ~(TWL4030_SEL_16K);
19897154b3e8SJoonyoung Shim break;
19907154b3e8SJoonyoung Shim case 16000:
19917154b3e8SJoonyoung Shim mode |= TWL4030_SEL_16K;
19927154b3e8SJoonyoung Shim break;
19937154b3e8SJoonyoung Shim default:
1994c68e7f5bSKuninori Morimoto dev_err(component->dev, "%s: unknown rate %d\n", __func__,
19957154b3e8SJoonyoung Shim params_rate(params));
19967154b3e8SJoonyoung Shim return -EINVAL;
19977154b3e8SJoonyoung Shim }
19987154b3e8SJoonyoung Shim
19997154b3e8SJoonyoung Shim if (mode != old_mode) {
20002046f175SPeter Ujfalusi if (twl4030->codec_powered) {
20012046f175SPeter Ujfalusi /*
20022046f175SPeter Ujfalusi * If the codec is powered, than we need to toggle the
20032046f175SPeter Ujfalusi * codec power.
20042046f175SPeter Ujfalusi */
2005c68e7f5bSKuninori Morimoto twl4030_codec_enable(component, 0);
2006c68e7f5bSKuninori Morimoto twl4030_write(component, TWL4030_REG_CODEC_MODE, mode);
2007c68e7f5bSKuninori Morimoto twl4030_codec_enable(component, 1);
20082046f175SPeter Ujfalusi } else {
2009c68e7f5bSKuninori Morimoto twl4030_write(component, TWL4030_REG_CODEC_MODE, mode);
20102046f175SPeter Ujfalusi }
20117154b3e8SJoonyoung Shim }
20127154b3e8SJoonyoung Shim
20137154b3e8SJoonyoung Shim return 0;
20147154b3e8SJoonyoung Shim }
20157154b3e8SJoonyoung Shim
twl4030_voice_set_dai_sysclk(struct snd_soc_dai * codec_dai,int clk_id,unsigned int freq,int dir)20167154b3e8SJoonyoung Shim static int twl4030_voice_set_dai_sysclk(struct snd_soc_dai *codec_dai,
20177154b3e8SJoonyoung Shim int clk_id, unsigned int freq, int dir)
20187154b3e8SJoonyoung Shim {
2019c68e7f5bSKuninori Morimoto struct snd_soc_component *component = codec_dai->component;
2020c68e7f5bSKuninori Morimoto struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
20217154b3e8SJoonyoung Shim
202268d01955SPeter Ujfalusi if (freq != 26000000) {
2023c68e7f5bSKuninori Morimoto dev_err(component->dev,
20243b8a0795SPeter Ujfalusi "%s: HFCLKIN is %u KHz, voice interface needs 26MHz\n",
20253b8a0795SPeter Ujfalusi __func__, freq / 1000);
20267154b3e8SJoonyoung Shim return -EINVAL;
20277154b3e8SJoonyoung Shim }
202868d01955SPeter Ujfalusi if ((freq / 1000) != twl4030->sysclk) {
2029c68e7f5bSKuninori Morimoto dev_err(component->dev,
20303b8a0795SPeter Ujfalusi "Mismatch in HFCLKIN: %u (configured: %u)\n",
203168d01955SPeter Ujfalusi freq, twl4030->sysclk * 1000);
203268d01955SPeter Ujfalusi return -EINVAL;
203368d01955SPeter Ujfalusi }
20347154b3e8SJoonyoung Shim return 0;
20357154b3e8SJoonyoung Shim }
20367154b3e8SJoonyoung Shim
twl4030_voice_set_dai_fmt(struct snd_soc_dai * codec_dai,unsigned int fmt)20377154b3e8SJoonyoung Shim static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai,
20387154b3e8SJoonyoung Shim unsigned int fmt)
20397154b3e8SJoonyoung Shim {
2040c68e7f5bSKuninori Morimoto struct snd_soc_component *component = codec_dai->component;
2041c68e7f5bSKuninori Morimoto struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
20427154b3e8SJoonyoung Shim u8 old_format, format;
20437154b3e8SJoonyoung Shim
20447154b3e8SJoonyoung Shim /* get format */
2045c68e7f5bSKuninori Morimoto old_format = twl4030_read(component, TWL4030_REG_VOICE_IF);
20467154b3e8SJoonyoung Shim format = old_format;
20477154b3e8SJoonyoung Shim
20487effe2cdSMark Brown switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
20497effe2cdSMark Brown case SND_SOC_DAIFMT_CBP_CFP:
20507154b3e8SJoonyoung Shim format &= ~(TWL4030_VIF_SLAVE_EN);
20517154b3e8SJoonyoung Shim break;
20527154b3e8SJoonyoung Shim case SND_SOC_DAIFMT_CBS_CFS:
20537154b3e8SJoonyoung Shim format |= TWL4030_VIF_SLAVE_EN;
20547154b3e8SJoonyoung Shim break;
20557154b3e8SJoonyoung Shim default:
20567154b3e8SJoonyoung Shim return -EINVAL;
20577154b3e8SJoonyoung Shim }
20587154b3e8SJoonyoung Shim
20597154b3e8SJoonyoung Shim /* clock inversion */
20607154b3e8SJoonyoung Shim switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
20617154b3e8SJoonyoung Shim case SND_SOC_DAIFMT_IB_NF:
20627154b3e8SJoonyoung Shim format &= ~(TWL4030_VIF_FORMAT);
20637154b3e8SJoonyoung Shim break;
20647154b3e8SJoonyoung Shim case SND_SOC_DAIFMT_NB_IF:
20657154b3e8SJoonyoung Shim format |= TWL4030_VIF_FORMAT;
20667154b3e8SJoonyoung Shim break;
20677154b3e8SJoonyoung Shim default:
20687154b3e8SJoonyoung Shim return -EINVAL;
20697154b3e8SJoonyoung Shim }
20707154b3e8SJoonyoung Shim
20717154b3e8SJoonyoung Shim if (format != old_format) {
20722046f175SPeter Ujfalusi if (twl4030->codec_powered) {
20732046f175SPeter Ujfalusi /*
20742046f175SPeter Ujfalusi * If the codec is powered, than we need to toggle the
20752046f175SPeter Ujfalusi * codec power.
20762046f175SPeter Ujfalusi */
2077c68e7f5bSKuninori Morimoto twl4030_codec_enable(component, 0);
2078c68e7f5bSKuninori Morimoto twl4030_write(component, TWL4030_REG_VOICE_IF, format);
2079c68e7f5bSKuninori Morimoto twl4030_codec_enable(component, 1);
20802046f175SPeter Ujfalusi } else {
2081c68e7f5bSKuninori Morimoto twl4030_write(component, TWL4030_REG_VOICE_IF, format);
20822046f175SPeter Ujfalusi }
20837154b3e8SJoonyoung Shim }
20847154b3e8SJoonyoung Shim
20857154b3e8SJoonyoung Shim return 0;
20867154b3e8SJoonyoung Shim }
20877154b3e8SJoonyoung Shim
twl4030_voice_set_tristate(struct snd_soc_dai * dai,int tristate)208868140443SLopez Cruz, Misael static int twl4030_voice_set_tristate(struct snd_soc_dai *dai, int tristate)
208968140443SLopez Cruz, Misael {
2090c68e7f5bSKuninori Morimoto struct snd_soc_component *component = dai->component;
2091c68e7f5bSKuninori Morimoto u8 reg = twl4030_read(component, TWL4030_REG_VOICE_IF);
209268140443SLopez Cruz, Misael
209368140443SLopez Cruz, Misael if (tristate)
209468140443SLopez Cruz, Misael reg |= TWL4030_VIF_TRI_EN;
209568140443SLopez Cruz, Misael else
209668140443SLopez Cruz, Misael reg &= ~TWL4030_VIF_TRI_EN;
209768140443SLopez Cruz, Misael
2098c68e7f5bSKuninori Morimoto return twl4030_write(component, TWL4030_REG_VOICE_IF, reg);
209968140443SLopez Cruz, Misael }
210068140443SLopez Cruz, Misael
2101bbba9444SJarkko Nikula #define TWL4030_RATES (SNDRV_PCM_RATE_8000_48000)
2102dcdeda4aSPeter Ujfalusi #define TWL4030_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
2103cc17557eSSteve Sakoman
210485e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops twl4030_dai_hifi_ops = {
21057220b9f4SPeter Ujfalusi .startup = twl4030_startup,
21067220b9f4SPeter Ujfalusi .shutdown = twl4030_shutdown,
210710d9e3d9SJoonyoung Shim .hw_params = twl4030_hw_params,
210810d9e3d9SJoonyoung Shim .set_sysclk = twl4030_set_dai_sysclk,
210910d9e3d9SJoonyoung Shim .set_fmt = twl4030_set_dai_fmt,
211068140443SLopez Cruz, Misael .set_tristate = twl4030_set_tristate,
211110d9e3d9SJoonyoung Shim };
211210d9e3d9SJoonyoung Shim
211385e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops twl4030_dai_voice_ops = {
21147154b3e8SJoonyoung Shim .startup = twl4030_voice_startup,
2115b7a755a8SMisael Lopez Cruz .shutdown = twl4030_voice_shutdown,
21167154b3e8SJoonyoung Shim .hw_params = twl4030_voice_hw_params,
21177154b3e8SJoonyoung Shim .set_sysclk = twl4030_voice_set_dai_sysclk,
21187154b3e8SJoonyoung Shim .set_fmt = twl4030_voice_set_dai_fmt,
211968140443SLopez Cruz, Misael .set_tristate = twl4030_voice_set_tristate,
21207154b3e8SJoonyoung Shim };
21217154b3e8SJoonyoung Shim
2122f0fba2adSLiam Girdwood static struct snd_soc_dai_driver twl4030_dai[] = {
21237154b3e8SJoonyoung Shim {
2124f0fba2adSLiam Girdwood .name = "twl4030-hifi",
2125cc17557eSSteve Sakoman .playback = {
2126b4852b79SPeter Ujfalusi .stream_name = "HiFi Playback",
2127cc17557eSSteve Sakoman .channels_min = 2,
21288a1f936aSPeter Ujfalusi .channels_max = 4,
212931ad0f31SPeter Ujfalusi .rates = TWL4030_RATES | SNDRV_PCM_RATE_96000,
21308819f65cSPeter Ujfalusi .formats = TWL4030_FORMATS,
21318819f65cSPeter Ujfalusi .sig_bits = 24,},
2132cc17557eSSteve Sakoman .capture = {
21337f51e7d3SPeter Ujfalusi .stream_name = "HiFi Capture",
2134cc17557eSSteve Sakoman .channels_min = 2,
21358a1f936aSPeter Ujfalusi .channels_max = 4,
2136cc17557eSSteve Sakoman .rates = TWL4030_RATES,
21378819f65cSPeter Ujfalusi .formats = TWL4030_FORMATS,
21388819f65cSPeter Ujfalusi .sig_bits = 24,},
2139f0fba2adSLiam Girdwood .ops = &twl4030_dai_hifi_ops,
21407154b3e8SJoonyoung Shim },
21417154b3e8SJoonyoung Shim {
2142f0fba2adSLiam Girdwood .name = "twl4030-voice",
21437154b3e8SJoonyoung Shim .playback = {
2144b4852b79SPeter Ujfalusi .stream_name = "Voice Playback",
21457154b3e8SJoonyoung Shim .channels_min = 1,
21467154b3e8SJoonyoung Shim .channels_max = 1,
21477154b3e8SJoonyoung Shim .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
21487154b3e8SJoonyoung Shim .formats = SNDRV_PCM_FMTBIT_S16_LE,},
21497154b3e8SJoonyoung Shim .capture = {
21507f51e7d3SPeter Ujfalusi .stream_name = "Voice Capture",
21517154b3e8SJoonyoung Shim .channels_min = 1,
21527154b3e8SJoonyoung Shim .channels_max = 2,
21537154b3e8SJoonyoung Shim .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
21547154b3e8SJoonyoung Shim .formats = SNDRV_PCM_FMTBIT_S16_LE,},
21557154b3e8SJoonyoung Shim .ops = &twl4030_dai_voice_ops,
21567154b3e8SJoonyoung Shim },
2157cc17557eSSteve Sakoman };
2158cc17557eSSteve Sakoman
twl4030_soc_probe(struct snd_soc_component * component)2159c68e7f5bSKuninori Morimoto static int twl4030_soc_probe(struct snd_soc_component *component)
2160cc17557eSSteve Sakoman {
2161f0fba2adSLiam Girdwood struct twl4030_priv *twl4030;
2162cc17557eSSteve Sakoman
2163c68e7f5bSKuninori Morimoto twl4030 = devm_kzalloc(component->dev, sizeof(struct twl4030_priv),
2164f2b1ce49SPeter Ujfalusi GFP_KERNEL);
216504cc41a8SSachin Kamat if (!twl4030)
2166f0fba2adSLiam Girdwood return -ENOMEM;
2167c68e7f5bSKuninori Morimoto snd_soc_component_set_drvdata(component, twl4030);
2168f0fba2adSLiam Girdwood /* Set the defaults, and power up the codec */
216957fe7251SPeter Ujfalusi twl4030->sysclk = twl4030_audio_get_mclk() / 1000;
2170f0fba2adSLiam Girdwood
2171c68e7f5bSKuninori Morimoto twl4030_init_chip(component);
2172cc17557eSSteve Sakoman
21737a1fecf5SPeter Ujfalusi return 0;
2174cc17557eSSteve Sakoman }
2175cc17557eSSteve Sakoman
twl4030_soc_remove(struct snd_soc_component * component)2176c68e7f5bSKuninori Morimoto static void twl4030_soc_remove(struct snd_soc_component *component)
2177cc17557eSSteve Sakoman {
2178c68e7f5bSKuninori Morimoto struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
21797adadfb0SPeter Ujfalusi struct twl4030_board_params *board_params = twl4030->board_params;
21805b3b0fa8SAxel Lin
21817adadfb0SPeter Ujfalusi if (board_params && board_params->hs_extmute &&
21827adadfb0SPeter Ujfalusi gpio_is_valid(board_params->hs_extmute_gpio))
21837adadfb0SPeter Ujfalusi gpio_free(board_params->hs_extmute_gpio);
2184cc17557eSSteve Sakoman }
2185cc17557eSSteve Sakoman
2186c68e7f5bSKuninori Morimoto static const struct snd_soc_component_driver soc_component_dev_twl4030 = {
2187f0fba2adSLiam Girdwood .probe = twl4030_soc_probe,
2188f0fba2adSLiam Girdwood .remove = twl4030_soc_remove,
21898146acffSTony Lindgren .read = twl4030_read,
21908146acffSTony Lindgren .write = twl4030_write,
2191f0fba2adSLiam Girdwood .set_bias_level = twl4030_set_bias_level,
2192f7c93f01SPeter Ujfalusi .controls = twl4030_snd_controls,
2193f7c93f01SPeter Ujfalusi .num_controls = ARRAY_SIZE(twl4030_snd_controls),
2194f7c93f01SPeter Ujfalusi .dapm_widgets = twl4030_dapm_widgets,
2195f7c93f01SPeter Ujfalusi .num_dapm_widgets = ARRAY_SIZE(twl4030_dapm_widgets),
2196f7c93f01SPeter Ujfalusi .dapm_routes = intercon,
2197f7c93f01SPeter Ujfalusi .num_dapm_routes = ARRAY_SIZE(intercon),
2198c68e7f5bSKuninori Morimoto .use_pmdown_time = 1,
2199c68e7f5bSKuninori Morimoto .endianness = 1,
2200f0fba2adSLiam Girdwood };
2201f0fba2adSLiam Girdwood
twl4030_codec_probe(struct platform_device * pdev)220205c4c6f7SBill Pemberton static int twl4030_codec_probe(struct platform_device *pdev)
22037a1fecf5SPeter Ujfalusi {
2204c68e7f5bSKuninori Morimoto return devm_snd_soc_register_component(&pdev->dev,
2205c68e7f5bSKuninori Morimoto &soc_component_dev_twl4030,
2206f0fba2adSLiam Girdwood twl4030_dai, ARRAY_SIZE(twl4030_dai));
22077a1fecf5SPeter Ujfalusi }
22087a1fecf5SPeter Ujfalusi
2209f0fba2adSLiam Girdwood MODULE_ALIAS("platform:twl4030-codec");
22107a1fecf5SPeter Ujfalusi
22117a1fecf5SPeter Ujfalusi static struct platform_driver twl4030_codec_driver = {
22127a1fecf5SPeter Ujfalusi .probe = twl4030_codec_probe,
22137a1fecf5SPeter Ujfalusi .driver = {
2214f0fba2adSLiam Girdwood .name = "twl4030-codec",
22157a1fecf5SPeter Ujfalusi },
2216cc17557eSSteve Sakoman };
2217cc17557eSSteve Sakoman
22185bbcc3c0SMark Brown module_platform_driver(twl4030_codec_driver);
221964089b84SMark Brown
2220cc17557eSSteve Sakoman MODULE_DESCRIPTION("ASoC TWL4030 codec driver");
2221cc17557eSSteve Sakoman MODULE_AUTHOR("Steve Sakoman");
2222cc17557eSSteve Sakoman MODULE_LICENSE("GPL");
2223