diff options
Diffstat (limited to 'sound/soc/codecs/tpa6130a2.c')
-rw-r--r-- | sound/soc/codecs/tpa6130a2.c | 135 |
1 files changed, 48 insertions, 87 deletions
diff --git a/sound/soc/codecs/tpa6130a2.c b/sound/soc/codecs/tpa6130a2.c index 99b70e5978a2..239e0c461068 100644 --- a/sound/soc/codecs/tpa6130a2.c +++ b/sound/soc/codecs/tpa6130a2.c | |||
@@ -3,7 +3,7 @@ | |||
3 | * | 3 | * |
4 | * Copyright (C) Nokia Corporation | 4 | * Copyright (C) Nokia Corporation |
5 | * | 5 | * |
6 | * Author: Peter Ujfalusi <peter.ujfalusi@nokia.com> | 6 | * Author: Peter Ujfalusi <peter.ujfalusi@ti.com> |
7 | * | 7 | * |
8 | * This program is free software; you can redistribute it and/or | 8 | * This program is free software; you can redistribute it and/or |
9 | * modify it under the terms of the GNU General Public License | 9 | * modify it under the terms of the GNU General Public License |
@@ -29,7 +29,6 @@ | |||
29 | #include <linux/slab.h> | 29 | #include <linux/slab.h> |
30 | #include <sound/tpa6130a2-plat.h> | 30 | #include <sound/tpa6130a2-plat.h> |
31 | #include <sound/soc.h> | 31 | #include <sound/soc.h> |
32 | #include <sound/soc-dapm.h> | ||
33 | #include <sound/tlv.h> | 32 | #include <sound/tlv.h> |
34 | 33 | ||
35 | #include "tpa6130a2.h" | 34 | #include "tpa6130a2.h" |
@@ -42,7 +41,7 @@ struct tpa6130a2_data { | |||
42 | unsigned char regs[TPA6130A2_CACHEREGNUM]; | 41 | unsigned char regs[TPA6130A2_CACHEREGNUM]; |
43 | struct regulator *supply; | 42 | struct regulator *supply; |
44 | int power_gpio; | 43 | int power_gpio; |
45 | unsigned char power_state; | 44 | u8 power_state:1; |
46 | enum tpa_model id; | 45 | enum tpa_model id; |
47 | }; | 46 | }; |
48 | 47 | ||
@@ -78,8 +77,10 @@ static int tpa6130a2_i2c_write(int reg, u8 value) | |||
78 | 77 | ||
79 | if (data->power_state) { | 78 | if (data->power_state) { |
80 | val = i2c_smbus_write_byte_data(tpa6130a2_client, reg, value); | 79 | val = i2c_smbus_write_byte_data(tpa6130a2_client, reg, value); |
81 | if (val < 0) | 80 | if (val < 0) { |
82 | dev_err(&tpa6130a2_client->dev, "Write failed\n"); | 81 | dev_err(&tpa6130a2_client->dev, "Write failed\n"); |
82 | return val; | ||
83 | } | ||
83 | } | 84 | } |
84 | 85 | ||
85 | /* Either powered on or off, we save the context */ | 86 | /* Either powered on or off, we save the context */ |
@@ -98,47 +99,58 @@ static u8 tpa6130a2_read(int reg) | |||
98 | return data->regs[reg]; | 99 | return data->regs[reg]; |
99 | } | 100 | } |
100 | 101 | ||
101 | static void tpa6130a2_initialize(void) | 102 | static int tpa6130a2_initialize(void) |
102 | { | 103 | { |
103 | struct tpa6130a2_data *data; | 104 | struct tpa6130a2_data *data; |
104 | int i; | 105 | int i, ret = 0; |
105 | 106 | ||
106 | BUG_ON(tpa6130a2_client == NULL); | 107 | BUG_ON(tpa6130a2_client == NULL); |
107 | data = i2c_get_clientdata(tpa6130a2_client); | 108 | data = i2c_get_clientdata(tpa6130a2_client); |
108 | 109 | ||
109 | for (i = 1; i < TPA6130A2_REG_VERSION; i++) | 110 | for (i = 1; i < TPA6130A2_REG_VERSION; i++) { |
110 | tpa6130a2_i2c_write(i, data->regs[i]); | 111 | ret = tpa6130a2_i2c_write(i, data->regs[i]); |
112 | if (ret < 0) | ||
113 | break; | ||
114 | } | ||
115 | |||
116 | return ret; | ||
111 | } | 117 | } |
112 | 118 | ||
113 | static int tpa6130a2_power(int power) | 119 | static int tpa6130a2_power(u8 power) |
114 | { | 120 | { |
115 | struct tpa6130a2_data *data; | 121 | struct tpa6130a2_data *data; |
116 | u8 val; | 122 | u8 val; |
117 | int ret; | 123 | int ret = 0; |
118 | 124 | ||
119 | BUG_ON(tpa6130a2_client == NULL); | 125 | BUG_ON(tpa6130a2_client == NULL); |
120 | data = i2c_get_clientdata(tpa6130a2_client); | 126 | data = i2c_get_clientdata(tpa6130a2_client); |
121 | 127 | ||
122 | mutex_lock(&data->mutex); | 128 | mutex_lock(&data->mutex); |
123 | if (power) { | 129 | if (power == data->power_state) |
124 | /* Power on */ | 130 | goto exit; |
125 | if (data->power_gpio >= 0) | ||
126 | gpio_set_value(data->power_gpio, 1); | ||
127 | 131 | ||
132 | if (power) { | ||
128 | ret = regulator_enable(data->supply); | 133 | ret = regulator_enable(data->supply); |
129 | if (ret != 0) { | 134 | if (ret != 0) { |
130 | dev_err(&tpa6130a2_client->dev, | 135 | dev_err(&tpa6130a2_client->dev, |
131 | "Failed to enable supply: %d\n", ret); | 136 | "Failed to enable supply: %d\n", ret); |
132 | goto exit; | 137 | goto exit; |
133 | } | 138 | } |
139 | /* Power on */ | ||
140 | if (data->power_gpio >= 0) | ||
141 | gpio_set_value(data->power_gpio, 1); | ||
134 | 142 | ||
135 | data->power_state = 1; | 143 | data->power_state = 1; |
136 | tpa6130a2_initialize(); | 144 | ret = tpa6130a2_initialize(); |
137 | 145 | if (ret < 0) { | |
138 | /* Clear SWS */ | 146 | dev_err(&tpa6130a2_client->dev, |
139 | val = tpa6130a2_read(TPA6130A2_REG_CONTROL); | 147 | "Failed to initialize chip\n"); |
140 | val &= ~TPA6130A2_SWS; | 148 | if (data->power_gpio >= 0) |
141 | tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val); | 149 | gpio_set_value(data->power_gpio, 0); |
150 | regulator_disable(data->supply); | ||
151 | data->power_state = 0; | ||
152 | goto exit; | ||
153 | } | ||
142 | } else { | 154 | } else { |
143 | /* set SWS */ | 155 | /* set SWS */ |
144 | val = tpa6130a2_read(TPA6130A2_REG_CONTROL); | 156 | val = tpa6130a2_read(TPA6130A2_REG_CONTROL); |
@@ -284,6 +296,7 @@ static void tpa6130a2_channel_enable(u8 channel, int enable) | |||
284 | /* Enable amplifier */ | 296 | /* Enable amplifier */ |
285 | val = tpa6130a2_read(TPA6130A2_REG_CONTROL); | 297 | val = tpa6130a2_read(TPA6130A2_REG_CONTROL); |
286 | val |= channel; | 298 | val |= channel; |
299 | val &= ~TPA6130A2_SWS; | ||
287 | tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val); | 300 | tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val); |
288 | 301 | ||
289 | /* Unmute channel */ | 302 | /* Unmute channel */ |
@@ -304,84 +317,33 @@ static void tpa6130a2_channel_enable(u8 channel, int enable) | |||
304 | } | 317 | } |
305 | } | 318 | } |
306 | 319 | ||
307 | static int tpa6130a2_left_event(struct snd_soc_dapm_widget *w, | 320 | int tpa6130a2_stereo_enable(struct snd_soc_codec *codec, int enable) |
308 | struct snd_kcontrol *kcontrol, int event) | ||
309 | { | ||
310 | switch (event) { | ||
311 | case SND_SOC_DAPM_POST_PMU: | ||
312 | tpa6130a2_channel_enable(TPA6130A2_HP_EN_L, 1); | ||
313 | break; | ||
314 | case SND_SOC_DAPM_POST_PMD: | ||
315 | tpa6130a2_channel_enable(TPA6130A2_HP_EN_L, 0); | ||
316 | break; | ||
317 | } | ||
318 | return 0; | ||
319 | } | ||
320 | |||
321 | static int tpa6130a2_right_event(struct snd_soc_dapm_widget *w, | ||
322 | struct snd_kcontrol *kcontrol, int event) | ||
323 | { | ||
324 | switch (event) { | ||
325 | case SND_SOC_DAPM_POST_PMU: | ||
326 | tpa6130a2_channel_enable(TPA6130A2_HP_EN_R, 1); | ||
327 | break; | ||
328 | case SND_SOC_DAPM_POST_PMD: | ||
329 | tpa6130a2_channel_enable(TPA6130A2_HP_EN_R, 0); | ||
330 | break; | ||
331 | } | ||
332 | return 0; | ||
333 | } | ||
334 | |||
335 | static int tpa6130a2_supply_event(struct snd_soc_dapm_widget *w, | ||
336 | struct snd_kcontrol *kcontrol, int event) | ||
337 | { | 321 | { |
338 | int ret = 0; | 322 | int ret = 0; |
339 | 323 | if (enable) { | |
340 | switch (event) { | ||
341 | case SND_SOC_DAPM_POST_PMU: | ||
342 | ret = tpa6130a2_power(1); | 324 | ret = tpa6130a2_power(1); |
343 | break; | 325 | if (ret < 0) |
344 | case SND_SOC_DAPM_POST_PMD: | 326 | return ret; |
327 | tpa6130a2_channel_enable(TPA6130A2_HP_EN_R | TPA6130A2_HP_EN_L, | ||
328 | 1); | ||
329 | } else { | ||
330 | tpa6130a2_channel_enable(TPA6130A2_HP_EN_R | TPA6130A2_HP_EN_L, | ||
331 | 0); | ||
345 | ret = tpa6130a2_power(0); | 332 | ret = tpa6130a2_power(0); |
346 | break; | ||
347 | } | 333 | } |
334 | |||
348 | return ret; | 335 | return ret; |
349 | } | 336 | } |
350 | 337 | EXPORT_SYMBOL_GPL(tpa6130a2_stereo_enable); | |
351 | static const struct snd_soc_dapm_widget tpa6130a2_dapm_widgets[] = { | ||
352 | SND_SOC_DAPM_PGA_E("TPA6130A2 Left", SND_SOC_NOPM, | ||
353 | 0, 0, NULL, 0, tpa6130a2_left_event, | ||
354 | SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), | ||
355 | SND_SOC_DAPM_PGA_E("TPA6130A2 Right", SND_SOC_NOPM, | ||
356 | 0, 0, NULL, 0, tpa6130a2_right_event, | ||
357 | SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), | ||
358 | SND_SOC_DAPM_SUPPLY("TPA6130A2 Enable", SND_SOC_NOPM, | ||
359 | 0, 0, tpa6130a2_supply_event, | ||
360 | SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), | ||
361 | /* Outputs */ | ||
362 | SND_SOC_DAPM_OUTPUT("TPA6130A2 Headphone Left"), | ||
363 | SND_SOC_DAPM_OUTPUT("TPA6130A2 Headphone Right"), | ||
364 | }; | ||
365 | |||
366 | static const struct snd_soc_dapm_route audio_map[] = { | ||
367 | {"TPA6130A2 Headphone Left", NULL, "TPA6130A2 Left"}, | ||
368 | {"TPA6130A2 Headphone Right", NULL, "TPA6130A2 Right"}, | ||
369 | |||
370 | {"TPA6130A2 Headphone Left", NULL, "TPA6130A2 Enable"}, | ||
371 | {"TPA6130A2 Headphone Right", NULL, "TPA6130A2 Enable"}, | ||
372 | }; | ||
373 | 338 | ||
374 | int tpa6130a2_add_controls(struct snd_soc_codec *codec) | 339 | int tpa6130a2_add_controls(struct snd_soc_codec *codec) |
375 | { | 340 | { |
376 | struct tpa6130a2_data *data; | 341 | struct tpa6130a2_data *data; |
377 | 342 | ||
378 | BUG_ON(tpa6130a2_client == NULL); | 343 | if (tpa6130a2_client == NULL) |
379 | data = i2c_get_clientdata(tpa6130a2_client); | 344 | return -ENODEV; |
380 | |||
381 | snd_soc_dapm_new_controls(codec, tpa6130a2_dapm_widgets, | ||
382 | ARRAY_SIZE(tpa6130a2_dapm_widgets)); | ||
383 | 345 | ||
384 | snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); | 346 | data = i2c_get_clientdata(tpa6130a2_client); |
385 | 347 | ||
386 | if (data->id == TPA6140A2) | 348 | if (data->id == TPA6140A2) |
387 | return snd_soc_add_controls(codec, tpa6140a2_controls, | 349 | return snd_soc_add_controls(codec, tpa6140a2_controls, |
@@ -389,7 +351,6 @@ int tpa6130a2_add_controls(struct snd_soc_codec *codec) | |||
389 | else | 351 | else |
390 | return snd_soc_add_controls(codec, tpa6130a2_controls, | 352 | return snd_soc_add_controls(codec, tpa6130a2_controls, |
391 | ARRAY_SIZE(tpa6130a2_controls)); | 353 | ARRAY_SIZE(tpa6130a2_controls)); |
392 | |||
393 | } | 354 | } |
394 | EXPORT_SYMBOL_GPL(tpa6130a2_add_controls); | 355 | EXPORT_SYMBOL_GPL(tpa6130a2_add_controls); |
395 | 356 | ||
@@ -534,7 +495,7 @@ static void __exit tpa6130a2_exit(void) | |||
534 | i2c_del_driver(&tpa6130a2_i2c_driver); | 495 | i2c_del_driver(&tpa6130a2_i2c_driver); |
535 | } | 496 | } |
536 | 497 | ||
537 | MODULE_AUTHOR("Peter Ujfalusi"); | 498 | MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>"); |
538 | MODULE_DESCRIPTION("TPA6130A2 Headphone amplifier driver"); | 499 | MODULE_DESCRIPTION("TPA6130A2 Headphone amplifier driver"); |
539 | MODULE_LICENSE("GPL"); | 500 | MODULE_LICENSE("GPL"); |
540 | 501 | ||