root/drivers/staging/fbtft/fb_ssd1306.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. init_display
  2. set_addr_win_64x48
  3. set_addr_win
  4. blank
  5. set_gamma
  6. write_vmem

   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 */
  32 static 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 
 123 static 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 
 136 static 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 
 149 static int blank(struct fbtft_par *par, bool on)
 150 {
 151         fbtft_par_dbg(DEBUG_BLANK, par, "(%s=%s)\n",
 152                       __func__, on ? "true" : "false");
 153 
 154         if (on)
 155                 write_reg(par, 0xAE);
 156         else
 157                 write_reg(par, 0xAF);
 158         return 0;
 159 }
 160 
 161 /* Gamma is used to control Contrast */
 162 static int set_gamma(struct fbtft_par *par, u32 *curves)
 163 {
 164         /* apply mask */
 165         curves[0] &= 0xFF;
 166 
 167         /* Set Contrast Control for BANK0 */
 168         write_reg(par, 0x81);
 169         write_reg(par, curves[0]);
 170 
 171         return 0;
 172 }
 173 
 174 static int write_vmem(struct fbtft_par *par, size_t offset, size_t len)
 175 {
 176         u16 *vmem16 = (u16 *)par->info->screen_buffer;
 177         u32 xres = par->info->var.xres;
 178         u32 yres = par->info->var.yres;
 179         u8 *buf = par->txbuf.buf;
 180         int x, y, i;
 181         int ret = 0;
 182 
 183         for (x = 0; x < xres; x++) {
 184                 for (y = 0; y < yres / 8; y++) {
 185                         *buf = 0x00;
 186                         for (i = 0; i < 8; i++)
 187                                 if (vmem16[(y * 8 + i) * xres + x])
 188                                         *buf |= BIT(i);
 189                         buf++;
 190                 }
 191         }
 192 
 193         /* Write data */
 194         gpiod_set_value(par->gpio.dc, 1);
 195         ret = par->fbtftops.write(par, par->txbuf.buf, xres * yres / 8);
 196         if (ret < 0)
 197                 dev_err(par->info->device, "write failed and returned: %d\n",
 198                         ret);
 199 
 200         return ret;
 201 }
 202 
 203 static struct fbtft_display display = {
 204         .regwidth = 8,
 205         .width = WIDTH,
 206         .height = HEIGHT,
 207         .gamma_num = 1,
 208         .gamma_len = 1,
 209         .gamma = "00",
 210         .fbtftops = {
 211                 .write_vmem = write_vmem,
 212                 .init_display = init_display,
 213                 .set_addr_win = set_addr_win,
 214                 .blank = blank,
 215                 .set_gamma = set_gamma,
 216         },
 217 };
 218 
 219 FBTFT_REGISTER_DRIVER(DRVNAME, "solomon,ssd1306", &display);
 220 
 221 MODULE_ALIAS("spi:" DRVNAME);
 222 MODULE_ALIAS("platform:" DRVNAME);
 223 MODULE_ALIAS("spi:ssd1306");
 224 MODULE_ALIAS("platform:ssd1306");
 225 
 226 MODULE_DESCRIPTION("SSD1306 OLED Driver");
 227 MODULE_AUTHOR("Noralf Tronnes");
 228 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */