panel-boe-bf060y8m-aj0.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * BOE BF060Y8M-AJ0 5.99" MIPI-DSI OLED Panel on SW43404 DriverIC
  4. *
  5. * Copyright (c) 2020 AngeloGioacchino Del Regno
  6. * <[email protected]>
  7. */
  8. #include <linux/backlight.h>
  9. #include <linux/delay.h>
  10. #include <linux/gpio/consumer.h>
  11. #include <linux/module.h>
  12. #include <linux/of.h>
  13. #include <linux/regulator/consumer.h>
  14. #include <video/mipi_display.h>
  15. #include <drm/drm_mipi_dsi.h>
  16. #include <drm/drm_modes.h>
  17. #include <drm/drm_panel.h>
  18. #define DCS_ALLOW_HBM_RANGE 0x0c
  19. #define DCS_DISALLOW_HBM_RANGE 0x08
  20. enum boe_bf060y8m_aj0_supplies {
  21. BF060Y8M_VREG_VCC,
  22. BF060Y8M_VREG_VDDIO,
  23. BF060Y8M_VREG_VCI,
  24. BF060Y8M_VREG_EL_VDD,
  25. BF060Y8M_VREG_EL_VSS,
  26. BF060Y8M_VREG_MAX
  27. };
  28. struct boe_bf060y8m_aj0 {
  29. struct drm_panel panel;
  30. struct mipi_dsi_device *dsi;
  31. struct regulator_bulk_data vregs[BF060Y8M_VREG_MAX];
  32. struct gpio_desc *reset_gpio;
  33. bool prepared;
  34. };
  35. static inline
  36. struct boe_bf060y8m_aj0 *to_boe_bf060y8m_aj0(struct drm_panel *panel)
  37. {
  38. return container_of(panel, struct boe_bf060y8m_aj0, panel);
  39. }
  40. #define dsi_dcs_write_seq(dsi, seq...) do { \
  41. static const u8 d[] = { seq }; \
  42. int ret; \
  43. ret = mipi_dsi_dcs_write_buffer(dsi, d, ARRAY_SIZE(d)); \
  44. if (ret < 0) \
  45. return ret; \
  46. } while (0)
  47. static void boe_bf060y8m_aj0_reset(struct boe_bf060y8m_aj0 *boe)
  48. {
  49. gpiod_set_value_cansleep(boe->reset_gpio, 0);
  50. usleep_range(2000, 3000);
  51. gpiod_set_value_cansleep(boe->reset_gpio, 1);
  52. usleep_range(15000, 16000);
  53. gpiod_set_value_cansleep(boe->reset_gpio, 0);
  54. usleep_range(5000, 6000);
  55. }
  56. static int boe_bf060y8m_aj0_on(struct boe_bf060y8m_aj0 *boe)
  57. {
  58. struct mipi_dsi_device *dsi = boe->dsi;
  59. struct device *dev = &dsi->dev;
  60. int ret;
  61. dsi_dcs_write_seq(dsi, 0xb0, 0xa5, 0x00);
  62. dsi_dcs_write_seq(dsi, 0xb2, 0x00, 0x4c);
  63. dsi_dcs_write_seq(dsi, MIPI_DCS_SET_3D_CONTROL, 0x10);
  64. dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_POWER_SAVE, DCS_ALLOW_HBM_RANGE);
  65. dsi_dcs_write_seq(dsi, 0xf8,
  66. 0x00, 0x08, 0x10, 0x00, 0x22, 0x00, 0x00, 0x2d);
  67. ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
  68. if (ret < 0) {
  69. dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
  70. return ret;
  71. }
  72. msleep(30);
  73. dsi_dcs_write_seq(dsi, 0xb0, 0xa5, 0x00);
  74. dsi_dcs_write_seq(dsi, 0xc0,
  75. 0x08, 0x48, 0x65, 0x33, 0x33, 0x33,
  76. 0x2a, 0x31, 0x39, 0x20, 0x09);
  77. dsi_dcs_write_seq(dsi, 0xc1, 0x00, 0x00, 0x00, 0x1f, 0x1f,
  78. 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
  79. 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
  80. dsi_dcs_write_seq(dsi, 0xe2, 0x20, 0x04, 0x10, 0x12, 0x92,
  81. 0x4f, 0x8f, 0x44, 0x84, 0x83, 0x83, 0x83,
  82. 0x5c, 0x5c, 0x5c);
  83. dsi_dcs_write_seq(dsi, 0xde, 0x01, 0x2c, 0x00, 0x77, 0x3e);
  84. msleep(30);
  85. ret = mipi_dsi_dcs_set_display_on(dsi);
  86. if (ret < 0) {
  87. dev_err(dev, "Failed to set display on: %d\n", ret);
  88. return ret;
  89. }
  90. msleep(50);
  91. return 0;
  92. }
  93. static int boe_bf060y8m_aj0_off(struct boe_bf060y8m_aj0 *boe)
  94. {
  95. struct mipi_dsi_device *dsi = boe->dsi;
  96. struct device *dev = &dsi->dev;
  97. int ret;
  98. /* OFF commands sent in HS mode */
  99. dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
  100. ret = mipi_dsi_dcs_set_display_off(dsi);
  101. if (ret < 0) {
  102. dev_err(dev, "Failed to set display off: %d\n", ret);
  103. return ret;
  104. }
  105. msleep(20);
  106. ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
  107. if (ret < 0) {
  108. dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
  109. return ret;
  110. }
  111. usleep_range(1000, 2000);
  112. dsi->mode_flags |= MIPI_DSI_MODE_LPM;
  113. return 0;
  114. }
  115. static int boe_bf060y8m_aj0_prepare(struct drm_panel *panel)
  116. {
  117. struct boe_bf060y8m_aj0 *boe = to_boe_bf060y8m_aj0(panel);
  118. struct device *dev = &boe->dsi->dev;
  119. int ret;
  120. if (boe->prepared)
  121. return 0;
  122. /*
  123. * Enable EL Driving Voltage first - doing that at the beginning
  124. * or at the end of the power sequence doesn't matter, so enable
  125. * it here to avoid yet another usleep at the end.
  126. */
  127. ret = regulator_enable(boe->vregs[BF060Y8M_VREG_EL_VDD].consumer);
  128. if (ret)
  129. return ret;
  130. ret = regulator_enable(boe->vregs[BF060Y8M_VREG_EL_VSS].consumer);
  131. if (ret)
  132. goto err_elvss;
  133. ret = regulator_enable(boe->vregs[BF060Y8M_VREG_VCC].consumer);
  134. if (ret)
  135. goto err_vcc;
  136. usleep_range(1000, 2000);
  137. ret = regulator_enable(boe->vregs[BF060Y8M_VREG_VDDIO].consumer);
  138. if (ret)
  139. goto err_vddio;
  140. usleep_range(500, 1000);
  141. ret = regulator_enable(boe->vregs[BF060Y8M_VREG_VCI].consumer);
  142. if (ret)
  143. goto err_vci;
  144. usleep_range(2000, 3000);
  145. boe_bf060y8m_aj0_reset(boe);
  146. ret = boe_bf060y8m_aj0_on(boe);
  147. if (ret < 0) {
  148. dev_err(dev, "Failed to initialize panel: %d\n", ret);
  149. gpiod_set_value_cansleep(boe->reset_gpio, 1);
  150. return ret;
  151. }
  152. boe->prepared = true;
  153. return 0;
  154. err_vci:
  155. regulator_disable(boe->vregs[BF060Y8M_VREG_VDDIO].consumer);
  156. err_vddio:
  157. regulator_disable(boe->vregs[BF060Y8M_VREG_VCC].consumer);
  158. err_vcc:
  159. regulator_disable(boe->vregs[BF060Y8M_VREG_EL_VSS].consumer);
  160. err_elvss:
  161. regulator_disable(boe->vregs[BF060Y8M_VREG_EL_VDD].consumer);
  162. return ret;
  163. }
  164. static int boe_bf060y8m_aj0_unprepare(struct drm_panel *panel)
  165. {
  166. struct boe_bf060y8m_aj0 *boe = to_boe_bf060y8m_aj0(panel);
  167. struct device *dev = &boe->dsi->dev;
  168. int ret;
  169. if (!boe->prepared)
  170. return 0;
  171. ret = boe_bf060y8m_aj0_off(boe);
  172. if (ret < 0)
  173. dev_err(dev, "Failed to un-initialize panel: %d\n", ret);
  174. gpiod_set_value_cansleep(boe->reset_gpio, 1);
  175. ret = regulator_bulk_disable(ARRAY_SIZE(boe->vregs), boe->vregs);
  176. boe->prepared = false;
  177. return 0;
  178. }
  179. static const struct drm_display_mode boe_bf060y8m_aj0_mode = {
  180. .clock = 165268,
  181. .hdisplay = 1080,
  182. .hsync_start = 1080 + 36,
  183. .hsync_end = 1080 + 36 + 24,
  184. .htotal = 1080 + 36 + 24 + 96,
  185. .vdisplay = 2160,
  186. .vsync_start = 2160 + 16,
  187. .vsync_end = 2160 + 16 + 1,
  188. .vtotal = 2160 + 16 + 1 + 15,
  189. .width_mm = 68, /* 68.04 mm */
  190. .height_mm = 136, /* 136.08 mm */
  191. };
  192. static int boe_bf060y8m_aj0_get_modes(struct drm_panel *panel,
  193. struct drm_connector *connector)
  194. {
  195. struct drm_display_mode *mode;
  196. mode = drm_mode_duplicate(connector->dev, &boe_bf060y8m_aj0_mode);
  197. if (!mode)
  198. return -ENOMEM;
  199. drm_mode_set_name(mode);
  200. mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
  201. connector->display_info.width_mm = mode->width_mm;
  202. connector->display_info.height_mm = mode->height_mm;
  203. drm_mode_probed_add(connector, mode);
  204. return 1;
  205. }
  206. static const struct drm_panel_funcs boe_bf060y8m_aj0_panel_funcs = {
  207. .prepare = boe_bf060y8m_aj0_prepare,
  208. .unprepare = boe_bf060y8m_aj0_unprepare,
  209. .get_modes = boe_bf060y8m_aj0_get_modes,
  210. };
  211. static int boe_bf060y8m_aj0_bl_update_status(struct backlight_device *bl)
  212. {
  213. struct mipi_dsi_device *dsi = bl_get_data(bl);
  214. u16 brightness = backlight_get_brightness(bl);
  215. int ret;
  216. ret = mipi_dsi_dcs_set_display_brightness(dsi, brightness);
  217. if (ret < 0)
  218. return ret;
  219. return 0;
  220. }
  221. static int boe_bf060y8m_aj0_bl_get_brightness(struct backlight_device *bl)
  222. {
  223. struct mipi_dsi_device *dsi = bl_get_data(bl);
  224. u16 brightness;
  225. int ret;
  226. ret = mipi_dsi_dcs_get_display_brightness(dsi, &brightness);
  227. if (ret < 0)
  228. return ret;
  229. return brightness & 0xff;
  230. }
  231. static const struct backlight_ops boe_bf060y8m_aj0_bl_ops = {
  232. .update_status = boe_bf060y8m_aj0_bl_update_status,
  233. .get_brightness = boe_bf060y8m_aj0_bl_get_brightness,
  234. };
  235. static struct backlight_device *
  236. boe_bf060y8m_aj0_create_backlight(struct mipi_dsi_device *dsi)
  237. {
  238. struct device *dev = &dsi->dev;
  239. const struct backlight_properties props = {
  240. .type = BACKLIGHT_RAW,
  241. .brightness = 127,
  242. .max_brightness = 255,
  243. .scale = BACKLIGHT_SCALE_NON_LINEAR,
  244. };
  245. return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
  246. &boe_bf060y8m_aj0_bl_ops, &props);
  247. }
  248. static int boe_bf060y8m_aj0_init_vregs(struct boe_bf060y8m_aj0 *boe,
  249. struct device *dev)
  250. {
  251. struct regulator *vreg;
  252. int ret;
  253. boe->vregs[BF060Y8M_VREG_VCC].supply = "vcc";
  254. boe->vregs[BF060Y8M_VREG_VDDIO].supply = "vddio";
  255. boe->vregs[BF060Y8M_VREG_VCI].supply = "vci";
  256. boe->vregs[BF060Y8M_VREG_EL_VDD].supply = "elvdd";
  257. boe->vregs[BF060Y8M_VREG_EL_VSS].supply = "elvss";
  258. ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(boe->vregs),
  259. boe->vregs);
  260. if (ret < 0) {
  261. dev_err(dev, "Failed to get regulators: %d\n", ret);
  262. return ret;
  263. }
  264. vreg = boe->vregs[BF060Y8M_VREG_VCC].consumer;
  265. ret = regulator_is_supported_voltage(vreg, 2700000, 3600000);
  266. if (!ret)
  267. return ret;
  268. vreg = boe->vregs[BF060Y8M_VREG_VDDIO].consumer;
  269. ret = regulator_is_supported_voltage(vreg, 1620000, 1980000);
  270. if (!ret)
  271. return ret;
  272. vreg = boe->vregs[BF060Y8M_VREG_VCI].consumer;
  273. ret = regulator_is_supported_voltage(vreg, 2600000, 3600000);
  274. if (!ret)
  275. return ret;
  276. vreg = boe->vregs[BF060Y8M_VREG_EL_VDD].consumer;
  277. ret = regulator_is_supported_voltage(vreg, 4400000, 4800000);
  278. if (!ret)
  279. return ret;
  280. /* ELVSS is negative: -5.00V to -1.40V */
  281. vreg = boe->vregs[BF060Y8M_VREG_EL_VSS].consumer;
  282. ret = regulator_is_supported_voltage(vreg, 1400000, 5000000);
  283. if (!ret)
  284. return ret;
  285. /*
  286. * Set min/max rated current, known only for VCI and VDDIO and,
  287. * in case of failure, just go on gracefully, as this step is not
  288. * guaranteed to succeed on all regulator HW but do a debug print
  289. * to inform the developer during debugging.
  290. * In any case, these two supplies are also optional, so they may
  291. * be fixed-regulator which, at the time of writing, does not
  292. * support fake current limiting.
  293. */
  294. vreg = boe->vregs[BF060Y8M_VREG_VDDIO].consumer;
  295. ret = regulator_set_current_limit(vreg, 1500, 2500);
  296. if (ret)
  297. dev_dbg(dev, "Current limit cannot be set on %s: %d\n",
  298. boe->vregs[1].supply, ret);
  299. vreg = boe->vregs[BF060Y8M_VREG_VCI].consumer;
  300. ret = regulator_set_current_limit(vreg, 20000, 40000);
  301. if (ret)
  302. dev_dbg(dev, "Current limit cannot be set on %s: %d\n",
  303. boe->vregs[2].supply, ret);
  304. return 0;
  305. }
  306. static int boe_bf060y8m_aj0_probe(struct mipi_dsi_device *dsi)
  307. {
  308. struct device *dev = &dsi->dev;
  309. struct boe_bf060y8m_aj0 *boe;
  310. int ret;
  311. boe = devm_kzalloc(dev, sizeof(*boe), GFP_KERNEL);
  312. if (!boe)
  313. return -ENOMEM;
  314. ret = boe_bf060y8m_aj0_init_vregs(boe, dev);
  315. if (ret)
  316. return dev_err_probe(dev, ret,
  317. "Failed to initialize supplies.\n");
  318. boe->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS);
  319. if (IS_ERR(boe->reset_gpio))
  320. return dev_err_probe(dev, PTR_ERR(boe->reset_gpio),
  321. "Failed to get reset-gpios\n");
  322. boe->dsi = dsi;
  323. mipi_dsi_set_drvdata(dsi, boe);
  324. dsi->lanes = 4;
  325. dsi->format = MIPI_DSI_FMT_RGB888;
  326. dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_NO_EOT_PACKET |
  327. MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
  328. MIPI_DSI_CLOCK_NON_CONTINUOUS |
  329. MIPI_DSI_MODE_LPM;
  330. drm_panel_init(&boe->panel, dev, &boe_bf060y8m_aj0_panel_funcs,
  331. DRM_MODE_CONNECTOR_DSI);
  332. boe->panel.backlight = boe_bf060y8m_aj0_create_backlight(dsi);
  333. if (IS_ERR(boe->panel.backlight))
  334. return dev_err_probe(dev, PTR_ERR(boe->panel.backlight),
  335. "Failed to create backlight\n");
  336. drm_panel_add(&boe->panel);
  337. ret = mipi_dsi_attach(dsi);
  338. if (ret < 0) {
  339. dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
  340. return ret;
  341. }
  342. return 0;
  343. }
  344. static void boe_bf060y8m_aj0_remove(struct mipi_dsi_device *dsi)
  345. {
  346. struct boe_bf060y8m_aj0 *boe = mipi_dsi_get_drvdata(dsi);
  347. int ret;
  348. ret = mipi_dsi_detach(dsi);
  349. if (ret < 0)
  350. dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
  351. drm_panel_remove(&boe->panel);
  352. }
  353. static const struct of_device_id boe_bf060y8m_aj0_of_match[] = {
  354. { .compatible = "boe,bf060y8m-aj0" },
  355. { /* sentinel */ }
  356. };
  357. MODULE_DEVICE_TABLE(of, boe_bf060y8m_aj0_of_match);
  358. static struct mipi_dsi_driver boe_bf060y8m_aj0_driver = {
  359. .probe = boe_bf060y8m_aj0_probe,
  360. .remove = boe_bf060y8m_aj0_remove,
  361. .driver = {
  362. .name = "panel-sw43404-boe-fhd-amoled",
  363. .of_match_table = boe_bf060y8m_aj0_of_match,
  364. },
  365. };
  366. module_mipi_dsi_driver(boe_bf060y8m_aj0_driver);
  367. MODULE_AUTHOR("AngeloGioacchino Del Regno <[email protected]>");
  368. MODULE_DESCRIPTION("BOE BF060Y8M-AJ0 MIPI-DSI OLED panel");
  369. MODULE_LICENSE("GPL v2");