From 326c29b6bedb7440b4232631686027af84a62cf5 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 4 Jul 2013 08:56:27 +0100 Subject: mfd: arizona: Add GPIO control register bit definitions Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- include/linux/mfd/arizona/gpio.h | 96 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 include/linux/mfd/arizona/gpio.h (limited to 'include') diff --git a/include/linux/mfd/arizona/gpio.h b/include/linux/mfd/arizona/gpio.h new file mode 100644 index 000000000000..d2146bb74f89 --- /dev/null +++ b/include/linux/mfd/arizona/gpio.h @@ -0,0 +1,96 @@ +/* + * GPIO configuration for Arizona devices + * + * Copyright 2013 Wolfson Microelectronics. PLC. + * + * Author: Charles Keepax + * + * 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. + */ + +#ifndef _ARIZONA_GPIO_H +#define _ARIZONA_GPIO_H + +#define ARIZONA_GP_FN_TXLRCLK 0x00 +#define ARIZONA_GP_FN_GPIO 0x01 +#define ARIZONA_GP_FN_IRQ1 0x02 +#define ARIZONA_GP_FN_IRQ2 0x03 +#define ARIZONA_GP_FN_OPCLK 0x04 +#define ARIZONA_GP_FN_FLL1_OUT 0x05 +#define ARIZONA_GP_FN_FLL2_OUT 0x06 +#define ARIZONA_GP_FN_PWM1 0x08 +#define ARIZONA_GP_FN_PWM2 0x09 +#define ARIZONA_GP_FN_SYSCLK_UNDERCLOCKED 0x0A +#define ARIZONA_GP_FN_ASYNCCLK_UNDERCLOCKED 0x0B +#define ARIZONA_GP_FN_FLL1_LOCK 0x0C +#define ARIZONA_GP_FN_FLL2_LOCK 0x0D +#define ARIZONA_GP_FN_FLL1_CLOCK_OK 0x0F +#define ARIZONA_GP_FN_FLL2_CLOCK_OK 0x10 +#define ARIZONA_GP_FN_HEADPHONE_DET 0x12 +#define ARIZONA_GP_FN_MIC_DET 0x13 +#define ARIZONA_GP_FN_WSEQ_STATUS 0x15 +#define ARIZONA_GP_FN_CIF_ADDRESS_ERROR 0x16 +#define ARIZONA_GP_FN_ASRC1_LOCK 0x1A +#define ARIZONA_GP_FN_ASRC2_LOCK 0x1B +#define ARIZONA_GP_FN_ASRC_CONFIG_ERROR 0x1C +#define ARIZONA_GP_FN_DRC1_SIGNAL_DETECT 0x1D +#define ARIZONA_GP_FN_DRC1_ANTICLIP 0x1E +#define ARIZONA_GP_FN_DRC1_DECAY 0x1F +#define ARIZONA_GP_FN_DRC1_NOISE 0x20 +#define ARIZONA_GP_FN_DRC1_QUICK_RELEASE 0x21 +#define ARIZONA_GP_FN_DRC2_SIGNAL_DETECT 0x22 +#define ARIZONA_GP_FN_DRC2_ANTICLIP 0x23 +#define ARIZONA_GP_FN_DRC2_DECAY 0x24 +#define ARIZONA_GP_FN_DRC2_NOISE 0x25 +#define ARIZONA_GP_FN_DRC2_QUICK_RELEASE 0x26 +#define ARIZONA_GP_FN_MIXER_DROPPED_SAMPLE 0x27 +#define ARIZONA_GP_FN_AIF1_CONFIG_ERROR 0x28 +#define ARIZONA_GP_FN_AIF2_CONFIG_ERROR 0x29 +#define ARIZONA_GP_FN_AIF3_CONFIG_ERROR 0x2A +#define ARIZONA_GP_FN_SPK_TEMP_SHUTDOWN 0x2B +#define ARIZONA_GP_FN_SPK_TEMP_WARNING 0x2C +#define ARIZONA_GP_FN_UNDERCLOCKED 0x2D +#define ARIZONA_GP_FN_OVERCLOCKED 0x2E +#define ARIZONA_GP_FN_DSP_IRQ1 0x35 +#define ARIZONA_GP_FN_DSP_IRQ2 0x36 +#define ARIZONA_GP_FN_ASYNC_OPCLK 0x3D +#define ARIZONA_GP_FN_BOOT_DONE 0x44 +#define ARIZONA_GP_FN_DSP1_RAM_READY 0x45 +#define ARIZONA_GP_FN_SYSCLK_ENA_STATUS 0x4B +#define ARIZONA_GP_FN_ASYNCCLK_ENA_STATUS 0x4C + +#define ARIZONA_GPN_DIR 0x8000 /* GPN_DIR */ +#define ARIZONA_GPN_DIR_MASK 0x8000 /* GPN_DIR */ +#define ARIZONA_GPN_DIR_SHIFT 15 /* GPN_DIR */ +#define ARIZONA_GPN_DIR_WIDTH 1 /* GPN_DIR */ +#define ARIZONA_GPN_PU 0x4000 /* GPN_PU */ +#define ARIZONA_GPN_PU_MASK 0x4000 /* GPN_PU */ +#define ARIZONA_GPN_PU_SHIFT 14 /* GPN_PU */ +#define ARIZONA_GPN_PU_WIDTH 1 /* GPN_PU */ +#define ARIZONA_GPN_PD 0x2000 /* GPN_PD */ +#define ARIZONA_GPN_PD_MASK 0x2000 /* GPN_PD */ +#define ARIZONA_GPN_PD_SHIFT 13 /* GPN_PD */ +#define ARIZONA_GPN_PD_WIDTH 1 /* GPN_PD */ +#define ARIZONA_GPN_LVL 0x0800 /* GPN_LVL */ +#define ARIZONA_GPN_LVL_MASK 0x0800 /* GPN_LVL */ +#define ARIZONA_GPN_LVL_SHIFT 11 /* GPN_LVL */ +#define ARIZONA_GPN_LVL_WIDTH 1 /* GPN_LVL */ +#define ARIZONA_GPN_POL 0x0400 /* GPN_POL */ +#define ARIZONA_GPN_POL_MASK 0x0400 /* GPN_POL */ +#define ARIZONA_GPN_POL_SHIFT 10 /* GPN_POL */ +#define ARIZONA_GPN_POL_WIDTH 1 /* GPN_POL */ +#define ARIZONA_GPN_OP_CFG 0x0200 /* GPN_OP_CFG */ +#define ARIZONA_GPN_OP_CFG_MASK 0x0200 /* GPN_OP_CFG */ +#define ARIZONA_GPN_OP_CFG_SHIFT 9 /* GPN_OP_CFG */ +#define ARIZONA_GPN_OP_CFG_WIDTH 1 /* GPN_OP_CFG */ +#define ARIZONA_GPN_DB 0x0100 /* GPN_DB */ +#define ARIZONA_GPN_DB_MASK 0x0100 /* GPN_DB */ +#define ARIZONA_GPN_DB_SHIFT 8 /* GPN_DB */ +#define ARIZONA_GPN_DB_WIDTH 1 /* GPN_DB */ +#define ARIZONA_GPN_FN_MASK 0x007F /* GPN_DB */ +#define ARIZONA_GPN_FN_SHIFT 0 /* GPN_DB */ +#define ARIZONA_GPN_FN_WIDTH 7 /* GPN_DB */ + +#endif -- cgit v1.2.3 From 10175b3b2fdd287515db4c103d96b323bb4cd690 Mon Sep 17 00:00:00 2001 From: Bo Shen Date: Wed, 3 Jul 2013 16:37:59 +0800 Subject: ARM: atmel-ssc: change phybase type to dma_addr_t as the phybase paramter only used for DMA operation, change it's type from resource_size_t to dma_addr_t Signed-off-by: Bo Shen Reviewed-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/linux/atmel-ssc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/atmel-ssc.h b/include/linux/atmel-ssc.h index deb0ae58b99b..66a0e5384edd 100644 --- a/include/linux/atmel-ssc.h +++ b/include/linux/atmel-ssc.h @@ -11,7 +11,7 @@ struct atmel_ssc_platform_data { struct ssc_device { struct list_head list; - resource_size_t phybase; + dma_addr_t phybase; void __iomem *regs; struct platform_device *pdev; struct atmel_ssc_platform_data *pdata; -- cgit v1.2.3 From c3f48ae6fd5a1ebdcaff5efe35f88f31daaee225 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 24 Jul 2013 15:27:36 +0200 Subject: ASoC: dapm: Pass snd_soc_card directly to soc_dpcm_runtime_update() soc_dpcm_runtime_update() operates on a ASoC card as a whole. Currently it takes a snd_soc_dapm_widget as its only parameter though. The widget is then used to look up the card and is otherwise unused. This patch changes the function to take a pointer to the card directly. This makes it possible to to call soc_dpcm_runtime_update() for updates which are not related to one specific widget. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dpcm.h | 2 +- sound/soc/soc-dapm.c | 4 ++-- sound/soc/soc-pcm.c | 10 +--------- 3 files changed, 4 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dpcm.h b/include/sound/soc-dpcm.h index 04598f1efd77..047d657c331c 100644 --- a/include/sound/soc-dpcm.h +++ b/include/sound/soc-dpcm.h @@ -133,6 +133,6 @@ void snd_soc_dpcm_be_set_state(struct snd_soc_pcm_runtime *be, int stream, /* internal use only */ int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute); int soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd); -int soc_dpcm_runtime_update(struct snd_soc_dapm_widget *); +int soc_dpcm_runtime_update(struct snd_soc_card *); #endif diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 378655839f74..8d9c09b266fd 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1986,7 +1986,7 @@ int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget, ret = soc_dapm_mux_update_power(widget, kcontrol, mux, e); mutex_unlock(&card->dapm_mutex); if (ret > 0) - soc_dpcm_runtime_update(widget); + soc_dpcm_runtime_update(card); return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_mux_update_power); @@ -2032,7 +2032,7 @@ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, ret = soc_dapm_mixer_update_power(widget, kcontrol, connect); mutex_unlock(&card->dapm_mutex); if (ret > 0) - soc_dpcm_runtime_update(widget); + soc_dpcm_runtime_update(card); return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_mixer_update_power); diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index b6c640332a17..5c2c66209808 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1832,18 +1832,10 @@ static int dpcm_run_old_update(struct snd_soc_pcm_runtime *fe, int stream) /* Called by DAPM mixer/mux changes to update audio routing between PCMs and * any DAI links. */ -int soc_dpcm_runtime_update(struct snd_soc_dapm_widget *widget) +int soc_dpcm_runtime_update(struct snd_soc_card *card) { - struct snd_soc_card *card; int i, old, new, paths; - if (widget->codec) - card = widget->codec->card; - else if (widget->platform) - card = widget->platform->card; - else - return -EINVAL; - mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_RUNTIME); for (i = 0; i < card->num_rtd; i++) { struct snd_soc_dapm_widget_list *list; -- cgit v1.2.3 From ce6cfaf1de136cd3e6ed7c0ed984be8d003a58c1 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 24 Jul 2013 15:27:37 +0200 Subject: ASoC: dapm: Run widget updates for shared controls at the same time Currently when updating a control that is shared between multiple widgets the whole power-up/power-down sequence is being run once for each widget. The control register is updated during the first run, which means the CODEC internal routing is also updated for all widgets during this first run. The input and output paths for each widgets are only updated though during the respective run for that widget. This leads to a slight inconsistency between the CODEC's internal state and ASoC's state, which causes non optimal behavior in regard to click and pop avoidance. E.g. consider the following setup where two MUXs share the same control. +------+ A1 ------| | | MUX1 |----- C1 B1 ------| | +------+ | control ---+ | +------+ A2 ------| | | MUX2 |----- C2 B2 ------| | +------+ If the control is updated to switch the MUXs from input A to input B with the current code the power-up/power-down sequence will look like this: Run soc_dapm_mux_update_power for MUX1 Power-down A1 Update MUXing Power-up B1 Run soc_dapm_mux_update_power for MUX2 Power-down A2 (Update MUXing) Power-up B2 Note that the second 'Update Muxing' is a no-op, since the register was already updated. While the preferred order for avoiding pops and clicks should be: Run soc_dapm_mux_update_power for control Power-down A1 Power-down A2 Update MUXing Power-up B1 Power-up B2 This patch changes the behavior to the later by running the updates for all widgets that the control is attached to at the same time. The new code is also a bit simpler since callers of soc_dapm_{mux,muxer}_update_power don't have to loop over each widget anymore and neither do we need to keep track for which of the kcontrol's widgets the current update is. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 7 +- sound/soc/soc-dapm.c | 162 ++++++++++++++++++++--------------------------- 2 files changed, 71 insertions(+), 98 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 3e479f4e15f5..3717ad089486 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -391,10 +391,10 @@ void snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, void snd_soc_dapm_shutdown(struct snd_soc_card *card); /* external DAPM widget events */ -int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, +int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, struct snd_kcontrol *kcontrol, int connect); -int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget, - struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e); +int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, + struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e); /* dapm sys fs - used by the core */ int snd_soc_dapm_sys_add(struct device *dev); @@ -559,7 +559,6 @@ struct snd_soc_dapm_widget { }; struct snd_soc_dapm_update { - struct snd_soc_dapm_widget *widget; struct snd_kcontrol *kcontrol; int reg; int mask; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 8d9c09b266fd..6f8a01bf6ca8 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1456,34 +1456,45 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, static void dapm_widget_update(struct snd_soc_dapm_context *dapm) { struct snd_soc_dapm_update *update = dapm->update; - struct snd_soc_dapm_widget *w; + struct snd_soc_dapm_widget_list *wlist; + struct snd_soc_dapm_widget *w = NULL; + unsigned int wi; int ret; if (!update) return; - w = update->widget; + wlist = snd_kcontrol_chip(update->kcontrol); - if (w->event && - (w->event_flags & SND_SOC_DAPM_PRE_REG)) { - ret = w->event(w, update->kcontrol, SND_SOC_DAPM_PRE_REG); - if (ret != 0) - dev_err(dapm->dev, "ASoC: %s DAPM pre-event failed: %d\n", - w->name, ret); + for (wi = 0; wi < wlist->num_widgets; wi++) { + w = wlist->widgets[wi]; + + if (w->event && (w->event_flags & SND_SOC_DAPM_PRE_REG)) { + ret = w->event(w, update->kcontrol, SND_SOC_DAPM_PRE_REG); + if (ret != 0) + dev_err(dapm->dev, "ASoC: %s DAPM pre-event failed: %d\n", + w->name, ret); + } } + if (!w) + return; + ret = soc_widget_update_bits_locked(w, update->reg, update->mask, update->val); if (ret < 0) dev_err(dapm->dev, "ASoC: %s DAPM update failed: %d\n", w->name, ret); - if (w->event && - (w->event_flags & SND_SOC_DAPM_POST_REG)) { - ret = w->event(w, update->kcontrol, SND_SOC_DAPM_POST_REG); - if (ret != 0) - dev_err(dapm->dev, "ASoC: %s DAPM post-event failed: %d\n", - w->name, ret); + for (wi = 0; wi < wlist->num_widgets; wi++) { + w = wlist->widgets[wi]; + + if (w->event && (w->event_flags & SND_SOC_DAPM_POST_REG)) { + ret = w->event(w, update->kcontrol, SND_SOC_DAPM_POST_REG); + if (ret != 0) + dev_err(dapm->dev, "ASoC: %s DAPM post-event failed: %d\n", + w->name, ret); + } } } @@ -1936,19 +1947,14 @@ static inline void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm) #endif /* test and update the power status of a mux widget */ -static int soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget, +static int soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e) { struct snd_soc_dapm_path *path; int found = 0; - if (widget->id != snd_soc_dapm_mux && - widget->id != snd_soc_dapm_virt_mux && - widget->id != snd_soc_dapm_value_mux) - return -ENODEV; - /* find dapm widget path assoc with kcontrol */ - list_for_each_entry(path, &widget->dapm->card->paths, list) { + list_for_each_entry(path, &dapm->card->paths, list) { if (path->kcontrol != kcontrol) continue; @@ -1966,24 +1972,23 @@ static int soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget, "mux disconnection"); path->connect = 0; /* old connection must be powered down */ } + dapm_mark_dirty(path->sink, "mux change"); } - if (found) { - dapm_mark_dirty(widget, "mux change"); - dapm_power_widgets(widget->dapm, SND_SOC_DAPM_STREAM_NOP); - } + if (found) + dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP); return found; } -int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget, - struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e) +int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, + struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e) { - struct snd_soc_card *card = widget->dapm->card; + struct snd_soc_card *card = dapm->card; int ret; mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - ret = soc_dapm_mux_update_power(widget, kcontrol, mux, e); + ret = soc_dapm_mux_update_power(dapm, kcontrol, mux, e); mutex_unlock(&card->dapm_mutex); if (ret > 0) soc_dpcm_runtime_update(card); @@ -1992,19 +1997,14 @@ int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget, EXPORT_SYMBOL_GPL(snd_soc_dapm_mux_update_power); /* test and update the power status of a mixer or switch widget */ -static int soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, +static int soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, struct snd_kcontrol *kcontrol, int connect) { struct snd_soc_dapm_path *path; int found = 0; - if (widget->id != snd_soc_dapm_mixer && - widget->id != snd_soc_dapm_mixer_named_ctl && - widget->id != snd_soc_dapm_switch) - return -ENODEV; - /* find dapm widget path assoc with kcontrol */ - list_for_each_entry(path, &widget->dapm->card->paths, list) { + list_for_each_entry(path, &dapm->card->paths, list) { if (path->kcontrol != kcontrol) continue; @@ -2012,24 +2012,23 @@ static int soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, found = 1; path->connect = connect; dapm_mark_dirty(path->source, "mixer connection"); + dapm_mark_dirty(path->sink, "mixer update"); } - if (found) { - dapm_mark_dirty(widget, "mixer update"); - dapm_power_widgets(widget->dapm, SND_SOC_DAPM_STREAM_NOP); - } + if (found) + dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP); return found; } -int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, - struct snd_kcontrol *kcontrol, int connect) +int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, + struct snd_kcontrol *kcontrol, int connect) { - struct snd_soc_card *card = widget->dapm->card; + struct snd_soc_card *card = dapm->card; int ret; mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - ret = soc_dapm_mixer_update_power(widget, kcontrol, connect); + ret = soc_dapm_mixer_update_power(dapm, kcontrol, connect); mutex_unlock(&card->dapm_mutex); if (ret > 0) soc_dpcm_runtime_update(card); @@ -2695,7 +2694,6 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, unsigned int val; int connect, change; struct snd_soc_dapm_update update; - int wi; if (snd_soc_volsw_is_stereo(mc)) dev_warn(widget->dapm->dev, @@ -2714,22 +2712,16 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, change = snd_soc_test_bits(widget->codec, reg, mask, val); if (change) { - for (wi = 0; wi < wlist->num_widgets; wi++) { - widget = wlist->widgets[wi]; - - widget->value = val; + update.kcontrol = kcontrol; + update.reg = reg; + update.mask = mask; + update.val = val; - update.kcontrol = kcontrol; - update.widget = widget; - update.reg = reg; - update.mask = mask; - update.val = val; - widget->dapm->update = &update; + widget->dapm->update = &update; - soc_dapm_mixer_update_power(widget, kcontrol, connect); + soc_dapm_mixer_update_power(widget->dapm, kcontrol, connect); - widget->dapm->update = NULL; - } + widget->dapm->update = NULL; } mutex_unlock(&card->dapm_mutex); @@ -2784,7 +2776,6 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, unsigned int val, mux, change; unsigned int mask; struct snd_soc_dapm_update update; - int wi; if (ucontrol->value.enumerated.item[0] > e->max - 1) return -EINVAL; @@ -2802,22 +2793,17 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, change = snd_soc_test_bits(widget->codec, e->reg, mask, val); if (change) { - for (wi = 0; wi < wlist->num_widgets; wi++) { - widget = wlist->widgets[wi]; + widget->value = val; - widget->value = val; + update.kcontrol = kcontrol; + update.reg = e->reg; + update.mask = mask; + update.val = val; + widget->dapm->update = &update; - update.kcontrol = kcontrol; - update.widget = widget; - update.reg = e->reg; - update.mask = mask; - update.val = val; - widget->dapm->update = &update; + soc_dapm_mux_update_power(widget->dapm, kcontrol, mux, e); - soc_dapm_mux_update_power(widget, kcontrol, mux, e); - - widget->dapm->update = NULL; - } + widget->dapm->update = NULL; } mutex_unlock(&card->dapm_mutex); @@ -2861,7 +2847,6 @@ int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; int change; - int wi; if (ucontrol->value.enumerated.item[0] >= e->max) return -EINVAL; @@ -2870,13 +2855,8 @@ int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, change = widget->value != ucontrol->value.enumerated.item[0]; if (change) { - for (wi = 0; wi < wlist->num_widgets; wi++) { - widget = wlist->widgets[wi]; - - widget->value = ucontrol->value.enumerated.item[0]; - - soc_dapm_mux_update_power(widget, kcontrol, widget->value, e); - } + widget->value = ucontrol->value.enumerated.item[0]; + soc_dapm_mux_update_power(widget->dapm, kcontrol, widget->value, e); } mutex_unlock(&card->dapm_mutex); @@ -2949,7 +2929,6 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, unsigned int val, mux, change; unsigned int mask; struct snd_soc_dapm_update update; - int wi; if (ucontrol->value.enumerated.item[0] > e->max - 1) return -EINVAL; @@ -2967,22 +2946,17 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, change = snd_soc_test_bits(widget->codec, e->reg, mask, val); if (change) { - for (wi = 0; wi < wlist->num_widgets; wi++) { - widget = wlist->widgets[wi]; - - widget->value = val; + widget->value = val; - update.kcontrol = kcontrol; - update.widget = widget; - update.reg = e->reg; - update.mask = mask; - update.val = val; - widget->dapm->update = &update; + update.kcontrol = kcontrol; + update.reg = e->reg; + update.mask = mask; + update.val = val; + widget->dapm->update = &update; - soc_dapm_mux_update_power(widget, kcontrol, mux, e); + soc_dapm_mux_update_power(widget->dapm, kcontrol, mux, e); - widget->dapm->update = NULL; - } + widget->dapm->update = NULL; } mutex_unlock(&card->dapm_mutex); -- cgit v1.2.3 From 6b3fc03b3b614ced09df96ca60ab6f627d8c240c Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 24 Jul 2013 15:27:38 +0200 Subject: ASoC: dapm: Add a update parameter to snd_soc_dapm_{mux,mixer}_update_power In order to avoid race conditions the assignment of dapm->update should happen while card->dapm_mutex is being held. To allow CODEC drivers to run a register update when using snd_soc_dapm_mux_update_power() or snd_soc_dapm_mixer_update_power() add a update parameter to these two functions. The update parameter will be assigned to dapm->update while card->dapm_mutex is locked. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 7 +++++-- sound/soc/soc-dapm.c | 10 ++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 3717ad089486..e77c6f5a8390 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -333,6 +333,7 @@ struct snd_soc_dapm_route; struct snd_soc_dapm_context; struct regulator; struct snd_soc_dapm_widget_list; +struct snd_soc_dapm_update; int dapm_reg_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); @@ -392,9 +393,11 @@ void snd_soc_dapm_shutdown(struct snd_soc_card *card); /* external DAPM widget events */ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, - struct snd_kcontrol *kcontrol, int connect); + struct snd_kcontrol *kcontrol, int connect, + struct snd_soc_dapm_update *update); int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, - struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e); + struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e, + struct snd_soc_dapm_update *update); /* dapm sys fs - used by the core */ int snd_soc_dapm_sys_add(struct device *dev); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 6f8a01bf6ca8..758761146a42 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1982,13 +1982,16 @@ static int soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, } int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, - struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e) + struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e, + struct snd_soc_dapm_update *update) { struct snd_soc_card *card = dapm->card; int ret; mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + dapm->update = update; ret = soc_dapm_mux_update_power(dapm, kcontrol, mux, e); + dapm->update = NULL; mutex_unlock(&card->dapm_mutex); if (ret > 0) soc_dpcm_runtime_update(card); @@ -2022,13 +2025,16 @@ static int soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, } int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, - struct snd_kcontrol *kcontrol, int connect) + struct snd_kcontrol *kcontrol, int connect, + struct snd_soc_dapm_update *update) { struct snd_soc_card *card = dapm->card; int ret; mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + dapm->update = update; ret = soc_dapm_mixer_update_power(dapm, kcontrol, connect); + dapm->update = NULL; mutex_unlock(&card->dapm_mutex); if (ret > 0) soc_dpcm_runtime_update(card); -- cgit v1.2.3 From 1536a968892aa9095aada4b6d2ed326432cd71c8 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 21 Jul 2013 21:35:52 -0700 Subject: ASoC: add Renesas R-Car core feature Renesas R-Car series sound circuit consists of SSI and its peripheral. But this peripheral circuits are different between R-Car Generation1 (E1/M1/H1) and Generation2 (E2/M2/H2). (Actually, there are many difference in Generation1 chips) Basically, for the future, Renesas R-Car series will use Gen2 style sound circuit, but driver should care Gen1 also. The main differences between Gen1 and Gen2 peripheral are 1) register offset, 2) data path. This patch adds basic (core) feature for R-Car series sound driver as prototype Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/rcar_snd.h | 33 +++ sound/soc/sh/Kconfig | 7 + sound/soc/sh/Makefile | 3 + sound/soc/sh/rcar/Makefile | 2 + sound/soc/sh/rcar/core.c | 554 +++++++++++++++++++++++++++++++++++++++++++++ sound/soc/sh/rcar/rsnd.h | 94 ++++++++ 6 files changed, 693 insertions(+) create mode 100644 include/sound/rcar_snd.h create mode 100644 sound/soc/sh/rcar/Makefile create mode 100644 sound/soc/sh/rcar/core.c create mode 100644 sound/soc/sh/rcar/rsnd.h (limited to 'include') diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h new file mode 100644 index 000000000000..7272b2ea7108 --- /dev/null +++ b/include/sound/rcar_snd.h @@ -0,0 +1,33 @@ +/* + * Renesas R-Car SRU/SCU/SSIU/SSI support + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Kuninori Morimoto + * + * 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. + */ + +#ifndef RCAR_SND_H +#define RCAR_SND_H + +#include + + +#define RSND_BASE_MAX 0 + +struct rsnd_dai_platform_info { + int ssi_id_playback; + int ssi_id_capture; +}; + +struct rcar_snd_info { + u32 flags; + struct rsnd_dai_platform_info *dai_info; + int dai_info_nr; + int (*start)(int id); + int (*stop)(int id); +}; + +#endif diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig index 6bcb1164d599..56d8ff6a402d 100644 --- a/sound/soc/sh/Kconfig +++ b/sound/soc/sh/Kconfig @@ -34,6 +34,13 @@ config SND_SOC_SH4_SIU select SH_DMAE select FW_LOADER +config SND_SOC_RCAR + tristate "R-Car series SRU/SCU/SSIU/SSI support" + select SND_SIMPLE_CARD + select RCAR_CLK_ADG + help + This option enables R-Car SUR/SCU/SSIU/SSI sound support + ## ## Boards ## diff --git a/sound/soc/sh/Makefile b/sound/soc/sh/Makefile index 849b387d17d9..aaf3dcd1ee2a 100644 --- a/sound/soc/sh/Makefile +++ b/sound/soc/sh/Makefile @@ -12,6 +12,9 @@ obj-$(CONFIG_SND_SOC_SH4_SSI) += snd-soc-ssi.o obj-$(CONFIG_SND_SOC_SH4_FSI) += snd-soc-fsi.o obj-$(CONFIG_SND_SOC_SH4_SIU) += snd-soc-siu.o +## audio units for R-Car +obj-$(CONFIG_SND_SOC_RCAR) += rcar/ + ## boards snd-soc-sh7760-ac97-objs := sh7760-ac97.o snd-soc-migor-objs := migor.o diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile new file mode 100644 index 000000000000..cd8089f20c94 --- /dev/null +++ b/sound/soc/sh/rcar/Makefile @@ -0,0 +1,2 @@ +snd-soc-rcar-objs := core.o +obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c new file mode 100644 index 000000000000..13b5d50efd06 --- /dev/null +++ b/sound/soc/sh/rcar/core.c @@ -0,0 +1,554 @@ +/* + * Renesas R-Car SRU/SCU/SSIU/SSI support + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Kuninori Morimoto + * + * Based on fsi.c + * Kuninori Morimoto + * + * 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. + */ + +/* + * Renesas R-Car sound device structure + * + * Gen1 + * + * SRU : Sound Routing Unit + * - SRC : Sampling Rate Converter + * - CMD + * - CTU : Channel Count Conversion Unit + * - MIX : Mixer + * - DVC : Digital Volume and Mute Function + * - SSI : Serial Sound Interface + * + * Gen2 + * + * SCU : Sampling Rate Converter Unit + * - SRC : Sampling Rate Converter + * - CMD + * - CTU : Channel Count Conversion Unit + * - MIX : Mixer + * - DVC : Digital Volume and Mute Function + * SSIU : Serial Sound Interface Unit + * - SSI : Serial Sound Interface + */ + +/* + * driver data Image + * + * rsnd_priv + * | + * | ** this depends on Gen1/Gen2 + * | + * +- gen + * | + * | ** these depend on data path + * | ** gen and platform data control it + * | + * +- rdai[0] + * | | sru ssiu ssi + * | +- playback -> [mod] -> [mod] -> [mod] -> ... + * | | + * | | sru ssiu ssi + * | +- capture -> [mod] -> [mod] -> [mod] -> ... + * | + * +- rdai[1] + * | | sru ssiu ssi + * | +- playback -> [mod] -> [mod] -> [mod] -> ... + * | | + * | | sru ssiu ssi + * | +- capture -> [mod] -> [mod] -> [mod] -> ... + * ... + * | + * | ** these control ssi + * | + * +- ssi + * | | + * | +- ssi[0] + * | +- ssi[1] + * | +- ssi[2] + * | ... + * | + * | ** these control scu + * | + * +- scu + * | + * +- scu[0] + * +- scu[1] + * +- scu[2] + * ... + * + * + * for_each_rsnd_dai(xx, priv, xx) + * rdai[0] => rdai[1] => rdai[2] => ... + * + * for_each_rsnd_mod(xx, rdai, xx) + * [mod] => [mod] => [mod] => ... + * + * rsnd_dai_call(xxx, fn ) + * [mod]->fn() -> [mod]->fn() -> [mod]->fn()... + * + */ +#include +#include "rsnd.h" + +#define RSND_RATES SNDRV_PCM_RATE_8000_96000 +#define RSND_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE) + +/* + * rsnd_platform functions + */ +#define rsnd_platform_call(priv, dai, func, param...) \ + (!(priv->info->func) ? -ENODEV : \ + priv->info->func(param)) + + +/* + * rsnd_dai functions + */ +struct rsnd_dai *rsnd_dai_get(struct rsnd_priv *priv, int id) +{ + return priv->rdai + id; +} + +static struct rsnd_dai *rsnd_dai_to_rdai(struct snd_soc_dai *dai) +{ + struct rsnd_priv *priv = snd_soc_dai_get_drvdata(dai); + + return rsnd_dai_get(priv, dai->id); +} + +int rsnd_dai_is_play(struct rsnd_dai *rdai, struct rsnd_dai_stream *io) +{ + return &rdai->playback == io; +} + +/* + * rsnd_soc_dai functions + */ +int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional) +{ + struct snd_pcm_substream *substream = io->substream; + struct snd_pcm_runtime *runtime = substream->runtime; + int pos = io->byte_pos + additional; + + pos %= (runtime->periods * io->byte_per_period); + + return pos; +} + +void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int byte) +{ + io->byte_pos += byte; + + if (io->byte_pos >= io->next_period_byte) { + struct snd_pcm_substream *substream = io->substream; + struct snd_pcm_runtime *runtime = substream->runtime; + + io->period_pos++; + io->next_period_byte += io->byte_per_period; + + if (io->period_pos >= runtime->periods) { + io->byte_pos = 0; + io->period_pos = 0; + io->next_period_byte = io->byte_per_period; + } + + snd_pcm_period_elapsed(substream); + } +} + +static int rsnd_dai_stream_init(struct rsnd_dai_stream *io, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + if (!list_empty(&io->head)) + return -EIO; + + INIT_LIST_HEAD(&io->head); + io->substream = substream; + io->byte_pos = 0; + io->period_pos = 0; + io->byte_per_period = runtime->period_size * + runtime->channels * + samples_to_bytes(runtime, 1); + io->next_period_byte = io->byte_per_period; + + return 0; +} + +static +struct snd_soc_dai *rsnd_substream_to_dai(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + + return rtd->cpu_dai; +} + +static +struct rsnd_dai_stream *rsnd_rdai_to_io(struct rsnd_dai *rdai, + struct snd_pcm_substream *substream) +{ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + return &rdai->playback; + else + return &rdai->capture; +} + +static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct rsnd_priv *priv = snd_soc_dai_get_drvdata(dai); + struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); + struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); + struct rsnd_dai_platform_info *info = rsnd_dai_get_platform_info(rdai); + int ssi_id = rsnd_dai_is_play(rdai, io) ? info->ssi_id_playback : + info->ssi_id_capture; + int ret; + unsigned long flags; + + rsnd_lock(priv, flags); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + ret = rsnd_dai_stream_init(io, substream); + if (ret < 0) + goto dai_trigger_end; + + ret = rsnd_platform_call(priv, dai, start, ssi_id); + if (ret < 0) + goto dai_trigger_end; + + break; + case SNDRV_PCM_TRIGGER_STOP: + ret = rsnd_platform_call(priv, dai, stop, ssi_id); + if (ret < 0) + goto dai_trigger_end; + + break; + default: + ret = -EINVAL; + } + +dai_trigger_end: + rsnd_unlock(priv, flags); + + return ret; +} + +static int rsnd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + rdai->clk_master = 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + rdai->clk_master = 0; + break; + default: + return -EINVAL; + } + + /* set clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_IF: + rdai->bit_clk_inv = 0; + rdai->frm_clk_inv = 1; + break; + case SND_SOC_DAIFMT_IB_NF: + rdai->bit_clk_inv = 1; + rdai->frm_clk_inv = 0; + break; + case SND_SOC_DAIFMT_IB_IF: + rdai->bit_clk_inv = 1; + rdai->frm_clk_inv = 1; + break; + case SND_SOC_DAIFMT_NB_NF: + default: + rdai->bit_clk_inv = 0; + rdai->frm_clk_inv = 0; + break; + } + + /* set format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + rdai->sys_delay = 0; + rdai->data_alignment = 0; + break; + case SND_SOC_DAIFMT_LEFT_J: + rdai->sys_delay = 1; + rdai->data_alignment = 0; + break; + case SND_SOC_DAIFMT_RIGHT_J: + rdai->sys_delay = 1; + rdai->data_alignment = 1; + break; + } + + return 0; +} + +static const struct snd_soc_dai_ops rsnd_soc_dai_ops = { + .trigger = rsnd_soc_dai_trigger, + .set_fmt = rsnd_soc_dai_set_fmt, +}; + +static int rsnd_dai_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv) +{ + struct snd_soc_dai_driver *drv; + struct rsnd_dai *rdai; + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_dai_platform_info *dai_info; + int dai_nr = info->dai_info_nr; + int i, pid, cid; + + drv = devm_kzalloc(dev, sizeof(*drv) * dai_nr, GFP_KERNEL); + rdai = devm_kzalloc(dev, sizeof(*rdai) * dai_nr, GFP_KERNEL); + if (!drv || !rdai) { + dev_err(dev, "dai allocate failed\n"); + return -ENOMEM; + } + + for (i = 0; i < dai_nr; i++) { + dai_info = &info->dai_info[i]; + + pid = dai_info->ssi_id_playback; + cid = dai_info->ssi_id_capture; + + /* + * init rsnd_dai + */ + INIT_LIST_HEAD(&rdai[i].playback.head); + INIT_LIST_HEAD(&rdai[i].capture.head); + + rdai[i].info = dai_info; + + snprintf(rdai[i].name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", i); + + /* + * init snd_soc_dai_driver + */ + drv[i].name = rdai[i].name; + drv[i].ops = &rsnd_soc_dai_ops; + if (pid >= 0) { + drv[i].playback.rates = RSND_RATES; + drv[i].playback.formats = RSND_FMTS; + drv[i].playback.channels_min = 2; + drv[i].playback.channels_max = 2; + } + if (cid >= 0) { + drv[i].capture.rates = RSND_RATES; + drv[i].capture.formats = RSND_FMTS; + drv[i].capture.channels_min = 2; + drv[i].capture.channels_max = 2; + } + + dev_dbg(dev, "%s (%d, %d) probed", rdai[i].name, pid, cid); + } + + priv->dai_nr = dai_nr; + priv->daidrv = drv; + priv->rdai = rdai; + + return 0; +} + +static void rsnd_dai_remove(struct platform_device *pdev, + struct rsnd_priv *priv) +{ +} + +/* + * pcm ops + */ +static struct snd_pcm_hardware rsnd_pcm_hardware = { + .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE, + .formats = RSND_FMTS, + .rates = RSND_RATES, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 64 * 1024, + .period_bytes_min = 32, + .period_bytes_max = 8192, + .periods_min = 1, + .periods_max = 32, + .fifo_size = 256, +}; + +static int rsnd_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + int ret = 0; + + snd_soc_set_runtime_hwparams(substream, &rsnd_pcm_hardware); + + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + + return ret; +} + +static int rsnd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); +} + +static snd_pcm_uframes_t rsnd_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_dai *dai = rsnd_substream_to_dai(substream); + struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); + struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); + + return bytes_to_frames(runtime, io->byte_pos); +} + +static struct snd_pcm_ops rsnd_pcm_ops = { + .open = rsnd_pcm_open, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = rsnd_hw_params, + .hw_free = snd_pcm_lib_free_pages, + .pointer = rsnd_pointer, +}; + +/* + * snd_soc_platform + */ + +#define PREALLOC_BUFFER (32 * 1024) +#define PREALLOC_BUFFER_MAX (32 * 1024) + +static int rsnd_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + return snd_pcm_lib_preallocate_pages_for_all( + rtd->pcm, + SNDRV_DMA_TYPE_DEV, + rtd->card->snd_card->dev, + PREALLOC_BUFFER, PREALLOC_BUFFER_MAX); +} + +static void rsnd_pcm_free(struct snd_pcm *pcm) +{ + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static struct snd_soc_platform_driver rsnd_soc_platform = { + .ops = &rsnd_pcm_ops, + .pcm_new = rsnd_pcm_new, + .pcm_free = rsnd_pcm_free, +}; + +static const struct snd_soc_component_driver rsnd_soc_component = { + .name = "rsnd", +}; + +/* + * rsnd probe + */ +static int rsnd_probe(struct platform_device *pdev) +{ + struct rcar_snd_info *info; + struct rsnd_priv *priv; + struct device *dev = &pdev->dev; + int ret; + + info = pdev->dev.platform_data; + if (!info) { + dev_err(dev, "driver needs R-Car sound information\n"); + return -ENODEV; + } + + /* + * init priv data + */ + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) { + dev_err(dev, "priv allocate failed\n"); + return -ENODEV; + } + + priv->dev = dev; + priv->info = info; + spin_lock_init(&priv->lock); + + /* + * init each module + */ + ret = rsnd_dai_probe(pdev, info, priv); + if (ret < 0) + return ret; + + /* + * asoc register + */ + ret = snd_soc_register_platform(dev, &rsnd_soc_platform); + if (ret < 0) { + dev_err(dev, "cannot snd soc register\n"); + return ret; + } + + ret = snd_soc_register_component(dev, &rsnd_soc_component, + priv->daidrv, rsnd_dai_nr(priv)); + if (ret < 0) { + dev_err(dev, "cannot snd dai register\n"); + goto exit_snd_soc; + } + + dev_set_drvdata(dev, priv); + + pm_runtime_enable(dev); + + dev_info(dev, "probed\n"); + return ret; + +exit_snd_soc: + snd_soc_unregister_platform(dev); + + return ret; +} + +static int rsnd_remove(struct platform_device *pdev) +{ + struct rsnd_priv *priv = dev_get_drvdata(&pdev->dev); + + pm_runtime_disable(&pdev->dev); + + /* + * remove each module + */ + rsnd_dai_remove(pdev, priv); + + return 0; +} + +static struct platform_driver rsnd_driver = { + .driver = { + .name = "rcar_sound", + }, + .probe = rsnd_probe, + .remove = rsnd_remove, +}; +module_platform_driver(rsnd_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Renesas R-Car audio driver"); +MODULE_AUTHOR("Kuninori Morimoto "); +MODULE_ALIAS("platform:rcar-pcm-audio"); diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h new file mode 100644 index 000000000000..8d04fd0352bd --- /dev/null +++ b/sound/soc/sh/rcar/rsnd.h @@ -0,0 +1,94 @@ +/* + * Renesas R-Car + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Kuninori Morimoto + * + * 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. + */ +#ifndef RSND_H +#define RSND_H + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * pseudo register + * + * The register address offsets SRU/SCU/SSIU on Gen1/Gen2 are very different. + * This driver uses pseudo register in order to hide it. + * see gen1/gen2 for detail + */ +struct rsnd_priv; +struct rsnd_dai; +struct rsnd_dai_stream; + +/* + * R-Car sound DAI + */ +#define RSND_DAI_NAME_SIZE 16 +struct rsnd_dai_stream { + struct list_head head; /* head of rsnd_mod list */ + struct snd_pcm_substream *substream; + int byte_pos; + int period_pos; + int byte_per_period; + int next_period_byte; +}; + +struct rsnd_dai { + char name[RSND_DAI_NAME_SIZE]; + struct rsnd_dai_platform_info *info; /* rcar_snd.h */ + struct rsnd_dai_stream playback; + struct rsnd_dai_stream capture; + + int clk_master:1; + int bit_clk_inv:1; + int frm_clk_inv:1; + int sys_delay:1; + int data_alignment:1; +}; + +#define rsnd_dai_nr(priv) ((priv)->dai_nr) +#define for_each_rsnd_dai(rdai, priv, i) \ + for (i = 0, (rdai) = rsnd_dai_get(priv, i); \ + i < rsnd_dai_nr(priv); \ + i++, (rdai) = rsnd_dai_get(priv, i)) + +struct rsnd_dai *rsnd_dai_get(struct rsnd_priv *priv, int id); +int rsnd_dai_is_play(struct rsnd_dai *rdai, struct rsnd_dai_stream *io); +#define rsnd_dai_get_platform_info(rdai) ((rdai)->info) + +void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt); +int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional); + +/* + * R-Car sound priv + */ +struct rsnd_priv { + + struct device *dev; + struct rcar_snd_info *info; + spinlock_t lock; + + /* + * below value will be filled on rsnd_dai_probe() + */ + struct snd_soc_dai_driver *daidrv; + struct rsnd_dai *rdai; + int dai_nr; +}; + +#define rsnd_priv_to_dev(priv) ((priv)->dev) +#define rsnd_lock(priv, flags) spin_lock_irqsave(&priv->lock, flags) +#define rsnd_unlock(priv, flags) spin_unlock_irqrestore(&priv->lock, flags) + +#endif -- cgit v1.2.3 From 3337744ac41bee00b0068ad5f926dd9c27540809 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 21 Jul 2013 21:36:21 -0700 Subject: ASoC: add Renesas R-Car Generation feature Renesas R-Car series sound circuit consists of SSI and its peripheral. But this peripheral circuit is different between R-Car Generation1 (E1/M1/H1) and Generation2 (E2/M2/H2) (Actually, there are many difference in Generation1 chips) The main difference between Gen1 and Gen2 are 1) register offset, 2) data path In order to control Gen1/Gen2 by same method, this patch adds gen.c. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/rcar_snd.h | 10 +++ sound/soc/sh/rcar/Makefile | 2 +- sound/soc/sh/rcar/core.c | 58 ++++++++++++++++- sound/soc/sh/rcar/gen.c | 154 +++++++++++++++++++++++++++++++++++++++++++++ sound/soc/sh/rcar/rsnd.h | 47 ++++++++++++++ 5 files changed, 269 insertions(+), 2 deletions(-) create mode 100644 sound/soc/sh/rcar/gen.c (limited to 'include') diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index 7272b2ea7108..14942a827fe5 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -22,6 +22,16 @@ struct rsnd_dai_platform_info { int ssi_id_capture; }; +/* + * flags + * + * 0x0000000A + * + * A : generation + */ +#define RSND_GEN1 (1 << 0) /* fixme */ +#define RSND_GEN2 (2 << 0) /* fixme */ + struct rcar_snd_info { u32 flags; struct rsnd_dai_platform_info *dai_info; diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile index cd8089f20c94..b2d313b1eb94 100644 --- a/sound/soc/sh/rcar/Makefile +++ b/sound/soc/sh/rcar/Makefile @@ -1,2 +1,2 @@ -snd-soc-rcar-objs := core.o +snd-soc-rcar-objs := core.o gen.o obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index a47fda2aa600..bb8959f93a7d 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -107,6 +107,50 @@ priv->info->func(param)) +/* + * basic function + */ +u32 rsnd_read(struct rsnd_priv *priv, + struct rsnd_mod *mod, enum rsnd_reg reg) +{ + void __iomem *base = rsnd_gen_reg_get(priv, mod, reg); + + BUG_ON(!base); + + return ioread32(base); +} + +void rsnd_write(struct rsnd_priv *priv, + struct rsnd_mod *mod, + enum rsnd_reg reg, u32 data) +{ + void __iomem *base = rsnd_gen_reg_get(priv, mod, reg); + struct device *dev = rsnd_priv_to_dev(priv); + + BUG_ON(!base); + + dev_dbg(dev, "w %p : %08x\n", base, data); + + iowrite32(data, base); +} + +void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, + enum rsnd_reg reg, u32 mask, u32 data) +{ + void __iomem *base = rsnd_gen_reg_get(priv, mod, reg); + struct device *dev = rsnd_priv_to_dev(priv); + u32 val; + + BUG_ON(!base); + + val = ioread32(base); + val &= ~mask; + val |= data & mask; + iowrite32(val, base); + + dev_dbg(dev, "s %p : %08x\n", base, val); +} + /* * rsnd_mod functions */ @@ -289,6 +333,10 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, if (ret < 0) goto dai_trigger_end; + ret = rsnd_gen_path_init(priv, rdai, io); + if (ret < 0) + goto dai_trigger_end; + ret = rsnd_dai_call(rdai, io, init); if (ret < 0) goto dai_trigger_end; @@ -306,10 +354,13 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, if (ret < 0) goto dai_trigger_end; - ret = rsnd_platform_call(priv, dai, stop, ssi_id); + ret = rsnd_gen_path_exit(priv, rdai, io); if (ret < 0) goto dai_trigger_end; + ret = rsnd_platform_call(priv, dai, stop, ssi_id); + if (ret < 0) + goto dai_trigger_end; break; default: ret = -EINVAL; @@ -572,6 +623,10 @@ static int rsnd_probe(struct platform_device *pdev) /* * init each module */ + ret = rsnd_gen_probe(pdev, info, priv); + if (ret < 0) + return ret; + ret = rsnd_dai_probe(pdev, info, priv); if (ret < 0) return ret; @@ -615,6 +670,7 @@ static int rsnd_remove(struct platform_device *pdev) * remove each module */ rsnd_dai_remove(pdev, priv); + rsnd_gen_remove(pdev, priv); return 0; } diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c new file mode 100644 index 000000000000..ec67a796eca2 --- /dev/null +++ b/sound/soc/sh/rcar/gen.c @@ -0,0 +1,154 @@ +/* + * Renesas R-Car Gen1 SRU/SSI support + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Kuninori Morimoto + * + * 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. + */ +#include "rsnd.h" + +struct rsnd_gen_ops { + int (*path_init)(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io); + int (*path_exit)(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io); +}; + +struct rsnd_gen_reg_map { + int index; /* -1 : not supported */ + u32 offset_id; /* offset of ssi0, ssi1, ssi2... */ + u32 offset_adr; /* offset of SSICR, SSISR, ... */ +}; + +struct rsnd_gen { + void __iomem *base[RSND_BASE_MAX]; + + struct rsnd_gen_reg_map reg_map[RSND_REG_MAX]; + struct rsnd_gen_ops *ops; +}; + +#define rsnd_priv_to_gen(p) ((struct rsnd_gen *)(p)->gen) + +#define rsnd_is_gen1(s) ((s)->info->flags & RSND_GEN1) +#define rsnd_is_gen2(s) ((s)->info->flags & RSND_GEN2) + +/* + * Gen2 + * will be filled in the future + */ + +/* + * Gen1 + */ +static int rsnd_gen1_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv) +{ + return 0; +} + +static void rsnd_gen1_remove(struct platform_device *pdev, + struct rsnd_priv *priv) +{ +} + +/* + * Gen + */ +int rsnd_gen_path_init(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + + return gen->ops->path_init(priv, rdai, io); +} + +int rsnd_gen_path_exit(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + + return gen->ops->path_exit(priv, rdai, io); +} + +void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv, + struct rsnd_mod *mod, + enum rsnd_reg reg) +{ + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + struct device *dev = rsnd_priv_to_dev(priv); + int index; + u32 offset_id, offset_adr; + + if (reg >= RSND_REG_MAX) { + dev_err(dev, "rsnd_reg reg error\n"); + return NULL; + } + + index = gen->reg_map[reg].index; + offset_id = gen->reg_map[reg].offset_id; + offset_adr = gen->reg_map[reg].offset_adr; + + if (index < 0) { + dev_err(dev, "unsupported reg access %d\n", reg); + return NULL; + } + + if (offset_id && mod) + offset_id *= rsnd_mod_id(mod); + + /* + * index/offset were set on gen1/gen2 + */ + + return gen->base[index] + offset_id + offset_adr; +} + +int rsnd_gen_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_gen *gen; + int i; + + gen = devm_kzalloc(dev, sizeof(*gen), GFP_KERNEL); + if (!gen) { + dev_err(dev, "GEN allocate failed\n"); + return -ENOMEM; + } + + priv->gen = gen; + + /* + * see + * rsnd_reg_get() + * rsnd_gen_probe() + */ + for (i = 0; i < RSND_REG_MAX; i++) + gen->reg_map[i].index = -1; + + /* + * init each module + */ + if (rsnd_is_gen1(priv)) + return rsnd_gen1_probe(pdev, info, priv); + + dev_err(dev, "unknown generation R-Car sound device\n"); + + return -ENODEV; +} + +void rsnd_gen_remove(struct platform_device *pdev, + struct rsnd_priv *priv) +{ + if (rsnd_is_gen1(priv)) + rsnd_gen1_remove(pdev, priv); +} diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 65d3835cffbc..8cc36416da25 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -27,11 +27,35 @@ * This driver uses pseudo register in order to hide it. * see gen1/gen2 for detail */ +enum rsnd_reg { + RSND_REG_MAX, +}; + struct rsnd_priv; struct rsnd_mod; struct rsnd_dai; struct rsnd_dai_stream; +/* + * R-Car basic functions + */ +#define rsnd_mod_read(m, r) \ + rsnd_read(rsnd_mod_to_priv(m), m, RSND_REG_##r) +#define rsnd_mod_write(m, r, d) \ + rsnd_write(rsnd_mod_to_priv(m), m, RSND_REG_##r, d) +#define rsnd_mod_bset(m, r, s, d) \ + rsnd_bset(rsnd_mod_to_priv(m), m, RSND_REG_##r, s, d) + +#define rsnd_priv_read(p, r) rsnd_read(p, NULL, RSND_REG_##r) +#define rsnd_priv_write(p, r, d) rsnd_write(p, NULL, RSND_REG_##r, d) +#define rsnd_priv_bset(p, r, s, d) rsnd_bset(p, NULL, RSND_REG_##r, s, d) + +u32 rsnd_read(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg); +void rsnd_write(struct rsnd_priv *priv, struct rsnd_mod *mod, + enum rsnd_reg reg, u32 data); +void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg, + u32 mask, u32 data); + /* * R-Car sound mod */ @@ -116,6 +140,24 @@ int rsnd_dai_is_play(struct rsnd_dai *rdai, struct rsnd_dai_stream *io); void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt); int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional); +/* + * R-Car Gen1/Gen2 + */ +int rsnd_gen_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv); +void rsnd_gen_remove(struct platform_device *pdev, + struct rsnd_priv *priv); +int rsnd_gen_path_init(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io); +int rsnd_gen_path_exit(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io); +void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv, + struct rsnd_mod *mod, + enum rsnd_reg reg); + /* * R-Car sound priv */ @@ -125,6 +167,11 @@ struct rsnd_priv { struct rcar_snd_info *info; spinlock_t lock; + /* + * below value will be filled on rsnd_gen_probe() + */ + void *gen; + /* * below value will be filled on rsnd_dai_probe() */ -- cgit v1.2.3 From 07539c1de82cdc0ecbe72b413762b2e920407227 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 21 Jul 2013 21:36:35 -0700 Subject: ASoC: add Renesas R-Car SCU feature Renesas R-Car series sound circuit consists of SSI and its peripheral. But this peripheral circuit is different between R-Car Generation1 (E1/M1/H1) and Generation2 (E2/M2/H2) (Actually, there are many difference in Generation1 chips) This patch adds SCU feature on this driver. But, it defines SCU style only, does nothing at this point. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/rcar_snd.h | 11 +++- sound/soc/sh/rcar/Makefile | 4 +- sound/soc/sh/rcar/core.c | 5 ++ sound/soc/sh/rcar/gen.c | 95 ++++++++++++++++++++++++++++++++++ sound/soc/sh/rcar/rsnd.h | 21 ++++++++ sound/soc/sh/rcar/scu.c | 125 +++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 258 insertions(+), 3 deletions(-) create mode 100644 sound/soc/sh/rcar/scu.c (limited to 'include') diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index 14942a827fe5..01f2e453dcbf 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -14,8 +14,15 @@ #include +#define RSND_GEN1_SRU 0 -#define RSND_BASE_MAX 0 +#define RSND_GEN2_SRU 0 + +#define RSND_BASE_MAX 1 + +struct rsnd_scu_platform_info { + u32 flags; +}; struct rsnd_dai_platform_info { int ssi_id_playback; @@ -34,6 +41,8 @@ struct rsnd_dai_platform_info { struct rcar_snd_info { u32 flags; + struct rsnd_scu_platform_info *scu_info; + int scu_info_nr; struct rsnd_dai_platform_info *dai_info; int dai_info_nr; int (*start)(int id); diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile index b2d313b1eb94..112b2cfd793b 100644 --- a/sound/soc/sh/rcar/Makefile +++ b/sound/soc/sh/rcar/Makefile @@ -1,2 +1,2 @@ -snd-soc-rcar-objs := core.o gen.o -obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o +snd-soc-rcar-objs := core.o gen.o scu.o +obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o \ No newline at end of file diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index bb8959f93a7d..02d736bb4f54 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -631,6 +631,10 @@ static int rsnd_probe(struct platform_device *pdev) if (ret < 0) return ret; + ret = rsnd_scu_probe(pdev, info, priv); + if (ret < 0) + return ret; + /* * asoc register */ @@ -669,6 +673,7 @@ static int rsnd_remove(struct platform_device *pdev) /* * remove each module */ + rsnd_scu_remove(pdev, priv); rsnd_dai_remove(pdev, priv); rsnd_gen_remove(pdev, priv); diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index ec67a796eca2..2934c0d731c8 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -45,10 +45,105 @@ struct rsnd_gen { /* * Gen1 */ +static int rsnd_gen1_path_init(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_dai_platform_info *info = rsnd_dai_get_platform_info(rdai); + struct rsnd_mod *mod; + int ret; + int id; + + /* + * Gen1 is created by SRU/SSI, and this SRU is base module of + * Gen2's SCU/SSIU/SSI. (Gen2 SCU/SSIU came from SRU) + * + * Easy image is.. + * Gen1 SRU = Gen2 SCU + SSIU + etc + * + * Gen2 SCU path is very flexible, but, Gen1 SRU (SCU parts) is + * using fixed path. + * + * Then, SSI id = SCU id here + */ + + if (rsnd_dai_is_play(rdai, io)) + id = info->ssi_id_playback; + else + id = info->ssi_id_capture; + + /* SCU */ + mod = rsnd_scu_mod_get(priv, id); + ret = rsnd_dai_connect(rdai, mod, io); + + return ret; +} + +static int rsnd_gen1_path_exit(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_mod *mod, *n; + int ret = 0; + + /* + * remove all mod from rdai + */ + for_each_rsnd_mod(mod, n, io) + ret |= rsnd_dai_disconnect(mod); + + return ret; +} + +static struct rsnd_gen_ops rsnd_gen1_ops = { + .path_init = rsnd_gen1_path_init, + .path_exit = rsnd_gen1_path_exit, +}; + +#define RSND_GEN1_REG_MAP(g, s, i, oi, oa) \ + do { \ + (g)->reg_map[RSND_REG_##i].index = RSND_GEN1_##s; \ + (g)->reg_map[RSND_REG_##i].offset_id = oi; \ + (g)->reg_map[RSND_REG_##i].offset_adr = oa; \ + } while (0) + +static void rsnd_gen1_reg_map_init(struct rsnd_gen *gen) +{ + RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE0, 0x0, 0xD0); + RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE1, 0x0, 0xD4); +} + static int rsnd_gen1_probe(struct platform_device *pdev, struct rcar_snd_info *info, struct rsnd_priv *priv) { + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + struct resource *sru_res; + + /* + * map address + */ + sru_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_SRU); + if (!sru_res) { + dev_err(dev, "Not enough SRU/SSI/ADG platform resources.\n"); + return -ENODEV; + } + + gen->ops = &rsnd_gen1_ops; + + gen->base[RSND_GEN1_SRU] = devm_ioremap_resource(dev, sru_res); + if (!gen->base[RSND_GEN1_SRU]) { + dev_err(dev, "SRU/SSI/ADG ioremap failed\n"); + return -ENODEV; + } + + rsnd_gen1_reg_map_init(gen); + + dev_dbg(dev, "Gen1 device probed\n"); + dev_dbg(dev, "SRU : %08x => %p\n", sru_res->start, + gen->base[RSND_GEN1_SRU]); + return 0; } diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 8cc36416da25..95a391ff0627 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -28,6 +28,10 @@ * see gen1/gen2 for detail */ enum rsnd_reg { + /* SRU/SCU */ + RSND_REG_SSI_MODE0, + RSND_REG_SSI_MODE1, + RSND_REG_MAX, }; @@ -172,6 +176,12 @@ struct rsnd_priv { */ void *gen; + /* + * below value will be filled on rsnd_scu_probe() + */ + void *scu; + int scu_nr; + /* * below value will be filled on rsnd_dai_probe() */ @@ -184,4 +194,15 @@ struct rsnd_priv { #define rsnd_lock(priv, flags) spin_lock_irqsave(&priv->lock, flags) #define rsnd_unlock(priv, flags) spin_unlock_irqrestore(&priv->lock, flags) +/* + * R-Car SCU + */ +int rsnd_scu_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv); +void rsnd_scu_remove(struct platform_device *pdev, + struct rsnd_priv *priv); +struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id); +#define rsnd_scu_nr(priv) ((priv)->scu_nr) + #endif diff --git a/sound/soc/sh/rcar/scu.c b/sound/soc/sh/rcar/scu.c new file mode 100644 index 000000000000..c12e65f240a1 --- /dev/null +++ b/sound/soc/sh/rcar/scu.c @@ -0,0 +1,125 @@ +/* + * Renesas R-Car SCU support + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Kuninori Morimoto + * + * 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. + */ +#include "rsnd.h" + +struct rsnd_scu { + struct rsnd_scu_platform_info *info; /* rcar_snd.h */ + struct rsnd_mod mod; +}; + +#define rsnd_mod_to_scu(_mod) \ + container_of((_mod), struct rsnd_scu, mod) + +#define for_each_rsnd_scu(pos, priv, i) \ + for ((i) = 0; \ + ((i) < rsnd_scu_nr(priv)) && \ + ((pos) = (struct rsnd_scu *)(priv)->scu + i); \ + i++) + +static int rsnd_scu_init(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + dev_dbg(dev, "%s.%d init\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + + return 0; +} + +static int rsnd_scu_quit(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + dev_dbg(dev, "%s.%d quit\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + + return 0; +} + +static int rsnd_scu_start(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + dev_dbg(dev, "%s.%d start\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + + return 0; +} + +static int rsnd_scu_stop(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + dev_dbg(dev, "%s.%d stop\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + + return 0; +} + +static struct rsnd_mod_ops rsnd_scu_ops = { + .name = "scu", + .init = rsnd_scu_init, + .quit = rsnd_scu_quit, + .start = rsnd_scu_start, + .stop = rsnd_scu_stop, +}; + +struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id) +{ + BUG_ON(id < 0 || id >= rsnd_scu_nr(priv)); + + return &((struct rsnd_scu *)(priv->scu) + id)->mod; +} + +int rsnd_scu_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_scu *scu; + int i, nr; + + /* + * init SCU + */ + nr = info->scu_info_nr; + scu = devm_kzalloc(dev, sizeof(*scu) * nr, GFP_KERNEL); + if (!scu) { + dev_err(dev, "SCU allocate failed\n"); + return -ENOMEM; + } + + priv->scu_nr = nr; + priv->scu = scu; + + for_each_rsnd_scu(scu, priv, i) { + rsnd_mod_init(priv, &scu->mod, + &rsnd_scu_ops, i); + scu->info = &info->scu_info[i]; + } + + dev_dbg(dev, "scu probed\n"); + + return 0; +} + +void rsnd_scu_remove(struct platform_device *pdev, + struct rsnd_priv *priv) +{ +} -- cgit v1.2.3 From dfc9403b7c1f566bb099a12c58aee20589e390f1 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 21 Jul 2013 21:36:46 -0700 Subject: ASoC: add Renesas R-Car ADG feature Renesas R-Car series sound circuit consists of SSI and its peripheral. But this peripheral circuit is different between R-Car Generation1 (E1/M1/H1) and Generation2 (E2/M2/H2) (Actually, there are many difference in Generation1 chips) This patch adds ADG feature which controls sound clock Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/rcar_snd.h | 4 +- sound/soc/sh/rcar/Makefile | 2 +- sound/soc/sh/rcar/adg.c | 234 +++++++++++++++++++++++++++++++++++++++++++++ sound/soc/sh/rcar/core.c | 5 + sound/soc/sh/rcar/gen.c | 20 +++- sound/soc/sh/rcar/rsnd.h | 27 ++++++ 6 files changed, 288 insertions(+), 4 deletions(-) create mode 100644 sound/soc/sh/rcar/adg.c (limited to 'include') diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index 01f2e453dcbf..6babd6f7b537 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -15,10 +15,12 @@ #include #define RSND_GEN1_SRU 0 +#define RSND_GEN1_ADG 1 #define RSND_GEN2_SRU 0 +#define RSND_GEN2_ADG 1 -#define RSND_BASE_MAX 1 +#define RSND_BASE_MAX 2 struct rsnd_scu_platform_info { u32 flags; diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile index 112b2cfd793b..c11280cffcfe 100644 --- a/sound/soc/sh/rcar/Makefile +++ b/sound/soc/sh/rcar/Makefile @@ -1,2 +1,2 @@ -snd-soc-rcar-objs := core.o gen.o scu.o +snd-soc-rcar-objs := core.o gen.o scu.o adg.o obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o \ No newline at end of file diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c new file mode 100644 index 000000000000..d80deb7ccf13 --- /dev/null +++ b/sound/soc/sh/rcar/adg.c @@ -0,0 +1,234 @@ +/* + * Helper routines for R-Car sound ADG. + * + * Copyright (C) 2013 Kuninori Morimoto + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include +#include +#include "rsnd.h" + +#define CLKA 0 +#define CLKB 1 +#define CLKC 2 +#define CLKI 3 +#define CLKMAX 4 + +struct rsnd_adg { + struct clk *clk[CLKMAX]; + + int rate_of_441khz_div_6; + int rate_of_48khz_div_6; +}; + +#define for_each_rsnd_clk(pos, adg, i) \ + for (i = 0, (pos) = adg->clk[i]; \ + i < CLKMAX; \ + i++, (pos) = adg->clk[i]) +#define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg) + +static enum rsnd_reg rsnd_adg_ssi_reg_get(int id) +{ + enum rsnd_reg reg; + + /* + * SSI 8 is not connected to ADG. + * it works with SSI 7 + */ + if (id == 8) + return RSND_REG_MAX; + + if (0 <= id && id <= 3) + reg = RSND_REG_AUDIO_CLK_SEL0; + else if (4 <= id && id <= 7) + reg = RSND_REG_AUDIO_CLK_SEL1; + else + reg = RSND_REG_AUDIO_CLK_SEL2; + + return reg; +} + +int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + enum rsnd_reg reg; + int id; + + /* + * "mod" = "ssi" here. + * we can get "ssi id" from mod + */ + id = rsnd_mod_id(mod); + reg = rsnd_adg_ssi_reg_get(id); + + rsnd_write(priv, mod, reg, 0); + + return 0; +} + +int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + struct device *dev = rsnd_priv_to_dev(priv); + struct clk *clk; + enum rsnd_reg reg; + int id, shift, i; + u32 data; + int sel_table[] = { + [CLKA] = 0x1, + [CLKB] = 0x2, + [CLKC] = 0x3, + [CLKI] = 0x0, + }; + + dev_dbg(dev, "request clock = %d\n", rate); + + /* + * find suitable clock from + * AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC/AUDIO_CLKI. + */ + data = 0; + for_each_rsnd_clk(clk, adg, i) { + if (rate == clk_get_rate(clk)) { + data = sel_table[i]; + goto found_clock; + } + } + + /* + * find 1/6 clock from BRGA/BRGB + */ + if (rate == adg->rate_of_441khz_div_6) { + data = 0x10; + goto found_clock; + } + + if (rate == adg->rate_of_48khz_div_6) { + data = 0x20; + goto found_clock; + } + + return -EIO; + +found_clock: + + /* + * This "mod" = "ssi" here. + * we can get "ssi id" from mod + */ + id = rsnd_mod_id(mod); + reg = rsnd_adg_ssi_reg_get(id); + + dev_dbg(dev, "ADG: ssi%d selects clk%d = %d", id, i, rate); + + /* + * Enable SSIx clock + */ + shift = (id % 4) * 8; + + rsnd_bset(priv, mod, reg, + 0xFF << shift, + data << shift); + + return 0; +} + +static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg) +{ + struct clk *clk; + unsigned long rate; + u32 ckr; + int i; + int brg_table[] = { + [CLKA] = 0x0, + [CLKB] = 0x1, + [CLKC] = 0x4, + [CLKI] = 0x2, + }; + + /* + * This driver is assuming that AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC + * have 44.1kHz or 48kHz base clocks for now. + * + * SSI itself can divide parent clock by 1/1 - 1/16 + * So, BRGA outputs 44.1kHz base parent clock 1/32, + * and, BRGB outputs 48.0kHz base parent clock 1/32 here. + * see + * rsnd_adg_ssi_clk_try_start() + */ + ckr = 0; + adg->rate_of_441khz_div_6 = 0; + adg->rate_of_48khz_div_6 = 0; + for_each_rsnd_clk(clk, adg, i) { + rate = clk_get_rate(clk); + + if (0 == rate) /* not used */ + continue; + + /* RBGA */ + if (!adg->rate_of_441khz_div_6 && (0 == rate % 44100)) { + adg->rate_of_441khz_div_6 = rate / 6; + ckr |= brg_table[i] << 20; + } + + /* RBGB */ + if (!adg->rate_of_48khz_div_6 && (0 == rate % 48000)) { + adg->rate_of_48khz_div_6 = rate / 6; + ckr |= brg_table[i] << 16; + } + } + + rsnd_priv_bset(priv, SSICKR, 0x00FF0000, ckr); + rsnd_priv_write(priv, BRRA, 0x00000002); /* 1/6 */ + rsnd_priv_write(priv, BRRB, 0x00000002); /* 1/6 */ +} + +int rsnd_adg_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv) +{ + struct rsnd_adg *adg; + struct device *dev = rsnd_priv_to_dev(priv); + struct clk *clk; + int i; + + adg = devm_kzalloc(dev, sizeof(*adg), GFP_KERNEL); + if (!adg) { + dev_err(dev, "ADG allocate failed\n"); + return -ENOMEM; + } + + adg->clk[CLKA] = clk_get(NULL, "audio_clk_a"); + adg->clk[CLKB] = clk_get(NULL, "audio_clk_b"); + adg->clk[CLKC] = clk_get(NULL, "audio_clk_c"); + adg->clk[CLKI] = clk_get(NULL, "audio_clk_internal"); + for_each_rsnd_clk(clk, adg, i) { + if (IS_ERR(clk)) { + dev_err(dev, "Audio clock failed\n"); + return -EIO; + } + } + + rsnd_adg_ssi_clk_init(priv, adg); + + priv->adg = adg; + + dev_dbg(dev, "adg probed\n"); + + return 0; +} + +void rsnd_adg_remove(struct platform_device *pdev, + struct rsnd_priv *priv) +{ + struct rsnd_adg *adg = priv->adg; + struct clk *clk; + int i; + + for_each_rsnd_clk(clk, adg, i) + clk_put(clk); +} diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 02d736bb4f54..e588d8a8ae40 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -635,6 +635,10 @@ static int rsnd_probe(struct platform_device *pdev) if (ret < 0) return ret; + ret = rsnd_adg_probe(pdev, info, priv); + if (ret < 0) + return ret; + /* * asoc register */ @@ -673,6 +677,7 @@ static int rsnd_remove(struct platform_device *pdev) /* * remove each module */ + rsnd_adg_remove(pdev, priv); rsnd_scu_remove(pdev, priv); rsnd_dai_remove(pdev, priv); rsnd_gen_remove(pdev, priv); diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 2934c0d731c8..ed21a136354f 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -111,6 +111,15 @@ static void rsnd_gen1_reg_map_init(struct rsnd_gen *gen) { RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE0, 0x0, 0xD0); RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE1, 0x0, 0xD4); + + RSND_GEN1_REG_MAP(gen, ADG, BRRA, 0x0, 0x00); + RSND_GEN1_REG_MAP(gen, ADG, BRRB, 0x0, 0x04); + RSND_GEN1_REG_MAP(gen, ADG, SSICKR, 0x0, 0x08); + RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL0, 0x0, 0x0c); + RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL1, 0x0, 0x10); + RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL3, 0x0, 0x18); + RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL4, 0x0, 0x1c); + RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL5, 0x0, 0x20); } static int rsnd_gen1_probe(struct platform_device *pdev, @@ -120,12 +129,15 @@ static int rsnd_gen1_probe(struct platform_device *pdev, struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_gen *gen = rsnd_priv_to_gen(priv); struct resource *sru_res; + struct resource *adg_res; /* * map address */ sru_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_SRU); - if (!sru_res) { + adg_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_ADG); + if (!sru_res || + !adg_res) { dev_err(dev, "Not enough SRU/SSI/ADG platform resources.\n"); return -ENODEV; } @@ -133,7 +145,9 @@ static int rsnd_gen1_probe(struct platform_device *pdev, gen->ops = &rsnd_gen1_ops; gen->base[RSND_GEN1_SRU] = devm_ioremap_resource(dev, sru_res); - if (!gen->base[RSND_GEN1_SRU]) { + gen->base[RSND_GEN1_ADG] = devm_ioremap_resource(dev, adg_res); + if (!gen->base[RSND_GEN1_SRU] || + !gen->base[RSND_GEN1_ADG]) { dev_err(dev, "SRU/SSI/ADG ioremap failed\n"); return -ENODEV; } @@ -143,6 +157,8 @@ static int rsnd_gen1_probe(struct platform_device *pdev, dev_dbg(dev, "Gen1 device probed\n"); dev_dbg(dev, "SRU : %08x => %p\n", sru_res->start, gen->base[RSND_GEN1_SRU]); + dev_dbg(dev, "ADG : %08x => %p\n", adg_res->start, + gen->base[RSND_GEN1_ADG]); return 0; } diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 95a391ff0627..344fd59cb7fd 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -32,6 +32,17 @@ enum rsnd_reg { RSND_REG_SSI_MODE0, RSND_REG_SSI_MODE1, + /* ADG */ + RSND_REG_BRRA, + RSND_REG_BRRB, + RSND_REG_SSICKR, + RSND_REG_AUDIO_CLK_SEL0, + RSND_REG_AUDIO_CLK_SEL1, + RSND_REG_AUDIO_CLK_SEL2, + RSND_REG_AUDIO_CLK_SEL3, + RSND_REG_AUDIO_CLK_SEL4, + RSND_REG_AUDIO_CLK_SEL5, + RSND_REG_MAX, }; @@ -162,6 +173,17 @@ void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg); +/* + * R-Car ADG + */ +int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod); +int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate); +int rsnd_adg_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv); +void rsnd_adg_remove(struct platform_device *pdev, + struct rsnd_priv *priv); + /* * R-Car sound priv */ @@ -182,6 +204,11 @@ struct rsnd_priv { void *scu; int scu_nr; + /* + * below value will be filled on rsnd_adg_probe() + */ + void *adg; + /* * below value will be filled on rsnd_dai_probe() */ -- cgit v1.2.3 From ae5c322303fff50b93d60e34c6563f1264a5941b Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 21 Jul 2013 21:36:57 -0700 Subject: ASoC: add Renesas R-Car SSI feature Renesas R-Car series sound circuit consists of SSI and its peripheral. But this peripheral circuit is different between R-Car Generation1 (E1/M1/H1) and Generation2 (E2/M2/H2) (Actually, there are many difference in Generation1 chips) As 1st protype, this patch adds SSI feature on this driver. But, it is PIO sound playback support only at this point. The DMA transfer, and capture feature will be supported in the future Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/rcar_snd.h | 23 +- sound/soc/sh/rcar/Makefile | 2 +- sound/soc/sh/rcar/core.c | 5 + sound/soc/sh/rcar/gen.c | 24 +- sound/soc/sh/rcar/rsnd.h | 23 ++ sound/soc/sh/rcar/ssi.c | 588 +++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 661 insertions(+), 4 deletions(-) create mode 100644 sound/soc/sh/rcar/ssi.c (limited to 'include') diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index 6babd6f7b537..99d8dd029906 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -16,11 +16,30 @@ #define RSND_GEN1_SRU 0 #define RSND_GEN1_ADG 1 +#define RSND_GEN1_SSI 2 #define RSND_GEN2_SRU 0 #define RSND_GEN2_ADG 1 +#define RSND_GEN2_SSIU 2 +#define RSND_GEN2_SSI 3 -#define RSND_BASE_MAX 2 +#define RSND_BASE_MAX 4 + +/* + * flags + * + * 0xA0000000 + * + * A : clock sharing settings + */ +#define RSND_SSI_CLK_PIN_SHARE (1 << 31) +#define RSND_SSI_CLK_FROM_ADG (1 << 30) /* clock parent is master */ +#define RSND_SSI_SYNC (1 << 29) /* SSI34_sync etc */ + +struct rsnd_ssi_platform_info { + int pio_irq; + u32 flags; +}; struct rsnd_scu_platform_info { u32 flags; @@ -43,6 +62,8 @@ struct rsnd_dai_platform_info { struct rcar_snd_info { u32 flags; + struct rsnd_ssi_platform_info *ssi_info; + int ssi_info_nr; struct rsnd_scu_platform_info *scu_info; int scu_info_nr; struct rsnd_dai_platform_info *dai_info; diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile index c11280cffcfe..0ff492df7929 100644 --- a/sound/soc/sh/rcar/Makefile +++ b/sound/soc/sh/rcar/Makefile @@ -1,2 +1,2 @@ -snd-soc-rcar-objs := core.o gen.o scu.o adg.o +snd-soc-rcar-objs := core.o gen.o scu.o adg.o ssi.o obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o \ No newline at end of file diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index e588d8a8ae40..9a5469d3f352 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -639,6 +639,10 @@ static int rsnd_probe(struct platform_device *pdev) if (ret < 0) return ret; + ret = rsnd_ssi_probe(pdev, info, priv); + if (ret < 0) + return ret; + /* * asoc register */ @@ -677,6 +681,7 @@ static int rsnd_remove(struct platform_device *pdev) /* * remove each module */ + rsnd_ssi_remove(pdev, priv); rsnd_adg_remove(pdev, priv); rsnd_scu_remove(pdev, priv); rsnd_dai_remove(pdev, priv); diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index ed21a136354f..5e4ae0da4352 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -72,6 +72,12 @@ static int rsnd_gen1_path_init(struct rsnd_priv *priv, else id = info->ssi_id_capture; + /* SSI */ + mod = rsnd_ssi_mod_get(priv, id); + ret = rsnd_dai_connect(rdai, mod, io); + if (ret < 0) + return ret; + /* SCU */ mod = rsnd_scu_mod_get(priv, id); ret = rsnd_dai_connect(rdai, mod, io); @@ -120,6 +126,12 @@ static void rsnd_gen1_reg_map_init(struct rsnd_gen *gen) RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL3, 0x0, 0x18); RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL4, 0x0, 0x1c); RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL5, 0x0, 0x20); + + RSND_GEN1_REG_MAP(gen, SSI, SSICR, 0x40, 0x00); + RSND_GEN1_REG_MAP(gen, SSI, SSISR, 0x40, 0x04); + RSND_GEN1_REG_MAP(gen, SSI, SSITDR, 0x40, 0x08); + RSND_GEN1_REG_MAP(gen, SSI, SSIRDR, 0x40, 0x0c); + RSND_GEN1_REG_MAP(gen, SSI, SSIWSR, 0x40, 0x20); } static int rsnd_gen1_probe(struct platform_device *pdev, @@ -130,14 +142,17 @@ static int rsnd_gen1_probe(struct platform_device *pdev, struct rsnd_gen *gen = rsnd_priv_to_gen(priv); struct resource *sru_res; struct resource *adg_res; + struct resource *ssi_res; /* * map address */ sru_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_SRU); adg_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_ADG); + ssi_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_SSI); if (!sru_res || - !adg_res) { + !adg_res || + !ssi_res) { dev_err(dev, "Not enough SRU/SSI/ADG platform resources.\n"); return -ENODEV; } @@ -146,8 +161,10 @@ static int rsnd_gen1_probe(struct platform_device *pdev, gen->base[RSND_GEN1_SRU] = devm_ioremap_resource(dev, sru_res); gen->base[RSND_GEN1_ADG] = devm_ioremap_resource(dev, adg_res); + gen->base[RSND_GEN1_SSI] = devm_ioremap_resource(dev, ssi_res); if (!gen->base[RSND_GEN1_SRU] || - !gen->base[RSND_GEN1_ADG]) { + !gen->base[RSND_GEN1_ADG] || + !gen->base[RSND_GEN1_SSI]) { dev_err(dev, "SRU/SSI/ADG ioremap failed\n"); return -ENODEV; } @@ -159,8 +176,11 @@ static int rsnd_gen1_probe(struct platform_device *pdev, gen->base[RSND_GEN1_SRU]); dev_dbg(dev, "ADG : %08x => %p\n", adg_res->start, gen->base[RSND_GEN1_ADG]); + dev_dbg(dev, "SSI : %08x => %p\n", ssi_res->start, + gen->base[RSND_GEN1_SSI]); return 0; + } static void rsnd_gen1_remove(struct platform_device *pdev, diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 344fd59cb7fd..0e7727cc41db 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -43,6 +43,13 @@ enum rsnd_reg { RSND_REG_AUDIO_CLK_SEL4, RSND_REG_AUDIO_CLK_SEL5, + /* SSI */ + RSND_REG_SSICR, + RSND_REG_SSISR, + RSND_REG_SSITDR, + RSND_REG_SSIRDR, + RSND_REG_SSIWSR, + RSND_REG_MAX, }; @@ -151,6 +158,7 @@ int rsnd_dai_connect(struct rsnd_dai *rdai, struct rsnd_mod *mod, struct rsnd_dai_stream *io); int rsnd_dai_is_play(struct rsnd_dai *rdai, struct rsnd_dai_stream *io); #define rsnd_dai_get_platform_info(rdai) ((rdai)->info) +#define rsnd_io_to_runtime(io) ((io)->substream->runtime) void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt); int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional); @@ -209,6 +217,11 @@ struct rsnd_priv { */ void *adg; + /* + * below value will be filled on rsnd_ssi_probe() + */ + void *ssiu; + /* * below value will be filled on rsnd_dai_probe() */ @@ -232,4 +245,14 @@ void rsnd_scu_remove(struct platform_device *pdev, struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id); #define rsnd_scu_nr(priv) ((priv)->scu_nr) +/* + * R-Car SSI + */ +int rsnd_ssi_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv); +void rsnd_ssi_remove(struct platform_device *pdev, + struct rsnd_priv *priv); +struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); + #endif diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c new file mode 100644 index 000000000000..061ac7e88309 --- /dev/null +++ b/sound/soc/sh/rcar/ssi.c @@ -0,0 +1,588 @@ +/* + * Renesas R-Car SSIU/SSI support + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Kuninori Morimoto + * + * Based on fsi.c + * Kuninori Morimoto + * + * 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. + */ +#include +#include "rsnd.h" +#define RSND_SSI_NAME_SIZE 16 + +/* + * SSICR + */ +#define FORCE (1 << 31) /* Fixed */ +#define UIEN (1 << 27) /* Underflow Interrupt Enable */ +#define OIEN (1 << 26) /* Overflow Interrupt Enable */ +#define IIEN (1 << 25) /* Idle Mode Interrupt Enable */ +#define DIEN (1 << 24) /* Data Interrupt Enable */ + +#define DWL_8 (0 << 19) /* Data Word Length */ +#define DWL_16 (1 << 19) /* Data Word Length */ +#define DWL_18 (2 << 19) /* Data Word Length */ +#define DWL_20 (3 << 19) /* Data Word Length */ +#define DWL_22 (4 << 19) /* Data Word Length */ +#define DWL_24 (5 << 19) /* Data Word Length */ +#define DWL_32 (6 << 19) /* Data Word Length */ + +#define SWL_32 (3 << 16) /* R/W System Word Length */ +#define SCKD (1 << 15) /* Serial Bit Clock Direction */ +#define SWSD (1 << 14) /* Serial WS Direction */ +#define SCKP (1 << 13) /* Serial Bit Clock Polarity */ +#define SWSP (1 << 12) /* Serial WS Polarity */ +#define SDTA (1 << 10) /* Serial Data Alignment */ +#define DEL (1 << 8) /* Serial Data Delay */ +#define CKDV(v) (v << 4) /* Serial Clock Division Ratio */ +#define TRMD (1 << 1) /* Transmit/Receive Mode Select */ +#define EN (1 << 0) /* SSI Module Enable */ + +/* + * SSISR + */ +#define UIRQ (1 << 27) /* Underflow Error Interrupt Status */ +#define OIRQ (1 << 26) /* Overflow Error Interrupt Status */ +#define IIRQ (1 << 25) /* Idle Mode Interrupt Status */ +#define DIRQ (1 << 24) /* Data Interrupt Status Flag */ + +struct rsnd_ssi { + struct clk *clk; + struct rsnd_ssi_platform_info *info; /* rcar_snd.h */ + struct rsnd_ssi *parent; + struct rsnd_mod mod; + + struct rsnd_dai *rdai; + struct rsnd_dai_stream *io; + u32 cr_own; + u32 cr_clk; + u32 cr_etc; + int err; + unsigned int usrcnt; + unsigned int rate; +}; + +struct rsnd_ssiu { + u32 ssi_mode0; + u32 ssi_mode1; + + int ssi_nr; + struct rsnd_ssi *ssi; +}; + +#define for_each_rsnd_ssi(pos, priv, i) \ + for (i = 0; \ + (i < rsnd_ssi_nr(priv)) && \ + ((pos) = ((struct rsnd_ssiu *)((priv)->ssiu))->ssi + i); \ + i++) + +#define rsnd_ssi_nr(priv) (((struct rsnd_ssiu *)((priv)->ssiu))->ssi_nr) +#define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod) +#define rsnd_ssi_is_pio(ssi) ((ssi)->info->pio_irq > 0) +#define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent) +#define rsnd_rdai_is_clk_master(rdai) ((rdai)->clk_master) +#define rsnd_ssi_mode_flags(p) ((p)->info->flags) +#define rsnd_ssi_to_ssiu(ssi)\ + (((struct rsnd_ssiu *)((ssi) - rsnd_mod_id(&(ssi)->mod))) - 1) + +static void rsnd_ssi_mode_init(struct rsnd_priv *priv, + struct rsnd_ssiu *ssiu) +{ + struct rsnd_ssi *ssi; + u32 flags; + u32 val; + int i; + + /* + * SSI_MODE0 + */ + ssiu->ssi_mode0 = 0; + for_each_rsnd_ssi(ssi, priv, i) + ssiu->ssi_mode0 |= (1 << i); + + /* + * SSI_MODE1 + */ +#define ssi_parent_set(p, sync, adg, ext) \ + do { \ + ssi->parent = ssiu->ssi + p; \ + if (flags & RSND_SSI_CLK_FROM_ADG) \ + val = adg; \ + else \ + val = ext; \ + if (flags & RSND_SSI_SYNC) \ + val |= sync; \ + } while (0) + + ssiu->ssi_mode1 = 0; + for_each_rsnd_ssi(ssi, priv, i) { + flags = rsnd_ssi_mode_flags(ssi); + + if (!(flags & RSND_SSI_CLK_PIN_SHARE)) + continue; + + val = 0; + switch (i) { + case 1: + ssi_parent_set(0, (1 << 4), (0x2 << 0), (0x1 << 0)); + break; + case 2: + ssi_parent_set(0, (1 << 4), (0x2 << 2), (0x1 << 2)); + break; + case 4: + ssi_parent_set(3, (1 << 20), (0x2 << 16), (0x1 << 16)); + break; + case 8: + ssi_parent_set(7, 0, 0, 0); + break; + } + + ssiu->ssi_mode1 |= val; + } +} + +static void rsnd_ssi_mode_set(struct rsnd_ssi *ssi) +{ + struct rsnd_ssiu *ssiu = rsnd_ssi_to_ssiu(ssi); + + rsnd_mod_write(&ssi->mod, SSI_MODE0, ssiu->ssi_mode0); + rsnd_mod_write(&ssi->mod, SSI_MODE1, ssiu->ssi_mode1); +} + +static void rsnd_ssi_status_check(struct rsnd_mod *mod, + u32 bit) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + u32 status; + int i; + + for (i = 0; i < 1024; i++) { + status = rsnd_mod_read(mod, SSISR); + if (status & bit) + return; + + udelay(50); + } + + dev_warn(dev, "status check failed\n"); +} + +static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, + unsigned int rate) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod); + struct device *dev = rsnd_priv_to_dev(priv); + int i, j, ret; + int adg_clk_div_table[] = { + 1, 6, /* see adg.c */ + }; + int ssi_clk_mul_table[] = { + 1, 2, 4, 8, 16, 6, 12, + }; + unsigned int main_rate; + + /* + * Find best clock, and try to start ADG + */ + for (i = 0; i < ARRAY_SIZE(adg_clk_div_table); i++) { + for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) { + + /* + * this driver is assuming that + * system word is 64fs (= 2 x 32bit) + * see rsnd_ssi_start() + */ + main_rate = rate / adg_clk_div_table[i] + * 32 * 2 * ssi_clk_mul_table[j]; + + ret = rsnd_adg_ssi_clk_try_start(&ssi->mod, main_rate); + if (0 == ret) { + ssi->rate = rate; + ssi->cr_clk = FORCE | SWL_32 | + SCKD | SWSD | CKDV(j); + + dev_dbg(dev, "ssi%d outputs %u Hz\n", + rsnd_mod_id(&ssi->mod), rate); + + return 0; + } + } + } + + dev_err(dev, "unsupported clock rate\n"); + return -EIO; +} + +static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi) +{ + ssi->rate = 0; + ssi->cr_clk = 0; + rsnd_adg_ssi_clk_stop(&ssi->mod); +} + +static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod); + struct device *dev = rsnd_priv_to_dev(priv); + u32 cr; + + if (0 == ssi->usrcnt) { + clk_enable(ssi->clk); + + if (rsnd_rdai_is_clk_master(rdai)) { + struct snd_pcm_runtime *runtime; + + runtime = rsnd_io_to_runtime(io); + + if (rsnd_ssi_clk_from_parent(ssi)) + rsnd_ssi_hw_start(ssi->parent, rdai, io); + else + rsnd_ssi_master_clk_start(ssi, runtime->rate); + } + } + + cr = ssi->cr_own | + ssi->cr_clk | + ssi->cr_etc | + EN; + + rsnd_mod_write(&ssi->mod, SSICR, cr); + + ssi->usrcnt++; + + dev_dbg(dev, "ssi%d hw started\n", rsnd_mod_id(&ssi->mod)); +} + +static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi, + struct rsnd_dai *rdai) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod); + struct device *dev = rsnd_priv_to_dev(priv); + u32 cr; + + if (0 == ssi->usrcnt) /* stop might be called without start */ + return; + + ssi->usrcnt--; + + if (0 == ssi->usrcnt) { + /* + * disable all IRQ, + * and, wait all data was sent + */ + cr = ssi->cr_own | + ssi->cr_clk; + + rsnd_mod_write(&ssi->mod, SSICR, cr | EN); + rsnd_ssi_status_check(&ssi->mod, DIRQ); + + /* + * disable SSI, + * and, wait idle state + */ + rsnd_mod_write(&ssi->mod, SSICR, cr); /* disabled all */ + rsnd_ssi_status_check(&ssi->mod, IIRQ); + + if (rsnd_rdai_is_clk_master(rdai)) { + if (rsnd_ssi_clk_from_parent(ssi)) + rsnd_ssi_hw_stop(ssi->parent, rdai); + else + rsnd_ssi_master_clk_stop(ssi); + } + + clk_disable(ssi->clk); + } + + dev_dbg(dev, "ssi%d hw stopped\n", rsnd_mod_id(&ssi->mod)); +} + +/* + * SSI mod common functions + */ +static int rsnd_ssi_init(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + u32 cr; + + cr = FORCE; + + /* + * always use 32bit system word for easy clock calculation. + * see also rsnd_ssi_master_clk_enable() + */ + cr |= SWL_32; + + /* + * init clock settings for SSICR + */ + switch (runtime->sample_bits) { + case 16: + cr |= DWL_16; + break; + case 32: + cr |= DWL_24; + break; + default: + return -EIO; + } + + if (rdai->bit_clk_inv) + cr |= SCKP; + if (rdai->frm_clk_inv) + cr |= SWSP; + if (rdai->data_alignment) + cr |= SDTA; + if (rdai->sys_delay) + cr |= DEL; + if (rsnd_dai_is_play(rdai, io)) + cr |= TRMD; + + /* + * set ssi parameter + */ + ssi->rdai = rdai; + ssi->io = io; + ssi->cr_own = cr; + ssi->err = -1; /* ignore 1st error */ + + rsnd_ssi_mode_set(ssi); + + dev_dbg(dev, "%s.%d init\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + + return 0; +} + +static int rsnd_ssi_quit(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + dev_dbg(dev, "%s.%d quit\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + + if (ssi->err > 0) + dev_warn(dev, "ssi under/over flow err = %d\n", ssi->err); + + ssi->rdai = NULL; + ssi->io = NULL; + ssi->cr_own = 0; + ssi->err = 0; + + return 0; +} + +static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status) +{ + /* under/over flow error */ + if (status & (UIRQ | OIRQ)) { + ssi->err++; + + /* clear error status */ + rsnd_mod_write(&ssi->mod, SSISR, 0); + } +} + +/* + * SSI PIO + */ +static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data) +{ + struct rsnd_ssi *ssi = data; + struct rsnd_dai_stream *io = ssi->io; + u32 status = rsnd_mod_read(&ssi->mod, SSISR); + irqreturn_t ret = IRQ_NONE; + + if (io && (status & DIRQ)) { + struct rsnd_dai *rdai = ssi->rdai; + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + u32 *buf = (u32 *)(runtime->dma_area + + rsnd_dai_pointer_offset(io, 0)); + + rsnd_ssi_record_error(ssi, status); + + /* + * 8/16/32 data can be assesse to TDR/RDR register + * directly as 32bit data + * see rsnd_ssi_init() + */ + if (rsnd_dai_is_play(rdai, io)) + rsnd_mod_write(&ssi->mod, SSITDR, *buf); + else + *buf = rsnd_mod_read(&ssi->mod, SSIRDR); + + rsnd_dai_pointer_update(io, sizeof(*buf)); + + ret = IRQ_HANDLED; + } + + return ret; +} + +static int rsnd_ssi_pio_start(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + /* enable PIO IRQ */ + ssi->cr_etc = UIEN | OIEN | DIEN; + + rsnd_ssi_hw_start(ssi, rdai, io); + + dev_dbg(dev, "%s.%d start\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + + return 0; +} + +static int rsnd_ssi_pio_stop(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + + dev_dbg(dev, "%s.%d stop\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + + ssi->cr_etc = 0; + + rsnd_ssi_hw_stop(ssi, rdai); + + return 0; +} + +static struct rsnd_mod_ops rsnd_ssi_pio_ops = { + .name = "ssi (pio)", + .init = rsnd_ssi_init, + .quit = rsnd_ssi_quit, + .start = rsnd_ssi_pio_start, + .stop = rsnd_ssi_pio_stop, +}; + +/* + * Non SSI + */ +static int rsnd_ssi_non(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + dev_dbg(dev, "%s\n", __func__); + + return 0; +} + +static struct rsnd_mod_ops rsnd_ssi_non_ops = { + .name = "ssi (non)", + .init = rsnd_ssi_non, + .quit = rsnd_ssi_non, + .start = rsnd_ssi_non, + .stop = rsnd_ssi_non, +}; + +/* + * ssi mod function + */ +struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id) +{ + BUG_ON(id < 0 || id >= rsnd_ssi_nr(priv)); + + return &(((struct rsnd_ssiu *)(priv->ssiu))->ssi + id)->mod; +} + +int rsnd_ssi_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv) +{ + struct rsnd_ssi_platform_info *pinfo; + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_mod_ops *ops; + struct clk *clk; + struct rsnd_ssiu *ssiu; + struct rsnd_ssi *ssi; + char name[RSND_SSI_NAME_SIZE]; + int i, nr, ret; + + /* + * init SSI + */ + nr = info->ssi_info_nr; + ssiu = devm_kzalloc(dev, sizeof(*ssiu) + (sizeof(*ssi) * nr), + GFP_KERNEL); + if (!ssiu) { + dev_err(dev, "SSI allocate failed\n"); + return -ENOMEM; + } + + priv->ssiu = ssiu; + ssiu->ssi = (struct rsnd_ssi *)(ssiu + 1); + ssiu->ssi_nr = nr; + + for_each_rsnd_ssi(ssi, priv, i) { + pinfo = &info->ssi_info[i]; + + snprintf(name, RSND_SSI_NAME_SIZE, "ssi.%d", i); + + clk = clk_get(dev, name); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + ssi->info = pinfo; + ssi->clk = clk; + + ops = &rsnd_ssi_non_ops; + + /* + * SSI PIO case + */ + if (rsnd_ssi_is_pio(ssi)) { + ret = devm_request_irq(dev, pinfo->pio_irq, + &rsnd_ssi_pio_interrupt, + IRQF_SHARED, + dev_name(dev), ssi); + if (ret) { + dev_err(dev, "SSI request interrupt failed\n"); + return ret; + } + + ops = &rsnd_ssi_pio_ops; + } + + rsnd_mod_init(priv, &ssi->mod, ops, i); + } + + rsnd_ssi_mode_init(priv, ssiu); + + dev_dbg(dev, "ssi probed\n"); + + return 0; +} + +void rsnd_ssi_remove(struct platform_device *pdev, + struct rsnd_priv *priv) +{ + struct rsnd_ssi *ssi; + int i; + + for_each_rsnd_ssi(ssi, priv, i) + clk_put(ssi->clk); +} -- cgit v1.2.3 From 4fefd69853a4e83040ddaa98d3b6e5e12cc4f97a Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Mon, 29 Jul 2013 13:51:58 +0100 Subject: ASoC: core: Add snd_soc_card_get_kcontrol() This is useful for drivers who want to grab a pointer to snd_kcontrol outside of the kcontrol callbacks. Signed-off-by: Dimitris Papastamos Signed-off-by: Mark Brown --- include/sound/soc.h | 2 ++ sound/soc/soc-core.c | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 6eabee7ec15a..b33d1de46396 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -475,6 +475,8 @@ int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops); struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template, void *data, const char *long_name, const char *prefix); +struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card, + const char *name); int snd_soc_add_codec_controls(struct snd_soc_codec *codec, const struct snd_kcontrol_new *controls, int num_controls); int snd_soc_add_platform_controls(struct snd_soc_platform *platform, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 0ec070cf7231..cef714effc1e 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2299,6 +2299,22 @@ static int snd_soc_add_controls(struct snd_card *card, struct device *dev, return 0; } +struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card, + const char *name) +{ + struct snd_card *card = soc_card->snd_card; + struct snd_kcontrol *kctl; + + if (unlikely(!name)) + return NULL; + + list_for_each_entry(kctl, &card->controls, list) + if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name))) + return kctl; + return NULL; +} +EXPORT_SYMBOL_GPL(snd_soc_card_get_kcontrol); + /** * snd_soc_add_codec_controls - add an array of controls to a codec. * Convenience function to add a list of controls. Many codecs were -- cgit v1.2.3 From 564c65049eddb1a95b48958080db97eda88c98dd Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 29 Jul 2013 17:13:55 +0200 Subject: ASoC: dapm: Move snd_soc_dapm_update from dapm context to card The update field of a DAPM context is only assigned while the card's dapm_mutex is locked, the field is also cleared again while the mutex is stil locked. So there will only ever be one DAPM context at a time with a non-NULL update field. So it is safe to move the update field from the DAPM context struct to the card struct. Doing so will allow further cleanups in this area. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 2 -- include/sound/soc.h | 1 + sound/soc/soc-dapm.c | 22 +++++++++++----------- 3 files changed, 12 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index e77c6f5a8390..3397292d94c8 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -575,8 +575,6 @@ struct snd_soc_dapm_context { struct delayed_work delayed_work; unsigned int idle_bias_off:1; /* Use BIAS_OFF instead of STANDBY */ - struct snd_soc_dapm_update *update; - void (*seq_notifier)(struct snd_soc_dapm_context *, enum snd_soc_dapm_type, int); diff --git a/include/sound/soc.h b/include/sound/soc.h index 6eabee7ec15a..b1e1f967ae1e 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1042,6 +1042,7 @@ struct snd_soc_card { /* Generic DAPM context for the card */ struct snd_soc_dapm_context dapm; struct snd_soc_dapm_stats dapm_stats; + struct snd_soc_dapm_update *update; #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_card_root; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 366daef006ed..7449e27bf133 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1425,7 +1425,7 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, static void dapm_widget_update(struct snd_soc_dapm_context *dapm) { - struct snd_soc_dapm_update *update = dapm->update; + struct snd_soc_dapm_update *update = dapm->card->update; struct snd_soc_dapm_widget_list *wlist; struct snd_soc_dapm_widget *w = NULL; unsigned int wi; @@ -1959,9 +1959,9 @@ int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, int ret; mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - dapm->update = update; + card->update = update; ret = soc_dapm_mux_update_power(dapm, kcontrol, mux, e); - dapm->update = NULL; + card->update = NULL; mutex_unlock(&card->dapm_mutex); if (ret > 0) soc_dpcm_runtime_update(card); @@ -2002,9 +2002,9 @@ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, int ret; mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - dapm->update = update; + card->update = update; ret = soc_dapm_mixer_update_power(dapm, kcontrol, connect); - dapm->update = NULL; + card->update = NULL; mutex_unlock(&card->dapm_mutex); if (ret > 0) soc_dpcm_runtime_update(card); @@ -2693,11 +2693,11 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, update.mask = mask; update.val = val; - widget->dapm->update = &update; + card->update = &update; soc_dapm_mixer_update_power(widget->dapm, kcontrol, connect); - widget->dapm->update = NULL; + card->update = NULL; } mutex_unlock(&card->dapm_mutex); @@ -2775,11 +2775,11 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, update.reg = e->reg; update.mask = mask; update.val = val; - widget->dapm->update = &update; + card->update = &update; soc_dapm_mux_update_power(widget->dapm, kcontrol, mux, e); - widget->dapm->update = NULL; + card->update = NULL; } mutex_unlock(&card->dapm_mutex); @@ -2928,11 +2928,11 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, update.reg = e->reg; update.mask = mask; update.val = val; - widget->dapm->update = &update; + card->update = &update; soc_dapm_mux_update_power(widget->dapm, kcontrol, mux, e); - widget->dapm->update = NULL; + card->update = NULL; } mutex_unlock(&card->dapm_mutex); -- cgit v1.2.3 From eee5d7f99ae95059e1a3d1cfa2dea3ed8dbd94ac Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 29 Jul 2013 17:13:57 +0200 Subject: ASoC: dapm: Add a helper to get the CODEC for DAPM kcontrol We use the same 3 lines to get the CODEC for a kcontrol in a quite a few places. This patch puts them into a common helper function. Having this encapsulated in a helper function will also make it more easier to eventually change the data layout of the kcontrol's private data. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 2 ++ sound/soc/codecs/tlv320aic3x.c | 7 +++---- sound/soc/codecs/twl6040.c | 4 +--- sound/soc/codecs/wm8903.c | 4 +--- sound/soc/codecs/wm8994.c | 4 +--- sound/soc/codecs/wm8995.c | 5 +---- sound/soc/codecs/wm_hubs.c | 8 ++------ sound/soc/soc-dapm.c | 40 +++++++++++++++++++++++----------------- 8 files changed, 34 insertions(+), 40 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 3397292d94c8..ebfae8f3fda7 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -427,6 +427,8 @@ void dapm_mark_io_dirty(struct snd_soc_dapm_context *dapm); int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, struct snd_soc_dapm_widget_list **list); +struct snd_soc_codec *snd_soc_dapm_kcontrol_codec(struct snd_kcontrol *kcontrol); + /* dapm widget types */ enum snd_soc_dapm_type { snd_soc_dapm_input = 0, /* input pin */ diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 1325c0c0df50..fec0db04262d 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -138,8 +138,7 @@ static const u8 aic3x_reg[AIC3X_CACHEREGNUM] = { static int snd_soc_dapm_put_volsw_aic3x(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 = snd_soc_dapm_kcontrol_codec(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; unsigned int reg = mc->reg; @@ -165,14 +164,14 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol, mask <<= shift; val <<= shift; - change = snd_soc_test_bits(widget->codec, val, mask, reg); + change = snd_soc_test_bits(codec, val, mask, reg); if (change) { update.kcontrol = kcontrol; update.reg = reg; update.mask = mask; update.val = val; - snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, connect, + snd_soc_dapm_mixer_update_power(&codec->dapm, kcontrol, connect, &update); } diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 44621ddc332d..d6c5bf14179a 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -437,9 +437,7 @@ static irqreturn_t twl6040_audio_handler(int irq, void *data) 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 snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val; diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index fa24cedee687..eebcb1da3b7b 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -364,9 +364,7 @@ static void wm8903_seq_notifier(struct snd_soc_dapm_context *dapm, static int wm8903_class_w_put(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 snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); u16 reg; int ret; diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index ba832b77c543..eee2a01f2691 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -1437,9 +1437,7 @@ SOC_DAPM_SINGLE("AIF1.1 Switch", WM8994_DAC2_RIGHT_MIXER_ROUTING, static int wm8994_put_class_w(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 *w = wlist->widgets[0]; - struct snd_soc_codec *codec = w->codec; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); int ret; ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol); diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c index 90a65c427541..da2899e6c401 100644 --- a/sound/soc/codecs/wm8995.c +++ b/sound/soc/codecs/wm8995.c @@ -549,12 +549,9 @@ static int check_clk_sys(struct snd_soc_dapm_widget *source, static int wm8995_put_class_w(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 *w = wlist->widgets[0]; - struct snd_soc_codec *codec; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); int ret; - codec = w->codec; ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol); wm8995_update_class_w(codec); return ret; diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index 2d9e099415a5..8b50e5958de5 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -699,9 +699,7 @@ EXPORT_SYMBOL_GPL(wm_hubs_update_class_w); static int class_w_put_volsw(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 snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); int ret; ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol); @@ -721,9 +719,7 @@ static int class_w_put_volsw(struct snd_kcontrol *kcontrol, static int class_w_put_double(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 snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); int ret; ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 5db8df2f8866..b18ac5b1cc2e 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -174,6 +174,17 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget( return kmemdup(_widget, sizeof(*_widget), GFP_KERNEL); } +/** + * snd_soc_dapm_kcontrol_codec() - Returns the codec associated to a kcontrol + * @kcontrol: The kcontrol + */ +struct snd_soc_codec *snd_soc_dapm_kcontrol_codec(struct snd_kcontrol *kcontrol) +{ + struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); + return wlist->widgets[0]->codec; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_codec); + static void dapm_reset(struct snd_soc_card *card) { struct snd_soc_dapm_widget *w; @@ -2617,8 +2628,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets); int snd_soc_dapm_get_volsw(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 = snd_soc_dapm_kcontrol_codec(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; unsigned int reg = mc->reg; @@ -2628,12 +2638,12 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, unsigned int invert = mc->invert; if (snd_soc_volsw_is_stereo(mc)) - dev_warn(widget->dapm->dev, + dev_warn(codec->dapm.dev, "ASoC: Control '%s' is stereo, which is not supported\n", kcontrol->id.name); ucontrol->value.integer.value[0] = - (snd_soc_read(widget->codec, reg) >> shift) & mask; + (snd_soc_read(codec, reg) >> shift) & mask; if (invert) ucontrol->value.integer.value[0] = max - ucontrol->value.integer.value[0]; @@ -2654,9 +2664,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_volsw); int snd_soc_dapm_put_volsw(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 snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); struct snd_soc_card *card = codec->card; struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; @@ -2670,7 +2678,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, struct snd_soc_dapm_update update; if (snd_soc_volsw_is_stereo(mc)) - dev_warn(widget->dapm->dev, + dev_warn(codec->dapm.dev, "ASoC: Control '%s' is stereo, which is not supported\n", kcontrol->id.name); @@ -2684,7 +2692,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - change = snd_soc_test_bits(widget->codec, reg, mask, val); + change = snd_soc_test_bits(codec, reg, mask, val); if (change) { update.kcontrol = kcontrol; update.reg = reg; @@ -2715,12 +2723,11 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw); int snd_soc_dapm_get_enum_double(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 = snd_soc_dapm_kcontrol_codec(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val; - val = snd_soc_read(widget->codec, e->reg); + val = snd_soc_read(codec, e->reg); ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & e->mask; if (e->shift_l != e->shift_r) ucontrol->value.enumerated.item[1] = @@ -2765,7 +2772,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - change = snd_soc_test_bits(widget->codec, e->reg, mask, val); + change = snd_soc_test_bits(codec, e->reg, mask, val); if (change) { widget->value = val; @@ -2854,12 +2861,11 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_virt); int snd_soc_dapm_get_value_enum_double(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 = snd_soc_dapm_kcontrol_codec(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int reg_val, val, mux; - reg_val = snd_soc_read(widget->codec, e->reg); + reg_val = snd_soc_read(codec, e->reg); val = (reg_val >> e->shift_l) & e->mask; for (mux = 0; mux < e->max; mux++) { if (val == e->values[mux]) @@ -2918,7 +2924,7 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - change = snd_soc_test_bits(widget->codec, e->reg, mask, val); + change = snd_soc_test_bits(codec, e->reg, mask, val); if (change) { widget->value = val; -- cgit v1.2.3 From cf7c1de20c576477d42deae255cbc6e439bb5dc0 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 29 Jul 2013 17:13:59 +0200 Subject: ASoC: dapm: Move 'value' field from widget to control The 'value' field is really per control and not per widget. Currently it is only used for virtual MUXes, which only have one control per widget. So in that case there is not so much of a difference between whether it is stored per widget or per control. Moving the 'value' field from the widget to the control will allow us to use it also for cases where we have more than one control per widget. E.g. for mixers with multiple input controls. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 1 - sound/soc/soc-dapm.c | 53 +++++++++++++++++++++++++++--------------------- 2 files changed, 30 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index ebfae8f3fda7..d7d26cc8e3fc 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -523,7 +523,6 @@ struct snd_soc_dapm_widget { /* dapm control */ int reg; /* negative reg = no direct dapm */ unsigned char shift; /* bits to shift */ - unsigned int value; /* widget current value */ unsigned int mask; /* non-shifted mask */ unsigned int on_val; /* on state value */ unsigned int off_val; /* off state value */ diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index da35b10ce6d1..bad6f6db74c9 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -175,6 +175,7 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget( } struct dapm_kcontrol_data { + unsigned int value; struct snd_soc_dapm_widget_list wlist; }; @@ -233,6 +234,26 @@ static int dapm_kcontrol_add_widget(struct snd_kcontrol *kcontrol, return 0; } +static unsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol) +{ + struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); + + return data->value; +} + +static bool dapm_kcontrol_set_value(const struct snd_kcontrol *kcontrol, + unsigned int value) +{ + struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); + + if (data->value == value) + return false; + + data->value = value; + + return true; +} + /** * snd_soc_dapm_kcontrol_codec() - Returns the codec associated to a kcontrol * @kcontrol: The kcontrol @@ -2786,9 +2807,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_double); int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = dapm_kcontrol_get_wlist(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; - struct snd_soc_codec *codec = widget->codec; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); struct snd_soc_card *card = codec->card; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val, mux, change; @@ -2811,8 +2830,6 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, change = snd_soc_test_bits(codec, e->reg, mask, val); if (change) { - widget->value = val; - update.kcontrol = kcontrol; update.reg = e->reg; update.mask = mask; @@ -2839,11 +2856,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double); int snd_soc_dapm_get_enum_virt(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = dapm_kcontrol_get_wlist(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; - - ucontrol->value.enumerated.item[0] = widget->value; - + ucontrol->value.enumerated.item[0] = dapm_kcontrol_get_value(kcontrol); return 0; } EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_virt); @@ -2858,10 +2871,9 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_virt); int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = dapm_kcontrol_get_wlist(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; - struct snd_soc_codec *codec = widget->codec; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); struct snd_soc_card *card = codec->card; + unsigned int value; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; int change; @@ -2871,11 +2883,10 @@ int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - change = widget->value != ucontrol->value.enumerated.item[0]; - if (change) { - widget->value = ucontrol->value.enumerated.item[0]; - soc_dapm_mux_update_power(card, kcontrol, widget->value, e); - } + value = ucontrol->value.enumerated.item[0]; + change = dapm_kcontrol_set_value(kcontrol, value); + if (change) + soc_dapm_mux_update_power(card, kcontrol, value, e); mutex_unlock(&card->dapm_mutex); return change; @@ -2938,9 +2949,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_value_enum_double); int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = dapm_kcontrol_get_wlist(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; - struct snd_soc_codec *codec = widget->codec; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); struct snd_soc_card *card = codec->card; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val, mux, change; @@ -2963,8 +2972,6 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, change = snd_soc_test_bits(codec, e->reg, mask, val); if (change) { - widget->value = val; - update.kcontrol = kcontrol; update.reg = e->reg; update.mask = mask; -- cgit v1.2.3 From 5106b92f80a2cd37c52cffed80b4f5acfb77ccfd Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 29 Jul 2013 17:14:00 +0200 Subject: ASoC: dapm: Keep a list of paths per kcontrol Currently we store for each path which control (if any at all) is associated with that control. But we are only ever interested in the reverse relationship, i.e. we want to know all the paths a certain control is associated with. This is currently implemented by always iterating over all paths. This patch updates the code to keep a list for each control which contains all the paths that are associated with that control. This improves the run time of e.g. soc_dapm_mixer_update_power() and soc_dapm_mux_update_power() from O(n) (with n being the number of paths for the card) to O(1). Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 2 +- sound/soc/soc-dapm.c | 40 ++++++++++++++++++++++++++++------------ 2 files changed, 29 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index d7d26cc8e3fc..693c75bbd5d1 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -490,7 +490,6 @@ struct snd_soc_dapm_path { /* source (input) and sink (output) widgets */ struct snd_soc_dapm_widget *source; struct snd_soc_dapm_widget *sink; - struct snd_kcontrol *kcontrol; /* status */ u32 connect:1; /* source and sink widgets are connected */ @@ -503,6 +502,7 @@ struct snd_soc_dapm_path { struct list_head list_source; struct list_head list_sink; + struct list_head list_kcontrol; struct list_head list; }; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index bad6f6db74c9..b779d36d5b3a 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -176,6 +176,7 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget( struct dapm_kcontrol_data { unsigned int value; + struct list_head paths; struct snd_soc_dapm_widget_list wlist; }; @@ -194,6 +195,7 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, data->wlist.widgets[0] = widget; data->wlist.num_widgets = 1; + INIT_LIST_HEAD(&data->paths); kcontrol->private_data = data; @@ -234,6 +236,26 @@ static int dapm_kcontrol_add_widget(struct snd_kcontrol *kcontrol, return 0; } +static void dapm_kcontrol_add_path(const struct snd_kcontrol *kcontrol, + struct snd_soc_dapm_path *path) +{ + struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); + + list_add_tail(&path->list_kcontrol, &data->paths); +} + +static struct list_head *dapm_kcontrol_get_path_list( + const struct snd_kcontrol *kcontrol) +{ + struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); + + return &data->paths; +} + +#define dapm_kcontrol_for_each_path(path, kcontrol) \ + list_for_each_entry(path, dapm_kcontrol_get_path_list(kcontrol), \ + list_kcontrol) + static unsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol) { struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); @@ -671,7 +693,7 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w, } w->kcontrols[kci] = kcontrol; - path->kcontrol = kcontrol; + dapm_kcontrol_add_path(kcontrol, path); return 0; } @@ -691,7 +713,7 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w) continue; if (w->kcontrols[i]) { - path->kcontrol = w->kcontrols[i]; + dapm_kcontrol_add_path(w->kcontrols[i], path); continue; } @@ -730,7 +752,7 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w) return ret; list_for_each_entry(path, &w->sources, list_sink) - path->kcontrol = w->kcontrols[0]; + dapm_kcontrol_add_path(w->kcontrols[0], path); return 0; } @@ -1990,10 +2012,7 @@ static int soc_dapm_mux_update_power(struct snd_soc_card *card, int found = 0; /* find dapm widget path assoc with kcontrol */ - list_for_each_entry(path, &card->paths, list) { - if (path->kcontrol != kcontrol) - continue; - + dapm_kcontrol_for_each_path(path, kcontrol) { if (!path->name || !e->texts[mux]) continue; @@ -2043,11 +2062,7 @@ static int soc_dapm_mixer_update_power(struct snd_soc_card *card, int found = 0; /* find dapm widget path assoc with kcontrol */ - list_for_each_entry(path, &card->paths, list) { - if (path->kcontrol != kcontrol) - continue; - - /* found, now check type */ + dapm_kcontrol_for_each_path(path, kcontrol) { found = 1; path->connect = connect; dapm_mark_dirty(path->source, "mixer connection"); @@ -2152,6 +2167,7 @@ static void dapm_free_path(struct snd_soc_dapm_path *path) { list_del(&path->list_sink); list_del(&path->list_source); + list_del(&path->list_kcontrol); list_del(&path->list); kfree(path); } -- cgit v1.2.3 From de9ba98b6d2629f53fd271a973176c2fa9736d9c Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 29 Jul 2013 17:14:01 +0200 Subject: ASoC: dapm: Make widget power register settings more flexible Currently the DAPM code is limited to only setting or clearing a single bit in a register to power a widget up or down. This patch extends the DAPM code to be more flexible in that regard and allow widgets to use arbitrary values to be used to put a widget in either on or off state. Since the snd_soc_dapm_widget struct already contains a on_val and off_val field no additional fields need to be added and in fact the invert field can even be removed. Also the generated code is slightly smaller. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 163 +++++++++++++++++++++++++++-------------------- sound/soc/soc-dapm.c | 34 ++++------ 2 files changed, 106 insertions(+), 91 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 693c75bbd5d1..3575721a955d 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -70,121 +70,144 @@ struct device; .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD} +#define SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert) \ + .reg = wreg, .mask = 1, .shift = wshift, \ + .on_val = winvert ? 0 : 1, .off_val = winvert ? 1 : 0 + /* path domain */ #define SND_SOC_DAPM_PGA(wname, wreg, wshift, winvert,\ wcontrols, wncontrols) \ -{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols} +{ .id = snd_soc_dapm_pga, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = wncontrols} #define SND_SOC_DAPM_OUT_DRV(wname, wreg, wshift, winvert,\ wcontrols, wncontrols) \ -{ .id = snd_soc_dapm_out_drv, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols} +{ .id = snd_soc_dapm_out_drv, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = wncontrols} #define SND_SOC_DAPM_MIXER(wname, wreg, wshift, winvert, \ wcontrols, wncontrols)\ -{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols} +{ .id = snd_soc_dapm_mixer, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = wncontrols} #define SND_SOC_DAPM_MIXER_NAMED_CTL(wname, wreg, wshift, winvert, \ wcontrols, wncontrols)\ -{ .id = snd_soc_dapm_mixer_named_ctl, .name = wname, .reg = wreg, \ - .shift = wshift, .invert = winvert, .kcontrol_news = wcontrols, \ - .num_kcontrols = wncontrols} +{ .id = snd_soc_dapm_mixer_named_ctl, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = wncontrols} #define SND_SOC_DAPM_MICBIAS(wname, wreg, wshift, winvert) \ -{ .id = snd_soc_dapm_micbias, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = NULL, .num_kcontrols = 0} +{ .id = snd_soc_dapm_micbias, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = NULL, .num_kcontrols = 0} #define SND_SOC_DAPM_SWITCH(wname, wreg, wshift, winvert, wcontrols) \ -{ .id = snd_soc_dapm_switch, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1} +{ .id = snd_soc_dapm_switch, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = 1} #define SND_SOC_DAPM_MUX(wname, wreg, wshift, winvert, wcontrols) \ -{ .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1} +{ .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, \ + .kcontrol_news = wcontrols, .num_kcontrols = 1} #define SND_SOC_DAPM_VIRT_MUX(wname, wreg, wshift, winvert, wcontrols) \ -{ .id = snd_soc_dapm_virt_mux, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1} +{ .id = snd_soc_dapm_virt_mux, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = 1} #define SND_SOC_DAPM_VALUE_MUX(wname, wreg, wshift, winvert, wcontrols) \ -{ .id = snd_soc_dapm_value_mux, .name = wname, .reg = wreg, \ - .shift = wshift, .invert = winvert, .kcontrol_news = wcontrols, \ - .num_kcontrols = 1} +{ .id = snd_soc_dapm_value_mux, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = 1} /* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */ #define SOC_PGA_ARRAY(wname, wreg, wshift, winvert,\ wcontrols) \ -{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)} +{ .id = snd_soc_dapm_pga, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)} #define SOC_MIXER_ARRAY(wname, wreg, wshift, winvert, \ wcontrols)\ -{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)} +{ .id = snd_soc_dapm_mixer, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)} #define SOC_MIXER_NAMED_CTL_ARRAY(wname, wreg, wshift, winvert, \ wcontrols)\ -{ .id = snd_soc_dapm_mixer_named_ctl, .name = wname, .reg = wreg, \ - .shift = wshift, .invert = winvert, .kcontrol_news = wcontrols, \ - .num_kcontrols = ARRAY_SIZE(wcontrols)} +{ .id = snd_soc_dapm_mixer_named_ctl, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)} /* path domain with event - event handler must return 0 for success */ #define SND_SOC_DAPM_PGA_E(wname, wreg, wshift, winvert, wcontrols, \ wncontrols, wevent, wflags) \ -{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \ +{ .id = snd_soc_dapm_pga, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \ .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_OUT_DRV_E(wname, wreg, wshift, winvert, wcontrols, \ wncontrols, wevent, wflags) \ -{ .id = snd_soc_dapm_out_drv, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \ +{ .id = snd_soc_dapm_out_drv, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \ .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_MIXER_E(wname, wreg, wshift, winvert, wcontrols, \ wncontrols, wevent, wflags) \ -{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \ +{ .id = snd_soc_dapm_mixer, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \ .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_MIXER_NAMED_CTL_E(wname, wreg, wshift, winvert, \ wcontrols, wncontrols, wevent, wflags) \ -{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, \ +{ .id = snd_soc_dapm_mixer, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, \ .num_kcontrols = wncontrols, .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_SWITCH_E(wname, wreg, wshift, winvert, wcontrols, \ wevent, wflags) \ -{ .id = snd_soc_dapm_switch, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1, \ +{ .id = snd_soc_dapm_switch, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = 1, \ .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_MUX_E(wname, wreg, wshift, winvert, wcontrols, \ wevent, wflags) \ -{ .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1, \ +{ .id = snd_soc_dapm_mux, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = 1, \ .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_VIRT_MUX_E(wname, wreg, wshift, winvert, wcontrols, \ wevent, wflags) \ -{ .id = snd_soc_dapm_virt_mux, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1, \ +{ .id = snd_soc_dapm_virt_mux, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = 1, \ .event = wevent, .event_flags = wflags} /* additional sequencing control within an event type */ #define SND_SOC_DAPM_PGA_S(wname, wsubseq, wreg, wshift, winvert, \ wevent, wflags) \ -{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .event = wevent, .event_flags = wflags, \ +{ .id = snd_soc_dapm_pga, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .event = wevent, .event_flags = wflags, \ .subseq = wsubseq} #define SND_SOC_DAPM_SUPPLY_S(wname, wsubseq, wreg, wshift, winvert, wevent, \ wflags) \ -{ .id = snd_soc_dapm_supply, .name = wname, .reg = wreg, \ - .shift = wshift, .invert = winvert, .event = wevent, \ - .event_flags = wflags, .subseq = wsubseq} +{ .id = snd_soc_dapm_supply, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .event = wevent, .event_flags = wflags, .subseq = wsubseq} /* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */ #define SOC_PGA_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \ wevent, wflags) \ -{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \ +{ .id = snd_soc_dapm_pga, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \ .event = wevent, .event_flags = wflags} #define SOC_MIXER_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \ wevent, wflags) \ -{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \ +{ .id = snd_soc_dapm_mixer, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \ .event = wevent, .event_flags = wflags} #define SOC_MIXER_NAMED_CTL_E_ARRAY(wname, wreg, wshift, winvert, \ wcontrols, wevent, wflags) \ -{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, \ - .num_kcontrols = ARRAY_SIZE(wcontrols), .event = wevent, .event_flags = wflags} +{ .id = snd_soc_dapm_mixer, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \ + .event = wevent, .event_flags = wflags} /* events that are pre and post DAPM */ #define SND_SOC_DAPM_PRE(wname, wevent) \ @@ -199,35 +222,36 @@ struct device; /* stream domain */ #define SND_SOC_DAPM_AIF_IN(wname, stname, wslot, wreg, wshift, winvert) \ { .id = snd_soc_dapm_aif_in, .name = wname, .sname = stname, \ - .reg = wreg, .shift = wshift, .invert = winvert } + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), } #define SND_SOC_DAPM_AIF_IN_E(wname, stname, wslot, wreg, wshift, winvert, \ wevent, wflags) \ { .id = snd_soc_dapm_aif_in, .name = wname, .sname = stname, \ - .reg = wreg, .shift = wshift, .invert = winvert, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ .event = wevent, .event_flags = wflags } #define SND_SOC_DAPM_AIF_OUT(wname, stname, wslot, wreg, wshift, winvert) \ { .id = snd_soc_dapm_aif_out, .name = wname, .sname = stname, \ - .reg = wreg, .shift = wshift, .invert = winvert } + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), } #define SND_SOC_DAPM_AIF_OUT_E(wname, stname, wslot, wreg, wshift, winvert, \ wevent, wflags) \ { .id = snd_soc_dapm_aif_out, .name = wname, .sname = stname, \ - .reg = wreg, .shift = wshift, .invert = winvert, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ .event = wevent, .event_flags = wflags } #define SND_SOC_DAPM_DAC(wname, stname, wreg, wshift, winvert) \ -{ .id = snd_soc_dapm_dac, .name = wname, .sname = stname, .reg = wreg, \ - .shift = wshift, .invert = winvert} +{ .id = snd_soc_dapm_dac, .name = wname, .sname = stname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert) } #define SND_SOC_DAPM_DAC_E(wname, stname, wreg, wshift, winvert, \ wevent, wflags) \ -{ .id = snd_soc_dapm_dac, .name = wname, .sname = stname, .reg = wreg, \ - .shift = wshift, .invert = winvert, \ +{ .id = snd_soc_dapm_dac, .name = wname, .sname = stname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ .event = wevent, .event_flags = wflags} + #define SND_SOC_DAPM_ADC(wname, stname, wreg, wshift, winvert) \ -{ .id = snd_soc_dapm_adc, .name = wname, .sname = stname, .reg = wreg, \ - .shift = wshift, .invert = winvert} +{ .id = snd_soc_dapm_adc, .name = wname, .sname = stname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), } #define SND_SOC_DAPM_ADC_E(wname, stname, wreg, wshift, winvert, \ wevent, wflags) \ -{ .id = snd_soc_dapm_adc, .name = wname, .sname = stname, .reg = wreg, \ - .shift = wshift, .invert = winvert, \ +{ .id = snd_soc_dapm_adc, .name = wname, .sname = stname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_CLOCK_SUPPLY(wname) \ { .id = snd_soc_dapm_clock_supply, .name = wname, \ @@ -241,14 +265,14 @@ struct device; .on_val = won_val, .off_val = woff_val, .event = dapm_reg_event, \ .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD} #define SND_SOC_DAPM_SUPPLY(wname, wreg, wshift, winvert, wevent, wflags) \ -{ .id = snd_soc_dapm_supply, .name = wname, .reg = wreg, \ - .shift = wshift, .invert = winvert, .event = wevent, \ - .event_flags = wflags} +{ .id = snd_soc_dapm_supply, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_REGULATOR_SUPPLY(wname, wdelay, wflags) \ { .id = snd_soc_dapm_regulator_supply, .name = wname, \ .reg = SND_SOC_NOPM, .shift = wdelay, .event = dapm_regulator_event, \ .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \ - .invert = wflags} + .on_val = wflags} /* dapm kcontrol types */ @@ -527,7 +551,6 @@ struct snd_soc_dapm_widget { unsigned int on_val; /* on state value */ unsigned int off_val; /* off state value */ unsigned char power:1; /* block power status */ - unsigned char invert:1; /* invert the power bit */ unsigned char active:1; /* active stream on DAC, ADC's */ unsigned char connected:1; /* connected codec pin */ unsigned char new:1; /* cnew complete */ diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index b779d36d5b3a..59bcc66358ca 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1122,7 +1122,7 @@ int dapm_regulator_event(struct snd_soc_dapm_widget *w, int ret; if (SND_SOC_DAPM_EVENT_ON(event)) { - if (w->invert & SND_SOC_DAPM_REGULATOR_BYPASS) { + if (w->on_val & SND_SOC_DAPM_REGULATOR_BYPASS) { ret = regulator_allow_bypass(w->regulator, false); if (ret != 0) dev_warn(w->dapm->dev, @@ -1132,7 +1132,7 @@ int dapm_regulator_event(struct snd_soc_dapm_widget *w, return regulator_enable(w->regulator); } else { - if (w->invert & SND_SOC_DAPM_REGULATOR_BYPASS) { + if (w->on_val & SND_SOC_DAPM_REGULATOR_BYPASS) { ret = regulator_allow_bypass(w->regulator, true); if (ret != 0) dev_warn(w->dapm->dev, @@ -1360,26 +1360,21 @@ static void dapm_seq_run_coalesced(struct snd_soc_card *card, struct list_head *pending) { struct snd_soc_dapm_widget *w; - int reg, power; + int reg; unsigned int value = 0; unsigned int mask = 0; - unsigned int cur_mask; reg = list_first_entry(pending, struct snd_soc_dapm_widget, power_list)->reg; list_for_each_entry(w, pending, power_list) { - cur_mask = 1 << w->shift; BUG_ON(reg != w->reg); - if (w->invert) - power = !w->power; + mask |= w->mask << w->shift; + if (w->power) + value |= w->on_val << w->shift; else - power = w->power; - - mask |= cur_mask; - if (power) - value |= cur_mask; + value |= w->off_val << w->shift; pop_dbg(w->dapm->dev, card->pop_time, "pop test : Queue %s: reg=0x%x, 0x%x/0x%x\n", @@ -1867,8 +1862,8 @@ static ssize_t dapm_widget_power_read_file(struct file *file, if (w->reg >= 0) ret += snprintf(buf + ret, PAGE_SIZE - ret, - " - R%d(0x%x) bit %d", - w->reg, w->reg, w->shift); + " - R%d(0x%x) mask 0x%x", + w->reg, w->reg, w->mask << w->shift); ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n"); @@ -2669,12 +2664,9 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) /* Read the initial power state from the device */ if (w->reg >= 0) { - val = soc_widget_read(w, w->reg); - val &= 1 << w->shift; - if (w->invert) - val = !val; - - if (val) + val = soc_widget_read(w, w->reg) >> w->shift; + val &= w->mask; + if (val == w->on_val) w->power = 1; } @@ -3093,7 +3085,7 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, return NULL; } - if (w->invert & SND_SOC_DAPM_REGULATOR_BYPASS) { + if (w->on_val & SND_SOC_DAPM_REGULATOR_BYPASS) { ret = regulator_allow_bypass(w->regulator, true); if (ret != 0) dev_warn(w->dapm->dev, -- cgit v1.2.3 From 8fe120b5a665fc869c23f86e4964b801f6e53486 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 31 Jul 2013 19:00:32 +0200 Subject: ASoC: omap-abe-twl6040: Remove support for pdata (legacy boot) Just recently OMAP4 legacy boot support has been removed. No reason to keep the code used by the legacy boot (pdata based) since neither OMAP4 or OMAP5 can boot in this mode. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- include/linux/platform_data/omap-abe-twl6040.h | 49 --------- sound/soc/omap/omap-abe-twl6040.c | 133 ++++++++----------------- 2 files changed, 41 insertions(+), 141 deletions(-) delete mode 100644 include/linux/platform_data/omap-abe-twl6040.h (limited to 'include') diff --git a/include/linux/platform_data/omap-abe-twl6040.h b/include/linux/platform_data/omap-abe-twl6040.h deleted file mode 100644 index 5d298ac10fc2..000000000000 --- a/include/linux/platform_data/omap-abe-twl6040.h +++ /dev/null @@ -1,49 +0,0 @@ -/** - * omap-abe-twl6040.h - ASoC machine driver OMAP4+ devices, header. - * - * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com - * All rights reserved. - * - * Author: Peter Ujfalusi - * - * 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 _OMAP_ABE_TWL6040_H_ -#define _OMAP_ABE_TWL6040_H_ - -/* To select if only one channel is connected in a stereo port */ -#define ABE_TWL6040_LEFT (1 << 0) -#define ABE_TWL6040_RIGHT (1 << 1) - -struct omap_abe_twl6040_data { - char *card_name; - /* Feature flags for connected audio pins */ - u8 has_hs; - u8 has_hf; - bool has_ep; - u8 has_aux; - u8 has_vibra; - bool has_dmic; - bool has_hsmic; - bool has_mainmic; - bool has_submic; - u8 has_afm; - /* Other features */ - bool jack_detection; /* board can detect jack events */ - int mclk_freq; /* MCLK frequency speed for twl6040 */ -}; - -#endif /* _OMAP_ABE_TWL6040_H_ */ diff --git a/sound/soc/omap/omap-abe-twl6040.c b/sound/soc/omap/omap-abe-twl6040.c index 70cd5c7b2e14..ebb13906b3a0 100644 --- a/sound/soc/omap/omap-abe-twl6040.c +++ b/sound/soc/omap/omap-abe-twl6040.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include @@ -166,19 +165,10 @@ static const struct snd_soc_dapm_route audio_map[] = { {"AFMR", NULL, "Line In"}, }; -static inline void twl6040_disconnect_pin(struct snd_soc_dapm_context *dapm, - int connected, char *pin) -{ - if (!connected) - snd_soc_dapm_disable_pin(dapm, pin); -} - static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; struct snd_soc_card *card = codec->card; - struct snd_soc_dapm_context *dapm = &codec->dapm; - struct omap_abe_twl6040_data *pdata = dev_get_platdata(card->dev); struct abe_twl6040 *priv = snd_soc_card_get_drvdata(card); int hs_trim; int ret = 0; @@ -203,24 +193,6 @@ static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd) twl6040_hs_jack_detect(codec, &hs_jack, SND_JACK_HEADSET); } - /* - * NULL pdata means we booted with DT. In this case the routing is - * provided and the card is fully routed, no need to mark pins. - */ - if (!pdata) - return ret; - - /* Disable not connected paths if not used */ - twl6040_disconnect_pin(dapm, pdata->has_hs, "Headset Stereophone"); - twl6040_disconnect_pin(dapm, pdata->has_hf, "Ext Spk"); - twl6040_disconnect_pin(dapm, pdata->has_ep, "Earphone Spk"); - twl6040_disconnect_pin(dapm, pdata->has_aux, "Line Out"); - twl6040_disconnect_pin(dapm, pdata->has_vibra, "Vibrator"); - twl6040_disconnect_pin(dapm, pdata->has_hsmic, "Headset Mic"); - twl6040_disconnect_pin(dapm, pdata->has_mainmic, "Main Handset Mic"); - twl6040_disconnect_pin(dapm, pdata->has_submic, "Sub Handset Mic"); - twl6040_disconnect_pin(dapm, pdata->has_afm, "Line In"); - return ret; } @@ -274,13 +246,18 @@ static struct snd_soc_card omap_abe_card = { static int omap_abe_probe(struct platform_device *pdev) { - struct omap_abe_twl6040_data *pdata = dev_get_platdata(&pdev->dev); struct device_node *node = pdev->dev.of_node; struct snd_soc_card *card = &omap_abe_card; + struct device_node *dai_node; struct abe_twl6040 *priv; int num_links = 0; int ret = 0; + if (!node) { + dev_err(&pdev->dev, "of node is missing.\n"); + return -ENODEV; + } + card->dev = &pdev->dev; priv = devm_kzalloc(&pdev->dev, sizeof(struct abe_twl6040), GFP_KERNEL); @@ -289,78 +266,50 @@ static int omap_abe_probe(struct platform_device *pdev) priv->dmic_codec_dev = ERR_PTR(-EINVAL); - if (node) { - struct device_node *dai_node; - - if (snd_soc_of_parse_card_name(card, "ti,model")) { - dev_err(&pdev->dev, "Card name is not provided\n"); - return -ENODEV; - } + if (snd_soc_of_parse_card_name(card, "ti,model")) { + dev_err(&pdev->dev, "Card name is not provided\n"); + return -ENODEV; + } - ret = snd_soc_of_parse_audio_routing(card, - "ti,audio-routing"); - if (ret) { - dev_err(&pdev->dev, - "Error while parsing DAPM routing\n"); - return ret; - } + ret = snd_soc_of_parse_audio_routing(card, "ti,audio-routing"); + if (ret) { + dev_err(&pdev->dev, "Error while parsing DAPM routing\n"); + return ret; + } - dai_node = of_parse_phandle(node, "ti,mcpdm", 0); - if (!dai_node) { - dev_err(&pdev->dev, "McPDM node is not provided\n"); - return -EINVAL; - } - abe_twl6040_dai_links[0].cpu_dai_name = NULL; - abe_twl6040_dai_links[0].cpu_of_node = dai_node; + dai_node = of_parse_phandle(node, "ti,mcpdm", 0); + if (!dai_node) { + dev_err(&pdev->dev, "McPDM node is not provided\n"); + return -EINVAL; + } + abe_twl6040_dai_links[0].cpu_dai_name = NULL; + abe_twl6040_dai_links[0].cpu_of_node = dai_node; - dai_node = of_parse_phandle(node, "ti,dmic", 0); - if (dai_node) { - num_links = 2; - abe_twl6040_dai_links[1].cpu_dai_name = NULL; - abe_twl6040_dai_links[1].cpu_of_node = dai_node; + dai_node = of_parse_phandle(node, "ti,dmic", 0); + if (dai_node) { + num_links = 2; + abe_twl6040_dai_links[1].cpu_dai_name = NULL; + abe_twl6040_dai_links[1].cpu_of_node = dai_node; - priv->dmic_codec_dev = platform_device_register_simple( + priv->dmic_codec_dev = platform_device_register_simple( "dmic-codec", -1, NULL, 0); - if (IS_ERR(priv->dmic_codec_dev)) { - dev_err(&pdev->dev, - "Can't instantiate dmic-codec\n"); - return PTR_ERR(priv->dmic_codec_dev); - } - } else { - num_links = 1; - } - - priv->jack_detection = of_property_read_bool(node, - "ti,jack-detection"); - of_property_read_u32(node, "ti,mclk-freq", - &priv->mclk_freq); - if (!priv->mclk_freq) { - dev_err(&pdev->dev, "MCLK frequency not provided\n"); - ret = -EINVAL; - goto err_unregister; + if (IS_ERR(priv->dmic_codec_dev)) { + dev_err(&pdev->dev, "Can't instantiate dmic-codec\n"); + return PTR_ERR(priv->dmic_codec_dev); } - - omap_abe_card.fully_routed = 1; - } else if (pdata) { - if (pdata->card_name) { - card->name = pdata->card_name; - } else { - dev_err(&pdev->dev, "Card name is not provided\n"); - return -ENODEV; - } - - if (pdata->has_dmic) - num_links = 2; - else - num_links = 1; - - priv->jack_detection = pdata->jack_detection; - priv->mclk_freq = pdata->mclk_freq; } else { - dev_err(&pdev->dev, "Missing pdata\n"); - return -ENODEV; + num_links = 1; + } + + priv->jack_detection = of_property_read_bool(node, "ti,jack-detection"); + of_property_read_u32(node, "ti,mclk-freq", &priv->mclk_freq); + if (!priv->mclk_freq) { + dev_err(&pdev->dev, "MCLK frequency not provided\n"); + ret = -EINVAL; + goto err_unregister; } + card->fully_routed = 1; if (!priv->mclk_freq) { dev_err(&pdev->dev, "MCLK frequency missing\n"); -- cgit v1.2.3 From 57295073b6acfdfaf9319d3caf92a5c433fdf109 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 5 Aug 2013 11:27:31 +0200 Subject: ASoC: dapm: Implement mixer input auto-disable Some devices have the problem that if a internal audio signal source is disabled the output of the source becomes undefined or goes to a undesired state (E.g. DAC output goes to ground instead of VMID). In this case it is necessary, in order to avoid unwanted clicks and pops, to disable any mixer input the signal feeds into or to active a mute control along the path to the output. Often it is still desirable to expose the same mixer input control to userspace, so cerain paths can sill be disabled manually. This means we can not use conventional DAPM to manage the mixer input control. This patch implements a method for letting DAPM overwrite the state of a userspace visible control. I.e. DAPM will disable the control if the path on which the control sits becomes inactive. Userspace will then only see a cached copy of the controls state. Once DAPM powers the path up again it will sync the userspace setting with the hardware and give control back to userspace. To implement this a new widget type is introduced. One widget of this type will be created for each DAPM kcontrol which has the auto-disable feature enabled. For each path that is controlled by the kcontrol the widget will be connected to the source of that path. The new widget type behaves like a supply widget, which means it will power up if one of its sinks are powered up and will only power down if all of its sinks are powered down. In order to only have the mixer input enabled when the source signal is valid the new widget type will be disabled before all other widget types and only be enabled after all other widget types. E.g. consider the following simplified example. A DAC is connected to a mixer and the mixer has a control to enable or disable the signal from the DAC. +-------+ +-----+ | | | DAC |-----[Ctrl]-| Mixer | +-----+ : | | | : +-------+ | : +-------------+ | Ctrl widget | +-------------+ If the control has the auto-disable feature enabled we'll create a widget for the control. This widget is connected to the DAC as it is the source for the mixer input. If the DAC powers up the control widget powers up and if the DAC powers down the control widget is powered down. As long as the control widget is powered down the hardware input control is kept disabled and if it is enabled userspace can freely change the control's state. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 17 +++++++- include/sound/soc.h | 28 ++++++------ sound/soc/soc-dapm.c | 108 +++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 125 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 3575721a955d..c728d28ae9a5 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -280,14 +280,26 @@ struct device; { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_volsw, \ .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \ - .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } + .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) } +#define SOC_DAPM_SINGLE_AUTODISABLE(xname, reg, shift, max, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_volsw, \ + .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \ + .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 1) } #define SOC_DAPM_SINGLE_TLV(xname, reg, shift, max, invert, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_volsw, \ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE,\ .tlv.p = (tlv_array), \ .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \ - .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } + .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) } +#define SOC_DAPM_SINGLE_TLV_AUTODISABLE(xname, reg, shift, max, invert, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_volsw, \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE,\ + .tlv.p = (tlv_array), \ + .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \ + .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) } #define SOC_DAPM_ENUM(xname, xenum) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_enum_double, \ @@ -484,6 +496,7 @@ enum snd_soc_dapm_type { snd_soc_dapm_dai_in, /* link to DAI structure */ snd_soc_dapm_dai_out, snd_soc_dapm_dai_link, /* link between two DAI structures */ + snd_soc_dapm_kcontrol, /* Auto-disabled kcontrol */ }; enum snd_soc_dapm_subclass { diff --git a/include/sound/soc.h b/include/sound/soc.h index b1e1f967ae1e..6201c6ede8ba 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -30,13 +30,13 @@ /* * Convenience kcontrol builders */ -#define SOC_DOUBLE_VALUE(xreg, shift_left, shift_right, xmax, xinvert) \ +#define SOC_DOUBLE_VALUE(xreg, shift_left, shift_right, xmax, xinvert, xautodisable) \ ((unsigned long)&(struct soc_mixer_control) \ {.reg = xreg, .rreg = xreg, .shift = shift_left, \ .rshift = shift_right, .max = xmax, .platform_max = xmax, \ - .invert = xinvert}) -#define SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) \ - SOC_DOUBLE_VALUE(xreg, xshift, xshift, xmax, xinvert) + .invert = xinvert, .autodisable = xautodisable}) +#define SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert, xautodisable) \ + SOC_DOUBLE_VALUE(xreg, xshift, xshift, xmax, xinvert, xautodisable) #define SOC_SINGLE_VALUE_EXT(xreg, xmax, xinvert) \ ((unsigned long)&(struct soc_mixer_control) \ {.reg = xreg, .max = xmax, .platform_max = xmax, .invert = xinvert}) @@ -52,7 +52,7 @@ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\ .put = snd_soc_put_volsw, \ - .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } + .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) } #define SOC_SINGLE_RANGE(xname, xreg, xshift, xmin, xmax, xinvert) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ .info = snd_soc_info_volsw_range, .get = snd_soc_get_volsw_range, \ @@ -68,7 +68,7 @@ .tlv.p = (tlv_array), \ .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\ .put = snd_soc_put_volsw, \ - .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } + .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) } #define SOC_SINGLE_SX_TLV(xname, xreg, xshift, xmin, xmax, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ @@ -97,7 +97,7 @@ .info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \ .put = snd_soc_put_volsw, \ .private_value = SOC_DOUBLE_VALUE(reg, shift_left, shift_right, \ - max, invert) } + max, invert, 0) } #define SOC_DOUBLE_R(xname, reg_left, reg_right, xshift, xmax, xinvert) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ .info = snd_soc_info_volsw, \ @@ -119,7 +119,7 @@ .info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \ .put = snd_soc_put_volsw, \ .private_value = SOC_DOUBLE_VALUE(reg, shift_left, shift_right, \ - max, invert) } + max, invert, 0) } #define SOC_DOUBLE_R_TLV(xname, reg_left, reg_right, xshift, xmax, xinvert, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ @@ -190,14 +190,14 @@ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_volsw, \ .get = xhandler_get, .put = xhandler_put, \ - .private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) } + .private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert, 0) } #define SOC_DOUBLE_EXT(xname, reg, shift_left, shift_right, max, invert,\ xhandler_get, xhandler_put) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ .info = snd_soc_info_volsw, \ .get = xhandler_get, .put = xhandler_put, \ .private_value = \ - SOC_DOUBLE_VALUE(reg, shift_left, shift_right, max, invert) } + SOC_DOUBLE_VALUE(reg, shift_left, shift_right, max, invert, 0) } #define SOC_SINGLE_EXT_TLV(xname, xreg, xshift, xmax, xinvert,\ xhandler_get, xhandler_put, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ @@ -206,7 +206,7 @@ .tlv.p = (tlv_array), \ .info = snd_soc_info_volsw, \ .get = xhandler_get, .put = xhandler_put, \ - .private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) } + .private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert, 0) } #define SOC_DOUBLE_EXT_TLV(xname, xreg, shift_left, shift_right, xmax, xinvert,\ xhandler_get, xhandler_put, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ @@ -216,7 +216,7 @@ .info = snd_soc_info_volsw, \ .get = xhandler_get, .put = xhandler_put, \ .private_value = SOC_DOUBLE_VALUE(xreg, shift_left, shift_right, \ - xmax, xinvert) } + xmax, xinvert, 0) } #define SOC_DOUBLE_R_EXT_TLV(xname, reg_left, reg_right, xshift, xmax, xinvert,\ xhandler_get, xhandler_put, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ @@ -1088,7 +1088,9 @@ struct snd_soc_pcm_runtime { /* mixer control */ struct soc_mixer_control { int min, max, platform_max; - unsigned int reg, rreg, shift, rshift, invert; + unsigned int reg, rreg, shift, rshift; + unsigned int invert:1; + unsigned int autodisable:1; }; struct soc_bytes { diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 5f64c16336ad..0944bc4bd4a4 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -47,6 +47,15 @@ #define DAPM_UPDATE_STAT(widget, val) widget->dapm->card->dapm_stats.val++; +static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, + struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink, + const char *control, + int (*connected)(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink)); +static struct snd_soc_dapm_widget * +snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, + const struct snd_soc_dapm_widget *widget); + /* dapm power sequences - make this per codec in the future */ static int dapm_up_seq[] = { [snd_soc_dapm_pre] = 0, @@ -73,16 +82,18 @@ static int dapm_up_seq[] = { [snd_soc_dapm_hp] = 10, [snd_soc_dapm_spk] = 10, [snd_soc_dapm_line] = 10, - [snd_soc_dapm_post] = 11, + [snd_soc_dapm_kcontrol] = 11, + [snd_soc_dapm_post] = 12, }; static int dapm_down_seq[] = { [snd_soc_dapm_pre] = 0, - [snd_soc_dapm_adc] = 1, - [snd_soc_dapm_hp] = 2, - [snd_soc_dapm_spk] = 2, - [snd_soc_dapm_line] = 2, - [snd_soc_dapm_out_drv] = 2, + [snd_soc_dapm_kcontrol] = 1, + [snd_soc_dapm_adc] = 2, + [snd_soc_dapm_hp] = 3, + [snd_soc_dapm_spk] = 3, + [snd_soc_dapm_line] = 3, + [snd_soc_dapm_out_drv] = 3, [snd_soc_dapm_pga] = 4, [snd_soc_dapm_switch] = 5, [snd_soc_dapm_mixer_named_ctl] = 5, @@ -176,6 +187,7 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget( struct dapm_kcontrol_data { unsigned int value; + struct snd_soc_dapm_widget *widget; struct list_head paths; struct snd_soc_dapm_widget_list *wlist; }; @@ -184,6 +196,7 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, struct snd_kcontrol *kcontrol) { struct dapm_kcontrol_data *data; + struct soc_mixer_control *mc; data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) { @@ -195,6 +208,39 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, INIT_LIST_HEAD(&data->paths); + switch (widget->id) { + case snd_soc_dapm_switch: + case snd_soc_dapm_mixer: + case snd_soc_dapm_mixer_named_ctl: + mc = (struct soc_mixer_control *)kcontrol->private_value; + + if (mc->autodisable) { + struct snd_soc_dapm_widget template; + + memset(&template, 0, sizeof(template)); + template.reg = mc->reg; + template.mask = (1 << fls(mc->max)) - 1; + template.shift = mc->shift; + if (mc->invert) + template.off_val = mc->max; + else + template.off_val = 0; + template.on_val = template.off_val; + template.id = snd_soc_dapm_kcontrol; + template.name = kcontrol->id.name; + + data->widget = snd_soc_dapm_new_control(widget->dapm, + &template); + if (!data->widget) { + kfree(data); + return -ENOMEM; + } + } + break; + default: + break; + } + kcontrol->private_data = data; return 0; @@ -203,6 +249,7 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, static void dapm_kcontrol_free(struct snd_kcontrol *kctl) { struct dapm_kcontrol_data *data = snd_kcontrol_chip(kctl); + kfree(data->widget); kfree(data->wlist); kfree(data); } @@ -246,6 +293,21 @@ static void dapm_kcontrol_add_path(const struct snd_kcontrol *kcontrol, struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); list_add_tail(&path->list_kcontrol, &data->paths); + + if (data->widget) { + snd_soc_dapm_add_path(data->widget->dapm, data->widget, + path->source, NULL, NULL); + } +} + +static bool dapm_kcontrol_is_powered(const struct snd_kcontrol *kcontrol) +{ + struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); + + if (!data->widget) + return true; + + return data->widget->power; } static struct list_head *dapm_kcontrol_get_path_list( @@ -275,6 +337,9 @@ static bool dapm_kcontrol_set_value(const struct snd_kcontrol *kcontrol, if (data->value == value) return false; + if (data->widget) + data->widget->on_val = value; + data->value = value; return true; @@ -515,6 +580,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, case snd_soc_dapm_spk: case snd_soc_dapm_line: case snd_soc_dapm_dai_link: + case snd_soc_dapm_kcontrol: p->connect = 1; break; /* does affect routing - dynamically connected */ @@ -880,6 +946,7 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget, case snd_soc_dapm_supply: case snd_soc_dapm_regulator_supply: case snd_soc_dapm_clock_supply: + case snd_soc_dapm_kcontrol: return 0; default: break; @@ -975,6 +1042,7 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget, case snd_soc_dapm_supply: case snd_soc_dapm_regulator_supply: case snd_soc_dapm_clock_supply: + case snd_soc_dapm_kcontrol: return 0; default: break; @@ -1523,7 +1591,7 @@ static void dapm_widget_update(struct snd_soc_card *card) unsigned int wi; int ret; - if (!update) + if (!update || !dapm_kcontrol_is_powered(update->kcontrol)) return; wlist = dapm_kcontrol_get_wlist(update->kcontrol); @@ -1668,6 +1736,7 @@ static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power, case snd_soc_dapm_supply: case snd_soc_dapm_regulator_supply: case snd_soc_dapm_clock_supply: + case snd_soc_dapm_kcontrol: /* Supplies can't affect their outputs, only their inputs */ break; default: @@ -2335,6 +2404,7 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, case snd_soc_dapm_dai_in: case snd_soc_dapm_dai_out: case snd_soc_dapm_dai_link: + case snd_soc_dapm_kcontrol: list_add(&path->list, &dapm->card->paths); list_add(&path->list_sink, &wsink->sources); list_add(&path->list_source, &wsource->sinks); @@ -2717,6 +2787,7 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); + struct snd_soc_card *card = codec->card; struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; unsigned int reg = mc->reg; @@ -2724,17 +2795,24 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, int max = mc->max; unsigned int mask = (1 << fls(max)) - 1; unsigned int invert = mc->invert; + unsigned int val; if (snd_soc_volsw_is_stereo(mc)) dev_warn(codec->dapm.dev, "ASoC: Control '%s' is stereo, which is not supported\n", kcontrol->id.name); - ucontrol->value.integer.value[0] = - (snd_soc_read(codec, reg) >> shift) & mask; + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + if (dapm_kcontrol_is_powered(kcontrol)) + val = (snd_soc_read(codec, reg) >> shift) & mask; + else + val = dapm_kcontrol_get_value(kcontrol); + mutex_unlock(&card->dapm_mutex); + if (invert) - ucontrol->value.integer.value[0] = - max - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[0] = max - val; + else + ucontrol->value.integer.value[0] = val; return 0; } @@ -2775,11 +2853,14 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, if (invert) val = max - val; - mask = mask << shift; - val = val << shift; mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + dapm_kcontrol_set_value(kcontrol, val); + + mask = mask << shift; + val = val << shift; + change = snd_soc_test_bits(codec, reg, mask, val); if (change) { update.kcontrol = kcontrol; @@ -3179,6 +3260,7 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, case snd_soc_dapm_supply: case snd_soc_dapm_regulator_supply: case snd_soc_dapm_clock_supply: + case snd_soc_dapm_kcontrol: w->power_check = dapm_supply_check_power; break; default: -- cgit v1.2.3 From 4b4dab82340d969521f4f86108441cb597c8595d Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 28 Jul 2013 18:58:29 -0700 Subject: ASoC: rsnd: remove platform dai and add dai_id on platform setting Current rsnd driver is using struct rsnd_dai_platform_info so that indicate sound DAI information (playback/capture SSI ID). But, SSI settings were also required separately. Thus, platform settings was very un-understandable. This patch adds dai_id to SSI settings, and removed rsnd_dai_platform_info. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/rcar_snd.h | 18 ++++++++------- sound/soc/sh/rcar/core.c | 60 +++++++++++++++++++++++++++++++++--------------- sound/soc/sh/rcar/gen.c | 10 ++++---- sound/soc/sh/rcar/rsnd.h | 3 +++ sound/soc/sh/rcar/ssi.c | 22 ++++++++++++++++++ 5 files changed, 82 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index 99d8dd029906..33233edd1664 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -28,15 +28,24 @@ /* * flags * - * 0xA0000000 + * 0xAB000000 * * A : clock sharing settings + * B : SSI direction */ #define RSND_SSI_CLK_PIN_SHARE (1 << 31) #define RSND_SSI_CLK_FROM_ADG (1 << 30) /* clock parent is master */ #define RSND_SSI_SYNC (1 << 29) /* SSI34_sync etc */ +#define RSND_SSI_PLAY (1 << 24) + +#define RSND_SSI_SET(_dai_id, _pio_irq, _flags) \ +{ .dai_id = _dai_id, .pio_irq = _pio_irq, .flags = _flags } +#define RSND_SSI_UNUSED \ +{ .dai_id = -1, .pio_irq = -1, .flags = 0 } + struct rsnd_ssi_platform_info { + int dai_id; int pio_irq; u32 flags; }; @@ -45,11 +54,6 @@ struct rsnd_scu_platform_info { u32 flags; }; -struct rsnd_dai_platform_info { - int ssi_id_playback; - int ssi_id_capture; -}; - /* * flags * @@ -66,8 +70,6 @@ struct rcar_snd_info { int ssi_info_nr; struct rsnd_scu_platform_info *scu_info; int scu_info_nr; - struct rsnd_dai_platform_info *dai_info; - int dai_info_nr; int (*start)(int id); int (*stop)(int id); }; diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 9a5469d3f352..420d6df9c3d0 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -219,6 +219,16 @@ int rsnd_dai_disconnect(struct rsnd_mod *mod) return 0; } +int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai) +{ + int id = rdai - priv->rdai; + + if ((id < 0) || (id >= rsnd_dai_nr(priv))) + return -EINVAL; + + return id; +} + struct rsnd_dai *rsnd_dai_get(struct rsnd_priv *priv, int id) { return priv->rdai + id; @@ -315,9 +325,10 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct rsnd_priv *priv = snd_soc_dai_get_drvdata(dai); struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); - struct rsnd_dai_platform_info *info = rsnd_dai_get_platform_info(rdai); - int ssi_id = rsnd_dai_is_play(rdai, io) ? info->ssi_id_playback : - info->ssi_id_capture; + struct rsnd_mod *mod = rsnd_ssi_mod_get_frm_dai(priv, + rsnd_dai_id(priv, rdai), + rsnd_dai_is_play(rdai, io)); + int ssi_id = rsnd_mod_id(mod); int ret; unsigned long flags; @@ -439,10 +450,24 @@ static int rsnd_dai_probe(struct platform_device *pdev, { struct snd_soc_dai_driver *drv; struct rsnd_dai *rdai; + struct rsnd_mod *pmod, *cmod; struct device *dev = rsnd_priv_to_dev(priv); - struct rsnd_dai_platform_info *dai_info; - int dai_nr = info->dai_info_nr; - int i, pid, cid; + int dai_nr; + int i; + + /* get max dai nr */ + for (dai_nr = 0; dai_nr < 32; dai_nr++) { + pmod = rsnd_ssi_mod_get_frm_dai(priv, dai_nr, 1); + cmod = rsnd_ssi_mod_get_frm_dai(priv, dai_nr, 0); + + if (!pmod && !cmod) + break; + } + + if (!dai_nr) { + dev_err(dev, "no dai\n"); + return -EIO; + } drv = devm_kzalloc(dev, sizeof(*drv) * dai_nr, GFP_KERNEL); rdai = devm_kzalloc(dev, sizeof(*rdai) * dai_nr, GFP_KERNEL); @@ -452,10 +477,9 @@ static int rsnd_dai_probe(struct platform_device *pdev, } for (i = 0; i < dai_nr; i++) { - dai_info = &info->dai_info[i]; - pid = dai_info->ssi_id_playback; - cid = dai_info->ssi_id_capture; + pmod = rsnd_ssi_mod_get_frm_dai(priv, i, 1); + cmod = rsnd_ssi_mod_get_frm_dai(priv, i, 0); /* * init rsnd_dai @@ -463,8 +487,6 @@ static int rsnd_dai_probe(struct platform_device *pdev, INIT_LIST_HEAD(&rdai[i].playback.head); INIT_LIST_HEAD(&rdai[i].capture.head); - rdai[i].info = dai_info; - snprintf(rdai[i].name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", i); /* @@ -472,20 +494,22 @@ static int rsnd_dai_probe(struct platform_device *pdev, */ drv[i].name = rdai[i].name; drv[i].ops = &rsnd_soc_dai_ops; - if (pid >= 0) { + if (pmod) { drv[i].playback.rates = RSND_RATES; drv[i].playback.formats = RSND_FMTS; drv[i].playback.channels_min = 2; drv[i].playback.channels_max = 2; } - if (cid >= 0) { + if (cmod) { drv[i].capture.rates = RSND_RATES; drv[i].capture.formats = RSND_FMTS; drv[i].capture.channels_min = 2; drv[i].capture.channels_max = 2; } - dev_dbg(dev, "%s (%d, %d) probed", rdai[i].name, pid, cid); + dev_dbg(dev, "%s (%s/%s)\n", rdai[i].name, + pmod ? "play" : " -- ", + cmod ? "capture" : " -- "); } priv->dai_nr = dai_nr; @@ -627,10 +651,6 @@ static int rsnd_probe(struct platform_device *pdev) if (ret < 0) return ret; - ret = rsnd_dai_probe(pdev, info, priv); - if (ret < 0) - return ret; - ret = rsnd_scu_probe(pdev, info, priv); if (ret < 0) return ret; @@ -643,6 +663,10 @@ static int rsnd_probe(struct platform_device *pdev) if (ret < 0) return ret; + ret = rsnd_dai_probe(pdev, info, priv); + if (ret < 0) + return ret; + /* * asoc register */ diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 61232cd9908f..460c57eef267 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -49,7 +49,6 @@ static int rsnd_gen1_path_init(struct rsnd_priv *priv, struct rsnd_dai *rdai, struct rsnd_dai_stream *io) { - struct rsnd_dai_platform_info *info = rsnd_dai_get_platform_info(rdai); struct rsnd_mod *mod; int ret; int id; @@ -67,10 +66,11 @@ static int rsnd_gen1_path_init(struct rsnd_priv *priv, * Then, SSI id = SCU id here */ - if (rsnd_dai_is_play(rdai, io)) - id = info->ssi_id_playback; - else - id = info->ssi_id_capture; + /* get SSI's ID */ + mod = rsnd_ssi_mod_get_frm_dai(priv, + rsnd_dai_id(priv, rdai), + rsnd_dai_is_play(rdai, io)); + id = rsnd_mod_id(mod); /* SSI */ mod = rsnd_ssi_mod_get(priv, id); diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 0e7727cc41db..9243e387104c 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -157,6 +157,7 @@ int rsnd_dai_disconnect(struct rsnd_mod *mod); int rsnd_dai_connect(struct rsnd_dai *rdai, struct rsnd_mod *mod, struct rsnd_dai_stream *io); int rsnd_dai_is_play(struct rsnd_dai *rdai, struct rsnd_dai_stream *io); +int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai); #define rsnd_dai_get_platform_info(rdai) ((rdai)->info) #define rsnd_io_to_runtime(io) ((io)->substream->runtime) @@ -254,5 +255,7 @@ int rsnd_ssi_probe(struct platform_device *pdev, void rsnd_ssi_remove(struct platform_device *pdev, struct rsnd_priv *priv); struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); +struct rsnd_mod *rsnd_ssi_mod_get_frm_dai(struct rsnd_priv *priv, + int dai_id, int is_play); #endif diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 061ac7e88309..c48a6c7cd08e 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -87,6 +87,7 @@ struct rsnd_ssiu { #define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent) #define rsnd_rdai_is_clk_master(rdai) ((rdai)->clk_master) #define rsnd_ssi_mode_flags(p) ((p)->info->flags) +#define rsnd_ssi_dai_id(ssi) ((ssi)->info->dai_id) #define rsnd_ssi_to_ssiu(ssi)\ (((struct rsnd_ssiu *)((ssi) - rsnd_mod_id(&(ssi)->mod))) - 1) @@ -502,6 +503,27 @@ static struct rsnd_mod_ops rsnd_ssi_non_ops = { /* * ssi mod function */ +struct rsnd_mod *rsnd_ssi_mod_get_frm_dai(struct rsnd_priv *priv, + int dai_id, int is_play) +{ + struct rsnd_ssi *ssi; + int i, has_play; + + is_play = !!is_play; + + for_each_rsnd_ssi(ssi, priv, i) { + if (rsnd_ssi_dai_id(ssi) != dai_id) + continue; + + has_play = !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_PLAY); + + if (is_play == has_play) + return &ssi->mod; + } + + return NULL; +} + struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id) { BUG_ON(id < 0 || id >= rsnd_ssi_nr(priv)); -- cgit v1.2.3 From 849fc82a6f4f32b4c8c502bb7c4a68df51170232 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 28 Jul 2013 18:59:02 -0700 Subject: ASoC: rsnd: SSI supports DMA transfer This patch adds DMAEngine transfer on SSI. But, it transfers sound data from memory to SSI directly without using HPBIF at this time. It will be updated soon Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/rcar_snd.h | 7 +-- sound/soc/sh/rcar/ssi.c | 110 +++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 111 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index 33233edd1664..a72687dda0cd 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -39,13 +39,14 @@ #define RSND_SSI_PLAY (1 << 24) -#define RSND_SSI_SET(_dai_id, _pio_irq, _flags) \ -{ .dai_id = _dai_id, .pio_irq = _pio_irq, .flags = _flags } +#define RSND_SSI_SET(_dai_id, _dma_id, _pio_irq, _flags) \ +{ .dai_id = _dai_id, .dma_id = _dma_id, .pio_irq = _pio_irq, .flags = _flags } #define RSND_SSI_UNUSED \ -{ .dai_id = -1, .pio_irq = -1, .flags = 0 } +{ .dai_id = -1, .dma_id = -1, .pio_irq = -1, .flags = 0 } struct rsnd_ssi_platform_info { int dai_id; + int dma_id; int pio_irq; u32 flags; }; diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index c48a6c7cd08e..2079ccf5f322 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -19,6 +19,7 @@ * SSICR */ #define FORCE (1 << 31) /* Fixed */ +#define DMEN (1 << 28) /* DMA Enable */ #define UIEN (1 << 27) /* Underflow Interrupt Enable */ #define OIEN (1 << 26) /* Overflow Interrupt Enable */ #define IIEN (1 << 25) /* Idle Mode Interrupt Enable */ @@ -51,6 +52,11 @@ #define IIRQ (1 << 25) /* Idle Mode Interrupt Status */ #define DIRQ (1 << 24) /* Data Interrupt Status Flag */ +/* + * SSIWSR + */ +#define CONT (1 << 8) /* WS Continue Function */ + struct rsnd_ssi { struct clk *clk; struct rsnd_ssi_platform_info *info; /* rcar_snd.h */ @@ -63,6 +69,7 @@ struct rsnd_ssi { u32 cr_clk; u32 cr_etc; int err; + int dma_offset; unsigned int usrcnt; unsigned int rate; }; @@ -83,7 +90,10 @@ struct rsnd_ssiu { #define rsnd_ssi_nr(priv) (((struct rsnd_ssiu *)((priv)->ssiu))->ssi_nr) #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod) -#define rsnd_ssi_is_pio(ssi) ((ssi)->info->pio_irq > 0) +#define rsnd_dma_to_ssi(dma) rsnd_mod_to_ssi(rsnd_dma_to_mod(dma)) +#define rsnd_ssi_pio_available(ssi) ((ssi)->info->pio_irq > 0) +#define rsnd_ssi_dma_available(ssi) \ + rsnd_dma_available(rsnd_mod_to_dma(&(ssi)->mod)) #define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent) #define rsnd_rdai_is_clk_master(rdai) ((rdai)->clk_master) #define rsnd_ssi_mode_flags(p) ((p)->info->flags) @@ -477,6 +487,79 @@ static struct rsnd_mod_ops rsnd_ssi_pio_ops = { .stop = rsnd_ssi_pio_stop, }; +static int rsnd_ssi_dma_inquiry(struct rsnd_dma *dma, dma_addr_t *buf, int *len) +{ + struct rsnd_ssi *ssi = rsnd_dma_to_ssi(dma); + struct rsnd_dai_stream *io = ssi->io; + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + + *len = io->byte_per_period; + *buf = runtime->dma_addr + + rsnd_dai_pointer_offset(io, ssi->dma_offset + *len); + ssi->dma_offset = *len; /* it cares A/B plane */ + + return 0; +} + +static int rsnd_ssi_dma_complete(struct rsnd_dma *dma) +{ + struct rsnd_ssi *ssi = rsnd_dma_to_ssi(dma); + struct rsnd_dai_stream *io = ssi->io; + u32 status = rsnd_mod_read(&ssi->mod, SSISR); + + rsnd_ssi_record_error(ssi, status); + + rsnd_dai_pointer_update(ssi->io, io->byte_per_period); + + return 0; +} + +static int rsnd_ssi_dma_start(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod); + + /* enable DMA transfer */ + ssi->cr_etc = DMEN; + ssi->dma_offset = 0; + + rsnd_dma_start(dma); + + rsnd_ssi_hw_start(ssi, ssi->rdai, io); + + /* enable WS continue */ + if (rsnd_rdai_is_clk_master(rdai)) + rsnd_mod_write(&ssi->mod, SSIWSR, CONT); + + return 0; +} + +static int rsnd_ssi_dma_stop(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod); + + ssi->cr_etc = 0; + + rsnd_ssi_hw_stop(ssi, rdai); + + rsnd_dma_stop(dma); + + return 0; +} + +static struct rsnd_mod_ops rsnd_ssi_dma_ops = { + .name = "ssi (dma)", + .init = rsnd_ssi_init, + .quit = rsnd_ssi_quit, + .start = rsnd_ssi_dma_start, + .stop = rsnd_ssi_dma_stop, +}; + /* * Non SSI */ @@ -573,10 +656,27 @@ int rsnd_ssi_probe(struct platform_device *pdev, ops = &rsnd_ssi_non_ops; + /* + * SSI DMA case + */ + if (pinfo->dma_id > 0) { + ret = rsnd_dma_init( + priv, rsnd_mod_to_dma(&ssi->mod), + (rsnd_ssi_mode_flags(ssi) & RSND_SSI_PLAY), + pinfo->dma_id, + rsnd_ssi_dma_inquiry, + rsnd_ssi_dma_complete); + if (ret < 0) + dev_info(dev, "SSI DMA failed. try PIO transter\n"); + else + ops = &rsnd_ssi_dma_ops; + } + /* * SSI PIO case */ - if (rsnd_ssi_is_pio(ssi)) { + if (!rsnd_ssi_dma_available(ssi) && + rsnd_ssi_pio_available(ssi)) { ret = devm_request_irq(dev, pinfo->pio_irq, &rsnd_ssi_pio_interrupt, IRQF_SHARED, @@ -605,6 +705,10 @@ void rsnd_ssi_remove(struct platform_device *pdev, struct rsnd_ssi *ssi; int i; - for_each_rsnd_ssi(ssi, priv, i) + for_each_rsnd_ssi(ssi, priv, i) { clk_put(ssi->clk); + if (rsnd_ssi_dma_available(ssi)) + rsnd_dma_quit(priv, rsnd_mod_to_dma(&ssi->mod)); + } + } -- cgit v1.2.3 From 374a528111fa07878090bd9694a3e153814de39c Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 28 Jul 2013 18:59:12 -0700 Subject: ASoC: rsnd: SSI supports DMA transfer via BUSIF This patch adds BUSIF support for R-Car sound DMAEngine transfer. The sound data will be transferred via FIFO which can cover blank time which will happen when DMA channel is switching. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/rcar_snd.h | 6 ++ sound/soc/sh/rcar/gen.c | 10 ++- sound/soc/sh/rcar/rsnd.h | 9 +++ sound/soc/sh/rcar/scu.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++- sound/soc/sh/rcar/ssi.c | 18 +++++- 5 files changed, 190 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index a72687dda0cd..d35412ae03b3 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -36,6 +36,7 @@ #define RSND_SSI_CLK_PIN_SHARE (1 << 31) #define RSND_SSI_CLK_FROM_ADG (1 << 30) /* clock parent is master */ #define RSND_SSI_SYNC (1 << 29) /* SSI34_sync etc */ +#define RSND_SSI_DEPENDENT (1 << 28) /* SSI needs SRU/SCU */ #define RSND_SSI_PLAY (1 << 24) @@ -51,6 +52,11 @@ struct rsnd_ssi_platform_info { u32 flags; }; +/* + * flags + */ +#define RSND_SCU_USB_HPBIF (1 << 31) /* it needs RSND_SSI_DEPENDENT */ + struct rsnd_scu_platform_info { u32 flags; }; diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 460c57eef267..babb203b43b7 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -34,9 +34,6 @@ struct rsnd_gen { #define rsnd_priv_to_gen(p) ((struct rsnd_gen *)(p)->gen) -#define rsnd_is_gen1(s) ((s)->info->flags & RSND_GEN1) -#define rsnd_is_gen2(s) ((s)->info->flags & RSND_GEN2) - /* * Gen2 * will be filled in the future @@ -115,8 +112,15 @@ static struct rsnd_gen_ops rsnd_gen1_ops = { static void rsnd_gen1_reg_map_init(struct rsnd_gen *gen) { + RSND_GEN1_REG_MAP(gen, SRU, SRC_ROUTE_SEL, 0x0, 0x00); + RSND_GEN1_REG_MAP(gen, SRU, SRC_TMG_SEL0, 0x0, 0x08); + RSND_GEN1_REG_MAP(gen, SRU, SRC_TMG_SEL1, 0x0, 0x0c); + RSND_GEN1_REG_MAP(gen, SRU, SRC_TMG_SEL2, 0x0, 0x10); + RSND_GEN1_REG_MAP(gen, SRU, SRC_CTRL, 0x0, 0xc0); RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE0, 0x0, 0xD0); RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE1, 0x0, 0xD4); + RSND_GEN1_REG_MAP(gen, SRU, BUSIF_MODE, 0x4, 0x20); + RSND_GEN1_REG_MAP(gen, SRU, BUSIF_ADINR, 0x40, 0x214); RSND_GEN1_REG_MAP(gen, ADG, BRRA, 0x0, 0x00); RSND_GEN1_REG_MAP(gen, ADG, BRRB, 0x0, 0x04); diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 15dccd598960..9cc6986a8cfb 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -32,8 +32,15 @@ */ enum rsnd_reg { /* SRU/SCU */ + RSND_REG_SRC_ROUTE_SEL, + RSND_REG_SRC_TMG_SEL0, + RSND_REG_SRC_TMG_SEL1, + RSND_REG_SRC_TMG_SEL2, + RSND_REG_SRC_CTRL, RSND_REG_SSI_MODE0, RSND_REG_SSI_MODE1, + RSND_REG_BUSIF_MODE, + RSND_REG_BUSIF_ADINR, /* ADG */ RSND_REG_BRRA, @@ -213,6 +220,8 @@ int rsnd_gen_path_exit(struct rsnd_priv *priv, void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg); +#define rsnd_is_gen1(s) ((s)->info->flags & RSND_GEN1) +#define rsnd_is_gen2(s) ((s)->info->flags & RSND_GEN2) /* * R-Car ADG diff --git a/sound/soc/sh/rcar/scu.c b/sound/soc/sh/rcar/scu.c index c12e65f240a1..29837e326bc5 100644 --- a/sound/soc/sh/rcar/scu.c +++ b/sound/soc/sh/rcar/scu.c @@ -15,6 +15,18 @@ struct rsnd_scu { struct rsnd_mod mod; }; +#define rsnd_scu_mode_flags(p) ((p)->info->flags) + +/* + * ADINR + */ +#define OTBL_24 (0 << 16) +#define OTBL_22 (2 << 16) +#define OTBL_20 (4 << 16) +#define OTBL_18 (6 << 16) +#define OTBL_16 (8 << 16) + + #define rsnd_mod_to_scu(_mod) \ container_of((_mod), struct rsnd_scu, mod) @@ -24,6 +36,116 @@ struct rsnd_scu { ((pos) = (struct rsnd_scu *)(priv)->scu + i); \ i++) +static int rsnd_scu_set_route(struct rsnd_priv *priv, + struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct scu_route_config { + u32 mask; + int shift; + } routes[] = { + { 0xF, 0, }, /* 0 */ + { 0xF, 4, }, /* 1 */ + { 0xF, 8, }, /* 2 */ + { 0x7, 12, }, /* 3 */ + { 0x7, 16, }, /* 4 */ + { 0x7, 20, }, /* 5 */ + { 0x7, 24, }, /* 6 */ + { 0x3, 28, }, /* 7 */ + { 0x3, 30, }, /* 8 */ + }; + + u32 mask; + u32 val; + int shift; + int id; + + /* + * Gen1 only + */ + if (!rsnd_is_gen1(priv)) + return 0; + + id = rsnd_mod_id(mod); + if (id < 0 || id > ARRAY_SIZE(routes)) + return -EIO; + + /* + * SRC_ROUTE_SELECT + */ + val = rsnd_dai_is_play(rdai, io) ? 0x1 : 0x2; + val = val << routes[id].shift; + mask = routes[id].mask << routes[id].shift; + + rsnd_mod_bset(mod, SRC_ROUTE_SEL, mask, val); + + /* + * SRC_TIMING_SELECT + */ + shift = (id % 4) * 8; + mask = 0x1F << shift; + if (8 == id) /* SRU8 is very special */ + val = id << shift; + else + val = (id + 1) << shift; + + switch (id / 4) { + case 0: + rsnd_mod_bset(mod, SRC_TMG_SEL0, mask, val); + break; + case 1: + rsnd_mod_bset(mod, SRC_TMG_SEL1, mask, val); + break; + case 2: + rsnd_mod_bset(mod, SRC_TMG_SEL2, mask, val); + break; + } + + return 0; +} + +static int rsnd_scu_set_mode(struct rsnd_priv *priv, + struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + int id = rsnd_mod_id(mod); + u32 val; + + if (rsnd_is_gen1(priv)) { + val = (1 << id); + rsnd_mod_bset(mod, SRC_CTRL, val, val); + } + + return 0; +} + +static int rsnd_scu_set_hpbif(struct rsnd_priv *priv, + struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + u32 adinr = runtime->channels; + + switch (runtime->sample_bits) { + case 16: + adinr |= OTBL_16; + break; + case 32: + adinr |= OTBL_24; + break; + default: + return -EIO; + } + + rsnd_mod_write(mod, BUSIF_MODE, 1); + rsnd_mod_write(mod, BUSIF_ADINR, adinr); + + return 0; +} + static int rsnd_scu_init(struct rsnd_mod *mod, struct rsnd_dai *rdai, struct rsnd_dai_stream *io) @@ -53,9 +175,36 @@ static int rsnd_scu_start(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_scu *scu = rsnd_mod_to_scu(mod); struct device *dev = rsnd_priv_to_dev(priv); + u32 flags = rsnd_scu_mode_flags(scu); + int ret; + + /* + * SCU will be used if it has RSND_SCU_USB_HPBIF flags + */ + if (!(flags & RSND_SCU_USB_HPBIF)) { + /* it use PIO transter */ + dev_dbg(dev, "%s%d is not used\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); + + return 0; + } + + /* it use DMA transter */ + ret = rsnd_scu_set_route(priv, mod, rdai, io); + if (ret < 0) + return ret; + + ret = rsnd_scu_set_mode(priv, mod, rdai, io); + if (ret < 0) + return ret; - dev_dbg(dev, "%s.%d start\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + ret = rsnd_scu_set_hpbif(priv, mod, rdai, io); + if (ret < 0) + return ret; + + dev_dbg(dev, "%s%d start\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); return 0; } @@ -112,8 +261,9 @@ int rsnd_scu_probe(struct platform_device *pdev, rsnd_mod_init(priv, &scu->mod, &rsnd_scu_ops, i); scu->info = &info->scu_info[i]; - } + dev_dbg(dev, "SCU%d probed\n", i); + } dev_dbg(dev, "scu probed\n"); return 0; diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 2079ccf5f322..fae26d3f79d2 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -104,6 +104,7 @@ struct rsnd_ssiu { static void rsnd_ssi_mode_init(struct rsnd_priv *priv, struct rsnd_ssiu *ssiu) { + struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_ssi *ssi; u32 flags; u32 val; @@ -113,8 +114,17 @@ static void rsnd_ssi_mode_init(struct rsnd_priv *priv, * SSI_MODE0 */ ssiu->ssi_mode0 = 0; - for_each_rsnd_ssi(ssi, priv, i) - ssiu->ssi_mode0 |= (1 << i); + for_each_rsnd_ssi(ssi, priv, i) { + flags = rsnd_ssi_mode_flags(ssi); + + /* see also BUSIF_MODE */ + if (!(flags & RSND_SSI_DEPENDENT)) { + ssiu->ssi_mode0 |= (1 << i); + dev_dbg(dev, "SSI%d uses INDEPENDENT mode\n", i); + } else { + dev_dbg(dev, "SSI%d uses DEPENDENT mode\n", i); + } + } /* * SSI_MODE1 @@ -670,6 +680,8 @@ int rsnd_ssi_probe(struct platform_device *pdev, dev_info(dev, "SSI DMA failed. try PIO transter\n"); else ops = &rsnd_ssi_dma_ops; + + dev_dbg(dev, "SSI%d use DMA transfer\n", i); } /* @@ -687,6 +699,8 @@ int rsnd_ssi_probe(struct platform_device *pdev, } ops = &rsnd_ssi_pio_ops; + + dev_dbg(dev, "SSI%d use PIO transfer\n", i); } rsnd_mod_init(priv, &ssi->mod, ops, i); -- cgit v1.2.3 From c77f872e663e3f6ea18f774bf4399884884b4b22 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 10 Aug 2013 21:33:09 +0200 Subject: ASoC: Remove unused snd_soc_info_volsw_ext() The SOC_SINGLE_EXT control has been using snd_soc_info_volsw() for its info callback since commit 1c433fb ("[ALSA] soc - 0.13 ASoC headers"). The snd_soc_info_volsw_ext() function has been unused ever since then, so remove it. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 2 -- sound/soc/soc-core.c | 26 -------------------------- 2 files changed, 28 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 6eabee7ec15a..724a42af40fa 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -497,8 +497,6 @@ int snd_soc_put_value_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_soc_info_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); -int snd_soc_info_volsw_ext(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo); #define snd_soc_info_bool_ext snd_ctl_boolean_mono_info int snd_soc_get_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index c7d16df9efd9..6ba5f7c23d3a 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2577,32 +2577,6 @@ int snd_soc_info_enum_ext(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_GPL(snd_soc_info_enum_ext); -/** - * snd_soc_info_volsw_ext - external single mixer info callback - * @kcontrol: mixer control - * @uinfo: control element information - * - * Callback to provide information about a single external mixer control. - * - * Returns 0 for success. - */ -int snd_soc_info_volsw_ext(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - int max = kcontrol->private_value; - - if (max == 1 && !strstr(kcontrol->id.name, " Volume")) - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - else - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = max; - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_info_volsw_ext); - /** * snd_soc_info_volsw - single mixer info callback * @kcontrol: mixer control -- cgit v1.2.3 From 9a953e6f27fd280a2af5719b77394fbb228c5b46 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 10 Aug 2013 21:33:10 +0200 Subject: ASoC: Use snd_soc_info_enum_double() for SOC_ENUM_EXT controls snd_soc_info_enum_ext() and snd_soc_info_enum_double() are almost identical. The only difference is that snd_soc_info_enum_double() is also able to handle stereo controls. Using snd_soc_info_enum double() instead of snd_soc_info_enum_ext() for the SOC_ENUM_EXT control's info callback allows us to remove snd_soc_info_enum_ext(). Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 4 +--- sound/soc/soc-core.c | 27 --------------------------- 2 files changed, 1 insertion(+), 30 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 724a42af40fa..6f86a4187f58 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -234,7 +234,7 @@ .private_value = xdata } #define SOC_ENUM_EXT(xname, xenum, xhandler_get, xhandler_put) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ - .info = snd_soc_info_enum_ext, \ + .info = snd_soc_info_enum_double, \ .get = xhandler_get, .put = xhandler_put, \ .private_value = (unsigned long)&xenum } @@ -485,8 +485,6 @@ int snd_soc_add_dai_controls(struct snd_soc_dai *dai, const struct snd_kcontrol_new *controls, int num_controls); int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); -int snd_soc_info_enum_ext(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo); int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 6ba5f7c23d3a..f46472d50c9b 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2550,33 +2550,6 @@ int snd_soc_put_value_enum_double(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_GPL(snd_soc_put_value_enum_double); -/** - * snd_soc_info_enum_ext - external enumerated single mixer info callback - * @kcontrol: mixer control - * @uinfo: control element information - * - * Callback to provide information about an external enumerated - * single mixer. - * - * Returns 0 for success. - */ -int snd_soc_info_enum_ext(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = e->max; - - if (uinfo->value.enumerated.item > e->max - 1) - uinfo->value.enumerated.item = e->max - 1; - strcpy(uinfo->value.enumerated.name, - e->texts[uinfo->value.enumerated.item]); - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_info_enum_ext); - /** * snd_soc_info_volsw - single mixer info callback * @kcontrol: mixer control -- cgit v1.2.3 From 4ca0c0d4784fa82d68733f7793e3487023e12282 Mon Sep 17 00:00:00 2001 From: Padmavathi Venna Date: Mon, 12 Aug 2013 15:19:52 +0530 Subject: ASoC: Samsung: I2S: Modify the I2S driver to support I2S on Exynos5420 Exynos5420 added support for I2S TDM mode. For this, there are some register changes in the I2S controller. This patch adds the relevant register changes to support I2S in normal mode. This patch adds a quirk for TDM mode and if TDM mode is present all the relevent changes will be applied. Signed-off-by: Padmavathi Venna Reviewed-by: Tomasz Figa Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/samsung-i2s.txt | 4 ++ include/linux/platform_data/asoc-s3c.h | 1 + sound/soc/samsung/i2s-regs.h | 15 ++++ sound/soc/samsung/i2s.c | 81 +++++++++++++++++++--- 4 files changed, 93 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/sound/samsung-i2s.txt b/Documentation/devicetree/bindings/sound/samsung-i2s.txt index 25a0024d1b0a..7386d444ada1 100644 --- a/Documentation/devicetree/bindings/sound/samsung-i2s.txt +++ b/Documentation/devicetree/bindings/sound/samsung-i2s.txt @@ -6,6 +6,10 @@ Required SoC Specific Properties: - samsung,s3c6410-i2s: for 8/16/24bit stereo I2S. - samsung,s5pv210-i2s: for 8/16/24bit multichannel(5.1) I2S with secondary fifo, s/w reset control and internal mux for root clk src. + - samsung,exynos5420-i2s: for 8/16/24bit multichannel(7.1) I2S with + secondary fifo, s/w reset control, internal mux for root clk src and + TDM support. TDM (Time division multiplexing) is to allow transfer of + multiple channel audio data on single data line. - reg: physical base address of the controller and length of memory mapped region. diff --git a/include/linux/platform_data/asoc-s3c.h b/include/linux/platform_data/asoc-s3c.h index 88272591a895..9efc04dd255a 100644 --- a/include/linux/platform_data/asoc-s3c.h +++ b/include/linux/platform_data/asoc-s3c.h @@ -36,6 +36,7 @@ struct samsung_i2s { */ #define QUIRK_NO_MUXPSR (1 << 2) #define QUIRK_NEED_RSTCLR (1 << 3) +#define QUIRK_SUPPORTS_TDM (1 << 4) /* Quirks of the I2S controller */ u32 quirks; dma_addr_t idma_addr; diff --git a/sound/soc/samsung/i2s-regs.h b/sound/soc/samsung/i2s-regs.h index 30513b7ede3a..821a50231002 100644 --- a/sound/soc/samsung/i2s-regs.h +++ b/sound/soc/samsung/i2s-regs.h @@ -31,6 +31,10 @@ #define I2SLVL1ADDR 0x34 #define I2SLVL2ADDR 0x38 #define I2SLVL3ADDR 0x3c +#define I2SSTR1 0x40 +#define I2SVER 0x44 +#define I2SFIC2 0x48 +#define I2STDM 0x4c #define CON_RSTCLR (1 << 31) #define CON_FRXOFSTATUS (1 << 26) @@ -117,6 +121,17 @@ #define MOD_BCLK_MASK 3 #define MOD_8BIT (1 << 0) +#define EXYNOS5420_MOD_LRP_SHIFT 15 +#define EXYNOS5420_MOD_SDF_SHIFT 6 +#define EXYNOS5420_MOD_RCLK_SHIFT 4 +#define EXYNOS5420_MOD_BCLK_SHIFT 0 +#define EXYNOS5420_MOD_BCLK_64FS 4 +#define EXYNOS5420_MOD_BCLK_96FS 5 +#define EXYNOS5420_MOD_BCLK_128FS 6 +#define EXYNOS5420_MOD_BCLK_192FS 7 +#define EXYNOS5420_MOD_BCLK_256FS 8 +#define EXYNOS5420_MOD_BCLK_MASK 0xf + #define MOD_CDCLKCON (1 << 12) #define PSR_PSREN (1 << 15) diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 3b4835a1bd23..dd995a7ab55c 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -199,7 +199,12 @@ static inline bool is_manager(struct i2s_dai *i2s) /* Read RCLK of I2S (in multiples of LRCLK) */ static inline unsigned get_rfs(struct i2s_dai *i2s) { - u32 rfs = (readl(i2s->addr + I2SMOD) >> MOD_RCLK_SHIFT); + u32 rfs; + + if (i2s->quirks & QUIRK_SUPPORTS_TDM) + rfs = readl(i2s->addr + I2SMOD) >> EXYNOS5420_MOD_RCLK_SHIFT; + else + rfs = (readl(i2s->addr + I2SMOD) >> MOD_RCLK_SHIFT); rfs &= MOD_RCLK_MASK; switch (rfs) { @@ -214,8 +219,12 @@ static inline unsigned get_rfs(struct i2s_dai *i2s) static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs) { u32 mod = readl(i2s->addr + I2SMOD); - int rfs_shift = MOD_RCLK_SHIFT; + int rfs_shift; + if (i2s->quirks & QUIRK_SUPPORTS_TDM) + rfs_shift = EXYNOS5420_MOD_RCLK_SHIFT; + else + rfs_shift = MOD_RCLK_SHIFT; mod &= ~(MOD_RCLK_MASK << rfs_shift); switch (rfs) { @@ -239,10 +248,22 @@ static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs) /* Read Bit-Clock of I2S (in multiples of LRCLK) */ static inline unsigned get_bfs(struct i2s_dai *i2s) { - u32 bfs = readl(i2s->addr + I2SMOD) >> MOD_BCLK_SHIFT; - bfs &= MOD_BCLK_MASK; + u32 bfs; + + if (i2s->quirks & QUIRK_SUPPORTS_TDM) { + bfs = readl(i2s->addr + I2SMOD) >> EXYNOS5420_MOD_BCLK_SHIFT; + bfs &= EXYNOS5420_MOD_BCLK_MASK; + } else { + bfs = readl(i2s->addr + I2SMOD) >> MOD_BCLK_SHIFT; + bfs &= MOD_BCLK_MASK; + } switch (bfs) { + case 8: return 256; + case 7: return 192; + case 6: return 128; + case 5: return 96; + case 4: return 64; case 3: return 24; case 2: return 16; case 1: return 48; @@ -254,9 +275,22 @@ static inline unsigned get_bfs(struct i2s_dai *i2s) static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs) { u32 mod = readl(i2s->addr + I2SMOD); - int bfs_shift = MOD_BCLK_SHIFT; + int bfs_shift; + int tdm = i2s->quirks & QUIRK_SUPPORTS_TDM; - mod &= ~(MOD_BCLK_MASK << bfs_shift); + if (i2s->quirks & QUIRK_SUPPORTS_TDM) { + bfs_shift = EXYNOS5420_MOD_BCLK_SHIFT; + mod &= ~(EXYNOS5420_MOD_BCLK_MASK << bfs_shift); + } else { + bfs_shift = MOD_BCLK_SHIFT; + mod &= ~(MOD_BCLK_MASK << bfs_shift); + } + + /* Non-TDM I2S controllers do not support BCLK > 48 * FS */ + if (!tdm && bfs > 48) { + dev_err(&i2s->pdev->dev, "Unsupported BCLK divider\n"); + return; + } switch (bfs) { case 48: @@ -271,6 +305,21 @@ static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs) case 16: mod |= (MOD_BCLK_16FS << bfs_shift); break; + case 64: + mod |= (EXYNOS5420_MOD_BCLK_64FS << bfs_shift); + break; + case 96: + mod |= (EXYNOS5420_MOD_BCLK_96FS << bfs_shift); + break; + case 128: + mod |= (EXYNOS5420_MOD_BCLK_128FS << bfs_shift); + break; + case 192: + mod |= (EXYNOS5420_MOD_BCLK_192FS << bfs_shift); + break; + case 256: + mod |= (EXYNOS5420_MOD_BCLK_256FS << bfs_shift); + break; default: dev_err(&i2s->pdev->dev, "Wrong BCLK Divider!\n"); return; @@ -496,10 +545,17 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, { struct i2s_dai *i2s = to_info(dai); u32 mod = readl(i2s->addr + I2SMOD); - int lrp_shift = MOD_LRP_SHIFT, sdf_shift = MOD_SDF_SHIFT; - int sdf_mask, lrp_rlow; + int lrp_shift, sdf_shift, sdf_mask, lrp_rlow; u32 tmp = 0; + if (i2s->quirks & QUIRK_SUPPORTS_TDM) { + lrp_shift = EXYNOS5420_MOD_LRP_SHIFT; + sdf_shift = EXYNOS5420_MOD_SDF_SHIFT; + } else { + lrp_shift = MOD_LRP_SHIFT; + sdf_shift = MOD_SDF_SHIFT; + } + sdf_mask = MOD_SDF_MASK << sdf_shift; lrp_rlow = MOD_LR_RLOW << lrp_shift; @@ -1253,6 +1309,12 @@ static const struct samsung_i2s_dai_data i2sv5_dai_type = { .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR, }; +static const struct samsung_i2s_dai_data i2sv6_dai_type = { + .dai_type = TYPE_PRI, + .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | + QUIRK_SUPPORTS_TDM, +}; + static const struct samsung_i2s_dai_data samsung_dai_type_pri = { .dai_type = TYPE_PRI, }; @@ -1281,6 +1343,9 @@ static const struct of_device_id exynos_i2s_match[] = { }, { .compatible = "samsung,s5pv210-i2s", .data = &i2sv5_dai_type, + }, { + .compatible = "samsung,exynos5420-i2s", + .data = &i2sv6_dai_type, }, {}, }; -- cgit v1.2.3 From 6446221c14ef3bf58754cf1948631128dbe62700 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Mon, 12 Aug 2013 10:37:18 +0200 Subject: ARM: pxa: ssp: add pxa_ssp_request_of() Add a function to lookup ssp devices from device tree. This way, users can reference the ssp devices in order to register to them. Signed-off-by: Daniel Mack Acked-by: Haojian Zhuang Signed-off-by: Mark Brown --- arch/arm/plat-pxa/ssp.c | 25 +++++++++++++++++++++++++ include/linux/pxa2xx_ssp.h | 11 +++++++++++ 2 files changed, 36 insertions(+) (limited to 'include') diff --git a/arch/arm/plat-pxa/ssp.c b/arch/arm/plat-pxa/ssp.c index f2661355fa4e..c83f27b6bdda 100644 --- a/arch/arm/plat-pxa/ssp.c +++ b/arch/arm/plat-pxa/ssp.c @@ -62,6 +62,30 @@ struct ssp_device *pxa_ssp_request(int port, const char *label) } EXPORT_SYMBOL(pxa_ssp_request); +struct ssp_device *pxa_ssp_request_of(const struct device_node *of_node, + const char *label) +{ + struct ssp_device *ssp = NULL; + + mutex_lock(&ssp_lock); + + list_for_each_entry(ssp, &ssp_list, node) { + if (ssp->of_node == of_node && ssp->use_count == 0) { + ssp->use_count++; + ssp->label = label; + break; + } + } + + mutex_unlock(&ssp_lock); + + if (&ssp->node == &ssp_list) + return NULL; + + return ssp; +} +EXPORT_SYMBOL(pxa_ssp_request_of); + void pxa_ssp_free(struct ssp_device *ssp) { mutex_lock(&ssp_lock); @@ -185,6 +209,7 @@ static int pxa_ssp_probe(struct platform_device *pdev) } ssp->use_count = 0; + ssp->of_node = dev->of_node; mutex_lock(&ssp_lock); list_add(&ssp->node, &ssp_list); diff --git a/include/linux/pxa2xx_ssp.h b/include/linux/pxa2xx_ssp.h index 467cc6307b62..49444203328a 100644 --- a/include/linux/pxa2xx_ssp.h +++ b/include/linux/pxa2xx_ssp.h @@ -21,6 +21,8 @@ #include #include +#include + /* * SSP Serial Port Registers @@ -190,6 +192,8 @@ struct ssp_device { int irq; int drcmr_rx; int drcmr_tx; + + struct device_node *of_node; }; /** @@ -218,11 +222,18 @@ static inline u32 pxa_ssp_read_reg(struct ssp_device *dev, u32 reg) #ifdef CONFIG_ARCH_PXA struct ssp_device *pxa_ssp_request(int port, const char *label); void pxa_ssp_free(struct ssp_device *); +struct ssp_device *pxa_ssp_request_of(const struct device_node *of_node, + const char *label); #else static inline struct ssp_device *pxa_ssp_request(int port, const char *label) { return NULL; } +static inline struct ssp_device *pxa_ssp_request_of(const struct device_node *n, + const char *name) +{ + return NULL; +} static inline void pxa_ssp_free(struct ssp_device *ssp) {} #endif -- cgit v1.2.3 From d65a14587a9be853a887a1407db133df1fb68e29 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Mon, 12 Aug 2013 10:42:39 +0200 Subject: ASoC: pxa: use snd_dmaengine_dai_dma_data Use snd_dmaengine_dai_dma_data for passing the dma parameters from clients to the pxa pcm lib. This does no functional change, it's just an intermedia step to migrate the pxa bits over to dmaengine. The calculation of dcmd is a transition hack which will be removed again in a later patch. It's just there to make the transition more readable. Signed-off-by: Daniel Mack Acked-by: Mark Brown Signed-off-by: Mark Brown --- include/sound/pxa2xx-lib.h | 7 ----- sound/arm/pxa2xx-ac97.c | 26 ++++++++++-------- sound/arm/pxa2xx-pcm-lib.c | 52 +++++++++++++++++++++++++++++------ sound/arm/pxa2xx-pcm.c | 5 +++- sound/arm/pxa2xx-pcm.h | 6 ++-- sound/soc/pxa/mmp-pcm.c | 8 ++++-- sound/soc/pxa/mmp-sspa.c | 12 +++++--- sound/soc/pxa/pxa-ssp.c | 36 ++++++++---------------- sound/soc/pxa/pxa2xx-ac97.c | 67 +++++++++++++++++++++++---------------------- sound/soc/pxa/pxa2xx-i2s.c | 28 +++++++++---------- sound/soc/pxa/pxa2xx-pcm.c | 8 ++++-- 11 files changed, 142 insertions(+), 113 deletions(-) (limited to 'include') diff --git a/include/sound/pxa2xx-lib.h b/include/sound/pxa2xx-lib.h index 2fd3d251d9a5..56e818e4a1cb 100644 --- a/include/sound/pxa2xx-lib.h +++ b/include/sound/pxa2xx-lib.h @@ -6,13 +6,6 @@ /* PCM */ -struct pxa2xx_pcm_dma_params { - char *name; /* stream identifier */ - u32 dcmd; /* DMA descriptor dcmd field */ - volatile u32 *drcmr; /* the DMA request channel to use */ - u32 dev_addr; /* device physical address for DMA */ -}; - extern int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params); extern int __pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream); diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c index ce431e6e07cf..5066a3768b28 100644 --- a/sound/arm/pxa2xx-ac97.c +++ b/sound/arm/pxa2xx-ac97.c @@ -14,12 +14,14 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -41,20 +43,20 @@ static struct snd_ac97_bus_ops pxa2xx_ac97_ops = { .reset = pxa2xx_ac97_reset, }; -static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_out = { - .name = "AC97 PCM out", - .dev_addr = __PREG(PCDR), - .drcmr = &DRCMR(12), - .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | - DCMD_BURST32 | DCMD_WIDTH4, +static unsigned long pxa2xx_ac97_pcm_out_req = 12; +static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_out = { + .addr = __PREG(PCDR), + .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .maxburst = 32, + .filter_data = &pxa2xx_ac97_pcm_out_req, }; -static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_in = { - .name = "AC97 PCM in", - .dev_addr = __PREG(PCDR), - .drcmr = &DRCMR(11), - .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | - DCMD_BURST32 | DCMD_WIDTH4, +static unsigned long pxa2xx_ac97_pcm_in_req = 11; +static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_in = { + .addr = __PREG(PCDR), + .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .maxburst = 32, + .filter_data = &pxa2xx_ac97_pcm_in_req, }; static struct snd_pcm *pxa2xx_ac97_pcm; diff --git a/sound/arm/pxa2xx-pcm-lib.c b/sound/arm/pxa2xx-pcm-lib.c index 823359ed95e1..a61d7a9a995e 100644 --- a/sound/arm/pxa2xx-pcm-lib.c +++ b/sound/arm/pxa2xx-pcm-lib.c @@ -7,11 +7,13 @@ #include #include #include +#include #include #include #include #include +#include #include @@ -43,6 +45,35 @@ int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, size_t period = params_period_bytes(params); pxa_dma_desc *dma_desc; dma_addr_t dma_buff_phys, next_desc_phys; + u32 dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG; + + /* temporary transition hack */ + switch (rtd->params->addr_width) { + case DMA_SLAVE_BUSWIDTH_1_BYTE: + dcmd |= DCMD_WIDTH1; + break; + case DMA_SLAVE_BUSWIDTH_2_BYTES: + dcmd |= DCMD_WIDTH2; + break; + case DMA_SLAVE_BUSWIDTH_4_BYTES: + dcmd |= DCMD_WIDTH4; + break; + default: + /* can't happen */ + break; + } + + switch (rtd->params->maxburst) { + case 8: + dcmd |= DCMD_BURST8; + break; + case 16: + dcmd |= DCMD_BURST16; + break; + case 32: + dcmd |= DCMD_BURST32; + break; + } snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); runtime->dma_bytes = totsize; @@ -55,14 +86,14 @@ int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, dma_desc->ddadr = next_desc_phys; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { dma_desc->dsadr = dma_buff_phys; - dma_desc->dtadr = rtd->params->dev_addr; + dma_desc->dtadr = rtd->params->addr; } else { - dma_desc->dsadr = rtd->params->dev_addr; + dma_desc->dsadr = rtd->params->addr; dma_desc->dtadr = dma_buff_phys; } if (period > totsize) period = totsize; - dma_desc->dcmd = rtd->params->dcmd | period | DCMD_ENDIRQEN; + dma_desc->dcmd = dcmd | period | DCMD_ENDIRQEN; dma_desc++; dma_buff_phys += period; } while (totsize -= period); @@ -76,8 +107,10 @@ int __pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream) { struct pxa2xx_runtime_data *rtd = substream->runtime->private_data; - if (rtd && rtd->params && rtd->params->drcmr) - *rtd->params->drcmr = 0; + if (rtd && rtd->params && rtd->params->filter_data) { + unsigned long req = *(unsigned long *) rtd->params->filter_data; + DRCMR(req) = 0; + } snd_pcm_set_runtime_buffer(substream, NULL); return 0; @@ -136,6 +169,7 @@ EXPORT_SYMBOL(pxa2xx_pcm_pointer); int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream) { struct pxa2xx_runtime_data *prtd = substream->runtime->private_data; + unsigned long req; if (!prtd || !prtd->params) return 0; @@ -146,7 +180,8 @@ int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream) DCSR(prtd->dma_ch) &= ~DCSR_RUN; DCSR(prtd->dma_ch) = 0; DCMD(prtd->dma_ch) = 0; - *prtd->params->drcmr = prtd->dma_ch | DRCMR_MAPVLD; + req = *(unsigned long *) prtd->params->filter_data; + DRCMR(req) = prtd->dma_ch | DRCMR_MAPVLD; return 0; } @@ -155,7 +190,6 @@ EXPORT_SYMBOL(__pxa2xx_pcm_prepare); void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id) { struct snd_pcm_substream *substream = dev_id; - struct pxa2xx_runtime_data *rtd = substream->runtime->private_data; int dcsr; dcsr = DCSR(dma_ch); @@ -164,8 +198,8 @@ void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id) if (dcsr & DCSR_ENDINTR) { snd_pcm_period_elapsed(substream); } else { - printk(KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n", - rtd->params->name, dma_ch, dcsr); + printk(KERN_ERR "DMA error on channel %d (DCSR=%#x)\n", + dma_ch, dcsr); snd_pcm_stream_lock(substream); snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); snd_pcm_stream_unlock(substream); diff --git a/sound/arm/pxa2xx-pcm.c b/sound/arm/pxa2xx-pcm.c index 26422a3584ea..69a2455b4472 100644 --- a/sound/arm/pxa2xx-pcm.c +++ b/sound/arm/pxa2xx-pcm.c @@ -11,8 +11,11 @@ */ #include +#include + #include #include +#include #include "pxa2xx-pcm.h" @@ -40,7 +43,7 @@ static int pxa2xx_pcm_open(struct snd_pcm_substream *substream) rtd->params = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? client->playback_params : client->capture_params; - ret = pxa_request_dma(rtd->params->name, DMA_PRIO_LOW, + ret = pxa_request_dma("dma", DMA_PRIO_LOW, pxa2xx_pcm_dma_irq, substream); if (ret < 0) goto err2; diff --git a/sound/arm/pxa2xx-pcm.h b/sound/arm/pxa2xx-pcm.h index 65f86b56ba42..2a8fc08d52a1 100644 --- a/sound/arm/pxa2xx-pcm.h +++ b/sound/arm/pxa2xx-pcm.h @@ -13,14 +13,14 @@ struct pxa2xx_runtime_data { int dma_ch; - struct pxa2xx_pcm_dma_params *params; + struct snd_dmaengine_dai_dma_data *params; pxa_dma_desc *dma_desc_array; dma_addr_t dma_desc_array_phys; }; struct pxa2xx_pcm_client { - struct pxa2xx_pcm_dma_params *playback_params; - struct pxa2xx_pcm_dma_params *capture_params; + struct snd_dmaengine_dai_dma_data *playback_params; + struct snd_dmaengine_dai_dma_data *capture_params; int (*startup)(struct snd_pcm_substream *); void (*shutdown)(struct snd_pcm_substream *); int (*prepare)(struct snd_pcm_substream *); diff --git a/sound/soc/pxa/mmp-pcm.c b/sound/soc/pxa/mmp-pcm.c index 5d57e071cdf5..9a97843ab09f 100644 --- a/sound/soc/pxa/mmp-pcm.c +++ b/sound/soc/pxa/mmp-pcm.c @@ -17,6 +17,8 @@ #include #include #include +#include + #include #include #include @@ -67,7 +69,7 @@ static int mmp_pcm_hw_params(struct snd_pcm_substream *substream, { struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream); struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct pxa2xx_pcm_dma_params *dma_params; + struct snd_dmaengine_dai_dma_data *dma_params; struct dma_slave_config slave_config; int ret; @@ -80,10 +82,10 @@ static int mmp_pcm_hw_params(struct snd_pcm_substream *substream, return ret; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - slave_config.dst_addr = dma_params->dev_addr; + slave_config.dst_addr = dma_params->addr; slave_config.dst_maxburst = 4; } else { - slave_config.src_addr = dma_params->dev_addr; + slave_config.src_addr = dma_params->addr; slave_config.src_maxburst = 4; } diff --git a/sound/soc/pxa/mmp-sspa.c b/sound/soc/pxa/mmp-sspa.c index 1605934d525e..41752a5fe3b0 100644 --- a/sound/soc/pxa/mmp-sspa.c +++ b/sound/soc/pxa/mmp-sspa.c @@ -27,12 +27,15 @@ #include #include #include +#include + #include #include #include #include #include #include +#include #include "mmp-sspa.h" /* @@ -40,7 +43,7 @@ */ struct sspa_priv { struct ssp_device *sspa; - struct pxa2xx_pcm_dma_params *dma_params; + struct snd_dmaengine_dai_dma_data *dma_params; struct clk *audio_clk; struct clk *sysclk; int dai_fmt; @@ -266,7 +269,7 @@ static int mmp_sspa_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct sspa_priv *sspa_priv = snd_soc_dai_get_drvdata(dai); struct ssp_device *sspa = sspa_priv->sspa; - struct pxa2xx_pcm_dma_params *dma_params; + struct snd_dmaengine_dai_dma_data *dma_params; u32 sspa_ctrl; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) @@ -309,7 +312,7 @@ static int mmp_sspa_hw_params(struct snd_pcm_substream *substream, } dma_params = &sspa_priv->dma_params[substream->stream]; - dma_params->dev_addr = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? + dma_params->addr = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? (sspa->phys_base + SSPA_TXD) : (sspa->phys_base + SSPA_RXD); snd_soc_dai_set_dma_data(cpu_dai, substream, dma_params); @@ -425,7 +428,8 @@ static int asoc_mmp_sspa_probe(struct platform_device *pdev) return -ENOMEM; priv->dma_params = devm_kzalloc(&pdev->dev, - 2 * sizeof(struct pxa2xx_pcm_dma_params), GFP_KERNEL); + 2 * sizeof(struct snd_dmaengine_dai_dma_data), + GFP_KERNEL); if (priv->dma_params == NULL) return -ENOMEM; diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c index 19296f22cb28..c0dcc3538e35 100644 --- a/sound/soc/pxa/pxa-ssp.c +++ b/sound/soc/pxa/pxa-ssp.c @@ -22,6 +22,7 @@ #include #include #include +#include #include @@ -31,9 +32,9 @@ #include #include #include +#include #include -#include #include "../../arm/pxa2xx-pcm.h" #include "pxa-ssp.h" @@ -80,27 +81,14 @@ static void pxa_ssp_disable(struct ssp_device *ssp) __raw_writel(sscr0, ssp->mmio_base + SSCR0); } -struct pxa2xx_pcm_dma_data { - struct pxa2xx_pcm_dma_params params; - char name[20]; -}; - static void pxa_ssp_set_dma_params(struct ssp_device *ssp, int width4, - int out, struct pxa2xx_pcm_dma_params *dma_data) + int out, struct snd_dmaengine_dai_dma_data *dma) { - struct pxa2xx_pcm_dma_data *dma; - - dma = container_of(dma_data, struct pxa2xx_pcm_dma_data, params); - - snprintf(dma->name, 20, "SSP%d PCM %s %s", ssp->port_id, - width4 ? "32-bit" : "16-bit", out ? "out" : "in"); - - dma->params.name = dma->name; - dma->params.drcmr = &DRCMR(out ? ssp->drcmr_tx : ssp->drcmr_rx); - dma->params.dcmd = (out ? (DCMD_INCSRCADDR | DCMD_FLOWTRG) : - (DCMD_INCTRGADDR | DCMD_FLOWSRC)) | - (width4 ? DCMD_WIDTH4 : DCMD_WIDTH2) | DCMD_BURST16; - dma->params.dev_addr = ssp->phys_base + SSDR; + dma->filter_data = out ? &ssp->drcmr_tx : &ssp->drcmr_rx; + dma->addr_width = width4 ? DMA_SLAVE_BUSWIDTH_4_BYTES : + DMA_SLAVE_BUSWIDTH_2_BYTES; + dma->maxburst = 16; + dma->addr = ssp->phys_base + SSDR; } static int pxa_ssp_startup(struct snd_pcm_substream *substream, @@ -108,7 +96,7 @@ static int pxa_ssp_startup(struct snd_pcm_substream *substream, { struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); struct ssp_device *ssp = priv->ssp; - struct pxa2xx_pcm_dma_data *dma; + struct snd_dmaengine_dai_dma_data *dma; int ret = 0; if (!cpu_dai->active) { @@ -116,10 +104,10 @@ static int pxa_ssp_startup(struct snd_pcm_substream *substream, pxa_ssp_disable(ssp); } - dma = kzalloc(sizeof(struct pxa2xx_pcm_dma_data), GFP_KERNEL); + dma = kzalloc(sizeof(struct snd_dmaengine_dai_dma_data), GFP_KERNEL); if (!dma) return -ENOMEM; - snd_soc_dai_set_dma_data(cpu_dai, substream, &dma->params); + snd_soc_dai_set_dma_data(cpu_dai, substream, dma); return ret; } @@ -560,7 +548,7 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream, u32 sspsp; int width = snd_pcm_format_physical_width(params_format(params)); int ttsa = pxa_ssp_read_reg(ssp, SSTSA) & 0xf; - struct pxa2xx_pcm_dma_params *dma_data; + struct snd_dmaengine_dai_dma_data *dma_data; dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream); diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index 1475515712e6..f1059d999de6 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c @@ -14,15 +14,16 @@ #include #include #include +#include #include #include #include #include +#include #include #include -#include #include #include "pxa2xx-ac97.h" @@ -48,44 +49,44 @@ static struct snd_ac97_bus_ops pxa2xx_ac97_ops = { .reset = pxa2xx_ac97_cold_reset, }; -static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_stereo_out = { - .name = "AC97 PCM Stereo out", - .dev_addr = __PREG(PCDR), - .drcmr = &DRCMR(12), - .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | - DCMD_BURST32 | DCMD_WIDTH4, +static unsigned long pxa2xx_ac97_pcm_stereo_in_req = 12; +static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_in = { + .addr = __PREG(PCDR), + .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .maxburst = 32, + .filter_data = &pxa2xx_ac97_pcm_stereo_in_req, }; -static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_stereo_in = { - .name = "AC97 PCM Stereo in", - .dev_addr = __PREG(PCDR), - .drcmr = &DRCMR(11), - .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | - DCMD_BURST32 | DCMD_WIDTH4, +static unsigned long pxa2xx_ac97_pcm_stereo_out_req = 11; +static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_out = { + .addr = __PREG(PCDR), + .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .maxburst = 32, + .filter_data = &pxa2xx_ac97_pcm_stereo_out_req, }; -static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_aux_mono_out = { - .name = "AC97 Aux PCM (Slot 5) Mono out", - .dev_addr = __PREG(MODR), - .drcmr = &DRCMR(10), - .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | - DCMD_BURST16 | DCMD_WIDTH2, +static unsigned long pxa2xx_ac97_pcm_aux_mono_out_req = 10; +static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_out = { + .addr = __PREG(MODR), + .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, + .maxburst = 16, + .filter_data = &pxa2xx_ac97_pcm_aux_mono_out_req, }; -static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_aux_mono_in = { - .name = "AC97 Aux PCM (Slot 5) Mono in", - .dev_addr = __PREG(MODR), - .drcmr = &DRCMR(9), - .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | - DCMD_BURST16 | DCMD_WIDTH2, +static unsigned long pxa2xx_ac97_pcm_aux_mono_in_req = 9; +static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_in = { + .addr = __PREG(MODR), + .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, + .maxburst = 16, + .filter_data = &pxa2xx_ac97_pcm_aux_mono_in_req, }; -static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_mic_mono_in = { - .name = "AC97 Mic PCM (Slot 6) Mono in", - .dev_addr = __PREG(MCDR), - .drcmr = &DRCMR(8), - .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | - DCMD_BURST16 | DCMD_WIDTH2, +static unsigned long pxa2xx_ac97_pcm_aux_mic_mono_req = 8; +static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_mic_mono_in = { + .addr = __PREG(MCDR), + .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, + .maxburst = 16, + .filter_data = &pxa2xx_ac97_pcm_aux_mic_mono_req, }; #ifdef CONFIG_PM @@ -119,7 +120,7 @@ static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai) { - struct pxa2xx_pcm_dma_params *dma_data; + struct snd_dmaengine_dai_dma_data *dma_data; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) dma_data = &pxa2xx_ac97_pcm_stereo_out; @@ -135,7 +136,7 @@ static int pxa2xx_ac97_hw_aux_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai) { - struct pxa2xx_pcm_dma_params *dma_data; + struct snd_dmaengine_dai_dma_data *dma_data; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) dma_data = &pxa2xx_ac97_pcm_aux_mono_out; diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c index f7ca71664112..d5340a088858 100644 --- a/sound/soc/pxa/pxa2xx-i2s.c +++ b/sound/soc/pxa/pxa2xx-i2s.c @@ -23,9 +23,9 @@ #include #include #include +#include #include -#include #include #include "pxa2xx-i2s.h" @@ -82,20 +82,20 @@ static struct pxa_i2s_port pxa_i2s; static struct clk *clk_i2s; static int clk_ena = 0; -static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_out = { - .name = "I2S PCM Stereo out", - .dev_addr = __PREG(SADR), - .drcmr = &DRCMR(3), - .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | - DCMD_BURST32 | DCMD_WIDTH4, +static unsigned long pxa2xx_i2s_pcm_stereo_out_req = 3; +static struct snd_dmaengine_dai_dma_data pxa2xx_i2s_pcm_stereo_out = { + .addr = __PREG(SADR), + .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .maxburst = 32, + .filter_data = &pxa2xx_i2s_pcm_stereo_out_req, }; -static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_in = { - .name = "I2S PCM Stereo in", - .dev_addr = __PREG(SADR), - .drcmr = &DRCMR(2), - .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | - DCMD_BURST32 | DCMD_WIDTH4, +static unsigned long pxa2xx_i2s_pcm_stereo_in_req = 2; +static struct snd_dmaengine_dai_dma_data pxa2xx_i2s_pcm_stereo_in = { + .addr = __PREG(SADR), + .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .maxburst = 32, + .filter_data = &pxa2xx_i2s_pcm_stereo_in_req, }; static int pxa2xx_i2s_startup(struct snd_pcm_substream *substream, @@ -163,7 +163,7 @@ static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct pxa2xx_pcm_dma_params *dma_data; + struct snd_dmaengine_dai_dma_data *dma_data; BUG_ON(IS_ERR(clk_i2s)); clk_prepare_enable(clk_i2s); diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c index ecff116cb7b0..0aa2d695064a 100644 --- a/sound/soc/pxa/pxa2xx-pcm.c +++ b/sound/soc/pxa/pxa2xx-pcm.c @@ -12,10 +12,12 @@ #include #include +#include #include #include #include +#include #include "../../arm/pxa2xx-pcm.h" @@ -25,7 +27,7 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime = substream->runtime; struct pxa2xx_runtime_data *prtd = runtime->private_data; struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct pxa2xx_pcm_dma_params *dma; + struct snd_dmaengine_dai_dma_data *dma; int ret; dma = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); @@ -39,7 +41,7 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, * with different params */ if (prtd->params == NULL) { prtd->params = dma; - ret = pxa_request_dma(prtd->params->name, DMA_PRIO_LOW, + ret = pxa_request_dma("name", DMA_PRIO_LOW, pxa2xx_pcm_dma_irq, substream); if (ret < 0) return ret; @@ -47,7 +49,7 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, } else if (prtd->params != dma) { pxa_free_dma(prtd->dma_ch); prtd->params = dma; - ret = pxa_request_dma(prtd->params->name, DMA_PRIO_LOW, + ret = pxa_request_dma("name", DMA_PRIO_LOW, pxa2xx_pcm_dma_irq, substream); if (ret < 0) return ret; -- cgit v1.2.3 From 74b77b1510c90787d4e2e3a8412b85b235590ba5 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Fri, 16 Aug 2013 11:45:33 +0200 Subject: ASoC: imx-audmux: Move definitions to dt-bindings Move imx-audmux macro definitions to include/dt-bindings, so they can be used for devicetree. Signed-off-by: Markus Pargmann Signed-off-by: Mark Brown --- include/dt-bindings/sound/fsl-imx-audmux.h | 56 ++++++++++++++++++++++++++++++ sound/soc/fsl/imx-audmux.h | 52 +-------------------------- 2 files changed, 57 insertions(+), 51 deletions(-) create mode 100644 include/dt-bindings/sound/fsl-imx-audmux.h (limited to 'include') diff --git a/include/dt-bindings/sound/fsl-imx-audmux.h b/include/dt-bindings/sound/fsl-imx-audmux.h new file mode 100644 index 000000000000..50b09e96f247 --- /dev/null +++ b/include/dt-bindings/sound/fsl-imx-audmux.h @@ -0,0 +1,56 @@ +#ifndef __DT_FSL_IMX_AUDMUX_H +#define __DT_FSL_IMX_AUDMUX_H + +#define MX27_AUDMUX_HPCR1_SSI0 0 +#define MX27_AUDMUX_HPCR2_SSI1 1 +#define MX27_AUDMUX_HPCR3_SSI_PINS_4 2 +#define MX27_AUDMUX_PPCR1_SSI_PINS_1 3 +#define MX27_AUDMUX_PPCR2_SSI_PINS_2 4 +#define MX27_AUDMUX_PPCR3_SSI_PINS_3 5 + +#define MX31_AUDMUX_PORT1_SSI0 0 +#define MX31_AUDMUX_PORT2_SSI1 1 +#define MX31_AUDMUX_PORT3_SSI_PINS_3 2 +#define MX31_AUDMUX_PORT4_SSI_PINS_4 3 +#define MX31_AUDMUX_PORT5_SSI_PINS_5 4 +#define MX31_AUDMUX_PORT6_SSI_PINS_6 5 +#define MX31_AUDMUX_PORT7_SSI_PINS_7 6 + +#define MX51_AUDMUX_PORT1_SSI0 0 +#define MX51_AUDMUX_PORT2_SSI1 1 +#define MX51_AUDMUX_PORT3 2 +#define MX51_AUDMUX_PORT4 3 +#define MX51_AUDMUX_PORT5 4 +#define MX51_AUDMUX_PORT6 5 +#define MX51_AUDMUX_PORT7 6 + +/* Register definitions for the i.MX21/27 Digital Audio Multiplexer */ +#define IMX_AUDMUX_V1_PCR_INMMASK(x) ((x) & 0xff) +#define IMX_AUDMUX_V1_PCR_INMEN (1 << 8) +#define IMX_AUDMUX_V1_PCR_TXRXEN (1 << 10) +#define IMX_AUDMUX_V1_PCR_SYN (1 << 12) +#define IMX_AUDMUX_V1_PCR_RXDSEL(x) (((x) & 0x7) << 13) +#define IMX_AUDMUX_V1_PCR_RFCSEL(x) (((x) & 0xf) << 20) +#define IMX_AUDMUX_V1_PCR_RCLKDIR (1 << 24) +#define IMX_AUDMUX_V1_PCR_RFSDIR (1 << 25) +#define IMX_AUDMUX_V1_PCR_TFCSEL(x) (((x) & 0xf) << 26) +#define IMX_AUDMUX_V1_PCR_TCLKDIR (1 << 30) +#define IMX_AUDMUX_V1_PCR_TFSDIR (1 << 31) + +/* Register definitions for the i.MX25/31/35/51 Digital Audio Multiplexer */ +#define IMX_AUDMUX_V2_PTCR_TFSDIR (1 << 31) +#define IMX_AUDMUX_V2_PTCR_TFSEL(x) (((x) & 0xf) << 27) +#define IMX_AUDMUX_V2_PTCR_TCLKDIR (1 << 26) +#define IMX_AUDMUX_V2_PTCR_TCSEL(x) (((x) & 0xf) << 22) +#define IMX_AUDMUX_V2_PTCR_RFSDIR (1 << 21) +#define IMX_AUDMUX_V2_PTCR_RFSEL(x) (((x) & 0xf) << 17) +#define IMX_AUDMUX_V2_PTCR_RCLKDIR (1 << 16) +#define IMX_AUDMUX_V2_PTCR_RCSEL(x) (((x) & 0xf) << 12) +#define IMX_AUDMUX_V2_PTCR_SYN (1 << 11) + +#define IMX_AUDMUX_V2_PDCR_RXDSEL(x) (((x) & 0x7) << 13) +#define IMX_AUDMUX_V2_PDCR_TXRXEN (1 << 12) +#define IMX_AUDMUX_V2_PDCR_MODE(x) (((x) & 0x3) << 8) +#define IMX_AUDMUX_V2_PDCR_INMMASK(x) ((x) & 0xff) + +#endif /* __DT_FSL_IMX_AUDMUX_H */ diff --git a/sound/soc/fsl/imx-audmux.h b/sound/soc/fsl/imx-audmux.h index b8ff44b9dafa..38a4209af7c6 100644 --- a/sound/soc/fsl/imx-audmux.h +++ b/sound/soc/fsl/imx-audmux.h @@ -1,57 +1,7 @@ #ifndef __IMX_AUDMUX_H #define __IMX_AUDMUX_H -#define MX27_AUDMUX_HPCR1_SSI0 0 -#define MX27_AUDMUX_HPCR2_SSI1 1 -#define MX27_AUDMUX_HPCR3_SSI_PINS_4 2 -#define MX27_AUDMUX_PPCR1_SSI_PINS_1 3 -#define MX27_AUDMUX_PPCR2_SSI_PINS_2 4 -#define MX27_AUDMUX_PPCR3_SSI_PINS_3 5 - -#define MX31_AUDMUX_PORT1_SSI0 0 -#define MX31_AUDMUX_PORT2_SSI1 1 -#define MX31_AUDMUX_PORT3_SSI_PINS_3 2 -#define MX31_AUDMUX_PORT4_SSI_PINS_4 3 -#define MX31_AUDMUX_PORT5_SSI_PINS_5 4 -#define MX31_AUDMUX_PORT6_SSI_PINS_6 5 -#define MX31_AUDMUX_PORT7_SSI_PINS_7 6 - -#define MX51_AUDMUX_PORT1_SSI0 0 -#define MX51_AUDMUX_PORT2_SSI1 1 -#define MX51_AUDMUX_PORT3 2 -#define MX51_AUDMUX_PORT4 3 -#define MX51_AUDMUX_PORT5 4 -#define MX51_AUDMUX_PORT6 5 -#define MX51_AUDMUX_PORT7 6 - -/* Register definitions for the i.MX21/27 Digital Audio Multiplexer */ -#define IMX_AUDMUX_V1_PCR_INMMASK(x) ((x) & 0xff) -#define IMX_AUDMUX_V1_PCR_INMEN (1 << 8) -#define IMX_AUDMUX_V1_PCR_TXRXEN (1 << 10) -#define IMX_AUDMUX_V1_PCR_SYN (1 << 12) -#define IMX_AUDMUX_V1_PCR_RXDSEL(x) (((x) & 0x7) << 13) -#define IMX_AUDMUX_V1_PCR_RFCSEL(x) (((x) & 0xf) << 20) -#define IMX_AUDMUX_V1_PCR_RCLKDIR (1 << 24) -#define IMX_AUDMUX_V1_PCR_RFSDIR (1 << 25) -#define IMX_AUDMUX_V1_PCR_TFCSEL(x) (((x) & 0xf) << 26) -#define IMX_AUDMUX_V1_PCR_TCLKDIR (1 << 30) -#define IMX_AUDMUX_V1_PCR_TFSDIR (1 << 31) - -/* Register definitions for the i.MX25/31/35/51 Digital Audio Multiplexer */ -#define IMX_AUDMUX_V2_PTCR_TFSDIR (1 << 31) -#define IMX_AUDMUX_V2_PTCR_TFSEL(x) (((x) & 0xf) << 27) -#define IMX_AUDMUX_V2_PTCR_TCLKDIR (1 << 26) -#define IMX_AUDMUX_V2_PTCR_TCSEL(x) (((x) & 0xf) << 22) -#define IMX_AUDMUX_V2_PTCR_RFSDIR (1 << 21) -#define IMX_AUDMUX_V2_PTCR_RFSEL(x) (((x) & 0xf) << 17) -#define IMX_AUDMUX_V2_PTCR_RCLKDIR (1 << 16) -#define IMX_AUDMUX_V2_PTCR_RCSEL(x) (((x) & 0xf) << 12) -#define IMX_AUDMUX_V2_PTCR_SYN (1 << 11) - -#define IMX_AUDMUX_V2_PDCR_RXDSEL(x) (((x) & 0x7) << 13) -#define IMX_AUDMUX_V2_PDCR_TXRXEN (1 << 12) -#define IMX_AUDMUX_V2_PDCR_MODE(x) (((x) & 0x3) << 8) -#define IMX_AUDMUX_V2_PDCR_INMMASK(x) ((x) & 0xff) +#include int imx_audmux_v1_configure_port(unsigned int port, unsigned int pcr); -- cgit v1.2.3 From bcbb15530ebe9737622cb7779b35c61f48b49734 Mon Sep 17 00:00:00 2001 From: Tim Gardner Date: Fri, 16 Aug 2013 11:18:59 -0600 Subject: ALSA: pcm: Add snd_printd_ratelimit() Direct calls to printk_limit() will emit log noise even when CONFIG_SND_DEBUG is not defined. Add a wrapper macro around printk_limit() that is conditionally defined by CONFIG_SND_DEBUG. Cc: Jaroslav Kysela Cc: Takashi Iwai Cc: Pierre-Louis Bossart Cc: Lars-Peter Clausen Cc: Yacine Belkadi Signed-off-by: Tim Gardner Signed-off-by: Takashi Iwai --- include/sound/core.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'include') diff --git a/include/sound/core.h b/include/sound/core.h index c586617cfa0d..2a14f1f02d4f 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -27,6 +27,7 @@ #include /* struct rw_semaphore */ #include /* pm_message_t */ #include +#include /* number of supported soundcards */ #ifdef CONFIG_SND_DYNAMIC_MINORS @@ -375,6 +376,11 @@ void __snd_printk(unsigned int level, const char *file, int line, */ #define snd_BUG() WARN(1, "BUG?\n") +/** + * Suppress high rates of output when CONFIG_SND_DEBUG is enabled. + */ +#define snd_printd_ratelimit() printk_ratelimit() + /** * snd_BUG_ON - debugging check macro * @cond: condition to evaluate @@ -398,6 +404,8 @@ static inline void _snd_printd(int level, const char *format, ...) {} unlikely(__ret_warn_on); \ }) +static inline bool snd_printd_ratelimit(void) { return false; } + #endif /* CONFIG_SND_DEBUG */ #ifdef CONFIG_SND_DEBUG_VERBOSE -- cgit v1.2.3 From b43dd416be21bc8ad60984e13def032f01aaaa18 Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Mon, 19 Aug 2013 17:20:32 +0200 Subject: ALSA: hdspm - Fix SNDRV_HDSPM_IOCTL_GET_LTC Use struct hdspm_ltc to query the LTC, using a mixer struct is just plain wrong. Due to the wrong struct, this ioctl was never working, so we're free to fix it without breaking userspace compatibility. Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai --- include/uapi/sound/hdspm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/sound/hdspm.h b/include/uapi/sound/hdspm.h index 1f59ea2a4a76..d956c3593f65 100644 --- a/include/uapi/sound/hdspm.h +++ b/include/uapi/sound/hdspm.h @@ -111,7 +111,7 @@ struct hdspm_ltc { enum hdspm_ltc_input_format input_format; }; -#define SNDRV_HDSPM_IOCTL_GET_LTC _IOR('H', 0x46, struct hdspm_mixer_ioctl) +#define SNDRV_HDSPM_IOCTL_GET_LTC _IOR('H', 0x46, struct hdspm_ltc) /** * The status data reflects the device's current state -- cgit v1.2.3 From 741a509f34d8d702f70d0ad99b8152c57d76961e Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Mon, 19 Aug 2013 17:05:55 +0200 Subject: ASoC: core: Generic ac97 link reset functions This patch adds generic ac97 reset functions using pincontrol and gpio parsed from devicetree. Signed-off-by: Markus Pargmann Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/soc-ac97link.txt | 28 ++++ include/sound/soc.h | 2 + sound/soc/soc-core.c | 153 +++++++++++++++++++++ 3 files changed, 183 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/soc-ac97link.txt (limited to 'include') diff --git a/Documentation/devicetree/bindings/sound/soc-ac97link.txt b/Documentation/devicetree/bindings/sound/soc-ac97link.txt new file mode 100644 index 000000000000..80152a87f239 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/soc-ac97link.txt @@ -0,0 +1,28 @@ +AC97 link bindings + +These bindings can be included within any other device node. + +Required properties: + - pinctrl-names: Has to contain following states to setup the correct + pinmuxing for the used gpios: + "ac97-running": AC97-link is active + "ac97-reset": AC97-link reset state + "ac97-warm-reset": AC97-link warm reset state + - ac97-gpios: List of gpio phandles with args in the order ac97-sync, + ac97-sdata, ac97-reset + + +Example: + +ssi { + ... + + pinctrl-names = "default", "ac97-running", "ac97-reset", "ac97-warm-reset"; + pinctrl-0 = <&ac97link_running>; + pinctrl-1 = <&ac97link_running>; + pinctrl-2 = <&ac97link_reset>; + pinctrl-3 = <&ac97link_warm_reset>; + ac97-gpios = <&gpio3 20 0 &gpio3 22 0 &gpio3 28 0>; + + ... +}; diff --git a/include/sound/soc.h b/include/sound/soc.h index 6eabee7ec15a..c0ac3bc7b7f4 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -468,6 +468,8 @@ int snd_soc_new_ac97_codec(struct snd_soc_codec *codec, void snd_soc_free_ac97_codec(struct snd_soc_codec *codec); int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops); +int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops, + struct platform_device *pdev); /* *Controls diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index d82ee386eab5..b5c91f9aa160 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -30,9 +30,12 @@ #include #include #include +#include #include #include #include +#include +#include #include #include #include @@ -69,6 +72,16 @@ static int pmdown_time = 5000; module_param(pmdown_time, int, 0); MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)"); +struct snd_ac97_reset_cfg { + struct pinctrl *pctl; + struct pinctrl_state *pstate_reset; + struct pinctrl_state *pstate_warm_reset; + struct pinctrl_state *pstate_run; + int gpio_sdata; + int gpio_sync; + int gpio_reset; +}; + /* returns the minimum number of bytes needed to represent * a particular given value */ static int min_bytes_needed(unsigned long val) @@ -2080,6 +2093,117 @@ int snd_soc_new_ac97_codec(struct snd_soc_codec *codec, } EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec); +static struct snd_ac97_reset_cfg snd_ac97_rst_cfg; + +static void snd_soc_ac97_warm_reset(struct snd_ac97 *ac97) +{ + struct pinctrl *pctl = snd_ac97_rst_cfg.pctl; + + pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_warm_reset); + + gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 1); + + udelay(10); + + gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0); + + pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run); + msleep(2); +} + +static void snd_soc_ac97_reset(struct snd_ac97 *ac97) +{ + struct pinctrl *pctl = snd_ac97_rst_cfg.pctl; + + pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_reset); + + gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0); + gpio_direction_output(snd_ac97_rst_cfg.gpio_sdata, 0); + gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 0); + + udelay(10); + + gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 1); + + pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run); + msleep(2); +} + +static int snd_soc_ac97_parse_pinctl(struct device *dev, + struct snd_ac97_reset_cfg *cfg) +{ + struct pinctrl *p; + struct pinctrl_state *state; + int gpio; + int ret; + + p = devm_pinctrl_get(dev); + if (IS_ERR(p)) { + dev_err(dev, "Failed to get pinctrl\n"); + return PTR_RET(p); + } + cfg->pctl = p; + + state = pinctrl_lookup_state(p, "ac97-reset"); + if (IS_ERR(state)) { + dev_err(dev, "Can't find pinctrl state ac97-reset\n"); + return PTR_RET(state); + } + cfg->pstate_reset = state; + + state = pinctrl_lookup_state(p, "ac97-warm-reset"); + if (IS_ERR(state)) { + dev_err(dev, "Can't find pinctrl state ac97-warm-reset\n"); + return PTR_RET(state); + } + cfg->pstate_warm_reset = state; + + state = pinctrl_lookup_state(p, "ac97-running"); + if (IS_ERR(state)) { + dev_err(dev, "Can't find pinctrl state ac97-running\n"); + return PTR_RET(state); + } + cfg->pstate_run = state; + + gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 0); + if (gpio < 0) { + dev_err(dev, "Can't find ac97-sync gpio\n"); + return gpio; + } + ret = devm_gpio_request(dev, gpio, "AC97 link sync"); + if (ret) { + dev_err(dev, "Failed requesting ac97-sync gpio\n"); + return ret; + } + cfg->gpio_sync = gpio; + + gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 1); + if (gpio < 0) { + dev_err(dev, "Can't find ac97-sdata gpio %d\n", gpio); + return gpio; + } + ret = devm_gpio_request(dev, gpio, "AC97 link sdata"); + if (ret) { + dev_err(dev, "Failed requesting ac97-sdata gpio\n"); + return ret; + } + cfg->gpio_sdata = gpio; + + gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 2); + if (gpio < 0) { + dev_err(dev, "Can't find ac97-reset gpio\n"); + return gpio; + } + ret = devm_gpio_request(dev, gpio, "AC97 link reset"); + if (ret) { + dev_err(dev, "Failed requesting ac97-reset gpio\n"); + return ret; + } + cfg->gpio_reset = gpio; + + return 0; +} + struct snd_ac97_bus_ops *soc_ac97_ops; EXPORT_SYMBOL_GPL(soc_ac97_ops); @@ -2097,6 +2221,35 @@ int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops) } EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops); +/** + * snd_soc_set_ac97_ops_of_reset - Set ac97 ops with generic ac97 reset functions + * + * This function sets the reset and warm_reset properties of ops and parses + * the device node of pdev to get pinctrl states and gpio numbers to use. + */ +int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops, + struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct snd_ac97_reset_cfg cfg; + int ret; + + ret = snd_soc_ac97_parse_pinctl(dev, &cfg); + if (ret) + return ret; + + ret = snd_soc_set_ac97_ops(ops); + if (ret) + return ret; + + ops->warm_reset = snd_soc_ac97_warm_reset; + ops->reset = snd_soc_ac97_reset; + + snd_ac97_rst_cfg = cfg; + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops_of_reset); + /** * snd_soc_free_ac97_codec - free AC97 codec device * @codec: audio codec -- cgit v1.2.3 From 824ef826f3c4d83d1925a5e351313bfd3e5ca6cb Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 27 Aug 2013 15:51:01 +0200 Subject: ASoC: Pass card instead of dapm context to snd_soc_dapm_new_widgets() snd_soc_dapm_new_widgets() works on the ASoC card as a whole not on a specific DAPM context. The DAPM context that is passed as the parameter is only used to look up the pointer to the card. This patch updates the signature of snd_soc_dapm_new_widgets() to take the card directly. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 2 +- sound/soc/soc-core.c | 2 +- sound/soc/soc-dapm.c | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index c728d28ae9a5..27a72d5d4b00 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -413,7 +413,7 @@ int snd_soc_dapm_new_pcm(struct snd_soc_card *card, struct snd_soc_dapm_widget *sink); /* dapm path setup */ -int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm); +int snd_soc_dapm_new_widgets(struct snd_soc_card *card); void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm); int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_route *route, int num); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index d476f752e3f1..ed3c253066b1 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1807,7 +1807,7 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) list_for_each_entry(codec, &card->codec_dev_list, card_list) snd_soc_dapm_auto_nc_codec_pins(codec); - snd_soc_dapm_new_widgets(&card->dapm); + snd_soc_dapm_new_widgets(card); ret = snd_card_register(card->snd_card); if (ret < 0) { diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 7e9afbc49ef2..548b1c9e875e 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2712,9 +2712,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_weak_routes); * * Returns 0 for success. */ -int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) +int snd_soc_dapm_new_widgets(struct snd_soc_card *card) { - struct snd_soc_card *card = dapm->card; struct snd_soc_dapm_widget *w; unsigned int val; -- cgit v1.2.3 From 43d92e7d9aa13b91687f671ea7015204bc88fb84 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 31 Aug 2013 11:02:17 +0200 Subject: ASoC: Remove unused control_type field from snd_soc_codec struct The control_type field was used by the core to track which raw IO methods to use, but when switching to regmap this was no longer necessary and so the last user of the field was removed in commit be3ea3b9 ("ASoC: Use new register map API for ASoC generic physical I/O"). The field is now completely unused and can be removed. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index d57a04e7553d..254bcda40695 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -701,7 +701,6 @@ struct snd_soc_codec { /* codec IO */ void *control_data; /* codec control (i2c/3wire) data */ - enum snd_soc_control_type control_type; hw_write_t hw_write; unsigned int (*hw_read)(struct snd_soc_codec *, unsigned int); unsigned int (*read)(struct snd_soc_codec *, unsigned int); -- cgit v1.2.3 From ad758a67048f58205f2777b0f0a1a02de824d280 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 31 Aug 2013 11:02:18 +0200 Subject: ASoC: Remove unused debugfs_dapm field from snd_soc_{platform,codec} struct The DAPM context struct has its own field where it stores the pointer to the DAPM debugfs entry. The debugfs_dapm field in the snd_soc_platform and snd_soc_codec structs are completely unused and can be removed. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 254bcda40695..c65cc7d70353 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -719,7 +719,6 @@ struct snd_soc_codec { #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_codec_root; struct dentry *debugfs_reg; - struct dentry *debugfs_dapm; #endif }; @@ -844,7 +843,6 @@ struct snd_soc_platform { #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_platform_root; - struct dentry *debugfs_dapm; #endif }; -- cgit v1.2.3 From d7b1538c7c0e395a308d6f4098d0985fe19e4584 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 31 Aug 2013 11:02:19 +0200 Subject: ASoC: Remove unused sysfs_registered field from snd_soc_codec struct The sysfs_registered field was added to the snd_soc_codec struct in commit f0fba2ad ("ASoC: multi-component - ASoC Multi-Component Support"), but has never been used. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index c65cc7d70353..26e0df0acd15 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -693,7 +693,6 @@ struct snd_soc_codec { unsigned int probed:1; /* Codec has been probed */ unsigned int ac97_registered:1; /* Codec has been AC97 registered */ unsigned int ac97_created:1; /* Codec has been created by SoC */ - unsigned int sysfs_registered:1; /* codec has been sysfs registered */ unsigned int cache_init:1; /* codec cache has been initialized */ unsigned int using_regmap:1; /* using regmap access */ u32 cache_only; /* Suppress writes to hardware */ -- cgit v1.2.3 From d6bead020d8f8bcaca5cdcb035250c44b21c93e7 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 29 Aug 2013 10:32:13 -0300 Subject: ASoC: soc-pcm: Allow to specify unidirectional dai_link Add 'playback_only' and 'capture_only' fields that can be used for specifying that a dai_link has a unidirectional capability. The motivation for this is for the cases of systems, such as Freescale MX28, that has two unidirectional DAIs. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown --- include/sound/soc.h | 4 ++++ sound/soc/soc-pcm.c | 10 ++++++++++ 2 files changed, 14 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index c0ac3bc7b7f4..65414e82c762 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -936,6 +936,10 @@ struct snd_soc_dai_link { /* machine stream operations */ const struct snd_soc_ops *ops; const struct snd_soc_compr_ops *compr_ops; + + /* For unidirectional dai links */ + bool playback_only; + bool capture_only; }; struct snd_soc_codec_conf { diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index b6c640332a17..9abaa52efb2e 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -2027,6 +2027,16 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) capture = 1; } + if (rtd->dai_link->playback_only) { + playback = 1; + capture = 0; + } + + if (rtd->dai_link->capture_only) { + playback = 0; + capture = 1; + } + /* create the PCM */ if (rtd->dai_link->no_pcm) { snprintf(new_name, sizeof(new_name), "(%s)", -- cgit v1.2.3