11a11d88fSJerome Brunet // SPDX-License-Identifier: (GPL-2.0 OR MIT)
21a11d88fSJerome Brunet //
31a11d88fSJerome Brunet // Copyright (c) 2018 BayLibre, SAS.
41a11d88fSJerome Brunet // Author: Jerome Brunet <jbrunet@baylibre.com>
51a11d88fSJerome Brunet 
61a11d88fSJerome Brunet #include <linux/clk.h>
71a11d88fSJerome Brunet #include <linux/module.h>
81a11d88fSJerome Brunet #include <linux/of_platform.h>
91a11d88fSJerome Brunet #include <linux/regmap.h>
10751bd5dbSJerome Brunet #include <linux/reset.h>
111a11d88fSJerome Brunet #include <sound/soc.h>
121a11d88fSJerome Brunet 
131a11d88fSJerome Brunet #include "axg-tdm-formatter.h"
141a11d88fSJerome Brunet 
151a11d88fSJerome Brunet struct axg_tdm_formatter {
161a11d88fSJerome Brunet 	struct list_head list;
171a11d88fSJerome Brunet 	struct axg_tdm_stream *stream;
181a11d88fSJerome Brunet 	const struct axg_tdm_formatter_driver *drv;
191a11d88fSJerome Brunet 	struct clk *pclk;
201a11d88fSJerome Brunet 	struct clk *sclk;
211a11d88fSJerome Brunet 	struct clk *lrclk;
221a11d88fSJerome Brunet 	struct clk *sclk_sel;
231a11d88fSJerome Brunet 	struct clk *lrclk_sel;
24751bd5dbSJerome Brunet 	struct reset_control *reset;
251a11d88fSJerome Brunet 	bool enabled;
261a11d88fSJerome Brunet 	struct regmap *map;
271a11d88fSJerome Brunet };
281a11d88fSJerome Brunet 
axg_tdm_formatter_set_channel_masks(struct regmap * map,struct axg_tdm_stream * ts,unsigned int offset)291a11d88fSJerome Brunet int axg_tdm_formatter_set_channel_masks(struct regmap *map,
301a11d88fSJerome Brunet 					struct axg_tdm_stream *ts,
311a11d88fSJerome Brunet 					unsigned int offset)
321a11d88fSJerome Brunet {
33*c1f848f1SJerome Brunet 	unsigned int ch = ts->channels;
34*c1f848f1SJerome Brunet 	u32 val[AXG_TDM_NUM_LANES];
35*c1f848f1SJerome Brunet 	int i, j, k;
36*c1f848f1SJerome Brunet 
37*c1f848f1SJerome Brunet 	/*
38*c1f848f1SJerome Brunet 	 * We need to mimick the slot distribution used by the HW to keep the
39*c1f848f1SJerome Brunet 	 * channel placement consistent regardless of the number of channel
40*c1f848f1SJerome Brunet 	 * in the stream. This is why the odd algorithm below is used.
41*c1f848f1SJerome Brunet 	 */
42*c1f848f1SJerome Brunet 	memset(val, 0, sizeof(*val) * AXG_TDM_NUM_LANES);
431a11d88fSJerome Brunet 
441a11d88fSJerome Brunet 	/*
451a11d88fSJerome Brunet 	 * Distribute the channels of the stream over the available slots
46*c1f848f1SJerome Brunet 	 * of each TDM lane. We need to go over the 32 slots ...
471a11d88fSJerome Brunet 	 */
48*c1f848f1SJerome Brunet 	for (i = 0; (i < 32) && ch; i += 2) {
49*c1f848f1SJerome Brunet 		/* ... of all the lanes ... */
50*c1f848f1SJerome Brunet 		for (j = 0; j < AXG_TDM_NUM_LANES; j++) {
51*c1f848f1SJerome Brunet 			/* ... then distribute the channels in pairs */
52*c1f848f1SJerome Brunet 			for (k = 0; k < 2; k++) {
53*c1f848f1SJerome Brunet 				if ((BIT(i + k) & ts->mask[j]) && ch) {
54*c1f848f1SJerome Brunet 					val[j] |= BIT(i + k);
551a11d88fSJerome Brunet 					ch -= 1;
561a11d88fSJerome Brunet 				}
57*c1f848f1SJerome Brunet 			}
58*c1f848f1SJerome Brunet 		}
591a11d88fSJerome Brunet 	}
601a11d88fSJerome Brunet 
611a11d88fSJerome Brunet 	/*
621a11d88fSJerome Brunet 	 * If we still have channel left at the end of the process, it means
631a11d88fSJerome Brunet 	 * the stream has more channels than we can accommodate and we should
641a11d88fSJerome Brunet 	 * have caught this earlier.
651a11d88fSJerome Brunet 	 */
661a11d88fSJerome Brunet 	if (WARN_ON(ch != 0)) {
671a11d88fSJerome Brunet 		pr_err("channel mask error\n");
681a11d88fSJerome Brunet 		return -EINVAL;
691a11d88fSJerome Brunet 	}
701a11d88fSJerome Brunet 
71*c1f848f1SJerome Brunet 	for (i = 0; i < AXG_TDM_NUM_LANES; i++) {
72*c1f848f1SJerome Brunet 		regmap_write(map, offset, val[i]);
73*c1f848f1SJerome Brunet 		offset += regmap_get_reg_stride(map);
74*c1f848f1SJerome Brunet 	}
75*c1f848f1SJerome Brunet 
761a11d88fSJerome Brunet 	return 0;
771a11d88fSJerome Brunet }
781a11d88fSJerome Brunet EXPORT_SYMBOL_GPL(axg_tdm_formatter_set_channel_masks);
791a11d88fSJerome Brunet 
axg_tdm_formatter_enable(struct axg_tdm_formatter * formatter)801a11d88fSJerome Brunet static int axg_tdm_formatter_enable(struct axg_tdm_formatter *formatter)
811a11d88fSJerome Brunet {
821a11d88fSJerome Brunet 	struct axg_tdm_stream *ts = formatter->stream;
830d3f01dcSJerome Brunet 	bool invert;
841a11d88fSJerome Brunet 	int ret;
851a11d88fSJerome Brunet 
861a11d88fSJerome Brunet 	/* Do nothing if the formatter is already enabled */
871a11d88fSJerome Brunet 	if (formatter->enabled)
881a11d88fSJerome Brunet 		return 0;
891a11d88fSJerome Brunet 
901a11d88fSJerome Brunet 	/*
91751bd5dbSJerome Brunet 	 * On the g12a (and possibly other SoCs), when a stream using
92751bd5dbSJerome Brunet 	 * multiple lanes is restarted, it will sometimes not start
93751bd5dbSJerome Brunet 	 * from the first lane, but randomly from another used one.
94751bd5dbSJerome Brunet 	 * The result is an unexpected and random channel shift.
95751bd5dbSJerome Brunet 	 *
96751bd5dbSJerome Brunet 	 * The hypothesis is that an HW counter is not properly reset
97751bd5dbSJerome Brunet 	 * and the formatter simply starts on the lane it stopped
98751bd5dbSJerome Brunet 	 * before. Unfortunately, there does not seems to be a way to
99751bd5dbSJerome Brunet 	 * reset this through the registers of the block.
100751bd5dbSJerome Brunet 	 *
101751bd5dbSJerome Brunet 	 * However, the g12a has indenpendent reset lines for each audio
102751bd5dbSJerome Brunet 	 * devices. Using this reset before each start solves the issue.
103751bd5dbSJerome Brunet 	 */
104751bd5dbSJerome Brunet 	ret = reset_control_reset(formatter->reset);
105751bd5dbSJerome Brunet 	if (ret)
106751bd5dbSJerome Brunet 		return ret;
107751bd5dbSJerome Brunet 
108751bd5dbSJerome Brunet 	/*
1090d3f01dcSJerome Brunet 	 * If sclk is inverted, it means the bit should latched on the
1100d3f01dcSJerome Brunet 	 * rising edge which is what our HW expects. If not, we need to
1110d3f01dcSJerome Brunet 	 * invert it before the formatter.
1121a11d88fSJerome Brunet 	 */
1130d3f01dcSJerome Brunet 	invert = axg_tdm_sclk_invert(ts->iface->fmt);
1140d3f01dcSJerome Brunet 	ret = clk_set_phase(formatter->sclk, invert ? 0 : 180);
1151a11d88fSJerome Brunet 	if (ret)
1161a11d88fSJerome Brunet 		return ret;
1171a11d88fSJerome Brunet 
1181a11d88fSJerome Brunet 	/* Setup the stream parameter in the formatter */
119f01bc67fSJerome Brunet 	ret = formatter->drv->ops->prepare(formatter->map,
120f01bc67fSJerome Brunet 					   formatter->drv->quirks,
121f01bc67fSJerome Brunet 					   formatter->stream);
1221a11d88fSJerome Brunet 	if (ret)
1231a11d88fSJerome Brunet 		return ret;
1241a11d88fSJerome Brunet 
1251a11d88fSJerome Brunet 	/* Enable the signal clocks feeding the formatter */
1261a11d88fSJerome Brunet 	ret = clk_prepare_enable(formatter->sclk);
1271a11d88fSJerome Brunet 	if (ret)
1281a11d88fSJerome Brunet 		return ret;
1291a11d88fSJerome Brunet 
1301a11d88fSJerome Brunet 	ret = clk_prepare_enable(formatter->lrclk);
1311a11d88fSJerome Brunet 	if (ret) {
1321a11d88fSJerome Brunet 		clk_disable_unprepare(formatter->sclk);
1331a11d88fSJerome Brunet 		return ret;
1341a11d88fSJerome Brunet 	}
1351a11d88fSJerome Brunet 
1361a11d88fSJerome Brunet 	/* Finally, actually enable the formatter */
1371a11d88fSJerome Brunet 	formatter->drv->ops->enable(formatter->map);
1381a11d88fSJerome Brunet 	formatter->enabled = true;
1391a11d88fSJerome Brunet 
1401a11d88fSJerome Brunet 	return 0;
1411a11d88fSJerome Brunet }
1421a11d88fSJerome Brunet 
axg_tdm_formatter_disable(struct axg_tdm_formatter * formatter)1431a11d88fSJerome Brunet static void axg_tdm_formatter_disable(struct axg_tdm_formatter *formatter)
1441a11d88fSJerome Brunet {
1451a11d88fSJerome Brunet 	/* Do nothing if the formatter is already disabled */
1461a11d88fSJerome Brunet 	if (!formatter->enabled)
1471a11d88fSJerome Brunet 		return;
1481a11d88fSJerome Brunet 
1491a11d88fSJerome Brunet 	formatter->drv->ops->disable(formatter->map);
1501a11d88fSJerome Brunet 	clk_disable_unprepare(formatter->lrclk);
1511a11d88fSJerome Brunet 	clk_disable_unprepare(formatter->sclk);
1521a11d88fSJerome Brunet 	formatter->enabled = false;
1531a11d88fSJerome Brunet }
1541a11d88fSJerome Brunet 
axg_tdm_formatter_attach(struct axg_tdm_formatter * formatter)1551a11d88fSJerome Brunet static int axg_tdm_formatter_attach(struct axg_tdm_formatter *formatter)
1561a11d88fSJerome Brunet {
1571a11d88fSJerome Brunet 	struct axg_tdm_stream *ts = formatter->stream;
1581a11d88fSJerome Brunet 	int ret = 0;
1591a11d88fSJerome Brunet 
1601a11d88fSJerome Brunet 	mutex_lock(&ts->lock);
1611a11d88fSJerome Brunet 
1621a11d88fSJerome Brunet 	/* Catch up if the stream is already running when we attach */
1631a11d88fSJerome Brunet 	if (ts->ready) {
1641a11d88fSJerome Brunet 		ret = axg_tdm_formatter_enable(formatter);
1651a11d88fSJerome Brunet 		if (ret) {
1661a11d88fSJerome Brunet 			pr_err("failed to enable formatter\n");
1671a11d88fSJerome Brunet 			goto out;
1681a11d88fSJerome Brunet 		}
1691a11d88fSJerome Brunet 	}
1701a11d88fSJerome Brunet 
1711a11d88fSJerome Brunet 	list_add_tail(&formatter->list, &ts->formatter_list);
1721a11d88fSJerome Brunet out:
1731a11d88fSJerome Brunet 	mutex_unlock(&ts->lock);
1741a11d88fSJerome Brunet 	return ret;
1751a11d88fSJerome Brunet }
1761a11d88fSJerome Brunet 
axg_tdm_formatter_dettach(struct axg_tdm_formatter * formatter)1771a11d88fSJerome Brunet static void axg_tdm_formatter_dettach(struct axg_tdm_formatter *formatter)
1781a11d88fSJerome Brunet {
1791a11d88fSJerome Brunet 	struct axg_tdm_stream *ts = formatter->stream;
1801a11d88fSJerome Brunet 
1811a11d88fSJerome Brunet 	mutex_lock(&ts->lock);
1821a11d88fSJerome Brunet 	list_del(&formatter->list);
1831a11d88fSJerome Brunet 	mutex_unlock(&ts->lock);
1841a11d88fSJerome Brunet 
1851a11d88fSJerome Brunet 	axg_tdm_formatter_disable(formatter);
1861a11d88fSJerome Brunet }
1871a11d88fSJerome Brunet 
axg_tdm_formatter_power_up(struct axg_tdm_formatter * formatter,struct snd_soc_dapm_widget * w)1881a11d88fSJerome Brunet static int axg_tdm_formatter_power_up(struct axg_tdm_formatter *formatter,
1891a11d88fSJerome Brunet 				      struct snd_soc_dapm_widget *w)
1901a11d88fSJerome Brunet {
1911a11d88fSJerome Brunet 	struct axg_tdm_stream *ts = formatter->drv->ops->get_stream(w);
1921a11d88fSJerome Brunet 	int ret;
1931a11d88fSJerome Brunet 
1941a11d88fSJerome Brunet 	/*
1951a11d88fSJerome Brunet 	 * If we don't get a stream at this stage, it would mean that the
1961a11d88fSJerome Brunet 	 * widget is powering up but is not attached to any backend DAI.
1971a11d88fSJerome Brunet 	 * It should not happen, ever !
1981a11d88fSJerome Brunet 	 */
1991a11d88fSJerome Brunet 	if (WARN_ON(!ts))
2001a11d88fSJerome Brunet 		return -ENODEV;
2011a11d88fSJerome Brunet 
2021a11d88fSJerome Brunet 	/* Clock our device */
2031a11d88fSJerome Brunet 	ret = clk_prepare_enable(formatter->pclk);
2041a11d88fSJerome Brunet 	if (ret)
2051a11d88fSJerome Brunet 		return ret;
2061a11d88fSJerome Brunet 
2071a11d88fSJerome Brunet 	/* Reparent the bit clock to the TDM interface */
2081a11d88fSJerome Brunet 	ret = clk_set_parent(formatter->sclk_sel, ts->iface->sclk);
2091a11d88fSJerome Brunet 	if (ret)
2101a11d88fSJerome Brunet 		goto disable_pclk;
2111a11d88fSJerome Brunet 
2121a11d88fSJerome Brunet 	/* Reparent the sample clock to the TDM interface */
2131a11d88fSJerome Brunet 	ret = clk_set_parent(formatter->lrclk_sel, ts->iface->lrclk);
2141a11d88fSJerome Brunet 	if (ret)
2151a11d88fSJerome Brunet 		goto disable_pclk;
2161a11d88fSJerome Brunet 
2171a11d88fSJerome Brunet 	formatter->stream = ts;
2181a11d88fSJerome Brunet 	ret = axg_tdm_formatter_attach(formatter);
2191a11d88fSJerome Brunet 	if (ret)
2201a11d88fSJerome Brunet 		goto disable_pclk;
2211a11d88fSJerome Brunet 
2221a11d88fSJerome Brunet 	return 0;
2231a11d88fSJerome Brunet 
2241a11d88fSJerome Brunet disable_pclk:
2251a11d88fSJerome Brunet 	clk_disable_unprepare(formatter->pclk);
2261a11d88fSJerome Brunet 	return ret;
2271a11d88fSJerome Brunet }
2281a11d88fSJerome Brunet 
axg_tdm_formatter_power_down(struct axg_tdm_formatter * formatter)2291a11d88fSJerome Brunet static void axg_tdm_formatter_power_down(struct axg_tdm_formatter *formatter)
2301a11d88fSJerome Brunet {
2311a11d88fSJerome Brunet 	axg_tdm_formatter_dettach(formatter);
2321a11d88fSJerome Brunet 	clk_disable_unprepare(formatter->pclk);
2331a11d88fSJerome Brunet 	formatter->stream = NULL;
2341a11d88fSJerome Brunet }
2351a11d88fSJerome Brunet 
axg_tdm_formatter_event(struct snd_soc_dapm_widget * w,struct snd_kcontrol * control,int event)2361a11d88fSJerome Brunet int axg_tdm_formatter_event(struct snd_soc_dapm_widget *w,
2371a11d88fSJerome Brunet 			    struct snd_kcontrol *control,
2381a11d88fSJerome Brunet 			    int event)
2391a11d88fSJerome Brunet {
2401a11d88fSJerome Brunet 	struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
2411a11d88fSJerome Brunet 	struct axg_tdm_formatter *formatter = snd_soc_component_get_drvdata(c);
2421a11d88fSJerome Brunet 	int ret = 0;
2431a11d88fSJerome Brunet 
2441a11d88fSJerome Brunet 	switch (event) {
2451a11d88fSJerome Brunet 	case SND_SOC_DAPM_PRE_PMU:
2461a11d88fSJerome Brunet 		ret = axg_tdm_formatter_power_up(formatter, w);
2471a11d88fSJerome Brunet 		break;
2481a11d88fSJerome Brunet 
2491a11d88fSJerome Brunet 	case SND_SOC_DAPM_PRE_PMD:
2501a11d88fSJerome Brunet 		axg_tdm_formatter_power_down(formatter);
2511a11d88fSJerome Brunet 		break;
2521a11d88fSJerome Brunet 
2531a11d88fSJerome Brunet 	default:
2541a11d88fSJerome Brunet 		dev_err(c->dev, "Unexpected event %d\n", event);
2551a11d88fSJerome Brunet 		return -EINVAL;
2561a11d88fSJerome Brunet 	}
2571a11d88fSJerome Brunet 
2581a11d88fSJerome Brunet 	return ret;
2591a11d88fSJerome Brunet }
2601a11d88fSJerome Brunet EXPORT_SYMBOL_GPL(axg_tdm_formatter_event);
2611a11d88fSJerome Brunet 
axg_tdm_formatter_probe(struct platform_device * pdev)2621a11d88fSJerome Brunet int axg_tdm_formatter_probe(struct platform_device *pdev)
2631a11d88fSJerome Brunet {
2641a11d88fSJerome Brunet 	struct device *dev = &pdev->dev;
2651a11d88fSJerome Brunet 	const struct axg_tdm_formatter_driver *drv;
2661a11d88fSJerome Brunet 	struct axg_tdm_formatter *formatter;
2671a11d88fSJerome Brunet 	void __iomem *regs;
2681a11d88fSJerome Brunet 
2691a11d88fSJerome Brunet 	drv = of_device_get_match_data(dev);
2701a11d88fSJerome Brunet 	if (!drv) {
2711a11d88fSJerome Brunet 		dev_err(dev, "failed to match device\n");
2721a11d88fSJerome Brunet 		return -ENODEV;
2731a11d88fSJerome Brunet 	}
2741a11d88fSJerome Brunet 
2751a11d88fSJerome Brunet 	formatter = devm_kzalloc(dev, sizeof(*formatter), GFP_KERNEL);
2761a11d88fSJerome Brunet 	if (!formatter)
2771a11d88fSJerome Brunet 		return -ENOMEM;
2781a11d88fSJerome Brunet 	platform_set_drvdata(pdev, formatter);
2791a11d88fSJerome Brunet 	formatter->drv = drv;
2801a11d88fSJerome Brunet 
28110392fcaSYueHaibing 	regs = devm_platform_ioremap_resource(pdev, 0);
2821a11d88fSJerome Brunet 	if (IS_ERR(regs))
2831a11d88fSJerome Brunet 		return PTR_ERR(regs);
2841a11d88fSJerome Brunet 
2851a11d88fSJerome Brunet 	formatter->map = devm_regmap_init_mmio(dev, regs, drv->regmap_cfg);
2861a11d88fSJerome Brunet 	if (IS_ERR(formatter->map)) {
2871a11d88fSJerome Brunet 		dev_err(dev, "failed to init regmap: %ld\n",
2881a11d88fSJerome Brunet 			PTR_ERR(formatter->map));
2891a11d88fSJerome Brunet 		return PTR_ERR(formatter->map);
2901a11d88fSJerome Brunet 	}
2911a11d88fSJerome Brunet 
2921a11d88fSJerome Brunet 	/* Peripharal clock */
2931a11d88fSJerome Brunet 	formatter->pclk = devm_clk_get(dev, "pclk");
2942ff4e003SKuninori Morimoto 	if (IS_ERR(formatter->pclk))
2952ff4e003SKuninori Morimoto 		return dev_err_probe(dev, PTR_ERR(formatter->pclk), "failed to get pclk\n");
2961a11d88fSJerome Brunet 
2971a11d88fSJerome Brunet 	/* Formatter bit clock */
2981a11d88fSJerome Brunet 	formatter->sclk = devm_clk_get(dev, "sclk");
2992ff4e003SKuninori Morimoto 	if (IS_ERR(formatter->sclk))
3002ff4e003SKuninori Morimoto 		return dev_err_probe(dev, PTR_ERR(formatter->sclk), "failed to get sclk\n");
3011a11d88fSJerome Brunet 
3021a11d88fSJerome Brunet 	/* Formatter sample clock */
3031a11d88fSJerome Brunet 	formatter->lrclk = devm_clk_get(dev, "lrclk");
3042ff4e003SKuninori Morimoto 	if (IS_ERR(formatter->lrclk))
3052ff4e003SKuninori Morimoto 		return dev_err_probe(dev, PTR_ERR(formatter->lrclk), "failed to get lrclk\n");
3061a11d88fSJerome Brunet 
3071a11d88fSJerome Brunet 	/* Formatter bit clock input multiplexer */
3081a11d88fSJerome Brunet 	formatter->sclk_sel = devm_clk_get(dev, "sclk_sel");
3092ff4e003SKuninori Morimoto 	if (IS_ERR(formatter->sclk_sel))
3102ff4e003SKuninori Morimoto 		return dev_err_probe(dev, PTR_ERR(formatter->sclk_sel), "failed to get sclk_sel\n");
3111a11d88fSJerome Brunet 
3121a11d88fSJerome Brunet 	/* Formatter sample clock input multiplexer */
3131a11d88fSJerome Brunet 	formatter->lrclk_sel = devm_clk_get(dev, "lrclk_sel");
3142ff4e003SKuninori Morimoto 	if (IS_ERR(formatter->lrclk_sel))
3152ff4e003SKuninori Morimoto 		return dev_err_probe(dev, PTR_ERR(formatter->lrclk_sel),
3162ff4e003SKuninori Morimoto 				     "failed to get lrclk_sel\n");
3171a11d88fSJerome Brunet 
318751bd5dbSJerome Brunet 	/* Formatter dedicated reset line */
31918dd62aeSJerome Brunet 	formatter->reset = devm_reset_control_get_optional_exclusive(dev, NULL);
3202ff4e003SKuninori Morimoto 	if (IS_ERR(formatter->reset))
3212ff4e003SKuninori Morimoto 		return dev_err_probe(dev, PTR_ERR(formatter->reset), "failed to get reset\n");
322751bd5dbSJerome Brunet 
3231a11d88fSJerome Brunet 	return devm_snd_soc_register_component(dev, drv->component_drv,
3241a11d88fSJerome Brunet 					       NULL, 0);
3251a11d88fSJerome Brunet }
3261a11d88fSJerome Brunet EXPORT_SYMBOL_GPL(axg_tdm_formatter_probe);
3271a11d88fSJerome Brunet 
axg_tdm_stream_start(struct axg_tdm_stream * ts)3281a11d88fSJerome Brunet int axg_tdm_stream_start(struct axg_tdm_stream *ts)
3291a11d88fSJerome Brunet {
3301a11d88fSJerome Brunet 	struct axg_tdm_formatter *formatter;
3311a11d88fSJerome Brunet 	int ret = 0;
3321a11d88fSJerome Brunet 
3331a11d88fSJerome Brunet 	mutex_lock(&ts->lock);
3341a11d88fSJerome Brunet 	ts->ready = true;
3351a11d88fSJerome Brunet 
3361a11d88fSJerome Brunet 	/* Start all the formatters attached to the stream */
3371a11d88fSJerome Brunet 	list_for_each_entry(formatter, &ts->formatter_list, list) {
3381a11d88fSJerome Brunet 		ret = axg_tdm_formatter_enable(formatter);
3391a11d88fSJerome Brunet 		if (ret) {
3401a11d88fSJerome Brunet 			pr_err("failed to start tdm stream\n");
3411a11d88fSJerome Brunet 			goto out;
3421a11d88fSJerome Brunet 		}
3431a11d88fSJerome Brunet 	}
3441a11d88fSJerome Brunet 
3451a11d88fSJerome Brunet out:
3461a11d88fSJerome Brunet 	mutex_unlock(&ts->lock);
3471a11d88fSJerome Brunet 	return ret;
3481a11d88fSJerome Brunet }
3491a11d88fSJerome Brunet EXPORT_SYMBOL_GPL(axg_tdm_stream_start);
3501a11d88fSJerome Brunet 
axg_tdm_stream_stop(struct axg_tdm_stream * ts)3511a11d88fSJerome Brunet void axg_tdm_stream_stop(struct axg_tdm_stream *ts)
3521a11d88fSJerome Brunet {
3531a11d88fSJerome Brunet 	struct axg_tdm_formatter *formatter;
3541a11d88fSJerome Brunet 
3551a11d88fSJerome Brunet 	mutex_lock(&ts->lock);
3561a11d88fSJerome Brunet 	ts->ready = false;
3571a11d88fSJerome Brunet 
3581a11d88fSJerome Brunet 	/* Stop all the formatters attached to the stream */
3591a11d88fSJerome Brunet 	list_for_each_entry(formatter, &ts->formatter_list, list) {
3601a11d88fSJerome Brunet 		axg_tdm_formatter_disable(formatter);
3611a11d88fSJerome Brunet 	}
3621a11d88fSJerome Brunet 
3631a11d88fSJerome Brunet 	mutex_unlock(&ts->lock);
3641a11d88fSJerome Brunet }
3651a11d88fSJerome Brunet EXPORT_SYMBOL_GPL(axg_tdm_stream_stop);
3661a11d88fSJerome Brunet 
axg_tdm_stream_alloc(struct axg_tdm_iface * iface)3671a11d88fSJerome Brunet struct axg_tdm_stream *axg_tdm_stream_alloc(struct axg_tdm_iface *iface)
3681a11d88fSJerome Brunet {
3691a11d88fSJerome Brunet 	struct axg_tdm_stream *ts;
3701a11d88fSJerome Brunet 
3711a11d88fSJerome Brunet 	ts = kzalloc(sizeof(*ts), GFP_KERNEL);
3721a11d88fSJerome Brunet 	if (ts) {
3731a11d88fSJerome Brunet 		INIT_LIST_HEAD(&ts->formatter_list);
3741a11d88fSJerome Brunet 		mutex_init(&ts->lock);
3751a11d88fSJerome Brunet 		ts->iface = iface;
3761a11d88fSJerome Brunet 	}
3771a11d88fSJerome Brunet 
3781a11d88fSJerome Brunet 	return ts;
3791a11d88fSJerome Brunet }
3801a11d88fSJerome Brunet EXPORT_SYMBOL_GPL(axg_tdm_stream_alloc);
3811a11d88fSJerome Brunet 
axg_tdm_stream_free(struct axg_tdm_stream * ts)3821a11d88fSJerome Brunet void axg_tdm_stream_free(struct axg_tdm_stream *ts)
3831a11d88fSJerome Brunet {
3841a11d88fSJerome Brunet 	/*
3851a11d88fSJerome Brunet 	 * If the list is not empty, it would mean that one of the formatter
3861a11d88fSJerome Brunet 	 * widget is still powered and attached to the interface while we
3870d8aa2ccSRandy Dunlap 	 * are removing the TDM DAI. It should not be possible
3881a11d88fSJerome Brunet 	 */
3891a11d88fSJerome Brunet 	WARN_ON(!list_empty(&ts->formatter_list));
3901a11d88fSJerome Brunet 	mutex_destroy(&ts->lock);
3911a11d88fSJerome Brunet 	kfree(ts);
3921a11d88fSJerome Brunet }
3931a11d88fSJerome Brunet EXPORT_SYMBOL_GPL(axg_tdm_stream_free);
3941a11d88fSJerome Brunet 
3951a11d88fSJerome Brunet MODULE_DESCRIPTION("Amlogic AXG TDM formatter driver");
3961a11d88fSJerome Brunet MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
3971a11d88fSJerome Brunet MODULE_LICENSE("GPL v2");
398