Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ASoC: meson: g12a: extract codec-to-codec utils

The hdmi routing mechanism used on g12a hdmi is also used:
* other Amlogic SoC types
* for the internal DAC path

Each of these codec glues are slightly different but the idea
behind it remains the same. This change extract some helper functions
from the g12a-tohdmitx driver to make them available for other Amlogic
codecs.

Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Link: https://lore.kernel.org/r/20200213155159.3235792-3-jbrunet@baylibre.com
Signed-off-by: Mark Brown <broonie@kernel.org>

authored by

Jerome Brunet and committed by
Mark Brown
9c29fd9b 1dfa5a5a

+230 -176
+4
sound/soc/meson/Kconfig
··· 85 85 Select Y or M to add support for PDM input embedded 86 86 in the Amlogic AXG SoC family 87 87 88 + config SND_MESON_CODEC_GLUE 89 + tristate 90 + 88 91 config SND_MESON_G12A_TOHDMITX 89 92 tristate "Amlogic G12A To HDMI TX Control Support" 90 93 select REGMAP_MMIO 94 + select SND_MESON_CODEC_GLUE 91 95 imply SND_SOC_HDMI_CODEC 92 96 help 93 97 Select Y or M to add support for HDMI audio on the g12a SoC
+2
sound/soc/meson/Makefile
··· 11 11 snd-soc-meson-axg-spdifin-objs := axg-spdifin.o 12 12 snd-soc-meson-axg-spdifout-objs := axg-spdifout.o 13 13 snd-soc-meson-axg-pdm-objs := axg-pdm.o 14 + snd-soc-meson-codec-glue-objs := meson-codec-glue.o 14 15 snd-soc-meson-g12a-tohdmitx-objs := g12a-tohdmitx.o 15 16 16 17 obj-$(CONFIG_SND_MESON_AXG_FIFO) += snd-soc-meson-axg-fifo.o ··· 25 24 obj-$(CONFIG_SND_MESON_AXG_SPDIFIN) += snd-soc-meson-axg-spdifin.o 26 25 obj-$(CONFIG_SND_MESON_AXG_SPDIFOUT) += snd-soc-meson-axg-spdifout.o 27 26 obj-$(CONFIG_SND_MESON_AXG_PDM) += snd-soc-meson-axg-pdm.o 27 + obj-$(CONFIG_SND_MESON_CODEC_GLUE) += snd-soc-meson-codec-glue.o 28 28 obj-$(CONFIG_SND_MESON_G12A_TOHDMITX) += snd-soc-meson-g12a-tohdmitx.o
+43 -176
sound/soc/meson/g12a-tohdmitx.c
··· 12 12 #include <sound/soc-dai.h> 13 13 14 14 #include <dt-bindings/sound/meson-g12a-tohdmitx.h> 15 + #include "meson-codec-glue.h" 15 16 16 17 #define G12A_TOHDMITX_DRV_NAME "g12a-tohdmitx" 17 18 18 19 #define TOHDMITX_CTRL0 0x0 19 20 #define CTRL0_ENABLE_SHIFT 31 20 - #define CTRL0_I2S_DAT_SEL GENMASK(13, 12) 21 + #define CTRL0_I2S_DAT_SEL_SHIFT 12 22 + #define CTRL0_I2S_DAT_SEL (0x3 << CTRL0_I2S_DAT_SEL_SHIFT) 21 23 #define CTRL0_I2S_LRCLK_SEL GENMASK(9, 8) 22 24 #define CTRL0_I2S_BLK_CAP_INV BIT(7) 23 25 #define CTRL0_I2S_BCLK_O_INV BIT(6) 24 26 #define CTRL0_I2S_BCLK_SEL GENMASK(5, 4) 25 27 #define CTRL0_SPDIF_CLK_CAP_INV BIT(3) 26 28 #define CTRL0_SPDIF_CLK_O_INV BIT(2) 27 - #define CTRL0_SPDIF_SEL BIT(1) 29 + #define CTRL0_SPDIF_SEL_SHIFT 1 30 + #define CTRL0_SPDIF_SEL (0x1 << CTRL0_SPDIF_SEL_SHIFT) 28 31 #define CTRL0_SPDIF_CLK_SEL BIT(0) 29 - 30 - struct g12a_tohdmitx_input { 31 - struct snd_soc_pcm_stream params; 32 - unsigned int fmt; 33 - }; 34 - 35 - static struct snd_soc_dapm_widget * 36 - g12a_tohdmitx_get_input(struct snd_soc_dapm_widget *w) 37 - { 38 - struct snd_soc_dapm_path *p = NULL; 39 - struct snd_soc_dapm_widget *in; 40 - 41 - snd_soc_dapm_widget_for_each_source_path(w, p) { 42 - if (!p->connect) 43 - continue; 44 - 45 - /* Check that we still are in the same component */ 46 - if (snd_soc_dapm_to_component(w->dapm) != 47 - snd_soc_dapm_to_component(p->source->dapm)) 48 - continue; 49 - 50 - if (p->source->id == snd_soc_dapm_dai_in) 51 - return p->source; 52 - 53 - in = g12a_tohdmitx_get_input(p->source); 54 - if (in) 55 - return in; 56 - } 57 - 58 - return NULL; 59 - } 60 - 61 - static struct g12a_tohdmitx_input * 62 - g12a_tohdmitx_get_input_data(struct snd_soc_dapm_widget *w) 63 - { 64 - struct snd_soc_dapm_widget *in = 65 - g12a_tohdmitx_get_input(w); 66 - struct snd_soc_dai *dai; 67 - 68 - if (WARN_ON(!in)) 69 - return NULL; 70 - 71 - dai = in->priv; 72 - 73 - return dai->playback_dma_data; 74 - } 75 32 76 33 static const char * const g12a_tohdmitx_i2s_mux_texts[] = { 77 34 "I2S A", "I2S B", "I2S C", 78 35 }; 79 36 80 - static SOC_ENUM_SINGLE_EXT_DECL(g12a_tohdmitx_i2s_mux_enum, 81 - g12a_tohdmitx_i2s_mux_texts); 82 - 83 - static int g12a_tohdmitx_get_input_val(struct snd_soc_component *component, 84 - unsigned int mask) 85 - { 86 - unsigned int val; 87 - 88 - snd_soc_component_read(component, TOHDMITX_CTRL0, &val); 89 - return (val & mask) >> __ffs(mask); 90 - } 91 - 92 - static int g12a_tohdmitx_i2s_mux_get_enum(struct snd_kcontrol *kcontrol, 93 - struct snd_ctl_elem_value *ucontrol) 94 - { 95 - struct snd_soc_component *component = 96 - snd_soc_dapm_kcontrol_component(kcontrol); 97 - 98 - ucontrol->value.enumerated.item[0] = 99 - g12a_tohdmitx_get_input_val(component, CTRL0_I2S_DAT_SEL); 100 - 101 - return 0; 102 - } 103 - 104 37 static int g12a_tohdmitx_i2s_mux_put_enum(struct snd_kcontrol *kcontrol, 105 - struct snd_ctl_elem_value *ucontrol) 38 + struct snd_ctl_elem_value *ucontrol) 106 39 { 107 40 struct snd_soc_component *component = 108 41 snd_soc_dapm_kcontrol_component(kcontrol); 109 42 struct snd_soc_dapm_context *dapm = 110 43 snd_soc_dapm_kcontrol_dapm(kcontrol); 111 44 struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 112 - unsigned int mux = ucontrol->value.enumerated.item[0]; 113 - unsigned int val = g12a_tohdmitx_get_input_val(component, 114 - CTRL0_I2S_DAT_SEL); 45 + unsigned int mux, changed; 46 + 47 + mux = snd_soc_enum_item_to_val(e, ucontrol->value.enumerated.item[0]); 48 + changed = snd_soc_component_test_bits(component, e->reg, 49 + CTRL0_I2S_DAT_SEL, 50 + FIELD_PREP(CTRL0_I2S_DAT_SEL, 51 + mux)); 52 + 53 + if (!changed) 54 + return 0; 115 55 116 56 /* Force disconnect of the mux while updating */ 117 - if (val != mux) 118 - snd_soc_dapm_mux_update_power(dapm, kcontrol, 0, NULL, NULL); 57 + snd_soc_dapm_mux_update_power(dapm, kcontrol, 0, NULL, NULL); 119 58 120 - snd_soc_component_update_bits(component, TOHDMITX_CTRL0, 59 + snd_soc_component_update_bits(component, e->reg, 121 60 CTRL0_I2S_DAT_SEL | 122 61 CTRL0_I2S_LRCLK_SEL | 123 62 CTRL0_I2S_BCLK_SEL, ··· 69 130 return 0; 70 131 } 71 132 133 + static SOC_ENUM_SINGLE_DECL(g12a_tohdmitx_i2s_mux_enum, TOHDMITX_CTRL0, 134 + CTRL0_I2S_DAT_SEL_SHIFT, 135 + g12a_tohdmitx_i2s_mux_texts); 136 + 72 137 static const struct snd_kcontrol_new g12a_tohdmitx_i2s_mux = 73 138 SOC_DAPM_ENUM_EXT("I2S Source", g12a_tohdmitx_i2s_mux_enum, 74 - g12a_tohdmitx_i2s_mux_get_enum, 139 + snd_soc_dapm_get_enum_double, 75 140 g12a_tohdmitx_i2s_mux_put_enum); 76 141 77 142 static const char * const g12a_tohdmitx_spdif_mux_texts[] = { 78 143 "SPDIF A", "SPDIF B", 79 144 }; 80 - 81 - static SOC_ENUM_SINGLE_EXT_DECL(g12a_tohdmitx_spdif_mux_enum, 82 - g12a_tohdmitx_spdif_mux_texts); 83 - 84 - static int g12a_tohdmitx_spdif_mux_get_enum(struct snd_kcontrol *kcontrol, 85 - struct snd_ctl_elem_value *ucontrol) 86 - { 87 - struct snd_soc_component *component = 88 - snd_soc_dapm_kcontrol_component(kcontrol); 89 - 90 - ucontrol->value.enumerated.item[0] = 91 - g12a_tohdmitx_get_input_val(component, CTRL0_SPDIF_SEL); 92 - 93 - return 0; 94 - } 95 145 96 146 static int g12a_tohdmitx_spdif_mux_put_enum(struct snd_kcontrol *kcontrol, 97 147 struct snd_ctl_elem_value *ucontrol) ··· 90 162 struct snd_soc_dapm_context *dapm = 91 163 snd_soc_dapm_kcontrol_dapm(kcontrol); 92 164 struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 93 - unsigned int mux = ucontrol->value.enumerated.item[0]; 94 - unsigned int val = g12a_tohdmitx_get_input_val(component, 95 - CTRL0_SPDIF_SEL); 165 + unsigned int mux, changed; 166 + 167 + mux = snd_soc_enum_item_to_val(e, ucontrol->value.enumerated.item[0]); 168 + changed = snd_soc_component_test_bits(component, TOHDMITX_CTRL0, 169 + CTRL0_SPDIF_SEL, 170 + FIELD_PREP(CTRL0_SPDIF_SEL, mux)); 171 + 172 + if (!changed) 173 + return 0; 96 174 97 175 /* Force disconnect of the mux while updating */ 98 - if (val != mux) 99 - snd_soc_dapm_mux_update_power(dapm, kcontrol, 0, NULL, NULL); 176 + snd_soc_dapm_mux_update_power(dapm, kcontrol, 0, NULL, NULL); 100 177 101 178 snd_soc_component_update_bits(component, TOHDMITX_CTRL0, 102 179 CTRL0_SPDIF_SEL | ··· 114 181 return 0; 115 182 } 116 183 184 + static SOC_ENUM_SINGLE_DECL(g12a_tohdmitx_spdif_mux_enum, TOHDMITX_CTRL0, 185 + CTRL0_SPDIF_SEL_SHIFT, 186 + g12a_tohdmitx_spdif_mux_texts); 187 + 117 188 static const struct snd_kcontrol_new g12a_tohdmitx_spdif_mux = 118 189 SOC_DAPM_ENUM_EXT("SPDIF Source", g12a_tohdmitx_spdif_mux_enum, 119 - g12a_tohdmitx_spdif_mux_get_enum, 190 + snd_soc_dapm_get_enum_double, 120 191 g12a_tohdmitx_spdif_mux_put_enum); 121 192 122 193 static const struct snd_kcontrol_new g12a_tohdmitx_out_enable = ··· 138 201 &g12a_tohdmitx_out_enable), 139 202 }; 140 203 141 - static int g12a_tohdmitx_input_probe(struct snd_soc_dai *dai) 142 - { 143 - struct g12a_tohdmitx_input *data; 144 - 145 - data = kzalloc(sizeof(*data), GFP_KERNEL); 146 - if (!data) 147 - return -ENOMEM; 148 - 149 - dai->playback_dma_data = data; 150 - return 0; 151 - } 152 - 153 - static int g12a_tohdmitx_input_remove(struct snd_soc_dai *dai) 154 - { 155 - kfree(dai->playback_dma_data); 156 - return 0; 157 - } 158 - 159 - static int g12a_tohdmitx_input_hw_params(struct snd_pcm_substream *substream, 160 - struct snd_pcm_hw_params *params, 161 - struct snd_soc_dai *dai) 162 - { 163 - struct g12a_tohdmitx_input *data = dai->playback_dma_data; 164 - 165 - data->params.rates = snd_pcm_rate_to_rate_bit(params_rate(params)); 166 - data->params.rate_min = params_rate(params); 167 - data->params.rate_max = params_rate(params); 168 - data->params.formats = 1 << params_format(params); 169 - data->params.channels_min = params_channels(params); 170 - data->params.channels_max = params_channels(params); 171 - data->params.sig_bits = dai->driver->playback.sig_bits; 172 - 173 - return 0; 174 - } 175 - 176 - 177 - static int g12a_tohdmitx_input_set_fmt(struct snd_soc_dai *dai, 178 - unsigned int fmt) 179 - { 180 - struct g12a_tohdmitx_input *data = dai->playback_dma_data; 181 - 182 - /* Save the source stream format for the downstream link */ 183 - data->fmt = fmt; 184 - return 0; 185 - } 186 - 187 - static int g12a_tohdmitx_output_startup(struct snd_pcm_substream *substream, 188 - struct snd_soc_dai *dai) 189 - { 190 - struct snd_soc_pcm_runtime *rtd = substream->private_data; 191 - struct g12a_tohdmitx_input *in_data = 192 - g12a_tohdmitx_get_input_data(dai->capture_widget); 193 - 194 - if (!in_data) 195 - return -ENODEV; 196 - 197 - if (WARN_ON(!rtd->dai_link->params)) { 198 - dev_warn(dai->dev, "codec2codec link expected\n"); 199 - return -EINVAL; 200 - } 201 - 202 - /* Replace link params with the input params */ 203 - rtd->dai_link->params = &in_data->params; 204 - 205 - if (!in_data->fmt) 206 - return 0; 207 - 208 - return snd_soc_runtime_set_dai_fmt(rtd, in_data->fmt); 209 - } 210 - 211 204 static const struct snd_soc_dai_ops g12a_tohdmitx_input_ops = { 212 - .hw_params = g12a_tohdmitx_input_hw_params, 213 - .set_fmt = g12a_tohdmitx_input_set_fmt, 205 + .hw_params = meson_codec_glue_input_hw_params, 206 + .set_fmt = meson_codec_glue_input_set_fmt, 214 207 }; 215 208 216 209 static const struct snd_soc_dai_ops g12a_tohdmitx_output_ops = { 217 - .startup = g12a_tohdmitx_output_startup, 210 + .startup = meson_codec_glue_output_startup, 218 211 }; 219 212 220 213 #define TOHDMITX_SPDIF_FORMATS \ ··· 171 304 .id = (xid), \ 172 305 .playback = TOHDMITX_STREAM(xname, "Playback", xfmt, xchmax), \ 173 306 .ops = &g12a_tohdmitx_input_ops, \ 174 - .probe = g12a_tohdmitx_input_probe, \ 175 - .remove = g12a_tohdmitx_input_remove, \ 307 + .probe = meson_codec_glue_input_dai_probe, \ 308 + .remove = meson_codec_glue_input_dai_remove, \ 176 309 } 177 310 178 311 #define TOHDMITX_OUT(xname, xid, xfmt, xchmax) { \
+149
sound/soc/meson/meson-codec-glue.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + // 3 + // Copyright (c) 2019 BayLibre, SAS. 4 + // Author: Jerome Brunet <jbrunet@baylibre.com> 5 + 6 + #include <linux/module.h> 7 + #include <sound/pcm_params.h> 8 + #include <sound/soc.h> 9 + #include <sound/soc-dai.h> 10 + 11 + #include "meson-codec-glue.h" 12 + 13 + static struct snd_soc_dapm_widget * 14 + meson_codec_glue_get_input(struct snd_soc_dapm_widget *w) 15 + { 16 + struct snd_soc_dapm_path *p = NULL; 17 + struct snd_soc_dapm_widget *in; 18 + 19 + snd_soc_dapm_widget_for_each_source_path(w, p) { 20 + if (!p->connect) 21 + continue; 22 + 23 + /* Check that we still are in the same component */ 24 + if (snd_soc_dapm_to_component(w->dapm) != 25 + snd_soc_dapm_to_component(p->source->dapm)) 26 + continue; 27 + 28 + if (p->source->id == snd_soc_dapm_dai_in) 29 + return p->source; 30 + 31 + in = meson_codec_glue_get_input(p->source); 32 + if (in) 33 + return in; 34 + } 35 + 36 + return NULL; 37 + } 38 + 39 + static void meson_codec_glue_input_set_data(struct snd_soc_dai *dai, 40 + struct meson_codec_glue_input *data) 41 + { 42 + dai->playback_dma_data = data; 43 + } 44 + 45 + struct meson_codec_glue_input * 46 + meson_codec_glue_input_get_data(struct snd_soc_dai *dai) 47 + { 48 + return dai->playback_dma_data; 49 + } 50 + EXPORT_SYMBOL_GPL(meson_codec_glue_input_get_data); 51 + 52 + static struct meson_codec_glue_input * 53 + meson_codec_glue_output_get_input_data(struct snd_soc_dapm_widget *w) 54 + { 55 + struct snd_soc_dapm_widget *in = 56 + meson_codec_glue_get_input(w); 57 + struct snd_soc_dai *dai; 58 + 59 + if (WARN_ON(!in)) 60 + return NULL; 61 + 62 + dai = in->priv; 63 + 64 + return meson_codec_glue_input_get_data(dai); 65 + } 66 + 67 + int meson_codec_glue_input_hw_params(struct snd_pcm_substream *substream, 68 + struct snd_pcm_hw_params *params, 69 + struct snd_soc_dai *dai) 70 + { 71 + struct meson_codec_glue_input *data = 72 + meson_codec_glue_input_get_data(dai); 73 + 74 + data->params.rates = snd_pcm_rate_to_rate_bit(params_rate(params)); 75 + data->params.rate_min = params_rate(params); 76 + data->params.rate_max = params_rate(params); 77 + data->params.formats = 1 << params_format(params); 78 + data->params.channels_min = params_channels(params); 79 + data->params.channels_max = params_channels(params); 80 + data->params.sig_bits = dai->driver->playback.sig_bits; 81 + 82 + return 0; 83 + } 84 + EXPORT_SYMBOL_GPL(meson_codec_glue_input_hw_params); 85 + 86 + int meson_codec_glue_input_set_fmt(struct snd_soc_dai *dai, 87 + unsigned int fmt) 88 + { 89 + struct meson_codec_glue_input *data = 90 + meson_codec_glue_input_get_data(dai); 91 + 92 + /* Save the source stream format for the downstream link */ 93 + data->fmt = fmt; 94 + return 0; 95 + } 96 + EXPORT_SYMBOL_GPL(meson_codec_glue_input_set_fmt); 97 + 98 + int meson_codec_glue_output_startup(struct snd_pcm_substream *substream, 99 + struct snd_soc_dai *dai) 100 + { 101 + struct snd_soc_pcm_runtime *rtd = substream->private_data; 102 + struct meson_codec_glue_input *in_data = 103 + meson_codec_glue_output_get_input_data(dai->capture_widget); 104 + 105 + if (!in_data) 106 + return -ENODEV; 107 + 108 + if (WARN_ON(!rtd->dai_link->params)) { 109 + dev_warn(dai->dev, "codec2codec link expected\n"); 110 + return -EINVAL; 111 + } 112 + 113 + /* Replace link params with the input params */ 114 + rtd->dai_link->params = &in_data->params; 115 + 116 + if (!in_data->fmt) 117 + return 0; 118 + 119 + return snd_soc_runtime_set_dai_fmt(rtd, in_data->fmt); 120 + } 121 + EXPORT_SYMBOL_GPL(meson_codec_glue_output_startup); 122 + 123 + int meson_codec_glue_input_dai_probe(struct snd_soc_dai *dai) 124 + { 125 + struct meson_codec_glue_input *data; 126 + 127 + data = kzalloc(sizeof(*data), GFP_KERNEL); 128 + if (!data) 129 + return -ENOMEM; 130 + 131 + meson_codec_glue_input_set_data(dai, data); 132 + return 0; 133 + } 134 + EXPORT_SYMBOL_GPL(meson_codec_glue_input_dai_probe); 135 + 136 + int meson_codec_glue_input_dai_remove(struct snd_soc_dai *dai) 137 + { 138 + struct meson_codec_glue_input *data = 139 + meson_codec_glue_input_get_data(dai); 140 + 141 + kfree(data); 142 + return 0; 143 + } 144 + EXPORT_SYMBOL_GPL(meson_codec_glue_input_dai_remove); 145 + 146 + MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); 147 + MODULE_DESCRIPTION("Amlogic Codec Glue Helpers"); 148 + MODULE_LICENSE("GPL v2"); 149 +
+32
sound/soc/meson/meson-codec-glue.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 2 + * 3 + * Copyright (c) 2018 Baylibre SAS. 4 + * Author: Jerome Brunet <jbrunet@baylibre.com> 5 + */ 6 + 7 + #ifndef _MESON_CODEC_GLUE_H 8 + #define _MESON_CODEC_GLUE_H 9 + 10 + #include <sound/soc.h> 11 + 12 + struct meson_codec_glue_input { 13 + struct snd_soc_pcm_stream params; 14 + unsigned int fmt; 15 + }; 16 + 17 + /* Input helpers */ 18 + struct meson_codec_glue_input * 19 + meson_codec_glue_input_get_data(struct snd_soc_dai *dai); 20 + int meson_codec_glue_input_hw_params(struct snd_pcm_substream *substream, 21 + struct snd_pcm_hw_params *params, 22 + struct snd_soc_dai *dai); 23 + int meson_codec_glue_input_set_fmt(struct snd_soc_dai *dai, 24 + unsigned int fmt); 25 + int meson_codec_glue_input_dai_probe(struct snd_soc_dai *dai); 26 + int meson_codec_glue_input_dai_remove(struct snd_soc_dai *dai); 27 + 28 + /* Output helpers */ 29 + int meson_codec_glue_output_startup(struct snd_pcm_substream *substream, 30 + struct snd_soc_dai *dai); 31 + 32 + #endif /* _MESON_CODEC_GLUE_H */