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.
15 #include <linux/module.h>
16 #include <linux/moduleparam.h>
17 #include <linux/timer.h>
18 #include <linux/interrupt.h>
19 #include <linux/of_platform.h>
20 #include <linux/clk.h>
21 #include <linux/i2c.h>
22 #include <linux/edma.h>
23 #include <sound/core.h>
24 #include <sound/pcm.h>
25 #include <sound/soc.h>
28 #include <asm/mach-types.h>
30 #include "../codecs/sgtl5000.h"
32 #include "davinci-pcm.h"
33 #include "davinci-i2s.h"
34 #include "davinci-mcasp.h"
36 struct am335x_tx48_drvdata {
41 static int am335x_tx48_startup(struct snd_pcm_substream *substream)
43 struct snd_soc_pcm_runtime *rtd = substream->private_data;
44 struct snd_soc_card *soc_card = rtd->card;
45 struct am335x_tx48_drvdata *drvdata =
46 snd_soc_card_get_drvdata(soc_card);
49 return clk_prepare_enable(drvdata->mclk);
54 static void am335x_tx48_shutdown(struct snd_pcm_substream *substream)
56 struct snd_soc_pcm_runtime *rtd = substream->private_data;
57 struct snd_soc_card *soc_card = rtd->card;
58 struct am335x_tx48_drvdata *drvdata =
59 snd_soc_card_get_drvdata(soc_card);
62 clk_disable_unprepare(drvdata->mclk);
65 static int sgtl5000_hw_params(struct snd_pcm_substream *substream,
66 struct snd_pcm_hw_params *params)
69 struct snd_soc_pcm_runtime *rtd = substream->private_data;
70 struct snd_soc_dai *codec_dai = rtd->codec_dai;
71 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
72 struct snd_soc_card *soc_card = rtd->codec->card;
73 struct am335x_tx48_drvdata *drvdata = snd_soc_card_get_drvdata(soc_card);
74 unsigned sysclk = drvdata->sysclk;
78 dev_err(rtd->dev->parent, "No CODEC DAI\n");
81 if (!codec_dai->driver) {
82 dev_err(rtd->dev->parent, "No CODEC DAI driver\n");
87 dev_err(rtd->dev->parent, "No CPU DAI\n");
90 if (!cpu_dai->driver) {
91 dev_err(rtd->dev->parent, "No CPU DAI driver\n");
95 dev_info(rtd->dev->parent, "%s: setting codec clock to %u.%03uMHz\n", __func__,
96 sysclk / 1000000, sysclk / 1000 % 1000);
97 /* Set SGTL5000's SYSCLK */
98 ret = snd_soc_dai_set_sysclk(codec_dai, SGTL5000_SYSCLK, sysclk, 0);
102 dev_info(rtd->dev->parent, "%s: setting mcasp clock to %u.%03uMHz\n", __func__,
103 sysclk / 1000000, sysclk / 1000 % 1000);
105 /* set codec to master mode */
106 dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
107 SND_SOC_DAIFMT_CBM_CFM;
109 /* set codec DAI configuration */
110 ret = snd_soc_dai_set_fmt(codec_dai, dai_format);
114 /* set cpu DAI configuration */
115 ret = snd_soc_dai_set_fmt(cpu_dai, dai_format);
122 static struct snd_soc_ops am335x_tx48_ops = {
123 .startup = am335x_tx48_startup,
124 .shutdown = am335x_tx48_shutdown,
125 .hw_params = sgtl5000_hw_params,
129 * The struct is used as place holder. It will be completely
130 * filled with data from dt node.
132 static struct snd_soc_dai_link am335x_tx48_dai = {
134 .stream_name = "SGTL5000",
135 .codec_dai_name = "sgtl5000",
136 .ops = &am335x_tx48_ops,
139 static struct snd_soc_card am335x_tx48_soc_card = {
140 .owner = THIS_MODULE,
141 .dai_link = &am335x_tx48_dai,
145 static const struct of_device_id am335x_tx48_dt_ids[] = {
146 { .compatible = "ti,am335x-tx48-audio", },
149 MODULE_DEVICE_TABLE(of, am335x_tx48_dt_ids);
151 static int am335x_tx48_probe(struct platform_device *pdev)
154 struct device_node *np = pdev->dev.of_node;
155 struct am335x_tx48_drvdata *drvdata;
156 struct device_node *codec_np;
157 struct device_node *mcasp_np;
158 struct platform_device *mcasp_pdev;
159 struct i2c_client *codec_dev;
162 codec_np = of_parse_phandle(np, "ti,audio-codec", 0);
164 dev_err(&pdev->dev, "codec handle missing in DT\n");
168 mcasp_np = of_parse_phandle(np, "ti,mcasp-controller", 0);
170 dev_err(&pdev->dev, "mcasp handle missing in DT\n");
175 codec_dev = of_find_i2c_device_by_node(codec_np);
177 dev_err(&pdev->dev, "failed to find codec platform device\n");
182 mcasp_pdev = of_find_device_by_node(mcasp_np);
184 dev_err(&pdev->dev, "failed to find MCASP platform device\n");
189 am335x_tx48_dai.codec_of_node = codec_np;
190 am335x_tx48_dai.cpu_of_node = mcasp_np;
191 am335x_tx48_dai.platform_of_node = mcasp_np;
193 am335x_tx48_soc_card.dev = &pdev->dev;
194 ret = snd_soc_of_parse_card_name(&am335x_tx48_soc_card, "ti,model");
198 mclk = devm_clk_get(&codec_dev->dev, NULL);
201 if (ret != -EPROBE_DEFER)
202 dev_err(&pdev->dev, "mclk not found: %d\n", ret);
206 drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
211 drvdata->mclk = mclk;
212 ret = of_property_read_u32(np, "ti,codec-clock-rate", &drvdata->sysclk);
214 if (!drvdata->mclk) {
216 "No clock or clock rate defined.\n");
220 drvdata->sysclk = clk_get_rate(drvdata->mclk);
221 } else if (drvdata->mclk) {
222 unsigned int requested_rate = drvdata->sysclk;
224 ret = clk_set_rate(drvdata->mclk, drvdata->sysclk);
226 dev_err(&pdev->dev, "Could not set mclk rate to %u\n",
230 drvdata->sysclk = clk_get_rate(drvdata->mclk);
231 if (drvdata->sysclk != requested_rate)
233 "Could not get requested rate %u using %u\n",
234 requested_rate, drvdata->sysclk);
237 snd_soc_card_set_drvdata(&am335x_tx48_soc_card, drvdata);
238 ret = devm_snd_soc_register_card(&pdev->dev, &am335x_tx48_soc_card);
240 dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
243 dev_dbg(&pdev->dev, "Soundcard %s registered\n",
244 am335x_tx48_soc_card.name);
248 of_node_put(mcasp_np);
251 of_node_put(codec_np);
255 static struct platform_driver am335x_tx48_driver = {
256 .probe = am335x_tx48_probe,
258 .name = "am335x_tx48",
259 .owner = THIS_MODULE,
260 .of_match_table = am335x_tx48_dt_ids,
263 module_platform_driver(am335x_tx48_driver);
265 MODULE_AUTHOR("Lothar Waßmann");
266 MODULE_DESCRIPTION("Ka-Ro TX48 ASoC driver");
267 MODULE_LICENSE("GPL");
268 MODULE_ALIAS("platform:am335x-tx48");