panel-mantix-mlaf057we51.c (9398B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Mantix MLAF057WE51 5.7" MIPI-DSI panel driver 4 * 5 * Copyright (C) Purism SPC 2020 6 */ 7 8#include <linux/backlight.h> 9#include <linux/delay.h> 10#include <linux/gpio/consumer.h> 11#include <linux/media-bus-format.h> 12#include <linux/module.h> 13#include <linux/of_device.h> 14#include <linux/regulator/consumer.h> 15 16#include <video/mipi_display.h> 17 18#include <drm/drm_mipi_dsi.h> 19#include <drm/drm_modes.h> 20#include <drm/drm_panel.h> 21 22#define DRV_NAME "panel-mantix-mlaf057we51" 23 24/* Manufacturer specific Commands send via DSI */ 25#define MANTIX_CMD_OTP_STOP_RELOAD_MIPI 0x41 26#define MANTIX_CMD_INT_CANCEL 0x4C 27#define MANTIX_CMD_SPI_FINISH 0x90 28 29struct mantix { 30 struct device *dev; 31 struct drm_panel panel; 32 33 struct gpio_desc *reset_gpio; 34 struct gpio_desc *tp_rstn_gpio; 35 36 struct regulator *avdd; 37 struct regulator *avee; 38 struct regulator *vddi; 39 40 const struct drm_display_mode *default_mode; 41}; 42 43static inline struct mantix *panel_to_mantix(struct drm_panel *panel) 44{ 45 return container_of(panel, struct mantix, panel); 46} 47 48#define dsi_generic_write_seq(dsi, seq...) do { \ 49 static const u8 d[] = { seq }; \ 50 int ret; \ 51 ret = mipi_dsi_generic_write(dsi, d, ARRAY_SIZE(d)); \ 52 if (ret < 0) \ 53 return ret; \ 54 } while (0) 55 56static int mantix_init_sequence(struct mantix *ctx) 57{ 58 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 59 struct device *dev = ctx->dev; 60 61 /* 62 * Init sequence was supplied by the panel vendor. 63 */ 64 dsi_generic_write_seq(dsi, MANTIX_CMD_OTP_STOP_RELOAD_MIPI, 0x5A); 65 66 dsi_generic_write_seq(dsi, MANTIX_CMD_INT_CANCEL, 0x03); 67 dsi_generic_write_seq(dsi, MANTIX_CMD_OTP_STOP_RELOAD_MIPI, 0x5A, 0x03); 68 dsi_generic_write_seq(dsi, 0x80, 0xA9, 0x00); 69 70 dsi_generic_write_seq(dsi, MANTIX_CMD_OTP_STOP_RELOAD_MIPI, 0x5A, 0x09); 71 dsi_generic_write_seq(dsi, 0x80, 0x64, 0x00, 0x64, 0x00, 0x00); 72 msleep(20); 73 74 dsi_generic_write_seq(dsi, MANTIX_CMD_SPI_FINISH, 0xA5); 75 dsi_generic_write_seq(dsi, MANTIX_CMD_OTP_STOP_RELOAD_MIPI, 0x00, 0x2F); 76 msleep(20); 77 78 dev_dbg(dev, "Panel init sequence done\n"); 79 return 0; 80} 81 82static int mantix_enable(struct drm_panel *panel) 83{ 84 struct mantix *ctx = panel_to_mantix(panel); 85 struct device *dev = ctx->dev; 86 struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev); 87 int ret; 88 89 ret = mantix_init_sequence(ctx); 90 if (ret < 0) { 91 dev_err(ctx->dev, "Panel init sequence failed: %d\n", ret); 92 return ret; 93 } 94 95 ret = mipi_dsi_dcs_exit_sleep_mode(dsi); 96 if (ret < 0) { 97 dev_err(dev, "Failed to exit sleep mode\n"); 98 return ret; 99 } 100 msleep(20); 101 102 ret = mipi_dsi_dcs_set_display_on(dsi); 103 if (ret) 104 return ret; 105 usleep_range(10000, 12000); 106 107 ret = mipi_dsi_turn_on_peripheral(dsi); 108 if (ret < 0) { 109 dev_err(dev, "Failed to turn on peripheral\n"); 110 return ret; 111 } 112 113 return 0; 114} 115 116static int mantix_disable(struct drm_panel *panel) 117{ 118 struct mantix *ctx = panel_to_mantix(panel); 119 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 120 int ret; 121 122 ret = mipi_dsi_dcs_set_display_off(dsi); 123 if (ret < 0) 124 dev_err(ctx->dev, "Failed to turn off the display: %d\n", ret); 125 126 ret = mipi_dsi_dcs_enter_sleep_mode(dsi); 127 if (ret < 0) 128 dev_err(ctx->dev, "Failed to enter sleep mode: %d\n", ret); 129 130 131 return 0; 132} 133 134static int mantix_unprepare(struct drm_panel *panel) 135{ 136 struct mantix *ctx = panel_to_mantix(panel); 137 138 gpiod_set_value_cansleep(ctx->tp_rstn_gpio, 1); 139 usleep_range(5000, 6000); 140 gpiod_set_value_cansleep(ctx->reset_gpio, 1); 141 142 regulator_disable(ctx->avee); 143 regulator_disable(ctx->avdd); 144 /* T11 */ 145 usleep_range(5000, 6000); 146 regulator_disable(ctx->vddi); 147 /* T14 */ 148 msleep(50); 149 150 return 0; 151} 152 153static int mantix_prepare(struct drm_panel *panel) 154{ 155 struct mantix *ctx = panel_to_mantix(panel); 156 int ret; 157 158 /* Focaltech FT8006P, section 7.3.1 and 7.3.4 */ 159 dev_dbg(ctx->dev, "Resetting the panel\n"); 160 ret = regulator_enable(ctx->vddi); 161 if (ret < 0) { 162 dev_err(ctx->dev, "Failed to enable vddi supply: %d\n", ret); 163 return ret; 164 } 165 166 /* T1 + T2 */ 167 usleep_range(8000, 10000); 168 169 ret = regulator_enable(ctx->avdd); 170 if (ret < 0) { 171 dev_err(ctx->dev, "Failed to enable avdd supply: %d\n", ret); 172 return ret; 173 } 174 175 /* T2d */ 176 usleep_range(3500, 4000); 177 ret = regulator_enable(ctx->avee); 178 if (ret < 0) { 179 dev_err(ctx->dev, "Failed to enable avee supply: %d\n", ret); 180 return ret; 181 } 182 183 /* T3 + T4 + time for voltage to become stable: */ 184 usleep_range(6000, 7000); 185 gpiod_set_value_cansleep(ctx->reset_gpio, 0); 186 gpiod_set_value_cansleep(ctx->tp_rstn_gpio, 0); 187 188 /* T6 */ 189 msleep(50); 190 191 return 0; 192} 193 194static const struct drm_display_mode default_mode_mantix = { 195 .hdisplay = 720, 196 .hsync_start = 720 + 45, 197 .hsync_end = 720 + 45 + 14, 198 .htotal = 720 + 45 + 14 + 25, 199 .vdisplay = 1440, 200 .vsync_start = 1440 + 130, 201 .vsync_end = 1440 + 130 + 8, 202 .vtotal = 1440 + 130 + 8 + 106, 203 .clock = 85298, 204 .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, 205 .width_mm = 65, 206 .height_mm = 130, 207}; 208 209static const struct drm_display_mode default_mode_ys = { 210 .hdisplay = 720, 211 .hsync_start = 720 + 45, 212 .hsync_end = 720 + 45 + 14, 213 .htotal = 720 + 45 + 14 + 25, 214 .vdisplay = 1440, 215 .vsync_start = 1440 + 175, 216 .vsync_end = 1440 + 175 + 8, 217 .vtotal = 1440 + 175 + 8 + 50, 218 .clock = 85298, 219 .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, 220 .width_mm = 65, 221 .height_mm = 130, 222}; 223 224static const u32 mantix_bus_formats[] = { 225 MEDIA_BUS_FMT_RGB888_1X24, 226}; 227 228static int mantix_get_modes(struct drm_panel *panel, 229 struct drm_connector *connector) 230{ 231 struct mantix *ctx = panel_to_mantix(panel); 232 struct drm_display_mode *mode; 233 234 mode = drm_mode_duplicate(connector->dev, ctx->default_mode); 235 if (!mode) { 236 dev_err(ctx->dev, "Failed to add mode %ux%u@%u\n", 237 ctx->default_mode->hdisplay, ctx->default_mode->vdisplay, 238 drm_mode_vrefresh(ctx->default_mode)); 239 return -ENOMEM; 240 } 241 242 drm_mode_set_name(mode); 243 244 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 245 connector->display_info.width_mm = mode->width_mm; 246 connector->display_info.height_mm = mode->height_mm; 247 drm_mode_probed_add(connector, mode); 248 249 drm_display_info_set_bus_formats(&connector->display_info, 250 mantix_bus_formats, 251 ARRAY_SIZE(mantix_bus_formats)); 252 253 return 1; 254} 255 256static const struct drm_panel_funcs mantix_drm_funcs = { 257 .disable = mantix_disable, 258 .unprepare = mantix_unprepare, 259 .prepare = mantix_prepare, 260 .enable = mantix_enable, 261 .get_modes = mantix_get_modes, 262}; 263 264static int mantix_probe(struct mipi_dsi_device *dsi) 265{ 266 struct device *dev = &dsi->dev; 267 struct mantix *ctx; 268 int ret; 269 270 ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 271 if (!ctx) 272 return -ENOMEM; 273 ctx->default_mode = of_device_get_match_data(dev); 274 275 ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 276 if (IS_ERR(ctx->reset_gpio)) { 277 dev_err(dev, "cannot get reset gpio\n"); 278 return PTR_ERR(ctx->reset_gpio); 279 } 280 281 ctx->tp_rstn_gpio = devm_gpiod_get(dev, "mantix,tp-rstn", GPIOD_OUT_HIGH); 282 if (IS_ERR(ctx->tp_rstn_gpio)) { 283 dev_err(dev, "cannot get tp-rstn gpio\n"); 284 return PTR_ERR(ctx->tp_rstn_gpio); 285 } 286 287 mipi_dsi_set_drvdata(dsi, ctx); 288 ctx->dev = dev; 289 290 dsi->lanes = 4; 291 dsi->format = MIPI_DSI_FMT_RGB888; 292 dsi->mode_flags = MIPI_DSI_MODE_VIDEO | 293 MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_VIDEO_SYNC_PULSE; 294 295 ctx->avdd = devm_regulator_get(dev, "avdd"); 296 if (IS_ERR(ctx->avdd)) 297 return dev_err_probe(dev, PTR_ERR(ctx->avdd), "Failed to request avdd regulator\n"); 298 299 ctx->avee = devm_regulator_get(dev, "avee"); 300 if (IS_ERR(ctx->avee)) 301 return dev_err_probe(dev, PTR_ERR(ctx->avee), "Failed to request avee regulator\n"); 302 303 ctx->vddi = devm_regulator_get(dev, "vddi"); 304 if (IS_ERR(ctx->vddi)) 305 return dev_err_probe(dev, PTR_ERR(ctx->vddi), "Failed to request vddi regulator\n"); 306 307 drm_panel_init(&ctx->panel, dev, &mantix_drm_funcs, 308 DRM_MODE_CONNECTOR_DSI); 309 310 ret = drm_panel_of_backlight(&ctx->panel); 311 if (ret) 312 return ret; 313 314 drm_panel_add(&ctx->panel); 315 316 ret = mipi_dsi_attach(dsi); 317 if (ret < 0) { 318 dev_err(dev, "mipi_dsi_attach failed (%d). Is host ready?\n", ret); 319 drm_panel_remove(&ctx->panel); 320 return ret; 321 } 322 323 dev_info(dev, "%ux%u@%u %ubpp dsi %udl - ready\n", 324 ctx->default_mode->hdisplay, ctx->default_mode->vdisplay, 325 drm_mode_vrefresh(ctx->default_mode), 326 mipi_dsi_pixel_format_to_bpp(dsi->format), dsi->lanes); 327 328 return 0; 329} 330 331static void mantix_shutdown(struct mipi_dsi_device *dsi) 332{ 333 struct mantix *ctx = mipi_dsi_get_drvdata(dsi); 334 335 drm_panel_unprepare(&ctx->panel); 336 drm_panel_disable(&ctx->panel); 337} 338 339static int mantix_remove(struct mipi_dsi_device *dsi) 340{ 341 struct mantix *ctx = mipi_dsi_get_drvdata(dsi); 342 343 mantix_shutdown(dsi); 344 345 mipi_dsi_detach(dsi); 346 drm_panel_remove(&ctx->panel); 347 348 return 0; 349} 350 351static const struct of_device_id mantix_of_match[] = { 352 { .compatible = "mantix,mlaf057we51-x", .data = &default_mode_mantix }, 353 { .compatible = "ys,ys57pss36bh5gq", .data = &default_mode_ys }, 354 { /* sentinel */ } 355}; 356MODULE_DEVICE_TABLE(of, mantix_of_match); 357 358static struct mipi_dsi_driver mantix_driver = { 359 .probe = mantix_probe, 360 .remove = mantix_remove, 361 .shutdown = mantix_shutdown, 362 .driver = { 363 .name = DRV_NAME, 364 .of_match_table = mantix_of_match, 365 }, 366}; 367module_mipi_dsi_driver(mantix_driver); 368 369MODULE_AUTHOR("Guido Günther <agx@sigxcpu.org>"); 370MODULE_DESCRIPTION("DRM driver for Mantix MLAF057WE51-X MIPI DSI panel"); 371MODULE_LICENSE("GPL v2");