]>
Commit | Line | Data |
---|---|---|
b82b734c JB |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // | |
3 | // Copyright (c) 2020 BayLibre, SAS. | |
4 | // Author: Jerome Brunet <jbrunet@baylibre.com> | |
5 | ||
6 | #include <linux/bitfield.h> | |
7 | #include <sound/pcm_params.h> | |
8 | #include <sound/soc.h> | |
9 | #include <sound/soc-dai.h> | |
10 | ||
11 | #include <dt-bindings/sound/meson-aiu.h> | |
12 | #include "aiu.h" | |
13 | #include "meson-codec-glue.h" | |
14 | ||
15 | #define CTRL_CLK_SEL GENMASK(1, 0) | |
16 | #define CTRL_DATA_SEL_SHIFT 4 | |
17 | #define CTRL_DATA_SEL (0x3 << CTRL_DATA_SEL_SHIFT) | |
18 | ||
19 | static const char * const aiu_codec_ctrl_mux_texts[] = { | |
20 | "DISABLED", "PCM", "I2S", | |
21 | }; | |
22 | ||
23 | static int aiu_codec_ctrl_mux_put_enum(struct snd_kcontrol *kcontrol, | |
24 | struct snd_ctl_elem_value *ucontrol) | |
25 | { | |
26 | struct snd_soc_component *component = | |
27 | snd_soc_dapm_kcontrol_component(kcontrol); | |
28 | struct snd_soc_dapm_context *dapm = | |
29 | snd_soc_dapm_kcontrol_dapm(kcontrol); | |
30 | struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; | |
31 | unsigned int mux, changed; | |
32 | ||
33 | mux = snd_soc_enum_item_to_val(e, ucontrol->value.enumerated.item[0]); | |
34 | changed = snd_soc_component_test_bits(component, e->reg, | |
35 | CTRL_DATA_SEL, | |
36 | FIELD_PREP(CTRL_DATA_SEL, mux)); | |
37 | ||
38 | if (!changed) | |
39 | return 0; | |
40 | ||
41 | /* Force disconnect of the mux while updating */ | |
42 | snd_soc_dapm_mux_update_power(dapm, kcontrol, 0, NULL, NULL); | |
43 | ||
44 | /* Reset the source first */ | |
45 | snd_soc_component_update_bits(component, e->reg, | |
46 | CTRL_CLK_SEL | | |
47 | CTRL_DATA_SEL, | |
48 | FIELD_PREP(CTRL_CLK_SEL, 0) | | |
49 | FIELD_PREP(CTRL_DATA_SEL, 0)); | |
50 | ||
51 | /* Set the appropriate source */ | |
52 | snd_soc_component_update_bits(component, e->reg, | |
53 | CTRL_CLK_SEL | | |
54 | CTRL_DATA_SEL, | |
55 | FIELD_PREP(CTRL_CLK_SEL, mux) | | |
56 | FIELD_PREP(CTRL_DATA_SEL, mux)); | |
57 | ||
58 | snd_soc_dapm_mux_update_power(dapm, kcontrol, mux, e, NULL); | |
59 | ||
92190af3 | 60 | return 1; |
b82b734c JB |
61 | } |
62 | ||
63 | static SOC_ENUM_SINGLE_DECL(aiu_hdmi_ctrl_mux_enum, AIU_HDMI_CLK_DATA_CTRL, | |
64 | CTRL_DATA_SEL_SHIFT, | |
65 | aiu_codec_ctrl_mux_texts); | |
66 | ||
67 | static const struct snd_kcontrol_new aiu_hdmi_ctrl_mux = | |
68 | SOC_DAPM_ENUM_EXT("HDMI Source", aiu_hdmi_ctrl_mux_enum, | |
69 | snd_soc_dapm_get_enum_double, | |
70 | aiu_codec_ctrl_mux_put_enum); | |
71 | ||
72 | static const struct snd_soc_dapm_widget aiu_hdmi_ctrl_widgets[] = { | |
73 | SND_SOC_DAPM_MUX("HDMI CTRL SRC", SND_SOC_NOPM, 0, 0, | |
74 | &aiu_hdmi_ctrl_mux), | |
75 | }; | |
76 | ||
77 | static const struct snd_soc_dai_ops aiu_codec_ctrl_input_ops = { | |
78 | .hw_params = meson_codec_glue_input_hw_params, | |
79 | .set_fmt = meson_codec_glue_input_set_fmt, | |
80 | }; | |
81 | ||
82 | static const struct snd_soc_dai_ops aiu_codec_ctrl_output_ops = { | |
83 | .startup = meson_codec_glue_output_startup, | |
84 | }; | |
85 | ||
86 | #define AIU_CODEC_CTRL_FORMATS \ | |
87 | (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ | |
88 | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE | \ | |
89 | SNDRV_PCM_FMTBIT_S32_LE) | |
90 | ||
91 | #define AIU_CODEC_CTRL_STREAM(xname, xsuffix) \ | |
92 | { \ | |
93 | .stream_name = xname " " xsuffix, \ | |
94 | .channels_min = 1, \ | |
95 | .channels_max = 8, \ | |
96 | .rate_min = 5512, \ | |
97 | .rate_max = 192000, \ | |
98 | .formats = AIU_CODEC_CTRL_FORMATS, \ | |
99 | } | |
100 | ||
101 | #define AIU_CODEC_CTRL_INPUT(xname) { \ | |
102 | .name = "CODEC CTRL " xname, \ | |
103 | .playback = AIU_CODEC_CTRL_STREAM(xname, "Playback"), \ | |
104 | .ops = &aiu_codec_ctrl_input_ops, \ | |
105 | .probe = meson_codec_glue_input_dai_probe, \ | |
106 | .remove = meson_codec_glue_input_dai_remove, \ | |
107 | } | |
108 | ||
109 | #define AIU_CODEC_CTRL_OUTPUT(xname) { \ | |
110 | .name = "CODEC CTRL " xname, \ | |
111 | .capture = AIU_CODEC_CTRL_STREAM(xname, "Capture"), \ | |
112 | .ops = &aiu_codec_ctrl_output_ops, \ | |
113 | } | |
114 | ||
115 | static struct snd_soc_dai_driver aiu_hdmi_ctrl_dai_drv[] = { | |
116 | [CTRL_I2S] = AIU_CODEC_CTRL_INPUT("HDMI I2S IN"), | |
117 | [CTRL_PCM] = AIU_CODEC_CTRL_INPUT("HDMI PCM IN"), | |
118 | [CTRL_OUT] = AIU_CODEC_CTRL_OUTPUT("HDMI OUT"), | |
119 | }; | |
120 | ||
121 | static const struct snd_soc_dapm_route aiu_hdmi_ctrl_routes[] = { | |
122 | { "HDMI CTRL SRC", "I2S", "HDMI I2S IN Playback" }, | |
123 | { "HDMI CTRL SRC", "PCM", "HDMI PCM IN Playback" }, | |
124 | { "HDMI OUT Capture", NULL, "HDMI CTRL SRC" }, | |
125 | }; | |
126 | ||
127 | static int aiu_hdmi_of_xlate_dai_name(struct snd_soc_component *component, | |
933f98be | 128 | const struct of_phandle_args *args, |
b82b734c JB |
129 | const char **dai_name) |
130 | { | |
131 | return aiu_of_xlate_dai_name(component, args, dai_name, AIU_HDMI); | |
132 | } | |
133 | ||
134 | static const struct snd_soc_component_driver aiu_hdmi_ctrl_component = { | |
135 | .name = "AIU HDMI Codec Control", | |
136 | .dapm_widgets = aiu_hdmi_ctrl_widgets, | |
137 | .num_dapm_widgets = ARRAY_SIZE(aiu_hdmi_ctrl_widgets), | |
138 | .dapm_routes = aiu_hdmi_ctrl_routes, | |
139 | .num_dapm_routes = ARRAY_SIZE(aiu_hdmi_ctrl_routes), | |
140 | .of_xlate_dai_name = aiu_hdmi_of_xlate_dai_name, | |
141 | .endianness = 1, | |
142 | .non_legacy_dai_naming = 1, | |
143 | }; | |
144 | ||
145 | int aiu_hdmi_ctrl_register_component(struct device *dev) | |
146 | { | |
02471422 JB |
147 | return snd_soc_register_component(dev, &aiu_hdmi_ctrl_component, |
148 | aiu_hdmi_ctrl_dai_drv, | |
149 | ARRAY_SIZE(aiu_hdmi_ctrl_dai_drv)); | |
b82b734c JB |
150 | } |
151 |