tegra20_i2s.c 12.0 KB
Newer Older
1
/*
2
 * tegra20_i2s.c - Tegra20 I2S driver
3 4
 *
 * Author: Stephen Warren <swarren@nvidia.com>
5
 * Copyright (C) 2010,2012 - NVIDIA, Inc.
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
 *
 * Based on code copyright/by:
 *
 * Copyright (c) 2009-2010, NVIDIA Corporation.
 * Scott Peterson <speterson@nvidia.com>
 *
 * Copyright (C) 2010 Google, Inc.
 * Iliyan Malchev <malchev@google.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 *
 */

#include <linux/clk.h>
#include <linux/device.h>
33 34 35
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
36
#include <linux/platform_device.h>
37
#include <linux/pm_runtime.h>
38
#include <linux/regmap.h>
39 40 41 42 43 44
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>

45
#include "tegra20_i2s.h"
46

47
#define DRV_NAME "tegra20-i2s"
48

49 50 51 52
static int tegra20_i2s_runtime_suspend(struct device *dev)
{
	struct tegra20_i2s *i2s = dev_get_drvdata(dev);

53
	clk_disable_unprepare(i2s->clk_i2s);
54 55 56 57 58 59 60 61 62

	return 0;
}

static int tegra20_i2s_runtime_resume(struct device *dev)
{
	struct tegra20_i2s *i2s = dev_get_drvdata(dev);
	int ret;

63
	ret = clk_prepare_enable(i2s->clk_i2s);
64 65 66 67 68 69 70 71
	if (ret) {
		dev_err(dev, "clk_enable failed: %d\n", ret);
		return ret;
	}

	return 0;
}

72
static int tegra20_i2s_set_fmt(struct snd_soc_dai *dai,
73 74
				unsigned int fmt)
{
75
	struct tegra20_i2s *i2s = snd_soc_dai_get_drvdata(dai);
76
	unsigned int mask, val;
77 78 79 80 81 82 83 84

	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
	case SND_SOC_DAIFMT_NB_NF:
		break;
	default:
		return -EINVAL;
	}

85
	mask = TEGRA20_I2S_CTRL_MASTER_ENABLE;
86 87
	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
	case SND_SOC_DAIFMT_CBS_CFS:
88
		val = TEGRA20_I2S_CTRL_MASTER_ENABLE;
89 90 91 92 93 94 95
		break;
	case SND_SOC_DAIFMT_CBM_CFM:
		break;
	default:
		return -EINVAL;
	}

96 97
	mask |= TEGRA20_I2S_CTRL_BIT_FORMAT_MASK |
		TEGRA20_I2S_CTRL_LRCK_MASK;
98 99
	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
	case SND_SOC_DAIFMT_DSP_A:
100 101
		val |= TEGRA20_I2S_CTRL_BIT_FORMAT_DSP;
		val |= TEGRA20_I2S_CTRL_LRCK_L_LOW;
102 103
		break;
	case SND_SOC_DAIFMT_DSP_B:
104 105
		val |= TEGRA20_I2S_CTRL_BIT_FORMAT_DSP;
		val |= TEGRA20_I2S_CTRL_LRCK_R_LOW;
106 107
		break;
	case SND_SOC_DAIFMT_I2S:
108 109
		val |= TEGRA20_I2S_CTRL_BIT_FORMAT_I2S;
		val |= TEGRA20_I2S_CTRL_LRCK_L_LOW;
110 111
		break;
	case SND_SOC_DAIFMT_RIGHT_J:
112 113
		val |= TEGRA20_I2S_CTRL_BIT_FORMAT_RJM;
		val |= TEGRA20_I2S_CTRL_LRCK_L_LOW;
114 115
		break;
	case SND_SOC_DAIFMT_LEFT_J:
116 117
		val |= TEGRA20_I2S_CTRL_BIT_FORMAT_LJM;
		val |= TEGRA20_I2S_CTRL_LRCK_L_LOW;
118 119 120 121 122
		break;
	default:
		return -EINVAL;
	}

123 124
	regmap_update_bits(i2s->regmap, TEGRA20_I2S_CTRL, mask, val);

125 126 127
	return 0;
}

128 129 130
static int tegra20_i2s_hw_params(struct snd_pcm_substream *substream,
				 struct snd_pcm_hw_params *params,
				 struct snd_soc_dai *dai)
