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_WIDTH 96
20#define SSD1307FB_HEIGHT 16
21
22#define SSD1307FB_DATA 0x40
23#define SSD1307FB_COMMAND 0x80
24
25#define SSD1307FB_CONTRAST 0x81
26#define SSD1307FB_SEG_REMAP_ON 0xa1
27#define SSD1307FB_DISPLAY_OFF 0xae
28#define SSD1307FB_DISPLAY_ON 0xaf
29#define SSD1307FB_START_PAGE_ADDRESS 0xb0
30
31struct ssd1307fb_par {
32 struct i2c_client *client;
33 struct fb_info *info;
34 struct pwm_device *pwm;
35 u32 pwm_period;
36 int reset;
37};
38
39static struct fb_fix_screeninfo ssd1307fb_fix = {
40 .id = "Solomon SSD1307",
41 .type = FB_TYPE_PACKED_PIXELS,
42 .visual = FB_VISUAL_MONO10,
43 .xpanstep = 0,
44 .ypanstep = 0,
45 .ywrapstep = 0,
46 .line_length = SSD1307FB_WIDTH / 8,
47 .accel = FB_ACCEL_NONE,
48};
49
50static struct fb_var_screeninfo ssd1307fb_var = {
51 .xres = SSD1307FB_WIDTH,
52 .yres = SSD1307FB_HEIGHT,
53 .xres_virtual = SSD1307FB_WIDTH,
54 .yres_virtual = SSD1307FB_HEIGHT,
55 .bits_per_pixel = 1,
56};
57
58static int ssd1307fb_write_array(struct i2c_client *client, u8 type, u8 *cmd, u32 len)
59{
60 u8 *buf;
61 int ret = 0;
62
63 buf = kzalloc(len + 1, GFP_KERNEL);
64 if (!buf) {
65 dev_err(&client->dev, "Couldn't allocate sending buffer.\n");
66 return -ENOMEM;
67 }
68
69 buf[0] = type;
70 memcpy(buf + 1, cmd, len);
71
72 ret = i2c_master_send(client, buf, len + 1);
73 if (ret != len + 1) {
74 dev_err(&client->dev, "Couldn't send I2C command.\n");
75 goto error;
76 }
77
78error:
79 kfree(buf);
80 return ret;
81}
82
83static inline int ssd1307fb_write_cmd_array(struct i2c_client *client, u8 *cmd, u32 len)
84{
85 return ssd1307fb_write_array(client, SSD1307FB_COMMAND, cmd, len);
86}
87
88static inline int ssd1307fb_write_cmd(struct i2c_client *client, u8 cmd)
89{
90 return ssd1307fb_write_cmd_array(client, &cmd, 1);
91}
92
93static inline int ssd1307fb_write_data_array(struct i2c_client *client, u8 *cmd, u32 len)
94{
95 return ssd1307fb_write_array(client, SSD1307FB_DATA, cmd, len);
96}
97
98static inline int ssd1307fb_write_data(struct i2c_client *client, u8 data)
99{
100 return ssd1307fb_write_data_array(client, &data, 1);
101}
102
103static void ssd1307fb_update_display(struct ssd1307fb_par *par)
104{
105 u8 *vmem = par->info->screen_base;
106 int i, j, k;
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137 for (i = 0; i < (SSD1307FB_HEIGHT / 8); i++) {
138 ssd1307fb_write_cmd(par->client, SSD1307FB_START_PAGE_ADDRESS + (i + 1));
139 ssd1307fb_write_cmd(par->client, 0x00);
140 ssd1307fb_write_cmd(par->client, 0x10);
141
142 for (j = 0; j < SSD1307FB_WIDTH; j++) {
143 u8 buf = 0;
144 for (k = 0; k < 8; k++) {
145 u32 page_length = SSD1307FB_WIDTH * i;
146 u32 index = page_length + (SSD1307FB_WIDTH * k + j) / 8;
147 u8 byte = *(vmem + index);
148 u8 bit = byte & (1 << (j % 8));
149 bit = bit >> (j % 8);
150 buf |= bit << k;
151 }
152 ssd1307fb_write_data(par->client, buf);
153 }
154 }
155}
156
157
158static ssize_t ssd1307fb_write(struct fb_info *info, const char __user *buf,
159 size_t count, loff_t *ppos)
160{
161 struct ssd1307fb_par *par = info->par;
162 unsigned long total_size;
163 unsigned long p = *ppos;
164 u8 __iomem *dst;
165
166 total_size = info->fix.smem_len;
167
168 if (p > total_size)
169 return -EINVAL;
170
171 if (count + p > total_size)
172 count = total_size - p;
173
174 if (!count)
175 return -EINVAL;
176
177 dst = (void __force *) (info->screen_base + p);
178
179 if (copy_from_user(dst, buf, count))
180 return -EFAULT;
181
182 ssd1307fb_update_display(par);
183
184 *ppos += count;
185
186 return count;
187}
188
189static void ssd1307fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
190{
191 struct ssd1307fb_par *par = info->par;
192 sys_fillrect(info, rect);
193 ssd1307fb_update_display(par);
194}
195
196static void ssd1307fb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
197{
198 struct ssd1307fb_par *par = info->par;
199 sys_copyarea(info, area);
200 ssd1307fb_update_display(par);
201}
202
203static void ssd1307fb_imageblit(struct fb_info *info, const struct fb_image *image)
204{
205 struct ssd1307fb_par *par = info->par;
206 sys_imageblit(info, image);
207 ssd1307fb_update_display(par);
208}
209
210static struct fb_ops ssd1307fb_ops = {
211 .owner = THIS_MODULE,
212 .fb_read = fb_sys_read,
213 .fb_write = ssd1307fb_write,
214 .fb_fillrect = ssd1307fb_fillrect,
215 .fb_copyarea = ssd1307fb_copyarea,
216 .fb_imageblit = ssd1307fb_imageblit,
217};
218
219static void ssd1307fb_deferred_io(struct fb_info *info,
220 struct list_head *pagelist)
221{
222 ssd1307fb_update_display(info->par);
223}
224
225static struct fb_deferred_io ssd1307fb_defio = {
226 .delay = HZ,
227 .deferred_io = ssd1307fb_deferred_io,
228};
229
230static int ssd1307fb_probe(struct i2c_client *client,
231 const struct i2c_device_id *id)
232{
233 struct fb_info *info;
234 u32 vmem_size = SSD1307FB_WIDTH * SSD1307FB_HEIGHT / 8;
235 struct ssd1307fb_par *par;
236 u8 *vmem;
237 int ret;
238
239 if (!client->dev.of_node) {
240 dev_err(&client->dev, "No device tree data found!\n");
241 return -EINVAL;
242 }
243
244 info = framebuffer_alloc(sizeof(struct ssd1307fb_par), &client->dev);
245 if (!info) {
246 dev_err(&client->dev, "Couldn't allocate framebuffer.\n");
247 return -ENOMEM;
248 }
249
250 vmem = devm_kzalloc(&client->dev, vmem_size, GFP_KERNEL);
251 if (!vmem) {
252 dev_err(&client->dev, "Couldn't allocate graphical memory.\n");
253 ret = -ENOMEM;
254 goto fb_alloc_error;
255 }
256
257 info->fbops = &ssd1307fb_ops;
258 info->fix = ssd1307fb_fix;
259 info->fbdefio = &ssd1307fb_defio;
260
261 info->var = ssd1307fb_var;
262 info->var.red.length = 1;
263 info->var.red.offset = 0;
264 info->var.green.length = 1;
265 info->var.green.offset = 0;
266 info->var.blue.length = 1;
267 info->var.blue.offset = 0;
268
269 info->screen_base = (u8 __force __iomem *)vmem;
270 info->fix.smem_start = (unsigned long)vmem;
271 info->fix.smem_len = vmem_size;
272
273 fb_deferred_io_init(info);
274
275 par = info->par;
276 par->info = info;
277 par->client = client;
278
279 par->reset = of_get_named_gpio(client->dev.of_node,
280 "reset-gpios", 0);
281 if (!gpio_is_valid(par->reset)) {
282 ret = -EINVAL;
283 goto reset_oled_error;
284 }
285
286 ret = devm_gpio_request_one(&client->dev, par->reset,
287 GPIOF_OUT_INIT_HIGH,
288 "oled-reset");
289 if (ret) {
290 dev_err(&client->dev,
291 "failed to request gpio %d: %d\n",
292 par->reset, ret);
293 goto reset_oled_error;
294 }
295
296 par->pwm = pwm_get(&client->dev, NULL);
297 if (IS_ERR(par->pwm)) {
298 dev_err(&client->dev, "Could not get PWM from device tree!\n");
299 ret = PTR_ERR(par->pwm);
300 goto pwm_error;
301 }
302
303 par->pwm_period = pwm_get_period(par->pwm);
304
305 dev_dbg(&client->dev, "Using PWM%d with a %dns period.\n", par->pwm->pwm, par->pwm_period);
306
307 ret = register_framebuffer(info);
308 if (ret) {
309 dev_err(&client->dev, "Couldn't register the framebuffer\n");
310 goto fbreg_error;
311 }
312
313 i2c_set_clientdata(client, info);
314
315
316 gpio_set_value(par->reset, 0);
317 udelay(4);
318 gpio_set_value(par->reset, 1);
319 udelay(4);
320
321
322 pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period);
323 pwm_enable(par->pwm);
324
325
326 ret = ssd1307fb_write_cmd(client, SSD1307FB_SEG_REMAP_ON);
327 if (ret < 0) {
328 dev_err(&client->dev, "Couldn't remap the screen.\n");
329 goto remap_error;
330 }
331
332
333 ret = ssd1307fb_write_cmd(client, SSD1307FB_DISPLAY_ON);
334 if (ret < 0) {
335 dev_err(&client->dev, "Couldn't turn the display on.\n");
336 goto remap_error;
337 }
338
339 dev_info(&client->dev, "fb%d: %s framebuffer device registered, using %d bytes of video memory\n", info->node, info->fix.id, vmem_size);
340
341 return 0;
342
343remap_error:
344 unregister_framebuffer(info);
345 pwm_disable(par->pwm);
346fbreg_error:
347 pwm_put(par->pwm);
348pwm_error:
349reset_oled_error:
350 fb_deferred_io_cleanup(info);
351fb_alloc_error:
352 framebuffer_release(info);
353 return ret;
354}
355
356static int ssd1307fb_remove(struct i2c_client *client)
357{
358 struct fb_info *info = i2c_get_clientdata(client);
359 struct ssd1307fb_par *par = info->par;
360
361 unregister_framebuffer(info);
362 pwm_disable(par->pwm);
363 pwm_put(par->pwm);
364 fb_deferred_io_cleanup(info);
365 framebuffer_release(info);
366
367 return 0;
368}
369
370static const struct i2c_device_id ssd1307fb_i2c_id[] = {
371 { "ssd1307fb", 0 },
372 { }
373};
374MODULE_DEVICE_TABLE(i2c, ssd1307fb_i2c_id);
375
376static const struct of_device_id ssd1307fb_of_match[] = {
377 { .compatible = "solomon,ssd1307fb-i2c" },
378 {},
379};
380MODULE_DEVICE_TABLE(of, ssd1307fb_of_match);
381
382static struct i2c_driver ssd1307fb_driver = {
383 .probe = ssd1307fb_probe,
384 .remove = ssd1307fb_remove,
385 .id_table = ssd1307fb_i2c_id,
386 .driver = {
387 .name = "ssd1307fb",
388 .of_match_table = of_match_ptr(ssd1307fb_of_match),
389 .owner = THIS_MODULE,
390 },
391};
392
393module_i2c_driver(ssd1307fb_driver);
394
395MODULE_DESCRIPTION("FB driver for the Solomon SSD1307 OLED controller");
396MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
397MODULE_LICENSE("GPL");
398