Path: blob/master/sound/soc/mediatek/mt2701/mt2701-cs42448.c
26488 views
// SPDX-License-Identifier: GPL-2.01/*2* mt2701-cs42448.c -- MT2701 CS42448 ALSA SoC machine driver3*4* Copyright (c) 2016 MediaTek Inc.5* Author: Ir Lian <[email protected]>6* Garlic Tseng <[email protected]>7*/89#include <linux/module.h>10#include <sound/soc.h>11#include <linux/delay.h>12#include <linux/gpio/consumer.h>13#include <linux/pinctrl/consumer.h>1415#include "mt2701-afe-common.h"1617struct mt2701_cs42448_private {18int i2s1_in_mux;19struct gpio_desc *i2s1_in_mux_sel_1;20struct gpio_desc *i2s1_in_mux_sel_2;21};2223static const char * const i2sin_mux_switch_text[] = {24"ADC_SDOUT2",25"ADC_SDOUT3",26"I2S_IN_1",27"I2S_IN_2",28};2930static const struct soc_enum i2sin_mux_enum =31SOC_ENUM_SINGLE_EXT(4, i2sin_mux_switch_text);3233static int mt2701_cs42448_i2sin1_mux_get(struct snd_kcontrol *kcontrol,34struct snd_ctl_elem_value *ucontrol)35{36struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);37struct mt2701_cs42448_private *priv = snd_soc_card_get_drvdata(card);3839ucontrol->value.integer.value[0] = priv->i2s1_in_mux;40return 0;41}4243static int mt2701_cs42448_i2sin1_mux_set(struct snd_kcontrol *kcontrol,44struct snd_ctl_elem_value *ucontrol)45{46struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);47struct mt2701_cs42448_private *priv = snd_soc_card_get_drvdata(card);4849if (ucontrol->value.integer.value[0] == priv->i2s1_in_mux)50return 0;5152switch (ucontrol->value.integer.value[0]) {53case 0:54gpiod_set_value(priv->i2s1_in_mux_sel_1, 0);55gpiod_set_value(priv->i2s1_in_mux_sel_2, 0);56break;57case 1:58gpiod_set_value(priv->i2s1_in_mux_sel_1, 1);59gpiod_set_value(priv->i2s1_in_mux_sel_2, 0);60break;61case 2:62gpiod_set_value(priv->i2s1_in_mux_sel_1, 0);63gpiod_set_value(priv->i2s1_in_mux_sel_2, 1);64break;65case 3:66gpiod_set_value(priv->i2s1_in_mux_sel_1, 1);67gpiod_set_value(priv->i2s1_in_mux_sel_2, 1);68break;69default:70dev_warn(card->dev, "%s invalid setting\n", __func__);71}7273priv->i2s1_in_mux = ucontrol->value.integer.value[0];74return 0;75}7677static const struct snd_soc_dapm_widget78mt2701_cs42448_asoc_card_dapm_widgets[] = {79SND_SOC_DAPM_LINE("Line Out Jack", NULL),80SND_SOC_DAPM_MIC("AMIC", NULL),81SND_SOC_DAPM_LINE("Tuner In", NULL),82SND_SOC_DAPM_LINE("Satellite Tuner In", NULL),83SND_SOC_DAPM_LINE("AUX In", NULL),84};8586static const struct snd_kcontrol_new mt2701_cs42448_controls[] = {87SOC_DAPM_PIN_SWITCH("Line Out Jack"),88SOC_DAPM_PIN_SWITCH("AMIC"),89SOC_DAPM_PIN_SWITCH("Tuner In"),90SOC_DAPM_PIN_SWITCH("Satellite Tuner In"),91SOC_DAPM_PIN_SWITCH("AUX In"),92SOC_ENUM_EXT("I2SIN1_MUX_Switch", i2sin_mux_enum,93mt2701_cs42448_i2sin1_mux_get,94mt2701_cs42448_i2sin1_mux_set),95};9697static const unsigned int mt2701_cs42448_sampling_rates[] = {48000};9899static const struct snd_pcm_hw_constraint_list mt2701_cs42448_constraints_rates = {100.count = ARRAY_SIZE(mt2701_cs42448_sampling_rates),101.list = mt2701_cs42448_sampling_rates,102.mask = 0,103};104105static int mt2701_cs42448_fe_ops_startup(struct snd_pcm_substream *substream)106{107int err;108109err = snd_pcm_hw_constraint_list(substream->runtime, 0,110SNDRV_PCM_HW_PARAM_RATE,111&mt2701_cs42448_constraints_rates);112if (err < 0) {113dev_err(substream->pcm->card->dev,114"%s snd_pcm_hw_constraint_list failed: 0x%x\n",115__func__, err);116return err;117}118return 0;119}120121static const struct snd_soc_ops mt2701_cs42448_48k_fe_ops = {122.startup = mt2701_cs42448_fe_ops_startup,123};124125static int mt2701_cs42448_be_ops_hw_params(struct snd_pcm_substream *substream,126struct snd_pcm_hw_params *params)127{128struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);129struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);130struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);131unsigned int mclk_rate;132unsigned int rate = params_rate(params);133unsigned int div_mclk_over_bck = rate > 192000 ? 2 : 4;134unsigned int div_bck_over_lrck = 64;135136mclk_rate = rate * div_bck_over_lrck * div_mclk_over_bck;137138/* mt2701 mclk */139snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_rate, SND_SOC_CLOCK_OUT);140141/* codec mclk */142snd_soc_dai_set_sysclk(codec_dai, 0, mclk_rate, SND_SOC_CLOCK_IN);143144return 0;145}146147static const struct snd_soc_ops mt2701_cs42448_be_ops = {148.hw_params = mt2701_cs42448_be_ops_hw_params149};150151enum {152DAI_LINK_FE_MULTI_CH_OUT,153DAI_LINK_FE_PCM0_IN,154DAI_LINK_FE_PCM1_IN,155DAI_LINK_FE_BT_OUT,156DAI_LINK_FE_BT_IN,157DAI_LINK_BE_I2S0,158DAI_LINK_BE_I2S1,159DAI_LINK_BE_I2S2,160DAI_LINK_BE_I2S3,161DAI_LINK_BE_MRG_BT,162};163164SND_SOC_DAILINK_DEFS(fe_multi_ch_out,165DAILINK_COMP_ARRAY(COMP_CPU("PCM_multi")),166DAILINK_COMP_ARRAY(COMP_DUMMY()),167DAILINK_COMP_ARRAY(COMP_EMPTY()));168169SND_SOC_DAILINK_DEFS(fe_pcm0_in,170DAILINK_COMP_ARRAY(COMP_CPU("PCM0")),171DAILINK_COMP_ARRAY(COMP_DUMMY()),172DAILINK_COMP_ARRAY(COMP_EMPTY()));173174SND_SOC_DAILINK_DEFS(fe_pcm1_in,175DAILINK_COMP_ARRAY(COMP_CPU("PCM1")),176DAILINK_COMP_ARRAY(COMP_DUMMY()),177DAILINK_COMP_ARRAY(COMP_EMPTY()));178179SND_SOC_DAILINK_DEFS(fe_bt_out,180DAILINK_COMP_ARRAY(COMP_CPU("PCM_BT_DL")),181DAILINK_COMP_ARRAY(COMP_DUMMY()),182DAILINK_COMP_ARRAY(COMP_EMPTY()));183184SND_SOC_DAILINK_DEFS(fe_bt_in,185DAILINK_COMP_ARRAY(COMP_CPU("PCM_BT_UL")),186DAILINK_COMP_ARRAY(COMP_DUMMY()),187DAILINK_COMP_ARRAY(COMP_EMPTY()));188189SND_SOC_DAILINK_DEFS(be_i2s0,190DAILINK_COMP_ARRAY(COMP_CPU("I2S0")),191DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "cs42448")),192DAILINK_COMP_ARRAY(COMP_EMPTY()));193194SND_SOC_DAILINK_DEFS(be_i2s1,195DAILINK_COMP_ARRAY(COMP_CPU("I2S1")),196DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "cs42448")),197DAILINK_COMP_ARRAY(COMP_EMPTY()));198199SND_SOC_DAILINK_DEFS(be_i2s2,200DAILINK_COMP_ARRAY(COMP_CPU("I2S2")),201DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "cs42448")),202DAILINK_COMP_ARRAY(COMP_EMPTY()));203204SND_SOC_DAILINK_DEFS(be_i2s3,205DAILINK_COMP_ARRAY(COMP_CPU("I2S3")),206DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "cs42448")),207DAILINK_COMP_ARRAY(COMP_EMPTY()));208209SND_SOC_DAILINK_DEFS(be_mrg_bt,210DAILINK_COMP_ARRAY(COMP_CPU("MRG BT")),211DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "bt-sco-pcm-wb")),212DAILINK_COMP_ARRAY(COMP_EMPTY()));213214static struct snd_soc_dai_link mt2701_cs42448_dai_links[] = {215/* FE */216[DAI_LINK_FE_MULTI_CH_OUT] = {217.name = "mt2701-cs42448-multi-ch-out",218.stream_name = "mt2701-cs42448-multi-ch-out",219.trigger = {SND_SOC_DPCM_TRIGGER_POST,220SND_SOC_DPCM_TRIGGER_POST},221.ops = &mt2701_cs42448_48k_fe_ops,222.dynamic = 1,223.playback_only = 1,224SND_SOC_DAILINK_REG(fe_multi_ch_out),225},226[DAI_LINK_FE_PCM0_IN] = {227.name = "mt2701-cs42448-pcm0",228.stream_name = "mt2701-cs42448-pcm0-data-UL",229.trigger = {SND_SOC_DPCM_TRIGGER_POST,230SND_SOC_DPCM_TRIGGER_POST},231.ops = &mt2701_cs42448_48k_fe_ops,232.dynamic = 1,233.capture_only = 1,234SND_SOC_DAILINK_REG(fe_pcm0_in),235},236[DAI_LINK_FE_PCM1_IN] = {237.name = "mt2701-cs42448-pcm1-data-UL",238.stream_name = "mt2701-cs42448-pcm1-data-UL",239.trigger = {SND_SOC_DPCM_TRIGGER_POST,240SND_SOC_DPCM_TRIGGER_POST},241.ops = &mt2701_cs42448_48k_fe_ops,242.dynamic = 1,243.capture_only = 1,244SND_SOC_DAILINK_REG(fe_pcm1_in),245},246[DAI_LINK_FE_BT_OUT] = {247.name = "mt2701-cs42448-pcm-BT-out",248.stream_name = "mt2701-cs42448-pcm-BT",249.trigger = {SND_SOC_DPCM_TRIGGER_POST,250SND_SOC_DPCM_TRIGGER_POST},251.dynamic = 1,252.playback_only = 1,253SND_SOC_DAILINK_REG(fe_bt_out),254},255[DAI_LINK_FE_BT_IN] = {256.name = "mt2701-cs42448-pcm-BT-in",257.stream_name = "mt2701-cs42448-pcm-BT",258.trigger = {SND_SOC_DPCM_TRIGGER_POST,259SND_SOC_DPCM_TRIGGER_POST},260.dynamic = 1,261.capture_only = 1,262SND_SOC_DAILINK_REG(fe_bt_in),263},264/* BE */265[DAI_LINK_BE_I2S0] = {266.name = "mt2701-cs42448-I2S0",267.no_pcm = 1,268.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC269| SND_SOC_DAIFMT_GATED,270.ops = &mt2701_cs42448_be_ops,271SND_SOC_DAILINK_REG(be_i2s0),272},273[DAI_LINK_BE_I2S1] = {274.name = "mt2701-cs42448-I2S1",275.no_pcm = 1,276.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC277| SND_SOC_DAIFMT_GATED,278.ops = &mt2701_cs42448_be_ops,279SND_SOC_DAILINK_REG(be_i2s1),280},281[DAI_LINK_BE_I2S2] = {282.name = "mt2701-cs42448-I2S2",283.no_pcm = 1,284.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC285| SND_SOC_DAIFMT_GATED,286.ops = &mt2701_cs42448_be_ops,287SND_SOC_DAILINK_REG(be_i2s2),288},289[DAI_LINK_BE_I2S3] = {290.name = "mt2701-cs42448-I2S3",291.no_pcm = 1,292.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC293| SND_SOC_DAIFMT_GATED,294.ops = &mt2701_cs42448_be_ops,295SND_SOC_DAILINK_REG(be_i2s3),296},297[DAI_LINK_BE_MRG_BT] = {298.name = "mt2701-cs42448-MRG-BT",299.no_pcm = 1,300SND_SOC_DAILINK_REG(be_mrg_bt),301},302};303304static struct snd_soc_card mt2701_cs42448_soc_card = {305.name = "mt2701-cs42448",306.owner = THIS_MODULE,307.dai_link = mt2701_cs42448_dai_links,308.num_links = ARRAY_SIZE(mt2701_cs42448_dai_links),309.controls = mt2701_cs42448_controls,310.num_controls = ARRAY_SIZE(mt2701_cs42448_controls),311.dapm_widgets = mt2701_cs42448_asoc_card_dapm_widgets,312.num_dapm_widgets = ARRAY_SIZE(mt2701_cs42448_asoc_card_dapm_widgets),313};314315static int mt2701_cs42448_machine_probe(struct platform_device *pdev)316{317struct snd_soc_card *card = &mt2701_cs42448_soc_card;318int ret;319int i;320struct device_node *platform_node, *codec_node, *codec_node_bt_mrg;321struct device *dev = &pdev->dev;322struct mt2701_cs42448_private *priv =323devm_kzalloc(dev, sizeof(struct mt2701_cs42448_private),324GFP_KERNEL);325struct snd_soc_dai_link *dai_link;326327if (!priv)328return -ENOMEM;329330platform_node = of_parse_phandle(pdev->dev.of_node,331"mediatek,platform", 0);332if (!platform_node) {333dev_err(dev, "Property 'platform' missing or invalid\n");334return -EINVAL;335}336for_each_card_prelinks(card, i, dai_link) {337if (dai_link->platforms->name)338continue;339dai_link->platforms->of_node = platform_node;340}341342card->dev = dev;343344codec_node = of_parse_phandle(pdev->dev.of_node,345"mediatek,audio-codec", 0);346if (!codec_node) {347dev_err(dev,348"Property 'audio-codec' missing or invalid\n");349return -EINVAL;350}351for_each_card_prelinks(card, i, dai_link) {352if (dai_link->codecs->name)353continue;354dai_link->codecs->of_node = codec_node;355}356357codec_node_bt_mrg = of_parse_phandle(pdev->dev.of_node,358"mediatek,audio-codec-bt-mrg", 0);359if (!codec_node_bt_mrg) {360dev_err(dev,361"Property 'audio-codec-bt-mrg' missing or invalid\n");362return -EINVAL;363}364mt2701_cs42448_dai_links[DAI_LINK_BE_MRG_BT].codecs->of_node365= codec_node_bt_mrg;366367ret = snd_soc_of_parse_audio_routing(card, "audio-routing");368if (ret) {369dev_err(dev, "failed to parse audio-routing: %d\n", ret);370return ret;371}372373priv->i2s1_in_mux_sel_1 = devm_gpiod_get_optional(dev, "i2s1-in-sel-gpio1",374GPIOD_OUT_LOW);375if (IS_ERR(priv->i2s1_in_mux_sel_1))376return dev_err_probe(dev, PTR_ERR(priv->i2s1_in_mux_sel_1),377"error getting mux 1 selector\n");378379priv->i2s1_in_mux_sel_2 = devm_gpiod_get_optional(dev, "i2s1-in-sel-gpio2",380GPIOD_OUT_LOW);381if (IS_ERR(priv->i2s1_in_mux_sel_2))382return dev_err_probe(dev, PTR_ERR(priv->i2s1_in_mux_sel_2),383"error getting mux 2 selector\n");384385snd_soc_card_set_drvdata(card, priv);386387ret = devm_snd_soc_register_card(dev, card);388389if (ret)390dev_err(dev, "%s snd_soc_register_card fail %d\n",391__func__, ret);392return ret;393}394395#ifdef CONFIG_OF396static const struct of_device_id mt2701_cs42448_machine_dt_match[] = {397{.compatible = "mediatek,mt2701-cs42448-machine",},398{}399};400MODULE_DEVICE_TABLE(of, mt2701_cs42448_machine_dt_match);401#endif402403static struct platform_driver mt2701_cs42448_machine = {404.driver = {405.name = "mt2701-cs42448",406#ifdef CONFIG_OF407.of_match_table = mt2701_cs42448_machine_dt_match,408#endif409},410.probe = mt2701_cs42448_machine_probe,411};412413module_platform_driver(mt2701_cs42448_machine);414415/* Module information */416MODULE_DESCRIPTION("MT2701 CS42448 ALSA SoC machine driver");417MODULE_AUTHOR("Ir Lian <[email protected]>");418MODULE_LICENSE("GPL v2");419MODULE_ALIAS("mt2701 cs42448 soc card");420421422