0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/clk.h>
0010 #include <linux/platform_device.h>
0011 #include <linux/mfd/twl6040.h>
0012 #include <linux/module.h>
0013 #include <linux/of.h>
0014
0015 #include <sound/core.h>
0016 #include <sound/pcm.h>
0017 #include <sound/soc.h>
0018 #include <sound/jack.h>
0019
0020 #include "omap-dmic.h"
0021 #include "omap-mcpdm.h"
0022 #include "../codecs/twl6040.h"
0023
0024 SND_SOC_DAILINK_DEFS(link0,
0025 DAILINK_COMP_ARRAY(COMP_EMPTY()),
0026 DAILINK_COMP_ARRAY(COMP_CODEC("twl6040-codec",
0027 "twl6040-legacy")),
0028 DAILINK_COMP_ARRAY(COMP_EMPTY()));
0029
0030 SND_SOC_DAILINK_DEFS(link1,
0031 DAILINK_COMP_ARRAY(COMP_EMPTY()),
0032 DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec",
0033 "dmic-hifi")),
0034 DAILINK_COMP_ARRAY(COMP_EMPTY()));
0035
0036 struct abe_twl6040 {
0037 struct snd_soc_card card;
0038 struct snd_soc_dai_link dai_links[2];
0039 int jack_detection;
0040 int mclk_freq;
0041 };
0042
0043 static struct platform_device *dmic_codec_dev;
0044
0045 static int omap_abe_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 struct snd_soc_card *card = rtd->card;
0051 struct abe_twl6040 *priv = snd_soc_card_get_drvdata(card);
0052 int clk_id, freq;
0053 int ret;
0054
0055 clk_id = twl6040_get_clk_id(codec_dai->component);
0056 if (clk_id == TWL6040_SYSCLK_SEL_HPPLL)
0057 freq = priv->mclk_freq;
0058 else if (clk_id == TWL6040_SYSCLK_SEL_LPPLL)
0059 freq = 32768;
0060 else
0061 return -EINVAL;
0062
0063
0064 ret = snd_soc_dai_set_sysclk(codec_dai, clk_id, freq,
0065 SND_SOC_CLOCK_IN);
0066 if (ret) {
0067 printk(KERN_ERR "can't set codec system clock\n");
0068 return ret;
0069 }
0070 return ret;
0071 }
0072
0073 static const struct snd_soc_ops omap_abe_ops = {
0074 .hw_params = omap_abe_hw_params,
0075 };
0076
0077 static int omap_abe_dmic_hw_params(struct snd_pcm_substream *substream,
0078 struct snd_pcm_hw_params *params)
0079 {
0080 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
0081 struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
0082 int ret = 0;
0083
0084 ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_DMIC_SYSCLK_PAD_CLKS,
0085 19200000, SND_SOC_CLOCK_IN);
0086 if (ret < 0) {
0087 printk(KERN_ERR "can't set DMIC cpu system clock\n");
0088 return ret;
0089 }
0090 ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_DMIC_ABE_DMIC_CLK, 2400000,
0091 SND_SOC_CLOCK_OUT);
0092 if (ret < 0) {
0093 printk(KERN_ERR "can't set DMIC output clock\n");
0094 return ret;
0095 }
0096 return 0;
0097 }
0098
0099 static const struct snd_soc_ops omap_abe_dmic_ops = {
0100 .hw_params = omap_abe_dmic_hw_params,
0101 };
0102
0103
0104 static struct snd_soc_jack hs_jack;
0105
0106
0107 static struct snd_soc_jack_pin hs_jack_pins[] = {
0108 {
0109 .pin = "Headset Mic",
0110 .mask = SND_JACK_MICROPHONE,
0111 },
0112 {
0113 .pin = "Headset Stereophone",
0114 .mask = SND_JACK_HEADPHONE,
0115 },
0116 };
0117
0118
0119 static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = {
0120
0121 SND_SOC_DAPM_HP("Headset Stereophone", NULL),
0122 SND_SOC_DAPM_SPK("Earphone Spk", NULL),
0123 SND_SOC_DAPM_SPK("Ext Spk", NULL),
0124 SND_SOC_DAPM_LINE("Line Out", NULL),
0125 SND_SOC_DAPM_SPK("Vibrator", NULL),
0126
0127
0128 SND_SOC_DAPM_MIC("Headset Mic", NULL),
0129 SND_SOC_DAPM_MIC("Main Handset Mic", NULL),
0130 SND_SOC_DAPM_MIC("Sub Handset Mic", NULL),
0131 SND_SOC_DAPM_LINE("Line In", NULL),
0132
0133
0134 SND_SOC_DAPM_MIC("Digital Mic", NULL),
0135 };
0136
0137 static const struct snd_soc_dapm_route audio_map[] = {
0138
0139 {"Headset Stereophone", NULL, "HSOL"},
0140 {"Headset Stereophone", NULL, "HSOR"},
0141
0142 {"Earphone Spk", NULL, "EP"},
0143
0144 {"Ext Spk", NULL, "HFL"},
0145 {"Ext Spk", NULL, "HFR"},
0146
0147 {"Line Out", NULL, "AUXL"},
0148 {"Line Out", NULL, "AUXR"},
0149
0150 {"Vibrator", NULL, "VIBRAL"},
0151 {"Vibrator", NULL, "VIBRAR"},
0152
0153
0154 {"HSMIC", NULL, "Headset Mic"},
0155 {"Headset Mic", NULL, "Headset Mic Bias"},
0156
0157 {"MAINMIC", NULL, "Main Handset Mic"},
0158 {"Main Handset Mic", NULL, "Main Mic Bias"},
0159
0160 {"SUBMIC", NULL, "Sub Handset Mic"},
0161 {"Sub Handset Mic", NULL, "Main Mic Bias"},
0162
0163 {"AFML", NULL, "Line In"},
0164 {"AFMR", NULL, "Line In"},
0165 };
0166
0167 static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd)
0168 {
0169 struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
0170 struct snd_soc_card *card = rtd->card;
0171 struct abe_twl6040 *priv = snd_soc_card_get_drvdata(card);
0172 int hs_trim;
0173 int ret;
0174
0175
0176
0177
0178
0179 hs_trim = twl6040_get_trim_value(component, TWL6040_TRIM_HSOTRIM);
0180 omap_mcpdm_configure_dn_offsets(rtd, TWL6040_HSF_TRIM_LEFT(hs_trim),
0181 TWL6040_HSF_TRIM_RIGHT(hs_trim));
0182
0183
0184 if (priv->jack_detection) {
0185 ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
0186 SND_JACK_HEADSET, &hs_jack,
0187 hs_jack_pins,
0188 ARRAY_SIZE(hs_jack_pins));
0189 if (ret)
0190 return ret;
0191
0192 twl6040_hs_jack_detect(component, &hs_jack, SND_JACK_HEADSET);
0193 }
0194
0195 return 0;
0196 }
0197
0198 static const struct snd_soc_dapm_route dmic_audio_map[] = {
0199 {"DMic", NULL, "Digital Mic"},
0200 {"Digital Mic", NULL, "Digital Mic1 Bias"},
0201 };
0202
0203 static int omap_abe_dmic_init(struct snd_soc_pcm_runtime *rtd)
0204 {
0205 struct snd_soc_dapm_context *dapm = &rtd->card->dapm;
0206
0207 return snd_soc_dapm_add_routes(dapm, dmic_audio_map,
0208 ARRAY_SIZE(dmic_audio_map));
0209 }
0210
0211 static int omap_abe_probe(struct platform_device *pdev)
0212 {
0213 struct device_node *node = pdev->dev.of_node;
0214 struct snd_soc_card *card;
0215 struct device_node *dai_node;
0216 struct abe_twl6040 *priv;
0217 int num_links = 0;
0218 int ret = 0;
0219
0220 if (!node) {
0221 dev_err(&pdev->dev, "of node is missing.\n");
0222 return -ENODEV;
0223 }
0224
0225 priv = devm_kzalloc(&pdev->dev, sizeof(struct abe_twl6040), GFP_KERNEL);
0226 if (priv == NULL)
0227 return -ENOMEM;
0228
0229 card = &priv->card;
0230 card->dev = &pdev->dev;
0231 card->owner = THIS_MODULE;
0232 card->dapm_widgets = twl6040_dapm_widgets;
0233 card->num_dapm_widgets = ARRAY_SIZE(twl6040_dapm_widgets);
0234 card->dapm_routes = audio_map;
0235 card->num_dapm_routes = ARRAY_SIZE(audio_map);
0236
0237 if (snd_soc_of_parse_card_name(card, "ti,model")) {
0238 dev_err(&pdev->dev, "Card name is not provided\n");
0239 return -ENODEV;
0240 }
0241
0242 ret = snd_soc_of_parse_audio_routing(card, "ti,audio-routing");
0243 if (ret) {
0244 dev_err(&pdev->dev, "Error while parsing DAPM routing\n");
0245 return ret;
0246 }
0247
0248 dai_node = of_parse_phandle(node, "ti,mcpdm", 0);
0249 if (!dai_node) {
0250 dev_err(&pdev->dev, "McPDM node is not provided\n");
0251 return -EINVAL;
0252 }
0253
0254 priv->dai_links[0].name = "DMIC";
0255 priv->dai_links[0].stream_name = "TWL6040";
0256 priv->dai_links[0].cpus = link0_cpus;
0257 priv->dai_links[0].num_cpus = 1;
0258 priv->dai_links[0].cpus->of_node = dai_node;
0259 priv->dai_links[0].platforms = link0_platforms;
0260 priv->dai_links[0].num_platforms = 1;
0261 priv->dai_links[0].platforms->of_node = dai_node;
0262 priv->dai_links[0].codecs = link0_codecs;
0263 priv->dai_links[0].num_codecs = 1;
0264 priv->dai_links[0].init = omap_abe_twl6040_init;
0265 priv->dai_links[0].ops = &omap_abe_ops;
0266
0267 dai_node = of_parse_phandle(node, "ti,dmic", 0);
0268 if (dai_node) {
0269 num_links = 2;
0270 priv->dai_links[1].name = "TWL6040";
0271 priv->dai_links[1].stream_name = "DMIC Capture";
0272 priv->dai_links[1].cpus = link1_cpus;
0273 priv->dai_links[1].num_cpus = 1;
0274 priv->dai_links[1].cpus->of_node = dai_node;
0275 priv->dai_links[1].platforms = link1_platforms;
0276 priv->dai_links[1].num_platforms = 1;
0277 priv->dai_links[1].platforms->of_node = dai_node;
0278 priv->dai_links[1].codecs = link1_codecs;
0279 priv->dai_links[1].num_codecs = 1;
0280 priv->dai_links[1].init = omap_abe_dmic_init;
0281 priv->dai_links[1].ops = &omap_abe_dmic_ops;
0282 } else {
0283 num_links = 1;
0284 }
0285
0286 priv->jack_detection = of_property_read_bool(node, "ti,jack-detection");
0287 of_property_read_u32(node, "ti,mclk-freq", &priv->mclk_freq);
0288 if (!priv->mclk_freq) {
0289 dev_err(&pdev->dev, "MCLK frequency not provided\n");
0290 return -EINVAL;
0291 }
0292
0293 card->fully_routed = 1;
0294
0295 card->dai_link = priv->dai_links;
0296 card->num_links = num_links;
0297
0298 snd_soc_card_set_drvdata(card, priv);
0299
0300 ret = devm_snd_soc_register_card(&pdev->dev, card);
0301 if (ret)
0302 dev_err(&pdev->dev, "devm_snd_soc_register_card() failed: %d\n",
0303 ret);
0304
0305 return ret;
0306 }
0307
0308 static const struct of_device_id omap_abe_of_match[] = {
0309 {.compatible = "ti,abe-twl6040", },
0310 { },
0311 };
0312 MODULE_DEVICE_TABLE(of, omap_abe_of_match);
0313
0314 static struct platform_driver omap_abe_driver = {
0315 .driver = {
0316 .name = "omap-abe-twl6040",
0317 .pm = &snd_soc_pm_ops,
0318 .of_match_table = omap_abe_of_match,
0319 },
0320 .probe = omap_abe_probe,
0321 };
0322
0323 static int __init omap_abe_init(void)
0324 {
0325 int ret;
0326
0327 dmic_codec_dev = platform_device_register_simple("dmic-codec", -1, NULL,
0328 0);
0329 if (IS_ERR(dmic_codec_dev)) {
0330 pr_err("%s: dmic-codec device registration failed\n", __func__);
0331 return PTR_ERR(dmic_codec_dev);
0332 }
0333
0334 ret = platform_driver_register(&omap_abe_driver);
0335 if (ret) {
0336 pr_err("%s: platform driver registration failed\n", __func__);
0337 platform_device_unregister(dmic_codec_dev);
0338 }
0339
0340 return ret;
0341 }
0342 module_init(omap_abe_init);
0343
0344 static void __exit omap_abe_exit(void)
0345 {
0346 platform_driver_unregister(&omap_abe_driver);
0347 platform_device_unregister(dmic_codec_dev);
0348 }
0349 module_exit(omap_abe_exit);
0350
0351 MODULE_AUTHOR("Misael Lopez Cruz <misael.lopez@ti.com>");
0352 MODULE_DESCRIPTION("ALSA SoC for OMAP boards with ABE and twl6040 codec");
0353 MODULE_LICENSE("GPL");
0354 MODULE_ALIAS("platform:omap-abe-twl6040");