Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,9 @@
# SPDX-License-Identifier: GPL-2.0
# platform driver
snd-soc-mt2701-afe-y := mt2701-afe-pcm.o mt2701-afe-clock-ctrl.o
obj-$(CONFIG_SND_SOC_MT2701) += snd-soc-mt2701-afe.o
# machine driver
obj-$(CONFIG_SND_SOC_MT2701_CS42448) += mt2701-cs42448.o
obj-$(CONFIG_SND_SOC_MT2701_HDMI) += mt2701-hdmi.o
obj-$(CONFIG_SND_SOC_MT2701_WM8960) += mt2701-wm8960.o
@@ -0,0 +1,322 @@
// SPDX-License-Identifier: GPL-2.0
/*
* mt2701-afe-clock-ctrl.c -- Mediatek 2701 afe clock ctrl
*
* Copyright (c) 2016 MediaTek Inc.
* Author: Garlic Tseng <garlic.tseng@mediatek.com>
* Ryder Lee <ryder.lee@mediatek.com>
*/
#include "mt2701-afe-common.h"
#include "mt2701-afe-clock-ctrl.h"
static const char *const base_clks[] = {
[MT2701_INFRA_SYS_AUDIO] = "infra_sys_audio_clk",
[MT2701_TOP_AUD_MCLK_SRC0] = "top_audio_mux1_sel",
[MT2701_TOP_AUD_MCLK_SRC1] = "top_audio_mux2_sel",
[MT2701_TOP_AUD_A1SYS] = "top_audio_a1sys_hp",
[MT2701_TOP_AUD_A2SYS] = "top_audio_a2sys_hp",
[MT2701_AUDSYS_AFE] = "audio_afe_pd",
[MT2701_AUDSYS_AFE_CONN] = "audio_afe_conn_pd",
[MT2701_AUDSYS_A1SYS] = "audio_a1sys_pd",
[MT2701_AUDSYS_A2SYS] = "audio_a2sys_pd",
};
int mt2701_init_clock(struct mtk_base_afe *afe)
{
struct mt2701_afe_private *afe_priv = afe->platform_priv;
int i2s_num;
int i;
for (i = 0; i < MT2701_BASE_CLK_NUM; i++) {
afe_priv->base_ck[i] = devm_clk_get(afe->dev, base_clks[i]);
if (IS_ERR(afe_priv->base_ck[i])) {
dev_err(afe->dev, "failed to get %s\n", base_clks[i]);
return PTR_ERR(afe_priv->base_ck[i]);
}
}
i2s_num = min(afe_priv->soc->i2s_num, MT2701_BASE_CLK_NUM);
/* Get I2S related clocks */
for (i = 0; i < i2s_num; i++) {
struct mt2701_i2s_path *i2s_path = &afe_priv->i2s_path[i];
struct clk *i2s_ck;
char name[13];
snprintf(name, sizeof(name), "i2s%d_src_sel", i);
i2s_path->sel_ck = devm_clk_get(afe->dev, name);
if (IS_ERR(i2s_path->sel_ck)) {
dev_err(afe->dev, "failed to get %s\n", name);
return PTR_ERR(i2s_path->sel_ck);
}
snprintf(name, sizeof(name), "i2s%d_src_div", i);
i2s_path->div_ck = devm_clk_get(afe->dev, name);
if (IS_ERR(i2s_path->div_ck)) {
dev_err(afe->dev, "failed to get %s\n", name);
return PTR_ERR(i2s_path->div_ck);
}
snprintf(name, sizeof(name), "i2s%d_mclk_en", i);
i2s_path->mclk_ck = devm_clk_get(afe->dev, name);
if (IS_ERR(i2s_path->mclk_ck)) {
dev_err(afe->dev, "failed to get %s\n", name);
return PTR_ERR(i2s_path->mclk_ck);
}
snprintf(name, sizeof(name), "i2so%d_hop_ck", i);
i2s_ck = devm_clk_get(afe->dev, name);
if (IS_ERR(i2s_ck)) {
dev_err(afe->dev, "failed to get %s\n", name);
return PTR_ERR(i2s_ck);
}
i2s_path->hop_ck[SNDRV_PCM_STREAM_PLAYBACK] = i2s_ck;
snprintf(name, sizeof(name), "i2si%d_hop_ck", i);
i2s_ck = devm_clk_get(afe->dev, name);
if (IS_ERR(i2s_ck)) {
dev_err(afe->dev, "failed to get %s\n", name);
return PTR_ERR(i2s_ck);
}
i2s_path->hop_ck[SNDRV_PCM_STREAM_CAPTURE] = i2s_ck;
snprintf(name, sizeof(name), "asrc%d_out_ck", i);
i2s_path->asrco_ck = devm_clk_get(afe->dev, name);
if (IS_ERR(i2s_path->asrco_ck)) {
dev_err(afe->dev, "failed to get %s\n", name);
return PTR_ERR(i2s_path->asrco_ck);
}
}
/* Some platforms may support BT path */
afe_priv->mrgif_ck = devm_clk_get(afe->dev, "audio_mrgif_pd");
if (IS_ERR(afe_priv->mrgif_ck)) {
if (PTR_ERR(afe_priv->mrgif_ck) == -EPROBE_DEFER)
return -EPROBE_DEFER;
afe_priv->mrgif_ck = NULL;
}
/*
* Optional HDMI audio clocks. Platforms that do not wire up the
* HDMI output (e.g. MT2701 devkits using only the I2S BE DAIs)
* may omit these; in that case the HDMI BE DAI simply cannot be
* enabled, but the rest of the AFE still probes.
*/
afe_priv->hadds2pll_ck = devm_clk_get_optional(afe->dev, "hadds2pll_294m");
if (IS_ERR(afe_priv->hadds2pll_ck))
return PTR_ERR(afe_priv->hadds2pll_ck);
afe_priv->audio_hdmi_ck = devm_clk_get_optional(afe->dev, "audio_hdmi_pd");
if (IS_ERR(afe_priv->audio_hdmi_ck))
return PTR_ERR(afe_priv->audio_hdmi_ck);
afe_priv->audio_spdf_ck = devm_clk_get_optional(afe->dev, "audio_spdf_pd");
if (IS_ERR(afe_priv->audio_spdf_ck))
return PTR_ERR(afe_priv->audio_spdf_ck);
afe_priv->audio_apll_ck = devm_clk_get_optional(afe->dev, "audio_apll_pd");
if (IS_ERR(afe_priv->audio_apll_ck))
return PTR_ERR(afe_priv->audio_apll_ck);
return 0;
}
int mt2701_afe_enable_i2s(struct mtk_base_afe *afe,
struct mt2701_i2s_path *i2s_path,
int dir)
{
int ret;
ret = clk_prepare_enable(i2s_path->asrco_ck);
if (ret) {
dev_err(afe->dev, "failed to enable ASRC clock %d\n", ret);
return ret;
}
ret = clk_prepare_enable(i2s_path->hop_ck[dir]);
if (ret) {
dev_err(afe->dev, "failed to enable I2S clock %d\n", ret);
goto err_hop_ck;
}
return 0;
err_hop_ck:
clk_disable_unprepare(i2s_path->asrco_ck);
return ret;
}
void mt2701_afe_disable_i2s(struct mtk_base_afe *afe,
struct mt2701_i2s_path *i2s_path,
int dir)
{
clk_disable_unprepare(i2s_path->hop_ck[dir]);
clk_disable_unprepare(i2s_path->asrco_ck);
}
int mt2701_afe_enable_mclk(struct mtk_base_afe *afe, int id)
{
struct mt2701_afe_private *afe_priv = afe->platform_priv;
struct mt2701_i2s_path *i2s_path = &afe_priv->i2s_path[id];
return clk_prepare_enable(i2s_path->mclk_ck);
}
void mt2701_afe_disable_mclk(struct mtk_base_afe *afe, int id)
{
struct mt2701_afe_private *afe_priv = afe->platform_priv;
struct mt2701_i2s_path *i2s_path = &afe_priv->i2s_path[id];
clk_disable_unprepare(i2s_path->mclk_ck);
}
int mt2701_enable_btmrg_clk(struct mtk_base_afe *afe)
{
struct mt2701_afe_private *afe_priv = afe->platform_priv;
return clk_prepare_enable(afe_priv->mrgif_ck);
}
void mt2701_disable_btmrg_clk(struct mtk_base_afe *afe)
{
struct mt2701_afe_private *afe_priv = afe->platform_priv;
clk_disable_unprepare(afe_priv->mrgif_ck);
}
static int mt2701_afe_enable_audsys(struct mtk_base_afe *afe)
{
struct mt2701_afe_private *afe_priv = afe->platform_priv;
int ret;
/* Enable infra clock gate */
ret = clk_prepare_enable(afe_priv->base_ck[MT2701_INFRA_SYS_AUDIO]);
if (ret)
return ret;
/* Enable top a1sys clock gate */
ret = clk_prepare_enable(afe_priv->base_ck[MT2701_TOP_AUD_A1SYS]);
if (ret)
goto err_a1sys;
/* Enable top a2sys clock gate */
ret = clk_prepare_enable(afe_priv->base_ck[MT2701_TOP_AUD_A2SYS]);
if (ret)
goto err_a2sys;
/* Internal clock gates */
ret = clk_prepare_enable(afe_priv->base_ck[MT2701_AUDSYS_AFE]);
if (ret)
goto err_afe;
ret = clk_prepare_enable(afe_priv->base_ck[MT2701_AUDSYS_A1SYS]);
if (ret)
goto err_audio_a1sys;
ret = clk_prepare_enable(afe_priv->base_ck[MT2701_AUDSYS_A2SYS]);
if (ret)
goto err_audio_a2sys;
ret = clk_prepare_enable(afe_priv->base_ck[MT2701_AUDSYS_AFE_CONN]);
if (ret)
goto err_afe_conn;
return 0;
err_afe_conn:
clk_disable_unprepare(afe_priv->base_ck[MT2701_AUDSYS_A2SYS]);
err_audio_a2sys:
clk_disable_unprepare(afe_priv->base_ck[MT2701_AUDSYS_A1SYS]);
err_audio_a1sys:
clk_disable_unprepare(afe_priv->base_ck[MT2701_AUDSYS_AFE]);
err_afe:
clk_disable_unprepare(afe_priv->base_ck[MT2701_TOP_AUD_A2SYS]);
err_a2sys:
clk_disable_unprepare(afe_priv->base_ck[MT2701_TOP_AUD_A1SYS]);
err_a1sys:
clk_disable_unprepare(afe_priv->base_ck[MT2701_INFRA_SYS_AUDIO]);
return ret;
}
static void mt2701_afe_disable_audsys(struct mtk_base_afe *afe)
{
struct mt2701_afe_private *afe_priv = afe->platform_priv;
clk_disable_unprepare(afe_priv->base_ck[MT2701_AUDSYS_AFE_CONN]);
clk_disable_unprepare(afe_priv->base_ck[MT2701_AUDSYS_A2SYS]);
clk_disable_unprepare(afe_priv->base_ck[MT2701_AUDSYS_A1SYS]);
clk_disable_unprepare(afe_priv->base_ck[MT2701_AUDSYS_AFE]);
clk_disable_unprepare(afe_priv->base_ck[MT2701_TOP_AUD_A1SYS]);
clk_disable_unprepare(afe_priv->base_ck[MT2701_TOP_AUD_A2SYS]);
clk_disable_unprepare(afe_priv->base_ck[MT2701_INFRA_SYS_AUDIO]);
}
int mt2701_afe_enable_clock(struct mtk_base_afe *afe)
{
int ret;
/* Enable audio system */
ret = mt2701_afe_enable_audsys(afe);
if (ret) {
dev_err(afe->dev, "failed to enable audio system %d\n", ret);
return ret;
}
regmap_update_bits(afe->regmap, ASYS_TOP_CON,
ASYS_TOP_CON_ASYS_TIMING_ON,
ASYS_TOP_CON_ASYS_TIMING_ON);
regmap_update_bits(afe->regmap, AFE_DAC_CON0,
AFE_DAC_CON0_AFE_ON,
AFE_DAC_CON0_AFE_ON);
/* Configure ASRC */
regmap_write(afe->regmap, PWR1_ASM_CON1, PWR1_ASM_CON1_INIT_VAL);
regmap_write(afe->regmap, PWR2_ASM_CON1, PWR2_ASM_CON1_INIT_VAL);
return 0;
}
int mt2701_afe_disable_clock(struct mtk_base_afe *afe)
{
regmap_update_bits(afe->regmap, ASYS_TOP_CON,
ASYS_TOP_CON_ASYS_TIMING_ON, 0);
regmap_update_bits(afe->regmap, AFE_DAC_CON0,
AFE_DAC_CON0_AFE_ON, 0);
mt2701_afe_disable_audsys(afe);
return 0;
}
int mt2701_mclk_configuration(struct mtk_base_afe *afe, int id)
{
struct mt2701_afe_private *priv = afe->platform_priv;
struct mt2701_i2s_path *i2s_path = &priv->i2s_path[id];
int ret = -EINVAL;
/* Set mclk source */
if (!(MT2701_PLL_DOMAIN_0_RATE % i2s_path->mclk_rate))
ret = clk_set_parent(i2s_path->sel_ck,
priv->base_ck[MT2701_TOP_AUD_MCLK_SRC0]);
else if (!(MT2701_PLL_DOMAIN_1_RATE % i2s_path->mclk_rate))
ret = clk_set_parent(i2s_path->sel_ck,
priv->base_ck[MT2701_TOP_AUD_MCLK_SRC1]);
if (ret) {
dev_err(afe->dev, "failed to set mclk source\n");
return ret;
}
/* Set mclk divider */
ret = clk_set_rate(i2s_path->div_ck, i2s_path->mclk_rate);
if (ret) {
dev_err(afe->dev, "failed to set mclk divider %d\n", ret);
return ret;
}
return 0;
}
@@ -0,0 +1,34 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* mt2701-afe-clock-ctrl.h -- Mediatek 2701 afe clock ctrl definition
*
* Copyright (c) 2016 MediaTek Inc.
* Author: Garlic Tseng <garlic.tseng@mediatek.com>
* Ryder Lee <ryder.lee@mediatek.com>
*/
#ifndef _MT2701_AFE_CLOCK_CTRL_H_
#define _MT2701_AFE_CLOCK_CTRL_H_
struct mtk_base_afe;
struct mt2701_i2s_path;
int mt2701_init_clock(struct mtk_base_afe *afe);
int mt2701_afe_enable_clock(struct mtk_base_afe *afe);
int mt2701_afe_disable_clock(struct mtk_base_afe *afe);
int mt2701_afe_enable_i2s(struct mtk_base_afe *afe,
struct mt2701_i2s_path *i2s_path,
int dir);
void mt2701_afe_disable_i2s(struct mtk_base_afe *afe,
struct mt2701_i2s_path *i2s_path,
int dir);
int mt2701_afe_enable_mclk(struct mtk_base_afe *afe, int id);
void mt2701_afe_disable_mclk(struct mtk_base_afe *afe, int id);
int mt2701_enable_btmrg_clk(struct mtk_base_afe *afe);
void mt2701_disable_btmrg_clk(struct mtk_base_afe *afe);
int mt2701_mclk_configuration(struct mtk_base_afe *afe, int id);
#endif
@@ -0,0 +1,104 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* mt2701-afe-common.h -- Mediatek 2701 audio driver definitions
*
* Copyright (c) 2016 MediaTek Inc.
* Author: Garlic Tseng <garlic.tseng@mediatek.com>
*/
#ifndef _MT_2701_AFE_COMMON_H_
#define _MT_2701_AFE_COMMON_H_
#include <sound/soc.h>
#include <linux/clk.h>
#include <linux/regmap.h>
#include "mt2701-reg.h"
#include "../common/mtk-base-afe.h"
#define MT2701_PLL_DOMAIN_0_RATE 98304000
#define MT2701_PLL_DOMAIN_1_RATE 90316800
enum {
MT2701_MEMIF_DL1,
MT2701_MEMIF_DL2,
MT2701_MEMIF_DL3,
MT2701_MEMIF_DL4,
MT2701_MEMIF_DL5,
MT2701_MEMIF_DL_SINGLE_NUM,
MT2701_MEMIF_DLM = MT2701_MEMIF_DL_SINGLE_NUM,
MT2701_MEMIF_UL1,
MT2701_MEMIF_UL2,
MT2701_MEMIF_UL3,
MT2701_MEMIF_UL4,
MT2701_MEMIF_UL5,
MT2701_MEMIF_DLBT,
MT2701_MEMIF_ULBT,
MT2701_MEMIF_HDMI,
MT2701_MEMIF_NUM,
MT2701_IO_I2S = MT2701_MEMIF_NUM,
MT2701_IO_2ND_I2S,
MT2701_IO_3RD_I2S,
MT2701_IO_4TH_I2S,
MT2701_IO_5TH_I2S,
MT2701_IO_6TH_I2S,
MT2701_IO_MRG,
MT2701_IO_HDMI,
};
enum {
MT2701_IRQ_ASYS_IRQ1,
MT2701_IRQ_ASYS_IRQ2,
MT2701_IRQ_ASYS_IRQ3,
MT2701_IRQ_ASYS_END,
};
enum audio_base_clock {
MT2701_INFRA_SYS_AUDIO,
MT2701_TOP_AUD_MCLK_SRC0,
MT2701_TOP_AUD_MCLK_SRC1,
MT2701_TOP_AUD_A1SYS,
MT2701_TOP_AUD_A2SYS,
MT2701_AUDSYS_AFE,
MT2701_AUDSYS_AFE_CONN,
MT2701_AUDSYS_A1SYS,
MT2701_AUDSYS_A2SYS,
MT2701_BASE_CLK_NUM,
};
struct mt2701_i2s_data {
int i2s_ctrl_reg;
int i2s_asrc_fs_shift;
int i2s_asrc_fs_mask;
};
struct mt2701_i2s_path {
int mclk_rate;
int on[MTK_STREAM_NUM];
int occupied[MTK_STREAM_NUM];
const struct mt2701_i2s_data *i2s_data[MTK_STREAM_NUM];
struct clk *hop_ck[MTK_STREAM_NUM];
struct clk *sel_ck;
struct clk *div_ck;
struct clk *mclk_ck;
struct clk *asrco_ck;
};
struct mt2701_soc_variants {
bool has_one_heart_mode;
int i2s_num;
};
struct mt2701_afe_private {
struct clk *base_ck[MT2701_BASE_CLK_NUM];
struct clk *mrgif_ck;
struct clk *hadds2pll_ck;
struct clk *audio_hdmi_ck;
struct clk *audio_spdf_ck;
struct clk *audio_apll_ck;
bool mrg_enable[MTK_STREAM_NUM];
const struct mt2701_soc_variants *soc;
struct mt2701_i2s_path i2s_path[];
};
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,420 @@
// SPDX-License-Identifier: GPL-2.0
/*
* mt2701-cs42448.c -- MT2701 CS42448 ALSA SoC machine driver
*
* Copyright (c) 2016 MediaTek Inc.
* Author: Ir Lian <ir.lian@mediatek.com>
* Garlic Tseng <garlic.tseng@mediatek.com>
*/
#include <linux/module.h>
#include <sound/soc.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/pinctrl/consumer.h>
#include "mt2701-afe-common.h"
struct mt2701_cs42448_private {
int i2s1_in_mux;
struct gpio_desc *i2s1_in_mux_sel_1;
struct gpio_desc *i2s1_in_mux_sel_2;
};
static const char * const i2sin_mux_switch_text[] = {
"ADC_SDOUT2",
"ADC_SDOUT3",
"I2S_IN_1",
"I2S_IN_2",
};
static const struct soc_enum i2sin_mux_enum =
SOC_ENUM_SINGLE_EXT(4, i2sin_mux_switch_text);
static int mt2701_cs42448_i2sin1_mux_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
struct mt2701_cs42448_private *priv = snd_soc_card_get_drvdata(card);
ucontrol->value.integer.value[0] = priv->i2s1_in_mux;
return 0;
}
static int mt2701_cs42448_i2sin1_mux_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
struct mt2701_cs42448_private *priv = snd_soc_card_get_drvdata(card);
if (ucontrol->value.integer.value[0] == priv->i2s1_in_mux)
return 0;
switch (ucontrol->value.integer.value[0]) {
case 0:
gpiod_set_value(priv->i2s1_in_mux_sel_1, 0);
gpiod_set_value(priv->i2s1_in_mux_sel_2, 0);
break;
case 1:
gpiod_set_value(priv->i2s1_in_mux_sel_1, 1);
gpiod_set_value(priv->i2s1_in_mux_sel_2, 0);
break;
case 2:
gpiod_set_value(priv->i2s1_in_mux_sel_1, 0);
gpiod_set_value(priv->i2s1_in_mux_sel_2, 1);
break;
case 3:
gpiod_set_value(priv->i2s1_in_mux_sel_1, 1);
gpiod_set_value(priv->i2s1_in_mux_sel_2, 1);
break;
default:
dev_warn(card->dev, "%s invalid setting\n", __func__);
}
priv->i2s1_in_mux = ucontrol->value.integer.value[0];
return 0;
}
static const struct snd_soc_dapm_widget
mt2701_cs42448_asoc_card_dapm_widgets[] = {
SND_SOC_DAPM_LINE("Line Out Jack", NULL),
SND_SOC_DAPM_MIC("AMIC", NULL),
SND_SOC_DAPM_LINE("Tuner In", NULL),
SND_SOC_DAPM_LINE("Satellite Tuner In", NULL),
SND_SOC_DAPM_LINE("AUX In", NULL),
};
static const struct snd_kcontrol_new mt2701_cs42448_controls[] = {
SOC_DAPM_PIN_SWITCH("Line Out Jack"),
SOC_DAPM_PIN_SWITCH("AMIC"),
SOC_DAPM_PIN_SWITCH("Tuner In"),
SOC_DAPM_PIN_SWITCH("Satellite Tuner In"),
SOC_DAPM_PIN_SWITCH("AUX In"),
SOC_ENUM_EXT("I2SIN1_MUX_Switch", i2sin_mux_enum,
mt2701_cs42448_i2sin1_mux_get,
mt2701_cs42448_i2sin1_mux_set),
};
static const unsigned int mt2701_cs42448_sampling_rates[] = {48000};
static const struct snd_pcm_hw_constraint_list mt2701_cs42448_constraints_rates = {
.count = ARRAY_SIZE(mt2701_cs42448_sampling_rates),
.list = mt2701_cs42448_sampling_rates,
.mask = 0,
};
static int mt2701_cs42448_fe_ops_startup(struct snd_pcm_substream *substream)
{
int err;
err = snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
&mt2701_cs42448_constraints_rates);
if (err < 0) {
dev_err(substream->pcm->card->dev,
"%s snd_pcm_hw_constraint_list failed: 0x%x\n",
__func__, err);
return err;
}
return 0;
}
static const struct snd_soc_ops mt2701_cs42448_48k_fe_ops = {
.startup = mt2701_cs42448_fe_ops_startup,
};
static int mt2701_cs42448_be_ops_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
unsigned int mclk_rate;
unsigned int rate = params_rate(params);
unsigned int div_mclk_over_bck = rate > 192000 ? 2 : 4;
unsigned int div_bck_over_lrck = 64;
mclk_rate = rate * div_bck_over_lrck * div_mclk_over_bck;
/* mt2701 mclk */
snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_rate, SND_SOC_CLOCK_OUT);
/* codec mclk */
snd_soc_dai_set_sysclk(codec_dai, 0, mclk_rate, SND_SOC_CLOCK_IN);
return 0;
}
static const struct snd_soc_ops mt2701_cs42448_be_ops = {
.hw_params = mt2701_cs42448_be_ops_hw_params
};
enum {
DAI_LINK_FE_MULTI_CH_OUT,
DAI_LINK_FE_PCM0_IN,
DAI_LINK_FE_PCM1_IN,
DAI_LINK_FE_BT_OUT,
DAI_LINK_FE_BT_IN,
DAI_LINK_BE_I2S0,
DAI_LINK_BE_I2S1,
DAI_LINK_BE_I2S2,
DAI_LINK_BE_I2S3,
DAI_LINK_BE_MRG_BT,
};
SND_SOC_DAILINK_DEFS(fe_multi_ch_out,
DAILINK_COMP_ARRAY(COMP_CPU("PCM_multi")),
DAILINK_COMP_ARRAY(COMP_DUMMY()),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
SND_SOC_DAILINK_DEFS(fe_pcm0_in,
DAILINK_COMP_ARRAY(COMP_CPU("PCM0")),
DAILINK_COMP_ARRAY(COMP_DUMMY()),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
SND_SOC_DAILINK_DEFS(fe_pcm1_in,
DAILINK_COMP_ARRAY(COMP_CPU("PCM1")),
DAILINK_COMP_ARRAY(COMP_DUMMY()),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
SND_SOC_DAILINK_DEFS(fe_bt_out,
DAILINK_COMP_ARRAY(COMP_CPU("PCM_BT_DL")),
DAILINK_COMP_ARRAY(COMP_DUMMY()),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
SND_SOC_DAILINK_DEFS(fe_bt_in,
DAILINK_COMP_ARRAY(COMP_CPU("PCM_BT_UL")),
DAILINK_COMP_ARRAY(COMP_DUMMY()),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
SND_SOC_DAILINK_DEFS(be_i2s0,
DAILINK_COMP_ARRAY(COMP_CPU("I2S0")),
DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "cs42448")),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
SND_SOC_DAILINK_DEFS(be_i2s1,
DAILINK_COMP_ARRAY(COMP_CPU("I2S1")),
DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "cs42448")),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
SND_SOC_DAILINK_DEFS(be_i2s2,
DAILINK_COMP_ARRAY(COMP_CPU("I2S2")),
DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "cs42448")),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
SND_SOC_DAILINK_DEFS(be_i2s3,
DAILINK_COMP_ARRAY(COMP_CPU("I2S3")),
DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "cs42448")),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
SND_SOC_DAILINK_DEFS(be_mrg_bt,
DAILINK_COMP_ARRAY(COMP_CPU("MRG BT")),
DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "bt-sco-pcm-wb")),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
static struct snd_soc_dai_link mt2701_cs42448_dai_links[] = {
/* FE */
[DAI_LINK_FE_MULTI_CH_OUT] = {
.name = "mt2701-cs42448-multi-ch-out",
.stream_name = "mt2701-cs42448-multi-ch-out",
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST},
.ops = &mt2701_cs42448_48k_fe_ops,
.dynamic = 1,
.playback_only = 1,
SND_SOC_DAILINK_REG(fe_multi_ch_out),
},
[DAI_LINK_FE_PCM0_IN] = {
.name = "mt2701-cs42448-pcm0",
.stream_name = "mt2701-cs42448-pcm0-data-UL",
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST},
.ops = &mt2701_cs42448_48k_fe_ops,
.dynamic = 1,
.capture_only = 1,
SND_SOC_DAILINK_REG(fe_pcm0_in),
},
[DAI_LINK_FE_PCM1_IN] = {
.name = "mt2701-cs42448-pcm1-data-UL",
.stream_name = "mt2701-cs42448-pcm1-data-UL",
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST},
.ops = &mt2701_cs42448_48k_fe_ops,
.dynamic = 1,
.capture_only = 1,
SND_SOC_DAILINK_REG(fe_pcm1_in),
},
[DAI_LINK_FE_BT_OUT] = {
.name = "mt2701-cs42448-pcm-BT-out",
.stream_name = "mt2701-cs42448-pcm-BT",
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST},
.dynamic = 1,
.playback_only = 1,
SND_SOC_DAILINK_REG(fe_bt_out),
},
[DAI_LINK_FE_BT_IN] = {
.name = "mt2701-cs42448-pcm-BT-in",
.stream_name = "mt2701-cs42448-pcm-BT",
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST},
.dynamic = 1,
.capture_only = 1,
SND_SOC_DAILINK_REG(fe_bt_in),
},
/* BE */
[DAI_LINK_BE_I2S0] = {
.name = "mt2701-cs42448-I2S0",
.no_pcm = 1,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC
| SND_SOC_DAIFMT_GATED,
.ops = &mt2701_cs42448_be_ops,
SND_SOC_DAILINK_REG(be_i2s0),
},
[DAI_LINK_BE_I2S1] = {
.name = "mt2701-cs42448-I2S1",
.no_pcm = 1,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC
| SND_SOC_DAIFMT_GATED,
.ops = &mt2701_cs42448_be_ops,
SND_SOC_DAILINK_REG(be_i2s1),
},
[DAI_LINK_BE_I2S2] = {
.name = "mt2701-cs42448-I2S2",
.no_pcm = 1,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC
| SND_SOC_DAIFMT_GATED,
.ops = &mt2701_cs42448_be_ops,
SND_SOC_DAILINK_REG(be_i2s2),
},
[DAI_LINK_BE_I2S3] = {
.name = "mt2701-cs42448-I2S3",
.no_pcm = 1,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC
| SND_SOC_DAIFMT_GATED,
.ops = &mt2701_cs42448_be_ops,
SND_SOC_DAILINK_REG(be_i2s3),
},
[DAI_LINK_BE_MRG_BT] = {
.name = "mt2701-cs42448-MRG-BT",
.no_pcm = 1,
SND_SOC_DAILINK_REG(be_mrg_bt),
},
};
static struct snd_soc_card mt2701_cs42448_soc_card = {
.name = "mt2701-cs42448",
.owner = THIS_MODULE,
.dai_link = mt2701_cs42448_dai_links,
.num_links = ARRAY_SIZE(mt2701_cs42448_dai_links),
.controls = mt2701_cs42448_controls,
.num_controls = ARRAY_SIZE(mt2701_cs42448_controls),
.dapm_widgets = mt2701_cs42448_asoc_card_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(mt2701_cs42448_asoc_card_dapm_widgets),
};
static int mt2701_cs42448_machine_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = &mt2701_cs42448_soc_card;
int ret;
int i;
struct device_node *platform_node, *codec_node, *codec_node_bt_mrg;
struct device *dev = &pdev->dev;
struct mt2701_cs42448_private *priv =
devm_kzalloc(dev, sizeof(struct mt2701_cs42448_private),
GFP_KERNEL);
struct snd_soc_dai_link *dai_link;
if (!priv)
return -ENOMEM;
platform_node = of_parse_phandle(pdev->dev.of_node,
"mediatek,platform", 0);
if (!platform_node) {
dev_err(dev, "Property 'platform' missing or invalid\n");
return -EINVAL;
}
for_each_card_prelinks(card, i, dai_link) {
if (dai_link->platforms->name)
continue;
dai_link->platforms->of_node = platform_node;
}
card->dev = dev;
codec_node = of_parse_phandle(pdev->dev.of_node,
"mediatek,audio-codec", 0);
if (!codec_node) {
dev_err(dev,
"Property 'audio-codec' missing or invalid\n");
return -EINVAL;
}
for_each_card_prelinks(card, i, dai_link) {
if (dai_link->codecs->name)
continue;
dai_link->codecs->of_node = codec_node;
}
codec_node_bt_mrg = of_parse_phandle(pdev->dev.of_node,
"mediatek,audio-codec-bt-mrg", 0);
if (!codec_node_bt_mrg) {
dev_err(dev,
"Property 'audio-codec-bt-mrg' missing or invalid\n");
return -EINVAL;
}
mt2701_cs42448_dai_links[DAI_LINK_BE_MRG_BT].codecs->of_node
= codec_node_bt_mrg;
ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
if (ret) {
dev_err(dev, "failed to parse audio-routing: %d\n", ret);
return ret;
}
priv->i2s1_in_mux_sel_1 = devm_gpiod_get_optional(dev, "i2s1-in-sel-gpio1",
GPIOD_OUT_LOW);
if (IS_ERR(priv->i2s1_in_mux_sel_1))
return dev_err_probe(dev, PTR_ERR(priv->i2s1_in_mux_sel_1),
"error getting mux 1 selector\n");
priv->i2s1_in_mux_sel_2 = devm_gpiod_get_optional(dev, "i2s1-in-sel-gpio2",
GPIOD_OUT_LOW);
if (IS_ERR(priv->i2s1_in_mux_sel_2))
return dev_err_probe(dev, PTR_ERR(priv->i2s1_in_mux_sel_2),
"error getting mux 2 selector\n");
snd_soc_card_set_drvdata(card, priv);
ret = devm_snd_soc_register_card(dev, card);
if (ret)
dev_err(dev, "%s snd_soc_register_card fail %d\n",
__func__, ret);
return ret;
}
#ifdef CONFIG_OF
static const struct of_device_id mt2701_cs42448_machine_dt_match[] = {
{.compatible = "mediatek,mt2701-cs42448-machine",},
{}
};
MODULE_DEVICE_TABLE(of, mt2701_cs42448_machine_dt_match);
#endif
static struct platform_driver mt2701_cs42448_machine = {
.driver = {
.name = "mt2701-cs42448",
#ifdef CONFIG_OF
.of_match_table = mt2701_cs42448_machine_dt_match,
#endif
},
.probe = mt2701_cs42448_machine_probe,
};
module_platform_driver(mt2701_cs42448_machine);
/* Module information */
MODULE_DESCRIPTION("MT2701 CS42448 ALSA SoC machine driver");
MODULE_AUTHOR("Ir Lian <ir.lian@mediatek.com>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("mt2701 cs42448 soc card");
@@ -0,0 +1,114 @@
// SPDX-License-Identifier: GPL-2.0
/*
* mt2701-hdmi.c -- MT2701 HDMI ALSA SoC machine driver
*
* Copyright (c) 2026 Daniel Golle <daniel@makrotopia.org>
*
* Based on mt2701-cs42448.c
*/
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <sound/soc.h>
enum {
DAI_LINK_FE_HDMI_OUT,
DAI_LINK_BE_HDMI_I2S,
};
SND_SOC_DAILINK_DEFS(fe_hdmi_out,
DAILINK_COMP_ARRAY(COMP_CPU("PCM_HDMI")),
DAILINK_COMP_ARRAY(COMP_DUMMY()),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
SND_SOC_DAILINK_DEFS(be_hdmi_i2s,
DAILINK_COMP_ARRAY(COMP_CPU("HDMI I2S")),
DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "i2s-hifi")),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
static struct snd_soc_dai_link mt2701_hdmi_dai_links[] = {
[DAI_LINK_FE_HDMI_OUT] = {
.name = "HDMI Playback",
.stream_name = "HDMI Playback",
.trigger = { SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST },
.dynamic = 1,
.playback_only = 1,
SND_SOC_DAILINK_REG(fe_hdmi_out),
},
[DAI_LINK_BE_HDMI_I2S] = {
.name = "HDMI BE",
.no_pcm = 1,
.playback_only = 1,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBC_CFC,
SND_SOC_DAILINK_REG(be_hdmi_i2s),
},
};
static struct snd_soc_card mt2701_hdmi_soc_card = {
.name = "mt2701-hdmi",
.owner = THIS_MODULE,
.dai_link = mt2701_hdmi_dai_links,
.num_links = ARRAY_SIZE(mt2701_hdmi_dai_links),
};
static int mt2701_hdmi_machine_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = &mt2701_hdmi_soc_card;
struct device *dev = &pdev->dev;
struct device_node *platform_node;
struct device_node *codec_node;
struct snd_soc_dai_link *dai_link;
int ret;
int i;
platform_node = of_parse_phandle(dev->of_node, "mediatek,platform", 0);
if (!platform_node)
return dev_err_probe(dev, -EINVAL,
"Property 'mediatek,platform' missing\n");
for_each_card_prelinks(card, i, dai_link) {
if (dai_link->platforms->name)
continue;
dai_link->platforms->of_node = platform_node;
}
codec_node = of_parse_phandle(dev->of_node, "mediatek,audio-codec", 0);
if (!codec_node) {
of_node_put(platform_node);
return dev_err_probe(dev, -EINVAL,
"Property 'mediatek,audio-codec' missing\n");
}
mt2701_hdmi_dai_links[DAI_LINK_BE_HDMI_I2S].codecs->of_node = codec_node;
card->dev = dev;
ret = devm_snd_soc_register_card(dev, card);
of_node_put(platform_node);
of_node_put(codec_node);
return ret;
}
static const struct of_device_id mt2701_hdmi_machine_dt_match[] = {
{ .compatible = "mediatek,mt2701-hdmi-audio" },
{ .compatible = "mediatek,mt7623n-hdmi-audio" },
{}
};
MODULE_DEVICE_TABLE(of, mt2701_hdmi_machine_dt_match);
static struct platform_driver mt2701_hdmi_machine = {
.driver = {
.name = "mt2701-hdmi",
.of_match_table = mt2701_hdmi_machine_dt_match,
},
.probe = mt2701_hdmi_machine_probe,
};
module_platform_driver(mt2701_hdmi_machine);
MODULE_DESCRIPTION("MT2701 HDMI ALSA SoC machine driver");
MODULE_AUTHOR("Daniel Golle <daniel@makrotopia.org>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:mt2701-hdmi");
@@ -0,0 +1,170 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* mt2701-reg.h -- Mediatek 2701 audio driver reg definition
*
* Copyright (c) 2016 MediaTek Inc.
* Author: Garlic Tseng <garlic.tseng@mediatek.com>
*/
#ifndef _MT2701_REG_H_
#define _MT2701_REG_H_
#define AUDIO_TOP_CON0 0x0000
#define AUDIO_TOP_CON3 0x000c
#define AUDIO_TOP_CON4 0x0010
#define AUDIO_TOP_CON5 0x0014
#define AFE_DAIBT_CON0 0x001c
#define AFE_MRGIF_CON 0x003c
#define AFE_HDMI_OUT_CON0 0x0370
#define AFE_HDMI_OUT_BASE 0x0374
#define AFE_HDMI_OUT_CUR 0x0378
#define AFE_HDMI_OUT_END 0x037c
#define AFE_HDMI_CONN0 0x0390
#define AFE_8CH_I2S_OUT_CON 0x0394
#define ASMI_TIMING_CON1 0x0100
#define ASMO_TIMING_CON1 0x0104
#define PWR1_ASM_CON1 0x0108
#define ASYS_TOP_CON 0x0600
#define ASYS_I2SIN1_CON 0x0604
#define ASYS_I2SIN2_CON 0x0608
#define ASYS_I2SIN3_CON 0x060c
#define ASYS_I2SIN4_CON 0x0610
#define ASYS_I2SIN5_CON 0x0614
#define ASYS_I2SO1_CON 0x061C
#define ASYS_I2SO2_CON 0x0620
#define ASYS_I2SO3_CON 0x0624
#define ASYS_I2SO4_CON 0x0628
#define ASYS_I2SO5_CON 0x062c
#define PWR2_TOP_CON 0x0634
#define AFE_CONN0 0x06c0
#define AFE_CONN1 0x06c4
#define AFE_CONN2 0x06c8
#define AFE_CONN3 0x06cc
#define AFE_CONN14 0x06f8
#define AFE_CONN15 0x06fc
#define AFE_CONN16 0x0700
#define AFE_CONN17 0x0704
#define AFE_CONN18 0x0708
#define AFE_CONN19 0x070c
#define AFE_CONN20 0x0710
#define AFE_CONN21 0x0714
#define AFE_CONN22 0x0718
#define AFE_CONN23 0x071c
#define AFE_CONN24 0x0720
#define AFE_CONN41 0x0764
#define ASYS_IRQ1_CON 0x0780
#define ASYS_IRQ2_CON 0x0784
#define ASYS_IRQ3_CON 0x0788
#define ASYS_IRQ_CLR 0x07c0
#define ASYS_IRQ_STATUS 0x07c4
#define PWR2_ASM_CON1 0x1070
#define AFE_DAC_CON0 0x1200
#define AFE_DAC_CON1 0x1204
#define AFE_DAC_CON2 0x1208
#define AFE_DAC_CON3 0x120c
#define AFE_DAC_CON4 0x1210
#define AFE_MEMIF_HD_CON1 0x121c
#define AFE_MEMIF_PBUF_SIZE 0x1238
#define AFE_MEMIF_HD_CON0 0x123c
#define AFE_DL1_BASE 0x1240
#define AFE_DL1_CUR 0x1244
#define AFE_DL2_BASE 0x1250
#define AFE_DL2_CUR 0x1254
#define AFE_DL3_BASE 0x1260
#define AFE_DL3_CUR 0x1264
#define AFE_DL4_BASE 0x1270
#define AFE_DL4_CUR 0x1274
#define AFE_DL5_BASE 0x1280
#define AFE_DL5_CUR 0x1284
#define AFE_DLMCH_BASE 0x12a0
#define AFE_DLMCH_CUR 0x12a4
#define AFE_ARB1_BASE 0x12b0
#define AFE_ARB1_CUR 0x12b4
#define AFE_VUL_BASE 0x1300
#define AFE_VUL_CUR 0x130c
#define AFE_UL2_BASE 0x1310
#define AFE_UL2_END 0x1318
#define AFE_UL2_CUR 0x131c
#define AFE_UL3_BASE 0x1320
#define AFE_UL3_END 0x1328
#define AFE_UL3_CUR 0x132c
#define AFE_UL4_BASE 0x1330
#define AFE_UL4_END 0x1338
#define AFE_UL4_CUR 0x133c
#define AFE_UL5_BASE 0x1340
#define AFE_UL5_END 0x1348
#define AFE_UL5_CUR 0x134c
#define AFE_DAI_BASE 0x1370
#define AFE_DAI_CUR 0x137c
/* AFE_DAIBT_CON0 (0x001c) */
#define AFE_DAIBT_CON0_DAIBT_EN (0x1 << 0)
#define AFE_DAIBT_CON0_BT_FUNC_EN (0x1 << 1)
#define AFE_DAIBT_CON0_BT_FUNC_RDY (0x1 << 3)
#define AFE_DAIBT_CON0_BT_WIDE_MODE_EN (0x1 << 9)
#define AFE_DAIBT_CON0_MRG_USE (0x1 << 12)
/* PWR1_ASM_CON1 (0x0108) */
#define PWR1_ASM_CON1_INIT_VAL (0x492)
/* AFE_MRGIF_CON (0x003c) */
#define AFE_MRGIF_CON_MRG_EN (0x1 << 0)
#define AFE_MRGIF_CON_MRG_I2S_EN (0x1 << 16)
#define AFE_MRGIF_CON_I2S_MODE_MASK (0xf << 20)
#define AFE_MRGIF_CON_I2S_MODE_32K (0x4 << 20)
/* ASYS_TOP_CON (0x0600) */
#define ASYS_TOP_CON_ASYS_TIMING_ON (0x3 << 0)
/* PWR2_ASM_CON1 (0x1070) */
#define PWR2_ASM_CON1_INIT_VAL (0x492492)
/* AFE_DAC_CON0 (0x1200) */
#define AFE_DAC_CON0_AFE_ON (0x1 << 0)
/* AFE_MEMIF_PBUF_SIZE (0x1238) */
#define AFE_MEMIF_PBUF_SIZE_DLM_MASK (0x1 << 29)
#define AFE_MEMIF_PBUF_SIZE_PAIR_INTERLEAVE (0x0 << 29)
#define AFE_MEMIF_PBUF_SIZE_FULL_INTERLEAVE (0x1 << 29)
#define DLMCH_BIT_WIDTH_MASK (0x1 << 28)
#define AFE_MEMIF_PBUF_SIZE_DLM_CH_MASK (0xf << 24)
#define AFE_MEMIF_PBUF_SIZE_DLM_CH(x) ((x) << 24)
#define AFE_MEMIF_PBUF_SIZE_DLM_BYTE_MASK (0x3 << 12)
#define AFE_MEMIF_PBUF_SIZE_DLM_32BYTES (0x1 << 12)
/* AUDIO_TOP_CON3 (0x000c) -- HDMI BCK divider */
#define AUDIO_TOP_CON3_HDMI_BCK_DIV_MASK (0x3f << 8)
#define AUDIO_TOP_CON3_HDMI_BCK_DIV(x) (((x) & 0x3f) << 8)
/* AFE_HDMI_OUT_CON0 (0x0370) */
#define AFE_HDMI_OUT_CON0_OUT_ON (0x1 << 0)
#define AFE_HDMI_OUT_CON0_BIT_WIDTH_MASK (0x1 << 1)
#define AFE_HDMI_OUT_CON0_BIT_WIDTH_16 (0x0 << 1)
#define AFE_HDMI_OUT_CON0_BIT_WIDTH_32 (0x1 << 1)
#define AFE_HDMI_OUT_CON0_CH_NUM_MASK (0xf << 4)
#define AFE_HDMI_OUT_CON0_CH_NUM(x) (((x) & 0xf) << 4)
/* AFE_8CH_I2S_OUT_CON (0x0394) -- on-SoC 8-channel I2S that feeds HDMI TX */
#define AFE_8CH_I2S_OUT_CON_EN (0x1 << 0)
#define AFE_8CH_I2S_OUT_CON_BCK_INV (0x1 << 1)
#define AFE_8CH_I2S_OUT_CON_LRCK_INV (0x1 << 2)
#define AFE_8CH_I2S_OUT_CON_I2S_DELAY (0x1 << 3)
#define AFE_8CH_I2S_OUT_CON_WLEN_MASK (0x3 << 4)
#define AFE_8CH_I2S_OUT_CON_WLEN_16BIT (0x1 << 4)
#define AFE_8CH_I2S_OUT_CON_WLEN_24BIT (0x2 << 4)
#define AFE_8CH_I2S_OUT_CON_WLEN_32BIT (0x3 << 4)
/* I2S in/out register bit control */
#define ASYS_I2S_CON_FS (0x1f << 8)
#define ASYS_I2S_CON_FS_SET(x) ((x) << 8)
#define ASYS_I2S_CON_RESET (0x1 << 30)
#define ASYS_I2S_CON_I2S_EN (0x1 << 0)
#define ASYS_I2S_CON_ONE_HEART_MODE (0x1 << 16)
#define ASYS_I2S_CON_I2S_COUPLE_MODE (0x1 << 17)
/* 0:EIAJ 1:I2S */
#define ASYS_I2S_CON_I2S_MODE (0x1 << 3)
#define ASYS_I2S_CON_WIDE_MODE (0x1 << 1)
#define ASYS_I2S_CON_WIDE_MODE_SET(x) ((x) << 1)
#define ASYS_I2S_IN_PHASE_FIX (0x1 << 31)
#endif
@@ -0,0 +1,182 @@
// SPDX-License-Identifier: GPL-2.0
/*
* mt2701-wm8960.c -- MT2701 WM8960 ALSA SoC machine driver
*
* Copyright (c) 2017 MediaTek Inc.
* Author: Ryder Lee <ryder.lee@mediatek.com>
*/
#include <linux/module.h>
#include <sound/soc.h>
#include "mt2701-afe-common.h"
static const struct snd_soc_dapm_widget mt2701_wm8960_widgets[] = {
SND_SOC_DAPM_HP("Headphone", NULL),
SND_SOC_DAPM_MIC("AMIC", NULL),
};
static const struct snd_kcontrol_new mt2701_wm8960_controls[] = {
SOC_DAPM_PIN_SWITCH("Headphone"),
SOC_DAPM_PIN_SWITCH("AMIC"),
};
static int mt2701_wm8960_be_ops_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
unsigned int mclk_rate;
unsigned int rate = params_rate(params);
unsigned int div_mclk_over_bck = rate > 192000 ? 2 : 4;
unsigned int div_bck_over_lrck = 64;
mclk_rate = rate * div_bck_over_lrck * div_mclk_over_bck;
snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_rate, SND_SOC_CLOCK_OUT);
snd_soc_dai_set_sysclk(codec_dai, 0, mclk_rate, SND_SOC_CLOCK_IN);
return 0;
}
static const struct snd_soc_ops mt2701_wm8960_be_ops = {
.hw_params = mt2701_wm8960_be_ops_hw_params
};
SND_SOC_DAILINK_DEFS(playback,
DAILINK_COMP_ARRAY(COMP_CPU("PCMO0")),
DAILINK_COMP_ARRAY(COMP_DUMMY()),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
SND_SOC_DAILINK_DEFS(capture,
DAILINK_COMP_ARRAY(COMP_CPU("PCM0")),
DAILINK_COMP_ARRAY(COMP_DUMMY()),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
SND_SOC_DAILINK_DEFS(codec,
DAILINK_COMP_ARRAY(COMP_CPU("I2S0")),
DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8960-hifi")),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
static struct snd_soc_dai_link mt2701_wm8960_dai_links[] = {
/* FE */
{
.name = "wm8960-playback",
.stream_name = "wm8960-playback",
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST},
.dynamic = 1,
.playback_only = 1,
SND_SOC_DAILINK_REG(playback),
},
{
.name = "wm8960-capture",
.stream_name = "wm8960-capture",
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST},
.dynamic = 1,
.capture_only = 1,
SND_SOC_DAILINK_REG(capture),
},
/* BE */
{
.name = "wm8960-codec",
.no_pcm = 1,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC
| SND_SOC_DAIFMT_GATED,
.ops = &mt2701_wm8960_be_ops,
SND_SOC_DAILINK_REG(codec),
},
};
static struct snd_soc_card mt2701_wm8960_card = {
.name = "mt2701-wm8960",
.owner = THIS_MODULE,
.dai_link = mt2701_wm8960_dai_links,
.num_links = ARRAY_SIZE(mt2701_wm8960_dai_links),
.controls = mt2701_wm8960_controls,
.num_controls = ARRAY_SIZE(mt2701_wm8960_controls),
.dapm_widgets = mt2701_wm8960_widgets,
.num_dapm_widgets = ARRAY_SIZE(mt2701_wm8960_widgets),
};
static int mt2701_wm8960_machine_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = &mt2701_wm8960_card;
struct device_node *platform_node, *codec_node;
struct snd_soc_dai_link *dai_link;
int ret, i;
platform_node = of_parse_phandle(pdev->dev.of_node,
"mediatek,platform", 0);
if (!platform_node) {
dev_err(&pdev->dev, "Property 'platform' missing or invalid\n");
return -EINVAL;
}
for_each_card_prelinks(card, i, dai_link) {
if (dai_link->platforms->name)
continue;
dai_link->platforms->of_node = platform_node;
}
card->dev = &pdev->dev;
codec_node = of_parse_phandle(pdev->dev.of_node,
"mediatek,audio-codec", 0);
if (!codec_node) {
dev_err(&pdev->dev,
"Property 'audio-codec' missing or invalid\n");
ret = -EINVAL;
goto put_platform_node;
}
for_each_card_prelinks(card, i, dai_link) {
if (dai_link->codecs->name)
continue;
dai_link->codecs->of_node = codec_node;
}
ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
if (ret) {
dev_err(&pdev->dev, "failed to parse audio-routing: %d\n", ret);
goto put_codec_node;
}
ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret)
dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
__func__, ret);
put_codec_node:
of_node_put(codec_node);
put_platform_node:
of_node_put(platform_node);
return ret;
}
#ifdef CONFIG_OF
static const struct of_device_id mt2701_wm8960_machine_dt_match[] = {
{.compatible = "mediatek,mt2701-wm8960-machine",},
{}
};
MODULE_DEVICE_TABLE(of, mt2701_wm8960_machine_dt_match);
#endif
static struct platform_driver mt2701_wm8960_machine = {
.driver = {
.name = "mt2701-wm8960",
#ifdef CONFIG_OF
.of_match_table = mt2701_wm8960_machine_dt_match,
#endif
},
.probe = mt2701_wm8960_machine_probe,
};
module_platform_driver(mt2701_wm8960_machine);
/* Module information */
MODULE_DESCRIPTION("MT2701 WM8960 ALSA SoC machine driver");
MODULE_AUTHOR("Ryder Lee <ryder.lee@mediatek.com>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("mt2701 wm8960 soc card");