led-class-multicolor.c 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  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. led_set_brightness(led_cdev, led_cdev->brightness);
  54. ret = size;
  55. err_out:
  56. mutex_unlock(&led_cdev->led_access);
  57. return ret;
  58. }
  59. static ssize_t multi_intensity_show(struct device *dev,
  60. struct device_attribute *intensity_attr,
  61. char *buf)
  62. {
  63. struct led_classdev *led_cdev = dev_get_drvdata(dev);
  64. struct led_classdev_mc *mcled_cdev = lcdev_to_mccdev(led_cdev);
  65. int len = 0;
  66. int i;
  67. for (i = 0; i < mcled_cdev->num_colors; i++) {
  68. len += sprintf(buf + len, "%d",
  69. mcled_cdev->subled_info[i].intensity);
  70. if (i < mcled_cdev->num_colors - 1)
  71. len += sprintf(buf + len, " ");
  72. }
  73. buf[len++] = '\n';
  74. return len;
  75. }
  76. static DEVICE_ATTR_RW(multi_intensity);
  77. static ssize_t multi_index_show(struct device *dev,
  78. struct device_attribute *multi_index_attr,
  79. char *buf)
  80. {
  81. struct led_classdev *led_cdev = dev_get_drvdata(dev);
  82. struct led_classdev_mc *mcled_cdev = lcdev_to_mccdev(led_cdev);
  83. int len = 0;
  84. int index;
  85. int i;
  86. for (i = 0; i < mcled_cdev->num_colors; i++) {
  87. index = mcled_cdev->subled_info[i].color_index;
  88. len += sprintf(buf + len, "%s", led_get_color_name(index));
  89. if (i < mcled_cdev->num_colors - 1)
  90. len += sprintf(buf + len, " ");
  91. }
  92. buf[len++] = '\n';
  93. return len;
  94. }
  95. static DEVICE_ATTR_RO(multi_index);
  96. static struct attribute *led_multicolor_attrs[] = {
  97. &dev_attr_multi_intensity.attr,
  98. &dev_attr_multi_index.attr,
  99. NULL,
  100. };
  101. ATTRIBUTE_GROUPS(led_multicolor);
  102. int led_classdev_multicolor_register_ext(struct device *parent,
  103. struct led_classdev_mc *mcled_cdev,
  104. struct led_init_data *init_data)
  105. {
  106. struct led_classdev *led_cdev;
  107. if (!mcled_cdev)
  108. return -EINVAL;
  109. if (mcled_cdev->num_colors <= 0)
  110. return -EINVAL;
  111. if (mcled_cdev->num_colors > LED_COLOR_ID_MAX)
  112. return -EINVAL;
  113. led_cdev = &mcled_cdev->led_cdev;
  114. led_cdev->flags |= LED_MULTI_COLOR;
  115. mcled_cdev->led_cdev.groups = led_multicolor_groups;
  116. return led_classdev_register_ext(parent, led_cdev, init_data);
  117. }
  118. EXPORT_SYMBOL_GPL(led_classdev_multicolor_register_ext);
  119. void led_classdev_multicolor_unregister(struct led_classdev_mc *mcled_cdev)
  120. {
  121. if (!mcled_cdev)
  122. return;
  123. led_classdev_unregister(&mcled_cdev->led_cdev);
  124. }
  125. EXPORT_SYMBOL_GPL(led_classdev_multicolor_unregister);
  126. static void devm_led_classdev_multicolor_release(struct device *dev, void *res)
  127. {
  128. led_classdev_multicolor_unregister(*(struct led_classdev_mc **)res);
  129. }
  130. int devm_led_classdev_multicolor_register_ext(struct device *parent,
  131. struct led_classdev_mc *mcled_cdev,
  132. struct led_init_data *init_data)
  133. {
  134. struct led_classdev_mc **dr;
  135. int ret;
  136. dr = devres_alloc(devm_led_classdev_multicolor_release,
  137. sizeof(*dr), GFP_KERNEL);
  138. if (!dr)
  139. return -ENOMEM;
  140. ret = led_classdev_multicolor_register_ext(parent, mcled_cdev,
  141. init_data);
  142. if (ret) {
  143. devres_free(dr);
  144. return ret;
  145. }
  146. *dr = mcled_cdev;
  147. devres_add(parent, dr);
  148. return 0;
  149. }
  150. EXPORT_SYMBOL_GPL(devm_led_classdev_multicolor_register_ext);
  151. static int devm_led_classdev_multicolor_match(struct device *dev,
  152. void *res, void *data)
  153. {
  154. struct led_classdev_mc **p = res;
  155. if (WARN_ON(!p || !*p))
  156. return 0;
  157. return *p == data;
  158. }
  159. void devm_led_classdev_multicolor_unregister(struct device *dev,
  160. struct led_classdev_mc *mcled_cdev)
  161. {
  162. WARN_ON(devres_release(dev,
  163. devm_led_classdev_multicolor_release,
  164. devm_led_classdev_multicolor_match, mcled_cdev));
  165. }
  166. EXPORT_SYMBOL_GPL(devm_led_classdev_multicolor_unregister);
  167. MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
  168. MODULE_DESCRIPTION("Multicolor LED class interface");
  169. MODULE_LICENSE("GPL v2");