xref: /openbmc/linux/sound/soc/meson/axg-tdmin.c (revision c765ceda)
113a22e6aSJerome Brunet // SPDX-License-Identifier: (GPL-2.0 OR MIT)
213a22e6aSJerome Brunet //
313a22e6aSJerome Brunet // Copyright (c) 2018 BayLibre, SAS.
413a22e6aSJerome Brunet // Author: Jerome Brunet <jbrunet@baylibre.com>
513a22e6aSJerome Brunet 
613a22e6aSJerome Brunet #include <linux/module.h>
713a22e6aSJerome Brunet #include <linux/of_platform.h>
813a22e6aSJerome Brunet #include <linux/regmap.h>
913a22e6aSJerome Brunet #include <sound/soc.h>
1013a22e6aSJerome Brunet #include <sound/soc-dai.h>
1113a22e6aSJerome Brunet 
1213a22e6aSJerome Brunet #include "axg-tdm-formatter.h"
1313a22e6aSJerome Brunet 
1413a22e6aSJerome Brunet #define TDMIN_CTRL			0x00
1513a22e6aSJerome Brunet #define  TDMIN_CTRL_ENABLE		BIT(31)
1613a22e6aSJerome Brunet #define  TDMIN_CTRL_I2S_MODE		BIT(30)
1713a22e6aSJerome Brunet #define  TDMIN_CTRL_RST_OUT		BIT(29)
1813a22e6aSJerome Brunet #define  TDMIN_CTRL_RST_IN		BIT(28)
1913a22e6aSJerome Brunet #define  TDMIN_CTRL_WS_INV		BIT(25)
2013a22e6aSJerome Brunet #define  TDMIN_CTRL_SEL_SHIFT		20
2113a22e6aSJerome Brunet #define  TDMIN_CTRL_IN_BIT_SKEW_MASK	GENMASK(18, 16)
2213a22e6aSJerome Brunet #define  TDMIN_CTRL_IN_BIT_SKEW(x)	((x) << 16)
2313a22e6aSJerome Brunet #define  TDMIN_CTRL_LSB_FIRST		BIT(5)
2413a22e6aSJerome Brunet #define  TDMIN_CTRL_BITNUM_MASK	GENMASK(4, 0)
2513a22e6aSJerome Brunet #define  TDMIN_CTRL_BITNUM(x)		((x) << 0)
2613a22e6aSJerome Brunet #define TDMIN_SWAP			0x04
2713a22e6aSJerome Brunet #define TDMIN_MASK0			0x08
2813a22e6aSJerome Brunet #define TDMIN_MASK1			0x0c
2913a22e6aSJerome Brunet #define TDMIN_MASK2			0x10
3013a22e6aSJerome Brunet #define TDMIN_MASK3			0x14
3113a22e6aSJerome Brunet #define TDMIN_STAT			0x18
3213a22e6aSJerome Brunet #define TDMIN_MUTE_VAL			0x1c
3313a22e6aSJerome Brunet #define TDMIN_MUTE0			0x20
3413a22e6aSJerome Brunet #define TDMIN_MUTE1			0x24
3513a22e6aSJerome Brunet #define TDMIN_MUTE2			0x28
3613a22e6aSJerome Brunet #define TDMIN_MUTE3			0x2c
3713a22e6aSJerome Brunet 
3813a22e6aSJerome Brunet static const struct regmap_config axg_tdmin_regmap_cfg = {
3913a22e6aSJerome Brunet 	.reg_bits	= 32,
4013a22e6aSJerome Brunet 	.val_bits	= 32,
4113a22e6aSJerome Brunet 	.reg_stride	= 4,
4213a22e6aSJerome Brunet 	.max_register	= TDMIN_MUTE3,
4313a22e6aSJerome Brunet };
4413a22e6aSJerome Brunet 
4513a22e6aSJerome Brunet static const char * const axg_tdmin_sel_texts[] = {
46cde9f12eSJerome Brunet 	"IN 0", "IN 1", "IN 2",  "IN 3",  "IN 4",  "IN 5",  "IN 6",  "IN 7",
47cde9f12eSJerome Brunet 	"IN 8", "IN 9", "IN 10", "IN 11", "IN 12", "IN 13", "IN 14", "IN 15",
4813a22e6aSJerome Brunet };
4913a22e6aSJerome Brunet 
5013a22e6aSJerome Brunet /* Change to special mux control to reset dapm */
5113a22e6aSJerome Brunet static SOC_ENUM_SINGLE_DECL(axg_tdmin_sel_enum, TDMIN_CTRL,
5213a22e6aSJerome Brunet 			    TDMIN_CTRL_SEL_SHIFT, axg_tdmin_sel_texts);
5313a22e6aSJerome Brunet 
5413a22e6aSJerome Brunet static const struct snd_kcontrol_new axg_tdmin_in_mux =
5513a22e6aSJerome Brunet 	SOC_DAPM_ENUM("Input Source", axg_tdmin_sel_enum);
5613a22e6aSJerome Brunet 
5713a22e6aSJerome Brunet static struct snd_soc_dai *
axg_tdmin_get_be(struct snd_soc_dapm_widget * w)5813a22e6aSJerome Brunet axg_tdmin_get_be(struct snd_soc_dapm_widget *w)
5913a22e6aSJerome Brunet {
6084d0493fSPierre-Louis Bossart 	struct snd_soc_dapm_path *p;
6113a22e6aSJerome Brunet 	struct snd_soc_dai *be;
6213a22e6aSJerome Brunet 
6313a22e6aSJerome Brunet 	snd_soc_dapm_widget_for_each_source_path(w, p) {
6413a22e6aSJerome Brunet 		if (!p->connect)
6513a22e6aSJerome Brunet 			continue;
6613a22e6aSJerome Brunet 
6713a22e6aSJerome Brunet 		if (p->source->id == snd_soc_dapm_dai_out)
6813a22e6aSJerome Brunet 			return (struct snd_soc_dai *)p->source->priv;
6913a22e6aSJerome Brunet 
7013a22e6aSJerome Brunet 		be = axg_tdmin_get_be(p->source);
7113a22e6aSJerome Brunet 		if (be)
7213a22e6aSJerome Brunet 			return be;
7313a22e6aSJerome Brunet 	}
7413a22e6aSJerome Brunet 
7513a22e6aSJerome Brunet 	return NULL;
7613a22e6aSJerome Brunet }
7713a22e6aSJerome Brunet 
7813a22e6aSJerome Brunet static struct axg_tdm_stream *
axg_tdmin_get_tdm_stream(struct snd_soc_dapm_widget * w)7913a22e6aSJerome Brunet axg_tdmin_get_tdm_stream(struct snd_soc_dapm_widget *w)
8013a22e6aSJerome Brunet {
8113a22e6aSJerome Brunet 	struct snd_soc_dai *be = axg_tdmin_get_be(w);
8213a22e6aSJerome Brunet 
8313a22e6aSJerome Brunet 	if (!be)
8413a22e6aSJerome Brunet 		return NULL;
8513a22e6aSJerome Brunet 
86*c765cedaSKuninori Morimoto 	return snd_soc_dai_dma_data_get_capture(be);
8713a22e6aSJerome Brunet }
8813a22e6aSJerome Brunet 
axg_tdmin_enable(struct regmap * map)8913a22e6aSJerome Brunet static void axg_tdmin_enable(struct regmap *map)
9013a22e6aSJerome Brunet {
9113a22e6aSJerome Brunet 	/* Apply both reset */
9213a22e6aSJerome Brunet 	regmap_update_bits(map, TDMIN_CTRL,
9313a22e6aSJerome Brunet 			   TDMIN_CTRL_RST_OUT | TDMIN_CTRL_RST_IN, 0);
9413a22e6aSJerome Brunet 
9513a22e6aSJerome Brunet 	/* Clear out reset before in reset */
9613a22e6aSJerome Brunet 	regmap_update_bits(map, TDMIN_CTRL,
9713a22e6aSJerome Brunet 			   TDMIN_CTRL_RST_OUT, TDMIN_CTRL_RST_OUT);
9813a22e6aSJerome Brunet 	regmap_update_bits(map, TDMIN_CTRL,
9913a22e6aSJerome Brunet 			   TDMIN_CTRL_RST_IN,  TDMIN_CTRL_RST_IN);
10013a22e6aSJerome Brunet 
10113a22e6aSJerome Brunet 	/* Actually enable tdmin */
10213a22e6aSJerome Brunet 	regmap_update_bits(map, TDMIN_CTRL,
10313a22e6aSJerome Brunet 			   TDMIN_CTRL_ENABLE, TDMIN_CTRL_ENABLE);
10413a22e6aSJerome Brunet }
10513a22e6aSJerome Brunet 
axg_tdmin_disable(struct regmap * map)10613a22e6aSJerome Brunet static void axg_tdmin_disable(struct regmap *map)
10713a22e6aSJerome Brunet {
10813a22e6aSJerome Brunet 	regmap_update_bits(map, TDMIN_CTRL, TDMIN_CTRL_ENABLE, 0);
10913a22e6aSJerome Brunet }
11013a22e6aSJerome Brunet 
axg_tdmin_prepare(struct regmap * map,const struct axg_tdm_formatter_hw * quirks,struct axg_tdm_stream * ts)111f01bc67fSJerome Brunet static int axg_tdmin_prepare(struct regmap *map,
112f01bc67fSJerome Brunet 			     const struct axg_tdm_formatter_hw *quirks,
113f01bc67fSJerome Brunet 			     struct axg_tdm_stream *ts)
11413a22e6aSJerome Brunet {
115f01bc67fSJerome Brunet 	unsigned int val, skew = quirks->skew_offset;
11613a22e6aSJerome Brunet 
11713a22e6aSJerome Brunet 	/* Set stream skew */
11813a22e6aSJerome Brunet 	switch (ts->iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
11913a22e6aSJerome Brunet 	case SND_SOC_DAIFMT_I2S:
12013a22e6aSJerome Brunet 	case SND_SOC_DAIFMT_DSP_A:
121f01bc67fSJerome Brunet 		skew += 1;
12213a22e6aSJerome Brunet 		break;
12313a22e6aSJerome Brunet 
12413a22e6aSJerome Brunet 	case SND_SOC_DAIFMT_LEFT_J:
12513a22e6aSJerome Brunet 	case SND_SOC_DAIFMT_DSP_B:
12613a22e6aSJerome Brunet 		break;
12713a22e6aSJerome Brunet 
12813a22e6aSJerome Brunet 	default:
12913a22e6aSJerome Brunet 		pr_err("Unsupported format: %u\n",
13013a22e6aSJerome Brunet 		       ts->iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK);
13113a22e6aSJerome Brunet 		return -EINVAL;
13213a22e6aSJerome Brunet 	}
13313a22e6aSJerome Brunet 
134f01bc67fSJerome Brunet 	val = TDMIN_CTRL_IN_BIT_SKEW(skew);
135f01bc67fSJerome Brunet 
13613a22e6aSJerome Brunet 	/* Set stream format mode */
13713a22e6aSJerome Brunet 	switch (ts->iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
13813a22e6aSJerome Brunet 	case SND_SOC_DAIFMT_I2S:
13913a22e6aSJerome Brunet 	case SND_SOC_DAIFMT_LEFT_J:
14013a22e6aSJerome Brunet 	case SND_SOC_DAIFMT_RIGHT_J:
14113a22e6aSJerome Brunet 		val |= TDMIN_CTRL_I2S_MODE;
14213a22e6aSJerome Brunet 		break;
14313a22e6aSJerome Brunet 	}
14413a22e6aSJerome Brunet 
14513a22e6aSJerome Brunet 	/* If the sample clock is inverted, invert it back for the formatter */
14613a22e6aSJerome Brunet 	if (axg_tdm_lrclk_invert(ts->iface->fmt))
14713a22e6aSJerome Brunet 		val |= TDMIN_CTRL_WS_INV;
14813a22e6aSJerome Brunet 
14913a22e6aSJerome Brunet 	/* Set the slot width */
15013a22e6aSJerome Brunet 	val |= TDMIN_CTRL_BITNUM(ts->iface->slot_width - 1);
15113a22e6aSJerome Brunet 
15213a22e6aSJerome Brunet 	/*
15313a22e6aSJerome Brunet 	 * The following also reset LSB_FIRST which result in the formatter
15413a22e6aSJerome Brunet 	 * placing the first bit received at bit 31
15513a22e6aSJerome Brunet 	 */
15613a22e6aSJerome Brunet 	regmap_update_bits(map, TDMIN_CTRL,
15713a22e6aSJerome Brunet 			   (TDMIN_CTRL_IN_BIT_SKEW_MASK | TDMIN_CTRL_WS_INV |
15813a22e6aSJerome Brunet 			    TDMIN_CTRL_I2S_MODE | TDMIN_CTRL_LSB_FIRST |
15913a22e6aSJerome Brunet 			    TDMIN_CTRL_BITNUM_MASK), val);
16013a22e6aSJerome Brunet 
16113a22e6aSJerome Brunet 	/* Set static swap mask configuration */
16213a22e6aSJerome Brunet 	regmap_write(map, TDMIN_SWAP, 0x76543210);
16313a22e6aSJerome Brunet 
16413a22e6aSJerome Brunet 	return axg_tdm_formatter_set_channel_masks(map, ts, TDMIN_MASK0);
16513a22e6aSJerome Brunet }
16613a22e6aSJerome Brunet 
16713a22e6aSJerome Brunet static const struct snd_soc_dapm_widget axg_tdmin_dapm_widgets[] = {
16813a22e6aSJerome Brunet 	SND_SOC_DAPM_AIF_IN("IN 0",  NULL, 0, SND_SOC_NOPM, 0, 0),
16913a22e6aSJerome Brunet 	SND_SOC_DAPM_AIF_IN("IN 1",  NULL, 0, SND_SOC_NOPM, 0, 0),
17013a22e6aSJerome Brunet 	SND_SOC_DAPM_AIF_IN("IN 2",  NULL, 0, SND_SOC_NOPM, 0, 0),
17113a22e6aSJerome Brunet 	SND_SOC_DAPM_AIF_IN("IN 3",  NULL, 0, SND_SOC_NOPM, 0, 0),
17213a22e6aSJerome Brunet 	SND_SOC_DAPM_AIF_IN("IN 4",  NULL, 0, SND_SOC_NOPM, 0, 0),
17313a22e6aSJerome Brunet 	SND_SOC_DAPM_AIF_IN("IN 5",  NULL, 0, SND_SOC_NOPM, 0, 0),
174cde9f12eSJerome Brunet 	SND_SOC_DAPM_AIF_IN("IN 6",  NULL, 0, SND_SOC_NOPM, 0, 0),
175cde9f12eSJerome Brunet 	SND_SOC_DAPM_AIF_IN("IN 7",  NULL, 0, SND_SOC_NOPM, 0, 0),
176cde9f12eSJerome Brunet 	SND_SOC_DAPM_AIF_IN("IN 8",  NULL, 0, SND_SOC_NOPM, 0, 0),
177cde9f12eSJerome Brunet 	SND_SOC_DAPM_AIF_IN("IN 9",  NULL, 0, SND_SOC_NOPM, 0, 0),
178cde9f12eSJerome Brunet 	SND_SOC_DAPM_AIF_IN("IN 10", NULL, 0, SND_SOC_NOPM, 0, 0),
179cde9f12eSJerome Brunet 	SND_SOC_DAPM_AIF_IN("IN 11", NULL, 0, SND_SOC_NOPM, 0, 0),
180cde9f12eSJerome Brunet 	SND_SOC_DAPM_AIF_IN("IN 12", NULL, 0, SND_SOC_NOPM, 0, 0),
181cde9f12eSJerome Brunet 	SND_SOC_DAPM_AIF_IN("IN 13", NULL, 0, SND_SOC_NOPM, 0, 0),
182cde9f12eSJerome Brunet 	SND_SOC_DAPM_AIF_IN("IN 14", NULL, 0, SND_SOC_NOPM, 0, 0),
183cde9f12eSJerome Brunet 	SND_SOC_DAPM_AIF_IN("IN 15", NULL, 0, SND_SOC_NOPM, 0, 0),
18413a22e6aSJerome Brunet 	SND_SOC_DAPM_MUX("SRC SEL", SND_SOC_NOPM, 0, 0, &axg_tdmin_in_mux),
18513a22e6aSJerome Brunet 	SND_SOC_DAPM_PGA_E("DEC", SND_SOC_NOPM, 0, 0, NULL, 0,
18613a22e6aSJerome Brunet 			   axg_tdm_formatter_event,
18713a22e6aSJerome Brunet 			   (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD)),
18813a22e6aSJerome Brunet 	SND_SOC_DAPM_AIF_OUT("OUT", NULL, 0, SND_SOC_NOPM, 0, 0),
18913a22e6aSJerome Brunet };
19013a22e6aSJerome Brunet 
19113a22e6aSJerome Brunet static const struct snd_soc_dapm_route axg_tdmin_dapm_routes[] = {
19213a22e6aSJerome Brunet 	{ "SRC SEL", "IN 0",  "IN 0" },
19313a22e6aSJerome Brunet 	{ "SRC SEL", "IN 1",  "IN 1" },
19413a22e6aSJerome Brunet 	{ "SRC SEL", "IN 2",  "IN 2" },
19513a22e6aSJerome Brunet 	{ "SRC SEL", "IN 3",  "IN 3" },
19613a22e6aSJerome Brunet 	{ "SRC SEL", "IN 4",  "IN 4" },
19713a22e6aSJerome Brunet 	{ "SRC SEL", "IN 5",  "IN 5" },
198cde9f12eSJerome Brunet 	{ "SRC SEL", "IN 6",  "IN 6" },
199cde9f12eSJerome Brunet 	{ "SRC SEL", "IN 7",  "IN 7" },
200cde9f12eSJerome Brunet 	{ "SRC SEL", "IN 8",  "IN 8" },
201cde9f12eSJerome Brunet 	{ "SRC SEL", "IN 9",  "IN 9" },
202cde9f12eSJerome Brunet 	{ "SRC SEL", "IN 10", "IN 10" },
203cde9f12eSJerome Brunet 	{ "SRC SEL", "IN 11", "IN 11" },
204cde9f12eSJerome Brunet 	{ "SRC SEL", "IN 12", "IN 12" },
205cde9f12eSJerome Brunet 	{ "SRC SEL", "IN 13", "IN 13" },
206cde9f12eSJerome Brunet 	{ "SRC SEL", "IN 14", "IN 14" },
207cde9f12eSJerome Brunet 	{ "SRC SEL", "IN 15", "IN 15" },
20813a22e6aSJerome Brunet 	{ "DEC", NULL, "SRC SEL" },
20913a22e6aSJerome Brunet 	{ "OUT", NULL, "DEC" },
21013a22e6aSJerome Brunet };
21113a22e6aSJerome Brunet 
21213a22e6aSJerome Brunet static const struct snd_soc_component_driver axg_tdmin_component_drv = {
21313a22e6aSJerome Brunet 	.dapm_widgets		= axg_tdmin_dapm_widgets,
21413a22e6aSJerome Brunet 	.num_dapm_widgets	= ARRAY_SIZE(axg_tdmin_dapm_widgets),
21513a22e6aSJerome Brunet 	.dapm_routes		= axg_tdmin_dapm_routes,
21613a22e6aSJerome Brunet 	.num_dapm_routes	= ARRAY_SIZE(axg_tdmin_dapm_routes),
21713a22e6aSJerome Brunet };
21813a22e6aSJerome Brunet 
21913a22e6aSJerome Brunet static const struct axg_tdm_formatter_ops axg_tdmin_ops = {
22013a22e6aSJerome Brunet 	.get_stream	= axg_tdmin_get_tdm_stream,
22113a22e6aSJerome Brunet 	.prepare	= axg_tdmin_prepare,
22213a22e6aSJerome Brunet 	.enable		= axg_tdmin_enable,
22313a22e6aSJerome Brunet 	.disable	= axg_tdmin_disable,
22413a22e6aSJerome Brunet };
22513a22e6aSJerome Brunet 
22613a22e6aSJerome Brunet static const struct axg_tdm_formatter_driver axg_tdmin_drv = {
22713a22e6aSJerome Brunet 	.component_drv	= &axg_tdmin_component_drv,
22813a22e6aSJerome Brunet 	.regmap_cfg	= &axg_tdmin_regmap_cfg,
22913a22e6aSJerome Brunet 	.ops		= &axg_tdmin_ops,
230f01bc67fSJerome Brunet 	.quirks		= &(const struct axg_tdm_formatter_hw) {
23180a25439SJerome Brunet 		.skew_offset	= 3,
23280a25439SJerome Brunet 	},
23380a25439SJerome Brunet };
23480a25439SJerome Brunet 
23513a22e6aSJerome Brunet static const struct of_device_id axg_tdmin_of_match[] = {
23613a22e6aSJerome Brunet 	{
23713a22e6aSJerome Brunet 		.compatible = "amlogic,axg-tdmin",
23813a22e6aSJerome Brunet 		.data = &axg_tdmin_drv,
23980a25439SJerome Brunet 	}, {
24080a25439SJerome Brunet 		.compatible = "amlogic,g12a-tdmin",
241a84dfb3dSJerome Brunet 		.data = &axg_tdmin_drv,
24280a25439SJerome Brunet 	}, {
24380a25439SJerome Brunet 		.compatible = "amlogic,sm1-tdmin",
244a84dfb3dSJerome Brunet 		.data = &axg_tdmin_drv,
24513a22e6aSJerome Brunet 	}, {}
24613a22e6aSJerome Brunet };
24713a22e6aSJerome Brunet MODULE_DEVICE_TABLE(of, axg_tdmin_of_match);
24813a22e6aSJerome Brunet 
24913a22e6aSJerome Brunet static struct platform_driver axg_tdmin_pdrv = {
25013a22e6aSJerome Brunet 	.probe = axg_tdm_formatter_probe,
25113a22e6aSJerome Brunet 	.driver = {
25213a22e6aSJerome Brunet 		.name = "axg-tdmin",
25313a22e6aSJerome Brunet 		.of_match_table = axg_tdmin_of_match,
25413a22e6aSJerome Brunet 	},
25513a22e6aSJerome Brunet };
25613a22e6aSJerome Brunet module_platform_driver(axg_tdmin_pdrv);
25713a22e6aSJerome Brunet 
25813a22e6aSJerome Brunet MODULE_DESCRIPTION("Amlogic AXG TDM input formatter driver");
25913a22e6aSJerome Brunet MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
26013a22e6aSJerome Brunet MODULE_LICENSE("GPL v2");
261