1/* 2 * Copyright (C) 2014 Samsung Electronics Co., Ltd. 3 * 4 * This program is free software; you can redistribute it and/or modify it 5 * under the terms of the GNU General Public License as published by the 6 * Free Software Foundation; either version 2 of the License, or (at your 7 * option) any later version. 8 */ 9 10#include <linux/of.h> 11#include <linux/module.h> 12#include <sound/soc.h> 13#include <sound/pcm_params.h> 14#include "i2s.h" 15 16struct odroidx2_drv_data { 17 const struct snd_soc_dapm_widget *dapm_widgets; 18 unsigned int num_dapm_widgets; 19}; 20 21/* The I2S CDCLK output clock frequency for the MAX98090 codec */ 22#define MAX98090_MCLK 19200000 23 24static struct snd_soc_dai_link odroidx2_dai[]; 25 26static int odroidx2_late_probe(struct snd_soc_card *card) 27{ 28 struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai; 29 struct snd_soc_dai *cpu_dai = card->rtd[0].cpu_dai; 30 int ret; 31 32 ret = snd_soc_dai_set_sysclk(codec_dai, 0, MAX98090_MCLK, 33 SND_SOC_CLOCK_IN); 34 35 if (ret < 0 || of_find_property(odroidx2_dai[0].codec_of_node, 36 "clocks", NULL)) 37 return ret; 38 39 /* Set the cpu DAI configuration in order to use CDCLK */ 40 return snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_CDCLK, 41 0, SND_SOC_CLOCK_OUT); 42} 43 44static const struct snd_soc_dapm_widget odroidx2_dapm_widgets[] = { 45 SND_SOC_DAPM_HP("Headphone Jack", NULL), 46 SND_SOC_DAPM_MIC("Mic Jack", NULL), 47 SND_SOC_DAPM_MIC("DMIC", NULL), 48}; 49 50static const struct snd_soc_dapm_widget odroidu3_dapm_widgets[] = { 51 SND_SOC_DAPM_HP("Headphone Jack", NULL), 52 SND_SOC_DAPM_SPK("Speakers", NULL), 53}; 54 55static struct snd_soc_dai_link odroidx2_dai[] = { 56 { 57 .name = "MAX98090", 58 .stream_name = "MAX98090 PCM", 59 .codec_dai_name = "HiFi", 60 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | 61 SND_SOC_DAIFMT_CBM_CFM, 62 } 63}; 64 65static struct snd_soc_card odroidx2 = { 66 .owner = THIS_MODULE, 67 .dai_link = odroidx2_dai, 68 .num_links = ARRAY_SIZE(odroidx2_dai), 69 .fully_routed = true, 70 .late_probe = odroidx2_late_probe, 71}; 72 73static const struct odroidx2_drv_data odroidx2_drvdata = { 74 .dapm_widgets = odroidx2_dapm_widgets, 75 .num_dapm_widgets = ARRAY_SIZE(odroidx2_dapm_widgets), 76}; 77 78static const struct odroidx2_drv_data odroidu3_drvdata = { 79 .dapm_widgets = odroidu3_dapm_widgets, 80 .num_dapm_widgets = ARRAY_SIZE(odroidu3_dapm_widgets), 81}; 82 83static const struct of_device_id odroidx2_audio_of_match[] = { 84 { 85 .compatible = "samsung,odroidx2-audio", 86 .data = &odroidx2_drvdata, 87 }, { 88 .compatible = "samsung,odroidu3-audio", 89 .data = &odroidu3_drvdata, 90 }, 91 { }, 92}; 93MODULE_DEVICE_TABLE(of, odroidx2_audio_of_match); 94 95static int odroidx2_audio_probe(struct platform_device *pdev) 96{ 97 struct device_node *snd_node = pdev->dev.of_node; 98 struct snd_soc_card *card = &odroidx2; 99 struct device_node *i2s_node, *codec_node; 100 struct odroidx2_drv_data *dd; 101 const struct of_device_id *of_id; 102 int ret; 103 104 of_id = of_match_node(odroidx2_audio_of_match, snd_node); 105 dd = (struct odroidx2_drv_data *)of_id->data; 106 107 card->num_dapm_widgets = dd->num_dapm_widgets; 108 card->dapm_widgets = dd->dapm_widgets; 109 110 card->dev = &pdev->dev; 111 112 ret = snd_soc_of_parse_card_name(card, "samsung,model"); 113 if (ret < 0) 114 return ret; 115 116 ret = snd_soc_of_parse_audio_routing(card, "samsung,audio-routing"); 117 if (ret < 0) 118 return ret; 119 120 codec_node = of_parse_phandle(snd_node, "samsung,audio-codec", 0); 121 if (!codec_node) { 122 dev_err(&pdev->dev, 123 "Failed parsing samsung,i2s-codec property\n"); 124 return -EINVAL; 125 } 126 127 i2s_node = of_parse_phandle(snd_node, "samsung,i2s-controller", 0); 128 if (!i2s_node) { 129 dev_err(&pdev->dev, 130 "Failed parsing samsung,i2s-controller property\n"); 131 ret = -EINVAL; 132 goto err_put_codec_n; 133 } 134 135 odroidx2_dai[0].codec_of_node = codec_node; 136 odroidx2_dai[0].cpu_of_node = i2s_node; 137 odroidx2_dai[0].platform_of_node = i2s_node; 138 139 ret = snd_soc_register_card(card); 140 if (ret) { 141 dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", 142 ret); 143 goto err_put_i2s_n; 144 } 145 return 0; 146 147err_put_i2s_n: 148 of_node_put(i2s_node); 149err_put_codec_n: 150 of_node_put(codec_node); 151 return ret; 152} 153 154static int odroidx2_audio_remove(struct platform_device *pdev) 155{ 156 struct snd_soc_card *card = platform_get_drvdata(pdev); 157 158 snd_soc_unregister_card(card); 159 160 of_node_put(odroidx2_dai[0].cpu_of_node); 161 of_node_put(odroidx2_dai[0].codec_of_node); 162 163 return 0; 164} 165 166static struct platform_driver odroidx2_audio_driver = { 167 .driver = { 168 .name = "odroidx2-audio", 169 .of_match_table = odroidx2_audio_of_match, 170 .pm = &snd_soc_pm_ops, 171 }, 172 .probe = odroidx2_audio_probe, 173 .remove = odroidx2_audio_remove, 174}; 175module_platform_driver(odroidx2_audio_driver); 176 177MODULE_AUTHOR("Chen Zhen <zhen1.chen@samsung.com>"); 178MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); 179MODULE_DESCRIPTION("ALSA SoC Odroid X2/U3 Audio Support"); 180MODULE_LICENSE("GPL v2"); 181