Signed-off-by: Alexey Mednyy <swexru@xxxxxxxxx> --- drivers/staging/fbtft/Kconfig | 6 + drivers/staging/fbtft/Makefile | 1 + drivers/staging/fbtft/fb_ssd1325.c | 266 +++++++++++++++++++++++++++++++++++++ 3 files changed, 273 insertions(+) create mode 100644 drivers/staging/fbtft/fb_ssd1325.c diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig index 0bfc776..6f5e824 100644 --- a/drivers/staging/fbtft/Kconfig +++ b/drivers/staging/fbtft/Kconfig @@ -129,6 +129,12 @@ config FB_TFT_SSD1306 help Framebuffer support for SSD1306 +config FB_TFT_SSD1325 + tristate "FB driver for the SSD1325 OLED Controller" + depends on FB_TFT + help + Framebuffer support for SSD1305 + config FB_TFT_SSD1331 tristate "FB driver for the SSD1331 LCD Controller" depends on FB_TFT diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile index 1ddc764..2725ea9 100644 --- a/drivers/staging/fbtft/Makefile +++ b/drivers/staging/fbtft/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_FB_TFT_S6D1121) += fb_s6d1121.o obj-$(CONFIG_FB_TFT_SSD1289) += fb_ssd1289.o obj-$(CONFIG_FB_TFT_SSD1305) += fb_ssd1305.o obj-$(CONFIG_FB_TFT_SSD1306) += fb_ssd1306.o +obj-$(CONFIG_FB_TFT_SSD1305) += fb_ssd1325.o obj-$(CONFIG_FB_TFT_SSD1331) += fb_ssd1331.o obj-$(CONFIG_FB_TFT_SSD1351) += fb_ssd1351.o obj-$(CONFIG_FB_TFT_ST7735R) += fb_st7735r.o diff --git a/drivers/staging/fbtft/fb_ssd1325.c b/drivers/staging/fbtft/fb_ssd1325.c new file mode 100644 index 0000000..cdb96be --- /dev/null +++ b/drivers/staging/fbtft/fb_ssd1325.c @@ -0,0 +1,266 @@ +/* + * FB driver for the SSD1325 OLED Controller + * + * 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. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/gpio.h> +#include <linux/delay.h> + +#include "fbtft.h" + +#define DRVNAME "fb_ssd1325" +#define WIDTH 128 +#define HEIGHT 64 + +#define GAMMA_NUM 1 +#define GAMMA_LEN 15 +#define DEFAULT_GAMMA "7 1 1 1 1 2 2 3 3 4 4 5 5 6 6" + +/* + * write_reg() caveat: + * + * This doesn't work because D/C has to be LOW for both values: + * write_reg(par, val1, val2); + * + * Do it like this: + * write_reg(par, val1); + * write_reg(par, val2); + */ + +/* Init sequence taken from the Adafruit SSD1306 Arduino library */ +static int init_display(struct fbtft_par *par) +{ + par->fbtftops.reset(par); + + gpio_set_value(par->gpio.cs, 0); + + write_reg(par, 0xb3); + write_reg(par, 0xf0); + write_reg(par, 0xae); + write_reg(par, 0xa1); + write_reg(par, 0x00); + write_reg(par, 0xa8); + write_reg(par, 0x3f); + write_reg(par, 0xa0); + write_reg(par, 0x45); + write_reg(par, 0xa2); + write_reg(par, 0x40); + write_reg(par, 0x75); + write_reg(par, 0x00); + write_reg(par, 0x3f); + write_reg(par, 0x15); + write_reg(par, 0x00); + write_reg(par, 0x7f); + write_reg(par, 0xa4); + write_reg(par, 0xaf); + + return 0; +} + +static uint8_t rgb565_to_g16(uint16_t pixel) +{ + uint16_t b = pixel & 0x1f; + uint16_t g = (pixel & (0x3f << 5)) >> 5; + uint16_t r = (pixel & (0x1f << (5 + 6))) >> (5 + 6); + + pixel = (299 * r + 587 * g + 114 * b) / 195; + if (pixel > 255) + pixel = 255; + return (uint8_t) pixel / 16; +} + +/* Check if all necessary GPIOS defined */ +static int verify_gpios(struct fbtft_par *par) +{ + int i; + + dev_dbg(par->info->device, "%s()\n", __func__); + + if (par->gpio.wr < 0) { + dev_err(par->info->device, + "Missing info about 'wr' (aka E) gpio. Aborting.\n"); + return -EINVAL; + } + for (i = 0; i < 8; ++i) { + if (par->gpio.db[i] < 0) { + dev_err(par->info->device, + "Missing info about 'db[%i]' gpio. Aborting.\n", + i); + return -EINVAL; + } + } + if (par->gpio.dc < 0) { + dev_err(par->info->device, + "Missing info about 'dc' gpio. Aborting.\n"); + return -EINVAL; + } + if (par->gpio.rd < 0) { + dev_err(par->info->device, + "Missing info about 'rd' gpio. Aborting.\n"); + return -EINVAL; + } + + return 0; +} + +static unsigned long +request_gpios_match(struct fbtft_par *par, const struct fbtft_gpio *gpio) +{ + dev_dbg(par->info->device, "%s('%s')\n", __func__, gpio->name); + + if (strcasecmp(gpio->name, "wr") == 0) { + par->gpio.wr = gpio->gpio; + return GPIOF_OUT_INIT_LOW; + } else if (strcasecmp(gpio->name, "dc") == 0) { + par->gpio.dc = gpio->gpio; + return GPIOF_OUT_INIT_HIGH; + } else if (strcasecmp(gpio->name, "rd") == 0) { + par->gpio.rd = gpio->gpio; + return GPIOF_OUT_INIT_HIGH; + } else if (strcasecmp(gpio->name, "cs") == 0) { + par->gpio.cs = gpio->gpio; + return GPIOF_OUT_INIT_HIGH; + } else if (strcasecmp(gpio->name, "reset") == 0) { + par->gpio.reset = gpio->gpio; + return GPIOF_OUT_INIT_LOW; + } + + return FBTFT_GPIO_NO_MATCH; +} + +static void 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, 0x75); + write_reg(par, 0x00); + write_reg(par, 0x3f); + write_reg(par, 0x15); + write_reg(par, 0x00); + write_reg(par, 0x7f); + +} + +static int blank(struct fbtft_par *par, bool on) +{ + fbtft_par_dbg(DEBUG_BLANK, par, "%s(blank=%s)\n", + __func__, on ? "true" : "false"); + + if (on) + write_reg(par, 0xAE); + else + write_reg(par, 0xAF); + return 0; +} + +/* + * Grayscale Lookup Table + * GS1 - GS15 + * The "Gamma curve" contains the relative values between the entries + * in the Lookup table. + * + * 0 = Setting of GS1 < Setting of GS2 < Setting of GS3.....< + * Setting of GS14 < Setting of GS15 +*/ +static int set_gamma(struct fbtft_par *par, unsigned long *curves) +{ + int i; + + fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); + + for (i = 0; i < GAMMA_LEN; i++) { + if (i > 0 && curves[i] < 1) { + dev_err(par->info->device, + "Illegal value in Grayscale Lookup Table at index %d. " + "Must be greater than 0\n", i); + return -EINVAL; + } + if (curves[i] > 7) { + dev_err(par->info->device, + "Illegal value(s) in Grayscale Lookup Table. " + "At index=%d, the accumulated value has exceeded 7\n", + i); + return -EINVAL; + } + } + write_reg(par, 0xB8); + for (i = 0; i < 8; i++) + write_reg(par, (curves[i] & 0xFF)); + return 0; +} + +static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) +{ + u16 *vmem16 = (u16 *) par->info->screen_buffer; + u8 *buf = par->txbuf.buf; + u8 n1; + u8 n2; + int y, x; + int ret = 0; + + for (x = 0; x < par->info->var.xres; x++) { + if (x % 2) + continue; + for (y = 0; y < par->info->var.yres; y++) { + n1 = rgb565_to_g16(vmem16[y * par->info->var.xres + x]); + n2 = rgb565_to_g16(vmem16 + [y * par->info->var.xres + x + 1]); + *buf = (n1 << 4) | n2; + buf++; + } + } + + gpio_set_value(par->gpio.dc, 1); + /* Write data */ + + ret = + par->fbtftops.write(par, par->txbuf.buf, + par->info->var.xres * par->info->var.yres / 2); + if (ret < 0) + dev_err(par->info->device, + "%s: write failed and returned: %d\n", __func__, ret); + + return ret; +} + +static struct fbtft_display display = { + .regwidth = 8, + .width = WIDTH, + .height = HEIGHT, + .txbuflen = WIDTH * HEIGHT / 2, + .gamma_num = GAMMA_NUM, + .gamma_len = GAMMA_LEN, + .gamma = DEFAULT_GAMMA, + .fbtftops = { + .write_vmem = write_vmem, + .init_display = init_display, + .verify_gpios = verify_gpios, + .request_gpios_match = request_gpios_match, + .set_addr_win = set_addr_win, + .blank = blank, + .set_gamma = set_gamma, + }, +}; + +FBTFT_REGISTER_DRIVER(DRVNAME, "solomon,ssd1325", &display); + +MODULE_ALIAS("platform:" DRVNAME); +MODULE_ALIAS("platform:ssd1325"); + +MODULE_DESCRIPTION("SSD1325 OLED Driver"); +MODULE_AUTHOR("Alexey Mednyy"); +MODULE_LICENSE("GPL"); -- 2.5.0 _______________________________________________ devel mailing list devel@xxxxxxxxxxxxxxxxxxxxxx http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel