tps65217_bl.c 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  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 int tps65217_bl_get_brightness(struct backlight_device *bl)
  93. {
  94. return bl->props.brightness;
  95. }
  96. static const struct backlight_ops tps65217_bl_ops = {
  97. .options = BL_CORE_SUSPENDRESUME,
  98. .update_status = tps65217_bl_update_status,
  99. .get_brightness = tps65217_bl_get_brightness
  100. };
  101. static int tps65217_bl_hw_init(struct tps65217_bl *tps65217_bl,
  102. struct tps65217_bl_pdata *pdata)
  103. {
  104. int rc;
  105. rc = tps65217_bl_disable(tps65217_bl);
  106. if (rc)
  107. return rc;
  108. switch (pdata->isel) {
  109. case TPS65217_BL_ISET1:
  110. /* select ISET_1 current level */
  111. rc = tps65217_clear_bits(tps65217_bl->tps,
  112. TPS65217_REG_WLEDCTRL1,
  113. TPS65217_WLEDCTRL1_ISEL,
  114. TPS65217_PROTECT_NONE);
  115. if (rc) {
  116. dev_err(tps65217_bl->dev,
  117. "failed to select ISET1 current level: %d)\n",
  118. rc);
  119. return rc;
  120. }
  121. dev_dbg(tps65217_bl->dev, "selected ISET1 current level\n");
  122. break;
  123. case TPS65217_BL_ISET2:
  124. /* select ISET2 current level */
  125. rc = tps65217_set_bits(tps65217_bl->tps, TPS65217_REG_WLEDCTRL1,
  126. TPS65217_WLEDCTRL1_ISEL,
  127. TPS65217_WLEDCTRL1_ISEL, TPS65217_PROTECT_NONE);
  128. if (rc) {
  129. dev_err(tps65217_bl->dev,
  130. "failed to select ISET2 current level: %d\n",
  131. rc);
  132. return rc;
  133. }
  134. dev_dbg(tps65217_bl->dev, "selected ISET2 current level\n");
  135. break;
  136. default:
  137. dev_err(tps65217_bl->dev,
  138. "invalid value for current level: %d\n", pdata->isel);
  139. return -EINVAL;
  140. }
  141. /* set PWM frequency */
  142. rc = tps65217_set_bits(tps65217_bl->tps,
  143. TPS65217_REG_WLEDCTRL1,
  144. TPS65217_WLEDCTRL1_FDIM_MASK,
  145. pdata->fdim,
  146. TPS65217_PROTECT_NONE);
  147. if (rc) {
  148. dev_err(tps65217_bl->dev,
  149. "failed to select PWM dimming frequency: %d\n",
  150. rc);
  151. return rc;
  152. }
  153. return 0;
  154. }
  155. #ifdef CONFIG_OF
  156. static struct tps65217_bl_pdata *
  157. tps65217_bl_parse_dt(struct platform_device *pdev)
  158. {
  159. struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent);
  160. struct device_node *node = of_node_get(tps->dev->of_node);
  161. struct tps65217_bl_pdata *pdata, *err;
  162. u32 val;
  163. node = of_find_node_by_name(node, "backlight");
  164. if (!node)
  165. return ERR_PTR(-ENODEV);
  166. pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
  167. if (!pdata) {
  168. dev_err(&pdev->dev, "failed to allocate platform data\n");
  169. err = ERR_PTR(-ENOMEM);
  170. goto err;
  171. }
  172. pdata->isel = TPS65217_BL_ISET1;
  173. if (!of_property_read_u32(node, "isel", &val)) {
  174. if (val < TPS65217_BL_ISET1 ||
  175. val > TPS65217_BL_ISET2) {
  176. dev_err(&pdev->dev,
  177. "invalid 'isel' value in the device tree\n");
  178. err = ERR_PTR(-EINVAL);
  179. goto err;
  180. }
  181. pdata->isel = val;
  182. }
  183. pdata->fdim = TPS65217_BL_FDIM_200HZ;
  184. if (!of_property_read_u32(node, "fdim", &val)) {
  185. switch (val) {
  186. case 100:
  187. pdata->fdim = TPS65217_BL_FDIM_100HZ;
  188. break;
  189. case 200:
  190. pdata->fdim = TPS65217_BL_FDIM_200HZ;
  191. break;
  192. case 500:
  193. pdata->fdim = TPS65217_BL_FDIM_500HZ;
  194. break;
  195. case 1000:
  196. pdata->fdim = TPS65217_BL_FDIM_1000HZ;
  197. break;
  198. default:
  199. dev_err(&pdev->dev,
  200. "invalid 'fdim' value in the device tree\n");
  201. err = ERR_PTR(-EINVAL);
  202. goto err;
  203. }
  204. }
  205. of_node_put(node);
  206. return pdata;
  207. err:
  208. of_node_put(node);
  209. return err;
  210. }
  211. #else
  212. static struct tps65217_bl_pdata *
  213. tps65217_bl_parse_dt(struct platform_device *pdev)
  214. {
  215. return NULL;
  216. }
  217. #endif
  218. static int tps65217_bl_probe(struct platform_device *pdev)
  219. {
  220. int rc;
  221. struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent);
  222. struct tps65217_bl *tps65217_bl;
  223. struct tps65217_bl_pdata *pdata;
  224. struct backlight_properties bl_props;
  225. if (tps->dev->of_node) {
  226. pdata = tps65217_bl_parse_dt(pdev);
  227. if (IS_ERR(pdata))
  228. return PTR_ERR(pdata);
  229. } else {
  230. if (!pdev->dev.platform_data) {
  231. dev_err(&pdev->dev, "no platform data provided\n");
  232. return -EINVAL;
  233. }
  234. pdata = pdev->dev.platform_data;
  235. }
  236. tps65217_bl = devm_kzalloc(&pdev->dev, sizeof(*tps65217_bl),
  237. GFP_KERNEL);
  238. if (tps65217_bl == NULL) {
  239. dev_err(&pdev->dev, "allocation of struct tps65217_bl failed\n");
  240. return -ENOMEM;
  241. }
  242. tps65217_bl->tps = tps;
  243. tps65217_bl->dev = &pdev->dev;
  244. tps65217_bl->is_enabled = false;
  245. rc = tps65217_bl_hw_init(tps65217_bl, pdata);
  246. if (rc)
  247. return rc;
  248. memset(&bl_props, 0, sizeof(struct backlight_properties));
  249. bl_props.type = BACKLIGHT_RAW;
  250. bl_props.max_brightness = 100;
  251. tps65217_bl->bl = backlight_device_register(pdev->name,
  252. tps65217_bl->dev, tps65217_bl,
  253. &tps65217_bl_ops, &bl_props);
  254. if (IS_ERR(tps65217_bl->bl)) {
  255. dev_err(tps65217_bl->dev,
  256. "registration of backlight device failed: %d\n", rc);
  257. return PTR_ERR(tps65217_bl->bl);
  258. }
  259. tps65217_bl->bl->props.brightness = 0;
  260. platform_set_drvdata(pdev, tps65217_bl);
  261. return 0;
  262. }
  263. static int tps65217_bl_remove(struct platform_device *pdev)
  264. {
  265. struct tps65217_bl *tps65217_bl = platform_get_drvdata(pdev);
  266. backlight_device_unregister(tps65217_bl->bl);
  267. return 0;
  268. }
  269. static struct platform_driver tps65217_bl_driver = {
  270. .probe = tps65217_bl_probe,
  271. .remove = tps65217_bl_remove,
  272. .driver = {
  273. .owner = THIS_MODULE,
  274. .name = "tps65217-bl",
  275. },
  276. };
  277. module_platform_driver(tps65217_bl_driver);
  278. MODULE_DESCRIPTION("TPS65217 Backlight driver");
  279. MODULE_LICENSE("GPL v2");
  280. MODULE_AUTHOR("Matthias Kaehlcke <matthias@kaehlcke.net>");