| OLD | NEW |
| 1 /* | 1 /* |
| 2 * ALSA SoC Texas Instruments TPA6130A2 headset stereo amplifier driver | 2 * ALSA SoC Texas Instruments TPA6130A2 headset stereo amplifier driver |
| 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@nokia.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 |
| 10 * version 2 as published by the Free Software Foundation. | 10 * version 2 as published by the Free Software Foundation. |
| (...skipping 11 matching lines...) Expand all Loading... |
| 22 | 22 |
| 23 #include <linux/module.h> | 23 #include <linux/module.h> |
| 24 #include <linux/errno.h> | 24 #include <linux/errno.h> |
| 25 #include <linux/device.h> | 25 #include <linux/device.h> |
| 26 #include <linux/i2c.h> | 26 #include <linux/i2c.h> |
| 27 #include <linux/gpio.h> | 27 #include <linux/gpio.h> |
| 28 #include <linux/regulator/consumer.h> | 28 #include <linux/regulator/consumer.h> |
| 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" |
| 36 | 35 |
| 37 static struct i2c_client *tpa6130a2_client; | 36 static struct i2c_client *tpa6130a2_client; |
| 38 | 37 |
| 39 /* This struct is used to save the context */ | 38 /* This struct is used to save the context */ |
| 40 struct tpa6130a2_data { | 39 struct tpa6130a2_data { |
| 41 struct mutex mutex; | 40 struct mutex mutex; |
| 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 |
| 49 static int tpa6130a2_i2c_read(int reg) | 48 static int tpa6130a2_i2c_read(int reg) |
| 50 { | 49 { |
| 51 struct tpa6130a2_data *data; | 50 struct tpa6130a2_data *data; |
| 52 int val; | 51 int val; |
| 53 | 52 |
| 54 BUG_ON(tpa6130a2_client == NULL); | 53 BUG_ON(tpa6130a2_client == NULL); |
| 55 data = i2c_get_clientdata(tpa6130a2_client); | 54 data = i2c_get_clientdata(tpa6130a2_client); |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 110 | 109 |
| 111 for (i = 1; i < TPA6130A2_REG_VERSION; i++) { | 110 for (i = 1; i < TPA6130A2_REG_VERSION; i++) { |
| 112 ret = tpa6130a2_i2c_write(i, data->regs[i]); | 111 ret = tpa6130a2_i2c_write(i, data->regs[i]); |
| 113 if (ret < 0) | 112 if (ret < 0) |
| 114 break; | 113 break; |
| 115 } | 114 } |
| 116 | 115 |
| 117 return ret; | 116 return ret; |
| 118 } | 117 } |
| 119 | 118 |
| 120 static int tpa6130a2_power(int power) | 119 static int tpa6130a2_power(u8 power) |
| 121 { | 120 { |
| 122 struct tpa6130a2_data *data; | 121 struct tpa6130a2_data *data; |
| 123 u8 val; | 122 u8 val; |
| 124 int ret = 0; | 123 int ret = 0; |
| 125 | 124 |
| 126 BUG_ON(tpa6130a2_client == NULL); | 125 BUG_ON(tpa6130a2_client == NULL); |
| 127 data = i2c_get_clientdata(tpa6130a2_client); | 126 data = i2c_get_clientdata(tpa6130a2_client); |
| 128 | 127 |
| 129 mutex_lock(&data->mutex); | 128 mutex_lock(&data->mutex); |
| 130 » if (power && !data->power_state) { | 129 » if (power == data->power_state) |
| 131 » » /* Power on */ | 130 » » goto exit; |
| 132 » » if (data->power_gpio >= 0) | |
| 133 » » » gpio_set_value(data->power_gpio, 1); | |
| 134 | 131 |
| 132 if (power) { |
| 135 ret = regulator_enable(data->supply); | 133 ret = regulator_enable(data->supply); |
| 136 if (ret != 0) { | 134 if (ret != 0) { |
| 137 dev_err(&tpa6130a2_client->dev, | 135 dev_err(&tpa6130a2_client->dev, |
| 138 "Failed to enable supply: %d\n", ret); | 136 "Failed to enable supply: %d\n", ret); |
| 139 goto exit; | 137 goto exit; |
| 140 } | 138 } |
| 139 /* Power on */ |
| 140 if (data->power_gpio >= 0) |
| 141 gpio_set_value(data->power_gpio, 1); |
| 141 | 142 |
| 142 data->power_state = 1; | 143 data->power_state = 1; |
| 143 ret = tpa6130a2_initialize(); | 144 ret = tpa6130a2_initialize(); |
| 144 if (ret < 0) { | 145 if (ret < 0) { |
| 145 dev_err(&tpa6130a2_client->dev, | 146 dev_err(&tpa6130a2_client->dev, |
| 146 "Failed to initialize chip\n"); | 147 "Failed to initialize chip\n"); |
| 147 if (data->power_gpio >= 0) | 148 if (data->power_gpio >= 0) |
| 148 gpio_set_value(data->power_gpio, 0); | 149 gpio_set_value(data->power_gpio, 0); |
| 149 regulator_disable(data->supply); | 150 regulator_disable(data->supply); |
| 150 data->power_state = 0; | 151 data->power_state = 0; |
| 151 goto exit; | 152 goto exit; |
| 152 } | 153 } |
| 153 | 154 » } else { |
| 154 » » /* Clear SWS */ | |
| 155 » » val = tpa6130a2_read(TPA6130A2_REG_CONTROL); | |
| 156 » » val &= ~TPA6130A2_SWS; | |
| 157 » » tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val); | |
| 158 » } else if (!power && data->power_state) { | |
| 159 /* set SWS */ | 155 /* set SWS */ |
| 160 val = tpa6130a2_read(TPA6130A2_REG_CONTROL); | 156 val = tpa6130a2_read(TPA6130A2_REG_CONTROL); |
| 161 val |= TPA6130A2_SWS; | 157 val |= TPA6130A2_SWS; |
| 162 tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val); | 158 tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val); |
| 163 | 159 |
| 164 /* Power off */ | 160 /* Power off */ |
| 165 if (data->power_gpio >= 0) | 161 if (data->power_gpio >= 0) |
| 166 gpio_set_value(data->power_gpio, 0); | 162 gpio_set_value(data->power_gpio, 0); |
| 167 | 163 |
| 168 ret = regulator_disable(data->supply); | 164 ret = regulator_disable(data->supply); |
| (...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 293 */ | 289 */ |
| 294 static void tpa6130a2_channel_enable(u8 channel, int enable) | 290 static void tpa6130a2_channel_enable(u8 channel, int enable) |
| 295 { | 291 { |
| 296 u8 val; | 292 u8 val; |
| 297 | 293 |
| 298 if (enable) { | 294 if (enable) { |
| 299 /* Enable channel */ | 295 /* Enable channel */ |
| 300 /* Enable amplifier */ | 296 /* Enable amplifier */ |
| 301 val = tpa6130a2_read(TPA6130A2_REG_CONTROL); | 297 val = tpa6130a2_read(TPA6130A2_REG_CONTROL); |
| 302 val |= channel; | 298 val |= channel; |
| 299 val &= ~TPA6130A2_SWS; |
| 303 tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val); | 300 tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val); |
| 304 | 301 |
| 305 /* Unmute channel */ | 302 /* Unmute channel */ |
| 306 val = tpa6130a2_read(TPA6130A2_REG_VOL_MUTE); | 303 val = tpa6130a2_read(TPA6130A2_REG_VOL_MUTE); |
| 307 val &= ~channel; | 304 val &= ~channel; |
| 308 tpa6130a2_i2c_write(TPA6130A2_REG_VOL_MUTE, val); | 305 tpa6130a2_i2c_write(TPA6130A2_REG_VOL_MUTE, val); |
| 309 } else { | 306 } else { |
| 310 /* Disable channel */ | 307 /* Disable channel */ |
| 311 /* Mute channel */ | 308 /* Mute channel */ |
| 312 val = tpa6130a2_read(TPA6130A2_REG_VOL_MUTE); | 309 val = tpa6130a2_read(TPA6130A2_REG_VOL_MUTE); |
| 313 val |= channel; | 310 val |= channel; |
| 314 tpa6130a2_i2c_write(TPA6130A2_REG_VOL_MUTE, val); | 311 tpa6130a2_i2c_write(TPA6130A2_REG_VOL_MUTE, val); |
| 315 | 312 |
| 316 /* Disable amplifier */ | 313 /* Disable amplifier */ |
| 317 val = tpa6130a2_read(TPA6130A2_REG_CONTROL); | 314 val = tpa6130a2_read(TPA6130A2_REG_CONTROL); |
| 318 val &= ~channel; | 315 val &= ~channel; |
| 319 tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val); | 316 tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val); |
| 320 } | 317 } |
| 321 } | 318 } |
| 322 | 319 |
| 323 static int tpa6130a2_left_event(struct snd_soc_dapm_widget *w, | 320 int tpa6130a2_stereo_enable(struct snd_soc_codec *codec, int enable) |
| 324 » » struct snd_kcontrol *kcontrol, int event) | |
| 325 { | |
| 326 » switch (event) { | |
| 327 » case SND_SOC_DAPM_POST_PMU: | |
| 328 » » tpa6130a2_channel_enable(TPA6130A2_HP_EN_L, 1); | |
| 329 » » break; | |
| 330 » case SND_SOC_DAPM_POST_PMD: | |
| 331 » » tpa6130a2_channel_enable(TPA6130A2_HP_EN_L, 0); | |
| 332 » » break; | |
| 333 » } | |
| 334 » return 0; | |
| 335 } | |
| 336 | |
| 337 static int tpa6130a2_right_event(struct snd_soc_dapm_widget *w, | |
| 338 » » struct snd_kcontrol *kcontrol, int event) | |
| 339 { | |
| 340 » switch (event) { | |
| 341 » case SND_SOC_DAPM_POST_PMU: | |
| 342 » » tpa6130a2_channel_enable(TPA6130A2_HP_EN_R, 1); | |
| 343 » » break; | |
| 344 » case SND_SOC_DAPM_POST_PMD: | |
| 345 » » tpa6130a2_channel_enable(TPA6130A2_HP_EN_R, 0); | |
| 346 » » break; | |
| 347 » } | |
| 348 » return 0; | |
| 349 } | |
| 350 | |
| 351 static int tpa6130a2_supply_event(struct snd_soc_dapm_widget *w, | |
| 352 » » struct snd_kcontrol *kcontrol, int event) | |
| 353 { | 321 { |
| 354 int ret = 0; | 322 int ret = 0; |
| 323 if (enable) { |
| 324 ret = tpa6130a2_power(1); |
| 325 if (ret < 0) |
| 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); |
| 332 ret = tpa6130a2_power(0); |
| 333 } |
| 355 | 334 |
| 356 switch (event) { | |
| 357 case SND_SOC_DAPM_POST_PMU: | |
| 358 ret = tpa6130a2_power(1); | |
| 359 break; | |
| 360 case SND_SOC_DAPM_POST_PMD: | |
| 361 ret = tpa6130a2_power(0); | |
| 362 break; | |
| 363 } | |
| 364 return ret; | 335 return ret; |
| 365 } | 336 } |
| 366 | 337 EXPORT_SYMBOL_GPL(tpa6130a2_stereo_enable); |
| 367 static const struct snd_soc_dapm_widget tpa6130a2_dapm_widgets[] = { | |
| 368 » SND_SOC_DAPM_PGA_E("TPA6130A2 Left", SND_SOC_NOPM, | |
| 369 » » » 0, 0, NULL, 0, tpa6130a2_left_event, | |
| 370 » » » SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), | |
| 371 » SND_SOC_DAPM_PGA_E("TPA6130A2 Right", SND_SOC_NOPM, | |
| 372 » » » 0, 0, NULL, 0, tpa6130a2_right_event, | |
| 373 » » » SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), | |
| 374 » SND_SOC_DAPM_SUPPLY("TPA6130A2 Enable", SND_SOC_NOPM, | |
| 375 » » » 0, 0, tpa6130a2_supply_event, | |
| 376 » » » SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), | |
| 377 » /* Outputs */ | |
| 378 » SND_SOC_DAPM_OUTPUT("TPA6130A2 Headphone Left"), | |
| 379 » SND_SOC_DAPM_OUTPUT("TPA6130A2 Headphone Right"), | |
| 380 }; | |
| 381 | |
| 382 static const struct snd_soc_dapm_route audio_map[] = { | |
| 383 » {"TPA6130A2 Headphone Left", NULL, "TPA6130A2 Left"}, | |
| 384 » {"TPA6130A2 Headphone Right", NULL, "TPA6130A2 Right"}, | |
| 385 | |
| 386 » {"TPA6130A2 Headphone Left", NULL, "TPA6130A2 Enable"}, | |
| 387 » {"TPA6130A2 Headphone Right", NULL, "TPA6130A2 Enable"}, | |
| 388 }; | |
| 389 | 338 |
| 390 int tpa6130a2_add_controls(struct snd_soc_codec *codec) | 339 int tpa6130a2_add_controls(struct snd_soc_codec *codec) |
| 391 { | 340 { |
| 392 struct tpa6130a2_data *data; | 341 struct tpa6130a2_data *data; |
| 393 | 342 |
| 394 if (tpa6130a2_client == NULL) | 343 if (tpa6130a2_client == NULL) |
| 395 return -ENODEV; | 344 return -ENODEV; |
| 396 | 345 |
| 397 data = i2c_get_clientdata(tpa6130a2_client); | 346 data = i2c_get_clientdata(tpa6130a2_client); |
| 398 | 347 |
| 399 snd_soc_dapm_new_controls(codec, tpa6130a2_dapm_widgets, | |
| 400 ARRAY_SIZE(tpa6130a2_dapm_widgets)); | |
| 401 | |
| 402 snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); | |
| 403 | |
| 404 if (data->id == TPA6140A2) | 348 if (data->id == TPA6140A2) |
| 405 return snd_soc_add_controls(codec, tpa6140a2_controls, | 349 return snd_soc_add_controls(codec, tpa6140a2_controls, |
| 406 ARRAY_SIZE(tpa6140a2_controls)); | 350 ARRAY_SIZE(tpa6140a2_controls)); |
| 407 else | 351 else |
| 408 return snd_soc_add_controls(codec, tpa6130a2_controls, | 352 return snd_soc_add_controls(codec, tpa6130a2_controls, |
| 409 ARRAY_SIZE(tpa6130a2_controls)); | 353 ARRAY_SIZE(tpa6130a2_controls)); |
| 410 | |
| 411 } | 354 } |
| 412 EXPORT_SYMBOL_GPL(tpa6130a2_add_controls); | 355 EXPORT_SYMBOL_GPL(tpa6130a2_add_controls); |
| 413 | 356 |
| 414 static int __devinit tpa6130a2_probe(struct i2c_client *client, | 357 static int __devinit tpa6130a2_probe(struct i2c_client *client, |
| 415 const struct i2c_device_id *id) | 358 const struct i2c_device_id *id) |
| 416 { | 359 { |
| 417 struct device *dev; | 360 struct device *dev; |
| 418 struct tpa6130a2_data *data; | 361 struct tpa6130a2_data *data; |
| 419 struct tpa6130a2_platform_data *pdata; | 362 struct tpa6130a2_platform_data *pdata; |
| 420 const char *regulator; | 363 const char *regulator; |
| (...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 551 { | 494 { |
| 552 i2c_del_driver(&tpa6130a2_i2c_driver); | 495 i2c_del_driver(&tpa6130a2_i2c_driver); |
| 553 } | 496 } |
| 554 | 497 |
| 555 MODULE_AUTHOR("Peter Ujfalusi"); | 498 MODULE_AUTHOR("Peter Ujfalusi"); |
| 556 MODULE_DESCRIPTION("TPA6130A2 Headphone amplifier driver"); | 499 MODULE_DESCRIPTION("TPA6130A2 Headphone amplifier driver"); |
| 557 MODULE_LICENSE("GPL"); | 500 MODULE_LICENSE("GPL"); |
| 558 | 501 |
| 559 module_init(tpa6130a2_init); | 502 module_init(tpa6130a2_init); |
| 560 module_exit(tpa6130a2_exit); | 503 module_exit(tpa6130a2_exit); |
| OLD | NEW |