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