aml_m1_armdev_wm8900.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. #include <linux/module.h>
  2. #include <linux/moduleparam.h>
  3. #include <linux/kernel.h>
  4. #include <linux/clk.h>
  5. #include <linux/timer.h>
  6. #include <linux/interrupt.h>
  7. #include <linux/platform_device.h>
  8. #include <linux/i2c.h>
  9. #include <sound/core.h>
  10. #include <sound/pcm.h>
  11. #include <sound/pcm_params.h>
  12. #include <sound/soc.h>
  13. #include <sound/soc-dapm.h>
  14. #include <sound/jack.h>
  15. #include <asm/mach-types.h>
  16. #include <mach/hardware.h>
  17. #include <mach/gpio.h>
  18. #include "aml_dai.h"
  19. #include "aml_pcm.h"
  20. #include "../codecs/wm8900.h"
  21. #define HP_DET 0//1
  22. #if HP_DET
  23. static struct timer_list timer;
  24. #endif
  25. static int aml_m1_hw_params(struct snd_pcm_substream *substream,
  26. struct snd_pcm_hw_params *params)
  27. {
  28. struct snd_soc_pcm_runtime *rtd = substream->private_data;
  29. struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
  30. struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
  31. int ret;
  32. // TODO
  33. printk("***Entered %s:%s\n", __FILE__,__func__);
  34. ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S|SND_SOC_DAIFMT_NB_NF|SND_SOC_DAIFMT_CBS_CFS);
  35. if(ret<0)
  36. return ret;
  37. ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S|SND_SOC_DAIFMT_NB_NF|SND_SOC_DAIFMT_CBS_CFS);
  38. if(ret<0)
  39. return ret;
  40. return 0;
  41. }
  42. static struct snd_soc_ops aml_m1_ops = {
  43. .hw_params = aml_m1_hw_params,
  44. };
  45. static int aml_m1_set_bias_level(struct snd_soc_card *card,
  46. enum snd_soc_bias_level level)
  47. {
  48. int ret = 0;
  49. // TODO
  50. printk("***Entered %s:%s: %d\n", __FILE__,__func__, level);
  51. switch (level) {
  52. case SND_SOC_BIAS_ON:
  53. case SND_SOC_BIAS_PREPARE:
  54. #if HP_DET
  55. del_timer_sync(&timer);
  56. timer.expires = jiffies + HZ*1;
  57. del_timer(&timer);
  58. add_timer(&timer);
  59. #endif
  60. break;
  61. case SND_SOC_BIAS_OFF:
  62. case SND_SOC_BIAS_STANDBY:
  63. #if HP_DET
  64. del_timer(&timer);
  65. #endif
  66. break;
  67. };
  68. return ret;
  69. }
  70. static const struct snd_soc_dapm_widget aml_m1_dapm_widgets[] = {
  71. SND_SOC_DAPM_SPK("AVout Jack", NULL),
  72. SND_SOC_DAPM_HP("Headphone Jack", NULL),
  73. SND_SOC_DAPM_MIC("Headphone Mic", NULL),
  74. };
  75. static const struct snd_soc_dapm_route intercon[] = {
  76. /* speaker connected to LINEOUT */
  77. {"AVout Jack", NULL, "LINEOUT1L"},
  78. {"AVout Jack", NULL, "LINEOUT1R"},
  79. {"Headphone Jack", NULL, "HP_L"},
  80. {"Headphone Jack", NULL, "HP_R"},
  81. /* input */
  82. /*
  83. {"RINPUT2", NULL, "Mic Bias"},
  84. {"LINPUT2", NULL, "Headphone Mic"},
  85. {"Mic Bias", NULL, "Headphone Mic"},
  86. */
  87. };
  88. #if HP_DET
  89. /* Hook switch */
  90. static struct snd_soc_jack hp_jack;
  91. static struct snd_soc_jack_pin hp_jack_pins[] = {
  92. { .pin = "Headphone Jack", .mask = SND_JACK_HEADSET },
  93. };
  94. static struct snd_soc_jack av_jack;
  95. static struct snd_soc_jack_pin av_jack_pins[] = {
  96. { .pin = "AVout Jack", .mask = SND_JACK_AVOUT },
  97. };
  98. // use LED_CS1 as detect pin
  99. /*
  100. * 函数名:inner_cs_input_level
  101. * 用途:使用内置VGHL B/L的反馈CS0/1做输入.
  102. * 平台: AML8726-M_DEV_BOARD 2010-10-11_V1.0
  103. * 引脚:P4(VGHL_CS0/1) T2(LED_CS1) T1(LED_CS0)
  104. * 注意:输入脚的高电平需大于0.7V,低电平需小于0.4V
  105. * 入口参数
  106. * 返回参数:
  107. level[12] <--> VGHL_CS1上的逻辑电平
  108. level[ 8] <--> VGHL_CS0上的逻辑电平
  109. level[ 4] <--> LED_CS1上的逻辑电平
  110. level[ 0] <--> LED_CS0上的逻辑电平
  111. */
  112. #define PWM_TCNT (600-1)
  113. #define PWM_MAX_VAL (420)
  114. static unsigned int inner_cs_input_level()
  115. {
  116. unsigned int level = 0;
  117. unsigned int cs_no = 0;
  118. //pin64 LED_CS0
  119. CLEAR_CBUS_REG_MASK(PERIPHS_PIN_MUX_0, (3<<21));
  120. // Enable VBG_EN
  121. WRITE_CBUS_REG_BITS(PREG_AM_ANALOG_ADDR, 1, 0, 1);
  122. // pin mux
  123. // wire pm_gpioA_7_led_pwm = pin_mux_reg0[22];
  124. WRITE_CBUS_REG(LED_PWM_REG0, 0);
  125. WRITE_CBUS_REG(LED_PWM_REG1, 0);
  126. WRITE_CBUS_REG(LED_PWM_REG2, 0);
  127. WRITE_CBUS_REG(LED_PWM_REG3, 0);
  128. WRITE_CBUS_REG(LED_PWM_REG4, 0);
  129. WRITE_CBUS_REG(LED_PWM_REG0, (0 << 31) | // disable the overall circuit
  130. (0 << 30) | // 1:Closed Loop 0:Open Loop
  131. (PWM_TCNT << 16) | // PWM total count
  132. (0 << 13) | // Enable
  133. (1 << 12) | // enable
  134. (0 << 10) | // test
  135. (7 << 7) | // CS0 REF, Voltage FeedBack: about 0.505V
  136. (7 << 4) | // CS1 REF, Current FeedBack: about 0.505V
  137. (0 << 0) // DIMCTL Analog dimmer
  138. );
  139. WRITE_CBUS_REG(LED_PWM_REG1, (1 << 30) | // enable high frequency clock
  140. (PWM_MAX_VAL << 16) | // MAX PWM value
  141. (0 << 0) // MIN PWM value
  142. );
  143. WRITE_CBUS_REG(LED_PWM_REG2, (0 << 31) | // disable timeout test mode
  144. (0 << 30) | // timeout based on the comparator output
  145. (0 << 16) | // timeout = 10uS
  146. (0 << 13) | // Select oscillator as the clock (just for grins)
  147. (1 << 11) | // 1:Enable OverCurrent Portection 0:Disable
  148. (3 << 8) | // Filter: shift every 3 ticks
  149. (0 << 6) | // Filter: count 1uS ticks
  150. (0 << 5) | // PWM polarity : negative
  151. (0 << 4) | // comparator: negative, Different with NikeD3
  152. (1 << 0) // +/- 1
  153. );
  154. WRITE_CBUS_REG(LED_PWM_REG3, ( 1 << 16) | // Feedback down-sampling = PWM_freq/1 = PWM_freq
  155. ( 1 << 14) | // enable to re-write MATCH_VAL
  156. ( 210 << 0) // preset PWM_duty = 50%
  157. );
  158. WRITE_CBUS_REG(LED_PWM_REG4, ( 0 << 30) | // 1:Digital Dimmer 0:Analog Dimmer
  159. ( 2 << 28) | // dimmer_timebase = 1uS
  160. (1000 << 14) | // Digital dimmer_duty = 0%, the most darkness
  161. (1000 << 0) // dimmer_freq = 1KHz
  162. );
  163. cs_no = READ_CBUS_REG(LED_PWM_REG3);
  164. if(cs_no &(1<<14))
  165. level |= (1<<0);
  166. if(cs_no &(1<<15))
  167. level |= (1<<4);
  168. WRITE_CBUS_REG(VGHL_PWM_REG0, 0);
  169. WRITE_CBUS_REG(VGHL_PWM_REG1, 0);
  170. WRITE_CBUS_REG(VGHL_PWM_REG2, 0);
  171. WRITE_CBUS_REG(VGHL_PWM_REG3, 0);
  172. WRITE_CBUS_REG(VGHL_PWM_REG4, 0);
  173. WRITE_CBUS_REG(VGHL_PWM_REG0, (0 << 31) | // disable the overall circuit
  174. (0 << 30) | // 1:Closed Loop 0:Open Loop
  175. (PWM_TCNT << 16) | // PWM total count
  176. (0 << 13) | // Enable
  177. (1 << 12) | // enable
  178. (0 << 10) | // test
  179. (7 << 7) | // CS0 REF, Voltage FeedBack: about 0.505V
  180. (7 << 4) | // CS1 REF, Current FeedBack: about 0.505V
  181. (0 << 0) // DIMCTL Analog dimmer
  182. );
  183. WRITE_CBUS_REG(VGHL_PWM_REG1, (1 << 30) | // enable high frequency clock
  184. (PWM_MAX_VAL << 16) | // MAX PWM value
  185. (0 << 0) // MIN PWM value
  186. );
  187. WRITE_CBUS_REG(VGHL_PWM_REG2, (0 << 31) | // disable timeout test mode
  188. (0 << 30) | // timeout based on the comparator output
  189. (0 << 16) | // timeout = 10uS
  190. (0 << 13) | // Select oscillator as the clock (just for grins)
  191. (1 << 11) | // 1:Enable OverCurrent Portection 0:Disable
  192. (3 << 8) | // Filter: shift every 3 ticks
  193. (0 << 6) | // Filter: count 1uS ticks
  194. (0 << 5) | // PWM polarity : negative
  195. (0 << 4) | // comparator: negative, Different with NikeD3
  196. (1 << 0) // +/- 1
  197. );
  198. WRITE_CBUS_REG (VGHL_PWM_REG3, ( 1 << 16) | // Feedback down-sampling = PWM_freq/1 = PWM_freq
  199. ( 1 << 14) | // enable to re-write MATCH_VAL
  200. ( 210 << 0) // preset PWM_duty = 50%
  201. );
  202. WRITE_CBUS_REG(VGHL_PWM_REG4, ( 0 << 30) | // 1:Digital Dimmer 0:Analog Dimmer
  203. ( 2 << 28) | // dimmer_timebase = 1uS
  204. (1000 << 14) | // Digital dimmer_duty = 0%, the most darkness
  205. (1000 << 0) // dimmer_freq = 1KHz
  206. );
  207. cs_no = READ_CBUS_REG(VGHL_PWM_REG3);
  208. if(cs_no &(1<<14))
  209. level |= (1<<8);
  210. if(cs_no &(1<<15))
  211. level |= (1<<12);
  212. return level;
  213. }
  214. static int hp_detect_flag = 0;
  215. static spinlock_t lock;
  216. static void wm8900_hp_detect_queue(struct work_struct*);
  217. static struct wm8900_work_t{
  218. unsigned long data;
  219. struct work_struct wm8900_workqueue;
  220. }wm8900_work;
  221. static void wm8900_hp_detect_queue(struct work_struct* work)
  222. {
  223. int level = inner_cs_input_level();
  224. struct wm8900_work_t* pwork = container_of(work,struct wm8900_work_t, wm8900_workqueue);
  225. struct snd_soc_codec* codec = (struct snd_soc_codec*)(pwork->data);
  226. if(level == 0x1 && hp_detect_flag!= 0x1){ // HP
  227. snd_soc_dapm_disable_pin(codec, "AVout Jack");
  228. snd_soc_dapm_sync(codec);
  229. snd_soc_jack_report(&hp_jack, SND_JACK_HEADSET, SND_JACK_HEADSET);
  230. hp_detect_flag = 0x1;
  231. }else if(level == 0x10 && hp_detect_flag != 0x10){ // AV
  232. snd_soc_jack_report(&av_jack, SND_JACK_AVOUT, SND_JACK_AVOUT);
  233. hp_detect_flag = 0x10;
  234. }else if(level != hp_detect_flag){ // HDMI
  235. snd_soc_dapm_disable_pin(codec, "AVout Jack");
  236. snd_soc_dapm_disable_pin(codec, "Headphone Jack");
  237. snd_soc_dapm_sync(codec);
  238. hp_detect_flag = level;
  239. }
  240. }
  241. static void wm8900_hp_detect_timer(unsigned long data)
  242. {
  243. struct snd_soc_codec *codec = (struct snd_soc_codec*) data;
  244. wm8900_work.data = (unsigned long)codec;
  245. schedule_work(&wm8900_work.wm8900_workqueue);
  246. mod_timer(&timer, jiffies + HZ*1);
  247. }
  248. #endif
  249. static int aml_m1_codec_init(struct snd_soc_codec *codec)
  250. {
  251. struct snd_soc_dai *codec_dai = codec->dai;
  252. struct snd_soc_card *card = codec->socdev->card;
  253. int err;
  254. //Add board specific DAPM widgets and routes
  255. err = snd_soc_dapm_new_controls(codec, aml_m1_dapm_widgets, ARRAY_SIZE(aml_m1_dapm_widgets));
  256. if(err){
  257. dev_warn(card->dev, "Failed to register DAPM widgets\n");
  258. return 0;
  259. }
  260. err = snd_soc_dapm_add_routes(codec, intercon,
  261. ARRAY_SIZE(intercon));
  262. if(err){
  263. dev_warn(card->dev, "Failed to setup dapm widgets routine\n");
  264. return 0;
  265. }
  266. //hook switch
  267. #if HP_DET
  268. hp_detect_flag = 0;
  269. err = snd_soc_jack_new(card, "hp_switch",
  270. SND_JACK_HEADSET, &hp_jack);
  271. if(err){
  272. dev_warn(card->dev, "Failed to alloc resource for hook switch\n");
  273. }else{
  274. err = snd_soc_jack_add_pins(&hp_jack,
  275. ARRAY_SIZE(hp_jack_pins),
  276. hp_jack_pins);
  277. if(err){
  278. dev_warn(card->dev, "Failed to setup hook hp jack pin\n");
  279. }
  280. }
  281. err = snd_soc_jack_new(card, "av_switch",
  282. SND_JACK_HEADSET, &av_jack);
  283. if(err){
  284. dev_warn(card->dev, "Failed to alloc resource for av hook switch\n");
  285. }else{
  286. err = snd_soc_jack_add_pins(&av_jack,
  287. ARRAY_SIZE(av_jack_pins),
  288. av_jack_pins);
  289. if(err){
  290. dev_warn(card->dev, "Failed to setup hook av jack pin\n");
  291. }
  292. }
  293. // create a timer to poll the HP IN status
  294. timer.function = &wm8900_hp_detect_timer;
  295. timer.data = (unsigned long)codec;
  296. timer.expires = jiffies + HZ*1;
  297. init_timer(&timer);
  298. #endif
  299. snd_soc_dapm_nc_pin(codec,"LINPUT1");
  300. snd_soc_dapm_nc_pin(codec,"RINPUT1");
  301. snd_soc_dapm_enable_pin(codec, "AVout Jack");
  302. snd_soc_dapm_disable_pin(codec, "Headphone Jack");
  303. snd_soc_dapm_disable_pin(codec, "Headphone Mic");
  304. snd_soc_dapm_sync(codec);
  305. return 0;
  306. }
  307. static struct snd_soc_dai_link aml_m1_dai = {
  308. .name = "AML-M1",
  309. .stream_name = "AML M1 PCM",
  310. .cpu_dai = &aml_dai[0], //////
  311. .codec_dai = &wm8900_dai,
  312. .init = aml_m1_codec_init,
  313. .ops = &aml_m1_ops,
  314. };
  315. static struct snd_soc_card snd_soc_aml_m1 = {
  316. .name = "AML-M1",
  317. .platform = &aml_soc_platform,
  318. .dai_link = &aml_m1_dai,
  319. .num_links = 1,
  320. .set_bias_level = aml_m1_set_bias_level,
  321. };
  322. static struct snd_soc_device aml_m1_snd_devdata = {
  323. .card = &snd_soc_aml_m1,
  324. .codec_dev = &soc_codec_dev_wm8900,
  325. };
  326. static struct platform_device *aml_m1_snd_device;
  327. static struct platform_device *aml_m1_platform_device;
  328. static int aml_m1_audio_probe(struct platform_device *pdev)
  329. {
  330. int ret;
  331. //pdev->dev.platform_data;
  332. // TODO
  333. printk("***Entered %s:%s\n", __FILE__,__func__);
  334. aml_m1_snd_device = platform_device_alloc("soc-audio", -1);
  335. if (!aml_m1_snd_device) {
  336. printk(KERN_ERR "ASoC: Platform device allocation failed\n");
  337. ret = -ENOMEM;
  338. }
  339. platform_set_drvdata(aml_m1_snd_device,&aml_m1_snd_devdata);
  340. aml_m1_snd_devdata.dev = &aml_m1_snd_device->dev;
  341. ret = platform_device_add(aml_m1_snd_device);
  342. if (ret) {
  343. printk(KERN_ERR "ASoC: Platform device allocation failed\n");
  344. goto error;
  345. }
  346. aml_m1_platform_device = platform_device_register_simple("aml_m1_codec",
  347. -1, NULL, 0);
  348. return 0;
  349. error:
  350. platform_device_put(aml_m1_snd_device);
  351. return ret;
  352. }
  353. static int aml_m1_audio_remove(struct platform_device *pdev)
  354. {
  355. printk("***Entered %s:%s\n", __FILE__,__func__);
  356. #if HP_DET
  357. del_timer_sync(&timer);
  358. #endif
  359. platform_device_unregister(aml_m1_snd_device);
  360. return 0;
  361. }
  362. static struct platform_driver aml_m1_audio_driver = {
  363. .probe = aml_m1_audio_probe,
  364. .remove = aml_m1_audio_remove,
  365. .driver = {
  366. .name = "aml_m1_audio_wm8900",
  367. .owner = THIS_MODULE,
  368. },
  369. };
  370. static int __init aml_m1_init(void)
  371. {
  372. return platform_driver_register(&aml_m1_audio_driver);
  373. }
  374. static void __exit aml_m1_exit(void)
  375. {
  376. platform_driver_unregister(&aml_m1_audio_driver);
  377. }
  378. module_init(aml_m1_init);
  379. module_exit(aml_m1_exit);
  380. /* Module information */
  381. MODULE_AUTHOR("AMLogic, Inc.");
  382. MODULE_DESCRIPTION("ALSA SoC AML M1 AUDIO");
  383. MODULE_LICENSE("GPL");