ltv350qv.c revision 18f65c793a5106b9f99822ef248e71582db03386
1/*
2 * Power control for Samsung LTV350QV Quarter VGA LCD Panel
3 *
4 * Copyright (C) 2006, 2007 Atmel Corporation
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
10#include <linux/delay.h>
11#include <linux/err.h>
12#include <linux/fb.h>
13#include <linux/init.h>
14#include <linux/lcd.h>
15#include <linux/module.h>
16#include <linux/spi/spi.h>
17
18#include "ltv350qv.h"
19
20#define POWER_IS_ON(pwr)	((pwr) <= FB_BLANK_NORMAL)
21
22struct ltv350qv {
23	struct spi_device	*spi;
24	u8			*buffer;
25	int			power;
26	struct lcd_device	*ld;
27};
28
29/*
30 * The power-on and power-off sequences are taken from the
31 * LTV350QV-F04 data sheet from Samsung. The register definitions are
32 * taken from the S6F2002 command list also from Samsung. Both
33 * documents are distributed with the AVR32 Linux BSP CD from Atmel.
34 *
35 * There's still some voodoo going on here, but it's a lot better than
36 * in the first incarnation of the driver where all we had was the raw
37 * numbers from the initialization sequence.
38 */
39static int ltv350qv_write_reg(struct ltv350qv *lcd, u8 reg, u16 val)
40{
41	struct spi_message msg;
42	struct spi_transfer index_xfer = {
43		.len		= 3,
44		.cs_change	= 1,
45	};
46	struct spi_transfer value_xfer = {
47		.len		= 3,
48	};
49
50	spi_message_init(&msg);
51
52	/* register index */
53	lcd->buffer[0] = LTV_OPC_INDEX;
54	lcd->buffer[1] = 0x00;
55	lcd->buffer[2] = reg & 0x7f;
56	index_xfer.tx_buf = lcd->buffer;
57	spi_message_add_tail(&index_xfer, &msg);
58
59	/* register value */
60	lcd->buffer[4] = LTV_OPC_DATA;
61	lcd->buffer[5] = val >> 8;
62	lcd->buffer[6] = val;
63	value_xfer.tx_buf = lcd->buffer + 4;
64	spi_message_add_tail(&value_xfer, &msg);
65
66	return spi_sync(lcd->spi, &msg);
67}
68
69/* The comments are taken straight from the data sheet */
70static int ltv350qv_power_on(struct ltv350qv *lcd)
71{
72	int ret;
73
74	/* Power On Reset Display off State */
75	if (ltv350qv_write_reg(lcd, LTV_PWRCTL1, 0x0000))
76		goto err;
77	msleep(15);
78
79	/* Power Setting Function 1 */
80	if (ltv350qv_write_reg(lcd, LTV_PWRCTL1, LTV_VCOM_DISABLE))
81		goto err;
82	if (ltv350qv_write_reg(lcd, LTV_PWRCTL2, LTV_VCOML_ENABLE))
83		goto err_power1;
84
85	/* Power Setting Function 2 */
86	if (ltv350qv_write_reg(lcd, LTV_PWRCTL1,
87			       LTV_VCOM_DISABLE | LTV_DRIVE_CURRENT(5)
88			       | LTV_SUPPLY_CURRENT(5)))
89		goto err_power2;
90
91	msleep(55);
92
93	/* Instruction Setting */
94	ret = ltv350qv_write_reg(lcd, LTV_IFCTL,
95				 LTV_NMD | LTV_REV | LTV_NL(0x1d));
96	ret |= ltv350qv_write_reg(lcd, LTV_DATACTL,
97				  LTV_DS_SAME | LTV_CHS_480
98				  | LTV_DF_RGB | LTV_RGB_BGR);
99	ret |= ltv350qv_write_reg(lcd, LTV_ENTRY_MODE,
100				  LTV_VSPL_ACTIVE_LOW
101				  | LTV_HSPL_ACTIVE_LOW
102				  | LTV_DPL_SAMPLE_RISING
103				  | LTV_EPL_ACTIVE_LOW
104				  | LTV_SS_RIGHT_TO_LEFT);
105	ret |= ltv350qv_write_reg(lcd, LTV_GATECTL1, LTV_CLW(3));
106	ret |= ltv350qv_write_reg(lcd, LTV_GATECTL2,
107				  LTV_NW_INV_1LINE | LTV_FWI(3));
108	ret |= ltv350qv_write_reg(lcd, LTV_VBP, 0x000a);
109	ret |= ltv350qv_write_reg(lcd, LTV_HBP, 0x0021);
110	ret |= ltv350qv_write_reg(lcd, LTV_SOTCTL, LTV_SDT(3) | LTV_EQ(0));
111	ret |= ltv350qv_write_reg(lcd, LTV_GAMMA(0), 0x0103);
112	ret |= ltv350qv_write_reg(lcd, LTV_GAMMA(1), 0x0301);
113	ret |= ltv350qv_write_reg(lcd, LTV_GAMMA(2), 0x1f0f);
114	ret |= ltv350qv_write_reg(lcd, LTV_GAMMA(3), 0x1f0f);
115	ret |= ltv350qv_write_reg(lcd, LTV_GAMMA(4), 0x0707);
116	ret |= ltv350qv_write_reg(lcd, LTV_GAMMA(5), 0x0307);
117	ret |= ltv350qv_write_reg(lcd, LTV_GAMMA(6), 0x0707);
118	ret |= ltv350qv_write_reg(lcd, LTV_GAMMA(7), 0x0000);
119	ret |= ltv350qv_write_reg(lcd, LTV_GAMMA(8), 0x0004);
120	ret |= ltv350qv_write_reg(lcd, LTV_GAMMA(9), 0x0000);
121	if (ret)
122		goto err_settings;
123
124	/* Wait more than 2 frames */
125	msleep(20);
126
127	/* Display On Sequence */
128	ret = ltv350qv_write_reg(lcd, LTV_PWRCTL1,
129				 LTV_VCOM_DISABLE | LTV_VCOMOUT_ENABLE
130				 | LTV_POWER_ON | LTV_DRIVE_CURRENT(5)
131				 | LTV_SUPPLY_CURRENT(5));
132	ret |= ltv350qv_write_reg(lcd, LTV_GATECTL2,
133				  LTV_NW_INV_1LINE | LTV_DSC | LTV_FWI(3));
134	if (ret)
135		goto err_disp_on;
136
137	/* Display should now be ON. Phew. */
138	return 0;
139
140err_disp_on:
141	/*
142	 * Try to recover. Error handling probably isn't very useful
143	 * at this point, just make a best effort to switch the panel
144	 * off.
145	 */
146	ltv350qv_write_reg(lcd, LTV_PWRCTL1,
147			   LTV_VCOM_DISABLE | LTV_DRIVE_CURRENT(5)
148			   | LTV_SUPPLY_CURRENT(5));
149	ltv350qv_write_reg(lcd, LTV_GATECTL2,
150			   LTV_NW_INV_1LINE | LTV_FWI(3));
151err_settings:
152err_power2:
153err_power1:
154	ltv350qv_write_reg(lcd, LTV_PWRCTL2, 0x0000);
155	msleep(1);
156err:
157	ltv350qv_write_reg(lcd, LTV_PWRCTL1, LTV_VCOM_DISABLE);
158	return -EIO;
159}
160
161static int ltv350qv_power_off(struct ltv350qv *lcd)
162{
163	int ret;
164
165	/* Display Off Sequence */
166	ret = ltv350qv_write_reg(lcd, LTV_PWRCTL1,
167				 LTV_VCOM_DISABLE
168				 | LTV_DRIVE_CURRENT(5)
169				 | LTV_SUPPLY_CURRENT(5));
170	ret |= ltv350qv_write_reg(lcd, LTV_GATECTL2,
171				  LTV_NW_INV_1LINE | LTV_FWI(3));
172
173	/* Power down setting 1 */
174	ret |= ltv350qv_write_reg(lcd, LTV_PWRCTL2, 0x0000);
175
176	/* Wait at least 1 ms */
177	msleep(1);
178
179	/* Power down setting 2 */
180	ret |= ltv350qv_write_reg(lcd, LTV_PWRCTL1, LTV_VCOM_DISABLE);
181
182	/*
183	 * No point in trying to recover here. If we can't switch the
184	 * panel off, what are we supposed to do other than inform the
185	 * user about the failure?
186	 */
187	if (ret)
188		return -EIO;
189
190	/* Display power should now be OFF */
191	return 0;
192}
193
194static int ltv350qv_power(struct ltv350qv *lcd, int power)
195{
196	int ret = 0;
197
198	if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power))
199		ret = ltv350qv_power_on(lcd);
200	else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power))
201		ret = ltv350qv_power_off(lcd);
202
203	if (!ret)
204		lcd->power = power;
205
206	return ret;
207}
208
209static int ltv350qv_set_power(struct lcd_device *ld, int power)
210{
211	struct ltv350qv *lcd = lcd_get_data(ld);
212
213	return ltv350qv_power(lcd, power);
214}
215
216static int ltv350qv_get_power(struct lcd_device *ld)
217{
218	struct ltv350qv *lcd = lcd_get_data(ld);
219
220	return lcd->power;
221}
222
223static struct lcd_ops ltv_ops = {
224	.get_power	= ltv350qv_get_power,
225	.set_power	= ltv350qv_set_power,
226};
227
228static int __devinit ltv350qv_probe(struct spi_device *spi)
229{
230	struct ltv350qv *lcd;
231	struct lcd_device *ld;
232	int ret;
233
234	lcd = kzalloc(sizeof(struct ltv350qv), GFP_KERNEL);
235	if (!lcd)
236		return -ENOMEM;
237
238	lcd->spi = spi;
239	lcd->power = FB_BLANK_POWERDOWN;
240	lcd->buffer = kzalloc(8, GFP_KERNEL);
241
242	ld = lcd_device_register("ltv350qv", &spi->dev, lcd, &ltv_ops);
243	if (IS_ERR(ld)) {
244		ret = PTR_ERR(ld);
245		goto out_free_lcd;
246	}
247	lcd->ld = ld;
248
249	ret = ltv350qv_power(lcd, FB_BLANK_UNBLANK);
250	if (ret)
251		goto out_unregister;
252
253	dev_set_drvdata(&spi->dev, lcd);
254
255	return 0;
256
257out_unregister:
258	lcd_device_unregister(ld);
259out_free_lcd:
260	kfree(lcd);
261	return ret;
262}
263
264static int __devexit ltv350qv_remove(struct spi_device *spi)
265{
266	struct ltv350qv *lcd = dev_get_drvdata(&spi->dev);
267
268	ltv350qv_power(lcd, FB_BLANK_POWERDOWN);
269	lcd_device_unregister(lcd->ld);
270	kfree(lcd);
271
272	return 0;
273}
274
275#ifdef CONFIG_PM
276static int ltv350qv_suspend(struct spi_device *spi, pm_message_t state)
277{
278	struct ltv350qv *lcd = dev_get_drvdata(&spi->dev);
279
280	return ltv350qv_power(lcd, FB_BLANK_POWERDOWN);
281}
282
283static int ltv350qv_resume(struct spi_device *spi)
284{
285	struct ltv350qv *lcd = dev_get_drvdata(&spi->dev);
286
287	return ltv350qv_power(lcd, FB_BLANK_UNBLANK);
288}
289#else
290#define ltv350qv_suspend	NULL
291#define ltv350qv_resume		NULL
292#endif
293
294/* Power down all displays on reboot, poweroff or halt */
295static void ltv350qv_shutdown(struct spi_device *spi)
296{
297	struct ltv350qv *lcd = dev_get_drvdata(&spi->dev);
298
299	ltv350qv_power(lcd, FB_BLANK_POWERDOWN);
300}
301
302static struct spi_driver ltv350qv_driver = {
303	.driver = {
304		.name		= "ltv350qv",
305		.bus		= &spi_bus_type,
306		.owner		= THIS_MODULE,
307	},
308
309	.probe		= ltv350qv_probe,
310	.remove		= __devexit_p(ltv350qv_remove),
311	.shutdown	= ltv350qv_shutdown,
312	.suspend	= ltv350qv_suspend,
313	.resume		= ltv350qv_resume,
314};
315
316static int __init ltv350qv_init(void)
317{
318	return spi_register_driver(&ltv350qv_driver);
319}
320
321static void __exit ltv350qv_exit(void)
322{
323	spi_unregister_driver(&ltv350qv_driver);
324}
325module_init(ltv350qv_init);
326module_exit(ltv350qv_exit);
327
328MODULE_AUTHOR("Haavard Skinnemoen <hskinnemoen@atmel.com>");
329MODULE_DESCRIPTION("Samsung LTV350QV LCD Driver");
330MODULE_LICENSE("GPL");
331