Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 //
0003 // Freescale P1022DS ALSA SoC Machine driver
0004 //
0005 // Author: Timur Tabi <timur@freescale.com>
0006 //
0007 // Copyright 2010 Freescale Semiconductor, Inc.
0008 
0009 #include <linux/module.h>
0010 #include <linux/fsl/guts.h>
0011 #include <linux/interrupt.h>
0012 #include <linux/of_address.h>
0013 #include <linux/of_device.h>
0014 #include <linux/slab.h>
0015 #include <sound/soc.h>
0016 
0017 #include "fsl_dma.h"
0018 #include "fsl_ssi.h"
0019 #include "fsl_utils.h"
0020 
0021 /* P1022-specific PMUXCR and DMUXCR bit definitions */
0022 
0023 #define CCSR_GUTS_PMUXCR_UART0_I2C1_MASK    0x0001c000
0024 #define CCSR_GUTS_PMUXCR_UART0_I2C1_UART0_SSI   0x00010000
0025 #define CCSR_GUTS_PMUXCR_UART0_I2C1_SSI     0x00018000
0026 
0027 #define CCSR_GUTS_PMUXCR_SSI_DMA_TDM_MASK   0x00000c00
0028 #define CCSR_GUTS_PMUXCR_SSI_DMA_TDM_SSI    0x00000000
0029 
0030 #define CCSR_GUTS_DMUXCR_PAD    1   /* DMA controller/channel set to pad */
0031 #define CCSR_GUTS_DMUXCR_SSI    2   /* DMA controller/channel set to SSI */
0032 
0033 /*
0034  * Set the DMACR register in the GUTS
0035  *
0036  * The DMACR register determines the source of initiated transfers for each
0037  * channel on each DMA controller.  Rather than have a bunch of repetitive
0038  * macros for the bit patterns, we just have a function that calculates
0039  * them.
0040  *
0041  * guts: Pointer to GUTS structure
0042  * co: The DMA controller (0 or 1)
0043  * ch: The channel on the DMA controller (0, 1, 2, or 3)
0044  * device: The device to set as the target (CCSR_GUTS_DMUXCR_xxx)
0045  */
0046 static inline void guts_set_dmuxcr(struct ccsr_guts __iomem *guts,
0047     unsigned int co, unsigned int ch, unsigned int device)
0048 {
0049     unsigned int shift = 16 + (8 * (1 - co) + 2 * (3 - ch));
0050 
0051     clrsetbits_be32(&guts->dmuxcr, 3 << shift, device << shift);
0052 }
0053 
0054 /* There's only one global utilities register */
0055 static phys_addr_t guts_phys;
0056 
0057 /**
0058  * machine_data: machine-specific ASoC device data
0059  *
0060  * This structure contains data for a single sound platform device on an
0061  * P1022 DS.  Some of the data is taken from the device tree.
0062  */
0063 struct machine_data {
0064     struct snd_soc_dai_link dai[2];
0065     struct snd_soc_card card;
0066     unsigned int dai_format;
0067     unsigned int codec_clk_direction;
0068     unsigned int cpu_clk_direction;
0069     unsigned int clk_frequency;
0070     unsigned int ssi_id;        /* 0 = SSI1, 1 = SSI2, etc */
0071     unsigned int dma_id[2];     /* 0 = DMA1, 1 = DMA2, etc */
0072     unsigned int dma_channel_id[2]; /* 0 = ch 0, 1 = ch 1, etc*/
0073     char platform_name[2][DAI_NAME_SIZE]; /* One for each DMA channel */
0074 };
0075 
0076 /**
0077  * p1022_ds_machine_probe: initialize the board
0078  *
0079  * This function is used to initialize the board-specific hardware.
0080  *
0081  * Here we program the DMACR and PMUXCR registers.
0082  */
0083 static int p1022_ds_machine_probe(struct snd_soc_card *card)
0084 {
0085     struct machine_data *mdata =
0086         container_of(card, struct machine_data, card);
0087     struct ccsr_guts __iomem *guts;
0088 
0089     guts = ioremap(guts_phys, sizeof(struct ccsr_guts));
0090     if (!guts) {
0091         dev_err(card->dev, "could not map global utilities\n");
0092         return -ENOMEM;
0093     }
0094 
0095     /* Enable SSI Tx signal */
0096     clrsetbits_be32(&guts->pmuxcr, CCSR_GUTS_PMUXCR_UART0_I2C1_MASK,
0097             CCSR_GUTS_PMUXCR_UART0_I2C1_UART0_SSI);
0098 
0099     /* Enable SSI Rx signal */
0100     clrsetbits_be32(&guts->pmuxcr, CCSR_GUTS_PMUXCR_SSI_DMA_TDM_MASK,
0101             CCSR_GUTS_PMUXCR_SSI_DMA_TDM_SSI);
0102 
0103     /* Enable DMA Channel for SSI */
0104     guts_set_dmuxcr(guts, mdata->dma_id[0], mdata->dma_channel_id[0],
0105             CCSR_GUTS_DMUXCR_SSI);
0106 
0107     guts_set_dmuxcr(guts, mdata->dma_id[1], mdata->dma_channel_id[1],
0108             CCSR_GUTS_DMUXCR_SSI);
0109 
0110     iounmap(guts);
0111 
0112     return 0;
0113 }
0114 
0115 /**
0116  * p1022_ds_startup: program the board with various hardware parameters
0117  *
0118  * This function takes board-specific information, like clock frequencies
0119  * and serial data formats, and passes that information to the codec and
0120  * transport drivers.
0121  */
0122 static int p1022_ds_startup(struct snd_pcm_substream *substream)
0123 {
0124     struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
0125     struct machine_data *mdata =
0126         container_of(rtd->card, struct machine_data, card);
0127     struct device *dev = rtd->card->dev;
0128     int ret = 0;
0129 
0130     /* Tell the codec driver what the serial protocol is. */
0131     ret = snd_soc_dai_set_fmt(asoc_rtd_to_codec(rtd, 0), mdata->dai_format);
0132     if (ret < 0) {
0133         dev_err(dev, "could not set codec driver audio format\n");
0134         return ret;
0135     }
0136 
0137     /*
0138      * Tell the codec driver what the MCLK frequency is, and whether it's
0139      * a slave or master.
0140      */
0141     ret = snd_soc_dai_set_sysclk(asoc_rtd_to_codec(rtd, 0), 0, mdata->clk_frequency,
0142                      mdata->codec_clk_direction);
0143     if (ret < 0) {
0144         dev_err(dev, "could not set codec driver clock params\n");
0145         return ret;
0146     }
0147 
0148     return 0;
0149 }
0150 
0151 /**
0152  * p1022_ds_machine_remove: Remove the sound device
0153  *
0154  * This function is called to remove the sound device for one SSI.  We
0155  * de-program the DMACR and PMUXCR register.
0156  */
0157 static int p1022_ds_machine_remove(struct snd_soc_card *card)
0158 {
0159     struct machine_data *mdata =
0160         container_of(card, struct machine_data, card);
0161     struct ccsr_guts __iomem *guts;
0162 
0163     guts = ioremap(guts_phys, sizeof(struct ccsr_guts));
0164     if (!guts) {
0165         dev_err(card->dev, "could not map global utilities\n");
0166         return -ENOMEM;
0167     }
0168 
0169     /* Restore the signal routing */
0170     clrbits32(&guts->pmuxcr, CCSR_GUTS_PMUXCR_UART0_I2C1_MASK);
0171     clrbits32(&guts->pmuxcr, CCSR_GUTS_PMUXCR_SSI_DMA_TDM_MASK);
0172     guts_set_dmuxcr(guts, mdata->dma_id[0], mdata->dma_channel_id[0], 0);
0173     guts_set_dmuxcr(guts, mdata->dma_id[1], mdata->dma_channel_id[1], 0);
0174 
0175     iounmap(guts);
0176 
0177     return 0;
0178 }
0179 
0180 /**
0181  * p1022_ds_ops: ASoC machine driver operations
0182  */
0183 static const struct snd_soc_ops p1022_ds_ops = {
0184     .startup = p1022_ds_startup,
0185 };
0186 
0187 /**
0188  * p1022_ds_probe: platform probe function for the machine driver
0189  *
0190  * Although this is a machine driver, the SSI node is the "master" node with
0191  * respect to audio hardware connections.  Therefore, we create a new ASoC
0192  * device for each new SSI node that has a codec attached.
0193  */
0194 static int p1022_ds_probe(struct platform_device *pdev)
0195 {
0196     struct device *dev = pdev->dev.parent;
0197     /* ssi_pdev is the platform device for the SSI node that probed us */
0198     struct platform_device *ssi_pdev = to_platform_device(dev);
0199     struct device_node *np = ssi_pdev->dev.of_node;
0200     struct device_node *codec_np = NULL;
0201     struct machine_data *mdata;
0202     struct snd_soc_dai_link_component *comp;
0203     int ret;
0204     const char *sprop;
0205     const u32 *iprop;
0206 
0207     /* Find the codec node for this SSI. */
0208     codec_np = of_parse_phandle(np, "codec-handle", 0);
0209     if (!codec_np) {
0210         dev_err(dev, "could not find codec node\n");
0211         return -EINVAL;
0212     }
0213 
0214     mdata = kzalloc(sizeof(struct machine_data), GFP_KERNEL);
0215     if (!mdata) {
0216         ret = -ENOMEM;
0217         goto error_put;
0218     }
0219 
0220     comp = devm_kzalloc(&pdev->dev, 6 * sizeof(*comp), GFP_KERNEL);
0221     if (!comp) {
0222         ret = -ENOMEM;
0223         goto error_put;
0224     }
0225 
0226     mdata->dai[0].cpus  = &comp[0];
0227     mdata->dai[0].codecs    = &comp[1];
0228     mdata->dai[0].platforms = &comp[2];
0229 
0230     mdata->dai[0].num_cpus      = 1;
0231     mdata->dai[0].num_codecs    = 1;
0232     mdata->dai[0].num_platforms = 1;
0233 
0234     mdata->dai[1].cpus  = &comp[3];
0235     mdata->dai[1].codecs    = &comp[4];
0236     mdata->dai[1].platforms = &comp[5];
0237 
0238     mdata->dai[1].num_cpus      = 1;
0239     mdata->dai[1].num_codecs    = 1;
0240     mdata->dai[1].num_platforms = 1;
0241 
0242 
0243     mdata->dai[0].cpus->dai_name = dev_name(&ssi_pdev->dev);
0244     mdata->dai[0].ops = &p1022_ds_ops;
0245 
0246     /* ASoC core can match codec with device node */
0247     mdata->dai[0].codecs->of_node = codec_np;
0248 
0249     /* We register two DAIs per SSI, one for playback and the other for
0250      * capture.  We support codecs that have separate DAIs for both playback
0251      * and capture.
0252      */
0253     memcpy(&mdata->dai[1], &mdata->dai[0], sizeof(struct snd_soc_dai_link));
0254 
0255     /* The DAI names from the codec (snd_soc_dai_driver.name) */
0256     mdata->dai[0].codecs->dai_name = "wm8776-hifi-playback";
0257     mdata->dai[1].codecs->dai_name = "wm8776-hifi-capture";
0258 
0259     /* Get the device ID */
0260     iprop = of_get_property(np, "cell-index", NULL);
0261     if (!iprop) {
0262         dev_err(&pdev->dev, "cell-index property not found\n");
0263         ret = -EINVAL;
0264         goto error;
0265     }
0266     mdata->ssi_id = be32_to_cpup(iprop);
0267 
0268     /* Get the serial format and clock direction. */
0269     sprop = of_get_property(np, "fsl,mode", NULL);
0270     if (!sprop) {
0271         dev_err(&pdev->dev, "fsl,mode property not found\n");
0272         ret = -EINVAL;
0273         goto error;
0274     }
0275 
0276     if (strcasecmp(sprop, "i2s-slave") == 0) {
0277         mdata->dai_format = SND_SOC_DAIFMT_NB_NF |
0278             SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBP_CFP;
0279         mdata->codec_clk_direction = SND_SOC_CLOCK_OUT;
0280         mdata->cpu_clk_direction = SND_SOC_CLOCK_IN;
0281 
0282         /* In i2s-slave mode, the codec has its own clock source, so we
0283          * need to get the frequency from the device tree and pass it to
0284          * the codec driver.
0285          */
0286         iprop = of_get_property(codec_np, "clock-frequency", NULL);
0287         if (!iprop || !*iprop) {
0288             dev_err(&pdev->dev, "codec bus-frequency "
0289                 "property is missing or invalid\n");
0290             ret = -EINVAL;
0291             goto error;
0292         }
0293         mdata->clk_frequency = be32_to_cpup(iprop);
0294     } else if (strcasecmp(sprop, "i2s-master") == 0) {
0295         mdata->dai_format = SND_SOC_DAIFMT_NB_NF |
0296             SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBC_CFC;
0297         mdata->codec_clk_direction = SND_SOC_CLOCK_IN;
0298         mdata->cpu_clk_direction = SND_SOC_CLOCK_OUT;
0299     } else if (strcasecmp(sprop, "lj-slave") == 0) {
0300         mdata->dai_format = SND_SOC_DAIFMT_NB_NF |
0301             SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_CBP_CFP;
0302         mdata->codec_clk_direction = SND_SOC_CLOCK_OUT;
0303         mdata->cpu_clk_direction = SND_SOC_CLOCK_IN;
0304     } else if (strcasecmp(sprop, "lj-master") == 0) {
0305         mdata->dai_format = SND_SOC_DAIFMT_NB_NF |
0306             SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_CBC_CFC;
0307         mdata->codec_clk_direction = SND_SOC_CLOCK_IN;
0308         mdata->cpu_clk_direction = SND_SOC_CLOCK_OUT;
0309     } else if (strcasecmp(sprop, "rj-slave") == 0) {
0310         mdata->dai_format = SND_SOC_DAIFMT_NB_NF |
0311             SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_CBP_CFP;
0312         mdata->codec_clk_direction = SND_SOC_CLOCK_OUT;
0313         mdata->cpu_clk_direction = SND_SOC_CLOCK_IN;
0314     } else if (strcasecmp(sprop, "rj-master") == 0) {
0315         mdata->dai_format = SND_SOC_DAIFMT_NB_NF |
0316             SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_CBC_CFC;
0317         mdata->codec_clk_direction = SND_SOC_CLOCK_IN;
0318         mdata->cpu_clk_direction = SND_SOC_CLOCK_OUT;
0319     } else if (strcasecmp(sprop, "ac97-slave") == 0) {
0320         mdata->dai_format = SND_SOC_DAIFMT_NB_NF |
0321             SND_SOC_DAIFMT_AC97 | SND_SOC_DAIFMT_CBP_CFP;
0322         mdata->codec_clk_direction = SND_SOC_CLOCK_OUT;
0323         mdata->cpu_clk_direction = SND_SOC_CLOCK_IN;
0324     } else if (strcasecmp(sprop, "ac97-master") == 0) {
0325         mdata->dai_format = SND_SOC_DAIFMT_NB_NF |
0326             SND_SOC_DAIFMT_AC97 | SND_SOC_DAIFMT_CBC_CFC;
0327         mdata->codec_clk_direction = SND_SOC_CLOCK_IN;
0328         mdata->cpu_clk_direction = SND_SOC_CLOCK_OUT;
0329     } else {
0330         dev_err(&pdev->dev,
0331             "unrecognized fsl,mode property '%s'\n", sprop);
0332         ret = -EINVAL;
0333         goto error;
0334     }
0335 
0336     if (!mdata->clk_frequency) {
0337         dev_err(&pdev->dev, "unknown clock frequency\n");
0338         ret = -EINVAL;
0339         goto error;
0340     }
0341 
0342     /* Find the playback DMA channel to use. */
0343     mdata->dai[0].platforms->name = mdata->platform_name[0];
0344     ret = fsl_asoc_get_dma_channel(np, "fsl,playback-dma", &mdata->dai[0],
0345                        &mdata->dma_channel_id[0],
0346                        &mdata->dma_id[0]);
0347     if (ret) {
0348         dev_err(&pdev->dev, "missing/invalid playback DMA phandle\n");
0349         goto error;
0350     }
0351 
0352     /* Find the capture DMA channel to use. */
0353     mdata->dai[1].platforms->name = mdata->platform_name[1];
0354     ret = fsl_asoc_get_dma_channel(np, "fsl,capture-dma", &mdata->dai[1],
0355                        &mdata->dma_channel_id[1],
0356                        &mdata->dma_id[1]);
0357     if (ret) {
0358         dev_err(&pdev->dev, "missing/invalid capture DMA phandle\n");
0359         goto error;
0360     }
0361 
0362     /* Initialize our DAI data structure.  */
0363     mdata->dai[0].stream_name = "playback";
0364     mdata->dai[1].stream_name = "capture";
0365     mdata->dai[0].name = mdata->dai[0].stream_name;
0366     mdata->dai[1].name = mdata->dai[1].stream_name;
0367 
0368     mdata->card.probe = p1022_ds_machine_probe;
0369     mdata->card.remove = p1022_ds_machine_remove;
0370     mdata->card.name = pdev->name; /* The platform driver name */
0371     mdata->card.owner = THIS_MODULE;
0372     mdata->card.dev = &pdev->dev;
0373     mdata->card.num_links = 2;
0374     mdata->card.dai_link = mdata->dai;
0375 
0376     /* Register with ASoC */
0377     ret = snd_soc_register_card(&mdata->card);
0378     if (ret) {
0379         dev_err(&pdev->dev, "could not register card\n");
0380         goto error;
0381     }
0382 
0383     of_node_put(codec_np);
0384 
0385     return 0;
0386 
0387 error:
0388     kfree(mdata);
0389 error_put:
0390     of_node_put(codec_np);
0391     return ret;
0392 }
0393 
0394 /**
0395  * p1022_ds_remove: remove the platform device
0396  *
0397  * This function is called when the platform device is removed.
0398  */
0399 static int p1022_ds_remove(struct platform_device *pdev)
0400 {
0401     struct snd_soc_card *card = platform_get_drvdata(pdev);
0402     struct machine_data *mdata =
0403         container_of(card, struct machine_data, card);
0404 
0405     snd_soc_unregister_card(card);
0406     kfree(mdata);
0407 
0408     return 0;
0409 }
0410 
0411 static struct platform_driver p1022_ds_driver = {
0412     .probe = p1022_ds_probe,
0413     .remove = p1022_ds_remove,
0414     .driver = {
0415         /*
0416          * The name must match 'compatible' property in the device tree,
0417          * in lowercase letters.
0418          */
0419         .name = "snd-soc-p1022ds",
0420     },
0421 };
0422 
0423 /**
0424  * p1022_ds_init: machine driver initialization.
0425  *
0426  * This function is called when this module is loaded.
0427  */
0428 static int __init p1022_ds_init(void)
0429 {
0430     struct device_node *guts_np;
0431     struct resource res;
0432 
0433     /* Get the physical address of the global utilities registers */
0434     guts_np = of_find_compatible_node(NULL, NULL, "fsl,p1022-guts");
0435     if (of_address_to_resource(guts_np, 0, &res)) {
0436         pr_err("snd-soc-p1022ds: missing/invalid global utils node\n");
0437         of_node_put(guts_np);
0438         return -EINVAL;
0439     }
0440     guts_phys = res.start;
0441     of_node_put(guts_np);
0442 
0443     return platform_driver_register(&p1022_ds_driver);
0444 }
0445 
0446 /**
0447  * p1022_ds_exit: machine driver exit
0448  *
0449  * This function is called when this driver is unloaded.
0450  */
0451 static void __exit p1022_ds_exit(void)
0452 {
0453     platform_driver_unregister(&p1022_ds_driver);
0454 }
0455 
0456 module_init(p1022_ds_init);
0457 module_exit(p1022_ds_exit);
0458 
0459 MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
0460 MODULE_DESCRIPTION("Freescale P1022 DS ALSA SoC machine driver");
0461 MODULE_LICENSE("GPL v2");