pwm_backlight.c 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Copyright (c) 2016 Google, Inc
  4. * Written by Simon Glass <sjg@chromium.org>
  5. */
  6. #define LOG_CATEGORY UCLASS_PANEL_BACKLIGHT
  7. #include <common.h>
  8. #include <dm.h>
  9. #include <backlight.h>
  10. #include <malloc.h>
  11. #include <pwm.h>
  12. #include <asm/gpio.h>
  13. #include <power/regulator.h>
  14. /**
  15. * Private information for the PWM backlight
  16. *
  17. * If @num_levels is 0 then the levels are simple values with the backlight
  18. * value going between the minimum (default 0) and the maximum (default 255).
  19. * Otherwise the levels are an index into @levels (0..n-1).
  20. *
  21. * @reg: Regulator to enable to turn the backlight on (NULL if none)
  22. * @enable, GPIO to set to enable the backlight (can be missing)
  23. * @pwm: PWM to use to change the backlight brightness
  24. * @channel: PWM channel to use
  25. * @period_ns: Period of the backlight in nanoseconds
  26. * @levels: Levels for the backlight, or NULL if not using indexed levels
  27. * @num_levels: Number of levels
  28. * @cur_level: Current level for the backlight (index or value)
  29. * @default_level: Default level for the backlight (index or value)
  30. * @min_level: Minimum level of the backlight (full off)
  31. * @min_level: Maximum level of the backlight (full on)
  32. * @enabled: true if backlight is enabled
  33. */
  34. struct pwm_backlight_priv {
  35. struct udevice *reg;
  36. struct gpio_desc enable;
  37. struct udevice *pwm;
  38. uint channel;
  39. uint period_ns;
  40. /*
  41. * the polarity of one PWM
  42. * 0: normal polarity
  43. * 1: inverted polarity
  44. */
  45. bool polarity;
  46. u32 *levels;
  47. int num_levels;
  48. uint default_level;
  49. int cur_level;
  50. uint min_level;
  51. uint max_level;
  52. bool enabled;
  53. };
  54. static int set_pwm(struct pwm_backlight_priv *priv)
  55. {
  56. uint duty_cycle;
  57. int ret;
  58. duty_cycle = priv->period_ns * (priv->cur_level - priv->min_level) /
  59. (priv->max_level - priv->min_level + 1);
  60. ret = pwm_set_config(priv->pwm, priv->channel, priv->period_ns,
  61. duty_cycle);
  62. if (ret)
  63. return log_ret(ret);
  64. ret = pwm_set_invert(priv->pwm, priv->channel, priv->polarity);
  65. if (ret == -ENOSYS && !priv->polarity)
  66. ret = 0;
  67. return log_ret(ret);
  68. }
  69. static int enable_sequence(struct udevice *dev, int seq)
  70. {
  71. struct pwm_backlight_priv *priv = dev_get_priv(dev);
  72. int ret;
  73. switch (seq) {
  74. case 0:
  75. if (priv->reg) {
  76. __maybe_unused struct dm_regulator_uclass_platdata
  77. *plat;
  78. plat = dev_get_uclass_platdata(priv->reg);
  79. log_debug("Enable '%s', regulator '%s'/'%s'\n",
  80. dev->name, priv->reg->name, plat->name);
  81. ret = regulator_set_enable(priv->reg, true);
  82. if (ret) {
  83. log_debug("Cannot enable regulator for PWM '%s'\n",
  84. dev->name);
  85. return log_ret(ret);
  86. }
  87. mdelay(120);
  88. }
  89. break;
  90. case 1:
  91. mdelay(10);
  92. dm_gpio_set_value(&priv->enable, 1);
  93. break;
  94. }
  95. return 0;
  96. }
  97. static int pwm_backlight_enable(struct udevice *dev)
  98. {
  99. struct pwm_backlight_priv *priv = dev_get_priv(dev);
  100. int ret;
  101. ret = enable_sequence(dev, 0);
  102. if (ret)
  103. return log_ret(ret);
  104. ret = set_pwm(priv);
  105. if (ret)
  106. return log_ret(ret);
  107. ret = pwm_set_enable(priv->pwm, priv->channel, true);
  108. if (ret)
  109. return log_ret(ret);
  110. ret = enable_sequence(dev, 1);
  111. if (ret)
  112. return log_ret(ret);
  113. priv->enabled = true;
  114. return 0;
  115. }
  116. static int pwm_backlight_set_brightness(struct udevice *dev, int percent)
  117. {
  118. struct pwm_backlight_priv *priv = dev_get_priv(dev);
  119. bool disable = false;
  120. int level;
  121. int ret;
  122. if (!priv->enabled) {
  123. ret = enable_sequence(dev, 0);
  124. if (ret)
  125. return log_ret(ret);
  126. }
  127. if (percent == BACKLIGHT_OFF) {
  128. disable = true;
  129. percent = 0;
  130. }
  131. if (percent == BACKLIGHT_DEFAULT) {
  132. level = priv->default_level;
  133. } else {
  134. if (priv->levels) {
  135. level = priv->levels[percent * (priv->num_levels - 1)
  136. / 100];
  137. } else {
  138. level = priv->min_level +
  139. (priv->max_level - priv->min_level) *
  140. percent / 100;
  141. }
  142. }
  143. priv->cur_level = level;
  144. ret = set_pwm(priv);
  145. if (ret)
  146. return log_ret(ret);
  147. if (!priv->enabled) {
  148. ret = enable_sequence(dev, 1);
  149. if (ret)
  150. return log_ret(ret);
  151. priv->enabled = true;
  152. }
  153. if (disable) {
  154. dm_gpio_set_value(&priv->enable, 0);
  155. if (priv->reg) {
  156. ret = regulator_set_enable(priv->reg, false);
  157. if (ret)
  158. return log_ret(ret);
  159. }
  160. priv->enabled = false;
  161. }
  162. return 0;
  163. }
  164. static int pwm_backlight_ofdata_to_platdata(struct udevice *dev)
  165. {
  166. struct pwm_backlight_priv *priv = dev_get_priv(dev);
  167. struct ofnode_phandle_args args;
  168. int index, ret, count, len;
  169. const u32 *cell;
  170. log_debug("start\n");
  171. ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev,
  172. "power-supply", &priv->reg);
  173. if (ret)
  174. log_debug("Cannot get power supply: ret=%d\n", ret);
  175. ret = gpio_request_by_name(dev, "enable-gpios", 0, &priv->enable,
  176. GPIOD_IS_OUT);
  177. if (ret) {
  178. log_debug("Warning: cannot get enable GPIO: ret=%d\n", ret);
  179. if (ret != -ENOENT)
  180. return log_ret(ret);
  181. }
  182. ret = dev_read_phandle_with_args(dev, "pwms", "#pwm-cells", 0, 0,
  183. &args);
  184. if (ret) {
  185. log_debug("Cannot get PWM phandle: ret=%d\n", ret);
  186. return log_ret(ret);
  187. }
  188. ret = uclass_get_device_by_ofnode(UCLASS_PWM, args.node, &priv->pwm);
  189. if (ret) {
  190. log_debug("Cannot get PWM: ret=%d\n", ret);
  191. return log_ret(ret);
  192. }
  193. if (args.args_count < 2)
  194. return log_msg_ret("Not enough arguments to pwm\n", -EINVAL);
  195. priv->channel = args.args[0];
  196. priv->period_ns = args.args[1];
  197. if (args.args_count > 2)
  198. priv->polarity = args.args[2];
  199. index = dev_read_u32_default(dev, "default-brightness-level", 255);
  200. cell = dev_read_prop(dev, "brightness-levels", &len);
  201. count = len / sizeof(u32);
  202. if (cell && count > index) {
  203. priv->levels = malloc(len);
  204. if (!priv->levels)
  205. return log_ret(-ENOMEM);
  206. dev_read_u32_array(dev, "brightness-levels", priv->levels,
  207. count);
  208. priv->num_levels = count;
  209. priv->default_level = priv->levels[index];
  210. priv->max_level = priv->levels[count - 1];
  211. } else {
  212. priv->default_level = index;
  213. priv->max_level = 255;
  214. }
  215. priv->cur_level = priv->default_level;
  216. log_debug("done\n");
  217. return 0;
  218. }
  219. static int pwm_backlight_probe(struct udevice *dev)
  220. {
  221. return 0;
  222. }
  223. static const struct backlight_ops pwm_backlight_ops = {
  224. .enable = pwm_backlight_enable,
  225. .set_brightness = pwm_backlight_set_brightness,
  226. };
  227. static const struct udevice_id pwm_backlight_ids[] = {
  228. { .compatible = "pwm-backlight" },
  229. { }
  230. };
  231. U_BOOT_DRIVER(pwm_backlight) = {
  232. .name = "pwm_backlight",
  233. .id = UCLASS_PANEL_BACKLIGHT,
  234. .of_match = pwm_backlight_ids,
  235. .ops = &pwm_backlight_ops,
  236. .ofdata_to_platdata = pwm_backlight_ofdata_to_platdata,
  237. .probe = pwm_backlight_probe,
  238. .priv_auto_alloc_size = sizeof(struct pwm_backlight_priv),
  239. };