2 * tegra20_spdif.c - Tegra20 SPDIF driver
4 * Author: Stephen Warren <swarren@nvidia.com>
5 * Copyright (C) 2011-2012 - NVIDIA, Inc.
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.
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.
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
23 #include <linux/clk.h>
24 #include <linux/device.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>
36 #include "tegra20_spdif.h"
38 #define DRV_NAME "tegra20-spdif"
40 static inline void tegra20_spdif_write(struct tegra20_spdif *spdif, u32 reg,
43 regmap_write(spdif->regmap, reg, val);
46 static inline u32 tegra20_spdif_read(struct tegra20_spdif *spdif, u32 reg)
49 regmap_read(spdif->regmap, reg, &val);
53 static int tegra20_spdif_runtime_suspend(struct device *dev)
55 struct tegra20_spdif *spdif = dev_get_drvdata(dev);
57 clk_disable(spdif->clk_spdif_out);
62 static int tegra20_spdif_runtime_resume(struct device *dev)
64 struct tegra20_spdif *spdif = dev_get_drvdata(dev);
67 ret = clk_enable(spdif->clk_spdif_out);
69 dev_err(dev, "clk_enable failed: %d\n", ret);
76 static int tegra20_spdif_hw_params(struct snd_pcm_substream *substream,
77 struct snd_pcm_hw_params *params,
78 struct snd_soc_dai *dai)
80 struct device *dev = substream->pcm->card->dev;
81 struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai);
84 spdif->reg_ctrl &= ~TEGRA20_SPDIF_CTRL_PACK;
85 spdif->reg_ctrl &= ~TEGRA20_SPDIF_CTRL_BIT_MODE_MASK;
86 switch (params_format(params)) {
87 case SNDRV_PCM_FORMAT_S16_LE:
88 spdif->reg_ctrl |= TEGRA20_SPDIF_CTRL_PACK;
89 spdif->reg_ctrl |= TEGRA20_SPDIF_CTRL_BIT_MODE_16BIT;
95 switch (params_rate(params)) {
100 spdifclock = 5644800;
103 spdifclock = 6144000;
106 spdifclock = 11289600;
109 spdifclock = 12288000;
112 spdifclock = 22579200;
115 spdifclock = 24576000;
121 ret = clk_set_rate(spdif->clk_spdif_out, spdifclock);
123 dev_err(dev, "Can't set SPDIF clock rate: %d\n", ret);
130 static void tegra20_spdif_start_playback(struct tegra20_spdif *spdif)
132 spdif->reg_ctrl |= TEGRA20_SPDIF_CTRL_TX_EN;
133 tegra20_spdif_write(spdif, TEGRA20_SPDIF_CTRL, spdif->reg_ctrl);
136 static void tegra20_spdif_stop_playback(struct tegra20_spdif *spdif)
138 spdif->reg_ctrl &= ~TEGRA20_SPDIF_CTRL_TX_EN;
139 tegra20_spdif_write(spdif, TEGRA20_SPDIF_CTRL, spdif->reg_ctrl);
142 static int tegra20_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
143 struct snd_soc_dai *dai)
145 struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai);
148 case SNDRV_PCM_TRIGGER_START:
149 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
150 case SNDRV_PCM_TRIGGER_RESUME:
151 tegra20_spdif_start_playback(spdif);
153 case SNDRV_PCM_TRIGGER_STOP:
154 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
155 case SNDRV_PCM_TRIGGER_SUSPEND:
156 tegra20_spdif_stop_playback(spdif);
165 static int tegra20_spdif_probe(struct snd_soc_dai *dai)
167 struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai);
169 dai->capture_dma_data = NULL;
170 dai->playback_dma_data = &spdif->playback_dma_data;
175 static const struct snd_soc_dai_ops tegra20_spdif_dai_ops = {
176 .hw_params = tegra20_spdif_hw_params,
177 .trigger = tegra20_spdif_trigger,
180 static struct snd_soc_dai_driver tegra20_spdif_dai = {
182 .probe = tegra20_spdif_probe,
186 .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
187 SNDRV_PCM_RATE_48000,
188 .formats = SNDRV_PCM_FMTBIT_S16_LE,
190 .ops = &tegra20_spdif_dai_ops,
193 static bool tegra20_spdif_wr_rd_reg(struct device *dev, unsigned int reg)
196 case TEGRA20_SPDIF_CTRL:
197 case TEGRA20_SPDIF_STATUS:
198 case TEGRA20_SPDIF_STROBE_CTRL:
199 case TEGRA20_SPDIF_DATA_FIFO_CSR:
200 case TEGRA20_SPDIF_DATA_OUT:
201 case TEGRA20_SPDIF_DATA_IN:
202 case TEGRA20_SPDIF_CH_STA_RX_A:
203 case TEGRA20_SPDIF_CH_STA_RX_B:
204 case TEGRA20_SPDIF_CH_STA_RX_C:
205 case TEGRA20_SPDIF_CH_STA_RX_D:
206 case TEGRA20_SPDIF_CH_STA_RX_E:
207 case TEGRA20_SPDIF_CH_STA_RX_F:
208 case TEGRA20_SPDIF_CH_STA_TX_A:
209 case TEGRA20_SPDIF_CH_STA_TX_B:
210 case TEGRA20_SPDIF_CH_STA_TX_C:
211 case TEGRA20_SPDIF_CH_STA_TX_D:
212 case TEGRA20_SPDIF_CH_STA_TX_E:
213 case TEGRA20_SPDIF_CH_STA_TX_F:
214 case TEGRA20_SPDIF_USR_STA_RX_A:
215 case TEGRA20_SPDIF_USR_DAT_TX_A:
222 static bool tegra20_spdif_volatile_reg(struct device *dev, unsigned int reg)
225 case TEGRA20_SPDIF_STATUS:
226 case TEGRA20_SPDIF_DATA_FIFO_CSR:
227 case TEGRA20_SPDIF_DATA_OUT:
228 case TEGRA20_SPDIF_DATA_IN:
229 case TEGRA20_SPDIF_CH_STA_RX_A:
230 case TEGRA20_SPDIF_CH_STA_RX_B:
231 case TEGRA20_SPDIF_CH_STA_RX_C:
232 case TEGRA20_SPDIF_CH_STA_RX_D:
233 case TEGRA20_SPDIF_CH_STA_RX_E:
234 case TEGRA20_SPDIF_CH_STA_RX_F:
235 case TEGRA20_SPDIF_USR_STA_RX_A:
236 case TEGRA20_SPDIF_USR_DAT_TX_A:
243 static bool tegra20_spdif_precious_reg(struct device *dev, unsigned int reg)
246 case TEGRA20_SPDIF_DATA_OUT:
247 case TEGRA20_SPDIF_DATA_IN:
248 case TEGRA20_SPDIF_USR_STA_RX_A:
249 case TEGRA20_SPDIF_USR_DAT_TX_A:
256 static const struct regmap_config tegra20_spdif_regmap_config = {
260 .max_register = TEGRA20_SPDIF_USR_DAT_TX_A,
261 .writeable_reg = tegra20_spdif_wr_rd_reg,
262 .readable_reg = tegra20_spdif_wr_rd_reg,
263 .volatile_reg = tegra20_spdif_volatile_reg,
264 .precious_reg = tegra20_spdif_precious_reg,
265 .cache_type = REGCACHE_RBTREE,
268 static __devinit int tegra20_spdif_platform_probe(struct platform_device *pdev)
270 struct tegra20_spdif *spdif;
271 struct resource *mem, *memregion, *dmareq;
275 spdif = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_spdif),
278 dev_err(&pdev->dev, "Can't allocate tegra20_spdif\n");
282 dev_set_drvdata(&pdev->dev, spdif);
284 spdif->clk_spdif_out = clk_get(&pdev->dev, "spdif_out");
285 if (IS_ERR(spdif->clk_spdif_out)) {
286 pr_err("Can't retrieve spdif clock\n");
287 ret = PTR_ERR(spdif->clk_spdif_out);
291 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
293 dev_err(&pdev->dev, "No memory resource\n");
298 dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0);
300 dev_err(&pdev->dev, "No DMA resource\n");
305 memregion = devm_request_mem_region(&pdev->dev, mem->start,
306 resource_size(mem), DRV_NAME);
308 dev_err(&pdev->dev, "Memory region already claimed\n");
313 regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
315 dev_err(&pdev->dev, "ioremap failed\n");
320 spdif->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
321 &tegra20_spdif_regmap_config);
322 if (IS_ERR(spdif->regmap)) {
323 dev_err(&pdev->dev, "regmap init failed\n");
324 ret = PTR_ERR(spdif->regmap);
328 spdif->playback_dma_data.addr = mem->start + TEGRA20_SPDIF_DATA_OUT;
329 spdif->playback_dma_data.wrap = 4;
330 spdif->playback_dma_data.width = 32;
331 spdif->playback_dma_data.req_sel = dmareq->start;
333 pm_runtime_enable(&pdev->dev);
334 if (!pm_runtime_enabled(&pdev->dev)) {
335 ret = tegra20_spdif_runtime_resume(&pdev->dev);
340 ret = snd_soc_register_dai(&pdev->dev, &tegra20_spdif_dai);
342 dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
347 ret = tegra_pcm_platform_register(&pdev->dev);
349 dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);
350 goto err_unregister_dai;
356 snd_soc_unregister_dai(&pdev->dev);
358 if (!pm_runtime_status_suspended(&pdev->dev))
359 tegra20_spdif_runtime_suspend(&pdev->dev);
361 pm_runtime_disable(&pdev->dev);
363 clk_put(spdif->clk_spdif_out);
368 static int __devexit tegra20_spdif_platform_remove(struct platform_device *pdev)
370 struct tegra20_spdif *spdif = dev_get_drvdata(&pdev->dev);
372 pm_runtime_disable(&pdev->dev);
373 if (!pm_runtime_status_suspended(&pdev->dev))
374 tegra20_spdif_runtime_suspend(&pdev->dev);
376 tegra_pcm_platform_unregister(&pdev->dev);
377 snd_soc_unregister_dai(&pdev->dev);
379 clk_put(spdif->clk_spdif_out);
384 static const struct dev_pm_ops tegra20_spdif_pm_ops __devinitconst = {
385 SET_RUNTIME_PM_OPS(tegra20_spdif_runtime_suspend,
386 tegra20_spdif_runtime_resume, NULL)
389 static struct platform_driver tegra20_spdif_driver = {
392 .owner = THIS_MODULE,
393 .pm = &tegra20_spdif_pm_ops,
395 .probe = tegra20_spdif_platform_probe,
396 .remove = __devexit_p(tegra20_spdif_platform_remove),
399 module_platform_driver(tegra20_spdif_driver);
401 MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
402 MODULE_DESCRIPTION("Tegra20 SPDIF ASoC driver");
403 MODULE_LICENSE("GPL");
404 MODULE_ALIAS("platform:" DRV_NAME);