Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 //
0003 // ALSA SoC driver for Migo-R
0004 //
0005 // Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
0006 
0007 #include <linux/clkdev.h>
0008 #include <linux/device.h>
0009 #include <linux/firmware.h>
0010 #include <linux/module.h>
0011 
0012 #include <asm/clock.h>
0013 
0014 #include <cpu/sh7722.h>
0015 
0016 #include <sound/core.h>
0017 #include <sound/pcm.h>
0018 #include <sound/soc.h>
0019 
0020 #include "../codecs/wm8978.h"
0021 #include "siu.h"
0022 
0023 /* Default 8000Hz sampling frequency */
0024 static unsigned long codec_freq = 8000 * 512;
0025 
0026 static unsigned int use_count;
0027 
0028 /* External clock, sourced from the codec at the SIUMCKB pin */
0029 static unsigned long siumckb_recalc(struct clk *clk)
0030 {
0031     return codec_freq;
0032 }
0033 
0034 static struct sh_clk_ops siumckb_clk_ops = {
0035     .recalc = siumckb_recalc,
0036 };
0037 
0038 static struct clk siumckb_clk = {
0039     .ops        = &siumckb_clk_ops,
0040     .rate       = 0, /* initialised at run-time */
0041 };
0042 
0043 static struct clk_lookup *siumckb_lookup;
0044 
0045 static int migor_hw_params(struct snd_pcm_substream *substream,
0046                struct snd_pcm_hw_params *params)
0047 {
0048     struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
0049     struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
0050     int ret;
0051     unsigned int rate = params_rate(params);
0052 
0053     ret = snd_soc_dai_set_sysclk(codec_dai, WM8978_PLL, 13000000,
0054                      SND_SOC_CLOCK_IN);
0055     if (ret < 0)
0056         return ret;
0057 
0058     ret = snd_soc_dai_set_clkdiv(codec_dai, WM8978_OPCLKRATE, rate * 512);
0059     if (ret < 0)
0060         return ret;
0061 
0062     codec_freq = rate * 512;
0063     /*
0064      * This propagates the parent frequency change to children and
0065      * recalculates the frequency table
0066      */
0067     clk_set_rate(&siumckb_clk, codec_freq);
0068     dev_dbg(codec_dai->dev, "%s: configure %luHz\n", __func__, codec_freq);
0069 
0070     ret = snd_soc_dai_set_sysclk(asoc_rtd_to_cpu(rtd, 0), SIU_CLKB_EXT,
0071                      codec_freq / 2, SND_SOC_CLOCK_IN);
0072 
0073     if (!ret)
0074         use_count++;
0075 
0076     return ret;
0077 }
0078 
0079 static int migor_hw_free(struct snd_pcm_substream *substream)
0080 {
0081     struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
0082     struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
0083 
0084     if (use_count) {
0085         use_count--;
0086 
0087         if (!use_count)
0088             snd_soc_dai_set_sysclk(codec_dai, WM8978_PLL, 0,
0089                            SND_SOC_CLOCK_IN);
0090     } else {
0091         dev_dbg(codec_dai->dev, "Unbalanced hw_free!\n");
0092     }
0093 
0094     return 0;
0095 }
0096 
0097 static const struct snd_soc_ops migor_dai_ops = {
0098     .hw_params = migor_hw_params,
0099     .hw_free = migor_hw_free,
0100 };
0101 
0102 static const struct snd_soc_dapm_widget migor_dapm_widgets[] = {
0103     SND_SOC_DAPM_HP("Headphone", NULL),
0104     SND_SOC_DAPM_MIC("Onboard Microphone", NULL),
0105     SND_SOC_DAPM_MIC("External Microphone", NULL),
0106 };
0107 
0108 static const struct snd_soc_dapm_route audio_map[] = {
0109     /* Headphone output connected to LHP/RHP, enable OUT4 for VMID */
0110     { "Headphone", NULL,  "OUT4 VMID" },
0111     { "OUT4 VMID", NULL,  "LHP" },
0112     { "OUT4 VMID", NULL,  "RHP" },
0113 
0114     /* On-board microphone */
0115     { "RMICN", NULL, "Mic Bias" },
0116     { "RMICP", NULL, "Mic Bias" },
0117     { "Mic Bias", NULL, "Onboard Microphone" },
0118 
0119     /* External microphone */
0120     { "LMICN", NULL, "Mic Bias" },
0121     { "LMICP", NULL, "Mic Bias" },
0122     { "Mic Bias", NULL, "External Microphone" },
0123 };
0124 
0125 /* migor digital audio interface glue - connects codec <--> CPU */
0126 SND_SOC_DAILINK_DEFS(wm8978,
0127     DAILINK_COMP_ARRAY(COMP_CPU("siu-pcm-audio")),
0128     DAILINK_COMP_ARRAY(COMP_CODEC("wm8978.0-001a", "wm8978-hifi")),
0129     DAILINK_COMP_ARRAY(COMP_PLATFORM("siu-pcm-audio")));
0130 
0131 static struct snd_soc_dai_link migor_dai = {
0132     .name = "wm8978",
0133     .stream_name = "WM8978",
0134     .dai_fmt = SND_SOC_DAIFMT_NB_IF | SND_SOC_DAIFMT_I2S |
0135            SND_SOC_DAIFMT_CBS_CFS,
0136     .ops = &migor_dai_ops,
0137     SND_SOC_DAILINK_REG(wm8978),
0138 };
0139 
0140 /* migor audio machine driver */
0141 static struct snd_soc_card snd_soc_migor = {
0142     .name = "Migo-R",
0143     .owner = THIS_MODULE,
0144     .dai_link = &migor_dai,
0145     .num_links = 1,
0146 
0147     .dapm_widgets = migor_dapm_widgets,
0148     .num_dapm_widgets = ARRAY_SIZE(migor_dapm_widgets),
0149     .dapm_routes = audio_map,
0150     .num_dapm_routes = ARRAY_SIZE(audio_map),
0151 };
0152 
0153 static struct platform_device *migor_snd_device;
0154 
0155 static int __init migor_init(void)
0156 {
0157     int ret;
0158 
0159     ret = clk_register(&siumckb_clk);
0160     if (ret < 0)
0161         return ret;
0162 
0163     siumckb_lookup = clkdev_create(&siumckb_clk, "siumckb_clk", NULL);
0164     if (!siumckb_lookup) {
0165         ret = -ENOMEM;
0166         goto eclkdevalloc;
0167     }
0168 
0169     /* Port number used on this machine: port B */
0170     migor_snd_device = platform_device_alloc("soc-audio", 1);
0171     if (!migor_snd_device) {
0172         ret = -ENOMEM;
0173         goto epdevalloc;
0174     }
0175 
0176     platform_set_drvdata(migor_snd_device, &snd_soc_migor);
0177 
0178     ret = platform_device_add(migor_snd_device);
0179     if (ret)
0180         goto epdevadd;
0181 
0182     return 0;
0183 
0184 epdevadd:
0185     platform_device_put(migor_snd_device);
0186 epdevalloc:
0187     clkdev_drop(siumckb_lookup);
0188 eclkdevalloc:
0189     clk_unregister(&siumckb_clk);
0190     return ret;
0191 }
0192 
0193 static void __exit migor_exit(void)
0194 {
0195     clkdev_drop(siumckb_lookup);
0196     clk_unregister(&siumckb_clk);
0197     platform_device_unregister(migor_snd_device);
0198 }
0199 
0200 module_init(migor_init);
0201 module_exit(migor_exit);
0202 
0203 MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
0204 MODULE_DESCRIPTION("ALSA SoC Migor");
0205 MODULE_LICENSE("GPL v2");