0001
0002
0003
0004
0005
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
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
0229
0230
0231
0232
0233
0234
0235
0236
0237
0238
0239
0240
0241
0242
0243
0244
0245
0246
0247
0248
0249
0250
0251
0252
0253
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
0541 ret = ssd1307fb_update_display(par);
0542 if (ret < 0)
0543 return ret;
0544
0545
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
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
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");