diff options
Diffstat (limited to 'sound/soc/tegra/tegra_i2s.c')
-rw-r--r-- | sound/soc/tegra/tegra_i2s.c | 164 |
1 files changed, 56 insertions, 108 deletions
diff --git a/sound/soc/tegra/tegra_i2s.c b/sound/soc/tegra/tegra_i2s.c index 6728fab8c41..33509de5254 100644 --- a/sound/soc/tegra/tegra_i2s.c +++ b/sound/soc/tegra/tegra_i2s.c | |||
@@ -36,13 +36,13 @@ | |||
36 | #include <linux/seq_file.h> | 36 | #include <linux/seq_file.h> |
37 | #include <linux/slab.h> | 37 | #include <linux/slab.h> |
38 | #include <linux/io.h> | 38 | #include <linux/io.h> |
39 | #include <linux/of.h> | ||
39 | #include <mach/iomap.h> | 40 | #include <mach/iomap.h> |
40 | #include <sound/core.h> | 41 | #include <sound/core.h> |
41 | #include <sound/pcm.h> | 42 | #include <sound/pcm.h> |
42 | #include <sound/pcm_params.h> | 43 | #include <sound/pcm_params.h> |
43 | #include <sound/soc.h> | 44 | #include <sound/soc.h> |
44 | 45 | ||
45 | #include "tegra_das.h" | ||
46 | #include "tegra_i2s.h" | 46 | #include "tegra_i2s.h" |
47 | 47 | ||
48 | #define DRV_NAME "tegra-i2s" | 48 | #define DRV_NAME "tegra-i2s" |
@@ -99,13 +99,11 @@ static const struct file_operations tegra_i2s_debug_fops = { | |||
99 | .release = single_release, | 99 | .release = single_release, |
100 | }; | 100 | }; |
101 | 101 | ||
102 | static void tegra_i2s_debug_add(struct tegra_i2s *i2s, int id) | 102 | static void tegra_i2s_debug_add(struct tegra_i2s *i2s) |
103 | { | 103 | { |
104 | char name[] = DRV_NAME ".0"; | 104 | i2s->debug = debugfs_create_file(i2s->dai.name, S_IRUGO, |
105 | 105 | snd_soc_debugfs_root, i2s, | |
106 | snprintf(name, sizeof(name), DRV_NAME".%1d", id); | 106 | &tegra_i2s_debug_fops); |
107 | i2s->debug = debugfs_create_file(name, S_IRUGO, snd_soc_debugfs_root, | ||
108 | i2s, &tegra_i2s_debug_fops); | ||
109 | } | 107 | } |
110 | 108 | ||
111 | static void tegra_i2s_debug_remove(struct tegra_i2s *i2s) | 109 | static void tegra_i2s_debug_remove(struct tegra_i2s *i2s) |
@@ -306,93 +304,54 @@ static int tegra_i2s_probe(struct snd_soc_dai *dai) | |||
306 | return 0; | 304 | return 0; |
307 | } | 305 | } |
308 | 306 | ||
309 | static struct snd_soc_dai_ops tegra_i2s_dai_ops = { | 307 | static const struct snd_soc_dai_ops tegra_i2s_dai_ops = { |
310 | .set_fmt = tegra_i2s_set_fmt, | 308 | .set_fmt = tegra_i2s_set_fmt, |
311 | .hw_params = tegra_i2s_hw_params, | 309 | .hw_params = tegra_i2s_hw_params, |
312 | .trigger = tegra_i2s_trigger, | 310 | .trigger = tegra_i2s_trigger, |
313 | }; | 311 | }; |
314 | 312 | ||
315 | static struct snd_soc_dai_driver tegra_i2s_dai[] = { | 313 | static const struct snd_soc_dai_driver tegra_i2s_dai_template = { |
316 | { | 314 | .probe = tegra_i2s_probe, |
317 | .name = DRV_NAME ".0", | 315 | .playback = { |
318 | .probe = tegra_i2s_probe, | 316 | .channels_min = 2, |
319 | .playback = { | 317 | .channels_max = 2, |
320 | .channels_min = 2, | 318 | .rates = SNDRV_PCM_RATE_8000_96000, |
321 | .channels_max = 2, | 319 | .formats = SNDRV_PCM_FMTBIT_S16_LE, |
322 | .rates = SNDRV_PCM_RATE_8000_96000, | ||
323 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
324 | }, | ||
325 | .capture = { | ||
326 | .channels_min = 2, | ||
327 | .channels_max = 2, | ||
328 | .rates = SNDRV_PCM_RATE_8000_96000, | ||
329 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
330 | }, | ||
331 | .ops = &tegra_i2s_dai_ops, | ||
332 | .symmetric_rates = 1, | ||
333 | }, | 320 | }, |
334 | { | 321 | .capture = { |
335 | .name = DRV_NAME ".1", | 322 | .channels_min = 2, |
336 | .probe = tegra_i2s_probe, | 323 | .channels_max = 2, |
337 | .playback = { | 324 | .rates = SNDRV_PCM_RATE_8000_96000, |
338 | .channels_min = 2, | 325 | .formats = SNDRV_PCM_FMTBIT_S16_LE, |
339 | .channels_max = 2, | ||
340 | .rates = SNDRV_PCM_RATE_8000_96000, | ||
341 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
342 | }, | ||
343 | .capture = { | ||
344 | .channels_min = 2, | ||
345 | .channels_max = 2, | ||
346 | .rates = SNDRV_PCM_RATE_8000_96000, | ||
347 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
348 | }, | ||
349 | .ops = &tegra_i2s_dai_ops, | ||
350 | .symmetric_rates = 1, | ||
351 | }, | 326 | }, |
327 | .ops = &tegra_i2s_dai_ops, | ||
328 | .symmetric_rates = 1, | ||
352 | }; | 329 | }; |
353 | 330 | ||
354 | static __devinit int tegra_i2s_platform_probe(struct platform_device *pdev) | 331 | static __devinit int tegra_i2s_platform_probe(struct platform_device *pdev) |
355 | { | 332 | { |
356 | struct tegra_i2s * i2s; | 333 | struct tegra_i2s * i2s; |
357 | struct resource *mem, *memregion, *dmareq; | 334 | struct resource *mem, *memregion, *dmareq; |
335 | u32 of_dma[2]; | ||
336 | u32 dma_ch; | ||
358 | int ret; | 337 | int ret; |
359 | 338 | ||
360 | if ((pdev->id < 0) || | 339 | i2s = devm_kzalloc(&pdev->dev, sizeof(struct tegra_i2s), GFP_KERNEL); |
361 | (pdev->id >= ARRAY_SIZE(tegra_i2s_dai))) { | ||
362 | dev_err(&pdev->dev, "ID %d out of range\n", pdev->id); | ||
363 | return -EINVAL; | ||
364 | } | ||
365 | |||
366 | /* | ||
367 | * FIXME: Until a codec driver exists for the tegra DAS, hard-code a | ||
368 | * 1:1 mapping between audio controllers and audio ports. | ||
369 | */ | ||
370 | ret = tegra_das_connect_dap_to_dac(TEGRA_DAS_DAP_ID_1 + pdev->id, | ||
371 | TEGRA_DAS_DAP_SEL_DAC1 + pdev->id); | ||
372 | if (ret) { | ||
373 | dev_err(&pdev->dev, "Can't set up DAP connection\n"); | ||
374 | return ret; | ||
375 | } | ||
376 | ret = tegra_das_connect_dac_to_dap(TEGRA_DAS_DAC_ID_1 + pdev->id, | ||
377 | TEGRA_DAS_DAC_SEL_DAP1 + pdev->id); | ||
378 | if (ret) { | ||
379 | dev_err(&pdev->dev, "Can't set up DAC connection\n"); | ||
380 | return ret; | ||
381 | } | ||
382 | |||
383 | i2s = kzalloc(sizeof(struct tegra_i2s), GFP_KERNEL); | ||
384 | if (!i2s) { | 340 | if (!i2s) { |
385 | dev_err(&pdev->dev, "Can't allocate tegra_i2s\n"); | 341 | dev_err(&pdev->dev, "Can't allocate tegra_i2s\n"); |
386 | ret = -ENOMEM; | 342 | ret = -ENOMEM; |
387 | goto exit; | 343 | goto err; |
388 | } | 344 | } |
389 | dev_set_drvdata(&pdev->dev, i2s); | 345 | dev_set_drvdata(&pdev->dev, i2s); |
390 | 346 | ||
347 | i2s->dai = tegra_i2s_dai_template; | ||
348 | i2s->dai.name = dev_name(&pdev->dev); | ||
349 | |||
391 | i2s->clk_i2s = clk_get(&pdev->dev, NULL); | 350 | i2s->clk_i2s = clk_get(&pdev->dev, NULL); |
392 | if (IS_ERR(i2s->clk_i2s)) { | 351 | if (IS_ERR(i2s->clk_i2s)) { |
393 | dev_err(&pdev->dev, "Can't retrieve i2s clock\n"); | 352 | dev_err(&pdev->dev, "Can't retrieve i2s clock\n"); |
394 | ret = PTR_ERR(i2s->clk_i2s); | 353 | ret = PTR_ERR(i2s->clk_i2s); |
395 | goto err_free; | 354 | goto err; |
396 | } | 355 | } |
397 | 356 | ||
398 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 357 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
@@ -404,104 +363,93 @@ static __devinit int tegra_i2s_platform_probe(struct platform_device *pdev) | |||
404 | 363 | ||
405 | dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0); | 364 | dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0); |
406 | if (!dmareq) { | 365 | if (!dmareq) { |
407 | dev_err(&pdev->dev, "No DMA resource\n"); | 366 | if (of_property_read_u32_array(pdev->dev.of_node, |
408 | ret = -ENODEV; | 367 | "nvidia,dma-request-selector", |
409 | goto err_clk_put; | 368 | of_dma, 2) < 0) { |
369 | dev_err(&pdev->dev, "No DMA resource\n"); | ||
370 | ret = -ENODEV; | ||
371 | goto err_clk_put; | ||
372 | } | ||
373 | dma_ch = of_dma[1]; | ||
374 | } else { | ||
375 | dma_ch = dmareq->start; | ||
410 | } | 376 | } |
411 | 377 | ||
412 | memregion = request_mem_region(mem->start, resource_size(mem), | 378 | memregion = devm_request_mem_region(&pdev->dev, mem->start, |
413 | DRV_NAME); | 379 | resource_size(mem), DRV_NAME); |
414 | if (!memregion) { | 380 | if (!memregion) { |
415 | dev_err(&pdev->dev, "Memory region already claimed\n"); | 381 | dev_err(&pdev->dev, "Memory region already claimed\n"); |
416 | ret = -EBUSY; | 382 | ret = -EBUSY; |
417 | goto err_clk_put; | 383 | goto err_clk_put; |
418 | } | 384 | } |
419 | 385 | ||
420 | i2s->regs = ioremap(mem->start, resource_size(mem)); | 386 | i2s->regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem)); |
421 | if (!i2s->regs) { | 387 | if (!i2s->regs) { |
422 | dev_err(&pdev->dev, "ioremap failed\n"); | 388 | dev_err(&pdev->dev, "ioremap failed\n"); |
423 | ret = -ENOMEM; | 389 | ret = -ENOMEM; |
424 | goto err_release; | 390 | goto err_clk_put; |
425 | } | 391 | } |
426 | 392 | ||
427 | i2s->capture_dma_data.addr = mem->start + TEGRA_I2S_FIFO2; | 393 | i2s->capture_dma_data.addr = mem->start + TEGRA_I2S_FIFO2; |
428 | i2s->capture_dma_data.wrap = 4; | 394 | i2s->capture_dma_data.wrap = 4; |
429 | i2s->capture_dma_data.width = 32; | 395 | i2s->capture_dma_data.width = 32; |
430 | i2s->capture_dma_data.req_sel = dmareq->start; | 396 | i2s->capture_dma_data.req_sel = dma_ch; |
431 | 397 | ||
432 | i2s->playback_dma_data.addr = mem->start + TEGRA_I2S_FIFO1; | 398 | i2s->playback_dma_data.addr = mem->start + TEGRA_I2S_FIFO1; |
433 | i2s->playback_dma_data.wrap = 4; | 399 | i2s->playback_dma_data.wrap = 4; |
434 | i2s->playback_dma_data.width = 32; | 400 | i2s->playback_dma_data.width = 32; |
435 | i2s->playback_dma_data.req_sel = dmareq->start; | 401 | i2s->playback_dma_data.req_sel = dma_ch; |
436 | 402 | ||
437 | i2s->reg_ctrl = TEGRA_I2S_CTRL_FIFO_FORMAT_PACKED; | 403 | i2s->reg_ctrl = TEGRA_I2S_CTRL_FIFO_FORMAT_PACKED; |
438 | 404 | ||
439 | ret = snd_soc_register_dai(&pdev->dev, &tegra_i2s_dai[pdev->id]); | 405 | ret = snd_soc_register_dai(&pdev->dev, &i2s->dai); |
440 | if (ret) { | 406 | if (ret) { |
441 | dev_err(&pdev->dev, "Could not register DAI: %d\n", ret); | 407 | dev_err(&pdev->dev, "Could not register DAI: %d\n", ret); |
442 | ret = -ENOMEM; | 408 | ret = -ENOMEM; |
443 | goto err_unmap; | 409 | goto err_clk_put; |
444 | } | 410 | } |
445 | 411 | ||
446 | tegra_i2s_debug_add(i2s, pdev->id); | 412 | tegra_i2s_debug_add(i2s); |
447 | 413 | ||
448 | return 0; | 414 | return 0; |
449 | 415 | ||
450 | err_unmap: | ||
451 | iounmap(i2s->regs); | ||
452 | err_release: | ||
453 | release_mem_region(mem->start, resource_size(mem)); | ||
454 | err_clk_put: | 416 | err_clk_put: |
455 | clk_put(i2s->clk_i2s); | 417 | clk_put(i2s->clk_i2s); |
456 | err_free: | 418 | err: |
457 | kfree(i2s); | ||
458 | exit: | ||
459 | return ret; | 419 | return ret; |
460 | } | 420 | } |
461 | 421 | ||
462 | static int __devexit tegra_i2s_platform_remove(struct platform_device *pdev) | 422 | static int __devexit tegra_i2s_platform_remove(struct platform_device *pdev) |
463 | { | 423 | { |
464 | struct tegra_i2s *i2s = dev_get_drvdata(&pdev->dev); | 424 | struct tegra_i2s *i2s = dev_get_drvdata(&pdev->dev); |
465 | struct resource *res; | ||
466 | 425 | ||
467 | snd_soc_unregister_dai(&pdev->dev); | 426 | snd_soc_unregister_dai(&pdev->dev); |
468 | 427 | ||
469 | tegra_i2s_debug_remove(i2s); | 428 | tegra_i2s_debug_remove(i2s); |
470 | 429 | ||
471 | iounmap(i2s->regs); | ||
472 | |||
473 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
474 | release_mem_region(res->start, resource_size(res)); | ||
475 | |||
476 | clk_put(i2s->clk_i2s); | 430 | clk_put(i2s->clk_i2s); |
477 | 431 | ||
478 | kfree(i2s); | ||
479 | |||
480 | return 0; | 432 | return 0; |
481 | } | 433 | } |
482 | 434 | ||
435 | static const struct of_device_id tegra_i2s_of_match[] __devinitconst = { | ||
436 | { .compatible = "nvidia,tegra20-i2s", }, | ||
437 | {}, | ||
438 | }; | ||
439 | |||
483 | static struct platform_driver tegra_i2s_driver = { | 440 | static struct platform_driver tegra_i2s_driver = { |
484 | .driver = { | 441 | .driver = { |
485 | .name = DRV_NAME, | 442 | .name = DRV_NAME, |
486 | .owner = THIS_MODULE, | 443 | .owner = THIS_MODULE, |
444 | .of_match_table = tegra_i2s_of_match, | ||
487 | }, | 445 | }, |
488 | .probe = tegra_i2s_platform_probe, | 446 | .probe = tegra_i2s_platform_probe, |
489 | .remove = __devexit_p(tegra_i2s_platform_remove), | 447 | .remove = __devexit_p(tegra_i2s_platform_remove), |
490 | }; | 448 | }; |
491 | 449 | module_platform_driver(tegra_i2s_driver); | |
492 | static int __init snd_tegra_i2s_init(void) | ||
493 | { | ||
494 | return platform_driver_register(&tegra_i2s_driver); | ||
495 | } | ||
496 | module_init(snd_tegra_i2s_init); | ||
497 | |||
498 | static void __exit snd_tegra_i2s_exit(void) | ||
499 | { | ||
500 | platform_driver_unregister(&tegra_i2s_driver); | ||
501 | } | ||
502 | module_exit(snd_tegra_i2s_exit); | ||
503 | 450 | ||
504 | MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); | 451 | MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); |
505 | MODULE_DESCRIPTION("Tegra I2S ASoC driver"); | 452 | MODULE_DESCRIPTION("Tegra I2S ASoC driver"); |
506 | MODULE_LICENSE("GPL"); | 453 | MODULE_LICENSE("GPL"); |
507 | MODULE_ALIAS("platform:" DRV_NAME); | 454 | MODULE_ALIAS("platform:" DRV_NAME); |
455 | MODULE_DEVICE_TABLE(of, tegra_i2s_of_match); | ||