diff options
Diffstat (limited to 'sound/soc/codecs/twl4030.c')
-rw-r--r-- | sound/soc/codecs/twl4030.c | 157 |
1 files changed, 112 insertions, 45 deletions
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 391fcfc7b63b..e7f608996c41 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c | |||
@@ -26,8 +26,11 @@ | |||
26 | #include <linux/pm.h> | 26 | #include <linux/pm.h> |
27 | #include <linux/i2c.h> | 27 | #include <linux/i2c.h> |
28 | #include <linux/platform_device.h> | 28 | #include <linux/platform_device.h> |
29 | #include <linux/of.h> | ||
30 | #include <linux/of_gpio.h> | ||
29 | #include <linux/i2c/twl.h> | 31 | #include <linux/i2c/twl.h> |
30 | #include <linux/slab.h> | 32 | #include <linux/slab.h> |
33 | #include <linux/gpio.h> | ||
31 | #include <sound/core.h> | 34 | #include <sound/core.h> |
32 | #include <sound/pcm.h> | 35 | #include <sound/pcm.h> |
33 | #include <sound/pcm_params.h> | 36 | #include <sound/pcm_params.h> |
@@ -152,8 +155,7 @@ struct twl4030_priv { | |||
152 | u8 predrivel_enabled, predriver_enabled; | 155 | u8 predrivel_enabled, predriver_enabled; |
153 | u8 carkitl_enabled, carkitr_enabled; | 156 | u8 carkitl_enabled, carkitr_enabled; |
154 | 157 | ||
155 | /* Delay needed after enabling the digimic interface */ | 158 | struct twl4030_codec_data *pdata; |
156 | unsigned int digimic_delay; | ||
157 | }; | 159 | }; |
158 | 160 | ||
159 | /* | 161 | /* |
@@ -295,13 +297,73 @@ static inline void twl4030_reset_registers(struct snd_soc_codec *codec) | |||
295 | 297 | ||
296 | } | 298 | } |
297 | 299 | ||
298 | static void twl4030_init_chip(struct snd_soc_codec *codec) | 300 | static void twl4030_setup_pdata_of(struct twl4030_codec_data *pdata, |
301 | struct device_node *node) | ||
302 | { | ||
303 | int value; | ||
304 | |||
305 | of_property_read_u32(node, "ti,digimic_delay", | ||
306 | &pdata->digimic_delay); | ||
307 | of_property_read_u32(node, "ti,ramp_delay_value", | ||
308 | &pdata->ramp_delay_value); | ||
309 | of_property_read_u32(node, "ti,offset_cncl_path", | ||
310 | &pdata->offset_cncl_path); | ||
311 | if (!of_property_read_u32(node, "ti,hs_extmute", &value)) | ||
312 | pdata->hs_extmute = value; | ||
313 | |||
314 | pdata->hs_extmute_gpio = of_get_named_gpio(node, | ||
315 | "ti,hs_extmute_gpio", 0); | ||
316 | if (gpio_is_valid(pdata->hs_extmute_gpio)) | ||
317 | pdata->hs_extmute = 1; | ||
318 | } | ||
319 | |||
320 | static struct twl4030_codec_data *twl4030_get_pdata(struct snd_soc_codec *codec) | ||
299 | { | 321 | { |
300 | struct twl4030_codec_data *pdata = dev_get_platdata(codec->dev); | 322 | struct twl4030_codec_data *pdata = dev_get_platdata(codec->dev); |
323 | struct device_node *twl4030_codec_node = NULL; | ||
324 | |||
325 | twl4030_codec_node = of_find_node_by_name(codec->dev->parent->of_node, | ||
326 | "codec"); | ||
327 | |||
328 | if (!pdata && twl4030_codec_node) { | ||
329 | pdata = devm_kzalloc(codec->dev, | ||
330 | sizeof(struct twl4030_codec_data), | ||
331 | GFP_KERNEL); | ||
332 | if (!pdata) { | ||
333 | dev_err(codec->dev, "Can not allocate memory\n"); | ||
334 | return NULL; | ||
335 | } | ||
336 | twl4030_setup_pdata_of(pdata, twl4030_codec_node); | ||
337 | } | ||
338 | |||
339 | return pdata; | ||
340 | } | ||
341 | |||
342 | static void twl4030_init_chip(struct snd_soc_codec *codec) | ||
343 | { | ||
344 | struct twl4030_codec_data *pdata; | ||
301 | struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); | 345 | struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); |
302 | u8 reg, byte; | 346 | u8 reg, byte; |
303 | int i = 0; | 347 | int i = 0; |
304 | 348 | ||
349 | pdata = twl4030_get_pdata(codec); | ||
350 | |||
351 | if (pdata && pdata->hs_extmute && | ||
352 | gpio_is_valid(pdata->hs_extmute_gpio)) { | ||
353 | int ret; | ||
354 | |||
355 | if (!pdata->hs_extmute_gpio) | ||
356 | dev_warn(codec->dev, | ||
357 | "Extmute GPIO is 0 is this correct?\n"); | ||
358 | |||
359 | ret = gpio_request_one(pdata->hs_extmute_gpio, | ||
360 | GPIOF_OUT_INIT_LOW, "hs_extmute"); | ||
361 | if (ret) { | ||
362 | dev_err(codec->dev, "Failed to get hs_extmute GPIO\n"); | ||
363 | pdata->hs_extmute_gpio = -1; | ||
364 | } | ||
365 | } | ||
366 | |||
305 | /* Check defaults, if instructed before anything else */ | 367 | /* Check defaults, if instructed before anything else */ |
306 | if (pdata && pdata->check_defaults) | 368 | if (pdata && pdata->check_defaults) |
307 | twl4030_check_defaults(codec); | 369 | twl4030_check_defaults(codec); |
@@ -331,7 +393,7 @@ static void twl4030_init_chip(struct snd_soc_codec *codec) | |||
331 | if (!pdata) | 393 | if (!pdata) |
332 | return; | 394 | return; |
333 | 395 | ||
334 | twl4030->digimic_delay = pdata->digimic_delay; | 396 | twl4030->pdata = pdata; |
335 | 397 | ||
336 | reg = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET); | 398 | reg = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET); |
337 | reg &= ~TWL4030_RAMP_DELAY; | 399 | reg &= ~TWL4030_RAMP_DELAY; |
@@ -732,9 +794,9 @@ static int aif_event(struct snd_soc_dapm_widget *w, | |||
732 | 794 | ||
733 | static void headset_ramp(struct snd_soc_codec *codec, int ramp) | 795 | static void headset_ramp(struct snd_soc_codec *codec, int ramp) |
734 | { | 796 | { |
735 | struct twl4030_codec_data *pdata = codec->dev->platform_data; | ||
736 | unsigned char hs_gain, hs_pop; | 797 | unsigned char hs_gain, hs_pop; |
737 | struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); | 798 | struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); |
799 | struct twl4030_codec_data *pdata = twl4030->pdata; | ||
738 | /* Base values for ramp delay calculation: 2^19 - 2^26 */ | 800 | /* Base values for ramp delay calculation: 2^19 - 2^26 */ |
739 | unsigned int ramp_base[] = {524288, 1048576, 2097152, 4194304, | 801 | unsigned int ramp_base[] = {524288, 1048576, 2097152, 4194304, |
740 | 8388608, 16777216, 33554432, 67108864}; | 802 | 8388608, 16777216, 33554432, 67108864}; |
@@ -748,8 +810,8 @@ static void headset_ramp(struct snd_soc_codec *codec, int ramp) | |||
748 | /* Enable external mute control, this dramatically reduces | 810 | /* Enable external mute control, this dramatically reduces |
749 | * the pop-noise */ | 811 | * the pop-noise */ |
750 | if (pdata && pdata->hs_extmute) { | 812 | if (pdata && pdata->hs_extmute) { |
751 | if (pdata->set_hs_extmute) { | 813 | if (gpio_is_valid(pdata->hs_extmute_gpio)) { |
752 | pdata->set_hs_extmute(1); | 814 | gpio_set_value(pdata->hs_extmute_gpio, 1); |
753 | } else { | 815 | } else { |
754 | hs_pop |= TWL4030_EXTMUTE; | 816 | hs_pop |= TWL4030_EXTMUTE; |
755 | twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop); | 817 | twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop); |
@@ -786,8 +848,8 @@ static void headset_ramp(struct snd_soc_codec *codec, int ramp) | |||
786 | 848 | ||
787 | /* Disable external mute */ | 849 | /* Disable external mute */ |
788 | if (pdata && pdata->hs_extmute) { | 850 | if (pdata && pdata->hs_extmute) { |
789 | if (pdata->set_hs_extmute) { | 851 | if (gpio_is_valid(pdata->hs_extmute_gpio)) { |
790 | pdata->set_hs_extmute(0); | 852 | gpio_set_value(pdata->hs_extmute_gpio, 0); |
791 | } else { | 853 | } else { |
792 | hs_pop &= ~TWL4030_EXTMUTE; | 854 | hs_pop &= ~TWL4030_EXTMUTE; |
793 | twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop); | 855 | twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop); |
@@ -847,9 +909,10 @@ static int digimic_event(struct snd_soc_dapm_widget *w, | |||
847 | struct snd_kcontrol *kcontrol, int event) | 909 | struct snd_kcontrol *kcontrol, int event) |
848 | { | 910 | { |
849 | struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(w->codec); | 911 | struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(w->codec); |
912 | struct twl4030_codec_data *pdata = twl4030->pdata; | ||
850 | 913 | ||
851 | if (twl4030->digimic_delay) | 914 | if (pdata && pdata->digimic_delay) |
852 | twl4030_wait_ms(twl4030->digimic_delay); | 915 | twl4030_wait_ms(pdata->digimic_delay); |
853 | return 0; | 916 | return 0; |
854 | } | 917 | } |
855 | 918 | ||
@@ -999,7 +1062,7 @@ static int snd_soc_put_twl4030_opmode_enum_double(struct snd_kcontrol *kcontrol, | |||
999 | struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); | 1062 | struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); |
1000 | struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; | 1063 | struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; |
1001 | unsigned short val; | 1064 | unsigned short val; |
1002 | unsigned short mask, bitmask; | 1065 | unsigned short mask; |
1003 | 1066 | ||
1004 | if (twl4030->configured) { | 1067 | if (twl4030->configured) { |
1005 | dev_err(codec->dev, | 1068 | dev_err(codec->dev, |
@@ -1007,18 +1070,16 @@ static int snd_soc_put_twl4030_opmode_enum_double(struct snd_kcontrol *kcontrol, | |||
1007 | return -EBUSY; | 1070 | return -EBUSY; |
1008 | } | 1071 | } |
1009 | 1072 | ||
1010 | for (bitmask = 1; bitmask < e->max; bitmask <<= 1) | ||
1011 | ; | ||
1012 | if (ucontrol->value.enumerated.item[0] > e->max - 1) | 1073 | if (ucontrol->value.enumerated.item[0] > e->max - 1) |
1013 | return -EINVAL; | 1074 | return -EINVAL; |
1014 | 1075 | ||
1015 | val = ucontrol->value.enumerated.item[0] << e->shift_l; | 1076 | val = ucontrol->value.enumerated.item[0] << e->shift_l; |
1016 | mask = (bitmask - 1) << e->shift_l; | 1077 | mask = e->mask << e->shift_l; |
1017 | if (e->shift_l != e->shift_r) { | 1078 | if (e->shift_l != e->shift_r) { |
1018 | if (ucontrol->value.enumerated.item[1] > e->max - 1) | 1079 | if (ucontrol->value.enumerated.item[1] > e->max - 1) |
1019 | return -EINVAL; | 1080 | return -EINVAL; |
1020 | val |= ucontrol->value.enumerated.item[1] << e->shift_r; | 1081 | val |= ucontrol->value.enumerated.item[1] << e->shift_r; |
1021 | mask |= (bitmask - 1) << e->shift_r; | 1082 | mask |= e->mask << e->shift_r; |
1022 | } | 1083 | } |
1023 | 1084 | ||
1024 | return snd_soc_update_bits(codec, e->reg, mask, val); | 1085 | return snd_soc_update_bits(codec, e->reg, mask, val); |
@@ -1239,16 +1300,11 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { | |||
1239 | SND_SOC_DAPM_OUTPUT("Virtual Voice OUT"), | 1300 | SND_SOC_DAPM_OUTPUT("Virtual Voice OUT"), |
1240 | 1301 | ||
1241 | /* DACs */ | 1302 | /* DACs */ |
1242 | SND_SOC_DAPM_DAC("DAC Right1", "Right Front HiFi Playback", | 1303 | SND_SOC_DAPM_DAC("DAC Right1", NULL, SND_SOC_NOPM, 0, 0), |
1243 | SND_SOC_NOPM, 0, 0), | 1304 | SND_SOC_DAPM_DAC("DAC Left1", NULL, SND_SOC_NOPM, 0, 0), |
1244 | SND_SOC_DAPM_DAC("DAC Left1", "Left Front HiFi Playback", | 1305 | SND_SOC_DAPM_DAC("DAC Right2", NULL, SND_SOC_NOPM, 0, 0), |
1245 | SND_SOC_NOPM, 0, 0), | 1306 | SND_SOC_DAPM_DAC("DAC Left2", NULL, SND_SOC_NOPM, 0, 0), |
1246 | SND_SOC_DAPM_DAC("DAC Right2", "Right Rear HiFi Playback", | 1307 | SND_SOC_DAPM_DAC("DAC Voice", NULL, SND_SOC_NOPM, 0, 0), |
1247 | SND_SOC_NOPM, 0, 0), | ||
1248 | SND_SOC_DAPM_DAC("DAC Left2", "Left Rear HiFi Playback", | ||
1249 | SND_SOC_NOPM, 0, 0), | ||
1250 | SND_SOC_DAPM_DAC("DAC Voice", "Voice Playback", | ||
1251 | SND_SOC_NOPM, 0, 0), | ||
1252 | 1308 | ||
1253 | /* Analog bypasses */ | 1309 | /* Analog bypasses */ |
1254 | SND_SOC_DAPM_SWITCH("Right1 Analog Loopback", SND_SOC_NOPM, 0, 0, | 1310 | SND_SOC_DAPM_SWITCH("Right1 Analog Loopback", SND_SOC_NOPM, 0, 0, |
@@ -1377,14 +1433,10 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { | |||
1377 | 1433 | ||
1378 | /* Introducing four virtual ADC, since TWL4030 have four channel for | 1434 | /* Introducing four virtual ADC, since TWL4030 have four channel for |
1379 | capture */ | 1435 | capture */ |
1380 | SND_SOC_DAPM_ADC("ADC Virtual Left1", "Left Front Capture", | 1436 | SND_SOC_DAPM_ADC("ADC Virtual Left1", NULL, SND_SOC_NOPM, 0, 0), |
1381 | SND_SOC_NOPM, 0, 0), | 1437 | SND_SOC_DAPM_ADC("ADC Virtual Right1", NULL, SND_SOC_NOPM, 0, 0), |
1382 | SND_SOC_DAPM_ADC("ADC Virtual Right1", "Right Front Capture", | 1438 | SND_SOC_DAPM_ADC("ADC Virtual Left2", NULL, SND_SOC_NOPM, 0, 0), |
1383 | SND_SOC_NOPM, 0, 0), | 1439 | SND_SOC_DAPM_ADC("ADC Virtual Right2", NULL, SND_SOC_NOPM, 0, 0), |
1384 | SND_SOC_DAPM_ADC("ADC Virtual Left2", "Left Rear Capture", | ||
1385 | SND_SOC_NOPM, 0, 0), | ||
1386 | SND_SOC_DAPM_ADC("ADC Virtual Right2", "Right Rear Capture", | ||
1387 | SND_SOC_NOPM, 0, 0), | ||
1388 | 1440 | ||
1389 | /* Analog/Digital mic path selection. | 1441 | /* Analog/Digital mic path selection. |
1390 | TX1 Left/Right: either analog Left/Right or Digimic0 | 1442 | TX1 Left/Right: either analog Left/Right or Digimic0 |
@@ -1428,6 +1480,23 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { | |||
1428 | }; | 1480 | }; |
1429 | 1481 | ||
1430 | static const struct snd_soc_dapm_route intercon[] = { | 1482 | static const struct snd_soc_dapm_route intercon[] = { |
1483 | /* Stream -> DAC mapping */ | ||
1484 | {"DAC Right1", NULL, "HiFi Playback"}, | ||
1485 | {"DAC Left1", NULL, "HiFi Playback"}, | ||
1486 | {"DAC Right2", NULL, "HiFi Playback"}, | ||
1487 | {"DAC Left2", NULL, "HiFi Playback"}, | ||
1488 | {"DAC Voice", NULL, "Voice Playback"}, | ||
1489 | |||
1490 | /* ADC -> Stream mapping */ | ||
1491 | {"HiFi Capture", NULL, "ADC Virtual Left1"}, | ||
1492 | {"HiFi Capture", NULL, "ADC Virtual Right1"}, | ||
1493 | {"HiFi Capture", NULL, "ADC Virtual Left2"}, | ||
1494 | {"HiFi Capture", NULL, "ADC Virtual Right2"}, | ||
1495 | {"Voice Capture", NULL, "ADC Virtual Left1"}, | ||
1496 | {"Voice Capture", NULL, "ADC Virtual Right1"}, | ||
1497 | {"Voice Capture", NULL, "ADC Virtual Left2"}, | ||
1498 | {"Voice Capture", NULL, "ADC Virtual Right2"}, | ||
1499 | |||
1431 | {"Digital L1 Playback Mixer", NULL, "DAC Left1"}, | 1500 | {"Digital L1 Playback Mixer", NULL, "DAC Left1"}, |
1432 | {"Digital R1 Playback Mixer", NULL, "DAC Right1"}, | 1501 | {"Digital R1 Playback Mixer", NULL, "DAC Right1"}, |
1433 | {"Digital L2 Playback Mixer", NULL, "DAC Left2"}, | 1502 | {"Digital L2 Playback Mixer", NULL, "DAC Left2"}, |
@@ -2172,7 +2241,7 @@ static struct snd_soc_dai_driver twl4030_dai[] = { | |||
2172 | .formats = TWL4030_FORMATS, | 2241 | .formats = TWL4030_FORMATS, |
2173 | .sig_bits = 24,}, | 2242 | .sig_bits = 24,}, |
2174 | .capture = { | 2243 | .capture = { |
2175 | .stream_name = "Capture", | 2244 | .stream_name = "HiFi Capture", |
2176 | .channels_min = 2, | 2245 | .channels_min = 2, |
2177 | .channels_max = 4, | 2246 | .channels_max = 4, |
2178 | .rates = TWL4030_RATES, | 2247 | .rates = TWL4030_RATES, |
@@ -2189,7 +2258,7 @@ static struct snd_soc_dai_driver twl4030_dai[] = { | |||
2189 | .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, | 2258 | .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, |
2190 | .formats = SNDRV_PCM_FMTBIT_S16_LE,}, | 2259 | .formats = SNDRV_PCM_FMTBIT_S16_LE,}, |
2191 | .capture = { | 2260 | .capture = { |
2192 | .stream_name = "Capture", | 2261 | .stream_name = "Voice Capture", |
2193 | .channels_min = 1, | 2262 | .channels_min = 1, |
2194 | .channels_max = 2, | 2263 | .channels_max = 2, |
2195 | .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, | 2264 | .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, |
@@ -2214,7 +2283,8 @@ static int twl4030_soc_probe(struct snd_soc_codec *codec) | |||
2214 | { | 2283 | { |
2215 | struct twl4030_priv *twl4030; | 2284 | struct twl4030_priv *twl4030; |
2216 | 2285 | ||
2217 | twl4030 = kzalloc(sizeof(struct twl4030_priv), GFP_KERNEL); | 2286 | twl4030 = devm_kzalloc(codec->dev, sizeof(struct twl4030_priv), |
2287 | GFP_KERNEL); | ||
2218 | if (twl4030 == NULL) { | 2288 | if (twl4030 == NULL) { |
2219 | dev_err(codec->dev, "Can not allocate memory\n"); | 2289 | dev_err(codec->dev, "Can not allocate memory\n"); |
2220 | return -ENOMEM; | 2290 | return -ENOMEM; |
@@ -2231,11 +2301,15 @@ static int twl4030_soc_probe(struct snd_soc_codec *codec) | |||
2231 | static int twl4030_soc_remove(struct snd_soc_codec *codec) | 2301 | static int twl4030_soc_remove(struct snd_soc_codec *codec) |
2232 | { | 2302 | { |
2233 | struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); | 2303 | struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); |
2304 | struct twl4030_codec_data *pdata = twl4030->pdata; | ||
2234 | 2305 | ||
2235 | /* Reset registers to their chip default before leaving */ | 2306 | /* Reset registers to their chip default before leaving */ |
2236 | twl4030_reset_registers(codec); | 2307 | twl4030_reset_registers(codec); |
2237 | twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF); | 2308 | twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF); |
2238 | kfree(twl4030); | 2309 | |
2310 | if (pdata && pdata->hs_extmute && gpio_is_valid(pdata->hs_extmute_gpio)) | ||
2311 | gpio_free(pdata->hs_extmute_gpio); | ||
2312 | |||
2239 | return 0; | 2313 | return 0; |
2240 | } | 2314 | } |
2241 | 2315 | ||
@@ -2262,13 +2336,6 @@ static struct snd_soc_codec_driver soc_codec_dev_twl4030 = { | |||
2262 | 2336 | ||
2263 | static int __devinit twl4030_codec_probe(struct platform_device *pdev) | 2337 | static int __devinit twl4030_codec_probe(struct platform_device *pdev) |
2264 | { | 2338 | { |
2265 | struct twl4030_codec_data *pdata = pdev->dev.platform_data; | ||
2266 | |||
2267 | if (!pdata) { | ||
2268 | dev_err(&pdev->dev, "platform_data is missing\n"); | ||
2269 | return -EINVAL; | ||
2270 | } | ||
2271 | |||
2272 | return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_twl4030, | 2339 | return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_twl4030, |
2273 | twl4030_dai, ARRAY_SIZE(twl4030_dai)); | 2340 | twl4030_dai, ARRAY_SIZE(twl4030_dai)); |
2274 | } | 2341 | } |