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 }; 56 57 static inline struct xpp055c272 *panel_to_xpp055c272(struct drm_panel *panel) 58 { 59 return container_of(panel, struct xpp055c272, panel); 60 } 61 62 static void xpp055c272_init_sequence(struct mipi_dsi_multi_context *dsi_ctx) 63 { 64 /* 65 * Init sequence was supplied by the panel vendor without much 66 * documentation. 67 */ 68 mipi_dsi_dcs_write_seq_multi(dsi_ctx, XPP055C272_CMD_SETEXTC, 0xf1, 0x12, 0x83); 69 mipi_dsi_dcs_write_seq_multi(dsi_ctx, XPP055C272_CMD_SETMIPI, 70 0x33, 0x81, 0x05, 0xf9, 0x0e, 0x0e, 0x00, 0x00, 71 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x25, 72 0x00, 0x91, 0x0a, 0x00, 0x00, 0x02, 0x4f, 0x01, 73 0x00, 0x00, 0x37); 74 mipi_dsi_dcs_write_seq_multi(dsi_ctx, XPP055C272_CMD_SETPOWER_EXT, 0x25); 75 mipi_dsi_dcs_write_seq_multi(dsi_ctx, XPP055C272_CMD_SETPCR, 0x02, 0x11, 0x00); 76 mipi_dsi_dcs_write_seq_multi(dsi_ctx, XPP055C272_CMD_SETRGBIF, 77 0x0c, 0x10, 0x0a, 0x50, 0x03, 0xff, 0x00, 0x00, 78 0x00, 0x00); 79 mipi_dsi_dcs_write_seq_multi(dsi_ctx, XPP055C272_CMD_SETSCR, 80 0x73, 0x73, 0x50, 0x50, 0x00, 0x00, 0x08, 0x70, 81 0x00); 82 mipi_dsi_dcs_write_seq_multi(dsi_ctx, XPP055C272_CMD_SETVDC, 0x46); 83 mipi_dsi_dcs_write_seq_multi(dsi_ctx, XPP055C272_CMD_SETPANEL, 0x0b); 84 mipi_dsi_dcs_write_seq_multi(dsi_ctx, XPP055C272_CMD_SETCYC, 0x80); 85 mipi_dsi_dcs_write_seq_multi(dsi_ctx, XPP055C272_CMD_SETDISP, 0xc8, 0x12, 0x30); 86 mipi_dsi_dcs_write_seq_multi(dsi_ctx, XPP055C272_CMD_SETEQ, 87 0x07, 0x07, 0x0b, 0x0b, 0x03, 0x0b, 0x00, 0x00, 88 0x00, 0x00, 0xff, 0x00, 0xC0, 0x10); 89 mipi_dsi_dcs_write_seq_multi(dsi_ctx, XPP055C272_CMD_SETPOWER, 90 0x53, 0x00, 0x1e, 0x1e, 0x77, 0xe1, 0xcc, 0xdd, 91 0x67, 0x77, 0x33, 0x33); 92 mipi_dsi_dcs_write_seq_multi(dsi_ctx, XPP055C272_CMD_SETECO, 0x00, 0x00, 0xff, 93 0xff, 0x01, 0xff); 94 mipi_dsi_dcs_write_seq_multi(dsi_ctx, XPP055C272_CMD_SETBGP, 0x09, 0x09); 95 mipi_dsi_msleep(dsi_ctx, 20); 96 97 mipi_dsi_dcs_write_seq_multi(dsi_ctx, XPP055C272_CMD_SETVCOM, 0x87, 0x95); 98 mipi_dsi_dcs_write_seq_multi(dsi_ctx, XPP055C272_CMD_SETGIP1, 99 0xc2, 0x10, 0x05, 0x05, 0x10, 0x05, 0xa0, 0x12, 100 0x31, 0x23, 0x3f, 0x81, 0x0a, 0xa0, 0x37, 0x18, 101 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80, 102 0x01, 0x00, 0x00, 0x00, 0x48, 0xf8, 0x86, 0x42, 103 0x08, 0x88, 0x88, 0x80, 0x88, 0x88, 0x88, 0x58, 104 0xf8, 0x87, 0x53, 0x18, 0x88, 0x88, 0x81, 0x88, 105 0x88, 0x88, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 106 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); 107 mipi_dsi_dcs_write_seq_multi(dsi_ctx, XPP055C272_CMD_SETGIP2, 108 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 109 0x00, 0x00, 0x00, 0x00, 0x1f, 0x88, 0x81, 0x35, 110 0x78, 0x88, 0x88, 0x85, 0x88, 0x88, 0x88, 0x0f, 111 0x88, 0x80, 0x24, 0x68, 0x88, 0x88, 0x84, 0x88, 112 0x88, 0x88, 0x23, 0x10, 0x00, 0x00, 0x1c, 0x00, 113 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 114 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x05, 115 0xa0, 0x00, 0x00, 0x00, 0x00); 116 mipi_dsi_dcs_write_seq_multi(dsi_ctx, XPP055C272_CMD_SETGAMMA, 117 0x00, 0x06, 0x08, 0x2a, 0x31, 0x3f, 0x38, 0x36, 118 0x07, 0x0c, 0x0d, 0x11, 0x13, 0x12, 0x13, 0x11, 119 0x18, 0x00, 0x06, 0x08, 0x2a, 0x31, 0x3f, 0x38, 120 0x36, 0x07, 0x0c, 0x0d, 0x11, 0x13, 0x12, 0x13, 121 0x11, 0x18); 122 123 mipi_dsi_msleep(dsi_ctx, 60); 124 } 125 126 static int xpp055c272_unprepare(struct drm_panel *panel) 127 { 128 struct xpp055c272 *ctx = panel_to_xpp055c272(panel); 129 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 130 struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; 131 132 mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); 133 mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); 134 if (dsi_ctx.accum_err) 135 return dsi_ctx.accum_err; 136 137 regulator_disable(ctx->iovcc); 138 regulator_disable(ctx->vci); 139 140 return 0; 141 } 142 143 static int xpp055c272_prepare(struct drm_panel *panel) 144 { 145 struct xpp055c272 *ctx = panel_to_xpp055c272(panel); 146 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 147 struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; 148 149 dev_dbg(ctx->dev, "Resetting the panel\n"); 150 dsi_ctx.accum_err = regulator_enable(ctx->vci); 151 if (dsi_ctx.accum_err) { 152 dev_err(ctx->dev, "Failed to enable vci supply: %d\n", 153 dsi_ctx.accum_err); 154 return dsi_ctx.accum_err; 155 } 156 dsi_ctx.accum_err = regulator_enable(ctx->iovcc); 157 if (dsi_ctx.accum_err) { 158 dev_err(ctx->dev, "Failed to enable iovcc supply: %d\n", 159 dsi_ctx.accum_err); 160 goto disable_vci; 161 } 162 163 gpiod_set_value_cansleep(ctx->reset_gpio, 1); 164 /* T6: 10us */ 165 usleep_range(10, 20); 166 gpiod_set_value_cansleep(ctx->reset_gpio, 0); 167 168 /* T8: 20ms */ 169 msleep(20); 170 171 xpp055c272_init_sequence(&dsi_ctx); 172 if (!dsi_ctx.accum_err) 173 dev_dbg(ctx->dev, "Panel init sequence done\n"); 174 175 mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); 176 /* T9: 120ms */ 177 mipi_dsi_msleep(&dsi_ctx, 120); 178 mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); 179 180 if (dsi_ctx.accum_err) 181 goto disable_iovcc; 182 183 msleep(50); 184 185 return 0; 186 187 disable_iovcc: 188 regulator_disable(ctx->iovcc); 189 disable_vci: 190 regulator_disable(ctx->vci); 191 return dsi_ctx.accum_err; 192 } 193 194 static const struct drm_display_mode default_mode = { 195 .hdisplay = 720, 196 .hsync_start = 720 + 40, 197 .hsync_end = 720 + 40 + 10, 198 .htotal = 720 + 40 + 10 + 40, 199 .vdisplay = 1280, 200 .vsync_start = 1280 + 22, 201 .vsync_end = 1280 + 22 + 4, 202 .vtotal = 1280 + 22 + 4 + 11, 203 .clock = 64000, 204 .width_mm = 68, 205 .height_mm = 121, 206 }; 207 208 static int xpp055c272_get_modes(struct drm_panel *panel, 209 struct drm_connector *connector) 210 { 211 struct xpp055c272 *ctx = panel_to_xpp055c272(panel); 212 struct drm_display_mode *mode; 213 214 mode = drm_mode_duplicate(connector->dev, &default_mode); 215 if (!mode) { 216 dev_err(ctx->dev, "Failed to add mode %ux%u@%u\n", 217 default_mode.hdisplay, default_mode.vdisplay, 218 drm_mode_vrefresh(&default_mode)); 219 return -ENOMEM; 220 } 221 222 drm_mode_set_name(mode); 223 224 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 225 connector->display_info.width_mm = mode->width_mm; 226 connector->display_info.height_mm = mode->height_mm; 227 drm_mode_probed_add(connector, mode); 228 229 return 1; 230 } 231 232 static const struct drm_panel_funcs xpp055c272_funcs = { 233 .unprepare = xpp055c272_unprepare, 234 .prepare = xpp055c272_prepare, 235 .get_modes = xpp055c272_get_modes, 236 }; 237 238 static int xpp055c272_probe(struct mipi_dsi_device *dsi) 239 { 240 struct device *dev = &dsi->dev; 241 struct xpp055c272 *ctx; 242 int ret; 243 244 ctx = devm_drm_panel_alloc(dev, struct xpp055c272, panel, 245 &xpp055c272_funcs, DRM_MODE_CONNECTOR_DSI); 246 if (IS_ERR(ctx)) 247 return PTR_ERR(ctx); 248 249 ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); 250 if (IS_ERR(ctx->reset_gpio)) 251 return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), 252 "cannot get reset gpio\n"); 253 254 ctx->vci = devm_regulator_get(dev, "vci"); 255 if (IS_ERR(ctx->vci)) 256 return dev_err_probe(dev, PTR_ERR(ctx->vci), 257 "Failed to request vci regulator\n"); 258 259 ctx->iovcc = devm_regulator_get(dev, "iovcc"); 260 if (IS_ERR(ctx->iovcc)) 261 return dev_err_probe(dev, PTR_ERR(ctx->iovcc), 262 "Failed to request iovcc regulator\n"); 263 264 mipi_dsi_set_drvdata(dsi, ctx); 265 266 ctx->dev = dev; 267 268 dsi->lanes = 4; 269 dsi->format = MIPI_DSI_FMT_RGB888; 270 dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | 271 MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET; 272 273 ret = drm_panel_of_backlight(&ctx->panel); 274 if (ret) 275 return ret; 276 277 drm_panel_add(&ctx->panel); 278 279 ret = mipi_dsi_attach(dsi); 280 if (ret < 0) { 281 dev_err(dev, "mipi_dsi_attach failed: %d\n", ret); 282 drm_panel_remove(&ctx->panel); 283 return ret; 284 } 285 286 return 0; 287 } 288 289 static void xpp055c272_remove(struct mipi_dsi_device *dsi) 290 { 291 struct xpp055c272 *ctx = mipi_dsi_get_drvdata(dsi); 292 int ret; 293 294 ret = mipi_dsi_detach(dsi); 295 if (ret < 0) 296 dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); 297 298 drm_panel_remove(&ctx->panel); 299 } 300 301 static const struct of_device_id xpp055c272_of_match[] = { 302 { .compatible = "xinpeng,xpp055c272" }, 303 { /* sentinel */ } 304 }; 305 MODULE_DEVICE_TABLE(of, xpp055c272_of_match); 306 307 static struct mipi_dsi_driver xpp055c272_driver = { 308 .driver = { 309 .name = "panel-xinpeng-xpp055c272", 310 .of_match_table = xpp055c272_of_match, 311 }, 312 .probe = xpp055c272_probe, 313 .remove = xpp055c272_remove, 314 }; 315 module_mipi_dsi_driver(xpp055c272_driver); 316 317 MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>"); 318 MODULE_DESCRIPTION("DRM driver for Xinpeng xpp055c272 MIPI DSI panel"); 319 MODULE_LICENSE("GPL v2"); 320