PageRenderTime 63ms CodeModel.GetById 21ms app.highlight 38ms RepoModel.GetById 0ms app.codeStats 0ms

/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c

https://bitbucket.org/cyanogenmod/android_kernel_asus_tf300t
C | 279 lines | 208 code | 51 blank | 20 comment | 11 complexity | 2baa7b6972da0acb1aba1dad35ef45e1 MD5 | raw file
Possible License(s): LGPL-2.0, AGPL-1.0, GPL-2.0
  1/*
  2 * LCD panel driver for LG.Philips LB035Q02
  3 *
  4 * Author: Steve Sakoman <steve@sakoman.com>
  5 *
  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/mutex.h>
 23
 24#include <video/omapdss.h>
 25
 26struct lb035q02_data {
 27	struct mutex lock;
 28};
 29
 30static struct omap_video_timings lb035q02_timings = {
 31	.x_res = 320,
 32	.y_res = 240,
 33
 34	.pixel_clock	= 6500,
 35
 36	.hsw		= 2,
 37	.hfp		= 20,
 38	.hbp		= 68,
 39
 40	.vsw		= 2,
 41	.vfp		= 4,
 42	.vbp		= 18,
 43};
 44
 45static int lb035q02_panel_power_on(struct omap_dss_device *dssdev)
 46{
 47	int r;
 48
 49	if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
 50		return 0;
 51
 52	r = omapdss_dpi_display_enable(dssdev);
 53	if (r)
 54		goto err0;
 55
 56	if (dssdev->platform_enable) {
 57		r = dssdev->platform_enable(dssdev);
 58		if (r)
 59			goto err1;
 60	}
 61
 62	return 0;
 63err1:
 64	omapdss_dpi_display_disable(dssdev);
 65err0:
 66	return r;
 67}
 68
 69static void lb035q02_panel_power_off(struct omap_dss_device *dssdev)
 70{
 71	if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
 72		return;
 73
 74	if (dssdev->platform_disable)
 75		dssdev->platform_disable(dssdev);
 76
 77	omapdss_dpi_display_disable(dssdev);
 78}
 79
 80static int lb035q02_panel_probe(struct omap_dss_device *dssdev)
 81{
 82	struct lb035q02_data *ld;
 83	int r;
 84
 85	dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
 86		OMAP_DSS_LCD_IHS;
 87	dssdev->panel.timings = lb035q02_timings;
 88
 89	ld = kzalloc(sizeof(*ld), GFP_KERNEL);
 90	if (!ld) {
 91		r = -ENOMEM;
 92		goto err;
 93	}
 94	mutex_init(&ld->lock);
 95	dev_set_drvdata(&dssdev->dev, ld);
 96	return 0;
 97err:
 98	return r;
 99}
100
101static void lb035q02_panel_remove(struct omap_dss_device *dssdev)
102{
103	struct lb035q02_data *ld = dev_get_drvdata(&dssdev->dev);
104
105	kfree(ld);
106}
107
108static int lb035q02_panel_enable(struct omap_dss_device *dssdev)
109{
110	struct lb035q02_data *ld = dev_get_drvdata(&dssdev->dev);
111	int r;
112
113	mutex_lock(&ld->lock);
114
115	r = lb035q02_panel_power_on(dssdev);
116	if (r)
117		goto err;
118	dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
119
120	mutex_unlock(&ld->lock);
121	return 0;
122err:
123	mutex_unlock(&ld->lock);
124	return r;
125}
126
127static void lb035q02_panel_disable(struct omap_dss_device *dssdev)
128{
129	struct lb035q02_data *ld = dev_get_drvdata(&dssdev->dev);
130
131	mutex_lock(&ld->lock);
132
133	lb035q02_panel_power_off(dssdev);
134	dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
135
136	mutex_unlock(&ld->lock);
137}
138
139static int lb035q02_panel_suspend(struct omap_dss_device *dssdev)
140{
141	struct lb035q02_data *ld = dev_get_drvdata(&dssdev->dev);
142
143	mutex_lock(&ld->lock);
144
145	lb035q02_panel_power_off(dssdev);
146	dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
147
148	mutex_unlock(&ld->lock);
149	return 0;
150}
151
152static int lb035q02_panel_resume(struct omap_dss_device *dssdev)
153{
154	struct lb035q02_data *ld = dev_get_drvdata(&dssdev->dev);
155	int r;
156
157	mutex_lock(&ld->lock);
158
159	r = lb035q02_panel_power_on(dssdev);
160	if (r)
161		goto err;
162	dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
163
164	mutex_unlock(&ld->lock);
165	return 0;
166err:
167	mutex_unlock(&ld->lock);
168	return r;
169}
170
171static struct omap_dss_driver lb035q02_driver = {
172	.probe		= lb035q02_panel_probe,
173	.remove		= lb035q02_panel_remove,
174
175	.enable		= lb035q02_panel_enable,
176	.disable	= lb035q02_panel_disable,
177	.suspend	= lb035q02_panel_suspend,
178	.resume		= lb035q02_panel_resume,
179
180	.driver         = {
181		.name   = "lgphilips_lb035q02_panel",
182		.owner  = THIS_MODULE,
183	},
184};
185
186static int lb035q02_write_reg(struct spi_device *spi, u8 reg, u16 val)
187{
188	struct spi_message msg;
189	struct spi_transfer index_xfer = {
190		.len		= 3,
191		.cs_change	= 1,
192	};
193	struct spi_transfer value_xfer = {
194		.len		= 3,
195	};
196	u8	buffer[16];
197
198	spi_message_init(&msg);
199
200	/* register index */
201	buffer[0] = 0x70;
202	buffer[1] = 0x00;
203	buffer[2] = reg & 0x7f;
204	index_xfer.tx_buf = buffer;
205	spi_message_add_tail(&index_xfer, &msg);
206
207	/* register value */
208	buffer[4] = 0x72;
209	buffer[5] = val >> 8;
210	buffer[6] = val;
211	value_xfer.tx_buf = buffer + 4;
212	spi_message_add_tail(&value_xfer, &msg);
213
214	return spi_sync(spi, &msg);
215}
216
217static void init_lb035q02_panel(struct spi_device *spi)
218{
219	/* Init sequence from page 28 of the lb035q02 spec */
220	lb035q02_write_reg(spi, 0x01, 0x6300);
221	lb035q02_write_reg(spi, 0x02, 0x0200);
222	lb035q02_write_reg(spi, 0x03, 0x0177);
223	lb035q02_write_reg(spi, 0x04, 0x04c7);
224	lb035q02_write_reg(spi, 0x05, 0xffc0);
225	lb035q02_write_reg(spi, 0x06, 0xe806);
226	lb035q02_write_reg(spi, 0x0a, 0x4008);
227	lb035q02_write_reg(spi, 0x0b, 0x0000);
228	lb035q02_write_reg(spi, 0x0d, 0x0030);
229	lb035q02_write_reg(spi, 0x0e, 0x2800);
230	lb035q02_write_reg(spi, 0x0f, 0x0000);
231	lb035q02_write_reg(spi, 0x16, 0x9f80);
232	lb035q02_write_reg(spi, 0x17, 0x0a0f);
233	lb035q02_write_reg(spi, 0x1e, 0x00c1);
234	lb035q02_write_reg(spi, 0x30, 0x0300);
235	lb035q02_write_reg(spi, 0x31, 0x0007);
236	lb035q02_write_reg(spi, 0x32, 0x0000);
237	lb035q02_write_reg(spi, 0x33, 0x0000);
238	lb035q02_write_reg(spi, 0x34, 0x0707);
239	lb035q02_write_reg(spi, 0x35, 0x0004);
240	lb035q02_write_reg(spi, 0x36, 0x0302);
241	lb035q02_write_reg(spi, 0x37, 0x0202);
242	lb035q02_write_reg(spi, 0x3a, 0x0a0d);
243	lb035q02_write_reg(spi, 0x3b, 0x0806);
244}
245
246static int __devinit lb035q02_panel_spi_probe(struct spi_device *spi)
247{
248	init_lb035q02_panel(spi);
249	return omap_dss_register_driver(&lb035q02_driver);
250}
251
252static int __devexit lb035q02_panel_spi_remove(struct spi_device *spi)
253{
254	omap_dss_unregister_driver(&lb035q02_driver);
255	return 0;
256}
257
258static struct spi_driver lb035q02_spi_driver = {
259	.driver		= {
260		.name	= "lgphilips_lb035q02_panel-spi",
261		.owner	= THIS_MODULE,
262	},
263	.probe		= lb035q02_panel_spi_probe,
264	.remove		= __devexit_p(lb035q02_panel_spi_remove),
265};
266
267static int __init lb035q02_panel_drv_init(void)
268{
269	return spi_register_driver(&lb035q02_spi_driver);
270}
271
272static void __exit lb035q02_panel_drv_exit(void)
273{
274	spi_unregister_driver(&lb035q02_spi_driver);
275}
276
277module_init(lb035q02_panel_drv_init);
278module_exit(lb035q02_panel_drv_exit);
279MODULE_LICENSE("GPL");