sed_saip.c revision 1.8.6.3 1 1.8.6.3 nathanw /* $NetBSD: sed_saip.c,v 1.8.6.3 2002/04/01 07:40:18 nathanw Exp $ */
2 1.8.6.2 nathanw
3 1.8.6.2 nathanw /*-
4 1.8.6.2 nathanw * Copyright (c) 1999-2001
5 1.8.6.2 nathanw * Shin Takemura and PocketBSD Project. All rights reserved.
6 1.8.6.2 nathanw *
7 1.8.6.2 nathanw * Redistribution and use in source and binary forms, with or without
8 1.8.6.2 nathanw * modification, are permitted provided that the following conditions
9 1.8.6.2 nathanw * are met:
10 1.8.6.2 nathanw * 1. Redistributions of source code must retain the above copyright
11 1.8.6.2 nathanw * notice, this list of conditions and the following disclaimer.
12 1.8.6.2 nathanw * 2. Redistributions in binary form must reproduce the above copyright
13 1.8.6.2 nathanw * notice, this list of conditions and the following disclaimer in the
14 1.8.6.2 nathanw * documentation and/or other materials provided with the distribution.
15 1.8.6.2 nathanw * 3. All advertising materials mentioning features or use of this software
16 1.8.6.2 nathanw * must display the following acknowledgement:
17 1.8.6.2 nathanw * This product includes software developed by the PocketBSD project
18 1.8.6.2 nathanw * and its contributors.
19 1.8.6.2 nathanw * 4. Neither the name of the project nor the names of its contributors
20 1.8.6.2 nathanw * may be used to endorse or promote products derived from this software
21 1.8.6.2 nathanw * without specific prior written permission.
22 1.8.6.2 nathanw *
23 1.8.6.2 nathanw * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 1.8.6.2 nathanw * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 1.8.6.2 nathanw * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 1.8.6.2 nathanw * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 1.8.6.2 nathanw * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 1.8.6.2 nathanw * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 1.8.6.2 nathanw * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 1.8.6.2 nathanw * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 1.8.6.2 nathanw * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 1.8.6.2 nathanw * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 1.8.6.2 nathanw * SUCH DAMAGE.
34 1.8.6.2 nathanw *
35 1.8.6.2 nathanw */
36 1.8.6.2 nathanw
37 1.8.6.2 nathanw #include <sys/param.h>
38 1.8.6.2 nathanw #include <sys/systm.h>
39 1.8.6.2 nathanw #include <sys/device.h>
40 1.8.6.2 nathanw #include <sys/buf.h>
41 1.8.6.2 nathanw #include <sys/ioctl.h>
42 1.8.6.2 nathanw #include <sys/reboot.h>
43 1.8.6.2 nathanw
44 1.8.6.2 nathanw #include <uvm/uvm_extern.h>
45 1.8.6.2 nathanw
46 1.8.6.2 nathanw #include <machine/bus.h>
47 1.8.6.2 nathanw #include <machine/bootinfo.h>
48 1.8.6.2 nathanw #include <machine/config_hook.h>
49 1.8.6.2 nathanw #include <machine/platid.h>
50 1.8.6.2 nathanw #include <machine/platid_mask.h>
51 1.8.6.2 nathanw
52 1.8.6.2 nathanw #include <dev/wscons/wsconsio.h>
53 1.8.6.2 nathanw #include <dev/wscons/wsdisplayvar.h>
54 1.8.6.2 nathanw
55 1.8.6.2 nathanw #include <dev/rasops/rasops.h>
56 1.8.6.2 nathanw
57 1.8.6.2 nathanw #include <dev/hpc/hpcfbvar.h>
58 1.8.6.2 nathanw #include <dev/hpc/hpcfbio.h>
59 1.8.6.2 nathanw #include <dev/hpc/hpccmapvar.h>
60 1.8.6.2 nathanw
61 1.8.6.2 nathanw #include <arch/hpcarm/dev/sed1356var.h>
62 1.8.6.2 nathanw
63 1.8.6.2 nathanw #define VPRINTF(arg) do { if (bootverbose) printf arg; } while(0);
64 1.8.6.2 nathanw
65 1.8.6.2 nathanw /*
66 1.8.6.2 nathanw * function prototypes
67 1.8.6.2 nathanw */
68 1.8.6.2 nathanw int sed1356match(struct device *, struct cfdata *, void *);
69 1.8.6.2 nathanw void sed1356attach(struct device *, struct device *, void *);
70 1.8.6.2 nathanw int sed1356_ioctl(void *, u_long, caddr_t, int, struct proc *);
71 1.8.6.2 nathanw paddr_t sed1356_mmap(void *, off_t, int);
72 1.8.6.2 nathanw
73 1.8.6.2 nathanw
74 1.8.6.2 nathanw extern struct bus_space sa11x0_bs_tag;
75 1.8.6.2 nathanw extern int j720lcdpower(void *, int, long, void *); /* XXX */
76 1.8.6.2 nathanw
77 1.8.6.2 nathanw static int sed1356_init(struct hpcfb_fbconf *);
78 1.8.6.2 nathanw static void sed1356_power(int, void *);
79 1.8.6.2 nathanw static void sed1356_update_powerstate(struct sed1356_softc *, int);
80 1.8.6.2 nathanw void sed1356_init_backlight(struct sed1356_softc *, int);
81 1.8.6.2 nathanw void sed1356_init_brightness(struct sed1356_softc *, int);
82 1.8.6.2 nathanw void sed1356_init_contrast(struct sed1356_softc *, int);
83 1.8.6.2 nathanw void sed1356_set_brightness(struct sed1356_softc *, int);
84 1.8.6.2 nathanw void sed1356_set_contrast(struct sed1356_softc *, int);
85 1.8.6.2 nathanw
86 1.8.6.2 nathanw #if defined __mips__ || defined __sh__ || defined __arm__
87 1.8.6.2 nathanw #define __BTOP(x) ((paddr_t)(x) >> PGSHIFT)
88 1.8.6.2 nathanw #define __PTOB(x) ((paddr_t)(x) << PGSHIFT)
89 1.8.6.2 nathanw #else
90 1.8.6.2 nathanw #error "define btop, ptob."
91 1.8.6.2 nathanw #endif
92 1.8.6.2 nathanw
93 1.8.6.2 nathanw /*
94 1.8.6.2 nathanw * static variables
95 1.8.6.2 nathanw */
96 1.8.6.2 nathanw struct cfattach sed_ca = {
97 1.8.6.2 nathanw sizeof(struct sed1356_softc), sed1356match, sed1356attach,
98 1.8.6.2 nathanw };
99 1.8.6.2 nathanw struct hpcfb_accessops sed1356_ha = {
100 1.8.6.2 nathanw sed1356_ioctl, sed1356_mmap
101 1.8.6.2 nathanw };
102 1.8.6.2 nathanw
103 1.8.6.2 nathanw static int attach_flag = 0;
104 1.8.6.2 nathanw
105 1.8.6.2 nathanw /*
106 1.8.6.2 nathanw * function bodies
107 1.8.6.2 nathanw */
108 1.8.6.2 nathanw int
109 1.8.6.2 nathanw sed1356match(struct device *parent, struct cfdata *match, void *aux)
110 1.8.6.2 nathanw {
111 1.8.6.2 nathanw
112 1.8.6.2 nathanw /* XXX check version register */
113 1.8.6.2 nathanw return 1;
114 1.8.6.2 nathanw }
115 1.8.6.2 nathanw
116 1.8.6.2 nathanw void
117 1.8.6.2 nathanw sed1356attach(struct device *parent, struct device *self, void *aux)
118 1.8.6.2 nathanw {
119 1.8.6.2 nathanw struct sed1356_softc *sc = (struct sed1356_softc *)self;
120 1.8.6.2 nathanw struct hpcfb_attach_args ha;
121 1.8.6.2 nathanw int console = (bootinfo->bi_cnuse & BI_CNUSE_SERIAL) ? 0 : 1;
122 1.8.6.2 nathanw
123 1.8.6.2 nathanw printf("\n");
124 1.8.6.2 nathanw
125 1.8.6.2 nathanw if (attach_flag) {
126 1.8.6.2 nathanw panic("%s(%d): sed1356 attached twice", __FILE__, __LINE__);
127 1.8.6.2 nathanw }
128 1.8.6.2 nathanw attach_flag = 1;
129 1.8.6.2 nathanw
130 1.8.6.2 nathanw if (sed1356_init(&sc->sc_fbconf) != 0) {
131 1.8.6.2 nathanw /* just return so that hpcfb will not be attached */
132 1.8.6.2 nathanw return;
133 1.8.6.2 nathanw }
134 1.8.6.2 nathanw
135 1.8.6.2 nathanw sc->sc_iot = &sa11x0_bs_tag;
136 1.8.6.2 nathanw sc->sc_parent = (struct sa11x0_softc *)parent;
137 1.8.6.2 nathanw if (bus_space_map(sc->sc_iot, (bus_addr_t)bootinfo->fb_addr & ~0x3fffff,
138 1.8.6.2 nathanw 0x200, 0, &sc->sc_regh)) {
139 1.8.6.2 nathanw printf("%s: unable to map register\n", sc->sc_dev.dv_xname);
140 1.8.6.2 nathanw return;
141 1.8.6.2 nathanw }
142 1.8.6.2 nathanw
143 1.8.6.2 nathanw printf("%s: Epson SED1356", sc->sc_dev.dv_xname);
144 1.8.6.2 nathanw if (console) {
145 1.8.6.2 nathanw printf(", console");
146 1.8.6.2 nathanw }
147 1.8.6.2 nathanw printf("\n");
148 1.8.6.2 nathanw printf("%s: framebuffer address: 0x%08lx\n",
149 1.8.6.2 nathanw sc->sc_dev.dv_xname, (u_long)bootinfo->fb_addr);
150 1.8.6.2 nathanw
151 1.8.6.2 nathanw /* Add a suspend hook to power saving */
152 1.8.6.2 nathanw sc->sc_powerstate = 0;
153 1.8.6.2 nathanw sc->sc_powerhook = powerhook_establish(sed1356_power, sc);
154 1.8.6.2 nathanw if (sc->sc_powerhook == NULL)
155 1.8.6.2 nathanw printf("%s: WARNING: unable to establish power hook\n",
156 1.8.6.2 nathanw sc->sc_dev.dv_xname);
157 1.8.6.2 nathanw
158 1.8.6.2 nathanw /* initialize backlight brightness and lcd contrast */
159 1.8.6.2 nathanw sc->sc_lcd_inited = 0;
160 1.8.6.2 nathanw sed1356_init_brightness(sc, 1);
161 1.8.6.2 nathanw sed1356_init_contrast(sc, 1);
162 1.8.6.2 nathanw sed1356_init_backlight(sc, 1);
163 1.8.6.2 nathanw
164 1.8.6.2 nathanw if (console && hpcfb_cnattach(&sc->sc_fbconf) != 0)
165 1.8.6.2 nathanw panic("sed1356attach: cannot init fb console");
166 1.8.6.2 nathanw
167 1.8.6.2 nathanw ha.ha_console = console;
168 1.8.6.2 nathanw ha.ha_accessops = &sed1356_ha;
169 1.8.6.2 nathanw ha.ha_accessctx = sc;
170 1.8.6.2 nathanw ha.ha_curfbconf = 0;
171 1.8.6.2 nathanw ha.ha_nfbconf = 1;
172 1.8.6.2 nathanw ha.ha_fbconflist = &sc->sc_fbconf;
173 1.8.6.2 nathanw ha.ha_curdspconf = 0;
174 1.8.6.2 nathanw ha.ha_ndspconf = 1;
175 1.8.6.2 nathanw ha.ha_dspconflist = &sc->sc_dspconf;
176 1.8.6.2 nathanw
177 1.8.6.2 nathanw /* XXX */
178 1.8.6.2 nathanw if (platid_match(&platid, &platid_mask_MACH_HP_JORNADA_7XX)) {
179 1.8.6.2 nathanw config_hook(CONFIG_HOOK_POWERCONTROL,
180 1.8.6.2 nathanw CONFIG_HOOK_POWERCONTROL_LCDLIGHT,
181 1.8.6.2 nathanw CONFIG_HOOK_SHARE, j720lcdpower, sc);
182 1.8.6.2 nathanw }
183 1.8.6.2 nathanw
184 1.8.6.2 nathanw config_found(self, &ha, hpcfbprint);
185 1.8.6.2 nathanw }
186 1.8.6.2 nathanw
187 1.8.6.2 nathanw static int
188 1.8.6.2 nathanw sed1356_init(struct hpcfb_fbconf *fb)
189 1.8.6.2 nathanw {
190 1.8.6.2 nathanw /*
191 1.8.6.2 nathanw * get fb settings from bootinfo
192 1.8.6.2 nathanw */
193 1.8.6.2 nathanw if (bootinfo == NULL ||
194 1.8.6.2 nathanw bootinfo->fb_addr == 0 ||
195 1.8.6.2 nathanw bootinfo->fb_line_bytes == 0 ||
196 1.8.6.2 nathanw bootinfo->fb_width == 0 ||
197 1.8.6.2 nathanw bootinfo->fb_height == 0) {
198 1.8.6.2 nathanw printf("no frame buffer information.\n");
199 1.8.6.2 nathanw return (-1);
200 1.8.6.2 nathanw }
201 1.8.6.2 nathanw
202 1.8.6.2 nathanw /* zero fill */
203 1.8.6.2 nathanw memset(fb, 0, sizeof(*fb));
204 1.8.6.2 nathanw
205 1.8.6.2 nathanw fb->hf_conf_index = 0; /* configuration index */
206 1.8.6.2 nathanw fb->hf_nconfs = 1; /* how many configurations */
207 1.8.6.2 nathanw strcpy(fb->hf_name, "built-in video");
208 1.8.6.2 nathanw /* frame buffer name */
209 1.8.6.2 nathanw strcpy(fb->hf_conf_name, "default");
210 1.8.6.2 nathanw /* configuration name */
211 1.8.6.2 nathanw fb->hf_height = bootinfo->fb_height;
212 1.8.6.2 nathanw fb->hf_width = bootinfo->fb_width;
213 1.8.6.2 nathanw
214 1.8.6.2 nathanw if (bus_space_map(&sa11x0_bs_tag, (bus_addr_t)bootinfo->fb_addr,
215 1.8.6.2 nathanw bootinfo->fb_height * bootinfo->fb_line_bytes,
216 1.8.6.2 nathanw 0, &fb->hf_baseaddr)) {
217 1.8.6.2 nathanw printf("unable to map framebuffer\n");
218 1.8.6.2 nathanw return (-1);
219 1.8.6.2 nathanw }
220 1.8.6.2 nathanw fb->hf_offset = (u_long)bootinfo->fb_addr -
221 1.8.6.2 nathanw __PTOB(__BTOP(bootinfo->fb_addr));
222 1.8.6.2 nathanw /* frame buffer start offset */
223 1.8.6.2 nathanw fb->hf_bytes_per_line = bootinfo->fb_line_bytes;
224 1.8.6.2 nathanw fb->hf_nplanes = 1;
225 1.8.6.2 nathanw fb->hf_bytes_per_plane = bootinfo->fb_height *
226 1.8.6.2 nathanw bootinfo->fb_line_bytes;
227 1.8.6.2 nathanw
228 1.8.6.2 nathanw fb->hf_access_flags |= HPCFB_ACCESS_BYTE;
229 1.8.6.2 nathanw fb->hf_access_flags |= HPCFB_ACCESS_WORD;
230 1.8.6.2 nathanw fb->hf_access_flags |= HPCFB_ACCESS_DWORD;
231 1.8.6.2 nathanw
232 1.8.6.2 nathanw switch (bootinfo->fb_type) {
233 1.8.6.2 nathanw /*
234 1.8.6.2 nathanw * gray scale
235 1.8.6.2 nathanw */
236 1.8.6.2 nathanw case BIFB_D4_M2L_F:
237 1.8.6.2 nathanw case BIFB_D4_M2L_Fx2:
238 1.8.6.2 nathanw fb->hf_access_flags |= HPCFB_ACCESS_REVERSE;
239 1.8.6.2 nathanw /* fall through */
240 1.8.6.2 nathanw case BIFB_D4_M2L_0:
241 1.8.6.2 nathanw case BIFB_D4_M2L_0x2:
242 1.8.6.2 nathanw fb->hf_class = HPCFB_CLASS_GRAYSCALE;
243 1.8.6.2 nathanw fb->hf_access_flags |= HPCFB_ACCESS_STATIC;
244 1.8.6.2 nathanw fb->hf_pack_width = 8;
245 1.8.6.2 nathanw fb->hf_pixels_per_pack = 2;
246 1.8.6.2 nathanw fb->hf_pixel_width = 4;
247 1.8.6.2 nathanw fb->hf_class_data_length = sizeof(struct hf_gray_tag);
248 1.8.6.2 nathanw fb->hf_u.hf_gray.hf_flags = 0; /* reserved for future use */
249 1.8.6.2 nathanw break;
250 1.8.6.2 nathanw
251 1.8.6.2 nathanw /*
252 1.8.6.2 nathanw * indexed color
253 1.8.6.2 nathanw */
254 1.8.6.2 nathanw case BIFB_D8_FF:
255 1.8.6.2 nathanw fb->hf_access_flags |= HPCFB_ACCESS_REVERSE;
256 1.8.6.2 nathanw /* fall through */
257 1.8.6.2 nathanw case BIFB_D8_00:
258 1.8.6.2 nathanw fb->hf_class = HPCFB_CLASS_INDEXCOLOR;
259 1.8.6.2 nathanw fb->hf_access_flags |= HPCFB_ACCESS_STATIC;
260 1.8.6.2 nathanw fb->hf_pack_width = 8;
261 1.8.6.2 nathanw fb->hf_pixels_per_pack = 1;
262 1.8.6.2 nathanw fb->hf_pixel_width = 8;
263 1.8.6.2 nathanw fb->hf_class_data_length = sizeof(struct hf_indexed_tag);
264 1.8.6.2 nathanw fb->hf_u.hf_indexed.hf_flags = 0; /* reserved for future use */
265 1.8.6.2 nathanw break;
266 1.8.6.2 nathanw
267 1.8.6.2 nathanw /*
268 1.8.6.2 nathanw * RGB color
269 1.8.6.2 nathanw */
270 1.8.6.2 nathanw case BIFB_D16_FFFF:
271 1.8.6.2 nathanw fb->hf_access_flags |= HPCFB_ACCESS_REVERSE;
272 1.8.6.2 nathanw /* fall through */
273 1.8.6.2 nathanw case BIFB_D16_0000:
274 1.8.6.2 nathanw fb->hf_class = HPCFB_CLASS_RGBCOLOR;
275 1.8.6.2 nathanw fb->hf_access_flags |= HPCFB_ACCESS_STATIC;
276 1.8.6.2 nathanw fb->hf_order_flags = HPCFB_REVORDER_BYTE;
277 1.8.6.2 nathanw fb->hf_pack_width = 16;
278 1.8.6.2 nathanw fb->hf_pixels_per_pack = 1;
279 1.8.6.2 nathanw fb->hf_pixel_width = 16;
280 1.8.6.2 nathanw
281 1.8.6.2 nathanw fb->hf_class_data_length = sizeof(struct hf_rgb_tag);
282 1.8.6.2 nathanw fb->hf_u.hf_rgb.hf_flags = 0; /* reserved for future use */
283 1.8.6.2 nathanw
284 1.8.6.2 nathanw fb->hf_u.hf_rgb.hf_red_width = 5;
285 1.8.6.2 nathanw fb->hf_u.hf_rgb.hf_red_shift = 11;
286 1.8.6.2 nathanw fb->hf_u.hf_rgb.hf_green_width = 6;
287 1.8.6.2 nathanw fb->hf_u.hf_rgb.hf_green_shift = 5;
288 1.8.6.2 nathanw fb->hf_u.hf_rgb.hf_blue_width = 5;
289 1.8.6.2 nathanw fb->hf_u.hf_rgb.hf_blue_shift = 0;
290 1.8.6.2 nathanw fb->hf_u.hf_rgb.hf_alpha_width = 0;
291 1.8.6.2 nathanw fb->hf_u.hf_rgb.hf_alpha_shift = 0;
292 1.8.6.2 nathanw break;
293 1.8.6.2 nathanw
294 1.8.6.2 nathanw default:
295 1.8.6.2 nathanw printf("unsupported type %d.\n", bootinfo->fb_type);
296 1.8.6.2 nathanw return (-1);
297 1.8.6.2 nathanw break;
298 1.8.6.2 nathanw }
299 1.8.6.2 nathanw
300 1.8.6.2 nathanw return (0); /* no error */
301 1.8.6.2 nathanw }
302 1.8.6.2 nathanw
303 1.8.6.2 nathanw static void
304 1.8.6.2 nathanw sed1356_power(int why, void *arg)
305 1.8.6.2 nathanw {
306 1.8.6.2 nathanw struct sed1356_softc *sc = arg;
307 1.8.6.2 nathanw
308 1.8.6.2 nathanw switch (why) {
309 1.8.6.2 nathanw case PWR_SUSPEND:
310 1.8.6.2 nathanw case PWR_STANDBY:
311 1.8.6.2 nathanw sc->sc_powerstate |= PWRSTAT_SUSPEND;
312 1.8.6.2 nathanw sed1356_update_powerstate(sc, PWRSTAT_ALL);
313 1.8.6.2 nathanw break;
314 1.8.6.2 nathanw case PWR_RESUME:
315 1.8.6.2 nathanw sc->sc_powerstate &= ~PWRSTAT_SUSPEND;
316 1.8.6.2 nathanw sed1356_update_powerstate(sc, PWRSTAT_ALL);
317 1.8.6.2 nathanw break;
318 1.8.6.2 nathanw }
319 1.8.6.2 nathanw }
320 1.8.6.2 nathanw
321 1.8.6.2 nathanw static void
322 1.8.6.2 nathanw sed1356_update_powerstate(struct sed1356_softc *sc, int updates)
323 1.8.6.2 nathanw {
324 1.8.6.2 nathanw if (updates & PWRSTAT_LCD)
325 1.8.6.2 nathanw config_hook_call(CONFIG_HOOK_POWERCONTROL,
326 1.8.6.2 nathanw CONFIG_HOOK_POWERCONTROL_LCD,
327 1.8.6.2 nathanw (void*)!(sc->sc_powerstate &
328 1.8.6.2 nathanw (PWRSTAT_VIDEOOFF|PWRSTAT_SUSPEND)));
329 1.8.6.2 nathanw
330 1.8.6.2 nathanw if (updates & PWRSTAT_BACKLIGHT)
331 1.8.6.2 nathanw config_hook_call(CONFIG_HOOK_POWERCONTROL,
332 1.8.6.2 nathanw CONFIG_HOOK_POWERCONTROL_LCDLIGHT,
333 1.8.6.2 nathanw (void*)(!(sc->sc_powerstate &
334 1.8.6.2 nathanw (PWRSTAT_VIDEOOFF|PWRSTAT_SUSPEND)) &&
335 1.8.6.2 nathanw (sc->sc_powerstate & PWRSTAT_BACKLIGHT)));
336 1.8.6.2 nathanw }
337 1.8.6.2 nathanw
338 1.8.6.2 nathanw int
339 1.8.6.2 nathanw sed1356_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
340 1.8.6.2 nathanw {
341 1.8.6.2 nathanw struct sed1356_softc *sc = (struct sed1356_softc *)v;
342 1.8.6.2 nathanw struct hpcfb_fbconf *fbconf;
343 1.8.6.2 nathanw struct hpcfb_dspconf *dspconf;
344 1.8.6.2 nathanw struct wsdisplay_param *dispparam;
345 1.8.6.2 nathanw
346 1.8.6.2 nathanw switch (cmd) {
347 1.8.6.2 nathanw case WSDISPLAYIO_GETCMAP:
348 1.8.6.2 nathanw case WSDISPLAYIO_PUTCMAP:
349 1.8.6.2 nathanw /*
350 1.8.6.2 nathanw * XXX should be able to handle color map in 4/8 bpp mode.
351 1.8.6.2 nathanw */
352 1.8.6.2 nathanw return (EINVAL);
353 1.8.6.2 nathanw
354 1.8.6.2 nathanw case WSDISPLAYIO_SVIDEO:
355 1.8.6.2 nathanw if (*(int *)data == WSDISPLAYIO_VIDEO_OFF)
356 1.8.6.2 nathanw sc->sc_powerstate |= PWRSTAT_VIDEOOFF;
357 1.8.6.2 nathanw else
358 1.8.6.2 nathanw sc->sc_powerstate &= ~PWRSTAT_VIDEOOFF;
359 1.8.6.2 nathanw sed1356_update_powerstate(sc, PWRSTAT_ALL);
360 1.8.6.2 nathanw return 0;
361 1.8.6.2 nathanw
362 1.8.6.2 nathanw case WSDISPLAYIO_GVIDEO:
363 1.8.6.2 nathanw *(int *)data = (sc->sc_powerstate&PWRSTAT_VIDEOOFF) ?
364 1.8.6.2 nathanw WSDISPLAYIO_VIDEO_OFF:WSDISPLAYIO_VIDEO_ON;
365 1.8.6.2 nathanw return 0;
366 1.8.6.2 nathanw
367 1.8.6.2 nathanw
368 1.8.6.2 nathanw case WSDISPLAYIO_GETPARAM:
369 1.8.6.2 nathanw dispparam = (struct wsdisplay_param*)data;
370 1.8.6.2 nathanw switch (dispparam->param) {
371 1.8.6.2 nathanw case WSDISPLAYIO_PARAM_BACKLIGHT:
372 1.8.6.2 nathanw VPRINTF(("sed1356_ioctl: GET:BACKLIGHT\n"));
373 1.8.6.2 nathanw sed1356_init_brightness(sc, 0);
374 1.8.6.2 nathanw sed1356_init_backlight(sc, 0);
375 1.8.6.2 nathanw VPRINTF(("sed1356_ioctl: GET:(real)BACKLIGHT %d\n",
376 1.8.6.2 nathanw (sc->sc_powerstate&PWRSTAT_BACKLIGHT)? 1: 0));
377 1.8.6.2 nathanw dispparam->min = 0;
378 1.8.6.2 nathanw dispparam->max = 1;
379 1.8.6.2 nathanw if (sc->sc_max_brightness > 0)
380 1.8.6.2 nathanw dispparam->curval = sc->sc_brightness > 0? 1: 0;
381 1.8.6.2 nathanw else
382 1.8.6.2 nathanw dispparam->curval =
383 1.8.6.2 nathanw (sc->sc_powerstate&PWRSTAT_BACKLIGHT) ? 1: 0;
384 1.8.6.2 nathanw VPRINTF(("sed1356_ioctl: GET:BACKLIGHT:%d(%s)\n",
385 1.8.6.2 nathanw dispparam->curval,
386 1.8.6.2 nathanw sc->sc_max_brightness > 0? "brightness": "light"));
387 1.8.6.2 nathanw return 0;
388 1.8.6.2 nathanw break;
389 1.8.6.2 nathanw case WSDISPLAYIO_PARAM_CONTRAST:
390 1.8.6.2 nathanw VPRINTF(("sed1356_ioctl: GET:CONTRAST\n"));
391 1.8.6.2 nathanw sed1356_init_contrast(sc, 0);
392 1.8.6.2 nathanw if (sc->sc_max_contrast > 0) {
393 1.8.6.2 nathanw dispparam->min = 0;
394 1.8.6.2 nathanw dispparam->max = sc->sc_max_contrast;
395 1.8.6.2 nathanw dispparam->curval = sc->sc_contrast;
396 1.8.6.2 nathanw VPRINTF(("sed1356_ioctl: GET:CONTRAST max=%d, current=%d\n", sc->sc_max_contrast, sc->sc_contrast));
397 1.8.6.2 nathanw return 0;
398 1.8.6.2 nathanw } else {
399 1.8.6.2 nathanw VPRINTF(("sed1356_ioctl: GET:CONTRAST EINVAL\n"));
400 1.8.6.2 nathanw return (EINVAL);
401 1.8.6.2 nathanw }
402 1.8.6.2 nathanw break;
403 1.8.6.2 nathanw case WSDISPLAYIO_PARAM_BRIGHTNESS:
404 1.8.6.2 nathanw VPRINTF(("sed1356_ioctl: GET:BRIGHTNESS\n"));
405 1.8.6.2 nathanw sed1356_init_brightness(sc, 0);
406 1.8.6.2 nathanw if (sc->sc_max_brightness > 0) {
407 1.8.6.2 nathanw dispparam->min = 0;
408 1.8.6.2 nathanw dispparam->max = sc->sc_max_brightness;
409 1.8.6.2 nathanw dispparam->curval = sc->sc_brightness;
410 1.8.6.2 nathanw VPRINTF(("sed1356_ioctl: GET:BRIGHTNESS max=%d, current=%d\n", sc->sc_max_brightness, sc->sc_brightness));
411 1.8.6.2 nathanw return 0;
412 1.8.6.2 nathanw } else {
413 1.8.6.2 nathanw VPRINTF(("sed1356_ioctl: GET:BRIGHTNESS EINVAL\n"));
414 1.8.6.2 nathanw return (EINVAL);
415 1.8.6.2 nathanw }
416 1.8.6.2 nathanw return (EINVAL);
417 1.8.6.2 nathanw default:
418 1.8.6.2 nathanw return (EINVAL);
419 1.8.6.2 nathanw }
420 1.8.6.2 nathanw return (0);
421 1.8.6.2 nathanw
422 1.8.6.2 nathanw case WSDISPLAYIO_SETPARAM:
423 1.8.6.2 nathanw dispparam = (struct wsdisplay_param*)data;
424 1.8.6.2 nathanw switch (dispparam->param) {
425 1.8.6.2 nathanw case WSDISPLAYIO_PARAM_BACKLIGHT:
426 1.8.6.2 nathanw VPRINTF(("sed1356_ioctl: SET:BACKLIGHT\n"));
427 1.8.6.2 nathanw if (dispparam->curval < 0 ||
428 1.8.6.2 nathanw 1 < dispparam->curval)
429 1.8.6.2 nathanw return (EINVAL);
430 1.8.6.2 nathanw sed1356_init_brightness(sc, 0);
431 1.8.6.2 nathanw VPRINTF(("sed1356_ioctl: SET:max brightness=%d\n", sc->sc_max_brightness));
432 1.8.6.2 nathanw if (sc->sc_max_brightness > 0) { /* dimmer */
433 1.8.6.2 nathanw if (dispparam->curval == 0){
434 1.8.6.2 nathanw sc->sc_brightness_save = sc->sc_brightness;
435 1.8.6.2 nathanw sed1356_set_brightness(sc, 0); /* min */
436 1.8.6.2 nathanw } else {
437 1.8.6.2 nathanw if (sc->sc_brightness_save == 0)
438 1.8.6.2 nathanw sc->sc_brightness_save = sc->sc_max_brightness;
439 1.8.6.2 nathanw sed1356_set_brightness(sc, sc->sc_brightness_save);
440 1.8.6.2 nathanw }
441 1.8.6.2 nathanw VPRINTF(("sed1356_ioctl: SET:BACKLIGHT:brightness=%d\n", sc->sc_brightness));
442 1.8.6.2 nathanw } else { /* off */
443 1.8.6.2 nathanw if (dispparam->curval == 0)
444 1.8.6.2 nathanw sc->sc_powerstate &= ~PWRSTAT_BACKLIGHT;
445 1.8.6.2 nathanw else
446 1.8.6.2 nathanw sc->sc_powerstate |= PWRSTAT_BACKLIGHT;
447 1.8.6.2 nathanw VPRINTF(("sed1356_ioctl: SET:BACKLIGHT:powerstate %d\n",
448 1.8.6.2 nathanw (sc->sc_powerstate & PWRSTAT_BACKLIGHT)?1:0));
449 1.8.6.2 nathanw sed1356_update_powerstate(sc, PWRSTAT_BACKLIGHT);
450 1.8.6.2 nathanw VPRINTF(("sed1356_ioctl: SET:BACKLIGHT:%d\n",
451 1.8.6.2 nathanw (sc->sc_powerstate & PWRSTAT_BACKLIGHT)?1:0));
452 1.8.6.2 nathanw }
453 1.8.6.2 nathanw return 0;
454 1.8.6.2 nathanw break;
455 1.8.6.2 nathanw case WSDISPLAYIO_PARAM_CONTRAST:
456 1.8.6.2 nathanw VPRINTF(("sed1356_ioctl: SET:CONTRAST\n"));
457 1.8.6.2 nathanw sed1356_init_contrast(sc, 0);
458 1.8.6.2 nathanw if (dispparam->curval < 0 ||
459 1.8.6.2 nathanw sc->sc_max_contrast < dispparam->curval)
460 1.8.6.2 nathanw return (EINVAL);
461 1.8.6.2 nathanw if (sc->sc_max_contrast > 0) {
462 1.8.6.2 nathanw int org = sc->sc_contrast;
463 1.8.6.2 nathanw sed1356_set_contrast(sc, dispparam->curval);
464 1.8.6.2 nathanw VPRINTF(("sed1356_ioctl: SET:CONTRAST org=%d, current=%d\n", org, sc->sc_contrast));
465 1.8.6.2 nathanw return 0;
466 1.8.6.2 nathanw } else {
467 1.8.6.2 nathanw VPRINTF(("sed1356_ioctl: SET:CONTRAST EINVAL\n"));
468 1.8.6.2 nathanw return (EINVAL);
469 1.8.6.2 nathanw }
470 1.8.6.2 nathanw break;
471 1.8.6.2 nathanw case WSDISPLAYIO_PARAM_BRIGHTNESS:
472 1.8.6.2 nathanw VPRINTF(("sed1356_ioctl: SET:BRIGHTNESS\n"));
473 1.8.6.2 nathanw sed1356_init_brightness(sc, 0);
474 1.8.6.2 nathanw if (dispparam->curval < 0 ||
475 1.8.6.2 nathanw sc->sc_max_brightness < dispparam->curval)
476 1.8.6.2 nathanw return (EINVAL);
477 1.8.6.2 nathanw if (sc->sc_max_brightness > 0) {
478 1.8.6.2 nathanw int org = sc->sc_brightness;
479 1.8.6.2 nathanw sed1356_set_brightness(sc, dispparam->curval);
480 1.8.6.2 nathanw VPRINTF(("sed1356_ioctl: SET:BRIGHTNESS org=%d, current=%d\n", org, sc->sc_brightness));
481 1.8.6.2 nathanw return 0;
482 1.8.6.2 nathanw } else {
483 1.8.6.2 nathanw VPRINTF(("sed1356_ioctl: SET:BRIGHTNESS EINVAL\n"));
484 1.8.6.2 nathanw return (EINVAL);
485 1.8.6.2 nathanw }
486 1.8.6.2 nathanw break;
487 1.8.6.2 nathanw default:
488 1.8.6.2 nathanw return (EINVAL);
489 1.8.6.2 nathanw }
490 1.8.6.2 nathanw return (0);
491 1.8.6.2 nathanw
492 1.8.6.2 nathanw case HPCFBIO_GCONF:
493 1.8.6.2 nathanw fbconf = (struct hpcfb_fbconf *)data;
494 1.8.6.2 nathanw if (fbconf->hf_conf_index != 0 &&
495 1.8.6.2 nathanw fbconf->hf_conf_index != HPCFB_CURRENT_CONFIG) {
496 1.8.6.2 nathanw return (EINVAL);
497 1.8.6.2 nathanw }
498 1.8.6.2 nathanw *fbconf = sc->sc_fbconf; /* structure assignment */
499 1.8.6.2 nathanw return (0);
500 1.8.6.2 nathanw case HPCFBIO_SCONF:
501 1.8.6.2 nathanw fbconf = (struct hpcfb_fbconf *)data;
502 1.8.6.2 nathanw if (fbconf->hf_conf_index != 0 &&
503 1.8.6.2 nathanw fbconf->hf_conf_index != HPCFB_CURRENT_CONFIG) {
504 1.8.6.2 nathanw return (EINVAL);
505 1.8.6.2 nathanw }
506 1.8.6.2 nathanw /*
507 1.8.6.2 nathanw * nothing to do because we have only one configration
508 1.8.6.2 nathanw */
509 1.8.6.2 nathanw return (0);
510 1.8.6.2 nathanw case HPCFBIO_GDSPCONF:
511 1.8.6.2 nathanw dspconf = (struct hpcfb_dspconf *)data;
512 1.8.6.2 nathanw if ((dspconf->hd_unit_index != 0 &&
513 1.8.6.2 nathanw dspconf->hd_unit_index != HPCFB_CURRENT_UNIT) ||
514 1.8.6.2 nathanw (dspconf->hd_conf_index != 0 &&
515 1.8.6.2 nathanw dspconf->hd_conf_index != HPCFB_CURRENT_CONFIG)) {
516 1.8.6.2 nathanw return (EINVAL);
517 1.8.6.2 nathanw }
518 1.8.6.2 nathanw *dspconf = sc->sc_dspconf; /* structure assignment */
519 1.8.6.2 nathanw return (0);
520 1.8.6.2 nathanw case HPCFBIO_SDSPCONF:
521 1.8.6.2 nathanw dspconf = (struct hpcfb_dspconf *)data;
522 1.8.6.2 nathanw if ((dspconf->hd_unit_index != 0 &&
523 1.8.6.2 nathanw dspconf->hd_unit_index != HPCFB_CURRENT_UNIT) ||
524 1.8.6.2 nathanw (dspconf->hd_conf_index != 0 &&
525 1.8.6.2 nathanw dspconf->hd_conf_index != HPCFB_CURRENT_CONFIG)) {
526 1.8.6.2 nathanw return (EINVAL);
527 1.8.6.2 nathanw }
528 1.8.6.2 nathanw /*
529 1.8.6.2 nathanw * nothing to do
530 1.8.6.2 nathanw * because we have only one unit and one configration
531 1.8.6.2 nathanw */
532 1.8.6.2 nathanw return (0);
533 1.8.6.2 nathanw case HPCFBIO_GOP:
534 1.8.6.2 nathanw case HPCFBIO_SOP:
535 1.8.6.2 nathanw /*
536 1.8.6.2 nathanw * curently not implemented...
537 1.8.6.2 nathanw */
538 1.8.6.2 nathanw return (EINVAL);
539 1.8.6.2 nathanw }
540 1.8.6.2 nathanw
541 1.8.6.3 nathanw return (EPASSTHROUGH);
542 1.8.6.2 nathanw }
543 1.8.6.2 nathanw
544 1.8.6.2 nathanw paddr_t
545 1.8.6.2 nathanw sed1356_mmap(void *ctx, off_t offset, int prot)
546 1.8.6.2 nathanw {
547 1.8.6.2 nathanw struct sed1356_softc *sc = (struct sed1356_softc *)ctx;
548 1.8.6.2 nathanw
549 1.8.6.2 nathanw if (offset < 0 ||
550 1.8.6.2 nathanw (sc->sc_fbconf.hf_bytes_per_plane +
551 1.8.6.2 nathanw sc->sc_fbconf.hf_offset) < offset)
552 1.8.6.2 nathanw return -1;
553 1.8.6.2 nathanw
554 1.8.6.2 nathanw return __BTOP((u_long)bootinfo->fb_addr + offset);
555 1.8.6.2 nathanw }
556 1.8.6.2 nathanw
557 1.8.6.2 nathanw
558 1.8.6.2 nathanw void
559 1.8.6.2 nathanw sed1356_init_backlight(struct sed1356_softc *sc, int inattach)
560 1.8.6.2 nathanw {
561 1.8.6.2 nathanw int val = -1;
562 1.8.6.2 nathanw
563 1.8.6.2 nathanw if (sc->sc_lcd_inited&BACKLIGHT_INITED)
564 1.8.6.2 nathanw return;
565 1.8.6.2 nathanw
566 1.8.6.2 nathanw if (config_hook_call(CONFIG_HOOK_GET,
567 1.8.6.2 nathanw CONFIG_HOOK_POWER_LCDLIGHT, &val) != -1) {
568 1.8.6.2 nathanw /* we can get real light state */
569 1.8.6.2 nathanw VPRINTF(("sed1356_init_backlight: real backlight=%d\n", val));
570 1.8.6.2 nathanw if (val == 0)
571 1.8.6.2 nathanw sc->sc_powerstate &= ~PWRSTAT_BACKLIGHT;
572 1.8.6.2 nathanw else
573 1.8.6.2 nathanw sc->sc_powerstate |= PWRSTAT_BACKLIGHT;
574 1.8.6.2 nathanw sc->sc_lcd_inited |= BACKLIGHT_INITED;
575 1.8.6.2 nathanw } else if (inattach) {
576 1.8.6.2 nathanw /*
577 1.8.6.2 nathanw we cannot get real light state in attach time
578 1.8.6.2 nathanw because light device not yet attached.
579 1.8.6.2 nathanw we will retry in !inattach.
580 1.8.6.2 nathanw temporary assume light is on.
581 1.8.6.2 nathanw */
582 1.8.6.2 nathanw sc->sc_powerstate |= PWRSTAT_BACKLIGHT;
583 1.8.6.2 nathanw } else {
584 1.8.6.2 nathanw /* we cannot get real light state, so work by myself state */
585 1.8.6.2 nathanw sc->sc_lcd_inited |= BACKLIGHT_INITED;
586 1.8.6.2 nathanw }
587 1.8.6.2 nathanw }
588 1.8.6.2 nathanw
589 1.8.6.2 nathanw void
590 1.8.6.2 nathanw sed1356_init_brightness(struct sed1356_softc *sc, int inattach)
591 1.8.6.2 nathanw {
592 1.8.6.2 nathanw int val = -1;
593 1.8.6.2 nathanw
594 1.8.6.2 nathanw if (sc->sc_lcd_inited&BRIGHTNESS_INITED)
595 1.8.6.2 nathanw return;
596 1.8.6.2 nathanw
597 1.8.6.2 nathanw VPRINTF(("sed1356_init_brightness\n"));
598 1.8.6.2 nathanw if (config_hook_call(CONFIG_HOOK_GET,
599 1.8.6.2 nathanw CONFIG_HOOK_BRIGHTNESS_MAX, &val) != -1) {
600 1.8.6.2 nathanw /* we can get real brightness max */
601 1.8.6.2 nathanw VPRINTF(("sed1356_init_brightness: real brightness max=%d\n", val));
602 1.8.6.2 nathanw sc->sc_max_brightness = val;
603 1.8.6.2 nathanw val = -1;
604 1.8.6.2 nathanw if (config_hook_call(CONFIG_HOOK_GET,
605 1.8.6.2 nathanw CONFIG_HOOK_BRIGHTNESS, &val) != -1) {
606 1.8.6.2 nathanw /* we can get real brightness */
607 1.8.6.2 nathanw VPRINTF(("sed1356_init_brightness: real brightness=%d\n", val));
608 1.8.6.2 nathanw sc->sc_brightness_save = sc->sc_brightness = val;
609 1.8.6.2 nathanw } else {
610 1.8.6.2 nathanw sc->sc_brightness_save =
611 1.8.6.2 nathanw sc->sc_brightness = sc->sc_max_brightness;
612 1.8.6.2 nathanw }
613 1.8.6.2 nathanw sc->sc_lcd_inited |= BRIGHTNESS_INITED;
614 1.8.6.2 nathanw } else if (inattach) {
615 1.8.6.2 nathanw /*
616 1.8.6.2 nathanw we cannot get real brightness in attach time
617 1.8.6.2 nathanw because brightness device not yet attached.
618 1.8.6.2 nathanw we will retry in !inattach.
619 1.8.6.2 nathanw */
620 1.8.6.2 nathanw sc->sc_max_brightness = -1;
621 1.8.6.2 nathanw sc->sc_brightness = -1;
622 1.8.6.2 nathanw sc->sc_brightness_save = -1;
623 1.8.6.2 nathanw } else {
624 1.8.6.2 nathanw /* we cannot get real brightness */
625 1.8.6.2 nathanw sc->sc_lcd_inited |= BRIGHTNESS_INITED;
626 1.8.6.2 nathanw }
627 1.8.6.2 nathanw
628 1.8.6.2 nathanw return;
629 1.8.6.2 nathanw }
630 1.8.6.2 nathanw
631 1.8.6.2 nathanw void
632 1.8.6.2 nathanw sed1356_init_contrast(struct sed1356_softc *sc, int inattach)
633 1.8.6.2 nathanw {
634 1.8.6.2 nathanw int val = -1;
635 1.8.6.2 nathanw
636 1.8.6.2 nathanw if (sc->sc_lcd_inited&CONTRAST_INITED)
637 1.8.6.2 nathanw return;
638 1.8.6.2 nathanw
639 1.8.6.2 nathanw VPRINTF(("sed1356_init_contrast\n"));
640 1.8.6.2 nathanw if (config_hook_call(CONFIG_HOOK_GET,
641 1.8.6.2 nathanw CONFIG_HOOK_CONTRAST_MAX, &val) != -1) {
642 1.8.6.2 nathanw /* we can get real contrast max */
643 1.8.6.2 nathanw VPRINTF(("sed1356_init_contrast: real contrast max=%d\n", val));
644 1.8.6.2 nathanw sc->sc_max_contrast = val;
645 1.8.6.2 nathanw val = -1;
646 1.8.6.2 nathanw if (config_hook_call(CONFIG_HOOK_GET,
647 1.8.6.2 nathanw CONFIG_HOOK_CONTRAST, &val) != -1) {
648 1.8.6.2 nathanw /* we can get real contrast */
649 1.8.6.2 nathanw VPRINTF(("sed1356_init_contrast: real contrast=%d\n", val));
650 1.8.6.2 nathanw sc->sc_contrast = val;
651 1.8.6.2 nathanw } else {
652 1.8.6.2 nathanw sc->sc_contrast = sc->sc_max_contrast;
653 1.8.6.2 nathanw }
654 1.8.6.2 nathanw sc->sc_lcd_inited |= CONTRAST_INITED;
655 1.8.6.2 nathanw } else if (inattach) {
656 1.8.6.2 nathanw /*
657 1.8.6.2 nathanw we cannot get real contrast in attach time
658 1.8.6.2 nathanw because contrast device not yet attached.
659 1.8.6.2 nathanw we will retry in !inattach.
660 1.8.6.2 nathanw */
661 1.8.6.2 nathanw sc->sc_max_contrast = -1;
662 1.8.6.2 nathanw sc->sc_contrast = -1;
663 1.8.6.2 nathanw } else {
664 1.8.6.2 nathanw /* we cannot get real contrast */
665 1.8.6.2 nathanw sc->sc_lcd_inited |= CONTRAST_INITED;
666 1.8.6.2 nathanw }
667 1.8.6.2 nathanw
668 1.8.6.2 nathanw return;
669 1.8.6.2 nathanw }
670 1.8.6.2 nathanw
671 1.8.6.2 nathanw void
672 1.8.6.2 nathanw sed1356_set_brightness(struct sed1356_softc *sc, int val)
673 1.8.6.2 nathanw {
674 1.8.6.2 nathanw sc->sc_brightness = val;
675 1.8.6.2 nathanw
676 1.8.6.2 nathanw config_hook_call(CONFIG_HOOK_SET, CONFIG_HOOK_BRIGHTNESS, &val);
677 1.8.6.2 nathanw if (config_hook_call(CONFIG_HOOK_GET,
678 1.8.6.2 nathanw CONFIG_HOOK_BRIGHTNESS, &val) != -1) {
679 1.8.6.2 nathanw sc->sc_brightness = val;
680 1.8.6.2 nathanw }
681 1.8.6.2 nathanw }
682 1.8.6.2 nathanw
683 1.8.6.2 nathanw void
684 1.8.6.2 nathanw sed1356_set_contrast(struct sed1356_softc *sc, int val)
685 1.8.6.2 nathanw {
686 1.8.6.2 nathanw sc->sc_contrast = val;
687 1.8.6.2 nathanw
688 1.8.6.2 nathanw config_hook_call(CONFIG_HOOK_SET, CONFIG_HOOK_CONTRAST, &val);
689 1.8.6.2 nathanw if (config_hook_call(CONFIG_HOOK_GET,
690 1.8.6.2 nathanw CONFIG_HOOK_CONTRAST, &val) != -1) {
691 1.8.6.2 nathanw sc->sc_contrast = val;
692 1.8.6.2 nathanw }
693 1.8.6.2 nathanw }
694