tegra20_spdif.c 8.48 KB
Newer Older
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 10
 */

#include <linux/clk.h>
#include <linux/device.h>
11 12
#include <linux/io.h>
#include <linux/module.h>
13
#include <linux/platform_device.h>
14
#include <linux/pm_runtime.h>
15
#include <linux/regmap.h>
16 17 18 19 20
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
21
#include <sound/dmaengine_pcm.h>
22

23
#include "tegra20_spdif.h"
24

25
#define DRV_NAME "tegra20-spdif"
26

27
static __maybe_unused int tegra20_spdif_runtime_suspend(struct device *dev)
28 29 30
{
	struct tegra20_spdif *spdif = dev_get_drvdata(dev);

31
	clk_disable_unprepare(spdif->clk_spdif_out);
32 33 34 35

	return 0;
}

36
static __maybe_unused int tegra20_spdif_runtime_resume(struct device *dev)
37 38 39 40
{
	struct tegra20_spdif *spdif = dev_get_drvdata(dev);
	int ret;

41
	ret = clk_prepare_enable(spdif->clk_spdif_out);
42 43 44 45 46 47 48 49
	if (ret) {
		dev_err(dev, "clk_enable failed: %d\n", ret);
		return ret;
	}

	return 0;
}

50
static int tegra20_spdif_hw_params(struct snd_pcm_substream *substream,
51 52 53
				struct snd_pcm_hw_params *params,
				struct snd_soc_dai *dai)
{
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 60
	mask |= TEGRA20_SPDIF_CTRL_PACK |
		TEGRA20_SPDIF_CTRL_BIT_MODE_MASK;
61 62
	switch (params_format(params)) {
	case SNDRV_PCM_FORMAT_S16_LE:
63 64
		val |= TEGRA20_SPDIF_CTRL_PACK |
		       TEGRA20_SPDIF_CTRL_BIT_MODE_16BIT;
65 66 67 68 69
		break;
	default:
		return -EINVAL;
	}

70 71
	regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_CTRL, mask, val);

72 73 74 75 76 77 78 79
	/*
	 * FIFO trigger level must be bigger than DMA burst or equal to it,
	 * otherwise data is discarded on overflow.
	 */
	regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_DATA_FIFO_CSR,
			   TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_MASK,
			   TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU4_WORD_FULL);

80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
	switch (params_rate(params)) {
	case 32000:
		spdifclock = 4096000;
		break;
	case 44100:
		spdifclock = 5644800;
		break;
	case 48000:
		spdifclock = 6144000;
		break;
	case 88200:
		spdifclock = 11289600;
		break;
	case 96000:
		spdifclock = 12288000;
		break;
	case 176400:
		spdifclock = 22579200;
		break;
	case 192000:
		spdifclock = 24576000;
		break;
	default:
		return -EINVAL;
	}

	ret = clk_set_rate(spdif->clk_spdif_out, spdifclock);
	if (ret) {
		dev_err(dev, "Can't set SPDIF clock rate: %d\n", ret);
		return ret;
	}

	return 0;
}

115
static void tegra20_spdif_start_playback(struct tegra20_spdif *spdif)
116
{
117 118 119
	regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_CTRL,
			   TEGRA20_SPDIF_CTRL_TX_EN,
			   TEGRA20_SPDIF_CTRL_TX_EN);
120 121
}

122
static void tegra20_spdif_stop_playback(struct tegra20_spdif *spdif)
123
{
124 125
	regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_CTRL,
			   TEGRA20_SPDIF_CTRL_TX_EN, 0);
126 127
}

128
static int tegra20_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
129 130
				struct snd_soc_dai *dai)
{
131
	struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai);
132 133 134 135 136

	switch (cmd) {
	case SNDRV_PCM_TRIGGER_START:
	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
	case SNDRV_PCM_TRIGGER_RESUME:
137
		tegra20_spdif_start_playback(spdif);
138 139 140 141
		break;
	case SNDRV_PCM_TRIGGER_STOP:
	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
	case SNDRV_PCM_TRIGGER_SUSPEND:
142
		tegra20_spdif_stop_playback(spdif);
143 144 145 146 147 148 149 150
		break;
	default:
		return -EINVAL;
	}

	return 0;
}

