Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * sam9g20_wm8731  --  SoC audio for AT91SAM9G20-based
0004  *          ATMEL AT91SAM9G20ek board.
0005  *
0006  *  Copyright (C) 2005 SAN People
0007  *  Copyright (C) 2008 Atmel
0008  *
0009  * Authors: Sedji Gaouaou <sedji.gaouaou@atmel.com>
0010  *
0011  * Based on ati_b1_wm8731.c by:
0012  * Frank Mandarino <fmandarino@endrelia.com>
0013  * Copyright 2006 Endrelia Technologies Inc.
0014  * Based on corgi.c by:
0015  * Copyright 2005 Wolfson Microelectronics PLC.
0016  * Copyright 2005 Openedhand Ltd.
0017  */
0018 
0019 #include <linux/module.h>
0020 #include <linux/moduleparam.h>
0021 #include <linux/kernel.h>
0022 #include <linux/clk.h>
0023 #include <linux/timer.h>
0024 #include <linux/interrupt.h>
0025 #include <linux/platform_device.h>
0026 #include <linux/i2c.h>
0027 #include <linux/of.h>
0028 
0029 #include <linux/atmel-ssc.h>
0030 
0031 #include <sound/core.h>
0032 #include <sound/pcm.h>
0033 #include <sound/pcm_params.h>
0034 #include <sound/soc.h>
0035 
0036 #include "../codecs/wm8731.h"
0037 #include "atmel-pcm.h"
0038 #include "atmel_ssc_dai.h"
0039 
0040 #define MCLK_RATE 12000000
0041 
0042 /*
0043  * As shipped the board does not have inputs.  However, it is relatively
0044  * straightforward to modify the board to hook them up so support is left
0045  * in the driver.
0046  */
0047 #undef ENABLE_MIC_INPUT
0048 
0049 static const struct snd_soc_dapm_widget at91sam9g20ek_dapm_widgets[] = {
0050     SND_SOC_DAPM_MIC("Int Mic", NULL),
0051     SND_SOC_DAPM_SPK("Ext Spk", NULL),
0052 };
0053 
0054 static const struct snd_soc_dapm_route intercon[] = {
0055 
0056     /* speaker connected to LHPOUT/RHPOUT */
0057     {"Ext Spk", NULL, "LHPOUT"},
0058     {"Ext Spk", NULL, "RHPOUT"},
0059 
0060     /* mic is connected to Mic Jack, with WM8731 Mic Bias */
0061     {"MICIN", NULL, "Mic Bias"},
0062     {"Mic Bias", NULL, "Int Mic"},
0063 };
0064 
0065 /*
0066  * Logic for a wm8731 as connected on a at91sam9g20ek board.
0067  */
0068 static int at91sam9g20ek_wm8731_init(struct snd_soc_pcm_runtime *rtd)
0069 {
0070     struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
0071     struct device *dev = rtd->dev;
0072     int ret;
0073 
0074     dev_dbg(dev, "%s called\n", __func__);
0075 
0076     ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_MCLK,
0077                      MCLK_RATE, SND_SOC_CLOCK_IN);
0078     if (ret < 0) {
0079         dev_err(dev, "Failed to set WM8731 SYSCLK: %d\n", ret);
0080         return ret;
0081     }
0082 
0083 #ifndef ENABLE_MIC_INPUT
0084     snd_soc_dapm_nc_pin(&rtd->card->dapm, "Int Mic");
0085 #endif
0086 
0087     return 0;
0088 }
0089 
0090 SND_SOC_DAILINK_DEFS(pcm,
0091     DAILINK_COMP_ARRAY(COMP_CPU("at91rm9200_ssc.0")),
0092     DAILINK_COMP_ARRAY(COMP_CODEC("wm8731.0-001b", "wm8731-hifi")),
0093     DAILINK_COMP_ARRAY(COMP_PLATFORM("at91rm9200_ssc.0")));
0094 
0095 static struct snd_soc_dai_link at91sam9g20ek_dai = {
0096     .name = "WM8731",
0097     .stream_name = "WM8731 PCM",
0098     .init = at91sam9g20ek_wm8731_init,
0099     .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
0100            SND_SOC_DAIFMT_CBP_CFP,
0101     SND_SOC_DAILINK_REG(pcm),
0102 };
0103 
0104 static struct snd_soc_card snd_soc_at91sam9g20ek = {
0105     .name = "AT91SAMG20-EK",
0106     .owner = THIS_MODULE,
0107     .dai_link = &at91sam9g20ek_dai,
0108     .num_links = 1,
0109 
0110     .dapm_widgets = at91sam9g20ek_dapm_widgets,
0111     .num_dapm_widgets = ARRAY_SIZE(at91sam9g20ek_dapm_widgets),
0112     .dapm_routes = intercon,
0113     .num_dapm_routes = ARRAY_SIZE(intercon),
0114     .fully_routed = true,
0115 };
0116 
0117 static int at91sam9g20ek_audio_probe(struct platform_device *pdev)
0118 {
0119     struct device_node *np = pdev->dev.of_node;
0120     struct device_node *codec_np, *cpu_np;
0121     struct snd_soc_card *card = &snd_soc_at91sam9g20ek;
0122     int ret;
0123 
0124     if (!np) {
0125         return -ENODEV;
0126     }
0127 
0128     ret = atmel_ssc_set_audio(0);
0129     if (ret) {
0130         dev_err(&pdev->dev, "ssc channel is not valid: %d\n", ret);
0131         return ret;
0132     }
0133 
0134     card->dev = &pdev->dev;
0135 
0136     /* Parse device node info */
0137     ret = snd_soc_of_parse_card_name(card, "atmel,model");
0138     if (ret)
0139         goto err;
0140 
0141     ret = snd_soc_of_parse_audio_routing(card,
0142         "atmel,audio-routing");
0143     if (ret)
0144         goto err;
0145 
0146     /* Parse codec info */
0147     at91sam9g20ek_dai.codecs->name = NULL;
0148     codec_np = of_parse_phandle(np, "atmel,audio-codec", 0);
0149     if (!codec_np) {
0150         dev_err(&pdev->dev, "codec info missing\n");
0151         ret = -EINVAL;
0152         goto err;
0153     }
0154     at91sam9g20ek_dai.codecs->of_node = codec_np;
0155 
0156     /* Parse dai and platform info */
0157     at91sam9g20ek_dai.cpus->dai_name = NULL;
0158     at91sam9g20ek_dai.platforms->name = NULL;
0159     cpu_np = of_parse_phandle(np, "atmel,ssc-controller", 0);
0160     if (!cpu_np) {
0161         dev_err(&pdev->dev, "dai and pcm info missing\n");
0162         of_node_put(codec_np);
0163         ret = -EINVAL;
0164         goto err;
0165     }
0166     at91sam9g20ek_dai.cpus->of_node = cpu_np;
0167     at91sam9g20ek_dai.platforms->of_node = cpu_np;
0168 
0169     of_node_put(codec_np);
0170     of_node_put(cpu_np);
0171 
0172     ret = snd_soc_register_card(card);
0173     if (ret) {
0174         dev_err_probe(&pdev->dev, ret,
0175                   "snd_soc_register_card() failed: %d\n", ret);
0176         goto err;
0177     }
0178 
0179     return 0;
0180 
0181 err:
0182     atmel_ssc_put_audio(0);
0183     return ret;
0184 }
0185 
0186 static int at91sam9g20ek_audio_remove(struct platform_device *pdev)
0187 {
0188     struct snd_soc_card *card = platform_get_drvdata(pdev);
0189 
0190     snd_soc_unregister_card(card);
0191     atmel_ssc_put_audio(0);
0192 
0193     return 0;
0194 }
0195 
0196 #ifdef CONFIG_OF
0197 static const struct of_device_id at91sam9g20ek_wm8731_dt_ids[] = {
0198     { .compatible = "atmel,at91sam9g20ek-wm8731-audio", },
0199     { }
0200 };
0201 MODULE_DEVICE_TABLE(of, at91sam9g20ek_wm8731_dt_ids);
0202 #endif
0203 
0204 static struct platform_driver at91sam9g20ek_audio_driver = {
0205     .driver = {
0206         .name   = "at91sam9g20ek-audio",
0207         .of_match_table = of_match_ptr(at91sam9g20ek_wm8731_dt_ids),
0208     },
0209     .probe  = at91sam9g20ek_audio_probe,
0210     .remove = at91sam9g20ek_audio_remove,
0211 };
0212 
0213 module_platform_driver(at91sam9g20ek_audio_driver);
0214 
0215 /* Module information */
0216 MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>");
0217 MODULE_DESCRIPTION("ALSA SoC AT91SAM9G20EK_WM8731");
0218 MODULE_ALIAS("platform:at91sam9g20ek-audio");
0219 MODULE_LICENSE("GPL");