]>
Commit | Line | Data |
---|---|---|
e7fb9c4a AP |
1 | /* |
2 | * l4f00242t03.c -- support for Epson L4F00242T03 LCD | |
3 | * | |
4 | * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved. | |
5 | * | |
6 | * Copyright (c) 2009 Alberto Panizzo <maramaopercheseimorto@gmail.com> | |
7 | * Inspired by Marek Vasut work in l4f00242t03.c | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License version 2 as | |
11 | * published by the Free Software Foundation. | |
12 | */ | |
13 | ||
14 | #include <linux/device.h> | |
15 | #include <linux/kernel.h> | |
16 | #include <linux/delay.h> | |
17 | #include <linux/gpio.h> | |
18 | #include <linux/lcd.h> | |
19 | #include <linux/regulator/consumer.h> | |
20 | ||
21 | #include <linux/spi/spi.h> | |
22 | #include <linux/spi/l4f00242t03.h> | |
23 | ||
24 | struct l4f00242t03_priv { | |
25 | struct spi_device *spi; | |
26 | struct lcd_device *ld; | |
27 | int lcd_on:1; | |
28 | struct regulator *io_reg; | |
29 | struct regulator *core_reg; | |
30 | }; | |
31 | ||
32 | ||
33 | static void l4f00242t03_reset(unsigned int gpio) | |
34 | { | |
35 | pr_debug("l4f00242t03_reset.\n"); | |
36 | gpio_set_value(gpio, 1); | |
37 | mdelay(100); | |
38 | gpio_set_value(gpio, 0); | |
39 | mdelay(10); /* tRES >= 100us */ | |
40 | gpio_set_value(gpio, 1); | |
41 | mdelay(20); | |
42 | } | |
43 | ||
44 | #define param(x) ((x) | 0x100) | |
45 | ||
46 | static void l4f00242t03_lcd_init(struct spi_device *spi) | |
47 | { | |
48 | struct l4f00242t03_pdata *pdata = spi->dev.platform_data; | |
49 | struct l4f00242t03_priv *priv = dev_get_drvdata(&spi->dev); | |
50 | const u16 cmd[] = { 0x36, param(0), 0x3A, param(0x60) }; | |
51 | ||
52 | dev_dbg(&spi->dev, "initializing LCD\n"); | |
53 | ||
54 | if (priv->io_reg) { | |
55 | regulator_set_voltage(priv->io_reg, 1800000, 1800000); | |
56 | regulator_enable(priv->io_reg); | |
57 | } | |
58 | ||
59 | if (priv->core_reg) { | |
60 | regulator_set_voltage(priv->core_reg, 2800000, 2800000); | |
61 | regulator_enable(priv->core_reg); | |
62 | } | |
63 | ||
64 | gpio_set_value(pdata->data_enable_gpio, 1); | |
65 | msleep(60); | |
66 | spi_write(spi, (const u8 *)cmd, ARRAY_SIZE(cmd) * sizeof(u16)); | |
67 | } | |
68 | ||
69 | static int l4f00242t03_lcd_power_set(struct lcd_device *ld, int power) | |
70 | { | |
71 | struct l4f00242t03_priv *priv = lcd_get_data(ld); | |
72 | struct spi_device *spi = priv->spi; | |
73 | ||
74 | const u16 slpout = 0x11; | |
75 | const u16 dison = 0x29; | |
76 | ||
77 | const u16 slpin = 0x10; | |
78 | const u16 disoff = 0x28; | |
79 | ||
80 | if (power) { | |
81 | if (priv->lcd_on) | |
82 | return 0; | |
83 | ||
84 | dev_dbg(&spi->dev, "turning on LCD\n"); | |
85 | ||
86 | spi_write(spi, (const u8 *)&slpout, sizeof(u16)); | |
87 | msleep(60); | |
88 | spi_write(spi, (const u8 *)&dison, sizeof(u16)); | |
89 | ||
90 | priv->lcd_on = 1; | |
91 | } else { | |
92 | if (!priv->lcd_on) | |
93 | return 0; | |
94 | ||
95 | dev_dbg(&spi->dev, "turning off LCD\n"); | |
96 | ||
97 | spi_write(spi, (const u8 *)&disoff, sizeof(u16)); | |
98 | msleep(60); | |
99 | spi_write(spi, (const u8 *)&slpin, sizeof(u16)); | |
100 | ||
101 | priv->lcd_on = 0; | |
102 | } | |
103 | ||
104 | return 0; | |
105 | } | |
106 | ||
107 | static struct lcd_ops l4f_ops = { | |
108 | .set_power = l4f00242t03_lcd_power_set, | |
109 | .get_power = NULL, | |
110 | }; | |
111 | ||
112 | static int __devinit l4f00242t03_probe(struct spi_device *spi) | |
113 | { | |
114 | struct l4f00242t03_priv *priv; | |
115 | struct l4f00242t03_pdata *pdata = spi->dev.platform_data; | |
116 | int ret; | |
117 | ||
118 | if (pdata == NULL) { | |
119 | dev_err(&spi->dev, "Uninitialized platform data.\n"); | |
120 | return -EINVAL; | |
121 | } | |
122 | ||
123 | priv = kzalloc(sizeof(struct l4f00242t03_priv), GFP_KERNEL); | |
124 | ||
125 | if (priv == NULL) { | |
126 | dev_err(&spi->dev, "No memory for this device.\n"); | |
127 | ret = -ENOMEM; | |
128 | goto err; | |
129 | } | |
130 | ||
131 | dev_set_drvdata(&spi->dev, priv); | |
132 | spi->bits_per_word = 9; | |
133 | spi_setup(spi); | |
134 | ||
135 | priv->spi = spi; | |
136 | ||
137 | ret = gpio_request(pdata->reset_gpio, "lcd l4f00242t03 reset"); | |
138 | if (ret) { | |
139 | dev_err(&spi->dev, | |
140 | "Unable to get the lcd l4f00242t03 reset gpio.\n"); | |
141 | return ret; | |
142 | } | |
143 | ||
144 | ret = gpio_direction_output(pdata->reset_gpio, 1); | |
145 | if (ret) | |
146 | goto err2; | |
147 | ||
148 | ret = gpio_request(pdata->data_enable_gpio, | |
149 | "lcd l4f00242t03 data enable"); | |
150 | if (ret) { | |
151 | dev_err(&spi->dev, | |
152 | "Unable to get the lcd l4f00242t03 data en gpio.\n"); | |
153 | return ret; | |
154 | } | |
155 | ||
156 | ret = gpio_direction_output(pdata->data_enable_gpio, 0); | |
157 | if (ret) | |
158 | goto err3; | |
159 | ||
160 | if (pdata->io_supply) { | |
161 | priv->io_reg = regulator_get(NULL, pdata->io_supply); | |
162 | ||
163 | if (IS_ERR(priv->io_reg)) { | |
164 | pr_err("%s: Unable to get the IO regulator\n", | |
165 | __func__); | |
166 | goto err3; | |
167 | } | |
168 | } | |
169 | ||
170 | if (pdata->core_supply) { | |
171 | priv->core_reg = regulator_get(NULL, pdata->core_supply); | |
172 | ||
173 | if (IS_ERR(priv->core_reg)) { | |
174 | pr_err("%s: Unable to get the core regulator\n", | |
175 | __func__); | |
176 | goto err4; | |
177 | } | |
178 | } | |
179 | ||
180 | priv->ld = lcd_device_register("l4f00242t03", | |
181 | &spi->dev, priv, &l4f_ops); | |
182 | if (IS_ERR(priv->ld)) { | |
183 | ret = PTR_ERR(priv->ld); | |
184 | goto err5; | |
185 | } | |
186 | ||
187 | /* Init the LCD */ | |
188 | l4f00242t03_reset(pdata->reset_gpio); | |
189 | l4f00242t03_lcd_init(spi); | |
190 | l4f00242t03_lcd_power_set(priv->ld, 1); | |
191 | ||
192 | dev_info(&spi->dev, "Epson l4f00242t03 lcd probed.\n"); | |
193 | ||
194 | return 0; | |
195 | ||
196 | err5: | |
197 | if (priv->core_reg) | |
198 | regulator_put(priv->core_reg); | |
199 | err4: | |
200 | if (priv->io_reg) | |
201 | regulator_put(priv->io_reg); | |
202 | err3: | |
203 | gpio_free(pdata->data_enable_gpio); | |
204 | err2: | |
205 | gpio_free(pdata->reset_gpio); | |
206 | err: | |
207 | kfree(priv); | |
208 | ||
209 | return ret; | |
210 | } | |
211 | ||
212 | static int __devexit l4f00242t03_remove(struct spi_device *spi) | |
213 | { | |
214 | struct l4f00242t03_priv *priv = dev_get_drvdata(&spi->dev); | |
215 | struct l4f00242t03_pdata *pdata = priv->spi->dev.platform_data; | |
216 | ||
217 | l4f00242t03_lcd_power_set(priv->ld, 0); | |
218 | lcd_device_unregister(priv->ld); | |
219 | ||
220 | gpio_free(pdata->data_enable_gpio); | |
221 | gpio_free(pdata->reset_gpio); | |
222 | ||
223 | if (priv->io_reg) | |
224 | regulator_put(priv->core_reg); | |
225 | if (priv->core_reg) | |
226 | regulator_put(priv->io_reg); | |
227 | ||
228 | kfree(priv); | |
229 | ||
230 | return 0; | |
231 | } | |
232 | ||
233 | static struct spi_driver l4f00242t03_driver = { | |
234 | .driver = { | |
235 | .name = "l4f00242t03", | |
236 | .owner = THIS_MODULE, | |
237 | }, | |
238 | .probe = l4f00242t03_probe, | |
239 | .remove = __devexit_p(l4f00242t03_remove), | |
240 | }; | |
241 | ||
242 | static __init int l4f00242t03_init(void) | |
243 | { | |
244 | return spi_register_driver(&l4f00242t03_driver); | |
245 | } | |
246 | ||
247 | static __exit void l4f00242t03_exit(void) | |
248 | { | |
249 | spi_unregister_driver(&l4f00242t03_driver); | |
250 | } | |
251 | ||
252 | module_init(l4f00242t03_init); | |
253 | module_exit(l4f00242t03_exit); | |
254 | ||
255 | MODULE_AUTHOR("Alberto Panizzo <maramaopercheseimorto@gmail.com>"); | |
256 | MODULE_DESCRIPTION("EPSON L4F00242T03 LCD"); | |
c3cf2e44 | 257 | MODULE_LICENSE("GPL v2"); |