0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020 #include <linux/backlight.h>
0021 #include <linux/delay.h>
0022 #include <linux/gpio/consumer.h>
0023 #include <linux/jiffies.h>
0024 #include <linux/module.h>
0025 #include <linux/mutex.h>
0026 #include <linux/sched.h>
0027 #include <linux/spi/spi.h>
0028 #include <video/mipi_display.h>
0029
0030 #include <drm/drm_connector.h>
0031 #include <drm/drm_modes.h>
0032 #include <drm/drm_panel.h>
0033
0034 #define CTRL_DISP_BRIGHTNESS_CTRL_ON BIT(5)
0035 #define CTRL_DISP_AMBIENT_LIGHT_CTRL_ON BIT(4)
0036 #define CTRL_DISP_BACKLIGHT_ON BIT(2)
0037 #define CTRL_DISP_AUTO_BRIGHTNESS_ON BIT(1)
0038
0039 #define MIPID_CMD_WRITE_CABC 0x55
0040 #define MIPID_CMD_READ_CABC 0x56
0041
0042 #define MIPID_VER_LPH8923 3
0043 #define MIPID_VER_LS041Y3 4
0044 #define MIPID_VER_L4F00311 8
0045 #define MIPID_VER_ACX565AKM 9
0046
0047 struct acx565akm_panel {
0048 struct drm_panel panel;
0049
0050 struct spi_device *spi;
0051 struct gpio_desc *reset_gpio;
0052 struct backlight_device *backlight;
0053
0054 struct mutex mutex;
0055
0056 const char *name;
0057 u8 display_id[3];
0058 int model;
0059 int revision;
0060 bool has_bc;
0061 bool has_cabc;
0062
0063 bool enabled;
0064 unsigned int cabc_mode;
0065
0066
0067
0068
0069 unsigned long hw_guard_end;
0070 unsigned long hw_guard_wait;
0071 };
0072
0073 #define to_acx565akm_device(p) container_of(p, struct acx565akm_panel, panel)
0074
0075 static void acx565akm_transfer(struct acx565akm_panel *lcd, int cmd,
0076 const u8 *wbuf, int wlen, u8 *rbuf, int rlen)
0077 {
0078 struct spi_message m;
0079 struct spi_transfer *x, xfer[5];
0080 int ret;
0081
0082 spi_message_init(&m);
0083
0084 memset(xfer, 0, sizeof(xfer));
0085 x = &xfer[0];
0086
0087 cmd &= 0xff;
0088 x->tx_buf = &cmd;
0089 x->bits_per_word = 9;
0090 x->len = 2;
0091
0092 if (rlen > 1 && wlen == 0) {
0093
0094
0095
0096
0097
0098 x->bits_per_word = 10;
0099 cmd <<= 1;
0100 }
0101 spi_message_add_tail(x, &m);
0102
0103 if (wlen) {
0104 x++;
0105 x->tx_buf = wbuf;
0106 x->len = wlen;
0107 x->bits_per_word = 9;
0108 spi_message_add_tail(x, &m);
0109 }
0110
0111 if (rlen) {
0112 x++;
0113 x->rx_buf = rbuf;
0114 x->len = rlen;
0115 spi_message_add_tail(x, &m);
0116 }
0117
0118 ret = spi_sync(lcd->spi, &m);
0119 if (ret < 0)
0120 dev_dbg(&lcd->spi->dev, "spi_sync %d\n", ret);
0121 }
0122
0123 static inline void acx565akm_cmd(struct acx565akm_panel *lcd, int cmd)
0124 {
0125 acx565akm_transfer(lcd, cmd, NULL, 0, NULL, 0);
0126 }
0127
0128 static inline void acx565akm_write(struct acx565akm_panel *lcd,
0129 int reg, const u8 *buf, int len)
0130 {
0131 acx565akm_transfer(lcd, reg, buf, len, NULL, 0);
0132 }
0133
0134 static inline void acx565akm_read(struct acx565akm_panel *lcd,
0135 int reg, u8 *buf, int len)
0136 {
0137 acx565akm_transfer(lcd, reg, NULL, 0, buf, len);
0138 }
0139
0140
0141
0142
0143
0144 static unsigned int acx565akm_get_cabc_mode(struct acx565akm_panel *lcd)
0145 {
0146 return lcd->cabc_mode;
0147 }
0148
0149 static void acx565akm_set_cabc_mode(struct acx565akm_panel *lcd,
0150 unsigned int mode)
0151 {
0152 u16 cabc_ctrl;
0153
0154 lcd->cabc_mode = mode;
0155 if (!lcd->enabled)
0156 return;
0157 cabc_ctrl = 0;
0158 acx565akm_read(lcd, MIPID_CMD_READ_CABC, (u8 *)&cabc_ctrl, 1);
0159 cabc_ctrl &= ~3;
0160 cabc_ctrl |= (1 << 8) | (mode & 3);
0161 acx565akm_write(lcd, MIPID_CMD_WRITE_CABC, (u8 *)&cabc_ctrl, 2);
0162 }
0163
0164 static unsigned int acx565akm_get_hw_cabc_mode(struct acx565akm_panel *lcd)
0165 {
0166 u8 cabc_ctrl;
0167
0168 acx565akm_read(lcd, MIPID_CMD_READ_CABC, &cabc_ctrl, 1);
0169 return cabc_ctrl & 3;
0170 }
0171
0172 static const char * const acx565akm_cabc_modes[] = {
0173 "off",
0174 "ui",
0175 "still-image",
0176 "moving-image",
0177 };
0178
0179 static ssize_t cabc_mode_show(struct device *dev,
0180 struct device_attribute *attr,
0181 char *buf)
0182 {
0183 struct acx565akm_panel *lcd = dev_get_drvdata(dev);
0184 const char *mode_str;
0185 int mode;
0186
0187 if (!lcd->has_cabc)
0188 mode = 0;
0189 else
0190 mode = acx565akm_get_cabc_mode(lcd);
0191
0192 mode_str = "unknown";
0193 if (mode >= 0 && mode < ARRAY_SIZE(acx565akm_cabc_modes))
0194 mode_str = acx565akm_cabc_modes[mode];
0195
0196 return sprintf(buf, "%s\n", mode_str);
0197 }
0198
0199 static ssize_t cabc_mode_store(struct device *dev,
0200 struct device_attribute *attr,
0201 const char *buf, size_t count)
0202 {
0203 struct acx565akm_panel *lcd = dev_get_drvdata(dev);
0204 unsigned int i;
0205
0206 for (i = 0; i < ARRAY_SIZE(acx565akm_cabc_modes); i++) {
0207 const char *mode_str = acx565akm_cabc_modes[i];
0208 int cmp_len = strlen(mode_str);
0209
0210 if (count > 0 && buf[count - 1] == '\n')
0211 count--;
0212 if (count != cmp_len)
0213 continue;
0214
0215 if (strncmp(buf, mode_str, cmp_len) == 0)
0216 break;
0217 }
0218
0219 if (i == ARRAY_SIZE(acx565akm_cabc_modes))
0220 return -EINVAL;
0221
0222 if (!lcd->has_cabc && i != 0)
0223 return -EINVAL;
0224
0225 mutex_lock(&lcd->mutex);
0226 acx565akm_set_cabc_mode(lcd, i);
0227 mutex_unlock(&lcd->mutex);
0228
0229 return count;
0230 }
0231
0232 static ssize_t cabc_available_modes_show(struct device *dev,
0233 struct device_attribute *attr,
0234 char *buf)
0235 {
0236 struct acx565akm_panel *lcd = dev_get_drvdata(dev);
0237 unsigned int i;
0238 size_t len = 0;
0239
0240 if (!lcd->has_cabc)
0241 return sprintf(buf, "%s\n", acx565akm_cabc_modes[0]);
0242
0243 for (i = 0; i < ARRAY_SIZE(acx565akm_cabc_modes); i++)
0244 len += sprintf(&buf[len], "%s%s", i ? " " : "",
0245 acx565akm_cabc_modes[i]);
0246
0247 buf[len++] = '\n';
0248
0249 return len;
0250 }
0251
0252 static DEVICE_ATTR_RW(cabc_mode);
0253 static DEVICE_ATTR_RO(cabc_available_modes);
0254
0255 static struct attribute *acx565akm_cabc_attrs[] = {
0256 &dev_attr_cabc_mode.attr,
0257 &dev_attr_cabc_available_modes.attr,
0258 NULL,
0259 };
0260
0261 static const struct attribute_group acx565akm_cabc_attr_group = {
0262 .attrs = acx565akm_cabc_attrs,
0263 };
0264
0265
0266
0267
0268
0269 static int acx565akm_get_actual_brightness(struct acx565akm_panel *lcd)
0270 {
0271 u8 bv;
0272
0273 acx565akm_read(lcd, MIPI_DCS_GET_DISPLAY_BRIGHTNESS, &bv, 1);
0274
0275 return bv;
0276 }
0277
0278 static void acx565akm_set_brightness(struct acx565akm_panel *lcd, int level)
0279 {
0280 u16 ctrl;
0281 int bv;
0282
0283 bv = level | (1 << 8);
0284 acx565akm_write(lcd, MIPI_DCS_SET_DISPLAY_BRIGHTNESS, (u8 *)&bv, 2);
0285
0286 acx565akm_read(lcd, MIPI_DCS_GET_CONTROL_DISPLAY, (u8 *)&ctrl, 1);
0287 if (level)
0288 ctrl |= CTRL_DISP_BRIGHTNESS_CTRL_ON |
0289 CTRL_DISP_BACKLIGHT_ON;
0290 else
0291 ctrl &= ~(CTRL_DISP_BRIGHTNESS_CTRL_ON |
0292 CTRL_DISP_BACKLIGHT_ON);
0293
0294 ctrl |= 1 << 8;
0295 acx565akm_write(lcd, MIPI_DCS_WRITE_CONTROL_DISPLAY, (u8 *)&ctrl, 2);
0296 }
0297
0298 static int acx565akm_bl_update_status_locked(struct backlight_device *dev)
0299 {
0300 struct acx565akm_panel *lcd = dev_get_drvdata(&dev->dev);
0301 int level = backlight_get_brightness(dev);
0302
0303 acx565akm_set_brightness(lcd, level);
0304
0305 return 0;
0306 }
0307
0308 static int acx565akm_bl_update_status(struct backlight_device *dev)
0309 {
0310 struct acx565akm_panel *lcd = dev_get_drvdata(&dev->dev);
0311 int ret;
0312
0313 mutex_lock(&lcd->mutex);
0314 ret = acx565akm_bl_update_status_locked(dev);
0315 mutex_unlock(&lcd->mutex);
0316
0317 return ret;
0318 }
0319
0320 static int acx565akm_bl_get_intensity(struct backlight_device *dev)
0321 {
0322 struct acx565akm_panel *lcd = dev_get_drvdata(&dev->dev);
0323 unsigned int intensity;
0324
0325 mutex_lock(&lcd->mutex);
0326
0327 if (!backlight_is_blank(dev))
0328 intensity = acx565akm_get_actual_brightness(lcd);
0329 else
0330 intensity = 0;
0331
0332 mutex_unlock(&lcd->mutex);
0333
0334 return intensity;
0335 }
0336
0337 static const struct backlight_ops acx565akm_bl_ops = {
0338 .get_brightness = acx565akm_bl_get_intensity,
0339 .update_status = acx565akm_bl_update_status,
0340 };
0341
0342 static int acx565akm_backlight_init(struct acx565akm_panel *lcd)
0343 {
0344 struct backlight_properties props = {
0345 .power = FB_BLANK_UNBLANK,
0346 .type = BACKLIGHT_RAW,
0347 };
0348 int ret;
0349
0350 lcd->backlight = backlight_device_register(lcd->name, &lcd->spi->dev,
0351 lcd, &acx565akm_bl_ops,
0352 &props);
0353 if (IS_ERR(lcd->backlight)) {
0354 ret = PTR_ERR(lcd->backlight);
0355 lcd->backlight = NULL;
0356 return ret;
0357 }
0358
0359 if (lcd->has_cabc) {
0360 ret = sysfs_create_group(&lcd->backlight->dev.kobj,
0361 &acx565akm_cabc_attr_group);
0362 if (ret < 0) {
0363 dev_err(&lcd->spi->dev,
0364 "%s failed to create sysfs files\n", __func__);
0365 backlight_device_unregister(lcd->backlight);
0366 return ret;
0367 }
0368
0369 lcd->cabc_mode = acx565akm_get_hw_cabc_mode(lcd);
0370 }
0371
0372 lcd->backlight->props.max_brightness = 255;
0373 lcd->backlight->props.brightness = acx565akm_get_actual_brightness(lcd);
0374
0375 acx565akm_bl_update_status_locked(lcd->backlight);
0376
0377 return 0;
0378 }
0379
0380 static void acx565akm_backlight_cleanup(struct acx565akm_panel *lcd)
0381 {
0382 if (lcd->has_cabc)
0383 sysfs_remove_group(&lcd->backlight->dev.kobj,
0384 &acx565akm_cabc_attr_group);
0385
0386 backlight_device_unregister(lcd->backlight);
0387 }
0388
0389
0390
0391
0392
0393 static void acx565akm_set_sleep_mode(struct acx565akm_panel *lcd, int on)
0394 {
0395 int cmd = on ? MIPI_DCS_ENTER_SLEEP_MODE : MIPI_DCS_EXIT_SLEEP_MODE;
0396 unsigned long wait;
0397
0398
0399
0400
0401
0402 wait = lcd->hw_guard_end - jiffies;
0403 if ((long)wait > 0 && wait <= lcd->hw_guard_wait) {
0404 set_current_state(TASK_UNINTERRUPTIBLE);
0405 schedule_timeout(wait);
0406 }
0407
0408 acx565akm_cmd(lcd, cmd);
0409
0410 lcd->hw_guard_wait = msecs_to_jiffies(120);
0411 lcd->hw_guard_end = jiffies + lcd->hw_guard_wait;
0412 }
0413
0414 static void acx565akm_set_display_state(struct acx565akm_panel *lcd,
0415 int enabled)
0416 {
0417 int cmd = enabled ? MIPI_DCS_SET_DISPLAY_ON : MIPI_DCS_SET_DISPLAY_OFF;
0418
0419 acx565akm_cmd(lcd, cmd);
0420 }
0421
0422 static int acx565akm_power_on(struct acx565akm_panel *lcd)
0423 {
0424
0425 msleep(50);
0426
0427 gpiod_set_value(lcd->reset_gpio, 1);
0428
0429 if (lcd->enabled) {
0430 dev_dbg(&lcd->spi->dev, "panel already enabled\n");
0431 return 0;
0432 }
0433
0434
0435
0436
0437
0438
0439
0440
0441
0442 msleep(120);
0443
0444 acx565akm_set_sleep_mode(lcd, 0);
0445 lcd->enabled = true;
0446
0447
0448 usleep_range(5000, 10000);
0449 acx565akm_set_display_state(lcd, 1);
0450 acx565akm_set_cabc_mode(lcd, lcd->cabc_mode);
0451
0452 return acx565akm_bl_update_status_locked(lcd->backlight);
0453 }
0454
0455 static void acx565akm_power_off(struct acx565akm_panel *lcd)
0456 {
0457 if (!lcd->enabled)
0458 return;
0459
0460 acx565akm_set_display_state(lcd, 0);
0461 acx565akm_set_sleep_mode(lcd, 1);
0462 lcd->enabled = false;
0463
0464
0465
0466
0467
0468
0469 msleep(50);
0470
0471 gpiod_set_value(lcd->reset_gpio, 0);
0472
0473
0474 msleep(100);
0475 }
0476
0477 static int acx565akm_disable(struct drm_panel *panel)
0478 {
0479 struct acx565akm_panel *lcd = to_acx565akm_device(panel);
0480
0481 mutex_lock(&lcd->mutex);
0482 acx565akm_power_off(lcd);
0483 mutex_unlock(&lcd->mutex);
0484
0485 return 0;
0486 }
0487
0488 static int acx565akm_enable(struct drm_panel *panel)
0489 {
0490 struct acx565akm_panel *lcd = to_acx565akm_device(panel);
0491
0492 mutex_lock(&lcd->mutex);
0493 acx565akm_power_on(lcd);
0494 mutex_unlock(&lcd->mutex);
0495
0496 return 0;
0497 }
0498
0499 static const struct drm_display_mode acx565akm_mode = {
0500 .clock = 24000,
0501 .hdisplay = 800,
0502 .hsync_start = 800 + 28,
0503 .hsync_end = 800 + 28 + 4,
0504 .htotal = 800 + 28 + 4 + 24,
0505 .vdisplay = 480,
0506 .vsync_start = 480 + 3,
0507 .vsync_end = 480 + 3 + 3,
0508 .vtotal = 480 + 3 + 3 + 4,
0509 .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
0510 .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
0511 .width_mm = 77,
0512 .height_mm = 46,
0513 };
0514
0515 static int acx565akm_get_modes(struct drm_panel *panel,
0516 struct drm_connector *connector)
0517 {
0518 struct drm_display_mode *mode;
0519
0520 mode = drm_mode_duplicate(connector->dev, &acx565akm_mode);
0521 if (!mode)
0522 return -ENOMEM;
0523
0524 drm_mode_set_name(mode);
0525 drm_mode_probed_add(connector, mode);
0526
0527 connector->display_info.width_mm = acx565akm_mode.width_mm;
0528 connector->display_info.height_mm = acx565akm_mode.height_mm;
0529 connector->display_info.bus_flags = DRM_BUS_FLAG_DE_HIGH
0530 | DRM_BUS_FLAG_SYNC_SAMPLE_POSEDGE
0531 | DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE;
0532
0533 return 1;
0534 }
0535
0536 static const struct drm_panel_funcs acx565akm_funcs = {
0537 .disable = acx565akm_disable,
0538 .enable = acx565akm_enable,
0539 .get_modes = acx565akm_get_modes,
0540 };
0541
0542
0543
0544
0545
0546 static int acx565akm_detect(struct acx565akm_panel *lcd)
0547 {
0548 __be32 value;
0549 u32 status;
0550 int ret = 0;
0551
0552
0553
0554
0555
0556 gpiod_set_value(lcd->reset_gpio, 1);
0557 usleep_range(5000, 10000);
0558
0559 acx565akm_read(lcd, MIPI_DCS_GET_DISPLAY_STATUS, (u8 *)&value, 4);
0560 status = __be32_to_cpu(value);
0561 lcd->enabled = (status & (1 << 17)) && (status & (1 << 10));
0562
0563 dev_dbg(&lcd->spi->dev,
0564 "LCD panel %s by bootloader (status 0x%04x)\n",
0565 lcd->enabled ? "enabled" : "disabled ", status);
0566
0567 acx565akm_read(lcd, MIPI_DCS_GET_DISPLAY_ID, lcd->display_id, 3);
0568 dev_dbg(&lcd->spi->dev, "MIPI display ID: %02x%02x%02x\n",
0569 lcd->display_id[0], lcd->display_id[1], lcd->display_id[2]);
0570
0571 switch (lcd->display_id[0]) {
0572 case 0x10:
0573 lcd->model = MIPID_VER_ACX565AKM;
0574 lcd->name = "acx565akm";
0575 lcd->has_bc = 1;
0576 lcd->has_cabc = 1;
0577 break;
0578 case 0x29:
0579 lcd->model = MIPID_VER_L4F00311;
0580 lcd->name = "l4f00311";
0581 break;
0582 case 0x45:
0583 lcd->model = MIPID_VER_LPH8923;
0584 lcd->name = "lph8923";
0585 break;
0586 case 0x83:
0587 lcd->model = MIPID_VER_LS041Y3;
0588 lcd->name = "ls041y3";
0589 break;
0590 default:
0591 lcd->name = "unknown";
0592 dev_err(&lcd->spi->dev, "unknown display ID\n");
0593 ret = -ENODEV;
0594 goto done;
0595 }
0596
0597 lcd->revision = lcd->display_id[1];
0598
0599 dev_info(&lcd->spi->dev, "%s rev %02x panel detected\n",
0600 lcd->name, lcd->revision);
0601
0602 done:
0603 if (!lcd->enabled)
0604 gpiod_set_value(lcd->reset_gpio, 0);
0605
0606 return ret;
0607 }
0608
0609 static int acx565akm_probe(struct spi_device *spi)
0610 {
0611 struct acx565akm_panel *lcd;
0612 int ret;
0613
0614 lcd = devm_kzalloc(&spi->dev, sizeof(*lcd), GFP_KERNEL);
0615 if (!lcd)
0616 return -ENOMEM;
0617
0618 spi_set_drvdata(spi, lcd);
0619 spi->mode = SPI_MODE_3;
0620
0621 lcd->spi = spi;
0622 mutex_init(&lcd->mutex);
0623
0624 lcd->reset_gpio = devm_gpiod_get(&spi->dev, "reset", GPIOD_OUT_HIGH);
0625 if (IS_ERR(lcd->reset_gpio)) {
0626 dev_err(&spi->dev, "failed to get reset GPIO\n");
0627 return PTR_ERR(lcd->reset_gpio);
0628 }
0629
0630 ret = acx565akm_detect(lcd);
0631 if (ret < 0) {
0632 dev_err(&spi->dev, "panel detection failed\n");
0633 return ret;
0634 }
0635
0636 if (lcd->has_bc) {
0637 ret = acx565akm_backlight_init(lcd);
0638 if (ret < 0)
0639 return ret;
0640 }
0641
0642 drm_panel_init(&lcd->panel, &lcd->spi->dev, &acx565akm_funcs,
0643 DRM_MODE_CONNECTOR_DPI);
0644
0645 drm_panel_add(&lcd->panel);
0646
0647 return 0;
0648 }
0649
0650 static void acx565akm_remove(struct spi_device *spi)
0651 {
0652 struct acx565akm_panel *lcd = spi_get_drvdata(spi);
0653
0654 drm_panel_remove(&lcd->panel);
0655
0656 if (lcd->has_bc)
0657 acx565akm_backlight_cleanup(lcd);
0658
0659 drm_panel_disable(&lcd->panel);
0660 drm_panel_unprepare(&lcd->panel);
0661 }
0662
0663 static const struct of_device_id acx565akm_of_match[] = {
0664 { .compatible = "sony,acx565akm", },
0665 { },
0666 };
0667
0668 MODULE_DEVICE_TABLE(of, acx565akm_of_match);
0669
0670 static const struct spi_device_id acx565akm_ids[] = {
0671 { "acx565akm", 0 },
0672 { }
0673 };
0674
0675 MODULE_DEVICE_TABLE(spi, acx565akm_ids);
0676
0677 static struct spi_driver acx565akm_driver = {
0678 .probe = acx565akm_probe,
0679 .remove = acx565akm_remove,
0680 .id_table = acx565akm_ids,
0681 .driver = {
0682 .name = "panel-sony-acx565akm",
0683 .of_match_table = acx565akm_of_match,
0684 },
0685 };
0686
0687 module_spi_driver(acx565akm_driver);
0688
0689 MODULE_AUTHOR("Nokia Corporation");
0690 MODULE_DESCRIPTION("Sony ACX565AKM LCD Panel Driver");
0691 MODULE_LICENSE("GPL");