aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/fsl/fsl_ssi.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/fsl/fsl_ssi.c')
-rw-r--r--sound/soc/fsl/fsl_ssi.c298
1 files changed, 184 insertions, 114 deletions
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index 762c1b8e8e4e..4cc167a7aeb8 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -3,10 +3,11 @@
3 * 3 *
4 * Author: Timur Tabi <timur@freescale.com> 4 * Author: Timur Tabi <timur@freescale.com>
5 * 5 *
6 * Copyright 2007-2008 Freescale Semiconductor, Inc. This file is licensed 6 * Copyright 2007-2010 Freescale Semiconductor, Inc.
7 * under the terms of the GNU General Public License version 2. This 7 *
8 * program is licensed "as is" without any warranty of any kind, whether 8 * This file is licensed under the terms of the GNU General Public License
9 * express or implied. 9 * version 2. This program is licensed "as is" without any warranty of any
10 * kind, whether express or implied.
10 */ 11 */
11 12
12#include <linux/init.h> 13#include <linux/init.h>
@@ -15,6 +16,7 @@
15#include <linux/device.h> 16#include <linux/device.h>
16#include <linux/delay.h> 17#include <linux/delay.h>
17#include <linux/slab.h> 18#include <linux/slab.h>
19#include <linux/of_platform.h>
18 20
19#include <sound/core.h> 21#include <sound/core.h>
20#include <sound/pcm.h> 22#include <sound/pcm.h>
@@ -22,8 +24,6 @@
22#include <sound/initval.h> 24#include <sound/initval.h>
23#include <sound/soc.h> 25#include <sound/soc.h>
24 26
25#include <asm/immap_86xx.h>
26
27#include "fsl_ssi.h" 27#include "fsl_ssi.h"
28 28
29/** 29/**
@@ -71,33 +71,32 @@
71/** 71/**
72 * fsl_ssi_private: per-SSI private data 72 * fsl_ssi_private: per-SSI private data
73 * 73 *
74 * @name: short name for this device ("SSI0", "SSI1", etc)
75 * @ssi: pointer to the SSI's registers 74 * @ssi: pointer to the SSI's registers
76 * @ssi_phys: physical address of the SSI registers 75 * @ssi_phys: physical address of the SSI registers
77 * @irq: IRQ of this SSI 76 * @irq: IRQ of this SSI
78 * @first_stream: pointer to the stream that was opened first 77 * @first_stream: pointer to the stream that was opened first
79 * @second_stream: pointer to second stream 78 * @second_stream: pointer to second stream
80 * @dev: struct device pointer
81 * @playback: the number of playback streams opened 79 * @playback: the number of playback streams opened
82 * @capture: the number of capture streams opened 80 * @capture: the number of capture streams opened
83 * @asynchronous: 0=synchronous mode, 1=asynchronous mode 81 * @asynchronous: 0=synchronous mode, 1=asynchronous mode
84 * @cpu_dai: the CPU DAI for this device 82 * @cpu_dai: the CPU DAI for this device
85 * @dev_attr: the sysfs device attribute structure 83 * @dev_attr: the sysfs device attribute structure
86 * @stats: SSI statistics 84 * @stats: SSI statistics
85 * @name: name for this device
87 */ 86 */
88struct fsl_ssi_private { 87struct fsl_ssi_private {
89 char name[8];
90 struct ccsr_ssi __iomem *ssi; 88 struct ccsr_ssi __iomem *ssi;
91 dma_addr_t ssi_phys; 89 dma_addr_t ssi_phys;
92 unsigned int irq; 90 unsigned int irq;
93 struct snd_pcm_substream *first_stream; 91 struct snd_pcm_substream *first_stream;
94 struct snd_pcm_substream *second_stream; 92 struct snd_pcm_substream *second_stream;
95 struct device *dev;
96 unsigned int playback; 93 unsigned int playback;
97 unsigned int capture; 94 unsigned int capture;
98 int asynchronous; 95 int asynchronous;
99 struct snd_soc_dai cpu_dai; 96 unsigned int fifo_depth;
97 struct snd_soc_dai_driver cpu_dai_drv;
100 struct device_attribute dev_attr; 98 struct device_attribute dev_attr;
99 struct platform_device *pdev;
101 100
102 struct { 101 struct {
103 unsigned int rfrc; 102 unsigned int rfrc;
@@ -122,6 +121,8 @@ struct fsl_ssi_private {
122 unsigned int tfe1; 121 unsigned int tfe1;
123 unsigned int tfe0; 122 unsigned int tfe0;
124 } stats; 123 } stats;
124
125 char name[1];
125}; 126};
126 127
127/** 128/**
@@ -280,7 +281,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
280 struct snd_soc_dai *dai) 281 struct snd_soc_dai *dai)
281{ 282{
282 struct snd_soc_pcm_runtime *rtd = substream->private_data; 283 struct snd_soc_pcm_runtime *rtd = substream->private_data;
283 struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data; 284 struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai);
284 285
285 /* 286 /*
286 * If this is the first stream opened, then request the IRQ 287 * If this is the first stream opened, then request the IRQ
@@ -290,6 +291,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
290 struct ccsr_ssi __iomem *ssi = ssi_private->ssi; 291 struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
291 int ret; 292 int ret;
292 293
294 /* The 'name' should not have any slashes in it. */
293 ret = request_irq(ssi_private->irq, fsl_ssi_isr, 0, 295 ret = request_irq(ssi_private->irq, fsl_ssi_isr, 0,
294 ssi_private->name, ssi_private); 296 ssi_private->name, ssi_private);
295 if (ret < 0) { 297 if (ret < 0) {
@@ -336,11 +338,20 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
336 338
337 /* 339 /*
338 * Set the watermark for transmit FIFI 0 and receive FIFO 0. We 340 * Set the watermark for transmit FIFI 0 and receive FIFO 0. We
339 * don't use FIFO 1. Since the SSI only supports stereo, the 341 * don't use FIFO 1. We program the transmit water to signal a
340 * watermark should never be an odd number. 342 * DMA transfer if there are only two (or fewer) elements left
343 * in the FIFO. Two elements equals one frame (left channel,
344 * right channel). This value, however, depends on the depth of
345 * the transmit buffer.
346 *
347 * We program the receive FIFO to notify us if at least two
348 * elements (one frame) have been written to the FIFO. We could
349 * make this value larger (and maybe we should), but this way
350 * data will be written to memory as soon as it's available.
341 */ 351 */
342 out_be32(&ssi->sfcsr, 352 out_be32(&ssi->sfcsr,
343 CCSR_SSI_SFCSR_TFWM0(6) | CCSR_SSI_SFCSR_RFWM0(2)); 353 CCSR_SSI_SFCSR_TFWM0(ssi_private->fifo_depth - 2) |
354 CCSR_SSI_SFCSR_RFWM0(ssi_private->fifo_depth - 2));
344 355
345 /* 356 /*
346 * We keep the SSI disabled because if we enable it, then the 357 * We keep the SSI disabled because if we enable it, then the
@@ -422,7 +433,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
422static int fsl_ssi_hw_params(struct snd_pcm_substream *substream, 433static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
423 struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *cpu_dai) 434 struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *cpu_dai)
424{ 435{
425 struct fsl_ssi_private *ssi_private = cpu_dai->private_data; 436 struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
426 437
427 if (substream == ssi_private->first_stream) { 438 if (substream == ssi_private->first_stream) {
428 struct ccsr_ssi __iomem *ssi = ssi_private->ssi; 439 struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
@@ -458,7 +469,7 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
458 struct snd_soc_dai *dai) 469 struct snd_soc_dai *dai)
459{ 470{
460 struct snd_soc_pcm_runtime *rtd = substream->private_data; 471 struct snd_soc_pcm_runtime *rtd = substream->private_data;
461 struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data; 472 struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai);
462 struct ccsr_ssi __iomem *ssi = ssi_private->ssi; 473 struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
463 474
464 switch (cmd) { 475 switch (cmd) {
@@ -497,7 +508,7 @@ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream,
497 struct snd_soc_dai *dai) 508 struct snd_soc_dai *dai)
498{ 509{
499 struct snd_soc_pcm_runtime *rtd = substream->private_data; 510 struct snd_soc_pcm_runtime *rtd = substream->private_data;
500 struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data; 511 struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai);
501 512
502 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 513 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
503 ssi_private->playback--; 514 ssi_private->playback--;
@@ -523,56 +534,15 @@ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream,
523 } 534 }
524} 535}
525 536
526/**
527 * fsl_ssi_set_sysclk: set the clock frequency and direction
528 *
529 * This function is called by the machine driver to tell us what the clock
530 * frequency and direction are.
531 *
532 * Currently, we only support operating as a clock slave (SND_SOC_CLOCK_IN),
533 * and we don't care about the frequency. Return an error if the direction
534 * is not SND_SOC_CLOCK_IN.
535 *
536 * @clk_id: reserved, should be zero
537 * @freq: the frequency of the given clock ID, currently ignored
538 * @dir: SND_SOC_CLOCK_IN (clock slave) or SND_SOC_CLOCK_OUT (clock master)
539 */
540static int fsl_ssi_set_sysclk(struct snd_soc_dai *cpu_dai,
541 int clk_id, unsigned int freq, int dir)
542{
543
544 return (dir == SND_SOC_CLOCK_IN) ? 0 : -EINVAL;
545}
546
547/**
548 * fsl_ssi_set_fmt: set the serial format.
549 *
550 * This function is called by the machine driver to tell us what serial
551 * format to use.
552 *
553 * Currently, we only support I2S mode. Return an error if the format is
554 * not SND_SOC_DAIFMT_I2S.
555 *
556 * @format: one of SND_SOC_DAIFMT_xxx
557 */
558static int fsl_ssi_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format)
559{
560 return (format == SND_SOC_DAIFMT_I2S) ? 0 : -EINVAL;
561}
562
563/**
564 * fsl_ssi_dai_template: template CPU DAI for the SSI
565 */
566static struct snd_soc_dai_ops fsl_ssi_dai_ops = { 537static struct snd_soc_dai_ops fsl_ssi_dai_ops = {
567 .startup = fsl_ssi_startup, 538 .startup = fsl_ssi_startup,
568 .hw_params = fsl_ssi_hw_params, 539 .hw_params = fsl_ssi_hw_params,
569 .shutdown = fsl_ssi_shutdown, 540 .shutdown = fsl_ssi_shutdown,
570 .trigger = fsl_ssi_trigger, 541 .trigger = fsl_ssi_trigger,
571 .set_sysclk = fsl_ssi_set_sysclk,
572 .set_fmt = fsl_ssi_set_fmt,
573}; 542};
574 543
575static struct snd_soc_dai fsl_ssi_dai_template = { 544/* Template for the CPU dai driver structure */
545static struct snd_soc_dai_driver fsl_ssi_dai_template = {
576 .playback = { 546 .playback = {
577 /* The SSI does not support monaural audio. */ 547 /* The SSI does not support monaural audio. */
578 .channels_min = 2, 548 .channels_min = 2,
@@ -640,95 +610,195 @@ static ssize_t fsl_sysfs_ssi_show(struct device *dev,
640} 610}
641 611
642/** 612/**
643 * fsl_ssi_create_dai: create a snd_soc_dai structure 613 * Make every character in a string lower-case
644 *
645 * This function is called by the machine driver to create a snd_soc_dai
646 * structure. The function creates an ssi_private object, which contains
647 * the snd_soc_dai. It also creates the sysfs statistics device.
648 */ 614 */
649struct snd_soc_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info) 615static void make_lowercase(char *s)
616{
617 char *p = s;
618 char c;
619
620 while ((c = *p)) {
621 if ((c >= 'A') && (c <= 'Z'))
622 *p = c + ('a' - 'A');
623 p++;
624 }
625}
626
627static int __devinit fsl_ssi_probe(struct platform_device *pdev,
628 const struct of_device_id *match)
650{ 629{
651 struct snd_soc_dai *fsl_ssi_dai;
652 struct fsl_ssi_private *ssi_private; 630 struct fsl_ssi_private *ssi_private;
653 int ret = 0; 631 int ret = 0;
654 struct device_attribute *dev_attr; 632 struct device_attribute *dev_attr = NULL;
633 struct device_node *np = pdev->dev.of_node;
634 const char *p, *sprop;
635 const uint32_t *iprop;
636 struct resource res;
637 char name[64];
638
639 /* SSIs that are not connected on the board should have a
640 * status = "disabled"
641 * property in their device tree nodes.
642 */
643 if (!of_device_is_available(np))
644 return -ENODEV;
645
646 /* Check for a codec-handle property. */
647 if (!of_get_property(np, "codec-handle", NULL)) {
648 dev_err(&pdev->dev, "missing codec-handle property\n");
649 return -ENODEV;
650 }
655 651
656 ssi_private = kzalloc(sizeof(struct fsl_ssi_private), GFP_KERNEL); 652 /* We only support the SSI in "I2S Slave" mode */
653 sprop = of_get_property(np, "fsl,mode", NULL);
654 if (!sprop || strcmp(sprop, "i2s-slave")) {
655 dev_notice(&pdev->dev, "mode %s is unsupported\n", sprop);
656 return -ENODEV;
657 }
658
659 /* The DAI name is the last part of the full name of the node. */
660 p = strrchr(np->full_name, '/') + 1;
661 ssi_private = kzalloc(sizeof(struct fsl_ssi_private) + strlen(p),
662 GFP_KERNEL);
657 if (!ssi_private) { 663 if (!ssi_private) {
658 dev_err(ssi_info->dev, "could not allocate DAI object\n"); 664 dev_err(&pdev->dev, "could not allocate DAI object\n");
659 return NULL; 665 return -ENOMEM;
660 } 666 }
661 memcpy(&ssi_private->cpu_dai, &fsl_ssi_dai_template,
662 sizeof(struct snd_soc_dai));
663 667
664 fsl_ssi_dai = &ssi_private->cpu_dai; 668 strcpy(ssi_private->name, p);
665 dev_attr = &ssi_private->dev_attr;
666 669
667 sprintf(ssi_private->name, "ssi%u", (u8) ssi_info->id); 670 /* Initialize this copy of the CPU DAI driver structure */
668 ssi_private->ssi = ssi_info->ssi; 671 memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_dai_template,
669 ssi_private->ssi_phys = ssi_info->ssi_phys; 672 sizeof(fsl_ssi_dai_template));
670 ssi_private->irq = ssi_info->irq; 673 ssi_private->cpu_dai_drv.name = ssi_private->name;
671 ssi_private->dev = ssi_info->dev;
672 ssi_private->asynchronous = ssi_info->asynchronous;
673 674
674 dev_set_drvdata(ssi_private->dev, fsl_ssi_dai); 675 /* Get the addresses and IRQ */
676 ret = of_address_to_resource(np, 0, &res);
677 if (ret) {
678 dev_err(&pdev->dev, "could not determine device resources\n");
679 kfree(ssi_private);
680 return ret;
681 }
682 ssi_private->ssi = ioremap(res.start, 1 + res.end - res.start);
683 ssi_private->ssi_phys = res.start;
684 ssi_private->irq = irq_of_parse_and_map(np, 0);
685
686 /* Are the RX and the TX clocks locked? */
687 if (of_find_property(np, "fsl,ssi-asynchronous", NULL))
688 ssi_private->asynchronous = 1;
689 else
690 ssi_private->cpu_dai_drv.symmetric_rates = 1;
691
692 /* Determine the FIFO depth. */
693 iprop = of_get_property(np, "fsl,fifo-depth", NULL);
694 if (iprop)
695 ssi_private->fifo_depth = *iprop;
696 else
697 /* Older 8610 DTs didn't have the fifo-depth property */
698 ssi_private->fifo_depth = 8;
675 699
676 /* Initialize the the device_attribute structure */ 700 /* Initialize the the device_attribute structure */
677 dev_attr->attr.name = "ssi-stats"; 701 dev_attr = &ssi_private->dev_attr;
702 dev_attr->attr.name = "statistics";
678 dev_attr->attr.mode = S_IRUGO; 703 dev_attr->attr.mode = S_IRUGO;
679 dev_attr->show = fsl_sysfs_ssi_show; 704 dev_attr->show = fsl_sysfs_ssi_show;
680 705
681 ret = device_create_file(ssi_private->dev, dev_attr); 706 ret = device_create_file(&pdev->dev, dev_attr);
682 if (ret) { 707 if (ret) {
683 dev_err(ssi_info->dev, "could not create sysfs %s file\n", 708 dev_err(&pdev->dev, "could not create sysfs %s file\n",
684 ssi_private->dev_attr.attr.name); 709 ssi_private->dev_attr.attr.name);
685 kfree(fsl_ssi_dai); 710 goto error;
686 return NULL;
687 } 711 }
688 712
689 fsl_ssi_dai->private_data = ssi_private; 713 /* Register with ASoC */
690 fsl_ssi_dai->name = ssi_private->name; 714 dev_set_drvdata(&pdev->dev, ssi_private);
691 fsl_ssi_dai->id = ssi_info->id; 715
692 fsl_ssi_dai->dev = ssi_info->dev; 716 ret = snd_soc_register_dai(&pdev->dev, &ssi_private->cpu_dai_drv);
693 fsl_ssi_dai->symmetric_rates = 1; 717 if (ret) {
718 dev_err(&pdev->dev, "failed to register DAI: %d\n", ret);
719 goto error;
720 }
694 721
695 ret = snd_soc_register_dai(fsl_ssi_dai); 722 /* Trigger the machine driver's probe function. The platform driver
696 if (ret != 0) { 723 * name of the machine driver is taken from the /model property of the
697 dev_err(ssi_info->dev, "failed to register DAI: %d\n", ret); 724 * device tree. We also pass the address of the CPU DAI driver
698 kfree(fsl_ssi_dai); 725 * structure.
699 return NULL; 726 */
727 sprop = of_get_property(of_find_node_by_path("/"), "model", NULL);
728 /* Sometimes the model name has a "fsl," prefix, so we strip that. */
729 p = strrchr(sprop, ',');
730 if (p)
731 sprop = p + 1;
732 snprintf(name, sizeof(name), "snd-soc-%s", sprop);
733 make_lowercase(name);
734
735 ssi_private->pdev =
736 platform_device_register_data(&pdev->dev, name, 0, NULL, 0);
737 if (IS_ERR(ssi_private->pdev)) {
738 ret = PTR_ERR(ssi_private->pdev);
739 dev_err(&pdev->dev, "failed to register platform: %d\n", ret);
740 goto error;
700 } 741 }
701 742
702 return fsl_ssi_dai; 743 return 0;
744
745error:
746 snd_soc_unregister_dai(&pdev->dev);
747 dev_set_drvdata(&pdev->dev, NULL);
748 if (dev_attr)
749 device_remove_file(&pdev->dev, dev_attr);
750 irq_dispose_mapping(ssi_private->irq);
751 iounmap(ssi_private->ssi);
752 kfree(ssi_private);
753
754 return ret;
703} 755}
704EXPORT_SYMBOL_GPL(fsl_ssi_create_dai);
705 756
706/** 757static int fsl_ssi_remove(struct platform_device *pdev)
707 * fsl_ssi_destroy_dai: destroy the snd_soc_dai object
708 *
709 * This function undoes the operations of fsl_ssi_create_dai()
710 */
711void fsl_ssi_destroy_dai(struct snd_soc_dai *fsl_ssi_dai)
712{ 758{
713 struct fsl_ssi_private *ssi_private = 759 struct fsl_ssi_private *ssi_private = dev_get_drvdata(&pdev->dev);
714 container_of(fsl_ssi_dai, struct fsl_ssi_private, cpu_dai);
715
716 device_remove_file(ssi_private->dev, &ssi_private->dev_attr);
717 760
718 snd_soc_unregister_dai(&ssi_private->cpu_dai); 761 platform_device_unregister(ssi_private->pdev);
762 snd_soc_unregister_dai(&pdev->dev);
763 device_remove_file(&pdev->dev, &ssi_private->dev_attr);
719 764
720 kfree(ssi_private); 765 kfree(ssi_private);
766 dev_set_drvdata(&pdev->dev, NULL);
767
768 return 0;
721} 769}
722EXPORT_SYMBOL_GPL(fsl_ssi_destroy_dai); 770
771static const struct of_device_id fsl_ssi_ids[] = {
772 { .compatible = "fsl,mpc8610-ssi", },
773 {}
774};
775MODULE_DEVICE_TABLE(of, fsl_ssi_ids);
776
777static struct of_platform_driver fsl_ssi_driver = {
778 .driver = {
779 .name = "fsl-ssi-dai",
780 .owner = THIS_MODULE,
781 .of_match_table = fsl_ssi_ids,
782 },
783 .probe = fsl_ssi_probe,
784 .remove = fsl_ssi_remove,
785};
723 786
724static int __init fsl_ssi_init(void) 787static int __init fsl_ssi_init(void)
725{ 788{
726 printk(KERN_INFO "Freescale Synchronous Serial Interface (SSI) ASoC Driver\n"); 789 printk(KERN_INFO "Freescale Synchronous Serial Interface (SSI) ASoC Driver\n");
727 790
728 return 0; 791 return of_register_platform_driver(&fsl_ssi_driver);
729} 792}
793
794static void __exit fsl_ssi_exit(void)
795{
796 of_unregister_platform_driver(&fsl_ssi_driver);
797}
798
730module_init(fsl_ssi_init); 799module_init(fsl_ssi_init);
800module_exit(fsl_ssi_exit);
731 801
732MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); 802MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
733MODULE_DESCRIPTION("Freescale Synchronous Serial Interface (SSI) ASoC Driver"); 803MODULE_DESCRIPTION("Freescale Synchronous Serial Interface (SSI) ASoC Driver");
734MODULE_LICENSE("GPL"); 804MODULE_LICENSE("GPL v2");