Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Xinpeng xpp055c272 5.5" MIPI-DSI panel driver
0004  * Copyright (C) 2019 Theobroma Systems Design und Consulting GmbH
0005  *
0006  * based on
0007  *
0008  * Rockteck jh057n00900 5.5" MIPI-DSI panel driver
0009  * Copyright (C) Purism SPC 2019
0010  */
0011 
0012 #include <drm/drm_mipi_dsi.h>
0013 #include <drm/drm_modes.h>
0014 #include <drm/drm_panel.h>
0015 
0016 #include <video/display_timing.h>
0017 #include <video/mipi_display.h>
0018 
0019 #include <linux/delay.h>
0020 #include <linux/gpio/consumer.h>
0021 #include <linux/media-bus-format.h>
0022 #include <linux/module.h>
0023 #include <linux/of.h>
0024 #include <linux/regulator/consumer.h>
0025 
0026 /* Manufacturer specific Commands send via DSI */
0027 #define XPP055C272_CMD_ALL_PIXEL_OFF    0x22
0028 #define XPP055C272_CMD_ALL_PIXEL_ON 0x23
0029 #define XPP055C272_CMD_SETDISP      0xb2
0030 #define XPP055C272_CMD_SETRGBIF     0xb3
0031 #define XPP055C272_CMD_SETCYC       0xb4
0032 #define XPP055C272_CMD_SETBGP       0xb5
0033 #define XPP055C272_CMD_SETVCOM      0xb6
0034 #define XPP055C272_CMD_SETOTP       0xb7
0035 #define XPP055C272_CMD_SETPOWER_EXT 0xb8
0036 #define XPP055C272_CMD_SETEXTC      0xb9
0037 #define XPP055C272_CMD_SETMIPI      0xbA
0038 #define XPP055C272_CMD_SETVDC       0xbc
0039 #define XPP055C272_CMD_SETPCR       0xbf
0040 #define XPP055C272_CMD_SETSCR       0xc0
0041 #define XPP055C272_CMD_SETPOWER     0xc1
0042 #define XPP055C272_CMD_SETECO       0xc6
0043 #define XPP055C272_CMD_SETPANEL     0xcc
0044 #define XPP055C272_CMD_SETGAMMA     0xe0
0045 #define XPP055C272_CMD_SETEQ        0xe3
0046 #define XPP055C272_CMD_SETGIP1      0xe9
0047 #define XPP055C272_CMD_SETGIP2      0xea
0048 
0049 struct xpp055c272 {
0050     struct device *dev;
0051     struct drm_panel panel;
0052     struct gpio_desc *reset_gpio;
0053     struct regulator *vci;
0054     struct regulator *iovcc;
0055     bool prepared;
0056 };
0057 
0058 static inline struct xpp055c272 *panel_to_xpp055c272(struct drm_panel *panel)
0059 {
0060     return container_of(panel, struct xpp055c272, panel);
0061 }
0062 
0063 #define dsi_generic_write_seq(dsi, cmd, seq...) do {            \
0064         static const u8 b[] = { cmd, seq };         \
0065         int ret;                        \
0066         ret = mipi_dsi_dcs_write_buffer(dsi, b, ARRAY_SIZE(b)); \
0067         if (ret < 0)                        \
0068             return ret;                 \
0069     } while (0)
0070 
0071 static int xpp055c272_init_sequence(struct xpp055c272 *ctx)
0072 {
0073     struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
0074     struct device *dev = ctx->dev;
0075 
0076     /*
0077      * Init sequence was supplied by the panel vendor without much
0078      * documentation.
0079      */
0080     dsi_generic_write_seq(dsi, XPP055C272_CMD_SETEXTC, 0xf1, 0x12, 0x83);
0081     dsi_generic_write_seq(dsi, XPP055C272_CMD_SETMIPI,
0082                   0x33, 0x81, 0x05, 0xf9, 0x0e, 0x0e, 0x00, 0x00,
0083                   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x25,
0084                   0x00, 0x91, 0x0a, 0x00, 0x00, 0x02, 0x4f, 0x01,
0085                   0x00, 0x00, 0x37);
0086     dsi_generic_write_seq(dsi, XPP055C272_CMD_SETPOWER_EXT, 0x25);
0087     dsi_generic_write_seq(dsi, XPP055C272_CMD_SETPCR, 0x02, 0x11, 0x00);
0088     dsi_generic_write_seq(dsi, XPP055C272_CMD_SETRGBIF,
0089                   0x0c, 0x10, 0x0a, 0x50, 0x03, 0xff, 0x00, 0x00,
0090                   0x00, 0x00);
0091     dsi_generic_write_seq(dsi, XPP055C272_CMD_SETSCR,
0092                   0x73, 0x73, 0x50, 0x50, 0x00, 0x00, 0x08, 0x70,
0093                   0x00);
0094     dsi_generic_write_seq(dsi, XPP055C272_CMD_SETVDC, 0x46);
0095     dsi_generic_write_seq(dsi, XPP055C272_CMD_SETPANEL, 0x0b);
0096     dsi_generic_write_seq(dsi, XPP055C272_CMD_SETCYC, 0x80);
0097     dsi_generic_write_seq(dsi, XPP055C272_CMD_SETDISP, 0xc8, 0x12, 0x30);
0098     dsi_generic_write_seq(dsi, XPP055C272_CMD_SETEQ,
0099                   0x07, 0x07, 0x0B, 0x0B, 0x03, 0x0B, 0x00, 0x00,
0100                   0x00, 0x00, 0xFF, 0x00, 0xC0, 0x10);
0101     dsi_generic_write_seq(dsi, XPP055C272_CMD_SETPOWER,
0102                   0x53, 0x00, 0x1e, 0x1e, 0x77, 0xe1, 0xcc, 0xdd,
0103                   0x67, 0x77, 0x33, 0x33);
0104     dsi_generic_write_seq(dsi, XPP055C272_CMD_SETECO, 0x00, 0x00, 0xff,
0105                   0xff, 0x01, 0xff);
0106     dsi_generic_write_seq(dsi, XPP055C272_CMD_SETBGP, 0x09, 0x09);
0107     msleep(20);
0108 
0109     dsi_generic_write_seq(dsi, XPP055C272_CMD_SETVCOM, 0x87, 0x95);
0110     dsi_generic_write_seq(dsi, XPP055C272_CMD_SETGIP1,
0111                   0xc2, 0x10, 0x05, 0x05, 0x10, 0x05, 0xa0, 0x12,
0112                   0x31, 0x23, 0x3f, 0x81, 0x0a, 0xa0, 0x37, 0x18,
0113                   0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80,
0114                   0x01, 0x00, 0x00, 0x00, 0x48, 0xf8, 0x86, 0x42,
0115                   0x08, 0x88, 0x88, 0x80, 0x88, 0x88, 0x88, 0x58,
0116                   0xf8, 0x87, 0x53, 0x18, 0x88, 0x88, 0x81, 0x88,
0117                   0x88, 0x88, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
0118                   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
0119     dsi_generic_write_seq(dsi, XPP055C272_CMD_SETGIP2,
0120                   0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
0121                   0x00, 0x00, 0x00, 0x00, 0x1f, 0x88, 0x81, 0x35,
0122                   0x78, 0x88, 0x88, 0x85, 0x88, 0x88, 0x88, 0x0f,
0123                   0x88, 0x80, 0x24, 0x68, 0x88, 0x88, 0x84, 0x88,
0124                   0x88, 0x88, 0x23, 0x10, 0x00, 0x00, 0x1c, 0x00,
0125                   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0126                   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x05,
0127                   0xa0, 0x00, 0x00, 0x00, 0x00);
0128     dsi_generic_write_seq(dsi, XPP055C272_CMD_SETGAMMA,
0129                   0x00, 0x06, 0x08, 0x2a, 0x31, 0x3f, 0x38, 0x36,
0130                   0x07, 0x0c, 0x0d, 0x11, 0x13, 0x12, 0x13, 0x11,
0131                   0x18, 0x00, 0x06, 0x08, 0x2a, 0x31, 0x3f, 0x38,
0132                   0x36, 0x07, 0x0c, 0x0d, 0x11, 0x13, 0x12, 0x13,
0133                   0x11, 0x18);
0134 
0135     msleep(60);
0136 
0137     dev_dbg(dev, "Panel init sequence done\n");
0138     return 0;
0139 }
0140 
0141 static int xpp055c272_unprepare(struct drm_panel *panel)
0142 {
0143     struct xpp055c272 *ctx = panel_to_xpp055c272(panel);
0144     struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
0145     int ret;
0146 
0147     if (!ctx->prepared)
0148         return 0;
0149 
0150     ret = mipi_dsi_dcs_set_display_off(dsi);
0151     if (ret < 0)
0152         dev_err(ctx->dev, "failed to set display off: %d\n", ret);
0153 
0154     mipi_dsi_dcs_enter_sleep_mode(dsi);
0155     if (ret < 0) {
0156         dev_err(ctx->dev, "failed to enter sleep mode: %d\n", ret);
0157         return ret;
0158     }
0159 
0160     regulator_disable(ctx->iovcc);
0161     regulator_disable(ctx->vci);
0162 
0163     ctx->prepared = false;
0164 
0165     return 0;
0166 }
0167 
0168 static int xpp055c272_prepare(struct drm_panel *panel)
0169 {
0170     struct xpp055c272 *ctx = panel_to_xpp055c272(panel);
0171     struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
0172     int ret;
0173 
0174     if (ctx->prepared)
0175         return 0;
0176 
0177     dev_dbg(ctx->dev, "Resetting the panel\n");
0178     ret = regulator_enable(ctx->vci);
0179     if (ret < 0) {
0180         dev_err(ctx->dev, "Failed to enable vci supply: %d\n", ret);
0181         return ret;
0182     }
0183     ret = regulator_enable(ctx->iovcc);
0184     if (ret < 0) {
0185         dev_err(ctx->dev, "Failed to enable iovcc supply: %d\n", ret);
0186         goto disable_vci;
0187     }
0188 
0189     gpiod_set_value_cansleep(ctx->reset_gpio, 1);
0190     /* T6: 10us */
0191     usleep_range(10, 20);
0192     gpiod_set_value_cansleep(ctx->reset_gpio, 0);
0193 
0194     /* T8: 20ms */
0195     msleep(20);
0196 
0197     ret = xpp055c272_init_sequence(ctx);
0198     if (ret < 0) {
0199         dev_err(ctx->dev, "Panel init sequence failed: %d\n", ret);
0200         goto disable_iovcc;
0201     }
0202 
0203     ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
0204     if (ret < 0) {
0205         dev_err(ctx->dev, "Failed to exit sleep mode: %d\n", ret);
0206         goto disable_iovcc;
0207     }
0208 
0209     /* T9: 120ms */
0210     msleep(120);
0211 
0212     ret = mipi_dsi_dcs_set_display_on(dsi);
0213     if (ret < 0) {
0214         dev_err(ctx->dev, "Failed to set display on: %d\n", ret);
0215         goto disable_iovcc;
0216     }
0217 
0218     msleep(50);
0219 
0220     ctx->prepared = true;
0221 
0222     return 0;
0223 
0224 disable_iovcc:
0225     regulator_disable(ctx->iovcc);
0226 disable_vci:
0227     regulator_disable(ctx->vci);
0228     return ret;
0229 }
0230 
0231 static const struct drm_display_mode default_mode = {
0232     .hdisplay   = 720,
0233     .hsync_start    = 720 + 40,
0234     .hsync_end  = 720 + 40 + 10,
0235     .htotal     = 720 + 40 + 10 + 40,
0236     .vdisplay   = 1280,
0237     .vsync_start    = 1280 + 22,
0238     .vsync_end  = 1280 + 22 + 4,
0239     .vtotal     = 1280 + 22 + 4 + 11,
0240     .clock      = 64000,
0241     .width_mm   = 68,
0242     .height_mm  = 121,
0243 };
0244 
0245 static int xpp055c272_get_modes(struct drm_panel *panel,
0246                 struct drm_connector *connector)
0247 {
0248     struct xpp055c272 *ctx = panel_to_xpp055c272(panel);
0249     struct drm_display_mode *mode;
0250 
0251     mode = drm_mode_duplicate(connector->dev, &default_mode);
0252     if (!mode) {
0253         dev_err(ctx->dev, "Failed to add mode %ux%u@%u\n",
0254             default_mode.hdisplay, default_mode.vdisplay,
0255             drm_mode_vrefresh(&default_mode));
0256         return -ENOMEM;
0257     }
0258 
0259     drm_mode_set_name(mode);
0260 
0261     mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
0262     connector->display_info.width_mm = mode->width_mm;
0263     connector->display_info.height_mm = mode->height_mm;
0264     drm_mode_probed_add(connector, mode);
0265 
0266     return 1;
0267 }
0268 
0269 static const struct drm_panel_funcs xpp055c272_funcs = {
0270     .unprepare  = xpp055c272_unprepare,
0271     .prepare    = xpp055c272_prepare,
0272     .get_modes  = xpp055c272_get_modes,
0273 };
0274 
0275 static int xpp055c272_probe(struct mipi_dsi_device *dsi)
0276 {
0277     struct device *dev = &dsi->dev;
0278     struct xpp055c272 *ctx;
0279     int ret;
0280 
0281     ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
0282     if (!ctx)
0283         return -ENOMEM;
0284 
0285     ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
0286     if (IS_ERR(ctx->reset_gpio))
0287         return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
0288                      "cannot get reset gpio\n");
0289 
0290     ctx->vci = devm_regulator_get(dev, "vci");
0291     if (IS_ERR(ctx->vci))
0292         return dev_err_probe(dev, PTR_ERR(ctx->vci),
0293                      "Failed to request vci regulator\n");
0294 
0295     ctx->iovcc = devm_regulator_get(dev, "iovcc");
0296     if (IS_ERR(ctx->iovcc))
0297         return dev_err_probe(dev, PTR_ERR(ctx->iovcc),
0298                      "Failed to request iovcc regulator\n");
0299 
0300     mipi_dsi_set_drvdata(dsi, ctx);
0301 
0302     ctx->dev = dev;
0303 
0304     dsi->lanes = 4;
0305     dsi->format = MIPI_DSI_FMT_RGB888;
0306     dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
0307               MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET;
0308 
0309     drm_panel_init(&ctx->panel, &dsi->dev, &xpp055c272_funcs,
0310                DRM_MODE_CONNECTOR_DSI);
0311 
0312     ret = drm_panel_of_backlight(&ctx->panel);
0313     if (ret)
0314         return ret;
0315 
0316     drm_panel_add(&ctx->panel);
0317 
0318     ret = mipi_dsi_attach(dsi);
0319     if (ret < 0) {
0320         dev_err(dev, "mipi_dsi_attach failed: %d\n", ret);
0321         drm_panel_remove(&ctx->panel);
0322         return ret;
0323     }
0324 
0325     return 0;
0326 }
0327 
0328 static void xpp055c272_shutdown(struct mipi_dsi_device *dsi)
0329 {
0330     struct xpp055c272 *ctx = mipi_dsi_get_drvdata(dsi);
0331     int ret;
0332 
0333     ret = drm_panel_unprepare(&ctx->panel);
0334     if (ret < 0)
0335         dev_err(&dsi->dev, "Failed to unprepare panel: %d\n", ret);
0336 
0337     ret = drm_panel_disable(&ctx->panel);
0338     if (ret < 0)
0339         dev_err(&dsi->dev, "Failed to disable panel: %d\n", ret);
0340 }
0341 
0342 static int xpp055c272_remove(struct mipi_dsi_device *dsi)
0343 {
0344     struct xpp055c272 *ctx = mipi_dsi_get_drvdata(dsi);
0345     int ret;
0346 
0347     xpp055c272_shutdown(dsi);
0348 
0349     ret = mipi_dsi_detach(dsi);
0350     if (ret < 0)
0351         dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
0352 
0353     drm_panel_remove(&ctx->panel);
0354 
0355     return 0;
0356 }
0357 
0358 static const struct of_device_id xpp055c272_of_match[] = {
0359     { .compatible = "xinpeng,xpp055c272" },
0360     { /* sentinel */ }
0361 };
0362 MODULE_DEVICE_TABLE(of, xpp055c272_of_match);
0363 
0364 static struct mipi_dsi_driver xpp055c272_driver = {
0365     .driver = {
0366         .name = "panel-xinpeng-xpp055c272",
0367         .of_match_table = xpp055c272_of_match,
0368     },
0369     .probe  = xpp055c272_probe,
0370     .remove = xpp055c272_remove,
0371     .shutdown = xpp055c272_shutdown,
0372 };
0373 module_mipi_dsi_driver(xpp055c272_driver);
0374 
0375 MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@theobroma-systems.com>");
0376 MODULE_DESCRIPTION("DRM driver for Xinpeng xpp055c272 MIPI DSI panel");
0377 MODULE_LICENSE("GPL v2");