0001
0002
0003
0004
0005
0006
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
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
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 {
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 {
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 = <k035c5444t_panel_info },
0466 { }
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");