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