1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Xinpeng xpp055c272 5.5" MIPI-DSI panel driver 4 * Copyright (C) 2019 Theobroma Systems Design und Consulting GmbH 5 * 6 * based on 7 * 8 * Rockteck jh057n00900 5.5" MIPI-DSI panel driver 9 * Copyright (C) Purism SPC 2019 10 */ 11 12 #include <drm/drm_mipi_dsi.h> 13 #include <drm/drm_modes.h> 14 #include <drm/drm_panel.h> 15 16 #include <video/display_timing.h> 17 #include <video/mipi_display.h> 18 19 #include <linux/delay.h> 20 #include <linux/gpio/consumer.h> 21 #include <linux/media-bus-format.h> 22 #include <linux/module.h> 23 #include <linux/of.h> 24 #include <linux/regulator/consumer.h> 25 26 /* Manufacturer specific Commands send via DSI */ 27 #define XPP055C272_CMD_ALL_PIXEL_OFF 0x22 28 #define XPP055C272_CMD_ALL_PIXEL_ON 0x23 29 #define XPP055C272_CMD_SETDISP 0xb2 30 #define XPP055C272_CMD_SETRGBIF 0xb3 31 #define XPP055C272_CMD_SETCYC 0xb4 32 #define XPP055C272_CMD_SETBGP 0xb5 33 #define XPP055C272_CMD_SETVCOM 0xb6 34 #define XPP055C272_CMD_SETOTP 0xb7 35 #define XPP055C272_CMD_SETPOWER_EXT 0xb8 36 #define XPP055C272_CMD_SETEXTC 0xb9 37 #define XPP055C272_CMD_SETMIPI 0xbA 38 #define XPP055C272_CMD_SETVDC 0xbc 39 #define XPP055C272_CMD_SETPCR 0xbf 40 #define XPP055C272_CMD_SETSCR 0xc0 41 #define XPP055C272_CMD_SETPOWER 0xc1 42 #define XPP055C272_CMD_SETECO 0xc6 43 #define XPP055C272_CMD_SETPANEL 0xcc 44 #define XPP055C272_CMD_SETGAMMA 0xe0 45 #define XPP055C272_CMD_SETEQ 0xe3 46 #define XPP055C272_CMD_SETGIP1 0xe9 47 #define XPP055C272_CMD_SETGIP2 0xea 48 49 struct xpp055c272 { 50 struct device *dev; 51 struct drm_panel panel; 52 struct gpio_desc *reset_gpio; 53 struct regulator *vci; 54 struct regulator *iovcc; 55 bool prepared; 56 }; 57 58 static inline struct xpp055c272 *panel_to_xpp055c272(struct drm_panel *panel) 59 { 60 return container_of(panel, struct xpp055c272, panel); 61 } 62 63 static int xpp055c272_init_sequence(struct xpp055c272 *ctx) 64 { 65 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 66 struct device *dev = ctx->dev; 67 68 /* 69 * Init sequence was supplied by the panel vendor without much 70 * documentation. 71 */ 72 mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETEXTC, 0xf1, 0x12, 0x83); 73 mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETMIPI, 74 0x33, 0x81, 0x05, 0xf9, 0x0e, 0x0e, 0x00, 0x00, 75 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x25, 76 0x00, 0x91, 0x0a, 0x00, 0x00, 0x02, 0x4f, 0x01, 77 0x00, 0x00, 0x37); 78 mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETPOWER_EXT, 0x25); 79 mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETPCR, 0x02, 0x11, 0x00); 80 mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETRGBIF, 81 0x0c, 0x10, 0x0a, 0x50, 0x03, 0xff, 0x00, 0x00, 82 0x00, 0x00); 83 mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETSCR, 84 0x73, 0x73, 0x50, 0x50, 0x00, 0x00, 0x08, 0x70, 85 0x00); 86 mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETVDC, 0x46); 87 mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETPANEL, 0x0b); 88 mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETCYC, 0x80); 89 mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETDISP, 0xc8, 0x12, 0x30); 90 mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETEQ, 91 0x07, 0x07, 0x0B, 0x0B, 0x03, 0x0B, 0x00, 0x00, 92 0x00, 0x00, 0xFF, 0x00, 0xC0, 0x10); 93 mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETPOWER, 94 0x53, 0x00, 0x1e, 0x1e, 0x77, 0xe1, 0xcc, 0xdd, 95 0x67, 0x77, 0x33, 0x33); 96 mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETECO, 0x00, 0x00, 0xff, 97 0xff, 0x01, 0xff); 98 mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETBGP, 0x09, 0x09); 99 msleep(20); 100 101 mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETVCOM, 0x87, 0x95); 102 mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETGIP1, 103 0xc2, 0x10, 0x05, 0x05, 0x10, 0x05, 0xa0, 0x12, 104 0x31, 0x23, 0x3f, 0x81, 0x0a, 0xa0, 0x37, 0x18, 105 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80, 106 0x01, 0x00, 0x00, 0x00, 0x48, 0xf8, 0x86, 0x42, 107 0x08, 0x88, 0x88, 0x80, 0x88, 0x88, 0x88, 0x58, 108 0xf8, 0x87, 0x53, 0x18, 0x88, 0x88, 0x81, 0x88, 109 0x88, 0x88, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 110 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); 111 mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETGIP2, 112 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 113 0x00, 0x00, 0x00, 0x00, 0x1f, 0x88, 0x81, 0x35, 114 0x78, 0x88, 0x88, 0x85, 0x88, 0x88, 0x88, 0x0f, 115 0x88, 0x80, 0x24, 0x68, 0x88, 0x88, 0x84, 0x88, 116 0x88, 0x88, 0x23, 0x10, 0x00, 0x00, 0x1c, 0x00, 117 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 118 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x05, 119 0xa0, 0x00, 0x00, 0x00, 0x00); 120 mipi_dsi_dcs_write_seq(dsi, XPP055C272_CMD_SETGAMMA, 121 0x00, 0x06, 0x08, 0x2a, 0x31, 0x3f, 0x38, 0x36, 122 0x07, 0x0c, 0x0d, 0x11, 0x13, 0x12, 0x13, 0x11, 123 0x18, 0x00, 0x06, 0x08, 0x2a, 0x31, 0x3f, 0x38, 124 0x36, 0x07, 0x0c, 0x0d, 0x11, 0x13, 0x12, 0x13, 125 0x11, 0x18); 126 127 msleep(60); 128 129 dev_dbg(dev, "Panel init sequence done\n"); 130 return 0; 131 } 132 133 static int xpp055c272_unprepare(struct drm_panel *panel) 134 { 135 struct xpp055c272 *ctx = panel_to_xpp055c272(panel); 136 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 137 int ret; 138 139 if (!ctx->prepared) 140 return 0; 141 142 ret = mipi_dsi_dcs_set_display_off(dsi); 143 if (ret < 0) 144 dev_err(ctx->dev, "failed to set display off: %d\n", ret); 145 146 mipi_dsi_dcs_enter_sleep_mode(dsi); 147 if (ret < 0) { 148 dev_err(ctx->dev, "failed to enter sleep mode: %d\n", ret); 149 return ret; 150 } 151 152 regulator_disable(ctx->iovcc); 153 regulator_disable(ctx->vci); 154 155 ctx->prepared = false; 156 157 return 0; 158 } 159 160 static int xpp055c272_prepare(struct drm_panel *panel) 161 { 162 struct xpp055c272 *ctx = panel_to_xpp055c272(panel); 163 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 164 int ret; 165 166 if (ctx->prepared) 167 return 0; 168 169 dev_dbg(ctx->dev, "Resetting the panel\n"); 170 ret = regulator_enable(ctx->vci); 171 if (ret < 0) { 172 dev_err(ctx->dev, "Failed to enable vci supply: %d\n", ret); 173 return ret; 174 } 175 ret = regulator_enable(ctx->iovcc); 176 if (ret < 0) { 177 dev_err(ctx->dev, "Failed to enable iovcc supply: %d\n", ret); 178 goto disable_vci; 179 } 180 181 gpiod_set_value_cansleep(ctx->reset_gpio, 1); 182 /* T6: 10us */ 183 usleep_range(10, 20); 184 gpiod_set_value_cansleep(ctx->reset_gpio, 0); 185 186 /* T8: 20ms */ 187 msleep(20); 188 189 ret = xpp055c272_init_sequence(ctx); 190 if (ret < 0) { 191 dev_err(ctx->dev, "Panel init sequence failed: %d\n", ret); 192 goto disable_iovcc; 193 } 194 195 ret = mipi_dsi_dcs_exit_sleep_mode(dsi); 196 if (ret < 0) { 197 dev_err(ctx->dev, "Failed to exit sleep mode: %d\n", ret); 198 goto disable_iovcc; 199 } 200 201 /* T9: 120ms */ 202 msleep(120); 203 204 ret = mipi_dsi_dcs_set_display_on(dsi); 205 if (ret < 0) { 206 dev_err(ctx->dev, "Failed to set display on: %d\n", ret); 207 goto disable_iovcc; 208 } 209 210 msleep(50); 211 212 ctx->prepared = true; 213 214 return 0; 215 216 disable_iovcc: 217 regulator_disable(ctx->iovcc); 218 disable_vci: 219 regulator_disable(ctx->vci); 220 return ret; 221 } 222 223 static const struct drm_display_mode default_mode = { 224 .hdisplay = 720, 225 .hsync_start = 720 + 40, 226 .hsync_end = 720 + 40 + 10, 227 .htotal = 720 + 40 + 10 + 40, 228 .vdisplay = 1280, 229 .vsync_start = 1280 + 22, 230 .vsync_end = 1280 + 22 + 4, 231 .vtotal = 1280 + 22 + 4 + 11, 232 .clock = 64000, 233 .width_mm = 68, 234 .height_mm = 121, 235 }; 236 237 static int xpp055c272_get_modes(struct drm_panel *panel, 238 struct drm_connector *connector) 239 { 240 struct xpp055c272 *ctx = panel_to_xpp055c272(panel); 241 struct drm_display_mode *mode; 242 243 mode = drm_mode_duplicate(connector->dev, &default_mode); 244 if (!mode) { 245 dev_err(ctx->dev, "Failed to add mode %ux%u@%u\n", 246 default_mode.hdisplay, default_mode.vdisplay, 247 drm_mode_vrefresh(&default_mode)); 248 return -ENOMEM; 249 } 250 251 drm_mode_set_name(mode); 252 253 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 254 connector->display_info.width_mm = mode->width_mm; 255 connector->display_info.height_mm = mode->height_mm; 256 drm_mode_probed_add(connector, mode); 257 258 return 1; 259 } 260 261 static const struct drm_panel_funcs xpp055c272_funcs = { 262 .unprepare = xpp055c272_unprepare, 263 .prepare = xpp055c272_prepare, 264 .get_modes = xpp055c272_get_modes, 265 }; 266 267 static int xpp055c272_probe(struct mipi_dsi_device *dsi) 268 { 269 struct device *dev = &dsi->dev; 270 struct xpp055c272 *ctx; 271 int ret; 272 273 ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 274 if (!ctx) 275 return -ENOMEM; 276 277 ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); 278 if (IS_ERR(ctx->reset_gpio)) 279 return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), 280 "cannot get reset gpio\n"); 281 282 ctx->vci = devm_regulator_get(dev, "vci"); 283 if (IS_ERR(ctx->vci)) 284 return dev_err_probe(dev, PTR_ERR(ctx->vci), 285 "Failed to request vci regulator\n"); 286 287 ctx->iovcc = devm_regulator_get(dev, "iovcc"); 288 if (IS_ERR(ctx->iovcc)) 289 return dev_err_probe(dev, PTR_ERR(ctx->iovcc), 290 "Failed to request iovcc regulator\n"); 291 292 mipi_dsi_set_drvdata(dsi, ctx); 293 294 ctx->dev = dev; 295 296 dsi->lanes = 4; 297 dsi->format = MIPI_DSI_FMT_RGB888; 298 dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | 299 MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET; 300 301 drm_panel_init(&ctx->panel, &dsi->dev, &xpp055c272_funcs, 302 DRM_MODE_CONNECTOR_DSI); 303 304 ret = drm_panel_of_backlight(&ctx->panel); 305 if (ret) 306 return ret; 307 308 drm_panel_add(&ctx->panel); 309 310 ret = mipi_dsi_attach(dsi); 311 if (ret < 0) { 312 dev_err(dev, "mipi_dsi_attach failed: %d\n", ret); 313 drm_panel_remove(&ctx->panel); 314 return ret; 315 } 316 317 return 0; 318 } 319 320 static void xpp055c272_shutdown(struct mipi_dsi_device *dsi) 321 { 322 struct xpp055c272 *ctx = mipi_dsi_get_drvdata(dsi); 323 int ret; 324 325 ret = drm_panel_unprepare(&ctx->panel); 326 if (ret < 0) 327 dev_err(&dsi->dev, "Failed to unprepare panel: %d\n", ret); 328 329 ret = drm_panel_disable(&ctx->panel); 330 if (ret < 0) 331 dev_err(&dsi->dev, "Failed to disable panel: %d\n", ret); 332 } 333 334 static void xpp055c272_remove(struct mipi_dsi_device *dsi) 335 { 336 struct xpp055c272 *ctx = mipi_dsi_get_drvdata(dsi); 337 int ret; 338 339 xpp055c272_shutdown(dsi); 340 341 ret = mipi_dsi_detach(dsi); 342 if (ret < 0) 343 dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); 344 345 drm_panel_remove(&ctx->panel); 346 } 347 348 static const struct of_device_id xpp055c272_of_match[] = { 349 { .compatible = "xinpeng,xpp055c272" }, 350 { /* sentinel */ } 351 }; 352 MODULE_DEVICE_TABLE(of, xpp055c272_of_match); 353 354 static struct mipi_dsi_driver xpp055c272_driver = { 355 .driver = { 356 .name = "panel-xinpeng-xpp055c272", 357 .of_match_table = xpp055c272_of_match, 358 }, 359 .probe = xpp055c272_probe, 360 .remove = xpp055c272_remove, 361 .shutdown = xpp055c272_shutdown, 362 }; 363 module_mipi_dsi_driver(xpp055c272_driver); 364 365 MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>"); 366 MODULE_DESCRIPTION("DRM driver for Xinpeng xpp055c272 MIPI DSI panel"); 367 MODULE_LICENSE("GPL v2"); 368