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.c249
1 files changed, 146 insertions, 103 deletions
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index 762c1b8e8e4e..64f65910a7d7 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>
@@ -71,33 +73,31 @@
71/** 73/**
72 * fsl_ssi_private: per-SSI private data 74 * fsl_ssi_private: per-SSI private data
73 * 75 *
74 * @name: short name for this device ("SSI0", "SSI1", etc)
75 * @ssi: pointer to the SSI's registers 76 * @ssi: pointer to the SSI's registers
76 * @ssi_phys: physical address of the SSI registers 77 * @ssi_phys: physical address of the SSI registers
77 * @irq: IRQ of this SSI 78 * @irq: IRQ of this SSI
78 * @first_stream: pointer to the stream that was opened first 79 * @first_stream: pointer to the stream that was opened first
79 * @second_stream: pointer to second stream 80 * @second_stream: pointer to second stream
80 * @dev: struct device pointer
81 * @playback: the number of playback streams opened 81 * @playback: the number of playback streams opened
82 * @capture: the number of capture streams opened 82 * @capture: the number of capture streams opened
83 * @asynchronous: 0=synchronous mode, 1=asynchronous mode 83 * @asynchronous: 0=synchronous mode, 1=asynchronous mode
84 * @cpu_dai: the CPU DAI for this device 84 * @cpu_dai: the CPU DAI for this device
85 * @dev_attr: the sysfs device attribute structure 85 * @dev_attr: the sysfs device attribute structure
86 * @stats: SSI statistics 86 * @stats: SSI statistics
87 * @name: name for this device
87 */ 88 */
88struct fsl_ssi_private { 89struct fsl_ssi_private {
89 char name[8];
90 struct ccsr_ssi __iomem *ssi; 90 struct ccsr_ssi __iomem *ssi;
91 dma_addr_t ssi_phys; 91 dma_addr_t ssi_phys;
92 unsigned int irq; 92 unsigned int irq;
93 struct snd_pcm_substream *first_stream; 93 struct snd_pcm_substream *first_stream;
94 struct snd_pcm_substream *second_stream; 94 struct snd_pcm_substream *second_stream;
95 struct device *dev;
96 unsigned int playback; 95 unsigned int playback;
97 unsigned int capture; 96 unsigned int capture;
98 int asynchronous; 97 int asynchronous;
99 struct snd_soc_dai cpu_dai; 98 struct snd_soc_dai_driver cpu_dai_drv;
100 struct device_attribute dev_attr; 99 struct device_attribute dev_attr;
100 struct platform_device *pdev;
101 101
102 struct { 102 struct {
103 unsigned int rfrc; 103 unsigned int rfrc;
@@ -122,6 +122,8 @@ struct fsl_ssi_private {
122 unsigned int tfe1; 122 unsigned int tfe1;
123 unsigned int tfe0; 123 unsigned int tfe0;
124 } stats; 124 } stats;
125
126 char name[1];
125}; 127};
126 128
127/** 129/**
@@ -280,7 +282,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
280 struct snd_soc_dai *dai) 282 struct snd_soc_dai *dai)
281{ 283{
282 struct snd_soc_pcm_runtime *rtd = substream->private_data; 284 struct snd_soc_pcm_runtime *rtd = substream->private_data;
283 struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data; 285 struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai);
284 286
285 /* 287 /*
286 * If this is the first stream opened, then request the IRQ 288 * If this is the first stream opened, then request the IRQ
@@ -290,6 +292,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
290 struct ccsr_ssi __iomem *ssi = ssi_private->ssi; 292 struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
291 int ret; 293 int ret;
292 294
295 /* The 'name' should not have any slashes in it. */
293 ret = request_irq(ssi_private->irq, fsl_ssi_isr, 0, 296 ret = request_irq(ssi_private->irq, fsl_ssi_isr, 0,
294 ssi_private->name, ssi_private); 297 ssi_private->name, ssi_private);
295 if (ret < 0) { 298 if (ret < 0) {
@@ -422,7 +425,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
422static int fsl_ssi_hw_params(struct snd_pcm_substream *substream, 425static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
423 struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *cpu_dai) 426 struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *cpu_dai)
424{ 427{
425 struct fsl_ssi_private *ssi_private = cpu_dai->private_data; 428 struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
426 429
427 if (substream == ssi_private->first_stream) { 430 if (substream == ssi_private->first_stream) {
428 struct ccsr_ssi __iomem *ssi = ssi_private->ssi; 431 struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
@@ -458,7 +461,7 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
458 struct snd_soc_dai *dai) 461 struct snd_soc_dai *dai)
459{ 462{
460 struct snd_soc_pcm_runtime *rtd = substream->private_data; 463 struct snd_soc_pcm_runtime *rtd = substream->private_data;
461 struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data; 464 struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai);
462 struct ccsr_ssi __iomem *ssi = ssi_private->ssi; 465 struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
463 466
464 switch (cmd) { 467 switch (cmd) {
@@ -497,7 +500,7 @@ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream,
497 struct snd_soc_dai *dai) 500 struct snd_soc_dai *dai)
498{ 501{
499 struct snd_soc_pcm_runtime *rtd = substream->private_data; 502 struct snd_soc_pcm_runtime *rtd = substream->private_data;
500 struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data; 503 struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai);
501 504
502 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 505 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
503 ssi_private->playback--; 506 ssi_private->playback--;
@@ -523,56 +526,15 @@ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream,
523 } 526 }
524} 527}
525 528
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 = { 529static struct snd_soc_dai_ops fsl_ssi_dai_ops = {
567 .startup = fsl_ssi_startup, 530 .startup = fsl_ssi_startup,
568 .hw_params = fsl_ssi_hw_params, 531 .hw_params = fsl_ssi_hw_params,
569 .shutdown = fsl_ssi_shutdown, 532 .shutdown = fsl_ssi_shutdown,
570 .trigger = fsl_ssi_trigger, 533 .trigger = fsl_ssi_trigger,
571 .set_sysclk = fsl_ssi_set_sysclk,
572 .set_fmt = fsl_ssi_set_fmt,
573}; 534};
574 535
575static struct snd_soc_dai fsl_ssi_dai_template = { 536/* Template for the CPU dai driver structure */
537static struct snd_soc_dai_driver fsl_ssi_dai_template = {
576 .playback = { 538 .playback = {
577 /* The SSI does not support monaural audio. */ 539 /* The SSI does not support monaural audio. */
578 .channels_min = 2, 540 .channels_min = 2,
@@ -640,95 +602,176 @@ static ssize_t fsl_sysfs_ssi_show(struct device *dev,
640} 602}
641 603
642/** 604/**
643 * fsl_ssi_create_dai: create a snd_soc_dai structure 605 * 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 */ 606 */
649struct snd_soc_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info) 607static void make_lowercase(char *s)
608{
609 char *p = s;
610 char c;
611
612 while ((c = *p)) {
613 if ((c >= 'A') && (c <= 'Z'))
614 *p = c + ('a' - 'A');
615 p++;
616 }
617}
618
619static int __devinit fsl_ssi_probe(struct of_device *of_dev,
620 const struct of_device_id *match)
650{ 621{
651 struct snd_soc_dai *fsl_ssi_dai;
652 struct fsl_ssi_private *ssi_private; 622 struct fsl_ssi_private *ssi_private;
653 int ret = 0; 623 int ret = 0;
654 struct device_attribute *dev_attr; 624 struct device_attribute *dev_attr;
625 struct device_node *np = of_dev->dev.of_node;
626 const char *p, *sprop;
627 struct resource res;
628 char name[64];
655 629
656 ssi_private = kzalloc(sizeof(struct fsl_ssi_private), GFP_KERNEL); 630 /* We are only interested in SSIs with a codec phandle in them, so let's
631 * make sure this SSI has one.
632 */
633 if (!of_get_property(np, "codec-handle", NULL))
634 return -ENODEV;
635
636 /* We only support the SSI in "I2S Slave" mode */
637 sprop = of_get_property(np, "fsl,mode", NULL);
638 if (!sprop || strcmp(sprop, "i2s-slave")) {
639 dev_notice(&of_dev->dev, "mode %s is unsupported\n", sprop);
640 return -ENODEV;
641 }
642
643 /* The DAI name is the last part of the full name of the node. */
644 p = strrchr(np->full_name, '/') + 1;
645 ssi_private = kzalloc(sizeof(struct fsl_ssi_private) + strlen(p),
646 GFP_KERNEL);
657 if (!ssi_private) { 647 if (!ssi_private) {
658 dev_err(ssi_info->dev, "could not allocate DAI object\n"); 648 dev_err(&of_dev->dev, "could not allocate DAI object\n");
659 return NULL; 649 return -ENOMEM;
660 } 650 }
661 memcpy(&ssi_private->cpu_dai, &fsl_ssi_dai_template,
662 sizeof(struct snd_soc_dai));
663 651
664 fsl_ssi_dai = &ssi_private->cpu_dai; 652 strcpy(ssi_private->name, p);
665 dev_attr = &ssi_private->dev_attr;
666 653
667 sprintf(ssi_private->name, "ssi%u", (u8) ssi_info->id); 654 /* Initialize this copy of the CPU DAI driver structure */
668 ssi_private->ssi = ssi_info->ssi; 655 memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_dai_template,
669 ssi_private->ssi_phys = ssi_info->ssi_phys; 656 sizeof(fsl_ssi_dai_template));
670 ssi_private->irq = ssi_info->irq; 657 ssi_private->cpu_dai_drv.name = ssi_private->name;
671 ssi_private->dev = ssi_info->dev; 658
672 ssi_private->asynchronous = ssi_info->asynchronous; 659 /* Get the addresses and IRQ */
660 ret = of_address_to_resource(np, 0, &res);
661 if (ret) {
662 dev_err(&of_dev->dev, "could not determine device resources\n");
663 kfree(ssi_private);
664 return ret;
665 }
666 ssi_private->ssi = ioremap(res.start, 1 + res.end - res.start);
667 ssi_private->ssi_phys = res.start;
668 ssi_private->irq = irq_of_parse_and_map(np, 0);
673 669
674 dev_set_drvdata(ssi_private->dev, fsl_ssi_dai); 670 /* Are the RX and the TX clocks locked? */
671 if (of_find_property(np, "fsl,ssi-asynchronous", NULL))
672 ssi_private->asynchronous = 1;
673 else
674 ssi_private->cpu_dai_drv.symmetric_rates = 1;
675 675
676 /* Initialize the the device_attribute structure */ 676 /* Initialize the the device_attribute structure */
677 dev_attr->attr.name = "ssi-stats"; 677 dev_attr = &ssi_private->dev_attr;
678 dev_attr->attr.name = "statistics";
678 dev_attr->attr.mode = S_IRUGO; 679 dev_attr->attr.mode = S_IRUGO;
679 dev_attr->show = fsl_sysfs_ssi_show; 680 dev_attr->show = fsl_sysfs_ssi_show;
680 681
681 ret = device_create_file(ssi_private->dev, dev_attr); 682 ret = device_create_file(&of_dev->dev, dev_attr);
682 if (ret) { 683 if (ret) {
683 dev_err(ssi_info->dev, "could not create sysfs %s file\n", 684 dev_err(&of_dev->dev, "could not create sysfs %s file\n",
684 ssi_private->dev_attr.attr.name); 685 ssi_private->dev_attr.attr.name);
685 kfree(fsl_ssi_dai); 686 kfree(ssi_private);
686 return NULL; 687 return ret;
687 } 688 }
688 689
689 fsl_ssi_dai->private_data = ssi_private; 690 /* Register with ASoC */
690 fsl_ssi_dai->name = ssi_private->name; 691 dev_set_drvdata(&of_dev->dev, ssi_private);
691 fsl_ssi_dai->id = ssi_info->id;
692 fsl_ssi_dai->dev = ssi_info->dev;
693 fsl_ssi_dai->symmetric_rates = 1;
694 692
695 ret = snd_soc_register_dai(fsl_ssi_dai); 693 ret = snd_soc_register_dai(&of_dev->dev, &ssi_private->cpu_dai_drv);
696 if (ret != 0) { 694 if (ret != 0) {
697 dev_err(ssi_info->dev, "failed to register DAI: %d\n", ret); 695 dev_err(&of_dev->dev, "failed to register DAI: %d\n", ret);
698 kfree(fsl_ssi_dai); 696 kfree(ssi_private);
699 return NULL; 697 return ret;
698 }
699
700 /* Trigger the machine driver's probe function. The platform driver
701 * name of the machine driver is taken from the /model property of the
702 * device tree. We also pass the address of the CPU DAI driver
703 * structure.
704 */
705 sprop = of_get_property(of_find_node_by_path("/"), "model", NULL);
706 /* Sometimes the model name has a "fsl," prefix, so we strip that. */
707 p = strrchr(sprop, ',');
708 if (p)
709 sprop = p + 1;
710 snprintf(name, sizeof(name), "snd-soc-%s", sprop);
711 make_lowercase(name);
712
713 ssi_private->pdev =
714 platform_device_register_data(&of_dev->dev, name, 0, NULL, 0);
715 if (IS_ERR(ssi_private->pdev)) {
716 ret = PTR_ERR(ssi_private->pdev);
717 dev_err(&of_dev->dev, "failed to register platform: %d\n", ret);
718 kfree(ssi_private);
719 return ret;
700 } 720 }
701 721
702 return fsl_ssi_dai; 722 return 0;
703} 723}
704EXPORT_SYMBOL_GPL(fsl_ssi_create_dai);
705 724
706/** 725/**
707 * fsl_ssi_destroy_dai: destroy the snd_soc_dai object 726 * fsl_ssi_destroy_dai: destroy the snd_soc_dai object
708 * 727 *
709 * This function undoes the operations of fsl_ssi_create_dai() 728 * This function undoes the operations of fsl_ssi_probe()
710 */ 729 */
711void fsl_ssi_destroy_dai(struct snd_soc_dai *fsl_ssi_dai) 730static int fsl_ssi_remove(struct of_device *of_dev)
712{ 731{
713 struct fsl_ssi_private *ssi_private = 732 struct fsl_ssi_private *ssi_private = dev_get_drvdata(&of_dev->dev);
714 container_of(fsl_ssi_dai, struct fsl_ssi_private, cpu_dai);
715 733
716 device_remove_file(ssi_private->dev, &ssi_private->dev_attr); 734 platform_device_unregister(ssi_private->pdev);
717 735 snd_soc_unregister_dai(&of_dev->dev);
718 snd_soc_unregister_dai(&ssi_private->cpu_dai); 736 device_remove_file(&of_dev->dev, &ssi_private->dev_attr);
719 737
720 kfree(ssi_private); 738 kfree(ssi_private);
739 dev_set_drvdata(&of_dev->dev, NULL);
740
741 return 0;
721} 742}
722EXPORT_SYMBOL_GPL(fsl_ssi_destroy_dai); 743
744static const struct of_device_id fsl_ssi_ids[] = {
745 { .compatible = "fsl,mpc8610-ssi", },
746 {}
747};
748MODULE_DEVICE_TABLE(of, fsl_ssi_ids);
749
750static struct of_platform_driver fsl_ssi_driver = {
751 .driver = {
752 .name = "fsl-ssi-dai",
753 .owner = THIS_MODULE,
754 .of_match_table = fsl_ssi_ids,
755 },
756 .probe = fsl_ssi_probe,
757 .remove = fsl_ssi_remove,
758};
723 759
724static int __init fsl_ssi_init(void) 760static int __init fsl_ssi_init(void)
725{ 761{
726 printk(KERN_INFO "Freescale Synchronous Serial Interface (SSI) ASoC Driver\n"); 762 printk(KERN_INFO "Freescale Synchronous Serial Interface (SSI) ASoC Driver\n");
727 763
728 return 0; 764 return of_register_platform_driver(&fsl_ssi_driver);
765}
766
767static void __exit fsl_ssi_exit(void)
768{
769 of_unregister_platform_driver(&fsl_ssi_driver);
729} 770}
771
730module_init(fsl_ssi_init); 772module_init(fsl_ssi_init);
773module_exit(fsl_ssi_exit);
731 774
732MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); 775MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
733MODULE_DESCRIPTION("Freescale Synchronous Serial Interface (SSI) ASoC Driver"); 776MODULE_DESCRIPTION("Freescale Synchronous Serial Interface (SSI) ASoC Driver");
734MODULE_LICENSE("GPL"); 777MODULE_LICENSE("GPL v2");