blob: 2bf0528fd80fb32b1c25b598b4feefe1f834f2af [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* mt8696-evb.c -- MT8696 machine driver
*
* Copyright (c) 2022 MediaTek Inc.
* Author: Sail Yang <sail.yang@mediatek.com>
*
*/
#include <linux/module.h>
#include <linux/of_gpio.h>
#include <sound/soc.h>
#include <sound/pcm_params.h>
#include "mt8696-afe-common.h"
#define BACKEND_WITH_ENDPOINT
#define PREFIX "mediatek,"
#define ENUM_TO_STR(enum) #enum
enum PINCTRL_PIN_STATE {
PIN_STATE_DEFAULT = 0,
PIN_STATE_EXTAMP_ON,
PIN_STATE_EXTAMP_OFF,
PIN_STATE_MAX
};
static const char * const mt8696_evb_pin_str[PIN_STATE_MAX] = {
"default",
"extamp_on",
"extamp_off",
};
struct mt8696_evb_etdm_ctrl_data {
unsigned int mck_multp_in;
unsigned int mck_multp_out;
unsigned int lrck_width_in;
unsigned int lrck_width_out;
unsigned int fix_rate_in;
unsigned int fix_rate_out;
unsigned int fix_bit_width_in;
unsigned int fix_bit_width_out;
unsigned int fix_channels_in;
unsigned int fix_channels_out;
};
struct mt8696_evb_priv {
struct pinctrl *pinctrl;
struct pinctrl_state *pin_states[PIN_STATE_MAX];
struct mt8696_evb_etdm_ctrl_data etdm_data[MT8696_ETDM_SETS];
struct device_node *afe_plat_node;
};
struct mt8696_dai_link_prop {
char *name;
unsigned int link_id;
};
static const struct snd_soc_dapm_widget mt8696_evb_widgets[] = {
#ifdef BACKEND_WITH_ENDPOINT
SND_SOC_DAPM_OUTPUT("HDMI Output"),
SND_SOC_DAPM_OUTPUT("BTSCO Output"),
SND_SOC_DAPM_INPUT("BTSCO Input"),
#endif
};
static const struct snd_soc_dapm_route mt8696_evb_routes[] = {
#ifdef BACKEND_WITH_ENDPOINT
{"HDMI Output", NULL, "ETDM1 Playback"},
{"BTSCO Output", NULL, "ETDM2 Playback"},
{"ETDM2 Capture", NULL, "BTSCO Input"},
#endif
};
enum {
/* FE */
DAI_LINK_AFE_FE_BASE = 0,
DAI_LINK_DL8_PLAYBACK = DAI_LINK_AFE_FE_BASE,
DAI_LINK_DL5_PLAYBACK,
DAI_LINK_UL1_CAPTURE,
#ifdef CONFIG_SND_SOC_MTK_BTCVSD
DAI_LINK_BTCVSD_RX,
DAI_LINK_BTCVSD_TX,
DAI_LINK_AFE_FE_END = DAI_LINK_BTCVSD_TX,
#else
DAI_LINK_AFE_FE_END = DAI_LINK_UL1_CAPTURE,
#endif
/* BE */
DAI_LINK_AFE_BE_BASE,
DAI_LINK_ETDM1_OUT = DAI_LINK_AFE_BE_BASE,
DAI_LINK_ETDM2_IN,
DAI_LINK_ETDM2_OUT,
DAI_LINK_AFE_BE_END = DAI_LINK_ETDM2_OUT,
DAI_LINK_NUM
};
static int link_to_dai(int link_id)
{
switch (link_id) {
case DAI_LINK_ETDM1_OUT:
return MT8696_AFE_IO_ETDM1_OUT;
case DAI_LINK_ETDM2_OUT:
return MT8696_AFE_IO_ETDM2_OUT;
case DAI_LINK_ETDM2_IN:
return MT8696_AFE_IO_ETDM2_IN;
default:
break;
}
return -1;
}
static int mt8696_evb_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct mt8696_evb_priv *priv = snd_soc_card_get_drvdata(rtd->card);
struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
int id = rtd->dai_link->id;
struct mt8696_evb_etdm_ctrl_data *etdm;
unsigned int mclk_multiplier = 0;
unsigned int mclk = 0;
unsigned int lrck_width = 0;
int slot = 0;
int slot_width = 0;
unsigned int slot_bitmask = 0;
unsigned int idx;
int ret;
if (id == DAI_LINK_ETDM1_OUT) {
idx = MT8696_ETDM1;
etdm = &priv->etdm_data[idx];
mclk_multiplier = etdm->mck_multp_out;
lrck_width = etdm->lrck_width_out;
} else if (id == DAI_LINK_ETDM2_OUT) {
idx = MT8696_ETDM2;
etdm = &priv->etdm_data[idx];
mclk_multiplier = etdm->mck_multp_out;
lrck_width = etdm->lrck_width_out;
} else if (id == DAI_LINK_ETDM2_IN) {
idx = MT8696_ETDM2;
etdm = &priv->etdm_data[idx];
mclk_multiplier = etdm->mck_multp_in;
lrck_width = etdm->lrck_width_in;
}
if (mclk_multiplier > 0) {
mclk = mclk_multiplier * params_rate(params);
ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
SND_SOC_CLOCK_OUT);
if (!ret)
return ret;
}
slot_width = lrck_width;
if (slot_width > 0) {
slot = params_channels(params);
slot_bitmask = GENMASK(slot - 1, 0);
ret = snd_soc_dai_set_tdm_slot(cpu_dai,
slot_bitmask,
slot_bitmask,
slot,
slot_width);
if (!ret)
return ret;
}
return 0;
}
static int mt8696_evb_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
{
struct mt8696_evb_priv *priv = snd_soc_card_get_drvdata(rtd->card);
int id = rtd->dai_link->id;
struct mt8696_evb_etdm_ctrl_data *etdm;
unsigned int fix_rate = 0;
unsigned int fix_bit_width = 0;
unsigned int fix_channels = 0;
unsigned int idx;
if (id == DAI_LINK_ETDM1_OUT) {
idx = MT8696_ETDM1;
etdm = &priv->etdm_data[idx];
fix_rate = etdm->fix_rate_out;
fix_bit_width = etdm->fix_bit_width_out;
fix_channels = etdm->fix_channels_out;
} else if (id == DAI_LINK_ETDM2_OUT) {
idx = MT8696_ETDM2;
etdm = &priv->etdm_data[idx];
fix_rate = etdm->fix_rate_out;
fix_bit_width = etdm->fix_bit_width_out;
fix_channels = etdm->fix_channels_out;
} else if (id == DAI_LINK_ETDM2_IN) {
idx = MT8696_ETDM2;
etdm = &priv->etdm_data[idx];
fix_rate = etdm->fix_rate_in;
fix_bit_width = etdm->fix_bit_width_in;
fix_channels = etdm->fix_channels_in;
}
if (fix_rate > 0) {
struct snd_interval *rate =
hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
rate->max = rate->min = fix_rate;
}
if (fix_bit_width > 0) {
struct snd_mask *mask =
hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
if (fix_bit_width == 32) {
snd_mask_none(mask);
snd_mask_set(mask, SNDRV_PCM_FORMAT_S32_LE);
} else if (fix_bit_width == 16) {
snd_mask_none(mask);
snd_mask_set(mask, SNDRV_PCM_FORMAT_S16_LE);
}
}
if (fix_channels > 0) {
struct snd_interval *channels = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_CHANNELS);
channels->min = channels->max = fix_channels;
}
return 0;
}
static struct snd_soc_ops mt8696_evb_etdm_ops = {
.hw_params = mt8696_evb_hw_params,
};
/* FE */
SND_SOC_DAILINK_DEFS(HDMI_OUTPUT,
DAILINK_COMP_ARRAY(COMP_CPU("DL8")),
DAILINK_COMP_ARRAY(COMP_DUMMY()),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
SND_SOC_DAILINK_DEFS(BTSCO_OUTPUT,
DAILINK_COMP_ARRAY(COMP_CPU("DL5")),
DAILINK_COMP_ARRAY(COMP_DUMMY()),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
SND_SOC_DAILINK_DEFS(BTSCO_INPUT,
DAILINK_COMP_ARRAY(COMP_CPU("UL1")),
DAILINK_COMP_ARRAY(COMP_DUMMY()),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
#ifdef CONFIG_SND_SOC_MTK_BTCVSD
SND_SOC_DAILINK_DEFS(BTCVSD_RX,
DAILINK_COMP_ARRAY(COMP_DUMMY()),
DAILINK_COMP_ARRAY(COMP_DUMMY()),
DAILINK_COMP_ARRAY(COMP_PLATFORM("mt-soc-btcvsd-rx-pcm")));
SND_SOC_DAILINK_DEFS(BTCVSD_TX,
DAILINK_COMP_ARRAY(COMP_DUMMY()),
DAILINK_COMP_ARRAY(COMP_DUMMY()),
DAILINK_COMP_ARRAY(COMP_PLATFORM("mt-soc-btcvsd-tx-pcm")));
#endif
/* BE */
SND_SOC_DAILINK_DEFS(ETDM1_OUT_BE,
DAILINK_COMP_ARRAY(COMP_CPU("ETDM1_OUT")),
DAILINK_COMP_ARRAY(COMP_DUMMY()),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
SND_SOC_DAILINK_DEFS(ETDM2_IN_BE,
DAILINK_COMP_ARRAY(COMP_CPU("ETDM2_IN")),
DAILINK_COMP_ARRAY(COMP_DUMMY()),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
SND_SOC_DAILINK_DEFS(ETDM2_OUT_BE,
DAILINK_COMP_ARRAY(COMP_CPU("ETDM2_OUT")),
DAILINK_COMP_ARRAY(COMP_DUMMY()),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
/* Digital audio interface glue - connects codec <---> CPU */
static struct snd_soc_dai_link mt8696_evb_dais[] = {
/* Front End DAI links */
[DAI_LINK_DL8_PLAYBACK] = {
.name = "HDMI_OUTPUT",
.stream_name = "HDMI_Playback",
.id = DAI_LINK_DL8_PLAYBACK,
.trigger = {
SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST
},
.dynamic = 1,
.dpcm_playback = 1,
SND_SOC_DAILINK_REG(HDMI_OUTPUT),
},
[DAI_LINK_DL5_PLAYBACK] = {
.name = "BTSCO_OUTPUT",
.stream_name = "DL5_Playback",
.id = DAI_LINK_DL5_PLAYBACK,
.trigger = {
SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST
},
.dynamic = 1,
.dpcm_playback = 1,
SND_SOC_DAILINK_REG(BTSCO_OUTPUT),
},
[DAI_LINK_UL1_CAPTURE] = {
.name = "BTSCO_INPUT",
.stream_name = "UL1_Capture",
.id = DAI_LINK_UL1_CAPTURE,
.trigger = {
SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST
},
.dynamic = 1,
.dpcm_capture = 1,
SND_SOC_DAILINK_REG(BTSCO_INPUT),
},
#ifdef CONFIG_SND_SOC_MTK_BTCVSD
[DAI_LINK_BTCVSD_RX] = {
.name = "BTCVSD_RX",
.stream_name = "BTCVSD_Capture",
.capture_only = 1,
SND_SOC_DAILINK_REG(BTCVSD_RX),
},
[DAI_LINK_BTCVSD_TX] = {
.name = "BTCVSD_TX",
.stream_name = "BTCVSD_Playback",
.playback_only = 1,
SND_SOC_DAILINK_REG(BTCVSD_TX),
},
#endif
/* Back End DAI links */
[DAI_LINK_ETDM1_OUT] = {
.name = "ETDM1_OUT BE",
.no_pcm = 1,
.id = DAI_LINK_ETDM1_OUT,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS,
.ops = &mt8696_evb_etdm_ops,
.dpcm_playback = 1,
SND_SOC_DAILINK_REG(ETDM1_OUT_BE),
},
[DAI_LINK_ETDM2_IN] = {
.name = "ETDM2_IN BE",
.no_pcm = 1,
.id = DAI_LINK_ETDM2_IN,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS,
.ops = &mt8696_evb_etdm_ops,
.dpcm_capture = 1,
SND_SOC_DAILINK_REG(ETDM2_IN_BE),
},
[DAI_LINK_ETDM2_OUT] = {
.name = "ETDM2_OUT BE",
.no_pcm = 1,
.id = DAI_LINK_ETDM2_OUT,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS,
.ops = &mt8696_evb_etdm_ops,
.dpcm_playback = 1,
SND_SOC_DAILINK_REG(ETDM2_OUT_BE),
},
};
static int mt8696_evb_gpio_probe(struct snd_soc_card *card)
{
struct mt8696_evb_priv *priv = snd_soc_card_get_drvdata(card);
int ret = 0;
int i;
priv->pinctrl = devm_pinctrl_get(card->dev);
if (IS_ERR(priv->pinctrl)) {
ret = PTR_ERR(priv->pinctrl);
dev_info(card->dev, "%s devm_pinctrl_get failed %d\n",
__func__, ret);
return ret;
}
for (i = 0 ; i < PIN_STATE_MAX ; i++) {
priv->pin_states[i] = pinctrl_lookup_state(priv->pinctrl,
mt8696_evb_pin_str[i]);
if (IS_ERR(priv->pin_states[i])) {
ret = PTR_ERR(priv->pin_states[i]);
dev_dbg(card->dev, "%s Can't find pin state %s %d\n",
__func__, mt8696_evb_pin_str[i], ret);
}
}
if (IS_ERR(priv->pin_states[PIN_STATE_DEFAULT])) {
dev_info(card->dev, "%s can't find default pin state\n",
__func__);
return 0;
}
/* default state */
ret = pinctrl_select_state(priv->pinctrl,
priv->pin_states[PIN_STATE_DEFAULT]);
if (ret)
dev_info(card->dev, "%s failed to select state %d\n",
__func__, ret);
/* turn off ext amp if exist */
if (!IS_ERR(priv->pin_states[PIN_STATE_EXTAMP_OFF])) {
ret = pinctrl_select_state(priv->pinctrl,
priv->pin_states[PIN_STATE_EXTAMP_OFF]);
if (ret)
dev_dbg(card->dev,
"%s failed to select state %d\n",
__func__, ret);
}
return ret;
}
static void mt8696_evb_parse_of(struct snd_soc_card *card,
struct device_node *np)
{
struct mt8696_evb_priv *priv = snd_soc_card_get_drvdata(card);
size_t i, j;
int ret;
char prop[128];
const char *str;
unsigned int val;
int sret = 0;
static const struct mt8696_dai_link_prop of_dai_links_daifmt[] = {
{ "dl8", DAI_LINK_DL8_PLAYBACK },
{ "dl5", DAI_LINK_DL5_PLAYBACK },
{ "ul1", DAI_LINK_UL1_CAPTURE },
{ "etdm1-out", DAI_LINK_ETDM1_OUT },
{ "etdm2-out", DAI_LINK_ETDM2_OUT },
{ "etdm2-in", DAI_LINK_ETDM2_IN },
};
static const struct mt8696_dai_link_prop of_dai_links_etdm[] = {
{ "etdm1-out", DAI_LINK_ETDM1_OUT },
{ "etdm2-out", DAI_LINK_ETDM2_OUT },
{ "etdm2-in", DAI_LINK_ETDM2_IN },
};
struct {
char *name;
unsigned int val;
} of_fmt_table[] = {
{ "i2s", SND_SOC_DAIFMT_I2S },
{ "right_j", SND_SOC_DAIFMT_RIGHT_J },
{ "left_j", SND_SOC_DAIFMT_LEFT_J },
{ "dsp_a", SND_SOC_DAIFMT_DSP_A },
{ "dsp_b", SND_SOC_DAIFMT_DSP_B },
{ "ac97", SND_SOC_DAIFMT_AC97 },
{ "pdm", SND_SOC_DAIFMT_PDM },
{ "msb", SND_SOC_DAIFMT_MSB },
{ "lsb", SND_SOC_DAIFMT_LSB },
};
snd_soc_of_parse_card_name(card, PREFIX"card-name");
for (i = 0; i < ARRAY_SIZE(of_dai_links_daifmt); i++) {
struct snd_soc_dai_link *dai_link =
&mt8696_evb_dais[of_dai_links_daifmt[i].link_id];
bool lrck_inverse = false;
bool bck_inverse = false;
/* parse format */
sret = snprintf(prop, sizeof(prop), PREFIX"%s-format",
of_dai_links_daifmt[i].name);
if (sret < 0)
dev_dbg(card->dev, "%s snprintf error\n", __func__);
ret = of_property_read_string(np, prop, &str);
if (ret == 0) {
unsigned int format = 0;
for (j = 0; j < ARRAY_SIZE(of_fmt_table); j++) {
if (strcmp(str, of_fmt_table[i].name) == 0) {
format |= of_fmt_table[i].val;
break;
}
}
dai_link->dai_fmt &= ~SND_SOC_DAIFMT_FORMAT_MASK;
dai_link->dai_fmt |= format;
}
/* parse clock mode */
sret = snprintf(prop, sizeof(prop), PREFIX"%s-master-clock",
of_dai_links_daifmt[i].name);
if (sret < 0)
dev_dbg(card->dev, "%s snprintf error\n", __func__);
ret = of_property_read_u32(np, prop, &val);
if (ret == 0) {
dai_link->dai_fmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
if (val)
dai_link->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS;
else
dai_link->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
}
/* parse lrck inverse */
sret = snprintf(prop, sizeof(prop), PREFIX"%s-lrck-inverse",
of_dai_links_daifmt[i].name);
if (sret < 0)
dev_dbg(card->dev, "%s snprintf error\n", __func__);
lrck_inverse = of_property_read_bool(np, prop);
/* parse bck inverse */
sret = snprintf(prop, sizeof(prop), PREFIX"%s-bck-inverse",
of_dai_links_daifmt[i].name);
if (sret < 0)
dev_dbg(card->dev, "%s snprintf error\n", __func__);
bck_inverse = of_property_read_bool(np, prop);
dai_link->dai_fmt &= ~SND_SOC_DAIFMT_INV_MASK;
if (lrck_inverse && bck_inverse)
dai_link->dai_fmt |= SND_SOC_DAIFMT_IB_IF;
else if (lrck_inverse && !bck_inverse)
dai_link->dai_fmt |= SND_SOC_DAIFMT_IB_NF;
else if (!lrck_inverse && bck_inverse)
dai_link->dai_fmt |= SND_SOC_DAIFMT_NB_IF;
else
dai_link->dai_fmt |= SND_SOC_DAIFMT_NB_NF;
}
for (i = 0; i < ARRAY_SIZE(of_dai_links_etdm); i++) {
unsigned int link_id = of_dai_links_etdm[i].link_id;
struct snd_soc_dai_link *dai_link = &mt8696_evb_dais[link_id];
struct mt8696_evb_etdm_ctrl_data *etdm;
struct device_node *codec_node;
/* parse mclk multiplier */
sret = snprintf(prop, sizeof(prop), PREFIX"%s-mclk-multiplier",
of_dai_links_etdm[i].name);
if (sret < 0)
dev_dbg(card->dev, "%s snprintf error\n", __func__);
ret = of_property_read_u32(np, prop, &val);
if (ret == 0) {
switch (link_id) {
case DAI_LINK_ETDM1_OUT:
etdm = &priv->etdm_data[MT8696_ETDM1];
etdm->mck_multp_out = val;
break;
case DAI_LINK_ETDM2_OUT:
etdm = &priv->etdm_data[MT8696_ETDM2];
etdm->mck_multp_out = val;
break;
case DAI_LINK_ETDM2_IN:
etdm = &priv->etdm_data[MT8696_ETDM2];
etdm->mck_multp_in = val;
break;
default:
break;
}
}
/* parse lrck width */
sret = snprintf(prop, sizeof(prop), PREFIX"%s-lrck-width",
of_dai_links_etdm[i].name);
if (sret < 0)
dev_dbg(card->dev, "%s snprintf error\n", __func__);
ret = of_property_read_u32(np, prop, &val);
if (ret == 0) {
switch (link_id) {
case DAI_LINK_ETDM1_OUT:
etdm = &priv->etdm_data[MT8696_ETDM1];
etdm->lrck_width_out = val;
break;
case DAI_LINK_ETDM2_OUT:
etdm = &priv->etdm_data[MT8696_ETDM2];
etdm->lrck_width_out = val;
break;
case DAI_LINK_ETDM2_IN:
etdm = &priv->etdm_data[MT8696_ETDM2];
etdm->lrck_width_in = val;
break;
default:
break;
}
}
/* parse fix rate */
sret = snprintf(prop, sizeof(prop), PREFIX"%s-fix-rate",
of_dai_links_etdm[i].name);
if (sret < 0)
dev_dbg(card->dev, "%s snprintf error\n", __func__);
ret = of_property_read_u32(np, prop, &val);
if (ret == 0 && mt8696_afe_rate_supported(val,
link_to_dai(link_id))) {
switch (link_id) {
case DAI_LINK_ETDM1_OUT:
etdm = &priv->etdm_data[MT8696_ETDM1];
etdm->fix_rate_out = val;
break;
case DAI_LINK_ETDM2_OUT:
etdm = &priv->etdm_data[MT8696_ETDM2];
etdm->fix_rate_out = val;
break;
case DAI_LINK_ETDM2_IN:
etdm = &priv->etdm_data[MT8696_ETDM2];
etdm->fix_rate_in = val;
break;
default:
break;
}
dai_link->be_hw_params_fixup =
mt8696_evb_be_hw_params_fixup;
}
/* parse fix bit width */
sret = snprintf(prop, sizeof(prop), PREFIX"%s-fix-bit-width",
of_dai_links_etdm[i].name);
if (sret < 0)
dev_dbg(card->dev, "%s snprintf error\n", __func__);
ret = of_property_read_u32(np, prop, &val);
if (ret == 0 && (val == 32 || val == 16)) {
switch (link_id) {
case DAI_LINK_ETDM1_OUT:
etdm = &priv->etdm_data[MT8696_ETDM1];
etdm->fix_bit_width_out = val;
break;
case DAI_LINK_ETDM2_OUT:
etdm = &priv->etdm_data[MT8696_ETDM2];
etdm->fix_bit_width_out = val;
break;
case DAI_LINK_ETDM2_IN:
etdm = &priv->etdm_data[MT8696_ETDM2];
etdm->fix_bit_width_in = val;
break;
default:
break;
}
dai_link->be_hw_params_fixup =
mt8696_evb_be_hw_params_fixup;
}
/* parse fix channels */
sret = snprintf(prop, sizeof(prop), PREFIX"%s-fix-channels",
of_dai_links_etdm[i].name);
if (sret < 0)
dev_dbg(card->dev, "%s snprintf error\n", __func__);
ret = of_property_read_u32(np, prop, &val);
if (ret == 0 && mt8696_afe_channel_supported(val,
link_to_dai(link_id))) {
switch (link_id) {
case DAI_LINK_ETDM1_OUT:
etdm = &priv->etdm_data[MT8696_ETDM1];
etdm->fix_channels_out = val;
break;
case DAI_LINK_ETDM2_OUT:
etdm = &priv->etdm_data[MT8696_ETDM2];
etdm->fix_channels_out = val;
break;
case DAI_LINK_ETDM2_IN:
etdm = &priv->etdm_data[MT8696_ETDM2];
etdm->fix_channels_in = val;
break;
default:
break;
}
dai_link->be_hw_params_fixup =
mt8696_evb_be_hw_params_fixup;
}
/* parse codec_of_node */
sret = snprintf(prop, sizeof(prop), PREFIX"%s-audio-codec",
of_dai_links_etdm[i].name);
if (sret < 0)
dev_dbg(card->dev, "%s snprintf error\n", __func__);
codec_node = of_parse_phandle(np, prop, 0);
if (codec_node) {
dai_link->codecs->of_node = codec_node;
dai_link->codecs->name = NULL;
}
/* parse codec dai name */
sret = snprintf(prop, sizeof(prop), PREFIX"%s-codec-dai-name",
of_dai_links_etdm[i].name);
if (sret < 0)
dev_dbg(card->dev, "%s snprintf error\n", __func__);
of_property_read_string(np, prop, &dai_link->codecs->dai_name);
/* parse ignore pmdown time */
sret = snprintf(prop, sizeof(prop), PREFIX"%s-ignore-pmdown-time",
of_dai_links_etdm[i].name);
if (sret < 0)
dev_dbg(card->dev, "%s snprintf error\n", __func__);
if (of_property_read_bool(np, prop))
dai_link->ignore_pmdown_time = 1;
/* parse ignore suspend */
sret = snprintf(prop, sizeof(prop), PREFIX"%s-ignore-suspend",
of_dai_links_etdm[i].name);
if (sret < 0)
dev_dbg(card->dev, "%s snprintf error\n", __func__);
if (of_property_read_bool(np, prop))
dai_link->ignore_suspend = 1;
of_node_put(codec_node);
}
}
static struct snd_soc_card mt8696_evb_card = {
.name = "mt-snd-card",
.long_name = "mt8696-evb-card",
.owner = THIS_MODULE,
.dai_link = mt8696_evb_dais,
.num_links = ARRAY_SIZE(mt8696_evb_dais),
.dapm_widgets = mt8696_evb_widgets,
.num_dapm_widgets = ARRAY_SIZE(mt8696_evb_widgets),
.dapm_routes = mt8696_evb_routes,
.num_dapm_routes = ARRAY_SIZE(mt8696_evb_routes),
};
static int mt8696_evb_dev_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = &mt8696_evb_card;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct mt8696_evb_priv *priv;
int ret, id;
size_t i;
size_t dais_num = ARRAY_SIZE(mt8696_evb_dais);
priv = devm_kzalloc(dev, sizeof(struct mt8696_evb_priv),
GFP_KERNEL);
if (!priv) {
ret = -ENOMEM;
dev_info(dev, "%s allocate card private data fail %d\n",
__func__, ret);
return ret;
}
priv->afe_plat_node = of_parse_phandle(dev->of_node, "mediatek,platform", 0);
if (!priv->afe_plat_node) {
dev_info(dev, "Property 'platform' missing or invalid\n");
return -EINVAL;
}
for (i = 0; i < dais_num; i++) {
if (mt8696_evb_dais[i].platforms->name)
continue;
id = mt8696_evb_dais[i].id;
if ((id >= DAI_LINK_AFE_FE_BASE &&
id <= DAI_LINK_AFE_FE_END) ||
(id >= DAI_LINK_AFE_BE_BASE &&
id <= DAI_LINK_AFE_BE_END)) {
mt8696_evb_dais[i].platforms->of_node = priv->afe_plat_node;
}
}
card->dev = dev;
snd_soc_card_set_drvdata(card, priv);
mt8696_evb_gpio_probe(card);
mt8696_evb_parse_of(card, np);
ret = devm_snd_soc_register_card(dev, card);
if (ret) {
dev_info(dev, "%s snd_soc_register_card fail %d\n",
__func__, ret);
of_node_put(priv->afe_plat_node);
return ret;
}
return ret;
}
static int mt8696_evb_dev_remove(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
struct mt8696_evb_priv *priv = snd_soc_card_get_drvdata(card);
of_node_put(priv->afe_plat_node);
return 0;
}
static const struct of_device_id mt8696_evb_dt_match[] = {
{ .compatible = "mediatek,mt8696-evb", },
{ }
};
MODULE_DEVICE_TABLE(of, mt8696_evb_dt_match);
static struct platform_driver mt8696_evb_driver = {
.driver = {
.name = "mt8696-evb",
.of_match_table = mt8696_evb_dt_match,
#ifdef CONFIG_PM
.pm = &snd_soc_pm_ops,
#endif
},
.probe = mt8696_evb_dev_probe,
.remove = mt8696_evb_dev_remove,
};
module_platform_driver(mt8696_evb_driver);
/* Module information */
MODULE_DESCRIPTION("MT8696 EVB SoC machine driver");
MODULE_AUTHOR("Sail Yang <sail.yang@mediatek.com>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:mt8696-evb");