linux/drivers/staging/fbtft/fb_tls8204.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * FB driver for the TLS8204 LCD Controller
   4 *
   5 * The display is monochrome and the video memory is RGB565.
   6 * Any pixel value except 0 turns the pixel on.
   7 *
   8 * Copyright (C) 2013 Noralf Tronnes
   9 * Copyright (C) 2014 Michael Hope (adapted for the TLS8204)
  10 */
  11
  12#include <linux/module.h>
  13#include <linux/kernel.h>
  14#include <linux/init.h>
  15#include <linux/gpio/consumer.h>
  16#include <linux/spi/spi.h>
  17#include <linux/delay.h>
  18
  19#include "fbtft.h"
  20
  21#define DRVNAME         "fb_tls8204"
  22#define WIDTH           84
  23#define HEIGHT          48
  24#define TXBUFLEN        WIDTH
  25
  26/* gamma is used to control contrast in this driver */
  27#define DEFAULT_GAMMA   "40"
  28
  29static unsigned int bs = 4;
  30module_param(bs, uint, 0000);
  31MODULE_PARM_DESC(bs, "BS[2:0] Bias voltage level: 0-7 (default: 4)");
  32
  33static int init_display(struct fbtft_par *par)
  34{
  35        par->fbtftops.reset(par);
  36
  37        /* Enter extended command mode */
  38        write_reg(par, 0x21);   /* 5:1  1
  39                                 * 2:0  PD - Powerdown control: chip is active
  40                                 * 1:0  V  - Entry mode: horizontal addressing
  41                                 * 0:1  H  - Extended instruction set control:
  42                                 *           extended
  43                                 */
  44
  45        /* H=1 Bias system */
  46        write_reg(par, 0x10 | (bs & 0x7));
  47                                /* 4:1  1
  48                                 * 3:0  0
  49                                 * 2:x  BS2 - Bias System
  50                                 * 1:x  BS1
  51                                 * 0:x  BS0
  52                                 */
  53
  54        /* Set the address of the first display line. */
  55        write_reg(par, 0x04 | (64 >> 6));
  56        write_reg(par, 0x40 | (64 & 0x3F));
  57
  58        /* Enter H=0 standard command mode */
  59        write_reg(par, 0x20);
  60
  61        /* H=0 Display control */
  62        write_reg(par, 0x08 | 4);
  63                                /* 3:1  1
  64                                 * 2:1  D - DE: 10=normal mode
  65                                 * 1:0  0
  66                                 * 0:0  E
  67                                 */
  68
  69        return 0;
  70}
  71
  72static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
  73{
  74        /* H=0 Set X address of RAM */
  75        write_reg(par, 0x80);   /* 7:1  1
  76                                 * 6-0: X[6:0] - 0x00
  77                                 */
  78
  79        /* H=0 Set Y address of RAM */
  80        write_reg(par, 0x40);   /* 7:0  0
  81                                 * 6:1  1
  82                                 * 2-0: Y[2:0] - 0x0
  83                                 */
  84}
  85
  86static int write_vmem(struct fbtft_par *par, size_t offset, size_t len)
  87{
  88        u16 *vmem16 = (u16 *)par->info->screen_buffer;
  89        int x, y, i;
  90        int ret = 0;
  91
  92        for (y = 0; y < HEIGHT / 8; y++) {
  93                u8 *buf = par->txbuf.buf;
  94                /* The display is 102x68 but the LCD is 84x48.
  95                 * Set the write pointer at the start of each row.
  96                 */
  97                gpiod_set_value(par->gpio.dc, 0);
  98                write_reg(par, 0x80 | 0);
  99                write_reg(par, 0x40 | y);
 100
 101                for (x = 0; x < WIDTH; x++) {
 102                        u8 ch = 0;
 103
 104                        for (i = 0; i < 8 * WIDTH; i += WIDTH) {
 105                                ch >>= 1;
 106                                if (vmem16[(y * 8 * WIDTH) + i + x])
 107                                        ch |= 0x80;
 108                        }
 109                        *buf++ = ch;
 110                }
 111                /* Write the row */
 112                gpiod_set_value(par->gpio.dc, 1);
 113                ret = par->fbtftops.write(par, par->txbuf.buf, WIDTH);
 114                if (ret < 0) {
 115                        dev_err(par->info->device,
 116                                "write failed and returned: %d\n", ret);
 117                        break;
 118                }
 119        }
 120
 121        return ret;
 122}
 123
 124static int set_gamma(struct fbtft_par *par, u32 *curves)
 125{
 126        /* apply mask */
 127        curves[0] &= 0x7F;
 128
 129        write_reg(par, 0x21); /* turn on extended instruction set */
 130        write_reg(par, 0x80 | curves[0]);
 131        write_reg(par, 0x20); /* turn off extended instruction set */
 132
 133        return 0;
 134}
 135
 136static struct fbtft_display display = {
 137        .regwidth = 8,
 138        .width = WIDTH,
 139        .height = HEIGHT,
 140        .txbuflen = TXBUFLEN,
 141        .gamma_num = 1,
 142        .gamma_len = 1,
 143        .gamma = DEFAULT_GAMMA,
 144        .fbtftops = {
 145                .init_display = init_display,
 146                .set_addr_win = set_addr_win,
 147                .write_vmem = write_vmem,
 148                .set_gamma = set_gamma,
 149        },
 150        .backlight = 1,
 151};
 152
 153FBTFT_REGISTER_DRIVER(DRVNAME, "teralane,tls8204", &display);
 154
 155MODULE_ALIAS("spi:" DRVNAME);
 156MODULE_ALIAS("spi:tls8204");
 157
 158MODULE_DESCRIPTION("FB driver for the TLS8204 LCD Controller");
 159MODULE_AUTHOR("Michael Hope");
 160MODULE_LICENSE("GPL");
 161