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