1
2
3
4
5
6
7
8
9#include <linux/module.h>
10#include <linux/kernel.h>
11#include <linux/i2c.h>
12#include <linux/fb.h>
13#include <linux/uaccess.h>
14#include <linux/of_device.h>
15#include <linux/of_gpio.h>
16#include <linux/pwm.h>
17#include <linux/delay.h>
18
19#define SSD1307FB_DATA 0x40
20#define SSD1307FB_COMMAND 0x80
21
22#define SSD1307FB_SET_ADDRESS_MODE 0x20
23#define SSD1307FB_SET_ADDRESS_MODE_HORIZONTAL (0x00)
24#define SSD1307FB_SET_ADDRESS_MODE_VERTICAL (0x01)
25#define SSD1307FB_SET_ADDRESS_MODE_PAGE (0x02)
26#define SSD1307FB_SET_COL_RANGE 0x21
27#define SSD1307FB_SET_PAGE_RANGE 0x22
28#define SSD1307FB_CONTRAST 0x81
29#define SSD1307FB_CHARGE_PUMP 0x8d
30#define SSD1307FB_SEG_REMAP_ON 0xa1
31#define SSD1307FB_DISPLAY_OFF 0xae
32#define SSD1307FB_SET_MULTIPLEX_RATIO 0xa8
33#define SSD1307FB_DISPLAY_ON 0xaf
34#define SSD1307FB_START_PAGE_ADDRESS 0xb0
35#define SSD1307FB_SET_DISPLAY_OFFSET 0xd3
36#define SSD1307FB_SET_CLOCK_FREQ 0xd5
37#define SSD1307FB_SET_PRECHARGE_PERIOD 0xd9
38#define SSD1307FB_SET_COM_PINS_CONFIG 0xda
39#define SSD1307FB_SET_VCOMH 0xdb
40
41struct ssd1307fb_par;
42
43struct ssd1307fb_ops {
44 int (*init)(struct ssd1307fb_par *);
45 int (*remove)(struct ssd1307fb_par *);
46};
47
48struct ssd1307fb_par {
49 struct i2c_client *client;
50 u32 height;
51 struct fb_info *info;
52 struct ssd1307fb_ops *ops;
53 u32 page_offset;
54 struct pwm_device *pwm;
55 u32 pwm_period;
56 int reset;
57 u32 width;
58};
59
60struct ssd1307fb_array {
61 u8 type;
62 u8 data[0];
63};
64
65static struct fb_fix_screeninfo ssd1307fb_fix = {
66 .id = "Solomon SSD1307",
67 .type = FB_TYPE_PACKED_PIXELS,
68 .visual = FB_VISUAL_MONO10,
69 .xpanstep = 0,
70 .ypanstep = 0,
71 .ywrapstep = 0,
72 .accel = FB_ACCEL_NONE,
73};
74
75static struct fb_var_screeninfo ssd1307fb_var = {
76 .bits_per_pixel = 1,
77};
78
79static struct ssd1307fb_array *ssd1307fb_alloc_array(u32 len, u8 type)
80{
81 struct ssd1307fb_array *array;
82
83 array = kzalloc(sizeof(struct ssd1307fb_array) + len, GFP_KERNEL);
84 if (!array)
85 return NULL;
86
87 array->type = type;
88
89 return array;
90}
91
92static int ssd1307fb_write_array(struct i2c_client *client,
93 struct ssd1307fb_array *array, u32 len)
94{
95 int ret;
96
97 len += sizeof(struct ssd1307fb_array);
98
99 ret = i2c_master_send(client, (u8 *)array, len);
100 if (ret != len) {
101 dev_err(&client->dev, "Couldn't send I2C command.\n");
102 return ret;
103 }
104
105 return 0;
106}
107
108static inline int ssd1307fb_write_cmd(struct i2c_client *client, u8 cmd)
109{
110 struct ssd1307fb_array *array;
111 int ret;
112
113 array = ssd1307fb_alloc_array(1, SSD1307FB_COMMAND);
114 if (!array)
115 return -ENOMEM;
116
117 array->data[0] = cmd;
118
119 ret = ssd1307fb_write_array(client, array, 1);
120 kfree(array);
121
122 return ret;
123}
124
125static inline int ssd1307fb_write_data(struct i2c_client *client, u8 data)
126{
127 struct ssd1307fb_array *array;
128 int ret;
129
130 array = ssd1307fb_alloc_array(1, SSD1307FB_DATA);
131 if (!array)
132 return -ENOMEM;
133
134 array->data[0] = data;
135
136 ret = ssd1307fb_write_array(client, array, 1);
137 kfree(array);
138
139 return ret;
140}
141
142static void ssd1307fb_update_display(struct ssd1307fb_par *par)
143{
144 struct ssd1307fb_array *array;
145 u8 *vmem = par->info->screen_base;
146 int i, j, k;
147
148 array = ssd1307fb_alloc_array(par->width * par->height / 8,
149 SSD1307FB_DATA);
150 if (!array)
151 return;
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182 for (i = 0; i < (par->height / 8); i++) {
183 for (j = 0; j < par->width; j++) {
184 u32 array_idx = i * par->width + j;
185 array->data[array_idx] = 0;
186 for (k = 0; k < 8; k++) {
187 u32 page_length = par->width * i;
188 u32 index = page_length + (par->width * k + j) / 8;
189 u8 byte = *(vmem + index);
190 u8 bit = byte & (1 << (j % 8));
191 bit = bit >> (j % 8);
192 array->data[array_idx] |= bit << k;
193 }
194 }
195 }
196
197 ssd1307fb_write_array(par->client, array, par->width * par->height / 8);
198 kfree(array);
199}
200
201
202static ssize_t ssd1307fb_write(struct fb_info *info, const char __user *buf,
203 size_t count, loff_t *ppos)
204{
205 struct ssd1307fb_par *par = info->par;
206 unsigned long total_size;
207 unsigned long p = *ppos;
208 u8 __iomem *dst;
209
210 total_size = info->fix.smem_len;
211
212 if (p > total_size)
213 return -EINVAL;
214
215 if (count + p > total_size)
216 count = total_size - p;
217
218 if (!count)
219 return -EINVAL;
220
221 dst = (void __force *) (info->screen_base + p);
222
223 if (copy_from_user(dst, buf, count))
224 return -EFAULT;
225
226 ssd1307fb_update_display(par);
227
228 *ppos += count;
229
230 return count;
231}
232
233static void ssd1307fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
234{
235 struct ssd1307fb_par *par = info->par;
236 sys_fillrect(info, rect);
237 ssd1307fb_update_display(par);
238}
239
240static void ssd1307fb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
241{
242 struct ssd1307fb_par *par = info->par;
243 sys_copyarea(info, area);
244 ssd1307fb_update_display(par);
245}
246
247static void ssd1307fb_imageblit(struct fb_info *info, const struct fb_image *image)
248{
249 struct ssd1307fb_par *par = info->par;
250 sys_imageblit(info, image);
251 ssd1307fb_update_display(par);
252}
253
254static struct fb_ops ssd1307fb_ops = {
255 .owner = THIS_MODULE,
256 .fb_read = fb_sys_read,
257 .fb_write = ssd1307fb_write,
258 .fb_fillrect = ssd1307fb_fillrect,
259 .fb_copyarea = ssd1307fb_copyarea,
260 .fb_imageblit = ssd1307fb_imageblit,
261};
262
263static void ssd1307fb_deferred_io(struct fb_info *info,
264 struct list_head *pagelist)
265{
266 ssd1307fb_update_display(info->par);
267}
268
269static struct fb_deferred_io ssd1307fb_defio = {
270 .delay = HZ,
271 .deferred_io = ssd1307fb_deferred_io,
272};
273
274static int ssd1307fb_ssd1307_init(struct ssd1307fb_par *par)
275{
276 int ret;
277
278 par->pwm = pwm_get(&par->client->dev, NULL);
279 if (IS_ERR(par->pwm)) {
280 dev_err(&par->client->dev, "Could not get PWM from device tree!\n");
281 return PTR_ERR(par->pwm);
282 }
283
284 par->pwm_period = pwm_get_period(par->pwm);
285
286 pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period);
287 pwm_enable(par->pwm);
288
289 dev_dbg(&par->client->dev, "Using PWM%d with a %dns period.\n",
290 par->pwm->pwm, par->pwm_period);
291
292
293 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON);
294 if (ret < 0)
295 return ret;
296
297
298 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON);
299 if (ret < 0)
300 return ret;
301
302 return 0;
303}
304
305static int ssd1307fb_ssd1307_remove(struct ssd1307fb_par *par)
306{
307 pwm_disable(par->pwm);
308 pwm_put(par->pwm);
309 return 0;
310}
311
312static struct ssd1307fb_ops ssd1307fb_ssd1307_ops = {
313 .init = ssd1307fb_ssd1307_init,
314 .remove = ssd1307fb_ssd1307_remove,
315};
316
317static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
318{
319 int ret;
320
321
322 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CONTRAST);
323 ret = ret & ssd1307fb_write_cmd(par->client, 0x7f);
324 if (ret < 0)
325 return ret;
326
327
328 ret = ssd1307fb_write_cmd(par->client, 0xc8);
329 if (ret < 0)
330 return ret;
331
332
333 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON);
334 if (ret < 0)
335 return ret;
336
337
338 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_MULTIPLEX_RATIO);
339 ret = ret & ssd1307fb_write_cmd(par->client, par->height - 1);
340 if (ret < 0)
341 return ret;
342
343
344 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_DISPLAY_OFFSET);
345 ret = ssd1307fb_write_cmd(par->client, 0x20);
346 if (ret < 0)
347 return ret;
348
349
350 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_CLOCK_FREQ);
351 ret = ret & ssd1307fb_write_cmd(par->client, 0xf0);
352 if (ret < 0)
353 return ret;
354
355
356 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PRECHARGE_PERIOD);
357 ret = ret & ssd1307fb_write_cmd(par->client, 0x22);
358 if (ret < 0)
359 return ret;
360
361
362 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COM_PINS_CONFIG);
363 ret = ret & ssd1307fb_write_cmd(par->client, 0x22);
364 if (ret < 0)
365 return ret;
366
367
368 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_VCOMH);
369 ret = ret & ssd1307fb_write_cmd(par->client, 0x49);
370 if (ret < 0)
371 return ret;
372
373
374 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CHARGE_PUMP);
375 ret = ret & ssd1307fb_write_cmd(par->client, 0x14);
376 if (ret < 0)
377 return ret;
378
379
380 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_ADDRESS_MODE);
381 ret = ret & ssd1307fb_write_cmd(par->client,
382 SSD1307FB_SET_ADDRESS_MODE_HORIZONTAL);
383 if (ret < 0)
384 return ret;
385
386 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COL_RANGE);
387 ret = ret & ssd1307fb_write_cmd(par->client, 0x0);
388 ret = ret & ssd1307fb_write_cmd(par->client, par->width - 1);
389 if (ret < 0)
390 return ret;
391
392 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PAGE_RANGE);
393 ret = ret & ssd1307fb_write_cmd(par->client, 0x0);
394 ret = ret & ssd1307fb_write_cmd(par->client,
395 par->page_offset + (par->height / 8) - 1);
396 if (ret < 0)
397 return ret;
398
399
400 ret = ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON);
401 if (ret < 0)
402 return ret;
403
404 return 0;
405}
406
407static struct ssd1307fb_ops ssd1307fb_ssd1306_ops = {
408 .init = ssd1307fb_ssd1306_init,
409};
410
411static const struct of_device_id ssd1307fb_of_match[] = {
412 {
413 .compatible = "solomon,ssd1306fb-i2c",
414 .data = (void *)&ssd1307fb_ssd1306_ops,
415 },
416 {
417 .compatible = "solomon,ssd1307fb-i2c",
418 .data = (void *)&ssd1307fb_ssd1307_ops,
419 },
420 {},
421};
422MODULE_DEVICE_TABLE(of, ssd1307fb_of_match);
423
424static int ssd1307fb_probe(struct i2c_client *client,
425 const struct i2c_device_id *id)
426{
427 struct fb_info *info;
428 struct device_node *node = client->dev.of_node;
429 u32 vmem_size;
430 struct ssd1307fb_par *par;
431 u8 *vmem;
432 int ret;
433
434 if (!node) {
435 dev_err(&client->dev, "No device tree data found!\n");
436 return -EINVAL;
437 }
438
439 info = framebuffer_alloc(sizeof(struct ssd1307fb_par), &client->dev);
440 if (!info) {
441 dev_err(&client->dev, "Couldn't allocate framebuffer.\n");
442 return -ENOMEM;
443 }
444
445 par = info->par;
446 par->info = info;
447 par->client = client;
448
449 par->ops = (struct ssd1307fb_ops *)of_match_device(ssd1307fb_of_match,
450 &client->dev)->data;
451
452 par->reset = of_get_named_gpio(client->dev.of_node,
453 "reset-gpios", 0);
454 if (!gpio_is_valid(par->reset)) {
455 ret = -EINVAL;
456 goto fb_alloc_error;
457 }
458
459 if (of_property_read_u32(node, "solomon,width", &par->width))
460 par->width = 96;
461
462 if (of_property_read_u32(node, "solomon,height", &par->height))
463 par->width = 16;
464
465 if (of_property_read_u32(node, "solomon,page-offset", &par->page_offset))
466 par->page_offset = 1;
467
468 vmem_size = par->width * par->height / 8;
469
470 vmem = devm_kzalloc(&client->dev, vmem_size, GFP_KERNEL);
471 if (!vmem) {
472 dev_err(&client->dev, "Couldn't allocate graphical memory.\n");
473 ret = -ENOMEM;
474 goto fb_alloc_error;
475 }
476
477 info->fbops = &ssd1307fb_ops;
478 info->fix = ssd1307fb_fix;
479 info->fix.line_length = par->width / 8;
480 info->fbdefio = &ssd1307fb_defio;
481
482 info->var = ssd1307fb_var;
483 info->var.xres = par->width;
484 info->var.xres_virtual = par->width;
485 info->var.yres = par->height;
486 info->var.yres_virtual = par->height;
487
488 info->var.red.length = 1;
489 info->var.red.offset = 0;
490 info->var.green.length = 1;
491 info->var.green.offset = 0;
492 info->var.blue.length = 1;
493 info->var.blue.offset = 0;
494
495 info->screen_base = (u8 __force __iomem *)vmem;
496 info->fix.smem_start = (unsigned long)vmem;
497 info->fix.smem_len = vmem_size;
498
499 fb_deferred_io_init(info);
500
501 ret = devm_gpio_request_one(&client->dev, par->reset,
502 GPIOF_OUT_INIT_HIGH,
503 "oled-reset");
504 if (ret) {
505 dev_err(&client->dev,
506 "failed to request gpio %d: %d\n",
507 par->reset, ret);
508 goto reset_oled_error;
509 }
510
511 i2c_set_clientdata(client, info);
512
513
514 gpio_set_value(par->reset, 0);
515 udelay(4);
516 gpio_set_value(par->reset, 1);
517 udelay(4);
518
519 if (par->ops->init) {
520 ret = par->ops->init(par);
521 if (ret)
522 goto reset_oled_error;
523 }
524
525 ret = register_framebuffer(info);
526 if (ret) {
527 dev_err(&client->dev, "Couldn't register the framebuffer\n");
528 goto panel_init_error;
529 }
530
531 dev_info(&client->dev, "fb%d: %s framebuffer device registered, using %d bytes of video memory\n", info->node, info->fix.id, vmem_size);
532
533 return 0;
534
535panel_init_error:
536 if (par->ops->remove)
537 par->ops->remove(par);
538reset_oled_error:
539 fb_deferred_io_cleanup(info);
540fb_alloc_error:
541 framebuffer_release(info);
542 return ret;
543}
544
545static int ssd1307fb_remove(struct i2c_client *client)
546{
547 struct fb_info *info = i2c_get_clientdata(client);
548 struct ssd1307fb_par *par = info->par;
549
550 unregister_framebuffer(info);
551 if (par->ops->remove)
552 par->ops->remove(par);
553 fb_deferred_io_cleanup(info);
554 framebuffer_release(info);
555
556 return 0;
557}
558
559static const struct i2c_device_id ssd1307fb_i2c_id[] = {
560 { "ssd1306fb", 0 },
561 { "ssd1307fb", 0 },
562 { }
563};
564MODULE_DEVICE_TABLE(i2c, ssd1307fb_i2c_id);
565
566static struct i2c_driver ssd1307fb_driver = {
567 .probe = ssd1307fb_probe,
568 .remove = ssd1307fb_remove,
569 .id_table = ssd1307fb_i2c_id,
570 .driver = {
571 .name = "ssd1307fb",
572 .of_match_table = ssd1307fb_of_match,
573 .owner = THIS_MODULE,
574 },
575};
576
577module_i2c_driver(ssd1307fb_driver);
578
579MODULE_DESCRIPTION("FB driver for the Solomon SSD1307 OLED controller");
580MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
581MODULE_LICENSE("GPL");
582