]> git.karo-electronics.de Git - karo-tx-linux.git/blob - sound/soc/davinci/am335x-tx48.c
381f545b59c7b092caeb95834bbc0a809064aba9
[karo-tx-linux.git] / sound / soc / davinci / am335x-tx48.c
1 /*
2  * ASoC driver for Ka-Ro electronics TX48 module
3  * (C) Copyright 2013 Lothar Waßmann <LW@KARO-electronics.de>
4  *
5  * based on: davinci-evm.c
6  * Author:      Vladimir Barinov, <vbarinov@embeddedalley.com>
7  * Copyright:   (C) 2007 MontaVista Software, Inc., <source@mvista.com>
8  *
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.
12  */
13 #define DEBUG
14
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/i2c.h>
21 #include <linux/edma.h>
22 #include <sound/core.h>
23 #include <sound/pcm.h>
24 #include <sound/soc.h>
25
26 #include <asm/dma.h>
27 #include <asm/mach-types.h>
28
29 #include "../codecs/sgtl5000.h"
30
31 #include "davinci-pcm.h"
32 #include "davinci-i2s.h"
33 #include "davinci-mcasp.h"
34
35 struct am335x_tx48_drvdata {
36         unsigned sysclk;
37 };
38
39 static int sgtl5000_hw_params(struct snd_pcm_substream *substream,
40         struct snd_pcm_hw_params *params)
41 {
42         int ret;
43         struct snd_soc_pcm_runtime *rtd = substream->private_data;
44         struct snd_soc_dai *codec_dai = rtd->codec_dai;
45         struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
46         struct snd_soc_card *soc_card = rtd->codec->card;
47         struct am335x_tx48_drvdata *drvdata = snd_soc_card_get_drvdata(soc_card);
48         unsigned sysclk = drvdata->sysclk;
49         u32 dai_format;
50
51         if (!codec_dai) {
52                 dev_err(rtd->dev->parent, "No CODEC DAI\n");
53                 return -ENODEV;
54         }
55         if (!codec_dai->driver) {
56                 dev_err(rtd->dev->parent, "No CODEC DAI driver\n");
57                 return -ENODEV;
58         }
59
60         if (!cpu_dai) {
61                 dev_err(rtd->dev->parent, "No CPU DAI\n");
62                 return -ENODEV;
63         }
64         if (!cpu_dai->driver) {
65                 dev_err(rtd->dev->parent, "No CPU DAI driver\n");
66                 return -ENODEV;
67         }
68
69         dev_info(rtd->dev->parent, "%s: setting codec clock to %u.%03uMHz\n", __func__,
70                 sysclk / 1000000, sysclk / 1000 % 1000);
71         /* Set SGTL5000's SYSCLK */
72         ret = snd_soc_dai_set_sysclk(codec_dai, SGTL5000_SYSCLK, sysclk, 0);
73         if (ret)
74                 return ret;
75
76         dev_info(rtd->dev->parent, "%s: setting mcasp clock to %u.%03uMHz\n", __func__,
77                 sysclk / 1000000, sysclk / 1000 % 1000);
78
79         /* set codec to master mode */
80         dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
81                         SND_SOC_DAIFMT_CBM_CFM;
82
83         /* set codec DAI configuration */
84         ret = snd_soc_dai_set_fmt(codec_dai, dai_format);
85         if (ret)
86                 return ret;
87
88         /* set cpu DAI configuration */
89         ret = snd_soc_dai_set_fmt(cpu_dai, dai_format);
90         if (ret)
91                 return ret;
92
93         return 0;
94 }
95
96 static struct snd_soc_ops am335x_tx48_ops = {
97         .hw_params = sgtl5000_hw_params,
98 };
99
100 /*
101  * The struct is used as place holder. It will be completely
102  * filled with data from dt node.
103  */
104 static struct snd_soc_dai_link am335x_tx48_dai = {
105         .name = "SGTL5000",
106         .stream_name = "SGTL5000",
107         .codec_dai_name = "sgtl5000",
108         .ops = &am335x_tx48_ops,
109 };
110
111 static struct snd_soc_card am335x_tx48_soc_card = {
112         .owner = THIS_MODULE,
113         .dai_link = &am335x_tx48_dai,
114         .num_links = 1,
115 };
116
117 static const struct of_device_id am335x_tx48_dt_ids[] = {
118         { .compatible = "ti,am335x-tx48-audio", },
119         { /* sentinel */ }
120 };
121 MODULE_DEVICE_TABLE(of, am335x_tx48_dt_ids);
122
123 static int am335x_tx48_probe(struct platform_device *pdev)
124 {
125         int ret;
126         struct device_node *np = pdev->dev.of_node;
127         struct am335x_tx48_drvdata *drvdata;
128         struct device_node *of_node;
129
130         of_node = of_parse_phandle(np, "ti,audio-codec", 0);
131         if (!of_node) {
132                 dev_err(&pdev->dev, "codec handle missing in DT\n");
133                 return -EINVAL;
134         }
135
136         am335x_tx48_dai.codec_of_node = of_node;
137         of_node_put(of_node);
138
139         of_node = of_parse_phandle(np, "ti,mcasp-controller", 0);
140         if (!of_node) {
141                 dev_err(&pdev->dev, "mcasp handle missing in DT\n");
142                 return -EINVAL;
143         }
144
145         am335x_tx48_dai.cpu_of_node = of_node;
146         am335x_tx48_dai.platform_of_node = of_node;
147         of_node_put(of_node);
148
149         am335x_tx48_soc_card.dev = &pdev->dev;
150         ret = snd_soc_of_parse_card_name(&am335x_tx48_soc_card, "ti,model");
151         if (ret)
152                 return ret;
153
154         drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
155         if (!drvdata)
156                 return -ENOMEM;
157
158         ret = of_property_read_u32(np, "ti,codec-clock-rate", &drvdata->sysclk);
159         if (ret < 0) {
160                 dev_err(&pdev->dev, "codec clock rate not set in DT\n");
161                 return -EINVAL;
162         }
163
164         snd_soc_card_set_drvdata(&am335x_tx48_soc_card, drvdata);
165         ret = devm_snd_soc_register_card(&pdev->dev, &am335x_tx48_soc_card);
166         if (ret) {
167                 dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
168                 return ret;
169         }
170         dev_dbg(&pdev->dev, "Soundcard %p registered\n", &am335x_tx48_soc_card);
171         return 0;
172 }
173
174 static int am335x_tx48_remove(struct platform_device *pdev)
175 {
176         struct snd_soc_card *card = platform_get_drvdata(pdev);
177
178 dev_dbg(&pdev->dev, "%s: Unregistering card %p\n", __func__, card);
179         snd_soc_unregister_card(card);
180
181         return 0;
182 }
183
184 static struct platform_driver am335x_tx48_driver = {
185         .probe          = am335x_tx48_probe,
186         .remove         = am335x_tx48_remove,
187         .driver         = {
188                 .name   = "am335x_tx48",
189                 .owner  = THIS_MODULE,
190                 .of_match_table = am335x_tx48_dt_ids,
191         },
192 };
193 module_platform_driver(am335x_tx48_driver);
194
195 MODULE_AUTHOR("Lothar Waßmann");
196 MODULE_DESCRIPTION("Ka-Ro TX48 ASoC driver");
197 MODULE_LICENSE("GPL");
198 MODULE_ALIAS("platform:am335x-tx48");