0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019 #include <linux/platform_device.h>
0020 #include <linux/platform_data/omap-twl4030.h>
0021 #include <linux/module.h>
0022 #include <linux/of.h>
0023 #include <linux/gpio.h>
0024 #include <linux/of_gpio.h>
0025
0026 #include <sound/core.h>
0027 #include <sound/pcm.h>
0028 #include <sound/soc.h>
0029 #include <sound/jack.h>
0030
0031 #include "omap-mcbsp.h"
0032
0033 struct omap_twl4030 {
0034 int jack_detect;
0035 struct snd_soc_jack hs_jack;
0036 };
0037
0038 static int omap_twl4030_hw_params(struct snd_pcm_substream *substream,
0039 struct snd_pcm_hw_params *params)
0040 {
0041 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
0042 unsigned int fmt;
0043
0044 switch (params_channels(params)) {
0045 case 2:
0046 fmt = SND_SOC_DAIFMT_I2S |
0047 SND_SOC_DAIFMT_NB_NF |
0048 SND_SOC_DAIFMT_CBM_CFM;
0049 break;
0050 case 4:
0051 fmt = SND_SOC_DAIFMT_DSP_A |
0052 SND_SOC_DAIFMT_IB_NF |
0053 SND_SOC_DAIFMT_CBM_CFM;
0054 break;
0055 default:
0056 return -EINVAL;
0057 }
0058
0059 return snd_soc_runtime_set_dai_fmt(rtd, fmt);
0060 }
0061
0062 static const struct snd_soc_ops omap_twl4030_ops = {
0063 .hw_params = omap_twl4030_hw_params,
0064 };
0065
0066 static const struct snd_soc_dapm_widget dapm_widgets[] = {
0067 SND_SOC_DAPM_SPK("Earpiece Spk", NULL),
0068 SND_SOC_DAPM_SPK("Handsfree Spk", NULL),
0069 SND_SOC_DAPM_HP("Headset Stereophone", NULL),
0070 SND_SOC_DAPM_SPK("Ext Spk", NULL),
0071 SND_SOC_DAPM_SPK("Carkit Spk", NULL),
0072
0073 SND_SOC_DAPM_MIC("Main Mic", NULL),
0074 SND_SOC_DAPM_MIC("Sub Mic", NULL),
0075 SND_SOC_DAPM_MIC("Headset Mic", NULL),
0076 SND_SOC_DAPM_MIC("Carkit Mic", NULL),
0077 SND_SOC_DAPM_MIC("Digital0 Mic", NULL),
0078 SND_SOC_DAPM_MIC("Digital1 Mic", NULL),
0079 SND_SOC_DAPM_LINE("Line In", NULL),
0080 };
0081
0082 static const struct snd_soc_dapm_route audio_map[] = {
0083
0084 {"Headset Stereophone", NULL, "HSOL"},
0085 {"Headset Stereophone", NULL, "HSOR"},
0086
0087 {"Handsfree Spk", NULL, "HFL"},
0088 {"Handsfree Spk", NULL, "HFR"},
0089
0090 {"Ext Spk", NULL, "PREDRIVEL"},
0091 {"Ext Spk", NULL, "PREDRIVER"},
0092
0093 {"Carkit Spk", NULL, "CARKITL"},
0094 {"Carkit Spk", NULL, "CARKITR"},
0095
0096 {"Earpiece Spk", NULL, "EARPIECE"},
0097
0098
0099 {"MAINMIC", NULL, "Main Mic"},
0100 {"Main Mic", NULL, "Mic Bias 1"},
0101 {"SUBMIC", NULL, "Sub Mic"},
0102 {"Sub Mic", NULL, "Mic Bias 2"},
0103
0104 {"HSMIC", NULL, "Headset Mic"},
0105 {"Headset Mic", NULL, "Headset Mic Bias"},
0106
0107 {"DIGIMIC0", NULL, "Digital0 Mic"},
0108 {"Digital0 Mic", NULL, "Mic Bias 1"},
0109 {"DIGIMIC1", NULL, "Digital1 Mic"},
0110 {"Digital1 Mic", NULL, "Mic Bias 2"},
0111
0112 {"CARKITMIC", NULL, "Carkit Mic"},
0113
0114 {"AUXL", NULL, "Line In"},
0115 {"AUXR", NULL, "Line In"},
0116 };
0117
0118
0119 static struct snd_soc_jack_pin hs_jack_pins[] = {
0120 {
0121 .pin = "Headset Mic",
0122 .mask = SND_JACK_MICROPHONE,
0123 },
0124 {
0125 .pin = "Headset Stereophone",
0126 .mask = SND_JACK_HEADPHONE,
0127 },
0128 };
0129
0130
0131 static struct snd_soc_jack_gpio hs_jack_gpios[] = {
0132 {
0133 .name = "hsdet-gpio",
0134 .report = SND_JACK_HEADSET,
0135 .debounce_time = 200,
0136 },
0137 };
0138
0139 static inline void twl4030_disconnect_pin(struct snd_soc_dapm_context *dapm,
0140 int connected, char *pin)
0141 {
0142 if (!connected)
0143 snd_soc_dapm_disable_pin(dapm, pin);
0144 }
0145
0146 static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd)
0147 {
0148 struct snd_soc_card *card = rtd->card;
0149 struct snd_soc_dapm_context *dapm = &card->dapm;
0150 struct omap_tw4030_pdata *pdata = dev_get_platdata(card->dev);
0151 struct omap_twl4030 *priv = snd_soc_card_get_drvdata(card);
0152 int ret = 0;
0153
0154
0155 if (priv->jack_detect > 0) {
0156 hs_jack_gpios[0].gpio = priv->jack_detect;
0157
0158 ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
0159 SND_JACK_HEADSET,
0160 &priv->hs_jack, hs_jack_pins,
0161 ARRAY_SIZE(hs_jack_pins));
0162 if (ret)
0163 return ret;
0164
0165 ret = snd_soc_jack_add_gpios(&priv->hs_jack,
0166 ARRAY_SIZE(hs_jack_gpios),
0167 hs_jack_gpios);
0168 if (ret)
0169 return ret;
0170 }
0171
0172
0173
0174
0175
0176 if (!pdata || !pdata->custom_routing)
0177 return ret;
0178
0179
0180 twl4030_disconnect_pin(dapm, pdata->has_ear, "Earpiece Spk");
0181 twl4030_disconnect_pin(dapm, pdata->has_hf, "Handsfree Spk");
0182 twl4030_disconnect_pin(dapm, pdata->has_hs, "Headset Stereophone");
0183 twl4030_disconnect_pin(dapm, pdata->has_predriv, "Ext Spk");
0184 twl4030_disconnect_pin(dapm, pdata->has_carkit, "Carkit Spk");
0185
0186 twl4030_disconnect_pin(dapm, pdata->has_mainmic, "Main Mic");
0187 twl4030_disconnect_pin(dapm, pdata->has_submic, "Sub Mic");
0188 twl4030_disconnect_pin(dapm, pdata->has_hsmic, "Headset Mic");
0189 twl4030_disconnect_pin(dapm, pdata->has_carkitmic, "Carkit Mic");
0190 twl4030_disconnect_pin(dapm, pdata->has_digimic0, "Digital0 Mic");
0191 twl4030_disconnect_pin(dapm, pdata->has_digimic1, "Digital1 Mic");
0192 twl4030_disconnect_pin(dapm, pdata->has_linein, "Line In");
0193
0194 return ret;
0195 }
0196
0197
0198 SND_SOC_DAILINK_DEFS(hifi,
0199 DAILINK_COMP_ARRAY(COMP_CPU("omap-mcbsp.2")),
0200 DAILINK_COMP_ARRAY(COMP_CODEC("twl4030-codec", "twl4030-hifi")),
0201 DAILINK_COMP_ARRAY(COMP_PLATFORM("omap-mcbsp.2")));
0202
0203 SND_SOC_DAILINK_DEFS(voice,
0204 DAILINK_COMP_ARRAY(COMP_CPU("omap-mcbsp.3")),
0205 DAILINK_COMP_ARRAY(COMP_CODEC("twl4030-codec", "twl4030-voice")),
0206 DAILINK_COMP_ARRAY(COMP_PLATFORM("omap-mcbsp.3")));
0207
0208 static struct snd_soc_dai_link omap_twl4030_dai_links[] = {
0209 {
0210 .name = "TWL4030 HiFi",
0211 .stream_name = "TWL4030 HiFi",
0212 .init = omap_twl4030_init,
0213 .ops = &omap_twl4030_ops,
0214 SND_SOC_DAILINK_REG(hifi),
0215 },
0216 {
0217 .name = "TWL4030 Voice",
0218 .stream_name = "TWL4030 Voice",
0219 .dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF |
0220 SND_SOC_DAIFMT_CBM_CFM,
0221 SND_SOC_DAILINK_REG(voice),
0222 },
0223 };
0224
0225
0226 static struct snd_soc_card omap_twl4030_card = {
0227 .owner = THIS_MODULE,
0228 .dai_link = omap_twl4030_dai_links,
0229 .num_links = ARRAY_SIZE(omap_twl4030_dai_links),
0230
0231 .dapm_widgets = dapm_widgets,
0232 .num_dapm_widgets = ARRAY_SIZE(dapm_widgets),
0233 .dapm_routes = audio_map,
0234 .num_dapm_routes = ARRAY_SIZE(audio_map),
0235 };
0236
0237 static int omap_twl4030_probe(struct platform_device *pdev)
0238 {
0239 struct omap_tw4030_pdata *pdata = dev_get_platdata(&pdev->dev);
0240 struct device_node *node = pdev->dev.of_node;
0241 struct snd_soc_card *card = &omap_twl4030_card;
0242 struct omap_twl4030 *priv;
0243 int ret = 0;
0244
0245 card->dev = &pdev->dev;
0246
0247 priv = devm_kzalloc(&pdev->dev, sizeof(struct omap_twl4030), GFP_KERNEL);
0248 if (priv == NULL)
0249 return -ENOMEM;
0250
0251 if (node) {
0252 struct device_node *dai_node;
0253 struct property *prop;
0254
0255 if (snd_soc_of_parse_card_name(card, "ti,model")) {
0256 dev_err(&pdev->dev, "Card name is not provided\n");
0257 return -ENODEV;
0258 }
0259
0260 dai_node = of_parse_phandle(node, "ti,mcbsp", 0);
0261 if (!dai_node) {
0262 dev_err(&pdev->dev, "McBSP node is not provided\n");
0263 return -EINVAL;
0264 }
0265 omap_twl4030_dai_links[0].cpus->dai_name = NULL;
0266 omap_twl4030_dai_links[0].cpus->of_node = dai_node;
0267
0268 omap_twl4030_dai_links[0].platforms->name = NULL;
0269 omap_twl4030_dai_links[0].platforms->of_node = dai_node;
0270
0271 dai_node = of_parse_phandle(node, "ti,mcbsp-voice", 0);
0272 if (!dai_node) {
0273 card->num_links = 1;
0274 } else {
0275 omap_twl4030_dai_links[1].cpus->dai_name = NULL;
0276 omap_twl4030_dai_links[1].cpus->of_node = dai_node;
0277
0278 omap_twl4030_dai_links[1].platforms->name = NULL;
0279 omap_twl4030_dai_links[1].platforms->of_node = dai_node;
0280 }
0281
0282 priv->jack_detect = of_get_named_gpio(node,
0283 "ti,jack-det-gpio", 0);
0284
0285
0286 prop = of_find_property(node, "ti,audio-routing", NULL);
0287 if (prop) {
0288 ret = snd_soc_of_parse_audio_routing(card,
0289 "ti,audio-routing");
0290 if (ret)
0291 return ret;
0292
0293 card->fully_routed = 1;
0294 }
0295 } else if (pdata) {
0296 if (pdata->card_name) {
0297 card->name = pdata->card_name;
0298 } else {
0299 dev_err(&pdev->dev, "Card name is not provided\n");
0300 return -ENODEV;
0301 }
0302
0303 if (!pdata->voice_connected)
0304 card->num_links = 1;
0305
0306 priv->jack_detect = pdata->jack_detect;
0307 } else {
0308 dev_err(&pdev->dev, "Missing pdata\n");
0309 return -ENODEV;
0310 }
0311
0312 snd_soc_card_set_drvdata(card, priv);
0313 ret = devm_snd_soc_register_card(&pdev->dev, card);
0314 if (ret) {
0315 dev_err(&pdev->dev, "devm_snd_soc_register_card() failed: %d\n",
0316 ret);
0317 return ret;
0318 }
0319
0320 return 0;
0321 }
0322
0323 static const struct of_device_id omap_twl4030_of_match[] = {
0324 {.compatible = "ti,omap-twl4030", },
0325 { },
0326 };
0327 MODULE_DEVICE_TABLE(of, omap_twl4030_of_match);
0328
0329 static struct platform_driver omap_twl4030_driver = {
0330 .driver = {
0331 .name = "omap-twl4030",
0332 .pm = &snd_soc_pm_ops,
0333 .of_match_table = omap_twl4030_of_match,
0334 },
0335 .probe = omap_twl4030_probe,
0336 };
0337
0338 module_platform_driver(omap_twl4030_driver);
0339
0340 MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
0341 MODULE_DESCRIPTION("ALSA SoC for TI SoC based boards with twl4030 codec");
0342 MODULE_LICENSE("GPL");
0343 MODULE_ALIAS("platform:omap-twl4030");