[alsa-devel] [PATCH 0/4] twl4030 codec as MFD device
Hello,
The following series adds new MFD device on top of the twl4030 MFD device for the codec part of the chip, and also converts the soc audio driver to use the correct probing (device model).
Reason for the twl4030_codec MFD: the vibra control is actually in the codec part of the twl4030. If both the vibra and the audio functionality is needed from the twl4030 at the same time, than they need to control the codec power and APLL at the same time without breaking the other driver. Also these two has to be able to work without the need for the other driver.
The proposed solution: Register twl4030_codec as a child for the twl4030 mfd. twl4030_codec is also and mfd and at the moment it has two child: twl4030_codec_audio: for audio codec twl4030_codec_vibra: for vibra driver
The twl4030_codec mfd registers the devices for audio and vibra (if the platform data is not NULL for them), and has - at the moment - functions to enable/disable resources, each of these resources has ref-counts, so the twl4030_codec knows, when the resource is need to be turned on or off.
The interface is quite simple: int twl4030_codec_enable_resource(enum twl4030_codec_res id); int twl4030_codec_disable_resource(enum twl4030_codec_res id);
These functions are returning the content of the given register after the modification (or negative value, if error - the error handling need to be revisited in a near future), so that the caller can update it's local cache if it need to do (the audio driver needs it).
There are two resource defined: TWL4030_CODEC_RES_POWER: Controlling the power of the codec TWL4030_CODEC_RES_APLL: For APLL
The vibra driver is not part of this series.
I have also included a patch, which changes the board files for those, which in my knowledge uses the twl4030 codec as audio device. I might missed some boards...
To-Do list: - revisit the error handling in twl4030-codec driver - add the actual vibra driver ;) - coexistence related fixes for the audio and vibra driver - move all audio related configuration from soc machine drivers to the board files (if any)
--- Peter Ujfalusi (4): MFD: twl4030: add twl4030_codec MFD as a new child to the core OMAP: Platform support for twl4030_codec MFD ASoC: TWL4030: Only update the needed bits in *set_dai_sysclk ASoC: TWL4030: Driver registration via twl4030_codec MFD
arch/arm/mach-omap2/board-3430sdp.c | 9 + arch/arm/mach-omap2/board-omap3beagle.c | 9 + arch/arm/mach-omap2/board-omap3evm.c | 9 + arch/arm/mach-omap2/board-omap3pandora.c | 9 + arch/arm/mach-omap2/board-overo.c | 9 + arch/arm/mach-omap2/board-zoom2.c | 9 + drivers/mfd/Kconfig | 13 ++ drivers/mfd/Makefile | 1 + drivers/mfd/twl4030-codec.c | 245 ++++++++++++++++++++++++++++ drivers/mfd/twl4030-core.c | 14 ++ include/linux/i2c/twl4030.h | 18 ++ include/linux/mfd/twl4030-codec.h | 261 ++++++++++++++++++++++++++++++ sound/soc/codecs/Kconfig | 2 +- sound/soc/codecs/twl4030.c | 225 ++++++++++++++++---------- sound/soc/codecs/twl4030.h | 242 +--------------------------- 15 files changed, 751 insertions(+), 324 deletions(-) create mode 100644 drivers/mfd/twl4030-codec.c create mode 100644 include/linux/mfd/twl4030-codec.h
New MFD child to twl4030 MFD device. This MFD device will be used by the drivers, which needs resources from the twl4030 codec like audio and vibra.
The platform specific configuration data is passed along to the child drivers (audio, vibra).
Signed-off-by: Peter Ujfalusi peter.ujfalusi@nokia.com --- drivers/mfd/Kconfig | 13 ++ drivers/mfd/Makefile | 1 + drivers/mfd/twl4030-codec.c | 245 ++++++++++++++++++++++++++++++++++ drivers/mfd/twl4030-core.c | 14 ++ include/linux/i2c/twl4030.h | 18 +++ include/linux/mfd/twl4030-codec.h | 261 +++++++++++++++++++++++++++++++++++++ 6 files changed, 552 insertions(+), 0 deletions(-) create mode 100644 drivers/mfd/twl4030-codec.c create mode 100644 include/linux/mfd/twl4030-codec.h
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 570be13..08e4aa6 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -121,6 +121,19 @@ config TWL4030_POWER and load scripts controling which resources are switched off/on or reset when a sleep, wakeup or warm reset event occurs.
+config TWL4030_CODEC + bool "Support codec part of the TWL4030 family chips" + depends on TWL4030_CORE + help + Say yes here if you want to use the codec resources on the + TWL4030 family chips. The codec in TWL4030 provides the audio + functionality and also has the vibrator controls. + + This driver provides MFD device to be used for drivers, which needs + access to the codec part, the board-specific data is passed from this + driver to the childs as platform data with the board specific + configuration. + config MFD_TMIO bool default n diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index f3b277b..af0fc90 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_MENELAUS) += menelaus.o
obj-$(CONFIG_TWL4030_CORE) += twl4030-core.o twl4030-irq.o obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o +obj-$(CONFIG_TWL4030_CODEC) += twl4030-codec.o
obj-$(CONFIG_MFD_MC13783) += mc13783-core.o
diff --git a/drivers/mfd/twl4030-codec.c b/drivers/mfd/twl4030-codec.c new file mode 100644 index 0000000..a459052 --- /dev/null +++ b/drivers/mfd/twl4030-codec.c @@ -0,0 +1,245 @@ +/* + * MFD driver for twl4030 codec submodule + * + * Author: Peter Ujfalusi peter.ujfalusi@nokia.com + * + * 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 <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/platform_device.h> +#include <linux/i2c/twl4030.h> +#include <linux/mfd/twl4030-codec.h> + +static struct platform_device *twl4030_codec_dev; + +struct twl4030_codec_resource { + int request_count; + u8 reg; + u8 mask; +}; + +struct twl4030_codec { + struct mutex mutex; + struct twl4030_codec_resource resource[TWL4030_CODEC_RES_MAX]; +}; + +/* + * 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) +{ + struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev); + u8 val; + + twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val, + codec->resource[id].reg); + + if (enable) + val |= codec->resource[id].mask; + else + val &= ~codec->resource[id].mask; + + twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, + val, codec->resource[id].reg); + + return val; +} + +static inline int twl4030_codec_get_resource(enum twl4030_codec_res id) +{ + struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev); + u8 val; + + twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val, + codec->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_codec *codec = platform_get_drvdata(twl4030_codec_dev); + int val; + + if (id >= TWL4030_CODEC_RES_MAX) { + dev_err(&twl4030_codec_dev->dev, + "Invalid resource ID (%u)\n", id); + return -EINVAL; + } + + mutex_lock(&codec->mutex); + if (!codec->resource[id].request_count) + /* Resource was disabled, enable it */ + val = twl4030_codec_set_resource(id, 1); + else + val = twl4030_codec_get_resource(id); + + codec->resource[id].request_count++; + mutex_unlock(&codec->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_codec *codec = platform_get_drvdata(twl4030_codec_dev); + int val; + + if (id >= TWL4030_CODEC_RES_MAX) { + dev_err(&twl4030_codec_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, + "Resource has been disabled already (%u)\n", id); + mutex_unlock(&codec->mutex); + return -EPERM; + } + codec->resource[id].request_count--; + + if (!codec->resource[id].request_count) + /* Resource can be disabled now */ + val = twl4030_codec_set_resource(id, 0); + else + val = twl4030_codec_get_resource(id); + + mutex_unlock(&codec->mutex); + + return val; +} +EXPORT_SYMBOL_GPL(twl4030_codec_disable_resource); + +static struct device * +twl4030_codec_new_child(const char *name, void *pdata, unsigned pdata_len) +{ + struct platform_device *pdev; + int status = 0; + + pdev = platform_device_alloc(name, -1); + if (!pdev) { + dev_dbg(&twl4030_codec_dev->dev, "can't alloc dev (%s)\n", + name); + return ERR_PTR(-ENOMEM); + } + pdev->dev.parent = &twl4030_codec_dev->dev; + + if (pdata) { + status = platform_device_add_data(pdev, pdata, pdata_len); + if (status < 0) { + dev_dbg(&pdev->dev, "can't add platform_data\n"); + goto err; + } + } + status = platform_device_add(pdev); + if (status < 0) { + dev_dbg(&pdev->dev, "Adding platform device failed\n"); + goto err; + } + + return &pdev->dev; +err: + platform_device_put(pdev); + dev_err(&twl4030_codec_dev->dev, "can't add %s dev\n", name); + return ERR_PTR(status); +} + +static int __devinit twl4030_codec_probe(struct platform_device *pdev) +{ + struct twl4030_codec *codec; + struct twl4030_codec_data *pdata = pdev->dev.platform_data; + + codec = kzalloc(sizeof(struct twl4030_codec), GFP_KERNEL); + if (!codec) + return -ENOMEM; + + platform_set_drvdata(pdev, codec); + + twl4030_codec_dev = pdev; + mutex_init(&codec->mutex); + + /* Codec power */ + codec->resource[TWL4030_CODEC_RES_POWER].reg = TWL4030_REG_CODEC_MODE; + codec->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; + + if (pdata->audio) + twl4030_codec_new_child("twl4030_codec_audio", pdata->audio, + sizeof(*pdata->audio)); + if (pdata->vibra) + twl4030_codec_new_child("twl4030_codec_vibra", pdata->vibra, + sizeof(*pdata->vibra)); + return 0; +} + +static int __devexit twl4030_codec_remove(struct platform_device *pdev) +{ + struct twl4030_codec *codec = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + kfree(codec); + twl4030_codec_dev = NULL; + + return 0; +} + +MODULE_ALIAS("platform:twl4030_codec"); + +static struct platform_driver twl4030_codec_driver = { + .probe = twl4030_codec_probe, + .remove = __devexit_p(twl4030_codec_remove), + .driver = { + .owner = THIS_MODULE, + .name = "twl4030_codec", + }, +}; + +static int __devinit twl4030_codec_init(void) +{ + return platform_driver_register(&twl4030_codec_driver); +} +module_init(twl4030_codec_init); + +static void __devexit twl4030_codec_exit(void) +{ + platform_driver_unregister(&twl4030_codec_driver); +} +module_exit(twl4030_codec_exit); + +MODULE_AUTHOR("Peter Ujfalusi peter.ujfalusi@nokia.com"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/mfd/twl4030-core.c b/drivers/mfd/twl4030-core.c index e424cf6..0ee81e4 100644 --- a/drivers/mfd/twl4030-core.c +++ b/drivers/mfd/twl4030-core.c @@ -114,6 +114,12 @@ #define twl_has_watchdog() false #endif
+#if defined(CONFIG_TWL4030_CODEC) || defined(CONFIG_TWL4030_CODEC_MODULE) +#define twl_has_codec() true +#else +#define twl_has_codec() false +#endif + /* Triton Core internal information (BEGIN) */
/* Last - for index max*/ @@ -557,6 +563,14 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) return PTR_ERR(child); }
+ if (twl_has_codec() && pdata->codec) { + child = add_child(1, "twl4030_codec", + pdata->codec, sizeof(*pdata->codec), + false, 0, 0); + if (IS_ERR(child)) + return PTR_ERR(child); + } + if (twl_has_regulator()) { /* child = add_regulator(TWL4030_REG_VPLL1, pdata->vpll1); diff --git a/include/linux/i2c/twl4030.h b/include/linux/i2c/twl4030.h index 2d02dfd..42d6c72 100644 --- a/include/linux/i2c/twl4030.h +++ b/include/linux/i2c/twl4030.h @@ -401,6 +401,23 @@ struct twl4030_power_data {
extern void twl4030_power_init(struct twl4030_power_data *triton2_scripts);
+struct twl4030_codec_audio_data { + unsigned int audio_mclk; + unsigned int ramp_delay_value; + unsigned int hs_extmute:1; + void (*set_hs_extmute)(int mute); +}; + +struct twl4030_codec_vibra_data { + unsigned int audio_mclk; + unsigned int coexist; +}; + +struct twl4030_codec_data { + struct twl4030_codec_audio_data *audio; + struct twl4030_codec_vibra_data *vibra; +}; + struct twl4030_platform_data { unsigned irq_base, irq_end; struct twl4030_bci_platform_data *bci; @@ -409,6 +426,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;
/* LDO regulators */ struct regulator_init_data *vdac; diff --git a/include/linux/mfd/twl4030-codec.h b/include/linux/mfd/twl4030-codec.h new file mode 100644 index 0000000..5fec31a --- /dev/null +++ b/include/linux/mfd/twl4030-codec.h @@ -0,0 +1,261 @@ +/* + * MFD driver for twl4030 codec submodule + * + * Author: Peter Ujfalusi peter.ujfalusi@nokia.com + * + * 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 + +/* 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); + +#endif /* End of __TWL4030_CODEC_H__ */
Add needed platform data for the twl4030_codec MFD on boards, where the audio part of the twl4030 codec is used.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@nokia.com --- arch/arm/mach-omap2/board-3430sdp.c | 9 +++++++++ arch/arm/mach-omap2/board-omap3beagle.c | 9 +++++++++ arch/arm/mach-omap2/board-omap3evm.c | 9 +++++++++ arch/arm/mach-omap2/board-omap3pandora.c | 9 +++++++++ arch/arm/mach-omap2/board-overo.c | 9 +++++++++ arch/arm/mach-omap2/board-zoom2.c | 9 +++++++++ 6 files changed, 54 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-omap2/board-3430sdp.c b/arch/arm/mach-omap2/board-3430sdp.c index efaf053..4f91f7a 100644 --- a/arch/arm/mach-omap2/board-3430sdp.c +++ b/arch/arm/mach-omap2/board-3430sdp.c @@ -410,6 +410,14 @@ static struct regulator_init_data sdp3430_vpll2 = { .consumer_supplies = &sdp3430_vdvi_supply, };
+static struct twl4030_codec_audio_data sdp3430_audio = { + .audio_mclk = 26000000, +}; + +static struct twl4030_codec_data sdp3430_codec = { + .audio = &sdp3430_audio, +}; + static struct twl4030_platform_data sdp3430_twldata = { .irq_base = TWL4030_IRQ_BASE, .irq_end = TWL4030_IRQ_END, @@ -420,6 +428,7 @@ static struct twl4030_platform_data sdp3430_twldata = { .madc = &sdp3430_madc_data, .keypad = &sdp3430_kp_data, .usb = &sdp3430_usb_data, + .codec = &sdp3430_codec,
.vaux1 = &sdp3430_vaux1, .vaux2 = &sdp3430_vaux2, diff --git a/arch/arm/mach-omap2/board-omap3beagle.c b/arch/arm/mach-omap2/board-omap3beagle.c index 70df6b4..2161d85 100644 --- a/arch/arm/mach-omap2/board-omap3beagle.c +++ b/arch/arm/mach-omap2/board-omap3beagle.c @@ -254,6 +254,14 @@ static struct twl4030_usb_data beagle_usb_data = { .usb_mode = T2_USB_MODE_ULPI, };
+static struct twl4030_codec_audio_data beagle_audio_data = { + .audio_mclk = 26000000, +}; + +static struct twl4030_codec_data beagle_codec_data = { + .audio = &beagle_audio_data, +}; + static struct twl4030_platform_data beagle_twldata = { .irq_base = TWL4030_IRQ_BASE, .irq_end = TWL4030_IRQ_END, @@ -261,6 +269,7 @@ static struct twl4030_platform_data beagle_twldata = { /* platform_data for children goes here */ .usb = &beagle_usb_data, .gpio = &beagle_gpio_data, + .codec = &beagle_codec_data, .vmmc1 = &beagle_vmmc1, .vsim = &beagle_vsim, .vdac = &beagle_vdac, diff --git a/arch/arm/mach-omap2/board-omap3evm.c b/arch/arm/mach-omap2/board-omap3evm.c index e4ec0c5..d9a6103 100644 --- a/arch/arm/mach-omap2/board-omap3evm.c +++ b/arch/arm/mach-omap2/board-omap3evm.c @@ -194,6 +194,14 @@ static struct twl4030_madc_platform_data omap3evm_madc_data = { .irq_line = 1, };
+static struct twl4030_codec_audio_data omap3evm_audio_data = { + .audio_mclk = 26000000, +}; + +static struct twl4030_codec_data omap3evm_codec_data = { + .audio = &omap3evm_audio_data, +}; + static struct twl4030_platform_data omap3evm_twldata = { .irq_base = TWL4030_IRQ_BASE, .irq_end = TWL4030_IRQ_END, @@ -203,6 +211,7 @@ static struct twl4030_platform_data omap3evm_twldata = { .madc = &omap3evm_madc_data, .usb = &omap3evm_usb_data, .gpio = &omap3evm_gpio_data, + .codec = &omap3evm_codec_data, };
static struct i2c_board_info __initdata omap3evm_i2c_boardinfo[] = { diff --git a/arch/arm/mach-omap2/board-omap3pandora.c b/arch/arm/mach-omap2/board-omap3pandora.c index 7f6bf87..5036b56 100644 --- a/arch/arm/mach-omap2/board-omap3pandora.c +++ b/arch/arm/mach-omap2/board-omap3pandora.c @@ -281,11 +281,20 @@ static struct twl4030_usb_data omap3pandora_usb_data = { .usb_mode = T2_USB_MODE_ULPI, };
+static struct twl4030_codec_audio_data omap3pandora_audio_data = { + .audio_mclk = 26000000, +}; + +static struct twl4030_codec_data omap3pandora_codec_data = { + .audio = &omap3pandora_audio_data, +}; + static struct twl4030_platform_data omap3pandora_twldata = { .irq_base = TWL4030_IRQ_BASE, .irq_end = TWL4030_IRQ_END, .gpio = &omap3pandora_gpio_data, .usb = &omap3pandora_usb_data, + .codec = &omap3pandora_codec_data, .vmmc1 = &pandora_vmmc1, .vmmc2 = &pandora_vmmc2, .keypad = &pandora_kp_data, diff --git a/arch/arm/mach-omap2/board-overo.c b/arch/arm/mach-omap2/board-overo.c index 9917d2f..dc55008 100644 --- a/arch/arm/mach-omap2/board-overo.c +++ b/arch/arm/mach-omap2/board-overo.c @@ -329,6 +329,14 @@ static struct regulator_init_data overo_vmmc1 = { .consumer_supplies = &overo_vmmc1_supply, };
+static struct twl4030_codec_audio_data overo_audio_data = { + .audio_mclk = 26000000, +}; + +static struct twl4030_codec_data overo_codec_data = { + .audio = &overo_audio_data, +}; + /* mmc2 (WLAN) and Bluetooth don't use twl4030 regulators */
static struct twl4030_platform_data overo_twldata = { @@ -336,6 +344,7 @@ static struct twl4030_platform_data overo_twldata = { .irq_end = TWL4030_IRQ_END, .gpio = &overo_gpio_data, .usb = &overo_usb_data, + .codec = &overo_codec_data, .vmmc1 = &overo_vmmc1, };
diff --git a/arch/arm/mach-omap2/board-zoom2.c b/arch/arm/mach-omap2/board-zoom2.c index b7b3220..f1b4e7c 100644 --- a/arch/arm/mach-omap2/board-zoom2.c +++ b/arch/arm/mach-omap2/board-zoom2.c @@ -229,6 +229,14 @@ static struct twl4030_madc_platform_data zoom2_madc_data = { .irq_line = 1, };
+static struct twl4030_codec_audio_data zoom2_audio_data = { + .audio_mclk = 26000000, +}; + +static struct twl4030_codec_data zoom2_codec_data = { + .audio = &zoom2_audio_data, +}; + static struct twl4030_platform_data zoom2_twldata = { .irq_base = TWL4030_IRQ_BASE, .irq_end = TWL4030_IRQ_END, @@ -239,6 +247,7 @@ static struct twl4030_platform_data zoom2_twldata = { .usb = &zoom2_usb_data, .gpio = &zoom2_gpio_data, .keypad = &zoom2_kp_twl4030_data, + .codec = &zoom2_codec_data, .vmmc1 = &zoom2_vmmc1, .vmmc2 = &zoom2_vmmc2, .vsim = &zoom2_vsim,
Do not rewrite the whole register, but only update the needed bits in set_dai_sysclk functions.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@nokia.com --- sound/soc/codecs/twl4030.c | 22 ++++++++++++---------- 1 files changed, 12 insertions(+), 10 deletions(-)
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 4df7c6c..559e9b2 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -1785,19 +1785,21 @@ static int twl4030_set_dai_sysclk(struct snd_soc_dai *codec_dai, { struct snd_soc_codec *codec = codec_dai->codec; struct twl4030_priv *twl4030 = codec->private_data; - u8 infreq; + u8 apll_ctrl;
+ apll_ctrl = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL); + apll_ctrl &= ~TWL4030_APLL_INFREQ; switch (freq) { case 19200000: - infreq = TWL4030_APLL_INFREQ_19200KHZ; + apll_ctrl |= TWL4030_APLL_INFREQ_19200KHZ; twl4030->sysclk = 19200; break; case 26000000: - infreq = TWL4030_APLL_INFREQ_26000KHZ; + apll_ctrl |= TWL4030_APLL_INFREQ_26000KHZ; twl4030->sysclk = 26000; break; case 38400000: - infreq = TWL4030_APLL_INFREQ_38400KHZ; + apll_ctrl |= TWL4030_APLL_INFREQ_38400KHZ; twl4030->sysclk = 38400; break; default: @@ -1806,8 +1808,7 @@ static int twl4030_set_dai_sysclk(struct snd_soc_dai *codec_dai, return -EINVAL; }
- infreq |= TWL4030_APLL_EN; - twl4030_write(codec, TWL4030_REG_APLL_CTL, infreq); + twl4030_write(codec, TWL4030_REG_APLL_CTL, apll_ctrl);
return 0; } @@ -1989,11 +1990,13 @@ static int twl4030_voice_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; - u8 infreq; + u8 apll_ctrl;
+ apll_ctrl = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL); + apll_ctrl &= ~TWL4030_APLL_INFREQ; switch (freq) { case 26000000: - infreq = TWL4030_APLL_INFREQ_26000KHZ; + apll_ctrl |= TWL4030_APLL_INFREQ_26000KHZ; break; default: printk(KERN_ERR "TWL4030 voice set sysclk: unknown rate %d\n", @@ -2001,8 +2004,7 @@ static int twl4030_voice_set_dai_sysclk(struct snd_soc_dai *codec_dai, return -EINVAL; }
- infreq |= TWL4030_APLL_EN; - twl4030_write(codec, TWL4030_REG_APLL_CTL, infreq); + twl4030_write(codec, TWL4030_REG_APLL_CTL, apll_ctrl);
return 0; }
TWL4030 codec is now using the device registration via tlw4030_codec MFD device.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@nokia.com --- sound/soc/codecs/Kconfig | 2 +- sound/soc/codecs/twl4030.c | 203 +++++++++++++++++++++++-------------- sound/soc/codecs/twl4030.h | 242 +------------------------------------------ 3 files changed, 133 insertions(+), 314 deletions(-)
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index d30fce7..3d3ae0a 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -31,7 +31,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_TLV320AIC3X if I2C select SND_SOC_TPA6130A2 if I2C select SND_SOC_TLV320DAC33 if I2C - select SND_SOC_TWL4030 if TWL4030_CORE + select SND_SOC_TWL4030 if TWL4030_CODEC select SND_SOC_UDA134X select SND_SOC_UDA1380 if I2C select SND_SOC_WM8350 if MFD_WM8350 diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 559e9b2..78dde5d 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -120,6 +120,8 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = {
/* codec private data */ struct twl4030_priv { + struct snd_soc_codec codec; + unsigned int bypass_state; unsigned int codec_powered; unsigned int codec_muted; @@ -183,19 +185,20 @@ static int twl4030_write(struct snd_soc_codec *codec, static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable) { struct twl4030_priv *twl4030 = codec->private_data; - u8 mode; + int mode;
if (enable == twl4030->codec_powered) return;
- mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE); if (enable) - mode |= TWL4030_CODECPDZ; + mode = twl4030_codec_enable_resource(TWL4030_CODEC_RES_POWER); else - mode &= ~TWL4030_CODECPDZ; + mode = twl4030_codec_disable_resource(TWL4030_CODEC_RES_POWER);
- twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode); - twl4030->codec_powered = enable; + if (mode >= 0) { + twl4030_write_reg_cache(codec, TWL4030_REG_CODEC_MODE, mode); + twl4030->codec_powered = enable; + }
/* REVISIT: this delay is present in TI sample drivers */ /* but there seems to be no TRM requirement for it */ @@ -219,22 +222,20 @@ static void twl4030_init_chip(struct snd_soc_codec *codec) static void twl4030_codec_mute(struct snd_soc_codec *codec, int mute) { struct twl4030_priv *twl4030 = codec->private_data; - u8 reg_val; + int status;
if (mute == twl4030->codec_muted) return;
- if (mute) { + if (mute) /* Disable PLL */ - reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL); - reg_val &= ~TWL4030_APLL_EN; - twl4030_write(codec, TWL4030_REG_APLL_CTL, reg_val); - } else { + status = twl4030_codec_disable_resource(TWL4030_CODEC_RES_APLL); + else /* Enable PLL */ - reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL); - reg_val |= TWL4030_APLL_EN; - twl4030_write(codec, TWL4030_REG_APLL_CTL, reg_val); - } + status = twl4030_codec_enable_resource(TWL4030_CODEC_RES_APLL); + + if (status >= 0) + twl4030_write_reg_cache(codec, TWL4030_REG_APLL_CTL, status);
twl4030->codec_muted = mute; } @@ -2123,7 +2124,7 @@ struct snd_soc_dai twl4030_dai[] = { }; EXPORT_SYMBOL_GPL(twl4030_dai);
-static int twl4030_suspend(struct platform_device *pdev, pm_message_t state) +static int twl4030_soc_suspend(struct platform_device *pdev, pm_message_t state) { struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_soc_codec *codec = socdev->card->codec; @@ -2133,7 +2134,7 @@ static int twl4030_suspend(struct platform_device *pdev, pm_message_t state) return 0; }
-static int twl4030_resume(struct platform_device *pdev) +static int twl4030_soc_resume(struct platform_device *pdev) { struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_soc_codec *codec = socdev->card->codec; @@ -2143,32 +2144,21 @@ static int twl4030_resume(struct platform_device *pdev) return 0; }
-/* - * initialize the driver - * register the mixer and dsp interfaces with the kernel - */ +static struct snd_soc_codec *twl4030_codec;
-static int twl4030_init(struct snd_soc_device *socdev) +static int twl4030_soc_probe(struct platform_device *pdev) { - struct snd_soc_codec *codec = socdev->card->codec; + struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct twl4030_setup_data *setup = socdev->codec_data; - struct twl4030_priv *twl4030 = codec->private_data; - int ret = 0; + struct snd_soc_codec *codec; + struct twl4030_priv *twl4030; + int ret;
- printk(KERN_INFO "TWL4030 Audio Codec init \n"); + BUG_ON(!twl4030_codec);
- codec->name = "twl4030"; - codec->owner = THIS_MODULE; - codec->read = twl4030_read_reg_cache; - codec->write = twl4030_write; - codec->set_bias_level = twl4030_set_bias_level; - codec->dai = twl4030_dai; - codec->num_dai = ARRAY_SIZE(twl4030_dai), - codec->reg_cache_size = sizeof(twl4030_reg); - codec->reg_cache = kmemdup(twl4030_reg, sizeof(twl4030_reg), - GFP_KERNEL); - if (codec->reg_cache == NULL) - return -ENOMEM; + codec = twl4030_codec; + twl4030 = codec->private_data; + socdev->card->codec = codec;
/* Configuration for headset ramp delay from setup data */ if (setup) { @@ -2187,103 +2177,162 @@ static int twl4030_init(struct snd_soc_device *socdev) twl4030->sysclk = 26000; }
+ twl4030_init_chip(codec); + /* register pcms */ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); if (ret < 0) { - printk(KERN_ERR "twl4030: failed to create pcms\n"); - goto pcm_err; + dev_err(&pdev->dev, "failed to create pcms\n"); + return ret; }
- twl4030_init_chip(codec); - - /* power on device */ - twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - snd_soc_add_controls(codec, twl4030_snd_controls, ARRAY_SIZE(twl4030_snd_controls)); twl4030_add_widgets(codec);
+ twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + ret = snd_soc_init_card(socdev); if (ret < 0) { - printk(KERN_ERR "twl4030: failed to register card\n"); + dev_err(&pdev->dev, "failed to register card\n"); goto card_err; }
- return ret; + return 0;
card_err: snd_soc_free_pcms(socdev); snd_soc_dapm_free(socdev); -pcm_err: - kfree(codec->reg_cache); + return ret; }
-static struct snd_soc_device *twl4030_socdev; - -static int twl4030_probe(struct platform_device *pdev) +static int twl4030_soc_remove(struct platform_device *pdev) { struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->card->codec; + + printk(KERN_INFO "TWL4030 Audio Codec remove\n"); + twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF); + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); + kfree(codec->private_data); + kfree(codec); + + return 0; +} + +static int __devinit twl4030_codec_probe(struct platform_device *pdev) +{ + struct twl4030_codec_audio_data *pdata = pdev->dev.platform_data; struct snd_soc_codec *codec; struct twl4030_priv *twl4030; + int ret;
- codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); - if (codec == NULL) - return -ENOMEM; + if (!pdata || !(pdata->audio_mclk == 19200000 || + pdata->audio_mclk == 26000000 || + pdata->audio_mclk == 38400000)) { + dev_err(&pdev->dev, "Invalid platform_data\n"); + return -EINVAL; + }
twl4030 = kzalloc(sizeof(struct twl4030_priv), GFP_KERNEL); if (twl4030 == NULL) { - kfree(codec); + dev_err(&pdev->dev, "Can not allocate memroy\n"); return -ENOMEM; }
+ codec = &twl4030->codec; codec->private_data = twl4030; - socdev->card->codec = codec; + codec->dev = &pdev->dev; + twl4030_dai[0].dev = &pdev->dev; + twl4030_dai[1].dev = &pdev->dev; + mutex_init(&codec->mutex); INIT_LIST_HEAD(&codec->dapm_widgets); INIT_LIST_HEAD(&codec->dapm_paths);
- twl4030_socdev = socdev; - twl4030_init(socdev); + codec->name = "twl4030"; + codec->owner = THIS_MODULE; + codec->read = twl4030_read_reg_cache; + codec->write = twl4030_write; + codec->set_bias_level = twl4030_set_bias_level; + codec->dai = twl4030_dai; + codec->num_dai = ARRAY_SIZE(twl4030_dai), + codec->reg_cache_size = sizeof(twl4030_reg); + codec->reg_cache = kmemdup(twl4030_reg, sizeof(twl4030_reg), + GFP_KERNEL); + if (codec->reg_cache == NULL) { + ret = -ENOMEM; + goto error_cache; + } + + platform_set_drvdata(pdev, twl4030); + twl4030_codec = codec; + + ret = snd_soc_register_codec(codec); + if (ret != 0) { + dev_err(codec->dev, "Failed to register codec: %d\n", ret); + goto error_codec; + } + + ret = snd_soc_register_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai)); + if (ret != 0) { + dev_err(codec->dev, "Failed to register DAIs: %d\n", ret); + snd_soc_unregister_codec(codec); + goto error_codec; + }
return 0; + +error_codec: + kfree(codec->reg_cache); +error_cache: + kfree(twl4030); + return ret; }
-static int twl4030_remove(struct platform_device *pdev) +static int __devexit twl4030_codec_remove(struct platform_device *pdev) { - struct snd_soc_device *socdev = platform_get_drvdata(pdev); - struct snd_soc_codec *codec = socdev->card->codec; + struct twl4030_priv *twl4030 = platform_get_drvdata(pdev);
- printk(KERN_INFO "TWL4030 Audio Codec remove\n"); - twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF); - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); - kfree(codec->private_data); - kfree(codec); + kfree(twl4030);
+ twl4030_codec = NULL; return 0; }
-struct snd_soc_codec_device soc_codec_dev_twl4030 = { - .probe = twl4030_probe, - .remove = twl4030_remove, - .suspend = twl4030_suspend, - .resume = twl4030_resume, +MODULE_ALIAS("platform:twl4030_codec:audio"); + +static struct platform_driver twl4030_codec_driver = { + .probe = twl4030_codec_probe, + .remove = __devexit_p(twl4030_codec_remove), + .driver = { + .name = "twl4030_codec_audio", + .owner = THIS_MODULE, + }, }; -EXPORT_SYMBOL_GPL(soc_codec_dev_twl4030);
static int __init twl4030_modinit(void) { - return snd_soc_register_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai)); + return platform_driver_register(&twl4030_codec_driver); } module_init(twl4030_modinit);
static void __exit twl4030_exit(void) { - snd_soc_unregister_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai)); + platform_driver_unregister(&twl4030_codec_driver); } module_exit(twl4030_exit);
+struct snd_soc_codec_device soc_codec_dev_twl4030 = { + .probe = twl4030_soc_probe, + .remove = twl4030_soc_remove, + .suspend = twl4030_soc_suspend, + .resume = twl4030_soc_resume, +}; +EXPORT_SYMBOL_GPL(soc_codec_dev_twl4030); + MODULE_DESCRIPTION("ASoC TWL4030 codec driver"); MODULE_AUTHOR("Steve Sakoman"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/twl4030.h b/sound/soc/codecs/twl4030.h index 2b4bfa2..dd6396e 100644 --- a/sound/soc/codecs/twl4030.h +++ b/sound/soc/codecs/twl4030.h @@ -22,245 +22,13 @@ #ifndef __TWL4030_AUDIO_H__ #define __TWL4030_AUDIO_H__
-#define TWL4030_REG_CODEC_MODE 0x1 -#define TWL4030_REG_OPTION 0x2 -#define TWL4030_REG_UNKNOWN 0x3 -#define TWL4030_REG_MICBIAS_CTL 0x4 -#define TWL4030_REG_ANAMICL 0x5 -#define TWL4030_REG_ANAMICR 0x6 -#define TWL4030_REG_AVADC_CTL 0x7 -#define TWL4030_REG_ADCMICSEL 0x8 -#define TWL4030_REG_DIGMIXING 0x9 -#define TWL4030_REG_ATXL1PGA 0xA -#define TWL4030_REG_ATXR1PGA 0xB -#define TWL4030_REG_AVTXL2PGA 0xC -#define TWL4030_REG_AVTXR2PGA 0xD -#define TWL4030_REG_AUDIO_IF 0xE -#define TWL4030_REG_VOICE_IF 0xF -#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 -#define TWL4030_REG_SW_SHADOW 0x4A +/* Register descriptions are here */ +#include <linux/mfd/twl4030-codec.h>
+/* Sgadow register used by the audio driver */ +#define TWL4030_REG_SW_SHADOW 0x4A #define TWL4030_CACHEREGNUM (TWL4030_REG_SW_SHADOW + 1)
-/* 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 - /* TWL4030_REG_SW_SHADOW (0x4A) Fields */ #define TWL4030_HFL_EN 0x01 #define TWL4030_HFR_EN 0x02 @@ -279,3 +47,5 @@ struct twl4030_setup_data { };
#endif /* End of __TWL4030_AUDIO_H__ */ + +
On Mon, Oct 19, 2009 at 03:42:20PM +0300, Peter Ujfalusi wrote:
TWL4030 codec is now using the device registration via tlw4030_codec MFD device.
This looks pretty good but obviously depends on the MFD changes. The major thing that jumps out at me is the removal of the register definitions from the ASoC headers - it might be nice to have that done as part of the MFD patch, or as a separate patch.
You've also got the bias being brought up when the ASoC system comes up rather than when the driver comes up. To be honest it doesn't really make any difference either way, it's just slightly different to other drivers. What is useful with things like twl4030 which take a little while to come up is if you can do the bias bringup out of line from device probe, avoiding blocking system startup on CODEC bringup. That's definitely a separate patch, I'm just mentioning it for interest here.
There's also a couple of debug prints (like in the remove function) and
+MODULE_ALIAS("platform:twl4030_codec:audio");
Is that second colon right given...
- .driver = {
.name = "twl4030_codec_audio",
.owner = THIS_MODULE,
this.
On Tuesday 20 October 2009 13:25:40 ext Mark Brown wrote:
On Mon, Oct 19, 2009 at 03:42:20PM +0300, Peter Ujfalusi wrote:
TWL4030 codec is now using the device registration via tlw4030_codec MFD device.
This looks pretty good but obviously depends on the MFD changes.
Thanks, yes it all depends on the MFD changes.
The major thing that jumps out at me is the removal of the register definitions from the ASoC headers - it might be nice to have that done as part of the MFD patch, or as a separate patch.
The reason why I have done it like this is that with one patch I only touch one subsystem at the time: 1. MFD changes only 2. OMAP related changes 3. soc codec driver change
In patch 1, the register definitions had to be added, so that the twl4030_codec driver knows the registers (and there could be the vibra driver placed separately from the soc codec driver). In patch 3, where I modify the soc codec driver to use the new method, than I remove the definitions and use the existing header file, introduced by the first patch. All in all, after each patch the kernel can be builds, boots and works as before.
You've also got the bias being brought up when the ASoC system comes up rather than when the driver comes up. To be honest it doesn't really make any difference either way, it's just slightly different to other drivers.
I was thinking that if you built the kernel with SND_SOC_ALL_CODECS on OMAP platform for some reason and you don't actually use the twl4030 as audio device -> no machine driver, which would use it, than the codec part would be off. But yes, probably I can move the povering up to the probe function.
What is useful with things like twl4030 which take a little while to come up is if you can do the bias bringup out of line from device probe, avoiding blocking system startup on CODEC bringup. That's definitely a separate patch, I'm just mentioning it for interest here.
I'm sure there will be another round for this series, so I can make this change at the same time within the patch, since this anyway changes the way how the driver is loaded/probed.
There's also a couple of debug prints (like in the remove function) and
I'll get rid of them. But at least this time I did not had those unneeded casts, which I usually have ;)
+MODULE_ALIAS("platform:twl4030_codec:audio");
Is that second colon right given...
I'm not sure about it at all either. I did not found any other 'nested MFD' drivers around, so this is just a guess Should it be:
+MODULE_ALIAS("platform:twl4030_codec_audio");
- .driver = {
.name = "twl4030_codec_audio",
.owner = THIS_MODULE,
this.
On Tue, Oct 20, 2009 at 02:30:49PM +0300, Peter Ujfalusi wrote:
In patch 1, the register definitions had to be added, so that the twl4030_codec driver knows the registers (and there could be the vibra driver placed separately from the soc codec driver). In patch 3, where I modify the soc codec driver to use the new method, than I remove the definitions and use the existing header file, introduced by the first patch. All in all, after each patch the kernel can be builds, boots and works as before.
Sure, though I suspect patch 3 could just be split in two happily with an include. I don't really mind either way. If you are going to keep them as one patch it'd be good to call out the move in the changelog.
You've also got the bias being brought up when the ASoC system comes up rather than when the driver comes up. To be honest it doesn't really make any difference either way, it's just slightly different to other drivers.
I was thinking that if you built the kernel with SND_SOC_ALL_CODECS on OMAP platform for some reason and you don't actually use the twl4030 as audio device -> no machine driver, which would use it, than the codec part would be off. But yes, probably I can move the povering up to the probe function.
That's a good enough reason to leave things as they are, though really if you're building SND_SOC_ALL_CODECs in a production system you're being a bit strange.
+MODULE_ALIAS("platform:twl4030_codec:audio");
Is that second colon right given...
I'm not sure about it at all either. I did not found any other 'nested MFD' drivers around, so this is just a guess Should it be:
+MODULE_ALIAS("platform:twl4030_codec_audio");
Yes. The aliasing makes no reference to the parents of the device, it only cares about the device name - for the purposes of module loading and driver matching it's identical to any other platform device.
On Tuesday 20 October 2009 14:51:06 ext Mark Brown wrote:
Sure, though I suspect patch 3 could just be split in two happily with an include. I don't really mind either way. If you are going to keep them as one patch it'd be good to call out the move in the changelog.
I'll split the soc codec patch to two, one is changing the header the other is for the change in the codec driver.
That's a good enough reason to leave things as they are, though really if you're building SND_SOC_ALL_CODECs in a production system you're being a bit strange.
Fair enough.
+MODULE_ALIAS("platform:twl4030_codec_audio");
Yes. The aliasing makes no reference to the parents of the device, it only cares about the device name - for the purposes of module loading and driver matching it's identical to any other platform device.
Good to know, I'll change this as well.
Thanks, Péter
On Mon, Oct 19, 2009 at 03:42:19PM +0300, Peter Ujfalusi wrote:
- apll_ctrl = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL);
- apll_ctrl &= ~TWL4030_APLL_INFREQ; switch (freq) {
I'll apply this - it's idiomatic for the current driver - but you might want to look at snd_soc_update_bits() which does a read/modify/write cycle for you.
On Monday 19 October 2009 15:55:08 ext Mark Brown wrote:
On Mon, Oct 19, 2009 at 03:42:19PM +0300, Peter Ujfalusi wrote:
- apll_ctrl = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL);
- apll_ctrl &= ~TWL4030_APLL_INFREQ; switch (freq) {
I'll apply this - it's idiomatic for the current driver - but you might want to look at snd_soc_update_bits() which does a read/modify/write cycle for you.
Yeah, it is a good idea. I'll address this when I go through the soc codec driver in my planned revisit and cleanup round quite soon.
Thanks, Péter
* Peter Ujfalusi peter.ujfalusi@nokia.com [091019 05:42]:
Add needed platform data for the twl4030_codec MFD on boards, where the audio part of the twl4030 codec is used.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@nokia.com
This looks safe to merge with the twl/audio patches to keep them together. So not adding to my queue.
Acked-by: Tony Lindgren tony@atomide.com
arch/arm/mach-omap2/board-3430sdp.c | 9 +++++++++ arch/arm/mach-omap2/board-omap3beagle.c | 9 +++++++++ arch/arm/mach-omap2/board-omap3evm.c | 9 +++++++++ arch/arm/mach-omap2/board-omap3pandora.c | 9 +++++++++ arch/arm/mach-omap2/board-overo.c | 9 +++++++++ arch/arm/mach-omap2/board-zoom2.c | 9 +++++++++ 6 files changed, 54 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-omap2/board-3430sdp.c b/arch/arm/mach-omap2/board-3430sdp.c index efaf053..4f91f7a 100644 --- a/arch/arm/mach-omap2/board-3430sdp.c +++ b/arch/arm/mach-omap2/board-3430sdp.c @@ -410,6 +410,14 @@ static struct regulator_init_data sdp3430_vpll2 = { .consumer_supplies = &sdp3430_vdvi_supply, };
+static struct twl4030_codec_audio_data sdp3430_audio = {
- .audio_mclk = 26000000,
+};
+static struct twl4030_codec_data sdp3430_codec = {
- .audio = &sdp3430_audio,
+};
static struct twl4030_platform_data sdp3430_twldata = { .irq_base = TWL4030_IRQ_BASE, .irq_end = TWL4030_IRQ_END, @@ -420,6 +428,7 @@ static struct twl4030_platform_data sdp3430_twldata = { .madc = &sdp3430_madc_data, .keypad = &sdp3430_kp_data, .usb = &sdp3430_usb_data,
.codec = &sdp3430_codec,
.vaux1 = &sdp3430_vaux1, .vaux2 = &sdp3430_vaux2,
diff --git a/arch/arm/mach-omap2/board-omap3beagle.c b/arch/arm/mach-omap2/board-omap3beagle.c index 70df6b4..2161d85 100644 --- a/arch/arm/mach-omap2/board-omap3beagle.c +++ b/arch/arm/mach-omap2/board-omap3beagle.c @@ -254,6 +254,14 @@ static struct twl4030_usb_data beagle_usb_data = { .usb_mode = T2_USB_MODE_ULPI, };
+static struct twl4030_codec_audio_data beagle_audio_data = {
- .audio_mclk = 26000000,
+};
+static struct twl4030_codec_data beagle_codec_data = {
- .audio = &beagle_audio_data,
+};
static struct twl4030_platform_data beagle_twldata = { .irq_base = TWL4030_IRQ_BASE, .irq_end = TWL4030_IRQ_END, @@ -261,6 +269,7 @@ static struct twl4030_platform_data beagle_twldata = { /* platform_data for children goes here */ .usb = &beagle_usb_data, .gpio = &beagle_gpio_data,
- .codec = &beagle_codec_data, .vmmc1 = &beagle_vmmc1, .vsim = &beagle_vsim, .vdac = &beagle_vdac,
diff --git a/arch/arm/mach-omap2/board-omap3evm.c b/arch/arm/mach-omap2/board-omap3evm.c index e4ec0c5..d9a6103 100644 --- a/arch/arm/mach-omap2/board-omap3evm.c +++ b/arch/arm/mach-omap2/board-omap3evm.c @@ -194,6 +194,14 @@ static struct twl4030_madc_platform_data omap3evm_madc_data = { .irq_line = 1, };
+static struct twl4030_codec_audio_data omap3evm_audio_data = {
- .audio_mclk = 26000000,
+};
+static struct twl4030_codec_data omap3evm_codec_data = {
- .audio = &omap3evm_audio_data,
+};
static struct twl4030_platform_data omap3evm_twldata = { .irq_base = TWL4030_IRQ_BASE, .irq_end = TWL4030_IRQ_END, @@ -203,6 +211,7 @@ static struct twl4030_platform_data omap3evm_twldata = { .madc = &omap3evm_madc_data, .usb = &omap3evm_usb_data, .gpio = &omap3evm_gpio_data,
- .codec = &omap3evm_codec_data,
};
static struct i2c_board_info __initdata omap3evm_i2c_boardinfo[] = { diff --git a/arch/arm/mach-omap2/board-omap3pandora.c b/arch/arm/mach-omap2/board-omap3pandora.c index 7f6bf87..5036b56 100644 --- a/arch/arm/mach-omap2/board-omap3pandora.c +++ b/arch/arm/mach-omap2/board-omap3pandora.c @@ -281,11 +281,20 @@ static struct twl4030_usb_data omap3pandora_usb_data = { .usb_mode = T2_USB_MODE_ULPI, };
+static struct twl4030_codec_audio_data omap3pandora_audio_data = {
- .audio_mclk = 26000000,
+};
+static struct twl4030_codec_data omap3pandora_codec_data = {
- .audio = &omap3pandora_audio_data,
+};
static struct twl4030_platform_data omap3pandora_twldata = { .irq_base = TWL4030_IRQ_BASE, .irq_end = TWL4030_IRQ_END, .gpio = &omap3pandora_gpio_data, .usb = &omap3pandora_usb_data,
- .codec = &omap3pandora_codec_data, .vmmc1 = &pandora_vmmc1, .vmmc2 = &pandora_vmmc2, .keypad = &pandora_kp_data,
diff --git a/arch/arm/mach-omap2/board-overo.c b/arch/arm/mach-omap2/board-overo.c index 9917d2f..dc55008 100644 --- a/arch/arm/mach-omap2/board-overo.c +++ b/arch/arm/mach-omap2/board-overo.c @@ -329,6 +329,14 @@ static struct regulator_init_data overo_vmmc1 = { .consumer_supplies = &overo_vmmc1_supply, };
+static struct twl4030_codec_audio_data overo_audio_data = {
- .audio_mclk = 26000000,
+};
+static struct twl4030_codec_data overo_codec_data = {
- .audio = &overo_audio_data,
+};
/* mmc2 (WLAN) and Bluetooth don't use twl4030 regulators */
static struct twl4030_platform_data overo_twldata = { @@ -336,6 +344,7 @@ static struct twl4030_platform_data overo_twldata = { .irq_end = TWL4030_IRQ_END, .gpio = &overo_gpio_data, .usb = &overo_usb_data,
- .codec = &overo_codec_data, .vmmc1 = &overo_vmmc1,
};
diff --git a/arch/arm/mach-omap2/board-zoom2.c b/arch/arm/mach-omap2/board-zoom2.c index b7b3220..f1b4e7c 100644 --- a/arch/arm/mach-omap2/board-zoom2.c +++ b/arch/arm/mach-omap2/board-zoom2.c @@ -229,6 +229,14 @@ static struct twl4030_madc_platform_data zoom2_madc_data = { .irq_line = 1, };
+static struct twl4030_codec_audio_data zoom2_audio_data = {
- .audio_mclk = 26000000,
+};
+static struct twl4030_codec_data zoom2_codec_data = {
- .audio = &zoom2_audio_data,
+};
static struct twl4030_platform_data zoom2_twldata = { .irq_base = TWL4030_IRQ_BASE, .irq_end = TWL4030_IRQ_END, @@ -239,6 +247,7 @@ static struct twl4030_platform_data zoom2_twldata = { .usb = &zoom2_usb_data, .gpio = &zoom2_gpio_data, .keypad = &zoom2_kp_twl4030_data,
- .codec = &zoom2_codec_data, .vmmc1 = &zoom2_vmmc1, .vmmc2 = &zoom2_vmmc2, .vsim = &zoom2_vsim,
-- 1.6.5.1
On Mon, Oct 19, 2009 at 03:42:17PM +0300, Peter Ujfalusi wrote:
New MFD child to twl4030 MFD device. This MFD device will be used by the drivers, which needs resources from the twl4030 codec like audio and vibra.
The platform specific configuration data is passed along to the child drivers (audio, vibra).
Signed-off-by: Peter Ujfalusi peter.ujfalusi@nokia.com
+config TWL4030_CODEC
- bool "Support codec part of the TWL4030 family chips"
This seems like something that users shouldn't really have to worry about selecting explicitly - might it be better to have it selected by the drivers that use it instead? It feels like it's an implementation detail of the driver.
On Tuesday 20 October 2009 13:03:34 ext Mark Brown wrote:
On Mon, Oct 19, 2009 at 03:42:17PM +0300, Peter Ujfalusi wrote:
New MFD child to twl4030 MFD device. This MFD device will be used by the drivers, which needs resources from the twl4030 codec like audio and vibra.
The platform specific configuration data is passed along to the child drivers (audio, vibra).
Signed-off-by: Peter Ujfalusi peter.ujfalusi@nokia.com
+config TWL4030_CODEC
- bool "Support codec part of the TWL4030 family chips"
This seems like something that users shouldn't really have to worry about selecting explicitly - might it be better to have it selected by the drivers that use it instead? It feels like it's an implementation detail of the driver.
Yes, first I had the TWL4030_CODEC selected in the sound/soc/codecs/Kconfig, but for some reason I have decided to add user selectable config option instead and use the depends on in the codecs Makefile. I'll make the change as you suggested.
Hi Peter,
On Mon, Oct 19, 2009 at 03:42:17PM +0300, Peter Ujfalusi wrote:
New MFD child to twl4030 MFD device. This MFD device will be used by the drivers, which needs resources from the twl4030 codec like audio and vibra.
The platform specific configuration data is passed along to the child drivers (audio, vibra).
Some comments on your code:
+config TWL4030_CODEC
- bool "Support codec part of the TWL4030 family chips"
- depends on TWL4030_CORE
- help
Say yes here if you want to use the codec resources on the
TWL4030 family chips. The codec in TWL4030 provides the audio
functionality and also has the vibrator controls.
This driver provides MFD device to be used for drivers, which needs
access to the codec part, the board-specific data is passed from this
driver to the childs as platform data with the board specific
configuration.
As Mark noticed already, you dont really want users to explicitely select this obscure mfd driver to get their audio and vibre driver selectable. It should be the other way around, and I think you already agreed with that.
+static struct device * +twl4030_codec_new_child(const char *name, void *pdata, unsigned pdata_len) +{
- struct platform_device *pdev;
- int status = 0;
- pdev = platform_device_alloc(name, -1);
- if (!pdev) {
dev_dbg(&twl4030_codec_dev->dev, "can't alloc dev (%s)\n",
name);
return ERR_PTR(-ENOMEM);
- }
- pdev->dev.parent = &twl4030_codec_dev->dev;
- if (pdata) {
status = platform_device_add_data(pdev, pdata, pdata_len);
if (status < 0) {
dev_dbg(&pdev->dev, "can't add platform_data\n");
goto err;
}
- }
- status = platform_device_add(pdev);
- if (status < 0) {
dev_dbg(&pdev->dev, "Adding platform device failed\n");
goto err;
- }
- return &pdev->dev;
+err:
- platform_device_put(pdev);
- dev_err(&twl4030_codec_dev->dev, "can't add %s dev\n", name);
- return ERR_PTR(status);
+}
This could really use the mfd-core API, and avoid duplicating code. You just would have to define a couple cells, and call mfd_add_devices on them.
+static int __devexit twl4030_codec_remove(struct platform_device *pdev) +{
- struct twl4030_codec *codec = platform_get_drvdata(pdev);
- platform_set_drvdata(pdev, NULL);
- kfree(codec);
- twl4030_codec_dev = NULL;
- return 0;
+}
I think you're missing a platform_device_unregister() here (or an mfd_remove_devices() if you're going to switch to the mfd-core API)
Cheers, Samuel.
On Thursday 22 October 2009 02:13:11 ext Samuel Ortiz wrote:
As Mark noticed already, you dont really want users to explicitely select this obscure mfd driver to get their audio and vibre driver selectable. It should be the other way around, and I think you already agreed with that.
Yes, I have already made the modification.
+static struct device * +twl4030_codec_new_child(const char *name, void *pdata, unsigned pdata_len) +{
...
This could really use the mfd-core API, and avoid duplicating code. You just would have to define a couple cells, and call mfd_add_devices on them.
Good point. When I wrote this driver I have been looking at the twl4030-core driver, which is not using the mfd-core API. I'll make the change.
+static int __devexit twl4030_codec_remove(struct platform_device *pdev) +{
- struct twl4030_codec *codec = platform_get_drvdata(pdev);
- platform_set_drvdata(pdev, NULL);
- kfree(codec);
- twl4030_codec_dev = NULL;
- return 0;
+}
I think you're missing a platform_device_unregister() here (or an mfd_remove_devices() if you're going to switch to the mfd-core API)
You are right, although I have used the bool in the Kconfig for the twl4030- codec in a same way as the twl4030-core (and the core did not unregister the devices either), but yes, this is not correct so I'll fix it.
I have now question about the practicalities on how this series would be taken, and via which tree. The final patch for the soc codec driver depends on the change in MFD and in the OMAP board files. The change in the OMAP board files depends on the MFD changes, obviously.
So in order to have the soc codec changes the MFD and OMAP part has to be applied before, otherwise the audio will be broken.
the mfd-2.6:for-next branch has some patches against the twl4030-core in addition to the ones in l-o and in sound-2.6:topic/asoc.
I'll check, if the MFD patch applies to mfd-2.6:for-next also, but to have the soc codec changes the MFD patch should go to the sound-2.6 tree as well to make sure it is not braking things.
All-in-all, how these things can be handled?
Thanks, Péter
On Thu, Oct 22, 2009 at 09:04:32AM +0300, Peter Ujfalusi wrote:
I'll check, if the MFD patch applies to mfd-2.6:for-next also, but to have the soc codec changes the MFD patch should go to the sound-2.6 tree as well to make sure it is not braking things.
All-in-all, how these things can be handled?
The OMAP patch has been acked by Tony. Then we I'm fine with the mfd one and Mark is also ok with the remaining asoc one, all 3 patches have to go through one single tree.
It seems to me that this patchset is mostly an asoc one, even though all of those patches depend on the MFD one. So I'd perfectly fine if they'd all go through Mark's tree, and then I'd have to make sure I'm sending my 2.6.33 merge window pull request _after_ Mark's code is in Linus tree. Once it's there, I can work on merging conflicts with the few twl4030-core pending patches from my tree. Mark, what do you think ?
Cheers, Samuel.
Thanks, Péter
On Thu, Oct 22, 2009 at 09:57:49AM +0200, Samuel Ortiz wrote:
It seems to me that this patchset is mostly an asoc one, even though all of those patches depend on the MFD one. So I'd perfectly fine if they'd all go through Mark's tree, and then I'd have to make sure I'm sending my 2.6.33 merge window pull request _after_ Mark's code is in Linus tree. Once it's there, I can work on merging conflicts with the few twl4030-core pending patches from my tree. Mark, what do you think ?
Seems reasonable. I'll apply them on a separate branch and merge them into my 2.6.33 branch so that you can merge them into your tree too and avoid any cross-tree issues without needing to think about how things get merged with Linus (though since Takashi generally merges early it'll probably happen naturally anyway). Note that if you do pull this branch you won't be able to rebase your tree, you'd have to merge up Linus' tree instead (but that'd be handy anyway :) ).
On Thursday 22 October 2009 13:55:17 ext Mark Brown wrote:
On Thu, Oct 22, 2009 at 09:57:49AM +0200, Samuel Ortiz wrote:
It seems to me that this patchset is mostly an asoc one, even though all of those patches depend on the MFD one. So I'd perfectly fine if they'd all go through Mark's tree, and then I'd have to make sure I'm sending my 2.6.33 merge window pull request _after_ Mark's code is in Linus tree. Once it's there, I can work on merging conflicts with the few twl4030-core pending patches from my tree. Mark, what do you think ?
Seems reasonable. I'll apply them on a separate branch and merge them into my 2.6.33 branch so that you can merge them into your tree too and avoid any cross-tree issues without needing to think about how things get merged with Linus (though since Takashi generally merges early it'll probably happen naturally anyway). Note that if you do pull this branch you won't be able to rebase your tree, you'd have to merge up Linus' tree instead (but that'd be handy anyway :) ).
I just sent the second series, I'll hope the I have not missed anything. I was scratching my head on which tree it should be going, but it is really nice to see that it can be done like this. Thank you Samuel, Mark and Tony!
Hello Samuel,
On Monday 19 October 2009 15:42:16 Ujfalusi Peter (Nokia-D/Tampere) wrote:
Hello,
The following series adds new MFD device on top of the twl4030 MFD device for the codec part of the chip, and also converts the soc audio driver to use the correct probing (device model).
Reason for the twl4030_codec MFD: the vibra control is actually in the codec part of the twl4030. If both the vibra and the audio functionality is needed from the twl4030 at the same time, than they need to control the codec power and APLL at the same time without breaking the other driver. Also these two has to be able to work without the need for the other driver.
Have you had time to look at the series? I have made all the modification suggested by Mark Brown, and I'm holding back the second series (to have the comments from you for the initial series), so I can address those in the same round.
Thank you, Péter
participants (4)
-
Mark Brown
-
Peter Ujfalusi
-
Samuel Ortiz
-
Tony Lindgren