aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNicolin Chen <b42378@freescale.com>2013-08-23 07:31:52 -0400
committerNitin Garg <nitin.garg@freescale.com>2014-04-16 09:01:31 -0400
commitb34d7e9229f04a02592cf1424820ba8aef5a5f5c (patch)
tree4fcf7461b28963836575c28c358b482fb3f4722f
parent2df4851ae48d1b2d4e10b0f6aea0386937880af1 (diff)
ENGR00276567-2 ASoC: fsl: Add I2S Master support for ssi
Add I2S master/PCM(DSP_A, DSP_B)/LEFT_J formats support for ssi, also dropped the constrain of i2s-slave in probe(). Signed-off-by: Nicolin Chen <b42378@freescale.com>
-rw-r--r--sound/soc/fsl/fsl_ssi.c219
-rw-r--r--sound/soc/fsl/fsl_ssi.h6
2 files changed, 215 insertions, 10 deletions
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index ad9875f7bb30..894e2109c8a0 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -138,6 +138,7 @@ struct fsl_ssi_private {
138 struct device_attribute dev_attr; 138 struct device_attribute dev_attr;
139 struct platform_device *pdev; 139 struct platform_device *pdev;
140 140
141 unsigned long sysrate;
141 bool new_binding; 142 bool new_binding;
142 bool ssi_on_imx; 143 bool ssi_on_imx;
143 bool use_dual_fifo; 144 bool use_dual_fifo;
@@ -488,6 +489,8 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
488 snd_pcm_format_width(params_format(hw_params)); 489 snd_pcm_format_width(params_format(hw_params));
489 u32 wl = CCSR_SSI_SxCCR_WL(sample_size); 490 u32 wl = CCSR_SSI_SxCCR_WL(sample_size);
490 int enabled = read_ssi(&ssi->scr) & CCSR_SSI_SCR_SSIEN; 491 int enabled = read_ssi(&ssi->scr) & CCSR_SSI_SCR_SSIEN;
492 u32 dc = CCSR_SSI_SxCCR_DC(params_channels(hw_params));
493 int ret;
491 494
492 /* 495 /*
493 * If we're in synchronous mode, and the SSI is already enabled, 496 * If we're in synchronous mode, and the SSI is already enabled,
@@ -496,6 +499,14 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
496 if (enabled && ssi_private->cpu_dai_drv.symmetric_rates) 499 if (enabled && ssi_private->cpu_dai_drv.symmetric_rates)
497 return 0; 500 return 0;
498 501
502 if (ssi_private->sysrate) {
503 ret = clk_set_rate(ssi_private->clk, ssi_private->sysrate);
504 if (ret) {
505 dev_err(cpu_dai->dev, "failed to set clock rate\n");
506 return ret;
507 }
508 }
509
499 /* 510 /*
500 * FIXME: The documentation says that SxCCR[WL] should not be 511 * FIXME: The documentation says that SxCCR[WL] should not be
501 * modified while the SSI is enabled. The only time this can 512 * modified while the SSI is enabled. The only time this can
@@ -508,10 +519,13 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
508 519
509 /* In synchronous mode, the SSI uses STCCR for capture */ 520 /* In synchronous mode, the SSI uses STCCR for capture */
510 if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) || 521 if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ||
511 ssi_private->cpu_dai_drv.symmetric_rates) 522 ssi_private->cpu_dai_drv.symmetric_rates) {
512 write_ssi_mask(&ssi->stccr, CCSR_SSI_SxCCR_WL_MASK, wl); 523 write_ssi_mask(&ssi->stccr, CCSR_SSI_SxCCR_WL_MASK, wl);
513 else 524 write_ssi_mask(&ssi->stccr, CCSR_SSI_SxCCR_DC_MASK, dc);
525 } else {
514 write_ssi_mask(&ssi->srccr, CCSR_SSI_SxCCR_WL_MASK, wl); 526 write_ssi_mask(&ssi->srccr, CCSR_SSI_SxCCR_WL_MASK, wl);
527 write_ssi_mask(&ssi->srccr, CCSR_SSI_SxCCR_WL_MASK, dc);
528 }
515 529
516 return 0; 530 return 0;
517} 531}
@@ -567,6 +581,194 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
567 return 0; 581 return 0;
568} 582}
569 583
584static int fsl_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
585{
586 struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
587 struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
588 u32 strcr = 0, stcr, srcr, scr, mask;
589
590 scr = read_ssi(&ssi->scr) & ~(CCSR_SSI_SCR_SYN | CCSR_SSI_SCR_NET);
591
592 mask = CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFDIR | CCSR_SSI_STCR_TXDIR
593 | CCSR_SSI_STCR_TSCKP | CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TFSL
594 | CCSR_SSI_STCR_TEFS;
595 stcr = read_ssi(&ssi->stcr) & ~mask;
596 srcr = read_ssi(&ssi->srcr) & ~mask;
597
598 /* DAI format */
599 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
600 case SND_SOC_DAIFMT_I2S:
601 scr |= CCSR_SSI_SCR_NET;
602
603 /* Pre-set SSI I2S mode */
604 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
605 case SND_SOC_DAIFMT_CBS_CFS:
606 scr &= ~CCSR_SSI_SCR_I2S_MODE_MASK;
607 scr |= CCSR_SSI_SCR_I2S_MODE_MASTER;
608 break;
609 case SND_SOC_DAIFMT_CBM_CFM:
610 scr &= ~CCSR_SSI_SCR_I2S_MODE_MASK;
611 scr |= CCSR_SSI_SCR_I2S_MODE_SLAVE;
612 break;
613 default:
614 dev_err(cpu_dai->dev, "unsupported SND_SOC_DAIFMT_MASTER: %d",
615 fmt & SND_SOC_DAIFMT_MASTER_MASK);
616 return -EINVAL;
617 }
618
619 /* Data on rising edge of bclk, frame low, 1clk before data */
620 strcr |= CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TSCKP
621 | CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TEFS;
622 break;
623 case SND_SOC_DAIFMT_LEFT_J:
624 /* Data on rising edge of bclk, frame high */
625 strcr |= CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TSCKP;
626 break;
627 case SND_SOC_DAIFMT_DSP_A:
628 /* Data on rising edge of bclk, frame high, 1clk before data */
629 strcr |= CCSR_SSI_STCR_TFSL | CCSR_SSI_STCR_TSCKP
630 | CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TEFS;
631 break;
632 case SND_SOC_DAIFMT_DSP_B:
633 /* Data on rising edge of bclk, frame high */
634 strcr |= CCSR_SSI_STCR_TFSL | CCSR_SSI_STCR_TSCKP
635 | CCSR_SSI_STCR_TXBIT0;
636 break;
637 default:
638 dev_err(cpu_dai->dev, "unsupported SND_SOC_DAIFMT: %d",
639 fmt & SND_SOC_DAIFMT_FORMAT_MASK);
640 return -EINVAL;
641 }
642
643 /* DAI clock inversion */
644 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
645 case SND_SOC_DAIFMT_NB_NF:
646 /* Nothing to do for both normal cases */
647 break;
648 case SND_SOC_DAIFMT_IB_NF:
649 /* Invert bit clock */
650 strcr ^= CCSR_SSI_STCR_TSCKP;
651 break;
652 case SND_SOC_DAIFMT_NB_IF:
653 /* Invert frame clock */
654 strcr ^= CCSR_SSI_STCR_TFSI;
655 break;
656 case SND_SOC_DAIFMT_IB_IF:
657 /* Invert both clocks */
658 strcr ^= CCSR_SSI_STCR_TSCKP;
659 strcr ^= CCSR_SSI_STCR_TFSI;
660 break;
661 default:
662 dev_err(cpu_dai->dev, "unsupported SND_SOC_DAIFMT_INV: %d",
663 fmt & SND_SOC_DAIFMT_INV_MASK);
664 return -EINVAL;
665 }
666
667 /* DAI clock master masks */
668 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
669 case SND_SOC_DAIFMT_CBS_CFS:
670 scr |= CCSR_SSI_SCR_SYS_CLK_EN;
671 strcr |= CCSR_SSI_STCR_TFDIR | CCSR_SSI_STCR_TXDIR;
672 break;
673 case SND_SOC_DAIFMT_CBM_CFM:
674 scr &= ~CCSR_SSI_SCR_SYS_CLK_EN;
675 break;
676 default:
677 dev_err(cpu_dai->dev, "unsupported SND_SOC_DAIFMT_MASTER: %d",
678 fmt & SND_SOC_DAIFMT_MASTER_MASK);
679 return -EINVAL;
680 }
681
682 stcr |= strcr;
683 srcr |= strcr;
684
685 if (ssi_private->cpu_dai_drv.symmetric_rates) {
686 scr |= CCSR_SSI_SCR_SYN;
687
688 /* Need to clear RXDIR when using SYNC mode */
689 srcr &= ~CCSR_SSI_SRCR_RXDIR;
690 }
691
692 write_ssi(stcr, &ssi->stcr);
693 write_ssi(srcr, &ssi->srcr);
694 write_ssi(scr, &ssi->scr);
695
696 return 0;
697}
698
699static int fsl_ssi_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
700 int clk_id, unsigned int freq, int dir)
701{
702 struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
703 struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
704 int synchronous = ssi_private->cpu_dai_drv.symmetric_rates;
705 u32 pm = 999, div2, psr, stccr, mask, afreq, factor, i;
706 unsigned long clkrate, sysrate = 0;
707 u64 sub, savesub = 100000;
708
709 /*
710 * It should be already enough to divide clock by setting pm.
711 * So we here keep psr and div2 to 0.
712 */
713 psr = 0;
714 div2 = 0;
715
716 factor = (div2 + 1) * (7 * psr + 1) * 2;
717
718 for (i = 0; i < 255; i++) {
719 /* The bclk rate must be smaller than 1/5 sysclk rate */
720 if (factor * (i + 1) < 5)
721 continue;
722
723 sysrate = freq * factor * (i + 2);
724 clkrate = clk_round_rate(ssi_private->clk, sysrate);
725
726 do_div(clkrate, factor);
727 afreq = (u32)clkrate / (i + 1);
728
729 if (freq == afreq)
730 sub = 0;
731 else if (freq / afreq == 1)
732 sub = freq - afreq;
733 else if (afreq / freq == 1)
734 sub = afreq - freq;
735 else
736 continue;
737
738 /* Calculate the fraction */
739 sub *= 100000;
740 do_div(sub, freq);
741
742 if (sub < savesub) {
743 ssi_private->sysrate = sysrate;
744 savesub = sub;
745 pm = i;
746 }
747
748 /* We are lucky */
749 if (savesub == 0)
750 break;
751 }
752
753 /* No proper pm found if it is still remaining the initial value */
754 if (pm == 999) {
755 dev_err(cpu_dai->dev, "failed to handle the required sysclk\n");
756 return -EINVAL;
757 }
758
759 stccr = CCSR_SSI_SxCCR_PM(pm + 1) | (div2 ? CCSR_SSI_SxCCR_DIV2 : 0)
760 | (psr ? CCSR_SSI_SxCCR_PSR : 0);
761 mask = CCSR_SSI_SxCCR_PM_MASK | CCSR_SSI_SxCCR_DIV2_MASK
762 | CCSR_SSI_SxCCR_PSR_MASK;
763
764 if (dir == SND_SOC_CLOCK_OUT || synchronous)
765 write_ssi_mask(&ssi->stccr, mask, stccr);
766 else
767 write_ssi_mask(&ssi->srccr, mask, stccr);
768
769 return 0;
770}
771
570/** 772/**
571 * fsl_ssi_shutdown: shutdown the SSI 773 * fsl_ssi_shutdown: shutdown the SSI
572 * 774 *
@@ -588,6 +790,8 @@ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream,
588 struct ccsr_ssi __iomem *ssi = ssi_private->ssi; 790 struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
589 791
590 write_ssi_mask(&ssi->sier, SIER_FLAGS, 0); 792 write_ssi_mask(&ssi->sier, SIER_FLAGS, 0);
793
794 ssi_private->sysrate = 0;
591 } 795 }
592 796
593 if (ssi_private->ssi_on_imx) { 797 if (ssi_private->ssi_on_imx) {
@@ -613,6 +817,8 @@ static int fsl_ssi_dai_probe(struct snd_soc_dai *dai)
613static const struct snd_soc_dai_ops fsl_ssi_dai_ops = { 817static const struct snd_soc_dai_ops fsl_ssi_dai_ops = {
614 .startup = fsl_ssi_startup, 818 .startup = fsl_ssi_startup,
615 .hw_params = fsl_ssi_hw_params, 819 .hw_params = fsl_ssi_hw_params,
820 .set_fmt = fsl_ssi_set_dai_fmt,
821 .set_sysclk = fsl_ssi_set_dai_sysclk,
616 .shutdown = fsl_ssi_shutdown, 822 .shutdown = fsl_ssi_shutdown,
617 .trigger = fsl_ssi_trigger, 823 .trigger = fsl_ssi_trigger,
618}; 824};
@@ -723,13 +929,6 @@ static int fsl_ssi_probe(struct platform_device *pdev)
723 if (!of_device_is_available(np)) 929 if (!of_device_is_available(np))
724 return -ENODEV; 930 return -ENODEV;
725 931
726 /* We only support the SSI in "I2S Slave" mode */
727 sprop = of_get_property(np, "fsl,mode", NULL);
728 if (!sprop || strcmp(sprop, "i2s-slave")) {
729 dev_notice(&pdev->dev, "mode %s is unsupported\n", sprop);
730 return -ENODEV;
731 }
732
733 /* The DAI name is the last part of the full name of the node. */ 932 /* The DAI name is the last part of the full name of the node. */
734 p = strrchr(np->full_name, '/') + 1; 933 p = strrchr(np->full_name, '/') + 1;
735 ssi_private = kzalloc(sizeof(struct fsl_ssi_private) + strlen(p), 934 ssi_private = kzalloc(sizeof(struct fsl_ssi_private) + strlen(p),
@@ -817,6 +1016,8 @@ static int fsl_ssi_probe(struct platform_device *pdev)
817 ssi_private->ssi_phys + offsetof(struct ccsr_ssi, srx0); 1016 ssi_private->ssi_phys + offsetof(struct ccsr_ssi, srx0);
818 } 1017 }
819 1018
1019 ssi_private->sysrate = 0;
1020
820 /* Initialize the the device_attribute structure */ 1021 /* Initialize the the device_attribute structure */
821 dev_attr = &ssi_private->dev_attr; 1022 dev_attr = &ssi_private->dev_attr;
822 sysfs_attr_init(&dev_attr->attr); 1023 sysfs_attr_init(&dev_attr->attr);
diff --git a/sound/soc/fsl/fsl_ssi.h b/sound/soc/fsl/fsl_ssi.h
index e6b9a69e2a68..5ff9b1d5270d 100644
--- a/sound/soc/fsl/fsl_ssi.h
+++ b/sound/soc/fsl/fsl_ssi.h
@@ -3,7 +3,7 @@
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-2013 Freescale Semiconductor, Inc. This file is licensed
7 * under the terms of the GNU General Public License version 2. This 7 * under the terms of the GNU General Public License version 2. This
8 * program is licensed "as is" without any warranty of any kind, whether 8 * program is licensed "as is" without any warranty of any kind, whether
9 * express or implied. 9 * express or implied.
@@ -125,7 +125,11 @@ struct ccsr_ssi {
125#define CCSR_SSI_SRCR_REFS 0x00000001 125#define CCSR_SSI_SRCR_REFS 0x00000001
126 126
127/* STCCR and SRCCR */ 127/* STCCR and SRCCR */
128#define CCSR_SSI_SxCCR_DIV2_SHIFT 18
129#define CCSR_SSI_SxCCR_DIV2_MASK 0x00040000
128#define CCSR_SSI_SxCCR_DIV2 0x00040000 130#define CCSR_SSI_SxCCR_DIV2 0x00040000
131#define CCSR_SSI_SxCCR_PSR_SHIFT 17
132#define CCSR_SSI_SxCCR_PSR_MASK 0x00020000
129#define CCSR_SSI_SxCCR_PSR 0x00020000 133#define CCSR_SSI_SxCCR_PSR 0x00020000
130#define CCSR_SSI_SxCCR_WL_SHIFT 13 134#define CCSR_SSI_SxCCR_WL_SHIFT 13
131#define CCSR_SSI_SxCCR_WL_MASK 0x0001E000 135#define CCSR_SSI_SxCCR_WL_MASK 0x0001E000