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/module.h>
78c2ecf20Sopenharmony_ci#include <linux/of_platform.h>
88c2ecf20Sopenharmony_ci#include <linux/regmap.h>
98c2ecf20Sopenharmony_ci#include <sound/soc.h>
108c2ecf20Sopenharmony_ci#include <sound/soc-dai.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include "axg-tdm-formatter.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#define TDMIN_CTRL			0x00
158c2ecf20Sopenharmony_ci#define  TDMIN_CTRL_ENABLE		BIT(31)
168c2ecf20Sopenharmony_ci#define  TDMIN_CTRL_I2S_MODE		BIT(30)
178c2ecf20Sopenharmony_ci#define  TDMIN_CTRL_RST_OUT		BIT(29)
188c2ecf20Sopenharmony_ci#define  TDMIN_CTRL_RST_IN		BIT(28)
198c2ecf20Sopenharmony_ci#define  TDMIN_CTRL_WS_INV		BIT(25)
208c2ecf20Sopenharmony_ci#define  TDMIN_CTRL_SEL_SHIFT		20
218c2ecf20Sopenharmony_ci#define  TDMIN_CTRL_IN_BIT_SKEW_MASK	GENMASK(18, 16)
228c2ecf20Sopenharmony_ci#define  TDMIN_CTRL_IN_BIT_SKEW(x)	((x) << 16)
238c2ecf20Sopenharmony_ci#define  TDMIN_CTRL_LSB_FIRST		BIT(5)
248c2ecf20Sopenharmony_ci#define  TDMIN_CTRL_BITNUM_MASK	GENMASK(4, 0)
258c2ecf20Sopenharmony_ci#define  TDMIN_CTRL_BITNUM(x)		((x) << 0)
268c2ecf20Sopenharmony_ci#define TDMIN_SWAP			0x04
278c2ecf20Sopenharmony_ci#define TDMIN_MASK0			0x08
288c2ecf20Sopenharmony_ci#define TDMIN_MASK1			0x0c
298c2ecf20Sopenharmony_ci#define TDMIN_MASK2			0x10
308c2ecf20Sopenharmony_ci#define TDMIN_MASK3			0x14
318c2ecf20Sopenharmony_ci#define TDMIN_STAT			0x18
328c2ecf20Sopenharmony_ci#define TDMIN_MUTE_VAL			0x1c
338c2ecf20Sopenharmony_ci#define TDMIN_MUTE0			0x20
348c2ecf20Sopenharmony_ci#define TDMIN_MUTE1			0x24
358c2ecf20Sopenharmony_ci#define TDMIN_MUTE2			0x28
368c2ecf20Sopenharmony_ci#define TDMIN_MUTE3			0x2c
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic const struct regmap_config axg_tdmin_regmap_cfg = {
398c2ecf20Sopenharmony_ci	.reg_bits	= 32,
408c2ecf20Sopenharmony_ci	.val_bits	= 32,
418c2ecf20Sopenharmony_ci	.reg_stride	= 4,
428c2ecf20Sopenharmony_ci	.max_register	= TDMIN_MUTE3,
438c2ecf20Sopenharmony_ci};
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic const char * const axg_tdmin_sel_texts[] = {
468c2ecf20Sopenharmony_ci	"IN 0", "IN 1", "IN 2",  "IN 3",  "IN 4",  "IN 5",  "IN 6",  "IN 7",
478c2ecf20Sopenharmony_ci	"IN 8", "IN 9", "IN 10", "IN 11", "IN 12", "IN 13", "IN 14", "IN 15",
488c2ecf20Sopenharmony_ci};
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci/* Change to special mux control to reset dapm */
518c2ecf20Sopenharmony_cistatic SOC_ENUM_SINGLE_DECL(axg_tdmin_sel_enum, TDMIN_CTRL,
528c2ecf20Sopenharmony_ci			    TDMIN_CTRL_SEL_SHIFT, axg_tdmin_sel_texts);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new axg_tdmin_in_mux =
558c2ecf20Sopenharmony_ci	SOC_DAPM_ENUM("Input Source", axg_tdmin_sel_enum);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic struct snd_soc_dai *
588c2ecf20Sopenharmony_ciaxg_tdmin_get_be(struct snd_soc_dapm_widget *w)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	struct snd_soc_dapm_path *p = NULL;
618c2ecf20Sopenharmony_ci	struct snd_soc_dai *be;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	snd_soc_dapm_widget_for_each_source_path(w, p) {
648c2ecf20Sopenharmony_ci		if (!p->connect)
658c2ecf20Sopenharmony_ci			continue;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci		if (p->source->id == snd_soc_dapm_dai_out)
688c2ecf20Sopenharmony_ci			return (struct snd_soc_dai *)p->source->priv;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci		be = axg_tdmin_get_be(p->source);
718c2ecf20Sopenharmony_ci		if (be)
728c2ecf20Sopenharmony_ci			return be;
738c2ecf20Sopenharmony_ci	}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	return NULL;
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistatic struct axg_tdm_stream *
798c2ecf20Sopenharmony_ciaxg_tdmin_get_tdm_stream(struct snd_soc_dapm_widget *w)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	struct snd_soc_dai *be = axg_tdmin_get_be(w);
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	if (!be)
848c2ecf20Sopenharmony_ci		return NULL;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	return be->capture_dma_data;
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic void axg_tdmin_enable(struct regmap *map)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	/* Apply both reset */
928c2ecf20Sopenharmony_ci	regmap_update_bits(map, TDMIN_CTRL,
938c2ecf20Sopenharmony_ci			   TDMIN_CTRL_RST_OUT | TDMIN_CTRL_RST_IN, 0);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	/* Clear out reset before in reset */
968c2ecf20Sopenharmony_ci	regmap_update_bits(map, TDMIN_CTRL,
978c2ecf20Sopenharmony_ci			   TDMIN_CTRL_RST_OUT, TDMIN_CTRL_RST_OUT);
988c2ecf20Sopenharmony_ci	regmap_update_bits(map, TDMIN_CTRL,
998c2ecf20Sopenharmony_ci			   TDMIN_CTRL_RST_IN,  TDMIN_CTRL_RST_IN);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	/* Actually enable tdmin */
1028c2ecf20Sopenharmony_ci	regmap_update_bits(map, TDMIN_CTRL,
1038c2ecf20Sopenharmony_ci			   TDMIN_CTRL_ENABLE, TDMIN_CTRL_ENABLE);
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic void axg_tdmin_disable(struct regmap *map)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	regmap_update_bits(map, TDMIN_CTRL, TDMIN_CTRL_ENABLE, 0);
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic int axg_tdmin_prepare(struct regmap *map,
1128c2ecf20Sopenharmony_ci			     const struct axg_tdm_formatter_hw *quirks,
1138c2ecf20Sopenharmony_ci			     struct axg_tdm_stream *ts)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	unsigned int val, skew = quirks->skew_offset;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	/* Set stream skew */
1188c2ecf20Sopenharmony_ci	switch (ts->iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
1198c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_I2S:
1208c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_DSP_A:
1218c2ecf20Sopenharmony_ci		skew += 1;
1228c2ecf20Sopenharmony_ci		break;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_LEFT_J:
1258c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_DSP_B:
1268c2ecf20Sopenharmony_ci		break;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	default:
1298c2ecf20Sopenharmony_ci		pr_err("Unsupported format: %u\n",
1308c2ecf20Sopenharmony_ci		       ts->iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK);
1318c2ecf20Sopenharmony_ci		return -EINVAL;
1328c2ecf20Sopenharmony_ci	}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	val = TDMIN_CTRL_IN_BIT_SKEW(skew);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	/* Set stream format mode */
1378c2ecf20Sopenharmony_ci	switch (ts->iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
1388c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_I2S:
1398c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_LEFT_J:
1408c2ecf20Sopenharmony_ci	case SND_SOC_DAIFMT_RIGHT_J:
1418c2ecf20Sopenharmony_ci		val |= TDMIN_CTRL_I2S_MODE;
1428c2ecf20Sopenharmony_ci		break;
1438c2ecf20Sopenharmony_ci	}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	/* If the sample clock is inverted, invert it back for the formatter */
1468c2ecf20Sopenharmony_ci	if (axg_tdm_lrclk_invert(ts->iface->fmt))
1478c2ecf20Sopenharmony_ci		val |= TDMIN_CTRL_WS_INV;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	/* Set the slot width */
1508c2ecf20Sopenharmony_ci	val |= TDMIN_CTRL_BITNUM(ts->iface->slot_width - 1);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	/*
1538c2ecf20Sopenharmony_ci	 * The following also reset LSB_FIRST which result in the formatter
1548c2ecf20Sopenharmony_ci	 * placing the first bit received at bit 31
1558c2ecf20Sopenharmony_ci	 */
1568c2ecf20Sopenharmony_ci	regmap_update_bits(map, TDMIN_CTRL,
1578c2ecf20Sopenharmony_ci			   (TDMIN_CTRL_IN_BIT_SKEW_MASK | TDMIN_CTRL_WS_INV |
1588c2ecf20Sopenharmony_ci			    TDMIN_CTRL_I2S_MODE | TDMIN_CTRL_LSB_FIRST |
1598c2ecf20Sopenharmony_ci			    TDMIN_CTRL_BITNUM_MASK), val);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	/* Set static swap mask configuration */
1628c2ecf20Sopenharmony_ci	regmap_write(map, TDMIN_SWAP, 0x76543210);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	return axg_tdm_formatter_set_channel_masks(map, ts, TDMIN_MASK0);
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_widget axg_tdmin_dapm_widgets[] = {
1688c2ecf20Sopenharmony_ci	SND_SOC_DAPM_AIF_IN("IN 0",  NULL, 0, SND_SOC_NOPM, 0, 0),
1698c2ecf20Sopenharmony_ci	SND_SOC_DAPM_AIF_IN("IN 1",  NULL, 0, SND_SOC_NOPM, 0, 0),
1708c2ecf20Sopenharmony_ci	SND_SOC_DAPM_AIF_IN("IN 2",  NULL, 0, SND_SOC_NOPM, 0, 0),
1718c2ecf20Sopenharmony_ci	SND_SOC_DAPM_AIF_IN("IN 3",  NULL, 0, SND_SOC_NOPM, 0, 0),
1728c2ecf20Sopenharmony_ci	SND_SOC_DAPM_AIF_IN("IN 4",  NULL, 0, SND_SOC_NOPM, 0, 0),
1738c2ecf20Sopenharmony_ci	SND_SOC_DAPM_AIF_IN("IN 5",  NULL, 0, SND_SOC_NOPM, 0, 0),
1748c2ecf20Sopenharmony_ci	SND_SOC_DAPM_AIF_IN("IN 6",  NULL, 0, SND_SOC_NOPM, 0, 0),
1758c2ecf20Sopenharmony_ci	SND_SOC_DAPM_AIF_IN("IN 7",  NULL, 0, SND_SOC_NOPM, 0, 0),
1768c2ecf20Sopenharmony_ci	SND_SOC_DAPM_AIF_IN("IN 8",  NULL, 0, SND_SOC_NOPM, 0, 0),
1778c2ecf20Sopenharmony_ci	SND_SOC_DAPM_AIF_IN("IN 9",  NULL, 0, SND_SOC_NOPM, 0, 0),
1788c2ecf20Sopenharmony_ci	SND_SOC_DAPM_AIF_IN("IN 10", NULL, 0, SND_SOC_NOPM, 0, 0),
1798c2ecf20Sopenharmony_ci	SND_SOC_DAPM_AIF_IN("IN 11", NULL, 0, SND_SOC_NOPM, 0, 0),
1808c2ecf20Sopenharmony_ci	SND_SOC_DAPM_AIF_IN("IN 12", NULL, 0, SND_SOC_NOPM, 0, 0),
1818c2ecf20Sopenharmony_ci	SND_SOC_DAPM_AIF_IN("IN 13", NULL, 0, SND_SOC_NOPM, 0, 0),
1828c2ecf20Sopenharmony_ci	SND_SOC_DAPM_AIF_IN("IN 14", NULL, 0, SND_SOC_NOPM, 0, 0),
1838c2ecf20Sopenharmony_ci	SND_SOC_DAPM_AIF_IN("IN 15", NULL, 0, SND_SOC_NOPM, 0, 0),
1848c2ecf20Sopenharmony_ci	SND_SOC_DAPM_MUX("SRC SEL", SND_SOC_NOPM, 0, 0, &axg_tdmin_in_mux),
1858c2ecf20Sopenharmony_ci	SND_SOC_DAPM_PGA_E("DEC", SND_SOC_NOPM, 0, 0, NULL, 0,
1868c2ecf20Sopenharmony_ci			   axg_tdm_formatter_event,
1878c2ecf20Sopenharmony_ci			   (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD)),
1888c2ecf20Sopenharmony_ci	SND_SOC_DAPM_AIF_OUT("OUT", NULL, 0, SND_SOC_NOPM, 0, 0),
1898c2ecf20Sopenharmony_ci};
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_route axg_tdmin_dapm_routes[] = {
1928c2ecf20Sopenharmony_ci	{ "SRC SEL", "IN 0",  "IN 0" },
1938c2ecf20Sopenharmony_ci	{ "SRC SEL", "IN 1",  "IN 1" },
1948c2ecf20Sopenharmony_ci	{ "SRC SEL", "IN 2",  "IN 2" },
1958c2ecf20Sopenharmony_ci	{ "SRC SEL", "IN 3",  "IN 3" },
1968c2ecf20Sopenharmony_ci	{ "SRC SEL", "IN 4",  "IN 4" },
1978c2ecf20Sopenharmony_ci	{ "SRC SEL", "IN 5",  "IN 5" },
1988c2ecf20Sopenharmony_ci	{ "SRC SEL", "IN 6",  "IN 6" },
1998c2ecf20Sopenharmony_ci	{ "SRC SEL", "IN 7",  "IN 7" },
2008c2ecf20Sopenharmony_ci	{ "SRC SEL", "IN 8",  "IN 8" },
2018c2ecf20Sopenharmony_ci	{ "SRC SEL", "IN 9",  "IN 9" },
2028c2ecf20Sopenharmony_ci	{ "SRC SEL", "IN 10", "IN 10" },
2038c2ecf20Sopenharmony_ci	{ "SRC SEL", "IN 11", "IN 11" },
2048c2ecf20Sopenharmony_ci	{ "SRC SEL", "IN 12", "IN 12" },
2058c2ecf20Sopenharmony_ci	{ "SRC SEL", "IN 13", "IN 13" },
2068c2ecf20Sopenharmony_ci	{ "SRC SEL", "IN 14", "IN 14" },
2078c2ecf20Sopenharmony_ci	{ "SRC SEL", "IN 15", "IN 15" },
2088c2ecf20Sopenharmony_ci	{ "DEC", NULL, "SRC SEL" },
2098c2ecf20Sopenharmony_ci	{ "OUT", NULL, "DEC" },
2108c2ecf20Sopenharmony_ci};
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver axg_tdmin_component_drv = {
2138c2ecf20Sopenharmony_ci	.dapm_widgets		= axg_tdmin_dapm_widgets,
2148c2ecf20Sopenharmony_ci	.num_dapm_widgets	= ARRAY_SIZE(axg_tdmin_dapm_widgets),
2158c2ecf20Sopenharmony_ci	.dapm_routes		= axg_tdmin_dapm_routes,
2168c2ecf20Sopenharmony_ci	.num_dapm_routes	= ARRAY_SIZE(axg_tdmin_dapm_routes),
2178c2ecf20Sopenharmony_ci};
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_cistatic const struct axg_tdm_formatter_ops axg_tdmin_ops = {
2208c2ecf20Sopenharmony_ci	.get_stream	= axg_tdmin_get_tdm_stream,
2218c2ecf20Sopenharmony_ci	.prepare	= axg_tdmin_prepare,
2228c2ecf20Sopenharmony_ci	.enable		= axg_tdmin_enable,
2238c2ecf20Sopenharmony_ci	.disable	= axg_tdmin_disable,
2248c2ecf20Sopenharmony_ci};
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_cistatic const struct axg_tdm_formatter_driver axg_tdmin_drv = {
2278c2ecf20Sopenharmony_ci	.component_drv	= &axg_tdmin_component_drv,
2288c2ecf20Sopenharmony_ci	.regmap_cfg	= &axg_tdmin_regmap_cfg,
2298c2ecf20Sopenharmony_ci	.ops		= &axg_tdmin_ops,
2308c2ecf20Sopenharmony_ci	.quirks		= &(const struct axg_tdm_formatter_hw) {
2318c2ecf20Sopenharmony_ci		.skew_offset	= 3,
2328c2ecf20Sopenharmony_ci	},
2338c2ecf20Sopenharmony_ci};
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_cistatic const struct of_device_id axg_tdmin_of_match[] = {
2368c2ecf20Sopenharmony_ci	{
2378c2ecf20Sopenharmony_ci		.compatible = "amlogic,axg-tdmin",
2388c2ecf20Sopenharmony_ci		.data = &axg_tdmin_drv,
2398c2ecf20Sopenharmony_ci	}, {
2408c2ecf20Sopenharmony_ci		.compatible = "amlogic,g12a-tdmin",
2418c2ecf20Sopenharmony_ci		.data = &axg_tdmin_drv,
2428c2ecf20Sopenharmony_ci	}, {
2438c2ecf20Sopenharmony_ci		.compatible = "amlogic,sm1-tdmin",
2448c2ecf20Sopenharmony_ci		.data = &axg_tdmin_drv,
2458c2ecf20Sopenharmony_ci	}, {}
2468c2ecf20Sopenharmony_ci};
2478c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, axg_tdmin_of_match);
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cistatic struct platform_driver axg_tdmin_pdrv = {
2508c2ecf20Sopenharmony_ci	.probe = axg_tdm_formatter_probe,
2518c2ecf20Sopenharmony_ci	.driver = {
2528c2ecf20Sopenharmony_ci		.name = "axg-tdmin",
2538c2ecf20Sopenharmony_ci		.of_match_table = axg_tdmin_of_match,
2548c2ecf20Sopenharmony_ci	},
2558c2ecf20Sopenharmony_ci};
2568c2ecf20Sopenharmony_cimodule_platform_driver(axg_tdmin_pdrv);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Amlogic AXG TDM input formatter driver");
2598c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
2608c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
261