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 <nor...@tronnes.org>
---
 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
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel

Reply via email to