Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Sony ACX565AKM LCD Panel driver
0004  *
0005  * Copyright (C) 2019 Texas Instruments Incorporated
0006  *
0007  * Based on the omapdrm-specific panel-sony-acx565akm driver
0008  *
0009  * Copyright (C) 2010 Nokia Corporation
0010  * Author: Imre Deak <imre.deak@nokia.com>
0011  */
0012 
0013 /*
0014  * TODO (to be addressed with hardware access to test the changes):
0015  *
0016  * - Update backlight support to use backlight_update_status() etc.
0017  * - Use prepare/unprepare for the basic power on/off of the backligt
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      * Next value of jiffies when we can issue the next sleep in/out
0067      * command.
0068      */
0069     unsigned long hw_guard_end;
0070     unsigned long hw_guard_wait;        /* max guard time in jiffies */
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          * Between the command and the response data there is a
0095          * dummy clock cycle. Add an extra bit after the command
0096          * word to account for this.
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  * Auto Brightness Control Via sysfs
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",      /* always used when CABC is not supported */
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  * Backlight Device
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  * DRM Bridge Operations
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      * We have to keep 120msec between sleep in/out commands.
0400      * (8.2.15, 8.2.16).
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     /*FIXME tweak me */
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      * We have to meet all the following delay requirements:
0436      * 1. tRW: reset pulse width 10usec (7.12.1)
0437      * 2. tRT: reset cancel time 5msec (7.12.1)
0438      * 3. Providing PCLK,HS,VS signals for 2 frames = ~50msec worst
0439      *    case (7.6.2)
0440      * 4. 120msec before the sleep out command (7.12.1)
0441      */
0442     msleep(120);
0443 
0444     acx565akm_set_sleep_mode(lcd, 0);
0445     lcd->enabled = true;
0446 
0447     /* 5msec between sleep out and the next command. (8.2.16) */
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      * We have to provide PCLK,HS,VS signals for 2 frames (worst case
0465      * ~50msec) after sending the sleep in command and asserting the
0466      * reset signal. We probably could assert the reset w/o the delay
0467      * but we still delay to avoid possible artifacts. (7.6.1)
0468      */
0469     msleep(50);
0470 
0471     gpiod_set_value(lcd->reset_gpio, 0);
0472 
0473     /* FIXME need to tweak this delay */
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  * Probe, Detect and Remove
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      * After being taken out of reset the panel needs 5ms before the first
0554      * command can be sent.
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     { /* sentinel */ },
0666 };
0667 
0668 MODULE_DEVICE_TABLE(of, acx565akm_of_match);
0669 
0670 static const struct spi_device_id acx565akm_ids[] = {
0671     { "acx565akm", 0 },
0672     { /* sentinel */ }
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");