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 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	 * The screen is divided in pages, each having a height of 8
155	 * pixels, and the width of the screen. When sending a byte of
156	 * data to the controller, it gives the 8 bits for the current
157	 * column. I.e, the first byte are the 8 bits of the first
158	 * column, then the 8 bits for the second column, etc.
159	 *
160	 *
161	 * Representation of the screen, assuming it is 5 bits
162	 * wide. Each letter-number combination is a bit that controls
163	 * one pixel.
164	 *
165	 * A0 A1 A2 A3 A4
166	 * B0 B1 B2 B3 B4
167	 * C0 C1 C2 C3 C4
168	 * D0 D1 D2 D3 D4
169	 * E0 E1 E2 E3 E4
170	 * F0 F1 F2 F3 F4
171	 * G0 G1 G2 G3 G4
172	 * H0 H1 H2 H3 H4
173	 *
174	 * If you want to update this screen, you need to send 5 bytes:
175	 *  (1) A0 B0 C0 D0 E0 F0 G0 H0
176	 *  (2) A1 B1 C1 D1 E1 F1 G1 H1
177	 *  (3) A2 B2 C2 D2 E2 F2 G2 H2
178	 *  (4) A3 B3 C3 D3 E3 F3 G3 H3
179	 *  (5) A4 B4 C4 D4 E4 F4 G4 H4
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	/* Enable the PWM */
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	/* Map column 127 of the OLED to segment 0 */
293	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON);
294	if (ret < 0)
295		return ret;
296
297	/* Turn on the display */
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	/* Set initial contrast */
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	/* Set COM direction */
328	ret = ssd1307fb_write_cmd(par->client, 0xc8);
329	if (ret < 0)
330		return ret;
331
332	/* Set segment re-map */
333	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON);
334	if (ret < 0)
335		return ret;
336
337	/* Set multiplex ratio value */
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	/* set display offset value */
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	/* Set clock frequency */
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	/* Set precharge period in number of ticks from the internal clock */
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	/* Set COM pins configuration */
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	/* Set VCOMH */
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	/* Turn on the DC-DC Charge Pump */
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	/* Switch to horizontal addressing mode */
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	/* Turn on the display */
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	/* Reset the screen */
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