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