1
2
3
4
5
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/of_device.h>
16#include <linux/of_gpio.h>
17#include <linux/pwm.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_CHARGE_PUMP 0x8d
32#define SSD1307FB_SEG_REMAP_ON 0xa1
33#define SSD1307FB_DISPLAY_OFF 0xae
34#define SSD1307FB_SET_MULTIPLEX_RATIO 0xa8
35#define SSD1307FB_DISPLAY_ON 0xaf
36#define SSD1307FB_START_PAGE_ADDRESS 0xb0
37#define SSD1307FB_SET_DISPLAY_OFFSET 0xd3
38#define SSD1307FB_SET_CLOCK_FREQ 0xd5
39#define SSD1307FB_SET_PRECHARGE_PERIOD 0xd9
40#define SSD1307FB_SET_COM_PINS_CONFIG 0xda
41#define SSD1307FB_SET_VCOMH 0xdb
42
43#define MAX_CONTRAST 255
44
45#define REFRESHRATE 1
46
47static u_int refreshrate = REFRESHRATE;
48module_param(refreshrate, uint, 0);
49
50struct ssd1307fb_par;
51
52struct ssd1307fb_deviceinfo {
53 u32 default_vcomh;
54 u32 default_dclk_div;
55 u32 default_dclk_frq;
56 int need_pwm;
57 int need_chargepump;
58};
59
60struct ssd1307fb_par {
61 u32 com_invdir;
62 u32 com_lrremap;
63 u32 com_offset;
64 u32 com_seq;
65 u32 contrast;
66 u32 dclk_div;
67 u32 dclk_frq;
68 const struct ssd1307fb_deviceinfo *device_info;
69 struct i2c_client *client;
70 u32 height;
71 struct fb_info *info;
72 u32 page_offset;
73 u32 prechargep1;
74 u32 prechargep2;
75 struct pwm_device *pwm;
76 u32 pwm_period;
77 struct gpio_desc *reset;
78 struct regulator *vbat_reg;
79 u32 seg_remap;
80 u32 vcomh;
81 u32 width;
82};
83
84struct ssd1307fb_array {
85 u8 type;
86 u8 data[0];
87};
88
89static const struct fb_fix_screeninfo ssd1307fb_fix = {
90 .id = "Solomon SSD1307",
91 .type = FB_TYPE_PACKED_PIXELS,
92 .visual = FB_VISUAL_MONO10,
93 .xpanstep = 0,
94 .ypanstep = 0,
95 .ywrapstep = 0,
96 .accel = FB_ACCEL_NONE,
97};
98
99static const struct fb_var_screeninfo ssd1307fb_var = {
100 .bits_per_pixel = 1,
101};
102
103static struct ssd1307fb_array *ssd1307fb_alloc_array(u32 len, u8 type)
104{
105 struct ssd1307fb_array *array;
106
107 array = kzalloc(sizeof(struct ssd1307fb_array) + len, GFP_KERNEL);
108 if (!array)
109 return NULL;
110
111 array->type = type;
112
113 return array;
114}
115
116static int ssd1307fb_write_array(struct i2c_client *client,
117 struct ssd1307fb_array *array, u32 len)
118{
119 int ret;
120
121 len += sizeof(struct ssd1307fb_array);
122
123 ret = i2c_master_send(client, (u8 *)array, len);
124 if (ret != len) {
125 dev_err(&client->dev, "Couldn't send I2C command.\n");
126 return ret;
127 }
128
129 return 0;
130}
131
132static inline int ssd1307fb_write_cmd(struct i2c_client *client, u8 cmd)
133{
134 struct ssd1307fb_array *array;
135 int ret;
136
137 array = ssd1307fb_alloc_array(1, SSD1307FB_COMMAND);
138 if (!array)
139 return -ENOMEM;
140
141 array->data[0] = cmd;
142
143 ret = ssd1307fb_write_array(client, array, 1);
144 kfree(array);
145
146 return ret;
147}
148
149static void ssd1307fb_update_display(struct ssd1307fb_par *par)
150{
151 struct ssd1307fb_array *array;
152 u8 *vmem = par->info->screen_base;
153 int i, j, k;
154
155 array = ssd1307fb_alloc_array(par->width * par->height / 8,
156 SSD1307FB_DATA);
157 if (!array)
158 return;
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189 for (i = 0; i < (par->height / 8); i++) {
190 for (j = 0; j < par->width; j++) {
191 u32 array_idx = i * par->width + j;
192 array->data[array_idx] = 0;
193 for (k = 0; k < 8; k++) {
194 u32 page_length = par->width * i;
195 u32 index = page_length + (par->width * k + j) / 8;
196 u8 byte = *(vmem + index);
197 u8 bit = byte & (1 << (j % 8));
198 bit = bit >> (j % 8);
199 array->data[array_idx] |= bit << k;
200 }
201 }
202 }
203
204 ssd1307fb_write_array(par->client, array, par->width * par->height / 8);
205 kfree(array);
206}
207
208
209static ssize_t ssd1307fb_write(struct fb_info *info, const char __user *buf,
210 size_t count, loff_t *ppos)
211{
212 struct ssd1307fb_par *par = info->par;
213 unsigned long total_size;
214 unsigned long p = *ppos;
215 u8 __iomem *dst;
216
217 total_size = info->fix.smem_len;
218
219 if (p > total_size)
220 return -EINVAL;
221
222 if (count + p > total_size)
223 count = total_size - p;
224
225 if (!count)
226 return -EINVAL;
227
228 dst = (void __force *) (info->screen_base + p);
229
230 if (copy_from_user(dst, buf, count))
231 return -EFAULT;
232
233 ssd1307fb_update_display(par);
234
235 *ppos += count;
236
237 return count;
238}
239
240static int ssd1307fb_blank(int blank_mode, struct fb_info *info)
241{
242 struct ssd1307fb_par *par = info->par;
243
244 if (blank_mode != FB_BLANK_UNBLANK)
245 return ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_OFF);
246 else
247 return ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON);
248}
249
250static void ssd1307fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
251{
252 struct ssd1307fb_par *par = info->par;
253 sys_fillrect(info, rect);
254 ssd1307fb_update_display(par);
255}
256
257static void ssd1307fb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
258{
259 struct ssd1307fb_par *par = info->par;
260 sys_copyarea(info, area);
261 ssd1307fb_update_display(par);
262}
263
264static void ssd1307fb_imageblit(struct fb_info *info, const struct fb_image *image)
265{
266 struct ssd1307fb_par *par = info->par;
267 sys_imageblit(info, image);
268 ssd1307fb_update_display(par);
269}
270
271static struct fb_ops ssd1307fb_ops = {
272 .owner = THIS_MODULE,
273 .fb_read = fb_sys_read,
274 .fb_write = ssd1307fb_write,
275 .fb_blank = ssd1307fb_blank,
276 .fb_fillrect = ssd1307fb_fillrect,
277 .fb_copyarea = ssd1307fb_copyarea,
278 .fb_imageblit = ssd1307fb_imageblit,
279};
280
281static void ssd1307fb_deferred_io(struct fb_info *info,
282 struct list_head *pagelist)
283{
284 ssd1307fb_update_display(info->par);
285}
286
287static int ssd1307fb_init(struct ssd1307fb_par *par)
288{
289 int ret;
290 u32 precharge, dclk, com_invdir, compins;
291 struct pwm_args pargs;
292
293 if (par->device_info->need_pwm) {
294 par->pwm = pwm_get(&par->client->dev, NULL);
295 if (IS_ERR(par->pwm)) {
296 dev_err(&par->client->dev, "Could not get PWM from device tree!\n");
297 return PTR_ERR(par->pwm);
298 }
299
300
301
302
303
304 pwm_apply_args(par->pwm);
305
306 pwm_get_args(par->pwm, &pargs);
307
308 par->pwm_period = pargs.period;
309
310 pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period);
311 pwm_enable(par->pwm);
312
313 dev_dbg(&par->client->dev, "Using PWM%d with a %dns period.\n",
314 par->pwm->pwm, par->pwm_period);
315 };
316
317
318 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CONTRAST);
319 if (ret < 0)
320 return ret;
321
322 ret = ssd1307fb_write_cmd(par->client, par->contrast);
323 if (ret < 0)
324 return ret;
325
326
327 if (par->seg_remap) {
328 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON);
329 if (ret < 0)
330 return ret;
331 };
332
333
334 com_invdir = 0xc0 | (par->com_invdir & 0x1) << 3;
335 ret = ssd1307fb_write_cmd(par->client, com_invdir);
336 if (ret < 0)
337 return ret;
338
339
340 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_MULTIPLEX_RATIO);
341 if (ret < 0)
342 return ret;
343
344 ret = ssd1307fb_write_cmd(par->client, par->height - 1);
345 if (ret < 0)
346 return ret;
347
348
349 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_DISPLAY_OFFSET);
350 if (ret < 0)
351 return ret;
352
353 ret = ssd1307fb_write_cmd(par->client, par->com_offset);
354 if (ret < 0)
355 return ret;
356
357
358 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_CLOCK_FREQ);
359 if (ret < 0)
360 return ret;
361
362 dclk = ((par->dclk_div - 1) & 0xf) | (par->dclk_frq & 0xf) << 4;
363 ret = ssd1307fb_write_cmd(par->client, dclk);
364 if (ret < 0)
365 return ret;
366
367
368 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PRECHARGE_PERIOD);
369 if (ret < 0)
370 return ret;
371
372 precharge = (par->prechargep1 & 0xf) | (par->prechargep2 & 0xf) << 4;
373 ret = ssd1307fb_write_cmd(par->client, precharge);
374 if (ret < 0)
375 return ret;
376
377
378 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COM_PINS_CONFIG);
379 if (ret < 0)
380 return ret;
381
382 compins = 0x02 | !(par->com_seq & 0x1) << 4
383 | (par->com_lrremap & 0x1) << 5;
384 ret = ssd1307fb_write_cmd(par->client, compins);
385 if (ret < 0)
386 return ret;
387
388
389 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_VCOMH);
390 if (ret < 0)
391 return ret;
392
393 ret = ssd1307fb_write_cmd(par->client, par->vcomh);
394 if (ret < 0)
395 return ret;
396
397
398 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CHARGE_PUMP);
399 if (ret < 0)
400 return ret;
401
402 ret = ssd1307fb_write_cmd(par->client,
403 BIT(4) | (par->device_info->need_chargepump ? BIT(2) : 0));
404 if (ret < 0)
405 return ret;
406
407
408 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_ADDRESS_MODE);
409 if (ret < 0)
410 return ret;
411
412 ret = ssd1307fb_write_cmd(par->client,
413 SSD1307FB_SET_ADDRESS_MODE_HORIZONTAL);
414 if (ret < 0)
415 return ret;
416
417
418 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COL_RANGE);
419 if (ret < 0)
420 return ret;
421
422 ret = ssd1307fb_write_cmd(par->client, 0x0);
423 if (ret < 0)
424 return ret;
425
426 ret = ssd1307fb_write_cmd(par->client, par->width - 1);
427 if (ret < 0)
428 return ret;
429
430
431 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PAGE_RANGE);
432 if (ret < 0)
433 return ret;
434
435 ret = ssd1307fb_write_cmd(par->client, 0x0);
436 if (ret < 0)
437 return ret;
438
439 ret = ssd1307fb_write_cmd(par->client,
440 par->page_offset + (par->height / 8) - 1);
441 if (ret < 0)
442 return ret;
443
444
445 ssd1307fb_update_display(par);
446
447
448 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON);
449 if (ret < 0)
450 return ret;
451
452 return 0;
453}
454
455static int ssd1307fb_update_bl(struct backlight_device *bdev)
456{
457 struct ssd1307fb_par *par = bl_get_data(bdev);
458 int ret;
459 int brightness = bdev->props.brightness;
460
461 par->contrast = brightness;
462
463 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CONTRAST);
464 if (ret < 0)
465 return ret;
466 ret = ssd1307fb_write_cmd(par->client, par->contrast);
467 if (ret < 0)
468 return ret;
469 return 0;
470}
471
472static int ssd1307fb_get_brightness(struct backlight_device *bdev)
473{
474 struct ssd1307fb_par *par = bl_get_data(bdev);
475
476 return par->contrast;
477}
478
479static int ssd1307fb_check_fb(struct backlight_device *bdev,
480 struct fb_info *info)
481{
482 return (info->bl_dev == bdev);
483}
484
485static const struct backlight_ops ssd1307fb_bl_ops = {
486 .options = BL_CORE_SUSPENDRESUME,
487 .update_status = ssd1307fb_update_bl,
488 .get_brightness = ssd1307fb_get_brightness,
489 .check_fb = ssd1307fb_check_fb,
490};
491
492static struct ssd1307fb_deviceinfo ssd1307fb_ssd1305_deviceinfo = {
493 .default_vcomh = 0x34,
494 .default_dclk_div = 1,
495 .default_dclk_frq = 7,
496};
497
498static struct ssd1307fb_deviceinfo ssd1307fb_ssd1306_deviceinfo = {
499 .default_vcomh = 0x20,
500 .default_dclk_div = 1,
501 .default_dclk_frq = 8,
502 .need_chargepump = 1,
503};
504
505static struct ssd1307fb_deviceinfo ssd1307fb_ssd1307_deviceinfo = {
506 .default_vcomh = 0x20,
507 .default_dclk_div = 2,
508 .default_dclk_frq = 12,
509 .need_pwm = 1,
510};
511
512static struct ssd1307fb_deviceinfo ssd1307fb_ssd1309_deviceinfo = {
513 .default_vcomh = 0x34,
514 .default_dclk_div = 1,
515 .default_dclk_frq = 10,
516};
517
518static const struct of_device_id ssd1307fb_of_match[] = {
519 {
520 .compatible = "solomon,ssd1305fb-i2c",
521 .data = (void *)&ssd1307fb_ssd1305_deviceinfo,
522 },
523 {
524 .compatible = "solomon,ssd1306fb-i2c",
525 .data = (void *)&ssd1307fb_ssd1306_deviceinfo,
526 },
527 {
528 .compatible = "solomon,ssd1307fb-i2c",
529 .data = (void *)&ssd1307fb_ssd1307_deviceinfo,
530 },
531 {
532 .compatible = "solomon,ssd1309fb-i2c",
533 .data = (void *)&ssd1307fb_ssd1309_deviceinfo,
534 },
535 {},
536};
537MODULE_DEVICE_TABLE(of, ssd1307fb_of_match);
538
539static int ssd1307fb_probe(struct i2c_client *client,
540 const struct i2c_device_id *id)
541{
542 struct backlight_device *bl;
543 char bl_name[12];
544 struct fb_info *info;
545 struct device_node *node = client->dev.of_node;
546 struct fb_deferred_io *ssd1307fb_defio;
547 u32 vmem_size;
548 struct ssd1307fb_par *par;
549 u8 *vmem;
550 int ret;
551
552 if (!node) {
553 dev_err(&client->dev, "No device tree data found!\n");
554 return -EINVAL;
555 }
556
557 info = framebuffer_alloc(sizeof(struct ssd1307fb_par), &client->dev);
558 if (!info)
559 return -ENOMEM;
560
561 par = info->par;
562 par->info = info;
563 par->client = client;
564
565 par->device_info = of_device_get_match_data(&client->dev);
566
567 par->reset = devm_gpiod_get_optional(&client->dev, "reset",
568 GPIOD_OUT_LOW);
569 if (IS_ERR(par->reset)) {
570 dev_err(&client->dev, "failed to get reset gpio: %ld\n",
571 PTR_ERR(par->reset));
572 ret = PTR_ERR(par->reset);
573 goto fb_alloc_error;
574 }
575
576 par->vbat_reg = devm_regulator_get_optional(&client->dev, "vbat");
577 if (IS_ERR(par->vbat_reg)) {
578 ret = PTR_ERR(par->vbat_reg);
579 if (ret == -ENODEV) {
580 par->vbat_reg = NULL;
581 } else {
582 dev_err(&client->dev, "failed to get VBAT regulator: %d\n",
583 ret);
584 goto fb_alloc_error;
585 }
586 }
587
588 if (of_property_read_u32(node, "solomon,width", &par->width))
589 par->width = 96;
590
591 if (of_property_read_u32(node, "solomon,height", &par->height))
592 par->height = 16;
593
594 if (of_property_read_u32(node, "solomon,page-offset", &par->page_offset))
595 par->page_offset = 1;
596
597 if (of_property_read_u32(node, "solomon,com-offset", &par->com_offset))
598 par->com_offset = 0;
599
600 if (of_property_read_u32(node, "solomon,prechargep1", &par->prechargep1))
601 par->prechargep1 = 2;
602
603 if (of_property_read_u32(node, "solomon,prechargep2", &par->prechargep2))
604 par->prechargep2 = 2;
605
606 par->seg_remap = !of_property_read_bool(node, "solomon,segment-no-remap");
607 par->com_seq = of_property_read_bool(node, "solomon,com-seq");
608 par->com_lrremap = of_property_read_bool(node, "solomon,com-lrremap");
609 par->com_invdir = of_property_read_bool(node, "solomon,com-invdir");
610
611 par->contrast = 127;
612 par->vcomh = par->device_info->default_vcomh;
613
614
615 par->dclk_div = par->device_info->default_dclk_div;
616 par->dclk_frq = par->device_info->default_dclk_frq;
617
618 vmem_size = par->width * par->height / 8;
619
620 vmem = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
621 get_order(vmem_size));
622 if (!vmem) {
623 dev_err(&client->dev, "Couldn't allocate graphical memory.\n");
624 ret = -ENOMEM;
625 goto fb_alloc_error;
626 }
627
628 ssd1307fb_defio = devm_kzalloc(&client->dev, sizeof(*ssd1307fb_defio),
629 GFP_KERNEL);
630 if (!ssd1307fb_defio) {
631 dev_err(&client->dev, "Couldn't allocate deferred io.\n");
632 ret = -ENOMEM;
633 goto fb_alloc_error;
634 }
635
636 ssd1307fb_defio->delay = HZ / refreshrate;
637 ssd1307fb_defio->deferred_io = ssd1307fb_deferred_io;
638
639 info->fbops = &ssd1307fb_ops;
640 info->fix = ssd1307fb_fix;
641 info->fix.line_length = par->width / 8;
642 info->fbdefio = ssd1307fb_defio;
643
644 info->var = ssd1307fb_var;
645 info->var.xres = par->width;
646 info->var.xres_virtual = par->width;
647 info->var.yres = par->height;
648 info->var.yres_virtual = par->height;
649
650 info->var.red.length = 1;
651 info->var.red.offset = 0;
652 info->var.green.length = 1;
653 info->var.green.offset = 0;
654 info->var.blue.length = 1;
655 info->var.blue.offset = 0;
656
657 info->screen_base = (u8 __force __iomem *)vmem;
658 info->fix.smem_start = __pa(vmem);
659 info->fix.smem_len = vmem_size;
660
661 fb_deferred_io_init(info);
662
663 i2c_set_clientdata(client, info);
664
665 if (par->reset) {
666
667 gpiod_set_value_cansleep(par->reset, 1);
668 udelay(4);
669 gpiod_set_value_cansleep(par->reset, 0);
670 udelay(4);
671 }
672
673 if (par->vbat_reg) {
674 ret = regulator_enable(par->vbat_reg);
675 if (ret) {
676 dev_err(&client->dev, "failed to enable VBAT: %d\n",
677 ret);
678 goto reset_oled_error;
679 }
680 }
681
682 ret = ssd1307fb_init(par);
683 if (ret)
684 goto regulator_enable_error;
685
686 ret = register_framebuffer(info);
687 if (ret) {
688 dev_err(&client->dev, "Couldn't register the framebuffer\n");
689 goto panel_init_error;
690 }
691
692 snprintf(bl_name, sizeof(bl_name), "ssd1307fb%d", info->node);
693 bl = backlight_device_register(bl_name, &client->dev, par,
694 &ssd1307fb_bl_ops, NULL);
695 if (IS_ERR(bl)) {
696 ret = PTR_ERR(bl);
697 dev_err(&client->dev, "unable to register backlight device: %d\n",
698 ret);
699 goto bl_init_error;
700 }
701
702 bl->props.brightness = par->contrast;
703 bl->props.max_brightness = MAX_CONTRAST;
704 info->bl_dev = bl;
705
706 dev_info(&client->dev, "fb%d: %s framebuffer device registered, using %d bytes of video memory\n", info->node, info->fix.id, vmem_size);
707
708 return 0;
709
710bl_init_error:
711 unregister_framebuffer(info);
712panel_init_error:
713 if (par->device_info->need_pwm) {
714 pwm_disable(par->pwm);
715 pwm_put(par->pwm);
716 };
717regulator_enable_error:
718 if (par->vbat_reg)
719 regulator_disable(par->vbat_reg);
720reset_oled_error:
721 fb_deferred_io_cleanup(info);
722fb_alloc_error:
723 framebuffer_release(info);
724 return ret;
725}
726
727static int ssd1307fb_remove(struct i2c_client *client)
728{
729 struct fb_info *info = i2c_get_clientdata(client);
730 struct ssd1307fb_par *par = info->par;
731
732 ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_OFF);
733
734 backlight_device_unregister(info->bl_dev);
735
736 unregister_framebuffer(info);
737 if (par->device_info->need_pwm) {
738 pwm_disable(par->pwm);
739 pwm_put(par->pwm);
740 };
741 fb_deferred_io_cleanup(info);
742 __free_pages(__va(info->fix.smem_start), get_order(info->fix.smem_len));
743 framebuffer_release(info);
744
745 return 0;
746}
747
748static const struct i2c_device_id ssd1307fb_i2c_id[] = {
749 { "ssd1305fb", 0 },
750 { "ssd1306fb", 0 },
751 { "ssd1307fb", 0 },
752 { "ssd1309fb", 0 },
753 { }
754};
755MODULE_DEVICE_TABLE(i2c, ssd1307fb_i2c_id);
756
757static struct i2c_driver ssd1307fb_driver = {
758 .probe = ssd1307fb_probe,
759 .remove = ssd1307fb_remove,
760 .id_table = ssd1307fb_i2c_id,
761 .driver = {
762 .name = "ssd1307fb",
763 .of_match_table = ssd1307fb_of_match,
764 },
765};
766
767module_i2c_driver(ssd1307fb_driver);
768
769MODULE_DESCRIPTION("FB driver for the Solomon SSD1307 OLED controller");
770MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
771MODULE_LICENSE("GPL");
772