panel-novatek-nt39016.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Novatek NT39016 TFT LCD panel driver
  4. *
  5. * Copyright (C) 2017, Maarten ter Huurne <maarten@treewalker.org>
  6. * Copyright (C) 2019, Paul Cercueil <paul@crapouillou.net>
  7. */
  8. #include <linux/delay.h>
  9. #include <linux/device.h>
  10. #include <linux/gpio/consumer.h>
  11. #include <linux/media-bus-format.h>
  12. #include <linux/module.h>
  13. #include <linux/of.h>
  14. #include <linux/regmap.h>
  15. #include <linux/regulator/consumer.h>
  16. #include <linux/spi/spi.h>
  17. #include <drm/drm_modes.h>
  18. #include <drm/drm_panel.h>
  19. enum nt39016_regs {
  20. NT39016_REG_SYSTEM,
  21. NT39016_REG_TIMING,
  22. NT39016_REG_OP,
  23. NT39016_REG_DATA_IN,
  24. NT39016_REG_SRC_TIMING_DELAY,
  25. NT39016_REG_GATE_TIMING_DELAY,
  26. NT39016_REG_RESERVED,
  27. NT39016_REG_INITIAL_FUNC,
  28. NT39016_REG_CONTRAST,
  29. NT39016_REG_BRIGHTNESS,
  30. NT39016_REG_HUE_SATURATION,
  31. NT39016_REG_RB_SUBCONTRAST,
  32. NT39016_REG_R_SUBBRIGHTNESS,
  33. NT39016_REG_B_SUBBRIGHTNESS,
  34. NT39016_REG_VCOMDC,
  35. NT39016_REG_VCOMAC,
  36. NT39016_REG_VGAM2,
  37. NT39016_REG_VGAM34,
  38. NT39016_REG_VGAM56,
  39. NT39016_REG_VCOMDC_TRIM = 0x1e,
  40. NT39016_REG_DISPLAY_MODE = 0x20,
  41. };
  42. #define NT39016_SYSTEM_RESET_N BIT(0)
  43. #define NT39016_SYSTEM_STANDBY BIT(1)
  44. struct nt39016_panel_info {
  45. const struct drm_display_mode *display_modes;
  46. unsigned int num_modes;
  47. u16 width_mm, height_mm;
  48. u32 bus_format, bus_flags;
  49. };
  50. struct nt39016 {
  51. struct drm_panel drm_panel;
  52. struct regmap *map;
  53. struct regulator *supply;
  54. const struct nt39016_panel_info *panel_info;
  55. struct gpio_desc *reset_gpio;
  56. };
  57. static inline struct nt39016 *to_nt39016(struct drm_panel *panel)
  58. {
  59. return container_of(panel, struct nt39016, drm_panel);
  60. }
  61. #define RV(REG, VAL) { .reg = (REG), .def = (VAL), .delay_us = 2 }
  62. static const struct reg_sequence nt39016_panel_regs[] = {
  63. RV(NT39016_REG_SYSTEM, 0x00),
  64. RV(NT39016_REG_TIMING, 0x00),
  65. RV(NT39016_REG_OP, 0x03),
  66. RV(NT39016_REG_DATA_IN, 0xCC),
  67. RV(NT39016_REG_SRC_TIMING_DELAY, 0x46),
  68. RV(NT39016_REG_GATE_TIMING_DELAY, 0x05),
  69. RV(NT39016_REG_RESERVED, 0x00),
  70. RV(NT39016_REG_INITIAL_FUNC, 0x00),
  71. RV(NT39016_REG_CONTRAST, 0x08),
  72. RV(NT39016_REG_BRIGHTNESS, 0x40),
  73. RV(NT39016_REG_HUE_SATURATION, 0x88),
  74. RV(NT39016_REG_RB_SUBCONTRAST, 0x88),
  75. RV(NT39016_REG_R_SUBBRIGHTNESS, 0x20),
  76. RV(NT39016_REG_B_SUBBRIGHTNESS, 0x20),
  77. RV(NT39016_REG_VCOMDC, 0x67),
  78. RV(NT39016_REG_VCOMAC, 0xA4),
  79. RV(NT39016_REG_VGAM2, 0x04),
  80. RV(NT39016_REG_VGAM34, 0x24),
  81. RV(NT39016_REG_VGAM56, 0x24),
  82. RV(NT39016_REG_DISPLAY_MODE, 0x00),
  83. };
  84. #undef RV
  85. static const struct regmap_range nt39016_regmap_no_ranges[] = {
  86. regmap_reg_range(0x13, 0x1D),
  87. regmap_reg_range(0x1F, 0x1F),
  88. };
  89. static const struct regmap_access_table nt39016_regmap_access_table = {
  90. .no_ranges = nt39016_regmap_no_ranges,
  91. .n_no_ranges = ARRAY_SIZE(nt39016_regmap_no_ranges),
  92. };
  93. static const struct regmap_config nt39016_regmap_config = {
  94. .reg_bits = 6,
  95. .pad_bits = 2,
  96. .val_bits = 8,
  97. .max_register = NT39016_REG_DISPLAY_MODE,
  98. .wr_table = &nt39016_regmap_access_table,
  99. .write_flag_mask = 0x02,
  100. .cache_type = REGCACHE_FLAT,
  101. };
  102. static int nt39016_prepare(struct drm_panel *drm_panel)
  103. {
  104. struct nt39016 *panel = to_nt39016(drm_panel);
  105. int err;
  106. err = regulator_enable(panel->supply);
  107. if (err) {
  108. dev_err(drm_panel->dev, "Failed to enable power supply: %d\n", err);
  109. return err;
  110. }
  111. /*
  112. * Reset the NT39016.
  113. * The documentation says the reset pulse should be at least 40 us to
  114. * pass the glitch filter, but when testing I see some resets fail and
  115. * some succeed when using a 70 us delay, so we use 100 us instead.
  116. */
  117. gpiod_set_value_cansleep(panel->reset_gpio, 1);
  118. usleep_range(100, 1000);
  119. gpiod_set_value_cansleep(panel->reset_gpio, 0);
  120. udelay(2);
  121. /* Init all registers. */
  122. err = regmap_multi_reg_write(panel->map, nt39016_panel_regs,
  123. ARRAY_SIZE(nt39016_panel_regs));
  124. if (err) {
  125. dev_err(drm_panel->dev, "Failed to init registers: %d\n", err);
  126. goto err_disable_regulator;
  127. }
  128. return 0;
  129. err_disable_regulator:
  130. regulator_disable(panel->supply);
  131. return err;
  132. }
  133. static int nt39016_unprepare(struct drm_panel *drm_panel)
  134. {
  135. struct nt39016 *panel = to_nt39016(drm_panel);
  136. gpiod_set_value_cansleep(panel->reset_gpio, 1);
  137. regulator_disable(panel->supply);
  138. return 0;
  139. }
  140. static int nt39016_enable(struct drm_panel *drm_panel)
  141. {
  142. struct nt39016 *panel = to_nt39016(drm_panel);
  143. int ret;
  144. ret = regmap_write(panel->map, NT39016_REG_SYSTEM,
  145. NT39016_SYSTEM_RESET_N | NT39016_SYSTEM_STANDBY);
  146. if (ret) {
  147. dev_err(drm_panel->dev, "Unable to enable panel: %d\n", ret);
  148. return ret;
  149. }
  150. if (drm_panel->backlight) {
  151. /* Wait for the picture to be ready before enabling backlight */
  152. msleep(150);
  153. }
  154. return 0;
  155. }
  156. static int nt39016_disable(struct drm_panel *drm_panel)
  157. {
  158. struct nt39016 *panel = to_nt39016(drm_panel);
  159. int err;
  160. err = regmap_write(panel->map, NT39016_REG_SYSTEM,
  161. NT39016_SYSTEM_RESET_N);
  162. if (err) {
  163. dev_err(drm_panel->dev, "Unable to disable panel: %d\n", err);
  164. return err;
  165. }
  166. return 0;
  167. }
  168. static int nt39016_get_modes(struct drm_panel *drm_panel,
  169. struct drm_connector *connector)
  170. {
  171. struct nt39016 *panel = to_nt39016(drm_panel);
  172. const struct nt39016_panel_info *panel_info = panel->panel_info;
  173. struct drm_display_mode *mode;
  174. unsigned int i;
  175. for (i = 0; i < panel_info->num_modes; i++) {
  176. mode = drm_mode_duplicate(connector->dev,
  177. &panel_info->display_modes[i]);
  178. if (!mode)
  179. return -ENOMEM;
  180. drm_mode_set_name(mode);
  181. mode->type = DRM_MODE_TYPE_DRIVER;
  182. if (panel_info->num_modes == 1)
  183. mode->type |= DRM_MODE_TYPE_PREFERRED;
  184. drm_mode_probed_add(connector, mode);
  185. }
  186. connector->display_info.bpc = 8;
  187. connector->display_info.width_mm = panel_info->width_mm;
  188. connector->display_info.height_mm = panel_info->height_mm;
  189. drm_display_info_set_bus_formats(&connector->display_info,
  190. &panel_info->bus_format, 1);
  191. connector->display_info.bus_flags = panel_info->bus_flags;
  192. return panel_info->num_modes;
  193. }
  194. static const struct drm_panel_funcs nt39016_funcs = {
  195. .prepare = nt39016_prepare,
  196. .unprepare = nt39016_unprepare,
  197. .enable = nt39016_enable,
  198. .disable = nt39016_disable,
  199. .get_modes = nt39016_get_modes,
  200. };
  201. static int nt39016_probe(struct spi_device *spi)
  202. {
  203. struct device *dev = &spi->dev;
  204. struct nt39016 *panel;
  205. int err;
  206. panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL);
  207. if (!panel)
  208. return -ENOMEM;
  209. spi_set_drvdata(spi, panel);
  210. panel->panel_info = of_device_get_match_data(dev);
  211. if (!panel->panel_info)
  212. return -EINVAL;
  213. panel->supply = devm_regulator_get(dev, "power");
  214. if (IS_ERR(panel->supply))
  215. return dev_err_probe(dev, PTR_ERR(panel->supply),
  216. "Failed to get power supply\n");
  217. panel->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
  218. if (IS_ERR(panel->reset_gpio))
  219. return dev_err_probe(dev, PTR_ERR(panel->reset_gpio), "Failed to get reset GPIO\n");
  220. spi->bits_per_word = 8;
  221. spi->mode = SPI_MODE_3 | SPI_3WIRE;
  222. err = spi_setup(spi);
  223. if (err) {
  224. dev_err(dev, "Failed to setup SPI\n");
  225. return err;
  226. }
  227. panel->map = devm_regmap_init_spi(spi, &nt39016_regmap_config);
  228. if (IS_ERR(panel->map)) {
  229. dev_err(dev, "Failed to init regmap\n");
  230. return PTR_ERR(panel->map);
  231. }
  232. drm_panel_init(&panel->drm_panel, dev, &nt39016_funcs,
  233. DRM_MODE_CONNECTOR_DPI);
  234. err = drm_panel_of_backlight(&panel->drm_panel);
  235. if (err)
  236. return dev_err_probe(dev, err, "Failed to get backlight handle\n");
  237. drm_panel_add(&panel->drm_panel);
  238. return 0;
  239. }
  240. static void nt39016_remove(struct spi_device *spi)
  241. {
  242. struct nt39016 *panel = spi_get_drvdata(spi);
  243. drm_panel_remove(&panel->drm_panel);
  244. nt39016_disable(&panel->drm_panel);
  245. nt39016_unprepare(&panel->drm_panel);
  246. }
  247. static const struct drm_display_mode kd035g6_display_modes[] = {
  248. { /* 60 Hz */
  249. .clock = 6000,
  250. .hdisplay = 320,
  251. .hsync_start = 320 + 10,
  252. .hsync_end = 320 + 10 + 50,
  253. .htotal = 320 + 10 + 50 + 20,
  254. .vdisplay = 240,
  255. .vsync_start = 240 + 5,
  256. .vsync_end = 240 + 5 + 1,
  257. .vtotal = 240 + 5 + 1 + 4,
  258. .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
  259. },
  260. { /* 50 Hz */
  261. .clock = 5400,
  262. .hdisplay = 320,
  263. .hsync_start = 320 + 42,
  264. .hsync_end = 320 + 42 + 50,
  265. .htotal = 320 + 42 + 50 + 20,
  266. .vdisplay = 240,
  267. .vsync_start = 240 + 5,
  268. .vsync_end = 240 + 5 + 1,
  269. .vtotal = 240 + 5 + 1 + 4,
  270. .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
  271. },
  272. };
  273. static const struct nt39016_panel_info kd035g6_info = {
  274. .display_modes = kd035g6_display_modes,
  275. .num_modes = ARRAY_SIZE(kd035g6_display_modes),
  276. .width_mm = 71,
  277. .height_mm = 53,
  278. .bus_format = MEDIA_BUS_FMT_RGB888_1X24,
  279. .bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE,
  280. };
  281. static const struct of_device_id nt39016_of_match[] = {
  282. { .compatible = "kingdisplay,kd035g6-54nt", .data = &kd035g6_info },
  283. { /* sentinel */ }
  284. };
  285. MODULE_DEVICE_TABLE(of, nt39016_of_match);
  286. static struct spi_driver nt39016_driver = {
  287. .driver = {
  288. .name = "nt39016",
  289. .of_match_table = nt39016_of_match,
  290. },
  291. .probe = nt39016_probe,
  292. .remove = nt39016_remove,
  293. };
  294. module_spi_driver(nt39016_driver);
  295. MODULE_AUTHOR("Maarten ter Huurne <maarten@treewalker.org>");
  296. MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
  297. MODULE_DESCRIPTION("Novatek NT39016 TFT LCD panel driver");
  298. MODULE_LICENSE("GPL v2");