2 * ASoC driver for Ka-Ro electronics TX48 module
3 * (C) Copyright 2013 Lothar Waßmann <LW@KARO-electronics.de>
5 * based on: davinci-evm.c
6 * Author: Vladimir Barinov, <vbarinov@embeddedalley.com>
7 * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
14 #include <linux/module.h>
15 #include <linux/moduleparam.h>
16 #include <linux/timer.h>
17 #include <linux/interrupt.h>
18 #include <linux/of_platform.h>
19 #include <linux/clk.h>
20 #include <linux/i2c.h>
21 #include <linux/edma.h>
22 #include <sound/core.h>
23 #include <sound/pcm.h>
24 #include <sound/soc.h>
27 #include <asm/mach-types.h>
29 #include "../codecs/sgtl5000.h"
31 #include "davinci-pcm.h"
32 #include "davinci-i2s.h"
33 #include "davinci-mcasp.h"
35 struct am335x_tx48_drvdata {
40 static int am335x_tx48_startup(struct snd_pcm_substream *substream)
42 struct snd_soc_pcm_runtime *rtd = substream->private_data;
43 struct snd_soc_card *soc_card = rtd->card;
44 struct am335x_tx48_drvdata *drvdata =
45 snd_soc_card_get_drvdata(soc_card);
48 return clk_prepare_enable(drvdata->mclk);
53 static void am335x_tx48_shutdown(struct snd_pcm_substream *substream)
55 struct snd_soc_pcm_runtime *rtd = substream->private_data;
56 struct snd_soc_card *soc_card = rtd->card;
57 struct am335x_tx48_drvdata *drvdata =
58 snd_soc_card_get_drvdata(soc_card);
61 clk_disable_unprepare(drvdata->mclk);
64 static int sgtl5000_hw_params(struct snd_pcm_substream *substream,
65 struct snd_pcm_hw_params *params)
68 struct snd_soc_pcm_runtime *rtd = substream->private_data;
69 struct snd_soc_dai *codec_dai = rtd->codec_dai;
70 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
71 struct snd_soc_card *soc_card = rtd->codec->card;
72 struct am335x_tx48_drvdata *drvdata = snd_soc_card_get_drvdata(soc_card);
73 unsigned sysclk = drvdata->sysclk;
77 dev_err(rtd->dev->parent, "No CODEC DAI\n");
80 if (!codec_dai->driver) {
81 dev_err(rtd->dev->parent, "No CODEC DAI driver\n");
86 dev_err(rtd->dev->parent, "No CPU DAI\n");
89 if (!cpu_dai->driver) {
90 dev_err(rtd->dev->parent, "No CPU DAI driver\n");
94 dev_dbg(rtd->dev->parent, "%s: setting codec clock to %u.%03uMHz\n", __func__,
95 sysclk / 1000000, sysclk / 1000 % 1000);
96 /* Set SGTL5000's SYSCLK */
97 ret = snd_soc_dai_set_sysclk(codec_dai, SGTL5000_SYSCLK, sysclk, 0);
101 dev_dbg(rtd->dev->parent, "%s: setting mcasp clock to %u.%03uMHz\n", __func__,
102 sysclk / 1000000, sysclk / 1000 % 1000);
104 /* set codec to master mode */
105 dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
106 SND_SOC_DAIFMT_CBM_CFM;
108 /* set codec DAI configuration */
109 ret = snd_soc_dai_set_fmt(codec_dai, dai_format);
113 /* set cpu DAI configuration */
114 ret = snd_soc_dai_set_fmt(cpu_dai, dai_format);
121 static struct snd_soc_ops am335x_tx48_ops = {
122 .startup = am335x_tx48_startup,
123 .shutdown = am335x_tx48_shutdown,
124 .hw_params = sgtl5000_hw_params,
128 * The struct is used as place holder. It will be completely
129 * filled with data from dt node.
131 static struct snd_soc_dai_link am335x_tx48_dai = {
133 .stream_name = "SGTL5000",
134 .codec_dai_name = "sgtl5000",
135 .ops = &am335x_tx48_ops,
138 static struct snd_soc_card am335x_tx48_soc_card = {
139 .owner = THIS_MODULE,
140 .dai_link = &am335x_tx48_dai,
144 static const struct of_device_id am335x_tx48_dt_ids[] = {
145 { .compatible = "ti,am335x-tx48-audio", },
148 MODULE_DEVICE_TABLE(of, am335x_tx48_dt_ids);
150 static int am335x_tx48_probe(struct platform_device *pdev)
153 struct device_node *np = pdev->dev.of_node;
154 struct am335x_tx48_drvdata *drvdata;
155 struct device_node *codec_np;
156 struct device_node *mcasp_np;
157 struct platform_device *mcasp_pdev;
158 struct i2c_client *codec_dev;
161 codec_np = of_parse_phandle(np, "ti,audio-codec", 0);
163 dev_err(&pdev->dev, "codec handle missing in DT\n");
167 mcasp_np = of_parse_phandle(np, "ti,mcasp-controller", 0);
169 dev_err(&pdev->dev, "mcasp handle missing in DT\n");
174 codec_dev = of_find_i2c_device_by_node(codec_np);
176 dev_err(&pdev->dev, "failed to find codec platform device\n");
181 mcasp_pdev = of_find_device_by_node(mcasp_np);
183 dev_err(&pdev->dev, "failed to find MCASP platform device\n");
188 am335x_tx48_dai.codec_of_node = codec_np;
189 am335x_tx48_dai.cpu_of_node = mcasp_np;
190 am335x_tx48_dai.platform_of_node = mcasp_np;
192 am335x_tx48_soc_card.dev = &pdev->dev;
193 ret = snd_soc_of_parse_card_name(&am335x_tx48_soc_card, "ti,model");
197 mclk = devm_clk_get(&codec_dev->dev, NULL);
200 if (ret != -EPROBE_DEFER)
201 dev_err(&pdev->dev, "mclk not found: %d\n", ret);
205 drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
210 drvdata->mclk = mclk;
211 ret = of_property_read_u32(np, "ti,codec-clock-rate", &drvdata->sysclk);
213 if (!drvdata->mclk) {
215 "No clock or clock rate defined.\n");
219 drvdata->sysclk = clk_get_rate(drvdata->mclk);
220 } else if (drvdata->mclk) {
221 unsigned int requested_rate = drvdata->sysclk;
223 ret = clk_set_rate(drvdata->mclk, drvdata->sysclk);
225 dev_err(&pdev->dev, "Could not set mclk rate to %u\n",
229 drvdata->sysclk = clk_get_rate(drvdata->mclk);
230 if (drvdata->sysclk != requested_rate)
232 "Could not get requested rate %u using %u\n",
233 requested_rate, drvdata->sysclk);
236 snd_soc_card_set_drvdata(&am335x_tx48_soc_card, drvdata);
237 ret = devm_snd_soc_register_card(&pdev->dev, &am335x_tx48_soc_card);
239 dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
242 dev_dbg(&pdev->dev, "Soundcard %s registered\n",
243 am335x_tx48_soc_card.name);
247 of_node_put(mcasp_np);
250 of_node_put(codec_np);
254 static struct platform_driver am335x_tx48_driver = {
255 .probe = am335x_tx48_probe,
257 .name = "am335x_tx48",
258 .owner = THIS_MODULE,
259 .of_match_table = am335x_tx48_dt_ids,
262 module_platform_driver(am335x_tx48_driver);
264 MODULE_AUTHOR("Lothar Waßmann");
265 MODULE_DESCRIPTION("Ka-Ro TX48 ASoC driver");
266 MODULE_LICENSE("GPL");
267 MODULE_ALIAS("platform:am335x-tx48");