Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.9-rc8 397 lines 9.6 kB view raw
1/* 2 * Driver for the Solomon SSD1307 OLED controler 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_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 * The screen is divided in pages, each having a height of 8 110 * pixels, and the width of the screen. When sending a byte of 111 * data to the controller, it gives the 8 bits for the current 112 * column. I.e, the first byte are the 8 bits of the first 113 * column, then the 8 bits for the second column, etc. 114 * 115 * 116 * Representation of the screen, assuming it is 5 bits 117 * wide. Each letter-number combination is a bit that controls 118 * one pixel. 119 * 120 * A0 A1 A2 A3 A4 121 * B0 B1 B2 B3 B4 122 * C0 C1 C2 C3 C4 123 * D0 D1 D2 D3 D4 124 * E0 E1 E2 E3 E4 125 * F0 F1 F2 F3 F4 126 * G0 G1 G2 G3 G4 127 * H0 H1 H2 H3 H4 128 * 129 * If you want to update this screen, you need to send 5 bytes: 130 * (1) A0 B0 C0 D0 E0 F0 G0 H0 131 * (2) A1 B1 C1 D1 E1 F1 G1 H1 132 * (3) A2 B2 C2 D2 E2 F2 G2 H2 133 * (4) A3 B3 C3 D3 E3 F3 G3 H3 134 * (5) A4 B4 C4 D4 E4 F4 G4 H4 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 /* Reset the screen */ 316 gpio_set_value(par->reset, 0); 317 udelay(4); 318 gpio_set_value(par->reset, 1); 319 udelay(4); 320 321 /* Enable the PWM */ 322 pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period); 323 pwm_enable(par->pwm); 324 325 /* Map column 127 of the OLED to segment 0 */ 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 /* Turn on the display */ 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 controler"); 396MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); 397MODULE_LICENSE("GPL");