blob: a2469df201b91c67924b0c390a0396df1d166d3c [file] [log] [blame]
/*
* ASoC Driver for the Excelsior dev board
*
* Copyright (C) 2019 Google, LLC.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
#include <linux/gpio.h>
#include <linux/module.h>
#include <linux/of_gpio.h>
#include <sound/jack.h>
#include <sound/soc.h>
static struct snd_soc_jack mt8167_excelsior_jack;
static struct snd_soc_jack_pin mt8167_excelsior_jack_pins[] = {
{
.pin = "Headphone",
.mask = SND_JACK_HEADSET,
},
};
static struct snd_soc_jack_gpio mt8167_excelsior_jack_gpios[] = {
{
.gpio = -1,
.name = "headset-gpio",
.report = SND_JACK_HEADSET,
.invert = 0,
.debounce_time = 200,
},
};
enum PINCTRL_PIN_STATE { PIN_STATE_DEFAULT = 0, PIN_STATE_MAX };
struct mt8167_excelsior_priv {
struct pinctrl *pinctrl;
struct pinctrl_state *pin_states[PIN_STATE_MAX];
int jack_gpio;
};
static const char *const mt8167_excelsior_pinctrl_pin_str[PIN_STATE_MAX] = {
"default",
};
static int mt8167_excelsior_mic1_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_dapm_context *dapm = w->dapm;
struct snd_soc_card *card = dapm->card;
dev_dbg(card->dev, "%s, event %d\n", __func__, event);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
break;
case SND_SOC_DAPM_POST_PMD:
break;
default:
break;
}
return 0;
}
static int mt8167_excelsior_mic2_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_dapm_context *dapm = w->dapm;
struct snd_soc_card *card = dapm->card;
dev_dbg(card->dev, "%s, event %d\n", __func__, event);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
break;
case SND_SOC_DAPM_POST_PMD:
break;
default:
break;
}
return 0;
}
static int mt8167_excelsior_headset_mic_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event)
{
struct snd_soc_dapm_context *dapm = w->dapm;
struct snd_soc_card *card = dapm->card;
dev_dbg(card->dev, "%s, event %d\n", __func__, event);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
break;
case SND_SOC_DAPM_POST_PMD:
break;
default:
break;
}
return 0;
}
static int mt8167_excelsior_int_adda_init(struct snd_soc_pcm_runtime *runtime)
{
int ret = 0;
struct mt8167_excelsior_priv *card_data;
struct snd_soc_card *card = runtime->card;
card_data = snd_soc_card_get_drvdata(card);
dev_dbg(card->dev, "%s\n", __func__);
if (gpio_is_valid(card_data->jack_gpio)) {
ret = snd_soc_card_jack_new(card, "Headset Jack",
SND_JACK_HEADSET,
&mt8167_excelsior_jack,
mt8167_excelsior_jack_pins,
ARRAY_SIZE
(mt8167_excelsior_jack_pins));
if (ret) {
dev_err(card->dev, "Can't snd_soc_jack_new %d\n", ret);
return ret;
}
mt8167_excelsior_jack_gpios[0].gpio = card_data->jack_gpio;
ret = snd_soc_jack_add_gpios(&mt8167_excelsior_jack,
ARRAY_SIZE
(mt8167_excelsior_jack_gpios),
mt8167_excelsior_jack_gpios);
if (ret) {
dev_err(card->dev, "Can't snd_soc_jack_add_pins %d\n",
ret);
return ret;
}
} else {
dev_err(card->dev, "Invalid gpio for headphone jack.\n");
}
return ret;
}
static int mt8167_excelsior_card_remove(struct snd_soc_card *card)
{
snd_soc_jack_free_gpios(&mt8167_excelsior_jack,
ARRAY_SIZE(mt8167_excelsior_jack_gpios),
mt8167_excelsior_jack_gpios);
return 0;
}
static const struct snd_soc_dapm_widget mt8167_excelsior_dapm_widgets[] = {
SND_SOC_DAPM_HP("Headphone", NULL),
SND_SOC_DAPM_MIC("Mic 1", mt8167_excelsior_mic1_event),
SND_SOC_DAPM_MIC("Mic 2", mt8167_excelsior_mic2_event),
SND_SOC_DAPM_MIC("Headset Mic", mt8167_excelsior_headset_mic_event),
};
static const struct snd_soc_dapm_route mt8167_excelsior_audio_map[] = {
/* Uplink */
{"AU_VIN0", NULL, "Mic 1"},
{"AU_VIN1", NULL, "Headset Mic"},
/* Downlink */
/* use internal spk amp of MT6392 */
{"Int Spk Amp", NULL, "AU_LOL"},
{"Headphone", NULL, "AU_HPL"},
{"Headphone", NULL, "AU_HPR"},
};
static struct snd_soc_dai_link mt8167_excelsior_dais[] = {
/* Front End DAI links */
{
.name = "DL1 Playback",
.stream_name = "MultiMedia1_PLayback",
.cpu_dai_name = "DL1",
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST},
.init = mt8167_excelsior_int_adda_init,
.dynamic = 1,
.dpcm_playback = 1,
},
{
.name = "VUL Capture",
.stream_name = "MultiMedia1_Capture",
.cpu_dai_name = "VUL",
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST},
.dynamic = 1,
.dpcm_capture = 1,
},
{
.name = "AWB Capture",
.stream_name = "DL1_AWB_Record",
.cpu_dai_name = "AWB",
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST},
.dynamic = 1,
.dpcm_capture = 1,
},
{
.name = "DL2 Playback",
.stream_name = "MultiMedia2_PLayback",
.cpu_dai_name = "DL2",
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST},
.dynamic = 1,
.dpcm_playback = 1,
},
/* Back End DAI links */
{
.name = "MTK Codec",
.cpu_dai_name = "INT ADDA",
.no_pcm = 1,
.codec_name = "mt8167-codec",
.codec_dai_name = "mt8167-codec-dai",
.dpcm_playback = 1,
.dpcm_capture = 1,
},
};
static const struct snd_kcontrol_new mt8167_excelsior_card_controls[] = {
SOC_DAPM_PIN_SWITCH("Headphone"),
};
static struct snd_soc_card mt8167_excelsior_card = {
.name = "excelsior-card",
.owner = THIS_MODULE,
.remove = mt8167_excelsior_card_remove,
.dai_link = mt8167_excelsior_dais,
.num_links = ARRAY_SIZE(mt8167_excelsior_dais),
.dapm_widgets = mt8167_excelsior_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(mt8167_excelsior_dapm_widgets),
.controls = mt8167_excelsior_card_controls,
.num_controls = ARRAY_SIZE(mt8167_excelsior_card_controls),
.dapm_routes = mt8167_excelsior_audio_map,
.num_dapm_routes = ARRAY_SIZE(mt8167_excelsior_audio_map),
};
static int mt8167_excelsior_gpio_probe(struct snd_soc_card *card)
{
struct mt8167_excelsior_priv *card_data;
int ret = 0;
int i;
dev_dbg(card->dev, "%s\n", __func__);
card_data = snd_soc_card_get_drvdata(card);
card_data->pinctrl = devm_pinctrl_get(card->dev);
if (IS_ERR(card_data->pinctrl)) {
ret = PTR_ERR(card_data->pinctrl);
dev_err(card->dev, "%s pinctrl_get failed %d\n", __func__, ret);
goto exit;
}
for (i = 0; i < PIN_STATE_MAX; i++) {
card_data->pin_states[i] =
pinctrl_lookup_state(card_data->pinctrl,
mt8167_excelsior_pinctrl_pin_str[i]);
if (IS_ERR(card_data->pin_states[i])) {
ret = PTR_ERR(card_data->pin_states[i]);
dev_warn(card->dev,
"%s Can't find pinctrl state %s %d\n",
__func__, mt8167_excelsior_pinctrl_pin_str[i],
ret);
} else {
dev_warn(card->dev, "%s Set pinctrl state %s %d\n",
__func__, mt8167_excelsior_pinctrl_pin_str[i],
ret);
}
}
/* default state */
if (!IS_ERR(card_data->pin_states[PIN_STATE_DEFAULT])) {
ret = pinctrl_select_state(card_data->pinctrl,
card_data->
pin_states[PIN_STATE_DEFAULT]);
if (ret) {
dev_err(card->dev, "%s failed to select state %d\n",
__func__, ret);
goto exit;
}
}
card_data->jack_gpio =
of_get_named_gpio(card->dev->of_node, "mediatek,jack-detect-gpio",
0);
dev_dbg(card->dev, "jack gpio: %d\n", card_data->jack_gpio);
exit:
return ret;
}
static int mt8167_excelsior_dev_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = &mt8167_excelsior_card;
struct device_node *platform_node;
int ret, i;
struct mt8167_excelsior_priv *card_data;
dev_dbg(card->dev, "%s\n", __func__);
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 (i = 0; i < card->num_links; i++) {
if (mt8167_excelsior_dais[i].platform_name)
continue;
mt8167_excelsior_dais[i].platform_of_node = platform_node;
}
card->dev = &pdev->dev;
card_data =
devm_kzalloc(&pdev->dev, sizeof(struct mt8167_excelsior_priv),
GFP_KERNEL);
if (!card_data) {
ret = -ENOMEM;
dev_err(&pdev->dev, "%s allocate card private data fail %d\n",
__func__, ret);
return ret;
}
snd_soc_card_set_drvdata(card, card_data);
mt8167_excelsior_gpio_probe(card);
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);
return ret;
}
static int mt8167_excelsior_dev_remove(struct platform_device *pdev)
{
return 0;
}
static const struct of_device_id mt8167_excelsior_dt_match[] = {
{
.compatible = "mediatek,snd-soc-mt8167-excelsior",
},
{}
};
MODULE_DEVICE_TABLE(of, mt8167_excelsior_dt_match);
static struct platform_driver mt8167_excelsior_mach_driver = {
.driver = {
.name = "mt8167-excelsior",
.of_match_table = mt8167_excelsior_dt_match,
#ifdef CONFIG_PM
.pm = &snd_soc_pm_ops,
#endif
},
.probe = mt8167_excelsior_dev_probe,
.remove = mt8167_excelsior_dev_remove,
};
module_platform_driver(mt8167_excelsior_mach_driver);
/* Module information */
MODULE_DESCRIPTION("MT8167 Excelsior ALSA SoC machine driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:mt8167-excelsior");