Add support for Solomon SSD1306 Monochrome OLED controller. The controller supports up to 128x64 pixles and supports interfaces: Parallel 6800, parallel 8080, SPI (3/4), I2C. Signed-off-by: Noralf Trønnes <noralf@xxxxxxxxxxx> --- drivers/staging/fbtft/lcdctrl/Kconfig | 5 + drivers/staging/fbtft/lcdctrl/Makefile | 2 + drivers/staging/fbtft/lcdctrl/ssd1306.c | 168 ++++++++++++++++++++++++++++++++ drivers/staging/fbtft/lcdctrl/ssd1306.h | 51 ++++++++++ 4 files changed, 226 insertions(+) create mode 100644 drivers/staging/fbtft/lcdctrl/ssd1306.c create mode 100644 drivers/staging/fbtft/lcdctrl/ssd1306.h diff --git a/drivers/staging/fbtft/lcdctrl/Kconfig b/drivers/staging/fbtft/lcdctrl/Kconfig index 5272847..61bfef1 100644 --- a/drivers/staging/fbtft/lcdctrl/Kconfig +++ b/drivers/staging/fbtft/lcdctrl/Kconfig @@ -1,3 +1,8 @@ config LCDCTRL tristate default n + +config LCDCTRL_SSD1306 + tristate + select LCDCTRL + default n diff --git a/drivers/staging/fbtft/lcdctrl/Makefile b/drivers/staging/fbtft/lcdctrl/Makefile index e6e4e8c..42a61f9 100644 --- a/drivers/staging/fbtft/lcdctrl/Makefile +++ b/drivers/staging/fbtft/lcdctrl/Makefile @@ -1 +1,3 @@ obj-$(CONFIG_LCDCTRL) += lcdctrl.o + +obj-$(CONFIG_LCDCTRL_SSD1306) += ssd1306.o diff --git a/drivers/staging/fbtft/lcdctrl/ssd1306.c b/drivers/staging/fbtft/lcdctrl/ssd1306.c new file mode 100644 index 0000000..251bcc2 --- /dev/null +++ b/drivers/staging/fbtft/lcdctrl/ssd1306.c @@ -0,0 +1,168 @@ +/* + * SSD1306 LCD controller support + * + * Copyright 2015 Noralf Trønnes + * + * 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. + */ + +#include <linux/module.h> + +#include "ssd1306.h" + +/* + * TODO: contrast control is not implemented + * If gamma control is to be user configurable, maybe the same + * mechanism can be used for contrast. + */ + +struct ssd1306_controller { + struct lcdctrl lcdctrl; + void *buf; +}; + +static inline struct ssd1306_controller *to_ssd1306(struct lcdctrl *ctrl) +{ + return container_of(ctrl, struct ssd1306_controller, lcdctrl); +} + +static int ssd1306_update(struct lcdctrl *ctrl, struct lcdctrl_update *update) +{ + struct lcdreg *reg = ctrl->lcdreg; + u8 *buf = to_ssd1306(ctrl)->buf; + u32 xres = lcdctrl_xres(ctrl); + u32 yres = lcdctrl_yres(ctrl); + int x, y, i; + struct lcdreg_transfer tr = { + .index = 1, + .width = 8, + .buf = buf, + .count = xres * yres / 8, + }; + + /* + * RGB565 is implemented on this monochrome controller for 2 reasons: + * 1. sys_imageblit and friends doesn't honour + * CONFIG_FB_CFB_REV_PIXELS_IN_BYTE which results in mirrored or + * garbeled fonts with fbcon on systems like the Raspberry Pi. + * 2. Very few applications and no grahics libraries supports + * monochrome framebuffers. + */ + if (ctrl->format == LCDCTRL_FORMAT_RGB565) { + u16 *vmem16 = (u16 *)update->base; + + /* TODO: add better conversion as done in fb_agm1264k-fl */ + for (x = 0; x < xres; x++) { + for (y = 0; y < yres / 8; y++) { + *buf = 0x00; + for (i = 0; i < 8; i++) { + int y1 = y * 8 + i; + + if (vmem16[y1 * xres + x]) + *buf |= 1 << i; + } + buf++; + } + } + } else { /* LCDCTRL_FORMAT_MONO10 */ + u8 *vmem8 = (u8 *)update->base; + + for (x = 0; x < xres; x++) { + for (y = 0; y < yres / 8; y++) { + *buf = 0x00; + for (i = 0; i < 8; i++) { + int y1 = y * 8 + i; + int idx = y1 * xres / 8 + x / 8; + int mask = (1 << (7 - (x % 8))); + + if (vmem8[idx] & mask) + *buf |= 1 << i; + } + buf++; + } + } + } + return lcdreg_write(reg, SSD1306_DISPLAY_START_LINE, &tr); +} + +static int ssd1306_blank(struct lcdctrl *ctrl, bool blank) +{ + return lcdreg_writereg(ctrl->lcdreg, blank ? SSD1306_DISPLAY_OFF : + SSD1306_DISPLAY_ON); +} + +static int ssd1306_set_format(struct lcdctrl *ctrl) +{ + switch (ctrl->format) { + case LCDCTRL_FORMAT_MONO10: + break; + case LCDCTRL_FORMAT_RGB565: + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct lcdctrl ssd1306_lcdctrl = { + .update = ssd1306_update, + .blank = ssd1306_blank, + .set_format = ssd1306_set_format, +}; + +struct lcdctrl *devm_ssd1306_init(struct lcdreg *lcdreg, + struct ssd1306_config *config) +{ + struct ssd1306_controller *ssd1306; + struct lcdctrl *ctrl; + + ssd1306 = devm_kzalloc(lcdreg->dev, sizeof(*ssd1306), + GFP_KERNEL); + if (!ssd1306) + return ERR_PTR(-ENOMEM); + + ctrl = &ssd1306->lcdctrl; + *ctrl = ssd1306_lcdctrl; + ctrl->lcdreg = lcdreg; + ctrl->lcdreg->def_width = 8; + ctrl->width = config->width ? : 128; + ctrl->height = config->height ? : 64; + ctrl->format = LCDCTRL_FORMAT_RGB565; + ssd1306->buf = devm_kzalloc(lcdreg->dev, + ctrl->height * ctrl->width / 8, + GFP_KERNEL); + if (!ssd1306->buf) + return ERR_PTR(-ENOMEM); + + return ctrl; +} +EXPORT_SYMBOL(devm_ssd1306_init); + +bool ssd1306_check_status(struct lcdreg *reg, bool display_is_on) +{ + u32 val; + int ret; + + ret = lcdreg_readreg_buf32(reg, 0x00, &val, 1); + if (ret) { + dev_err(reg->dev, "failed to read status: %i\n", ret); + return false; + } + if (((val & BIT(6)) >> 6) == display_is_on) { + dev_warn(reg->dev, + "status check failed: 0x%02x", val); + return false; + } + dev_dbg(reg->dev, "%s: OK\n", __func__); + + return true; +} +EXPORT_SYMBOL(ssd1306_check_status); + +MODULE_DESCRIPTION("SSD1306 LCD controller support"); +MODULE_AUTHOR("Noralf Trønnes"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/fbtft/lcdctrl/ssd1306.h b/drivers/staging/fbtft/lcdctrl/ssd1306.h new file mode 100644 index 0000000..a1fea6e --- /dev/null +++ b/drivers/staging/fbtft/lcdctrl/ssd1306.h @@ -0,0 +1,51 @@ +/* + * SSD1306 LCD controller support + * + * Copyright 2015 Noralf Trønnes + * + * 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. + */ + +#ifndef __LINUX_SSD1306_H +#define __LINUX_SSD1306_H + +#include "../lcdreg/lcdreg.h" +#include "lcdctrl.h" + +#define SSD1306_ADDRESS_MODE 0x20 +#define SSD1306_COL_RANGE 0x21 +#define SSD1306_PAGE_RANGE 0x22 +#define SSD1306_DISPLAY_START_LINE 0x40 +#define SSD1306_CONTRAST 0x81 +#define SSD1306_CHARGE_PUMP 0x8d +#define SSD1306_SEG_REMAP_OFF 0xa0 +#define SSD1306_SEG_REMAP_ON 0xa1 +#define SSD1306_RESUME_TO_RAM 0xa4 +#define SSD1306_ENTIRE_DISPLAY_ON 0xa5 +#define SSD1306_NORMAL_DISPLAY 0xa6 +#define SSD1306_INVERSE_DISPLAY 0xa7 +#define SSD1306_MULTIPLEX_RATIO 0xa8 +#define SSD1306_DISPLAY_OFF 0xae +#define SSD1306_DISPLAY_ON 0xaf +#define SSD1306_START_PAGE_ADDRESS 0xb0 +#define SSD1306_COM_SCAN_NORMAL 0xc0 +#define SSD1306_COM_SCAN_REMAP 0xc8 +#define SSD1306_DISPLAY_OFFSET 0xd3 +#define SSD1306_CLOCK_FREQ 0xd5 +#define SSD1306_PRECHARGE_PERIOD 0xd9 +#define SSD1306_COM_PINS_CONFIG 0xda +#define SSD1306_VCOMH 0xdb + +struct ssd1306_config { + u32 width; + u32 height; +}; + +struct lcdctrl *devm_ssd1306_init(struct lcdreg *lcdreg, + struct ssd1306_config *config); +extern bool ssd1306_check_status(struct lcdreg *reg, bool display_is_on); + +#endif /* __LINUX_SSD1306_H */ -- 2.2.2 _______________________________________________ devel mailing list devel@xxxxxxxxxxxxxxxxxxxxxx http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel