xref: /openbmc/linux/sound/soc/tegra/tegra20_spdif.c (revision d0b73b48)
1 /*
2  * tegra20_spdif.c - Tegra20 SPDIF driver
3  *
4  * Author: Stephen Warren <swarren@nvidia.com>
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 
72 	mask = TEGRA20_SPDIF_CTRL_PACK |
73 	       TEGRA20_SPDIF_CTRL_BIT_MODE_MASK;
74 	switch (params_format(params)) {
75 	case SNDRV_PCM_FORMAT_S16_LE:
76 		val = TEGRA20_SPDIF_CTRL_PACK |
77 		      TEGRA20_SPDIF_CTRL_BIT_MODE_16BIT;
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 {
122 	regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_CTRL,
123 			   TEGRA20_SPDIF_CTRL_TX_EN,
124 			   TEGRA20_SPDIF_CTRL_TX_EN);
125 }
126 
127 static void tegra20_spdif_stop_playback(struct tegra20_spdif *spdif)
128 {
129 	regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_CTRL,
130 			   TEGRA20_SPDIF_CTRL_TX_EN, 0);
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) {
139 	case SNDRV_PCM_TRIGGER_START:
140 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
141 	case SNDRV_PCM_TRIGGER_RESUME:
142 		tegra20_spdif_start_playback(spdif);
143 		break;
144 	case SNDRV_PCM_TRIGGER_STOP:
145 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
146 	case SNDRV_PCM_TRIGGER_SUSPEND:
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,
178 		.rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
179 				SNDRV_PCM_RATE_48000,
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:
189 	case TEGRA20_SPDIF_STATUS:
190 	case TEGRA20_SPDIF_STROBE_CTRL:
191 	case TEGRA20_SPDIF_DATA_FIFO_CSR:
192 	case TEGRA20_SPDIF_DATA_OUT:
193 	case TEGRA20_SPDIF_DATA_IN:
194 	case TEGRA20_SPDIF_CH_STA_RX_A:
195 	case TEGRA20_SPDIF_CH_STA_RX_B:
196 	case TEGRA20_SPDIF_CH_STA_RX_C:
197 	case TEGRA20_SPDIF_CH_STA_RX_D:
198 	case TEGRA20_SPDIF_CH_STA_RX_E:
199 	case TEGRA20_SPDIF_CH_STA_RX_F:
200 	case TEGRA20_SPDIF_CH_STA_TX_A:
201 	case TEGRA20_SPDIF_CH_STA_TX_B:
202 	case TEGRA20_SPDIF_CH_STA_TX_C:
203 	case TEGRA20_SPDIF_CH_STA_TX_D:
204 	case TEGRA20_SPDIF_CH_STA_TX_E:
205 	case TEGRA20_SPDIF_CH_STA_TX_F:
206 	case TEGRA20_SPDIF_USR_STA_RX_A:
207 	case TEGRA20_SPDIF_USR_DAT_TX_A:
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) {
217 	case TEGRA20_SPDIF_STATUS:
218 	case TEGRA20_SPDIF_DATA_FIFO_CSR:
219 	case TEGRA20_SPDIF_DATA_OUT:
220 	case TEGRA20_SPDIF_DATA_IN:
221 	case TEGRA20_SPDIF_CH_STA_RX_A:
222 	case TEGRA20_SPDIF_CH_STA_RX_B:
223 	case TEGRA20_SPDIF_CH_STA_RX_C:
224 	case TEGRA20_SPDIF_CH_STA_RX_D:
225 	case TEGRA20_SPDIF_CH_STA_RX_E:
226 	case TEGRA20_SPDIF_CH_STA_RX_F:
227 	case TEGRA20_SPDIF_USR_STA_RX_A:
228 	case TEGRA20_SPDIF_USR_DAT_TX_A:
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) {
238 	case TEGRA20_SPDIF_DATA_OUT:
239 	case TEGRA20_SPDIF_DATA_IN:
240 	case TEGRA20_SPDIF_USR_STA_RX_A:
241 	case TEGRA20_SPDIF_USR_DAT_TX_A:
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 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 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 
368 	tegra_pcm_platform_unregister(&pdev->dev);
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 = {
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 = tegra20_spdif_platform_remove,
389 };
390 
391 module_platform_driver(tegra20_spdif_driver);
392 
393 MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
394 MODULE_DESCRIPTION("Tegra20 SPDIF ASoC driver");
395 MODULE_LICENSE("GPL");
396 MODULE_ALIAS("platform:" DRV_NAME);
397