leds-pm8058.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /* Copyright (c) 2010, 2011, 2016 The Linux Foundation. All rights reserved.
  3. */
  4. #include <linux/leds.h>
  5. #include <linux/module.h>
  6. #include <linux/of.h>
  7. #include <linux/platform_device.h>
  8. #include <linux/pm.h>
  9. #include <linux/regmap.h>
  10. #define PM8058_LED_TYPE_COMMON 0x00
  11. #define PM8058_LED_TYPE_KEYPAD 0x01
  12. #define PM8058_LED_TYPE_FLASH 0x02
  13. #define PM8058_LED_TYPE_COMMON_MASK 0xf8
  14. #define PM8058_LED_TYPE_KEYPAD_MASK 0xf0
  15. #define PM8058_LED_TYPE_COMMON_SHIFT 3
  16. #define PM8058_LED_TYPE_KEYPAD_SHIFT 4
  17. struct pm8058_led {
  18. struct regmap *map;
  19. u32 reg;
  20. u32 ledtype;
  21. struct led_classdev cdev;
  22. };
  23. static void pm8058_led_set(struct led_classdev *cled,
  24. enum led_brightness value)
  25. {
  26. struct pm8058_led *led;
  27. int ret = 0;
  28. unsigned int mask = 0;
  29. unsigned int val = 0;
  30. led = container_of(cled, struct pm8058_led, cdev);
  31. switch (led->ledtype) {
  32. case PM8058_LED_TYPE_COMMON:
  33. mask = PM8058_LED_TYPE_COMMON_MASK;
  34. val = value << PM8058_LED_TYPE_COMMON_SHIFT;
  35. break;
  36. case PM8058_LED_TYPE_KEYPAD:
  37. case PM8058_LED_TYPE_FLASH:
  38. mask = PM8058_LED_TYPE_KEYPAD_MASK;
  39. val = value << PM8058_LED_TYPE_KEYPAD_SHIFT;
  40. break;
  41. default:
  42. break;
  43. }
  44. ret = regmap_update_bits(led->map, led->reg, mask, val);
  45. if (ret)
  46. pr_err("Failed to set LED brightness\n");
  47. }
  48. static enum led_brightness pm8058_led_get(struct led_classdev *cled)
  49. {
  50. struct pm8058_led *led;
  51. int ret;
  52. unsigned int val;
  53. led = container_of(cled, struct pm8058_led, cdev);
  54. ret = regmap_read(led->map, led->reg, &val);
  55. if (ret) {
  56. pr_err("Failed to get LED brightness\n");
  57. return LED_OFF;
  58. }
  59. switch (led->ledtype) {
  60. case PM8058_LED_TYPE_COMMON:
  61. val &= PM8058_LED_TYPE_COMMON_MASK;
  62. val >>= PM8058_LED_TYPE_COMMON_SHIFT;
  63. break;
  64. case PM8058_LED_TYPE_KEYPAD:
  65. case PM8058_LED_TYPE_FLASH:
  66. val &= PM8058_LED_TYPE_KEYPAD_MASK;
  67. val >>= PM8058_LED_TYPE_KEYPAD_SHIFT;
  68. break;
  69. default:
  70. val = LED_OFF;
  71. break;
  72. }
  73. return val;
  74. }
  75. static int pm8058_led_probe(struct platform_device *pdev)
  76. {
  77. struct led_init_data init_data = {};
  78. struct device *dev = &pdev->dev;
  79. struct pm8058_led *led;
  80. struct device_node *np;
  81. int ret;
  82. struct regmap *map;
  83. enum led_brightness maxbright;
  84. enum led_default_state state;
  85. led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
  86. if (!led)
  87. return -ENOMEM;
  88. led->ledtype = (u32)(unsigned long)of_device_get_match_data(dev);
  89. map = dev_get_regmap(dev->parent, NULL);
  90. if (!map) {
  91. dev_err(dev, "Parent regmap unavailable.\n");
  92. return -ENXIO;
  93. }
  94. led->map = map;
  95. np = dev_of_node(dev);
  96. ret = of_property_read_u32(np, "reg", &led->reg);
  97. if (ret) {
  98. dev_err(dev, "no register offset specified\n");
  99. return -EINVAL;
  100. }
  101. led->cdev.brightness_set = pm8058_led_set;
  102. led->cdev.brightness_get = pm8058_led_get;
  103. if (led->ledtype == PM8058_LED_TYPE_COMMON)
  104. maxbright = 31; /* 5 bits */
  105. else
  106. maxbright = 15; /* 4 bits */
  107. led->cdev.max_brightness = maxbright;
  108. init_data.fwnode = of_fwnode_handle(np);
  109. state = led_init_default_state_get(init_data.fwnode);
  110. switch (state) {
  111. case LEDS_DEFSTATE_ON:
  112. led->cdev.brightness = maxbright;
  113. pm8058_led_set(&led->cdev, maxbright);
  114. break;
  115. case LEDS_DEFSTATE_KEEP:
  116. led->cdev.brightness = pm8058_led_get(&led->cdev);
  117. break;
  118. default:
  119. led->cdev.brightness = LED_OFF;
  120. pm8058_led_set(&led->cdev, LED_OFF);
  121. }
  122. if (led->ledtype == PM8058_LED_TYPE_KEYPAD ||
  123. led->ledtype == PM8058_LED_TYPE_FLASH)
  124. led->cdev.flags = LED_CORE_SUSPENDRESUME;
  125. ret = devm_led_classdev_register_ext(dev, &led->cdev, &init_data);
  126. if (ret)
  127. dev_err(dev, "Failed to register LED for %pOF\n", np);
  128. return ret;
  129. }
  130. static const struct of_device_id pm8058_leds_id_table[] = {
  131. {
  132. .compatible = "qcom,pm8058-led",
  133. .data = (void *)PM8058_LED_TYPE_COMMON
  134. },
  135. {
  136. .compatible = "qcom,pm8058-keypad-led",
  137. .data = (void *)PM8058_LED_TYPE_KEYPAD
  138. },
  139. {
  140. .compatible = "qcom,pm8058-flash-led",
  141. .data = (void *)PM8058_LED_TYPE_FLASH
  142. },
  143. { },
  144. };
  145. MODULE_DEVICE_TABLE(of, pm8058_leds_id_table);
  146. static struct platform_driver pm8058_led_driver = {
  147. .probe = pm8058_led_probe,
  148. .driver = {
  149. .name = "pm8058-leds",
  150. .of_match_table = pm8058_leds_id_table,
  151. },
  152. };
  153. module_platform_driver(pm8058_led_driver);
  154. MODULE_DESCRIPTION("PM8058 LEDs driver");
  155. MODULE_LICENSE("GPL v2");
  156. MODULE_ALIAS("platform:pm8058-leds");