diff options
author | Florian Tobias Schandinat <FlorianSchandinat@gmx.de> | 2011-10-14 20:19:52 -0400 |
---|---|---|
committer | Florian Tobias Schandinat <FlorianSchandinat@gmx.de> | 2011-10-14 20:19:52 -0400 |
commit | ef26b7943c5821aaff1efc14c098840c49fe15c0 (patch) | |
tree | a91834ef396633c5c56a0597c2fc525e0684fc30 /drivers/video/omap2/displays | |
parent | 07aaae44f5a3962c3a410a6dd7936dfa7dece2b9 (diff) | |
parent | 3e28189038bb831512cf4f8313e1aead97c3e63f (diff) |
Merge branch 'for-florian' of git://gitorious.org/linux-omap-dss2/linux into fbdev-next
Diffstat (limited to 'drivers/video/omap2/displays')
-rw-r--r-- | drivers/video/omap2/displays/Kconfig | 28 | ||||
-rw-r--r-- | drivers/video/omap2/displays/Makefile | 3 | ||||
-rw-r--r-- | drivers/video/omap2/displays/panel-dvi.c | 363 | ||||
-rw-r--r-- | drivers/video/omap2/displays/panel-generic-dpi.c | 113 | ||||
-rw-r--r-- | drivers/video/omap2/displays/panel-n8x0.c | 747 | ||||
-rw-r--r-- | drivers/video/omap2/displays/panel-picodlp.c | 594 | ||||
-rw-r--r-- | drivers/video/omap2/displays/panel-picodlp.h | 288 | ||||
-rw-r--r-- | drivers/video/omap2/displays/panel-taal.c | 123 |
8 files changed, 2157 insertions, 102 deletions
diff --git a/drivers/video/omap2/displays/Kconfig b/drivers/video/omap2/displays/Kconfig index 609a28073178..8d8e1fe1901c 100644 --- a/drivers/video/omap2/displays/Kconfig +++ b/drivers/video/omap2/displays/Kconfig | |||
@@ -10,6 +10,13 @@ config PANEL_GENERIC_DPI | |||
10 | Supports LCD Panel used in TI SDP3430 and EVM boards, | 10 | Supports LCD Panel used in TI SDP3430 and EVM boards, |
11 | OMAP3517 EVM boards and CM-T35. | 11 | OMAP3517 EVM boards and CM-T35. |
12 | 12 | ||
13 | config PANEL_DVI | ||
14 | tristate "DVI output" | ||
15 | depends on OMAP2_DSS_DPI | ||
16 | help | ||
17 | Driver for external monitors, connected via DVI. The driver uses i2c | ||
18 | to read EDID information from the monitor. | ||
19 | |||
13 | config PANEL_LGPHILIPS_LB035Q02 | 20 | config PANEL_LGPHILIPS_LB035Q02 |
14 | tristate "LG.Philips LB035Q02 LCD Panel" | 21 | tristate "LG.Philips LB035Q02 LCD Panel" |
15 | depends on OMAP2_DSS_DPI && SPI | 22 | depends on OMAP2_DSS_DPI && SPI |
@@ -19,20 +26,30 @@ config PANEL_LGPHILIPS_LB035Q02 | |||
19 | config PANEL_SHARP_LS037V7DW01 | 26 | config PANEL_SHARP_LS037V7DW01 |
20 | tristate "Sharp LS037V7DW01 LCD Panel" | 27 | tristate "Sharp LS037V7DW01 LCD Panel" |
21 | depends on OMAP2_DSS_DPI | 28 | depends on OMAP2_DSS_DPI |
22 | select BACKLIGHT_CLASS_DEVICE | 29 | depends on BACKLIGHT_CLASS_DEVICE |
23 | help | 30 | help |
24 | LCD Panel used in TI's SDP3430 and EVM boards | 31 | LCD Panel used in TI's SDP3430 and EVM boards |
25 | 32 | ||
26 | config PANEL_NEC_NL8048HL11_01B | 33 | config PANEL_NEC_NL8048HL11_01B |
27 | tristate "NEC NL8048HL11-01B Panel" | 34 | tristate "NEC NL8048HL11-01B Panel" |
28 | depends on OMAP2_DSS_DPI | 35 | depends on OMAP2_DSS_DPI |
36 | depends on SPI | ||
37 | depends on BACKLIGHT_CLASS_DEVICE | ||
29 | help | 38 | help |
30 | This NEC NL8048HL11-01B panel is TFT LCD | 39 | This NEC NL8048HL11-01B panel is TFT LCD |
31 | used in the Zoom2/3/3630 sdp boards. | 40 | used in the Zoom2/3/3630 sdp boards. |
32 | 41 | ||
42 | config PANEL_PICODLP | ||
43 | tristate "TI PICO DLP mini-projector" | ||
44 | depends on OMAP2_DSS && I2C | ||
45 | help | ||
46 | A mini-projector used in TI's SDP4430 and EVM boards | ||
47 | For more info please visit http://www.dlp.com/projector/ | ||
48 | |||
33 | config PANEL_TAAL | 49 | config PANEL_TAAL |
34 | tristate "Taal DSI Panel" | 50 | tristate "Taal DSI Panel" |
35 | depends on OMAP2_DSS_DSI | 51 | depends on OMAP2_DSS_DSI |
52 | depends on BACKLIGHT_CLASS_DEVICE | ||
36 | help | 53 | help |
37 | Taal DSI command mode panel from TPO. | 54 | Taal DSI command mode panel from TPO. |
38 | 55 | ||
@@ -45,7 +62,14 @@ config PANEL_TPO_TD043MTEA1 | |||
45 | config PANEL_ACX565AKM | 62 | config PANEL_ACX565AKM |
46 | tristate "ACX565AKM Panel" | 63 | tristate "ACX565AKM Panel" |
47 | depends on OMAP2_DSS_SDI && SPI | 64 | depends on OMAP2_DSS_SDI && SPI |
48 | select BACKLIGHT_CLASS_DEVICE | 65 | depends on BACKLIGHT_CLASS_DEVICE |
49 | help | 66 | help |
50 | This is the LCD panel used on Nokia N900 | 67 | This is the LCD panel used on Nokia N900 |
68 | |||
69 | config PANEL_N8X0 | ||
70 | tristate "N8X0 Panel" | ||
71 | depends on OMAP2_DSS_RFBI && SPI | ||
72 | depends on BACKLIGHT_CLASS_DEVICE | ||
73 | help | ||
74 | This is the LCD panel used on Nokia N8x0 | ||
51 | endmenu | 75 | endmenu |
diff --git a/drivers/video/omap2/displays/Makefile b/drivers/video/omap2/displays/Makefile index 0f601ab3abf4..fbfafc6eebb4 100644 --- a/drivers/video/omap2/displays/Makefile +++ b/drivers/video/omap2/displays/Makefile | |||
@@ -1,8 +1,11 @@ | |||
1 | obj-$(CONFIG_PANEL_GENERIC_DPI) += panel-generic-dpi.o | 1 | obj-$(CONFIG_PANEL_GENERIC_DPI) += panel-generic-dpi.o |
2 | obj-$(CONFIG_PANEL_DVI) += panel-dvi.o | ||
2 | obj-$(CONFIG_PANEL_LGPHILIPS_LB035Q02) += panel-lgphilips-lb035q02.o | 3 | obj-$(CONFIG_PANEL_LGPHILIPS_LB035Q02) += panel-lgphilips-lb035q02.o |
3 | obj-$(CONFIG_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o | 4 | obj-$(CONFIG_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o |
4 | obj-$(CONFIG_PANEL_NEC_NL8048HL11_01B) += panel-nec-nl8048hl11-01b.o | 5 | obj-$(CONFIG_PANEL_NEC_NL8048HL11_01B) += panel-nec-nl8048hl11-01b.o |
5 | 6 | ||
6 | obj-$(CONFIG_PANEL_TAAL) += panel-taal.o | 7 | obj-$(CONFIG_PANEL_TAAL) += panel-taal.o |
8 | obj-$(CONFIG_PANEL_PICODLP) += panel-picodlp.o | ||
7 | obj-$(CONFIG_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o | 9 | obj-$(CONFIG_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o |
8 | obj-$(CONFIG_PANEL_ACX565AKM) += panel-acx565akm.o | 10 | obj-$(CONFIG_PANEL_ACX565AKM) += panel-acx565akm.o |
11 | obj-$(CONFIG_PANEL_N8X0) += panel-n8x0.o | ||
diff --git a/drivers/video/omap2/displays/panel-dvi.c b/drivers/video/omap2/displays/panel-dvi.c new file mode 100644 index 000000000000..03eb14af33e0 --- /dev/null +++ b/drivers/video/omap2/displays/panel-dvi.c | |||
@@ -0,0 +1,363 @@ | |||
1 | /* | ||
2 | * DVI output support | ||
3 | * | ||
4 | * Copyright (C) 2011 Texas Instruments Inc | ||
5 | * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License version 2 as published by | ||
9 | * the Free Software Foundation. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
14 | * more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License along with | ||
17 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
18 | */ | ||
19 | |||
20 | #include <linux/module.h> | ||
21 | #include <linux/slab.h> | ||
22 | #include <video/omapdss.h> | ||
23 | #include <linux/i2c.h> | ||
24 | #include <drm/drm_edid.h> | ||
25 | |||
26 | #include <video/omap-panel-dvi.h> | ||
27 | |||
28 | static const struct omap_video_timings panel_dvi_default_timings = { | ||
29 | .x_res = 640, | ||
30 | .y_res = 480, | ||
31 | |||
32 | .pixel_clock = 23500, | ||
33 | |||
34 | .hfp = 48, | ||
35 | .hsw = 32, | ||
36 | .hbp = 80, | ||
37 | |||
38 | .vfp = 3, | ||
39 | .vsw = 4, | ||
40 | .vbp = 7, | ||
41 | }; | ||
42 | |||
43 | struct panel_drv_data { | ||
44 | struct omap_dss_device *dssdev; | ||
45 | |||
46 | struct mutex lock; | ||
47 | }; | ||
48 | |||
49 | static inline struct panel_dvi_platform_data | ||
50 | *get_pdata(const struct omap_dss_device *dssdev) | ||
51 | { | ||
52 | return dssdev->data; | ||
53 | } | ||
54 | |||
55 | static int panel_dvi_power_on(struct omap_dss_device *dssdev) | ||
56 | { | ||
57 | struct panel_dvi_platform_data *pdata = get_pdata(dssdev); | ||
58 | int r; | ||
59 | |||
60 | if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) | ||
61 | return 0; | ||
62 | |||
63 | r = omapdss_dpi_display_enable(dssdev); | ||
64 | if (r) | ||
65 | goto err0; | ||
66 | |||
67 | if (pdata->platform_enable) { | ||
68 | r = pdata->platform_enable(dssdev); | ||
69 | if (r) | ||
70 | goto err1; | ||
71 | } | ||
72 | |||
73 | return 0; | ||
74 | err1: | ||
75 | omapdss_dpi_display_disable(dssdev); | ||
76 | err0: | ||
77 | return r; | ||
78 | } | ||
79 | |||
80 | static void panel_dvi_power_off(struct omap_dss_device *dssdev) | ||
81 | { | ||
82 | struct panel_dvi_platform_data *pdata = get_pdata(dssdev); | ||
83 | |||
84 | if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) | ||
85 | return; | ||
86 | |||
87 | if (pdata->platform_disable) | ||
88 | pdata->platform_disable(dssdev); | ||
89 | |||
90 | omapdss_dpi_display_disable(dssdev); | ||
91 | } | ||
92 | |||
93 | static int panel_dvi_probe(struct omap_dss_device *dssdev) | ||
94 | { | ||
95 | struct panel_drv_data *ddata; | ||
96 | |||
97 | ddata = kzalloc(sizeof(*ddata), GFP_KERNEL); | ||
98 | if (!ddata) | ||
99 | return -ENOMEM; | ||
100 | |||
101 | dssdev->panel.timings = panel_dvi_default_timings; | ||
102 | dssdev->panel.config = OMAP_DSS_LCD_TFT; | ||
103 | |||
104 | ddata->dssdev = dssdev; | ||
105 | mutex_init(&ddata->lock); | ||
106 | |||
107 | dev_set_drvdata(&dssdev->dev, ddata); | ||
108 | |||
109 | return 0; | ||
110 | } | ||
111 | |||
112 | static void __exit panel_dvi_remove(struct omap_dss_device *dssdev) | ||
113 | { | ||
114 | struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); | ||
115 | |||
116 | mutex_lock(&ddata->lock); | ||
117 | |||
118 | dev_set_drvdata(&dssdev->dev, NULL); | ||
119 | |||
120 | mutex_unlock(&ddata->lock); | ||
121 | |||
122 | kfree(ddata); | ||
123 | } | ||
124 | |||
125 | static int panel_dvi_enable(struct omap_dss_device *dssdev) | ||
126 | { | ||
127 | struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); | ||
128 | int r; | ||
129 | |||
130 | mutex_lock(&ddata->lock); | ||
131 | |||
132 | r = panel_dvi_power_on(dssdev); | ||
133 | if (r == 0) | ||
134 | dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; | ||
135 | |||
136 | mutex_unlock(&ddata->lock); | ||
137 | |||
138 | return r; | ||
139 | } | ||
140 | |||
141 | static void panel_dvi_disable(struct omap_dss_device *dssdev) | ||
142 | { | ||
143 | struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); | ||
144 | |||
145 | mutex_lock(&ddata->lock); | ||
146 | |||
147 | panel_dvi_power_off(dssdev); | ||
148 | |||
149 | dssdev->state = OMAP_DSS_DISPLAY_DISABLED; | ||
150 | |||
151 | mutex_unlock(&ddata->lock); | ||
152 | } | ||
153 | |||
154 | static int panel_dvi_suspend(struct omap_dss_device *dssdev) | ||
155 | { | ||
156 | struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); | ||
157 | |||
158 | mutex_lock(&ddata->lock); | ||
159 | |||
160 | panel_dvi_power_off(dssdev); | ||
161 | |||
162 | dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; | ||
163 | |||
164 | mutex_unlock(&ddata->lock); | ||
165 | |||
166 | return 0; | ||
167 | } | ||
168 | |||
169 | static int panel_dvi_resume(struct omap_dss_device *dssdev) | ||
170 | { | ||
171 | struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); | ||
172 | int r; | ||
173 | |||
174 | mutex_lock(&ddata->lock); | ||
175 | |||
176 | r = panel_dvi_power_on(dssdev); | ||
177 | if (r == 0) | ||
178 | dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; | ||
179 | |||
180 | mutex_unlock(&ddata->lock); | ||
181 | |||
182 | return r; | ||
183 | } | ||
184 | |||
185 | static void panel_dvi_set_timings(struct omap_dss_device *dssdev, | ||
186 | struct omap_video_timings *timings) | ||
187 | { | ||
188 | struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); | ||
189 | |||
190 | mutex_lock(&ddata->lock); | ||
191 | dpi_set_timings(dssdev, timings); | ||
192 | mutex_unlock(&ddata->lock); | ||
193 | } | ||
194 | |||
195 | static void panel_dvi_get_timings(struct omap_dss_device *dssdev, | ||
196 | struct omap_video_timings *timings) | ||
197 | { | ||
198 | struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); | ||
199 | |||
200 | mutex_lock(&ddata->lock); | ||
201 | *timings = dssdev->panel.timings; | ||
202 | mutex_unlock(&ddata->lock); | ||
203 | } | ||
204 | |||
205 | static int panel_dvi_check_timings(struct omap_dss_device *dssdev, | ||
206 | struct omap_video_timings *timings) | ||
207 | { | ||
208 | struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); | ||
209 | int r; | ||
210 | |||
211 | mutex_lock(&ddata->lock); | ||
212 | r = dpi_check_timings(dssdev, timings); | ||
213 | mutex_unlock(&ddata->lock); | ||
214 | |||
215 | return r; | ||
216 | } | ||
217 | |||
218 | |||
219 | static int panel_dvi_ddc_read(struct i2c_adapter *adapter, | ||
220 | unsigned char *buf, u16 count, u8 offset) | ||
221 | { | ||
222 | int r, retries; | ||
223 | |||
224 | for (retries = 3; retries > 0; retries--) { | ||
225 | struct i2c_msg msgs[] = { | ||
226 | { | ||
227 | .addr = DDC_ADDR, | ||
228 | .flags = 0, | ||
229 | .len = 1, | ||
230 | .buf = &offset, | ||
231 | }, { | ||
232 | .addr = DDC_ADDR, | ||
233 | .flags = I2C_M_RD, | ||
234 | .len = count, | ||
235 | .buf = buf, | ||
236 | } | ||
237 | }; | ||
238 | |||
239 | r = i2c_transfer(adapter, msgs, 2); | ||
240 | if (r == 2) | ||
241 | return 0; | ||
242 | |||
243 | if (r != -EAGAIN) | ||
244 | break; | ||
245 | } | ||
246 | |||
247 | return r < 0 ? r : -EIO; | ||
248 | } | ||
249 | |||
250 | static int panel_dvi_read_edid(struct omap_dss_device *dssdev, | ||
251 | u8 *edid, int len) | ||
252 | { | ||
253 | struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); | ||
254 | struct panel_dvi_platform_data *pdata = get_pdata(dssdev); | ||
255 | struct i2c_adapter *adapter; | ||
256 | int r, l, bytes_read; | ||
257 | |||
258 | mutex_lock(&ddata->lock); | ||
259 | |||
260 | if (pdata->i2c_bus_num == 0) { | ||
261 | r = -ENODEV; | ||
262 | goto err; | ||
263 | } | ||
264 | |||
265 | adapter = i2c_get_adapter(pdata->i2c_bus_num); | ||
266 | if (!adapter) { | ||
267 | dev_err(&dssdev->dev, "Failed to get I2C adapter, bus %d\n", | ||
268 | pdata->i2c_bus_num); | ||
269 | r = -EINVAL; | ||
270 | goto err; | ||
271 | } | ||
272 | |||
273 | l = min(EDID_LENGTH, len); | ||
274 | r = panel_dvi_ddc_read(adapter, edid, l, 0); | ||
275 | if (r) | ||
276 | goto err; | ||
277 | |||
278 | bytes_read = l; | ||
279 | |||
280 | /* if there are extensions, read second block */ | ||
281 | if (len > EDID_LENGTH && edid[0x7e] > 0) { | ||
282 | l = min(EDID_LENGTH, len - EDID_LENGTH); | ||
283 | |||
284 | r = panel_dvi_ddc_read(adapter, edid + EDID_LENGTH, | ||
285 | l, EDID_LENGTH); | ||
286 | if (r) | ||
287 | goto err; | ||
288 | |||
289 | bytes_read += l; | ||
290 | } | ||
291 | |||
292 | mutex_unlock(&ddata->lock); | ||
293 | |||
294 | return bytes_read; | ||
295 | |||
296 | err: | ||
297 | mutex_unlock(&ddata->lock); | ||
298 | return r; | ||
299 | } | ||
300 | |||
301 | static bool panel_dvi_detect(struct omap_dss_device *dssdev) | ||
302 | { | ||
303 | struct panel_drv_data *ddata = dev_get_drvdata(&dssdev->dev); | ||
304 | struct panel_dvi_platform_data *pdata = get_pdata(dssdev); | ||
305 | struct i2c_adapter *adapter; | ||
306 | unsigned char out; | ||
307 | int r; | ||
308 | |||
309 | mutex_lock(&ddata->lock); | ||
310 | |||
311 | if (pdata->i2c_bus_num == 0) | ||
312 | goto out; | ||
313 | |||
314 | adapter = i2c_get_adapter(pdata->i2c_bus_num); | ||
315 | if (!adapter) | ||
316 | goto out; | ||
317 | |||
318 | r = panel_dvi_ddc_read(adapter, &out, 1, 0); | ||
319 | |||
320 | mutex_unlock(&ddata->lock); | ||
321 | |||
322 | return r == 0; | ||
323 | |||
324 | out: | ||
325 | mutex_unlock(&ddata->lock); | ||
326 | return true; | ||
327 | } | ||
328 | |||
329 | static struct omap_dss_driver panel_dvi_driver = { | ||
330 | .probe = panel_dvi_probe, | ||
331 | .remove = __exit_p(panel_dvi_remove), | ||
332 | |||
333 | .enable = panel_dvi_enable, | ||
334 | .disable = panel_dvi_disable, | ||
335 | .suspend = panel_dvi_suspend, | ||
336 | .resume = panel_dvi_resume, | ||
337 | |||
338 | .set_timings = panel_dvi_set_timings, | ||
339 | .get_timings = panel_dvi_get_timings, | ||
340 | .check_timings = panel_dvi_check_timings, | ||
341 | |||
342 | .read_edid = panel_dvi_read_edid, | ||
343 | .detect = panel_dvi_detect, | ||
344 | |||
345 | .driver = { | ||
346 | .name = "dvi", | ||
347 | .owner = THIS_MODULE, | ||
348 | }, | ||
349 | }; | ||
350 | |||
351 | static int __init panel_dvi_init(void) | ||
352 | { | ||
353 | return omap_dss_register_driver(&panel_dvi_driver); | ||
354 | } | ||
355 | |||
356 | static void __exit panel_dvi_exit(void) | ||
357 | { | ||
358 | omap_dss_unregister_driver(&panel_dvi_driver); | ||
359 | } | ||
360 | |||
361 | module_init(panel_dvi_init); | ||
362 | module_exit(panel_dvi_exit); | ||
363 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/video/omap2/displays/panel-generic-dpi.c b/drivers/video/omap2/displays/panel-generic-dpi.c index 9c90f75653fb..519c47d2057f 100644 --- a/drivers/video/omap2/displays/panel-generic-dpi.c +++ b/drivers/video/omap2/displays/panel-generic-dpi.c | |||
@@ -58,30 +58,6 @@ struct panel_config { | |||
58 | 58 | ||
59 | /* Panel configurations */ | 59 | /* Panel configurations */ |
60 | static struct panel_config generic_dpi_panels[] = { | 60 | static struct panel_config generic_dpi_panels[] = { |
61 | /* Generic Panel */ | ||
62 | { | ||
63 | { | ||
64 | .x_res = 640, | ||
65 | .y_res = 480, | ||
66 | |||
67 | .pixel_clock = 23500, | ||
68 | |||
69 | .hfp = 48, | ||
70 | .hsw = 32, | ||
71 | .hbp = 80, | ||
72 | |||
73 | .vfp = 3, | ||
74 | .vsw = 4, | ||
75 | .vbp = 7, | ||
76 | }, | ||
77 | .acbi = 0x0, | ||
78 | .acb = 0x0, | ||
79 | .config = OMAP_DSS_LCD_TFT, | ||
80 | .power_on_delay = 0, | ||
81 | .power_off_delay = 0, | ||
82 | .name = "generic", | ||
83 | }, | ||
84 | |||
85 | /* Sharp LQ043T1DG01 */ | 61 | /* Sharp LQ043T1DG01 */ |
86 | { | 62 | { |
87 | { | 63 | { |
@@ -232,6 +208,95 @@ static struct panel_config generic_dpi_panels[] = { | |||
232 | .power_off_delay = 0, | 208 | .power_off_delay = 0, |
233 | .name = "powertip_ph480272t", | 209 | .name = "powertip_ph480272t", |
234 | }, | 210 | }, |
211 | |||
212 | /* Innolux AT070TN83 */ | ||
213 | { | ||
214 | { | ||
215 | .x_res = 800, | ||
216 | .y_res = 480, | ||
217 | |||
218 | .pixel_clock = 40000, | ||
219 | |||
220 | .hsw = 48, | ||
221 | .hfp = 1, | ||
222 | .hbp = 1, | ||
223 | |||
224 | .vsw = 3, | ||
225 | .vfp = 12, | ||
226 | .vbp = 25, | ||
227 | }, | ||
228 | .acbi = 0x0, | ||
229 | .acb = 0x28, | ||
230 | .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | | ||
231 | OMAP_DSS_LCD_IHS, | ||
232 | .power_on_delay = 0, | ||
233 | .power_off_delay = 0, | ||
234 | .name = "innolux_at070tn83", | ||
235 | }, | ||
236 | |||
237 | /* NEC NL2432DR22-11B */ | ||
238 | { | ||
239 | { | ||
240 | .x_res = 240, | ||
241 | .y_res = 320, | ||
242 | |||
243 | .pixel_clock = 5400, | ||
244 | |||
245 | .hsw = 3, | ||
246 | .hfp = 3, | ||
247 | .hbp = 39, | ||
248 | |||
249 | .vsw = 1, | ||
250 | .vfp = 2, | ||
251 | .vbp = 7, | ||
252 | }, | ||
253 | .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | | ||
254 | OMAP_DSS_LCD_IHS, | ||
255 | .name = "nec_nl2432dr22-11b", | ||
256 | }, | ||
257 | |||
258 | /* Unknown panel used in OMAP H4 */ | ||
259 | { | ||
260 | { | ||
261 | .x_res = 240, | ||
262 | .y_res = 320, | ||
263 | |||
264 | .pixel_clock = 6250, | ||
265 | |||
266 | .hsw = 15, | ||
267 | .hfp = 15, | ||
268 | .hbp = 60, | ||
269 | |||
270 | .vsw = 1, | ||
271 | .vfp = 1, | ||
272 | .vbp = 1, | ||
273 | }, | ||
274 | .config = OMAP_DSS_LCD_TFT, | ||
275 | |||
276 | .name = "h4", | ||
277 | }, | ||
278 | |||
279 | /* Unknown panel used in Samsung OMAP2 Apollon */ | ||
280 | { | ||
281 | { | ||
282 | .x_res = 480, | ||
283 | .y_res = 272, | ||
284 | |||
285 | .pixel_clock = 6250, | ||
286 | |||
287 | .hsw = 41, | ||
288 | .hfp = 2, | ||
289 | .hbp = 2, | ||
290 | |||
291 | .vsw = 10, | ||
292 | .vfp = 2, | ||
293 | .vbp = 2, | ||
294 | }, | ||
295 | .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | | ||
296 | OMAP_DSS_LCD_IHS, | ||
297 | |||
298 | .name = "apollon", | ||
299 | }, | ||
235 | }; | 300 | }; |
236 | 301 | ||
237 | struct panel_drv_data { | 302 | struct panel_drv_data { |
diff --git a/drivers/video/omap2/displays/panel-n8x0.c b/drivers/video/omap2/displays/panel-n8x0.c new file mode 100644 index 000000000000..150e8bae35a1 --- /dev/null +++ b/drivers/video/omap2/displays/panel-n8x0.c | |||
@@ -0,0 +1,747 @@ | |||
1 | /* #define DEBUG */ | ||
2 | |||
3 | #include <linux/module.h> | ||
4 | #include <linux/delay.h> | ||
5 | #include <linux/slab.h> | ||
6 | #include <linux/gpio.h> | ||
7 | #include <linux/spi/spi.h> | ||
8 | #include <linux/backlight.h> | ||
9 | #include <linux/fb.h> | ||
10 | |||
11 | #include <video/omapdss.h> | ||
12 | #include <video/omap-panel-n8x0.h> | ||
13 | |||
14 | #define BLIZZARD_REV_CODE 0x00 | ||
15 | #define BLIZZARD_CONFIG 0x02 | ||
16 | #define BLIZZARD_PLL_DIV 0x04 | ||
17 | #define BLIZZARD_PLL_LOCK_RANGE 0x06 | ||
18 | #define BLIZZARD_PLL_CLOCK_SYNTH_0 0x08 | ||
19 | #define BLIZZARD_PLL_CLOCK_SYNTH_1 0x0a | ||
20 | #define BLIZZARD_PLL_MODE 0x0c | ||
21 | #define BLIZZARD_CLK_SRC 0x0e | ||
22 | #define BLIZZARD_MEM_BANK0_ACTIVATE 0x10 | ||
23 | #define BLIZZARD_MEM_BANK0_STATUS 0x14 | ||
24 | #define BLIZZARD_PANEL_CONFIGURATION 0x28 | ||
25 | #define BLIZZARD_HDISP 0x2a | ||
26 | #define BLIZZARD_HNDP 0x2c | ||
27 | #define BLIZZARD_VDISP0 0x2e | ||
28 | #define BLIZZARD_VDISP1 0x30 | ||
29 | #define BLIZZARD_VNDP 0x32 | ||
30 | #define BLIZZARD_HSW 0x34 | ||
31 | #define BLIZZARD_VSW 0x38 | ||
32 | #define BLIZZARD_DISPLAY_MODE 0x68 | ||
33 | #define BLIZZARD_INPUT_WIN_X_START_0 0x6c | ||
34 | #define BLIZZARD_DATA_SOURCE_SELECT 0x8e | ||
35 | #define BLIZZARD_DISP_MEM_DATA_PORT 0x90 | ||
36 | #define BLIZZARD_DISP_MEM_READ_ADDR0 0x92 | ||
37 | #define BLIZZARD_POWER_SAVE 0xE6 | ||
38 | #define BLIZZARD_NDISP_CTRL_STATUS 0xE8 | ||
39 | |||
40 | /* Data source select */ | ||
41 | /* For S1D13745 */ | ||
42 | #define BLIZZARD_SRC_WRITE_LCD_BACKGROUND 0x00 | ||
43 | #define BLIZZARD_SRC_WRITE_LCD_DESTRUCTIVE 0x01 | ||
44 | #define BLIZZARD_SRC_WRITE_OVERLAY_ENABLE 0x04 | ||
45 | #define BLIZZARD_SRC_DISABLE_OVERLAY 0x05 | ||
46 | /* For S1D13744 */ | ||
47 | #define BLIZZARD_SRC_WRITE_LCD 0x00 | ||
48 | #define BLIZZARD_SRC_BLT_LCD 0x06 | ||
49 | |||
50 | #define BLIZZARD_COLOR_RGB565 0x01 | ||
51 | #define BLIZZARD_COLOR_YUV420 0x09 | ||
52 | |||
53 | #define BLIZZARD_VERSION_S1D13745 0x01 /* Hailstorm */ | ||
54 | #define BLIZZARD_VERSION_S1D13744 0x02 /* Blizzard */ | ||
55 | |||
56 | #define MIPID_CMD_READ_DISP_ID 0x04 | ||
57 | #define MIPID_CMD_READ_RED 0x06 | ||
58 | #define MIPID_CMD_READ_GREEN 0x07 | ||
59 | #define MIPID_CMD_READ_BLUE 0x08 | ||
60 | #define MIPID_CMD_READ_DISP_STATUS 0x09 | ||
61 | #define MIPID_CMD_RDDSDR 0x0F | ||
62 | #define MIPID_CMD_SLEEP_IN 0x10 | ||
63 | #define MIPID_CMD_SLEEP_OUT 0x11 | ||
64 | #define MIPID_CMD_DISP_OFF 0x28 | ||
65 | #define MIPID_CMD_DISP_ON 0x29 | ||
66 | |||
67 | static struct panel_drv_data { | ||
68 | struct mutex lock; | ||
69 | |||
70 | struct omap_dss_device *dssdev; | ||
71 | struct spi_device *spidev; | ||
72 | struct backlight_device *bldev; | ||
73 | |||
74 | int blizzard_ver; | ||
75 | } s_drv_data; | ||
76 | |||
77 | |||
78 | static inline | ||
79 | struct panel_n8x0_data *get_board_data(const struct omap_dss_device *dssdev) | ||
80 | { | ||
81 | return dssdev->data; | ||
82 | } | ||
83 | |||
84 | static inline | ||
85 | struct panel_drv_data *get_drv_data(const struct omap_dss_device *dssdev) | ||
86 | { | ||
87 | return &s_drv_data; | ||
88 | } | ||
89 | |||
90 | |||
91 | static inline void blizzard_cmd(u8 cmd) | ||
92 | { | ||
93 | omap_rfbi_write_command(&cmd, 1); | ||
94 | } | ||
95 | |||
96 | static inline void blizzard_write(u8 cmd, const u8 *buf, int len) | ||
97 | { | ||
98 | omap_rfbi_write_command(&cmd, 1); | ||
99 | omap_rfbi_write_data(buf, len); | ||
100 | } | ||
101 | |||
102 | static inline void blizzard_read(u8 cmd, u8 *buf, int len) | ||
103 | { | ||
104 | omap_rfbi_write_command(&cmd, 1); | ||
105 | omap_rfbi_read_data(buf, len); | ||
106 | } | ||
107 | |||
108 | static u8 blizzard_read_reg(u8 cmd) | ||
109 | { | ||
110 | u8 data; | ||
111 | blizzard_read(cmd, &data, 1); | ||
112 | return data; | ||
113 | } | ||
114 | |||
115 | static void blizzard_ctrl_setup_update(struct omap_dss_device *dssdev, | ||
116 | int x, int y, int w, int h) | ||
117 | { | ||
118 | struct panel_drv_data *ddata = get_drv_data(dssdev); | ||
119 | u8 tmp[18]; | ||
120 | int x_end, y_end; | ||
121 | |||
122 | x_end = x + w - 1; | ||
123 | y_end = y + h - 1; | ||
124 | |||
125 | tmp[0] = x; | ||
126 | tmp[1] = x >> 8; | ||
127 | tmp[2] = y; | ||
128 | tmp[3] = y >> 8; | ||
129 | tmp[4] = x_end; | ||
130 | tmp[5] = x_end >> 8; | ||
131 | tmp[6] = y_end; | ||
132 | tmp[7] = y_end >> 8; | ||
133 | |||
134 | /* scaling? */ | ||
135 | tmp[8] = x; | ||
136 | tmp[9] = x >> 8; | ||
137 | tmp[10] = y; | ||
138 | tmp[11] = y >> 8; | ||
139 | tmp[12] = x_end; | ||
140 | tmp[13] = x_end >> 8; | ||
141 | tmp[14] = y_end; | ||
142 | tmp[15] = y_end >> 8; | ||
143 | |||
144 | tmp[16] = BLIZZARD_COLOR_RGB565; | ||
145 | |||
146 | if (ddata->blizzard_ver == BLIZZARD_VERSION_S1D13745) | ||
147 | tmp[17] = BLIZZARD_SRC_WRITE_LCD_BACKGROUND; | ||
148 | else | ||
149 | tmp[17] = ddata->blizzard_ver == BLIZZARD_VERSION_S1D13744 ? | ||
150 | BLIZZARD_SRC_WRITE_LCD : | ||
151 | BLIZZARD_SRC_WRITE_LCD_DESTRUCTIVE; | ||
152 | |||
153 | omap_rfbi_configure(dssdev, 16, 8); | ||
154 | |||
155 | blizzard_write(BLIZZARD_INPUT_WIN_X_START_0, tmp, 18); | ||
156 | |||
157 | omap_rfbi_configure(dssdev, 16, 16); | ||
158 | } | ||
159 | |||
160 | static void mipid_transfer(struct spi_device *spi, int cmd, const u8 *wbuf, | ||
161 | int wlen, u8 *rbuf, int rlen) | ||
162 | { | ||
163 | struct spi_message m; | ||
164 | struct spi_transfer *x, xfer[4]; | ||
165 | u16 w; | ||
166 | int r; | ||
167 | |||
168 | spi_message_init(&m); | ||
169 | |||
170 | memset(xfer, 0, sizeof(xfer)); | ||
171 | x = &xfer[0]; | ||
172 | |||
173 | cmd &= 0xff; | ||
174 | x->tx_buf = &cmd; | ||
175 | x->bits_per_word = 9; | ||
176 | x->len = 2; | ||
177 | spi_message_add_tail(x, &m); | ||
178 | |||
179 | if (wlen) { | ||
180 | x++; | ||
181 | x->tx_buf = wbuf; | ||
182 | x->len = wlen; | ||
183 | x->bits_per_word = 9; | ||
184 | spi_message_add_tail(x, &m); | ||
185 | } | ||
186 | |||
187 | if (rlen) { | ||
188 | x++; | ||
189 | x->rx_buf = &w; | ||
190 | x->len = 1; | ||
191 | spi_message_add_tail(x, &m); | ||
192 | |||
193 | if (rlen > 1) { | ||
194 | /* Arrange for the extra clock before the first | ||
195 | * data bit. | ||
196 | */ | ||
197 | x->bits_per_word = 9; | ||
198 | x->len = 2; | ||
199 | |||
200 | x++; | ||
201 | x->rx_buf = &rbuf[1]; | ||
202 | x->len = rlen - 1; | ||
203 | spi_message_add_tail(x, &m); | ||
204 | } | ||
205 | } | ||
206 | |||
207 | r = spi_sync(spi, &m); | ||
208 | if (r < 0) | ||
209 | dev_dbg(&spi->dev, "spi_sync %d\n", r); | ||
210 | |||
211 | if (rlen) | ||
212 | rbuf[0] = w & 0xff; | ||
213 | } | ||
214 | |||
215 | static inline void mipid_cmd(struct spi_device *spi, int cmd) | ||
216 | { | ||
217 | mipid_transfer(spi, cmd, NULL, 0, NULL, 0); | ||
218 | } | ||
219 | |||
220 | static inline void mipid_write(struct spi_device *spi, | ||
221 | int reg, const u8 *buf, int len) | ||
222 | { | ||
223 | mipid_transfer(spi, reg, buf, len, NULL, 0); | ||
224 | } | ||
225 | |||
226 | static inline void mipid_read(struct spi_device *spi, | ||
227 | int reg, u8 *buf, int len) | ||
228 | { | ||
229 | mipid_transfer(spi, reg, NULL, 0, buf, len); | ||
230 | } | ||
231 | |||
232 | static void set_data_lines(struct spi_device *spi, int data_lines) | ||
233 | { | ||
234 | u16 par; | ||
235 | |||
236 | switch (data_lines) { | ||
237 | case 16: | ||
238 | par = 0x150; | ||
239 | break; | ||
240 | case 18: | ||
241 | par = 0x160; | ||
242 | break; | ||
243 | case 24: | ||
244 | par = 0x170; | ||
245 | break; | ||
246 | } | ||
247 | |||
248 | mipid_write(spi, 0x3a, (u8 *)&par, 2); | ||
249 | } | ||
250 | |||
251 | static void send_init_string(struct spi_device *spi) | ||
252 | { | ||
253 | u16 initpar[] = { 0x0102, 0x0100, 0x0100 }; | ||
254 | mipid_write(spi, 0xc2, (u8 *)initpar, sizeof(initpar)); | ||
255 | } | ||
256 | |||
257 | static void send_display_on(struct spi_device *spi) | ||
258 | { | ||
259 | mipid_cmd(spi, MIPID_CMD_DISP_ON); | ||
260 | } | ||
261 | |||
262 | static void send_display_off(struct spi_device *spi) | ||
263 | { | ||
264 | mipid_cmd(spi, MIPID_CMD_DISP_OFF); | ||
265 | } | ||
266 | |||
267 | static void send_sleep_out(struct spi_device *spi) | ||
268 | { | ||
269 | mipid_cmd(spi, MIPID_CMD_SLEEP_OUT); | ||
270 | msleep(120); | ||
271 | } | ||
272 | |||
273 | static void send_sleep_in(struct spi_device *spi) | ||
274 | { | ||
275 | mipid_cmd(spi, MIPID_CMD_SLEEP_IN); | ||
276 | msleep(50); | ||
277 | } | ||
278 | |||
279 | static int n8x0_panel_power_on(struct omap_dss_device *dssdev) | ||
280 | { | ||
281 | int r; | ||
282 | struct panel_n8x0_data *bdata = get_board_data(dssdev); | ||
283 | struct panel_drv_data *ddata = get_drv_data(dssdev); | ||
284 | struct spi_device *spi = ddata->spidev; | ||
285 | u8 rev, conf; | ||
286 | u8 display_id[3]; | ||
287 | const char *panel_name; | ||
288 | |||
289 | if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) | ||
290 | return 0; | ||
291 | |||
292 | gpio_direction_output(bdata->ctrl_pwrdown, 1); | ||
293 | |||
294 | if (bdata->platform_enable) { | ||
295 | r = bdata->platform_enable(dssdev); | ||
296 | if (r) | ||
297 | goto err_plat_en; | ||
298 | } | ||
299 | |||
300 | r = omapdss_rfbi_display_enable(dssdev); | ||
301 | if (r) | ||
302 | goto err_rfbi_en; | ||
303 | |||
304 | rev = blizzard_read_reg(BLIZZARD_REV_CODE); | ||
305 | conf = blizzard_read_reg(BLIZZARD_CONFIG); | ||
306 | |||
307 | switch (rev & 0xfc) { | ||
308 | case 0x9c: | ||
309 | ddata->blizzard_ver = BLIZZARD_VERSION_S1D13744; | ||
310 | dev_info(&dssdev->dev, "s1d13744 LCD controller rev %d " | ||
311 | "initialized (CNF pins %x)\n", rev & 0x03, conf & 0x07); | ||
312 | break; | ||
313 | case 0xa4: | ||
314 | ddata->blizzard_ver = BLIZZARD_VERSION_S1D13745; | ||
315 | dev_info(&dssdev->dev, "s1d13745 LCD controller rev %d " | ||
316 | "initialized (CNF pins %x)\n", rev & 0x03, conf & 0x07); | ||
317 | break; | ||
318 | default: | ||
319 | dev_err(&dssdev->dev, "invalid s1d1374x revision %02x\n", rev); | ||
320 | r = -ENODEV; | ||
321 | goto err_inv_chip; | ||
322 | } | ||
323 | |||
324 | /* panel */ | ||
325 | |||
326 | gpio_direction_output(bdata->panel_reset, 1); | ||
327 | |||
328 | mipid_read(spi, MIPID_CMD_READ_DISP_ID, display_id, 3); | ||
329 | dev_dbg(&spi->dev, "MIPI display ID: %02x%02x%02x\n", | ||
330 | display_id[0], display_id[1], display_id[2]); | ||
331 | |||
332 | switch (display_id[0]) { | ||
333 | case 0x45: | ||
334 | panel_name = "lph8923"; | ||
335 | break; | ||
336 | case 0x83: | ||
337 | panel_name = "ls041y3"; | ||
338 | break; | ||
339 | default: | ||
340 | dev_err(&dssdev->dev, "invalid display ID 0x%x\n", | ||
341 | display_id[0]); | ||
342 | r = -ENODEV; | ||
343 | goto err_inv_panel; | ||
344 | } | ||
345 | |||
346 | dev_info(&dssdev->dev, "%s rev %02x LCD detected\n", | ||
347 | panel_name, display_id[1]); | ||
348 | |||
349 | send_sleep_out(spi); | ||
350 | send_init_string(spi); | ||
351 | set_data_lines(spi, 24); | ||
352 | send_display_on(spi); | ||
353 | |||
354 | return 0; | ||
355 | |||
356 | err_inv_panel: | ||
357 | /* | ||
358 | * HACK: we should turn off the panel here, but there is some problem | ||
359 | * with the initialization sequence, and we fail to init the panel if we | ||
360 | * have turned it off | ||
361 | */ | ||
362 | /* gpio_direction_output(bdata->panel_reset, 0); */ | ||
363 | err_inv_chip: | ||
364 | omapdss_rfbi_display_disable(dssdev); | ||
365 | err_rfbi_en: | ||
366 | if (bdata->platform_disable) | ||
367 | bdata->platform_disable(dssdev); | ||
368 | err_plat_en: | ||
369 | gpio_direction_output(bdata->ctrl_pwrdown, 0); | ||
370 | return r; | ||
371 | } | ||
372 | |||
373 | static void n8x0_panel_power_off(struct omap_dss_device *dssdev) | ||
374 | { | ||
375 | struct panel_n8x0_data *bdata = get_board_data(dssdev); | ||
376 | struct panel_drv_data *ddata = get_drv_data(dssdev); | ||
377 | struct spi_device *spi = ddata->spidev; | ||
378 | |||
379 | if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) | ||
380 | return; | ||
381 | |||
382 | send_display_off(spi); | ||
383 | send_sleep_in(spi); | ||
384 | |||
385 | if (bdata->platform_disable) | ||
386 | bdata->platform_disable(dssdev); | ||
387 | |||
388 | /* | ||
389 | * HACK: we should turn off the panel here, but there is some problem | ||
390 | * with the initialization sequence, and we fail to init the panel if we | ||
391 | * have turned it off | ||
392 | */ | ||
393 | /* gpio_direction_output(bdata->panel_reset, 0); */ | ||
394 | gpio_direction_output(bdata->ctrl_pwrdown, 0); | ||
395 | omapdss_rfbi_display_disable(dssdev); | ||
396 | } | ||
397 | |||
398 | static const struct rfbi_timings n8x0_panel_timings = { | ||
399 | .cs_on_time = 0, | ||
400 | |||
401 | .we_on_time = 9000, | ||
402 | .we_off_time = 18000, | ||
403 | .we_cycle_time = 36000, | ||
404 | |||
405 | .re_on_time = 9000, | ||
406 | .re_off_time = 27000, | ||
407 | .re_cycle_time = 36000, | ||
408 | |||
409 | .access_time = 27000, | ||
410 | .cs_off_time = 36000, | ||
411 | |||
412 | .cs_pulse_width = 0, | ||
413 | }; | ||
414 | |||
415 | static int n8x0_bl_update_status(struct backlight_device *dev) | ||
416 | { | ||
417 | struct omap_dss_device *dssdev = dev_get_drvdata(&dev->dev); | ||
418 | struct panel_n8x0_data *bdata = get_board_data(dssdev); | ||
419 | struct panel_drv_data *ddata = get_drv_data(dssdev); | ||
420 | int r; | ||
421 | int level; | ||
422 | |||
423 | mutex_lock(&ddata->lock); | ||
424 | |||
425 | if (dev->props.fb_blank == FB_BLANK_UNBLANK && | ||
426 | dev->props.power == FB_BLANK_UNBLANK) | ||
427 | level = dev->props.brightness; | ||
428 | else | ||
429 | level = 0; | ||
430 | |||
431 | dev_dbg(&dssdev->dev, "update brightness to %d\n", level); | ||
432 | |||
433 | if (!bdata->set_backlight) | ||
434 | r = -EINVAL; | ||
435 | else | ||
436 | r = bdata->set_backlight(dssdev, level); | ||
437 | |||
438 | mutex_unlock(&ddata->lock); | ||
439 | |||
440 | return r; | ||
441 | } | ||
442 | |||
443 | static int n8x0_bl_get_intensity(struct backlight_device *dev) | ||
444 | { | ||
445 | if (dev->props.fb_blank == FB_BLANK_UNBLANK && | ||
446 | dev->props.power == FB_BLANK_UNBLANK) | ||
447 | return dev->props.brightness; | ||
448 | |||
449 | return 0; | ||
450 | } | ||
451 | |||
452 | static const struct backlight_ops n8x0_bl_ops = { | ||
453 | .get_brightness = n8x0_bl_get_intensity, | ||
454 | .update_status = n8x0_bl_update_status, | ||
455 | }; | ||
456 | |||
457 | static int n8x0_panel_probe(struct omap_dss_device *dssdev) | ||
458 | { | ||
459 | struct panel_n8x0_data *bdata = get_board_data(dssdev); | ||
460 | struct panel_drv_data *ddata; | ||
461 | struct backlight_device *bldev; | ||
462 | struct backlight_properties props; | ||
463 | int r; | ||
464 | |||
465 | dev_dbg(&dssdev->dev, "probe\n"); | ||
466 | |||
467 | if (!bdata) | ||
468 | return -EINVAL; | ||
469 | |||
470 | s_drv_data.dssdev = dssdev; | ||
471 | |||
472 | ddata = &s_drv_data; | ||
473 | |||
474 | mutex_init(&ddata->lock); | ||
475 | |||
476 | dssdev->panel.config = OMAP_DSS_LCD_TFT; | ||
477 | dssdev->panel.timings.x_res = 800; | ||
478 | dssdev->panel.timings.y_res = 480; | ||
479 | dssdev->ctrl.pixel_size = 16; | ||
480 | dssdev->ctrl.rfbi_timings = n8x0_panel_timings; | ||
481 | |||
482 | memset(&props, 0, sizeof(props)); | ||
483 | props.max_brightness = 127; | ||
484 | props.type = BACKLIGHT_PLATFORM; | ||
485 | bldev = backlight_device_register(dev_name(&dssdev->dev), &dssdev->dev, | ||
486 | dssdev, &n8x0_bl_ops, &props); | ||
487 | if (IS_ERR(bldev)) { | ||
488 | r = PTR_ERR(bldev); | ||
489 | dev_err(&dssdev->dev, "register backlight failed\n"); | ||
490 | return r; | ||
491 | } | ||
492 | |||
493 | ddata->bldev = bldev; | ||
494 | |||
495 | bldev->props.fb_blank = FB_BLANK_UNBLANK; | ||
496 | bldev->props.power = FB_BLANK_UNBLANK; | ||
497 | bldev->props.brightness = 127; | ||
498 | |||
499 | n8x0_bl_update_status(bldev); | ||
500 | |||
501 | return 0; | ||
502 | } | ||
503 | |||
504 | static void n8x0_panel_remove(struct omap_dss_device *dssdev) | ||
505 | { | ||
506 | struct panel_drv_data *ddata = get_drv_data(dssdev); | ||
507 | struct backlight_device *bldev; | ||
508 | |||
509 | dev_dbg(&dssdev->dev, "remove\n"); | ||
510 | |||
511 | bldev = ddata->bldev; | ||
512 | bldev->props.power = FB_BLANK_POWERDOWN; | ||
513 | n8x0_bl_update_status(bldev); | ||
514 | backlight_device_unregister(bldev); | ||
515 | |||
516 | dev_set_drvdata(&dssdev->dev, NULL); | ||
517 | } | ||
518 | |||
519 | static int n8x0_panel_enable(struct omap_dss_device *dssdev) | ||
520 | { | ||
521 | struct panel_drv_data *ddata = get_drv_data(dssdev); | ||
522 | int r; | ||
523 | |||
524 | dev_dbg(&dssdev->dev, "enable\n"); | ||
525 | |||
526 | mutex_lock(&ddata->lock); | ||
527 | |||
528 | rfbi_bus_lock(); | ||
529 | |||
530 | r = n8x0_panel_power_on(dssdev); | ||
531 | |||
532 | rfbi_bus_unlock(); | ||
533 | |||
534 | if (r) { | ||
535 | mutex_unlock(&ddata->lock); | ||
536 | return r; | ||
537 | } | ||
538 | |||
539 | dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; | ||
540 | |||
541 | mutex_unlock(&ddata->lock); | ||
542 | |||
543 | return 0; | ||
544 | } | ||
545 | |||
546 | static void n8x0_panel_disable(struct omap_dss_device *dssdev) | ||
547 | { | ||
548 | struct panel_drv_data *ddata = get_drv_data(dssdev); | ||
549 | |||
550 | dev_dbg(&dssdev->dev, "disable\n"); | ||
551 | |||
552 | mutex_lock(&ddata->lock); | ||
553 | |||
554 | rfbi_bus_lock(); | ||
555 | |||
556 | n8x0_panel_power_off(dssdev); | ||
557 | |||
558 | rfbi_bus_unlock(); | ||
559 | |||
560 | dssdev->state = OMAP_DSS_DISPLAY_DISABLED; | ||
561 | |||
562 | mutex_unlock(&ddata->lock); | ||
563 | } | ||
564 | |||
565 | static int n8x0_panel_suspend(struct omap_dss_device *dssdev) | ||
566 | { | ||
567 | struct panel_drv_data *ddata = get_drv_data(dssdev); | ||
568 | |||
569 | dev_dbg(&dssdev->dev, "suspend\n"); | ||
570 | |||
571 | mutex_lock(&ddata->lock); | ||
572 | |||
573 | rfbi_bus_lock(); | ||
574 | |||
575 | n8x0_panel_power_off(dssdev); | ||
576 | |||
577 | rfbi_bus_unlock(); | ||
578 | |||
579 | dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; | ||
580 | |||
581 | mutex_unlock(&ddata->lock); | ||
582 | |||
583 | return 0; | ||
584 | } | ||
585 | |||
586 | static int n8x0_panel_resume(struct omap_dss_device *dssdev) | ||
587 | { | ||
588 | struct panel_drv_data *ddata = get_drv_data(dssdev); | ||
589 | int r; | ||
590 | |||
591 | dev_dbg(&dssdev->dev, "resume\n"); | ||
592 | |||
593 | mutex_lock(&ddata->lock); | ||
594 | |||
595 | rfbi_bus_lock(); | ||
596 | |||
597 | r = n8x0_panel_power_on(dssdev); | ||
598 | |||
599 | rfbi_bus_unlock(); | ||
600 | |||
601 | if (r) { | ||
602 | mutex_unlock(&ddata->lock); | ||
603 | return r; | ||
604 | } | ||
605 | |||
606 | dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; | ||
607 | |||
608 | mutex_unlock(&ddata->lock); | ||
609 | |||
610 | return 0; | ||
611 | } | ||
612 | |||
613 | static void n8x0_panel_get_timings(struct omap_dss_device *dssdev, | ||
614 | struct omap_video_timings *timings) | ||
615 | { | ||
616 | *timings = dssdev->panel.timings; | ||
617 | } | ||
618 | |||
619 | static void n8x0_panel_get_resolution(struct omap_dss_device *dssdev, | ||
620 | u16 *xres, u16 *yres) | ||
621 | { | ||
622 | *xres = dssdev->panel.timings.x_res; | ||
623 | *yres = dssdev->panel.timings.y_res; | ||
624 | } | ||
625 | |||
626 | static void update_done(void *data) | ||
627 | { | ||
628 | rfbi_bus_unlock(); | ||
629 | } | ||
630 | |||
631 | static int n8x0_panel_update(struct omap_dss_device *dssdev, | ||
632 | u16 x, u16 y, u16 w, u16 h) | ||
633 | { | ||
634 | struct panel_drv_data *ddata = get_drv_data(dssdev); | ||
635 | |||
636 | dev_dbg(&dssdev->dev, "update\n"); | ||
637 | |||
638 | mutex_lock(&ddata->lock); | ||
639 | rfbi_bus_lock(); | ||
640 | |||
641 | omap_rfbi_prepare_update(dssdev, &x, &y, &w, &h); | ||
642 | |||
643 | blizzard_ctrl_setup_update(dssdev, x, y, w, h); | ||
644 | |||
645 | omap_rfbi_update(dssdev, x, y, w, h, update_done, NULL); | ||
646 | |||
647 | mutex_unlock(&ddata->lock); | ||
648 | |||
649 | return 0; | ||
650 | } | ||
651 | |||
652 | static int n8x0_panel_sync(struct omap_dss_device *dssdev) | ||
653 | { | ||
654 | struct panel_drv_data *ddata = get_drv_data(dssdev); | ||
655 | |||
656 | dev_dbg(&dssdev->dev, "sync\n"); | ||
657 | |||
658 | mutex_lock(&ddata->lock); | ||
659 | rfbi_bus_lock(); | ||
660 | rfbi_bus_unlock(); | ||
661 | mutex_unlock(&ddata->lock); | ||
662 | |||
663 | return 0; | ||
664 | } | ||
665 | |||
666 | static struct omap_dss_driver n8x0_panel_driver = { | ||
667 | .probe = n8x0_panel_probe, | ||
668 | .remove = n8x0_panel_remove, | ||
669 | |||
670 | .enable = n8x0_panel_enable, | ||
671 | .disable = n8x0_panel_disable, | ||
672 | .suspend = n8x0_panel_suspend, | ||
673 | .resume = n8x0_panel_resume, | ||
674 | |||
675 | .update = n8x0_panel_update, | ||
676 | .sync = n8x0_panel_sync, | ||
677 | |||
678 | .get_resolution = n8x0_panel_get_resolution, | ||
679 | .get_recommended_bpp = omapdss_default_get_recommended_bpp, | ||
680 | |||
681 | .get_timings = n8x0_panel_get_timings, | ||
682 | |||
683 | .driver = { | ||
684 | .name = "n8x0_panel", | ||
685 | .owner = THIS_MODULE, | ||
686 | }, | ||
687 | }; | ||
688 | |||
689 | /* PANEL */ | ||
690 | |||
691 | static int mipid_spi_probe(struct spi_device *spi) | ||
692 | { | ||
693 | dev_dbg(&spi->dev, "mipid_spi_probe\n"); | ||
694 | |||
695 | spi->mode = SPI_MODE_0; | ||
696 | |||
697 | s_drv_data.spidev = spi; | ||
698 | |||
699 | return 0; | ||
700 | } | ||
701 | |||
702 | static int mipid_spi_remove(struct spi_device *spi) | ||
703 | { | ||
704 | dev_dbg(&spi->dev, "mipid_spi_remove\n"); | ||
705 | return 0; | ||
706 | } | ||
707 | |||
708 | static struct spi_driver mipid_spi_driver = { | ||
709 | .driver = { | ||
710 | .name = "lcd_mipid", | ||
711 | .bus = &spi_bus_type, | ||
712 | .owner = THIS_MODULE, | ||
713 | }, | ||
714 | .probe = mipid_spi_probe, | ||
715 | .remove = __devexit_p(mipid_spi_remove), | ||
716 | }; | ||
717 | |||
718 | static int __init n8x0_panel_drv_init(void) | ||
719 | { | ||
720 | int r; | ||
721 | |||
722 | r = spi_register_driver(&mipid_spi_driver); | ||
723 | if (r) { | ||
724 | pr_err("n8x0_panel: spi driver registration failed\n"); | ||
725 | return r; | ||
726 | } | ||
727 | |||
728 | r = omap_dss_register_driver(&n8x0_panel_driver); | ||
729 | if (r) { | ||
730 | pr_err("n8x0_panel: dss driver registration failed\n"); | ||
731 | spi_unregister_driver(&mipid_spi_driver); | ||
732 | return r; | ||
733 | } | ||
734 | |||
735 | return 0; | ||
736 | } | ||
737 | |||
738 | static void __exit n8x0_panel_drv_exit(void) | ||
739 | { | ||
740 | spi_unregister_driver(&mipid_spi_driver); | ||
741 | |||
742 | omap_dss_unregister_driver(&n8x0_panel_driver); | ||
743 | } | ||
744 | |||
745 | module_init(n8x0_panel_drv_init); | ||
746 | module_exit(n8x0_panel_drv_exit); | ||
747 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/video/omap2/displays/panel-picodlp.c b/drivers/video/omap2/displays/panel-picodlp.c new file mode 100644 index 000000000000..98ebdaddab5a --- /dev/null +++ b/drivers/video/omap2/displays/panel-picodlp.c | |||
@@ -0,0 +1,594 @@ | |||
1 | /* | ||
2 | * picodlp panel driver | ||
3 | * picodlp_i2c_driver: i2c_client driver | ||
4 | * | ||
5 | * Copyright (C) 2009-2011 Texas Instruments | ||
6 | * Author: Mythri P K <mythripk@ti.com> | ||
7 | * Mayuresh Janorkar <mayur@ti.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify it | ||
10 | * under the terms of the GNU General Public License version 2 as published by | ||
11 | * the Free Software Foundation. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
15 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
16 | * more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License along with | ||
19 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
20 | */ | ||
21 | |||
22 | #include <linux/module.h> | ||
23 | #include <linux/input.h> | ||
24 | #include <linux/platform_device.h> | ||
25 | #include <linux/interrupt.h> | ||
26 | #include <linux/firmware.h> | ||
27 | #include <linux/slab.h> | ||
28 | #include <linux/mutex.h> | ||
29 | #include <linux/i2c.h> | ||
30 | #include <linux/delay.h> | ||
31 | #include <linux/gpio.h> | ||
32 | |||
33 | #include <video/omapdss.h> | ||
34 | #include <video/omap-panel-picodlp.h> | ||
35 | |||
36 | #include "panel-picodlp.h" | ||
37 | |||
38 | struct picodlp_data { | ||
39 | struct mutex lock; | ||
40 | struct i2c_client *picodlp_i2c_client; | ||
41 | }; | ||
42 | |||
43 | static struct i2c_board_info picodlp_i2c_board_info = { | ||
44 | I2C_BOARD_INFO("picodlp_i2c_driver", 0x1b), | ||
45 | }; | ||
46 | |||
47 | struct picodlp_i2c_data { | ||
48 | struct mutex xfer_lock; | ||
49 | }; | ||
50 | |||
51 | static struct i2c_device_id picodlp_i2c_id[] = { | ||
52 | { "picodlp_i2c_driver", 0 }, | ||
53 | }; | ||
54 | |||
55 | struct picodlp_i2c_command { | ||
56 | u8 reg; | ||
57 | u32 value; | ||
58 | }; | ||
59 | |||
60 | static struct omap_video_timings pico_ls_timings = { | ||
61 | .x_res = 864, | ||
62 | .y_res = 480, | ||
63 | .hsw = 7, | ||
64 | .hfp = 11, | ||
65 | .hbp = 7, | ||
66 | |||
67 | .pixel_clock = 19200, | ||
68 | |||
69 | .vsw = 2, | ||
70 | .vfp = 3, | ||
71 | .vbp = 14, | ||
72 | }; | ||
73 | |||
74 | static inline struct picodlp_panel_data | ||
75 | *get_panel_data(const struct omap_dss_device *dssdev) | ||
76 | { | ||
77 | return (struct picodlp_panel_data *) dssdev->data; | ||
78 | } | ||
79 | |||
80 | static u32 picodlp_i2c_read(struct i2c_client *client, u8 reg) | ||
81 | { | ||
82 | u8 read_cmd[] = {READ_REG_SELECT, reg}, data[4]; | ||
83 | struct picodlp_i2c_data *picodlp_i2c_data = i2c_get_clientdata(client); | ||
84 | struct i2c_msg msg[2]; | ||
85 | |||
86 | mutex_lock(&picodlp_i2c_data->xfer_lock); | ||
87 | |||
88 | msg[0].addr = client->addr; | ||
89 | msg[0].flags = 0; | ||
90 | msg[0].len = 2; | ||
91 | msg[0].buf = read_cmd; | ||
92 | |||
93 | msg[1].addr = client->addr; | ||
94 | msg[1].flags = I2C_M_RD; | ||
95 | msg[1].len = 4; | ||
96 | msg[1].buf = data; | ||
97 | |||
98 | i2c_transfer(client->adapter, msg, 2); | ||
99 | mutex_unlock(&picodlp_i2c_data->xfer_lock); | ||
100 | return (data[3] | (data[2] << 8) | (data[1] << 16) | (data[0] << 24)); | ||
101 | } | ||
102 | |||
103 | static int picodlp_i2c_write_block(struct i2c_client *client, | ||
104 | u8 *data, int len) | ||
105 | { | ||
106 | struct i2c_msg msg; | ||
107 | int i, r, msg_count = 1; | ||
108 | |||
109 | struct picodlp_i2c_data *picodlp_i2c_data = i2c_get_clientdata(client); | ||
110 | |||
111 | if (len < 1 || len > 32) { | ||
112 | dev_err(&client->dev, | ||
113 | "too long syn_write_block len %d\n", len); | ||
114 | return -EIO; | ||
115 | } | ||
116 | mutex_lock(&picodlp_i2c_data->xfer_lock); | ||
117 | |||
118 | msg.addr = client->addr; | ||
119 | msg.flags = 0; | ||
120 | msg.len = len; | ||
121 | msg.buf = data; | ||
122 | r = i2c_transfer(client->adapter, &msg, msg_count); | ||
123 | mutex_unlock(&picodlp_i2c_data->xfer_lock); | ||
124 | |||
125 | /* | ||
126 | * i2c_transfer returns: | ||
127 | * number of messages sent in case of success | ||
128 | * a negative error number in case of failure | ||
129 | */ | ||
130 | if (r != msg_count) | ||
131 | goto err; | ||
132 | |||
133 | /* In case of success */ | ||
134 | for (i = 0; i < len; i++) | ||
135 | dev_dbg(&client->dev, | ||
136 | "addr %x bw 0x%02x[%d]: 0x%02x\n", | ||
137 | client->addr, data[0] + i, i, data[i]); | ||
138 | |||
139 | return 0; | ||
140 | err: | ||
141 | dev_err(&client->dev, "picodlp_i2c_write error\n"); | ||
142 | return r; | ||
143 | } | ||
144 | |||
145 | static int picodlp_i2c_write(struct i2c_client *client, u8 reg, u32 value) | ||
146 | { | ||
147 | u8 data[5]; | ||
148 | int i; | ||
149 | |||
150 | data[0] = reg; | ||
151 | for (i = 1; i < 5; i++) | ||
152 | data[i] = (value >> (32 - (i) * 8)) & 0xFF; | ||
153 | |||
154 | return picodlp_i2c_write_block(client, data, 5); | ||
155 | } | ||
156 | |||
157 | static int picodlp_i2c_write_array(struct i2c_client *client, | ||
158 | const struct picodlp_i2c_command commands[], | ||
159 | int count) | ||
160 | { | ||
161 | int i, r = 0; | ||
162 | for (i = 0; i < count; i++) { | ||
163 | r = picodlp_i2c_write(client, commands[i].reg, | ||
164 | commands[i].value); | ||
165 | if (r) | ||
166 | return r; | ||
167 | } | ||
168 | return r; | ||
169 | } | ||
170 | |||
171 | static int picodlp_wait_for_dma_done(struct i2c_client *client) | ||
172 | { | ||
173 | u8 trial = 100; | ||
174 | |||
175 | do { | ||
176 | msleep(1); | ||
177 | if (!trial--) | ||
178 | return -ETIMEDOUT; | ||
179 | } while (picodlp_i2c_read(client, MAIN_STATUS) & DMA_STATUS); | ||
180 | |||
181 | return 0; | ||
182 | } | ||
183 | |||
184 | /** | ||
185 | * picodlp_i2c_init: i2c_initialization routine | ||
186 | * client: i2c_client for communication | ||
187 | * | ||
188 | * return | ||
189 | * 0 : Success, no error | ||
190 | * error code : Failure | ||
191 | */ | ||
192 | static int picodlp_i2c_init(struct i2c_client *client) | ||
193 | { | ||
194 | int r; | ||
195 | static const struct picodlp_i2c_command init_cmd_set1[] = { | ||
196 | {SOFT_RESET, 1}, | ||
197 | {DMD_PARK_TRIGGER, 1}, | ||
198 | {MISC_REG, 5}, | ||
199 | {SEQ_CONTROL, 0}, | ||
200 | {SEQ_VECTOR, 0x100}, | ||
201 | {DMD_BLOCK_COUNT, 7}, | ||
202 | {DMD_VCC_CONTROL, 0x109}, | ||
203 | {DMD_PARK_PULSE_COUNT, 0xA}, | ||
204 | {DMD_PARK_PULSE_WIDTH, 0xB}, | ||
205 | {DMD_PARK_DELAY, 0x2ED}, | ||
206 | {DMD_SHADOW_ENABLE, 0}, | ||
207 | {FLASH_OPCODE, 0xB}, | ||
208 | {FLASH_DUMMY_BYTES, 1}, | ||
209 | {FLASH_ADDR_BYTES, 3}, | ||
210 | {PBC_CONTROL, 0}, | ||
211 | {FLASH_START_ADDR, CMT_LUT_0_START_ADDR}, | ||
212 | {FLASH_READ_BYTES, CMT_LUT_0_SIZE}, | ||
213 | {CMT_SPLASH_LUT_START_ADDR, 0}, | ||
214 | {CMT_SPLASH_LUT_DEST_SELECT, CMT_LUT_ALL}, | ||
215 | {PBC_CONTROL, 1}, | ||
216 | }; | ||
217 | |||
218 | static const struct picodlp_i2c_command init_cmd_set2[] = { | ||
219 | {PBC_CONTROL, 0}, | ||
220 | {CMT_SPLASH_LUT_DEST_SELECT, 0}, | ||
221 | {PBC_CONTROL, 0}, | ||
222 | {FLASH_START_ADDR, SEQUENCE_0_START_ADDR}, | ||
223 | {FLASH_READ_BYTES, SEQUENCE_0_SIZE}, | ||
224 | {SEQ_RESET_LUT_START_ADDR, 0}, | ||
225 | {SEQ_RESET_LUT_DEST_SELECT, SEQ_SEQ_LUT}, | ||
226 | {PBC_CONTROL, 1}, | ||
227 | }; | ||
228 | |||
229 | static const struct picodlp_i2c_command init_cmd_set3[] = { | ||
230 | {PBC_CONTROL, 0}, | ||
231 | {SEQ_RESET_LUT_DEST_SELECT, 0}, | ||
232 | {PBC_CONTROL, 0}, | ||
233 | {FLASH_START_ADDR, DRC_TABLE_0_START_ADDR}, | ||
234 | {FLASH_READ_BYTES, DRC_TABLE_0_SIZE}, | ||
235 | {SEQ_RESET_LUT_START_ADDR, 0}, | ||
236 | {SEQ_RESET_LUT_DEST_SELECT, SEQ_DRC_LUT_ALL}, | ||
237 | {PBC_CONTROL, 1}, | ||
238 | }; | ||
239 | |||
240 | static const struct picodlp_i2c_command init_cmd_set4[] = { | ||
241 | {PBC_CONTROL, 0}, | ||
242 | {SEQ_RESET_LUT_DEST_SELECT, 0}, | ||
243 | {SDC_ENABLE, 1}, | ||
244 | {AGC_CTRL, 7}, | ||
245 | {CCA_C1A, 0x100}, | ||
246 | {CCA_C1B, 0x0}, | ||
247 | {CCA_C1C, 0x0}, | ||
248 | {CCA_C2A, 0x0}, | ||
249 | {CCA_C2B, 0x100}, | ||
250 | {CCA_C2C, 0x0}, | ||
251 | {CCA_C3A, 0x0}, | ||
252 | {CCA_C3B, 0x0}, | ||
253 | {CCA_C3C, 0x100}, | ||
254 | {CCA_C7A, 0x100}, | ||
255 | {CCA_C7B, 0x100}, | ||
256 | {CCA_C7C, 0x100}, | ||
257 | {CCA_ENABLE, 1}, | ||
258 | {CPU_IF_MODE, 1}, | ||
259 | {SHORT_FLIP, 1}, | ||
260 | {CURTAIN_CONTROL, 0}, | ||
261 | {DMD_PARK_TRIGGER, 0}, | ||
262 | {R_DRIVE_CURRENT, 0x298}, | ||
263 | {G_DRIVE_CURRENT, 0x298}, | ||
264 | {B_DRIVE_CURRENT, 0x298}, | ||
265 | {RGB_DRIVER_ENABLE, 7}, | ||
266 | {SEQ_CONTROL, 0}, | ||
267 | {ACTGEN_CONTROL, 0x10}, | ||
268 | {SEQUENCE_MODE, SEQ_LOCK}, | ||
269 | {DATA_FORMAT, RGB888}, | ||
270 | {INPUT_RESOLUTION, WVGA_864_LANDSCAPE}, | ||
271 | {INPUT_SOURCE, PARALLEL_RGB}, | ||
272 | {CPU_IF_SYNC_METHOD, 1}, | ||
273 | {SEQ_CONTROL, 1} | ||
274 | }; | ||
275 | |||
276 | r = picodlp_i2c_write_array(client, init_cmd_set1, | ||
277 | ARRAY_SIZE(init_cmd_set1)); | ||
278 | if (r) | ||
279 | return r; | ||
280 | |||
281 | r = picodlp_wait_for_dma_done(client); | ||
282 | if (r) | ||
283 | return r; | ||
284 | |||
285 | r = picodlp_i2c_write_array(client, init_cmd_set2, | ||
286 | ARRAY_SIZE(init_cmd_set2)); | ||
287 | if (r) | ||
288 | return r; | ||
289 | |||
290 | r = picodlp_wait_for_dma_done(client); | ||
291 | if (r) | ||
292 | return r; | ||
293 | |||
294 | r = picodlp_i2c_write_array(client, init_cmd_set3, | ||
295 | ARRAY_SIZE(init_cmd_set3)); | ||
296 | if (r) | ||
297 | return r; | ||
298 | |||
299 | r = picodlp_wait_for_dma_done(client); | ||
300 | if (r) | ||
301 | return r; | ||
302 | |||
303 | r = picodlp_i2c_write_array(client, init_cmd_set4, | ||
304 | ARRAY_SIZE(init_cmd_set4)); | ||
305 | if (r) | ||
306 | return r; | ||
307 | |||
308 | return 0; | ||
309 | } | ||
310 | |||
311 | static int picodlp_i2c_probe(struct i2c_client *client, | ||
312 | const struct i2c_device_id *id) | ||
313 | { | ||
314 | struct picodlp_i2c_data *picodlp_i2c_data; | ||
315 | |||
316 | picodlp_i2c_data = kzalloc(sizeof(struct picodlp_i2c_data), GFP_KERNEL); | ||
317 | |||
318 | if (!picodlp_i2c_data) | ||
319 | return -ENOMEM; | ||
320 | |||
321 | mutex_init(&picodlp_i2c_data->xfer_lock); | ||
322 | i2c_set_clientdata(client, picodlp_i2c_data); | ||
323 | |||
324 | return 0; | ||
325 | } | ||
326 | |||
327 | static int picodlp_i2c_remove(struct i2c_client *client) | ||
328 | { | ||
329 | struct picodlp_i2c_data *picodlp_i2c_data = | ||
330 | i2c_get_clientdata(client); | ||
331 | kfree(picodlp_i2c_data); | ||
332 | return 0; | ||
333 | } | ||
334 | |||
335 | static struct i2c_driver picodlp_i2c_driver = { | ||
336 | .driver = { | ||
337 | .name = "picodlp_i2c_driver", | ||
338 | }, | ||
339 | .probe = picodlp_i2c_probe, | ||
340 | .remove = picodlp_i2c_remove, | ||
341 | .id_table = picodlp_i2c_id, | ||
342 | }; | ||
343 | |||
344 | static int picodlp_panel_power_on(struct omap_dss_device *dssdev) | ||
345 | { | ||
346 | int r, trial = 100; | ||
347 | struct picodlp_data *picod = dev_get_drvdata(&dssdev->dev); | ||
348 | struct picodlp_panel_data *picodlp_pdata = get_panel_data(dssdev); | ||
349 | |||
350 | if (dssdev->platform_enable) { | ||
351 | r = dssdev->platform_enable(dssdev); | ||
352 | if (r) | ||
353 | return r; | ||
354 | } | ||
355 | |||
356 | gpio_set_value(picodlp_pdata->pwrgood_gpio, 0); | ||
357 | msleep(1); | ||
358 | gpio_set_value(picodlp_pdata->pwrgood_gpio, 1); | ||
359 | |||
360 | while (!gpio_get_value(picodlp_pdata->emu_done_gpio)) { | ||
361 | if (!trial--) { | ||
362 | dev_err(&dssdev->dev, "emu_done signal not" | ||
363 | " going high\n"); | ||
364 | return -ETIMEDOUT; | ||
365 | } | ||
366 | msleep(5); | ||
367 | } | ||
368 | /* | ||
369 | * As per dpp2600 programming guide, | ||
370 | * it is required to sleep for 1000ms after emu_done signal goes high | ||
371 | * then only i2c commands can be successfully sent to dpp2600 | ||
372 | */ | ||
373 | msleep(1000); | ||
374 | r = omapdss_dpi_display_enable(dssdev); | ||
375 | if (r) { | ||
376 | dev_err(&dssdev->dev, "failed to enable DPI\n"); | ||
377 | goto err1; | ||
378 | } | ||
379 | |||
380 | r = picodlp_i2c_init(picod->picodlp_i2c_client); | ||
381 | if (r) | ||
382 | goto err; | ||
383 | |||
384 | dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; | ||
385 | |||
386 | return r; | ||
387 | err: | ||
388 | omapdss_dpi_display_disable(dssdev); | ||
389 | err1: | ||
390 | if (dssdev->platform_disable) | ||
391 | dssdev->platform_disable(dssdev); | ||
392 | |||
393 | return r; | ||
394 | } | ||
395 | |||
396 | static void picodlp_panel_power_off(struct omap_dss_device *dssdev) | ||
397 | { | ||
398 | struct picodlp_panel_data *picodlp_pdata = get_panel_data(dssdev); | ||
399 | |||
400 | omapdss_dpi_display_disable(dssdev); | ||
401 | |||
402 | gpio_set_value(picodlp_pdata->emu_done_gpio, 0); | ||
403 | gpio_set_value(picodlp_pdata->pwrgood_gpio, 0); | ||
404 | |||
405 | if (dssdev->platform_disable) | ||
406 | dssdev->platform_disable(dssdev); | ||
407 | } | ||
408 | |||
409 | static int picodlp_panel_probe(struct omap_dss_device *dssdev) | ||
410 | { | ||
411 | struct picodlp_data *picod; | ||
412 | struct picodlp_panel_data *picodlp_pdata = get_panel_data(dssdev); | ||
413 | struct i2c_adapter *adapter; | ||
414 | struct i2c_client *picodlp_i2c_client; | ||
415 | int r = 0, picodlp_adapter_id; | ||
416 | |||
417 | dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_ONOFF | | ||
418 | OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IVS; | ||
419 | dssdev->panel.acb = 0x0; | ||
420 | dssdev->panel.timings = pico_ls_timings; | ||
421 | |||
422 | picod = kzalloc(sizeof(struct picodlp_data), GFP_KERNEL); | ||
423 | if (!picod) | ||
424 | return -ENOMEM; | ||
425 | |||
426 | mutex_init(&picod->lock); | ||
427 | |||
428 | picodlp_adapter_id = picodlp_pdata->picodlp_adapter_id; | ||
429 | |||
430 | adapter = i2c_get_adapter(picodlp_adapter_id); | ||
431 | if (!adapter) { | ||
432 | dev_err(&dssdev->dev, "can't get i2c adapter\n"); | ||
433 | r = -ENODEV; | ||
434 | goto err; | ||
435 | } | ||
436 | |||
437 | picodlp_i2c_client = i2c_new_device(adapter, &picodlp_i2c_board_info); | ||
438 | if (!picodlp_i2c_client) { | ||
439 | dev_err(&dssdev->dev, "can't add i2c device::" | ||
440 | " picodlp_i2c_client is NULL\n"); | ||
441 | r = -ENODEV; | ||
442 | goto err; | ||
443 | } | ||
444 | |||
445 | picod->picodlp_i2c_client = picodlp_i2c_client; | ||
446 | |||
447 | dev_set_drvdata(&dssdev->dev, picod); | ||
448 | return r; | ||
449 | err: | ||
450 | kfree(picod); | ||
451 | return r; | ||
452 | } | ||
453 | |||
454 | static void picodlp_panel_remove(struct omap_dss_device *dssdev) | ||
455 | { | ||
456 | struct picodlp_data *picod = dev_get_drvdata(&dssdev->dev); | ||
457 | |||
458 | i2c_unregister_device(picod->picodlp_i2c_client); | ||
459 | dev_set_drvdata(&dssdev->dev, NULL); | ||
460 | dev_dbg(&dssdev->dev, "removing picodlp panel\n"); | ||
461 | |||
462 | kfree(picod); | ||
463 | } | ||
464 | |||
465 | static int picodlp_panel_enable(struct omap_dss_device *dssdev) | ||
466 | { | ||
467 | struct picodlp_data *picod = dev_get_drvdata(&dssdev->dev); | ||
468 | int r; | ||
469 | |||
470 | dev_dbg(&dssdev->dev, "enabling picodlp panel\n"); | ||
471 | |||
472 | mutex_lock(&picod->lock); | ||
473 | if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) { | ||
474 | mutex_unlock(&picod->lock); | ||
475 | return -EINVAL; | ||
476 | } | ||
477 | |||
478 | r = picodlp_panel_power_on(dssdev); | ||
479 | mutex_unlock(&picod->lock); | ||
480 | |||
481 | return r; | ||
482 | } | ||
483 | |||
484 | static void picodlp_panel_disable(struct omap_dss_device *dssdev) | ||
485 | { | ||
486 | struct picodlp_data *picod = dev_get_drvdata(&dssdev->dev); | ||
487 | |||
488 | mutex_lock(&picod->lock); | ||
489 | /* Turn off DLP Power */ | ||
490 | if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) | ||
491 | picodlp_panel_power_off(dssdev); | ||
492 | |||
493 | dssdev->state = OMAP_DSS_DISPLAY_DISABLED; | ||
494 | mutex_unlock(&picod->lock); | ||
495 | |||
496 | dev_dbg(&dssdev->dev, "disabling picodlp panel\n"); | ||
497 | } | ||
498 | |||
499 | static int picodlp_panel_suspend(struct omap_dss_device *dssdev) | ||
500 | { | ||
501 | struct picodlp_data *picod = dev_get_drvdata(&dssdev->dev); | ||
502 | |||
503 | mutex_lock(&picod->lock); | ||
504 | /* Turn off DLP Power */ | ||
505 | if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { | ||
506 | mutex_unlock(&picod->lock); | ||
507 | dev_err(&dssdev->dev, "unable to suspend picodlp panel," | ||
508 | " panel is not ACTIVE\n"); | ||
509 | return -EINVAL; | ||
510 | } | ||
511 | |||
512 | picodlp_panel_power_off(dssdev); | ||
513 | |||
514 | dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; | ||
515 | mutex_unlock(&picod->lock); | ||
516 | |||
517 | dev_dbg(&dssdev->dev, "suspending picodlp panel\n"); | ||
518 | return 0; | ||
519 | } | ||
520 | |||
521 | static int picodlp_panel_resume(struct omap_dss_device *dssdev) | ||
522 | { | ||
523 | struct picodlp_data *picod = dev_get_drvdata(&dssdev->dev); | ||
524 | int r; | ||
525 | |||
526 | mutex_lock(&picod->lock); | ||
527 | if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) { | ||
528 | mutex_unlock(&picod->lock); | ||
529 | dev_err(&dssdev->dev, "unable to resume picodlp panel," | ||
530 | " panel is not ACTIVE\n"); | ||
531 | return -EINVAL; | ||
532 | } | ||
533 | |||
534 | r = picodlp_panel_power_on(dssdev); | ||
535 | mutex_unlock(&picod->lock); | ||
536 | dev_dbg(&dssdev->dev, "resuming picodlp panel\n"); | ||
537 | return r; | ||
538 | } | ||
539 | |||
540 | static void picodlp_get_resolution(struct omap_dss_device *dssdev, | ||
541 | u16 *xres, u16 *yres) | ||
542 | { | ||
543 | *xres = dssdev->panel.timings.x_res; | ||
544 | *yres = dssdev->panel.timings.y_res; | ||
545 | } | ||
546 | |||
547 | static struct omap_dss_driver picodlp_driver = { | ||
548 | .probe = picodlp_panel_probe, | ||
549 | .remove = picodlp_panel_remove, | ||
550 | |||
551 | .enable = picodlp_panel_enable, | ||
552 | .disable = picodlp_panel_disable, | ||
553 | |||
554 | .get_resolution = picodlp_get_resolution, | ||
555 | |||
556 | .suspend = picodlp_panel_suspend, | ||
557 | .resume = picodlp_panel_resume, | ||
558 | |||
559 | .driver = { | ||
560 | .name = "picodlp_panel", | ||
561 | .owner = THIS_MODULE, | ||
562 | }, | ||
563 | }; | ||
564 | |||
565 | static int __init picodlp_init(void) | ||
566 | { | ||
567 | int r = 0; | ||
568 | |||
569 | r = i2c_add_driver(&picodlp_i2c_driver); | ||
570 | if (r) { | ||
571 | printk(KERN_WARNING "picodlp_i2c_driver" \ | ||
572 | " registration failed\n"); | ||
573 | return r; | ||
574 | } | ||
575 | |||
576 | r = omap_dss_register_driver(&picodlp_driver); | ||
577 | if (r) | ||
578 | i2c_del_driver(&picodlp_i2c_driver); | ||
579 | |||
580 | return r; | ||
581 | } | ||
582 | |||
583 | static void __exit picodlp_exit(void) | ||
584 | { | ||
585 | i2c_del_driver(&picodlp_i2c_driver); | ||
586 | omap_dss_unregister_driver(&picodlp_driver); | ||
587 | } | ||
588 | |||
589 | module_init(picodlp_init); | ||
590 | module_exit(picodlp_exit); | ||
591 | |||
592 | MODULE_AUTHOR("Mythri P K <mythripk@ti.com>"); | ||
593 | MODULE_DESCRIPTION("picodlp driver"); | ||
594 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/video/omap2/displays/panel-picodlp.h b/drivers/video/omap2/displays/panel-picodlp.h new file mode 100644 index 000000000000..a34b431a7267 --- /dev/null +++ b/drivers/video/omap2/displays/panel-picodlp.h | |||
@@ -0,0 +1,288 @@ | |||
1 | /* | ||
2 | * Header file required by picodlp panel driver | ||
3 | * | ||
4 | * Copyright (C) 2009-2011 Texas Instruments | ||
5 | * Author: Mythri P K <mythripk@ti.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License version 2 as published by | ||
9 | * the Free Software Foundation. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
14 | * more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License along with | ||
17 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
18 | */ | ||
19 | |||
20 | #ifndef __OMAP2_DISPLAY_PANEL_PICODLP_H | ||
21 | #define __OMAP2_DISPLAY_PANEL_PICODLP_H | ||
22 | |||
23 | /* Commands used for configuring picodlp panel */ | ||
24 | |||
25 | #define MAIN_STATUS 0x03 | ||
26 | #define PBC_CONTROL 0x08 | ||
27 | #define INPUT_SOURCE 0x0B | ||
28 | #define INPUT_RESOLUTION 0x0C | ||
29 | #define DATA_FORMAT 0x0D | ||
30 | #define IMG_ROTATION 0x0E | ||
31 | #define LONG_FLIP 0x0F | ||
32 | #define SHORT_FLIP 0x10 | ||
33 | #define TEST_PAT_SELECT 0x11 | ||
34 | #define R_DRIVE_CURRENT 0x12 | ||
35 | #define G_DRIVE_CURRENT 0x13 | ||
36 | #define B_DRIVE_CURRENT 0x14 | ||
37 | #define READ_REG_SELECT 0x15 | ||
38 | #define RGB_DRIVER_ENABLE 0x16 | ||
39 | |||
40 | #define CPU_IF_MODE 0x18 | ||
41 | #define FRAME_RATE 0x19 | ||
42 | #define CPU_IF_SYNC_METHOD 0x1A | ||
43 | #define CPU_IF_SOF 0x1B | ||
44 | #define CPU_IF_EOF 0x1C | ||
45 | #define CPU_IF_SLEEP 0x1D | ||
46 | |||
47 | #define SEQUENCE_MODE 0x1E | ||
48 | #define SOFT_RESET 0x1F | ||
49 | #define FRONT_END_RESET 0x21 | ||
50 | #define AUTO_PWR_ENABLE 0x22 | ||
51 | |||
52 | #define VSYNC_LINE_DELAY 0x23 | ||
53 | #define CPU_PI_HORIZ_START 0x24 | ||
54 | #define CPU_PI_VERT_START 0x25 | ||
55 | #define CPU_PI_HORIZ_WIDTH 0x26 | ||
56 | #define CPU_PI_VERT_HEIGHT 0x27 | ||
57 | |||
58 | #define PIXEL_MASK_CROP 0x28 | ||
59 | #define CROP_FIRST_LINE 0x29 | ||
60 | #define CROP_LAST_LINE 0x2A | ||
61 | #define CROP_FIRST_PIXEL 0x2B | ||
62 | #define CROP_LAST_PIXEL 0x2C | ||
63 | #define DMD_PARK_TRIGGER 0x2D | ||
64 | |||
65 | #define MISC_REG 0x30 | ||
66 | |||
67 | /* AGC registers */ | ||
68 | #define AGC_CTRL 0x50 | ||
69 | #define AGC_CLIPPED_PIXS 0x55 | ||
70 | #define AGC_BRIGHT_PIXS 0x56 | ||
71 | #define AGC_BG_PIXS 0x57 | ||
72 | #define AGC_SAFETY_MARGIN 0x17 | ||
73 | |||
74 | /* Color Coordinate Adjustment registers */ | ||
75 | #define CCA_ENABLE 0x5E | ||
76 | #define CCA_C1A 0x5F | ||
77 | #define CCA_C1B 0x60 | ||
78 | #define CCA_C1C 0x61 | ||
79 | #define CCA_C2A 0x62 | ||
80 | #define CCA_C2B 0x63 | ||
81 | #define CCA_C2C 0x64 | ||
82 | #define CCA_C3A 0x65 | ||
83 | #define CCA_C3B 0x66 | ||
84 | #define CCA_C3C 0x67 | ||
85 | #define CCA_C7A 0x71 | ||
86 | #define CCA_C7B 0x72 | ||
87 | #define CCA_C7C 0x73 | ||
88 | |||
89 | /** | ||
90 | * DLP Pico Processor 2600 comes with flash | ||
91 | * We can do DMA operations from flash for accessing Look Up Tables | ||
92 | */ | ||
93 | #define DMA_STATUS 0x100 | ||
94 | #define FLASH_ADDR_BYTES 0x74 | ||
95 | #define FLASH_DUMMY_BYTES 0x75 | ||
96 | #define FLASH_WRITE_BYTES 0x76 | ||
97 | #define FLASH_READ_BYTES 0x77 | ||
98 | #define FLASH_OPCODE 0x78 | ||
99 | #define FLASH_START_ADDR 0x79 | ||
100 | #define FLASH_DUMMY2 0x7A | ||
101 | #define FLASH_WRITE_DATA 0x7B | ||
102 | |||
103 | #define TEMPORAL_DITH_DISABLE 0x7E | ||
104 | #define SEQ_CONTROL 0x82 | ||
105 | #define SEQ_VECTOR 0x83 | ||
106 | |||
107 | /* DMD is Digital Micromirror Device */ | ||
108 | #define DMD_BLOCK_COUNT 0x84 | ||
109 | #define DMD_VCC_CONTROL 0x86 | ||
110 | #define DMD_PARK_PULSE_COUNT 0x87 | ||
111 | #define DMD_PARK_PULSE_WIDTH 0x88 | ||
112 | #define DMD_PARK_DELAY 0x89 | ||
113 | #define DMD_SHADOW_ENABLE 0x8E | ||
114 | #define SEQ_STATUS 0x8F | ||
115 | #define FLASH_CLOCK_CONTROL 0x98 | ||
116 | #define DMD_PARK 0x2D | ||
117 | |||
118 | #define SDRAM_BIST_ENABLE 0x46 | ||
119 | #define DDR_DRIVER_STRENGTH 0x9A | ||
120 | #define SDC_ENABLE 0x9D | ||
121 | #define SDC_BUFF_SWAP_DISABLE 0xA3 | ||
122 | #define CURTAIN_CONTROL 0xA6 | ||
123 | #define DDR_BUS_SWAP_ENABLE 0xA7 | ||
124 | #define DMD_TRC_ENABLE 0xA8 | ||
125 | #define DMD_BUS_SWAP_ENABLE 0xA9 | ||
126 | |||
127 | #define ACTGEN_ENABLE 0xAE | ||
128 | #define ACTGEN_CONTROL 0xAF | ||
129 | #define ACTGEN_HORIZ_BP 0xB0 | ||
130 | #define ACTGEN_VERT_BP 0xB1 | ||
131 | |||
132 | /* Look Up Table access */ | ||
133 | #define CMT_SPLASH_LUT_START_ADDR 0xFA | ||
134 | #define CMT_SPLASH_LUT_DEST_SELECT 0xFB | ||
135 | #define CMT_SPLASH_LUT_DATA 0xFC | ||
136 | #define SEQ_RESET_LUT_START_ADDR 0xFD | ||
137 | #define SEQ_RESET_LUT_DEST_SELECT 0xFE | ||
138 | #define SEQ_RESET_LUT_DATA 0xFF | ||
139 | |||
140 | /* Input source definitions */ | ||
141 | #define PARALLEL_RGB 0 | ||
142 | #define INT_TEST_PATTERN 1 | ||
143 | #define SPLASH_SCREEN 2 | ||
144 | #define CPU_INTF 3 | ||
145 | #define BT656 4 | ||
146 | |||
147 | /* Standard input resolution definitions */ | ||
148 | #define QWVGA_LANDSCAPE 3 /* (427h*240v) */ | ||
149 | #define WVGA_864_LANDSCAPE 21 /* (864h*480v) */ | ||
150 | #define WVGA_DMD_OPTICAL_TEST 35 /* (608h*684v) */ | ||
151 | |||
152 | /* Standard data format definitions */ | ||
153 | #define RGB565 0 | ||
154 | #define RGB666 1 | ||
155 | #define RGB888 2 | ||
156 | |||
157 | /* Test Pattern definitions */ | ||
158 | #define TPG_CHECKERBOARD 0 | ||
159 | #define TPG_BLACK 1 | ||
160 | #define TPG_WHITE 2 | ||
161 | #define TPG_RED 3 | ||
162 | #define TPG_BLUE 4 | ||
163 | #define TPG_GREEN 5 | ||
164 | #define TPG_VLINES_BLACK 6 | ||
165 | #define TPG_HLINES_BLACK 7 | ||
166 | #define TPG_VLINES_ALT 8 | ||
167 | #define TPG_HLINES_ALT 9 | ||
168 | #define TPG_DIAG_LINES 10 | ||
169 | #define TPG_GREYRAMP_VERT 11 | ||
170 | #define TPG_GREYRAMP_HORIZ 12 | ||
171 | #define TPG_ANSI_CHECKERBOARD 13 | ||
172 | |||
173 | /* sequence mode definitions */ | ||
174 | #define SEQ_FREE_RUN 0 | ||
175 | #define SEQ_LOCK 1 | ||
176 | |||
177 | /* curtain color definitions */ | ||
178 | #define CURTAIN_BLACK 0 | ||
179 | #define CURTAIN_RED 1 | ||
180 | #define CURTAIN_GREEN 2 | ||
181 | #define CURTAIN_BLUE 3 | ||
182 | #define CURTAIN_YELLOW 4 | ||
183 | #define CURTAIN_MAGENTA 5 | ||
184 | #define CURTAIN_CYAN 6 | ||
185 | #define CURTAIN_WHITE 7 | ||
186 | |||
187 | /* LUT definitions */ | ||
188 | #define CMT_LUT_NONE 0 | ||
189 | #define CMT_LUT_GREEN 1 | ||
190 | #define CMT_LUT_RED 2 | ||
191 | #define CMT_LUT_BLUE 3 | ||
192 | #define CMT_LUT_ALL 4 | ||
193 | #define SPLASH_LUT 5 | ||
194 | |||
195 | #define SEQ_LUT_NONE 0 | ||
196 | #define SEQ_DRC_LUT_0 1 | ||
197 | #define SEQ_DRC_LUT_1 2 | ||
198 | #define SEQ_DRC_LUT_2 3 | ||
199 | #define SEQ_DRC_LUT_3 4 | ||
200 | #define SEQ_SEQ_LUT 5 | ||
201 | #define SEQ_DRC_LUT_ALL 6 | ||
202 | #define WPC_PROGRAM_LUT 7 | ||
203 | |||
204 | #define BITSTREAM_START_ADDR 0x00000000 | ||
205 | #define BITSTREAM_SIZE 0x00040000 | ||
206 | |||
207 | #define WPC_FW_0_START_ADDR 0x00040000 | ||
208 | #define WPC_FW_0_SIZE 0x00000ce8 | ||
209 | |||
210 | #define SEQUENCE_0_START_ADDR 0x00044000 | ||
211 | #define SEQUENCE_0_SIZE 0x00001000 | ||
212 | |||
213 | #define SEQUENCE_1_START_ADDR 0x00045000 | ||
214 | #define SEQUENCE_1_SIZE 0x00000d10 | ||
215 | |||
216 | #define SEQUENCE_2_START_ADDR 0x00046000 | ||
217 | #define SEQUENCE_2_SIZE 0x00000d10 | ||
218 | |||
219 | #define SEQUENCE_3_START_ADDR 0x00047000 | ||
220 | #define SEQUENCE_3_SIZE 0x00000d10 | ||
221 | |||
222 | #define SEQUENCE_4_START_ADDR 0x00048000 | ||
223 | #define SEQUENCE_4_SIZE 0x00000d10 | ||
224 | |||
225 | #define SEQUENCE_5_START_ADDR 0x00049000 | ||
226 | #define SEQUENCE_5_SIZE 0x00000d10 | ||
227 | |||
228 | #define SEQUENCE_6_START_ADDR 0x0004a000 | ||
229 | #define SEQUENCE_6_SIZE 0x00000d10 | ||
230 | |||
231 | #define CMT_LUT_0_START_ADDR 0x0004b200 | ||
232 | #define CMT_LUT_0_SIZE 0x00000600 | ||
233 | |||
234 | #define CMT_LUT_1_START_ADDR 0x0004b800 | ||
235 | #define CMT_LUT_1_SIZE 0x00000600 | ||
236 | |||
237 | #define CMT_LUT_2_START_ADDR 0x0004be00 | ||
238 | #define CMT_LUT_2_SIZE 0x00000600 | ||
239 | |||
240 | #define CMT_LUT_3_START_ADDR 0x0004c400 | ||
241 | #define CMT_LUT_3_SIZE 0x00000600 | ||
242 | |||
243 | #define CMT_LUT_4_START_ADDR 0x0004ca00 | ||
244 | #define CMT_LUT_4_SIZE 0x00000600 | ||
245 | |||
246 | #define CMT_LUT_5_START_ADDR 0x0004d000 | ||
247 | #define CMT_LUT_5_SIZE 0x00000600 | ||
248 | |||
249 | #define CMT_LUT_6_START_ADDR 0x0004d600 | ||
250 | #define CMT_LUT_6_SIZE 0x00000600 | ||
251 | |||
252 | #define DRC_TABLE_0_START_ADDR 0x0004dc00 | ||
253 | #define DRC_TABLE_0_SIZE 0x00000100 | ||
254 | |||
255 | #define SPLASH_0_START_ADDR 0x0004dd00 | ||
256 | #define SPLASH_0_SIZE 0x00032280 | ||
257 | |||
258 | #define SEQUENCE_7_START_ADDR 0x00080000 | ||
259 | #define SEQUENCE_7_SIZE 0x00000d10 | ||
260 | |||
261 | #define SEQUENCE_8_START_ADDR 0x00081800 | ||
262 | #define SEQUENCE_8_SIZE 0x00000d10 | ||
263 | |||
264 | #define SEQUENCE_9_START_ADDR 0x00083000 | ||
265 | #define SEQUENCE_9_SIZE 0x00000d10 | ||
266 | |||
267 | #define CMT_LUT_7_START_ADDR 0x0008e000 | ||
268 | #define CMT_LUT_7_SIZE 0x00000600 | ||
269 | |||
270 | #define CMT_LUT_8_START_ADDR 0x0008e800 | ||
271 | #define CMT_LUT_8_SIZE 0x00000600 | ||
272 | |||
273 | #define CMT_LUT_9_START_ADDR 0x0008f000 | ||
274 | #define CMT_LUT_9_SIZE 0x00000600 | ||
275 | |||
276 | #define SPLASH_1_START_ADDR 0x0009a000 | ||
277 | #define SPLASH_1_SIZE 0x00032280 | ||
278 | |||
279 | #define SPLASH_2_START_ADDR 0x000cd000 | ||
280 | #define SPLASH_2_SIZE 0x00032280 | ||
281 | |||
282 | #define SPLASH_3_START_ADDR 0x00100000 | ||
283 | #define SPLASH_3_SIZE 0x00032280 | ||
284 | |||
285 | #define OPT_SPLASH_0_START_ADDR 0x00134000 | ||
286 | #define OPT_SPLASH_0_SIZE 0x000cb100 | ||
287 | |||
288 | #endif | ||
diff --git a/drivers/video/omap2/displays/panel-taal.c b/drivers/video/omap2/displays/panel-taal.c index ca00843ed2fe..80c3f6ab1a94 100644 --- a/drivers/video/omap2/displays/panel-taal.c +++ b/drivers/video/omap2/displays/panel-taal.c | |||
@@ -35,26 +35,12 @@ | |||
35 | 35 | ||
36 | #include <video/omapdss.h> | 36 | #include <video/omapdss.h> |
37 | #include <video/omap-panel-nokia-dsi.h> | 37 | #include <video/omap-panel-nokia-dsi.h> |
38 | #include <video/mipi_display.h> | ||
38 | 39 | ||
39 | /* DSI Virtual channel. Hardcoded for now. */ | 40 | /* DSI Virtual channel. Hardcoded for now. */ |
40 | #define TCH 0 | 41 | #define TCH 0 |
41 | 42 | ||
42 | #define DCS_READ_NUM_ERRORS 0x05 | 43 | #define DCS_READ_NUM_ERRORS 0x05 |
43 | #define DCS_READ_POWER_MODE 0x0a | ||
44 | #define DCS_READ_MADCTL 0x0b | ||
45 | #define DCS_READ_PIXEL_FORMAT 0x0c | ||
46 | #define DCS_RDDSDR 0x0f | ||
47 | #define DCS_SLEEP_IN 0x10 | ||
48 | #define DCS_SLEEP_OUT 0x11 | ||
49 | #define DCS_DISPLAY_OFF 0x28 | ||
50 | #define DCS_DISPLAY_ON 0x29 | ||
51 | #define DCS_COLUMN_ADDR 0x2a | ||
52 | #define DCS_PAGE_ADDR 0x2b | ||
53 | #define DCS_MEMORY_WRITE 0x2c | ||
54 | #define DCS_TEAR_OFF 0x34 | ||
55 | #define DCS_TEAR_ON 0x35 | ||
56 | #define DCS_MEM_ACC_CTRL 0x36 | ||
57 | #define DCS_PIXEL_FORMAT 0x3a | ||
58 | #define DCS_BRIGHTNESS 0x51 | 44 | #define DCS_BRIGHTNESS 0x51 |
59 | #define DCS_CTRL_DISPLAY 0x53 | 45 | #define DCS_CTRL_DISPLAY 0x53 |
60 | #define DCS_WRITE_CABC 0x55 | 46 | #define DCS_WRITE_CABC 0x55 |
@@ -222,8 +208,6 @@ struct taal_data { | |||
222 | 208 | ||
223 | struct delayed_work te_timeout_work; | 209 | struct delayed_work te_timeout_work; |
224 | 210 | ||
225 | bool use_dsi_bl; | ||
226 | |||
227 | bool cabc_broken; | 211 | bool cabc_broken; |
228 | unsigned cabc_mode; | 212 | unsigned cabc_mode; |
229 | 213 | ||
@@ -302,7 +286,7 @@ static int taal_sleep_in(struct taal_data *td) | |||
302 | 286 | ||
303 | hw_guard_wait(td); | 287 | hw_guard_wait(td); |
304 | 288 | ||
305 | cmd = DCS_SLEEP_IN; | 289 | cmd = MIPI_DCS_ENTER_SLEEP_MODE; |
306 | r = dsi_vc_dcs_write_nosync(td->dssdev, td->channel, &cmd, 1); | 290 | r = dsi_vc_dcs_write_nosync(td->dssdev, td->channel, &cmd, 1); |
307 | if (r) | 291 | if (r) |
308 | return r; | 292 | return r; |
@@ -321,7 +305,7 @@ static int taal_sleep_out(struct taal_data *td) | |||
321 | 305 | ||
322 | hw_guard_wait(td); | 306 | hw_guard_wait(td); |
323 | 307 | ||
324 | r = taal_dcs_write_0(td, DCS_SLEEP_OUT); | 308 | r = taal_dcs_write_0(td, MIPI_DCS_EXIT_SLEEP_MODE); |
325 | if (r) | 309 | if (r) |
326 | return r; | 310 | return r; |
327 | 311 | ||
@@ -356,7 +340,7 @@ static int taal_set_addr_mode(struct taal_data *td, u8 rotate, bool mirror) | |||
356 | u8 mode; | 340 | u8 mode; |
357 | int b5, b6, b7; | 341 | int b5, b6, b7; |
358 | 342 | ||
359 | r = taal_dcs_read_1(td, DCS_READ_MADCTL, &mode); | 343 | r = taal_dcs_read_1(td, MIPI_DCS_GET_ADDRESS_MODE, &mode); |
360 | if (r) | 344 | if (r) |
361 | return r; | 345 | return r; |
362 | 346 | ||
@@ -390,7 +374,7 @@ static int taal_set_addr_mode(struct taal_data *td, u8 rotate, bool mirror) | |||
390 | mode &= ~((1<<7) | (1<<6) | (1<<5)); | 374 | mode &= ~((1<<7) | (1<<6) | (1<<5)); |
391 | mode |= (b7 << 7) | (b6 << 6) | (b5 << 5); | 375 | mode |= (b7 << 7) | (b6 << 6) | (b5 << 5); |
392 | 376 | ||
393 | return taal_dcs_write_1(td, DCS_MEM_ACC_CTRL, mode); | 377 | return taal_dcs_write_1(td, MIPI_DCS_SET_ADDRESS_MODE, mode); |
394 | } | 378 | } |
395 | 379 | ||
396 | static int taal_set_update_window(struct taal_data *td, | 380 | static int taal_set_update_window(struct taal_data *td, |
@@ -403,7 +387,7 @@ static int taal_set_update_window(struct taal_data *td, | |||
403 | u16 y2 = y + h - 1; | 387 | u16 y2 = y + h - 1; |
404 | 388 | ||
405 | u8 buf[5]; | 389 | u8 buf[5]; |
406 | buf[0] = DCS_COLUMN_ADDR; | 390 | buf[0] = MIPI_DCS_SET_COLUMN_ADDRESS; |
407 | buf[1] = (x1 >> 8) & 0xff; | 391 | buf[1] = (x1 >> 8) & 0xff; |
408 | buf[2] = (x1 >> 0) & 0xff; | 392 | buf[2] = (x1 >> 0) & 0xff; |
409 | buf[3] = (x2 >> 8) & 0xff; | 393 | buf[3] = (x2 >> 8) & 0xff; |
@@ -413,7 +397,7 @@ static int taal_set_update_window(struct taal_data *td, | |||
413 | if (r) | 397 | if (r) |
414 | return r; | 398 | return r; |
415 | 399 | ||
416 | buf[0] = DCS_PAGE_ADDR; | 400 | buf[0] = MIPI_DCS_SET_PAGE_ADDRESS; |
417 | buf[1] = (y1 >> 8) & 0xff; | 401 | buf[1] = (y1 >> 8) & 0xff; |
418 | buf[2] = (y1 >> 0) & 0xff; | 402 | buf[2] = (y1 >> 0) & 0xff; |
419 | buf[3] = (y2 >> 8) & 0xff; | 403 | buf[3] = (y2 >> 8) & 0xff; |
@@ -555,7 +539,6 @@ static int taal_bl_update_status(struct backlight_device *dev) | |||
555 | { | 539 | { |
556 | struct omap_dss_device *dssdev = dev_get_drvdata(&dev->dev); | 540 | struct omap_dss_device *dssdev = dev_get_drvdata(&dev->dev); |
557 | struct taal_data *td = dev_get_drvdata(&dssdev->dev); | 541 | struct taal_data *td = dev_get_drvdata(&dssdev->dev); |
558 | struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev); | ||
559 | int r; | 542 | int r; |
560 | int level; | 543 | int level; |
561 | 544 | ||
@@ -569,23 +552,16 @@ static int taal_bl_update_status(struct backlight_device *dev) | |||
569 | 552 | ||
570 | mutex_lock(&td->lock); | 553 | mutex_lock(&td->lock); |
571 | 554 | ||
572 | if (td->use_dsi_bl) { | 555 | if (td->enabled) { |
573 | if (td->enabled) { | 556 | dsi_bus_lock(dssdev); |
574 | dsi_bus_lock(dssdev); | ||
575 | 557 | ||
576 | r = taal_wake_up(dssdev); | 558 | r = taal_wake_up(dssdev); |
577 | if (!r) | 559 | if (!r) |
578 | r = taal_dcs_write_1(td, DCS_BRIGHTNESS, level); | 560 | r = taal_dcs_write_1(td, DCS_BRIGHTNESS, level); |
579 | 561 | ||
580 | dsi_bus_unlock(dssdev); | 562 | dsi_bus_unlock(dssdev); |
581 | } else { | ||
582 | r = 0; | ||
583 | } | ||
584 | } else { | 563 | } else { |
585 | if (!panel_data->set_backlight) | 564 | r = 0; |
586 | r = -EINVAL; | ||
587 | else | ||
588 | r = panel_data->set_backlight(dssdev, level); | ||
589 | } | 565 | } |
590 | 566 | ||
591 | mutex_unlock(&td->lock); | 567 | mutex_unlock(&td->lock); |
@@ -964,7 +940,7 @@ static int taal_probe(struct omap_dss_device *dssdev) | |||
964 | { | 940 | { |
965 | struct backlight_properties props; | 941 | struct backlight_properties props; |
966 | struct taal_data *td; | 942 | struct taal_data *td; |
967 | struct backlight_device *bldev; | 943 | struct backlight_device *bldev = NULL; |
968 | struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev); | 944 | struct nokia_dsi_panel_data *panel_data = get_panel_data(dssdev); |
969 | struct panel_config *panel_config = NULL; | 945 | struct panel_config *panel_config = NULL; |
970 | int r, i; | 946 | int r, i; |
@@ -990,7 +966,7 @@ static int taal_probe(struct omap_dss_device *dssdev) | |||
990 | 966 | ||
991 | dssdev->panel.config = OMAP_DSS_LCD_TFT; | 967 | dssdev->panel.config = OMAP_DSS_LCD_TFT; |
992 | dssdev->panel.timings = panel_config->timings; | 968 | dssdev->panel.timings = panel_config->timings; |
993 | dssdev->ctrl.pixel_size = 24; | 969 | dssdev->panel.dsi_pix_fmt = OMAP_DSS_DSI_FMT_RGB888; |
994 | 970 | ||
995 | td = kzalloc(sizeof(*td), GFP_KERNEL); | 971 | td = kzalloc(sizeof(*td), GFP_KERNEL); |
996 | if (!td) { | 972 | if (!td) { |
@@ -1025,35 +1001,26 @@ static int taal_probe(struct omap_dss_device *dssdev) | |||
1025 | 1001 | ||
1026 | taal_hw_reset(dssdev); | 1002 | taal_hw_reset(dssdev); |
1027 | 1003 | ||
1028 | /* if no platform set_backlight() defined, presume DSI backlight | 1004 | if (panel_data->use_dsi_backlight) { |
1029 | * control */ | 1005 | memset(&props, 0, sizeof(struct backlight_properties)); |
1030 | memset(&props, 0, sizeof(struct backlight_properties)); | ||
1031 | if (!panel_data->set_backlight) | ||
1032 | td->use_dsi_bl = true; | ||
1033 | |||
1034 | if (td->use_dsi_bl) | ||
1035 | props.max_brightness = 255; | 1006 | props.max_brightness = 255; |
1036 | else | ||
1037 | props.max_brightness = 127; | ||
1038 | |||
1039 | props.type = BACKLIGHT_RAW; | ||
1040 | bldev = backlight_device_register(dev_name(&dssdev->dev), &dssdev->dev, | ||
1041 | dssdev, &taal_bl_ops, &props); | ||
1042 | if (IS_ERR(bldev)) { | ||
1043 | r = PTR_ERR(bldev); | ||
1044 | goto err_bl; | ||
1045 | } | ||
1046 | 1007 | ||
1047 | td->bldev = bldev; | 1008 | props.type = BACKLIGHT_RAW; |
1009 | bldev = backlight_device_register(dev_name(&dssdev->dev), | ||
1010 | &dssdev->dev, dssdev, &taal_bl_ops, &props); | ||
1011 | if (IS_ERR(bldev)) { | ||
1012 | r = PTR_ERR(bldev); | ||
1013 | goto err_bl; | ||
1014 | } | ||
1015 | |||
1016 | td->bldev = bldev; | ||
1048 | 1017 | ||
1049 | bldev->props.fb_blank = FB_BLANK_UNBLANK; | 1018 | bldev->props.fb_blank = FB_BLANK_UNBLANK; |
1050 | bldev->props.power = FB_BLANK_UNBLANK; | 1019 | bldev->props.power = FB_BLANK_UNBLANK; |
1051 | if (td->use_dsi_bl) | ||
1052 | bldev->props.brightness = 255; | 1020 | bldev->props.brightness = 255; |
1053 | else | ||
1054 | bldev->props.brightness = 127; | ||
1055 | 1021 | ||
1056 | taal_bl_update_status(bldev); | 1022 | taal_bl_update_status(bldev); |
1023 | } | ||
1057 | 1024 | ||
1058 | if (panel_data->use_ext_te) { | 1025 | if (panel_data->use_ext_te) { |
1059 | int gpio = panel_data->ext_te_gpio; | 1026 | int gpio = panel_data->ext_te_gpio; |
@@ -1111,7 +1078,8 @@ err_irq: | |||
1111 | if (panel_data->use_ext_te) | 1078 | if (panel_data->use_ext_te) |
1112 | gpio_free(panel_data->ext_te_gpio); | 1079 | gpio_free(panel_data->ext_te_gpio); |
1113 | err_gpio: | 1080 | err_gpio: |
1114 | backlight_device_unregister(bldev); | 1081 | if (bldev != NULL) |
1082 | backlight_device_unregister(bldev); | ||
1115 | err_bl: | 1083 | err_bl: |
1116 | destroy_workqueue(td->workqueue); | 1084 | destroy_workqueue(td->workqueue); |
1117 | err_wq: | 1085 | err_wq: |
@@ -1140,9 +1108,11 @@ static void __exit taal_remove(struct omap_dss_device *dssdev) | |||
1140 | } | 1108 | } |
1141 | 1109 | ||
1142 | bldev = td->bldev; | 1110 | bldev = td->bldev; |
1143 | bldev->props.power = FB_BLANK_POWERDOWN; | 1111 | if (bldev != NULL) { |
1144 | taal_bl_update_status(bldev); | 1112 | bldev->props.power = FB_BLANK_POWERDOWN; |
1145 | backlight_device_unregister(bldev); | 1113 | taal_bl_update_status(bldev); |
1114 | backlight_device_unregister(bldev); | ||
1115 | } | ||
1146 | 1116 | ||
1147 | taal_cancel_ulps_work(dssdev); | 1117 | taal_cancel_ulps_work(dssdev); |
1148 | taal_cancel_esd_work(dssdev); | 1118 | taal_cancel_esd_work(dssdev); |
@@ -1195,7 +1165,8 @@ static int taal_power_on(struct omap_dss_device *dssdev) | |||
1195 | if (r) | 1165 | if (r) |
1196 | goto err; | 1166 | goto err; |
1197 | 1167 | ||
1198 | r = taal_dcs_write_1(td, DCS_PIXEL_FORMAT, 0x7); /* 24bit/pixel */ | 1168 | r = taal_dcs_write_1(td, MIPI_DCS_SET_PIXEL_FORMAT, |
1169 | MIPI_DCS_PIXEL_FMT_24BIT); | ||
1199 | if (r) | 1170 | if (r) |
1200 | goto err; | 1171 | goto err; |
1201 | 1172 | ||
@@ -1209,7 +1180,7 @@ static int taal_power_on(struct omap_dss_device *dssdev) | |||
1209 | goto err; | 1180 | goto err; |
1210 | } | 1181 | } |
1211 | 1182 | ||
1212 | r = taal_dcs_write_0(td, DCS_DISPLAY_ON); | 1183 | r = taal_dcs_write_0(td, MIPI_DCS_SET_DISPLAY_ON); |
1213 | if (r) | 1184 | if (r) |
1214 | goto err; | 1185 | goto err; |
1215 | 1186 | ||
@@ -1246,7 +1217,7 @@ static void taal_power_off(struct omap_dss_device *dssdev) | |||
1246 | struct taal_data *td = dev_get_drvdata(&dssdev->dev); | 1217 | struct taal_data *td = dev_get_drvdata(&dssdev->dev); |
1247 | int r; | 1218 | int r; |
1248 | 1219 | ||
1249 | r = taal_dcs_write_0(td, DCS_DISPLAY_OFF); | 1220 | r = taal_dcs_write_0(td, MIPI_DCS_SET_DISPLAY_OFF); |
1250 | if (!r) | 1221 | if (!r) |
1251 | r = taal_sleep_in(td); | 1222 | r = taal_sleep_in(td); |
1252 | 1223 | ||
@@ -1529,9 +1500,9 @@ static int _taal_enable_te(struct omap_dss_device *dssdev, bool enable) | |||
1529 | int r; | 1500 | int r; |
1530 | 1501 | ||
1531 | if (enable) | 1502 | if (enable) |
1532 | r = taal_dcs_write_1(td, DCS_TEAR_ON, 0); | 1503 | r = taal_dcs_write_1(td, MIPI_DCS_SET_TEAR_ON, 0); |
1533 | else | 1504 | else |
1534 | r = taal_dcs_write_0(td, DCS_TEAR_OFF); | 1505 | r = taal_dcs_write_0(td, MIPI_DCS_SET_TEAR_OFF); |
1535 | 1506 | ||
1536 | if (!panel_data->use_ext_te) | 1507 | if (!panel_data->use_ext_te) |
1537 | omapdss_dsi_enable_te(dssdev, enable); | 1508 | omapdss_dsi_enable_te(dssdev, enable); |
@@ -1851,7 +1822,7 @@ static void taal_esd_work(struct work_struct *work) | |||
1851 | goto err; | 1822 | goto err; |
1852 | } | 1823 | } |
1853 | 1824 | ||
1854 | r = taal_dcs_read_1(td, DCS_RDDSDR, &state1); | 1825 | r = taal_dcs_read_1(td, MIPI_DCS_GET_DIAGNOSTIC_RESULT, &state1); |
1855 | if (r) { | 1826 | if (r) { |
1856 | dev_err(&dssdev->dev, "failed to read Taal status\n"); | 1827 | dev_err(&dssdev->dev, "failed to read Taal status\n"); |
1857 | goto err; | 1828 | goto err; |
@@ -1864,7 +1835,7 @@ static void taal_esd_work(struct work_struct *work) | |||
1864 | goto err; | 1835 | goto err; |
1865 | } | 1836 | } |
1866 | 1837 | ||
1867 | r = taal_dcs_read_1(td, DCS_RDDSDR, &state2); | 1838 | r = taal_dcs_read_1(td, MIPI_DCS_GET_DIAGNOSTIC_RESULT, &state2); |
1868 | if (r) { | 1839 | if (r) { |
1869 | dev_err(&dssdev->dev, "failed to read Taal status\n"); | 1840 | dev_err(&dssdev->dev, "failed to read Taal status\n"); |
1870 | goto err; | 1841 | goto err; |
@@ -1880,7 +1851,7 @@ static void taal_esd_work(struct work_struct *work) | |||
1880 | /* Self-diagnostics result is also shown on TE GPIO line. We need | 1851 | /* Self-diagnostics result is also shown on TE GPIO line. We need |
1881 | * to re-enable TE after self diagnostics */ | 1852 | * to re-enable TE after self diagnostics */ |
1882 | if (td->te_enabled && panel_data->use_ext_te) { | 1853 | if (td->te_enabled && panel_data->use_ext_te) { |
1883 | r = taal_dcs_write_1(td, DCS_TEAR_ON, 0); | 1854 | r = taal_dcs_write_1(td, MIPI_DCS_SET_TEAR_ON, 0); |
1884 | if (r) | 1855 | if (r) |
1885 | goto err; | 1856 | goto err; |
1886 | } | 1857 | } |