aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2016-04-04 16:52:35 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2016-04-04 16:52:35 -0400
commit53c43c5ca13328ac8f415aa2251791b441a12b51 (patch)
treece6dbd7c97b5cf8755d94e03fcf2a63c719bfe1e
parent6d79b6c761dd76b947505340adb07c8b90296a9e (diff)
Revert "Staging: olpc_dcon: Remove obsolete driver"
This reverts commit 82ef33af9dd30075adbd9f3dd161b606b8ba88ac. It turns out these machines are still out there, and the original patch broke them. So revert it, adding back the driver, so people's machines still work properly. Reported-by: James Cameron <quozl@laptop.org> Cc: Shraddha Barke <shraddha.6596@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--MAINTAINERS8
-rw-r--r--drivers/staging/Kconfig2
-rw-r--r--drivers/staging/Makefile1
-rw-r--r--drivers/staging/olpc_dcon/Kconfig35
-rw-r--r--drivers/staging/olpc_dcon/Makefile6
-rw-r--r--drivers/staging/olpc_dcon/TODO9
-rw-r--r--drivers/staging/olpc_dcon/olpc_dcon.c813
-rw-r--r--drivers/staging/olpc_dcon/olpc_dcon.h111
-rw-r--r--drivers/staging/olpc_dcon/olpc_dcon_xo_1.c205
-rw-r--r--drivers/staging/olpc_dcon/olpc_dcon_xo_1_5.c161
10 files changed, 1351 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 03e00c7c88eb..6cd7a5c6adca 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10583,6 +10583,14 @@ L: linux-tegra@vger.kernel.org
10583S: Maintained 10583S: Maintained
10584F: drivers/staging/nvec/ 10584F: drivers/staging/nvec/
10585 10585
10586STAGING - OLPC SECONDARY DISPLAY CONTROLLER (DCON)
10587M: Jens Frederich <jfrederich@gmail.com>
10588M: Daniel Drake <dsd@laptop.org>
10589M: Jon Nettleton <jon.nettleton@gmail.com>
10590W: http://wiki.laptop.org/go/DCON
10591S: Maintained
10592F: drivers/staging/olpc_dcon/
10593
10586STAGING - REALTEK RTL8712U DRIVERS 10594STAGING - REALTEK RTL8712U DRIVERS
10587M: Larry Finger <Larry.Finger@lwfinger.net> 10595M: Larry Finger <Larry.Finger@lwfinger.net>
10588M: Florian Schilhabel <florian.c.schilhabel@googlemail.com>. 10596M: Florian Schilhabel <florian.c.schilhabel@googlemail.com>.
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index cf84581287b9..5bac28a3944e 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -30,6 +30,8 @@ source "drivers/staging/wlan-ng/Kconfig"
30 30
31source "drivers/staging/comedi/Kconfig" 31source "drivers/staging/comedi/Kconfig"
32 32
33source "drivers/staging/olpc_dcon/Kconfig"
34
33source "drivers/staging/rtl8192u/Kconfig" 35source "drivers/staging/rtl8192u/Kconfig"
34 36
35source "drivers/staging/rtl8192e/Kconfig" 37source "drivers/staging/rtl8192e/Kconfig"
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index 7d6448d20464..a954242b0f2c 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -4,6 +4,7 @@ obj-y += media/
4obj-$(CONFIG_SLICOSS) += slicoss/ 4obj-$(CONFIG_SLICOSS) += slicoss/
5obj-$(CONFIG_PRISM2_USB) += wlan-ng/ 5obj-$(CONFIG_PRISM2_USB) += wlan-ng/
6obj-$(CONFIG_COMEDI) += comedi/ 6obj-$(CONFIG_COMEDI) += comedi/
7obj-$(CONFIG_FB_OLPC_DCON) += olpc_dcon/
7obj-$(CONFIG_RTL8192U) += rtl8192u/ 8obj-$(CONFIG_RTL8192U) += rtl8192u/
8obj-$(CONFIG_RTL8192E) += rtl8192e/ 9obj-$(CONFIG_RTL8192E) += rtl8192e/
9obj-$(CONFIG_R8712U) += rtl8712/ 10obj-$(CONFIG_R8712U) += rtl8712/
diff --git a/drivers/staging/olpc_dcon/Kconfig b/drivers/staging/olpc_dcon/Kconfig
new file mode 100644
index 000000000000..d277f048789e
--- /dev/null
+++ b/drivers/staging/olpc_dcon/Kconfig
@@ -0,0 +1,35 @@
1config FB_OLPC_DCON
2 tristate "One Laptop Per Child Display CONtroller support"
3 depends on OLPC && FB
4 depends on I2C
5 depends on (GPIO_CS5535 || GPIO_CS5535=n)
6 select BACKLIGHT_CLASS_DEVICE
7 ---help---
8 In order to support very low power operation, the XO laptop uses a
9 secondary Display CONtroller, or DCON. This secondary controller
10 is present in the video pipeline between the primary display
11 controller (integrate into the processor or chipset) and the LCD
12 panel. It allows the main processor/display controller to be
13 completely powered off while still retaining an image on the display.
14 This controller is only available on OLPC platforms. Unless you have
15 one of these platforms, you will want to say 'N'.
16
17config FB_OLPC_DCON_1
18 bool "OLPC XO-1 DCON support"
19 depends on FB_OLPC_DCON && GPIO_CS5535
20 default y
21 ---help---
22 Enable support for the DCON in XO-1 model laptops. The kernel
23 communicates with the DCON using model-specific code. If you
24 have an XO-1 (or if you're unsure what model you have), you should
25 say 'Y'.
26
27config FB_OLPC_DCON_1_5
28 bool "OLPC XO-1.5 DCON support"
29 depends on FB_OLPC_DCON && ACPI
30 default y
31 ---help---
32 Enable support for the DCON in XO-1.5 model laptops. The kernel
33 communicates with the DCON using model-specific code. If you
34 have an XO-1.5 (or if you're unsure what model you have), you
35 should say 'Y'.
diff --git a/drivers/staging/olpc_dcon/Makefile b/drivers/staging/olpc_dcon/Makefile
new file mode 100644
index 000000000000..36c7e67fec20
--- /dev/null
+++ b/drivers/staging/olpc_dcon/Makefile
@@ -0,0 +1,6 @@
1olpc-dcon-objs += olpc_dcon.o
2olpc-dcon-$(CONFIG_FB_OLPC_DCON_1) += olpc_dcon_xo_1.o
3olpc-dcon-$(CONFIG_FB_OLPC_DCON_1_5) += olpc_dcon_xo_1_5.o
4obj-$(CONFIG_FB_OLPC_DCON) += olpc-dcon.o
5
6
diff --git a/drivers/staging/olpc_dcon/TODO b/drivers/staging/olpc_dcon/TODO
new file mode 100644
index 000000000000..61c2e65ac354
--- /dev/null
+++ b/drivers/staging/olpc_dcon/TODO
@@ -0,0 +1,9 @@
1TODO:
2 - see if vx855 gpio API can be made similar enough to cs5535 so we can
3 share more code
4 - allow simultaneous XO-1 and XO-1.5 support
5
6Please send patches to Greg Kroah-Hartman <greg@kroah.com> and
7copy:
8 Daniel Drake <dsd@laptop.org>
9 Jens Frederich <jfrederich@gmail.com>
diff --git a/drivers/staging/olpc_dcon/olpc_dcon.c b/drivers/staging/olpc_dcon/olpc_dcon.c
new file mode 100644
index 000000000000..f45b2ef05f48
--- /dev/null
+++ b/drivers/staging/olpc_dcon/olpc_dcon.c
@@ -0,0 +1,813 @@
1/*
2 * Mainly by David Woodhouse, somewhat modified by Jordan Crouse
3 *
4 * Copyright © 2006-2007 Red Hat, Inc.
5 * Copyright © 2006-2007 Advanced Micro Devices, Inc.
6 * Copyright © 2009 VIA Technology, Inc.
7 * Copyright (c) 2010-2011 Andres Salomon <dilinger@queued.net>
8 *
9 * This program is free software. You can redistribute it and/or
10 * modify it under the terms of version 2 of the GNU General Public
11 * License as published by the Free Software Foundation.
12 */
13
14#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
15
16#include <linux/kernel.h>
17#include <linux/fb.h>
18#include <linux/console.h>
19#include <linux/i2c.h>
20#include <linux/platform_device.h>
21#include <linux/interrupt.h>
22#include <linux/delay.h>
23#include <linux/module.h>
24#include <linux/backlight.h>
25#include <linux/device.h>
26#include <linux/uaccess.h>
27#include <linux/ctype.h>
28#include <linux/reboot.h>
29#include <linux/olpc-ec.h>
30#include <asm/tsc.h>
31#include <asm/olpc.h>
32
33#include "olpc_dcon.h"
34
35/* Module definitions */
36
37static ushort resumeline = 898;
38module_param(resumeline, ushort, 0444);
39
40static struct dcon_platform_data *pdata;
41
42/* I2C structures */
43
44/* Platform devices */
45static struct platform_device *dcon_device;
46
47static unsigned short normal_i2c[] = { 0x0d, I2C_CLIENT_END };
48
49static s32 dcon_write(struct dcon_priv *dcon, u8 reg, u16 val)
50{
51 return i2c_smbus_write_word_data(dcon->client, reg, val);
52}
53
54static s32 dcon_read(struct dcon_priv *dcon, u8 reg)
55{
56 return i2c_smbus_read_word_data(dcon->client, reg);
57}
58
59/* ===== API functions - these are called by a variety of users ==== */
60
61static int dcon_hw_init(struct dcon_priv *dcon, int is_init)
62{
63 u16 ver;
64 int rc = 0;
65
66 ver = dcon_read(dcon, DCON_REG_ID);
67 if ((ver >> 8) != 0xDC) {
68 pr_err("DCON ID not 0xDCxx: 0x%04x instead.\n", ver);
69 rc = -ENXIO;
70 goto err;
71 }
72
73 if (is_init) {
74 pr_info("Discovered DCON version %x\n", ver & 0xFF);
75 rc = pdata->init(dcon);
76 if (rc != 0) {
77 pr_err("Unable to init.\n");
78 goto err;
79 }
80 }
81
82 if (ver < 0xdc02) {
83 dev_err(&dcon->client->dev,
84 "DCON v1 is unsupported, giving up..\n");
85 rc = -ENODEV;
86 goto err;
87 }
88
89 /* SDRAM setup/hold time */
90 dcon_write(dcon, 0x3a, 0xc040);
91 dcon_write(dcon, DCON_REG_MEM_OPT_A, 0x0000); /* clear option bits */
92 dcon_write(dcon, DCON_REG_MEM_OPT_A,
93 MEM_DLL_CLOCK_DELAY | MEM_POWER_DOWN);
94 dcon_write(dcon, DCON_REG_MEM_OPT_B, MEM_SOFT_RESET);
95
96 /* Colour swizzle, AA, no passthrough, backlight */
97 if (is_init) {
98 dcon->disp_mode = MODE_PASSTHRU | MODE_BL_ENABLE |
99 MODE_CSWIZZLE | MODE_COL_AA;
100 }
101 dcon_write(dcon, DCON_REG_MODE, dcon->disp_mode);
102
103 /* Set the scanline to interrupt on during resume */
104 dcon_write(dcon, DCON_REG_SCAN_INT, resumeline);
105
106err:
107 return rc;
108}
109
110/*
111 * The smbus doesn't always come back due to what is believed to be
112 * hardware (power rail) bugs. For older models where this is known to
113 * occur, our solution is to attempt to wait for the bus to stabilize;
114 * if it doesn't happen, cut power to the dcon, repower it, and wait
115 * for the bus to stabilize. Rinse, repeat until we have a working
116 * smbus. For newer models, we simply BUG(); we want to know if this
117 * still happens despite the power fixes that have been made!
118 */
119static int dcon_bus_stabilize(struct dcon_priv *dcon, int is_powered_down)
120{
121 unsigned long timeout;
122 u8 pm;
123 int x;
124
125power_up:
126 if (is_powered_down) {
127 pm = 1;
128 x = olpc_ec_cmd(EC_DCON_POWER_MODE, &pm, 1, NULL, 0);
129 if (x) {
130 pr_warn("unable to force dcon to power up: %d!\n", x);
131 return x;
132 }
133 usleep_range(10000, 11000); /* we'll be conservative */
134 }
135
136 pdata->bus_stabilize_wiggle();
137
138 for (x = -1, timeout = 50; timeout && x < 0; timeout--) {
139 usleep_range(1000, 1100);
140 x = dcon_read(dcon, DCON_REG_ID);
141 }
142 if (x < 0) {
143 pr_err("unable to stabilize dcon's smbus, reasserting power and praying.\n");
144 BUG_ON(olpc_board_at_least(olpc_board(0xc2)));
145 pm = 0;
146 olpc_ec_cmd(EC_DCON_POWER_MODE, &pm, 1, NULL, 0);
147 msleep(100);
148 is_powered_down = 1;
149 goto power_up; /* argh, stupid hardware.. */
150 }
151
152 if (is_powered_down)
153 return dcon_hw_init(dcon, 0);
154 return 0;
155}
156
157static void dcon_set_backlight(struct dcon_priv *dcon, u8 level)
158{
159 dcon->bl_val = level;
160 dcon_write(dcon, DCON_REG_BRIGHT, dcon->bl_val);
161
162 /* Purposely turn off the backlight when we go to level 0 */
163 if (dcon->bl_val == 0) {
164 dcon->disp_mode &= ~MODE_BL_ENABLE;
165 dcon_write(dcon, DCON_REG_MODE, dcon->disp_mode);
166 } else if (!(dcon->disp_mode & MODE_BL_ENABLE)) {
167 dcon->disp_mode |= MODE_BL_ENABLE;
168 dcon_write(dcon, DCON_REG_MODE, dcon->disp_mode);
169 }
170}
171
172/* Set the output type to either color or mono */
173static int dcon_set_mono_mode(struct dcon_priv *dcon, bool enable_mono)
174{
175 if (dcon->mono == enable_mono)
176 return 0;
177
178 dcon->mono = enable_mono;
179
180 if (enable_mono) {
181 dcon->disp_mode &= ~(MODE_CSWIZZLE | MODE_COL_AA);
182 dcon->disp_mode |= MODE_MONO_LUMA;
183 } else {
184 dcon->disp_mode &= ~(MODE_MONO_LUMA);
185 dcon->disp_mode |= MODE_CSWIZZLE | MODE_COL_AA;
186 }
187
188 dcon_write(dcon, DCON_REG_MODE, dcon->disp_mode);
189 return 0;
190}
191
192/* For now, this will be really stupid - we need to address how
193 * DCONLOAD works in a sleep and account for it accordingly
194 */
195
196static void dcon_sleep(struct dcon_priv *dcon, bool sleep)
197{
198 int x;
199
200 /* Turn off the backlight and put the DCON to sleep */
201
202 if (dcon->asleep == sleep)
203 return;
204
205 if (!olpc_board_at_least(olpc_board(0xc2)))
206 return;
207
208 if (sleep) {
209 u8 pm = 0;
210
211 x = olpc_ec_cmd(EC_DCON_POWER_MODE, &pm, 1, NULL, 0);
212 if (x)
213 pr_warn("unable to force dcon to power down: %d!\n", x);
214 else
215 dcon->asleep = sleep;
216 } else {
217 /* Only re-enable the backlight if the backlight value is set */
218 if (dcon->bl_val != 0)
219 dcon->disp_mode |= MODE_BL_ENABLE;
220 x = dcon_bus_stabilize(dcon, 1);
221 if (x)
222 pr_warn("unable to reinit dcon hardware: %d!\n", x);
223 else
224 dcon->asleep = sleep;
225
226 /* Restore backlight */
227 dcon_set_backlight(dcon, dcon->bl_val);
228 }
229
230 /* We should turn off some stuff in the framebuffer - but what? */
231}
232
233/* the DCON seems to get confused if we change DCONLOAD too
234 * frequently -- i.e., approximately faster than frame time.
235 * normally we don't change it this fast, so in general we won't
236 * delay here.
237 */
238static void dcon_load_holdoff(struct dcon_priv *dcon)
239{
240 ktime_t delta_t, now;
241
242 while (1) {
243 now = ktime_get();
244 delta_t = ktime_sub(now, dcon->load_time);
245 if (ktime_to_ns(delta_t) > NSEC_PER_MSEC * 20)
246 break;
247 mdelay(4);
248 }
249}
250
251static bool dcon_blank_fb(struct dcon_priv *dcon, bool blank)
252{
253 int err;
254
255 console_lock();
256 if (!lock_fb_info(dcon->fbinfo)) {
257 console_unlock();
258 dev_err(&dcon->client->dev, "unable to lock framebuffer\n");
259 return false;
260 }
261
262 dcon->ignore_fb_events = true;
263 err = fb_blank(dcon->fbinfo,
264 blank ? FB_BLANK_POWERDOWN : FB_BLANK_UNBLANK);
265 dcon->ignore_fb_events = false;
266 unlock_fb_info(dcon->fbinfo);
267 console_unlock();
268
269 if (err) {
270 dev_err(&dcon->client->dev, "couldn't %sblank framebuffer\n",
271 blank ? "" : "un");
272 return false;
273 }
274 return true;
275}
276
277/* Set the source of the display (CPU or DCON) */
278static void dcon_source_switch(struct work_struct *work)
279{
280 struct dcon_priv *dcon = container_of(work, struct dcon_priv,
281 switch_source);
282 int source = dcon->pending_src;
283
284 if (dcon->curr_src == source)
285 return;
286
287 dcon_load_holdoff(dcon);
288
289 dcon->switched = false;
290
291 switch (source) {
292 case DCON_SOURCE_CPU:
293 pr_info("dcon_source_switch to CPU\n");
294 /* Enable the scanline interrupt bit */
295 if (dcon_write(dcon, DCON_REG_MODE,
296 dcon->disp_mode | MODE_SCAN_INT))
297 pr_err("couldn't enable scanline interrupt!\n");
298 else
299 /* Wait up to one second for the scanline interrupt */
300 wait_event_timeout(dcon->waitq, dcon->switched, HZ);
301
302 if (!dcon->switched)
303 pr_err("Timeout entering CPU mode; expect a screen glitch.\n");
304
305 /* Turn off the scanline interrupt */
306 if (dcon_write(dcon, DCON_REG_MODE, dcon->disp_mode))
307 pr_err("couldn't disable scanline interrupt!\n");
308
309 /*
310 * Ideally we'd like to disable interrupts here so that the
311 * fb unblanking and DCON turn on happen at a known time value;
312 * however, we can't do that right now with fb_blank
313 * messing with semaphores.
314 *
315 * For now, we just hope..
316 */
317 if (!dcon_blank_fb(dcon, false)) {
318 pr_err("Failed to enter CPU mode\n");
319 dcon->pending_src = DCON_SOURCE_DCON;
320 return;
321 }
322
323 /* And turn off the DCON */
324 pdata->set_dconload(1);
325 dcon->load_time = ktime_get();
326
327 pr_info("The CPU has control\n");
328 break;
329 case DCON_SOURCE_DCON:
330 {
331 ktime_t delta_t;
332
333 pr_info("dcon_source_switch to DCON\n");
334
335 /* Clear DCONLOAD - this implies that the DCON is in control */
336 pdata->set_dconload(0);
337 dcon->load_time = ktime_get();
338
339 wait_event_timeout(dcon->waitq, dcon->switched, HZ/2);
340
341 if (!dcon->switched) {
342 pr_err("Timeout entering DCON mode; expect a screen glitch.\n");
343 } else {
344 /* sometimes the DCON doesn't follow its own rules,
345 * and doesn't wait for two vsync pulses before
346 * ack'ing the frame load with an IRQ. the result
347 * is that the display shows the *previously*
348 * loaded frame. we can detect this by looking at
349 * the time between asserting DCONLOAD and the IRQ --
350 * if it's less than 20msec, then the DCON couldn't
351 * have seen two VSYNC pulses. in that case we
352 * deassert and reassert, and hope for the best.
353 * see http://dev.laptop.org/ticket/9664
354 */
355 delta_t = ktime_sub(dcon->irq_time, dcon->load_time);
356 if (dcon->switched && ktime_to_ns(delta_t)
357 < NSEC_PER_MSEC * 20) {
358 pr_err("missed loading, retrying\n");
359 pdata->set_dconload(1);
360 mdelay(41);
361 pdata->set_dconload(0);
362 dcon->load_time = ktime_get();
363 mdelay(41);
364 }
365 }
366
367 dcon_blank_fb(dcon, true);
368 pr_info("The DCON has control\n");
369 break;
370 }
371 default:
372 BUG();
373 }
374
375 dcon->curr_src = source;
376}
377
378static void dcon_set_source(struct dcon_priv *dcon, int arg)
379{
380 if (dcon->pending_src == arg)
381 return;
382
383 dcon->pending_src = arg;
384
385 if (dcon->curr_src != arg)
386 schedule_work(&dcon->switch_source);
387}
388
389static void dcon_set_source_sync(struct dcon_priv *dcon, int arg)
390{
391 dcon_set_source(dcon, arg);
392 flush_scheduled_work();
393}
394
395static ssize_t dcon_mode_show(struct device *dev,
396 struct device_attribute *attr, char *buf)
397{
398 struct dcon_priv *dcon = dev_get_drvdata(dev);
399
400 return sprintf(buf, "%4.4X\n", dcon->disp_mode);
401}
402
403static ssize_t dcon_sleep_show(struct device *dev,
404 struct device_attribute *attr, char *buf)
405{
406 struct dcon_priv *dcon = dev_get_drvdata(dev);
407
408 return sprintf(buf, "%d\n", dcon->asleep);
409}
410
411static ssize_t dcon_freeze_show(struct device *dev,
412 struct device_attribute *attr, char *buf)
413{
414 struct dcon_priv *dcon = dev_get_drvdata(dev);
415
416 return sprintf(buf, "%d\n", dcon->curr_src == DCON_SOURCE_DCON ? 1 : 0);
417}
418
419static ssize_t dcon_mono_show(struct device *dev,
420 struct device_attribute *attr, char *buf)
421{
422 struct dcon_priv *dcon = dev_get_drvdata(dev);
423
424 return sprintf(buf, "%d\n", dcon->mono);
425}
426
427static ssize_t dcon_resumeline_show(struct device *dev,
428 struct device_attribute *attr, char *buf)
429{
430 return sprintf(buf, "%d\n", resumeline);
431}
432
433static ssize_t dcon_mono_store(struct device *dev,
434 struct device_attribute *attr, const char *buf, size_t count)
435{
436 unsigned long enable_mono;
437 int rc;
438
439 rc = kstrtoul(buf, 10, &enable_mono);
440 if (rc)
441 return rc;
442
443 dcon_set_mono_mode(dev_get_drvdata(dev), enable_mono ? true : false);
444
445 return count;
446}
447
448static ssize_t dcon_freeze_store(struct device *dev,
449 struct device_attribute *attr, const char *buf, size_t count)
450{
451 struct dcon_priv *dcon = dev_get_drvdata(dev);
452 unsigned long output;
453 int ret;
454
455 ret = kstrtoul(buf, 10, &output);
456 if (ret)
457 return ret;
458
459 pr_info("dcon_freeze_store: %lu\n", output);
460
461 switch (output) {
462 case 0:
463 dcon_set_source(dcon, DCON_SOURCE_CPU);
464 break;
465 case 1:
466 dcon_set_source_sync(dcon, DCON_SOURCE_DCON);
467 break;
468 case 2: /* normally unused */
469 dcon_set_source(dcon, DCON_SOURCE_DCON);
470 break;
471 default:
472 return -EINVAL;
473 }
474
475 return count;
476}
477
478static ssize_t dcon_resumeline_store(struct device *dev,
479 struct device_attribute *attr, const char *buf, size_t count)
480{
481 unsigned short rl;
482 int rc;
483
484 rc = kstrtou16(buf, 10, &rl);
485 if (rc)
486 return rc;
487
488 resumeline = rl;
489 dcon_write(dev_get_drvdata(dev), DCON_REG_SCAN_INT, resumeline);
490
491 return count;
492}
493
494static ssize_t dcon_sleep_store(struct device *dev,
495 struct device_attribute *attr, const char *buf, size_t count)
496{
497 unsigned long output;
498 int ret;
499
500 ret = kstrtoul(buf, 10, &output);
501 if (ret)
502 return ret;
503
504 dcon_sleep(dev_get_drvdata(dev), output ? true : false);
505 return count;
506}
507
508static struct device_attribute dcon_device_files[] = {
509 __ATTR(mode, 0444, dcon_mode_show, NULL),
510 __ATTR(sleep, 0644, dcon_sleep_show, dcon_sleep_store),
511 __ATTR(freeze, 0644, dcon_freeze_show, dcon_freeze_store),
512 __ATTR(monochrome, 0644, dcon_mono_show, dcon_mono_store),
513 __ATTR(resumeline, 0644, dcon_resumeline_show, dcon_resumeline_store),
514};
515
516static int dcon_bl_update(struct backlight_device *dev)
517{
518 struct dcon_priv *dcon = bl_get_data(dev);
519 u8 level = dev->props.brightness & 0x0F;
520
521 if (dev->props.power != FB_BLANK_UNBLANK)
522 level = 0;
523
524 if (level != dcon->bl_val)
525 dcon_set_backlight(dcon, level);
526
527 /* power down the DCON when the screen is blanked */
528 if (!dcon->ignore_fb_events)
529 dcon_sleep(dcon, !!(dev->props.state & BL_CORE_FBBLANK));
530
531 return 0;
532}
533
534static int dcon_bl_get(struct backlight_device *dev)
535{
536 struct dcon_priv *dcon = bl_get_data(dev);
537
538 return dcon->bl_val;
539}
540
541static const struct backlight_ops dcon_bl_ops = {
542 .update_status = dcon_bl_update,
543 .get_brightness = dcon_bl_get,
544};
545
546static struct backlight_properties dcon_bl_props = {
547 .max_brightness = 15,
548 .type = BACKLIGHT_RAW,
549 .power = FB_BLANK_UNBLANK,
550};
551
552static int dcon_reboot_notify(struct notifier_block *nb,
553 unsigned long foo, void *bar)
554{
555 struct dcon_priv *dcon = container_of(nb, struct dcon_priv, reboot_nb);
556
557 if (!dcon || !dcon->client)
558 return NOTIFY_DONE;
559
560 /* Turn off the DCON. Entirely. */
561 dcon_write(dcon, DCON_REG_MODE, 0x39);
562 dcon_write(dcon, DCON_REG_MODE, 0x32);
563 return NOTIFY_DONE;
564}
565
566static int unfreeze_on_panic(struct notifier_block *nb,
567 unsigned long e, void *p)
568{
569 pdata->set_dconload(1);
570 return NOTIFY_DONE;
571}
572
573static struct notifier_block dcon_panic_nb = {
574 .notifier_call = unfreeze_on_panic,
575};
576
577static int dcon_detect(struct i2c_client *client, struct i2c_board_info *info)
578{
579 strlcpy(info->type, "olpc_dcon", I2C_NAME_SIZE);
580
581 return 0;
582}
583
584static int dcon_probe(struct i2c_client *client, const struct i2c_device_id *id)
585{
586 struct dcon_priv *dcon;
587 int rc, i, j;
588
589 if (!pdata)
590 return -ENXIO;
591
592 dcon = kzalloc(sizeof(*dcon), GFP_KERNEL);
593 if (!dcon)
594 return -ENOMEM;
595
596 dcon->client = client;
597 init_waitqueue_head(&dcon->waitq);
598 INIT_WORK(&dcon->switch_source, dcon_source_switch);
599 dcon->reboot_nb.notifier_call = dcon_reboot_notify;
600 dcon->reboot_nb.priority = -1;
601
602 i2c_set_clientdata(client, dcon);
603
604 if (num_registered_fb < 1) {
605 dev_err(&client->dev, "DCON driver requires a registered fb\n");
606 rc = -EIO;
607 goto einit;
608 }
609 dcon->fbinfo = registered_fb[0];
610
611 rc = dcon_hw_init(dcon, 1);
612 if (rc)
613 goto einit;
614
615 /* Add the DCON device */
616
617 dcon_device = platform_device_alloc("dcon", -1);
618
619 if (!dcon_device) {
620 pr_err("Unable to create the DCON device\n");
621 rc = -ENOMEM;
622 goto eirq;
623 }
624 rc = platform_device_add(dcon_device);
625 platform_set_drvdata(dcon_device, dcon);
626
627 if (rc) {
628 pr_err("Unable to add the DCON device\n");
629 goto edev;
630 }
631
632 for (i = 0; i < ARRAY_SIZE(dcon_device_files); i++) {
633 rc = device_create_file(&dcon_device->dev,
634 &dcon_device_files[i]);
635 if (rc) {
636 dev_err(&dcon_device->dev, "Cannot create sysfs file\n");
637 goto ecreate;
638 }
639 }
640
641 dcon->bl_val = dcon_read(dcon, DCON_REG_BRIGHT) & 0x0F;
642
643 /* Add the backlight device for the DCON */
644 dcon_bl_props.brightness = dcon->bl_val;
645 dcon->bl_dev = backlight_device_register("dcon-bl", &dcon_device->dev,
646 dcon, &dcon_bl_ops, &dcon_bl_props);
647 if (IS_ERR(dcon->bl_dev)) {
648 dev_err(&client->dev, "cannot register backlight dev (%ld)\n",
649 PTR_ERR(dcon->bl_dev));
650 dcon->bl_dev = NULL;
651 }
652
653 register_reboot_notifier(&dcon->reboot_nb);
654 atomic_notifier_chain_register(&panic_notifier_list, &dcon_panic_nb);
655
656 return 0;
657
658 ecreate:
659 for (j = 0; j < i; j++)
660 device_remove_file(&dcon_device->dev, &dcon_device_files[j]);
661 edev:
662 platform_device_unregister(dcon_device);
663 dcon_device = NULL;
664 eirq:
665 free_irq(DCON_IRQ, dcon);
666 einit:
667 kfree(dcon);
668 return rc;
669}
670
671static int dcon_remove(struct i2c_client *client)
672{
673 struct dcon_priv *dcon = i2c_get_clientdata(client);
674
675 unregister_reboot_notifier(&dcon->reboot_nb);
676 atomic_notifier_chain_unregister(&panic_notifier_list, &dcon_panic_nb);
677
678 free_irq(DCON_IRQ, dcon);
679
680 backlight_device_unregister(dcon->bl_dev);
681
682 if (dcon_device)
683 platform_device_unregister(dcon_device);
684 cancel_work_sync(&dcon->switch_source);
685
686 kfree(dcon);
687
688 return 0;
689}
690
691#ifdef CONFIG_PM
692static int dcon_suspend(struct device *dev)
693{
694 struct i2c_client *client = to_i2c_client(dev);
695 struct dcon_priv *dcon = i2c_get_clientdata(client);
696
697 if (!dcon->asleep) {
698 /* Set up the DCON to have the source */
699 dcon_set_source_sync(dcon, DCON_SOURCE_DCON);
700 }
701
702 return 0;
703}
704
705static int dcon_resume(struct device *dev)
706{
707 struct i2c_client *client = to_i2c_client(dev);
708 struct dcon_priv *dcon = i2c_get_clientdata(client);
709
710 if (!dcon->asleep) {
711 dcon_bus_stabilize(dcon, 0);
712 dcon_set_source(dcon, DCON_SOURCE_CPU);
713 }
714
715 return 0;
716}
717
718#else
719
720#define dcon_suspend NULL
721#define dcon_resume NULL
722
723#endif /* CONFIG_PM */
724
725irqreturn_t dcon_interrupt(int irq, void *id)
726{
727 struct dcon_priv *dcon = id;
728 u8 status;
729
730 if (pdata->read_status(&status))
731 return IRQ_NONE;
732
733 switch (status & 3) {
734 case 3:
735 pr_debug("DCONLOAD_MISSED interrupt\n");
736 break;
737
738 case 2: /* switch to DCON mode */
739 case 1: /* switch to CPU mode */
740 dcon->switched = true;
741 dcon->irq_time = ktime_get();
742 wake_up(&dcon->waitq);
743 break;
744
745 case 0:
746 /* workaround resume case: the DCON (on 1.5) doesn't
747 * ever assert status 0x01 when switching to CPU mode
748 * during resume. this is because DCONLOAD is de-asserted
749 * _immediately_ upon exiting S3, so the actual release
750 * of the DCON happened long before this point.
751 * see http://dev.laptop.org/ticket/9869
752 */
753 if (dcon->curr_src != dcon->pending_src && !dcon->switched) {
754 dcon->switched = true;
755 dcon->irq_time = ktime_get();
756 wake_up(&dcon->waitq);
757 pr_debug("switching w/ status 0/0\n");
758 } else {
759 pr_debug("scanline interrupt w/CPU\n");
760 }
761 }
762
763 return IRQ_HANDLED;
764}
765
766static const struct dev_pm_ops dcon_pm_ops = {
767 .suspend = dcon_suspend,
768 .resume = dcon_resume,
769};
770
771static const struct i2c_device_id dcon_idtable[] = {
772 { "olpc_dcon", 0 },
773 { }
774};
775MODULE_DEVICE_TABLE(i2c, dcon_idtable);
776
777static struct i2c_driver dcon_driver = {
778 .driver = {
779 .name = "olpc_dcon",
780 .pm = &dcon_pm_ops,
781 },
782 .class = I2C_CLASS_DDC | I2C_CLASS_HWMON,
783 .id_table = dcon_idtable,
784 .probe = dcon_probe,
785 .remove = dcon_remove,
786 .detect = dcon_detect,
787 .address_list = normal_i2c,
788};
789
790static int __init olpc_dcon_init(void)
791{
792#ifdef CONFIG_FB_OLPC_DCON_1_5
793 /* XO-1.5 */
794 if (olpc_board_at_least(olpc_board(0xd0)))
795 pdata = &dcon_pdata_xo_1_5;
796#endif
797#ifdef CONFIG_FB_OLPC_DCON_1
798 if (!pdata)
799 pdata = &dcon_pdata_xo_1;
800#endif
801
802 return i2c_add_driver(&dcon_driver);
803}
804
805static void __exit olpc_dcon_exit(void)
806{
807 i2c_del_driver(&dcon_driver);
808}
809
810module_init(olpc_dcon_init);
811module_exit(olpc_dcon_exit);
812
813MODULE_LICENSE("GPL");
diff --git a/drivers/staging/olpc_dcon/olpc_dcon.h b/drivers/staging/olpc_dcon/olpc_dcon.h
new file mode 100644
index 000000000000..215e7ec4dea2
--- /dev/null
+++ b/drivers/staging/olpc_dcon/olpc_dcon.h
@@ -0,0 +1,111 @@
1#ifndef OLPC_DCON_H_
2#define OLPC_DCON_H_
3
4#include <linux/notifier.h>
5#include <linux/workqueue.h>
6
7/* DCON registers */
8
9#define DCON_REG_ID 0
10#define DCON_REG_MODE 1
11
12#define MODE_PASSTHRU (1<<0)
13#define MODE_SLEEP (1<<1)
14#define MODE_SLEEP_AUTO (1<<2)
15#define MODE_BL_ENABLE (1<<3)
16#define MODE_BLANK (1<<4)
17#define MODE_CSWIZZLE (1<<5)
18#define MODE_COL_AA (1<<6)
19#define MODE_MONO_LUMA (1<<7)
20#define MODE_SCAN_INT (1<<8)
21#define MODE_CLOCKDIV (1<<9)
22#define MODE_DEBUG (1<<14)
23#define MODE_SELFTEST (1<<15)
24
25#define DCON_REG_HRES 0x2
26#define DCON_REG_HTOTAL 0x3
27#define DCON_REG_HSYNC_WIDTH 0x4
28#define DCON_REG_VRES 0x5
29#define DCON_REG_VTOTAL 0x6
30#define DCON_REG_VSYNC_WIDTH 0x7
31#define DCON_REG_TIMEOUT 0x8
32#define DCON_REG_SCAN_INT 0x9
33#define DCON_REG_BRIGHT 0xa
34#define DCON_REG_MEM_OPT_A 0x41
35#define DCON_REG_MEM_OPT_B 0x42
36
37/* Load Delay Locked Loop (DLL) settings for clock delay */
38#define MEM_DLL_CLOCK_DELAY (1<<0)
39/* Memory controller power down function */
40#define MEM_POWER_DOWN (1<<8)
41/* Memory controller software reset */
42#define MEM_SOFT_RESET (1<<0)
43
44/* Status values */
45
46#define DCONSTAT_SCANINT 0
47#define DCONSTAT_SCANINT_DCON 1
48#define DCONSTAT_DISPLAYLOAD 2
49#define DCONSTAT_MISSED 3
50
51/* Source values */
52
53#define DCON_SOURCE_DCON 0
54#define DCON_SOURCE_CPU 1
55
56/* Interrupt */
57#define DCON_IRQ 6
58
59struct dcon_priv {
60 struct i2c_client *client;
61 struct fb_info *fbinfo;
62 struct backlight_device *bl_dev;
63
64 wait_queue_head_t waitq;
65 struct work_struct switch_source;
66 struct notifier_block reboot_nb;
67
68 /* Shadow register for the DCON_REG_MODE register */
69 u8 disp_mode;
70
71 /* The current backlight value - this saves us some smbus traffic */
72 u8 bl_val;
73
74 /* Current source, initialized at probe time */
75 int curr_src;
76
77 /* Desired source */
78 int pending_src;
79
80 /* Variables used during switches */
81 bool switched;
82 ktime_t irq_time;
83 ktime_t load_time;
84
85 /* Current output type; true == mono, false == color */
86 bool mono;
87 bool asleep;
88 /* This get set while controlling fb blank state from the driver */
89 bool ignore_fb_events;
90};
91
92struct dcon_platform_data {
93 int (*init)(struct dcon_priv *);
94 void (*bus_stabilize_wiggle)(void);
95 void (*set_dconload)(int);
96 int (*read_status)(u8 *);
97};
98
99#include <linux/interrupt.h>
100
101irqreturn_t dcon_interrupt(int irq, void *id);
102
103#ifdef CONFIG_FB_OLPC_DCON_1
104extern struct dcon_platform_data dcon_pdata_xo_1;
105#endif
106
107#ifdef CONFIG_FB_OLPC_DCON_1_5
108extern struct dcon_platform_data dcon_pdata_xo_1_5;
109#endif
110
111#endif
diff --git a/drivers/staging/olpc_dcon/olpc_dcon_xo_1.c b/drivers/staging/olpc_dcon/olpc_dcon_xo_1.c
new file mode 100644
index 000000000000..0c5a10c69401
--- /dev/null
+++ b/drivers/staging/olpc_dcon/olpc_dcon_xo_1.c
@@ -0,0 +1,205 @@
1/*
2 * Mainly by David Woodhouse, somewhat modified by Jordan Crouse
3 *
4 * Copyright © 2006-2007 Red Hat, Inc.
5 * Copyright © 2006-2007 Advanced Micro Devices, Inc.
6 * Copyright © 2009 VIA Technology, Inc.
7 * Copyright (c) 2010 Andres Salomon <dilinger@queued.net>
8 *
9 * This program is free software. You can redistribute it and/or
10 * modify it under the terms of version 2 of the GNU General Public
11 * License as published by the Free Software Foundation.
12 */
13
14#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
15
16#include <linux/cs5535.h>
17#include <linux/gpio.h>
18#include <linux/delay.h>
19#include <asm/olpc.h>
20
21#include "olpc_dcon.h"
22
23static int dcon_init_xo_1(struct dcon_priv *dcon)
24{
25 unsigned char lob;
26
27 if (gpio_request(OLPC_GPIO_DCON_STAT0, "OLPC-DCON")) {
28 pr_err("failed to request STAT0 GPIO\n");
29 return -EIO;
30 }
31 if (gpio_request(OLPC_GPIO_DCON_STAT1, "OLPC-DCON")) {
32 pr_err("failed to request STAT1 GPIO\n");
33 goto err_gp_stat1;
34 }
35 if (gpio_request(OLPC_GPIO_DCON_IRQ, "OLPC-DCON")) {
36 pr_err("failed to request IRQ GPIO\n");
37 goto err_gp_irq;
38 }
39 if (gpio_request(OLPC_GPIO_DCON_LOAD, "OLPC-DCON")) {
40 pr_err("failed to request LOAD GPIO\n");
41 goto err_gp_load;
42 }
43 if (gpio_request(OLPC_GPIO_DCON_BLANK, "OLPC-DCON")) {
44 pr_err("failed to request BLANK GPIO\n");
45 goto err_gp_blank;
46 }
47
48 /* Turn off the event enable for GPIO7 just to be safe */
49 cs5535_gpio_clear(OLPC_GPIO_DCON_IRQ, GPIO_EVENTS_ENABLE);
50
51 /*
52 * Determine the current state by reading the GPIO bit; earlier
53 * stages of the boot process have established the state.
54 *
55 * Note that we read GPIO_OUTPUT_VAL rather than GPIO_READ_BACK here;
56 * this is because OFW will disable input for the pin and set a value..
57 * READ_BACK will only contain a valid value if input is enabled and
58 * then a value is set. So, future readings of the pin can use
59 * READ_BACK, but the first one cannot. Awesome, huh?
60 */
61 dcon->curr_src = cs5535_gpio_isset(OLPC_GPIO_DCON_LOAD, GPIO_OUTPUT_VAL)
62 ? DCON_SOURCE_CPU
63 : DCON_SOURCE_DCON;
64 dcon->pending_src = dcon->curr_src;
65
66 /* Set the directions for the GPIO pins */
67 gpio_direction_input(OLPC_GPIO_DCON_STAT0);
68 gpio_direction_input(OLPC_GPIO_DCON_STAT1);
69 gpio_direction_input(OLPC_GPIO_DCON_IRQ);
70 gpio_direction_input(OLPC_GPIO_DCON_BLANK);
71 gpio_direction_output(OLPC_GPIO_DCON_LOAD,
72 dcon->curr_src == DCON_SOURCE_CPU);
73
74 /* Set up the interrupt mappings */
75
76 /* Set the IRQ to pair 2 */
77 cs5535_gpio_setup_event(OLPC_GPIO_DCON_IRQ, 2, 0);
78
79 /* Enable group 2 to trigger the DCON interrupt */
80 cs5535_gpio_set_irq(2, DCON_IRQ);
81
82 /* Select edge level for interrupt (in PIC) */
83 lob = inb(0x4d0);
84 lob &= ~(1 << DCON_IRQ);
85 outb(lob, 0x4d0);
86
87 /* Register the interrupt handler */
88 if (request_irq(DCON_IRQ, &dcon_interrupt, 0, "DCON", dcon)) {
89 pr_err("failed to request DCON's irq\n");
90 goto err_req_irq;
91 }
92
93 /* Clear INV_EN for GPIO7 (DCONIRQ) */
94 cs5535_gpio_clear(OLPC_GPIO_DCON_IRQ, GPIO_INPUT_INVERT);
95
96 /* Enable filter for GPIO12 (DCONBLANK) */
97 cs5535_gpio_set(OLPC_GPIO_DCON_BLANK, GPIO_INPUT_FILTER);
98
99 /* Disable filter for GPIO7 */
100 cs5535_gpio_clear(OLPC_GPIO_DCON_IRQ, GPIO_INPUT_FILTER);
101
102 /* Disable event counter for GPIO7 (DCONIRQ) and GPIO12 (DCONBLANK) */
103 cs5535_gpio_clear(OLPC_GPIO_DCON_IRQ, GPIO_INPUT_EVENT_COUNT);
104 cs5535_gpio_clear(OLPC_GPIO_DCON_BLANK, GPIO_INPUT_EVENT_COUNT);
105
106 /* Add GPIO12 to the Filter Event Pair #7 */
107 cs5535_gpio_set(OLPC_GPIO_DCON_BLANK, GPIO_FE7_SEL);
108
109 /* Turn off negative Edge Enable for GPIO12 */
110 cs5535_gpio_clear(OLPC_GPIO_DCON_BLANK, GPIO_NEGATIVE_EDGE_EN);
111
112 /* Enable negative Edge Enable for GPIO7 */
113 cs5535_gpio_set(OLPC_GPIO_DCON_IRQ, GPIO_NEGATIVE_EDGE_EN);
114
115 /* Zero the filter amount for Filter Event Pair #7 */
116 cs5535_gpio_set(0, GPIO_FLTR7_AMOUNT);
117
118 /* Clear the negative edge status for GPIO7 and GPIO12 */
119 cs5535_gpio_set(OLPC_GPIO_DCON_IRQ, GPIO_NEGATIVE_EDGE_STS);
120 cs5535_gpio_set(OLPC_GPIO_DCON_BLANK, GPIO_NEGATIVE_EDGE_STS);
121
122 /* FIXME: Clear the positive status as well, just to be sure */
123 cs5535_gpio_set(OLPC_GPIO_DCON_IRQ, GPIO_POSITIVE_EDGE_STS);
124 cs5535_gpio_set(OLPC_GPIO_DCON_BLANK, GPIO_POSITIVE_EDGE_STS);
125
126 /* Enable events for GPIO7 (DCONIRQ) and GPIO12 (DCONBLANK) */
127 cs5535_gpio_set(OLPC_GPIO_DCON_IRQ, GPIO_EVENTS_ENABLE);
128 cs5535_gpio_set(OLPC_GPIO_DCON_BLANK, GPIO_EVENTS_ENABLE);
129
130 return 0;
131
132err_req_irq:
133 gpio_free(OLPC_GPIO_DCON_BLANK);
134err_gp_blank:
135 gpio_free(OLPC_GPIO_DCON_LOAD);
136err_gp_load:
137 gpio_free(OLPC_GPIO_DCON_IRQ);
138err_gp_irq:
139 gpio_free(OLPC_GPIO_DCON_STAT1);
140err_gp_stat1:
141 gpio_free(OLPC_GPIO_DCON_STAT0);
142 return -EIO;
143}
144
145static void dcon_wiggle_xo_1(void)
146{
147 int x;
148
149 /*
150 * According to HiMax, when powering the DCON up we should hold
151 * SMB_DATA high for 8 SMB_CLK cycles. This will force the DCON
152 * state machine to reset to a (sane) initial state. Mitch Bradley
153 * did some testing and discovered that holding for 16 SMB_CLK cycles
154 * worked a lot more reliably, so that's what we do here.
155 *
156 * According to the cs5536 spec, to set GPIO14 to SMB_CLK we must
157 * simultaneously set AUX1 IN/OUT to GPIO14; ditto for SMB_DATA and
158 * GPIO15.
159 */
160 cs5535_gpio_set(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_VAL);
161 cs5535_gpio_set(OLPC_GPIO_SMB_DATA, GPIO_OUTPUT_VAL);
162 cs5535_gpio_set(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_ENABLE);
163 cs5535_gpio_set(OLPC_GPIO_SMB_DATA, GPIO_OUTPUT_ENABLE);
164 cs5535_gpio_clear(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_AUX1);
165 cs5535_gpio_clear(OLPC_GPIO_SMB_DATA, GPIO_OUTPUT_AUX1);
166 cs5535_gpio_clear(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_AUX2);
167 cs5535_gpio_clear(OLPC_GPIO_SMB_DATA, GPIO_OUTPUT_AUX2);
168 cs5535_gpio_clear(OLPC_GPIO_SMB_CLK, GPIO_INPUT_AUX1);
169 cs5535_gpio_clear(OLPC_GPIO_SMB_DATA, GPIO_INPUT_AUX1);
170
171 for (x = 0; x < 16; x++) {
172 udelay(5);
173 cs5535_gpio_clear(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_VAL);
174 udelay(5);
175 cs5535_gpio_set(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_VAL);
176 }
177 udelay(5);
178 cs5535_gpio_set(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_AUX1);
179 cs5535_gpio_set(OLPC_GPIO_SMB_DATA, GPIO_OUTPUT_AUX1);
180 cs5535_gpio_set(OLPC_GPIO_SMB_CLK, GPIO_INPUT_AUX1);
181 cs5535_gpio_set(OLPC_GPIO_SMB_DATA, GPIO_INPUT_AUX1);
182}
183
184static void dcon_set_dconload_1(int val)
185{
186 gpio_set_value(OLPC_GPIO_DCON_LOAD, val);
187}
188
189static int dcon_read_status_xo_1(u8 *status)
190{
191 *status = gpio_get_value(OLPC_GPIO_DCON_STAT0);
192 *status |= gpio_get_value(OLPC_GPIO_DCON_STAT1) << 1;
193
194 /* Clear the negative edge status for GPIO7 */
195 cs5535_gpio_set(OLPC_GPIO_DCON_IRQ, GPIO_NEGATIVE_EDGE_STS);
196
197 return 0;
198}
199
200struct dcon_platform_data dcon_pdata_xo_1 = {
201 .init = dcon_init_xo_1,
202 .bus_stabilize_wiggle = dcon_wiggle_xo_1,
203 .set_dconload = dcon_set_dconload_1,
204 .read_status = dcon_read_status_xo_1,
205};
diff --git a/drivers/staging/olpc_dcon/olpc_dcon_xo_1_5.c b/drivers/staging/olpc_dcon/olpc_dcon_xo_1_5.c
new file mode 100644
index 000000000000..6a4d379c16a3
--- /dev/null
+++ b/drivers/staging/olpc_dcon/olpc_dcon_xo_1_5.c
@@ -0,0 +1,161 @@
1/*
2 * Copyright (c) 2009,2010 One Laptop per Child
3 *
4 * This program is free software. You can redistribute it and/or
5 * modify it under the terms of version 2 of the GNU General Public
6 * License as published by the Free Software Foundation.
7 */
8
9#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10
11#include <linux/acpi.h>
12#include <linux/delay.h>
13#include <linux/gpio.h>
14#include <asm/olpc.h>
15
16/* TODO: this eventually belongs in linux/vx855.h */
17#define NR_VX855_GPI 14
18#define NR_VX855_GPO 13
19#define NR_VX855_GPIO 15
20
21#define VX855_GPI(n) (n)
22#define VX855_GPO(n) (NR_VX855_GPI + (n))
23#define VX855_GPIO(n) (NR_VX855_GPI + NR_VX855_GPO + (n))
24
25#include "olpc_dcon.h"
26
27/* Hardware setup on the XO 1.5:
28 * DCONLOAD connects to VX855_GPIO1 (not SMBCK2)
29 * DCONBLANK connects to VX855_GPIO8 (not SSPICLK) unused in driver
30 * DCONSTAT0 connects to VX855_GPI10 (not SSPISDI)
31 * DCONSTAT1 connects to VX855_GPI11 (not nSSPISS)
32 * DCONIRQ connects to VX855_GPIO12
33 * DCONSMBDATA connects to VX855 graphics CRTSPD
34 * DCONSMBCLK connects to VX855 graphics CRTSPCLK
35 */
36
37#define VX855_GENL_PURPOSE_OUTPUT 0x44c /* PMIO_Rx4c-4f */
38#define VX855_GPI_STATUS_CHG 0x450 /* PMIO_Rx50 */
39#define VX855_GPI_SCI_SMI 0x452 /* PMIO_Rx52 */
40#define BIT_GPIO12 0x40
41
42#define PREFIX "OLPC DCON:"
43
44static void dcon_clear_irq(void)
45{
46 /* irq status will appear in PMIO_Rx50[6] (RW1C) on gpio12 */
47 outb(BIT_GPIO12, VX855_GPI_STATUS_CHG);
48}
49
50static int dcon_was_irq(void)
51{
52 u_int8_t tmp;
53
54 /* irq status will appear in PMIO_Rx50[6] on gpio12 */
55 tmp = inb(VX855_GPI_STATUS_CHG);
56 return !!(tmp & BIT_GPIO12);
57
58 return 0;
59}
60
61static int dcon_init_xo_1_5(struct dcon_priv *dcon)
62{
63 unsigned int irq;
64
65 dcon_clear_irq();
66
67 /* set PMIO_Rx52[6] to enable SCI/SMI on gpio12 */
68 outb(inb(VX855_GPI_SCI_SMI)|BIT_GPIO12, VX855_GPI_SCI_SMI);
69
70 /* Determine the current state of DCONLOAD, likely set by firmware */
71 /* GPIO1 */
72 dcon->curr_src = (inl(VX855_GENL_PURPOSE_OUTPUT) & 0x1000) ?
73 DCON_SOURCE_CPU : DCON_SOURCE_DCON;
74 dcon->pending_src = dcon->curr_src;
75
76 /* we're sharing the IRQ with ACPI */
77 irq = acpi_gbl_FADT.sci_interrupt;
78 if (request_irq(irq, &dcon_interrupt, IRQF_SHARED, "DCON", dcon)) {
79 pr_err("DCON (IRQ%d) allocation failed\n", irq);
80 return 1;
81 }
82
83 return 0;
84}
85
86static void set_i2c_line(int sda, int scl)
87{
88 unsigned char tmp;
89 unsigned int port = 0x26;
90
91 /* FIXME: This directly accesses the CRT GPIO controller !!! */
92 outb(port, 0x3c4);
93 tmp = inb(0x3c5);
94
95 if (scl)
96 tmp |= 0x20;
97 else
98 tmp &= ~0x20;
99
100 if (sda)
101 tmp |= 0x10;
102 else
103 tmp &= ~0x10;
104
105 tmp |= 0x01;
106
107 outb(port, 0x3c4);
108 outb(tmp, 0x3c5);
109}
110
111
112static void dcon_wiggle_xo_1_5(void)
113{
114 int x;
115
116 /*
117 * According to HiMax, when powering the DCON up we should hold
118 * SMB_DATA high for 8 SMB_CLK cycles. This will force the DCON
119 * state machine to reset to a (sane) initial state. Mitch Bradley
120 * did some testing and discovered that holding for 16 SMB_CLK cycles
121 * worked a lot more reliably, so that's what we do here.
122 */
123 set_i2c_line(1, 1);
124
125 for (x = 0; x < 16; x++) {
126 udelay(5);
127 set_i2c_line(1, 0);
128 udelay(5);
129 set_i2c_line(1, 1);
130 }
131 udelay(5);
132
133 /* set PMIO_Rx52[6] to enable SCI/SMI on gpio12 */
134 outb(inb(VX855_GPI_SCI_SMI)|BIT_GPIO12, VX855_GPI_SCI_SMI);
135}
136
137static void dcon_set_dconload_xo_1_5(int val)
138{
139 gpio_set_value(VX855_GPIO(1), val);
140}
141
142static int dcon_read_status_xo_1_5(u8 *status)
143{
144 if (!dcon_was_irq())
145 return -1;
146
147 /* i believe this is the same as "inb(0x44b) & 3" */
148 *status = gpio_get_value(VX855_GPI(10));
149 *status |= gpio_get_value(VX855_GPI(11)) << 1;
150
151 dcon_clear_irq();
152
153 return 0;
154}
155
156struct dcon_platform_data dcon_pdata_xo_1_5 = {
157 .init = dcon_init_xo_1_5,
158 .bus_stabilize_wiggle = dcon_wiggle_xo_1_5,
159 .set_dconload = dcon_set_dconload_xo_1_5,
160 .read_status = dcon_read_status_xo_1_5,
161};