1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * KFR2R09 LCD panel support |
4 | * |
5 | * Copyright (C) 2009 Magnus Damm |
6 | * |
7 | * Register settings based on the out-of-tree t33fb.c driver |
8 | * Copyright (C) 2008 Lineo Solutions, Inc. |
9 | */ |
10 | |
11 | #include <linux/delay.h> |
12 | #include <linux/err.h> |
13 | #include <linux/fb.h> |
14 | #include <linux/init.h> |
15 | #include <linux/kernel.h> |
16 | #include <linux/module.h> |
17 | #include <linux/gpio.h> |
18 | #include <video/sh_mobile_lcdc.h> |
19 | #include <mach/kfr2r09.h> |
20 | #include <cpu/sh7724.h> |
21 | |
22 | /* The on-board LCD module is a Hitachi TX07D34VM0AAA. This module is made |
23 | * up of a 240x400 LCD hooked up to a R61517 driver IC. The driver IC is |
24 | * communicating with the main port of the LCDC using an 18-bit SYS interface. |
25 | * |
26 | * The device code for this LCD module is 0x01221517. |
27 | */ |
28 | |
29 | static const unsigned char data_frame_if[] = { |
30 | 0x02, /* WEMODE: 1=cont, 0=one-shot */ |
31 | 0x00, 0x00, |
32 | 0x00, /* EPF, DFM */ |
33 | 0x02, /* RIM[1] : 1 (18bpp) */ |
34 | }; |
35 | |
36 | static const unsigned char data_panel[] = { |
37 | 0x0b, |
38 | 0x63, /* 400 lines */ |
39 | 0x04, 0x00, 0x00, 0x04, 0x11, 0x00, 0x00, |
40 | }; |
41 | |
42 | static const unsigned char data_timing[] = { |
43 | 0x00, 0x00, 0x13, 0x08, 0x08, |
44 | }; |
45 | |
46 | static const unsigned char data_timing_src[] = { |
47 | 0x11, 0x01, 0x00, 0x01, |
48 | }; |
49 | |
50 | static const unsigned char data_gamma[] = { |
51 | 0x01, 0x02, 0x08, 0x23, 0x03, 0x0c, 0x00, 0x06, 0x00, 0x00, |
52 | 0x01, 0x00, 0x0c, 0x23, 0x03, 0x08, 0x02, 0x06, 0x00, 0x00, |
53 | }; |
54 | |
55 | static const unsigned char data_power[] = { |
56 | 0x07, 0xc5, 0xdc, 0x02, 0x33, 0x0a, |
57 | }; |
58 | |
59 | static unsigned long read_reg(void *sohandle, |
60 | struct sh_mobile_lcdc_sys_bus_ops *so) |
61 | { |
62 | return so->read_data(sohandle); |
63 | } |
64 | |
65 | static void write_reg(void *sohandle, |
66 | struct sh_mobile_lcdc_sys_bus_ops *so, |
67 | int i, unsigned long v) |
68 | { |
69 | if (i) |
70 | so->write_data(sohandle, v); /* PTH4/LCDRS High [param, 17:0] */ |
71 | else |
72 | so->write_index(sohandle, v); /* PTH4/LCDRS Low [cmd, 7:0] */ |
73 | } |
74 | |
75 | static void write_data(void *sohandle, |
76 | struct sh_mobile_lcdc_sys_bus_ops *so, |
77 | unsigned char const *data, int no_data) |
78 | { |
79 | int i; |
80 | |
81 | for (i = 0; i < no_data; i++) |
82 | write_reg(sohandle, so, i: 1, v: data[i]); |
83 | } |
84 | |
85 | static unsigned long read_device_code(void *sohandle, |
86 | struct sh_mobile_lcdc_sys_bus_ops *so) |
87 | { |
88 | unsigned long device_code; |
89 | |
90 | /* access protect OFF */ |
91 | write_reg(sohandle, so, i: 0, v: 0xb0); |
92 | write_reg(sohandle, so, i: 1, v: 0x00); |
93 | |
94 | /* deep standby OFF */ |
95 | write_reg(sohandle, so, i: 0, v: 0xb1); |
96 | write_reg(sohandle, so, i: 1, v: 0x00); |
97 | |
98 | /* device code command */ |
99 | write_reg(sohandle, so, i: 0, v: 0xbf); |
100 | mdelay(50); |
101 | |
102 | /* dummy read */ |
103 | read_reg(sohandle, so); |
104 | |
105 | /* read device code */ |
106 | device_code = ((read_reg(sohandle, so) & 0xff) << 24); |
107 | device_code |= ((read_reg(sohandle, so) & 0xff) << 16); |
108 | device_code |= ((read_reg(sohandle, so) & 0xff) << 8); |
109 | device_code |= (read_reg(sohandle, so) & 0xff); |
110 | |
111 | return device_code; |
112 | } |
113 | |
114 | static void write_memory_start(void *sohandle, |
115 | struct sh_mobile_lcdc_sys_bus_ops *so) |
116 | { |
117 | write_reg(sohandle, so, i: 0, v: 0x2c); |
118 | } |
119 | |
120 | static void clear_memory(void *sohandle, |
121 | struct sh_mobile_lcdc_sys_bus_ops *so) |
122 | { |
123 | int i; |
124 | |
125 | /* write start */ |
126 | write_memory_start(sohandle, so); |
127 | |
128 | /* paint it black */ |
129 | for (i = 0; i < (240 * 400); i++) |
130 | write_reg(sohandle, so, i: 1, v: 0x00); |
131 | } |
132 | |
133 | static void display_on(void *sohandle, |
134 | struct sh_mobile_lcdc_sys_bus_ops *so) |
135 | { |
136 | /* access protect off */ |
137 | write_reg(sohandle, so, i: 0, v: 0xb0); |
138 | write_reg(sohandle, so, i: 1, v: 0x00); |
139 | |
140 | /* exit deep standby mode */ |
141 | write_reg(sohandle, so, i: 0, v: 0xb1); |
142 | write_reg(sohandle, so, i: 1, v: 0x00); |
143 | |
144 | /* frame memory I/F */ |
145 | write_reg(sohandle, so, i: 0, v: 0xb3); |
146 | write_data(sohandle, so, data: data_frame_if, ARRAY_SIZE(data_frame_if)); |
147 | |
148 | /* display mode and frame memory write mode */ |
149 | write_reg(sohandle, so, i: 0, v: 0xb4); |
150 | write_reg(sohandle, so, i: 1, v: 0x00); /* DBI, internal clock */ |
151 | |
152 | /* panel */ |
153 | write_reg(sohandle, so, i: 0, v: 0xc0); |
154 | write_data(sohandle, so, data: data_panel, ARRAY_SIZE(data_panel)); |
155 | |
156 | /* timing (normal) */ |
157 | write_reg(sohandle, so, i: 0, v: 0xc1); |
158 | write_data(sohandle, so, data: data_timing, ARRAY_SIZE(data_timing)); |
159 | |
160 | /* timing (partial) */ |
161 | write_reg(sohandle, so, i: 0, v: 0xc2); |
162 | write_data(sohandle, so, data: data_timing, ARRAY_SIZE(data_timing)); |
163 | |
164 | /* timing (idle) */ |
165 | write_reg(sohandle, so, i: 0, v: 0xc3); |
166 | write_data(sohandle, so, data: data_timing, ARRAY_SIZE(data_timing)); |
167 | |
168 | /* timing (source/VCOM/gate driving) */ |
169 | write_reg(sohandle, so, i: 0, v: 0xc4); |
170 | write_data(sohandle, so, data: data_timing_src, ARRAY_SIZE(data_timing_src)); |
171 | |
172 | /* gamma (red) */ |
173 | write_reg(sohandle, so, i: 0, v: 0xc8); |
174 | write_data(sohandle, so, data: data_gamma, ARRAY_SIZE(data_gamma)); |
175 | |
176 | /* gamma (green) */ |
177 | write_reg(sohandle, so, i: 0, v: 0xc9); |
178 | write_data(sohandle, so, data: data_gamma, ARRAY_SIZE(data_gamma)); |
179 | |
180 | /* gamma (blue) */ |
181 | write_reg(sohandle, so, i: 0, v: 0xca); |
182 | write_data(sohandle, so, data: data_gamma, ARRAY_SIZE(data_gamma)); |
183 | |
184 | /* power (common) */ |
185 | write_reg(sohandle, so, i: 0, v: 0xd0); |
186 | write_data(sohandle, so, data: data_power, ARRAY_SIZE(data_power)); |
187 | |
188 | /* VCOM */ |
189 | write_reg(sohandle, so, i: 0, v: 0xd1); |
190 | write_reg(sohandle, so, i: 1, v: 0x00); |
191 | write_reg(sohandle, so, i: 1, v: 0x0f); |
192 | write_reg(sohandle, so, i: 1, v: 0x02); |
193 | |
194 | /* power (normal) */ |
195 | write_reg(sohandle, so, i: 0, v: 0xd2); |
196 | write_reg(sohandle, so, i: 1, v: 0x63); |
197 | write_reg(sohandle, so, i: 1, v: 0x24); |
198 | |
199 | /* power (partial) */ |
200 | write_reg(sohandle, so, i: 0, v: 0xd3); |
201 | write_reg(sohandle, so, i: 1, v: 0x63); |
202 | write_reg(sohandle, so, i: 1, v: 0x24); |
203 | |
204 | /* power (idle) */ |
205 | write_reg(sohandle, so, i: 0, v: 0xd4); |
206 | write_reg(sohandle, so, i: 1, v: 0x63); |
207 | write_reg(sohandle, so, i: 1, v: 0x24); |
208 | |
209 | write_reg(sohandle, so, i: 0, v: 0xd8); |
210 | write_reg(sohandle, so, i: 1, v: 0x77); |
211 | write_reg(sohandle, so, i: 1, v: 0x77); |
212 | |
213 | /* TE signal */ |
214 | write_reg(sohandle, so, i: 0, v: 0x35); |
215 | write_reg(sohandle, so, i: 1, v: 0x00); |
216 | |
217 | /* TE signal line */ |
218 | write_reg(sohandle, so, i: 0, v: 0x44); |
219 | write_reg(sohandle, so, i: 1, v: 0x00); |
220 | write_reg(sohandle, so, i: 1, v: 0x00); |
221 | |
222 | /* column address */ |
223 | write_reg(sohandle, so, i: 0, v: 0x2a); |
224 | write_reg(sohandle, so, i: 1, v: 0x00); |
225 | write_reg(sohandle, so, i: 1, v: 0x00); |
226 | write_reg(sohandle, so, i: 1, v: 0x00); |
227 | write_reg(sohandle, so, i: 1, v: 0xef); |
228 | |
229 | /* page address */ |
230 | write_reg(sohandle, so, i: 0, v: 0x2b); |
231 | write_reg(sohandle, so, i: 1, v: 0x00); |
232 | write_reg(sohandle, so, i: 1, v: 0x00); |
233 | write_reg(sohandle, so, i: 1, v: 0x01); |
234 | write_reg(sohandle, so, i: 1, v: 0x8f); |
235 | |
236 | /* exit sleep mode */ |
237 | write_reg(sohandle, so, i: 0, v: 0x11); |
238 | |
239 | mdelay(120); |
240 | |
241 | /* clear vram */ |
242 | clear_memory(sohandle, so); |
243 | |
244 | /* display ON */ |
245 | write_reg(sohandle, so, i: 0, v: 0x29); |
246 | mdelay(1); |
247 | |
248 | write_memory_start(sohandle, so); |
249 | } |
250 | |
251 | int kfr2r09_lcd_setup(void *sohandle, struct sh_mobile_lcdc_sys_bus_ops *so) |
252 | { |
253 | /* power on */ |
254 | gpio_set_value(gpio: GPIO_PTF4, value: 0); /* PROTECT/ -> L */ |
255 | gpio_set_value(gpio: GPIO_PTE4, value: 0); /* LCD_RST/ -> L */ |
256 | gpio_set_value(gpio: GPIO_PTF4, value: 1); /* PROTECT/ -> H */ |
257 | udelay(1100); |
258 | gpio_set_value(gpio: GPIO_PTE4, value: 1); /* LCD_RST/ -> H */ |
259 | udelay(10); |
260 | gpio_set_value(gpio: GPIO_PTF4, value: 0); /* PROTECT/ -> L */ |
261 | mdelay(20); |
262 | |
263 | if (read_device_code(sohandle, so) != 0x01221517) |
264 | return -ENODEV; |
265 | |
266 | pr_info("KFR2R09 WQVGA LCD Module detected.\n" ); |
267 | |
268 | display_on(sohandle, so); |
269 | return 0; |
270 | } |
271 | |
272 | void kfr2r09_lcd_start(void *sohandle, struct sh_mobile_lcdc_sys_bus_ops *so) |
273 | { |
274 | write_memory_start(sohandle, so); |
275 | } |
276 | |