X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=kernel%2Fdrivers%2Fstaging%2Ffbtft%2Ffbtft_device.c;fp=kernel%2Fdrivers%2Fstaging%2Ffbtft%2Ffbtft_device.c;h=df6cd775ac1eb5eabebf19c782df3488e9ad192c;hb=9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00;hp=0000000000000000000000000000000000000000;hpb=98260f3884f4a202f9ca5eabed40b1354c489b29;p=kvmfornfv.git diff --git a/kernel/drivers/staging/fbtft/fbtft_device.c b/kernel/drivers/staging/fbtft/fbtft_device.c new file mode 100644 index 000000000..df6cd775a --- /dev/null +++ b/kernel/drivers/staging/fbtft/fbtft_device.c @@ -0,0 +1,1488 @@ +/* + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include + +#include "fbtft.h" + +#define DRVNAME "fbtft_device" + +#define MAX_GPIOS 32 + +static struct spi_device *spi_device; +static struct platform_device *p_device; + +static char *name; +module_param(name, charp, 0); +MODULE_PARM_DESC(name, "Devicename (required). " \ +"name=list => list all supported devices."); + +static unsigned rotate; +module_param(rotate, uint, 0); +MODULE_PARM_DESC(rotate, +"Angle to rotate display counter clockwise: 0, 90, 180, 270"); + +static unsigned busnum; +module_param(busnum, uint, 0); +MODULE_PARM_DESC(busnum, "SPI bus number (default=0)"); + +static unsigned cs; +module_param(cs, uint, 0); +MODULE_PARM_DESC(cs, "SPI chip select (default=0)"); + +static unsigned speed; +module_param(speed, uint, 0); +MODULE_PARM_DESC(speed, "SPI speed (override device default)"); + +static int mode = -1; +module_param(mode, int, 0); +MODULE_PARM_DESC(mode, "SPI mode (override device default)"); + +static char *gpios; +module_param(gpios, charp, 0); +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 fps; +module_param(fps, uint, 0); +MODULE_PARM_DESC(fps, "Frames per second (override driver default)"); + +static char *gamma; +module_param(gamma, charp, 0); +MODULE_PARM_DESC(gamma, +"String representation of Gamma Curve(s). Driver specific."); + +static int txbuflen; +module_param(txbuflen, int, 0); +MODULE_PARM_DESC(txbuflen, "txbuflen (override driver default)"); + +static int bgr = -1; +module_param(bgr, int, 0); +MODULE_PARM_DESC(bgr, +"BGR bit (supported by some drivers)."); + +static unsigned startbyte; +module_param(startbyte, uint, 0); +MODULE_PARM_DESC(startbyte, "Sets the Start byte used by some SPI displays."); + +static bool custom; +module_param(custom, bool, 0); +MODULE_PARM_DESC(custom, "Add a custom display device. " \ +"Use speed= argument to make it a SPI device, else platform_device"); + +static unsigned width; +module_param(width, uint, 0); +MODULE_PARM_DESC(width, "Display width, used with the custom argument"); + +static unsigned height; +module_param(height, uint, 0); +MODULE_PARM_DESC(height, "Display height, used with the custom argument"); + +static unsigned buswidth = 8; +module_param(buswidth, uint, 0); +MODULE_PARM_DESC(buswidth, "Display bus width, used with the custom argument"); + +static int init[FBTFT_MAX_INIT_SEQUENCE]; +static int init_num; +module_param_array(init, int, &init_num, 0); +MODULE_PARM_DESC(init, "Init sequence, used with the custom argument"); + +static unsigned long debug; +module_param(debug, ulong, 0); +MODULE_PARM_DESC(debug, +"level: 0-7 (the remaining 29 bits is for advanced usage)"); + +static unsigned verbose = 3; +module_param(verbose, uint, 0); +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" + +static int 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 int pitft_init_sequence[] = { + -1, 0x01, -2, 5, -1, 0x28, -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, 0x3A, 0x55, + -1, 0xB1, 0x00, 0x18, -1, 0xB6, 0x08, 0x82, + 0x27, -1, 0xF2, 0x00, -1, 0x26, 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, + 0x11, -2, 100, -1, 0x29, -2, 20, -3 }; + +static int 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, 0x36, 0x28, -1, 0x3A, 0x55, -1, 0xB1, 0x00, + 0x18, -1, 0xB6, 0x08, 0x82, 0x27, + -1, 0xF2, 0x00, -1, 0x26, 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, 0x11, -2, 120, -1, 0x29, -1, 0x2c, -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 = "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 = "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) +{ + fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, + "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, 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 = container_of(dev, struct spi_device, dev); + + pr_info(DRVNAME": %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_info(DRVNAME": 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 = container_of(dev, struct platform_device, dev); + + if (strstr(pdev->name, "fb")) + pr_info(DRVNAME": %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_info(DRVNAME": '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 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) + pr_info(DRVNAME": 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(DRVNAME ": 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) { + pr_err(DRVNAME ": 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; + + pr_debug("\n\n"DRVNAME": init\n"); + + if (name == NULL) { +#ifdef MODULE + pr_err(DRVNAME": missing module parameter: 'name'\n"); + return -EINVAL; +#else + return 0; +#endif + } + + if (init_num > FBTFT_MAX_INIT_SEQUENCE) { + pr_err(DRVNAME \ + ": 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, ':') == NULL) { + pr_err(DRVNAME \ + ": error: missing ':' in gpios parameter: %s\n", + p_gpio); + return -EINVAL; + } + p_num = p_gpio; + p_name = strsep(&p_num, ":"); + if (p_name == NULL || p_num == NULL) { + pr_err(DRVNAME \ + ": something bad happened parsing gpios parameter: %s\n", + p_gpio); + return -EINVAL; + } + ret = kstrtol(p_num, 10, &val); + if (ret) { + pr_err(DRVNAME \ + ": could not parse number in gpios parameter: %s:%s\n", + p_name, p_num); + return -EINVAL; + } + strcpy(fbtft_device_param_gpios[i].name, p_name); + fbtft_device_param_gpios[i++].gpio = (int) val; + if (i == MAX_GPIOS) { + pr_err(DRVNAME \ + ": 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 */ + + if (verbose > 2) + pr_p_devices(); /* print list of 'fb' platform devices */ + + pr_debug(DRVNAME": 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", 32) == 0) { + pr_info(DRVNAME": Supported displays:\n"); + + for (i = 0; i < ARRAY_SIZE(displays); i++) + pr_info(DRVNAME": %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 { + strncpy(displays[i].spi->modalias, name, SPI_NAME_SIZE); + displays[i].pdev = NULL; + } + } + + for (i = 0; i < ARRAY_SIZE(displays); i++) { + if (strncmp(name, displays[i].name, 32) == 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(DRVNAME": 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(DRVNAME \ + ": failed to register SPI device\n"); + return ret; + } + found = true; + break; + } else { + ret = platform_device_register(p_device); + if (ret < 0) { + pr_err(DRVNAME \ + ": platform_device_register() returned %d\n", + ret); + return ret; + } + found = true; + break; + } + } + } + + if (!found) { + pr_err(DRVNAME": display not supported: '%s'\n", name); + return -EINVAL; + } + + if (verbose && pdata && pdata->gpios) { + gpio = pdata->gpios; + pr_info(DRVNAME": GPIOS used by '%s':\n", name); + found = false; + while (verbose && gpio->name[0]) { + pr_info(DRVNAME": '%s' = GPIO%d\n", + gpio->name, gpio->gpio); + gpio++; + found = true; + } + if (!found) + pr_info(DRVNAME": (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) +{ + pr_debug(DRVNAME" - exit\n"); + + 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");