fb_tls8204.c (3601B)
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");