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