Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (C) 2020 Theobroma Systems Design und Consulting GmbH
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 }, /* BGR SS GS */
0044     { 0x31, 0x00 }, /* column inversion */
0045     { 0x53, 0xA2 }, /* VCOM1 */
0046     { 0x55, 0xA2 }, /* VCOM2 */
0047     { 0x50, 0x81 }, /* VREG1OUT=5V */
0048     { 0x51, 0x85 }, /* VREG2OUT=-5V */
0049     { 0x62, 0x0D }, /* EQT Time setting */
0050 /*
0051  * The vendor init selected page 1 here _again_
0052  * Is this supposed to be page 2?
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 }, /* VGH_MOD clamp level=15v */
0230     { 0x84, 0x0F }, /* VGH clamp level 15V */
0231     { 0x85, 0x0D }, /* VGL clamp level (-10V) */
0232     { 0x32, 0xAC },
0233     { 0x8C, 0x80 },
0234     { 0x3C, 0xF5 },
0235     { 0xB5, 0x07 }, /* GAMMA OP */
0236     { 0x31, 0x45 }, /* SOURCE OP */
0237     { 0x3A, 0x24 }, /* PS_EN OFF */
0238     { 0x88, 0x33 }, /* LVD */
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      * Init sequence was supplied by the panel vendor without much
0262      * documentation.
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 = &ltk050h3146w_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      * Init sequence was supplied by the panel vendor without much
0384      * documentation.
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     /* vendor code called this without param, where there should be one */
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 = &ltk050h3146w_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     /* T9: 120ms */
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, &ltk050h3146w_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 = &ltk050h3146w_data,
0651     },
0652     {
0653         .compatible = "leadtek,ltk050h3146w-a2",
0654         .data = &ltk050h3146w_a2_data,
0655     },
0656     { /* sentinel */ }
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");