Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Copyright (C) 2019-2020 Icenowy Zheng <icenowy@aosc.io>
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     /* Switch to page 0 */
0044     { .data = { 0xE0, 0x00 } },
0045 
0046     /* Seems to be some password */
0047     { .data = { 0xE1, 0x93} },
0048     { .data = { 0xE2, 0x65 } },
0049     { .data = { 0xE3, 0xF8 } },
0050 
0051     /* Lane number, 0x02 - 3 lanes, 0x03 - 4 lanes */
0052     { .data = { 0x80, 0x03 } },
0053 
0054     /* Sequence control */
0055     { .data = { 0x70, 0x02 } },
0056     { .data = { 0x71, 0x23 } },
0057     { .data = { 0x72, 0x06 } },
0058 
0059     /* Switch to page 1 */
0060     { .data = { 0xE0, 0x01 } },
0061 
0062     /* Set VCOM */
0063     { .data = { 0x00, 0x00 } },
0064     { .data = { 0x01, 0x66 } },
0065     /* Set VCOM_Reverse */
0066     { .data = { 0x03, 0x00 } },
0067     { .data = { 0x04, 0x25 } },
0068 
0069     /* Set Gamma Power, VG[MS][PN] */
0070     { .data = { 0x17, 0x00 } },
0071     { .data = { 0x18, 0x6D } },
0072     { .data = { 0x19, 0x00 } },
0073     { .data = { 0x1A, 0x00 } },
0074     { .data = { 0x1B, 0xBF } }, /* VGMN = -4.5V */
0075     { .data = { 0x1C, 0x00 } },
0076 
0077     /* Set Gate Power */
0078     { .data = { 0x1F, 0x3E } }, /* VGH_R = 15V */
0079     { .data = { 0x20, 0x28 } }, /* VGL_R = -11V */
0080     { .data = { 0x21, 0x28 } }, /* VGL_R2 = -11V */
0081     { .data = { 0x22, 0x0E } }, /* PA[6:4] = 0, PA[0] = 0 */
0082 
0083     /* Set Panel */
0084     { .data = { 0x37, 0x09 } }, /* SS = 1, BGR = 1 */
0085 
0086     /* Set RGBCYC */
0087     { .data = { 0x38, 0x04 } }, /* JDT = 100 column inversion */
0088     { .data = { 0x39, 0x08 } }, /* RGB_N_EQ1 */
0089     { .data = { 0x3A, 0x12 } }, /* RGB_N_EQ2 */
0090     { .data = { 0x3C, 0x78 } }, /* set EQ3 for TE_H */
0091     { .data = { 0x3D, 0xFF } }, /* set CHGEN_ON */
0092     { .data = { 0x3E, 0xFF } }, /* set CHGEN_OFF */
0093     { .data = { 0x3F, 0x7F } }, /* set CHGEN_OFF2 */
0094 
0095     /* Set TCON parameter */
0096     { .data = { 0x40, 0x06 } }, /* RSO = 800 points */
0097     { .data = { 0x41, 0xA0 } }, /* LN = 1280 lines */
0098 
0099     /* Set power voltage */
0100     { .data = { 0x55, 0x0F } }, /* DCDCM */
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     /* Set gamma */
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     /* Switch to page 2, for GIP */
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     { /* sentinel */ }
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");