162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// MediaTek ALSA SoC Audio DAI HW Gain Control
462306a36Sopenharmony_ci//
562306a36Sopenharmony_ci// Copyright (c) 2022 MediaTek Inc.
662306a36Sopenharmony_ci// Author: Jiaxin Yu <jiaxin.yu@mediatek.com>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/regmap.h>
962306a36Sopenharmony_ci#include "mt8186-afe-common.h"
1062306a36Sopenharmony_ci#include "mt8186-interconnection.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#define HW_GAIN_1_EN_W_NAME "HW GAIN 1 Enable"
1362306a36Sopenharmony_ci#define HW_GAIN_2_EN_W_NAME "HW GAIN 2 Enable"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci/* dai component */
1662306a36Sopenharmony_cistatic const struct snd_kcontrol_new mtk_hw_gain1_in_ch1_mix[] = {
1762306a36Sopenharmony_ci	SOC_DAPM_SINGLE_AUTODISABLE("CONNSYS_I2S_CH1 Switch", AFE_CONN13_1,
1862306a36Sopenharmony_ci				    I_CONNSYS_I2S_CH1, 1, 0),
1962306a36Sopenharmony_ci};
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic const struct snd_kcontrol_new mtk_hw_gain1_in_ch2_mix[] = {
2262306a36Sopenharmony_ci	SOC_DAPM_SINGLE_AUTODISABLE("CONNSYS_I2S_CH2 Switch", AFE_CONN14_1,
2362306a36Sopenharmony_ci				    I_CONNSYS_I2S_CH2, 1, 0),
2462306a36Sopenharmony_ci};
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic const struct snd_kcontrol_new mtk_hw_gain2_in_ch1_mix[] = {
2762306a36Sopenharmony_ci	SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1 Switch", AFE_CONN15,
2862306a36Sopenharmony_ci				    I_ADDA_UL_CH1, 1, 0),
2962306a36Sopenharmony_ci};
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic const struct snd_kcontrol_new mtk_hw_gain2_in_ch2_mix[] = {
3262306a36Sopenharmony_ci	SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2 Switch", AFE_CONN16,
3362306a36Sopenharmony_ci				    I_ADDA_UL_CH2, 1, 0),
3462306a36Sopenharmony_ci};
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic int mtk_hw_gain_event(struct snd_soc_dapm_widget *w,
3762306a36Sopenharmony_ci			     struct snd_kcontrol *kcontrol,
3862306a36Sopenharmony_ci			     int event)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
4162306a36Sopenharmony_ci	struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
4262306a36Sopenharmony_ci	unsigned int gain_cur;
4362306a36Sopenharmony_ci	unsigned int gain_con1;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	dev_dbg(cmpnt->dev, "%s(), name %s, event 0x%x\n",
4662306a36Sopenharmony_ci		__func__, w->name, event);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	switch (event) {
4962306a36Sopenharmony_ci	case SND_SOC_DAPM_PRE_PMU:
5062306a36Sopenharmony_ci		if (strcmp(w->name, HW_GAIN_1_EN_W_NAME) == 0) {
5162306a36Sopenharmony_ci			gain_cur = AFE_GAIN1_CUR;
5262306a36Sopenharmony_ci			gain_con1 = AFE_GAIN1_CON1;
5362306a36Sopenharmony_ci		} else {
5462306a36Sopenharmony_ci			gain_cur = AFE_GAIN2_CUR;
5562306a36Sopenharmony_ci			gain_con1 = AFE_GAIN2_CON1;
5662306a36Sopenharmony_ci		}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci		/* let hw gain ramp up, set cur gain to 0 */
5962306a36Sopenharmony_ci		regmap_update_bits(afe->regmap, gain_cur, AFE_GAIN1_CUR_MASK_SFT, 0);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci		/* set target gain to 0 */
6262306a36Sopenharmony_ci		regmap_update_bits(afe->regmap, gain_con1, GAIN1_TARGET_MASK_SFT, 0);
6362306a36Sopenharmony_ci		break;
6462306a36Sopenharmony_ci	default:
6562306a36Sopenharmony_ci		break;
6662306a36Sopenharmony_ci	}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	return 0;
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget mtk_dai_hw_gain_widgets[] = {
7262306a36Sopenharmony_ci	/* inter-connections */
7362306a36Sopenharmony_ci	SND_SOC_DAPM_MIXER("HW_GAIN1_IN_CH1", SND_SOC_NOPM, 0, 0,
7462306a36Sopenharmony_ci			   mtk_hw_gain1_in_ch1_mix,
7562306a36Sopenharmony_ci			   ARRAY_SIZE(mtk_hw_gain1_in_ch1_mix)),
7662306a36Sopenharmony_ci	SND_SOC_DAPM_MIXER("HW_GAIN1_IN_CH2", SND_SOC_NOPM, 0, 0,
7762306a36Sopenharmony_ci			   mtk_hw_gain1_in_ch2_mix,
7862306a36Sopenharmony_ci			   ARRAY_SIZE(mtk_hw_gain1_in_ch2_mix)),
7962306a36Sopenharmony_ci	SND_SOC_DAPM_MIXER("HW_GAIN2_IN_CH1", SND_SOC_NOPM, 0, 0,
8062306a36Sopenharmony_ci			   mtk_hw_gain2_in_ch1_mix,
8162306a36Sopenharmony_ci			   ARRAY_SIZE(mtk_hw_gain2_in_ch1_mix)),
8262306a36Sopenharmony_ci	SND_SOC_DAPM_MIXER("HW_GAIN2_IN_CH2", SND_SOC_NOPM, 0, 0,
8362306a36Sopenharmony_ci			   mtk_hw_gain2_in_ch2_mix,
8462306a36Sopenharmony_ci			   ARRAY_SIZE(mtk_hw_gain2_in_ch2_mix)),
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	SND_SOC_DAPM_SUPPLY(HW_GAIN_1_EN_W_NAME,
8762306a36Sopenharmony_ci			    AFE_GAIN1_CON0, GAIN1_ON_SFT, 0,
8862306a36Sopenharmony_ci			    mtk_hw_gain_event,
8962306a36Sopenharmony_ci			    SND_SOC_DAPM_PRE_PMU),
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	SND_SOC_DAPM_SUPPLY(HW_GAIN_2_EN_W_NAME,
9262306a36Sopenharmony_ci			    AFE_GAIN2_CON0, GAIN2_ON_SFT, 0,
9362306a36Sopenharmony_ci			    mtk_hw_gain_event,
9462306a36Sopenharmony_ci			    SND_SOC_DAPM_PRE_PMU),
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	SND_SOC_DAPM_INPUT("HW Gain 1 Out Endpoint"),
9762306a36Sopenharmony_ci	SND_SOC_DAPM_INPUT("HW Gain 2 Out Endpoint"),
9862306a36Sopenharmony_ci	SND_SOC_DAPM_OUTPUT("HW Gain 1 In Endpoint"),
9962306a36Sopenharmony_ci};
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic const struct snd_soc_dapm_route mtk_dai_hw_gain_routes[] = {
10262306a36Sopenharmony_ci	{"HW Gain 1 In", NULL, "HW_GAIN1_IN_CH1"},
10362306a36Sopenharmony_ci	{"HW Gain 1 In", NULL, "HW_GAIN1_IN_CH2"},
10462306a36Sopenharmony_ci	{"HW Gain 2 In", NULL, "HW_GAIN2_IN_CH1"},
10562306a36Sopenharmony_ci	{"HW Gain 2 In", NULL, "HW_GAIN2_IN_CH2"},
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	{"HW Gain 1 In", NULL, HW_GAIN_1_EN_W_NAME},
10862306a36Sopenharmony_ci	{"HW Gain 1 Out", NULL, HW_GAIN_1_EN_W_NAME},
10962306a36Sopenharmony_ci	{"HW Gain 2 In", NULL, HW_GAIN_2_EN_W_NAME},
11062306a36Sopenharmony_ci	{"HW Gain 2 Out", NULL, HW_GAIN_2_EN_W_NAME},
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	{"HW Gain 1 In Endpoint", NULL, "HW Gain 1 In"},
11362306a36Sopenharmony_ci	{"HW Gain 1 Out", NULL, "HW Gain 1 Out Endpoint"},
11462306a36Sopenharmony_ci	{"HW Gain 2 Out", NULL, "HW Gain 2 Out Endpoint"},
11562306a36Sopenharmony_ci};
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic const struct snd_kcontrol_new mtk_hw_gain_controls[] = {
11862306a36Sopenharmony_ci	SOC_SINGLE("HW Gain 1 Volume", AFE_GAIN1_CON1,
11962306a36Sopenharmony_ci		   GAIN1_TARGET_SFT, GAIN1_TARGET_MASK, 0),
12062306a36Sopenharmony_ci	SOC_SINGLE("HW Gain 2 Volume", AFE_GAIN2_CON1,
12162306a36Sopenharmony_ci		   GAIN2_TARGET_SFT, GAIN2_TARGET_MASK, 0),
12262306a36Sopenharmony_ci};
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci/* dai ops */
12562306a36Sopenharmony_cistatic int mtk_dai_gain_hw_params(struct snd_pcm_substream *substream,
12662306a36Sopenharmony_ci				  struct snd_pcm_hw_params *params,
12762306a36Sopenharmony_ci				  struct snd_soc_dai *dai)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
13062306a36Sopenharmony_ci	unsigned int rate = params_rate(params);
13162306a36Sopenharmony_ci	unsigned int rate_reg = mt8186_rate_transform(afe->dev, rate, dai->id);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	dev_dbg(afe->dev, "%s(), id %d, stream %d, rate %d\n",
13462306a36Sopenharmony_ci		__func__, dai->id, substream->stream, rate);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	/* rate */
13762306a36Sopenharmony_ci	regmap_update_bits(afe->regmap,
13862306a36Sopenharmony_ci			   dai->id == MT8186_DAI_HW_GAIN_1 ?
13962306a36Sopenharmony_ci			   AFE_GAIN1_CON0 : AFE_GAIN2_CON0,
14062306a36Sopenharmony_ci			   GAIN1_MODE_MASK_SFT,
14162306a36Sopenharmony_ci			   rate_reg << GAIN1_MODE_SFT);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	/* sample per step */
14462306a36Sopenharmony_ci	regmap_update_bits(afe->regmap,
14562306a36Sopenharmony_ci			   dai->id == MT8186_DAI_HW_GAIN_1 ?
14662306a36Sopenharmony_ci			   AFE_GAIN1_CON0 : AFE_GAIN2_CON0,
14762306a36Sopenharmony_ci			   GAIN1_SAMPLE_PER_STEP_MASK_SFT,
14862306a36Sopenharmony_ci			   (dai->id == MT8186_DAI_HW_GAIN_1 ? 0x40 : 0x0) <<
14962306a36Sopenharmony_ci			   GAIN1_SAMPLE_PER_STEP_SFT);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	return 0;
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistatic const struct snd_soc_dai_ops mtk_dai_gain_ops = {
15562306a36Sopenharmony_ci	.hw_params = mtk_dai_gain_hw_params,
15662306a36Sopenharmony_ci};
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci/* dai driver */
15962306a36Sopenharmony_ci#define MTK_HW_GAIN_RATES (SNDRV_PCM_RATE_8000_48000 |\
16062306a36Sopenharmony_ci			   SNDRV_PCM_RATE_88200 |\
16162306a36Sopenharmony_ci			   SNDRV_PCM_RATE_96000 |\
16262306a36Sopenharmony_ci			   SNDRV_PCM_RATE_176400 |\
16362306a36Sopenharmony_ci			   SNDRV_PCM_RATE_192000)
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci#define MTK_HW_GAIN_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
16662306a36Sopenharmony_ci			     SNDRV_PCM_FMTBIT_S24_LE |\
16762306a36Sopenharmony_ci			     SNDRV_PCM_FMTBIT_S32_LE)
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic struct snd_soc_dai_driver mtk_dai_gain_driver[] = {
17062306a36Sopenharmony_ci	{
17162306a36Sopenharmony_ci		.name = "HW Gain 1",
17262306a36Sopenharmony_ci		.id = MT8186_DAI_HW_GAIN_1,
17362306a36Sopenharmony_ci		.playback = {
17462306a36Sopenharmony_ci			.stream_name = "HW Gain 1 In",
17562306a36Sopenharmony_ci			.channels_min = 1,
17662306a36Sopenharmony_ci			.channels_max = 2,
17762306a36Sopenharmony_ci			.rates = MTK_HW_GAIN_RATES,
17862306a36Sopenharmony_ci			.formats = MTK_HW_GAIN_FORMATS,
17962306a36Sopenharmony_ci		},
18062306a36Sopenharmony_ci		.capture = {
18162306a36Sopenharmony_ci			.stream_name = "HW Gain 1 Out",
18262306a36Sopenharmony_ci			.channels_min = 1,
18362306a36Sopenharmony_ci			.channels_max = 2,
18462306a36Sopenharmony_ci			.rates = MTK_HW_GAIN_RATES,
18562306a36Sopenharmony_ci			.formats = MTK_HW_GAIN_FORMATS,
18662306a36Sopenharmony_ci		},
18762306a36Sopenharmony_ci		.ops = &mtk_dai_gain_ops,
18862306a36Sopenharmony_ci		.symmetric_rate = 1,
18962306a36Sopenharmony_ci		.symmetric_channels = 1,
19062306a36Sopenharmony_ci		.symmetric_sample_bits = 1,
19162306a36Sopenharmony_ci	},
19262306a36Sopenharmony_ci	{
19362306a36Sopenharmony_ci		.name = "HW Gain 2",
19462306a36Sopenharmony_ci		.id = MT8186_DAI_HW_GAIN_2,
19562306a36Sopenharmony_ci		.playback = {
19662306a36Sopenharmony_ci			.stream_name = "HW Gain 2 In",
19762306a36Sopenharmony_ci			.channels_min = 1,
19862306a36Sopenharmony_ci			.channels_max = 2,
19962306a36Sopenharmony_ci			.rates = MTK_HW_GAIN_RATES,
20062306a36Sopenharmony_ci			.formats = MTK_HW_GAIN_FORMATS,
20162306a36Sopenharmony_ci		},
20262306a36Sopenharmony_ci		.capture = {
20362306a36Sopenharmony_ci			.stream_name = "HW Gain 2 Out",
20462306a36Sopenharmony_ci			.channels_min = 1,
20562306a36Sopenharmony_ci			.channels_max = 2,
20662306a36Sopenharmony_ci			.rates = MTK_HW_GAIN_RATES,
20762306a36Sopenharmony_ci			.formats = MTK_HW_GAIN_FORMATS,
20862306a36Sopenharmony_ci		},
20962306a36Sopenharmony_ci		.ops = &mtk_dai_gain_ops,
21062306a36Sopenharmony_ci		.symmetric_rate = 1,
21162306a36Sopenharmony_ci		.symmetric_channels = 1,
21262306a36Sopenharmony_ci		.symmetric_sample_bits = 1,
21362306a36Sopenharmony_ci	},
21462306a36Sopenharmony_ci};
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ciint mt8186_dai_hw_gain_register(struct mtk_base_afe *afe)
21762306a36Sopenharmony_ci{
21862306a36Sopenharmony_ci	struct mtk_base_afe_dai *dai;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
22162306a36Sopenharmony_ci	if (!dai)
22262306a36Sopenharmony_ci		return -ENOMEM;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	list_add(&dai->list, &afe->sub_dais);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	dai->dai_drivers = mtk_dai_gain_driver;
22762306a36Sopenharmony_ci	dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_gain_driver);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	dai->controls = mtk_hw_gain_controls;
23062306a36Sopenharmony_ci	dai->num_controls = ARRAY_SIZE(mtk_hw_gain_controls);
23162306a36Sopenharmony_ci	dai->dapm_widgets = mtk_dai_hw_gain_widgets;
23262306a36Sopenharmony_ci	dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_hw_gain_widgets);
23362306a36Sopenharmony_ci	dai->dapm_routes = mtk_dai_hw_gain_routes;
23462306a36Sopenharmony_ci	dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_hw_gain_routes);
23562306a36Sopenharmony_ci	return 0;
23662306a36Sopenharmony_ci}
237