From da07ecd93b196819dcec488b7ebec69a71f3819e Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 11 Sep 2011 09:53:50 +0100 Subject: regulator: Implement deferred disable support It is a reasonably common pattern for hardware to require some delay after being quiesced before the disable has finalised, especially in mixed signal devices. For example, an active discharge may be required to ensure that the circuit starts up again in a known state. Avoid having to implement such delays in the regulator API by providing regulator_deferred_disable() which will do a regulator_disable() a specified number of milliseconds after it is called. Due to the reference counting done on regulators a deferred disable can be cancelled by doing another regulator_enable(). Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- drivers/regulator/core.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index d8e6a429e8ba..d0bde70f3466 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1552,6 +1552,63 @@ int regulator_force_disable(struct regulator *regulator) } EXPORT_SYMBOL_GPL(regulator_force_disable); +static void regulator_disable_work(struct work_struct *work) +{ + struct regulator_dev *rdev = container_of(work, struct regulator_dev, + disable_work.work); + int count, i, ret; + + mutex_lock(&rdev->mutex); + + BUG_ON(!rdev->deferred_disables); + + count = rdev->deferred_disables; + rdev->deferred_disables = 0; + + for (i = 0; i < count; i++) { + ret = _regulator_disable(rdev); + if (ret != 0) + rdev_err(rdev, "Deferred disable failed: %d\n", ret); + } + + mutex_unlock(&rdev->mutex); + + if (rdev->supply) { + for (i = 0; i < count; i++) { + ret = regulator_disable(rdev->supply); + if (ret != 0) { + rdev_err(rdev, + "Supply disable failed: %d\n", ret); + } + } + } +} + +/** + * regulator_disable_deferred - disable regulator output with delay + * @regulator: regulator source + * @ms: miliseconds until the regulator is disabled + * + * Execute regulator_disable() on the regulator after a delay. This + * is intended for use with devices that require some time to quiesce. + * + * NOTE: this will only disable the regulator output if no other consumer + * devices have it enabled, the regulator device supports disabling and + * machine constraints permit this operation. + */ +int regulator_disable_deferred(struct regulator *regulator, int ms) +{ + struct regulator_dev *rdev = regulator->rdev; + + mutex_lock(&rdev->mutex); + rdev->deferred_disables++; + mutex_unlock(&rdev->mutex); + + return schedule_delayed_work(&rdev->disable_work, + msecs_to_jiffies(ms)); +} +EXPORT_SYMBOL_GPL(regulator_disable_deferred); + static int _regulator_is_enabled(struct regulator_dev *rdev) { /* If we don't know then assume that the regulator is always on */ @@ -2622,6 +2679,7 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc, INIT_LIST_HEAD(&rdev->consumer_list); INIT_LIST_HEAD(&rdev->list); BLOCKING_INIT_NOTIFIER_HEAD(&rdev->notifier); + INIT_DELAYED_WORK(&rdev->disable_work, regulator_disable_work); /* preform any regulator specific init */ if (init_data->regulator_init) { @@ -2729,6 +2787,7 @@ void regulator_unregister(struct regulator_dev *rdev) #ifdef CONFIG_DEBUG_FS debugfs_remove_recursive(rdev->debugfs); #endif + flush_work_sync(&rdev->disable_work.work); WARN_ON(rdev->open_count); unset_regulator_supplies(rdev); list_del(&rdev->list); -- cgit v1.2.1 From 2d7c957e2ec0aebdd595a32b884a51270d34d28d Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 15 Sep 2011 15:39:23 +0300 Subject: MFD: twl6040: Remove global pointer for platform_device There is no need to keep global pointer for the platform device, since it is only used for dev_* prints, and the device pointer available within the twl6040 structure. Signed-off-by: Peter Ujfalusi Acked-by: Samuel Ortiz Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- drivers/mfd/twl6040-core.c | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/twl6040-core.c b/drivers/mfd/twl6040-core.c index 24d436c2fe4a..b0519e663be9 100644 --- a/drivers/mfd/twl6040-core.c +++ b/drivers/mfd/twl6040-core.c @@ -34,8 +34,6 @@ #include #include -static struct platform_device *twl6040_dev; - int twl6040_reg_read(struct twl6040 *twl6040, unsigned int reg) { int ret; @@ -203,11 +201,11 @@ static irqreturn_t twl6040_naudint_handler(int irq, void *data) if (intid & TWL6040_THINT) { status = twl6040_reg_read(twl6040, TWL6040_REG_STATUS); if (status & TWL6040_TSHUTDET) { - dev_warn(&twl6040_dev->dev, + dev_warn(twl6040->dev, "Thermal shutdown, powering-off"); twl6040_power(twl6040, 0); } else { - dev_warn(&twl6040_dev->dev, + dev_warn(twl6040->dev, "Leaving thermal shutdown, powering-on"); twl6040_power(twl6040, 1); } @@ -227,7 +225,7 @@ static int twl6040_power_up_completion(struct twl6040 *twl6040, if (!time_left) { intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID); if (!(intid & TWL6040_READYINT)) { - dev_err(&twl6040_dev->dev, + dev_err(twl6040->dev, "timeout waiting for READYINT\n"); return -ETIMEDOUT; } @@ -255,7 +253,7 @@ int twl6040_power(struct twl6040 *twl6040, int on) /* wait for power-up completion */ ret = twl6040_power_up_completion(twl6040, naudint); if (ret) { - dev_err(&twl6040_dev->dev, + dev_err(twl6040->dev, "automatic power-down failed\n"); twl6040->power_count = 0; goto out; @@ -264,7 +262,7 @@ int twl6040_power(struct twl6040 *twl6040, int on) /* use manual power-up sequence */ ret = twl6040_power_up(twl6040); if (ret) { - dev_err(&twl6040_dev->dev, + dev_err(twl6040->dev, "manual power-up failed\n"); twl6040->power_count = 0; goto out; @@ -276,7 +274,7 @@ int twl6040_power(struct twl6040 *twl6040, int on) } else { /* already powered-down */ if (!twl6040->power_count) { - dev_err(&twl6040_dev->dev, + dev_err(twl6040->dev, "device is already powered-off\n"); ret = -EPERM; goto out; @@ -326,7 +324,7 @@ int twl6040_set_pll(struct twl6040 *twl6040, int pll_id, lppllctl &= ~TWL6040_LPLLFIN; break; default: - dev_err(&twl6040_dev->dev, + dev_err(twl6040->dev, "freq_out %d not supported\n", freq_out); ret = -EINVAL; goto pll_out; @@ -347,7 +345,7 @@ int twl6040_set_pll(struct twl6040 *twl6040, int pll_id, hppllctl); break; default: - dev_err(&twl6040_dev->dev, + dev_err(twl6040->dev, "freq_in %d not supported\n", freq_in); ret = -EINVAL; goto pll_out; @@ -356,7 +354,7 @@ int twl6040_set_pll(struct twl6040 *twl6040, int pll_id, case TWL6040_SYSCLK_SEL_HPPLL: /* high-performance PLL can provide only 19.2 MHz */ if (freq_out != 19200000) { - dev_err(&twl6040_dev->dev, + dev_err(twl6040->dev, "freq_out %d not supported\n", freq_out); ret = -EINVAL; goto pll_out; @@ -389,7 +387,7 @@ int twl6040_set_pll(struct twl6040 *twl6040, int pll_id, TWL6040_HPLLENA; break; default: - dev_err(&twl6040_dev->dev, + dev_err(twl6040->dev, "freq_in %d not supported\n", freq_in); ret = -EINVAL; goto pll_out; @@ -406,7 +404,7 @@ int twl6040_set_pll(struct twl6040 *twl6040, int pll_id, twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl); break; default: - dev_err(&twl6040_dev->dev, "unknown pll id %d\n", pll_id); + dev_err(twl6040->dev, "unknown pll id %d\n", pll_id); ret = -EINVAL; goto pll_out; } @@ -471,7 +469,6 @@ static int __devinit twl6040_probe(struct platform_device *pdev) platform_set_drvdata(pdev, twl6040); - twl6040_dev = pdev; twl6040->dev = &pdev->dev; twl6040->audpwron = pdata->audpwron_gpio; twl6040->irq = pdata->naudint_irq; @@ -566,7 +563,6 @@ gpio2_err: gpio1_err: platform_set_drvdata(pdev, NULL); kfree(twl6040); - twl6040_dev = NULL; return ret; } @@ -586,7 +582,6 @@ static int __devexit twl6040_remove(struct platform_device *pdev) mfd_remove_devices(&pdev->dev); platform_set_drvdata(pdev, NULL); kfree(twl6040); - twl6040_dev = NULL; return 0; } -- cgit v1.2.1 From a69882aec380512e5d6acff9bfc4336dc5162bb4 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 15 Sep 2011 15:39:24 +0300 Subject: MFD: twl6040: Add accessor for revision ID For client driver to use, if they need chip resvision information. Signed-off-by: Peter Ujfalusi Acked-by: Samuel Ortiz Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- drivers/mfd/twl6040-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mfd/twl6040-core.c b/drivers/mfd/twl6040-core.c index b0519e663be9..51c3b47be655 100644 --- a/drivers/mfd/twl6040-core.c +++ b/drivers/mfd/twl6040-core.c @@ -491,7 +491,7 @@ static int __devinit twl6040_probe(struct platform_device *pdev) } /* ERRATA: Automatic power-up is not possible in ES1.0 */ - if (twl6040->rev == TWL6040_REV_ES1_0) + if (twl6040_get_revid(twl6040) == TWL6040_REV_ES1_0) twl6040->audpwron = -EINVAL; /* codec interrupt */ -- cgit v1.2.1 From 7e968985cb82c011403432c2f2dbd18660780679 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 15 Sep 2011 15:39:25 +0300 Subject: Input: twl6040-vibra: Use accessor to get revision information Signed-off-by: Peter Ujfalusi Acked-by: Dmitry Torokhov Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- drivers/input/misc/twl6040-vibra.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/input/misc/twl6040-vibra.c b/drivers/input/misc/twl6040-vibra.c index c43002e7ec72..154b7a324d67 100644 --- a/drivers/input/misc/twl6040-vibra.c +++ b/drivers/input/misc/twl6040-vibra.c @@ -97,7 +97,7 @@ static void twl6040_vibra_enable(struct vibra_info *info) } twl6040_power(info->twl6040, 1); - if (twl6040->rev <= TWL6040_REV_ES1_1) { + if (twl6040_get_revid(twl6040) <= TWL6040_REV_ES1_1) { /* * ERRATA: Disable overcurrent protection for at least * 3ms when enabling vibrator drivers to avoid false -- cgit v1.2.1 From 77f63e06cb5d5127e6f78347db01e092b97e111e Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 15 Sep 2011 15:39:26 +0300 Subject: MFD: twl6040: Fix power on GPIO handling Avoid requesting the audpwron gpio in case of ES1.0 revision. In the past we requested the gpio, but we did not free it up, since we made the check for the revision later. This results later checks for gpio validity to fail, leaving the gpio reserved (even after the driver has been removed). Signed-off-by: Peter Ujfalusi Acked-by: Samuel Ortiz Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- drivers/mfd/twl6040-core.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/twl6040-core.c b/drivers/mfd/twl6040-core.c index 51c3b47be655..7dc8c4715001 100644 --- a/drivers/mfd/twl6040-core.c +++ b/drivers/mfd/twl6040-core.c @@ -470,7 +470,6 @@ static int __devinit twl6040_probe(struct platform_device *pdev) platform_set_drvdata(pdev, twl6040); twl6040->dev = &pdev->dev; - twl6040->audpwron = pdata->audpwron_gpio; twl6040->irq = pdata->naudint_irq; twl6040->irq_base = pdata->irq_base; @@ -480,6 +479,12 @@ static int __devinit twl6040_probe(struct platform_device *pdev) twl6040->rev = twl6040_reg_read(twl6040, TWL6040_REG_ASICREV); + /* ERRATA: Automatic power-up is not possible in ES1.0 */ + if (twl6040_get_revid(twl6040) > TWL6040_REV_ES1_0) + twl6040->audpwron = pdata->audpwron_gpio; + else + twl6040->audpwron = -EINVAL; + if (gpio_is_valid(twl6040->audpwron)) { ret = gpio_request(twl6040->audpwron, "audpwron"); if (ret) @@ -490,10 +495,6 @@ static int __devinit twl6040_probe(struct platform_device *pdev) goto gpio2_err; } - /* ERRATA: Automatic power-up is not possible in ES1.0 */ - if (twl6040_get_revid(twl6040) == TWL6040_REV_ES1_0) - twl6040->audpwron = -EINVAL; - /* codec interrupt */ ret = twl6040_irq_init(twl6040); if (ret) -- cgit v1.2.1 From b1f43bf3a52b085b786adf0b719712df574955f9 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 24 May 2011 17:35:40 +0800 Subject: mfd: Add WM1811 support The WM1811 is mostly register compatible with the WM8994 and WM8958, providing a high performance audio hub CODEC in a small form factor suitable for ultra compact system designs. Signed-off-by: Mark Brown Acked-by: Samuel Ortiz --- drivers/mfd/wm8994-core.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index 96479c9b1728..7c13ab2c64a4 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -243,6 +243,18 @@ static struct mfd_cell wm8994_devs[] = { * and should be handled via the standard regulator API supply * management. */ +static const char *wm1811_main_supplies[] = { + "DBVDD1", + "DBVDD2", + "DBVDD3", + "DCVDD", + "AVDD1", + "AVDD2", + "CPVDD", + "SPKVDD1", + "SPKVDD2", +}; + static const char *wm8994_main_supplies[] = { "DBVDD", "DCVDD", @@ -401,6 +413,9 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq) } switch (wm8994->type) { + case WM1811: + wm8994->num_supplies = ARRAY_SIZE(wm1811_main_supplies); + break; case WM8994: wm8994->num_supplies = ARRAY_SIZE(wm8994_main_supplies); break; @@ -421,6 +436,10 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq) } switch (wm8994->type) { + case WM1811: + for (i = 0; i < ARRAY_SIZE(wm1811_main_supplies); i++) + wm8994->supplies[i].supply = wm1811_main_supplies[i]; + break; case WM8994: for (i = 0; i < ARRAY_SIZE(wm8994_main_supplies); i++) wm8994->supplies[i].supply = wm8994_main_supplies[i]; @@ -454,6 +473,13 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq) goto err_enable; } switch (ret) { + case 0x1811: + devname = "WM1811"; + if (wm8994->type != WM1811) + dev_warn(wm8994->dev, "Device registered as type %d\n", + wm8994->type); + wm8994->type = WM1811; + break; case 0x8994: devname = "WM8994"; if (wm8994->type != WM8994) @@ -651,6 +677,7 @@ static int wm8994_i2c_remove(struct i2c_client *i2c) } static const struct i2c_device_id wm8994_i2c_id[] = { + { "wm1811", WM1811 }, { "wm8994", WM8994 }, { "wm8958", WM8958 }, { } -- cgit v1.2.1 From aa59802dedac98dc95310a456121cec6a9d6b63f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 3 Oct 2011 22:42:43 +0100 Subject: regulator: Fix return code from regulator_disable_deferred() schedule_delayed_work() returns a bool indicating if the work was already queued when it succeeds so we need to squash a true down to zero. Signed-off-by: Mark Brown --- drivers/regulator/core.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index d0bde70f3466..9e4c123c4028 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1599,13 +1599,18 @@ static void regulator_disable_work(struct work_struct *work) int regulator_disable_deferred(struct regulator *regulator, int ms) { struct regulator_dev *rdev = regulator->rdev; + int ret; mutex_lock(&rdev->mutex); rdev->deferred_disables++; mutex_unlock(&rdev->mutex); - return schedule_delayed_work(&rdev->disable_work, - msecs_to_jiffies(ms)); + ret = schedule_delayed_work(&rdev->disable_work, + msecs_to_jiffies(ms)); + if (ret < 0) + return ret; + else + return 0; } EXPORT_SYMBOL_GPL(regulator_disable_deferred); -- cgit v1.2.1 From a1ff89ef3cd6515d378f946db5f3760089bb644e Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 24 Jul 2011 12:40:48 +0100 Subject: regulator: Add WM1811 support The WM1811 has a slightly different range on LDO2 to other WM8994 class devices. Signed-off-by: Mark Brown --- drivers/regulator/wm8994-regulator.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers') diff --git a/drivers/regulator/wm8994-regulator.c b/drivers/regulator/wm8994-regulator.c index 1a6a690f24db..b87bf5c841f8 100644 --- a/drivers/regulator/wm8994-regulator.c +++ b/drivers/regulator/wm8994-regulator.c @@ -140,6 +140,14 @@ static int wm8994_ldo2_list_voltage(struct regulator_dev *rdev, return (selector * 100000) + 900000; case WM8958: return (selector * 100000) + 1000000; + case WM1811: + switch (selector) { + case 0: + return -EINVAL; + default: + return (selector * 100000) + 950000; + } + break; default: return -EINVAL; } @@ -170,6 +178,11 @@ static int wm8994_ldo2_set_voltage(struct regulator_dev *rdev, case WM8958: selector = (min_uV - 1000000) / 100000; break; + case WM1811: + selector = (min_uV - 950000) / 100000; + if (selector == 0) + selector = 1; + break; default: return -EINVAL; } -- cgit v1.2.1 From 1e036f65329901a2432c92132b785654944743d9 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 12 Oct 2011 11:57:53 +0300 Subject: Input: twl6040: Simplify vibra regsiter definitions The bits within the two control registers (for left and right channel) are identical. Use common names for the bits acros the two register. Also add the missing definition for the path selection bit. Signed-off-by: Peter Ujfalusi Acked-by: Dmitry Torokhov Signed-off-by: Mark Brown --- drivers/input/misc/twl6040-vibra.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/input/misc/twl6040-vibra.c b/drivers/input/misc/twl6040-vibra.c index 154b7a324d67..cb741858229a 100644 --- a/drivers/input/misc/twl6040-vibra.c +++ b/drivers/input/misc/twl6040-vibra.c @@ -74,12 +74,12 @@ static irqreturn_t twl6040_vib_irq_handler(int irq, void *data) if (status & TWL6040_VIBLOCDET) { dev_warn(info->dev, "Left Vibrator overcurrent detected\n"); twl6040_clear_bits(twl6040, TWL6040_REG_VIBCTLL, - TWL6040_VIBENAL); + TWL6040_VIBENA); } if (status & TWL6040_VIBROCDET) { dev_warn(info->dev, "Right Vibrator overcurrent detected\n"); twl6040_clear_bits(twl6040, TWL6040_REG_VIBCTLR, - TWL6040_VIBENAR); + TWL6040_VIBENA); } return IRQ_HANDLED; @@ -104,16 +104,16 @@ static void twl6040_vibra_enable(struct vibra_info *info) * overcurrent detection */ twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLL, - TWL6040_VIBENAL | TWL6040_VIBCTRLL); + TWL6040_VIBENA | TWL6040_VIBCTRL); twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLR, - TWL6040_VIBENAR | TWL6040_VIBCTRLR); + TWL6040_VIBENA | TWL6040_VIBCTRL); usleep_range(3000, 3500); } twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLL, - TWL6040_VIBENAL); + TWL6040_VIBENA); twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLR, - TWL6040_VIBENAR); + TWL6040_VIBENA); info->enabled = true; } -- cgit v1.2.1 From 31b402e3c9eb839a00530511dcf7de47bbf723f6 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 12 Oct 2011 11:57:54 +0300 Subject: MFD: twl6040: Cache the vibra control registers The vibra control register will be used from the ASoC codec driver as well. In order to avoid latency issues caused by I2C read access, cache the two control register within the core driver, so we do not need to reach out to the chip to read it back. Signed-off-by: Peter Ujfalusi Acked-by: Samuel Ortiz Signed-off-by: Mark Brown --- drivers/mfd/twl6040-core.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/twl6040-core.c b/drivers/mfd/twl6040-core.c index 7dc8c4715001..75987c8ca049 100644 --- a/drivers/mfd/twl6040-core.c +++ b/drivers/mfd/twl6040-core.c @@ -34,16 +34,24 @@ #include #include +#define VIBRACTRL_MEMBER(reg) ((reg == TWL6040_REG_VIBCTLL) ? 0 : 1) + 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; + /* Vibra control registers from cache */ + if (unlikely(reg == TWL6040_REG_VIBCTLL || + reg == TWL6040_REG_VIBCTLR)) { + val = twl6040->vibra_ctrl_cache[VIBRACTRL_MEMBER(reg)]; + } else { + 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); @@ -57,6 +65,9 @@ int twl6040_reg_write(struct twl6040 *twl6040, unsigned int reg, u8 val) mutex_lock(&twl6040->io_mutex); ret = twl_i2c_write_u8(TWL_MODULE_AUDIO_VOICE, val, reg); + /* Cache the vibra control registers */ + if (reg == TWL6040_REG_VIBCTLL || reg == TWL6040_REG_VIBCTLR) + twl6040->vibra_ctrl_cache[VIBRACTRL_MEMBER(reg)] = val; mutex_unlock(&twl6040->io_mutex); return ret; -- cgit v1.2.1 From 70601ec10a2450369d554e49d708ab26deb17b66 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 12 Oct 2011 11:57:55 +0300 Subject: MFD: twl6040: function to query the vibra status for clients If the client only interested, if any of the vibra channels enabled, or if any of the channels are set to receive audio data via PDM. This function targets mainly the vibra driver, so it can check if it is allowed to execute effects ot not. Signed-off-by: Peter Ujfalusi Acked-by: Samuel Ortiz Signed-off-by: Mark Brown --- drivers/mfd/twl6040-core.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/twl6040-core.c b/drivers/mfd/twl6040-core.c index 75987c8ca049..268f80fd0439 100644 --- a/drivers/mfd/twl6040-core.c +++ b/drivers/mfd/twl6040-core.c @@ -444,6 +444,18 @@ unsigned int twl6040_get_sysclk(struct twl6040 *twl6040) } EXPORT_SYMBOL(twl6040_get_sysclk); +/* Get the combined status of the vibra control register */ +int twl6040_get_vibralr_status(struct twl6040 *twl6040) +{ + u8 status; + + status = twl6040->vibra_ctrl_cache[0] | twl6040->vibra_ctrl_cache[1]; + status &= (TWL6040_VIBENA | TWL6040_VIBSEL); + + return status; +} +EXPORT_SYMBOL(twl6040_get_vibralr_status); + static struct resource twl6040_vibra_rsrc[] = { { .flags = IORESOURCE_IRQ, -- cgit v1.2.1 From 5f07c32e289d159be3fc1e4f257e8cad5336f83a Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 12 Oct 2011 11:57:56 +0300 Subject: Input: twl6040-vibra: Check the selected path for vibra The VIBSELL/R bit in the VIBCTLL/R register tells the source of the data, which is going to be used to drive the attached motor(s). Do not allow effect execution if any of the channels are set to receive audio data. Signed-off-by: Peter Ujfalusi Acked-by: Dmitry Torokhov Signed-off-by: Mark Brown --- drivers/input/misc/twl6040-vibra.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/input/misc/twl6040-vibra.c b/drivers/input/misc/twl6040-vibra.c index cb741858229a..2a828e53db58 100644 --- a/drivers/input/misc/twl6040-vibra.c +++ b/drivers/input/misc/twl6040-vibra.c @@ -201,6 +201,13 @@ static int vibra_play(struct input_dev *input, void *data, struct vibra_info *info = input_get_drvdata(input); int ret; + /* Do not allow effect, while the routing is set to use audio */ + ret = twl6040_get_vibralr_status(info->twl6040); + if (ret & TWL6040_VIBSEL) { + dev_info(&input->dev, "Vibra is configured for audio\n"); + return -EBUSY; + } + 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; -- cgit v1.2.1