panel-feixin-k101-im2ba02.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Copyright (C) 2019-2020 Icenowy Zheng <icenowy@aosc.io>
  4. */
  5. #include <linux/gpio/consumer.h>
  6. #include <linux/delay.h>
  7. #include <linux/mod_devicetable.h>
  8. #include <linux/module.h>
  9. #include <linux/of_device.h>
  10. #include <linux/regulator/consumer.h>
  11. #include <drm/drm_mipi_dsi.h>
  12. #include <drm/drm_modes.h>
  13. #include <drm/drm_panel.h>
  14. #define K101_IM2BA02_INIT_CMD_LEN 2
  15. static const char * const regulator_names[] = {
  16. "dvdd",
  17. "avdd",
  18. "cvdd"
  19. };
  20. struct k101_im2ba02 {
  21. struct drm_panel panel;
  22. struct mipi_dsi_device *dsi;
  23. struct regulator_bulk_data supplies[ARRAY_SIZE(regulator_names)];
  24. struct gpio_desc *reset;
  25. };
  26. static inline struct k101_im2ba02 *panel_to_k101_im2ba02(struct drm_panel *panel)
  27. {
  28. return container_of(panel, struct k101_im2ba02, panel);
  29. }
  30. struct k101_im2ba02_init_cmd {
  31. u8 data[K101_IM2BA02_INIT_CMD_LEN];
  32. };
  33. static const struct k101_im2ba02_init_cmd k101_im2ba02_init_cmds[] = {
  34. /* Switch to page 0 */
  35. { .data = { 0xE0, 0x00 } },
  36. /* Seems to be some password */
  37. { .data = { 0xE1, 0x93} },
  38. { .data = { 0xE2, 0x65 } },
  39. { .data = { 0xE3, 0xF8 } },
  40. /* Lane number, 0x02 - 3 lanes, 0x03 - 4 lanes */
  41. { .data = { 0x80, 0x03 } },
  42. /* Sequence control */
  43. { .data = { 0x70, 0x02 } },
  44. { .data = { 0x71, 0x23 } },
  45. { .data = { 0x72, 0x06 } },
  46. /* Switch to page 1 */
  47. { .data = { 0xE0, 0x01 } },
  48. /* Set VCOM */
  49. { .data = { 0x00, 0x00 } },
  50. { .data = { 0x01, 0x66 } },
  51. /* Set VCOM_Reverse */
  52. { .data = { 0x03, 0x00 } },
  53. { .data = { 0x04, 0x25 } },
  54. /* Set Gamma Power, VG[MS][PN] */
  55. { .data = { 0x17, 0x00 } },
  56. { .data = { 0x18, 0x6D } },
  57. { .data = { 0x19, 0x00 } },
  58. { .data = { 0x1A, 0x00 } },
  59. { .data = { 0x1B, 0xBF } }, /* VGMN = -4.5V */
  60. { .data = { 0x1C, 0x00 } },
  61. /* Set Gate Power */
  62. { .data = { 0x1F, 0x3E } }, /* VGH_R = 15V */
  63. { .data = { 0x20, 0x28 } }, /* VGL_R = -11V */
  64. { .data = { 0x21, 0x28 } }, /* VGL_R2 = -11V */
  65. { .data = { 0x22, 0x0E } }, /* PA[6:4] = 0, PA[0] = 0 */
  66. /* Set Panel */
  67. { .data = { 0x37, 0x09 } }, /* SS = 1, BGR = 1 */
  68. /* Set RGBCYC */
  69. { .data = { 0x38, 0x04 } }, /* JDT = 100 column inversion */
  70. { .data = { 0x39, 0x08 } }, /* RGB_N_EQ1 */
  71. { .data = { 0x3A, 0x12 } }, /* RGB_N_EQ2 */
  72. { .data = { 0x3C, 0x78 } }, /* set EQ3 for TE_H */
  73. { .data = { 0x3D, 0xFF } }, /* set CHGEN_ON */
  74. { .data = { 0x3E, 0xFF } }, /* set CHGEN_OFF */
  75. { .data = { 0x3F, 0x7F } }, /* set CHGEN_OFF2 */
  76. /* Set TCON parameter */
  77. { .data = { 0x40, 0x06 } }, /* RSO = 800 points */
  78. { .data = { 0x41, 0xA0 } }, /* LN = 1280 lines */
  79. /* Set power voltage */
  80. { .data = { 0x55, 0x0F } }, /* DCDCM */
  81. { .data = { 0x56, 0x01 } },
  82. { .data = { 0x57, 0x69 } },
  83. { .data = { 0x58, 0x0A } },
  84. { .data = { 0x59, 0x0A } },
  85. { .data = { 0x5A, 0x45 } },
  86. { .data = { 0x5B, 0x15 } },
  87. /* Set gamma */
  88. { .data = { 0x5D, 0x7C } },
  89. { .data = { 0x5E, 0x65 } },
  90. { .data = { 0x5F, 0x55 } },
  91. { .data = { 0x60, 0x49 } },
  92. { .data = { 0x61, 0x44 } },
  93. { .data = { 0x62, 0x35 } },
  94. { .data = { 0x63, 0x3A } },
  95. { .data = { 0x64, 0x23 } },
  96. { .data = { 0x65, 0x3D } },
  97. { .data = { 0x66, 0x3C } },
  98. { .data = { 0x67, 0x3D } },
  99. { .data = { 0x68, 0x5D } },
  100. { .data = { 0x69, 0x4D } },
  101. { .data = { 0x6A, 0x56 } },
  102. { .data = { 0x6B, 0x48 } },
  103. { .data = { 0x6C, 0x45 } },
  104. { .data = { 0x6D, 0x38 } },
  105. { .data = { 0x6E, 0x25 } },
  106. { .data = { 0x6F, 0x00 } },
  107. { .data = { 0x70, 0x7C } },
  108. { .data = { 0x71, 0x65 } },
  109. { .data = { 0x72, 0x55 } },
  110. { .data = { 0x73, 0x49 } },
  111. { .data = { 0x74, 0x44 } },
  112. { .data = { 0x75, 0x35 } },
  113. { .data = { 0x76, 0x3A } },
  114. { .data = { 0x77, 0x23 } },
  115. { .data = { 0x78, 0x3D } },
  116. { .data = { 0x79, 0x3C } },
  117. { .data = { 0x7A, 0x3D } },
  118. { .data = { 0x7B, 0x5D } },
  119. { .data = { 0x7C, 0x4D } },
  120. { .data = { 0x7D, 0x56 } },
  121. { .data = { 0x7E, 0x48 } },
  122. { .data = { 0x7F, 0x45 } },
  123. { .data = { 0x80, 0x38 } },
  124. { .data = { 0x81, 0x25 } },
  125. { .data = { 0x82, 0x00 } },
  126. /* Switch to page 2, for GIP */
  127. { .data = { 0xE0, 0x02 } },
  128. { .data = { 0x00, 0x1E } },
  129. { .data = { 0x01, 0x1E } },
  130. { .data = { 0x02, 0x41 } },
  131. { .data = { 0x03, 0x41 } },
  132. { .data = { 0x04, 0x43 } },
  133. { .data = { 0x05, 0x43 } },
  134. { .data = { 0x06, 0x1F } },
  135. { .data = { 0x07, 0x1F } },
  136. { .data = { 0x08, 0x1F } },
  137. { .data = { 0x09, 0x1F } },
  138. { .data = { 0x0A, 0x1E } },
  139. { .data = { 0x0B, 0x1E } },
  140. { .data = { 0x0C, 0x1F } },
  141. { .data = { 0x0D, 0x47 } },
  142. { .data = { 0x0E, 0x47 } },
  143. { .data = { 0x0F, 0x45 } },
  144. { .data = { 0x10, 0x45 } },
  145. { .data = { 0x11, 0x4B } },
  146. { .data = { 0x12, 0x4B } },
  147. { .data = { 0x13, 0x49 } },
  148. { .data = { 0x14, 0x49 } },
  149. { .data = { 0x15, 0x1F } },
  150. { .data = { 0x16, 0x1E } },
  151. { .data = { 0x17, 0x1E } },
  152. { .data = { 0x18, 0x40 } },
  153. { .data = { 0x19, 0x40 } },
  154. { .data = { 0x1A, 0x42 } },
  155. { .data = { 0x1B, 0x42 } },
  156. { .data = { 0x1C, 0x1F } },
  157. { .data = { 0x1D, 0x1F } },
  158. { .data = { 0x1E, 0x1F } },
  159. { .data = { 0x1F, 0x1f } },
  160. { .data = { 0x20, 0x1E } },
  161. { .data = { 0x21, 0x1E } },
  162. { .data = { 0x22, 0x1f } },
  163. { .data = { 0x23, 0x46 } },
  164. { .data = { 0x24, 0x46 } },
  165. { .data = { 0x25, 0x44 } },
  166. { .data = { 0x26, 0x44 } },
  167. { .data = { 0x27, 0x4A } },
  168. { .data = { 0x28, 0x4A } },
  169. { .data = { 0x29, 0x48 } },
  170. { .data = { 0x2A, 0x48 } },
  171. { .data = { 0x2B, 0x1f } },
  172. { .data = { 0x2C, 0x1F } },
  173. { .data = { 0x2D, 0x1F } },
  174. { .data = { 0x2E, 0x42 } },
  175. { .data = { 0x2F, 0x42 } },
  176. { .data = { 0x30, 0x40 } },
  177. { .data = { 0x31, 0x40 } },
  178. { .data = { 0x32, 0x1E } },
  179. { .data = { 0x33, 0x1E } },
  180. { .data = { 0x34, 0x1F } },
  181. { .data = { 0x35, 0x1F } },
  182. { .data = { 0x36, 0x1E } },
  183. { .data = { 0x37, 0x1E } },
  184. { .data = { 0x38, 0x1F } },
  185. { .data = { 0x39, 0x48 } },
  186. { .data = { 0x3A, 0x48 } },
  187. { .data = { 0x3B, 0x4A } },
  188. { .data = { 0x3C, 0x4A } },
  189. { .data = { 0x3D, 0x44 } },
  190. { .data = { 0x3E, 0x44 } },
  191. { .data = { 0x3F, 0x46 } },
  192. { .data = { 0x40, 0x46 } },
  193. { .data = { 0x41, 0x1F } },
  194. { .data = { 0x42, 0x1F } },
  195. { .data = { 0x43, 0x1F } },
  196. { .data = { 0x44, 0x43 } },
  197. { .data = { 0x45, 0x43 } },
  198. { .data = { 0x46, 0x41 } },
  199. { .data = { 0x47, 0x41 } },
  200. { .data = { 0x48, 0x1E } },
  201. { .data = { 0x49, 0x1E } },
  202. { .data = { 0x4A, 0x1E } },
  203. { .data = { 0x4B, 0x1F } },
  204. { .data = { 0x4C, 0x1E } },
  205. { .data = { 0x4D, 0x1E } },
  206. { .data = { 0x4E, 0x1F } },
  207. { .data = { 0x4F, 0x49 } },
  208. { .data = { 0x50, 0x49 } },
  209. { .data = { 0x51, 0x4B } },
  210. { .data = { 0x52, 0x4B } },
  211. { .data = { 0x53, 0x45 } },
  212. { .data = { 0x54, 0x45 } },
  213. { .data = { 0x55, 0x47 } },
  214. { .data = { 0x56, 0x47 } },
  215. { .data = { 0x57, 0x1F } },
  216. { .data = { 0x58, 0x10 } },
  217. { .data = { 0x59, 0x00 } },
  218. { .data = { 0x5A, 0x00 } },
  219. { .data = { 0x5B, 0x30 } },
  220. { .data = { 0x5C, 0x02 } },
  221. { .data = { 0x5D, 0x40 } },
  222. { .data = { 0x5E, 0x01 } },
  223. { .data = { 0x5F, 0x02 } },
  224. { .data = { 0x60, 0x30 } },
  225. { .data = { 0x61, 0x01 } },
  226. { .data = { 0x62, 0x02 } },
  227. { .data = { 0x63, 0x6A } },
  228. { .data = { 0x64, 0x6A } },
  229. { .data = { 0x65, 0x05 } },
  230. { .data = { 0x66, 0x12 } },
  231. { .data = { 0x67, 0x74 } },
  232. { .data = { 0x68, 0x04 } },
  233. { .data = { 0x69, 0x6A } },
  234. { .data = { 0x6A, 0x6A } },
  235. { .data = { 0x6B, 0x08 } },
  236. { .data = { 0x6C, 0x00 } },
  237. { .data = { 0x6D, 0x04 } },
  238. { .data = { 0x6E, 0x04 } },
  239. { .data = { 0x6F, 0x88 } },
  240. { .data = { 0x70, 0x00 } },
  241. { .data = { 0x71, 0x00 } },
  242. { .data = { 0x72, 0x06 } },
  243. { .data = { 0x73, 0x7B } },
  244. { .data = { 0x74, 0x00 } },
  245. { .data = { 0x75, 0x07 } },
  246. { .data = { 0x76, 0x00 } },
  247. { .data = { 0x77, 0x5D } },
  248. { .data = { 0x78, 0x17 } },
  249. { .data = { 0x79, 0x1F } },
  250. { .data = { 0x7A, 0x00 } },
  251. { .data = { 0x7B, 0x00 } },
  252. { .data = { 0x7C, 0x00 } },
  253. { .data = { 0x7D, 0x03 } },
  254. { .data = { 0x7E, 0x7B } },
  255. { .data = { 0xE0, 0x04 } },
  256. { .data = { 0x2B, 0x2B } },
  257. { .data = { 0x2E, 0x44 } },
  258. { .data = { 0xE0, 0x01 } },
  259. { .data = { 0x0E, 0x01 } },
  260. { .data = { 0xE0, 0x03 } },
  261. { .data = { 0x98, 0x2F } },
  262. { .data = { 0xE0, 0x00 } },
  263. { .data = { 0xE6, 0x02 } },
  264. { .data = { 0xE7, 0x02 } },
  265. { .data = { 0x11, 0x00 } },
  266. };
  267. static const struct k101_im2ba02_init_cmd timed_cmds[] = {
  268. { .data = { 0x29, 0x00 } },
  269. { .data = { 0x35, 0x00 } },
  270. };
  271. static int k101_im2ba02_prepare(struct drm_panel *panel)
  272. {
  273. struct k101_im2ba02 *ctx = panel_to_k101_im2ba02(panel);
  274. struct mipi_dsi_device *dsi = ctx->dsi;
  275. unsigned int i;
  276. int ret;
  277. ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
  278. if (ret)
  279. return ret;
  280. msleep(30);
  281. gpiod_set_value(ctx->reset, 1);
  282. msleep(50);
  283. gpiod_set_value(ctx->reset, 0);
  284. msleep(50);
  285. gpiod_set_value(ctx->reset, 1);
  286. msleep(200);
  287. for (i = 0; i < ARRAY_SIZE(k101_im2ba02_init_cmds); i++) {
  288. const struct k101_im2ba02_init_cmd *cmd = &k101_im2ba02_init_cmds[i];
  289. ret = mipi_dsi_dcs_write_buffer(dsi, cmd->data, K101_IM2BA02_INIT_CMD_LEN);
  290. if (ret < 0)
  291. goto powerdown;
  292. }
  293. return 0;
  294. powerdown:
  295. gpiod_set_value(ctx->reset, 0);
  296. msleep(50);
  297. return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
  298. }
  299. static int k101_im2ba02_enable(struct drm_panel *panel)
  300. {
  301. struct k101_im2ba02 *ctx = panel_to_k101_im2ba02(panel);
  302. const struct k101_im2ba02_init_cmd *cmd = &timed_cmds[1];
  303. int ret;
  304. msleep(150);
  305. ret = mipi_dsi_dcs_set_display_on(ctx->dsi);
  306. if (ret < 0)
  307. return ret;
  308. msleep(50);
  309. return mipi_dsi_dcs_write_buffer(ctx->dsi, cmd->data, K101_IM2BA02_INIT_CMD_LEN);
  310. }
  311. static int k101_im2ba02_disable(struct drm_panel *panel)
  312. {
  313. struct k101_im2ba02 *ctx = panel_to_k101_im2ba02(panel);
  314. return mipi_dsi_dcs_set_display_off(ctx->dsi);
  315. }
  316. static int k101_im2ba02_unprepare(struct drm_panel *panel)
  317. {
  318. struct k101_im2ba02 *ctx = panel_to_k101_im2ba02(panel);
  319. int ret;
  320. ret = mipi_dsi_dcs_set_display_off(ctx->dsi);
  321. if (ret < 0)
  322. dev_err(panel->dev, "failed to set display off: %d\n", ret);
  323. ret = mipi_dsi_dcs_enter_sleep_mode(ctx->dsi);
  324. if (ret < 0)
  325. dev_err(panel->dev, "failed to enter sleep mode: %d\n", ret);
  326. msleep(200);
  327. gpiod_set_value(ctx->reset, 0);
  328. msleep(20);
  329. return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
  330. }
  331. static const struct drm_display_mode k101_im2ba02_default_mode = {
  332. .clock = 70000,
  333. .hdisplay = 800,
  334. .hsync_start = 800 + 20,
  335. .hsync_end = 800 + 20 + 20,
  336. .htotal = 800 + 20 + 20 + 20,
  337. .vdisplay = 1280,
  338. .vsync_start = 1280 + 16,
  339. .vsync_end = 1280 + 16 + 4,
  340. .vtotal = 1280 + 16 + 4 + 4,
  341. .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
  342. .width_mm = 136,
  343. .height_mm = 217,
  344. };
  345. static int k101_im2ba02_get_modes(struct drm_panel *panel,
  346. struct drm_connector *connector)
  347. {
  348. struct k101_im2ba02 *ctx = panel_to_k101_im2ba02(panel);
  349. struct drm_display_mode *mode;
  350. mode = drm_mode_duplicate(connector->dev, &k101_im2ba02_default_mode);
  351. if (!mode) {
  352. dev_err(&ctx->dsi->dev, "failed to add mode %ux%u@%u\n",
  353. k101_im2ba02_default_mode.hdisplay,
  354. k101_im2ba02_default_mode.vdisplay,
  355. drm_mode_vrefresh(&k101_im2ba02_default_mode));
  356. return -ENOMEM;
  357. }
  358. drm_mode_set_name(mode);
  359. mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
  360. connector->display_info.width_mm = mode->width_mm;
  361. connector->display_info.height_mm = mode->height_mm;
  362. drm_mode_probed_add(connector, mode);
  363. return 1;
  364. }
  365. static const struct drm_panel_funcs k101_im2ba02_funcs = {
  366. .disable = k101_im2ba02_disable,
  367. .unprepare = k101_im2ba02_unprepare,
  368. .prepare = k101_im2ba02_prepare,
  369. .enable = k101_im2ba02_enable,
  370. .get_modes = k101_im2ba02_get_modes,
  371. };
  372. static int k101_im2ba02_dsi_probe(struct mipi_dsi_device *dsi)
  373. {
  374. struct k101_im2ba02 *ctx;
  375. unsigned int i;
  376. int ret;
  377. ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL);
  378. if (!ctx)
  379. return -ENOMEM;
  380. mipi_dsi_set_drvdata(dsi, ctx);
  381. ctx->dsi = dsi;
  382. for (i = 0; i < ARRAY_SIZE(ctx->supplies); i++)
  383. ctx->supplies[i].supply = regulator_names[i];
  384. ret = devm_regulator_bulk_get(&dsi->dev, ARRAY_SIZE(ctx->supplies),
  385. ctx->supplies);
  386. if (ret < 0) {
  387. dev_err(&dsi->dev, "Couldn't get regulators\n");
  388. return ret;
  389. }
  390. ctx->reset = devm_gpiod_get(&dsi->dev, "reset", GPIOD_OUT_LOW);
  391. if (IS_ERR(ctx->reset)) {
  392. dev_err(&dsi->dev, "Couldn't get our reset GPIO\n");
  393. return PTR_ERR(ctx->reset);
  394. }
  395. drm_panel_init(&ctx->panel, &dsi->dev, &k101_im2ba02_funcs,
  396. DRM_MODE_CONNECTOR_DSI);
  397. ret = drm_panel_of_backlight(&ctx->panel);
  398. if (ret)
  399. return ret;
  400. drm_panel_add(&ctx->panel);
  401. dsi->mode_flags = MIPI_DSI_MODE_VIDEO;
  402. dsi->format = MIPI_DSI_FMT_RGB888;
  403. dsi->lanes = 4;
  404. ret = mipi_dsi_attach(dsi);
  405. if (ret < 0) {
  406. drm_panel_remove(&ctx->panel);
  407. return ret;
  408. }
  409. return 0;
  410. }
  411. static int k101_im2ba02_dsi_remove(struct mipi_dsi_device *dsi)
  412. {
  413. struct k101_im2ba02 *ctx = mipi_dsi_get_drvdata(dsi);
  414. mipi_dsi_detach(dsi);
  415. drm_panel_remove(&ctx->panel);
  416. return 0;
  417. }
  418. static const struct of_device_id k101_im2ba02_of_match[] = {
  419. { .compatible = "feixin,k101-im2ba02", },
  420. { /* sentinel */ }
  421. };
  422. MODULE_DEVICE_TABLE(of, k101_im2ba02_of_match);
  423. static struct mipi_dsi_driver k101_im2ba02_driver = {
  424. .probe = k101_im2ba02_dsi_probe,
  425. .remove = k101_im2ba02_dsi_remove,
  426. .driver = {
  427. .name = "feixin-k101-im2ba02",
  428. .of_match_table = k101_im2ba02_of_match,
  429. },
  430. };
  431. module_mipi_dsi_driver(k101_im2ba02_driver);
  432. MODULE_AUTHOR("Icenowy Zheng <icenowy@aosc.io>");
  433. MODULE_DESCRIPTION("Feixin K101 IM2BA02 MIPI-DSI LCD panel");
  434. MODULE_LICENSE("GPL");