aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/tpa6130a2.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs/tpa6130a2.c')
-rw-r--r--sound/soc/codecs/tpa6130a2.c135
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
101static void tpa6130a2_initialize(void) 102static 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
113static int tpa6130a2_power(int power) 119static 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
307static int tpa6130a2_left_event(struct snd_soc_dapm_widget *w, 320int 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
321static 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
335static 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 337EXPORT_SYMBOL_GPL(tpa6130a2_stereo_enable);
351static 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
366static 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
374int tpa6130a2_add_controls(struct snd_soc_codec *codec) 339int 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}
394EXPORT_SYMBOL_GPL(tpa6130a2_add_controls); 355EXPORT_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
537MODULE_AUTHOR("Peter Ujfalusi"); 498MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
538MODULE_DESCRIPTION("TPA6130A2 Headphone amplifier driver"); 499MODULE_DESCRIPTION("TPA6130A2 Headphone amplifier driver");
539MODULE_LICENSE("GPL"); 500MODULE_LICENSE("GPL");
540 501