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