fb_sh1106.c (4132B)
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * FB driver for the SH1106 OLED Controller 4 * Based on the SSD1306 driver by Noralf Tronnes 5 * 6 * Copyright (C) 2017 Heiner Kallweit 7 */ 8 9#include <linux/module.h> 10#include <linux/kernel.h> 11#include <linux/init.h> 12#include <linux/delay.h> 13 14#include "fbtft.h" 15 16#define DRVNAME "fb_sh1106" 17#define WIDTH 128 18#define HEIGHT 64 19 20/* Init sequence based on the Adafruit SSD1306 Arduino library */ 21static int init_display(struct fbtft_par *par) 22{ 23 if (!par->info->var.xres || par->info->var.xres > WIDTH || 24 !par->info->var.yres || par->info->var.yres > HEIGHT || 25 par->info->var.yres % 8) { 26 dev_err(par->info->device, "Invalid screen size\n"); 27 return -EINVAL; 28 } 29 30 if (par->info->var.rotate) { 31 dev_err(par->info->device, "Display rotation not supported\n"); 32 return -EINVAL; 33 } 34 35 par->fbtftops.reset(par); 36 37 /* Set Display OFF */ 38 write_reg(par, 0xAE); 39 40 /* Set Display Clock Divide Ratio/ Oscillator Frequency */ 41 write_reg(par, 0xD5, 0x80); 42 43 /* Set Multiplex Ratio */ 44 write_reg(par, 0xA8, par->info->var.yres - 1); 45 46 /* Set Display Offset */ 47 write_reg(par, 0xD3, 0x00); 48 49 /* Set Display Start Line */ 50 write_reg(par, 0x40 | 0x0); 51 52 /* Set Segment Re-map */ 53 /* column address 127 is mapped to SEG0 */ 54 write_reg(par, 0xA0 | 0x1); 55 56 /* Set COM Output Scan Direction */ 57 /* remapped mode. Scan from COM[N-1] to COM0 */ 58 write_reg(par, 0xC8); 59 60 /* Set COM Pins Hardware Configuration */ 61 if (par->info->var.yres == 64) 62 /* A[4]=1b, Alternative COM pin configuration */ 63 write_reg(par, 0xDA, 0x12); 64 else if (par->info->var.yres == 48) 65 /* A[4]=1b, Alternative COM pin configuration */ 66 write_reg(par, 0xDA, 0x12); 67 else 68 /* A[4]=0b, Sequential COM pin configuration */ 69 write_reg(par, 0xDA, 0x02); 70 71 /* Set Pre-charge Period */ 72 write_reg(par, 0xD9, 0xF1); 73 74 /* Set VCOMH Deselect Level */ 75 write_reg(par, 0xDB, 0x40); 76 77 /* Set Display ON */ 78 write_reg(par, 0xAF); 79 80 msleep(150); 81 82 return 0; 83} 84 85static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) 86{ 87} 88 89static int blank(struct fbtft_par *par, bool on) 90{ 91 fbtft_par_dbg(DEBUG_BLANK, par, "(%s=%s)\n", 92 __func__, on ? "true" : "false"); 93 94 write_reg(par, on ? 0xAE : 0xAF); 95 96 return 0; 97} 98 99/* Gamma is used to control Contrast */ 100static int set_gamma(struct fbtft_par *par, u32 *curves) 101{ 102 /* apply mask */ 103 curves[0] &= 0xFF; 104 105 /* Set Contrast Control for BANK0 */ 106 write_reg(par, 0x81, curves[0]); 107 108 return 0; 109} 110 111static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) 112{ 113 u16 *vmem16 = (u16 *)par->info->screen_buffer; 114 u32 xres = par->info->var.xres; 115 int page, page_start, page_end, x, i, ret; 116 u8 *buf = par->txbuf.buf; 117 118 /* offset refers to vmem with 2 bytes element size */ 119 page_start = offset / (8 * 2 * xres); 120 page_end = DIV_ROUND_UP(offset + len, 8 * 2 * xres); 121 122 for (page = page_start; page < page_end; page++) { 123 /* set page and set column to 2 because of vidmem width 132 */ 124 write_reg(par, 0xb0 | page, 0x00 | 2, 0x10 | 0); 125 126 memset(buf, 0, xres); 127 for (x = 0; x < xres; x++) 128 for (i = 0; i < 8; i++) 129 if (vmem16[(page * 8 + i) * xres + x]) 130 buf[x] |= BIT(i); 131 132 /* Write data */ 133 ret = fbtft_write_buf_dc(par, buf, xres, 1); 134 if (ret < 0) 135 return ret; 136 } 137 138 return 0; 139} 140 141static void write_register(struct fbtft_par *par, int len, ...) 142{ 143 va_list args; 144 int i; 145 146 va_start(args, len); 147 148 for (i = 0; i < len; i++) 149 par->buf[i] = va_arg(args, unsigned int); 150 151 /* keep DC low for all command bytes to transfer */ 152 fbtft_write_buf_dc(par, par->buf, len, 0); 153 154 va_end(args); 155} 156 157static struct fbtft_display display = { 158 .regwidth = 8, 159 .width = WIDTH, 160 .height = HEIGHT, 161 .txbuflen = WIDTH, 162 .gamma_num = 1, 163 .gamma_len = 1, 164 /* set default contrast to 0xcd = 80% */ 165 .gamma = "cd", 166 .fbtftops = { 167 .write_vmem = write_vmem, 168 .write_register = write_register, 169 .init_display = init_display, 170 .set_addr_win = set_addr_win, 171 .blank = blank, 172 .set_gamma = set_gamma, 173 }, 174}; 175 176FBTFT_REGISTER_SPI_DRIVER(DRVNAME, "sinowealth", "sh1106", &display); 177 178MODULE_DESCRIPTION("SH1106 OLED Driver"); 179MODULE_AUTHOR("Heiner Kallweit"); 180MODULE_LICENSE("GPL");