├── i2c-ch341 ├── src │ ├── Makefile │ └── i2c-ch341.c └── Makefile ├── ssd1306fb ├── src │ ├── Makefile │ └── ssd1306fb.c └── Makefile ├── screenshot.jpg └── README.md /i2c-ch341/src/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += i2c-ch341.o 2 | -------------------------------------------------------------------------------- /ssd1306fb/src/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += ssd1306fb.o 2 | -------------------------------------------------------------------------------- /screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkschreder/hack-ssd1306-openwrt/HEAD/screenshot.jpg -------------------------------------------------------------------------------- /ssd1306fb/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2008-2012 OpenWrt.org 3 | # 4 | # This is free software, licensed under the GNU General Public License v2. 5 | # See /LICENSE for more information. 6 | # 7 | 8 | include $(TOPDIR)/rules.mk 9 | include $(INCLUDE_DIR)/kernel.mk 10 | 11 | PKG_NAME:=ssd1306fb 12 | PKG_RELEASE:=1 13 | 14 | include $(INCLUDE_DIR)/package.mk 15 | 16 | define KernelPackage/ssd1306fb 17 | SUBMENU:=OLED Display Support 18 | TITLE:=SSD1306/07 framebuffer driver 19 | FILES:=$(PKG_BUILD_DIR)/ssd1306fb.ko 20 | AUTOLOAD:=$(call AutoLoad,30,ssd1306fb,1) 21 | KCONFIG:= 22 | endef 23 | 24 | define KernelPackage/ssd1306fb/description 25 | Drives ssd1306/07 oled displays. 26 | endef 27 | 28 | MAKE_OPTS:= \ 29 | ARCH="$(LINUX_KARCH)" \ 30 | CROSS_COMPILE="$(TARGET_CROSS)" \ 31 | SUBDIRS="$(PKG_BUILD_DIR)" 32 | 33 | define Build/Prepare 34 | mkdir -p $(PKG_BUILD_DIR) 35 | $(CP) ./src/* $(PKG_BUILD_DIR)/ 36 | endef 37 | 38 | define Build/Compile 39 | $(MAKE) -C "$(LINUX_DIR)" \ 40 | $(MAKE_OPTS) \ 41 | modules 42 | endef 43 | 44 | $(eval $(call KernelPackage,ssd1306fb)) 45 | -------------------------------------------------------------------------------- /i2c-ch341/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2008-2012 OpenWrt.org 3 | # 4 | # This is free software, licensed under the GNU General Public License v2. 5 | # See /LICENSE for more information. 6 | # 7 | 8 | include $(TOPDIR)/rules.mk 9 | include $(INCLUDE_DIR)/kernel.mk 10 | 11 | PKG_NAME:=i2c-ch341 12 | PKG_RELEASE:=1 13 | 14 | include $(INCLUDE_DIR)/package.mk 15 | 16 | define KernelPackage/i2c-ch341 17 | SUBMENU:=USB Support 18 | TITLE:=CH341 USB-to-I2C SPI UART converter 19 | FILES:=$(PKG_BUILD_DIR)/i2c-ch341.ko 20 | DEPENDS:=+kmod-i2c-core +kmod-usb-core 21 | AUTOLOAD:=$(call AutoLoad,30,i2c-ch341,1) 22 | KCONFIG:= 23 | endef 24 | 25 | define KernelPackage/i2c-ch341/description 26 | Drives ch341 based usb dongles. 27 | endef 28 | 29 | MAKE_OPTS:= \ 30 | ARCH="$(LINUX_KARCH)" \ 31 | CROSS_COMPILE="$(TARGET_CROSS)" \ 32 | SUBDIRS="$(PKG_BUILD_DIR)" 33 | 34 | define Build/Prepare 35 | mkdir -p $(PKG_BUILD_DIR) 36 | $(CP) ./src/* $(PKG_BUILD_DIR)/ 37 | endef 38 | 39 | define Build/Compile 40 | $(MAKE) -C "$(LINUX_DIR)" \ 41 | $(MAKE_OPTS) \ 42 | modules 43 | endef 44 | 45 | $(eval $(call KernelPackage,i2c-ch341)) 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SSD1306 OLED Display On OpenWRT and Hootoo Box 2 | ---------------------------------------------- 3 | 4 | As a fun thing you can do to learn about linux device tree and playing with 5 | linux device drivers is connecting an i2c display through a usb dongle to a 6 | linux box and then making it work with a framebuffer driver. 7 | 8 | This for example allows you to write pixels to the display by using a standard 9 | framebuffer file (/dev/fbX) which a linux application can then easily access. 10 | 11 | To do this you need to get two drivers from this repo. First is a ch341 driver 12 | package for openwrt which I have confirmed works and another one is the ssd1306 13 | framebuffer driver which I modified to work on OpenWRT. 14 | 15 | ![Image](/screenshot.jpg) 16 | 17 | Some points of interest: 18 | 19 | - The ssd1306 driver interfaces with standard linux framebuffer system. I have 20 | modified the ssd1307fb driver already in the main linux tree because I could 21 | not make it work without having to modify it. After replacing init sequence 22 | to use one used by the u8g library things work a lot better. 23 | - To get the display driver to recognise the display, I register it from the 24 | command line like this (0x3c is the i2c address of my display): 25 | 26 | echo "ssd1306fb 0x3c" > /sys/bus/i2c/devices/i2c-0/new_device 27 | 28 | - The driver stores written data in a buffer and schedule work for putting it 29 | to the display. I removed the blocking updates from the original driver so 30 | that update is now triggered to happen in the background. If you are writing 31 | to the display while an update is in progress, the data will be partially new 32 | on the screen, but a new full update will happen after current one is done. 33 | - The i2c interface is very slow. Current implementation has to split writes 34 | into chunks of max 25 bytes due to limitations of the i2c converter. More 35 | info needed. There is probably lots of room for improvement here. But that's 36 | beyond this little test. 37 | 38 | COPYING 39 | ------- 40 | 41 | - Copyright 2016 Martin Schröder 42 | - Copyright [ original authors of driver code ] 43 | 44 | You may use this code according to their original licenses. 45 | -------------------------------------------------------------------------------- /ssd1306fb/src/ssd1306fb.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Driver for the SSD1306 OLED controller 3 | * 4 | * Copyright 2016 Martin K. Schröder 5 | * - making it work with ssd1306 based on u8g code 6 | * - make it use a work queue to blit data to the display 7 | * 8 | * Based on code by: 9 | * Copyright 2012 Free Electrons 10 | * 11 | * Licensed under the GPLv2 or later. 12 | */ 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | 25 | #define SSD1306_DATA 0x40 26 | #define SSD1306_COMMAND 0x00 27 | #define SSD1306_SETLOWCOLUMN 0x00 28 | #define SSD1306_SETHIGHCOLUMN 0x10 29 | #define SSD1306_COLUMNADDR 0x21 30 | #define SSD1306_PAGEADDR 0x22 31 | #define SSD1306_SETSTARTPAGE 0xb0 32 | 33 | // ssd1306 full list of definitions (not used by this driver) 34 | #if 0 35 | #define SSD1306_SETCONTRAST 0x81 36 | #define SSD1306_DISPLAYALLON_RESUME 0xA4 37 | #define SSD1306_DISPLAYALLON 0xA5 38 | #define SSD1306_NORMALDISPLAY 0xA6 39 | #define SSD1306_INVERTDISPLAY 0xA7 40 | #define SSD1306_DISPLAYOFF 0xAE 41 | #define SSD1306_DISPLAYON 0xAF 42 | 43 | #define SSD1306_SETDISPLAYOFFSET 0xD3 44 | #define SSD1306_SETCOMPINS 0xDA 45 | 46 | #define SSD1306_SETVCOMDETECT 0xDB 47 | 48 | #define SSD1306_SETDISPLAYCLOCKDIV 0xD5 49 | #define SSD1306_SETPRECHARGE 0xD9 50 | 51 | #define SSD1306_SETMULTIPLEX 0xA8 52 | 53 | #define SSD1306_SETLOWCOLUMN 0x00 54 | #define SSD1306_SETHIGHCOLUMN 0x10 55 | #define SSD1306_SETSTARTPAGE 0xb0 56 | #define SSD1306_SETSTARTLINE 0x40 57 | 58 | #define SSD1306_MEMORYMODE 0x20 59 | #define SSD1306_COLUMNADDR 0x21 60 | #define SSD1306_PAGEADDR 0x22 61 | 62 | #define SSD1306_COMSCANINC 0xC0 63 | #define SSD1306_COMSCANDEC 0xC8 64 | 65 | #define SSD1306_SEGREMAP 0xA0 66 | #define SSD1306_CHARGEPUMP 0x8D 67 | 68 | #define SSD1306_EXTERNALVCC 0x1 69 | #define SSD1306_SWITCHCAPVCC 0x2 70 | #define SSD1306_SETCONTRAST 0x81 71 | #define SSD1306_DISPLAYALLON_RESUME 0xA4 72 | #define SSD1306_DISPLAYALLON 0xA5 73 | #define SSD1306_NORMALDISPLAY 0xA6 74 | #define SSD1306_INVERTDISPLAY 0xA7 75 | #define SSD1306_DISPLAYOFF 0xAE 76 | #define SSD1306_DISPLAYON 0xAF 77 | #define SSD1306_SETDISPLAYOFFSET 0xD3 78 | #define SSD1306_SETCOMPINS 0xDA 79 | #define SSD1306_SETVCOMDETECT 0xDB 80 | #define SSD1306_SETDISPLAYCLOCKDIV 0xD5 81 | #define SSD1306_SETPRECHARGE 0xD9 82 | #define SSD1306_SETMULTIPLEX 0xA8 83 | #define SSD1306_SETLOWCOLUMN 0x00 84 | #define SSD1306_SETHIGHCOLUMN 0x10 85 | #define SSD1306_SETSTARTLINE 0x40 86 | #define SSD1306_MEMORYMODE 0x20 87 | #define SSD1306_COMSCANINC 0xC0 88 | #define SSD1306_COMSCANDEC 0xC8 89 | #define SSD1306_SEGREMAP 0xA0 90 | #define SSD1306_CHARGEPUMP 0x8D 91 | #define SSD1306_EXTERNALVCC 0x1 92 | #define SSD1306_SWITCHCAPVCC 0x2 93 | 94 | // Scrolling #defines 95 | #define SSD1306_ACTIVATE_SCROLL 0x2F 96 | #define SSD1306_DEACTIVATE_SCROLL 0x2E 97 | #define SSD1306_SET_VERTICAL_SCROLL_AREA 0xA3 98 | #define SSD1306_RIGHT_HORIZONTAL_SCROLL 0x26 99 | #define SSD1306_LEFT_HORIZONTAL_SCROLL 0x27 100 | #define SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL 0x29 101 | #define SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL 0x2A 102 | 103 | #define SSD1306_SET_ADDRESS_MODE 0x20 104 | #define SSD1306_SET_ADDRESS_MODE_HORIZONTAL (0x00) 105 | #define SSD1306_SET_ADDRESS_MODE_VERTICAL (0x01) 106 | #define SSD1306_SET_ADDRESS_MODE_PAGE (0x02) 107 | #define SSD1306_SET_COL_RANGE 0x21 108 | #define SSD1306_SET_PAGE_RANGE 0x22 109 | #define SSD1306_CONTRAST 0x81 110 | #define SSD1306_CHARGE_PUMP 0x8d 111 | #define SSD1306_SEG_REMAP_ON 0xa1 112 | #define SSD1306_DISPLAY_OFF 0xae 113 | #define SSD1306_SET_MULTIPLEX_RATIO 0xa8 114 | #define SSD1306_DISPLAY_ON 0xaf 115 | #define SSD1306_START_PAGE_ADDRESS 0xb0 116 | #define SSD1306_SET_DISPLAY_OFFSET 0xd3 117 | #define SSD1306_SET_CLOCK_FREQ 0xd5 118 | #define SSD1306_SET_PRECHARGE_PERIOD 0xd9 119 | #define SSD1306_SET_COM_PINS_CONFIG 0xda 120 | #define SSD1306_SET_VCOMH 0xdb 121 | #endif 122 | 123 | struct ssd1306fb_par; 124 | 125 | struct ssd1306fb_ops { 126 | int (*init)(struct ssd1306fb_par *); 127 | int (*remove)(struct ssd1306fb_par *); 128 | }; 129 | 130 | struct ssd1306fb_par { 131 | struct i2c_client *client; 132 | u32 height; 133 | struct fb_info *info; 134 | struct ssd1306fb_ops *ops; 135 | u32 page_offset; 136 | struct pwm_device *pwm; 137 | u32 pwm_period; 138 | u32 width; 139 | 140 | // used for temporary data storage 141 | struct ssd1306fb_array *cmd1; 142 | struct ssd1306fb_array *data16; 143 | 144 | struct work_struct work; 145 | }; 146 | 147 | struct ssd1306fb_array { 148 | u8 type; 149 | u8 data[0]; 150 | }; 151 | 152 | static struct fb_fix_screeninfo ssd1306fb_fix = { 153 | .id = "SSD1306 OLED", 154 | .type = FB_TYPE_PACKED_PIXELS, 155 | .visual = FB_VISUAL_MONO10, 156 | .xpanstep = 0, 157 | .ypanstep = 0, 158 | .ywrapstep = 0, 159 | .accel = FB_ACCEL_NONE, 160 | }; 161 | 162 | static struct fb_var_screeninfo ssd1306fb_var = { 163 | .bits_per_pixel = 1, 164 | }; 165 | 166 | static struct ssd1306fb_array *ssd1306fb_alloc_array(u32 len, u8 type){ 167 | struct ssd1306fb_array *array; 168 | 169 | array = kzalloc(sizeof(struct ssd1306fb_array) + len, GFP_KERNEL); 170 | if (!array) 171 | return NULL; 172 | 173 | array->type = type; 174 | 175 | return array; 176 | } 177 | 178 | static int ssd1306fb_write_array(struct ssd1306fb_par *par, 179 | struct ssd1306fb_array *array, u32 len){ 180 | int ret; 181 | 182 | len += sizeof(struct ssd1306fb_array); 183 | 184 | ret = i2c_master_send(par->client, (u8 *)array, len); 185 | if (ret != len) { 186 | dev_err(&par->client->dev, "Couldn't send I2C command.\n"); 187 | return ret; 188 | } 189 | 190 | return 0; 191 | } 192 | 193 | static inline int ssd1306fb_write_cmd(struct ssd1306fb_par *par, u8 cmd){ 194 | par->cmd1->data[0] = cmd; 195 | return ssd1306fb_write_array(par, par->cmd1, 1); 196 | } 197 | 198 | static inline int ssd1306fb_write_data(struct ssd1306fb_par *par, u8 data){ 199 | par->data16->data[0] = data; 200 | return ssd1306fb_write_array(par, par->data16, 1); 201 | } 202 | 203 | static void ssd1306fb_update_display(struct ssd1306fb_par *par){ 204 | u8 *vmem = par->info->screen_base; 205 | int i; 206 | 207 | // go to first character 208 | ssd1306fb_write_cmd(par, SSD1306_SETLOWCOLUMN); 209 | ssd1306fb_write_cmd(par, SSD1306_SETHIGHCOLUMN); 210 | ssd1306fb_write_cmd(par, SSD1306_SETSTARTPAGE); 211 | 212 | ssd1306fb_write_cmd(par, SSD1306_COLUMNADDR); 213 | ssd1306fb_write_cmd(par, 0); // Column start address (0 = reset) 214 | ssd1306fb_write_cmd(par, 127); // Column end address (127 = reset) 215 | 216 | ssd1306fb_write_cmd(par, SSD1306_PAGEADDR); 217 | ssd1306fb_write_cmd(par, 0); // Page start address (0 = reset) 218 | ssd1306fb_write_cmd(par, 7); // Page end address 219 | 220 | 221 | // write in chunks of 16 bytes (can not send more over i2c right now) 222 | for(i = 0; i < 64; i++){ 223 | memcpy(par->data16->data, vmem + i * 16, 16); 224 | ssd1306fb_write_array(par, par->data16, 16); 225 | } 226 | } 227 | 228 | 229 | static ssize_t ssd1306fb_write(struct fb_info *info, const char __user *buf, 230 | size_t count, loff_t *ppos){ 231 | unsigned long total_size; 232 | unsigned long p = *ppos; 233 | struct ssd1306fb_par *par = info->par; 234 | u8 __iomem *dst; 235 | 236 | //printk("ssd1306: write %d at %lu\n", count, p); 237 | 238 | total_size = info->fix.smem_len; 239 | 240 | if (p > total_size) 241 | return -EINVAL; 242 | 243 | if (count + p > total_size) 244 | count = total_size - p; 245 | 246 | if (!count) 247 | return -EINVAL; 248 | 249 | dst = (void __force *) (info->screen_base + p); 250 | 251 | if (copy_from_user(dst, buf, count)) 252 | return -EFAULT; 253 | 254 | schedule_work(&par->work); 255 | 256 | *ppos += count; 257 | 258 | return count; 259 | } 260 | 261 | static void ssd1306fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) 262 | { 263 | struct ssd1306fb_par *par = info->par; 264 | cfb_fillrect(info, rect); 265 | schedule_work(&par->work); 266 | } 267 | 268 | static void ssd1306fb_copyarea(struct fb_info *info, const struct fb_copyarea *area) 269 | { 270 | struct ssd1306fb_par *par = info->par; 271 | cfb_copyarea(info, area); 272 | schedule_work(&par->work); 273 | } 274 | 275 | static void ssd1306fb_imageblit(struct fb_info *info, const struct fb_image *image) 276 | { 277 | struct ssd1306fb_par *par = info->par; 278 | cfb_imageblit(info, image); 279 | schedule_work(&par->work); 280 | } 281 | 282 | static struct fb_ops ssd1306fb_ops = { 283 | .owner = THIS_MODULE, 284 | .fb_write = ssd1306fb_write, 285 | .fb_fillrect = ssd1306fb_fillrect, 286 | .fb_copyarea = ssd1306fb_copyarea, 287 | .fb_imageblit = ssd1306fb_imageblit, 288 | }; 289 | 290 | static void ssd1306fb_update_task(struct work_struct *work){ 291 | struct ssd1306fb_par *par = container_of(work, struct ssd1306fb_par, work); 292 | //printk("ssd1306: update task\n"); 293 | ssd1306fb_update_display(par); 294 | //printk("ssd1306: update done\n"); 295 | } 296 | 297 | static int ssd1306fb_ssd1306_init(struct ssd1306fb_par *par) 298 | { 299 | int ret, c; 300 | 301 | #define U8G_ESC_CS(x) 255, (0xd0 | ((x)&0x0f)) 302 | #define U8G_ESC_ADR(x) 255, (0xe0 | ((x)&0x0f)) 303 | #define U8G_ESC_RST(x) 255, (0xc0 | ((x)&0x0f)) 304 | #define U8G_ESC_END 255, 254 305 | 306 | static const uint8_t cmd_init[] = { 307 | U8G_ESC_CS(0), //disable chip 308 | U8G_ESC_ADR(0), /* instruction mode */ 309 | U8G_ESC_RST(1), /* do reset low pulse with (1*16)+2 milliseconds */ 310 | U8G_ESC_CS(1), /* enable chip */ 311 | 312 | 0x0ae, /* display off, sleep mode */ 313 | 0x0d5, 0x081, /* clock divide ratio (0x00=1) and oscillator frequency (0x8) */ 314 | 0x0a8, 0x03f, /* multiplex ratio */ 315 | 0x0d3, 0x000, 0x00, /* display offset */ 316 | //0x040, /* start line */ 317 | 0x08d, 0x14, /* charge pump setting (p62): 0x014 enable, 0x010 disable */ 318 | 0x020, 0x00, // memory addr mode 319 | 0x0a1, /* segment remap a0/a1*/ 320 | 0x0a5, // display on 321 | 0x0c8, /* c0: scan dir normal, c8: reverse */ 322 | 0x0da, 0x012, /* com pin HW config, sequential com pin config (bit 4), disable left/right remap (bit 5) */ 323 | 0x081, 0x09f, /* set contrast control */ 324 | 0x0d9, 0x011, /* pre-charge period */ 325 | 0x0db, 0x020, /* vcomh deselect level */ 326 | 0x021, 0x00, 0x7f, // (7d for 125 col screen!) column addressing mode 327 | 0x022, 0x00, 0x07, /* page addressing mode WRONG: 3 byte cmd! */ 328 | 0x0a4, /* output ram to display */ 329 | 0x0a6, /* none inverted normal display mode */ 330 | 0x0af, /* display on */ 331 | 332 | U8G_ESC_CS(0), /* disable chip */ 333 | U8G_ESC_END /* end of sequence */ 334 | }; 335 | 336 | for(c = 0; c < sizeof(cmd_init); c++){ 337 | ret = ssd1306fb_write_cmd(par, cmd_init[c]); 338 | if (ret < 0){ 339 | printk("ssd1306: i2c write failed!\n"); 340 | return ret; 341 | } 342 | mdelay(10); 343 | } 344 | 345 | schedule_work(&par->work); 346 | //ssd1306fb_update_display(par); 347 | 348 | printk("ssd1306: display initialized\n"); 349 | 350 | return 0; 351 | } 352 | 353 | static struct ssd1306fb_ops ssd1306fb_ssd1306_ops = { 354 | .init = ssd1306fb_ssd1306_init, 355 | }; 356 | 357 | 358 | static int ssd1306fb_probe(struct i2c_client *client, 359 | const struct i2c_device_id *id){ 360 | struct fb_info *info; 361 | struct ssd1306fb_par *par; 362 | u8 __iomem *vmem; 363 | u32 vmem_size; 364 | int ret; 365 | 366 | dev_info(&client->dev, "Probing ssd1306fb module..\n"); 367 | 368 | info = framebuffer_alloc(sizeof(struct ssd1306fb_par), &client->dev); 369 | if (!info) { 370 | dev_err(&client->dev, "Couldn't allocate framebuffer.\n"); 371 | return -ENOMEM; 372 | } 373 | 374 | par = info->par; 375 | par->info = info; 376 | par->client = client; 377 | 378 | par->ops = &ssd1306fb_ssd1306_ops; 379 | 380 | par->width = 128; 381 | par->height = 64; 382 | par->page_offset = 1; 383 | 384 | par->data16 = ssd1306fb_alloc_array(16, SSD1306_DATA); 385 | par->cmd1 = ssd1306fb_alloc_array(1, SSD1306_COMMAND); 386 | 387 | INIT_WORK(&par->work, ssd1306fb_update_task); 388 | 389 | vmem_size = par->width * par->height / 8; 390 | 391 | vmem = (u8 __force __iomem*) devm_kzalloc(&client->dev, vmem_size, GFP_KERNEL); 392 | info->screen_base = vmem; 393 | 394 | if (!info->screen_base) { 395 | dev_err(&client->dev, "Couldn't allocate graphical memory.\n"); 396 | ret = -ENOMEM; 397 | goto fb_alloc_error; 398 | } 399 | 400 | info->fbops = &ssd1306fb_ops; 401 | info->fix = ssd1306fb_fix; 402 | info->fix.line_length = par->width / 8; 403 | //info->fbdefio = &ssd1306fb_defio; 404 | 405 | info->var = ssd1306fb_var; 406 | info->var.xres = par->width; 407 | info->var.xres_virtual = par->width; 408 | info->var.yres = par->height; 409 | info->var.yres_virtual = par->height; 410 | 411 | info->var.red.length = 1; 412 | info->var.red.offset = 0; 413 | info->var.green.length = 1; 414 | info->var.green.offset = 0; 415 | info->var.blue.length = 1; 416 | info->var.blue.offset = 0; 417 | 418 | info->fix.smem_start = (unsigned long)info->screen_base; 419 | info->fix.smem_len = vmem_size; 420 | 421 | i2c_set_clientdata(client, info); 422 | 423 | if (par->ops->init) { 424 | printk("ssd1306: initializing display\n"); 425 | ret = par->ops->init(par); 426 | if (ret){ 427 | printk("ssd1306: failed to initialize screen!\n"); 428 | goto reset_oled_error; 429 | } 430 | } 431 | 432 | ret = register_framebuffer(info); 433 | if (ret) { 434 | dev_err(&client->dev, "Couldn't register the framebuffer\n"); 435 | goto panel_init_error; 436 | } 437 | 438 | dev_info(&client->dev, "fb%d: %s framebuffer device registered, using %d bytes of video memory\n", info->node, info->fix.id, vmem_size); 439 | 440 | return 0; 441 | 442 | panel_init_error: 443 | if (par->ops->remove) 444 | par->ops->remove(par); 445 | reset_oled_error: 446 | //fb_deferred_io_cleanup(info); 447 | fb_alloc_error: 448 | framebuffer_release(info); 449 | return ret; 450 | } 451 | 452 | static int ssd1306fb_remove(struct i2c_client *client) 453 | { 454 | struct fb_info *info = i2c_get_clientdata(client); 455 | struct ssd1306fb_par *par = info->par; 456 | 457 | dev_info(&client->dev, "removing ssd1307 driver\n"); 458 | 459 | kfree(par->data16); 460 | kfree(par->cmd1); 461 | 462 | unregister_framebuffer(info); 463 | if (par->ops->remove) 464 | par->ops->remove(par); 465 | framebuffer_release(info); 466 | 467 | return 0; 468 | } 469 | 470 | static const struct i2c_device_id ssd1306fb_i2c_id[] = { 471 | { "ssd1306fb", 0 }, 472 | { } 473 | }; 474 | 475 | MODULE_DEVICE_TABLE(i2c, ssd1306fb_i2c_id); 476 | 477 | static struct i2c_driver ssd1306fb_driver = { 478 | .probe = ssd1306fb_probe, 479 | .remove = ssd1306fb_remove, 480 | .id_table = ssd1306fb_i2c_id, 481 | .driver = { 482 | .name = "ssd1306fb", 483 | .owner = THIS_MODULE, 484 | }, 485 | }; 486 | 487 | module_i2c_driver(ssd1306fb_driver); 488 | 489 | MODULE_DESCRIPTION("FB driver for SSD1306 OLED I2C display"); 490 | MODULE_AUTHOR("Maxime Ripard "); 491 | MODULE_LICENSE("GPL"); 492 | -------------------------------------------------------------------------------- /i2c-ch341/src/i2c-ch341.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Driver for the CH341 USB-I2C adapter 3 | * 4 | * Copyright (c) 2014 Marco Gittler 5 | * 6 | * Derived from: 7 | * i2c-tiny-usb.c 8 | * Copyright (C) 2006-2007 Till Harbaum (Till@Harbaum.org) 9 | * 10 | * This program is free software; you can redistribute it and/or 11 | * modify it under the terms of the GNU General Public License as 12 | * published by the Free Software Foundation, version 2. 13 | */ 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #define DRIVER_NAME "i2c-ch341-u2c" 24 | 25 | #define USB_VENDOR_ID_CH341 0x1a86 26 | #define USB_DEVICE_ID_CH341_U2C 0x5512 27 | 28 | #define DEFAULT_CONFIGURATION 0x01 29 | #define DEFAULT_TIMEOUT 100 // 100mS for USB timeouts 30 | 31 | #define mCH341_PACKET_LENGTH 32 32 | #define mCH341_PKT_LEN_SHORT 8 33 | 34 | #define mCH341_ENDP_INTER_UP 0x81 35 | #define mCH341_ENDP_INTER_DOWN 0x01 36 | #define mCH341_ENDP_DATA_UP 0x82 37 | #define mCH341_ENDP_DATA_DOWN 0x02 38 | 39 | #define mCH341_VENDOR_READ 0xC0 40 | #define mCH341_VENDOR_WRITE 0x40 41 | 42 | #define mCH341_PARA_INIT 0xB1 43 | #define mCH341_I2C_STATUS 0x52 44 | #define mCH341_I2C_COMMAND 0x53 45 | 46 | #define mCH341_PARA_CMD_R0 0xAC 47 | #define mCH341_PARA_CMD_R1 0xAD 48 | #define mCH341_PARA_CMD_W0 0xA6 49 | #define mCH341_PARA_CMD_W1 0xA7 50 | #define mCH341_PARA_CMD_STS 0xA0 51 | 52 | #define mCH341A_CMD_SET_OUTPUT 0xA1 53 | #define mCH341A_CMD_IO_ADDR 0xA2 54 | #define mCH341A_CMD_PRINT_OUT 0xA3 55 | #define mCH341A_CMD_SPI_STREAM 0xA8 56 | #define mCH341A_CMD_SIO_STREAM 0xA9 57 | #define mCH341A_CMD_I2C_STREAM 0xAA 58 | #define mCH341A_CMD_UIO_STREAM 0xAB 59 | 60 | #define mCH341A_BUF_CLEAR 0xB2 61 | #define mCH341A_I2C_CMD_X 0x54 62 | #define mCH341A_DELAY_MS 0x5E 63 | #define mCH341A_GET_VER 0x5F 64 | 65 | #define mCH341_EPP_IO_MAX ( mCH341_PACKET_LENGTH - 1 ) 66 | #define mCH341A_EPP_IO_MAX 0xFF 67 | 68 | #define mCH341A_CMD_IO_ADDR_W 0x00 69 | #define mCH341A_CMD_IO_ADDR_R 0x80 70 | 71 | #define mCH341A_CMD_I2C_STM_STA 0x74 72 | #define mCH341A_CMD_I2C_STM_STO 0x75 73 | #define mCH341A_CMD_I2C_STM_OUT 0x80 74 | #define mCH341A_CMD_I2C_STM_IN 0xC0 75 | #define mCH341A_CMD_I2C_STM_MAX ( min( 0x3F, mCH341_PACKET_LENGTH ) ) 76 | #define mCH341A_CMD_I2C_STM_SET 0x60 77 | #define mCH341A_CMD_I2C_STM_US 0x40 78 | #define mCH341A_CMD_I2C_STM_MS 0x50 79 | #define mCH341A_CMD_I2C_STM_DLY 0x0F 80 | #define mCH341A_CMD_I2C_STM_END 0x00 81 | 82 | #define mCH341A_CMD_UIO_STM_IN 0x00 83 | #define mCH341A_CMD_UIO_STM_DIR 0x40 84 | #define mCH341A_CMD_UIO_STM_OUT 0x80 85 | #define mCH341A_CMD_UIO_STM_US 0xC0 86 | #define mCH341A_CMD_UIO_STM_END 0x20 87 | 88 | #define mCH341_PARA_MODE_EPP 0x00 89 | #define mCH341_PARA_MODE_EPP17 0x00 90 | #define mCH341_PARA_MODE_EPP19 0x01 91 | #define mCH341_PARA_MODE_MEM 0x02 92 | 93 | 94 | #define CH341_I2C_LOW_SPEED 0 // low speed - 20kHz 95 | #define CH341_I2C_STANDARD_SPEED 1 // standard speed - 100kHz 96 | #define CH341_I2C_FAST_SPEED 2 // fast speed - 400kHz 97 | #define CH341_I2C_HIGH_SPEED 3 // high speed - 750kHz 98 | 99 | #define U2C_I2C_FREQ_FAST 400000 100 | #define U2C_I2C_FREQ_STD 100000 101 | #define U2C_I2C_FREQ(s) (1000000 / (2 * (s - 1) + 10)) 102 | 103 | #define RESP_OK 0x00 104 | #define RESP_FAILED 0x01 105 | #define RESP_BAD_MEMADDR 0x04 106 | #define RESP_DATA_ERR 0x05 107 | #define RESP_NOT_IMPLEMENTED 0x06 108 | #define RESP_NACK 0x07 109 | #define RESP_TIMEOUT 0x09 110 | 111 | #define CH341_OUTBUF_LEN 128 112 | #define CH341_INBUF_LEN 256 /* Maximum supported receive length */ 113 | 114 | 115 | struct i2c_ch341_u2c { 116 | u8 obuffer[CH341_OUTBUF_LEN]; /* output buffer */ 117 | u8 ibuffer[CH341_INBUF_LEN]; /* input buffer */ 118 | int ep_in, ep_out; /* Endpoints */ 119 | struct usb_device *usb_dev; /* the usb device for this device */ 120 | struct usb_interface *interface;/* the interface for this device */ 121 | struct i2c_adapter adapter; /* i2c related things */ 122 | int olen; /* Output buffer length */ 123 | int ocount; /* Number of enqueued messages */ 124 | int ilen; 125 | int check_ack; 126 | }; 127 | 128 | static uint frequency = U2C_I2C_FREQ_STD; /* I2C clock frequency in Hz */ 129 | 130 | module_param(frequency, uint, S_IRUGO | S_IWUSR); 131 | MODULE_PARM_DESC(frequency, "I2C clock frequency in hertz"); 132 | 133 | 134 | static int ch341_usb_transfer(struct i2c_ch341_u2c *dev) 135 | { 136 | int ret = 0; 137 | int actual; 138 | int i; 139 | 140 | if (!dev->olen || !dev->ocount) { 141 | return -EINVAL; 142 | } 143 | 144 | ret = usb_bulk_msg(dev->usb_dev, 145 | usb_sndbulkpipe(dev->usb_dev, dev->ep_out), 146 | dev->obuffer, dev->olen, &actual, 147 | DEFAULT_TIMEOUT); 148 | 149 | if (!ret) { 150 | dev->ocount = 1; 151 | for (i = 0; i < dev->ocount; i++) { 152 | int tmpret; 153 | memset(dev->ibuffer, 0, 256); 154 | tmpret = usb_bulk_msg(dev->usb_dev, 155 | usb_rcvbulkpipe(dev->usb_dev, 156 | dev->ep_in), 157 | dev->ibuffer, 158 | sizeof(dev->ibuffer), &actual, 159 | DEFAULT_TIMEOUT); 160 | if (dev->olen > 1 && dev->obuffer[0] == 0xaa && dev->obuffer[1] == 0x60) { 161 | return 0; // set speed should not fail 162 | } 163 | //if (actual>0) 164 | ret = tmpret; 165 | //dev->ilen=actual; 166 | if (dev->check_ack == 0 && (dev->ibuffer[0 ] & 0x80) != 0) { //detect chip write len 0 167 | //dev_info(&dev->interface->dev,"_nackonly %d ",-EIO); 168 | return -EIO; 169 | } 170 | if (ret == 0 && actual > 0) { 171 | //dev_info(&dev->interface->dev,"__ibuf 0=%02x 1=%02x ",dev->ibuffer[0],dev->ibuffer[1]); 172 | ret = actual; 173 | dev->ilen = actual; 174 | /*switch (dev->ibuffer[actual - 1]&0x80) { 175 | 176 | case RESP_TIMEOUT: 177 | ret = -ETIMEDOUT; 178 | break; 179 | case RESP_OK: 180 | ret = actual - 1; 181 | break; 182 | default: 183 | //ret = 0; 184 | dev->ilen=actual; 185 | break; 186 | }*/ 187 | 188 | } 189 | } 190 | } 191 | dev->olen = 0; 192 | dev->ocount = 0; 193 | return ret; 194 | } 195 | 196 | 197 | /* Send command (no data) */ 198 | static int ch341_usb_cmd_msg(struct i2c_ch341_u2c *dev, u8 *msg, u8 len) 199 | { 200 | //dev_info(&dev->interface->dev,"%s",__FUNCTION__); 201 | memcpy(dev->obuffer, msg, len); 202 | dev->olen = len; 203 | dev->ocount++; 204 | return ch341_usb_transfer(dev); 205 | } 206 | 207 | 208 | static int ch341_usb_cmd_read_addr(struct i2c_ch341_u2c *dev, u8 addr, u8 *data, u8 datalen, bool recv_len) 209 | { 210 | u8 msg0[256]; 211 | int msgsize = 0, ret, ilen = datalen; 212 | msg0[msgsize++] = mCH341A_CMD_I2C_STREAM; 213 | msg0[msgsize++] = mCH341A_CMD_I2C_STM_STA; 214 | msg0[msgsize++] = mCH341A_CMD_I2C_STM_OUT | (recv_len ? 1 : 0); // 1 bytE 215 | msg0[msgsize++] = (addr) | 0x01 ; //&0xfe; 216 | 217 | for (; ilen > 0 ; ilen--) { 218 | msg0[msgsize++] = mCH341A_CMD_I2C_STM_IN | (ilen - 1); 219 | } 220 | msg0[msgsize++] = mCH341A_CMD_I2C_STM_STO; 221 | msg0[msgsize++] = mCH341A_CMD_I2C_STM_END; 222 | 223 | dev->check_ack = recv_len; 224 | ret = ch341_usb_cmd_msg(dev, msg0, msgsize); 225 | 226 | if (ret >= 0) { 227 | memcpy(data, dev->ibuffer + (recv_len ? 0 : 1), dev->ilen - (recv_len ? 1 : 0)); 228 | } 229 | return ret; 230 | 231 | } 232 | static int ch341_usb_cmd_write_addr(struct i2c_ch341_u2c *dev, u8 addr, u8 *data, u8 datalen) 233 | { 234 | u8 msg0[256]; 235 | 236 | int msgsize = 0, ret = -5; 237 | msg0[msgsize++] = mCH341A_CMD_I2C_STREAM, 238 | msg0[msgsize++] = mCH341A_CMD_I2C_STM_STA, 239 | msg0[msgsize++] = mCH341A_CMD_I2C_STM_OUT | (datalen > 0 ? datalen + 1 : 0), // 1 byte 240 | msg0[msgsize++] = addr & 0xfe, 241 | dev->check_ack = 0; //(datalen>0); 242 | memcpy(msg0 + msgsize, data, datalen); 243 | msgsize += datalen; 244 | 245 | msg0[msgsize++] = mCH341A_CMD_I2C_STM_STO; 246 | msg0[msgsize++] = mCH341A_CMD_I2C_STM_END; 247 | 248 | ret = ch341_usb_cmd_msg(dev, msg0, msgsize); 249 | if (datalen > 0 && ret == -ETIMEDOUT) { 250 | ret = 0; 251 | } 252 | return ret; 253 | 254 | } 255 | /* 256 | static int ch341_i2c_start(struct i2c_ch341_u2c *dev) 257 | { 258 | u8 msg[]={ 259 | mCH341A_CMD_I2C_STREAM, 260 | mCH341A_CMD_I2C_STM_STA, 261 | mCH341A_CMD_I2C_STM_END 262 | }; 263 | dev_info(&dev->interface->dev,"%s",__FUNCTION__); 264 | return ch341_usb_cmd_msg(dev,msg,3); 265 | } 266 | static int ch341_i2c_stop(struct i2c_ch341_u2c *dev) 267 | { 268 | u8 msg[]={ 269 | mCH341A_CMD_I2C_STREAM, 270 | mCH341A_CMD_I2C_STM_STO, 271 | mCH341A_CMD_I2C_STM_END 272 | }; 273 | dev_info(&dev->interface->dev,"%s",__FUNCTION__); 274 | return ch341_usb_cmd_msg(dev,msg,3); 275 | } 276 | */ 277 | 278 | static int ch341_set_speed(struct i2c_ch341_u2c *dev, u8 speed) 279 | { 280 | u8 msg[] = { 281 | mCH341A_CMD_I2C_STREAM, 282 | mCH341A_CMD_I2C_STM_SET | (speed & 0x03), 283 | mCH341A_CMD_I2C_STM_END 284 | }; 285 | return ch341_usb_cmd_msg(dev, msg, 3); 286 | } 287 | 288 | 289 | static int ch341_init(struct i2c_ch341_u2c *dev) 290 | { 291 | int speed, freq, ret; 292 | dev_info(&dev->interface->dev, "%s", __FUNCTION__); 293 | if (frequency >= 750000) { 294 | speed = CH341_I2C_HIGH_SPEED; 295 | freq = frequency; 296 | } else if (frequency >= 400000) { 297 | speed = CH341_I2C_FAST_SPEED; 298 | freq = frequency; 299 | } else if (frequency >= 200000 || frequency == 0) { 300 | speed = CH341_I2C_STANDARD_SPEED; 301 | freq = frequency; 302 | } else { 303 | speed = CH341_I2C_LOW_SPEED; 304 | freq = frequency; 305 | } 306 | 307 | dev_info(&dev->interface->dev, 308 | "CH341 U2C at USB bus %03d address %03d speed %d Hz\n", 309 | dev->usb_dev->bus->busnum, dev->usb_dev->devnum, freq); 310 | 311 | /* Set I2C speed */ 312 | ret = ch341_set_speed(dev, speed); 313 | if (ret < 0) { 314 | return ret; 315 | } 316 | return ret; 317 | } 318 | 319 | /* i2c layer */ 320 | 321 | static int ch341_usb_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, 322 | int num) 323 | { 324 | struct i2c_ch341_u2c *dev = i2c_get_adapdata(adapter); 325 | struct i2c_msg *pmsg; 326 | int i, ret; 327 | 328 | for (i = 0; i < num; i++) { 329 | pmsg = &msgs[i]; 330 | 331 | if (pmsg->flags & I2C_M_RD) { 332 | int recv_len = pmsg->flags & I2C_M_RECV_LEN; 333 | dev->ilen = recv_len; 334 | ret = ch341_usb_cmd_read_addr(dev, pmsg->addr << 1 , pmsg->buf, pmsg->len, recv_len); 335 | if (recv_len && ret > 0) { 336 | pmsg->len = ret; 337 | } 338 | if (ret < 0) { 339 | goto abort; 340 | } 341 | } else { 342 | ret = ch341_usb_cmd_write_addr(dev, pmsg->addr << 1, pmsg->buf, pmsg->len); 343 | if (ret < 0) { 344 | goto abort; 345 | } 346 | } 347 | } 348 | ret = num; 349 | abort: 350 | //ch341_i2c_stop(dev); 351 | return (ret < 0) ? ret : num; 352 | } 353 | 354 | /* 355 | * Return list of supported functionality. 356 | */ 357 | static u32 ch341_usb_func(struct i2c_adapter *a) 358 | { 359 | return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL ;/*| 360 | I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL;*/ 361 | } 362 | 363 | static const struct i2c_algorithm ch341_usb_algorithm = { 364 | .master_xfer = ch341_usb_xfer, 365 | .functionality = ch341_usb_func, 366 | }; 367 | 368 | /* device layer */ 369 | 370 | static const struct usb_device_id ch341_u2c_table[] = { 371 | { USB_DEVICE(USB_VENDOR_ID_CH341, USB_DEVICE_ID_CH341_U2C) }, 372 | { } 373 | }; 374 | 375 | MODULE_DEVICE_TABLE(usb, ch341_u2c_table); 376 | 377 | static void ch341_u2c_free(struct i2c_ch341_u2c *dev) 378 | { 379 | usb_put_dev(dev->usb_dev); 380 | kfree(dev); 381 | } 382 | 383 | static int ch341_u2c_probe(struct usb_interface *interface, 384 | const struct usb_device_id *id) 385 | { 386 | struct usb_device *udev; 387 | struct usb_host_interface *hostif = interface->cur_altsetting; 388 | struct i2c_ch341_u2c *dev; 389 | const int ifnum = interface->altsetting[DEFAULT_CONFIGURATION].desc.bInterfaceNumber; 390 | char *speed; 391 | int ret; 392 | 393 | if (hostif->desc.bInterfaceNumber != 0 394 | || hostif->desc.bNumEndpoints < 2) { 395 | return -ENODEV; 396 | } 397 | 398 | /* allocate memory for our device state and initialize it */ 399 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); 400 | if (dev == NULL) { 401 | dev_err(&interface->dev, "no memory for device state\n"); 402 | ret = -ENOMEM; 403 | goto error; 404 | } 405 | dev->ep_out = hostif->endpoint[1].desc.bEndpointAddress; 406 | dev_info(&interface->dev, "ep_out=%x\n", dev->ep_out); 407 | dev->ep_in = hostif->endpoint[0].desc.bEndpointAddress; 408 | dev_info(&interface->dev, "ep_in=%x\n", dev->ep_in); 409 | 410 | dev->usb_dev = usb_get_dev(interface_to_usbdev(interface)); 411 | dev->interface = interface; 412 | 413 | udev = dev->usb_dev; 414 | switch (udev->speed) { 415 | case USB_SPEED_LOW: 416 | speed = "1.5"; 417 | break; 418 | case USB_SPEED_UNKNOWN: 419 | case USB_SPEED_FULL: 420 | speed = "12"; 421 | break; 422 | case USB_SPEED_HIGH: 423 | speed = "480"; 424 | break; 425 | default: 426 | speed = "unknown"; 427 | } 428 | printk(KERN_INFO DRIVER_NAME 429 | ": New device %s %s @ %s Mbps " 430 | "(%04x:%04x, interface %d, class %d, version %d.%02d)\n", 431 | udev->manufacturer ? udev->manufacturer : "", 432 | udev->product ? udev->product : "", 433 | speed, 434 | le16_to_cpu(udev->descriptor.idVendor), 435 | le16_to_cpu(udev->descriptor.idProduct), 436 | ifnum, 437 | interface->altsetting->desc.bInterfaceNumber, 438 | le16_to_cpu(udev->descriptor.bcdDevice % 0xff), 439 | le16_to_cpu(udev->descriptor.bcdDevice >> 8 % 0xff) 440 | ); 441 | 442 | /* save our data pointer in this interface device */ 443 | usb_set_intfdata(interface, dev); 444 | 445 | /* setup i2c adapter description */ 446 | dev->adapter.owner = THIS_MODULE; 447 | dev->adapter.class = I2C_CLASS_HWMON; 448 | dev->adapter.algo = &ch341_usb_algorithm; 449 | i2c_set_adapdata(&dev->adapter, dev); 450 | snprintf(dev->adapter.name, sizeof(dev->adapter.name), 451 | DRIVER_NAME " at bus %03d device %03d", 452 | dev->usb_dev->bus->busnum, dev->usb_dev->devnum); 453 | 454 | dev->adapter.dev.parent = &dev->interface->dev; 455 | /* initialize ch341 i2c interface */ 456 | ret = ch341_init(dev); 457 | 458 | if (ret < 0) { 459 | dev_err(&interface->dev, "failed to initialize adapter\n"); 460 | goto error_free; 461 | } 462 | /* and finally attach to i2c layer */ 463 | ret = i2c_add_adapter(&dev->adapter); 464 | if (ret < 0) { 465 | dev_err(&interface->dev, "failed to add I2C adapter\n"); 466 | goto error_free; 467 | } 468 | 469 | dev_dbg(&interface->dev, "connected " DRIVER_NAME "\n"); 470 | 471 | return 0; 472 | 473 | error_free: 474 | usb_set_intfdata(interface, NULL); 475 | ch341_u2c_free(dev); 476 | error: 477 | return ret; 478 | } 479 | 480 | static void ch341_u2c_disconnect(struct usb_interface *interface) 481 | { 482 | struct i2c_ch341_u2c *dev = usb_get_intfdata(interface); 483 | 484 | i2c_del_adapter(&dev->adapter); 485 | usb_set_intfdata(interface, NULL); 486 | ch341_u2c_free(dev); 487 | 488 | dev_dbg(&interface->dev, "disconnected\n"); 489 | } 490 | 491 | static struct usb_driver ch341_u2c_driver = { 492 | .name = DRIVER_NAME, 493 | .probe = ch341_u2c_probe, 494 | .disconnect = ch341_u2c_disconnect, 495 | .id_table = ch341_u2c_table, 496 | }; 497 | 498 | module_usb_driver(ch341_u2c_driver); 499 | 500 | MODULE_AUTHOR("Marco Gittler "); 501 | MODULE_DESCRIPTION(DRIVER_NAME " driver"); 502 | MODULE_LICENSE("GPL"); 503 | --------------------------------------------------------------------------------