FILE
fb_ssd1306.c
File preview with syntax highlighting.
linux-6.19.14/drivers/staging/fbtft/fb_ssd1306.c
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * FB driver for the SSD1306 OLED Controller 4 * 5 * Copyright (C) 2013 Noralf Tronnes 6 */ 7 8#include <linux/module.h> 9#include <linux/kernel.h> 10#include <linux/init.h> 11#include <linux/gpio/consumer.h> 12#include <linux/delay.h> 13 14#include "fbtft.h" 15 16#define DRVNAME "fb_ssd1306" 17#define WIDTH 128 18#define HEIGHT 64 19 20/* 21 * write_reg() caveat: 22 * 23 * This doesn't work because D/C has to be LOW for both values: 24 * write_reg(par, val1, val2); 25 * 26 * Do it like this: 27 * write_reg(par, val1); 28 * write_reg(par, val2); 29 */ 30 31/* Init sequence taken from the Adafruit SSD1306 Arduino library */ 32static int init_display(struct fbtft_par *par) 33{ 34 par->fbtftops.reset(par); 35 36 if (par->gamma.curves[0] == 0) { 37 mutex_lock(&par->gamma.lock); 38 if (par->info->var.yres == 64) 39 par->gamma.curves[0] = 0xCF; 40 else 41 par->gamma.curves[0] = 0x8F; 42 mutex_unlock(&par->gamma.lock); 43 } 44 45 /* Set Display OFF */ 46 write_reg(par, 0xAE); 47 48 /* Set Display Clock Divide Ratio/ Oscillator Frequency */ 49 write_reg(par, 0xD5); 50 write_reg(par, 0x80); 51 52 /* Set Multiplex Ratio */ 53 write_reg(par, 0xA8); 54 if (par->info->var.yres == 64) 55 write_reg(par, 0x3F); 56 else if (par->info->var.yres == 48) 57 write_reg(par, 0x2F); 58 else 59 write_reg(par, 0x1F); 60 61 /* Set Display Offset */ 62 write_reg(par, 0xD3); 63 write_reg(par, 0x0); 64 65 /* Set Display Start Line */ 66 write_reg(par, 0x40 | 0x0); 67 68 /* Charge Pump Setting */ 69 write_reg(par, 0x8D); 70 /* A[2] = 1b, Enable charge pump during display on */ 71 write_reg(par, 0x14); 72 73 /* Set Memory Addressing Mode */ 74 write_reg(par, 0x20); 75 /* Vertical addressing mode */ 76 write_reg(par, 0x01); 77 78 /* Set Segment Re-map */ 79 /* column address 127 is mapped to SEG0 */ 80 write_reg(par, 0xA0 | 0x1); 81 82 /* Set COM Output Scan Direction */ 83 /* remapped mode. Scan from COM[N-1] to COM0 */ 84 write_reg(par, 0xC8); 85 86 /* Set COM Pins Hardware Configuration */ 87 write_reg(par, 0xDA); 88 if (par->info->var.yres == 64) 89 /* A[4]=1b, Alternative COM pin configuration */ 90 write_reg(par, 0x12); 91 else if (par->info->var.yres == 48) 92 /* A[4]=1b, Alternative COM pin configuration */ 93 write_reg(par, 0x12); 94 else 95 /* A[4]=0b, Sequential COM pin configuration */ 96 write_reg(par, 0x02); 97 98 /* Set Pre-charge Period */ 99 write_reg(par, 0xD9); 100 write_reg(par, 0xF1); 101 102 /* Set VCOMH Deselect Level */ 103 write_reg(par, 0xDB); 104 /* according to the datasheet, this value is out of bounds */ 105 write_reg(par, 0x40); 106 107 /* Entire Display ON */ 108 /* Resume to RAM content display. Output follows RAM content */ 109 write_reg(par, 0xA4); 110 111 /* Set Normal Display 112 * 0 in RAM: OFF in display panel 113 * 1 in RAM: ON in display panel 114 */ 115 write_reg(par, 0xA6); 116 117 /* Set Display ON */ 118 write_reg(par, 0xAF); 119 120 return 0; 121} 122 123static void set_addr_win_64x48(struct fbtft_par *par) 124{ 125 /* Set Column Address */ 126 write_reg(par, 0x21); 127 write_reg(par, 0x20); 128 write_reg(par, 0x5F); 129 130 /* Set Page Address */ 131 write_reg(par, 0x22); 132 write_reg(par, 0x0); 133 write_reg(par, 0x5); 134} 135 136static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) 137{ 138 /* Set Lower Column Start Address for Page Addressing Mode */ 139 write_reg(par, 0x00 | 0x0); 140 /* Set Higher Column Start Address for Page Addressing Mode */ 141 write_reg(par, 0x10 | 0x0); 142 /* Set Display Start Line */ 143 write_reg(par, 0x40 | 0x0); 144 145 if (par->info->var.xres == 64 && par->info->var.yres == 48) 146 set_addr_win_64x48(par); 147} 148 149static int blank(struct fbtft_par *par, bool on) 150{ 151 if (on) 152 write_reg(par, 0xAE); 153 else 154 write_reg(par, 0xAF); 155 return 0; 156} 157 158/* Gamma is used to control Contrast */ 159static int set_gamma(struct fbtft_par *par, u32 *curves) 160{ 161 /* apply mask */ 162 curves[0] &= 0xFF; 163 164 /* Set Contrast Control for BANK0 */ 165 write_reg(par, 0x81); 166 write_reg(par, curves[0]); 167 168 return 0; 169} 170 171static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) 172{ 173 u16 *vmem16 = (u16 *)par->info->screen_buffer; 174 u32 xres = par->info->var.xres; 175 u32 yres = par->info->var.yres; 176 u8 *buf = par->txbuf.buf; 177 int x, y, i; 178 int ret = 0; 179 180 for (x = 0; x < xres; x++) { 181 for (y = 0; y < yres / 8; y++) { 182 *buf = 0x00; 183 for (i = 0; i < 8; i++) 184 if (vmem16[(y * 8 + i) * xres + x]) 185 *buf |= BIT(i); 186 buf++; 187 } 188 } 189 190 /* Write data */ 191 gpiod_set_value(par->gpio.dc, 1); 192 ret = par->fbtftops.write(par, par->txbuf.buf, xres * yres / 8); 193 if (ret < 0) 194 dev_err(par->info->device, "write failed and returned: %d\n", 195 ret); 196 197 return ret; 198} 199 200static struct fbtft_display display = { 201 .regwidth = 8, 202 .width = WIDTH, 203 .height = HEIGHT, 204 .gamma_num = 1, 205 .gamma_len = 1, 206 .gamma = "00", 207 .fbtftops = { 208 .write_vmem = write_vmem, 209 .init_display = init_display, 210 .set_addr_win = set_addr_win, 211 .blank = blank, 212 .set_gamma = set_gamma, 213 }, 214}; 215 216FBTFT_REGISTER_DRIVER(DRVNAME, "solomon,ssd1306", &display); 217 218MODULE_ALIAS("spi:" DRVNAME); 219MODULE_ALIAS("platform:" DRVNAME); 220MODULE_ALIAS("spi:ssd1306"); 221MODULE_ALIAS("platform:ssd1306"); 222 223MODULE_DESCRIPTION("SSD1306 OLED Driver"); 224MODULE_AUTHOR("Noralf Tronnes"); 225MODULE_LICENSE("GPL");
~/
Read-only file preview.