1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * tegra20_spdif.c - Tegra20 SPDIF driver
4 *
5 * Author: Stephen Warren <swarren@nvidia.com>
6 * Copyright (C) 2011-2012 - NVIDIA, Inc.
7 */
8
9#include <linux/clk.h>
10#include <linux/device.h>
11#include <linux/io.h>
12#include <linux/module.h>
13#include <linux/platform_device.h>
14#include <linux/pm_runtime.h>
15#include <linux/regmap.h>
16#include <linux/slab.h>
17#include <sound/core.h>
18#include <sound/pcm.h>
19#include <sound/pcm_params.h>
20#include <sound/soc.h>
21#include <sound/dmaengine_pcm.h>
22
23#include "tegra20_spdif.h"
24
25#define DRV_NAME "tegra20-spdif"
26
27static int tegra20_spdif_runtime_suspend(struct device *dev)
28{
29	struct tegra20_spdif *spdif = dev_get_drvdata(dev);
30
31	clk_disable_unprepare(spdif->clk_spdif_out);
32
33	return 0;
34}
35
36static int tegra20_spdif_runtime_resume(struct device *dev)
37{
38	struct tegra20_spdif *spdif = dev_get_drvdata(dev);
39	int ret;
40
41	ret = clk_prepare_enable(spdif->clk_spdif_out);
42	if (ret) {
43		dev_err(dev, "clk_enable failed: %d\n", ret);
44		return ret;
45	}
46
47	return 0;
48}
49
50static int tegra20_spdif_hw_params(struct snd_pcm_substream *substream,
51				struct snd_pcm_hw_params *params,
52				struct snd_soc_dai *dai)
53{
54	struct device *dev = dai->dev;
55	struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai);
56	unsigned int mask = 0, val = 0;
57	int ret, spdifclock;
58
59	mask |= TEGRA20_SPDIF_CTRL_PACK |
60		TEGRA20_SPDIF_CTRL_BIT_MODE_MASK;
61	switch (params_format(params)) {
62	case SNDRV_PCM_FORMAT_S16_LE:
63		val |= TEGRA20_SPDIF_CTRL_PACK |
64		       TEGRA20_SPDIF_CTRL_BIT_MODE_16BIT;
65		break;
66	default:
67		return -EINVAL;
68	}
69
70	regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_CTRL, mask, val);
71
72	switch (params_rate(params)) {
73	case 32000:
74		spdifclock = 4096000;
75		break;
76	case 44100:
77		spdifclock = 5644800;
78		break;
79	case 48000:
80		spdifclock = 6144000;
81		break;
82	case 88200:
83		spdifclock = 11289600;
84		break;
85	case 96000:
86		spdifclock = 12288000;
87		break;
88	case 176400:
89		spdifclock = 22579200;
90		break;
91	case 192000:
92		spdifclock = 24576000;
93		break;
94	default:
95		return -EINVAL;
96	}
97
98	ret = clk_set_rate(spdif->clk_spdif_out, spdifclock);
99	if (ret) {
100		dev_err(dev, "Can't set SPDIF clock rate: %d\n", ret);
101		return ret;
102	}
103
104	return 0;
105}
106
107static void tegra20_spdif_start_playback(struct tegra20_spdif *spdif)
108{
109	regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_CTRL,
110			   TEGRA20_SPDIF_CTRL_TX_EN,
111			   TEGRA20_SPDIF_CTRL_TX_EN);
112}
113
114static void tegra20_spdif_stop_playback(struct tegra20_spdif *spdif)
115{
116	regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_CTRL,
117			   TEGRA20_SPDIF_CTRL_TX_EN, 0);
118}
119
120static int tegra20_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
121				struct snd_soc_dai *dai)
122{
123	struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai);
124
125	switch (cmd) {
126	case SNDRV_PCM_TRIGGER_START:
127	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
128	case SNDRV_PCM_TRIGGER_RESUME:
129		tegra20_spdif_start_playback(spdif);
130		break;
131	case SNDRV_PCM_TRIGGER_STOP:
132	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
133	case SNDRV_PCM_TRIGGER_SUSPEND:
134		tegra20_spdif_stop_playback(spdif);
135		break;
136	default:
137		return -EINVAL;
138	}
139
140	return 0;
141}
142
143static int tegra20_spdif_probe(struct snd_soc_dai *dai)
144{
145	struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai);
146
147	dai->capture_dma_data = NULL;
148	dai->playback_dma_data = &spdif->playback_dma_data;
149
150	return 0;
151}
152
153static const struct snd_soc_dai_ops tegra20_spdif_dai_ops = {
154	.hw_params	= tegra20_spdif_hw_params,
155	.trigger	= tegra20_spdif_trigger,
156};
157
158static struct snd_soc_dai_driver tegra20_spdif_dai = {
159	.name = DRV_NAME,
160	.probe = tegra20_spdif_probe,
161	.playback = {
162		.stream_name = "Playback",
163		.channels_min = 2,
164		.channels_max = 2,
165		.rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
166				SNDRV_PCM_RATE_48000,
167		.formats = SNDRV_PCM_FMTBIT_S16_LE,
168	},
169	.ops = &tegra20_spdif_dai_ops,
170};
171
172static const struct snd_soc_component_driver tegra20_spdif_component = {
173	.name		= DRV_NAME,
174};
175
176static bool tegra20_spdif_wr_rd_reg(struct device *dev, unsigned int reg)
177{
178	switch (reg) {
179	case TEGRA20_SPDIF_CTRL:
180	case TEGRA20_SPDIF_STATUS:
181	case TEGRA20_SPDIF_STROBE_CTRL:
182	case TEGRA20_SPDIF_DATA_FIFO_CSR:
183	case TEGRA20_SPDIF_DATA_OUT:
184	case TEGRA20_SPDIF_DATA_IN:
185	case TEGRA20_SPDIF_CH_STA_RX_A:
186	case TEGRA20_SPDIF_CH_STA_RX_B:
187	case TEGRA20_SPDIF_CH_STA_RX_C:
188	case TEGRA20_SPDIF_CH_STA_RX_D:
189	case TEGRA20_SPDIF_CH_STA_RX_E:
190	case TEGRA20_SPDIF_CH_STA_RX_F:
191	case TEGRA20_SPDIF_CH_STA_TX_A:
192	case TEGRA20_SPDIF_CH_STA_TX_B:
193	case TEGRA20_SPDIF_CH_STA_TX_C:
194	case TEGRA20_SPDIF_CH_STA_TX_D:
195	case TEGRA20_SPDIF_CH_STA_TX_E:
196	case TEGRA20_SPDIF_CH_STA_TX_F:
197	case TEGRA20_SPDIF_USR_STA_RX_A:
198	case TEGRA20_SPDIF_USR_DAT_TX_A:
199		return true;
200	default:
201		return false;
202	}
203}
204
205static bool tegra20_spdif_volatile_reg(struct device *dev, unsigned int reg)
206{
207	switch (reg) {
208	case TEGRA20_SPDIF_STATUS:
209	case TEGRA20_SPDIF_DATA_FIFO_CSR:
210	case TEGRA20_SPDIF_DATA_OUT:
211	case TEGRA20_SPDIF_DATA_IN:
212	case TEGRA20_SPDIF_CH_STA_RX_A:
213	case TEGRA20_SPDIF_CH_STA_RX_B:
214	case TEGRA20_SPDIF_CH_STA_RX_C:
215	case TEGRA20_SPDIF_CH_STA_RX_D:
216	case TEGRA20_SPDIF_CH_STA_RX_E:
217	case TEGRA20_SPDIF_CH_STA_RX_F:
218	case TEGRA20_SPDIF_USR_STA_RX_A:
219	case TEGRA20_SPDIF_USR_DAT_TX_A:
220		return true;
221	default:
222		return false;
223	}
224}
225
226static bool tegra20_spdif_precious_reg(struct device *dev, unsigned int reg)
227{
228	switch (reg) {
229	case TEGRA20_SPDIF_DATA_OUT:
230	case TEGRA20_SPDIF_DATA_IN:
231	case TEGRA20_SPDIF_USR_STA_RX_A:
232	case TEGRA20_SPDIF_USR_DAT_TX_A:
233		return true;
234	default:
235		return false;
236	}
237}
238
239static const struct regmap_config tegra20_spdif_regmap_config = {
240	.reg_bits = 32,
241	.reg_stride = 4,
242	.val_bits = 32,
243	.max_register = TEGRA20_SPDIF_USR_DAT_TX_A,
244	.writeable_reg = tegra20_spdif_wr_rd_reg,
245	.readable_reg = tegra20_spdif_wr_rd_reg,
246	.volatile_reg = tegra20_spdif_volatile_reg,
247	.precious_reg = tegra20_spdif_precious_reg,
248	.cache_type = REGCACHE_FLAT,
249};
250
251static int tegra20_spdif_platform_probe(struct platform_device *pdev)
252{
253	struct tegra20_spdif *spdif;
254	struct resource *mem, *dmareq;
255	void __iomem *regs;
256	int ret;
257
258	spdif = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_spdif),
259			     GFP_KERNEL);
260	if (!spdif)
261		return -ENOMEM;
262
263	dev_set_drvdata(&pdev->dev, spdif);
264
265	spdif->clk_spdif_out = devm_clk_get(&pdev->dev, "spdif_out");
266	if (IS_ERR(spdif->clk_spdif_out)) {
267		pr_err("Can't retrieve spdif clock\n");
268		ret = PTR_ERR(spdif->clk_spdif_out);
269		return ret;
270	}
271
272	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
273	regs = devm_ioremap_resource(&pdev->dev, mem);
274	if (IS_ERR(regs))
275		return PTR_ERR(regs);
276
277	dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0);
278	if (!dmareq) {
279		dev_err(&pdev->dev, "No DMA resource\n");
280		return -ENODEV;
281	}
282
283	spdif->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
284					    &tegra20_spdif_regmap_config);
285	if (IS_ERR(spdif->regmap)) {
286		dev_err(&pdev->dev, "regmap init failed\n");
287		ret = PTR_ERR(spdif->regmap);
288		return ret;
289	}
290
291	spdif->playback_dma_data.addr = mem->start + TEGRA20_SPDIF_DATA_OUT;
292	spdif->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
293	spdif->playback_dma_data.maxburst = 4;
294	spdif->playback_dma_data.slave_id = dmareq->start;
295
296	pm_runtime_enable(&pdev->dev);
297	if (!pm_runtime_enabled(&pdev->dev)) {
298		ret = tegra20_spdif_runtime_resume(&pdev->dev);
299		if (ret)
300			goto err_pm_disable;
301	}
302
303	ret = snd_soc_register_component(&pdev->dev, &tegra20_spdif_component,
304					 &tegra20_spdif_dai, 1);
305	if (ret) {
306		dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
307		ret = -ENOMEM;
308		goto err_suspend;
309	}
310
311	ret = tegra_pcm_platform_register(&pdev->dev);
312	if (ret) {
313		dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);
314		goto err_unregister_component;
315	}
316
317	return 0;
318
319err_unregister_component:
320	snd_soc_unregister_component(&pdev->dev);
321err_suspend:
322	if (!pm_runtime_status_suspended(&pdev->dev))
323		tegra20_spdif_runtime_suspend(&pdev->dev);
324err_pm_disable:
325	pm_runtime_disable(&pdev->dev);
326
327	return ret;
328}
329
330static int tegra20_spdif_platform_remove(struct platform_device *pdev)
331{
332	pm_runtime_disable(&pdev->dev);
333	if (!pm_runtime_status_suspended(&pdev->dev))
334		tegra20_spdif_runtime_suspend(&pdev->dev);
335
336	tegra_pcm_platform_unregister(&pdev->dev);
337	snd_soc_unregister_component(&pdev->dev);
338
339	return 0;
340}
341
342static const struct dev_pm_ops tegra20_spdif_pm_ops = {
343	SET_RUNTIME_PM_OPS(tegra20_spdif_runtime_suspend,
344			   tegra20_spdif_runtime_resume, NULL)
345};
346
347static struct platform_driver tegra20_spdif_driver = {
348	.driver = {
349		.name = DRV_NAME,
350		.pm = &tegra20_spdif_pm_ops,
351	},
352	.probe = tegra20_spdif_platform_probe,
353	.remove = tegra20_spdif_platform_remove,
354};
355
356module_platform_driver(tegra20_spdif_driver);
357
358MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
359MODULE_DESCRIPTION("Tegra20 SPDIF ASoC driver");
360MODULE_LICENSE("GPL");
361MODULE_ALIAS("platform:" DRV_NAME);
362