1*1dc652efSthorpej /* $NetBSD: ffb.c,v 1.68 2023/12/20 05:33:58 thorpej Exp $ */
249f3f8ccSpetrov /* $OpenBSD: creator.c,v 1.20 2002/07/30 19:48:15 jason Exp $ */
349f3f8ccSpetrov
449f3f8ccSpetrov /*
549f3f8ccSpetrov * Copyright (c) 2002 Jason L. Wright (jason@thought.net)
649f3f8ccSpetrov * All rights reserved.
749f3f8ccSpetrov *
849f3f8ccSpetrov * Redistribution and use in source and binary forms, with or without
949f3f8ccSpetrov * modification, are permitted provided that the following conditions
1049f3f8ccSpetrov * are met:
1149f3f8ccSpetrov * 1. Redistributions of source code must retain the above copyright
1249f3f8ccSpetrov * notice, this list of conditions and the following disclaimer.
1349f3f8ccSpetrov * 2. Redistributions in binary form must reproduce the above copyright
1449f3f8ccSpetrov * notice, this list of conditions and the following disclaimer in the
1549f3f8ccSpetrov * documentation and/or other materials provided with the distribution.
1649f3f8ccSpetrov * 3. All advertising materials mentioning features or use of this software
1749f3f8ccSpetrov * must display the following acknowledgement:
1849f3f8ccSpetrov * This product includes software developed by Jason L. Wright
1949f3f8ccSpetrov * 4. The name of the author may not be used to endorse or promote products
2049f3f8ccSpetrov * derived from this software without specific prior written permission.
2149f3f8ccSpetrov *
2249f3f8ccSpetrov * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2349f3f8ccSpetrov * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
2449f3f8ccSpetrov * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2549f3f8ccSpetrov * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
2649f3f8ccSpetrov * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2749f3f8ccSpetrov * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
2849f3f8ccSpetrov * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2949f3f8ccSpetrov * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
3049f3f8ccSpetrov * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
3149f3f8ccSpetrov * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3249f3f8ccSpetrov * POSSIBILITY OF SUCH DAMAGE.
3349f3f8ccSpetrov */
3449f3f8ccSpetrov
35ed517291Slukem #include <sys/cdefs.h>
36*1dc652efSthorpej __KERNEL_RCSID(0, "$NetBSD: ffb.c,v 1.68 2023/12/20 05:33:58 thorpej Exp $");
37ed517291Slukem
3849f3f8ccSpetrov #include <sys/types.h>
3949f3f8ccSpetrov #include <sys/param.h>
4049f3f8ccSpetrov #include <sys/systm.h>
4149f3f8ccSpetrov #include <sys/kernel.h>
4249f3f8ccSpetrov #include <sys/device.h>
4349f3f8ccSpetrov #include <sys/conf.h>
44d2c9943bSmacallan #include <sys/ioctl.h>
45d2c9943bSmacallan #include <sys/mman.h>
4649f3f8ccSpetrov
47b6584574Sdyoung #include <sys/bus.h>
4849f3f8ccSpetrov #include <machine/autoconf.h>
4949f3f8ccSpetrov #include <machine/openfirm.h>
5021d2a323Smartin #include <machine/vmparam.h>
5149f3f8ccSpetrov
5249f3f8ccSpetrov #include <dev/wscons/wsconsio.h>
5321d2a323Smartin #include <dev/sun/fbio.h>
5421d2a323Smartin #include <dev/sun/fbvar.h>
5549f3f8ccSpetrov
5602fbfe26Smartin #include <dev/wsfont/wsfont.h>
5702fbfe26Smartin #include <dev/wscons/wsdisplay_vconsvar.h>
5802fbfe26Smartin
59d436f96fSjdc #include <prop/proplib.h>
60d436f96fSjdc
61342707d5Sjdc #include <dev/i2c/i2cvar.h>
62342707d5Sjdc #include <dev/i2c/i2c_bitbang.h>
63342707d5Sjdc #include <dev/i2c/ddcvar.h>
64342707d5Sjdc
6549f3f8ccSpetrov #include <sparc64/dev/ffbreg.h>
6649f3f8ccSpetrov #include <sparc64/dev/ffbvar.h>
6749f3f8ccSpetrov
689912ffbbSmacallan #include "opt_wsdisplay_compat.h"
699912ffbbSmacallan #include "opt_ffb.h"
709912ffbbSmacallan
71d2c9943bSmacallan #ifndef WS_DEFAULT_BG
72d2c9943bSmacallan /* Sun -> background should be white */
73d2c9943bSmacallan #define WS_DEFAULT_BG 0xf
74d2c9943bSmacallan #endif
75d2c9943bSmacallan
769912ffbbSmacallan #ifdef FFB_SYNC
779912ffbbSmacallan #define SYNC ffb_ras_wait(sc)
789912ffbbSmacallan #else
799912ffbbSmacallan #define SYNC
809912ffbbSmacallan #endif
819912ffbbSmacallan
82342707d5Sjdc /* Debugging */
83342707d5Sjdc #if !defined FFB_DEBUG
84342707d5Sjdc #define FFB_DEBUG 0
85342707d5Sjdc #endif
86342707d5Sjdc #define DPRINTF(x) if (ffb_debug) printf x
87342707d5Sjdc /* Patchable */
88342707d5Sjdc extern int ffb_debug;
89342707d5Sjdc #if FFB_DEBUG > 0
90342707d5Sjdc int ffb_debug = 1;
91342707d5Sjdc #else
92342707d5Sjdc int ffb_debug = 0;
93342707d5Sjdc #endif
94342707d5Sjdc
9521d2a323Smartin extern struct cfdriver ffb_cd;
9621d2a323Smartin
9749f3f8ccSpetrov struct wsscreen_descr ffb_stdscreen = {
98de28b235Sheas "sunffb",
9949f3f8ccSpetrov 0, 0, /* will be filled in -- XXX shouldn't, it's global. */
10049f3f8ccSpetrov 0,
10149f3f8ccSpetrov 0, 0,
102733da76fSmacallan WSSCREEN_REVERSE | WSSCREEN_WSCOLORS | WSSCREEN_UNDERLINE |
103733da76fSmacallan WSSCREEN_RESIZE,
1049384ee0dSmartin NULL /* modecookie */
10549f3f8ccSpetrov };
10649f3f8ccSpetrov
10749f3f8ccSpetrov const struct wsscreen_descr *ffb_scrlist[] = {
10849f3f8ccSpetrov &ffb_stdscreen,
10949f3f8ccSpetrov /* XXX other formats? */
11049f3f8ccSpetrov };
11149f3f8ccSpetrov
11249f3f8ccSpetrov struct wsscreen_list ffb_screenlist = {
11349f3f8ccSpetrov sizeof(ffb_scrlist) / sizeof(struct wsscreen_descr *),
11449f3f8ccSpetrov ffb_scrlist
11549f3f8ccSpetrov };
11649f3f8ccSpetrov
11702fbfe26Smartin static struct vcons_screen ffb_console_screen;
118d2c9943bSmacallan
11953524e44Schristos int ffb_ioctl(void *, void *, u_long, void *, int, struct lwp *);
120a60b59ccSheas static int ffb_blank(struct ffb_softc *, u_long, u_int *);
1217a51d4ddSjmmv paddr_t ffb_mmap(void *, void *, off_t, int);
12249f3f8ccSpetrov void ffb_ras_fifo_wait(struct ffb_softc *, int);
12349f3f8ccSpetrov void ffb_ras_wait(struct ffb_softc *);
12449f3f8ccSpetrov void ffb_ras_init(struct ffb_softc *);
12549f3f8ccSpetrov void ffb_ras_copyrows(void *, int, int, int);
12649f3f8ccSpetrov void ffb_ras_erasecols(void *, int, int, int, long int);
12749f3f8ccSpetrov void ffb_ras_eraserows(void *, int, int, long int);
12849f3f8ccSpetrov void ffb_ras_fill(struct ffb_softc *);
129b1dfce18Smacallan void ffb_ras_invert(struct ffb_softc *);
1309912ffbbSmacallan static void ffb_ras_setfg(struct ffb_softc *, int32_t);
1319912ffbbSmacallan static void ffb_ras_setbg(struct ffb_softc *, int32_t);
13249f3f8ccSpetrov
133d2c9943bSmacallan void ffb_clearscreen(struct ffb_softc *);
13402fbfe26Smartin void ffb_init_screen(void *, struct vcons_screen *, int,
135d2c9943bSmacallan long *);
136d2c9943bSmacallan int ffb_allocattr(void *, int, int, int, long *);
137b1dfce18Smacallan void ffb_putchar_mono(void *, int, int, u_int, long);
138b1dfce18Smacallan void ffb_putchar_aa(void *, int, int, u_int, long);
139d2c9943bSmacallan void ffb_cursor(void *, int, int, int);
140d2c9943bSmacallan
14121d2a323Smartin /* frame buffer generic driver */
142817999e9Schristos static void ffbfb_unblank(device_t);
14321d2a323Smartin dev_type_open(ffbfb_open);
14421d2a323Smartin dev_type_close(ffbfb_close);
14521d2a323Smartin dev_type_ioctl(ffbfb_ioctl);
14621d2a323Smartin dev_type_mmap(ffbfb_mmap);
147d2c9943bSmacallan
14821d2a323Smartin static struct fbdriver ffb_fbdriver = {
14921d2a323Smartin ffbfb_unblank, ffbfb_open, ffbfb_close, ffbfb_ioctl, nopoll,
15021d2a323Smartin ffbfb_mmap, nokqfilter
15121d2a323Smartin };
15221d2a323Smartin
15349f3f8ccSpetrov struct wsdisplay_accessops ffb_accessops = {
15402fbfe26Smartin .ioctl = ffb_ioctl,
15502fbfe26Smartin .mmap = ffb_mmap,
15649f3f8ccSpetrov };
15749f3f8ccSpetrov
158342707d5Sjdc /* I2C glue */
159342707d5Sjdc static int ffb_i2c_send_start(void *, int);
160342707d5Sjdc static int ffb_i2c_send_stop(void *, int);
161342707d5Sjdc static int ffb_i2c_initiate_xfer(void *, i2c_addr_t, int);
162342707d5Sjdc static int ffb_i2c_read_byte(void *, uint8_t *, int);
163342707d5Sjdc static int ffb_i2c_write_byte(void *, uint8_t, int);
164342707d5Sjdc
165342707d5Sjdc /* I2C bitbang glue */
166342707d5Sjdc static void ffb_i2cbb_set_bits(void *, uint32_t);
167342707d5Sjdc static void ffb_i2cbb_set_dir(void *, uint32_t);
168342707d5Sjdc static uint32_t ffb_i2cbb_read(void *);
169342707d5Sjdc
170342707d5Sjdc static const struct i2c_bitbang_ops ffb_i2cbb_ops = {
171342707d5Sjdc ffb_i2cbb_set_bits,
172342707d5Sjdc ffb_i2cbb_set_dir,
173342707d5Sjdc ffb_i2cbb_read,
174342707d5Sjdc {
175342707d5Sjdc FFB_DAC_CFG_MPDATA_SDA,
176342707d5Sjdc FFB_DAC_CFG_MPDATA_SCL,
177342707d5Sjdc 0,
178342707d5Sjdc 0
179342707d5Sjdc }
180342707d5Sjdc };
181342707d5Sjdc
182342707d5Sjdc void ffb_attach_i2c(struct ffb_softc *);
183342707d5Sjdc
184342707d5Sjdc /* Video mode setting */
185342707d5Sjdc int ffb_tgc_disable(struct ffb_softc *);
186342707d5Sjdc void ffb_get_pclk(int, uint32_t *, int *);
187342707d5Sjdc int ffb_set_vmode(struct ffb_softc *, struct videomode *, int, int *, int *);
188342707d5Sjdc
189342707d5Sjdc
19049f3f8ccSpetrov void
ffb_attach(device_t self)191d436f96fSjdc ffb_attach(device_t self)
19249f3f8ccSpetrov {
193d436f96fSjdc struct ffb_softc *sc = device_private(self);
19449f3f8ccSpetrov struct wsemuldisplaydev_attach_args waa;
195d2c9943bSmacallan struct rasops_info *ri;
196d2c9943bSmacallan long defattr;
197342707d5Sjdc const char *model, *out_dev;
19849f3f8ccSpetrov int btype;
19902fbfe26Smartin uint32_t dac;
200880e9bcaSmartin int maxrow;
201a60b59ccSheas u_int blank = WSDISPLAYIO_VIDEO_ON;
2026c325ce2Spk char buf[6+1];
203342707d5Sjdc int i, try_edid;
204d436f96fSjdc prop_data_t data;
20549f3f8ccSpetrov
20649f3f8ccSpetrov printf(":");
20749f3f8ccSpetrov
20849f3f8ccSpetrov if (sc->sc_type == FFB_CREATOR) {
209ea53363eSpk btype = prom_getpropint(sc->sc_node, "board_type", 0);
21049f3f8ccSpetrov if ((btype & 7) == 3)
21149f3f8ccSpetrov printf(" Creator3D");
21249f3f8ccSpetrov else
21349f3f8ccSpetrov printf(" Creator");
214342707d5Sjdc } else {
21549f3f8ccSpetrov printf(" Elite3D");
216342707d5Sjdc btype = 0;
217342707d5Sjdc }
21849f3f8ccSpetrov
219ea53363eSpk model = prom_getpropstring(sc->sc_node, "model");
22049f3f8ccSpetrov if (model == NULL || strlen(model) == 0)
22149f3f8ccSpetrov model = "unknown";
22249f3f8ccSpetrov
22349f3f8ccSpetrov sc->sc_depth = 24;
22449f3f8ccSpetrov sc->sc_linebytes = 8192;
225342707d5Sjdc /* We might alter these during EDID mode setting */
226ea53363eSpk sc->sc_height = prom_getpropint(sc->sc_node, "height", 0);
227ea53363eSpk sc->sc_width = prom_getpropint(sc->sc_node, "width", 0);
22849f3f8ccSpetrov
22902fbfe26Smartin sc->sc_locked = 0;
23002fbfe26Smartin sc->sc_mode = WSDISPLAYIO_MODE_EMUL;
23102fbfe26Smartin
23202fbfe26Smartin maxrow = (prom_getoption("screen-#rows", buf, sizeof buf) != 0)
2336c325ce2Spk ? strtoul(buf, NULL, 10)
2346c325ce2Spk : 34;
2356c325ce2Spk
23642027578Sheas /* collect DAC version, as Elite3D cursor enable bit is reversed */
237342707d5Sjdc DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_DEVID);
23802fbfe26Smartin dac = DAC_READ(sc, FFB_DAC_VALUE);
23902fbfe26Smartin sc->sc_dacrev = (dac >> 28) & 0xf;
24042027578Sheas
24102fbfe26Smartin if (sc->sc_type == FFB_AFB) {
24242027578Sheas sc->sc_dacrev = 10;
24302fbfe26Smartin sc->sc_needredraw = 0;
24402fbfe26Smartin } else {
24502fbfe26Smartin /* see what kind of DAC we have */
24602fbfe26Smartin int pnum = (dac & 0x0ffff000) >> 12;
24702fbfe26Smartin if (pnum == 0x236e) {
24802fbfe26Smartin sc->sc_needredraw = 0;
24902fbfe26Smartin } else {
25002fbfe26Smartin sc->sc_needredraw = 1;
25102fbfe26Smartin }
25202fbfe26Smartin }
25342027578Sheas printf(", model %s, dac %u\n", model, sc->sc_dacrev);
25402fbfe26Smartin if (sc->sc_needredraw)
25502fbfe26Smartin printf("%s: found old DAC, enabling redraw on unblank\n",
256817999e9Schristos device_xname(sc->sc_dev));
25742027578Sheas
258342707d5Sjdc /* Check if a console resolution "<device>:r<res>" is set. */
259342707d5Sjdc if (sc->sc_console) {
2606492be2aSjdc out_dev = prom_getpropstring(sc->sc_node, "output-device");
261342707d5Sjdc if (out_dev != NULL && strlen(out_dev) != 0 &&
262342707d5Sjdc strstr(out_dev, ":r") != NULL)
263342707d5Sjdc try_edid = 0;
264342707d5Sjdc else
265342707d5Sjdc try_edid = 1;
266342707d5Sjdc } else
267342707d5Sjdc try_edid = 1;
268342707d5Sjdc
269eb422932Smacallan #if FFB_DEBUG > 0
2703bdd0f03Smacallan DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_TGC);
2713bdd0f03Smacallan printf("tgc: %08x\n", DAC_READ(sc, FFB_DAC_VALUE));
2723bdd0f03Smacallan DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_DAC_CTRL);
2733bdd0f03Smacallan printf("dcl: %08x\n", DAC_READ(sc, FFB_DAC_VALUE));
2743bdd0f03Smacallan #endif
275342707d5Sjdc ffb_attach_i2c(sc);
276342707d5Sjdc
277342707d5Sjdc /* Need to set asynchronous blank during DDC write/read */
278342707d5Sjdc DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_USR_CTRL);
279342707d5Sjdc dac = DAC_READ(sc, FFB_DAC_VALUE);
280342707d5Sjdc DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_USR_CTRL);
281342707d5Sjdc DAC_WRITE(sc, FFB_DAC_VALUE, dac | FFB_DAC_USR_CTRL_BLANK);
282342707d5Sjdc
283342707d5Sjdc /* Some monitors don't respond first time */
284342707d5Sjdc i = 0;
285342707d5Sjdc while (sc->sc_edid_data[1] == 0 && i++ < 3)
286342707d5Sjdc ddc_read_edid(&sc->sc_i2c, sc->sc_edid_data, EDID_DATA_LEN);
287342707d5Sjdc
288342707d5Sjdc /* Remove asynchronous blank */
289342707d5Sjdc DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_USR_CTRL);
290342707d5Sjdc DAC_WRITE(sc, FFB_DAC_VALUE, dac);
291342707d5Sjdc
292342707d5Sjdc if (edid_parse(&sc->sc_edid_data[0], &sc->sc_edid_info) != -1) {
293342707d5Sjdc sort_modes(sc->sc_edid_info.edid_modes,
294342707d5Sjdc &sc->sc_edid_info.edid_preferred_mode,
295342707d5Sjdc sc->sc_edid_info.edid_nmodes);
296817999e9Schristos DPRINTF(("%s: EDID data:\n ", device_xname(sc->sc_dev)));
297342707d5Sjdc for (i = 0; i < EDID_DATA_LEN; i++) {
298342707d5Sjdc if (i && !(i % 32))
299342707d5Sjdc DPRINTF(("\n "));
300342707d5Sjdc if (i && !(i % 4))
301342707d5Sjdc DPRINTF((" "));
302342707d5Sjdc DPRINTF(("%02x", sc->sc_edid_data[i]));
303342707d5Sjdc }
304342707d5Sjdc DPRINTF(("\n"));
305342707d5Sjdc if (ffb_debug)
306342707d5Sjdc edid_print(&sc->sc_edid_info);
307342707d5Sjdc
30846b33b4bSmartin data = prop_data_create_copy(sc->sc_edid_data, EDID_DATA_LEN);
309d436f96fSjdc prop_dictionary_set(device_properties(self), "EDID", data);
310d436f96fSjdc prop_object_release(data);
311d436f96fSjdc
312342707d5Sjdc if (try_edid)
313342707d5Sjdc for (i = 0; i < sc->sc_edid_info.edid_nmodes; i++) {
314342707d5Sjdc if (ffb_set_vmode(sc,
315342707d5Sjdc &(sc->sc_edid_info.edid_modes[i]), btype,
316342707d5Sjdc &(sc->sc_width), &(sc->sc_height)))
317342707d5Sjdc break;
318342707d5Sjdc }
319342707d5Sjdc } else {
320817999e9Schristos DPRINTF(("%s: No EDID data.\n", device_xname(sc->sc_dev)));
321342707d5Sjdc }
322342707d5Sjdc
323342707d5Sjdc ffb_ras_init(sc);
324342707d5Sjdc
325a60b59ccSheas ffb_blank(sc, WSDISPLAYIO_SVIDEO, &blank);
326a60b59ccSheas
327817999e9Schristos sc->sc_accel = ((device_cfdata(sc->sc_dev)->cf_flags &
3282be6494fSthorpej FFB_CFFLAG_NOACCEL) == 0);
329d2c9943bSmacallan
330d2c9943bSmacallan wsfont_init();
331d2c9943bSmacallan
33202fbfe26Smartin vcons_init(&sc->vd, sc, &ffb_stdscreen, &ffb_accessops);
33302fbfe26Smartin sc->vd.init_screen = ffb_init_screen;
3344453737fSmacallan ri = &ffb_console_screen.scr_ri;
3351a8d6cd5Smartin
33602fbfe26Smartin /* we mess with ffb_console_screen only once */
3371a8d6cd5Smartin if (sc->sc_console) {
338733da76fSmacallan ffb_console_screen.scr_flags = VCONS_SCREEN_IS_STATIC;
33902fbfe26Smartin vcons_init_screen(&sc->vd, &ffb_console_screen, 1, &defattr);
34002fbfe26Smartin SCREEN_VISIBLE((&ffb_console_screen));
34102fbfe26Smartin /*
34202fbfe26Smartin * XXX we shouldn't use a global variable for the console
34302fbfe26Smartin * screen
34402fbfe26Smartin */
34502fbfe26Smartin sc->vd.active = &ffb_console_screen;
34602fbfe26Smartin } else {
34702fbfe26Smartin if (ffb_console_screen.scr_ri.ri_rows == 0) {
34802fbfe26Smartin /* do some minimal setup to avoid weirdnesses later */
34902fbfe26Smartin vcons_init_screen(&sc->vd, &ffb_console_screen, 1, &defattr);
3504453737fSmacallan } else
3514453737fSmacallan (*ri->ri_ops.allocattr)(ri, 0, 0, 0, &defattr);
35293bdac45Sjdc }
353d2c9943bSmacallan
354d2c9943bSmacallan ffb_stdscreen.nrows = ri->ri_rows;
355d2c9943bSmacallan ffb_stdscreen.ncols = ri->ri_cols;
356d2c9943bSmacallan ffb_stdscreen.textops = &ri->ri_ops;
357d2c9943bSmacallan
35821d2a323Smartin sc->sc_fb.fb_driver = &ffb_fbdriver;
35921d2a323Smartin sc->sc_fb.fb_type.fb_cmsize = 0;
36021d2a323Smartin sc->sc_fb.fb_type.fb_size = maxrow * sc->sc_linebytes;
36121d2a323Smartin sc->sc_fb.fb_type.fb_type = FBTYPE_CREATOR;
3629277bf83Smhitch sc->sc_fb.fb_type.fb_width = sc->sc_width;
3639277bf83Smhitch sc->sc_fb.fb_type.fb_depth = sc->sc_depth;
3649277bf83Smhitch sc->sc_fb.fb_type.fb_height = sc->sc_height;
365817999e9Schristos sc->sc_fb.fb_device = sc->sc_dev;
36621d2a323Smartin fb_attach(&sc->sc_fb, sc->sc_console);
36721d2a323Smartin
36845727432Smacallan ffb_clearscreen(sc);
36945727432Smacallan
37049f3f8ccSpetrov if (sc->sc_console) {
371d2c9943bSmacallan wsdisplay_cnattach(&ffb_stdscreen, ri, 0, 0, defattr);
37245727432Smacallan vcons_replay_msgbuf(&ffb_console_screen);
37349f3f8ccSpetrov }
37449f3f8ccSpetrov
37549f3f8ccSpetrov waa.console = sc->sc_console;
37649f3f8ccSpetrov waa.scrdata = &ffb_screenlist;
37749f3f8ccSpetrov waa.accessops = &ffb_accessops;
37802fbfe26Smartin waa.accesscookie = &sc->vd;
379c7fb772bSthorpej config_found(sc->sc_dev, &waa, wsemuldisplaydevprint, CFARGS_NONE);
38049f3f8ccSpetrov }
38149f3f8ccSpetrov
382342707d5Sjdc void
ffb_attach_i2c(struct ffb_softc * sc)383342707d5Sjdc ffb_attach_i2c(struct ffb_softc *sc)
384342707d5Sjdc {
385342707d5Sjdc
386342707d5Sjdc /* Fill in the i2c tag */
387601e1783Sthorpej iic_tag_init(&sc->sc_i2c);
388342707d5Sjdc sc->sc_i2c.ic_cookie = sc;
389342707d5Sjdc sc->sc_i2c.ic_send_start = ffb_i2c_send_start;
390342707d5Sjdc sc->sc_i2c.ic_send_stop = ffb_i2c_send_stop;
391342707d5Sjdc sc->sc_i2c.ic_initiate_xfer = ffb_i2c_initiate_xfer;
392342707d5Sjdc sc->sc_i2c.ic_read_byte = ffb_i2c_read_byte;
393342707d5Sjdc sc->sc_i2c.ic_write_byte = ffb_i2c_write_byte;
394342707d5Sjdc }
395342707d5Sjdc
39649f3f8ccSpetrov int
ffb_ioctl(void * v,void * vs,u_long cmd,void * data,int flags,struct lwp * l)39753524e44Schristos ffb_ioctl(void *v, void *vs, u_long cmd, void *data, int flags, struct lwp *l)
39849f3f8ccSpetrov {
39902fbfe26Smartin struct vcons_data *vd = v;
40002fbfe26Smartin struct ffb_softc *sc = vd->cookie;
40149f3f8ccSpetrov struct wsdisplay_fbinfo *wdf;
40202fbfe26Smartin struct vcons_screen *ms = vd->active;
40349f3f8ccSpetrov
404342707d5Sjdc DPRINTF(("ffb_ioctl: %s cmd _IO%s%s('%c', %lu)\n",
405817999e9Schristos device_xname(sc->sc_dev),
40649f3f8ccSpetrov (cmd & IOC_IN) ? "W" : "", (cmd & IOC_OUT) ? "R" : "",
407342707d5Sjdc (char)IOCGROUP(cmd), cmd & 0xff));
40849f3f8ccSpetrov
40949f3f8ccSpetrov switch (cmd) {
41021d2a323Smartin case FBIOGTYPE:
41121d2a323Smartin *(struct fbtype *)data = sc->sc_fb.fb_type;
41221d2a323Smartin break;
41321d2a323Smartin case FBIOGATTR:
41421d2a323Smartin #define fba ((struct fbgattr *)data)
41521d2a323Smartin fba->real_type = sc->sc_fb.fb_type.fb_type;
41621d2a323Smartin fba->owner = 0; /* XXX ??? */
41721d2a323Smartin fba->fbtype = sc->sc_fb.fb_type;
41821d2a323Smartin fba->sattr.flags = 0;
41921d2a323Smartin fba->sattr.emu_type = sc->sc_fb.fb_type.fb_type;
42021d2a323Smartin fba->sattr.dev_specific[0] = -1;
42121d2a323Smartin fba->emu_types[0] = sc->sc_fb.fb_type.fb_type;
42221d2a323Smartin fba->emu_types[1] = -1;
42321d2a323Smartin #undef fba
42421d2a323Smartin break;
42521d2a323Smartin
42621d2a323Smartin case FBIOGETCMAP:
42721d2a323Smartin case FBIOPUTCMAP:
42821d2a323Smartin return EIO;
42921d2a323Smartin
43021d2a323Smartin case FBIOGVIDEO:
43121d2a323Smartin case FBIOSVIDEO:
43221d2a323Smartin return ffb_blank(sc, cmd == FBIOGVIDEO?
43321d2a323Smartin WSDISPLAYIO_GVIDEO : WSDISPLAYIO_SVIDEO,
43421d2a323Smartin (u_int *)data);
43521d2a323Smartin break;
43621d2a323Smartin case FBIOGCURSOR:
43721d2a323Smartin case FBIOSCURSOR:
438de0502e5Smartin /* the console driver is not using the hardware cursor */
439de0502e5Smartin break;
44021d2a323Smartin case FBIOGCURPOS:
441817999e9Schristos printf("%s: FBIOGCURPOS not implemented\n",
442817999e9Schristos device_xname(sc->sc_dev));
44321d2a323Smartin return EIO;
44421d2a323Smartin case FBIOSCURPOS:
445817999e9Schristos printf("%s: FBIOSCURPOS not implemented\n",
446817999e9Schristos device_xname(sc->sc_dev));
44721d2a323Smartin return EIO;
44821d2a323Smartin case FBIOGCURMAX:
449817999e9Schristos printf("%s: FBIOGCURMAX not implemented\n",
450817999e9Schristos device_xname(sc->sc_dev));
45121d2a323Smartin return EIO;
45221d2a323Smartin
45349f3f8ccSpetrov case WSDISPLAYIO_GTYPE:
454de28b235Sheas *(u_int *)data = WSDISPLAY_TYPE_SUNFFB;
45549f3f8ccSpetrov break;
45649f3f8ccSpetrov case WSDISPLAYIO_SMODE:
457d2c9943bSmacallan {
458d2c9943bSmacallan if (sc->sc_mode != *(u_int *)data) {
45949f3f8ccSpetrov sc->sc_mode = *(u_int *)data;
46002fbfe26Smartin if ((sc->sc_mode == WSDISPLAYIO_MODE_EMUL) &&
46102fbfe26Smartin (sc->sc_locked == 0)) {
462d2c9943bSmacallan ffb_ras_init(sc);
46302fbfe26Smartin vcons_redraw_screen(ms);
464877740feSmacallan } else {
465877740feSmacallan ffb_ras_wait(sc);
466d2c9943bSmacallan }
467d2c9943bSmacallan }
468d2c9943bSmacallan }
46949f3f8ccSpetrov break;
47049f3f8ccSpetrov case WSDISPLAYIO_GINFO:
47149f3f8ccSpetrov wdf = (void *)data;
47249f3f8ccSpetrov wdf->height = sc->sc_height;
47349f3f8ccSpetrov wdf->width = sc->sc_width;
47449f3f8ccSpetrov wdf->depth = 32;
47549f3f8ccSpetrov wdf->cmsize = 256; /* XXX */
47649f3f8ccSpetrov break;
47749f3f8ccSpetrov #ifdef WSDISPLAYIO_LINEBYTES
47849f3f8ccSpetrov case WSDISPLAYIO_LINEBYTES:
47949f3f8ccSpetrov *(u_int *)data = sc->sc_linebytes;
48049f3f8ccSpetrov break;
48149f3f8ccSpetrov #endif
48249f3f8ccSpetrov case WSDISPLAYIO_GETCMAP:
48349f3f8ccSpetrov break;/* XXX */
48449f3f8ccSpetrov
48549f3f8ccSpetrov case WSDISPLAYIO_PUTCMAP:
48649f3f8ccSpetrov break;/* XXX */
48749f3f8ccSpetrov
48849f3f8ccSpetrov case WSDISPLAYIO_SVIDEO:
48949f3f8ccSpetrov case WSDISPLAYIO_GVIDEO:
490a60b59ccSheas return(ffb_blank(sc, cmd, (u_int *)data));
491a60b59ccSheas break;
492da28d6ecSmacallan
49349f3f8ccSpetrov case WSDISPLAYIO_GCURPOS:
49449f3f8ccSpetrov case WSDISPLAYIO_SCURPOS:
49549f3f8ccSpetrov case WSDISPLAYIO_GCURMAX:
49649f3f8ccSpetrov case WSDISPLAYIO_GCURSOR:
49749f3f8ccSpetrov case WSDISPLAYIO_SCURSOR:
49882a05c7fSmartin return EIO; /* not supported yet */
499d436f96fSjdc break;
500da28d6ecSmacallan
501d436f96fSjdc case WSDISPLAYIO_GET_EDID: {
502d436f96fSjdc struct wsdisplayio_edid_info *d = data;
503d436f96fSjdc return wsdisplayio_get_edid(sc->sc_dev, d);
504d436f96fSjdc }
505da28d6ecSmacallan
506da28d6ecSmacallan case WSDISPLAYIO_GET_FBINFO: {
507da28d6ecSmacallan struct wsdisplayio_fbinfo *fbi = data;
508da28d6ecSmacallan return wsdisplayio_get_fbinfo(&ms->scr_ri, fbi);
509da28d6ecSmacallan }
510da28d6ecSmacallan
51149f3f8ccSpetrov default:
51282a05c7fSmartin return EPASSTHROUGH;
51349f3f8ccSpetrov }
51449f3f8ccSpetrov
51549f3f8ccSpetrov return (0);
51649f3f8ccSpetrov }
51749f3f8ccSpetrov
518a60b59ccSheas /* blank/unblank the screen */
519a60b59ccSheas static int
ffb_blank(struct ffb_softc * sc,u_long cmd,u_int * data)520a60b59ccSheas ffb_blank(struct ffb_softc *sc, u_long cmd, u_int *data)
521a60b59ccSheas {
52202fbfe26Smartin struct vcons_screen *ms = sc->vd.active;
523a60b59ccSheas u_int val;
524a60b59ccSheas
525342707d5Sjdc DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_TGC);
526a60b59ccSheas val = DAC_READ(sc, FFB_DAC_VALUE);
527a60b59ccSheas
528a60b59ccSheas switch (cmd) {
529a60b59ccSheas case WSDISPLAYIO_GVIDEO:
530a60b59ccSheas *data = val & 1;
531a60b59ccSheas return(0);
532a60b59ccSheas break;
533a60b59ccSheas case WSDISPLAYIO_SVIDEO:
534a60b59ccSheas if (*data == WSDISPLAYIO_VIDEO_OFF)
535a60b59ccSheas val &= ~1;
536a60b59ccSheas else if (*data == WSDISPLAYIO_VIDEO_ON)
537a60b59ccSheas val |= 1;
538a60b59ccSheas else
539a60b59ccSheas return(EINVAL);
540a60b59ccSheas break;
541a60b59ccSheas default:
542a60b59ccSheas return(EINVAL);
543a60b59ccSheas }
544a60b59ccSheas
545342707d5Sjdc DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_TGC);
546a60b59ccSheas DAC_WRITE(sc, FFB_DAC_VALUE, val);
547a60b59ccSheas
54802fbfe26Smartin if ((val & 1) && sc->sc_needredraw) {
54902fbfe26Smartin if (ms != NULL) {
55002fbfe26Smartin if ((sc->sc_mode == WSDISPLAYIO_MODE_EMUL) &&
55102fbfe26Smartin (sc->sc_locked == 0)) {
55202fbfe26Smartin ffb_ras_init(sc);
55302fbfe26Smartin vcons_redraw_screen(ms);
55402fbfe26Smartin }
55502fbfe26Smartin }
55602fbfe26Smartin }
55702fbfe26Smartin
558a60b59ccSheas return(0);
559a60b59ccSheas }
560a60b59ccSheas
56149f3f8ccSpetrov paddr_t
ffb_mmap(void * vsc,void * vs,off_t off,int prot)5627a51d4ddSjmmv ffb_mmap(void *vsc, void *vs, off_t off, int prot)
56349f3f8ccSpetrov {
56402fbfe26Smartin struct vcons_data *vd = vsc;
56502fbfe26Smartin struct ffb_softc *sc = vd->cookie;
56649f3f8ccSpetrov int i;
56749f3f8ccSpetrov
56849f3f8ccSpetrov switch (sc->sc_mode) {
56949f3f8ccSpetrov case WSDISPLAYIO_MODE_MAPPED:
57049f3f8ccSpetrov for (i = 0; i < sc->sc_nreg; i++) {
57149f3f8ccSpetrov /* Before this set? */
57249f3f8ccSpetrov if (off < sc->sc_addrs[i])
57349f3f8ccSpetrov continue;
57449f3f8ccSpetrov /* After this set? */
57549f3f8ccSpetrov if (off >= (sc->sc_addrs[i] + sc->sc_sizes[i]))
57649f3f8ccSpetrov continue;
57749f3f8ccSpetrov
57849f3f8ccSpetrov return (bus_space_mmap(sc->sc_bt, sc->sc_addrs[i],
57949f3f8ccSpetrov off - sc->sc_addrs[i], prot, BUS_SPACE_MAP_LINEAR));
58049f3f8ccSpetrov }
58149f3f8ccSpetrov break;
58249f3f8ccSpetrov #ifdef WSDISPLAYIO_MODE_DUMBFB
58349f3f8ccSpetrov case WSDISPLAYIO_MODE_DUMBFB:
58449f3f8ccSpetrov if (sc->sc_nreg < FFB_REG_DFB24)
58549f3f8ccSpetrov break;
58649f3f8ccSpetrov if (off >= 0 && off < sc->sc_sizes[FFB_REG_DFB24])
58749f3f8ccSpetrov return (bus_space_mmap(sc->sc_bt,
58849f3f8ccSpetrov sc->sc_addrs[FFB_REG_DFB24], off, prot,
589877740feSmacallan BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE));
59049f3f8ccSpetrov break;
59149f3f8ccSpetrov #endif
59249f3f8ccSpetrov }
59349f3f8ccSpetrov return (-1);
59449f3f8ccSpetrov }
59549f3f8ccSpetrov
59649f3f8ccSpetrov void
ffb_ras_fifo_wait(struct ffb_softc * sc,int n)597b49b5731Smartin ffb_ras_fifo_wait(struct ffb_softc *sc, int n)
59849f3f8ccSpetrov {
59949f3f8ccSpetrov int32_t cache = sc->sc_fifo_cache;
60049f3f8ccSpetrov
60149f3f8ccSpetrov if (cache < n) {
60249f3f8ccSpetrov do {
60349f3f8ccSpetrov cache = FBC_READ(sc, FFB_FBC_UCSR);
60449f3f8ccSpetrov cache = (cache & FBC_UCSR_FIFO_MASK) - 8;
60549f3f8ccSpetrov } while (cache < n);
60649f3f8ccSpetrov }
60749f3f8ccSpetrov sc->sc_fifo_cache = cache - n;
60849f3f8ccSpetrov }
60949f3f8ccSpetrov
61049f3f8ccSpetrov void
ffb_ras_wait(struct ffb_softc * sc)611b49b5731Smartin ffb_ras_wait(struct ffb_softc *sc)
61249f3f8ccSpetrov {
6131a509d61Scdi uint32_t ucsr, r;
61449f3f8ccSpetrov
61549f3f8ccSpetrov while (1) {
61649f3f8ccSpetrov ucsr = FBC_READ(sc, FFB_FBC_UCSR);
61749f3f8ccSpetrov if ((ucsr & (FBC_UCSR_FB_BUSY|FBC_UCSR_RP_BUSY)) == 0)
61849f3f8ccSpetrov break;
61949f3f8ccSpetrov r = ucsr & (FBC_UCSR_READ_ERR | FBC_UCSR_FIFO_OVFL);
62049f3f8ccSpetrov if (r != 0)
62149f3f8ccSpetrov FBC_WRITE(sc, FFB_FBC_UCSR, r);
62249f3f8ccSpetrov }
62349f3f8ccSpetrov }
62449f3f8ccSpetrov
62549f3f8ccSpetrov void
ffb_ras_init(struct ffb_softc * sc)626b49b5731Smartin ffb_ras_init(struct ffb_softc *sc)
62749f3f8ccSpetrov {
628342707d5Sjdc uint32_t fbc;
629342707d5Sjdc
630342707d5Sjdc if (sc->sc_width > 1280) {
631342707d5Sjdc DPRINTF(("ffb_ras_init: high resolution.\n"));
6323bdd0f03Smacallan fbc = FFB_FBC_WM_COMBINED | FFB_FBC_WE_FORCEON |
633342707d5Sjdc FFB_FBC_ZE_OFF | FFB_FBC_YE_OFF | FFB_FBC_XE_ON;
634342707d5Sjdc } else {
635342707d5Sjdc DPRINTF(("ffb_ras_init: standard resolution.\n"));
636342707d5Sjdc fbc = FFB_FBC_XE_OFF;
637342707d5Sjdc }
6387b2ed462Smacallan ffb_ras_fifo_wait(sc, 7);
6393bdd0f03Smacallan DPRINTF(("WID: %08x\n", FBC_READ(sc, FFB_FBC_WID)));
6403bdd0f03Smacallan FBC_WRITE(sc, FFB_FBC_WID, 0x0);
64149f3f8ccSpetrov FBC_WRITE(sc, FFB_FBC_PPC,
6429912ffbbSmacallan FBC_PPC_VCE_DIS | FBC_PPC_TBE_OPAQUE | FBC_PPC_ACE_DIS |
6433bdd0f03Smacallan FBC_PPC_APE_DIS | FBC_PPC_DCE_DIS | FBC_PPC_CS_CONST |
644fa70cee0Smacallan FBC_PPC_ABE_DIS | FBC_PPC_XS_WID);
6453bdd0f03Smacallan
6463bdd0f03Smacallan fbc |= FFB_FBC_WB_A | FFB_FBC_RB_A | FFB_FBC_SB_BOTH |
6473bdd0f03Smacallan FFB_FBC_RGBE_MASK;
6483bdd0f03Smacallan DPRINTF(("%s: fbc is %08x\n", __func__, fbc));
6493bdd0f03Smacallan FBC_WRITE(sc, FFB_FBC_FBC, fbc);
65049f3f8ccSpetrov FBC_WRITE(sc, FFB_FBC_ROP, FBC_ROP_NEW);
65149f3f8ccSpetrov FBC_WRITE(sc, FFB_FBC_DRAWOP, FBC_DRAWOP_RECTANGLE);
65249f3f8ccSpetrov FBC_WRITE(sc, FFB_FBC_PMASK, 0xffffffff);
65349f3f8ccSpetrov FBC_WRITE(sc, FFB_FBC_FONTINC, 0x10000);
6547b2ed462Smacallan ffb_ras_fifo_wait(sc, 5);
65549f3f8ccSpetrov sc->sc_fg_cache = 0;
65649f3f8ccSpetrov FBC_WRITE(sc, FFB_FBC_FG, sc->sc_fg_cache);
6577b2ed462Smacallan sc->sc_bg_cache = 0;
6587b2ed462Smacallan FBC_WRITE(sc, FFB_FBC_BG, sc->sc_bg_cache);
659fa70cee0Smacallan FBC_WRITE(sc, FFB_FBC_BLENDC, FFB_BLENDC_FORCE_ONE |
660fa70cee0Smacallan FFB_BLENDC_DF_ONE_M_A |
661fa70cee0Smacallan FFB_BLENDC_SF_A);
662fa70cee0Smacallan FBC_WRITE(sc, FFB_FBC_BLENDC1, 0);
663fa70cee0Smacallan FBC_WRITE(sc, FFB_FBC_BLENDC2, 0);
66449f3f8ccSpetrov ffb_ras_wait(sc);
66549f3f8ccSpetrov }
66649f3f8ccSpetrov
66749f3f8ccSpetrov void
ffb_ras_eraserows(void * cookie,int row,int n,long attr)668b49b5731Smartin ffb_ras_eraserows(void *cookie, int row, int n, long attr)
66949f3f8ccSpetrov {
67049f3f8ccSpetrov struct rasops_info *ri = cookie;
67102fbfe26Smartin struct vcons_screen *scr = ri->ri_hw;
67202fbfe26Smartin struct ffb_softc *sc = scr->scr_cookie;
67349f3f8ccSpetrov
67449f3f8ccSpetrov if (row < 0) {
67549f3f8ccSpetrov n += row;
67649f3f8ccSpetrov row = 0;
67749f3f8ccSpetrov }
67849f3f8ccSpetrov if (row + n > ri->ri_rows)
67949f3f8ccSpetrov n = ri->ri_rows - row;
68049f3f8ccSpetrov if (n <= 0)
68149f3f8ccSpetrov return;
68249f3f8ccSpetrov
68349f3f8ccSpetrov ffb_ras_fill(sc);
68449f3f8ccSpetrov ffb_ras_setfg(sc, ri->ri_devcmap[(attr >> 16) & 0xf]);
68549f3f8ccSpetrov ffb_ras_fifo_wait(sc, 4);
68649f3f8ccSpetrov if ((n == ri->ri_rows) && (ri->ri_flg & RI_FULLCLEAR)) {
68749f3f8ccSpetrov FBC_WRITE(sc, FFB_FBC_BY, 0);
68849f3f8ccSpetrov FBC_WRITE(sc, FFB_FBC_BX, 0);
68949f3f8ccSpetrov FBC_WRITE(sc, FFB_FBC_BH, ri->ri_height);
69049f3f8ccSpetrov FBC_WRITE(sc, FFB_FBC_BW, ri->ri_width);
691c385f585Smacallan ri->ri_flg &= ~RI_CURSOR;
69249f3f8ccSpetrov } else {
69349f3f8ccSpetrov row *= ri->ri_font->fontheight;
69449f3f8ccSpetrov FBC_WRITE(sc, FFB_FBC_BY, ri->ri_yorigin + row);
69549f3f8ccSpetrov FBC_WRITE(sc, FFB_FBC_BX, ri->ri_xorigin);
69649f3f8ccSpetrov FBC_WRITE(sc, FFB_FBC_BH, n * ri->ri_font->fontheight);
69749f3f8ccSpetrov FBC_WRITE(sc, FFB_FBC_BW, ri->ri_emuwidth);
69849f3f8ccSpetrov }
6999912ffbbSmacallan SYNC;
70049f3f8ccSpetrov }
70149f3f8ccSpetrov
70249f3f8ccSpetrov void
ffb_ras_erasecols(void * cookie,int row,int col,int n,long attr)703b49b5731Smartin ffb_ras_erasecols(void *cookie, int row, int col, int n, long attr)
70449f3f8ccSpetrov {
70549f3f8ccSpetrov struct rasops_info *ri = cookie;
70602fbfe26Smartin struct vcons_screen *scr = ri->ri_hw;
70702fbfe26Smartin struct ffb_softc *sc = scr->scr_cookie;
70849f3f8ccSpetrov
70949f3f8ccSpetrov if ((row < 0) || (row >= ri->ri_rows))
71049f3f8ccSpetrov return;
71149f3f8ccSpetrov if (col < 0) {
71249f3f8ccSpetrov n += col;
71349f3f8ccSpetrov col = 0;
71449f3f8ccSpetrov }
71549f3f8ccSpetrov if (col + n > ri->ri_cols)
71649f3f8ccSpetrov n = ri->ri_cols - col;
71749f3f8ccSpetrov if (n <= 0)
71849f3f8ccSpetrov return;
719c385f585Smacallan
72049f3f8ccSpetrov n *= ri->ri_font->fontwidth;
72149f3f8ccSpetrov col *= ri->ri_font->fontwidth;
72249f3f8ccSpetrov row *= ri->ri_font->fontheight;
72349f3f8ccSpetrov
72449f3f8ccSpetrov ffb_ras_fill(sc);
72549f3f8ccSpetrov ffb_ras_setfg(sc, ri->ri_devcmap[(attr >> 16) & 0xf]);
72649f3f8ccSpetrov ffb_ras_fifo_wait(sc, 4);
72749f3f8ccSpetrov FBC_WRITE(sc, FFB_FBC_BY, ri->ri_yorigin + row);
72849f3f8ccSpetrov FBC_WRITE(sc, FFB_FBC_BX, ri->ri_xorigin + col);
72949f3f8ccSpetrov FBC_WRITE(sc, FFB_FBC_BH, ri->ri_font->fontheight);
7307b2ed462Smacallan FBC_WRITE(sc, FFB_FBC_BW, n);
7319912ffbbSmacallan SYNC;
73249f3f8ccSpetrov }
73349f3f8ccSpetrov
73449f3f8ccSpetrov void
ffb_ras_fill(struct ffb_softc * sc)735b49b5731Smartin ffb_ras_fill(struct ffb_softc *sc)
73649f3f8ccSpetrov {
737fa70cee0Smacallan ffb_ras_fifo_wait(sc, 3);
738fa70cee0Smacallan FBC_WRITE(sc, FFB_FBC_PPC,
739fa70cee0Smacallan FBC_PPC_VCE_DIS | FBC_PPC_TBE_OPAQUE | FBC_PPC_ACE_DIS |
740fa70cee0Smacallan FBC_PPC_APE_DIS | FBC_PPC_DCE_DIS | FBC_PPC_CS_CONST |
741fa70cee0Smacallan FBC_PPC_ABE_DIS | FBC_PPC_XS_WID);
74249f3f8ccSpetrov FBC_WRITE(sc, FFB_FBC_ROP, FBC_ROP_NEW);
74349f3f8ccSpetrov FBC_WRITE(sc, FFB_FBC_DRAWOP, FBC_DRAWOP_RECTANGLE);
7449912ffbbSmacallan SYNC;
74549f3f8ccSpetrov }
74649f3f8ccSpetrov
74749f3f8ccSpetrov void
ffb_ras_invert(struct ffb_softc * sc)748b1dfce18Smacallan ffb_ras_invert(struct ffb_softc *sc)
749b1dfce18Smacallan {
750b1dfce18Smacallan ffb_ras_fifo_wait(sc, 3);
751b1dfce18Smacallan FBC_WRITE(sc, FFB_FBC_PPC,
752b1dfce18Smacallan FBC_PPC_VCE_DIS | FBC_PPC_TBE_OPAQUE | FBC_PPC_ACE_DIS |
753b1dfce18Smacallan FBC_PPC_APE_DIS | FBC_PPC_DCE_DIS | FBC_PPC_CS_CONST |
754b1dfce18Smacallan FBC_PPC_ABE_DIS | FBC_PPC_XS_WID);
755b1dfce18Smacallan FBC_WRITE(sc, FFB_FBC_ROP, FBC_ROP_INVERT);
756b1dfce18Smacallan FBC_WRITE(sc, FFB_FBC_DRAWOP, FBC_DRAWOP_RECTANGLE);
757b1dfce18Smacallan SYNC;
758b1dfce18Smacallan }
759b1dfce18Smacallan
760b1dfce18Smacallan void
ffb_ras_copyrows(void * cookie,int src,int dst,int n)761b49b5731Smartin ffb_ras_copyrows(void *cookie, int src, int dst, int n)
76249f3f8ccSpetrov {
76349f3f8ccSpetrov struct rasops_info *ri = cookie;
76402fbfe26Smartin struct vcons_screen *scr = ri->ri_hw;
76502fbfe26Smartin struct ffb_softc *sc = scr->scr_cookie;
76649f3f8ccSpetrov
76749f3f8ccSpetrov if (dst == src)
76849f3f8ccSpetrov return;
76949f3f8ccSpetrov if (src < 0) {
77049f3f8ccSpetrov n += src;
77149f3f8ccSpetrov src = 0;
77249f3f8ccSpetrov }
77349f3f8ccSpetrov if ((src + n) > ri->ri_rows)
77449f3f8ccSpetrov n = ri->ri_rows - src;
77549f3f8ccSpetrov if (dst < 0) {
77649f3f8ccSpetrov n += dst;
77749f3f8ccSpetrov dst = 0;
77849f3f8ccSpetrov }
77949f3f8ccSpetrov if ((dst + n) > ri->ri_rows)
78049f3f8ccSpetrov n = ri->ri_rows - dst;
78149f3f8ccSpetrov if (n <= 0)
78249f3f8ccSpetrov return;
78349f3f8ccSpetrov n *= ri->ri_font->fontheight;
78449f3f8ccSpetrov src *= ri->ri_font->fontheight;
78549f3f8ccSpetrov dst *= ri->ri_font->fontheight;
78649f3f8ccSpetrov
787fa70cee0Smacallan ffb_ras_fifo_wait(sc, 9);
788fa70cee0Smacallan FBC_WRITE(sc, FFB_FBC_PPC,
789fa70cee0Smacallan FBC_PPC_VCE_DIS | FBC_PPC_TBE_OPAQUE | FBC_PPC_ACE_DIS |
790fa70cee0Smacallan FBC_PPC_APE_DIS | FBC_PPC_DCE_DIS | FBC_PPC_CS_CONST |
791fa70cee0Smacallan FBC_PPC_ABE_DIS | FBC_PPC_XS_WID);
79249f3f8ccSpetrov FBC_WRITE(sc, FFB_FBC_ROP, FBC_ROP_OLD | (FBC_ROP_OLD << 8));
79349f3f8ccSpetrov FBC_WRITE(sc, FFB_FBC_DRAWOP, FBC_DRAWOP_VSCROLL);
79449f3f8ccSpetrov FBC_WRITE(sc, FFB_FBC_BY, ri->ri_yorigin + src);
79549f3f8ccSpetrov FBC_WRITE(sc, FFB_FBC_BX, ri->ri_xorigin);
79649f3f8ccSpetrov FBC_WRITE(sc, FFB_FBC_DY, ri->ri_yorigin + dst);
79749f3f8ccSpetrov FBC_WRITE(sc, FFB_FBC_DX, ri->ri_xorigin);
79849f3f8ccSpetrov FBC_WRITE(sc, FFB_FBC_BH, n);
79949f3f8ccSpetrov FBC_WRITE(sc, FFB_FBC_BW, ri->ri_emuwidth);
8009912ffbbSmacallan SYNC;
80149f3f8ccSpetrov }
80249f3f8ccSpetrov
8039912ffbbSmacallan static void
ffb_ras_setfg(struct ffb_softc * sc,int32_t fg)804b49b5731Smartin ffb_ras_setfg(struct ffb_softc *sc, int32_t fg)
80549f3f8ccSpetrov {
80649f3f8ccSpetrov ffb_ras_fifo_wait(sc, 1);
80749f3f8ccSpetrov if (fg == sc->sc_fg_cache)
80849f3f8ccSpetrov return;
80949f3f8ccSpetrov sc->sc_fg_cache = fg;
81049f3f8ccSpetrov FBC_WRITE(sc, FFB_FBC_FG, fg);
8119912ffbbSmacallan SYNC;
8129912ffbbSmacallan }
8139912ffbbSmacallan
8149912ffbbSmacallan static void
ffb_ras_setbg(struct ffb_softc * sc,int32_t bg)8159912ffbbSmacallan ffb_ras_setbg(struct ffb_softc *sc, int32_t bg)
8169912ffbbSmacallan {
8179912ffbbSmacallan ffb_ras_fifo_wait(sc, 1);
8189912ffbbSmacallan if (bg == sc->sc_bg_cache)
8199912ffbbSmacallan return;
8209912ffbbSmacallan sc->sc_bg_cache = bg;
8219912ffbbSmacallan FBC_WRITE(sc, FFB_FBC_BG, bg);
8229912ffbbSmacallan SYNC;
82349f3f8ccSpetrov }
82421d2a323Smartin
82521d2a323Smartin /* frame buffer generic driver support functions */
82621d2a323Smartin static void
ffbfb_unblank(device_t dev)827817999e9Schristos ffbfb_unblank(device_t dev)
82821d2a323Smartin {
829d38a1476Smartin struct ffb_softc *sc = device_private(dev);
83002fbfe26Smartin struct vcons_screen *ms = sc->vd.active;
831df850dc7Smartin u_int on = 1;
83202fbfe26Smartin int redraw = 0;
83302fbfe26Smartin
83402fbfe26Smartin ffb_ras_init(sc);
83502fbfe26Smartin if (sc->sc_locked) {
83602fbfe26Smartin sc->sc_locked = 0;
83702fbfe26Smartin redraw = 1;
83802fbfe26Smartin }
83921d2a323Smartin
840cfce4b55Smacallan ffb_blank(sc, WSDISPLAYIO_SVIDEO, &on);
841cfce4b55Smacallan #if 0
84202fbfe26Smartin if ((sc->vd.active != &ffb_console_screen) &&
84302fbfe26Smartin (ffb_console_screen.scr_flags & VCONS_SCREEN_IS_STATIC)) {
84402fbfe26Smartin /*
84502fbfe26Smartin * force-switch to the console screen.
84602fbfe26Smartin * Caveat: the higher layer will think we're still on the
84702fbfe26Smartin * other screen
84802fbfe26Smartin */
84902fbfe26Smartin
85002fbfe26Smartin SCREEN_INVISIBLE(sc->vd.active);
85102fbfe26Smartin sc->vd.active = &ffb_console_screen;
85202fbfe26Smartin SCREEN_VISIBLE(sc->vd.active);
85302fbfe26Smartin ms = sc->vd.active;
85402fbfe26Smartin redraw = 1;
85502fbfe26Smartin }
856cfce4b55Smacallan #endif
85702fbfe26Smartin if (redraw) {
85802fbfe26Smartin vcons_redraw_screen(ms);
85902fbfe26Smartin }
86021d2a323Smartin }
86121d2a323Smartin
86221d2a323Smartin int
ffbfb_open(dev_t dev,int flags,int mode,struct lwp * l)863948731b9Schristos ffbfb_open(dev_t dev, int flags, int mode, struct lwp *l)
86421d2a323Smartin {
86502fbfe26Smartin struct ffb_softc *sc;
86621d2a323Smartin
8672dea63feScegger sc = device_lookup_private(&ffb_cd, minor(dev));
8682dea63feScegger if (sc == NULL)
86921d2a323Smartin return ENXIO;
87021d2a323Smartin
87102fbfe26Smartin sc->sc_locked = 1;
87221d2a323Smartin return 0;
87321d2a323Smartin }
87421d2a323Smartin
87521d2a323Smartin int
ffbfb_close(dev_t dev,int flags,int mode,struct lwp * l)876948731b9Schristos ffbfb_close(dev_t dev, int flags, int mode, struct lwp *l)
87721d2a323Smartin {
8782dea63feScegger struct ffb_softc *sc = device_lookup_private(&ffb_cd, minor(dev));
87902fbfe26Smartin struct vcons_screen *ms = sc->vd.active;
88002fbfe26Smartin
88102fbfe26Smartin sc->sc_locked = 0;
88202fbfe26Smartin if (ms != NULL) {
88302fbfe26Smartin if ((sc->sc_mode == WSDISPLAYIO_MODE_EMUL) &&
88402fbfe26Smartin (sc->sc_locked == 0)) {
88502fbfe26Smartin ffb_ras_init(sc);
88602fbfe26Smartin vcons_redraw_screen(ms);
88702fbfe26Smartin }
88802fbfe26Smartin }
88921d2a323Smartin return 0;
89021d2a323Smartin }
89121d2a323Smartin
89221d2a323Smartin int
ffbfb_ioctl(dev_t dev,u_long cmd,void * data,int flags,struct lwp * l)89353524e44Schristos ffbfb_ioctl(dev_t dev, u_long cmd, void *data, int flags, struct lwp *l)
89421d2a323Smartin {
8952dea63feScegger struct ffb_softc *sc = device_lookup_private(&ffb_cd, minor(dev));
89621d2a323Smartin
89702fbfe26Smartin return ffb_ioctl(&sc->vd, NULL, cmd, data, flags, l);
89821d2a323Smartin }
89921d2a323Smartin
90021d2a323Smartin paddr_t
ffbfb_mmap(dev_t dev,off_t off,int prot)90121d2a323Smartin ffbfb_mmap(dev_t dev, off_t off, int prot)
90221d2a323Smartin {
9032dea63feScegger struct ffb_softc *sc = device_lookup_private(&ffb_cd, minor(dev));
904588c5238Smacallan uint64_t size;
90521d2a323Smartin int i, reg;
90621d2a323Smartin off_t o;
90721d2a323Smartin
90821d2a323Smartin /*
90921d2a323Smartin * off is a magic cookie (see xfree86/drivers/sunffb/ffb.h),
91021d2a323Smartin * which we map to an index into the "reg" property, and use
91121d2a323Smartin * our copy of the firmware data as arguments for the real
91221d2a323Smartin * mapping.
91321d2a323Smartin */
914877740feSmacallan static struct { unsigned long voff; int reg; long flags; } map[] = {
915877740feSmacallan { 0x00000000, FFB_REG_SFB8R, BUS_SPACE_MAP_PREFETCHABLE },
916877740feSmacallan { 0x00400000, FFB_REG_SFB8G, BUS_SPACE_MAP_PREFETCHABLE },
917877740feSmacallan { 0x00800000, FFB_REG_SFB8B, BUS_SPACE_MAP_PREFETCHABLE },
918877740feSmacallan { 0x00c00000, FFB_REG_SFB8X, BUS_SPACE_MAP_PREFETCHABLE },
919877740feSmacallan { 0x01000000, FFB_REG_SFB32, BUS_SPACE_MAP_PREFETCHABLE },
920877740feSmacallan { 0x02000000, FFB_REG_SFB64, BUS_SPACE_MAP_PREFETCHABLE },
921877740feSmacallan { 0x04000000, FFB_REG_FBC, 0 },
922877740feSmacallan { 0x04004000, FFB_REG_DFB8R, BUS_SPACE_MAP_PREFETCHABLE },
923877740feSmacallan { 0x04404000, FFB_REG_DFB8G, BUS_SPACE_MAP_PREFETCHABLE },
924877740feSmacallan { 0x04804000, FFB_REG_DFB8B, BUS_SPACE_MAP_PREFETCHABLE },
925877740feSmacallan { 0x04c04000, FFB_REG_DFB8X, BUS_SPACE_MAP_PREFETCHABLE },
926877740feSmacallan { 0x05004000, FFB_REG_DFB24, BUS_SPACE_MAP_PREFETCHABLE },
927877740feSmacallan { 0x06004000, FFB_REG_DFB32, BUS_SPACE_MAP_PREFETCHABLE },
928877740feSmacallan { 0x07004000, FFB_REG_DFB422A, BUS_SPACE_MAP_PREFETCHABLE },
929877740feSmacallan { 0x0bc06000, FFB_REG_DAC, 0 },
930877740feSmacallan { 0x0bc08000, FFB_REG_PROM, 0 },
931877740feSmacallan { 0x0bc18000, 0, 0 }
93221d2a323Smartin };
93321d2a323Smartin
93421d2a323Smartin /* special value "FFB_EXP_VOFF" - not backed by any "reg" entry */
93521d2a323Smartin if (off == 0x0bc18000)
93621d2a323Smartin return bus_space_mmap(sc->sc_bt, sc->sc_addrs[FFB_REG_PROM],
93721d2a323Smartin 0x00200000, prot, BUS_SPACE_MAP_LINEAR);
93821d2a323Smartin
939588c5238Smacallan /*
940588c5238Smacallan * FFB_VOFF_FBC_KREGS - used by afbinit to upload firmware. We should
941588c5238Smacallan * probably mmap them only on afb boards
942588c5238Smacallan */
943588c5238Smacallan if ((off >= 0x0bc04000) && (off < 0x0bc06000))
944588c5238Smacallan return bus_space_mmap(sc->sc_bt, sc->sc_addrs[FFB_REG_PROM],
945588c5238Smacallan 0x00610000 + (off - 0x0bc04000), prot,
946588c5238Smacallan BUS_SPACE_MAP_LINEAR);
947588c5238Smacallan
94821d2a323Smartin #define NELEMS(arr) (sizeof(arr)/sizeof((arr)[0]))
94921d2a323Smartin
95021d2a323Smartin /* the map is ordered by voff */
951588c5238Smacallan for (i = 0; i < NELEMS(map)-1; i++) {
95221d2a323Smartin reg = map[i].reg;
953d2c9943bSmacallan /* the number of entries in reg seems to vary */
954588c5238Smacallan if (reg < sc->sc_nreg) {
955d1579b2dSriastradh size = uimin((map[i + 1].voff - map[i].voff),
956588c5238Smacallan sc->sc_sizes[reg]);
957588c5238Smacallan if ((off >= map[i].voff) &&
958588c5238Smacallan (off < (map[i].voff + size))) {
95921d2a323Smartin o = off - map[i].voff;
960588c5238Smacallan return bus_space_mmap(sc->sc_bt,
961588c5238Smacallan sc->sc_addrs[reg], o, prot,
962877740feSmacallan BUS_SPACE_MAP_LINEAR | map[i].flags);
96321d2a323Smartin }
964588c5238Smacallan }
965588c5238Smacallan }
96621d2a323Smartin
96721d2a323Smartin return -1;
96821d2a323Smartin }
969d2c9943bSmacallan
970d2c9943bSmacallan void
ffb_clearscreen(struct ffb_softc * sc)971d2c9943bSmacallan ffb_clearscreen(struct ffb_softc *sc)
972d2c9943bSmacallan {
97302fbfe26Smartin struct rasops_info *ri = &ffb_console_screen.scr_ri;
974d2c9943bSmacallan ffb_ras_fill(sc);
975d2c9943bSmacallan ffb_ras_setfg(sc, ri->ri_devcmap[WS_DEFAULT_BG]);
976d2c9943bSmacallan ffb_ras_fifo_wait(sc, 4);
977d2c9943bSmacallan FBC_WRITE(sc, FFB_FBC_BY, 0);
978d2c9943bSmacallan FBC_WRITE(sc, FFB_FBC_BX, 0);
97902fbfe26Smartin FBC_WRITE(sc, FFB_FBC_BH, sc->sc_height);
98002fbfe26Smartin FBC_WRITE(sc, FFB_FBC_BW, sc->sc_width);
981d2c9943bSmacallan }
982d2c9943bSmacallan
983d2c9943bSmacallan void
ffb_cursor(void * cookie,int on,int row,int col)984d2c9943bSmacallan ffb_cursor(void *cookie, int on, int row, int col)
985d2c9943bSmacallan {
986d2c9943bSmacallan struct rasops_info *ri = cookie;
98702fbfe26Smartin struct vcons_screen *scr;
98802fbfe26Smartin struct ffb_softc *sc;
989b1dfce18Smacallan int x, y, wi, he;
990d2c9943bSmacallan
99102fbfe26Smartin if (cookie != NULL) {
99202fbfe26Smartin scr = ri->ri_hw;
99302fbfe26Smartin sc = scr->scr_cookie;
99402fbfe26Smartin
995d2c9943bSmacallan wi = ri->ri_font->fontwidth;
996d2c9943bSmacallan he = ri->ri_font->fontheight;
997d2c9943bSmacallan
99802fbfe26Smartin if (sc->sc_mode == WSDISPLAYIO_MODE_EMUL) {
999b1dfce18Smacallan
1000b1dfce18Smacallan if (ri->ri_flg & RI_CURSOR) {
1001b1dfce18Smacallan
1002b1dfce18Smacallan /* remove cursor */
100302fbfe26Smartin x = ri->ri_ccol * wi + ri->ri_xorigin;
100402fbfe26Smartin y = ri->ri_crow * he + ri->ri_yorigin;
1005d2c9943bSmacallan
1006b1dfce18Smacallan ffb_ras_invert(sc);
1007b1dfce18Smacallan ffb_ras_fifo_wait(sc, 4);
1008b1dfce18Smacallan FBC_WRITE(sc, FFB_FBC_BY, y);
1009b1dfce18Smacallan FBC_WRITE(sc, FFB_FBC_BX, x);
1010b1dfce18Smacallan FBC_WRITE(sc, FFB_FBC_BH, he);
1011b1dfce18Smacallan FBC_WRITE(sc, FFB_FBC_BW, wi);
1012b1dfce18Smacallan
101302fbfe26Smartin ri->ri_flg &= ~RI_CURSOR;
1014d2c9943bSmacallan }
101502fbfe26Smartin ri->ri_crow = row;
101602fbfe26Smartin ri->ri_ccol = col;
101702fbfe26Smartin if (on)
1018d2c9943bSmacallan {
101902fbfe26Smartin x = ri->ri_ccol * wi + ri->ri_xorigin;
102002fbfe26Smartin y = ri->ri_crow * he + ri->ri_yorigin;
1021b1dfce18Smacallan
1022b1dfce18Smacallan ffb_ras_invert(sc);
1023b1dfce18Smacallan ffb_ras_fifo_wait(sc, 4);
1024b1dfce18Smacallan FBC_WRITE(sc, FFB_FBC_BY, y);
1025b1dfce18Smacallan FBC_WRITE(sc, FFB_FBC_BX, x);
1026b1dfce18Smacallan FBC_WRITE(sc, FFB_FBC_BH, he);
1027b1dfce18Smacallan FBC_WRITE(sc, FFB_FBC_BW, wi);
1028b1dfce18Smacallan
102902fbfe26Smartin ri->ri_flg |= RI_CURSOR;
1030d2c9943bSmacallan }
1031d2c9943bSmacallan } else {
103202fbfe26Smartin ri->ri_crow = row;
103302fbfe26Smartin ri->ri_ccol = col;
103402fbfe26Smartin ri->ri_flg &= ~RI_CURSOR;
103502fbfe26Smartin }
1036d2c9943bSmacallan }
1037d2c9943bSmacallan }
1038d2c9943bSmacallan
1039b1dfce18Smacallan /* mono bitmap font */
1040d2c9943bSmacallan void
ffb_putchar_mono(void * cookie,int row,int col,u_int c,long attr)1041b1dfce18Smacallan ffb_putchar_mono(void *cookie, int row, int col, u_int c, long attr)
1042d2c9943bSmacallan {
1043d2c9943bSmacallan struct rasops_info *ri = cookie;
104402fbfe26Smartin struct vcons_screen *scr = ri->ri_hw;
10459912ffbbSmacallan struct wsdisplay_font *font = PICK_FONT(ri, c);
104602fbfe26Smartin struct ffb_softc *sc = scr->scr_cookie;
10479912ffbbSmacallan void *data;
10489912ffbbSmacallan uint32_t fg, bg;
1049b1dfce18Smacallan int i;
10509912ffbbSmacallan int x, y, wi, he;
10519912ffbbSmacallan
1052fa70cee0Smacallan if (sc->sc_mode != WSDISPLAYIO_MODE_EMUL)
1053fa70cee0Smacallan return;
1054fa70cee0Smacallan
10559912ffbbSmacallan wi = font->fontwidth;
10569912ffbbSmacallan he = font->fontheight;
10579912ffbbSmacallan
10589912ffbbSmacallan if (!CHAR_IN_FONT(c, font))
10599912ffbbSmacallan return;
1060fa70cee0Smacallan
10619912ffbbSmacallan bg = ri->ri_devcmap[(attr >> 16) & 0xf];
10629912ffbbSmacallan fg = ri->ri_devcmap[(attr >> 24) & 0xf];
10639912ffbbSmacallan x = ri->ri_xorigin + col * wi;
10649912ffbbSmacallan y = ri->ri_yorigin + row * he;
10659912ffbbSmacallan
1066b1dfce18Smacallan data = WSFONT_GLYPH(c, font);
10679912ffbbSmacallan
10689912ffbbSmacallan ffb_ras_setbg(sc, bg);
10699912ffbbSmacallan ffb_ras_setfg(sc, fg);
1070fa70cee0Smacallan ffb_ras_fifo_wait(sc, 4);
10719912ffbbSmacallan FBC_WRITE(sc, FFB_FBC_ROP, FBC_ROP_NEW);
10729912ffbbSmacallan FBC_WRITE(sc, FFB_FBC_FONTXY, (y << 16) | x);
10739912ffbbSmacallan FBC_WRITE(sc, FFB_FBC_FONTW, wi);
1074fa70cee0Smacallan FBC_WRITE(sc, FFB_FBC_PPC,
1075fa70cee0Smacallan FBC_PPC_VCE_DIS | FBC_PPC_TBE_OPAQUE | FBC_PPC_ACE_DIS |
1076fa70cee0Smacallan FBC_PPC_APE_DIS | FBC_PPC_DCE_DIS | FBC_PPC_CS_CONST |
1077fa70cee0Smacallan FBC_PPC_ABE_DIS | FBC_PPC_XS_WID);
10789912ffbbSmacallan
1079fa70cee0Smacallan switch (font->stride) {
10809912ffbbSmacallan case 1: {
10819912ffbbSmacallan uint8_t *data8 = data;
10829912ffbbSmacallan uint32_t reg;
108305c8e03aSmacallan if (attr & WSATTR_UNDERLINE) {
108405c8e03aSmacallan for (i = 0; i < he - 2; i++) {
108505c8e03aSmacallan reg = *data8;
108605c8e03aSmacallan FBC_WRITE(sc, FFB_FBC_FONT, reg << 24);
108705c8e03aSmacallan data8++;
108805c8e03aSmacallan }
108905c8e03aSmacallan FBC_WRITE(sc, FFB_FBC_FONT, 0xff000000);
109005c8e03aSmacallan data8++;
109105c8e03aSmacallan reg = *data8;
109205c8e03aSmacallan FBC_WRITE(sc, FFB_FBC_FONT, reg << 24);
109305c8e03aSmacallan } else {
10949912ffbbSmacallan for (i = 0; i < he; i++) {
10959912ffbbSmacallan reg = *data8;
10969912ffbbSmacallan FBC_WRITE(sc, FFB_FBC_FONT, reg << 24);
10979912ffbbSmacallan data8++;
10989912ffbbSmacallan }
109905c8e03aSmacallan }
11009912ffbbSmacallan break;
11019912ffbbSmacallan }
11029912ffbbSmacallan case 2: {
11039912ffbbSmacallan uint16_t *data16 = data;
11049912ffbbSmacallan uint32_t reg;
110505c8e03aSmacallan if (attr & WSATTR_UNDERLINE) {
110605c8e03aSmacallan for (i = 0; i < he - 2; i++) {
110705c8e03aSmacallan reg = *data16;
110805c8e03aSmacallan FBC_WRITE(sc, FFB_FBC_FONT, reg << 16);
110905c8e03aSmacallan data16++;
111005c8e03aSmacallan }
111105c8e03aSmacallan FBC_WRITE(sc, FFB_FBC_FONT, 0xffff0000);
111205c8e03aSmacallan data16++;
111305c8e03aSmacallan reg = *data16;
111405c8e03aSmacallan FBC_WRITE(sc, FFB_FBC_FONT, reg << 16);
111505c8e03aSmacallan } else {
11169912ffbbSmacallan for (i = 0; i < he; i++) {
11179912ffbbSmacallan reg = *data16;
11189912ffbbSmacallan FBC_WRITE(sc, FFB_FBC_FONT, reg << 16);
11199912ffbbSmacallan data16++;
11209912ffbbSmacallan }
112105c8e03aSmacallan }
11229912ffbbSmacallan break;
11239912ffbbSmacallan }
11249912ffbbSmacallan }
1125b1dfce18Smacallan }
1126b1dfce18Smacallan
1127fa70cee0Smacallan /* alpha font */
1128b1dfce18Smacallan void
ffb_putchar_aa(void * cookie,int row,int col,u_int c,long attr)1129b1dfce18Smacallan ffb_putchar_aa(void *cookie, int row, int col, u_int c, long attr)
1130b1dfce18Smacallan {
1131b1dfce18Smacallan struct rasops_info *ri = cookie;
1132b1dfce18Smacallan struct vcons_screen *scr = ri->ri_hw;
1133b1dfce18Smacallan struct wsdisplay_font *font = PICK_FONT(ri, c);
1134b1dfce18Smacallan struct ffb_softc *sc = scr->scr_cookie;
1135fa70cee0Smacallan volatile uint32_t *dest, *ddest;
1136b1dfce18Smacallan uint8_t *data8;
1137b1dfce18Smacallan uint32_t fg, bg;
1138b1dfce18Smacallan int i;
1139b1dfce18Smacallan int x, y, wi, he;
1140fa70cee0Smacallan uint32_t alpha = 0x80;
1141fa70cee0Smacallan int j;
1142fa70cee0Smacallan
1143b1dfce18Smacallan if (sc->sc_mode != WSDISPLAYIO_MODE_EMUL)
1144b1dfce18Smacallan return;
1145b1dfce18Smacallan
1146b1dfce18Smacallan wi = font->fontwidth;
1147b1dfce18Smacallan he = font->fontheight;
1148b1dfce18Smacallan
1149b1dfce18Smacallan if (!CHAR_IN_FONT(c, font))
1150b1dfce18Smacallan return;
1151b1dfce18Smacallan
1152b1dfce18Smacallan bg = ri->ri_devcmap[(attr >> 16) & 0xf];
1153b1dfce18Smacallan fg = ri->ri_devcmap[(attr >> 24) & 0xf];
1154b1dfce18Smacallan x = ri->ri_xorigin + col * wi;
1155b1dfce18Smacallan y = ri->ri_yorigin + row * he;
1156b1dfce18Smacallan
1157b1dfce18Smacallan data8 = WSFONT_GLYPH(c, font);
1158b1dfce18Smacallan
1159fa70cee0Smacallan /* first we erase the background */
1160fa70cee0Smacallan ffb_ras_fill(sc);
1161fa70cee0Smacallan ffb_ras_setfg(sc, bg);
1162fa70cee0Smacallan ffb_ras_fifo_wait(sc, 4);
1163fa70cee0Smacallan FBC_WRITE(sc, FFB_FBC_BY, y);
1164fa70cee0Smacallan FBC_WRITE(sc, FFB_FBC_BX, x);
1165fa70cee0Smacallan FBC_WRITE(sc, FFB_FBC_BH, he);
1166fa70cee0Smacallan FBC_WRITE(sc, FFB_FBC_BW, wi);
1167fa70cee0Smacallan
1168fa70cee0Smacallan /* if we draw a space we're done */
116905c8e03aSmacallan if (c == ' ') goto out;
1170fa70cee0Smacallan
1171fa70cee0Smacallan /* now enable alpha blending */
1172fa70cee0Smacallan ffb_ras_setfg(sc, fg);
1173fa70cee0Smacallan ffb_ras_fifo_wait(sc, 2);
1174fa70cee0Smacallan FBC_WRITE(sc, FFB_FBC_ROP, FBC_ROP_NEW);
1175fa70cee0Smacallan
1176fa70cee0Smacallan FBC_WRITE(sc, FFB_FBC_PPC,
1177fa70cee0Smacallan FBC_PPC_VCE_DIS | FBC_PPC_TBE_OPAQUE | FBC_PPC_ACE_DIS |
1178fa70cee0Smacallan FBC_PPC_APE_DIS | FBC_PPC_DCE_DIS | FBC_PPC_CS_CONST |
1179fa70cee0Smacallan FBC_PPC_ABE_ENA | FBC_PPC_XS_VAR);
1180fa70cee0Smacallan /*
1181b1dfce18Smacallan * we have to wait for both the rectangle drawing op above and the
1182b1dfce18Smacallan * FFB_FBC_PPC write to finish before mucking around in the SFB aperture
1183fa70cee0Smacallan */
1184fa70cee0Smacallan ffb_ras_wait(sc);
1185fa70cee0Smacallan
1186fa70cee0Smacallan /* ... and draw the character */
1187b1dfce18Smacallan dest = sc->sc_sfb32 + (y << 11) + x;
1188fa70cee0Smacallan for (i = 0; i < he; i++) {
1189fa70cee0Smacallan ddest = dest;
1190fa70cee0Smacallan for (j = 0; j < wi; j++) {
1191fa70cee0Smacallan alpha = *data8;
1192fa70cee0Smacallan /*
1193b1dfce18Smacallan * We set the colour source to constant above so we only
1194b1dfce18Smacallan * have to write the alpha channel here and the colour
1195b1dfce18Smacallan * comes from the FG register. It would be nice if we
1196b1dfce18Smacallan * could just use the SFB8X aperture and memcpy() the
1197b1dfce18Smacallan * alpha map line by line but for some strange reason
1198b1dfce18Smacallan * that will take colour info from the framebuffer even
1199b1dfce18Smacallan * if we set the FBC_PPC_CS_CONST bit above.
1200fa70cee0Smacallan */
1201fa70cee0Smacallan *ddest = alpha << 24;
1202fa70cee0Smacallan data8++;
1203fa70cee0Smacallan ddest++;
1204fa70cee0Smacallan }
1205fa70cee0Smacallan dest += 2048;
1206fa70cee0Smacallan }
120705c8e03aSmacallan out:
120805c8e03aSmacallan /* check if we need to draw an underline */
120905c8e03aSmacallan if (attr & WSATTR_UNDERLINE) {
121005c8e03aSmacallan dest = sc->sc_sfb32 + ((y + he - 2) << 11) + x;
121105c8e03aSmacallan for (i = 0; i < wi; i++) {
121205c8e03aSmacallan *dest = 0xa0000000;
121305c8e03aSmacallan dest++;
121405c8e03aSmacallan }
121505c8e03aSmacallan }
1216d2c9943bSmacallan }
1217d2c9943bSmacallan
1218d2c9943bSmacallan int
ffb_allocattr(void * cookie,int fg,int bg,int flags,long * attrp)1219d2c9943bSmacallan ffb_allocattr(void *cookie, int fg, int bg, int flags, long *attrp)
1220d2c9943bSmacallan {
1221d2c9943bSmacallan if ((fg == 0) && (bg == 0))
1222d2c9943bSmacallan {
1223d2c9943bSmacallan fg = WS_DEFAULT_FG;
1224d2c9943bSmacallan bg = WS_DEFAULT_BG;
1225d2c9943bSmacallan }
1226d2c9943bSmacallan if (flags & WSATTR_REVERSE) {
122705c8e03aSmacallan *attrp = (bg & 0xff) << 24 | (fg & 0xff) << 16;
1228d2c9943bSmacallan } else
122905c8e03aSmacallan *attrp = (fg & 0xff) << 24 | (bg & 0xff) << 16;
123005c8e03aSmacallan if (flags & WSATTR_UNDERLINE)
123105c8e03aSmacallan *attrp |= WSATTR_UNDERLINE;
1232d2c9943bSmacallan return 0;
1233d2c9943bSmacallan }
1234d2c9943bSmacallan
1235d2c9943bSmacallan void
ffb_init_screen(void * cookie,struct vcons_screen * scr,int existing,long * defattr)123602fbfe26Smartin ffb_init_screen(void *cookie, struct vcons_screen *scr,
1237d2c9943bSmacallan int existing, long *defattr)
1238d2c9943bSmacallan {
123902fbfe26Smartin struct ffb_softc *sc = cookie;
124002fbfe26Smartin struct rasops_info *ri = &scr->scr_ri;
1241d2c9943bSmacallan
1242d2c9943bSmacallan ri->ri_depth = 32;
1243d2c9943bSmacallan ri->ri_width = sc->sc_width;
1244d2c9943bSmacallan ri->ri_height = sc->sc_height;
1245d2c9943bSmacallan ri->ri_stride = sc->sc_linebytes;
124605c8e03aSmacallan ri->ri_flg = RI_CENTER | RI_ENABLE_ALPHA | RI_PREFER_ALPHA;
1247d2c9943bSmacallan
12489912ffbbSmacallan /*
12499912ffbbSmacallan * we can't accelerate copycols() so instead of falling back to
12509912ffbbSmacallan * software use vcons' putchar() based implementation
12519912ffbbSmacallan */
1252733da76fSmacallan scr->scr_flags |= VCONS_NO_COPYCOLS | VCONS_LOADFONT;
1253733da76fSmacallan
12543bdd0f03Smacallan #ifdef VCONS_DRAW_INTR
12553bdd0f03Smacallan scr->scr_flags |= VCONS_DONT_READ;
12563bdd0f03Smacallan #endif
1257342707d5Sjdc DPRINTF(("ffb_init_screen: addr: %08lx\n",(ulong)ri->ri_bits));
1258342707d5Sjdc
1259c6ba58b3Smacallan /* explicitly request BGR in case the default changes */
1260c6ba58b3Smacallan ri->ri_rnum = 8;
1261c6ba58b3Smacallan ri->ri_gnum = 8;
1262c6ba58b3Smacallan ri->ri_bnum = 8;
1263c6ba58b3Smacallan ri->ri_rpos = 0;
1264c6ba58b3Smacallan ri->ri_gpos = 8;
1265c6ba58b3Smacallan ri->ri_bpos = 16;
1266c6ba58b3Smacallan
12671fa79ac8Smacallan rasops_init(ri, 0, 0);
1268d2c9943bSmacallan rasops_reconfig(ri, sc->sc_height / ri->ri_font->fontheight,
1269d2c9943bSmacallan sc->sc_width / ri->ri_font->fontwidth);
1270733da76fSmacallan ri->ri_caps = WSSCREEN_WSCOLORS | WSSCREEN_UNDERLINE |
1271733da76fSmacallan WSSCREEN_REVERSE | WSSCREEN_RESIZE;
1272d2c9943bSmacallan
1273d2c9943bSmacallan /* enable acceleration */
1274d2c9943bSmacallan ri->ri_ops.copyrows = ffb_ras_copyrows;
1275d2c9943bSmacallan ri->ri_ops.eraserows = ffb_ras_eraserows;
1276d2c9943bSmacallan ri->ri_ops.erasecols = ffb_ras_erasecols;
1277d2c9943bSmacallan ri->ri_ops.cursor = ffb_cursor;
1278d2c9943bSmacallan ri->ri_ops.allocattr = ffb_allocattr;
1279b1dfce18Smacallan if (FONT_IS_ALPHA(ri->ri_font)) {
1280b1dfce18Smacallan ri->ri_ops.putchar = ffb_putchar_aa;
1281b1dfce18Smacallan } else
1282b1dfce18Smacallan ri->ri_ops.putchar = ffb_putchar_mono;
1283d2c9943bSmacallan }
1284342707d5Sjdc
1285342707d5Sjdc /* I2C bitbanging */
ffb_i2cbb_set_bits(void * cookie,uint32_t bits)1286342707d5Sjdc static void ffb_i2cbb_set_bits(void *cookie, uint32_t bits)
1287342707d5Sjdc {
1288342707d5Sjdc struct ffb_softc *sc = cookie;
1289342707d5Sjdc
1290342707d5Sjdc DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_CFG_MPDATA);
1291342707d5Sjdc DAC_WRITE(sc, FFB_DAC_VALUE, bits);
1292342707d5Sjdc }
1293342707d5Sjdc
ffb_i2cbb_set_dir(void * cookie,uint32_t dir)1294342707d5Sjdc static void ffb_i2cbb_set_dir(void *cookie, uint32_t dir)
1295342707d5Sjdc {
1296342707d5Sjdc /* Nothing to do */
1297342707d5Sjdc }
1298342707d5Sjdc
ffb_i2cbb_read(void * cookie)1299342707d5Sjdc static uint32_t ffb_i2cbb_read(void *cookie)
1300342707d5Sjdc {
1301342707d5Sjdc struct ffb_softc *sc = cookie;
1302342707d5Sjdc uint32_t bits;
1303342707d5Sjdc
1304342707d5Sjdc DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_CFG_MPSENSE);
1305342707d5Sjdc bits = DAC_READ(sc, FFB_DAC_VALUE);
1306342707d5Sjdc
1307342707d5Sjdc return bits;
1308342707d5Sjdc }
1309342707d5Sjdc
1310342707d5Sjdc /* higher level I2C stuff */
1311342707d5Sjdc static int
ffb_i2c_send_start(void * cookie,int flags)1312342707d5Sjdc ffb_i2c_send_start(void *cookie, int flags)
1313342707d5Sjdc {
1314342707d5Sjdc return (i2c_bitbang_send_start(cookie, flags, &ffb_i2cbb_ops));
1315342707d5Sjdc }
1316342707d5Sjdc
1317342707d5Sjdc static int
ffb_i2c_send_stop(void * cookie,int flags)1318342707d5Sjdc ffb_i2c_send_stop(void *cookie, int flags)
1319342707d5Sjdc {
1320342707d5Sjdc
1321342707d5Sjdc return (i2c_bitbang_send_stop(cookie, flags, &ffb_i2cbb_ops));
1322342707d5Sjdc }
1323342707d5Sjdc
1324342707d5Sjdc static int
ffb_i2c_initiate_xfer(void * cookie,i2c_addr_t addr,int flags)1325342707d5Sjdc ffb_i2c_initiate_xfer(void *cookie, i2c_addr_t addr, int flags)
1326342707d5Sjdc {
1327342707d5Sjdc /*
1328342707d5Sjdc * for some reason i2c_bitbang_initiate_xfer left-shifts
1329342707d5Sjdc * the I2C-address and then sets the direction bit
1330342707d5Sjdc */
1331342707d5Sjdc return (i2c_bitbang_initiate_xfer(cookie, addr, flags,
1332342707d5Sjdc &ffb_i2cbb_ops));
1333342707d5Sjdc }
1334342707d5Sjdc
1335342707d5Sjdc static int
ffb_i2c_read_byte(void * cookie,uint8_t * valp,int flags)1336342707d5Sjdc ffb_i2c_read_byte(void *cookie, uint8_t *valp, int flags)
1337342707d5Sjdc {
1338342707d5Sjdc return (i2c_bitbang_read_byte(cookie, valp, flags, &ffb_i2cbb_ops));
1339342707d5Sjdc }
1340342707d5Sjdc
1341342707d5Sjdc static int
ffb_i2c_write_byte(void * cookie,uint8_t val,int flags)1342342707d5Sjdc ffb_i2c_write_byte(void *cookie, uint8_t val, int flags)
1343342707d5Sjdc {
1344342707d5Sjdc return (i2c_bitbang_write_byte(cookie, val, flags, &ffb_i2cbb_ops));
1345342707d5Sjdc }
1346342707d5Sjdc
1347342707d5Sjdc
1348342707d5Sjdc #define TVC_READ_LIMIT 100000
1349342707d5Sjdc int
ffb_tgc_disable(struct ffb_softc * sc)1350342707d5Sjdc ffb_tgc_disable(struct ffb_softc *sc)
1351342707d5Sjdc {
1352342707d5Sjdc int i;
1353342707d5Sjdc
1354342707d5Sjdc /* Is the timing generator disabled? */
1355342707d5Sjdc DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_TGC);
1356342707d5Sjdc if (!(DAC_READ(sc, FFB_DAC_VALUE) & FFB_DAC_TGC_TIMING_ENABLE))
1357342707d5Sjdc return 1;
1358342707d5Sjdc
1359342707d5Sjdc /* If not, disable it when the vertical counter reaches 0 */
1360342707d5Sjdc for (i = 0; i < TVC_READ_LIMIT; i++) {
1361342707d5Sjdc DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_TVC);
1362342707d5Sjdc if (!DAC_READ(sc, FFB_DAC_VALUE)) {
1363342707d5Sjdc DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_TGC);
1364342707d5Sjdc DAC_WRITE(sc, FFB_DAC_VALUE, 0);
1365342707d5Sjdc return 1;
1366342707d5Sjdc }
1367342707d5Sjdc }
1368342707d5Sjdc return 0;
1369342707d5Sjdc }
1370342707d5Sjdc
1371342707d5Sjdc /*
1372342707d5Sjdc * PLL Control Register values:
1373342707d5Sjdc * M)ultiplier = bits 0:6 + 1
1374342707d5Sjdc * D)ivisor = bits 7:10 + 1
1375342707d5Sjdc * P)ost divisor = bits 11:13 (000 = 1, 001 = 2, 010 = 4, 011 = 8)
1376342707d5Sjdc * Frequency = 13.5 * M / D / P
1377342707d5Sjdc */
1378342707d5Sjdc #define FFB_PLL_FREQ 13500000
1379342707d5Sjdc void
ffb_get_pclk(int request,uint32_t * pll,int * diff)1380342707d5Sjdc ffb_get_pclk(int request, uint32_t *pll, int *diff)
1381342707d5Sjdc {
1382342707d5Sjdc int m, d, p, f, hex = 0, curdiff;
1383342707d5Sjdc
1384342707d5Sjdc *diff = 100000000;
1385342707d5Sjdc
1386342707d5Sjdc for (m = 32; m <= 80; m++) {
1387342707d5Sjdc for (d = 4; d <= 11; d++) {
1388342707d5Sjdc for (p = 1; p <= 8; p = p << 1) {
1389342707d5Sjdc switch (p) {
1390342707d5Sjdc case 1:
1391342707d5Sjdc hex = 0x4000 + (d << 7) + m;
1392342707d5Sjdc break;
1393342707d5Sjdc case 2:
1394342707d5Sjdc hex = 0x4800 + (d << 7) + m;
1395342707d5Sjdc break;
1396342707d5Sjdc case 4:
1397342707d5Sjdc hex = 0x5000 + (d << 7) + m;
1398342707d5Sjdc break;
1399342707d5Sjdc case 8:
1400342707d5Sjdc hex = 0x6000 + (d << 7) + m;
1401342707d5Sjdc break;
1402342707d5Sjdc }
1403342707d5Sjdc f = 13500000 * m / d / p;
1404342707d5Sjdc if (f == request) {
1405342707d5Sjdc *diff = 0;
1406342707d5Sjdc *pll = hex;
1407342707d5Sjdc return;
1408342707d5Sjdc } else {
1409342707d5Sjdc curdiff = abs(request - f);
1410342707d5Sjdc if (curdiff < *diff) {
1411342707d5Sjdc *diff = curdiff;
1412342707d5Sjdc *pll = hex;
1413342707d5Sjdc }
1414342707d5Sjdc }
1415342707d5Sjdc }
1416342707d5Sjdc }
1417342707d5Sjdc }
1418342707d5Sjdc }
1419342707d5Sjdc
1420342707d5Sjdc /*
1421342707d5Sjdc * Details of the FFB RAMDAC are contained in the Brooktree BT497/498
1422342707d5Sjdc * and in the Connexant BT497A/498A documentation.
1423342707d5Sjdc *
1424342707d5Sjdc * VESA timings to FFB register conversion:
1425342707d5Sjdc * If interleave = 4/2:1 then x = 2, if interleave = 8/2:1 then x = 4
1426342707d5Sjdc * VBE = VBS - vres = (sync pulse - 1) + back porch
1427342707d5Sjdc * VBS = VSS - front porch = (sync pulse - 1) + back porch + vres
1428342707d5Sjdc * VSE = sync pulse - 1
1429342707d5Sjdc * VSS = (sync pulse - 1) + back porch + vres + front porch
1430342707d5Sjdc * HRE = HSS - HSE - 1
1431342707d5Sjdc * HBE = (sync pulse + back porch) / x - 1
1432342707d5Sjdc * HBS = (sync pulse + back porch + hres) / x - 1
1433342707d5Sjdc * HSE = sync pulse / x - 1
1434342707d5Sjdc * HSS = (sync pulse + back porch + hres + front porch) / x - 1
1435342707d5Sjdc * HCE = HBS - 4
1436342707d5Sjdc * HCS = HBE - 4
1437342707d5Sjdc * EPE = EIE = EIS = 0 (for all non-interlaced modes)
1438342707d5Sjdc *
1439342707d5Sjdc * Note, that 8/2:1 Single Buffered Interleaving is only supported by the
1440342707d5Sjdc * double-buffered FFB (Creator3D), and at higher resolutions than 1280x1024
1441342707d5Sjdc *
1442342707d5Sjdc * Note, that the timing generator should be disabled and re-enabled when the
1443342707d5Sjdc * the timing parameter registers are being programmed. Stopping the timing
1444342707d5Sjdc * generator should only be done when the vertical counter is zero.
1445342707d5Sjdc */
1446342707d5Sjdc #define DIVIDE(x,y) (((x) + ((y) / 2)) / (y))
1447342707d5Sjdc int
ffb_set_vmode(struct ffb_softc * sc,struct videomode * mode,int btype,int * hres,int * vres)1448342707d5Sjdc ffb_set_vmode(struct ffb_softc *sc, struct videomode *mode, int btype,
1449342707d5Sjdc int *hres, int *vres)
1450342707d5Sjdc {
1451342707d5Sjdc int diff;
1452342707d5Sjdc uint32_t fp, sp, bp, x;
1453342707d5Sjdc uint32_t pll, pfc, ucl, dcl, tgc;
1454342707d5Sjdc uint32_t vbe, vbs, vse, vss, hre, hbe, hbs, hse, hss, hce, hcs;
1455342707d5Sjdc uint32_t epe, eie, eis;
1456342707d5Sjdc uint32_t fbcfg0;
1457342707d5Sjdc
1458342707d5Sjdc DPRINTF(("ffb_set_vmode: %dx%d@%d", mode->hdisplay, mode->vdisplay,
1459342707d5Sjdc DIVIDE(DIVIDE(mode->dot_clock * 1000,
1460342707d5Sjdc mode->htotal), mode->vtotal)));
1461342707d5Sjdc DPRINTF((" (%d %d %d %d %d %d %d",
1462342707d5Sjdc mode->dot_clock, mode->hsync_start, mode->hsync_end, mode->htotal,
1463342707d5Sjdc mode->vsync_start, mode->vsync_end, mode->vtotal));
1464342707d5Sjdc DPRINTF((" %s%sH %s%sV)\n",
1465342707d5Sjdc mode->flags & VID_PHSYNC ? "+" : "",
1466342707d5Sjdc mode->flags & VID_NHSYNC ? "-" : "",
1467342707d5Sjdc mode->flags & VID_PVSYNC ? "+" : "",
1468342707d5Sjdc mode->flags & VID_NVSYNC ? "-" : ""));
1469342707d5Sjdc
1470342707d5Sjdc /* We don't handle interlaced or doublescan (yet) */
1471342707d5Sjdc if ((mode->flags & VID_INTERLACE) || (mode->flags & VID_DBLSCAN))
1472342707d5Sjdc return 0;
1473342707d5Sjdc
1474342707d5Sjdc /* Only Creator3D can be set to > 1280x1024 */
1475342707d5Sjdc if(((sc->sc_type == FFB_CREATOR && !((btype & 7) == 3)) ||
1476342707d5Sjdc sc->sc_type == FFB_AFB)
1477342707d5Sjdc && (mode->hdisplay > 1280 || mode->vdisplay > 1024))
1478342707d5Sjdc return 0;
1479342707d5Sjdc /* Creator3D can be set to <= 1920x1360 */
1480342707d5Sjdc if (mode->hdisplay > 1920 || mode->vdisplay > 1360)
1481342707d5Sjdc return 0;
1482342707d5Sjdc
1483342707d5Sjdc /*
1484342707d5Sjdc * Look for a matching pixel clock and set PLL Control.
1485342707d5Sjdc * XXX: 640x480@60 is 25175000 in modelines but 25125000 in the
1486342707d5Sjdc * FFB PROM, and the closest match to 25175000 (0x4da9/25159090)
1487342707d5Sjdc * does not work. So, use the PROM value instead.
1488342707d5Sjdc */
1489342707d5Sjdc if (mode->hdisplay == 640 && mode->vdisplay == 480 &&
1490342707d5Sjdc mode->dot_clock == 25175) {
1491342707d5Sjdc DPRINTF(("ffb_set_vmode: 640x480@60: adjusted dot clock\n"));
1492342707d5Sjdc mode->dot_clock = 25125;
1493342707d5Sjdc }
1494342707d5Sjdc ffb_get_pclk(mode->dot_clock * 1000, &pll, &diff);
1495342707d5Sjdc if (diff > 250000)
1496342707d5Sjdc return 0;
1497342707d5Sjdc
1498342707d5Sjdc /* Pixel Format Control, User Control and FBC Configuration. */
1499342707d5Sjdc if (mode->hdisplay > 1280) {
1500342707d5Sjdc pfc = FFB_DAC_PIX_FMT_821;
1501342707d5Sjdc ucl = FFB_DAC_USR_CTRL_OVERLAY | FFB_DAC_USR_CTRL_WMODE_C;
1502342707d5Sjdc x = 4;
1503342707d5Sjdc fbcfg0 = FBC_READ(sc, FFB_FBC_FBCFG0) | FBC_CFG0_DOUBLE_BUF;
1504342707d5Sjdc } else {
1505342707d5Sjdc pfc = FFB_DAC_PIX_FMT_421;
1506342707d5Sjdc /* Only Creator3D and Elite3D can have double-buffer */
1507342707d5Sjdc if ((sc->sc_type == FFB_CREATOR && !((btype & 7) == 3)))
1508342707d5Sjdc ucl = 0;
1509342707d5Sjdc else
1510342707d5Sjdc ucl = FFB_DAC_USR_CTRL_DOUBLE;
1511342707d5Sjdc ucl |= (FFB_DAC_USR_CTRL_OVERLAY | FFB_DAC_USR_CTRL_WMODE_S8);
1512342707d5Sjdc x = 2;
1513342707d5Sjdc fbcfg0 = FBC_READ(sc, FFB_FBC_FBCFG0) | FBC_CFG0_SINGLE_BUF;
1514342707d5Sjdc }
1515342707d5Sjdc
1516342707d5Sjdc /* DAC Control and Timing Generator Control */
1517d5f801e0Sjdc if (mode->flags & VID_PVSYNC)
1518d5f801e0Sjdc dcl = FFB_DAC_DAC_CTRL_POS_VSYNC;
1519d5f801e0Sjdc else
1520342707d5Sjdc dcl = 0;
1521342707d5Sjdc tgc = 0;
1522342707d5Sjdc #define EDID_VID_INP sc->sc_edid_info.edid_video_input
1523d5f801e0Sjdc if ((EDID_VID_INP & EDID_VIDEO_INPUT_COMPOSITE_SYNC)) {
1524d5f801e0Sjdc dcl |= FFB_DAC_DAC_CTRL_VSYNC_DIS;
1525d5f801e0Sjdc tgc = FFB_DAC_TGC_EQUAL_DISABLE;
1526d5f801e0Sjdc } else {
1527342707d5Sjdc dcl |= FFB_DAC_DAC_CTRL_SYNC_G;
15288902e570Sjdc if (EDID_VID_INP & EDID_VIDEO_INPUT_SEPARATE_SYNCS)
1529342707d5Sjdc tgc |= FFB_DAC_TGC_VSYNC_DISABLE;
1530d5f801e0Sjdc else
1531d5f801e0Sjdc tgc = FFB_DAC_TGC_EQUAL_DISABLE;
1532342707d5Sjdc }
1533342707d5Sjdc if (EDID_VID_INP & EDID_VIDEO_INPUT_BLANK_TO_BLACK)
1534342707d5Sjdc dcl |= FFB_DAC_DAC_CTRL_PED_ENABLE;
1535342707d5Sjdc tgc |= (FFB_DAC_TGC_VIDEO_ENABLE | FFB_DAC_TGC_TIMING_ENABLE |
1536342707d5Sjdc FFB_DAC_TGC_MASTER_ENABLE);
1537342707d5Sjdc
1538342707d5Sjdc /* Vertical timing */
1539342707d5Sjdc fp = mode->vsync_start - mode->vdisplay;
1540342707d5Sjdc sp = mode->vsync_end - mode->vsync_start;
1541342707d5Sjdc bp = mode->vtotal - mode->vsync_end;
1542342707d5Sjdc
1543342707d5Sjdc vbe = sp - 1 + bp;
1544342707d5Sjdc vbs = sp - 1 + bp + mode->vdisplay;
1545342707d5Sjdc vse = sp - 1;
1546342707d5Sjdc vss = sp - 1 + bp + mode->vdisplay + fp;
1547342707d5Sjdc
1548342707d5Sjdc /* Horizontal timing */
1549342707d5Sjdc fp = mode->hsync_start - mode->hdisplay;
1550342707d5Sjdc sp = mode->hsync_end - mode->hsync_start;
1551342707d5Sjdc bp = mode->htotal - mode->hsync_end;
1552342707d5Sjdc
1553342707d5Sjdc hbe = (sp + bp) / x - 1;
1554342707d5Sjdc hbs = (sp + bp + mode->hdisplay) / x - 1;
1555342707d5Sjdc hse = sp / x - 1;
1556342707d5Sjdc hss = (sp + bp + mode->hdisplay + fp) / x -1;
1557342707d5Sjdc hre = hss - hse - 1;
1558342707d5Sjdc hce = hbs - 4;
1559342707d5Sjdc hcs = hbe - 4;
1560342707d5Sjdc
1561342707d5Sjdc /* Equalisation (interlaced modes) */
1562342707d5Sjdc epe = 0;
1563342707d5Sjdc eie = 0;
1564342707d5Sjdc eis = 0;
1565342707d5Sjdc
1566342707d5Sjdc DPRINTF(("ffb_set_vmode: 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x\n",
1567342707d5Sjdc pll, pfc, ucl, dcl, tgc));
1568342707d5Sjdc DPRINTF(("\t0x%04x 0x%04x 0x%04x 0x%04x\n", vbe, vbs, vse, vss));
1569342707d5Sjdc DPRINTF(("\t0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x\n",
1570342707d5Sjdc hre, hbe, hbs, hse, hss, hce, hcs));
1571342707d5Sjdc DPRINTF(("\t0x%04x 0x%04x 0x%04x\n", epe, eie, eis));
1572342707d5Sjdc
1573342707d5Sjdc if (!ffb_tgc_disable(sc)) {
1574342707d5Sjdc DPRINTF(("ffb_set_vmode: failed to disable TGC register\n"));
1575342707d5Sjdc return 0;
1576342707d5Sjdc }
1577342707d5Sjdc
1578342707d5Sjdc /*
1579342707d5Sjdc * Program the mode registers.
1580342707d5Sjdc * Program the timing generator last, as that re-enables output.
1581342707d5Sjdc * Note, that a read to/write from a register increments the
1582342707d5Sjdc * register address to the next register automatically.
1583342707d5Sjdc */
1584342707d5Sjdc DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_PLL_CTRL);
1585342707d5Sjdc DAC_WRITE(sc, FFB_DAC_VALUE, pll);
1586342707d5Sjdc DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_PIX_FMT);
1587342707d5Sjdc DAC_WRITE(sc, FFB_DAC_VALUE, pfc);
1588342707d5Sjdc DAC_WRITE(sc, FFB_DAC_VALUE, ucl);
1589342707d5Sjdc DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_DAC_CTRL);
1590342707d5Sjdc DAC_WRITE(sc, FFB_DAC_VALUE, dcl);
1591342707d5Sjdc
1592342707d5Sjdc DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_VBE);
1593342707d5Sjdc DAC_WRITE(sc, FFB_DAC_VALUE, vbe);
1594342707d5Sjdc DAC_WRITE(sc, FFB_DAC_VALUE, vbs);
1595342707d5Sjdc DAC_WRITE(sc, FFB_DAC_VALUE, vse);
1596342707d5Sjdc DAC_WRITE(sc, FFB_DAC_VALUE, vss);
1597342707d5Sjdc
1598342707d5Sjdc DAC_WRITE(sc, FFB_DAC_VALUE, hre);
1599342707d5Sjdc DAC_WRITE(sc, FFB_DAC_VALUE, hbe);
1600342707d5Sjdc DAC_WRITE(sc, FFB_DAC_VALUE, hbs);
1601342707d5Sjdc DAC_WRITE(sc, FFB_DAC_VALUE, hse);
1602342707d5Sjdc DAC_WRITE(sc, FFB_DAC_VALUE, hss);
1603342707d5Sjdc DAC_WRITE(sc, FFB_DAC_VALUE, hce);
1604342707d5Sjdc DAC_WRITE(sc, FFB_DAC_VALUE, hcs);
1605342707d5Sjdc
1606342707d5Sjdc DAC_WRITE(sc, FFB_DAC_VALUE, epe);
1607342707d5Sjdc DAC_WRITE(sc, FFB_DAC_VALUE, eie);
1608342707d5Sjdc DAC_WRITE(sc, FFB_DAC_VALUE, eis);
1609342707d5Sjdc
1610342707d5Sjdc FBC_WRITE(sc, FFB_FBC_FBCFG0, fbcfg0);
1611342707d5Sjdc
1612342707d5Sjdc DAC_WRITE(sc, FFB_DAC_TYPE, FFB_DAC_TGC);
1613342707d5Sjdc DAC_WRITE(sc, FFB_DAC_VALUE, tgc);
16143bdd0f03Smacallan DPRINTF(("new tgc: %08x\n", tgc));
1615342707d5Sjdc
1616342707d5Sjdc *hres = mode->hdisplay;
1617342707d5Sjdc *vres = mode->vdisplay;
1618342707d5Sjdc
1619342707d5Sjdc printf("%s: video mode set to %d x %d @ %dHz\n",
1620817999e9Schristos device_xname(sc->sc_dev),
1621342707d5Sjdc mode->hdisplay, mode->vdisplay,
1622342707d5Sjdc DIVIDE(DIVIDE(mode->dot_clock * 1000,
1623342707d5Sjdc mode->htotal), mode->vtotal));
1624342707d5Sjdc
1625342707d5Sjdc return 1;
1626342707d5Sjdc }
1627