| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * mt8532-evb.c -- MT8532 machine driver |
| * |
| * Copyright (c) 2022 MediaTek Inc. |
| * Author: Wen Cai <wen.cai@mediatek.com> |
| */ |
| |
| #include <linux/module.h> |
| #include <linux/of_gpio.h> |
| #include <linux/of_platform.h> |
| #include <sound/soc.h> |
| #include <sound/pcm_params.h> |
| #include "mt8532-afe-common.h" |
| #include "mt8532-afe-utils.h" |
| #include "mt8532-snd-utils.h" |
| |
| #define TEST_BACKEND_WITH_ENDPOINT |
| |
| #define PREFIX "mediatek," |
| #define ENUM_TO_STR(enum) #enum |
| #define MAX_CODEC_CONF 32 |
| |
| enum PINCTRL_PIN_STATE { |
| PIN_STATE_DEFAULT = 0, |
| PIN_STATE_MAX |
| }; |
| |
| static const char * const mt8532_evb_pin_str[PIN_STATE_MAX] = { |
| "default", |
| }; |
| |
| enum { |
| /* FE */ |
| DAI_LINK_FE_BASE = 0, |
| DAI_LINK_FE_AFE_BASE = DAI_LINK_FE_BASE, |
| DAI_LINK_FE_DLM_PLAYBACK = DAI_LINK_FE_AFE_BASE, |
| //DAI_LINK_FE_DL11_PLAYBACK, |
| DAI_LINK_FE_DL2_PLAYBACK/* = DAI_LINK_FE_AFE_BASE*/, |
| //DAI_LINK_FE_DL3_PLAYBACK, |
| //DAI_LINK_FE_DL6_PLAYBACK, |
| //DAI_LINK_FE_DL7_PLAYBACK, |
| DAI_LINK_FE_DL10_PLAYBACK, |
| |
| DAI_LINK_FE_UL1_CAPTURE, |
| DAI_LINK_FE_UL2_CAPTURE, |
| //DAI_LINK_FE_UL3_CAPTURE, |
| DAI_LINK_FE_UL4_CAPTURE, |
| //DAI_LINK_FE_UL5_CAPTURE, |
| //DAI_LINK_FE_UL6_CAPTURE, |
| //DAI_LINK_FE_UL8_CAPTURE, |
| //DAI_LINK_FE_UL9_CAPTURE, |
| //DAI_LINK_FE_UL10_CAPTURE, |
| DAI_LINK_FE_AFE_END, |
| DAI_LINK_FE_END = DAI_LINK_FE_AFE_END, |
| /* BE */ |
| DAI_LINK_BE_BASE = DAI_LINK_FE_END, |
| DAI_LINK_BE_AFE_BASE = DAI_LINK_BE_BASE, |
| DAI_LINK_BE_ETDM1_OUT = DAI_LINK_BE_AFE_BASE, |
| DAI_LINK_BE_ETDM1_IN, |
| DAI_LINK_BE_ETDM2_OUT, //= DAI_LINK_BE_AFE_BASE |
| DAI_LINK_BE_ETDM2_IN, |
| DAI_LINK_BE_ETDM3_OUT, |
| //DAI_LINK_BE_PCM_INTF, |
| //DAI_LINK_BE_VIRTUAL_DL_SOURCE, |
| //DAI_LINK_BE_DMIC, |
| DAI_LINK_BE_GASRC0, |
| DAI_LINK_BE_GASRC1, |
| DAI_LINK_BE_GASRC2, |
| DAI_LINK_BE_GASRC3, |
| |
| //DAI_LINK_BE_SPDIF_OUT, |
| DAI_LINK_BE_SPDIF_IN, |
| DAI_LINK_BE_MULTI_IN, |
| DAI_LINK_BE_VIRTUAL, |
| DAI_LINK_BE_AFE_END, |
| DAI_LINK_BE_END = DAI_LINK_BE_AFE_END, |
| DAI_LINK_NUM = DAI_LINK_BE_END, |
| DAI_LINK_FE_NUM = DAI_LINK_FE_END - DAI_LINK_FE_BASE, |
| DAI_LINK_BE_NUM = DAI_LINK_BE_END - DAI_LINK_BE_BASE, |
| }; |
| |
| struct mt8532_evb_codec_pll_clk_data { |
| unsigned int pll_id; |
| unsigned int src_id; |
| unsigned int clk_multp; |
| unsigned int freq_in; |
| unsigned int freq_out; |
| }; |
| |
| struct mt8532_evb_codec_dai_data { |
| const char *dai_name; |
| bool set_pll_clk_in_hw_params; |
| struct mt8532_evb_codec_pll_clk_data pll_clk; |
| }; |
| |
| struct mt8532_evb_be_ctrl_data { |
| unsigned int mck_multp; |
| unsigned int lrck_width; |
| unsigned int fix_rate; |
| unsigned int fix_channels; |
| unsigned int fix_bit_width; |
| unsigned int num_codec_dais; |
| struct mt8532_evb_codec_dai_data *codec_dai_data; |
| }; |
| |
| #ifdef CONFIG_SND_SOC_GAPP_AUDIO_CONTROL |
| 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 SOC_ENUM_SINGLE_EXT_DECL(pcm_state_enums, pcm_state_func); |
| |
| 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, |
| }; |
| |
| static const char *nfy_ctl_names[CTRL_NOTIFY_NUM] = { |
| "Master Volume 1", |
| "Master Volume X", |
| "Master Switch", |
| "Master Switch X", |
| "PCM State", |
| "PCM State X", |
| }; |
| |
| 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; |
| }; |
| #endif |
| |
| struct mt8532_evb_priv { |
| struct pinctrl *pinctrl; |
| struct pinctrl_state *pin_states[PIN_STATE_MAX]; |
| struct mt8532_evb_be_ctrl_data be_data[DAI_LINK_BE_NUM]; |
| struct device_node *afe_plat_node; |
| #ifdef CONFIG_SND_SOC_MT8570 |
| struct device_node *spi_plat_node; |
| #endif |
| #ifdef CONFIG_SND_SOC_GAPP_AUDIO_CONTROL |
| struct soc_ctlx_res ctlx_res; |
| #endif |
| struct snd_soc_codec_conf codec_conf[MAX_CODEC_CONF]; |
| unsigned int num_codec_configs; |
| }; |
| |
| #ifdef CONFIG_SND_SOC_GAPP_AUDIO_CONTROL |
| 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 (strcmp(control->id.name, nfy_ctl_names[i])) |
| 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 mt8532_evb_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_notice("%s invalid mixer control(numid:%d)\n", |
| __func__, 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_debug("%s (%s) value is:%ld\n", |
| __func__, 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 mt8532_evb_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_notice("%s invalid mixer control(numid:%d)\n", |
| __func__, 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_debug("%s (%s) value is:%ld, notify id:%x, notify self:%d\n", |
| __func__, 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 mt8532_evb_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_debug("%s (%s) value is:%ld\n", |
| __func__, 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 mt8532_evb_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_debug("%s (%s) value is:%ld\n", |
| __func__, kctl->id.name, |
| ucontrol->value.integer.value[0]); |
| return 0; |
| } |
| |
| static int dlm_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 mt8532_evb_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 dlm_playback_startup(struct snd_pcm_substream *substream) |
| { |
| dlm_playback_state_set(substream, MTKFILE_PCM_STATE_OPEN); |
| return 0; |
| } |
| |
| static void dlm_playback_shutdown(struct snd_pcm_substream *substream) |
| { |
| dlm_playback_state_set(substream, MTKFILE_PCM_STATE_CLOSE); |
| } |
| |
| static int dlm_playback_hw_params(struct snd_pcm_substream *substream, |
| struct snd_pcm_hw_params *params) |
| { |
| dlm_playback_state_set(substream, MTKFILE_PCM_STATE_HW_PARAMS); |
| return 0; |
| } |
| |
| static int dlm_playback_hw_free(struct snd_pcm_substream *substream) |
| { |
| dlm_playback_state_set(substream, MTKFILE_PCM_STATE_HW_FREE); |
| return 0; |
| } |
| |
| static int dlm_playback_trigger(struct snd_pcm_substream *substream, int cmd) |
| { |
| switch (cmd) { |
| case SNDRV_PCM_TRIGGER_START: |
| dlm_playback_state_set(substream, MTKFILE_PCM_STATE_START); |
| break; |
| default: |
| break; |
| } |
| return 0; |
| } |
| |
| static struct snd_soc_ops dlm_playback_ops = { |
| .startup = dlm_playback_startup, |
| .shutdown = dlm_playback_shutdown, |
| .hw_params = dlm_playback_hw_params, |
| .hw_free = dlm_playback_hw_free, |
| .trigger = dlm_playback_trigger, |
| }; |
| #endif |
| |
| struct mt8532_dai_link_prop { |
| char *name; |
| unsigned int link_id; |
| }; |
| |
| |
| |
| |
| static const struct snd_soc_dapm_widget mt8532_evb_widgets[] = { |
| //SND_SOC_DAPM_OUTPUT("HFP Out"), |
| //SND_SOC_DAPM_INPUT("HFP In"), |
| //SND_SOC_DAPM_INPUT("DMIC In"), |
| //SND_SOC_DAPM_SPK("Ext Spk Amp", mt8532_evb_ext_spk_amp_wevent), |
| #ifdef TEST_BACKEND_WITH_ENDPOINT |
| SND_SOC_DAPM_INPUT("MULTI In"), |
| SND_SOC_DAPM_OUTPUT("ETDM3 Out"), |
| SND_SOC_DAPM_INPUT("ETDM1 Out"), |
| SND_SOC_DAPM_INPUT("ETDM1 In"), |
| SND_SOC_DAPM_OUTPUT("ETDM2 Out"), |
| SND_SOC_DAPM_INPUT("ETDM2 In"), |
| #endif |
| }; |
| |
| static const struct snd_soc_dapm_route mt8532_evb_routes[] = { |
| //{"HFP Out", NULL, "PCM1 Playback"}, |
| //{"PCM1 Capture", NULL, "HFP In"}, |
| #ifdef TEST_BACKEND_WITH_ENDPOINT |
| {"MULTI Capture", NULL, "MULTI In"}, |
| {"ETDM3 Out", NULL, "ETDM3 Playback"}, |
| {"ETDM1 Out", NULL, "ETDM1 Playback"}, |
| {"ETDM1 Capture", NULL, "ETDM1 In"}, |
| {"ETDM2 Out", NULL, "ETDM2 Playback"}, |
| {"ETDM2 Capture", NULL, "ETDM2 In"}, |
| #endif |
| //{"DMIC Capture", NULL, "DMIC In"}, |
| |
| }; |
| |
| static int link_to_dai(int link_id) |
| { |
| switch (link_id) { |
| case DAI_LINK_BE_ETDM1_OUT: |
| return MT8532_AFE_IO_ETDM1_OUT; |
| case DAI_LINK_BE_ETDM1_IN: |
| return MT8532_AFE_IO_ETDM1_IN; |
| case DAI_LINK_BE_ETDM2_OUT: |
| return MT8532_AFE_IO_ETDM2_OUT; |
| case DAI_LINK_BE_ETDM2_IN: |
| return MT8532_AFE_IO_ETDM2_IN; |
| //case DAI_LINK_BE_PCM_INTF: |
| // return MT8532_AFE_IO_PCM1; |
| //case DAI_LINK_BE_DMIC: |
| // return MT8532_AFE_IO_DMIC; |
| default: |
| break; |
| } |
| return -1; |
| } |
| |
| static int mt8532_evb_hw_params(struct snd_pcm_substream *substream, |
| struct snd_pcm_hw_params *params) |
| { |
| struct snd_soc_pcm_runtime *rtd = substream->private_data; |
| struct mt8532_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 mt8532_evb_be_ctrl_data *be; |
| struct mt8532_evb_codec_dai_data *codec_dai_data; |
| struct mt8532_evb_codec_pll_clk_data *pll_clk; |
| struct snd_soc_dai *codec_dai; |
| 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, i; |
| int ret; |
| |
| if (id < DAI_LINK_BE_BASE || id >= DAI_LINK_BE_END) |
| return -EINVAL; |
| |
| idx = id - DAI_LINK_BE_BASE; |
| be = &priv->be_data[idx]; |
| |
| mclk_multiplier = be->mck_multp; |
| lrck_width = be->lrck_width; |
| |
| 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; |
| } |
| |
| for (i = 0; i < rtd->num_codecs; i++) { |
| codec_dai = rtd->codec_dais[i]; |
| codec_dai_data = &be->codec_dai_data[i]; |
| |
| if (codec_dai_data && |
| codec_dai_data->set_pll_clk_in_hw_params && |
| codec_dai) { |
| unsigned int freq_in; |
| unsigned int freq_out; |
| |
| pll_clk = &codec_dai_data->pll_clk; |
| |
| freq_in = pll_clk->freq_in; |
| freq_out = pll_clk->freq_out; |
| |
| if ((freq_in == 0) || (freq_out == 0)) { |
| freq_in = pll_clk->clk_multp * |
| params_rate(params); |
| freq_out = params_rate(params); |
| } |
| |
| snd_soc_dai_set_pll(codec_dai, |
| pll_clk->pll_id, |
| pll_clk->src_id, |
| freq_in, freq_out); |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int mt8532_evb_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, |
| struct snd_pcm_hw_params *params) |
| { |
| struct mt8532_evb_priv *priv = snd_soc_card_get_drvdata(rtd->card); |
| int id = rtd->dai_link->id; |
| struct mt8532_evb_be_ctrl_data *be; |
| unsigned int fix_rate = 0; |
| unsigned int fix_bit_width = 0; |
| unsigned int fix_channels = 0; |
| unsigned int idx; |
| |
| if (id < DAI_LINK_BE_BASE || id >= DAI_LINK_BE_END) |
| return -EINVAL; |
| |
| idx = id - DAI_LINK_BE_BASE; |
| be = &priv->be_data[idx]; |
| |
| fix_rate = be->fix_rate; |
| fix_bit_width = be->fix_bit_width; |
| fix_channels = be->fix_channels; |
| |
| 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 mt8532_evb_etdm_ops = { |
| .hw_params = mt8532_evb_hw_params, |
| }; |
| |
| #ifdef CONFIG_SND_SOC_MT8570 |
| static struct snd_soc_ops mt8532_evb_spi_be_ops = { |
| .hw_params = mt8532_evb_hw_params, |
| }; |
| #endif |
| |
| #define RSV_DAI_LNIK(x) \ |
| { \ |
| .name = #x "_FE", \ |
| .stream_name = #x, \ |
| .cpu_dai_name = "snd-soc-dummy-dai", \ |
| .codec_name = "snd-soc-dummy", \ |
| .codec_dai_name = "snd-soc-dummy-dai", \ |
| .platform_name = "snd-soc-dummy" \ |
| } |
| |
| /* FE */ |
| SND_SOC_DAILINK_DEFS(DLM_FE, |
| DAILINK_COMP_ARRAY(COMP_CPU("DLM")), |
| DAILINK_COMP_ARRAY(COMP_DUMMY()), |
| DAILINK_COMP_ARRAY(COMP_EMPTY())); |
| |
| SND_SOC_DAILINK_DEFS(DL2_FE, |
| DAILINK_COMP_ARRAY(COMP_CPU("DL2")), |
| DAILINK_COMP_ARRAY(COMP_DUMMY()), |
| DAILINK_COMP_ARRAY(COMP_EMPTY())); |
| |
| SND_SOC_DAILINK_DEFS(DL10_FE, |
| DAILINK_COMP_ARRAY(COMP_CPU("DL10")), |
| DAILINK_COMP_ARRAY(COMP_DUMMY()), |
| DAILINK_COMP_ARRAY(COMP_EMPTY())); |
| |
| SND_SOC_DAILINK_DEFS(UL1_FE, |
| DAILINK_COMP_ARRAY(COMP_CPU("UL1")), |
| DAILINK_COMP_ARRAY(COMP_DUMMY()), |
| DAILINK_COMP_ARRAY(COMP_EMPTY())); |
| |
| SND_SOC_DAILINK_DEFS(UL2_FE, |
| DAILINK_COMP_ARRAY(COMP_CPU("UL2")), |
| DAILINK_COMP_ARRAY(COMP_DUMMY()), |
| DAILINK_COMP_ARRAY(COMP_EMPTY())); |
| |
| SND_SOC_DAILINK_DEFS(UL4_FE, |
| DAILINK_COMP_ARRAY(COMP_CPU("UL4")), |
| DAILINK_COMP_ARRAY(COMP_DUMMY()), |
| DAILINK_COMP_ARRAY(COMP_EMPTY())); |
| |
| /* 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(ETDM1_IN_BE, |
| DAILINK_COMP_ARRAY(COMP_CPU("ETDM1_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())); |
| |
| 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(ETDM3_OUT_BE, |
| DAILINK_COMP_ARRAY(COMP_CPU("ETDM3_OUT")), |
| DAILINK_COMP_ARRAY(COMP_DUMMY()), |
| DAILINK_COMP_ARRAY(COMP_EMPTY())); |
| |
| SND_SOC_DAILINK_DEFS(GASRC0_BE, |
| DAILINK_COMP_ARRAY(COMP_CPU("GASRC0")), |
| DAILINK_COMP_ARRAY(COMP_DUMMY()), |
| DAILINK_COMP_ARRAY(COMP_EMPTY())); |
| |
| |
| SND_SOC_DAILINK_DEFS(GASRC1_BE, |
| DAILINK_COMP_ARRAY(COMP_CPU("GASRC1")), |
| DAILINK_COMP_ARRAY(COMP_DUMMY()), |
| DAILINK_COMP_ARRAY(COMP_EMPTY())); |
| |
| SND_SOC_DAILINK_DEFS(GASRC2_BE, |
| DAILINK_COMP_ARRAY(COMP_CPU("GASRC2")), |
| DAILINK_COMP_ARRAY(COMP_DUMMY()), |
| DAILINK_COMP_ARRAY(COMP_EMPTY())); |
| |
| SND_SOC_DAILINK_DEFS(GASRC3_BE, |
| DAILINK_COMP_ARRAY(COMP_CPU("GASRC3")), |
| DAILINK_COMP_ARRAY(COMP_DUMMY()), |
| DAILINK_COMP_ARRAY(COMP_EMPTY())); |
| |
| SND_SOC_DAILINK_DEFS(SPDIF_IN_BE, |
| DAILINK_COMP_ARRAY(COMP_CPU("SPDIF_IN")), |
| DAILINK_COMP_ARRAY(COMP_DUMMY()), |
| DAILINK_COMP_ARRAY(COMP_EMPTY())); |
| |
| SND_SOC_DAILINK_DEFS(MULTI_IN_BE, |
| DAILINK_COMP_ARRAY(COMP_CPU("MULTI_IN")), |
| DAILINK_COMP_ARRAY(COMP_DUMMY()), |
| DAILINK_COMP_ARRAY(COMP_EMPTY())); |
| |
| SND_SOC_DAILINK_DEFS(VIRTUAL_IN_OUT_BE, |
| DAILINK_COMP_ARRAY(COMP_CPU("VIRTUAL_IN_OUT")), |
| DAILINK_COMP_ARRAY(COMP_DUMMY()), |
| DAILINK_COMP_ARRAY(COMP_EMPTY())); |
| |
| |
| |
| |
| /* Digital audio interface glue - connects codec <---> CPU */ |
| static struct snd_soc_dai_link mt8532_evb_dais[] = { |
| /* Front End DAI links */ |
| |
| [DAI_LINK_FE_DLM_PLAYBACK] = { |
| .name = "DLM_FE", |
| .stream_name = "DLM Playback", |
| .id = DAI_LINK_FE_DLM_PLAYBACK, |
| .trigger = { |
| SND_SOC_DPCM_TRIGGER_POST, |
| SND_SOC_DPCM_TRIGGER_POST |
| }, |
| .dynamic = 1, |
| .dpcm_playback = 1, |
| #ifdef CONFIG_SND_SOC_GAPP_AUDIO_CONTROL |
| .ops = &dlm_playback_ops, |
| #endif |
| SND_SOC_DAILINK_REG(DLM_FE), |
| }, |
| #if 0 |
| [DAI_LINK_FE_DL11_PLAYBACK] = { |
| .name = "DL11_FE", |
| .stream_name = "DL11 Playback", |
| .cpu_dai_name = "DL11", |
| .codec_name = "snd-soc-dummy", |
| .codec_dai_name = "snd-soc-dummy-dai", |
| .id = DAI_LINK_FE_DL11_PLAYBACK, |
| .trigger = { |
| SND_SOC_DPCM_TRIGGER_POST, |
| SND_SOC_DPCM_TRIGGER_POST |
| }, |
| .dynamic = 1, |
| .dpcm_playback = 1, |
| }, |
| #endif |
| [DAI_LINK_FE_DL2_PLAYBACK] = { |
| .name = "DL2_FE", |
| .stream_name = "DL2 Playback", |
| .id = DAI_LINK_FE_DL2_PLAYBACK, |
| .trigger = { |
| SND_SOC_DPCM_TRIGGER_POST, |
| SND_SOC_DPCM_TRIGGER_POST |
| }, |
| .dynamic = 1, |
| .dpcm_playback = 1, |
| SND_SOC_DAILINK_REG(DL2_FE), |
| }, |
| #if 0 |
| [DAI_LINK_FE_DL3_PLAYBACK] = { |
| .name = "DL3_FE", |
| .stream_name = "DL3 Playback", |
| .cpu_dai_name = "DL3", |
| .codec_name = "snd-soc-dummy", |
| .codec_dai_name = "snd-soc-dummy-dai", |
| .id = DAI_LINK_FE_DL3_PLAYBACK, |
| .trigger = { |
| SND_SOC_DPCM_TRIGGER_POST, |
| SND_SOC_DPCM_TRIGGER_POST |
| }, |
| .dynamic = 1, |
| .dpcm_playback = 1, |
| }, |
| [DAI_LINK_FE_DL6_PLAYBACK] = { |
| .name = "DL6_FE", |
| .stream_name = "DL6 Playback", |
| .cpu_dai_name = "DL6", |
| .codec_name = "snd-soc-dummy", |
| .codec_dai_name = "snd-soc-dummy-dai", |
| .id = DAI_LINK_FE_DL6_PLAYBACK, |
| .trigger = { |
| SND_SOC_DPCM_TRIGGER_POST, |
| SND_SOC_DPCM_TRIGGER_POST |
| }, |
| .dynamic = 1, |
| .dpcm_playback = 1, |
| }, |
| [DAI_LINK_FE_DL7_PLAYBACK] = { |
| .name = "DL7_FE", |
| .stream_name = "DL7 Playback", |
| .cpu_dai_name = "DL7", |
| .codec_name = "snd-soc-dummy", |
| .codec_dai_name = "snd-soc-dummy-dai", |
| .id = DAI_LINK_FE_DL7_PLAYBACK, |
| .trigger = { |
| SND_SOC_DPCM_TRIGGER_POST, |
| SND_SOC_DPCM_TRIGGER_POST |
| }, |
| .dynamic = 1, |
| .dpcm_playback = 1, |
| }, |
| #endif |
| [DAI_LINK_FE_DL10_PLAYBACK] = { |
| .name = "DL10_FE", |
| .stream_name = "DL10 Playback", |
| .id = DAI_LINK_FE_DL10_PLAYBACK, |
| .trigger = { |
| SND_SOC_DPCM_TRIGGER_POST, |
| SND_SOC_DPCM_TRIGGER_POST |
| }, |
| .dynamic = 1, |
| .dpcm_playback = 1, |
| SND_SOC_DAILINK_REG(DL10_FE), |
| }, |
| [DAI_LINK_FE_UL1_CAPTURE] = { |
| .name = "UL1_FE", |
| .stream_name = "UL1 Capture", |
| .id = DAI_LINK_FE_UL1_CAPTURE, |
| .trigger = { |
| SND_SOC_DPCM_TRIGGER_PRE, |
| SND_SOC_DPCM_TRIGGER_PRE |
| }, |
| .dynamic = 1, |
| .dpcm_capture = 1, |
| SND_SOC_DAILINK_REG(UL1_FE), |
| }, |
| [DAI_LINK_FE_UL2_CAPTURE] = { |
| .name = "UL2_FE", |
| .stream_name = "UL2 Capture", |
| .id = DAI_LINK_FE_UL2_CAPTURE, |
| .trigger = { |
| SND_SOC_DPCM_TRIGGER_POST, |
| SND_SOC_DPCM_TRIGGER_POST |
| }, |
| .dynamic = 1, |
| .dpcm_capture = 1, |
| SND_SOC_DAILINK_REG(UL2_FE), |
| }, |
| #if 0 |
| [DAI_LINK_FE_UL3_CAPTURE] = { |
| .name = "UL3_FE", |
| .stream_name = "UL3 Capture", |
| .cpu_dai_name = "UL3", |
| .codec_name = "snd-soc-dummy", |
| .codec_dai_name = "snd-soc-dummy-dai", |
| .id = DAI_LINK_FE_UL3_CAPTURE, |
| .trigger = { |
| SND_SOC_DPCM_TRIGGER_POST, |
| SND_SOC_DPCM_TRIGGER_POST |
| }, |
| .dynamic = 1, |
| .dpcm_capture = 1, |
| }, |
| #endif |
| [DAI_LINK_FE_UL4_CAPTURE] = { |
| .name = "UL4_FE", |
| .stream_name = "UL4 Capture", |
| .id = DAI_LINK_FE_UL4_CAPTURE, |
| .trigger = { |
| SND_SOC_DPCM_TRIGGER_POST, |
| SND_SOC_DPCM_TRIGGER_POST |
| }, |
| .dynamic = 1, |
| .dpcm_capture = 1, |
| SND_SOC_DAILINK_REG(UL4_FE), |
| }, |
| #if 0 |
| [DAI_LINK_FE_UL5_CAPTURE] = { |
| .name = "UL5_FE", |
| .stream_name = "UL5 Capture", |
| .cpu_dai_name = "UL5", |
| .codec_name = "snd-soc-dummy", |
| .codec_dai_name = "snd-soc-dummy-dai", |
| .id = DAI_LINK_FE_UL5_CAPTURE, |
| .trigger = { |
| SND_SOC_DPCM_TRIGGER_POST, |
| SND_SOC_DPCM_TRIGGER_POST |
| }, |
| .dynamic = 1, |
| .dpcm_capture = 1, |
| }, |
| [DAI_LINK_FE_UL6_CAPTURE] = { |
| .name = "UL6_FE", |
| .stream_name = "UL6 Capture", |
| .cpu_dai_name = "UL6", |
| .codec_name = "snd-soc-dummy", |
| .codec_dai_name = "snd-soc-dummy-dai", |
| .id = DAI_LINK_FE_UL6_CAPTURE, |
| .trigger = { |
| SND_SOC_DPCM_TRIGGER_POST, |
| SND_SOC_DPCM_TRIGGER_POST |
| }, |
| .dynamic = 1, |
| .dpcm_capture = 1, |
| }, |
| [DAI_LINK_FE_UL8_CAPTURE] = { |
| .name = "UL8_FE", |
| .stream_name = "UL8 Capture", |
| .cpu_dai_name = "UL8", |
| .codec_name = "snd-soc-dummy", |
| .codec_dai_name = "snd-soc-dummy-dai", |
| .id = DAI_LINK_FE_UL8_CAPTURE, |
| .trigger = { |
| SND_SOC_DPCM_TRIGGER_POST, |
| SND_SOC_DPCM_TRIGGER_POST |
| }, |
| .dynamic = 1, |
| .dpcm_capture = 1, |
| }, |
| [DAI_LINK_FE_UL9_CAPTURE] = { |
| .name = "UL9_FE", |
| .stream_name = "UL9 Capture", |
| .cpu_dai_name = "UL9", |
| .codec_name = "snd-soc-dummy", |
| .codec_dai_name = "snd-soc-dummy-dai", |
| .id = DAI_LINK_FE_UL9_CAPTURE, |
| .trigger = { |
| SND_SOC_DPCM_TRIGGER_POST, |
| SND_SOC_DPCM_TRIGGER_POST |
| }, |
| .dynamic = 1, |
| .dpcm_capture = 1, |
| }, |
| [DAI_LINK_FE_UL10_CAPTURE] = { |
| .name = "UL10_FE", |
| .stream_name = "UL10 Capture", |
| .cpu_dai_name = "UL10", |
| .codec_name = "snd-soc-dummy", |
| .codec_dai_name = "snd-soc-dummy-dai", |
| .id = DAI_LINK_FE_UL10_CAPTURE, |
| .trigger = { |
| SND_SOC_DPCM_TRIGGER_POST, |
| SND_SOC_DPCM_TRIGGER_POST |
| }, |
| .dynamic = 1, |
| .dpcm_capture = 1, |
| }, |
| |
| #endif |
| |
| /* Back End DAI links */ |
| [DAI_LINK_BE_ETDM1_OUT] = { |
| .name = "ETDM1_OUT BE", |
| .no_pcm = 1, |
| .id = DAI_LINK_BE_ETDM1_OUT, |
| .dai_fmt = SND_SOC_DAIFMT_I2S | |
| SND_SOC_DAIFMT_NB_NF | |
| SND_SOC_DAIFMT_CBS_CFS, |
| .ops = &mt8532_evb_etdm_ops, |
| .dpcm_playback = 1, |
| SND_SOC_DAILINK_REG(ETDM1_OUT_BE), |
| }, |
| [DAI_LINK_BE_ETDM1_IN] = { |
| .name = "ETDM1_IN BE", |
| .no_pcm = 1, |
| .id = DAI_LINK_BE_ETDM1_IN, |
| .dai_fmt = SND_SOC_DAIFMT_I2S | |
| SND_SOC_DAIFMT_NB_NF | |
| SND_SOC_DAIFMT_CBM_CFM, /* set be slave*/ |
| .ops = &mt8532_evb_etdm_ops, |
| .dpcm_capture = 1, |
| SND_SOC_DAILINK_REG(ETDM1_IN_BE), |
| }, |
| [DAI_LINK_BE_ETDM2_OUT] = { |
| .name = "ETDM2_OUT BE", |
| .no_pcm = 1, |
| .id = DAI_LINK_BE_ETDM2_OUT, |
| .dai_fmt = SND_SOC_DAIFMT_I2S | |
| SND_SOC_DAIFMT_NB_NF | |
| SND_SOC_DAIFMT_CBS_CFS, |
| .ops = &mt8532_evb_etdm_ops, |
| .dpcm_playback = 1, |
| SND_SOC_DAILINK_REG(ETDM2_OUT_BE), |
| }, |
| [DAI_LINK_BE_ETDM2_IN] = { |
| .name = "ETDM2_IN BE", |
| .no_pcm = 1, |
| .id = DAI_LINK_BE_ETDM2_IN, |
| .dai_fmt = SND_SOC_DAIFMT_I2S | |
| SND_SOC_DAIFMT_NB_NF | |
| SND_SOC_DAIFMT_CBM_CFM, /* set be slave*/ |
| .ops = &mt8532_evb_etdm_ops, |
| .dpcm_capture = 1, |
| SND_SOC_DAILINK_REG(ETDM2_IN_BE), |
| }, |
| [DAI_LINK_BE_ETDM3_OUT] = { |
| .name = "ETDM3_OUT BE", |
| .no_pcm = 1, |
| .id = DAI_LINK_BE_ETDM3_OUT, |
| .dai_fmt = SND_SOC_DAIFMT_I2S | |
| SND_SOC_DAIFMT_NB_NF | |
| SND_SOC_DAIFMT_CBS_CFS, |
| .ops = &mt8532_evb_etdm_ops, |
| .dpcm_playback = 1, |
| SND_SOC_DAILINK_REG(ETDM3_OUT_BE), |
| }, |
| #if 0 |
| [DAI_LINK_BE_PCM_INTF] = { |
| .name = "PCM1 BE", |
| .cpu_dai_name = "PCM1", |
| .no_pcm = 1, |
| .codec_name = "snd-soc-dummy", |
| .codec_dai_name = "snd-soc-dummy-dai", |
| .id = DAI_LINK_BE_PCM_INTF, |
| .dai_fmt = SND_SOC_DAIFMT_I2S | |
| SND_SOC_DAIFMT_NB_NF | |
| SND_SOC_DAIFMT_CBS_CFS, |
| .dpcm_playback = 1, |
| .dpcm_capture = 1, |
| }, |
| [DAI_LINK_BE_DMIC] = { |
| .name = "DMIC BE", |
| .cpu_dai_name = "DMIC", |
| .no_pcm = 1, |
| .codec_name = "snd-soc-dummy", |
| .codec_dai_name = "snd-soc-dummy-dai", |
| .id = DAI_LINK_BE_DMIC, |
| .dpcm_capture = 1, |
| }, |
| #endif |
| |
| [DAI_LINK_BE_GASRC0] = { |
| .name = "GASRC0 BE", |
| .no_pcm = 1, |
| .id = DAI_LINK_BE_GASRC0, |
| .dpcm_playback = 1, |
| .dpcm_capture = 1, |
| SND_SOC_DAILINK_REG(GASRC0_BE), |
| }, |
| #if 1 |
| [DAI_LINK_BE_GASRC1] = { |
| .name = "GASRC1 BE", |
| .no_pcm = 1, |
| .codec_name = "snd-soc-dummy", |
| .codec_dai_name = "snd-soc-dummy-dai", |
| .id = DAI_LINK_BE_GASRC1, |
| .dpcm_playback = 1, |
| .dpcm_capture = 1, |
| SND_SOC_DAILINK_REG(GASRC1_BE), |
| }, |
| [DAI_LINK_BE_GASRC2] = { |
| .name = "GASRC2 BE", |
| .no_pcm = 1, |
| .id = DAI_LINK_BE_GASRC2, |
| .dpcm_playback = 1, |
| .dpcm_capture = 1, |
| SND_SOC_DAILINK_REG(GASRC2_BE), |
| }, |
| [DAI_LINK_BE_GASRC3] = { |
| .name = "GASRC3 BE", |
| .no_pcm = 1, |
| .id = DAI_LINK_BE_GASRC3, |
| .dpcm_playback = 1, |
| .dpcm_capture = 1, |
| SND_SOC_DAILINK_REG(GASRC3_BE), |
| }, |
| #endif |
| #if 0 |
| [DAI_LINK_BE_SPDIF_OUT] = { |
| .name = "SPDIF_OUT BE", |
| .cpu_dai_name = "SPDIF_OUT", |
| .no_pcm = 1, |
| .codec_name = "snd-soc-dummy", |
| .codec_dai_name = "snd-soc-dummy-dai", |
| .id = DAI_LINK_BE_SPDIF_OUT, |
| .dpcm_playback = 1, |
| }, |
| #endif |
| [DAI_LINK_BE_SPDIF_IN] = { |
| .name = "SPDIF_IN BE", |
| .no_pcm = 1, |
| .id = DAI_LINK_BE_SPDIF_IN, |
| .dpcm_capture = 1, |
| SND_SOC_DAILINK_REG(SPDIF_IN_BE), |
| }, |
| [DAI_LINK_BE_MULTI_IN] = { |
| .name = "MULTI_IN BE", |
| .no_pcm = 1, |
| .id = DAI_LINK_BE_MULTI_IN, |
| .dai_fmt = SND_SOC_DAIFMT_I2S | |
| SND_SOC_DAIFMT_NB_NF | |
| SND_SOC_DAIFMT_CBM_CFM, |
| .dpcm_capture = 1, |
| SND_SOC_DAILINK_REG(MULTI_IN_BE), |
| }, |
| [DAI_LINK_BE_VIRTUAL] = { |
| .name = "VIRTUAL_IN_OUT BE", |
| .no_pcm = 1, |
| .id = DAI_LINK_BE_VIRTUAL, |
| .dai_fmt = SND_SOC_DAIFMT_I2S | |
| SND_SOC_DAIFMT_NB_NF | |
| SND_SOC_DAIFMT_CBM_CFM, |
| .dpcm_playback = 1, |
| .dpcm_capture = 1, |
| SND_SOC_DAILINK_REG(VIRTUAL_IN_OUT_BE), |
| }, |
| }; |
| |
| static const struct snd_kcontrol_new mt8532_evb_controls[] = { |
| #ifdef CONFIG_SND_SOC_GAPP_AUDIO_CONTROL |
| SOC_SINGLE_EXT("Master Volume 1", 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), |
| #endif |
| }; |
| |
| static int mt8532_evb_gpio_probe(struct snd_soc_card *card) |
| { |
| struct mt8532_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_dbg(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, |
| mt8532_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__, mt8532_evb_pin_str[i], ret); |
| } |
| } |
| |
| if (IS_ERR(priv->pin_states[PIN_STATE_DEFAULT])) { |
| dev_dbg(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_dbg(card->dev, "%s failed to select state %d\n", |
| __func__, ret); |
| |
| return ret; |
| } |
| |
| static void mt8532_evb_parse_of_codec_dai_data( |
| struct mt8532_evb_codec_dai_data *codec_dai_data, |
| struct device_node *np, |
| const char *name) |
| { |
| char prop[128]; |
| unsigned int val; |
| int ret; |
| struct mt8532_evb_codec_pll_clk_data *pll_clk; |
| |
| pll_clk = &codec_dai_data->pll_clk; |
| |
| snprintf(prop, sizeof(prop), |
| PREFIX"%s-set-pll-clk-in-hw-params", name); |
| codec_dai_data->set_pll_clk_in_hw_params = |
| of_property_read_bool(np, prop); |
| |
| snprintf(prop, sizeof(prop), PREFIX"%s-pll-id", name); |
| ret = of_property_read_u32(np, prop, &val); |
| if (ret == 0) |
| pll_clk->pll_id = val; |
| |
| snprintf(prop, sizeof(prop), PREFIX"%s-pll-src-id", name); |
| ret = of_property_read_u32(np, prop, &val); |
| if (ret == 0) |
| pll_clk->src_id = val; |
| |
| snprintf(prop, sizeof(prop), |
| PREFIX"%s-pll-clk-multiplier", name); |
| ret = of_property_read_u32(np, prop, &val); |
| if (ret == 0) |
| pll_clk->clk_multp = val; |
| |
| snprintf(prop, sizeof(prop), PREFIX"%s-pll-freq-in", name); |
| ret = of_property_read_u32(np, prop, &val); |
| if (ret == 0) |
| pll_clk->freq_in = val; |
| |
| snprintf(prop, sizeof(prop), PREFIX"%s-pll-freq-out", name); |
| ret = of_property_read_u32(np, prop, &val); |
| if (ret == 0) |
| pll_clk->freq_out = val; |
| } |
| |
| static void mt8532_evb_parse_of_codec(struct device *dev, |
| struct device_node *np, |
| struct snd_soc_dai_link *dai_link, |
| struct mt8532_evb_priv *priv, |
| struct mt8532_evb_be_ctrl_data *be, |
| char *name) |
| { |
| char prop[128]; |
| const char *of_str; |
| int ret; |
| unsigned int i, num_codecs, idx; |
| struct device_node *codec_node; |
| struct snd_soc_codec_conf *codec_conf; |
| struct mt8532_evb_codec_dai_data *codec_dai_data; |
| |
| snprintf(prop, sizeof(prop), PREFIX"%s-audio-codec-num", name); |
| ret = of_property_read_u32(np, prop, &num_codecs); |
| if (ret) |
| goto single_codec; |
| |
| if (num_codecs == 0) |
| return; |
| |
| dai_link->codecs = devm_kzalloc(dev, |
| num_codecs * sizeof(struct snd_soc_dai_link_component), |
| GFP_KERNEL); |
| |
| dai_link->num_codecs = num_codecs; |
| dai_link->codec_name = NULL; |
| dai_link->codec_of_node = NULL; |
| dai_link->codec_dai_name = NULL; |
| |
| be->num_codec_dais = num_codecs; |
| be->codec_dai_data = devm_kzalloc(dev, |
| be->num_codec_dais * |
| sizeof(struct mt8532_evb_codec_dai_data), |
| GFP_KERNEL); |
| |
| for (i = 0; i < num_codecs; i++) { |
| codec_node = NULL; |
| |
| // parse codec_of_node |
| snprintf(prop, sizeof(prop), |
| PREFIX"%s-audio-codec%u", |
| name, i); |
| codec_node = of_parse_phandle(np, prop, 0); |
| if (codec_node) |
| dai_link->codecs[i].of_node = codec_node; |
| else { |
| // parse codec name |
| snprintf(prop, sizeof(prop), |
| PREFIX"%s-codec-name%u", |
| name, i); |
| of_property_read_string(np, prop, |
| &dai_link->codecs[i].name); |
| } |
| |
| of_str = NULL; |
| |
| // parse codec prefix |
| snprintf(prop, sizeof(prop), |
| PREFIX"%s-audio-codec%u-prefix", |
| name, i); |
| of_property_read_string(np, prop, &of_str); |
| if (of_str) { |
| idx = priv->num_codec_configs; |
| |
| if (idx < MAX_CODEC_CONF) { |
| codec_conf = &priv->codec_conf[idx]; |
| |
| if (codec_node) |
| codec_conf->of_node = codec_node; |
| else |
| codec_conf->dev_name = |
| dai_link->codecs[i].name; |
| |
| priv->codec_conf[idx].name_prefix = of_str; |
| priv->num_codec_configs++; |
| } |
| } |
| |
| of_str = NULL; |
| |
| // parse codec dai name |
| snprintf(prop, sizeof(prop), |
| PREFIX"%s-codec-dai-name%u", |
| name, i); |
| of_property_read_string(np, prop, &of_str); |
| if (of_str) { |
| dai_link->codecs[i].dai_name = of_str; |
| |
| codec_dai_data = &be->codec_dai_data[i]; |
| codec_dai_data->dai_name = of_str; |
| |
| mt8532_evb_parse_of_codec_dai_data(codec_dai_data, |
| np, codec_dai_data->dai_name); |
| } |
| of_node_put(codec_node); |
| } |
| |
| return; |
| |
| single_codec: |
| // parse codec_of_node |
| snprintf(prop, sizeof(prop), PREFIX"%s-audio-codec", name); |
| codec_node = of_parse_phandle(np, prop, 0); |
| if (codec_node) { |
| dai_link->codec_of_node = codec_node; |
| dai_link->codec_name = NULL; |
| } |
| |
| of_str = NULL; |
| |
| // parse codec prefix |
| snprintf(prop, sizeof(prop), PREFIX"%s-audio-codec-prefix", name); |
| of_property_read_string(np, prop, &of_str); |
| if (of_str) { |
| idx = priv->num_codec_configs; |
| |
| if (idx < MAX_CODEC_CONF) { |
| codec_conf = &priv->codec_conf[idx]; |
| |
| if (codec_node) |
| codec_conf->of_node = codec_node; |
| else |
| codec_conf->dev_name = |
| dai_link->codec_name; |
| |
| priv->codec_conf[idx].name_prefix = of_str; |
| priv->num_codec_configs++; |
| } |
| } |
| |
| of_str = NULL; |
| |
| // parse codec dai name |
| snprintf(prop, sizeof(prop), PREFIX"%s-codec-dai-name", name); |
| of_property_read_string(np, prop, &of_str); |
| if (of_str) { |
| dai_link->codec_dai_name = of_str; |
| |
| be->num_codec_dais = 1; |
| be->codec_dai_data = devm_kzalloc(dev, |
| be->num_codec_dais * |
| sizeof(struct mt8532_evb_codec_dai_data), |
| GFP_KERNEL); |
| |
| codec_dai_data = &be->codec_dai_data[0]; |
| codec_dai_data->dai_name = of_str; |
| |
| mt8532_evb_parse_of_codec_dai_data(codec_dai_data, |
| np, codec_dai_data->dai_name); |
| } |
| of_node_put(codec_node); |
| } |
| |
| static void mt8532_evb_parse_of(struct snd_soc_card *card, |
| struct device_node *np) |
| { |
| struct mt8532_evb_priv *priv = snd_soc_card_get_drvdata(card); |
| size_t i; |
| int ret; |
| char prop[128]; |
| const char *str; |
| unsigned int val; |
| unsigned int vals[2]; |
| struct snd_soc_dai_link *dai_link; |
| unsigned int link_id; |
| |
| static const struct mt8532_dai_link_prop of_dai_links_fe[] = { |
| //{"dlm", DAI_LINK_FE_DLM_PLAYBACK}, |
| //{"dl11", DAI_LINK_FE_DL11_PLAYBACK}, |
| //{"dl2", DAI_LINK_FE_DL2_PLAYBACK}, |
| //{"dl3", DAI_LINK_FE_DL3_PLAYBACK}, |
| //{"dl6", DAI_LINK_FE_DL6_PLAYBACK}, |
| //{"ul2", DAI_LINK_FE_UL2_CAPTURE}, |
| //{"ul3", DAI_LINK_FE_UL3_CAPTURE}, |
| {"ul4", DAI_LINK_FE_UL4_CAPTURE}, |
| //{"ul5", DAI_LINK_FE_UL5_CAPTURE}, |
| //{"ul8", DAI_LINK_FE_UL8_CAPTURE}, |
| //{"ul9", DAI_LINK_FE_UL9_CAPTURE}, |
| //{"ul10", DAI_LINK_FE_UL10_CAPTURE}, |
| }; |
| |
| static const struct mt8532_dai_link_prop of_dai_links_be[] = { |
| {"etdm1-out", DAI_LINK_BE_ETDM1_OUT}, |
| {"etdm1-in", DAI_LINK_BE_ETDM1_IN}, |
| {"etdm2-out", DAI_LINK_BE_ETDM2_OUT}, |
| {"etdm2-in", DAI_LINK_BE_ETDM2_IN}, |
| //{"pcm-intf", DAI_LINK_BE_PCM_INTF}, |
| //{"dmic", DAI_LINK_BE_DMIC}, |
| //{"multi-in", DAI_LINK_BE_MULTI_IN}, |
| }; |
| |
| snd_soc_of_parse_card_name(card, PREFIX"card-name"); |
| |
| if (of_property_read_bool(np, PREFIX "widgets")) { |
| snd_soc_of_parse_audio_simple_widgets(card, |
| PREFIX "widgets"); |
| } |
| |
| if (of_property_read_bool(np, PREFIX "routing")) { |
| snd_soc_of_parse_audio_routing(card, |
| PREFIX "routing"); |
| } |
| |
| for (i = 0; i < ARRAY_SIZE(of_dai_links_fe); i++) { |
| bool lrck_inverse = false; |
| bool bck_inverse = false; |
| |
| link_id = of_dai_links_fe[i].link_id; |
| dai_link = &mt8532_evb_dais[link_id]; |
| |
| // parse format |
| snprintf(prop, sizeof(prop), PREFIX"%s-format", |
| of_dai_links_fe[i].name); |
| ret = of_property_read_string(np, prop, &str); |
| if (ret == 0) { |
| unsigned int format = 0; |
| |
| format = mt8532_snd_get_dai_format(str); |
| |
| dai_link->dai_fmt &= ~SND_SOC_DAIFMT_FORMAT_MASK; |
| dai_link->dai_fmt |= format; |
| } |
| |
| // parse clock mode |
| snprintf(prop, sizeof(prop), PREFIX"%s-master-clock", |
| of_dai_links_fe[i].name); |
| 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 |
| snprintf(prop, sizeof(prop), PREFIX"%s-lrck-inverse", |
| of_dai_links_fe[i].name); |
| lrck_inverse = of_property_read_bool(np, prop); |
| |
| // parse bck inverse |
| snprintf(prop, sizeof(prop), PREFIX"%s-bck-inverse", |
| of_dai_links_fe[i].name); |
| 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_NB_IF; |
| else if (!lrck_inverse && bck_inverse) |
| dai_link->dai_fmt |= SND_SOC_DAIFMT_IB_NF; |
| else |
| dai_link->dai_fmt |= SND_SOC_DAIFMT_NB_NF; |
| |
| // parse trigger order |
| snprintf(prop, sizeof(prop), PREFIX"%s-trigger-order", |
| of_dai_links_fe[i].name); |
| ret = of_property_read_u32_array(np, prop, vals, 2); |
| if (ret == 0) { |
| if (vals[0] <= SND_SOC_DPCM_TRIGGER_BESPOKE) |
| dai_link->trigger[0] = vals[0]; |
| |
| if (vals[1] <= SND_SOC_DPCM_TRIGGER_BESPOKE) |
| dai_link->trigger[1] = vals[1]; |
| } |
| } |
| |
| for (i = 0; i < ARRAY_SIZE(of_dai_links_be); i++) { |
| struct mt8532_evb_be_ctrl_data *be; |
| bool lrck_inverse = false; |
| bool bck_inverse = false; |
| bool hook_be_fixup_cb = false; |
| |
| link_id = of_dai_links_be[i].link_id; |
| |
| if ((link_id < DAI_LINK_BE_BASE) || |
| (link_id >= DAI_LINK_BE_END)) |
| continue; |
| |
| dai_link = &mt8532_evb_dais[link_id]; |
| be = &priv->be_data[link_id - DAI_LINK_BE_BASE]; |
| |
| // parse format |
| snprintf(prop, sizeof(prop), PREFIX"%s-format", |
| of_dai_links_be[i].name); |
| ret = of_property_read_string(np, prop, &str); |
| if (ret == 0) { |
| unsigned int format = 0; |
| |
| format = mt8532_snd_get_dai_format(str); |
| |
| dai_link->dai_fmt &= ~SND_SOC_DAIFMT_FORMAT_MASK; |
| dai_link->dai_fmt |= format; |
| } |
| |
| // parse clock mode |
| snprintf(prop, sizeof(prop), PREFIX"%s-master-clock", |
| of_dai_links_be[i].name); |
| 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 |
| snprintf(prop, sizeof(prop), PREFIX"%s-lrck-inverse", |
| of_dai_links_be[i].name); |
| lrck_inverse = of_property_read_bool(np, prop); |
| |
| // parse bck inverse |
| snprintf(prop, sizeof(prop), PREFIX"%s-bck-inverse", |
| of_dai_links_be[i].name); |
| 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_NB_IF; |
| else if (!lrck_inverse && bck_inverse) |
| dai_link->dai_fmt |= SND_SOC_DAIFMT_IB_NF; |
| else |
| dai_link->dai_fmt |= SND_SOC_DAIFMT_NB_NF; |
| |
| // parse mclk multiplier |
| snprintf(prop, sizeof(prop), PREFIX"%s-mclk-multiplier", |
| of_dai_links_be[i].name); |
| ret = of_property_read_u32(np, prop, &val); |
| if (ret == 0) |
| be->mck_multp = val; |
| |
| // parse lrck width |
| snprintf(prop, sizeof(prop), PREFIX"%s-lrck-width", |
| of_dai_links_be[i].name); |
| ret = of_property_read_u32(np, prop, &val); |
| if (ret == 0) |
| be->lrck_width = val; |
| |
| // parse fix rate |
| snprintf(prop, sizeof(prop), PREFIX"%s-fix-rate", |
| of_dai_links_be[i].name); |
| ret = of_property_read_u32(np, prop, &val); |
| if ((ret == 0) && ((link_to_dai(link_id) < 0) || |
| mt8532_afe_rate_supported(val, link_to_dai(link_id)))) { |
| be->fix_rate = val; |
| hook_be_fixup_cb = true; |
| } |
| |
| // parse fix bit width |
| snprintf(prop, sizeof(prop), PREFIX"%s-fix-bit-width", |
| of_dai_links_be[i].name); |
| ret = of_property_read_u32(np, prop, &val); |
| if (ret == 0 && (val == 32 || val == 16)) { |
| be->fix_bit_width = val; |
| hook_be_fixup_cb = true; |
| } |
| |
| // parse fix channels |
| snprintf(prop, sizeof(prop), PREFIX"%s-fix-channels", |
| of_dai_links_be[i].name); |
| ret = of_property_read_u32(np, prop, &val); |
| if ((ret == 0) && ((link_to_dai(link_id) < 0) || |
| mt8532_afe_channel_supported(val, link_to_dai(link_id)))) { |
| be->fix_channels = val; |
| hook_be_fixup_cb = true; |
| } |
| |
| if (hook_be_fixup_cb) |
| dai_link->be_hw_params_fixup = |
| mt8532_evb_be_hw_params_fixup; |
| |
| mt8532_evb_parse_of_codec(card->dev, np, dai_link, priv, be, |
| of_dai_links_be[i].name); |
| |
| // parse ignore pmdown time |
| snprintf(prop, sizeof(prop), PREFIX"%s-ignore-pmdown-time", |
| of_dai_links_be[i].name); |
| if (of_property_read_bool(np, prop)) |
| dai_link->ignore_pmdown_time = 1; |
| |
| // parse ignore suspend |
| snprintf(prop, sizeof(prop), PREFIX"%s-ignore-suspend", |
| of_dai_links_be[i].name); |
| if (of_property_read_bool(np, prop)) |
| dai_link->ignore_suspend = 1; |
| } |
| |
| if (priv->num_codec_configs > 0) { |
| card->num_configs = priv->num_codec_configs; |
| card->codec_conf = &priv->codec_conf[0]; |
| } |
| } |
| |
| static struct snd_soc_card mt8532_evb_card = { |
| .name = "mt8532-evb-card", |
| .owner = THIS_MODULE, |
| .dai_link = mt8532_evb_dais, |
| .num_links = ARRAY_SIZE(mt8532_evb_dais), |
| .controls = mt8532_evb_controls, |
| .num_controls = ARRAY_SIZE(mt8532_evb_controls), |
| .dapm_widgets = mt8532_evb_widgets, |
| .num_dapm_widgets = ARRAY_SIZE(mt8532_evb_widgets), |
| .dapm_routes = mt8532_evb_routes, |
| .num_dapm_routes = ARRAY_SIZE(mt8532_evb_routes), |
| }; |
| |
| static void mt8532_evb_cleanup_of_resource(struct snd_soc_card *card) |
| { |
| struct mt8532_evb_priv *priv = snd_soc_card_get_drvdata(card); |
| struct snd_soc_dai_link *dai_link; |
| int i, j; |
| |
| of_node_put(priv->afe_plat_node); |
| |
| #ifdef CONFIG_SND_SOC_MT8570 |
| of_node_put(priv->spi_plat_node); |
| #endif |
| |
| for (i = 0, dai_link = card->dai_link; |
| i < card->num_links; i++, dai_link++) { |
| if (dai_link->num_codecs > 1) { |
| struct snd_soc_dai_link_component *codec; |
| |
| for (j = 0, codec = dai_link->codecs; |
| j < dai_link->num_codecs; j++, codec++) { |
| if (!codec) |
| break; |
| of_node_put(codec->of_node); |
| } |
| } else if (dai_link->num_codecs == 1) |
| of_node_put(dai_link->codec_of_node); |
| } |
| } |
| |
| static int mt8532_evb_dev_probe(struct platform_device *pdev) |
| { |
| struct snd_soc_card *card = &mt8532_evb_card; |
| struct device *dev = &pdev->dev; |
| struct device_node *np = dev->of_node; |
| struct device_node *afe_plat_node; |
| struct mt8532_evb_priv *priv; |
| struct platform_device *afe_pdev; |
| struct mtk_base_afe *afe; |
| int ret, id; |
| size_t i; |
| size_t dais_num = ARRAY_SIZE(mt8532_evb_dais); |
| |
| afe_plat_node = of_parse_phandle(dev->of_node, "mediatek,platform", 0); |
| if (!afe_plat_node) { |
| dev_info(dev, "Property 'platform' missing or invalid\n"); |
| return -EINVAL; |
| } |
| afe_pdev = of_find_device_by_node(afe_plat_node); |
| if (!afe_pdev) { |
| dev_info(dev, "'platform' platform_device missing or invalid\n"); |
| return -EINVAL; |
| } |
| afe = platform_get_drvdata(afe_pdev); |
| if (!afe) { |
| dev_info(dev, "'platform' driver missing or invalid\n"); |
| return -EINVAL; |
| } |
| for (i = 0; i < dais_num; i++) { |
| if (mt8532_evb_dais[i].platform_name) |
| continue; |
| |
| id = mt8532_evb_dais[i].id; |
| |
| if ((id >= DAI_LINK_FE_AFE_BASE && |
| id < DAI_LINK_FE_AFE_END) || |
| (id >= DAI_LINK_BE_AFE_BASE && |
| id < DAI_LINK_BE_AFE_END)) { |
| mt8532_evb_dais[i].platform_of_node = afe_plat_node; |
| } |
| } |
| |
| card->dev = dev; |
| |
| priv = devm_kzalloc(dev, sizeof(struct mt8532_evb_priv), |
| GFP_KERNEL); |
| if (!priv) { |
| ret = -ENOMEM; |
| dev_dbg(dev, "%s allocate card private data fail %d\n", |
| __func__, ret); |
| return ret; |
| } |
| |
| priv->afe_plat_node = afe_plat_node; |
| |
| |
| snd_soc_card_set_drvdata(card, priv); |
| |
| mt8532_evb_gpio_probe(card); |
| |
| mt8532_evb_parse_of(card, np); |
| |
| mt8532_afe_enable_reg_rw_clk(afe); |
| ret = devm_snd_soc_register_card(dev, card); |
| if (ret) { |
| dev_dbg(dev, "%s snd_soc_register_card fail %d\n", |
| __func__, ret); |
| mt8532_afe_disable_reg_rw_clk(afe); |
| of_node_put(priv->afe_plat_node); |
| return ret; |
| } |
| mt8532_afe_disable_reg_rw_clk(afe); |
| |
| #ifdef CONFIG_SND_SOC_GAPP_AUDIO_CONTROL |
| soc_ctlx_init(&priv->ctlx_res, card); |
| #endif |
| |
| dev_dbg(dev, "%s %d leave\n", __func__, __LINE__); |
| |
| return ret; |
| } |
| |
| static int mt8532_evb_dev_remove(struct platform_device *pdev) |
| { |
| struct snd_soc_card *card = platform_get_drvdata(pdev); |
| |
| mt8532_evb_cleanup_of_resource(card); |
| |
| return 0; |
| } |
| |
| #ifdef CONFIG_SND_SOC_MT8696_EVB |
| static const struct of_device_id mt8532_evb_dt_match[] = { |
| { .compatible = "mediatek,mt8696-card1", }, |
| { } |
| }; |
| MODULE_DEVICE_TABLE(of, mt8532_evb_dt_match); |
| #else |
| static const struct of_device_id mt8532_evb_dt_match[] = { |
| { .compatible = "mediatek,mt8532-evb", }, |
| { } |
| }; |
| MODULE_DEVICE_TABLE(of, mt8532_evb_dt_match); |
| #endif |
| |
| static struct platform_driver mt8532_evb_driver = { |
| .driver = { |
| .name = "mt8532-evb", |
| .of_match_table = mt8532_evb_dt_match, |
| #ifdef CONFIG_PM |
| .pm = &snd_soc_pm_ops, |
| #endif |
| }, |
| .probe = mt8532_evb_dev_probe, |
| .remove = mt8532_evb_dev_remove, |
| }; |
| |
| module_platform_driver(mt8532_evb_driver); |
| |
| /* Module information */ |
| MODULE_DESCRIPTION("MT8532 EVB SoC machine driver"); |
| MODULE_AUTHOR("Wen Cai <wen.cai@mediatek.com>"); |
| MODULE_LICENSE("GPL v2"); |
| MODULE_ALIAS("platform:mt8532-evb"); |
| |