0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019 #include <linux/module.h>
0020 #include <linux/moduleparam.h>
0021 #include <linux/kernel.h>
0022 #include <linux/clk.h>
0023 #include <linux/timer.h>
0024 #include <linux/interrupt.h>
0025 #include <linux/platform_device.h>
0026 #include <linux/i2c.h>
0027 #include <linux/of.h>
0028
0029 #include <linux/atmel-ssc.h>
0030
0031 #include <sound/core.h>
0032 #include <sound/pcm.h>
0033 #include <sound/pcm_params.h>
0034 #include <sound/soc.h>
0035
0036 #include "../codecs/wm8731.h"
0037 #include "atmel-pcm.h"
0038 #include "atmel_ssc_dai.h"
0039
0040 #define MCLK_RATE 12000000
0041
0042
0043
0044
0045
0046
0047 #undef ENABLE_MIC_INPUT
0048
0049 static const struct snd_soc_dapm_widget at91sam9g20ek_dapm_widgets[] = {
0050 SND_SOC_DAPM_MIC("Int Mic", NULL),
0051 SND_SOC_DAPM_SPK("Ext Spk", NULL),
0052 };
0053
0054 static const struct snd_soc_dapm_route intercon[] = {
0055
0056
0057 {"Ext Spk", NULL, "LHPOUT"},
0058 {"Ext Spk", NULL, "RHPOUT"},
0059
0060
0061 {"MICIN", NULL, "Mic Bias"},
0062 {"Mic Bias", NULL, "Int Mic"},
0063 };
0064
0065
0066
0067
0068 static int at91sam9g20ek_wm8731_init(struct snd_soc_pcm_runtime *rtd)
0069 {
0070 struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
0071 struct device *dev = rtd->dev;
0072 int ret;
0073
0074 dev_dbg(dev, "%s called\n", __func__);
0075
0076 ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_MCLK,
0077 MCLK_RATE, SND_SOC_CLOCK_IN);
0078 if (ret < 0) {
0079 dev_err(dev, "Failed to set WM8731 SYSCLK: %d\n", ret);
0080 return ret;
0081 }
0082
0083 #ifndef ENABLE_MIC_INPUT
0084 snd_soc_dapm_nc_pin(&rtd->card->dapm, "Int Mic");
0085 #endif
0086
0087 return 0;
0088 }
0089
0090 SND_SOC_DAILINK_DEFS(pcm,
0091 DAILINK_COMP_ARRAY(COMP_CPU("at91rm9200_ssc.0")),
0092 DAILINK_COMP_ARRAY(COMP_CODEC("wm8731.0-001b", "wm8731-hifi")),
0093 DAILINK_COMP_ARRAY(COMP_PLATFORM("at91rm9200_ssc.0")));
0094
0095 static struct snd_soc_dai_link at91sam9g20ek_dai = {
0096 .name = "WM8731",
0097 .stream_name = "WM8731 PCM",
0098 .init = at91sam9g20ek_wm8731_init,
0099 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
0100 SND_SOC_DAIFMT_CBP_CFP,
0101 SND_SOC_DAILINK_REG(pcm),
0102 };
0103
0104 static struct snd_soc_card snd_soc_at91sam9g20ek = {
0105 .name = "AT91SAMG20-EK",
0106 .owner = THIS_MODULE,
0107 .dai_link = &at91sam9g20ek_dai,
0108 .num_links = 1,
0109
0110 .dapm_widgets = at91sam9g20ek_dapm_widgets,
0111 .num_dapm_widgets = ARRAY_SIZE(at91sam9g20ek_dapm_widgets),
0112 .dapm_routes = intercon,
0113 .num_dapm_routes = ARRAY_SIZE(intercon),
0114 .fully_routed = true,
0115 };
0116
0117 static int at91sam9g20ek_audio_probe(struct platform_device *pdev)
0118 {
0119 struct device_node *np = pdev->dev.of_node;
0120 struct device_node *codec_np, *cpu_np;
0121 struct snd_soc_card *card = &snd_soc_at91sam9g20ek;
0122 int ret;
0123
0124 if (!np) {
0125 return -ENODEV;
0126 }
0127
0128 ret = atmel_ssc_set_audio(0);
0129 if (ret) {
0130 dev_err(&pdev->dev, "ssc channel is not valid: %d\n", ret);
0131 return ret;
0132 }
0133
0134 card->dev = &pdev->dev;
0135
0136
0137 ret = snd_soc_of_parse_card_name(card, "atmel,model");
0138 if (ret)
0139 goto err;
0140
0141 ret = snd_soc_of_parse_audio_routing(card,
0142 "atmel,audio-routing");
0143 if (ret)
0144 goto err;
0145
0146
0147 at91sam9g20ek_dai.codecs->name = NULL;
0148 codec_np = of_parse_phandle(np, "atmel,audio-codec", 0);
0149 if (!codec_np) {
0150 dev_err(&pdev->dev, "codec info missing\n");
0151 ret = -EINVAL;
0152 goto err;
0153 }
0154 at91sam9g20ek_dai.codecs->of_node = codec_np;
0155
0156
0157 at91sam9g20ek_dai.cpus->dai_name = NULL;
0158 at91sam9g20ek_dai.platforms->name = NULL;
0159 cpu_np = of_parse_phandle(np, "atmel,ssc-controller", 0);
0160 if (!cpu_np) {
0161 dev_err(&pdev->dev, "dai and pcm info missing\n");
0162 of_node_put(codec_np);
0163 ret = -EINVAL;
0164 goto err;
0165 }
0166 at91sam9g20ek_dai.cpus->of_node = cpu_np;
0167 at91sam9g20ek_dai.platforms->of_node = cpu_np;
0168
0169 of_node_put(codec_np);
0170 of_node_put(cpu_np);
0171
0172 ret = snd_soc_register_card(card);
0173 if (ret) {
0174 dev_err_probe(&pdev->dev, ret,
0175 "snd_soc_register_card() failed: %d\n", ret);
0176 goto err;
0177 }
0178
0179 return 0;
0180
0181 err:
0182 atmel_ssc_put_audio(0);
0183 return ret;
0184 }
0185
0186 static int at91sam9g20ek_audio_remove(struct platform_device *pdev)
0187 {
0188 struct snd_soc_card *card = platform_get_drvdata(pdev);
0189
0190 snd_soc_unregister_card(card);
0191 atmel_ssc_put_audio(0);
0192
0193 return 0;
0194 }
0195
0196 #ifdef CONFIG_OF
0197 static const struct of_device_id at91sam9g20ek_wm8731_dt_ids[] = {
0198 { .compatible = "atmel,at91sam9g20ek-wm8731-audio", },
0199 { }
0200 };
0201 MODULE_DEVICE_TABLE(of, at91sam9g20ek_wm8731_dt_ids);
0202 #endif
0203
0204 static struct platform_driver at91sam9g20ek_audio_driver = {
0205 .driver = {
0206 .name = "at91sam9g20ek-audio",
0207 .of_match_table = of_match_ptr(at91sam9g20ek_wm8731_dt_ids),
0208 },
0209 .probe = at91sam9g20ek_audio_probe,
0210 .remove = at91sam9g20ek_audio_remove,
0211 };
0212
0213 module_platform_driver(at91sam9g20ek_audio_driver);
0214
0215
0216 MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>");
0217 MODULE_DESCRIPTION("ALSA SoC AT91SAM9G20EK_WM8731");
0218 MODULE_ALIAS("platform:at91sam9g20ek-audio");
0219 MODULE_LICENSE("GPL");