1 // SPDX-License-Identifier: (GPL-2.0 OR MIT)
3 // Copyright (c) 2018 BayLibre, SAS.
4 // Author: Jerome Brunet <jbrunet@baylibre.com>
7 #include <linux/module.h>
8 #include <linux/of_platform.h>
9 #include <linux/regmap.h>
10 #include <sound/soc.h>
12 #include "axg-tdm-formatter.h"
14 struct axg_tdm_formatter
{
15 struct list_head list
;
16 struct axg_tdm_stream
*stream
;
17 const struct axg_tdm_formatter_driver
*drv
;
22 struct clk
*lrclk_sel
;
27 int axg_tdm_formatter_set_channel_masks(struct regmap
*map
,
28 struct axg_tdm_stream
*ts
,
31 unsigned int val
, ch
= ts
->channels
;
36 * Distribute the channels of the stream over the available slots
39 for (i
= 0; i
< AXG_TDM_NUM_LANES
; i
++) {
43 for (j
= find_first_bit(&mask
, 32);
45 j
= find_next_bit(&mask
, 32, j
+ 1)) {
50 regmap_write(map
, offset
, val
);
51 offset
+= regmap_get_reg_stride(map
);
55 * If we still have channel left at the end of the process, it means
56 * the stream has more channels than we can accommodate and we should
57 * have caught this earlier.
59 if (WARN_ON(ch
!= 0)) {
60 pr_err("channel mask error\n");
66 EXPORT_SYMBOL_GPL(axg_tdm_formatter_set_channel_masks
);
68 static int axg_tdm_formatter_enable(struct axg_tdm_formatter
*formatter
)
70 struct axg_tdm_stream
*ts
= formatter
->stream
;
71 bool invert
= formatter
->drv
->invert_sclk
;
74 /* Do nothing if the formatter is already enabled */
75 if (formatter
->enabled
)
79 * If sclk is inverted, invert it back and provide the inversion
80 * required by the formatter
82 invert
^= axg_tdm_sclk_invert(ts
->iface
->fmt
);
83 ret
= clk_set_phase(formatter
->sclk
, invert
? 180 : 0);
87 /* Setup the stream parameter in the formatter */
88 ret
= formatter
->drv
->ops
->prepare(formatter
->map
, formatter
->stream
);
92 /* Enable the signal clocks feeding the formatter */
93 ret
= clk_prepare_enable(formatter
->sclk
);
97 ret
= clk_prepare_enable(formatter
->lrclk
);
99 clk_disable_unprepare(formatter
->sclk
);
103 /* Finally, actually enable the formatter */
104 formatter
->drv
->ops
->enable(formatter
->map
);
105 formatter
->enabled
= true;
110 static void axg_tdm_formatter_disable(struct axg_tdm_formatter
*formatter
)
112 /* Do nothing if the formatter is already disabled */
113 if (!formatter
->enabled
)
116 formatter
->drv
->ops
->disable(formatter
->map
);
117 clk_disable_unprepare(formatter
->lrclk
);
118 clk_disable_unprepare(formatter
->sclk
);
119 formatter
->enabled
= false;
122 static int axg_tdm_formatter_attach(struct axg_tdm_formatter
*formatter
)
124 struct axg_tdm_stream
*ts
= formatter
->stream
;
127 mutex_lock(&ts
->lock
);
129 /* Catch up if the stream is already running when we attach */
131 ret
= axg_tdm_formatter_enable(formatter
);
133 pr_err("failed to enable formatter\n");
138 list_add_tail(&formatter
->list
, &ts
->formatter_list
);
140 mutex_unlock(&ts
->lock
);
144 static void axg_tdm_formatter_dettach(struct axg_tdm_formatter
*formatter
)
146 struct axg_tdm_stream
*ts
= formatter
->stream
;
148 mutex_lock(&ts
->lock
);
149 list_del(&formatter
->list
);
150 mutex_unlock(&ts
->lock
);
152 axg_tdm_formatter_disable(formatter
);
155 static int axg_tdm_formatter_power_up(struct axg_tdm_formatter
*formatter
,
156 struct snd_soc_dapm_widget
*w
)
158 struct axg_tdm_stream
*ts
= formatter
->drv
->ops
->get_stream(w
);
162 * If we don't get a stream at this stage, it would mean that the
163 * widget is powering up but is not attached to any backend DAI.
164 * It should not happen, ever !
169 /* Clock our device */
170 ret
= clk_prepare_enable(formatter
->pclk
);
174 /* Reparent the bit clock to the TDM interface */
175 ret
= clk_set_parent(formatter
->sclk_sel
, ts
->iface
->sclk
);
179 /* Reparent the sample clock to the TDM interface */
180 ret
= clk_set_parent(formatter
->lrclk_sel
, ts
->iface
->lrclk
);
184 formatter
->stream
= ts
;
185 ret
= axg_tdm_formatter_attach(formatter
);
192 clk_disable_unprepare(formatter
->pclk
);
196 static void axg_tdm_formatter_power_down(struct axg_tdm_formatter
*formatter
)
198 axg_tdm_formatter_dettach(formatter
);
199 clk_disable_unprepare(formatter
->pclk
);
200 formatter
->stream
= NULL
;
203 int axg_tdm_formatter_event(struct snd_soc_dapm_widget
*w
,
204 struct snd_kcontrol
*control
,
207 struct snd_soc_component
*c
= snd_soc_dapm_to_component(w
->dapm
);
208 struct axg_tdm_formatter
*formatter
= snd_soc_component_get_drvdata(c
);
212 case SND_SOC_DAPM_PRE_PMU
:
213 ret
= axg_tdm_formatter_power_up(formatter
, w
);
216 case SND_SOC_DAPM_PRE_PMD
:
217 axg_tdm_formatter_power_down(formatter
);
221 dev_err(c
->dev
, "Unexpected event %d\n", event
);
227 EXPORT_SYMBOL_GPL(axg_tdm_formatter_event
);
229 int axg_tdm_formatter_probe(struct platform_device
*pdev
)
231 struct device
*dev
= &pdev
->dev
;
232 const struct axg_tdm_formatter_driver
*drv
;
233 struct axg_tdm_formatter
*formatter
;
234 struct resource
*res
;
238 drv
= of_device_get_match_data(dev
);
240 dev_err(dev
, "failed to match device\n");
244 formatter
= devm_kzalloc(dev
, sizeof(*formatter
), GFP_KERNEL
);
247 platform_set_drvdata(pdev
, formatter
);
248 formatter
->drv
= drv
;
250 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
251 regs
= devm_ioremap_resource(dev
, res
);
253 return PTR_ERR(regs
);
255 formatter
->map
= devm_regmap_init_mmio(dev
, regs
, drv
->regmap_cfg
);
256 if (IS_ERR(formatter
->map
)) {
257 dev_err(dev
, "failed to init regmap: %ld\n",
258 PTR_ERR(formatter
->map
));
259 return PTR_ERR(formatter
->map
);
262 /* Peripharal clock */
263 formatter
->pclk
= devm_clk_get(dev
, "pclk");
264 if (IS_ERR(formatter
->pclk
)) {
265 ret
= PTR_ERR(formatter
->pclk
);
266 if (ret
!= -EPROBE_DEFER
)
267 dev_err(dev
, "failed to get pclk: %d\n", ret
);
271 /* Formatter bit clock */
272 formatter
->sclk
= devm_clk_get(dev
, "sclk");
273 if (IS_ERR(formatter
->sclk
)) {
274 ret
= PTR_ERR(formatter
->sclk
);
275 if (ret
!= -EPROBE_DEFER
)
276 dev_err(dev
, "failed to get sclk: %d\n", ret
);
280 /* Formatter sample clock */
281 formatter
->lrclk
= devm_clk_get(dev
, "lrclk");
282 if (IS_ERR(formatter
->lrclk
)) {
283 ret
= PTR_ERR(formatter
->lrclk
);
284 if (ret
!= -EPROBE_DEFER
)
285 dev_err(dev
, "failed to get lrclk: %d\n", ret
);
289 /* Formatter bit clock input multiplexer */
290 formatter
->sclk_sel
= devm_clk_get(dev
, "sclk_sel");
291 if (IS_ERR(formatter
->sclk_sel
)) {
292 ret
= PTR_ERR(formatter
->sclk_sel
);
293 if (ret
!= -EPROBE_DEFER
)
294 dev_err(dev
, "failed to get sclk_sel: %d\n", ret
);
298 /* Formatter sample clock input multiplexer */
299 formatter
->lrclk_sel
= devm_clk_get(dev
, "lrclk_sel");
300 if (IS_ERR(formatter
->lrclk_sel
)) {
301 ret
= PTR_ERR(formatter
->lrclk_sel
);
302 if (ret
!= -EPROBE_DEFER
)
303 dev_err(dev
, "failed to get lrclk_sel: %d\n", ret
);
307 return devm_snd_soc_register_component(dev
, drv
->component_drv
,
310 EXPORT_SYMBOL_GPL(axg_tdm_formatter_probe
);
312 int axg_tdm_stream_start(struct axg_tdm_stream
*ts
)
314 struct axg_tdm_formatter
*formatter
;
317 mutex_lock(&ts
->lock
);
320 /* Start all the formatters attached to the stream */
321 list_for_each_entry(formatter
, &ts
->formatter_list
, list
) {
322 ret
= axg_tdm_formatter_enable(formatter
);
324 pr_err("failed to start tdm stream\n");
330 mutex_unlock(&ts
->lock
);
333 EXPORT_SYMBOL_GPL(axg_tdm_stream_start
);
335 void axg_tdm_stream_stop(struct axg_tdm_stream
*ts
)
337 struct axg_tdm_formatter
*formatter
;
339 mutex_lock(&ts
->lock
);
342 /* Stop all the formatters attached to the stream */
343 list_for_each_entry(formatter
, &ts
->formatter_list
, list
) {
344 axg_tdm_formatter_disable(formatter
);
347 mutex_unlock(&ts
->lock
);
349 EXPORT_SYMBOL_GPL(axg_tdm_stream_stop
);
351 struct axg_tdm_stream
*axg_tdm_stream_alloc(struct axg_tdm_iface
*iface
)
353 struct axg_tdm_stream
*ts
;
355 ts
= kzalloc(sizeof(*ts
), GFP_KERNEL
);
357 INIT_LIST_HEAD(&ts
->formatter_list
);
358 mutex_init(&ts
->lock
);
364 EXPORT_SYMBOL_GPL(axg_tdm_stream_alloc
);
366 void axg_tdm_stream_free(struct axg_tdm_stream
*ts
)
369 * If the list is not empty, it would mean that one of the formatter
370 * widget is still powered and attached to the interface while we
371 * we are removing the TDM DAI. It should not be possible
373 WARN_ON(!list_empty(&ts
->formatter_list
));
374 mutex_destroy(&ts
->lock
);
377 EXPORT_SYMBOL_GPL(axg_tdm_stream_free
);
379 MODULE_DESCRIPTION("Amlogic AXG TDM formatter driver");
380 MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
381 MODULE_LICENSE("GPL v2");