blob: e5a743563e803e7683ba5511262a869fbbe53a35 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 BayLibre, SAS
* Copyright (c) 2019 MediaTek, Inc
* Author: Fabien Parent <fparent@baylibre.com>
*/
#include <sound/soc.h>
#include <sound/pcm_params.h>
#include "mt8516-afe-common.h"
#include "mt8516-afe-regs.h"
enum {
MTK_AFE_ADDA_DL_RATE_8K = 0,
MTK_AFE_ADDA_DL_RATE_11K = 1,
MTK_AFE_ADDA_DL_RATE_12K = 2,
MTK_AFE_ADDA_DL_RATE_16K = 3,
MTK_AFE_ADDA_DL_RATE_22K = 4,
MTK_AFE_ADDA_DL_RATE_24K = 5,
MTK_AFE_ADDA_DL_RATE_32K = 6,
MTK_AFE_ADDA_DL_RATE_44K = 7,
MTK_AFE_ADDA_DL_RATE_48K = 8,
};
enum {
MTK_AFE_ADDA_UL_RATE_8K = 0,
MTK_AFE_ADDA_UL_RATE_16K = 1,
MTK_AFE_ADDA_UL_RATE_32K = 2,
MTK_AFE_ADDA_UL_RATE_48K = 3,
};
static int mt8516_afe_setup_i2s(struct mtk_base_afe *afe,
struct snd_pcm_substream *substream,
unsigned int rate, int bit_width)
{
int fs = afe->memif_fs(substream, rate);
unsigned int val;
if (bit_width > 16)
val |= AFE_I2S_CON1_WLEN_32BIT;
if (fs < 0)
return -EINVAL;
val = AFE_I2S_CON1_I2S2_TO_PAD |
AFE_I2S_CON1_LOW_JITTER_CLK |
AFE_I2S_CON1_RATE(fs) |
AFE_I2S_CON1_FORMAT_I2S |
AFE_I2S_CON1_EN;
regmap_write(afe->regmap, AFE_I2S_CON1, val);
return 0;
}
static int mt8516_afe_setup_adda_dl(struct mtk_base_afe *afe, unsigned int rate)
{
unsigned int val = AFE_ADDA_DL_8X_UPSAMPLE |
AFE_ADDA_DL_MUTE_OFF |
AFE_ADDA_DL_DEGRADE_GAIN;
if (rate == 8000 || rate == 16000)
val |= AFE_ADDA_DL_VOICE_DATA;
switch (rate) {
case 8000:
val |= MTK_AFE_ADDA_DL_RATE_8K << AFE_ADDA_DL_RATE_SHIFT;
break;
case 11025:
val |= MTK_AFE_ADDA_DL_RATE_11K << AFE_ADDA_DL_RATE_SHIFT;
break;
case 12000:
val |= MTK_AFE_ADDA_DL_RATE_12K << AFE_ADDA_DL_RATE_SHIFT;
break;
case 16000:
val |= MTK_AFE_ADDA_DL_RATE_16K << AFE_ADDA_DL_RATE_SHIFT;
break;
case 22050:
val |= MTK_AFE_ADDA_DL_RATE_22K << AFE_ADDA_DL_RATE_SHIFT;
break;
case 24000:
val |= MTK_AFE_ADDA_DL_RATE_24K << AFE_ADDA_DL_RATE_SHIFT;
break;
case 32000:
val |= MTK_AFE_ADDA_DL_RATE_32K << AFE_ADDA_DL_RATE_SHIFT;
break;
case 44100:
val |= MTK_AFE_ADDA_DL_RATE_44K << AFE_ADDA_DL_RATE_SHIFT;
break;
case 48000:
val |= MTK_AFE_ADDA_DL_RATE_48K << AFE_ADDA_DL_RATE_SHIFT;
break;
default:
return -EINVAL;
}
regmap_write(afe->regmap, AFE_ADDA_PREDIS_CON0, 0);
regmap_write(afe->regmap, AFE_ADDA_PREDIS_CON1, 0);
regmap_write(afe->regmap, AFE_ADDA_DL_SRC2_CON0, val);
regmap_write(afe->regmap, AFE_ADDA_DL_SRC2_CON1, 0xf74f0000);
return 0;
}
static int mt8516_afe_setup_adda_ul(struct mtk_base_afe *afe, unsigned int rate)
{
unsigned int val = 0;
unsigned int val2 = 0;
switch (rate) {
case 8000:
val |= MTK_AFE_ADDA_UL_RATE_8K << AFE_ADDA_UL_RATE_CH1_SHIFT;
val |= MTK_AFE_ADDA_UL_RATE_8K << AFE_ADDA_UL_RATE_CH2_SHIFT;
val2 |= 1 << AFE_ADDA_NEWIF_ADC_VOICE_MODE_SHIFT;
break;
case 16000:
val |= MTK_AFE_ADDA_UL_RATE_16K << AFE_ADDA_UL_RATE_CH1_SHIFT;
val |= MTK_AFE_ADDA_UL_RATE_16K << AFE_ADDA_UL_RATE_CH2_SHIFT;
val2 |= 1 << AFE_ADDA_NEWIF_ADC_VOICE_MODE_SHIFT;
break;
case 32000:
val |= MTK_AFE_ADDA_UL_RATE_32K << AFE_ADDA_UL_RATE_CH1_SHIFT;
val |= MTK_AFE_ADDA_UL_RATE_32K << AFE_ADDA_UL_RATE_CH2_SHIFT;
val2 |= 1 << AFE_ADDA_NEWIF_ADC_VOICE_MODE_SHIFT;
break;
case 48000:
val |= MTK_AFE_ADDA_UL_RATE_48K << AFE_ADDA_UL_RATE_CH1_SHIFT;
val |= MTK_AFE_ADDA_UL_RATE_48K << AFE_ADDA_UL_RATE_CH2_SHIFT;
val2 |= 3 << AFE_ADDA_NEWIF_ADC_VOICE_MODE_SHIFT;
break;
default:
return -EINVAL;
}
regmap_update_bits(afe->regmap, AFE_ADDA_UL_SRC_CON0,
(AFE_ADDA_UL_RATE_CH1_MASK << AFE_ADDA_UL_RATE_CH1_SHIFT) ||
(AFE_ADDA_UL_RATE_CH2_MASK << AFE_ADDA_UL_RATE_CH2_MASK), val);
regmap_update_bits(afe->regmap, AFE_ADDA_NEWIF_CFG1,
AFE_ADDA_NEWIF_ADC_VOICE_MODE_CLR, val2);
regmap_update_bits(afe->regmap, AFE_ADDA_TOP_CON0, 1, 0);
return 0;
}
static void mt8516_afe_adda_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
unsigned int stream = substream->stream;
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
regmap_update_bits(afe->regmap, AFE_ADDA_DL_SRC2_CON0, 1, 0);
regmap_update_bits(afe->regmap, AFE_I2S_CON1, 1, 0);
} else {
regmap_update_bits(afe->regmap, AFE_ADDA_UL_SRC_CON0, 1, 0);
}
regmap_update_bits(afe->regmap, AFE_ADDA_UL_DL_CON0, 1, 0);
}
static int mt8516_afe_adda_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
unsigned int width_val = 0;
if (params_width(params) > 16)
width_val = AFE_CONN_24BIT_O03 | AFE_CONN_24BIT_O04;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
regmap_update_bits(afe->regmap, AFE_CONN_24BIT,
AFE_CONN_24BIT_O03 | AFE_CONN_24BIT_O04, width_val);
return 0;
}
static int mt8516_afe_adda_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
const unsigned int rate = substream->runtime->rate;
unsigned int stream = substream->stream;
int bit_width = snd_pcm_format_width(substream->runtime->format);
int ret;
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
ret = mt8516_afe_setup_adda_dl(afe, rate);
if (ret)
return ret;
ret = mt8516_afe_setup_i2s(afe, substream, rate, bit_width);
if (ret)
return ret;
regmap_update_bits(afe->regmap, AFE_ADDA_DL_SRC2_CON0, 1, 1);
} else {
ret = mt8516_afe_setup_adda_ul(afe, rate);
if (ret)
return ret;
regmap_update_bits(afe->regmap, AFE_ADDA_UL_SRC_CON0, 1, 1);
}
regmap_update_bits(afe->regmap, AFE_ADDA_UL_DL_CON0, 1, 1);
return 0;
}
static const struct snd_soc_dai_ops mt8516_afe_adda_ops = {
.shutdown = mt8516_afe_adda_shutdown,
.hw_params = mt8516_afe_adda_hw_params,
.prepare = mt8516_afe_adda_prepare,
};
static const struct snd_kcontrol_new adda_o03_o04_enable_ctl =
SOC_DAPM_SINGLE_VIRT("Switch", 1);
static const char * const ain_text[] = {
"INT ADC", "EXT ADC"
};
static SOC_ENUM_SINGLE_DECL(ain_enum, AFE_ADDA_TOP_CON0, 0, ain_text);
static const struct snd_kcontrol_new ain_mux =
SOC_DAPM_ENUM("AIN Source", ain_enum);
enum {
SUPPLY_SEQ_ADDA_AFE_ON,
};
static const struct snd_soc_dapm_widget mtk_dai_adda_widgets[] = {
SND_SOC_DAPM_MUX("AIN Mux", SND_SOC_NOPM, 0, 0, &ain_mux),
SND_SOC_DAPM_SWITCH("ADDA O03_O04", SND_SOC_NOPM, 0, 0,
&adda_o03_o04_enable_ctl),
SND_SOC_DAPM_SUPPLY_S("ADDA Enable", SUPPLY_SEQ_ADDA_AFE_ON,
AFE_DAC_CON0, 0, 0,
NULL, 0),
/* Clocks */
SND_SOC_DAPM_CLOCK_SUPPLY("top_pdn_audio"),
SND_SOC_DAPM_CLOCK_SUPPLY("aud_dac_clk"),
SND_SOC_DAPM_CLOCK_SUPPLY("aud_dac_predis_clk"),
SND_SOC_DAPM_CLOCK_SUPPLY("aud_adc_clk"),
};
static const struct snd_soc_dapm_route mtk_dai_adda_routes[] = {
/* playback */
{"ADDA O03_O04", "Switch", "O03"},
{"ADDA O03_O04", "Switch", "O04"},
{"ADDA Playback", NULL, "ADDA O03_O04"},
/* capture */
{"AIN Mux", "INT ADC", "ADDA Capture"},
/* enable */
{"ADDA Playback", NULL, "ADDA Enable"},
{"ADDA Capture", NULL, "ADDA Enable"},
/* clock */
{"ADDA Playback", NULL, "aud_dac_clk"},
{"ADDA Playback", NULL, "aud_dac_predis_clk"},
{"ADDA Playback", NULL, "top_pdn_audio"},
{"ADDA Capture", NULL, "top_pdn_audio"},
{"ADDA Capture", NULL, "aud_adc_clk"},
};
static struct snd_soc_dai_driver mtk_dai_adda_driver[] = {
{
.name = "ADDA",
.id = MT8516_AFE_BE_ADDA,
.playback = {
.stream_name = "ADDA Playback",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE,
},
.capture = {
.stream_name = "ADDA Capture",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000 |
SNDRV_PCM_RATE_16000 |
SNDRV_PCM_RATE_32000 |
SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.ops = &mt8516_afe_adda_ops,
},
};
int mt8516_dai_adda_register(struct mtk_base_afe *afe)
{
struct mtk_base_afe_dai *dai;
dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
if (!dai)
return -ENOMEM;
list_add(&dai->list, &afe->sub_dais);
dai->dai_drivers = mtk_dai_adda_driver;
dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_adda_driver);
dai->dapm_widgets = mtk_dai_adda_widgets;
dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_adda_widgets);
dai->dapm_routes = mtk_dai_adda_routes;
dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_adda_routes);
return 0;
}