ASoC: mt8188: add new board support

Merge series from Trevor Wu <trevor.wu@mediatek.com>:

In the series, we extend the capability of mt8188-mt6359 driver.

The following changes are included.
1. Divide ADDA BE dai into two dais for SOF.
2. Register hdmi/dp jack pins.
3. dai_fmt can be configured from device tree.
4. Add some I2S codecs support.

In addition, new compatible string "mediatek,mt8188-nau8825" is
included for a new board support.
This commit is contained in:
Mark Brown 2023-06-05 18:29:36 +01:00
commit c7e076de2d
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
7 changed files with 507 additions and 55 deletions

View File

@ -11,7 +11,9 @@ maintainers:
properties: properties:
compatible: compatible:
const: mediatek,mt8188-mt6359-evb enum:
- mediatek,mt8188-mt6359-evb
- mediatek,mt8188-nau8825
model: model:
$ref: /schemas/types.yaml#/definitions/string $ref: /schemas/types.yaml#/definitions/string
@ -42,7 +44,6 @@ patternProperties:
we are going to update parameters in this node. we are going to update parameters in this node.
items: items:
enum: enum:
- ADDA_BE
- DPTX_BE - DPTX_BE
- ETDM1_IN_BE - ETDM1_IN_BE
- ETDM2_IN_BE - ETDM2_IN_BE
@ -62,11 +63,28 @@ patternProperties:
required: required:
- sound-dai - sound-dai
dai-format:
description: audio format.
items:
enum:
- i2s
- right_j
- left_j
- dsp_a
- dsp_b
mediatek,clk-provider:
$ref: /schemas/types.yaml#/definitions/string
description: Indicates dai-link clock master.
items:
enum:
- cpu
- codec
additionalProperties: false additionalProperties: false
required: required:
- link-name - link-name
- codec
additionalProperties: false additionalProperties: false
@ -87,7 +105,8 @@ examples:
"AIN1", "Headset Mic"; "AIN1", "Headset Mic";
dai-link-0 { dai-link-0 {
link-name = "ETDM3_OUT_BE"; link-name = "ETDM3_OUT_BE";
dai-format = "i2s";
mediatek,clk-provider = "cpu";
codec { codec {
sound-dai = <&hdmi0>; sound-dai = <&hdmi0>;
}; };

View File

@ -225,6 +225,10 @@ config SND_SOC_MT8188_MT6359
depends on SND_SOC_MT8188 && MTK_PMIC_WRAP depends on SND_SOC_MT8188 && MTK_PMIC_WRAP
select SND_SOC_MT6359 select SND_SOC_MT6359
select SND_SOC_HDMI_CODEC select SND_SOC_HDMI_CODEC
select SND_SOC_DMIC
select SND_SOC_MAX98390
select SND_SOC_NAU8315
select SND_SOC_NAU8825
help help
This adds support for ASoC machine driver for MediaTek MT8188 This adds support for ASoC machine driver for MediaTek MT8188
boards with the MT6359 and other I2S audio codecs. boards with the MT6359 and other I2S audio codecs.

View File

@ -21,8 +21,10 @@ static int set_card_codec_info(struct snd_soc_card *card,
int ret; int ret;
codec_node = of_get_child_by_name(sub_node, "codec"); codec_node = of_get_child_by_name(sub_node, "codec");
if (!codec_node) if (!codec_node) {
return -EINVAL; dev_dbg(dev, "%s no specified codec\n", dai_link->name);
return 0;
}
/* set card codec info */ /* set card codec info */
ret = snd_soc_of_get_dai_link_codecs(dev, codec_node, dai_link); ret = snd_soc_of_get_dai_link_codecs(dev, codec_node, dai_link);
@ -36,6 +38,47 @@ static int set_card_codec_info(struct snd_soc_card *card,
return 0; return 0;
} }
static int set_dailink_daifmt(struct snd_soc_card *card,
struct device_node *sub_node,
struct snd_soc_dai_link *dai_link)
{
unsigned int daifmt;
const char *str;
int ret;
struct {
char *name;
unsigned int val;
} of_clk_table[] = {
{ "cpu", SND_SOC_DAIFMT_CBC_CFC },
{ "codec", SND_SOC_DAIFMT_CBP_CFP },
};
daifmt = snd_soc_daifmt_parse_format(sub_node, NULL);
if (daifmt) {
dai_link->dai_fmt &= SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
dai_link->dai_fmt |= daifmt;
}
/*
* check "mediatek,clk-provider = xxx"
* SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK area
*/
ret = of_property_read_string(sub_node, "mediatek,clk-provider", &str);
if (ret == 0) {
int i;
for (i = 0; i < ARRAY_SIZE(of_clk_table); i++) {
if (strcmp(str, of_clk_table[i].name) == 0) {
dai_link->dai_fmt &= ~SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
dai_link->dai_fmt |= of_clk_table[i].val;
break;
}
}
}
return 0;
}
int parse_dai_link_info(struct snd_soc_card *card) int parse_dai_link_info(struct snd_soc_card *card)
{ {
struct device *dev = card->dev; struct device *dev = card->dev;
@ -67,6 +110,12 @@ int parse_dai_link_info(struct snd_soc_card *card)
of_node_put(sub_node); of_node_put(sub_node);
return ret; return ret;
} }
ret = set_dailink_daifmt(card, sub_node, dai_link);
if (ret < 0) {
of_node_put(sub_node);
return ret;
}
} }
return 0; return 0;

