brintos

brintos / linux-shallow public Read only

0
0
Text · 9.7 KiB · 466f176 Raw
365 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * mt8173-rt5650.c  --  MT8173 machine driver with RT5650 codecs4 *5 * Copyright (c) 2016 MediaTek Inc.6 * Author: Koro Chen <koro.chen@mediatek.com>7 */8 9#include <linux/module.h>10#include <sound/soc.h>11#include <sound/jack.h>12#include "../../codecs/rt5645.h"13 14#define MCLK_FOR_CODECS		1228800015 16enum mt8173_rt5650_mclk {17	MT8173_RT5650_MCLK_EXTERNAL = 0,18	MT8173_RT5650_MCLK_INTERNAL,19};20 21struct mt8173_rt5650_platform_data {22	enum mt8173_rt5650_mclk pll_from;23	/* 0 = external oscillator; 1 = internal source from mt8173 */24};25 26static struct mt8173_rt5650_platform_data mt8173_rt5650_priv = {27	.pll_from = MT8173_RT5650_MCLK_EXTERNAL,28};29 30static const struct snd_soc_dapm_widget mt8173_rt5650_widgets[] = {31	SND_SOC_DAPM_SPK("Ext Spk", NULL),32	SND_SOC_DAPM_MIC("Int Mic", NULL),33	SND_SOC_DAPM_HP("Headphone", NULL),34	SND_SOC_DAPM_MIC("Headset Mic", NULL),35};36 37static const struct snd_soc_dapm_route mt8173_rt5650_routes[] = {38	{"Ext Spk", NULL, "SPOL"},39	{"Ext Spk", NULL, "SPOR"},40	{"DMIC L1", NULL, "Int Mic"},41	{"DMIC R1", NULL, "Int Mic"},42	{"Headphone", NULL, "HPOL"},43	{"Headphone", NULL, "HPOR"},44	{"IN1P", NULL, "Headset Mic"},45	{"IN1N", NULL, "Headset Mic"},46};47 48static const struct snd_kcontrol_new mt8173_rt5650_controls[] = {49	SOC_DAPM_PIN_SWITCH("Ext Spk"),50	SOC_DAPM_PIN_SWITCH("Int Mic"),51	SOC_DAPM_PIN_SWITCH("Headphone"),52	SOC_DAPM_PIN_SWITCH("Headset Mic"),53};54 55static struct snd_soc_jack_pin mt8173_rt5650_jack_pins[] = {56	{57		.pin	= "Headphone",58		.mask	= SND_JACK_HEADPHONE,59	},60	{61		.pin	= "Headset Mic",62		.mask	= SND_JACK_MICROPHONE,63	},64};65 66static int mt8173_rt5650_hw_params(struct snd_pcm_substream *substream,67				   struct snd_pcm_hw_params *params)68{69	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);70	unsigned int mclk_clock;71	struct snd_soc_dai *codec_dai;72	int i, ret;73 74	switch (mt8173_rt5650_priv.pll_from) {75	case MT8173_RT5650_MCLK_EXTERNAL:76		/* mclk = 12.288M */77		mclk_clock = MCLK_FOR_CODECS;78		break;79	case MT8173_RT5650_MCLK_INTERNAL:80		/* mclk = sampling rate*256 */81		mclk_clock = params_rate(params) * 256;82		break;83	default:84		/* mclk = 12.288M */85		mclk_clock = MCLK_FOR_CODECS;86		break;87	}88 89	for_each_rtd_codec_dais(rtd, i, codec_dai) {90		/* pll from mclk */91		ret = snd_soc_dai_set_pll(codec_dai, 0, 0, mclk_clock,92					  params_rate(params) * 512);93		if (ret)94			return ret;95 96		/* sysclk from pll */97		ret = snd_soc_dai_set_sysclk(codec_dai, 1,98					     params_rate(params) * 512,99					     SND_SOC_CLOCK_IN);100		if (ret)101			return ret;102	}103	return 0;104}105 106static const struct snd_soc_ops mt8173_rt5650_ops = {107	.hw_params = mt8173_rt5650_hw_params,108};109 110static struct snd_soc_jack mt8173_rt5650_jack, mt8173_rt5650_hdmi_jack;111 112static int mt8173_rt5650_init(struct snd_soc_pcm_runtime *runtime)113{114	struct snd_soc_card *card = runtime->card;115	struct snd_soc_component *component = snd_soc_rtd_to_codec(runtime, 0)->component;116	const char *codec_capture_dai = snd_soc_rtd_to_codec(runtime, 1)->name;117	int ret;118 119	rt5645_sel_asrc_clk_src(component,120				RT5645_DA_STEREO_FILTER,121				RT5645_CLK_SEL_I2S1_ASRC);122 123	if (!strcmp(codec_capture_dai, "rt5645-aif1")) {124		rt5645_sel_asrc_clk_src(component,125					RT5645_AD_STEREO_FILTER,126					RT5645_CLK_SEL_I2S1_ASRC);127	} else if (!strcmp(codec_capture_dai, "rt5645-aif2")) {128		rt5645_sel_asrc_clk_src(component,129					RT5645_AD_STEREO_FILTER,130					RT5645_CLK_SEL_I2S2_ASRC);131	} else {132		dev_warn(card->dev,133			 "Only one dai codec found in DTS, enabled rt5645 AD filter\n");134		rt5645_sel_asrc_clk_src(component,135					RT5645_AD_STEREO_FILTER,136					RT5645_CLK_SEL_I2S1_ASRC);137	}138 139	/* enable jack detection */140	ret = snd_soc_card_jack_new_pins(card, "Headset Jack",141					 SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |142					 SND_JACK_BTN_0 | SND_JACK_BTN_1 |143					 SND_JACK_BTN_2 | SND_JACK_BTN_3,144					 &mt8173_rt5650_jack,145					 mt8173_rt5650_jack_pins,146					 ARRAY_SIZE(mt8173_rt5650_jack_pins));147	if (ret) {148		dev_err(card->dev, "Can't new Headset Jack %d\n", ret);149		return ret;150	}151 152	return rt5645_set_jack_detect(component,153				      &mt8173_rt5650_jack,154				      &mt8173_rt5650_jack,155				      &mt8173_rt5650_jack);156}157 158static int mt8173_rt5650_hdmi_init(struct snd_soc_pcm_runtime *rtd)159{160	int ret;161 162	ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT,163				    &mt8173_rt5650_hdmi_jack);164	if (ret)165		return ret;166 167	return snd_soc_component_set_jack(snd_soc_rtd_to_codec(rtd, 0)->component,168					  &mt8173_rt5650_hdmi_jack, NULL);169}170 171enum {172	DAI_LINK_PLAYBACK,173	DAI_LINK_CAPTURE,174	DAI_LINK_HDMI,175	DAI_LINK_CODEC_I2S,176	DAI_LINK_HDMI_I2S,177};178 179SND_SOC_DAILINK_DEFS(playback,180	DAILINK_COMP_ARRAY(COMP_CPU("DL1")),181	DAILINK_COMP_ARRAY(COMP_DUMMY()),182	DAILINK_COMP_ARRAY(COMP_EMPTY()));183 184SND_SOC_DAILINK_DEFS(capture,185	DAILINK_COMP_ARRAY(COMP_CPU("VUL")),186	DAILINK_COMP_ARRAY(COMP_DUMMY()),187	DAILINK_COMP_ARRAY(COMP_EMPTY()));188 189SND_SOC_DAILINK_DEFS(hdmi_pcm,190	DAILINK_COMP_ARRAY(COMP_CPU("HDMI")),191	DAILINK_COMP_ARRAY(COMP_DUMMY()),192	DAILINK_COMP_ARRAY(COMP_EMPTY()));193 194SND_SOC_DAILINK_DEFS(codec,195	DAILINK_COMP_ARRAY(COMP_CPU("I2S")),196	DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "rt5645-aif1"), /* Playback */197			   COMP_CODEC(NULL, "rt5645-aif1")),/* Capture */198	DAILINK_COMP_ARRAY(COMP_EMPTY()));199 200SND_SOC_DAILINK_DEFS(hdmi_be,201	DAILINK_COMP_ARRAY(COMP_CPU("HDMIO")),202	DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "i2s-hifi")),203	DAILINK_COMP_ARRAY(COMP_EMPTY()));204 205/* Digital audio interface glue - connects codec <---> CPU */206static struct snd_soc_dai_link mt8173_rt5650_dais[] = {207	/* Front End DAI links */208	[DAI_LINK_PLAYBACK] = {209		.name = "rt5650 Playback",210		.stream_name = "rt5650 Playback",211		.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},212		.dynamic = 1,213		.dpcm_playback = 1,214		SND_SOC_DAILINK_REG(playback),215	},216	[DAI_LINK_CAPTURE] = {217		.name = "rt5650 Capture",218		.stream_name = "rt5650 Capture",219		.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},220		.dynamic = 1,221		.dpcm_capture = 1,222		SND_SOC_DAILINK_REG(capture),223	},224	[DAI_LINK_HDMI] = {225		.name = "HDMI",226		.stream_name = "HDMI PCM",227		.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},228		.dynamic = 1,229		.dpcm_playback = 1,230		SND_SOC_DAILINK_REG(hdmi_pcm),231	},232	/* Back End DAI links */233	[DAI_LINK_CODEC_I2S] = {234		.name = "Codec",235		.no_pcm = 1,236		.init = mt8173_rt5650_init,237		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |238			   SND_SOC_DAIFMT_CBS_CFS,239		.ops = &mt8173_rt5650_ops,240		.ignore_pmdown_time = 1,241		.dpcm_playback = 1,242		.dpcm_capture = 1,243		SND_SOC_DAILINK_REG(codec),244	},245	[DAI_LINK_HDMI_I2S] = {246		.name = "HDMI BE",247		.no_pcm = 1,248		.dpcm_playback = 1,249		.init = mt8173_rt5650_hdmi_init,250		SND_SOC_DAILINK_REG(hdmi_be),251	},252};253 254static struct snd_soc_card mt8173_rt5650_card = {255	.name = "mtk-rt5650",256	.owner = THIS_MODULE,257	.dai_link = mt8173_rt5650_dais,258	.num_links = ARRAY_SIZE(mt8173_rt5650_dais),259	.controls = mt8173_rt5650_controls,260	.num_controls = ARRAY_SIZE(mt8173_rt5650_controls),261	.dapm_widgets = mt8173_rt5650_widgets,262	.num_dapm_widgets = ARRAY_SIZE(mt8173_rt5650_widgets),263	.dapm_routes = mt8173_rt5650_routes,264	.num_dapm_routes = ARRAY_SIZE(mt8173_rt5650_routes),265};266 267static int mt8173_rt5650_dev_probe(struct platform_device *pdev)268{269	struct snd_soc_card *card = &mt8173_rt5650_card;270	struct device_node *platform_node;271	struct device_node *np;272	const char *codec_capture_dai;273	struct snd_soc_dai_link *dai_link;274	int i, ret;275 276	platform_node = of_parse_phandle(pdev->dev.of_node,277					 "mediatek,platform", 0);278	if (!platform_node) {279		dev_err(&pdev->dev, "Property 'platform' missing or invalid\n");280		return -EINVAL;281	}282 283	for_each_card_prelinks(card, i, dai_link) {284		if (dai_link->platforms->name)285			continue;286		dai_link->platforms->of_node = platform_node;287	}288 289	mt8173_rt5650_dais[DAI_LINK_CODEC_I2S].codecs[0].of_node =290		of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 0);291	if (!mt8173_rt5650_dais[DAI_LINK_CODEC_I2S].codecs[0].of_node) {292		dev_err(&pdev->dev,293			"Property 'audio-codec' missing or invalid\n");294		ret = -EINVAL;295		goto put_platform_node;296	}297	mt8173_rt5650_dais[DAI_LINK_CODEC_I2S].codecs[1].of_node =298		mt8173_rt5650_dais[DAI_LINK_CODEC_I2S].codecs[0].of_node;299 300	np = of_get_child_by_name(pdev->dev.of_node, "codec-capture");301	if (np) {302		ret = snd_soc_of_get_dai_name(np, &codec_capture_dai, 0);303		of_node_put(np);304		if (ret < 0) {305			dev_err(&pdev->dev,306				"%s codec_capture_dai name fail %d\n",307				__func__, ret);308			goto put_platform_node;309		}310		mt8173_rt5650_dais[DAI_LINK_CODEC_I2S].codecs[1].dai_name =311			codec_capture_dai;312	}313 314	if (device_property_present(&pdev->dev, "mediatek,mclk")) {315		ret = device_property_read_u32(&pdev->dev,316					       "mediatek,mclk",317					       &mt8173_rt5650_priv.pll_from);318		if (ret) {319			dev_err(&pdev->dev,320				"%s snd_soc_register_card fail %d\n",321				__func__, ret);322		}323	}324 325	mt8173_rt5650_dais[DAI_LINK_HDMI_I2S].codecs->of_node =326		of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 1);327	if (!mt8173_rt5650_dais[DAI_LINK_HDMI_I2S].codecs->of_node) {328		dev_err(&pdev->dev,329			"Property 'audio-codec' missing or invalid\n");330		ret = -EINVAL;331		goto put_platform_node;332	}333	card->dev = &pdev->dev;334 335	ret = devm_snd_soc_register_card(&pdev->dev, card);336 337put_platform_node:338	of_node_put(platform_node);339	return ret;340}341 342static const struct of_device_id mt8173_rt5650_dt_match[] = {343	{ .compatible = "mediatek,mt8173-rt5650", },344	{ }345};346MODULE_DEVICE_TABLE(of, mt8173_rt5650_dt_match);347 348static struct platform_driver mt8173_rt5650_driver = {349	.driver = {350		   .name = "mtk-rt5650",351		   .of_match_table = mt8173_rt5650_dt_match,352		   .pm = &snd_soc_pm_ops,353	},354	.probe = mt8173_rt5650_dev_probe,355};356 357module_platform_driver(mt8173_rt5650_driver);358 359/* Module information */360MODULE_DESCRIPTION("MT8173 RT5650 SoC machine driver");361MODULE_AUTHOR("Koro Chen <koro.chen@mediatek.com>");362MODULE_LICENSE("GPL v2");363MODULE_ALIAS("platform:mtk-rt5650");364 365