Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * NewVision NV3052C IPS LCD panel driver
0004  *
0005  * Copyright (C) 2020, Paul Cercueil <paul@crapouillou.net>
0006  * Copyright (C) 2022, Christophe Branchereau <cbranchereau@gmail.com>
0007  */
0008 
0009 #include <linux/delay.h>
0010 #include <linux/device.h>
0011 #include <linux/gpio/consumer.h>
0012 #include <linux/media-bus-format.h>
0013 #include <linux/module.h>
0014 #include <linux/of_device.h>
0015 #include <linux/regulator/consumer.h>
0016 #include <linux/spi/spi.h>
0017 #include <video/mipi_display.h>
0018 #include <drm/drm_mipi_dbi.h>
0019 #include <drm/drm_modes.h>
0020 #include <drm/drm_panel.h>
0021 
0022 struct nv3052c_panel_info {
0023     const struct drm_display_mode *display_modes;
0024     unsigned int num_modes;
0025     u16 width_mm, height_mm;
0026     u32 bus_format, bus_flags;
0027 };
0028 
0029 struct nv3052c {
0030     struct device *dev;
0031     struct drm_panel panel;
0032     struct mipi_dbi dbi;
0033     const struct nv3052c_panel_info *panel_info;
0034     struct regulator *supply;
0035     struct gpio_desc *reset_gpio;
0036 };
0037 
0038 struct nv3052c_reg {
0039     u8 cmd;
0040     u8 val;
0041 };
0042 
0043 static const struct nv3052c_reg nv3052c_panel_regs[] = {
0044     { 0xff, 0x30 },
0045     { 0xff, 0x52 },
0046     { 0xff, 0x01 },
0047     { 0xe3, 0x00 },
0048     { 0x40, 0x00 },
0049     { 0x03, 0x40 },
0050     { 0x04, 0x00 },
0051     { 0x05, 0x03 },
0052     { 0x08, 0x00 },
0053     { 0x09, 0x07 },
0054     { 0x0a, 0x01 },
0055     { 0x0b, 0x32 },
0056     { 0x0c, 0x32 },
0057     { 0x0d, 0x0b },
0058     { 0x0e, 0x00 },
0059     { 0x23, 0xa0 },
0060     { 0x24, 0x0c },
0061     { 0x25, 0x06 },
0062     { 0x26, 0x14 },
0063     { 0x27, 0x14 },
0064     { 0x38, 0xcc },
0065     { 0x39, 0xd7 },
0066     { 0x3a, 0x4a },
0067     { 0x28, 0x40 },
0068     { 0x29, 0x01 },
0069     { 0x2a, 0xdf },
0070     { 0x49, 0x3c },
0071     { 0x91, 0x77 },
0072     { 0x92, 0x77 },
0073     { 0xa0, 0x55 },
0074     { 0xa1, 0x50 },
0075     { 0xa4, 0x9c },
0076     { 0xa7, 0x02 },
0077     { 0xa8, 0x01 },
0078     { 0xa9, 0x01 },
0079     { 0xaa, 0xfc },
0080     { 0xab, 0x28 },
0081     { 0xac, 0x06 },
0082     { 0xad, 0x06 },
0083     { 0xae, 0x06 },
0084     { 0xaf, 0x03 },
0085     { 0xb0, 0x08 },
0086     { 0xb1, 0x26 },
0087     { 0xb2, 0x28 },
0088     { 0xb3, 0x28 },
0089     { 0xb4, 0x33 },
0090     { 0xb5, 0x08 },
0091     { 0xb6, 0x26 },
0092     { 0xb7, 0x08 },
0093     { 0xb8, 0x26 },
0094     { 0xf0, 0x00 },
0095     { 0xf6, 0xc0 },
0096     { 0xff, 0x30 },
0097     { 0xff, 0x52 },
0098     { 0xff, 0x02 },
0099     { 0xb0, 0x0b },
0100     { 0xb1, 0x16 },
0101     { 0xb2, 0x17 },
0102     { 0xb3, 0x2c },
0103     { 0xb4, 0x32 },
0104     { 0xb5, 0x3b },
0105     { 0xb6, 0x29 },
0106     { 0xb7, 0x40 },
0107     { 0xb8, 0x0d },
0108     { 0xb9, 0x05 },
0109     { 0xba, 0x12 },
0110     { 0xbb, 0x10 },
0111     { 0xbc, 0x12 },
0112     { 0xbd, 0x15 },
0113     { 0xbe, 0x19 },
0114     { 0xbf, 0x0e },
0115     { 0xc0, 0x16 },
0116     { 0xc1, 0x0a },
0117     { 0xd0, 0x0c },
0118     { 0xd1, 0x17 },
0119     { 0xd2, 0x14 },
0120     { 0xd3, 0x2e },
0121     { 0xd4, 0x32 },
0122     { 0xd5, 0x3c },
0123     { 0xd6, 0x22 },
0124     { 0xd7, 0x3d },
0125     { 0xd8, 0x0d },
0126     { 0xd9, 0x07 },
0127     { 0xda, 0x13 },
0128     { 0xdb, 0x13 },
0129     { 0xdc, 0x11 },
0130     { 0xdd, 0x15 },
0131     { 0xde, 0x19 },
0132     { 0xdf, 0x10 },
0133     { 0xe0, 0x17 },
0134     { 0xe1, 0x0a },
0135     { 0xff, 0x30 },
0136     { 0xff, 0x52 },
0137     { 0xff, 0x03 },
0138     { 0x00, 0x2a },
0139     { 0x01, 0x2a },
0140     { 0x02, 0x2a },
0141     { 0x03, 0x2a },
0142     { 0x04, 0x61 },
0143     { 0x05, 0x80 },
0144     { 0x06, 0xc7 },
0145     { 0x07, 0x01 },
0146     { 0x08, 0x03 },
0147     { 0x09, 0x04 },
0148     { 0x70, 0x22 },
0149     { 0x71, 0x80 },
0150     { 0x30, 0x2a },
0151     { 0x31, 0x2a },
0152     { 0x32, 0x2a },
0153     { 0x33, 0x2a },
0154     { 0x34, 0x61 },
0155     { 0x35, 0xc5 },
0156     { 0x36, 0x80 },
0157     { 0x37, 0x23 },
0158     { 0x40, 0x03 },
0159     { 0x41, 0x04 },
0160     { 0x42, 0x05 },
0161     { 0x43, 0x06 },
0162     { 0x44, 0x11 },
0163     { 0x45, 0xe8 },
0164     { 0x46, 0xe9 },
0165     { 0x47, 0x11 },
0166     { 0x48, 0xea },
0167     { 0x49, 0xeb },
0168     { 0x50, 0x07 },
0169     { 0x51, 0x08 },
0170     { 0x52, 0x09 },
0171     { 0x53, 0x0a },
0172     { 0x54, 0x11 },
0173     { 0x55, 0xec },
0174     { 0x56, 0xed },
0175     { 0x57, 0x11 },
0176     { 0x58, 0xef },
0177     { 0x59, 0xf0 },
0178     { 0xb1, 0x01 },
0179     { 0xb4, 0x15 },
0180     { 0xb5, 0x16 },
0181     { 0xb6, 0x09 },
0182     { 0xb7, 0x0f },
0183     { 0xb8, 0x0d },
0184     { 0xb9, 0x0b },
0185     { 0xba, 0x00 },
0186     { 0xc7, 0x02 },
0187     { 0xca, 0x17 },
0188     { 0xcb, 0x18 },
0189     { 0xcc, 0x0a },
0190     { 0xcd, 0x10 },
0191     { 0xce, 0x0e },
0192     { 0xcf, 0x0c },
0193     { 0xd0, 0x00 },
0194     { 0x81, 0x00 },
0195     { 0x84, 0x15 },
0196     { 0x85, 0x16 },
0197     { 0x86, 0x10 },
0198     { 0x87, 0x0a },
0199     { 0x88, 0x0c },
0200     { 0x89, 0x0e },
0201     { 0x8a, 0x02 },
0202     { 0x97, 0x00 },
0203     { 0x9a, 0x17 },
0204     { 0x9b, 0x18 },
0205     { 0x9c, 0x0f },
0206     { 0x9d, 0x09 },
0207     { 0x9e, 0x0b },
0208     { 0x9f, 0x0d },
0209     { 0xa0, 0x01 },
0210     { 0xff, 0x30 },
0211     { 0xff, 0x52 },
0212     { 0xff, 0x02 },
0213     { 0x01, 0x01 },
0214     { 0x02, 0xda },
0215     { 0x03, 0xba },
0216     { 0x04, 0xa8 },
0217     { 0x05, 0x9a },
0218     { 0x06, 0x70 },
0219     { 0x07, 0xff },
0220     { 0x08, 0x91 },
0221     { 0x09, 0x90 },
0222     { 0x0a, 0xff },
0223     { 0x0b, 0x8f },
0224     { 0x0c, 0x60 },
0225     { 0x0d, 0x58 },
0226     { 0x0e, 0x48 },
0227     { 0x0f, 0x38 },
0228     { 0x10, 0x2b },
0229     { 0xff, 0x30 },
0230     { 0xff, 0x52 },
0231     { 0xff, 0x00 },
0232     { 0x36, 0x0a },
0233 };
0234 
0235 static inline struct nv3052c *to_nv3052c(struct drm_panel *panel)
0236 {
0237     return container_of(panel, struct nv3052c, panel);
0238 }
0239 
0240 static int nv3052c_prepare(struct drm_panel *panel)
0241 {
0242     struct nv3052c *priv = to_nv3052c(panel);
0243     struct mipi_dbi *dbi = &priv->dbi;
0244     unsigned int i;
0245     int err;
0246 
0247     err = regulator_enable(priv->supply);
0248     if (err) {
0249         dev_err(priv->dev, "Failed to enable power supply: %d\n", err);
0250         return err;
0251     }
0252 
0253     /* Reset the chip */
0254     gpiod_set_value_cansleep(priv->reset_gpio, 1);
0255     usleep_range(10, 1000);
0256     gpiod_set_value_cansleep(priv->reset_gpio, 0);
0257     usleep_range(5000, 20000);
0258 
0259     for (i = 0; i < ARRAY_SIZE(nv3052c_panel_regs); i++) {
0260         err = mipi_dbi_command(dbi, nv3052c_panel_regs[i].cmd,
0261                        nv3052c_panel_regs[i].val);
0262 
0263         if (err) {
0264             dev_err(priv->dev, "Unable to set register: %d\n", err);
0265             goto err_disable_regulator;
0266         }
0267     }
0268 
0269     err = mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE);
0270     if (err) {
0271         dev_err(priv->dev, "Unable to exit sleep mode: %d\n", err);
0272         goto err_disable_regulator;
0273     }
0274 
0275     return 0;
0276 
0277 err_disable_regulator:
0278     regulator_disable(priv->supply);
0279     return err;
0280 }
0281 
0282 static int nv3052c_unprepare(struct drm_panel *panel)
0283 {
0284     struct nv3052c *priv = to_nv3052c(panel);
0285     struct mipi_dbi *dbi = &priv->dbi;
0286     int err;
0287 
0288     err = mipi_dbi_command(dbi, MIPI_DCS_ENTER_SLEEP_MODE);
0289     if (err)
0290         dev_err(priv->dev, "Unable to enter sleep mode: %d\n", err);
0291 
0292     gpiod_set_value_cansleep(priv->reset_gpio, 1);
0293     regulator_disable(priv->supply);
0294 
0295     return 0;
0296 }
0297 
0298 static int nv3052c_enable(struct drm_panel *panel)
0299 {
0300     struct nv3052c *priv = to_nv3052c(panel);
0301     struct mipi_dbi *dbi = &priv->dbi;
0302     int err;
0303 
0304     err = mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON);
0305     if (err) {
0306         dev_err(priv->dev, "Unable to enable display: %d\n", err);
0307         return err;
0308     }
0309 
0310     if (panel->backlight) {
0311         /* Wait for the picture to be ready before enabling backlight */
0312         msleep(120);
0313     }
0314 
0315     return 0;
0316 }
0317 
0318 static int nv3052c_disable(struct drm_panel *panel)
0319 {
0320     struct nv3052c *priv = to_nv3052c(panel);
0321     struct mipi_dbi *dbi = &priv->dbi;
0322     int err;
0323 
0324     err = mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_OFF);
0325     if (err) {
0326         dev_err(priv->dev, "Unable to disable display: %d\n", err);
0327         return err;
0328     }
0329 
0330     return 0;
0331 }
0332 
0333 static int nv3052c_get_modes(struct drm_panel *panel,
0334                  struct drm_connector *connector)
0335 {
0336     struct nv3052c *priv = to_nv3052c(panel);
0337     const struct nv3052c_panel_info *panel_info = priv->panel_info;
0338     struct drm_display_mode *mode;
0339     unsigned int i;
0340 
0341     for (i = 0; i < panel_info->num_modes; i++) {
0342         mode = drm_mode_duplicate(connector->dev,
0343                       &panel_info->display_modes[i]);
0344         if (!mode)
0345             return -ENOMEM;
0346 
0347         drm_mode_set_name(mode);
0348 
0349         mode->type = DRM_MODE_TYPE_DRIVER;
0350         if (panel_info->num_modes == 1)
0351             mode->type |= DRM_MODE_TYPE_PREFERRED;
0352 
0353         drm_mode_probed_add(connector, mode);
0354     }
0355 
0356     connector->display_info.bpc = 8;
0357     connector->display_info.width_mm = panel_info->width_mm;
0358     connector->display_info.height_mm = panel_info->height_mm;
0359 
0360     drm_display_info_set_bus_formats(&connector->display_info,
0361                      &panel_info->bus_format, 1);
0362     connector->display_info.bus_flags = panel_info->bus_flags;
0363 
0364     return panel_info->num_modes;
0365 }
0366 
0367 static const struct drm_panel_funcs nv3052c_funcs = {
0368     .prepare    = nv3052c_prepare,
0369     .unprepare  = nv3052c_unprepare,
0370     .enable     = nv3052c_enable,
0371     .disable    = nv3052c_disable,
0372     .get_modes  = nv3052c_get_modes,
0373 };
0374 
0375 static int nv3052c_probe(struct spi_device *spi)
0376 {
0377     struct device *dev = &spi->dev;
0378     struct nv3052c *priv;
0379     int err;
0380 
0381     priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
0382     if (!priv)
0383         return -ENOMEM;
0384 
0385     priv->dev = dev;
0386 
0387     priv->panel_info = of_device_get_match_data(dev);
0388     if (!priv->panel_info)
0389         return -EINVAL;
0390 
0391     priv->supply = devm_regulator_get(dev, "power");
0392     if (IS_ERR(priv->supply))
0393         return dev_err_probe(dev, PTR_ERR(priv->supply), "Failed to get power supply\n");
0394 
0395     priv->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
0396     if (IS_ERR(priv->reset_gpio))
0397         return dev_err_probe(dev, PTR_ERR(priv->reset_gpio), "Failed to get reset GPIO\n");
0398 
0399     err = mipi_dbi_spi_init(spi, &priv->dbi, NULL);
0400     if (err)
0401         return dev_err_probe(dev, err, "MIPI DBI init failed\n");
0402 
0403     priv->dbi.read_commands = NULL;
0404 
0405     spi_set_drvdata(spi, priv);
0406 
0407     drm_panel_init(&priv->panel, dev, &nv3052c_funcs,
0408                DRM_MODE_CONNECTOR_DPI);
0409 
0410     err = drm_panel_of_backlight(&priv->panel);
0411     if (err)
0412         return dev_err_probe(dev, err, "Failed to attach backlight\n");
0413 
0414     drm_panel_add(&priv->panel);
0415 
0416     return 0;
0417 }
0418 
0419 static void nv3052c_remove(struct spi_device *spi)
0420 {
0421     struct nv3052c *priv = spi_get_drvdata(spi);
0422 
0423     drm_panel_remove(&priv->panel);
0424     drm_panel_disable(&priv->panel);
0425     drm_panel_unprepare(&priv->panel);
0426 }
0427 
0428 static const struct drm_display_mode ltk035c5444t_modes[] = {
0429     { /* 60 Hz */
0430         .clock = 24000,
0431         .hdisplay = 640,
0432         .hsync_start = 640 + 96,
0433         .hsync_end = 640 + 96 + 16,
0434         .htotal = 640 + 96 + 16 + 48,
0435         .vdisplay = 480,
0436         .vsync_start = 480 + 5,
0437         .vsync_end = 480 + 5 + 2,
0438         .vtotal = 480 + 5 + 2 + 13,
0439         .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
0440     },
0441     { /* 50 Hz */
0442         .clock = 18000,
0443         .hdisplay = 640,
0444         .hsync_start = 640 + 39,
0445         .hsync_end = 640 + 39 + 2,
0446         .htotal = 640 + 39 + 2 + 39,
0447         .vdisplay = 480,
0448         .vsync_start = 480 + 5,
0449         .vsync_end = 480 + 5 + 2,
0450         .vtotal = 480 + 5 + 2 + 13,
0451         .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
0452     },
0453 };
0454 
0455 static const struct nv3052c_panel_info ltk035c5444t_panel_info = {
0456     .display_modes = ltk035c5444t_modes,
0457     .num_modes = ARRAY_SIZE(ltk035c5444t_modes),
0458     .width_mm = 77,
0459     .height_mm = 64,
0460     .bus_format = MEDIA_BUS_FMT_RGB888_1X24,
0461     .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE,
0462 };
0463 
0464 static const struct of_device_id nv3052c_of_match[] = {
0465     { .compatible = "leadtek,ltk035c5444t", .data = &ltk035c5444t_panel_info },
0466     { /* sentinel */ }
0467 };
0468 MODULE_DEVICE_TABLE(of, nv3052c_of_match);
0469 
0470 static struct spi_driver nv3052c_driver = {
0471     .driver = {
0472         .name = "nv3052c",
0473         .of_match_table = nv3052c_of_match,
0474     },
0475     .probe = nv3052c_probe,
0476     .remove = nv3052c_remove,
0477 };
0478 module_spi_driver(nv3052c_driver);
0479 
0480 MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
0481 MODULE_AUTHOR("Christophe Branchereau <cbranchereau@gmail.com>");
0482 MODULE_LICENSE("GPL v2");