Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 //
0003 // Freescale MPC5200 PSC in I2S mode
0004 // ALSA SoC Digital Audio Interface (DAI) driver
0005 //
0006 // Copyright (C) 2008 Secret Lab Technologies Ltd.
0007 // Copyright (C) 2009 Jon Smirl, Digispeaker
0008 
0009 #include <linux/module.h>
0010 #include <linux/of_device.h>
0011 #include <linux/of_platform.h>
0012 
0013 #include <sound/pcm.h>
0014 #include <sound/pcm_params.h>
0015 #include <sound/soc.h>
0016 
0017 #include <asm/mpc52xx_psc.h>
0018 
0019 #include "mpc5200_dma.h"
0020 
0021 /**
0022  * PSC_I2S_RATES: sample rates supported by the I2S
0023  *
0024  * This driver currently only supports the PSC running in I2S slave mode,
0025  * which means the codec determines the sample rate.  Therefore, we tell
0026  * ALSA that we support all rates and let the codec driver decide what rates
0027  * are really supported.
0028  */
0029 #define PSC_I2S_RATES SNDRV_PCM_RATE_CONTINUOUS
0030 
0031 /**
0032  * PSC_I2S_FORMATS: audio formats supported by the PSC I2S mode
0033  */
0034 #define PSC_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | \
0035              SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE)
0036 
0037 static int psc_i2s_hw_params(struct snd_pcm_substream *substream,
0038                  struct snd_pcm_hw_params *params,
0039                  struct snd_soc_dai *dai)
0040 {
0041     struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
0042     struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0));
0043     u32 mode;
0044 
0045     dev_dbg(psc_dma->dev, "%s(substream=%p) p_size=%i p_bytes=%i"
0046         " periods=%i buffer_size=%i  buffer_bytes=%i\n",
0047         __func__, substream, params_period_size(params),
0048         params_period_bytes(params), params_periods(params),
0049         params_buffer_size(params), params_buffer_bytes(params));
0050 
0051     switch (params_format(params)) {
0052     case SNDRV_PCM_FORMAT_S8:
0053         mode = MPC52xx_PSC_SICR_SIM_CODEC_8;
0054         break;
0055     case SNDRV_PCM_FORMAT_S16_BE:
0056         mode = MPC52xx_PSC_SICR_SIM_CODEC_16;
0057         break;
0058     case SNDRV_PCM_FORMAT_S24_BE:
0059         mode = MPC52xx_PSC_SICR_SIM_CODEC_24;
0060         break;
0061     case SNDRV_PCM_FORMAT_S32_BE:
0062         mode = MPC52xx_PSC_SICR_SIM_CODEC_32;
0063         break;
0064     default:
0065         dev_dbg(psc_dma->dev, "invalid format\n");
0066         return -EINVAL;
0067     }
0068     out_be32(&psc_dma->psc_regs->sicr, psc_dma->sicr | mode);
0069 
0070     return 0;
0071 }
0072 
0073 /**
0074  * psc_i2s_set_sysclk: set the clock frequency and direction
0075  *
0076  * This function is called by the machine driver to tell us what the clock
0077  * frequency and direction are.
0078  *
0079  * Currently, we only support operating as a clock slave (SND_SOC_CLOCK_IN),
0080  * and we don't care about the frequency.  Return an error if the direction
0081  * is not SND_SOC_CLOCK_IN.
0082  *
0083  * @clk_id: reserved, should be zero
0084  * @freq: the frequency of the given clock ID, currently ignored
0085  * @dir: SND_SOC_CLOCK_IN (clock slave) or SND_SOC_CLOCK_OUT (clock master)
0086  */
0087 static int psc_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
0088                   int clk_id, unsigned int freq, int dir)
0089 {
0090     struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(cpu_dai);
0091     dev_dbg(psc_dma->dev, "psc_i2s_set_sysclk(cpu_dai=%p, dir=%i)\n",
0092                 cpu_dai, dir);
0093     return (dir == SND_SOC_CLOCK_IN) ? 0 : -EINVAL;
0094 }
0095 
0096 /**
0097  * psc_i2s_set_fmt: set the serial format.
0098  *
0099  * This function is called by the machine driver to tell us what serial
0100  * format to use.
0101  *
0102  * This driver only supports I2S mode.  Return an error if the format is
0103  * not SND_SOC_DAIFMT_I2S.
0104  *
0105  * @format: one of SND_SOC_DAIFMT_xxx
0106  */
0107 static int psc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format)
0108 {
0109     struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(cpu_dai);
0110     dev_dbg(psc_dma->dev, "psc_i2s_set_fmt(cpu_dai=%p, format=%i)\n",
0111                 cpu_dai, format);
0112     return (format == SND_SOC_DAIFMT_I2S) ? 0 : -EINVAL;
0113 }
0114 
0115 /* ---------------------------------------------------------------------
0116  * ALSA SoC Bindings
0117  *
0118  * - Digital Audio Interface (DAI) template
0119  * - create/destroy dai hooks
0120  */
0121 
0122 /**
0123  * psc_i2s_dai_template: template CPU Digital Audio Interface
0124  */
0125 static const struct snd_soc_dai_ops psc_i2s_dai_ops = {
0126     .hw_params  = psc_i2s_hw_params,
0127     .set_sysclk = psc_i2s_set_sysclk,
0128     .set_fmt    = psc_i2s_set_fmt,
0129 };
0130 
0131 static struct snd_soc_dai_driver psc_i2s_dai[] = {{
0132     .name = "mpc5200-psc-i2s.0",
0133     .playback = {
0134         .stream_name = "I2S Playback",
0135         .channels_min = 2,
0136         .channels_max = 2,
0137         .rates = PSC_I2S_RATES,
0138         .formats = PSC_I2S_FORMATS,
0139     },
0140     .capture = {
0141         .stream_name = "I2S Capture",
0142         .channels_min = 2,
0143         .channels_max = 2,
0144         .rates = PSC_I2S_RATES,
0145         .formats = PSC_I2S_FORMATS,
0146     },
0147     .ops = &psc_i2s_dai_ops,
0148 } };
0149 
0150 static const struct snd_soc_component_driver psc_i2s_component = {
0151     .name           = "mpc5200-i2s",
0152     .legacy_dai_naming  = 1,
0153 };
0154 
0155 /* ---------------------------------------------------------------------
0156  * OF platform bus binding code:
0157  * - Probe/remove operations
0158  * - OF device match table
0159  */
0160 static int psc_i2s_of_probe(struct platform_device *op)
0161 {
0162     int rc;
0163     struct psc_dma *psc_dma;
0164     struct mpc52xx_psc __iomem *regs;
0165 
0166     rc = mpc5200_audio_dma_create(op);
0167     if (rc != 0)
0168         return rc;
0169 
0170     rc = snd_soc_register_component(&op->dev, &psc_i2s_component,
0171                     psc_i2s_dai, ARRAY_SIZE(psc_i2s_dai));
0172     if (rc != 0) {
0173         pr_err("Failed to register DAI\n");
0174         return rc;
0175     }
0176 
0177     psc_dma = dev_get_drvdata(&op->dev);
0178     regs = psc_dma->psc_regs;
0179 
0180     /* Configure the serial interface mode; defaulting to CODEC8 mode */
0181     psc_dma->sicr = MPC52xx_PSC_SICR_DTS1 | MPC52xx_PSC_SICR_I2S |
0182             MPC52xx_PSC_SICR_CLKPOL;
0183     out_be32(&psc_dma->psc_regs->sicr,
0184          psc_dma->sicr | MPC52xx_PSC_SICR_SIM_CODEC_8);
0185 
0186     /* Check for the codec handle.  If it is not present then we
0187      * are done */
0188     if (!of_get_property(op->dev.of_node, "codec-handle", NULL))
0189         return 0;
0190 
0191     /* Due to errata in the dma mode; need to line up enabling
0192      * the transmitter with a transition on the frame sync
0193      * line */
0194 
0195     /* first make sure it is low */
0196     while ((in_8(&regs->ipcr_acr.ipcr) & 0x80) != 0)
0197         ;
0198     /* then wait for the transition to high */
0199     while ((in_8(&regs->ipcr_acr.ipcr) & 0x80) == 0)
0200         ;
0201     /* Finally, enable the PSC.
0202      * Receiver must always be enabled; even when we only want
0203      * transmit.  (see 15.3.2.3 of MPC5200B User's Guide) */
0204 
0205     /* Go */
0206     out_8(&psc_dma->psc_regs->command,
0207             MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE);
0208 
0209     return 0;
0210 
0211 }
0212 
0213 static int psc_i2s_of_remove(struct platform_device *op)
0214 {
0215     mpc5200_audio_dma_destroy(op);
0216     snd_soc_unregister_component(&op->dev);
0217     return 0;
0218 }
0219 
0220 /* Match table for of_platform binding */
0221 static const struct of_device_id psc_i2s_match[] = {
0222     { .compatible = "fsl,mpc5200-psc-i2s", },
0223     { .compatible = "fsl,mpc5200b-psc-i2s", },
0224     {}
0225 };
0226 MODULE_DEVICE_TABLE(of, psc_i2s_match);
0227 
0228 static struct platform_driver psc_i2s_driver = {
0229     .probe = psc_i2s_of_probe,
0230     .remove = psc_i2s_of_remove,
0231     .driver = {
0232         .name = "mpc5200-psc-i2s",
0233         .of_match_table = psc_i2s_match,
0234     },
0235 };
0236 
0237 module_platform_driver(psc_i2s_driver);
0238 
0239 MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
0240 MODULE_DESCRIPTION("Freescale MPC5200 PSC in I2S mode ASoC Driver");
0241 MODULE_LICENSE("GPL");
0242