2015-02-06 22:35:58

by Thomas Niederprüm

[permalink] [raw]
Subject: [PATCH 0/8] Cleanup and add support for SSD1305

From: Thomas Niederprüm <[email protected]>

This patch series is the result of making the ssd1307fb driver work with
a Newhaven OLED display using the Solomon SSD1305 controller. To achieve
this the intialization code for the SSD1306 and the SSD1307 is merged
and based on device tree configuration. This gets rid of the magic bit
values that were used so far.
Based on these changes it was straight forward to add support for the
SSD1305 controller.

While working with the driver I realized that it was not possible to
correctly mmap the video memory from userspace since the memory
reserved by kzalloc is not page aligned. This problem is fixed by
using vmalloc as it is done inthe vfb driver.

Furthermore module parameters are added to set the bits per pixel
and the delay for the deferred io update. It makes sense to set
the bits per pixel for the video memory to 8 bits since there is
only very poor userspace support for 1 bit framebuffers.

Also sysfs handles are added to make the contrast settings and dim
mode setting available in userspace.

Thomas Niederprüm (8):
Documentation: dts: add missing Solomon Systech vendor prefix.
fbdev: ssd1307fb: Unify init code and make controller configurable
from device tree
fbdev: ssd1307fb: Add support for SSD1305
fbdev: ssd1307fb: Use vmalloc to allocate video memory.
fbdev: ssd1307fb: Add module parameter bitsperpixel.
fbdev: ssd1307fb: Add module parameter to set update delay of the
deffered io.
fbdev: ssd1307fb: Add sysfs handles to expose contrast and dim setting
to userspace.
fbdev: ssd1307fb: Turn off display on driver unload.

.../devicetree/bindings/vendor-prefixes.txt | 1 +
.../devicetree/bindings/video/ssd1307fb.txt | 13 +-
drivers/video/fbdev/ssd1307fb.c | 426 ++++++++++++++++-----
3 files changed, 346 insertions(+), 94 deletions(-)

--
2.1.1


2015-02-06 22:35:53

by Thomas Niederprüm

[permalink] [raw]
Subject: [PATCH 1/8] Documentation: dts: add missing Solomon Systech vendor prefix.

From: Thomas Niederprüm <[email protected]>

This patch adds the solomon prefix for Solomon Systech Limited.

Signed-off-by: Thomas Niederprüm <[email protected]>
---
Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index a344ec2..b1d9470 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -143,6 +143,7 @@ sitronix Sitronix Technology Corporation
smsc Standard Microsystems Corporation
snps Synopsys, Inc.
solidrun SolidRun
+solomon Solomon Systech Limited
sony Sony Corporation
spansion Spansion Inc.
st STMicroelectronics
--
2.1.1

2015-02-06 22:36:25

by Thomas Niederprüm

[permalink] [raw]
Subject: [PATCH 2/8] fbdev: ssd1307fb: Unify init code and make controller configurable from device tree

From: Thomas Niederprüm <[email protected]>

This patches unifies the init code for the ssd130X chips and
adds device tree bindings to describe the hardware configuration
of the used controller. This gets rid of the magic bit values
used in the init code so far.

Signed-off-by: Thomas Niederprüm <[email protected]>
---
.../devicetree/bindings/video/ssd1307fb.txt | 11 +
drivers/video/fbdev/ssd1307fb.c | 243 ++++++++++++++-------
2 files changed, 174 insertions(+), 80 deletions(-)

diff --git a/Documentation/devicetree/bindings/video/ssd1307fb.txt b/Documentation/devicetree/bindings/video/ssd1307fb.txt
index 7a12542..1230f68 100644
--- a/Documentation/devicetree/bindings/video/ssd1307fb.txt
+++ b/Documentation/devicetree/bindings/video/ssd1307fb.txt
@@ -15,6 +15,17 @@ Required properties:

Optional properties:
- reset-active-low: Is the reset gpio is active on physical low?
+ - solomon,segment-remap: Invert the order of data column to segment mapping
+ - solomon,offset: Map the display start line to one of COM0 - COM63
+ - solomon,contrast: Set initial contrast of the display
+ - solomon,prechargep1: Set the duration of the precharge period phase1
+ - solomon,prechargep2: Set the duration of the precharge period phase2
+ - solomon,com-alt: Enable/disable alternate COM pin configuration
+ - solomon,com-lrremap: Enable/disable left-right remap of COM pins
+ - solomon,com-invdir: Invert COM scan direction
+ - solomon,vcomh: Set VCOMH regulator voltage
+ - solomon,dclk-div: Set display clock divider
+ - solomon,dclk-frq: Set display clock frequency

[0]: Documentation/devicetree/bindings/pwm/pwm.txt

diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c
index 3d6611f..4f435aa 100644
--- a/drivers/video/fbdev/ssd1307fb.c
+++ b/drivers/video/fbdev/ssd1307fb.c
@@ -16,6 +16,9 @@
#include <linux/pwm.h>
#include <linux/delay.h>

+#define DEVID_SSD1306 6
+#define DEVID_SSD1307 7
+
#define SSD1307FB_DATA 0x40
#define SSD1307FB_COMMAND 0x80

@@ -40,20 +43,33 @@

struct ssd1307fb_par;

-struct ssd1307fb_ops {
- int (*init)(struct ssd1307fb_par *);
- int (*remove)(struct ssd1307fb_par *);
+struct ssd1307fb_deviceinfo {
+ int device_id;
+ u32 default_vcomh;
+ u32 default_dclk_div;
+ u32 default_dclk_frq;
};

struct ssd1307fb_par {
+ u32 com_alt;
+ u32 com_invdir;
+ u32 com_lrremap;
+ u32 contrast;
+ u32 dclk_div;
+ u32 dclk_frq;
+ struct ssd1307fb_deviceinfo *device_info;
struct i2c_client *client;
u32 height;
struct fb_info *info;
- struct ssd1307fb_ops *ops;
+ u32 offset;
u32 page_offset;
+ u32 prechargep1;
+ u32 prechargep2;
struct pwm_device *pwm;
u32 pwm_period;
int reset;
+ u32 seg_remap;
+ u32 vcomh;
u32 width;
};

@@ -254,127 +270,151 @@ static struct fb_deferred_io ssd1307fb_defio = {
.deferred_io = ssd1307fb_deferred_io,
};

-static int ssd1307fb_ssd1307_init(struct ssd1307fb_par *par)
+static int ssd1307fb_init(struct ssd1307fb_par *par)
{
int ret;
+ u32 precharge, dclk, com_invdir, compins;

- par->pwm = pwm_get(&par->client->dev, NULL);
- if (IS_ERR(par->pwm)) {
- dev_err(&par->client->dev, "Could not get PWM from device tree!\n");
- return PTR_ERR(par->pwm);
- }
-
- par->pwm_period = pwm_get_period(par->pwm);
- /* Enable the PWM */
- pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period);
- pwm_enable(par->pwm);
-
- dev_dbg(&par->client->dev, "Using PWM%d with a %dns period.\n",
- par->pwm->pwm, par->pwm_period);
-
- /* Map column 127 of the OLED to segment 0 */
- ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON);
- if (ret < 0)
- return ret;
-
- /* Turn on the display */
- ret = ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-static int ssd1307fb_ssd1307_remove(struct ssd1307fb_par *par)
-{
- pwm_disable(par->pwm);
- pwm_put(par->pwm);
- return 0;
-}
+ if (par->device_info->device_id == DEVID_SSD1307) {
+ par->pwm = pwm_get(&par->client->dev, NULL);
+ if (IS_ERR(par->pwm)) {
+ dev_err(&par->client->dev, "Could not get PWM from device tree!\n");
+ return PTR_ERR(par->pwm);
+ }

-static struct ssd1307fb_ops ssd1307fb_ssd1307_ops = {
- .init = ssd1307fb_ssd1307_init,
- .remove = ssd1307fb_ssd1307_remove,
-};
+ par->pwm_period = pwm_get_period(par->pwm);
+ /* Enable the PWM */
+ pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period);
+ pwm_enable(par->pwm);

-static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
-{
- int ret;
+ dev_dbg(&par->client->dev, "Using PWM%d with a %dns period.\n",
+ par->pwm->pwm, par->pwm_period);
+ };

/* Set initial contrast */
ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CONTRAST);
- ret = ret & ssd1307fb_write_cmd(par->client, 0x7f);
if (ret < 0)
return ret;

- /* Set COM direction */
- ret = ssd1307fb_write_cmd(par->client, 0xc8);
+ ret = ssd1307fb_write_cmd(par->client, par->contrast);
if (ret < 0)
return ret;

/* Set segment re-map */
- ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON);
+ if (par->seg_remap) {
+ ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON);
+ if (ret < 0)
+ return ret;
+ };
+
+ /* Set COM direction */
+ com_invdir = 0xc0 | (par->com_invdir & 0xf) << 3;
+ ret = ssd1307fb_write_cmd(par->client, com_invdir);
if (ret < 0)
return ret;

/* Set multiplex ratio value */
ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_MULTIPLEX_RATIO);
- ret = ret & ssd1307fb_write_cmd(par->client, par->height - 1);
+ if (ret < 0)
+ return ret;
+
+ ret = ssd1307fb_write_cmd(par->client, par->height - 1);
if (ret < 0)
return ret;

/* set display offset value */
ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_DISPLAY_OFFSET);
- ret = ssd1307fb_write_cmd(par->client, 0x20);
+ if (ret < 0)
+ return ret;
+
+ ret = ssd1307fb_write_cmd(par->client, par->offset);
if (ret < 0)
return ret;

/* Set clock frequency */
+ dclk = (par->dclk_div & 0xf) | (par->dclk_frq & 0xf) << 4;
ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_CLOCK_FREQ);
- ret = ret & ssd1307fb_write_cmd(par->client, 0xf0);
if (ret < 0)
return ret;

- /* Set precharge period in number of ticks from the internal clock */
+ ret = ssd1307fb_write_cmd(par->client, dclk);
+ if (ret < 0)
+ return ret;
+
+ /* Set precharge period in number of ticks from the internal clock*/
+ precharge = (par->prechargep1 & 0xf) | (par->prechargep2 & 0xf) << 4;
ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PRECHARGE_PERIOD);
- ret = ret & ssd1307fb_write_cmd(par->client, 0x22);
+ if (ret < 0)
+ return ret;
+
+ ret = ssd1307fb_write_cmd(par->client, precharge);
if (ret < 0)
return ret;

/* Set COM pins configuration */
+ compins = 0x02 | (par->com_alt & 0x1) << 4
+ | (par->com_lrremap & 0x1) << 5;
ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COM_PINS_CONFIG);
- ret = ret & ssd1307fb_write_cmd(par->client, 0x22);
+ if (ret < 0)
+ return ret;
+
+ ret = ssd1307fb_write_cmd(par->client, compins);
if (ret < 0)
return ret;

/* Set VCOMH */
ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_VCOMH);
- ret = ret & ssd1307fb_write_cmd(par->client, 0x49);
if (ret < 0)
return ret;

- /* Turn on the DC-DC Charge Pump */
- ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CHARGE_PUMP);
- ret = ret & ssd1307fb_write_cmd(par->client, 0x14);
+ ret = ssd1307fb_write_cmd(par->client, par->vcomh);
if (ret < 0)
return ret;

+ if (par->device_info->device_id == DEVID_SSD1306) {
+ /* Turn on the DC-DC Charge Pump */
+ ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CHARGE_PUMP);
+ if (ret < 0)
+ return ret;
+
+ ret = ssd1307fb_write_cmd(par->client, 0x14);
+ if (ret < 0)
+ return ret;
+ };
+
/* Switch to horizontal addressing mode */
ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_ADDRESS_MODE);
- ret = ret & ssd1307fb_write_cmd(par->client,
+ if (ret < 0)
+ return ret;
+
+ ret = ssd1307fb_write_cmd(par->client,
SSD1307FB_SET_ADDRESS_MODE_HORIZONTAL);
if (ret < 0)
return ret;

+ /* Set column range */
ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COL_RANGE);
- ret = ret & ssd1307fb_write_cmd(par->client, 0x0);
- ret = ret & ssd1307fb_write_cmd(par->client, par->width - 1);
if (ret < 0)
return ret;

+ ret = ssd1307fb_write_cmd(par->client, 0x0);
+ if (ret < 0)
+ return ret;
+
+ ret = ssd1307fb_write_cmd(par->client, par->width - 1);
+ if (ret < 0)
+ return ret;
+
+ /* Set page range */
ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PAGE_RANGE);
- ret = ret & ssd1307fb_write_cmd(par->client, 0x0);
- ret = ret & ssd1307fb_write_cmd(par->client,
+ if (ret < 0)
+ return ret;
+
+ ret = ssd1307fb_write_cmd(par->client, 0x0);
+ if (ret < 0)
+ return ret;
+
+ ret = ssd1307fb_write_cmd(par->client,
par->page_offset + (par->height / 8) - 1);
if (ret < 0)
return ret;
@@ -387,18 +427,28 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
return 0;
}

