162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include "../codecs/wm8994.h"
462306a36Sopenharmony_ci#include <sound/pcm_params.h>
562306a36Sopenharmony_ci#include <sound/soc.h>
662306a36Sopenharmony_ci#include <linux/module.h>
762306a36Sopenharmony_ci#include <linux/of.h>
862306a36Sopenharmony_ci#include <linux/of_device.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci /*
1162306a36Sopenharmony_ci  * Default CFG switch settings to use this driver:
1262306a36Sopenharmony_ci  *	SMDKV310: CFG5-1000, CFG7-111111
1362306a36Sopenharmony_ci  */
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci /*
1662306a36Sopenharmony_ci  * Configure audio route as :-
1762306a36Sopenharmony_ci  * $ amixer sset 'DAC1' on,on
1862306a36Sopenharmony_ci  * $ amixer sset 'Right Headphone Mux' 'DAC'
1962306a36Sopenharmony_ci  * $ amixer sset 'Left Headphone Mux' 'DAC'
2062306a36Sopenharmony_ci  * $ amixer sset 'DAC1R Mixer AIF1.1' on
2162306a36Sopenharmony_ci  * $ amixer sset 'DAC1L Mixer AIF1.1' on
2262306a36Sopenharmony_ci  * $ amixer sset 'IN2L' on
2362306a36Sopenharmony_ci  * $ amixer sset 'IN2L PGA IN2LN' on
2462306a36Sopenharmony_ci  * $ amixer sset 'MIXINL IN2L' on
2562306a36Sopenharmony_ci  * $ amixer sset 'AIF1ADC1L Mixer ADC/DMIC' on
2662306a36Sopenharmony_ci  * $ amixer sset 'IN2R' on
2762306a36Sopenharmony_ci  * $ amixer sset 'IN2R PGA IN2RN' on
2862306a36Sopenharmony_ci  * $ amixer sset 'MIXINR IN2R' on
2962306a36Sopenharmony_ci  * $ amixer sset 'AIF1ADC1R Mixer ADC/DMIC' on
3062306a36Sopenharmony_ci  */
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/* SMDK has a 16.934MHZ crystal attached to WM8994 */
3362306a36Sopenharmony_ci#define SMDK_WM8994_FREQ 16934000
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistruct smdk_wm8994_data {
3662306a36Sopenharmony_ci	int mclk1_rate;
3762306a36Sopenharmony_ci};
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci/* Default SMDKs */
4062306a36Sopenharmony_cistatic struct smdk_wm8994_data smdk_board_data = {
4162306a36Sopenharmony_ci	.mclk1_rate = SMDK_WM8994_FREQ,
4262306a36Sopenharmony_ci};
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic int smdk_hw_params(struct snd_pcm_substream *substream,
4562306a36Sopenharmony_ci	struct snd_pcm_hw_params *params)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
4862306a36Sopenharmony_ci	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
4962306a36Sopenharmony_ci	unsigned int pll_out;
5062306a36Sopenharmony_ci	int ret;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	/* AIF1CLK should be >=3MHz for optimal performance */
5362306a36Sopenharmony_ci	if (params_width(params) == 24)
5462306a36Sopenharmony_ci		pll_out = params_rate(params) * 384;
5562306a36Sopenharmony_ci	else if (params_rate(params) == 8000 || params_rate(params) == 11025)
5662306a36Sopenharmony_ci		pll_out = params_rate(params) * 512;
5762306a36Sopenharmony_ci	else
5862306a36Sopenharmony_ci		pll_out = params_rate(params) * 256;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL1, WM8994_FLL_SRC_MCLK1,
6162306a36Sopenharmony_ci					SMDK_WM8994_FREQ, pll_out);
6262306a36Sopenharmony_ci	if (ret < 0)
6362306a36Sopenharmony_ci		return ret;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL1,
6662306a36Sopenharmony_ci					pll_out, SND_SOC_CLOCK_IN);
6762306a36Sopenharmony_ci	if (ret < 0)
6862306a36Sopenharmony_ci		return ret;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	return 0;
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci/*
7462306a36Sopenharmony_ci * SMDK WM8994 DAI operations.
7562306a36Sopenharmony_ci */
7662306a36Sopenharmony_cistatic const struct snd_soc_ops smdk_ops = {
7762306a36Sopenharmony_ci	.hw_params = smdk_hw_params,
7862306a36Sopenharmony_ci};
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic int smdk_wm8994_init_paiftx(struct snd_soc_pcm_runtime *rtd)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	struct snd_soc_dapm_context *dapm = &rtd->card->dapm;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	/* Other pins NC */
8562306a36Sopenharmony_ci	snd_soc_dapm_nc_pin(dapm, "HPOUT2P");
8662306a36Sopenharmony_ci	snd_soc_dapm_nc_pin(dapm, "HPOUT2N");
8762306a36Sopenharmony_ci	snd_soc_dapm_nc_pin(dapm, "SPKOUTLN");
8862306a36Sopenharmony_ci	snd_soc_dapm_nc_pin(dapm, "SPKOUTLP");
8962306a36Sopenharmony_ci	snd_soc_dapm_nc_pin(dapm, "SPKOUTRP");
9062306a36Sopenharmony_ci	snd_soc_dapm_nc_pin(dapm, "SPKOUTRN");
9162306a36Sopenharmony_ci	snd_soc_dapm_nc_pin(dapm, "LINEOUT1N");
9262306a36Sopenharmony_ci	snd_soc_dapm_nc_pin(dapm, "LINEOUT1P");
9362306a36Sopenharmony_ci	snd_soc_dapm_nc_pin(dapm, "LINEOUT2N");
9462306a36Sopenharmony_ci	snd_soc_dapm_nc_pin(dapm, "LINEOUT2P");
9562306a36Sopenharmony_ci	snd_soc_dapm_nc_pin(dapm, "IN1LP");
9662306a36Sopenharmony_ci	snd_soc_dapm_nc_pin(dapm, "IN2LP:VXRN");
9762306a36Sopenharmony_ci	snd_soc_dapm_nc_pin(dapm, "IN1RP");
9862306a36Sopenharmony_ci	snd_soc_dapm_nc_pin(dapm, "IN2RP:VXRP");
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	return 0;
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ciSND_SOC_DAILINK_DEFS(aif1,
10462306a36Sopenharmony_ci	DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.0")),
10562306a36Sopenharmony_ci	DAILINK_COMP_ARRAY(COMP_CODEC("wm8994-codec", "wm8994-aif1")),
10662306a36Sopenharmony_ci	DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0")));
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ciSND_SOC_DAILINK_DEFS(fifo_tx,
10962306a36Sopenharmony_ci	DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s-sec")),
11062306a36Sopenharmony_ci	DAILINK_COMP_ARRAY(COMP_CODEC("wm8994-codec", "wm8994-aif1")),
11162306a36Sopenharmony_ci	DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s-sec")));
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic struct snd_soc_dai_link smdk_dai[] = {
11462306a36Sopenharmony_ci	{ /* Primary DAI i/f */
11562306a36Sopenharmony_ci		.name = "WM8994 AIF1",
11662306a36Sopenharmony_ci		.stream_name = "Pri_Dai",
11762306a36Sopenharmony_ci		.init = smdk_wm8994_init_paiftx,
11862306a36Sopenharmony_ci		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
11962306a36Sopenharmony_ci			SND_SOC_DAIFMT_CBM_CFM,
12062306a36Sopenharmony_ci		.ops = &smdk_ops,
12162306a36Sopenharmony_ci		SND_SOC_DAILINK_REG(aif1),
12262306a36Sopenharmony_ci	}, { /* Sec_Fifo Playback i/f */
12362306a36Sopenharmony_ci		.name = "Sec_FIFO TX",
12462306a36Sopenharmony_ci		.stream_name = "Sec_Dai",
12562306a36Sopenharmony_ci		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
12662306a36Sopenharmony_ci			SND_SOC_DAIFMT_CBM_CFM,
12762306a36Sopenharmony_ci		.ops = &smdk_ops,
12862306a36Sopenharmony_ci		SND_SOC_DAILINK_REG(fifo_tx),
12962306a36Sopenharmony_ci	},
13062306a36Sopenharmony_ci};
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic struct snd_soc_card smdk = {
13362306a36Sopenharmony_ci	.name = "SMDK-I2S",
13462306a36Sopenharmony_ci	.owner = THIS_MODULE,
13562306a36Sopenharmony_ci	.dai_link = smdk_dai,
13662306a36Sopenharmony_ci	.num_links = ARRAY_SIZE(smdk_dai),
13762306a36Sopenharmony_ci};
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic const struct of_device_id samsung_wm8994_of_match[] __maybe_unused = {
14062306a36Sopenharmony_ci	{ .compatible = "samsung,smdk-wm8994", .data = &smdk_board_data },
14162306a36Sopenharmony_ci	{},
14262306a36Sopenharmony_ci};
14362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, samsung_wm8994_of_match);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic int smdk_audio_probe(struct platform_device *pdev)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	int ret;
14862306a36Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
14962306a36Sopenharmony_ci	struct snd_soc_card *card = &smdk;
15062306a36Sopenharmony_ci	struct smdk_wm8994_data *board;
15162306a36Sopenharmony_ci	const struct of_device_id *id;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	card->dev = &pdev->dev;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	board = devm_kzalloc(&pdev->dev, sizeof(*board), GFP_KERNEL);
15662306a36Sopenharmony_ci	if (!board)
15762306a36Sopenharmony_ci		return -ENOMEM;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	if (np) {
16062306a36Sopenharmony_ci		smdk_dai[0].cpus->dai_name = NULL;
16162306a36Sopenharmony_ci		smdk_dai[0].cpus->of_node = of_parse_phandle(np,
16262306a36Sopenharmony_ci				"samsung,i2s-controller", 0);
16362306a36Sopenharmony_ci		if (!smdk_dai[0].cpus->of_node) {
16462306a36Sopenharmony_ci			dev_err(&pdev->dev,
16562306a36Sopenharmony_ci			   "Property 'samsung,i2s-controller' missing or invalid\n");
16662306a36Sopenharmony_ci			ret = -EINVAL;
16762306a36Sopenharmony_ci			return ret;
16862306a36Sopenharmony_ci		}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci		smdk_dai[0].platforms->name = NULL;
17162306a36Sopenharmony_ci		smdk_dai[0].platforms->of_node = smdk_dai[0].cpus->of_node;
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	id = of_match_device(samsung_wm8994_of_match, &pdev->dev);
17562306a36Sopenharmony_ci	if (id)
17662306a36Sopenharmony_ci		*board = *((struct smdk_wm8994_data *)id->data);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	platform_set_drvdata(pdev, board);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	ret = devm_snd_soc_register_card(&pdev->dev, card);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	if (ret)
18362306a36Sopenharmony_ci		dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n");
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	return ret;
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cistatic struct platform_driver smdk_audio_driver = {
18962306a36Sopenharmony_ci	.driver		= {
19062306a36Sopenharmony_ci		.name	= "smdk-audio-wm8994",
19162306a36Sopenharmony_ci		.of_match_table = of_match_ptr(samsung_wm8994_of_match),
19262306a36Sopenharmony_ci		.pm	= &snd_soc_pm_ops,
19362306a36Sopenharmony_ci	},
19462306a36Sopenharmony_ci	.probe		= smdk_audio_probe,
19562306a36Sopenharmony_ci};
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cimodule_platform_driver(smdk_audio_driver);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ciMODULE_DESCRIPTION("ALSA SoC SMDK WM8994");
20062306a36Sopenharmony_ciMODULE_LICENSE("GPL");
20162306a36Sopenharmony_ciMODULE_ALIAS("platform:smdk-audio-wm8994");
202