0001
0002
0003
0004
0005
0006 #include <linux/gpio/consumer.h>
0007 #include <linux/delay.h>
0008 #include <linux/mod_devicetable.h>
0009 #include <linux/module.h>
0010 #include <linux/of_device.h>
0011 #include <linux/regulator/consumer.h>
0012
0013 #include <drm/drm_mipi_dsi.h>
0014 #include <drm/drm_modes.h>
0015 #include <drm/drm_panel.h>
0016
0017 #define K101_IM2BA02_INIT_CMD_LEN 2
0018
0019 static const char * const regulator_names[] = {
0020 "dvdd",
0021 "avdd",
0022 "cvdd"
0023 };
0024
0025 struct k101_im2ba02 {
0026 struct drm_panel panel;
0027 struct mipi_dsi_device *dsi;
0028
0029 struct regulator_bulk_data supplies[ARRAY_SIZE(regulator_names)];
0030 struct gpio_desc *reset;
0031 };
0032
0033 static inline struct k101_im2ba02 *panel_to_k101_im2ba02(struct drm_panel *panel)
0034 {
0035 return container_of(panel, struct k101_im2ba02, panel);
0036 }
0037
0038 struct k101_im2ba02_init_cmd {
0039 u8 data[K101_IM2BA02_INIT_CMD_LEN];
0040 };
0041
0042 static const struct k101_im2ba02_init_cmd k101_im2ba02_init_cmds[] = {
0043
0044 { .data = { 0xE0, 0x00 } },
0045
0046
0047 { .data = { 0xE1, 0x93} },
0048 { .data = { 0xE2, 0x65 } },
0049 { .data = { 0xE3, 0xF8 } },
0050
0051
0052 { .data = { 0x80, 0x03 } },
0053
0054
0055 { .data = { 0x70, 0x02 } },
0056 { .data = { 0x71, 0x23 } },
0057 { .data = { 0x72, 0x06 } },
0058
0059
0060 { .data = { 0xE0, 0x01 } },
0061
0062
0063 { .data = { 0x00, 0x00 } },
0064 { .data = { 0x01, 0x66 } },
0065
0066 { .data = { 0x03, 0x00 } },
0067 { .data = { 0x04, 0x25 } },
0068
0069
0070 { .data = { 0x17, 0x00 } },
0071 { .data = { 0x18, 0x6D } },
0072 { .data = { 0x19, 0x00 } },
0073 { .data = { 0x1A, 0x00 } },
0074 { .data = { 0x1B, 0xBF } },
0075 { .data = { 0x1C, 0x00 } },
0076
0077
0078 { .data = { 0x1F, 0x3E } },
0079 { .data = { 0x20, 0x28 } },
0080 { .data = { 0x21, 0x28 } },
0081 { .data = { 0x22, 0x0E } },
0082
0083
0084 { .data = { 0x37, 0x09 } },
0085
0086
0087 { .data = { 0x38, 0x04 } },
0088 { .data = { 0x39, 0x08 } },
0089 { .data = { 0x3A, 0x12 } },
0090 { .data = { 0x3C, 0x78 } },
0091 { .data = { 0x3D, 0xFF } },
0092 { .data = { 0x3E, 0xFF } },
0093 { .data = { 0x3F, 0x7F } },
0094
0095
0096 { .data = { 0x40, 0x06 } },
0097 { .data = { 0x41, 0xA0 } },
0098
0099
0100 { .data = { 0x55, 0x0F } },
0101 { .data = { 0x56, 0x01 } },
0102 { .data = { 0x57, 0x69 } },
0103 { .data = { 0x58, 0x0A } },
0104 { .data = { 0x59, 0x0A } },
0105 { .data = { 0x5A, 0x45 } },
0106 { .data = { 0x5B, 0x15 } },
0107
0108
0109 { .data = { 0x5D, 0x7C } },
0110 { .data = { 0x5E, 0x65 } },
0111 { .data = { 0x5F, 0x55 } },
0112 { .data = { 0x60, 0x49 } },
0113 { .data = { 0x61, 0x44 } },
0114 { .data = { 0x62, 0x35 } },
0115 { .data = { 0x63, 0x3A } },
0116 { .data = { 0x64, 0x23 } },
0117 { .data = { 0x65, 0x3D } },
0118 { .data = { 0x66, 0x3C } },
0119 { .data = { 0x67, 0x3D } },
0120 { .data = { 0x68, 0x5D } },
0121 { .data = { 0x69, 0x4D } },
0122 { .data = { 0x6A, 0x56 } },
0123 { .data = { 0x6B, 0x48 } },
0124 { .data = { 0x6C, 0x45 } },
0125 { .data = { 0x6D, 0x38 } },
0126 { .data = { 0x6E, 0x25 } },
0127 { .data = { 0x6F, 0x00 } },
0128 { .data = { 0x70, 0x7C } },
0129 { .data = { 0x71, 0x65 } },
0130 { .data = { 0x72, 0x55 } },
0131 { .data = { 0x73, 0x49 } },
0132 { .data = { 0x74, 0x44 } },
0133 { .data = { 0x75, 0x35 } },
0134 { .data = { 0x76, 0x3A } },
0135 { .data = { 0x77, 0x23 } },
0136 { .data = { 0x78, 0x3D } },
0137 { .data = { 0x79, 0x3C } },
0138 { .data = { 0x7A, 0x3D } },
0139 { .data = { 0x7B, 0x5D } },
0140 { .data = { 0x7C, 0x4D } },
0141 { .data = { 0x7D, 0x56 } },
0142 { .data = { 0x7E, 0x48 } },
0143 { .data = { 0x7F, 0x45 } },
0144 { .data = { 0x80, 0x38 } },
0145 { .data = { 0x81, 0x25 } },
0146 { .data = { 0x82, 0x00 } },
0147
0148
0149 { .data = { 0xE0, 0x02 } },
0150
0151 { .data = { 0x00, 0x1E } },
0152 { .data = { 0x01, 0x1E } },
0153 { .data = { 0x02, 0x41 } },
0154 { .data = { 0x03, 0x41 } },
0155 { .data = { 0x04, 0x43 } },
0156 { .data = { 0x05, 0x43 } },
0157 { .data = { 0x06, 0x1F } },
0158 { .data = { 0x07, 0x1F } },
0159 { .data = { 0x08, 0x1F } },
0160 { .data = { 0x09, 0x1F } },
0161 { .data = { 0x0A, 0x1E } },
0162 { .data = { 0x0B, 0x1E } },
0163 { .data = { 0x0C, 0x1F } },
0164 { .data = { 0x0D, 0x47 } },
0165 { .data = { 0x0E, 0x47 } },
0166 { .data = { 0x0F, 0x45 } },
0167 { .data = { 0x10, 0x45 } },
0168 { .data = { 0x11, 0x4B } },
0169 { .data = { 0x12, 0x4B } },
0170 { .data = { 0x13, 0x49 } },
0171 { .data = { 0x14, 0x49 } },
0172 { .data = { 0x15, 0x1F } },
0173
0174 { .data = { 0x16, 0x1E } },
0175 { .data = { 0x17, 0x1E } },
0176 { .data = { 0x18, 0x40 } },
0177 { .data = { 0x19, 0x40 } },
0178 { .data = { 0x1A, 0x42 } },
0179 { .data = { 0x1B, 0x42 } },
0180 { .data = { 0x1C, 0x1F } },
0181 { .data = { 0x1D, 0x1F } },
0182 { .data = { 0x1E, 0x1F } },
0183 { .data = { 0x1F, 0x1f } },
0184 { .data = { 0x20, 0x1E } },
0185 { .data = { 0x21, 0x1E } },
0186 { .data = { 0x22, 0x1f } },
0187 { .data = { 0x23, 0x46 } },
0188 { .data = { 0x24, 0x46 } },
0189 { .data = { 0x25, 0x44 } },
0190 { .data = { 0x26, 0x44 } },
0191 { .data = { 0x27, 0x4A } },
0192 { .data = { 0x28, 0x4A } },
0193 { .data = { 0x29, 0x48 } },
0194 { .data = { 0x2A, 0x48 } },
0195 { .data = { 0x2B, 0x1f } },
0196
0197 { .data = { 0x2C, 0x1F } },
0198 { .data = { 0x2D, 0x1F } },
0199 { .data = { 0x2E, 0x42 } },
0200 { .data = { 0x2F, 0x42 } },
0201 { .data = { 0x30, 0x40 } },
0202 { .data = { 0x31, 0x40 } },
0203 { .data = { 0x32, 0x1E } },
0204 { .data = { 0x33, 0x1E } },
0205 { .data = { 0x34, 0x1F } },
0206 { .data = { 0x35, 0x1F } },
0207 { .data = { 0x36, 0x1E } },
0208 { .data = { 0x37, 0x1E } },
0209 { .data = { 0x38, 0x1F } },
0210 { .data = { 0x39, 0x48 } },
0211 { .data = { 0x3A, 0x48 } },
0212 { .data = { 0x3B, 0x4A } },
0213 { .data = { 0x3C, 0x4A } },
0214 { .data = { 0x3D, 0x44 } },
0215 { .data = { 0x3E, 0x44 } },
0216 { .data = { 0x3F, 0x46 } },
0217 { .data = { 0x40, 0x46 } },
0218 { .data = { 0x41, 0x1F } },
0219
0220 { .data = { 0x42, 0x1F } },
0221 { .data = { 0x43, 0x1F } },
0222 { .data = { 0x44, 0x43 } },
0223 { .data = { 0x45, 0x43 } },
0224 { .data = { 0x46, 0x41 } },
0225 { .data = { 0x47, 0x41 } },
0226 { .data = { 0x48, 0x1E } },
0227 { .data = { 0x49, 0x1E } },
0228 { .data = { 0x4A, 0x1E } },
0229 { .data = { 0x4B, 0x1F } },
0230 { .data = { 0x4C, 0x1E } },
0231 { .data = { 0x4D, 0x1E } },
0232 { .data = { 0x4E, 0x1F } },
0233 { .data = { 0x4F, 0x49 } },
0234 { .data = { 0x50, 0x49 } },
0235 { .data = { 0x51, 0x4B } },
0236 { .data = { 0x52, 0x4B } },
0237 { .data = { 0x53, 0x45 } },
0238 { .data = { 0x54, 0x45 } },
0239 { .data = { 0x55, 0x47 } },
0240 { .data = { 0x56, 0x47 } },
0241 { .data = { 0x57, 0x1F } },
0242
0243 { .data = { 0x58, 0x10 } },
0244 { .data = { 0x59, 0x00 } },
0245 { .data = { 0x5A, 0x00 } },
0246 { .data = { 0x5B, 0x30 } },
0247 { .data = { 0x5C, 0x02 } },
0248 { .data = { 0x5D, 0x40 } },
0249 { .data = { 0x5E, 0x01 } },
0250 { .data = { 0x5F, 0x02 } },
0251 { .data = { 0x60, 0x30 } },
0252 { .data = { 0x61, 0x01 } },
0253 { .data = { 0x62, 0x02 } },
0254 { .data = { 0x63, 0x6A } },
0255 { .data = { 0x64, 0x6A } },
0256 { .data = { 0x65, 0x05 } },
0257 { .data = { 0x66, 0x12 } },
0258 { .data = { 0x67, 0x74 } },
0259 { .data = { 0x68, 0x04 } },
0260 { .data = { 0x69, 0x6A } },
0261 { .data = { 0x6A, 0x6A } },
0262 { .data = { 0x6B, 0x08 } },
0263
0264 { .data = { 0x6C, 0x00 } },
0265 { .data = { 0x6D, 0x04 } },
0266 { .data = { 0x6E, 0x04 } },
0267 { .data = { 0x6F, 0x88 } },
0268 { .data = { 0x70, 0x00 } },
0269 { .data = { 0x71, 0x00 } },
0270 { .data = { 0x72, 0x06 } },
0271 { .data = { 0x73, 0x7B } },
0272 { .data = { 0x74, 0x00 } },
0273 { .data = { 0x75, 0x07 } },
0274 { .data = { 0x76, 0x00 } },
0275 { .data = { 0x77, 0x5D } },
0276 { .data = { 0x78, 0x17 } },
0277 { .data = { 0x79, 0x1F } },
0278 { .data = { 0x7A, 0x00 } },
0279 { .data = { 0x7B, 0x00 } },
0280 { .data = { 0x7C, 0x00 } },
0281 { .data = { 0x7D, 0x03 } },
0282 { .data = { 0x7E, 0x7B } },
0283
0284 { .data = { 0xE0, 0x04 } },
0285 { .data = { 0x2B, 0x2B } },
0286 { .data = { 0x2E, 0x44 } },
0287
0288 { .data = { 0xE0, 0x01 } },
0289 { .data = { 0x0E, 0x01 } },
0290
0291 { .data = { 0xE0, 0x03 } },
0292 { .data = { 0x98, 0x2F } },
0293
0294 { .data = { 0xE0, 0x00 } },
0295 { .data = { 0xE6, 0x02 } },
0296 { .data = { 0xE7, 0x02 } },
0297
0298 { .data = { 0x11, 0x00 } },
0299 };
0300
0301 static const struct k101_im2ba02_init_cmd timed_cmds[] = {
0302 { .data = { 0x29, 0x00 } },
0303 { .data = { 0x35, 0x00 } },
0304 };
0305
0306 static int k101_im2ba02_prepare(struct drm_panel *panel)
0307 {
0308 struct k101_im2ba02 *ctx = panel_to_k101_im2ba02(panel);
0309 struct mipi_dsi_device *dsi = ctx->dsi;
0310 unsigned int i;
0311 int ret;
0312
0313 ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
0314 if (ret)
0315 return ret;
0316
0317 msleep(30);
0318
0319 gpiod_set_value(ctx->reset, 1);
0320 msleep(50);
0321
0322 gpiod_set_value(ctx->reset, 0);
0323 msleep(50);
0324
0325 gpiod_set_value(ctx->reset, 1);
0326 msleep(200);
0327
0328 for (i = 0; i < ARRAY_SIZE(k101_im2ba02_init_cmds); i++) {
0329 const struct k101_im2ba02_init_cmd *cmd = &k101_im2ba02_init_cmds[i];
0330
0331 ret = mipi_dsi_dcs_write_buffer(dsi, cmd->data, K101_IM2BA02_INIT_CMD_LEN);
0332 if (ret < 0)
0333 goto powerdown;
0334 }
0335
0336 return 0;
0337
0338 powerdown:
0339 gpiod_set_value(ctx->reset, 0);
0340 msleep(50);
0341
0342 return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
0343 }
0344
0345 static int k101_im2ba02_enable(struct drm_panel *panel)
0346 {
0347 struct k101_im2ba02 *ctx = panel_to_k101_im2ba02(panel);
0348 const struct k101_im2ba02_init_cmd *cmd = &timed_cmds[1];
0349 int ret;
0350
0351 msleep(150);
0352
0353 ret = mipi_dsi_dcs_set_display_on(ctx->dsi);
0354 if (ret < 0)
0355 return ret;
0356
0357 msleep(50);
0358
0359 return mipi_dsi_dcs_write_buffer(ctx->dsi, cmd->data, K101_IM2BA02_INIT_CMD_LEN);
0360 }
0361
0362 static int k101_im2ba02_disable(struct drm_panel *panel)
0363 {
0364 struct k101_im2ba02 *ctx = panel_to_k101_im2ba02(panel);
0365
0366 return mipi_dsi_dcs_set_display_off(ctx->dsi);
0367 }
0368
0369 static int k101_im2ba02_unprepare(struct drm_panel *panel)
0370 {
0371 struct k101_im2ba02 *ctx = panel_to_k101_im2ba02(panel);
0372 int ret;
0373
0374 ret = mipi_dsi_dcs_set_display_off(ctx->dsi);
0375 if (ret < 0)
0376 dev_err(panel->dev, "failed to set display off: %d\n", ret);
0377
0378 ret = mipi_dsi_dcs_enter_sleep_mode(ctx->dsi);
0379 if (ret < 0)
0380 dev_err(panel->dev, "failed to enter sleep mode: %d\n", ret);
0381
0382 msleep(200);
0383
0384 gpiod_set_value(ctx->reset, 0);
0385 msleep(20);
0386
0387 return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
0388 }
0389
0390 static const struct drm_display_mode k101_im2ba02_default_mode = {
0391 .clock = 70000,
0392
0393 .hdisplay = 800,
0394 .hsync_start = 800 + 20,
0395 .hsync_end = 800 + 20 + 20,
0396 .htotal = 800 + 20 + 20 + 20,
0397
0398 .vdisplay = 1280,
0399 .vsync_start = 1280 + 16,
0400 .vsync_end = 1280 + 16 + 4,
0401 .vtotal = 1280 + 16 + 4 + 4,
0402
0403 .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
0404 .width_mm = 136,
0405 .height_mm = 217,
0406 };
0407
0408 static int k101_im2ba02_get_modes(struct drm_panel *panel,
0409 struct drm_connector *connector)
0410 {
0411 struct k101_im2ba02 *ctx = panel_to_k101_im2ba02(panel);
0412 struct drm_display_mode *mode;
0413
0414 mode = drm_mode_duplicate(connector->dev, &k101_im2ba02_default_mode);
0415 if (!mode) {
0416 dev_err(&ctx->dsi->dev, "failed to add mode %ux%u@%u\n",
0417 k101_im2ba02_default_mode.hdisplay,
0418 k101_im2ba02_default_mode.vdisplay,
0419 drm_mode_vrefresh(&k101_im2ba02_default_mode));
0420 return -ENOMEM;
0421 }
0422
0423 drm_mode_set_name(mode);
0424
0425 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
0426 connector->display_info.width_mm = mode->width_mm;
0427 connector->display_info.height_mm = mode->height_mm;
0428 drm_mode_probed_add(connector, mode);
0429
0430 return 1;
0431 }
0432
0433 static const struct drm_panel_funcs k101_im2ba02_funcs = {
0434 .disable = k101_im2ba02_disable,
0435 .unprepare = k101_im2ba02_unprepare,
0436 .prepare = k101_im2ba02_prepare,
0437 .enable = k101_im2ba02_enable,
0438 .get_modes = k101_im2ba02_get_modes,
0439 };
0440
0441 static int k101_im2ba02_dsi_probe(struct mipi_dsi_device *dsi)
0442 {
0443 struct k101_im2ba02 *ctx;
0444 unsigned int i;
0445 int ret;
0446
0447 ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL);
0448 if (!ctx)
0449 return -ENOMEM;
0450
0451 mipi_dsi_set_drvdata(dsi, ctx);
0452 ctx->dsi = dsi;
0453
0454 for (i = 0; i < ARRAY_SIZE(ctx->supplies); i++)
0455 ctx->supplies[i].supply = regulator_names[i];
0456
0457 ret = devm_regulator_bulk_get(&dsi->dev, ARRAY_SIZE(ctx->supplies),
0458 ctx->supplies);
0459 if (ret < 0)
0460 return dev_err_probe(&dsi->dev, ret, "Couldn't get regulators\n");
0461
0462 ctx->reset = devm_gpiod_get(&dsi->dev, "reset", GPIOD_OUT_LOW);
0463 if (IS_ERR(ctx->reset))
0464 return dev_err_probe(&dsi->dev, PTR_ERR(ctx->reset),
0465 "Couldn't get our reset GPIO\n");
0466
0467 drm_panel_init(&ctx->panel, &dsi->dev, &k101_im2ba02_funcs,
0468 DRM_MODE_CONNECTOR_DSI);
0469
0470 ret = drm_panel_of_backlight(&ctx->panel);
0471 if (ret)
0472 return ret;
0473
0474 drm_panel_add(&ctx->panel);
0475
0476 dsi->mode_flags = MIPI_DSI_MODE_VIDEO;
0477 dsi->format = MIPI_DSI_FMT_RGB888;
0478 dsi->lanes = 4;
0479
0480 ret = mipi_dsi_attach(dsi);
0481 if (ret < 0) {
0482 drm_panel_remove(&ctx->panel);
0483 return ret;
0484 }
0485
0486 return 0;
0487 }
0488
0489 static int k101_im2ba02_dsi_remove(struct mipi_dsi_device *dsi)
0490 {
0491 struct k101_im2ba02 *ctx = mipi_dsi_get_drvdata(dsi);
0492
0493 mipi_dsi_detach(dsi);
0494 drm_panel_remove(&ctx->panel);
0495
0496 return 0;
0497 }
0498
0499 static const struct of_device_id k101_im2ba02_of_match[] = {
0500 { .compatible = "feixin,k101-im2ba02", },
0501 { }
0502 };
0503 MODULE_DEVICE_TABLE(of, k101_im2ba02_of_match);
0504
0505 static struct mipi_dsi_driver k101_im2ba02_driver = {
0506 .probe = k101_im2ba02_dsi_probe,
0507 .remove = k101_im2ba02_dsi_remove,
0508 .driver = {
0509 .name = "feixin-k101-im2ba02",
0510 .of_match_table = k101_im2ba02_of_match,
0511 },
0512 };
0513 module_mipi_dsi_driver(k101_im2ba02_driver);
0514
0515 MODULE_AUTHOR("Icenowy Zheng <icenowy@aosc.io>");
0516 MODULE_DESCRIPTION("Feixin K101 IM2BA02 MIPI-DSI LCD panel");
0517 MODULE_LICENSE("GPL");