tps65217_bl.c 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * tps65217_bl.c
  4. *
  5. * TPS65217 backlight driver
  6. *
  7. * Copyright (C) 2012 Matthias Kaehlcke
  8. * Author: Matthias Kaehlcke <matthias@kaehlcke.net>
  9. */
  10. #include <linux/kernel.h>
  11. #include <linux/backlight.h>
  12. #include <linux/err.h>
  13. #include <linux/mfd/tps65217.h>
  14. #include <linux/module.h>
  15. #include <linux/platform_device.h>
  16. #include <linux/slab.h>
  17. struct tps65217_bl {
  18. struct tps65217 *tps;
  19. struct device *dev;
  20. struct backlight_device *bl;
  21. bool is_enabled;
  22. };
  23. static int tps65217_bl_enable(struct tps65217_bl *tps65217_bl)
  24. {
  25. int rc;
  26. rc = tps65217_set_bits(tps65217_bl->tps, TPS65217_REG_WLEDCTRL1,
  27. TPS65217_WLEDCTRL1_ISINK_ENABLE,
  28. TPS65217_WLEDCTRL1_ISINK_ENABLE, TPS65217_PROTECT_NONE);
  29. if (rc) {
  30. dev_err(tps65217_bl->dev,
  31. "failed to enable backlight: %d\n", rc);
  32. return rc;
  33. }
  34. tps65217_bl->is_enabled = true;
  35. dev_dbg(tps65217_bl->dev, "backlight enabled\n");
  36. return 0;
  37. }
  38. static int tps65217_bl_disable(struct tps65217_bl *tps65217_bl)
  39. {
  40. int rc;
  41. rc = tps65217_clear_bits(tps65217_bl->tps,
  42. TPS65217_REG_WLEDCTRL1,
  43. TPS65217_WLEDCTRL1_ISINK_ENABLE,
  44. TPS65217_PROTECT_NONE);
  45. if (rc) {
  46. dev_err(tps65217_bl->dev,
  47. "failed to disable backlight: %d\n", rc);
  48. return rc;
  49. }
  50. tps65217_bl->is_enabled = false;
  51. dev_dbg(tps65217_bl->dev, "backlight disabled\n");
  52. return 0;
  53. }
  54. static int tps65217_bl_update_status(struct backlight_device *bl)
  55. {
  56. struct tps65217_bl *tps65217_bl = bl_get_data(bl);
  57. int rc;
  58. int brightness = backlight_get_brightness(bl);
  59. if (brightness > 0) {
  60. rc = tps65217_reg_write(tps65217_bl->tps,
  61. TPS65217_REG_WLEDCTRL2,
  62. brightness - 1,
  63. TPS65217_PROTECT_NONE);
  64. if (rc) {
  65. dev_err(tps65217_bl->dev,
  66. "failed to set brightness level: %d\n", rc);
  67. return rc;
  68. }
  69. dev_dbg(tps65217_bl->dev, "brightness set to %d\n", brightness);
  70. if (!tps65217_bl->is_enabled)
  71. rc = tps65217_bl_enable(tps65217_bl);
  72. } else {
  73. rc = tps65217_bl_disable(tps65217_bl);
  74. }
  75. return rc;
  76. }
  77. static const struct backlight_ops tps65217_bl_ops = {
  78. .options = BL_CORE_SUSPENDRESUME,
  79. .update_status = tps65217_bl_update_status,
  80. };
  81. static int tps65217_bl_hw_init(struct tps65217_bl *tps65217_bl,
  82. struct tps65217_bl_pdata *pdata)
  83. {
  84. int rc;
  85. rc = tps65217_bl_disable(tps65217_bl);
  86. if (rc)
  87. return rc;
  88. switch (pdata->isel) {
  89. case TPS65217_BL_ISET1:
  90. /* select ISET_1 current level */
  91. rc = tps65217_clear_bits(tps65217_bl->tps,
  92. TPS65217_REG_WLEDCTRL1,
  93. TPS65217_WLEDCTRL1_ISEL,
  94. TPS65217_PROTECT_NONE);
  95. if (rc) {
  96. dev_err(tps65217_bl->dev,
  97. "failed to select ISET1 current level: %d)\n",
  98. rc);
  99. return rc;
  100. }
  101. dev_dbg(tps65217_bl->dev, "selected ISET1 current level\n");
  102. break;
  103. case TPS65217_BL_ISET2:
  104. /* select ISET2 current level */
  105. rc = tps65217_set_bits(tps65217_bl->tps, TPS65217_REG_WLEDCTRL1,
  106. TPS65217_WLEDCTRL1_ISEL,
  107. TPS65217_WLEDCTRL1_ISEL, TPS65217_PROTECT_NONE);
  108. if (rc) {
  109. dev_err(tps65217_bl->dev,
  110. "failed to select ISET2 current level: %d\n",
  111. rc);
  112. return rc;
  113. }
  114. dev_dbg(tps65217_bl->dev, "selected ISET2 current level\n");
  115. break;
  116. default:
  117. dev_err(tps65217_bl->dev,
  118. "invalid value for current level: %d\n", pdata->isel);
  119. return -EINVAL;
  120. }
  121. /* set PWM frequency */
  122. rc = tps65217_set_bits(tps65217_bl->tps,
  123. TPS65217_REG_WLEDCTRL1,
  124. TPS65217_WLEDCTRL1_FDIM_MASK,
  125. pdata->fdim,
  126. TPS65217_PROTECT_NONE);
  127. if (rc) {
  128. dev_err(tps65217_bl->dev,
  129. "failed to select PWM dimming frequency: %d\n",
  130. rc);
  131. return rc;
  132. }
  133. return 0;
  134. }
  135. #ifdef CONFIG_OF
  136. static struct tps65217_bl_pdata *
  137. tps65217_bl_parse_dt(struct platform_device *pdev)
  138. {
  139. struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent);
  140. struct device_node *node;
  141. struct tps65217_bl_pdata *pdata, *err;
  142. u32 val;
  143. node = of_get_child_by_name(tps->dev->of_node, "backlight");
  144. if (!node)
  145. return ERR_PTR(-ENODEV);
  146. pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
  147. if (!pdata) {
  148. err = ERR_PTR(-ENOMEM);
  149. goto err;
  150. }
  151. pdata->isel = TPS65217_BL_ISET1;
  152. if (!of_property_read_u32(node, "isel", &val)) {
  153. if (val < TPS65217_BL_ISET1 ||
  154. val > TPS65217_BL_ISET2) {
  155. dev_err(&pdev->dev,
  156. "invalid 'isel' value in the device tree\n");
  157. err = ERR_PTR(-EINVAL);
  158. goto err;
  159. }
  160. pdata->isel = val;
  161. }
  162. pdata->fdim = TPS65217_BL_FDIM_200HZ;
  163. if (!of_property_read_u32(node, "fdim", &val)) {
  164. switch (val) {
  165. case 100:
  166. pdata->fdim = TPS65217_BL_FDIM_100HZ;
  167. break;
  168. case 200:
  169. pdata->fdim = TPS65217_BL_FDIM_200HZ;
  170. break;
  171. case 500:
  172. pdata->fdim = TPS65217_BL_FDIM_500HZ;
  173. break;
  174. case 1000:
  175. pdata->fdim = TPS65217_BL_FDIM_1000HZ;
  176. break;
  177. default:
  178. dev_err(&pdev->dev,
  179. "invalid 'fdim' value in the device tree\n");
  180. err = ERR_PTR(-EINVAL);
  181. goto err;
  182. }
  183. }
  184. if (!of_property_read_u32(node, "default-brightness", &val)) {
  185. if (val > 100) {
  186. dev_err(&pdev->dev,
  187. "invalid 'default-brightness' value in the device tree\n");
  188. err = ERR_PTR(-EINVAL);
  189. goto err;
  190. }
  191. pdata->dft_brightness = val;
  192. }
  193. of_node_put(node);
  194. return pdata;
  195. err:
  196. of_node_put(node);
  197. return err;
  198. }
  199. #else
  200. static struct tps65217_bl_pdata *
  201. tps65217_bl_parse_dt(struct platform_device *pdev)
  202. {
  203. return NULL;
  204. }
  205. #endif
  206. static int tps65217_bl_probe(struct platform_device *pdev)
  207. {
  208. int rc;
  209. struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent);
  210. struct tps65217_bl *tps65217_bl;
  211. struct tps65217_bl_pdata *pdata;
  212. struct backlight_properties bl_props;
  213. pdata = tps65217_bl_parse_dt(pdev);
  214. if (IS_ERR(pdata))
  215. return PTR_ERR(pdata);
  216. tps65217_bl = devm_kzalloc(&pdev->dev, sizeof(*tps65217_bl),
  217. GFP_KERNEL);
  218. if (tps65217_bl == NULL)
  219. return -ENOMEM;
  220. tps65217_bl->tps = tps;
  221. tps65217_bl->dev = &pdev->dev;
  222. tps65217_bl->is_enabled = false;
  223. rc = tps65217_bl_hw_init(tps65217_bl, pdata);
  224. if (rc)
  225. return rc;
  226. memset(&bl_props, 0, sizeof(struct backlight_properties));
  227. bl_props.type = BACKLIGHT_RAW;
  228. bl_props.max_brightness = 100;
  229. tps65217_bl->bl = devm_backlight_device_register(&pdev->dev, pdev->name,
  230. tps65217_bl->dev, tps65217_bl,
  231. &tps65217_bl_ops, &bl_props);
  232. if (IS_ERR(tps65217_bl->bl)) {
  233. dev_err(tps65217_bl->dev,
  234. "registration of backlight device failed: %d\n", rc);
  235. return PTR_ERR(tps65217_bl->bl);
  236. }
  237. tps65217_bl->bl->props.brightness = pdata->dft_brightness;
  238. backlight_update_status(tps65217_bl->bl);
  239. platform_set_drvdata(pdev, tps65217_bl);
  240. return 0;
  241. }
  242. #ifdef CONFIG_OF
  243. static const struct of_device_id tps65217_bl_of_match[] = {
  244. { .compatible = "ti,tps65217-bl", },
  245. { /* sentinel */ },
  246. };
  247. MODULE_DEVICE_TABLE(of, tps65217_bl_of_match);
  248. #endif
  249. static struct platform_driver tps65217_bl_driver = {
  250. .probe = tps65217_bl_probe,
  251. .driver = {
  252. .name = "tps65217-bl",
  253. .of_match_table = of_match_ptr(tps65217_bl_of_match),
  254. },
  255. };
  256. module_platform_driver(tps65217_bl_driver);
  257. MODULE_DESCRIPTION("TPS65217 Backlight driver");
  258. MODULE_LICENSE("GPL v2");
  259. MODULE_AUTHOR("Matthias Kaehlcke <matthias@kaehlcke.net>");