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 | |
29 | static unsigned int bs = 4; |
30 | module_param(bs, uint, 0000); |
31 | MODULE_PARM_DESC(bs, "BS[2:0] Bias voltage level: 0-7 (default: 4)" ); |
32 | |
33 | static 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 | |
72 | static 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 | |
86 | static 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(desc: par->gpio.dc, value: 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(desc: par->gpio.dc, value: 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 | |
124 | static 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 | |
136 | static 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 | |
153 | FBTFT_REGISTER_DRIVER(DRVNAME, "teralane,tls8204" , &display); |
154 | |
155 | MODULE_ALIAS("spi:" DRVNAME); |
156 | MODULE_ALIAS("spi:tls8204" ); |
157 | |
158 | MODULE_DESCRIPTION("FB driver for the TLS8204 LCD Controller" ); |
159 | MODULE_AUTHOR("Michael Hope" ); |
160 | MODULE_LICENSE("GPL" ); |
161 | |