Path: blob/master/sound/soc/mediatek/mt8173/mt8173-rt5650.c
26488 views
// SPDX-License-Identifier: GPL-2.01/*2* mt8173-rt5650.c -- MT8173 machine driver with RT5650 codecs3*4* Copyright (c) 2016 MediaTek Inc.5* Author: Koro Chen <[email protected]>6*/78#include <linux/module.h>9#include <sound/soc.h>10#include <sound/jack.h>11#include "../../codecs/rt5645.h"1213#define MCLK_FOR_CODECS 122880001415enum mt8173_rt5650_mclk {16MT8173_RT5650_MCLK_EXTERNAL = 0,17MT8173_RT5650_MCLK_INTERNAL,18};1920struct mt8173_rt5650_platform_data {21enum mt8173_rt5650_mclk pll_from;22/* 0 = external oscillator; 1 = internal source from mt8173 */23};2425static struct mt8173_rt5650_platform_data mt8173_rt5650_priv = {26.pll_from = MT8173_RT5650_MCLK_EXTERNAL,27};2829static const struct snd_soc_dapm_widget mt8173_rt5650_widgets[] = {30SND_SOC_DAPM_SPK("Ext Spk", NULL),31SND_SOC_DAPM_MIC("Int Mic", NULL),32SND_SOC_DAPM_HP("Headphone", NULL),33SND_SOC_DAPM_MIC("Headset Mic", NULL),34};3536static const struct snd_soc_dapm_route mt8173_rt5650_routes[] = {37{"Ext Spk", NULL, "SPOL"},38{"Ext Spk", NULL, "SPOR"},39{"DMIC L1", NULL, "Int Mic"},40{"DMIC R1", NULL, "Int Mic"},41{"Headphone", NULL, "HPOL"},42{"Headphone", NULL, "HPOR"},43{"IN1P", NULL, "Headset Mic"},44{"IN1N", NULL, "Headset Mic"},45};4647static const struct snd_kcontrol_new mt8173_rt5650_controls[] = {48SOC_DAPM_PIN_SWITCH("Ext Spk"),49SOC_DAPM_PIN_SWITCH("Int Mic"),50SOC_DAPM_PIN_SWITCH("Headphone"),51SOC_DAPM_PIN_SWITCH("Headset Mic"),52};5354static struct snd_soc_jack_pin mt8173_rt5650_jack_pins[] = {55{56.pin = "Headphone",57.mask = SND_JACK_HEADPHONE,58},59{60.pin = "Headset Mic",61.mask = SND_JACK_MICROPHONE,62},63};6465static int mt8173_rt5650_hw_params(struct snd_pcm_substream *substream,66struct snd_pcm_hw_params *params)67{68struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);69unsigned int mclk_clock;70struct snd_soc_dai *codec_dai;71int i, ret;7273switch (mt8173_rt5650_priv.pll_from) {74case MT8173_RT5650_MCLK_EXTERNAL:75/* mclk = 12.288M */76mclk_clock = MCLK_FOR_CODECS;77break;78case MT8173_RT5650_MCLK_INTERNAL:79/* mclk = sampling rate*256 */80mclk_clock = params_rate(params) * 256;81break;82default:83/* mclk = 12.288M */84mclk_clock = MCLK_FOR_CODECS;85break;86}8788for_each_rtd_codec_dais(rtd, i, codec_dai) {89/* pll from mclk */90ret = snd_soc_dai_set_pll(codec_dai, 0, 0, mclk_clock,91params_rate(params) * 512);92if (ret)93return ret;9495/* sysclk from pll */96ret = snd_soc_dai_set_sysclk(codec_dai, 1,97params_rate(params) * 512,98SND_SOC_CLOCK_IN);99if (ret)100return ret;101}102return 0;103}104105static const struct snd_soc_ops mt8173_rt5650_ops = {106.hw_params = mt8173_rt5650_hw_params,107};108109static struct snd_soc_jack mt8173_rt5650_jack, mt8173_rt5650_hdmi_jack;110111static int mt8173_rt5650_init(struct snd_soc_pcm_runtime *runtime)112{113struct snd_soc_card *card = runtime->card;114struct snd_soc_component *component = snd_soc_rtd_to_codec(runtime, 0)->component;115const char *codec_capture_dai = snd_soc_rtd_to_codec(runtime, 1)->name;116int ret;117118rt5645_sel_asrc_clk_src(component,119RT5645_DA_STEREO_FILTER,120RT5645_CLK_SEL_I2S1_ASRC);121122if (!strcmp(codec_capture_dai, "rt5645-aif1")) {123rt5645_sel_asrc_clk_src(component,124RT5645_AD_STEREO_FILTER,125RT5645_CLK_SEL_I2S1_ASRC);126} else if (!strcmp(codec_capture_dai, "rt5645-aif2")) {127rt5645_sel_asrc_clk_src(component,128RT5645_AD_STEREO_FILTER,129RT5645_CLK_SEL_I2S2_ASRC);130} else {131dev_warn(card->dev,132"Only one dai codec found in DTS, enabled rt5645 AD filter\n");133rt5645_sel_asrc_clk_src(component,134RT5645_AD_STEREO_FILTER,135RT5645_CLK_SEL_I2S1_ASRC);136}137138/* enable jack detection */139ret = snd_soc_card_jack_new_pins(card, "Headset Jack",140SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |141SND_JACK_BTN_0 | SND_JACK_BTN_1 |142SND_JACK_BTN_2 | SND_JACK_BTN_3,143&mt8173_rt5650_jack,144mt8173_rt5650_jack_pins,145ARRAY_SIZE(mt8173_rt5650_jack_pins));146if (ret) {147dev_err(card->dev, "Can't new Headset Jack %d\n", ret);148return ret;149}150151return rt5645_set_jack_detect(component,152&mt8173_rt5650_jack,153&mt8173_rt5650_jack,154&mt8173_rt5650_jack);155}156157static int mt8173_rt5650_hdmi_init(struct snd_soc_pcm_runtime *rtd)158{159int ret;160161ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT,162&mt8173_rt5650_hdmi_jack);163if (ret)164return ret;165166return snd_soc_component_set_jack(snd_soc_rtd_to_codec(rtd, 0)->component,167&mt8173_rt5650_hdmi_jack, NULL);168}169170enum {171DAI_LINK_PLAYBACK,172DAI_LINK_CAPTURE,173DAI_LINK_HDMI,174DAI_LINK_CODEC_I2S,175DAI_LINK_HDMI_I2S,176};177178SND_SOC_DAILINK_DEFS(playback,179DAILINK_COMP_ARRAY(COMP_CPU("DL1")),180DAILINK_COMP_ARRAY(COMP_DUMMY()),181DAILINK_COMP_ARRAY(COMP_EMPTY()));182183SND_SOC_DAILINK_DEFS(capture,184DAILINK_COMP_ARRAY(COMP_CPU("VUL")),185DAILINK_COMP_ARRAY(COMP_DUMMY()),186DAILINK_COMP_ARRAY(COMP_EMPTY()));187188SND_SOC_DAILINK_DEFS(hdmi_pcm,189DAILINK_COMP_ARRAY(COMP_CPU("HDMI")),190DAILINK_COMP_ARRAY(COMP_DUMMY()),191DAILINK_COMP_ARRAY(COMP_EMPTY()));192193SND_SOC_DAILINK_DEFS(codec,194DAILINK_COMP_ARRAY(COMP_CPU("I2S")),195DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "rt5645-aif1"), /* Playback */196COMP_CODEC(NULL, "rt5645-aif1")),/* Capture */197DAILINK_COMP_ARRAY(COMP_EMPTY()));198199SND_SOC_DAILINK_DEFS(hdmi_be,200DAILINK_COMP_ARRAY(COMP_CPU("HDMIO")),201DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "i2s-hifi")),202DAILINK_COMP_ARRAY(COMP_EMPTY()));203204/* Digital audio interface glue - connects codec <---> CPU */205static struct snd_soc_dai_link mt8173_rt5650_dais[] = {206/* Front End DAI links */207[DAI_LINK_PLAYBACK] = {208.name = "rt5650 Playback",209.stream_name = "rt5650 Playback",210.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},211.dynamic = 1,212.playback_only = 1,213SND_SOC_DAILINK_REG(playback),214},215[DAI_LINK_CAPTURE] = {216.name = "rt5650 Capture",217.stream_name = "rt5650 Capture",218.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},219.dynamic = 1,220.capture_only = 1,221SND_SOC_DAILINK_REG(capture),222},223[DAI_LINK_HDMI] = {224.name = "HDMI",225.stream_name = "HDMI PCM",226.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},227.dynamic = 1,228.playback_only = 1,229SND_SOC_DAILINK_REG(hdmi_pcm),230},231/* Back End DAI links */232[DAI_LINK_CODEC_I2S] = {233.name = "Codec",234.no_pcm = 1,235.init = mt8173_rt5650_init,236.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |237SND_SOC_DAIFMT_CBC_CFC,238.ops = &mt8173_rt5650_ops,239.ignore_pmdown_time = 1,240SND_SOC_DAILINK_REG(codec),241},242[DAI_LINK_HDMI_I2S] = {243.name = "HDMI BE",244.no_pcm = 1,245.playback_only = 1,246.init = mt8173_rt5650_hdmi_init,247SND_SOC_DAILINK_REG(hdmi_be),248},249};250251static struct snd_soc_card mt8173_rt5650_card = {252.name = "mtk-rt5650",253.owner = THIS_MODULE,254.dai_link = mt8173_rt5650_dais,255.num_links = ARRAY_SIZE(mt8173_rt5650_dais),256.controls = mt8173_rt5650_controls,257.num_controls = ARRAY_SIZE(mt8173_rt5650_controls),258.dapm_widgets = mt8173_rt5650_widgets,259.num_dapm_widgets = ARRAY_SIZE(mt8173_rt5650_widgets),260.dapm_routes = mt8173_rt5650_routes,261.num_dapm_routes = ARRAY_SIZE(mt8173_rt5650_routes),262};263264static int mt8173_rt5650_dev_probe(struct platform_device *pdev)265{266struct snd_soc_card *card = &mt8173_rt5650_card;267struct device_node *platform_node;268struct device_node *np;269const char *codec_capture_dai;270struct snd_soc_dai_link *dai_link;271int i, ret;272273platform_node = of_parse_phandle(pdev->dev.of_node,274"mediatek,platform", 0);275if (!platform_node) {276dev_err(&pdev->dev, "Property 'platform' missing or invalid\n");277return -EINVAL;278}279280for_each_card_prelinks(card, i, dai_link) {281if (dai_link->platforms->name)282continue;283dai_link->platforms->of_node = platform_node;284}285286mt8173_rt5650_dais[DAI_LINK_CODEC_I2S].codecs[0].of_node =287of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 0);288if (!mt8173_rt5650_dais[DAI_LINK_CODEC_I2S].codecs[0].of_node) {289dev_err(&pdev->dev,290"Property 'audio-codec' missing or invalid\n");291ret = -EINVAL;292goto put_platform_node;293}294mt8173_rt5650_dais[DAI_LINK_CODEC_I2S].codecs[1].of_node =295mt8173_rt5650_dais[DAI_LINK_CODEC_I2S].codecs[0].of_node;296297np = of_get_child_by_name(pdev->dev.of_node, "codec-capture");298if (np) {299ret = snd_soc_of_get_dai_name(np, &codec_capture_dai, 0);300of_node_put(np);301if (ret < 0) {302dev_err(&pdev->dev,303"%s codec_capture_dai name fail %d\n",304__func__, ret);305goto put_platform_node;306}307mt8173_rt5650_dais[DAI_LINK_CODEC_I2S].codecs[1].dai_name =308codec_capture_dai;309}310311if (device_property_present(&pdev->dev, "mediatek,mclk")) {312ret = device_property_read_u32(&pdev->dev,313"mediatek,mclk",314&mt8173_rt5650_priv.pll_from);315if (ret) {316dev_err(&pdev->dev,317"%s snd_soc_register_card fail %d\n",318__func__, ret);319}320}321322mt8173_rt5650_dais[DAI_LINK_HDMI_I2S].codecs->of_node =323of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 1);324if (!mt8173_rt5650_dais[DAI_LINK_HDMI_I2S].codecs->of_node) {325dev_err(&pdev->dev,326"Property 'audio-codec' missing or invalid\n");327ret = -EINVAL;328goto put_platform_node;329}330card->dev = &pdev->dev;331332ret = devm_snd_soc_register_card(&pdev->dev, card);333334put_platform_node:335of_node_put(platform_node);336return ret;337}338339static const struct of_device_id mt8173_rt5650_dt_match[] = {340{ .compatible = "mediatek,mt8173-rt5650", },341{ }342};343MODULE_DEVICE_TABLE(of, mt8173_rt5650_dt_match);344345static struct platform_driver mt8173_rt5650_driver = {346.driver = {347.name = "mtk-rt5650",348.of_match_table = mt8173_rt5650_dt_match,349.pm = &snd_soc_pm_ops,350},351.probe = mt8173_rt5650_dev_probe,352};353354module_platform_driver(mt8173_rt5650_driver);355356/* Module information */357MODULE_DESCRIPTION("MT8173 RT5650 SoC machine driver");358MODULE_AUTHOR("Koro Chen <[email protected]>");359MODULE_LICENSE("GPL v2");360MODULE_ALIAS("platform:mtk-rt5650");361362363364