Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * sam9x5_wm8731   --   SoC audio for AT91SAM9X5-based boards
0004  *          that are using WM8731 as codec.
0005  *
0006  *  Copyright (C) 2011 Atmel,
0007  *        Nicolas Ferre <nicolas.ferre@atmel.com>
0008  *
0009  *  Copyright (C) 2013 Paratronic,
0010  *        Richard Genoud <richard.genoud@gmail.com>
0011  *
0012  * Based on sam9g20_wm8731.c by:
0013  * Sedji Gaouaou <sedji.gaouaou@atmel.com>
0014  */
0015 #include <linux/of.h>
0016 #include <linux/export.h>
0017 #include <linux/module.h>
0018 #include <linux/mod_devicetable.h>
0019 #include <linux/platform_device.h>
0020 #include <linux/device.h>
0021 
0022 #include <sound/soc.h>
0023 #include <sound/soc-dai.h>
0024 #include <sound/soc-dapm.h>
0025 
0026 #include "../codecs/wm8731.h"
0027 #include "atmel_ssc_dai.h"
0028 
0029 
0030 #define MCLK_RATE 12288000
0031 
0032 #define DRV_NAME "sam9x5-snd-wm8731"
0033 
0034 struct sam9x5_drvdata {
0035     int ssc_id;
0036 };
0037 
0038 /*
0039  * Logic for a wm8731 as connected on a at91sam9x5ek based board.
0040  */
0041 static int sam9x5_wm8731_init(struct snd_soc_pcm_runtime *rtd)
0042 {
0043     struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
0044     struct device *dev = rtd->dev;
0045     int ret;
0046 
0047     dev_dbg(dev, "%s called\n", __func__);
0048 
0049     /* set the codec system clock for DAC and ADC */
0050     ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL,
0051                      MCLK_RATE, SND_SOC_CLOCK_IN);
0052     if (ret < 0) {
0053         dev_err(dev, "Failed to set WM8731 SYSCLK: %d\n", ret);
0054         return ret;
0055     }
0056 
0057     return 0;
0058 }
0059 
0060 /*
0061  * Audio paths on at91sam9x5ek board:
0062  *
0063  *  |A| ------------> |      | ---R----> Headphone Jack
0064  *  |T| <----\        |  WM  | ---L--/
0065  *  |9| ---> CLK <--> | 8731 | <--R----- Line In Jack
0066  *  |1| <------------ |      | <--L--/
0067  */
0068 static const struct snd_soc_dapm_widget sam9x5_dapm_widgets[] = {
0069     SND_SOC_DAPM_HP("Headphone Jack", NULL),
0070     SND_SOC_DAPM_LINE("Line In Jack", NULL),
0071 };
0072 
0073 static int sam9x5_wm8731_driver_probe(struct platform_device *pdev)
0074 {
0075     struct device_node *np = pdev->dev.of_node;
0076     struct device_node *codec_np, *cpu_np;
0077     struct snd_soc_card *card;
0078     struct snd_soc_dai_link *dai;
0079     struct sam9x5_drvdata *priv;
0080     struct snd_soc_dai_link_component *comp;
0081     int ret;
0082 
0083     if (!np) {
0084         dev_err(&pdev->dev, "No device node supplied\n");
0085         return -EINVAL;
0086     }
0087 
0088     card = devm_kzalloc(&pdev->dev, sizeof(*card), GFP_KERNEL);
0089     priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
0090     dai = devm_kzalloc(&pdev->dev, sizeof(*dai), GFP_KERNEL);
0091     comp = devm_kzalloc(&pdev->dev, 3 * sizeof(*comp), GFP_KERNEL);
0092     if (!dai || !card || !priv || !comp) {
0093         ret = -ENOMEM;
0094         goto out;
0095     }
0096 
0097     snd_soc_card_set_drvdata(card, priv);
0098 
0099     card->dev = &pdev->dev;
0100     card->owner = THIS_MODULE;
0101     card->dai_link = dai;
0102     card->num_links = 1;
0103     card->dapm_widgets = sam9x5_dapm_widgets;
0104     card->num_dapm_widgets = ARRAY_SIZE(sam9x5_dapm_widgets);
0105 
0106     dai->cpus = &comp[0];
0107     dai->num_cpus = 1;
0108     dai->codecs = &comp[1];
0109     dai->num_codecs = 1;
0110     dai->platforms = &comp[2];
0111     dai->num_platforms = 1;
0112 
0113     dai->name = "WM8731";
0114     dai->stream_name = "WM8731 PCM";
0115     dai->codecs->dai_name = "wm8731-hifi";
0116     dai->init = sam9x5_wm8731_init;
0117     dai->dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF
0118         | SND_SOC_DAIFMT_CBP_CFP;
0119 
0120     ret = snd_soc_of_parse_card_name(card, "atmel,model");
0121     if (ret) {
0122         dev_err(&pdev->dev, "atmel,model node missing\n");
0123         goto out;
0124     }
0125 
0126     ret = snd_soc_of_parse_audio_routing(card, "atmel,audio-routing");
0127     if (ret) {
0128         dev_err(&pdev->dev, "atmel,audio-routing node missing\n");
0129         goto out;
0130     }
0131 
0132     codec_np = of_parse_phandle(np, "atmel,audio-codec", 0);
0133     if (!codec_np) {
0134         dev_err(&pdev->dev, "atmel,audio-codec node missing\n");
0135         ret = -EINVAL;
0136         goto out;
0137     }
0138 
0139     dai->codecs->of_node = codec_np;
0140 
0141     cpu_np = of_parse_phandle(np, "atmel,ssc-controller", 0);
0142     if (!cpu_np) {
0143         dev_err(&pdev->dev, "atmel,ssc-controller node missing\n");
0144         ret = -EINVAL;
0145         goto out_put_codec_np;
0146     }
0147     dai->cpus->of_node = cpu_np;
0148     dai->platforms->of_node = cpu_np;
0149 
0150     priv->ssc_id = of_alias_get_id(cpu_np, "ssc");
0151 
0152     ret = atmel_ssc_set_audio(priv->ssc_id);
0153     if (ret != 0) {
0154         dev_err(&pdev->dev, "Failed to set SSC %d for audio: %d\n",
0155             ret, priv->ssc_id);
0156         goto out_put_cpu_np;
0157     }
0158 
0159     ret = devm_snd_soc_register_card(&pdev->dev, card);
0160     if (ret) {
0161         dev_err(&pdev->dev, "Platform device allocation failed\n");
0162         goto out_put_audio;
0163     }
0164 
0165     dev_dbg(&pdev->dev, "%s ok\n", __func__);
0166 
0167     goto out_put_cpu_np;
0168 
0169 out_put_audio:
0170     atmel_ssc_put_audio(priv->ssc_id);
0171 out_put_cpu_np:
0172     of_node_put(cpu_np);
0173 out_put_codec_np:
0174     of_node_put(codec_np);
0175 out:
0176     return ret;
0177 }
0178 
0179 static int sam9x5_wm8731_driver_remove(struct platform_device *pdev)
0180 {
0181     struct snd_soc_card *card = platform_get_drvdata(pdev);
0182     struct sam9x5_drvdata *priv = card->drvdata;
0183 
0184     atmel_ssc_put_audio(priv->ssc_id);
0185 
0186     return 0;
0187 }
0188 
0189 static const struct of_device_id sam9x5_wm8731_of_match[] = {
0190     { .compatible = "atmel,sam9x5-wm8731-audio", },
0191     {},
0192 };
0193 MODULE_DEVICE_TABLE(of, sam9x5_wm8731_of_match);
0194 
0195 static struct platform_driver sam9x5_wm8731_driver = {
0196     .driver = {
0197         .name = DRV_NAME,
0198         .of_match_table = of_match_ptr(sam9x5_wm8731_of_match),
0199     },
0200     .probe = sam9x5_wm8731_driver_probe,
0201     .remove = sam9x5_wm8731_driver_remove,
0202 };
0203 module_platform_driver(sam9x5_wm8731_driver);
0204 
0205 /* Module information */
0206 MODULE_AUTHOR("Nicolas Ferre <nicolas.ferre@atmel.com>");
0207 MODULE_AUTHOR("Richard Genoud <richard.genoud@gmail.com>");
0208 MODULE_DESCRIPTION("ALSA SoC machine driver for AT91SAM9x5 - WM8731");
0209 MODULE_LICENSE("GPL");
0210 MODULE_ALIAS("platform:" DRV_NAME);