View File

@ -39,7 +39,7 @@ enum {
MT8188_AFE_MEMIF_END, MT8188_AFE_MEMIF_END,
MT8188_AFE_MEMIF_NUM = (MT8188_AFE_MEMIF_END - MT8188_AFE_MEMIF_START), MT8188_AFE_MEMIF_NUM = (MT8188_AFE_MEMIF_END - MT8188_AFE_MEMIF_START),
MT8188_AFE_IO_START = MT8188_AFE_MEMIF_END, MT8188_AFE_IO_START = MT8188_AFE_MEMIF_END,
MT8188_AFE_IO_ADDA = MT8188_AFE_IO_START, MT8188_AFE_IO_DL_SRC = MT8188_AFE_IO_START,
MT8188_AFE_IO_DMIC_IN, MT8188_AFE_IO_DMIC_IN,
MT8188_AFE_IO_DPTX, MT8188_AFE_IO_DPTX,
MT8188_AFE_IO_ETDM_START, MT8188_AFE_IO_ETDM_START,
@ -52,6 +52,7 @@ enum {
MT8188_AFE_IO_ETDM_NUM = MT8188_AFE_IO_ETDM_NUM =
(MT8188_AFE_IO_ETDM_END - MT8188_AFE_IO_ETDM_START), (MT8188_AFE_IO_ETDM_END - MT8188_AFE_IO_ETDM_START),
MT8188_AFE_IO_PCM = MT8188_AFE_IO_ETDM_END, MT8188_AFE_IO_PCM = MT8188_AFE_IO_ETDM_END,
MT8188_AFE_IO_UL_SRC,
MT8188_AFE_IO_END, MT8188_AFE_IO_END,
MT8188_AFE_IO_NUM = (MT8188_AFE_IO_END - MT8188_AFE_IO_START), MT8188_AFE_IO_NUM = (MT8188_AFE_IO_END - MT8188_AFE_IO_START),
MT8188_DAI_END = MT8188_AFE_IO_END, MT8188_DAI_END = MT8188_AFE_IO_END,

View File

@ -53,8 +53,7 @@ enum {
}; };
struct mtk_dai_adda_priv { struct mtk_dai_adda_priv {
unsigned int dl_rate; bool hires_required;
unsigned int ul_rate;
}; };
static unsigned int afe_adda_dl_rate_transform(struct mtk_base_afe *afe, static unsigned int afe_adda_dl_rate_transform(struct mtk_base_afe *afe,
@ -241,42 +240,35 @@ static int mtk_adda_ul_event(struct snd_soc_dapm_widget *w,
return 0; return 0;
} }
static int mtk_afe_adc_hires_connect(struct snd_soc_dapm_widget *source, static struct mtk_dai_adda_priv *get_adda_priv_by_name(struct mtk_base_afe *afe,
const char *name)
{
struct mt8188_afe_private *afe_priv = afe->platform_priv;
if (strstr(name, "aud_adc_hires"))
return afe_priv->dai_priv[MT8188_AFE_IO_UL_SRC];
else if (strstr(name, "aud_dac_hires"))
return afe_priv->dai_priv[MT8188_AFE_IO_DL_SRC];
else
return NULL;
}
static int mtk_afe_adda_hires_connect(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink) struct snd_soc_dapm_widget *sink)
{ {
struct snd_soc_dapm_widget *w = source; struct snd_soc_dapm_widget *w = source;
struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt); struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
struct mt8188_afe_private *afe_priv = afe->platform_priv;
struct mtk_dai_adda_priv *adda_priv; struct mtk_dai_adda_priv *adda_priv;
adda_priv = afe_priv->dai_priv[MT8188_AFE_IO_ADDA]; adda_priv = get_adda_priv_by_name(afe, w->name);
if (!adda_priv) { if (!adda_priv) {
dev_err(afe->dev, "%s adda_priv == NULL", __func__); dev_dbg(afe->dev, "adda_priv == NULL");
return 0; return 0;
} }
return !!(adda_priv->ul_rate > ADDA_HIRES_THRES); return (adda_priv->hires_required) ? 1 : 0;
}
static int mtk_afe_dac_hires_connect(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
struct snd_soc_dapm_widget *w = source;
struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
struct mt8188_afe_private *afe_priv = afe->platform_priv;
struct mtk_dai_adda_priv *adda_priv;
adda_priv = afe_priv->dai_priv[MT8188_AFE_IO_ADDA];
if (!adda_priv) {
dev_err(afe->dev, "%s adda_priv == NULL", __func__);
return 0;
}
return !!(adda_priv->dl_rate > ADDA_HIRES_THRES);
} }
static const struct snd_kcontrol_new mtk_dai_adda_o176_mix[] = { static const struct snd_kcontrol_new mtk_dai_adda_o176_mix[] = {
@ -361,7 +353,7 @@ static const struct snd_soc_dapm_route mtk_dai_adda_routes[] = {
{"ADDA Capture", NULL, "ADDA Capture Enable"}, {"ADDA Capture", NULL, "ADDA Capture Enable"},
{"ADDA Capture", NULL, "ADDA_MTKAIF_CFG"}, {"ADDA Capture", NULL, "ADDA_MTKAIF_CFG"},
{"ADDA Capture", NULL, "aud_adc"}, {"ADDA Capture", NULL, "aud_adc"},
{"ADDA Capture", NULL, "aud_adc_hires", mtk_afe_adc_hires_connect}, {"ADDA Capture", NULL, "aud_adc_hires", mtk_afe_adda_hires_connect},
{"I168", NULL, "ADDA Capture"}, {"I168", NULL, "ADDA Capture"},
{"I169", NULL, "ADDA Capture"}, {"I169", NULL, "ADDA Capture"},
@ -369,7 +361,7 @@ static const struct snd_soc_dapm_route mtk_dai_adda_routes[] = {
{"ADDA Playback", NULL, "ADDA Enable"}, {"ADDA Playback", NULL, "ADDA Enable"},
{"ADDA Playback", NULL, "ADDA Playback Enable"}, {"ADDA Playback", NULL, "ADDA Playback Enable"},
{"ADDA Playback", NULL, "aud_dac"}, {"ADDA Playback", NULL, "aud_dac"},
{"ADDA Playback", NULL, "aud_dac_hires", mtk_afe_dac_hires_connect}, {"ADDA Playback", NULL, "aud_dac_hires", mtk_afe_adda_hires_connect},
{"DL_GAIN", NULL, "O176"}, {"DL_GAIN", NULL, "O176"},
{"DL_GAIN", NULL, "O177"}, {"DL_GAIN", NULL, "O177"},
@ -503,13 +495,12 @@ static int mtk_dai_adda_hw_params(struct snd_pcm_substream *substream,
dev_dbg(afe->dev, "%s(), id %d, stream %d, rate %u\n", dev_dbg(afe->dev, "%s(), id %d, stream %d, rate %u\n",
__func__, id, substream->stream, rate); __func__, id, substream->stream, rate);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { adda_priv->hires_required = (rate > ADDA_HIRES_THRES);
adda_priv->dl_rate = rate;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
ret = mtk_dai_da_configure(afe, rate, id); ret = mtk_dai_da_configure(afe, rate, id);
} else { else
adda_priv->ul_rate = rate;
ret = mtk_dai_ad_configure(afe, rate, id); ret = mtk_dai_ad_configure(afe, rate, id);
}
return ret; return ret;
} }
@ -536,8 +527,8 @@ static const struct snd_soc_dai_ops mtk_dai_adda_ops = {
static struct snd_soc_dai_driver mtk_dai_adda_driver[] = { static struct snd_soc_dai_driver mtk_dai_adda_driver[] = {
{ {
.name = "ADDA", .name = "DL_SRC",
.id = MT8188_AFE_IO_ADDA, .id = MT8188_AFE_IO_DL_SRC,
.playback = { .playback = {
.stream_name = "ADDA Playback", .stream_name = "ADDA Playback",
.channels_min = 1, .channels_min = 1,
@ -545,6 +536,11 @@ static struct snd_soc_dai_driver mtk_dai_adda_driver[] = {
.rates = MTK_ADDA_PLAYBACK_RATES, .rates = MTK_ADDA_PLAYBACK_RATES,
.formats = MTK_ADDA_FORMATS, .formats = MTK_ADDA_FORMATS,
}, },
.ops = &mtk_dai_adda_ops,
},
{
.name = "UL_SRC",
.id = MT8188_AFE_IO_UL_SRC,
.capture = { .capture = {
.stream_name = "ADDA Capture", .stream_name = "ADDA Capture",
.channels_min = 1, .channels_min = 1,
@ -560,13 +556,18 @@ static int init_adda_priv_data(struct mtk_base_afe *afe)
{ {
struct mt8188_afe_private *afe_priv = afe->platform_priv; struct mt8188_afe_private *afe_priv = afe->platform_priv;
struct mtk_dai_adda_priv *adda_priv; struct mtk_dai_adda_priv *adda_priv;
int adda_dai_list[] = {MT8188_AFE_IO_DL_SRC, MT8188_AFE_IO_UL_SRC};
int i;
adda_priv = devm_kzalloc(afe->dev, sizeof(struct mtk_dai_adda_priv), for (i = 0; i < ARRAY_SIZE(adda_dai_list); i++) {
adda_priv = devm_kzalloc(afe->dev,
sizeof(struct mtk_dai_adda_priv),
GFP_KERNEL); GFP_KERNEL);
if (!adda_priv) if (!adda_priv)
return -ENOMEM; return -ENOMEM;
afe_priv->dai_priv[MT8188_AFE_IO_ADDA] = adda_priv; afe_priv->dai_priv[adda_dai_list[i]] = adda_priv;
}
return 0; return 0;
} }

View File

@ -6,6 +6,7 @@
* Author: Trevor Wu <trevor.wu@mediatek.com> * Author: Trevor Wu <trevor.wu@mediatek.com>
*/ */
#include <linux/input.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
@ -13,10 +14,27 @@
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
#include <sound/soc.h> #include <sound/soc.h>
#include "mt8188-afe-common.h" #include "mt8188-afe-common.h"
#include "../../codecs/nau8825.h"
#include "../../codecs/mt6359.h" #include "../../codecs/mt6359.h"
#include "../common/mtk-afe-platform-driver.h" #include "../common/mtk-afe-platform-driver.h"
#include "../common/mtk-soundcard-driver.h" #include "../common/mtk-soundcard-driver.h"
#define NAU8825_HS_PRESENT BIT(0)
/*
* Maxim MAX98390
*/
#define MAX98390_CODEC_DAI "max98390-aif1"
#define MAX98390_DEV0_NAME "max98390.0-0038" /* rear right */
#define MAX98390_DEV1_NAME "max98390.0-0039" /* rear left */
#define MAX98390_DEV2_NAME "max98390.0-003a" /* front right */
#define MAX98390_DEV3_NAME "max98390.0-003b" /* front left */
/*
* Nau88l25
*/
#define NAU8825_CODEC_DAI "nau8825-hifi"
/* FE */ /* FE */
SND_SOC_DAILINK_DEFS(playback2, SND_SOC_DAILINK_DEFS(playback2,
DAILINK_COMP_ARRAY(COMP_CPU("DL2")), DAILINK_COMP_ARRAY(COMP_CPU("DL2")),
@ -99,8 +117,8 @@ SND_SOC_DAILINK_DEFS(capture10,
DAILINK_COMP_ARRAY(COMP_EMPTY())); DAILINK_COMP_ARRAY(COMP_EMPTY()));
/* BE */ /* BE */
SND_SOC_DAILINK_DEFS(adda, SND_SOC_DAILINK_DEFS(dl_src,
DAILINK_COMP_ARRAY(COMP_CPU("ADDA")), DAILINK_COMP_ARRAY(COMP_CPU("DL_SRC")),
DAILINK_COMP_ARRAY(COMP_CODEC("mt6359-sound", DAILINK_COMP_ARRAY(COMP_CODEC("mt6359-sound",
"mt6359-snd-codec-aif1")), "mt6359-snd-codec-aif1")),
DAILINK_COMP_ARRAY(COMP_EMPTY())); DAILINK_COMP_ARRAY(COMP_EMPTY()));
@ -140,9 +158,44 @@ SND_SOC_DAILINK_DEFS(pcm1,
DAILINK_COMP_ARRAY(COMP_DUMMY()), DAILINK_COMP_ARRAY(COMP_DUMMY()),
DAILINK_COMP_ARRAY(COMP_EMPTY())); DAILINK_COMP_ARRAY(COMP_EMPTY()));
SND_SOC_DAILINK_DEFS(ul_src,
DAILINK_COMP_ARRAY(COMP_CPU("UL_SRC")),
DAILINK_COMP_ARRAY(COMP_CODEC("mt6359-sound",
"mt6359-snd-codec-aif1"),
COMP_CODEC("dmic-codec",
"dmic-hifi")),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
struct mt8188_mt6359_priv { struct mt8188_mt6359_priv {
struct snd_soc_jack dp_jack; struct snd_soc_jack dp_jack;
struct snd_soc_jack hdmi_jack; struct snd_soc_jack hdmi_jack;
struct snd_soc_jack headset_jack;
void *private_data;
};
static struct snd_soc_jack_pin mt8188_hdmi_jack_pins[] = {
{
.pin = "HDMI",
.mask = SND_JACK_LINEOUT,
},
};
static struct snd_soc_jack_pin mt8188_dp_jack_pins[] = {
{
.pin = "DP",
.mask = SND_JACK_LINEOUT,
},
};
static struct snd_soc_jack_pin nau8825_jack_pins[] = {
{
.pin = "Headphone Jack",
.mask = SND_JACK_HEADPHONE,
},
{
.pin = "Headset Mic",
.mask = SND_JACK_MICROPHONE,
},
}; };
struct mt8188_card_data { struct mt8188_card_data {
@ -150,9 +203,39 @@ struct mt8188_card_data {
unsigned long quirk; unsigned long quirk;
}; };
static const struct snd_kcontrol_new mt8188_dumb_spk_controls[] = {
SOC_DAPM_PIN_SWITCH("Ext Spk"),
};
static const struct snd_soc_dapm_widget mt8188_dumb_spk_widgets[] = {
SND_SOC_DAPM_SPK("Ext Spk", NULL),
};
static const struct snd_kcontrol_new mt8188_dual_spk_controls[] = {
SOC_DAPM_PIN_SWITCH("Left Spk"),
SOC_DAPM_PIN_SWITCH("Right Spk"),
};
static const struct snd_soc_dapm_widget mt8188_dual_spk_widgets[] = {
SND_SOC_DAPM_SPK("Left Spk", NULL),
SND_SOC_DAPM_SPK("Right Spk", NULL),
};
static const struct snd_kcontrol_new mt8188_rear_spk_controls[] = {
SOC_DAPM_PIN_SWITCH("Rear Left Spk"),
SOC_DAPM_PIN_SWITCH("Rear Right Spk"),
};
static const struct snd_soc_dapm_widget mt8188_rear_spk_widgets[] = {
SND_SOC_DAPM_SPK("Rear Left Spk", NULL),
SND_SOC_DAPM_SPK("Rear Right Spk", NULL),
};
static const struct snd_soc_dapm_widget mt8188_mt6359_widgets[] = { static const struct snd_soc_dapm_widget mt8188_mt6359_widgets[] = {
SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_HP("Headphone", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL),
SND_SOC_DAPM_SINK("HDMI"),
SND_SOC_DAPM_SINK("DP"),
}; };
static const struct snd_kcontrol_new mt8188_mt6359_controls[] = { static const struct snd_kcontrol_new mt8188_mt6359_controls[] = {
@ -160,6 +243,14 @@ static const struct snd_kcontrol_new mt8188_mt6359_controls[] = {
SOC_DAPM_PIN_SWITCH("Headset Mic"), SOC_DAPM_PIN_SWITCH("Headset Mic"),
}; };
static const struct snd_soc_dapm_widget mt8188_nau8825_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", NULL),
};
static const struct snd_kcontrol_new mt8188_nau8825_controls[] = {
SOC_DAPM_PIN_SWITCH("Headphone Jack"),
};
#define CKSYS_AUD_TOP_CFG 0x032c #define CKSYS_AUD_TOP_CFG 0x032c
#define CKSYS_AUD_TOP_MON 0x0330 #define CKSYS_AUD_TOP_MON 0x0330
@ -345,7 +436,7 @@ enum {
DAI_LINK_UL8_FE, DAI_LINK_UL8_FE,
DAI_LINK_UL9_FE, DAI_LINK_UL9_FE,
DAI_LINK_UL10_FE, DAI_LINK_UL10_FE,
DAI_LINK_ADDA_BE, DAI_LINK_DL_SRC_BE,
DAI_LINK_DPTX_BE, DAI_LINK_DPTX_BE,
DAI_LINK_ETDM1_IN_BE, DAI_LINK_ETDM1_IN_BE,
DAI_LINK_ETDM2_IN_BE, DAI_LINK_ETDM2_IN_BE,
@ -353,6 +444,7 @@ enum {
DAI_LINK_ETDM2_OUT_BE, DAI_LINK_ETDM2_OUT_BE,
DAI_LINK_ETDM3_OUT_BE, DAI_LINK_ETDM3_OUT_BE,
DAI_LINK_PCM1_BE, DAI_LINK_PCM1_BE,
DAI_LINK_UL_SRC_BE,
}; };
static int mt8188_dptx_hw_params(struct snd_pcm_substream *substream, static int mt8188_dptx_hw_params(struct snd_pcm_substream *substream,
@ -389,8 +481,10 @@ static int mt8188_hdmi_codec_init(struct snd_soc_pcm_runtime *rtd)
struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
int ret = 0; int ret = 0;
ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT, ret = snd_soc_card_jack_new_pins(rtd->card, "HDMI Jack",
&priv->hdmi_jack); SND_JACK_LINEOUT, &priv->hdmi_jack,
mt8188_hdmi_jack_pins,
ARRAY_SIZE(mt8188_hdmi_jack_pins));
if (ret) { if (ret) {
dev_info(rtd->dev, "%s, new jack failed: %d\n", __func__, ret); dev_info(rtd->dev, "%s, new jack failed: %d\n", __func__, ret);
return ret; return ret;
@ -410,8 +504,9 @@ static int mt8188_dptx_codec_init(struct snd_soc_pcm_runtime *rtd)
struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
int ret = 0; int ret = 0;
ret = snd_soc_card_jack_new(rtd->card, "DP Jack", SND_JACK_LINEOUT, ret = snd_soc_card_jack_new_pins(rtd->card, "DP Jack", SND_JACK_LINEOUT,
&priv->dp_jack); &priv->dp_jack, mt8188_dp_jack_pins,
ARRAY_SIZE(mt8188_dp_jack_pins));
if (ret) { if (ret) {
dev_info(rtd->dev, "%s, new jack failed: %d\n", __func__, ret); dev_info(rtd->dev, "%s, new jack failed: %d\n", __func__, ret);
return ret; return ret;
@ -425,6 +520,189 @@ static int mt8188_dptx_codec_init(struct snd_soc_pcm_runtime *rtd)
return ret; return ret;
} }
static int mt8188_dumb_amp_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_card *card = rtd->card;
int ret = 0;
ret = snd_soc_dapm_new_controls(&card->dapm, mt8188_dumb_spk_widgets,
ARRAY_SIZE(mt8188_dumb_spk_widgets));
if (ret) {
dev_err(rtd->dev, "unable to add Dumb Speaker dapm, ret %d\n", ret);
return ret;
}
ret = snd_soc_add_card_controls(card, mt8188_dumb_spk_controls,
ARRAY_SIZE(mt8188_dumb_spk_controls));
if (ret) {
dev_err(rtd->dev, "unable to add Dumb card controls, ret %d\n", ret);
return ret;
}
return ret;
}
static int mt8188_max98390_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
unsigned int bit_width = params_width(params);
struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
struct snd_soc_dai *codec_dai;
int i;
snd_soc_dai_set_tdm_slot(cpu_dai, 0xf, 0xf, 4, bit_width);
for_each_rtd_codec_dais(rtd, i, codec_dai) {
if (!strcmp(codec_dai->component->name, MAX98390_DEV0_NAME))
snd_soc_dai_set_tdm_slot(codec_dai, 0x8, 0x3, 4, bit_width);
if (!strcmp(codec_dai->component->name, MAX98390_DEV1_NAME))
snd_soc_dai_set_tdm_slot(codec_dai, 0x4, 0x3, 4, bit_width);
if (!strcmp(codec_dai->component->name, MAX98390_DEV2_NAME))
snd_soc_dai_set_tdm_slot(codec_dai, 0x2, 0x3, 4, bit_width);
if (!strcmp(codec_dai->component->name, MAX98390_DEV3_NAME))
snd_soc_dai_set_tdm_slot(codec_dai, 0x1, 0x3, 4, bit_width);
}
return 0;
}
static const struct snd_soc_ops mt8188_max98390_ops = {
.hw_params = mt8188_max98390_hw_params,
};
static int mt8188_max98390_codec_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_card *card = rtd->card;
int ret;
/* add regular speakers dapm route */
ret = snd_soc_dapm_new_controls(&card->dapm, mt8188_dual_spk_widgets,
ARRAY_SIZE(mt8188_dual_spk_widgets));
if (ret) {
dev_err(rtd->dev, "unable to add Left/Right Speaker widget, ret %d\n", ret);
return ret;
}
ret = snd_soc_add_card_controls(card, mt8188_dual_spk_controls,
ARRAY_SIZE(mt8188_dual_spk_controls));
if (ret) {
dev_err(rtd->dev, "unable to add Left/Right card controls, ret %d\n", ret);
return ret;
}
if (rtd->dai_link->num_codecs <= 2)
return ret;
/* add widgets/controls/dapm for rear speakers */
ret = snd_soc_dapm_new_controls(&card->dapm, mt8188_rear_spk_widgets,
ARRAY_SIZE(mt8188_rear_spk_widgets));
if (ret) {
dev_err(rtd->dev, "unable to add Rear Speaker widget, ret %d\n", ret);
/* Don't need to add routes if widget addition failed */
return ret;
}
ret = snd_soc_add_card_controls(card, mt8188_rear_spk_controls,
ARRAY_SIZE(mt8188_rear_spk_controls));
if (ret) {
dev_err(rtd->dev, "unable to add Rear card controls, ret %d\n", ret);
return ret;
}
return ret;
}
static int mt8188_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_card *card = rtd->card;
struct mt8188_mt6359_priv *priv = snd_soc_card_get_drvdata(card);
struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
struct snd_soc_jack *jack = &priv->headset_jack;
int ret;
ret = snd_soc_dapm_new_controls(&card->dapm, mt8188_nau8825_widgets,
ARRAY_SIZE(mt8188_nau8825_widgets));
if (ret) {
dev_err(rtd->dev, "unable to add nau8825 card widget, ret %d\n", ret);
return ret;
}
ret = snd_soc_add_card_controls(card, mt8188_nau8825_controls,
ARRAY_SIZE(mt8188_nau8825_controls));
if (ret) {
dev_err(rtd->dev, "unable to add nau8825 card controls, ret %d\n", ret);
return ret;
}
ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
SND_JACK_HEADSET | SND_JACK_BTN_0 |
SND_JACK_BTN_1 | SND_JACK_BTN_2 |
SND_JACK_BTN_3,
jack,
nau8825_jack_pins,
ARRAY_SIZE(nau8825_jack_pins));
if (ret) {
dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
return ret;
}
snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
ret = snd_soc_component_set_jack(component, jack, NULL);
if (ret) {
dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret);
return ret;
}
return ret;
};
static void mt8188_nau8825_codec_exit(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
snd_soc_component_set_jack(component, NULL, NULL);
}
static int mt8188_nau8825_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
unsigned int rate = params_rate(params);
unsigned int bit_width = params_width(params);
int clk_freq, ret;
clk_freq = rate * 2 * bit_width;
/* Configure clock for codec */
ret = snd_soc_dai_set_sysclk(codec_dai, NAU8825_CLK_FLL_BLK, 0,
SND_SOC_CLOCK_IN);
if (ret < 0) {
dev_err(codec_dai->dev, "can't set BCLK clock %d\n", ret);
return ret;
}
/* Configure pll for codec */
ret = snd_soc_dai_set_pll(codec_dai, 0, 0, clk_freq,
params_rate(params) * 256);
if (ret < 0) {
dev_err(codec_dai->dev, "can't set BCLK: %d\n", ret);
return ret;
}
return ret;
}
static const struct snd_soc_ops mt8188_nau8825_ops = {
.hw_params = mt8188_nau8825_hw_params,
};
static struct snd_soc_dai_link mt8188_mt6359_dai_links[] = { static struct snd_soc_dai_link mt8188_mt6359_dai_links[] = {
/* FE */ /* FE */
[DAI_LINK_DL2_FE] = { [DAI_LINK_DL2_FE] = {
@ -604,13 +882,11 @@ static struct snd_soc_dai_link mt8188_mt6359_dai_links[] = {
SND_SOC_DAILINK_REG(capture10), SND_SOC_DAILINK_REG(capture10),
}, },
/* BE */ /* BE */
[DAI_LINK_ADDA_BE] = { [DAI_LINK_DL_SRC_BE] = {
.name = "ADDA_BE", .name = "DL_SRC_BE",
.no_pcm = 1, .no_pcm = 1,
.dpcm_playback = 1, .dpcm_playback = 1,
.dpcm_capture = 1, SND_SOC_DAILINK_REG(dl_src),
.init = mt8188_mt6359_init,
SND_SOC_DAILINK_REG(adda),
}, },
[DAI_LINK_DPTX_BE] = { [DAI_LINK_DPTX_BE] = {
.name = "DPTX_BE", .name = "DPTX_BE",
@ -676,8 +952,48 @@ static struct snd_soc_dai_link mt8188_mt6359_dai_links[] = {
.dpcm_capture = 1, .dpcm_capture = 1,
SND_SOC_DAILINK_REG(pcm1), SND_SOC_DAILINK_REG(pcm1),
}, },
[DAI_LINK_UL_SRC_BE] = {
.name = "UL_SRC_BE",
.no_pcm = 1,
.dpcm_capture = 1,
SND_SOC_DAILINK_REG(ul_src),
},
}; };
static struct snd_kcontrol *ctl_find(struct snd_card *card, const char *name)
{
struct snd_ctl_elem_id sid;
memset(&sid, 0, sizeof(sid));
strcpy(sid.name, name);
sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
return snd_ctl_find_id(card, &sid);
}
static void mt8188_fixup_controls(struct snd_soc_card *card)
{
struct mt8188_mt6359_priv *priv = snd_soc_card_get_drvdata(card);
struct mt8188_card_data *card_data = (struct mt8188_card_data *)priv->private_data;
struct snd_kcontrol *kctl;
if (card_data->quirk & NAU8825_HS_PRESENT) {
struct snd_soc_dapm_widget *w, *next_w;
for_each_card_widgets_safe(card, w, next_w) {
if (strcmp(w->name, "Headphone"))
continue;
snd_soc_dapm_free_widget(w);
}
kctl = ctl_find(card->snd_card, "Headphone Switch");
if (kctl)
snd_ctl_remove(card->snd_card, kctl);
else
dev_warn(card->dev, "Cannot find ctl : Headphone Switch\n");
}
}
static struct snd_soc_card mt8188_mt6359_soc_card = { static struct snd_soc_card mt8188_mt6359_soc_card = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.dai_link = mt8188_mt6359_dai_links, .dai_link = mt8188_mt6359_dai_links,
@ -686,6 +1002,7 @@ static struct snd_soc_card mt8188_mt6359_soc_card = {
.num_dapm_widgets = ARRAY_SIZE(mt8188_mt6359_widgets), .num_dapm_widgets = ARRAY_SIZE(mt8188_mt6359_widgets),
.controls = mt8188_mt6359_controls, .controls = mt8188_mt6359_controls,
.num_controls = ARRAY_SIZE(mt8188_mt6359_controls), .num_controls = ARRAY_SIZE(mt8188_mt6359_controls),
.fixup_controls = mt8188_fixup_controls,
}; };
static int mt8188_mt6359_dev_probe(struct platform_device *pdev) static int mt8188_mt6359_dev_probe(struct platform_device *pdev)
@ -695,6 +1012,10 @@ static int mt8188_mt6359_dev_probe(struct platform_device *pdev)
struct mt8188_mt6359_priv *priv; struct mt8188_mt6359_priv *priv;
struct mt8188_card_data *card_data; struct mt8188_card_data *card_data;
struct snd_soc_dai_link *dai_link; struct snd_soc_dai_link *dai_link;
bool init_mt6359 = false;
bool init_nau8825 = false;
bool init_max98390 = false;
bool init_dumb = false;
int ret, i; int ret, i;
card_data = (struct mt8188_card_data *)of_device_get_match_data(&pdev->dev); card_data = (struct mt8188_card_data *)of_device_get_match_data(&pdev->dev);
@ -739,9 +1060,41 @@ static int mt8188_mt6359_dev_probe(struct platform_device *pdev)
} else if (strcmp(dai_link->name, "ETDM3_OUT_BE") == 0) { } else if (strcmp(dai_link->name, "ETDM3_OUT_BE") == 0) {
if (strcmp(dai_link->codecs->dai_name, "snd-soc-dummy-dai")) if (strcmp(dai_link->codecs->dai_name, "snd-soc-dummy-dai"))
dai_link->init = mt8188_hdmi_codec_init; dai_link->init = mt8188_hdmi_codec_init;
} else if (strcmp(dai_link->name, "DL_SRC_BE") == 0 ||
strcmp(dai_link->name, "UL_SRC_BE") == 0) {
if (!init_mt6359) {
dai_link->init = mt8188_mt6359_init;
init_mt6359 = true;
}
} else if (strcmp(dai_link->name, "ETDM1_OUT_BE") == 0 ||
strcmp(dai_link->name, "ETDM2_OUT_BE") == 0 ||
strcmp(dai_link->name, "ETDM1_IN_BE") == 0 ||
strcmp(dai_link->name, "ETDM2_IN_BE") == 0) {
if (!strcmp(dai_link->codecs->dai_name, MAX98390_CODEC_DAI)) {
dai_link->ops = &mt8188_max98390_ops;
if (!init_max98390) {
dai_link->init = mt8188_max98390_codec_init;
init_max98390 = true;
}
} else if (!strcmp(dai_link->codecs->dai_name, NAU8825_CODEC_DAI)) {
dai_link->ops = &mt8188_nau8825_ops;
if (!init_nau8825) {
dai_link->init = mt8188_nau8825_codec_init;
dai_link->exit = mt8188_nau8825_codec_exit;
init_nau8825 = true;
}
} else {
if (strcmp(dai_link->codecs->dai_name, "snd-soc-dummy-dai")) {
if (!init_dumb) {
dai_link->init = mt8188_dumb_amp_init;
init_dumb = true;
}
}
}
} }
} }
priv->private_data = card_data;
snd_soc_card_set_drvdata(card, priv); snd_soc_card_set_drvdata(card, priv);
ret = devm_snd_soc_register_card(&pdev->dev, card); ret = devm_snd_soc_register_card(&pdev->dev, card);
@ -758,11 +1111,20 @@ static struct mt8188_card_data mt8188_evb_card = {
.name = "mt8188_mt6359", .name = "mt8188_mt6359",
}; };
static struct mt8188_card_data mt8188_nau8825_card = {
.name = "mt8188_nau8825",
.quirk = NAU8825_HS_PRESENT,
};
static const struct of_device_id mt8188_mt6359_dt_match[] = { static const struct of_device_id mt8188_mt6359_dt_match[] = {
{ {
.compatible = "mediatek,mt8188-mt6359-evb", .compatible = "mediatek,mt8188-mt6359-evb",
.data = &mt8188_evb_card, .data = &mt8188_evb_card,
}, },
{
.compatible = "mediatek,mt8188-nau8825",
.data = &mt8188_nau8825_card,
},
{}, {},
}; };
MODULE_DEVICE_TABLE(of, mt8188_mt6359_dt_match); MODULE_DEVICE_TABLE(of, mt8188_mt6359_dt_match);

View File

@ -2216,6 +2216,16 @@ static void dapm_debugfs_add_widget(struct snd_soc_dapm_widget *w)
&dapm_widget_power_fops); &dapm_widget_power_fops);
} }
static void dapm_debugfs_free_widget(struct snd_soc_dapm_widget *w)
{
struct snd_soc_dapm_context *dapm = w->dapm;
if (!dapm->debugfs_dapm || !w->name)
return;
debugfs_lookup_and_remove(w->name, dapm->debugfs_dapm);
}
static void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm) static void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm)
{ {
debugfs_remove_recursive(dapm->debugfs_dapm); debugfs_remove_recursive(dapm->debugfs_dapm);
@ -2232,6 +2242,10 @@ static inline void dapm_debugfs_add_widget(struct snd_soc_dapm_widget *w)
{ {
} }
static inline void dapm_debugfs_free_widget(struct snd_soc_dapm_widget *w)
{
}
static inline void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm) static inline void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm)
{ {
} }
@ -2495,6 +2509,8 @@ void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w)
dapm_free_path(p); dapm_free_path(p);
} }
dapm_debugfs_free_widget(w);
kfree(w->kcontrols); kfree(w->kcontrols);
kfree_const(w->name); kfree_const(w->name);
kfree_const(w->sname); kfree_const(w->sname);