Commit | Line | Data |
---|---|---|
7ed4877b JB |
1 | // SPDX-License-Identifier: (GPL-2.0 OR MIT) |
2 | // | |
3 | // Copyright (c) 2018 BayLibre, SAS. | |
4 | // Author: Jerome Brunet <jbrunet@baylibre.com> | |
5 | ||
6 | /* This driver implements the frontend capture DAI of AXG based SoCs */ | |
7 | ||
8 | #include <linux/clk.h> | |
9 | #include <linux/regmap.h> | |
10 | #include <linux/module.h> | |
11 | #include <linux/of_platform.h> | |
12 | #include <sound/pcm_params.h> | |
13 | #include <sound/soc.h> | |
14 | #include <sound/soc-dai.h> | |
15 | ||
16 | #include "axg-fifo.h" | |
17 | ||
18 | #define CTRL0_TODDR_SEL_RESAMPLE BIT(30) | |
19 | #define CTRL0_TODDR_EXT_SIGNED BIT(29) | |
20 | #define CTRL0_TODDR_PP_MODE BIT(28) | |
21 | #define CTRL0_TODDR_TYPE_MASK GENMASK(15, 13) | |
22 | #define CTRL0_TODDR_TYPE(x) ((x) << 13) | |
23 | #define CTRL0_TODDR_MSB_POS_MASK GENMASK(12, 8) | |
24 | #define CTRL0_TODDR_MSB_POS(x) ((x) << 8) | |
25 | #define CTRL0_TODDR_LSB_POS_MASK GENMASK(7, 3) | |
26 | #define CTRL0_TODDR_LSB_POS(x) ((x) << 3) | |
a3c23a8a | 27 | #define CTRL1_TODDR_FORCE_FINISH BIT(25) |
7ed4877b | 28 | |
984463a9 JB |
29 | #define TODDR_MSB_POS 31 |
30 | ||
7ed4877b JB |
31 | static int axg_toddr_pcm_new(struct snd_soc_pcm_runtime *rtd, |
32 | struct snd_soc_dai *dai) | |
33 | { | |
34 | return axg_fifo_pcm_new(rtd, SNDRV_PCM_STREAM_CAPTURE); | |
35 | } | |
36 | ||
a3c23a8a JB |
37 | static int g12a_toddr_dai_prepare(struct snd_pcm_substream *substream, |
38 | struct snd_soc_dai *dai) | |
39 | { | |
40 | struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai); | |
41 | ||
42 | /* Reset the write pointer to the FIFO_INIT_ADDR */ | |
43 | regmap_update_bits(fifo->map, FIFO_CTRL1, | |
44 | CTRL1_TODDR_FORCE_FINISH, 0); | |
45 | regmap_update_bits(fifo->map, FIFO_CTRL1, | |
46 | CTRL1_TODDR_FORCE_FINISH, CTRL1_TODDR_FORCE_FINISH); | |
47 | regmap_update_bits(fifo->map, FIFO_CTRL1, | |
48 | CTRL1_TODDR_FORCE_FINISH, 0); | |
49 | ||
50 | return 0; | |
51 | } | |
52 | ||
7ed4877b JB |
53 | static int axg_toddr_dai_hw_params(struct snd_pcm_substream *substream, |
54 | struct snd_pcm_hw_params *params, | |
55 | struct snd_soc_dai *dai) | |
56 | { | |
57 | struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai); | |
984463a9 | 58 | unsigned int type, width; |
7ed4877b JB |
59 | |
60 | switch (params_physical_width(params)) { | |
61 | case 8: | |
62 | type = 0; /* 8 samples of 8 bits */ | |
63 | break; | |
64 | case 16: | |
65 | type = 2; /* 4 samples of 16 bits - right justified */ | |
66 | break; | |
67 | case 32: | |
68 | type = 4; /* 2 samples of 32 bits - right justified */ | |
69 | break; | |
70 | default: | |
71 | return -EINVAL; | |
72 | } | |
73 | ||
74 | width = params_width(params); | |
75 | ||
76 | regmap_update_bits(fifo->map, FIFO_CTRL0, | |
77 | CTRL0_TODDR_TYPE_MASK | | |
78 | CTRL0_TODDR_MSB_POS_MASK | | |
79 | CTRL0_TODDR_LSB_POS_MASK, | |
80 | CTRL0_TODDR_TYPE(type) | | |
984463a9 JB |
81 | CTRL0_TODDR_MSB_POS(TODDR_MSB_POS) | |
82 | CTRL0_TODDR_LSB_POS(TODDR_MSB_POS - (width - 1))); | |
7ed4877b JB |
83 | |
84 | return 0; | |
85 | } | |
86 | ||
87 | static int axg_toddr_dai_startup(struct snd_pcm_substream *substream, | |
88 | struct snd_soc_dai *dai) | |
89 | { | |
90 | struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai); | |
91 | unsigned int fifo_threshold; | |
92 | int ret; | |
93 | ||
94 | /* Enable pclk to access registers and clock the fifo ip */ | |
95 | ret = clk_prepare_enable(fifo->pclk); | |
96 | if (ret) | |
97 | return ret; | |
98 | ||
99 | /* Select orginal data - resampling not supported ATM */ | |
100 | regmap_update_bits(fifo->map, FIFO_CTRL0, CTRL0_TODDR_SEL_RESAMPLE, 0); | |
101 | ||
102 | /* Only signed format are supported ATM */ | |
103 | regmap_update_bits(fifo->map, FIFO_CTRL0, CTRL0_TODDR_EXT_SIGNED, | |
104 | CTRL0_TODDR_EXT_SIGNED); | |
105 | ||
106 | /* Apply single buffer mode to the interface */ | |
107 | regmap_update_bits(fifo->map, FIFO_CTRL0, CTRL0_TODDR_PP_MODE, 0); | |
108 | ||
109 | /* TODDR does not have a configurable fifo depth */ | |
110 | fifo_threshold = AXG_FIFO_MIN_CNT - 1; | |
111 | regmap_update_bits(fifo->map, FIFO_CTRL1, CTRL1_THRESHOLD_MASK, | |
112 | CTRL1_THRESHOLD(fifo_threshold)); | |
113 | ||
114 | return 0; | |
115 | } | |
116 | ||
117 | static void axg_toddr_dai_shutdown(struct snd_pcm_substream *substream, | |
118 | struct snd_soc_dai *dai) | |
119 | { | |
120 | struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai); | |
121 | ||
122 | clk_disable_unprepare(fifo->pclk); | |
123 | } | |
124 | ||
125 | static const struct snd_soc_dai_ops axg_toddr_ops = { | |
126 | .hw_params = axg_toddr_dai_hw_params, | |
127 | .startup = axg_toddr_dai_startup, | |
128 | .shutdown = axg_toddr_dai_shutdown, | |
129 | }; | |
130 | ||
131 | static struct snd_soc_dai_driver axg_toddr_dai_drv = { | |
132 | .name = "TODDR", | |
133 | .capture = { | |
134 | .stream_name = "Capture", | |
135 | .channels_min = 1, | |
136 | .channels_max = AXG_FIFO_CH_MAX, | |
137 | .rates = AXG_FIFO_RATES, | |
138 | .formats = AXG_FIFO_FORMATS, | |
139 | }, | |
140 | .ops = &axg_toddr_ops, | |
141 | .pcm_new = axg_toddr_pcm_new, | |
142 | }; | |
143 | ||
144 | static const char * const axg_toddr_sel_texts[] = { | |
145 | "IN 0", "IN 1", "IN 2", "IN 3", "IN 4", "IN 6" | |
146 | }; | |
147 | ||
148 | static const unsigned int axg_toddr_sel_values[] = { | |
149 | 0, 1, 2, 3, 4, 6 | |
150 | }; | |
151 | ||
152 | static SOC_VALUE_ENUM_SINGLE_DECL(axg_toddr_sel_enum, FIFO_CTRL0, | |
153 | CTRL0_SEL_SHIFT, CTRL0_SEL_MASK, | |
154 | axg_toddr_sel_texts, axg_toddr_sel_values); | |
155 | ||
156 | static const struct snd_kcontrol_new axg_toddr_in_mux = | |
157 | SOC_DAPM_ENUM("Input Source", axg_toddr_sel_enum); | |
158 | ||
159 | static const struct snd_soc_dapm_widget axg_toddr_dapm_widgets[] = { | |
160 | SND_SOC_DAPM_MUX("SRC SEL", SND_SOC_NOPM, 0, 0, &axg_toddr_in_mux), | |
161 | SND_SOC_DAPM_AIF_IN("IN 0", NULL, 0, SND_SOC_NOPM, 0, 0), | |
162 | SND_SOC_DAPM_AIF_IN("IN 1", NULL, 0, SND_SOC_NOPM, 0, 0), | |
163 | SND_SOC_DAPM_AIF_IN("IN 2", NULL, 0, SND_SOC_NOPM, 0, 0), | |
164 | SND_SOC_DAPM_AIF_IN("IN 3", NULL, 0, SND_SOC_NOPM, 0, 0), | |
165 | SND_SOC_DAPM_AIF_IN("IN 4", NULL, 0, SND_SOC_NOPM, 0, 0), | |
166 | SND_SOC_DAPM_AIF_IN("IN 6", NULL, 0, SND_SOC_NOPM, 0, 0), | |
167 | }; | |
168 | ||
169 | static const struct snd_soc_dapm_route axg_toddr_dapm_routes[] = { | |
170 | { "Capture", NULL, "SRC SEL" }, | |
171 | { "SRC SEL", "IN 0", "IN 0" }, | |
172 | { "SRC SEL", "IN 1", "IN 1" }, | |
173 | { "SRC SEL", "IN 2", "IN 2" }, | |
174 | { "SRC SEL", "IN 3", "IN 3" }, | |
175 | { "SRC SEL", "IN 4", "IN 4" }, | |
176 | { "SRC SEL", "IN 6", "IN 6" }, | |
177 | }; | |
178 | ||
179 | static const struct snd_soc_component_driver axg_toddr_component_drv = { | |
180 | .dapm_widgets = axg_toddr_dapm_widgets, | |
181 | .num_dapm_widgets = ARRAY_SIZE(axg_toddr_dapm_widgets), | |
182 | .dapm_routes = axg_toddr_dapm_routes, | |
183 | .num_dapm_routes = ARRAY_SIZE(axg_toddr_dapm_routes), | |
184 | .ops = &axg_fifo_pcm_ops | |
185 | }; | |
186 | ||
187 | static const struct axg_fifo_match_data axg_toddr_match_data = { | |
188 | .component_drv = &axg_toddr_component_drv, | |
189 | .dai_drv = &axg_toddr_dai_drv | |
190 | }; | |
191 | ||
a3c23a8a JB |
192 | static const struct snd_soc_dai_ops g12a_toddr_ops = { |
193 | .prepare = g12a_toddr_dai_prepare, | |
194 | .hw_params = axg_toddr_dai_hw_params, | |
195 | .startup = axg_toddr_dai_startup, | |
196 | .shutdown = axg_toddr_dai_shutdown, | |
197 | }; | |
198 | ||
199 | static struct snd_soc_dai_driver g12a_toddr_dai_drv = { | |
200 | .name = "TODDR", | |
201 | .capture = { | |
202 | .stream_name = "Capture", | |
203 | .channels_min = 1, | |
204 | .channels_max = AXG_FIFO_CH_MAX, | |
205 | .rates = AXG_FIFO_RATES, | |
206 | .formats = AXG_FIFO_FORMATS, | |
207 | }, | |
208 | .ops = &g12a_toddr_ops, | |
209 | .pcm_new = axg_toddr_pcm_new, | |
210 | }; | |
211 | ||
212 | static const struct snd_soc_component_driver g12a_toddr_component_drv = { | |
213 | .dapm_widgets = axg_toddr_dapm_widgets, | |
214 | .num_dapm_widgets = ARRAY_SIZE(axg_toddr_dapm_widgets), | |
215 | .dapm_routes = axg_toddr_dapm_routes, | |
216 | .num_dapm_routes = ARRAY_SIZE(axg_toddr_dapm_routes), | |
217 | .ops = &g12a_fifo_pcm_ops | |
218 | }; | |
219 | ||
220 | static const struct axg_fifo_match_data g12a_toddr_match_data = { | |
221 | .component_drv = &g12a_toddr_component_drv, | |
222 | .dai_drv = &g12a_toddr_dai_drv | |
223 | }; | |
224 | ||
7ed4877b JB |
225 | static const struct of_device_id axg_toddr_of_match[] = { |
226 | { | |
227 | .compatible = "amlogic,axg-toddr", | |
228 | .data = &axg_toddr_match_data, | |
a3c23a8a JB |
229 | }, { |
230 | .compatible = "amlogic,g12a-toddr", | |
231 | .data = &g12a_toddr_match_data, | |
7ed4877b JB |
232 | }, {} |
233 | }; | |
234 | MODULE_DEVICE_TABLE(of, axg_toddr_of_match); | |
235 | ||
236 | static struct platform_driver axg_toddr_pdrv = { | |
237 | .probe = axg_fifo_probe, | |
238 | .driver = { | |
239 | .name = "axg-toddr", | |
240 | .of_match_table = axg_toddr_of_match, | |
241 | }, | |
242 | }; | |
243 | module_platform_driver(axg_toddr_pdrv); | |
244 | ||
245 | MODULE_DESCRIPTION("Amlogic AXG capture fifo driver"); | |
246 | MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); | |
247 | MODULE_LICENSE("GPL v2"); |