1/*
2 * Driver for the Solomon SSD1307 OLED controller
3 *
4 * Copyright 2012 Free Electrons
5 *
6 * Licensed under the GPLv2 or later.
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 void ssd1307fb_update_display(struct ssd1307fb_par *par)
126{
127	struct ssd1307fb_array *array;
128	u8 *vmem = par->info->screen_base;
129	int i, j, k;
130
131	array = ssd1307fb_alloc_array(par->width * par->height / 8,
132				      SSD1307FB_DATA);
133	if (!array)
134		return;
135
136	/*
137	 * The screen is divided in pages, each having a height of 8
138	 * pixels, and the width of the screen. When sending a byte of
139	 * data to the controller, it gives the 8 bits for the current
140	 * column. I.e, the first byte are the 8 bits of the first
141	 * column, then the 8 bits for the second column, etc.
142	 *
143	 *
144	 * Representation of the screen, assuming it is 5 bits
145	 * wide. Each letter-number combination is a bit that controls
146	 * one pixel.
147	 *
148	 * A0 A1 A2 A3 A4
149	 * B0 B1 B2 B3 B4
150	 * C0 C1 C2 C3 C4
151	 * D0 D1 D2 D3 D4
152	 * E0 E1 E2 E3 E4
153	 * F0 F1 F2 F3 F4
154	 * G0 G1 G2 G3 G4
155	 * H0 H1 H2 H3 H4
156	 *
157	 * If you want to update this screen, you need to send 5 bytes:
158	 *  (1) A0 B0 C0 D0 E0 F0 G0 H0
159	 *  (2) A1 B1 C1 D1 E1 F1 G1 H1
160	 *  (3) A2 B2 C2 D2 E2 F2 G2 H2
161	 *  (4) A3 B3 C3 D3 E3 F3 G3 H3
162	 *  (5) A4 B4 C4 D4 E4 F4 G4 H4
163	 */
164
165	for (i = 0; i < (par->height / 8); i++) {
166		for (j = 0; j < par->width; j++) {
167			u32 array_idx = i * par->width + j;
168			array->data[array_idx] = 0;
169			for (k = 0; k < 8; k++) {
170				u32 page_length = par->width * i;
171				u32 index = page_length + (par->width * k + j) / 8;
172				u8 byte = *(vmem + index);
173				u8 bit = byte & (1 << (j % 8));
174				bit = bit >> (j % 8);
175				array->data[array_idx] |= bit << k;
176			}
177		}
178	}
179
180	ssd1307fb_write_array(par->client, array, par->width * par->height / 8);
181	kfree(array);
182}
183
184
185static ssize_t ssd1307fb_write(struct fb_info *info, const char __user *buf,
186		size_t count, loff_t *ppos)
187{
188	struct ssd1307fb_par *par = info->par;
189	unsigned long total_size;
190	unsigned long p = *ppos;
191	u8 __iomem *dst;
192
193	total_size = info->fix.smem_len;
194
195	if (p > total_size)
196		return -EINVAL;
197
198	if (count + p > total_size)
199		count = total_size - p;
200
201	if (!count)
202		return -EINVAL;
203
204	dst = (void __force *) (info->screen_base + p);
205
206	if (copy_from_user(dst, buf, count))
207		return -EFAULT;
208
209	ssd1307fb_update_display(par);
210
211	*ppos += count;
212
213	return count;
214}
215
216static void ssd1307fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
217{
218	struct ssd1307fb_par *par = info->par;
219	sys_fillrect(info, rect);
220	ssd1307fb_update_display(par);
221}
222
223static void ssd1307fb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
224{
225	struct ssd1307fb_par *par = info->par;
226	sys_copyarea(info, area);
227	ssd1307fb_update_display(par);
228}
229
230static void ssd1307fb_imageblit(struct fb_info *info, const struct fb_image *image)
231{
232	struct ssd1307fb_par *par = info->par;
233	sys_imageblit(info, image);
234	ssd1307fb_update_display(par);
235}
236
237static struct fb_ops ssd1307fb_ops = {
238	.owner		= THIS_MODULE,
239	.fb_read	= fb_sys_read,
240	.fb_write	= ssd1307fb_write,
241	.fb_fillrect	= ssd1307fb_fillrect,
242	.fb_copyarea	= ssd1307fb_copyarea,
243	.fb_imageblit	= ssd1307fb_imageblit,
244};
245
246static void ssd1307fb_deferred_io(struct fb_info *info,
247				struct list_head *pagelist)
248{
249	ssd1307fb_update_display(info->par);
250}
251
252static struct fb_deferred_io ssd1307fb_defio = {
253	.delay		= HZ,
254	.deferred_io	= ssd1307fb_deferred_io,
255};
256
257static int ssd1307fb_ssd1307_init(struct ssd1307fb_par *par)
258{
259	int ret;
260
261	par->pwm = pwm_get(&par->client->dev, NULL);
262	if (IS_ERR(par->pwm)) {
263		dev_err(&par->client->dev, "Could not get PWM from device tree!\n");
264		return PTR_ERR(par->pwm);
265	}
266
267	par->pwm_period = pwm_get_period(par->pwm);
268	/* Enable the PWM */
269	pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period);
270	pwm_enable(par->pwm);
271
272	dev_dbg(&par->client->dev, "Using PWM%d with a %dns period.\n",
273		par->pwm->pwm, par->pwm_period);
274
275	/* Map column 127 of the OLED to segment 0 */
276	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON);
277	if (ret < 0)
278		return ret;
279
280	/* Turn on the display */
281	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON);
282	if (ret < 0)
283		return ret;
284
285	return 0;
286}
287
288static int ssd1307fb_ssd1307_remove(struct ssd1307fb_par *par)
289{
290	pwm_disable(par->pwm);
291	pwm_put(par->pwm);
292	return 0;
293}
294
295static struct ssd1307fb_ops ssd1307fb_ssd1307_ops = {
296	.init	= ssd1307fb_ssd1307_init,
297	.remove	= ssd1307fb_ssd1307_remove,
298};
299
300static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
301{
302	int ret;
303
304	/* Set initial contrast */
305	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CONTRAST);
306	if (ret < 0)
307		return ret;
308
309	ret = ssd1307fb_write_cmd(par->client, 0x7f);
310	if (ret < 0)
311		return ret;
312
313	/* Set COM direction */
314	ret = ssd1307fb_write_cmd(par->client, 0xc8);
315	if (ret < 0)
316		return ret;
317
318	/* Set segment re-map */
319	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON);
320	if (ret < 0)
321		return ret;
322
323	/* Set multiplex ratio value */
324	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_MULTIPLEX_RATIO);
325	if (ret < 0)
326		return ret;
327
328	ret = ssd1307fb_write_cmd(par->client, par->height - 1);
329	if (ret < 0)
330		return ret;
331
332	/* set display offset value */
333	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_DISPLAY_OFFSET);
334	if (ret < 0)
335		return ret;
336
337	ret = ssd1307fb_write_cmd(par->client, 0x20);
338	if (ret < 0)
339		return ret;
340
341	/* Set clock frequency */
342	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_CLOCK_FREQ);
343	if (ret < 0)
344		return ret;
345
346	ret = ssd1307fb_write_cmd(par->client, 0xf0);
347	if (ret < 0)
348		return ret;
349
350	/* Set precharge period in number of ticks from the internal clock */
351	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PRECHARGE_PERIOD);
352	if (ret < 0)
353		return ret;
354
355	ret = ssd1307fb_write_cmd(par->client, 0x22);
356	if (ret < 0)
357		return ret;
358
359	/* Set COM pins configuration */
360	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COM_PINS_CONFIG);
361	if (ret < 0)
362		return ret;
363
364	ret = ssd1307fb_write_cmd(par->client, 0x22);
365	if (ret < 0)
366		return ret;
367
368	/* Set VCOMH */
369	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_VCOMH);
370	if (ret < 0)
371		return ret;
372
373	ret = ssd1307fb_write_cmd(par->client, 0x49);
374	if (ret < 0)
375		return ret;
376
377	/* Turn on the DC-DC Charge Pump */
378	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CHARGE_PUMP);
379	if (ret < 0)
380		return ret;
381
382	ret = ssd1307fb_write_cmd(par->client, 0x14);
383	if (ret < 0)
384		return ret;
385
386	/* Switch to horizontal addressing mode */
387	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_ADDRESS_MODE);
388	if (ret < 0)
389		return ret;
390
391	ret = ssd1307fb_write_cmd(par->client,
392				  SSD1307FB_SET_ADDRESS_MODE_HORIZONTAL);
393	if (ret < 0)
394		return ret;
395
396	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COL_RANGE);
397	if (ret < 0)
398		return ret;
399
400	ret = ssd1307fb_write_cmd(par->client, 0x0);
401	if (ret < 0)
402		return ret;
403
404	ret = ssd1307fb_write_cmd(par->client, par->width - 1);
405	if (ret < 0)
406		return ret;
407
408	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PAGE_RANGE);
409	if (ret < 0)
410		return ret;
411
412	ret = ssd1307fb_write_cmd(par->client, 0x0);
413	if (ret < 0)
414		return ret;
415
416	ret = ssd1307fb_write_cmd(par->client,
417				  par->page_offset + (par->height / 8) - 1);
418	if (ret < 0)
419		return ret;
420
421	/* Turn on the display */
422	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON);
423	if (ret < 0)
424		return ret;
425
426	return 0;
427}
428
429static struct ssd1307fb_ops ssd1307fb_ssd1306_ops = {
430	.init	= ssd1307fb_ssd1306_init,
431};
432
433static const struct of_device_id ssd1307fb_of_match[] = {
434	{
435		.compatible = "solomon,ssd1306fb-i2c",
436		.data = (void *)&ssd1307fb_ssd1306_ops,
437	},
438	{
439		.compatible = "solomon,ssd1307fb-i2c",
440		.data = (void *)&ssd1307fb_ssd1307_ops,
441	},
442	{},
443};
444MODULE_DEVICE_TABLE(of, ssd1307fb_of_match);
445
446static int ssd1307fb_probe(struct i2c_client *client,
447			   const struct i2c_device_id *id)
448{
449	struct fb_info *info;
450	struct device_node *node = client->dev.of_node;
451	u32 vmem_size;
452	struct ssd1307fb_par *par;
453	u8 *vmem;
454	int ret;
455
456	if (!node) {
457		dev_err(&client->dev, "No device tree data found!\n");
458		return -EINVAL;
459	}
460
461	info = framebuffer_alloc(sizeof(struct ssd1307fb_par), &client->dev);
462	if (!info) {
463		dev_err(&client->dev, "Couldn't allocate framebuffer.\n");
464		return -ENOMEM;
465	}
466
467	par = info->par;
468	par->info = info;
469	par->client = client;
470
471	par->ops = (struct ssd1307fb_ops *)of_match_device(ssd1307fb_of_match,
472							   &client->dev)->data;
473
474	par->reset = of_get_named_gpio(client->dev.of_node,
475					 "reset-gpios", 0);
476	if (!gpio_is_valid(par->reset)) {
477		ret = -EINVAL;
478		goto fb_alloc_error;
479	}
480
481	if (of_property_read_u32(node, "solomon,width", &par->width))
482		par->width = 96;
483
484	if (of_property_read_u32(node, "solomon,height", &par->height))
485		par->height = 16;
486
487	if (of_property_read_u32(node, "solomon,page-offset", &par->page_offset))
488		par->page_offset = 1;
489
490	vmem_size = par->width * par->height / 8;
491
492	vmem = devm_kzalloc(&client->dev, vmem_size, GFP_KERNEL);
493	if (!vmem) {
494		dev_err(&client->dev, "Couldn't allocate graphical memory.\n");
495		ret = -ENOMEM;
496		goto fb_alloc_error;
497	}
498
499	info->fbops = &ssd1307fb_ops;
500	info->fix = ssd1307fb_fix;
501	info->fix.line_length = par->width / 8;
502	info->fbdefio = &ssd1307fb_defio;
503
504	info->var = ssd1307fb_var;
505	info->var.xres = par->width;
506	info->var.xres_virtual = par->width;
507	info->var.yres = par->height;
508	info->var.yres_virtual = par->height;
509
510	info->var.red.length = 1;
511	info->var.red.offset = 0;
512	info->var.green.length = 1;
513	info->var.green.offset = 0;
514	info->var.blue.length = 1;
515	info->var.blue.offset = 0;
516
517	info->screen_base = (u8 __force __iomem *)vmem;
518	info->fix.smem_start = (unsigned long)vmem;
519	info->fix.smem_len = vmem_size;
520
521	fb_deferred_io_init(info);
522
523	ret = devm_gpio_request_one(&client->dev, par->reset,
524				    GPIOF_OUT_INIT_HIGH,
525				    "oled-reset");
526	if (ret) {
527		dev_err(&client->dev,
528			"failed to request gpio %d: %d\n",
529			par->reset, ret);
530		goto reset_oled_error;
531	}
532
533	i2c_set_clientdata(client, info);
534
535	/* Reset the screen */
536	gpio_set_value(par->reset, 0);
537	udelay(4);
538	gpio_set_value(par->reset, 1);
539	udelay(4);
540
541	if (par->ops->init) {
542		ret = par->ops->init(par);
543		if (ret)
544			goto reset_oled_error;
545	}
546
547	ret = register_framebuffer(info);
548	if (ret) {
549		dev_err(&client->dev, "Couldn't register the framebuffer\n");
550		goto panel_init_error;
551	}
552
553	dev_info(&client->dev, "fb%d: %s framebuffer device registered, using %d bytes of video memory\n", info->node, info->fix.id, vmem_size);
554
555	return 0;
556
557panel_init_error:
558	if (par->ops->remove)
559		par->ops->remove(par);
560reset_oled_error:
561	fb_deferred_io_cleanup(info);
562fb_alloc_error:
563	framebuffer_release(info);
564	return ret;
565}
566
567static int ssd1307fb_remove(struct i2c_client *client)
568{
569	struct fb_info *info = i2c_get_clientdata(client);
570	struct ssd1307fb_par *par = info->par;
571
572	unregister_framebuffer(info);
573	if (par->ops->remove)
574		par->ops->remove(par);
575	fb_deferred_io_cleanup(info);
576	framebuffer_release(info);
577
578	return 0;
579}
580
581static const struct i2c_device_id ssd1307fb_i2c_id[] = {
582	{ "ssd1306fb", 0 },
583	{ "ssd1307fb", 0 },
584	{ }
585};
586MODULE_DEVICE_TABLE(i2c, ssd1307fb_i2c_id);
587
588static struct i2c_driver ssd1307fb_driver = {
589	.probe = ssd1307fb_probe,
590	.remove = ssd1307fb_remove,
591	.id_table = ssd1307fb_i2c_id,
592	.driver = {
593		.name = "ssd1307fb",
594		.of_match_table = ssd1307fb_of_match,
595		.owner = THIS_MODULE,
596	},
597};
598
599module_i2c_driver(ssd1307fb_driver);
600
601MODULE_DESCRIPTION("FB driver for the Solomon SSD1307 OLED controller");
602MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
603MODULE_LICENSE("GPL");
604