/* * * 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 struct spi_device *spi_device; 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 = 0b01110000, .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 = 0b01110000, .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 = 0b01110000, .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 = "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");