hx4700.c 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * SoC audio for HP iPAQ hx4700
  4. *
  5. * Copyright (c) 2009 Philipp Zabel
  6. */
  7. #include <linux/module.h>
  8. #include <linux/timer.h>
  9. #include <linux/interrupt.h>
  10. #include <linux/platform_device.h>
  11. #include <linux/delay.h>
  12. #include <linux/gpio/consumer.h>
  13. #include <sound/core.h>
  14. #include <sound/jack.h>
  15. #include <sound/pcm.h>
  16. #include <sound/pcm_params.h>
  17. #include <sound/soc.h>
  18. #include <asm/mach-types.h>
  19. #include "pxa2xx-i2s.h"
  20. static struct gpio_desc *gpiod_hp_driver, *gpiod_spk_sd;
  21. static struct snd_soc_jack hs_jack;
  22. /* Headphones jack detection DAPM pin */
  23. static struct snd_soc_jack_pin hs_jack_pin[] = {
  24. {
  25. .pin = "Headphone Jack",
  26. .mask = SND_JACK_HEADPHONE,
  27. .invert = 1,
  28. },
  29. {
  30. .pin = "Speaker",
  31. /* disable speaker when hp jack is inserted */
  32. .mask = SND_JACK_HEADPHONE,
  33. },
  34. };
  35. /* Headphones jack detection GPIO */
  36. static struct snd_soc_jack_gpio hs_jack_gpio = {
  37. .name = "earphone-det",
  38. .report = SND_JACK_HEADPHONE,
  39. .debounce_time = 200,
  40. };
  41. /*
  42. * iPAQ hx4700 uses I2S for capture and playback.
  43. */
  44. static int hx4700_hw_params(struct snd_pcm_substream *substream,
  45. struct snd_pcm_hw_params *params)
  46. {
  47. struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
  48. struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
  49. struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
  50. int ret = 0;
  51. /* set the I2S system clock as output */
  52. ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
  53. SND_SOC_CLOCK_OUT);
  54. if (ret < 0)
  55. return ret;
  56. /* inform codec driver about clock freq *
  57. * (PXA I2S always uses divider 256) */
  58. ret = snd_soc_dai_set_sysclk(codec_dai, 0, 256 * params_rate(params),
  59. SND_SOC_CLOCK_IN);
  60. if (ret < 0)
  61. return ret;
  62. return 0;
  63. }
  64. static const struct snd_soc_ops hx4700_ops = {
  65. .hw_params = hx4700_hw_params,
  66. };
  67. static int hx4700_spk_power(struct snd_soc_dapm_widget *w,
  68. struct snd_kcontrol *k, int event)
  69. {
  70. gpiod_set_value(gpiod_spk_sd, !SND_SOC_DAPM_EVENT_ON(event));
  71. return 0;
  72. }
  73. static int hx4700_hp_power(struct snd_soc_dapm_widget *w,
  74. struct snd_kcontrol *k, int event)
  75. {
  76. gpiod_set_value(gpiod_hp_driver, !!SND_SOC_DAPM_EVENT_ON(event));
  77. return 0;
  78. }
  79. /* hx4700 machine dapm widgets */
  80. static const struct snd_soc_dapm_widget hx4700_dapm_widgets[] = {
  81. SND_SOC_DAPM_HP("Headphone Jack", hx4700_hp_power),
  82. SND_SOC_DAPM_SPK("Speaker", hx4700_spk_power),
  83. SND_SOC_DAPM_MIC("Built-in Microphone", NULL),
  84. };
  85. /* hx4700 machine audio_map */
  86. static const struct snd_soc_dapm_route hx4700_audio_map[] = {
  87. /* Headphone connected to LOUT, ROUT */
  88. {"Headphone Jack", NULL, "LOUT"},
  89. {"Headphone Jack", NULL, "ROUT"},
  90. /* Speaker connected to MOUT2 */
  91. {"Speaker", NULL, "MOUT2"},
  92. /* Microphone connected to MICIN */
  93. {"MICIN", NULL, "Built-in Microphone"},
  94. {"AIN", NULL, "MICOUT"},
  95. };
  96. /*
  97. * Logic for a ak4641 as connected on a HP iPAQ hx4700
  98. */
  99. static int hx4700_ak4641_init(struct snd_soc_pcm_runtime *rtd)
  100. {
  101. int err;
  102. /* Jack detection API stuff */
  103. err = snd_soc_card_jack_new_pins(rtd->card, "Headphone Jack",
  104. SND_JACK_HEADPHONE, &hs_jack,
  105. hs_jack_pin, ARRAY_SIZE(hs_jack_pin));
  106. if (err)
  107. return err;
  108. err = snd_soc_jack_add_gpios(&hs_jack, 1, &hs_jack_gpio);
  109. return err;
  110. }
  111. /* hx4700 digital audio interface glue - connects codec <--> CPU */
  112. SND_SOC_DAILINK_DEFS(ak4641,
  113. DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-i2s")),
  114. DAILINK_COMP_ARRAY(COMP_CODEC("ak4641.0-0012", "ak4641-hifi")),
  115. DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
  116. static struct snd_soc_dai_link hx4700_dai = {
  117. .name = "ak4641",
  118. .stream_name = "AK4641",
  119. .init = hx4700_ak4641_init,
  120. .dai_fmt = SND_SOC_DAIFMT_MSB | SND_SOC_DAIFMT_NB_NF |
  121. SND_SOC_DAIFMT_CBS_CFS,
  122. .ops = &hx4700_ops,
  123. SND_SOC_DAILINK_REG(ak4641),
  124. };
  125. /* hx4700 audio machine driver */
  126. static struct snd_soc_card snd_soc_card_hx4700 = {
  127. .name = "iPAQ hx4700",
  128. .owner = THIS_MODULE,
  129. .dai_link = &hx4700_dai,
  130. .num_links = 1,
  131. .dapm_widgets = hx4700_dapm_widgets,
  132. .num_dapm_widgets = ARRAY_SIZE(hx4700_dapm_widgets),
  133. .dapm_routes = hx4700_audio_map,
  134. .num_dapm_routes = ARRAY_SIZE(hx4700_audio_map),
  135. .fully_routed = true,
  136. };
  137. static int hx4700_audio_probe(struct platform_device *pdev)
  138. {
  139. int ret;
  140. if (!machine_is_h4700())
  141. return -ENODEV;
  142. gpiod_hp_driver = devm_gpiod_get(&pdev->dev, "hp-driver", GPIOD_ASIS);
  143. ret = PTR_ERR_OR_ZERO(gpiod_hp_driver);
  144. if (ret)
  145. return ret;
  146. gpiod_spk_sd = devm_gpiod_get(&pdev->dev, "spk-sd", GPIOD_ASIS);
  147. ret = PTR_ERR_OR_ZERO(gpiod_spk_sd);
  148. if (ret)
  149. return ret;
  150. hs_jack_gpio.gpiod_dev = &pdev->dev;
  151. snd_soc_card_hx4700.dev = &pdev->dev;
  152. ret = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_hx4700);
  153. return ret;
  154. }
  155. static int hx4700_audio_remove(struct platform_device *pdev)
  156. {
  157. gpiod_set_value(gpiod_hp_driver, 0);
  158. gpiod_set_value(gpiod_spk_sd, 0);
  159. return 0;
  160. }
  161. static struct platform_driver hx4700_audio_driver = {
  162. .driver = {
  163. .name = "hx4700-audio",
  164. .pm = &snd_soc_pm_ops,
  165. },
  166. .probe = hx4700_audio_probe,
  167. .remove = hx4700_audio_remove,
  168. };
  169. module_platform_driver(hx4700_audio_driver);
  170. MODULE_AUTHOR("Philipp Zabel");
  171. MODULE_DESCRIPTION("ALSA SoC iPAQ hx4700");
  172. MODULE_LICENSE("GPL");
  173. MODULE_ALIAS("platform:hx4700-audio");