Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.7-rc4 424 lines 11 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Rockteck jh057n00900 5.5" MIPI-DSI panel driver 4 * 5 * Copyright (C) Purism SPC 2019 6 */ 7 8#include <linux/debugfs.h> 9#include <linux/delay.h> 10#include <linux/gpio/consumer.h> 11#include <linux/media-bus-format.h> 12#include <linux/mod_devicetable.h> 13#include <linux/module.h> 14#include <linux/regulator/consumer.h> 15 16#include <video/display_timing.h> 17#include <video/mipi_display.h> 18 19#include <drm/drm_mipi_dsi.h> 20#include <drm/drm_modes.h> 21#include <drm/drm_panel.h> 22#include <drm/drm_print.h> 23 24#define DRV_NAME "panel-rocktech-jh057n00900" 25 26/* Manufacturer specific Commands send via DSI */ 27#define ST7703_CMD_ALL_PIXEL_OFF 0x22 28#define ST7703_CMD_ALL_PIXEL_ON 0x23 29#define ST7703_CMD_SETDISP 0xB2 30#define ST7703_CMD_SETRGBIF 0xB3 31#define ST7703_CMD_SETCYC 0xB4 32#define ST7703_CMD_SETBGP 0xB5 33#define ST7703_CMD_SETVCOM 0xB6 34#define ST7703_CMD_SETOTP 0xB7 35#define ST7703_CMD_SETPOWER_EXT 0xB8 36#define ST7703_CMD_SETEXTC 0xB9 37#define ST7703_CMD_SETMIPI 0xBA 38#define ST7703_CMD_SETVDC 0xBC 39#define ST7703_CMD_UNKNOWN0 0xBF 40#define ST7703_CMD_SETSCR 0xC0 41#define ST7703_CMD_SETPOWER 0xC1 42#define ST7703_CMD_SETPANEL 0xCC 43#define ST7703_CMD_SETGAMMA 0xE0 44#define ST7703_CMD_SETEQ 0xE3 45#define ST7703_CMD_SETGIP1 0xE9 46#define ST7703_CMD_SETGIP2 0xEA 47 48struct jh057n { 49 struct device *dev; 50 struct drm_panel panel; 51 struct gpio_desc *reset_gpio; 52 struct regulator *vcc; 53 struct regulator *iovcc; 54 bool prepared; 55 56 struct dentry *debugfs; 57}; 58 59static inline struct jh057n *panel_to_jh057n(struct drm_panel *panel) 60{ 61 return container_of(panel, struct jh057n, panel); 62} 63 64#define dsi_generic_write_seq(dsi, seq...) do { \ 65 static const u8 d[] = { seq }; \ 66 int ret; \ 67 ret = mipi_dsi_generic_write(dsi, d, ARRAY_SIZE(d)); \ 68 if (ret < 0) \ 69 return ret; \ 70 } while (0) 71 72static int jh057n_init_sequence(struct jh057n *ctx) 73{ 74 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 75 struct device *dev = ctx->dev; 76 int ret; 77 78 /* 79 * Init sequence was supplied by the panel vendor. Most of the commands 80 * resemble the ST7703 but the number of parameters often don't match 81 * so it's likely a clone. 82 */ 83 dsi_generic_write_seq(dsi, ST7703_CMD_SETEXTC, 84 0xF1, 0x12, 0x83); 85 dsi_generic_write_seq(dsi, ST7703_CMD_SETRGBIF, 86 0x10, 0x10, 0x05, 0x05, 0x03, 0xFF, 0x00, 0x00, 87 0x00, 0x00); 88 dsi_generic_write_seq(dsi, ST7703_CMD_SETSCR, 89 0x73, 0x73, 0x50, 0x50, 0x00, 0x00, 0x08, 0x70, 90 0x00); 91 dsi_generic_write_seq(dsi, ST7703_CMD_SETVDC, 0x4E); 92 dsi_generic_write_seq(dsi, ST7703_CMD_SETPANEL, 0x0B); 93 dsi_generic_write_seq(dsi, ST7703_CMD_SETCYC, 0x80); 94 dsi_generic_write_seq(dsi, ST7703_CMD_SETDISP, 0xF0, 0x12, 0x30); 95 dsi_generic_write_seq(dsi, ST7703_CMD_SETEQ, 96 0x07, 0x07, 0x0B, 0x0B, 0x03, 0x0B, 0x00, 0x00, 97 0x00, 0x00, 0xFF, 0x00, 0xC0, 0x10); 98 dsi_generic_write_seq(dsi, ST7703_CMD_SETBGP, 0x08, 0x08); 99 msleep(20); 100 101 dsi_generic_write_seq(dsi, ST7703_CMD_SETVCOM, 0x3F, 0x3F); 102 dsi_generic_write_seq(dsi, ST7703_CMD_UNKNOWN0, 0x02, 0x11, 0x00); 103 dsi_generic_write_seq(dsi, ST7703_CMD_SETGIP1, 104 0x82, 0x10, 0x06, 0x05, 0x9E, 0x0A, 0xA5, 0x12, 105 0x31, 0x23, 0x37, 0x83, 0x04, 0xBC, 0x27, 0x38, 106 0x0C, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x00, 107 0x03, 0x00, 0x00, 0x00, 0x75, 0x75, 0x31, 0x88, 108 0x88, 0x88, 0x88, 0x88, 0x88, 0x13, 0x88, 0x64, 109 0x64, 0x20, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 110 0x02, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 111 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); 112 dsi_generic_write_seq(dsi, ST7703_CMD_SETGIP2, 113 0x02, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 114 0x00, 0x00, 0x00, 0x00, 0x02, 0x46, 0x02, 0x88, 115 0x88, 0x88, 0x88, 0x88, 0x88, 0x64, 0x88, 0x13, 116 0x57, 0x13, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 117 0x75, 0x88, 0x23, 0x14, 0x00, 0x00, 0x02, 0x00, 118 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 119 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0A, 120 0xA5, 0x00, 0x00, 0x00, 0x00); 121 dsi_generic_write_seq(dsi, ST7703_CMD_SETGAMMA, 122 0x00, 0x09, 0x0E, 0x29, 0x2D, 0x3C, 0x41, 0x37, 123 0x07, 0x0B, 0x0D, 0x10, 0x11, 0x0F, 0x10, 0x11, 124 0x18, 0x00, 0x09, 0x0E, 0x29, 0x2D, 0x3C, 0x41, 125 0x37, 0x07, 0x0B, 0x0D, 0x10, 0x11, 0x0F, 0x10, 126 0x11, 0x18); 127 msleep(20); 128 129 ret = mipi_dsi_dcs_exit_sleep_mode(dsi); 130 if (ret < 0) { 131 DRM_DEV_ERROR(dev, "Failed to exit sleep mode: %d\n", ret); 132 return ret; 133 } 134 /* Panel is operational 120 msec after reset */ 135 msleep(60); 136 ret = mipi_dsi_dcs_set_display_on(dsi); 137 if (ret) 138 return ret; 139 140 DRM_DEV_DEBUG_DRIVER(dev, "Panel init sequence done\n"); 141 return 0; 142} 143 144static int jh057n_enable(struct drm_panel *panel) 145{ 146 struct jh057n *ctx = panel_to_jh057n(panel); 147 int ret; 148 149 ret = jh057n_init_sequence(ctx); 150 if (ret < 0) { 151 DRM_DEV_ERROR(ctx->dev, "Panel init sequence failed: %d\n", 152 ret); 153 return ret; 154 } 155 156 return 0; 157} 158 159static int jh057n_disable(struct drm_panel *panel) 160{ 161 struct jh057n *ctx = panel_to_jh057n(panel); 162 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 163 164 return mipi_dsi_dcs_set_display_off(dsi); 165} 166 167static int jh057n_unprepare(struct drm_panel *panel) 168{ 169 struct jh057n *ctx = panel_to_jh057n(panel); 170 171 if (!ctx->prepared) 172 return 0; 173 174 regulator_disable(ctx->iovcc); 175 regulator_disable(ctx->vcc); 176 ctx->prepared = false; 177 178 return 0; 179} 180 181static int jh057n_prepare(struct drm_panel *panel) 182{ 183 struct jh057n *ctx = panel_to_jh057n(panel); 184 int ret; 185 186 if (ctx->prepared) 187 return 0; 188 189 DRM_DEV_DEBUG_DRIVER(ctx->dev, "Resetting the panel\n"); 190 ret = regulator_enable(ctx->vcc); 191 if (ret < 0) { 192 DRM_DEV_ERROR(ctx->dev, 193 "Failed to enable vcc supply: %d\n", ret); 194 return ret; 195 } 196 ret = regulator_enable(ctx->iovcc); 197 if (ret < 0) { 198 DRM_DEV_ERROR(ctx->dev, 199 "Failed to enable iovcc supply: %d\n", ret); 200 goto disable_vcc; 201 } 202 203 gpiod_set_value_cansleep(ctx->reset_gpio, 1); 204 usleep_range(20, 40); 205 gpiod_set_value_cansleep(ctx->reset_gpio, 0); 206 msleep(20); 207 208 ctx->prepared = true; 209 210 return 0; 211 212disable_vcc: 213 regulator_disable(ctx->vcc); 214 return ret; 215} 216 217static const struct drm_display_mode default_mode = { 218 .hdisplay = 720, 219 .hsync_start = 720 + 90, 220 .hsync_end = 720 + 90 + 20, 221 .htotal = 720 + 90 + 20 + 20, 222 .vdisplay = 1440, 223 .vsync_start = 1440 + 20, 224 .vsync_end = 1440 + 20 + 4, 225 .vtotal = 1440 + 20 + 4 + 12, 226 .vrefresh = 60, 227 .clock = 75276, 228 .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, 229 .width_mm = 65, 230 .height_mm = 130, 231}; 232 233static int jh057n_get_modes(struct drm_panel *panel, 234 struct drm_connector *connector) 235{ 236 struct jh057n *ctx = panel_to_jh057n(panel); 237 struct drm_display_mode *mode; 238 239 mode = drm_mode_duplicate(connector->dev, &default_mode); 240 if (!mode) { 241 DRM_DEV_ERROR(ctx->dev, "Failed to add mode %ux%u@%u\n", 242 default_mode.hdisplay, default_mode.vdisplay, 243 default_mode.vrefresh); 244 return -ENOMEM; 245 } 246 247 drm_mode_set_name(mode); 248 249 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 250 connector->display_info.width_mm = mode->width_mm; 251 connector->display_info.height_mm = mode->height_mm; 252 drm_mode_probed_add(connector, mode); 253 254 return 1; 255} 256 257static const struct drm_panel_funcs jh057n_drm_funcs = { 258 .disable = jh057n_disable, 259 .unprepare = jh057n_unprepare, 260 .prepare = jh057n_prepare, 261 .enable = jh057n_enable, 262 .get_modes = jh057n_get_modes, 263}; 264 265static int allpixelson_set(void *data, u64 val) 266{ 267 struct jh057n *ctx = data; 268 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); 269 270 DRM_DEV_DEBUG_DRIVER(ctx->dev, "Setting all pixels on\n"); 271 dsi_generic_write_seq(dsi, ST7703_CMD_ALL_PIXEL_ON); 272 msleep(val * 1000); 273 /* Reset the panel to get video back */ 274 drm_panel_disable(&ctx->panel); 275 drm_panel_unprepare(&ctx->panel); 276 drm_panel_prepare(&ctx->panel); 277 drm_panel_enable(&ctx->panel); 278 279 return 0; 280} 281 282DEFINE_SIMPLE_ATTRIBUTE(allpixelson_fops, NULL, 283 allpixelson_set, "%llu\n"); 284 285static void jh057n_debugfs_init(struct jh057n *ctx) 286{ 287 ctx->debugfs = debugfs_create_dir(DRV_NAME, NULL); 288 289 debugfs_create_file("allpixelson", 0600, ctx->debugfs, ctx, 290 &allpixelson_fops); 291} 292 293static void jh057n_debugfs_remove(struct jh057n *ctx) 294{ 295 debugfs_remove_recursive(ctx->debugfs); 296 ctx->debugfs = NULL; 297} 298 299static int jh057n_probe(struct mipi_dsi_device *dsi) 300{ 301 struct device *dev = &dsi->dev; 302 struct jh057n *ctx; 303 int ret; 304 305 ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 306 if (!ctx) 307 return -ENOMEM; 308 309 ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); 310 if (IS_ERR(ctx->reset_gpio)) { 311 DRM_DEV_ERROR(dev, "cannot get reset gpio\n"); 312 return PTR_ERR(ctx->reset_gpio); 313 } 314 315 mipi_dsi_set_drvdata(dsi, ctx); 316 317 ctx->dev = dev; 318 319 dsi->lanes = 4; 320 dsi->format = MIPI_DSI_FMT_RGB888; 321 dsi->mode_flags = MIPI_DSI_MODE_VIDEO | 322 MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_VIDEO_SYNC_PULSE; 323 324 ctx->vcc = devm_regulator_get(dev, "vcc"); 325 if (IS_ERR(ctx->vcc)) { 326 ret = PTR_ERR(ctx->vcc); 327 if (ret != -EPROBE_DEFER) 328 DRM_DEV_ERROR(dev, 329 "Failed to request vcc regulator: %d\n", 330 ret); 331 return ret; 332 } 333 ctx->iovcc = devm_regulator_get(dev, "iovcc"); 334 if (IS_ERR(ctx->iovcc)) { 335 ret = PTR_ERR(ctx->iovcc); 336 if (ret != -EPROBE_DEFER) 337 DRM_DEV_ERROR(dev, 338 "Failed to request iovcc regulator: %d\n", 339 ret); 340 return ret; 341 } 342 343 drm_panel_init(&ctx->panel, dev, &jh057n_drm_funcs, 344 DRM_MODE_CONNECTOR_DSI); 345 346 ret = drm_panel_of_backlight(&ctx->panel); 347 if (ret) 348 return ret; 349 350 drm_panel_add(&ctx->panel); 351 352 ret = mipi_dsi_attach(dsi); 353 if (ret < 0) { 354 DRM_DEV_ERROR(dev, 355 "mipi_dsi_attach failed (%d). Is host ready?\n", 356 ret); 357 drm_panel_remove(&ctx->panel); 358 return ret; 359 } 360 361 DRM_DEV_INFO(dev, "%ux%u@%u %ubpp dsi %udl - ready\n", 362 default_mode.hdisplay, default_mode.vdisplay, 363 default_mode.vrefresh, 364 mipi_dsi_pixel_format_to_bpp(dsi->format), dsi->lanes); 365 366 jh057n_debugfs_init(ctx); 367 return 0; 368} 369 370static void jh057n_shutdown(struct mipi_dsi_device *dsi) 371{ 372 struct jh057n *ctx = mipi_dsi_get_drvdata(dsi); 373 int ret; 374 375 ret = drm_panel_unprepare(&ctx->panel); 376 if (ret < 0) 377 DRM_DEV_ERROR(&dsi->dev, "Failed to unprepare panel: %d\n", 378 ret); 379 380 ret = drm_panel_disable(&ctx->panel); 381 if (ret < 0) 382 DRM_DEV_ERROR(&dsi->dev, "Failed to disable panel: %d\n", 383 ret); 384} 385 386static int jh057n_remove(struct mipi_dsi_device *dsi) 387{ 388 struct jh057n *ctx = mipi_dsi_get_drvdata(dsi); 389 int ret; 390 391 jh057n_shutdown(dsi); 392 393 ret = mipi_dsi_detach(dsi); 394 if (ret < 0) 395 DRM_DEV_ERROR(&dsi->dev, "Failed to detach from DSI host: %d\n", 396 ret); 397 398 drm_panel_remove(&ctx->panel); 399 400 jh057n_debugfs_remove(ctx); 401 402 return 0; 403} 404 405static const struct of_device_id jh057n_of_match[] = { 406 { .compatible = "rocktech,jh057n00900" }, 407 { /* sentinel */ } 408}; 409MODULE_DEVICE_TABLE(of, jh057n_of_match); 410 411static struct mipi_dsi_driver jh057n_driver = { 412 .probe = jh057n_probe, 413 .remove = jh057n_remove, 414 .shutdown = jh057n_shutdown, 415 .driver = { 416 .name = DRV_NAME, 417 .of_match_table = jh057n_of_match, 418 }, 419}; 420module_mipi_dsi_driver(jh057n_driver); 421 422MODULE_AUTHOR("Guido Günther <agx@sigxcpu.org>"); 423MODULE_DESCRIPTION("DRM driver for Rocktech JH057N00900 MIPI DSI panel"); 424MODULE_LICENSE("GPL v2");