[RFC 4/7] staging: fbtft: lcdctrl: add ssd1306 support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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





[Index of Archives]     [Linux Driver Backports]     [DMA Engine]     [Linux GPIO]     [Linux SPI]     [Video for Linux]     [Linux USB Devel]     [Linux Coverity]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux