linux/drivers/gpu/drm/tinydrm/ili9225.c
<<
>>
Prefs
   1/*
   2 * DRM driver for Ilitek ILI9225 panels
   3 *
   4 * Copyright 2017 David Lechner <david@lechnology.com>
   5 *
   6 * Some code copied from mipi-dbi.c
   7 * Copyright 2016 Noralf Trønnes
   8 *
   9 * This program is free software; you can redistribute it and/or modify
  10 * it under the terms of the GNU General Public License as published by
  11 * the Free Software Foundation; either version 2 of the License, or
  12 * (at your option) any later version.
  13 */
  14
  15#include <linux/delay.h>
  16#include <linux/dma-buf.h>
  17#include <linux/gpio/consumer.h>
  18#include <linux/module.h>
  19#include <linux/property.h>
  20#include <linux/spi/spi.h>
  21#include <video/mipi_display.h>
  22
  23#include <drm/drm_fb_helper.h>
  24#include <drm/drm_gem_framebuffer_helper.h>
  25#include <drm/tinydrm/mipi-dbi.h>
  26#include <drm/tinydrm/tinydrm-helpers.h>
  27
  28#define ILI9225_DRIVER_READ_CODE        0x00
  29#define ILI9225_DRIVER_OUTPUT_CONTROL   0x01
  30#define ILI9225_LCD_AC_DRIVING_CONTROL  0x02
  31#define ILI9225_ENTRY_MODE              0x03
  32#define ILI9225_DISPLAY_CONTROL_1       0x07
  33#define ILI9225_BLANK_PERIOD_CONTROL_1  0x08
  34#define ILI9225_FRAME_CYCLE_CONTROL     0x0b
  35#define ILI9225_INTERFACE_CONTROL       0x0c
  36#define ILI9225_OSCILLATION_CONTROL     0x0f
  37#define ILI9225_POWER_CONTROL_1         0x10
  38#define ILI9225_POWER_CONTROL_2         0x11
  39#define ILI9225_POWER_CONTROL_3         0x12
  40#define ILI9225_POWER_CONTROL_4         0x13
  41#define ILI9225_POWER_CONTROL_5         0x14
  42#define ILI9225_VCI_RECYCLING           0x15
  43#define ILI9225_RAM_ADDRESS_SET_1       0x20
  44#define ILI9225_RAM_ADDRESS_SET_2       0x21
  45#define ILI9225_WRITE_DATA_TO_GRAM      0x22
  46#define ILI9225_SOFTWARE_RESET          0x28
  47#define ILI9225_GATE_SCAN_CONTROL       0x30
  48#define ILI9225_VERTICAL_SCROLL_1       0x31
  49#define ILI9225_VERTICAL_SCROLL_2       0x32
  50#define ILI9225_VERTICAL_SCROLL_3       0x33
  51#define ILI9225_PARTIAL_DRIVING_POS_1   0x34
  52#define ILI9225_PARTIAL_DRIVING_POS_2   0x35
  53#define ILI9225_HORIZ_WINDOW_ADDR_1     0x36
  54#define ILI9225_HORIZ_WINDOW_ADDR_2     0x37
  55#define ILI9225_VERT_WINDOW_ADDR_1      0x38
  56#define ILI9225_VERT_WINDOW_ADDR_2      0x39
  57#define ILI9225_GAMMA_CONTROL_1         0x50
  58#define ILI9225_GAMMA_CONTROL_2         0x51
  59#define ILI9225_GAMMA_CONTROL_3         0x52
  60#define ILI9225_GAMMA_CONTROL_4         0x53
  61#define ILI9225_GAMMA_CONTROL_5         0x54
  62#define ILI9225_GAMMA_CONTROL_6         0x55
  63#define ILI9225_GAMMA_CONTROL_7         0x56
  64#define ILI9225_GAMMA_CONTROL_8         0x57
  65#define ILI9225_GAMMA_CONTROL_9         0x58
  66#define ILI9225_GAMMA_CONTROL_10        0x59
  67
  68static inline int ili9225_command(struct mipi_dbi *mipi, u8 cmd, u16 data)
  69{
  70        u8 par[2] = { data >> 8, data & 0xff };
  71
  72        return mipi_dbi_command_buf(mipi, cmd, par, 2);
  73}
  74
  75static int ili9225_fb_dirty(struct drm_framebuffer *fb,
  76                            struct drm_file *file_priv, unsigned int flags,
  77                            unsigned int color, struct drm_clip_rect *clips,
  78                            unsigned int num_clips)
  79{
  80        struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
  81        struct tinydrm_device *tdev = fb->dev->dev_private;
  82        struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
  83        bool swap = mipi->swap_bytes;
  84        struct drm_clip_rect clip;
  85        u16 x_start, y_start;
  86        u16 x1, x2, y1, y2;
  87        int ret = 0;
  88        bool full;
  89        void *tr;
  90
  91        mutex_lock(&tdev->dirty_lock);
  92
  93        if (!mipi->enabled)
  94                goto out_unlock;
  95
  96        /* fbdev can flush even when we're not interested */
  97        if (tdev->pipe.plane.fb != fb)
  98                goto out_unlock;
  99
 100        full = tinydrm_merge_clips(&clip, clips, num_clips, flags,
 101                                   fb->width, fb->height);
 102
 103        DRM_DEBUG("Flushing [FB:%d] x1=%u, x2=%u, y1=%u, y2=%u\n", fb->base.id,
 104                  clip.x1, clip.x2, clip.y1, clip.y2);
 105
 106        if (!mipi->dc || !full || swap ||
 107            fb->format->format == DRM_FORMAT_XRGB8888) {
 108                tr = mipi->tx_buf;
 109                ret = mipi_dbi_buf_copy(mipi->tx_buf, fb, &clip, swap);
 110                if (ret)
 111                        goto out_unlock;
 112        } else {
 113                tr = cma_obj->vaddr;
 114        }
 115
 116        switch (mipi->rotation) {
 117        default:
 118                x1 = clip.x1;
 119                x2 = clip.x2 - 1;
 120                y1 = clip.y1;
 121                y2 = clip.y2 - 1;
 122                x_start = x1;
 123                y_start = y1;
 124                break;
 125        case 90:
 126                x1 = clip.y1;
 127                x2 = clip.y2 - 1;
 128                y1 = fb->width - clip.x2;
 129                y2 = fb->width - clip.x1 - 1;
 130                x_start = x1;
 131                y_start = y2;
 132                break;
 133        case 180:
 134                x1 = fb->width - clip.x2;
 135                x2 = fb->width - clip.x1 - 1;
 136                y1 = fb->height - clip.y2;
 137                y2 = fb->height - clip.y1 - 1;
 138                x_start = x2;
 139                y_start = y2;
 140                break;
 141        case 270:
 142                x1 = fb->height - clip.y2;
 143                x2 = fb->height - clip.y1 - 1;
 144                y1 = clip.x1;
 145                y2 = clip.x2 - 1;
 146                x_start = x2;
 147                y_start = y1;
 148                break;
 149        }
 150
 151        ili9225_command(mipi, ILI9225_HORIZ_WINDOW_ADDR_1, x2);
 152        ili9225_command(mipi, ILI9225_HORIZ_WINDOW_ADDR_2, x1);
 153        ili9225_command(mipi, ILI9225_VERT_WINDOW_ADDR_1, y2);
 154        ili9225_command(mipi, ILI9225_VERT_WINDOW_ADDR_2, y1);
 155
 156        ili9225_command(mipi, ILI9225_RAM_ADDRESS_SET_1, x_start);
 157        ili9225_command(mipi, ILI9225_RAM_ADDRESS_SET_2, y_start);
 158
 159        ret = mipi_dbi_command_buf(mipi, ILI9225_WRITE_DATA_TO_GRAM, tr,
 160                                (clip.x2 - clip.x1) * (clip.y2 - clip.y1) * 2);
 161
 162out_unlock:
 163        mutex_unlock(&tdev->dirty_lock);
 164
 165        if (ret)
 166                dev_err_once(fb->dev->dev, "Failed to update display %d\n",
 167                             ret);
 168
 169        return ret;
 170}
 171
 172static const struct drm_framebuffer_funcs ili9225_fb_funcs = {
 173        .destroy        = drm_gem_fb_destroy,
 174        .create_handle  = drm_gem_fb_create_handle,
 175        .dirty          = ili9225_fb_dirty,
 176};
 177
 178static void ili9225_pipe_enable(struct drm_simple_display_pipe *pipe,
 179                                struct drm_crtc_state *crtc_state)
 180{
 181        struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
 182        struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
 183        struct device *dev = tdev->drm->dev;
 184        int ret;
 185        u8 am_id;
 186
 187        DRM_DEBUG_KMS("\n");
 188
 189        mipi_dbi_hw_reset(mipi);
 190
 191        /*
 192         * There don't seem to be two example init sequences that match, so
 193         * using the one from the popular Arduino library for this display.
 194         * https://github.com/Nkawu/TFT_22_ILI9225/blob/master/src/TFT_22_ILI9225.cpp
 195         */
 196
 197        ret = ili9225_command(mipi, ILI9225_POWER_CONTROL_1, 0x0000);
 198        if (ret) {
 199                DRM_DEV_ERROR(dev, "Error sending command %d\n", ret);
 200                return;
 201        }
 202        ili9225_command(mipi, ILI9225_POWER_CONTROL_2, 0x0000);
 203        ili9225_command(mipi, ILI9225_POWER_CONTROL_3, 0x0000);
 204        ili9225_command(mipi, ILI9225_POWER_CONTROL_4, 0x0000);
 205        ili9225_command(mipi, ILI9225_POWER_CONTROL_5, 0x0000);
 206
 207        msleep(40);
 208
 209        ili9225_command(mipi, ILI9225_POWER_CONTROL_2, 0x0018);
 210        ili9225_command(mipi, ILI9225_POWER_CONTROL_3, 0x6121);
 211        ili9225_command(mipi, ILI9225_POWER_CONTROL_4, 0x006f);
 212        ili9225_command(mipi, ILI9225_POWER_CONTROL_5, 0x495f);
 213        ili9225_command(mipi, ILI9225_POWER_CONTROL_1, 0x0800);
 214
 215        msleep(10);
 216
 217        ili9225_command(mipi, ILI9225_POWER_CONTROL_2, 0x103b);
 218
 219        msleep(50);
 220
 221        switch (mipi->rotation) {
 222        default:
 223                am_id = 0x30;
 224                break;
 225        case 90:
 226                am_id = 0x18;
 227                break;
 228        case 180:
 229                am_id = 0x00;
 230                break;
 231        case 270:
 232                am_id = 0x28;
 233                break;
 234        }
 235        ili9225_command(mipi, ILI9225_DRIVER_OUTPUT_CONTROL, 0x011c);
 236        ili9225_command(mipi, ILI9225_LCD_AC_DRIVING_CONTROL, 0x0100);
 237        ili9225_command(mipi, ILI9225_ENTRY_MODE, 0x1000 | am_id);
 238        ili9225_command(mipi, ILI9225_DISPLAY_CONTROL_1, 0x0000);
 239        ili9225_command(mipi, ILI9225_BLANK_PERIOD_CONTROL_1, 0x0808);
 240        ili9225_command(mipi, ILI9225_FRAME_CYCLE_CONTROL, 0x1100);
 241        ili9225_command(mipi, ILI9225_INTERFACE_CONTROL, 0x0000);
 242        ili9225_command(mipi, ILI9225_OSCILLATION_CONTROL, 0x0d01);
 243        ili9225_command(mipi, ILI9225_VCI_RECYCLING, 0x0020);
 244        ili9225_command(mipi, ILI9225_RAM_ADDRESS_SET_1, 0x0000);
 245        ili9225_command(mipi, ILI9225_RAM_ADDRESS_SET_2, 0x0000);
 246
 247        ili9225_command(mipi, ILI9225_GATE_SCAN_CONTROL, 0x0000);
 248        ili9225_command(mipi, ILI9225_VERTICAL_SCROLL_1, 0x00db);
 249        ili9225_command(mipi, ILI9225_VERTICAL_SCROLL_2, 0x0000);
 250        ili9225_command(mipi, ILI9225_VERTICAL_SCROLL_3, 0x0000);
 251        ili9225_command(mipi, ILI9225_PARTIAL_DRIVING_POS_1, 0x00db);
 252        ili9225_command(mipi, ILI9225_PARTIAL_DRIVING_POS_2, 0x0000);
 253
 254        ili9225_command(mipi, ILI9225_GAMMA_CONTROL_1, 0x0000);
 255        ili9225_command(mipi, ILI9225_GAMMA_CONTROL_2, 0x0808);
 256        ili9225_command(mipi, ILI9225_GAMMA_CONTROL_3, 0x080a);
 257        ili9225_command(mipi, ILI9225_GAMMA_CONTROL_4, 0x000a);
 258        ili9225_command(mipi, ILI9225_GAMMA_CONTROL_5, 0x0a08);
 259        ili9225_command(mipi, ILI9225_GAMMA_CONTROL_6, 0x0808);
 260        ili9225_command(mipi, ILI9225_GAMMA_CONTROL_7, 0x0000);
 261        ili9225_command(mipi, ILI9225_GAMMA_CONTROL_8, 0x0a00);
 262        ili9225_command(mipi, ILI9225_GAMMA_CONTROL_9, 0x0710);
 263        ili9225_command(mipi, ILI9225_GAMMA_CONTROL_10, 0x0710);
 264
 265        ili9225_command(mipi, ILI9225_DISPLAY_CONTROL_1, 0x0012);
 266
 267        msleep(50);
 268
 269        ili9225_command(mipi, ILI9225_DISPLAY_CONTROL_1, 0x1017);
 270
 271        mipi_dbi_enable_flush(mipi);
 272}
 273
 274static void ili9225_pipe_disable(struct drm_simple_display_pipe *pipe)
 275{
 276        struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
 277        struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
 278
 279        DRM_DEBUG_KMS("\n");
 280
 281        if (!mipi->enabled)
 282                return;
 283
 284        ili9225_command(mipi, ILI9225_DISPLAY_CONTROL_1, 0x0000);
 285        msleep(50);
 286        ili9225_command(mipi, ILI9225_POWER_CONTROL_2, 0x0007);
 287        msleep(50);
 288        ili9225_command(mipi, ILI9225_POWER_CONTROL_1, 0x0a02);
 289
 290        mipi->enabled = false;
 291}
 292
 293static int ili9225_dbi_command(struct mipi_dbi *mipi, u8 cmd, u8 *par,
 294                               size_t num)
 295{
 296        struct spi_device *spi = mipi->spi;
 297        unsigned int bpw = 8;
 298        u32 speed_hz;
 299        int ret;
 300
 301        gpiod_set_value_cansleep(mipi->dc, 0);
 302        speed_hz = mipi_dbi_spi_cmd_max_speed(spi, 1);
 303        ret = tinydrm_spi_transfer(spi, speed_hz, NULL, 8, &cmd, 1);
 304        if (ret || !num)
 305                return ret;
 306
 307        if (cmd == ILI9225_WRITE_DATA_TO_GRAM && !mipi->swap_bytes)
 308                bpw = 16;
 309
 310        gpiod_set_value_cansleep(mipi->dc, 1);
 311        speed_hz = mipi_dbi_spi_cmd_max_speed(spi, num);
 312
 313        return tinydrm_spi_transfer(spi, speed_hz, NULL, bpw, par, num);
 314}
 315
 316static const u32 ili9225_formats[] = {
 317        DRM_FORMAT_RGB565,
 318        DRM_FORMAT_XRGB8888,
 319};
 320
 321static int ili9225_init(struct device *dev, struct mipi_dbi *mipi,
 322                        const struct drm_simple_display_pipe_funcs *pipe_funcs,
 323                        struct drm_driver *driver,
 324                        const struct drm_display_mode *mode,
 325                        unsigned int rotation)
 326{
 327        size_t bufsize = mode->vdisplay * mode->hdisplay * sizeof(u16);
 328        struct tinydrm_device *tdev = &mipi->tinydrm;
 329        int ret;
 330
 331        if (!mipi->command)
 332                return -EINVAL;
 333
 334        mutex_init(&mipi->cmdlock);
 335
 336        mipi->tx_buf = devm_kmalloc(dev, bufsize, GFP_KERNEL);
 337        if (!mipi->tx_buf)
 338                return -ENOMEM;
 339
 340        ret = devm_tinydrm_init(dev, tdev, &ili9225_fb_funcs, driver);
 341        if (ret)
 342                return ret;
 343
 344        ret = tinydrm_display_pipe_init(tdev, pipe_funcs,
 345                                        DRM_MODE_CONNECTOR_VIRTUAL,
 346                                        ili9225_formats,
 347                                        ARRAY_SIZE(ili9225_formats), mode,
 348                                        rotation);
 349        if (ret)
 350                return ret;
 351
 352        tdev->drm->mode_config.preferred_depth = 16;
 353        mipi->rotation = rotation;
 354
 355        drm_mode_config_reset(tdev->drm);
 356
 357        DRM_DEBUG_KMS("preferred_depth=%u, rotation = %u\n",
 358                      tdev->drm->mode_config.preferred_depth, rotation);
 359
 360        return 0;
 361}
 362
 363static const struct drm_simple_display_pipe_funcs ili9225_pipe_funcs = {
 364        .enable         = ili9225_pipe_enable,
 365        .disable        = ili9225_pipe_disable,
 366        .update         = tinydrm_display_pipe_update,
 367        .prepare_fb     = tinydrm_display_pipe_prepare_fb,
 368};
 369
 370static const struct drm_display_mode ili9225_mode = {
 371        TINYDRM_MODE(176, 220, 35, 44),
 372};
 373
 374DEFINE_DRM_GEM_CMA_FOPS(ili9225_fops);
 375
 376static struct drm_driver ili9225_driver = {
 377        .driver_features        = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
 378                                  DRIVER_ATOMIC,
 379        .fops                   = &ili9225_fops,
 380        TINYDRM_GEM_DRIVER_OPS,
 381        .lastclose              = drm_fb_helper_lastclose,
 382        .name                   = "ili9225",
 383        .desc                   = "Ilitek ILI9225",
 384        .date                   = "20171106",
 385        .major                  = 1,
 386        .minor                  = 0,
 387};
 388
 389static const struct of_device_id ili9225_of_match[] = {
 390        { .compatible = "vot,v220hf01a-t" },
 391        {},
 392};
 393MODULE_DEVICE_TABLE(of, ili9225_of_match);
 394
 395static const struct spi_device_id ili9225_id[] = {
 396        { "v220hf01a-t", 0 },
 397        { },
 398};
 399MODULE_DEVICE_TABLE(spi, ili9225_id);
 400
 401static int ili9225_probe(struct spi_device *spi)
 402{
 403        struct device *dev = &spi->dev;
 404        struct mipi_dbi *mipi;
 405        struct gpio_desc *rs;
 406        u32 rotation = 0;
 407        int ret;
 408
 409        mipi = devm_kzalloc(dev, sizeof(*mipi), GFP_KERNEL);
 410        if (!mipi)
 411                return -ENOMEM;
 412
 413        mipi->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
 414        if (IS_ERR(mipi->reset)) {
 415                DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n");
 416                return PTR_ERR(mipi->reset);
 417        }
 418
 419        rs = devm_gpiod_get(dev, "rs", GPIOD_OUT_LOW);
 420        if (IS_ERR(rs)) {
 421                DRM_DEV_ERROR(dev, "Failed to get gpio 'rs'\n");
 422                return PTR_ERR(rs);
 423        }
 424
 425        device_property_read_u32(dev, "rotation", &rotation);
 426
 427        ret = mipi_dbi_spi_init(spi, mipi, rs);
 428        if (ret)
 429                return ret;
 430
 431        /* override the command function set in  mipi_dbi_spi_init() */
 432        mipi->command = ili9225_dbi_command;
 433
 434        ret = ili9225_init(&spi->dev, mipi, &ili9225_pipe_funcs,
 435                           &ili9225_driver, &ili9225_mode, rotation);
 436        if (ret)
 437                return ret;
 438
 439        spi_set_drvdata(spi, mipi);
 440
 441        return devm_tinydrm_register(&mipi->tinydrm);
 442}
 443
 444static void ili9225_shutdown(struct spi_device *spi)
 445{
 446        struct mipi_dbi *mipi = spi_get_drvdata(spi);
 447
 448        tinydrm_shutdown(&mipi->tinydrm);
 449}
 450
 451static struct spi_driver ili9225_spi_driver = {
 452        .driver = {
 453                .name = "ili9225",
 454                .owner = THIS_MODULE,
 455                .of_match_table = ili9225_of_match,
 456        },
 457        .id_table = ili9225_id,
 458        .probe = ili9225_probe,
 459        .shutdown = ili9225_shutdown,
 460};
 461module_spi_driver(ili9225_spi_driver);
 462
 463MODULE_DESCRIPTION("Ilitek ILI9225 DRM driver");
 464MODULE_AUTHOR("David Lechner <david@lechnology.com>");
 465MODULE_LICENSE("GPL");
 466