From 4fe5668b73d7ad041101656c98f3a58d86f68840 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 3 May 2011 18:03:43 +0300 Subject: MFD: twl4030-codec: Rename internals from codec to audio In preparation of renaming the driver from twl4030-codec to twl4030-audio, first do some clean ups in the driver, which does not cause any problems outside. Signed-off-by: Peter Ujfalusi Acked-by: Samuel Ortiz --- drivers/mfd/twl4030-codec.c | 135 ++++++++++++++++++++++---------------------- 1 file changed, 68 insertions(+), 67 deletions(-) diff --git a/drivers/mfd/twl4030-codec.c b/drivers/mfd/twl4030-codec.c index 2bf4136..e1782b3 100644 --- a/drivers/mfd/twl4030-codec.c +++ b/drivers/mfd/twl4030-codec.c @@ -1,5 +1,6 @@ /* - * MFD driver for twl4030 codec submodule + * MFD driver for twl4030 audio submodule, which contains an audio codec, and + * the vibra control. * * Author: Peter Ujfalusi * @@ -31,53 +32,53 @@ #include #include -#define TWL4030_CODEC_CELLS 2 +#define TWL4030_AUDIO_CELLS 2 -static struct platform_device *twl4030_codec_dev; +static struct platform_device *twl4030_audio_dev; -struct twl4030_codec_resource { +struct twl4030_audio_resource { int request_count; u8 reg; u8 mask; }; -struct twl4030_codec { +struct twl4030_audio { unsigned int audio_mclk; struct mutex mutex; - struct twl4030_codec_resource resource[TWL4030_CODEC_RES_MAX]; - struct mfd_cell cells[TWL4030_CODEC_CELLS]; + struct twl4030_audio_resource resource[TWL4030_CODEC_RES_MAX]; + struct mfd_cell cells[TWL4030_AUDIO_CELLS]; }; /* * Modify the resource, the function returns the content of the register * after the modification. */ -static int twl4030_codec_set_resource(enum twl4030_codec_res id, int enable) +static int twl4030_audio_set_resource(enum twl4030_codec_res id, int enable) { - struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev); + struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev); u8 val; twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val, - codec->resource[id].reg); + audio->resource[id].reg); if (enable) - val |= codec->resource[id].mask; + val |= audio->resource[id].mask; else - val &= ~codec->resource[id].mask; + val &= ~audio->resource[id].mask; twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, - val, codec->resource[id].reg); + val, audio->resource[id].reg); return val; } -static inline int twl4030_codec_get_resource(enum twl4030_codec_res id) +static inline int twl4030_audio_get_resource(enum twl4030_codec_res id) { - struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev); + struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev); u8 val; twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val, - codec->resource[id].reg); + audio->resource[id].reg); return val; } @@ -88,24 +89,24 @@ static inline int twl4030_codec_get_resource(enum twl4030_codec_res id) */ int twl4030_codec_enable_resource(enum twl4030_codec_res id) { - struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev); + struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev); int val; if (id >= TWL4030_CODEC_RES_MAX) { - dev_err(&twl4030_codec_dev->dev, + dev_err(&twl4030_audio_dev->dev, "Invalid resource ID (%u)\n", id); return -EINVAL; } - mutex_lock(&codec->mutex); - if (!codec->resource[id].request_count) + mutex_lock(&audio->mutex); + if (!audio->resource[id].request_count) /* Resource was disabled, enable it */ - val = twl4030_codec_set_resource(id, 1); + val = twl4030_audio_set_resource(id, 1); else - val = twl4030_codec_get_resource(id); + val = twl4030_audio_get_resource(id); - codec->resource[id].request_count++; - mutex_unlock(&codec->mutex); + audio->resource[id].request_count++; + mutex_unlock(&audio->mutex); return val; } @@ -117,31 +118,31 @@ EXPORT_SYMBOL_GPL(twl4030_codec_enable_resource); */ int twl4030_codec_disable_resource(unsigned id) { - struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev); + struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev); int val; if (id >= TWL4030_CODEC_RES_MAX) { - dev_err(&twl4030_codec_dev->dev, + dev_err(&twl4030_audio_dev->dev, "Invalid resource ID (%u)\n", id); return -EINVAL; } - mutex_lock(&codec->mutex); - if (!codec->resource[id].request_count) { - dev_err(&twl4030_codec_dev->dev, + mutex_lock(&audio->mutex); + if (!audio->resource[id].request_count) { + dev_err(&twl4030_audio_dev->dev, "Resource has been disabled already (%u)\n", id); - mutex_unlock(&codec->mutex); + mutex_unlock(&audio->mutex); return -EPERM; } - codec->resource[id].request_count--; + audio->resource[id].request_count--; - if (!codec->resource[id].request_count) + if (!audio->resource[id].request_count) /* Resource can be disabled now */ - val = twl4030_codec_set_resource(id, 0); + val = twl4030_audio_set_resource(id, 0); else - val = twl4030_codec_get_resource(id); + val = twl4030_audio_get_resource(id); - mutex_unlock(&codec->mutex); + mutex_unlock(&audio->mutex); return val; } @@ -149,15 +150,15 @@ EXPORT_SYMBOL_GPL(twl4030_codec_disable_resource); unsigned int twl4030_codec_get_mclk(void) { - struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev); + struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev); - return codec->audio_mclk; + return audio->audio_mclk; } EXPORT_SYMBOL_GPL(twl4030_codec_get_mclk); -static int __devinit twl4030_codec_probe(struct platform_device *pdev) +static int __devinit twl4030_audio_probe(struct platform_device *pdev) { - struct twl4030_codec *codec; + struct twl4030_audio *audio; struct twl4030_codec_data *pdata = pdev->dev.platform_data; struct mfd_cell *cell = NULL; int ret, childs = 0; @@ -187,33 +188,33 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev) twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, val, TWL4030_REG_APLL_CTL); - codec = kzalloc(sizeof(struct twl4030_codec), GFP_KERNEL); - if (!codec) + audio = kzalloc(sizeof(struct twl4030_audio), GFP_KERNEL); + if (!audio) return -ENOMEM; - platform_set_drvdata(pdev, codec); + platform_set_drvdata(pdev, audio); - twl4030_codec_dev = pdev; - mutex_init(&codec->mutex); - codec->audio_mclk = pdata->audio_mclk; + twl4030_audio_dev = pdev; + mutex_init(&audio->mutex); + audio->audio_mclk = pdata->audio_mclk; /* Codec power */ - codec->resource[TWL4030_CODEC_RES_POWER].reg = TWL4030_REG_CODEC_MODE; - codec->resource[TWL4030_CODEC_RES_POWER].mask = TWL4030_CODECPDZ; + audio->resource[TWL4030_CODEC_RES_POWER].reg = TWL4030_REG_CODEC_MODE; + audio->resource[TWL4030_CODEC_RES_POWER].mask = TWL4030_CODECPDZ; /* PLL */ - codec->resource[TWL4030_CODEC_RES_APLL].reg = TWL4030_REG_APLL_CTL; - codec->resource[TWL4030_CODEC_RES_APLL].mask = TWL4030_APLL_EN; + audio->resource[TWL4030_CODEC_RES_APLL].reg = TWL4030_REG_APLL_CTL; + audio->resource[TWL4030_CODEC_RES_APLL].mask = TWL4030_APLL_EN; if (pdata->audio) { - cell = &codec->cells[childs]; + cell = &audio->cells[childs]; cell->name = "twl4030-codec"; cell->platform_data = pdata->audio; cell->pdata_size = sizeof(*pdata->audio); childs++; } if (pdata->vibra) { - cell = &codec->cells[childs]; + cell = &audio->cells[childs]; cell->name = "twl4030-vibra"; cell->platform_data = pdata->vibra; cell->pdata_size = sizeof(*pdata->vibra); @@ -221,7 +222,7 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev) } if (childs) - ret = mfd_add_devices(&pdev->dev, pdev->id, codec->cells, + ret = mfd_add_devices(&pdev->dev, pdev->id, audio->cells, childs, NULL, 0); else { dev_err(&pdev->dev, "No platform data found for childs\n"); @@ -232,45 +233,45 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev) return 0; platform_set_drvdata(pdev, NULL); - kfree(codec); - twl4030_codec_dev = NULL; + kfree(audio); + twl4030_audio_dev = NULL; return ret; } -static int __devexit twl4030_codec_remove(struct platform_device *pdev) +static int __devexit twl4030_audio_remove(struct platform_device *pdev) { - struct twl4030_codec *codec = platform_get_drvdata(pdev); + struct twl4030_audio *audio = platform_get_drvdata(pdev); mfd_remove_devices(&pdev->dev); platform_set_drvdata(pdev, NULL); - kfree(codec); - twl4030_codec_dev = NULL; + kfree(audio); + twl4030_audio_dev = NULL; return 0; } MODULE_ALIAS("platform:twl4030-audio"); -static struct platform_driver twl4030_codec_driver = { - .probe = twl4030_codec_probe, - .remove = __devexit_p(twl4030_codec_remove), +static struct platform_driver twl4030_audio_driver = { + .probe = twl4030_audio_probe, + .remove = __devexit_p(twl4030_audio_remove), .driver = { .owner = THIS_MODULE, .name = "twl4030-audio", }, }; -static int __devinit twl4030_codec_init(void) +static int __devinit twl4030_audio_init(void) { - return platform_driver_register(&twl4030_codec_driver); + return platform_driver_register(&twl4030_audio_driver); } -module_init(twl4030_codec_init); +module_init(twl4030_audio_init); -static void __devexit twl4030_codec_exit(void) +static void __devexit twl4030_audio_exit(void) { - platform_driver_unregister(&twl4030_codec_driver); + platform_driver_unregister(&twl4030_audio_driver); } -module_exit(twl4030_codec_exit); +module_exit(twl4030_audio_exit); MODULE_AUTHOR("Peter Ujfalusi "); MODULE_LICENSE("GPL"); -- cgit v1.1 From 57fe7251f5bfc4332f24479376de48a1e8ca6211 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 31 May 2011 12:02:49 +0300 Subject: MFD: twl4030-codec -> twl4030-audio: Rename the driver Rename the driver, and header file from twl4030-codec to twl4030-audio. To avoid breakage change depending drivers at the same time. Signed-off-by: Peter Ujfalusi CC: Misael Lopez Cruz Acked-by: Samuel Ortiz --- drivers/input/misc/Kconfig | 2 +- drivers/input/misc/twl4030-vibra.c | 10 +- drivers/mfd/Kconfig | 2 +- drivers/mfd/Makefile | 2 +- drivers/mfd/twl4030-audio.c | 277 ++++++++++++++++++++++++++++++++++++ drivers/mfd/twl4030-codec.c | 278 ------------------------------------- include/linux/mfd/twl4030-audio.h | 272 ++++++++++++++++++++++++++++++++++++ include/linux/mfd/twl4030-codec.h | 272 ------------------------------------ sound/soc/codecs/Kconfig | 2 +- sound/soc/codecs/twl4030.c | 16 +-- sound/soc/omap/sdp3430.c | 2 +- sound/soc/omap/zoom2.c | 2 +- 12 files changed, 568 insertions(+), 569 deletions(-) create mode 100644 drivers/mfd/twl4030-audio.c delete mode 100644 drivers/mfd/twl4030-codec.c create mode 100644 include/linux/mfd/twl4030-audio.h delete mode 100644 include/linux/mfd/twl4030-codec.h diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 45dc6aa..077309a 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -267,7 +267,7 @@ config INPUT_TWL4030_PWRBUTTON config INPUT_TWL4030_VIBRA tristate "Support for TWL4030 Vibrator" depends on TWL4030_CORE - select TWL4030_CODEC + select MFD_TWL4030_AUDIO select INPUT_FF_MEMLESS help This option enables support for TWL4030 Vibrator Driver. diff --git a/drivers/input/misc/twl4030-vibra.c b/drivers/input/misc/twl4030-vibra.c index 014dd4a..7abca85 100644 --- a/drivers/input/misc/twl4030-vibra.c +++ b/drivers/input/misc/twl4030-vibra.c @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include @@ -67,7 +67,7 @@ static void vibra_enable(struct vibra_info *info) { u8 reg; - twl4030_codec_enable_resource(TWL4030_CODEC_RES_POWER); + twl4030_audio_enable_resource(TWL4030_AUDIO_RES_POWER); /* turn H-Bridge on */ twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, @@ -75,7 +75,7 @@ static void vibra_enable(struct vibra_info *info) twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, (reg | TWL4030_VIBRA_EN), TWL4030_REG_VIBRA_CTL); - twl4030_codec_enable_resource(TWL4030_CODEC_RES_APLL); + twl4030_audio_enable_resource(TWL4030_AUDIO_RES_APLL); info->enabled = true; } @@ -90,8 +90,8 @@ static void vibra_disable(struct vibra_info *info) twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, (reg & ~TWL4030_VIBRA_EN), TWL4030_REG_VIBRA_CTL); - twl4030_codec_disable_resource(TWL4030_CODEC_RES_APLL); - twl4030_codec_disable_resource(TWL4030_CODEC_RES_POWER); + twl4030_audio_disable_resource(TWL4030_AUDIO_RES_APLL); + twl4030_audio_disable_resource(TWL4030_AUDIO_RES_POWER); info->enabled = false; } diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 0f09c05..3a6f76a 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -218,7 +218,7 @@ config TWL4030_POWER and load scripts controlling which resources are switched off/on or reset when a sleep, wakeup or warm reset event occurs. -config TWL4030_CODEC +config MFD_TWL4030_AUDIO bool depends on TWL4030_CORE select MFD_CORE diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index efe3cc3..4cf9465 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -40,7 +40,7 @@ obj-$(CONFIG_MENELAUS) += menelaus.o obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o -obj-$(CONFIG_TWL4030_CODEC) += twl4030-codec.o +obj-$(CONFIG_MFD_TWL4030_AUDIO) += twl4030-audio.o obj-$(CONFIG_TWL6030_PWM) += twl6030-pwm.o obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o diff --git a/drivers/mfd/twl4030-audio.c b/drivers/mfd/twl4030-audio.c new file mode 100644 index 0000000..5cdf841 --- /dev/null +++ b/drivers/mfd/twl4030-audio.c @@ -0,0 +1,277 @@ +/* + * MFD driver for twl4030 audio submodule, which contains an audio codec, and + * the vibra control. + * + * Author: Peter Ujfalusi + * + * Copyright: (C) 2009 Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TWL4030_AUDIO_CELLS 2 + +static struct platform_device *twl4030_audio_dev; + +struct twl4030_audio_resource { + int request_count; + u8 reg; + u8 mask; +}; + +struct twl4030_audio { + unsigned int audio_mclk; + struct mutex mutex; + struct twl4030_audio_resource resource[TWL4030_AUDIO_RES_MAX]; + struct mfd_cell cells[TWL4030_AUDIO_CELLS]; +}; + +/* + * Modify the resource, the function returns the content of the register + * after the modification. + */ +static int twl4030_audio_set_resource(enum twl4030_audio_res id, int enable) +{ + struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev); + u8 val; + + twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val, + audio->resource[id].reg); + + if (enable) + val |= audio->resource[id].mask; + else + val &= ~audio->resource[id].mask; + + twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, + val, audio->resource[id].reg); + + return val; +} + +static inline int twl4030_audio_get_resource(enum twl4030_audio_res id) +{ + struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev); + u8 val; + + twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val, + audio->resource[id].reg); + + return val; +} + +/* + * Enable the resource. + * The function returns with error or the content of the register + */ +int twl4030_audio_enable_resource(enum twl4030_audio_res id) +{ + struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev); + int val; + + if (id >= TWL4030_AUDIO_RES_MAX) { + dev_err(&twl4030_audio_dev->dev, + "Invalid resource ID (%u)\n", id); + return -EINVAL; + } + + mutex_lock(&audio->mutex); + if (!audio->resource[id].request_count) + /* Resource was disabled, enable it */ + val = twl4030_audio_set_resource(id, 1); + else + val = twl4030_audio_get_resource(id); + + audio->resource[id].request_count++; + mutex_unlock(&audio->mutex); + + return val; +} +EXPORT_SYMBOL_GPL(twl4030_audio_enable_resource); + +/* + * Disable the resource. + * The function returns with error or the content of the register + */ +int twl4030_audio_disable_resource(unsigned id) +{ + struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev); + int val; + + if (id >= TWL4030_AUDIO_RES_MAX) { + dev_err(&twl4030_audio_dev->dev, + "Invalid resource ID (%u)\n", id); + return -EINVAL; + } + + mutex_lock(&audio->mutex); + if (!audio->resource[id].request_count) { + dev_err(&twl4030_audio_dev->dev, + "Resource has been disabled already (%u)\n", id); + mutex_unlock(&audio->mutex); + return -EPERM; + } + audio->resource[id].request_count--; + + if (!audio->resource[id].request_count) + /* Resource can be disabled now */ + val = twl4030_audio_set_resource(id, 0); + else + val = twl4030_audio_get_resource(id); + + mutex_unlock(&audio->mutex); + + return val; +} +EXPORT_SYMBOL_GPL(twl4030_audio_disable_resource); + +unsigned int twl4030_audio_get_mclk(void) +{ + struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev); + + return audio->audio_mclk; +} +EXPORT_SYMBOL_GPL(twl4030_audio_get_mclk); + +static int __devinit twl4030_audio_probe(struct platform_device *pdev) +{ + struct twl4030_audio *audio; + struct twl4030_codec_data *pdata = pdev->dev.platform_data; + struct mfd_cell *cell = NULL; + int ret, childs = 0; + u8 val; + + if (!pdata) { + dev_err(&pdev->dev, "Platform data is missing\n"); + return -EINVAL; + } + + /* Configure APLL_INFREQ and disable APLL if enabled */ + val = 0; + switch (pdata->audio_mclk) { + case 19200000: + val |= TWL4030_APLL_INFREQ_19200KHZ; + break; + case 26000000: + val |= TWL4030_APLL_INFREQ_26000KHZ; + break; + case 38400000: + val |= TWL4030_APLL_INFREQ_38400KHZ; + break; + default: + dev_err(&pdev->dev, "Invalid audio_mclk\n"); + return -EINVAL; + } + twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, + val, TWL4030_REG_APLL_CTL); + + audio = kzalloc(sizeof(struct twl4030_audio), GFP_KERNEL); + if (!audio) + return -ENOMEM; + + platform_set_drvdata(pdev, audio); + + twl4030_audio_dev = pdev; + mutex_init(&audio->mutex); + audio->audio_mclk = pdata->audio_mclk; + + /* Codec power */ + audio->resource[TWL4030_AUDIO_RES_POWER].reg = TWL4030_REG_CODEC_MODE; + audio->resource[TWL4030_AUDIO_RES_POWER].mask = TWL4030_CODECPDZ; + + /* PLL */ + audio->resource[TWL4030_AUDIO_RES_APLL].reg = TWL4030_REG_APLL_CTL; + audio->resource[TWL4030_AUDIO_RES_APLL].mask = TWL4030_APLL_EN; + + if (pdata->audio) { + cell = &audio->cells[childs]; + cell->name = "twl4030-codec"; + cell->platform_data = pdata->audio; + cell->pdata_size = sizeof(*pdata->audio); + childs++; + } + if (pdata->vibra) { + cell = &audio->cells[childs]; + cell->name = "twl4030-vibra"; + cell->platform_data = pdata->vibra; + cell->pdata_size = sizeof(*pdata->vibra); + childs++; + } + + if (childs) + ret = mfd_add_devices(&pdev->dev, pdev->id, audio->cells, + childs, NULL, 0); + else { + dev_err(&pdev->dev, "No platform data found for childs\n"); + ret = -ENODEV; + } + + if (!ret) + return 0; + + platform_set_drvdata(pdev, NULL); + kfree(audio); + twl4030_audio_dev = NULL; + return ret; +} + +static int __devexit twl4030_audio_remove(struct platform_device *pdev) +{ + struct twl4030_audio *audio = platform_get_drvdata(pdev); + + mfd_remove_devices(&pdev->dev); + platform_set_drvdata(pdev, NULL); + kfree(audio); + twl4030_audio_dev = NULL; + + return 0; +} + +MODULE_ALIAS("platform:twl4030-audio"); + +static struct platform_driver twl4030_audio_driver = { + .probe = twl4030_audio_probe, + .remove = __devexit_p(twl4030_audio_remove), + .driver = { + .owner = THIS_MODULE, + .name = "twl4030-audio", + }, +}; + +static int __devinit twl4030_audio_init(void) +{ + return platform_driver_register(&twl4030_audio_driver); +} +module_init(twl4030_audio_init); + +static void __devexit twl4030_audio_exit(void) +{ + platform_driver_unregister(&twl4030_audio_driver); +} +module_exit(twl4030_audio_exit); + +MODULE_AUTHOR("Peter Ujfalusi "); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/twl4030-codec.c b/drivers/mfd/twl4030-codec.c deleted file mode 100644 index e1782b3..0000000 --- a/drivers/mfd/twl4030-codec.c +++ /dev/null @@ -1,278 +0,0 @@ -/* - * MFD driver for twl4030 audio submodule, which contains an audio codec, and - * the vibra control. - * - * Author: Peter Ujfalusi - * - * Copyright: (C) 2009 Nokia Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define TWL4030_AUDIO_CELLS 2 - -static struct platform_device *twl4030_audio_dev; - -struct twl4030_audio_resource { - int request_count; - u8 reg; - u8 mask; -}; - -struct twl4030_audio { - unsigned int audio_mclk; - struct mutex mutex; - struct twl4030_audio_resource resource[TWL4030_CODEC_RES_MAX]; - struct mfd_cell cells[TWL4030_AUDIO_CELLS]; -}; - -/* - * Modify the resource, the function returns the content of the register - * after the modification. - */ -static int twl4030_audio_set_resource(enum twl4030_codec_res id, int enable) -{ - struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev); - u8 val; - - twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val, - audio->resource[id].reg); - - if (enable) - val |= audio->resource[id].mask; - else - val &= ~audio->resource[id].mask; - - twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, - val, audio->resource[id].reg); - - return val; -} - -static inline int twl4030_audio_get_resource(enum twl4030_codec_res id) -{ - struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev); - u8 val; - - twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val, - audio->resource[id].reg); - - return val; -} - -/* - * Enable the resource. - * The function returns with error or the content of the register - */ -int twl4030_codec_enable_resource(enum twl4030_codec_res id) -{ - struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev); - int val; - - if (id >= TWL4030_CODEC_RES_MAX) { - dev_err(&twl4030_audio_dev->dev, - "Invalid resource ID (%u)\n", id); - return -EINVAL; - } - - mutex_lock(&audio->mutex); - if (!audio->resource[id].request_count) - /* Resource was disabled, enable it */ - val = twl4030_audio_set_resource(id, 1); - else - val = twl4030_audio_get_resource(id); - - audio->resource[id].request_count++; - mutex_unlock(&audio->mutex); - - return val; -} -EXPORT_SYMBOL_GPL(twl4030_codec_enable_resource); - -/* - * Disable the resource. - * The function returns with error or the content of the register - */ -int twl4030_codec_disable_resource(unsigned id) -{ - struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev); - int val; - - if (id >= TWL4030_CODEC_RES_MAX) { - dev_err(&twl4030_audio_dev->dev, - "Invalid resource ID (%u)\n", id); - return -EINVAL; - } - - mutex_lock(&audio->mutex); - if (!audio->resource[id].request_count) { - dev_err(&twl4030_audio_dev->dev, - "Resource has been disabled already (%u)\n", id); - mutex_unlock(&audio->mutex); - return -EPERM; - } - audio->resource[id].request_count--; - - if (!audio->resource[id].request_count) - /* Resource can be disabled now */ - val = twl4030_audio_set_resource(id, 0); - else - val = twl4030_audio_get_resource(id); - - mutex_unlock(&audio->mutex); - - return val; -} -EXPORT_SYMBOL_GPL(twl4030_codec_disable_resource); - -unsigned int twl4030_codec_get_mclk(void) -{ - struct twl4030_audio *audio = platform_get_drvdata(twl4030_audio_dev); - - return audio->audio_mclk; -} -EXPORT_SYMBOL_GPL(twl4030_codec_get_mclk); - -static int __devinit twl4030_audio_probe(struct platform_device *pdev) -{ - struct twl4030_audio *audio; - struct twl4030_codec_data *pdata = pdev->dev.platform_data; - struct mfd_cell *cell = NULL; - int ret, childs = 0; - u8 val; - - if (!pdata) { - dev_err(&pdev->dev, "Platform data is missing\n"); - return -EINVAL; - } - - /* Configure APLL_INFREQ and disable APLL if enabled */ - val = 0; - switch (pdata->audio_mclk) { - case 19200000: - val |= TWL4030_APLL_INFREQ_19200KHZ; - break; - case 26000000: - val |= TWL4030_APLL_INFREQ_26000KHZ; - break; - case 38400000: - val |= TWL4030_APLL_INFREQ_38400KHZ; - break; - default: - dev_err(&pdev->dev, "Invalid audio_mclk\n"); - return -EINVAL; - } - twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, - val, TWL4030_REG_APLL_CTL); - - audio = kzalloc(sizeof(struct twl4030_audio), GFP_KERNEL); - if (!audio) - return -ENOMEM; - - platform_set_drvdata(pdev, audio); - - twl4030_audio_dev = pdev; - mutex_init(&audio->mutex); - audio->audio_mclk = pdata->audio_mclk; - - /* Codec power */ - audio->resource[TWL4030_CODEC_RES_POWER].reg = TWL4030_REG_CODEC_MODE; - audio->resource[TWL4030_CODEC_RES_POWER].mask = TWL4030_CODECPDZ; - - /* PLL */ - audio->resource[TWL4030_CODEC_RES_APLL].reg = TWL4030_REG_APLL_CTL; - audio->resource[TWL4030_CODEC_RES_APLL].mask = TWL4030_APLL_EN; - - if (pdata->audio) { - cell = &audio->cells[childs]; - cell->name = "twl4030-codec"; - cell->platform_data = pdata->audio; - cell->pdata_size = sizeof(*pdata->audio); - childs++; - } - if (pdata->vibra) { - cell = &audio->cells[childs]; - cell->name = "twl4030-vibra"; - cell->platform_data = pdata->vibra; - cell->pdata_size = sizeof(*pdata->vibra); - childs++; - } - - if (childs) - ret = mfd_add_devices(&pdev->dev, pdev->id, audio->cells, - childs, NULL, 0); - else { - dev_err(&pdev->dev, "No platform data found for childs\n"); - ret = -ENODEV; - } - - if (!ret) - return 0; - - platform_set_drvdata(pdev, NULL); - kfree(audio); - twl4030_audio_dev = NULL; - return ret; -} - -static int __devexit twl4030_audio_remove(struct platform_device *pdev) -{ - struct twl4030_audio *audio = platform_get_drvdata(pdev); - - mfd_remove_devices(&pdev->dev); - platform_set_drvdata(pdev, NULL); - kfree(audio); - twl4030_audio_dev = NULL; - - return 0; -} - -MODULE_ALIAS("platform:twl4030-audio"); - -static struct platform_driver twl4030_audio_driver = { - .probe = twl4030_audio_probe, - .remove = __devexit_p(twl4030_audio_remove), - .driver = { - .owner = THIS_MODULE, - .name = "twl4030-audio", - }, -}; - -static int __devinit twl4030_audio_init(void) -{ - return platform_driver_register(&twl4030_audio_driver); -} -module_init(twl4030_audio_init); - -static void __devexit twl4030_audio_exit(void) -{ - platform_driver_unregister(&twl4030_audio_driver); -} -module_exit(twl4030_audio_exit); - -MODULE_AUTHOR("Peter Ujfalusi "); -MODULE_LICENSE("GPL"); - diff --git a/include/linux/mfd/twl4030-audio.h b/include/linux/mfd/twl4030-audio.h new file mode 100644 index 0000000..3d22b72 --- /dev/null +++ b/include/linux/mfd/twl4030-audio.h @@ -0,0 +1,272 @@ +/* + * MFD driver for twl4030 audio submodule + * + * Author: Peter Ujfalusi + * + * Copyright: (C) 2009 Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __TWL4030_CODEC_H__ +#define __TWL4030_CODEC_H__ + +/* Codec registers */ +#define TWL4030_REG_CODEC_MODE 0x01 +#define TWL4030_REG_OPTION 0x02 +#define TWL4030_REG_UNKNOWN 0x03 +#define TWL4030_REG_MICBIAS_CTL 0x04 +#define TWL4030_REG_ANAMICL 0x05 +#define TWL4030_REG_ANAMICR 0x06 +#define TWL4030_REG_AVADC_CTL 0x07 +#define TWL4030_REG_ADCMICSEL 0x08 +#define TWL4030_REG_DIGMIXING 0x09 +#define TWL4030_REG_ATXL1PGA 0x0A +#define TWL4030_REG_ATXR1PGA 0x0B +#define TWL4030_REG_AVTXL2PGA 0x0C +#define TWL4030_REG_AVTXR2PGA 0x0D +#define TWL4030_REG_AUDIO_IF 0x0E +#define TWL4030_REG_VOICE_IF 0x0F +#define TWL4030_REG_ARXR1PGA 0x10 +#define TWL4030_REG_ARXL1PGA 0x11 +#define TWL4030_REG_ARXR2PGA 0x12 +#define TWL4030_REG_ARXL2PGA 0x13 +#define TWL4030_REG_VRXPGA 0x14 +#define TWL4030_REG_VSTPGA 0x15 +#define TWL4030_REG_VRX2ARXPGA 0x16 +#define TWL4030_REG_AVDAC_CTL 0x17 +#define TWL4030_REG_ARX2VTXPGA 0x18 +#define TWL4030_REG_ARXL1_APGA_CTL 0x19 +#define TWL4030_REG_ARXR1_APGA_CTL 0x1A +#define TWL4030_REG_ARXL2_APGA_CTL 0x1B +#define TWL4030_REG_ARXR2_APGA_CTL 0x1C +#define TWL4030_REG_ATX2ARXPGA 0x1D +#define TWL4030_REG_BT_IF 0x1E +#define TWL4030_REG_BTPGA 0x1F +#define TWL4030_REG_BTSTPGA 0x20 +#define TWL4030_REG_EAR_CTL 0x21 +#define TWL4030_REG_HS_SEL 0x22 +#define TWL4030_REG_HS_GAIN_SET 0x23 +#define TWL4030_REG_HS_POPN_SET 0x24 +#define TWL4030_REG_PREDL_CTL 0x25 +#define TWL4030_REG_PREDR_CTL 0x26 +#define TWL4030_REG_PRECKL_CTL 0x27 +#define TWL4030_REG_PRECKR_CTL 0x28 +#define TWL4030_REG_HFL_CTL 0x29 +#define TWL4030_REG_HFR_CTL 0x2A +#define TWL4030_REG_ALC_CTL 0x2B +#define TWL4030_REG_ALC_SET1 0x2C +#define TWL4030_REG_ALC_SET2 0x2D +#define TWL4030_REG_BOOST_CTL 0x2E +#define TWL4030_REG_SOFTVOL_CTL 0x2F +#define TWL4030_REG_DTMF_FREQSEL 0x30 +#define TWL4030_REG_DTMF_TONEXT1H 0x31 +#define TWL4030_REG_DTMF_TONEXT1L 0x32 +#define TWL4030_REG_DTMF_TONEXT2H 0x33 +#define TWL4030_REG_DTMF_TONEXT2L 0x34 +#define TWL4030_REG_DTMF_TONOFF 0x35 +#define TWL4030_REG_DTMF_WANONOFF 0x36 +#define TWL4030_REG_I2S_RX_SCRAMBLE_H 0x37 +#define TWL4030_REG_I2S_RX_SCRAMBLE_M 0x38 +#define TWL4030_REG_I2S_RX_SCRAMBLE_L 0x39 +#define TWL4030_REG_APLL_CTL 0x3A +#define TWL4030_REG_DTMF_CTL 0x3B +#define TWL4030_REG_DTMF_PGA_CTL2 0x3C +#define TWL4030_REG_DTMF_PGA_CTL1 0x3D +#define TWL4030_REG_MISC_SET_1 0x3E +#define TWL4030_REG_PCMBTMUX 0x3F +#define TWL4030_REG_RX_PATH_SEL 0x43 +#define TWL4030_REG_VDL_APGA_CTL 0x44 +#define TWL4030_REG_VIBRA_CTL 0x45 +#define TWL4030_REG_VIBRA_SET 0x46 +#define TWL4030_REG_VIBRA_PWM_SET 0x47 +#define TWL4030_REG_ANAMIC_GAIN 0x48 +#define TWL4030_REG_MISC_SET_2 0x49 + +/* Bitfield Definitions */ + +/* TWL4030_CODEC_MODE (0x01) Fields */ +#define TWL4030_APLL_RATE 0xF0 +#define TWL4030_APLL_RATE_8000 0x00 +#define TWL4030_APLL_RATE_11025 0x10 +#define TWL4030_APLL_RATE_12000 0x20 +#define TWL4030_APLL_RATE_16000 0x40 +#define TWL4030_APLL_RATE_22050 0x50 +#define TWL4030_APLL_RATE_24000 0x60 +#define TWL4030_APLL_RATE_32000 0x80 +#define TWL4030_APLL_RATE_44100 0x90 +#define TWL4030_APLL_RATE_48000 0xA0 +#define TWL4030_APLL_RATE_96000 0xE0 +#define TWL4030_SEL_16K 0x08 +#define TWL4030_CODECPDZ 0x02 +#define TWL4030_OPT_MODE 0x01 +#define TWL4030_OPTION_1 (1 << 0) +#define TWL4030_OPTION_2 (0 << 0) + +/* TWL4030_OPTION (0x02) Fields */ +#define TWL4030_ATXL1_EN (1 << 0) +#define TWL4030_ATXR1_EN (1 << 1) +#define TWL4030_ATXL2_VTXL_EN (1 << 2) +#define TWL4030_ATXR2_VTXR_EN (1 << 3) +#define TWL4030_ARXL1_VRX_EN (1 << 4) +#define TWL4030_ARXR1_EN (1 << 5) +#define TWL4030_ARXL2_EN (1 << 6) +#define TWL4030_ARXR2_EN (1 << 7) + +/* TWL4030_REG_MICBIAS_CTL (0x04) Fields */ +#define TWL4030_MICBIAS2_CTL 0x40 +#define TWL4030_MICBIAS1_CTL 0x20 +#define TWL4030_HSMICBIAS_EN 0x04 +#define TWL4030_MICBIAS2_EN 0x02 +#define TWL4030_MICBIAS1_EN 0x01 + +/* ANAMICL (0x05) Fields */ +#define TWL4030_CNCL_OFFSET_START 0x80 +#define TWL4030_OFFSET_CNCL_SEL 0x60 +#define TWL4030_OFFSET_CNCL_SEL_ARX1 0x00 +#define TWL4030_OFFSET_CNCL_SEL_ARX2 0x20 +#define TWL4030_OFFSET_CNCL_SEL_VRX 0x40 +#define TWL4030_OFFSET_CNCL_SEL_ALL 0x60 +#define TWL4030_MICAMPL_EN 0x10 +#define TWL4030_CKMIC_EN 0x08 +#define TWL4030_AUXL_EN 0x04 +#define TWL4030_HSMIC_EN 0x02 +#define TWL4030_MAINMIC_EN 0x01 + +/* ANAMICR (0x06) Fields */ +#define TWL4030_MICAMPR_EN 0x10 +#define TWL4030_AUXR_EN 0x04 +#define TWL4030_SUBMIC_EN 0x01 + +/* AVADC_CTL (0x07) Fields */ +#define TWL4030_ADCL_EN 0x08 +#define TWL4030_AVADC_CLK_PRIORITY 0x04 +#define TWL4030_ADCR_EN 0x02 + +/* TWL4030_REG_ADCMICSEL (0x08) Fields */ +#define TWL4030_DIGMIC1_EN 0x08 +#define TWL4030_TX2IN_SEL 0x04 +#define TWL4030_DIGMIC0_EN 0x02 +#define TWL4030_TX1IN_SEL 0x01 + +/* AUDIO_IF (0x0E) Fields */ +#define TWL4030_AIF_SLAVE_EN 0x80 +#define TWL4030_DATA_WIDTH 0x60 +#define TWL4030_DATA_WIDTH_16S_16W 0x00 +#define TWL4030_DATA_WIDTH_32S_16W 0x40 +#define TWL4030_DATA_WIDTH_32S_24W 0x60 +#define TWL4030_AIF_FORMAT 0x18 +#define TWL4030_AIF_FORMAT_CODEC 0x00 +#define TWL4030_AIF_FORMAT_LEFT 0x08 +#define TWL4030_AIF_FORMAT_RIGHT 0x10 +#define TWL4030_AIF_FORMAT_TDM 0x18 +#define TWL4030_AIF_TRI_EN 0x04 +#define TWL4030_CLK256FS_EN 0x02 +#define TWL4030_AIF_EN 0x01 + +/* VOICE_IF (0x0F) Fields */ +#define TWL4030_VIF_SLAVE_EN 0x80 +#define TWL4030_VIF_DIN_EN 0x40 +#define TWL4030_VIF_DOUT_EN 0x20 +#define TWL4030_VIF_SWAP 0x10 +#define TWL4030_VIF_FORMAT 0x08 +#define TWL4030_VIF_TRI_EN 0x04 +#define TWL4030_VIF_SUB_EN 0x02 +#define TWL4030_VIF_EN 0x01 + +/* EAR_CTL (0x21) */ +#define TWL4030_EAR_GAIN 0x30 + +/* HS_GAIN_SET (0x23) Fields */ +#define TWL4030_HSR_GAIN 0x0C +#define TWL4030_HSR_GAIN_PWR_DOWN 0x00 +#define TWL4030_HSR_GAIN_PLUS_6DB 0x04 +#define TWL4030_HSR_GAIN_0DB 0x08 +#define TWL4030_HSR_GAIN_MINUS_6DB 0x0C +#define TWL4030_HSL_GAIN 0x03 +#define TWL4030_HSL_GAIN_PWR_DOWN 0x00 +#define TWL4030_HSL_GAIN_PLUS_6DB 0x01 +#define TWL4030_HSL_GAIN_0DB 0x02 +#define TWL4030_HSL_GAIN_MINUS_6DB 0x03 + +/* HS_POPN_SET (0x24) Fields */ +#define TWL4030_VMID_EN 0x40 +#define TWL4030_EXTMUTE 0x20 +#define TWL4030_RAMP_DELAY 0x1C +#define TWL4030_RAMP_DELAY_20MS 0x00 +#define TWL4030_RAMP_DELAY_40MS 0x04 +#define TWL4030_RAMP_DELAY_81MS 0x08 +#define TWL4030_RAMP_DELAY_161MS 0x0C +#define TWL4030_RAMP_DELAY_323MS 0x10 +#define TWL4030_RAMP_DELAY_645MS 0x14 +#define TWL4030_RAMP_DELAY_1291MS 0x18 +#define TWL4030_RAMP_DELAY_2581MS 0x1C +#define TWL4030_RAMP_EN 0x02 + +/* PREDL_CTL (0x25) */ +#define TWL4030_PREDL_GAIN 0x30 + +/* PREDR_CTL (0x26) */ +#define TWL4030_PREDR_GAIN 0x30 + +/* PRECKL_CTL (0x27) */ +#define TWL4030_PRECKL_GAIN 0x30 + +/* PRECKR_CTL (0x28) */ +#define TWL4030_PRECKR_GAIN 0x30 + +/* HFL_CTL (0x29, 0x2A) Fields */ +#define TWL4030_HF_CTL_HB_EN 0x04 +#define TWL4030_HF_CTL_LOOP_EN 0x08 +#define TWL4030_HF_CTL_RAMP_EN 0x10 +#define TWL4030_HF_CTL_REF_EN 0x20 + +/* APLL_CTL (0x3A) Fields */ +#define TWL4030_APLL_EN 0x10 +#define TWL4030_APLL_INFREQ 0x0F +#define TWL4030_APLL_INFREQ_19200KHZ 0x05 +#define TWL4030_APLL_INFREQ_26000KHZ 0x06 +#define TWL4030_APLL_INFREQ_38400KHZ 0x0F + +/* REG_MISC_SET_1 (0x3E) Fields */ +#define TWL4030_CLK64_EN 0x80 +#define TWL4030_SCRAMBLE_EN 0x40 +#define TWL4030_FMLOOP_EN 0x20 +#define TWL4030_SMOOTH_ANAVOL_EN 0x02 +#define TWL4030_DIGMIC_LR_SWAP_EN 0x01 + +/* VIBRA_CTL (0x45) */ +#define TWL4030_VIBRA_EN 0x01 +#define TWL4030_VIBRA_DIR 0x02 +#define TWL4030_VIBRA_AUDIO_SEL_L1 (0x00 << 2) +#define TWL4030_VIBRA_AUDIO_SEL_R1 (0x01 << 2) +#define TWL4030_VIBRA_AUDIO_SEL_L2 (0x02 << 2) +#define TWL4030_VIBRA_AUDIO_SEL_R2 (0x03 << 2) +#define TWL4030_VIBRA_SEL 0x10 +#define TWL4030_VIBRA_DIR_SEL 0x20 + +/* TWL4030 codec resource IDs */ +enum twl4030_audio_res { + TWL4030_AUDIO_RES_POWER = 0, + TWL4030_AUDIO_RES_APLL, + TWL4030_AUDIO_RES_MAX, +}; + +int twl4030_audio_disable_resource(enum twl4030_audio_res id); +int twl4030_audio_enable_resource(enum twl4030_audio_res id); +unsigned int twl4030_audio_get_mclk(void); + +#endif /* End of __TWL4030_CODEC_H__ */ diff --git a/include/linux/mfd/twl4030-codec.h b/include/linux/mfd/twl4030-codec.h deleted file mode 100644 index 5cc16bb..0000000 --- a/include/linux/mfd/twl4030-codec.h +++ /dev/null @@ -1,272 +0,0 @@ -/* - * MFD driver for twl4030 codec submodule - * - * Author: Peter Ujfalusi - * - * Copyright: (C) 2009 Nokia Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#ifndef __TWL4030_CODEC_H__ -#define __TWL4030_CODEC_H__ - -/* Codec registers */ -#define TWL4030_REG_CODEC_MODE 0x01 -#define TWL4030_REG_OPTION 0x02 -#define TWL4030_REG_UNKNOWN 0x03 -#define TWL4030_REG_MICBIAS_CTL 0x04 -#define TWL4030_REG_ANAMICL 0x05 -#define TWL4030_REG_ANAMICR 0x06 -#define TWL4030_REG_AVADC_CTL 0x07 -#define TWL4030_REG_ADCMICSEL 0x08 -#define TWL4030_REG_DIGMIXING 0x09 -#define TWL4030_REG_ATXL1PGA 0x0A -#define TWL4030_REG_ATXR1PGA 0x0B -#define TWL4030_REG_AVTXL2PGA 0x0C -#define TWL4030_REG_AVTXR2PGA 0x0D -#define TWL4030_REG_AUDIO_IF 0x0E -#define TWL4030_REG_VOICE_IF 0x0F -#define TWL4030_REG_ARXR1PGA 0x10 -#define TWL4030_REG_ARXL1PGA 0x11 -#define TWL4030_REG_ARXR2PGA 0x12 -#define TWL4030_REG_ARXL2PGA 0x13 -#define TWL4030_REG_VRXPGA 0x14 -#define TWL4030_REG_VSTPGA 0x15 -#define TWL4030_REG_VRX2ARXPGA 0x16 -#define TWL4030_REG_AVDAC_CTL 0x17 -#define TWL4030_REG_ARX2VTXPGA 0x18 -#define TWL4030_REG_ARXL1_APGA_CTL 0x19 -#define TWL4030_REG_ARXR1_APGA_CTL 0x1A -#define TWL4030_REG_ARXL2_APGA_CTL 0x1B -#define TWL4030_REG_ARXR2_APGA_CTL 0x1C -#define TWL4030_REG_ATX2ARXPGA 0x1D -#define TWL4030_REG_BT_IF 0x1E -#define TWL4030_REG_BTPGA 0x1F -#define TWL4030_REG_BTSTPGA 0x20 -#define TWL4030_REG_EAR_CTL 0x21 -#define TWL4030_REG_HS_SEL 0x22 -#define TWL4030_REG_HS_GAIN_SET 0x23 -#define TWL4030_REG_HS_POPN_SET 0x24 -#define TWL4030_REG_PREDL_CTL 0x25 -#define TWL4030_REG_PREDR_CTL 0x26 -#define TWL4030_REG_PRECKL_CTL 0x27 -#define TWL4030_REG_PRECKR_CTL 0x28 -#define TWL4030_REG_HFL_CTL 0x29 -#define TWL4030_REG_HFR_CTL 0x2A -#define TWL4030_REG_ALC_CTL 0x2B -#define TWL4030_REG_ALC_SET1 0x2C -#define TWL4030_REG_ALC_SET2 0x2D -#define TWL4030_REG_BOOST_CTL 0x2E -#define TWL4030_REG_SOFTVOL_CTL 0x2F -#define TWL4030_REG_DTMF_FREQSEL 0x30 -#define TWL4030_REG_DTMF_TONEXT1H 0x31 -#define TWL4030_REG_DTMF_TONEXT1L 0x32 -#define TWL4030_REG_DTMF_TONEXT2H 0x33 -#define TWL4030_REG_DTMF_TONEXT2L 0x34 -#define TWL4030_REG_DTMF_TONOFF 0x35 -#define TWL4030_REG_DTMF_WANONOFF 0x36 -#define TWL4030_REG_I2S_RX_SCRAMBLE_H 0x37 -#define TWL4030_REG_I2S_RX_SCRAMBLE_M 0x38 -#define TWL4030_REG_I2S_RX_SCRAMBLE_L 0x39 -#define TWL4030_REG_APLL_CTL 0x3A -#define TWL4030_REG_DTMF_CTL 0x3B -#define TWL4030_REG_DTMF_PGA_CTL2 0x3C -#define TWL4030_REG_DTMF_PGA_CTL1 0x3D -#define TWL4030_REG_MISC_SET_1 0x3E -#define TWL4030_REG_PCMBTMUX 0x3F -#define TWL4030_REG_RX_PATH_SEL 0x43 -#define TWL4030_REG_VDL_APGA_CTL 0x44 -#define TWL4030_REG_VIBRA_CTL 0x45 -#define TWL4030_REG_VIBRA_SET 0x46 -#define TWL4030_REG_VIBRA_PWM_SET 0x47 -#define TWL4030_REG_ANAMIC_GAIN 0x48 -#define TWL4030_REG_MISC_SET_2 0x49 - -/* Bitfield Definitions */ - -/* TWL4030_CODEC_MODE (0x01) Fields */ -#define TWL4030_APLL_RATE 0xF0 -#define TWL4030_APLL_RATE_8000 0x00 -#define TWL4030_APLL_RATE_11025 0x10 -#define TWL4030_APLL_RATE_12000 0x20 -#define TWL4030_APLL_RATE_16000 0x40 -#define TWL4030_APLL_RATE_22050 0x50 -#define TWL4030_APLL_RATE_24000 0x60 -#define TWL4030_APLL_RATE_32000 0x80 -#define TWL4030_APLL_RATE_44100 0x90 -#define TWL4030_APLL_RATE_48000 0xA0 -#define TWL4030_APLL_RATE_96000 0xE0 -#define TWL4030_SEL_16K 0x08 -#define TWL4030_CODECPDZ 0x02 -#define TWL4030_OPT_MODE 0x01 -#define TWL4030_OPTION_1 (1 << 0) -#define TWL4030_OPTION_2 (0 << 0) - -/* TWL4030_OPTION (0x02) Fields */ -#define TWL4030_ATXL1_EN (1 << 0) -#define TWL4030_ATXR1_EN (1 << 1) -#define TWL4030_ATXL2_VTXL_EN (1 << 2) -#define TWL4030_ATXR2_VTXR_EN (1 << 3) -#define TWL4030_ARXL1_VRX_EN (1 << 4) -#define TWL4030_ARXR1_EN (1 << 5) -#define TWL4030_ARXL2_EN (1 << 6) -#define TWL4030_ARXR2_EN (1 << 7) - -/* TWL4030_REG_MICBIAS_CTL (0x04) Fields */ -#define TWL4030_MICBIAS2_CTL 0x40 -#define TWL4030_MICBIAS1_CTL 0x20 -#define TWL4030_HSMICBIAS_EN 0x04 -#define TWL4030_MICBIAS2_EN 0x02 -#define TWL4030_MICBIAS1_EN 0x01 - -/* ANAMICL (0x05) Fields */ -#define TWL4030_CNCL_OFFSET_START 0x80 -#define TWL4030_OFFSET_CNCL_SEL 0x60 -#define TWL4030_OFFSET_CNCL_SEL_ARX1 0x00 -#define TWL4030_OFFSET_CNCL_SEL_ARX2 0x20 -#define TWL4030_OFFSET_CNCL_SEL_VRX 0x40 -#define TWL4030_OFFSET_CNCL_SEL_ALL 0x60 -#define TWL4030_MICAMPL_EN 0x10 -#define TWL4030_CKMIC_EN 0x08 -#define TWL4030_AUXL_EN 0x04 -#define TWL4030_HSMIC_EN 0x02 -#define TWL4030_MAINMIC_EN 0x01 - -/* ANAMICR (0x06) Fields */ -#define TWL4030_MICAMPR_EN 0x10 -#define TWL4030_AUXR_EN 0x04 -#define TWL4030_SUBMIC_EN 0x01 - -/* AVADC_CTL (0x07) Fields */ -#define TWL4030_ADCL_EN 0x08 -#define TWL4030_AVADC_CLK_PRIORITY 0x04 -#define TWL4030_ADCR_EN 0x02 - -/* TWL4030_REG_ADCMICSEL (0x08) Fields */ -#define TWL4030_DIGMIC1_EN 0x08 -#define TWL4030_TX2IN_SEL 0x04 -#define TWL4030_DIGMIC0_EN 0x02 -#define TWL4030_TX1IN_SEL 0x01 - -/* AUDIO_IF (0x0E) Fields */ -#define TWL4030_AIF_SLAVE_EN 0x80 -#define TWL4030_DATA_WIDTH 0x60 -#define TWL4030_DATA_WIDTH_16S_16W 0x00 -#define TWL4030_DATA_WIDTH_32S_16W 0x40 -#define TWL4030_DATA_WIDTH_32S_24W 0x60 -#define TWL4030_AIF_FORMAT 0x18 -#define TWL4030_AIF_FORMAT_CODEC 0x00 -#define TWL4030_AIF_FORMAT_LEFT 0x08 -#define TWL4030_AIF_FORMAT_RIGHT 0x10 -#define TWL4030_AIF_FORMAT_TDM 0x18 -#define TWL4030_AIF_TRI_EN 0x04 -#define TWL4030_CLK256FS_EN 0x02 -#define TWL4030_AIF_EN 0x01 - -/* VOICE_IF (0x0F) Fields */ -#define TWL4030_VIF_SLAVE_EN 0x80 -#define TWL4030_VIF_DIN_EN 0x40 -#define TWL4030_VIF_DOUT_EN 0x20 -#define TWL4030_VIF_SWAP 0x10 -#define TWL4030_VIF_FORMAT 0x08 -#define TWL4030_VIF_TRI_EN 0x04 -#define TWL4030_VIF_SUB_EN 0x02 -#define TWL4030_VIF_EN 0x01 - -/* EAR_CTL (0x21) */ -#define TWL4030_EAR_GAIN 0x30 - -/* HS_GAIN_SET (0x23) Fields */ -#define TWL4030_HSR_GAIN 0x0C -#define TWL4030_HSR_GAIN_PWR_DOWN 0x00 -#define TWL4030_HSR_GAIN_PLUS_6DB 0x04 -#define TWL4030_HSR_GAIN_0DB 0x08 -#define TWL4030_HSR_GAIN_MINUS_6DB 0x0C -#define TWL4030_HSL_GAIN 0x03 -#define TWL4030_HSL_GAIN_PWR_DOWN 0x00 -#define TWL4030_HSL_GAIN_PLUS_6DB 0x01 -#define TWL4030_HSL_GAIN_0DB 0x02 -#define TWL4030_HSL_GAIN_MINUS_6DB 0x03 - -/* HS_POPN_SET (0x24) Fields */ -#define TWL4030_VMID_EN 0x40 -#define TWL4030_EXTMUTE 0x20 -#define TWL4030_RAMP_DELAY 0x1C -#define TWL4030_RAMP_DELAY_20MS 0x00 -#define TWL4030_RAMP_DELAY_40MS 0x04 -#define TWL4030_RAMP_DELAY_81MS 0x08 -#define TWL4030_RAMP_DELAY_161MS 0x0C -#define TWL4030_RAMP_DELAY_323MS 0x10 -#define TWL4030_RAMP_DELAY_645MS 0x14 -#define TWL4030_RAMP_DELAY_1291MS 0x18 -#define TWL4030_RAMP_DELAY_2581MS 0x1C -#define TWL4030_RAMP_EN 0x02 - -/* PREDL_CTL (0x25) */ -#define TWL4030_PREDL_GAIN 0x30 - -/* PREDR_CTL (0x26) */ -#define TWL4030_PREDR_GAIN 0x30 - -/* PRECKL_CTL (0x27) */ -#define TWL4030_PRECKL_GAIN 0x30 - -/* PRECKR_CTL (0x28) */ -#define TWL4030_PRECKR_GAIN 0x30 - -/* HFL_CTL (0x29, 0x2A) Fields */ -#define TWL4030_HF_CTL_HB_EN 0x04 -#define TWL4030_HF_CTL_LOOP_EN 0x08 -#define TWL4030_HF_CTL_RAMP_EN 0x10 -#define TWL4030_HF_CTL_REF_EN 0x20 - -/* APLL_CTL (0x3A) Fields */ -#define TWL4030_APLL_EN 0x10 -#define TWL4030_APLL_INFREQ 0x0F -#define TWL4030_APLL_INFREQ_19200KHZ 0x05 -#define TWL4030_APLL_INFREQ_26000KHZ 0x06 -#define TWL4030_APLL_INFREQ_38400KHZ 0x0F - -/* REG_MISC_SET_1 (0x3E) Fields */ -#define TWL4030_CLK64_EN 0x80 -#define TWL4030_SCRAMBLE_EN 0x40 -#define TWL4030_FMLOOP_EN 0x20 -#define TWL4030_SMOOTH_ANAVOL_EN 0x02 -#define TWL4030_DIGMIC_LR_SWAP_EN 0x01 - -/* VIBRA_CTL (0x45) */ -#define TWL4030_VIBRA_EN 0x01 -#define TWL4030_VIBRA_DIR 0x02 -#define TWL4030_VIBRA_AUDIO_SEL_L1 (0x00 << 2) -#define TWL4030_VIBRA_AUDIO_SEL_R1 (0x01 << 2) -#define TWL4030_VIBRA_AUDIO_SEL_L2 (0x02 << 2) -#define TWL4030_VIBRA_AUDIO_SEL_R2 (0x03 << 2) -#define TWL4030_VIBRA_SEL 0x10 -#define TWL4030_VIBRA_DIR_SEL 0x20 - -/* TWL4030 codec resource IDs */ -enum twl4030_codec_res { - TWL4030_CODEC_RES_POWER = 0, - TWL4030_CODEC_RES_APLL, - TWL4030_CODEC_RES_MAX, -}; - -int twl4030_codec_disable_resource(enum twl4030_codec_res id); -int twl4030_codec_enable_resource(enum twl4030_codec_res id); -unsigned int twl4030_codec_get_mclk(void); - -#endif /* End of __TWL4030_CODEC_H__ */ diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 98175a0..0002220 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -236,7 +236,7 @@ config SND_SOC_TLV320DAC33 tristate config SND_SOC_TWL4030 - select TWL4030_CODEC + select MFD_TWL4030_AUDIO tristate config SND_SOC_TWL6040 diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index bec788b..5e648d3 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -36,7 +36,7 @@ #include /* Register descriptions are here */ -#include +#include /* Shadow register used by the audio driver */ #define TWL4030_REG_SW_SHADOW 0x4A @@ -251,9 +251,9 @@ static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable) return; if (enable) - mode = twl4030_codec_enable_resource(TWL4030_CODEC_RES_POWER); + mode = twl4030_audio_enable_resource(TWL4030_AUDIO_RES_POWER); else - mode = twl4030_codec_disable_resource(TWL4030_CODEC_RES_POWER); + mode = twl4030_audio_disable_resource(TWL4030_AUDIO_RES_POWER); if (mode >= 0) { twl4030_write_reg_cache(codec, TWL4030_REG_CODEC_MODE, mode); @@ -375,13 +375,13 @@ static void twl4030_apll_enable(struct snd_soc_codec *codec, int enable) if (enable) { twl4030->apll_enabled++; if (twl4030->apll_enabled == 1) - status = twl4030_codec_enable_resource( - TWL4030_CODEC_RES_APLL); + status = twl4030_audio_enable_resource( + TWL4030_AUDIO_RES_APLL); } else { twl4030->apll_enabled--; if (!twl4030->apll_enabled) - status = twl4030_codec_disable_resource( - TWL4030_CODEC_RES_APLL); + status = twl4030_audio_disable_resource( + TWL4030_AUDIO_RES_APLL); } if (status >= 0) @@ -2260,7 +2260,7 @@ static int twl4030_soc_probe(struct snd_soc_codec *codec) } snd_soc_codec_set_drvdata(codec, twl4030); /* Set the defaults, and power up the codec */ - twl4030->sysclk = twl4030_codec_get_mclk() / 1000; + twl4030->sysclk = twl4030_audio_get_mclk() / 1000; codec->dapm.idle_bias_off = 1; twl4030_init_chip(codec); diff --git a/sound/soc/omap/sdp3430.c b/sound/soc/omap/sdp3430.c index 3f72d17..9f6a758 100644 --- a/sound/soc/omap/sdp3430.c +++ b/sound/soc/omap/sdp3430.c @@ -36,7 +36,7 @@ #include /* Register descriptions for twl4030 codec part */ -#include +#include #include "omap-mcbsp.h" #include "omap-pcm.h" diff --git a/sound/soc/omap/zoom2.c b/sound/soc/omap/zoom2.c index 0170994..9a2666f 100644 --- a/sound/soc/omap/zoom2.c +++ b/sound/soc/omap/zoom2.c @@ -32,7 +32,7 @@ #include /* Register descriptions for twl4030 codec part */ -#include +#include #include "omap-mcbsp.h" #include "omap-pcm.h" -- cgit v1.1 From 4ae6df5e1018796ce260be59b2c603bd0f9faa94 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 31 May 2011 15:21:13 +0300 Subject: MFD: twl4030-audio: Rename platform data Allign the platform data names for twl4030 audio submodule: twl4030_audio_data: for the core MFD driver twl4030_codec_data: for ASoC codec driver twl4030_vibra_data: for the input/ForceFeedback driver To avoid breakage, change all depending drivers, files to use the new types. Signed-off-by: Peter Ujfalusi Acked-by: Samuel Ortiz --- arch/arm/mach-omap2/board-rx51-peripherals.c | 6 +++--- arch/arm/mach-omap2/board-zoom-peripherals.c | 10 +++++----- arch/arm/mach-omap2/twl-common.c | 10 +++++----- drivers/input/misc/twl4030-vibra.c | 2 +- drivers/mfd/twl-core.c | 8 ++++---- drivers/mfd/twl4030-audio.c | 8 ++++---- include/linux/i2c/twl.h | 12 ++++++------ sound/soc/codecs/twl4030.c | 6 +++--- sound/soc/codecs/twl6040.c | 2 +- 9 files changed, 32 insertions(+), 32 deletions(-) diff --git a/arch/arm/mach-omap2/board-rx51-peripherals.c b/arch/arm/mach-omap2/board-rx51-peripherals.c index 6140290..f52bc61 100644 --- a/arch/arm/mach-omap2/board-rx51-peripherals.c +++ b/arch/arm/mach-omap2/board-rx51-peripherals.c @@ -741,11 +741,11 @@ static struct twl4030_power_data rx51_t2scripts_data __initdata = { .resource_config = twl4030_rconfig, }; -struct twl4030_codec_vibra_data rx51_vibra_data __initdata = { +struct twl4030_vibra_data rx51_vibra_data __initdata = { .coexist = 0, }; -struct twl4030_codec_data rx51_codec_data __initdata = { +struct twl4030_audio_data rx51_audio_data __initdata = { .audio_mclk = 26000000, .vibra = &rx51_vibra_data, }; @@ -755,7 +755,7 @@ static struct twl4030_platform_data rx51_twldata __initdata = { .gpio = &rx51_gpio_data, .keypad = &rx51_kp_data, .power = &rx51_t2scripts_data, - .codec = &rx51_codec_data, + .audio = &rx51_audio_data, .vaux1 = &rx51_vaux1, .vaux2 = &rx51_vaux2, diff --git a/arch/arm/mach-omap2/board-zoom-peripherals.c b/arch/arm/mach-omap2/board-zoom-peripherals.c index 13a6442..6d0aa4f 100644 --- a/arch/arm/mach-omap2/board-zoom-peripherals.c +++ b/arch/arm/mach-omap2/board-zoom-peripherals.c @@ -274,12 +274,12 @@ static int __init omap_i2c_init(void) TWL_COMMON_REGULATOR_VDAC | TWL_COMMON_REGULATOR_VPLL2); if (machine_is_omap_zoom2()) { - struct twl4030_codec_audio_data *audio_data; - audio_data = zoom_twldata.codec->audio; + struct twl4030_codec_data *codec_data; + codec_data = zoom_twldata.audio->codec; - audio_data->ramp_delay_value = 3; /* 161 ms */ - audio_data->hs_extmute = 1; - audio_data->set_hs_extmute = zoom2_set_hs_extmute; + codec_data->ramp_delay_value = 3; /* 161 ms */ + codec_data->hs_extmute = 1; + codec_data->set_hs_extmute = zoom2_set_hs_extmute; } omap_pmic_init(1, 2400, "twl5030", INT_34XX_SYS_NIRQ, &zoom_twldata); omap_register_i2c_bus(2, 400, NULL, 0); diff --git a/arch/arm/mach-omap2/twl-common.c b/arch/arm/mach-omap2/twl-common.c index 3aaa46f..2543342 100644 --- a/arch/arm/mach-omap2/twl-common.c +++ b/arch/arm/mach-omap2/twl-common.c @@ -80,11 +80,11 @@ static struct twl4030_madc_platform_data omap3_madc_pdata = { .irq_line = 1, }; -static struct twl4030_codec_audio_data omap3_audio; +static struct twl4030_codec_data omap3_codec; -static struct twl4030_codec_data omap3_codec_pdata = { +static struct twl4030_audio_data omap3_audio_pdata = { .audio_mclk = 26000000, - .audio = &omap3_audio, + .codec = &omap3_codec, }; static struct regulator_consumer_supply omap3_vdda_dac_supplies[] = { @@ -292,8 +292,8 @@ void __init omap3_pmic_get_config(struct twl4030_platform_data *pmic_data, if (pdata_flags & TWL_COMMON_PDATA_MADC && !pmic_data->madc) pmic_data->madc = &omap3_madc_pdata; - if (pdata_flags & TWL_COMMON_PDATA_AUDIO && !pmic_data->codec) - pmic_data->codec = &omap3_codec_pdata; + if (pdata_flags & TWL_COMMON_PDATA_AUDIO && !pmic_data->audio) + pmic_data->audio = &omap3_audio_pdata; /* Common regulator configurations */ if (regulators_flags & TWL_COMMON_REGULATOR_VDAC && !pmic_data->vdac) diff --git a/drivers/input/misc/twl4030-vibra.c b/drivers/input/misc/twl4030-vibra.c index 7abca85..3c1a432 100644 --- a/drivers/input/misc/twl4030-vibra.c +++ b/drivers/input/misc/twl4030-vibra.c @@ -196,7 +196,7 @@ static SIMPLE_DEV_PM_OPS(twl4030_vibra_pm_ops, static int __devinit twl4030_vibra_probe(struct platform_device *pdev) { - struct twl4030_codec_vibra_data *pdata = pdev->dev.platform_data; + struct twl4030_vibra_data *pdata = pdev->dev.platform_data; struct vibra_info *info; int ret; diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index b8f2a4e..f9d7880 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -815,20 +815,20 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) return PTR_ERR(child); } - if (twl_has_codec() && pdata->codec && twl_class_is_4030()) { + if (twl_has_codec() && pdata->audio && twl_class_is_4030()) { sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid; child = add_child(sub_chip_id, "twl4030-audio", - pdata->codec, sizeof(*pdata->codec), + pdata->audio, sizeof(*pdata->audio), false, 0, 0); if (IS_ERR(child)) return PTR_ERR(child); } /* Phoenix codec driver is probed directly atm */ - if (twl_has_codec() && pdata->codec && twl_class_is_6030()) { + if (twl_has_codec() && pdata->audio && twl_class_is_6030()) { sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid; child = add_child(sub_chip_id, "twl6040-codec", - pdata->codec, sizeof(*pdata->codec), + pdata->audio, sizeof(*pdata->audio), false, 0, 0); if (IS_ERR(child)) return PTR_ERR(child); diff --git a/drivers/mfd/twl4030-audio.c b/drivers/mfd/twl4030-audio.c index 5cdf841..ae51ab5 100644 --- a/drivers/mfd/twl4030-audio.c +++ b/drivers/mfd/twl4030-audio.c @@ -159,7 +159,7 @@ EXPORT_SYMBOL_GPL(twl4030_audio_get_mclk); static int __devinit twl4030_audio_probe(struct platform_device *pdev) { struct twl4030_audio *audio; - struct twl4030_codec_data *pdata = pdev->dev.platform_data; + struct twl4030_audio_data *pdata = pdev->dev.platform_data; struct mfd_cell *cell = NULL; int ret, childs = 0; u8 val; @@ -206,11 +206,11 @@ static int __devinit twl4030_audio_probe(struct platform_device *pdev) audio->resource[TWL4030_AUDIO_RES_APLL].reg = TWL4030_REG_APLL_CTL; audio->resource[TWL4030_AUDIO_RES_APLL].mask = TWL4030_APLL_EN; - if (pdata->audio) { + if (pdata->codec) { cell = &audio->cells[childs]; cell->name = "twl4030-codec"; - cell->platform_data = pdata->audio; - cell->pdata_size = sizeof(*pdata->audio); + cell->platform_data = pdata->codec; + cell->pdata_size = sizeof(*pdata->codec); childs++; } if (pdata->vibra) { diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h index ba4f886..e0aba2b 100644 --- a/include/linux/i2c/twl.h +++ b/include/linux/i2c/twl.h @@ -657,7 +657,7 @@ struct twl4030_power_data { extern void twl4030_power_init(struct twl4030_power_data *triton2_scripts); extern int twl4030_remove_script(u8 flags); -struct twl4030_codec_audio_data { +struct twl4030_codec_data { unsigned int digimic_delay; /* in ms */ unsigned int ramp_delay_value; unsigned int offset_cncl_path; @@ -667,14 +667,14 @@ struct twl4030_codec_audio_data { void (*set_hs_extmute)(int mute); }; -struct twl4030_codec_vibra_data { +struct twl4030_vibra_data { unsigned int coexist; }; -struct twl4030_codec_data { +struct twl4030_audio_data { unsigned int audio_mclk; - struct twl4030_codec_audio_data *audio; - struct twl4030_codec_vibra_data *vibra; + struct twl4030_codec_data *codec; + struct twl4030_vibra_data *vibra; /* twl6040 */ int audpwron_gpio; /* audio power-on gpio */ @@ -690,7 +690,7 @@ struct twl4030_platform_data { struct twl4030_keypad_data *keypad; struct twl4030_usb_data *usb; struct twl4030_power_data *power; - struct twl4030_codec_data *codec; + struct twl4030_audio_data *audio; /* Common LDO regulators for TWL4030/TWL6030 */ struct regulator_init_data *vdac; diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 5e648d3..71674be 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -297,7 +297,7 @@ static inline void twl4030_reset_registers(struct snd_soc_codec *codec) static void twl4030_init_chip(struct snd_soc_codec *codec) { - struct twl4030_codec_audio_data *pdata = dev_get_platdata(codec->dev); + struct twl4030_codec_data *pdata = dev_get_platdata(codec->dev); struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); u8 reg, byte; int i = 0; @@ -732,7 +732,7 @@ static int aif_event(struct snd_soc_dapm_widget *w, static void headset_ramp(struct snd_soc_codec *codec, int ramp) { - struct twl4030_codec_audio_data *pdata = codec->dev->platform_data; + struct twl4030_codec_data *pdata = codec->dev->platform_data; unsigned char hs_gain, hs_pop; struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); /* Base values for ramp delay calculation: 2^19 - 2^26 */ @@ -2297,7 +2297,7 @@ static struct snd_soc_codec_driver soc_codec_dev_twl4030 = { static int __devinit twl4030_codec_probe(struct platform_device *pdev) { - struct twl4030_codec_audio_data *pdata = pdev->dev.platform_data; + struct twl4030_codec_data *pdata = pdev->dev.platform_data; if (!pdata) { dev_err(&pdev->dev, "platform_data is missing\n"); diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 4c33663..ade6616 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -1600,7 +1600,7 @@ static int twl6040_resume(struct snd_soc_codec *codec) static int twl6040_probe(struct snd_soc_codec *codec) { - struct twl4030_codec_data *twl_codec = codec->dev->platform_data; + struct twl4030_audio_data *twl_codec = codec->dev->platform_data; struct twl6040_data *priv; int audpwron, naudint; int ret = 0; -- cgit v1.1 From f19b2823f82499c60ec15d5fe8783193d77e3043 Mon Sep 17 00:00:00 2001 From: Misael Lopez Cruz Date: Wed, 27 Apr 2011 02:14:07 -0500 Subject: mfd: twl6040: Add initial support TWL6040 IC provides analog high-end audio codec functions for handset applications. It contains several audio analog inputs and outputs as well as vibrator support. It's connected to the host processor via PDM interface for audio data communication. The audio modules are controlled by internal registers that can be accessed by I2C and PDM interface. TWL6040 MFD will be registered as a child of TWL-CORE, and will have two children of its own: twl6040-codec and twl6040-vibra. This driver is based on TWL4030 and WM8350 MFD drivers. Signed-off-by: Misael Lopez Cruz Signed-off-by: Peter Ujfalusi Acked-by: Samuel Ortiz --- arch/arm/plat-omap/include/plat/irqs.h | 12 +- drivers/mfd/Kconfig | 6 + drivers/mfd/Makefile | 1 + drivers/mfd/twl-core.c | 5 +- drivers/mfd/twl6040-core.c | 596 +++++++++++++++++++++++++++++++++ drivers/mfd/twl6040-irq.c | 205 ++++++++++++ include/linux/i2c/twl.h | 1 + include/linux/mfd/twl6040.h | 255 ++++++++++++++ 8 files changed, 1076 insertions(+), 5 deletions(-) create mode 100644 drivers/mfd/twl6040-core.c create mode 100644 drivers/mfd/twl6040-irq.c create mode 100644 include/linux/mfd/twl6040.h diff --git a/arch/arm/plat-omap/include/plat/irqs.h b/arch/arm/plat-omap/include/plat/irqs.h index c884320..926d25c 100644 --- a/arch/arm/plat-omap/include/plat/irqs.h +++ b/arch/arm/plat-omap/include/plat/irqs.h @@ -407,11 +407,19 @@ #endif #define TWL6030_IRQ_END (TWL6030_IRQ_BASE + TWL6030_BASE_NR_IRQS) +#define TWL6040_CODEC_IRQ_BASE TWL6030_IRQ_END +#ifdef CONFIG_TWL6040_CODEC +#define TWL6040_CODEC_NR_IRQS 6 +#else +#define TWL6040_CODEC_NR_IRQS 0 +#endif +#define TWL6040_CODEC_IRQ_END (TWL6040_CODEC_IRQ_BASE + TWL6040_CODEC_NR_IRQS) + /* Total number of interrupts depends on the enabled blocks above */ -#if (TWL4030_GPIO_IRQ_END > TWL6030_IRQ_END) +#if (TWL4030_GPIO_IRQ_END > TWL6040_CODEC_IRQ_END) #define TWL_IRQ_END TWL4030_GPIO_IRQ_END #else -#define TWL_IRQ_END TWL6030_IRQ_END +#define TWL_IRQ_END TWL6040_CODEC_IRQ_END #endif /* GPMC related */ diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 3a6f76a..ac6b4ae 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -233,6 +233,12 @@ config TWL6030_PWM Say yes here if you want support for TWL6030 PWM. This is used to control charging LED brightness. +config TWL6040_CORE + bool + depends on TWL4030_CORE && GENERIC_HARDIRQS + select MFD_CORE + default n + config MFD_STMPE bool "Support STMicroelectronics STMPE" depends on I2C=y && GENERIC_HARDIRQS diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 4cf9465..41f3b61 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o obj-$(CONFIG_MFD_TWL4030_AUDIO) += twl4030-audio.o obj-$(CONFIG_TWL6030_PWM) += twl6030-pwm.o +obj-$(CONFIG_TWL6040_CORE) += twl6040-core.o twl6040-irq.o obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index f9d7880..a2eddc70 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -110,7 +110,7 @@ #endif #if defined(CONFIG_TWL4030_CODEC) || defined(CONFIG_TWL4030_CODEC_MODULE) ||\ - defined(CONFIG_SND_SOC_TWL6040) || defined(CONFIG_SND_SOC_TWL6040_MODULE) + defined(CONFIG_TWL6040_CORE) || defined(CONFIG_TWL6040_CORE_MODULE) #define twl_has_codec() true #else #define twl_has_codec() false @@ -824,10 +824,9 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) return PTR_ERR(child); } - /* Phoenix codec driver is probed directly atm */ if (twl_has_codec() && pdata->audio && twl_class_is_6030()) { sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid; - child = add_child(sub_chip_id, "twl6040-codec", + child = add_child(sub_chip_id, "twl6040", pdata->audio, sizeof(*pdata->audio), false, 0, 0); if (IS_ERR(child)) diff --git a/drivers/mfd/twl6040-core.c b/drivers/mfd/twl6040-core.c new file mode 100644 index 0000000..57ae36b --- /dev/null +++ b/drivers/mfd/twl6040-core.c @@ -0,0 +1,596 @@ +/* + * MFD driver for TWL6040 audio device + * + * Authors: Misael Lopez Cruz + * Jorge Eduardo Candelaria + * Peter Ujfalusi + * + * Copyright: (C) 2011 Texas Instruments, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct platform_device *twl6040_dev; + +int twl6040_reg_read(struct twl6040 *twl6040, unsigned int reg) +{ + int ret; + u8 val = 0; + + mutex_lock(&twl6040->io_mutex); + ret = twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &val, reg); + if (ret < 0) { + mutex_unlock(&twl6040->io_mutex); + return ret; + } + mutex_unlock(&twl6040->io_mutex); + + return val; +} +EXPORT_SYMBOL(twl6040_reg_read); + +int twl6040_reg_write(struct twl6040 *twl6040, unsigned int reg, u8 val) +{ + int ret; + + mutex_lock(&twl6040->io_mutex); + ret = twl_i2c_write_u8(TWL_MODULE_AUDIO_VOICE, val, reg); + mutex_unlock(&twl6040->io_mutex); + + return ret; +} +EXPORT_SYMBOL(twl6040_reg_write); + +int twl6040_set_bits(struct twl6040 *twl6040, unsigned int reg, u8 mask) +{ + int ret; + u8 val; + + mutex_lock(&twl6040->io_mutex); + ret = twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &val, reg); + if (ret) + goto out; + + val |= mask; + ret = twl_i2c_write_u8(TWL_MODULE_AUDIO_VOICE, val, reg); +out: + mutex_unlock(&twl6040->io_mutex); + return ret; +} +EXPORT_SYMBOL(twl6040_set_bits); + +int twl6040_clear_bits(struct twl6040 *twl6040, unsigned int reg, u8 mask) +{ + int ret; + u8 val; + + mutex_lock(&twl6040->io_mutex); + ret = twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &val, reg); + if (ret) + goto out; + + val &= ~mask; + ret = twl_i2c_write_u8(TWL_MODULE_AUDIO_VOICE, val, reg); +out: + mutex_unlock(&twl6040->io_mutex); + return ret; +} +EXPORT_SYMBOL(twl6040_clear_bits); + +/* twl6040 codec manual power-up sequence */ +static int twl6040_power_up(struct twl6040 *twl6040) +{ + u8 ldoctl, ncpctl, lppllctl; + int ret; + + /* enable high-side LDO, reference system and internal oscillator */ + ldoctl = TWL6040_HSLDOENA | TWL6040_REFENA | TWL6040_OSCENA; + ret = twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl); + if (ret) + return ret; + usleep_range(10000, 10500); + + /* enable negative charge pump */ + ncpctl = TWL6040_NCPENA; + ret = twl6040_reg_write(twl6040, TWL6040_REG_NCPCTL, ncpctl); + if (ret) + goto ncp_err; + usleep_range(1000, 1500); + + /* enable low-side LDO */ + ldoctl |= TWL6040_LSLDOENA; + ret = twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl); + if (ret) + goto lsldo_err; + usleep_range(1000, 1500); + + /* enable low-power PLL */ + lppllctl = TWL6040_LPLLENA; + ret = twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl); + if (ret) + goto lppll_err; + usleep_range(5000, 5500); + + /* disable internal oscillator */ + ldoctl &= ~TWL6040_OSCENA; + ret = twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl); + if (ret) + goto osc_err; + + return 0; + +osc_err: + lppllctl &= ~TWL6040_LPLLENA; + twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl); +lppll_err: + ldoctl &= ~TWL6040_LSLDOENA; + twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl); +lsldo_err: + ncpctl &= ~TWL6040_NCPENA; + twl6040_reg_write(twl6040, TWL6040_REG_NCPCTL, ncpctl); +ncp_err: + ldoctl &= ~(TWL6040_HSLDOENA | TWL6040_REFENA | TWL6040_OSCENA); + twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl); + + return ret; +} + +/* twl6040 manual power-down sequence */ +static void twl6040_power_down(struct twl6040 *twl6040) +{ + u8 ncpctl, ldoctl, lppllctl; + + ncpctl = twl6040_reg_read(twl6040, TWL6040_REG_NCPCTL); + ldoctl = twl6040_reg_read(twl6040, TWL6040_REG_LDOCTL); + lppllctl = twl6040_reg_read(twl6040, TWL6040_REG_LPPLLCTL); + + /* enable internal oscillator */ + ldoctl |= TWL6040_OSCENA; + twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl); + usleep_range(1000, 1500); + + /* disable low-power PLL */ + lppllctl &= ~TWL6040_LPLLENA; + twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl); + + /* disable low-side LDO */ + ldoctl &= ~TWL6040_LSLDOENA; + twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl); + + /* disable negative charge pump */ + ncpctl &= ~TWL6040_NCPENA; + twl6040_reg_write(twl6040, TWL6040_REG_NCPCTL, ncpctl); + + /* disable high-side LDO, reference system and internal oscillator */ + ldoctl &= ~(TWL6040_HSLDOENA | TWL6040_REFENA | TWL6040_OSCENA); + twl6040_reg_write(twl6040, TWL6040_REG_LDOCTL, ldoctl); +} + +static irqreturn_t twl6040_naudint_handler(int irq, void *data) +{ + struct twl6040 *twl6040 = data; + u8 intid, status; + + intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID); + + if (intid & TWL6040_READYINT) + complete(&twl6040->ready); + + if (intid & TWL6040_THINT) { + status = twl6040_reg_read(twl6040, TWL6040_REG_STATUS); + if (status & TWL6040_TSHUTDET) { + dev_warn(&twl6040_dev->dev, + "Thermal shutdown, powering-off"); + twl6040_power(twl6040, 0); + } else { + dev_warn(&twl6040_dev->dev, + "Leaving thermal shutdown, powering-on"); + twl6040_power(twl6040, 1); + } + } + + return IRQ_HANDLED; +} + +static int twl6040_power_up_completion(struct twl6040 *twl6040, + int naudint) +{ + int time_left; + u8 intid; + + time_left = wait_for_completion_timeout(&twl6040->ready, + msecs_to_jiffies(144)); + if (!time_left) { + intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID); + if (!(intid & TWL6040_READYINT)) { + dev_err(&twl6040_dev->dev, + "timeout waiting for READYINT\n"); + return -ETIMEDOUT; + } + } + + return 0; +} + +int twl6040_power(struct twl6040 *twl6040, int on) +{ + int audpwron = twl6040->audpwron; + int naudint = twl6040->irq; + int ret = 0; + + mutex_lock(&twl6040->mutex); + + if (on) { + /* already powered-up */ + if (twl6040->power_count++) + goto out; + + if (gpio_is_valid(audpwron)) { + /* use AUDPWRON line */ + gpio_set_value(audpwron, 1); + /* wait for power-up completion */ + ret = twl6040_power_up_completion(twl6040, naudint); + if (ret) { + dev_err(&twl6040_dev->dev, + "automatic power-down failed\n"); + twl6040->power_count = 0; + goto out; + } + } else { + /* use manual power-up sequence */ + ret = twl6040_power_up(twl6040); + if (ret) { + dev_err(&twl6040_dev->dev, + "manual power-up failed\n"); + twl6040->power_count = 0; + goto out; + } + } + twl6040->pll = TWL6040_LPPLL_ID; + twl6040->sysclk = 19200000; + } else { + /* already powered-down */ + if (!twl6040->power_count) { + dev_err(&twl6040_dev->dev, + "device is already powered-off\n"); + ret = -EPERM; + goto out; + } + + if (--twl6040->power_count) + goto out; + + if (gpio_is_valid(audpwron)) { + /* use AUDPWRON line */ + gpio_set_value(audpwron, 0); + + /* power-down sequence latency */ + usleep_range(500, 700); + } else { + /* use manual power-down sequence */ + twl6040_power_down(twl6040); + } + twl6040->pll = TWL6040_NOPLL_ID; + twl6040->sysclk = 0; + } + +out: + mutex_unlock(&twl6040->mutex); + return ret; +} +EXPORT_SYMBOL(twl6040_power); + +int twl6040_set_pll(struct twl6040 *twl6040, enum twl6040_pll_id id, + unsigned int freq_in, unsigned int freq_out) +{ + u8 hppllctl, lppllctl; + int ret = 0; + + mutex_lock(&twl6040->mutex); + + hppllctl = twl6040_reg_read(twl6040, TWL6040_REG_HPPLLCTL); + lppllctl = twl6040_reg_read(twl6040, TWL6040_REG_LPPLLCTL); + + switch (id) { + case TWL6040_LPPLL_ID: + /* low-power PLL divider */ + switch (freq_out) { + case 17640000: + lppllctl |= TWL6040_LPLLFIN; + break; + case 19200000: + lppllctl &= ~TWL6040_LPLLFIN; + break; + default: + dev_err(&twl6040_dev->dev, + "freq_out %d not supported\n", freq_out); + ret = -EINVAL; + goto pll_out; + } + twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl); + + switch (freq_in) { + case 32768: + lppllctl |= TWL6040_LPLLENA; + twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, + lppllctl); + mdelay(5); + lppllctl &= ~TWL6040_HPLLSEL; + twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, + lppllctl); + hppllctl &= ~TWL6040_HPLLENA; + twl6040_reg_write(twl6040, TWL6040_REG_HPPLLCTL, + hppllctl); + break; + default: + dev_err(&twl6040_dev->dev, + "freq_in %d not supported\n", freq_in); + ret = -EINVAL; + goto pll_out; + } + + twl6040->pll = TWL6040_LPPLL_ID; + break; + case TWL6040_HPPLL_ID: + /* high-performance PLL can provide only 19.2 MHz */ + if (freq_out != 19200000) { + dev_err(&twl6040_dev->dev, + "freq_out %d not supported\n", freq_out); + ret = -EINVAL; + goto pll_out; + } + + hppllctl &= ~TWL6040_MCLK_MSK; + + switch (freq_in) { + case 12000000: + /* PLL enabled, active mode */ + hppllctl |= TWL6040_MCLK_12000KHZ | + TWL6040_HPLLENA; + break; + case 19200000: + /* + * PLL disabled + * (enable PLL if MCLK jitter quality + * doesn't meet specification) + */ + hppllctl |= TWL6040_MCLK_19200KHZ; + break; + case 26000000: + /* PLL enabled, active mode */ + hppllctl |= TWL6040_MCLK_26000KHZ | + TWL6040_HPLLENA; + break; + case 38400000: + /* PLL enabled, active mode */ + hppllctl |= TWL6040_MCLK_38400KHZ | + TWL6040_HPLLENA; + break; + default: + dev_err(&twl6040_dev->dev, + "freq_in %d not supported\n", freq_in); + ret = -EINVAL; + goto pll_out; + } + + /* enable clock slicer to ensure input waveform is square */ + hppllctl |= TWL6040_HPLLSQRENA; + + twl6040_reg_write(twl6040, TWL6040_REG_HPPLLCTL, hppllctl); + usleep_range(500, 700); + lppllctl |= TWL6040_HPLLSEL; + twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl); + lppllctl &= ~TWL6040_LPLLENA; + twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl); + + twl6040->pll = TWL6040_HPPLL_ID; + break; + default: + dev_err(&twl6040_dev->dev, "unknown pll id %d\n", id); + ret = -EINVAL; + goto pll_out; + } + + twl6040->sysclk = freq_out; + +pll_out: + mutex_unlock(&twl6040->mutex); + return ret; +} +EXPORT_SYMBOL(twl6040_set_pll); + +enum twl6040_pll_id twl6040_get_pll(struct twl6040 *twl6040) +{ + return twl6040->pll; +} +EXPORT_SYMBOL(twl6040_get_pll); + +unsigned int twl6040_get_sysclk(struct twl6040 *twl6040) +{ + return twl6040->sysclk; +} +EXPORT_SYMBOL(twl6040_get_sysclk); + +static int __devinit twl6040_probe(struct platform_device *pdev) +{ + struct twl4030_audio_data *pdata = pdev->dev.platform_data; + struct twl6040 *twl6040; + struct mfd_cell *cell = NULL; + int ret, children = 0; + + if (!pdata) { + dev_err(&pdev->dev, "Platform data is missing\n"); + return -EINVAL; + } + + twl6040 = kzalloc(sizeof(struct twl6040), GFP_KERNEL); + if (!twl6040) + return -ENOMEM; + + platform_set_drvdata(pdev, twl6040); + + twl6040_dev = pdev; + twl6040->dev = &pdev->dev; + twl6040->audpwron = pdata->audpwron_gpio; + twl6040->irq = pdata->naudint_irq; + twl6040->irq_base = pdata->irq_base; + + mutex_init(&twl6040->mutex); + mutex_init(&twl6040->io_mutex); + init_completion(&twl6040->ready); + + twl6040->rev = twl6040_reg_read(twl6040, TWL6040_REG_ASICREV); + + if (gpio_is_valid(twl6040->audpwron)) { + ret = gpio_request(twl6040->audpwron, "audpwron"); + if (ret) + goto gpio1_err; + + ret = gpio_direction_output(twl6040->audpwron, 0); + if (ret) + goto gpio2_err; + } + + /* ERRATA: Automatic power-up is not possible in ES1.0 */ + if (twl6040->rev == TWL6040_REV_ES1_0) + twl6040->audpwron = -EINVAL; + + if (twl6040->irq) { + /* codec interrupt */ + ret = twl6040_irq_init(twl6040); + if (ret) + goto gpio2_err; + + ret = twl6040_request_irq(twl6040, TWL6040_IRQ_READY, + twl6040_naudint_handler, 0, + "twl6040_irq_ready", twl6040); + if (ret) { + dev_err(twl6040->dev, "READY IRQ request failed: %d\n", + ret); + goto irq_err; + } + } + + /* dual-access registers controlled by I2C only */ + twl6040_set_bits(twl6040, TWL6040_REG_ACCCTL, TWL6040_I2CSEL); + + if (pdata->codec) { + cell = &twl6040->cells[children]; + cell->name = "twl6040-codec"; + /* The codec expects the twl4030_audio_data as platform data */ + cell->platform_data = pdata; + cell->pdata_size = sizeof(*pdata); + children++; + } + + if (pdata->vibra) { + cell = &twl6040->cells[children]; + cell->name = "twl6040-vibra"; + cell->platform_data = pdata->vibra; + cell->pdata_size = sizeof(*pdata->vibra); + children++; + } + + if (children) { + ret = mfd_add_devices(&pdev->dev, pdev->id, twl6040->cells, + children, NULL, 0); + if (ret) + goto mfd_err; + } else { + dev_err(&pdev->dev, "No platform data found for children\n"); + ret = -ENODEV; + goto mfd_err; + } + + return 0; + +mfd_err: + if (twl6040->irq) + twl6040_free_irq(twl6040, TWL6040_IRQ_READY, twl6040); +irq_err: + if (twl6040->irq) + twl6040_irq_exit(twl6040); +gpio2_err: + if (gpio_is_valid(twl6040->audpwron)) + gpio_free(twl6040->audpwron); +gpio1_err: + platform_set_drvdata(pdev, NULL); + kfree(twl6040); + twl6040_dev = NULL; + return ret; +} + +static int __devexit twl6040_remove(struct platform_device *pdev) +{ + struct twl6040 *twl6040 = platform_get_drvdata(pdev); + + if (twl6040->power_count) + twl6040_power(twl6040, 0); + + if (gpio_is_valid(twl6040->audpwron)) + gpio_free(twl6040->audpwron); + + twl6040_free_irq(twl6040, TWL6040_IRQ_READY, twl6040); + + if (twl6040->irq) + twl6040_irq_exit(twl6040); + + mfd_remove_devices(&pdev->dev); + platform_set_drvdata(pdev, NULL); + kfree(twl6040); + twl6040_dev = NULL; + + return 0; +} + +static struct platform_driver twl6040_driver = { + .probe = twl6040_probe, + .remove = __devexit_p(twl6040_remove), + .driver = { + .owner = THIS_MODULE, + .name = "twl6040", + }, +}; + +static int __devinit twl6040_init(void) +{ + return platform_driver_register(&twl6040_driver); +} +module_init(twl6040_init); + +static void __devexit twl6040_exit(void) +{ + platform_driver_unregister(&twl6040_driver); +} + +module_exit(twl6040_exit); + +MODULE_DESCRIPTION("TWL6040 MFD"); +MODULE_AUTHOR("Misael Lopez Cruz "); +MODULE_AUTHOR("Jorge Eduardo Candelaria "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:twl6040"); diff --git a/drivers/mfd/twl6040-irq.c b/drivers/mfd/twl6040-irq.c new file mode 100644 index 0000000..938053541 --- /dev/null +++ b/drivers/mfd/twl6040-irq.c @@ -0,0 +1,205 @@ +/* + * Interrupt controller support for TWL6040 + * + * Author: Misael Lopez Cruz + * + * Copyright: (C) 2011 Texas Instruments, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include +#include + +struct twl6040_irq_data { + int mask; + int status; +}; + +static struct twl6040_irq_data twl6040_irqs[] = { + { + .mask = TWL6040_THMSK, + .status = TWL6040_THINT, + }, + { + .mask = TWL6040_PLUGMSK, + .status = TWL6040_PLUGINT | TWL6040_UNPLUGINT, + }, + { + .mask = TWL6040_HOOKMSK, + .status = TWL6040_HOOKINT, + }, + { + .mask = TWL6040_HFMSK, + .status = TWL6040_HFINT, + }, + { + .mask = TWL6040_VIBMSK, + .status = TWL6040_VIBINT, + }, + { + .mask = TWL6040_READYMSK, + .status = TWL6040_READYINT, + }, +}; + +static inline +struct twl6040_irq_data *irq_to_twl6040_irq(struct twl6040 *twl6040, + int irq) +{ + return &twl6040_irqs[irq - twl6040->irq_base]; +} + +static void twl6040_irq_lock(struct irq_data *data) +{ + struct twl6040 *twl6040 = irq_data_get_irq_chip_data(data); + + mutex_lock(&twl6040->irq_mutex); +} + +static void twl6040_irq_sync_unlock(struct irq_data *data) +{ + struct twl6040 *twl6040 = irq_data_get_irq_chip_data(data); + + /* write back to hardware any change in irq mask */ + if (twl6040->irq_masks_cur != twl6040->irq_masks_cache) { + twl6040->irq_masks_cache = twl6040->irq_masks_cur; + twl6040_reg_write(twl6040, TWL6040_REG_INTMR, + twl6040->irq_masks_cur); + } + + mutex_unlock(&twl6040->irq_mutex); +} + +static void twl6040_irq_enable(struct irq_data *data) +{ + struct twl6040 *twl6040 = irq_data_get_irq_chip_data(data); + struct twl6040_irq_data *irq_data = irq_to_twl6040_irq(twl6040, + data->irq); + + twl6040->irq_masks_cur &= ~irq_data->mask; +} + +static void twl6040_irq_disable(struct irq_data *data) +{ + struct twl6040 *twl6040 = irq_data_get_irq_chip_data(data); + struct twl6040_irq_data *irq_data = irq_to_twl6040_irq(twl6040, + data->irq); + + twl6040->irq_masks_cur |= irq_data->mask; +} + +static struct irq_chip twl6040_irq_chip = { + .name = "twl6040", + .irq_bus_lock = twl6040_irq_lock, + .irq_bus_sync_unlock = twl6040_irq_sync_unlock, + .irq_enable = twl6040_irq_enable, + .irq_disable = twl6040_irq_disable, +}; + +static irqreturn_t twl6040_irq_thread(int irq, void *data) +{ + struct twl6040 *twl6040 = data; + u8 intid; + int i; + + intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID); + + /* apply masking and report (backwards to handle READYINT first) */ + for (i = ARRAY_SIZE(twl6040_irqs) - 1; i >= 0; i--) { + if (twl6040->irq_masks_cur & twl6040_irqs[i].mask) + intid &= ~twl6040_irqs[i].status; + if (intid & twl6040_irqs[i].status) + handle_nested_irq(twl6040->irq_base + i); + } + + /* ack unmasked irqs */ + twl6040_reg_write(twl6040, TWL6040_REG_INTID, intid); + + return IRQ_HANDLED; +} + +int twl6040_irq_init(struct twl6040 *twl6040) +{ + int cur_irq, ret; + u8 val; + + mutex_init(&twl6040->irq_mutex); + + /* mask the individual interrupt sources */ + twl6040->irq_masks_cur = TWL6040_ALLINT_MSK; + twl6040->irq_masks_cache = TWL6040_ALLINT_MSK; + twl6040_reg_write(twl6040, TWL6040_REG_INTMR, TWL6040_ALLINT_MSK); + + if (!twl6040->irq) { + dev_warn(twl6040->dev, + "no interrupt specified, no interrupts\n"); + twl6040->irq_base = 0; + return 0; + } + + if (!twl6040->irq_base) { + dev_err(twl6040->dev, + "no interrupt base specified, no interrupts\n"); + return 0; + } + + /* Register them with genirq */ + for (cur_irq = twl6040->irq_base; + cur_irq < twl6040->irq_base + ARRAY_SIZE(twl6040_irqs); + cur_irq++) { + irq_set_chip_data(cur_irq, twl6040); + irq_set_chip_and_handler(cur_irq, &twl6040_irq_chip, + handle_level_irq); + irq_set_nested_thread(cur_irq, 1); + + /* ARM needs us to explicitly flag the IRQ as valid + * and will set them noprobe when we do so. */ +#ifdef CONFIG_ARM + set_irq_flags(cur_irq, IRQF_VALID); +#else + irq_set_noprobe(cur_irq); +#endif + } + + ret = request_threaded_irq(twl6040->irq, NULL, twl6040_irq_thread, + IRQF_ONESHOT, "twl6040", twl6040); + if (ret) { + dev_err(twl6040->dev, "failed to request IRQ %d: %d\n", + twl6040->irq, ret); + return ret; + } + + /* reset interrupts */ + val = twl6040_reg_read(twl6040, TWL6040_REG_INTID); + + /* interrupts cleared on write */ + twl6040_clear_bits(twl6040, TWL6040_REG_ACCCTL, TWL6040_INTCLRMODE); + + return 0; +} +EXPORT_SYMBOL(twl6040_irq_init); + +void twl6040_irq_exit(struct twl6040 *twl6040) +{ + if (twl6040->irq) + free_irq(twl6040->irq, twl6040); +} +EXPORT_SYMBOL(twl6040_irq_exit); diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h index e0aba2b..ea5baa2 100644 --- a/include/linux/i2c/twl.h +++ b/include/linux/i2c/twl.h @@ -679,6 +679,7 @@ struct twl4030_audio_data { /* twl6040 */ int audpwron_gpio; /* audio power-on gpio */ int naudint_irq; /* audio interrupt */ + unsigned int irq_base; }; struct twl4030_platform_data { diff --git a/include/linux/mfd/twl6040.h b/include/linux/mfd/twl6040.h new file mode 100644 index 0000000..c3c1de5 --- /dev/null +++ b/include/linux/mfd/twl6040.h @@ -0,0 +1,255 @@ +/* + * MFD driver for twl6040 + * + * Authors: Jorge Eduardo Candelaria + * Misael Lopez Cruz + * + * Copyright: (C) 2011 Texas Instruments, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __TWL6040_CODEC_H__ +#define __TWL6040_CODEC_H__ + +#include +#include + +#define TWL6040_REG_ASICID 0x01 +#define TWL6040_REG_ASICREV 0x02 +#define TWL6040_REG_INTID 0x03 +#define TWL6040_REG_INTMR 0x04 +#define TWL6040_REG_NCPCTL 0x05 +#define TWL6040_REG_LDOCTL 0x06 +#define TWL6040_REG_HPPLLCTL 0x07 +#define TWL6040_REG_LPPLLCTL 0x08 +#define TWL6040_REG_LPPLLDIV 0x09 +#define TWL6040_REG_AMICBCTL 0x0A +#define TWL6040_REG_DMICBCTL 0x0B +#define TWL6040_REG_MICLCTL 0x0C +#define TWL6040_REG_MICRCTL 0x0D +#define TWL6040_REG_MICGAIN 0x0E +#define TWL6040_REG_LINEGAIN 0x0F +#define TWL6040_REG_HSLCTL 0x10 +#define TWL6040_REG_HSRCTL 0x11 +#define TWL6040_REG_HSGAIN 0x12 +#define TWL6040_REG_EARCTL 0x13 +#define TWL6040_REG_HFLCTL 0x14 +#define TWL6040_REG_HFLGAIN 0x15 +#define TWL6040_REG_HFRCTL 0x16 +#define TWL6040_REG_HFRGAIN 0x17 +#define TWL6040_REG_VIBCTLL 0x18 +#define TWL6040_REG_VIBDATL 0x19 +#define TWL6040_REG_VIBCTLR 0x1A +#define TWL6040_REG_VIBDATR 0x1B +#define TWL6040_REG_HKCTL1 0x1C +#define TWL6040_REG_HKCTL2 0x1D +#define TWL6040_REG_GPOCTL 0x1E +#define TWL6040_REG_ALB 0x1F +#define TWL6040_REG_DLB 0x20 +#define TWL6040_REG_TRIM1 0x28 +#define TWL6040_REG_TRIM2 0x29 +#define TWL6040_REG_TRIM3 0x2A +#define TWL6040_REG_HSOTRIM 0x2B +#define TWL6040_REG_HFOTRIM 0x2C +#define TWL6040_REG_ACCCTL 0x2D +#define TWL6040_REG_STATUS 0x2E + +#define TWL6040_CACHEREGNUM (TWL6040_REG_STATUS + 1) + +#define TWL6040_VIOREGNUM 18 +#define TWL6040_VDDREGNUM 21 + +/* INTID (0x03) fields */ + +#define TWL6040_THINT 0x01 +#define TWL6040_PLUGINT 0x02 +#define TWL6040_UNPLUGINT 0x04 +#define TWL6040_HOOKINT 0x08 +#define TWL6040_HFINT 0x10 +#define TWL6040_VIBINT 0x20 +#define TWL6040_READYINT 0x40 + +/* INTMR (0x04) fields */ + +#define TWL6040_THMSK 0x01 +#define TWL6040_PLUGMSK 0x02 +#define TWL6040_HOOKMSK 0x08 +#define TWL6040_HFMSK 0x10 +#define TWL6040_VIBMSK 0x20 +#define TWL6040_READYMSK 0x40 +#define TWL6040_ALLINT_MSK 0x7B + +/* NCPCTL (0x05) fields */ + +#define TWL6040_NCPENA 0x01 +#define TWL6040_NCPOPEN 0x40 + +/* LDOCTL (0x06) fields */ + +#define TWL6040_LSLDOENA 0x01 +#define TWL6040_HSLDOENA 0x04 +#define TWL6040_REFENA 0x40 +#define TWL6040_OSCENA 0x80 + +/* HPPLLCTL (0x07) fields */ + +#define TWL6040_HPLLENA 0x01 +#define TWL6040_HPLLRST 0x02 +#define TWL6040_HPLLBP 0x04 +#define TWL6040_HPLLSQRENA 0x08 +#define TWL6040_MCLK_12000KHZ (0 << 5) +#define TWL6040_MCLK_19200KHZ (1 << 5) +#define TWL6040_MCLK_26000KHZ (2 << 5) +#define TWL6040_MCLK_38400KHZ (3 << 5) +#define TWL6040_MCLK_MSK 0x60 + +/* LPPLLCTL (0x08) fields */ + +#define TWL6040_LPLLENA 0x01 +#define TWL6040_LPLLRST 0x02 +#define TWL6040_LPLLSEL 0x04 +#define TWL6040_LPLLFIN 0x08 +#define TWL6040_HPLLSEL 0x10 + +/* HSLCTL (0x10) fields */ + +#define TWL6040_HSDACMODEL 0x02 +#define TWL6040_HSDRVMODEL 0x08 + +/* HSRCTL (0x11) fields */ + +#define TWL6040_HSDACMODER 0x02 +#define TWL6040_HSDRVMODER 0x08 + +/* VIBCTLL (0x18) fields */ + +#define TWL6040_VIBENAL 0x01 +#define TWL6040_VIBCTRLL 0x04 +#define TWL6040_VIBCTRLLP 0x08 +#define TWL6040_VIBCTRLLN 0x10 + +/* VIBDATL (0x19) fields */ + +#define TWL6040_VIBDAT_MAX 0x64 + +/* VIBCTLR (0x1A) fields */ + +#define TWL6040_VIBENAR 0x01 +#define TWL6040_VIBCTRLR 0x04 +#define TWL6040_VIBCTRLRP 0x08 +#define TWL6040_VIBCTRLRN 0x10 + +/* GPOCTL (0x1E) fields */ + +#define TWL6040_GPO1 0x01 +#define TWL6040_GPO2 0x02 +#define TWL6040_GPO3 0x03 + +/* ACCCTL (0x2D) fields */ + +#define TWL6040_I2CSEL 0x01 +#define TWL6040_RESETSPLIT 0x04 +#define TWL6040_INTCLRMODE 0x08 + +#define TWL6040_SYSCLK_SEL_LPPLL 1 +#define TWL6040_SYSCLK_SEL_HPPLL 2 + +/* STATUS (0x2E) fields */ + +#define TWL6040_PLUGCOMP 0x02 +#define TWL6040_VIBLOCDET 0x10 +#define TWL6040_VIBROCDET 0x20 +#define TWL6040_TSHUTDET 0x40 + +#define TWL6040_CELLS 2 + +#define TWL6040_REV_ES1_0 0x00 +#define TWL6040_REV_ES1_1 0x01 +#define TWL6040_REV_ES1_2 0x02 + +#define TWL6040_IRQ_TH 0 +#define TWL6040_IRQ_PLUG 1 +#define TWL6040_IRQ_HOOK 2 +#define TWL6040_IRQ_HF 3 +#define TWL6040_IRQ_VIB 4 +#define TWL6040_IRQ_READY 5 + +enum twl6040_pll_id { + TWL6040_NOPLL_ID, + TWL6040_LPPLL_ID, + TWL6040_HPPLL_ID, +}; + +struct twl6040 { + struct device *dev; + struct mutex mutex; + struct mutex io_mutex; + struct mutex irq_mutex; + struct mfd_cell cells[TWL6040_CELLS]; + struct completion ready; + + int audpwron; + int power_count; + int rev; + + enum twl6040_pll_id pll; + unsigned int sysclk; + + unsigned int irq; + unsigned int irq_base; + u8 irq_masks_cur; + u8 irq_masks_cache; +}; + +static inline int twl6040_request_irq(struct twl6040 *twl6040, int irq, + irq_handler_t handler, + unsigned long irqflags, + const char *name, + void *data) +{ + if (!twl6040->irq_base) + return -EINVAL; + + return request_threaded_irq(twl6040->irq_base + irq, NULL, handler, + irqflags, name, data); +} + +static inline void twl6040_free_irq(struct twl6040 *twl6040, int irq, + void *data) +{ + if (!twl6040->irq_base) + return; + + free_irq(twl6040->irq_base + irq, data); +} + +int twl6040_reg_read(struct twl6040 *twl6040, unsigned int reg); +int twl6040_reg_write(struct twl6040 *twl6040, unsigned int reg, + u8 val); +int twl6040_set_bits(struct twl6040 *twl6040, unsigned int reg, + u8 mask); +int twl6040_clear_bits(struct twl6040 *twl6040, unsigned int reg, + u8 mask); +int twl6040_power(struct twl6040 *twl6040, int on); +int twl6040_set_pll(struct twl6040 *twl6040, enum twl6040_pll_id id, + unsigned int freq_in, unsigned int freq_out); +enum twl6040_pll_id twl6040_get_pll(struct twl6040 *twl6040); +unsigned int twl6040_get_sysclk(struct twl6040 *twl6040); +int twl6040_irq_init(struct twl6040 *twl6040); +void twl6040_irq_exit(struct twl6040 *twl6040); + +#endif /* End of __TWL6040_CODEC_H__ */ -- cgit v1.1 From fb34d3d5056a1f8439bbfa13d4519345814d4255 Mon Sep 17 00:00:00 2001 From: Misael Lopez Cruz Date: Sun, 1 May 2011 21:27:00 -0500 Subject: ASoC: twl6040: Convert into TWL6040 MFD child Convert TWL6040 CODEC driver into a TWL6040 MFD child, it implies that MFD-level operations like register accesses, clock setting and power management are done through MFD APIs, not directly by CODEC driver anymore. To avoid conflicts with the other MFD child, vibrator registers are skipped in CODEC driver. Signed-off-by: Misael Lopez Cruz Signed-off-by: Peter Ujfalusi Acked-by: Mark Brown --- sound/soc/codecs/Kconfig | 1 + sound/soc/codecs/twl6040.c | 425 ++++++++++----------------------------------- sound/soc/codecs/twl6040.h | 118 ------------- sound/soc/omap/sdp4430.c | 2 + 4 files changed, 90 insertions(+), 456 deletions(-) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 0002220..922f59f 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -240,6 +240,7 @@ config SND_SOC_TWL4030 tristate config SND_SOC_TWL6040 + select TWL6040_CORE tristate config SND_SOC_UDA134X diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index ade6616..a20e293 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -24,11 +24,10 @@ #include #include #include -#include -#include #include #include #include +#include #include #include @@ -77,14 +76,12 @@ struct twl6040_jack_data { /* codec private data */ struct twl6040_data { - int audpwron; - int naudint; int codec_powered; int pll; int non_lp; + unsigned int clk_in; unsigned int sysclk; struct snd_pcm_hw_constraint_list *sysclk_constraints; - struct completion ready; struct twl6040_jack_data hs_jack; struct snd_soc_codec *codec; struct workqueue_struct *workqueue; @@ -239,12 +236,13 @@ static inline void twl6040_write_reg_cache(struct snd_soc_codec *codec, static int twl6040_read_reg_volatile(struct snd_soc_codec *codec, unsigned int reg) { + struct twl6040 *twl6040 = codec->control_data; u8 value; if (reg >= TWL6040_CACHEREGNUM) return -EIO; - twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &value, reg); + value = twl6040_reg_read(twl6040, reg); twl6040_write_reg_cache(codec, reg, value); return value; @@ -256,11 +254,13 @@ static int twl6040_read_reg_volatile(struct snd_soc_codec *codec, static int twl6040_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { + struct twl6040 *twl6040 = codec->control_data; + if (reg >= TWL6040_CACHEREGNUM) return -EIO; twl6040_write_reg_cache(codec, reg, value); - return twl_i2c_write_u8(TWL_MODULE_AUDIO_VOICE, value, reg); + return twl6040_reg_write(twl6040, reg, value); } static void twl6040_init_vio_regs(struct snd_soc_codec *codec) @@ -268,15 +268,21 @@ static void twl6040_init_vio_regs(struct snd_soc_codec *codec) u8 *cache = codec->reg_cache; int reg, i; - /* allow registers to be accessed by i2c */ - twl6040_write(codec, TWL6040_REG_ACCCTL, cache[TWL6040_REG_ACCCTL]); - for (i = 0; i < TWL6040_VIOREGNUM; i++) { reg = twl6040_vio_reg[i]; - /* skip read-only registers (ASICID, ASICREV, STATUS) */ + /* + * skip read-only registers (ASICID, ASICREV, STATUS) + * and registers shared among MFD children + */ switch (reg) { case TWL6040_REG_ASICID: case TWL6040_REG_ASICREV: + case TWL6040_REG_INTID: + case TWL6040_REG_INTMR: + case TWL6040_REG_NCPCTL: + case TWL6040_REG_LDOCTL: + case TWL6040_REG_GPOCTL: + case TWL6040_REG_ACCCTL: case TWL6040_REG_STATUS: continue; default: @@ -293,6 +299,20 @@ static void twl6040_init_vdd_regs(struct snd_soc_codec *codec) for (i = 0; i < TWL6040_VDDREGNUM; i++) { reg = twl6040_vdd_reg[i]; + /* skip vibra and PLL registers */ + switch (reg) { + case TWL6040_REG_VIBCTLL: + case TWL6040_REG_VIBDATL: + case TWL6040_REG_VIBCTLR: + case TWL6040_REG_VIBDATR: + case TWL6040_REG_HPPLLCTL: + case TWL6040_REG_LPPLLCTL: + case TWL6040_REG_LPPLLDIV: + continue; + default: + break; + } + twl6040_write(codec, reg, cache[reg]); } } @@ -596,88 +616,6 @@ static int pga_event(struct snd_soc_dapm_widget *w, return 0; } -/* twl6040 codec manual power-up sequence */ -static void twl6040_power_up(struct snd_soc_codec *codec) -{ - u8 ncpctl, ldoctl, lppllctl, accctl; - - ncpctl = twl6040_read_reg_cache(codec, TWL6040_REG_NCPCTL); - ldoctl = twl6040_read_reg_cache(codec, TWL6040_REG_LDOCTL); - lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL); - accctl = twl6040_read_reg_cache(codec, TWL6040_REG_ACCCTL); - - /* enable reference system */ - ldoctl |= TWL6040_REFENA; - twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); - msleep(10); - /* enable internal oscillator */ - ldoctl |= TWL6040_OSCENA; - twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); - udelay(10); - /* enable high-side ldo */ - ldoctl |= TWL6040_HSLDOENA; - twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); - udelay(244); - /* enable negative charge pump */ - ncpctl |= TWL6040_NCPENA | TWL6040_NCPOPEN; - twl6040_write(codec, TWL6040_REG_NCPCTL, ncpctl); - udelay(488); - /* enable low-side ldo */ - ldoctl |= TWL6040_LSLDOENA; - twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); - udelay(244); - /* enable low-power pll */ - lppllctl |= TWL6040_LPLLENA; - twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); - /* reset state machine */ - accctl |= TWL6040_RESETSPLIT; - twl6040_write(codec, TWL6040_REG_ACCCTL, accctl); - mdelay(5); - accctl &= ~TWL6040_RESETSPLIT; - twl6040_write(codec, TWL6040_REG_ACCCTL, accctl); - /* disable internal oscillator */ - ldoctl &= ~TWL6040_OSCENA; - twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); -} - -/* twl6040 codec manual power-down sequence */ -static void twl6040_power_down(struct snd_soc_codec *codec) -{ - u8 ncpctl, ldoctl, lppllctl, accctl; - - ncpctl = twl6040_read_reg_cache(codec, TWL6040_REG_NCPCTL); - ldoctl = twl6040_read_reg_cache(codec, TWL6040_REG_LDOCTL); - lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL); - accctl = twl6040_read_reg_cache(codec, TWL6040_REG_ACCCTL); - - /* enable internal oscillator */ - ldoctl |= TWL6040_OSCENA; - twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); - udelay(10); - /* disable low-power pll */ - lppllctl &= ~TWL6040_LPLLENA; - twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); - /* disable low-side ldo */ - ldoctl &= ~TWL6040_LSLDOENA; - twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); - udelay(244); - /* disable negative charge pump */ - ncpctl &= ~(TWL6040_NCPENA | TWL6040_NCPOPEN); - twl6040_write(codec, TWL6040_REG_NCPCTL, ncpctl); - udelay(488); - /* disable high-side ldo */ - ldoctl &= ~TWL6040_HSLDOENA; - twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); - udelay(244); - /* disable internal oscillator */ - ldoctl &= ~TWL6040_OSCENA; - twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); - /* disable reference system */ - ldoctl &= ~TWL6040_REFENA; - twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); - msleep(10); -} - /* set headset dac and driver power mode */ static int headset_power_mode(struct snd_soc_codec *codec, int high_perf) { @@ -766,33 +704,19 @@ static void twl6040_accessory_work(struct work_struct *work) } /* audio interrupt handler */ -static irqreturn_t twl6040_naudint_handler(int irq, void *data) +static irqreturn_t twl6040_audio_handler(int irq, void *data) { struct snd_soc_codec *codec = data; + struct twl6040 *twl6040 = codec->control_data; struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); u8 intid; - twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &intid, TWL6040_REG_INTID); - - if (intid & TWL6040_THINT) - dev_alert(codec->dev, "die temp over-limit detection\n"); + intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID); if ((intid & TWL6040_PLUGINT) || (intid & TWL6040_UNPLUGINT)) queue_delayed_work(priv->workqueue, &priv->delayed_work, msecs_to_jiffies(200)); - if (intid & TWL6040_HOOKINT) - dev_info(codec->dev, "hook detection\n"); - - if (intid & TWL6040_HFINT) - dev_alert(codec->dev, "hf drivers over current detection\n"); - - if (intid & TWL6040_VIBINT) - dev_alert(codec->dev, "vib drivers over current detection\n"); - - if (intid & TWL6040_READYINT) - complete(&priv->ready); - return IRQ_HANDLED; } @@ -1231,36 +1155,11 @@ static int twl6040_add_widgets(struct snd_soc_codec *codec) return 0; } -static int twl6040_power_up_completion(struct snd_soc_codec *codec, - int naudint) -{ - struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); - int time_left; - u8 intid; - - time_left = wait_for_completion_timeout(&priv->ready, - msecs_to_jiffies(144)); - - if (!time_left) { - twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &intid, - TWL6040_REG_INTID); - if (!(intid & TWL6040_READYINT)) { - dev_err(codec->dev, "timeout waiting for READYINT\n"); - return -ETIMEDOUT; - } - } - - priv->codec_powered = 1; - - return 0; -} - static int twl6040_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { + struct twl6040 *twl6040 = codec->control_data; struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); - int audpwron = priv->audpwron; - int naudint = priv->naudint; int ret; switch (level) { @@ -1272,62 +1171,30 @@ static int twl6040_set_bias_level(struct snd_soc_codec *codec, if (priv->codec_powered) break; - if (gpio_is_valid(audpwron)) { - /* use AUDPWRON line */ - gpio_set_value(audpwron, 1); - - /* wait for power-up completion */ - ret = twl6040_power_up_completion(codec, naudint); - if (ret) - return ret; + ret = twl6040_power(twl6040, 1); + if (ret) + return ret; - /* sync registers updated during power-up sequence */ - twl6040_read_reg_volatile(codec, TWL6040_REG_NCPCTL); - twl6040_read_reg_volatile(codec, TWL6040_REG_LDOCTL); - twl6040_read_reg_volatile(codec, TWL6040_REG_LPPLLCTL); - } else { - /* use manual power-up sequence */ - twl6040_power_up(codec); - priv->codec_powered = 1; - } + priv->codec_powered = 1; /* initialize vdd/vss registers with reg_cache */ twl6040_init_vdd_regs(codec); /* Set external boost GPO */ twl6040_write(codec, TWL6040_REG_GPOCTL, 0x02); - - /* Set initial minimal gain values */ - twl6040_write(codec, TWL6040_REG_HSGAIN, 0xFF); - twl6040_write(codec, TWL6040_REG_EARCTL, 0x1E); - twl6040_write(codec, TWL6040_REG_HFLGAIN, 0x1D); - twl6040_write(codec, TWL6040_REG_HFRGAIN, 0x1D); break; case SND_SOC_BIAS_OFF: if (!priv->codec_powered) break; - if (gpio_is_valid(audpwron)) { - /* use AUDPWRON line */ - gpio_set_value(audpwron, 0); - - /* power-down sequence latency */ - udelay(500); - - /* sync registers updated during power-down sequence */ - twl6040_read_reg_volatile(codec, TWL6040_REG_NCPCTL); - twl6040_read_reg_volatile(codec, TWL6040_REG_LDOCTL); - twl6040_write_reg_cache(codec, TWL6040_REG_LPPLLCTL, - 0x00); - } else { - /* use manual power-down sequence */ - twl6040_power_down(codec); - } - + twl6040_power(twl6040, 0); priv->codec_powered = 0; break; } + /* get PLL and sysclk after power transition */ + priv->pll = twl6040_get_pll(twl6040); + priv->sysclk = twl6040_get_sysclk(twl6040); codec->dapm.bias_level = level; return 0; @@ -1374,39 +1241,40 @@ static int twl6040_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_codec *codec = rtd->codec; + struct twl6040 *twl6040 = codec->control_data; struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); - u8 lppllctl; - int rate; + unsigned int sysclk; + int rate, ret; /* nothing to do for high-perf pll, it supports only 48 kHz */ if (priv->pll == TWL6040_HPPLL_ID) return 0; - lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL); - rate = params_rate(params); switch (rate) { case 11250: case 22500: case 44100: case 88200: - lppllctl |= TWL6040_LPLLFIN; - priv->sysclk = 17640000; + sysclk = 17640000; break; case 8000: case 16000: case 32000: case 48000: case 96000: - lppllctl &= ~TWL6040_LPLLFIN; - priv->sysclk = 19200000; + sysclk = 19200000; break; default: dev_err(codec->dev, "unsupported rate %d\n", rate); return -EINVAL; } - twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); + ret = twl6040_set_pll(twl6040, TWL6040_LPPLL_ID, priv->clk_in, sysclk); + if (ret) + return ret; + + priv->sysclk = twl6040_get_sysclk(twl6040); return 0; } @@ -1449,99 +1317,27 @@ static int twl6040_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, unsigned int freq, int dir) { struct snd_soc_codec *codec = codec_dai->codec; + struct twl6040 *twl6040 = codec->control_data; struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); - u8 hppllctl, lppllctl; - - hppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_HPPLLCTL); - lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL); + int ret = 0; switch (clk_id) { case TWL6040_SYSCLK_SEL_LPPLL: - switch (freq) { - case 32768: - /* headset dac and driver must be in low-power mode */ - headset_power_mode(codec, 0); - - /* clk32k input requires low-power pll */ - lppllctl |= TWL6040_LPLLENA; - twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); - mdelay(5); - lppllctl &= ~TWL6040_HPLLSEL; - twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); - hppllctl &= ~TWL6040_HPLLENA; - twl6040_write(codec, TWL6040_REG_HPPLLCTL, hppllctl); - break; - default: - dev_err(codec->dev, "unknown mclk freq %d\n", freq); - return -EINVAL; - } - - /* lppll divider */ - switch (priv->sysclk) { - case 17640000: - lppllctl |= TWL6040_LPLLFIN; - break; - case 19200000: - lppllctl &= ~TWL6040_LPLLFIN; - break; - default: - /* sysclk not yet configured */ - lppllctl &= ~TWL6040_LPLLFIN; - priv->sysclk = 19200000; - break; - } - - twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); + ret = twl6040_set_pll(twl6040, TWL6040_LPPLL_ID, + freq, priv->sysclk); + if (ret) + return ret; - priv->pll = TWL6040_LPPLL_ID; + headset_power_mode(codec, 0); priv->sysclk_constraints = &lp_constraints; break; case TWL6040_SYSCLK_SEL_HPPLL: - hppllctl &= ~TWL6040_MCLK_MSK; - - switch (freq) { - case 12000000: - /* mclk input, pll enabled */ - hppllctl |= TWL6040_MCLK_12000KHZ | - TWL6040_HPLLSQRBP | - TWL6040_HPLLENA; - break; - case 19200000: - /* mclk input, pll disabled */ - hppllctl |= TWL6040_MCLK_19200KHZ | - TWL6040_HPLLSQRENA | - TWL6040_HPLLBP; - break; - case 26000000: - /* mclk input, pll enabled */ - hppllctl |= TWL6040_MCLK_26000KHZ | - TWL6040_HPLLSQRBP | - TWL6040_HPLLENA; - break; - case 38400000: - /* clk slicer, pll disabled */ - hppllctl |= TWL6040_MCLK_38400KHZ | - TWL6040_HPLLSQRENA | - TWL6040_HPLLBP; - break; - default: - dev_err(codec->dev, "unknown mclk freq %d\n", freq); - return -EINVAL; - } + ret = twl6040_set_pll(twl6040, TWL6040_HPPLL_ID, + freq, priv->sysclk); + if (ret) + return ret; - /* headset dac and driver must be in high-performance mode */ headset_power_mode(codec, 1); - - twl6040_write(codec, TWL6040_REG_HPPLLCTL, hppllctl); - udelay(500); - lppllctl |= TWL6040_HPLLSEL; - twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); - lppllctl &= ~TWL6040_LPLLENA; - twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); - - /* high-performance pll can provide only 19.2 MHz */ - priv->pll = TWL6040_HPPLL_ID; - priv->sysclk = 19200000; priv->sysclk_constraints = &hp_constraints; break; default: @@ -1549,6 +1345,10 @@ static int twl6040_set_dai_sysclk(struct snd_soc_dai *codec_dai, return -EINVAL; } + priv->pll = twl6040_get_pll(twl6040); + priv->clk_in = freq; + priv->sysclk = twl6040_get_sysclk(twl6040); + return 0; } @@ -1600,11 +1400,8 @@ static int twl6040_resume(struct snd_soc_codec *codec) static int twl6040_probe(struct snd_soc_codec *codec) { - struct twl4030_audio_data *twl_codec = codec->dev->platform_data; struct twl6040_data *priv; - int audpwron, naudint; int ret = 0; - u8 icrev, intmr = TWL6040_ALLINT_MSK; priv = kzalloc(sizeof(struct twl6040_data), GFP_KERNEL); if (priv == NULL) @@ -1612,23 +1409,9 @@ static int twl6040_probe(struct snd_soc_codec *codec) snd_soc_codec_set_drvdata(codec, priv); priv->codec = codec; + codec->control_data = dev_get_drvdata(codec->dev->parent); - twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &icrev, TWL6040_REG_ASICREV); - - if (twl_codec && (icrev > 0)) - audpwron = twl_codec->audpwron_gpio; - else - audpwron = -EINVAL; - - if (twl_codec) - naudint = twl_codec->naudint_irq; - else - naudint = 0; - - priv->audpwron = audpwron; - priv->naudint = naudint; priv->workqueue = create_singlethread_workqueue("twl6040-codec"); - if (!priv->workqueue) { ret = -ENOMEM; goto work_err; @@ -1638,56 +1421,34 @@ static int twl6040_probe(struct snd_soc_codec *codec) mutex_init(&priv->mutex); - init_completion(&priv->ready); init_completion(&priv->headset.ramp_done); init_completion(&priv->handsfree.ramp_done); - if (gpio_is_valid(audpwron)) { - ret = gpio_request(audpwron, "audpwron"); - if (ret) - goto gpio1_err; - - ret = gpio_direction_output(audpwron, 0); - if (ret) - goto gpio2_err; - - priv->codec_powered = 0; - - /* enable only codec ready interrupt */ - intmr &= ~(TWL6040_READYMSK | TWL6040_PLUGMSK); - - /* reset interrupt status to allow correct power up sequence */ - twl6040_read_reg_volatile(codec, TWL6040_REG_INTID); - } - twl6040_write(codec, TWL6040_REG_INTMR, intmr); - - if (naudint) { - /* audio interrupt */ - ret = request_threaded_irq(naudint, NULL, - twl6040_naudint_handler, - IRQF_TRIGGER_LOW | IRQF_ONESHOT, - "twl6040_codec", codec); - if (ret) - goto gpio2_err; - } - - /* init vio registers */ - twl6040_init_vio_regs(codec); - priv->hf_workqueue = create_singlethread_workqueue("twl6040-hf"); if (priv->hf_workqueue == NULL) { ret = -ENOMEM; - goto irq_err; + goto hfwq_err; } priv->hs_workqueue = create_singlethread_workqueue("twl6040-hs"); if (priv->hs_workqueue == NULL) { ret = -ENOMEM; - goto wq_err; + goto hswq_err; } INIT_DELAYED_WORK(&priv->hs_delayed_work, twl6040_pga_hs_work); INIT_DELAYED_WORK(&priv->hf_delayed_work, twl6040_pga_hf_work); + ret = twl6040_request_irq(codec->control_data, TWL6040_IRQ_PLUG, + twl6040_audio_handler, 0, + "twl6040_irq_plug", codec); + if (ret) { + dev_err(codec->dev, "PLUG IRQ request failed: %d\n", ret); + goto plugirq_err; + } + + /* init vio registers */ + twl6040_init_vio_regs(codec); + /* power on device */ ret = twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY); if (ret) @@ -1700,16 +1461,12 @@ static int twl6040_probe(struct snd_soc_codec *codec) return 0; bias_err: + twl6040_free_irq(codec->control_data, TWL6040_IRQ_PLUG, codec); +plugirq_err: destroy_workqueue(priv->hs_workqueue); -wq_err: +hswq_err: destroy_workqueue(priv->hf_workqueue); -irq_err: - if (naudint) - free_irq(naudint, codec); -gpio2_err: - if (gpio_is_valid(audpwron)) - gpio_free(audpwron); -gpio1_err: +hfwq_err: destroy_workqueue(priv->workqueue); work_err: kfree(priv); @@ -1719,17 +1476,9 @@ work_err: static int twl6040_remove(struct snd_soc_codec *codec) { struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); - int audpwron = priv->audpwron; - int naudint = priv->naudint; twl6040_set_bias_level(codec, SND_SOC_BIAS_OFF); - - if (gpio_is_valid(audpwron)) - gpio_free(audpwron); - - if (naudint) - free_irq(naudint, codec); - + twl6040_free_irq(codec->control_data, TWL6040_IRQ_PLUG, codec); destroy_workqueue(priv->workqueue); destroy_workqueue(priv->hf_workqueue); destroy_workqueue(priv->hs_workqueue); diff --git a/sound/soc/codecs/twl6040.h b/sound/soc/codecs/twl6040.h index 23aeed0..234bfad 100644 --- a/sound/soc/codecs/twl6040.h +++ b/sound/soc/codecs/twl6040.h @@ -22,124 +22,6 @@ #ifndef __TWL6040_H__ #define __TWL6040_H__ -#define TWL6040_REG_ASICID 0x01 -#define TWL6040_REG_ASICREV 0x02 -#define TWL6040_REG_INTID 0x03 -#define TWL6040_REG_INTMR 0x04 -#define TWL6040_REG_NCPCTL 0x05 -#define TWL6040_REG_LDOCTL 0x06 -#define TWL6040_REG_HPPLLCTL 0x07 -#define TWL6040_REG_LPPLLCTL 0x08 -#define TWL6040_REG_LPPLLDIV 0x09 -#define TWL6040_REG_AMICBCTL 0x0A -#define TWL6040_REG_DMICBCTL 0x0B -#define TWL6040_REG_MICLCTL 0x0C -#define TWL6040_REG_MICRCTL 0x0D -#define TWL6040_REG_MICGAIN 0x0E -#define TWL6040_REG_LINEGAIN 0x0F -#define TWL6040_REG_HSLCTL 0x10 -#define TWL6040_REG_HSRCTL 0x11 -#define TWL6040_REG_HSGAIN 0x12 -#define TWL6040_REG_EARCTL 0x13 -#define TWL6040_REG_HFLCTL 0x14 -#define TWL6040_REG_HFLGAIN 0x15 -#define TWL6040_REG_HFRCTL 0x16 -#define TWL6040_REG_HFRGAIN 0x17 -#define TWL6040_REG_VIBCTLL 0x18 -#define TWL6040_REG_VIBDATL 0x19 -#define TWL6040_REG_VIBCTLR 0x1A -#define TWL6040_REG_VIBDATR 0x1B -#define TWL6040_REG_HKCTL1 0x1C -#define TWL6040_REG_HKCTL2 0x1D -#define TWL6040_REG_GPOCTL 0x1E -#define TWL6040_REG_ALB 0x1F -#define TWL6040_REG_DLB 0x20 -#define TWL6040_REG_TRIM1 0x28 -#define TWL6040_REG_TRIM2 0x29 -#define TWL6040_REG_TRIM3 0x2A -#define TWL6040_REG_HSOTRIM 0x2B -#define TWL6040_REG_HFOTRIM 0x2C -#define TWL6040_REG_ACCCTL 0x2D -#define TWL6040_REG_STATUS 0x2E - -#define TWL6040_CACHEREGNUM (TWL6040_REG_STATUS + 1) - -#define TWL6040_VIOREGNUM 18 -#define TWL6040_VDDREGNUM 21 - -/* INTID (0x03) fields */ - -#define TWL6040_THINT 0x01 -#define TWL6040_PLUGINT 0x02 -#define TWL6040_UNPLUGINT 0x04 -#define TWL6040_HOOKINT 0x08 -#define TWL6040_HFINT 0x10 -#define TWL6040_VIBINT 0x20 -#define TWL6040_READYINT 0x40 - -/* INTMR (0x04) fields */ - -#define TWL6040_PLUGMSK 0x02 -#define TWL6040_READYMSK 0x40 -#define TWL6040_ALLINT_MSK 0x7B - -/* NCPCTL (0x05) fields */ - -#define TWL6040_NCPENA 0x01 -#define TWL6040_NCPOPEN 0x40 - -/* LDOCTL (0x06) fields */ - -#define TWL6040_LSLDOENA 0x01 -#define TWL6040_HSLDOENA 0x04 -#define TWL6040_REFENA 0x40 -#define TWL6040_OSCENA 0x80 - -/* HPPLLCTL (0x07) fields */ - -#define TWL6040_HPLLENA 0x01 -#define TWL6040_HPLLRST 0x02 -#define TWL6040_HPLLBP 0x04 -#define TWL6040_HPLLSQRENA 0x08 -#define TWL6040_HPLLSQRBP 0x10 -#define TWL6040_MCLK_12000KHZ (0 << 5) -#define TWL6040_MCLK_19200KHZ (1 << 5) -#define TWL6040_MCLK_26000KHZ (2 << 5) -#define TWL6040_MCLK_38400KHZ (3 << 5) -#define TWL6040_MCLK_MSK 0x60 - -/* LPPLLCTL (0x08) fields */ - -#define TWL6040_LPLLENA 0x01 -#define TWL6040_LPLLRST 0x02 -#define TWL6040_LPLLSEL 0x04 -#define TWL6040_LPLLFIN 0x08 -#define TWL6040_HPLLSEL 0x10 - -/* HSLCTL (0x10) fields */ - -#define TWL6040_HSDACMODEL 0x02 -#define TWL6040_HSDRVMODEL 0x08 - -/* HSRCTL (0x11) fields */ - -#define TWL6040_HSDACMODER 0x02 -#define TWL6040_HSDRVMODER 0x08 - -/* ACCCTL (0x2D) fields */ - -#define TWL6040_RESETSPLIT 0x04 - -#define TWL6040_SYSCLK_SEL_LPPLL 1 -#define TWL6040_SYSCLK_SEL_HPPLL 2 - -#define TWL6040_HPPLL_ID 1 -#define TWL6040_LPPLL_ID 2 - -/* STATUS (0x2E) fields */ - -#define TWL6040_PLUGCOMP 0x02 - void twl6040_hs_jack_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, int report); diff --git a/sound/soc/omap/sdp4430.c b/sound/soc/omap/sdp4430.c index 189e039..5d67c25 100644 --- a/sound/soc/omap/sdp4430.c +++ b/sound/soc/omap/sdp4430.c @@ -21,6 +21,8 @@ #include #include +#include + #include #include #include -- cgit v1.1 From 6c44863763254eaf96ec7d4f816fb88605301e76 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 1 Jun 2011 13:05:10 +0300 Subject: MFD: twl6040: Change platform data for soc codec driver Pass twl4030_codec_data instead of the twl4030_audio_data for the ASoC codec driver. Signed-off-by: Peter Ujfalusi Acked-by: Samuel Ortiz --- drivers/mfd/twl6040-core.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/mfd/twl6040-core.c b/drivers/mfd/twl6040-core.c index 57ae36b..cfaedb5 100644 --- a/drivers/mfd/twl6040-core.c +++ b/drivers/mfd/twl6040-core.c @@ -501,9 +501,8 @@ static int __devinit twl6040_probe(struct platform_device *pdev) if (pdata->codec) { cell = &twl6040->cells[children]; cell->name = "twl6040-codec"; - /* The codec expects the twl4030_audio_data as platform data */ - cell->platform_data = pdata; - cell->pdata_size = sizeof(*pdata); + cell->platform_data = pdata->codec; + cell->pdata_size = sizeof(*pdata->codec); children++; } -- cgit v1.1 From cc697d38392c92b7504e7719c65ae905f0a0618a Mon Sep 17 00:00:00 2001 From: Misael Lopez Cruz Date: Sun, 1 May 2011 03:51:24 -0500 Subject: input: Add initial support for TWL6040 vibrator Add twl6040_vibra as a child of MFD device twl6040_codec. This implementation covers the PCM-to-PWM mode of TWL6040 vibrator module. Signed-off-by: Misael Lopez Cruz Signed-off-by: Peter Ujfalusi CC: Tejun Heo Acked-by: Dmitry Torokhov --- drivers/input/misc/Kconfig | 11 + drivers/input/misc/Makefile | 1 + drivers/input/misc/twl6040-vibra.c | 416 +++++++++++++++++++++++++++++++++++++ include/linux/i2c/twl.h | 8 + 4 files changed, 436 insertions(+) create mode 100644 drivers/input/misc/twl6040-vibra.c diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 077309a..d1bf872 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -275,6 +275,17 @@ config INPUT_TWL4030_VIBRA To compile this driver as a module, choose M here. The module will be called twl4030_vibra. +config INPUT_TWL6040_VIBRA + tristate "Support for TWL6040 Vibrator" + depends on TWL4030_CORE + select TWL6040_CORE + select INPUT_FF_MEMLESS + help + This option enables support for TWL6040 Vibrator Driver. + + To compile this driver as a module, choose M here. The module will + be called twl6040_vibra. + config INPUT_UINPUT tristate "User level driver support" help diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 38efb2c..4da7c3a 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o obj-$(CONFIG_INPUT_TWL4030_VIBRA) += twl4030-vibra.o +obj-$(CONFIG_INPUT_TWL6040_VIBRA) += twl6040-vibra.o obj-$(CONFIG_INPUT_UINPUT) += uinput.o obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o diff --git a/drivers/input/misc/twl6040-vibra.c b/drivers/input/misc/twl6040-vibra.c new file mode 100644 index 0000000..dbf745d --- /dev/null +++ b/drivers/input/misc/twl6040-vibra.c @@ -0,0 +1,416 @@ +/* + * twl6040-vibra.c - TWL6040 Vibrator driver + * + * Author: Jorge Eduardo Candelaria + * Author: Misael Lopez Cruz + * + * Copyright: (C) 2011 Texas Instruments, Inc. + * + * Based on twl4030-vibra.c by Henrik Saari + * Felipe Balbi + * Jari Vanhala + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define EFFECT_DIR_180_DEG 0x8000 + +/* Recommended modulation index 85% */ +#define TWL6040_VIBRA_MOD 85 + +#define TWL6040_NUM_SUPPLIES 2 + +struct vibra_info { + struct device *dev; + struct input_dev *input_dev; + struct workqueue_struct *workqueue; + struct work_struct play_work; + struct mutex mutex; + + bool enabled; + int weak_speed; + int strong_speed; + int direction; + + unsigned int vibldrv_res; + unsigned int vibrdrv_res; + unsigned int viblmotor_res; + unsigned int vibrmotor_res; + + struct regulator_bulk_data supplies[TWL6040_NUM_SUPPLIES]; + + struct twl6040 *twl6040; +}; + +static irqreturn_t twl6040_vib_irq_handler(int irq, void *data) +{ + struct vibra_info *info = data; + struct twl6040 *twl6040 = info->twl6040; + u8 status; + + status = twl6040_reg_read(twl6040, TWL6040_REG_STATUS); + if (status & TWL6040_VIBLOCDET) { + dev_warn(info->dev, "Left Vibrator overcurrent detected\n"); + twl6040_clear_bits(twl6040, TWL6040_REG_VIBCTLL, + TWL6040_VIBENAL); + } + if (status & TWL6040_VIBROCDET) { + dev_warn(info->dev, "Right Vibrator overcurrent detected\n"); + twl6040_clear_bits(twl6040, TWL6040_REG_VIBCTLR, + TWL6040_VIBENAR); + } + + return IRQ_HANDLED; +} + +static void twl6040_vibra_enable(struct vibra_info *info) +{ + struct twl6040 *twl6040 = info->twl6040; + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(info->supplies), info->supplies); + if (ret) { + dev_err(info->dev, "failed to enable regulators %d\n", ret); + return; + } + + twl6040_power(info->twl6040, 1); + if (twl6040->rev <= TWL6040_REV_ES1_1) { + /* + * ERRATA: Disable overcurrent protection for at least + * 3ms when enabling vibrator drivers to avoid false + * overcurrent detection + */ + twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLL, + TWL6040_VIBENAL | TWL6040_VIBCTRLL); + twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLR, + TWL6040_VIBENAR | TWL6040_VIBCTRLR); + usleep_range(3000, 3500); + } + + twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLL, + TWL6040_VIBENAL); + twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLR, + TWL6040_VIBENAR); + + info->enabled = true; +} + +static void twl6040_vibra_disable(struct vibra_info *info) +{ + struct twl6040 *twl6040 = info->twl6040; + + twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLL, 0x00); + twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLR, 0x00); + twl6040_power(info->twl6040, 0); + + regulator_bulk_disable(ARRAY_SIZE(info->supplies), info->supplies); + + info->enabled = false; +} + +static u8 twl6040_vibra_code(int vddvib, int vibdrv_res, int motor_res, + int speed, int direction) +{ + int vpk, max_code; + u8 vibdat; + + /* output swing */ + vpk = (vddvib * motor_res * TWL6040_VIBRA_MOD) / + (100 * (vibdrv_res + motor_res)); + + /* 50mV per VIBDAT code step */ + max_code = vpk / 50; + if (max_code > TWL6040_VIBDAT_MAX) + max_code = TWL6040_VIBDAT_MAX; + + /* scale speed to max allowed code */ + vibdat = (u8)((speed * max_code) / USHRT_MAX); + + /* 2's complement for direction > 180 degrees */ + vibdat *= direction; + + return vibdat; +} + +static void twl6040_vibra_set_effect(struct vibra_info *info) +{ + struct twl6040 *twl6040 = info->twl6040; + u8 vibdatl, vibdatr; + int volt; + + /* weak motor */ + volt = regulator_get_voltage(info->supplies[0].consumer) / 1000; + vibdatl = twl6040_vibra_code(volt, info->vibldrv_res, + info->viblmotor_res, + info->weak_speed, info->direction); + + /* strong motor */ + volt = regulator_get_voltage(info->supplies[1].consumer) / 1000; + vibdatr = twl6040_vibra_code(volt, info->vibrdrv_res, + info->vibrmotor_res, + info->strong_speed, info->direction); + + twl6040_reg_write(twl6040, TWL6040_REG_VIBDATL, vibdatl); + twl6040_reg_write(twl6040, TWL6040_REG_VIBDATR, vibdatr); +} + +static void vibra_play_work(struct work_struct *work) +{ + struct vibra_info *info = container_of(work, + struct vibra_info, play_work); + + mutex_lock(&info->mutex); + + if (info->weak_speed || info->strong_speed) { + if (!info->enabled) + twl6040_vibra_enable(info); + + twl6040_vibra_set_effect(info); + } else if (info->enabled) + twl6040_vibra_disable(info); + + mutex_unlock(&info->mutex); +} + +static int vibra_play(struct input_dev *input, void *data, + struct ff_effect *effect) +{ + struct vibra_info *info = input_get_drvdata(input); + int ret; + + info->weak_speed = effect->u.rumble.weak_magnitude; + info->strong_speed = effect->u.rumble.strong_magnitude; + info->direction = effect->direction < EFFECT_DIR_180_DEG ? 1 : -1; + + ret = queue_work(info->workqueue, &info->play_work); + if (!ret) { + dev_info(&input->dev, "work is already on queue\n"); + return ret; + } + + return 0; +} + +static void twl6040_vibra_close(struct input_dev *input) +{ + struct vibra_info *info = input_get_drvdata(input); + + cancel_work_sync(&info->play_work); + + mutex_lock(&info->mutex); + + if (info->enabled) + twl6040_vibra_disable(info); + + mutex_unlock(&info->mutex); +} + +#if CONFIG_PM_SLEEP +static int twl6040_vibra_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct vibra_info *info = platform_get_drvdata(pdev); + + mutex_lock(&info->mutex); + + if (info->enabled) + twl6040_vibra_disable(info); + + mutex_unlock(&info->mutex); + + return 0; +} + +#endif + +static SIMPLE_DEV_PM_OPS(twl6040_vibra_pm_ops, twl6040_vibra_suspend, NULL); + +static int __devinit twl6040_vibra_probe(struct platform_device *pdev) +{ + struct twl4030_vibra_data *pdata = pdev->dev.platform_data; + struct vibra_info *info; + int ret; + + if (!pdata) { + dev_err(&pdev->dev, "platform_data not available\n"); + return -EINVAL; + } + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + dev_err(&pdev->dev, "couldn't allocate memory\n"); + return -ENOMEM; + } + + info->dev = &pdev->dev; + info->twl6040 = dev_get_drvdata(pdev->dev.parent); + info->vibldrv_res = pdata->vibldrv_res; + info->vibrdrv_res = pdata->vibrdrv_res; + info->viblmotor_res = pdata->viblmotor_res; + info->vibrmotor_res = pdata->vibrmotor_res; + if ((!info->vibldrv_res && !info->viblmotor_res) || + (!info->vibrdrv_res && !info->vibrmotor_res)) { + dev_err(info->dev, "invalid vibra driver/motor resistance\n"); + ret = -EINVAL; + goto err_kzalloc; + } + + mutex_init(&info->mutex); + + info->input_dev = input_allocate_device(); + if (info->input_dev == NULL) { + dev_err(info->dev, "couldn't allocate input device\n"); + ret = -ENOMEM; + goto err_kzalloc; + } + + input_set_drvdata(info->input_dev, info); + + info->input_dev->name = "twl6040:vibrator"; + info->input_dev->id.version = 1; + info->input_dev->dev.parent = pdev->dev.parent; + info->input_dev->close = twl6040_vibra_close; + __set_bit(FF_RUMBLE, info->input_dev->ffbit); + + ret = input_ff_create_memless(info->input_dev, NULL, vibra_play); + if (ret < 0) { + dev_err(info->dev, "couldn't register vibrator to FF\n"); + goto err_ialloc; + } + + ret = input_register_device(info->input_dev); + if (ret < 0) { + dev_err(info->dev, "couldn't register input device\n"); + goto err_iff; + } + + platform_set_drvdata(pdev, info); + + ret = twl6040_request_irq(info->twl6040, TWL6040_IRQ_VIB, + twl6040_vib_irq_handler, 0, + "twl6040_irq_vib", info); + if (ret) { + dev_err(info->dev, "VIB IRQ request failed: %d\n", ret); + goto err_irq; + } + + info->supplies[0].supply = "vddvibl"; + info->supplies[1].supply = "vddvibr"; + ret = regulator_bulk_get(info->dev, ARRAY_SIZE(info->supplies), + info->supplies); + if (ret) { + dev_err(info->dev, "couldn't get regulators %d\n", ret); + goto err_regulator; + } + + if (pdata->vddvibl_uV) { + ret = regulator_set_voltage(info->supplies[0].consumer, + pdata->vddvibl_uV, + pdata->vddvibl_uV); + if (ret) { + dev_err(info->dev, "failed to set VDDVIBL volt %d\n", + ret); + goto err_voltage; + } + } + + if (pdata->vddvibr_uV) { + ret = regulator_set_voltage(info->supplies[1].consumer, + pdata->vddvibr_uV, + pdata->vddvibr_uV); + if (ret) { + dev_err(info->dev, "failed to set VDDVIBR volt %d\n", + ret); + goto err_voltage; + } + } + + info->workqueue = alloc_workqueue("twl6040-vibra", 0, 0); + if (info->workqueue == NULL) { + dev_err(info->dev, "couldn't create workqueue\n"); + ret = -ENOMEM; + goto err_voltage; + } + INIT_WORK(&info->play_work, vibra_play_work); + + return 0; + +err_voltage: + regulator_bulk_free(ARRAY_SIZE(info->supplies), info->supplies); +err_regulator: + twl6040_free_irq(info->twl6040, TWL6040_IRQ_VIB, info); +err_irq: + input_unregister_device(info->input_dev); + info->input_dev = NULL; +err_iff: + if (info->input_dev) + input_ff_destroy(info->input_dev); +err_ialloc: + input_free_device(info->input_dev); +err_kzalloc: + kfree(info); + return ret; +} + +static int __devexit twl6040_vibra_remove(struct platform_device *pdev) +{ + struct vibra_info *info = platform_get_drvdata(pdev); + + input_unregister_device(info->input_dev); + twl6040_free_irq(info->twl6040, TWL6040_IRQ_VIB, info); + regulator_bulk_free(ARRAY_SIZE(info->supplies), info->supplies); + destroy_workqueue(info->workqueue); + kfree(info); + + return 0; +} + +static struct platform_driver twl6040_vibra_driver = { + .probe = twl6040_vibra_probe, + .remove = __devexit_p(twl6040_vibra_remove), + .driver = { + .name = "twl6040-vibra", + .owner = THIS_MODULE, + .pm = &twl6040_vibra_pm_ops, + }, +}; + +static int __init twl6040_vibra_init(void) +{ + return platform_driver_register(&twl6040_vibra_driver); +} +module_init(twl6040_vibra_init); + +static void __exit twl6040_vibra_exit(void) +{ + platform_driver_unregister(&twl6040_vibra_driver); +} +module_exit(twl6040_vibra_exit); + +MODULE_ALIAS("platform:twl6040-vibra"); +MODULE_DESCRIPTION("TWL6040 Vibra driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jorge Eduardo Candelaria "); +MODULE_AUTHOR("Misael Lopez Cruz "); diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h index ea5baa2..685fd76 100644 --- a/include/linux/i2c/twl.h +++ b/include/linux/i2c/twl.h @@ -669,6 +669,14 @@ struct twl4030_codec_data { struct twl4030_vibra_data { unsigned int coexist; + + /* twl6040 */ + unsigned int vibldrv_res; /* left driver resistance */ + unsigned int vibrdrv_res; /* right driver resistance */ + unsigned int viblmotor_res; /* left motor resistance */ + unsigned int vibrmotor_res; /* right motor resistance */ + int vddvibl_uV; /* VDDVIBL volt, set 0 for fixed reg */ + int vddvibr_uV; /* VDDVIBR volt, set 0 for fixed reg */ }; struct twl4030_audio_data { -- cgit v1.1 From 87b25c1bab0489c8a8ff0c59ad3c5d9182d5327e Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 1 Jun 2011 11:30:38 +0300 Subject: OMAP4: SDP4430: Add twl6040 vibrator platform support Add twl4030_vibra platform data, and the needed regulators for twl6040 vibrator. Signed-off-by: Peter Ujfalusi Acked-by: Tony Lindgren --- arch/arm/mach-omap2/board-4430sdp.c | 48 +++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/arch/arm/mach-omap2/board-4430sdp.c b/arch/arm/mach-omap2/board-4430sdp.c index 933b25b..9c6bfb4 100644 --- a/arch/arm/mach-omap2/board-4430sdp.c +++ b/arch/arm/mach-omap2/board-4430sdp.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -275,11 +276,40 @@ static struct platform_device sdp4430_lcd_device = { .id = -1, }; +static struct regulator_consumer_supply sdp4430_vbat_supply[] = { + REGULATOR_SUPPLY("vddvibl", "twl6040-vibra"), + REGULATOR_SUPPLY("vddvibr", "twl6040-vibra"), +}; + +static struct regulator_init_data sdp4430_vbat_data = { + .constraints = { + .always_on = 1, + }, + .num_consumer_supplies = ARRAY_SIZE(sdp4430_vbat_supply), + .consumer_supplies = sdp4430_vbat_supply, +}; + +static struct fixed_voltage_config sdp4430_vbat_pdata = { + .supply_name = "VBAT", + .microvolts = 3750000, + .init_data = &sdp4430_vbat_data, + .gpio = -EINVAL, +}; + +static struct platform_device sdp4430_vbat = { + .name = "reg-fixed-voltage", + .id = -1, + .dev = { + .platform_data = &sdp4430_vbat_pdata, + }, +}; + static struct platform_device *sdp4430_devices[] __initdata = { &sdp4430_lcd_device, &sdp4430_gpio_keys_device, &sdp4430_leds_gpio, &sdp4430_leds_pwm, + &sdp4430_vbat, }; static struct omap_lcd_config sdp4430_lcd_config __initdata = { @@ -395,7 +425,25 @@ static struct regulator_init_data sdp4430_vusim = { }, }; +static struct twl4030_vibra_data twl6040_vibra = { + .vibldrv_res = 8, + .vibrdrv_res = 3, + .viblmotor_res = 10, + .vibrmotor_res = 10, + .vddvibl_uV = 0, /* fixed volt supply - VBAT */ + .vddvibr_uV = 0, /* fixed volt supply - VBAT */ +}; + +static struct twl4030_audio_data twl6040_audio = { + .codec = NULL, + .vibra = &twl6040_vibra, + .audpwron_gpio = 127, + .naudint_irq = OMAP44XX_IRQ_SYS_2N, + .irq_base = TWL6040_CODEC_IRQ_BASE, +}; + static struct twl4030_platform_data sdp4430_twldata = { + .audio = &twl6040_audio, /* Regulators */ .vusim = &sdp4430_vusim, .vaux1 = &sdp4430_vaux1, -- cgit v1.1 From 6510bdc3f4d7ea783d47af5a58741ea0b77c6823 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 11 Feb 2011 17:37:51 +0000 Subject: ASoC: twl6040: add all ABE DAIs Add all DAIs to fully support OMAP4 ABE. Signed-off-by: Liam Girdwood Signed-off-by: Peter Ujfalusi Acked-by: Mark Brown --- sound/soc/codecs/twl6040.c | 44 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index a20e293..28dc5d9 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -1359,23 +1359,51 @@ static struct snd_soc_dai_ops twl6040_dai_ops = { .set_sysclk = twl6040_set_dai_sysclk, }; -static struct snd_soc_dai_driver twl6040_dai = { - .name = "twl6040-hifi", +static struct snd_soc_dai_driver twl6040_dai[] = { +{ + .name = "twl6040-ul", + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = TWL6040_RATES, + .formats = TWL6040_FORMATS, + }, + .ops = &twl6040_dai_ops, +}, +{ + .name = "twl6040-dl1", .playback = { - .stream_name = "Playback", + .stream_name = "Headset Playback", .channels_min = 1, - .channels_max = 4, + .channels_max = 2, .rates = TWL6040_RATES, .formats = TWL6040_FORMATS, }, - .capture = { - .stream_name = "Capture", + .ops = &twl6040_dai_ops, +}, +{ + .name = "twl6040-dl2", + .playback = { + .stream_name = "Handsfree Playback", .channels_min = 1, .channels_max = 2, .rates = TWL6040_RATES, .formats = TWL6040_FORMATS, }, .ops = &twl6040_dai_ops, +}, +{ + .name = "twl6040-vib", + .playback = { + .stream_name = "Vibra Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .formats = TWL6040_FORMATS, + }, + .ops = &twl6040_dai_ops, +}, }; #ifdef CONFIG_PM @@ -1502,8 +1530,8 @@ static struct snd_soc_codec_driver soc_codec_dev_twl6040 = { static int __devinit twl6040_codec_probe(struct platform_device *pdev) { - return snd_soc_register_codec(&pdev->dev, - &soc_codec_dev_twl6040, &twl6040_dai, 1); + return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_twl6040, + twl6040_dai, ARRAY_SIZE(twl6040_dai)); } static int __devexit twl6040_codec_remove(struct platform_device *pdev) -- cgit v1.1 From e17e4ab8018bf0f18084577243cb6800dadd303c Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Fri, 18 Mar 2011 19:00:50 +0000 Subject: ASoC: twl6040: Support other sample rates in constraints. Add other supported sample rates to LP and HP modes. Signed-off-by: Liam Girdwood Signed-off-by: Peter Ujfalusi Acked-by: Mark Brown --- sound/soc/codecs/twl6040.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 28dc5d9..7845cdb 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -1203,6 +1203,13 @@ static int twl6040_set_bias_level(struct snd_soc_codec *codec, /* set of rates for each pll: low-power and high-performance */ static unsigned int lp_rates[] = { + 8000, + 11250, + 16000, + 22500, + 32000, + 44100, + 48000, 88200, 96000, }; @@ -1213,6 +1220,10 @@ static struct snd_pcm_hw_constraint_list lp_constraints = { }; static unsigned int hp_rates[] = { + 8000, + 16000, + 32000, + 48000, 96000, }; -- cgit v1.1 From 6bba63b68da7188953bf3cc2e29d1eb4e6a69115 Mon Sep 17 00:00:00 2001 From: Misael Lopez Cruz Date: Fri, 11 Feb 2011 17:51:05 -0600 Subject: ASoC: twl6040: Remove pll and headset mode dependency Remove dependency between pll (hppll, lppll) and headset power mode (low-power, high-performance), as headset power mode can be used with any pll. A new control is created to allow headset power mode configuration from userspace. Changing headset power mode during earpiece related usecases is not propagated down to the codec as earpiece requires HS DAC in HP mode. Signed-off-by: Misael Lopez Cruz Signed-off-by: Peter Ujfalusi Acked-by: Mark Brown --- sound/soc/codecs/twl6040.c | 62 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 57 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 7845cdb..c8f7fc2 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -79,6 +79,8 @@ struct twl6040_data { int codec_powered; int pll; int non_lp; + int hs_power_mode; + int hs_power_mode_locked; unsigned int clk_in; unsigned int sysclk; struct snd_pcm_hw_constraint_list *sysclk_constraints; @@ -651,15 +653,26 @@ static int twl6040_power_mode_event(struct snd_soc_dapm_widget *w, { struct snd_soc_codec *codec = w->codec; struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); + int ret = 0; - if (SND_SOC_DAPM_EVENT_ON(event)) + if (SND_SOC_DAPM_EVENT_ON(event)) { priv->non_lp++; - else + if (!strcmp(w->name, "Earphone Driver")) { + /* Earphone doesn't support low power mode */ + priv->hs_power_mode_locked = 1; + ret = headset_power_mode(codec, 1); + } + } else { priv->non_lp--; + if (!strcmp(w->name, "Earphone Driver")) { + priv->hs_power_mode_locked = 0; + ret = headset_power_mode(codec, priv->hs_power_mode); + } + } msleep(1); - return 0; + return ret; } static void twl6040_hs_jack_report(struct snd_soc_codec *codec, @@ -964,6 +977,43 @@ static const struct snd_kcontrol_new hfr_mux_controls = static const struct snd_kcontrol_new ep_driver_switch_controls = SOC_DAPM_SINGLE("Switch", TWL6040_REG_EARCTL, 0, 1, 0); +/* Headset power mode */ +static const char *twl6040_headset_power_texts[] = { + "Low-Power", "High-Perfomance", +}; + +static const struct soc_enum twl6040_headset_power_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(twl6040_headset_power_texts), + twl6040_headset_power_texts); + +static int twl6040_headset_power_get_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = priv->hs_power_mode; + + return 0; +} + +static int twl6040_headset_power_put_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); + int high_perf = ucontrol->value.enumerated.item[0]; + int ret = 0; + + if (!priv->hs_power_mode_locked) + ret = headset_power_mode(codec, high_perf); + + if (!ret) + priv->hs_power_mode = high_perf; + + return ret; +} + static const struct snd_kcontrol_new twl6040_snd_controls[] = { /* Capture gains */ SOC_DOUBLE_TLV("Capture Preamplifier Volume", @@ -982,6 +1032,10 @@ static const struct snd_kcontrol_new twl6040_snd_controls[] = { TWL6040_REG_HFLGAIN, TWL6040_REG_HFRGAIN, 0, 0x1D, 1, hf_tlv), SOC_SINGLE_TLV("Earphone Playback Volume", TWL6040_REG_EARCTL, 1, 0xF, 1, ep_tlv), + + SOC_ENUM_EXT("Headset Power Mode", twl6040_headset_power_enum, + twl6040_headset_power_get_enum, + twl6040_headset_power_put_enum), }; static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = { @@ -1339,7 +1393,6 @@ static int twl6040_set_dai_sysclk(struct snd_soc_dai *codec_dai, if (ret) return ret; - headset_power_mode(codec, 0); priv->sysclk_constraints = &lp_constraints; break; case TWL6040_SYSCLK_SEL_HPPLL: @@ -1348,7 +1401,6 @@ static int twl6040_set_dai_sysclk(struct snd_soc_dai *codec_dai, if (ret) return ret; - headset_power_mode(codec, 1); priv->sysclk_constraints = &hp_constraints; break; default: -- cgit v1.1 From f7026c99961da48cc4c09cc4f152db5fb30832e7 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 7 Mar 2011 15:25:23 +0000 Subject: ASoC: twl6040: set default constraints. Set default sysclk constraints to high performance mode. Signed-off-by: Liam Girdwood Signed-off-by: Peter Ujfalusi Acked-by: Mark Brown --- sound/soc/codecs/twl6040.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index c8f7fc2..39ecf2f 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -1502,6 +1502,7 @@ static int twl6040_probe(struct snd_soc_codec *codec) priv->codec = codec; codec->control_data = dev_get_drvdata(codec->dev->parent); + priv->sysclk_constraints = &hp_constraints; priv->workqueue = create_singlethread_workqueue("twl6040-codec"); if (!priv->workqueue) { ret = -ENOMEM; -- cgit v1.1 From 1fbe99529d9490fd29982af07731650f112ffdfa Mon Sep 17 00:00:00 2001 From: Axel Castaneda Gonzalez Date: Wed, 23 Feb 2011 20:08:28 -0600 Subject: ASoC: twl6040: Configure ramp step based on platform Enable ramp down/up step to be configured based on platform. Signed-off-by: Axel Castaneda Gonzalez Signed-off-by: Misael Lopez Cruz Signed-off-by: Peter Ujfalusi Acked-by: Mark Brown --- include/linux/i2c/twl.h | 4 ++ sound/soc/codecs/twl6040.c | 91 ++++++++++++++++++++++++++++++++++++---------- 2 files changed, 75 insertions(+), 20 deletions(-) diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h index 685fd76..114c0f6 100644 --- a/include/linux/i2c/twl.h +++ b/include/linux/i2c/twl.h @@ -664,6 +664,10 @@ struct twl4030_codec_data { unsigned int check_defaults:1; unsigned int reset_registers:1; unsigned int hs_extmute:1; + u16 hs_left_step; + u16 hs_right_step; + u16 hf_left_step; + u16 hf_right_step; void (*set_hs_extmute)(int mute); }; diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 39ecf2f..0145041 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -83,6 +83,10 @@ struct twl6040_data { int hs_power_mode_locked; unsigned int clk_in; unsigned int sysclk; + u16 hs_left_step; + u16 hs_right_step; + u16 hf_left_step; + u16 hf_right_step; struct snd_pcm_hw_constraint_list *sysclk_constraints; struct twl6040_jack_data hs_jack; struct snd_soc_codec *codec; @@ -339,7 +343,11 @@ static inline int twl6040_hs_ramp_step(struct snd_soc_codec *codec, if (headset->ramp == TWL6040_RAMP_UP) { /* ramp step up */ if (val < headset->left_vol) { - val += left_step; + if (val + left_step > headset->left_vol) + val = headset->left_vol; + else + val += left_step; + reg &= ~TWL6040_HSL_VOL_MASK; twl6040_write(codec, TWL6040_REG_HSGAIN, (reg | (~val & TWL6040_HSL_VOL_MASK))); @@ -349,7 +357,11 @@ static inline int twl6040_hs_ramp_step(struct snd_soc_codec *codec, } else if (headset->ramp == TWL6040_RAMP_DOWN) { /* ramp step down */ if (val > 0x0) { - val -= left_step; + if ((int)val - (int)left_step < 0) + val = 0; + else + val -= left_step; + reg &= ~TWL6040_HSL_VOL_MASK; twl6040_write(codec, TWL6040_REG_HSGAIN, reg | (~val & TWL6040_HSL_VOL_MASK)); @@ -366,7 +378,11 @@ static inline int twl6040_hs_ramp_step(struct snd_soc_codec *codec, if (headset->ramp == TWL6040_RAMP_UP) { /* ramp step up */ if (val < headset->right_vol) { - val += right_step; + if (val + right_step > headset->right_vol) + val = headset->right_vol; + else + val += right_step; + reg &= ~TWL6040_HSR_VOL_MASK; twl6040_write(codec, TWL6040_REG_HSGAIN, (reg | (~val << TWL6040_HSR_VOL_SHIFT))); @@ -376,7 +392,11 @@ static inline int twl6040_hs_ramp_step(struct snd_soc_codec *codec, } else if (headset->ramp == TWL6040_RAMP_DOWN) { /* ramp step down */ if (val > 0x0) { - val -= right_step; + if ((int)val - (int)right_step < 0) + val = 0; + else + val -= right_step; + reg &= ~TWL6040_HSR_VOL_MASK; twl6040_write(codec, TWL6040_REG_HSGAIN, reg | (~val << TWL6040_HSR_VOL_SHIFT)); @@ -407,7 +427,11 @@ static inline int twl6040_hf_ramp_step(struct snd_soc_codec *codec, if (handsfree->ramp == TWL6040_RAMP_UP) { /* ramp step up */ if (val < handsfree->left_vol) { - val += left_step; + if (val + left_step > handsfree->left_vol) + val = handsfree->left_vol; + else + val += left_step; + reg &= ~TWL6040_HF_VOL_MASK; twl6040_write(codec, TWL6040_REG_HFLGAIN, reg | (0x1D - val)); @@ -417,7 +441,11 @@ static inline int twl6040_hf_ramp_step(struct snd_soc_codec *codec, } else if (handsfree->ramp == TWL6040_RAMP_DOWN) { /* ramp step down */ if (val > 0) { - val -= left_step; + if ((int)val - (int)left_step < 0) + val = 0; + else + val -= left_step; + reg &= ~TWL6040_HF_VOL_MASK; twl6040_write(codec, TWL6040_REG_HFLGAIN, reg | (0x1D - val)); @@ -434,7 +462,11 @@ static inline int twl6040_hf_ramp_step(struct snd_soc_codec *codec, if (handsfree->ramp == TWL6040_RAMP_UP) { /* ramp step up */ if (val < handsfree->right_vol) { - val += right_step; + if (val + right_step > handsfree->right_vol) + val = handsfree->right_vol; + else + val += right_step; + reg &= ~TWL6040_HF_VOL_MASK; twl6040_write(codec, TWL6040_REG_HFRGAIN, reg | (0x1D - val)); @@ -444,7 +476,11 @@ static inline int twl6040_hf_ramp_step(struct snd_soc_codec *codec, } else if (handsfree->ramp == TWL6040_RAMP_DOWN) { /* ramp step down */ if (val > 0) { - val -= right_step; + if ((int)val - (int)right_step < 0) + val = 0; + else + val -= right_step; + reg &= ~TWL6040_HF_VOL_MASK; twl6040_write(codec, TWL6040_REG_HFRGAIN, reg | (0x1D - val)); @@ -473,11 +509,9 @@ static void twl6040_pga_hs_work(struct work_struct *work) /* HS PGA volumes have 4 bits of resolution to ramp */ for (i = 0; i <= 16; i++) { - headset_complete = 1; - if (headset->ramp != TWL6040_RAMP_NONE) - headset_complete = twl6040_hs_ramp_step(codec, - headset->left_step, - headset->right_step); + headset_complete = twl6040_hs_ramp_step(codec, + headset->left_step, + headset->right_step); /* ramp finished ? */ if (headset_complete) @@ -518,11 +552,9 @@ static void twl6040_pga_hf_work(struct work_struct *work) /* HF PGA volumes have 5 bits of resolution to ramp */ for (i = 0; i <= 32; i++) { - handsfree_complete = 1; - if (handsfree->ramp != TWL6040_RAMP_NONE) - handsfree_complete = twl6040_hf_ramp_step(codec, - handsfree->left_step, - handsfree->right_step); + handsfree_complete = twl6040_hf_ramp_step(codec, + handsfree->left_step, + handsfree->right_step); /* ramp finished ? */ if (handsfree_complete) @@ -563,12 +595,16 @@ static int pga_event(struct snd_soc_dapm_widget *w, out = &priv->headset; work = &priv->hs_delayed_work; queue = priv->hs_workqueue; + out->left_step = priv->hs_left_step; + out->right_step = priv->hs_right_step; out->step_delay = 5; /* 5 ms between volume ramp steps */ break; case 4: out = &priv->handsfree; work = &priv->hf_delayed_work; queue = priv->hf_workqueue; + out->left_step = priv->hf_left_step; + out->right_step = priv->hf_right_step; out->step_delay = 5; /* 5 ms between volume ramp steps */ if (SND_SOC_DAPM_EVENT_ON(event)) priv->non_lp++; @@ -601,8 +637,6 @@ static int pga_event(struct snd_soc_dapm_widget *w, if (!delayed_work_pending(work)) { /* use volume ramp for power-down */ - out->left_step = 1; - out->right_step = 1; out->ramp = TWL6040_RAMP_DOWN; INIT_COMPLETION(out->ramp_done); @@ -1492,6 +1526,7 @@ static int twl6040_resume(struct snd_soc_codec *codec) static int twl6040_probe(struct snd_soc_codec *codec) { struct twl6040_data *priv; + struct twl4030_codec_data *pdata = dev_get_platdata(codec->dev); int ret = 0; priv = kzalloc(sizeof(struct twl6040_data), GFP_KERNEL); @@ -1502,6 +1537,22 @@ static int twl6040_probe(struct snd_soc_codec *codec) priv->codec = codec; codec->control_data = dev_get_drvdata(codec->dev->parent); + if (pdata && pdata->hs_left_step && pdata->hs_right_step) { + priv->hs_left_step = pdata->hs_left_step; + priv->hs_right_step = pdata->hs_right_step; + } else { + priv->hs_left_step = 1; + priv->hs_right_step = 1; + } + + if (pdata && pdata->hf_left_step && pdata->hf_right_step) { + priv->hf_left_step = pdata->hf_left_step; + priv->hf_right_step = pdata->hf_right_step; + } else { + priv->hf_left_step = 1; + priv->hf_right_step = 1; + } + priv->sysclk_constraints = &hp_constraints; priv->workqueue = create_singlethread_workqueue("twl6040-codec"); if (!priv->workqueue) { -- cgit v1.1 From 1a4cdecec7d639ba2891195dc3f5917fb70653db Mon Sep 17 00:00:00 2001 From: Misael Lopez Cruz Date: Fri, 11 Feb 2011 19:50:37 -0600 Subject: OMAP4: SDP4430: Add twl6040 codec platform support Configure, and enable the twl6040 codec on SDP4430. Signed-off-by: Misael Lopez Cruz Signed-off-by: Peter Ujfalusi Acked-by: Tony Lindgren --- arch/arm/mach-omap2/board-4430sdp.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-omap2/board-4430sdp.c b/arch/arm/mach-omap2/board-4430sdp.c index 9c6bfb4..d4448a0 100644 --- a/arch/arm/mach-omap2/board-4430sdp.c +++ b/arch/arm/mach-omap2/board-4430sdp.c @@ -425,6 +425,14 @@ static struct regulator_init_data sdp4430_vusim = { }, }; +static struct twl4030_codec_data twl6040_codec = { + /* single-step ramp for headset and handsfree */ + .hs_left_step = 0x0f, + .hs_right_step = 0x0f, + .hf_left_step = 0x1d, + .hf_right_step = 0x1d, +}; + static struct twl4030_vibra_data twl6040_vibra = { .vibldrv_res = 8, .vibrdrv_res = 3, @@ -435,7 +443,7 @@ static struct twl4030_vibra_data twl6040_vibra = { }; static struct twl4030_audio_data twl6040_audio = { - .codec = NULL, + .codec = &twl6040_codec, .vibra = &twl6040_vibra, .audpwron_gpio = 127, .naudint_irq = OMAP44XX_IRQ_SYS_2N, -- cgit v1.1 From 0f962ae2dd8655f0800ff3c4b02d5f5a2ff3899e Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 5 Jul 2011 11:40:33 +0300 Subject: MFD: twl6040: Use resource to provide irq number for slaves Provide the irq number for slaves via resource on the platform device. The irq number configuration is done in the twl6040-core at probe time, so machine drivers do not need to be modified. Signed-off-by: Peter Ujfalusi Reviewed-by: Felipe Balbi Acked-by: Samuel Ortiz --- drivers/mfd/twl6040-core.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/mfd/twl6040-core.c b/drivers/mfd/twl6040-core.c index cfaedb5..471f489 100644 --- a/drivers/mfd/twl6040-core.c +++ b/drivers/mfd/twl6040-core.c @@ -435,6 +435,18 @@ unsigned int twl6040_get_sysclk(struct twl6040 *twl6040) } EXPORT_SYMBOL(twl6040_get_sysclk); +static struct resource twl6040_vibra_rsrc[] = { + { + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource twl6040_codec_rsrc[] = { + { + .flags = IORESOURCE_IRQ, + }, +}; + static int __devinit twl6040_probe(struct platform_device *pdev) { struct twl4030_audio_data *pdata = pdev->dev.platform_data; @@ -499,16 +511,29 @@ static int __devinit twl6040_probe(struct platform_device *pdev) twl6040_set_bits(twl6040, TWL6040_REG_ACCCTL, TWL6040_I2CSEL); if (pdata->codec) { + int irq = twl6040->irq_base + TWL6040_IRQ_PLUG; + cell = &twl6040->cells[children]; cell->name = "twl6040-codec"; + twl6040_codec_rsrc[0].start = irq; + twl6040_codec_rsrc[0].end = irq; + cell->resources = twl6040_codec_rsrc; + cell->num_resources = ARRAY_SIZE(twl6040_codec_rsrc); cell->platform_data = pdata->codec; cell->pdata_size = sizeof(*pdata->codec); children++; } if (pdata->vibra) { + int irq = twl6040->irq_base + TWL6040_IRQ_VIB; + cell = &twl6040->cells[children]; cell->name = "twl6040-vibra"; + twl6040_vibra_rsrc[0].start = irq; + twl6040_vibra_rsrc[0].end = irq; + cell->resources = twl6040_vibra_rsrc; + cell->num_resources = ARRAY_SIZE(twl6040_vibra_rsrc); + cell->platform_data = pdata->vibra; cell->pdata_size = sizeof(*pdata->vibra); children++; -- cgit v1.1 From 625cbe46bc97a82dcd9254b5bc01c8520d78b227 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 4 Jul 2011 19:50:02 +0300 Subject: input: twl6040-vibra: Do not use wrapper for irq request The twl6040_request_irq/free_irq inline functions are going to be removed, so replace them with direct calls. The irq number is provided by the core driver via resource. Signed-off-by: Peter Ujfalusi Reviewed-by: Felipe Balbi Acked-by: Dmitry Torokhov --- drivers/input/misc/twl6040-vibra.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/input/misc/twl6040-vibra.c b/drivers/input/misc/twl6040-vibra.c index dbf745d..c43002e 100644 --- a/drivers/input/misc/twl6040-vibra.c +++ b/drivers/input/misc/twl6040-vibra.c @@ -47,6 +47,7 @@ struct vibra_info { struct workqueue_struct *workqueue; struct work_struct play_work; struct mutex mutex; + int irq; bool enabled; int weak_speed; @@ -277,6 +278,13 @@ static int __devinit twl6040_vibra_probe(struct platform_device *pdev) goto err_kzalloc; } + info->irq = platform_get_irq(pdev, 0); + if (info->irq < 0) { + dev_err(info->dev, "invalid irq\n"); + ret = -EINVAL; + goto err_kzalloc; + } + mutex_init(&info->mutex); info->input_dev = input_allocate_device(); @@ -308,9 +316,8 @@ static int __devinit twl6040_vibra_probe(struct platform_device *pdev) platform_set_drvdata(pdev, info); - ret = twl6040_request_irq(info->twl6040, TWL6040_IRQ_VIB, - twl6040_vib_irq_handler, 0, - "twl6040_irq_vib", info); + ret = request_threaded_irq(info->irq, NULL, twl6040_vib_irq_handler, 0, + "twl6040_irq_vib", info); if (ret) { dev_err(info->dev, "VIB IRQ request failed: %d\n", ret); goto err_irq; @@ -360,7 +367,7 @@ static int __devinit twl6040_vibra_probe(struct platform_device *pdev) err_voltage: regulator_bulk_free(ARRAY_SIZE(info->supplies), info->supplies); err_regulator: - twl6040_free_irq(info->twl6040, TWL6040_IRQ_VIB, info); + free_irq(info->irq, info); err_irq: input_unregister_device(info->input_dev); info->input_dev = NULL; @@ -379,7 +386,7 @@ static int __devexit twl6040_vibra_remove(struct platform_device *pdev) struct vibra_info *info = platform_get_drvdata(pdev); input_unregister_device(info->input_dev); - twl6040_free_irq(info->twl6040, TWL6040_IRQ_VIB, info); + free_irq(info->irq, info); regulator_bulk_free(ARRAY_SIZE(info->supplies), info->supplies); destroy_workqueue(info->workqueue); kfree(info); -- cgit v1.1 From 2a433b9daff58c8ff231b879242a586371acc93f Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 4 Jul 2011 19:52:26 +0300 Subject: ASoC: twl6040: Do not use wrapper for irq request The twl6040_request_irq/free_irq inline functions are going to be removed, so replace them with direct calls. The irq number is provided by the core driver via resource. Signed-off-by: Peter Ujfalusi Reviewed-by: Felipe Balbi Acked-by: Mark Brown --- sound/soc/codecs/twl6040.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 0145041..14e3a58 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -76,6 +76,7 @@ struct twl6040_jack_data { /* codec private data */ struct twl6040_data { + int plug_irq; int codec_powered; int pll; int non_lp; @@ -1527,6 +1528,8 @@ static int twl6040_probe(struct snd_soc_codec *codec) { struct twl6040_data *priv; struct twl4030_codec_data *pdata = dev_get_platdata(codec->dev); + struct platform_device *pdev = container_of(codec->dev, + struct platform_device, dev); int ret = 0; priv = kzalloc(sizeof(struct twl6040_data), GFP_KERNEL); @@ -1553,6 +1556,13 @@ static int twl6040_probe(struct snd_soc_codec *codec) priv->hf_right_step = 1; } + priv->plug_irq = platform_get_irq(pdev, 0); + if (priv->plug_irq < 0) { + dev_err(codec->dev, "invalid irq\n"); + ret = -EINVAL; + goto work_err; + } + priv->sysclk_constraints = &hp_constraints; priv->workqueue = create_singlethread_workqueue("twl6040-codec"); if (!priv->workqueue) { @@ -1581,9 +1591,8 @@ static int twl6040_probe(struct snd_soc_codec *codec) INIT_DELAYED_WORK(&priv->hs_delayed_work, twl6040_pga_hs_work); INIT_DELAYED_WORK(&priv->hf_delayed_work, twl6040_pga_hf_work); - ret = twl6040_request_irq(codec->control_data, TWL6040_IRQ_PLUG, - twl6040_audio_handler, 0, - "twl6040_irq_plug", codec); + ret = request_threaded_irq(priv->plug_irq, NULL, twl6040_audio_handler, + 0, "twl6040_irq_plug", codec); if (ret) { dev_err(codec->dev, "PLUG IRQ request failed: %d\n", ret); goto plugirq_err; @@ -1604,7 +1613,7 @@ static int twl6040_probe(struct snd_soc_codec *codec) return 0; bias_err: - twl6040_free_irq(codec->control_data, TWL6040_IRQ_PLUG, codec); + free_irq(priv->plug_irq, codec); plugirq_err: destroy_workqueue(priv->hs_workqueue); hswq_err: @@ -1621,7 +1630,7 @@ static int twl6040_remove(struct snd_soc_codec *codec) struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); twl6040_set_bias_level(codec, SND_SOC_BIAS_OFF); - twl6040_free_irq(codec->control_data, TWL6040_IRQ_PLUG, codec); + free_irq(priv->plug_irq, codec); destroy_workqueue(priv->workqueue); destroy_workqueue(priv->hf_workqueue); destroy_workqueue(priv->hs_workqueue); -- cgit v1.1 From d20e1d21fd0c398a8beb170beacf8e2ca839844c Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 4 Jul 2011 20:15:19 +0300 Subject: MFD: twl6040: Demand valid interrupt configuration In order to operate correctly twl6040 needs correct interrupt configuration. The slave drivers (vibra, and ASoC codec) will refuse to probe, if the interrupt configuration is not correct. In this way some checks can be removed from the code. Signed-off-by: Peter Ujfalusi Reviewed-by: Felipe Balbi Acked-by: Samuel Ortiz --- drivers/mfd/twl6040-core.c | 42 +++++++++++++++++++++--------------------- drivers/mfd/twl6040-irq.c | 16 +--------------- 2 files changed, 22 insertions(+), 36 deletions(-) diff --git a/drivers/mfd/twl6040-core.c b/drivers/mfd/twl6040-core.c index 471f489..f71bb14 100644 --- a/drivers/mfd/twl6040-core.c +++ b/drivers/mfd/twl6040-core.c @@ -459,6 +459,12 @@ static int __devinit twl6040_probe(struct platform_device *pdev) return -EINVAL; } + /* In order to operate correctly we need valid interrupt config */ + if (!pdata->naudint_irq || !pdata->irq_base) { + dev_err(&pdev->dev, "Invalid IRQ configuration\n"); + return -EINVAL; + } + twl6040 = kzalloc(sizeof(struct twl6040), GFP_KERNEL); if (!twl6040) return -ENOMEM; @@ -491,20 +497,18 @@ static int __devinit twl6040_probe(struct platform_device *pdev) if (twl6040->rev == TWL6040_REV_ES1_0) twl6040->audpwron = -EINVAL; - if (twl6040->irq) { - /* codec interrupt */ - ret = twl6040_irq_init(twl6040); - if (ret) - goto gpio2_err; - - ret = twl6040_request_irq(twl6040, TWL6040_IRQ_READY, - twl6040_naudint_handler, 0, - "twl6040_irq_ready", twl6040); - if (ret) { - dev_err(twl6040->dev, "READY IRQ request failed: %d\n", - ret); - goto irq_err; - } + /* codec interrupt */ + ret = twl6040_irq_init(twl6040); + if (ret) + goto gpio2_err; + + ret = twl6040_request_irq(twl6040, TWL6040_IRQ_READY, + twl6040_naudint_handler, 0, + "twl6040_irq_ready", twl6040); + if (ret) { + dev_err(twl6040->dev, "READY IRQ request failed: %d\n", + ret); + goto irq_err; } /* dual-access registers controlled by I2C only */ @@ -553,11 +557,9 @@ static int __devinit twl6040_probe(struct platform_device *pdev) return 0; mfd_err: - if (twl6040->irq) - twl6040_free_irq(twl6040, TWL6040_IRQ_READY, twl6040); + twl6040_free_irq(twl6040, TWL6040_IRQ_READY, twl6040); irq_err: - if (twl6040->irq) - twl6040_irq_exit(twl6040); + twl6040_irq_exit(twl6040); gpio2_err: if (gpio_is_valid(twl6040->audpwron)) gpio_free(twl6040->audpwron); @@ -579,9 +581,7 @@ static int __devexit twl6040_remove(struct platform_device *pdev) gpio_free(twl6040->audpwron); twl6040_free_irq(twl6040, TWL6040_IRQ_READY, twl6040); - - if (twl6040->irq) - twl6040_irq_exit(twl6040); + twl6040_irq_exit(twl6040); mfd_remove_devices(&pdev->dev); platform_set_drvdata(pdev, NULL); diff --git a/drivers/mfd/twl6040-irq.c b/drivers/mfd/twl6040-irq.c index 938053541..b3f8dda 100644 --- a/drivers/mfd/twl6040-irq.c +++ b/drivers/mfd/twl6040-irq.c @@ -148,19 +148,6 @@ int twl6040_irq_init(struct twl6040 *twl6040) twl6040->irq_masks_cache = TWL6040_ALLINT_MSK; twl6040_reg_write(twl6040, TWL6040_REG_INTMR, TWL6040_ALLINT_MSK); - if (!twl6040->irq) { - dev_warn(twl6040->dev, - "no interrupt specified, no interrupts\n"); - twl6040->irq_base = 0; - return 0; - } - - if (!twl6040->irq_base) { - dev_err(twl6040->dev, - "no interrupt base specified, no interrupts\n"); - return 0; - } - /* Register them with genirq */ for (cur_irq = twl6040->irq_base; cur_irq < twl6040->irq_base + ARRAY_SIZE(twl6040_irqs); @@ -199,7 +186,6 @@ EXPORT_SYMBOL(twl6040_irq_init); void twl6040_irq_exit(struct twl6040 *twl6040) { - if (twl6040->irq) - free_irq(twl6040->irq, twl6040); + free_irq(twl6040->irq, twl6040); } EXPORT_SYMBOL(twl6040_irq_exit); -- cgit v1.1 From 1b7c4725e2e926eaeb8657ac22992ec27fe03135 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 4 Jul 2011 20:16:23 +0300 Subject: MFD: twl6040: Remove wrapper for threaded irq request Remove the twl6040_request_irq/free_irq inline functions, and use direct calls instead in the core driver to register the threaded irq. Signed-off-by: Peter Ujfalusi Reviewed-by: Felipe Balbi Acked-by: Samuel Ortiz --- drivers/mfd/twl6040-core.c | 10 +++++----- include/linux/mfd/twl6040.h | 22 ---------------------- 2 files changed, 5 insertions(+), 27 deletions(-) diff --git a/drivers/mfd/twl6040-core.c b/drivers/mfd/twl6040-core.c index f71bb14..6843977 100644 --- a/drivers/mfd/twl6040-core.c +++ b/drivers/mfd/twl6040-core.c @@ -502,9 +502,9 @@ static int __devinit twl6040_probe(struct platform_device *pdev) if (ret) goto gpio2_err; - ret = twl6040_request_irq(twl6040, TWL6040_IRQ_READY, - twl6040_naudint_handler, 0, - "twl6040_irq_ready", twl6040); + ret = request_threaded_irq(twl6040->irq_base + TWL6040_IRQ_READY, + NULL, twl6040_naudint_handler, 0, + "twl6040_irq_ready", twl6040); if (ret) { dev_err(twl6040->dev, "READY IRQ request failed: %d\n", ret); @@ -557,7 +557,7 @@ static int __devinit twl6040_probe(struct platform_device *pdev) return 0; mfd_err: - twl6040_free_irq(twl6040, TWL6040_IRQ_READY, twl6040); + free_irq(twl6040->irq_base + TWL6040_IRQ_READY, twl6040); irq_err: twl6040_irq_exit(twl6040); gpio2_err: @@ -580,7 +580,7 @@ static int __devexit twl6040_remove(struct platform_device *pdev) if (gpio_is_valid(twl6040->audpwron)) gpio_free(twl6040->audpwron); - twl6040_free_irq(twl6040, TWL6040_IRQ_READY, twl6040); + free_irq(twl6040->irq_base + TWL6040_IRQ_READY, twl6040); twl6040_irq_exit(twl6040); mfd_remove_devices(&pdev->dev); diff --git a/include/linux/mfd/twl6040.h b/include/linux/mfd/twl6040.h index c3c1de5..df890a2 100644 --- a/include/linux/mfd/twl6040.h +++ b/include/linux/mfd/twl6040.h @@ -215,28 +215,6 @@ struct twl6040 { u8 irq_masks_cache; }; -static inline int twl6040_request_irq(struct twl6040 *twl6040, int irq, - irq_handler_t handler, - unsigned long irqflags, - const char *name, - void *data) -{ - if (!twl6040->irq_base) - return -EINVAL; - - return request_threaded_irq(twl6040->irq_base + irq, NULL, handler, - irqflags, name, data); -} - -static inline void twl6040_free_irq(struct twl6040 *twl6040, int irq, - void *data) -{ - if (!twl6040->irq_base) - return; - - free_irq(twl6040->irq_base + irq, data); -} - int twl6040_reg_read(struct twl6040 *twl6040, unsigned int reg); int twl6040_reg_write(struct twl6040 *twl6040, unsigned int reg, u8 val); -- cgit v1.1 From 7cca606794ceb597e95fd0a0f3a8dcdd51af55e0 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 27 Jun 2011 13:33:14 +0300 Subject: ASoC: twl6040: Use neutral name for power mode text/enum Change the variable names to be neutral (not refering to HS). This will ease up the introduction of PLL selection, which going to use the same enum strings. Signed-off-by: Peter Ujfalusi Acked-by: Mark Brown --- sound/soc/codecs/twl6040.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 14e3a58..8f923e5 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -1013,13 +1013,13 @@ static const struct snd_kcontrol_new ep_driver_switch_controls = SOC_DAPM_SINGLE("Switch", TWL6040_REG_EARCTL, 0, 1, 0); /* Headset power mode */ -static const char *twl6040_headset_power_texts[] = { +static const char *twl6040_power_mode_texts[] = { "Low-Power", "High-Perfomance", }; -static const struct soc_enum twl6040_headset_power_enum = - SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(twl6040_headset_power_texts), - twl6040_headset_power_texts); +static const struct soc_enum twl6040_power_mode_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(twl6040_power_mode_texts), + twl6040_power_mode_texts); static int twl6040_headset_power_get_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1068,7 +1068,7 @@ static const struct snd_kcontrol_new twl6040_snd_controls[] = { SOC_SINGLE_TLV("Earphone Playback Volume", TWL6040_REG_EARCTL, 1, 0xF, 1, ep_tlv), - SOC_ENUM_EXT("Headset Power Mode", twl6040_headset_power_enum, + SOC_ENUM_EXT("Headset Power Mode", twl6040_power_mode_enum, twl6040_headset_power_get_enum, twl6040_headset_power_put_enum), }; -- cgit v1.1 From af958c72af88405501fe61a43f8011614cff29f5 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 27 Jun 2011 17:03:14 +0300 Subject: ASoC: twl6040: Move PLL selection to codec driver It is better if the selection between the Low power, and High performance PLL is handled within the codec driver, not in machine driver(s) to avoid duplicated code, and also to have consistent tracking of the selected PLL, and the resulting differences in supported sample rates. Signed-off-by: Peter Ujfalusi Acked-by: Mark Brown --- sound/soc/codecs/twl6040.c | 110 ++++++++++++++++++++++++++++++--------------- sound/soc/codecs/twl6040.h | 1 + sound/soc/omap/sdp4430.c | 50 +++------------------ 3 files changed, 79 insertions(+), 82 deletions(-) diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 8f923e5..94108ce 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -80,6 +80,7 @@ struct twl6040_data { int codec_powered; int pll; int non_lp; + int pll_power_mode; int hs_power_mode; int hs_power_mode_locked; unsigned int clk_in; @@ -210,6 +211,37 @@ static const int twl6040_vdd_reg[TWL6040_VDDREGNUM] = { TWL6040_REG_DLB, }; +/* set of rates for each pll: low-power and high-performance */ +static unsigned int lp_rates[] = { + 8000, + 11250, + 16000, + 22500, + 32000, + 44100, + 48000, + 88200, + 96000, +}; + +static struct snd_pcm_hw_constraint_list lp_constraints = { + .count = ARRAY_SIZE(lp_rates), + .list = lp_rates, +}; + +static unsigned int hp_rates[] = { + 8000, + 16000, + 32000, + 48000, + 96000, +}; + +static struct snd_pcm_hw_constraint_list hp_constraints = { + .count = ARRAY_SIZE(hp_rates), + .list = hp_rates, +}; + /* * read twl6040 register cache */ @@ -1049,6 +1081,43 @@ static int twl6040_headset_power_put_enum(struct snd_kcontrol *kcontrol, return ret; } +static int twl6040_pll_get_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = priv->pll_power_mode; + + return 0; +} + +static int twl6040_pll_put_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); + + priv->pll_power_mode = ucontrol->value.enumerated.item[0]; + if (priv->pll_power_mode) + priv->sysclk_constraints = &hp_constraints; + else + priv->sysclk_constraints = &lp_constraints; + + return 0; +} + +int twl6040_get_clk_id(struct snd_soc_codec *codec) +{ + struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); + + if (priv->pll_power_mode) + return TWL6040_SYSCLK_SEL_HPPLL; + else + return TWL6040_SYSCLK_SEL_LPPLL; +} +EXPORT_SYMBOL_GPL(twl6040_get_clk_id); + static const struct snd_kcontrol_new twl6040_snd_controls[] = { /* Capture gains */ SOC_DOUBLE_TLV("Capture Preamplifier Volume", @@ -1071,6 +1140,9 @@ static const struct snd_kcontrol_new twl6040_snd_controls[] = { SOC_ENUM_EXT("Headset Power Mode", twl6040_power_mode_enum, twl6040_headset_power_get_enum, twl6040_headset_power_put_enum), + + SOC_ENUM_EXT("PLL Selection", twl6040_power_mode_enum, + twl6040_pll_get_enum, twl6040_pll_put_enum), }; static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = { @@ -1289,38 +1361,6 @@ static int twl6040_set_bias_level(struct snd_soc_codec *codec, return 0; } -/* set of rates for each pll: low-power and high-performance */ - -static unsigned int lp_rates[] = { - 8000, - 11250, - 16000, - 22500, - 32000, - 44100, - 48000, - 88200, - 96000, -}; - -static struct snd_pcm_hw_constraint_list lp_constraints = { - .count = ARRAY_SIZE(lp_rates), - .list = lp_rates, -}; - -static unsigned int hp_rates[] = { - 8000, - 16000, - 32000, - 48000, - 96000, -}; - -static struct snd_pcm_hw_constraint_list hp_constraints = { - .count = ARRAY_SIZE(hp_rates), - .list = hp_rates, -}; - static int twl6040_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -1427,16 +1467,12 @@ static int twl6040_set_dai_sysclk(struct snd_soc_dai *codec_dai, freq, priv->sysclk); if (ret) return ret; - - priv->sysclk_constraints = &lp_constraints; break; case TWL6040_SYSCLK_SEL_HPPLL: ret = twl6040_set_pll(twl6040, TWL6040_HPPLL_ID, freq, priv->sysclk); if (ret) return ret; - - priv->sysclk_constraints = &hp_constraints; break; default: dev_err(codec->dev, "unknown clk_id %d\n", clk_id); @@ -1563,7 +1599,7 @@ static int twl6040_probe(struct snd_soc_codec *codec) goto work_err; } - priv->sysclk_constraints = &hp_constraints; + priv->sysclk_constraints = &lp_constraints; priv->workqueue = create_singlethread_workqueue("twl6040-codec"); if (!priv->workqueue) { ret = -ENOMEM; diff --git a/sound/soc/codecs/twl6040.h b/sound/soc/codecs/twl6040.h index 234bfad..d8de678 100644 --- a/sound/soc/codecs/twl6040.h +++ b/sound/soc/codecs/twl6040.h @@ -24,5 +24,6 @@ void twl6040_hs_jack_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, int report); +int twl6040_get_clk_id(struct snd_soc_codec *codec); #endif /* End of __TWL6040_H__ */ diff --git a/sound/soc/omap/sdp4430.c b/sound/soc/omap/sdp4430.c index 5d67c25..b80efb0 100644 --- a/sound/soc/omap/sdp4430.c +++ b/sound/soc/omap/sdp4430.c @@ -36,8 +36,6 @@ #include "omap-pcm.h" #include "../codecs/twl6040.h" -static int twl6040_power_mode; - static int sdp4430_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -46,13 +44,13 @@ static int sdp4430_hw_params(struct snd_pcm_substream *substream, int clk_id, freq; int ret; - if (twl6040_power_mode) { - clk_id = TWL6040_SYSCLK_SEL_HPPLL; + clk_id = twl6040_get_clk_id(rtd->codec); + if (clk_id == TWL6040_SYSCLK_SEL_HPPLL) freq = 38400000; - } else { - clk_id = TWL6040_SYSCLK_SEL_LPPLL; + else if (clk_id == TWL6040_SYSCLK_SEL_LPPLL) freq = 32768; - } + else + return -EINVAL; /* set the codec mclk */ ret = snd_soc_dai_set_sysclk(codec_dai, clk_id, freq, @@ -83,35 +81,6 @@ static struct snd_soc_jack_pin hs_jack_pins[] = { }, }; -static int sdp4430_get_power_mode(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - ucontrol->value.integer.value[0] = twl6040_power_mode; - return 0; -} - -static int sdp4430_set_power_mode(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - if (twl6040_power_mode == ucontrol->value.integer.value[0]) - return 0; - - twl6040_power_mode = ucontrol->value.integer.value[0]; - - return 1; -} - -static const char *power_texts[] = {"Low-Power", "High-Performance"}; - -static const struct soc_enum sdp4430_enum[] = { - SOC_ENUM_SINGLE_EXT(2, power_texts), -}; - -static const struct snd_kcontrol_new sdp4430_controls[] = { - SOC_ENUM_EXT("TWL6040 Power Mode", sdp4430_enum[0], - sdp4430_get_power_mode, sdp4430_set_power_mode), -}; - /* SDP4430 machine DAPM */ static const struct snd_soc_dapm_widget sdp4430_twl6040_dapm_widgets[] = { SND_SOC_DAPM_MIC("Ext Mic", NULL), @@ -154,12 +123,6 @@ static int sdp4430_twl6040_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_dapm_context *dapm = &codec->dapm; int ret; - /* Add SDP4430 specific controls */ - ret = snd_soc_add_controls(codec, sdp4430_controls, - ARRAY_SIZE(sdp4430_controls)); - if (ret) - return ret; - /* Add SDP4430 specific widgets */ ret = snd_soc_dapm_new_controls(dapm, sdp4430_twl6040_dapm_widgets, ARRAY_SIZE(sdp4430_twl6040_dapm_widgets)); @@ -239,9 +202,6 @@ static int __init sdp4430_soc_init(void) if (ret) goto err; - /* Codec starts in HP mode */ - twl6040_power_mode = 1; - return 0; err: -- cgit v1.1 From f53c346c08425b6448cf9729e882e4057ea505f0 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 29 Jun 2011 13:28:18 +0300 Subject: ASoC: twl6040: Simplify sample rate constraint handling We can manage the sample rate constraints without the need to maintain a variable and a pointer. This simplifies the handling of the constraint, and makes it more robust. Signed-off-by: Peter Ujfalusi Acked-by: Mark Brown --- sound/soc/codecs/twl6040.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 94108ce..c72268b 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -89,7 +89,6 @@ struct twl6040_data { u16 hs_right_step; u16 hf_left_step; u16 hf_right_step; - struct snd_pcm_hw_constraint_list *sysclk_constraints; struct twl6040_jack_data hs_jack; struct snd_soc_codec *codec; struct workqueue_struct *workqueue; @@ -224,11 +223,6 @@ static unsigned int lp_rates[] = { 96000, }; -static struct snd_pcm_hw_constraint_list lp_constraints = { - .count = ARRAY_SIZE(lp_rates), - .list = lp_rates, -}; - static unsigned int hp_rates[] = { 8000, 16000, @@ -237,9 +231,9 @@ static unsigned int hp_rates[] = { 96000, }; -static struct snd_pcm_hw_constraint_list hp_constraints = { - .count = ARRAY_SIZE(hp_rates), - .list = hp_rates, +static struct snd_pcm_hw_constraint_list sysclk_constraints[] = { + { .count = ARRAY_SIZE(lp_rates), .list = lp_rates, }, + { .count = ARRAY_SIZE(hp_rates), .list = hp_rates, }, }; /* @@ -1099,10 +1093,6 @@ static int twl6040_pll_put_enum(struct snd_kcontrol *kcontrol, struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); priv->pll_power_mode = ucontrol->value.enumerated.item[0]; - if (priv->pll_power_mode) - priv->sysclk_constraints = &hp_constraints; - else - priv->sysclk_constraints = &lp_constraints; return 0; } @@ -1370,7 +1360,7 @@ static int twl6040_startup(struct snd_pcm_substream *substream, snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - priv->sysclk_constraints); + &sysclk_constraints[priv->pll_power_mode]); return 0; } @@ -1599,7 +1589,6 @@ static int twl6040_probe(struct snd_soc_codec *codec) goto work_err; } - priv->sysclk_constraints = &lp_constraints; priv->workqueue = create_singlethread_workqueue("twl6040-codec"); if (!priv->workqueue) { ret = -ENOMEM; -- cgit v1.1 From 753621c2155bd49bff7d5d3844b3ddc203e44a06 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Sun, 3 Jul 2011 02:06:07 +0300 Subject: ASoC: twl6040: Configure PLL only once Avoid configuring the PLL several times during audio startup. We can configure the PLL at prepare time with parameters collected earlier hw_param, and set_dai_sysclk calls. Signed-off-by: Peter Ujfalusi Acked-by: Mark Brown --- sound/soc/codecs/twl6040.c | 72 +++++++++++++++++----------------------------- 1 file changed, 27 insertions(+), 45 deletions(-) diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index c72268b..bd364c3 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -1343,9 +1343,6 @@ static int twl6040_set_bias_level(struct snd_soc_codec *codec, break; } - /* get PLL and sysclk after power transition */ - priv->pll = twl6040_get_pll(twl6040); - priv->sysclk = twl6040_get_sysclk(twl6040); codec->dapm.bias_level = level; return 0; @@ -1371,14 +1368,8 @@ static int twl6040_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_codec *codec = rtd->codec; - struct twl6040 *twl6040 = codec->control_data; struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); - unsigned int sysclk; - int rate, ret; - - /* nothing to do for high-perf pll, it supports only 48 kHz */ - if (priv->pll == TWL6040_HPPLL_ID) - return 0; + int rate; rate = params_rate(params); switch (rate) { @@ -1386,26 +1377,33 @@ static int twl6040_hw_params(struct snd_pcm_substream *substream, case 22500: case 44100: case 88200: - sysclk = 17640000; + /* These rates are not supported when HPPLL is in use */ + if (unlikely(priv->pll == TWL6040_SYSCLK_SEL_HPPLL)) { + dev_err(codec->dev, "HPPLL does not support rate %d\n", + rate); + return -EINVAL; + } + /* Capture is not supported with 17.64MHz sysclk */ + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + dev_err(codec->dev, + "capture mode is not supported at %dHz\n", + rate); + return -EINVAL; + } + priv->sysclk = 17640000; break; case 8000: case 16000: case 32000: case 48000: case 96000: - sysclk = 19200000; + priv->sysclk = 19200000; break; default: dev_err(codec->dev, "unsupported rate %d\n", rate); return -EINVAL; } - ret = twl6040_set_pll(twl6040, TWL6040_LPPLL_ID, priv->clk_in, sysclk); - if (ret) - return ret; - - priv->sysclk = twl6040_get_sysclk(twl6040); - return 0; } @@ -1414,7 +1412,9 @@ static int twl6040_prepare(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_codec *codec = rtd->codec; + struct twl6040 *twl6040 = codec->control_data; struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); + int ret; if (!priv->sysclk) { dev_err(codec->dev, @@ -1422,24 +1422,19 @@ static int twl6040_prepare(struct snd_pcm_substream *substream, return -EINVAL; } - /* - * capture is not supported at 17.64 MHz, - * it's reserved for headset low-power playback scenario - */ - if ((priv->sysclk == 17640000) && - substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - dev_err(codec->dev, - "capture mode is not supported at %dHz\n", - priv->sysclk); - return -EINVAL; - } - if ((priv->sysclk == 17640000) && priv->non_lp) { dev_err(codec->dev, "some enabled paths aren't supported at %dHz\n", priv->sysclk); return -EPERM; } + + ret = twl6040_set_pll(twl6040, priv->pll, priv->clk_in, priv->sysclk); + if (ret) { + dev_err(codec->dev, "Can not set PLL (%d)\n", ret); + return -EPERM; + } + return 0; } @@ -1447,32 +1442,19 @@ static int twl6040_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, unsigned int freq, int dir) { struct snd_soc_codec *codec = codec_dai->codec; - struct twl6040 *twl6040 = codec->control_data; struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); - int ret = 0; switch (clk_id) { case TWL6040_SYSCLK_SEL_LPPLL: - ret = twl6040_set_pll(twl6040, TWL6040_LPPLL_ID, - freq, priv->sysclk); - if (ret) - return ret; - break; case TWL6040_SYSCLK_SEL_HPPLL: - ret = twl6040_set_pll(twl6040, TWL6040_HPPLL_ID, - freq, priv->sysclk); - if (ret) - return ret; + priv->pll = clk_id; + priv->clk_in = freq; break; default: dev_err(codec->dev, "unknown clk_id %d\n", clk_id); return -EINVAL; } - priv->pll = twl6040_get_pll(twl6040); - priv->clk_in = freq; - priv->sysclk = twl6040_get_sysclk(twl6040); - return 0; } -- cgit v1.1 From cfb7a33bea259d2d72a64adcb3de28532170dc25 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 4 Jul 2011 10:28:28 +0300 Subject: MFD: twl6040: Remove enum for PLL tracking There is no need to have two different types for tracking the selected PLL. Use only the defines, when dealing with the PLLs. Signed-off-by: Peter Ujfalusi Acked-by: Samuel Ortiz --- drivers/mfd/twl6040-core.c | 26 +++++++++++++------------- include/linux/mfd/twl6040.h | 17 ++++++----------- 2 files changed, 19 insertions(+), 24 deletions(-) diff --git a/drivers/mfd/twl6040-core.c b/drivers/mfd/twl6040-core.c index 6843977..24d436c 100644 --- a/drivers/mfd/twl6040-core.c +++ b/drivers/mfd/twl6040-core.c @@ -270,7 +270,8 @@ int twl6040_power(struct twl6040 *twl6040, int on) goto out; } } - twl6040->pll = TWL6040_LPPLL_ID; + /* Default PLL configuration after power up */ + twl6040->pll = TWL6040_SYSCLK_SEL_LPPLL; twl6040->sysclk = 19200000; } else { /* already powered-down */ @@ -294,7 +295,6 @@ int twl6040_power(struct twl6040 *twl6040, int on) /* use manual power-down sequence */ twl6040_power_down(twl6040); } - twl6040->pll = TWL6040_NOPLL_ID; twl6040->sysclk = 0; } @@ -304,7 +304,7 @@ out: } EXPORT_SYMBOL(twl6040_power); -int twl6040_set_pll(struct twl6040 *twl6040, enum twl6040_pll_id id, +int twl6040_set_pll(struct twl6040 *twl6040, int pll_id, unsigned int freq_in, unsigned int freq_out) { u8 hppllctl, lppllctl; @@ -315,8 +315,8 @@ int twl6040_set_pll(struct twl6040 *twl6040, enum twl6040_pll_id id, hppllctl = twl6040_reg_read(twl6040, TWL6040_REG_HPPLLCTL); lppllctl = twl6040_reg_read(twl6040, TWL6040_REG_LPPLLCTL); - switch (id) { - case TWL6040_LPPLL_ID: + switch (pll_id) { + case TWL6040_SYSCLK_SEL_LPPLL: /* low-power PLL divider */ switch (freq_out) { case 17640000: @@ -352,10 +352,8 @@ int twl6040_set_pll(struct twl6040 *twl6040, enum twl6040_pll_id id, ret = -EINVAL; goto pll_out; } - - twl6040->pll = TWL6040_LPPLL_ID; break; - case TWL6040_HPPLL_ID: + case TWL6040_SYSCLK_SEL_HPPLL: /* high-performance PLL can provide only 19.2 MHz */ if (freq_out != 19200000) { dev_err(&twl6040_dev->dev, @@ -406,16 +404,15 @@ int twl6040_set_pll(struct twl6040 *twl6040, enum twl6040_pll_id id, twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl); lppllctl &= ~TWL6040_LPLLENA; twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl); - - twl6040->pll = TWL6040_HPPLL_ID; break; default: - dev_err(&twl6040_dev->dev, "unknown pll id %d\n", id); + dev_err(&twl6040_dev->dev, "unknown pll id %d\n", pll_id); ret = -EINVAL; goto pll_out; } twl6040->sysclk = freq_out; + twl6040->pll = pll_id; pll_out: mutex_unlock(&twl6040->mutex); @@ -423,9 +420,12 @@ pll_out: } EXPORT_SYMBOL(twl6040_set_pll); -enum twl6040_pll_id twl6040_get_pll(struct twl6040 *twl6040) +int twl6040_get_pll(struct twl6040 *twl6040) { - return twl6040->pll; + if (twl6040->power_count) + return twl6040->pll; + else + return -ENODEV; } EXPORT_SYMBOL(twl6040_get_pll); diff --git a/include/linux/mfd/twl6040.h b/include/linux/mfd/twl6040.h index df890a2..4c806f6 100644 --- a/include/linux/mfd/twl6040.h +++ b/include/linux/mfd/twl6040.h @@ -165,9 +165,6 @@ #define TWL6040_RESETSPLIT 0x04 #define TWL6040_INTCLRMODE 0x08 -#define TWL6040_SYSCLK_SEL_LPPLL 1 -#define TWL6040_SYSCLK_SEL_HPPLL 2 - /* STATUS (0x2E) fields */ #define TWL6040_PLUGCOMP 0x02 @@ -188,11 +185,9 @@ #define TWL6040_IRQ_VIB 4 #define TWL6040_IRQ_READY 5 -enum twl6040_pll_id { - TWL6040_NOPLL_ID, - TWL6040_LPPLL_ID, - TWL6040_HPPLL_ID, -}; +/* PLL selection */ +#define TWL6040_SYSCLK_SEL_LPPLL 0 +#define TWL6040_SYSCLK_SEL_HPPLL 1 struct twl6040 { struct device *dev; @@ -206,7 +201,7 @@ struct twl6040 { int power_count; int rev; - enum twl6040_pll_id pll; + int pll; unsigned int sysclk; unsigned int irq; @@ -223,9 +218,9 @@ int twl6040_set_bits(struct twl6040 *twl6040, unsigned int reg, int twl6040_clear_bits(struct twl6040 *twl6040, unsigned int reg, u8 mask); int twl6040_power(struct twl6040 *twl6040, int on); -int twl6040_set_pll(struct twl6040 *twl6040, enum twl6040_pll_id id, +int twl6040_set_pll(struct twl6040 *twl6040, int pll_id, unsigned int freq_in, unsigned int freq_out); -enum twl6040_pll_id twl6040_get_pll(struct twl6040 *twl6040); +int twl6040_get_pll(struct twl6040 *twl6040); unsigned int twl6040_get_sysclk(struct twl6040 *twl6040); int twl6040_irq_init(struct twl6040 *twl6040); void twl6040_irq_exit(struct twl6040 *twl6040); -- cgit v1.1 From ff593ca1a43042499cbe341fc91c9cc282c084cb Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 4 Jul 2011 10:35:23 +0300 Subject: ASoC: twl6040: No need to convert the PLL ID Since the PLL handling has been simplified, and rebased on 0, there is no longer need for converting the PLL ID. Signed-off-by: Peter Ujfalusi Acked-by: Mark Brown --- sound/soc/codecs/twl6040.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index bd364c3..8784395 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -1101,10 +1101,7 @@ int twl6040_get_clk_id(struct snd_soc_codec *codec) { struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); - if (priv->pll_power_mode) - return TWL6040_SYSCLK_SEL_HPPLL; - else - return TWL6040_SYSCLK_SEL_LPPLL; + return priv->pll_power_mode; } EXPORT_SYMBOL_GPL(twl6040_get_clk_id); -- cgit v1.1 From 21385eeb02e59cb2a72b60cd146f9c1ce3cb0acd Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 5 Jul 2011 22:35:53 +0300 Subject: ASoC: twl6040: Add back support for legacy mode The legacy mode has been accidentaly removed by commit: ASoC: twl6040: add all ABE DAIs Add back the twl6040-hifi dai. Signed-off-by: Peter Ujfalusi Acked-by: Mark Brown --- sound/soc/codecs/twl6040.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 8784395..342c5a3 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -1464,6 +1464,24 @@ static struct snd_soc_dai_ops twl6040_dai_ops = { static struct snd_soc_dai_driver twl6040_dai[] = { { + .name = "twl6040-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = TWL6040_RATES, + .formats = TWL6040_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = TWL6040_RATES, + .formats = TWL6040_FORMATS, + }, + .ops = &twl6040_dai_ops, +}, +{ .name = "twl6040-ul", .capture = { .stream_name = "Capture", -- cgit v1.1