11896b485SSylwester Nawrocki // SPDX-License-Identifier: GPL-2.0 21896b485SSylwester Nawrocki // 31896b485SSylwester Nawrocki // Copyright (C) 2017 Samsung Electronics Co., Ltd. 4aba611fcSSylwester Nawrocki 5aba611fcSSylwester Nawrocki #include <linux/clk.h> 6bc3cf17bSSylwester Nawrocki #include <linux/clk-provider.h> 7aba611fcSSylwester Nawrocki #include <linux/of.h> 8aba611fcSSylwester Nawrocki #include <linux/of_device.h> 9aba611fcSSylwester Nawrocki #include <linux/module.h> 10aba611fcSSylwester Nawrocki #include <sound/soc.h> 11aba611fcSSylwester Nawrocki #include <sound/pcm_params.h> 12aba611fcSSylwester Nawrocki #include "i2s.h" 13aba611fcSSylwester Nawrocki #include "i2s-regs.h" 14aba611fcSSylwester Nawrocki 15aba611fcSSylwester Nawrocki struct odroid_priv { 16aba611fcSSylwester Nawrocki struct snd_soc_card card; 17a8ad0c85SSylwester Nawrocki struct clk *clk_i2s_bus; 18a8ad0c85SSylwester Nawrocki struct clk *sclk_i2s; 19b5c16a24SSylwester Nawrocki 20b5c16a24SSylwester Nawrocki /* Spinlock protecting fields below */ 21b5c16a24SSylwester Nawrocki spinlock_t lock; 22b5c16a24SSylwester Nawrocki unsigned int be_sample_rate; 23b5c16a24SSylwester Nawrocki bool be_active; 24aba611fcSSylwester Nawrocki }; 25aba611fcSSylwester Nawrocki 26bc3cf17bSSylwester Nawrocki static int odroid_card_fe_startup(struct snd_pcm_substream *substream) 27aba611fcSSylwester Nawrocki { 28aba611fcSSylwester Nawrocki struct snd_pcm_runtime *runtime = substream->runtime; 29aba611fcSSylwester Nawrocki 30aba611fcSSylwester Nawrocki snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2); 31bc3cf17bSSylwester Nawrocki 32aba611fcSSylwester Nawrocki return 0; 33aba611fcSSylwester Nawrocki } 34aba611fcSSylwester Nawrocki 35b5c16a24SSylwester Nawrocki static int odroid_card_fe_hw_params(struct snd_pcm_substream *substream, 36b5c16a24SSylwester Nawrocki struct snd_pcm_hw_params *params) 37b5c16a24SSylwester Nawrocki { 38b5c16a24SSylwester Nawrocki struct snd_soc_pcm_runtime *rtd = substream->private_data; 39b5c16a24SSylwester Nawrocki struct odroid_priv *priv = snd_soc_card_get_drvdata(rtd->card); 40b5c16a24SSylwester Nawrocki unsigned long flags; 41b5c16a24SSylwester Nawrocki int ret = 0; 42b5c16a24SSylwester Nawrocki 43b5c16a24SSylwester Nawrocki spin_lock_irqsave(&priv->lock, flags); 44b5c16a24SSylwester Nawrocki if (priv->be_active && priv->be_sample_rate != params_rate(params)) 45b5c16a24SSylwester Nawrocki ret = -EINVAL; 46b5c16a24SSylwester Nawrocki spin_unlock_irqrestore(&priv->lock, flags); 47b5c16a24SSylwester Nawrocki 48b5c16a24SSylwester Nawrocki return ret; 49b5c16a24SSylwester Nawrocki } 50b5c16a24SSylwester Nawrocki 51bc3cf17bSSylwester Nawrocki static const struct snd_soc_ops odroid_card_fe_ops = { 52bc3cf17bSSylwester Nawrocki .startup = odroid_card_fe_startup, 53b5c16a24SSylwester Nawrocki .hw_params = odroid_card_fe_hw_params, 54bc3cf17bSSylwester Nawrocki }; 55bc3cf17bSSylwester Nawrocki 56bc3cf17bSSylwester Nawrocki static int odroid_card_be_hw_params(struct snd_pcm_substream *substream, 57aba611fcSSylwester Nawrocki struct snd_pcm_hw_params *params) 58aba611fcSSylwester Nawrocki { 59aba611fcSSylwester Nawrocki struct snd_soc_pcm_runtime *rtd = substream->private_data; 60aba611fcSSylwester Nawrocki struct odroid_priv *priv = snd_soc_card_get_drvdata(rtd->card); 611d22c337SSylwester Nawrocki unsigned int pll_freq, rclk_freq, rfs; 62b5c16a24SSylwester Nawrocki unsigned long flags; 63aba611fcSSylwester Nawrocki int ret; 64aba611fcSSylwester Nawrocki 65aba611fcSSylwester Nawrocki switch (params_rate(params)) { 66aba611fcSSylwester Nawrocki case 64000: 671d22c337SSylwester Nawrocki pll_freq = 196608001U; 681d22c337SSylwester Nawrocki rfs = 384; 69aba611fcSSylwester Nawrocki break; 70aba611fcSSylwester Nawrocki case 44100: 71aba611fcSSylwester Nawrocki case 88200: 729d154e42SSylwester Nawrocki pll_freq = 180633609U; 731d22c337SSylwester Nawrocki rfs = 512; 74aba611fcSSylwester Nawrocki break; 751d22c337SSylwester Nawrocki case 32000: 76aba611fcSSylwester Nawrocki case 48000: 77aba611fcSSylwester Nawrocki case 96000: 789d154e42SSylwester Nawrocki pll_freq = 196608001U; 791d22c337SSylwester Nawrocki rfs = 512; 80aba611fcSSylwester Nawrocki break; 81aba611fcSSylwester Nawrocki default: 82aba611fcSSylwester Nawrocki return -EINVAL; 83aba611fcSSylwester Nawrocki } 84aba611fcSSylwester Nawrocki 85a8ad0c85SSylwester Nawrocki ret = clk_set_rate(priv->clk_i2s_bus, pll_freq / 2 + 1); 86aba611fcSSylwester Nawrocki if (ret < 0) 87aba611fcSSylwester Nawrocki return ret; 88aba611fcSSylwester Nawrocki 89a8ad0c85SSylwester Nawrocki /* 902b13bee3SSylwester Nawrocki * We add 2 to the rclk_freq value in order to avoid too low clock 91a8ad0c85SSylwester Nawrocki * frequency values due to the EPLL output frequency not being exact 92a8ad0c85SSylwester Nawrocki * multiple of the audio sampling rate. 93a8ad0c85SSylwester Nawrocki */ 942b13bee3SSylwester Nawrocki rclk_freq = params_rate(params) * rfs + 2; 95aba611fcSSylwester Nawrocki 96a8ad0c85SSylwester Nawrocki ret = clk_set_rate(priv->sclk_i2s, rclk_freq); 97aba611fcSSylwester Nawrocki if (ret < 0) 98aba611fcSSylwester Nawrocki return ret; 99aba611fcSSylwester Nawrocki 100aba611fcSSylwester Nawrocki if (rtd->num_codecs > 1) { 101aba611fcSSylwester Nawrocki struct snd_soc_dai *codec_dai = rtd->codec_dais[1]; 102aba611fcSSylwester Nawrocki 103aba611fcSSylwester Nawrocki ret = snd_soc_dai_set_sysclk(codec_dai, 0, rclk_freq, 104aba611fcSSylwester Nawrocki SND_SOC_CLOCK_IN); 105aba611fcSSylwester Nawrocki if (ret < 0) 106aba611fcSSylwester Nawrocki return ret; 107aba611fcSSylwester Nawrocki } 108aba611fcSSylwester Nawrocki 109b5c16a24SSylwester Nawrocki spin_lock_irqsave(&priv->lock, flags); 110b5c16a24SSylwester Nawrocki priv->be_sample_rate = params_rate(params); 111b5c16a24SSylwester Nawrocki spin_unlock_irqrestore(&priv->lock, flags); 112b5c16a24SSylwester Nawrocki 113b5c16a24SSylwester Nawrocki return 0; 114b5c16a24SSylwester Nawrocki } 115b5c16a24SSylwester Nawrocki 116b5c16a24SSylwester Nawrocki static int odroid_card_be_trigger(struct snd_pcm_substream *substream, int cmd) 117b5c16a24SSylwester Nawrocki { 118b5c16a24SSylwester Nawrocki struct snd_soc_pcm_runtime *rtd = substream->private_data; 119b5c16a24SSylwester Nawrocki struct odroid_priv *priv = snd_soc_card_get_drvdata(rtd->card); 120b5c16a24SSylwester Nawrocki unsigned long flags; 121b5c16a24SSylwester Nawrocki 122b5c16a24SSylwester Nawrocki spin_lock_irqsave(&priv->lock, flags); 123b5c16a24SSylwester Nawrocki 124b5c16a24SSylwester Nawrocki switch (cmd) { 125b5c16a24SSylwester Nawrocki case SNDRV_PCM_TRIGGER_START: 126b5c16a24SSylwester Nawrocki case SNDRV_PCM_TRIGGER_RESUME: 127b5c16a24SSylwester Nawrocki case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 128b5c16a24SSylwester Nawrocki priv->be_active = true; 129b5c16a24SSylwester Nawrocki break; 130b5c16a24SSylwester Nawrocki 131b5c16a24SSylwester Nawrocki case SNDRV_PCM_TRIGGER_STOP: 132b5c16a24SSylwester Nawrocki case SNDRV_PCM_TRIGGER_SUSPEND: 133b5c16a24SSylwester Nawrocki case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 134b5c16a24SSylwester Nawrocki priv->be_active = false; 135b5c16a24SSylwester Nawrocki break; 136b5c16a24SSylwester Nawrocki } 137b5c16a24SSylwester Nawrocki 138b5c16a24SSylwester Nawrocki spin_unlock_irqrestore(&priv->lock, flags); 139b5c16a24SSylwester Nawrocki 140aba611fcSSylwester Nawrocki return 0; 141aba611fcSSylwester Nawrocki } 142aba611fcSSylwester Nawrocki 143bc3cf17bSSylwester Nawrocki static const struct snd_soc_ops odroid_card_be_ops = { 144bc3cf17bSSylwester Nawrocki .hw_params = odroid_card_be_hw_params, 145b5c16a24SSylwester Nawrocki .trigger = odroid_card_be_trigger, 146bc3cf17bSSylwester Nawrocki }; 147bc3cf17bSSylwester Nawrocki 148f89aea0fSSylwester Nawrocki /* DAPM routes for backward compatibility with old DTS */ 149f89aea0fSSylwester Nawrocki static const struct snd_soc_dapm_route odroid_dapm_routes[] = { 150f89aea0fSSylwester Nawrocki { "I2S Playback", NULL, "Mixer DAI TX" }, 151f89aea0fSSylwester Nawrocki { "HiFi Playback", NULL, "Mixer DAI TX" }, 152f89aea0fSSylwester Nawrocki }; 153f89aea0fSSylwester Nawrocki 1543cddda10SKuninori Morimoto SND_SOC_DAILINK_DEFS(primary, 1553cddda10SKuninori Morimoto DAILINK_COMP_ARRAY(COMP_EMPTY()), 1563cddda10SKuninori Morimoto DAILINK_COMP_ARRAY(COMP_DUMMY()), 1573cddda10SKuninori Morimoto DAILINK_COMP_ARRAY(COMP_PLATFORM("3830000.i2s"))); 1583cddda10SKuninori Morimoto 1593cddda10SKuninori Morimoto SND_SOC_DAILINK_DEFS(mixer, 1603cddda10SKuninori Morimoto DAILINK_COMP_ARRAY(COMP_DUMMY()), 1613cddda10SKuninori Morimoto DAILINK_COMP_ARRAY(COMP_EMPTY()), 1623cddda10SKuninori Morimoto DAILINK_COMP_ARRAY(COMP_DUMMY())); 1633cddda10SKuninori Morimoto 1643cddda10SKuninori Morimoto SND_SOC_DAILINK_DEFS(secondary, 1653cddda10SKuninori Morimoto DAILINK_COMP_ARRAY(COMP_EMPTY()), 1663cddda10SKuninori Morimoto DAILINK_COMP_ARRAY(COMP_DUMMY()), 1673cddda10SKuninori Morimoto DAILINK_COMP_ARRAY(COMP_PLATFORM("3830000.i2s-sec"))); 1683cddda10SKuninori Morimoto 169bc3cf17bSSylwester Nawrocki static struct snd_soc_dai_link odroid_card_dais[] = { 170bc3cf17bSSylwester Nawrocki { 171bc3cf17bSSylwester Nawrocki /* Primary FE <-> BE link */ 172bc3cf17bSSylwester Nawrocki .ops = &odroid_card_fe_ops, 173bc3cf17bSSylwester Nawrocki .name = "Primary", 174bc3cf17bSSylwester Nawrocki .stream_name = "Primary", 175bc3cf17bSSylwester Nawrocki .dynamic = 1, 176bc3cf17bSSylwester Nawrocki .dpcm_playback = 1, 1773cddda10SKuninori Morimoto SND_SOC_DAILINK_REG(primary), 178bc3cf17bSSylwester Nawrocki }, { 179bc3cf17bSSylwester Nawrocki /* BE <-> CODECs link */ 180bc3cf17bSSylwester Nawrocki .name = "I2S Mixer", 181bc3cf17bSSylwester Nawrocki .ops = &odroid_card_be_ops, 182bc3cf17bSSylwester Nawrocki .no_pcm = 1, 183bc3cf17bSSylwester Nawrocki .dpcm_playback = 1, 184bc3cf17bSSylwester Nawrocki .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 185bc3cf17bSSylwester Nawrocki SND_SOC_DAIFMT_CBS_CFS, 1863cddda10SKuninori Morimoto SND_SOC_DAILINK_REG(mixer), 187bc3cf17bSSylwester Nawrocki }, { 188bc3cf17bSSylwester Nawrocki /* Secondary FE <-> BE link */ 189bc3cf17bSSylwester Nawrocki .playback_only = 1, 190bc3cf17bSSylwester Nawrocki .ops = &odroid_card_fe_ops, 191bc3cf17bSSylwester Nawrocki .name = "Secondary", 192bc3cf17bSSylwester Nawrocki .stream_name = "Secondary", 193bc3cf17bSSylwester Nawrocki .dynamic = 1, 194bc3cf17bSSylwester Nawrocki .dpcm_playback = 1, 1953cddda10SKuninori Morimoto SND_SOC_DAILINK_REG(secondary), 196bc3cf17bSSylwester Nawrocki } 197aba611fcSSylwester Nawrocki }; 198aba611fcSSylwester Nawrocki 199aba611fcSSylwester Nawrocki static int odroid_audio_probe(struct platform_device *pdev) 200aba611fcSSylwester Nawrocki { 201aba611fcSSylwester Nawrocki struct device *dev = &pdev->dev; 2023af81600SSylwester Nawrocki struct device_node *cpu_dai = NULL; 2033af81600SSylwester Nawrocki struct device_node *cpu, *codec; 204aba611fcSSylwester Nawrocki struct odroid_priv *priv; 205aba611fcSSylwester Nawrocki struct snd_soc_card *card; 206bc3cf17bSSylwester Nawrocki struct snd_soc_dai_link *link, *codec_link; 207bc3cf17bSSylwester Nawrocki int num_pcms, ret, i; 208bc3cf17bSSylwester Nawrocki struct of_phandle_args args = {}; 209aba611fcSSylwester Nawrocki 210aba611fcSSylwester Nawrocki priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 211aba611fcSSylwester Nawrocki if (!priv) 212aba611fcSSylwester Nawrocki return -ENOMEM; 213aba611fcSSylwester Nawrocki 214aba611fcSSylwester Nawrocki card = &priv->card; 215aba611fcSSylwester Nawrocki card->dev = dev; 216aba611fcSSylwester Nawrocki 217aba611fcSSylwester Nawrocki card->owner = THIS_MODULE; 218aba611fcSSylwester Nawrocki card->fully_routed = true; 219aba611fcSSylwester Nawrocki 220b5c16a24SSylwester Nawrocki spin_lock_init(&priv->lock); 221aba611fcSSylwester Nawrocki snd_soc_card_set_drvdata(card, priv); 222aba611fcSSylwester Nawrocki 223aba611fcSSylwester Nawrocki ret = snd_soc_of_parse_card_name(card, "model"); 224aba611fcSSylwester Nawrocki if (ret < 0) 225aba611fcSSylwester Nawrocki return ret; 226aba611fcSSylwester Nawrocki 227aba611fcSSylwester Nawrocki if (of_property_read_bool(dev->of_node, "samsung,audio-widgets")) { 228aba611fcSSylwester Nawrocki ret = snd_soc_of_parse_audio_simple_widgets(card, 229aba611fcSSylwester Nawrocki "samsung,audio-widgets"); 230aba611fcSSylwester Nawrocki if (ret < 0) 231aba611fcSSylwester Nawrocki return ret; 232aba611fcSSylwester Nawrocki } 233aba611fcSSylwester Nawrocki 234aba611fcSSylwester Nawrocki if (of_property_read_bool(dev->of_node, "samsung,audio-routing")) { 235aba611fcSSylwester Nawrocki ret = snd_soc_of_parse_audio_routing(card, 236aba611fcSSylwester Nawrocki "samsung,audio-routing"); 237aba611fcSSylwester Nawrocki if (ret < 0) 238aba611fcSSylwester Nawrocki return ret; 239aba611fcSSylwester Nawrocki } 240aba611fcSSylwester Nawrocki 241bc3cf17bSSylwester Nawrocki card->dai_link = odroid_card_dais; 242bc3cf17bSSylwester Nawrocki card->num_links = ARRAY_SIZE(odroid_card_dais); 243aba611fcSSylwester Nawrocki 244aba611fcSSylwester Nawrocki cpu = of_get_child_by_name(dev->of_node, "cpu"); 245aba611fcSSylwester Nawrocki codec = of_get_child_by_name(dev->of_node, "codec"); 246bc3cf17bSSylwester Nawrocki link = card->dai_link; 247bc3cf17bSSylwester Nawrocki codec_link = &card->dai_link[1]; 248aba611fcSSylwester Nawrocki 249bc3cf17bSSylwester Nawrocki /* 250bc3cf17bSSylwester Nawrocki * For backwards compatibility create the secondary CPU DAI link only 251bc3cf17bSSylwester Nawrocki * if there are 2 CPU DAI entries in the cpu sound-dai property in DT. 252f89aea0fSSylwester Nawrocki * Also add required DAPM routes not available in old DTS. 253bc3cf17bSSylwester Nawrocki */ 254bc3cf17bSSylwester Nawrocki num_pcms = of_count_phandle_with_args(cpu, "sound-dai", 255bc3cf17bSSylwester Nawrocki "#sound-dai-cells"); 256f89aea0fSSylwester Nawrocki if (num_pcms == 1) { 257f89aea0fSSylwester Nawrocki card->dapm_routes = odroid_dapm_routes; 258f89aea0fSSylwester Nawrocki card->num_dapm_routes = ARRAY_SIZE(odroid_dapm_routes); 259bc3cf17bSSylwester Nawrocki card->num_links--; 260f89aea0fSSylwester Nawrocki } 261bc3cf17bSSylwester Nawrocki 262bc3cf17bSSylwester Nawrocki for (i = 0; i < num_pcms; i++, link += 2) { 263bc3cf17bSSylwester Nawrocki ret = of_parse_phandle_with_args(cpu, "sound-dai", 264bc3cf17bSSylwester Nawrocki "#sound-dai-cells", i, &args); 265bc3cf17bSSylwester Nawrocki if (ret < 0) 266d832d2b2SSylwester Nawrocki break; 267bc3cf17bSSylwester Nawrocki 268bc3cf17bSSylwester Nawrocki if (!args.np) { 269bc3cf17bSSylwester Nawrocki dev_err(dev, "sound-dai property parse error: %d\n", ret); 270d832d2b2SSylwester Nawrocki ret = -EINVAL; 271d832d2b2SSylwester Nawrocki break; 272aba611fcSSylwester Nawrocki } 273aba611fcSSylwester Nawrocki 2743cddda10SKuninori Morimoto ret = snd_soc_get_dai_name(&args, &link->cpus->dai_name); 275bc3cf17bSSylwester Nawrocki of_node_put(args.np); 276bc3cf17bSSylwester Nawrocki 277bc3cf17bSSylwester Nawrocki if (ret < 0) 278d832d2b2SSylwester Nawrocki break; 279bc3cf17bSSylwester Nawrocki } 2803af81600SSylwester Nawrocki if (ret == 0) { 281bc3cf17bSSylwester Nawrocki cpu_dai = of_parse_phandle(cpu, "sound-dai", 0); 2823af81600SSylwester Nawrocki if (!cpu_dai) 2833af81600SSylwester Nawrocki ret = -EINVAL; 2843af81600SSylwester Nawrocki } 285d832d2b2SSylwester Nawrocki 286bc3cf17bSSylwester Nawrocki of_node_put(cpu); 287d832d2b2SSylwester Nawrocki if (ret < 0) 2889b6d104aSWen Yang goto err_put_node; 289bc3cf17bSSylwester Nawrocki 290bc3cf17bSSylwester Nawrocki ret = snd_soc_of_get_dai_link_codecs(dev, codec, codec_link); 291aba611fcSSylwester Nawrocki if (ret < 0) 292d832d2b2SSylwester Nawrocki goto err_put_cpu_dai; 293aba611fcSSylwester Nawrocki 294bc3cf17bSSylwester Nawrocki /* Set capture capability only for boards with the MAX98090 CODEC */ 295bc3cf17bSSylwester Nawrocki if (codec_link->num_codecs > 1) { 296bc3cf17bSSylwester Nawrocki card->dai_link[0].dpcm_capture = 1; 297bc3cf17bSSylwester Nawrocki card->dai_link[1].dpcm_capture = 1; 298a8ad0c85SSylwester Nawrocki } 299a8ad0c85SSylwester Nawrocki 300bc3cf17bSSylwester Nawrocki priv->sclk_i2s = of_clk_get_by_name(cpu_dai, "i2s_opclk1"); 301bc3cf17bSSylwester Nawrocki if (IS_ERR(priv->sclk_i2s)) { 302bc3cf17bSSylwester Nawrocki ret = PTR_ERR(priv->sclk_i2s); 303d832d2b2SSylwester Nawrocki goto err_put_cpu_dai; 304bc3cf17bSSylwester Nawrocki } 305bc3cf17bSSylwester Nawrocki 306bc3cf17bSSylwester Nawrocki priv->clk_i2s_bus = of_clk_get_by_name(cpu_dai, "iis"); 307a8ad0c85SSylwester Nawrocki if (IS_ERR(priv->clk_i2s_bus)) { 308a8ad0c85SSylwester Nawrocki ret = PTR_ERR(priv->clk_i2s_bus); 309a8ad0c85SSylwester Nawrocki goto err_put_sclk; 310a8ad0c85SSylwester Nawrocki } 311bc3cf17bSSylwester Nawrocki of_node_put(cpu_dai); 312a8ad0c85SSylwester Nawrocki 313aba611fcSSylwester Nawrocki ret = devm_snd_soc_register_card(dev, card); 314aba611fcSSylwester Nawrocki if (ret < 0) { 315aba611fcSSylwester Nawrocki dev_err(dev, "snd_soc_register_card() failed: %d\n", ret); 316a8ad0c85SSylwester Nawrocki goto err_put_clk_i2s; 317aba611fcSSylwester Nawrocki } 318aba611fcSSylwester Nawrocki 3199b6d104aSWen Yang of_node_put(codec); 320aba611fcSSylwester Nawrocki return 0; 321aba611fcSSylwester Nawrocki 322a8ad0c85SSylwester Nawrocki err_put_clk_i2s: 323a8ad0c85SSylwester Nawrocki clk_put(priv->clk_i2s_bus); 324a8ad0c85SSylwester Nawrocki err_put_sclk: 325a8ad0c85SSylwester Nawrocki clk_put(priv->sclk_i2s); 326d832d2b2SSylwester Nawrocki err_put_cpu_dai: 327d832d2b2SSylwester Nawrocki of_node_put(cpu_dai); 328bc3cf17bSSylwester Nawrocki snd_soc_of_put_dai_link_codecs(codec_link); 3299b6d104aSWen Yang err_put_node: 3309b6d104aSWen Yang of_node_put(codec); 331aba611fcSSylwester Nawrocki return ret; 332aba611fcSSylwester Nawrocki } 333aba611fcSSylwester Nawrocki 334aba611fcSSylwester Nawrocki static int odroid_audio_remove(struct platform_device *pdev) 335aba611fcSSylwester Nawrocki { 336aba611fcSSylwester Nawrocki struct odroid_priv *priv = platform_get_drvdata(pdev); 337aba611fcSSylwester Nawrocki 338bc3cf17bSSylwester Nawrocki snd_soc_of_put_dai_link_codecs(&priv->card.dai_link[1]); 339a8ad0c85SSylwester Nawrocki clk_put(priv->sclk_i2s); 340a8ad0c85SSylwester Nawrocki clk_put(priv->clk_i2s_bus); 341aba611fcSSylwester Nawrocki 342aba611fcSSylwester Nawrocki return 0; 343aba611fcSSylwester Nawrocki } 344aba611fcSSylwester Nawrocki 345aba611fcSSylwester Nawrocki static const struct of_device_id odroid_audio_of_match[] = { 346d9e57512SSylwester Nawrocki { .compatible = "hardkernel,odroid-xu3-audio" }, 347d9e57512SSylwester Nawrocki { .compatible = "hardkernel,odroid-xu4-audio" }, 348aba611fcSSylwester Nawrocki { .compatible = "samsung,odroid-xu3-audio" }, 349aba611fcSSylwester Nawrocki { .compatible = "samsung,odroid-xu4-audio" }, 350aba611fcSSylwester Nawrocki { }, 351aba611fcSSylwester Nawrocki }; 352aba611fcSSylwester Nawrocki MODULE_DEVICE_TABLE(of, odroid_audio_of_match); 353aba611fcSSylwester Nawrocki 354aba611fcSSylwester Nawrocki static struct platform_driver odroid_audio_driver = { 355aba611fcSSylwester Nawrocki .driver = { 356aba611fcSSylwester Nawrocki .name = "odroid-audio", 357aba611fcSSylwester Nawrocki .of_match_table = odroid_audio_of_match, 358aba611fcSSylwester Nawrocki .pm = &snd_soc_pm_ops, 359aba611fcSSylwester Nawrocki }, 360aba611fcSSylwester Nawrocki .probe = odroid_audio_probe, 361aba611fcSSylwester Nawrocki .remove = odroid_audio_remove, 362aba611fcSSylwester Nawrocki }; 363aba611fcSSylwester Nawrocki module_platform_driver(odroid_audio_driver); 364aba611fcSSylwester Nawrocki 365aba611fcSSylwester Nawrocki MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); 366aba611fcSSylwester Nawrocki MODULE_DESCRIPTION("Odroid XU3/XU4 audio support"); 367aba611fcSSylwester Nawrocki MODULE_LICENSE("GPL v2"); 368