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