mp3309c.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * Driver for MPS MP3309C White LED driver with I2C interface
  4. *
  5. * This driver support both analog (by I2C commands) and PWM dimming control
  6. * modes.
  7. *
  8. * Copyright (C) 2023 ASEM Srl
  9. * Author: Flavio Suligoi <f.suligoi@asem.it>
  10. *
  11. * Based on pwm_bl.c
  12. */
  13. #include <linux/backlight.h>
  14. #include <linux/delay.h>
  15. #include <linux/gpio/consumer.h>
  16. #include <linux/i2c.h>
  17. #include <linux/mod_devicetable.h>
  18. #include <linux/property.h>
  19. #include <linux/pwm.h>
  20. #include <linux/regmap.h>
  21. #define REG_I2C_0 0x00
  22. #define REG_I2C_1 0x01
  23. #define REG_I2C_0_EN 0x80
  24. #define REG_I2C_0_D0 0x40
  25. #define REG_I2C_0_D1 0x20
  26. #define REG_I2C_0_D2 0x10
  27. #define REG_I2C_0_D3 0x08
  28. #define REG_I2C_0_D4 0x04
  29. #define REG_I2C_0_RSRV1 0x02
  30. #define REG_I2C_0_RSRV2 0x01
  31. #define REG_I2C_1_RSRV1 0x80
  32. #define REG_I2C_1_DIMS 0x40
  33. #define REG_I2C_1_SYNC 0x20
  34. #define REG_I2C_1_OVP0 0x10
  35. #define REG_I2C_1_OVP1 0x08
  36. #define REG_I2C_1_VOS 0x04
  37. #define REG_I2C_1_LEDO 0x02
  38. #define REG_I2C_1_OTP 0x01
  39. #define ANALOG_I2C_NUM_LEVELS 32 /* 0..31 */
  40. #define ANALOG_I2C_REG_MASK 0x7c
  41. #define MP3309C_PWM_DEFAULT_NUM_LEVELS 256 /* 0..255 */
  42. enum mp3309c_status_value {
  43. FIRST_POWER_ON,
  44. BACKLIGHT_OFF,
  45. BACKLIGHT_ON,
  46. };
  47. enum mp3309c_dimming_mode_value {
  48. DIMMING_PWM,
  49. DIMMING_ANALOG_I2C,
  50. };
  51. struct mp3309c_platform_data {
  52. unsigned int max_brightness;
  53. unsigned int default_brightness;
  54. unsigned int *levels;
  55. u8 dimming_mode;
  56. u8 over_voltage_protection;
  57. bool sync_mode;
  58. u8 status;
  59. };
  60. struct mp3309c_chip {
  61. struct device *dev;
  62. struct mp3309c_platform_data *pdata;
  63. struct backlight_device *bl;
  64. struct gpio_desc *enable_gpio;
  65. struct regmap *regmap;
  66. struct pwm_device *pwmd;
  67. };
  68. static const struct regmap_config mp3309c_regmap = {
  69. .name = "mp3309c_regmap",
  70. .reg_bits = 8,
  71. .reg_stride = 1,
  72. .val_bits = 8,
  73. .max_register = REG_I2C_1,
  74. };
  75. static int mp3309c_enable_device(struct mp3309c_chip *chip)
  76. {
  77. u8 reg_val;
  78. int ret;
  79. /* I2C register #0 - Device enable */
  80. ret = regmap_update_bits(chip->regmap, REG_I2C_0, REG_I2C_0_EN,
  81. REG_I2C_0_EN);
  82. if (ret)
  83. return ret;
  84. /*
  85. * I2C register #1 - Set working mode:
  86. * - enable/disable synchronous mode
  87. * - set overvoltage protection (OVP)
  88. */
  89. reg_val = 0x00;
  90. if (chip->pdata->sync_mode)
  91. reg_val |= REG_I2C_1_SYNC;
  92. reg_val |= chip->pdata->over_voltage_protection;
  93. ret = regmap_write(chip->regmap, REG_I2C_1, reg_val);
  94. if (ret)
  95. return ret;
  96. return 0;
  97. }
  98. static int mp3309c_bl_update_status(struct backlight_device *bl)
  99. {
  100. struct mp3309c_chip *chip = bl_get_data(bl);
  101. int brightness = backlight_get_brightness(bl);
  102. struct pwm_state pwmstate;
  103. unsigned int analog_val, bits_val;
  104. int i, ret;
  105. if (chip->pdata->dimming_mode == DIMMING_PWM) {
  106. /*
  107. * PWM control mode
  108. */
  109. pwm_get_state(chip->pwmd, &pwmstate);
  110. pwm_set_relative_duty_cycle(&pwmstate,
  111. chip->pdata->levels[brightness],
  112. chip->pdata->levels[chip->pdata->max_brightness]);
  113. pwmstate.enabled = true;
  114. ret = pwm_apply_might_sleep(chip->pwmd, &pwmstate);
  115. if (ret)
  116. return ret;
  117. switch (chip->pdata->status) {
  118. case FIRST_POWER_ON:
  119. case BACKLIGHT_OFF:
  120. /*
  121. * After 20ms of low pwm signal level, the chip turns
  122. * off automatically. In this case, before enabling the
  123. * chip again, we must wait about 10ms for pwm signal to
  124. * stabilize.
  125. */
  126. if (brightness > 0) {
  127. msleep(10);
  128. mp3309c_enable_device(chip);
  129. chip->pdata->status = BACKLIGHT_ON;
  130. } else {
  131. chip->pdata->status = BACKLIGHT_OFF;
  132. }
  133. break;
  134. case BACKLIGHT_ON:
  135. if (brightness == 0)
  136. chip->pdata->status = BACKLIGHT_OFF;
  137. break;
  138. }
  139. } else {
  140. /*
  141. * Analog (by I2C command) control mode
  142. *
  143. * The first time, before setting brightness, we must enable the
  144. * device
  145. */
  146. if (chip->pdata->status == FIRST_POWER_ON)
  147. mp3309c_enable_device(chip);
  148. /*
  149. * Dimming mode I2C command (fixed dimming range 0..31)
  150. *
  151. * The 5 bits of the dimming analog value D4..D0 is allocated
  152. * in the I2C register #0, in the following way:
  153. *
  154. * +--+--+--+--+--+--+--+--+
  155. * |EN|D0|D1|D2|D3|D4|XX|XX|
  156. * +--+--+--+--+--+--+--+--+
  157. */
  158. analog_val = brightness;
  159. bits_val = 0;
  160. for (i = 0; i <= 5; i++)
  161. bits_val += ((analog_val >> i) & 0x01) << (6 - i);
  162. ret = regmap_update_bits(chip->regmap, REG_I2C_0,
  163. ANALOG_I2C_REG_MASK, bits_val);
  164. if (ret)
  165. return ret;
  166. if (brightness > 0)
  167. chip->pdata->status = BACKLIGHT_ON;
  168. else
  169. chip->pdata->status = BACKLIGHT_OFF;
  170. }
  171. return 0;
  172. }
  173. static const struct backlight_ops mp3309c_bl_ops = {
  174. .update_status = mp3309c_bl_update_status,
  175. };
  176. static int mp3309c_parse_fwnode(struct mp3309c_chip *chip,
  177. struct mp3309c_platform_data *pdata)
  178. {
  179. int ret, i;
  180. unsigned int tmp_value;
  181. struct device *dev = chip->dev;
  182. int num_levels;
  183. if (!dev_fwnode(dev))
  184. return dev_err_probe(dev, -ENODEV, "failed to get firmware node\n");
  185. /*
  186. * Dimming mode: the MP3309C provides two dimming control mode:
  187. *
  188. * - PWM mode
  189. * - Analog by I2C control mode (default)
  190. *
  191. * I2C control mode is assumed as default but, if the pwms property is
  192. * found in the backlight node, the mode switches to PWM mode.
  193. */
  194. pdata->dimming_mode = DIMMING_ANALOG_I2C;
  195. if (device_property_present(dev, "pwms")) {
  196. chip->pwmd = devm_pwm_get(dev, NULL);
  197. if (IS_ERR(chip->pwmd))
  198. return dev_err_probe(dev, PTR_ERR(chip->pwmd), "error getting pwm data\n");
  199. pdata->dimming_mode = DIMMING_PWM;
  200. pwm_apply_args(chip->pwmd);
  201. }
  202. /*
  203. * In I2C control mode the dimming levels (0..31) are fixed by the
  204. * hardware, while in PWM control mode they can be chosen by the user,
  205. * to allow nonlinear mappings.
  206. */
  207. if (pdata->dimming_mode == DIMMING_ANALOG_I2C) {
  208. /*
  209. * Analog (by I2C commands) control mode: fixed 0..31 brightness
  210. * levels
  211. */
  212. num_levels = ANALOG_I2C_NUM_LEVELS;
  213. /* Enable GPIO used in I2C dimming mode only */
  214. chip->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_HIGH);
  215. if (IS_ERR(chip->enable_gpio))
  216. return dev_err_probe(dev, PTR_ERR(chip->enable_gpio),
  217. "error getting enable gpio\n");
  218. } else {
  219. /*
  220. * PWM control mode: check for brightness level in DT
  221. */
  222. if (device_property_present(dev, "brightness-levels")) {
  223. /* Read brightness levels from DT */
  224. num_levels = device_property_count_u32(dev, "brightness-levels");
  225. if (num_levels < 2)
  226. return -EINVAL;
  227. } else {
  228. /* Use default brightness levels */
  229. num_levels = MP3309C_PWM_DEFAULT_NUM_LEVELS;
  230. }
  231. }
  232. /* Fill brightness levels array */
  233. pdata->levels = devm_kcalloc(dev, num_levels, sizeof(*pdata->levels), GFP_KERNEL);
  234. if (!pdata->levels)
  235. return -ENOMEM;
  236. if (device_property_present(dev, "brightness-levels")) {
  237. ret = device_property_read_u32_array(dev, "brightness-levels",
  238. pdata->levels, num_levels);
  239. if (ret < 0)
  240. return ret;
  241. } else {
  242. for (i = 0; i < num_levels; i++)
  243. pdata->levels[i] = i;
  244. }
  245. pdata->max_brightness = num_levels - 1;
  246. ret = device_property_read_u32(dev, "default-brightness", &pdata->default_brightness);
  247. if (ret)
  248. pdata->default_brightness = pdata->max_brightness;
  249. if (pdata->default_brightness > pdata->max_brightness) {
  250. dev_err_probe(dev, -ERANGE, "default brightness exceeds max brightness\n");
  251. pdata->default_brightness = pdata->max_brightness;
  252. }
  253. /*
  254. * Over-voltage protection (OVP)
  255. *
  256. * This (optional) property values are:
  257. *
  258. * - 13.5V
  259. * - 24V
  260. * - 35.5V (hardware default setting)
  261. *
  262. * If missing, the default value for OVP is 35.5V
  263. */
  264. pdata->over_voltage_protection = REG_I2C_1_OVP1;
  265. ret = device_property_read_u32(dev, "mps,overvoltage-protection-microvolt", &tmp_value);
  266. if (!ret) {
  267. switch (tmp_value) {
  268. case 13500000:
  269. pdata->over_voltage_protection = 0x00;
  270. break;
  271. case 24000000:
  272. pdata->over_voltage_protection = REG_I2C_1_OVP0;
  273. break;
  274. case 35500000:
  275. pdata->over_voltage_protection = REG_I2C_1_OVP1;
  276. break;
  277. default:
  278. return -EINVAL;
  279. }
  280. }
  281. /* Synchronous (default) and non-synchronous mode */
  282. pdata->sync_mode = !device_property_read_bool(dev, "mps,no-sync-mode");
  283. return 0;
  284. }
  285. static int mp3309c_probe(struct i2c_client *client)
  286. {
  287. struct device *dev = &client->dev;
  288. struct mp3309c_platform_data *pdata = dev_get_platdata(dev);
  289. struct mp3309c_chip *chip;
  290. struct backlight_properties props;
  291. struct pwm_state pwmstate;
  292. int ret;
  293. if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
  294. return dev_err_probe(dev, -EOPNOTSUPP, "failed to check i2c functionality\n");
  295. chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
  296. if (!chip)
  297. return -ENOMEM;
  298. chip->dev = dev;
  299. chip->regmap = devm_regmap_init_i2c(client, &mp3309c_regmap);
  300. if (IS_ERR(chip->regmap))
  301. return dev_err_probe(dev, PTR_ERR(chip->regmap),
  302. "failed to allocate register map\n");
  303. i2c_set_clientdata(client, chip);
  304. if (!pdata) {
  305. pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
  306. if (!pdata)
  307. return -ENOMEM;
  308. ret = mp3309c_parse_fwnode(chip, pdata);
  309. if (ret)
  310. return ret;
  311. }
  312. chip->pdata = pdata;
  313. /* Backlight properties */
  314. memset(&props, 0, sizeof(struct backlight_properties));
  315. props.brightness = pdata->default_brightness;
  316. props.max_brightness = pdata->max_brightness;
  317. props.scale = BACKLIGHT_SCALE_LINEAR;
  318. props.type = BACKLIGHT_RAW;
  319. props.power = BACKLIGHT_POWER_ON;
  320. chip->bl = devm_backlight_device_register(dev, "mp3309c", dev, chip,
  321. &mp3309c_bl_ops, &props);
  322. if (IS_ERR(chip->bl))
  323. return dev_err_probe(dev, PTR_ERR(chip->bl),
  324. "error registering backlight device\n");
  325. /* In PWM dimming mode, enable pwm device */
  326. if (chip->pdata->dimming_mode == DIMMING_PWM) {
  327. pwm_init_state(chip->pwmd, &pwmstate);
  328. pwm_set_relative_duty_cycle(&pwmstate,
  329. chip->pdata->default_brightness,
  330. chip->pdata->max_brightness);
  331. pwmstate.enabled = true;
  332. ret = pwm_apply_might_sleep(chip->pwmd, &pwmstate);
  333. if (ret)
  334. return dev_err_probe(dev, ret, "error setting pwm device\n");
  335. }
  336. chip->pdata->status = FIRST_POWER_ON;
  337. backlight_update_status(chip->bl);
  338. return 0;
  339. }
  340. static void mp3309c_remove(struct i2c_client *client)
  341. {
  342. struct mp3309c_chip *chip = i2c_get_clientdata(client);
  343. struct backlight_device *bl = chip->bl;
  344. bl->props.power = BACKLIGHT_POWER_OFF;
  345. bl->props.brightness = 0;
  346. backlight_update_status(chip->bl);
  347. }
  348. static const struct of_device_id mp3309c_match_table[] = {
  349. { .compatible = "mps,mp3309c", },
  350. { },
  351. };
  352. MODULE_DEVICE_TABLE(of, mp3309c_match_table);
  353. static const struct i2c_device_id mp3309c_id[] = {
  354. { "mp3309c" },
  355. { }
  356. };
  357. MODULE_DEVICE_TABLE(i2c, mp3309c_id);
  358. static struct i2c_driver mp3309c_i2c_driver = {
  359. .driver = {
  360. .name = KBUILD_MODNAME,
  361. .of_match_table = mp3309c_match_table,
  362. },
  363. .probe = mp3309c_probe,
  364. .remove = mp3309c_remove,
  365. .id_table = mp3309c_id,
  366. };
  367. module_i2c_driver(mp3309c_i2c_driver);
  368. MODULE_DESCRIPTION("Backlight Driver for MPS MP3309C");
  369. MODULE_AUTHOR("Flavio Suligoi <f.suligoi@asem.it>");
  370. MODULE_LICENSE("GPL");