12b27bdccSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
28ecbabd9SMisael Lopez Cruz /*
38ecbabd9SMisael Lopez Cruz * ALSA SoC TWL6040 codec driver
48ecbabd9SMisael Lopez Cruz *
58ecbabd9SMisael Lopez Cruz * Author: Misael Lopez Cruz <x0052729@ti.com>
68ecbabd9SMisael Lopez Cruz */
78ecbabd9SMisael Lopez Cruz
88ecbabd9SMisael Lopez Cruz #include <linux/module.h>
98ecbabd9SMisael Lopez Cruz #include <linux/moduleparam.h>
108ecbabd9SMisael Lopez Cruz #include <linux/init.h>
118ecbabd9SMisael Lopez Cruz #include <linux/delay.h>
128ecbabd9SMisael Lopez Cruz #include <linux/pm.h>
138ecbabd9SMisael Lopez Cruz #include <linux/platform_device.h>
1468b40cc4SStephen Rothwell #include <linux/slab.h>
15fb34d3d5SMisael Lopez Cruz #include <linux/mfd/twl6040.h>
168ecbabd9SMisael Lopez Cruz
178ecbabd9SMisael Lopez Cruz #include <sound/core.h>
188ecbabd9SMisael Lopez Cruz #include <sound/pcm.h>
198ecbabd9SMisael Lopez Cruz #include <sound/pcm_params.h>
208ecbabd9SMisael Lopez Cruz #include <sound/soc.h>
21e48b46baSLiam Girdwood #include <sound/soc-dapm.h>
228ecbabd9SMisael Lopez Cruz #include <sound/initval.h>
238ecbabd9SMisael Lopez Cruz #include <sound/tlv.h>
248ecbabd9SMisael Lopez Cruz
258ecbabd9SMisael Lopez Cruz #include "twl6040.h"
268ecbabd9SMisael Lopez Cruz
2768897497SPeter Ujfalusi enum twl6040_dai_id {
2868897497SPeter Ujfalusi TWL6040_DAI_LEGACY = 0,
2968897497SPeter Ujfalusi TWL6040_DAI_UL,
3068897497SPeter Ujfalusi TWL6040_DAI_DL1,
3168897497SPeter Ujfalusi TWL6040_DAI_DL2,
3268897497SPeter Ujfalusi TWL6040_DAI_VIB,
3368897497SPeter Ujfalusi };
3468897497SPeter Ujfalusi
3560ea4cecSOlaya, Margarita #define TWL6040_RATES SNDRV_PCM_RATE_8000_96000
368ecbabd9SMisael Lopez Cruz #define TWL6040_FORMATS (SNDRV_PCM_FMTBIT_S32_LE)
378ecbabd9SMisael Lopez Cruz
381bf84759SMargarita Olaya Cabrera #define TWL6040_OUTHS_0dB 0x00
391bf84759SMargarita Olaya Cabrera #define TWL6040_OUTHS_M30dB 0x0F
401bf84759SMargarita Olaya Cabrera #define TWL6040_OUTHF_0dB 0x03
411bf84759SMargarita Olaya Cabrera #define TWL6040_OUTHF_M52dB 0x1D
421bf84759SMargarita Olaya Cabrera
43290c348eSLars-Peter Clausen #define TWL6040_CACHEREGNUM (TWL6040_REG_STATUS + 1)
44317596a6SPeter Ujfalusi
45a2d2362eSJorge Eduardo Candelaria struct twl6040_jack_data {
46a2d2362eSJorge Eduardo Candelaria struct snd_soc_jack *jack;
4746dd0b93SPeter Ujfalusi struct delayed_work work;
48a2d2362eSJorge Eduardo Candelaria int report;
49a2d2362eSJorge Eduardo Candelaria };
50a2d2362eSJorge Eduardo Candelaria
518ecbabd9SMisael Lopez Cruz /* codec private data */
528ecbabd9SMisael Lopez Cruz struct twl6040_data {
532a433b9dSPeter Ujfalusi int plug_irq;
548ecbabd9SMisael Lopez Cruz int codec_powered;
558ecbabd9SMisael Lopez Cruz int pll;
56af958c72SPeter Ujfalusi int pll_power_mode;
576bba63b6SMisael Lopez Cruz int hs_power_mode;
586bba63b6SMisael Lopez Cruz int hs_power_mode_locked;
5998c5fb1fSPeter Ujfalusi bool dl1_unmuted;
6098c5fb1fSPeter Ujfalusi bool dl2_unmuted;
6153509108SPeter Ujfalusi u8 dl12_cache[TWL6040_REG_HFRCTL - TWL6040_REG_HSLCTL + 1];
62fb34d3d5SMisael Lopez Cruz unsigned int clk_in;
638ecbabd9SMisael Lopez Cruz unsigned int sysclk;
64a2d2362eSJorge Eduardo Candelaria struct twl6040_jack_data hs_jack;
657480389fSKuninori Morimoto struct snd_soc_component *component;
66a2d2362eSJorge Eduardo Candelaria struct mutex mutex;
678ecbabd9SMisael Lopez Cruz };
688ecbabd9SMisael Lopez Cruz
69af958c72SPeter Ujfalusi /* set of rates for each pll: low-power and high-performance */
700d76fc6aSLars-Peter Clausen static const unsigned int lp_rates[] = {
71af958c72SPeter Ujfalusi 8000,
72af958c72SPeter Ujfalusi 11250,
73af958c72SPeter Ujfalusi 16000,
74af958c72SPeter Ujfalusi 22500,
75af958c72SPeter Ujfalusi 32000,
76af958c72SPeter Ujfalusi 44100,
77af958c72SPeter Ujfalusi 48000,
78af958c72SPeter Ujfalusi 88200,
79af958c72SPeter Ujfalusi 96000,
80af958c72SPeter Ujfalusi };
81af958c72SPeter Ujfalusi
820d76fc6aSLars-Peter Clausen static const unsigned int hp_rates[] = {
83af958c72SPeter Ujfalusi 8000,
84af958c72SPeter Ujfalusi 16000,
85af958c72SPeter Ujfalusi 32000,
86af958c72SPeter Ujfalusi 48000,
87af958c72SPeter Ujfalusi 96000,
88af958c72SPeter Ujfalusi };
89af958c72SPeter Ujfalusi
900d76fc6aSLars-Peter Clausen static const struct snd_pcm_hw_constraint_list sysclk_constraints[] = {
91f53c346cSPeter Ujfalusi { .count = ARRAY_SIZE(lp_rates), .list = lp_rates, },
92f53c346cSPeter Ujfalusi { .count = ARRAY_SIZE(hp_rates), .list = hp_rates, },
93af958c72SPeter Ujfalusi };
94af958c72SPeter Ujfalusi
957480389fSKuninori Morimoto #define to_twl6040(component) dev_get_drvdata((component)->dev->parent)
963bd33367SKuninori Morimoto
twl6040_read(struct snd_soc_component * component,unsigned int reg)977480389fSKuninori Morimoto static unsigned int twl6040_read(struct snd_soc_component *component, unsigned int reg)
9853509108SPeter Ujfalusi {
997480389fSKuninori Morimoto struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
1007480389fSKuninori Morimoto struct twl6040 *twl6040 = to_twl6040(component);
1018ecbabd9SMisael Lopez Cruz u8 value;
1028ecbabd9SMisael Lopez Cruz
1038ecbabd9SMisael Lopez Cruz if (reg >= TWL6040_CACHEREGNUM)
1048ecbabd9SMisael Lopez Cruz return -EIO;
1058ecbabd9SMisael Lopez Cruz
106626bcacbSPeter Ujfalusi switch (reg) {
107626bcacbSPeter Ujfalusi case TWL6040_REG_HSLCTL:
108626bcacbSPeter Ujfalusi case TWL6040_REG_HSRCTL:
109626bcacbSPeter Ujfalusi case TWL6040_REG_EARCTL:
110626bcacbSPeter Ujfalusi case TWL6040_REG_HFLCTL:
111626bcacbSPeter Ujfalusi case TWL6040_REG_HFRCTL:
112626bcacbSPeter Ujfalusi value = priv->dl12_cache[reg - TWL6040_REG_HSLCTL];
113626bcacbSPeter Ujfalusi break;
114626bcacbSPeter Ujfalusi default:
115fb34d3d5SMisael Lopez Cruz value = twl6040_reg_read(twl6040, reg);
116626bcacbSPeter Ujfalusi break;
117626bcacbSPeter Ujfalusi }
1188ecbabd9SMisael Lopez Cruz
1198ecbabd9SMisael Lopez Cruz return value;
1208ecbabd9SMisael Lopez Cruz }
1218ecbabd9SMisael Lopez Cruz
twl6040_can_write_to_chip(struct snd_soc_component * component,unsigned int reg)1227480389fSKuninori Morimoto static bool twl6040_can_write_to_chip(struct snd_soc_component *component,
12398c5fb1fSPeter Ujfalusi unsigned int reg)
12498c5fb1fSPeter Ujfalusi {
1257480389fSKuninori Morimoto struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
12698c5fb1fSPeter Ujfalusi
12798c5fb1fSPeter Ujfalusi switch (reg) {
12898c5fb1fSPeter Ujfalusi case TWL6040_REG_HSLCTL:
12998c5fb1fSPeter Ujfalusi case TWL6040_REG_HSRCTL:
13098c5fb1fSPeter Ujfalusi case TWL6040_REG_EARCTL:
13198c5fb1fSPeter Ujfalusi /* DL1 path */
13298c5fb1fSPeter Ujfalusi return priv->dl1_unmuted;
13398c5fb1fSPeter Ujfalusi case TWL6040_REG_HFLCTL:
13498c5fb1fSPeter Ujfalusi case TWL6040_REG_HFRCTL:
13598c5fb1fSPeter Ujfalusi return priv->dl2_unmuted;
13698c5fb1fSPeter Ujfalusi default:
137bc94c888SGustavo A. R. Silva return true;
138bf551413SSachin Kamat }
13998c5fb1fSPeter Ujfalusi }
14098c5fb1fSPeter Ujfalusi
twl6040_update_dl12_cache(struct snd_soc_component * component,u8 reg,u8 value)1417480389fSKuninori Morimoto static inline void twl6040_update_dl12_cache(struct snd_soc_component *component,
142626bcacbSPeter Ujfalusi u8 reg, u8 value)
143626bcacbSPeter Ujfalusi {
1447480389fSKuninori Morimoto struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
145626bcacbSPeter Ujfalusi
146626bcacbSPeter Ujfalusi switch (reg) {
147626bcacbSPeter Ujfalusi case TWL6040_REG_HSLCTL:
148626bcacbSPeter Ujfalusi case TWL6040_REG_HSRCTL:
149626bcacbSPeter Ujfalusi case TWL6040_REG_EARCTL:
150626bcacbSPeter Ujfalusi case TWL6040_REG_HFLCTL:
151626bcacbSPeter Ujfalusi case TWL6040_REG_HFRCTL:
152626bcacbSPeter Ujfalusi priv->dl12_cache[reg - TWL6040_REG_HSLCTL] = value;
153626bcacbSPeter Ujfalusi break;
154626bcacbSPeter Ujfalusi default:
155626bcacbSPeter Ujfalusi break;
156626bcacbSPeter Ujfalusi }
157626bcacbSPeter Ujfalusi }
158626bcacbSPeter Ujfalusi
twl6040_write(struct snd_soc_component * component,unsigned int reg,unsigned int value)1597480389fSKuninori Morimoto static int twl6040_write(struct snd_soc_component *component,
1608ecbabd9SMisael Lopez Cruz unsigned int reg, unsigned int value)
1618ecbabd9SMisael Lopez Cruz {
1627480389fSKuninori Morimoto struct twl6040 *twl6040 = to_twl6040(component);
163fb34d3d5SMisael Lopez Cruz
1648ecbabd9SMisael Lopez Cruz if (reg >= TWL6040_CACHEREGNUM)
1658ecbabd9SMisael Lopez Cruz return -EIO;
1668ecbabd9SMisael Lopez Cruz
1677480389fSKuninori Morimoto twl6040_update_dl12_cache(component, reg, value);
1687480389fSKuninori Morimoto if (twl6040_can_write_to_chip(component, reg))
169fb34d3d5SMisael Lopez Cruz return twl6040_reg_write(twl6040, reg, value);
170d17bf318SPeter Ujfalusi else
171d17bf318SPeter Ujfalusi return 0;
1728ecbabd9SMisael Lopez Cruz }
1738ecbabd9SMisael Lopez Cruz
twl6040_init_chip(struct snd_soc_component * component)1747480389fSKuninori Morimoto static void twl6040_init_chip(struct snd_soc_component *component)
175a52762eeSPeter Ujfalusi {
1767480389fSKuninori Morimoto twl6040_read(component, TWL6040_REG_TRIM1);
1777480389fSKuninori Morimoto twl6040_read(component, TWL6040_REG_TRIM2);
1787480389fSKuninori Morimoto twl6040_read(component, TWL6040_REG_TRIM3);
1797480389fSKuninori Morimoto twl6040_read(component, TWL6040_REG_HSOTRIM);
1807480389fSKuninori Morimoto twl6040_read(component, TWL6040_REG_HFOTRIM);
181f97217f1SPeter Ujfalusi
1822c27ff41SPeter Ujfalusi /* Change chip defaults */
1832c27ff41SPeter Ujfalusi /* No imput selected for microphone amplifiers */
1847480389fSKuninori Morimoto twl6040_write(component, TWL6040_REG_MICLCTL, 0x18);
1857480389fSKuninori Morimoto twl6040_write(component, TWL6040_REG_MICRCTL, 0x18);
1863acef685SPeter Ujfalusi
1873acef685SPeter Ujfalusi /*
1883acef685SPeter Ujfalusi * We need to lower the default gain values, so the ramp code
1893acef685SPeter Ujfalusi * can work correctly for the first playback.
1903acef685SPeter Ujfalusi * This reduces the pop noise heard at the first playback.
1913acef685SPeter Ujfalusi */
1927480389fSKuninori Morimoto twl6040_write(component, TWL6040_REG_HSGAIN, 0xff);
1937480389fSKuninori Morimoto twl6040_write(component, TWL6040_REG_EARCTL, 0x1e);
1947480389fSKuninori Morimoto twl6040_write(component, TWL6040_REG_HFLGAIN, 0x1d);
1957480389fSKuninori Morimoto twl6040_write(component, TWL6040_REG_HFRGAIN, 0x1d);
1967480389fSKuninori Morimoto twl6040_write(component, TWL6040_REG_LINEGAIN, 0);
197a52762eeSPeter Ujfalusi }
198a52762eeSPeter Ujfalusi
1998ecbabd9SMisael Lopez Cruz /* set headset dac and driver power mode */
headset_power_mode(struct snd_soc_component * component,int high_perf)2007480389fSKuninori Morimoto static int headset_power_mode(struct snd_soc_component *component, int high_perf)
2018ecbabd9SMisael Lopez Cruz {
2028ecbabd9SMisael Lopez Cruz int hslctl, hsrctl;
203ab6cf139SPeter Ujfalusi int mask = TWL6040_HSDRVMODE | TWL6040_HSDACMODE;
2048ecbabd9SMisael Lopez Cruz
2057480389fSKuninori Morimoto hslctl = twl6040_read(component, TWL6040_REG_HSLCTL);
2067480389fSKuninori Morimoto hsrctl = twl6040_read(component, TWL6040_REG_HSRCTL);
2078ecbabd9SMisael Lopez Cruz
2088ecbabd9SMisael Lopez Cruz if (high_perf) {
2098ecbabd9SMisael Lopez Cruz hslctl &= ~mask;
2108ecbabd9SMisael Lopez Cruz hsrctl &= ~mask;
2118ecbabd9SMisael Lopez Cruz } else {
2128ecbabd9SMisael Lopez Cruz hslctl |= mask;
2138ecbabd9SMisael Lopez Cruz hsrctl |= mask;
2148ecbabd9SMisael Lopez Cruz }
2158ecbabd9SMisael Lopez Cruz
2167480389fSKuninori Morimoto twl6040_write(component, TWL6040_REG_HSLCTL, hslctl);
2177480389fSKuninori Morimoto twl6040_write(component, TWL6040_REG_HSRCTL, hsrctl);
2188ecbabd9SMisael Lopez Cruz
2198ecbabd9SMisael Lopez Cruz return 0;
2208ecbabd9SMisael Lopez Cruz }
2218ecbabd9SMisael Lopez Cruz
twl6040_hs_dac_event(struct snd_soc_dapm_widget * w,struct snd_kcontrol * kcontrol,int event)2220fad4ed7SJorge Eduardo Candelaria static int twl6040_hs_dac_event(struct snd_soc_dapm_widget *w,
2230fad4ed7SJorge Eduardo Candelaria struct snd_kcontrol *kcontrol, int event)
2240fad4ed7SJorge Eduardo Candelaria {
2257480389fSKuninori Morimoto struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
22633b6816cSPeter Ujfalusi u8 hslctl, hsrctl;
22733b6816cSPeter Ujfalusi
22833b6816cSPeter Ujfalusi /*
22933b6816cSPeter Ujfalusi * Workaround for Headset DC offset caused pop noise:
23033b6816cSPeter Ujfalusi * Both HS DAC need to be turned on (before the HS driver) and off at
23133b6816cSPeter Ujfalusi * the same time.
23233b6816cSPeter Ujfalusi */
2337480389fSKuninori Morimoto hslctl = twl6040_read(component, TWL6040_REG_HSLCTL);
2347480389fSKuninori Morimoto hsrctl = twl6040_read(component, TWL6040_REG_HSRCTL);
23533b6816cSPeter Ujfalusi if (SND_SOC_DAPM_EVENT_ON(event)) {
23633b6816cSPeter Ujfalusi hslctl |= TWL6040_HSDACENA;
23733b6816cSPeter Ujfalusi hsrctl |= TWL6040_HSDACENA;
23833b6816cSPeter Ujfalusi } else {
23933b6816cSPeter Ujfalusi hslctl &= ~TWL6040_HSDACENA;
24033b6816cSPeter Ujfalusi hsrctl &= ~TWL6040_HSDACENA;
24133b6816cSPeter Ujfalusi }
2427480389fSKuninori Morimoto twl6040_write(component, TWL6040_REG_HSLCTL, hslctl);
2437480389fSKuninori Morimoto twl6040_write(component, TWL6040_REG_HSRCTL, hsrctl);
24433b6816cSPeter Ujfalusi
2450fad4ed7SJorge Eduardo Candelaria msleep(1);
2460fad4ed7SJorge Eduardo Candelaria return 0;
2470fad4ed7SJorge Eduardo Candelaria }
2480fad4ed7SJorge Eduardo Candelaria
twl6040_ep_drv_event(struct snd_soc_dapm_widget * w,struct snd_kcontrol * kcontrol,int event)249694b0001SPeter Ujfalusi static int twl6040_ep_drv_event(struct snd_soc_dapm_widget *w,
2508ecbabd9SMisael Lopez Cruz struct snd_kcontrol *kcontrol, int event)
2518ecbabd9SMisael Lopez Cruz {
2527480389fSKuninori Morimoto struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
2537480389fSKuninori Morimoto struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
2546bba63b6SMisael Lopez Cruz int ret = 0;
2558ecbabd9SMisael Lopez Cruz
2566bba63b6SMisael Lopez Cruz if (SND_SOC_DAPM_EVENT_ON(event)) {
2576bba63b6SMisael Lopez Cruz /* Earphone doesn't support low power mode */
2586bba63b6SMisael Lopez Cruz priv->hs_power_mode_locked = 1;
2597480389fSKuninori Morimoto ret = headset_power_mode(component, 1);
2606bba63b6SMisael Lopez Cruz } else {
2616bba63b6SMisael Lopez Cruz priv->hs_power_mode_locked = 0;
2627480389fSKuninori Morimoto ret = headset_power_mode(component, priv->hs_power_mode);
2636bba63b6SMisael Lopez Cruz }
2648ecbabd9SMisael Lopez Cruz
2650fad4ed7SJorge Eduardo Candelaria msleep(1);
2660fad4ed7SJorge Eduardo Candelaria
2676bba63b6SMisael Lopez Cruz return ret;
2688ecbabd9SMisael Lopez Cruz }
2698ecbabd9SMisael Lopez Cruz
twl6040_hs_jack_report(struct snd_soc_component * component,struct snd_soc_jack * jack,int report)2707480389fSKuninori Morimoto static void twl6040_hs_jack_report(struct snd_soc_component *component,
271a2d2362eSJorge Eduardo Candelaria struct snd_soc_jack *jack, int report)
272a2d2362eSJorge Eduardo Candelaria {
2737480389fSKuninori Morimoto struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
274a2d2362eSJorge Eduardo Candelaria int status;
275a2d2362eSJorge Eduardo Candelaria
276a2d2362eSJorge Eduardo Candelaria mutex_lock(&priv->mutex);
277a2d2362eSJorge Eduardo Candelaria
278a2d2362eSJorge Eduardo Candelaria /* Sync status */
2797480389fSKuninori Morimoto status = twl6040_read(component, TWL6040_REG_STATUS);
280a2d2362eSJorge Eduardo Candelaria if (status & TWL6040_PLUGCOMP)
281a2d2362eSJorge Eduardo Candelaria snd_soc_jack_report(jack, report, report);
282a2d2362eSJorge Eduardo Candelaria else
283a2d2362eSJorge Eduardo Candelaria snd_soc_jack_report(jack, 0, report);
284a2d2362eSJorge Eduardo Candelaria
285a2d2362eSJorge Eduardo Candelaria mutex_unlock(&priv->mutex);
286a2d2362eSJorge Eduardo Candelaria }
287a2d2362eSJorge Eduardo Candelaria
twl6040_hs_jack_detect(struct snd_soc_component * component,struct snd_soc_jack * jack,int report)2887480389fSKuninori Morimoto void twl6040_hs_jack_detect(struct snd_soc_component *component,
289a2d2362eSJorge Eduardo Candelaria struct snd_soc_jack *jack, int report)
290a2d2362eSJorge Eduardo Candelaria {
2917480389fSKuninori Morimoto struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
292a2d2362eSJorge Eduardo Candelaria struct twl6040_jack_data *hs_jack = &priv->hs_jack;
293a2d2362eSJorge Eduardo Candelaria
294a2d2362eSJorge Eduardo Candelaria hs_jack->jack = jack;
295a2d2362eSJorge Eduardo Candelaria hs_jack->report = report;
296a2d2362eSJorge Eduardo Candelaria
2977480389fSKuninori Morimoto twl6040_hs_jack_report(component, hs_jack->jack, hs_jack->report);
298a2d2362eSJorge Eduardo Candelaria }
299a2d2362eSJorge Eduardo Candelaria EXPORT_SYMBOL_GPL(twl6040_hs_jack_detect);
300a2d2362eSJorge Eduardo Candelaria
twl6040_accessory_work(struct work_struct * work)301a2d2362eSJorge Eduardo Candelaria static void twl6040_accessory_work(struct work_struct *work)
302a2d2362eSJorge Eduardo Candelaria {
303a2d2362eSJorge Eduardo Candelaria struct twl6040_data *priv = container_of(work,
30446dd0b93SPeter Ujfalusi struct twl6040_data, hs_jack.work.work);
3057480389fSKuninori Morimoto struct snd_soc_component *component = priv->component;
306a2d2362eSJorge Eduardo Candelaria struct twl6040_jack_data *hs_jack = &priv->hs_jack;
307a2d2362eSJorge Eduardo Candelaria
3087480389fSKuninori Morimoto twl6040_hs_jack_report(component, hs_jack->jack, hs_jack->report);
309a2d2362eSJorge Eduardo Candelaria }
310a2d2362eSJorge Eduardo Candelaria
3118ecbabd9SMisael Lopez Cruz /* audio interrupt handler */
twl6040_audio_handler(int irq,void * data)312fb34d3d5SMisael Lopez Cruz static irqreturn_t twl6040_audio_handler(int irq, void *data)
3138ecbabd9SMisael Lopez Cruz {
3147480389fSKuninori Morimoto struct snd_soc_component *component = data;
3157480389fSKuninori Morimoto struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
3168ecbabd9SMisael Lopez Cruz
317a06e427dSMark Brown queue_delayed_work(system_power_efficient_wq,
318a06e427dSMark Brown &priv->hs_jack.work, msecs_to_jiffies(200));
319cf370a5aSOlaya, Margarita
3208ecbabd9SMisael Lopez Cruz return IRQ_HANDLED;
3218ecbabd9SMisael Lopez Cruz }
3228ecbabd9SMisael Lopez Cruz
twl6040_soc_dapm_put_vibra_enum(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)32367c34130SPeter Ujfalusi static int twl6040_soc_dapm_put_vibra_enum(struct snd_kcontrol *kcontrol,
32467c34130SPeter Ujfalusi struct snd_ctl_elem_value *ucontrol)
32567c34130SPeter Ujfalusi {
3267480389fSKuninori Morimoto struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol);
32767c34130SPeter Ujfalusi struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
32867c34130SPeter Ujfalusi unsigned int val;
32967c34130SPeter Ujfalusi
33067c34130SPeter Ujfalusi /* Do not allow changes while Input/FF efect is running */
3317480389fSKuninori Morimoto val = twl6040_read(component, e->reg);
33267c34130SPeter Ujfalusi if (val & TWL6040_VIBENA && !(val & TWL6040_VIBSEL))
33367c34130SPeter Ujfalusi return -EBUSY;
33467c34130SPeter Ujfalusi
33567c34130SPeter Ujfalusi return snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
33667c34130SPeter Ujfalusi }
33767c34130SPeter Ujfalusi
3388ecbabd9SMisael Lopez Cruz /*
3398ecbabd9SMisael Lopez Cruz * MICATT volume control:
3408ecbabd9SMisael Lopez Cruz * from -6 to 0 dB in 6 dB steps
3418ecbabd9SMisael Lopez Cruz */
3428ecbabd9SMisael Lopez Cruz static DECLARE_TLV_DB_SCALE(mic_preamp_tlv, -600, 600, 0);
3438ecbabd9SMisael Lopez Cruz
3448ecbabd9SMisael Lopez Cruz /*
3458ecbabd9SMisael Lopez Cruz * MICGAIN volume control:
3462763f45dSRicardo Neri * from 6 to 30 dB in 6 dB steps
3478ecbabd9SMisael Lopez Cruz */
3482763f45dSRicardo Neri static DECLARE_TLV_DB_SCALE(mic_amp_tlv, 600, 600, 0);
3498ecbabd9SMisael Lopez Cruz
3508ecbabd9SMisael Lopez Cruz /*
351370a0314SJorge Eduardo Candelaria * AFMGAIN volume control:
3521f71a3baSLiam Girdwood * from -18 to 24 dB in 6 dB steps
353370a0314SJorge Eduardo Candelaria */
3541f71a3baSLiam Girdwood static DECLARE_TLV_DB_SCALE(afm_amp_tlv, -1800, 600, 0);
355370a0314SJorge Eduardo Candelaria
356370a0314SJorge Eduardo Candelaria /*
3578ecbabd9SMisael Lopez Cruz * HSGAIN volume control:
3588ecbabd9SMisael Lopez Cruz * from -30 to 0 dB in 2 dB steps
3598ecbabd9SMisael Lopez Cruz */
3608ecbabd9SMisael Lopez Cruz static DECLARE_TLV_DB_SCALE(hs_tlv, -3000, 200, 0);
3618ecbabd9SMisael Lopez Cruz
3628ecbabd9SMisael Lopez Cruz /*
3638ecbabd9SMisael Lopez Cruz * HFGAIN volume control:
3648ecbabd9SMisael Lopez Cruz * from -52 to 6 dB in 2 dB steps
3658ecbabd9SMisael Lopez Cruz */
3668ecbabd9SMisael Lopez Cruz static DECLARE_TLV_DB_SCALE(hf_tlv, -5200, 200, 0);
3678ecbabd9SMisael Lopez Cruz
368871a05a7SJorge Eduardo Candelaria /*
369871a05a7SJorge Eduardo Candelaria * EPGAIN volume control:
370871a05a7SJorge Eduardo Candelaria * from -24 to 6 dB in 2 dB steps
371871a05a7SJorge Eduardo Candelaria */
372871a05a7SJorge Eduardo Candelaria static DECLARE_TLV_DB_SCALE(ep_tlv, -2400, 200, 0);
373871a05a7SJorge Eduardo Candelaria
3748ecbabd9SMisael Lopez Cruz /* Left analog microphone selection */
3758ecbabd9SMisael Lopez Cruz static const char *twl6040_amicl_texts[] =
3768ecbabd9SMisael Lopez Cruz {"Headset Mic", "Main Mic", "Aux/FM Left", "Off"};
3778ecbabd9SMisael Lopez Cruz
3788ecbabd9SMisael Lopez Cruz /* Right analog microphone selection */
3798ecbabd9SMisael Lopez Cruz static const char *twl6040_amicr_texts[] =
3808ecbabd9SMisael Lopez Cruz {"Headset Mic", "Sub Mic", "Aux/FM Right", "Off"};
3818ecbabd9SMisael Lopez Cruz
3828ecbabd9SMisael Lopez Cruz static const struct soc_enum twl6040_enum[] = {
383a1d0d786STakashi Iwai SOC_ENUM_SINGLE(TWL6040_REG_MICLCTL, 3,
384a1d0d786STakashi Iwai ARRAY_SIZE(twl6040_amicl_texts), twl6040_amicl_texts),
385a1d0d786STakashi Iwai SOC_ENUM_SINGLE(TWL6040_REG_MICRCTL, 3,
386a1d0d786STakashi Iwai ARRAY_SIZE(twl6040_amicr_texts), twl6040_amicr_texts),
3878ecbabd9SMisael Lopez Cruz };
3888ecbabd9SMisael Lopez Cruz
389370a0314SJorge Eduardo Candelaria static const char *twl6040_hs_texts[] = {
390370a0314SJorge Eduardo Candelaria "Off", "HS DAC", "Line-In amp"
391370a0314SJorge Eduardo Candelaria };
392370a0314SJorge Eduardo Candelaria
393370a0314SJorge Eduardo Candelaria static const struct soc_enum twl6040_hs_enum[] = {
394370a0314SJorge Eduardo Candelaria SOC_ENUM_SINGLE(TWL6040_REG_HSLCTL, 5, ARRAY_SIZE(twl6040_hs_texts),
395370a0314SJorge Eduardo Candelaria twl6040_hs_texts),
396370a0314SJorge Eduardo Candelaria SOC_ENUM_SINGLE(TWL6040_REG_HSRCTL, 5, ARRAY_SIZE(twl6040_hs_texts),
397370a0314SJorge Eduardo Candelaria twl6040_hs_texts),
398370a0314SJorge Eduardo Candelaria };
399370a0314SJorge Eduardo Candelaria
400370a0314SJorge Eduardo Candelaria static const char *twl6040_hf_texts[] = {
401370a0314SJorge Eduardo Candelaria "Off", "HF DAC", "Line-In amp"
402370a0314SJorge Eduardo Candelaria };
403370a0314SJorge Eduardo Candelaria
404370a0314SJorge Eduardo Candelaria static const struct soc_enum twl6040_hf_enum[] = {
405370a0314SJorge Eduardo Candelaria SOC_ENUM_SINGLE(TWL6040_REG_HFLCTL, 2, ARRAY_SIZE(twl6040_hf_texts),
406370a0314SJorge Eduardo Candelaria twl6040_hf_texts),
407370a0314SJorge Eduardo Candelaria SOC_ENUM_SINGLE(TWL6040_REG_HFRCTL, 2, ARRAY_SIZE(twl6040_hf_texts),
408370a0314SJorge Eduardo Candelaria twl6040_hf_texts),
409370a0314SJorge Eduardo Candelaria };
410370a0314SJorge Eduardo Candelaria
41167c34130SPeter Ujfalusi static const char *twl6040_vibrapath_texts[] = {
41267c34130SPeter Ujfalusi "Input FF", "Audio PDM"
41367c34130SPeter Ujfalusi };
41467c34130SPeter Ujfalusi
41567c34130SPeter Ujfalusi static const struct soc_enum twl6040_vibra_enum[] = {
41667c34130SPeter Ujfalusi SOC_ENUM_SINGLE(TWL6040_REG_VIBCTLL, 1,
41767c34130SPeter Ujfalusi ARRAY_SIZE(twl6040_vibrapath_texts),
41867c34130SPeter Ujfalusi twl6040_vibrapath_texts),
41967c34130SPeter Ujfalusi SOC_ENUM_SINGLE(TWL6040_REG_VIBCTLR, 1,
42067c34130SPeter Ujfalusi ARRAY_SIZE(twl6040_vibrapath_texts),
42167c34130SPeter Ujfalusi twl6040_vibrapath_texts),
42267c34130SPeter Ujfalusi };
42367c34130SPeter Ujfalusi
4248ecbabd9SMisael Lopez Cruz static const struct snd_kcontrol_new amicl_control =
4258ecbabd9SMisael Lopez Cruz SOC_DAPM_ENUM("Route", twl6040_enum[0]);
4268ecbabd9SMisael Lopez Cruz
4278ecbabd9SMisael Lopez Cruz static const struct snd_kcontrol_new amicr_control =
4288ecbabd9SMisael Lopez Cruz SOC_DAPM_ENUM("Route", twl6040_enum[1]);
4298ecbabd9SMisael Lopez Cruz
4308ecbabd9SMisael Lopez Cruz /* Headset DAC playback switches */
431370a0314SJorge Eduardo Candelaria static const struct snd_kcontrol_new hsl_mux_controls =
432370a0314SJorge Eduardo Candelaria SOC_DAPM_ENUM("Route", twl6040_hs_enum[0]);
4338ecbabd9SMisael Lopez Cruz
434370a0314SJorge Eduardo Candelaria static const struct snd_kcontrol_new hsr_mux_controls =
435370a0314SJorge Eduardo Candelaria SOC_DAPM_ENUM("Route", twl6040_hs_enum[1]);
4368ecbabd9SMisael Lopez Cruz
4378ecbabd9SMisael Lopez Cruz /* Handsfree DAC playback switches */
438370a0314SJorge Eduardo Candelaria static const struct snd_kcontrol_new hfl_mux_controls =
439370a0314SJorge Eduardo Candelaria SOC_DAPM_ENUM("Route", twl6040_hf_enum[0]);
4408ecbabd9SMisael Lopez Cruz
441370a0314SJorge Eduardo Candelaria static const struct snd_kcontrol_new hfr_mux_controls =
442370a0314SJorge Eduardo Candelaria SOC_DAPM_ENUM("Route", twl6040_hf_enum[1]);
4438ecbabd9SMisael Lopez Cruz
444317596a6SPeter Ujfalusi static const struct snd_kcontrol_new ep_path_enable_control =
445290c348eSLars-Peter Clausen SOC_DAPM_SINGLE_VIRT("Switch", 1);
446871a05a7SJorge Eduardo Candelaria
447fdb625ffSPeter Ujfalusi static const struct snd_kcontrol_new auxl_switch_control =
448fdb625ffSPeter Ujfalusi SOC_DAPM_SINGLE("Switch", TWL6040_REG_HFLCTL, 6, 1, 0);
449fdb625ffSPeter Ujfalusi
450fdb625ffSPeter Ujfalusi static const struct snd_kcontrol_new auxr_switch_control =
451fdb625ffSPeter Ujfalusi SOC_DAPM_SINGLE("Switch", TWL6040_REG_HFRCTL, 6, 1, 0);
452fdb625ffSPeter Ujfalusi
45367c34130SPeter Ujfalusi /* Vibra playback switches */
45467c34130SPeter Ujfalusi static const struct snd_kcontrol_new vibral_mux_controls =
45567c34130SPeter Ujfalusi SOC_DAPM_ENUM_EXT("Route", twl6040_vibra_enum[0],
45667c34130SPeter Ujfalusi snd_soc_dapm_get_enum_double,
45767c34130SPeter Ujfalusi twl6040_soc_dapm_put_vibra_enum);
45867c34130SPeter Ujfalusi
45967c34130SPeter Ujfalusi static const struct snd_kcontrol_new vibrar_mux_controls =
46067c34130SPeter Ujfalusi SOC_DAPM_ENUM_EXT("Route", twl6040_vibra_enum[1],
46167c34130SPeter Ujfalusi snd_soc_dapm_get_enum_double,
46267c34130SPeter Ujfalusi twl6040_soc_dapm_put_vibra_enum);
46367c34130SPeter Ujfalusi
4646bba63b6SMisael Lopez Cruz /* Headset power mode */
4657cca6067SPeter Ujfalusi static const char *twl6040_power_mode_texts[] = {
4663e453654SSimon Wilson "Low-Power", "High-Performance",
4676bba63b6SMisael Lopez Cruz };
4686bba63b6SMisael Lopez Cruz
469a1d0d786STakashi Iwai static SOC_ENUM_SINGLE_EXT_DECL(twl6040_power_mode_enum,
4707cca6067SPeter Ujfalusi twl6040_power_mode_texts);
4716bba63b6SMisael Lopez Cruz
twl6040_headset_power_get_enum(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)4726bba63b6SMisael Lopez Cruz static int twl6040_headset_power_get_enum(struct snd_kcontrol *kcontrol,
4736bba63b6SMisael Lopez Cruz struct snd_ctl_elem_value *ucontrol)
4746bba63b6SMisael Lopez Cruz {
4757480389fSKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
4767480389fSKuninori Morimoto struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
4776bba63b6SMisael Lopez Cruz
4786bba63b6SMisael Lopez Cruz ucontrol->value.enumerated.item[0] = priv->hs_power_mode;
4796bba63b6SMisael Lopez Cruz
4806bba63b6SMisael Lopez Cruz return 0;
4816bba63b6SMisael Lopez Cruz }
4826bba63b6SMisael Lopez Cruz
twl6040_headset_power_put_enum(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)4836bba63b6SMisael Lopez Cruz static int twl6040_headset_power_put_enum(struct snd_kcontrol *kcontrol,
4846bba63b6SMisael Lopez Cruz struct snd_ctl_elem_value *ucontrol)
4856bba63b6SMisael Lopez Cruz {
4867480389fSKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
4877480389fSKuninori Morimoto struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
4886bba63b6SMisael Lopez Cruz int high_perf = ucontrol->value.enumerated.item[0];
4896bba63b6SMisael Lopez Cruz int ret = 0;
4906bba63b6SMisael Lopez Cruz
4916bba63b6SMisael Lopez Cruz if (!priv->hs_power_mode_locked)
4927480389fSKuninori Morimoto ret = headset_power_mode(component, high_perf);
4936bba63b6SMisael Lopez Cruz
4946bba63b6SMisael Lopez Cruz if (!ret)
4956bba63b6SMisael Lopez Cruz priv->hs_power_mode = high_perf;
4966bba63b6SMisael Lopez Cruz
4976bba63b6SMisael Lopez Cruz return ret;
4986bba63b6SMisael Lopez Cruz }
4996bba63b6SMisael Lopez Cruz
twl6040_pll_get_enum(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)500af958c72SPeter Ujfalusi static int twl6040_pll_get_enum(struct snd_kcontrol *kcontrol,
501af958c72SPeter Ujfalusi struct snd_ctl_elem_value *ucontrol)
502af958c72SPeter Ujfalusi {
5037480389fSKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
5047480389fSKuninori Morimoto struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
505af958c72SPeter Ujfalusi
506af958c72SPeter Ujfalusi ucontrol->value.enumerated.item[0] = priv->pll_power_mode;
507af958c72SPeter Ujfalusi
508af958c72SPeter Ujfalusi return 0;
509af958c72SPeter Ujfalusi }
510af958c72SPeter Ujfalusi
twl6040_pll_put_enum(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)511af958c72SPeter Ujfalusi static int twl6040_pll_put_enum(struct snd_kcontrol *kcontrol,
512af958c72SPeter Ujfalusi struct snd_ctl_elem_value *ucontrol)
513af958c72SPeter Ujfalusi {
5147480389fSKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
5157480389fSKuninori Morimoto struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
516af958c72SPeter Ujfalusi
517af958c72SPeter Ujfalusi priv->pll_power_mode = ucontrol->value.enumerated.item[0];
518af958c72SPeter Ujfalusi
519af958c72SPeter Ujfalusi return 0;
520af958c72SPeter Ujfalusi }
521af958c72SPeter Ujfalusi
twl6040_get_dl1_gain(struct snd_soc_component * component)5227480389fSKuninori Morimoto int twl6040_get_dl1_gain(struct snd_soc_component *component)
523e48b46baSLiam Girdwood {
5247480389fSKuninori Morimoto struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
525e48b46baSLiam Girdwood
526e48b46baSLiam Girdwood if (snd_soc_dapm_get_pin_status(dapm, "EP"))
527e48b46baSLiam Girdwood return -1; /* -1dB */
528e48b46baSLiam Girdwood
529e48b46baSLiam Girdwood if (snd_soc_dapm_get_pin_status(dapm, "HSOR") ||
530e48b46baSLiam Girdwood snd_soc_dapm_get_pin_status(dapm, "HSOL")) {
531e48b46baSLiam Girdwood
5327480389fSKuninori Morimoto u8 val = twl6040_read(component, TWL6040_REG_HSLCTL);
533e48b46baSLiam Girdwood if (val & TWL6040_HSDACMODE)
534e48b46baSLiam Girdwood /* HSDACL in LP mode */
535e48b46baSLiam Girdwood return -8; /* -8dB */
536e48b46baSLiam Girdwood else
537e48b46baSLiam Girdwood /* HSDACL in HP mode */
538e48b46baSLiam Girdwood return -1; /* -1dB */
539e48b46baSLiam Girdwood }
540e48b46baSLiam Girdwood return 0; /* 0dB */
541e48b46baSLiam Girdwood }
542e48b46baSLiam Girdwood EXPORT_SYMBOL_GPL(twl6040_get_dl1_gain);
543e48b46baSLiam Girdwood
twl6040_get_clk_id(struct snd_soc_component * component)5447480389fSKuninori Morimoto int twl6040_get_clk_id(struct snd_soc_component *component)
545af958c72SPeter Ujfalusi {
5467480389fSKuninori Morimoto struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
547af958c72SPeter Ujfalusi
548ff593ca1SPeter Ujfalusi return priv->pll_power_mode;
549af958c72SPeter Ujfalusi }
550af958c72SPeter Ujfalusi EXPORT_SYMBOL_GPL(twl6040_get_clk_id);
551af958c72SPeter Ujfalusi
twl6040_get_trim_value(struct snd_soc_component * component,enum twl6040_trim trim)5527480389fSKuninori Morimoto int twl6040_get_trim_value(struct snd_soc_component *component, enum twl6040_trim trim)
553db4aabccSPeter Ujfalusi {
554db4aabccSPeter Ujfalusi if (unlikely(trim >= TWL6040_TRIM_INVAL))
555db4aabccSPeter Ujfalusi return -EINVAL;
556db4aabccSPeter Ujfalusi
5577480389fSKuninori Morimoto return twl6040_read(component, TWL6040_REG_TRIM1 + trim);
558db4aabccSPeter Ujfalusi }
559db4aabccSPeter Ujfalusi EXPORT_SYMBOL_GPL(twl6040_get_trim_value);
560db4aabccSPeter Ujfalusi
twl6040_get_hs_step_size(struct snd_soc_component * component)5617480389fSKuninori Morimoto int twl6040_get_hs_step_size(struct snd_soc_component *component)
56208656910SLiam Girdwood {
5637480389fSKuninori Morimoto struct twl6040 *twl6040 = to_twl6040(component);
56408656910SLiam Girdwood
565be4ac00aSPeter Ujfalusi if (twl6040_get_revid(twl6040) < TWL6040_REV_ES1_3)
56608656910SLiam Girdwood /* For ES under ES_1.3 HS step is 2 mV */
56708656910SLiam Girdwood return 2;
56808656910SLiam Girdwood else
56908656910SLiam Girdwood /* For ES_1.3 HS step is 1 mV */
57008656910SLiam Girdwood return 1;
57108656910SLiam Girdwood }
57208656910SLiam Girdwood EXPORT_SYMBOL_GPL(twl6040_get_hs_step_size);
57308656910SLiam Girdwood
5748ecbabd9SMisael Lopez Cruz static const struct snd_kcontrol_new twl6040_snd_controls[] = {
5758ecbabd9SMisael Lopez Cruz /* Capture gains */
5768ecbabd9SMisael Lopez Cruz SOC_DOUBLE_TLV("Capture Preamplifier Volume",
5778ecbabd9SMisael Lopez Cruz TWL6040_REG_MICGAIN, 6, 7, 1, 1, mic_preamp_tlv),
5788ecbabd9SMisael Lopez Cruz SOC_DOUBLE_TLV("Capture Volume",
5798ecbabd9SMisael Lopez Cruz TWL6040_REG_MICGAIN, 0, 3, 4, 0, mic_amp_tlv),
5808ecbabd9SMisael Lopez Cruz
581370a0314SJorge Eduardo Candelaria /* AFM gains */
582370a0314SJorge Eduardo Candelaria SOC_DOUBLE_TLV("Aux FM Volume",
5831f71a3baSLiam Girdwood TWL6040_REG_LINEGAIN, 0, 3, 7, 0, afm_amp_tlv),
584370a0314SJorge Eduardo Candelaria
5858ecbabd9SMisael Lopez Cruz /* Playback gains */
5863bb8a819SPeter Ujfalusi SOC_DOUBLE_TLV("Headset Playback Volume",
5873bb8a819SPeter Ujfalusi TWL6040_REG_HSGAIN, 0, 4, 0xF, 1, hs_tlv),
5883bb8a819SPeter Ujfalusi SOC_DOUBLE_R_TLV("Handsfree Playback Volume",
5893bb8a819SPeter Ujfalusi TWL6040_REG_HFLGAIN, TWL6040_REG_HFRGAIN, 0, 0x1D, 1, hf_tlv),
590871a05a7SJorge Eduardo Candelaria SOC_SINGLE_TLV("Earphone Playback Volume",
591871a05a7SJorge Eduardo Candelaria TWL6040_REG_EARCTL, 1, 0xF, 1, ep_tlv),
5926bba63b6SMisael Lopez Cruz
5937cca6067SPeter Ujfalusi SOC_ENUM_EXT("Headset Power Mode", twl6040_power_mode_enum,
5946bba63b6SMisael Lopez Cruz twl6040_headset_power_get_enum,
5956bba63b6SMisael Lopez Cruz twl6040_headset_power_put_enum),
596af958c72SPeter Ujfalusi
5970636e8b3SPeter Ujfalusi /* Left HS PDM data routed to Right HSDAC */
5980636e8b3SPeter Ujfalusi SOC_SINGLE("Headset Mono to Stereo Playback Switch",
5990636e8b3SPeter Ujfalusi TWL6040_REG_HSRCTL, 7, 1, 0),
6000636e8b3SPeter Ujfalusi
6010636e8b3SPeter Ujfalusi /* Left HF PDM data routed to Right HFDAC */
6020636e8b3SPeter Ujfalusi SOC_SINGLE("Handsfree Mono to Stereo Playback Switch",
6030636e8b3SPeter Ujfalusi TWL6040_REG_HFRCTL, 5, 1, 0),
6040636e8b3SPeter Ujfalusi
605af958c72SPeter Ujfalusi SOC_ENUM_EXT("PLL Selection", twl6040_power_mode_enum,
606af958c72SPeter Ujfalusi twl6040_pll_get_enum, twl6040_pll_put_enum),
6078ecbabd9SMisael Lopez Cruz };
6088ecbabd9SMisael Lopez Cruz
6098ecbabd9SMisael Lopez Cruz static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = {
6108ecbabd9SMisael Lopez Cruz /* Inputs */
6118ecbabd9SMisael Lopez Cruz SND_SOC_DAPM_INPUT("MAINMIC"),
6128ecbabd9SMisael Lopez Cruz SND_SOC_DAPM_INPUT("HSMIC"),
6138ecbabd9SMisael Lopez Cruz SND_SOC_DAPM_INPUT("SUBMIC"),
6148ecbabd9SMisael Lopez Cruz SND_SOC_DAPM_INPUT("AFML"),
6158ecbabd9SMisael Lopez Cruz SND_SOC_DAPM_INPUT("AFMR"),
6168ecbabd9SMisael Lopez Cruz
6178ecbabd9SMisael Lopez Cruz /* Outputs */
6188ecbabd9SMisael Lopez Cruz SND_SOC_DAPM_OUTPUT("HSOL"),
6198ecbabd9SMisael Lopez Cruz SND_SOC_DAPM_OUTPUT("HSOR"),
6208ecbabd9SMisael Lopez Cruz SND_SOC_DAPM_OUTPUT("HFL"),
6218ecbabd9SMisael Lopez Cruz SND_SOC_DAPM_OUTPUT("HFR"),
622871a05a7SJorge Eduardo Candelaria SND_SOC_DAPM_OUTPUT("EP"),
623fdb625ffSPeter Ujfalusi SND_SOC_DAPM_OUTPUT("AUXL"),
624fdb625ffSPeter Ujfalusi SND_SOC_DAPM_OUTPUT("AUXR"),
62567c34130SPeter Ujfalusi SND_SOC_DAPM_OUTPUT("VIBRAL"),
62667c34130SPeter Ujfalusi SND_SOC_DAPM_OUTPUT("VIBRAR"),
6278ecbabd9SMisael Lopez Cruz
6288ecbabd9SMisael Lopez Cruz /* Analog input muxes for the capture amplifiers */
6298ecbabd9SMisael Lopez Cruz SND_SOC_DAPM_MUX("Analog Left Capture Route",
6308ecbabd9SMisael Lopez Cruz SND_SOC_NOPM, 0, 0, &amicl_control),
6318ecbabd9SMisael Lopez Cruz SND_SOC_DAPM_MUX("Analog Right Capture Route",
6328ecbabd9SMisael Lopez Cruz SND_SOC_NOPM, 0, 0, &amicr_control),
6338ecbabd9SMisael Lopez Cruz
6348ecbabd9SMisael Lopez Cruz /* Analog capture PGAs */
6358ecbabd9SMisael Lopez Cruz SND_SOC_DAPM_PGA("MicAmpL",
6368ecbabd9SMisael Lopez Cruz TWL6040_REG_MICLCTL, 0, 0, NULL, 0),
6378ecbabd9SMisael Lopez Cruz SND_SOC_DAPM_PGA("MicAmpR",
6388ecbabd9SMisael Lopez Cruz TWL6040_REG_MICRCTL, 0, 0, NULL, 0),
6398ecbabd9SMisael Lopez Cruz
640370a0314SJorge Eduardo Candelaria /* Auxiliary FM PGAs */
641370a0314SJorge Eduardo Candelaria SND_SOC_DAPM_PGA("AFMAmpL",
642370a0314SJorge Eduardo Candelaria TWL6040_REG_MICLCTL, 1, 0, NULL, 0),
643370a0314SJorge Eduardo Candelaria SND_SOC_DAPM_PGA("AFMAmpR",
644370a0314SJorge Eduardo Candelaria TWL6040_REG_MICRCTL, 1, 0, NULL, 0),
645370a0314SJorge Eduardo Candelaria
6468ecbabd9SMisael Lopez Cruz /* ADCs */
647805238b1SPeter Ujfalusi SND_SOC_DAPM_ADC("ADC Left", NULL, TWL6040_REG_MICLCTL, 2, 0),
648805238b1SPeter Ujfalusi SND_SOC_DAPM_ADC("ADC Right", NULL, TWL6040_REG_MICRCTL, 2, 0),
6498ecbabd9SMisael Lopez Cruz
6508ecbabd9SMisael Lopez Cruz /* Microphone bias */
651778cee7aSPeter Ujfalusi SND_SOC_DAPM_SUPPLY("Headset Mic Bias",
652778cee7aSPeter Ujfalusi TWL6040_REG_AMICBCTL, 0, 0, NULL, 0),
653778cee7aSPeter Ujfalusi SND_SOC_DAPM_SUPPLY("Main Mic Bias",
654778cee7aSPeter Ujfalusi TWL6040_REG_AMICBCTL, 4, 0, NULL, 0),
655778cee7aSPeter Ujfalusi SND_SOC_DAPM_SUPPLY("Digital Mic1 Bias",
656778cee7aSPeter Ujfalusi TWL6040_REG_DMICBCTL, 0, 0, NULL, 0),
657778cee7aSPeter Ujfalusi SND_SOC_DAPM_SUPPLY("Digital Mic2 Bias",
658778cee7aSPeter Ujfalusi TWL6040_REG_DMICBCTL, 4, 0, NULL, 0),
6598ecbabd9SMisael Lopez Cruz
6608ecbabd9SMisael Lopez Cruz /* DACs */
661805238b1SPeter Ujfalusi SND_SOC_DAPM_DAC("HSDAC Left", NULL, SND_SOC_NOPM, 0, 0),
662805238b1SPeter Ujfalusi SND_SOC_DAPM_DAC("HSDAC Right", NULL, SND_SOC_NOPM, 0, 0),
663805238b1SPeter Ujfalusi SND_SOC_DAPM_DAC("HFDAC Left", NULL, TWL6040_REG_HFLCTL, 0, 0),
664805238b1SPeter Ujfalusi SND_SOC_DAPM_DAC("HFDAC Right", NULL, TWL6040_REG_HFRCTL, 0, 0),
66567c34130SPeter Ujfalusi /* Virtual DAC for vibra path (DL4 channel) */
666805238b1SPeter Ujfalusi SND_SOC_DAPM_DAC("VIBRA DAC", NULL, SND_SOC_NOPM, 0, 0),
6678ecbabd9SMisael Lopez Cruz
668df11ce29SPeter Ujfalusi SND_SOC_DAPM_MUX("Handsfree Left Playback",
669370a0314SJorge Eduardo Candelaria SND_SOC_NOPM, 0, 0, &hfl_mux_controls),
670df11ce29SPeter Ujfalusi SND_SOC_DAPM_MUX("Handsfree Right Playback",
671370a0314SJorge Eduardo Candelaria SND_SOC_NOPM, 0, 0, &hfr_mux_controls),
672370a0314SJorge Eduardo Candelaria /* Analog playback Muxes */
67345b0f60dSPeter Ujfalusi SND_SOC_DAPM_MUX("Headset Left Playback",
674370a0314SJorge Eduardo Candelaria SND_SOC_NOPM, 0, 0, &hsl_mux_controls),
67545b0f60dSPeter Ujfalusi SND_SOC_DAPM_MUX("Headset Right Playback",
676370a0314SJorge Eduardo Candelaria SND_SOC_NOPM, 0, 0, &hsr_mux_controls),
6778ecbabd9SMisael Lopez Cruz
67867c34130SPeter Ujfalusi SND_SOC_DAPM_MUX("Vibra Left Playback", SND_SOC_NOPM, 0, 0,
67967c34130SPeter Ujfalusi &vibral_mux_controls),
68067c34130SPeter Ujfalusi SND_SOC_DAPM_MUX("Vibra Right Playback", SND_SOC_NOPM, 0, 0,
68167c34130SPeter Ujfalusi &vibrar_mux_controls),
68267c34130SPeter Ujfalusi
683317596a6SPeter Ujfalusi SND_SOC_DAPM_SWITCH("Earphone Playback", SND_SOC_NOPM, 0, 0,
684317596a6SPeter Ujfalusi &ep_path_enable_control),
685fdb625ffSPeter Ujfalusi SND_SOC_DAPM_SWITCH("AUXL Playback", SND_SOC_NOPM, 0, 0,
686fdb625ffSPeter Ujfalusi &auxl_switch_control),
687fdb625ffSPeter Ujfalusi SND_SOC_DAPM_SWITCH("AUXR Playback", SND_SOC_NOPM, 0, 0,
688fdb625ffSPeter Ujfalusi &auxr_switch_control),
689317596a6SPeter Ujfalusi
6900fad4ed7SJorge Eduardo Candelaria /* Analog playback drivers */
6913bb8a819SPeter Ujfalusi SND_SOC_DAPM_OUT_DRV("HF Left Driver",
6923bb8a819SPeter Ujfalusi TWL6040_REG_HFLCTL, 4, 0, NULL, 0),
6933bb8a819SPeter Ujfalusi SND_SOC_DAPM_OUT_DRV("HF Right Driver",
6943bb8a819SPeter Ujfalusi TWL6040_REG_HFRCTL, 4, 0, NULL, 0),
6953bb8a819SPeter Ujfalusi SND_SOC_DAPM_OUT_DRV("HS Left Driver",
6963bb8a819SPeter Ujfalusi TWL6040_REG_HSLCTL, 2, 0, NULL, 0),
6973bb8a819SPeter Ujfalusi SND_SOC_DAPM_OUT_DRV("HS Right Driver",
6983bb8a819SPeter Ujfalusi TWL6040_REG_HSRCTL, 2, 0, NULL, 0),
699317596a6SPeter Ujfalusi SND_SOC_DAPM_OUT_DRV_E("Earphone Driver",
700317596a6SPeter Ujfalusi TWL6040_REG_EARCTL, 0, 0, NULL, 0,
701694b0001SPeter Ujfalusi twl6040_ep_drv_event,
702aa1a4108SPeter Ujfalusi SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
70367c34130SPeter Ujfalusi SND_SOC_DAPM_OUT_DRV("Vibra Left Driver",
70467c34130SPeter Ujfalusi TWL6040_REG_VIBCTLL, 0, 0, NULL, 0),
70567c34130SPeter Ujfalusi SND_SOC_DAPM_OUT_DRV("Vibra Right Driver",
70667c34130SPeter Ujfalusi TWL6040_REG_VIBCTLR, 0, 0, NULL, 0),
70767c34130SPeter Ujfalusi
70867c34130SPeter Ujfalusi SND_SOC_DAPM_SUPPLY("Vibra Left Control", TWL6040_REG_VIBCTLL, 2, 0,
70967c34130SPeter Ujfalusi NULL, 0),
71067c34130SPeter Ujfalusi SND_SOC_DAPM_SUPPLY("Vibra Right Control", TWL6040_REG_VIBCTLR, 2, 0,
71167c34130SPeter Ujfalusi NULL, 0),
71233b6816cSPeter Ujfalusi SND_SOC_DAPM_SUPPLY_S("HSDAC Power", 1, SND_SOC_NOPM, 0, 0,
71333b6816cSPeter Ujfalusi twl6040_hs_dac_event,
71433b6816cSPeter Ujfalusi SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
7158ecbabd9SMisael Lopez Cruz
7168ecbabd9SMisael Lopez Cruz /* Analog playback PGAs */
717df11ce29SPeter Ujfalusi SND_SOC_DAPM_PGA("HF Left PGA",
7188ecbabd9SMisael Lopez Cruz TWL6040_REG_HFLCTL, 1, 0, NULL, 0),
719df11ce29SPeter Ujfalusi SND_SOC_DAPM_PGA("HF Right PGA",
7208ecbabd9SMisael Lopez Cruz TWL6040_REG_HFRCTL, 1, 0, NULL, 0),
7218ecbabd9SMisael Lopez Cruz
7228ecbabd9SMisael Lopez Cruz };
7238ecbabd9SMisael Lopez Cruz
7248ecbabd9SMisael Lopez Cruz static const struct snd_soc_dapm_route intercon[] = {
725805238b1SPeter Ujfalusi /* Stream -> DAC mapping */
726805238b1SPeter Ujfalusi {"HSDAC Left", NULL, "Legacy Playback"},
727805238b1SPeter Ujfalusi {"HSDAC Left", NULL, "Headset Playback"},
728805238b1SPeter Ujfalusi {"HSDAC Right", NULL, "Legacy Playback"},
729805238b1SPeter Ujfalusi {"HSDAC Right", NULL, "Headset Playback"},
730805238b1SPeter Ujfalusi
731805238b1SPeter Ujfalusi {"HFDAC Left", NULL, "Legacy Playback"},
732805238b1SPeter Ujfalusi {"HFDAC Left", NULL, "Handsfree Playback"},
733805238b1SPeter Ujfalusi {"HFDAC Right", NULL, "Legacy Playback"},
734805238b1SPeter Ujfalusi {"HFDAC Right", NULL, "Handsfree Playback"},
735805238b1SPeter Ujfalusi
736805238b1SPeter Ujfalusi {"VIBRA DAC", NULL, "Legacy Playback"},
737805238b1SPeter Ujfalusi {"VIBRA DAC", NULL, "Vibra Playback"},
738805238b1SPeter Ujfalusi
739805238b1SPeter Ujfalusi /* ADC -> Stream mapping */
74007ac2296SPeter Ujfalusi {"Legacy Capture" , NULL, "ADC Left"},
74107ac2296SPeter Ujfalusi {"Capture", NULL, "ADC Left"},
74207ac2296SPeter Ujfalusi {"Legacy Capture", NULL, "ADC Right"},
74307ac2296SPeter Ujfalusi {"Capture" , NULL, "ADC Right"},
744805238b1SPeter Ujfalusi
7458ecbabd9SMisael Lopez Cruz /* Capture path */
7468ecbabd9SMisael Lopez Cruz {"Analog Left Capture Route", "Headset Mic", "HSMIC"},
7478ecbabd9SMisael Lopez Cruz {"Analog Left Capture Route", "Main Mic", "MAINMIC"},
7488ecbabd9SMisael Lopez Cruz {"Analog Left Capture Route", "Aux/FM Left", "AFML"},
7498ecbabd9SMisael Lopez Cruz
7508ecbabd9SMisael Lopez Cruz {"Analog Right Capture Route", "Headset Mic", "HSMIC"},
7518ecbabd9SMisael Lopez Cruz {"Analog Right Capture Route", "Sub Mic", "SUBMIC"},
7528ecbabd9SMisael Lopez Cruz {"Analog Right Capture Route", "Aux/FM Right", "AFMR"},
7538ecbabd9SMisael Lopez Cruz
7548ecbabd9SMisael Lopez Cruz {"MicAmpL", NULL, "Analog Left Capture Route"},
7558ecbabd9SMisael Lopez Cruz {"MicAmpR", NULL, "Analog Right Capture Route"},
7568ecbabd9SMisael Lopez Cruz
7578ecbabd9SMisael Lopez Cruz {"ADC Left", NULL, "MicAmpL"},
7588ecbabd9SMisael Lopez Cruz {"ADC Right", NULL, "MicAmpR"},
7598ecbabd9SMisael Lopez Cruz
760370a0314SJorge Eduardo Candelaria /* AFM path */
7615bf692d9SPeter Ujfalusi {"AFMAmpL", NULL, "AFML"},
7625bf692d9SPeter Ujfalusi {"AFMAmpR", NULL, "AFMR"},
7638ecbabd9SMisael Lopez Cruz
76433b6816cSPeter Ujfalusi {"HSDAC Left", NULL, "HSDAC Power"},
76533b6816cSPeter Ujfalusi {"HSDAC Right", NULL, "HSDAC Power"},
76633b6816cSPeter Ujfalusi
76745b0f60dSPeter Ujfalusi {"Headset Left Playback", "HS DAC", "HSDAC Left"},
76845b0f60dSPeter Ujfalusi {"Headset Left Playback", "Line-In amp", "AFMAmpL"},
769370a0314SJorge Eduardo Candelaria
77045b0f60dSPeter Ujfalusi {"Headset Right Playback", "HS DAC", "HSDAC Right"},
77145b0f60dSPeter Ujfalusi {"Headset Right Playback", "Line-In amp", "AFMAmpR"},
772370a0314SJorge Eduardo Candelaria
77345b0f60dSPeter Ujfalusi {"HS Left Driver", NULL, "Headset Left Playback"},
77445b0f60dSPeter Ujfalusi {"HS Right Driver", NULL, "Headset Right Playback"},
7758ecbabd9SMisael Lopez Cruz
77645b0f60dSPeter Ujfalusi {"HSOL", NULL, "HS Left Driver"},
77745b0f60dSPeter Ujfalusi {"HSOR", NULL, "HS Right Driver"},
7788ecbabd9SMisael Lopez Cruz
779871a05a7SJorge Eduardo Candelaria /* Earphone playback path */
780317596a6SPeter Ujfalusi {"Earphone Playback", "Switch", "HSDAC Left"},
781317596a6SPeter Ujfalusi {"Earphone Driver", NULL, "Earphone Playback"},
782871a05a7SJorge Eduardo Candelaria {"EP", NULL, "Earphone Driver"},
783871a05a7SJorge Eduardo Candelaria
784df11ce29SPeter Ujfalusi {"Handsfree Left Playback", "HF DAC", "HFDAC Left"},
785df11ce29SPeter Ujfalusi {"Handsfree Left Playback", "Line-In amp", "AFMAmpL"},
7868ecbabd9SMisael Lopez Cruz
787df11ce29SPeter Ujfalusi {"Handsfree Right Playback", "HF DAC", "HFDAC Right"},
788df11ce29SPeter Ujfalusi {"Handsfree Right Playback", "Line-In amp", "AFMAmpR"},
789370a0314SJorge Eduardo Candelaria
790df11ce29SPeter Ujfalusi {"HF Left PGA", NULL, "Handsfree Left Playback"},
791df11ce29SPeter Ujfalusi {"HF Right PGA", NULL, "Handsfree Right Playback"},
7928ecbabd9SMisael Lopez Cruz
793df11ce29SPeter Ujfalusi {"HF Left Driver", NULL, "HF Left PGA"},
794df11ce29SPeter Ujfalusi {"HF Right Driver", NULL, "HF Right PGA"},
7958ecbabd9SMisael Lopez Cruz
796df11ce29SPeter Ujfalusi {"HFL", NULL, "HF Left Driver"},
797df11ce29SPeter Ujfalusi {"HFR", NULL, "HF Right Driver"},
798fdb625ffSPeter Ujfalusi
799fdb625ffSPeter Ujfalusi {"AUXL Playback", "Switch", "HF Left PGA"},
800fdb625ffSPeter Ujfalusi {"AUXR Playback", "Switch", "HF Right PGA"},
801fdb625ffSPeter Ujfalusi
802fdb625ffSPeter Ujfalusi {"AUXL", NULL, "AUXL Playback"},
803fdb625ffSPeter Ujfalusi {"AUXR", NULL, "AUXR Playback"},
80467c34130SPeter Ujfalusi
80567c34130SPeter Ujfalusi /* Vibrator paths */
80667c34130SPeter Ujfalusi {"Vibra Left Playback", "Audio PDM", "VIBRA DAC"},
80767c34130SPeter Ujfalusi {"Vibra Right Playback", "Audio PDM", "VIBRA DAC"},
80867c34130SPeter Ujfalusi
80967c34130SPeter Ujfalusi {"Vibra Left Driver", NULL, "Vibra Left Playback"},
81067c34130SPeter Ujfalusi {"Vibra Right Driver", NULL, "Vibra Right Playback"},
81167c34130SPeter Ujfalusi {"Vibra Left Driver", NULL, "Vibra Left Control"},
81267c34130SPeter Ujfalusi {"Vibra Right Driver", NULL, "Vibra Right Control"},
81367c34130SPeter Ujfalusi
81467c34130SPeter Ujfalusi {"VIBRAL", NULL, "Vibra Left Driver"},
81567c34130SPeter Ujfalusi {"VIBRAR", NULL, "Vibra Right Driver"},
8168ecbabd9SMisael Lopez Cruz };
8178ecbabd9SMisael Lopez Cruz
twl6040_set_bias_level(struct snd_soc_component * component,enum snd_soc_bias_level level)8187480389fSKuninori Morimoto static int twl6040_set_bias_level(struct snd_soc_component *component,
8198ecbabd9SMisael Lopez Cruz enum snd_soc_bias_level level)
8208ecbabd9SMisael Lopez Cruz {
8217480389fSKuninori Morimoto struct twl6040 *twl6040 = to_twl6040(component);
8227480389fSKuninori Morimoto struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
8231135ef11SPeter Ujfalusi int ret = 0;
8248ecbabd9SMisael Lopez Cruz
8258ecbabd9SMisael Lopez Cruz switch (level) {
8268ecbabd9SMisael Lopez Cruz case SND_SOC_BIAS_ON:
8278ecbabd9SMisael Lopez Cruz break;
8288ecbabd9SMisael Lopez Cruz case SND_SOC_BIAS_PREPARE:
8298ecbabd9SMisael Lopez Cruz break;
8308ecbabd9SMisael Lopez Cruz case SND_SOC_BIAS_STANDBY:
8311135ef11SPeter Ujfalusi if (priv->codec_powered) {
8321135ef11SPeter Ujfalusi /* Select low power PLL in standby */
8331135ef11SPeter Ujfalusi ret = twl6040_set_pll(twl6040, TWL6040_SYSCLK_SEL_LPPLL,
8341135ef11SPeter Ujfalusi 32768, 19200000);
8358ecbabd9SMisael Lopez Cruz break;
8361135ef11SPeter Ujfalusi }
8378ecbabd9SMisael Lopez Cruz
838fb34d3d5SMisael Lopez Cruz ret = twl6040_power(twl6040, 1);
8398ecbabd9SMisael Lopez Cruz if (ret)
8401135ef11SPeter Ujfalusi break;
8418ecbabd9SMisael Lopez Cruz
8428ecbabd9SMisael Lopez Cruz priv->codec_powered = 1;
8438ecbabd9SMisael Lopez Cruz
84465b7ceccSOlaya, Margarita /* Set external boost GPO */
8457480389fSKuninori Morimoto twl6040_write(component, TWL6040_REG_GPOCTL, 0x02);
8468ecbabd9SMisael Lopez Cruz break;
8478ecbabd9SMisael Lopez Cruz case SND_SOC_BIAS_OFF:
8488ecbabd9SMisael Lopez Cruz if (!priv->codec_powered)
8498ecbabd9SMisael Lopez Cruz break;
8508ecbabd9SMisael Lopez Cruz
851fb34d3d5SMisael Lopez Cruz twl6040_power(twl6040, 0);
8528ecbabd9SMisael Lopez Cruz priv->codec_powered = 0;
8538ecbabd9SMisael Lopez Cruz break;
8548ecbabd9SMisael Lopez Cruz }
8558ecbabd9SMisael Lopez Cruz
8561135ef11SPeter Ujfalusi return ret;
8578ecbabd9SMisael Lopez Cruz }
8588ecbabd9SMisael Lopez Cruz
twl6040_startup(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)8598ecbabd9SMisael Lopez Cruz static int twl6040_startup(struct snd_pcm_substream *substream,
8608ecbabd9SMisael Lopez Cruz struct snd_soc_dai *dai)
8618ecbabd9SMisael Lopez Cruz {
8627480389fSKuninori Morimoto struct snd_soc_component *component = dai->component;
8637480389fSKuninori Morimoto struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
8648ecbabd9SMisael Lopez Cruz
8658ecbabd9SMisael Lopez Cruz snd_pcm_hw_constraint_list(substream->runtime, 0,
8668ecbabd9SMisael Lopez Cruz SNDRV_PCM_HW_PARAM_RATE,
867f53c346cSPeter Ujfalusi &sysclk_constraints[priv->pll_power_mode]);
8688ecbabd9SMisael Lopez Cruz
8698ecbabd9SMisael Lopez Cruz return 0;
8708ecbabd9SMisael Lopez Cruz }
8718ecbabd9SMisael Lopez Cruz
twl6040_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)8728ecbabd9SMisael Lopez Cruz static int twl6040_hw_params(struct snd_pcm_substream *substream,
8738ecbabd9SMisael Lopez Cruz struct snd_pcm_hw_params *params,
8748ecbabd9SMisael Lopez Cruz struct snd_soc_dai *dai)
8758ecbabd9SMisael Lopez Cruz {
8767480389fSKuninori Morimoto struct snd_soc_component *component = dai->component;
8777480389fSKuninori Morimoto struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
8788ecbabd9SMisael Lopez Cruz int rate;
8798ecbabd9SMisael Lopez Cruz
8808ecbabd9SMisael Lopez Cruz rate = params_rate(params);
8818ecbabd9SMisael Lopez Cruz switch (rate) {
88260ea4cecSOlaya, Margarita case 11250:
88360ea4cecSOlaya, Margarita case 22500:
88460ea4cecSOlaya, Margarita case 44100:
8858ecbabd9SMisael Lopez Cruz case 88200:
886753621c2SPeter Ujfalusi /* These rates are not supported when HPPLL is in use */
887753621c2SPeter Ujfalusi if (unlikely(priv->pll == TWL6040_SYSCLK_SEL_HPPLL)) {
8887480389fSKuninori Morimoto dev_err(component->dev, "HPPLL does not support rate %d\n",
889753621c2SPeter Ujfalusi rate);
890753621c2SPeter Ujfalusi return -EINVAL;
891753621c2SPeter Ujfalusi }
8928ecbabd9SMisael Lopez Cruz priv->sysclk = 17640000;
8938ecbabd9SMisael Lopez Cruz break;
89460ea4cecSOlaya, Margarita case 8000:
89560ea4cecSOlaya, Margarita case 16000:
89660ea4cecSOlaya, Margarita case 32000:
89760ea4cecSOlaya, Margarita case 48000:
8988ecbabd9SMisael Lopez Cruz case 96000:
8998ecbabd9SMisael Lopez Cruz priv->sysclk = 19200000;
9008ecbabd9SMisael Lopez Cruz break;
9018ecbabd9SMisael Lopez Cruz default:
9027480389fSKuninori Morimoto dev_err(component->dev, "unsupported rate %d\n", rate);
9038ecbabd9SMisael Lopez Cruz return -EINVAL;
9048ecbabd9SMisael Lopez Cruz }
9058ecbabd9SMisael Lopez Cruz
9068ecbabd9SMisael Lopez Cruz return 0;
9078ecbabd9SMisael Lopez Cruz }
9088ecbabd9SMisael Lopez Cruz
twl6040_prepare(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)9094e624d06SOlaya, Margarita static int twl6040_prepare(struct snd_pcm_substream *substream,
9104e624d06SOlaya, Margarita struct snd_soc_dai *dai)
9118ecbabd9SMisael Lopez Cruz {
9127480389fSKuninori Morimoto struct snd_soc_component *component = dai->component;
9137480389fSKuninori Morimoto struct twl6040 *twl6040 = to_twl6040(component);
9147480389fSKuninori Morimoto struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
915753621c2SPeter Ujfalusi int ret;
9168ecbabd9SMisael Lopez Cruz
9174e624d06SOlaya, Margarita if (!priv->sysclk) {
9187480389fSKuninori Morimoto dev_err(component->dev,
9194e624d06SOlaya, Margarita "no mclk configured, call set_sysclk() on init\n");
9204e624d06SOlaya, Margarita return -EINVAL;
9214e624d06SOlaya, Margarita }
9224e624d06SOlaya, Margarita
923753621c2SPeter Ujfalusi ret = twl6040_set_pll(twl6040, priv->pll, priv->clk_in, priv->sysclk);
924753621c2SPeter Ujfalusi if (ret) {
9257480389fSKuninori Morimoto dev_err(component->dev, "Can not set PLL (%d)\n", ret);
926753621c2SPeter Ujfalusi return -EPERM;
927753621c2SPeter Ujfalusi }
928753621c2SPeter Ujfalusi
9298ecbabd9SMisael Lopez Cruz return 0;
9308ecbabd9SMisael Lopez Cruz }
9318ecbabd9SMisael Lopez Cruz
twl6040_set_dai_sysclk(struct snd_soc_dai * codec_dai,int clk_id,unsigned int freq,int dir)9328ecbabd9SMisael Lopez Cruz static int twl6040_set_dai_sysclk(struct snd_soc_dai *codec_dai,
9338ecbabd9SMisael Lopez Cruz int clk_id, unsigned int freq, int dir)
9348ecbabd9SMisael Lopez Cruz {
9357480389fSKuninori Morimoto struct snd_soc_component *component = codec_dai->component;
9367480389fSKuninori Morimoto struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
9378ecbabd9SMisael Lopez Cruz
9388ecbabd9SMisael Lopez Cruz switch (clk_id) {
9398ecbabd9SMisael Lopez Cruz case TWL6040_SYSCLK_SEL_LPPLL:
9408ecbabd9SMisael Lopez Cruz case TWL6040_SYSCLK_SEL_HPPLL:
941753621c2SPeter Ujfalusi priv->pll = clk_id;
942753621c2SPeter Ujfalusi priv->clk_in = freq;
9438ecbabd9SMisael Lopez Cruz break;
9448ecbabd9SMisael Lopez Cruz default:
9457480389fSKuninori Morimoto dev_err(component->dev, "unknown clk_id %d\n", clk_id);
9468ecbabd9SMisael Lopez Cruz return -EINVAL;
9478ecbabd9SMisael Lopez Cruz }
9488ecbabd9SMisael Lopez Cruz
9498ecbabd9SMisael Lopez Cruz return 0;
9508ecbabd9SMisael Lopez Cruz }
9518ecbabd9SMisael Lopez Cruz
twl6040_mute_path(struct snd_soc_component * component,enum twl6040_dai_id id,int mute)9527480389fSKuninori Morimoto static void twl6040_mute_path(struct snd_soc_component *component, enum twl6040_dai_id id,
95398c5fb1fSPeter Ujfalusi int mute)
95498c5fb1fSPeter Ujfalusi {
9557480389fSKuninori Morimoto struct twl6040 *twl6040 = to_twl6040(component);
9567480389fSKuninori Morimoto struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
95798c5fb1fSPeter Ujfalusi int hslctl, hsrctl, earctl;
95898c5fb1fSPeter Ujfalusi int hflctl, hfrctl;
95998c5fb1fSPeter Ujfalusi
96098c5fb1fSPeter Ujfalusi switch (id) {
96198c5fb1fSPeter Ujfalusi case TWL6040_DAI_DL1:
9627480389fSKuninori Morimoto hslctl = twl6040_read(component, TWL6040_REG_HSLCTL);
9637480389fSKuninori Morimoto hsrctl = twl6040_read(component, TWL6040_REG_HSRCTL);
9647480389fSKuninori Morimoto earctl = twl6040_read(component, TWL6040_REG_EARCTL);
96598c5fb1fSPeter Ujfalusi
96698c5fb1fSPeter Ujfalusi if (mute) {
96798c5fb1fSPeter Ujfalusi /* Power down drivers and DACs */
96898c5fb1fSPeter Ujfalusi earctl &= ~0x01;
96998c5fb1fSPeter Ujfalusi hslctl &= ~(TWL6040_HSDRVENA | TWL6040_HSDACENA);
97098c5fb1fSPeter Ujfalusi hsrctl &= ~(TWL6040_HSDRVENA | TWL6040_HSDACENA);
97198c5fb1fSPeter Ujfalusi
97298c5fb1fSPeter Ujfalusi }
97398c5fb1fSPeter Ujfalusi
97498c5fb1fSPeter Ujfalusi twl6040_reg_write(twl6040, TWL6040_REG_EARCTL, earctl);
97598c5fb1fSPeter Ujfalusi twl6040_reg_write(twl6040, TWL6040_REG_HSLCTL, hslctl);
97698c5fb1fSPeter Ujfalusi twl6040_reg_write(twl6040, TWL6040_REG_HSRCTL, hsrctl);
97798c5fb1fSPeter Ujfalusi priv->dl1_unmuted = !mute;
97898c5fb1fSPeter Ujfalusi break;
97998c5fb1fSPeter Ujfalusi case TWL6040_DAI_DL2:
9807480389fSKuninori Morimoto hflctl = twl6040_read(component, TWL6040_REG_HFLCTL);
9817480389fSKuninori Morimoto hfrctl = twl6040_read(component, TWL6040_REG_HFRCTL);
98298c5fb1fSPeter Ujfalusi
98398c5fb1fSPeter Ujfalusi if (mute) {
98498c5fb1fSPeter Ujfalusi /* Power down drivers and DACs */
98598c5fb1fSPeter Ujfalusi hflctl &= ~(TWL6040_HFDACENA | TWL6040_HFPGAENA |
98645c04704SPeter Ujfalusi TWL6040_HFDRVENA | TWL6040_HFSWENA);
98798c5fb1fSPeter Ujfalusi hfrctl &= ~(TWL6040_HFDACENA | TWL6040_HFPGAENA |
98845c04704SPeter Ujfalusi TWL6040_HFDRVENA | TWL6040_HFSWENA);
98998c5fb1fSPeter Ujfalusi }
99098c5fb1fSPeter Ujfalusi
99198c5fb1fSPeter Ujfalusi twl6040_reg_write(twl6040, TWL6040_REG_HFLCTL, hflctl);
99298c5fb1fSPeter Ujfalusi twl6040_reg_write(twl6040, TWL6040_REG_HFRCTL, hfrctl);
99398c5fb1fSPeter Ujfalusi priv->dl2_unmuted = !mute;
99498c5fb1fSPeter Ujfalusi break;
99598c5fb1fSPeter Ujfalusi default:
99698c5fb1fSPeter Ujfalusi break;
997bf551413SSachin Kamat }
99898c5fb1fSPeter Ujfalusi }
99998c5fb1fSPeter Ujfalusi
twl6040_mute_stream(struct snd_soc_dai * dai,int mute,int direction)1000*54b59270SKuninori Morimoto static int twl6040_mute_stream(struct snd_soc_dai *dai, int mute, int direction)
100198c5fb1fSPeter Ujfalusi {
100298c5fb1fSPeter Ujfalusi switch (dai->id) {
100398c5fb1fSPeter Ujfalusi case TWL6040_DAI_LEGACY:
10047480389fSKuninori Morimoto twl6040_mute_path(dai->component, TWL6040_DAI_DL1, mute);
10057480389fSKuninori Morimoto twl6040_mute_path(dai->component, TWL6040_DAI_DL2, mute);
100698c5fb1fSPeter Ujfalusi break;
100798c5fb1fSPeter Ujfalusi case TWL6040_DAI_DL1:
100898c5fb1fSPeter Ujfalusi case TWL6040_DAI_DL2:
10097480389fSKuninori Morimoto twl6040_mute_path(dai->component, dai->id, mute);
101098c5fb1fSPeter Ujfalusi break;
101198c5fb1fSPeter Ujfalusi default:
101298c5fb1fSPeter Ujfalusi break;
101398c5fb1fSPeter Ujfalusi }
101498c5fb1fSPeter Ujfalusi
101598c5fb1fSPeter Ujfalusi return 0;
101698c5fb1fSPeter Ujfalusi }
101798c5fb1fSPeter Ujfalusi
101885e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops twl6040_dai_ops = {
10198ecbabd9SMisael Lopez Cruz .startup = twl6040_startup,
10208ecbabd9SMisael Lopez Cruz .hw_params = twl6040_hw_params,
10214e624d06SOlaya, Margarita .prepare = twl6040_prepare,
10228ecbabd9SMisael Lopez Cruz .set_sysclk = twl6040_set_dai_sysclk,
1023*54b59270SKuninori Morimoto .mute_stream = twl6040_mute_stream,
1024*54b59270SKuninori Morimoto .no_capture_mute = 1,
10258ecbabd9SMisael Lopez Cruz };
10268ecbabd9SMisael Lopez Cruz
10276510bdc3SLiam Girdwood static struct snd_soc_dai_driver twl6040_dai[] = {
10286510bdc3SLiam Girdwood {
1029d13f1fe0SPeter Ujfalusi .name = "twl6040-legacy",
103068897497SPeter Ujfalusi .id = TWL6040_DAI_LEGACY,
10318ecbabd9SMisael Lopez Cruz .playback = {
1032805238b1SPeter Ujfalusi .stream_name = "Legacy Playback",
10338ecbabd9SMisael Lopez Cruz .channels_min = 1,
1034cdd5054cSPeter Ujfalusi .channels_max = 5,
10358ecbabd9SMisael Lopez Cruz .rates = TWL6040_RATES,
10368ecbabd9SMisael Lopez Cruz .formats = TWL6040_FORMATS,
10378ecbabd9SMisael Lopez Cruz },
10388ecbabd9SMisael Lopez Cruz .capture = {
1039805238b1SPeter Ujfalusi .stream_name = "Legacy Capture",
10408ecbabd9SMisael Lopez Cruz .channels_min = 1,
10418ecbabd9SMisael Lopez Cruz .channels_max = 2,
10428ecbabd9SMisael Lopez Cruz .rates = TWL6040_RATES,
10438ecbabd9SMisael Lopez Cruz .formats = TWL6040_FORMATS,
10448ecbabd9SMisael Lopez Cruz },
10458ecbabd9SMisael Lopez Cruz .ops = &twl6040_dai_ops,
104621385eebSPeter Ujfalusi },
104721385eebSPeter Ujfalusi {
10486510bdc3SLiam Girdwood .name = "twl6040-ul",
104968897497SPeter Ujfalusi .id = TWL6040_DAI_UL,
10508ecbabd9SMisael Lopez Cruz .capture = {
10518ecbabd9SMisael Lopez Cruz .stream_name = "Capture",
10528ecbabd9SMisael Lopez Cruz .channels_min = 1,
10538ecbabd9SMisael Lopez Cruz .channels_max = 2,
10548ecbabd9SMisael Lopez Cruz .rates = TWL6040_RATES,
10558ecbabd9SMisael Lopez Cruz .formats = TWL6040_FORMATS,
10568ecbabd9SMisael Lopez Cruz },
10578ecbabd9SMisael Lopez Cruz .ops = &twl6040_dai_ops,
10586510bdc3SLiam Girdwood },
10596510bdc3SLiam Girdwood {
10606510bdc3SLiam Girdwood .name = "twl6040-dl1",
106168897497SPeter Ujfalusi .id = TWL6040_DAI_DL1,
10626510bdc3SLiam Girdwood .playback = {
10636510bdc3SLiam Girdwood .stream_name = "Headset Playback",
10646510bdc3SLiam Girdwood .channels_min = 1,
10656510bdc3SLiam Girdwood .channels_max = 2,
10666510bdc3SLiam Girdwood .rates = TWL6040_RATES,
10676510bdc3SLiam Girdwood .formats = TWL6040_FORMATS,
10686510bdc3SLiam Girdwood },
10696510bdc3SLiam Girdwood .ops = &twl6040_dai_ops,
10706510bdc3SLiam Girdwood },
10716510bdc3SLiam Girdwood {
10726510bdc3SLiam Girdwood .name = "twl6040-dl2",
107368897497SPeter Ujfalusi .id = TWL6040_DAI_DL2,
10746510bdc3SLiam Girdwood .playback = {
10756510bdc3SLiam Girdwood .stream_name = "Handsfree Playback",
10766510bdc3SLiam Girdwood .channels_min = 1,
10776510bdc3SLiam Girdwood .channels_max = 2,
10786510bdc3SLiam Girdwood .rates = TWL6040_RATES,
10796510bdc3SLiam Girdwood .formats = TWL6040_FORMATS,
10806510bdc3SLiam Girdwood },
10816510bdc3SLiam Girdwood .ops = &twl6040_dai_ops,
10826510bdc3SLiam Girdwood },
10836510bdc3SLiam Girdwood {
10846510bdc3SLiam Girdwood .name = "twl6040-vib",
108568897497SPeter Ujfalusi .id = TWL6040_DAI_VIB,
10866510bdc3SLiam Girdwood .playback = {
10876510bdc3SLiam Girdwood .stream_name = "Vibra Playback",
1088d8dd032dSPeter Ujfalusi .channels_min = 1,
1089d8dd032dSPeter Ujfalusi .channels_max = 1,
10906510bdc3SLiam Girdwood .rates = SNDRV_PCM_RATE_CONTINUOUS,
10916510bdc3SLiam Girdwood .formats = TWL6040_FORMATS,
10926510bdc3SLiam Girdwood },
10936510bdc3SLiam Girdwood .ops = &twl6040_dai_ops,
10946510bdc3SLiam Girdwood },
10958ecbabd9SMisael Lopez Cruz };
10968ecbabd9SMisael Lopez Cruz
twl6040_probe(struct snd_soc_component * component)10977480389fSKuninori Morimoto static int twl6040_probe(struct snd_soc_component *component)
10988ecbabd9SMisael Lopez Cruz {
10998ecbabd9SMisael Lopez Cruz struct twl6040_data *priv;
11007480389fSKuninori Morimoto struct platform_device *pdev = to_platform_device(component->dev);
11018ecbabd9SMisael Lopez Cruz int ret = 0;
11028ecbabd9SMisael Lopez Cruz
11037480389fSKuninori Morimoto priv = devm_kzalloc(component->dev, sizeof(*priv), GFP_KERNEL);
11048ecbabd9SMisael Lopez Cruz if (priv == NULL)
11058ecbabd9SMisael Lopez Cruz return -ENOMEM;
11069523fcdcSPeter Ujfalusi
11077480389fSKuninori Morimoto snd_soc_component_set_drvdata(component, priv);
11088ecbabd9SMisael Lopez Cruz
11097480389fSKuninori Morimoto priv->component = component;
1110a2d2362eSJorge Eduardo Candelaria
11112a433b9dSPeter Ujfalusi priv->plug_irq = platform_get_irq(pdev, 0);
1112cf9441adSStephen Boyd if (priv->plug_irq < 0)
1113cfe267daSGustavo A. R. Silva return priv->plug_irq;
11148ecbabd9SMisael Lopez Cruz
111546dd0b93SPeter Ujfalusi INIT_DELAYED_WORK(&priv->hs_jack.work, twl6040_accessory_work);
1116a2d2362eSJorge Eduardo Candelaria
1117a2d2362eSJorge Eduardo Candelaria mutex_init(&priv->mutex);
11188ecbabd9SMisael Lopez Cruz
1119f60596d6SPeter Ujfalusi ret = request_threaded_irq(priv->plug_irq, NULL,
1120208ba89bSFabio Estevam twl6040_audio_handler,
1121208ba89bSFabio Estevam IRQF_NO_SUSPEND | IRQF_ONESHOT,
11227480389fSKuninori Morimoto "twl6040_irq_plug", component);
1123fb34d3d5SMisael Lopez Cruz if (ret) {
11247480389fSKuninori Morimoto dev_err(component->dev, "PLUG IRQ request failed: %d\n", ret);
1125da2107d1SPeter Ujfalusi return ret;
1126fb34d3d5SMisael Lopez Cruz }
1127fb34d3d5SMisael Lopez Cruz
11287480389fSKuninori Morimoto snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY);
11297480389fSKuninori Morimoto twl6040_init_chip(component);
1130fb34d3d5SMisael Lopez Cruz
1131626bcacbSPeter Ujfalusi return 0;
11328ecbabd9SMisael Lopez Cruz }
11338ecbabd9SMisael Lopez Cruz
twl6040_remove(struct snd_soc_component * component)11347480389fSKuninori Morimoto static void twl6040_remove(struct snd_soc_component *component)
11358ecbabd9SMisael Lopez Cruz {
11367480389fSKuninori Morimoto struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
1137f60596d6SPeter Ujfalusi
11387480389fSKuninori Morimoto free_irq(priv->plug_irq, component);
1139f0fba2adSLiam Girdwood }
11408ecbabd9SMisael Lopez Cruz
11417480389fSKuninori Morimoto static const struct snd_soc_component_driver soc_component_dev_twl6040 = {
1142f0fba2adSLiam Girdwood .probe = twl6040_probe,
1143f0fba2adSLiam Girdwood .remove = twl6040_remove,
11448146acffSTony Lindgren .read = twl6040_read,
1145c9d06665SPeter Ujfalusi .write = twl6040_write,
1146f0fba2adSLiam Girdwood .set_bias_level = twl6040_set_bias_level,
1147a175fce0SPeter Ujfalusi .controls = twl6040_snd_controls,
1148a175fce0SPeter Ujfalusi .num_controls = ARRAY_SIZE(twl6040_snd_controls),
1149a175fce0SPeter Ujfalusi .dapm_widgets = twl6040_dapm_widgets,
1150a175fce0SPeter Ujfalusi .num_dapm_widgets = ARRAY_SIZE(twl6040_dapm_widgets),
1151a175fce0SPeter Ujfalusi .dapm_routes = intercon,
1152a175fce0SPeter Ujfalusi .num_dapm_routes = ARRAY_SIZE(intercon),
11537480389fSKuninori Morimoto .suspend_bias_off = 1,
11547480389fSKuninori Morimoto .idle_bias_on = 1,
11557480389fSKuninori Morimoto .endianness = 1,
1156f0fba2adSLiam Girdwood };
1157f0fba2adSLiam Girdwood
twl6040_codec_probe(struct platform_device * pdev)11587a79e94eSBill Pemberton static int twl6040_codec_probe(struct platform_device *pdev)
1159f0fba2adSLiam Girdwood {
11607480389fSKuninori Morimoto return devm_snd_soc_register_component(&pdev->dev,
11617480389fSKuninori Morimoto &soc_component_dev_twl6040,
11626510bdc3SLiam Girdwood twl6040_dai, ARRAY_SIZE(twl6040_dai));
1163f0fba2adSLiam Girdwood }
1164f0fba2adSLiam Girdwood
11658ecbabd9SMisael Lopez Cruz static struct platform_driver twl6040_codec_driver = {
11668ecbabd9SMisael Lopez Cruz .driver = {
1167f0fba2adSLiam Girdwood .name = "twl6040-codec",
11688ecbabd9SMisael Lopez Cruz },
11698ecbabd9SMisael Lopez Cruz .probe = twl6040_codec_probe,
11708ecbabd9SMisael Lopez Cruz };
11718ecbabd9SMisael Lopez Cruz
11725bbcc3c0SMark Brown module_platform_driver(twl6040_codec_driver);
11738ecbabd9SMisael Lopez Cruz
11748ecbabd9SMisael Lopez Cruz MODULE_DESCRIPTION("ASoC TWL6040 codec driver");
11758ecbabd9SMisael Lopez Cruz MODULE_AUTHOR("Misael Lopez Cruz");
11768ecbabd9SMisael Lopez Cruz MODULE_LICENSE("GPL");
1177