tps65217_bl.c 7.4 KB

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