brintos

brintos / linux-shallow public Read only

0
0
Text · 5.0 KiB · 2474eb6 Raw
220 lines · c
1// SPDX-License-Identifier: GPL-2.0+2//3// smdk_spdif.c - S/PDIF audio for SMDK4//5// Copyright (C) 2010 Samsung Electronics Co., Ltd.6 7#include <linux/clk.h>8#include <linux/module.h>9 10#include <sound/soc.h>11 12#include "spdif.h"13 14/* Audio clock settings are belonged to board specific part. Every15 * board can set audio source clock setting which is matched with H/W16 * like this function-'set_audio_clock_heirachy'.17 */18static int set_audio_clock_heirachy(struct platform_device *pdev)19{20	struct clk *fout_epll, *mout_epll, *sclk_audio0, *sclk_spdif;21	int ret = 0;22 23	fout_epll = clk_get(NULL, "fout_epll");24	if (IS_ERR(fout_epll)) {25		printk(KERN_WARNING "%s: Cannot find fout_epll.\n",26				__func__);27		return -EINVAL;28	}29 30	mout_epll = clk_get(NULL, "mout_epll");31	if (IS_ERR(mout_epll)) {32		printk(KERN_WARNING "%s: Cannot find mout_epll.\n",33				__func__);34		ret = -EINVAL;35		goto out1;36	}37 38	sclk_audio0 = clk_get(&pdev->dev, "sclk_audio");39	if (IS_ERR(sclk_audio0)) {40		printk(KERN_WARNING "%s: Cannot find sclk_audio.\n",41				__func__);42		ret = -EINVAL;43		goto out2;44	}45 46	sclk_spdif = clk_get(NULL, "sclk_spdif");47	if (IS_ERR(sclk_spdif)) {48		printk(KERN_WARNING "%s: Cannot find sclk_spdif.\n",49				__func__);50		ret = -EINVAL;51		goto out3;52	}53 54	/* Set audio clock hierarchy for S/PDIF */55	clk_set_parent(mout_epll, fout_epll);56	clk_set_parent(sclk_audio0, mout_epll);57	clk_set_parent(sclk_spdif, sclk_audio0);58 59	clk_put(sclk_spdif);60out3:61	clk_put(sclk_audio0);62out2:63	clk_put(mout_epll);64out1:65	clk_put(fout_epll);66 67	return ret;68}69 70/* We should haved to set clock directly on this part because of clock71 * scheme of Samsudng SoCs did not support to set rates from abstrct72 * clock of it's hierarchy.73 */74static int set_audio_clock_rate(unsigned long epll_rate,75				unsigned long audio_rate)76{77	struct clk *fout_epll, *sclk_spdif;78 79	fout_epll = clk_get(NULL, "fout_epll");80	if (IS_ERR(fout_epll)) {81		printk(KERN_ERR "%s: failed to get fout_epll\n", __func__);82		return -ENOENT;83	}84 85	clk_set_rate(fout_epll, epll_rate);86	clk_put(fout_epll);87 88	sclk_spdif = clk_get(NULL, "sclk_spdif");89	if (IS_ERR(sclk_spdif)) {90		printk(KERN_ERR "%s: failed to get sclk_spdif\n", __func__);91		return -ENOENT;92	}93 94	clk_set_rate(sclk_spdif, audio_rate);95	clk_put(sclk_spdif);96 97	return 0;98}99 100static int smdk_hw_params(struct snd_pcm_substream *substream,101		struct snd_pcm_hw_params *params)102{103	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);104	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);105	unsigned long pll_out, rclk_rate;106	int ret, ratio;107 108	switch (params_rate(params)) {109	case 44100:110		pll_out = 45158400;111		break;112	case 32000:113	case 48000:114	case 96000:115		pll_out = 49152000;116		break;117	default:118		return -EINVAL;119	}120 121	/* Setting ratio to 512fs helps to use S/PDIF with HDMI without122	 * modify S/PDIF ASoC machine driver.123	 */124	ratio = 512;125	rclk_rate = params_rate(params) * ratio;126 127	/* Set audio source clock rates */128	ret = set_audio_clock_rate(pll_out, rclk_rate);129	if (ret < 0)130		return ret;131 132	/* Set S/PDIF uses internal source clock */133	ret = snd_soc_dai_set_sysclk(cpu_dai, SND_SOC_SPDIF_INT_MCLK,134					rclk_rate, SND_SOC_CLOCK_IN);135	if (ret < 0)136		return ret;137 138	return ret;139}140 141static const struct snd_soc_ops smdk_spdif_ops = {142	.hw_params = smdk_hw_params,143};144 145SND_SOC_DAILINK_DEFS(spdif,146	DAILINK_COMP_ARRAY(COMP_CPU("samsung-spdif")),147	DAILINK_COMP_ARRAY(COMP_CODEC("spdif-dit", "dit-hifi")),148	DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-spdif")));149 150static struct snd_soc_dai_link smdk_dai = {151	.name = "S/PDIF",152	.stream_name = "S/PDIF PCM Playback",153	.ops = &smdk_spdif_ops,154	SND_SOC_DAILINK_REG(spdif),155};156 157static struct snd_soc_card smdk = {158	.name = "SMDK-S/PDIF",159	.owner = THIS_MODULE,160	.dai_link = &smdk_dai,161	.num_links = 1,162};163 164static struct platform_device *smdk_snd_spdif_dit_device;165static struct platform_device *smdk_snd_spdif_device;166 167static int __init smdk_init(void)168{169	int ret;170 171	smdk_snd_spdif_dit_device = platform_device_alloc("spdif-dit", -1);172	if (!smdk_snd_spdif_dit_device)173		return -ENOMEM;174 175	ret = platform_device_add(smdk_snd_spdif_dit_device);176	if (ret)177		goto err1;178 179	smdk_snd_spdif_device = platform_device_alloc("soc-audio", -1);180	if (!smdk_snd_spdif_device) {181		ret = -ENOMEM;182		goto err2;183	}184 185	platform_set_drvdata(smdk_snd_spdif_device, &smdk);186 187	ret = platform_device_add(smdk_snd_spdif_device);188	if (ret)189		goto err3;190 191	/* Set audio clock hierarchy manually */192	ret = set_audio_clock_heirachy(smdk_snd_spdif_device);193	if (ret)194		goto err4;195 196	return 0;197err4:198	platform_device_del(smdk_snd_spdif_device);199err3:200	platform_device_put(smdk_snd_spdif_device);201err2:202	platform_device_del(smdk_snd_spdif_dit_device);203err1:204	platform_device_put(smdk_snd_spdif_dit_device);205	return ret;206}207 208static void __exit smdk_exit(void)209{210	platform_device_unregister(smdk_snd_spdif_device);211	platform_device_unregister(smdk_snd_spdif_dit_device);212}213 214module_init(smdk_init);215module_exit(smdk_exit);216 217MODULE_AUTHOR("Seungwhan Youn, <sw.youn@samsung.com>");218MODULE_DESCRIPTION("ALSA SoC SMDK+S/PDIF");219MODULE_LICENSE("GPL");220