leds-msm-pdm.c 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. /* Copyright (c) 2011, The Linux Foundation. All rights reserved.
  2. *
  3. * This program is free software; you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License version 2 and
  5. * only version 2 as published by the Free Software Foundation.
  6. *
  7. * This program is distributed in the hope that it will be useful,
  8. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. * GNU General Public License for more details.
  11. */
  12. #define pr_fmt(fmt) "%s: " fmt, __func__
  13. #include <linux/kernel.h>
  14. #include <linux/init.h>
  15. #include <linux/platform_device.h>
  16. #include <linux/leds.h>
  17. #include <linux/io.h>
  18. #include <linux/slab.h>
  19. #include <linux/err.h>
  20. #include <linux/delay.h>
  21. #include <linux/pm.h>
  22. #include <linux/pm_runtime.h>
  23. #include <linux/module.h>
  24. #ifdef CONFIG_HAS_EARLYSUSPEND
  25. #include <linux/earlysuspend.h>
  26. /* Early-suspend level */
  27. #define LED_SUSPEND_LEVEL 1
  28. #endif
  29. #define PDM_DUTY_MAXVAL BIT(16)
  30. #define PDM_DUTY_REFVAL BIT(15)
  31. struct pdm_led_data {
  32. struct led_classdev cdev;
  33. void __iomem *perph_base;
  34. int pdm_offset;
  35. #ifdef CONFIG_HAS_EARLYSUSPEND
  36. struct early_suspend early_suspend;
  37. #endif
  38. };
  39. static void msm_led_brightness_set_percent(struct pdm_led_data *led,
  40. int duty_per)
  41. {
  42. u16 duty_val;
  43. duty_val = PDM_DUTY_REFVAL - ((PDM_DUTY_MAXVAL * duty_per) / 100);
  44. if (!duty_per)
  45. duty_val--;
  46. writel_relaxed(duty_val, led->perph_base + led->pdm_offset);
  47. }
  48. static void msm_led_brightness_set(struct led_classdev *led_cdev,
  49. enum led_brightness value)
  50. {
  51. struct pdm_led_data *led =
  52. container_of(led_cdev, struct pdm_led_data, cdev);
  53. msm_led_brightness_set_percent(led, (value * 100) / LED_FULL);
  54. }
  55. #ifdef CONFIG_PM_SLEEP
  56. static int msm_led_pdm_suspend(struct device *dev)
  57. {
  58. struct pdm_led_data *led = dev_get_drvdata(dev);
  59. msm_led_brightness_set_percent(led, 0);
  60. return 0;
  61. }
  62. #ifdef CONFIG_HAS_EARLYSUSPEND
  63. static void msm_led_pdm_early_suspend(struct early_suspend *h)
  64. {
  65. struct pdm_led_data *led = container_of(h,
  66. struct pdm_led_data, early_suspend);
  67. msm_led_pdm_suspend(led->cdev.dev->parent);
  68. }
  69. #endif
  70. static const struct dev_pm_ops msm_led_pdm_pm_ops = {
  71. #ifndef CONFIG_HAS_EARLYSUSPEND
  72. .suspend = msm_led_pdm_suspend,
  73. #endif
  74. };
  75. #endif
  76. static int __devinit msm_pdm_led_probe(struct platform_device *pdev)
  77. {
  78. const struct led_info *pdata = pdev->dev.platform_data;
  79. struct pdm_led_data *led;
  80. struct resource *res, *ioregion;
  81. u32 tcxo_pdm_ctl;
  82. int rc;
  83. if (!pdata) {
  84. pr_err("platform data is invalid\n");
  85. return -EINVAL;
  86. }
  87. if (pdev->id > 2) {
  88. pr_err("pdm id is invalid\n");
  89. return -EINVAL;
  90. }
  91. led = kzalloc(sizeof(struct pdm_led_data), GFP_KERNEL);
  92. if (!led)
  93. return -ENOMEM;
  94. /* Enable runtime PM ops, start in ACTIVE mode */
  95. rc = pm_runtime_set_active(&pdev->dev);
  96. if (rc < 0)
  97. dev_dbg(&pdev->dev, "unable to set runtime pm state\n");
  98. pm_runtime_enable(&pdev->dev);
  99. res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  100. if (!res) {
  101. pr_err("get resource failed\n");
  102. rc = -EINVAL;
  103. goto err_get_res;
  104. }
  105. ioregion = request_mem_region(res->start, resource_size(res),
  106. pdev->name);
  107. if (!ioregion) {
  108. pr_err("request for mem region failed\n");
  109. rc = -ENOMEM;
  110. goto err_get_res;
  111. }
  112. led->perph_base = ioremap(res->start, resource_size(res));
  113. if (!led->perph_base) {
  114. pr_err("ioremap failed\n");
  115. rc = -ENOMEM;
  116. goto err_ioremap;
  117. }
  118. /* Pulse Density Modulation(PDM) ids start with 0 and
  119. * every PDM register takes 4 bytes
  120. */
  121. led->pdm_offset = ((pdev->id) + 1) * 4;
  122. /* program tcxo_pdm_ctl register to enable pdm*/
  123. tcxo_pdm_ctl = readl_relaxed(led->perph_base);
  124. tcxo_pdm_ctl |= (1 << pdev->id);
  125. writel_relaxed(tcxo_pdm_ctl, led->perph_base);
  126. /* Start with LED in off state */
  127. msm_led_brightness_set_percent(led, 0);
  128. led->cdev.brightness_set = msm_led_brightness_set;
  129. led->cdev.name = pdata->name ? : "leds-msm-pdm";
  130. rc = led_classdev_register(&pdev->dev, &led->cdev);
  131. if (rc) {
  132. pr_err("led class registration failed\n");
  133. goto err_led_reg;
  134. }
  135. #ifdef CONFIG_HAS_EARLYSUSPEND
  136. led->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN +
  137. LED_SUSPEND_LEVEL;
  138. led->early_suspend.suspend = msm_led_pdm_early_suspend;
  139. register_early_suspend(&led->early_suspend);
  140. #endif
  141. platform_set_drvdata(pdev, led);
  142. return 0;
  143. err_led_reg:
  144. iounmap(led->perph_base);
  145. err_ioremap:
  146. release_mem_region(res->start, resource_size(res));
  147. err_get_res:
  148. pm_runtime_set_suspended(&pdev->dev);
  149. pm_runtime_disable(&pdev->dev);
  150. kfree(led);
  151. return rc;
  152. }
  153. static int __devexit msm_pdm_led_remove(struct platform_device *pdev)
  154. {
  155. struct pdm_led_data *led = platform_get_drvdata(pdev);
  156. struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  157. #ifdef CONFIG_HAS_EARLYSUSPEND
  158. unregister_early_suspend(&led->early_suspend);
  159. #endif
  160. pm_runtime_set_suspended(&pdev->dev);
  161. pm_runtime_disable(&pdev->dev);
  162. led_classdev_unregister(&led->cdev);
  163. msm_led_brightness_set_percent(led, 0);
  164. iounmap(led->perph_base);
  165. release_mem_region(res->start, resource_size(res));
  166. kfree(led);
  167. return 0;
  168. }
  169. static struct platform_driver msm_pdm_led_driver = {
  170. .probe = msm_pdm_led_probe,
  171. .remove = __devexit_p(msm_pdm_led_remove),
  172. .driver = {
  173. .name = "leds-msm-pdm",
  174. .owner = THIS_MODULE,
  175. #ifdef CONFIG_PM_SLEEP
  176. .pm = &msm_led_pdm_pm_ops,
  177. #endif
  178. },
  179. };
  180. static int __init msm_pdm_led_init(void)
  181. {
  182. return platform_driver_register(&msm_pdm_led_driver);
  183. }
  184. module_init(msm_pdm_led_init);
  185. static void __exit msm_pdm_led_exit(void)
  186. {
  187. platform_driver_unregister(&msm_pdm_led_driver);
  188. }
  189. module_exit(msm_pdm_led_exit);
  190. MODULE_DESCRIPTION("MSM PDM LEDs driver");
  191. MODULE_LICENSE("GPL v2");
  192. MODULE_ALIAS("platform:leds-msm-pdm");