[alsa-devel] [PATCH 0/8] ASoC/MFD/Input: twl6040: Support for audio driven vibra
Hello,
The following series adds support for the soc driver to route audio (PCM stream) to the vibra ports of the twl6040. We have ALSA controls for the vibra source selection (PDM, or Input/FF). The route can not be changed from Input/FF to PDM during ongoing effect coming from Input/FF. The Input/FF driver can not execute new effect, while the vibra source is configured to be PDM. TO get things working in legacy mode the omap-mcpdm, and twl6040 soc codec driver's channel count needed to be fixed.
To minimize the need for reads through the I2C bus, the MFD driver will cache the vibra control registers.
With this series we can route PCM audio to drive the twl6040 vibra output. In order to that we need to play 5 channel audio, and place the vibra sample at the 5th channel. This will change with ABE support (change == simplified).
The series depends on the previous two from me: ASoC: twl6040: Cleanups and fixes ASoC: omap-mcpdm/twl6040: Offset cancellation
Regards, Peter
PS: Samuel, Dmitry, Liam: will it be possible to take this through Liam (via audio), since we are going to have dependencies coming via audio.
--- Peter Ujfalusi (8): Input: twl6040: Simplify vibra regsiter definitions MFD: twl6040: Cache the vibra control registers MFD: twl6040: function to query the vibra status for clients Input: twl6040-vibra: Check the selected path for vibra ASoC: twl6040: Fix the number of channels for vibra ASoC: twl6040: Correct supported number of playback channels ASoC: omap-mcpdm: Correct the supported number of channels ASoC: twl6040: Support for vibra output paths
drivers/input/misc/twl6040-vibra.c | 19 ++++++--- drivers/mfd/twl6040-core.c | 31 ++++++++++++-- include/linux/mfd/twl6040.h | 24 +++++------ sound/soc/codecs/twl6040.c | 78 ++++++++++++++++++++++++++++++++++- sound/soc/omap/omap-mcpdm.c | 14 ++++--- 5 files changed, 134 insertions(+), 32 deletions(-)
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 peter.ujfalusi@ti.com --- drivers/input/misc/twl6040-vibra.c | 12 ++++++------ include/linux/mfd/twl6040.h | 20 +++++++------------- 2 files changed, 13 insertions(+), 19 deletions(-)
diff --git a/drivers/input/misc/twl6040-vibra.c b/drivers/input/misc/twl6040-vibra.c index 154b7a3..cb74185 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; } diff --git a/include/linux/mfd/twl6040.h b/include/linux/mfd/twl6040.h index 0bbb1e7..6059935 100644 --- a/include/linux/mfd/twl6040.h +++ b/include/linux/mfd/twl6040.h @@ -126,24 +126,18 @@ #define TWL6040_HSDACMODE (1 << 1) #define TWL6040_HSDRVMODE (1 << 3)
-/* VIBCTLL (0x18) fields */ +/* VIBCTLL/R (0x18/0x1A) fields */
-#define TWL6040_VIBENAL 0x01 -#define TWL6040_VIBCTRLL 0x04 -#define TWL6040_VIBCTRLLP 0x08 -#define TWL6040_VIBCTRLLN 0x10 +#define TWL6040_VIBENA (1 << 0) +#define TWL6040_VIBSEL (1 << 1) +#define TWL6040_VIBCTRL (1 << 2) +#define TWL6040_VIBCTRL_P (1 << 3) +#define TWL6040_VIBCTRL_N (1 << 4)
-/* VIBDATL (0x19) fields */ +/* VIBDATL/R (0x19/0x1B) 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
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 peter.ujfalusi@ti.com --- drivers/mfd/twl6040-core.c | 19 +++++++++++++++---- include/linux/mfd/twl6040.h | 1 + 2 files changed, 16 insertions(+), 4 deletions(-)
diff --git a/drivers/mfd/twl6040-core.c b/drivers/mfd/twl6040-core.c index 7dc8c47..75987c8 100644 --- a/drivers/mfd/twl6040-core.c +++ b/drivers/mfd/twl6040-core.c @@ -34,16 +34,24 @@ #include <linux/mfd/core.h> #include <linux/mfd/twl6040.h>
+#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; diff --git a/include/linux/mfd/twl6040.h b/include/linux/mfd/twl6040.h index 6059935..3ec9591 100644 --- a/include/linux/mfd/twl6040.h +++ b/include/linux/mfd/twl6040.h @@ -185,6 +185,7 @@ struct twl6040 { int audpwron; int power_count; int rev; + u8 vibra_ctrl_cache[2];
int pll; unsigned int sysclk;
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 peter.ujfalusi@ti.com --- drivers/mfd/twl6040-core.c | 12 ++++++++++++ include/linux/mfd/twl6040.h | 3 +++ 2 files changed, 15 insertions(+), 0 deletions(-)
diff --git a/drivers/mfd/twl6040-core.c b/drivers/mfd/twl6040-core.c index 75987c8..268f80f 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, diff --git a/include/linux/mfd/twl6040.h b/include/linux/mfd/twl6040.h index 3ec9591..2463c261 100644 --- a/include/linux/mfd/twl6040.h +++ b/include/linux/mfd/twl6040.h @@ -210,10 +210,13 @@ 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); +/* Get the combined status of the vibra control register */ +int twl6040_get_vibralr_status(struct twl6040 *twl6040);
static inline int twl6040_get_revid(struct twl6040 *twl6040) { return twl6040->rev; }
+ #endif /* End of __TWL6040_CODEC_H__ */
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 peter.ujfalusi@ti.com --- drivers/input/misc/twl6040-vibra.c | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-)
diff --git a/drivers/input/misc/twl6040-vibra.c b/drivers/input/misc/twl6040-vibra.c index cb74185..fe624f2 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 -EPERM; + } + 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;
On Thu, Sep 15, 2011 at 03:59:17PM +0300, Peter Ujfalusi wrote:
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 peter.ujfalusi@ti.com
drivers/input/misc/twl6040-vibra.c | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-)
diff --git a/drivers/input/misc/twl6040-vibra.c b/drivers/input/misc/twl6040-vibra.c index cb74185..fe624f2 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 -EPERM;
- }
This is not very helpful for the application trying to use the device. Maybe we should zap the device when channels are routed through audio instead of dripping requests?
Thanks.
On Thursday 15 September 2011 23:27:20 Dmitry Torokhov wrote:
This is not very helpful for the application trying to use the device. Maybe we should zap the device when channels are routed through audio instead of dripping requests?
Yes, I have thought about that as well, but it can cause other problems. I have modified the fftest tool from: http://www.koders.com/c/fidF60D5962FCA8B937A6D0947AA81AE95A8C58FB36.aspx for my needs. It opens the /dev/input/eventX, and it will keep it open as long as it is running. It is OK for me, since the twl6040-vibra goes to low power mode when we do not have effects running. I can imagine, that real world application will do the same, and that might cause some problems, if I try to zap the device. Can we zap a device, while it is open? How application will handle that event?
-- Péter
On Fri, Sep 16, 2011 at 09:42:16AM +0300, Péter Ujfalusi wrote:
On Thursday 15 September 2011 23:27:20 Dmitry Torokhov wrote:
This is not very helpful for the application trying to use the device. Maybe we should zap the device when channels are routed through audio instead of dripping requests?
Yes, I have thought about that as well, but it can cause other problems. I have modified the fftest tool from: http://www.koders.com/c/fidF60D5962FCA8B937A6D0947AA81AE95A8C58FB36.aspx for my needs. It opens the /dev/input/eventX, and it will keep it open as long as it is running. It is OK for me, since the twl6040-vibra goes to low power mode when we do not have effects running. I can imagine, that real world application will do the same, and that might cause some problems, if I try to zap the device. Can we zap a device, while it is open? How application will handle that event?
The application will get an error on read or write, like -ENODEV, and will hopefully "drop off".
On Friday 23 September 2011 23:00:41 Dmitry Torokhov wrote:
The application will get an error on read or write, like -ENODEV, and will hopefully "drop off".
Thank you for the answer, but I would still like to avoid zapping the input device runtime for now. In order to do that, I need to build up some sort of notification infra for the twl6040 vibra (in the MFD driver) to create, and destroy the input device depending on the changes in the audio driver. I'm also concerned about the needed LOC for this to implement, might be too overkill, since I would think that the twl6040 vibra driving method would not change runtime in any system. As a note: the other vibra (twl4030-vibra) does not even let user space know, that it ignored the effect due to the routing selection. Returning -EBUSY is a bit better than that.. I know this is not a valid argument for anything ;)
Regards, Péter
On Thu, 2011-09-15 at 15:59 +0300, Peter Ujfalusi wrote:
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 peter.ujfalusi@ti.com
drivers/input/misc/twl6040-vibra.c | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-)
diff --git a/drivers/input/misc/twl6040-vibra.c b/drivers/input/misc/twl6040-vibra.c index cb74185..fe624f2 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 -EPERM;
- }
Sorry, missed this earlier.
Probably best to return -EBUSY here otherwise it may look like a device permissions problem to some users.
Liam
On Friday 16 September 2011 10:26:41 Liam Girdwood wrote:
- /* 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 -EPERM;
- }
Sorry, missed this earlier.
Probably best to return -EBUSY here otherwise it may look like a device permissions problem to some users.
I'll change it.
Thanks, Péter
Only mono audio can be used for vibra (DL4 channel).
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- sound/soc/codecs/twl6040.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 8eecf4a..a386cb3 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -1548,8 +1548,8 @@ static struct snd_soc_dai_driver twl6040_dai[] = { .name = "twl6040-vib", .playback = { .stream_name = "Vibra Playback", - .channels_min = 2, - .channels_max = 2, + .channels_min = 1, + .channels_max = 1, .rates = SNDRV_PCM_RATE_CONTINUOUS, .formats = TWL6040_FORMATS, },
twl6040 supports 5 playback, and 2 capture channels
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- sound/soc/codecs/twl6040.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index a386cb3..70f9a64 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -1498,7 +1498,7 @@ static struct snd_soc_dai_driver twl6040_dai[] = { .playback = { .stream_name = "Playback", .channels_min = 1, - .channels_max = 2, + .channels_max = 5, .rates = TWL6040_RATES, .formats = TWL6040_FORMATS, },
OMAP4 McPDM supports 5 downlink (playback), and 3 uplink (capture) channels.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- sound/soc/omap/omap-mcpdm.c | 14 ++++++++------ 1 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/sound/soc/omap/omap-mcpdm.c b/sound/soc/omap/omap-mcpdm.c index 23eaacb..41d1706 100644 --- a/sound/soc/omap/omap-mcpdm.c +++ b/sound/soc/omap/omap-mcpdm.c @@ -315,15 +315,17 @@ static int omap_mcpdm_dai_hw_params(struct snd_pcm_substream *substream,
channels = params_channels(params); switch (channels) { + case 5: + if (stream == SNDRV_PCM_STREAM_CAPTURE) + /* up to 3 channels for capture */ + return -EINVAL; + link_mask |= 1 << 4; case 4: if (stream == SNDRV_PCM_STREAM_CAPTURE) - /* up to 2 channels for capture */ + /* up to 3 channels for capture */ return -EINVAL; link_mask |= 1 << 3; case 3: - if (stream == SNDRV_PCM_STREAM_CAPTURE) - /* up to 2 channels for capture */ - return -EINVAL; link_mask |= 1 << 2; case 2: link_mask |= 1 << 1; @@ -419,13 +421,13 @@ static struct snd_soc_dai_driver omap_mcpdm_dai = { .remove_order = SND_SOC_COMP_ORDER_EARLY, .playback = { .channels_min = 1, - .channels_max = 4, + .channels_max = 5, .rates = OMAP_MCPDM_RATES, .formats = OMAP_MCPDM_FORMATS, }, .capture = { .channels_min = 1, - .channels_max = 2, + .channels_max = 3, .rates = OMAP_MCPDM_RATES, .formats = OMAP_MCPDM_FORMATS, },
On Mon, Sep 19, 2011 at 11:17:40PM +0100, Mark Brown wrote:
On Thu, Sep 15, 2011 at 03:59:20PM +0300, Peter Ujfalusi wrote:
OMAP4 McPDM supports 5 downlink (playback), and 3 uplink (capture) channels.
Applied, thanks.
No I didn't - I guess this one only applies against Liam's tree
Acked-by: Mark Brown broonie@opensource.wolfsonmicro.com
On Tuesday 20 September 2011 11:57:14 Mark Brown wrote:
No I didn't - I guess this one only applies against Liam's tree
Acked-by: Mark Brown broonie@opensource.wolfsonmicro.com
Hrm, I have used Linus's tree for this series, and AFAIK there were no changes regarding to McPDM apart from the patches I have submitted.
Could you give me a hint, why it failed?
-- Péter
On Tue, 2011-09-20 at 14:10 +0300, Péter Ujfalusi wrote:
On Tuesday 20 September 2011 11:57:14 Mark Brown wrote:
No I didn't - I guess this one only applies against Liam's tree
Acked-by: Mark Brown broonie@opensource.wolfsonmicro.com
Hrm, I have used Linus's tree for this series, and AFAIK there were no changes regarding to McPDM apart from the patches I have submitted.
Could you give me a hint, why it failed?
-- Péter
Mark, could you take all these due to my urgent travel.
Thanks !
Liam
On Tuesday 20 September 2011 14:10:24 Péter Ujfalusi wrote:
On Tuesday 20 September 2011 11:57:14 Mark Brown wrote:
No I didn't - I guess this one only applies against Liam's tree
Acked-by: Mark Brown broonie@opensource.wolfsonmicro.com
Hrm, I have used Linus's tree for this series, and AFAIK there were no changes regarding to McPDM apart from the patches I have submitted.
Could you give me a hint, why it failed?
I know why it fails. We need to have the new McPDM driver patches for this to apply.
-- Péter
On Tue, Sep 20, 2011 at 02:24:05PM +0300, Péter Ujfalusi wrote:
On Tuesday 20 September 2011 14:10:24 Péter Ujfalusi wrote:
Hrm, I have used Linus's tree for this series, and AFAIK there were no changes regarding to McPDM apart from the patches I have submitted.
Could you give me a hint, why it failed?
I know why it fails. We need to have the new McPDM driver patches for this to apply.
I was about to say I was pretty sure it was due to your driver rewrite... Unfortunately I don't have a copy of Liam's tree as it was hosted on kernel.org and not in -next so I don't have the current state of the OMAP branches. Liam, can you push somewhere else or otherwise send me the latest code?
twl6040 have two vibra output drivers. They can be operated with audio stream coming through the PDM interface (fifth channel). The vibra outputs can be controlled via the input/FF driver as well. Selection between the two mode is implemented within the codec driver, the input/FF driver can only operate if the routing is set to "Input FF". Changing from "Input FF" to "Audio PDM" mode is protected as well: The switchin can only be done, if there is no running effect from the input/FF.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- sound/soc/codecs/twl6040.c | 72 ++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 72 insertions(+), 0 deletions(-)
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 70f9a64..6667d7a 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -944,6 +944,23 @@ static int twl6040_get_volsw_2r(struct snd_kcontrol *kcontrol, {.reg = reg_left, .rreg = reg_right, .shift = xshift, \ .rshift = xshift, .max = xmax, .invert = xinvert}, }
+static int twl6040_soc_dapm_put_vibra_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = widget->codec; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val; + + /* Do not allow changes while Input/FF efect is running */ + val = twl6040_read_reg_volatile(codec, e->reg); + if (val & TWL6040_VIBENA && !(val & TWL6040_VIBSEL)) + return -EBUSY; + + return snd_soc_dapm_put_enum_double(kcontrol, ucontrol); +} + /* * MICATT volume control: * from -6 to 0 dB in 6 dB steps @@ -1015,6 +1032,19 @@ static const struct soc_enum twl6040_hf_enum[] = { twl6040_hf_texts), };
+static const char *twl6040_vibrapath_texts[] = { + "Input FF", "Audio PDM" +}; + +static const struct soc_enum twl6040_vibra_enum[] = { + SOC_ENUM_SINGLE(TWL6040_REG_VIBCTLL, 1, + ARRAY_SIZE(twl6040_vibrapath_texts), + twl6040_vibrapath_texts), + SOC_ENUM_SINGLE(TWL6040_REG_VIBCTLR, 1, + ARRAY_SIZE(twl6040_vibrapath_texts), + twl6040_vibrapath_texts), +}; + static const struct snd_kcontrol_new amicl_control = SOC_DAPM_ENUM("Route", twl6040_enum[0]);
@@ -1044,6 +1074,17 @@ static const struct snd_kcontrol_new auxl_switch_control = static const struct snd_kcontrol_new auxr_switch_control = SOC_DAPM_SINGLE("Switch", TWL6040_REG_HFRCTL, 6, 1, 0);
+/* Vibra playback switches */ +static const struct snd_kcontrol_new vibral_mux_controls = + SOC_DAPM_ENUM_EXT("Route", twl6040_vibra_enum[0], + snd_soc_dapm_get_enum_double, + twl6040_soc_dapm_put_vibra_enum); + +static const struct snd_kcontrol_new vibrar_mux_controls = + SOC_DAPM_ENUM_EXT("Route", twl6040_vibra_enum[1], + snd_soc_dapm_get_enum_double, + twl6040_soc_dapm_put_vibra_enum); + /* Headset power mode */ static const char *twl6040_power_mode_texts[] = { "Low-Power", "High-Perfomance", @@ -1163,6 +1204,8 @@ static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = { SND_SOC_DAPM_OUTPUT("EP"), SND_SOC_DAPM_OUTPUT("AUXL"), SND_SOC_DAPM_OUTPUT("AUXR"), + SND_SOC_DAPM_OUTPUT("VIBRAL"), + SND_SOC_DAPM_OUTPUT("VIBRAR"),
/* Analog input muxes for the capture amplifiers */ SND_SOC_DAPM_MUX("Analog Left Capture Route", @@ -1215,6 +1258,9 @@ static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = { TWL6040_REG_HFRCTL, 0, 0, twl6040_power_mode_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + /* Virtual DAC for vibra path (DL4 channel) */ + SND_SOC_DAPM_DAC("VIBRA DAC", "Vibra Playback", + SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_MUX("Handsfree Left Playback", SND_SOC_NOPM, 0, 0, &hfl_mux_controls), @@ -1226,6 +1272,11 @@ static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = { SND_SOC_DAPM_MUX("Headset Right Playback", SND_SOC_NOPM, 0, 0, &hsr_mux_controls),
+ SND_SOC_DAPM_MUX("Vibra Left Playback", SND_SOC_NOPM, 0, 0, + &vibral_mux_controls), + SND_SOC_DAPM_MUX("Vibra Right Playback", SND_SOC_NOPM, 0, 0, + &vibrar_mux_controls), + SND_SOC_DAPM_SWITCH("Earphone Playback", SND_SOC_NOPM, 0, 0, &ep_path_enable_control), SND_SOC_DAPM_SWITCH("AUXL Playback", SND_SOC_NOPM, 0, 0, @@ -1254,6 +1305,15 @@ static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = { TWL6040_REG_EARCTL, 0, 0, NULL, 0, twl6040_power_mode_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_OUT_DRV("Vibra Left Driver", + TWL6040_REG_VIBCTLL, 0, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("Vibra Right Driver", + TWL6040_REG_VIBCTLR, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("Vibra Left Control", TWL6040_REG_VIBCTLL, 2, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("Vibra Right Control", TWL6040_REG_VIBCTLR, 2, 0, + NULL, 0),
/* Analog playback PGAs */ SND_SOC_DAPM_PGA("HF Left PGA", @@ -1320,6 +1380,18 @@ static const struct snd_soc_dapm_route intercon[] = {
{"AUXL", NULL, "AUXL Playback"}, {"AUXR", NULL, "AUXR Playback"}, + + /* Vibrator paths */ + {"Vibra Left Playback", "Audio PDM", "VIBRA DAC"}, + {"Vibra Right Playback", "Audio PDM", "VIBRA DAC"}, + + {"Vibra Left Driver", NULL, "Vibra Left Playback"}, + {"Vibra Right Driver", NULL, "Vibra Right Playback"}, + {"Vibra Left Driver", NULL, "Vibra Left Control"}, + {"Vibra Right Driver", NULL, "Vibra Right Control"}, + + {"VIBRAL", NULL, "Vibra Left Driver"}, + {"VIBRAR", NULL, "Vibra Right Driver"}, };
static int twl6040_add_widgets(struct snd_soc_codec *codec)
Hi Mark,
On Thursday 15 September 2011 15:59:21 Peter Ujfalusi wrote:
twl6040 have two vibra output drivers. They can be operated with audio stream coming through the PDM interface (fifth channel). The vibra outputs can be controlled via the input/FF driver as well. Selection between the two mode is implemented within the codec driver, the input/FF driver can only operate if the routing is set to "Input FF". Changing from "Input FF" to "Audio PDM" mode is protected as well: The switchin can only be done, if there is no running effect from the input/FF.
Can you take a look at this patch?
Thank you, Péter
On Wed, Sep 21, 2011 at 04:12:42PM +0300, Péter Ujfalusi wrote:
On Thursday 15 September 2011 15:59:21 Peter Ujfalusi wrote:
Can you take a look at this patch?
What makes you think I didn't look at the code yet? To reiterate I'm waiting for review from Liam. He's offline now but wasn't when you posted.
I also note that you've left it less than a week since the original posting...
Hi Samuel, Dmitry,
On Thursday 15 September 2011 15:59:13 Peter Ujfalusi wrote:
Peter Ujfalusi (8): Input: twl6040: Simplify vibra regsiter definitions MFD: twl6040: Cache the vibra control registers MFD: twl6040: function to query the vibra status for clients Input: twl6040-vibra: Check the selected path for vibra ASoC: twl6040: Fix the number of channels for vibra ASoC: twl6040: Correct supported number of playback channels ASoC: omap-mcpdm: Correct the supported number of channels ASoC: twl6040: Support for vibra output paths
Could you take a look at this series for the MFD/Input parts?
Dmitry: Do you have further comments for patch 4? I will change the error returned to -EBUSY, as Liam was suggesting.
Thank you, Péter
participants (4)
-
Dmitry Torokhov
-
Liam Girdwood
-
Mark Brown
-
Peter Ujfalusi