Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Driver for the Solomon SSD1307 OLED controller
0004  *
0005  * Copyright 2012 Free Electrons
0006  */
0007 
0008 #include <linux/backlight.h>
0009 #include <linux/delay.h>
0010 #include <linux/fb.h>
0011 #include <linux/gpio/consumer.h>
0012 #include <linux/i2c.h>
0013 #include <linux/kernel.h>
0014 #include <linux/module.h>
0015 #include <linux/property.h>
0016 #include <linux/pwm.h>
0017 #include <linux/uaccess.h>
0018 #include <linux/regulator/consumer.h>
0019 
0020 #define SSD1307FB_DATA          0x40
0021 #define SSD1307FB_COMMAND       0x80
0022 
0023 #define SSD1307FB_SET_ADDRESS_MODE  0x20
0024 #define SSD1307FB_SET_ADDRESS_MODE_HORIZONTAL   (0x00)
0025 #define SSD1307FB_SET_ADDRESS_MODE_VERTICAL (0x01)
0026 #define SSD1307FB_SET_ADDRESS_MODE_PAGE     (0x02)
0027 #define SSD1307FB_SET_COL_RANGE     0x21
0028 #define SSD1307FB_SET_PAGE_RANGE    0x22
0029 #define SSD1307FB_CONTRAST      0x81
0030 #define SSD1307FB_SET_LOOKUP_TABLE  0x91
0031 #define SSD1307FB_CHARGE_PUMP       0x8d
0032 #define SSD1307FB_SEG_REMAP_ON      0xa1
0033 #define SSD1307FB_DISPLAY_OFF       0xae
0034 #define SSD1307FB_SET_MULTIPLEX_RATIO   0xa8
0035 #define SSD1307FB_DISPLAY_ON        0xaf
0036 #define SSD1307FB_START_PAGE_ADDRESS    0xb0
0037 #define SSD1307FB_SET_DISPLAY_OFFSET    0xd3
0038 #define SSD1307FB_SET_CLOCK_FREQ    0xd5
0039 #define SSD1307FB_SET_AREA_COLOR_MODE   0xd8
0040 #define SSD1307FB_SET_PRECHARGE_PERIOD  0xd9
0041 #define SSD1307FB_SET_COM_PINS_CONFIG   0xda
0042 #define SSD1307FB_SET_VCOMH     0xdb
0043 
0044 #define MAX_CONTRAST 255
0045 
0046 #define REFRESHRATE 1
0047 
0048 static u_int refreshrate = REFRESHRATE;
0049 module_param(refreshrate, uint, 0);
0050 
0051 struct ssd1307fb_deviceinfo {
0052     u32 default_vcomh;
0053     u32 default_dclk_div;
0054     u32 default_dclk_frq;
0055     int need_pwm;
0056     int need_chargepump;
0057 };
0058 
0059 struct ssd1307fb_par {
0060     unsigned area_color_enable : 1;
0061     unsigned com_invdir : 1;
0062     unsigned com_lrremap : 1;
0063     unsigned com_seq : 1;
0064     unsigned lookup_table_set : 1;
0065     unsigned low_power : 1;
0066     unsigned seg_remap : 1;
0067     u32 com_offset;
0068     u32 contrast;
0069     u32 dclk_div;
0070     u32 dclk_frq;
0071     const struct ssd1307fb_deviceinfo *device_info;
0072     struct i2c_client *client;
0073     u32 height;
0074     struct fb_info *info;
0075     u8 lookup_table[4];
0076     u32 page_offset;
0077     u32 col_offset;
0078     u32 prechargep1;
0079     u32 prechargep2;
0080     struct pwm_device *pwm;
0081     struct gpio_desc *reset;
0082     struct regulator *vbat_reg;
0083     u32 vcomh;
0084     u32 width;
0085     /* Cached address ranges */
0086     u8 col_start;
0087     u8 col_end;
0088     u8 page_start;
0089     u8 page_end;
0090 };
0091 
0092 struct ssd1307fb_array {
0093     u8  type;
0094     u8  data[];
0095 };
0096 
0097 static const struct fb_fix_screeninfo ssd1307fb_fix = {
0098     .id     = "Solomon SSD1307",
0099     .type       = FB_TYPE_PACKED_PIXELS,
0100     .visual     = FB_VISUAL_MONO10,
0101     .xpanstep   = 0,
0102     .ypanstep   = 0,
0103     .ywrapstep  = 0,
0104     .accel      = FB_ACCEL_NONE,
0105 };
0106 
0107 static const struct fb_var_screeninfo ssd1307fb_var = {
0108     .bits_per_pixel = 1,
0109     .red = { .length = 1 },
0110     .green = { .length = 1 },
0111     .blue = { .length = 1 },
0112 };
0113 
0114 static struct ssd1307fb_array *ssd1307fb_alloc_array(u32 len, u8 type)
0115 {
0116     struct ssd1307fb_array *array;
0117 
0118     array = kzalloc(sizeof(struct ssd1307fb_array) + len, GFP_KERNEL);
0119     if (!array)
0120         return NULL;
0121 
0122     array->type = type;
0123 
0124     return array;
0125 }
0126 
0127 static int ssd1307fb_write_array(struct i2c_client *client,
0128                  struct ssd1307fb_array *array, u32 len)
0129 {
0130     int ret;
0131 
0132     len += sizeof(struct ssd1307fb_array);
0133 
0134     ret = i2c_master_send(client, (u8 *)array, len);
0135     if (ret != len) {
0136         dev_err(&client->dev, "Couldn't send I2C command.\n");
0137         return ret;
0138     }
0139 
0140     return 0;
0141 }
0142 
0143 static inline int ssd1307fb_write_cmd(struct i2c_client *client, u8 cmd)
0144 {
0145     struct ssd1307fb_array *array;
0146     int ret;
0147 
0148     array = ssd1307fb_alloc_array(1, SSD1307FB_COMMAND);
0149     if (!array)
0150         return -ENOMEM;
0151 
0152     array->data[0] = cmd;
0153 
0154     ret = ssd1307fb_write_array(client, array, 1);
0155     kfree(array);
0156 
0157     return ret;
0158 }
0159 
0160 static int ssd1307fb_set_col_range(struct ssd1307fb_par *par, u8 col_start,
0161                    u8 cols)
0162 {
0163     u8 col_end = col_start + cols - 1;
0164     int ret;
0165 
0166     if (col_start == par->col_start && col_end == par->col_end)
0167         return 0;
0168 
0169     ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COL_RANGE);
0170     if (ret < 0)
0171         return ret;
0172 
0173     ret = ssd1307fb_write_cmd(par->client, col_start);
0174     if (ret < 0)
0175         return ret;
0176 
0177     ret = ssd1307fb_write_cmd(par->client, col_end);
0178     if (ret < 0)
0179         return ret;
0180 
0181     par->col_start = col_start;
0182     par->col_end = col_end;
0183     return 0;
0184 }
0185 
0186 static int ssd1307fb_set_page_range(struct ssd1307fb_par *par, u8 page_start,
0187                     u8 pages)
0188 {
0189     u8 page_end = page_start + pages - 1;
0190     int ret;
0191 
0192     if (page_start == par->page_start && page_end == par->page_end)
0193         return 0;
0194 
0195     ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PAGE_RANGE);
0196     if (ret < 0)
0197         return ret;
0198 
0199     ret = ssd1307fb_write_cmd(par->client, page_start);
0200     if (ret < 0)
0201         return ret;
0202 
0203     ret = ssd1307fb_write_cmd(par->client, page_end);
0204     if (ret < 0)
0205         return ret;
0206 
0207     par->page_start = page_start;
0208     par->page_end = page_end;
0209     return 0;
0210 }
0211 
0212 static int ssd1307fb_update_rect(struct ssd1307fb_par *par, unsigned int x,
0213                  unsigned int y, unsigned int width,
0214                  unsigned int height)
0215 {
0216     struct ssd1307fb_array *array;
0217     u8 *vmem = par->info->screen_buffer;
0218     unsigned int line_length = par->info->fix.line_length;
0219     unsigned int pages = DIV_ROUND_UP(y % 8 + height, 8);
0220     u32 array_idx = 0;
0221     int ret, i, j, k;
0222 
0223     array = ssd1307fb_alloc_array(width * pages, SSD1307FB_DATA);
0224     if (!array)
0225         return -ENOMEM;
0226 
0227     /*
0228      * The screen is divided in pages, each having a height of 8
0229      * pixels, and the width of the screen. When sending a byte of
0230      * data to the controller, it gives the 8 bits for the current
0231      * column. I.e, the first byte are the 8 bits of the first
0232      * column, then the 8 bits for the second column, etc.
0233      *
0234      *
0235      * Representation of the screen, assuming it is 5 bits
0236      * wide. Each letter-number combination is a bit that controls
0237      * one pixel.
0238      *
0239      * A0 A1 A2 A3 A4
0240      * B0 B1 B2 B3 B4
0241      * C0 C1 C2 C3 C4
0242      * D0 D1 D2 D3 D4
0243      * E0 E1 E2 E3 E4
0244      * F0 F1 F2 F3 F4
0245      * G0 G1 G2 G3 G4
0246      * H0 H1 H2 H3 H4
0247      *
0248      * If you want to update this screen, you need to send 5 bytes:
0249      *  (1) A0 B0 C0 D0 E0 F0 G0 H0
0250      *  (2) A1 B1 C1 D1 E1 F1 G1 H1
0251      *  (3) A2 B2 C2 D2 E2 F2 G2 H2
0252      *  (4) A3 B3 C3 D3 E3 F3 G3 H3
0253      *  (5) A4 B4 C4 D4 E4 F4 G4 H4
0254      */
0255 
0256     ret = ssd1307fb_set_col_range(par, par->col_offset + x, width);
0257     if (ret < 0)
0258         goto out_free;
0259 
0260     ret = ssd1307fb_set_page_range(par, par->page_offset + y / 8, pages);
0261     if (ret < 0)
0262         goto out_free;
0263 
0264     for (i = y / 8; i < y / 8 + pages; i++) {
0265         int m = 8;
0266 
0267         /* Last page may be partial */
0268         if (8 * (i + 1) > par->height)
0269             m = par->height % 8;
0270         for (j = x; j < x + width; j++) {
0271             u8 data = 0;
0272 
0273             for (k = 0; k < m; k++) {
0274                 u8 byte = vmem[(8 * i + k) * line_length +
0275                            j / 8];
0276                 u8 bit = (byte >> (j % 8)) & 1;
0277                 data |= bit << k;
0278             }
0279             array->data[array_idx++] = data;
0280         }
0281     }
0282 
0283     ret = ssd1307fb_write_array(par->client, array, width * pages);
0284 
0285 out_free:
0286     kfree(array);
0287     return ret;
0288 }
0289 
0290 static int ssd1307fb_update_display(struct ssd1307fb_par *par)
0291 {
0292     return ssd1307fb_update_rect(par, 0, 0, par->width, par->height);
0293 }
0294 
0295 static ssize_t ssd1307fb_write(struct fb_info *info, const char __user *buf,
0296         size_t count, loff_t *ppos)
0297 {
0298     struct ssd1307fb_par *par = info->par;
0299     unsigned long total_size;
0300     unsigned long p = *ppos;
0301     void *dst;
0302     int ret;
0303 
0304     total_size = info->fix.smem_len;
0305 
0306     if (p > total_size)
0307         return -EINVAL;
0308 
0309     if (count + p > total_size)
0310         count = total_size - p;
0311 
0312     if (!count)
0313         return -EINVAL;
0314 
0315     dst = info->screen_buffer + p;
0316 
0317     if (copy_from_user(dst, buf, count))
0318         return -EFAULT;
0319 
0320     ret = ssd1307fb_update_display(par);
0321     if (ret < 0)
0322         return ret;
0323 
0324     *ppos += count;
0325 
0326     return count;
0327 }
0328 
0329 static int ssd1307fb_blank(int blank_mode, struct fb_info *info)
0330 {
0331     struct ssd1307fb_par *par = info->par;
0332 
0333     if (blank_mode != FB_BLANK_UNBLANK)
0334         return ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_OFF);
0335     else
0336         return ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON);
0337 }
0338 
0339 static void ssd1307fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
0340 {
0341     struct ssd1307fb_par *par = info->par;
0342     sys_fillrect(info, rect);
0343     ssd1307fb_update_rect(par, rect->dx, rect->dy, rect->width,
0344                   rect->height);
0345 }
0346 
0347 static void ssd1307fb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
0348 {
0349     struct ssd1307fb_par *par = info->par;
0350     sys_copyarea(info, area);
0351     ssd1307fb_update_rect(par, area->dx, area->dy, area->width,
0352                   area->height);
0353 }
0354 
0355 static void ssd1307fb_imageblit(struct fb_info *info, const struct fb_image *image)
0356 {
0357     struct ssd1307fb_par *par = info->par;
0358     sys_imageblit(info, image);
0359     ssd1307fb_update_rect(par, image->dx, image->dy, image->width,
0360                   image->height);
0361 }
0362 
0363 static const struct fb_ops ssd1307fb_ops = {
0364     .owner      = THIS_MODULE,
0365     .fb_read    = fb_sys_read,
0366     .fb_write   = ssd1307fb_write,
0367     .fb_blank   = ssd1307fb_blank,
0368     .fb_fillrect    = ssd1307fb_fillrect,
0369     .fb_copyarea    = ssd1307fb_copyarea,
0370     .fb_imageblit   = ssd1307fb_imageblit,
0371     .fb_mmap    = fb_deferred_io_mmap,
0372 };
0373 
0374 static void ssd1307fb_deferred_io(struct fb_info *info, struct list_head *pagereflist)
0375 {
0376     ssd1307fb_update_display(info->par);
0377 }
0378 
0379 static int ssd1307fb_init(struct ssd1307fb_par *par)
0380 {
0381     struct pwm_state pwmstate;
0382     int ret;
0383     u32 precharge, dclk, com_invdir, compins;
0384 
0385     if (par->device_info->need_pwm) {
0386         par->pwm = pwm_get(&par->client->dev, NULL);
0387         if (IS_ERR(par->pwm)) {
0388             dev_err(&par->client->dev, "Could not get PWM from device tree!\n");
0389             return PTR_ERR(par->pwm);
0390         }
0391 
0392         pwm_init_state(par->pwm, &pwmstate);
0393         pwm_set_relative_duty_cycle(&pwmstate, 50, 100);
0394         pwm_apply_state(par->pwm, &pwmstate);
0395 
0396         /* Enable the PWM */
0397         pwm_enable(par->pwm);
0398 
0399         dev_dbg(&par->client->dev, "Using PWM%d with a %lluns period.\n",
0400             par->pwm->pwm, pwm_get_period(par->pwm));
0401     }
0402 
0403     /* Set initial contrast */
0404     ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CONTRAST);
0405     if (ret < 0)
0406         return ret;
0407 
0408     ret = ssd1307fb_write_cmd(par->client, par->contrast);
0409     if (ret < 0)
0410         return ret;
0411 
0412     /* Set segment re-map */
0413     if (par->seg_remap) {
0414         ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON);
0415         if (ret < 0)
0416             return ret;
0417     }
0418 
0419     /* Set COM direction */
0420     com_invdir = 0xc0 | par->com_invdir << 3;
0421     ret = ssd1307fb_write_cmd(par->client,  com_invdir);
0422     if (ret < 0)
0423         return ret;
0424 
0425     /* Set multiplex ratio value */
0426     ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_MULTIPLEX_RATIO);
0427     if (ret < 0)
0428         return ret;
0429 
0430     ret = ssd1307fb_write_cmd(par->client, par->height - 1);
0431     if (ret < 0)
0432         return ret;
0433 
0434     /* set display offset value */
0435     ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_DISPLAY_OFFSET);
0436     if (ret < 0)
0437         return ret;
0438 
0439     ret = ssd1307fb_write_cmd(par->client, par->com_offset);
0440     if (ret < 0)
0441         return ret;
0442 
0443     /* Set clock frequency */
0444     ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_CLOCK_FREQ);
0445     if (ret < 0)
0446         return ret;
0447 
0448     dclk = ((par->dclk_div - 1) & 0xf) | (par->dclk_frq & 0xf) << 4;
0449     ret = ssd1307fb_write_cmd(par->client, dclk);
0450     if (ret < 0)
0451         return ret;
0452 
0453     /* Set Area Color Mode ON/OFF & Low Power Display Mode */
0454     if (par->area_color_enable || par->low_power) {
0455         u32 mode;
0456 
0457         ret = ssd1307fb_write_cmd(par->client,
0458                       SSD1307FB_SET_AREA_COLOR_MODE);
0459         if (ret < 0)
0460             return ret;
0461 
0462         mode = (par->area_color_enable ? 0x30 : 0) |
0463             (par->low_power ? 5 : 0);
0464         ret = ssd1307fb_write_cmd(par->client, mode);
0465         if (ret < 0)
0466             return ret;
0467     }
0468 
0469     /* Set precharge period in number of ticks from the internal clock */
0470     ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PRECHARGE_PERIOD);
0471     if (ret < 0)
0472         return ret;
0473 
0474     precharge = (par->prechargep1 & 0xf) | (par->prechargep2 & 0xf) << 4;
0475     ret = ssd1307fb_write_cmd(par->client, precharge);
0476     if (ret < 0)
0477         return ret;
0478 
0479     /* Set COM pins configuration */
0480     ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COM_PINS_CONFIG);
0481     if (ret < 0)
0482         return ret;
0483 
0484     compins = 0x02 | !par->com_seq << 4 | par->com_lrremap << 5;
0485     ret = ssd1307fb_write_cmd(par->client, compins);
0486     if (ret < 0)
0487         return ret;
0488 
0489     /* Set VCOMH */
0490     ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_VCOMH);
0491     if (ret < 0)
0492         return ret;
0493 
0494     ret = ssd1307fb_write_cmd(par->client, par->vcomh);
0495     if (ret < 0)
0496         return ret;
0497 
0498     /* Turn on the DC-DC Charge Pump */
0499     ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CHARGE_PUMP);
0500     if (ret < 0)
0501         return ret;
0502 
0503     ret = ssd1307fb_write_cmd(par->client,
0504         BIT(4) | (par->device_info->need_chargepump ? BIT(2) : 0));
0505     if (ret < 0)
0506         return ret;
0507 
0508     /* Set lookup table */
0509     if (par->lookup_table_set) {
0510         int i;
0511 
0512         ret = ssd1307fb_write_cmd(par->client,
0513                       SSD1307FB_SET_LOOKUP_TABLE);
0514         if (ret < 0)
0515             return ret;
0516 
0517         for (i = 0; i < ARRAY_SIZE(par->lookup_table); ++i) {
0518             u8 val = par->lookup_table[i];
0519 
0520             if (val < 31 || val > 63)
0521                 dev_warn(&par->client->dev,
0522                      "lookup table index %d value out of range 31 <= %d <= 63\n",
0523                      i, val);
0524             ret = ssd1307fb_write_cmd(par->client, val);
0525             if (ret < 0)
0526                 return ret;
0527         }
0528     }
0529 
0530     /* Switch to horizontal addressing mode */
0531     ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_ADDRESS_MODE);
0532     if (ret < 0)
0533         return ret;
0534 
0535     ret = ssd1307fb_write_cmd(par->client,
0536                   SSD1307FB_SET_ADDRESS_MODE_HORIZONTAL);
0537     if (ret < 0)
0538         return ret;
0539 
0540     /* Clear the screen */
0541     ret = ssd1307fb_update_display(par);
0542     if (ret < 0)
0543         return ret;
0544 
0545     /* Turn on the display */
0546     ret = ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON);
0547     if (ret < 0)
0548         return ret;
0549 
0550     return 0;
0551 }
0552 
0553 static int ssd1307fb_update_bl(struct backlight_device *bdev)
0554 {
0555     struct ssd1307fb_par *par = bl_get_data(bdev);
0556     int ret;
0557     int brightness = bdev->props.brightness;
0558 
0559     par->contrast = brightness;
0560 
0561     ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CONTRAST);
0562     if (ret < 0)
0563         return ret;
0564     ret = ssd1307fb_write_cmd(par->client, par->contrast);
0565     if (ret < 0)
0566         return ret;
0567     return 0;
0568 }
0569 
0570 static int ssd1307fb_get_brightness(struct backlight_device *bdev)
0571 {
0572     struct ssd1307fb_par *par = bl_get_data(bdev);
0573 
0574     return par->contrast;
0575 }
0576 
0577 static int ssd1307fb_check_fb(struct backlight_device *bdev,
0578                    struct fb_info *info)
0579 {
0580     return (info->bl_dev == bdev);
0581 }
0582 
0583 static const struct backlight_ops ssd1307fb_bl_ops = {
0584     .options    = BL_CORE_SUSPENDRESUME,
0585     .update_status  = ssd1307fb_update_bl,
0586     .get_brightness = ssd1307fb_get_brightness,
0587     .check_fb   = ssd1307fb_check_fb,
0588 };
0589 
0590 static struct ssd1307fb_deviceinfo ssd1307fb_ssd1305_deviceinfo = {
0591     .default_vcomh = 0x34,
0592     .default_dclk_div = 1,
0593     .default_dclk_frq = 7,
0594 };
0595 
0596 static struct ssd1307fb_deviceinfo ssd1307fb_ssd1306_deviceinfo = {
0597     .default_vcomh = 0x20,
0598     .default_dclk_div = 1,
0599     .default_dclk_frq = 8,
0600     .need_chargepump = 1,
0601 };
0602 
0603 static struct ssd1307fb_deviceinfo ssd1307fb_ssd1307_deviceinfo = {
0604     .default_vcomh = 0x20,
0605     .default_dclk_div = 2,
0606     .default_dclk_frq = 12,
0607     .need_pwm = 1,
0608 };
0609 
0610 static struct ssd1307fb_deviceinfo ssd1307fb_ssd1309_deviceinfo = {
0611     .default_vcomh = 0x34,
0612     .default_dclk_div = 1,
0613     .default_dclk_frq = 10,
0614 };
0615 
0616 static const struct of_device_id ssd1307fb_of_match[] = {
0617     {
0618         .compatible = "solomon,ssd1305fb-i2c",
0619         .data = (void *)&ssd1307fb_ssd1305_deviceinfo,
0620     },
0621     {
0622         .compatible = "solomon,ssd1306fb-i2c",
0623         .data = (void *)&ssd1307fb_ssd1306_deviceinfo,
0624     },
0625     {
0626         .compatible = "solomon,ssd1307fb-i2c",
0627         .data = (void *)&ssd1307fb_ssd1307_deviceinfo,
0628     },
0629     {
0630         .compatible = "solomon,ssd1309fb-i2c",
0631         .data = (void *)&ssd1307fb_ssd1309_deviceinfo,
0632     },
0633     {},
0634 };
0635 MODULE_DEVICE_TABLE(of, ssd1307fb_of_match);
0636 
0637 static int ssd1307fb_probe(struct i2c_client *client)
0638 {
0639     struct device *dev = &client->dev;
0640     struct backlight_device *bl;
0641     char bl_name[12];
0642     struct fb_info *info;
0643     struct fb_deferred_io *ssd1307fb_defio;
0644     u32 vmem_size;
0645     struct ssd1307fb_par *par;
0646     void *vmem;
0647     int ret;
0648 
0649     info = framebuffer_alloc(sizeof(struct ssd1307fb_par), dev);
0650     if (!info)
0651         return -ENOMEM;
0652 
0653     par = info->par;
0654     par->info = info;
0655     par->client = client;
0656 
0657     par->device_info = device_get_match_data(dev);
0658 
0659     par->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
0660     if (IS_ERR(par->reset)) {
0661         ret = dev_err_probe(dev, PTR_ERR(par->reset),
0662                     "failed to get reset gpio\n");
0663         goto fb_alloc_error;
0664     }
0665 
0666     par->vbat_reg = devm_regulator_get_optional(dev, "vbat");
0667     if (IS_ERR(par->vbat_reg)) {
0668         ret = PTR_ERR(par->vbat_reg);
0669         if (ret == -ENODEV) {
0670             par->vbat_reg = NULL;
0671         } else {
0672             dev_err_probe(dev, ret, "failed to get VBAT regulator\n");
0673             goto fb_alloc_error;
0674         }
0675     }
0676 
0677     if (device_property_read_u32(dev, "solomon,width", &par->width))
0678         par->width = 96;
0679 
0680     if (device_property_read_u32(dev, "solomon,height", &par->height))
0681         par->height = 16;
0682 
0683     if (device_property_read_u32(dev, "solomon,page-offset", &par->page_offset))
0684         par->page_offset = 1;
0685 
0686     if (device_property_read_u32(dev, "solomon,col-offset", &par->col_offset))
0687         par->col_offset = 0;
0688 
0689     if (device_property_read_u32(dev, "solomon,com-offset", &par->com_offset))
0690         par->com_offset = 0;
0691 
0692     if (device_property_read_u32(dev, "solomon,prechargep1", &par->prechargep1))
0693         par->prechargep1 = 2;
0694 
0695     if (device_property_read_u32(dev, "solomon,prechargep2", &par->prechargep2))
0696         par->prechargep2 = 2;
0697 
0698     if (!device_property_read_u8_array(dev, "solomon,lookup-table",
0699                        par->lookup_table,
0700                        ARRAY_SIZE(par->lookup_table)))
0701         par->lookup_table_set = 1;
0702 
0703     par->seg_remap = !device_property_read_bool(dev, "solomon,segment-no-remap");
0704     par->com_seq = device_property_read_bool(dev, "solomon,com-seq");
0705     par->com_lrremap = device_property_read_bool(dev, "solomon,com-lrremap");
0706     par->com_invdir = device_property_read_bool(dev, "solomon,com-invdir");
0707     par->area_color_enable =
0708         device_property_read_bool(dev, "solomon,area-color-enable");
0709     par->low_power = device_property_read_bool(dev, "solomon,low-power");
0710 
0711     par->contrast = 127;
0712     par->vcomh = par->device_info->default_vcomh;
0713 
0714     /* Setup display timing */
0715     if (device_property_read_u32(dev, "solomon,dclk-div", &par->dclk_div))
0716         par->dclk_div = par->device_info->default_dclk_div;
0717     if (device_property_read_u32(dev, "solomon,dclk-frq", &par->dclk_frq))
0718         par->dclk_frq = par->device_info->default_dclk_frq;
0719 
0720     vmem_size = DIV_ROUND_UP(par->width, 8) * par->height;
0721 
0722     vmem = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
0723                     get_order(vmem_size));
0724     if (!vmem) {
0725         dev_err(dev, "Couldn't allocate graphical memory.\n");
0726         ret = -ENOMEM;
0727         goto fb_alloc_error;
0728     }
0729 
0730     ssd1307fb_defio = devm_kzalloc(dev, sizeof(*ssd1307fb_defio),
0731                        GFP_KERNEL);
0732     if (!ssd1307fb_defio) {
0733         dev_err(dev, "Couldn't allocate deferred io.\n");
0734         ret = -ENOMEM;
0735         goto fb_alloc_error;
0736     }
0737 
0738     ssd1307fb_defio->delay = HZ / refreshrate;
0739     ssd1307fb_defio->deferred_io = ssd1307fb_deferred_io;
0740 
0741     info->fbops = &ssd1307fb_ops;
0742     info->fix = ssd1307fb_fix;
0743     info->fix.line_length = DIV_ROUND_UP(par->width, 8);
0744     info->fbdefio = ssd1307fb_defio;
0745 
0746     info->var = ssd1307fb_var;
0747     info->var.xres = par->width;
0748     info->var.xres_virtual = par->width;
0749     info->var.yres = par->height;
0750     info->var.yres_virtual = par->height;
0751 
0752     info->screen_buffer = vmem;
0753     info->fix.smem_start = __pa(vmem);
0754     info->fix.smem_len = vmem_size;
0755 
0756     fb_deferred_io_init(info);
0757 
0758     i2c_set_clientdata(client, info);
0759 
0760     if (par->reset) {
0761         /* Reset the screen */
0762         gpiod_set_value_cansleep(par->reset, 1);
0763         udelay(4);
0764         gpiod_set_value_cansleep(par->reset, 0);
0765         udelay(4);
0766     }
0767 
0768     if (par->vbat_reg) {
0769         ret = regulator_enable(par->vbat_reg);
0770         if (ret) {
0771             dev_err(dev, "failed to enable VBAT: %d\n", ret);
0772             goto reset_oled_error;
0773         }
0774     }
0775 
0776     ret = ssd1307fb_init(par);
0777     if (ret)
0778         goto regulator_enable_error;
0779 
0780     ret = register_framebuffer(info);
0781     if (ret) {
0782         dev_err(dev, "Couldn't register the framebuffer\n");
0783         goto panel_init_error;
0784     }
0785 
0786     snprintf(bl_name, sizeof(bl_name), "ssd1307fb%d", info->node);
0787     bl = backlight_device_register(bl_name, dev, par, &ssd1307fb_bl_ops,
0788                        NULL);
0789     if (IS_ERR(bl)) {
0790         ret = PTR_ERR(bl);
0791         dev_err(dev, "unable to register backlight device: %d\n", ret);
0792         goto bl_init_error;
0793     }
0794 
0795     bl->props.brightness = par->contrast;
0796     bl->props.max_brightness = MAX_CONTRAST;
0797     info->bl_dev = bl;
0798 
0799     dev_info(dev, "fb%d: %s framebuffer device registered, using %d bytes of video memory\n", info->node, info->fix.id, vmem_size);
0800 
0801     return 0;
0802 
0803 bl_init_error:
0804     unregister_framebuffer(info);
0805 panel_init_error:
0806     if (par->device_info->need_pwm) {
0807         pwm_disable(par->pwm);
0808         pwm_put(par->pwm);
0809     }
0810 regulator_enable_error:
0811     if (par->vbat_reg)
0812         regulator_disable(par->vbat_reg);
0813 reset_oled_error:
0814     fb_deferred_io_cleanup(info);
0815 fb_alloc_error:
0816     framebuffer_release(info);
0817     return ret;
0818 }
0819 
0820 static int ssd1307fb_remove(struct i2c_client *client)
0821 {
0822     struct fb_info *info = i2c_get_clientdata(client);
0823     struct ssd1307fb_par *par = info->par;
0824 
0825     ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_OFF);
0826 
0827     backlight_device_unregister(info->bl_dev);
0828 
0829     unregister_framebuffer(info);
0830     if (par->device_info->need_pwm) {
0831         pwm_disable(par->pwm);
0832         pwm_put(par->pwm);
0833     }
0834     if (par->vbat_reg)
0835         regulator_disable(par->vbat_reg);
0836     fb_deferred_io_cleanup(info);
0837     __free_pages(__va(info->fix.smem_start), get_order(info->fix.smem_len));
0838     framebuffer_release(info);
0839 
0840     return 0;
0841 }
0842 
0843 static const struct i2c_device_id ssd1307fb_i2c_id[] = {
0844     { "ssd1305fb", 0 },
0845     { "ssd1306fb", 0 },
0846     { "ssd1307fb", 0 },
0847     { "ssd1309fb", 0 },
0848     { }
0849 };
0850 MODULE_DEVICE_TABLE(i2c, ssd1307fb_i2c_id);
0851 
0852 static struct i2c_driver ssd1307fb_driver = {
0853     .probe_new = ssd1307fb_probe,
0854     .remove = ssd1307fb_remove,
0855     .id_table = ssd1307fb_i2c_id,
0856     .driver = {
0857         .name = "ssd1307fb",
0858         .of_match_table = ssd1307fb_of_match,
0859     },
0860 };
0861 
0862 module_i2c_driver(ssd1307fb_driver);
0863 
0864 MODULE_DESCRIPTION("FB driver for the Solomon SSD1307 OLED controller");
0865 MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
0866 MODULE_LICENSE("GPL");