0001
0002
0003
0004
0005
0006 #include <linux/delay.h>
0007 #include <linux/gpio/consumer.h>
0008 #include <linux/media-bus-format.h>
0009 #include <linux/module.h>
0010 #include <linux/of.h>
0011 #include <linux/of_device.h>
0012 #include <linux/regulator/consumer.h>
0013
0014 #include <video/display_timing.h>
0015 #include <video/mipi_display.h>
0016
0017 #include <drm/drm_mipi_dsi.h>
0018 #include <drm/drm_modes.h>
0019 #include <drm/drm_panel.h>
0020
0021 struct ltk050h3146w_cmd {
0022 char cmd;
0023 char data;
0024 };
0025
0026 struct ltk050h3146w;
0027 struct ltk050h3146w_desc {
0028 const struct drm_display_mode *mode;
0029 int (*init)(struct ltk050h3146w *ctx);
0030 };
0031
0032 struct ltk050h3146w {
0033 struct device *dev;
0034 struct drm_panel panel;
0035 struct gpio_desc *reset_gpio;
0036 struct regulator *vci;
0037 struct regulator *iovcc;
0038 const struct ltk050h3146w_desc *panel_desc;
0039 bool prepared;
0040 };
0041
0042 static const struct ltk050h3146w_cmd page1_cmds[] = {
0043 { 0x22, 0x0A },
0044 { 0x31, 0x00 },
0045 { 0x53, 0xA2 },
0046 { 0x55, 0xA2 },
0047 { 0x50, 0x81 },
0048 { 0x51, 0x85 },
0049 { 0x62, 0x0D },
0050
0051
0052
0053
0054 { 0xA0, 0x00 },
0055 { 0xA1, 0x1A },
0056 { 0xA2, 0x28 },
0057 { 0xA3, 0x13 },
0058 { 0xA4, 0x16 },
0059 { 0xA5, 0x29 },
0060 { 0xA6, 0x1D },
0061 { 0xA7, 0x1E },
0062 { 0xA8, 0x84 },
0063 { 0xA9, 0x1C },
0064 { 0xAA, 0x28 },
0065 { 0xAB, 0x75 },
0066 { 0xAC, 0x1A },
0067 { 0xAD, 0x19 },
0068 { 0xAE, 0x4D },
0069 { 0xAF, 0x22 },
0070 { 0xB0, 0x28 },
0071 { 0xB1, 0x54 },
0072 { 0xB2, 0x66 },
0073 { 0xB3, 0x39 },
0074 { 0xC0, 0x00 },
0075 { 0xC1, 0x1A },
0076 { 0xC2, 0x28 },
0077 { 0xC3, 0x13 },
0078 { 0xC4, 0x16 },
0079 { 0xC5, 0x29 },
0080 { 0xC6, 0x1D },
0081 { 0xC7, 0x1E },
0082 { 0xC8, 0x84 },
0083 { 0xC9, 0x1C },
0084 { 0xCA, 0x28 },
0085 { 0xCB, 0x75 },
0086 { 0xCC, 0x1A },
0087 { 0xCD, 0x19 },
0088 { 0xCE, 0x4D },
0089 { 0xCF, 0x22 },
0090 { 0xD0, 0x28 },
0091 { 0xD1, 0x54 },
0092 { 0xD2, 0x66 },
0093 { 0xD3, 0x39 },
0094 };
0095
0096 static const struct ltk050h3146w_cmd page3_cmds[] = {
0097 { 0x01, 0x00 },
0098 { 0x02, 0x00 },
0099 { 0x03, 0x73 },
0100 { 0x04, 0x00 },
0101 { 0x05, 0x00 },
0102 { 0x06, 0x0a },
0103 { 0x07, 0x00 },
0104 { 0x08, 0x00 },
0105 { 0x09, 0x01 },
0106 { 0x0a, 0x00 },
0107 { 0x0b, 0x00 },
0108 { 0x0c, 0x01 },
0109 { 0x0d, 0x00 },
0110 { 0x0e, 0x00 },
0111 { 0x0f, 0x1d },
0112 { 0x10, 0x1d },
0113 { 0x11, 0x00 },
0114 { 0x12, 0x00 },
0115 { 0x13, 0x00 },
0116 { 0x14, 0x00 },
0117 { 0x15, 0x00 },
0118 { 0x16, 0x00 },
0119 { 0x17, 0x00 },
0120 { 0x18, 0x00 },
0121 { 0x19, 0x00 },
0122 { 0x1a, 0x00 },
0123 { 0x1b, 0x00 },
0124 { 0x1c, 0x00 },
0125 { 0x1d, 0x00 },
0126 { 0x1e, 0x40 },
0127 { 0x1f, 0x80 },
0128 { 0x20, 0x06 },
0129 { 0x21, 0x02 },
0130 { 0x22, 0x00 },
0131 { 0x23, 0x00 },
0132 { 0x24, 0x00 },
0133 { 0x25, 0x00 },
0134 { 0x26, 0x00 },
0135 { 0x27, 0x00 },
0136 { 0x28, 0x33 },
0137 { 0x29, 0x03 },
0138 { 0x2a, 0x00 },
0139 { 0x2b, 0x00 },
0140 { 0x2c, 0x00 },
0141 { 0x2d, 0x00 },
0142 { 0x2e, 0x00 },
0143 { 0x2f, 0x00 },
0144 { 0x30, 0x00 },
0145 { 0x31, 0x00 },
0146 { 0x32, 0x00 },
0147 { 0x33, 0x00 },
0148 { 0x34, 0x04 },
0149 { 0x35, 0x00 },
0150 { 0x36, 0x00 },
0151 { 0x37, 0x00 },
0152 { 0x38, 0x3C },
0153 { 0x39, 0x35 },
0154 { 0x3A, 0x01 },
0155 { 0x3B, 0x40 },
0156 { 0x3C, 0x00 },
0157 { 0x3D, 0x01 },
0158 { 0x3E, 0x00 },
0159 { 0x3F, 0x00 },
0160 { 0x40, 0x00 },
0161 { 0x41, 0x88 },
0162 { 0x42, 0x00 },
0163 { 0x43, 0x00 },
0164 { 0x44, 0x1F },
0165 { 0x50, 0x01 },
0166 { 0x51, 0x23 },
0167 { 0x52, 0x45 },
0168 { 0x53, 0x67 },
0169 { 0x54, 0x89 },
0170 { 0x55, 0xab },
0171 { 0x56, 0x01 },
0172 { 0x57, 0x23 },
0173 { 0x58, 0x45 },
0174 { 0x59, 0x67 },
0175 { 0x5a, 0x89 },
0176 { 0x5b, 0xab },
0177 { 0x5c, 0xcd },
0178 { 0x5d, 0xef },
0179 { 0x5e, 0x11 },
0180 { 0x5f, 0x01 },
0181 { 0x60, 0x00 },
0182 { 0x61, 0x15 },
0183 { 0x62, 0x14 },
0184 { 0x63, 0x0E },
0185 { 0x64, 0x0F },
0186 { 0x65, 0x0C },
0187 { 0x66, 0x0D },
0188 { 0x67, 0x06 },
0189 { 0x68, 0x02 },
0190 { 0x69, 0x07 },
0191 { 0x6a, 0x02 },
0192 { 0x6b, 0x02 },
0193 { 0x6c, 0x02 },
0194 { 0x6d, 0x02 },
0195 { 0x6e, 0x02 },
0196 { 0x6f, 0x02 },
0197 { 0x70, 0x02 },
0198 { 0x71, 0x02 },
0199 { 0x72, 0x02 },
0200 { 0x73, 0x02 },
0201 { 0x74, 0x02 },
0202 { 0x75, 0x01 },
0203 { 0x76, 0x00 },
0204 { 0x77, 0x14 },
0205 { 0x78, 0x15 },
0206 { 0x79, 0x0E },
0207 { 0x7a, 0x0F },
0208 { 0x7b, 0x0C },
0209 { 0x7c, 0x0D },
0210 { 0x7d, 0x06 },
0211 { 0x7e, 0x02 },
0212 { 0x7f, 0x07 },
0213 { 0x80, 0x02 },
0214 { 0x81, 0x02 },
0215 { 0x82, 0x02 },
0216 { 0x83, 0x02 },
0217 { 0x84, 0x02 },
0218 { 0x85, 0x02 },
0219 { 0x86, 0x02 },
0220 { 0x87, 0x02 },
0221 { 0x88, 0x02 },
0222 { 0x89, 0x02 },
0223 { 0x8A, 0x02 },
0224 };
0225
0226 static const struct ltk050h3146w_cmd page4_cmds[] = {
0227 { 0x70, 0x00 },
0228 { 0x71, 0x00 },
0229 { 0x82, 0x0F },
0230 { 0x84, 0x0F },
0231 { 0x85, 0x0D },
0232 { 0x32, 0xAC },
0233 { 0x8C, 0x80 },
0234 { 0x3C, 0xF5 },
0235 { 0xB5, 0x07 },
0236 { 0x31, 0x45 },
0237 { 0x3A, 0x24 },
0238 { 0x88, 0x33 },
0239 };
0240
0241 static inline
0242 struct ltk050h3146w *panel_to_ltk050h3146w(struct drm_panel *panel)
0243 {
0244 return container_of(panel, struct ltk050h3146w, panel);
0245 }
0246
0247 #define dsi_dcs_write_seq(dsi, cmd, seq...) do { \
0248 static const u8 b[] = { cmd, seq }; \
0249 int ret; \
0250 ret = mipi_dsi_dcs_write_buffer(dsi, b, ARRAY_SIZE(b)); \
0251 if (ret < 0) \
0252 return ret; \
0253 } while (0)
0254
0255 static int ltk050h3146w_init_sequence(struct ltk050h3146w *ctx)
0256 {
0257 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
0258 int ret;
0259
0260
0261
0262
0263
0264 dsi_dcs_write_seq(dsi, 0xdf, 0x93, 0x65, 0xf8);
0265 dsi_dcs_write_seq(dsi, 0xb0, 0x01, 0x03, 0x02, 0x00, 0x64, 0x06,
0266 0x01);
0267 dsi_dcs_write_seq(dsi, 0xb2, 0x00, 0xb5);
0268 dsi_dcs_write_seq(dsi, 0xb3, 0x00, 0xb5);
0269 dsi_dcs_write_seq(dsi, 0xb7, 0x00, 0xbf, 0x00, 0x00, 0xbf, 0x00);
0270
0271 dsi_dcs_write_seq(dsi, 0xb9, 0x00, 0xc4, 0x23, 0x07);
0272 dsi_dcs_write_seq(dsi, 0xbb, 0x02, 0x01, 0x24, 0x00, 0x28, 0x0f,
0273 0x28, 0x04, 0xcc, 0xcc, 0xcc);
0274 dsi_dcs_write_seq(dsi, 0xbc, 0x0f, 0x04);
0275 dsi_dcs_write_seq(dsi, 0xbe, 0x1e, 0xf2);
0276 dsi_dcs_write_seq(dsi, 0xc0, 0x26, 0x03);
0277 dsi_dcs_write_seq(dsi, 0xc1, 0x00, 0x12);
0278 dsi_dcs_write_seq(dsi, 0xc3, 0x04, 0x02, 0x02, 0x76, 0x01, 0x80,
0279 0x80);
0280 dsi_dcs_write_seq(dsi, 0xc4, 0x24, 0x80, 0xb4, 0x81, 0x12, 0x0f,
0281 0x16, 0x00, 0x00);
0282 dsi_dcs_write_seq(dsi, 0xc8, 0x7f, 0x72, 0x67, 0x5d, 0x5d, 0x50,
0283 0x56, 0x41, 0x59, 0x57, 0x55, 0x70, 0x5b, 0x5f,
0284 0x4f, 0x47, 0x38, 0x23, 0x08, 0x7f, 0x72, 0x67,
0285 0x5d, 0x5d, 0x50, 0x56, 0x41, 0x59, 0x57, 0x55,
0286 0x70, 0x5b, 0x5f, 0x4f, 0x47, 0x38, 0x23, 0x08);
0287 dsi_dcs_write_seq(dsi, 0xd0, 0x1e, 0x1f, 0x57, 0x58, 0x48, 0x4a,
0288 0x44, 0x46, 0x40, 0x1f, 0x42, 0x1f, 0x1f, 0x1f,
0289 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
0290 dsi_dcs_write_seq(dsi, 0xd1, 0x1e, 0x1f, 0x57, 0x58, 0x49, 0x4b,
0291 0x45, 0x47, 0x41, 0x1f, 0x43, 0x1f, 0x1f, 0x1f,
0292 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
0293 dsi_dcs_write_seq(dsi, 0xd2, 0x1f, 0x1e, 0x17, 0x18, 0x07, 0x05,
0294 0x0b, 0x09, 0x03, 0x1f, 0x01, 0x1f, 0x1f, 0x1f,
0295 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
0296 dsi_dcs_write_seq(dsi, 0xd3, 0x1f, 0x1e, 0x17, 0x18, 0x06, 0x04,
0297 0x0a, 0x08, 0x02, 0x1f, 0x00, 0x1f, 0x1f, 0x1f,
0298 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f);
0299 dsi_dcs_write_seq(dsi, 0xd4, 0x00, 0x00, 0x00, 0x0c, 0x06, 0x20,
0300 0x01, 0x02, 0x00, 0x60, 0x15, 0xb0, 0x30, 0x03,
0301 0x04, 0x00, 0x60, 0x72, 0x0a, 0x00, 0x60, 0x08);
0302 dsi_dcs_write_seq(dsi, 0xd5, 0x00, 0x06, 0x06, 0x00, 0x30, 0x00,
0303 0x00, 0x00, 0x00, 0x00, 0xbc, 0x50, 0x00, 0x05,
0304 0x21, 0x00, 0x60);
0305 dsi_dcs_write_seq(dsi, 0xdd, 0x2c, 0xa3, 0x00);
0306 dsi_dcs_write_seq(dsi, 0xde, 0x02);
0307 dsi_dcs_write_seq(dsi, 0xb2, 0x32, 0x1c);
0308 dsi_dcs_write_seq(dsi, 0xb7, 0x3b, 0x70, 0x00, 0x04);
0309 dsi_dcs_write_seq(dsi, 0xc1, 0x11);
0310 dsi_dcs_write_seq(dsi, 0xbb, 0x21, 0x22, 0x23, 0x24, 0x36, 0x37);
0311 dsi_dcs_write_seq(dsi, 0xc2, 0x20, 0x38, 0x1e, 0x84);
0312 dsi_dcs_write_seq(dsi, 0xde, 0x00);
0313
0314 ret = mipi_dsi_dcs_set_tear_on(dsi, 1);
0315 if (ret < 0) {
0316 dev_err(ctx->dev, "failed to set tear on: %d\n", ret);
0317 return ret;
0318 }
0319
0320 msleep(60);
0321
0322 return 0;
0323 }
0324
0325 static const struct drm_display_mode ltk050h3146w_mode = {
0326 .hdisplay = 720,
0327 .hsync_start = 720 + 42,
0328 .hsync_end = 720 + 42 + 8,
0329 .htotal = 720 + 42 + 8 + 42,
0330 .vdisplay = 1280,
0331 .vsync_start = 1280 + 12,
0332 .vsync_end = 1280 + 12 + 4,
0333 .vtotal = 1280 + 12 + 4 + 18,
0334 .clock = 64018,
0335 .width_mm = 62,
0336 .height_mm = 110,
0337 };
0338
0339 static const struct ltk050h3146w_desc ltk050h3146w_data = {
0340 .mode = <k050h3146w_mode,
0341 .init = ltk050h3146w_init_sequence,
0342 };
0343
0344 static int ltk050h3146w_a2_select_page(struct ltk050h3146w *ctx, int page)
0345 {
0346 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
0347 u8 d[3] = { 0x98, 0x81, page };
0348
0349 return mipi_dsi_dcs_write(dsi, 0xff, d, ARRAY_SIZE(d));
0350 }
0351
0352 static int ltk050h3146w_a2_write_page(struct ltk050h3146w *ctx, int page,
0353 const struct ltk050h3146w_cmd *cmds,
0354 int num)
0355 {
0356 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
0357 int i, ret;
0358
0359 ret = ltk050h3146w_a2_select_page(ctx, page);
0360 if (ret < 0) {
0361 dev_err(ctx->dev, "failed to select page %d: %d\n", page, ret);
0362 return ret;
0363 }
0364
0365 for (i = 0; i < num; i++) {
0366 ret = mipi_dsi_generic_write(dsi, &cmds[i],
0367 sizeof(struct ltk050h3146w_cmd));
0368 if (ret < 0) {
0369 dev_err(ctx->dev, "failed to write page %d init cmds: %d\n", page, ret);
0370 return ret;
0371 }
0372 }
0373
0374 return 0;
0375 }
0376
0377 static int ltk050h3146w_a2_init_sequence(struct ltk050h3146w *ctx)
0378 {
0379 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
0380 int ret;
0381
0382
0383
0384
0385
0386 ret = ltk050h3146w_a2_write_page(ctx, 3, page3_cmds,
0387 ARRAY_SIZE(page3_cmds));
0388 if (ret < 0)
0389 return ret;
0390
0391 ret = ltk050h3146w_a2_write_page(ctx, 4, page4_cmds,
0392 ARRAY_SIZE(page4_cmds));
0393 if (ret < 0)
0394 return ret;
0395
0396 ret = ltk050h3146w_a2_write_page(ctx, 1, page1_cmds,
0397 ARRAY_SIZE(page1_cmds));
0398 if (ret < 0)
0399 return ret;
0400
0401 ret = ltk050h3146w_a2_select_page(ctx, 0);
0402 if (ret < 0) {
0403 dev_err(ctx->dev, "failed to select page 0: %d\n", ret);
0404 return ret;
0405 }
0406
0407
0408 ret = mipi_dsi_dcs_set_tear_on(dsi, 0);
0409 if (ret < 0) {
0410 dev_err(ctx->dev, "failed to set tear on: %d\n", ret);
0411 return ret;
0412 }
0413
0414 msleep(60);
0415
0416 return 0;
0417 }
0418
0419 static const struct drm_display_mode ltk050h3146w_a2_mode = {
0420 .hdisplay = 720,
0421 .hsync_start = 720 + 42,
0422 .hsync_end = 720 + 42 + 10,
0423 .htotal = 720 + 42 + 10 + 60,
0424 .vdisplay = 1280,
0425 .vsync_start = 1280 + 18,
0426 .vsync_end = 1280 + 18 + 4,
0427 .vtotal = 1280 + 18 + 4 + 12,
0428 .clock = 65595,
0429 .width_mm = 62,
0430 .height_mm = 110,
0431 };
0432
0433 static const struct ltk050h3146w_desc ltk050h3146w_a2_data = {
0434 .mode = <k050h3146w_a2_mode,
0435 .init = ltk050h3146w_a2_init_sequence,
0436 };
0437
0438 static int ltk050h3146w_unprepare(struct drm_panel *panel)
0439 {
0440 struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel);
0441 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
0442 int ret;
0443
0444 if (!ctx->prepared)
0445 return 0;
0446
0447 ret = mipi_dsi_dcs_set_display_off(dsi);
0448 if (ret < 0) {
0449 dev_err(ctx->dev, "failed to set display off: %d\n", ret);
0450 return ret;
0451 }
0452
0453 mipi_dsi_dcs_enter_sleep_mode(dsi);
0454 if (ret < 0) {
0455 dev_err(ctx->dev, "failed to enter sleep mode: %d\n", ret);
0456 return ret;
0457 }
0458
0459 regulator_disable(ctx->iovcc);
0460 regulator_disable(ctx->vci);
0461
0462 ctx->prepared = false;
0463
0464 return 0;
0465 }
0466
0467 static int ltk050h3146w_prepare(struct drm_panel *panel)
0468 {
0469 struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel);
0470 struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
0471 int ret;
0472
0473 if (ctx->prepared)
0474 return 0;
0475
0476 dev_dbg(ctx->dev, "Resetting the panel\n");
0477 ret = regulator_enable(ctx->vci);
0478 if (ret < 0) {
0479 dev_err(ctx->dev, "Failed to enable vci supply: %d\n", ret);
0480 return ret;
0481 }
0482 ret = regulator_enable(ctx->iovcc);
0483 if (ret < 0) {
0484 dev_err(ctx->dev, "Failed to enable iovcc supply: %d\n", ret);
0485 goto disable_vci;
0486 }
0487
0488 gpiod_set_value_cansleep(ctx->reset_gpio, 1);
0489 usleep_range(5000, 6000);
0490 gpiod_set_value_cansleep(ctx->reset_gpio, 0);
0491 msleep(20);
0492
0493 ret = ctx->panel_desc->init(ctx);
0494 if (ret < 0) {
0495 dev_err(ctx->dev, "Panel init sequence failed: %d\n", ret);
0496 goto disable_iovcc;
0497 }
0498
0499 ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
0500 if (ret < 0) {
0501 dev_err(ctx->dev, "Failed to exit sleep mode: %d\n", ret);
0502 goto disable_iovcc;
0503 }
0504
0505
0506 msleep(120);
0507
0508 ret = mipi_dsi_dcs_set_display_on(dsi);
0509 if (ret < 0) {
0510 dev_err(ctx->dev, "Failed to set display on: %d\n", ret);
0511 goto disable_iovcc;
0512 }
0513
0514 msleep(50);
0515
0516 ctx->prepared = true;
0517
0518 return 0;
0519
0520 disable_iovcc:
0521 regulator_disable(ctx->iovcc);
0522 disable_vci:
0523 regulator_disable(ctx->vci);
0524 return ret;
0525 }
0526
0527 static int ltk050h3146w_get_modes(struct drm_panel *panel,
0528 struct drm_connector *connector)
0529 {
0530 struct ltk050h3146w *ctx = panel_to_ltk050h3146w(panel);
0531 struct drm_display_mode *mode;
0532
0533 mode = drm_mode_duplicate(connector->dev, ctx->panel_desc->mode);
0534 if (!mode)
0535 return -ENOMEM;
0536
0537 drm_mode_set_name(mode);
0538
0539 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
0540 connector->display_info.width_mm = mode->width_mm;
0541 connector->display_info.height_mm = mode->height_mm;
0542 drm_mode_probed_add(connector, mode);
0543
0544 return 1;
0545 }
0546
0547 static const struct drm_panel_funcs ltk050h3146w_funcs = {
0548 .unprepare = ltk050h3146w_unprepare,
0549 .prepare = ltk050h3146w_prepare,
0550 .get_modes = ltk050h3146w_get_modes,
0551 };
0552
0553 static int ltk050h3146w_probe(struct mipi_dsi_device *dsi)
0554 {
0555 struct device *dev = &dsi->dev;
0556 struct ltk050h3146w *ctx;
0557 int ret;
0558
0559 ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
0560 if (!ctx)
0561 return -ENOMEM;
0562
0563 ctx->panel_desc = of_device_get_match_data(dev);
0564 if (!ctx->panel_desc)
0565 return -EINVAL;
0566
0567 ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
0568 if (IS_ERR(ctx->reset_gpio)) {
0569 dev_err(dev, "cannot get reset gpio\n");
0570 return PTR_ERR(ctx->reset_gpio);
0571 }
0572
0573 ctx->vci = devm_regulator_get(dev, "vci");
0574 if (IS_ERR(ctx->vci)) {
0575 ret = PTR_ERR(ctx->vci);
0576 if (ret != -EPROBE_DEFER)
0577 dev_err(dev, "Failed to request vci regulator: %d\n", ret);
0578 return ret;
0579 }
0580
0581 ctx->iovcc = devm_regulator_get(dev, "iovcc");
0582 if (IS_ERR(ctx->iovcc)) {
0583 ret = PTR_ERR(ctx->iovcc);
0584 if (ret != -EPROBE_DEFER)
0585 dev_err(dev, "Failed to request iovcc regulator: %d\n", ret);
0586 return ret;
0587 }
0588
0589 mipi_dsi_set_drvdata(dsi, ctx);
0590
0591 ctx->dev = dev;
0592
0593 dsi->lanes = 4;
0594 dsi->format = MIPI_DSI_FMT_RGB888;
0595 dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
0596 MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET;
0597
0598 drm_panel_init(&ctx->panel, &dsi->dev, <k050h3146w_funcs,
0599 DRM_MODE_CONNECTOR_DSI);
0600
0601 ret = drm_panel_of_backlight(&ctx->panel);
0602 if (ret)
0603 return ret;
0604
0605 drm_panel_add(&ctx->panel);
0606
0607 ret = mipi_dsi_attach(dsi);
0608 if (ret < 0) {
0609 dev_err(dev, "mipi_dsi_attach failed: %d\n", ret);
0610 drm_panel_remove(&ctx->panel);
0611 return ret;
0612 }
0613
0614 return 0;
0615 }
0616
0617 static void ltk050h3146w_shutdown(struct mipi_dsi_device *dsi)
0618 {
0619 struct ltk050h3146w *ctx = mipi_dsi_get_drvdata(dsi);
0620 int ret;
0621
0622 ret = drm_panel_unprepare(&ctx->panel);
0623 if (ret < 0)
0624 dev_err(&dsi->dev, "Failed to unprepare panel: %d\n", ret);
0625
0626 ret = drm_panel_disable(&ctx->panel);
0627 if (ret < 0)
0628 dev_err(&dsi->dev, "Failed to disable panel: %d\n", ret);
0629 }
0630
0631 static int ltk050h3146w_remove(struct mipi_dsi_device *dsi)
0632 {
0633 struct ltk050h3146w *ctx = mipi_dsi_get_drvdata(dsi);
0634 int ret;
0635
0636 ltk050h3146w_shutdown(dsi);
0637
0638 ret = mipi_dsi_detach(dsi);
0639 if (ret < 0)
0640 dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
0641
0642 drm_panel_remove(&ctx->panel);
0643
0644 return 0;
0645 }
0646
0647 static const struct of_device_id ltk050h3146w_of_match[] = {
0648 {
0649 .compatible = "leadtek,ltk050h3146w",
0650 .data = <k050h3146w_data,
0651 },
0652 {
0653 .compatible = "leadtek,ltk050h3146w-a2",
0654 .data = <k050h3146w_a2_data,
0655 },
0656 { }
0657 };
0658 MODULE_DEVICE_TABLE(of, ltk050h3146w_of_match);
0659
0660 static struct mipi_dsi_driver ltk050h3146w_driver = {
0661 .driver = {
0662 .name = "panel-leadtek-ltk050h3146w",
0663 .of_match_table = ltk050h3146w_of_match,
0664 },
0665 .probe = ltk050h3146w_probe,
0666 .remove = ltk050h3146w_remove,
0667 .shutdown = ltk050h3146w_shutdown,
0668 };
0669 module_mipi_dsi_driver(ltk050h3146w_driver);
0670
0671 MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>");
0672 MODULE_DESCRIPTION("DRM driver for Leadtek LTK050H3146W MIPI DSI panel");
0673 MODULE_LICENSE("GPL v2");