aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video
diff options
context:
space:
mode:
authorThomas Niederprüm <niederp@physik.uni-kl.de>2015-03-31 14:27:10 -0400
committerTomi Valkeinen <tomi.valkeinen@ti.com>2015-05-27 05:53:44 -0400
commitc89eacfc700675912b53df770953c30930c2554f (patch)
tree0aaee2f26e4544168394a58b047e35d50a066805 /drivers/video
parent7133fb51db0ace5c7563d0169ef1c91f81dc85b7 (diff)
fbdev: ssd1307fb: Unify init code and obtain hw specific bits from DT
The 130X controllers are very similar from the configuration point of view. The configuration registers for the SSD1305/6/7 are bit identical (except the the VHCOM register and the the default values for clock setup register). This patch unifies the init code of the controller and adds hardware specific properties to DT that are needed to correctly initialize the device. The SSD130X can be wired to the OLED panel in various ways. Even for the same controller this wiring can differ from one display module to another and can not be probed by software. The added DT properties reflect these hardware decisions of the display module manufacturer. The 'com-sequential', 'com-lrremap' and 'com-invdir' values define different possibilities for the COM signals pin configuration and readout direction of the video memory. The 'segment-no-remap' allows the inversion of the memory-to-pin mapping ultimately inverting the order of the controllers output pins. The 'prechargepX' values need to be adapted according to the capacitance of the OLEDs pixel cells. So far these hardware specific bits are hard coded in the init code, making the driver usable only for one certain wiring of the controller. This patch makes the driver usable with all possible hardware setups, given a valid hw description in DT. If these values are not set in DT the default values, as they are set in the ssd1307 init code right now, are used. This implies that without the corresponding DT property "segment-no-remap" the segment remap of the ssd130X controller gets activated. Even though this is not the default behaviour according to the datasheet it maintains backward compatibility with older DTBs. Note that the SSD1306 does not seem to be using the configuration written to the registers at all. Therefore this patch does not try to maintain these values without changes in DT. For reference an example is added to the DT bindings documentation that reproduces the configuration that is set in the current init code. Signed-off-by: Thomas Niederprüm <niederp@physik.uni-kl.de> Tested-by: Olliver Schinagl <o.schinagl@ultimaker.com> Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Diffstat (limited to 'drivers/video')
-rw-r--r--drivers/video/fbdev/ssd1307fb.c177
1 files changed, 104 insertions, 73 deletions
diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c
index 8d34c5651187..8667c7769d8a 100644
--- a/drivers/video/fbdev/ssd1307fb.c
+++ b/drivers/video/fbdev/ssd1307fb.c
@@ -40,20 +40,34 @@
40 40
41struct ssd1307fb_par; 41struct ssd1307fb_par;
42 42
43struct ssd1307fb_ops { 43struct ssd1307fb_deviceinfo {
44 int (*init)(struct ssd1307fb_par *); 44 u32 default_vcomh;
45 int (*remove)(struct ssd1307fb_par *); 45 u32 default_dclk_div;
46 u32 default_dclk_frq;
47 int need_pwm;
48 int need_chargepump;
46}; 49};
47 50
48struct ssd1307fb_par { 51struct ssd1307fb_par {
52 u32 com_invdir;
53 u32 com_lrremap;
54 u32 com_offset;
55 u32 com_seq;
56 u32 contrast;
57 u32 dclk_div;
58 u32 dclk_frq;
59 struct ssd1307fb_deviceinfo *device_info;
49 struct i2c_client *client; 60 struct i2c_client *client;
50 u32 height; 61 u32 height;
51 struct fb_info *info; 62 struct fb_info *info;
52 struct ssd1307fb_ops *ops;
53 u32 page_offset; 63 u32 page_offset;
64 u32 prechargep1;
65 u32 prechargep2;
54 struct pwm_device *pwm; 66 struct pwm_device *pwm;
55 u32 pwm_period; 67 u32 pwm_period;
56 int reset; 68 int reset;
69 u32 seg_remap;
70 u32 vcomh;
57 u32 width; 71 u32 width;
58}; 72};
59 73
@@ -254,69 +268,46 @@ static struct fb_deferred_io ssd1307fb_defio = {
254 .deferred_io = ssd1307fb_deferred_io, 268 .deferred_io = ssd1307fb_deferred_io,
255}; 269};
256 270
257static int ssd1307fb_ssd1307_init(struct ssd1307fb_par *par) 271static int ssd1307fb_init(struct ssd1307fb_par *par)
258{ 272{
259 int ret; 273 int ret;
274 u32 precharge, dclk, com_invdir, compins;
260 275
261 par->pwm = pwm_get(&par->client->dev, NULL); 276 if (par->device_info->need_pwm) {
262 if (IS_ERR(par->pwm)) { 277 par->pwm = pwm_get(&par->client->dev, NULL);
263 dev_err(&par->client->dev, "Could not get PWM from device tree!\n"); 278 if (IS_ERR(par->pwm)) {
264 return PTR_ERR(par->pwm); 279 dev_err(&par->client->dev, "Could not get PWM from device tree!\n");
265 } 280 return PTR_ERR(par->pwm);
266 281 }
267 par->pwm_period = pwm_get_period(par->pwm);
268 /* Enable the PWM */
269 pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period);
270 pwm_enable(par->pwm);
271
272 dev_dbg(&par->client->dev, "Using PWM%d with a %dns period.\n",
273 par->pwm->pwm, par->pwm_period);
274
275 /* Map column 127 of the OLED to segment 0 */
276 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON);
277 if (ret < 0)
278 return ret;
279
280 /* Turn on the display */
281 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON);
282 if (ret < 0)
283 return ret;
284
285 return 0;
286}
287 282
288static int ssd1307fb_ssd1307_remove(struct ssd1307fb_par *par) 283 par->pwm_period = pwm_get_period(par->pwm);
289{ 284 /* Enable the PWM */
290 pwm_disable(par->pwm); 285 pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period);
291 pwm_put(par->pwm); 286 pwm_enable(par->pwm);
292 return 0;
293}
294 287
295static struct ssd1307fb_ops ssd1307fb_ssd1307_ops = { 288 dev_dbg(&par->client->dev, "Using PWM%d with a %dns period.\n",
296 .init = ssd1307fb_ssd1307_init, 289 par->pwm->pwm, par->pwm_period);
297 .remove = ssd1307fb_ssd1307_remove, 290 };
298};
299
300static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
301{
302 int ret;
303 291
304 /* Set initial contrast */ 292 /* Set initial contrast */
305 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CONTRAST); 293 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CONTRAST);
306 if (ret < 0) 294 if (ret < 0)
307 return ret; 295 return ret;
308 296
309 ret = ssd1307fb_write_cmd(par->client, 0x7f); 297 ret = ssd1307fb_write_cmd(par->client, par->contrast);
310 if (ret < 0)
311 return ret;
312
313 /* Set COM direction */
314 ret = ssd1307fb_write_cmd(par->client, 0xc8);
315 if (ret < 0) 298 if (ret < 0)
316 return ret; 299 return ret;
317 300
318 /* Set segment re-map */ 301 /* Set segment re-map */
319 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON); 302 if (par->seg_remap) {
303 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON);
304 if (ret < 0)
305 return ret;
306 };
307
308 /* Set COM direction */
309 com_invdir = 0xc0 | (par->com_invdir & 0x1) << 3;
310 ret = ssd1307fb_write_cmd(par->client, com_invdir);
320 if (ret < 0) 311 if (ret < 0)
321 return ret; 312 return ret;
322 313
@@ -334,7 +325,7 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
334 if (ret < 0) 325 if (ret < 0)
335 return ret; 326 return ret;
336 327
337 ret = ssd1307fb_write_cmd(par->client, 0x20); 328 ret = ssd1307fb_write_cmd(par->client, par->com_offset);
338 if (ret < 0) 329 if (ret < 0)
339 return ret; 330 return ret;
340 331
@@ -343,7 +334,8 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
343 if (ret < 0) 334 if (ret < 0)
344 return ret; 335 return ret;
345 336
346 ret = ssd1307fb_write_cmd(par->client, 0xf0); 337 dclk = ((par->dclk_div - 1) & 0xf) | (par->dclk_frq & 0xf) << 4;
338 ret = ssd1307fb_write_cmd(par->client, dclk);
347 if (ret < 0) 339 if (ret < 0)
348 return ret; 340 return ret;
349 341
@@ -352,7 +344,8 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
352 if (ret < 0) 344 if (ret < 0)
353 return ret; 345 return ret;
354 346
355 ret = ssd1307fb_write_cmd(par->client, 0x22); 347 precharge = (par->prechargep1 & 0xf) | (par->prechargep2 & 0xf) << 4;
348 ret = ssd1307fb_write_cmd(par->client, precharge);
356 if (ret < 0) 349 if (ret < 0)
357 return ret; 350 return ret;
358 351
@@ -361,7 +354,9 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
361 if (ret < 0) 354 if (ret < 0)
362 return ret; 355 return ret;
363 356
364 ret = ssd1307fb_write_cmd(par->client, 0x22); 357 compins = 0x02 | (!par->com_seq & 0x1) << 4
358 | (par->com_lrremap & 0x1) << 5;
359 ret = ssd1307fb_write_cmd(par->client, compins);
365 if (ret < 0) 360 if (ret < 0)
366 return ret; 361 return ret;
367 362
@@ -370,7 +365,7 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
370 if (ret < 0) 365 if (ret < 0)
371 return ret; 366 return ret;
372 367
373 ret = ssd1307fb_write_cmd(par->client, 0x49); 368 ret = ssd1307fb_write_cmd(par->client, par->vcomh);
374 if (ret < 0) 369 if (ret < 0)
375 return ret; 370 return ret;
376 371
@@ -379,7 +374,8 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
379 if (ret < 0) 374 if (ret < 0)
380 return ret; 375 return ret;
381 376
382 ret = ssd1307fb_write_cmd(par->client, 0x14); 377 ret = ssd1307fb_write_cmd(par->client,
378 (par->device_info->need_chargepump & 0x1 << 2) & 0x14);
383 if (ret < 0) 379 if (ret < 0)
384 return ret; 380 return ret;
385 381
@@ -393,6 +389,7 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
393 if (ret < 0) 389 if (ret < 0)
394 return ret; 390 return ret;
395 391
392 /* Set column range */
396 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COL_RANGE); 393 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COL_RANGE);
397 if (ret < 0) 394 if (ret < 0)
398 return ret; 395 return ret;
@@ -405,6 +402,7 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
405 if (ret < 0) 402 if (ret < 0)
406 return ret; 403 return ret;
407 404
405 /* Set page range */
408 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PAGE_RANGE); 406 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PAGE_RANGE);
409 if (ret < 0) 407 if (ret < 0)
410 return ret; 408 return ret;
@@ -426,18 +424,28 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
426 return 0; 424 return 0;
427} 425}
428 426
429static struct ssd1307fb_ops ssd1307fb_ssd1306_ops = { 427static struct ssd1307fb_deviceinfo ssd1307fb_ssd1306_deviceinfo = {
430 .init = ssd1307fb_ssd1306_init, 428 .default_vcomh = 0x20,
429 .default_dclk_div = 1,
430 .default_dclk_frq = 8,
431 .need_chargepump = 1,
432};
433
434static struct ssd1307fb_deviceinfo ssd1307fb_ssd1307_deviceinfo = {
435 .default_vcomh = 0x20,
436 .default_dclk_div = 2,
437 .default_dclk_frq = 12,
438 .need_pwm = 1,
431}; 439};
432 440
433static const struct of_device_id ssd1307fb_of_match[] = { 441static const struct of_device_id ssd1307fb_of_match[] = {
434 { 442 {
435 .compatible = "solomon,ssd1306fb-i2c", 443 .compatible = "solomon,ssd1306fb-i2c",
436 .data = (void *)&ssd1307fb_ssd1306_ops, 444 .data = (void *)&ssd1307fb_ssd1306_deviceinfo,
437 }, 445 },
438 { 446 {
439 .compatible = "solomon,ssd1307fb-i2c", 447 .compatible = "solomon,ssd1307fb-i2c",
440 .data = (void *)&ssd1307fb_ssd1307_ops, 448 .data = (void *)&ssd1307fb_ssd1307_deviceinfo,
441 }, 449 },
442 {}, 450 {},
443}; 451};
@@ -468,8 +476,8 @@ static int ssd1307fb_probe(struct i2c_client *client,
468 par->info = info; 476 par->info = info;
469 par->client = client; 477 par->client = client;
470 478
471 par->ops = (struct ssd1307fb_ops *)of_match_device(ssd1307fb_of_match, 479 par->device_info = (struct ssd1307fb_deviceinfo *)of_match_device(
472 &client->dev)->data; 480 ssd1307fb_of_match, &client->dev)->data;
473 481
474 par->reset = of_get_named_gpio(client->dev.of_node, 482 par->reset = of_get_named_gpio(client->dev.of_node,
475 "reset-gpios", 0); 483 "reset-gpios", 0);
@@ -487,6 +495,27 @@ static int ssd1307fb_probe(struct i2c_client *client,
487 if (of_property_read_u32(node, "solomon,page-offset", &par->page_offset)) 495 if (of_property_read_u32(node, "solomon,page-offset", &par->page_offset))
488 par->page_offset = 1; 496 par->page_offset = 1;
489 497
498 if (of_property_read_u32(node, "solomon,com-offset", &par->com_offset))
499 par->com_offset = 0;
500
501 if (of_property_read_u32(node, "solomon,prechargep1", &par->prechargep1))
502 par->prechargep1 = 2;
503
504 if (of_property_read_u32(node, "solomon,prechargep2", &par->prechargep2))
505 par->prechargep2 = 2;
506
507 par->seg_remap = !of_property_read_bool(node, "solomon,segment-no-remap");
508 par->com_seq = of_property_read_bool(node, "solomon,com-seq");
509 par->com_lrremap = of_property_read_bool(node, "solomon,com-lrremap");
510 par->com_invdir = of_property_read_bool(node, "solomon,com-invdir");
511
512 par->contrast = 127;
513 par->vcomh = par->device_info->default_vcomh;
514
515 /* Setup display timing */
516 par->dclk_div = par->device_info->default_dclk_div;
517 par->dclk_frq = par->device_info->default_dclk_frq;
518
490 vmem_size = par->width * par->height / 8; 519 vmem_size = par->width * par->height / 8;
491 520
492 vmem = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 521 vmem = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
@@ -539,11 +568,9 @@ static int ssd1307fb_probe(struct i2c_client *client,
539 gpio_set_value(par->reset, 1); 568 gpio_set_value(par->reset, 1);
540 udelay(4); 569 udelay(4);
541 570
542 if (par->ops->init) { 571 ret = ssd1307fb_init(par);
543 ret = par->ops->init(par); 572 if (ret)
544 if (ret) 573 goto reset_oled_error;
545 goto reset_oled_error;
546 }
547 574
548 ret = register_framebuffer(info); 575 ret = register_framebuffer(info);
549 if (ret) { 576 if (ret) {
@@ -556,8 +583,10 @@ static int ssd1307fb_probe(struct i2c_client *client,
556 return 0; 583 return 0;
557 584
558panel_init_error: 585panel_init_error:
559 if (par->ops->remove) 586 if (par->device_info->need_pwm) {
560 par->ops->remove(par); 587 pwm_disable(par->pwm);
588 pwm_put(par->pwm);
589 };
561reset_oled_error: 590reset_oled_error:
562 fb_deferred_io_cleanup(info); 591 fb_deferred_io_cleanup(info);
563fb_alloc_error: 592fb_alloc_error:
@@ -571,8 +600,10 @@ static int ssd1307fb_remove(struct i2c_client *client)
571 struct ssd1307fb_par *par = info->par; 600 struct ssd1307fb_par *par = info->par;
572 601
573 unregister_framebuffer(info); 602 unregister_framebuffer(info);
574 if (par->ops->remove) 603 if (par->device_info->need_pwm) {
575 par->ops->remove(par); 604 pwm_disable(par->pwm);
605 pwm_put(par->pwm);
606 };
576 fb_deferred_io_cleanup(info); 607 fb_deferred_io_cleanup(info);
577 __free_pages(__va(info->fix.smem_start), get_order(info->fix.smem_len)); 608 __free_pages(__va(info->fix.smem_start), get_order(info->fix.smem_len));
578 framebuffer_release(info); 609 framebuffer_release(info);