1 /*
2  * Support for NEC-nl8048hl11-01b panel driver
3  *
4  * Copyright (C) 2010 Texas Instruments Inc.
5  * Author: Erik Gilling <konkers@android.com>
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License version 2 as published by
8  * the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13  * more details.
14  *
15  * You should have received a copy of the GNU General Public License along with
16  * this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include <linux/module.h>
20 #include <linux/delay.h>
21 #include <linux/spi/spi.h>
22 #include <linux/backlight.h>
23 #include <linux/fb.h>
24 
25 #include <video/omapdss.h>
26 
27 #define LCD_XRES		800
28 #define LCD_YRES		480
29 /*
30  * NEC PIX Clock Ratings
31  * MIN:21.8MHz TYP:23.8MHz MAX:25.7MHz
32  */
33 #define LCD_PIXEL_CLOCK		23800
34 
35 struct nec_8048_data {
36 	struct backlight_device *bl;
37 };
38 
39 static const struct {
40 	unsigned char addr;
41 	unsigned char dat;
42 } nec_8048_init_seq[] = {
43 	{ 3, 0x01 }, { 0, 0x00 }, { 1, 0x01 }, { 4, 0x00 }, { 5, 0x14 },
44 	{ 6, 0x24 }, { 16, 0xD7 }, { 17, 0x00 }, { 18, 0x00 }, { 19, 0x55 },
45 	{ 20, 0x01 }, { 21, 0x70 }, { 22, 0x1E }, { 23, 0x25 },	{ 24, 0x25 },
46 	{ 25, 0x02 }, { 26, 0x02 }, { 27, 0xA0 }, { 32, 0x2F }, { 33, 0x0F },
47 	{ 34, 0x0F }, { 35, 0x0F }, { 36, 0x0F }, { 37, 0x0F },	{ 38, 0x0F },
48 	{ 39, 0x00 }, { 40, 0x02 }, { 41, 0x02 }, { 42, 0x02 },	{ 43, 0x0F },
49 	{ 44, 0x0F }, { 45, 0x0F }, { 46, 0x0F }, { 47, 0x0F },	{ 48, 0x0F },
50 	{ 49, 0x0F }, { 50, 0x00 }, { 51, 0x02 }, { 52, 0x02 }, { 53, 0x02 },
51 	{ 80, 0x0C }, { 83, 0x42 }, { 84, 0x42 }, { 85, 0x41 },	{ 86, 0x14 },
52 	{ 89, 0x88 }, { 90, 0x01 }, { 91, 0x00 }, { 92, 0x02 },	{ 93, 0x0C },
53 	{ 94, 0x1C }, { 95, 0x27 }, { 98, 0x49 }, { 99, 0x27 }, { 102, 0x76 },
54 	{ 103, 0x27 }, { 112, 0x01 }, { 113, 0x0E }, { 114, 0x02 },
55 	{ 115, 0x0C }, { 118, 0x0C }, { 121, 0x30 }, { 130, 0x00 },
56 	{ 131, 0x00 }, { 132, 0xFC }, { 134, 0x00 }, { 136, 0x00 },
57 	{ 138, 0x00 }, { 139, 0x00 }, { 140, 0x00 }, { 141, 0xFC },
58 	{ 143, 0x00 }, { 145, 0x00 }, { 147, 0x00 }, { 148, 0x00 },
59 	{ 149, 0x00 }, { 150, 0xFC }, { 152, 0x00 }, { 154, 0x00 },
60 	{ 156, 0x00 }, { 157, 0x00 }, { 2, 0x00 },
61 };
62 
63 /*
64  * NEC NL8048HL11-01B  Manual
65  * defines HFB, HSW, HBP, VFP, VSW, VBP as shown below
66  */
67 
68 static struct omap_video_timings nec_8048_panel_timings = {
69 	/* 800 x 480 @ 60 Hz  Reduced blanking VESA CVT 0.31M3-R */
70 	.x_res		= LCD_XRES,
71 	.y_res		= LCD_YRES,
72 	.pixel_clock	= LCD_PIXEL_CLOCK,
73 	.hfp		= 6,
74 	.hsw		= 1,
75 	.hbp		= 4,
76 	.vfp		= 3,
77 	.vsw		= 1,
78 	.vbp		= 4,
79 };
80 
nec_8048_bl_update_status(struct backlight_device * bl)81 static int nec_8048_bl_update_status(struct backlight_device *bl)
82 {
83 	struct omap_dss_device *dssdev = dev_get_drvdata(&bl->dev);
84 	int level;
85 
86 	if (!dssdev->set_backlight)
87 		return -EINVAL;
88 
89 	if (bl->props.fb_blank == FB_BLANK_UNBLANK &&
90 			bl->props.power == FB_BLANK_UNBLANK)
91 		level = bl->props.brightness;
92 	else
93 		level = 0;
94 
95 	return dssdev->set_backlight(dssdev, level);
96 }
97 
nec_8048_bl_get_brightness(struct backlight_device * bl)98 static int nec_8048_bl_get_brightness(struct backlight_device *bl)
99 {
100 	if (bl->props.fb_blank == FB_BLANK_UNBLANK &&
101 			bl->props.power == FB_BLANK_UNBLANK)
102 		return bl->props.brightness;
103 
104 	return 0;
105 }
106 
107 static const struct backlight_ops nec_8048_bl_ops = {
108 	.get_brightness	= nec_8048_bl_get_brightness,
109 	.update_status	= nec_8048_bl_update_status,
110 };
111 
nec_8048_panel_probe(struct omap_dss_device * dssdev)112 static int nec_8048_panel_probe(struct omap_dss_device *dssdev)
113 {
114 	struct backlight_device *bl;
115 	struct nec_8048_data *necd;
116 	struct backlight_properties props;
117 	int r;
118 
119 	dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
120 				OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_RF |
121 				OMAP_DSS_LCD_ONOFF;
122 	dssdev->panel.timings = nec_8048_panel_timings;
123 
124 	necd = kzalloc(sizeof(*necd), GFP_KERNEL);
125 	if (!necd)
126 		return -ENOMEM;
127 
128 	dev_set_drvdata(&dssdev->dev, necd);
129 
130 	memset(&props, 0, sizeof(struct backlight_properties));
131 	props.max_brightness = 255;
132 
133 	bl = backlight_device_register("nec-8048", &dssdev->dev, dssdev,
134 			&nec_8048_bl_ops, &props);
135 	if (IS_ERR(bl)) {
136 		r = PTR_ERR(bl);
137 		kfree(necd);
138 		return r;
139 	}
140 	necd->bl = bl;
141 
142 	bl->props.fb_blank = FB_BLANK_UNBLANK;
143 	bl->props.power = FB_BLANK_UNBLANK;
144 	bl->props.max_brightness = dssdev->max_backlight_level;
145 	bl->props.brightness = dssdev->max_backlight_level;
146 
147 	r = nec_8048_bl_update_status(bl);
148 	if (r < 0)
149 		dev_err(&dssdev->dev, "failed to set lcd brightness\n");
150 
151 	return 0;
152 }
153 
nec_8048_panel_remove(struct omap_dss_device * dssdev)154 static void nec_8048_panel_remove(struct omap_dss_device *dssdev)
155 {
156 	struct nec_8048_data *necd = dev_get_drvdata(&dssdev->dev);
157 	struct backlight_device *bl = necd->bl;
158 
159 	bl->props.power = FB_BLANK_POWERDOWN;
160 	nec_8048_bl_update_status(bl);
161 	backlight_device_unregister(bl);
162 
163 	kfree(necd);
164 }
165 
nec_8048_panel_power_on(struct omap_dss_device * dssdev)166 static int nec_8048_panel_power_on(struct omap_dss_device *dssdev)
167 {
168 	int r;
169 	struct nec_8048_data *necd = dev_get_drvdata(&dssdev->dev);
170 	struct backlight_device *bl = necd->bl;
171 
172 	if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
173 		return 0;
174 
175 	r = omapdss_dpi_display_enable(dssdev);
176 	if (r)
177 		goto err0;
178 
179 	if (dssdev->platform_enable) {
180 		r = dssdev->platform_enable(dssdev);
181 		if (r)
182 			goto err1;
183 	}
184 
185 	r = nec_8048_bl_update_status(bl);
186 	if (r < 0)
187 		dev_err(&dssdev->dev, "failed to set lcd brightness\n");
188 
189 	return 0;
190 err1:
191 	omapdss_dpi_display_disable(dssdev);
192 err0:
193 	return r;
194 }
195 
nec_8048_panel_power_off(struct omap_dss_device * dssdev)196 static void nec_8048_panel_power_off(struct omap_dss_device *dssdev)
197 {
198 	struct nec_8048_data *necd = dev_get_drvdata(&dssdev->dev);
199 	struct backlight_device *bl = necd->bl;
200 
201 	if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
202 		return;
203 
204 	bl->props.brightness = 0;
205 	nec_8048_bl_update_status(bl);
206 
207 	if (dssdev->platform_disable)
208 		dssdev->platform_disable(dssdev);
209 
210 	omapdss_dpi_display_disable(dssdev);
211 }
212 
nec_8048_panel_enable(struct omap_dss_device * dssdev)213 static int nec_8048_panel_enable(struct omap_dss_device *dssdev)
214 {
215 	int r;
216 
217 	r = nec_8048_panel_power_on(dssdev);
218 	if (r)
219 		return r;
220 
221 	dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
222 
223 	return 0;
224 }
225 
nec_8048_panel_disable(struct omap_dss_device * dssdev)226 static void nec_8048_panel_disable(struct omap_dss_device *dssdev)
227 {
228 	nec_8048_panel_power_off(dssdev);
229 
230 	dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
231 }
232 
nec_8048_panel_suspend(struct omap_dss_device * dssdev)233 static int nec_8048_panel_suspend(struct omap_dss_device *dssdev)
234 {
235 	nec_8048_panel_power_off(dssdev);
236 
237 	dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
238 
239 	return 0;
240 }
241 
nec_8048_panel_resume(struct omap_dss_device * dssdev)242 static int nec_8048_panel_resume(struct omap_dss_device *dssdev)
243 {
244 	int r;
245 
246 	r = nec_8048_panel_power_on(dssdev);
247 	if (r)
248 		return r;
249 
250 	dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
251 
252 	return 0;
253 }
254 
nec_8048_recommended_bpp(struct omap_dss_device * dssdev)255 static int nec_8048_recommended_bpp(struct omap_dss_device *dssdev)
256 {
257 	return 16;
258 }
259 
260 static struct omap_dss_driver nec_8048_driver = {
261 	.probe			= nec_8048_panel_probe,
262 	.remove			= nec_8048_panel_remove,
263 	.enable			= nec_8048_panel_enable,
264 	.disable		= nec_8048_panel_disable,
265 	.suspend		= nec_8048_panel_suspend,
266 	.resume			= nec_8048_panel_resume,
267 	.get_recommended_bpp	= nec_8048_recommended_bpp,
268 
269 	.driver		= {
270 		.name		= "NEC_8048_panel",
271 		.owner		= THIS_MODULE,
272 	},
273 };
274 
nec_8048_spi_send(struct spi_device * spi,unsigned char reg_addr,unsigned char reg_data)275 static int nec_8048_spi_send(struct spi_device *spi, unsigned char reg_addr,
276 			unsigned char reg_data)
277 {
278 	int ret = 0;
279 	unsigned int cmd = 0, data = 0;
280 
281 	cmd = 0x0000 | reg_addr; /* register address write */
282 	data = 0x0100 | reg_data ; /* register data write */
283 	data = (cmd << 16) | data;
284 
285 	ret = spi_write(spi, (unsigned char *)&data, 4);
286 	if (ret)
287 		pr_err("error in spi_write %x\n", data);
288 
289 	return ret;
290 }
291 
init_nec_8048_wvga_lcd(struct spi_device * spi)292 static int init_nec_8048_wvga_lcd(struct spi_device *spi)
293 {
294 	unsigned int i;
295 	/* Initialization Sequence */
296 	/* nec_8048_spi_send(spi, REG, VAL) */
297 	for (i = 0; i < (ARRAY_SIZE(nec_8048_init_seq) - 1); i++)
298 		nec_8048_spi_send(spi, nec_8048_init_seq[i].addr,
299 				nec_8048_init_seq[i].dat);
300 	udelay(20);
301 	nec_8048_spi_send(spi, nec_8048_init_seq[i].addr,
302 				nec_8048_init_seq[i].dat);
303 	return 0;
304 }
305 
nec_8048_spi_probe(struct spi_device * spi)306 static int nec_8048_spi_probe(struct spi_device *spi)
307 {
308 	spi->mode = SPI_MODE_0;
309 	spi->bits_per_word = 32;
310 	spi_setup(spi);
311 
312 	init_nec_8048_wvga_lcd(spi);
313 
314 	return omap_dss_register_driver(&nec_8048_driver);
315 }
316 
nec_8048_spi_remove(struct spi_device * spi)317 static int nec_8048_spi_remove(struct spi_device *spi)
318 {
319 	omap_dss_unregister_driver(&nec_8048_driver);
320 
321 	return 0;
322 }
323 
nec_8048_spi_suspend(struct spi_device * spi,pm_message_t mesg)324 static int nec_8048_spi_suspend(struct spi_device *spi, pm_message_t mesg)
325 {
326 	nec_8048_spi_send(spi, 2, 0x01);
327 	mdelay(40);
328 
329 	return 0;
330 }
331 
nec_8048_spi_resume(struct spi_device * spi)332 static int nec_8048_spi_resume(struct spi_device *spi)
333 {
334 	/* reinitialize the panel */
335 	spi_setup(spi);
336 	nec_8048_spi_send(spi, 2, 0x00);
337 	init_nec_8048_wvga_lcd(spi);
338 
339 	return 0;
340 }
341 
342 static struct spi_driver nec_8048_spi_driver = {
343 	.probe		= nec_8048_spi_probe,
344 	.remove		= __devexit_p(nec_8048_spi_remove),
345 	.suspend	= nec_8048_spi_suspend,
346 	.resume		= nec_8048_spi_resume,
347 	.driver		= {
348 		.name	= "nec_8048_spi",
349 		.owner	= THIS_MODULE,
350 	},
351 };
352 
nec_8048_lcd_init(void)353 static int __init nec_8048_lcd_init(void)
354 {
355 	return spi_register_driver(&nec_8048_spi_driver);
356 }
357 
nec_8048_lcd_exit(void)358 static void __exit nec_8048_lcd_exit(void)
359 {
360 	return spi_unregister_driver(&nec_8048_spi_driver);
361 }
362 
363 module_init(nec_8048_lcd_init);
364 module_exit(nec_8048_lcd_exit);
365 MODULE_AUTHOR("Erik Gilling <konkers@android.com>");
366 MODULE_DESCRIPTION("NEC-nl8048hl11-01b Driver");
367 MODULE_LICENSE("GPL");
368