|
@@ -40,20 +40,34 @@
|
|
|
|
|
|
struct ssd1307fb_par;
|
|
|
|
|
|
-struct ssd1307fb_ops {
|
|
|
- int (*init)(struct ssd1307fb_par *);
|
|
|
- int (*remove)(struct ssd1307fb_par *);
|
|
|
+struct ssd1307fb_deviceinfo {
|
|
|
+ u32 default_vcomh;
|
|
|
+ u32 default_dclk_div;
|
|
|
+ u32 default_dclk_frq;
|
|
|
+ int need_pwm;
|
|
|
+ int need_chargepump;
|
|
|
};
|
|
|
|
|
|
struct ssd1307fb_par {
|
|
|
+ u32 com_invdir;
|
|
|
+ u32 com_lrremap;
|
|
|
+ u32 com_offset;
|
|
|
+ u32 com_seq;
|
|
|
+ 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 page_offset;
|
|
|
+ u32 prechargep1;
|
|
|
+ u32 prechargep2;
|
|
|
struct pwm_device *pwm;
|
|
|
u32 pwm_period;
|
|
|
int reset;
|
|
|
+ u32 seg_remap;
|
|
|
+ u32 vcomh;
|
|
|
u32 width;
|
|
|
};
|
|
|
|
|
@@ -254,69 +268,46 @@ 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;
|
|
|
-}
|
|
|
+ if (par->device_info->need_pwm) {
|
|
|
+ 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 int ssd1307fb_ssd1307_remove(struct ssd1307fb_par *par)
|
|
|
-{
|
|
|
- pwm_disable(par->pwm);
|
|
|
- pwm_put(par->pwm);
|
|
|
- return 0;
|
|
|
-}
|
|
|
+ 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 struct ssd1307fb_ops ssd1307fb_ssd1307_ops = {
|
|
|
- .init = ssd1307fb_ssd1307_init,
|
|
|
- .remove = ssd1307fb_ssd1307_remove,
|
|
|
-};
|
|
|
-
|
|
|
-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);
|
|
|
if (ret < 0)
|
|
|
return 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 & 0x1) << 3;
|
|
|
+ ret = ssd1307fb_write_cmd(par->client, com_invdir);
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
|
|
@@ -334,7 +325,7 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
|
|
|
- ret = ssd1307fb_write_cmd(par->client, 0x20);
|
|
|
+ ret = ssd1307fb_write_cmd(par->client, par->com_offset);
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
|
|
@@ -343,7 +334,8 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
|
|
|
- ret = ssd1307fb_write_cmd(par->client, 0xf0);
|
|
|
+ dclk = ((par->dclk_div - 1) & 0xf) | (par->dclk_frq & 0xf) << 4;
|
|
|
+ ret = ssd1307fb_write_cmd(par->client, dclk);
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
|
|
@@ -352,7 +344,8 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
|
|
|
- ret = ssd1307fb_write_cmd(par->client, 0x22);
|
|
|
+ precharge = (par->prechargep1 & 0xf) | (par->prechargep2 & 0xf) << 4;
|
|
|
+ ret = ssd1307fb_write_cmd(par->client, precharge);
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
|
|
@@ -361,7 +354,9 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
|
|
|
- ret = ssd1307fb_write_cmd(par->client, 0x22);
|
|
|
+ compins = 0x02 | (!par->com_seq & 0x1) << 4
|
|
|
+ | (par->com_lrremap & 0x1) << 5;
|
|
|
+ ret = ssd1307fb_write_cmd(par->client, compins);
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
|
|
@@ -370,7 +365,7 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
|
|
|
- ret = ssd1307fb_write_cmd(par->client, 0x49);
|
|
|
+ ret = ssd1307fb_write_cmd(par->client, par->vcomh);
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
|
|
@@ -379,7 +374,8 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
|
|
|
- ret = ssd1307fb_write_cmd(par->client, 0x14);
|
|
|
+ ret = ssd1307fb_write_cmd(par->client,
|
|
|
+ (par->device_info->need_chargepump & 0x1 << 2) & 0x14);
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
|
|
@@ -393,6 +389,7 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
|
|
|
+ /* Set column range */
|
|
|
ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COL_RANGE);
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
@@ -405,6 +402,7 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
|
|
|
+ /* Set page range */
|
|
|
ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PAGE_RANGE);
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
@@ -426,18 +424,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 = {
|
|
|
+ .default_vcomh = 0x20,
|
|
|
+ .default_dclk_div = 1,
|
|
|
+ .default_dclk_frq = 8,
|
|
|
+ .need_chargepump = 1,
|
|
|
+};
|
|
|
+
|
|
|
+static struct ssd1307fb_deviceinfo ssd1307fb_ssd1307_deviceinfo = {
|
|
|
+ .default_vcomh = 0x20,
|
|
|
+ .default_dclk_div = 2,
|
|
|
+ .default_dclk_frq = 12,
|
|
|
+ .need_pwm = 1,
|
|
|
};
|
|
|
|
|
|
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,
|
|
|
},
|
|
|
{},
|
|
|
};
|
|
@@ -468,8 +476,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);
|
|
@@ -487,6 +495,27 @@ static int ssd1307fb_probe(struct i2c_client *client,
|
|
|
if (of_property_read_u32(node, "solomon,page-offset", &par->page_offset))
|
|
|
par->page_offset = 1;
|
|
|
|
|
|
+ if (of_property_read_u32(node, "solomon,com-offset", &par->com_offset))
|
|
|
+ par->com_offset = 0;
|
|
|
+
|
|
|
+ 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;
|
|
|
+
|
|
|
+ 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->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;
|
|
|
+
|
|
|
vmem_size = par->width * par->height / 8;
|
|
|
|
|
|
vmem = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
|
|
@@ -539,11 +568,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) {
|
|
@@ -556,8 +583,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->need_pwm) {
|
|
|
+ pwm_disable(par->pwm);
|
|
|
+ pwm_put(par->pwm);
|
|
|
+ };
|
|
|
reset_oled_error:
|
|
|
fb_deferred_io_cleanup(info);
|
|
|
fb_alloc_error:
|
|
@@ -571,8 +600,10 @@ 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->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);
|