1208562Srwatson// SPDX-License-Identifier: GPL-2.0+ 2208562Srwatson// 3208562Srwatson// smdk_spdif.c - S/PDIF audio for SMDK 4208562Srwatson// 5208562Srwatson// Copyright (C) 2010 Samsung Electronics Co., Ltd. 6208562Srwatson 7208562Srwatson#include <linux/clk.h> 8208562Srwatson#include <linux/module.h> 9208562Srwatson 10208562Srwatson#include <sound/soc.h> 11208562Srwatson 12208562Srwatson#include "spdif.h" 13208562Srwatson 14208562Srwatson/* Audio clock settings are belonged to board specific part. Every 15208562Srwatson * board can set audio source clock setting which is matched with H/W 16208562Srwatson * like this function-'set_audio_clock_heirachy'. 17208562Srwatson */ 18208562Srwatsonstatic int set_audio_clock_heirachy(struct platform_device *pdev) 19208562Srwatson{ 20208562Srwatson struct clk *fout_epll, *mout_epll, *sclk_audio0, *sclk_spdif; 21208562Srwatson int ret = 0; 22208562Srwatson 23208562Srwatson fout_epll = clk_get(NULL, "fout_epll"); 24208562Srwatson if (IS_ERR(fout_epll)) { 25208562Srwatson printk(KERN_WARNING "%s: Cannot find fout_epll.\n", 26208562Srwatson __func__); 27208562Srwatson return -EINVAL; 28208562Srwatson } 29208562Srwatson 30208562Srwatson mout_epll = clk_get(NULL, "mout_epll"); 31208562Srwatson if (IS_ERR(mout_epll)) { 32208562Srwatson printk(KERN_WARNING "%s: Cannot find mout_epll.\n", 33208562Srwatson __func__); 34208562Srwatson ret = -EINVAL; 35208562Srwatson goto out1; 36208562Srwatson } 37208562Srwatson 38208562Srwatson sclk_audio0 = clk_get(&pdev->dev, "sclk_audio"); 39208562Srwatson if (IS_ERR(sclk_audio0)) { 40208562Srwatson printk(KERN_WARNING "%s: Cannot find sclk_audio.\n", 41208562Srwatson __func__); 42208562Srwatson ret = -EINVAL; 43208562Srwatson goto out2; 44208562Srwatson } 45208562Srwatson 46208562Srwatson sclk_spdif = clk_get(NULL, "sclk_spdif"); 47208562Srwatson if (IS_ERR(sclk_spdif)) { 48208562Srwatson printk(KERN_WARNING "%s: Cannot find sclk_spdif.\n", 49208562Srwatson __func__); 50208562Srwatson ret = -EINVAL; 51208562Srwatson goto out3; 52208562Srwatson } 53208562Srwatson 54208562Srwatson /* Set audio clock hierarchy for S/PDIF */ 55208562Srwatson clk_set_parent(mout_epll, fout_epll); 56208562Srwatson clk_set_parent(sclk_audio0, mout_epll); 57281400Sngie clk_set_parent(sclk_spdif, sclk_audio0); 58281400Sngie 59208562Srwatson clk_put(sclk_spdif); 60208562Srwatsonout3: 61208562Srwatson clk_put(sclk_audio0); 62208562Srwatsonout2: 63281400Sngie clk_put(mout_epll); 64208562Srwatsonout1: 65208562Srwatson clk_put(fout_epll); 66208562Srwatson 67208562Srwatson return ret; 68208562Srwatson} 69208562Srwatson 70208562Srwatson/* We should haved to set clock directly on this part because of clock 71208562Srwatson * scheme of Samsudng SoCs did not support to set rates from abstrct 72208562Srwatson * clock of it's hierarchy. 73208562Srwatson */ 74208562Srwatsonstatic int set_audio_clock_rate(unsigned long epll_rate, 75208562Srwatson unsigned long audio_rate) 76208562Srwatson{ 77208562Srwatson struct clk *fout_epll, *sclk_spdif; 78281400Sngie 79281400Sngie fout_epll = clk_get(NULL, "fout_epll"); 80281400Sngie if (IS_ERR(fout_epll)) { 81281400Sngie printk(KERN_ERR "%s: failed to get fout_epll\n", __func__); 82208562Srwatson return -ENOENT; 83208602Srwatson } 84208602Srwatson 85208562Srwatson clk_set_rate(fout_epll, epll_rate); 86208562Srwatson clk_put(fout_epll); 87208562Srwatson 88281400Sngie sclk_spdif = clk_get(NULL, "sclk_spdif"); 89208562Srwatson if (IS_ERR(sclk_spdif)) { 90208562Srwatson printk(KERN_ERR "%s: failed to get sclk_spdif\n", __func__); 91281400Sngie return -ENOENT; 92208562Srwatson } 93208562Srwatson 94208562Srwatson clk_set_rate(sclk_spdif, audio_rate); 95208562Srwatson clk_put(sclk_spdif); 96208562Srwatson 97208562Srwatson return 0; 98208562Srwatson} 99208562Srwatson 100208562Srwatsonstatic int smdk_hw_params(struct snd_pcm_substream *substream, 101208562Srwatson struct snd_pcm_hw_params *params) 102208562Srwatson{ 103208562Srwatson struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 104208562Srwatson struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); 105208562Srwatson unsigned long pll_out, rclk_rate; 106208562Srwatson int ret, ratio; 107208562Srwatson 108208562Srwatson switch (params_rate(params)) { 109208562Srwatson case 44100: 110281400Sngie pll_out = 45158400; 111208562Srwatson break; 112208602Srwatson case 32000: 113208602Srwatson case 48000: 114208602Srwatson case 96000: 115208602Srwatson pll_out = 49152000; 116208562Srwatson break; 117208602Srwatson default: 118208602Srwatson return -EINVAL; 119208602Srwatson } 120208602Srwatson 121208602Srwatson /* Setting ratio to 512fs helps to use S/PDIF with HDMI without 122208602Srwatson * modify S/PDIF ASoC machine driver. 123208602Srwatson */ 124208602Srwatson ratio = 512; 125208562Srwatson rclk_rate = params_rate(params) * ratio; 126208562Srwatson 127208562Srwatson /* Set audio source clock rates */ 128208562Srwatson ret = set_audio_clock_rate(pll_out, rclk_rate); 129208562Srwatson if (ret < 0) 130208562Srwatson return ret; 131208562Srwatson 132208562Srwatson /* Set S/PDIF uses internal source clock */ 133208562Srwatson ret = snd_soc_dai_set_sysclk(cpu_dai, SND_SOC_SPDIF_INT_MCLK, 134208562Srwatson rclk_rate, SND_SOC_CLOCK_IN); 135208562Srwatson if (ret < 0) 136208602Srwatson return ret; 137208562Srwatson 138208602Srwatson return ret; 139208562Srwatson} 140208562Srwatson 141208562Srwatsonstatic const struct snd_soc_ops smdk_spdif_ops = { 142208562Srwatson .hw_params = smdk_hw_params, 143208562Srwatson}; 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