131
{
132
	struct device *dev = dai->dev;
133
	struct tegra20_i2s *i2s = snd_soc_dai_get_drvdata(dai);
134
	unsigned int mask, val;
135 136
	int ret, sample_size, srate, i2sclock, bitcnt;

137
	mask = TEGRA20_I2S_CTRL_BIT_SIZE_MASK;
138 139
	switch (params_format(params)) {
	case SNDRV_PCM_FORMAT_S16_LE:
140
		val = TEGRA20_I2S_CTRL_BIT_SIZE_16;
141 142 143
		sample_size = 16;
		break;
	case SNDRV_PCM_FORMAT_S24_LE:
144
		val = TEGRA20_I2S_CTRL_BIT_SIZE_24;
145 146 147
		sample_size = 24;
		break;
	case SNDRV_PCM_FORMAT_S32_LE:
148
		val = TEGRA20_I2S_CTRL_BIT_SIZE_32;
149 150 151 152 153 154
		sample_size = 32;
		break;
	default:
		return -EINVAL;
	}

155 156 157 158 159
	mask |= TEGRA20_I2S_CTRL_FIFO_FORMAT_MASK;
	val |= TEGRA20_I2S_CTRL_FIFO_FORMAT_PACKED;

	regmap_update_bits(i2s->regmap, TEGRA20_I2S_CTRL, mask, val);

160 161 162 163 164 165 166 167 168 169 170 171
	srate = params_rate(params);

	/* Final "* 2" required by Tegra hardware */
	i2sclock = srate * params_channels(params) * sample_size * 2;

	ret = clk_set_rate(i2s->clk_i2s, i2sclock);
	if (ret) {
		dev_err(dev, "Can't set I2S clock rate: %d\n", ret);
		return ret;
	}

	bitcnt = (i2sclock / (2 * srate)) - 1;
172
	if (bitcnt < 0 || bitcnt > TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US)
173
		return -EINVAL;
174
	val = bitcnt << TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT;
175 176

	if (i2sclock % (2 * srate))
177
		val |= TEGRA20_I2S_TIMING_NON_SYM_ENABLE;
178

179
	regmap_write(i2s->regmap, TEGRA20_I2S_TIMING, val);
180

181 182 183
	regmap_write(i2s->regmap, TEGRA20_I2S_FIFO_SCR,
		     TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS |
		     TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS);
184 185 186 187

	return 0;
}

188
static void tegra20_i2s_start_playback(struct tegra20_i2s *i2s)
189
{
190 191 192
	regmap_update_bits(i2s->regmap, TEGRA20_I2S_CTRL,
			   TEGRA20_I2S_CTRL_FIFO1_ENABLE,
			   TEGRA20_I2S_CTRL_FIFO1_ENABLE);
193 194
}

195
static void tegra20_i2s_stop_playback(struct tegra20_i2s *i2s)
196
{
197 198
	regmap_update_bits(i2s->regmap, TEGRA20_I2S_CTRL,
			   TEGRA20_I2S_CTRL_FIFO1_ENABLE, 0);
199 200
}

201
static void tegra20_i2s_start_capture(struct tegra20_i2s *i2s)
202
{
203 204 205
	regmap_update_bits(i2s->regmap, TEGRA20_I2S_CTRL,
			   TEGRA20_I2S_CTRL_FIFO2_ENABLE,
			   TEGRA20_I2S_CTRL_FIFO2_ENABLE);
206 207
}

208
static void tegra20_i2s_stop_capture(struct tegra20_i2s *i2s)
209
{
210 211
	regmap_update_bits(i2s->regmap, TEGRA20_I2S_CTRL,
			   TEGRA20_I2S_CTRL_FIFO2_ENABLE, 0);
212 213
}

214 215
static int tegra20_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
			       struct snd_soc_dai *dai)
216
{
217
	struct tegra20_i2s *i2s = snd_soc_dai_get_drvdata(dai);
218 219 220 221 222 223

	switch (cmd) {
	case SNDRV_PCM_TRIGGER_START:
	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
	case SNDRV_PCM_TRIGGER_RESUME:
		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
224
			tegra20_i2s_start_playback(i2s);
225
		else
226
			tegra20_i2s_start_capture(i2s);
227 228 229 230 231
		break;
	case SNDRV_PCM_TRIGGER_STOP:
	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
	case SNDRV_PCM_TRIGGER_SUSPEND:
		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
232
			tegra20_i2s_stop_playback(i2s);
233
		else
234
			tegra20_i2s_stop_capture(i2s);
235 236 237 238 239 240 241 242
		break;
	default:
		return -EINVAL;
	}

	return 0;
}

243
static int tegra20_i2s_probe(struct snd_soc_dai *dai)
244
{
245
	struct tegra20_i2s *i2s = snd_soc_dai_get_drvdata(dai);
246 247 248 249 250 251 252

	dai->capture_dma_data = &i2s->capture_dma_data;
	dai->playback_dma_data = &i2s->playback_dma_data;

	return 0;
}

