1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Driver for the Solomon SSD1307 OLED controller
4 *
5 * Copyright 2012 Free Electrons
6 */
7
8 #include <linux/backlight.h>
9 #include <linux/delay.h>
10 #include <linux/fb.h>
11 #include <linux/gpio/consumer.h>
12 #include <linux/i2c.h>
13 #include <linux/kernel.h>
14 #include <linux/module.h>
15 #include <linux/property.h>
16 #include <linux/pwm.h>
17 #include <linux/spinlock.h>
18 #include <linux/uaccess.h>
19 #include <linux/regulator/consumer.h>
20
21 #define SSD1307FB_DATA 0x40
22 #define SSD1307FB_COMMAND 0x80
23
24 #define SSD1307FB_SET_ADDRESS_MODE 0x20
25 #define SSD1307FB_SET_ADDRESS_MODE_HORIZONTAL (0x00)
26 #define SSD1307FB_SET_ADDRESS_MODE_VERTICAL (0x01)
27 #define SSD1307FB_SET_ADDRESS_MODE_PAGE (0x02)
28 #define SSD1307FB_SET_COL_RANGE 0x21
29 #define SSD1307FB_SET_PAGE_RANGE 0x22
30 #define SSD1307FB_CONTRAST 0x81
31 #define SSD1307FB_SET_LOOKUP_TABLE 0x91
32 #define SSD1307FB_CHARGE_PUMP 0x8d
33 #define SSD1307FB_SEG_REMAP_ON 0xa1
34 #define SSD1307FB_DISPLAY_OFF 0xae
35 #define SSD1307FB_SET_MULTIPLEX_RATIO 0xa8
36 #define SSD1307FB_DISPLAY_ON 0xaf
37 #define SSD1307FB_START_PAGE_ADDRESS 0xb0
38 #define SSD1307FB_SET_DISPLAY_OFFSET 0xd3
39 #define SSD1307FB_SET_CLOCK_FREQ 0xd5
40 #define SSD1307FB_SET_AREA_COLOR_MODE 0xd8
41 #define SSD1307FB_SET_PRECHARGE_PERIOD 0xd9
42 #define SSD1307FB_SET_COM_PINS_CONFIG 0xda
43 #define SSD1307FB_SET_VCOMH 0xdb
44
45 #define MAX_CONTRAST 255
46
47 #define REFRESHRATE 1
48
49 static u_int refreshrate = REFRESHRATE;
50 module_param(refreshrate, uint, 0);
51
52 struct ssd1307fb_deviceinfo {
53 u32 default_vcomh;
54 u32 default_dclk_div;
55 u32 default_dclk_frq;
56 bool need_pwm;
57 bool need_chargepump;
58 };
59
60 struct ssd1307fb_par {
61 unsigned area_color_enable : 1;
62 unsigned com_invdir : 1;
63 unsigned com_lrremap : 1;
64 unsigned com_seq : 1;
65 unsigned lookup_table_set : 1;
66 unsigned low_power : 1;
67 unsigned seg_remap : 1;
68 u32 com_offset;
69 u32 contrast;
70 u32 dclk_div;
71 u32 dclk_frq;
72 const struct ssd1307fb_deviceinfo *device_info;
73 struct i2c_client *client;
74 u32 height;
75 struct fb_info *info;
76 /* Pending damage, with exclusive x2/y2, protected by damage_lock. */
77 spinlock_t damage_lock;
78 bool damage_pending;
79 u32 damage_x1;
80 u32 damage_x2;
81 u32 damage_y1;
82 u32 damage_y2;
83 u8 lookup_table[4];
84 u32 page_offset;
85 u32 col_offset;
86 u32 prechargep1;
87 u32 prechargep2;
88 struct pwm_device *pwm;
89 struct gpio_desc *reset;
90 struct regulator *vbat_reg;
91 u32 vcomh;
92 u32 width;
93 /* Cached address ranges */
94 u8 col_start;
95 u8 col_end;
96 u8 page_start;
97 u8 page_end;
98 };
99
100 struct ssd1307fb_array {
101 u8 type;
102 u8 data[];
103 };
104
105 static const struct fb_fix_screeninfo ssd1307fb_fix = {
106 .id = "Solomon SSD1307",
107 .type = FB_TYPE_PACKED_PIXELS,
108 .visual = FB_VISUAL_MONO10,
109 .xpanstep = 0,
110 .ypanstep = 0,
111 .ywrapstep = 0,
112 .accel = FB_ACCEL_NONE,
113 };
114
115 static const struct fb_var_screeninfo ssd1307fb_var = {
116 .bits_per_pixel = 1,
117 .red = { .length = 1 },
118 .green = { .length = 1 },
119 .blue = { .length = 1 },
120 };
121
ssd1307fb_alloc_array(u32 len,u8 type)122 static struct ssd1307fb_array *ssd1307fb_alloc_array(u32 len, u8 type)
123 {
124 struct ssd1307fb_array *array;
125
126 array = kzalloc(sizeof(struct ssd1307fb_array) + len, GFP_KERNEL);
127 if (!array)
128 return NULL;
129
130 array->type = type;
131
132 return array;
133 }
134
ssd1307fb_write_array(struct i2c_client * client,struct ssd1307fb_array * array,u32 len)135 static int ssd1307fb_write_array(struct i2c_client *client,
136 struct ssd1307fb_array *array, u32 len)
137 {
138 int ret;
139
140 len += sizeof(struct ssd1307fb_array);
141
142 ret = i2c_master_send(client, (u8 *)array, len);
143 if (ret != len) {
144 dev_err(&client->dev, "Couldn't send I2C command.\n");
145 return ret;
146 }
147
148 return 0;
149 }
150
ssd1307fb_write_cmd(struct i2c_client * client,u8 cmd)151 static inline int ssd1307fb_write_cmd(struct i2c_client *client, u8 cmd)
152 {
153 struct ssd1307fb_array *array;
154 int ret;
155
156 array = ssd1307fb_alloc_array(1, SSD1307FB_COMMAND);
157 if (!array)
158 return -ENOMEM;
159
160 array->data[0] = cmd;
161
162 ret = ssd1307fb_write_array(client, array, 1);
163 kfree(array);
164
165 return ret;
166 }
167
ssd1307fb_set_col_range(struct ssd1307fb_par * par,u8 col_start,u8 cols)168 static int ssd1307fb_set_col_range(struct ssd1307fb_par *par, u8 col_start,
169 u8 cols)
170 {
171 u8 col_end = col_start + cols - 1;
172 int ret;
173
174 if (col_start == par->col_start && col_end == par->col_end)
175 return 0;
176
177 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COL_RANGE);
178 if (ret < 0)
179 return ret;
180
181 ret = ssd1307fb_write_cmd(par->client, col_start);
182 if (ret < 0)
183 return ret;
184
185 ret = ssd1307fb_write_cmd(par->client, col_end);
186 if (ret < 0)
187 return ret;
188
189 par->col_start = col_start;
190 par->col_end = col_end;
191 return 0;
192 }
193
ssd1307fb_set_page_range(struct ssd1307fb_par * par,u8 page_start,u8 pages)194 static int ssd1307fb_set_page_range(struct ssd1307fb_par *par, u8 page_start,
195 u8 pages)
196 {
197 u8 page_end = page_start + pages - 1;
198 int ret;
199
200 if (page_start == par->page_start && page_end == par->page_end)
201 return 0;
202
203 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PAGE_RANGE);
204 if (ret < 0)
205 return ret;
206
207 ret = ssd1307fb_write_cmd(par->client, page_start);
208 if (ret < 0)
209 return ret;
210
211 ret = ssd1307fb_write_cmd(par->client, page_end);
212 if (ret < 0)
213 return ret;
214
215 par->page_start = page_start;
216 par->page_end = page_end;
217 return 0;
218 }
219
ssd1307fb_update_rect(struct ssd1307fb_par * par,unsigned int x,unsigned int y,unsigned int width,unsigned int height)220 static int ssd1307fb_update_rect(struct ssd1307fb_par *par, unsigned int x,
221 unsigned int y, unsigned int width,
222 unsigned int height)
223 {
224 struct ssd1307fb_array *array;
225 u8 *vmem = par->info->screen_buffer;
226 unsigned int line_length = par->info->fix.line_length;
227 unsigned int pages = DIV_ROUND_UP(y % 8 + height, 8);
228 u32 array_idx = 0;
229 int ret, i, j, k;
230
231 array = ssd1307fb_alloc_array(width * pages, SSD1307FB_DATA);
232 if (!array)
233 return -ENOMEM;
234
235 /*
236 * The screen is divided in pages, each having a height of 8
237 * pixels, and the width of the screen. When sending a byte of
238 * data to the controller, it gives the 8 bits for the current
239 * column. I.e, the first byte are the 8 bits of the first
240 * column, then the 8 bits for the second column, etc.
241 *
242 *
243 * Representation of the screen, assuming it is 5 bits
244 * wide. Each letter-number combination is a bit that controls
245 * one pixel.
246 *
247 * A0 A1 A2 A3 A4
248 * B0 B1 B2 B3 B4
249 * C0 C1 C2 C3 C4
250 * D0 D1 D2 D3 D4
251 * E0 E1 E2 E3 E4
252 * F0 F1 F2 F3 F4
253 * G0 G1 G2 G3 G4
254 * H0 H1 H2 H3 H4
255 *
256 * If you want to update this screen, you need to send 5 bytes:
257 * (1) A0 B0 C0 D0 E0 F0 G0 H0
258 * (2) A1 B1 C1 D1 E1 F1 G1 H1
259 * (3) A2 B2 C2 D2 E2 F2 G2 H2
260 * (4) A3 B3 C3 D3 E3 F3 G3 H3
261 * (5) A4 B4 C4 D4 E4 F4 G4 H4
262 */
263
264 ret = ssd1307fb_set_col_range(par, par->col_offset + x, width);
265 if (ret < 0)
266 goto out_free;
267
268 ret = ssd1307fb_set_page_range(par, par->page_offset + y / 8, pages);
269 if (ret < 0)
270 goto out_free;
271
272 for (i = y / 8; i < y / 8 + pages; i++) {
273 int m = 8;
274
275 /* Last page may be partial */
276 if (8 * (i + 1) > par->height)
277 m = par->height % 8;
278 for (j = x; j < x + width; j++) {
279 u8 data = 0;
280
281 for (k = 0; k < m; k++) {
282 u8 byte = vmem[(8 * i + k) * line_length +
283 j / 8];
284 u8 bit = (byte >> (j % 8)) & 1;
285 data |= bit << k;
286 }
287 array->data[array_idx++] = data;
288 }
289 }
290
291 ret = ssd1307fb_write_array(par->client, array, width * pages);
292
293 out_free:
294 kfree(array);
295 return ret;
296 }
297
ssd1307fb_update_display(struct ssd1307fb_par * par)298 static int ssd1307fb_update_display(struct ssd1307fb_par *par)
299 {
300 return ssd1307fb_update_rect(par, 0, 0, par->width, par->height);
301 }
302
ssd1307fb_blank(int blank_mode,struct fb_info * info)303 static int ssd1307fb_blank(int blank_mode, struct fb_info *info)
304 {
305 struct ssd1307fb_par *par = info->par;
306
307 if (blank_mode != FB_BLANK_UNBLANK)
308 return ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_OFF);
309 else
310 return ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON);
311 }
312
ssd1307fb_schedule_damage(struct fb_info * info,u32 x,u32 y,u32 width,u32 height)313 static void ssd1307fb_schedule_damage(struct fb_info *info, u32 x, u32 y,
314 u32 width, u32 height)
315 {
316 struct ssd1307fb_par *par = info->par;
317 unsigned long flags;
318 u32 x2, y2;
319
320 if (!width || !height || x >= par->width || y >= par->height)
321 return;
322
323 x2 = x + min(width, par->width - x);
324 y2 = y + min(height, par->height - y);
325
326 spin_lock_irqsave(&par->damage_lock, flags);
327 if (par->damage_pending) {
328 par->damage_x1 = min(par->damage_x1, x);
329 par->damage_y1 = min(par->damage_y1, y);
330 par->damage_x2 = max(par->damage_x2, x2);
331 par->damage_y2 = max(par->damage_y2, y2);
332 } else {
333 par->damage_x1 = x;
334 par->damage_y1 = y;
335 par->damage_x2 = x2;
336 par->damage_y2 = y2;
337 par->damage_pending = true;
338 }
339 spin_unlock_irqrestore(&par->damage_lock, flags);
340
341 /* Advance an already-pending mmap update as well. */
342 mod_delayed_work(system_wq, &info->deferred_work, 0);
343 }
344
ssd1307fb_defio_damage_range(struct fb_info * info,off_t off,size_t len)345 static void ssd1307fb_defio_damage_range(struct fb_info *info, off_t off, size_t len)
346 {
347 struct ssd1307fb_par *par = info->par;
348
349 ssd1307fb_schedule_damage(info, 0, 0, par->width, par->height);
350 }
351
ssd1307fb_defio_damage_area(struct fb_info * info,u32 x,u32 y,u32 width,u32 height)352 static void ssd1307fb_defio_damage_area(struct fb_info *info, u32 x, u32 y,
353 u32 width, u32 height)
354 {
355 ssd1307fb_schedule_damage(info, x, y, width, height);
356 }
357
358 FB_GEN_DEFAULT_DEFERRED_SYSMEM_OPS(ssd1307fb,
359 ssd1307fb_defio_damage_range,
360 ssd1307fb_defio_damage_area)
361
362 static const struct fb_ops ssd1307fb_ops = {
363 .owner = THIS_MODULE,
364 FB_DEFAULT_DEFERRED_OPS(ssd1307fb),
365 .fb_blank = ssd1307fb_blank,
366 };
367
ssd1307fb_deferred_io(struct fb_info * info,struct list_head * pagereflist)368 static void ssd1307fb_deferred_io(struct fb_info *info, struct list_head *pagereflist)
369 {
370 struct ssd1307fb_par *par = info->par;
371 unsigned long flags;
372 u32 x, y, width, height;
373
374 spin_lock_irqsave(&par->damage_lock, flags);
375 if (!list_empty(pagereflist)) {
376 x = 0;
377 y = 0;
378 width = par->width;
379 height = par->height;
380 par->damage_pending = false;
381 } else if (par->damage_pending) {
382 x = par->damage_x1;
383 y = par->damage_y1;
384 width = par->damage_x2 - par->damage_x1;
385 height = par->damage_y2 - par->damage_y1;
386 par->damage_pending = false;
387 } else {
388 spin_unlock_irqrestore(&par->damage_lock, flags);
389 return;
390 }
391 spin_unlock_irqrestore(&par->damage_lock, flags);
392
393 ssd1307fb_update_rect(par, x, y, width, height);
394 }
395
ssd1307fb_init(struct ssd1307fb_par * par)396 static int ssd1307fb_init(struct ssd1307fb_par *par)
397 {
398 struct pwm_state pwmstate;
399 int ret;
400 u32 precharge, dclk, com_invdir, compins;
401
402 if (par->device_info->need_pwm) {
403 par->pwm = pwm_get(&par->client->dev, NULL);
404 if (IS_ERR(par->pwm)) {
405 dev_err(&par->client->dev, "Could not get PWM from device tree!\n");
406 return PTR_ERR(par->pwm);
407 }
408
409 pwm_init_state(par->pwm, &pwmstate);
410 pwm_set_relative_duty_cycle(&pwmstate, 50, 100);
411 pwm_apply_might_sleep(par->pwm, &pwmstate);
412
413 /* Enable the PWM */
414 pwm_enable(par->pwm);
415
416 dev_dbg(&par->client->dev, "Using PWM %s with a %lluns period.\n",
417 par->pwm->label, pwm_get_period(par->pwm));
418 }
419
420 /* Set initial contrast */
421 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CONTRAST);
422 if (ret < 0)
423 return ret;
424
425 ret = ssd1307fb_write_cmd(par->client, par->contrast);
426 if (ret < 0)
427 return ret;
428
429 /* Set segment re-map */
430 if (par->seg_remap) {
431 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON);
432 if (ret < 0)
433 return ret;
434 }
435
436 /* Set COM direction */
437 com_invdir = 0xc0 | par->com_invdir << 3;
438 ret = ssd1307fb_write_cmd(par->client, com_invdir);
439 if (ret < 0)
440 return ret;
441
442 /* Set multiplex ratio value */
443 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_MULTIPLEX_RATIO);
444 if (ret < 0)
445 return ret;
446
447 ret = ssd1307fb_write_cmd(par->client, par->height - 1);
448 if (ret < 0)
449 return ret;
450
451 /* set display offset value */
452 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_DISPLAY_OFFSET);
453 if (ret < 0)
454 return ret;
455
456 ret = ssd1307fb_write_cmd(par->client, par->com_offset);
457 if (ret < 0)
458 return ret;
459
460 /* Set clock frequency */
461 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_CLOCK_FREQ);
462 if (ret < 0)
463 return ret;
464
465 dclk = ((par->dclk_div - 1) & 0xf) | (par->dclk_frq & 0xf) << 4;
466 ret = ssd1307fb_write_cmd(par->client, dclk);
467 if (ret < 0)
468 return ret;
469
470 /* Set Area Color Mode ON/OFF & Low Power Display Mode */
471 if (par->area_color_enable || par->low_power) {
472 u32 mode;
473
474 ret = ssd1307fb_write_cmd(par->client,
475 SSD1307FB_SET_AREA_COLOR_MODE);
476 if (ret < 0)
477 return ret;
478
479 mode = (par->area_color_enable ? 0x30 : 0) |
480 (par->low_power ? 5 : 0);
481 ret = ssd1307fb_write_cmd(par->client, mode);
482 if (ret < 0)
483 return ret;
484 }
485
486 /* Set precharge period in number of ticks from the internal clock */
487 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PRECHARGE_PERIOD);
488 if (ret < 0)
489 return ret;
490
491 precharge = (par->prechargep1 & 0xf) | (par->prechargep2 & 0xf) << 4;
492 ret = ssd1307fb_write_cmd(par->client, precharge);
493 if (ret < 0)
494 return ret;
495
496 /* Set COM pins configuration */
497 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COM_PINS_CONFIG);
498 if (ret < 0)
499 return ret;
500
501 compins = 0x02 | !par->com_seq << 4 | par->com_lrremap << 5;
502 ret = ssd1307fb_write_cmd(par->client, compins);
503 if (ret < 0)
504 return ret;
505
506 /* Set VCOMH */
507 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_VCOMH);
508 if (ret < 0)
509 return ret;
510
511 ret = ssd1307fb_write_cmd(par->client, par->vcomh);
512 if (ret < 0)
513 return ret;
514
515 /* Turn on the DC-DC Charge Pump */
516 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CHARGE_PUMP);
517 if (ret < 0)
518 return ret;
519
520 ret = ssd1307fb_write_cmd(par->client,
521 BIT(4) | (par->device_info->need_chargepump ? BIT(2) : 0));
522 if (ret < 0)
523 return ret;
524
525 /* Set lookup table */
526 if (par->lookup_table_set) {
527 int i;
528
529 ret = ssd1307fb_write_cmd(par->client,
530 SSD1307FB_SET_LOOKUP_TABLE);
531 if (ret < 0)
532 return ret;
533
534 for (i = 0; i < ARRAY_SIZE(par->lookup_table); ++i) {
535 u8 val = par->lookup_table[i];
536
537 if (val < 31 || val > 63)
538 dev_warn(&par->client->dev,
539 "lookup table index %d value out of range 31 <= %d <= 63\n",
540 i, val);
541 ret = ssd1307fb_write_cmd(par->client, val);
542 if (ret < 0)
543 return ret;
544 }
545 }
546
547 /* Switch to horizontal addressing mode */
548 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_ADDRESS_MODE);
549 if (ret < 0)
550 return ret;
551
552 ret = ssd1307fb_write_cmd(par->client,
553 SSD1307FB_SET_ADDRESS_MODE_HORIZONTAL);
554 if (ret < 0)
555 return ret;
556
557 /* Clear the screen */
558 ret = ssd1307fb_update_display(par);
559 if (ret < 0)
560 return ret;
561
562 /* Turn on the display */
563 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON);
564 if (ret < 0)
565 return ret;
566
567 return 0;
568 }
569
ssd1307fb_update_bl(struct backlight_device * bdev)570 static int ssd1307fb_update_bl(struct backlight_device *bdev)
571 {
572 struct ssd1307fb_par *par = bl_get_data(bdev);
573 int ret;
574 int brightness = bdev->props.brightness;
575
576 par->contrast = brightness;
577
578 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CONTRAST);
579 if (ret < 0)
580 return ret;
581 ret = ssd1307fb_write_cmd(par->client, par->contrast);
582 if (ret < 0)
583 return ret;
584 return 0;
585 }
586
ssd1307fb_get_brightness(struct backlight_device * bdev)587 static int ssd1307fb_get_brightness(struct backlight_device *bdev)
588 {
589 struct ssd1307fb_par *par = bl_get_data(bdev);
590
591 return par->contrast;
592 }
593
594 static const struct backlight_ops ssd1307fb_bl_ops = {
595 .options = BL_CORE_SUSPENDRESUME,
596 .update_status = ssd1307fb_update_bl,
597 .get_brightness = ssd1307fb_get_brightness,
598 };
599
600 static struct ssd1307fb_deviceinfo ssd1307fb_ssd1305_deviceinfo = {
601 .default_vcomh = 0x34,
602 .default_dclk_div = 1,
603 .default_dclk_frq = 7,
604 };
605
606 static struct ssd1307fb_deviceinfo ssd1307fb_ssd1306_deviceinfo = {
607 .default_vcomh = 0x20,
608 .default_dclk_div = 1,
609 .default_dclk_frq = 8,
610 .need_chargepump = 1,
611 };
612
613 static struct ssd1307fb_deviceinfo ssd1307fb_ssd1307_deviceinfo = {
614 .default_vcomh = 0x20,
615 .default_dclk_div = 2,
616 .default_dclk_frq = 12,
617 .need_pwm = 1,
618 };
619
620 static struct ssd1307fb_deviceinfo ssd1307fb_ssd1309_deviceinfo = {
621 .default_vcomh = 0x34,
622 .default_dclk_div = 1,
623 .default_dclk_frq = 10,
624 };
625
626 static const struct of_device_id ssd1307fb_of_match[] = {
627 {
628 .compatible = "solomon,ssd1305fb-i2c",
629 .data = (void *)&ssd1307fb_ssd1305_deviceinfo,
630 },
631 {
632 .compatible = "solomon,ssd1306fb-i2c",
633 .data = (void *)&ssd1307fb_ssd1306_deviceinfo,
634 },
635 {
636 .compatible = "solomon,ssd1307fb-i2c",
637 .data = (void *)&ssd1307fb_ssd1307_deviceinfo,
638 },
639 {
640 .compatible = "solomon,ssd1309fb-i2c",
641 .data = (void *)&ssd1307fb_ssd1309_deviceinfo,
642 },
643 {},
644 };
645 MODULE_DEVICE_TABLE(of, ssd1307fb_of_match);
646
ssd1307fb_probe(struct i2c_client * client)647 static int ssd1307fb_probe(struct i2c_client *client)
648 {
649 struct device *dev = &client->dev;
650 struct backlight_device *bl;
651 struct fb_info *info;
652 struct fb_deferred_io *ssd1307fb_defio;
653 u32 vmem_size;
654 struct ssd1307fb_par *par;
655 void *vmem;
656 int ret;
657
658 info = framebuffer_alloc(sizeof(struct ssd1307fb_par), dev);
659 if (!info)
660 return -ENOMEM;
661
662 par = info->par;
663 par->info = info;
664 par->client = client;
665 spin_lock_init(&par->damage_lock);
666
667 par->device_info = device_get_match_data(dev);
668 if (!par->device_info) {
669 ret = -ENODEV;
670 goto fb_alloc_error;
671 }
672
673 par->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
674 if (IS_ERR(par->reset)) {
675 ret = dev_err_probe(dev, PTR_ERR(par->reset),
676 "failed to get reset gpio\n");
677 goto fb_alloc_error;
678 }
679
680 par->vbat_reg = devm_regulator_get_optional(dev, "vbat");
681 if (IS_ERR(par->vbat_reg)) {
682 ret = PTR_ERR(par->vbat_reg);
683 if (ret == -ENODEV) {
684 par->vbat_reg = NULL;
685 } else {
686 dev_err_probe(dev, ret, "failed to get VBAT regulator\n");
687 goto fb_alloc_error;
688 }
689 }
690
691 if (device_property_read_u32(dev, "solomon,width", &par->width))
692 par->width = 96;
693
694 if (device_property_read_u32(dev, "solomon,height", &par->height))
695 par->height = 16;
696
697 if (device_property_read_u32(dev, "solomon,page-offset", &par->page_offset))
698 par->page_offset = 1;
699
700 if (device_property_read_u32(dev, "solomon,col-offset", &par->col_offset))
701 par->col_offset = 0;
702
703 if (device_property_read_u32(dev, "solomon,com-offset", &par->com_offset))
704 par->com_offset = 0;
705
706 if (device_property_read_u32(dev, "solomon,prechargep1", &par->prechargep1))
707 par->prechargep1 = 2;
708
709 if (device_property_read_u32(dev, "solomon,prechargep2", &par->prechargep2))
710 par->prechargep2 = 2;
711
712 if (!device_property_read_u8_array(dev, "solomon,lookup-table",
713 par->lookup_table,
714 ARRAY_SIZE(par->lookup_table)))
715 par->lookup_table_set = 1;
716
717 par->seg_remap = !device_property_read_bool(dev, "solomon,segment-no-remap");
718 par->com_seq = device_property_read_bool(dev, "solomon,com-seq");
719 par->com_lrremap = device_property_read_bool(dev, "solomon,com-lrremap");
720 par->com_invdir = device_property_read_bool(dev, "solomon,com-invdir");
721 par->area_color_enable =
722 device_property_read_bool(dev, "solomon,area-color-enable");
723 par->low_power = device_property_read_bool(dev, "solomon,low-power");
724
725 par->contrast = 127;
726 par->vcomh = par->device_info->default_vcomh;
727
728 /* Setup display timing */
729 if (device_property_read_u32(dev, "solomon,dclk-div", &par->dclk_div))
730 par->dclk_div = par->device_info->default_dclk_div;
731 if (device_property_read_u32(dev, "solomon,dclk-frq", &par->dclk_frq))
732 par->dclk_frq = par->device_info->default_dclk_frq;
733
734 vmem_size = DIV_ROUND_UP(par->width, 8) * par->height;
735
736 vmem = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
737 get_order(vmem_size));
738 if (!vmem) {
739 dev_err(dev, "Couldn't allocate graphical memory.\n");
740 ret = -ENOMEM;
741 goto fb_alloc_error;
742 }
743
744 ssd1307fb_defio = devm_kzalloc(dev, sizeof(*ssd1307fb_defio),
745 GFP_KERNEL);
746 if (!ssd1307fb_defio) {
747 dev_err(dev, "Couldn't allocate deferred io.\n");
748 ret = -ENOMEM;
749 goto fb_defio_error;
750 }
751
752 ssd1307fb_defio->delay = HZ / refreshrate;
753 ssd1307fb_defio->deferred_io = ssd1307fb_deferred_io;
754
755 info->fbops = &ssd1307fb_ops;
756 info->fix = ssd1307fb_fix;
757 info->fix.line_length = DIV_ROUND_UP(par->width, 8);
758 info->fbdefio = ssd1307fb_defio;
759
760 info->var = ssd1307fb_var;
761 info->var.xres = par->width;
762 info->var.xres_virtual = par->width;
763 info->var.yres = par->height;
764 info->var.yres_virtual = par->height;
765
766 info->screen_buffer = vmem;
767 info->fix.smem_start = __pa(vmem);
768 info->fix.smem_len = vmem_size;
769
770 fb_deferred_io_init(info);
771
772 i2c_set_clientdata(client, info);
773
774 if (par->reset) {
775 /* Reset the screen */
776 gpiod_set_value_cansleep(par->reset, 1);
777 udelay(4);
778 gpiod_set_value_cansleep(par->reset, 0);
779 udelay(4);
780 }
781
782 if (par->vbat_reg) {
783 ret = regulator_enable(par->vbat_reg);
784 if (ret) {
785 dev_err(dev, "failed to enable VBAT: %d\n", ret);
786 goto reset_oled_error;
787 }
788 }
789
790 ret = ssd1307fb_init(par);
791 if (ret)
792 goto regulator_enable_error;
793
794 bl = backlight_device_register("ssd1307fb-bl", dev, par, &ssd1307fb_bl_ops,
795 NULL);
796 if (IS_ERR(bl)) {
797 ret = PTR_ERR(bl);
798 dev_err(dev, "unable to register backlight device: %d\n", ret);
799 goto panel_init_error;
800 }
801 info->bl_dev = bl;
802
803 ret = register_framebuffer(info);
804 if (ret) {
805 dev_err(dev, "Couldn't register the framebuffer\n");
806 goto fb_init_error;
807 }
808
809 bl->props.brightness = par->contrast;
810 bl->props.max_brightness = MAX_CONTRAST;
811
812 dev_info(dev, "fb%d: %s framebuffer device registered, using %d bytes of video memory\n", info->node, info->fix.id, vmem_size);
813
814 return 0;
815
816 fb_init_error:
817 backlight_device_unregister(bl);
818 panel_init_error:
819 pwm_disable(par->pwm);
820 pwm_put(par->pwm);
821 regulator_enable_error:
822 if (par->vbat_reg)
823 regulator_disable(par->vbat_reg);
824 reset_oled_error:
825 fb_deferred_io_cleanup(info);
826 fb_defio_error:
827 __free_pages(vmem, get_order(vmem_size));
828 fb_alloc_error:
829 framebuffer_release(info);
830 return ret;
831 }
832
ssd1307fb_remove(struct i2c_client * client)833 static void ssd1307fb_remove(struct i2c_client *client)
834 {
835 struct fb_info *info = i2c_get_clientdata(client);
836 struct ssd1307fb_par *par = info->par;
837
838 ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_OFF);
839
840 backlight_device_unregister(info->bl_dev);
841
842 unregister_framebuffer(info);
843 pwm_disable(par->pwm);
844 pwm_put(par->pwm);
845 if (par->vbat_reg)
846 regulator_disable(par->vbat_reg);
847 fb_deferred_io_cleanup(info);
848 __free_pages(__va(info->fix.smem_start), get_order(info->fix.smem_len));
849 framebuffer_release(info);
850 }
851
852 static const struct i2c_device_id ssd1307fb_i2c_id[] = {
853 { .name = "ssd1305fb" },
854 { .name = "ssd1306fb" },
855 { .name = "ssd1307fb" },
856 { .name = "ssd1309fb" },
857 { }
858 };
859 MODULE_DEVICE_TABLE(i2c, ssd1307fb_i2c_id);
860
861 static struct i2c_driver ssd1307fb_driver = {
862 .probe = ssd1307fb_probe,
863 .remove = ssd1307fb_remove,
864 .id_table = ssd1307fb_i2c_id,
865 .driver = {
866 .name = "ssd1307fb",
867 .of_match_table = ssd1307fb_of_match,
868 },
869 };
870
871 module_i2c_driver(ssd1307fb_driver);
872
873 MODULE_DESCRIPTION("FB driver for the Solomon SSD1307 OLED controller");
874 MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
875 MODULE_LICENSE("GPL");
876