Merge branch 'peter/topic/for-tony' of git://gitorious.org/omap-audio/linux-audio into twl-asoc

This commit is contained in:
Tony Lindgren 2011-07-07 12:11:23 -07:00
commit 345e397d84
26 changed files with 2230 additions and 904 deletions

View file

@ -22,6 +22,7 @@
#include <linux/i2c/twl.h>
#include <linux/gpio_keys.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/fixed.h>
#include <linux/leds.h>
#include <linux/leds_pwm.h>
@ -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,33 @@ 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,
.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 = &twl6040_codec,
.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,

View file

@ -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,

View file

@ -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);

View file

@ -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)

View file

@ -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 */

View file

@ -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.
@ -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

View file

@ -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

View file

@ -28,7 +28,7 @@
#include <linux/platform_device.h>
#include <linux/workqueue.h>
#include <linux/i2c/twl.h>
#include <linux/mfd/twl4030-codec.h>
#include <linux/mfd/twl4030-audio.h>
#include <linux/input.h>
#include <linux/slab.h>
@ -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;
}
@ -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;

View file

@ -0,0 +1,423 @@
/*
* twl6040-vibra.c - TWL6040 Vibrator driver
*
* Author: Jorge Eduardo Candelaria <jorge.candelaria@ti.com>
* Author: Misael Lopez Cruz <misael.lopez@ti.com>
*
* Copyright: (C) 2011 Texas Instruments, Inc.
*
* Based on twl4030-vibra.c by Henrik Saari <henrik.saari@nokia.com>
* Felipe Balbi <felipe.balbi@nokia.com>
* Jari Vanhala <ext-javi.vanhala@nokia.com>
*
* 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/platform_device.h>
#include <linux/workqueue.h>
#include <linux/i2c/twl.h>
#include <linux/mfd/twl6040.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/regulator/consumer.h>
#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;
int irq;
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;
}
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();
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 = 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;
}
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:
free_irq(info->irq, 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);
free_irq(info->irq, 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 <jorge.candelaria@ti.com>");
MODULE_AUTHOR("Misael Lopez Cruz <misael.lopez@ti.com>");

View file

@ -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
@ -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

View file

@ -40,8 +40,9 @@ 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_TWL6040_CORE) += twl6040-core.o twl6040-irq.o
obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o

View file

@ -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
@ -815,20 +815,19 @@ 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),
child = add_child(sub_chip_id, "twl6040",
pdata->audio, sizeof(*pdata->audio),
false, 0, 0);
if (IS_ERR(child))
return PTR_ERR(child);

277
drivers/mfd/twl4030-audio.c Normal file
View file

@ -0,0 +1,277 @@
/*
* MFD driver for twl4030 audio submodule, which contains an audio codec, and
* the vibra control.
*
* Author: Peter Ujfalusi <peter.ujfalusi@ti.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/slab.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
#include <linux/i2c/twl.h>
#include <linux/mfd/core.h>
#include <linux/mfd/twl4030-audio.h>
#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_audio_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->codec) {
cell = &audio->cells[childs];
cell->name = "twl4030-codec";
cell->platform_data = pdata->codec;
cell->pdata_size = sizeof(*pdata->codec);
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 <peter.ujfalusi@ti.com>");
MODULE_LICENSE("GPL");

View file

@ -1,277 +0,0 @@
/*
* MFD driver for twl4030 codec submodule
*
* Author: Peter Ujfalusi <peter.ujfalusi@ti.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/slab.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
#include <linux/i2c/twl.h>
#include <linux/mfd/core.h>
#include <linux/mfd/twl4030-codec.h>
#define TWL4030_CODEC_CELLS 2
static struct platform_device *twl4030_codec_dev;
struct twl4030_codec_resource {
int request_count;
u8 reg;
u8 mask;
};
struct twl4030_codec {
unsigned int audio_mclk;
struct mutex mutex;
struct twl4030_codec_resource resource[TWL4030_CODEC_RES_MAX];
struct mfd_cell cells[TWL4030_CODEC_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)
{
struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
u8 val;
twl_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;
twl_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;
twl_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);
unsigned int twl4030_codec_get_mclk(void)
{
struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
return codec->audio_mclk;
}
EXPORT_SYMBOL_GPL(twl4030_codec_get_mclk);
static int __devinit twl4030_codec_probe(struct platform_device *pdev)
{
struct twl4030_codec *codec;
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);
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->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;
/* 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) {
cell = &codec->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->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, codec->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(codec);
twl4030_codec_dev = NULL;
return ret;
}
static int __devexit twl4030_codec_remove(struct platform_device *pdev)
{
struct twl4030_codec *codec = platform_get_drvdata(pdev);
mfd_remove_devices(&pdev->dev);
platform_set_drvdata(pdev, NULL);
kfree(codec);
twl4030_codec_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),
.driver = {
.owner = THIS_MODULE,
.name = "twl4030-audio",
},
};
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@ti.com>");
MODULE_LICENSE("GPL");

620
drivers/mfd/twl6040-core.c Normal file
View file

@ -0,0 +1,620 @@
/*
* MFD driver for TWL6040 audio device
*
* Authors: Misael Lopez Cruz <misael.lopez@ti.com>
* Jorge Eduardo Candelaria <jorge.candelaria@ti.com>
* Peter Ujfalusi <peter.ujfalusi@ti.com>
*
* 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 <linux/module.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/i2c/twl.h>
#include <linux/mfd/core.h>
#include <linux/mfd/twl6040.h>
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;
}
}
/* Default PLL configuration after power up */
twl6040->pll = TWL6040_SYSCLK_SEL_LPPLL;
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->sysclk = 0;
}
out:
mutex_unlock(&twl6040->mutex);
return ret;
}
EXPORT_SYMBOL(twl6040_power);
int twl6040_set_pll(struct twl6040 *twl6040, int pll_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 (pll_id) {
case TWL6040_SYSCLK_SEL_LPPLL:
/* 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;
}
break;
case TWL6040_SYSCLK_SEL_HPPLL:
/* 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);
break;
default:
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);
return ret;
}
EXPORT_SYMBOL(twl6040_set_pll);
int twl6040_get_pll(struct twl6040 *twl6040)
{
if (twl6040->power_count)
return twl6040->pll;
else
return -ENODEV;
}
EXPORT_SYMBOL(twl6040_get_pll);
unsigned int twl6040_get_sysclk(struct twl6040 *twl6040)
{
return twl6040->sysclk;
}
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;
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;
}
/* 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;
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;
/* codec interrupt */
ret = twl6040_irq_init(twl6040);
if (ret)
goto gpio2_err;
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);
goto irq_err;
}
/* dual-access registers controlled by I2C only */
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++;
}
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:
free_irq(twl6040->irq_base + TWL6040_IRQ_READY, twl6040);
irq_err:
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);
free_irq(twl6040->irq_base + TWL6040_IRQ_READY, twl6040);
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 <misael.lopez@ti.com>");
MODULE_AUTHOR("Jorge Eduardo Candelaria <jorge.candelaria@ti.com>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:twl6040");

