18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0 OR MIT)
28c2ecf20Sopenharmony_ci//
38c2ecf20Sopenharmony_ci// Copyright (c) 2018 BayLibre, SAS.
48c2ecf20Sopenharmony_ci// Author: Jerome Brunet <jbrunet@baylibre.com>
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/clk.h>
78c2ecf20Sopenharmony_ci#include <linux/module.h>
88c2ecf20Sopenharmony_ci#include <linux/of_platform.h>
98c2ecf20Sopenharmony_ci#include <linux/regmap.h>
108c2ecf20Sopenharmony_ci#include <linux/reset.h>
118c2ecf20Sopenharmony_ci#include <sound/soc.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include "axg-tdm-formatter.h"
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_cistruct axg_tdm_formatter {
168c2ecf20Sopenharmony_ci	struct list_head list;
178c2ecf20Sopenharmony_ci	struct axg_tdm_stream *stream;
188c2ecf20Sopenharmony_ci	const struct axg_tdm_formatter_driver *drv;
198c2ecf20Sopenharmony_ci	struct clk *pclk;
208c2ecf20Sopenharmony_ci	struct clk *sclk;
218c2ecf20Sopenharmony_ci	struct clk *lrclk;
228c2ecf20Sopenharmony_ci	struct clk *sclk_sel;
238c2ecf20Sopenharmony_ci	struct clk *lrclk_sel;
248c2ecf20Sopenharmony_ci	struct reset_control *reset;
258c2ecf20Sopenharmony_ci	bool enabled;
268c2ecf20Sopenharmony_ci	struct regmap *map;
278c2ecf20Sopenharmony_ci};
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ciint axg_tdm_formatter_set_channel_masks(struct regmap *map,
308c2ecf20Sopenharmony_ci					struct axg_tdm_stream *ts,
318c2ecf20Sopenharmony_ci					unsigned int offset)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	unsigned int ch = ts->channels;
348c2ecf20Sopenharmony_ci	u32 val[AXG_TDM_NUM_LANES];
358c2ecf20Sopenharmony_ci	int i, j, k;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	/*
388c2ecf20Sopenharmony_ci	 * We need to mimick the slot distribution used by the HW to keep the
398c2ecf20Sopenharmony_ci	 * channel placement consistent regardless of the number of channel
408c2ecf20Sopenharmony_ci	 * in the stream. This is why the odd algorithm below is used.
418c2ecf20Sopenharmony_ci	 */
428c2ecf20Sopenharmony_ci	memset(val, 0, sizeof(*val) * AXG_TDM_NUM_LANES);
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	/*
458c2ecf20Sopenharmony_ci	 * Distribute the channels of the stream over the available slots
468c2ecf20Sopenharmony_ci	 * of each TDM lane. We need to go over the 32 slots ...
478c2ecf20Sopenharmony_ci	 */
488c2ecf20Sopenharmony_ci	for (i = 0; (i < 32) && ch; i += 2) {
498c2ecf20Sopenharmony_ci		/* ... of all the lanes ... */
508c2ecf20Sopenharmony_ci		for (j = 0; j < AXG_TDM_NUM_LANES; j++) {
518c2ecf20Sopenharmony_ci			/* ... then distribute the channels in pairs */
528c2ecf20Sopenharmony_ci			for (k = 0; k < 2; k++) {
538c2ecf20Sopenharmony_ci				if ((BIT(i + k) & ts->mask[j]) && ch) {
548c2ecf20Sopenharmony_ci					val[j] |= BIT(i + k);
558c2ecf20Sopenharmony_ci					ch -= 1;
568c2ecf20Sopenharmony_ci				}
578c2ecf20Sopenharmony_ci			}
588c2ecf20Sopenharmony_ci		}
598c2ecf20Sopenharmony_ci	}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	/*
628c2ecf20Sopenharmony_ci	 * If we still have channel left at the end of the process, it means
638c2ecf20Sopenharmony_ci	 * the stream has more channels than we can accommodate and we should
648c2ecf20Sopenharmony_ci	 * have caught this earlier.
658c2ecf20Sopenharmony_ci	 */
668c2ecf20Sopenharmony_ci	if (WARN_ON(ch != 0)) {
678c2ecf20Sopenharmony_ci		pr_err("channel mask error\n");
688c2ecf20Sopenharmony_ci		return -EINVAL;
698c2ecf20Sopenharmony_ci	}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	for (i = 0; i < AXG_TDM_NUM_LANES; i++) {
728c2ecf20Sopenharmony_ci		regmap_write(map, offset, val[i]);
738c2ecf20Sopenharmony_ci		offset += regmap_get_reg_stride(map);
748c2ecf20Sopenharmony_ci	}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	return 0;
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(axg_tdm_formatter_set_channel_masks);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic int axg_tdm_formatter_enable(struct axg_tdm_formatter *formatter)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	struct axg_tdm_stream *ts = formatter->stream;
838c2ecf20Sopenharmony_ci	bool invert;
848c2ecf20Sopenharmony_ci	int ret;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	/* Do nothing if the formatter is already enabled */
878c2ecf20Sopenharmony_ci	if (formatter->enabled)
888c2ecf20Sopenharmony_ci		return 0;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	/*
918c2ecf20Sopenharmony_ci	 * On the g12a (and possibly other SoCs), when a stream using
928c2ecf20Sopenharmony_ci	 * multiple lanes is restarted, it will sometimes not start
938c2ecf20Sopenharmony_ci	 * from the first lane, but randomly from another used one.
948c2ecf20Sopenharmony_ci	 * The result is an unexpected and random channel shift.
958c2ecf20Sopenharmony_ci	 *
968c2ecf20Sopenharmony_ci	 * The hypothesis is that an HW counter is not properly reset
978c2ecf20Sopenharmony_ci	 * and the formatter simply starts on the lane it stopped
988c2ecf20Sopenharmony_ci	 * before. Unfortunately, there does not seems to be a way to
998c2ecf20Sopenharmony_ci	 * reset this through the registers of the block.
1008c2ecf20Sopenharmony_ci	 *
1018c2ecf20Sopenharmony_ci	 * However, the g12a has indenpendent reset lines for each audio
1028c2ecf20Sopenharmony_ci	 * devices. Using this reset before each start solves the issue.
1038c2ecf20Sopenharmony_ci	 */
1048c2ecf20Sopenharmony_ci	ret = reset_control_reset(formatter->reset);
1058c2ecf20Sopenharmony_ci	if (ret)
1068c2ecf20Sopenharmony_ci		return ret;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	/*
1098c2ecf20Sopenharmony_ci	 * If sclk is inverted, it means the bit should latched on the
1108c2ecf20Sopenharmony_ci	 * rising edge which is what our HW expects. If not, we need to
1118c2ecf20Sopenharmony_ci	 * invert it before the formatter.
1128c2ecf20Sopenharmony_ci	 */
1138c2ecf20Sopenharmony_ci	invert = axg_tdm_sclk_invert(ts->iface->fmt);
1148c2ecf20Sopenharmony_ci	ret = clk_set_phase(formatter->sclk, invert ? 0 : 180);
1158c2ecf20Sopenharmony_ci	if (ret)
1168c2ecf20Sopenharmony_ci		return ret;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	/* Setup the stream parameter in the formatter */
1198c2ecf20Sopenharmony_ci	ret = formatter->drv->ops->prepare(formatter->map,
1208c2ecf20Sopenharmony_ci					   formatter->drv->quirks,
1218c2ecf20Sopenharmony_ci					   formatter->stream);
1228c2ecf20Sopenharmony_ci	if (ret)
1238c2ecf20Sopenharmony_ci		return ret;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	/* Enable the signal clocks feeding the formatter */
1268c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(formatter->sclk);
1278c2ecf20Sopenharmony_ci	if (ret)
1288c2ecf20Sopenharmony_ci		return ret;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(formatter->lrclk);
1318c2ecf20Sopenharmony_ci	if (ret) {
1328c2ecf20Sopenharmony_ci		clk_disable_unprepare(formatter->sclk);
1338c2ecf20Sopenharmony_ci		return ret;
1348c2ecf20Sopenharmony_ci	}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	/* Finally, actually enable the formatter */
1378c2ecf20Sopenharmony_ci	formatter->drv->ops->enable(formatter->map);
1388c2ecf20Sopenharmony_ci	formatter->enabled = true;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	return 0;
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic void axg_tdm_formatter_disable(struct axg_tdm_formatter *formatter)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	/* Do nothing if the formatter is already disabled */
1468c2ecf20Sopenharmony_ci	if (!formatter->enabled)
1478c2ecf20Sopenharmony_ci		return;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	formatter->drv->ops->disable(formatter->map);
1508c2ecf20Sopenharmony_ci	clk_disable_unprepare(formatter->lrclk);
1518c2ecf20Sopenharmony_ci	clk_disable_unprepare(formatter->sclk);
1528c2ecf20Sopenharmony_ci	formatter->enabled = false;
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic int axg_tdm_formatter_attach(struct axg_tdm_formatter *formatter)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	struct axg_tdm_stream *ts = formatter->stream;
1588c2ecf20Sopenharmony_ci	int ret = 0;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	mutex_lock(&ts->lock);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	/* Catch up if the stream is already running when we attach */
1638c2ecf20Sopenharmony_ci	if (ts->ready) {
1648c2ecf20Sopenharmony_ci		ret = axg_tdm_formatter_enable(formatter);
1658c2ecf20Sopenharmony_ci		if (ret) {
1668c2ecf20Sopenharmony_ci			pr_err("failed to enable formatter\n");
1678c2ecf20Sopenharmony_ci			goto out;
1688c2ecf20Sopenharmony_ci		}
1698c2ecf20Sopenharmony_ci	}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	list_add_tail(&formatter->list, &ts->formatter_list);
1728c2ecf20Sopenharmony_ciout:
1738c2ecf20Sopenharmony_ci	mutex_unlock(&ts->lock);
1748c2ecf20Sopenharmony_ci	return ret;
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cistatic void axg_tdm_formatter_dettach(struct axg_tdm_formatter *formatter)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	struct axg_tdm_stream *ts = formatter->stream;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	mutex_lock(&ts->lock);
1828c2ecf20Sopenharmony_ci	list_del(&formatter->list);
1838c2ecf20Sopenharmony_ci	mutex_unlock(&ts->lock);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	axg_tdm_formatter_disable(formatter);
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic int axg_tdm_formatter_power_up(struct axg_tdm_formatter *formatter,
1898c2ecf20Sopenharmony_ci				      struct snd_soc_dapm_widget *w)
1908c2ecf20Sopenharmony_ci{
1918c2ecf20Sopenharmony_ci	struct axg_tdm_stream *ts = formatter->drv->ops->get_stream(w);
1928c2ecf20Sopenharmony_ci	int ret;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	/*
1958c2ecf20Sopenharmony_ci	 * If we don't get a stream at this stage, it would mean that the
1968c2ecf20Sopenharmony_ci	 * widget is powering up but is not attached to any backend DAI.
1978c2ecf20Sopenharmony_ci	 * It should not happen, ever !
1988c2ecf20Sopenharmony_ci	 */
1998c2ecf20Sopenharmony_ci	if (WARN_ON(!ts))
2008c2ecf20Sopenharmony_ci		return -ENODEV;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	/* Clock our device */
2038c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(formatter->pclk);
2048c2ecf20Sopenharmony_ci	if (ret)
2058c2ecf20Sopenharmony_ci		return ret;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	/* Reparent the bit clock to the TDM interface */
2088c2ecf20Sopenharmony_ci	ret = clk_set_parent(formatter->sclk_sel, ts->iface->sclk);
2098c2ecf20Sopenharmony_ci	if (ret)
2108c2ecf20Sopenharmony_ci		goto disable_pclk;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	/* Reparent the sample clock to the TDM interface */
2138c2ecf20Sopenharmony_ci	ret = clk_set_parent(formatter->lrclk_sel, ts->iface->lrclk);
2148c2ecf20Sopenharmony_ci	if (ret)
2158c2ecf20Sopenharmony_ci		goto disable_pclk;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	formatter->stream = ts;
2188c2ecf20Sopenharmony_ci	ret = axg_tdm_formatter_attach(formatter);
2198c2ecf20Sopenharmony_ci	if (ret)
2208c2ecf20Sopenharmony_ci		goto disable_pclk;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	return 0;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_cidisable_pclk:
2258c2ecf20Sopenharmony_ci	clk_disable_unprepare(formatter->pclk);
2268c2ecf20Sopenharmony_ci	return ret;
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic void axg_tdm_formatter_power_down(struct axg_tdm_formatter *formatter)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	axg_tdm_formatter_dettach(formatter);
2328c2ecf20Sopenharmony_ci	clk_disable_unprepare(formatter->pclk);
2338c2ecf20Sopenharmony_ci	formatter->stream = NULL;
2348c2ecf20Sopenharmony_ci}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ciint axg_tdm_formatter_event(struct snd_soc_dapm_widget *w,
2378c2ecf20Sopenharmony_ci			    struct snd_kcontrol *control,
2388c2ecf20Sopenharmony_ci			    int event)
2398c2ecf20Sopenharmony_ci{
2408c2ecf20Sopenharmony_ci	struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
2418c2ecf20Sopenharmony_ci	struct axg_tdm_formatter *formatter = snd_soc_component_get_drvdata(c);
2428c2ecf20Sopenharmony_ci	int ret = 0;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	switch (event) {
2458c2ecf20Sopenharmony_ci	case SND_SOC_DAPM_PRE_PMU:
2468c2ecf20Sopenharmony_ci		ret = axg_tdm_formatter_power_up(formatter, w);
2478c2ecf20Sopenharmony_ci		break;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	case SND_SOC_DAPM_PRE_PMD:
2508c2ecf20Sopenharmony_ci		axg_tdm_formatter_power_down(formatter);
2518c2ecf20Sopenharmony_ci		break;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	default:
2548c2ecf20Sopenharmony_ci		dev_err(c->dev, "Unexpected event %d\n", event);
2558c2ecf20Sopenharmony_ci		return -EINVAL;
2568c2ecf20Sopenharmony_ci	}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	return ret;
2598c2ecf20Sopenharmony_ci}
2608c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(axg_tdm_formatter_event);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ciint axg_tdm_formatter_probe(struct platform_device *pdev)
2638c2ecf20Sopenharmony_ci{
2648c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
2658c2ecf20Sopenharmony_ci	const struct axg_tdm_formatter_driver *drv;
2668c2ecf20Sopenharmony_ci	struct axg_tdm_formatter *formatter;
2678c2ecf20Sopenharmony_ci	void __iomem *regs;
2688c2ecf20Sopenharmony_ci	int ret;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	drv = of_device_get_match_data(dev);
2718c2ecf20Sopenharmony_ci	if (!drv) {
2728c2ecf20Sopenharmony_ci		dev_err(dev, "failed to match device\n");
2738c2ecf20Sopenharmony_ci		return -ENODEV;
2748c2ecf20Sopenharmony_ci	}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	formatter = devm_kzalloc(dev, sizeof(*formatter), GFP_KERNEL);
2778c2ecf20Sopenharmony_ci	if (!formatter)
2788c2ecf20Sopenharmony_ci		return -ENOMEM;
2798c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, formatter);
2808c2ecf20Sopenharmony_ci	formatter->drv = drv;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	regs = devm_platform_ioremap_resource(pdev, 0);
2838c2ecf20Sopenharmony_ci	if (IS_ERR(regs))
2848c2ecf20Sopenharmony_ci		return PTR_ERR(regs);
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	formatter->map = devm_regmap_init_mmio(dev, regs, drv->regmap_cfg);
2878c2ecf20Sopenharmony_ci	if (IS_ERR(formatter->map)) {
2888c2ecf20Sopenharmony_ci		dev_err(dev, "failed to init regmap: %ld\n",
2898c2ecf20Sopenharmony_ci			PTR_ERR(formatter->map));
2908c2ecf20Sopenharmony_ci		return PTR_ERR(formatter->map);
2918c2ecf20Sopenharmony_ci	}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	/* Peripharal clock */
2948c2ecf20Sopenharmony_ci	formatter->pclk = devm_clk_get(dev, "pclk");
2958c2ecf20Sopenharmony_ci	if (IS_ERR(formatter->pclk)) {
2968c2ecf20Sopenharmony_ci		ret = PTR_ERR(formatter->pclk);
2978c2ecf20Sopenharmony_ci		if (ret != -EPROBE_DEFER)
2988c2ecf20Sopenharmony_ci			dev_err(dev, "failed to get pclk: %d\n", ret);
2998c2ecf20Sopenharmony_ci		return ret;
3008c2ecf20Sopenharmony_ci	}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	/* Formatter bit clock */
3038c2ecf20Sopenharmony_ci	formatter->sclk = devm_clk_get(dev, "sclk");
3048c2ecf20Sopenharmony_ci	if (IS_ERR(formatter->sclk)) {
3058c2ecf20Sopenharmony_ci		ret = PTR_ERR(formatter->sclk);
3068c2ecf20Sopenharmony_ci		if (ret != -EPROBE_DEFER)
3078c2ecf20Sopenharmony_ci			dev_err(dev, "failed to get sclk: %d\n", ret);
3088c2ecf20Sopenharmony_ci		return ret;
3098c2ecf20Sopenharmony_ci	}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	/* Formatter sample clock */
3128c2ecf20Sopenharmony_ci	formatter->lrclk = devm_clk_get(dev, "lrclk");
3138c2ecf20Sopenharmony_ci	if (IS_ERR(formatter->lrclk)) {
3148c2ecf20Sopenharmony_ci		ret = PTR_ERR(formatter->lrclk);
3158c2ecf20Sopenharmony_ci		if (ret != -EPROBE_DEFER)
3168c2ecf20Sopenharmony_ci			dev_err(dev, "failed to get lrclk: %d\n", ret);
3178c2ecf20Sopenharmony_ci		return ret;
3188c2ecf20Sopenharmony_ci	}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	/* Formatter bit clock input multiplexer */
3218c2ecf20Sopenharmony_ci	formatter->sclk_sel = devm_clk_get(dev, "sclk_sel");
3228c2ecf20Sopenharmony_ci	if (IS_ERR(formatter->sclk_sel)) {
3238c2ecf20Sopenharmony_ci		ret = PTR_ERR(formatter->sclk_sel);
3248c2ecf20Sopenharmony_ci		if (ret != -EPROBE_DEFER)
3258c2ecf20Sopenharmony_ci			dev_err(dev, "failed to get sclk_sel: %d\n", ret);
3268c2ecf20Sopenharmony_ci		return ret;
3278c2ecf20Sopenharmony_ci	}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	/* Formatter sample clock input multiplexer */
3308c2ecf20Sopenharmony_ci	formatter->lrclk_sel = devm_clk_get(dev, "lrclk_sel");
3318c2ecf20Sopenharmony_ci	if (IS_ERR(formatter->lrclk_sel)) {
3328c2ecf20Sopenharmony_ci		ret = PTR_ERR(formatter->lrclk_sel);
3338c2ecf20Sopenharmony_ci		if (ret != -EPROBE_DEFER)
3348c2ecf20Sopenharmony_ci			dev_err(dev, "failed to get lrclk_sel: %d\n", ret);
3358c2ecf20Sopenharmony_ci		return ret;
3368c2ecf20Sopenharmony_ci	}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	/* Formatter dedicated reset line */
3398c2ecf20Sopenharmony_ci	formatter->reset = devm_reset_control_get_optional_exclusive(dev, NULL);
3408c2ecf20Sopenharmony_ci	if (IS_ERR(formatter->reset)) {
3418c2ecf20Sopenharmony_ci		ret = PTR_ERR(formatter->reset);
3428c2ecf20Sopenharmony_ci		if (ret != -EPROBE_DEFER)
3438c2ecf20Sopenharmony_ci			dev_err(dev, "failed to get reset: %d\n", ret);
3448c2ecf20Sopenharmony_ci		return ret;
3458c2ecf20Sopenharmony_ci	}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	return devm_snd_soc_register_component(dev, drv->component_drv,
3488c2ecf20Sopenharmony_ci					       NULL, 0);
3498c2ecf20Sopenharmony_ci}
3508c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(axg_tdm_formatter_probe);
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ciint axg_tdm_stream_start(struct axg_tdm_stream *ts)
3538c2ecf20Sopenharmony_ci{
3548c2ecf20Sopenharmony_ci	struct axg_tdm_formatter *formatter;
3558c2ecf20Sopenharmony_ci	int ret = 0;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	mutex_lock(&ts->lock);
3588c2ecf20Sopenharmony_ci	ts->ready = true;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	/* Start all the formatters attached to the stream */
3618c2ecf20Sopenharmony_ci	list_for_each_entry(formatter, &ts->formatter_list, list) {
3628c2ecf20Sopenharmony_ci		ret = axg_tdm_formatter_enable(formatter);
3638c2ecf20Sopenharmony_ci		if (ret) {
3648c2ecf20Sopenharmony_ci			pr_err("failed to start tdm stream\n");
3658c2ecf20Sopenharmony_ci			goto out;
3668c2ecf20Sopenharmony_ci		}
3678c2ecf20Sopenharmony_ci	}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ciout:
3708c2ecf20Sopenharmony_ci	mutex_unlock(&ts->lock);
3718c2ecf20Sopenharmony_ci	return ret;
3728c2ecf20Sopenharmony_ci}
3738c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(axg_tdm_stream_start);
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_civoid axg_tdm_stream_stop(struct axg_tdm_stream *ts)
3768c2ecf20Sopenharmony_ci{
3778c2ecf20Sopenharmony_ci	struct axg_tdm_formatter *formatter;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	mutex_lock(&ts->lock);
3808c2ecf20Sopenharmony_ci	ts->ready = false;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	/* Stop all the formatters attached to the stream */
3838c2ecf20Sopenharmony_ci	list_for_each_entry(formatter, &ts->formatter_list, list) {
3848c2ecf20Sopenharmony_ci		axg_tdm_formatter_disable(formatter);
3858c2ecf20Sopenharmony_ci	}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	mutex_unlock(&ts->lock);
3888c2ecf20Sopenharmony_ci}
3898c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(axg_tdm_stream_stop);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_cistruct axg_tdm_stream *axg_tdm_stream_alloc(struct axg_tdm_iface *iface)
3928c2ecf20Sopenharmony_ci{
3938c2ecf20Sopenharmony_ci	struct axg_tdm_stream *ts;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	ts = kzalloc(sizeof(*ts), GFP_KERNEL);
3968c2ecf20Sopenharmony_ci	if (ts) {
3978c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&ts->formatter_list);
3988c2ecf20Sopenharmony_ci		mutex_init(&ts->lock);
3998c2ecf20Sopenharmony_ci		ts->iface = iface;
4008c2ecf20Sopenharmony_ci	}
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	return ts;
4038c2ecf20Sopenharmony_ci}
4048c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(axg_tdm_stream_alloc);
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_civoid axg_tdm_stream_free(struct axg_tdm_stream *ts)
4078c2ecf20Sopenharmony_ci{
4088c2ecf20Sopenharmony_ci	/*
4098c2ecf20Sopenharmony_ci	 * If the list is not empty, it would mean that one of the formatter
4108c2ecf20Sopenharmony_ci	 * widget is still powered and attached to the interface while we
4118c2ecf20Sopenharmony_ci	 * are removing the TDM DAI. It should not be possible
4128c2ecf20Sopenharmony_ci	 */
4138c2ecf20Sopenharmony_ci	WARN_ON(!list_empty(&ts->formatter_list));
4148c2ecf20Sopenharmony_ci	mutex_destroy(&ts->lock);
4158c2ecf20Sopenharmony_ci	kfree(ts);
4168c2ecf20Sopenharmony_ci}
4178c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(axg_tdm_stream_free);
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Amlogic AXG TDM formatter driver");
4208c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
4218c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
422