151
static int tegra20_spdif_probe(struct snd_soc_dai *dai)
152
{
153
	struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai);
154 155 156 157 158 159 160

	dai->capture_dma_data = NULL;
	dai->playback_dma_data = &spdif->playback_dma_data;

	return 0;
}

161 162 163
static const struct snd_soc_dai_ops tegra20_spdif_dai_ops = {
	.hw_params	= tegra20_spdif_hw_params,
	.trigger	= tegra20_spdif_trigger,
164 165
};

166
static struct snd_soc_dai_driver tegra20_spdif_dai = {
167
	.name = DRV_NAME,
168
	.probe = tegra20_spdif_probe,
169
	.playback = {
170
		.stream_name = "Playback",
171 172 173 174 175 176
		.channels_min = 2,
		.channels_max = 2,
		.rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
				SNDRV_PCM_RATE_48000,
		.formats = SNDRV_PCM_FMTBIT_S16_LE,
	},
177
	.ops = &tegra20_spdif_dai_ops,
178 179
};

180 181 182 183
static const struct snd_soc_component_driver tegra20_spdif_component = {
	.name		= DRV_NAME,
};

184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
static bool tegra20_spdif_wr_rd_reg(struct device *dev, unsigned int reg)
{
	switch (reg) {
	case TEGRA20_SPDIF_CTRL:
	case TEGRA20_SPDIF_STATUS:
	case TEGRA20_SPDIF_STROBE_CTRL:
	case TEGRA20_SPDIF_DATA_FIFO_CSR:
	case TEGRA20_SPDIF_DATA_OUT:
	case TEGRA20_SPDIF_DATA_IN:
	case TEGRA20_SPDIF_CH_STA_RX_A:
	case TEGRA20_SPDIF_CH_STA_RX_B:
	case TEGRA20_SPDIF_CH_STA_RX_C:
	case TEGRA20_SPDIF_CH_STA_RX_D:
	case TEGRA20_SPDIF_CH_STA_RX_E:
	case TEGRA20_SPDIF_CH_STA_RX_F:
	case TEGRA20_SPDIF_CH_STA_TX_A:
	case TEGRA20_SPDIF_CH_STA_TX_B:
	case TEGRA20_SPDIF_CH_STA_TX_C:
	case TEGRA20_SPDIF_CH_STA_TX_D:
	case TEGRA20_SPDIF_CH_STA_TX_E:
	case TEGRA20_SPDIF_CH_STA_TX_F:
	case TEGRA20_SPDIF_USR_STA_RX_A:
	case TEGRA20_SPDIF_USR_DAT_TX_A:
		return true;
	default:
		return false;
210
	}
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
}

static bool tegra20_spdif_volatile_reg(struct device *dev, unsigned int reg)
{
	switch (reg) {
	case TEGRA20_SPDIF_STATUS:
	case TEGRA20_SPDIF_DATA_FIFO_CSR:
	case TEGRA20_SPDIF_DATA_OUT:
	case TEGRA20_SPDIF_DATA_IN:
	case TEGRA20_SPDIF_CH_STA_RX_A:
	case TEGRA20_SPDIF_CH_STA_RX_B:
	case TEGRA20_SPDIF_CH_STA_RX_C:
	case TEGRA20_SPDIF_CH_STA_RX_D:
	case TEGRA20_SPDIF_CH_STA_RX_E:
	case TEGRA20_SPDIF_CH_STA_RX_F:
	case TEGRA20_SPDIF_USR_STA_RX_A:
	case TEGRA20_SPDIF_USR_DAT_TX_A:
		return true;
	default:
		return false;
231
	}
232 233 234 235 236 237 238 239 240 241 242 243
}

static bool tegra20_spdif_precious_reg(struct device *dev, unsigned int reg)
{
	switch (reg) {
	case TEGRA20_SPDIF_DATA_OUT:
	case TEGRA20_SPDIF_DATA_IN:
	case TEGRA20_SPDIF_USR_STA_RX_A:
	case TEGRA20_SPDIF_USR_DAT_TX_A:
		return true;
	default:
		return false;
244
	}
245 246 247 248 249 250 251 252 253 254 255
}

