panel-ilitek-ili9881c.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Copyright (C) 2017-2018, Bootlin
  4. */
  5. #include <linux/backlight.h>
  6. #include <linux/delay.h>
  7. #include <linux/device.h>
  8. #include <linux/err.h>
  9. #include <linux/errno.h>
  10. #include <linux/fb.h>
  11. #include <linux/kernel.h>
  12. #include <linux/module.h>
  13. #include <linux/gpio/consumer.h>
  14. #include <linux/regulator/consumer.h>
  15. #include <drm/drm_mipi_dsi.h>
  16. #include <drm/drm_modes.h>
  17. #include <drm/drm_panel.h>
  18. #include <video/mipi_display.h>
  19. struct ili9881c {
  20. struct drm_panel panel;
  21. struct mipi_dsi_device *dsi;
  22. struct backlight_device *backlight;
  23. struct regulator *power;
  24. struct gpio_desc *reset;
  25. };
  26. enum ili9881c_op {
  27. ILI9881C_SWITCH_PAGE,
  28. ILI9881C_COMMAND,
  29. };
  30. struct ili9881c_instr {
  31. enum ili9881c_op op;
  32. union arg {
  33. struct cmd {
  34. u8 cmd;
  35. u8 data;
  36. } cmd;
  37. u8 page;
  38. } arg;
  39. };
  40. #define ILI9881C_SWITCH_PAGE_INSTR(_page) \
  41. { \
  42. .op = ILI9881C_SWITCH_PAGE, \
  43. .arg = { \
  44. .page = (_page), \
  45. }, \
  46. }
  47. #define ILI9881C_COMMAND_INSTR(_cmd, _data) \
  48. { \
  49. .op = ILI9881C_COMMAND, \
  50. .arg = { \
  51. .cmd = { \
  52. .cmd = (_cmd), \
  53. .data = (_data), \
  54. }, \
  55. }, \
  56. }
  57. static const struct ili9881c_instr ili9881c_init[] = {
  58. ILI9881C_SWITCH_PAGE_INSTR(3),
  59. ILI9881C_COMMAND_INSTR(0x01, 0x00),
  60. ILI9881C_COMMAND_INSTR(0x02, 0x00),
  61. ILI9881C_COMMAND_INSTR(0x03, 0x73),
  62. ILI9881C_COMMAND_INSTR(0x04, 0x03),
  63. ILI9881C_COMMAND_INSTR(0x05, 0x00),
  64. ILI9881C_COMMAND_INSTR(0x06, 0x06),
  65. ILI9881C_COMMAND_INSTR(0x07, 0x06),
  66. ILI9881C_COMMAND_INSTR(0x08, 0x00),
  67. ILI9881C_COMMAND_INSTR(0x09, 0x18),
  68. ILI9881C_COMMAND_INSTR(0x0a, 0x04),
  69. ILI9881C_COMMAND_INSTR(0x0b, 0x00),
  70. ILI9881C_COMMAND_INSTR(0x0c, 0x02),
  71. ILI9881C_COMMAND_INSTR(0x0d, 0x03),
  72. ILI9881C_COMMAND_INSTR(0x0e, 0x00),
  73. ILI9881C_COMMAND_INSTR(0x0f, 0x25),
  74. ILI9881C_COMMAND_INSTR(0x10, 0x25),
  75. ILI9881C_COMMAND_INSTR(0x11, 0x00),
  76. ILI9881C_COMMAND_INSTR(0x12, 0x00),
  77. ILI9881C_COMMAND_INSTR(0x13, 0x00),
  78. ILI9881C_COMMAND_INSTR(0x14, 0x00),
  79. ILI9881C_COMMAND_INSTR(0x15, 0x00),
  80. ILI9881C_COMMAND_INSTR(0x16, 0x0C),
  81. ILI9881C_COMMAND_INSTR(0x17, 0x00),
  82. ILI9881C_COMMAND_INSTR(0x18, 0x00),
  83. ILI9881C_COMMAND_INSTR(0x19, 0x00),
  84. ILI9881C_COMMAND_INSTR(0x1a, 0x00),
  85. ILI9881C_COMMAND_INSTR(0x1b, 0x00),
  86. ILI9881C_COMMAND_INSTR(0x1c, 0x00),
  87. ILI9881C_COMMAND_INSTR(0x1d, 0x00),
  88. ILI9881C_COMMAND_INSTR(0x1e, 0xC0),
  89. ILI9881C_COMMAND_INSTR(0x1f, 0x80),
  90. ILI9881C_COMMAND_INSTR(0x20, 0x04),
  91. ILI9881C_COMMAND_INSTR(0x21, 0x01),
  92. ILI9881C_COMMAND_INSTR(0x22, 0x00),
  93. ILI9881C_COMMAND_INSTR(0x23, 0x00),
  94. ILI9881C_COMMAND_INSTR(0x24, 0x00),
  95. ILI9881C_COMMAND_INSTR(0x25, 0x00),
  96. ILI9881C_COMMAND_INSTR(0x26, 0x00),
  97. ILI9881C_COMMAND_INSTR(0x27, 0x00),
  98. ILI9881C_COMMAND_INSTR(0x28, 0x33),
  99. ILI9881C_COMMAND_INSTR(0x29, 0x03),
  100. ILI9881C_COMMAND_INSTR(0x2a, 0x00),
  101. ILI9881C_COMMAND_INSTR(0x2b, 0x00),
  102. ILI9881C_COMMAND_INSTR(0x2c, 0x00),
  103. ILI9881C_COMMAND_INSTR(0x2d, 0x00),
  104. ILI9881C_COMMAND_INSTR(0x2e, 0x00),
  105. ILI9881C_COMMAND_INSTR(0x2f, 0x00),
  106. ILI9881C_COMMAND_INSTR(0x30, 0x00),
  107. ILI9881C_COMMAND_INSTR(0x31, 0x00),
  108. ILI9881C_COMMAND_INSTR(0x32, 0x00),
  109. ILI9881C_COMMAND_INSTR(0x33, 0x00),
  110. ILI9881C_COMMAND_INSTR(0x34, 0x04),
  111. ILI9881C_COMMAND_INSTR(0x35, 0x00),
  112. ILI9881C_COMMAND_INSTR(0x36, 0x00),
  113. ILI9881C_COMMAND_INSTR(0x37, 0x00),
  114. ILI9881C_COMMAND_INSTR(0x38, 0x3C),
  115. ILI9881C_COMMAND_INSTR(0x39, 0x00),
  116. ILI9881C_COMMAND_INSTR(0x3a, 0x00),
  117. ILI9881C_COMMAND_INSTR(0x3b, 0x00),
  118. ILI9881C_COMMAND_INSTR(0x3c, 0x00),
  119. ILI9881C_COMMAND_INSTR(0x3d, 0x00),
  120. ILI9881C_COMMAND_INSTR(0x3e, 0x00),
  121. ILI9881C_COMMAND_INSTR(0x3f, 0x00),
  122. ILI9881C_COMMAND_INSTR(0x40, 0x00),
  123. ILI9881C_COMMAND_INSTR(0x41, 0x00),
  124. ILI9881C_COMMAND_INSTR(0x42, 0x00),
  125. ILI9881C_COMMAND_INSTR(0x43, 0x00),
  126. ILI9881C_COMMAND_INSTR(0x44, 0x00),
  127. ILI9881C_COMMAND_INSTR(0x50, 0x01),
  128. ILI9881C_COMMAND_INSTR(0x51, 0x23),
  129. ILI9881C_COMMAND_INSTR(0x52, 0x45),
  130. ILI9881C_COMMAND_INSTR(0x53, 0x67),
  131. ILI9881C_COMMAND_INSTR(0x54, 0x89),
  132. ILI9881C_COMMAND_INSTR(0x55, 0xab),
  133. ILI9881C_COMMAND_INSTR(0x56, 0x01),
  134. ILI9881C_COMMAND_INSTR(0x57, 0x23),
  135. ILI9881C_COMMAND_INSTR(0x58, 0x45),
  136. ILI9881C_COMMAND_INSTR(0x59, 0x67),
  137. ILI9881C_COMMAND_INSTR(0x5a, 0x89),
  138. ILI9881C_COMMAND_INSTR(0x5b, 0xab),
  139. ILI9881C_COMMAND_INSTR(0x5c, 0xcd),
  140. ILI9881C_COMMAND_INSTR(0x5d, 0xef),
  141. ILI9881C_COMMAND_INSTR(0x5e, 0x11),
  142. ILI9881C_COMMAND_INSTR(0x5f, 0x02),
  143. ILI9881C_COMMAND_INSTR(0x60, 0x02),
  144. ILI9881C_COMMAND_INSTR(0x61, 0x02),
  145. ILI9881C_COMMAND_INSTR(0x62, 0x02),
  146. ILI9881C_COMMAND_INSTR(0x63, 0x02),
  147. ILI9881C_COMMAND_INSTR(0x64, 0x02),
  148. ILI9881C_COMMAND_INSTR(0x65, 0x02),
  149. ILI9881C_COMMAND_INSTR(0x66, 0x02),
  150. ILI9881C_COMMAND_INSTR(0x67, 0x02),
  151. ILI9881C_COMMAND_INSTR(0x68, 0x02),
  152. ILI9881C_COMMAND_INSTR(0x69, 0x02),
  153. ILI9881C_COMMAND_INSTR(0x6a, 0x0C),
  154. ILI9881C_COMMAND_INSTR(0x6b, 0x02),
  155. ILI9881C_COMMAND_INSTR(0x6c, 0x0F),
  156. ILI9881C_COMMAND_INSTR(0x6d, 0x0E),
  157. ILI9881C_COMMAND_INSTR(0x6e, 0x0D),
  158. ILI9881C_COMMAND_INSTR(0x6f, 0x06),
  159. ILI9881C_COMMAND_INSTR(0x70, 0x07),
  160. ILI9881C_COMMAND_INSTR(0x71, 0x02),
  161. ILI9881C_COMMAND_INSTR(0x72, 0x02),
  162. ILI9881C_COMMAND_INSTR(0x73, 0x02),
  163. ILI9881C_COMMAND_INSTR(0x74, 0x02),
  164. ILI9881C_COMMAND_INSTR(0x75, 0x02),
  165. ILI9881C_COMMAND_INSTR(0x76, 0x02),
  166. ILI9881C_COMMAND_INSTR(0x77, 0x02),
  167. ILI9881C_COMMAND_INSTR(0x78, 0x02),
  168. ILI9881C_COMMAND_INSTR(0x79, 0x02),
  169. ILI9881C_COMMAND_INSTR(0x7a, 0x02),
  170. ILI9881C_COMMAND_INSTR(0x7b, 0x02),
  171. ILI9881C_COMMAND_INSTR(0x7c, 0x02),
  172. ILI9881C_COMMAND_INSTR(0x7d, 0x02),
  173. ILI9881C_COMMAND_INSTR(0x7e, 0x02),
  174. ILI9881C_COMMAND_INSTR(0x7f, 0x02),
  175. ILI9881C_COMMAND_INSTR(0x80, 0x0C),
  176. ILI9881C_COMMAND_INSTR(0x81, 0x02),
  177. ILI9881C_COMMAND_INSTR(0x82, 0x0F),
  178. ILI9881C_COMMAND_INSTR(0x83, 0x0E),
  179. ILI9881C_COMMAND_INSTR(0x84, 0x0D),
  180. ILI9881C_COMMAND_INSTR(0x85, 0x06),
  181. ILI9881C_COMMAND_INSTR(0x86, 0x07),
  182. ILI9881C_COMMAND_INSTR(0x87, 0x02),
  183. ILI9881C_COMMAND_INSTR(0x88, 0x02),
  184. ILI9881C_COMMAND_INSTR(0x89, 0x02),
  185. ILI9881C_COMMAND_INSTR(0x8A, 0x02),
  186. ILI9881C_SWITCH_PAGE_INSTR(4),
  187. ILI9881C_COMMAND_INSTR(0x6C, 0x15),
  188. ILI9881C_COMMAND_INSTR(0x6E, 0x22),
  189. ILI9881C_COMMAND_INSTR(0x6F, 0x33),
  190. ILI9881C_COMMAND_INSTR(0x3A, 0xA4),
  191. ILI9881C_COMMAND_INSTR(0x8D, 0x0D),
  192. ILI9881C_COMMAND_INSTR(0x87, 0xBA),
  193. ILI9881C_COMMAND_INSTR(0x26, 0x76),
  194. ILI9881C_COMMAND_INSTR(0xB2, 0xD1),
  195. ILI9881C_SWITCH_PAGE_INSTR(1),
  196. ILI9881C_COMMAND_INSTR(0x22, 0x0A),
  197. ILI9881C_COMMAND_INSTR(0x53, 0xDC),
  198. ILI9881C_COMMAND_INSTR(0x55, 0xA7),
  199. ILI9881C_COMMAND_INSTR(0x50, 0x78),
  200. ILI9881C_COMMAND_INSTR(0x51, 0x78),
  201. ILI9881C_COMMAND_INSTR(0x31, 0x02),
  202. ILI9881C_COMMAND_INSTR(0x60, 0x14),
  203. ILI9881C_COMMAND_INSTR(0xA0, 0x2A),
  204. ILI9881C_COMMAND_INSTR(0xA1, 0x39),
  205. ILI9881C_COMMAND_INSTR(0xA2, 0x46),
  206. ILI9881C_COMMAND_INSTR(0xA3, 0x0e),
  207. ILI9881C_COMMAND_INSTR(0xA4, 0x12),
  208. ILI9881C_COMMAND_INSTR(0xA5, 0x25),
  209. ILI9881C_COMMAND_INSTR(0xA6, 0x19),
  210. ILI9881C_COMMAND_INSTR(0xA7, 0x1d),
  211. ILI9881C_COMMAND_INSTR(0xA8, 0xa6),
  212. ILI9881C_COMMAND_INSTR(0xA9, 0x1C),
  213. ILI9881C_COMMAND_INSTR(0xAA, 0x29),
  214. ILI9881C_COMMAND_INSTR(0xAB, 0x85),
  215. ILI9881C_COMMAND_INSTR(0xAC, 0x1C),
  216. ILI9881C_COMMAND_INSTR(0xAD, 0x1B),
  217. ILI9881C_COMMAND_INSTR(0xAE, 0x51),
  218. ILI9881C_COMMAND_INSTR(0xAF, 0x22),
  219. ILI9881C_COMMAND_INSTR(0xB0, 0x2d),
  220. ILI9881C_COMMAND_INSTR(0xB1, 0x4f),
  221. ILI9881C_COMMAND_INSTR(0xB2, 0x59),
  222. ILI9881C_COMMAND_INSTR(0xB3, 0x3F),
  223. ILI9881C_COMMAND_INSTR(0xC0, 0x2A),
  224. ILI9881C_COMMAND_INSTR(0xC1, 0x3a),
  225. ILI9881C_COMMAND_INSTR(0xC2, 0x45),
  226. ILI9881C_COMMAND_INSTR(0xC3, 0x0e),
  227. ILI9881C_COMMAND_INSTR(0xC4, 0x11),
  228. ILI9881C_COMMAND_INSTR(0xC5, 0x24),
  229. ILI9881C_COMMAND_INSTR(0xC6, 0x1a),
  230. ILI9881C_COMMAND_INSTR(0xC7, 0x1c),
  231. ILI9881C_COMMAND_INSTR(0xC8, 0xaa),
  232. ILI9881C_COMMAND_INSTR(0xC9, 0x1C),
  233. ILI9881C_COMMAND_INSTR(0xCA, 0x29),
  234. ILI9881C_COMMAND_INSTR(0xCB, 0x96),
  235. ILI9881C_COMMAND_INSTR(0xCC, 0x1C),
  236. ILI9881C_COMMAND_INSTR(0xCD, 0x1B),
  237. ILI9881C_COMMAND_INSTR(0xCE, 0x51),
  238. ILI9881C_COMMAND_INSTR(0xCF, 0x22),
  239. ILI9881C_COMMAND_INSTR(0xD0, 0x2b),
  240. ILI9881C_COMMAND_INSTR(0xD1, 0x4b),
  241. ILI9881C_COMMAND_INSTR(0xD2, 0x59),
  242. ILI9881C_COMMAND_INSTR(0xD3, 0x3F),
  243. };
  244. static inline struct ili9881c *panel_to_ili9881c(struct drm_panel *panel)
  245. {
  246. return container_of(panel, struct ili9881c, panel);
  247. }
  248. /*
  249. * The panel seems to accept some private DCS commands that map
  250. * directly to registers.
  251. *
  252. * It is organised by page, with each page having its own set of
  253. * registers, and the first page looks like it's holding the standard
  254. * DCS commands.
  255. *
  256. * So before any attempt at sending a command or data, we have to be
  257. * sure if we're in the right page or not.
  258. */
  259. static int ili9881c_switch_page(struct ili9881c *ctx, u8 page)
  260. {
  261. u8 buf[4] = { 0xff, 0x98, 0x81, page };
  262. int ret;
  263. ret = mipi_dsi_dcs_write_buffer(ctx->dsi, buf, sizeof(buf));
  264. if (ret < 0)
  265. return ret;
  266. return 0;
  267. }
  268. static int ili9881c_send_cmd_data(struct ili9881c *ctx, u8 cmd, u8 data)
  269. {
  270. u8 buf[2] = { cmd, data };
  271. int ret;
  272. ret = mipi_dsi_dcs_write_buffer(ctx->dsi, buf, sizeof(buf));
  273. if (ret < 0)
  274. return ret;
  275. return 0;
  276. }
  277. static int ili9881c_prepare(struct drm_panel *panel)
  278. {
  279. struct ili9881c *ctx = panel_to_ili9881c(panel);
  280. unsigned int i;
  281. int ret;
  282. /* Power the panel */
  283. ret = regulator_enable(ctx->power);
  284. if (ret)
  285. return ret;
  286. msleep(5);
  287. /* And reset it */
  288. gpiod_set_value(ctx->reset, 1);
  289. msleep(20);
  290. gpiod_set_value(ctx->reset, 0);
  291. msleep(20);
  292. for (i = 0; i < ARRAY_SIZE(ili9881c_init); i++) {
  293. const struct ili9881c_instr *instr = &ili9881c_init[i];
  294. if (instr->op == ILI9881C_SWITCH_PAGE)
  295. ret = ili9881c_switch_page(ctx, instr->arg.page);
  296. else if (instr->op == ILI9881C_COMMAND)
  297. ret = ili9881c_send_cmd_data(ctx, instr->arg.cmd.cmd,
  298. instr->arg.cmd.data);
  299. if (ret)
  300. return ret;
  301. }
  302. ret = ili9881c_switch_page(ctx, 0);
  303. if (ret)
  304. return ret;
  305. ret = mipi_dsi_dcs_set_tear_on(ctx->dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
  306. if (ret)
  307. return ret;
  308. ret = mipi_dsi_dcs_exit_sleep_mode(ctx->dsi);
  309. if (ret)
  310. return ret;
  311. return 0;
  312. }
  313. static int ili9881c_enable(struct drm_panel *panel)
  314. {
  315. struct ili9881c *ctx = panel_to_ili9881c(panel);
  316. msleep(120);
  317. mipi_dsi_dcs_set_display_on(ctx->dsi);
  318. backlight_enable(ctx->backlight);
  319. return 0;
  320. }
  321. static int ili9881c_disable(struct drm_panel *panel)
  322. {
  323. struct ili9881c *ctx = panel_to_ili9881c(panel);
  324. backlight_disable(ctx->backlight);
  325. return mipi_dsi_dcs_set_display_off(ctx->dsi);
  326. }
  327. static int ili9881c_unprepare(struct drm_panel *panel)
  328. {
  329. struct ili9881c *ctx = panel_to_ili9881c(panel);
  330. mipi_dsi_dcs_enter_sleep_mode(ctx->dsi);
  331. regulator_disable(ctx->power);
  332. gpiod_set_value(ctx->reset, 1);
  333. return 0;
  334. }
  335. static const struct drm_display_mode bananapi_default_mode = {
  336. .clock = 62000,
  337. .vrefresh = 60,
  338. .hdisplay = 720,
  339. .hsync_start = 720 + 10,
  340. .hsync_end = 720 + 10 + 20,
  341. .htotal = 720 + 10 + 20 + 30,
  342. .vdisplay = 1280,
  343. .vsync_start = 1280 + 10,
  344. .vsync_end = 1280 + 10 + 10,
  345. .vtotal = 1280 + 10 + 10 + 20,
  346. };
  347. static int ili9881c_get_modes(struct drm_panel *panel)
  348. {
  349. struct drm_connector *connector = panel->connector;
  350. struct ili9881c *ctx = panel_to_ili9881c(panel);
  351. struct drm_display_mode *mode;
  352. mode = drm_mode_duplicate(panel->drm, &bananapi_default_mode);
  353. if (!mode) {
  354. dev_err(&ctx->dsi->dev, "failed to add mode %ux%ux@%u\n",
  355. bananapi_default_mode.hdisplay,
  356. bananapi_default_mode.vdisplay,
  357. bananapi_default_mode.vrefresh);
  358. return -ENOMEM;
  359. }
  360. drm_mode_set_name(mode);
  361. mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
  362. drm_mode_probed_add(connector, mode);
  363. panel->connector->display_info.width_mm = 62;
  364. panel->connector->display_info.height_mm = 110;
  365. return 1;
  366. }
  367. static const struct drm_panel_funcs ili9881c_funcs = {
  368. .prepare = ili9881c_prepare,
  369. .unprepare = ili9881c_unprepare,
  370. .enable = ili9881c_enable,
  371. .disable = ili9881c_disable,
  372. .get_modes = ili9881c_get_modes,
  373. };
  374. static int ili9881c_dsi_probe(struct mipi_dsi_device *dsi)
  375. {
  376. struct device_node *np;
  377. struct ili9881c *ctx;
  378. int ret;
  379. ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL);
  380. if (!ctx)
  381. return -ENOMEM;
  382. mipi_dsi_set_drvdata(dsi, ctx);
  383. ctx->dsi = dsi;
  384. drm_panel_init(&ctx->panel);
  385. ctx->panel.dev = &dsi->dev;
  386. ctx->panel.funcs = &ili9881c_funcs;
  387. ctx->power = devm_regulator_get(&dsi->dev, "power");
  388. if (IS_ERR(ctx->power)) {
  389. dev_err(&dsi->dev, "Couldn't get our power regulator\n");
  390. return PTR_ERR(ctx->power);
  391. }
  392. ctx->reset = devm_gpiod_get(&dsi->dev, "reset", GPIOD_OUT_LOW);
  393. if (IS_ERR(ctx->reset)) {
  394. dev_err(&dsi->dev, "Couldn't get our reset GPIO\n");
  395. return PTR_ERR(ctx->reset);
  396. }
  397. np = of_parse_phandle(dsi->dev.of_node, "backlight", 0);
  398. if (np) {
  399. ctx->backlight = of_find_backlight_by_node(np);
  400. of_node_put(np);
  401. if (!ctx->backlight)
  402. return -EPROBE_DEFER;
  403. }
  404. ret = drm_panel_add(&ctx->panel);
  405. if (ret < 0)
  406. return ret;
  407. dsi->mode_flags = MIPI_DSI_MODE_VIDEO_SYNC_PULSE;
  408. dsi->format = MIPI_DSI_FMT_RGB888;
  409. dsi->lanes = 4;
  410. return mipi_dsi_attach(dsi);
  411. }
  412. static int ili9881c_dsi_remove(struct mipi_dsi_device *dsi)
  413. {
  414. struct ili9881c *ctx = mipi_dsi_get_drvdata(dsi);
  415. mipi_dsi_detach(dsi);
  416. drm_panel_remove(&ctx->panel);
  417. if (ctx->backlight)
  418. put_device(&ctx->backlight->dev);
  419. return 0;
  420. }
  421. static const struct of_device_id ili9881c_of_match[] = {
  422. { .compatible = "bananapi,lhr050h41" },
  423. { }
  424. };
  425. MODULE_DEVICE_TABLE(of, ili9881c_of_match);
  426. static struct mipi_dsi_driver ili9881c_dsi_driver = {
  427. .probe = ili9881c_dsi_probe,
  428. .remove = ili9881c_dsi_remove,
  429. .driver = {
  430. .name = "ili9881c-dsi",
  431. .of_match_table = ili9881c_of_match,
  432. },
  433. };
  434. module_mipi_dsi_driver(ili9881c_dsi_driver);
  435. MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
  436. MODULE_DESCRIPTION("Ilitek ILI9881C Controller Driver");
  437. MODULE_LICENSE("GPL v2");