lp855x_bl.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * TI LP855x Backlight Driver
  4. *
  5. * Copyright (C) 2011 Texas Instruments
  6. */
  7. #include <linux/acpi.h>
  8. #include <linux/module.h>
  9. #include <linux/slab.h>
  10. #include <linux/i2c.h>
  11. #include <linux/backlight.h>
  12. #include <linux/delay.h>
  13. #include <linux/err.h>
  14. #include <linux/of.h>
  15. #include <linux/platform_data/lp855x.h>
  16. #include <linux/pwm.h>
  17. #include <linux/regulator/consumer.h>
  18. /* LP8550/1/2/3/6 Registers */
  19. #define LP855X_BRIGHTNESS_CTRL 0x00
  20. #define LP855X_DEVICE_CTRL 0x01
  21. #define LP855X_EEPROM_START 0xA0
  22. #define LP855X_EEPROM_END 0xA7
  23. #define LP8556_EPROM_START 0xA0
  24. #define LP8556_EPROM_END 0xAF
  25. /* LP8555/7 Registers */
  26. #define LP8557_BL_CMD 0x00
  27. #define LP8557_BL_MASK 0x01
  28. #define LP8557_BL_ON 0x01
  29. #define LP8557_BL_OFF 0x00
  30. #define LP8557_BRIGHTNESS_CTRL 0x04
  31. #define LP8557_CONFIG 0x10
  32. #define LP8555_EPROM_START 0x10
  33. #define LP8555_EPROM_END 0x7A
  34. #define LP8557_EPROM_START 0x10
  35. #define LP8557_EPROM_END 0x1E
  36. #define DEFAULT_BL_NAME "lcd-backlight"
  37. #define MAX_BRIGHTNESS 255
  38. enum lp855x_brightness_ctrl_mode {
  39. PWM_BASED = 1,
  40. REGISTER_BASED,
  41. };
  42. struct lp855x;
  43. /*
  44. * struct lp855x_device_config
  45. * @pre_init_device: init device function call before updating the brightness
  46. * @reg_brightness: register address for brigthenss control
  47. * @reg_devicectrl: register address for device control
  48. * @post_init_device: late init device function call
  49. */
  50. struct lp855x_device_config {
  51. int (*pre_init_device)(struct lp855x *);
  52. u8 reg_brightness;
  53. u8 reg_devicectrl;
  54. int (*post_init_device)(struct lp855x *);
  55. };
  56. struct lp855x {
  57. const char *chipname;
  58. enum lp855x_chip_id chip_id;
  59. enum lp855x_brightness_ctrl_mode mode;
  60. struct lp855x_device_config *cfg;
  61. struct i2c_client *client;
  62. struct backlight_device *bl;
  63. struct device *dev;
  64. struct lp855x_platform_data *pdata;
  65. struct pwm_device *pwm;
  66. bool needs_pwm_init;
  67. struct regulator *supply; /* regulator for VDD input */
  68. struct regulator *enable; /* regulator for EN/VDDIO input */
  69. };
  70. static int lp855x_write_byte(struct lp855x *lp, u8 reg, u8 data)
  71. {
  72. return i2c_smbus_write_byte_data(lp->client, reg, data);
  73. }
  74. static int lp855x_update_bit(struct lp855x *lp, u8 reg, u8 mask, u8 data)
  75. {
  76. int ret;
  77. u8 tmp;
  78. ret = i2c_smbus_read_byte_data(lp->client, reg);
  79. if (ret < 0) {
  80. dev_err(lp->dev, "failed to read 0x%.2x\n", reg);
  81. return ret;
  82. }
  83. tmp = (u8)ret;
  84. tmp &= ~mask;
  85. tmp |= data & mask;
  86. return lp855x_write_byte(lp, reg, tmp);
  87. }
  88. static bool lp855x_is_valid_rom_area(struct lp855x *lp, u8 addr)
  89. {
  90. u8 start, end;
  91. switch (lp->chip_id) {
  92. case LP8550:
  93. case LP8551:
  94. case LP8552:
  95. case LP8553:
  96. start = LP855X_EEPROM_START;
  97. end = LP855X_EEPROM_END;
  98. break;
  99. case LP8556:
  100. start = LP8556_EPROM_START;
  101. end = LP8556_EPROM_END;
  102. break;
  103. case LP8555:
  104. start = LP8555_EPROM_START;
  105. end = LP8555_EPROM_END;
  106. break;
  107. case LP8557:
  108. start = LP8557_EPROM_START;
  109. end = LP8557_EPROM_END;
  110. break;
  111. default:
  112. return false;
  113. }
  114. return addr >= start && addr <= end;
  115. }
  116. static int lp8557_bl_off(struct lp855x *lp)
  117. {
  118. /* BL_ON = 0 before updating EPROM settings */
  119. return lp855x_update_bit(lp, LP8557_BL_CMD, LP8557_BL_MASK,
  120. LP8557_BL_OFF);
  121. }
  122. static int lp8557_bl_on(struct lp855x *lp)
  123. {
  124. /* BL_ON = 1 after updating EPROM settings */
  125. return lp855x_update_bit(lp, LP8557_BL_CMD, LP8557_BL_MASK,
  126. LP8557_BL_ON);
  127. }
  128. static struct lp855x_device_config lp855x_dev_cfg = {
  129. .reg_brightness = LP855X_BRIGHTNESS_CTRL,
  130. .reg_devicectrl = LP855X_DEVICE_CTRL,
  131. };
  132. static struct lp855x_device_config lp8557_dev_cfg = {
  133. .reg_brightness = LP8557_BRIGHTNESS_CTRL,
  134. .reg_devicectrl = LP8557_CONFIG,
  135. .pre_init_device = lp8557_bl_off,
  136. .post_init_device = lp8557_bl_on,
  137. };
  138. /*
  139. * Device specific configuration flow
  140. *
  141. * a) pre_init_device(optional)
  142. * b) update the brightness register
  143. * c) update device control register
  144. * d) update ROM area(optional)
  145. * e) post_init_device(optional)
  146. *
  147. */
  148. static int lp855x_configure(struct lp855x *lp)
  149. {
  150. u8 val, addr;
  151. int i, ret;
  152. struct lp855x_platform_data *pd = lp->pdata;
  153. if (lp->cfg->pre_init_device) {
  154. ret = lp->cfg->pre_init_device(lp);
  155. if (ret) {
  156. dev_err(lp->dev, "pre init device err: %d\n", ret);
  157. goto err;
  158. }
  159. }
  160. val = pd->initial_brightness;
  161. ret = lp855x_write_byte(lp, lp->cfg->reg_brightness, val);
  162. if (ret)
  163. goto err;
  164. val = pd->device_control;
  165. ret = lp855x_write_byte(lp, lp->cfg->reg_devicectrl, val);
  166. if (ret)
  167. goto err;
  168. if (pd->size_program > 0) {
  169. for (i = 0; i < pd->size_program; i++) {
  170. addr = pd->rom_data[i].addr;
  171. val = pd->rom_data[i].val;
  172. if (!lp855x_is_valid_rom_area(lp, addr))
  173. continue;
  174. ret = lp855x_write_byte(lp, addr, val);
  175. if (ret)
  176. goto err;
  177. }
  178. }
  179. if (lp->cfg->post_init_device) {
  180. ret = lp->cfg->post_init_device(lp);
  181. if (ret) {
  182. dev_err(lp->dev, "post init device err: %d\n", ret);
  183. goto err;
  184. }
  185. }
  186. return 0;
  187. err:
  188. return ret;
  189. }
  190. static int lp855x_pwm_ctrl(struct lp855x *lp, int br, int max_br)
  191. {
  192. struct pwm_state state;
  193. if (lp->needs_pwm_init) {
  194. pwm_init_state(lp->pwm, &state);
  195. /* Legacy platform data compatibility */
  196. if (lp->pdata->period_ns > 0)
  197. state.period = lp->pdata->period_ns;
  198. lp->needs_pwm_init = false;
  199. } else {
  200. pwm_get_state(lp->pwm, &state);
  201. }
  202. state.duty_cycle = div_u64(br * state.period, max_br);
  203. state.enabled = state.duty_cycle;
  204. return pwm_apply_might_sleep(lp->pwm, &state);
  205. }
  206. static int lp855x_bl_update_status(struct backlight_device *bl)
  207. {
  208. struct lp855x *lp = bl_get_data(bl);
  209. int brightness = bl->props.brightness;
  210. if (bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK))
  211. brightness = 0;
  212. if (lp->mode == PWM_BASED)
  213. return lp855x_pwm_ctrl(lp, brightness,
  214. bl->props.max_brightness);
  215. else if (lp->mode == REGISTER_BASED)
  216. return lp855x_write_byte(lp, lp->cfg->reg_brightness,
  217. (u8)brightness);
  218. return -EINVAL;
  219. }
  220. static const struct backlight_ops lp855x_bl_ops = {
  221. .options = BL_CORE_SUSPENDRESUME,
  222. .update_status = lp855x_bl_update_status,
  223. };
  224. static int lp855x_backlight_register(struct lp855x *lp)
  225. {
  226. struct backlight_device *bl;
  227. struct backlight_properties props;
  228. struct lp855x_platform_data *pdata = lp->pdata;
  229. const char *name = pdata->name ? : DEFAULT_BL_NAME;
  230. memset(&props, 0, sizeof(props));
  231. props.type = BACKLIGHT_PLATFORM;
  232. props.max_brightness = MAX_BRIGHTNESS;
  233. if (pdata->initial_brightness > props.max_brightness)
  234. pdata->initial_brightness = props.max_brightness;
  235. props.brightness = pdata->initial_brightness;
  236. bl = devm_backlight_device_register(lp->dev, name, lp->dev, lp,
  237. &lp855x_bl_ops, &props);
  238. if (IS_ERR(bl))
  239. return PTR_ERR(bl);
  240. lp->bl = bl;
  241. return 0;
  242. }
  243. static ssize_t lp855x_get_chip_id(struct device *dev,
  244. struct device_attribute *attr, char *buf)
  245. {
  246. struct lp855x *lp = dev_get_drvdata(dev);
  247. return scnprintf(buf, PAGE_SIZE, "%s\n", lp->chipname);
  248. }
  249. static ssize_t lp855x_get_bl_ctl_mode(struct device *dev,
  250. struct device_attribute *attr, char *buf)
  251. {
  252. struct lp855x *lp = dev_get_drvdata(dev);
  253. char *strmode = NULL;
  254. if (lp->mode == PWM_BASED)
  255. strmode = "pwm based";
  256. else if (lp->mode == REGISTER_BASED)
  257. strmode = "register based";
  258. return scnprintf(buf, PAGE_SIZE, "%s\n", strmode);
  259. }
  260. static DEVICE_ATTR(chip_id, S_IRUGO, lp855x_get_chip_id, NULL);
  261. static DEVICE_ATTR(bl_ctl_mode, S_IRUGO, lp855x_get_bl_ctl_mode, NULL);
  262. static struct attribute *lp855x_attributes[] = {
  263. &dev_attr_chip_id.attr,
  264. &dev_attr_bl_ctl_mode.attr,
  265. NULL,
  266. };
  267. static const struct attribute_group lp855x_attr_group = {
  268. .attrs = lp855x_attributes,
  269. };
  270. #ifdef CONFIG_OF
  271. static int lp855x_parse_dt(struct lp855x *lp)
  272. {
  273. struct device *dev = lp->dev;
  274. struct device_node *node = dev->of_node;
  275. struct lp855x_platform_data *pdata = lp->pdata;
  276. int rom_length;
  277. if (!node) {
  278. dev_err(dev, "no platform data\n");
  279. return -EINVAL;
  280. }
  281. of_property_read_string(node, "bl-name", &pdata->name);
  282. of_property_read_u8(node, "dev-ctrl", &pdata->device_control);
  283. of_property_read_u8(node, "init-brt", &pdata->initial_brightness);
  284. /* Deprecated, specify period in pwms property instead */
  285. of_property_read_u32(node, "pwm-period", &pdata->period_ns);
  286. /* Fill ROM platform data if defined */
  287. rom_length = of_get_child_count(node);
  288. if (rom_length > 0) {
  289. struct lp855x_rom_data *rom;
  290. struct device_node *child;
  291. int i = 0;
  292. rom = devm_kcalloc(dev, rom_length, sizeof(*rom), GFP_KERNEL);
  293. if (!rom)
  294. return -ENOMEM;
  295. for_each_child_of_node(node, child) {
  296. of_property_read_u8(child, "rom-addr", &rom[i].addr);
  297. of_property_read_u8(child, "rom-val", &rom[i].val);
  298. i++;
  299. }
  300. pdata->size_program = rom_length;
  301. pdata->rom_data = &rom[0];
  302. }
  303. return 0;
  304. }
  305. #else
  306. static int lp855x_parse_dt(struct lp855x *lp)
  307. {
  308. return -EINVAL;
  309. }
  310. #endif
  311. static int lp855x_parse_acpi(struct lp855x *lp)
  312. {
  313. int ret;
  314. /*
  315. * On ACPI the device has already been initialized by the firmware
  316. * and is in register mode, so we can read back the settings from
  317. * the registers.
  318. */
  319. ret = i2c_smbus_read_byte_data(lp->client, lp->cfg->reg_brightness);
  320. if (ret < 0)
  321. return ret;
  322. lp->pdata->initial_brightness = ret;
  323. ret = i2c_smbus_read_byte_data(lp->client, lp->cfg->reg_devicectrl);
  324. if (ret < 0)
  325. return ret;
  326. lp->pdata->device_control = ret;
  327. return 0;
  328. }
  329. static int lp855x_probe(struct i2c_client *cl)
  330. {
  331. const struct i2c_device_id *id = i2c_client_get_device_id(cl);
  332. const struct acpi_device_id *acpi_id = NULL;
  333. struct device *dev = &cl->dev;
  334. struct lp855x *lp;
  335. int ret;
  336. if (!i2c_check_functionality(cl->adapter, I2C_FUNC_SMBUS_I2C_BLOCK))
  337. return -EIO;
  338. lp = devm_kzalloc(dev, sizeof(struct lp855x), GFP_KERNEL);
  339. if (!lp)
  340. return -ENOMEM;
  341. lp->client = cl;
  342. lp->dev = dev;
  343. lp->pdata = dev_get_platdata(dev);
  344. if (id) {
  345. lp->chipname = id->name;
  346. lp->chip_id = id->driver_data;
  347. } else {
  348. acpi_id = acpi_match_device(dev->driver->acpi_match_table, dev);
  349. if (!acpi_id)
  350. return -ENODEV;
  351. lp->chipname = acpi_id->id;
  352. lp->chip_id = acpi_id->driver_data;
  353. }
  354. switch (lp->chip_id) {
  355. case LP8550:
  356. case LP8551:
  357. case LP8552:
  358. case LP8553:
  359. case LP8556:
  360. lp->cfg = &lp855x_dev_cfg;
  361. break;
  362. case LP8555:
  363. case LP8557:
  364. lp->cfg = &lp8557_dev_cfg;
  365. break;
  366. default:
  367. return -EINVAL;
  368. }
  369. if (!lp->pdata) {
  370. lp->pdata = devm_kzalloc(dev, sizeof(*lp->pdata), GFP_KERNEL);
  371. if (!lp->pdata)
  372. return -ENOMEM;
  373. if (id) {
  374. ret = lp855x_parse_dt(lp);
  375. if (ret < 0)
  376. return ret;
  377. } else {
  378. ret = lp855x_parse_acpi(lp);
  379. if (ret < 0)
  380. return ret;
  381. }
  382. }
  383. lp->supply = devm_regulator_get(dev, "power");
  384. if (IS_ERR(lp->supply)) {
  385. if (PTR_ERR(lp->supply) == -EPROBE_DEFER)
  386. return -EPROBE_DEFER;
  387. lp->supply = NULL;
  388. }
  389. lp->enable = devm_regulator_get_optional(dev, "enable");
  390. if (IS_ERR(lp->enable)) {
  391. ret = PTR_ERR(lp->enable);
  392. if (ret == -ENODEV)
  393. lp->enable = NULL;
  394. else
  395. return dev_err_probe(dev, ret, "getting enable regulator\n");
  396. }
  397. lp->pwm = devm_pwm_get(lp->dev, lp->chipname);
  398. if (IS_ERR(lp->pwm)) {
  399. ret = PTR_ERR(lp->pwm);
  400. if (ret == -ENODEV || ret == -EINVAL)
  401. lp->pwm = NULL;
  402. else
  403. return dev_err_probe(dev, ret, "getting PWM\n");
  404. lp->needs_pwm_init = false;
  405. lp->mode = REGISTER_BASED;
  406. dev_dbg(dev, "mode: register based\n");
  407. } else {
  408. lp->needs_pwm_init = true;
  409. lp->mode = PWM_BASED;
  410. dev_dbg(dev, "mode: PWM based\n");
  411. }
  412. if (lp->supply) {
  413. ret = regulator_enable(lp->supply);
  414. if (ret < 0) {
  415. dev_err(dev, "failed to enable supply: %d\n", ret);
  416. return ret;
  417. }
  418. }
  419. if (lp->enable) {
  420. ret = regulator_enable(lp->enable);
  421. if (ret < 0) {
  422. dev_err(dev, "failed to enable vddio: %d\n", ret);
  423. goto disable_supply;
  424. }
  425. /*
  426. * LP8555 datasheet says t_RESPONSE (time between VDDIO and
  427. * I2C) is 1ms.
  428. */
  429. usleep_range(1000, 2000);
  430. }
  431. i2c_set_clientdata(cl, lp);
  432. ret = lp855x_configure(lp);
  433. if (ret) {
  434. dev_err(dev, "device config err: %d", ret);
  435. goto disable_vddio;
  436. }
  437. ret = lp855x_backlight_register(lp);
  438. if (ret) {
  439. dev_err(dev, "failed to register backlight. err: %d\n", ret);
  440. goto disable_vddio;
  441. }
  442. ret = sysfs_create_group(&dev->kobj, &lp855x_attr_group);
  443. if (ret) {
  444. dev_err(dev, "failed to register sysfs. err: %d\n", ret);
  445. goto disable_vddio;
  446. }
  447. backlight_update_status(lp->bl);
  448. return 0;
  449. disable_vddio:
  450. if (lp->enable)
  451. regulator_disable(lp->enable);
  452. disable_supply:
  453. if (lp->supply)
  454. regulator_disable(lp->supply);
  455. return ret;
  456. }
  457. static void lp855x_remove(struct i2c_client *cl)
  458. {
  459. struct lp855x *lp = i2c_get_clientdata(cl);
  460. lp->bl->props.brightness = 0;
  461. backlight_update_status(lp->bl);
  462. if (lp->enable)
  463. regulator_disable(lp->enable);
  464. if (lp->supply)
  465. regulator_disable(lp->supply);
  466. sysfs_remove_group(&lp->dev->kobj, &lp855x_attr_group);
  467. }
  468. static const struct of_device_id lp855x_dt_ids[] __maybe_unused = {
  469. { .compatible = "ti,lp8550", },
  470. { .compatible = "ti,lp8551", },
  471. { .compatible = "ti,lp8552", },
  472. { .compatible = "ti,lp8553", },
  473. { .compatible = "ti,lp8555", },
  474. { .compatible = "ti,lp8556", },
  475. { .compatible = "ti,lp8557", },
  476. { }
  477. };
  478. MODULE_DEVICE_TABLE(of, lp855x_dt_ids);
  479. static const struct i2c_device_id lp855x_ids[] = {
  480. {"lp8550", LP8550},
  481. {"lp8551", LP8551},
  482. {"lp8552", LP8552},
  483. {"lp8553", LP8553},
  484. {"lp8555", LP8555},
  485. {"lp8556", LP8556},
  486. {"lp8557", LP8557},
  487. { }
  488. };
  489. MODULE_DEVICE_TABLE(i2c, lp855x_ids);
  490. #ifdef CONFIG_ACPI
  491. static const struct acpi_device_id lp855x_acpi_match[] = {
  492. /* Xiaomi specific HID used for the LP8556 on the Mi Pad 2 */
  493. { "XMCC0001", LP8556 },
  494. { }
  495. };
  496. MODULE_DEVICE_TABLE(acpi, lp855x_acpi_match);
  497. #endif
  498. static struct i2c_driver lp855x_driver = {
  499. .driver = {
  500. .name = "lp855x",
  501. .of_match_table = of_match_ptr(lp855x_dt_ids),
  502. .acpi_match_table = ACPI_PTR(lp855x_acpi_match),
  503. },
  504. .probe = lp855x_probe,
  505. .remove = lp855x_remove,
  506. .id_table = lp855x_ids,
  507. };
  508. module_i2c_driver(lp855x_driver);
  509. MODULE_DESCRIPTION("Texas Instruments LP855x Backlight driver");
  510. MODULE_AUTHOR("Milo Kim <milo.kim@ti.com>");
  511. MODULE_LICENSE("GPL");