sed_saip.c revision 1.8.6.4 1 1.8.6.4 nathanw /* $NetBSD: sed_saip.c,v 1.8.6.4 2002/10/18 02:37:00 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.4 nathanw CFATTACH_DECL(sed, sizeof(struct sed1356_softc),
97 1.8.6.4 nathanw sed1356match, sed1356attach, NULL, NULL);
98 1.8.6.2 nathanw struct hpcfb_accessops sed1356_ha = {
99 1.8.6.2 nathanw sed1356_ioctl, sed1356_mmap
100 1.8.6.2 nathanw };
101 1.8.6.2 nathanw
102 1.8.6.2 nathanw static int attach_flag = 0;
103 1.8.6.2 nathanw
104 1.8.6.2 nathanw /*
105 1.8.6.2 nathanw * function bodies
106 1.8.6.2 nathanw */
107 1.8.6.2 nathanw int
108 1.8.6.2 nathanw sed1356match(struct device *parent, struct cfdata *match, void *aux)
109 1.8.6.2 nathanw {
110 1.8.6.2 nathanw
111 1.8.6.2 nathanw /* XXX check version register */
112 1.8.6.2 nathanw return 1;
113 1.8.6.2 nathanw }
114 1.8.6.2 nathanw
115 1.8.6.2 nathanw void
116 1.8.6.2 nathanw sed1356attach(struct device *parent, struct device *self, void *aux)
117 1.8.6.2 nathanw {
118 1.8.6.2 nathanw struct sed1356_softc *sc = (struct sed1356_softc *)self;
119 1.8.6.2 nathanw struct hpcfb_attach_args ha;
120 1.8.6.2 nathanw int console = (bootinfo->bi_cnuse & BI_CNUSE_SERIAL) ? 0 : 1;
121 1.8.6.2 nathanw
122 1.8.6.2 nathanw printf("\n");
123 1.8.6.2 nathanw
124 1.8.6.2 nathanw if (attach_flag) {
125 1.8.6.2 nathanw panic("%s(%d): sed1356 attached twice", __FILE__, __LINE__);
126 1.8.6.2 nathanw }
127 1.8.6.2 nathanw attach_flag = 1;
128 1.8.6.2 nathanw
129 1.8.6.2 nathanw if (sed1356_init(&sc->sc_fbconf) != 0) {
130 1.8.6.2 nathanw /* just return so that hpcfb will not be attached */
131 1.8.6.2 nathanw return;
132 1.8.6.2 nathanw }
133 1.8.6.2 nathanw
134 1.8.6.2 nathanw sc->sc_iot = &sa11x0_bs_tag;
135 1.8.6.2 nathanw sc->sc_parent = (struct sa11x0_softc *)parent;
136 1.8.6.2 nathanw if (bus_space_map(sc->sc_iot, (bus_addr_t)bootinfo->fb_addr & ~0x3fffff,
137 1.8.6.2 nathanw 0x200, 0, &sc->sc_regh)) {
138 1.8.6.2 nathanw printf("%s: unable to map register\n", sc->sc_dev.dv_xname);
139 1.8.6.2 nathanw return;
140 1.8.6.2 nathanw }
141 1.8.6.2 nathanw
142 1.8.6.2 nathanw printf("%s: Epson SED1356", sc->sc_dev.dv_xname);
143 1.8.6.2 nathanw if (console) {
144 1.8.6.2 nathanw printf(", console");
145 1.8.6.2 nathanw }
146 1.8.6.2 nathanw printf("\n");
147 1.8.6.2 nathanw printf("%s: framebuffer address: 0x%08lx\n",
148 1.8.6.2 nathanw sc->sc_dev.dv_xname, (u_long)bootinfo->fb_addr);
149 1.8.6.2 nathanw
150 1.8.6.2 nathanw /* Add a suspend hook to power saving */
151 1.8.6.2 nathanw sc->sc_powerstate = 0;
152 1.8.6.2 nathanw sc->sc_powerhook = powerhook_establish(sed1356_power, sc);
153 1.8.6.2 nathanw if (sc->sc_powerhook == NULL)
154 1.8.6.2 nathanw printf("%s: WARNING: unable to establish power hook\n",
155 1.8.6.2 nathanw sc->sc_dev.dv_xname);
156 1.8.6.2 nathanw
157 1.8.6.2 nathanw /* initialize backlight brightness and lcd contrast */
158 1.8.6.2 nathanw sc->sc_lcd_inited = 0;
159 1.8.6.2 nathanw sed1356_init_brightness(sc, 1);
160 1.8.6.2 nathanw sed1356_init_contrast(sc, 1);
161 1.8.6.2 nathanw sed1356_init_backlight(sc, 1);
162 1.8.6.2 nathanw
163 1.8.6.2 nathanw if (console && hpcfb_cnattach(&sc->sc_fbconf) != 0)
164 1.8.6.2 nathanw panic("sed1356attach: cannot init fb console");
165 1.8.6.2 nathanw
166 1.8.6.2 nathanw ha.ha_console = console;
167 1.8.6.2 nathanw ha.ha_accessops = &sed1356_ha;
168 1.8.6.2 nathanw ha.ha_accessctx = sc;
169 1.8.6.2 nathanw ha.ha_curfbconf = 0;
170 1.8.6.2 nathanw ha.ha_nfbconf = 1;
171 1.8.6.2 nathanw ha.ha_fbconflist = &sc->sc_fbconf;
172 1.8.6.2 nathanw ha.ha_curdspconf = 0;
173 1.8.6.2 nathanw ha.ha_ndspconf = 1;
174 1.8.6.2 nathanw ha.ha_dspconflist = &sc->sc_dspconf;
175 1.8.6.2 nathanw
176 1.8.6.2 nathanw /* XXX */
177 1.8.6.2 nathanw if (platid_match(&platid, &platid_mask_MACH_HP_JORNADA_7XX)) {
178 1.8.6.2 nathanw config_hook(CONFIG_HOOK_POWERCONTROL,
179 1.8.6.2 nathanw CONFIG_HOOK_POWERCONTROL_LCDLIGHT,
180 1.8.6.2 nathanw CONFIG_HOOK_SHARE, j720lcdpower, sc);
181 1.8.6.2 nathanw }
182 1.8.6.2 nathanw
183 1.8.6.2 nathanw config_found(self, &ha, hpcfbprint);
184 1.8.6.2 nathanw }
185 1.8.6.2 nathanw
186 1.8.6.2 nathanw static int
187 1.8.6.2 nathanw sed1356_init(struct hpcfb_fbconf *fb)
188 1.8.6.2 nathanw {
189 1.8.6.2 nathanw /*
190 1.8.6.2 nathanw * get fb settings from bootinfo
191 1.8.6.2 nathanw */
192 1.8.6.2 nathanw if (bootinfo == NULL ||
193 1.8.6.2 nathanw bootinfo->fb_addr == 0 ||
194 1.8.6.2 nathanw bootinfo->fb_line_bytes == 0 ||
195 1.8.6.2 nathanw bootinfo->fb_width == 0 ||
196 1.8.6.2 nathanw bootinfo->fb_height == 0) {
197 1.8.6.2 nathanw printf("no frame buffer information.\n");
198 1.8.6.2 nathanw return (-1);
199 1.8.6.2 nathanw }
200 1.8.6.2 nathanw
201 1.8.6.2 nathanw /* zero fill */
202 1.8.6.2 nathanw memset(fb, 0, sizeof(*fb));
203 1.8.6.2 nathanw
204 1.8.6.2 nathanw fb->hf_conf_index = 0; /* configuration index */
205 1.8.6.2 nathanw fb->hf_nconfs = 1; /* how many configurations */
206 1.8.6.2 nathanw strcpy(fb->hf_name, "built-in video");
207 1.8.6.2 nathanw /* frame buffer name */
208 1.8.6.2 nathanw strcpy(fb->hf_conf_name, "default");
209 1.8.6.2 nathanw /* configuration name */
210 1.8.6.2 nathanw fb->hf_height = bootinfo->fb_height;
211 1.8.6.2 nathanw fb->hf_width = bootinfo->fb_width;
212 1.8.6.2 nathanw
213 1.8.6.2 nathanw if (bus_space_map(&sa11x0_bs_tag, (bus_addr_t)bootinfo->fb_addr,
214 1.8.6.2 nathanw bootinfo->fb_height * bootinfo->fb_line_bytes,
215 1.8.6.2 nathanw 0, &fb->hf_baseaddr)) {
216 1.8.6.2 nathanw printf("unable to map framebuffer\n");
217 1.8.6.2 nathanw return (-1);
218 1.8.6.2 nathanw }
219 1.8.6.2 nathanw fb->hf_offset = (u_long)bootinfo->fb_addr -
220 1.8.6.2 nathanw __PTOB(__BTOP(bootinfo->fb_addr));
221 1.8.6.2 nathanw /* frame buffer start offset */
222 1.8.6.2 nathanw fb->hf_bytes_per_line = bootinfo->fb_line_bytes;
223 1.8.6.2 nathanw fb->hf_nplanes = 1;
224 1.8.6.2 nathanw fb->hf_bytes_per_plane = bootinfo->fb_height *
225 1.8.6.2 nathanw bootinfo->fb_line_bytes;
226 1.8.6.2 nathanw
227 1.8.6.2 nathanw fb->hf_access_flags |= HPCFB_ACCESS_BYTE;
228 1.8.6.2 nathanw fb->hf_access_flags |= HPCFB_ACCESS_WORD;
229 1.8.6.2 nathanw fb->hf_access_flags |= HPCFB_ACCESS_DWORD;
230 1.8.6.2 nathanw
231 1.8.6.2 nathanw switch (bootinfo->fb_type) {
232 1.8.6.2 nathanw /*
233 1.8.6.2 nathanw * gray scale
234 1.8.6.2 nathanw */
235 1.8.6.2 nathanw case BIFB_D4_M2L_F:
236 1.8.6.2 nathanw case BIFB_D4_M2L_Fx2:
237 1.8.6.2 nathanw fb->hf_access_flags |= HPCFB_ACCESS_REVERSE;
238 1.8.6.2 nathanw /* fall through */
239 1.8.6.2 nathanw case BIFB_D4_M2L_0:
240 1.8.6.2 nathanw case BIFB_D4_M2L_0x2:
241 1.8.6.2 nathanw fb->hf_class = HPCFB_CLASS_GRAYSCALE;
242 1.8.6.2 nathanw fb->hf_access_flags |= HPCFB_ACCESS_STATIC;
243 1.8.6.2 nathanw fb->hf_pack_width = 8;
244 1.8.6.2 nathanw fb->hf_pixels_per_pack = 2;
245 1.8.6.2 nathanw fb->hf_pixel_width = 4;
246 1.8.6.2 nathanw fb->hf_class_data_length = sizeof(struct hf_gray_tag);
247 1.8.6.2 nathanw fb->hf_u.hf_gray.hf_flags = 0; /* reserved for future use */
248 1.8.6.2 nathanw break;
249 1.8.6.2 nathanw
250 1.8.6.2 nathanw /*
251 1.8.6.2 nathanw * indexed color
252 1.8.6.2 nathanw */
253 1.8.6.2 nathanw case BIFB_D8_FF:
254 1.8.6.2 nathanw fb->hf_access_flags |= HPCFB_ACCESS_REVERSE;
255 1.8.6.2 nathanw /* fall through */
256 1.8.6.2 nathanw case BIFB_D8_00:
257 1.8.6.2 nathanw fb->hf_class = HPCFB_CLASS_INDEXCOLOR;
258 1.8.6.2 nathanw fb->hf_access_flags |= HPCFB_ACCESS_STATIC;
259 1.8.6.2 nathanw fb->hf_pack_width = 8;
260 1.8.6.2 nathanw fb->hf_pixels_per_pack = 1;
261 1.8.6.2 nathanw fb->hf_pixel_width = 8;
262 1.8.6.2 nathanw fb->hf_class_data_length = sizeof(struct hf_indexed_tag);
263 1.8.6.2 nathanw fb->hf_u.hf_indexed.hf_flags = 0; /* reserved for future use */
264 1.8.6.2 nathanw break;
265 1.8.6.2 nathanw
266 1.8.6.2 nathanw /*
267 1.8.6.2 nathanw * RGB color
268 1.8.6.2 nathanw */
269 1.8.6.2 nathanw case BIFB_D16_FFFF:
270 1.8.6.2 nathanw fb->hf_access_flags |= HPCFB_ACCESS_REVERSE;
271 1.8.6.2 nathanw /* fall through */
272 1.8.6.2 nathanw case BIFB_D16_0000:
273 1.8.6.2 nathanw fb->hf_class = HPCFB_CLASS_RGBCOLOR;
274 1.8.6.2 nathanw fb->hf_access_flags |= HPCFB_ACCESS_STATIC;
275 1.8.6.2 nathanw fb->hf_order_flags = HPCFB_REVORDER_BYTE;
276 1.8.6.2 nathanw fb->hf_pack_width = 16;
277 1.8.6.2 nathanw fb->hf_pixels_per_pack = 1;
278 1.8.6.2 nathanw fb->hf_pixel_width = 16;
279 1.8.6.2 nathanw
280 1.8.6.2 nathanw fb->hf_class_data_length = sizeof(struct hf_rgb_tag);
281 1.8.6.2 nathanw fb->hf_u.hf_rgb.hf_flags = 0; /* reserved for future use */
282 1.8.6.2 nathanw
283 1.8.6.2 nathanw fb->hf_u.hf_rgb.hf_red_width = 5;
284 1.8.6.2 nathanw fb->hf_u.hf_rgb.hf_red_shift = 11;
285 1.8.6.2 nathanw fb->hf_u.hf_rgb.hf_green_width = 6;
286 1.8.6.2 nathanw fb->hf_u.hf_rgb.hf_green_shift = 5;
287 1.8.6.2 nathanw fb->hf_u.hf_rgb.hf_blue_width = 5;
288 1.8.6.2 nathanw fb->hf_u.hf_rgb.hf_blue_shift = 0;
289 1.8.6.2 nathanw fb->hf_u.hf_rgb.hf_alpha_width = 0;
290 1.8.6.2 nathanw fb->hf_u.hf_rgb.hf_alpha_shift = 0;
291 1.8.6.2 nathanw break;
292 1.8.6.2 nathanw
293 1.8.6.2 nathanw default:
294 1.8.6.2 nathanw printf("unsupported type %d.\n", bootinfo->fb_type);
295 1.8.6.2 nathanw return (-1);
296 1.8.6.2 nathanw break;
297 1.8.6.2 nathanw }
298 1.8.6.2 nathanw
299 1.8.6.2 nathanw return (0); /* no error */
300 1.8.6.2 nathanw }
301 1.8.6.2 nathanw
302 1.8.6.2 nathanw static void
303 1.8.6.2 nathanw sed1356_power(int why, void *arg)
304 1.8.6.2 nathanw {
305 1.8.6.2 nathanw struct sed1356_softc *sc = arg;
306 1.8.6.2 nathanw
307 1.8.6.2 nathanw switch (why) {
308 1.8.6.2 nathanw case PWR_SUSPEND:
309 1.8.6.2 nathanw case PWR_STANDBY:
310 1.8.6.2 nathanw sc->sc_powerstate |= PWRSTAT_SUSPEND;
311 1.8.6.2 nathanw sed1356_update_powerstate(sc, PWRSTAT_ALL);
312 1.8.6.2 nathanw break;
313 1.8.6.2 nathanw case PWR_RESUME:
314 1.8.6.2 nathanw sc->sc_powerstate &= ~PWRSTAT_SUSPEND;
315 1.8.6.2 nathanw sed1356_update_powerstate(sc, PWRSTAT_ALL);
316 1.8.6.2 nathanw break;
317 1.8.6.2 nathanw }
318 1.8.6.2 nathanw }
319 1.8.6.2 nathanw
320 1.8.6.2 nathanw static void
321 1.8.6.2 nathanw sed1356_update_powerstate(struct sed1356_softc *sc, int updates)
322 1.8.6.2 nathanw {
323 1.8.6.2 nathanw if (updates & PWRSTAT_LCD)
324 1.8.6.2 nathanw config_hook_call(CONFIG_HOOK_POWERCONTROL,
325 1.8.6.2 nathanw CONFIG_HOOK_POWERCONTROL_LCD,
326 1.8.6.2 nathanw (void*)!(sc->sc_powerstate &
327 1.8.6.2 nathanw (PWRSTAT_VIDEOOFF|PWRSTAT_SUSPEND)));
328 1.8.6.2 nathanw
329 1.8.6.2 nathanw if (updates & PWRSTAT_BACKLIGHT)
330 1.8.6.2 nathanw config_hook_call(CONFIG_HOOK_POWERCONTROL,
331 1.8.6.2 nathanw CONFIG_HOOK_POWERCONTROL_LCDLIGHT,
332 1.8.6.2 nathanw (void*)(!(sc->sc_powerstate &
333 1.8.6.2 nathanw (PWRSTAT_VIDEOOFF|PWRSTAT_SUSPEND)) &&
334 1.8.6.2 nathanw (sc->sc_powerstate & PWRSTAT_BACKLIGHT)));
335 1.8.6.2 nathanw }
336 1.8.6.2 nathanw
337 1.8.6.2 nathanw int
338 1.8.6.2 nathanw sed1356_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
339 1.8.6.2 nathanw {
340 1.8.6.2 nathanw struct sed1356_softc *sc = (struct sed1356_softc *)v;
341 1.8.6.2 nathanw struct hpcfb_fbconf *fbconf;
342 1.8.6.2 nathanw struct hpcfb_dspconf *dspconf;
343 1.8.6.2 nathanw struct wsdisplay_param *dispparam;
344 1.8.6.2 nathanw
345 1.8.6.2 nathanw switch (cmd) {
346 1.8.6.2 nathanw case WSDISPLAYIO_GETCMAP:
347 1.8.6.2 nathanw case WSDISPLAYIO_PUTCMAP:
348 1.8.6.2 nathanw /*
349 1.8.6.2 nathanw * XXX should be able to handle color map in 4/8 bpp mode.
350 1.8.6.2 nathanw */
351 1.8.6.2 nathanw return (EINVAL);
352 1.8.6.2 nathanw
353 1.8.6.2 nathanw case WSDISPLAYIO_SVIDEO:
354 1.8.6.2 nathanw if (*(int *)data == WSDISPLAYIO_VIDEO_OFF)
355 1.8.6.2 nathanw sc->sc_powerstate |= PWRSTAT_VIDEOOFF;
356 1.8.6.2 nathanw else
357 1.8.6.2 nathanw sc->sc_powerstate &= ~PWRSTAT_VIDEOOFF;
358 1.8.6.2 nathanw sed1356_update_powerstate(sc, PWRSTAT_ALL);
359 1.8.6.2 nathanw return 0;
360 1.8.6.2 nathanw
361 1.8.6.2 nathanw case WSDISPLAYIO_GVIDEO:
362 1.8.6.2 nathanw *(int *)data = (sc->sc_powerstate&PWRSTAT_VIDEOOFF) ?
363 1.8.6.2 nathanw WSDISPLAYIO_VIDEO_OFF:WSDISPLAYIO_VIDEO_ON;
364 1.8.6.2 nathanw return 0;
365 1.8.6.2 nathanw
366 1.8.6.2 nathanw
367 1.8.6.2 nathanw case WSDISPLAYIO_GETPARAM:
368 1.8.6.2 nathanw dispparam = (struct wsdisplay_param*)data;
369 1.8.6.2 nathanw switch (dispparam->param) {
370 1.8.6.2 nathanw case WSDISPLAYIO_PARAM_BACKLIGHT:
371 1.8.6.2 nathanw VPRINTF(("sed1356_ioctl: GET:BACKLIGHT\n"));
372 1.8.6.2 nathanw sed1356_init_brightness(sc, 0);
373 1.8.6.2 nathanw sed1356_init_backlight(sc, 0);
374 1.8.6.2 nathanw VPRINTF(("sed1356_ioctl: GET:(real)BACKLIGHT %d\n",
375 1.8.6.2 nathanw (sc->sc_powerstate&PWRSTAT_BACKLIGHT)? 1: 0));
376 1.8.6.2 nathanw dispparam->min = 0;
377 1.8.6.2 nathanw dispparam->max = 1;
378 1.8.6.2 nathanw if (sc->sc_max_brightness > 0)
379 1.8.6.2 nathanw dispparam->curval = sc->sc_brightness > 0? 1: 0;
380 1.8.6.2 nathanw else
381 1.8.6.2 nathanw dispparam->curval =
382 1.8.6.2 nathanw (sc->sc_powerstate&PWRSTAT_BACKLIGHT) ? 1: 0;
383 1.8.6.2 nathanw VPRINTF(("sed1356_ioctl: GET:BACKLIGHT:%d(%s)\n",
384 1.8.6.2 nathanw dispparam->curval,
385 1.8.6.2 nathanw sc->sc_max_brightness > 0? "brightness": "light"));
386 1.8.6.2 nathanw return 0;
387 1.8.6.2 nathanw break;
388 1.8.6.2 nathanw case WSDISPLAYIO_PARAM_CONTRAST:
389 1.8.6.2 nathanw VPRINTF(("sed1356_ioctl: GET:CONTRAST\n"));
390 1.8.6.2 nathanw sed1356_init_contrast(sc, 0);
391 1.8.6.2 nathanw if (sc->sc_max_contrast > 0) {
392 1.8.6.2 nathanw dispparam->min = 0;
393 1.8.6.2 nathanw dispparam->max = sc->sc_max_contrast;
394 1.8.6.2 nathanw dispparam->curval = sc->sc_contrast;
395 1.8.6.2 nathanw VPRINTF(("sed1356_ioctl: GET:CONTRAST max=%d, current=%d\n", sc->sc_max_contrast, sc->sc_contrast));
396 1.8.6.2 nathanw return 0;
397 1.8.6.2 nathanw } else {
398 1.8.6.2 nathanw VPRINTF(("sed1356_ioctl: GET:CONTRAST EINVAL\n"));
399 1.8.6.2 nathanw return (EINVAL);
400 1.8.6.2 nathanw }
401 1.8.6.2 nathanw break;
402 1.8.6.2 nathanw case WSDISPLAYIO_PARAM_BRIGHTNESS:
403 1.8.6.2 nathanw VPRINTF(("sed1356_ioctl: GET:BRIGHTNESS\n"));
404 1.8.6.2 nathanw sed1356_init_brightness(sc, 0);
405 1.8.6.2 nathanw if (sc->sc_max_brightness > 0) {
406 1.8.6.2 nathanw dispparam->min = 0;
407 1.8.6.2 nathanw dispparam->max = sc->sc_max_brightness;
408 1.8.6.2 nathanw dispparam->curval = sc->sc_brightness;
409 1.8.6.2 nathanw VPRINTF(("sed1356_ioctl: GET:BRIGHTNESS max=%d, current=%d\n", sc->sc_max_brightness, sc->sc_brightness));
410 1.8.6.2 nathanw return 0;
411 1.8.6.2 nathanw } else {
412 1.8.6.2 nathanw VPRINTF(("sed1356_ioctl: GET:BRIGHTNESS EINVAL\n"));
413 1.8.6.2 nathanw return (EINVAL);
414 1.8.6.2 nathanw }
415 1.8.6.2 nathanw return (EINVAL);
416 1.8.6.2 nathanw default:
417 1.8.6.2 nathanw return (EINVAL);
418 1.8.6.2 nathanw }
419 1.8.6.2 nathanw return (0);
420 1.8.6.2 nathanw
421 1.8.6.2 nathanw case WSDISPLAYIO_SETPARAM:
422 1.8.6.2 nathanw dispparam = (struct wsdisplay_param*)data;
423 1.8.6.2 nathanw switch (dispparam->param) {
424 1.8.6.2 nathanw case WSDISPLAYIO_PARAM_BACKLIGHT:
425 1.8.6.2 nathanw VPRINTF(("sed1356_ioctl: SET:BACKLIGHT\n"));
426 1.8.6.2 nathanw if (dispparam->curval < 0 ||
427 1.8.6.2 nathanw 1 < dispparam->curval)
428 1.8.6.2 nathanw return (EINVAL);
429 1.8.6.2 nathanw sed1356_init_brightness(sc, 0);
430 1.8.6.2 nathanw VPRINTF(("sed1356_ioctl: SET:max brightness=%d\n", sc->sc_max_brightness));
431 1.8.6.2 nathanw if (sc->sc_max_brightness > 0) { /* dimmer */
432 1.8.6.2 nathanw if (dispparam->curval == 0){
433 1.8.6.2 nathanw sc->sc_brightness_save = sc->sc_brightness;
434 1.8.6.2 nathanw sed1356_set_brightness(sc, 0); /* min */
435 1.8.6.2 nathanw } else {
436 1.8.6.2 nathanw if (sc->sc_brightness_save == 0)
437 1.8.6.2 nathanw sc->sc_brightness_save = sc->sc_max_brightness;
438 1.8.6.2 nathanw sed1356_set_brightness(sc, sc->sc_brightness_save);
439 1.8.6.2 nathanw }
440 1.8.6.2 nathanw VPRINTF(("sed1356_ioctl: SET:BACKLIGHT:brightness=%d\n", sc->sc_brightness));
441 1.8.6.2 nathanw } else { /* off */
442 1.8.6.2 nathanw if (dispparam->curval == 0)
443 1.8.6.2 nathanw sc->sc_powerstate &= ~PWRSTAT_BACKLIGHT;
444 1.8.6.2 nathanw else
445 1.8.6.2 nathanw sc->sc_powerstate |= PWRSTAT_BACKLIGHT;
446 1.8.6.2 nathanw VPRINTF(("sed1356_ioctl: SET:BACKLIGHT:powerstate %d\n",
447 1.8.6.2 nathanw (sc->sc_powerstate & PWRSTAT_BACKLIGHT)?1:0));
448 1.8.6.2 nathanw sed1356_update_powerstate(sc, PWRSTAT_BACKLIGHT);
449 1.8.6.2 nathanw VPRINTF(("sed1356_ioctl: SET:BACKLIGHT:%d\n",
450 1.8.6.2 nathanw (sc->sc_powerstate & PWRSTAT_BACKLIGHT)?1:0));
451 1.8.6.2 nathanw }
452 1.8.6.2 nathanw return 0;
453 1.8.6.2 nathanw break;
454 1.8.6.2 nathanw case WSDISPLAYIO_PARAM_CONTRAST:
455 1.8.6.2 nathanw VPRINTF(("sed1356_ioctl: SET:CONTRAST\n"));
456 1.8.6.2 nathanw sed1356_init_contrast(sc, 0);
457 1.8.6.2 nathanw if (dispparam->curval < 0 ||
458 1.8.6.2 nathanw sc->sc_max_contrast < dispparam->curval)
459 1.8.6.2 nathanw return (EINVAL);
460 1.8.6.2 nathanw if (sc->sc_max_contrast > 0) {
461 1.8.6.2 nathanw int org = sc->sc_contrast;
462 1.8.6.2 nathanw sed1356_set_contrast(sc, dispparam->curval);
463 1.8.6.2 nathanw VPRINTF(("sed1356_ioctl: SET:CONTRAST org=%d, current=%d\n", org, sc->sc_contrast));
464 1.8.6.2 nathanw return 0;
465 1.8.6.2 nathanw } else {
466 1.8.6.2 nathanw VPRINTF(("sed1356_ioctl: SET:CONTRAST EINVAL\n"));
467 1.8.6.2 nathanw return (EINVAL);
468 1.8.6.2 nathanw }
469 1.8.6.2 nathanw break;
470 1.8.6.2 nathanw case WSDISPLAYIO_PARAM_BRIGHTNESS:
471 1.8.6.2 nathanw VPRINTF(("sed1356_ioctl: SET:BRIGHTNESS\n"));
472 1.8.6.2 nathanw sed1356_init_brightness(sc, 0);
473 1.8.6.2 nathanw if (dispparam->curval < 0 ||
474 1.8.6.2 nathanw sc->sc_max_brightness < dispparam->curval)
475 1.8.6.2 nathanw return (EINVAL);
476 1.8.6.2 nathanw if (sc->sc_max_brightness > 0) {
477 1.8.6.2 nathanw int org = sc->sc_brightness;
478 1.8.6.2 nathanw sed1356_set_brightness(sc, dispparam->curval);
479 1.8.6.2 nathanw VPRINTF(("sed1356_ioctl: SET:BRIGHTNESS org=%d, current=%d\n", org, sc->sc_brightness));
480 1.8.6.2 nathanw return 0;
481 1.8.6.2 nathanw } else {
482 1.8.6.2 nathanw VPRINTF(("sed1356_ioctl: SET:BRIGHTNESS EINVAL\n"));
483 1.8.6.2 nathanw return (EINVAL);
484 1.8.6.2 nathanw }
485 1.8.6.2 nathanw break;
486 1.8.6.2 nathanw default:
487 1.8.6.2 nathanw return (EINVAL);
488 1.8.6.2 nathanw }
489 1.8.6.2 nathanw return (0);
490 1.8.6.2 nathanw
491 1.8.6.2 nathanw case HPCFBIO_GCONF:
492 1.8.6.2 nathanw fbconf = (struct hpcfb_fbconf *)data;
493 1.8.6.2 nathanw if (fbconf->hf_conf_index != 0 &&
494 1.8.6.2 nathanw fbconf->hf_conf_index != HPCFB_CURRENT_CONFIG) {
495 1.8.6.2 nathanw return (EINVAL);
496 1.8.6.2 nathanw }
497 1.8.6.2 nathanw *fbconf = sc->sc_fbconf; /* structure assignment */
498 1.8.6.2 nathanw return (0);
499 1.8.6.2 nathanw case HPCFBIO_SCONF:
500 1.8.6.2 nathanw fbconf = (struct hpcfb_fbconf *)data;
501 1.8.6.2 nathanw if (fbconf->hf_conf_index != 0 &&
502 1.8.6.2 nathanw fbconf->hf_conf_index != HPCFB_CURRENT_CONFIG) {
503 1.8.6.2 nathanw return (EINVAL);
504 1.8.6.2 nathanw }
505 1.8.6.2 nathanw /*
506 1.8.6.2 nathanw * nothing to do because we have only one configration
507 1.8.6.2 nathanw */
508 1.8.6.2 nathanw return (0);
509 1.8.6.2 nathanw case HPCFBIO_GDSPCONF:
510 1.8.6.2 nathanw dspconf = (struct hpcfb_dspconf *)data;
511 1.8.6.2 nathanw if ((dspconf->hd_unit_index != 0 &&
512 1.8.6.2 nathanw dspconf->hd_unit_index != HPCFB_CURRENT_UNIT) ||
513 1.8.6.2 nathanw (dspconf->hd_conf_index != 0 &&
514 1.8.6.2 nathanw dspconf->hd_conf_index != HPCFB_CURRENT_CONFIG)) {
515 1.8.6.2 nathanw return (EINVAL);
516 1.8.6.2 nathanw }
517 1.8.6.2 nathanw *dspconf = sc->sc_dspconf; /* structure assignment */
518 1.8.6.2 nathanw return (0);
519 1.8.6.2 nathanw case HPCFBIO_SDSPCONF:
520 1.8.6.2 nathanw dspconf = (struct hpcfb_dspconf *)data;
521 1.8.6.2 nathanw if ((dspconf->hd_unit_index != 0 &&
522 1.8.6.2 nathanw dspconf->hd_unit_index != HPCFB_CURRENT_UNIT) ||
523 1.8.6.2 nathanw (dspconf->hd_conf_index != 0 &&
524 1.8.6.2 nathanw dspconf->hd_conf_index != HPCFB_CURRENT_CONFIG)) {
525 1.8.6.2 nathanw return (EINVAL);
526 1.8.6.2 nathanw }
527 1.8.6.2 nathanw /*
528 1.8.6.2 nathanw * nothing to do
529 1.8.6.2 nathanw * because we have only one unit and one configration
530 1.8.6.2 nathanw */
531 1.8.6.2 nathanw return (0);
532 1.8.6.2 nathanw case HPCFBIO_GOP:
533 1.8.6.2 nathanw case HPCFBIO_SOP:
534 1.8.6.2 nathanw /*
535 1.8.6.2 nathanw * curently not implemented...
536 1.8.6.2 nathanw */
537 1.8.6.2 nathanw return (EINVAL);
538 1.8.6.2 nathanw }
539 1.8.6.2 nathanw
540 1.8.6.3 nathanw return (EPASSTHROUGH);
541 1.8.6.2 nathanw }
542 1.8.6.2 nathanw
543 1.8.6.2 nathanw paddr_t
544 1.8.6.2 nathanw sed1356_mmap(void *ctx, off_t offset, int prot)
545 1.8.6.2 nathanw {
546 1.8.6.2 nathanw struct sed1356_softc *sc = (struct sed1356_softc *)ctx;
547 1.8.6.2 nathanw
548 1.8.6.2 nathanw if (offset < 0 ||
549 1.8.6.2 nathanw (sc->sc_fbconf.hf_bytes_per_plane +
550 1.8.6.2 nathanw sc->sc_fbconf.hf_offset) < offset)
551 1.8.6.2 nathanw return -1;
552 1.8.6.2 nathanw
553 1.8.6.2 nathanw return __BTOP((u_long)bootinfo->fb_addr + offset);
554 1.8.6.2 nathanw }
555 1.8.6.2 nathanw
556 1.8.6.2 nathanw
557 1.8.6.2 nathanw void
558 1.8.6.2 nathanw sed1356_init_backlight(struct sed1356_softc *sc, int inattach)
559 1.8.6.2 nathanw {
560 1.8.6.2 nathanw int val = -1;
561 1.8.6.2 nathanw
562 1.8.6.2 nathanw if (sc->sc_lcd_inited&BACKLIGHT_INITED)
563 1.8.6.2 nathanw return;
564 1.8.6.2 nathanw
565 1.8.6.2 nathanw if (config_hook_call(CONFIG_HOOK_GET,
566 1.8.6.2 nathanw CONFIG_HOOK_POWER_LCDLIGHT, &val) != -1) {
567 1.8.6.2 nathanw /* we can get real light state */
568 1.8.6.2 nathanw VPRINTF(("sed1356_init_backlight: real backlight=%d\n", val));
569 1.8.6.2 nathanw if (val == 0)
570 1.8.6.2 nathanw sc->sc_powerstate &= ~PWRSTAT_BACKLIGHT;
571 1.8.6.2 nathanw else
572 1.8.6.2 nathanw sc->sc_powerstate |= PWRSTAT_BACKLIGHT;
573 1.8.6.2 nathanw sc->sc_lcd_inited |= BACKLIGHT_INITED;
574 1.8.6.2 nathanw } else if (inattach) {
575 1.8.6.2 nathanw /*
576 1.8.6.2 nathanw we cannot get real light state in attach time
577 1.8.6.2 nathanw because light device not yet attached.
578 1.8.6.2 nathanw we will retry in !inattach.
579 1.8.6.2 nathanw temporary assume light is on.
580 1.8.6.2 nathanw */
581 1.8.6.2 nathanw sc->sc_powerstate |= PWRSTAT_BACKLIGHT;
582 1.8.6.2 nathanw } else {
583 1.8.6.2 nathanw /* we cannot get real light state, so work by myself state */
584 1.8.6.2 nathanw sc->sc_lcd_inited |= BACKLIGHT_INITED;
585 1.8.6.2 nathanw }
586 1.8.6.2 nathanw }
587 1.8.6.2 nathanw
588 1.8.6.2 nathanw void
589 1.8.6.2 nathanw sed1356_init_brightness(struct sed1356_softc *sc, int inattach)
590 1.8.6.2 nathanw {
591 1.8.6.2 nathanw int val = -1;
592 1.8.6.2 nathanw
593 1.8.6.2 nathanw if (sc->sc_lcd_inited&BRIGHTNESS_INITED)
594 1.8.6.2 nathanw return;
595 1.8.6.2 nathanw
596 1.8.6.2 nathanw VPRINTF(("sed1356_init_brightness\n"));
597 1.8.6.2 nathanw if (config_hook_call(CONFIG_HOOK_GET,
598 1.8.6.2 nathanw CONFIG_HOOK_BRIGHTNESS_MAX, &val) != -1) {
599 1.8.6.2 nathanw /* we can get real brightness max */
600 1.8.6.2 nathanw VPRINTF(("sed1356_init_brightness: real brightness max=%d\n", val));
601 1.8.6.2 nathanw sc->sc_max_brightness = val;
602 1.8.6.2 nathanw val = -1;
603 1.8.6.2 nathanw if (config_hook_call(CONFIG_HOOK_GET,
604 1.8.6.2 nathanw CONFIG_HOOK_BRIGHTNESS, &val) != -1) {
605 1.8.6.2 nathanw /* we can get real brightness */
606 1.8.6.2 nathanw VPRINTF(("sed1356_init_brightness: real brightness=%d\n", val));
607 1.8.6.2 nathanw sc->sc_brightness_save = sc->sc_brightness = val;
608 1.8.6.2 nathanw } else {
609 1.8.6.2 nathanw sc->sc_brightness_save =
610 1.8.6.2 nathanw sc->sc_brightness = sc->sc_max_brightness;
611 1.8.6.2 nathanw }
612 1.8.6.2 nathanw sc->sc_lcd_inited |= BRIGHTNESS_INITED;
613 1.8.6.2 nathanw } else if (inattach) {
614 1.8.6.2 nathanw /*
615 1.8.6.2 nathanw we cannot get real brightness in attach time
616 1.8.6.2 nathanw because brightness device not yet attached.
617 1.8.6.2 nathanw we will retry in !inattach.
618 1.8.6.2 nathanw */
619 1.8.6.2 nathanw sc->sc_max_brightness = -1;
620 1.8.6.2 nathanw sc->sc_brightness = -1;
621 1.8.6.2 nathanw sc->sc_brightness_save = -1;
622 1.8.6.2 nathanw } else {
623 1.8.6.2 nathanw /* we cannot get real brightness */
624 1.8.6.2 nathanw sc->sc_lcd_inited |= BRIGHTNESS_INITED;
625 1.8.6.2 nathanw }
626 1.8.6.2 nathanw
627 1.8.6.2 nathanw return;
628 1.8.6.2 nathanw }
629 1.8.6.2 nathanw
630 1.8.6.2 nathanw void
631 1.8.6.2 nathanw sed1356_init_contrast(struct sed1356_softc *sc, int inattach)
632 1.8.6.2 nathanw {
633 1.8.6.2 nathanw int val = -1;
634 1.8.6.2 nathanw
635 1.8.6.2 nathanw if (sc->sc_lcd_inited&CONTRAST_INITED)
636 1.8.6.2 nathanw return;
637 1.8.6.2 nathanw
638 1.8.6.2 nathanw VPRINTF(("sed1356_init_contrast\n"));
639 1.8.6.2 nathanw if (config_hook_call(CONFIG_HOOK_GET,
640 1.8.6.2 nathanw CONFIG_HOOK_CONTRAST_MAX, &val) != -1) {
641 1.8.6.2 nathanw /* we can get real contrast max */
642 1.8.6.2 nathanw VPRINTF(("sed1356_init_contrast: real contrast max=%d\n", val));
643 1.8.6.2 nathanw sc->sc_max_contrast = val;
644 1.8.6.2 nathanw val = -1;
645 1.8.6.2 nathanw if (config_hook_call(CONFIG_HOOK_GET,
646 1.8.6.2 nathanw CONFIG_HOOK_CONTRAST, &val) != -1) {
647 1.8.6.2 nathanw /* we can get real contrast */
648 1.8.6.2 nathanw VPRINTF(("sed1356_init_contrast: real contrast=%d\n", val));
649 1.8.6.2 nathanw sc->sc_contrast = val;
650 1.8.6.2 nathanw } else {
651 1.8.6.2 nathanw sc->sc_contrast = sc->sc_max_contrast;
652 1.8.6.2 nathanw }
653 1.8.6.2 nathanw sc->sc_lcd_inited |= CONTRAST_INITED;
654 1.8.6.2 nathanw } else if (inattach) {
655 1.8.6.2 nathanw /*
656 1.8.6.2 nathanw we cannot get real contrast in attach time
657 1.8.6.2 nathanw because contrast device not yet attached.
658 1.8.6.2 nathanw we will retry in !inattach.
659 1.8.6.2 nathanw */
660 1.8.6.2 nathanw sc->sc_max_contrast = -1;
661 1.8.6.2 nathanw sc->sc_contrast = -1;
662 1.8.6.2 nathanw } else {
663 1.8.6.2 nathanw /* we cannot get real contrast */
664 1.8.6.2 nathanw sc->sc_lcd_inited |= CONTRAST_INITED;
665 1.8.6.2 nathanw }
666 1.8.6.2 nathanw
667 1.8.6.2 nathanw return;
668 1.8.6.2 nathanw }
669 1.8.6.2 nathanw
670 1.8.6.2 nathanw void
671 1.8.6.2 nathanw sed1356_set_brightness(struct sed1356_softc *sc, int val)
672 1.8.6.2 nathanw {
673 1.8.6.2 nathanw sc->sc_brightness = val;
674 1.8.6.2 nathanw
675 1.8.6.2 nathanw config_hook_call(CONFIG_HOOK_SET, CONFIG_HOOK_BRIGHTNESS, &val);
676 1.8.6.2 nathanw if (config_hook_call(CONFIG_HOOK_GET,
677 1.8.6.2 nathanw CONFIG_HOOK_BRIGHTNESS, &val) != -1) {
678 1.8.6.2 nathanw sc->sc_brightness = val;
679 1.8.6.2 nathanw }
680 1.8.6.2 nathanw }
681 1.8.6.2 nathanw
682 1.8.6.2 nathanw void
683 1.8.6.2 nathanw sed1356_set_contrast(struct sed1356_softc *sc, int val)
684 1.8.6.2 nathanw {
685 1.8.6.2 nathanw sc->sc_contrast = val;
686 1.8.6.2 nathanw
687 1.8.6.2 nathanw config_hook_call(CONFIG_HOOK_SET, CONFIG_HOOK_CONTRAST, &val);
688 1.8.6.2 nathanw if (config_hook_call(CONFIG_HOOK_GET,
689 1.8.6.2 nathanw CONFIG_HOOK_CONTRAST, &val) != -1) {
690 1.8.6.2 nathanw sc->sc_contrast = val;
691 1.8.6.2 nathanw }
692 1.8.6.2 nathanw }
693