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