root/drivers/video/fbdev/ssd1307fb.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ssd1307fb_alloc_array
  2. ssd1307fb_write_array
  3. ssd1307fb_write_cmd
  4. ssd1307fb_update_display
  5. ssd1307fb_write
  6. ssd1307fb_blank
  7. ssd1307fb_fillrect
  8. ssd1307fb_copyarea
  9. ssd1307fb_imageblit
  10. ssd1307fb_deferred_io
  11. ssd1307fb_init
  12. ssd1307fb_update_bl
  13. ssd1307fb_get_brightness
  14. ssd1307fb_check_fb
  15. ssd1307fb_probe
  16. ssd1307fb_remove

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

/* [<][>][^][v][top][bottom][index][help] */