191
drivers/mfd/twl6040-irq.c Normal file
View file

@ -0,0 +1,191 @@
/*
* Interrupt controller support for TWL6040
*
* Author: Misael Lopez Cruz <misael.lopez@ti.com>
*
* 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 <linux/kernel.h>
#include <linux/module.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/mfd/core.h>
#include <linux/mfd/twl6040.h>
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);
/* 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)
{
free_irq(twl6040->irq, twl6040);
}
EXPORT_SYMBOL(twl6040_irq_exit);

View file

@ -657,28 +657,41 @@ 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;
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);
};
struct twl4030_codec_vibra_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_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 */
int naudint_irq; /* audio interrupt */
unsigned int irq_base;
};
struct twl4030_platform_data {
@ -690,7 +703,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;

View file

@ -1,5 +1,5 @@
/*
* MFD driver for twl4030 codec submodule
* MFD driver for twl4030 audio submodule
*
* Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
*
@ -259,14 +259,14 @@
#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,
enum twl4030_audio_res {
TWL4030_AUDIO_RES_POWER = 0,
TWL4030_AUDIO_RES_APLL,
TWL4030_AUDIO_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);
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__ */

228
include/linux/mfd/twl6040.h Normal file
View file

@ -0,0 +1,228 @@
/*
* MFD driver for twl6040
*
* Authors: Jorge Eduardo Candelaria <jorge.candelaria@ti.com>
* Misael Lopez Cruz <misael.lopez@ti.com>
*
* 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 <linux/interrupt.h>
#include <linux/mfd/core.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_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
/* 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
/* PLL selection */
#define TWL6040_SYSCLK_SEL_LPPLL 0
#define TWL6040_SYSCLK_SEL_HPPLL 1
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;
int pll;
unsigned int sysclk;
unsigned int irq;
unsigned int irq_base;
u8 irq_masks_cur;
u8 irq_masks_cache;
};
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, int pll_id,
unsigned int freq_in, unsigned int freq_out);
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);
#endif /* End of __TWL6040_CODEC_H__ */

View file

@ -236,10 +236,11 @@ config SND_SOC_TLV320DAC33
tristate
config SND_SOC_TWL4030
select TWL4030_CODEC
select MFD_TWL4030_AUDIO
tristate
config SND_SOC_TWL6040
select TWL6040_CORE
tristate
config SND_SOC_UDA134X

View file

@ -36,7 +36,7 @@
#include <sound/tlv.h>
/* Register descriptions are here */
#include <linux/mfd/twl4030-codec.h>
#include <linux/mfd/twl4030-audio.h>
/* 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);
@ -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;
@ -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)
@ -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 */
@ -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);
@ -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");

File diff suppressed because it is too large Load diff

View file

@ -22,125 +22,8 @@
#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);
int twl6040_get_clk_id(struct snd_soc_codec *codec);
#endif /* End of __TWL6040_H__ */

View file

@ -36,7 +36,7 @@
#include <plat/mcbsp.h>
/* Register descriptions for twl4030 codec part */
#include <linux/mfd/twl4030-codec.h>
#include <linux/mfd/twl4030-audio.h>
#include "omap-mcbsp.h"
#include "omap-pcm.h"

View file

@ -21,6 +21,8 @@
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <linux/mfd/twl6040.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
@ -34,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)
{
@ -44,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,
@ -81,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),
@ -152,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));
@ -237,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:

View file

@ -32,7 +32,7 @@
#include <plat/mcbsp.h>
/* Register descriptions for twl4030 codec part */
#include <linux/mfd/twl4030-codec.h>
#include <linux/mfd/twl4030-audio.h>
#include "omap-mcbsp.h"
#include "omap-pcm.h"