Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
tegra20_spdif.c
Go to the documentation of this file.
1 /*
2  * tegra20_spdif.c - Tegra20 SPDIF driver
3  *
4  * Author: Stephen Warren <[email protected]>
5  * Copyright (C) 2011-2012 - NVIDIA, Inc.
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>
24 #include <linux/device.h>
25 #include <linux/io.h>
26 #include <linux/module.h>
27 #include <linux/platform_device.h>
28 #include <linux/pm_runtime.h>
29 #include <linux/regmap.h>
30 #include <linux/slab.h>
31 #include <sound/core.h>
32 #include <sound/pcm.h>
33 #include <sound/pcm_params.h>
34 #include <sound/soc.h>
35 
36 #include "tegra20_spdif.h"
37 
38 #define DRV_NAME "tegra20-spdif"
39 
40 static int tegra20_spdif_runtime_suspend(struct device *dev)
41 {
42  struct tegra20_spdif *spdif = dev_get_drvdata(dev);
43 
44  clk_disable_unprepare(spdif->clk_spdif_out);
45 
46  return 0;
47 }
48 
49 static int tegra20_spdif_runtime_resume(struct device *dev)
50 {
51  struct tegra20_spdif *spdif = dev_get_drvdata(dev);
52  int ret;
53 
54  ret = clk_prepare_enable(spdif->clk_spdif_out);
55  if (ret) {
56  dev_err(dev, "clk_enable failed: %d\n", ret);
57  return ret;
58  }
59 
60  return 0;
61 }
62 
63 static int tegra20_spdif_hw_params(struct snd_pcm_substream *substream,
64  struct snd_pcm_hw_params *params,
65  struct snd_soc_dai *dai)
66 {
67  struct device *dev = dai->dev;
68  struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai);
69  unsigned int mask, val;
70  int ret, spdifclock;
71 
74  switch (params_format(params)) {
78  break;
79  default:
80  return -EINVAL;
81  }
82 
83  regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_CTRL, mask, val);
84 
85  switch (params_rate(params)) {
86  case 32000:
87  spdifclock = 4096000;
88  break;
89  case 44100:
90  spdifclock = 5644800;
91  break;
92  case 48000:
93  spdifclock = 6144000;
94  break;
95  case 88200:
96  spdifclock = 11289600;
97  break;
98  case 96000:
99  spdifclock = 12288000;
100  break;
101  case 176400:
102  spdifclock = 22579200;
103  break;
104  case 192000:
105  spdifclock = 24576000;
106  break;
107  default:
108  return -EINVAL;
109  }
110 
111  ret = clk_set_rate(spdif->clk_spdif_out, spdifclock);
112  if (ret) {
113  dev_err(dev, "Can't set SPDIF clock rate: %d\n", ret);
114  return ret;
115  }
116 
117  return 0;
118 }
119 
120 static void tegra20_spdif_start_playback(struct tegra20_spdif *spdif)
121 {
125 }
126 
127 static void tegra20_spdif_stop_playback(struct tegra20_spdif *spdif)
128 {
131 }
132 
133 static int tegra20_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
134  struct snd_soc_dai *dai)
135 {
136  struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai);
137 
138  switch (cmd) {
142  tegra20_spdif_start_playback(spdif);
143  break;
147  tegra20_spdif_stop_playback(spdif);
148  break;
149  default:
150  return -EINVAL;
151  }
152 
153  return 0;
154 }
155 
156 static int tegra20_spdif_probe(struct snd_soc_dai *dai)
157 {
158  struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai);
159 
160  dai->capture_dma_data = NULL;
161  dai->playback_dma_data = &spdif->playback_dma_data;
162 
163  return 0;
164 }
165 
166 static const struct snd_soc_dai_ops tegra20_spdif_dai_ops = {
167  .hw_params = tegra20_spdif_hw_params,
168  .trigger = tegra20_spdif_trigger,
169 };
170 
171 static struct snd_soc_dai_driver tegra20_spdif_dai = {
172  .name = DRV_NAME,
173  .probe = tegra20_spdif_probe,
174  .playback = {
175  .stream_name = "Playback",
176  .channels_min = 2,
177  .channels_max = 2,
180  .formats = SNDRV_PCM_FMTBIT_S16_LE,
181  },
182  .ops = &tegra20_spdif_dai_ops,
183 };
184 
185 static bool tegra20_spdif_wr_rd_reg(struct device *dev, unsigned int reg)
186 {
187  switch (reg) {
188  case TEGRA20_SPDIF_CTRL:
208  return true;
209  default:
210  return false;
211  };
212 }
213 
214 static bool tegra20_spdif_volatile_reg(struct device *dev, unsigned int reg)
215 {
216  switch (reg) {
229  return true;
230  default:
231  return false;
232  };
233 }
234 
235 static bool tegra20_spdif_precious_reg(struct device *dev, unsigned int reg)
236 {
237  switch (reg) {
242  return true;
243  default:
244  return false;
245  };
246 }
247 
248 static const struct regmap_config tegra20_spdif_regmap_config = {
249  .reg_bits = 32,
250  .reg_stride = 4,
251  .val_bits = 32,
252  .max_register = TEGRA20_SPDIF_USR_DAT_TX_A,
253  .writeable_reg = tegra20_spdif_wr_rd_reg,
254  .readable_reg = tegra20_spdif_wr_rd_reg,
255  .volatile_reg = tegra20_spdif_volatile_reg,
256  .precious_reg = tegra20_spdif_precious_reg,
257  .cache_type = REGCACHE_RBTREE,
258 };
259 
260 static __devinit int tegra20_spdif_platform_probe(struct platform_device *pdev)
261 {
262  struct tegra20_spdif *spdif;
263  struct resource *mem, *memregion, *dmareq;
264  void __iomem *regs;
265  int ret;
266 
267  spdif = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_spdif),
268  GFP_KERNEL);
269  if (!spdif) {
270  dev_err(&pdev->dev, "Can't allocate tegra20_spdif\n");
271  ret = -ENOMEM;
272  goto err;
273  }
274  dev_set_drvdata(&pdev->dev, spdif);
275 
276  spdif->clk_spdif_out = clk_get(&pdev->dev, "spdif_out");
277  if (IS_ERR(spdif->clk_spdif_out)) {
278  pr_err("Can't retrieve spdif clock\n");
279  ret = PTR_ERR(spdif->clk_spdif_out);
280  goto err;
281  }
282 
283  mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
284  if (!mem) {
285  dev_err(&pdev->dev, "No memory resource\n");
286  ret = -ENODEV;
287  goto err_clk_put;
288  }
289 
290  dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0);
291  if (!dmareq) {
292  dev_err(&pdev->dev, "No DMA resource\n");
293  ret = -ENODEV;
294  goto err_clk_put;
295  }
296 
297  memregion = devm_request_mem_region(&pdev->dev, mem->start,
298  resource_size(mem), DRV_NAME);
299  if (!memregion) {
300  dev_err(&pdev->dev, "Memory region already claimed\n");
301  ret = -EBUSY;
302  goto err_clk_put;
303  }
304 
305  regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
306  if (!regs) {
307  dev_err(&pdev->dev, "ioremap failed\n");
308  ret = -ENOMEM;
309  goto err_clk_put;
310  }
311 
312  spdif->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
313  &tegra20_spdif_regmap_config);
314  if (IS_ERR(spdif->regmap)) {
315  dev_err(&pdev->dev, "regmap init failed\n");
316  ret = PTR_ERR(spdif->regmap);
317  goto err_clk_put;
318  }
319 
320  spdif->playback_dma_data.addr = mem->start + TEGRA20_SPDIF_DATA_OUT;
321  spdif->playback_dma_data.wrap = 4;
322  spdif->playback_dma_data.width = 32;
323  spdif->playback_dma_data.req_sel = dmareq->start;
324 
325  pm_runtime_enable(&pdev->dev);
326  if (!pm_runtime_enabled(&pdev->dev)) {
327  ret = tegra20_spdif_runtime_resume(&pdev->dev);
328  if (ret)
329  goto err_pm_disable;
330  }
331 
332  ret = snd_soc_register_dai(&pdev->dev, &tegra20_spdif_dai);
333  if (ret) {
334  dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
335  ret = -ENOMEM;
336  goto err_suspend;
337  }
338 
339  ret = tegra_pcm_platform_register(&pdev->dev);
340  if (ret) {
341  dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);
342  goto err_unregister_dai;
343  }
344 
345  return 0;
346 
347 err_unregister_dai:
348  snd_soc_unregister_dai(&pdev->dev);
349 err_suspend:
350  if (!pm_runtime_status_suspended(&pdev->dev))
351  tegra20_spdif_runtime_suspend(&pdev->dev);
352 err_pm_disable:
353  pm_runtime_disable(&pdev->dev);
354 err_clk_put:
355  clk_put(spdif->clk_spdif_out);
356 err:
357  return ret;
358 }
359 
360 static int __devexit tegra20_spdif_platform_remove(struct platform_device *pdev)
361 {
362  struct tegra20_spdif *spdif = dev_get_drvdata(&pdev->dev);
363 
364  pm_runtime_disable(&pdev->dev);
365  if (!pm_runtime_status_suspended(&pdev->dev))
366  tegra20_spdif_runtime_suspend(&pdev->dev);
367 
369  snd_soc_unregister_dai(&pdev->dev);
370 
371  clk_put(spdif->clk_spdif_out);
372 
373  return 0;
374 }
375 
376 static const struct dev_pm_ops tegra20_spdif_pm_ops __devinitconst = {
377  SET_RUNTIME_PM_OPS(tegra20_spdif_runtime_suspend,
378  tegra20_spdif_runtime_resume, NULL)
379 };
380 
381 static struct platform_driver tegra20_spdif_driver = {
382  .driver = {
383  .name = DRV_NAME,
384  .owner = THIS_MODULE,
385  .pm = &tegra20_spdif_pm_ops,
386  },
387  .probe = tegra20_spdif_platform_probe,
388  .remove = __devexit_p(tegra20_spdif_platform_remove),
389 };
390 
391 module_platform_driver(tegra20_spdif_driver);
392 
393 MODULE_AUTHOR("Stephen Warren <[email protected]>");
394 MODULE_DESCRIPTION("Tegra20 SPDIF ASoC driver");
395 MODULE_LICENSE("GPL");
396 MODULE_ALIAS("platform:" DRV_NAME);