static const struct regmap_config tegra20_spdif_regmap_config = {
	.reg_bits = 32,
	.reg_stride = 4,
	.val_bits = 32,
	.max_register = TEGRA20_SPDIF_USR_DAT_TX_A,
	.writeable_reg = tegra20_spdif_wr_rd_reg,
	.readable_reg = tegra20_spdif_wr_rd_reg,
	.volatile_reg = tegra20_spdif_volatile_reg,
	.precious_reg = tegra20_spdif_precious_reg,
Dylan Reid's avatar
Dylan Reid committed
256
	.cache_type = REGCACHE_FLAT,
257 258
};

259
static int tegra20_spdif_platform_probe(struct platform_device *pdev)
260
{
261
	struct tegra20_spdif *spdif;
262
	struct resource *mem, *dmareq;
263
	void __iomem *regs;
264 265
	int ret;

266 267
	spdif = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_spdif),
			     GFP_KERNEL);
268
	if (!spdif)
269
		return -ENOMEM;
270

271 272
	dev_set_drvdata(&pdev->dev, spdif);

273
	spdif->clk_spdif_out = devm_clk_get(&pdev->dev, "spdif_out");
274 275 276
	if (IS_ERR(spdif->clk_spdif_out)) {
		pr_err("Can't retrieve spdif clock\n");
		ret = PTR_ERR(spdif->clk_spdif_out);
277
		return ret;
278 279
	}

280
	regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem);
281 282
	if (IS_ERR(regs))
		return PTR_ERR(regs);
283 284 285 286

	dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0);
	if (!dmareq) {
		dev_err(&pdev->dev, "No DMA resource\n");
287
		return -ENODEV;
288 289
	}

290 291 292 293 294
	spdif->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
					    &tegra20_spdif_regmap_config);
	if (IS_ERR(spdif->regmap)) {
		dev_err(&pdev->dev, "regmap init failed\n");
		ret = PTR_ERR(spdif->regmap);
295
		return ret;
296 297
	}

298
	spdif->playback_dma_data.addr = mem->start + TEGRA20_SPDIF_DATA_OUT;
299 300
	spdif->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
	spdif->playback_dma_data.maxburst = 4;
301

302 303
	pm_runtime_enable(&pdev->dev);

304 305
	ret = snd_soc_register_component(&pdev->dev, &tegra20_spdif_component,
					 &tegra20_spdif_dai, 1);
306 307 308
	if (ret) {
		dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
		ret = -ENOMEM;
309
		goto err_pm_disable;
310 311
	}

312 313 314
	ret = tegra_pcm_platform_register(&pdev->dev);
	if (ret) {
		dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);
315
		goto err_unregister_component;
316 317
	}

318 319
	return 0;

320 321
err_unregister_component:
	snd_soc_unregister_component(&pdev->dev);
322 323
err_pm_disable:
	pm_runtime_disable(&pdev->dev);
324

325 326 327
	return ret;
}

328
static int tegra20_spdif_platform_remove(struct platform_device *pdev)
329
{
330 331 332
	tegra_pcm_platform_unregister(&pdev->dev);
	snd_soc_unregister_component(&pdev->dev);

333 334
	pm_runtime_disable(&pdev->dev);

335 336 337
	return 0;
}

338
static const struct dev_pm_ops tegra20_spdif_pm_ops = {
339 340 341 342
	SET_RUNTIME_PM_OPS(tegra20_spdif_runtime_suspend,
			   tegra20_spdif_runtime_resume, NULL)
};

343
static struct platform_driver tegra20_spdif_driver = {
344 345
	.driver = {
		.name = DRV_NAME,
346
		.pm = &tegra20_spdif_pm_ops,
347
	},
348
	.probe = tegra20_spdif_platform_probe,
349
	.remove = tegra20_spdif_platform_remove,
350 351
};

352
module_platform_driver(tegra20_spdif_driver);
353 354

MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
355
MODULE_DESCRIPTION("Tegra20 SPDIF ASoC driver");
356 357
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRV_NAME);