Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * DRM driver for Ilitek ILI9225 panels
0004  *
0005  * Copyright 2017 David Lechner <david@lechnology.com>
0006  *
0007  * Some code copied from mipi-dbi.c
0008  * Copyright 2016 Noralf Trønnes
0009  */
0010 
0011 #include <linux/delay.h>
0012 #include <linux/dma-buf.h>
0013 #include <linux/gpio/consumer.h>
0014 #include <linux/module.h>
0015 #include <linux/property.h>
0016 #include <linux/spi/spi.h>
0017 #include <video/mipi_display.h>
0018 
0019 #include <drm/drm_atomic_helper.h>
0020 #include <drm/drm_damage_helper.h>
0021 #include <drm/drm_drv.h>
0022 #include <drm/drm_fb_cma_helper.h>
0023 #include <drm/drm_fb_helper.h>
0024 #include <drm/drm_fourcc.h>
0025 #include <drm/drm_framebuffer.h>
0026 #include <drm/drm_gem_atomic_helper.h>
0027 #include <drm/drm_gem_cma_helper.h>
0028 #include <drm/drm_managed.h>
0029 #include <drm/drm_mipi_dbi.h>
0030 #include <drm/drm_rect.h>
0031 
0032 #define ILI9225_DRIVER_READ_CODE    0x00
0033 #define ILI9225_DRIVER_OUTPUT_CONTROL   0x01
0034 #define ILI9225_LCD_AC_DRIVING_CONTROL  0x02
0035 #define ILI9225_ENTRY_MODE      0x03
0036 #define ILI9225_DISPLAY_CONTROL_1   0x07
0037 #define ILI9225_BLANK_PERIOD_CONTROL_1  0x08
0038 #define ILI9225_FRAME_CYCLE_CONTROL 0x0b
0039 #define ILI9225_INTERFACE_CONTROL   0x0c
0040 #define ILI9225_OSCILLATION_CONTROL 0x0f
0041 #define ILI9225_POWER_CONTROL_1     0x10
0042 #define ILI9225_POWER_CONTROL_2     0x11
0043 #define ILI9225_POWER_CONTROL_3     0x12
0044 #define ILI9225_POWER_CONTROL_4     0x13
0045 #define ILI9225_POWER_CONTROL_5     0x14
0046 #define ILI9225_VCI_RECYCLING       0x15
0047 #define ILI9225_RAM_ADDRESS_SET_1   0x20
0048 #define ILI9225_RAM_ADDRESS_SET_2   0x21
0049 #define ILI9225_WRITE_DATA_TO_GRAM  0x22
0050 #define ILI9225_SOFTWARE_RESET      0x28
0051 #define ILI9225_GATE_SCAN_CONTROL   0x30
0052 #define ILI9225_VERTICAL_SCROLL_1   0x31
0053 #define ILI9225_VERTICAL_SCROLL_2   0x32
0054 #define ILI9225_VERTICAL_SCROLL_3   0x33
0055 #define ILI9225_PARTIAL_DRIVING_POS_1   0x34
0056 #define ILI9225_PARTIAL_DRIVING_POS_2   0x35
0057 #define ILI9225_HORIZ_WINDOW_ADDR_1 0x36
0058 #define ILI9225_HORIZ_WINDOW_ADDR_2 0x37
0059 #define ILI9225_VERT_WINDOW_ADDR_1  0x38
0060 #define ILI9225_VERT_WINDOW_ADDR_2  0x39
0061 #define ILI9225_GAMMA_CONTROL_1     0x50
0062 #define ILI9225_GAMMA_CONTROL_2     0x51
0063 #define ILI9225_GAMMA_CONTROL_3     0x52
0064 #define ILI9225_GAMMA_CONTROL_4     0x53
0065 #define ILI9225_GAMMA_CONTROL_5     0x54
0066 #define ILI9225_GAMMA_CONTROL_6     0x55
0067 #define ILI9225_GAMMA_CONTROL_7     0x56
0068 #define ILI9225_GAMMA_CONTROL_8     0x57
0069 #define ILI9225_GAMMA_CONTROL_9     0x58
0070 #define ILI9225_GAMMA_CONTROL_10    0x59
0071 
0072 static inline int ili9225_command(struct mipi_dbi *dbi, u8 cmd, u16 data)
0073 {
0074     u8 par[2] = { data >> 8, data & 0xff };
0075 
0076     return mipi_dbi_command_buf(dbi, cmd, par, 2);
0077 }
0078 
0079 static void ili9225_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect)
0080 {
0081     struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
0082     struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(fb->dev);
0083     unsigned int height = rect->y2 - rect->y1;
0084     unsigned int width = rect->x2 - rect->x1;
0085     struct mipi_dbi *dbi = &dbidev->dbi;
0086     bool swap = dbi->swap_bytes;
0087     u16 x_start, y_start;
0088     u16 x1, x2, y1, y2;
0089     int idx, ret = 0;
0090     bool full;
0091     void *tr;
0092 
0093     if (!drm_dev_enter(fb->dev, &idx))
0094         return;
0095 
0096     full = width == fb->width && height == fb->height;
0097 
0098     DRM_DEBUG_KMS("Flushing [FB:%d] " DRM_RECT_FMT "\n", fb->base.id, DRM_RECT_ARG(rect));
0099 
0100     if (!dbi->dc || !full || swap ||
0101         fb->format->format == DRM_FORMAT_XRGB8888) {
0102         tr = dbidev->tx_buf;
0103         ret = mipi_dbi_buf_copy(dbidev->tx_buf, fb, rect, swap);
0104         if (ret)
0105             goto err_msg;
0106     } else {
0107         tr = cma_obj->vaddr;
0108     }
0109 
0110     switch (dbidev->rotation) {
0111     default:
0112         x1 = rect->x1;
0113         x2 = rect->x2 - 1;
0114         y1 = rect->y1;
0115         y2 = rect->y2 - 1;
0116         x_start = x1;
0117         y_start = y1;
0118         break;
0119     case 90:
0120         x1 = rect->y1;
0121         x2 = rect->y2 - 1;
0122         y1 = fb->width - rect->x2;
0123         y2 = fb->width - rect->x1 - 1;
0124         x_start = x1;
0125         y_start = y2;
0126         break;
0127     case 180:
0128         x1 = fb->width - rect->x2;
0129         x2 = fb->width - rect->x1 - 1;
0130         y1 = fb->height - rect->y2;
0131         y2 = fb->height - rect->y1 - 1;
0132         x_start = x2;
0133         y_start = y2;
0134         break;
0135     case 270:
0136         x1 = fb->height - rect->y2;
0137         x2 = fb->height - rect->y1 - 1;
0138         y1 = rect->x1;
0139         y2 = rect->x2 - 1;
0140         x_start = x2;
0141         y_start = y1;
0142         break;
0143     }
0144 
0145     ili9225_command(dbi, ILI9225_HORIZ_WINDOW_ADDR_1, x2);
0146     ili9225_command(dbi, ILI9225_HORIZ_WINDOW_ADDR_2, x1);
0147     ili9225_command(dbi, ILI9225_VERT_WINDOW_ADDR_1, y2);
0148     ili9225_command(dbi, ILI9225_VERT_WINDOW_ADDR_2, y1);
0149 
0150     ili9225_command(dbi, ILI9225_RAM_ADDRESS_SET_1, x_start);
0151     ili9225_command(dbi, ILI9225_RAM_ADDRESS_SET_2, y_start);
0152 
0153     ret = mipi_dbi_command_buf(dbi, ILI9225_WRITE_DATA_TO_GRAM, tr,
0154                    width * height * 2);
0155 err_msg:
0156     if (ret)
0157         dev_err_once(fb->dev->dev, "Failed to update display %d\n", ret);
0158 
0159     drm_dev_exit(idx);
0160 }
0161 
0162 static void ili9225_pipe_update(struct drm_simple_display_pipe *pipe,
0163                 struct drm_plane_state *old_state)
0164 {
0165     struct drm_plane_state *state = pipe->plane.state;
0166     struct drm_rect rect;
0167 
0168     if (!pipe->crtc.state->active)
0169         return;
0170 
0171     if (drm_atomic_helper_damage_merged(old_state, state, &rect))
0172         ili9225_fb_dirty(state->fb, &rect);
0173 }
0174 
0175 static void ili9225_pipe_enable(struct drm_simple_display_pipe *pipe,
0176                 struct drm_crtc_state *crtc_state,
0177                 struct drm_plane_state *plane_state)
0178 {
0179     struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(pipe->crtc.dev);
0180     struct drm_framebuffer *fb = plane_state->fb;
0181     struct device *dev = pipe->crtc.dev->dev;
0182     struct mipi_dbi *dbi = &dbidev->dbi;
0183     struct drm_rect rect = {
0184         .x1 = 0,
0185         .x2 = fb->width,
0186         .y1 = 0,
0187         .y2 = fb->height,
0188     };
0189     int ret, idx;
0190     u8 am_id;
0191 
0192     if (!drm_dev_enter(pipe->crtc.dev, &idx))
0193         return;
0194 
0195     DRM_DEBUG_KMS("\n");
0196 
0197     mipi_dbi_hw_reset(dbi);
0198 
0199     /*
0200      * There don't seem to be two example init sequences that match, so
0201      * using the one from the popular Arduino library for this display.
0202      * https://github.com/Nkawu/TFT_22_ILI9225/blob/master/src/TFT_22_ILI9225.cpp
0203      */
0204 
0205     ret = ili9225_command(dbi, ILI9225_POWER_CONTROL_1, 0x0000);
0206     if (ret) {
0207         DRM_DEV_ERROR(dev, "Error sending command %d\n", ret);
0208         goto out_exit;
0209     }
0210     ili9225_command(dbi, ILI9225_POWER_CONTROL_2, 0x0000);
0211     ili9225_command(dbi, ILI9225_POWER_CONTROL_3, 0x0000);
0212     ili9225_command(dbi, ILI9225_POWER_CONTROL_4, 0x0000);
0213     ili9225_command(dbi, ILI9225_POWER_CONTROL_5, 0x0000);
0214 
0215     msleep(40);
0216 
0217     ili9225_command(dbi, ILI9225_POWER_CONTROL_2, 0x0018);
0218     ili9225_command(dbi, ILI9225_POWER_CONTROL_3, 0x6121);
0219     ili9225_command(dbi, ILI9225_POWER_CONTROL_4, 0x006f);
0220     ili9225_command(dbi, ILI9225_POWER_CONTROL_5, 0x495f);
0221     ili9225_command(dbi, ILI9225_POWER_CONTROL_1, 0x0800);
0222 
0223     msleep(10);
0224 
0225     ili9225_command(dbi, ILI9225_POWER_CONTROL_2, 0x103b);
0226 
0227     msleep(50);
0228 
0229     switch (dbidev->rotation) {
0230     default:
0231         am_id = 0x30;
0232         break;
0233     case 90:
0234         am_id = 0x18;
0235         break;
0236     case 180:
0237         am_id = 0x00;
0238         break;
0239     case 270:
0240         am_id = 0x28;
0241         break;
0242     }
0243     ili9225_command(dbi, ILI9225_DRIVER_OUTPUT_CONTROL, 0x011c);
0244     ili9225_command(dbi, ILI9225_LCD_AC_DRIVING_CONTROL, 0x0100);
0245     ili9225_command(dbi, ILI9225_ENTRY_MODE, 0x1000 | am_id);
0246     ili9225_command(dbi, ILI9225_DISPLAY_CONTROL_1, 0x0000);
0247     ili9225_command(dbi, ILI9225_BLANK_PERIOD_CONTROL_1, 0x0808);
0248     ili9225_command(dbi, ILI9225_FRAME_CYCLE_CONTROL, 0x1100);
0249     ili9225_command(dbi, ILI9225_INTERFACE_CONTROL, 0x0000);
0250     ili9225_command(dbi, ILI9225_OSCILLATION_CONTROL, 0x0d01);
0251     ili9225_command(dbi, ILI9225_VCI_RECYCLING, 0x0020);
0252     ili9225_command(dbi, ILI9225_RAM_ADDRESS_SET_1, 0x0000);
0253     ili9225_command(dbi, ILI9225_RAM_ADDRESS_SET_2, 0x0000);
0254 
0255     ili9225_command(dbi, ILI9225_GATE_SCAN_CONTROL, 0x0000);
0256     ili9225_command(dbi, ILI9225_VERTICAL_SCROLL_1, 0x00db);
0257     ili9225_command(dbi, ILI9225_VERTICAL_SCROLL_2, 0x0000);
0258     ili9225_command(dbi, ILI9225_VERTICAL_SCROLL_3, 0x0000);
0259     ili9225_command(dbi, ILI9225_PARTIAL_DRIVING_POS_1, 0x00db);
0260     ili9225_command(dbi, ILI9225_PARTIAL_DRIVING_POS_2, 0x0000);
0261 
0262     ili9225_command(dbi, ILI9225_GAMMA_CONTROL_1, 0x0000);
0263     ili9225_command(dbi, ILI9225_GAMMA_CONTROL_2, 0x0808);
0264     ili9225_command(dbi, ILI9225_GAMMA_CONTROL_3, 0x080a);
0265     ili9225_command(dbi, ILI9225_GAMMA_CONTROL_4, 0x000a);
0266     ili9225_command(dbi, ILI9225_GAMMA_CONTROL_5, 0x0a08);
0267     ili9225_command(dbi, ILI9225_GAMMA_CONTROL_6, 0x0808);
0268     ili9225_command(dbi, ILI9225_GAMMA_CONTROL_7, 0x0000);
0269     ili9225_command(dbi, ILI9225_GAMMA_CONTROL_8, 0x0a00);
0270     ili9225_command(dbi, ILI9225_GAMMA_CONTROL_9, 0x0710);
0271     ili9225_command(dbi, ILI9225_GAMMA_CONTROL_10, 0x0710);
0272 
0273     ili9225_command(dbi, ILI9225_DISPLAY_CONTROL_1, 0x0012);
0274 
0275     msleep(50);
0276 
0277     ili9225_command(dbi, ILI9225_DISPLAY_CONTROL_1, 0x1017);
0278 
0279     ili9225_fb_dirty(fb, &rect);
0280 out_exit:
0281     drm_dev_exit(idx);
0282 }
0283 
0284 static void ili9225_pipe_disable(struct drm_simple_display_pipe *pipe)
0285 {
0286     struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(pipe->crtc.dev);
0287     struct mipi_dbi *dbi = &dbidev->dbi;
0288 
0289     DRM_DEBUG_KMS("\n");
0290 
0291     /*
0292      * This callback is not protected by drm_dev_enter/exit since we want to
0293      * turn off the display on regular driver unload. It's highly unlikely
0294      * that the underlying SPI controller is gone should this be called after
0295      * unplug.
0296      */
0297 
0298     ili9225_command(dbi, ILI9225_DISPLAY_CONTROL_1, 0x0000);
0299     msleep(50);
0300     ili9225_command(dbi, ILI9225_POWER_CONTROL_2, 0x0007);
0301     msleep(50);
0302     ili9225_command(dbi, ILI9225_POWER_CONTROL_1, 0x0a02);
0303 }
0304 
0305 static int ili9225_dbi_command(struct mipi_dbi *dbi, u8 *cmd, u8 *par,
0306                    size_t num)
0307 {
0308     struct spi_device *spi = dbi->spi;
0309     unsigned int bpw = 8;
0310     u32 speed_hz;
0311     int ret;
0312 
0313     gpiod_set_value_cansleep(dbi->dc, 0);
0314     speed_hz = mipi_dbi_spi_cmd_max_speed(spi, 1);
0315     ret = mipi_dbi_spi_transfer(spi, speed_hz, 8, cmd, 1);
0316     if (ret || !num)
0317         return ret;
0318 
0319     if (*cmd == ILI9225_WRITE_DATA_TO_GRAM && !dbi->swap_bytes)
0320         bpw = 16;
0321 
0322     gpiod_set_value_cansleep(dbi->dc, 1);
0323     speed_hz = mipi_dbi_spi_cmd_max_speed(spi, num);
0324 
0325     return mipi_dbi_spi_transfer(spi, speed_hz, bpw, par, num);
0326 }
0327 
0328 static const struct drm_simple_display_pipe_funcs ili9225_pipe_funcs = {
0329     .enable     = ili9225_pipe_enable,
0330     .disable    = ili9225_pipe_disable,
0331     .update     = ili9225_pipe_update,
0332 };
0333 
0334 static const struct drm_display_mode ili9225_mode = {
0335     DRM_SIMPLE_MODE(176, 220, 35, 44),
0336 };
0337 
0338 DEFINE_DRM_GEM_CMA_FOPS(ili9225_fops);
0339 
0340 static const struct drm_driver ili9225_driver = {
0341     .driver_features    = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
0342     .fops           = &ili9225_fops,
0343     DRM_GEM_CMA_DRIVER_OPS_VMAP,
0344     .name           = "ili9225",
0345     .desc           = "Ilitek ILI9225",
0346     .date           = "20171106",
0347     .major          = 1,
0348     .minor          = 0,
0349 };
0350 
0351 static const struct of_device_id ili9225_of_match[] = {
0352     { .compatible = "vot,v220hf01a-t" },
0353     {},
0354 };
0355 MODULE_DEVICE_TABLE(of, ili9225_of_match);
0356 
0357 static const struct spi_device_id ili9225_id[] = {
0358     { "v220hf01a-t", 0 },
0359     { },
0360 };
0361 MODULE_DEVICE_TABLE(spi, ili9225_id);
0362 
0363 static int ili9225_probe(struct spi_device *spi)
0364 {
0365     struct device *dev = &spi->dev;
0366     struct mipi_dbi_dev *dbidev;
0367     struct drm_device *drm;
0368     struct mipi_dbi *dbi;
0369     struct gpio_desc *rs;
0370     u32 rotation = 0;
0371     int ret;
0372 
0373     dbidev = devm_drm_dev_alloc(dev, &ili9225_driver,
0374                     struct mipi_dbi_dev, drm);
0375     if (IS_ERR(dbidev))
0376         return PTR_ERR(dbidev);
0377 
0378     dbi = &dbidev->dbi;
0379     drm = &dbidev->drm;
0380 
0381     dbi->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
0382     if (IS_ERR(dbi->reset))
0383         return dev_err_probe(dev, PTR_ERR(dbi->reset), "Failed to get GPIO 'reset'\n");
0384 
0385     rs = devm_gpiod_get(dev, "rs", GPIOD_OUT_LOW);
0386     if (IS_ERR(rs))
0387         return dev_err_probe(dev, PTR_ERR(rs), "Failed to get GPIO 'rs'\n");
0388 
0389     device_property_read_u32(dev, "rotation", &rotation);
0390 
0391     ret = mipi_dbi_spi_init(spi, dbi, rs);
0392     if (ret)
0393         return ret;
0394 
0395     /* override the command function set in  mipi_dbi_spi_init() */
0396     dbi->command = ili9225_dbi_command;
0397 
0398     ret = mipi_dbi_dev_init(dbidev, &ili9225_pipe_funcs, &ili9225_mode, rotation);
0399     if (ret)
0400         return ret;
0401 
0402     drm_mode_config_reset(drm);
0403 
0404     ret = drm_dev_register(drm, 0);
0405     if (ret)
0406         return ret;
0407 
0408     spi_set_drvdata(spi, drm);
0409 
0410     drm_fbdev_generic_setup(drm, 0);
0411 
0412     return 0;
0413 }
0414 
0415 static void ili9225_remove(struct spi_device *spi)
0416 {
0417     struct drm_device *drm = spi_get_drvdata(spi);
0418 
0419     drm_dev_unplug(drm);
0420     drm_atomic_helper_shutdown(drm);
0421 }
0422 
0423 static void ili9225_shutdown(struct spi_device *spi)
0424 {
0425     drm_atomic_helper_shutdown(spi_get_drvdata(spi));
0426 }
0427 
0428 static struct spi_driver ili9225_spi_driver = {
0429     .driver = {
0430         .name = "ili9225",
0431         .owner = THIS_MODULE,
0432         .of_match_table = ili9225_of_match,
0433     },
0434     .id_table = ili9225_id,
0435     .probe = ili9225_probe,
0436     .remove = ili9225_remove,
0437     .shutdown = ili9225_shutdown,
0438 };
0439 module_spi_driver(ili9225_spi_driver);
0440 
0441 MODULE_DESCRIPTION("Ilitek ILI9225 DRM driver");
0442 MODULE_AUTHOR("David Lechner <david@lechnology.com>");
0443 MODULE_LICENSE("GPL");