413 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd4 */5 6#include <linux/delay.h>7#include <linux/gpio/consumer.h>8#include <linux/module.h>9#include <linux/of.h>10#include <linux/regulator/consumer.h>11 12#include <video/mipi_display.h>13 14#include <drm/drm_crtc.h>15#include <drm/drm_device.h>16#include <drm/drm_mipi_dsi.h>17#include <drm/drm_modes.h>18#include <drm/drm_panel.h>19 20struct kingdisplay_panel {21 struct drm_panel base;22 struct mipi_dsi_device *link;23 24 struct regulator *supply;25 struct gpio_desc *enable_gpio;26};27 28struct kingdisplay_panel_cmd {29 char cmd;30 char data;31};32 33/*34 * According to the discussion on35 * https://review.coreboot.org/#/c/coreboot/+/22472/36 * the panel init array is not part of the panels datasheet but instead37 * just came in this form from the panel vendor.38 */39static const struct kingdisplay_panel_cmd init_code[] = {40 /* voltage setting */41 { 0xB0, 0x00 },42 { 0xB2, 0x02 },43 { 0xB3, 0x11 },44 { 0xB4, 0x00 },45 { 0xB6, 0x80 },46 /* VCOM disable */47 { 0xB7, 0x02 },48 { 0xB8, 0x80 },49 { 0xBA, 0x43 },50 /* VCOM setting */51 { 0xBB, 0x53 },52 /* VSP setting */53 { 0xBC, 0x0A },54 /* VSN setting */55 { 0xBD, 0x4A },56 /* VGH setting */57 { 0xBE, 0x2F },58 /* VGL setting */59 { 0xBF, 0x1A },60 { 0xF0, 0x39 },61 { 0xF1, 0x22 },62 /* Gamma setting */63 { 0xB0, 0x02 },64 { 0xC0, 0x00 },65 { 0xC1, 0x01 },66 { 0xC2, 0x0B },67 { 0xC3, 0x15 },68 { 0xC4, 0x22 },69 { 0xC5, 0x11 },70 { 0xC6, 0x15 },71 { 0xC7, 0x19 },72 { 0xC8, 0x1A },73 { 0xC9, 0x16 },74 { 0xCA, 0x18 },75 { 0xCB, 0x13 },76 { 0xCC, 0x18 },77 { 0xCD, 0x13 },78 { 0xCE, 0x1C },79 { 0xCF, 0x19 },80 { 0xD0, 0x21 },81 { 0xD1, 0x2C },82 { 0xD2, 0x2F },83 { 0xD3, 0x30 },84 { 0xD4, 0x19 },85 { 0xD5, 0x1F },86 { 0xD6, 0x00 },87 { 0xD7, 0x01 },88 { 0xD8, 0x0B },89 { 0xD9, 0x15 },90 { 0xDA, 0x22 },91 { 0xDB, 0x11 },92 { 0xDC, 0x15 },93 { 0xDD, 0x19 },94 { 0xDE, 0x1A },95 { 0xDF, 0x16 },96 { 0xE0, 0x18 },97 { 0xE1, 0x13 },98 { 0xE2, 0x18 },99 { 0xE3, 0x13 },100 { 0xE4, 0x1C },101 { 0xE5, 0x19 },102 { 0xE6, 0x21 },103 { 0xE7, 0x2C },104 { 0xE8, 0x2F },105 { 0xE9, 0x30 },106 { 0xEA, 0x19 },107 { 0xEB, 0x1F },108 /* GOA MUX setting */109 { 0xB0, 0x01 },110 { 0xC0, 0x10 },111 { 0xC1, 0x0F },112 { 0xC2, 0x0E },113 { 0xC3, 0x0D },114 { 0xC4, 0x0C },115 { 0xC5, 0x0B },116 { 0xC6, 0x0A },117 { 0xC7, 0x09 },118 { 0xC8, 0x08 },119 { 0xC9, 0x07 },120 { 0xCA, 0x06 },121 { 0xCB, 0x05 },122 { 0xCC, 0x00 },123 { 0xCD, 0x01 },124 { 0xCE, 0x02 },125 { 0xCF, 0x03 },126 { 0xD0, 0x04 },127 { 0xD6, 0x10 },128 { 0xD7, 0x0F },129 { 0xD8, 0x0E },130 { 0xD9, 0x0D },131 { 0xDA, 0x0C },132 { 0xDB, 0x0B },133 { 0xDC, 0x0A },134 { 0xDD, 0x09 },135 { 0xDE, 0x08 },136 { 0xDF, 0x07 },137 { 0xE0, 0x06 },138 { 0xE1, 0x05 },139 { 0xE2, 0x00 },140 { 0xE3, 0x01 },141 { 0xE4, 0x02 },142 { 0xE5, 0x03 },143 { 0xE6, 0x04 },144 { 0xE7, 0x00 },145 { 0xEC, 0xC0 },146 /* GOA timing setting */147 { 0xB0, 0x03 },148 { 0xC0, 0x01 },149 { 0xC2, 0x6F },150 { 0xC3, 0x6F },151 { 0xC5, 0x36 },152 { 0xC8, 0x08 },153 { 0xC9, 0x04 },154 { 0xCA, 0x41 },155 { 0xCC, 0x43 },156 { 0xCF, 0x60 },157 { 0xD2, 0x04 },158 { 0xD3, 0x04 },159 { 0xD4, 0x03 },160 { 0xD5, 0x02 },161 { 0xD6, 0x01 },162 { 0xD7, 0x00 },163 { 0xDB, 0x01 },164 { 0xDE, 0x36 },165 { 0xE6, 0x6F },166 { 0xE7, 0x6F },167 /* GOE setting */168 { 0xB0, 0x06 },169 { 0xB8, 0xA5 },170 { 0xC0, 0xA5 },171 { 0xD5, 0x3F },172};173 174static inline175struct kingdisplay_panel *to_kingdisplay_panel(struct drm_panel *panel)176{177 return container_of(panel, struct kingdisplay_panel, base);178}179 180static int kingdisplay_panel_disable(struct drm_panel *panel)181{182 struct kingdisplay_panel *kingdisplay = to_kingdisplay_panel(panel);183 int err;184 185 err = mipi_dsi_dcs_set_display_off(kingdisplay->link);186 if (err < 0)187 dev_err(panel->dev, "failed to set display off: %d\n", err);188 189 return 0;190}191 192static int kingdisplay_panel_unprepare(struct drm_panel *panel)193{194 struct kingdisplay_panel *kingdisplay = to_kingdisplay_panel(panel);195 int err;196 197 err = mipi_dsi_dcs_enter_sleep_mode(kingdisplay->link);198 if (err < 0) {199 dev_err(panel->dev, "failed to enter sleep mode: %d\n", err);200 return err;201 }202 203 /* T15: 120ms */204 msleep(120);205 206 gpiod_set_value_cansleep(kingdisplay->enable_gpio, 0);207 208 err = regulator_disable(kingdisplay->supply);209 if (err < 0)210 return err;211 212 return 0;213}214 215static int kingdisplay_panel_prepare(struct drm_panel *panel)216{217 struct kingdisplay_panel *kingdisplay = to_kingdisplay_panel(panel);218 int err, regulator_err;219 unsigned int i;220 221 gpiod_set_value_cansleep(kingdisplay->enable_gpio, 0);222 223 err = regulator_enable(kingdisplay->supply);224 if (err < 0)225 return err;226 227 /* T2: 15ms */228 usleep_range(15000, 16000);229 230 gpiod_set_value_cansleep(kingdisplay->enable_gpio, 1);231 232 /* T4: 15ms */233 usleep_range(15000, 16000);234 235 for (i = 0; i < ARRAY_SIZE(init_code); i++) {236 err = mipi_dsi_generic_write(kingdisplay->link, &init_code[i],237 sizeof(struct kingdisplay_panel_cmd));238 if (err < 0) {239 dev_err(panel->dev, "failed write init cmds: %d\n", err);240 goto poweroff;241 }242 }243 244 err = mipi_dsi_dcs_exit_sleep_mode(kingdisplay->link);245 if (err < 0) {246 dev_err(panel->dev, "failed to exit sleep mode: %d\n", err);247 goto poweroff;248 }249 250 /* T6: 120ms */251 msleep(120);252 253 err = mipi_dsi_dcs_set_display_on(kingdisplay->link);254 if (err < 0) {255 dev_err(panel->dev, "failed to set display on: %d\n", err);256 goto poweroff;257 }258 259 /* T7: 10ms */260 usleep_range(10000, 11000);261 262 return 0;263 264poweroff:265 gpiod_set_value_cansleep(kingdisplay->enable_gpio, 0);266 267 regulator_err = regulator_disable(kingdisplay->supply);268 if (regulator_err)269 dev_err(panel->dev, "failed to disable regulator: %d\n", regulator_err);270 271 return err;272}273 274static const struct drm_display_mode default_mode = {275 .clock = 229000,276 .hdisplay = 1536,277 .hsync_start = 1536 + 100,278 .hsync_end = 1536 + 100 + 24,279 .htotal = 1536 + 100 + 24 + 100,280 .vdisplay = 2048,281 .vsync_start = 2048 + 95,282 .vsync_end = 2048 + 95 + 2,283 .vtotal = 2048 + 95 + 2 + 23,284};285 286static int kingdisplay_panel_get_modes(struct drm_panel *panel,287 struct drm_connector *connector)288{289 struct drm_display_mode *mode;290 291 mode = drm_mode_duplicate(connector->dev, &default_mode);292 if (!mode) {293 dev_err(panel->dev, "failed to add mode %ux%u@%u\n",294 default_mode.hdisplay, default_mode.vdisplay,295 drm_mode_vrefresh(&default_mode));296 return -ENOMEM;297 }298 299 drm_mode_set_name(mode);300 301 drm_mode_probed_add(connector, mode);302 303 connector->display_info.width_mm = 147;304 connector->display_info.height_mm = 196;305 connector->display_info.bpc = 8;306 307 return 1;308}309 310static const struct drm_panel_funcs kingdisplay_panel_funcs = {311 .disable = kingdisplay_panel_disable,312 .unprepare = kingdisplay_panel_unprepare,313 .prepare = kingdisplay_panel_prepare,314 .get_modes = kingdisplay_panel_get_modes,315};316 317static const struct of_device_id kingdisplay_of_match[] = {318 { .compatible = "kingdisplay,kd097d04", },319 { }320};321MODULE_DEVICE_TABLE(of, kingdisplay_of_match);322 323static int kingdisplay_panel_add(struct kingdisplay_panel *kingdisplay)324{325 struct device *dev = &kingdisplay->link->dev;326 int err;327 328 kingdisplay->supply = devm_regulator_get(dev, "power");329 if (IS_ERR(kingdisplay->supply))330 return PTR_ERR(kingdisplay->supply);331 332 kingdisplay->enable_gpio = devm_gpiod_get_optional(dev, "enable",333 GPIOD_OUT_HIGH);334 if (IS_ERR(kingdisplay->enable_gpio)) {335 err = PTR_ERR(kingdisplay->enable_gpio);336 dev_dbg(dev, "failed to get enable gpio: %d\n", err);337 kingdisplay->enable_gpio = NULL;338 }339 340 drm_panel_init(&kingdisplay->base, &kingdisplay->link->dev,341 &kingdisplay_panel_funcs, DRM_MODE_CONNECTOR_DSI);342 343 err = drm_panel_of_backlight(&kingdisplay->base);344 if (err)345 return err;346 347 drm_panel_add(&kingdisplay->base);348 349 return 0;350}351 352static void kingdisplay_panel_del(struct kingdisplay_panel *kingdisplay)353{354 drm_panel_remove(&kingdisplay->base);355}356 357static int kingdisplay_panel_probe(struct mipi_dsi_device *dsi)358{359 struct kingdisplay_panel *kingdisplay;360 int err;361 362 dsi->lanes = 4;363 dsi->format = MIPI_DSI_FMT_RGB888;364 dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |365 MIPI_DSI_MODE_LPM;366 367 kingdisplay = devm_kzalloc(&dsi->dev, sizeof(*kingdisplay), GFP_KERNEL);368 if (!kingdisplay)369 return -ENOMEM;370 371 mipi_dsi_set_drvdata(dsi, kingdisplay);372 kingdisplay->link = dsi;373 374 err = kingdisplay_panel_add(kingdisplay);375 if (err < 0)376 return err;377 378 err = mipi_dsi_attach(dsi);379 if (err < 0) {380 kingdisplay_panel_del(kingdisplay);381 return err;382 }383 384 return 0;385}386 387static void kingdisplay_panel_remove(struct mipi_dsi_device *dsi)388{389 struct kingdisplay_panel *kingdisplay = mipi_dsi_get_drvdata(dsi);390 int err;391 392 err = mipi_dsi_detach(dsi);393 if (err < 0)394 dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", err);395 396 kingdisplay_panel_del(kingdisplay);397}398 399static struct mipi_dsi_driver kingdisplay_panel_driver = {400 .driver = {401 .name = "panel-kingdisplay-kd097d04",402 .of_match_table = kingdisplay_of_match,403 },404 .probe = kingdisplay_panel_probe,405 .remove = kingdisplay_panel_remove,406};407module_mipi_dsi_driver(kingdisplay_panel_driver);408 409MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>");410MODULE_AUTHOR("Nickey Yang <nickey.yang@rock-chips.com>");411MODULE_DESCRIPTION("kingdisplay KD097D04 panel driver");412MODULE_LICENSE("GPL v2");413