1 /* 2 * Copyright (C) 2017 Samsung Electronics Co., Ltd. 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 2 as 6 * published by the Free Software Foundation. 7 */ 8 9 #include <linux/clk.h> 10 #include <linux/clk-provider.h> 11 #include <linux/of.h> 12 #include <linux/of_device.h> 13 #include <linux/module.h> 14 #include <sound/soc.h> 15 #include <sound/pcm_params.h> 16 #include "i2s.h" 17 #include "i2s-regs.h" 18 19 struct odroid_priv { 20 struct snd_soc_card card; 21 struct clk *clk_i2s_bus; 22 struct clk *sclk_i2s; 23 }; 24 25 static int odroid_card_fe_startup(struct snd_pcm_substream *substream) 26 { 27 struct snd_pcm_runtime *runtime = substream->runtime; 28 29 snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2); 30 31 return 0; 32 } 33 34 static const struct snd_soc_ops odroid_card_fe_ops = { 35 .startup = odroid_card_fe_startup, 36 }; 37 38 static int odroid_card_be_hw_params(struct snd_pcm_substream *substream, 39 struct snd_pcm_hw_params *params) 40 { 41 struct snd_soc_pcm_runtime *rtd = substream->private_data; 42 struct odroid_priv *priv = snd_soc_card_get_drvdata(rtd->card); 43 unsigned int pll_freq, rclk_freq, rfs; 44 int ret; 45 46 switch (params_rate(params)) { 47 case 64000: 48 pll_freq = 196608001U; 49 rfs = 384; 50 break; 51 case 44100: 52 case 88200: 53 pll_freq = 180633609U; 54 rfs = 512; 55 break; 56 case 32000: 57 case 48000: 58 case 96000: 59 pll_freq = 196608001U; 60 rfs = 512; 61 break; 62 default: 63 return -EINVAL; 64 } 65 66 ret = clk_set_rate(priv->clk_i2s_bus, pll_freq / 2 + 1); 67 if (ret < 0) 68 return ret; 69 70 /* 71 * We add 1 to the rclk_freq value in order to avoid too low clock 72 * frequency values due to the EPLL output frequency not being exact 73 * multiple of the audio sampling rate. 74 */ 75 rclk_freq = params_rate(params) * rfs + 1; 76 77 ret = clk_set_rate(priv->sclk_i2s, rclk_freq); 78 if (ret < 0) 79 return ret; 80 81 if (rtd->num_codecs > 1) { 82 struct snd_soc_dai *codec_dai = rtd->codec_dais[1]; 83 84 ret = snd_soc_dai_set_sysclk(codec_dai, 0, rclk_freq, 85 SND_SOC_CLOCK_IN); 86 if (ret < 0) 87 return ret; 88 } 89 90 return 0; 91 } 92 93 static const struct snd_soc_ops odroid_card_be_ops = { 94 .hw_params = odroid_card_be_hw_params, 95 }; 96 97 static struct snd_soc_dai_link odroid_card_dais[] = { 98 { 99 /* Primary FE <-> BE link */ 100 .codec_name = "snd-soc-dummy", 101 .codec_dai_name = "snd-soc-dummy-dai", 102 .ops = &odroid_card_fe_ops, 103 .name = "Primary", 104 .stream_name = "Primary", 105 .platform_name = "3830000.i2s", 106 .dynamic = 1, 107 .dpcm_playback = 1, 108 }, { 109 /* BE <-> CODECs link */ 110 .name = "I2S Mixer", 111 .cpu_name = "snd-soc-dummy", 112 .cpu_dai_name = "snd-soc-dummy-dai", 113 .platform_name = "snd-soc-dummy", 114 .ops = &odroid_card_be_ops, 115 .no_pcm = 1, 116 .dpcm_playback = 1, 117 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 118 SND_SOC_DAIFMT_CBS_CFS, 119 }, { 120 /* Secondary FE <-> BE link */ 121 .playback_only = 1, 122 .codec_name = "snd-soc-dummy", 123 .codec_dai_name = "snd-soc-dummy-dai", 124 .ops = &odroid_card_fe_ops, 125 .name = "Secondary", 126 .stream_name = "Secondary", 127 .platform_name = "samsung-i2s-sec", 128 .dynamic = 1, 129 .dpcm_playback = 1, 130 } 131 }; 132 133 static int odroid_audio_probe(struct platform_device *pdev) 134 { 135 struct device *dev = &pdev->dev; 136 struct device_node *cpu, *cpu_dai, *codec; 137 struct odroid_priv *priv; 138 struct snd_soc_card *card; 139 struct snd_soc_dai_link *link, *codec_link; 140 int num_pcms, ret, i; 141 struct of_phandle_args args = {}; 142 143 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 144 if (!priv) 145 return -ENOMEM; 146 147 card = &priv->card; 148 card->dev = dev; 149 150 card->owner = THIS_MODULE; 151 card->fully_routed = true; 152 153 snd_soc_card_set_drvdata(card, priv); 154 155 ret = snd_soc_of_parse_card_name(card, "model"); 156 if (ret < 0) 157 return ret; 158 159 if (of_property_read_bool(dev->of_node, "samsung,audio-widgets")) { 160 ret = snd_soc_of_parse_audio_simple_widgets(card, 161 "samsung,audio-widgets"); 162 if (ret < 0) 163 return ret; 164 } 165 166 if (of_property_read_bool(dev->of_node, "samsung,audio-routing")) { 167 ret = snd_soc_of_parse_audio_routing(card, 168 "samsung,audio-routing"); 169 if (ret < 0) 170 return ret; 171 } 172 173 card->dai_link = odroid_card_dais; 174 card->num_links = ARRAY_SIZE(odroid_card_dais); 175 176 cpu = of_get_child_by_name(dev->of_node, "cpu"); 177 codec = of_get_child_by_name(dev->of_node, "codec"); 178 link = card->dai_link; 179 codec_link = &card->dai_link[1]; 180 181 /* 182 * For backwards compatibility create the secondary CPU DAI link only 183 * if there are 2 CPU DAI entries in the cpu sound-dai property in DT. 184 */ 185 num_pcms = of_count_phandle_with_args(cpu, "sound-dai", 186 "#sound-dai-cells"); 187 if (num_pcms == 1) 188 card->num_links--; 189 190 for (i = 0; i < num_pcms; i++, link += 2) { 191 ret = of_parse_phandle_with_args(cpu, "sound-dai", 192 "#sound-dai-cells", i, &args); 193 if (ret < 0) 194 return ret; 195 196 if (!args.np) { 197 dev_err(dev, "sound-dai property parse error: %d\n", ret); 198 return -EINVAL; 199 } 200 201 ret = snd_soc_get_dai_name(&args, &link->cpu_dai_name); 202 of_node_put(args.np); 203 204 if (ret < 0) 205 return ret; 206 } 207 208 cpu_dai = of_parse_phandle(cpu, "sound-dai", 0); 209 of_node_put(cpu); 210 of_node_put(codec); 211 212 ret = snd_soc_of_get_dai_link_codecs(dev, codec, codec_link); 213 if (ret < 0) 214 goto err_put_codec_n; 215 216 /* Set capture capability only for boards with the MAX98090 CODEC */ 217 if (codec_link->num_codecs > 1) { 218 card->dai_link[0].dpcm_capture = 1; 219 card->dai_link[1].dpcm_capture = 1; 220 } 221 222 priv->sclk_i2s = of_clk_get_by_name(cpu_dai, "i2s_opclk1"); 223 if (IS_ERR(priv->sclk_i2s)) { 224 ret = PTR_ERR(priv->sclk_i2s); 225 goto err_put_codec_n; 226 } 227 228 priv->clk_i2s_bus = of_clk_get_by_name(cpu_dai, "iis"); 229 if (IS_ERR(priv->clk_i2s_bus)) { 230 ret = PTR_ERR(priv->clk_i2s_bus); 231 goto err_put_sclk; 232 } 233 of_node_put(cpu_dai); 234 235 ret = devm_snd_soc_register_card(dev, card); 236 if (ret < 0) { 237 dev_err(dev, "snd_soc_register_card() failed: %d\n", ret); 238 goto err_put_clk_i2s; 239 } 240 241 return 0; 242 243 err_put_clk_i2s: 244 clk_put(priv->clk_i2s_bus); 245 err_put_sclk: 246 clk_put(priv->sclk_i2s); 247 err_put_codec_n: 248 snd_soc_of_put_dai_link_codecs(codec_link); 249 return ret; 250 } 251 252 static int odroid_audio_remove(struct platform_device *pdev) 253 { 254 struct odroid_priv *priv = platform_get_drvdata(pdev); 255 256 snd_soc_of_put_dai_link_codecs(&priv->card.dai_link[1]); 257 clk_put(priv->sclk_i2s); 258 clk_put(priv->clk_i2s_bus); 259 260 return 0; 261 } 262 263 static const struct of_device_id odroid_audio_of_match[] = { 264 { .compatible = "hardkernel,odroid-xu3-audio" }, 265 { .compatible = "hardkernel,odroid-xu4-audio" }, 266 { .compatible = "samsung,odroid-xu3-audio" }, 267 { .compatible = "samsung,odroid-xu4-audio" }, 268 { }, 269 }; 270 MODULE_DEVICE_TABLE(of, odroid_audio_of_match); 271 272 static struct platform_driver odroid_audio_driver = { 273 .driver = { 274 .name = "odroid-audio", 275 .of_match_table = odroid_audio_of_match, 276 .pm = &snd_soc_pm_ops, 277 }, 278 .probe = odroid_audio_probe, 279 .remove = odroid_audio_remove, 280 }; 281 module_platform_driver(odroid_audio_driver); 282 283 MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); 284 MODULE_DESCRIPTION("Odroid XU3/XU4 audio support"); 285 MODULE_LICENSE("GPL v2"); 286