linux/drivers/staging/fbtft/fbtft_device.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 *
   4 * Copyright (C) 2013, Noralf Tronnes
   5 */
   6
   7#define pr_fmt(fmt) "fbtft_device: " fmt
   8#include <linux/module.h>
   9#include <linux/kernel.h>
  10#include <linux/init.h>
  11#include <linux/gpio/consumer.h>
  12#include <linux/spi/spi.h>
  13#include <video/mipi_display.h>
  14
  15#include "fbtft.h"
  16
  17#define MAX_GPIOS 32
  18
  19static struct spi_device *spi_device;
  20static struct platform_device *p_device;
  21
  22static char *name;
  23module_param(name, charp, 0000);
  24MODULE_PARM_DESC(name,
  25                 "Devicename (required). name=list => list all supported devices.");
  26
  27static unsigned int rotate;
  28module_param(rotate, uint, 0000);
  29MODULE_PARM_DESC(rotate,
  30                 "Angle to rotate display counter clockwise: 0, 90, 180, 270");
  31
  32static unsigned int busnum;
  33module_param(busnum, uint, 0000);
  34MODULE_PARM_DESC(busnum, "SPI bus number (default=0)");
  35
  36static unsigned int cs;
  37module_param(cs, uint, 0000);
  38MODULE_PARM_DESC(cs, "SPI chip select (default=0)");
  39
  40static unsigned int speed;
  41module_param(speed, uint, 0000);
  42MODULE_PARM_DESC(speed, "SPI speed (override device default)");
  43
  44static int mode = -1;
  45module_param(mode, int, 0000);
  46MODULE_PARM_DESC(mode, "SPI mode (override device default)");
  47
  48static unsigned int fps;
  49module_param(fps, uint, 0000);
  50MODULE_PARM_DESC(fps, "Frames per second (override driver default)");
  51
  52static char *gamma;
  53module_param(gamma, charp, 0000);
  54MODULE_PARM_DESC(gamma,
  55                 "String representation of Gamma Curve(s). Driver specific.");
  56
  57static int txbuflen;
  58module_param(txbuflen, int, 0000);
  59MODULE_PARM_DESC(txbuflen, "txbuflen (override driver default)");
  60
  61static int bgr = -1;
  62module_param(bgr, int, 0000);
  63MODULE_PARM_DESC(bgr,
  64                 "BGR bit (supported by some drivers).");
  65
  66static unsigned int startbyte;
  67module_param(startbyte, uint, 0000);
  68MODULE_PARM_DESC(startbyte, "Sets the Start byte used by some SPI displays.");
  69
  70static bool custom;
  71module_param(custom, bool, 0000);
  72MODULE_PARM_DESC(custom, "Add a custom display device. Use speed= argument to make it a SPI device, else platform_device");
  73
  74static unsigned int width;
  75module_param(width, uint, 0000);
  76MODULE_PARM_DESC(width, "Display width, used with the custom argument");
  77
  78static unsigned int height;
  79module_param(height, uint, 0000);
  80MODULE_PARM_DESC(height, "Display height, used with the custom argument");
  81
  82static unsigned int buswidth = 8;
  83module_param(buswidth, uint, 0000);
  84MODULE_PARM_DESC(buswidth, "Display bus width, used with the custom argument");
  85
  86static s16 init[FBTFT_MAX_INIT_SEQUENCE];
  87static int init_num;
  88module_param_array(init, short, &init_num, 0000);
  89MODULE_PARM_DESC(init, "Init sequence, used with the custom argument");
  90
  91static unsigned long debug;
  92module_param(debug, ulong, 0000);
  93MODULE_PARM_DESC(debug,
  94                 "level: 0-7 (the remaining 29 bits is for advanced usage)");
  95
  96static unsigned int verbose = 3;
  97module_param(verbose, uint, 0000);
  98MODULE_PARM_DESC(verbose,
  99                 "0 silent, >1 show devices, >2 show devices before (default=3)");
 100
 101struct fbtft_device_display {
 102        char *name;
 103        struct spi_board_info *spi;
 104        struct platform_device *pdev;
 105};
 106
 107static void fbtft_device_pdev_release(struct device *dev);
 108
 109static int write_gpio16_wr_slow(struct fbtft_par *par, void *buf, size_t len);
 110static void adafruit18_green_tab_set_addr_win(struct fbtft_par *par,
 111                                              int xs, int ys, int xe, int ye);
 112
 113#define ADAFRUIT18_GAMMA \
 114                "02 1c 07 12 37 32 29 2d 29 25 2B 39 00 01 03 10\n" \
 115                "03 1d 07 06 2E 2C 29 2D 2E 2E 37 3F 00 00 02 10"
 116
 117#define CBERRY28_GAMMA \
 118                "D0 00 14 15 13 2C 42 43 4E 09 16 14 18 21\n" \
 119                "D0 00 14 15 13 0B 43 55 53 0C 17 14 23 20"
 120
 121static const s16 cberry28_init_sequence[] = {
 122        /* turn off sleep mode */
 123        -1, MIPI_DCS_EXIT_SLEEP_MODE,
 124        -2, 120,
 125
 126        /* set pixel format to RGB-565 */
 127        -1, MIPI_DCS_SET_PIXEL_FORMAT, MIPI_DCS_PIXEL_FMT_16BIT,
 128
 129        -1, 0xB2, 0x0C, 0x0C, 0x00, 0x33, 0x33,
 130
 131        /*
 132         * VGH = 13.26V
 133         * VGL = -10.43V
 134         */
 135        -1, 0xB7, 0x35,
 136
 137        /*
 138         * VDV and VRH register values come from command write
 139         * (instead of NVM)
 140         */
 141        -1, 0xC2, 0x01, 0xFF,
 142
 143        /*
 144         * VAP =  4.7V + (VCOM + VCOM offset + 0.5 * VDV)
 145         * VAN = -4.7V + (VCOM + VCOM offset + 0.5 * VDV)
 146         */
 147        -1, 0xC3, 0x17,
 148
 149        /* VDV = 0V */
 150        -1, 0xC4, 0x20,
 151
 152        /* VCOM = 0.675V */
 153        -1, 0xBB, 0x17,
 154
 155        /* VCOM offset = 0V */
 156        -1, 0xC5, 0x20,
 157
 158        /*
 159         * AVDD = 6.8V
 160         * AVCL = -4.8V
 161         * VDS = 2.3V
 162         */
 163        -1, 0xD0, 0xA4, 0xA1,
 164
 165        -1, MIPI_DCS_SET_DISPLAY_ON,
 166
 167        -3,
 168};
 169
 170static const s16 hy28b_init_sequence[] = {
 171        -1, 0x00e7, 0x0010, -1, 0x0000, 0x0001,
 172        -1, 0x0001, 0x0100, -1, 0x0002, 0x0700,
 173        -1, 0x0003, 0x1030, -1, 0x0004, 0x0000,
 174        -1, 0x0008, 0x0207, -1, 0x0009, 0x0000,
 175        -1, 0x000a, 0x0000, -1, 0x000c, 0x0001,
 176        -1, 0x000d, 0x0000, -1, 0x000f, 0x0000,
 177        -1, 0x0010, 0x0000, -1, 0x0011, 0x0007,
 178        -1, 0x0012, 0x0000, -1, 0x0013, 0x0000,
 179        -2, 50, -1, 0x0010, 0x1590, -1, 0x0011,
 180        0x0227, -2, 50, -1, 0x0012, 0x009c, -2, 50,
 181        -1, 0x0013, 0x1900, -1, 0x0029, 0x0023,
 182        -1, 0x002b, 0x000e, -2, 50,
 183        -1, 0x0020, 0x0000, -1, 0x0021, 0x0000,
 184        -2, 50, -1, 0x0050, 0x0000,
 185        -1, 0x0051, 0x00ef, -1, 0x0052, 0x0000,
 186        -1, 0x0053, 0x013f, -1, 0x0060, 0xa700,
 187        -1, 0x0061, 0x0001, -1, 0x006a, 0x0000,
 188        -1, 0x0080, 0x0000, -1, 0x0081, 0x0000,
 189        -1, 0x0082, 0x0000, -1, 0x0083, 0x0000,
 190        -1, 0x0084, 0x0000, -1, 0x0085, 0x0000,
 191        -1, 0x0090, 0x0010, -1, 0x0092, 0x0000,
 192        -1, 0x0093, 0x0003, -1, 0x0095, 0x0110,
 193        -1, 0x0097, 0x0000, -1, 0x0098, 0x0000,
 194        -1, 0x0007, 0x0133, -1, 0x0020, 0x0000,
 195        -1, 0x0021, 0x0000, -2, 100, -3 };
 196
 197#define HY28B_GAMMA \
 198        "04 1F 4 7 7 0 7 7 6 0\n" \
 199        "0F 00 1 7 4 0 0 0 6 7"
 200
 201static const s16 pitft_init_sequence[] = {
 202        -1, MIPI_DCS_SOFT_RESET,
 203        -2, 5,
 204        -1, MIPI_DCS_SET_DISPLAY_OFF,
 205        -1, 0xEF, 0x03, 0x80, 0x02,
 206        -1, 0xCF, 0x00, 0xC1, 0x30,
 207        -1, 0xED, 0x64, 0x03, 0x12, 0x81,
 208        -1, 0xE8, 0x85, 0x00, 0x78,
 209        -1, 0xCB, 0x39, 0x2C, 0x00, 0x34, 0x02,
 210        -1, 0xF7, 0x20,
 211        -1, 0xEA, 0x00, 0x00,
 212        -1, 0xC0, 0x23,
 213        -1, 0xC1, 0x10,
 214        -1, 0xC5, 0x3E, 0x28,
 215        -1, 0xC7, 0x86,
 216        -1, MIPI_DCS_SET_PIXEL_FORMAT, 0x55,
 217        -1, 0xB1, 0x00, 0x18,
 218        -1, 0xB6, 0x08, 0x82, 0x27,
 219        -1, 0xF2, 0x00,
 220        -1, MIPI_DCS_SET_GAMMA_CURVE, 0x01,
 221        -1, 0xE0, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E,
 222                0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00,
 223        -1, 0xE1, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31,
 224                0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F,
 225        -1, MIPI_DCS_EXIT_SLEEP_MODE,
 226        -2, 100,
 227        -1, MIPI_DCS_SET_DISPLAY_ON,
 228        -2, 20,
 229        -3
 230};
 231
 232static const s16 waveshare32b_init_sequence[] = {
 233        -1, 0xCB, 0x39, 0x2C, 0x00, 0x34, 0x02,
 234        -1, 0xCF, 0x00, 0xC1, 0x30,
 235        -1, 0xE8, 0x85, 0x00, 0x78,
 236        -1, 0xEA, 0x00, 0x00,
 237        -1, 0xED, 0x64, 0x03, 0x12, 0x81,
 238        -1, 0xF7, 0x20,
 239        -1, 0xC0, 0x23,
 240        -1, 0xC1, 0x10,
 241        -1, 0xC5, 0x3E, 0x28,
 242        -1, 0xC7, 0x86,
 243        -1, MIPI_DCS_SET_ADDRESS_MODE, 0x28,
 244        -1, MIPI_DCS_SET_PIXEL_FORMAT, 0x55,
 245        -1, 0xB1, 0x00, 0x18,
 246        -1, 0xB6, 0x08, 0x82, 0x27,
 247        -1, 0xF2, 0x00,
 248        -1, MIPI_DCS_SET_GAMMA_CURVE, 0x01,
 249        -1, 0xE0, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E,
 250                0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00,
 251        -1, 0xE1, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31,
 252                0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F,
 253        -1, MIPI_DCS_EXIT_SLEEP_MODE,
 254        -2, 120,
 255        -1, MIPI_DCS_SET_DISPLAY_ON,
 256        -1, MIPI_DCS_WRITE_MEMORY_START,
 257        -3
 258};
 259
 260#define PIOLED_GAMMA    "0 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 " \
 261                        "2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 " \
 262                        "3 3 3 4 4 4 4 4 4 4 4 4 4 4 4"
 263
 264/* Supported displays in alphabetical order */
 265static struct fbtft_device_display displays[] = {
 266        {
 267                .name = "adafruit18",
 268                .spi = &(struct spi_board_info) {
 269                        .modalias = "fb_st7735r",
 270                        .max_speed_hz = 32000000,
 271                        .mode = SPI_MODE_0,
 272                        .platform_data = &(struct fbtft_platform_data) {
 273                                .display = {
 274                                        .buswidth = 8,
 275                                        .backlight = 1,
 276                                },
 277                                .gamma = ADAFRUIT18_GAMMA,
 278                        }
 279                }
 280        }, {
 281                .name = "adafruit18_green",
 282                .spi = &(struct spi_board_info) {
 283                        .modalias = "fb_st7735r",
 284                        .max_speed_hz = 4000000,
 285                        .mode = SPI_MODE_0,
 286                        .platform_data = &(struct fbtft_platform_data) {
 287                                .display = {
 288                                        .buswidth = 8,
 289                                        .backlight = 1,
 290                                        .fbtftops.set_addr_win =
 291                                            adafruit18_green_tab_set_addr_win,
 292                                },
 293                                .bgr = true,
 294                                .gamma = ADAFRUIT18_GAMMA,
 295                        }
 296                }
 297        }, {
 298                .name = "adafruit22",
 299                .spi = &(struct spi_board_info) {
 300                        .modalias = "fb_hx8340bn",
 301                        .max_speed_hz = 32000000,
 302                        .mode = SPI_MODE_0,
 303                        .platform_data = &(struct fbtft_platform_data) {
 304                                .display = {
 305                                        .buswidth = 9,
 306                                        .backlight = 1,
 307                                },
 308                                .bgr = true,
 309                        }
 310                }
 311        }, {
 312                .name = "adafruit22a",
 313                .spi = &(struct spi_board_info) {
 314                        .modalias = "fb_ili9340",
 315                        .max_speed_hz = 32000000,
 316                        .mode = SPI_MODE_0,
 317                        .platform_data = &(struct fbtft_platform_data) {
 318                                .display = {
 319                                        .buswidth = 8,
 320                                        .backlight = 1,
 321                                },
 322                                .bgr = true,
 323                        }
 324                }
 325        }, {
 326                .name = "adafruit28",
 327                .spi = &(struct spi_board_info) {
 328                        .modalias = "fb_ili9341",
 329                        .max_speed_hz = 32000000,
 330                        .mode = SPI_MODE_0,
 331                        .platform_data = &(struct fbtft_platform_data) {
 332                                .display = {
 333                                        .buswidth = 8,
 334                                        .backlight = 1,
 335                                },
 336                                .bgr = true,
 337                        }
 338                }
 339        }, {
 340                .name = "adafruit13m",
 341                .spi = &(struct spi_board_info) {
 342                        .modalias = "fb_ssd1306",
 343                        .max_speed_hz = 16000000,
 344                        .mode = SPI_MODE_0,
 345                        .platform_data = &(struct fbtft_platform_data) {
 346                                .display = {
 347                                        .buswidth = 8,
 348                                },
 349                        }
 350                }
 351        }, {
 352                .name = "admatec_c-berry28",
 353                .spi = &(struct spi_board_info) {
 354                        .modalias = "fb_st7789v",
 355                        .max_speed_hz = 48000000,
 356                        .mode = SPI_MODE_0,
 357                        .platform_data = &(struct fbtft_platform_data) {
 358                                .display = {
 359                                        .buswidth = 8,
 360                                        .backlight = 1,
 361                                        .init_sequence = cberry28_init_sequence,
 362                                },
 363                                .gamma = CBERRY28_GAMMA,
 364                        }
 365                }
 366        }, {
 367                .name = "agm1264k-fl",
 368                .pdev = &(struct platform_device) {
 369                        .name = "fb_agm1264k-fl",
 370                        .id = 0,
 371                        .dev = {
 372                        .release = fbtft_device_pdev_release,
 373                        .platform_data = &(struct fbtft_platform_data) {
 374                                .display = {
 375                                        .buswidth = 8,
 376                                        .backlight = FBTFT_ONBOARD_BACKLIGHT,
 377                                },
 378                        },
 379                        }
 380                }
 381        }, {
 382                .name = "dogs102",
 383                .spi = &(struct spi_board_info) {
 384                        .modalias = "fb_uc1701",
 385                        .max_speed_hz = 8000000,
 386                        .mode = SPI_MODE_0,
 387                        .platform_data = &(struct fbtft_platform_data) {
 388                                .display = {
 389                                        .buswidth = 8,
 390                                },
 391                                .bgr = true,
 392                        }
 393                }
 394        }, {
 395                .name = "er_tftm050_2",
 396                .spi = &(struct spi_board_info) {
 397                        .modalias = "fb_ra8875",
 398                        .max_speed_hz = 5000000,
 399                        .mode = SPI_MODE_3,
 400                        .platform_data = &(struct fbtft_platform_data) {
 401                                .display = {
 402                                        .buswidth = 8,
 403                                        .backlight = 1,
 404                                        .width = 480,
 405                                        .height = 272,
 406                                },
 407                                .bgr = true,
 408                        }
 409                }
 410        }, {
 411                .name = "er_tftm070_5",
 412                .spi = &(struct spi_board_info) {
 413                        .modalias = "fb_ra8875",
 414                        .max_speed_hz = 5000000,
 415                        .mode = SPI_MODE_3,
 416                        .platform_data = &(struct fbtft_platform_data) {
 417                                .display = {
 418                                        .buswidth = 8,
 419                                        .backlight = 1,
 420                                        .width = 800,
 421                                        .height = 480,
 422                                },
 423                                .bgr = true,
 424                        }
 425                }
 426        }, {
 427                .name = "ew24ha0",
 428                .spi = &(struct spi_board_info) {
 429                        .modalias = "fb_uc1611",
 430                        .max_speed_hz = 32000000,
 431                        .mode = SPI_MODE_3,
 432                        .platform_data = &(struct fbtft_platform_data) {
 433                                .display = {
 434                                        .buswidth = 8,
 435                                },
 436                        }
 437                }
 438        }, {
 439                .name = "ew24ha0_9bit",
 440                .spi = &(struct spi_board_info) {
 441                        .modalias = "fb_uc1611",
 442                        .max_speed_hz = 32000000,
 443                        .mode = SPI_MODE_3,
 444                        .platform_data = &(struct fbtft_platform_data) {
 445                                .display = {
 446                                        .buswidth = 9,
 447                                },
 448                        }
 449                }
 450        }, {
 451                .name = "flexfb",
 452                .spi = &(struct spi_board_info) {
 453                        .modalias = "flexfb",
 454                        .max_speed_hz = 32000000,
 455                        .mode = SPI_MODE_0,
 456                }
 457        }, {
 458                .name = "flexpfb",
 459                .pdev = &(struct platform_device) {
 460                        .name = "flexpfb",
 461                        .id = 0,
 462                        .dev = {
 463                        .release = fbtft_device_pdev_release,
 464                        }
 465                }
 466        }, {
 467                .name = "freetronicsoled128",
 468                .spi = &(struct spi_board_info) {
 469                        .modalias = "fb_ssd1351",
 470                        .max_speed_hz = 20000000,
 471                        .mode = SPI_MODE_0,
 472                        .platform_data = &(struct fbtft_platform_data) {
 473                                .display = {
 474                                        .buswidth = 8,
 475                                        .backlight = FBTFT_ONBOARD_BACKLIGHT,
 476                                },
 477                                .bgr = true,
 478                        }
 479                }
 480        }, {
 481                .name = "hx8353d",
 482                .spi = &(struct spi_board_info) {
 483                        .modalias = "fb_hx8353d",
 484                        .max_speed_hz = 16000000,
 485                        .mode = SPI_MODE_0,
 486                        .platform_data = &(struct fbtft_platform_data) {
 487                                .display = {
 488                                        .buswidth = 8,
 489                                        .backlight = 1,
 490                                },
 491                        }
 492                }
 493        }, {
 494                .name = "hy28a",
 495                .spi = &(struct spi_board_info) {
 496                        .modalias = "fb_ili9320",
 497                        .max_speed_hz = 32000000,
 498                        .mode = SPI_MODE_3,
 499                        .platform_data = &(struct fbtft_platform_data) {
 500                                .display = {
 501                                        .buswidth = 8,
 502                                        .backlight = 1,
 503                                },
 504                                .startbyte = 0x70,
 505                                .bgr = true,
 506                        }
 507                }
 508        }, {
 509                .name = "hy28b",
 510                .spi = &(struct spi_board_info) {
 511                        .modalias = "fb_ili9325",
 512                        .max_speed_hz = 48000000,
 513                        .mode = SPI_MODE_3,
 514                        .platform_data = &(struct fbtft_platform_data) {
 515                                .display = {
 516                                        .buswidth = 8,
 517                                        .backlight = 1,
 518                                        .init_sequence = hy28b_init_sequence,
 519                                },
 520                                .startbyte = 0x70,
 521                                .bgr = true,
 522                                .fps = 50,
 523                                .gamma = HY28B_GAMMA,
 524                        }
 525                }
 526        }, {
 527                .name = "ili9481",
 528                .spi = &(struct spi_board_info) {
 529                        .modalias = "fb_ili9481",
 530                        .max_speed_hz = 32000000,
 531                        .mode = SPI_MODE_0,
 532                        .platform_data = &(struct fbtft_platform_data) {
 533                                .display = {
 534                                        .regwidth = 16,
 535                                        .buswidth = 8,
 536                                        .backlight = 1,
 537                                },
 538                                .bgr = true,
 539                        }
 540                }
 541        }, {
 542                .name = "itdb24",
 543                .pdev = &(struct platform_device) {
 544                        .name = "fb_s6d1121",
 545                        .id = 0,
 546                        .dev = {
 547                        .release = fbtft_device_pdev_release,
 548                        .platform_data = &(struct fbtft_platform_data) {
 549                                .display = {
 550                                        .buswidth = 8,
 551                                        .backlight = 1,
 552                                },
 553                                .bgr = false,
 554                        },
 555                        }
 556                }
 557        }, {
 558                .name = "itdb28",
 559                .pdev = &(struct platform_device) {
 560                        .name = "fb_ili9325",
 561                        .id = 0,
 562                        .dev = {
 563                        .release = fbtft_device_pdev_release,
 564                        .platform_data = &(struct fbtft_platform_data) {
 565                                .display = {
 566                                        .buswidth = 8,
 567                                        .backlight = 1,
 568                                },
 569                                .bgr = true,
 570                        },
 571                        }
 572                }
 573        }, {
 574                .name = "itdb28_spi",
 575                .spi = &(struct spi_board_info) {
 576                        .modalias = "fb_ili9325",
 577                        .max_speed_hz = 32000000,
 578                        .mode = SPI_MODE_0,
 579                        .platform_data = &(struct fbtft_platform_data) {
 580                                .display = {
 581                                        .buswidth = 8,
 582                                        .backlight = 1,
 583                                },
 584                                .bgr = true,
 585                        }
 586                }
 587        }, {
 588                .name = "mi0283qt-2",
 589                .spi = &(struct spi_board_info) {
 590                        .modalias = "fb_hx8347d",
 591                        .max_speed_hz = 32000000,
 592                        .mode = SPI_MODE_0,
 593                        .platform_data = &(struct fbtft_platform_data) {
 594                                .display = {
 595                                        .buswidth = 8,
 596                                        .backlight = 1,
 597                                },
 598                                .startbyte = 0x70,
 599                                .bgr = true,
 600                        }
 601                }
 602        }, {
 603                .name = "mi0283qt-9a",
 604                .spi = &(struct spi_board_info) {
 605                        .modalias = "fb_ili9341",
 606                        .max_speed_hz = 32000000,
 607                        .mode = SPI_MODE_0,
 608                        .platform_data = &(struct fbtft_platform_data) {
 609                                .display = {
 610                                        .buswidth = 9,
 611                                        .backlight = 1,
 612                                },
 613                                .bgr = true,
 614                        }
 615                }
 616        }, {
 617                .name = "mi0283qt-v2",
 618                .spi = &(struct spi_board_info) {
 619                        .modalias = "fb_watterott",
 620                        .max_speed_hz = 4000000,
 621                        .mode = SPI_MODE_3,
 622                        .platform_data = &(struct fbtft_platform_data) {
 623                        }
 624                }
 625        }, {
 626                .name = "nokia3310",
 627                .spi = &(struct spi_board_info) {
 628                        .modalias = "fb_pcd8544",
 629                        .max_speed_hz = 400000,
 630                        .mode = SPI_MODE_0,
 631                        .platform_data = &(struct fbtft_platform_data) {
 632                                .display = {
 633                                        .buswidth = 8,
 634                                },
 635                        }
 636                }
 637        }, {
 638                .name = "nokia3310a",
 639                .spi = &(struct spi_board_info) {
 640                        .modalias = "fb_tls8204",
 641                        .max_speed_hz = 1000000,
 642                        .mode = SPI_MODE_0,
 643                        .platform_data = &(struct fbtft_platform_data) {
 644                                .display = {
 645                                        .buswidth = 8,
 646                                },
 647                        }
 648                }
 649        }, {
 650                .name = "nokia5110",
 651                .spi = &(struct spi_board_info) {
 652                        .modalias = "fb_ili9163",
 653                        .max_speed_hz = 12000000,
 654                        .mode = SPI_MODE_0,
 655                        .platform_data = &(struct fbtft_platform_data) {
 656                                .display = {
 657                                        .buswidth = 8,
 658                                        .backlight = 1,
 659                                },
 660                                .bgr = true,
 661                        }
 662                }
 663        }, {
 664                .name = "piscreen",
 665                .spi = &(struct spi_board_info) {
 666                        .modalias = "fb_ili9486",
 667                        .max_speed_hz = 32000000,
 668                        .mode = SPI_MODE_0,
 669                        .platform_data = &(struct fbtft_platform_data) {
 670                                .display = {
 671                                        .regwidth = 16,
 672                                        .buswidth = 8,
 673                                        .backlight = 1,
 674                                },
 675                                .bgr = true,
 676                        }
 677                }
 678        }, {
 679                .name = "pitft",
 680                .spi = &(struct spi_board_info) {
 681                        .modalias = "fb_ili9340",
 682                        .max_speed_hz = 32000000,
 683                        .mode = SPI_MODE_0,
 684                        .chip_select = 0,
 685                        .platform_data = &(struct fbtft_platform_data) {
 686                                .display = {
 687                                        .buswidth = 8,
 688                                        .backlight = 1,
 689                                        .init_sequence = pitft_init_sequence,
 690                                },
 691                                .bgr = true,
 692                        }
 693                }
 694        }, {
 695                .name = "pioled",
 696                .spi = &(struct spi_board_info) {
 697                        .modalias = "fb_ssd1351",
 698                        .max_speed_hz = 20000000,
 699                        .mode = SPI_MODE_0,
 700                        .platform_data = &(struct fbtft_platform_data) {
 701                                .display = {
 702                                        .buswidth = 8,
 703                                },
 704                                .bgr = true,
 705                                .gamma = PIOLED_GAMMA
 706                        }
 707                }
 708        }, {
 709                .name = "rpi-display",
 710                .spi = &(struct spi_board_info) {
 711                        .modalias = "fb_ili9341",
 712                        .max_speed_hz = 32000000,
 713                        .mode = SPI_MODE_0,
 714                        .platform_data = &(struct fbtft_platform_data) {
 715                                .display = {
 716                                        .buswidth = 8,
 717                                        .backlight = 1,
 718                                },
 719                                .bgr = true,
 720                        }
 721                }
 722        }, {
 723                .name = "s6d02a1",
 724                .spi = &(struct spi_board_info) {
 725                        .modalias = "fb_s6d02a1",
 726                        .max_speed_hz = 32000000,
 727                        .mode = SPI_MODE_0,
 728                        .platform_data = &(struct fbtft_platform_data) {
 729                                .display = {
 730                                        .buswidth = 8,
 731                                        .backlight = 1,
 732                                },
 733                                .bgr = true,
 734                        }
 735                }
 736        }, {
 737                .name = "sainsmart18",
 738                .spi = &(struct spi_board_info) {
 739                        .modalias = "fb_st7735r",
 740                        .max_speed_hz = 32000000,
 741                        .mode = SPI_MODE_0,
 742                        .platform_data = &(struct fbtft_platform_data) {
 743                                .display = {
 744                                        .buswidth = 8,
 745                                },
 746                        }
 747                }
 748        }, {
 749                .name = "sainsmart32",
 750                .pdev = &(struct platform_device) {
 751                        .name = "fb_ssd1289",
 752                        .id = 0,
 753                        .dev = {
 754                        .release = fbtft_device_pdev_release,
 755                        .platform_data = &(struct fbtft_platform_data) {
 756                                .display = {
 757                                        .buswidth = 16,
 758                                        .txbuflen = -2, /* disable buffer */
 759                                        .backlight = 1,
 760                                        .fbtftops.write = write_gpio16_wr_slow,
 761                                },
 762                                .bgr = true,
 763                        },
 764                },
 765                }
 766        }, {
 767                .name = "sainsmart32_fast",
 768                .pdev = &(struct platform_device) {
 769                        .name = "fb_ssd1289",
 770                        .id = 0,
 771                        .dev = {
 772                        .release = fbtft_device_pdev_release,
 773                        .platform_data = &(struct fbtft_platform_data) {
 774                                .display = {
 775                                        .buswidth = 16,
 776                                        .txbuflen = -2, /* disable buffer */
 777                                        .backlight = 1,
 778                                },
 779                                .bgr = true,
 780                        },
 781                },
 782                }
 783        }, {
 784                .name = "sainsmart32_latched",
 785                .pdev = &(struct platform_device) {
 786                        .name = "fb_ssd1289",
 787                        .id = 0,
 788                        .dev = {
 789                        .release = fbtft_device_pdev_release,
 790                        .platform_data = &(struct fbtft_platform_data) {
 791                                .display = {
 792                                        .buswidth = 16,
 793                                        .txbuflen = -2, /* disable buffer */
 794                                        .backlight = 1,
 795                                        .fbtftops.write =
 796                                                fbtft_write_gpio16_wr_latched,
 797                                },
 798                                .bgr = true,
 799                        },
 800                },
 801                }
 802        }, {
 803                .name = "sainsmart32_spi",
 804                .spi = &(struct spi_board_info) {
 805                        .modalias = "fb_ssd1289",
 806                        .max_speed_hz = 16000000,
 807                        .mode = SPI_MODE_0,
 808                        .platform_data = &(struct fbtft_platform_data) {
 809                                .display = {
 810                                        .buswidth = 8,
 811                                        .backlight = 1,
 812                                },
 813                                .bgr = true,
 814                        }
 815                }
 816        }, {
 817                .name = "spidev",
 818                .spi = &(struct spi_board_info) {
 819                        .modalias = "spidev",
 820                        .max_speed_hz = 500000,
 821                        .bus_num = 0,
 822                        .chip_select = 0,
 823                        .mode = SPI_MODE_0,
 824                        .platform_data = &(struct fbtft_platform_data) {
 825                        }
 826                }
 827        }, {
 828                .name = "ssd1331",
 829                .spi = &(struct spi_board_info) {
 830                        .modalias = "fb_ssd1331",
 831                        .max_speed_hz = 20000000,
 832                        .mode = SPI_MODE_3,
 833                        .platform_data = &(struct fbtft_platform_data) {
 834                                .display = {
 835                                        .buswidth = 8,
 836                                },
 837                        }
 838                }
 839        }, {
 840                .name = "tinylcd35",
 841                .spi = &(struct spi_board_info) {
 842                        .modalias = "fb_tinylcd",
 843                        .max_speed_hz = 32000000,
 844                        .mode = SPI_MODE_0,
 845                        .platform_data = &(struct fbtft_platform_data) {
 846                                .display = {
 847                                        .buswidth = 8,
 848                                        .backlight = 1,
 849                                },
 850                                .bgr = true,
 851                        }
 852                }
 853        }, {
 854                .name = "tm022hdh26",
 855                .spi = &(struct spi_board_info) {
 856                        .modalias = "fb_ili9341",
 857                        .max_speed_hz = 32000000,
 858                        .mode = SPI_MODE_0,
 859                        .platform_data = &(struct fbtft_platform_data) {
 860                                .display = {
 861                                        .buswidth = 8,
 862                                        .backlight = 1,
 863                                },
 864                                .bgr = true,
 865                        }
 866                }
 867        }, {
 868                .name = "tontec35_9481", /* boards before 02 July 2014 */
 869                .spi = &(struct spi_board_info) {
 870                        .modalias = "fb_ili9481",
 871                        .max_speed_hz = 128000000,
 872                        .mode = SPI_MODE_3,
 873                        .platform_data = &(struct fbtft_platform_data) {
 874                                .display = {
 875                                        .buswidth = 8,
 876                                        .backlight = 1,
 877                                },
 878                                .bgr = true,
 879                        }
 880                }
 881        }, {
 882                .name = "tontec35_9486", /* boards after 02 July 2014 */
 883                .spi = &(struct spi_board_info) {
 884                        .modalias = "fb_ili9486",
 885                        .max_speed_hz = 128000000,
 886                        .mode = SPI_MODE_3,
 887                        .platform_data = &(struct fbtft_platform_data) {
 888                                .display = {
 889                                        .buswidth = 8,
 890                                        .backlight = 1,
 891                                },
 892                                .bgr = true,
 893                        }
 894                }
 895        }, {
 896                .name = "upd161704",
 897                .spi = &(struct spi_board_info) {
 898                        .modalias = "fb_upd161704",
 899                        .max_speed_hz = 32000000,
 900                        .mode = SPI_MODE_0,
 901                        .platform_data = &(struct fbtft_platform_data) {
 902                                .display = {
 903                                        .buswidth = 8,
 904                                },
 905                        }
 906                }
 907        }, {
 908                .name = "waveshare32b",
 909                .spi = &(struct spi_board_info) {
 910                        .modalias = "fb_ili9340",
 911                        .max_speed_hz = 48000000,
 912                        .mode = SPI_MODE_0,
 913                        .platform_data = &(struct fbtft_platform_data) {
 914                                .display = {
 915                                        .buswidth = 8,
 916                                        .backlight = 1,
 917                                        .init_sequence =
 918                                                waveshare32b_init_sequence,
 919                                },
 920                                .bgr = true,
 921                        }
 922                }
 923        }, {
 924                .name = "waveshare22",
 925                .spi = &(struct spi_board_info) {
 926                        .modalias = "fb_bd663474",
 927                        .max_speed_hz = 32000000,
 928                        .mode = SPI_MODE_3,
 929                        .platform_data = &(struct fbtft_platform_data) {
 930                                .display = {
 931                                        .buswidth = 8,
 932                                },
 933                        }
 934                }
 935        }, {
 936                /* This should be the last item.
 937                 * Used with the custom argument
 938                 */
 939                .name = "",
 940                .spi = &(struct spi_board_info) {
 941                        .modalias = "",
 942                        .max_speed_hz = 0,
 943                        .mode = SPI_MODE_0,
 944                        .platform_data = &(struct fbtft_platform_data) {
 945                        }
 946                },
 947                .pdev = &(struct platform_device) {
 948                        .name = "",
 949                        .id = 0,
 950                        .dev = {
 951                                .release = fbtft_device_pdev_release,
 952                                .platform_data = &(struct fbtft_platform_data) {
 953                                },
 954                        },
 955                },
 956        }
 957};
 958
 959static int write_gpio16_wr_slow(struct fbtft_par *par, void *buf, size_t len)
 960{
 961        u16 data;
 962        int i;
 963#ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO
 964        static u16 prev_data;
 965#endif
 966
 967        fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len,
 968                          "%s(len=%zu): ", __func__, len);
 969
 970        while (len) {
 971                data = *(u16 *)buf;
 972
 973                /* Start writing by pulling down /WR */
 974                gpiod_set_value(par->gpio.wr, 0);
 975
 976                /* Set data */
 977#ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO
 978                if (data == prev_data) {
 979                        gpiod_set_value(par->gpio.wr, 0); /* used as delay */
 980                } else {
 981                        for (i = 0; i < 16; i++) {
 982                                if ((data & 1) != (prev_data & 1))
 983                                        gpiod_set_value(par->gpio.db[i],
 984                                                        data & 1);
 985                                data >>= 1;
 986                                prev_data >>= 1;
 987                        }
 988                }
 989#else
 990                for (i = 0; i < 16; i++) {
 991                        gpiod_set_value(par->gpio.db[i], data & 1);
 992                        data >>= 1;
 993                }
 994#endif
 995
 996                /* Pullup /WR */
 997                gpiod_set_value(par->gpio.wr, 1);
 998
 999#ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO
1000                prev_data = *(u16 *)buf;
1001#endif
1002                buf += 2;
1003                len -= 2;
1004        }
1005
1006        return 0;
1007}
1008
1009static void adafruit18_green_tab_set_addr_win(struct fbtft_par *par,
1010                                              int xs, int ys, int xe, int ye)
1011{
1012        write_reg(par, 0x2A, 0, xs + 2, 0, xe + 2);
1013        write_reg(par, 0x2B, 0, ys + 1, 0, ye + 1);
1014        write_reg(par, 0x2C);
1015}
1016
1017static void fbtft_device_pdev_release(struct device *dev)
1018{
1019/* Needed to silence this message:
1020 * Device 'xxx' does not have a release() function,
1021 * it is broken and must be fixed
1022 */
1023}
1024
1025static int spi_device_found(struct device *dev, void *data)
1026{
1027        struct spi_device *spi = to_spi_device(dev);
1028
1029        dev_info(dev, "%s %s %dkHz %d bits mode=0x%02X\n", spi->modalias,
1030                 dev_name(dev), spi->max_speed_hz / 1000, spi->bits_per_word,
1031                 spi->mode);
1032
1033        return 0;
1034}
1035
1036static void pr_spi_devices(void)
1037{
1038        pr_debug("SPI devices registered:\n");
1039        bus_for_each_dev(&spi_bus_type, NULL, NULL, spi_device_found);
1040}
1041
1042static int p_device_found(struct device *dev, void *data)
1043{
1044        struct platform_device
1045        *pdev = to_platform_device(dev);
1046
1047        if (strstr(pdev->name, "fb"))
1048                dev_info(dev, "%s id=%d pdata? %s\n", pdev->name, pdev->id,
1049                         pdev->dev.platform_data ? "yes" : "no");
1050
1051        return 0;
1052}
1053
1054static void pr_p_devices(void)
1055{
1056        pr_debug("'fb' Platform devices registered:\n");
1057        bus_for_each_dev(&platform_bus_type, NULL, NULL, p_device_found);
1058}
1059
1060#ifdef MODULE
1061static void fbtft_device_spi_delete(struct spi_master *master, unsigned int cs)
1062{
1063        struct device *dev;
1064        char str[32];
1065
1066        snprintf(str, sizeof(str), "%s.%u", dev_name(&master->dev), cs);
1067
1068        dev = bus_find_device_by_name(&spi_bus_type, NULL, str);
1069        if (dev) {
1070                if (verbose)
1071                        dev_info(dev, "Deleting %s\n", str);
1072                device_del(dev);
1073        }
1074}
1075
1076static int fbtft_device_spi_device_register(struct spi_board_info *spi)
1077{
1078        struct spi_master *master;
1079
1080        master = spi_busnum_to_master(spi->bus_num);
1081        if (!master) {
1082                pr_err("spi_busnum_to_master(%d) returned NULL\n",
1083                       spi->bus_num);
1084                return -EINVAL;
1085        }
1086        /* make sure it's available */
1087        fbtft_device_spi_delete(master, spi->chip_select);
1088        spi_device = spi_new_device(master, spi);
1089        put_device(&master->dev);
1090        if (!spi_device) {
1091                dev_err(&master->dev, "spi_new_device() returned NULL\n");
1092                return -EPERM;
1093        }
1094        return 0;
1095}
1096#else
1097static int fbtft_device_spi_device_register(struct spi_board_info *spi)
1098{
1099        return spi_register_board_info(spi, 1);
1100}
1101#endif
1102
1103static int __init fbtft_device_init(void)
1104{
1105        struct spi_board_info *spi = NULL;
1106        struct fbtft_platform_data *pdata;
1107        bool found = false;
1108        int i = 0;
1109        int ret = 0;
1110
1111        if (!name) {
1112#ifdef MODULE
1113                pr_err("missing module parameter: 'name'\n");
1114                return -EINVAL;
1115#else
1116                return 0;
1117#endif
1118        }
1119
1120        if (init_num > FBTFT_MAX_INIT_SEQUENCE) {
1121                pr_err("init parameter: exceeded max array size: %d\n",
1122                       FBTFT_MAX_INIT_SEQUENCE);
1123                return -EINVAL;
1124        }
1125
1126        if (verbose > 2) {
1127                pr_spi_devices(); /* print list of registered SPI devices */
1128                pr_p_devices(); /* print list of 'fb' platform devices */
1129        }
1130
1131        pr_debug("name='%s', busnum=%d, cs=%d\n", name, busnum, cs);
1132
1133        if (rotate > 0 && rotate < 4) {
1134                rotate = (4 - rotate) * 90;
1135                pr_warn("argument 'rotate' should be an angle. Values 1-3 is deprecated. Setting it to %d.\n",
1136                        rotate);
1137        }
1138        if (rotate != 0 && rotate != 90 && rotate != 180 && rotate != 270) {
1139                pr_warn("argument 'rotate' illegal value: %d. Setting it to 0.\n",
1140                        rotate);
1141                rotate = 0;
1142        }
1143
1144        /* name=list lists all supported displays */
1145        if (strcmp(name, "list") == 0) {
1146                pr_info("Supported displays:\n");
1147
1148                for (i = 0; i < ARRAY_SIZE(displays); i++)
1149                        pr_info("%s\n", displays[i].name);
1150                return -ECANCELED;
1151        }
1152
1153        if (custom) {
1154                i = ARRAY_SIZE(displays) - 1;
1155                displays[i].name = name;
1156                if (speed == 0) {
1157                        displays[i].pdev->name = name;
1158                        displays[i].spi = NULL;
1159                } else {
1160                        size_t len;
1161
1162                        len = strlcpy(displays[i].spi->modalias, name,
1163                                      SPI_NAME_SIZE);
1164                        if (len >= SPI_NAME_SIZE)
1165                                pr_warn("modalias (name) truncated to: %s\n",
1166                                        displays[i].spi->modalias);
1167                        displays[i].pdev = NULL;
1168                }
1169        }
1170
1171        for (i = 0; i < ARRAY_SIZE(displays); i++) {
1172                if (strncmp(name, displays[i].name, SPI_NAME_SIZE) == 0) {
1173                        if (displays[i].spi) {
1174                                spi = displays[i].spi;
1175                                spi->chip_select = cs;
1176                                spi->bus_num = busnum;
1177                                if (speed)
1178                                        spi->max_speed_hz = speed;
1179                                if (mode != -1)
1180                                        spi->mode = mode;
1181                                pdata = (void *)spi->platform_data;
1182                        } else if (displays[i].pdev) {
1183                                p_device = displays[i].pdev;
1184                                pdata = p_device->dev.platform_data;
1185                        } else {
1186                                pr_err("broken displays array\n");
1187                                return -EINVAL;
1188                        }
1189
1190                        pdata->rotate = rotate;
1191                        if (bgr == 0)
1192                                pdata->bgr = false;
1193                        else if (bgr == 1)
1194                                pdata->bgr = true;
1195                        if (startbyte)
1196                                pdata->startbyte = startbyte;
1197                        if (gamma)
1198                                pdata->gamma = gamma;
1199                        pdata->display.debug = debug;
1200                        if (fps)
1201                                pdata->fps = fps;
1202                        if (txbuflen)
1203                                pdata->txbuflen = txbuflen;
1204                        if (init_num)
1205                                pdata->display.init_sequence = init;
1206                        if (custom) {
1207                                pdata->display.width = width;
1208                                pdata->display.height = height;
1209                                pdata->display.buswidth = buswidth;
1210                                pdata->display.backlight = 1;
1211                        }
1212
1213                        if (displays[i].spi) {
1214                                ret = fbtft_device_spi_device_register(spi);
1215                                if (ret) {
1216                                        pr_err("failed to register SPI device\n");
1217                                        return ret;
1218                                }
1219                        } else {
1220                                ret = platform_device_register(p_device);
1221                                if (ret < 0) {
1222                                        pr_err("platform_device_register() returned %d\n",
1223                                               ret);
1224                                        return ret;
1225                                }
1226                        }
1227                        found = true;
1228                        break;
1229                }
1230        }
1231
1232        if (!found) {
1233                pr_err("display not supported: '%s'\n", name);
1234                return -EINVAL;
1235        }
1236
1237        if (spi_device && (verbose > 1))
1238                pr_spi_devices();
1239        if (p_device && (verbose > 1))
1240                pr_p_devices();
1241
1242        return 0;
1243}
1244
1245static void __exit fbtft_device_exit(void)
1246{
1247        if (spi_device) {
1248                device_del(&spi_device->dev);
1249                kfree(spi_device);
1250        }
1251
1252        if (p_device)
1253                platform_device_unregister(p_device);
1254}
1255
1256arch_initcall(fbtft_device_init);
1257module_exit(fbtft_device_exit);
1258
1259MODULE_DESCRIPTION("Add a FBTFT device.");
1260MODULE_AUTHOR("Noralf Tronnes");
1261MODULE_LICENSE("GPL");
1262