led-class-multicolor.c 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. // SPDX-License-Identifier: GPL-2.0
  2. // LED Multicolor class interface
  3. // Copyright (C) 2019-20 Texas Instruments Incorporated - http://www.ti.com/
  4. // Author: Dan Murphy <dmurphy@ti.com>
  5. #include <linux/device.h>
  6. #include <linux/init.h>
  7. #include <linux/led-class-multicolor.h>
  8. #include <linux/math.h>
  9. #include <linux/module.h>
  10. #include <linux/slab.h>
  11. #include <linux/uaccess.h>
  12. #include "leds.h"
  13. int led_mc_calc_color_components(struct led_classdev_mc *mcled_cdev,
  14. enum led_brightness brightness)
  15. {
  16. struct led_classdev *led_cdev = &mcled_cdev->led_cdev;
  17. int i;
  18. for (i = 0; i < mcled_cdev->num_colors; i++)
  19. mcled_cdev->subled_info[i].brightness =
  20. DIV_ROUND_CLOSEST(brightness *
  21. mcled_cdev->subled_info[i].intensity,
  22. led_cdev->max_brightness);
  23. return 0;
  24. }
  25. EXPORT_SYMBOL_GPL(led_mc_calc_color_components);
  26. static ssize_t multi_intensity_store(struct device *dev,
  27. struct device_attribute *intensity_attr,
  28. const char *buf, size_t size)
  29. {
  30. struct led_classdev *led_cdev = dev_get_drvdata(dev);
  31. struct led_classdev_mc *mcled_cdev = lcdev_to_mccdev(led_cdev);
  32. int nrchars, offset = 0;
  33. int intensity_value[LED_COLOR_ID_MAX];
  34. int i;
  35. ssize_t ret;
  36. mutex_lock(&led_cdev->led_access);
  37. for (i = 0; i < mcled_cdev->num_colors; i++) {
  38. ret = sscanf(buf + offset, "%i%n",
  39. &intensity_value[i], &nrchars);
  40. if (ret != 1) {
  41. ret = -EINVAL;
  42. goto err_out;
  43. }
  44. offset += nrchars;
  45. }
  46. offset++;
  47. if (offset < size) {
  48. ret = -EINVAL;
  49. goto err_out;
  50. }
  51. for (i = 0; i < mcled_cdev->num_colors; i++)
  52. mcled_cdev->subled_info[i].intensity = intensity_value[i];
  53. if (!test_bit(LED_BLINK_SW, &led_cdev->work_flags))
  54. led_set_brightness(led_cdev, led_cdev->brightness);
  55. ret = size;
  56. err_out:
  57. mutex_unlock(&led_cdev->led_access);
  58. return ret;
  59. }
  60. static ssize_t multi_intensity_show(struct device *dev,
  61. struct device_attribute *intensity_attr,
  62. char *buf)
  63. {
  64. struct led_classdev *led_cdev = dev_get_drvdata(dev);
  65. struct led_classdev_mc *mcled_cdev = lcdev_to_mccdev(led_cdev);
  66. int len = 0;
  67. int i;
  68. for (i = 0; i < mcled_cdev->num_colors; i++) {
  69. len += sprintf(buf + len, "%d",
  70. mcled_cdev->subled_info[i].intensity);
  71. if (i < mcled_cdev->num_colors - 1)
  72. len += sprintf(buf + len, " ");
  73. }
  74. buf[len++] = '\n';
  75. return len;
  76. }
  77. static DEVICE_ATTR_RW(multi_intensity);
  78. static ssize_t multi_index_show(struct device *dev,
  79. struct device_attribute *multi_index_attr,
  80. char *buf)
  81. {
  82. struct led_classdev *led_cdev = dev_get_drvdata(dev);
  83. struct led_classdev_mc *mcled_cdev = lcdev_to_mccdev(led_cdev);
  84. int len = 0;
  85. int index;
  86. int i;
  87. for (i = 0; i < mcled_cdev->num_colors; i++) {
  88. index = mcled_cdev->subled_info[i].color_index;
  89. len += sprintf(buf + len, "%s", led_get_color_name(index));
  90. if (i < mcled_cdev->num_colors - 1)
  91. len += sprintf(buf + len, " ");
  92. }
  93. buf[len++] = '\n';
  94. return len;
  95. }
  96. static DEVICE_ATTR_RO(multi_index);
  97. static struct attribute *led_multicolor_attrs[] = {
  98. &dev_attr_multi_intensity.attr,
  99. &dev_attr_multi_index.attr,
  100. NULL,
  101. };
  102. ATTRIBUTE_GROUPS(led_multicolor);
  103. int led_classdev_multicolor_register_ext(struct device *parent,
  104. struct led_classdev_mc *mcled_cdev,
  105. struct led_init_data *init_data)
  106. {
  107. struct led_classdev *led_cdev;
  108. if (!mcled_cdev)
  109. return -EINVAL;
  110. if (mcled_cdev->num_colors <= 0)
  111. return -EINVAL;
  112. if (mcled_cdev->num_colors > LED_COLOR_ID_MAX)
  113. return -EINVAL;
  114. led_cdev = &mcled_cdev->led_cdev;
  115. led_cdev->flags |= LED_MULTI_COLOR;
  116. mcled_cdev->led_cdev.groups = led_multicolor_groups;
  117. return led_classdev_register_ext(parent, led_cdev, init_data);
  118. }
  119. EXPORT_SYMBOL_GPL(led_classdev_multicolor_register_ext);
  120. void led_classdev_multicolor_unregister(struct led_classdev_mc *mcled_cdev)
  121. {
  122. if (!mcled_cdev)
  123. return;
  124. led_classdev_unregister(&mcled_cdev->led_cdev);
  125. }
  126. EXPORT_SYMBOL_GPL(led_classdev_multicolor_unregister);
  127. static void devm_led_classdev_multicolor_release(struct device *dev, void *res)
  128. {
  129. led_classdev_multicolor_unregister(*(struct led_classdev_mc **)res);
  130. }
  131. int devm_led_classdev_multicolor_register_ext(struct device *parent,
  132. struct led_classdev_mc *mcled_cdev,
  133. struct led_init_data *init_data)
  134. {
  135. struct led_classdev_mc **dr;
  136. int ret;
  137. dr = devres_alloc(devm_led_classdev_multicolor_release,
  138. sizeof(*dr), GFP_KERNEL);
  139. if (!dr)
  140. return -ENOMEM;
  141. ret = led_classdev_multicolor_register_ext(parent, mcled_cdev,
  142. init_data);
  143. if (ret) {
  144. devres_free(dr);
  145. return ret;
  146. }
  147. *dr = mcled_cdev;
  148. devres_add(parent, dr);
  149. return 0;
  150. }
  151. EXPORT_SYMBOL_GPL(devm_led_classdev_multicolor_register_ext);
  152. static int devm_led_classdev_multicolor_match(struct device *dev,
  153. void *res, void *data)
  154. {
  155. struct led_classdev_mc **p = res;
  156. if (WARN_ON(!p || !*p))
  157. return 0;
  158. return *p == data;
  159. }
  160. void devm_led_classdev_multicolor_unregister(struct device *dev,
  161. struct led_classdev_mc *mcled_cdev)
  162. {
  163. WARN_ON(devres_release(dev,
  164. devm_led_classdev_multicolor_release,
  165. devm_led_classdev_multicolor_match, mcled_cdev));
  166. }
  167. EXPORT_SYMBOL_GPL(devm_led_classdev_multicolor_unregister);
  168. MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
  169. MODULE_DESCRIPTION("Multicolor LED class interface");
  170. MODULE_LICENSE("GPL v2");