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