blob: 1a9c22c2933bc92b99d1f67b8c9ccebe0c741f58 [file] [log] [blame]
/*
* mt8516_p1.c -- MT8516P1 ALSA SoC machine driver
*
* Copyright (c) 2016 MediaTek Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only 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/module.h>
#include <sound/soc.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/regulator/consumer.h>
#define ENUM_TO_STR(enum) #enum
enum PINCTRL_PIN_STATE {
PIN_STATE_DEFAULT = 0,
PIN_STATE_MAX
};
enum mtkfile_pcm_state {
MTKFILE_PCM_STATE_UNKNOWN = 0,
MTKFILE_PCM_STATE_OPEN,
MTKFILE_PCM_STATE_HW_PARAMS,
MTKFILE_PCM_STATE_PREPARE,
MTKFILE_PCM_STATE_START,
MTKFILE_PCM_STATE_PAUSE,
MTKFILE_PCM_STATE_RESUME,
MTKFILE_PCM_STATE_DRAIN,
MTKFILE_PCM_STATE_STOP,
MTKFILE_PCM_STATE_HW_FREE,
MTKFILE_PCM_STATE_CLOSE,
MTKFILE_PCM_STATE_NUM,
};
static const char *const pcm_state_func[] = {
ENUM_TO_STR(MTKFILE_PCM_STATE_UNKNOWN),
ENUM_TO_STR(MTKFILE_PCM_STATE_OPEN),
ENUM_TO_STR(MTKFILE_PCM_STATE_HW_PARAMS),
ENUM_TO_STR(MTKFILE_PCM_STATE_PREPARE),
ENUM_TO_STR(MTKFILE_PCM_STATE_START),
ENUM_TO_STR(MTKFILE_PCM_STATE_PAUSE),
ENUM_TO_STR(MTKFILE_PCM_STATE_RESUME),
ENUM_TO_STR(MTKFILE_PCM_STATE_DRAIN),
ENUM_TO_STR(MTKFILE_PCM_STATE_STOP),
ENUM_TO_STR(MTKFILE_PCM_STATE_HW_FREE),
ENUM_TO_STR(MTKFILE_PCM_STATE_CLOSE),
};
static const char * const nfy_ctl_names[] = {
"Master Volume",
"Master Volume X",
"Master Switch",
"Master Switch X",
"PCM State",
"PCM State X",
};
enum {
MASTER_VOLUME_ID = 0,
MASTER_VOLUMEX_ID,
MASTER_SWITCH_ID,
MASTER_SWITCHX_ID,
PCM_STATE_ID,
PCM_STATEX_ID,
CTRL_NOTIFY_NUM,
CTRL_NOTIFY_INVAL = 0xFFFF,
};
struct soc_ctlx_res {
int master_volume;
int master_switch;
int pcm_state;
struct snd_ctl_elem_id nfy_ids[CTRL_NOTIFY_NUM];
struct mutex res_mutex;
spinlock_t res_lock;
};
struct mt8516_pumpkin_priv {
struct pinctrl *pinctrl;
struct pinctrl_state *pin_states[PIN_STATE_MAX];
struct regulator *tdmadc_1p8_supply;
struct regulator *tdmadc_3p3_supply;
struct soc_ctlx_res ctlx_res;
};
static const char * const mt8516_pumpkin_pinctrl_pin_str[PIN_STATE_MAX] = {
"default",
};
static SOC_ENUM_SINGLE_EXT_DECL(pcm_state_enums, pcm_state_func);
/* ctrl resource manager */
static inline int soc_ctlx_init(struct soc_ctlx_res *ctlx_res, struct snd_soc_card *soc_card)
{
int i;
struct snd_card *card = soc_card->snd_card;
struct snd_kcontrol *control;
ctlx_res->master_volume = 100;
ctlx_res->master_switch = 1;
ctlx_res->pcm_state = MTKFILE_PCM_STATE_UNKNOWN;
mutex_init(&ctlx_res->res_mutex);
spin_lock_init(&ctlx_res->res_lock);
for (i = 0; i < CTRL_NOTIFY_NUM; i++) {
list_for_each_entry(control, &card->controls, list) {
if (strncmp(control->id.name, nfy_ctl_names[i], sizeof(control->id.name)))
continue;
ctlx_res->nfy_ids[i] = control->id;
}
}
return 0;
}
static int soc_ctlx_get(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_card *card = snd_kcontrol_chip(kctl);
struct mt8516_pumpkin_priv *card_data = snd_soc_card_get_drvdata(card);
struct soc_ctlx_res *res_mgr = &card_data->ctlx_res;
int type;
for (type = 0; type < CTRL_NOTIFY_NUM; type++) {
if (kctl->id.numid == res_mgr->nfy_ids[type].numid)
break;
}
if (type == CTRL_NOTIFY_NUM) {
pr_err("invalid mixer control(numid:%d)\n", kctl->id.numid);
return -EINVAL;
}
mutex_lock(&res_mgr->res_mutex);
switch (type) {
case MASTER_VOLUME_ID:
case MASTER_VOLUMEX_ID:
ucontrol->value.integer.value[0] = res_mgr->master_volume;
break;
case MASTER_SWITCH_ID:
case MASTER_SWITCHX_ID:
ucontrol->value.integer.value[0] = res_mgr->master_switch;
break;
default:
break;
}
mutex_unlock(&res_mgr->res_mutex);
// pr_notice("get mixer control(%s) value is:%ld\n", kctl->id.name, ucontrol->value.integer.value[0]);
return 0;
}
static int soc_ctlx_put(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_card *card = snd_kcontrol_chip(kctl);
struct mt8516_pumpkin_priv *card_data = snd_soc_card_get_drvdata(card);
struct soc_ctlx_res *res_mgr = &card_data->ctlx_res;
int type;
int nfy_type;
int need_notify_self = 0;
int *value = NULL;
for (type = 0; type < CTRL_NOTIFY_NUM; type++) {
if (kctl->id.numid == res_mgr->nfy_ids[type].numid)
break;
}
if (type == CTRL_NOTIFY_NUM) {
pr_err("invalid mixer control(numid:%d)\n", kctl->id.numid);
return -EINVAL;
}
mutex_lock(&res_mgr->res_mutex);
switch (type) {
case MASTER_VOLUME_ID:
if ((res_mgr->master_switch == 1) ||
(ucontrol->value.integer.value[0] != 0)) {
nfy_type = MASTER_VOLUMEX_ID;
value = &res_mgr->master_volume;
need_notify_self = 1;
}
break;
case MASTER_VOLUMEX_ID:
nfy_type = MASTER_VOLUME_ID;
value = &res_mgr->master_volume;
break;
case MASTER_SWITCH_ID:
nfy_type = MASTER_SWITCHX_ID;
value = &res_mgr->master_switch;
need_notify_self = 1;
break;
case MASTER_SWITCHX_ID:
nfy_type = MASTER_SWITCH_ID;
value = &res_mgr->master_switch;
break;
default:
break;
}
if (value != NULL) {
*value = ucontrol->value.integer.value[0];
snd_ctl_notify(card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, &(res_mgr->nfy_ids[nfy_type]));
} else {
nfy_type = CTRL_NOTIFY_INVAL;
}
if (need_notify_self) {
snd_ctl_notify(card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, &(kctl->id));
}
mutex_unlock(&res_mgr->res_mutex);
pr_notice("set mixer control(%s) value is:%ld, notify id:%x, notify self:%d\n",
kctl->id.name,
ucontrol->value.integer.value[0],
nfy_type,
need_notify_self);
return 0;
}
static int soc_pcm_state_get(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_card *card = snd_kcontrol_chip(kctl);
struct mt8516_pumpkin_priv *card_data = snd_soc_card_get_drvdata(card);
struct soc_ctlx_res *res_mgr = &card_data->ctlx_res;
unsigned long flags;
spin_lock_irqsave(&res_mgr->res_lock, flags);
ucontrol->value.integer.value[0] = res_mgr->pcm_state;
spin_unlock_irqrestore(&res_mgr->res_lock, flags);
pr_notice("get mixer control(%s) value is:%ld\n", kctl->id.name, ucontrol->value.integer.value[0]);
return 0;
}
static int soc_pcm_state_put(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_card *card = snd_kcontrol_chip(kctl);
struct mt8516_pumpkin_priv *card_data = snd_soc_card_get_drvdata(card);
struct soc_ctlx_res *res_mgr = &card_data->ctlx_res;
unsigned long flags;
spin_lock_irqsave(&res_mgr->res_lock, flags);
if (ucontrol->value.integer.value[0] != res_mgr->pcm_state) {
res_mgr->pcm_state = ucontrol->value.integer.value[0];
snd_ctl_notify(card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, &(res_mgr->nfy_ids[PCM_STATEX_ID]));
}
spin_unlock_irqrestore(&res_mgr->res_lock, flags);
pr_notice("set mixer control(%s) value is:%ld\n",
kctl->id.name,
ucontrol->value.integer.value[0]);
return 0;
}
static const struct snd_kcontrol_new mt8516_pumpkin_soc_controls[] = {
/* for third party app use */
SOC_SINGLE_EXT("Master Volume",
0,
0,
100,
0,
soc_ctlx_get,
soc_ctlx_put),
SOC_SINGLE_EXT("Master Volume X",
0,
0,
100,
0,
soc_ctlx_get,
soc_ctlx_put),
SOC_SINGLE_BOOL_EXT("Master Switch",
0,
soc_ctlx_get,
soc_ctlx_put),
SOC_SINGLE_BOOL_EXT("Master Switch X",
0,
soc_ctlx_get,
soc_ctlx_put),
SOC_ENUM_EXT("PCM State",
pcm_state_enums,
soc_pcm_state_get,
soc_pcm_state_put),
SOC_ENUM_EXT("PCM State X",
pcm_state_enums,
soc_pcm_state_get,
0),
};
static int i2s_8ch_playback_state_set(struct snd_pcm_substream *substream, int state)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_card *card = rtd->card;
struct mt8516_pumpkin_priv *card_data = snd_soc_card_get_drvdata(card);
struct soc_ctlx_res *res_mgr = &card_data->ctlx_res;
int nfy_type;
unsigned long flags;
nfy_type = PCM_STATEX_ID;
spin_lock_irqsave(&res_mgr->res_lock, flags);
if (res_mgr->pcm_state != state) {
res_mgr->pcm_state = state;
snd_ctl_notify(card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, &(res_mgr->nfy_ids[nfy_type]));
} else {
nfy_type = CTRL_NOTIFY_INVAL;
}
spin_unlock_irqrestore(&res_mgr->res_lock, flags);
return 0;
}
static int i2s_8ch_playback_startup(struct snd_pcm_substream *substream)
{
i2s_8ch_playback_state_set(substream, MTKFILE_PCM_STATE_OPEN);
return 0;
}
static void i2s_8ch_playback_shutdown(struct snd_pcm_substream *substream)
{
i2s_8ch_playback_state_set(substream, MTKFILE_PCM_STATE_CLOSE);
}
static int i2s_8ch_playback_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
i2s_8ch_playback_state_set(substream, MTKFILE_PCM_STATE_HW_PARAMS);
return 0;
}
static int i2s_8ch_playback_hw_free(struct snd_pcm_substream *substream)
{
i2s_8ch_playback_state_set(substream, MTKFILE_PCM_STATE_HW_FREE);
return 0;
}
static int i2s_8ch_playback_trigger(struct snd_pcm_substream *substream, int cmd)
{
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
i2s_8ch_playback_state_set(substream, MTKFILE_PCM_STATE_START);
break;
default:
break;
}
return 0;
}
static struct snd_soc_ops i2s_8ch_playback_ops = {
.startup = i2s_8ch_playback_startup,
.shutdown = i2s_8ch_playback_shutdown,
.hw_params = i2s_8ch_playback_hw_params,
.hw_free = i2s_8ch_playback_hw_free,
.trigger = i2s_8ch_playback_trigger,
};
static int pcm186x_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
pr_notice("%s\n", __func__);
#if 0
struct snd_soc_pcm_runtime *rtd;
struct snd_soc_dai *codec_dai;
printk("aic:%s\n", __func__);
if (substream == NULL) {
pr_err("invalid stream parameter\n");
return -EINVAL;
}
rtd = substream->private_data;
if (rtd == NULL) {
pr_err("invalid runtime parameter\n");
return -EINVAL;
}
codec_dai = rtd->codec_dai;
if (codec_dai == NULL) {
pr_err("invalid dai parameter\n");
return -EINVAL;
}
#define TLV320_MCLK_SOURCE 0
#define TLV320_BCLK_SOURCE 1
snd_soc_dai_set_pll(codec_dai, 0,TLV320_MCLK_SOURCE,
256 * params_rate(params), params_rate(params));
#endif
return 0;
}
static struct snd_soc_ops pcm186x_machine_ops = {
.hw_params = pcm186x_hw_params,
};
#if 0
static struct snd_soc_dai_link_component tdm_in_codecs[] = {
{.name = "pcm186x.2-004a", .dai_name = "pcm1865-aif" },
{.name = "pcm186x.2-004b", .dai_name = "pcm1865-aif" },
};
#endif
/* Digital audio interface glue - connects codec <---> CPU */
static struct snd_soc_dai_link mt8516_pumpkin_dais[] = {
/* Front End DAI links */
{
.name = "I2S 8CH Playback",
.stream_name = "I2S8CH Playback",
.cpu_dai_name = "HDMI",
.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,
.ops = &i2s_8ch_playback_ops,
},
{
.name = "TDM Capture",
.stream_name = "TDM_Capture",
.cpu_dai_name = "TDM_IN",
// .codecs = tdm_in_codecs,
// .num_codecs = 2,
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
// .codec_name = "pcm186x.2-004a", //i2c2 addr 0x4a
// .codec_dai_name = "pcm1865-aif",
.dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS,
.trigger = {
SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST
},
.dynamic = 1,
.dpcm_capture = 1,
.ops = &pcm186x_machine_ops,
},
{
.name = "DMIC Capture",
.stream_name = "DMIC_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 = "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,
},
#ifdef CONFIG_MTK_BTCVSD_ALSA
{
.name = "BTCVSD_RX",
.stream_name = "BTCVSD_Capture",
.cpu_dai_name = "snd-soc-dummy-dai",
.platform_name = "mt-soc-btcvsd-rx-pcm",
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
},
{
.name = "BTCVSD_TX",
.stream_name = "BTCVSD_Playback",
.cpu_dai_name = "snd-soc-dummy-dai",
.platform_name = "mt-soc-btcvsd-tx-pcm",
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
},
#endif
{
.name = "DL1 Playback",
.stream_name = "DL1_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
},
.dynamic = 1,
.dpcm_playback = 1,
},
{
.name = "Ref In 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 = "DAI Capture",
.stream_name = "VOIP_Call_BT_Capture",
.cpu_dai_name = "DAI",
.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 = "DL2_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,
},
/* Backend End DAI links */
{
.name = "HDMI BE",
.cpu_dai_name = "HDMIO",
.no_pcm = 1,
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS,
.dpcm_playback = 1,
},
{
.name = "2ND EXT Codec",
.cpu_dai_name = "2ND I2S",
.no_pcm = 1,
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS,
.dpcm_playback = 1,
.dpcm_capture = 1,
},
{
.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,
},
{
.name = "DMIC BE",
.cpu_dai_name = "INT ADDA",
.no_pcm = 1,
.codec_name = "mt8167-codec",
.codec_dai_name = "mt8167-codec-dai",
.dpcm_capture = 1,
},
{
.name = "HW Gain1 BE",
.cpu_dai_name = "HW_GAIN1",
.no_pcm = 1,
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.dpcm_capture = 1,
},
{
.name = "TDM IN BE",
.cpu_dai_name = "TDM_IN_IO",
.no_pcm = 1,
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
// .codecs = tdm_in_codecs,
// .num_codecs = 2,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
// .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS,
.dpcm_capture = 1,
},
{
.name = "I2S BE",
.cpu_dai_name = "I2S",
.no_pcm = 1,
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
//.codec_name = "tas5782m",
//.codec_dai_name = "tas5782m-i2s",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS,
.dpcm_playback = 1,
.dpcm_capture = 1,
},
{
.name = "DL BE",
.cpu_dai_name = "DL Input",
.no_pcm = 1,
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.dpcm_capture = 1,
},
{
.name = "MRG BT BE",
.cpu_dai_name = "MRG BT",
.no_pcm = 1,
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.dpcm_playback = 1,
.dpcm_capture = 1,
},
{
.name = "INTDIR BE",
.cpu_dai_name = "INTDIR_IO",
.no_pcm = 1,
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.dpcm_capture = 1,
},
};
static const struct snd_soc_dapm_widget mt8516_pumpkin_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("External Line In"),
SND_SOC_DAPM_OUTPUT("External I2S out"),
SND_SOC_DAPM_INPUT("External Line In2"),
SND_SOC_DAPM_OUTPUT("External I2S out2"),
};
static const struct snd_soc_dapm_route mt8516_pumpkin_audio_map[] = {
{"2ND I2S Capture", NULL, "External Line In"},
{"I2S Capture", NULL, "External Line In2"},
{"External I2S out", NULL, "I2S Playback"},
{"External I2S out2", NULL, "2ND I2S Playback"},
};
static int mt8516_pumpkin_suspend_post(struct snd_soc_card *card)
{
struct mt8516_pumpkin_priv *card_data;
card_data = snd_soc_card_get_drvdata(card);
if (!IS_ERR(card_data->tdmadc_1p8_supply))
regulator_disable(card_data->tdmadc_1p8_supply);
if (!IS_ERR(card_data->tdmadc_3p3_supply))
regulator_disable(card_data->tdmadc_3p3_supply);
return 0;
}
static int mt8516_pumpkin_resume_pre(struct snd_soc_card *card)
{
struct mt8516_pumpkin_priv *card_data;
int ret;
card_data = snd_soc_card_get_drvdata(card);
/* tdm adc power down */
if (!IS_ERR(card_data->tdmadc_1p8_supply)) {
ret = regulator_enable(card_data->tdmadc_1p8_supply);
if (ret != 0)
dev_err(card->dev, "%s failed to enable tdm 1p8 supply %d!\n", __func__, ret);
}
if (!IS_ERR(card_data->tdmadc_3p3_supply)) {
ret = regulator_enable(card_data->tdmadc_3p3_supply);
if (ret != 0)
dev_err(card->dev, "%s failed to enable tdm 3p3 supply %d!\n", __func__, ret);
}
return 0;
}
static struct snd_soc_card mt8516_pumpkin_card = {
.name = "mt-snd-card",
.owner = THIS_MODULE,
.dai_link = mt8516_pumpkin_dais,
.num_links = ARRAY_SIZE(mt8516_pumpkin_dais),
.controls = mt8516_pumpkin_soc_controls,
.num_controls = ARRAY_SIZE(mt8516_pumpkin_soc_controls),
.dapm_widgets = mt8516_pumpkin_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(mt8516_pumpkin_dapm_widgets),
.dapm_routes = mt8516_pumpkin_audio_map,
.num_dapm_routes = ARRAY_SIZE(mt8516_pumpkin_audio_map),
.suspend_post = mt8516_pumpkin_suspend_post,
.resume_pre = mt8516_pumpkin_resume_pre,
};
static int mt8516_pumpkin_gpio_probe(struct snd_soc_card *card)
{
struct mt8516_pumpkin_priv *card_data;
int ret = 0;
int i;
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,
mt8516_pumpkin_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__, mt8516_pumpkin_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;
}
}
exit:
return ret;
}
static int mt8516_pumpkin_regulator_probe(struct snd_soc_card *card)
{
struct mt8516_pumpkin_priv *card_data;
int isenable, vol, ret;
card_data = snd_soc_card_get_drvdata(card);
card_data->tdmadc_3p3_supply = devm_regulator_get(card->dev, "tdmadc-3p3v");
if (IS_ERR(card_data->tdmadc_3p3_supply)) {
ret = PTR_ERR(card_data->tdmadc_3p3_supply);
dev_err(card->dev, "%s failed to get tdmadc-3p3v regulator %d\n", __func__, ret);
return ret;
}
ret = regulator_set_voltage(card_data->tdmadc_3p3_supply, 3300000, 3300000);
if (ret != 0) {
dev_err(card->dev, "%s failed to set tdmadc-3p3v supply to 3.3v %d\n", __func__, ret);
return ret;
}
ret = regulator_enable(card_data->tdmadc_3p3_supply);
if (ret != 0) {
dev_err(card->dev, "%s failed to enable tdmadc 3p3 supply %d!\n", __func__, ret);
return ret;
}
isenable = regulator_is_enabled(card_data->tdmadc_3p3_supply);
if (isenable != 1)
dev_err(card->dev, "%s tdmadc 3.3V supply is not enabled\n", __func__);
vol = regulator_get_voltage(card_data->tdmadc_3p3_supply);
if (vol != 3300000)
dev_err(card->dev, "%s tdmadc 3p3 supply != 3.3V (%d)\n", __func__, vol);
card_data->tdmadc_1p8_supply = devm_regulator_get(card->dev, "tdmadc-1p8v");
if (IS_ERR(card_data->tdmadc_1p8_supply)) {
ret = PTR_ERR(card_data->tdmadc_1p8_supply);
dev_err(card->dev, "%s failed to get tdmadc-1p8v regulator %d\n", __func__, ret);
return ret;
}
ret = regulator_set_voltage(card_data->tdmadc_1p8_supply, 1800000, 1800000);
if (ret != 0) {
dev_err(card->dev, "%s failed to set tdmadc-1p8v supply to 1.8v %d\n", __func__, ret);
return ret;
}
ret = regulator_enable(card_data->tdmadc_1p8_supply);
if (ret != 0) {
dev_err(card->dev, "%s failed to enable tdmadc 1p8 supply %d!\n", __func__, ret);
return ret;
}
isenable = regulator_is_enabled(card_data->tdmadc_1p8_supply);
if (isenable != 1)
dev_err(card->dev, "%s tdmadc 1.8V supply is not enabled\n", __func__);
vol = regulator_get_voltage(card_data->tdmadc_1p8_supply);
if (vol != 1800000)
dev_err(card->dev, "%s tdmadc 1p8 supply != 1.8V (%d)\n", __func__, vol);
return 0;
}
static int mt8516_pumpkin_dev_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = &mt8516_pumpkin_card;
struct device_node *platform_node;
struct device_node *codec_node;
// struct device_node *tdmin_adc_node;
int ret, i;
struct mt8516_pumpkin_priv *card_data;
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;
}
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");
return -EINVAL;
}
/* tdmin_adc_node = of_parse_phandle(pdev->dev.of_node,
"mediatek,tdmin-adc", 0);
if (!tdmin_adc_node) {
dev_err(&pdev->dev, "Property 'tdmin-adc' missing or invalid\n");
}
*/
for (i = 0; i < card->num_links; i++) {
if (mt8516_pumpkin_dais[i].platform_name)
continue;
mt8516_pumpkin_dais[i].platform_of_node = platform_node;
}
for (i = 0; i < card->num_links; i++) {
/* if (tdmin_adc_node && (strcmp(mt8516_pumpkin_dais[i].cpu_dai_name, "TDM_IN") == 0)) {
mt8516_pumpkin_dais[i].codec_of_node = tdmin_adc_node;
mt8516_pumpkin_dais[i].codec_name = NULL;
continue;
}
if (mt8516_pumpkin_dais[i].codec_name)
continue;
mt8516_pumpkin_dais[i].codec_of_node = codec_node;
*/ }
card->dev = &pdev->dev;
card_data = devm_kzalloc(&pdev->dev,
sizeof(struct mt8516_pumpkin_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);
mt8516_pumpkin_regulator_probe(card);
mt8516_pumpkin_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;
}
soc_ctlx_init(&card_data->ctlx_res, card);
return ret;
}
static const struct of_device_id mt8516_pumpkin_dt_match[] = {
{ .compatible = "mediatek,mt8516-soc-pumpkin", },
{ }
};
MODULE_DEVICE_TABLE(of, mt8516_pumpkin_dt_match);
static struct platform_driver mt8516_pumpkin_mach_driver = {
.driver = {
.name = "mt8516-soc-pumpkin",
.of_match_table = mt8516_pumpkin_dt_match,
#ifdef CONFIG_PM
.pm = &snd_soc_pm_ops,
#endif
},
.probe = mt8516_pumpkin_dev_probe,
};
module_platform_driver(mt8516_pumpkin_mach_driver);
/* Module information */
MODULE_DESCRIPTION("MT8516Pumpkin ALSA SoC machine driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:mt8516-pumpkin");