diff options
Diffstat (limited to 'sound/soc/tegra/tegra_wm8903.c')
-rw-r--r-- | sound/soc/tegra/tegra_wm8903.c | 193 |
1 files changed, 120 insertions, 73 deletions
diff --git a/sound/soc/tegra/tegra_wm8903.c b/sound/soc/tegra/tegra_wm8903.c index a81cf39257bf..566655e23b7d 100644 --- a/sound/soc/tegra/tegra_wm8903.c +++ b/sound/soc/tegra/tegra_wm8903.c | |||
@@ -34,6 +34,7 @@ | |||
34 | #include <linux/platform_device.h> | 34 | #include <linux/platform_device.h> |
35 | #include <linux/slab.h> | 35 | #include <linux/slab.h> |
36 | #include <linux/gpio.h> | 36 | #include <linux/gpio.h> |
37 | #include <linux/of_gpio.h> | ||
37 | 38 | ||
38 | #include <mach/tegra_wm8903_pdata.h> | 39 | #include <mach/tegra_wm8903_pdata.h> |
39 | 40 | ||
@@ -59,8 +60,9 @@ | |||
59 | #define GPIO_HP_DET BIT(4) | 60 | #define GPIO_HP_DET BIT(4) |
60 | 61 | ||
61 | struct tegra_wm8903 { | 62 | struct tegra_wm8903 { |
63 | struct tegra_wm8903_platform_data pdata; | ||
64 | struct platform_device *pcm_dev; | ||
62 | struct tegra_asoc_utils_data util_data; | 65 | struct tegra_asoc_utils_data util_data; |
63 | struct tegra_wm8903_platform_data *pdata; | ||
64 | int gpio_requested; | 66 | int gpio_requested; |
65 | }; | 67 | }; |
66 | 68 | ||
@@ -160,7 +162,7 @@ static int tegra_wm8903_event_int_spk(struct snd_soc_dapm_widget *w, | |||
160 | struct snd_soc_dapm_context *dapm = w->dapm; | 162 | struct snd_soc_dapm_context *dapm = w->dapm; |
161 | struct snd_soc_card *card = dapm->card; | 163 | struct snd_soc_card *card = dapm->card; |
162 | struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card); | 164 | struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card); |
163 | struct tegra_wm8903_platform_data *pdata = machine->pdata; | 165 | struct tegra_wm8903_platform_data *pdata = &machine->pdata; |
164 | 166 | ||
165 | if (!(machine->gpio_requested & GPIO_SPKR_EN)) | 167 | if (!(machine->gpio_requested & GPIO_SPKR_EN)) |
166 | return 0; | 168 | return 0; |
@@ -177,7 +179,7 @@ static int tegra_wm8903_event_hp(struct snd_soc_dapm_widget *w, | |||
177 | struct snd_soc_dapm_context *dapm = w->dapm; | 179 | struct snd_soc_dapm_context *dapm = w->dapm; |
178 | struct snd_soc_card *card = dapm->card; | 180 | struct snd_soc_card *card = dapm->card; |
179 | struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card); | 181 | struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card); |
180 | struct tegra_wm8903_platform_data *pdata = machine->pdata; | 182 | struct tegra_wm8903_platform_data *pdata = &machine->pdata; |
181 | 183 | ||
182 | if (!(machine->gpio_requested & GPIO_HP_MUTE)) | 184 | if (!(machine->gpio_requested & GPIO_HP_MUTE)) |
183 | return 0; | 185 | return 0; |
@@ -201,8 +203,8 @@ static const struct snd_soc_dapm_route harmony_audio_map[] = { | |||
201 | {"Int Spk", NULL, "RON"}, | 203 | {"Int Spk", NULL, "RON"}, |
202 | {"Int Spk", NULL, "LOP"}, | 204 | {"Int Spk", NULL, "LOP"}, |
203 | {"Int Spk", NULL, "LON"}, | 205 | {"Int Spk", NULL, "LON"}, |
204 | {"Mic Bias", NULL, "Mic Jack"}, | 206 | {"Mic Jack", NULL, "MICBIAS"}, |
205 | {"IN1L", NULL, "Mic Bias"}, | 207 | {"IN1L", NULL, "Mic Jack"}, |
206 | }; | 208 | }; |
207 | 209 | ||
208 | static const struct snd_soc_dapm_route seaboard_audio_map[] = { | 210 | static const struct snd_soc_dapm_route seaboard_audio_map[] = { |
@@ -212,8 +214,8 @@ static const struct snd_soc_dapm_route seaboard_audio_map[] = { | |||
212 | {"Int Spk", NULL, "RON"}, | 214 | {"Int Spk", NULL, "RON"}, |
213 | {"Int Spk", NULL, "LOP"}, | 215 | {"Int Spk", NULL, "LOP"}, |
214 | {"Int Spk", NULL, "LON"}, | 216 | {"Int Spk", NULL, "LON"}, |
215 | {"Mic Bias", NULL, "Mic Jack"}, | 217 | {"Mic Jack", NULL, "MICBIAS"}, |
216 | {"IN1R", NULL, "Mic Bias"}, | 218 | {"IN1R", NULL, "Mic Jack"}, |
217 | }; | 219 | }; |
218 | 220 | ||
219 | static const struct snd_soc_dapm_route kaen_audio_map[] = { | 221 | static const struct snd_soc_dapm_route kaen_audio_map[] = { |
@@ -223,8 +225,8 @@ static const struct snd_soc_dapm_route kaen_audio_map[] = { | |||
223 | {"Int Spk", NULL, "RON"}, | 225 | {"Int Spk", NULL, "RON"}, |
224 | {"Int Spk", NULL, "LOP"}, | 226 | {"Int Spk", NULL, "LOP"}, |
225 | {"Int Spk", NULL, "LON"}, | 227 | {"Int Spk", NULL, "LON"}, |
226 | {"Mic Bias", NULL, "Mic Jack"}, | 228 | {"Mic Jack", NULL, "MICBIAS"}, |
227 | {"IN2R", NULL, "Mic Bias"}, | 229 | {"IN2R", NULL, "Mic Jack"}, |
228 | }; | 230 | }; |
229 | 231 | ||
230 | static const struct snd_soc_dapm_route aebl_audio_map[] = { | 232 | static const struct snd_soc_dapm_route aebl_audio_map[] = { |
@@ -232,8 +234,8 @@ static const struct snd_soc_dapm_route aebl_audio_map[] = { | |||
232 | {"Headphone Jack", NULL, "HPOUTL"}, | 234 | {"Headphone Jack", NULL, "HPOUTL"}, |
233 | {"Int Spk", NULL, "LINEOUTR"}, | 235 | {"Int Spk", NULL, "LINEOUTR"}, |
234 | {"Int Spk", NULL, "LINEOUTL"}, | 236 | {"Int Spk", NULL, "LINEOUTL"}, |
235 | {"Mic Bias", NULL, "Mic Jack"}, | 237 | {"Mic Jack", NULL, "MICBIAS"}, |
236 | {"IN1R", NULL, "Mic Bias"}, | 238 | {"IN1R", NULL, "Mic Jack"}, |
237 | }; | 239 | }; |
238 | 240 | ||
239 | static const struct snd_kcontrol_new tegra_wm8903_controls[] = { | 241 | static const struct snd_kcontrol_new tegra_wm8903_controls[] = { |
@@ -246,9 +248,36 @@ static int tegra_wm8903_init(struct snd_soc_pcm_runtime *rtd) | |||
246 | struct snd_soc_dapm_context *dapm = &codec->dapm; | 248 | struct snd_soc_dapm_context *dapm = &codec->dapm; |
247 | struct snd_soc_card *card = codec->card; | 249 | struct snd_soc_card *card = codec->card; |
248 | struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card); | 250 | struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card); |
249 | struct tegra_wm8903_platform_data *pdata = machine->pdata; | 251 | struct tegra_wm8903_platform_data *pdata = &machine->pdata; |
252 | struct device_node *np = card->dev->of_node; | ||
250 | int ret; | 253 | int ret; |
251 | 254 | ||
255 | if (card->dev->platform_data) { | ||
256 | memcpy(pdata, card->dev->platform_data, sizeof(*pdata)); | ||
257 | } else if (np) { | ||
258 | /* | ||
259 | * This part must be in init() rather than probe() in order to | ||
260 | * guarantee that the WM8903 has been probed, and hence its | ||
261 | * GPIO controller registered, which is a pre-condition for | ||
262 | * of_get_named_gpio() to be able to map the phandles in the | ||
263 | * properties to the controller node. Given this, all | ||
264 | * pdata handling is in init() for consistency. | ||
265 | */ | ||
266 | pdata->gpio_spkr_en = of_get_named_gpio(np, | ||
267 | "nvidia,spkr-en-gpios", 0); | ||
268 | pdata->gpio_hp_mute = of_get_named_gpio(np, | ||
269 | "nvidia,hp-mute-gpios", 0); | ||
270 | pdata->gpio_hp_det = of_get_named_gpio(np, | ||
271 | "nvidia,hp-det-gpios", 0); | ||
272 | pdata->gpio_int_mic_en = of_get_named_gpio(np, | ||
273 | "nvidia,int-mic-en-gpios", 0); | ||
274 | pdata->gpio_ext_mic_en = of_get_named_gpio(np, | ||
275 | "nvidia,ext-mic-en-gpios", 0); | ||
276 | } else { | ||
277 | dev_err(card->dev, "No platform data supplied\n"); | ||
278 | return -EINVAL; | ||
279 | } | ||
280 | |||
252 | if (gpio_is_valid(pdata->gpio_spkr_en)) { | 281 | if (gpio_is_valid(pdata->gpio_spkr_en)) { |
253 | ret = gpio_request(pdata->gpio_spkr_en, "spkr_en"); | 282 | ret = gpio_request(pdata->gpio_spkr_en, "spkr_en"); |
254 | if (ret) { | 283 | if (ret) { |
@@ -316,28 +345,7 @@ static int tegra_wm8903_init(struct snd_soc_pcm_runtime *rtd) | |||
316 | wm8903_mic_detect(codec, &tegra_wm8903_mic_jack, SND_JACK_MICROPHONE, | 345 | wm8903_mic_detect(codec, &tegra_wm8903_mic_jack, SND_JACK_MICROPHONE, |
317 | 0); | 346 | 0); |
318 | 347 | ||
319 | snd_soc_dapm_force_enable_pin(dapm, "Mic Bias"); | 348 | snd_soc_dapm_force_enable_pin(dapm, "MICBIAS"); |
320 | |||
321 | /* FIXME: Calculate automatically based on DAPM routes? */ | ||
322 | if (!machine_is_harmony()) | ||
323 | snd_soc_dapm_nc_pin(dapm, "IN1L"); | ||
324 | if (!machine_is_seaboard() && !machine_is_aebl()) | ||
325 | snd_soc_dapm_nc_pin(dapm, "IN1R"); | ||
326 | snd_soc_dapm_nc_pin(dapm, "IN2L"); | ||
327 | if (!machine_is_kaen()) | ||
328 | snd_soc_dapm_nc_pin(dapm, "IN2R"); | ||
329 | snd_soc_dapm_nc_pin(dapm, "IN3L"); | ||
330 | snd_soc_dapm_nc_pin(dapm, "IN3R"); | ||
331 | |||
332 | if (machine_is_aebl()) { | ||
333 | snd_soc_dapm_nc_pin(dapm, "LON"); | ||
334 | snd_soc_dapm_nc_pin(dapm, "RON"); | ||
335 | snd_soc_dapm_nc_pin(dapm, "ROP"); | ||
336 | snd_soc_dapm_nc_pin(dapm, "LOP"); | ||
337 | } else { | ||
338 | snd_soc_dapm_nc_pin(dapm, "LINEOUTR"); | ||
339 | snd_soc_dapm_nc_pin(dapm, "LINEOUTL"); | ||
340 | } | ||
341 | 349 | ||
342 | return 0; | 350 | return 0; |
343 | } | 351 | } |
@@ -355,6 +363,7 @@ static struct snd_soc_dai_link tegra_wm8903_dai = { | |||
355 | 363 | ||
356 | static struct snd_soc_card snd_soc_tegra_wm8903 = { | 364 | static struct snd_soc_card snd_soc_tegra_wm8903 = { |
357 | .name = "tegra-wm8903", | 365 | .name = "tegra-wm8903", |
366 | .owner = THIS_MODULE, | ||
358 | .dai_link = &tegra_wm8903_dai, | 367 | .dai_link = &tegra_wm8903_dai, |
359 | .num_links = 1, | 368 | .num_links = 1, |
360 | 369 | ||
@@ -362,51 +371,91 @@ static struct snd_soc_card snd_soc_tegra_wm8903 = { | |||
362 | .num_controls = ARRAY_SIZE(tegra_wm8903_controls), | 371 | .num_controls = ARRAY_SIZE(tegra_wm8903_controls), |
363 | .dapm_widgets = tegra_wm8903_dapm_widgets, | 372 | .dapm_widgets = tegra_wm8903_dapm_widgets, |
364 | .num_dapm_widgets = ARRAY_SIZE(tegra_wm8903_dapm_widgets), | 373 | .num_dapm_widgets = ARRAY_SIZE(tegra_wm8903_dapm_widgets), |
374 | .fully_routed = true, | ||
365 | }; | 375 | }; |
366 | 376 | ||
367 | static __devinit int tegra_wm8903_driver_probe(struct platform_device *pdev) | 377 | static __devinit int tegra_wm8903_driver_probe(struct platform_device *pdev) |
368 | { | 378 | { |
369 | struct snd_soc_card *card = &snd_soc_tegra_wm8903; | 379 | struct snd_soc_card *card = &snd_soc_tegra_wm8903; |
370 | struct tegra_wm8903 *machine; | 380 | struct tegra_wm8903 *machine; |
371 | struct tegra_wm8903_platform_data *pdata; | ||
372 | int ret; | 381 | int ret; |
373 | 382 | ||
374 | pdata = pdev->dev.platform_data; | 383 | if (!pdev->dev.platform_data && !pdev->dev.of_node) { |
375 | if (!pdata) { | ||
376 | dev_err(&pdev->dev, "No platform data supplied\n"); | 384 | dev_err(&pdev->dev, "No platform data supplied\n"); |
377 | return -EINVAL; | 385 | return -EINVAL; |
378 | } | 386 | } |
379 | 387 | ||
380 | machine = kzalloc(sizeof(struct tegra_wm8903), GFP_KERNEL); | 388 | machine = devm_kzalloc(&pdev->dev, sizeof(struct tegra_wm8903), |
389 | GFP_KERNEL); | ||
381 | if (!machine) { | 390 | if (!machine) { |
382 | dev_err(&pdev->dev, "Can't allocate tegra_wm8903 struct\n"); | 391 | dev_err(&pdev->dev, "Can't allocate tegra_wm8903 struct\n"); |
383 | return -ENOMEM; | 392 | ret = -ENOMEM; |
393 | goto err; | ||
384 | } | 394 | } |
385 | 395 | machine->pcm_dev = ERR_PTR(-EINVAL); | |
386 | machine->pdata = pdata; | ||
387 | |||
388 | ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev); | ||
389 | if (ret) | ||
390 | goto err_free_machine; | ||
391 | 396 | ||
392 | card->dev = &pdev->dev; | 397 | card->dev = &pdev->dev; |
393 | platform_set_drvdata(pdev, card); | 398 | platform_set_drvdata(pdev, card); |
394 | snd_soc_card_set_drvdata(card, machine); | 399 | snd_soc_card_set_drvdata(card, machine); |
395 | 400 | ||
396 | if (machine_is_harmony()) { | 401 | if (pdev->dev.of_node) { |
397 | card->dapm_routes = harmony_audio_map; | 402 | ret = snd_soc_of_parse_card_name(card, "nvidia,model"); |
398 | card->num_dapm_routes = ARRAY_SIZE(harmony_audio_map); | 403 | if (ret) |
399 | } else if (machine_is_seaboard()) { | 404 | goto err; |
400 | card->dapm_routes = seaboard_audio_map; | 405 | |
401 | card->num_dapm_routes = ARRAY_SIZE(seaboard_audio_map); | 406 | ret = snd_soc_of_parse_audio_routing(card, |
402 | } else if (machine_is_kaen()) { | 407 | "nvidia,audio-routing"); |
403 | card->dapm_routes = kaen_audio_map; | 408 | if (ret) |
404 | card->num_dapm_routes = ARRAY_SIZE(kaen_audio_map); | 409 | goto err; |
410 | |||
411 | tegra_wm8903_dai.codec_name = NULL; | ||
412 | tegra_wm8903_dai.codec_of_node = of_parse_phandle( | ||
413 | pdev->dev.of_node, "nvidia,audio-codec", 0); | ||
414 | if (!tegra_wm8903_dai.codec_of_node) { | ||
415 | dev_err(&pdev->dev, | ||
416 | "Property 'nvidia,audio-codec' missing or invalid\n"); | ||
417 | ret = -EINVAL; | ||
418 | goto err; | ||
419 | } | ||
420 | |||
421 | tegra_wm8903_dai.cpu_dai_name = NULL; | ||
422 | tegra_wm8903_dai.cpu_dai_of_node = of_parse_phandle( | ||
423 | pdev->dev.of_node, "nvidia,i2s-controller", 0); | ||
424 | if (!tegra_wm8903_dai.cpu_dai_of_node) { | ||
425 | dev_err(&pdev->dev, | ||
426 | "Property 'nvidia,i2s-controller' missing or invalid\n"); | ||
427 | ret = -EINVAL; | ||
428 | goto err; | ||
429 | } | ||
430 | |||
431 | machine->pcm_dev = platform_device_register_simple( | ||
432 | "tegra-pcm-audio", -1, NULL, 0); | ||
433 | if (IS_ERR(machine->pcm_dev)) { | ||
434 | dev_err(&pdev->dev, | ||
435 | "Can't instantiate tegra-pcm-audio\n"); | ||
436 | ret = PTR_ERR(machine->pcm_dev); | ||
437 | goto err; | ||
438 | } | ||
405 | } else { | 439 | } else { |
406 | card->dapm_routes = aebl_audio_map; | 440 | if (machine_is_harmony()) { |
407 | card->num_dapm_routes = ARRAY_SIZE(aebl_audio_map); | 441 | card->dapm_routes = harmony_audio_map; |
442 | card->num_dapm_routes = ARRAY_SIZE(harmony_audio_map); | ||
443 | } else if (machine_is_seaboard()) { | ||
444 | card->dapm_routes = seaboard_audio_map; | ||
445 | card->num_dapm_routes = ARRAY_SIZE(seaboard_audio_map); | ||
446 | } else if (machine_is_kaen()) { | ||
447 | card->dapm_routes = kaen_audio_map; | ||
448 | card->num_dapm_routes = ARRAY_SIZE(kaen_audio_map); | ||
449 | } else { | ||
450 | card->dapm_routes = aebl_audio_map; | ||
451 | card->num_dapm_routes = ARRAY_SIZE(aebl_audio_map); | ||
452 | } | ||
408 | } | 453 | } |
409 | 454 | ||
455 | ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev); | ||
456 | if (ret) | ||
457 | goto err_unregister; | ||
458 | |||
410 | ret = snd_soc_register_card(card); | 459 | ret = snd_soc_register_card(card); |
411 | if (ret) { | 460 | if (ret) { |
412 | dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", | 461 | dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", |
@@ -418,8 +467,10 @@ static __devinit int tegra_wm8903_driver_probe(struct platform_device *pdev) | |||
418 | 467 | ||
419 | err_fini_utils: | 468 | err_fini_utils: |
420 | tegra_asoc_utils_fini(&machine->util_data); | 469 | tegra_asoc_utils_fini(&machine->util_data); |
421 | err_free_machine: | 470 | err_unregister: |
422 | kfree(machine); | 471 | if (!IS_ERR(machine->pcm_dev)) |
472 | platform_device_unregister(machine->pcm_dev); | ||
473 | err: | ||
423 | return ret; | 474 | return ret; |
424 | } | 475 | } |
425 | 476 | ||
@@ -427,7 +478,7 @@ static int __devexit tegra_wm8903_driver_remove(struct platform_device *pdev) | |||
427 | { | 478 | { |
428 | struct snd_soc_card *card = platform_get_drvdata(pdev); | 479 | struct snd_soc_card *card = platform_get_drvdata(pdev); |
429 | struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card); | 480 | struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card); |
430 | struct tegra_wm8903_platform_data *pdata = machine->pdata; | 481 | struct tegra_wm8903_platform_data *pdata = &machine->pdata; |
431 | 482 | ||
432 | if (machine->gpio_requested & GPIO_HP_DET) | 483 | if (machine->gpio_requested & GPIO_HP_DET) |
433 | snd_soc_jack_free_gpios(&tegra_wm8903_hp_jack, | 484 | snd_soc_jack_free_gpios(&tegra_wm8903_hp_jack, |
@@ -446,35 +497,31 @@ static int __devexit tegra_wm8903_driver_remove(struct platform_device *pdev) | |||
446 | snd_soc_unregister_card(card); | 497 | snd_soc_unregister_card(card); |
447 | 498 | ||
448 | tegra_asoc_utils_fini(&machine->util_data); | 499 | tegra_asoc_utils_fini(&machine->util_data); |
449 | 500 | if (!IS_ERR(machine->pcm_dev)) | |
450 | kfree(machine); | 501 | platform_device_unregister(machine->pcm_dev); |
451 | 502 | ||
452 | return 0; | 503 | return 0; |
453 | } | 504 | } |
454 | 505 | ||
506 | static const struct of_device_id tegra_wm8903_of_match[] __devinitconst = { | ||
507 | { .compatible = "nvidia,tegra-audio-wm8903", }, | ||
508 | {}, | ||
509 | }; | ||
510 | |||
455 | static struct platform_driver tegra_wm8903_driver = { | 511 | static struct platform_driver tegra_wm8903_driver = { |
456 | .driver = { | 512 | .driver = { |
457 | .name = DRV_NAME, | 513 | .name = DRV_NAME, |
458 | .owner = THIS_MODULE, | 514 | .owner = THIS_MODULE, |
459 | .pm = &snd_soc_pm_ops, | 515 | .pm = &snd_soc_pm_ops, |
516 | .of_match_table = tegra_wm8903_of_match, | ||
460 | }, | 517 | }, |
461 | .probe = tegra_wm8903_driver_probe, | 518 | .probe = tegra_wm8903_driver_probe, |
462 | .remove = __devexit_p(tegra_wm8903_driver_remove), | 519 | .remove = __devexit_p(tegra_wm8903_driver_remove), |
463 | }; | 520 | }; |
464 | 521 | module_platform_driver(tegra_wm8903_driver); | |
465 | static int __init tegra_wm8903_modinit(void) | ||
466 | { | ||
467 | return platform_driver_register(&tegra_wm8903_driver); | ||
468 | } | ||
469 | module_init(tegra_wm8903_modinit); | ||
470 | |||
471 | static void __exit tegra_wm8903_modexit(void) | ||
472 | { | ||
473 | platform_driver_unregister(&tegra_wm8903_driver); | ||
474 | } | ||
475 | module_exit(tegra_wm8903_modexit); | ||
476 | 522 | ||
477 | MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); | 523 | MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); |
478 | MODULE_DESCRIPTION("Tegra+WM8903 machine ASoC driver"); | 524 | MODULE_DESCRIPTION("Tegra+WM8903 machine ASoC driver"); |
479 | MODULE_LICENSE("GPL"); | 525 | MODULE_LICENSE("GPL"); |
480 | MODULE_ALIAS("platform:" DRV_NAME); | 526 | MODULE_ALIAS("platform:" DRV_NAME); |
527 | MODULE_DEVICE_TABLE(of, tegra_wm8903_of_match); | ||