xref: /openbmc/linux/sound/soc/meson/axg-tdm-formatter.c (revision 1a11d88f499ceb69e9b4098ddc36866820335a54)
1*1a11d88fSJerome Brunet // SPDX-License-Identifier: (GPL-2.0 OR MIT)
2*1a11d88fSJerome Brunet //
3*1a11d88fSJerome Brunet // Copyright (c) 2018 BayLibre, SAS.
4*1a11d88fSJerome Brunet // Author: Jerome Brunet <jbrunet@baylibre.com>
5*1a11d88fSJerome Brunet 
6*1a11d88fSJerome Brunet #include <linux/clk.h>
7*1a11d88fSJerome Brunet #include <linux/module.h>
8*1a11d88fSJerome Brunet #include <linux/of_platform.h>
9*1a11d88fSJerome Brunet #include <linux/regmap.h>
10*1a11d88fSJerome Brunet #include <sound/soc.h>
11*1a11d88fSJerome Brunet 
12*1a11d88fSJerome Brunet #include "axg-tdm-formatter.h"
13*1a11d88fSJerome Brunet 
14*1a11d88fSJerome Brunet struct axg_tdm_formatter {
15*1a11d88fSJerome Brunet 	struct list_head list;
16*1a11d88fSJerome Brunet 	struct axg_tdm_stream *stream;
17*1a11d88fSJerome Brunet 	const struct axg_tdm_formatter_driver *drv;
18*1a11d88fSJerome Brunet 	struct clk *pclk;
19*1a11d88fSJerome Brunet 	struct clk *sclk;
20*1a11d88fSJerome Brunet 	struct clk *lrclk;
21*1a11d88fSJerome Brunet 	struct clk *sclk_sel;
22*1a11d88fSJerome Brunet 	struct clk *lrclk_sel;
23*1a11d88fSJerome Brunet 	bool enabled;
24*1a11d88fSJerome Brunet 	struct regmap *map;
25*1a11d88fSJerome Brunet };
26*1a11d88fSJerome Brunet 
27*1a11d88fSJerome Brunet int axg_tdm_formatter_set_channel_masks(struct regmap *map,
28*1a11d88fSJerome Brunet 					struct axg_tdm_stream *ts,
29*1a11d88fSJerome Brunet 					unsigned int offset)
30*1a11d88fSJerome Brunet {
31*1a11d88fSJerome Brunet 	unsigned int val, ch = ts->channels;
32*1a11d88fSJerome Brunet 	unsigned long mask;
33*1a11d88fSJerome Brunet 	int i, j;
34*1a11d88fSJerome Brunet 
35*1a11d88fSJerome Brunet 	/*
36*1a11d88fSJerome Brunet 	 * Distribute the channels of the stream over the available slots
37*1a11d88fSJerome Brunet 	 * of each TDM lane
38*1a11d88fSJerome Brunet 	 */
39*1a11d88fSJerome Brunet 	for (i = 0; i < AXG_TDM_NUM_LANES; i++) {
40*1a11d88fSJerome Brunet 		val = 0;
41*1a11d88fSJerome Brunet 		mask = ts->mask[i];
42*1a11d88fSJerome Brunet 
43*1a11d88fSJerome Brunet 		for (j = find_first_bit(&mask, 32);
44*1a11d88fSJerome Brunet 		     (j < 32) && ch;
45*1a11d88fSJerome Brunet 		     j = find_next_bit(&mask, 32, j + 1)) {
46*1a11d88fSJerome Brunet 			val |= 1 << j;
47*1a11d88fSJerome Brunet 			ch -= 1;
48*1a11d88fSJerome Brunet 		}
49*1a11d88fSJerome Brunet 
50*1a11d88fSJerome Brunet 		regmap_write(map, offset, val);
51*1a11d88fSJerome Brunet 		offset += regmap_get_reg_stride(map);
52*1a11d88fSJerome Brunet 	}
53*1a11d88fSJerome Brunet 
54*1a11d88fSJerome Brunet 	/*
55*1a11d88fSJerome Brunet 	 * If we still have channel left at the end of the process, it means
56*1a11d88fSJerome Brunet 	 * the stream has more channels than we can accommodate and we should
57*1a11d88fSJerome Brunet 	 * have caught this earlier.
58*1a11d88fSJerome Brunet 	 */
59*1a11d88fSJerome Brunet 	if (WARN_ON(ch != 0)) {
60*1a11d88fSJerome Brunet 		pr_err("channel mask error\n");
61*1a11d88fSJerome Brunet 		return -EINVAL;
62*1a11d88fSJerome Brunet 	}
63*1a11d88fSJerome Brunet 
64*1a11d88fSJerome Brunet 	return 0;
65*1a11d88fSJerome Brunet }
66*1a11d88fSJerome Brunet EXPORT_SYMBOL_GPL(axg_tdm_formatter_set_channel_masks);
67*1a11d88fSJerome Brunet 
68*1a11d88fSJerome Brunet static int axg_tdm_formatter_enable(struct axg_tdm_formatter *formatter)
69*1a11d88fSJerome Brunet {
70*1a11d88fSJerome Brunet 	struct axg_tdm_stream *ts = formatter->stream;
71*1a11d88fSJerome Brunet 	bool invert = formatter->drv->invert_sclk;
72*1a11d88fSJerome Brunet 	int ret;
73*1a11d88fSJerome Brunet 
74*1a11d88fSJerome Brunet 	/* Do nothing if the formatter is already enabled */
75*1a11d88fSJerome Brunet 	if (formatter->enabled)
76*1a11d88fSJerome Brunet 		return 0;
77*1a11d88fSJerome Brunet 
78*1a11d88fSJerome Brunet 	/*
79*1a11d88fSJerome Brunet 	 * If sclk is inverted, invert it back and provide the inversion
80*1a11d88fSJerome Brunet 	 * required by the formatter
81*1a11d88fSJerome Brunet 	 */
82*1a11d88fSJerome Brunet 	invert ^= axg_tdm_sclk_invert(ts->iface->fmt);
83*1a11d88fSJerome Brunet 	ret = clk_set_phase(formatter->sclk, invert ? 180 : 0);
84*1a11d88fSJerome Brunet 	if (ret)
85*1a11d88fSJerome Brunet 		return ret;
86*1a11d88fSJerome Brunet 
87*1a11d88fSJerome Brunet 	/* Setup the stream parameter in the formatter */
88*1a11d88fSJerome Brunet 	ret = formatter->drv->ops->prepare(formatter->map, formatter->stream);
89*1a11d88fSJerome Brunet 	if (ret)
90*1a11d88fSJerome Brunet 		return ret;
91*1a11d88fSJerome Brunet 
92*1a11d88fSJerome Brunet 	/* Enable the signal clocks feeding the formatter */
93*1a11d88fSJerome Brunet 	ret = clk_prepare_enable(formatter->sclk);
94*1a11d88fSJerome Brunet 	if (ret)
95*1a11d88fSJerome Brunet 		return ret;
96*1a11d88fSJerome Brunet 
97*1a11d88fSJerome Brunet 	ret = clk_prepare_enable(formatter->lrclk);
98*1a11d88fSJerome Brunet 	if (ret) {
99*1a11d88fSJerome Brunet 		clk_disable_unprepare(formatter->sclk);
100*1a11d88fSJerome Brunet 		return ret;
101*1a11d88fSJerome Brunet 	}
102*1a11d88fSJerome Brunet 
103*1a11d88fSJerome Brunet 	/* Finally, actually enable the formatter */
104*1a11d88fSJerome Brunet 	formatter->drv->ops->enable(formatter->map);
105*1a11d88fSJerome Brunet 	formatter->enabled = true;
106*1a11d88fSJerome Brunet 
107*1a11d88fSJerome Brunet 	return 0;
108*1a11d88fSJerome Brunet }
109*1a11d88fSJerome Brunet 
110*1a11d88fSJerome Brunet static void axg_tdm_formatter_disable(struct axg_tdm_formatter *formatter)
111*1a11d88fSJerome Brunet {
112*1a11d88fSJerome Brunet 	/* Do nothing if the formatter is already disabled */
113*1a11d88fSJerome Brunet 	if (!formatter->enabled)
114*1a11d88fSJerome Brunet 		return;
115*1a11d88fSJerome Brunet 
116*1a11d88fSJerome Brunet 	formatter->drv->ops->disable(formatter->map);
117*1a11d88fSJerome Brunet 	clk_disable_unprepare(formatter->lrclk);
118*1a11d88fSJerome Brunet 	clk_disable_unprepare(formatter->sclk);
119*1a11d88fSJerome Brunet 	formatter->enabled = false;
120*1a11d88fSJerome Brunet }
121*1a11d88fSJerome Brunet 
122*1a11d88fSJerome Brunet static int axg_tdm_formatter_attach(struct axg_tdm_formatter *formatter)
123*1a11d88fSJerome Brunet {
124*1a11d88fSJerome Brunet 	struct axg_tdm_stream *ts = formatter->stream;
125*1a11d88fSJerome Brunet 	int ret = 0;
126*1a11d88fSJerome Brunet 
127*1a11d88fSJerome Brunet 	mutex_lock(&ts->lock);
128*1a11d88fSJerome Brunet 
129*1a11d88fSJerome Brunet 	/* Catch up if the stream is already running when we attach */
130*1a11d88fSJerome Brunet 	if (ts->ready) {
131*1a11d88fSJerome Brunet 		ret = axg_tdm_formatter_enable(formatter);
132*1a11d88fSJerome Brunet 		if (ret) {
133*1a11d88fSJerome Brunet 			pr_err("failed to enable formatter\n");
134*1a11d88fSJerome Brunet 			goto out;
135*1a11d88fSJerome Brunet 		}
136*1a11d88fSJerome Brunet 	}
137*1a11d88fSJerome Brunet 
138*1a11d88fSJerome Brunet 	list_add_tail(&formatter->list, &ts->formatter_list);
139*1a11d88fSJerome Brunet out:
140*1a11d88fSJerome Brunet 	mutex_unlock(&ts->lock);
141*1a11d88fSJerome Brunet 	return ret;
142*1a11d88fSJerome Brunet }
143*1a11d88fSJerome Brunet 
144*1a11d88fSJerome Brunet static void axg_tdm_formatter_dettach(struct axg_tdm_formatter *formatter)
145*1a11d88fSJerome Brunet {
146*1a11d88fSJerome Brunet 	struct axg_tdm_stream *ts = formatter->stream;
147*1a11d88fSJerome Brunet 
148*1a11d88fSJerome Brunet 	mutex_lock(&ts->lock);
149*1a11d88fSJerome Brunet 	list_del(&formatter->list);
150*1a11d88fSJerome Brunet 	mutex_unlock(&ts->lock);
151*1a11d88fSJerome Brunet 
152*1a11d88fSJerome Brunet 	axg_tdm_formatter_disable(formatter);
153*1a11d88fSJerome Brunet }
154*1a11d88fSJerome Brunet 
155*1a11d88fSJerome Brunet static int axg_tdm_formatter_power_up(struct axg_tdm_formatter *formatter,
156*1a11d88fSJerome Brunet 				      struct snd_soc_dapm_widget *w)
157*1a11d88fSJerome Brunet {
158*1a11d88fSJerome Brunet 	struct axg_tdm_stream *ts = formatter->drv->ops->get_stream(w);
159*1a11d88fSJerome Brunet 	int ret;
160*1a11d88fSJerome Brunet 
161*1a11d88fSJerome Brunet 	/*
162*1a11d88fSJerome Brunet 	 * If we don't get a stream at this stage, it would mean that the
163*1a11d88fSJerome Brunet 	 * widget is powering up but is not attached to any backend DAI.
164*1a11d88fSJerome Brunet 	 * It should not happen, ever !
165*1a11d88fSJerome Brunet 	 */
166*1a11d88fSJerome Brunet 	if (WARN_ON(!ts))
167*1a11d88fSJerome Brunet 		return -ENODEV;
168*1a11d88fSJerome Brunet 
169*1a11d88fSJerome Brunet 	/* Clock our device */
170*1a11d88fSJerome Brunet 	ret = clk_prepare_enable(formatter->pclk);
171*1a11d88fSJerome Brunet 	if (ret)
172*1a11d88fSJerome Brunet 		return ret;
173*1a11d88fSJerome Brunet 
174*1a11d88fSJerome Brunet 	/* Reparent the bit clock to the TDM interface */
175*1a11d88fSJerome Brunet 	ret = clk_set_parent(formatter->sclk_sel, ts->iface->sclk);
176*1a11d88fSJerome Brunet 	if (ret)
177*1a11d88fSJerome Brunet 		goto disable_pclk;
178*1a11d88fSJerome Brunet 
179*1a11d88fSJerome Brunet 	/* Reparent the sample clock to the TDM interface */
180*1a11d88fSJerome Brunet 	ret = clk_set_parent(formatter->lrclk_sel, ts->iface->lrclk);
181*1a11d88fSJerome Brunet 	if (ret)
182*1a11d88fSJerome Brunet 		goto disable_pclk;
183*1a11d88fSJerome Brunet 
184*1a11d88fSJerome Brunet 	formatter->stream = ts;
185*1a11d88fSJerome Brunet 	ret = axg_tdm_formatter_attach(formatter);
186*1a11d88fSJerome Brunet 	if (ret)
187*1a11d88fSJerome Brunet 		goto disable_pclk;
188*1a11d88fSJerome Brunet 
189*1a11d88fSJerome Brunet 	return 0;
190*1a11d88fSJerome Brunet 
191*1a11d88fSJerome Brunet disable_pclk:
192*1a11d88fSJerome Brunet 	clk_disable_unprepare(formatter->pclk);
193*1a11d88fSJerome Brunet 	return ret;
194*1a11d88fSJerome Brunet }
195*1a11d88fSJerome Brunet 
196*1a11d88fSJerome Brunet static void axg_tdm_formatter_power_down(struct axg_tdm_formatter *formatter)
197*1a11d88fSJerome Brunet {
198*1a11d88fSJerome Brunet 	axg_tdm_formatter_dettach(formatter);
199*1a11d88fSJerome Brunet 	clk_disable_unprepare(formatter->pclk);
200*1a11d88fSJerome Brunet 	formatter->stream = NULL;
201*1a11d88fSJerome Brunet }
202*1a11d88fSJerome Brunet 
203*1a11d88fSJerome Brunet int axg_tdm_formatter_event(struct snd_soc_dapm_widget *w,
204*1a11d88fSJerome Brunet 			    struct snd_kcontrol *control,
205*1a11d88fSJerome Brunet 			    int event)
206*1a11d88fSJerome Brunet {
207*1a11d88fSJerome Brunet 	struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
208*1a11d88fSJerome Brunet 	struct axg_tdm_formatter *formatter = snd_soc_component_get_drvdata(c);
209*1a11d88fSJerome Brunet 	int ret = 0;
210*1a11d88fSJerome Brunet 
211*1a11d88fSJerome Brunet 	switch (event) {
212*1a11d88fSJerome Brunet 	case SND_SOC_DAPM_PRE_PMU:
213*1a11d88fSJerome Brunet 		ret = axg_tdm_formatter_power_up(formatter, w);
214*1a11d88fSJerome Brunet 		break;
215*1a11d88fSJerome Brunet 
216*1a11d88fSJerome Brunet 	case SND_SOC_DAPM_PRE_PMD:
217*1a11d88fSJerome Brunet 		axg_tdm_formatter_power_down(formatter);
218*1a11d88fSJerome Brunet 		break;
219*1a11d88fSJerome Brunet 
220*1a11d88fSJerome Brunet 	default:
221*1a11d88fSJerome Brunet 		dev_err(c->dev, "Unexpected event %d\n", event);
222*1a11d88fSJerome Brunet 		return -EINVAL;
223*1a11d88fSJerome Brunet 	}
224*1a11d88fSJerome Brunet 
225*1a11d88fSJerome Brunet 	return ret;
226*1a11d88fSJerome Brunet }
227*1a11d88fSJerome Brunet EXPORT_SYMBOL_GPL(axg_tdm_formatter_event);
228*1a11d88fSJerome Brunet 
229*1a11d88fSJerome Brunet int axg_tdm_formatter_probe(struct platform_device *pdev)
230*1a11d88fSJerome Brunet {
231*1a11d88fSJerome Brunet 	struct device *dev = &pdev->dev;
232*1a11d88fSJerome Brunet 	const struct axg_tdm_formatter_driver *drv;
233*1a11d88fSJerome Brunet 	struct axg_tdm_formatter *formatter;
234*1a11d88fSJerome Brunet 	struct resource *res;
235*1a11d88fSJerome Brunet 	void __iomem *regs;
236*1a11d88fSJerome Brunet 	int ret;
237*1a11d88fSJerome Brunet 
238*1a11d88fSJerome Brunet 	drv = of_device_get_match_data(dev);
239*1a11d88fSJerome Brunet 	if (!drv) {
240*1a11d88fSJerome Brunet 		dev_err(dev, "failed to match device\n");
241*1a11d88fSJerome Brunet 		return -ENODEV;
242*1a11d88fSJerome Brunet 	}
243*1a11d88fSJerome Brunet 
244*1a11d88fSJerome Brunet 	formatter = devm_kzalloc(dev, sizeof(*formatter), GFP_KERNEL);
245*1a11d88fSJerome Brunet 	if (!formatter)
246*1a11d88fSJerome Brunet 		return -ENOMEM;
247*1a11d88fSJerome Brunet 	platform_set_drvdata(pdev, formatter);
248*1a11d88fSJerome Brunet 	formatter->drv = drv;
249*1a11d88fSJerome Brunet 
250*1a11d88fSJerome Brunet 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
251*1a11d88fSJerome Brunet 	regs = devm_ioremap_resource(dev, res);
252*1a11d88fSJerome Brunet 	if (IS_ERR(regs))
253*1a11d88fSJerome Brunet 		return PTR_ERR(regs);
254*1a11d88fSJerome Brunet 
255*1a11d88fSJerome Brunet 	formatter->map = devm_regmap_init_mmio(dev, regs, drv->regmap_cfg);
256*1a11d88fSJerome Brunet 	if (IS_ERR(formatter->map)) {
257*1a11d88fSJerome Brunet 		dev_err(dev, "failed to init regmap: %ld\n",
258*1a11d88fSJerome Brunet 			PTR_ERR(formatter->map));
259*1a11d88fSJerome Brunet 		return PTR_ERR(formatter->map);
260*1a11d88fSJerome Brunet 	}
261*1a11d88fSJerome Brunet 
262*1a11d88fSJerome Brunet 	/* Peripharal clock */
263*1a11d88fSJerome Brunet 	formatter->pclk = devm_clk_get(dev, "pclk");
264*1a11d88fSJerome Brunet 	if (IS_ERR(formatter->pclk)) {
265*1a11d88fSJerome Brunet 		ret = PTR_ERR(formatter->pclk);
266*1a11d88fSJerome Brunet 		if (ret != -EPROBE_DEFER)
267*1a11d88fSJerome Brunet 			dev_err(dev, "failed to get pclk: %d\n", ret);
268*1a11d88fSJerome Brunet 		return ret;
269*1a11d88fSJerome Brunet 	}
270*1a11d88fSJerome Brunet 
271*1a11d88fSJerome Brunet 	/* Formatter bit clock */
272*1a11d88fSJerome Brunet 	formatter->sclk = devm_clk_get(dev, "sclk");
273*1a11d88fSJerome Brunet 	if (IS_ERR(formatter->sclk)) {
274*1a11d88fSJerome Brunet 		ret = PTR_ERR(formatter->sclk);
275*1a11d88fSJerome Brunet 		if (ret != -EPROBE_DEFER)
276*1a11d88fSJerome Brunet 			dev_err(dev, "failed to get sclk: %d\n", ret);
277*1a11d88fSJerome Brunet 		return ret;
278*1a11d88fSJerome Brunet 	}
279*1a11d88fSJerome Brunet 
280*1a11d88fSJerome Brunet 	/* Formatter sample clock */
281*1a11d88fSJerome Brunet 	formatter->lrclk = devm_clk_get(dev, "lrclk");
282*1a11d88fSJerome Brunet 	if (IS_ERR(formatter->lrclk)) {
283*1a11d88fSJerome Brunet 		ret = PTR_ERR(formatter->lrclk);
284*1a11d88fSJerome Brunet 		if (ret != -EPROBE_DEFER)
285*1a11d88fSJerome Brunet 			dev_err(dev, "failed to get lrclk: %d\n", ret);
286*1a11d88fSJerome Brunet 		return ret;
287*1a11d88fSJerome Brunet 	}
288*1a11d88fSJerome Brunet 
289*1a11d88fSJerome Brunet 	/* Formatter bit clock input multiplexer */
290*1a11d88fSJerome Brunet 	formatter->sclk_sel = devm_clk_get(dev, "sclk_sel");
291*1a11d88fSJerome Brunet 	if (IS_ERR(formatter->sclk_sel)) {
292*1a11d88fSJerome Brunet 		ret = PTR_ERR(formatter->sclk_sel);
293*1a11d88fSJerome Brunet 		if (ret != -EPROBE_DEFER)
294*1a11d88fSJerome Brunet 			dev_err(dev, "failed to get sclk_sel: %d\n", ret);
295*1a11d88fSJerome Brunet 		return ret;
296*1a11d88fSJerome Brunet 	}
297*1a11d88fSJerome Brunet 
298*1a11d88fSJerome Brunet 	/* Formatter sample clock input multiplexer */
299*1a11d88fSJerome Brunet 	formatter->lrclk_sel = devm_clk_get(dev, "lrclk_sel");
300*1a11d88fSJerome Brunet 	if (IS_ERR(formatter->lrclk_sel)) {
301*1a11d88fSJerome Brunet 		ret = PTR_ERR(formatter->lrclk_sel);
302*1a11d88fSJerome Brunet 		if (ret != -EPROBE_DEFER)
303*1a11d88fSJerome Brunet 			dev_err(dev, "failed to get lrclk_sel: %d\n", ret);
304*1a11d88fSJerome Brunet 		return ret;
305*1a11d88fSJerome Brunet 	}
306*1a11d88fSJerome Brunet 
307*1a11d88fSJerome Brunet 	return devm_snd_soc_register_component(dev, drv->component_drv,
308*1a11d88fSJerome Brunet 					       NULL, 0);
309*1a11d88fSJerome Brunet }
310*1a11d88fSJerome Brunet EXPORT_SYMBOL_GPL(axg_tdm_formatter_probe);
311*1a11d88fSJerome Brunet 
312*1a11d88fSJerome Brunet int axg_tdm_stream_start(struct axg_tdm_stream *ts)
313*1a11d88fSJerome Brunet {
314*1a11d88fSJerome Brunet 	struct axg_tdm_formatter *formatter;
315*1a11d88fSJerome Brunet 	int ret = 0;
316*1a11d88fSJerome Brunet 
317*1a11d88fSJerome Brunet 	mutex_lock(&ts->lock);
318*1a11d88fSJerome Brunet 	ts->ready = true;
319*1a11d88fSJerome Brunet 
320*1a11d88fSJerome Brunet 	/* Start all the formatters attached to the stream */
321*1a11d88fSJerome Brunet 	list_for_each_entry(formatter, &ts->formatter_list, list) {
322*1a11d88fSJerome Brunet 		ret = axg_tdm_formatter_enable(formatter);
323*1a11d88fSJerome Brunet 		if (ret) {
324*1a11d88fSJerome Brunet 			pr_err("failed to start tdm stream\n");
325*1a11d88fSJerome Brunet 			goto out;
326*1a11d88fSJerome Brunet 		}
327*1a11d88fSJerome Brunet 	}
328*1a11d88fSJerome Brunet 
329*1a11d88fSJerome Brunet out:
330*1a11d88fSJerome Brunet 	mutex_unlock(&ts->lock);
331*1a11d88fSJerome Brunet 	return ret;
332*1a11d88fSJerome Brunet }
333*1a11d88fSJerome Brunet EXPORT_SYMBOL_GPL(axg_tdm_stream_start);
334*1a11d88fSJerome Brunet 
335*1a11d88fSJerome Brunet void axg_tdm_stream_stop(struct axg_tdm_stream *ts)
336*1a11d88fSJerome Brunet {
337*1a11d88fSJerome Brunet 	struct axg_tdm_formatter *formatter;
338*1a11d88fSJerome Brunet 
339*1a11d88fSJerome Brunet 	mutex_lock(&ts->lock);
340*1a11d88fSJerome Brunet 	ts->ready = false;
341*1a11d88fSJerome Brunet 
342*1a11d88fSJerome Brunet 	/* Stop all the formatters attached to the stream */
343*1a11d88fSJerome Brunet 	list_for_each_entry(formatter, &ts->formatter_list, list) {
344*1a11d88fSJerome Brunet 		axg_tdm_formatter_disable(formatter);
345*1a11d88fSJerome Brunet 	}
346*1a11d88fSJerome Brunet 
347*1a11d88fSJerome Brunet 	mutex_unlock(&ts->lock);
348*1a11d88fSJerome Brunet }
349*1a11d88fSJerome Brunet EXPORT_SYMBOL_GPL(axg_tdm_stream_stop);
350*1a11d88fSJerome Brunet 
351*1a11d88fSJerome Brunet struct axg_tdm_stream *axg_tdm_stream_alloc(struct axg_tdm_iface *iface)
352*1a11d88fSJerome Brunet {
353*1a11d88fSJerome Brunet 	struct axg_tdm_stream *ts;
354*1a11d88fSJerome Brunet 
355*1a11d88fSJerome Brunet 	ts = kzalloc(sizeof(*ts), GFP_KERNEL);
356*1a11d88fSJerome Brunet 	if (ts) {
357*1a11d88fSJerome Brunet 		INIT_LIST_HEAD(&ts->formatter_list);
358*1a11d88fSJerome Brunet 		mutex_init(&ts->lock);
359*1a11d88fSJerome Brunet 		ts->iface = iface;
360*1a11d88fSJerome Brunet 	}
361*1a11d88fSJerome Brunet 
362*1a11d88fSJerome Brunet 	return ts;
363*1a11d88fSJerome Brunet }
364*1a11d88fSJerome Brunet EXPORT_SYMBOL_GPL(axg_tdm_stream_alloc);
365*1a11d88fSJerome Brunet 
366*1a11d88fSJerome Brunet void axg_tdm_stream_free(struct axg_tdm_stream *ts)
367*1a11d88fSJerome Brunet {
368*1a11d88fSJerome Brunet 	/*
369*1a11d88fSJerome Brunet 	 * If the list is not empty, it would mean that one of the formatter
370*1a11d88fSJerome Brunet 	 * widget is still powered and attached to the interface while we
371*1a11d88fSJerome Brunet 	 * we are removing the TDM DAI. It should not be possible
372*1a11d88fSJerome Brunet 	 */
373*1a11d88fSJerome Brunet 	WARN_ON(!list_empty(&ts->formatter_list));
374*1a11d88fSJerome Brunet 	mutex_destroy(&ts->lock);
375*1a11d88fSJerome Brunet 	kfree(ts);
376*1a11d88fSJerome Brunet }
377*1a11d88fSJerome Brunet EXPORT_SYMBOL_GPL(axg_tdm_stream_free);
378*1a11d88fSJerome Brunet 
379*1a11d88fSJerome Brunet MODULE_DESCRIPTION("Amlogic AXG TDM formatter driver");
380*1a11d88fSJerome Brunet MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
381*1a11d88fSJerome Brunet MODULE_LICENSE("GPL v2");
382