aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMaxime Ripard <maxime.ripard@free-electrons.com>2013-04-22 05:55:54 -0400
committerTomi Valkeinen <tomi.valkeinen@ti.com>2013-05-28 07:41:57 -0400
commitbbc79089ae2bd0306db7f8dce85d56f9be65b205 (patch)
tree4b30423a2a1259d6e6d83eced2e0dc5da1fd5ae8
parentf722406faae2d073cc1d01063d1123c35425939e (diff)
video: ssd1307fb: Add support for SSD1306 OLED controller
The Solomon SSD1306 OLED controller is very similar to the SSD1307, except for the fact that the power is given through an external PWM for the 1307, and while the 1306 can generate its own power without any PWM. Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
-rw-r--r--Documentation/devicetree/bindings/video/ssd1307fb.txt10
-rw-r--r--drivers/video/ssd1307fb.c273
2 files changed, 209 insertions, 74 deletions
diff --git a/Documentation/devicetree/bindings/video/ssd1307fb.txt b/Documentation/devicetree/bindings/video/ssd1307fb.txt
index 3d0060cff062..7a125427ff4b 100644
--- a/Documentation/devicetree/bindings/video/ssd1307fb.txt
+++ b/Documentation/devicetree/bindings/video/ssd1307fb.txt
@@ -1,13 +1,17 @@
1* Solomon SSD1307 Framebuffer Driver 1* Solomon SSD1307 Framebuffer Driver
2 2
3Required properties: 3Required properties:
4 - compatible: Should be "solomon,ssd1307fb-<bus>". The only supported bus for 4 - compatible: Should be "solomon,<chip>fb-<bus>". The only supported bus for
5 now is i2c. 5 now is i2c, and the supported chips are ssd1306 and ssd1307.
6 - reg: Should contain address of the controller on the I2C bus. Most likely 6 - reg: Should contain address of the controller on the I2C bus. Most likely
7 0x3c or 0x3d 7 0x3c or 0x3d
8 - pwm: Should contain the pwm to use according to the OF device tree PWM 8 - pwm: Should contain the pwm to use according to the OF device tree PWM
9 specification [0] 9 specification [0]. Only required for the ssd1307.
10 - reset-gpios: Should contain the GPIO used to reset the OLED display 10 - reset-gpios: Should contain the GPIO used to reset the OLED display
11 - solomon,height: Height in pixel of the screen driven by the controller
12 - solomon,width: Width in pixel of the screen driven by the controller
13 - solomon,page-offset: Offset of pages (band of 8 pixels) that the screen is
14 mapped to.
11 15
12Optional properties: 16Optional properties:
13 - reset-active-low: Is the reset gpio is active on physical low? 17 - reset-active-low: Is the reset gpio is active on physical low?
diff --git a/drivers/video/ssd1307fb.c b/drivers/video/ssd1307fb.c
index 9ef05d3ef68a..a0d6f96ec4e4 100644
--- a/drivers/video/ssd1307fb.c
+++ b/drivers/video/ssd1307fb.c
@@ -16,24 +16,39 @@
16#include <linux/pwm.h> 16#include <linux/pwm.h>
17#include <linux/delay.h> 17#include <linux/delay.h>
18 18
19#define SSD1307FB_WIDTH 96
20#define SSD1307FB_HEIGHT 16
21
22#define SSD1307FB_DATA 0x40 19#define SSD1307FB_DATA 0x40
23#define SSD1307FB_COMMAND 0x80 20#define SSD1307FB_COMMAND 0x80
24 21
25#define SSD1307FB_CONTRAST 0x81 22#define SSD1307FB_CONTRAST 0x81
23#define SSD1307FB_CHARGE_PUMP 0x8d
26#define SSD1307FB_SEG_REMAP_ON 0xa1 24#define SSD1307FB_SEG_REMAP_ON 0xa1
27#define SSD1307FB_DISPLAY_OFF 0xae 25#define SSD1307FB_DISPLAY_OFF 0xae
26#define SSD1307FB_SET_MULTIPLEX_RATIO 0xa8
28#define SSD1307FB_DISPLAY_ON 0xaf 27#define SSD1307FB_DISPLAY_ON 0xaf
29#define SSD1307FB_START_PAGE_ADDRESS 0xb0 28#define SSD1307FB_START_PAGE_ADDRESS 0xb0
29#define SSD1307FB_SET_DISPLAY_OFFSET 0xd3
30#define SSD1307FB_SET_CLOCK_FREQ 0xd5
31#define SSD1307FB_SET_PRECHARGE_PERIOD 0xd9
32#define SSD1307FB_SET_COM_PINS_CONFIG 0xda
33#define SSD1307FB_SET_VCOMH 0xdb
34
35struct ssd1307fb_par;
36
37struct ssd1307fb_ops {
38 int (*init)(struct ssd1307fb_par *);
39 int (*remove)(struct ssd1307fb_par *);
40};
30 41
31struct ssd1307fb_par { 42struct ssd1307fb_par {
32 struct i2c_client *client; 43 struct i2c_client *client;
44 u32 height;
33 struct fb_info *info; 45 struct fb_info *info;
46 struct ssd1307fb_ops *ops;
47 u32 page_offset;
34 struct pwm_device *pwm; 48 struct pwm_device *pwm;
35 u32 pwm_period; 49 u32 pwm_period;
36 int reset; 50 int reset;
51 u32 width;
37}; 52};
38 53
39static struct fb_fix_screeninfo ssd1307fb_fix = { 54static struct fb_fix_screeninfo ssd1307fb_fix = {
@@ -43,15 +58,10 @@ static struct fb_fix_screeninfo ssd1307fb_fix = {
43 .xpanstep = 0, 58 .xpanstep = 0,
44 .ypanstep = 0, 59 .ypanstep = 0,
45 .ywrapstep = 0, 60 .ywrapstep = 0,
46 .line_length = SSD1307FB_WIDTH / 8,
47 .accel = FB_ACCEL_NONE, 61 .accel = FB_ACCEL_NONE,
48}; 62};
49 63
50static struct fb_var_screeninfo ssd1307fb_var = { 64static struct fb_var_screeninfo ssd1307fb_var = {
51 .xres = SSD1307FB_WIDTH,
52 .yres = SSD1307FB_HEIGHT,
53 .xres_virtual = SSD1307FB_WIDTH,
54 .yres_virtual = SSD1307FB_HEIGHT,
55 .bits_per_pixel = 1, 65 .bits_per_pixel = 1,
56}; 66};
57 67
@@ -134,16 +144,17 @@ static void ssd1307fb_update_display(struct ssd1307fb_par *par)
134 * (5) A4 B4 C4 D4 E4 F4 G4 H4 144 * (5) A4 B4 C4 D4 E4 F4 G4 H4
135 */ 145 */
136 146
137 for (i = 0; i < (SSD1307FB_HEIGHT / 8); i++) { 147 for (i = 0; i < (par->height / 8); i++) {
138 ssd1307fb_write_cmd(par->client, SSD1307FB_START_PAGE_ADDRESS + (i + 1)); 148 ssd1307fb_write_cmd(par->client,
149 SSD1307FB_START_PAGE_ADDRESS + i + par->page_offset);
139 ssd1307fb_write_cmd(par->client, 0x00); 150 ssd1307fb_write_cmd(par->client, 0x00);
140 ssd1307fb_write_cmd(par->client, 0x10); 151 ssd1307fb_write_cmd(par->client, 0x10);
141 152
142 for (j = 0; j < SSD1307FB_WIDTH; j++) { 153 for (j = 0; j < par->width; j++) {
143 u8 buf = 0; 154 u8 buf = 0;
144 for (k = 0; k < 8; k++) { 155 for (k = 0; k < 8; k++) {
145 u32 page_length = SSD1307FB_WIDTH * i; 156 u32 page_length = par->width * i;
146 u32 index = page_length + (SSD1307FB_WIDTH * k + j) / 8; 157 u32 index = page_length + (par->width * k + j) / 8;
147 u8 byte = *(vmem + index); 158 u8 byte = *(vmem + index);
148 u8 bit = byte & (1 << (j % 8)); 159 u8 bit = byte & (1 << (j % 8));
149 bit = bit >> (j % 8); 160 bit = bit >> (j % 8);
@@ -227,16 +238,147 @@ static struct fb_deferred_io ssd1307fb_defio = {
227 .deferred_io = ssd1307fb_deferred_io, 238 .deferred_io = ssd1307fb_deferred_io,
228}; 239};
229 240
241static int ssd1307fb_ssd1307_init(struct ssd1307fb_par *par)
242{
243 int ret;
244
245 par->pwm = pwm_get(&par->client->dev, NULL);
246 if (IS_ERR(par->pwm)) {
247 dev_err(&par->client->dev, "Could not get PWM from device tree!\n");
248 return PTR_ERR(par->pwm);
249 }
250
251 par->pwm_period = pwm_get_period(par->pwm);
252 /* Enable the PWM */
253 pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period);
254 pwm_enable(par->pwm);
255
256 dev_dbg(&par->client->dev, "Using PWM%d with a %dns period.\n",
257 par->pwm->pwm, par->pwm_period);
258
259 /* Map column 127 of the OLED to segment 0 */
260 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON);
261 if (ret < 0)
262 return ret;
263
264 /* Turn on the display */
265 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON);
266 if (ret < 0)
267 return ret;
268
269 return 0;
270}
271
272static int ssd1307fb_ssd1307_remove(struct ssd1307fb_par *par)
273{
274 pwm_disable(par->pwm);
275 pwm_put(par->pwm);
276 return 0;
277}
278
279static struct ssd1307fb_ops ssd1307fb_ssd1307_ops = {
280 .init = ssd1307fb_ssd1307_init,
281 .remove = ssd1307fb_ssd1307_remove,
282};
283
284static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
285{
286 int ret;
287
288 /* Set initial contrast */
289 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CONTRAST);
290 ret = ret & ssd1307fb_write_cmd(par->client, 0x7f);
291 if (ret < 0)
292 return ret;
293
294 /* Set COM direction */
295 ret = ssd1307fb_write_cmd(par->client, 0xc8);
296 if (ret < 0)
297 return ret;
298
299 /* Set segment re-map */
300 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON);
301 if (ret < 0)
302 return ret;
303
304 /* Set multiplex ratio value */
305 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_MULTIPLEX_RATIO);
306 ret = ret & ssd1307fb_write_cmd(par->client, par->height - 1);
307 if (ret < 0)
308 return ret;
309
310 /* set display offset value */
311 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_DISPLAY_OFFSET);
312 ret = ssd1307fb_write_cmd(par->client, 0x20);
313 if (ret < 0)
314 return ret;
315
316 /* Set clock frequency */
317 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_CLOCK_FREQ);
318 ret = ret & ssd1307fb_write_cmd(par->client, 0xf0);
319 if (ret < 0)
320 return ret;
321
322 /* Set precharge period in number of ticks from the internal clock */
323 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PRECHARGE_PERIOD);
324 ret = ret & ssd1307fb_write_cmd(par->client, 0x22);
325 if (ret < 0)
326 return ret;
327
328 /* Set COM pins configuration */
329 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COM_PINS_CONFIG);
330 ret = ret & ssd1307fb_write_cmd(par->client, 0x22);
331 if (ret < 0)
332 return ret;
333
334 /* Set VCOMH */
335 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_VCOMH);
336 ret = ret & ssd1307fb_write_cmd(par->client, 0x49);
337 if (ret < 0)
338 return ret;
339
340 /* Turn on the DC-DC Charge Pump */
341 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CHARGE_PUMP);
342 ret = ret & ssd1307fb_write_cmd(par->client, 0x14);
343 if (ret < 0)
344 return ret;
345
346 /* Turn on the display */
347 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON);
348 if (ret < 0)
349 return ret;
350
351 return 0;
352}
353
354static struct ssd1307fb_ops ssd1307fb_ssd1306_ops = {
355 .init = ssd1307fb_ssd1306_init,
356};
357
358static const struct of_device_id ssd1307fb_of_match[] = {
359 {
360 .compatible = "solomon,ssd1306fb-i2c",
361 .data = (void *)&ssd1307fb_ssd1306_ops,
362 },
363 {
364 .compatible = "solomon,ssd1307fb-i2c",
365 .data = (void *)&ssd1307fb_ssd1307_ops,
366 },
367 {},
368};
369MODULE_DEVICE_TABLE(of, ssd1307fb_of_match);
370
230static int ssd1307fb_probe(struct i2c_client *client, 371static int ssd1307fb_probe(struct i2c_client *client,
231 const struct i2c_device_id *id) 372 const struct i2c_device_id *id)
232{ 373{
233 struct fb_info *info; 374 struct fb_info *info;
234 u32 vmem_size = SSD1307FB_WIDTH * SSD1307FB_HEIGHT / 8; 375 struct device_node *node = client->dev.of_node;
376 u32 vmem_size;
235 struct ssd1307fb_par *par; 377 struct ssd1307fb_par *par;
236 u8 *vmem; 378 u8 *vmem;
237 int ret; 379 int ret;
238 380
239 if (!client->dev.of_node) { 381 if (!node) {
240 dev_err(&client->dev, "No device tree data found!\n"); 382 dev_err(&client->dev, "No device tree data found!\n");
241 return -EINVAL; 383 return -EINVAL;
242 } 384 }
@@ -247,6 +389,31 @@ static int ssd1307fb_probe(struct i2c_client *client,
247 return -ENOMEM; 389 return -ENOMEM;
248 } 390 }
249 391
392 par = info->par;
393 par->info = info;
394 par->client = client;
395
396 par->ops = (struct ssd1307fb_ops *)of_match_device(ssd1307fb_of_match,
397 &client->dev)->data;
398
399 par->reset = of_get_named_gpio(client->dev.of_node,
400 "reset-gpios", 0);
401 if (!gpio_is_valid(par->reset)) {
402 ret = -EINVAL;
403 goto fb_alloc_error;
404 }
405
406 if (of_property_read_u32(node, "solomon,width", &par->width))
407 par->width = 96;
408
409 if (of_property_read_u32(node, "solomon,height", &par->height))
410 par->width = 16;
411
412 if (of_property_read_u32(node, "solomon,page-offset", &par->page_offset))
413 par->page_offset = 1;
414
415 vmem_size = par->width * par->height / 8;
416
250 vmem = devm_kzalloc(&client->dev, vmem_size, GFP_KERNEL); 417 vmem = devm_kzalloc(&client->dev, vmem_size, GFP_KERNEL);
251 if (!vmem) { 418 if (!vmem) {
252 dev_err(&client->dev, "Couldn't allocate graphical memory.\n"); 419 dev_err(&client->dev, "Couldn't allocate graphical memory.\n");
@@ -256,9 +423,15 @@ static int ssd1307fb_probe(struct i2c_client *client,
256 423
257 info->fbops = &ssd1307fb_ops; 424 info->fbops = &ssd1307fb_ops;
258 info->fix = ssd1307fb_fix; 425 info->fix = ssd1307fb_fix;
426 info->fix.line_length = par->width / 8;
259 info->fbdefio = &ssd1307fb_defio; 427 info->fbdefio = &ssd1307fb_defio;
260 428
261 info->var = ssd1307fb_var; 429 info->var = ssd1307fb_var;
430 info->var.xres = par->width;
431 info->var.xres_virtual = par->width;
432 info->var.yres = par->height;
433 info->var.yres_virtual = par->height;
434
262 info->var.red.length = 1; 435 info->var.red.length = 1;
263 info->var.red.offset = 0; 436 info->var.red.offset = 0;
264 info->var.green.length = 1; 437 info->var.green.length = 1;
@@ -272,17 +445,6 @@ static int ssd1307fb_probe(struct i2c_client *client,
272 445
273 fb_deferred_io_init(info); 446 fb_deferred_io_init(info);
274 447
275 par = info->par;
276 par->info = info;
277 par->client = client;
278
279 par->reset = of_get_named_gpio(client->dev.of_node,
280 "reset-gpios", 0);
281 if (!gpio_is_valid(par->reset)) {
282 ret = -EINVAL;
283 goto reset_oled_error;
284 }
285
286 ret = devm_gpio_request_one(&client->dev, par->reset, 448 ret = devm_gpio_request_one(&client->dev, par->reset,
287 GPIOF_OUT_INIT_HIGH, 449 GPIOF_OUT_INIT_HIGH,
288 "oled-reset"); 450 "oled-reset");
@@ -293,23 +455,6 @@ static int ssd1307fb_probe(struct i2c_client *client,
293 goto reset_oled_error; 455 goto reset_oled_error;
294 } 456 }
295 457
296 par->pwm = pwm_get(&client->dev, NULL);
297 if (IS_ERR(par->pwm)) {
298 dev_err(&client->dev, "Could not get PWM from device tree!\n");
299 ret = PTR_ERR(par->pwm);
300 goto pwm_error;
301 }
302
303 par->pwm_period = pwm_get_period(par->pwm);
304
305 dev_dbg(&client->dev, "Using PWM%d with a %dns period.\n", par->pwm->pwm, par->pwm_period);
306
307 ret = register_framebuffer(info);
308 if (ret) {
309 dev_err(&client->dev, "Couldn't register the framebuffer\n");
310 goto fbreg_error;
311 }
312
313 i2c_set_clientdata(client, info); 458 i2c_set_clientdata(client, info);
314 459
315 /* Reset the screen */ 460 /* Reset the screen */
@@ -318,34 +463,25 @@ static int ssd1307fb_probe(struct i2c_client *client,
318 gpio_set_value(par->reset, 1); 463 gpio_set_value(par->reset, 1);
319 udelay(4); 464 udelay(4);
320 465
321 /* Enable the PWM */ 466 if (par->ops->init) {
322 pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period); 467 ret = par->ops->init(par);
323 pwm_enable(par->pwm); 468 if (ret)
324 469 goto reset_oled_error;
325 /* Map column 127 of the OLED to segment 0 */
326 ret = ssd1307fb_write_cmd(client, SSD1307FB_SEG_REMAP_ON);
327 if (ret < 0) {
328 dev_err(&client->dev, "Couldn't remap the screen.\n");
329 goto remap_error;
330 } 470 }
331 471
332 /* Turn on the display */ 472 ret = register_framebuffer(info);
333 ret = ssd1307fb_write_cmd(client, SSD1307FB_DISPLAY_ON); 473 if (ret) {
334 if (ret < 0) { 474 dev_err(&client->dev, "Couldn't register the framebuffer\n");
335 dev_err(&client->dev, "Couldn't turn the display on.\n"); 475 goto panel_init_error;
336 goto remap_error;
337 } 476 }
338 477
339 dev_info(&client->dev, "fb%d: %s framebuffer device registered, using %d bytes of video memory\n", info->node, info->fix.id, vmem_size); 478 dev_info(&client->dev, "fb%d: %s framebuffer device registered, using %d bytes of video memory\n", info->node, info->fix.id, vmem_size);
340 479
341 return 0; 480 return 0;
342 481
343remap_error: 482panel_init_error:
344 unregister_framebuffer(info); 483 if (par->ops->remove)
345 pwm_disable(par->pwm); 484 par->ops->remove(par);
346fbreg_error:
347 pwm_put(par->pwm);
348pwm_error:
349reset_oled_error: 485reset_oled_error:
350 fb_deferred_io_cleanup(info); 486 fb_deferred_io_cleanup(info);
351fb_alloc_error: 487fb_alloc_error:
@@ -359,8 +495,8 @@ static int ssd1307fb_remove(struct i2c_client *client)
359 struct ssd1307fb_par *par = info->par; 495 struct ssd1307fb_par *par = info->par;
360 496
361 unregister_framebuffer(info); 497 unregister_framebuffer(info);
362 pwm_disable(par->pwm); 498 if (par->ops->remove)
363 pwm_put(par->pwm); 499 par->ops->remove(par);
364 fb_deferred_io_cleanup(info); 500 fb_deferred_io_cleanup(info);
365 framebuffer_release(info); 501 framebuffer_release(info);
366 502
@@ -368,17 +504,12 @@ static int ssd1307fb_remove(struct i2c_client *client)
368} 504}
369 505
370static const struct i2c_device_id ssd1307fb_i2c_id[] = { 506static const struct i2c_device_id ssd1307fb_i2c_id[] = {
507 { "ssd1306fb", 0 },
371 { "ssd1307fb", 0 }, 508 { "ssd1307fb", 0 },
372 { } 509 { }
373}; 510};
374MODULE_DEVICE_TABLE(i2c, ssd1307fb_i2c_id); 511MODULE_DEVICE_TABLE(i2c, ssd1307fb_i2c_id);
375 512
376static const struct of_device_id ssd1307fb_of_match[] = {
377 { .compatible = "solomon,ssd1307fb-i2c" },
378 {},
379};
380MODULE_DEVICE_TABLE(of, ssd1307fb_of_match);
381
382static struct i2c_driver ssd1307fb_driver = { 513static struct i2c_driver ssd1307fb_driver = {
383 .probe = ssd1307fb_probe, 514 .probe = ssd1307fb_probe,
384 .remove = ssd1307fb_remove, 515 .remove = ssd1307fb_remove,