ASoC: tegra: use DAI's not card's dev for dev_err
[deliverable/linux.git] / sound / soc / tegra / tegra20_spdif.c
CommitLineData
774fec33 1/*
ef280d39 2 * tegra20_spdif.c - Tegra20 SPDIF driver
774fec33
SW
3 *
4 * Author: Stephen Warren <swarren@nvidia.com>
518de86b 5 * Copyright (C) 2011-2012 - NVIDIA, Inc.
774fec33
SW
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * version 2 as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
19 * 02110-1301 USA
20 *
21 */
22
23#include <linux/clk.h>
774fec33 24#include <linux/device.h>
7613c508
SW
25#include <linux/io.h>
26#include <linux/module.h>
774fec33 27#include <linux/platform_device.h>
82ef0ae4 28#include <linux/pm_runtime.h>
5939ae74 29#include <linux/regmap.h>
774fec33 30#include <linux/slab.h>
774fec33
SW
31#include <sound/core.h>
32#include <sound/pcm.h>
33#include <sound/pcm_params.h>
34#include <sound/soc.h>
35
ef280d39 36#include "tegra20_spdif.h"
774fec33 37
896637ac 38#define DRV_NAME "tegra20-spdif"
774fec33 39
896637ac 40static inline void tegra20_spdif_write(struct tegra20_spdif *spdif, u32 reg,
774fec33
SW
41 u32 val)
42{
5939ae74 43 regmap_write(spdif->regmap, reg, val);
774fec33
SW
44}
45
896637ac 46static inline u32 tegra20_spdif_read(struct tegra20_spdif *spdif, u32 reg)
774fec33 47{
5939ae74
SW
48 u32 val;
49 regmap_read(spdif->regmap, reg, &val);
50 return val;
774fec33
SW
51}
52
82ef0ae4
SW
53static int tegra20_spdif_runtime_suspend(struct device *dev)
54{
55 struct tegra20_spdif *spdif = dev_get_drvdata(dev);
56
57 clk_disable(spdif->clk_spdif_out);
58
59 return 0;
60}
61
62static int tegra20_spdif_runtime_resume(struct device *dev)
63{
64 struct tegra20_spdif *spdif = dev_get_drvdata(dev);
65 int ret;
66
67 ret = clk_enable(spdif->clk_spdif_out);
68 if (ret) {
69 dev_err(dev, "clk_enable failed: %d\n", ret);
70 return ret;
71 }
72
73 return 0;
74}
75
896637ac 76static int tegra20_spdif_hw_params(struct snd_pcm_substream *substream,
774fec33
SW
77 struct snd_pcm_hw_params *params,
78 struct snd_soc_dai *dai)
79{
c92a40e3 80 struct device *dev = dai->dev;
896637ac 81 struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai);
4b8713fd 82 int ret, spdifclock;
774fec33 83
896637ac
SW
84 spdif->reg_ctrl &= ~TEGRA20_SPDIF_CTRL_PACK;
85 spdif->reg_ctrl &= ~TEGRA20_SPDIF_CTRL_BIT_MODE_MASK;
774fec33
SW
86 switch (params_format(params)) {
87 case SNDRV_PCM_FORMAT_S16_LE:
896637ac
SW
88 spdif->reg_ctrl |= TEGRA20_SPDIF_CTRL_PACK;
89 spdif->reg_ctrl |= TEGRA20_SPDIF_CTRL_BIT_MODE_16BIT;
774fec33
SW
90 break;
91 default:
92 return -EINVAL;
93 }
94
774fec33
SW
95 switch (params_rate(params)) {
96 case 32000:
97 spdifclock = 4096000;
98 break;
99 case 44100:
100 spdifclock = 5644800;
101 break;
102 case 48000:
103 spdifclock = 6144000;
104 break;
105 case 88200:
106 spdifclock = 11289600;
107 break;
108 case 96000:
109 spdifclock = 12288000;
110 break;
111 case 176400:
112 spdifclock = 22579200;
113 break;
114 case 192000:
115 spdifclock = 24576000;
116 break;
117 default:
118 return -EINVAL;
119 }
120
121 ret = clk_set_rate(spdif->clk_spdif_out, spdifclock);
122 if (ret) {
123 dev_err(dev, "Can't set SPDIF clock rate: %d\n", ret);
124 return ret;
125 }
126
127 return 0;
128}
129
896637ac 130static void tegra20_spdif_start_playback(struct tegra20_spdif *spdif)
774fec33 131{
896637ac
SW
132 spdif->reg_ctrl |= TEGRA20_SPDIF_CTRL_TX_EN;
133 tegra20_spdif_write(spdif, TEGRA20_SPDIF_CTRL, spdif->reg_ctrl);
774fec33
SW
134}
135
896637ac 136static void tegra20_spdif_stop_playback(struct tegra20_spdif *spdif)
774fec33 137{
896637ac
SW
138 spdif->reg_ctrl &= ~TEGRA20_SPDIF_CTRL_TX_EN;
139 tegra20_spdif_write(spdif, TEGRA20_SPDIF_CTRL, spdif->reg_ctrl);
774fec33
SW
140}
141
896637ac 142static int tegra20_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
774fec33
SW
143 struct snd_soc_dai *dai)
144{
896637ac 145 struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai);
774fec33
SW
146
147 switch (cmd) {
148 case SNDRV_PCM_TRIGGER_START:
149 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
150 case SNDRV_PCM_TRIGGER_RESUME:
896637ac 151 tegra20_spdif_start_playback(spdif);
774fec33
SW
152 break;
153 case SNDRV_PCM_TRIGGER_STOP:
154 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
155 case SNDRV_PCM_TRIGGER_SUSPEND:
896637ac 156 tegra20_spdif_stop_playback(spdif);
774fec33
SW
157 break;
158 default:
159 return -EINVAL;
160 }
161
162 return 0;
163}
164
896637ac 165static int tegra20_spdif_probe(struct snd_soc_dai *dai)
774fec33 166{
896637ac 167 struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai);
774fec33
SW
168
169 dai->capture_dma_data = NULL;
170 dai->playback_dma_data = &spdif->playback_dma_data;
171
172 return 0;
173}
174
896637ac
SW
175static const struct snd_soc_dai_ops tegra20_spdif_dai_ops = {
176 .hw_params = tegra20_spdif_hw_params,
177 .trigger = tegra20_spdif_trigger,
774fec33
SW
178};
179
896637ac 180static struct snd_soc_dai_driver tegra20_spdif_dai = {
774fec33 181 .name = DRV_NAME,
896637ac 182 .probe = tegra20_spdif_probe,
774fec33 183 .playback = {
9515c101 184 .stream_name = "Playback",
774fec33
SW
185 .channels_min = 2,
186 .channels_max = 2,
187 .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
188 SNDRV_PCM_RATE_48000,
189 .formats = SNDRV_PCM_FMTBIT_S16_LE,
190 },
896637ac 191 .ops = &tegra20_spdif_dai_ops,
774fec33
SW
192};
193
5939ae74
SW
194static bool tegra20_spdif_wr_rd_reg(struct device *dev, unsigned int reg)
195{
196 switch (reg) {
197 case TEGRA20_SPDIF_CTRL:
198 case TEGRA20_SPDIF_STATUS:
199 case TEGRA20_SPDIF_STROBE_CTRL:
200 case TEGRA20_SPDIF_DATA_FIFO_CSR:
201 case TEGRA20_SPDIF_DATA_OUT:
202 case TEGRA20_SPDIF_DATA_IN:
203 case TEGRA20_SPDIF_CH_STA_RX_A:
204 case TEGRA20_SPDIF_CH_STA_RX_B:
205 case TEGRA20_SPDIF_CH_STA_RX_C:
206 case TEGRA20_SPDIF_CH_STA_RX_D:
207 case TEGRA20_SPDIF_CH_STA_RX_E:
208 case TEGRA20_SPDIF_CH_STA_RX_F:
209 case TEGRA20_SPDIF_CH_STA_TX_A:
210 case TEGRA20_SPDIF_CH_STA_TX_B:
211 case TEGRA20_SPDIF_CH_STA_TX_C:
212 case TEGRA20_SPDIF_CH_STA_TX_D:
213 case TEGRA20_SPDIF_CH_STA_TX_E:
214 case TEGRA20_SPDIF_CH_STA_TX_F:
215 case TEGRA20_SPDIF_USR_STA_RX_A:
216 case TEGRA20_SPDIF_USR_DAT_TX_A:
217 return true;
218 default:
219 return false;
220 };
221}
222
223static bool tegra20_spdif_volatile_reg(struct device *dev, unsigned int reg)
224{
225 switch (reg) {
226 case TEGRA20_SPDIF_STATUS:
227 case TEGRA20_SPDIF_DATA_FIFO_CSR:
228 case TEGRA20_SPDIF_DATA_OUT:
229 case TEGRA20_SPDIF_DATA_IN:
230 case TEGRA20_SPDIF_CH_STA_RX_A:
231 case TEGRA20_SPDIF_CH_STA_RX_B:
232 case TEGRA20_SPDIF_CH_STA_RX_C:
233 case TEGRA20_SPDIF_CH_STA_RX_D:
234 case TEGRA20_SPDIF_CH_STA_RX_E:
235 case TEGRA20_SPDIF_CH_STA_RX_F:
236 case TEGRA20_SPDIF_USR_STA_RX_A:
237 case TEGRA20_SPDIF_USR_DAT_TX_A:
238 return true;
239 default:
240 return false;
241 };
242}
243
244static bool tegra20_spdif_precious_reg(struct device *dev, unsigned int reg)
245{
246 switch (reg) {
247 case TEGRA20_SPDIF_DATA_OUT:
248 case TEGRA20_SPDIF_DATA_IN:
249 case TEGRA20_SPDIF_USR_STA_RX_A:
250 case TEGRA20_SPDIF_USR_DAT_TX_A:
251 return true;
252 default:
253 return false;
254 };
255}
256
257static const struct regmap_config tegra20_spdif_regmap_config = {
258 .reg_bits = 32,
259 .reg_stride = 4,
260 .val_bits = 32,
261 .max_register = TEGRA20_SPDIF_USR_DAT_TX_A,
262 .writeable_reg = tegra20_spdif_wr_rd_reg,
263 .readable_reg = tegra20_spdif_wr_rd_reg,
264 .volatile_reg = tegra20_spdif_volatile_reg,
265 .precious_reg = tegra20_spdif_precious_reg,
266 .cache_type = REGCACHE_RBTREE,
267};
268
896637ac 269static __devinit int tegra20_spdif_platform_probe(struct platform_device *pdev)
774fec33 270{
896637ac 271 struct tegra20_spdif *spdif;
774fec33 272 struct resource *mem, *memregion, *dmareq;
5939ae74 273 void __iomem *regs;
774fec33
SW
274 int ret;
275
17933db2
SW
276 spdif = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_spdif),
277 GFP_KERNEL);
774fec33 278 if (!spdif) {
896637ac 279 dev_err(&pdev->dev, "Can't allocate tegra20_spdif\n");
774fec33 280 ret = -ENOMEM;
17933db2 281 goto err;
774fec33
SW
282 }
283 dev_set_drvdata(&pdev->dev, spdif);
284
285 spdif->clk_spdif_out = clk_get(&pdev->dev, "spdif_out");
286 if (IS_ERR(spdif->clk_spdif_out)) {
287 pr_err("Can't retrieve spdif clock\n");
288 ret = PTR_ERR(spdif->clk_spdif_out);
17933db2 289 goto err;
774fec33
SW
290 }
291
292 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
293 if (!mem) {
294 dev_err(&pdev->dev, "No memory resource\n");
295 ret = -ENODEV;
296 goto err_clk_put;
297 }
298
299 dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0);
300 if (!dmareq) {
301 dev_err(&pdev->dev, "No DMA resource\n");
302 ret = -ENODEV;
303 goto err_clk_put;
304 }
305
17933db2
SW
306 memregion = devm_request_mem_region(&pdev->dev, mem->start,
307 resource_size(mem), DRV_NAME);
774fec33
SW
308 if (!memregion) {
309 dev_err(&pdev->dev, "Memory region already claimed\n");
310 ret = -EBUSY;
311 goto err_clk_put;
312 }
313
5939ae74
SW
314 regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
315 if (!regs) {
774fec33
SW
316 dev_err(&pdev->dev, "ioremap failed\n");
317 ret = -ENOMEM;
17933db2 318 goto err_clk_put;
774fec33
SW
319 }
320
5939ae74
SW
321 spdif->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
322 &tegra20_spdif_regmap_config);
323 if (IS_ERR(spdif->regmap)) {
324 dev_err(&pdev->dev, "regmap init failed\n");
325 ret = PTR_ERR(spdif->regmap);
326 goto err_clk_put;
327 }
328
896637ac 329 spdif->playback_dma_data.addr = mem->start + TEGRA20_SPDIF_DATA_OUT;
774fec33
SW
330 spdif->playback_dma_data.wrap = 4;
331 spdif->playback_dma_data.width = 32;
332 spdif->playback_dma_data.req_sel = dmareq->start;
333
82ef0ae4
SW
334 pm_runtime_enable(&pdev->dev);
335 if (!pm_runtime_enabled(&pdev->dev)) {
336 ret = tegra20_spdif_runtime_resume(&pdev->dev);
337 if (ret)
338 goto err_pm_disable;
339 }
340
896637ac 341 ret = snd_soc_register_dai(&pdev->dev, &tegra20_spdif_dai);
774fec33
SW
342 if (ret) {
343 dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
344 ret = -ENOMEM;
82ef0ae4 345 goto err_suspend;
774fec33
SW
346 }
347
518de86b
SW
348 ret = tegra_pcm_platform_register(&pdev->dev);
349 if (ret) {
350 dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);
351 goto err_unregister_dai;
352 }
353
774fec33
SW
354 return 0;
355
518de86b
SW
356err_unregister_dai:
357 snd_soc_unregister_dai(&pdev->dev);
82ef0ae4
SW
358err_suspend:
359 if (!pm_runtime_status_suspended(&pdev->dev))
360 tegra20_spdif_runtime_suspend(&pdev->dev);
361err_pm_disable:
362 pm_runtime_disable(&pdev->dev);
774fec33
SW
363err_clk_put:
364 clk_put(spdif->clk_spdif_out);
17933db2 365err:
774fec33
SW
366 return ret;
367}
368
896637ac 369static int __devexit tegra20_spdif_platform_remove(struct platform_device *pdev)
774fec33 370{
896637ac 371 struct tegra20_spdif *spdif = dev_get_drvdata(&pdev->dev);
774fec33 372
82ef0ae4
SW
373 pm_runtime_disable(&pdev->dev);
374 if (!pm_runtime_status_suspended(&pdev->dev))
375 tegra20_spdif_runtime_suspend(&pdev->dev);
376
518de86b 377 tegra_pcm_platform_unregister(&pdev->dev);
774fec33
SW
378 snd_soc_unregister_dai(&pdev->dev);
379
774fec33
SW
380 clk_put(spdif->clk_spdif_out);
381
774fec33
SW
382 return 0;
383}
384
82ef0ae4
SW
385static const struct dev_pm_ops tegra20_spdif_pm_ops __devinitconst = {
386 SET_RUNTIME_PM_OPS(tegra20_spdif_runtime_suspend,
387 tegra20_spdif_runtime_resume, NULL)
388};
389
896637ac 390static struct platform_driver tegra20_spdif_driver = {
774fec33
SW
391 .driver = {
392 .name = DRV_NAME,
393 .owner = THIS_MODULE,
82ef0ae4 394 .pm = &tegra20_spdif_pm_ops,
774fec33 395 },
896637ac
SW
396 .probe = tegra20_spdif_platform_probe,
397 .remove = __devexit_p(tegra20_spdif_platform_remove),
774fec33
SW
398};
399
896637ac 400module_platform_driver(tegra20_spdif_driver);
774fec33
SW
401
402MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
896637ac 403MODULE_DESCRIPTION("Tegra20 SPDIF ASoC driver");
774fec33
SW
404MODULE_LICENSE("GPL");
405MODULE_ALIAS("platform:" DRV_NAME);
This page took 0.078909 seconds and 5 git commands to generate.