-static struct ssd1307fb_ops ssd1307fb_ssd1306_ops = {
- .init = ssd1307fb_ssd1306_init,
+static struct ssd1307fb_deviceinfo ssd1307fb_ssd1306_deviceinfo = {
+ .device_id = DEVID_SSD1306,
+ .default_vcomh = 0x20,
+ .default_dclk_div = 0,
+ .default_dclk_frq = 8,
+};
+
+static struct ssd1307fb_deviceinfo ssd1307fb_ssd1307_deviceinfo = {
+ .device_id = DEVID_SSD1307,
+ .default_vcomh = 0x20,
+ .default_dclk_div = 1,
+ .default_dclk_frq = 12,
};

static const struct of_device_id ssd1307fb_of_match[] = {
{
.compatible = "solomon,ssd1306fb-i2c",
- .data = (void *)&ssd1307fb_ssd1306_ops,
+ .data = (void *)&ssd1307fb_ssd1306_deviceinfo,
},
{
.compatible = "solomon,ssd1307fb-i2c",
- .data = (void *)&ssd1307fb_ssd1307_ops,
+ .data = (void *)&ssd1307fb_ssd1307_deviceinfo,
},
{},
};
@@ -429,8 +479,8 @@ static int ssd1307fb_probe(struct i2c_client *client,
par->info = info;
par->client = client;

- par->ops = (struct ssd1307fb_ops *)of_match_device(ssd1307fb_of_match,
- &client->dev)->data;
+ par->device_info = (struct ssd1307fb_deviceinfo *)of_match_device(
+ ssd1307fb_of_match, &client->dev)->data;

par->reset = of_get_named_gpio(client->dev.of_node,
"reset-gpios", 0);
@@ -449,8 +499,40 @@ static int ssd1307fb_probe(struct i2c_client *client,
par->page_offset = 1;

vmem_size = par->width * par->height / 8;
+ if (of_property_read_u32(node, "solomon,segment-remap", &par->seg_remap))
+ par->seg_remap = 0;
+
+ if (of_property_read_u32(node, "solomon,offset", &par->offset))
+ par->offset = 0;
+
+ if (of_property_read_u32(node, "solomon,contrast", &par->contrast))
+ par->contrast = 128;
+
+ if (of_property_read_u32(node, "solomon,prechargep1", &par->prechargep1))
+ par->prechargep1 = 2;
+
+ if (of_property_read_u32(node, "solomon,prechargep2", &par->prechargep2))
+ par->prechargep2 = 2;
+
+ if (of_property_read_u32(node, "solomon,com-alt", &par->com_alt))
+ par->com_alt = 1;
+
+ if (of_property_read_u32(node, "solomon,com-lrremap", &par->com_lrremap))
+ par->com_lrremap = 0;
+
+ if (of_property_read_u32(node, "solomon,com-invdir", &par->com_invdir))
+ par->com_invdir = 0;
+
+ if (of_property_read_u32(node, "solomon,vcomh", &par->vcomh))
+ par->vcomh = par->device_info->default_vcomh;

vmem = devm_kzalloc(&client->dev, vmem_size, GFP_KERNEL);
+ if (of_property_read_u32(node, "solomon,dclk-div", &par->dclk_div))
+ par->dclk_div = par->device_info->default_dclk_div;
+
+ if (of_property_read_u32(node, "solomon,dclk-frq", &par->dclk_frq))
+ par->dclk_frq = par->device_info->default_dclk_frq;
+
if (!vmem) {
dev_err(&client->dev, "Couldn't allocate graphical memory.\n");
ret = -ENOMEM;
@@ -499,11 +581,9 @@ static int ssd1307fb_probe(struct i2c_client *client,
gpio_set_value(par->reset, 1);
udelay(4);

- if (par->ops->init) {
- ret = par->ops->init(par);
- if (ret)
- goto reset_oled_error;
- }
+ ret = ssd1307fb_init(par);
+ if (ret)
+ goto reset_oled_error;

ret = register_framebuffer(info);
if (ret) {
@@ -516,8 +596,10 @@ static int ssd1307fb_probe(struct i2c_client *client,
return 0;

panel_init_error:
- if (par->ops->remove)
- par->ops->remove(par);
+ if (par->device_info->device_id == DEVID_SSD1307) {
+ pwm_disable(par->pwm);
+ pwm_put(par->pwm);
+ };
reset_oled_error:
fb_deferred_io_cleanup(info);
fb_alloc_error:
@@ -531,11 +613,12 @@ static int ssd1307fb_remove(struct i2c_client *client)
struct ssd1307fb_par *par = info->par;

unregister_framebuffer(info);
- if (par->ops->remove)
- par->ops->remove(par);
+ if (par->device_info->device_id == DEVID_SSD1307) {
+ pwm_disable(par->pwm);
+ pwm_put(par->pwm);
+ };
fb_deferred_io_cleanup(info);
framebuffer_release(info);
-
return 0;
}

--
2.1.1

2015-02-06 22:37:10

by Thomas Niederprüm

[permalink] [raw]
Subject: [PATCH 3/8] fbdev: ssd1307fb: Add support for SSD1305

From: Thomas Niederprüm <[email protected]>

This patch adds support for the SSD1305 OLED controller.

Signed-off-by: Thomas Niederprüm <[email protected]>
---
Documentation/devicetree/bindings/video/ssd1307fb.txt | 2 +-
drivers/video/fbdev/ssd1307fb.c | 13 +++++++++++++
2 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/video/ssd1307fb.txt b/Documentation/devicetree/bindings/video/ssd1307fb.txt
index 1230f68..51fa263 100644
--- a/Documentation/devicetree/bindings/video/ssd1307fb.txt
+++ b/Documentation/devicetree/bindings/video/ssd1307fb.txt
@@ -2,7 +2,7 @@

Required properties:
- compatible: Should be "solomon,<chip>fb-<bus>". The only supported bus for
- now is i2c, and the supported chips are ssd1306 and ssd1307.
+ now is i2c, and the supported chips are ssd1305, ssd1306 and ssd1307.
- reg: Should contain address of the controller on the I2C bus. Most likely
0x3c or 0x3d
- pwm: Should contain the pwm to use according to the OF device tree PWM
diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c
index 4f435aa..01cfb46 100644
--- a/drivers/video/fbdev/ssd1307fb.c
+++ b/drivers/video/fbdev/ssd1307fb.c
@@ -16,6 +16,7 @@
#include <linux/pwm.h>
#include <linux/delay.h>

+#define DEVID_SSD1305 5
#define DEVID_SSD1306 6
#define DEVID_SSD1307 7

@@ -427,6 +428,13 @@ static int ssd1307fb_init(struct ssd1307fb_par *par)
return 0;
}

+static struct ssd1307fb_deviceinfo ssd1307fb_ssd1305_deviceinfo = {
+ .device_id = DEVID_SSD1305,
+ .default_vcomh = 0x34,
+ .default_dclk_div = 0,
+ .default_dclk_frq = 7,
+};
+
static struct ssd1307fb_deviceinfo ssd1307fb_ssd1306_deviceinfo = {
.device_id = DEVID_SSD1306,
.default_vcomh = 0x20,
@@ -443,6 +451,10 @@ static struct ssd1307fb_deviceinfo ssd1307fb_ssd1307_deviceinfo = {

static const struct of_device_id ssd1307fb_of_match[] = {
{
+ .compatible = "solomon,ssd1305fb-i2c",
+ .data = (void *)&ssd1307fb_ssd1305_deviceinfo,
+ },
+ {
.compatible = "solomon,ssd1306fb-i2c",
.data = (void *)&ssd1307fb_ssd1306_deviceinfo,
},
@@ -623,6 +635,7 @@ static int ssd1307fb_remove(struct i2c_client *client)
}

static const struct i2c_device_id ssd1307fb_i2c_id[] = {
+ { "ssd1305fb", 0 },
{ "ssd1306fb", 0 },
{ "ssd1307fb", 0 },
{ }
--
2.1.1

2015-02-06 22:36:04

by Thomas Niederprüm

[permalink] [raw]
Subject: [PATCH 4/8] fbdev: ssd1307fb: Use vmalloc to allocate video memory.

From: Thomas Niederprüm <[email protected]>

It makes sense to use vmalloc to allocate the video buffer since it has to be page aligned memory for using it with
mmap. Also deffered io seems buggy in combination with kmalloc'ed memory (crash on unloading the module).

Signed-off-by: Thomas Niederprüm <[email protected]>
---
drivers/video/fbdev/ssd1307fb.c | 43 +++++++++++++++++++++++++++++++++++++++--
1 file changed, 41 insertions(+), 2 deletions(-)

diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c
index 01cfb46..945ded9 100644
--- a/drivers/video/fbdev/ssd1307fb.c
+++ b/drivers/video/fbdev/ssd1307fb.c
@@ -11,6 +11,7 @@
#include <linux/i2c.h>
#include <linux/fb.h>
#include <linux/uaccess.h>
+#include <linux/vmalloc.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/pwm.h>
@@ -93,6 +94,43 @@ static struct fb_var_screeninfo ssd1307fb_var = {
.bits_per_pixel = 1,
};

+static void *rvmalloc(unsigned long size)
+{
+ void *mem;
+ unsigned long adr;
+
+ size = PAGE_ALIGN(size);
+ mem = vmalloc_32(size);
+ if (!mem)
+ return NULL;
+
+ memset(mem, 0, size); /* Clear the ram out, no junk to the user */
+ adr = (unsigned long) mem;
+ while (size > 0) {
+ SetPageReserved(vmalloc_to_page((void *)adr));
+ adr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+
+ return mem;
+}
+
+static void rvfree(void *mem, unsigned long size)
+{
+ unsigned long adr;
+
+ if (!mem)
+ return;
+
+ adr = (unsigned long) mem;
+ while ((long) size > 0) {
+ ClearPageReserved(vmalloc_to_page((void *)adr));
+ adr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+ vfree(mem);
+}
+
static struct ssd1307fb_array *ssd1307fb_alloc_array(u32 len, u8 type)
{
struct ssd1307fb_array *array;
@@ -538,13 +576,13 @@ static int ssd1307fb_probe(struct i2c_client *client,
if (of_property_read_u32(node, "solomon,vcomh", &par->vcomh))
par->vcomh = par->device_info->default_vcomh;

- vmem = devm_kzalloc(&client->dev, vmem_size, GFP_KERNEL);
if (of_property_read_u32(node, "solomon,dclk-div", &par->dclk_div))
par->dclk_div = par->device_info->default_dclk_div;

if (of_property_read_u32(node, "solomon,dclk-frq", &par->dclk_frq))
par->dclk_frq = par->device_info->default_dclk_frq;

+ vmem = rvmalloc(vmem_size);
if (!vmem) {
dev_err(&client->dev, "Couldn't allocate graphical memory.\n");
ret = -ENOMEM;
@@ -570,7 +608,7 @@ static int ssd1307fb_probe(struct i2c_client *client,
info->var.blue.offset = 0;

info->screen_base = (u8 __force __iomem *)vmem;
- info->fix.smem_start = (unsigned long)vmem;
+ info->fix.smem_start = __pa(vmem);
info->fix.smem_len = vmem_size;

fb_deferred_io_init(info);
@@ -614,6 +652,7 @@ panel_init_error:
};
reset_oled_error:
fb_deferred_io_cleanup(info);
+ rvfree(vmem, vmem_size);
fb_alloc_error:
framebuffer_release(info);
return ret;
--
2.1.1

2015-02-06 22:36:47

by Thomas Niederprüm

[permalink] [raw]
Subject: [PATCH 5/8] fbdev: ssd1307fb: Add module parameter bitsperpixel.

From: Thomas Niederprüm <[email protected]>

This patch adds a module parameter 'bitsperpixel' to adjust the colordepth
of the framebuffer. All values >1 will result in memory map of the requested
color depth. However only the MSB of each pixel will be sent to the device.
The framebuffer identifies itself as a grayscale display with the specified
depth.

Signed-off-by: Thomas Niederprüm <[email protected]>
---
drivers/video/fbdev/ssd1307fb.c | 27 ++++++++++++++++++---------
1 file changed, 18 insertions(+), 9 deletions(-)

diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c
index 945ded9..1d81877 100644
--- a/drivers/video/fbdev/ssd1307fb.c
+++ b/drivers/video/fbdev/ssd1307fb.c
@@ -43,6 +43,11 @@
#define SSD1307FB_SET_COM_PINS_CONFIG 0xda
#define SSD1307FB_SET_VCOMH 0xdb

+#define BITSPERPIXEL 1
+
+static u_int bitsperpixel = BITSPERPIXEL;
+module_param(bitsperpixel, uint, 0);
+
struct ssd1307fb_par;

struct ssd1307fb_deviceinfo {
@@ -91,7 +96,8 @@ static struct fb_fix_screeninfo ssd1307fb_fix = {
};

static struct fb_var_screeninfo ssd1307fb_var = {
- .bits_per_pixel = 1,
+ .bits_per_pixel = BITSPERPIXEL,
+ .grayscale = 1,
};

static void *rvmalloc(unsigned long size)
@@ -223,10 +229,11 @@ static void ssd1307fb_update_display(struct ssd1307fb_par *par)
array->data[array_idx] = 0;
for (k = 0; k < 8; k++) {
u32 page_length = par->width * i;
- u32 index = page_length + (par->width * k + j) / 8;
+ u32 index = page_length * bitsperpixel + (par->width * k + j) * bitsperpixel / 8;
u8 byte = *(vmem + index);
- u8 bit = byte & (1 << (j % 8));
- bit = bit >> (j % 8);
+ u8 bit = byte & (((1 << (bitsperpixel))-1) << (j % 8/bitsperpixel));
+
+ bit = (bit >> (j % 8/bitsperpixel)) >> (bitsperpixel-1);
array->data[array_idx] |= bit << k;
}
}
@@ -548,7 +555,6 @@ static int ssd1307fb_probe(struct i2c_client *client,
if (of_property_read_u32(node, "solomon,page-offset", &par->page_offset))
par->page_offset = 1;

- vmem_size = par->width * par->height / 8;
if (of_property_read_u32(node, "solomon,segment-remap", &par->seg_remap))
par->seg_remap = 0;

@@ -582,6 +588,8 @@ static int ssd1307fb_probe(struct i2c_client *client,
if (of_property_read_u32(node, "solomon,dclk-frq", &par->dclk_frq))
par->dclk_frq = par->device_info->default_dclk_frq;

+ vmem_size = par->width * par->height * bitsperpixel / 8;
+
vmem = rvmalloc(vmem_size);
if (!vmem) {
dev_err(&client->dev, "Couldn't allocate graphical memory.\n");
@@ -591,20 +599,21 @@ static int ssd1307fb_probe(struct i2c_client *client,

info->fbops = &ssd1307fb_ops;
info->fix = ssd1307fb_fix;
- info->fix.line_length = par->width / 8;
+ info->fix.line_length = par->width * bitsperpixel / 8;
info->fbdefio = &ssd1307fb_defio;

info->var = ssd1307fb_var;
+ info->var.bits_per_pixel = bitsperpixel;
info->var.xres = par->width;
info->var.xres_virtual = par->width;
info->var.yres = par->height;
info->var.yres_virtual = par->height;

- info->var.red.length = 1;
+ info->var.red.length = bitsperpixel;
info->var.red.offset = 0;
- info->var.green.length = 1;
+ info->var.green.length = bitsperpixel;
info->var.green.offset = 0;
- info->var.blue.length = 1;
+ info->var.blue.length = bitsperpixel;
info->var.blue.offset = 0;

info->screen_base = (u8 __force __iomem *)vmem;
--
2.1.1

2015-02-06 22:37:53

by Thomas Niederprüm

[permalink] [raw]
Subject: [PATCH 6/8] fbdev: ssd1307fb: Add module parameter to set update delay of the deffered io.

From: Thomas Niederprüm <[email protected]>

This patch adds the module parameter "delaydivider" to set delay for the
deferred io. Effectively this is setting the refresh rate for mmap access
to the framebuffer. The delay for the deferred io is HZ/delaydivider.

Signed-off-by: Thomas Niederprüm <[email protected]>
---
drivers/video/fbdev/ssd1307fb.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c
index 1d81877..b38315d 100644
--- a/drivers/video/fbdev/ssd1307fb.c
+++ b/drivers/video/fbdev/ssd1307fb.c
@@ -44,10 +44,14 @@
#define SSD1307FB_SET_VCOMH 0xdb

#define BITSPERPIXEL 1
+#define DELAYDIVIDER 20

static u_int bitsperpixel = BITSPERPIXEL;
module_param(bitsperpixel, uint, 0);

+static u_int delaydivider = DELAYDIVIDER;
+module_param(delaydivider, uint, 0);
+
struct ssd1307fb_par;

struct ssd1307fb_deviceinfo {
@@ -312,7 +316,7 @@ static void ssd1307fb_deferred_io(struct fb_info *info,
}

static struct fb_deferred_io ssd1307fb_defio = {
- .delay = HZ,
+ .delay = HZ/DELAYDIVIDER,
.deferred_io = ssd1307fb_deferred_io,
};

@@ -601,6 +605,7 @@ static int ssd1307fb_probe(struct i2c_client *client,
info->fix = ssd1307fb_fix;
info->fix.line_length = par->width * bitsperpixel / 8;
info->fbdefio = &ssd1307fb_defio;
+ info->fbdefio->delay = HZ/delaydivider;

info->var = ssd1307fb_var;
info->var.bits_per_pixel = bitsperpixel;
--
2.1.1

2015-02-06 22:36:08

by Thomas Niederprüm

[permalink] [raw]
Subject: [PATCH 7/8] fbdev: ssd1307fb: Add sysfs handles to expose contrast and dim setting to userspace.

From: Thomas Niederprüm <[email protected]>

This patch adds sysfs handles to enable userspace control over the display
contrast as well as the dim mode. The handles are available as "contrast"
and "dim" in the framebuffers sysfs domain.

Signed-off-by: Thomas Niederprüm <[email protected]>
---
drivers/video/fbdev/ssd1307fb.c | 88 ++++++++++++++++++++++++++++++++++++++++-
1 file changed, 87 insertions(+), 1 deletion(-)

diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c
index b38315d..02931c7 100644
--- a/drivers/video/fbdev/ssd1307fb.c
+++ b/drivers/video/fbdev/ssd1307fb.c
@@ -33,6 +33,7 @@
#define SSD1307FB_CONTRAST 0x81
#define SSD1307FB_CHARGE_PUMP 0x8d
#define SSD1307FB_SEG_REMAP_ON 0xa1
+#define SSD1307FB_DISPLAY_DIM 0xac
#define SSD1307FB_DISPLAY_OFF 0xae
#define SSD1307FB_SET_MULTIPLEX_RATIO 0xa8
#define SSD1307FB_DISPLAY_ON 0xaf
@@ -43,6 +44,9 @@
#define SSD1307FB_SET_COM_PINS_CONFIG 0xda
#define SSD1307FB_SET_VCOMH 0xdb

+#define MIN_CONTRAST 0
+#define MAX_CONTRAST 255
+
#define BITSPERPIXEL 1
#define DELAYDIVIDER 20

@@ -69,6 +73,7 @@ struct ssd1307fb_par {
u32 dclk_div;
u32 dclk_frq;
struct ssd1307fb_deviceinfo *device_info;
+ u32 dim;
struct i2c_client *client;
u32 height;
struct fb_info *info;
@@ -515,6 +520,79 @@ static const struct of_device_id ssd1307fb_of_match[] = {
};
MODULE_DEVICE_TABLE(of, ssd1307fb_of_match);

+static ssize_t show_contrast(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct fb_info *info = dev_get_drvdata(device);
+ struct ssd1307fb_par *par = info->par;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", par->contrast);
+}
+
+static ssize_t store_contrast(struct device *device,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fb_info *info = dev_get_drvdata(device);
+ struct ssd1307fb_par *par = info->par;
+ unsigned long contrastval;
+ int ret;
+
+ ret = kstrtoul(buf, 0, &contrastval);
+ if (ret < 0)
+ return ret;
+
+ par->contrast = max(min(contrastval,
+ (ulong)MAX_CONTRAST), (ulong)MIN_CONTRAST);
+
+ ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CONTRAST);
+ ret = ret & ssd1307fb_write_cmd(par->client, par->contrast);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+
+static ssize_t show_dim(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct fb_info *info = dev_get_drvdata(device);
+ struct ssd1307fb_par *par = info->par;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", par->dim);
+}
+
+static ssize_t store_dim(struct device *device,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fb_info *info = dev_get_drvdata(device);
+ struct ssd1307fb_par *par = info->par;
+ unsigned long dimval;
+ int ret;
+
+ ret = kstrtoul(buf, 0, &dimval);
+ if (ret < 0)
+ return ret;
+
+ par->dim = max(min(dimval, (ulong)1), (ulong)0);
+ if (par->dim)
+ ret = ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_DIM);
+ else
+ ret = ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static struct device_attribute device_attrs[] = {
+ __ATTR(contrast, S_IRUGO|S_IWUSR, show_contrast, store_contrast),
+ __ATTR(dim, S_IRUGO|S_IWUSR, show_dim, store_dim),
+
+};
+
static int ssd1307fb_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -523,7 +601,7 @@ static int ssd1307fb_probe(struct i2c_client *client,
u32 vmem_size;
struct ssd1307fb_par *par;
u8 *vmem;
- int ret;
+ int ret, i;

if (!node) {
dev_err(&client->dev, "No device tree data found!\n");
@@ -650,6 +728,14 @@ static int ssd1307fb_probe(struct i2c_client *client,
goto reset_oled_error;

ret = register_framebuffer(info);
+
+ for (i = 0; i < ARRAY_SIZE(device_attrs); i++)
+ ret = device_create_file(info->dev, &device_attrs[i]);
+
+ if (ret) {
+ dev_err(&client->dev, "Couldn't register sysfs nodes\n");
+ }
+
if (ret) {
dev_err(&client->dev, "Couldn't register the framebuffer\n");
goto panel_init_error;
--
2.1.1

2015-02-06 22:35:50

by Thomas Niederprüm

[permalink] [raw]
Subject: [PATCH 8/8] fbdev: ssd1307fb: Turn off display on driver unload.

From: Thomas Niederprüm <[email protected]>

Signed-off-by: Thomas Niederprüm <[email protected]>
---
drivers/video/fbdev/ssd1307fb.c | 5 +++++
1 file changed, 5 insertions(+)

diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c
index 02931c7..be91dfc 100644
--- a/drivers/video/fbdev/ssd1307fb.c
+++ b/drivers/video/fbdev/ssd1307fb.c
@@ -762,6 +762,11 @@ static int ssd1307fb_remove(struct i2c_client *client)
{
struct fb_info *info = i2c_get_clientdata(client);
struct ssd1307fb_par *par = info->par;
+ int ret = 0;
+
+ ret = ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_OFF);
+ if (ret < 0)
+ return ret;

unregister_framebuffer(info);
if (par->device_info->device_id == DEVID_SSD1307) {
--
2.1.1

2015-02-07 10:45:08

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH 2/8] fbdev: ssd1307fb: Unify init code and make controller configurable from device tree

Hi,

On Fri, Feb 06, 2015 at 11:28:08PM +0100, [email protected] wrote:
> From: Thomas Niederpr?m <[email protected]>
>
> This patches unifies the init code for the ssd130X chips and
> adds device tree bindings to describe the hardware configuration
> of the used controller. This gets rid of the magic bit values
> used in the init code so far.
>
> Signed-off-by: Thomas Niederpr?m <[email protected]>
> ---
> .../devicetree/bindings/video/ssd1307fb.txt | 11 +
> drivers/video/fbdev/ssd1307fb.c | 243 ++++++++++++++-------
> 2 files changed, 174 insertions(+), 80 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/video/ssd1307fb.txt b/Documentation/devicetree/bindings/video/ssd1307fb.txt
> index 7a12542..1230f68 100644
> --- a/Documentation/devicetree/bindings/video/ssd1307fb.txt
> +++ b/Documentation/devicetree/bindings/video/ssd1307fb.txt
> @@ -15,6 +15,17 @@ Required properties:
>
> Optional properties:
> - reset-active-low: Is the reset gpio is active on physical low?
> + - solomon,segment-remap: Invert the order of data column to segment mapping
> + - solomon,offset: Map the display start line to one of COM0 - COM63
> + - solomon,contrast: Set initial contrast of the display
> + - solomon,prechargep1: Set the duration of the precharge period phase1
> + - solomon,prechargep2: Set the duration of the precharge period phase2
> + - solomon,com-alt: Enable/disable alternate COM pin configuration
> + - solomon,com-lrremap: Enable/disable left-right remap of COM pins
> + - solomon,com-invdir: Invert COM scan direction
> + - solomon,vcomh: Set VCOMH regulator voltage
> + - solomon,dclk-div: Set display clock divider
> + - solomon,dclk-frq: Set display clock frequency

I'm sorry, but this is the wrong approach, for at least two reasons:
you broke all existing users of that driver, which is a clear no-go,
and the DT itself should not contain any direct mapping of the
registers.


>
> [0]: Documentation/devicetree/bindings/pwm/pwm.txt
>
> diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c
> index 3d6611f..4f435aa 100644
> --- a/drivers/video/fbdev/ssd1307fb.c
> +++ b/drivers/video/fbdev/ssd1307fb.c
> @@ -16,6 +16,9 @@
> #include <linux/pwm.h>
> #include <linux/delay.h>
>
> +#define DEVID_SSD1306 6
> +#define DEVID_SSD1307 7
> +
> #define SSD1307FB_DATA 0x40
> #define SSD1307FB_COMMAND 0x80
>
> @@ -40,20 +43,33 @@
>
> struct ssd1307fb_par;
>
> -struct ssd1307fb_ops {
> - int (*init)(struct ssd1307fb_par *);
> - int (*remove)(struct ssd1307fb_par *);
> +struct ssd1307fb_deviceinfo {
> + int device_id;
> + u32 default_vcomh;
> + u32 default_dclk_div;
> + u32 default_dclk_frq;
> };
>
> struct ssd1307fb_par {
> + u32 com_alt;
> + u32 com_invdir;
> + u32 com_lrremap;
> + u32 contrast;
> + u32 dclk_div;
> + u32 dclk_frq;
> + struct ssd1307fb_deviceinfo *device_info;
> struct i2c_client *client;
> u32 height;
> struct fb_info *info;
> - struct ssd1307fb_ops *ops;
> + u32 offset;
> u32 page_offset;
> + u32 prechargep1;
> + u32 prechargep2;
> struct pwm_device *pwm;
> u32 pwm_period;
> int reset;
> + u32 seg_remap;
> + u32 vcomh;
> u32 width;
> };
>
> @@ -254,127 +270,151 @@ static struct fb_deferred_io ssd1307fb_defio = {
> .deferred_io = ssd1307fb_deferred_io,
> };
>
> -static int ssd1307fb_ssd1307_init(struct ssd1307fb_par *par)
> +static int ssd1307fb_init(struct ssd1307fb_par *par)
> {
> int ret;
> + u32 precharge, dclk, com_invdir, compins;
>
> - par->pwm = pwm_get(&par->client->dev, NULL);
> - if (IS_ERR(par->pwm)) {
> - dev_err(&par->client->dev, "Could not get PWM from device tree!\n");
> - return PTR_ERR(par->pwm);
> - }
> -
> - par->pwm_period = pwm_get_period(par->pwm);
> - /* Enable the PWM */
> - pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period);
> - pwm_enable(par->pwm);
> -
> - dev_dbg(&par->client->dev, "Using PWM%d with a %dns period.\n",
> - par->pwm->pwm, par->pwm_period);
> -
> - /* Map column 127 of the OLED to segment 0 */
> - ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON);
> - if (ret < 0)
> - return ret;
> -
> - /* Turn on the display */
> - ret = ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON);
> - if (ret < 0)
> - return ret;
> -
> - return 0;
> -}
> -
> -static int ssd1307fb_ssd1307_remove(struct ssd1307fb_par *par)
> -{
> - pwm_disable(par->pwm);
> - pwm_put(par->pwm);
> - return 0;
> -}
> + if (par->device_info->device_id == DEVID_SSD1307) {
> + par->pwm = pwm_get(&par->client->dev, NULL);
> + if (IS_ERR(par->pwm)) {
> + dev_err(&par->client->dev, "Could not get PWM from device tree!\n");
> + return PTR_ERR(par->pwm);
> + }
>
> -static struct ssd1307fb_ops ssd1307fb_ssd1307_ops = {
> - .init = ssd1307fb_ssd1307_init,
> - .remove = ssd1307fb_ssd1307_remove,
> -};
> + par->pwm_period = pwm_get_period(par->pwm);
> + /* Enable the PWM */
> + pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period);
> + pwm_enable(par->pwm);
>
> -static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
> -{
> - int ret;
> + dev_dbg(&par->client->dev, "Using PWM%d with a %dns period.\n",
> + par->pwm->pwm, par->pwm_period);
> + };
>
> /* Set initial contrast */
> ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CONTRAST);
> - ret = ret & ssd1307fb_write_cmd(par->client, 0x7f);
> if (ret < 0)
> return ret;
>
> - /* Set COM direction */
> - ret = ssd1307fb_write_cmd(par->client, 0xc8);
> + ret = ssd1307fb_write_cmd(par->client, par->contrast);
> if (ret < 0)
> return ret;
>
> /* Set segment re-map */
> - ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON);
> + if (par->seg_remap) {
> + ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON);
> + if (ret < 0)
> + return ret;
> + };
> +
> + /* Set COM direction */
> + com_invdir = 0xc0 | (par->com_invdir & 0xf) << 3;
> + ret = ssd1307fb_write_cmd(par->client, com_invdir);
> if (ret < 0)
> return ret;
>
> /* Set multiplex ratio value */
> ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_MULTIPLEX_RATIO);
> - ret = ret & ssd1307fb_write_cmd(par->client, par->height - 1);
> + if (ret < 0)
> + return ret;
> +
> + ret = ssd1307fb_write_cmd(par->client, par->height - 1);
> if (ret < 0)
> return ret;
>
> /* set display offset value */
> ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_DISPLAY_OFFSET);
> - ret = ssd1307fb_write_cmd(par->client, 0x20);
> + if (ret < 0)
> + return ret;
> +
> + ret = ssd1307fb_write_cmd(par->client, par->offset);
> if (ret < 0)
> return ret;
>
> /* Set clock frequency */
> + dclk = (par->dclk_div & 0xf) | (par->dclk_frq & 0xf) << 4;
> ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_CLOCK_FREQ);
> - ret = ret & ssd1307fb_write_cmd(par->client, 0xf0);
> if (ret < 0)
> return ret;
>
> - /* Set precharge period in number of ticks from the internal clock */
> + ret = ssd1307fb_write_cmd(par->client, dclk);
> + if (ret < 0)
> + return ret;
> +
> + /* Set precharge period in number of ticks from the internal clock*/
> + precharge = (par->prechargep1 & 0xf) | (par->prechargep2 & 0xf) << 4;
> ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PRECHARGE_PERIOD);
> - ret = ret & ssd1307fb_write_cmd(par->client, 0x22);
> + if (ret < 0)
> + return ret;
> +
> + ret = ssd1307fb_write_cmd(par->client, precharge);
> if (ret < 0)
> return ret;
>
> /* Set COM pins configuration */
> + compins = 0x02 | (par->com_alt & 0x1) << 4
> + | (par->com_lrremap & 0x1) << 5;
> ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COM_PINS_CONFIG);
> - ret = ret & ssd1307fb_write_cmd(par->client, 0x22);
> + if (ret < 0)
> + return ret;
> +
> + ret = ssd1307fb_write_cmd(par->client, compins);
> if (ret < 0)
> return ret;
>
> /* Set VCOMH */
> ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_VCOMH);
> - ret = ret & ssd1307fb_write_cmd(par->client, 0x49);
> if (ret < 0)
> return ret;
>
> - /* Turn on the DC-DC Charge Pump */
> - ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CHARGE_PUMP);
> - ret = ret & ssd1307fb_write_cmd(par->client, 0x14);
> + ret = ssd1307fb_write_cmd(par->client, par->vcomh);
> if (ret < 0)
> return ret;
>
> + if (par->device_info->device_id == DEVID_SSD1306) {
> + /* Turn on the DC-DC Charge Pump */
> + ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CHARGE_PUMP);
> + if (ret < 0)
> + return ret;
> +
> + ret = ssd1307fb_write_cmd(par->client, 0x14);
> + if (ret < 0)
> + return ret;
> + };
> +
> /* Switch to horizontal addressing mode */
> ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_ADDRESS_MODE);
> - ret = ret & ssd1307fb_write_cmd(par->client,
> + if (ret < 0)
> + return ret;
> +
> + ret = ssd1307fb_write_cmd(par->client,
> SSD1307FB_SET_ADDRESS_MODE_HORIZONTAL);
> if (ret < 0)
> return ret;
>
> + /* Set column range */
> ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COL_RANGE);
> - ret = ret & ssd1307fb_write_cmd(par->client, 0x0);
> - ret = ret & ssd1307fb_write_cmd(par->client, par->width - 1);
> if (ret < 0)
> return ret;
>
> + ret = ssd1307fb_write_cmd(par->client, 0x0);
> + if (ret < 0)
> + return ret;
> +
> + ret = ssd1307fb_write_cmd(par->client, par->width - 1);
> + if (ret < 0)
> + return ret;
> +
> + /* Set page range */
> ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PAGE_RANGE);
> - ret = ret & ssd1307fb_write_cmd(par->client, 0x0);
> - ret = ret & ssd1307fb_write_cmd(par->client,
> + if (ret < 0)
> + return ret;
> +
> + ret = ssd1307fb_write_cmd(par->client, 0x0);
> + if (ret < 0)
> + return ret;
> +
> + ret = ssd1307fb_write_cmd(par->client,
> par->page_offset + (par->height / 8) - 1);
> if (ret < 0)
> return ret;
> @@ -387,18 +427,28 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
> return 0;
> }
>
> -static struct ssd1307fb_ops ssd1307fb_ssd1306_ops = {
> - .init = ssd1307fb_ssd1306_init,
> +static struct ssd1307fb_deviceinfo ssd1307fb_ssd1306_deviceinfo = {
> + .device_id = DEVID_SSD1306,
> + .default_vcomh = 0x20,
> + .default_dclk_div = 0,
> + .default_dclk_frq = 8,
> +};
> +
> +static struct ssd1307fb_deviceinfo ssd1307fb_ssd1307_deviceinfo = {
> + .device_id = DEVID_SSD1307,
> + .default_vcomh = 0x20,
> + .default_dclk_div = 1,
> + .default_dclk_frq = 12,
> };
>
> static const struct of_device_id ssd1307fb_of_match[] = {
> {
> .compatible = "solomon,ssd1306fb-i2c",
> - .data = (void *)&ssd1307fb_ssd1306_ops,
> + .data = (void *)&ssd1307fb_ssd1306_deviceinfo,
> },
> {
> .compatible = "solomon,ssd1307fb-i2c",
> - .data = (void *)&ssd1307fb_ssd1307_ops,
> + .data = (void *)&ssd1307fb_ssd1307_deviceinfo,
> },
> {},
> };
> @@ -429,8 +479,8 @@ static int ssd1307fb_probe(struct i2c_client *client,
> par->info = info;
> par->client = client;
>
> - par->ops = (struct ssd1307fb_ops *)of_match_device(ssd1307fb_of_match,
> - &client->dev)->data;
> + par->device_info = (struct ssd1307fb_deviceinfo *)of_match_device(
> + ssd1307fb_of_match, &client->dev)->data;
>
> par->reset = of_get_named_gpio(client->dev.of_node,
> "reset-gpios", 0);
> @@ -449,8 +499,40 @@ static int ssd1307fb_probe(struct i2c_client *client,
> par->page_offset = 1;
>
> vmem_size = par->width * par->height / 8;
> + if (of_property_read_u32(node, "solomon,segment-remap", &par->seg_remap))
> + par->seg_remap = 0;
> +
> + if (of_property_read_u32(node, "solomon,offset", &par->offset))
> + par->offset = 0;
> +
> + if (of_property_read_u32(node, "solomon,contrast", &par->contrast))
> + par->contrast = 128;
> +
> + if (of_property_read_u32(node, "solomon,prechargep1", &par->prechargep1))
> + par->prechargep1 = 2;
> +
> + if (of_property_read_u32(node, "solomon,prechargep2", &par->prechargep2))
> + par->prechargep2 = 2;
> +
> + if (of_property_read_u32(node, "solomon,com-alt", &par->com_alt))
> + par->com_alt = 1;
> +
> + if (of_property_read_u32(node, "solomon,com-lrremap", &par->com_lrremap))
> + par->com_lrremap = 0;
> +
> + if (of_property_read_u32(node, "solomon,com-invdir", &par->com_invdir))
> + par->com_invdir = 0;
> +
> + if (of_property_read_u32(node, "solomon,vcomh", &par->vcomh))
> + par->vcomh = par->device_info->default_vcomh;
>
> vmem = devm_kzalloc(&client->dev, vmem_size, GFP_KERNEL);
> + if (of_property_read_u32(node, "solomon,dclk-div", &par->dclk_div))
> + par->dclk_div = par->device_info->default_dclk_div;
> +
> + if (of_property_read_u32(node, "solomon,dclk-frq", &par->dclk_frq))
> + par->dclk_frq = par->device_info->default_dclk_frq;
> +
> if (!vmem) {
> dev_err(&client->dev, "Couldn't allocate graphical memory.\n");
> ret = -ENOMEM;
> @@ -499,11 +581,9 @@ static int ssd1307fb_probe(struct i2c_client *client,
> gpio_set_value(par->reset, 1);
> udelay(4);
>
> - if (par->ops->init) {
> - ret = par->ops->init(par);
> - if (ret)
> - goto reset_oled_error;
> - }
> + ret = ssd1307fb_init(par);
> + if (ret)
> + goto reset_oled_error;
>
> ret = register_framebuffer(info);
> if (ret) {
> @@ -516,8 +596,10 @@ static int ssd1307fb_probe(struct i2c_client *client,
> return 0;
>
> panel_init_error:
> - if (par->ops->remove)
> - par->ops->remove(par);
> + if (par->device_info->device_id == DEVID_SSD1307) {
> + pwm_disable(par->pwm);
> + pwm_put(par->pwm);
> + };
> reset_oled_error:
> fb_deferred_io_cleanup(info);
> fb_alloc_error:
> @@ -531,11 +613,12 @@ static int ssd1307fb_remove(struct i2c_client *client)
> struct ssd1307fb_par *par = info->par;
>
> unregister_framebuffer(info);
> - if (par->ops->remove)
> - par->ops->remove(par);
> + if (par->device_info->device_id == DEVID_SSD1307) {
> + pwm_disable(par->pwm);
> + pwm_put(par->pwm);
> + };
> fb_deferred_io_cleanup(info);
> framebuffer_release(info);
> -
> return 0;
> }
>
> --
> 2.1.1
>

--
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com


Attachments:
(No filename) (13.72 kB)
signature.asc (819.00 B)
Digital signature
Download all attachments

2015-02-07 11:20:18

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH 4/8] fbdev: ssd1307fb: Use vmalloc to allocate video memory.

Hi,

On Fri, Feb 06, 2015 at 11:28:10PM +0100, [email protected] wrote:
> From: Thomas Niederpr?m <[email protected]>
>
> It makes sense to use vmalloc to allocate the video buffer since it
> has to be page aligned memory for using it with mmap.

Please wrap your commit log at 80 chars.

It looks like there's numerous fbdev drivers using this (especially
since you copy pasted that code, without mentionning it).

That should be turned into an allocator so that drivers all get this
right.

> Also deffered io seems buggy in combination with kmalloc'ed memory
> (crash on unloading the module).

And maybe that's the real issue to fix.

> Signed-off-by: Thomas Niederpr?m <[email protected]>
> ---
> drivers/video/fbdev/ssd1307fb.c | 43 +++++++++++++++++++++++++++++++++++++++--
> 1 file changed, 41 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c
> index 01cfb46..945ded9 100644
> --- a/drivers/video/fbdev/ssd1307fb.c
> +++ b/drivers/video/fbdev/ssd1307fb.c
> @@ -11,6 +11,7 @@
> #include <linux/i2c.h>
> #include <linux/fb.h>
> #include <linux/uaccess.h>
> +#include <linux/vmalloc.h>
> #include <linux/of_device.h>
> #include <linux/of_gpio.h>
> #include <linux/pwm.h>
> @@ -93,6 +94,43 @@ static struct fb_var_screeninfo ssd1307fb_var = {
> .bits_per_pixel = 1,
> };
>
> +static void *rvmalloc(unsigned long size)
> +{
> + void *mem;
> + unsigned long adr;
> +
> + size = PAGE_ALIGN(size);

Isn't vmalloc already taking care of that?

> + mem = vmalloc_32(size);

Why the 32 bits variant?

> + if (!mem)
> + return NULL;
> +
> + memset(mem, 0, size); /* Clear the ram out, no junk to the user */
> + adr = (unsigned long) mem;
> + while (size > 0) {
> + SetPageReserved(vmalloc_to_page((void *)adr));

I'm not really sure it makes sense to mark all pages reserved if we're
not even sure we're going to use mmap.

And why do you need to mark these pages reserved in the first place?

> + adr += PAGE_SIZE;
> + size -= PAGE_SIZE;
> + }
> +
> + return mem;
> +}
> +
> +static void rvfree(void *mem, unsigned long size)
> +{
> + unsigned long adr;
> +
> + if (!mem)
> + return;
> +
> + adr = (unsigned long) mem;
> + while ((long) size > 0) {
> + ClearPageReserved(vmalloc_to_page((void *)adr));
> + adr += PAGE_SIZE;
> + size -= PAGE_SIZE;
> + }
> + vfree(mem);
> +}
> +
> static struct ssd1307fb_array *ssd1307fb_alloc_array(u32 len, u8 type)
> {
> struct ssd1307fb_array *array;
> @@ -538,13 +576,13 @@ static int ssd1307fb_probe(struct i2c_client *client,
> if (of_property_read_u32(node, "solomon,vcomh", &par->vcomh))
> par->vcomh = par->device_info->default_vcomh;
>
> - vmem = devm_kzalloc(&client->dev, vmem_size, GFP_KERNEL);
> if (of_property_read_u32(node, "solomon,dclk-div", &par->dclk_div))
> par->dclk_div = par->device_info->default_dclk_div;
>
> if (of_property_read_u32(node, "solomon,dclk-frq", &par->dclk_frq))
> par->dclk_frq = par->device_info->default_dclk_frq;
>
> + vmem = rvmalloc(vmem_size);
> if (!vmem) {
> dev_err(&client->dev, "Couldn't allocate graphical memory.\n");
> ret = -ENOMEM;
> @@ -570,7 +608,7 @@ static int ssd1307fb_probe(struct i2c_client *client,
> info->var.blue.offset = 0;
>
> info->screen_base = (u8 __force __iomem *)vmem;
> - info->fix.smem_start = (unsigned long)vmem;
> + info->fix.smem_start = __pa(vmem);

Why are you changing from virtual to physical address here?

Maxime

--
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com


Attachments:
(No filename) (3.51 kB)
signature.asc (819.00 B)
Digital signature
Download all attachments

2015-02-07 11:25:15

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH 5/8] fbdev: ssd1307fb: Add module parameter bitsperpixel.

On Fri, Feb 06, 2015 at 11:28:11PM +0100, [email protected] wrote:
> From: Thomas Niederpr?m <[email protected]>
>
> This patch adds a module parameter 'bitsperpixel' to adjust the colordepth
> of the framebuffer. All values >1 will result in memory map of the requested
> color depth. However only the MSB of each pixel will be sent to the device.
> The framebuffer identifies itself as a grayscale display with the specified
> depth.

I'm not sure this is the right thing to do.

The bits per pixel for this display is rightfully defined, used and
reported to the userspace, why would you want to change that?

Maxime

--
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com


Attachments:
(No filename) (743.00 B)
signature.asc (819.00 B)
Digital signature
Download all attachments

2015-02-07 11:30:20

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH 6/8] fbdev: ssd1307fb: Add module parameter to set update delay of the deffered io.

On Fri, Feb 06, 2015 at 11:28:12PM +0100, [email protected] wrote:
> From: Thomas Niederpr?m <[email protected]>
>
> This patch adds the module parameter "delaydivider" to set delay for the
> deferred io. Effectively this is setting the refresh rate for mmap access
> to the framebuffer. The delay for the deferred io is HZ/delaydivider.

So this is actually a refresh rate?

Maybe you could expose it as such, and pass a frequency in Hz as an
argument.

Exposing the divider directly has some issues, since the bootloader
that set the parameter won't know the HZ value, you'll end up with
different rates for different configurations, without any way to do
something about it.

>
> Signed-off-by: Thomas Niederpr?m <[email protected]>
> ---
> drivers/video/fbdev/ssd1307fb.c | 7 ++++++-
> 1 file changed, 6 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c
> index 1d81877..b38315d 100644
> --- a/drivers/video/fbdev/ssd1307fb.c
> +++ b/drivers/video/fbdev/ssd1307fb.c
> @@ -44,10 +44,14 @@
> #define SSD1307FB_SET_VCOMH 0xdb
>
> #define BITSPERPIXEL 1
> +#define DELAYDIVIDER 20
>
> static u_int bitsperpixel = BITSPERPIXEL;
> module_param(bitsperpixel, uint, 0);
>
> +static u_int delaydivider = DELAYDIVIDER;
> +module_param(delaydivider, uint, 0);
> +

You're breaking the existing behaviour.

> struct ssd1307fb_par;
>
> struct ssd1307fb_deviceinfo {
> @@ -312,7 +316,7 @@ static void ssd1307fb_deferred_io(struct fb_info *info,
> }
>
> static struct fb_deferred_io ssd1307fb_defio = {
> - .delay = HZ,
> + .delay = HZ/DELAYDIVIDER,
> .deferred_io = ssd1307fb_deferred_io,
> };
>
> @@ -601,6 +605,7 @@ static int ssd1307fb_probe(struct i2c_client *client,
> info->fix = ssd1307fb_fix;
> info->fix.line_length = par->width * bitsperpixel / 8;
> info->fbdefio = &ssd1307fb_defio;
> + info->fbdefio->delay = HZ/delaydivider;

That won't work with multiple instances of the same driver
unfortunately.

>
> info->var = ssd1307fb_var;
> info->var.bits_per_pixel = bitsperpixel;
> --
> 2.1.1
>

--
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com


Attachments:
(No filename) (2.18 kB)
signature.asc (819.00 B)
Digital signature
Download all attachments

2015-02-07 11:45:20

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH 7/8] fbdev: ssd1307fb: Add sysfs handles to expose contrast and dim setting to userspace.

On Fri, Feb 06, 2015 at 11:28:13PM +0100, [email protected] wrote:
> From: Thomas Niederpr?m <[email protected]>
>
> This patch adds sysfs handles to enable userspace control over the display
> contrast as well as the dim mode. The handles are available as "contrast"
> and "dim" in the framebuffers sysfs domain.
>
> Signed-off-by: Thomas Niederpr?m <[email protected]>
> ---
> drivers/video/fbdev/ssd1307fb.c | 88 ++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 87 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c
> index b38315d..02931c7 100644
> --- a/drivers/video/fbdev/ssd1307fb.c
> +++ b/drivers/video/fbdev/ssd1307fb.c
> @@ -33,6 +33,7 @@
> #define SSD1307FB_CONTRAST 0x81
> #define SSD1307FB_CHARGE_PUMP 0x8d
> #define SSD1307FB_SEG_REMAP_ON 0xa1
> +#define SSD1307FB_DISPLAY_DIM 0xac
> #define SSD1307FB_DISPLAY_OFF 0xae
> #define SSD1307FB_SET_MULTIPLEX_RATIO 0xa8
> #define SSD1307FB_DISPLAY_ON 0xaf
> @@ -43,6 +44,9 @@
> #define SSD1307FB_SET_COM_PINS_CONFIG 0xda
> #define SSD1307FB_SET_VCOMH 0xdb
>
> +#define MIN_CONTRAST 0
> +#define MAX_CONTRAST 255
> +
> #define BITSPERPIXEL 1
> #define DELAYDIVIDER 20
>
> @@ -69,6 +73,7 @@ struct ssd1307fb_par {
> u32 dclk_div;
> u32 dclk_frq;
> struct ssd1307fb_deviceinfo *device_info;
> + u32 dim;
> struct i2c_client *client;
> u32 height;
> struct fb_info *info;
> @@ -515,6 +520,79 @@ static const struct of_device_id ssd1307fb_of_match[] = {
> };
> MODULE_DEVICE_TABLE(of, ssd1307fb_of_match);
>
> +static ssize_t show_contrast(struct device *device,
> + struct device_attribute *attr, char *buf)
> +{
> + struct fb_info *info = dev_get_drvdata(device);
> + struct ssd1307fb_par *par = info->par;
> +
> + return snprintf(buf, PAGE_SIZE, "%d\n", par->contrast);
> +}
> +
> +static ssize_t store_contrast(struct device *device,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct fb_info *info = dev_get_drvdata(device);
> + struct ssd1307fb_par *par = info->par;
> + unsigned long contrastval;
> + int ret;
> +
> + ret = kstrtoul(buf, 0, &contrastval);
> + if (ret < 0)
> + return ret;
> +
> + par->contrast = max(min(contrastval,
> + (ulong)MAX_CONTRAST), (ulong)MIN_CONTRAST);
> +
> + ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CONTRAST);
> + ret = ret & ssd1307fb_write_cmd(par->client, par->contrast);
> + if (ret < 0)
> + return ret;
> +
> + return count;
> +}
> +
> +
> +static ssize_t show_dim(struct device *device,
> + struct device_attribute *attr, char *buf)
> +{
> + struct fb_info *info = dev_get_drvdata(device);
> + struct ssd1307fb_par *par = info->par;
> +
> + return snprintf(buf, PAGE_SIZE, "%d\n", par->dim);
> +}
> +
> +static ssize_t store_dim(struct device *device,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct fb_info *info = dev_get_drvdata(device);
> + struct ssd1307fb_par *par = info->par;
> + unsigned long dimval;
> + int ret;
> +
> + ret = kstrtoul(buf, 0, &dimval);
> + if (ret < 0)
> + return ret;
> +
> + par->dim = max(min(dimval, (ulong)1), (ulong)0);
> + if (par->dim)
> + ret = ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_DIM);
> + else
> + ret = ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON);
> + if (ret < 0)
> + return ret;
> +
> + return count;
> +}
> +
> +static struct device_attribute device_attrs[] = {
> + __ATTR(contrast, S_IRUGO|S_IWUSR, show_contrast, store_contrast),
> + __ATTR(dim, S_IRUGO|S_IWUSR, show_dim, store_dim),
> +
> +};
> +

I would have thought this was something accessible through the
framebuffer ioctl.

Apparently it's not, at least for the contrast, so maybe it should be
added there, instead of doing it for a single driver?

(oh, and btw, every sysfs file should be documented in
Documentation/ABI)

> static int ssd1307fb_probe(struct i2c_client *client,
> const struct i2c_device_id *id)
> {
> @@ -523,7 +601,7 @@ static int ssd1307fb_probe(struct i2c_client *client,
> u32 vmem_size;
> struct ssd1307fb_par *par;
> u8 *vmem;
> - int ret;
> + int ret, i;
>
> if (!node) {
> dev_err(&client->dev, "No device tree data found!\n");
> @@ -650,6 +728,14 @@ static int ssd1307fb_probe(struct i2c_client *client,
> goto reset_oled_error;
>
> ret = register_framebuffer(info);
> +
> + for (i = 0; i < ARRAY_SIZE(device_attrs); i++)
> + ret = device_create_file(info->dev, &device_attrs[i]);
> +
> + if (ret) {
> + dev_err(&client->dev, "Couldn't register sysfs nodes\n");
> + }
> +

sysfs_create_groups does pretty much that already.

And don't forget to remove these files in the .remove()

> if (ret) {
> dev_err(&client->dev, "Couldn't register the framebuffer\n");
> goto panel_init_error;
> --
> 2.1.1
>

Maxime

--
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com


Attachments:
(No filename) (4.85 kB)
signature.asc (819.00 B)
Digital signature
Download all attachments

2015-02-07 11:50:18

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH 8/8] fbdev: ssd1307fb: Turn off display on driver unload.

On Fri, Feb 06, 2015 at 11:28:14PM +0100, [email protected] wrote:
> From: Thomas Niederpr?m <[email protected]>
>

A commit log is always nice :)

> Signed-off-by: Thomas Niederpr?m <[email protected]>
> ---
> drivers/video/fbdev/ssd1307fb.c | 5 +++++
> 1 file changed, 5 insertions(+)
>
> diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c
> index 02931c7..be91dfc 100644
> --- a/drivers/video/fbdev/ssd1307fb.c
> +++ b/drivers/video/fbdev/ssd1307fb.c
> @@ -762,6 +762,11 @@ static int ssd1307fb_remove(struct i2c_client *client)
> {
> struct fb_info *info = i2c_get_clientdata(client);
> struct ssd1307fb_par *par = info->par;
> + int ret = 0;
> +
> + ret = ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_OFF);
> + if (ret < 0)
> + return ret;

I don't think we really care about the return value here.

It might be even worse actually, since you'll end up in a intermediate
state, where you won't have freed everything, but your remove method
has been called still.

Maxime

--
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com


Attachments:
(No filename) (1.12 kB)
signature.asc (819.00 B)
Digital signature
Download all attachments

2015-02-07 15:00:15

by Thomas Niederprüm

[permalink] [raw]
Subject: Re: [PATCH 2/8] fbdev: ssd1307fb: Unify init code and make controller configurable from device tree

Am Sat, 7 Feb 2015 11:42:25 +0100
schrieb Maxime Ripard <[email protected]>:

> Hi,
>
> On Fri, Feb 06, 2015 at 11:28:08PM +0100, [email protected]
> wrote:
> > From: Thomas Niederprüm <[email protected]>
> >
> > This patches unifies the init code for the ssd130X chips and
> > adds device tree bindings to describe the hardware configuration
> > of the used controller. This gets rid of the magic bit values
> > used in the init code so far.
> >
> > Signed-off-by: Thomas Niederprüm <[email protected]>
> > ---
> > .../devicetree/bindings/video/ssd1307fb.txt | 11 +
> > drivers/video/fbdev/ssd1307fb.c | 243
> > ++++++++++++++------- 2 files changed, 174 insertions(+), 80
> > deletions(-)
> >
> > diff --git a/Documentation/devicetree/bindings/video/ssd1307fb.txt
> > b/Documentation/devicetree/bindings/video/ssd1307fb.txt index
> > 7a12542..1230f68 100644 ---
> > a/Documentation/devicetree/bindings/video/ssd1307fb.txt +++
> > b/Documentation/devicetree/bindings/video/ssd1307fb.txt @@ -15,6
> > +15,17 @@ Required properties:
> > Optional properties:
> > - reset-active-low: Is the reset gpio is active on physical low?
> > + - solomon,segment-remap: Invert the order of data column to
> > segment mapping
> > + - solomon,offset: Map the display start line to one of COM0 -
> > COM63
> > + - solomon,contrast: Set initial contrast of the display
> > + - solomon,prechargep1: Set the duration of the precharge period
> > phase1
> > + - solomon,prechargep2: Set the duration of the precharge period
> > phase2
> > + - solomon,com-alt: Enable/disable alternate COM pin configuration
> > + - solomon,com-lrremap: Enable/disable left-right remap of COM
> > pins
> > + - solomon,com-invdir: Invert COM scan direction
> > + - solomon,vcomh: Set VCOMH regulator voltage
> > + - solomon,dclk-div: Set display clock divider
> > + - solomon,dclk-frq: Set display clock frequency
>
> I'm sorry, but this is the wrong approach, for at least two reasons:
> you broke all existing users of that driver, which is a clear no-go,

Unfortunately this is true. The problem is that the SSD130X controllers
allow for a very versatile wiring of the display to the controller.
It's over to the manufacturer of the OLED module (disp+controller) to
decide how it's actually wired and during device initialization the
driver has to take care to configure the SSD130X controller according
to that wiring. If the driver fails to do so you will end up having
your display showing garbage. Unfortunately the current sate of the
initialization code of the ssd1307fb driver is not very flexible in that
respect. Taking a look at the initialization code for the ssd1306 shows
that it was written with one very special display module in mind. Most
of the magic bit values set there are non-default values according to
the datasheet. The result is that the driver works with that one
particular display module but many other (differently wired) display
modules using a ssd1306 controller won't work without changing the
hardcoded magic bit values.

My idea here was to set all configuration to the default values (as
given in the datasheet) unless it is overwritten by DT. Of course,
without a change in DT, this breaks the driver for all existing users.
The only alternative would be to set the current values as default.
Somehow this feels wrong to me as these values look arbitrary when you
don't know what exact display module they were set for. But if you
insist, I will change the default values.

> and the DT itself should not contain any direct mapping of the
> registers.
>

I think I don't get what you mean here. Is it because I do no sanity
checks of the numbers set in DT? I was just looking for a way to hand
over the information about the wiring of display to the driver. How
would you propose to solve this?

>
> >
> > [0]: Documentation/devicetree/bindings/pwm/pwm.txt
> >
> > diff --git a/drivers/video/fbdev/ssd1307fb.c
> > b/drivers/video/fbdev/ssd1307fb.c index 3d6611f..4f435aa 100644
> > --- a/drivers/video/fbdev/ssd1307fb.c
> > +++ b/drivers/video/fbdev/ssd1307fb.c
> > @@ -16,6 +16,9 @@
> > #include <linux/pwm.h>
> > #include <linux/delay.h>
> >
> > +#define DEVID_SSD1306 6
> > +#define DEVID_SSD1307 7
> > +
> > #define SSD1307FB_DATA 0x40
> > #define SSD1307FB_COMMAND 0x80
> >
> > @@ -40,20 +43,33 @@
> >
> > struct ssd1307fb_par;
> >
> > -struct ssd1307fb_ops {
> > - int (*init)(struct ssd1307fb_par *);
> > - int (*remove)(struct ssd1307fb_par *);
> > +struct ssd1307fb_deviceinfo {
> > + int device_id;
> > + u32 default_vcomh;
> > + u32 default_dclk_div;
> > + u32 default_dclk_frq;
> > };
> >
> > struct ssd1307fb_par {
> > + u32 com_alt;
> > + u32 com_invdir;
> > + u32 com_lrremap;
> > + u32 contrast;
> > + u32 dclk_div;
> > + u32 dclk_frq;
> > + struct ssd1307fb_deviceinfo *device_info;
> > struct i2c_client *client;
> > u32 height;
> > struct fb_info *info;
> > - struct ssd1307fb_ops *ops;
> > + u32 offset;
> > u32 page_offset;
> > + u32 prechargep1;
> > + u32 prechargep2;
> > struct pwm_device *pwm;
> > u32 pwm_period;
> > int reset;
> > + u32 seg_remap;
> > + u32 vcomh;
> > u32 width;
> > };
> >
> > @@ -254,127 +270,151 @@ static struct fb_deferred_io
> > ssd1307fb_defio = { .deferred_io = ssd1307fb_deferred_io,
> > };
> >
> > -static int ssd1307fb_ssd1307_init(struct ssd1307fb_par *par)
> > +static int ssd1307fb_init(struct ssd1307fb_par *par)
> > {
> > int ret;
> > + u32 precharge, dclk, com_invdir, compins;
> >
> > - par->pwm = pwm_get(&par->client->dev, NULL);
> > - if (IS_ERR(par->pwm)) {
> > - dev_err(&par->client->dev, "Could not get PWM from
> > device tree!\n");
> > - return PTR_ERR(par->pwm);
> > - }
> > -
> > - par->pwm_period = pwm_get_period(par->pwm);
> > - /* Enable the PWM */
> > - pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period);
> > - pwm_enable(par->pwm);
> > -
> > - dev_dbg(&par->client->dev, "Using PWM%d with a %dns
> > period.\n",
> > - par->pwm->pwm, par->pwm_period);
> > -
> > - /* Map column 127 of the OLED to segment 0 */
> > - ret = ssd1307fb_write_cmd(par->client,
> > SSD1307FB_SEG_REMAP_ON);
> > - if (ret < 0)
> > - return ret;
> > -
> > - /* Turn on the display */
> > - ret = ssd1307fb_write_cmd(par->client,
> > SSD1307FB_DISPLAY_ON);
> > - if (ret < 0)
> > - return ret;
> > -
> > - return 0;
> > -}
> > -
> > -static int ssd1307fb_ssd1307_remove(struct ssd1307fb_par *par)
> > -{
> > - pwm_disable(par->pwm);
> > - pwm_put(par->pwm);
> > - return 0;
> > -}
> > + if (par->device_info->device_id == DEVID_SSD1307) {
> > + par->pwm = pwm_get(&par->client->dev, NULL);
> > + if (IS_ERR(par->pwm)) {
> > + dev_err(&par->client->dev, "Could not get
> > PWM from device tree!\n");
> > + return PTR_ERR(par->pwm);
> > + }
> >
> > -static struct ssd1307fb_ops ssd1307fb_ssd1307_ops = {
> > - .init = ssd1307fb_ssd1307_init,
> > - .remove = ssd1307fb_ssd1307_remove,
> > -};
> > + par->pwm_period = pwm_get_period(par->pwm);
> > + /* Enable the PWM */
> > + pwm_config(par->pwm, par->pwm_period / 2,
> > par->pwm_period);
> > + pwm_enable(par->pwm);
> >
> > -static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
> > -{
> > - int ret;
> > + dev_dbg(&par->client->dev, "Using PWM%d with a
> > %dns period.\n",
> > + par->pwm->pwm, par->pwm_period);
> > + };
> >
> > /* Set initial contrast */
> > ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CONTRAST);
> > - ret = ret & ssd1307fb_write_cmd(par->client, 0x7f);
> > if (ret < 0)
> > return ret;
> >
> > - /* Set COM direction */
> > - ret = ssd1307fb_write_cmd(par->client, 0xc8);
> > + ret = ssd1307fb_write_cmd(par->client, par->contrast);
> > if (ret < 0)
> > return ret;
> >
> > /* Set segment re-map */
> > - ret = ssd1307fb_write_cmd(par->client,
> > SSD1307FB_SEG_REMAP_ON);
> > + if (par->seg_remap) {
> > + ret = ssd1307fb_write_cmd(par->client,
> > SSD1307FB_SEG_REMAP_ON);
> > + if (ret < 0)
> > + return ret;
> > + };
> > +
> > + /* Set COM direction */
> > + com_invdir = 0xc0 | (par->com_invdir & 0xf) << 3;
> > + ret = ssd1307fb_write_cmd(par->client, com_invdir);
> > if (ret < 0)
> > return ret;
> >
> > /* Set multiplex ratio value */
> > ret = ssd1307fb_write_cmd(par->client,
> > SSD1307FB_SET_MULTIPLEX_RATIO);
> > - ret = ret & ssd1307fb_write_cmd(par->client, par->height -
> > 1);
> > + if (ret < 0)
> > + return ret;
> > +
> > + ret = ssd1307fb_write_cmd(par->client, par->height - 1);
> > if (ret < 0)
> > return ret;
> >
> > /* set display offset value */
> > ret = ssd1307fb_write_cmd(par->client,
> > SSD1307FB_SET_DISPLAY_OFFSET);
> > - ret = ssd1307fb_write_cmd(par->client, 0x20);
> > + if (ret < 0)
> > + return ret;
> > +
> > + ret = ssd1307fb_write_cmd(par->client, par->offset);
> > if (ret < 0)
> > return ret;
> >
> > /* Set clock frequency */
> > + dclk = (par->dclk_div & 0xf) | (par->dclk_frq & 0xf) << 4;
> > ret = ssd1307fb_write_cmd(par->client,
> > SSD1307FB_SET_CLOCK_FREQ);
> > - ret = ret & ssd1307fb_write_cmd(par->client, 0xf0);
> > if (ret < 0)
> > return ret;
> >
> > - /* Set precharge period in number of ticks from the
> > internal clock */
> > + ret = ssd1307fb_write_cmd(par->client, dclk);
> > + if (ret < 0)
> > + return ret;
> > +
> > + /* Set precharge period in number of ticks from the
> > internal clock*/
> > + precharge = (par->prechargep1 & 0xf) | (par->prechargep2 &
> > 0xf) << 4; ret = ssd1307fb_write_cmd(par->client,
> > SSD1307FB_SET_PRECHARGE_PERIOD);
> > - ret = ret & ssd1307fb_write_cmd(par->client, 0x22);
> > + if (ret < 0)
> > + return ret;
> > +
> > + ret = ssd1307fb_write_cmd(par->client, precharge);
> > if (ret < 0)
> > return ret;
> >
> > /* Set COM pins configuration */
> > + compins = 0x02 | (par->com_alt & 0x1) << 4
> > + | (par->com_lrremap & 0x1) << 5;
> > ret = ssd1307fb_write_cmd(par->client,
> > SSD1307FB_SET_COM_PINS_CONFIG);
> > - ret = ret & ssd1307fb_write_cmd(par->client, 0x22);
> > + if (ret < 0)
> > + return ret;
> > +
> > + ret = ssd1307fb_write_cmd(par->client, compins);
> > if (ret < 0)
> > return ret;
> >
> > /* Set VCOMH */
> > ret = ssd1307fb_write_cmd(par->client,
> > SSD1307FB_SET_VCOMH);
> > - ret = ret & ssd1307fb_write_cmd(par->client, 0x49);
> > if (ret < 0)
> > return ret;
> >
> > - /* Turn on the DC-DC Charge Pump */
> > - ret = ssd1307fb_write_cmd(par->client,
> > SSD1307FB_CHARGE_PUMP);
> > - ret = ret & ssd1307fb_write_cmd(par->client, 0x14);
> > + ret = ssd1307fb_write_cmd(par->client, par->vcomh);
> > if (ret < 0)
> > return ret;
> >
> > + if (par->device_info->device_id == DEVID_SSD1306) {
> > + /* Turn on the DC-DC Charge Pump */
> > + ret = ssd1307fb_write_cmd(par->client,
> > SSD1307FB_CHARGE_PUMP);
> > + if (ret < 0)
> > + return ret;
> > +
> > + ret = ssd1307fb_write_cmd(par->client, 0x14);
> > + if (ret < 0)
> > + return ret;
> > + };
> > +
> > /* Switch to horizontal addressing mode */
> > ret = ssd1307fb_write_cmd(par->client,
> > SSD1307FB_SET_ADDRESS_MODE);
> > - ret = ret & ssd1307fb_write_cmd(par->client,
> > + if (ret < 0)
> > + return ret;
> > +
> > + ret = ssd1307fb_write_cmd(par->client,
> > SSD1307FB_SET_ADDRESS_MODE_HORIZONTAL);
> > if (ret < 0)
> > return ret;
> >
> > + /* Set column range */
> > ret = ssd1307fb_write_cmd(par->client,
> > SSD1307FB_SET_COL_RANGE);
> > - ret = ret & ssd1307fb_write_cmd(par->client, 0x0);
> > - ret = ret & ssd1307fb_write_cmd(par->client, par->width -
> > 1); if (ret < 0)
> > return ret;
> >
> > + ret = ssd1307fb_write_cmd(par->client, 0x0);
> > + if (ret < 0)
> > + return ret;
> > +
> > + ret = ssd1307fb_write_cmd(par->client, par->width - 1);
> > + if (ret < 0)
> > + return ret;
> > +
> > + /* Set page range */
> > ret = ssd1307fb_write_cmd(par->client,
> > SSD1307FB_SET_PAGE_RANGE);
> > - ret = ret & ssd1307fb_write_cmd(par->client, 0x0);
> > - ret = ret & ssd1307fb_write_cmd(par->client,
> > + if (ret < 0)
> > + return ret;
> > +
> > + ret = ssd1307fb_write_cmd(par->client, 0x0);
> > + if (ret < 0)
> > + return ret;
> > +
> > + ret = ssd1307fb_write_cmd(par->client,
> > par->page_offset +
> > (par->height / 8) - 1); if (ret < 0)
> > return ret;
> > @@ -387,18 +427,28 @@ static int ssd1307fb_ssd1306_init(struct
> > ssd1307fb_par *par) return 0;
> > }
> >
> > -static struct ssd1307fb_ops ssd1307fb_ssd1306_ops = {
> > - .init = ssd1307fb_ssd1306_init,
> > +static struct ssd1307fb_deviceinfo ssd1307fb_ssd1306_deviceinfo = {
> > + .device_id = DEVID_SSD1306,
> > + .default_vcomh = 0x20,
> > + .default_dclk_div = 0,
> > + .default_dclk_frq = 8,
> > +};
> > +
> > +static struct ssd1307fb_deviceinfo ssd1307fb_ssd1307_deviceinfo = {
> > + .device_id = DEVID_SSD1307,
> > + .default_vcomh = 0x20,
> > + .default_dclk_div = 1,
> > + .default_dclk_frq = 12,
> > };
> >
> > static const struct of_device_id ssd1307fb_of_match[] = {
> > {
> > .compatible = "solomon,ssd1306fb-i2c",
> > - .data = (void *)&ssd1307fb_ssd1306_ops,
> > + .data = (void *)&ssd1307fb_ssd1306_deviceinfo,
> > },
> > {
> > .compatible = "solomon,ssd1307fb-i2c",
> > - .data = (void *)&ssd1307fb_ssd1307_ops,
> > + .data = (void *)&ssd1307fb_ssd1307_deviceinfo,
> > },
> > {},
> > };
> > @@ -429,8 +479,8 @@ static int ssd1307fb_probe(struct i2c_client
> > *client, par->info = info;
> > par->client = client;
> >
> > - par->ops = (struct ssd1307fb_ops
> > *)of_match_device(ssd1307fb_of_match,
> > -
> > &client->dev)->data;
> > + par->device_info = (struct ssd1307fb_deviceinfo
> > *)of_match_device(
> > + ssd1307fb_of_match, &client->dev)->data;
> >
> > par->reset = of_get_named_gpio(client->dev.of_node,
> > "reset-gpios", 0);
> > @@ -449,8 +499,40 @@ static int ssd1307fb_probe(struct i2c_client
> > *client, par->page_offset = 1;
> >
> > vmem_size = par->width * par->height / 8;
> > + if (of_property_read_u32(node, "solomon,segment-remap",
> > &par->seg_remap))
> > + par->seg_remap = 0;
> > +
> > + if (of_property_read_u32(node, "solomon,offset",
> > &par->offset))
> > + par->offset = 0;
> > +
> > + if (of_property_read_u32(node, "solomon,contrast",
> > &par->contrast))
> > + par->contrast = 128;
> > +
> > + if (of_property_read_u32(node, "solomon,prechargep1",
> > &par->prechargep1))
> > + par->prechargep1 = 2;
> > +
> > + if (of_property_read_u32(node, "solomon,prechargep2",
> > &par->prechargep2))
> > + par->prechargep2 = 2;
> > +
> > + if (of_property_read_u32(node, "solomon,com-alt",
> > &par->com_alt))
> > + par->com_alt = 1;
> > +
> > + if (of_property_read_u32(node, "solomon,com-lrremap",
> > &par->com_lrremap))
> > + par->com_lrremap = 0;
> > +
> > + if (of_property_read_u32(node, "solomon,com-invdir",
> > &par->com_invdir))
> > + par->com_invdir = 0;
> > +
> > + if (of_property_read_u32(node, "solomon,vcomh",
> > &par->vcomh))
> > + par->vcomh = par->device_info->default_vcomh;
> >
> > vmem = devm_kzalloc(&client->dev, vmem_size, GFP_KERNEL);
> > + if (of_property_read_u32(node, "solomon,dclk-div",
> > &par->dclk_div))
> > + par->dclk_div = par->device_info->default_dclk_div;
> > +
> > + if (of_property_read_u32(node, "solomon,dclk-frq",
> > &par->dclk_frq))
> > + par->dclk_frq =
> > par->device_info->default_dclk_frq; +
> > if (!vmem) {
> > dev_err(&client->dev, "Couldn't allocate graphical
> > memory.\n"); ret = -ENOMEM;
> > @@ -499,11 +581,9 @@ static int ssd1307fb_probe(struct i2c_client
> > *client, gpio_set_value(par->reset, 1);
> > udelay(4);
> >
> > - if (par->ops->init) {
> > - ret = par->ops->init(par);
> > - if (ret)
> > - goto reset_oled_error;
> > - }
> > + ret = ssd1307fb_init(par);
> > + if (ret)
> > + goto reset_oled_error;
> >
> > ret = register_framebuffer(info);
> > if (ret) {
> > @@ -516,8 +596,10 @@ static int ssd1307fb_probe(struct i2c_client
> > *client, return 0;
> >
> > panel_init_error:
> > - if (par->ops->remove)
> > - par->ops->remove(par);
> > + if (par->device_info->device_id == DEVID_SSD1307) {
> > + pwm_disable(par->pwm);
> > + pwm_put(par->pwm);
> > + };
> > reset_oled_error:
> > fb_deferred_io_cleanup(info);
> > fb_alloc_error:
> > @@ -531,11 +613,12 @@ static int ssd1307fb_remove(struct i2c_client
> > *client) struct ssd1307fb_par *par = info->par;
> >
> > unregister_framebuffer(info);
> > - if (par->ops->remove)
> > - par->ops->remove(par);
> > + if (par->device_info->device_id == DEVID_SSD1307) {
> > + pwm_disable(par->pwm);
> > + pwm_put(par->pwm);
> > + };
> > fb_deferred_io_cleanup(info);
> > framebuffer_release(info);
> > -
> > return 0;
> > }
> >
> > --
> > 2.1.1
> >
>

2015-02-07 15:25:21

by Noralf Trønnes

[permalink] [raw]
Subject: Re: [PATCH 2/8] fbdev: ssd1307fb: Unify init code and make controller configurable from device tree

Hi,

Den 07.02.2015 15:59, skrev Thomas Niederprüm:
> Am Sat, 7 Feb 2015 11:42:25 +0100
> schrieb Maxime Ripard <[email protected]>:
>
>> Hi,
>>
>> On Fri, Feb 06, 2015 at 11:28:08PM +0100, [email protected]
>> wrote:
>>> From: Thomas Niederprüm <[email protected]>
>>>
>>> This patches unifies the init code for the ssd130X chips and
>>> adds device tree bindings to describe the hardware configuration
>>> of the used controller. This gets rid of the magic bit values
>>> used in the init code so far.
>>>
>>> Signed-off-by: Thomas Niederprüm <[email protected]>
>>> ---
>>> .../devicetree/bindings/video/ssd1307fb.txt | 11 +
>>> drivers/video/fbdev/ssd1307fb.c | 243
>>> ++++++++++++++------- 2 files changed, 174 insertions(+), 80
>>> deletions(-)
>>>
>>> diff --git a/Documentation/devicetree/bindings/video/ssd1307fb.txt
>>> b/Documentation/devicetree/bindings/video/ssd1307fb.txt index
>>> 7a12542..1230f68 100644 ---
>>> a/Documentation/devicetree/bindings/video/ssd1307fb.txt +++
>>> b/Documentation/devicetree/bindings/video/ssd1307fb.txt @@ -15,6
>>> +15,17 @@ Required properties:
>>> Optional properties:
>>> - reset-active-low: Is the reset gpio is active on physical low?
>>> + - solomon,segment-remap: Invert the order of data column to
>>> segment mapping
>>> + - solomon,offset: Map the display start line to one of COM0 -
>>> COM63
>>> + - solomon,contrast: Set initial contrast of the display
>>> + - solomon,prechargep1: Set the duration of the precharge period
>>> phase1
>>> + - solomon,prechargep2: Set the duration of the precharge period
>>> phase2
>>> + - solomon,com-alt: Enable/disable alternate COM pin configuration
>>> + - solomon,com-lrremap: Enable/disable left-right remap of COM
>>> pins
>>> + - solomon,com-invdir: Invert COM scan direction
>>> + - solomon,vcomh: Set VCOMH regulator voltage
>>> + - solomon,dclk-div: Set display clock divider
>>> + - solomon,dclk-frq: Set display clock frequency
>> I'm sorry, but this is the wrong approach, for at least two reasons:
>> you broke all existing users of that driver, which is a clear no-go,
> Unfortunately this is true. The problem is that the SSD130X controllers
> allow for a very versatile wiring of the display to the controller.
> It's over to the manufacturer of the OLED module (disp+controller) to
> decide how it's actually wired and during device initialization the
> driver has to take care to configure the SSD130X controller according
> to that wiring. If the driver fails to do so you will end up having
> your display showing garbage. Unfortunately the current sate of the
> initialization code of the ssd1307fb driver is not very flexible in that
> respect. Taking a look at the initialization code for the ssd1306 shows
> that it was written with one very special display module in mind. Most
> of the magic bit values set there are non-default values according to
> the datasheet. The result is that the driver works with that one
> particular display module but many other (differently wired) display
> modules using a ssd1306 controller won't work without changing the
> hardcoded magic bit values.
>
> My idea here was to set all configuration to the default values (as
> given in the datasheet) unless it is overwritten by DT. Of course,
> without a change in DT, this breaks the driver for all existing users.
> The only alternative would be to set the current values as default.
> Somehow this feels wrong to me as these values look arbitrary when you
> don't know what exact display module they were set for. But if you
> insist, I will change the default values.
>
>> and the DT itself should not contain any direct mapping of the
>> registers.
>>
> I think I don't get what you mean here. Is it because I do no sanity
> checks of the numbers set in DT? I was just looking for a way to hand
> over the information about the wiring of display to the driver. How
> would you propose to solve this?

I have the exact same challenge with the staging/fbtft drivers.
I have asked about this on the DT list a couple of days ago, but no
answer yet:

Can I do register initialization from Device Tree?
http://www.spinics.net/lists/devicetree/msg68174.html


Regards,
Noralf Trønnes

2015-02-07 15:33:09

by Thomas Niederprüm

[permalink] [raw]
Subject: Re: [PATCH 4/8] fbdev: ssd1307fb: Use vmalloc to allocate video memory.

Am Sat, 7 Feb 2015 12:18:21 +0100
schrieb Maxime Ripard <[email protected]>:

> Hi,
>
> On Fri, Feb 06, 2015 at 11:28:10PM +0100, [email protected]
> wrote:
> > From: Thomas Niederprüm <[email protected]>
> >
> > It makes sense to use vmalloc to allocate the video buffer since it
> > has to be page aligned memory for using it with mmap.
>
> Please wrap your commit log at 80 chars.

I'll try to do so in future, sorry for that.

>
> It looks like there's numerous fbdev drivers using this (especially
> since you copy pasted that code, without mentionning it).

Yes, I should have mentioned that in the commit message. As
implicitly indicated in the cover letter the rvmalloc() and rvfree() are
copy pasted from the vfb driver. Honestly, I didn't give this one too
much thought. It seemed a viable solution to the mmap problem. For a
bit more history on that, see my comment below.

>
> That should be turned into an allocator so that drivers all get this
> right.
>
> > Also deffered io seems buggy in combination with kmalloc'ed memory
> > (crash on unloading the module).
>
> And maybe that's the real issue to fix.

The problem is solved by using vmalloc ;)

>
> > Signed-off-by: Thomas Niederprüm <[email protected]>
> > ---
> > drivers/video/fbdev/ssd1307fb.c | 43
> > +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41
> > insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/video/fbdev/ssd1307fb.c
> > b/drivers/video/fbdev/ssd1307fb.c index 01cfb46..945ded9 100644
> > --- a/drivers/video/fbdev/ssd1307fb.c
> > +++ b/drivers/video/fbdev/ssd1307fb.c
> > @@ -11,6 +11,7 @@
> > #include <linux/i2c.h>
> > #include <linux/fb.h>
> > #include <linux/uaccess.h>
> > +#include <linux/vmalloc.h>
> > #include <linux/of_device.h>
> > #include <linux/of_gpio.h>
> > #include <linux/pwm.h>
> > @@ -93,6 +94,43 @@ static struct fb_var_screeninfo ssd1307fb_var = {
> > .bits_per_pixel = 1,
> > };
> >
> > +static void *rvmalloc(unsigned long size)
> > +{
> > + void *mem;
> > + unsigned long adr;
> > +
> > + size = PAGE_ALIGN(size);
>
> Isn't vmalloc already taking care of that?
>
> > + mem = vmalloc_32(size);
>
> Why the 32 bits variant?
>
> > + if (!mem)
> > + return NULL;
> > +
> > + memset(mem, 0, size); /* Clear the ram out, no junk to the
> > user */
> > + adr = (unsigned long) mem;
> > + while (size > 0) {
> > + SetPageReserved(vmalloc_to_page((void *)adr));
>
> I'm not really sure it makes sense to mark all pages reserved if we're
> not even sure we're going to use mmap.
>
> And why do you need to mark these pages reserved in the first place?
>
> > + adr += PAGE_SIZE;
> > + size -= PAGE_SIZE;
> > + }
> > +
> > + return mem;
> > +}
> > +
> > +static void rvfree(void *mem, unsigned long size)
> > +{
> > + unsigned long adr;
> > +
> > + if (!mem)
> > + return;
> > +
> > + adr = (unsigned long) mem;
> > + while ((long) size > 0) {
> > + ClearPageReserved(vmalloc_to_page((void *)adr));
> > + adr += PAGE_SIZE;
> > + size -= PAGE_SIZE;
> > + }
> > + vfree(mem);
> > +}
> > +
> > static struct ssd1307fb_array *ssd1307fb_alloc_array(u32 len, u8
> > type) {
> > struct ssd1307fb_array *array;
> > @@ -538,13 +576,13 @@ static int ssd1307fb_probe(struct i2c_client
> > *client, if (of_property_read_u32(node, "solomon,vcomh",
> > &par->vcomh)) par->vcomh = par->device_info->default_vcomh;
> >
> > - vmem = devm_kzalloc(&client->dev, vmem_size, GFP_KERNEL);
> > if (of_property_read_u32(node, "solomon,dclk-div",
> > &par->dclk_div)) par->dclk_div = par->device_info->default_dclk_div;
> >
> > if (of_property_read_u32(node, "solomon,dclk-frq",
> > &par->dclk_frq)) par->dclk_frq =
> > par->device_info->default_dclk_frq;
> > + vmem = rvmalloc(vmem_size);
> > if (!vmem) {
> > dev_err(&client->dev, "Couldn't allocate graphical
> > memory.\n"); ret = -ENOMEM;
> > @@ -570,7 +608,7 @@ static int ssd1307fb_probe(struct i2c_client
> > *client, info->var.blue.offset = 0;
> >
> > info->screen_base = (u8 __force __iomem *)vmem;
> > - info->fix.smem_start = (unsigned long)vmem;
> > + info->fix.smem_start = __pa(vmem);
>
> Why are you changing from virtual to physical address here?

Oh, good catch! This is still a residual of my attempts to get this
working with kmalloc'ed memory. In the current state the driver is
presenting a completely wrong memory address upon mmap. As reported in
[0] info->fix.smem_start has to hold the physical address of the video
memory if it was allocated using kmalloc. Correcting this let me run
into the problem that the kmalloc'ed memory was not page aligned but,
the memory address handed to userspace mmap was aligned to the next
full page, resulting in an inaccessable display region. At that point I
just copied the vmalloc approach from the vfb driver.

[0] http://stackoverflow.com/questions/22285151/kernel-panic-using-deferred-io-on-kmalloced-buffer

>
> Maxime
>

2015-02-07 16:02:31

by Thomas Niederprüm

[permalink] [raw]
Subject: Re: [PATCH 5/8] fbdev: ssd1307fb: Add module parameter bitsperpixel.

Am Sat, 7 Feb 2015 12:20:43 +0100
schrieb Maxime Ripard <[email protected]>:

> On Fri, Feb 06, 2015 at 11:28:11PM +0100, [email protected]
> wrote:
> > From: Thomas Niederprüm <[email protected]>
> >
> > This patch adds a module parameter 'bitsperpixel' to adjust the
> > colordepth of the framebuffer. All values >1 will result in memory
> > map of the requested color depth. However only the MSB of each
> > pixel will be sent to the device. The framebuffer identifies itself
> > as a grayscale display with the specified depth.
>
> I'm not sure this is the right thing to do.
>
> The bits per pixel for this display is rightfully defined, used and
> reported to the userspace, why would you want to change that?
>

You are right of course. The display is 1bpp and it reports to be 1
bpp. The problem is that there is almost no userspace library that can
handle 1 bit framebuffers correctly. So it is nice if the framebuffer
(optionally) can expose itself as 8 bits per pixel grayscale to the
userspace program. As an example this allows to run DirectFB on the
framebuffer, which is not possible out of the box for 1bpp.

Also note that if do not set the module parameter at load time
the framebuffer will be 1bpp. So you have to actively set that module
parameter to make the framebuffer pretend to be more than 1bpp.

In any case I don't cling to that patch, I just thought it was a nice
feature.

> Maxime
>

2015-02-07 16:09:39

by Thomas Niederprüm

[permalink] [raw]
Subject: Re: [PATCH 6/8] fbdev: ssd1307fb: Add module parameter to set update delay of the deffered io.

Am Sat, 7 Feb 2015 12:26:27 +0100
schrieb Maxime Ripard <[email protected]>:

> On Fri, Feb 06, 2015 at 11:28:12PM +0100, [email protected]
> wrote:
> > From: Thomas Niederprüm <[email protected]>
> >
> > This patch adds the module parameter "delaydivider" to set delay
> > for the deferred io. Effectively this is setting the refresh rate
> > for mmap access to the framebuffer. The delay for the deferred io
> > is HZ/delaydivider.
>
> So this is actually a refresh rate?
>
> Maybe you could expose it as such, and pass a frequency in Hz as an
> argument.

Good idea! I'll try to do it that way.

>
> Exposing the divider directly has some issues, since the bootloader
> that set the parameter won't know the HZ value, you'll end up with
> different rates for different configurations, without any way to do
> something about it.
>
> >
> > Signed-off-by: Thomas Niederprüm <[email protected]>
> > ---
> > drivers/video/fbdev/ssd1307fb.c | 7 ++++++-
> > 1 file changed, 6 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/video/fbdev/ssd1307fb.c
> > b/drivers/video/fbdev/ssd1307fb.c index 1d81877..b38315d 100644
> > --- a/drivers/video/fbdev/ssd1307fb.c
> > +++ b/drivers/video/fbdev/ssd1307fb.c
> > @@ -44,10 +44,14 @@
> > #define SSD1307FB_SET_VCOMH 0xdb
> >
> > #define BITSPERPIXEL 1
> > +#define DELAYDIVIDER 20
> >
> > static u_int bitsperpixel = BITSPERPIXEL;
> > module_param(bitsperpixel, uint, 0);
> >
> > +static u_int delaydivider = DELAYDIVIDER;
> > +module_param(delaydivider, uint, 0);
> > +
>
> You're breaking the existing behaviour.

True! I'll try to keep the existing behaviour when rewriting this to
use the refresh rate.

>
> > struct ssd1307fb_par;
> >
> > struct ssd1307fb_deviceinfo {
> > @@ -312,7 +316,7 @@ static void ssd1307fb_deferred_io(struct
> > fb_info *info, }
> >
> > static struct fb_deferred_io ssd1307fb_defio = {
> > - .delay = HZ,
> > + .delay = HZ/DELAYDIVIDER,
> > .deferred_io = ssd1307fb_deferred_io,
> > };
> >
> > @@ -601,6 +605,7 @@ static int ssd1307fb_probe(struct i2c_client
> > *client, info->fix = ssd1307fb_fix;
> > info->fix.line_length = par->width * bitsperpixel / 8;
> > info->fbdefio = &ssd1307fb_defio;
> > + info->fbdefio->delay = HZ/delaydivider;
>
> That won't work with multiple instances of the same driver
> unfortunately.

Could you please elaborate why? I'm not seeing it...

>
> >
> > info->var = ssd1307fb_var;
> > info->var.bits_per_pixel = bitsperpixel;
> > --
> > 2.1.1
> >
>

2015-02-07 16:40:12

by Thomas Niederprüm

[permalink] [raw]
Subject: Re: [PATCH 7/8] fbdev: ssd1307fb: Add sysfs handles to expose contrast and dim setting to userspace.

Am Sat, 7 Feb 2015 12:43:29 +0100
schrieb Maxime Ripard <[email protected]>:

> On Fri, Feb 06, 2015 at 11:28:13PM +0100, [email protected]
> wrote:
> > From: Thomas Niederprüm <[email protected]>
> >
> > This patch adds sysfs handles to enable userspace control over the
> > display contrast as well as the dim mode. The handles are available
> > as "contrast" and "dim" in the framebuffers sysfs domain.
> >
> > Signed-off-by: Thomas Niederprüm <[email protected]>
> > ---
> > drivers/video/fbdev/ssd1307fb.c | 88
> > ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 87
> > insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/video/fbdev/ssd1307fb.c
> > b/drivers/video/fbdev/ssd1307fb.c index b38315d..02931c7 100644
> > --- a/drivers/video/fbdev/ssd1307fb.c
> > +++ b/drivers/video/fbdev/ssd1307fb.c
> > @@ -33,6 +33,7 @@
> > #define SSD1307FB_CONTRAST 0x81
> > #define SSD1307FB_CHARGE_PUMP 0x8d
> > #define SSD1307FB_SEG_REMAP_ON 0xa1
> > +#define SSD1307FB_DISPLAY_DIM 0xac
> > #define SSD1307FB_DISPLAY_OFF 0xae
> > #define SSD1307FB_SET_MULTIPLEX_RATIO 0xa8
> > #define SSD1307FB_DISPLAY_ON 0xaf
> > @@ -43,6 +44,9 @@
> > #define SSD1307FB_SET_COM_PINS_CONFIG 0xda
> > #define SSD1307FB_SET_VCOMH 0xdb
> >
> > +#define MIN_CONTRAST 0
> > +#define MAX_CONTRAST 255
> > +
> > #define BITSPERPIXEL 1
> > #define DELAYDIVIDER 20
> >
> > @@ -69,6 +73,7 @@ struct ssd1307fb_par {
> > u32 dclk_div;
> > u32 dclk_frq;
> > struct ssd1307fb_deviceinfo *device_info;
> > + u32 dim;
> > struct i2c_client *client;
> > u32 height;
> > struct fb_info *info;
> > @@ -515,6 +520,79 @@ static const struct of_device_id
> > ssd1307fb_of_match[] = { };
> > MODULE_DEVICE_TABLE(of, ssd1307fb_of_match);
> >
> > +static ssize_t show_contrast(struct device *device,
> > + struct device_attribute *attr, char
> > *buf) +{
> > + struct fb_info *info = dev_get_drvdata(device);
> > + struct ssd1307fb_par *par = info->par;
> > +
> > + return snprintf(buf, PAGE_SIZE, "%d\n", par->contrast);
> > +}
> > +
> > +static ssize_t store_contrast(struct device *device,
> > + struct device_attribute *attr,
> > + const char *buf, size_t count)
> > +{
> > + struct fb_info *info = dev_get_drvdata(device);
> > + struct ssd1307fb_par *par = info->par;
> > + unsigned long contrastval;
> > + int ret;
> > +
> > + ret = kstrtoul(buf, 0, &contrastval);
> > + if (ret < 0)
> > + return ret;
> > +
> > + par->contrast = max(min(contrastval,
> > + (ulong)MAX_CONTRAST), (ulong)MIN_CONTRAST);
> > +
> > + ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CONTRAST);
> > + ret = ret & ssd1307fb_write_cmd(par->client,
> > par->contrast);
> > + if (ret < 0)
> > + return ret;
> > +
> > + return count;
> > +}
> > +
> > +
> > +static ssize_t show_dim(struct device *device,
> > + struct device_attribute *attr, char
> > *buf) +{
> > + struct fb_info *info = dev_get_drvdata(device);
> > + struct ssd1307fb_par *par = info->par;
> > +
> > + return snprintf(buf, PAGE_SIZE, "%d\n", par->dim);
> > +}
> > +
> > +static ssize_t store_dim(struct device *device,
> > + struct device_attribute *attr,
> > + const char *buf, size_t count)
> > +{
> > + struct fb_info *info = dev_get_drvdata(device);
> > + struct ssd1307fb_par *par = info->par;
> > + unsigned long dimval;
> > + int ret;
> > +
> > + ret = kstrtoul(buf, 0, &dimval);
> > + if (ret < 0)
> > + return ret;
> > +
> > + par->dim = max(min(dimval, (ulong)1), (ulong)0);
> > + if (par->dim)
> > + ret = ssd1307fb_write_cmd(par->client,
> > SSD1307FB_DISPLAY_DIM);
> > + else
> > + ret = ssd1307fb_write_cmd(par->client,
> > SSD1307FB_DISPLAY_ON);
> > + if (ret < 0)
> > + return ret;
> > +
> > + return count;
> > +}
> > +
> > +static struct device_attribute device_attrs[] = {
> > + __ATTR(contrast, S_IRUGO|S_IWUSR, show_contrast,
> > store_contrast),
> > + __ATTR(dim, S_IRUGO|S_IWUSR, show_dim, store_dim),
> > +
> > +};
> > +
>
> I would have thought this was something accessible through the
> framebuffer ioctl.
>
> Apparently it's not, at least for the contrast, so maybe it should be
> added there, instead of doing it for a single driver?

I think the contrast setting for an OLED display is much like the
backlight setting for LCD panel. Since there is also no ioctl to set
the backlight of an LCD I wonder if the contrast of an OLED should have
one.

>
> (oh, and btw, every sysfs file should be documented in
> Documentation/ABI)
>
> > static int ssd1307fb_probe(struct i2c_client *client,
> > const struct i2c_device_id *id)
> > {
> > @@ -523,7 +601,7 @@ static int ssd1307fb_probe(struct i2c_client
> > *client, u32 vmem_size;
> > struct ssd1307fb_par *par;
> > u8 *vmem;
> > - int ret;
> > + int ret, i;
> >
> > if (!node) {
> > dev_err(&client->dev, "No device tree data
> > found!\n"); @@ -650,6 +728,14 @@ static int ssd1307fb_probe(struct
> > i2c_client *client, goto reset_oled_error;
> >
> > ret = register_framebuffer(info);
> > +
> > + for (i = 0; i < ARRAY_SIZE(device_attrs); i++)
> > + ret = device_create_file(info->dev,
> > &device_attrs[i]); +
> > + if (ret) {
> > + dev_err(&client->dev, "Couldn't register sysfs
> > nodes\n");
> > + }
> > +
>
> sysfs_create_groups does pretty much that already.

I'll have a look at it.

>
> And don't forget to remove these files in the .remove()

Good point! :)

>
> > if (ret) {
> > dev_err(&client->dev, "Couldn't register the
> > framebuffer\n"); goto panel_init_error;
> > --
> > 2.1.1
> >
>
> Maxime
>

2015-02-07 16:46:57

by Thomas Niederprüm

[permalink] [raw]
Subject: Re: [PATCH 8/8] fbdev: ssd1307fb: Turn off display on driver unload.

Am Sat, 7 Feb 2015 12:45:34 +0100
schrieb Maxime Ripard <[email protected]>:

> On Fri, Feb 06, 2015 at 11:28:14PM +0100, [email protected]
> wrote:
> > From: Thomas Niederprüm <[email protected]>
> >
>
> A commit log is always nice :)

Will be added.

>
> > Signed-off-by: Thomas Niederprüm <[email protected]>
> > ---
> > drivers/video/fbdev/ssd1307fb.c | 5 +++++
> > 1 file changed, 5 insertions(+)
> >
> > diff --git a/drivers/video/fbdev/ssd1307fb.c
> > b/drivers/video/fbdev/ssd1307fb.c index 02931c7..be91dfc 100644
> > --- a/drivers/video/fbdev/ssd1307fb.c
> > +++ b/drivers/video/fbdev/ssd1307fb.c
> > @@ -762,6 +762,11 @@ static int ssd1307fb_remove(struct i2c_client
> > *client) {
> > struct fb_info *info = i2c_get_clientdata(client);
> > struct ssd1307fb_par *par = info->par;
> > + int ret = 0;
> > +
> > + ret = ssd1307fb_write_cmd(par->client,
> > SSD1307FB_DISPLAY_OFF);
> > + if (ret < 0)
> > + return ret;
>
> I don't think we really care about the return value here.
>
> It might be even worse actually, since you'll end up in a intermediate
> state, where you won't have freed everything, but your remove method
> has been called still.

I agree. I will remove the check for the return statement.

>
> Maxime
>

2015-02-09 08:55:40

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH 7/8] fbdev: ssd1307fb: Add sysfs handles to expose contrast and dim setting to userspace.

On Sat, Feb 07, 2015 at 05:42:44PM +0100, Thomas Niederpr?m wrote:
> > > +static struct device_attribute device_attrs[] = {
> > > + __ATTR(contrast, S_IRUGO|S_IWUSR, show_contrast,
> > > store_contrast),
> > > + __ATTR(dim, S_IRUGO|S_IWUSR, show_dim, store_dim),
> > > +
> > > +};
> > > +
> >
> > I would have thought this was something accessible through the
> > framebuffer ioctl.
> >
> > Apparently it's not, at least for the contrast, so maybe it should be
> > added there, instead of doing it for a single driver?
>
> I think the contrast setting for an OLED display is much like the
> backlight setting for LCD panel. Since there is also no ioctl to set
> the backlight of an LCD I wonder if the contrast of an OLED should have
> one.

It's too much of framebuffer interface debate for me here. Tomi?

Maxime

--
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com


Attachments:
(No filename) (926.00 B)
signature.asc (819.00 B)
Digital signature
Download all attachments

2015-02-09 09:06:16

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH 6/8] fbdev: ssd1307fb: Add module parameter to set update delay of the deffered io.

On Sat, Feb 07, 2015 at 05:12:11PM +0100, Thomas Niederpr?m wrote:
> Am Sat, 7 Feb 2015 12:26:27 +0100
> schrieb Maxime Ripard <[email protected]>:
>
> > On Fri, Feb 06, 2015 at 11:28:12PM +0100, [email protected]
> > wrote:
> > > From: Thomas Niederpr?m <[email protected]>
> > >
> > > This patch adds the module parameter "delaydivider" to set delay
> > > for the deferred io. Effectively this is setting the refresh rate
> > > for mmap access to the framebuffer. The delay for the deferred io
> > > is HZ/delaydivider.
> >
> > So this is actually a refresh rate?
> >
> > Maybe you could expose it as such, and pass a frequency in Hz as an
> > argument.
>
> Good idea! I'll try to do it that way.
>
> >
> > Exposing the divider directly has some issues, since the bootloader
> > that set the parameter won't know the HZ value, you'll end up with
> > different rates for different configurations, without any way to do
> > something about it.
> >
> > >
> > > Signed-off-by: Thomas Niederpr?m <[email protected]>
> > > ---
> > > drivers/video/fbdev/ssd1307fb.c | 7 ++++++-
> > > 1 file changed, 6 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/drivers/video/fbdev/ssd1307fb.c
> > > b/drivers/video/fbdev/ssd1307fb.c index 1d81877..b38315d 100644
> > > --- a/drivers/video/fbdev/ssd1307fb.c
> > > +++ b/drivers/video/fbdev/ssd1307fb.c
> > > @@ -44,10 +44,14 @@
> > > #define SSD1307FB_SET_VCOMH 0xdb
> > >
> > > #define BITSPERPIXEL 1
> > > +#define DELAYDIVIDER 20
> > >
> > > static u_int bitsperpixel = BITSPERPIXEL;
> > > module_param(bitsperpixel, uint, 0);
> > >
> > > +static u_int delaydivider = DELAYDIVIDER;
> > > +module_param(delaydivider, uint, 0);
> > > +
> >
> > You're breaking the existing behaviour.
>
> True! I'll try to keep the existing behaviour when rewriting this to
> use the refresh rate.
>
> >
> > > struct ssd1307fb_par;
> > >
> > > struct ssd1307fb_deviceinfo {
> > > @@ -312,7 +316,7 @@ static void ssd1307fb_deferred_io(struct
> > > fb_info *info, }
> > >
> > > static struct fb_deferred_io ssd1307fb_defio = {
> > > - .delay = HZ,
> > > + .delay = HZ/DELAYDIVIDER,
> > > .deferred_io = ssd1307fb_deferred_io,
> > > };
> > >
> > > @@ -601,6 +605,7 @@ static int ssd1307fb_probe(struct i2c_client
> > > *client, info->fix = ssd1307fb_fix;
> > > info->fix.line_length = par->width * bitsperpixel / 8;
> > > info->fbdefio = &ssd1307fb_defio;
> > > + info->fbdefio->delay = HZ/delaydivider;
> >
> > That won't work with multiple instances of the same driver
> > unfortunately.
>
> Could you please elaborate why? I'm not seeing it...

On a general basis, because the structure is shared by all the
instances of the driver, so it's usually not such a good idea to mix
the static declaration of the structure and the dynamic one.

From a more fundamental point of view, because this parameter will
most likely be different from one instance to another?

Maxime

--
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com


Attachments:
(No filename) (3.01 kB)
signature.asc (819.00 B)
Digital signature
Download all attachments

2015-02-09 10:40:13

by Thomas Niederprüm

[permalink] [raw]
Subject: Re: [PATCH 2/8] fbdev: ssd1307fb: Unify init code and make controller configurable from device tree

Am Samstag, den 07.02.2015, 16:19 +0100 schrieb Noralf Trønnes:
> Hi,
>
> Den 07.02.2015 15:59, skrev Thomas Niederprüm:
> > Am Sat, 7 Feb 2015 11:42:25 +0100
> > schrieb Maxime Ripard <[email protected]>:
> >
> >> Hi,
> >>
> >> On Fri, Feb 06, 2015 at 11:28:08PM +0100, [email protected]
> >> wrote:
> >>> From: Thomas Niederprüm <[email protected]>
> >>>
> >>> This patches unifies the init code for the ssd130X chips and
> >>> adds device tree bindings to describe the hardware configuration
> >>> of the used controller. This gets rid of the magic bit values
> >>> used in the init code so far.
> >>>
> >>> Signed-off-by: Thomas Niederprüm <[email protected]>
> >>> ---
> >>> .../devicetree/bindings/video/ssd1307fb.txt | 11 +
> >>> drivers/video/fbdev/ssd1307fb.c | 243
> >>> ++++++++++++++------- 2 files changed, 174 insertions(+), 80
> >>> deletions(-)
> >>>
> >>> diff --git a/Documentation/devicetree/bindings/video/ssd1307fb.txt
> >>> b/Documentation/devicetree/bindings/video/ssd1307fb.txt index
> >>> 7a12542..1230f68 100644 ---
> >>> a/Documentation/devicetree/bindings/video/ssd1307fb.txt +++
> >>> b/Documentation/devicetree/bindings/video/ssd1307fb.txt @@ -15,6
> >>> +15,17 @@ Required properties:
> >>> Optional properties:
> >>> - reset-active-low: Is the reset gpio is active on physical low?
> >>> + - solomon,segment-remap: Invert the order of data column to
> >>> segment mapping
> >>> + - solomon,offset: Map the display start line to one of COM0 -
> >>> COM63
> >>> + - solomon,contrast: Set initial contrast of the display
> >>> + - solomon,prechargep1: Set the duration of the precharge period
> >>> phase1
> >>> + - solomon,prechargep2: Set the duration of the precharge period
> >>> phase2
> >>> + - solomon,com-alt: Enable/disable alternate COM pin configuration
> >>> + - solomon,com-lrremap: Enable/disable left-right remap of COM
> >>> pins
> >>> + - solomon,com-invdir: Invert COM scan direction
> >>> + - solomon,vcomh: Set VCOMH regulator voltage
> >>> + - solomon,dclk-div: Set display clock divider
> >>> + - solomon,dclk-frq: Set display clock frequency
> >> I'm sorry, but this is the wrong approach, for at least two reasons:
> >> you broke all existing users of that driver, which is a clear no-go,
> > Unfortunately this is true. The problem is that the SSD130X controllers
> > allow for a very versatile wiring of the display to the controller.
> > It's over to the manufacturer of the OLED module (disp+controller) to
> > decide how it's actually wired and during device initialization the
> > driver has to take care to configure the SSD130X controller according
> > to that wiring. If the driver fails to do so you will end up having
> > your display showing garbage. Unfortunately the current sate of the
> > initialization code of the ssd1307fb driver is not very flexible in that
> > respect. Taking a look at the initialization code for the ssd1306 shows
> > that it was written with one very special display module in mind. Most
> > of the magic bit values set there are non-default values according to
> > the datasheet. The result is that the driver works with that one
> > particular display module but many other (differently wired) display
> > modules using a ssd1306 controller won't work without changing the
> > hardcoded magic bit values.
> >
> > My idea here was to set all configuration to the default values (as
> > given in the datasheet) unless it is overwritten by DT. Of course,
> > without a change in DT, this breaks the driver for all existing users.
> > The only alternative would be to set the current values as default.
> > Somehow this feels wrong to me as these values look arbitrary when you
> > don't know what exact display module they were set for. But if you
> > insist, I will change the default values.
> >
> >> and the DT itself should not contain any direct mapping of the
> >> registers.
> >>
> > I think I don't get what you mean here. Is it because I do no sanity
> > checks of the numbers set in DT? I was just looking for a way to hand
> > over the information about the wiring of display to the driver. How
> > would you propose to solve this?
>
> I have the exact same challenge with the staging/fbtft drivers.

Even though we are facing the same problem here, yours is much harder
than mine, since I have much more knowledge about the controller.
Therefor I have no need to completely expose the registers for
initialization. I just define all configurable options that the
controller has in DT and let the driver take care to write the
corresponding register values during initialization. Of course this
needs the exact knowledge of the configuration options of the controller
as well as register addresses of these options in the driver. So I have
the fear that this approach does not scale for a driver handling
different controllers.

> I have asked about this on the DT list a couple of days ago, but no
> answer yet:
>
> Can I do register initialization from Device Tree?
> http://www.spinics.net/lists/devicetree/msg68174.html
>
>
> Regards,
> Noralf Trønnes
>

--
Thomas Niederprüm
TU Kaiserslautern
FB Physik (AG Ott)
Erwin-Schrödinger-Str. 46/431
67663 Kaiserslautern
Tel.: 0631 205 2387
Fax: 0631 205 3906

2015-02-12 15:15:07

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH 4/8] fbdev: ssd1307fb: Use vmalloc to allocate video memory.

On Sat, Feb 07, 2015 at 04:35:41PM +0100, Thomas Niederpr?m wrote:
> Am Sat, 7 Feb 2015 12:18:21 +0100
> schrieb Maxime Ripard <[email protected]>:
>
> > Hi,
> >
> > On Fri, Feb 06, 2015 at 11:28:10PM +0100, [email protected]
> > wrote:
> > > From: Thomas Niederpr?m <[email protected]>
> > >
> > > It makes sense to use vmalloc to allocate the video buffer since it
> > > has to be page aligned memory for using it with mmap.
> >
> > Please wrap your commit log at 80 chars.
>
> I'll try to do so in future, sorry for that.
>
> >
> > It looks like there's numerous fbdev drivers using this (especially
> > since you copy pasted that code, without mentionning it).
>
> Yes, I should have mentioned that in the commit message. As
> implicitly indicated in the cover letter the rvmalloc() and rvfree() are
> copy pasted from the vfb driver. Honestly, I didn't give this one too
> much thought. It seemed a viable solution to the mmap problem. For a
> bit more history on that, see my comment below.
>
> >
> > That should be turned into an allocator so that drivers all get this
> > right.
> >
> > > Also deffered io seems buggy in combination with kmalloc'ed memory
> > > (crash on unloading the module).
> >
> > And maybe that's the real issue to fix.
>
> The problem is solved by using vmalloc ;)

Yep, but why do you need to mark the reserved pages?

...

> > > @@ -570,7 +608,7 @@ static int ssd1307fb_probe(struct i2c_client
> > > *client, info->var.blue.offset = 0;
> > >
> > > info->screen_base = (u8 __force __iomem *)vmem;
> > > - info->fix.smem_start = (unsigned long)vmem;
> > > + info->fix.smem_start = __pa(vmem);
> >
> > Why are you changing from virtual to physical address here?
>
> Oh, good catch! This is still a residual of my attempts to get this
> working with kmalloc'ed memory. In the current state the driver is
> presenting a completely wrong memory address upon mmap. As reported in
> [0] info->fix.smem_start has to hold the physical address of the video
> memory if it was allocated using kmalloc. Correcting this let me run
> into the problem that the kmalloc'ed memory was not page aligned but,
> the memory address handed to userspace mmap was aligned to the next
> full page, resulting in an inaccessable display region. At that point I
> just copied the vmalloc approach from the vfb driver.
>
> [0] http://stackoverflow.com/questions/22285151/kernel-panic-using-deferred-io-on-kmalloced-buffer

And the documentation of fb_fix_screeninfo indeed state that this
should be the physical address:
http://lxr.free-electrons.com/source/include/uapi/linux/fb.h#L158

Could you make that a separate patch?

Thanks,
Maxime

--
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com


Attachments:
(No filename) (2.74 kB)
signature.asc (819.00 B)
Digital signature
Download all attachments

2015-02-12 16:45:20

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH 2/8] fbdev: ssd1307fb: Unify init code and make controller configurable from device tree

On Sat, Feb 07, 2015 at 03:59:33PM +0100, Thomas Niederpr?m wrote:
> Am Sat, 7 Feb 2015 11:42:25 +0100
> schrieb Maxime Ripard <[email protected]>:
>
> > Hi,
> >
> > On Fri, Feb 06, 2015 at 11:28:08PM +0100, [email protected]
> > wrote:
> > > From: Thomas Niederpr?m <[email protected]>
> > >
> > > This patches unifies the init code for the ssd130X chips and
> > > adds device tree bindings to describe the hardware configuration
> > > of the used controller. This gets rid of the magic bit values
> > > used in the init code so far.
> > >
> > > Signed-off-by: Thomas Niederpr?m <[email protected]>
> > > ---
> > > .../devicetree/bindings/video/ssd1307fb.txt | 11 +
> > > drivers/video/fbdev/ssd1307fb.c | 243
> > > ++++++++++++++------- 2 files changed, 174 insertions(+), 80
> > > deletions(-)
> > >
> > > diff --git a/Documentation/devicetree/bindings/video/ssd1307fb.txt
> > > b/Documentation/devicetree/bindings/video/ssd1307fb.txt index
> > > 7a12542..1230f68 100644 ---
> > > a/Documentation/devicetree/bindings/video/ssd1307fb.txt +++
> > > b/Documentation/devicetree/bindings/video/ssd1307fb.txt @@ -15,6
> > > +15,17 @@ Required properties:
> > > Optional properties:
> > > - reset-active-low: Is the reset gpio is active on physical low?
> > > + - solomon,segment-remap: Invert the order of data column to
> > > segment mapping
> > > + - solomon,offset: Map the display start line to one of COM0 -
> > > COM63
> > > + - solomon,contrast: Set initial contrast of the display
> > > + - solomon,prechargep1: Set the duration of the precharge period
> > > phase1
> > > + - solomon,prechargep2: Set the duration of the precharge period
> > > phase2
> > > + - solomon,com-alt: Enable/disable alternate COM pin configuration
> > > + - solomon,com-lrremap: Enable/disable left-right remap of COM
> > > pins
> > > + - solomon,com-invdir: Invert COM scan direction
> > > + - solomon,vcomh: Set VCOMH regulator voltage
> > > + - solomon,dclk-div: Set display clock divider
> > > + - solomon,dclk-frq: Set display clock frequency
> >
> > I'm sorry, but this is the wrong approach, for at least two reasons:
> > you broke all existing users of that driver, which is a clear no-go,
>
> Unfortunately this is true. The problem is that the SSD130X
> controllers allow for a very versatile wiring of the display to the
> controller. It's over to the manufacturer of the OLED module
> (disp+controller) to decide how it's actually wired and during
> device initialization the driver has to take care to configure the
> SSD130X controller according to that wiring. If the driver fails to
> do so you will end up having your display showing
> garbage.

How so?

Does it depend on the X, or can it change from one same controller to
another? to what extent?

The 1306 for example seems to not be using these values at all, while
the 1307 does.

> Unfortunately the current sate of the initialization code of the
> ssd1307fb driver is not very flexible in that respect. Taking a look
> at the initialization code for the ssd1306 shows that it was written
> with one very special display module in mind. Most of the magic bit
> values set there are non-default values according to the
> datasheet. The result is that the driver works with that one
> particular display module but many other (differently wired) display
> modules using a ssd1306 controller won't work without changing the
> hardcoded magic bit values.
>
> My idea here was to set all configuration to the default values (as
> given in the datasheet) unless it is overwritten by DT. Of course,
> without a change in DT, this breaks the driver for all existing users.
> The only alternative would be to set the current values as default.
> Somehow this feels wrong to me as these values look arbitrary when you
> don't know what exact display module they were set for. But if you
> insist, I will change the default values.

Unfortunately, the DT bindings are to be considered an ABI, and we
should support booting with older DTs (not that I personally care
about it, but that's another issue). So we really don't have much
choice here.

Moreover, that issue left aside, modifying bindings like this without
fixing up the in-tree users is considered quite rude :)

> > and the DT itself should not contain any direct mapping of the
> > registers.
> >
>
> I think I don't get what you mean here. Is it because I do no sanity
> checks of the numbers set in DT? I was just looking for a way to hand
> over the information about the wiring of display to the driver. How
> would you propose to solve this?

What I meant was that replicating direct registers value is usually a
recipe for a later failure, especially if we can have the information
under a generic and easy to understand manner.

For example, replacing the solomon,dclk-div and solomon,dclk-frq
properties by a clock-frequency property in Hz, and computing the
divider and that register in your driver is usually better, also
because it allows to have different requirements / algorithms to
compute that if some other chip needs it.

Maxime

--
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com


Attachments:
(No filename) (5.13 kB)
signature.asc (819.00 B)
Digital signature
Download all attachments

2015-02-12 17:00:22

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH 2/8] fbdev: ssd1307fb: Unify init code and make controller configurable from device tree

On Sat, Feb 07, 2015 at 04:19:37PM +0100, Noralf Tr?nnes wrote:
> >>and the DT itself should not contain any direct mapping of the
> >>registers.
> >>
> >I think I don't get what you mean here. Is it because I do no sanity
> >checks of the numbers set in DT? I was just looking for a way to hand
> >over the information about the wiring of display to the driver. How
> >would you propose to solve this?
>
> I have the exact same challenge with the staging/fbtft drivers.
> I have asked about this on the DT list a couple of days ago, but no answer
> yet:
>
> Can I do register initialization from Device Tree?
> http://www.spinics.net/lists/devicetree/msg68174.html

The DT is just an hardware representation, and should not contain any
logic. Any attempts at doing so have been failures, mostly because
that kind of construct are really fragile.

What would happen if at some point some new controller pops up and
need to poke a GPIO before every write?

Every driver should contain all the needed code to initialise properly
its hardware. The only exception being stuff that are volatile by
essence, like bus addresses, GPIOs, etc.

In your case, using (and maybe extending) the generic panel bindings
look like a better way forward.

Maxime

--
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com


Attachments:
(No filename) (1.32 kB)
signature.asc (819.00 B)
Digital signature
Download all attachments

2015-02-14 14:19:31

by Thomas Niederprüm

[permalink] [raw]
Subject: Re: [PATCH 4/8] fbdev: ssd1307fb: Use vmalloc to allocate video memory.

Am Thu, 12 Feb 2015 16:11:21 +0100
schrieb Maxime Ripard <[email protected]>:

> On Sat, Feb 07, 2015 at 04:35:41PM +0100, Thomas Niederprüm wrote:
> > Am Sat, 7 Feb 2015 12:18:21 +0100
> > schrieb Maxime Ripard <[email protected]>:
> >
> > > Hi,
> > >
> > > On Fri, Feb 06, 2015 at 11:28:10PM +0100, [email protected]
> > > wrote:
> > > > From: Thomas Niederprüm <[email protected]>
> > > >
> > > > It makes sense to use vmalloc to allocate the video buffer
> > > > since it has to be page aligned memory for using it with mmap.
> > >
> > > Please wrap your commit log at 80 chars.
> >
> > I'll try to do so in future, sorry for that.
> >
> > >
> > > It looks like there's numerous fbdev drivers using this
> > > (especially since you copy pasted that code, without mentionning
> > > it).
> >
> > Yes, I should have mentioned that in the commit message. As
> > implicitly indicated in the cover letter the rvmalloc() and
> > rvfree() are copy pasted from the vfb driver. Honestly, I didn't
> > give this one too much thought. It seemed a viable solution to the
> > mmap problem. For a bit more history on that, see my comment below.
> >
> > >
> > > That should be turned into an allocator so that drivers all get
> > > this right.
> > >
> > > > Also deffered io seems buggy in combination with kmalloc'ed
> > > > memory (crash on unloading the module).
> > >
> > > And maybe that's the real issue to fix.
> >
> > The problem is solved by using vmalloc ;)
>
> Yep, but why do you need to mark the reserved pages?
>
> ...

As far as I understood mmaped memory is marked as userspace memory in
the page table and is therefore subject to swapping. The pages are
marked reserved to make clear that this memory can not be swapped and
thus lock the pages in memory. See discussions [0,1,2].

[0]http://stackoverflow.com/questions/10760479/mmap-kernel-buffer-to-user-space
[1]http://www.linuxquestions.org/questions/linux-kernel-70/why-why-setpagereserved-is-needed-when-map-a-kernel-space-to-user-space-885176/
[2]https://sites.google.com/site/skartikeyan/mmap.html

>
> > > > @@ -570,7 +608,7 @@ static int ssd1307fb_probe(struct i2c_client
> > > > *client, info->var.blue.offset = 0;
> > > >
> > > > info->screen_base = (u8 __force __iomem *)vmem;
> > > > - info->fix.smem_start = (unsigned long)vmem;
> > > > + info->fix.smem_start = __pa(vmem);
> > >
> > > Why are you changing from virtual to physical address here?
> >
> > Oh, good catch! This is still a residual of my attempts to get this
> > working with kmalloc'ed memory. In the current state the driver is
> > presenting a completely wrong memory address upon mmap. As reported
> > in [0] info->fix.smem_start has to hold the physical address of the
> > video memory if it was allocated using kmalloc. Correcting this let
> > me run into the problem that the kmalloc'ed memory was not page
> > aligned but, the memory address handed to userspace mmap was
> > aligned to the next full page, resulting in an inaccessable display
> > region. At that point I just copied the vmalloc approach from the
> > vfb driver.
> >
> > [0]
> > http://stackoverflow.com/questions/22285151/kernel-panic-using-deferred-io-on-kmalloced-buffer
>
> And the documentation of fb_fix_screeninfo indeed state that this
> should be the physical address:
> http://lxr.free-electrons.com/source/include/uapi/linux/fb.h#L158
>
> Could you make that a separate patch?

Will be a separate patch in v2.

>
> Thanks,
> Maxime
>

Thomas

2015-02-14 15:40:10

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH 4/8] fbdev: ssd1307fb: Use vmalloc to allocate video memory.

On Sat, Feb 14, 2015 at 03:22:12PM +0100, Thomas Niederpr?m wrote:
> Am Thu, 12 Feb 2015 16:11:21 +0100
> schrieb Maxime Ripard <[email protected]>:
>
> > On Sat, Feb 07, 2015 at 04:35:41PM +0100, Thomas Niederpr?m wrote:
> > > Am Sat, 7 Feb 2015 12:18:21 +0100
> > > schrieb Maxime Ripard <[email protected]>:
> > >
> > > > Hi,
> > > >
> > > > On Fri, Feb 06, 2015 at 11:28:10PM +0100, [email protected]
> > > > wrote:
> > > > > From: Thomas Niederpr?m <[email protected]>
> > > > >
> > > > > It makes sense to use vmalloc to allocate the video buffer
> > > > > since it has to be page aligned memory for using it with mmap.
> > > >
> > > > Please wrap your commit log at 80 chars.
> > >
> > > I'll try to do so in future, sorry for that.
> > >
> > > >
> > > > It looks like there's numerous fbdev drivers using this
> > > > (especially since you copy pasted that code, without mentionning
> > > > it).
> > >
> > > Yes, I should have mentioned that in the commit message. As
> > > implicitly indicated in the cover letter the rvmalloc() and
> > > rvfree() are copy pasted from the vfb driver. Honestly, I didn't
> > > give this one too much thought. It seemed a viable solution to the
> > > mmap problem. For a bit more history on that, see my comment below.
> > >
> > > >
> > > > That should be turned into an allocator so that drivers all get
> > > > this right.
> > > >
> > > > > Also deffered io seems buggy in combination with kmalloc'ed
> > > > > memory (crash on unloading the module).
> > > >
> > > > And maybe that's the real issue to fix.
> > >
> > > The problem is solved by using vmalloc ;)
> >
> > Yep, but why do you need to mark the reserved pages?
> >
> > ...
>
> As far as I understood mmaped memory is marked as userspace memory in
> the page table and is therefore subject to swapping. The pages are
> marked reserved to make clear that this memory can not be swapped and
> thus lock the pages in memory. See discussions [0,1,2].
>
> [0]http://stackoverflow.com/questions/10760479/mmap-kernel-buffer-to-user-space
> [1]http://www.linuxquestions.org/questions/linux-kernel-70/why-why-setpagereserved-is-needed-when-map-a-kernel-space-to-user-space-885176/
> [2]https://sites.google.com/site/skartikeyan/mmap.html

Hmmm, both https://lwn.net/Articles/28746/ and
http://linux-mm.org/DeviceDriverMmap tell otherwise :)

Maxime

--
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com


Attachments:
(No filename) (2.46 kB)
signature.asc (819.00 B)
Digital signature
Download all attachments

2015-02-14 15:55:06

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH 5/8] fbdev: ssd1307fb: Add module parameter bitsperpixel.

On Sat, Feb 07, 2015 at 05:05:03PM +0100, Thomas Niederpr?m wrote:
> Am Sat, 7 Feb 2015 12:20:43 +0100
> schrieb Maxime Ripard <[email protected]>:
>
> > On Fri, Feb 06, 2015 at 11:28:11PM +0100, [email protected]
> > wrote:
> > > From: Thomas Niederpr?m <[email protected]>
> > >
> > > This patch adds a module parameter 'bitsperpixel' to adjust the
> > > colordepth of the framebuffer. All values >1 will result in memory
> > > map of the requested color depth. However only the MSB of each
> > > pixel will be sent to the device. The framebuffer identifies itself
> > > as a grayscale display with the specified depth.
> >
> > I'm not sure this is the right thing to do.
> >
> > The bits per pixel for this display is rightfully defined, used and
> > reported to the userspace, why would you want to change that?
>
> You are right of course. The display is 1bpp and it reports to be 1
> bpp. The problem is that there is almost no userspace library that can
> handle 1 bit framebuffers correctly. So it is nice if the framebuffer
> (optionally) can expose itself as 8 bits per pixel grayscale to the
> userspace program. As an example this allows to run DirectFB on the
> framebuffer, which is not possible out of the box for 1bpp.
>
> Also note that if do not set the module parameter at load time
> the framebuffer will be 1bpp. So you have to actively set that module
> parameter to make the framebuffer pretend to be more than 1bpp.
>
> In any case I don't cling to that patch, I just thought it was a nice
> feature.

I'd say that the right fix would be to patch DirectFB, instead of
faking that in the kernel.

But again, that's probably Tomi's call, not mine.

Maxime

--
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com


Attachments:
(No filename) (1.78 kB)
signature.asc (819.00 B)
Digital signature
Download all attachments

2015-02-14 16:09:51

by Thomas Niederprüm

[permalink] [raw]
Subject: Re: [PATCH 2/8] fbdev: ssd1307fb: Unify init code and make controller configurable from device tree

Am Thu, 12 Feb 2015 17:41:47 +0100
schrieb Maxime Ripard <[email protected]>:

> On Sat, Feb 07, 2015 at 03:59:33PM +0100, Thomas Niederprüm wrote:
> > Am Sat, 7 Feb 2015 11:42:25 +0100
> > schrieb Maxime Ripard <[email protected]>:
> >
> > > Hi,
> > >
> > > On Fri, Feb 06, 2015 at 11:28:08PM +0100, [email protected]
> > > wrote:
> > > > From: Thomas Niederprüm <[email protected]>
> > > >
> > > > This patches unifies the init code for the ssd130X chips and
> > > > adds device tree bindings to describe the hardware configuration
> > > > of the used controller. This gets rid of the magic bit values
> > > > used in the init code so far.
> > > >
> > > > Signed-off-by: Thomas Niederprüm <[email protected]>
> > > > ---
> > > > .../devicetree/bindings/video/ssd1307fb.txt | 11 +
> > > > drivers/video/fbdev/ssd1307fb.c | 243
> > > > ++++++++++++++------- 2 files changed, 174 insertions(+), 80
> > > > deletions(-)
> > > >
> > > > diff --git
> > > > a/Documentation/devicetree/bindings/video/ssd1307fb.txt
> > > > b/Documentation/devicetree/bindings/video/ssd1307fb.txt index
> > > > 7a12542..1230f68 100644 ---
> > > > a/Documentation/devicetree/bindings/video/ssd1307fb.txt +++
> > > > b/Documentation/devicetree/bindings/video/ssd1307fb.txt @@
> > > > -15,6 +15,17 @@ Required properties: Optional properties:
> > > > - reset-active-low: Is the reset gpio is active on physical
> > > > low?
> > > > + - solomon,segment-remap: Invert the order of data column to
> > > > segment mapping
> > > > + - solomon,offset: Map the display start line to one of COM0 -
> > > > COM63
> > > > + - solomon,contrast: Set initial contrast of the display
> > > > + - solomon,prechargep1: Set the duration of the precharge
> > > > period phase1
> > > > + - solomon,prechargep2: Set the duration of the precharge
> > > > period phase2
> > > > + - solomon,com-alt: Enable/disable alternate COM pin
> > > > configuration
> > > > + - solomon,com-lrremap: Enable/disable left-right remap of COM
> > > > pins
> > > > + - solomon,com-invdir: Invert COM scan direction
> > > > + - solomon,vcomh: Set VCOMH regulator voltage
> > > > + - solomon,dclk-div: Set display clock divider
> > > > + - solomon,dclk-frq: Set display clock frequency
> > >
> > > I'm sorry, but this is the wrong approach, for at least two
> > > reasons: you broke all existing users of that driver, which is a
> > > clear no-go,
> >
> > Unfortunately this is true. The problem is that the SSD130X
> > controllers allow for a very versatile wiring of the display to the
> > controller. It's over to the manufacturer of the OLED module
> > (disp+controller) to decide how it's actually wired and during
> > device initialization the driver has to take care to configure the
> > SSD130X controller according to that wiring. If the driver fails to
> > do so you will end up having your display showing
> > garbage.
>
> How so?

One good example is the segment remap. It basically allows to invert the
order of the output pins connecting to the oled panel. This gives the
manufacturer of the module the freedom wire it the one way or the
other, depending on the PCB restrictions/panel layout (Section 10.1.12
of [0], 10.1.8 in [1], 9.1.8 in [2]). However, once the panel is
connected to the controller it's determined whether the segment remap
is needed or not. Setting the segment remap as done in the current
initialization code for ssd1306 and ssd1307 makes my display module
show it's contents mirrored left to right, probably since the
manufacturer decided not to connect the panel in an inverted order.

The same applies to the com-alt, com-lrremap and com-invdir values,
which define different possibilities for the COM signals pin
configuration (Section 10.1.26 of [0], 10.1.18 in [1], 9.1.18 in [2])
and readout direction of the video memory (Section 10.1.21 of [0],
10.1.14 in [1], 9.1.14 in [2]). Setting com-alt incorrectly leaves
every other line of the display blank. Setting com-lrremap incorrectly
produces a very distorted image. Setting com-invdir incorrectly flips
the image upside down.

IMHO at least these four hardware-specific properties need to be known
to the driver in order to initialize the hardware correctly.

>
> Does it depend on the X, or can it change from one same controller to
> another? to what extent?

Unfortunately I do not posses any hardware utilizing a ssd1306 or
ssd1307 controller. My primary and only target device is a Newhaven
NHD-3.12-25664UCB2 OLED display module using an SSD1305 controller. I
just inferred from the datasheets of ssd1306/7 [1,2] that they should
behave the same since the registers are bit to bit identical (except
for the VHCOM register). Maybe that was a bit too naive :/

>
> The 1306 for example seems to not be using these values at all, while
> the 1307 does.

That is surprising. In that case I would like to ask the guys from
Solomon why they describe all these options in the SSD1306 datasheet
[1]. But in any case, isn't that good news for the problem of setting
the default values. When the 1306 isn't using these values anyway we can
not break the initialization by setting different default values. In
this case the problem of the default values boils down to the segment
remap only since this is set in the init code of the 1307, while the
default would be to leave it off.

>
> > Unfortunately the current sate of the initialization code of the
> > ssd1307fb driver is not very flexible in that respect. Taking a look
> > at the initialization code for the ssd1306 shows that it was written
> > with one very special display module in mind. Most of the magic bit
> > values set there are non-default values according to the
> > datasheet. The result is that the driver works with that one
> > particular display module but many other (differently wired) display
> > modules using a ssd1306 controller won't work without changing the
> > hardcoded magic bit values.
> >
> > My idea here was to set all configuration to the default values (as
> > given in the datasheet) unless it is overwritten by DT. Of course,
> > without a change in DT, this breaks the driver for all existing
> > users. The only alternative would be to set the current values as
> > default. Somehow this feels wrong to me as these values look
> > arbitrary when you don't know what exact display module they were
> > set for. But if you insist, I will change the default values.
>
> Unfortunately, the DT bindings are to be considered an ABI, and we
> should support booting with older DTs (not that I personally care
> about it, but that's another issue). So we really don't have much
> choice here.
>
> Moreover, that issue left aside, modifying bindings like this without
> fixing up the in-tree users is considered quite rude :)

I didn't intend to be rude, sorry. A quick search revealed that there
is luckily only one in-tree user, which is imx28-cfa10036.dts. In case
it will be necessary I will include a patch to fix this.

> > > and the DT itself should not contain any direct mapping of the
> > > registers.
> > >
> >
> > I think I don't get what you mean here. Is it because I do no sanity
> > checks of the numbers set in DT? I was just looking for a way to
> > hand over the information about the wiring of display to the
> > driver. How would you propose to solve this?
>
> What I meant was that replicating direct registers value is usually a
> recipe for a later failure, especially if we can have the information
> under a generic and easy to understand manner.
>
> For example, replacing the solomon,dclk-div and solomon,dclk-frq
> properties by a clock-frequency property in Hz, and computing the
> divider and that register in your driver is usually better, also
> because it allows to have different requirements / algorithms to
> compute that if some other chip needs it.

I'll give that a try, even though that particular one is not trivial
since the documentation on the actual frequency that is set by the
dclk-freq is very poor (not present for 1306/1307 [1,2], just a graph
for 1305 [0]).

For the properties describing the hardware pin configuration (see above)
I see no real alternative. Maybe they can all be covered by one DT
property like:
solomon,com-cfg = PINCFG_SEGREMAP | PINCFG_COMALT | PINCFG_COMINV |
PINCFG_COMLRRM
each PINCFG_* setting one bit. The driver will then translate this into
the correct settings for the 130X registers. The only problem here is
that this implicitly assumes the default values of each bit to be 0.

>
> Maxime
>

[0]http://www.newhavendisplay.com/app_notes/SSD1305.pdf
[1]http://www.adafruit.com/datasheets/SSD1306.pdf
[2]http://www.displayfuture.com/Display/datasheet/controller/SSD1307.pdf

Thomas

2015-02-23 09:45:07

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH 2/8] fbdev: ssd1307fb: Unify init code and make controller configurable from device tree

On Sat, Feb 14, 2015 at 05:12:32PM +0100, Thomas Niederpr?m wrote:
> Am Thu, 12 Feb 2015 17:41:47 +0100
> schrieb Maxime Ripard <[email protected]>:
>
> > On Sat, Feb 07, 2015 at 03:59:33PM +0100, Thomas Niederpr?m wrote:
> > > Am Sat, 7 Feb 2015 11:42:25 +0100
> > > schrieb Maxime Ripard <[email protected]>:
> > >
> > > > Hi,
> > > >
> > > > On Fri, Feb 06, 2015 at 11:28:08PM +0100, [email protected]
> > > > wrote:
> > > > > From: Thomas Niederpr?m <[email protected]>
> > > > >
> > > > > This patches unifies the init code for the ssd130X chips and
> > > > > adds device tree bindings to describe the hardware configuration
> > > > > of the used controller. This gets rid of the magic bit values
> > > > > used in the init code so far.
> > > > >
> > > > > Signed-off-by: Thomas Niederpr?m <[email protected]>
> > > > > ---
> > > > > .../devicetree/bindings/video/ssd1307fb.txt | 11 +
> > > > > drivers/video/fbdev/ssd1307fb.c | 243
> > > > > ++++++++++++++------- 2 files changed, 174 insertions(+), 80
> > > > > deletions(-)
> > > > >
> > > > > diff --git
> > > > > a/Documentation/devicetree/bindings/video/ssd1307fb.txt
> > > > > b/Documentation/devicetree/bindings/video/ssd1307fb.txt index
> > > > > 7a12542..1230f68 100644 ---
> > > > > a/Documentation/devicetree/bindings/video/ssd1307fb.txt +++
> > > > > b/Documentation/devicetree/bindings/video/ssd1307fb.txt @@
> > > > > -15,6 +15,17 @@ Required properties: Optional properties:
> > > > > - reset-active-low: Is the reset gpio is active on physical
> > > > > low?
> > > > > + - solomon,segment-remap: Invert the order of data column to
> > > > > segment mapping
> > > > > + - solomon,offset: Map the display start line to one of COM0 -
> > > > > COM63
> > > > > + - solomon,contrast: Set initial contrast of the display
> > > > > + - solomon,prechargep1: Set the duration of the precharge
> > > > > period phase1
> > > > > + - solomon,prechargep2: Set the duration of the precharge
> > > > > period phase2
> > > > > + - solomon,com-alt: Enable/disable alternate COM pin
> > > > > configuration
> > > > > + - solomon,com-lrremap: Enable/disable left-right remap of COM
> > > > > pins
> > > > > + - solomon,com-invdir: Invert COM scan direction
> > > > > + - solomon,vcomh: Set VCOMH regulator voltage
> > > > > + - solomon,dclk-div: Set display clock divider
> > > > > + - solomon,dclk-frq: Set display clock frequency
> > > >
> > > > I'm sorry, but this is the wrong approach, for at least two
> > > > reasons: you broke all existing users of that driver, which is a
> > > > clear no-go,
> > >
> > > Unfortunately this is true. The problem is that the SSD130X
> > > controllers allow for a very versatile wiring of the display to the
> > > controller. It's over to the manufacturer of the OLED module
> > > (disp+controller) to decide how it's actually wired and during
> > > device initialization the driver has to take care to configure the
> > > SSD130X controller according to that wiring. If the driver fails to
> > > do so you will end up having your display showing
> > > garbage.
> >
> > How so?
>
> One good example is the segment remap. It basically allows to invert the
> order of the output pins connecting to the oled panel. This gives the
> manufacturer of the module the freedom wire it the one way or the
> other, depending on the PCB restrictions/panel layout (Section 10.1.12
> of [0], 10.1.8 in [1], 9.1.8 in [2]). However, once the panel is
> connected to the controller it's determined whether the segment remap
> is needed or not. Setting the segment remap as done in the current
> initialization code for ssd1306 and ssd1307 makes my display module
> show it's contents mirrored left to right, probably since the
> manufacturer decided not to connect the panel in an inverted order.
>
> The same applies to the com-alt, com-lrremap and com-invdir values,
> which define different possibilities for the COM signals pin
> configuration (Section 10.1.26 of [0], 10.1.18 in [1], 9.1.18 in [2])
> and readout direction of the video memory (Section 10.1.21 of [0],
> 10.1.14 in [1], 9.1.14 in [2]). Setting com-alt incorrectly leaves
> every other line of the display blank. Setting com-lrremap incorrectly
> produces a very distorted image. Setting com-invdir incorrectly flips
> the image upside down.
>
> IMHO at least these four hardware-specific properties need to be known
> to the driver in order to initialize the hardware correctly.

I'd agree then.

> > Does it depend on the X, or can it change from one same controller to
> > another? to what extent?
>
> Unfortunately I do not posses any hardware utilizing a ssd1306 or
> ssd1307 controller. My primary and only target device is a Newhaven
> NHD-3.12-25664UCB2 OLED display module using an SSD1305 controller. I
> just inferred from the datasheets of ssd1306/7 [1,2] that they should
> behave the same since the registers are bit to bit identical (except
> for the VHCOM register). Maybe that was a bit too naive :/

I would guess it's a rather safe assumption.

> > The 1306 for example seems to not be using these values at all, while
> > the 1307 does.
>
> That is surprising. In that case I would like to ask the guys from
> Solomon why they describe all these options in the SSD1306 datasheet
> [1]. But in any case, isn't that good news for the problem of setting
> the default values. When the 1306 isn't using these values anyway we can
> not break the initialization by setting different default values. In
> this case the problem of the default values boils down to the segment
> remap only since this is set in the init code of the 1307, while the
> default would be to leave it off.

Indeed.

> > > Unfortunately the current sate of the initialization code of the
> > > ssd1307fb driver is not very flexible in that respect. Taking a look
> > > at the initialization code for the ssd1306 shows that it was written
> > > with one very special display module in mind. Most of the magic bit
> > > values set there are non-default values according to the
> > > datasheet. The result is that the driver works with that one
> > > particular display module but many other (differently wired) display
> > > modules using a ssd1306 controller won't work without changing the
> > > hardcoded magic bit values.
> > >
> > > My idea here was to set all configuration to the default values (as
> > > given in the datasheet) unless it is overwritten by DT. Of course,
> > > without a change in DT, this breaks the driver for all existing
> > > users. The only alternative would be to set the current values as
> > > default. Somehow this feels wrong to me as these values look
> > > arbitrary when you don't know what exact display module they were
> > > set for. But if you insist, I will change the default values.
> >
> > Unfortunately, the DT bindings are to be considered an ABI, and we
> > should support booting with older DTs (not that I personally care
> > about it, but that's another issue). So we really don't have much
> > choice here.
> >
> > Moreover, that issue left aside, modifying bindings like this without
> > fixing up the in-tree users is considered quite rude :)
>
> I didn't intend to be rude, sorry. A quick search revealed that there
> is luckily only one in-tree user, which is imx28-cfa10036.dts. In case
> it will be necessary I will include a patch to fix this.

Please do (and fix the bindings Documentation too).

> > > > and the DT itself should not contain any direct mapping of the
> > > > registers.
> > > >
> > >
> > > I think I don't get what you mean here. Is it because I do no sanity
> > > checks of the numbers set in DT? I was just looking for a way to
> > > hand over the information about the wiring of display to the
> > > driver. How would you propose to solve this?
> >
> > What I meant was that replicating direct registers value is usually a
> > recipe for a later failure, especially if we can have the information
> > under a generic and easy to understand manner.
> >
> > For example, replacing the solomon,dclk-div and solomon,dclk-frq
> > properties by a clock-frequency property in Hz, and computing the
> > divider and that register in your driver is usually better, also
> > because it allows to have different requirements / algorithms to
> > compute that if some other chip needs it.
>
> I'll give that a try, even though that particular one is not trivial
> since the documentation on the actual frequency that is set by the
> dclk-freq is very poor (not present for 1306/1307 [1,2], just a graph
> for 1305 [0]).
>
> For the properties describing the hardware pin configuration (see above)
> I see no real alternative. Maybe they can all be covered by one DT
> property like:
> solomon,com-cfg = PINCFG_SEGREMAP | PINCFG_COMALT | PINCFG_COMINV |
> PINCFG_COMLRRM
> each PINCFG_* setting one bit. The driver will then translate this into
> the correct settings for the 130X registers. The only problem here is
> that this implicitly assumes the default values of each bit to be 0.

A property that would be here or not is better. You can have all the
defaults you want, it's more clear in the DT, and you don't need the
macros.

Maxime

--
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com


Attachments:
(No filename) (9.15 kB)
signature.asc (819.00 B)
Digital signature
Download all attachments