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