forked from ~ljy/RK356X_SDK_RELEASE

hc
2023-12-11 072de836f53be56a70cecf70b43ae43b7ce17376
kernel/drivers/video/fbdev/ssd1307fb.c
....@@ -1,9 +1,8 @@
1
+// SPDX-License-Identifier: GPL-2.0-or-later
12 /*
23 * Driver for the Solomon SSD1307 OLED controller
34 *
45 * Copyright 2012 Free Electrons
5
- *
6
- * Licensed under the GPLv2 or later.
76 */
87
98 #include <linux/backlight.h>
....@@ -13,8 +12,7 @@
1312 #include <linux/i2c.h>
1413 #include <linux/kernel.h>
1514 #include <linux/module.h>
16
-#include <linux/of_device.h>
17
-#include <linux/of_gpio.h>
15
+#include <linux/property.h>
1816 #include <linux/pwm.h>
1917 #include <linux/uaccess.h>
2018 #include <linux/regulator/consumer.h>
....@@ -29,6 +27,7 @@
2927 #define SSD1307FB_SET_COL_RANGE 0x21
3028 #define SSD1307FB_SET_PAGE_RANGE 0x22
3129 #define SSD1307FB_CONTRAST 0x81
30
+#define SSD1307FB_SET_LOOKUP_TABLE 0x91
3231 #define SSD1307FB_CHARGE_PUMP 0x8d
3332 #define SSD1307FB_SEG_REMAP_ON 0xa1
3433 #define SSD1307FB_DISPLAY_OFF 0xae
....@@ -37,6 +36,7 @@
3736 #define SSD1307FB_START_PAGE_ADDRESS 0xb0
3837 #define SSD1307FB_SET_DISPLAY_OFFSET 0xd3
3938 #define SSD1307FB_SET_CLOCK_FREQ 0xd5
39
+#define SSD1307FB_SET_AREA_COLOR_MODE 0xd8
4040 #define SSD1307FB_SET_PRECHARGE_PERIOD 0xd9
4141 #define SSD1307FB_SET_COM_PINS_CONFIG 0xda
4242 #define SSD1307FB_SET_VCOMH 0xdb
....@@ -48,8 +48,6 @@
4848 static u_int refreshrate = REFRESHRATE;
4949 module_param(refreshrate, uint, 0);
5050
51
-struct ssd1307fb_par;
52
-
5351 struct ssd1307fb_deviceinfo {
5452 u32 default_vcomh;
5553 u32 default_dclk_div;
....@@ -59,10 +57,14 @@
5957 };
6058
6159 struct ssd1307fb_par {
62
- u32 com_invdir;
63
- u32 com_lrremap;
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;
6467 u32 com_offset;
65
- u32 com_seq;
6668 u32 contrast;
6769 u32 dclk_div;
6870 u32 dclk_frq;
....@@ -70,21 +72,21 @@
7072 struct i2c_client *client;
7173 u32 height;
7274 struct fb_info *info;
75
+ u8 lookup_table[4];
7376 u32 page_offset;
77
+ u32 col_offset;
7478 u32 prechargep1;
7579 u32 prechargep2;
7680 struct pwm_device *pwm;
77
- u32 pwm_period;
7881 struct gpio_desc *reset;
7982 struct regulator *vbat_reg;
80
- u32 seg_remap;
8183 u32 vcomh;
8284 u32 width;
8385 };
8486
8587 struct ssd1307fb_array {
8688 u8 type;
87
- u8 data[0];
89
+ u8 data[];
8890 };
8991
9092 static const struct fb_fix_screeninfo ssd1307fb_fix = {
....@@ -99,6 +101,9 @@
99101
100102 static const struct fb_var_screeninfo ssd1307fb_var = {
101103 .bits_per_pixel = 1,
104
+ .red = { .length = 1 },
105
+ .green = { .length = 1 },
106
+ .blue = { .length = 1 },
102107 };
103108
104109 static struct ssd1307fb_array *ssd1307fb_alloc_array(u32 len, u8 type)
....@@ -150,11 +155,12 @@
150155 static void ssd1307fb_update_display(struct ssd1307fb_par *par)
151156 {
152157 struct ssd1307fb_array *array;
153
- u8 *vmem = par->info->screen_base;
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);
154161 int i, j, k;
155162
156
- array = ssd1307fb_alloc_array(par->width * par->height / 8,
157
- SSD1307FB_DATA);
163
+ array = ssd1307fb_alloc_array(par->width * pages, SSD1307FB_DATA);
158164 if (!array)
159165 return;
160166
....@@ -187,22 +193,24 @@
187193 * (5) A4 B4 C4 D4 E4 F4 G4 H4
188194 */
189195
190
- for (i = 0; i < (par->height / 8); i++) {
196
+ for (i = 0; i < pages; i++) {
191197 for (j = 0; j < par->width; j++) {
198
+ int m = 8;
192199 u32 array_idx = i * par->width + j;
193200 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);
201
+ /* Last page may be partial */
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;
200208 array->data[array_idx] |= bit << k;
201209 }
202210 }
203211 }
204212
205
- ssd1307fb_write_array(par->client, array, par->width * par->height / 8);
213
+ ssd1307fb_write_array(par->client, array, par->width * pages);
206214 kfree(array);
207215 }
208216
....@@ -213,7 +221,7 @@
213221 struct ssd1307fb_par *par = info->par;
214222 unsigned long total_size;
215223 unsigned long p = *ppos;
216
- u8 __iomem *dst;
224
+ void *dst;
217225
218226 total_size = info->fix.smem_len;
219227
....@@ -226,7 +234,7 @@
226234 if (!count)
227235 return -EINVAL;
228236
229
- dst = (void __force *) (info->screen_base + p);
237
+ dst = info->screen_buffer + p;
230238
231239 if (copy_from_user(dst, buf, count))
232240 return -EFAULT;
....@@ -269,7 +277,7 @@
269277 ssd1307fb_update_display(par);
270278 }
271279
272
-static struct fb_ops ssd1307fb_ops = {
280
+static const struct fb_ops ssd1307fb_ops = {
273281 .owner = THIS_MODULE,
274282 .fb_read = fb_sys_read,
275283 .fb_write = ssd1307fb_write,
....@@ -287,9 +295,9 @@
287295
288296 static int ssd1307fb_init(struct ssd1307fb_par *par)
289297 {
298
+ struct pwm_state pwmstate;
290299 int ret;
291300 u32 precharge, dclk, com_invdir, compins;
292
- struct pwm_args pargs;
293301
294302 if (par->device_info->need_pwm) {
295303 par->pwm = pwm_get(&par->client->dev, NULL);
....@@ -298,22 +306,16 @@
298306 return PTR_ERR(par->pwm);
299307 }
300308
301
- /*
302
- * FIXME: pwm_apply_args() should be removed when switching to
303
- * the atomic PWM API.
304
- */
305
- pwm_apply_args(par->pwm);
309
+ pwm_init_state(par->pwm, &pwmstate);
310
+ pwm_set_relative_duty_cycle(&pwmstate, 50, 100);
311
+ pwm_apply_state(par->pwm, &pwmstate);
306312
307
- pwm_get_args(par->pwm, &pargs);
308
-
309
- par->pwm_period = pargs.period;
310313 /* Enable the PWM */
311
- pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period);
312314 pwm_enable(par->pwm);
313315
314
- dev_dbg(&par->client->dev, "Using PWM%d with a %dns period.\n",
315
- par->pwm->pwm, par->pwm_period);
316
- };
316
+ dev_dbg(&par->client->dev, "Using PWM%d with a %lluns period.\n",
317
+ par->pwm->pwm, pwm_get_period(par->pwm));
318
+ }
317319
318320 /* Set initial contrast */
319321 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CONTRAST);
....@@ -329,10 +331,10 @@
329331 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON);
330332 if (ret < 0)
331333 return ret;
332
- };
334
+ }
333335
334336 /* Set COM direction */
335
- com_invdir = 0xc0 | (par->com_invdir & 0x1) << 3;
337
+ com_invdir = 0xc0 | par->com_invdir << 3;
336338 ret = ssd1307fb_write_cmd(par->client, com_invdir);
337339 if (ret < 0)
338340 return ret;
....@@ -365,6 +367,22 @@
365367 if (ret < 0)
366368 return ret;
367369
370
+ /* Set Set Area Color Mode ON/OFF & Low Power Display Mode */
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
+
368386 /* Set precharge period in number of ticks from the internal clock */
369387 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PRECHARGE_PERIOD);
370388 if (ret < 0)
....@@ -380,8 +398,7 @@
380398 if (ret < 0)
381399 return ret;
382400
383
- compins = 0x02 | !(par->com_seq & 0x1) << 4
384
- | (par->com_lrremap & 0x1) << 5;
401
+ compins = 0x02 | !par->com_seq << 4 | par->com_lrremap << 5;
385402 ret = ssd1307fb_write_cmd(par->client, compins);
386403 if (ret < 0)
387404 return ret;
....@@ -405,6 +422,28 @@
405422 if (ret < 0)
406423 return ret;
407424
425
+ /* Set lookup table */
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
+
408447 /* Switch to horizontal addressing mode */
409448 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_ADDRESS_MODE);
410449 if (ret < 0)
....@@ -420,11 +459,11 @@
420459 if (ret < 0)
421460 return ret;
422461
423
- ret = ssd1307fb_write_cmd(par->client, 0x0);
462
+ ret = ssd1307fb_write_cmd(par->client, par->col_offset);
424463 if (ret < 0)
425464 return ret;
426465
427
- ret = ssd1307fb_write_cmd(par->client, par->width - 1);
466
+ ret = ssd1307fb_write_cmd(par->client, par->col_offset + par->width - 1);
428467 if (ret < 0)
429468 return ret;
430469
....@@ -438,7 +477,8 @@
438477 return ret;
439478
440479 ret = ssd1307fb_write_cmd(par->client,
441
- par->page_offset + (par->height / 8) - 1);
480
+ par->page_offset +
481
+ DIV_ROUND_UP(par->height, 8) - 1);
442482 if (ret < 0)
443483 return ret;
444484
....@@ -537,101 +577,104 @@
537577 };
538578 MODULE_DEVICE_TABLE(of, ssd1307fb_of_match);
539579
540
-static int ssd1307fb_probe(struct i2c_client *client,
541
- const struct i2c_device_id *id)
580
+static int ssd1307fb_probe(struct i2c_client *client)
542581 {
582
+ struct device *dev = &client->dev;
543583 struct backlight_device *bl;
544584 char bl_name[12];
545585 struct fb_info *info;
546
- struct device_node *node = client->dev.of_node;
547586 struct fb_deferred_io *ssd1307fb_defio;
548587 u32 vmem_size;
549588 struct ssd1307fb_par *par;
550
- u8 *vmem;
589
+ void *vmem;
551590 int ret;
552591
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");
592
+ info = framebuffer_alloc(sizeof(struct ssd1307fb_par), dev);
593
+ if (!info)
561594 return -ENOMEM;
562
- }
563595
564596 par = info->par;
565597 par->info = info;
566598 par->client = client;
567599
568
- par->device_info = of_device_get_match_data(&client->dev);
600
+ par->device_info = device_get_match_data(dev);
569601
570
- par->reset = devm_gpiod_get_optional(&client->dev, "reset",
571
- GPIOD_OUT_LOW);
602
+ par->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
572603 if (IS_ERR(par->reset)) {
573
- dev_err(&client->dev, "failed to get reset gpio: %ld\n",
604
+ dev_err(dev, "failed to get reset gpio: %ld\n",
574605 PTR_ERR(par->reset));
575606 ret = PTR_ERR(par->reset);
576607 goto fb_alloc_error;
577608 }
578609
579
- par->vbat_reg = devm_regulator_get_optional(&client->dev, "vbat");
610
+ par->vbat_reg = devm_regulator_get_optional(dev, "vbat");
580611 if (IS_ERR(par->vbat_reg)) {
581612 ret = PTR_ERR(par->vbat_reg);
582613 if (ret == -ENODEV) {
583614 par->vbat_reg = NULL;
584615 } else {
585
- dev_err(&client->dev, "failed to get VBAT regulator: %d\n",
586
- ret);
616
+ dev_err(dev, "failed to get VBAT regulator: %d\n", ret);
587617 goto fb_alloc_error;
588618 }
589619 }
590620
591
- if (of_property_read_u32(node, "solomon,width", &par->width))
621
+ if (device_property_read_u32(dev, "solomon,width", &par->width))
592622 par->width = 96;
593623
594
- if (of_property_read_u32(node, "solomon,height", &par->height))
624
+ if (device_property_read_u32(dev, "solomon,height", &par->height))
595625 par->height = 16;
596626
597
- if (of_property_read_u32(node, "solomon,page-offset", &par->page_offset))
627
+ if (device_property_read_u32(dev, "solomon,page-offset", &par->page_offset))
598628 par->page_offset = 1;
599629
600
- if (of_property_read_u32(node, "solomon,com-offset", &par->com_offset))
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))
601634 par->com_offset = 0;
602635
603
- if (of_property_read_u32(node, "solomon,prechargep1", &par->prechargep1))
636
+ if (device_property_read_u32(dev, "solomon,prechargep1", &par->prechargep1))
604637 par->prechargep1 = 2;
605638
606
- if (of_property_read_u32(node, "solomon,prechargep2", &par->prechargep2))
639
+ if (device_property_read_u32(dev, "solomon,prechargep2", &par->prechargep2))
607640 par->prechargep2 = 2;
608641
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");
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");
613654
614655 par->contrast = 127;
615656 par->vcomh = par->device_info->default_vcomh;
616657
617658 /* Setup display timing */
618
- par->dclk_div = par->device_info->default_dclk_div;
619
- par->dclk_frq = par->device_info->default_dclk_frq;
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;
620663
621
- vmem_size = par->width * par->height / 8;
664
+ vmem_size = DIV_ROUND_UP(par->width, 8) * par->height;
622665
623666 vmem = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
624667 get_order(vmem_size));
625668 if (!vmem) {
626
- dev_err(&client->dev, "Couldn't allocate graphical memory.\n");
669
+ dev_err(dev, "Couldn't allocate graphical memory.\n");
627670 ret = -ENOMEM;
628671 goto fb_alloc_error;
629672 }
630673
631
- ssd1307fb_defio = devm_kzalloc(&client->dev, sizeof(*ssd1307fb_defio),
674
+ ssd1307fb_defio = devm_kzalloc(dev, sizeof(*ssd1307fb_defio),
632675 GFP_KERNEL);
633676 if (!ssd1307fb_defio) {
634
- dev_err(&client->dev, "Couldn't allocate deferred io.\n");
677
+ dev_err(dev, "Couldn't allocate deferred io.\n");
635678 ret = -ENOMEM;
636679 goto fb_alloc_error;
637680 }
....@@ -641,7 +684,7 @@
641684
642685 info->fbops = &ssd1307fb_ops;
643686 info->fix = ssd1307fb_fix;
644
- info->fix.line_length = par->width / 8;
687
+ info->fix.line_length = DIV_ROUND_UP(par->width, 8);
645688 info->fbdefio = ssd1307fb_defio;
646689
647690 info->var = ssd1307fb_var;
....@@ -650,14 +693,7 @@
650693 info->var.yres = par->height;
651694 info->var.yres_virtual = par->height;
652695
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;
696
+ info->screen_buffer = vmem;
661697 info->fix.smem_start = __pa(vmem);
662698 info->fix.smem_len = vmem_size;
663699
....@@ -667,17 +703,16 @@
667703
668704 if (par->reset) {
669705 /* Reset the screen */
670
- gpiod_set_value(par->reset, 0);
706
+ gpiod_set_value_cansleep(par->reset, 1);
671707 udelay(4);
672
- gpiod_set_value(par->reset, 1);
708
+ gpiod_set_value_cansleep(par->reset, 0);
673709 udelay(4);
674710 }
675711
676712 if (par->vbat_reg) {
677713 ret = regulator_enable(par->vbat_reg);
678714 if (ret) {
679
- dev_err(&client->dev, "failed to enable VBAT: %d\n",
680
- ret);
715
+ dev_err(dev, "failed to enable VBAT: %d\n", ret);
681716 goto reset_oled_error;
682717 }
683718 }
....@@ -688,17 +723,16 @@
688723
689724 ret = register_framebuffer(info);
690725 if (ret) {
691
- dev_err(&client->dev, "Couldn't register the framebuffer\n");
726
+ dev_err(dev, "Couldn't register the framebuffer\n");
692727 goto panel_init_error;
693728 }
694729
695730 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);
731
+ bl = backlight_device_register(bl_name, dev, par, &ssd1307fb_bl_ops,
732
+ NULL);
698733 if (IS_ERR(bl)) {
699734 ret = PTR_ERR(bl);
700
- dev_err(&client->dev, "unable to register backlight device: %d\n",
701
- ret);
735
+ dev_err(dev, "unable to register backlight device: %d\n", ret);
702736 goto bl_init_error;
703737 }
704738
....@@ -706,7 +740,7 @@
706740 bl->props.max_brightness = MAX_CONTRAST;
707741 info->bl_dev = bl;
708742
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);
743
+ dev_info(dev, "fb%d: %s framebuffer device registered, using %d bytes of video memory\n", info->node, info->fix.id, vmem_size);
710744
711745 return 0;
712746
....@@ -716,7 +750,7 @@
716750 if (par->device_info->need_pwm) {
717751 pwm_disable(par->pwm);
718752 pwm_put(par->pwm);
719
- };
753
+ }
720754 regulator_enable_error:
721755 if (par->vbat_reg)
722756 regulator_disable(par->vbat_reg);
....@@ -740,7 +774,9 @@
740774 if (par->device_info->need_pwm) {
741775 pwm_disable(par->pwm);
742776 pwm_put(par->pwm);
743
- };
777
+ }
778
+ if (par->vbat_reg)
779
+ regulator_disable(par->vbat_reg);
744780 fb_deferred_io_cleanup(info);
745781 __free_pages(__va(info->fix.smem_start), get_order(info->fix.smem_len));
746782 framebuffer_release(info);
....@@ -758,7 +794,7 @@
758794 MODULE_DEVICE_TABLE(i2c, ssd1307fb_i2c_id);
759795
760796 static struct i2c_driver ssd1307fb_driver = {
761
- .probe = ssd1307fb_probe,
797
+ .probe_new = ssd1307fb_probe,
762798 .remove = ssd1307fb_remove,
763799 .id_table = ssd1307fb_i2c_id,
764800 .driver = {