0001
0002
0003
0004
0005
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
0024 static unsigned long codec_freq = 8000 * 512;
0025
0026 static unsigned int use_count;
0027
0028
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,
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
0065
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
0110 { "Headphone", NULL, "OUT4 VMID" },
0111 { "OUT4 VMID", NULL, "LHP" },
0112 { "OUT4 VMID", NULL, "RHP" },
0113
0114
0115 { "RMICN", NULL, "Mic Bias" },
0116 { "RMICP", NULL, "Mic Bias" },
0117 { "Mic Bias", NULL, "Onboard Microphone" },
0118
0119
0120 { "LMICN", NULL, "Mic Bias" },
0121 { "LMICP", NULL, "Mic Bias" },
0122 { "Mic Bias", NULL, "External Microphone" },
0123 };
0124
0125
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
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
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");