1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * FB driver for the UC1701 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) 2014 Juergen Holzmann |
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_uc1701" |
21 | #define WIDTH 102 |
22 | #define HEIGHT 64 |
23 | #define PAGES (HEIGHT / 8) |
24 | |
25 | /* 1: Display on/off */ |
26 | #define LCD_DISPLAY_ENABLE 0xAE |
27 | /* 2: display start line set */ |
28 | #define LCD_START_LINE 0x40 |
29 | /* 3: Page address set (lower 4 bits select one of the pages) */ |
30 | #define LCD_PAGE_ADDRESS 0xB0 |
31 | /* 4: column address */ |
32 | #define LCD_COL_ADDRESS 0x10 |
33 | /* 8: select orientation */ |
34 | #define LCD_BOTTOMVIEW 0xA0 |
35 | /* 9: inverted display */ |
36 | #define LCD_DISPLAY_INVERT 0xA6 |
37 | /* 10: show memory content or switch all pixels on */ |
38 | #define LCD_ALL_PIXEL 0xA4 |
39 | /* 11: lcd bias set */ |
40 | #define LCD_BIAS 0xA2 |
41 | /* 14: Reset Controller */ |
42 | #define LCD_RESET_CMD 0xE2 |
43 | /* 15: output mode select (turns display upside-down) */ |
44 | #define LCD_SCAN_DIR 0xC0 |
45 | /* 16: power control set */ |
46 | #define LCD_POWER_CONTROL 0x28 |
47 | /* 17: voltage regulator resistor ratio set */ |
48 | #define LCD_VOLTAGE 0x20 |
49 | /* 18: Volume mode set */ |
50 | #define LCD_VOLUME_MODE 0x81 |
51 | /* 22: NOP command */ |
52 | #define LCD_NO_OP 0xE3 |
53 | /* 25: advanced program control */ |
54 | #define LCD_ADV_PROG_CTRL 0xFA |
55 | /* 25: advanced program control2 */ |
56 | #define LCD_ADV_PROG_CTRL2 0x10 |
57 | #define LCD_TEMPCOMP_HIGH 0x80 |
58 | /* column offset for normal orientation */ |
59 | #define SHIFT_ADDR_NORMAL 0 |
60 | /* column offset for bottom view orientation */ |
61 | #define SHIFT_ADDR_TOPVIEW 30 |
62 | |
63 | static int init_display(struct fbtft_par *par) |
64 | { |
65 | par->fbtftops.reset(par); |
66 | |
67 | /* softreset of LCD */ |
68 | write_reg(par, LCD_RESET_CMD); |
69 | mdelay(10); |
70 | |
71 | /* set startpoint */ |
72 | write_reg(par, LCD_START_LINE); |
73 | |
74 | /* select orientation BOTTOMVIEW */ |
75 | write_reg(par, LCD_BOTTOMVIEW | 1); |
76 | |
77 | /* output mode select (turns display upside-down) */ |
78 | write_reg(par, LCD_SCAN_DIR | 0x00); |
79 | |
80 | /* Normal Pixel mode */ |
81 | write_reg(par, LCD_ALL_PIXEL | 0); |
82 | |
83 | /* positive display */ |
84 | write_reg(par, LCD_DISPLAY_INVERT | 0); |
85 | |
86 | /* bias 1/9 */ |
87 | write_reg(par, LCD_BIAS | 0); |
88 | |
89 | /* power control mode: all features on */ |
90 | write_reg(par, LCD_POWER_CONTROL | 0x07); |
91 | |
92 | /* set voltage regulator R/R */ |
93 | write_reg(par, LCD_VOLTAGE | 0x07); |
94 | |
95 | /* volume mode set */ |
96 | write_reg(par, LCD_VOLUME_MODE); |
97 | write_reg(par, 0x09); |
98 | write_reg(par, LCD_NO_OP); |
99 | |
100 | /* advanced program control */ |
101 | write_reg(par, LCD_ADV_PROG_CTRL); |
102 | write_reg(par, LCD_ADV_PROG_CTRL2 | LCD_TEMPCOMP_HIGH); |
103 | |
104 | /* enable display */ |
105 | write_reg(par, LCD_DISPLAY_ENABLE | 1); |
106 | |
107 | return 0; |
108 | } |
109 | |
110 | static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) |
111 | { |
112 | /* goto address */ |
113 | write_reg(par, LCD_PAGE_ADDRESS); |
114 | write_reg(par, 0x00); |
115 | write_reg(par, LCD_COL_ADDRESS); |
116 | } |
117 | |
118 | static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) |
119 | { |
120 | u16 *vmem16 = (u16 *)par->info->screen_buffer; |
121 | u8 *buf; |
122 | int x, y, i; |
123 | int ret = 0; |
124 | |
125 | for (y = 0; y < PAGES; y++) { |
126 | buf = par->txbuf.buf; |
127 | for (x = 0; x < WIDTH; x++) { |
128 | *buf = 0x00; |
129 | for (i = 0; i < 8; i++) |
130 | *buf |= (vmem16[((y * 8 * WIDTH) + |
131 | (i * WIDTH)) + x] ? |
132 | 1 : 0) << i; |
133 | buf++; |
134 | } |
135 | |
136 | write_reg(par, LCD_PAGE_ADDRESS | (u8)y); |
137 | write_reg(par, 0x00); |
138 | write_reg(par, LCD_COL_ADDRESS); |
139 | gpiod_set_value(par->gpio.dc, 1); |
140 | ret = par->fbtftops.write(par, par->txbuf.buf, WIDTH); |
141 | gpiod_set_value(par->gpio.dc, 0); |
142 | } |
143 | |
144 | if (ret < 0) |
145 | dev_err(par->info->device, "write failed and returned: %d\n" , |
146 | ret); |
147 | |
148 | return ret; |
149 | } |
150 | |
151 | static struct fbtft_display display = { |
152 | .regwidth = 8, |
153 | .width = WIDTH, |
154 | .height = HEIGHT, |
155 | .fbtftops = { |
156 | .init_display = init_display, |
157 | .set_addr_win = set_addr_win, |
158 | .write_vmem = write_vmem, |
159 | }, |
160 | .backlight = 1, |
161 | }; |
162 | |
163 | FBTFT_REGISTER_DRIVER(DRVNAME, "UltraChip,uc1701" , &display); |
164 | |
165 | MODULE_ALIAS("spi:" DRVNAME); |
166 | MODULE_ALIAS("spi:uc1701" ); |
167 | |
168 | MODULE_DESCRIPTION("FB driver for the UC1701 LCD Controller" ); |
169 | MODULE_AUTHOR("Juergen Holzmann" ); |
170 | MODULE_LICENSE("GPL" ); |
171 | |