| /* |
| * |
| * Copyright (C) 2013, Noralf Tronnes |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| */ |
| |
| #define pr_fmt(fmt) "fbtft_device: " fmt |
| #include <linux/module.h> |
| #include <linux/kernel.h> |
| #include <linux/init.h> |
| #include <linux/gpio.h> |
| #include <linux/spi/spi.h> |
| #include <video/mipi_display.h> |
| |
| #include "fbtft.h" |
| |
| #define MAX_GPIOS 32 |
| |
| static struct spi_device *spi_device; |
| static struct platform_device *p_device; |
| |
| static char *name; |
| module_param(name, charp, 0000); |
| MODULE_PARM_DESC(name, "Devicename (required). name=list => list all supported devices."); |
| |
| static unsigned int rotate; |
| module_param(rotate, uint, 0000); |
| MODULE_PARM_DESC(rotate, |
| "Angle to rotate display counter clockwise: 0, 90, 180, 270"); |
| |
| static unsigned int busnum; |
| module_param(busnum, uint, 0000); |
| MODULE_PARM_DESC(busnum, "SPI bus number (default=0)"); |
| |
| static unsigned int cs; |
| module_param(cs, uint, 0000); |
| MODULE_PARM_DESC(cs, "SPI chip select (default=0)"); |
| |
| static unsigned int speed; |
| module_param(speed, uint, 0000); |
| MODULE_PARM_DESC(speed, "SPI speed (override device default)"); |
| |
| static int mode = -1; |
| module_param(mode, int, 0000); |
| MODULE_PARM_DESC(mode, "SPI mode (override device default)"); |
| |
| static char *gpios; |
| module_param(gpios, charp, 0000); |
| MODULE_PARM_DESC(gpios, |
| "List of gpios. Comma separated with the form: reset:23,dc:24 (when overriding the default, all gpios must be specified)"); |
| |
| static unsigned int fps; |
| module_param(fps, uint, 0000); |
| MODULE_PARM_DESC(fps, "Frames per second (override driver default)"); |
| |
| static char *gamma; |
| module_param(gamma, charp, 0000); |
| MODULE_PARM_DESC(gamma, |
| "String representation of Gamma Curve(s). Driver specific."); |
| |
| static int txbuflen; |
| module_param(txbuflen, int, 0000); |
| MODULE_PARM_DESC(txbuflen, "txbuflen (override driver default)"); |
| |
| static int bgr = -1; |
| module_param(bgr, int, 0000); |
| MODULE_PARM_DESC(bgr, |
| "BGR bit (supported by some drivers)."); |
| |
| static unsigned int startbyte; |
| module_param(startbyte, uint, 0000); |
| MODULE_PARM_DESC(startbyte, "Sets the Start byte used by some SPI displays."); |
| |
| static bool custom; |
| module_param(custom, bool, 0000); |
| MODULE_PARM_DESC(custom, "Add a custom display device. Use speed= argument to make it a SPI device, else platform_device"); |
| |
| static unsigned int width; |
| module_param(width, uint, 0000); |
| MODULE_PARM_DESC(width, "Display width, used with the custom argument"); |
| |
| static unsigned int height; |
| module_param(height, uint, 0000); |
| MODULE_PARM_DESC(height, "Display height, used with the custom argument"); |
| |
| static unsigned int buswidth = 8; |
| module_param(buswidth, uint, 0000); |
| MODULE_PARM_DESC(buswidth, "Display bus width, used with the custom argument"); |
| |
| static s16 init[FBTFT_MAX_INIT_SEQUENCE]; |
| static int init_num; |
| module_param_array(init, short, &init_num, 0000); |
| MODULE_PARM_DESC(init, "Init sequence, used with the custom argument"); |
| |
| static unsigned long debug; |
| module_param(debug, ulong, 0000); |
| MODULE_PARM_DESC(debug, |
| "level: 0-7 (the remaining 29 bits is for advanced usage)"); |
| |
| static unsigned int verbose = 3; |
| module_param(verbose, uint, 0000); |
| MODULE_PARM_DESC(verbose, |
| "0 silent, >0 show gpios, >1 show devices, >2 show devices before (default=3)"); |
| |
| struct fbtft_device_display { |
| char *name; |
| struct spi_board_info *spi; |
| struct platform_device *pdev; |
| }; |
| |
| static void fbtft_device_pdev_release(struct device *dev); |
| |
| static int write_gpio16_wr_slow(struct fbtft_par *par, void *buf, size_t len); |
| static void adafruit18_green_tab_set_addr_win(struct fbtft_par *par, |
| int xs, int ys, int xe, int ye); |
| |
| #define ADAFRUIT18_GAMMA \ |
| "02 1c 07 12 37 32 29 2d 29 25 2B 39 00 01 03 10\n" \ |
| "03 1d 07 06 2E 2C 29 2D 2E 2E 37 3F 00 00 02 10" |
| |
| #define CBERRY28_GAMMA \ |
| "D0 00 14 15 13 2C 42 43 4E 09 16 14 18 21\n" \ |
| "D0 00 14 15 13 0B 43 55 53 0C 17 14 23 20" |
| |
| static const s16 cberry28_init_sequence[] = { |
| /* turn off sleep mode */ |
| -1, MIPI_DCS_EXIT_SLEEP_MODE, |
| -2, 120, |
| |
| /* set pixel format to RGB-565 */ |
| -1, MIPI_DCS_SET_PIXEL_FORMAT, MIPI_DCS_PIXEL_FMT_16BIT, |
| |
| -1, 0xB2, 0x0C, 0x0C, 0x00, 0x33, 0x33, |
| |
| /* |
| * VGH = 13.26V |
| * VGL = -10.43V |
| */ |
| -1, 0xB7, 0x35, |
| |
| /* |
| * VDV and VRH register values come from command write |
| * (instead of NVM) |
| */ |
| -1, 0xC2, 0x01, 0xFF, |
| |
| /* |
| * VAP = 4.7V + (VCOM + VCOM offset + 0.5 * VDV) |
| * VAN = -4.7V + (VCOM + VCOM offset + 0.5 * VDV) |
| */ |
| -1, 0xC3, 0x17, |
| |
| /* VDV = 0V */ |
| -1, 0xC4, 0x20, |
| |
| /* VCOM = 0.675V */ |
| -1, 0xBB, 0x17, |
| |
| /* VCOM offset = 0V */ |
| -1, 0xC5, 0x20, |
| |
| /* |
| * AVDD = 6.8V |
| * AVCL = -4.8V |
| * VDS = 2.3V |
| */ |
| -1, 0xD0, 0xA4, 0xA1, |
| |
| -1, MIPI_DCS_SET_DISPLAY_ON, |
| |
| -3, |
| }; |
| |
| static const s16 hy28b_init_sequence[] = { |
| -1, 0x00e7, 0x0010, -1, 0x0000, 0x0001, |
| -1, 0x0001, 0x0100, -1, 0x0002, 0x0700, |
| -1, 0x0003, 0x1030, -1, 0x0004, 0x0000, |
| -1, 0x0008, 0x0207, -1, 0x0009, 0x0000, |
| -1, 0x000a, 0x0000, -1, 0x000c, 0x0001, |
| -1, 0x000d, 0x0000, -1, 0x000f, 0x0000, |
| -1, 0x0010, 0x0000, -1, 0x0011, 0x0007, |
| -1, 0x0012, 0x0000, -1, 0x0013, 0x0000, |
| -2, 50, -1, 0x0010, 0x1590, -1, 0x0011, |
| 0x0227, -2, 50, -1, 0x0012, 0x009c, -2, 50, |
| -1, 0x0013, 0x1900, -1, 0x0029, 0x0023, |
| -1, 0x002b, 0x000e, -2, 50, |
| -1, 0x0020, 0x0000, -1, 0x0021, 0x0000, |
| -2, 50, -1, 0x0050, 0x0000, |
| -1, 0x0051, 0x00ef, -1, 0x0052, 0x0000, |
| -1, 0x0053, 0x013f, -1, 0x0060, 0xa700, |
| -1, 0x0061, 0x0001, -1, 0x006a, 0x0000, |
| -1, 0x0080, 0x0000, -1, 0x0081, 0x0000, |
| -1, 0x0082, 0x0000, -1, 0x0083, 0x0000, |
| -1, 0x0084, 0x0000, -1, 0x0085, 0x0000, |
| -1, 0x0090, 0x0010, -1, 0x0092, 0x0000, |
| -1, 0x0093, 0x0003, -1, 0x0095, 0x0110, |
| -1, 0x0097, 0x0000, -1, 0x0098, 0x0000, |
| -1, 0x0007, 0x0133, -1, 0x0020, 0x0000, |
| -1, 0x0021, 0x0000, -2, 100, -3 }; |
| |
| #define HY28B_GAMMA \ |
| "04 1F 4 7 7 0 7 7 6 0\n" \ |
| "0F 00 1 7 4 0 0 0 6 7" |
| |
| static const s16 pitft_init_sequence[] = { |
| -1, MIPI_DCS_SOFT_RESET, |
| -2, 5, |
| -1, MIPI_DCS_SET_DISPLAY_OFF, |
| -1, 0xEF, 0x03, 0x80, 0x02, |
| -1, 0xCF, 0x00, 0xC1, 0x30, |
| -1, 0xED, 0x64, 0x03, 0x12, 0x81, |
| -1, 0xE8, 0x85, 0x00, 0x78, |
| -1, 0xCB, 0x39, 0x2C, 0x00, 0x34, 0x02, |
| -1, 0xF7, 0x20, |
| -1, 0xEA, 0x00, 0x00, |
| -1, 0xC0, 0x23, |
| -1, 0xC1, 0x10, |
| -1, 0xC5, 0x3E, 0x28, |
| -1, 0xC7, 0x86, |
| -1, MIPI_DCS_SET_PIXEL_FORMAT, 0x55, |
| -1, 0xB1, 0x00, 0x18, |
| -1, 0xB6, 0x08, 0x82, 0x27, |
| -1, 0xF2, 0x00, |
| -1, MIPI_DCS_SET_GAMMA_CURVE, 0x01, |
| -1, 0xE0, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, |
| 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00, |
| -1, 0xE1, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, |
| 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F, |
| -1, MIPI_DCS_EXIT_SLEEP_MODE, |
| -2, 100, |
| -1, MIPI_DCS_SET_DISPLAY_ON, |
| -2, 20, |
| -3 |
| }; |
| |
| static const s16 waveshare32b_init_sequence[] = { |
| -1, 0xCB, 0x39, 0x2C, 0x00, 0x34, 0x02, |
| -1, 0xCF, 0x00, 0xC1, 0x30, |
| -1, 0xE8, 0x85, 0x00, 0x78, |
| -1, 0xEA, 0x00, 0x00, |
| -1, 0xED, 0x64, 0x03, 0x12, 0x81, |
| -1, 0xF7, 0x20, |
| -1, 0xC0, 0x23, |
| -1, 0xC1, 0x10, |
| -1, 0xC5, 0x3E, 0x28, |
| -1, 0xC7, 0x86, |
| -1, MIPI_DCS_SET_ADDRESS_MODE, 0x28, |
| -1, MIPI_DCS_SET_PIXEL_FORMAT, 0x55, |
| -1, 0xB1, 0x00, 0x18, |
| -1, 0xB6, 0x08, 0x82, 0x27, |
| -1, 0xF2, 0x00, |
| -1, MIPI_DCS_SET_GAMMA_CURVE, 0x01, |
| -1, 0xE0, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, |
| 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00, |
| -1, 0xE1, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, |
| 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F, |
| -1, MIPI_DCS_EXIT_SLEEP_MODE, |
| -2, 120, |
| -1, MIPI_DCS_SET_DISPLAY_ON, |
| -1, MIPI_DCS_WRITE_MEMORY_START, |
| -3 |
| }; |
| |
| /* Supported displays in alphabetical order */ |
| static struct fbtft_device_display displays[] = { |
| { |
| .name = "adafruit18", |
| .spi = &(struct spi_board_info) { |
| .modalias = "fb_st7735r", |
| .max_speed_hz = 32000000, |
| .mode = SPI_MODE_0, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 8, |
| .backlight = 1, |
| }, |
| .gpios = (const struct fbtft_gpio []) { |
| { "reset", 25 }, |
| { "dc", 24 }, |
| { "led", 18 }, |
| {}, |
| }, |
| .gamma = ADAFRUIT18_GAMMA, |
| } |
| } |
| }, { |
| .name = "adafruit18_green", |
| .spi = &(struct spi_board_info) { |
| .modalias = "fb_st7735r", |
| .max_speed_hz = 4000000, |
| .mode = SPI_MODE_0, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 8, |
| .backlight = 1, |
| .fbtftops.set_addr_win = |
| adafruit18_green_tab_set_addr_win, |
| }, |
| .bgr = true, |
| .gpios = (const struct fbtft_gpio []) { |
| { "reset", 25 }, |
| { "dc", 24 }, |
| { "led", 18 }, |
| {}, |
| }, |
| .gamma = ADAFRUIT18_GAMMA, |
| } |
| } |
| }, { |
| .name = "adafruit22", |
| .spi = &(struct spi_board_info) { |
| .modalias = "fb_hx8340bn", |
| .max_speed_hz = 32000000, |
| .mode = SPI_MODE_0, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 9, |
| .backlight = 1, |
| }, |
| .bgr = true, |
| .gpios = (const struct fbtft_gpio []) { |
| { "reset", 25 }, |
| { "led", 23 }, |
| {}, |
| }, |
| } |
| } |
| }, { |
| .name = "adafruit22a", |
| .spi = &(struct spi_board_info) { |
| .modalias = "fb_ili9340", |
| .max_speed_hz = 32000000, |
| .mode = SPI_MODE_0, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 8, |
| .backlight = 1, |
| }, |
| .bgr = true, |
| .gpios = (const struct fbtft_gpio []) { |
| { "reset", 25 }, |
| { "dc", 24 }, |
| { "led", 18 }, |
| {}, |
| }, |
| } |
| } |
| }, { |
| .name = "adafruit28", |
| .spi = &(struct spi_board_info) { |
| .modalias = "fb_ili9341", |
| .max_speed_hz = 32000000, |
| .mode = SPI_MODE_0, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 8, |
| .backlight = 1, |
| }, |
| .bgr = true, |
| .gpios = (const struct fbtft_gpio []) { |
| { "reset", 25 }, |
| { "dc", 24 }, |
| { "led", 18 }, |
| {}, |
| }, |
| } |
| } |
| }, { |
| .name = "adafruit13m", |
| .spi = &(struct spi_board_info) { |
| .modalias = "fb_ssd1306", |
| .max_speed_hz = 16000000, |
| .mode = SPI_MODE_0, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 8, |
| }, |
| .gpios = (const struct fbtft_gpio []) { |
| { "reset", 25 }, |
| { "dc", 24 }, |
| {}, |
| }, |
| } |
| } |
| }, { |
| .name = "admatec_c-berry28", |
| .spi = &(struct spi_board_info) { |
| .modalias = "fb_st7789v", |
| .max_speed_hz = 48000000, |
| .mode = SPI_MODE_0, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 8, |
| .backlight = 1, |
| .init_sequence = cberry28_init_sequence, |
| }, |
| .gpios = (const struct fbtft_gpio []) { |
| { "reset", 25 }, |
| { "dc", 22 }, |
| { "led", 18 }, |
| {}, |
| }, |
| .gamma = CBERRY28_GAMMA, |
| } |
| } |
| }, { |
| .name = "agm1264k-fl", |
| .pdev = &(struct platform_device) { |
| .name = "fb_agm1264k-fl", |
| .id = 0, |
| .dev = { |
| .release = fbtft_device_pdev_release, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 8, |
| .backlight = FBTFT_ONBOARD_BACKLIGHT, |
| }, |
| .gpios = (const struct fbtft_gpio []) { |
| {}, |
| }, |
| }, |
| } |
| } |
| }, { |
| .name = "dogs102", |
| .spi = &(struct spi_board_info) { |
| .modalias = "fb_uc1701", |
| .max_speed_hz = 8000000, |
| .mode = SPI_MODE_0, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 8, |
| }, |
| .bgr = true, |
| .gpios = (const struct fbtft_gpio []) { |
| { "reset", 13 }, |
| { "dc", 6 }, |
| {}, |
| }, |
| } |
| } |
| }, { |
| .name = "er_tftm050_2", |
| .spi = &(struct spi_board_info) { |
| .modalias = "fb_ra8875", |
| .max_speed_hz = 5000000, |
| .mode = SPI_MODE_3, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 8, |
| .backlight = 1, |
| .width = 480, |
| .height = 272, |
| }, |
| .bgr = true, |
| .gpios = (const struct fbtft_gpio []) { |
| { "reset", 25 }, |
| { "dc", 24 }, |
| {}, |
| }, |
| } |
| } |
| }, { |
| .name = "er_tftm070_5", |
| .spi = &(struct spi_board_info) { |
| .modalias = "fb_ra8875", |
| .max_speed_hz = 5000000, |
| .mode = SPI_MODE_3, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 8, |
| .backlight = 1, |
| .width = 800, |
| .height = 480, |
| }, |
| .bgr = true, |
| .gpios = (const struct fbtft_gpio []) { |
| { "reset", 25 }, |
| { "dc", 24 }, |
| {}, |
| }, |
| } |
| } |
| }, { |
| .name = "ew24ha0", |
| .spi = &(struct spi_board_info) { |
| .modalias = "fb_uc1611", |
| .max_speed_hz = 32000000, |
| .mode = SPI_MODE_3, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 8, |
| }, |
| .gpios = (const struct fbtft_gpio []) { |
| { "dc", 24 }, |
| {}, |
| }, |
| } |
| } |
| }, { |
| .name = "ew24ha0_9bit", |
| .spi = &(struct spi_board_info) { |
| .modalias = "fb_uc1611", |
| .max_speed_hz = 32000000, |
| .mode = SPI_MODE_3, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 9, |
| }, |
| .gpios = (const struct fbtft_gpio []) { |
| {}, |
| }, |
| } |
| } |
| }, { |
| .name = "flexfb", |
| .spi = &(struct spi_board_info) { |
| .modalias = "flexfb", |
| .max_speed_hz = 32000000, |
| .mode = SPI_MODE_0, |
| .platform_data = &(struct fbtft_platform_data) { |
| .gpios = (const struct fbtft_gpio []) { |
| { "reset", 25 }, |
| { "dc", 24 }, |
| {}, |
| }, |
| } |
| } |
| }, { |
| .name = "flexpfb", |
| .pdev = &(struct platform_device) { |
| .name = "flexpfb", |
| .id = 0, |
| .dev = { |
| .release = fbtft_device_pdev_release, |
| .platform_data = &(struct fbtft_platform_data) { |
| .gpios = (const struct fbtft_gpio []) { |
| { "reset", 17 }, |
| { "dc", 1 }, |
| { "wr", 0 }, |
| { "cs", 21 }, |
| { "db00", 9 }, |
| { "db01", 11 }, |
| { "db02", 18 }, |
| { "db03", 23 }, |
| { "db04", 24 }, |
| { "db05", 25 }, |
| { "db06", 8 }, |
| { "db07", 7 }, |
| { "led", 4 }, |
| {}, |
| }, |
| }, |
| } |
| } |
| }, { |
| .name = "freetronicsoled128", |
| .spi = &(struct spi_board_info) { |
| .modalias = "fb_ssd1351", |
| .max_speed_hz = 20000000, |
| .mode = SPI_MODE_0, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 8, |
| .backlight = FBTFT_ONBOARD_BACKLIGHT, |
| }, |
| .bgr = true, |
| .gpios = (const struct fbtft_gpio []) { |
| { "reset", 24 }, |
| { "dc", 25 }, |
| {}, |
| }, |
| } |
| } |
| }, { |
| .name = "hx8353d", |
| .spi = &(struct spi_board_info) { |
| .modalias = "fb_hx8353d", |
| .max_speed_hz = 16000000, |
| .mode = SPI_MODE_0, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 8, |
| .backlight = 1, |
| }, |
| .gpios = (const struct fbtft_gpio []) { |
| { "reset", 25 }, |
| { "dc", 24 }, |
| { "led", 23 }, |
| {}, |
| }, |
| } |
| } |
| }, { |
| .name = "hy28a", |
| .spi = &(struct spi_board_info) { |
| .modalias = "fb_ili9320", |
| .max_speed_hz = 32000000, |
| .mode = SPI_MODE_3, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 8, |
| .backlight = 1, |
| }, |
| .startbyte = 0x70, |
| .bgr = true, |
| .gpios = (const struct fbtft_gpio []) { |
| { "reset", 25 }, |
| { "led", 18 }, |
| {}, |
| }, |
| } |
| } |
| }, { |
| .name = "hy28b", |
| .spi = &(struct spi_board_info) { |
| .modalias = "fb_ili9325", |
| .max_speed_hz = 48000000, |
| .mode = SPI_MODE_3, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 8, |
| .backlight = 1, |
| .init_sequence = hy28b_init_sequence, |
| }, |
| .startbyte = 0x70, |
| .bgr = true, |
| .fps = 50, |
| .gpios = (const struct fbtft_gpio []) { |
| { "reset", 25 }, |
| { "led", 18 }, |
| {}, |
| }, |
| .gamma = HY28B_GAMMA, |
| } |
| } |
| }, { |
| .name = "ili9481", |
| .spi = &(struct spi_board_info) { |
| .modalias = "fb_ili9481", |
| .max_speed_hz = 32000000, |
| .mode = SPI_MODE_0, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .regwidth = 16, |
| .buswidth = 8, |
| .backlight = 1, |
| }, |
| .bgr = true, |
| .gpios = (const struct fbtft_gpio []) { |
| { "reset", 25 }, |
| { "dc", 24 }, |
| { "led", 22 }, |
| {}, |
| }, |
| } |
| } |
| }, { |
| .name = "itdb24", |
| .pdev = &(struct platform_device) { |
| .name = "fb_s6d1121", |
| .id = 0, |
| .dev = { |
| .release = fbtft_device_pdev_release, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 8, |
| .backlight = 1, |
| }, |
| .bgr = false, |
| .gpios = (const struct fbtft_gpio []) { |
| /* Wiring for LCD adapter kit */ |
| { "reset", 7 }, |
| { "dc", 0 }, /* rev 2: 2 */ |
| { "wr", 1 }, /* rev 2: 3 */ |
| { "cs", 8 }, |
| { "db00", 17 }, |
| { "db01", 18 }, |
| { "db02", 21 }, /* rev 2: 27 */ |
| { "db03", 22 }, |
| { "db04", 23 }, |
| { "db05", 24 }, |
| { "db06", 25 }, |
| { "db07", 4 }, |
| {} |
| }, |
| }, |
| } |
| } |
| }, { |
| .name = "itdb28", |
| .pdev = &(struct platform_device) { |
| .name = "fb_ili9325", |
| .id = 0, |
| .dev = { |
| .release = fbtft_device_pdev_release, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 8, |
| .backlight = 1, |
| }, |
| .bgr = true, |
| .gpios = (const struct fbtft_gpio []) { |
| {}, |
| }, |
| }, |
| } |
| } |
| }, { |
| .name = "itdb28_spi", |
| .spi = &(struct spi_board_info) { |
| .modalias = "fb_ili9325", |
| .max_speed_hz = 32000000, |
| .mode = SPI_MODE_0, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 8, |
| .backlight = 1, |
| }, |
| .bgr = true, |
| .gpios = (const struct fbtft_gpio []) { |
| { "reset", 25 }, |
| { "dc", 24 }, |
| {}, |
| }, |
| } |
| } |
| }, { |
| .name = "mi0283qt-2", |
| .spi = &(struct spi_board_info) { |
| .modalias = "fb_hx8347d", |
| .max_speed_hz = 32000000, |
| .mode = SPI_MODE_0, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 8, |
| .backlight = 1, |
| }, |
| .startbyte = 0x70, |
| .bgr = true, |
| .gpios = (const struct fbtft_gpio []) { |
| { "reset", 25 }, |
| { "dc", 24 }, |
| { "led", 18 }, |
| {}, |
| }, |
| } |
| } |
| }, { |
| .name = "mi0283qt-9a", |
| .spi = &(struct spi_board_info) { |
| .modalias = "fb_ili9341", |
| .max_speed_hz = 32000000, |
| .mode = SPI_MODE_0, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 9, |
| .backlight = 1, |
| }, |
| .bgr = true, |
| .gpios = (const struct fbtft_gpio []) { |
| { "reset", 25 }, |
| { "led", 18 }, |
| {}, |
| }, |
| } |
| } |
| }, { |
| .name = "mi0283qt-v2", |
| .spi = &(struct spi_board_info) { |
| .modalias = "fb_watterott", |
| .max_speed_hz = 4000000, |
| .mode = SPI_MODE_3, |
| .platform_data = &(struct fbtft_platform_data) { |
| .gpios = (const struct fbtft_gpio []) { |
| { "reset", 25 }, |
| {}, |
| }, |
| } |
| } |
| }, { |
| .name = "nokia3310", |
| .spi = &(struct spi_board_info) { |
| .modalias = "fb_pcd8544", |
| .max_speed_hz = 400000, |
| .mode = SPI_MODE_0, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 8, |
| }, |
| .gpios = (const struct fbtft_gpio []) { |
| { "reset", 25 }, |
| { "dc", 24 }, |
| { "led", 23 }, |
| {}, |
| }, |
| } |
| } |
| }, { |
| .name = "nokia3310a", |
| .spi = &(struct spi_board_info) { |
| .modalias = "fb_tls8204", |
| .max_speed_hz = 1000000, |
| .mode = SPI_MODE_0, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 8, |
| }, |
| .gpios = (const struct fbtft_gpio []) { |
| { "reset", 25 }, |
| { "dc", 24 }, |
| { "led", 23 }, |
| {}, |
| }, |
| } |
| } |
| }, { |
| .name = "nokia5110", |
| .spi = &(struct spi_board_info) { |
| .modalias = "fb_ili9163", |
| .max_speed_hz = 12000000, |
| .mode = SPI_MODE_0, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 8, |
| .backlight = 1, |
| }, |
| .bgr = true, |
| .gpios = (const struct fbtft_gpio []) { |
| {}, |
| }, |
| } |
| } |
| }, { |
| |
| .name = "piscreen", |
| .spi = &(struct spi_board_info) { |
| .modalias = "fb_ili9486", |
| .max_speed_hz = 32000000, |
| .mode = SPI_MODE_0, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .regwidth = 16, |
| .buswidth = 8, |
| .backlight = 1, |
| }, |
| .bgr = true, |
| .gpios = (const struct fbtft_gpio []) { |
| { "reset", 25 }, |
| { "dc", 24 }, |
| { "led", 22 }, |
| {}, |
| }, |
| } |
| } |
| }, { |
| .name = "pitft", |
| .spi = &(struct spi_board_info) { |
| .modalias = "fb_ili9340", |
| .max_speed_hz = 32000000, |
| .mode = SPI_MODE_0, |
| .chip_select = 0, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 8, |
| .backlight = 1, |
| .init_sequence = pitft_init_sequence, |
| }, |
| .bgr = true, |
| .gpios = (const struct fbtft_gpio []) { |
| { "dc", 25 }, |
| {}, |
| }, |
| } |
| } |
| }, { |
| .name = "pioled", |
| .spi = &(struct spi_board_info) { |
| .modalias = "fb_ssd1351", |
| .max_speed_hz = 20000000, |
| .mode = SPI_MODE_0, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 8, |
| }, |
| .bgr = true, |
| .gpios = (const struct fbtft_gpio []) { |
| { "reset", 24 }, |
| { "dc", 25 }, |
| {}, |
| }, |
| .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 " |
| "2 2 2 2 2 2 2 3 " |
| "3 3 3 3 3 3 3 3 " |
| "3 3 3 3 3 3 3 3 " |
| "3 3 3 4 4 4 4 4 " |
| "4 4 4 4 4 4 4" |
| } |
| } |
| }, { |
| .name = "rpi-display", |
| .spi = &(struct spi_board_info) { |
| .modalias = "fb_ili9341", |
| .max_speed_hz = 32000000, |
| .mode = SPI_MODE_0, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 8, |
| .backlight = 1, |
| }, |
| .bgr = true, |
| .gpios = (const struct fbtft_gpio []) { |
| { "reset", 23 }, |
| { "dc", 24 }, |
| { "led", 18 }, |
| {}, |
| }, |
| } |
| } |
| }, { |
| .name = "s6d02a1", |
| .spi = &(struct spi_board_info) { |
| .modalias = "fb_s6d02a1", |
| .max_speed_hz = 32000000, |
| .mode = SPI_MODE_0, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 8, |
| .backlight = 1, |
| }, |
| .bgr = true, |
| .gpios = (const struct fbtft_gpio []) { |
| { "reset", 25 }, |
| { "dc", 24 }, |
| { "led", 23 }, |
| {}, |
| }, |
| } |
| } |
| }, { |
| .name = "sainsmart18", |
| .spi = &(struct spi_board_info) { |
| .modalias = "fb_st7735r", |
| .max_speed_hz = 32000000, |
| .mode = SPI_MODE_0, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 8, |
| }, |
| .gpios = (const struct fbtft_gpio []) { |
| { "reset", 25 }, |
| { "dc", 24 }, |
| {}, |
| }, |
| } |
| } |
| }, { |
| .name = "sainsmart32", |
| .pdev = &(struct platform_device) { |
| .name = "fb_ssd1289", |
| .id = 0, |
| .dev = { |
| .release = fbtft_device_pdev_release, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 16, |
| .txbuflen = -2, /* disable buffer */ |
| .backlight = 1, |
| .fbtftops.write = write_gpio16_wr_slow, |
| }, |
| .bgr = true, |
| .gpios = (const struct fbtft_gpio []) { |
| {}, |
| }, |
| }, |
| }, |
| } |
| }, { |
| .name = "sainsmart32_fast", |
| .pdev = &(struct platform_device) { |
| .name = "fb_ssd1289", |
| .id = 0, |
| .dev = { |
| .release = fbtft_device_pdev_release, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 16, |
| .txbuflen = -2, /* disable buffer */ |
| .backlight = 1, |
| }, |
| .bgr = true, |
| .gpios = (const struct fbtft_gpio []) { |
| {}, |
| }, |
| }, |
| }, |
| } |
| }, { |
| .name = "sainsmart32_latched", |
| .pdev = &(struct platform_device) { |
| .name = "fb_ssd1289", |
| .id = 0, |
| .dev = { |
| .release = fbtft_device_pdev_release, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 16, |
| .txbuflen = -2, /* disable buffer */ |
| .backlight = 1, |
| .fbtftops.write = |
| fbtft_write_gpio16_wr_latched, |
| }, |
| .bgr = true, |
| .gpios = (const struct fbtft_gpio []) { |
| {}, |
| }, |
| }, |
| }, |
| } |
| }, { |
| .name = "sainsmart32_spi", |
| .spi = &(struct spi_board_info) { |
| .modalias = "fb_ssd1289", |
| .max_speed_hz = 16000000, |
| .mode = SPI_MODE_0, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 8, |
| .backlight = 1, |
| }, |
| .bgr = true, |
| .gpios = (const struct fbtft_gpio []) { |
| { "reset", 25 }, |
| { "dc", 24 }, |
| {}, |
| }, |
| } |
| } |
| }, { |
| .name = "spidev", |
| .spi = &(struct spi_board_info) { |
| .modalias = "spidev", |
| .max_speed_hz = 500000, |
| .bus_num = 0, |
| .chip_select = 0, |
| .mode = SPI_MODE_0, |
| .platform_data = &(struct fbtft_platform_data) { |
| .gpios = (const struct fbtft_gpio []) { |
| {}, |
| }, |
| } |
| } |
| }, { |
| .name = "ssd1331", |
| .spi = &(struct spi_board_info) { |
| .modalias = "fb_ssd1331", |
| .max_speed_hz = 20000000, |
| .mode = SPI_MODE_3, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 8, |
| }, |
| .gpios = (const struct fbtft_gpio []) { |
| { "reset", 24 }, |
| { "dc", 25 }, |
| {}, |
| }, |
| } |
| } |
| }, { |
| .name = "tinylcd35", |
| .spi = &(struct spi_board_info) { |
| .modalias = "fb_tinylcd", |
| .max_speed_hz = 32000000, |
| .mode = SPI_MODE_0, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 8, |
| .backlight = 1, |
| }, |
| .bgr = true, |
| .gpios = (const struct fbtft_gpio []) { |
| { "reset", 25 }, |
| { "dc", 24 }, |
| { "led", 18 }, |
| {}, |
| }, |
| } |
| } |
| }, { |
| .name = "tm022hdh26", |
| .spi = &(struct spi_board_info) { |
| .modalias = "fb_ili9341", |
| .max_speed_hz = 32000000, |
| .mode = SPI_MODE_0, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 8, |
| .backlight = 1, |
| }, |
| .bgr = true, |
| .gpios = (const struct fbtft_gpio []) { |
| { "reset", 25 }, |
| { "dc", 24 }, |
| { "led", 18 }, |
| {}, |
| }, |
| } |
| } |
| }, { |
| .name = "tontec35_9481", /* boards before 02 July 2014 */ |
| .spi = &(struct spi_board_info) { |
| .modalias = "fb_ili9481", |
| .max_speed_hz = 128000000, |
| .mode = SPI_MODE_3, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 8, |
| .backlight = 1, |
| }, |
| .bgr = true, |
| .gpios = (const struct fbtft_gpio []) { |
| { "reset", 15 }, |
| { "dc", 25 }, |
| { "led_", 18 }, |
| {}, |
| }, |
| } |
| } |
| }, { |
| .name = "tontec35_9486", /* boards after 02 July 2014 */ |
| .spi = &(struct spi_board_info) { |
| .modalias = "fb_ili9486", |
| .max_speed_hz = 128000000, |
| .mode = SPI_MODE_3, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 8, |
| .backlight = 1, |
| }, |
| .bgr = true, |
| .gpios = (const struct fbtft_gpio []) { |
| { "reset", 15 }, |
| { "dc", 25 }, |
| { "led_", 18 }, |
| {}, |
| }, |
| } |
| } |
| }, { |
| .name = "upd161704", |
| .spi = &(struct spi_board_info) { |
| .modalias = "fb_upd161704", |
| .max_speed_hz = 32000000, |
| .mode = SPI_MODE_0, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 8, |
| }, |
| .gpios = (const struct fbtft_gpio []) { |
| { "reset", 24 }, |
| { "dc", 25 }, |
| {}, |
| }, |
| } |
| } |
| }, { |
| .name = "waveshare32b", |
| .spi = &(struct spi_board_info) { |
| .modalias = "fb_ili9340", |
| .max_speed_hz = 48000000, |
| .mode = SPI_MODE_0, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 8, |
| .backlight = 1, |
| .init_sequence = |
| waveshare32b_init_sequence, |
| }, |
| .bgr = true, |
| .gpios = (const struct fbtft_gpio []) { |
| { "reset", 27 }, |
| { "dc", 22 }, |
| {}, |
| }, |
| } |
| } |
| }, { |
| .name = "waveshare22", |
| .spi = &(struct spi_board_info) { |
| .modalias = "fb_bd663474", |
| .max_speed_hz = 32000000, |
| .mode = SPI_MODE_3, |
| .platform_data = &(struct fbtft_platform_data) { |
| .display = { |
| .buswidth = 8, |
| }, |
| .gpios = (const struct fbtft_gpio []) { |
| { "reset", 24 }, |
| { "dc", 25 }, |
| {}, |
| }, |
| } |
| } |
| }, { |
| /* This should be the last item. |
| * Used with the custom argument |
| */ |
| .name = "", |
| .spi = &(struct spi_board_info) { |
| .modalias = "", |
| .max_speed_hz = 0, |
| .mode = SPI_MODE_0, |
| .platform_data = &(struct fbtft_platform_data) { |
| .gpios = (const struct fbtft_gpio []) { |
| {}, |
| }, |
| } |
| }, |
| .pdev = &(struct platform_device) { |
| .name = "", |
| .id = 0, |
| .dev = { |
| .release = fbtft_device_pdev_release, |
| .platform_data = &(struct fbtft_platform_data) { |
| .gpios = (const struct fbtft_gpio []) { |
| {}, |
| }, |
| }, |
| }, |
| }, |
| } |
| }; |
| |
| static int write_gpio16_wr_slow(struct fbtft_par *par, void *buf, size_t len) |
| { |
| u16 data; |
| int i; |
| #ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO |
| static u16 prev_data; |
| #endif |
| |
| fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len, |
| "%s(len=%d): ", __func__, len); |
| |
| while (len) { |
| data = *(u16 *)buf; |
| |
| /* Start writing by pulling down /WR */ |
| gpio_set_value(par->gpio.wr, 0); |
| |
| /* Set data */ |
| #ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO |
| if (data == prev_data) { |
| gpio_set_value(par->gpio.wr, 0); /* used as delay */ |
| } else { |
| for (i = 0; i < 16; i++) { |
| if ((data & 1) != (prev_data & 1)) |
| gpio_set_value(par->gpio.db[i], |
| data & 1); |
| data >>= 1; |
| prev_data >>= 1; |
| } |
| } |
| #else |
| for (i = 0; i < 16; i++) { |
| gpio_set_value(par->gpio.db[i], data & 1); |
| data >>= 1; |
| } |
| #endif |
| |
| /* Pullup /WR */ |
| gpio_set_value(par->gpio.wr, 1); |
| |
| #ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO |
| prev_data = *(u16 *)buf; |
| #endif |
| buf += 2; |
| len -= 2; |
| } |
| |
| return 0; |
| } |
| |
| static void adafruit18_green_tab_set_addr_win(struct fbtft_par *par, |
| int xs, int ys, int xe, int ye) |
| { |
| write_reg(par, 0x2A, 0, xs + 2, 0, xe + 2); |
| write_reg(par, 0x2B, 0, ys + 1, 0, ye + 1); |
| write_reg(par, 0x2C); |
| } |
| |
| /* used if gpios parameter is present */ |
| static struct fbtft_gpio fbtft_device_param_gpios[MAX_GPIOS + 1] = { }; |
| |
| static void fbtft_device_pdev_release(struct device *dev) |
| { |
| /* Needed to silence this message: |
| * Device 'xxx' does not have a release() function, |
| * it is broken and must be fixed |
| */ |
| } |
| |
| static int spi_device_found(struct device *dev, void *data) |
| { |
| struct spi_device *spi = to_spi_device(dev); |
| |
| dev_info(dev, "%s %s %dkHz %d bits mode=0x%02X\n", spi->modalias, |
| dev_name(dev), spi->max_speed_hz / 1000, spi->bits_per_word, |
| spi->mode); |
| |
| return 0; |
| } |
| |
| static void pr_spi_devices(void) |
| { |
| pr_debug("SPI devices registered:\n"); |
| bus_for_each_dev(&spi_bus_type, NULL, NULL, spi_device_found); |
| } |
| |
| static int p_device_found(struct device *dev, void *data) |
| { |
| struct platform_device |
| *pdev = to_platform_device(dev); |
| |
| if (strstr(pdev->name, "fb")) |
| dev_info(dev, "%s id=%d pdata? %s\n", pdev->name, pdev->id, |
| pdev->dev.platform_data ? "yes" : "no"); |
| |
| return 0; |
| } |
| |
| static void pr_p_devices(void) |
| { |
| pr_debug("'fb' Platform devices registered:\n"); |
| bus_for_each_dev(&platform_bus_type, NULL, NULL, p_device_found); |
| } |
| |
| #ifdef MODULE |
| static void fbtft_device_spi_delete(struct spi_master *master, unsigned int cs) |
| { |
| struct device *dev; |
| char str[32]; |
| |
| snprintf(str, sizeof(str), "%s.%u", dev_name(&master->dev), cs); |
| |
| dev = bus_find_device_by_name(&spi_bus_type, NULL, str); |
| if (dev) { |
| if (verbose) |
| dev_info(dev, "Deleting %s\n", str); |
| device_del(dev); |
| } |
| } |
| |
| static int fbtft_device_spi_device_register(struct spi_board_info *spi) |
| { |
| struct spi_master *master; |
| |
| master = spi_busnum_to_master(spi->bus_num); |
| if (!master) { |
| pr_err("spi_busnum_to_master(%d) returned NULL\n", |
| spi->bus_num); |
| return -EINVAL; |
| } |
| /* make sure it's available */ |
| fbtft_device_spi_delete(master, spi->chip_select); |
| spi_device = spi_new_device(master, spi); |
| put_device(&master->dev); |
| if (!spi_device) { |
| dev_err(&master->dev, "spi_new_device() returned NULL\n"); |
| return -EPERM; |
| } |
| return 0; |
| } |
| #else |
| static int fbtft_device_spi_device_register(struct spi_board_info *spi) |
| { |
| return spi_register_board_info(spi, 1); |
| } |
| #endif |
| |
| static int __init fbtft_device_init(void) |
| { |
| struct spi_board_info *spi = NULL; |
| struct fbtft_platform_data *pdata; |
| const struct fbtft_gpio *gpio = NULL; |
| char *p_gpio, *p_name, *p_num; |
| bool found = false; |
| int i = 0; |
| long val; |
| int ret = 0; |
| |
| if (!name) { |
| #ifdef MODULE |
| pr_err("missing module parameter: 'name'\n"); |
| return -EINVAL; |
| #else |
| return 0; |
| #endif |
| } |
| |
| if (init_num > FBTFT_MAX_INIT_SEQUENCE) { |
| pr_err("init parameter: exceeded max array size: %d\n", |
| FBTFT_MAX_INIT_SEQUENCE); |
| return -EINVAL; |
| } |
| |
| /* parse module parameter: gpios */ |
| while ((p_gpio = strsep(&gpios, ","))) { |
| if (!strchr(p_gpio, ':')) { |
| pr_err("error: missing ':' in gpios parameter: %s\n", |
| p_gpio); |
| return -EINVAL; |
| } |
| p_num = p_gpio; |
| p_name = strsep(&p_num, ":"); |
| if (!p_name || !p_num) { |
| pr_err("something bad happened parsing gpios parameter: %s\n", |
| p_gpio); |
| return -EINVAL; |
| } |
| ret = kstrtol(p_num, 10, &val); |
| if (ret) { |
| pr_err("could not parse number in gpios parameter: %s:%s\n", |
| p_name, p_num); |
| return -EINVAL; |
| } |
| strncpy(fbtft_device_param_gpios[i].name, p_name, |
| FBTFT_GPIO_NAME_SIZE - 1); |
| fbtft_device_param_gpios[i++].gpio = (int)val; |
| if (i == MAX_GPIOS) { |
| pr_err("gpios parameter: exceeded max array size: %d\n", |
| MAX_GPIOS); |
| return -EINVAL; |
| } |
| } |
| if (fbtft_device_param_gpios[0].name[0]) |
| gpio = fbtft_device_param_gpios; |
| |
| if (verbose > 2) { |
| pr_spi_devices(); /* print list of registered SPI devices */ |
| pr_p_devices(); /* print list of 'fb' platform devices */ |
| } |
| |
| pr_debug("name='%s', busnum=%d, cs=%d\n", name, busnum, cs); |
| |
| if (rotate > 0 && rotate < 4) { |
| rotate = (4 - rotate) * 90; |
| pr_warn("argument 'rotate' should be an angle. Values 1-3 is deprecated. Setting it to %d.\n", |
| rotate); |
| } |
| if (rotate != 0 && rotate != 90 && rotate != 180 && rotate != 270) { |
| pr_warn("argument 'rotate' illegal value: %d. Setting it to 0.\n", |
| rotate); |
| rotate = 0; |
| } |
| |
| /* name=list lists all supported displays */ |
| if (strncmp(name, "list", FBTFT_GPIO_NAME_SIZE) == 0) { |
| pr_info("Supported displays:\n"); |
| |
| for (i = 0; i < ARRAY_SIZE(displays); i++) |
| pr_info("%s\n", displays[i].name); |
| return -ECANCELED; |
| } |
| |
| if (custom) { |
| i = ARRAY_SIZE(displays) - 1; |
| displays[i].name = name; |
| if (speed == 0) { |
| displays[i].pdev->name = name; |
| displays[i].spi = NULL; |
| } else { |
| size_t len; |
| |
| len = strlcpy(displays[i].spi->modalias, name, |
| SPI_NAME_SIZE); |
| if (len >= SPI_NAME_SIZE) |
| pr_warn("modalias (name) truncated to: %s\n", |
| displays[i].spi->modalias); |
| displays[i].pdev = NULL; |
| } |
| } |
| |
| for (i = 0; i < ARRAY_SIZE(displays); i++) { |
| if (strncmp(name, displays[i].name, SPI_NAME_SIZE) == 0) { |
| if (displays[i].spi) { |
| spi = displays[i].spi; |
| spi->chip_select = cs; |
| spi->bus_num = busnum; |
| if (speed) |
| spi->max_speed_hz = speed; |
| if (mode != -1) |
| spi->mode = mode; |
| pdata = (void *)spi->platform_data; |
| } else if (displays[i].pdev) { |
| p_device = displays[i].pdev; |
| pdata = p_device->dev.platform_data; |
| } else { |
| pr_err("broken displays array\n"); |
| return -EINVAL; |
| } |
| |
| pdata->rotate = rotate; |
| if (bgr == 0) |
| pdata->bgr = false; |
| else if (bgr == 1) |
| pdata->bgr = true; |
| if (startbyte) |
| pdata->startbyte = startbyte; |
| if (gamma) |
| pdata->gamma = gamma; |
| pdata->display.debug = debug; |
| if (fps) |
| pdata->fps = fps; |
| if (txbuflen) |
| pdata->txbuflen = txbuflen; |
| if (init_num) |
| pdata->display.init_sequence = init; |
| if (gpio) |
| pdata->gpios = gpio; |
| if (custom) { |
| pdata->display.width = width; |
| pdata->display.height = height; |
| pdata->display.buswidth = buswidth; |
| pdata->display.backlight = 1; |
| } |
| |
| if (displays[i].spi) { |
| ret = fbtft_device_spi_device_register(spi); |
| if (ret) { |
| pr_err("failed to register SPI device\n"); |
| return ret; |
| } |
| } else { |
| ret = platform_device_register(p_device); |
| if (ret < 0) { |
| pr_err("platform_device_register() returned %d\n", |
| ret); |
| return ret; |
| } |
| } |
| found = true; |
| break; |
| } |
| } |
| |
| if (!found) { |
| pr_err("display not supported: '%s'\n", name); |
| return -EINVAL; |
| } |
| |
| if (verbose && pdata && pdata->gpios) { |
| gpio = pdata->gpios; |
| pr_info("GPIOS used by '%s':\n", name); |
| found = false; |
| while (verbose && gpio->name[0]) { |
| pr_info("'%s' = GPIO%d\n", gpio->name, gpio->gpio); |
| gpio++; |
| found = true; |
| } |
| if (!found) |
| pr_info("(none)\n"); |
| } |
| |
| if (spi_device && (verbose > 1)) |
| pr_spi_devices(); |
| if (p_device && (verbose > 1)) |
| pr_p_devices(); |
| |
| return 0; |
| } |
| |
| static void __exit fbtft_device_exit(void) |
| { |
| if (spi_device) { |
| device_del(&spi_device->dev); |
| kfree(spi_device); |
| } |
| |
| if (p_device) |
| platform_device_unregister(p_device); |
| |
| } |
| |
| arch_initcall(fbtft_device_init); |
| module_exit(fbtft_device_exit); |
| |
| MODULE_DESCRIPTION("Add a FBTFT device."); |
| MODULE_AUTHOR("Noralf Tronnes"); |
| MODULE_LICENSE("GPL"); |