summaryrefslogtreecommitdiffstats
path: root/drivers/video/fbdev/ssd1307fb.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video/fbdev/ssd1307fb.c')
-rw-r--r--drivers/video/fbdev/ssd1307fb.c131
1 files changed, 92 insertions, 39 deletions
diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c
index b674948e3bb8..78ca7ffc40c2 100644
--- a/drivers/video/fbdev/ssd1307fb.c
+++ b/drivers/video/fbdev/ssd1307fb.c
@@ -28,6 +28,7 @@
#define SSD1307FB_SET_COL_RANGE 0x21
#define SSD1307FB_SET_PAGE_RANGE 0x22
#define SSD1307FB_CONTRAST 0x81
+#define SSD1307FB_SET_LOOKUP_TABLE 0x91
#define SSD1307FB_CHARGE_PUMP 0x8d
#define SSD1307FB_SEG_REMAP_ON 0xa1
#define SSD1307FB_DISPLAY_OFF 0xae
@@ -36,6 +37,7 @@
#define SSD1307FB_START_PAGE_ADDRESS 0xb0
#define SSD1307FB_SET_DISPLAY_OFFSET 0xd3
#define SSD1307FB_SET_CLOCK_FREQ 0xd5
+#define SSD1307FB_SET_AREA_COLOR_MODE 0xd8
#define SSD1307FB_SET_PRECHARGE_PERIOD 0xd9
#define SSD1307FB_SET_COM_PINS_CONFIG 0xda
#define SSD1307FB_SET_VCOMH 0xdb
@@ -58,10 +60,14 @@ struct ssd1307fb_deviceinfo {
};
struct ssd1307fb_par {
- u32 com_invdir;
- u32 com_lrremap;
+ unsigned area_color_enable : 1;
+ unsigned com_invdir : 1;
+ unsigned com_lrremap : 1;
+ unsigned com_seq : 1;
+ unsigned lookup_table_set : 1;
+ unsigned low_power : 1;
+ unsigned seg_remap : 1;
u32 com_offset;
- u32 com_seq;
u32 contrast;
u32 dclk_div;
u32 dclk_frq;
@@ -69,6 +75,7 @@ struct ssd1307fb_par {
struct i2c_client *client;
u32 height;
struct fb_info *info;
+ u8 lookup_table[4];
u32 page_offset;
u32 prechargep1;
u32 prechargep2;
@@ -76,7 +83,6 @@ struct ssd1307fb_par {
u32 pwm_period;
struct gpio_desc *reset;
struct regulator *vbat_reg;
- u32 seg_remap;
u32 vcomh;
u32 width;
};
@@ -98,6 +104,9 @@ static const struct fb_fix_screeninfo ssd1307fb_fix = {
static const struct fb_var_screeninfo ssd1307fb_var = {
.bits_per_pixel = 1,
+ .red = { .length = 1 },
+ .green = { .length = 1 },
+ .blue = { .length = 1 },
};
static struct ssd1307fb_array *ssd1307fb_alloc_array(u32 len, u8 type)
@@ -149,11 +158,12 @@ static inline int ssd1307fb_write_cmd(struct i2c_client *client, u8 cmd)
static void ssd1307fb_update_display(struct ssd1307fb_par *par)
{
struct ssd1307fb_array *array;
- u8 *vmem = par->info->screen_base;
+ u8 *vmem = par->info->screen_buffer;
+ unsigned int line_length = par->info->fix.line_length;
+ unsigned int pages = DIV_ROUND_UP(par->height, 8);
int i, j, k;
- array = ssd1307fb_alloc_array(par->width * par->height / 8,
- SSD1307FB_DATA);
+ array = ssd1307fb_alloc_array(par->width * pages, SSD1307FB_DATA);
if (!array)
return;
@@ -186,22 +196,24 @@ static void ssd1307fb_update_display(struct ssd1307fb_par *par)
* (5) A4 B4 C4 D4 E4 F4 G4 H4
*/
- for (i = 0; i < (par->height / 8); i++) {
+ for (i = 0; i < pages; i++) {
for (j = 0; j < par->width; j++) {
+ int m = 8;
u32 array_idx = i * par->width + j;
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;
- u8 byte = *(vmem + index);
- u8 bit = byte & (1 << (j % 8));
- bit = bit >> (j % 8);
+ /* Last page may be partial */
+ if (i + 1 == pages && par->height % 8)
+ m = par->height % 8;
+ for (k = 0; k < m; k++) {
+ u8 byte = vmem[(8 * i + k) * line_length +
+ j / 8];
+ u8 bit = (byte >> (j % 8)) & 1;
array->data[array_idx] |= bit << k;
}
}
}
- ssd1307fb_write_array(par->client, array, par->width * par->height / 8);
+ ssd1307fb_write_array(par->client, array, par->width * pages);
kfree(array);
}
@@ -212,7 +224,7 @@ static ssize_t ssd1307fb_write(struct fb_info *info, const char __user *buf,
struct ssd1307fb_par *par = info->par;
unsigned long total_size;
unsigned long p = *ppos;
- u8 __iomem *dst;
+ void *dst;
total_size = info->fix.smem_len;
@@ -225,7 +237,7 @@ static ssize_t ssd1307fb_write(struct fb_info *info, const char __user *buf,
if (!count)
return -EINVAL;
- dst = (void __force *) (info->screen_base + p);
+ dst = info->screen_buffer + p;
if (copy_from_user(dst, buf, count))
return -EFAULT;
@@ -312,7 +324,7 @@ static int ssd1307fb_init(struct ssd1307fb_par *par)
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);
@@ -328,10 +340,10 @@ static int ssd1307fb_init(struct ssd1307fb_par *par)
ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON);
if (ret < 0)
return ret;
- };
+ }
/* Set COM direction */
- com_invdir = 0xc0 | (par->com_invdir & 0x1) << 3;
+ com_invdir = 0xc0 | par->com_invdir << 3;
ret = ssd1307fb_write_cmd(par->client, com_invdir);
if (ret < 0)
return ret;
@@ -364,6 +376,22 @@ static int ssd1307fb_init(struct ssd1307fb_par *par)
if (ret < 0)
return ret;
+ /* Set Set Area Color Mode ON/OFF & Low Power Display Mode */
+ if (par->area_color_enable || par->low_power) {
+ u32 mode;
+
+ ret = ssd1307fb_write_cmd(par->client,
+ SSD1307FB_SET_AREA_COLOR_MODE);
+ if (ret < 0)
+ return ret;
+
+ mode = (par->area_color_enable ? 0x30 : 0) |
+ (par->low_power ? 5 : 0);
+ ret = ssd1307fb_write_cmd(par->client, mode);
+ if (ret < 0)
+ return ret;
+ }
+
/* Set precharge period in number of ticks from the internal clock */
ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PRECHARGE_PERIOD);
if (ret < 0)
@@ -379,8 +407,7 @@ static int ssd1307fb_init(struct ssd1307fb_par *par)
if (ret < 0)
return ret;
- compins = 0x02 | !(par->com_seq & 0x1) << 4
- | (par->com_lrremap & 0x1) << 5;
+ compins = 0x02 | !par->com_seq << 4 | par->com_lrremap << 5;
ret = ssd1307fb_write_cmd(par->client, compins);
if (ret < 0)
return ret;
@@ -404,6 +431,28 @@ static int ssd1307fb_init(struct ssd1307fb_par *par)
if (ret < 0)
return ret;
+ /* Set lookup table */
+ if (par->lookup_table_set) {
+ int i;
+
+ ret = ssd1307fb_write_cmd(par->client,
+ SSD1307FB_SET_LOOKUP_TABLE);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(par->lookup_table); ++i) {
+ u8 val = par->lookup_table[i];
+
+ if (val < 31 || val > 63)
+ dev_warn(&par->client->dev,
+ "lookup table index %d value out of range 31 <= %d <= 63\n",
+ i, val);
+ ret = ssd1307fb_write_cmd(par->client, val);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
/* Switch to horizontal addressing mode */
ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_ADDRESS_MODE);
if (ret < 0)
@@ -432,12 +481,13 @@ static int ssd1307fb_init(struct ssd1307fb_par *par)
if (ret < 0)
return ret;
- ret = ssd1307fb_write_cmd(par->client, 0x0);
+ ret = ssd1307fb_write_cmd(par->client, par->page_offset);
if (ret < 0)
return ret;
ret = ssd1307fb_write_cmd(par->client,
- par->page_offset + (par->height / 8) - 1);
+ par->page_offset +
+ DIV_ROUND_UP(par->height, 8) - 1);
if (ret < 0)
return ret;
@@ -546,7 +596,7 @@ static int ssd1307fb_probe(struct i2c_client *client,
struct fb_deferred_io *ssd1307fb_defio;
u32 vmem_size;
struct ssd1307fb_par *par;
- u8 *vmem;
+ void *vmem;
int ret;
if (!node) {
@@ -603,19 +653,29 @@ static int ssd1307fb_probe(struct i2c_client *client,
if (of_property_read_u32(node, "solomon,prechargep2", &par->prechargep2))
par->prechargep2 = 2;
+ if (!of_property_read_u8_array(node, "solomon,lookup-table",
+ par->lookup_table,
+ ARRAY_SIZE(par->lookup_table)))
+ par->lookup_table_set = 1;
+
par->seg_remap = !of_property_read_bool(node, "solomon,segment-no-remap");
par->com_seq = of_property_read_bool(node, "solomon,com-seq");
par->com_lrremap = of_property_read_bool(node, "solomon,com-lrremap");
par->com_invdir = of_property_read_bool(node, "solomon,com-invdir");
+ par->area_color_enable =
+ of_property_read_bool(node, "solomon,area-color-enable");
+ par->low_power = of_property_read_bool(node, "solomon,low-power");
par->contrast = 127;
par->vcomh = par->device_info->default_vcomh;
/* Setup display timing */
- par->dclk_div = par->device_info->default_dclk_div;
- par->dclk_frq = par->device_info->default_dclk_frq;
+ 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_size = par->width * par->height / 8;
+ vmem_size = DIV_ROUND_UP(par->width, 8) * par->height;
vmem = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
get_order(vmem_size));
@@ -638,7 +698,7 @@ 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 = DIV_ROUND_UP(par->width, 8);
info->fbdefio = ssd1307fb_defio;
info->var = ssd1307fb_var;
@@ -647,14 +707,7 @@ static int ssd1307fb_probe(struct i2c_client *client,
info->var.yres = par->height;
info->var.yres_virtual = par->height;
- info->var.red.length = 1;
- info->var.red.offset = 0;
- info->var.green.length = 1;
- info->var.green.offset = 0;
- info->var.blue.length = 1;
- info->var.blue.offset = 0;
-
- info->screen_base = (u8 __force __iomem *)vmem;
+ info->screen_buffer = vmem;
info->fix.smem_start = __pa(vmem);
info->fix.smem_len = vmem_size;
@@ -713,7 +766,7 @@ panel_init_error:
if (par->device_info->need_pwm) {
pwm_disable(par->pwm);
pwm_put(par->pwm);
- };
+ }
regulator_enable_error:
if (par->vbat_reg)
regulator_disable(par->vbat_reg);
@@ -737,7 +790,7 @@ static int ssd1307fb_remove(struct i2c_client *client)
if (par->device_info->need_pwm) {
pwm_disable(par->pwm);
pwm_put(par->pwm);
- };
+ }
fb_deferred_io_cleanup(info);
__free_pages(__va(info->fix.smem_start), get_order(info->fix.smem_len));
framebuffer_release(info);
OpenPOWER on IntegriCloud