palm27x.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. /*
  2. * linux/sound/soc/pxa/palm27x.c
  3. *
  4. * SoC Audio driver for Palm T|X, T5 and LifeDrive
  5. *
  6. * based on tosa.c
  7. *
  8. * Copyright (C) 2008 Marek Vasut <marek.vasut@gmail.com>
  9. *
  10. * This program is free software; you can redistribute it and/or modify
  11. * it under the terms of the GNU General Public License version 2 as
  12. * published by the Free Software Foundation.
  13. *
  14. */
  15. #include <linux/module.h>
  16. #include <linux/moduleparam.h>
  17. #include <linux/device.h>
  18. #include <linux/gpio.h>
  19. #include <linux/interrupt.h>
  20. #include <linux/irq.h>
  21. #include <sound/core.h>
  22. #include <sound/pcm.h>
  23. #include <sound/soc.h>
  24. #include <sound/soc-dapm.h>
  25. #include <asm/mach-types.h>
  26. #include <mach/audio.h>
  27. #include <mach/palmasoc.h>
  28. #include "../codecs/wm9712.h"
  29. #include "pxa2xx-pcm.h"
  30. #include "pxa2xx-ac97.h"
  31. static int palm27x_jack_func = 1;
  32. static int palm27x_spk_func = 1;
  33. static int palm27x_ep_gpio = -1;
  34. static void palm27x_ext_control(struct snd_soc_codec *codec)
  35. {
  36. if (!palm27x_spk_func)
  37. snd_soc_dapm_enable_pin(codec, "Speaker");
  38. else
  39. snd_soc_dapm_disable_pin(codec, "Speaker");
  40. if (!palm27x_jack_func)
  41. snd_soc_dapm_enable_pin(codec, "Headphone Jack");
  42. else
  43. snd_soc_dapm_disable_pin(codec, "Headphone Jack");
  44. snd_soc_dapm_sync(codec);
  45. }
  46. static int palm27x_startup(struct snd_pcm_substream *substream)
  47. {
  48. struct snd_soc_pcm_runtime *rtd = substream->private_data;
  49. struct snd_soc_codec *codec = rtd->socdev->codec;
  50. /* check the jack status at stream startup */
  51. palm27x_ext_control(codec);
  52. return 0;
  53. }
  54. static struct snd_soc_ops palm27x_ops = {
  55. .startup = palm27x_startup,
  56. };
  57. static irqreturn_t palm27x_interrupt(int irq, void *v)
  58. {
  59. palm27x_spk_func = gpio_get_value(palm27x_ep_gpio);
  60. palm27x_jack_func = !palm27x_spk_func;
  61. return IRQ_HANDLED;
  62. }
  63. static int palm27x_get_jack(struct snd_kcontrol *kcontrol,
  64. struct snd_ctl_elem_value *ucontrol)
  65. {
  66. ucontrol->value.integer.value[0] = palm27x_jack_func;
  67. return 0;
  68. }
  69. static int palm27x_set_jack(struct snd_kcontrol *kcontrol,
  70. struct snd_ctl_elem_value *ucontrol)
  71. {
  72. struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
  73. if (palm27x_jack_func == ucontrol->value.integer.value[0])
  74. return 0;
  75. palm27x_jack_func = ucontrol->value.integer.value[0];
  76. palm27x_ext_control(codec);
  77. return 1;
  78. }
  79. static int palm27x_get_spk(struct snd_kcontrol *kcontrol,
  80. struct snd_ctl_elem_value *ucontrol)
  81. {
  82. ucontrol->value.integer.value[0] = palm27x_spk_func;
  83. return 0;
  84. }
  85. static int palm27x_set_spk(struct snd_kcontrol *kcontrol,
  86. struct snd_ctl_elem_value *ucontrol)
  87. {
  88. struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
  89. if (palm27x_spk_func == ucontrol->value.integer.value[0])
  90. return 0;
  91. palm27x_spk_func = ucontrol->value.integer.value[0];
  92. palm27x_ext_control(codec);
  93. return 1;
  94. }
  95. /* PalmTX machine dapm widgets */
  96. static const struct snd_soc_dapm_widget palm27x_dapm_widgets[] = {
  97. SND_SOC_DAPM_HP("Headphone Jack", NULL),
  98. SND_SOC_DAPM_SPK("Speaker", NULL),
  99. };
  100. /* PalmTX audio map */
  101. static const struct snd_soc_dapm_route audio_map[] = {
  102. /* headphone connected to HPOUTL, HPOUTR */
  103. {"Headphone Jack", NULL, "HPOUTL"},
  104. {"Headphone Jack", NULL, "HPOUTR"},
  105. /* ext speaker connected to ROUT2, LOUT2 */
  106. {"Speaker", NULL, "LOUT2"},
  107. {"Speaker", NULL, "ROUT2"},
  108. };
  109. static const char *jack_function[] = {"Headphone", "Off"};
  110. static const char *spk_function[] = {"On", "Off"};
  111. static const struct soc_enum palm27x_enum[] = {
  112. SOC_ENUM_SINGLE_EXT(2, jack_function),
  113. SOC_ENUM_SINGLE_EXT(2, spk_function),
  114. };
  115. static const struct snd_kcontrol_new palm27x_controls[] = {
  116. SOC_ENUM_EXT("Jack Function", palm27x_enum[0], palm27x_get_jack,
  117. palm27x_set_jack),
  118. SOC_ENUM_EXT("Speaker Function", palm27x_enum[1], palm27x_get_spk,
  119. palm27x_set_spk),
  120. };
  121. static int palm27x_ac97_init(struct snd_soc_codec *codec)
  122. {
  123. int i, err;
  124. snd_soc_dapm_nc_pin(codec, "OUT3");
  125. snd_soc_dapm_nc_pin(codec, "MONOOUT");
  126. /* add palm27x specific controls */
  127. for (i = 0; i < ARRAY_SIZE(palm27x_controls); i++) {
  128. err = snd_ctl_add(codec->card,
  129. snd_soc_cnew(&palm27x_controls[i],
  130. codec, NULL));
  131. if (err < 0)
  132. return err;
  133. }
  134. /* add palm27x specific widgets */
  135. snd_soc_dapm_new_controls(codec, palm27x_dapm_widgets,
  136. ARRAY_SIZE(palm27x_dapm_widgets));
  137. /* set up palm27x specific audio path audio_map */
  138. snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
  139. snd_soc_dapm_sync(codec);
  140. return 0;
  141. }
  142. static struct snd_soc_dai_link palm27x_dai[] = {
  143. {
  144. .name = "AC97 HiFi",
  145. .stream_name = "AC97 HiFi",
  146. .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI],
  147. .codec_dai = &wm9712_dai[WM9712_DAI_AC97_HIFI],
  148. .init = palm27x_ac97_init,
  149. .ops = &palm27x_ops,
  150. },
  151. {
  152. .name = "AC97 Aux",
  153. .stream_name = "AC97 Aux",
  154. .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX],
  155. .codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX],
  156. .ops = &palm27x_ops,
  157. },
  158. };
  159. static struct snd_soc_card palm27x_asoc = {
  160. .name = "Palm/PXA27x",
  161. .platform = &pxa2xx_soc_platform,
  162. .dai_link = palm27x_dai,
  163. .num_links = ARRAY_SIZE(palm27x_dai),
  164. };
  165. static struct snd_soc_device palm27x_snd_devdata = {
  166. .card = &palm27x_asoc,
  167. .codec_dev = &soc_codec_dev_wm9712,
  168. };
  169. static struct platform_device *palm27x_snd_device;
  170. static int __init palm27x_asoc_init(void)
  171. {
  172. int ret;
  173. if (!(machine_is_palmtx() || machine_is_palmt5() ||
  174. machine_is_palmld()))
  175. return -ENODEV;
  176. ret = gpio_request(palm27x_ep_gpio, "Headphone Jack");
  177. if (ret)
  178. return ret;
  179. ret = gpio_direction_input(palm27x_ep_gpio);
  180. if (ret)
  181. goto err_alloc;
  182. if (request_irq(gpio_to_irq(palm27x_ep_gpio), palm27x_interrupt,
  183. IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
  184. "Headphone jack", NULL))
  185. goto err_alloc;
  186. palm27x_snd_device = platform_device_alloc("soc-audio", -1);
  187. if (!palm27x_snd_device) {
  188. ret = -ENOMEM;
  189. goto err_dev;
  190. }
  191. platform_set_drvdata(palm27x_snd_device, &palm27x_snd_devdata);
  192. palm27x_snd_devdata.dev = &palm27x_snd_device->dev;
  193. ret = platform_device_add(palm27x_snd_device);
  194. if (ret != 0)
  195. goto put_device;
  196. return 0;
  197. put_device:
  198. platform_device_put(palm27x_snd_device);
  199. err_dev:
  200. free_irq(gpio_to_irq(palm27x_ep_gpio), NULL);
  201. err_alloc:
  202. gpio_free(palm27x_ep_gpio);
  203. return ret;
  204. }
  205. static void __exit palm27x_asoc_exit(void)
  206. {
  207. free_irq(gpio_to_irq(palm27x_ep_gpio), NULL);
  208. gpio_free(palm27x_ep_gpio);
  209. platform_device_unregister(palm27x_snd_device);
  210. }
  211. void __init palm27x_asoc_set_pdata(struct palm27x_asoc_info *data)
  212. {
  213. palm27x_ep_gpio = data->jack_gpio;
  214. }
  215. module_init(palm27x_asoc_init);
  216. module_exit(palm27x_asoc_exit);
  217. /* Module information */
  218. MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
  219. MODULE_DESCRIPTION("ALSA SoC Palm T|X, T5 and LifeDrive");
  220. MODULE_LICENSE("GPL");