diff --git a/arch/arm/configs/mako-perf_defconfig b/arch/arm/configs/mako-perf_defconfig index 405e8ffc67a..172e953057f 100644 --- a/arch/arm/configs/mako-perf_defconfig +++ b/arch/arm/configs/mako-perf_defconfig @@ -353,6 +353,7 @@ CONFIG_SND_USB_AUDIO=y CONFIG_SND_SOC=y CONFIG_SND_SOC_MSM8960=y CONFIG_SND_SOC_DUAL_AMIC=y +CONFIG_SND_SOC_TPA2028D=y CONFIG_HID_APPLE=y CONFIG_HID_MAGICMOUSE=y CONFIG_HID_MICROSOFT=y diff --git a/arch/arm/mach-msm/lge/mako/board-mako-sound.c b/arch/arm/mach-msm/lge/mako/board-mako-sound.c index 9099a028221..6f364ca1bd4 100644 --- a/arch/arm/mach-msm/lge/mako/board-mako-sound.c +++ b/arch/arm/mach-msm/lge/mako/board-mako-sound.c @@ -24,7 +24,7 @@ #include "../../../../sound/soc/codecs/wcd9310.h" #endif -#ifdef CONFIG_LGE_AUDIO_TPA2028D +#ifdef CONFIG_SND_SOC_TPA2028D #include #endif @@ -77,12 +77,7 @@ struct fsa8008_platform_data { unsigned int latency_for_detection; }; -#ifdef CONFIG_LGE_AUDIO_TPA2028D -int amp_power(bool on) -{ - return 0; -} - +#ifdef CONFIG_SND_SOC_TPA2028D int amp_enable(int on_state) { int err = 0; @@ -135,7 +130,6 @@ int amp_enable(int on_state) static struct audio_amp_platform_data amp_platform_data = { .enable = amp_enable, - .power = amp_power, .agc_compression_rate = AGC_COMPRESIION_RATE, .agc_output_limiter_disable = AGC_OUTPUT_LIMITER_DISABLE, .agc_fixed_gain = AGC_FIXED_GAIN, @@ -143,7 +137,7 @@ static struct audio_amp_platform_data amp_platform_data = { #endif static struct i2c_board_info msm_i2c_audiosubsystem_info[] = { -#ifdef CONFIG_LGE_AUDIO_TPA2028D +#ifdef CONFIG_SND_SOC_TPA2028D { I2C_BOARD_INFO("tpa2028d_amp", TPA2028D_ADDRESS), .platform_data = &_platform_data, diff --git a/include/sound/tpa2028d.h b/include/sound/tpa2028d.h new file mode 100644 index 00000000000..43d99411a65 --- /dev/null +++ b/include/sound/tpa2028d.h @@ -0,0 +1,50 @@ +/* include/sound/tpa2028d.h + * + * Copyright (C) 2009 LGE, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/* + * Revision 0.1 -- BJB -- 3/18/10 -- Original Version + * Revision 0.2 -- BJB -- 3/19/10 -- Corrected dB to DB in Register 0x01, Added function prototypes. + * Revision 0.3 -- AME -- 5/17/10 -- No changes to TPA2055D3_FDK.h file. + * + */ + +/* ---- I2C ADDR -----*/ +#define IC_CONTROL (char) 0x01 +#define AGC_ATTACK_CONTROL (char) 0x02 +#define AGC_RELEASE_CONTROL (char) 0x03 +#define AGC_HOLD_TIME_CONTROL (char) 0x04 +#define AGC_FIXED_GAIN_CONTROL (char) 0x05 +#define AGC1_CONTROL (char) 0x06 +#define AGC2_CONTROL (char) 0x07 + +struct amp_cal { + u8 dev_type; + u8 gain_type; + u8 data; +}; + +void set_amp_gain(int num); + +struct audio_amp_platform_data { + int (*enable)(int); + int (*power)(bool); + char agc_compression_rate; + char agc_output_limiter_disable; + char agc_fixed_gain; +}; + +/* SPK FUNCTION */ +#define SPK_ON 1 +#define SPK_OFF 0 diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index b211ac43e19..2e2e05f446f 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -448,3 +448,9 @@ config SND_SOC_TPA6130A2 config SND_SOC_MSM_STUB tristate + +config SND_SOC_TPA2028D + bool "Audio subsystem TPA2028D" + default y + ---help--- + Texas Instruments 3W Mono Class-D Audio Amplifier diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 5aabfeebfac..ec05d3c644d 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -212,3 +212,4 @@ obj-$(CONFIG_SND_SOC_MSM_STUB) += snd-soc-msm-stub.o # Amp obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o obj-$(CONFIG_SND_SOC_TPA6130A2) += snd-soc-tpa6130a2.o +obj-$(CONFIG_SND_SOC_TPA2028D) += tpa2028d.o diff --git a/sound/soc/codecs/tpa2028d.c b/sound/soc/codecs/tpa2028d.c new file mode 100644 index 00000000000..38c8c962c37 --- /dev/null +++ b/sound/soc/codecs/tpa2028d.c @@ -0,0 +1,321 @@ +/* sound/soc/codecs/tpa2028d.c + * + * Copyright (C) 2009 LGE, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define MODULE_NAME "tpa2028d" + +#undef AMP_DEBUG_PRINT +#define AMP_DEBUG_PRINT + +#define AMP_IOCTL_MAGIC 't' +#define AMP_SET_DATA _IOW(AMP_IOCTL_MAGIC, 0, struct amp_cal *) +#define AMP_GET_DATA _IOW(AMP_IOCTL_MAGIC, 1, struct amp_cal *) + +static uint32_t msm_snd_debug = 1; +module_param_named(debug_mask, msm_snd_debug, uint, 0664); + +#if defined (AMP_DEBUG_PRINT) +#define D(fmt, args...) printk(KERN_INFO "[%s:%5d]" fmt, __func__, __LINE__, ##args) +#else +#define D(fmt, args...) do {} while (0) +#endif + +struct amp_config { + struct i2c_client *client; + struct audio_amp_platform_data *pdata; +}; + +static struct amp_config *amp_data = NULL; + +int ReadI2C(char reg, char *ret) +{ + + unsigned int err; + unsigned char buf = reg; + + struct i2c_msg msg[2] = { + { amp_data->client->addr, 0, 1, &buf }, + { amp_data->client->addr, I2C_M_RD, 1, ret} + }; + err = i2c_transfer(amp_data->client->adapter, msg, 2); + if (err < 0) + D("i2c read error:%x\n", reg); + else + D("i2c read ok:%x\n", reg); + return 0; + +} + +int WriteI2C(char reg, char val) +{ + + int err; + unsigned char buf[2]; + struct i2c_msg msg = {amp_data->client->addr, 0, 2, buf }; + + buf[0] = reg; + buf[1] = val; + err = i2c_transfer(amp_data->client->adapter, &msg, 1); + + if (err < 0) { + D("i2c write error:%x:%x\n", reg, val); + return -EIO; + } else { + return 0; + } +} + +int tpa2028d_poweron(void) +{ + int fail = 0; + char agc_compression_rate = amp_data->pdata->agc_compression_rate; + char agc_output_limiter_disable = amp_data->pdata->agc_output_limiter_disable; + char agc_fixed_gain = amp_data->pdata->agc_fixed_gain; + + agc_output_limiter_disable = (agc_output_limiter_disable<<7); + + fail |= WriteI2C(IC_CONTROL, 0xE3); /*Tuen On*/ + fail |= WriteI2C(AGC_ATTACK_CONTROL, 0x05); /*Tuen On*/ + fail |= WriteI2C(AGC_RELEASE_CONTROL, 0x0B); /*Tuen On*/ + fail |= WriteI2C(AGC_HOLD_TIME_CONTROL, 0x00); /*Tuen On*/ + fail |= WriteI2C(AGC_FIXED_GAIN_CONTROL, agc_fixed_gain); /*Tuen On*/ + fail |= WriteI2C(AGC1_CONTROL, 0x3A|agc_output_limiter_disable); /*Tuen On*/ + fail |= WriteI2C(AGC2_CONTROL, 0xC0|agc_compression_rate); /*Tuen On*/ + fail |= WriteI2C(IC_CONTROL, 0xC3); /*Tuen On*/ + + return fail; +} + +int tpa2028d_powerdown(void) +{ + int fail = 0; + return fail; +} + +inline void set_amp_gain(int amp_state) +{ + int fail = 0; + + switch (amp_state) { + case SPK_ON: + /* if the delay time is need for chip ready, + * should be added to each power or enable function. + */ + if (amp_data->pdata->power) + fail = amp_data->pdata->power(1); + /*need 5 msec for prevent mute issue*/ + msleep(5); + if (amp_data->pdata->enable) + fail = amp_data->pdata->enable(1); + /*need 10 msec for chip ready*/ + msleep(10); + fail = tpa2028d_poweron(); + break; + case SPK_OFF: + if (amp_data->pdata->enable) + fail = amp_data->pdata->enable(2); + fail = tpa2028d_powerdown(); + if (amp_data->pdata->enable) + fail = amp_data->pdata->enable(0); + if (amp_data->pdata->power) + fail = amp_data->pdata->power(0); + break; + default: + D("Amp_state [%d] does not support \n", amp_state); + } +} +EXPORT_SYMBOL(set_amp_gain); + +static ssize_t +tpa2028d_comp_rate_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct audio_amp_platform_data *pdata = amp_data->pdata; + int val; + + if (sscanf(buf, "%d", &val) != 1) + return -EINVAL; + if (val > 3 || val < 0) + return -EINVAL; + + pdata->agc_compression_rate = val; + return count; +} + +static ssize_t +tpa2028d_comp_rate_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct audio_amp_platform_data *pdata = amp_data->pdata; + char val = 0; + + ReadI2C(AGC2_CONTROL, &val); + + D("[tpa2028d_comp_rate_show] val : %x \n",val); + + return sprintf(buf, "AGC2_CONTROL : %x, pdata->agc_compression_rate : %d\n", val, pdata->agc_compression_rate); +} + +static ssize_t +tpa2028d_out_lim_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct audio_amp_platform_data *pdata = amp_data->pdata; + int val; + + if (sscanf(buf, "%d", &val) != 1) + return -EINVAL; + pdata->agc_output_limiter_disable = val&0x0001; + return count; +} + +static ssize_t +tpa2028d_out_lim_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct audio_amp_platform_data *pdata = amp_data->pdata; + char val=0; + + ReadI2C(AGC1_CONTROL, &val); + + D("[tpa2028d_out_lim_show] val : %x \n",val); + + return sprintf(buf, "AGC1_CONTROL : %x, pdata->agc_output_limiter_disable : %d\n", val, pdata->agc_output_limiter_disable); +} + +static ssize_t +tpa2028d_fixed_gain_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct audio_amp_platform_data *pdata = amp_data->pdata; + int val; + + if (sscanf(buf, "%d", &val) != 1) + return -EINVAL; + + pdata->agc_fixed_gain = val; + return count; +} + +static ssize_t +tpa2028d_fixed_gain_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct audio_amp_platform_data *pdata = amp_data->pdata; + char val=0; + + ReadI2C(AGC_FIXED_GAIN_CONTROL, &val); + + D("[tpa2028d_fixed_gain_show] val : %x \n",val); + + return sprintf(buf, "fixed_gain : %x, pdata->agc_fixed_gain : %d\n", val, pdata->agc_fixed_gain); +} + +static struct device_attribute tpa2028d_device_attrs[] = { + __ATTR(comp_rate, S_IRUGO | S_IWUSR, tpa2028d_comp_rate_show, tpa2028d_comp_rate_store), + __ATTR(out_lim, S_IRUGO | S_IWUSR, tpa2028d_out_lim_show, tpa2028d_out_lim_store), + __ATTR(fixed_gain, S_IRUGO | S_IWUSR, tpa2028d_fixed_gain_show, tpa2028d_fixed_gain_store), +}; + +static int tpa2028d_amp_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct amp_config *data; + struct audio_amp_platform_data *pdata; + struct i2c_adapter *adapter = client->adapter; + int err, i; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { + err = -EOPNOTSUPP; + return err; + } + + pdata = client->dev.platform_data; + if (pdata == NULL) { + D("platform data is null\n"); + return -ENODEV; + } + + data = kzalloc(sizeof(struct amp_config), GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + + amp_data = data; + amp_data->pdata = pdata; + data->client = client; + i2c_set_clientdata(client, data); + + set_amp_gain(SPK_OFF); + + for (i = 0; i < ARRAY_SIZE(tpa2028d_device_attrs); i++) { + err = device_create_file(&client->dev, &tpa2028d_device_attrs[i]); + if (err) + return err; + } + return 0; +} + +static int tpa2028d_amp_remove(struct i2c_client *client) +{ + struct amp_config *data = i2c_get_clientdata(client); + kfree(data); + + i2c_set_clientdata(client, NULL); + return 0; +} + + +static struct i2c_device_id tpa2028d_amp_idtable[] = { + { "tpa2028d_amp", 1 }, +}; + +static struct i2c_driver tpa2028d_amp_driver = { + .probe = tpa2028d_amp_probe, + .remove = tpa2028d_amp_remove, + .id_table = tpa2028d_amp_idtable, + .driver = { + .name = MODULE_NAME, + }, +}; + + +static int __init tpa2028d_amp_init(void) +{ + return i2c_add_driver(&tpa2028d_amp_driver); +} + +static void __exit tpa2028d_amp_exit(void) +{ + return i2c_del_driver(&tpa2028d_amp_driver); +} + +module_init(tpa2028d_amp_init); +module_exit(tpa2028d_amp_exit); + +MODULE_DESCRIPTION("TPA2028D Amp Control"); +MODULE_AUTHOR("Kim EunHye "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/msm/apq8064.c b/sound/soc/msm/apq8064.c index d2f79f5ab4f..b3449ec4906 100644 --- a/sound/soc/msm/apq8064.c +++ b/sound/soc/msm/apq8064.c @@ -30,6 +30,10 @@ #include "msm-pcm-routing.h" #include "../codecs/wcd9310.h" +#ifdef CONFIG_SND_SOC_TPA2028D +#include +#endif + /* 8064 machine driver */ #define PM8921_GPIO_BASE NR_GPIO_IRQS @@ -226,10 +230,14 @@ static void msm_ext_spk_power_amp_on(u32 spk) (msm_ext_top_spk_pamp & TOP_SPK_AMP_NEG)) || (msm_ext_top_spk_pamp & TOP_SPK_AMP)) { +#ifdef CONFIG_SND_SOC_TPA2028D + set_amp_gain(SPK_ON); +#else msm_enable_ext_spk_amp_gpio(top_spk_pamp_gpio); pr_debug("%s: sleeping 4 ms after turning on " " external Top Speaker Ampl\n", __func__); usleep_range(4000, 4000); +#endif } } else { @@ -274,6 +282,10 @@ static void msm_ext_spk_power_amp_off(u32 spk) if (msm_ext_top_spk_pamp) return; +#ifdef CONFIG_SND_SOC_TPA2028D + set_amp_gain(SPK_OFF); + msm_ext_top_spk_pamp = 0; +#else gpio_direction_output(top_spk_pamp_gpio, 0); gpio_free(top_spk_pamp_gpio); msm_ext_top_spk_pamp = 0; @@ -282,6 +294,7 @@ static void msm_ext_spk_power_amp_off(u32 spk) __func__); usleep_range(4000, 4000); +#endif } else { pr_err("%s: ERROR : Invalid Ext Spk Ampl. spk = 0x%08x\n", @@ -300,11 +313,17 @@ static void msm_ext_control(struct snd_soc_codec *codec) snd_soc_dapm_enable_pin(dapm, "Ext Spk Bottom Neg"); snd_soc_dapm_enable_pin(dapm, "Ext Spk Top Pos"); snd_soc_dapm_enable_pin(dapm, "Ext Spk Top Neg"); +#ifdef CONFIG_SND_SOC_TPA2028D + snd_soc_dapm_enable_pin(dapm, "Ext Spk Top"); +#endif } else { snd_soc_dapm_disable_pin(dapm, "Ext Spk Bottom Pos"); snd_soc_dapm_disable_pin(dapm, "Ext Spk Bottom Neg"); snd_soc_dapm_disable_pin(dapm, "Ext Spk Top Pos"); snd_soc_dapm_disable_pin(dapm, "Ext Spk Top Neg"); +#ifdef CONFIG_SND_SOC_TPA2028D + snd_soc_dapm_disable_pin(dapm, "Ext Spk Top"); +#endif } snd_soc_dapm_sync(dapm); @@ -504,13 +523,16 @@ static const struct snd_soc_dapm_route apq8064_common_audio_map[] = { {"HEADPHONE", NULL, "LDO_H"}, /* Speaker path */ +#ifdef CONFIG_SND_SOC_TPA2028D + {"Ext Spk Top", NULL, "LINEOUT1"}, +#else {"Ext Spk Bottom Pos", NULL, "LINEOUT1"}, {"Ext Spk Bottom Neg", NULL, "LINEOUT3"}, {"Ext Spk Top Pos", NULL, "LINEOUT2"}, {"Ext Spk Top Neg", NULL, "LINEOUT4"}, {"Ext Spk Top", NULL, "LINEOUT5"}, - +#endif /************ Analog MIC Paths ************/ #ifdef CONFIG_SND_SOC_DUAL_AMIC /* Handset Mic */ @@ -1158,10 +1180,14 @@ static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd) ARRAY_SIZE(apq8064_liquid_cdp_audio_map)); } +#ifdef CONFIG_SND_SOC_TPA2028D + snd_soc_dapm_enable_pin(dapm, "Ext Spk Top"); +#else snd_soc_dapm_enable_pin(dapm, "Ext Spk Bottom Pos"); snd_soc_dapm_enable_pin(dapm, "Ext Spk Bottom Neg"); snd_soc_dapm_enable_pin(dapm, "Ext Spk Top Pos"); snd_soc_dapm_enable_pin(dapm, "Ext Spk Top Neg"); +#endif snd_soc_dapm_sync(dapm);