18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright 2011 Freescale Semiconductor, Inc.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/module.h>
78c2ecf20Sopenharmony_ci#include <linux/device.h>
88c2ecf20Sopenharmony_ci#include <linux/of.h>
98c2ecf20Sopenharmony_ci#include <linux/of_device.h>
108c2ecf20Sopenharmony_ci#include <sound/core.h>
118c2ecf20Sopenharmony_ci#include <sound/pcm.h>
128c2ecf20Sopenharmony_ci#include <sound/soc.h>
138c2ecf20Sopenharmony_ci#include <sound/jack.h>
148c2ecf20Sopenharmony_ci#include <sound/soc-dapm.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include "../codecs/sgtl5000.h"
178c2ecf20Sopenharmony_ci#include "mxs-saif.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic int mxs_sgtl5000_hw_params(struct snd_pcm_substream *substream,
208c2ecf20Sopenharmony_ci	struct snd_pcm_hw_params *params)
218c2ecf20Sopenharmony_ci{
228c2ecf20Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
238c2ecf20Sopenharmony_ci	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
248c2ecf20Sopenharmony_ci	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
258c2ecf20Sopenharmony_ci	unsigned int rate = params_rate(params);
268c2ecf20Sopenharmony_ci	u32 mclk;
278c2ecf20Sopenharmony_ci	int ret;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	/* sgtl5000 does not support 512*rate when in 96000 fs */
308c2ecf20Sopenharmony_ci	switch (rate) {
318c2ecf20Sopenharmony_ci	case 96000:
328c2ecf20Sopenharmony_ci		mclk = 256 * rate;
338c2ecf20Sopenharmony_ci		break;
348c2ecf20Sopenharmony_ci	default:
358c2ecf20Sopenharmony_ci		mclk = 512 * rate;
368c2ecf20Sopenharmony_ci		break;
378c2ecf20Sopenharmony_ci	}
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	/* Set SGTL5000's SYSCLK (provided by SAIF MCLK) */
408c2ecf20Sopenharmony_ci	ret = snd_soc_dai_set_sysclk(codec_dai, SGTL5000_SYSCLK, mclk, 0);
418c2ecf20Sopenharmony_ci	if (ret) {
428c2ecf20Sopenharmony_ci		dev_err(codec_dai->dev, "Failed to set sysclk to %u.%03uMHz\n",
438c2ecf20Sopenharmony_ci			mclk / 1000000, mclk / 1000 % 1000);
448c2ecf20Sopenharmony_ci		return ret;
458c2ecf20Sopenharmony_ci	}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	/* The SAIF MCLK should be the same as SGTL5000_SYSCLK */
488c2ecf20Sopenharmony_ci	ret = snd_soc_dai_set_sysclk(cpu_dai, MXS_SAIF_MCLK, mclk, 0);
498c2ecf20Sopenharmony_ci	if (ret) {
508c2ecf20Sopenharmony_ci		dev_err(cpu_dai->dev, "Failed to set sysclk to %u.%03uMHz\n",
518c2ecf20Sopenharmony_ci			mclk / 1000000, mclk / 1000 % 1000);
528c2ecf20Sopenharmony_ci		return ret;
538c2ecf20Sopenharmony_ci	}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	return 0;
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic const struct snd_soc_ops mxs_sgtl5000_hifi_ops = {
598c2ecf20Sopenharmony_ci	.hw_params = mxs_sgtl5000_hw_params,
608c2ecf20Sopenharmony_ci};
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci#define MXS_SGTL5000_DAI_FMT (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | \
638c2ecf20Sopenharmony_ci	SND_SOC_DAIFMT_CBS_CFS)
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ciSND_SOC_DAILINK_DEFS(hifi_tx,
678c2ecf20Sopenharmony_ci	DAILINK_COMP_ARRAY(COMP_EMPTY()),
688c2ecf20Sopenharmony_ci	DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "sgtl5000")),
698c2ecf20Sopenharmony_ci	DAILINK_COMP_ARRAY(COMP_EMPTY()));
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ciSND_SOC_DAILINK_DEFS(hifi_rx,
728c2ecf20Sopenharmony_ci	DAILINK_COMP_ARRAY(COMP_EMPTY()),
738c2ecf20Sopenharmony_ci	DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "sgtl5000")),
748c2ecf20Sopenharmony_ci	DAILINK_COMP_ARRAY(COMP_EMPTY()));
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic struct snd_soc_dai_link mxs_sgtl5000_dai[] = {
778c2ecf20Sopenharmony_ci	{
788c2ecf20Sopenharmony_ci		.name		= "HiFi Tx",
798c2ecf20Sopenharmony_ci		.stream_name	= "HiFi Playback",
808c2ecf20Sopenharmony_ci		.dai_fmt	= MXS_SGTL5000_DAI_FMT,
818c2ecf20Sopenharmony_ci		.ops		= &mxs_sgtl5000_hifi_ops,
828c2ecf20Sopenharmony_ci		.playback_only	= true,
838c2ecf20Sopenharmony_ci		SND_SOC_DAILINK_REG(hifi_tx),
848c2ecf20Sopenharmony_ci	}, {
858c2ecf20Sopenharmony_ci		.name		= "HiFi Rx",
868c2ecf20Sopenharmony_ci		.stream_name	= "HiFi Capture",
878c2ecf20Sopenharmony_ci		.dai_fmt	= MXS_SGTL5000_DAI_FMT,
888c2ecf20Sopenharmony_ci		.ops		= &mxs_sgtl5000_hifi_ops,
898c2ecf20Sopenharmony_ci		.capture_only	= true,
908c2ecf20Sopenharmony_ci		SND_SOC_DAILINK_REG(hifi_rx),
918c2ecf20Sopenharmony_ci	},
928c2ecf20Sopenharmony_ci};
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_widget mxs_sgtl5000_dapm_widgets[] = {
958c2ecf20Sopenharmony_ci	SND_SOC_DAPM_MIC("Mic Jack", NULL),
968c2ecf20Sopenharmony_ci	SND_SOC_DAPM_LINE("Line In Jack", NULL),
978c2ecf20Sopenharmony_ci	SND_SOC_DAPM_HP("Headphone Jack", NULL),
988c2ecf20Sopenharmony_ci	SND_SOC_DAPM_SPK("Line Out Jack", NULL),
998c2ecf20Sopenharmony_ci	SND_SOC_DAPM_SPK("Ext Spk", NULL),
1008c2ecf20Sopenharmony_ci};
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistatic struct snd_soc_card mxs_sgtl5000 = {
1038c2ecf20Sopenharmony_ci	.name		= "mxs_sgtl5000",
1048c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
1058c2ecf20Sopenharmony_ci	.dai_link	= mxs_sgtl5000_dai,
1068c2ecf20Sopenharmony_ci	.num_links	= ARRAY_SIZE(mxs_sgtl5000_dai),
1078c2ecf20Sopenharmony_ci};
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic int mxs_sgtl5000_probe(struct platform_device *pdev)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	struct snd_soc_card *card = &mxs_sgtl5000;
1128c2ecf20Sopenharmony_ci	int ret, i;
1138c2ecf20Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
1148c2ecf20Sopenharmony_ci	struct device_node *saif_np[2], *codec_np;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	saif_np[0] = of_parse_phandle(np, "saif-controllers", 0);
1178c2ecf20Sopenharmony_ci	saif_np[1] = of_parse_phandle(np, "saif-controllers", 1);
1188c2ecf20Sopenharmony_ci	codec_np = of_parse_phandle(np, "audio-codec", 0);
1198c2ecf20Sopenharmony_ci	if (!saif_np[0] || !saif_np[1] || !codec_np) {
1208c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "phandle missing or invalid\n");
1218c2ecf20Sopenharmony_ci		of_node_put(codec_np);
1228c2ecf20Sopenharmony_ci		of_node_put(saif_np[0]);
1238c2ecf20Sopenharmony_ci		of_node_put(saif_np[1]);
1248c2ecf20Sopenharmony_ci		return -EINVAL;
1258c2ecf20Sopenharmony_ci	}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	for (i = 0; i < 2; i++) {
1288c2ecf20Sopenharmony_ci		mxs_sgtl5000_dai[i].codecs->name = NULL;
1298c2ecf20Sopenharmony_ci		mxs_sgtl5000_dai[i].codecs->of_node = codec_np;
1308c2ecf20Sopenharmony_ci		mxs_sgtl5000_dai[i].cpus->dai_name = NULL;
1318c2ecf20Sopenharmony_ci		mxs_sgtl5000_dai[i].cpus->of_node = saif_np[i];
1328c2ecf20Sopenharmony_ci		mxs_sgtl5000_dai[i].platforms->name = NULL;
1338c2ecf20Sopenharmony_ci		mxs_sgtl5000_dai[i].platforms->of_node = saif_np[i];
1348c2ecf20Sopenharmony_ci	}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	of_node_put(codec_np);
1378c2ecf20Sopenharmony_ci	of_node_put(saif_np[0]);
1388c2ecf20Sopenharmony_ci	of_node_put(saif_np[1]);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	/*
1418c2ecf20Sopenharmony_ci	 * Set an init clock(11.28Mhz) for sgtl5000 initialization(i2c r/w).
1428c2ecf20Sopenharmony_ci	 * The Sgtl5000 sysclk is derived from saif0 mclk and it's range
1438c2ecf20Sopenharmony_ci	 * should be >= 8MHz and <= 27M.
1448c2ecf20Sopenharmony_ci	 */
1458c2ecf20Sopenharmony_ci	ret = mxs_saif_get_mclk(0, 44100 * 256, 44100);
1468c2ecf20Sopenharmony_ci	if (ret) {
1478c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to get mclk\n");
1488c2ecf20Sopenharmony_ci		return ret;
1498c2ecf20Sopenharmony_ci	}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	card->dev = &pdev->dev;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	if (of_find_property(np, "audio-routing", NULL)) {
1548c2ecf20Sopenharmony_ci		card->dapm_widgets = mxs_sgtl5000_dapm_widgets;
1558c2ecf20Sopenharmony_ci		card->num_dapm_widgets = ARRAY_SIZE(mxs_sgtl5000_dapm_widgets);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci		ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
1588c2ecf20Sopenharmony_ci		if (ret) {
1598c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "failed to parse audio-routing (%d)\n",
1608c2ecf20Sopenharmony_ci				ret);
1618c2ecf20Sopenharmony_ci			return ret;
1628c2ecf20Sopenharmony_ci		}
1638c2ecf20Sopenharmony_ci	}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	ret = devm_snd_soc_register_card(&pdev->dev, card);
1668c2ecf20Sopenharmony_ci	if (ret) {
1678c2ecf20Sopenharmony_ci		if (ret != -EPROBE_DEFER)
1688c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
1698c2ecf20Sopenharmony_ci				ret);
1708c2ecf20Sopenharmony_ci		return ret;
1718c2ecf20Sopenharmony_ci	}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	return 0;
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic int mxs_sgtl5000_remove(struct platform_device *pdev)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	mxs_saif_put_mclk(0);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	return 0;
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic const struct of_device_id mxs_sgtl5000_dt_ids[] = {
1848c2ecf20Sopenharmony_ci	{ .compatible = "fsl,mxs-audio-sgtl5000", },
1858c2ecf20Sopenharmony_ci	{ /* sentinel */ }
1868c2ecf20Sopenharmony_ci};
1878c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, mxs_sgtl5000_dt_ids);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic struct platform_driver mxs_sgtl5000_audio_driver = {
1908c2ecf20Sopenharmony_ci	.driver = {
1918c2ecf20Sopenharmony_ci		.name = "mxs-sgtl5000",
1928c2ecf20Sopenharmony_ci		.of_match_table = mxs_sgtl5000_dt_ids,
1938c2ecf20Sopenharmony_ci	},
1948c2ecf20Sopenharmony_ci	.probe = mxs_sgtl5000_probe,
1958c2ecf20Sopenharmony_ci	.remove = mxs_sgtl5000_remove,
1968c2ecf20Sopenharmony_ci};
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cimodule_platform_driver(mxs_sgtl5000_audio_driver);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ciMODULE_AUTHOR("Freescale Semiconductor, Inc.");
2018c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MXS ALSA SoC Machine driver");
2028c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
2038c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:mxs-sgtl5000");
204