253 254 255 256
static const struct snd_soc_dai_ops tegra20_i2s_dai_ops = {
	.set_fmt	= tegra20_i2s_set_fmt,
	.hw_params	= tegra20_i2s_hw_params,
	.trigger	= tegra20_i2s_trigger,
257 258
};

259 260
static const struct snd_soc_dai_driver tegra20_i2s_dai_template = {
	.probe = tegra20_i2s_probe,
261
	.playback = {
262
		.stream_name = "Playback",
263 264 265 266
		.channels_min = 2,
		.channels_max = 2,
		.rates = SNDRV_PCM_RATE_8000_96000,
		.formats = SNDRV_PCM_FMTBIT_S16_LE,
267
	},
268
	.capture = {
269
		.stream_name = "Capture",
270 271 272 273
		.channels_min = 2,
		.channels_max = 2,
		.rates = SNDRV_PCM_RATE_8000_96000,
		.formats = SNDRV_PCM_FMTBIT_S16_LE,
274
	},
275
	.ops = &tegra20_i2s_dai_ops,
276
	.symmetric_rates = 1,
277 278
};

279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
static bool tegra20_i2s_wr_rd_reg(struct device *dev, unsigned int reg)
{
	switch (reg) {
	case TEGRA20_I2S_CTRL:
	case TEGRA20_I2S_STATUS:
	case TEGRA20_I2S_TIMING:
	case TEGRA20_I2S_FIFO_SCR:
	case TEGRA20_I2S_PCM_CTRL:
	case TEGRA20_I2S_NW_CTRL:
	case TEGRA20_I2S_TDM_CTRL:
	case TEGRA20_I2S_TDM_TX_RX_CTRL:
	case TEGRA20_I2S_FIFO1:
	case TEGRA20_I2S_FIFO2:
		return true;
	default:
		return false;
	};
}

static bool tegra20_i2s_volatile_reg(struct device *dev, unsigned int reg)
{
	switch (reg) {
	case TEGRA20_I2S_STATUS:
	case TEGRA20_I2S_FIFO_SCR:
	case TEGRA20_I2S_FIFO1:
	case TEGRA20_I2S_FIFO2:
		return true;
	default:
		return false;
	};
}

static bool tegra20_i2s_precious_reg(struct device *dev, unsigned int reg)
{
	switch (reg) {
	case TEGRA20_I2S_FIFO1:
	case TEGRA20_I2S_FIFO2:
		return true;
	default:
		return false;
	};
}

static const struct regmap_config tegra20_i2s_regmap_config = {
	.reg_bits = 32,
	.reg_stride = 4,
	.val_bits = 32,
	.max_register = TEGRA20_I2S_FIFO2,
	.writeable_reg = tegra20_i2s_wr_rd_reg,
	.readable_reg = tegra20_i2s_wr_rd_reg,
	.volatile_reg = tegra20_i2s_volatile_reg,
	.precious_reg = tegra20_i2s_precious_reg,
	.cache_type = REGCACHE_RBTREE,
};

334
static __devinit int tegra20_i2s_platform_probe(struct platform_device *pdev)
335
{
336
	struct tegra20_i2s *i2s;
337
	struct resource *mem, *memregion, *dmareq;
338 339
	u32 of_dma[2];
	u32 dma_ch;
340
	void __iomem *regs;
341 342
	int ret;

343
	i2s = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_i2s), GFP_KERNEL);
344
	if (!i2s) {
345
		dev_err(&pdev->dev, "Can't allocate tegra20_i2s\n");
346
		ret = -ENOMEM;
347
		goto err;
348 349 350
	}
	dev_set_drvdata(&pdev->dev, i2s);

351
	i2s->dai = tegra20_i2s_dai_template;
352 353
	i2s->dai.name = dev_name(&pdev->dev);

354
	i2s->clk_i2s = clk_get(&pdev->dev, NULL);
355
	if (IS_ERR(i2s->clk_i2s)) {
356
		dev_err(&pdev->dev, "Can't retrieve i2s clock\n");
357
		ret = PTR_ERR(i2s->clk_i2s);
358
		goto err;
359 360 361 362 363 364 365 366 367 368 369
	}

	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!mem) {
		dev_err(&pdev->dev, "No memory resource\n");
		ret = -ENODEV;
		goto err_clk_put;
	}

	dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0);
	if (!dmareq) {
370 371 372 373 374 375 376 377 378 379
		if (of_property_read_u32_array(pdev->dev.of_node,
					"nvidia,dma-request-selector",
					of_dma, 2) < 0) {
			dev_err(&pdev->dev, "No DMA resource\n");
			ret = -ENODEV;
			goto err_clk_put;
		}
		dma_ch = of_dma[1];
	} else {
		dma_ch = dmareq->start;
380 381
	}

