aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/tegra/tegra_p1852.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/tegra/tegra_p1852.c')
-rw-r--r--sound/soc/tegra/tegra_p1852.c272
1 files changed, 272 insertions, 0 deletions
diff --git a/sound/soc/tegra/tegra_p1852.c b/sound/soc/tegra/tegra_p1852.c
new file mode 100644
index 00000000000..27a1ea59034
--- /dev/null
+++ b/sound/soc/tegra/tegra_p1852.c
@@ -0,0 +1,272 @@
1/*
2 * tegra_p1852.c - Tegra machine ASoC driver for P1852 Boards.
3 *
4 * Author: Nitin Pai <npai@nvidia.com>
5 * Copyright (C) 2010-2012 - NVIDIA, Inc.
6 *
7 * Based on code copyright/by:
8 * Copyright (c) 2009-2010, NVIDIA Corporation.
9 * Stephen Warren <swarren@nvidia.com>
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * version 2 as published by the Free Software Foundation.
14 *
15 * This program is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
23 * 02110-1301 USA
24 *
25 */
26
27#include <asm/mach-types.h>
28
29#include <linux/clk.h>
30#include <linux/module.h>
31#include <linux/platform_device.h>
32#include <linux/slab.h>
33
34#include <mach/tegra_p1852_pdata.h>
35
36#include <sound/core.h>
37#include <sound/pcm.h>
38#include <sound/pcm_params.h>
39#include <sound/soc.h>
40
41#include "tegra_pcm.h"
42#include "tegra_asoc_utils.h"
43
44#define DRV_NAME "tegra-snd-p1852"
45
46struct tegra_p1852 {
47 struct tegra_asoc_utils_data util_data;
48 struct tegra_p1852_platform_data *pdata;
49};
50
51static int tegra_p1852_hw_params(struct snd_pcm_substream *substream,
52 struct snd_pcm_hw_params *params)
53{
54 struct snd_soc_pcm_runtime *rtd = substream->private_data;
55 struct snd_soc_dai *codec_dai = rtd->codec_dai;
56 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
57 struct snd_soc_codec *codec = rtd->codec;
58 struct snd_soc_card *card = codec->card;
59 struct tegra_p1852 *machine = snd_soc_card_get_drvdata(card);
60 int srate, mclk;
61 int i2s_daifmt = 0;
62 int err;
63 struct tegra_p1852_platform_data *pdata;
64 int codec_id = codec_dai->id;
65
66 pdata = machine->pdata;
67
68 srate = params_rate(params);
69 switch (srate) {
70 case 64000:
71 case 88200:
72 case 96000:
73 mclk = 128 * srate;
74 break;
75 default:
76 mclk = 256 * srate;
77 break;
78 }
79
80 err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk);
81 if (err < 0) {
82 if (!(machine->util_data.set_mclk % mclk))
83 mclk = machine->util_data.set_mclk;
84 else {
85 dev_err(card->dev, "Can't configure clocks\n");
86 return err;
87 }
88 }
89
90 tegra_asoc_utils_lock_clk_rate(&machine->util_data, 1);
91
92 if (pdata->codec_info[codec_id].master)
93 i2s_daifmt |= SND_SOC_DAIFMT_CBM_CFM;
94 else
95 i2s_daifmt |= SND_SOC_DAIFMT_CBS_CFS;
96
97 switch (pdata->codec_info[codec_id].i2s_format) {
98 case format_tdm:
99 i2s_daifmt |= SND_SOC_DAIFMT_DSP_A;
100 break;
101 case format_i2s:
102 i2s_daifmt |= SND_SOC_DAIFMT_I2S;
103 break;
104 case format_rjm:
105 i2s_daifmt |= SND_SOC_DAIFMT_RIGHT_J;
106 break;
107 case format_ljm:
108 i2s_daifmt |= SND_SOC_DAIFMT_LEFT_J;
109 break;
110 default:
111 break;
112 }
113
114 err = snd_soc_dai_set_fmt(codec_dai, i2s_daifmt);
115 if (err < 0)
116 dev_info(card->dev, "codec_dai fmt not set\n");
117
118 err = snd_soc_dai_set_fmt(cpu_dai, i2s_daifmt);
119 if (err < 0) {
120 dev_err(card->dev, "cpu_dai fmt not set\n");
121 return err;
122 }
123
124 err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
125 SND_SOC_CLOCK_IN);
126 if (err < 0)
127 dev_info(card->dev, "codec_dai clock not set\n");
128
129 return 0;
130}
131
132static int tegra_hw_free(struct snd_pcm_substream *substream)
133{
134 struct snd_soc_pcm_runtime *rtd = substream->private_data;
135 struct tegra_p1852 *machine = snd_soc_card_get_drvdata(rtd->card);
136
137 tegra_asoc_utils_lock_clk_rate(&machine->util_data, 0);
138
139 return 0;
140}
141
142static struct snd_soc_ops tegra_p1852_ops = {
143 .hw_params = tegra_p1852_hw_params,
144 .hw_free = tegra_hw_free,
145};
146
147static struct snd_soc_dai_link tegra_p1852_dai_link[] = {
148 {
149 .name = "I2S-TDM-1",
150 .stream_name = "TEGRA PCM",
151 .platform_name = "tegra-pcm-audio",
152 .ops = &tegra_p1852_ops,
153 },
154 {
155 .name = "I2S-TDM-2",
156 .stream_name = "TEGRA PCM",
157 .platform_name = "tegra-pcm-audio",
158 .ops = &tegra_p1852_ops,
159 }
160};
161
162static struct snd_soc_card snd_soc_tegra_p1852 = {
163 .name = "tegra-p1852",
164 .dai_link = tegra_p1852_dai_link,
165 .num_links = ARRAY_SIZE(tegra_p1852_dai_link),
166};
167
168static __devinit int tegra_p1852_driver_probe(struct platform_device *pdev)
169{
170 struct snd_soc_card *card = &snd_soc_tegra_p1852;
171 struct tegra_p1852 *machine;
172 struct tegra_p1852_platform_data *pdata;
173 int ret;
174 int i;
175
176 pdata = pdev->dev.platform_data;
177 if (!pdata) {
178 dev_err(&pdev->dev, "No platform data supplied\n");
179 return -EINVAL;
180 }
181
182 machine = kzalloc(sizeof(struct tegra_p1852), GFP_KERNEL);
183 if (!machine) {
184 dev_err(&pdev->dev, "Can't allocate tegra_p1852 struct\n");
185 return -ENOMEM;
186 }
187
188 machine->pdata = pdata;
189
190 /* The codec driver and codec dai have to come from the system
191 * level board configuration file
192 * */
193 for (i = 0; i < ARRAY_SIZE(tegra_p1852_dai_link); i++) {
194 tegra_p1852_dai_link[i].codec_name =
195 pdata->codec_info[i].codec_name;
196 tegra_p1852_dai_link[i].cpu_dai_name =
197 pdata->codec_info[i].cpu_dai_name;
198 tegra_p1852_dai_link[i].codec_dai_name =
199 pdata->codec_info[i].codec_dai_name;
200 tegra_p1852_dai_link[i].name =
201 pdata->codec_info[i].name;
202 }
203
204 ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev);
205 if (ret)
206 goto err_free_machine;
207
208 card->dev = &pdev->dev;
209 platform_set_drvdata(pdev, card);
210 snd_soc_card_set_drvdata(card, machine);
211
212 ret = snd_soc_register_card(card);
213 if (ret) {
214 dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
215 ret);
216 }
217
218 if (!card->instantiated) {
219 ret = -ENODEV;
220 dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
221 ret);
222 goto err_unregister_card;
223 }
224
225 return 0;
226
227err_unregister_card:
228 snd_soc_unregister_card(card);
229 tegra_asoc_utils_fini(&machine->util_data);
230err_free_machine:
231 kfree(machine);
232 return ret;
233}
234
235static int __devexit tegra_p1852_driver_remove(struct platform_device *pdev)
236{
237 struct snd_soc_card *card = platform_get_drvdata(pdev);
238 struct tegra_p1852 *machine = snd_soc_card_get_drvdata(card);
239
240 snd_soc_unregister_card(card);
241 tegra_asoc_utils_fini(&machine->util_data);
242 kfree(machine);
243
244 return 0;
245}
246
247static struct platform_driver tegra_p1852_driver = {
248 .driver = {
249 .name = DRV_NAME,
250 .owner = THIS_MODULE,
251 .pm = &snd_soc_pm_ops,
252 },
253 .probe = tegra_p1852_driver_probe,
254 .remove = __devexit_p(tegra_p1852_driver_remove),
255};
256
257static int __init tegra_p1852_modinit(void)
258{
259 return platform_driver_register(&tegra_p1852_driver);
260}
261module_init(tegra_p1852_modinit);
262
263static void __exit tegra_p1852_modexit(void)
264{
265 platform_driver_unregister(&tegra_p1852_driver);
266}
267module_exit(tegra_p1852_modexit);
268
269MODULE_AUTHOR("Nitin Pai <npai@nvidia.com>");
270MODULE_DESCRIPTION("Tegra+P1852 machine ASoC driver");
271MODULE_LICENSE("GPL");
272MODULE_ALIAS("platform:" DRV_NAME);