voyagerfb.c revision 1.8 1 /* $NetBSD: voyagerfb.c,v 1.8 2011/11/08 06:56:07 macallan Exp $ */
2
3 /*
4 * Copyright (c) 2009 Michael Lorenz
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 /*
29 * A console driver for Silicon Motion SM502 / Voyager GX graphics controllers
30 * tested on GDIUM only so far
31 */
32
33 #include <sys/cdefs.h>
34 __KERNEL_RCSID(0, "$NetBSD: voyagerfb.c,v 1.8 2011/11/08 06:56:07 macallan Exp $");
35
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/kernel.h>
39 #include <sys/device.h>
40 #include <sys/malloc.h>
41 #include <sys/lwp.h>
42 #include <sys/kauth.h>
43
44 #include <dev/videomode/videomode.h>
45
46 #include <dev/pci/pcivar.h>
47 #include <dev/pci/pcireg.h>
48 #include <dev/pci/pcidevs.h>
49 #include <dev/pci/pciio.h>
50 #include <dev/ic/sm502reg.h>
51
52 #include <dev/wscons/wsdisplayvar.h>
53 #include <dev/wscons/wsconsio.h>
54 #include <dev/wsfont/wsfont.h>
55 #include <dev/rasops/rasops.h>
56 #include <dev/wscons/wsdisplay_vconsvar.h>
57 #include <dev/pci/wsdisplay_pci.h>
58
59 #include <dev/i2c/i2cvar.h>
60 #include <dev/pci/voyagervar.h>
61
62 #ifdef VOYAGERFB_DEBUG
63 #define DPRINTF aprint_error
64 #else
65 #define DPRINTF while (0) printf
66 #endif
67
68 /* there are probably gdium-specific */
69 #define GPIO_BACKLIGHT 0x20000000
70
71 struct voyagerfb_softc {
72 device_t sc_dev;
73
74 pci_chipset_tag_t sc_pc;
75 pcitag_t sc_pcitag;
76 bus_space_tag_t sc_memt;
77
78 bus_space_handle_t sc_fbh;
79 bus_space_handle_t sc_regh;
80 bus_addr_t sc_fb, sc_reg;
81 bus_size_t sc_fbsize, sc_regsize;
82
83 int sc_width, sc_height, sc_depth, sc_stride;
84 int sc_locked;
85 void *sc_fbaddr;
86 struct vcons_screen sc_console_screen;
87 struct wsscreen_descr sc_defaultscreen_descr;
88 const struct wsscreen_descr *sc_screens[1];
89 struct wsscreen_list sc_screenlist;
90 struct vcons_data vd;
91 uint8_t *sc_dataport;
92 int sc_mode;
93 int sc_bl_on, sc_bl_level;
94 void *sc_gpio_cookie;
95
96 /* cursor stuff */
97 int sc_cur_x;
98 int sc_cur_y;
99 int sc_hot_x;
100 int sc_hot_y;
101 uint32_t sc_cursor_addr;
102 uint32_t *sc_cursor;
103
104 /* colour map */
105 u_char sc_cmap_red[256];
106 u_char sc_cmap_green[256];
107 u_char sc_cmap_blue[256];
108 };
109
110 static int voyagerfb_match(device_t, cfdata_t, void *);
111 static void voyagerfb_attach(device_t, device_t, void *);
112
113 CFATTACH_DECL_NEW(voyagerfb, sizeof(struct voyagerfb_softc),
114 voyagerfb_match, voyagerfb_attach, NULL, NULL);
115
116 extern const u_char rasops_cmap[768];
117
118 static int voyagerfb_ioctl(void *, void *, u_long, void *, int,
119 struct lwp *);
120 static paddr_t voyagerfb_mmap(void *, void *, off_t, int);
121 static void voyagerfb_init_screen(void *, struct vcons_screen *, int,
122 long *);
123
124 static int voyagerfb_putcmap(struct voyagerfb_softc *,
125 struct wsdisplay_cmap *);
126 static int voyagerfb_getcmap(struct voyagerfb_softc *,
127 struct wsdisplay_cmap *);
128 static void voyagerfb_restore_palette(struct voyagerfb_softc *);
129 static int voyagerfb_putpalreg(struct voyagerfb_softc *, int, uint8_t,
130 uint8_t, uint8_t);
131
132 static void voyagerfb_init(struct voyagerfb_softc *);
133
134 static void voyagerfb_rectfill(struct voyagerfb_softc *, int, int, int, int,
135 uint32_t);
136 static void voyagerfb_bitblt(struct voyagerfb_softc *, int, int, int, int,
137 int, int, int);
138
139 static void voyagerfb_cursor(void *, int, int, int);
140 static void voyagerfb_putchar(void *, int, int, u_int, long);
141 static void voyagerfb_copycols(void *, int, int, int, int);
142 static void voyagerfb_erasecols(void *, int, int, int, long);
143 static void voyagerfb_copyrows(void *, int, int, int);
144 static void voyagerfb_eraserows(void *, int, int, long);
145
146 static int voyagerfb_set_curpos(struct voyagerfb_softc *, int, int);
147 static int voyagerfb_gcursor(struct voyagerfb_softc *, struct wsdisplay_cursor *);
148 static int voyagerfb_scursor(struct voyagerfb_softc *, struct wsdisplay_cursor *);
149
150 struct wsdisplay_accessops voyagerfb_accessops = {
151 voyagerfb_ioctl,
152 voyagerfb_mmap,
153 NULL, /* alloc_screen */
154 NULL, /* free_screen */
155 NULL, /* show_screen */
156 NULL, /* load_font */
157 NULL, /* pollc */
158 NULL /* scroll */
159 };
160
161 static void voyagerfb_setup_backlight(struct voyagerfb_softc *);
162 static void voyagerfb_brightness_up(device_t);
163 static void voyagerfb_brightness_down(device_t);
164 /* set backlight level */
165 static void voyagerfb_set_backlight(struct voyagerfb_softc *, int);
166 /* turn backlight on and off without messing with the level */
167 static void voyagerfb_switch_backlight(struct voyagerfb_softc *, int);
168
169 /* wait for FIFO empty so we can feed it another command */
170 static inline void
171 voyagerfb_ready(struct voyagerfb_softc *sc)
172 {
173 do {} while ((bus_space_read_4(sc->sc_memt, sc->sc_regh,
174 SM502_SYSTEM_CTRL) & SM502_SYSCTL_FIFO_EMPTY) == 0);
175 }
176
177 /* wait for the drawing engine to be idle */
178 static inline void
179 voyagerfb_wait(struct voyagerfb_softc *sc)
180 {
181 do {} while ((bus_space_read_4(sc->sc_memt, sc->sc_regh,
182 SM502_SYSTEM_CTRL) & SM502_SYSCTL_ENGINE_BUSY) != 0);
183 }
184
185 static int
186 voyagerfb_match(device_t parent, cfdata_t match, void *aux)
187 {
188 struct voyager_attach_args *vaa = (struct voyager_attach_args *)aux;
189
190 if (strcmp(vaa->vaa_name, "voyagerfb") == 0) return 100;
191 return 0;
192 }
193
194 static void
195 voyagerfb_attach(device_t parent, device_t self, void *aux)
196 {
197 struct voyagerfb_softc *sc = device_private(self);
198 struct voyager_attach_args *vaa = aux;
199 struct rasops_info *ri;
200 struct wsemuldisplaydev_attach_args aa;
201 prop_dictionary_t dict;
202 unsigned long defattr;
203 uint32_t reg;
204 bool is_console;
205 int i, j;
206
207 sc->sc_pc = vaa->vaa_pc;
208 sc->sc_pcitag = vaa->vaa_pcitag;
209 sc->sc_memt = vaa->vaa_tag;
210 sc->sc_dev = self;
211
212 aprint_normal("\n");
213
214 dict = device_properties(self);
215 prop_dictionary_get_bool(dict, "is_console", &is_console);
216
217 sc->sc_fb = vaa->vaa_mem_pa;
218 sc->sc_fbh = vaa->vaa_memh;
219 sc->sc_fbsize = 16 * 1024 * 1024;
220 sc->sc_fbaddr = bus_space_vaddr(sc->sc_memt, sc->sc_fbh);
221
222 sc->sc_reg = vaa->vaa_reg_pa;
223 sc->sc_regh = vaa->vaa_regh;
224 sc->sc_regsize = 2 * 1024 * 1024;
225 sc->sc_dataport = bus_space_vaddr(sc->sc_memt, sc->sc_regh);
226 sc->sc_dataport += SM502_DATAPORT;
227
228 reg = bus_space_read_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_DISP_CTRL);
229 switch (reg & SM502_PDC_DEPTH_MASK) {
230 case SM502_PDC_8BIT:
231 sc->sc_depth = 8;
232 break;
233 case SM502_PDC_16BIT:
234 sc->sc_depth = 16;
235 break;
236 case SM502_PDC_32BIT:
237 sc->sc_depth = 24;
238 break;
239 default:
240 panic("%s: unsupported depth", device_xname(self));
241 }
242 sc->sc_stride = (bus_space_read_4(sc->sc_memt, sc->sc_regh,
243 SM502_PANEL_FB_OFFSET) & SM502_FBA_WIN_STRIDE_MASK) >> 16;
244 sc->sc_width = (bus_space_read_4(sc->sc_memt, sc->sc_regh,
245 SM502_PANEL_FB_WIDTH) & SM502_FBW_WIN_WIDTH_MASK) >> 16;
246 sc->sc_height = (bus_space_read_4(sc->sc_memt, sc->sc_regh,
247 SM502_PANEL_FB_HEIGHT) & SM502_FBH_WIN_HEIGHT_MASK) >> 16;
248
249 printf("%s: %d x %d, %d bit, stride %d\n", device_xname(self),
250 sc->sc_width, sc->sc_height, sc->sc_depth, sc->sc_stride);
251 /*
252 * XXX yeah, casting the fb address to uint32_t is formally wrong
253 * but as far as I know there are no SM502 with 64bit BARs
254 */
255 aprint_normal("%s: %d MB aperture at 0x%08x\n", device_xname(self),
256 (int)(sc->sc_fbsize >> 20), (uint32_t)sc->sc_fb);
257
258 sc->sc_defaultscreen_descr = (struct wsscreen_descr){
259 "default",
260 0, 0,
261 NULL,
262 8, 16,
263 WSSCREEN_WSCOLORS | WSSCREEN_HILIT,
264 NULL
265 };
266 sc->sc_screens[0] = &sc->sc_defaultscreen_descr;
267 sc->sc_screenlist = (struct wsscreen_list){1, sc->sc_screens};
268 sc->sc_mode = WSDISPLAYIO_MODE_EMUL;
269 sc->sc_locked = 0;
270
271 vcons_init(&sc->vd, sc, &sc->sc_defaultscreen_descr,
272 &voyagerfb_accessops);
273 sc->vd.init_screen = voyagerfb_init_screen;
274
275 /* backlight control */
276 sc->sc_gpio_cookie = device_private(parent);
277 voyagerfb_setup_backlight(sc);
278
279 /* init engine here */
280 sc->sc_depth = 8;
281 voyagerfb_init(sc);
282
283 ri = &sc->sc_console_screen.scr_ri;
284
285 if (is_console) {
286 vcons_init_screen(&sc->vd, &sc->sc_console_screen, 1,
287 &defattr);
288 sc->sc_console_screen.scr_flags |= VCONS_SCREEN_IS_STATIC;
289
290 sc->sc_defaultscreen_descr.textops = &ri->ri_ops;
291 sc->sc_defaultscreen_descr.capabilities = ri->ri_caps;
292 sc->sc_defaultscreen_descr.nrows = ri->ri_rows;
293 sc->sc_defaultscreen_descr.ncols = ri->ri_cols;
294 wsdisplay_cnattach(&sc->sc_defaultscreen_descr, ri, 0, 0,
295 defattr);
296 } else {
297 /*
298 * since we're not the console we can postpone the rest
299 * until someone actually allocates a screen for us
300 */
301 (*ri->ri_ops.allocattr)(ri, 0, 0, 0, &defattr);
302 }
303
304 j = 0;
305 if (sc->sc_depth <= 8) {
306 for (i = 0; i < (1 << sc->sc_depth); i++) {
307
308 sc->sc_cmap_red[i] = rasops_cmap[j];
309 sc->sc_cmap_green[i] = rasops_cmap[j + 1];
310 sc->sc_cmap_blue[i] = rasops_cmap[j + 2];
311 voyagerfb_putpalreg(sc, i, rasops_cmap[j],
312 rasops_cmap[j + 1], rasops_cmap[j + 2]);
313 j += 3;
314 }
315 }
316
317 voyagerfb_rectfill(sc, 0, 0, sc->sc_width, sc->sc_height,
318 ri->ri_devcmap[(defattr >> 16) & 0xff]);
319
320 if (is_console)
321 vcons_replay_msgbuf(&sc->sc_console_screen);
322
323 aa.console = is_console;
324 aa.scrdata = &sc->sc_screenlist;
325 aa.accessops = &voyagerfb_accessops;
326 aa.accesscookie = &sc->vd;
327
328 config_found(sc->sc_dev, &aa, wsemuldisplaydevprint);
329 }
330
331 static int
332 voyagerfb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag,
333 struct lwp *l)
334 {
335 struct vcons_data *vd = v;
336 struct voyagerfb_softc *sc = vd->cookie;
337 struct wsdisplay_fbinfo *wdf;
338 struct vcons_screen *ms = vd->active;
339 struct wsdisplay_param *param;
340
341 switch (cmd) {
342 case WSDISPLAYIO_GTYPE:
343 *(u_int *)data = WSDISPLAY_TYPE_PCIMISC;
344 return 0;
345
346 /* PCI config read/write passthrough. */
347 case PCI_IOC_CFGREAD:
348 case PCI_IOC_CFGWRITE:
349 return pci_devioctl(sc->sc_pc, sc->sc_pcitag,
350 cmd, data, flag, l);
351
352 case WSDISPLAYIO_GET_BUSID:
353 return wsdisplayio_busid_pci(device_parent(sc->sc_dev),
354 sc->sc_pc, sc->sc_pcitag, data);
355
356 case WSDISPLAYIO_GINFO:
357 if (ms == NULL)
358 return ENODEV;
359 wdf = (void *)data;
360 wdf->height = ms->scr_ri.ri_height;
361 wdf->width = ms->scr_ri.ri_width;
362 wdf->depth = ms->scr_ri.ri_depth;
363 wdf->cmsize = 256;
364 return 0;
365
366 case WSDISPLAYIO_GETCMAP:
367 return voyagerfb_getcmap(sc,
368 (struct wsdisplay_cmap *)data);
369
370 case WSDISPLAYIO_PUTCMAP:
371 return voyagerfb_putcmap(sc,
372 (struct wsdisplay_cmap *)data);
373
374 case WSDISPLAYIO_LINEBYTES:
375 *(u_int *)data = sc->sc_stride;
376 return 0;
377
378 case WSDISPLAYIO_SMODE: {
379 int new_mode = *(int*)data;
380 if (new_mode != sc->sc_mode) {
381 sc->sc_mode = new_mode;
382 if(new_mode == WSDISPLAYIO_MODE_EMUL) {
383 voyagerfb_restore_palette(sc);
384 vcons_redraw_screen(ms);
385 }
386 }
387 }
388 return 0;
389
390 case WSDISPLAYIO_GVIDEO:
391 *(int *)data = sc->sc_bl_on ? WSDISPLAYIO_VIDEO_ON :
392 WSDISPLAYIO_VIDEO_OFF;
393 return 0;
394
395 case WSDISPLAYIO_SVIDEO: {
396 int new_bl = *(int *)data;
397
398 voyagerfb_switch_backlight(sc, new_bl);
399 }
400 return 0;
401
402 case WSDISPLAYIO_GETPARAM:
403 param = (struct wsdisplay_param *)data;
404 switch (param->param) {
405 case WSDISPLAYIO_PARAM_BRIGHTNESS:
406 param->min = 0;
407 param->max = 255;
408 param->curval = sc->sc_bl_level;
409 return 0;
410 case WSDISPLAYIO_PARAM_BACKLIGHT:
411 param->min = 0;
412 param->max = 1;
413 param->curval = sc->sc_bl_on;
414 return 0;
415 }
416 return EPASSTHROUGH;
417
418 case WSDISPLAYIO_SETPARAM:
419 param = (struct wsdisplay_param *)data;
420 switch (param->param) {
421 case WSDISPLAYIO_PARAM_BRIGHTNESS:
422 voyagerfb_set_backlight(sc, param->curval);
423 return 0;
424 case WSDISPLAYIO_PARAM_BACKLIGHT:
425 voyagerfb_switch_backlight(sc, param->curval);
426 return 0;
427 }
428 return EPASSTHROUGH;
429
430 case WSDISPLAYIO_GCURPOS:
431 {
432 struct wsdisplay_curpos *pos;
433
434 pos = (struct wsdisplay_curpos *)data;
435 pos->x = sc->sc_cur_x;
436 pos->y = sc->sc_cur_y;
437 }
438 return 0;
439
440 case WSDISPLAYIO_SCURPOS:
441 {
442 struct wsdisplay_curpos *pos;
443
444 pos = (struct wsdisplay_curpos *)data;
445 voyagerfb_set_curpos(sc, pos->x, pos->y);
446 }
447 return 0;
448
449 case WSDISPLAYIO_GCURMAX:
450 {
451 struct wsdisplay_curpos *pos;
452
453 pos = (struct wsdisplay_curpos *)data;
454 pos->x = 64;
455 pos->y = 64;
456 }
457 return 0;
458
459 case WSDISPLAYIO_GCURSOR:
460 {
461 struct wsdisplay_cursor *cu;
462
463 cu = (struct wsdisplay_cursor *)data;
464 return voyagerfb_gcursor(sc, cu);
465 }
466
467 case WSDISPLAYIO_SCURSOR:
468 {
469 struct wsdisplay_cursor *cu;
470
471 cu = (struct wsdisplay_cursor *)data;
472 return voyagerfb_scursor(sc, cu);
473 }
474 }
475 return EPASSTHROUGH;
476 }
477
478 static paddr_t
479 voyagerfb_mmap(void *v, void *vs, off_t offset, int prot)
480 {
481 struct vcons_data *vd = v;
482 struct voyagerfb_softc *sc = vd->cookie;
483 paddr_t pa;
484
485 /* 'regular' framebuffer mmap()ing */
486 if (offset < sc->sc_fbsize) {
487 pa = bus_space_mmap(sc->sc_memt, sc->sc_fb + offset, 0, prot,
488 BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE);
489 return pa;
490 }
491
492 /*
493 * restrict all other mappings to processes with superuser privileges
494 * or the kernel itself
495 */
496 if (kauth_authorize_generic(kauth_cred_get(), KAUTH_GENERIC_ISSUSER,
497 NULL) != 0) {
498 aprint_normal("%s: mmap() rejected.\n",
499 device_xname(sc->sc_dev));
500 return -1;
501 }
502
503 if ((offset >= sc->sc_fb) && (offset < (sc->sc_fb + sc->sc_fbsize))) {
504 pa = bus_space_mmap(sc->sc_memt, offset, 0, prot,
505 BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE);
506 return pa;
507 }
508
509 if ((offset >= sc->sc_reg) &&
510 (offset < (sc->sc_reg + sc->sc_regsize))) {
511 pa = bus_space_mmap(sc->sc_memt, offset, 0, prot, 0);
512 return pa;
513 }
514
515 return -1;
516 }
517
518 static void
519 voyagerfb_init_screen(void *cookie, struct vcons_screen *scr,
520 int existing, long *defattr)
521 {
522 struct voyagerfb_softc *sc = cookie;
523 struct rasops_info *ri = &scr->scr_ri;
524
525 ri->ri_depth = sc->sc_depth;
526 ri->ri_width = sc->sc_width;
527 ri->ri_height = sc->sc_height;
528 ri->ri_stride = sc->sc_stride;
529 ri->ri_flg = RI_CENTER | RI_FULLCLEAR;
530
531 ri->ri_bits = (char *)sc->sc_fbaddr;
532
533 if (existing) {
534 ri->ri_flg |= RI_CLEAR;
535 }
536
537 rasops_init(ri, sc->sc_height / 8, sc->sc_width / 8);
538 ri->ri_caps = WSSCREEN_WSCOLORS;
539
540 rasops_reconfig(ri, sc->sc_height / ri->ri_font->fontheight,
541 sc->sc_width / ri->ri_font->fontwidth);
542
543 ri->ri_hw = scr;
544 ri->ri_ops.copyrows = voyagerfb_copyrows;
545 ri->ri_ops.copycols = voyagerfb_copycols;
546 ri->ri_ops.eraserows = voyagerfb_eraserows;
547 ri->ri_ops.erasecols = voyagerfb_erasecols;
548 ri->ri_ops.cursor = voyagerfb_cursor;
549 ri->ri_ops.putchar = voyagerfb_putchar;
550 }
551
552 static int
553 voyagerfb_putcmap(struct voyagerfb_softc *sc, struct wsdisplay_cmap *cm)
554 {
555 u_char *r, *g, *b;
556 u_int index = cm->index;
557 u_int count = cm->count;
558 int i, error;
559 u_char rbuf[256], gbuf[256], bbuf[256];
560
561 #ifdef VOYAGERFB_DEBUG
562 aprint_debug("putcmap: %d %d\n",index, count);
563 #endif
564 if (cm->index >= 256 || cm->count > 256 ||
565 (cm->index + cm->count) > 256)
566 return EINVAL;
567 error = copyin(cm->red, &rbuf[index], count);
568 if (error)
569 return error;
570 error = copyin(cm->green, &gbuf[index], count);
571 if (error)
572 return error;
573 error = copyin(cm->blue, &bbuf[index], count);
574 if (error)
575 return error;
576
577 memcpy(&sc->sc_cmap_red[index], &rbuf[index], count);
578 memcpy(&sc->sc_cmap_green[index], &gbuf[index], count);
579 memcpy(&sc->sc_cmap_blue[index], &bbuf[index], count);
580
581 r = &sc->sc_cmap_red[index];
582 g = &sc->sc_cmap_green[index];
583 b = &sc->sc_cmap_blue[index];
584
585 for (i = 0; i < count; i++) {
586 voyagerfb_putpalreg(sc, index, *r, *g, *b);
587 index++;
588 r++, g++, b++;
589 }
590 return 0;
591 }
592
593 static int
594 voyagerfb_getcmap(struct voyagerfb_softc *sc, struct wsdisplay_cmap *cm)
595 {
596 u_int index = cm->index;
597 u_int count = cm->count;
598 int error;
599
600 if (index >= 255 || count > 256 || index + count > 256)
601 return EINVAL;
602
603 error = copyout(&sc->sc_cmap_red[index], cm->red, count);
604 if (error)
605 return error;
606 error = copyout(&sc->sc_cmap_green[index], cm->green, count);
607 if (error)
608 return error;
609 error = copyout(&sc->sc_cmap_blue[index], cm->blue, count);
610 if (error)
611 return error;
612
613 return 0;
614 }
615
616 static void
617 voyagerfb_restore_palette(struct voyagerfb_softc *sc)
618 {
619 int i;
620
621 for (i = 0; i < (1 << sc->sc_depth); i++) {
622 voyagerfb_putpalreg(sc, i, sc->sc_cmap_red[i],
623 sc->sc_cmap_green[i], sc->sc_cmap_blue[i]);
624 }
625 }
626
627 static int
628 voyagerfb_putpalreg(struct voyagerfb_softc *sc, int idx, uint8_t r,
629 uint8_t g, uint8_t b)
630 {
631 uint32_t reg;
632
633 reg = (r << 16) | (g << 8) | b;
634 /* XXX we should probably write the CRT palette too */
635 bus_space_write_4(sc->sc_memt, sc->sc_regh,
636 SM502_PALETTE_PANEL + (idx << 2), reg);
637 return 0;
638 }
639
640 static void
641 voyagerfb_init(struct voyagerfb_softc *sc)
642 {
643 int reg;
644
645 voyagerfb_wait(sc);
646 /* disable colour compare */
647 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_COLOR_COMP_MASK, 0);
648 /* allow writes to all planes */
649 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PLANEMASK,
650 0xffffffff);
651 /* disable clipping */
652 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_CLIP_TOP_LEFT, 0);
653 /* source and destination in local memory, no offset */
654 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_SRC_BASE, 0);
655 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_DST_BASE, 0);
656 /* pitch is screen stride */
657 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PITCH,
658 sc->sc_width | (sc->sc_width << 16));
659 /* window is screen width */
660 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_WINDOW_WIDTH,
661 sc->sc_width | (sc->sc_width << 16));
662 reg = bus_space_read_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_DISP_CTRL);
663 reg &= ~SM502_PDC_DEPTH_MASK;
664
665 switch (sc->sc_depth) {
666 case 8:
667 bus_space_write_4(sc->sc_memt, sc->sc_regh,
668 SM502_STRETCH, SM502_STRETCH_8BIT);
669 sc->sc_stride = sc->sc_width;
670 reg |= SM502_PDC_8BIT;
671 break;
672 case 16:
673 bus_space_write_4(sc->sc_memt, sc->sc_regh,
674 SM502_STRETCH, SM502_STRETCH_16BIT);
675 sc->sc_stride = sc->sc_width << 1;
676 reg |= SM502_PDC_16BIT;
677 break;
678 case 24:
679 case 32:
680 bus_space_write_4(sc->sc_memt, sc->sc_regh,
681 SM502_STRETCH, SM502_STRETCH_32BIT);
682 sc->sc_stride = sc->sc_width << 2;
683 reg |= SM502_PDC_32BIT;
684 break;
685 }
686 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_FB_OFFSET,
687 (sc->sc_stride << 16) | sc->sc_stride);
688 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_DISP_CTRL,
689 reg);
690
691 /* put the cursor at the end of video memory */
692 sc->sc_cursor_addr = 16 * 1024 * 1024 - 16 * 64; /* XXX */
693 DPRINTF("%s: %08x\n", __func__, sc->sc_cursor_addr);
694 sc->sc_cursor = (uint32_t *)((uint8_t *)bus_space_vaddr(sc->sc_memt, sc->sc_fbh)
695 + sc->sc_cursor_addr);
696 #ifdef VOYAGERFB_DEBUG
697 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_CRSR_XY, 0x00100010);
698 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_CRSR_COL12, 0x0000ffff);
699 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_CRSR_COL3, 0x0000f800);
700 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_CRSR_ADDR,
701 SM502_CRSR_ENABLE | sc->sc_cursor_addr);
702 sc->sc_cursor[0] = 0x00000000;
703 sc->sc_cursor[1] = 0x00000000;
704 sc->sc_cursor[2] = 0xffffffff;
705 sc->sc_cursor[3] = 0xffffffff;
706 sc->sc_cursor[4] = 0xaaaaaaaa;
707 sc->sc_cursor[5] = 0xaaaaaaaa;
708 sc->sc_cursor[6] = 0xffffffff;
709 sc->sc_cursor[7] = 0x00000000;
710 #else
711 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_CRSR_ADDR,
712 sc->sc_cursor_addr);
713 #endif
714 }
715
716 static void
717 voyagerfb_rectfill(struct voyagerfb_softc *sc, int x, int y, int wi, int he,
718 uint32_t colour)
719 {
720
721 voyagerfb_ready(sc);
722 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_CONTROL,
723 ROP_COPY |
724 SM502_CTRL_USE_ROP2 |
725 SM502_CTRL_CMD_RECTFILL |
726 SM502_CTRL_QUICKSTART_E);
727 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_FOREGROUND,
728 colour);
729 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_DST,
730 (x << 16) | y);
731 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_DIMENSION,
732 (wi << 16) | he);
733 }
734
735 static void
736 voyagerfb_bitblt(struct voyagerfb_softc *sc, int xs, int ys, int xd, int yd,
737 int wi, int he, int rop)
738 {
739 uint32_t cmd;
740
741 cmd = (rop & 0xf) | SM502_CTRL_USE_ROP2 | SM502_CTRL_CMD_BITBLT |
742 SM502_CTRL_QUICKSTART_E;
743
744 voyagerfb_ready(sc);
745
746 if (xd <= xs) {
747 /* left to right */
748 } else {
749 /*
750 * According to the manual this flag reverses only the blitter's
751 * X direction. At least on my Gdium it also reverses the Y
752 * direction
753 */
754 cmd |= SM502_CTRL_R_TO_L;
755 xs += wi - 1;
756 xd += wi - 1;
757 ys += he - 1;
758 yd += he - 1;
759 }
760
761 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_CONTROL, cmd);
762 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_SRC,
763 (xs << 16) | ys);
764 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_DST,
765 (xd << 16) | yd);
766 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_DIMENSION,
767 (wi << 16) | he);
768 }
769
770 static void
771 voyagerfb_cursor(void *cookie, int on, int row, int col)
772 {
773 struct rasops_info *ri = cookie;
774 struct vcons_screen *scr = ri->ri_hw;
775 struct voyagerfb_softc *sc = scr->scr_cookie;
776 int x, y, wi, he;
777
778 wi = ri->ri_font->fontwidth;
779 he = ri->ri_font->fontheight;
780
781 if (sc->sc_mode == WSDISPLAYIO_MODE_EMUL) {
782 x = ri->ri_ccol * wi + ri->ri_xorigin;
783 y = ri->ri_crow * he + ri->ri_yorigin;
784 if (ri->ri_flg & RI_CURSOR) {
785 voyagerfb_bitblt(sc, x, y, x, y, wi, he, ROP_INVERT);
786 ri->ri_flg &= ~RI_CURSOR;
787 }
788 ri->ri_crow = row;
789 ri->ri_ccol = col;
790 if (on) {
791 x = ri->ri_ccol * wi + ri->ri_xorigin;
792 y = ri->ri_crow * he + ri->ri_yorigin;
793 voyagerfb_bitblt(sc, x, y, x, y, wi, he, ROP_INVERT);
794 ri->ri_flg |= RI_CURSOR;
795 }
796 } else {
797 scr->scr_ri.ri_crow = row;
798 scr->scr_ri.ri_ccol = col;
799 scr->scr_ri.ri_flg &= ~RI_CURSOR;
800 }
801
802 }
803
804 static inline void
805 voyagerfb_feed8(struct voyagerfb_softc *sc, uint8_t *data, int len)
806 {
807 uint32_t *port = (uint32_t *)sc->sc_dataport;
808 int i;
809
810 for (i = 0; i < ((len + 3) & 0xfffc); i++) {
811 *port = *data;
812 data++;
813 }
814 }
815
816 static inline void
817 voyagerfb_feed16(struct voyagerfb_softc *sc, uint16_t *data, int len)
818 {
819 uint32_t *port = (uint32_t *)sc->sc_dataport;
820 int i;
821
822 len = len << 1;
823 for (i = 0; i < ((len + 1) & 0xfffe); i++) {
824 *port = *data;
825 data++;
826 }
827 }
828
829
830 static void
831 voyagerfb_putchar(void *cookie, int row, int col, u_int c, long attr)
832 {
833 struct rasops_info *ri = cookie;
834 struct wsdisplay_font *font = PICK_FONT(ri, c);
835 struct vcons_screen *scr = ri->ri_hw;
836 struct voyagerfb_softc *sc = scr->scr_cookie;
837 uint32_t cmd;
838
839 if (sc->sc_mode == WSDISPLAYIO_MODE_EMUL) {
840 int fg, bg, uc;
841 uint8_t *data;
842 int x, y, wi, he;
843 wi = font->fontwidth;
844 he = font->fontheight;
845
846 if (!CHAR_IN_FONT(c, font))
847 return;
848 bg = ri->ri_devcmap[(attr >> 16) & 0x0f];
849 fg = ri->ri_devcmap[(attr >> 24) & 0x0f];
850 x = ri->ri_xorigin + col * wi;
851 y = ri->ri_yorigin + row * he;
852 if (c == 0x20) {
853 voyagerfb_rectfill(sc, x, y, wi, he, bg);
854 } else {
855 uc = c - font->firstchar;
856 data = (uint8_t *)font->data + uc * ri->ri_fontscale;
857 cmd = ROP_COPY |
858 SM502_CTRL_USE_ROP2 |
859 SM502_CTRL_CMD_HOSTWRT |
860 SM502_CTRL_HOSTBLT_MONO |
861 SM502_CTRL_QUICKSTART_E |
862 SM502_CTRL_MONO_PACK_32BIT;
863 voyagerfb_ready(sc);
864 bus_space_write_4(sc->sc_memt, sc->sc_regh,
865 SM502_CONTROL, cmd);
866 bus_space_write_4(sc->sc_memt, sc->sc_regh,
867 SM502_FOREGROUND, fg);
868 bus_space_write_4(sc->sc_memt, sc->sc_regh,
869 SM502_BACKGROUND, bg);
870 bus_space_write_4(sc->sc_memt, sc->sc_regh,
871 SM502_SRC, 0);
872 bus_space_write_4(sc->sc_memt, sc->sc_regh,
873 SM502_DST, (x << 16) | y);
874 bus_space_write_4(sc->sc_memt, sc->sc_regh,
875 SM502_DIMENSION, (wi << 16) | he);
876 /* now feed the data, padded to 32bit */
877 switch (ri->ri_font->stride) {
878 case 1:
879 voyagerfb_feed8(sc, data, ri->ri_fontscale);
880 break;
881 case 2:
882 voyagerfb_feed16(sc, (uint16_t *)data,
883 ri->ri_fontscale);
884 break;
885
886 }
887 }
888 }
889 }
890
891 static void
892 voyagerfb_copycols(void *cookie, int row, int srccol, int dstcol, int ncols)
893 {
894 struct rasops_info *ri = cookie;
895 struct vcons_screen *scr = ri->ri_hw;
896 struct voyagerfb_softc *sc = scr->scr_cookie;
897 int32_t xs, xd, y, width, height;
898
899 if ((sc->sc_locked == 0) && (sc->sc_mode == WSDISPLAYIO_MODE_EMUL)) {
900 xs = ri->ri_xorigin + ri->ri_font->fontwidth * srccol;
901 xd = ri->ri_xorigin + ri->ri_font->fontwidth * dstcol;
902 y = ri->ri_yorigin + ri->ri_font->fontheight * row;
903 width = ri->ri_font->fontwidth * ncols;
904 height = ri->ri_font->fontheight;
905 voyagerfb_bitblt(sc, xs, y, xd, y, width, height, ROP_COPY);
906 }
907 }
908
909 static void
910 voyagerfb_erasecols(void *cookie, int row, int startcol, int ncols,
911 long fillattr)
912 {
913 struct rasops_info *ri = cookie;
914 struct vcons_screen *scr = ri->ri_hw;
915 struct voyagerfb_softc *sc = scr->scr_cookie;
916 int32_t x, y, width, height, fg, bg, ul;
917
918 if ((sc->sc_locked == 0) && (sc->sc_mode == WSDISPLAYIO_MODE_EMUL)) {
919 x = ri->ri_xorigin + ri->ri_font->fontwidth * startcol;
920 y = ri->ri_yorigin + ri->ri_font->fontheight * row;
921 width = ri->ri_font->fontwidth * ncols;
922 height = ri->ri_font->fontheight;
923 rasops_unpack_attr(fillattr, &fg, &bg, &ul);
924
925 voyagerfb_rectfill(sc, x, y, width, height, ri->ri_devcmap[bg]);
926 }
927 }
928
929 static void
930 voyagerfb_copyrows(void *cookie, int srcrow, int dstrow, int nrows)
931 {
932 struct rasops_info *ri = cookie;
933 struct vcons_screen *scr = ri->ri_hw;
934 struct voyagerfb_softc *sc = scr->scr_cookie;
935 int32_t x, ys, yd, width, height;
936 int i;
937
938 if ((sc->sc_locked == 0) && (sc->sc_mode == WSDISPLAYIO_MODE_EMUL)) {
939 x = ri->ri_xorigin;
940 ys = ri->ri_yorigin + ri->ri_font->fontheight * srcrow;
941 yd = ri->ri_yorigin + ri->ri_font->fontheight * dstrow;
942 width = ri->ri_emuwidth;
943 height = ri->ri_font->fontheight * nrows;
944 if ((nrows > 1) && (dstrow > srcrow)) {
945 /*
946 * the blitter can't do bottom-up copies so we have
947 * to copy line by line here
948 * should probably use a command sequence
949 */
950 ys += (height - ri->ri_font->fontheight);
951 yd += (height - ri->ri_font->fontheight);
952 for (i = 0; i < nrows; i++) {
953 voyagerfb_bitblt(sc, x, ys, x, yd, width,
954 ri->ri_font->fontheight, ROP_COPY);
955 ys -= ri->ri_font->fontheight;
956 yd -= ri->ri_font->fontheight;
957 }
958 } else
959 voyagerfb_bitblt(sc, x, ys, x, yd, width, height,
960 ROP_COPY);
961 }
962 }
963
964 static void
965 voyagerfb_eraserows(void *cookie, int row, int nrows, long fillattr)
966 {
967 struct rasops_info *ri = cookie;
968 struct vcons_screen *scr = ri->ri_hw;
969 struct voyagerfb_softc *sc = scr->scr_cookie;
970 int32_t x, y, width, height, fg, bg, ul;
971
972 if ((sc->sc_locked == 0) && (sc->sc_mode == WSDISPLAYIO_MODE_EMUL)) {
973 x = ri->ri_xorigin;
974 y = ri->ri_yorigin + ri->ri_font->fontheight * row;
975 width = ri->ri_emuwidth;
976 height = ri->ri_font->fontheight * nrows;
977 rasops_unpack_attr(fillattr, &fg, &bg, &ul);
978
979 voyagerfb_rectfill(sc, x, y, width, height, ri->ri_devcmap[bg]);
980 }
981 }
982
983 /* backlight control */
984 static void
985 voyagerfb_setup_backlight(struct voyagerfb_softc *sc)
986 {
987 /* switch the pin to gpio mode if it isn't already */
988 voyager_control_gpio(sc->sc_gpio_cookie, ~GPIO_BACKLIGHT, 0);
989 /* turn it on */
990 voyager_write_gpio(sc->sc_gpio_cookie, 0xffffffff, GPIO_BACKLIGHT);
991 sc->sc_bl_on = 1;
992 sc->sc_bl_level = 255;
993 pmf_event_register(sc->sc_dev, PMFE_DISPLAY_BRIGHTNESS_UP,
994 voyagerfb_brightness_up, TRUE);
995 pmf_event_register(sc->sc_dev, PMFE_DISPLAY_BRIGHTNESS_DOWN,
996 voyagerfb_brightness_down, TRUE);
997 }
998
999 static void
1000 voyagerfb_set_backlight(struct voyagerfb_softc *sc, int level)
1001 {
1002
1003 /*
1004 * should we do nothing when backlight is off, should we just store the
1005 * level and use it when turning back on or should we just flip sc_bl_on
1006 * and turn the backlight on?
1007 * For now turn it on so a crashed screensaver can't get the user stuck
1008 * with a dark screen as long as hotkeys work
1009 */
1010 if (level > 255) level = 255;
1011 if (level < 0) level = 0;
1012 if (level == sc->sc_bl_level)
1013 return;
1014 sc->sc_bl_level = level;
1015 if (sc->sc_bl_on == 0)
1016 sc->sc_bl_on = 1;
1017 /* and here we would actually muck with the hardware */
1018 if ((level == 0) || (level == 255)) {
1019 /* in these cases bypass the PWM and use the gpio */
1020 voyager_control_gpio(sc->sc_gpio_cookie, ~GPIO_BACKLIGHT, 0);
1021 if (level == 0) {
1022 voyager_write_gpio(sc->sc_gpio_cookie, ~GPIO_BACKLIGHT, 0);
1023 } else {
1024 voyager_write_gpio(sc->sc_gpio_cookie, 0xffffffff, GPIO_BACKLIGHT);
1025 }
1026 } else {
1027 uint32_t pwm;
1028
1029 pwm = voyager_set_pwm(20000, level * 1000 / 256);
1030 pwm |= SM502_PWM_ENABLE;
1031 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PWM0, pwm);
1032
1033 /* let the PWM take over */
1034 voyager_control_gpio(sc->sc_gpio_cookie, 0xffffffff, GPIO_BACKLIGHT);
1035 }
1036 }
1037
1038 static void
1039 voyagerfb_switch_backlight(struct voyagerfb_softc *sc, int on)
1040 {
1041
1042 if (on == sc->sc_bl_on)
1043 return;
1044 sc->sc_bl_on = on;
1045 if (on) {
1046 int level = sc->sc_bl_level;
1047
1048 sc->sc_bl_level = -1;
1049 voyagerfb_set_backlight(sc, level);
1050 } else {
1051 voyager_control_gpio(sc->sc_gpio_cookie, ~GPIO_BACKLIGHT, 0);
1052 voyager_write_gpio(sc->sc_gpio_cookie, ~GPIO_BACKLIGHT, 0);
1053 }
1054 }
1055
1056
1057 static void
1058 voyagerfb_brightness_up(device_t dev)
1059 {
1060 struct voyagerfb_softc *sc = device_private(dev);
1061
1062 voyagerfb_set_backlight(sc, sc->sc_bl_level + 8);
1063 }
1064
1065 static void
1066 voyagerfb_brightness_down(device_t dev)
1067 {
1068 struct voyagerfb_softc *sc = device_private(dev);
1069
1070 voyagerfb_set_backlight(sc, sc->sc_bl_level - 8);
1071 }
1072
1073 static int
1074 voyagerfb_set_curpos(struct voyagerfb_softc *sc, int x, int y)
1075 {
1076 uint32_t val;
1077 int xx, yy;
1078
1079 sc->sc_cur_x = x;
1080 sc->sc_cur_y = y;
1081
1082 xx = x - sc->sc_hot_x;
1083 yy = y - sc->sc_hot_y;
1084
1085 if (xx < 0) xx = abs(xx) | 0x800;
1086 if (yy < 0) yy = abs(yy) | 0x800;
1087
1088 val = (xx & 0xffff) | (yy << 16);
1089 bus_space_write_4(sc->sc_memt, sc->sc_regh, SM502_PANEL_CRSR_XY, val);
1090
1091 return 0;
1092 }
1093
1094 static int
1095 voyagerfb_gcursor(struct voyagerfb_softc *sc, struct wsdisplay_cursor *cur)
1096 {
1097 /* do nothing for now */
1098 return 0;
1099 }
1100
1101 static int
1102 voyagerfb_scursor(struct voyagerfb_softc *sc, struct wsdisplay_cursor *cur)
1103 {
1104 if (cur->which & WSDISPLAY_CURSOR_DOCUR) {
1105
1106 bus_space_write_4(sc->sc_memt, sc->sc_regh,
1107 SM502_PANEL_CRSR_ADDR,
1108 sc->sc_cursor_addr | (cur->enable ? SM502_CRSR_ENABLE : 0));
1109 DPRINTF("%s: %08x\n", __func__, sc->sc_cursor_addr);
1110 }
1111 if (cur->which & WSDISPLAY_CURSOR_DOHOT) {
1112
1113 sc->sc_hot_x = cur->hot.x;
1114 sc->sc_hot_y = cur->hot.y;
1115 }
1116 if (cur->which & WSDISPLAY_CURSOR_DOPOS) {
1117
1118 voyagerfb_set_curpos(sc, cur->pos.x, cur->pos.y);
1119 }
1120 if (cur->which & WSDISPLAY_CURSOR_DOCMAP) {
1121 int i, idx;
1122 uint32_t val;
1123
1124 for (i = 0; i < cur->cmap.count; i++) {
1125 val = ((cur->cmap.red[i] & 0xf8) << 8) |
1126 ((cur->cmap.green[i] & 0xfc) << 3) |
1127 ((cur->cmap.blue[i] & 0xf8) >> 3);
1128 idx = i + cur->cmap.index;
1129 bus_space_write_2(sc->sc_memt, sc->sc_regh,
1130 SM502_PANEL_CRSR_COL12 + (idx << 1),
1131 val);
1132 /*
1133 * if userland doesn't try to set the 3rd colour we
1134 * assume it expects an X11-style 2 colour cursor
1135 * X should be our main user anyway
1136 */
1137 if ((idx == 1) &&
1138 ((cur->cmap.count + cur->cmap.index) < 3)) {
1139 bus_space_write_2(sc->sc_memt, sc->sc_regh,
1140 SM502_PANEL_CRSR_COL3,
1141 val);
1142 }
1143 DPRINTF("%s: %d %04x\n", __func__, i + cur->cmap.index, val);
1144 }
1145 }
1146 if (cur->which & WSDISPLAY_CURSOR_DOSHAPE) {
1147
1148 int i, j, cnt = 0;
1149 uint32_t latch = 0, omask;
1150 uint8_t imask;
1151 DPRINTF("%s: %d %d\n", __func__, cur->size.x, cur->size.y);
1152 for (i = 0; i < 256; i++) {
1153 omask = 0x00000001;
1154 imask = 0x01;
1155 cur->image[cnt] &= cur->mask[cnt];
1156 for (j = 0; j < 8; j++) {
1157 if (cur->mask[cnt] & imask)
1158 latch |= omask;
1159 omask <<= 1;
1160 if (cur->image[cnt] & imask)
1161 latch |= omask;
1162 omask <<= 1;
1163 imask <<= 1;
1164 }
1165 cnt++;
1166 imask = 0x01;
1167 cur->image[cnt] &= cur->mask[cnt];
1168 for (j = 0; j < 8; j++) {
1169 if (cur->mask[cnt] & imask)
1170 latch |= omask;
1171 omask <<= 1;
1172 if (cur->image[cnt] & imask)
1173 latch |= omask;
1174 omask <<= 1;
1175 imask <<= 1;
1176 }
1177 cnt++;
1178 sc->sc_cursor[i] = latch;
1179 latch = 0;
1180 }
1181 }
1182 return 0;
1183 }
1184