382 383
	memregion = devm_request_mem_region(&pdev->dev, mem->start,
					    resource_size(mem), DRV_NAME);
384 385 386 387 388 389
	if (!memregion) {
		dev_err(&pdev->dev, "Memory region already claimed\n");
		ret = -EBUSY;
		goto err_clk_put;
	}

390 391
	regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
	if (!regs) {
392 393
		dev_err(&pdev->dev, "ioremap failed\n");
		ret = -ENOMEM;
394
		goto err_clk_put;
395 396
	}

397 398 399 400 401 402 403 404
	i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
					    &tegra20_i2s_regmap_config);
	if (IS_ERR(i2s->regmap)) {
		dev_err(&pdev->dev, "regmap init failed\n");
		ret = PTR_ERR(i2s->regmap);
		goto err_clk_put;
	}

405
	i2s->capture_dma_data.addr = mem->start + TEGRA20_I2S_FIFO2;
406 407
	i2s->capture_dma_data.wrap = 4;
	i2s->capture_dma_data.width = 32;
408
	i2s->capture_dma_data.req_sel = dma_ch;
409

410
	i2s->playback_dma_data.addr = mem->start + TEGRA20_I2S_FIFO1;
411 412
	i2s->playback_dma_data.wrap = 4;
	i2s->playback_dma_data.width = 32;
413
	i2s->playback_dma_data.req_sel = dma_ch;
414

415 416 417 418 419 420 421
	pm_runtime_enable(&pdev->dev);
	if (!pm_runtime_enabled(&pdev->dev)) {
		ret = tegra20_i2s_runtime_resume(&pdev->dev);
		if (ret)
			goto err_pm_disable;
	}

422
	ret = snd_soc_register_dai(&pdev->dev, &i2s->dai);
423 424 425
	if (ret) {
		dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
		ret = -ENOMEM;
426
		goto err_suspend;
427 428
	}

429 430 431 432 433 434
	ret = tegra_pcm_platform_register(&pdev->dev);
	if (ret) {
		dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);
		goto err_unregister_dai;
	}

435 436
	return 0;

437 438
err_unregister_dai:
	snd_soc_unregister_dai(&pdev->dev);
439 440 441 442 443
err_suspend:
	if (!pm_runtime_status_suspended(&pdev->dev))
		tegra20_i2s_runtime_suspend(&pdev->dev);
err_pm_disable:
	pm_runtime_disable(&pdev->dev);
444 445
err_clk_put:
	clk_put(i2s->clk_i2s);
446
err:
447 448 449
	return ret;
}

450
static int __devexit tegra20_i2s_platform_remove(struct platform_device *pdev)
451
{
452
	struct tegra20_i2s *i2s = dev_get_drvdata(&pdev->dev);
453

454 455 456 457
	pm_runtime_disable(&pdev->dev);
	if (!pm_runtime_status_suspended(&pdev->dev))
		tegra20_i2s_runtime_suspend(&pdev->dev);

458
	tegra_pcm_platform_unregister(&pdev->dev);
459 460 461 462 463 464 465
	snd_soc_unregister_dai(&pdev->dev);

	clk_put(i2s->clk_i2s);

	return 0;
}

466
static const struct of_device_id tegra20_i2s_of_match[] = {
467 468 469 470
	{ .compatible = "nvidia,tegra20-i2s", },
	{},
};

471
static const struct dev_pm_ops tegra20_i2s_pm_ops = {
472 473 474 475
	SET_RUNTIME_PM_OPS(tegra20_i2s_runtime_suspend,
			   tegra20_i2s_runtime_resume, NULL)
};

476
static struct platform_driver tegra20_i2s_driver = {
477 478 479
	.driver = {
		.name = DRV_NAME,
		.owner = THIS_MODULE,
480
		.of_match_table = tegra20_i2s_of_match,
481
		.pm = &tegra20_i2s_pm_ops,
482
	},
483 484
	.probe = tegra20_i2s_platform_probe,
	.remove = __devexit_p(tegra20_i2s_platform_remove),
485
};
486
module_platform_driver(tegra20_i2s_driver);
487 488

MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
489
MODULE_DESCRIPTION("Tegra20 I2S ASoC driver");
490
MODULE_LICENSE("GPL");
S
Stephen Warren 已提交
491
MODULE_ALIAS("platform:" DRV_NAME);
492
MODULE_DEVICE_TABLE(of, tegra20_i2s_of_match);