diff options
-rw-r--r-- | Documentation/devicetree/bindings/sound/tegra-audio-wm8903.txt | 71 | ||||
-rw-r--r-- | sound/soc/tegra/tegra_wm8903.c | 128 |
2 files changed, 174 insertions, 25 deletions
diff --git a/Documentation/devicetree/bindings/sound/tegra-audio-wm8903.txt b/Documentation/devicetree/bindings/sound/tegra-audio-wm8903.txt new file mode 100644 index 000000000000..d5b0da8bf1d8 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/tegra-audio-wm8903.txt | |||
@@ -0,0 +1,71 @@ | |||
1 | NVIDIA Tegra audio complex | ||
2 | |||
3 | Required properties: | ||
4 | - compatible : "nvidia,tegra-audio-wm8903" | ||
5 | - nvidia,model : The user-visible name of this sound complex. | ||
6 | - nvidia,audio-routing : A list of the connections between audio components. | ||
7 | Each entry is a pair of strings, the first being the connection's sink, | ||
8 | the second being the connection's source. Valid names for sources and | ||
9 | sinks are the WM8903's pins, and the jacks on the board: | ||
10 | |||
11 | WM8903 pins: | ||
12 | |||
13 | * IN1L | ||
14 | * IN1R | ||
15 | * IN2L | ||
16 | * IN2R | ||
17 | * IN3L | ||
18 | * IN3R | ||
19 | * DMICDAT | ||
20 | * HPOUTL | ||
21 | * HPOUTR | ||
22 | * LINEOUTL | ||
23 | * LINEOUTR | ||
24 | * LOP | ||
25 | * LON | ||
26 | * ROP | ||
27 | * RON | ||
28 | * MICBIAS | ||
29 | |||
30 | Board connectors: | ||
31 | |||
32 | * Headphone Jack | ||
33 | * Int Spk | ||
34 | * Mic Jack | ||
35 | |||
36 | - nvidia,i2s-controller : The phandle of the Tegra I2S1 controller | ||
37 | - nvidia,audio-codec : The phandle of the WM8903 audio codec | ||
38 | |||
39 | Optional properties: | ||
40 | - nvidia,spkr-en-gpios : The GPIO that enables the speakers | ||
41 | - nvidia,hp-mute-gpios : The GPIO that mutes the headphones | ||
42 | - nvidia,hp-det-gpios : The GPIO that detect headphones are plugged in | ||
43 | - nvidia,int-mic-en-gpios : The GPIO that enables the internal microphone | ||
44 | - nvidia,ext-mic-en-gpios : The GPIO that enables the external microphone | ||
45 | |||
46 | Example: | ||
47 | |||
48 | sound { | ||
49 | compatible = "nvidia,tegra-audio-wm8903-harmony", | ||
50 | "nvidia,tegra-audio-wm8903" | ||
51 | nvidia,model = "tegra-wm8903-harmony"; | ||
52 | |||
53 | nvidia,audio-routing = | ||
54 | "Headphone Jack", "HPOUTR", | ||
55 | "Headphone Jack", "HPOUTL", | ||
56 | "Int Spk", "ROP", | ||
57 | "Int Spk", "RON", | ||
58 | "Int Spk", "LOP", | ||
59 | "Int Spk", "LON", | ||
60 | "Mic Jack", "MICBIAS", | ||
61 | "IN1L", "Mic Jack"; | ||
62 | |||
63 | nvidia,i2s-controller = <&i2s1>; | ||
64 | nvidia,audio-codec = <&wm8903>; | ||
65 | |||
66 | nvidia,spkr-en-gpios = <&codec 2 0>; | ||
67 | nvidia,hp-det-gpios = <&gpio 178 0>; /* gpio PW2 */ | ||
68 | nvidia,int-mic-en-gpios = <&gpio 184 0>; /*gpio PX0 */ | ||
69 | nvidia,ext-mic-en-gpios = <&gpio 185 0>; /* gpio PX1 */ | ||
70 | }; | ||
71 | |||
diff --git a/sound/soc/tegra/tegra_wm8903.c b/sound/soc/tegra/tegra_wm8903.c index ba2d23ea6424..4677f2666300 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; |
@@ -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) { |
@@ -348,11 +377,9 @@ static __devinit int tegra_wm8903_driver_probe(struct platform_device *pdev) | |||
348 | { | 377 | { |
349 | struct snd_soc_card *card = &snd_soc_tegra_wm8903; | 378 | struct snd_soc_card *card = &snd_soc_tegra_wm8903; |
350 | struct tegra_wm8903 *machine; | 379 | struct tegra_wm8903 *machine; |
351 | struct tegra_wm8903_platform_data *pdata; | ||
352 | int ret; | 380 | int ret; |
353 | 381 | ||
354 | pdata = pdev->dev.platform_data; | 382 | if (!pdev->dev.platform_data && !pdev->dev.of_node) { |
355 | if (!pdata) { | ||
356 | dev_err(&pdev->dev, "No platform data supplied\n"); | 383 | dev_err(&pdev->dev, "No platform data supplied\n"); |
357 | return -EINVAL; | 384 | return -EINVAL; |
358 | } | 385 | } |
@@ -364,31 +391,70 @@ static __devinit int tegra_wm8903_driver_probe(struct platform_device *pdev) | |||
364 | ret = -ENOMEM; | 391 | ret = -ENOMEM; |
365 | goto err; | 392 | goto err; |
366 | } | 393 | } |
367 | 394 | machine->pcm_dev = ERR_PTR(-EINVAL); | |
368 | machine->pdata = pdata; | ||
369 | |||
370 | ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev); | ||
371 | if (ret) | ||
372 | goto err; | ||
373 | 395 | ||
374 | card->dev = &pdev->dev; | 396 | card->dev = &pdev->dev; |
375 | platform_set_drvdata(pdev, card); | 397 | platform_set_drvdata(pdev, card); |
376 | snd_soc_card_set_drvdata(card, machine); | 398 | snd_soc_card_set_drvdata(card, machine); |
377 | 399 | ||
378 | if (machine_is_harmony()) { | 400 | if (pdev->dev.of_node) { |
379 | card->dapm_routes = harmony_audio_map; | 401 | ret = snd_soc_of_parse_card_name(card, "nvidia,model"); |
380 | card->num_dapm_routes = ARRAY_SIZE(harmony_audio_map); | 402 | if (ret) |
381 | } else if (machine_is_seaboard()) { | 403 | goto err; |
382 | card->dapm_routes = seaboard_audio_map; | 404 | |
383 | card->num_dapm_routes = ARRAY_SIZE(seaboard_audio_map); | 405 | ret = snd_soc_of_parse_audio_routing(card, |
384 | } else if (machine_is_kaen()) { | 406 | "nvidia,audio-routing"); |
385 | card->dapm_routes = kaen_audio_map; | 407 | if (ret) |
386 | card->num_dapm_routes = ARRAY_SIZE(kaen_audio_map); | 408 | goto err; |
409 | |||
410 | tegra_wm8903_dai.codec_name = NULL; | ||
411 | tegra_wm8903_dai.codec_of_node = of_parse_phandle( | ||
412 | pdev->dev.of_node, "nvidia,audio-codec", 0); | ||
413 | if (!tegra_wm8903_dai.codec_of_node) { | ||
414 | dev_err(&pdev->dev, | ||
415 | "Property 'nvidia,audio-codec' missing or invalid\n"); | ||
416 | ret = -EINVAL; | ||
417 | goto err; | ||
418 | } | ||
419 | |||
420 | tegra_wm8903_dai.cpu_dai_name = NULL; | ||
421 | tegra_wm8903_dai.cpu_dai_of_node = of_parse_phandle( | ||
422 | pdev->dev.of_node, "nvidia,i2s-controller", 0); | ||
423 | if (!tegra_wm8903_dai.cpu_dai_of_node) { | ||
424 | dev_err(&pdev->dev, | ||
425 | "Property 'nvidia,i2s-controller' missing or invalid\n"); | ||
426 | ret = -EINVAL; | ||
427 | goto err; | ||
428 | } | ||
429 | |||
430 | machine->pcm_dev = platform_device_register_simple( | ||
431 | "tegra-pcm-audio", -1, NULL, 0); | ||
432 | if (IS_ERR(machine->pcm_dev)) { | ||
433 | dev_err(&pdev->dev, | ||
434 | "Can't instantiate tegra-pcm-audio\n"); | ||
435 | ret = PTR_ERR(machine->pcm_dev); | ||
436 | goto err; | ||
437 | } | ||
387 | } else { | 438 | } else { |
388 | card->dapm_routes = aebl_audio_map; | 439 | if (machine_is_harmony()) { |
389 | card->num_dapm_routes = ARRAY_SIZE(aebl_audio_map); | 440 | card->dapm_routes = harmony_audio_map; |
441 | card->num_dapm_routes = ARRAY_SIZE(harmony_audio_map); | ||
442 | } else if (machine_is_seaboard()) { | ||
443 | card->dapm_routes = seaboard_audio_map; | ||
444 | card->num_dapm_routes = ARRAY_SIZE(seaboard_audio_map); | ||
445 | } else if (machine_is_kaen()) { | ||
446 | card->dapm_routes = kaen_audio_map; | ||
447 | card->num_dapm_routes = ARRAY_SIZE(kaen_audio_map); | ||
448 | } else { | ||
449 | card->dapm_routes = aebl_audio_map; | ||
450 | card->num_dapm_routes = ARRAY_SIZE(aebl_audio_map); | ||
451 | } | ||
390 | } | 452 | } |
391 | 453 | ||
454 | ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev); | ||
455 | if (ret) | ||
456 | goto err_unregister; | ||
457 | |||
392 | ret = snd_soc_register_card(card); | 458 | ret = snd_soc_register_card(card); |
393 | if (ret) { | 459 | if (ret) { |
394 | dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", | 460 | dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", |
@@ -400,6 +466,9 @@ static __devinit int tegra_wm8903_driver_probe(struct platform_device *pdev) | |||
400 | 466 | ||
401 | err_fini_utils: | 467 | err_fini_utils: |
402 | tegra_asoc_utils_fini(&machine->util_data); | 468 | tegra_asoc_utils_fini(&machine->util_data); |
469 | err_unregister: | ||
470 | if (!IS_ERR(machine->pcm_dev)) | ||
471 | platform_device_unregister(machine->pcm_dev); | ||
403 | err: | 472 | err: |
404 | return ret; | 473 | return ret; |
405 | } | 474 | } |
@@ -408,7 +477,7 @@ static int __devexit tegra_wm8903_driver_remove(struct platform_device *pdev) | |||
408 | { | 477 | { |
409 | struct snd_soc_card *card = platform_get_drvdata(pdev); | 478 | struct snd_soc_card *card = platform_get_drvdata(pdev); |
410 | struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card); | 479 | struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card); |
411 | struct tegra_wm8903_platform_data *pdata = machine->pdata; | 480 | struct tegra_wm8903_platform_data *pdata = &machine->pdata; |
412 | 481 | ||
413 | if (machine->gpio_requested & GPIO_HP_DET) | 482 | if (machine->gpio_requested & GPIO_HP_DET) |
414 | snd_soc_jack_free_gpios(&tegra_wm8903_hp_jack, | 483 | snd_soc_jack_free_gpios(&tegra_wm8903_hp_jack, |
@@ -427,15 +496,23 @@ static int __devexit tegra_wm8903_driver_remove(struct platform_device *pdev) | |||
427 | snd_soc_unregister_card(card); | 496 | snd_soc_unregister_card(card); |
428 | 497 | ||
429 | tegra_asoc_utils_fini(&machine->util_data); | 498 | tegra_asoc_utils_fini(&machine->util_data); |
499 | if (!IS_ERR(machine->pcm_dev)) | ||
500 | platform_device_unregister(machine->pcm_dev); | ||
430 | 501 | ||
431 | return 0; | 502 | return 0; |
432 | } | 503 | } |
433 | 504 | ||
505 | static const struct of_device_id tegra_wm8903_of_match[] __devinitconst = { | ||
506 | { .compatible = "nvidia,tegra-audio-wm8903", }, | ||
507 | {}, | ||
508 | }; | ||
509 | |||
434 | static struct platform_driver tegra_wm8903_driver = { | 510 | static struct platform_driver tegra_wm8903_driver = { |
435 | .driver = { | 511 | .driver = { |
436 | .name = DRV_NAME, | 512 | .name = DRV_NAME, |
437 | .owner = THIS_MODULE, | 513 | .owner = THIS_MODULE, |
438 | .pm = &snd_soc_pm_ops, | 514 | .pm = &snd_soc_pm_ops, |
515 | .of_match_table = tegra_wm8903_of_match, | ||
439 | }, | 516 | }, |
440 | .probe = tegra_wm8903_driver_probe, | 517 | .probe = tegra_wm8903_driver_probe, |
441 | .remove = __devexit_p(tegra_wm8903_driver_remove), | 518 | .remove = __devexit_p(tegra_wm8903_driver_remove), |
@@ -446,3 +523,4 @@ MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); | |||
446 | MODULE_DESCRIPTION("Tegra+WM8903 machine ASoC driver"); | 523 | MODULE_DESCRIPTION("Tegra+WM8903 machine ASoC driver"); |
447 | MODULE_LICENSE("GPL"); | 524 | MODULE_LICENSE("GPL"); |
448 | MODULE_ALIAS("platform:" DRV_NAME); | 525 | MODULE_ALIAS("platform:" DRV_NAME); |
526 | MODULE_DEVICE_TABLE(of, tegra_wm8903_of_match); | ||