1 /* $OpenBSD: tvtwo.c,v 1.18 2022/07/15 17:57:27 kettenis Exp $ */
2
3 /*
4 * Copyright (c) 2003, 2006, 2008, Miodrag Vallat.
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
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
25 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 *
28 */
29
30 /*
31 * Driver for the Parallax XVideo and PowerVideo graphics boards.
32 *
33 * Some details about these board used to be available at:
34 * http://www.jlw.com/~woolsey/parallax/support/developers/xvideotech.html
35 */
36
37 /*
38 * The Parallax XVideo series frame buffers are 8/24-bit accelerated
39 * frame buffers, with hardware MPEG capabilities using a CCube chipset.
40 */
41
42 /*
43 * Currently, this driver can only handle the 8-bit and 24-bit planes of the
44 * frame buffer, in an unaccelerated mode.
45 *
46 * TODO:
47 * - nvram handling
48 * - use the accelerator
49 * - interface to the c^3
50 */
51
52 #include <sys/param.h>
53 #include <sys/systm.h>
54 #include <sys/buf.h>
55 #include <sys/device.h>
56 #include <sys/ioctl.h>
57 #include <sys/mman.h>
58 #include <sys/conf.h>
59
60 #include <uvm/uvm_extern.h>
61
62 #include <machine/autoconf.h>
63 #include <machine/bus.h>
64 #include <machine/pmap.h>
65 #include <machine/cpu.h>
66 #include <machine/conf.h>
67
68 #include <dev/wscons/wsconsio.h>
69 #include <dev/wscons/wsdisplayvar.h>
70 #include <dev/rasops/rasops.h>
71 #include <machine/fbvar.h>
72
73 #include <dev/sbus/sbusvar.h>
74
75 /*
76 * The memory layout of the board is as follows:
77 *
78 * PROM0 000000 - 00ffff
79 * overlay plane 010000 - 037fff
80 * registers 040000 - 0404d0
81 * CCube 050000 - 05ffff
82 * 8-bit plane 080000 - 17ffff
83 * 24-bit plane 200000 - 6fffff
84 * PROM1 7f0000 - 7fffff
85 */
86
87 #define PX_PROM0_OFFSET 0x000000
88 #define PX_OVERLAY_OFFSET 0x010000
89 #define PX_REG_OFFSET 0x040000
90 #define PX_CCUBE_OFFSET 0x050000
91 #define PX_PLANE8_OFFSET 0x080000
92 #define PX_PLANE24_OFFSET 0x200000
93 #define PX_PROM1_OFFSET 0x7f0000
94
95 #define PX_MAP_SIZE 0x800000
96
97 /*
98 * Partial registers layout
99 */
100
101 #define PX_REG_DISPKLUGE 0x00b8 /* write only */
102 #define DISPKLUGE_DEFAULT 0xc41f
103 #define DISPKLUGE_BLANK (1 << 12)
104 #define DISPKLUGE_SYNC (1 << 13)
105
106 #define PX_REG_BT463_RED 0x0480
107 #define PX_REG_BT463_GREEN 0x0490
108 #define PX_REG_BT463_BLUE 0x04a0
109 #define PX_REG_BT463_ALL 0x04b0
110
111 #define PX_REG_SIZE 0x04d0
112
113
114 /* per-display variables */
115 struct tvtwo_softc {
116 struct sunfb sc_sunfb; /* common base device */
117
118 bus_space_tag_t sc_bustag;
119 bus_addr_t sc_paddr;
120
121 volatile u_int8_t *sc_m8;
122 volatile u_int8_t *sc_m24;
123 volatile u_int8_t *sc_regs;
124
125 int sc_nscreens;
126 };
127
128 int tvtwo_ioctl(void *, u_long, caddr_t, int, struct proc *);
129 paddr_t tvtwo_mmap(void *, off_t, int);
130 void tvtwo_burner(void *, u_int, u_int);
131
132 struct wsdisplay_accessops tvtwo_accessops = {
133 .ioctl = tvtwo_ioctl,
134 .mmap = tvtwo_mmap,
135 .burn_screen = tvtwo_burner
136 };
137
138 void tvtwo_directcmap(struct tvtwo_softc *);
139 static __inline__
140 void tvtwo_ramdac_wraddr(struct tvtwo_softc *, u_int32_t);
141 void tvtwo_reset(struct tvtwo_softc *, u_int);
142 void tvtwo_setcolor(void *, u_int, u_int8_t, u_int8_t, u_int8_t);
143
144 int tvtwomatch(struct device *, void *, void *);
145 void tvtwoattach(struct device *, struct device *, void *);
146
147 const struct cfattach tvtwo_ca = {
148 sizeof(struct tvtwo_softc), tvtwomatch, tvtwoattach
149 };
150
151 struct cfdriver tvtwo_cd = {
152 NULL, "tvtwo", DV_DULL
153 };
154
155 /*
156 * Default frame buffer resolution, depending upon the "freqcode"
157 */
158 #define NFREQCODE 5
159 const int defwidth[NFREQCODE] = { 1152, 1152, 1152, 1024, 640 };
160 const int defheight[NFREQCODE] = { 900, 900, 900, 768, 480 };
161
162 /*
163 * Match an XVideo or PowerVideo card.
164 */
165 int
tvtwomatch(struct device * parent,void * vcf,void * aux)166 tvtwomatch(struct device *parent, void *vcf, void *aux)
167 {
168 struct sbus_attach_args *sa = aux;
169
170 if (strcmp(sa->sa_name, "PGI,tvtwo") == 0 ||
171 strcmp(sa->sa_name, "PGI,tvthree") == 0)
172 return (1);
173
174 return (0);
175 }
176
177 /*
178 * Attach a display.
179 */
180 void
tvtwoattach(struct device * parent,struct device * self,void * args)181 tvtwoattach(struct device *parent, struct device *self, void *args)
182 {
183 struct tvtwo_softc *sc = (struct tvtwo_softc *)self;
184 struct sbus_attach_args *sa = args;
185 bus_space_tag_t bt;
186 bus_space_handle_t bh;
187 int node, width, height, freqcode;
188 int isconsole;
189 char *freqstring, *revision;
190
191 bt = sa->sa_bustag;
192 node = sa->sa_node;
193
194 printf(": %s", getpropstring(node, "model"));
195 revision = getpropstring(node, "revision");
196 if (*revision != '\0')
197 printf(", revision %s", revision);
198
199 /* Older XVideo provide two sets of SBus registers:
200 * R0 040000 - 040800
201 * R1 080000 - 17d200
202 * While the more recent revisions provide only one register:
203 * R0 000000 - 7fffff
204 *
205 * We'll simply ``rewrite'' R0 on older boards and handle them as
206 * recent boards.
207 */
208 if (sa->sa_nreg > 1) {
209 sa->sa_offset -= PX_REG_OFFSET;
210 sa->sa_size = PX_MAP_SIZE;
211 }
212
213 isconsole = node == fbnode;
214
215 /* Map registers. */
216 sc->sc_bustag = bt;
217 if (sbus_bus_map(bt, sa->sa_slot, sa->sa_offset + PX_REG_OFFSET,
218 PX_REG_SIZE, BUS_SPACE_MAP_LINEAR, 0, &bh) != 0) {
219 printf("%s: couldn't map registers\n", self->dv_xname);
220 return;
221 }
222 sc->sc_regs = bus_space_vaddr(bt, bh);
223
224 /*
225 * Compute framebuffer size.
226 * Older boards do not have the ``freqcode'' property and are
227 * restricted to 1152x900.
228 */
229 freqstring = getpropstring(node, "freqcode");
230 if (*freqstring != '\0') {
231 freqcode = (int)*freqstring;
232 if (freqcode == 'g') {
233 width = height = 1024;
234 } else {
235 if (freqcode < '1' || freqcode > '6')
236 freqcode = 0;
237 else
238 freqcode -= '1';
239 width = defwidth[freqcode];
240 height = defheight[freqcode];
241
242 /* in case our table is wrong or incomplete... */
243 width = getpropint(node, "hres", width);
244 height = getpropint(node, "vres", height);
245 }
246 } else {
247 width = 1152;
248 height = 900;
249 }
250
251 /*
252 * Since the depth property is missing, we could do
253 * fb_setsize(&sc->sc_sunfb, 8, width, height, node, 0);
254 * but for safety in case it would exist and be set to 32, do it
255 * manually...
256 */
257 sc->sc_sunfb.sf_depth = 8;
258 sc->sc_sunfb.sf_width = width;
259 sc->sc_sunfb.sf_height = height;
260 sc->sc_sunfb.sf_linebytes = width >= 1024 ? width : 1024;
261 sc->sc_sunfb.sf_fbsize = sc->sc_sunfb.sf_linebytes * height;
262
263 printf(", %dx%d\n", sc->sc_sunfb.sf_width, sc->sc_sunfb.sf_height);
264
265 /* Map the frame buffer memory area we're interested in. */
266 sc->sc_paddr = sbus_bus_addr(bt, sa->sa_slot, sa->sa_offset);
267 if (sbus_bus_map(bt, sa->sa_slot, sa->sa_offset + PX_PLANE8_OFFSET,
268 round_page(sc->sc_sunfb.sf_fbsize), BUS_SPACE_MAP_LINEAR, 0,
269 &bh) != 0) {
270 printf("%s: couldn't map 8-bit video plane\n", self->dv_xname);
271 return;
272 }
273 sc->sc_m8 = bus_space_vaddr(bt, bh);
274 if (sbus_bus_map(bt, sa->sa_slot, sa->sa_offset + PX_PLANE24_OFFSET,
275 round_page(4 * sc->sc_sunfb.sf_fbsize), BUS_SPACE_MAP_LINEAR, 0,
276 &bh) != 0) {
277 printf("%s: couldn't map 32-bit video plane\n", self->dv_xname);
278 return;
279 }
280 sc->sc_m24 = bus_space_vaddr(bt, bh);
281
282 /* Enable video. */
283 tvtwo_burner(sc, 1, 0);
284
285 sc->sc_sunfb.sf_ro.ri_hw = sc;
286 sc->sc_sunfb.sf_ro.ri_bits = (u_char *)sc->sc_m8;
287
288 fbwscons_init(&sc->sc_sunfb, 0, isconsole);
289 fbwscons_setcolormap(&sc->sc_sunfb, tvtwo_setcolor);
290
291 if (isconsole)
292 fbwscons_console_init(&sc->sc_sunfb, -1);
293
294 fbwscons_attach(&sc->sc_sunfb, &tvtwo_accessops, isconsole);
295 }
296
297 int
tvtwo_ioctl(void * dev,u_long cmd,caddr_t data,int flags,struct proc * p)298 tvtwo_ioctl(void *dev, u_long cmd, caddr_t data, int flags, struct proc *p)
299 {
300 struct tvtwo_softc *sc = dev;
301 struct wsdisplay_fbinfo *wdf;
302
303 /*
304 * Note that, although the emulation (text) mode is running in a
305 * 8-bit plane, we advertise the frame buffer as 32-bit.
306 */
307 switch (cmd) {
308 case WSDISPLAYIO_GTYPE:
309 *(u_int *)data = WSDISPLAY_TYPE_XVIDEO;
310 break;
311 case WSDISPLAYIO_GINFO:
312 wdf = (struct wsdisplay_fbinfo *)data;
313 wdf->height = sc->sc_sunfb.sf_height;
314 wdf->width = sc->sc_sunfb.sf_width;
315 wdf->depth = 32;
316 wdf->stride = sc->sc_sunfb.sf_linebytes * 4;
317 wdf->offset = 0;
318 wdf->cmsize = 0;
319 break;
320 case WSDISPLAYIO_GETSUPPORTEDDEPTH:
321 *(u_int *)data = WSDISPLAYIO_DEPTH_24_32;
322 break;
323 case WSDISPLAYIO_LINEBYTES:
324 *(u_int *)data = sc->sc_sunfb.sf_linebytes * 4;
325 break;
326
327 case WSDISPLAYIO_GETCMAP:
328 case WSDISPLAYIO_PUTCMAP:
329 break;
330
331 case WSDISPLAYIO_SMODE:
332 if (*(int *)data == WSDISPLAYIO_MODE_EMUL) {
333 /* Back from X11 to text mode */
334 tvtwo_reset(sc, 8);
335 } else {
336 /* Starting X11, initialize 32-bit mode */
337 tvtwo_reset(sc, 32);
338 }
339 break;
340
341 case WSDISPLAYIO_SVIDEO:
342 case WSDISPLAYIO_GVIDEO:
343 break;
344
345 case WSDISPLAYIO_GCURPOS:
346 case WSDISPLAYIO_SCURPOS:
347 case WSDISPLAYIO_GCURMAX:
348 case WSDISPLAYIO_GCURSOR:
349 case WSDISPLAYIO_SCURSOR:
350 default:
351 return (-1);
352 }
353
354 return (0);
355 }
356
357 /*
358 * Return the address that would map the given device at the given
359 * offset, allowing for the given protection, or return -1 for error.
360 */
361 paddr_t
tvtwo_mmap(void * v,off_t offset,int prot)362 tvtwo_mmap(void *v, off_t offset, int prot)
363 {
364 struct tvtwo_softc *sc = v;
365
366 if (offset & PGOFSET)
367 return (-1);
368
369 /* Allow mapping as a dumb framebuffer from offset 0 */
370 if (offset >= 0 && offset < sc->sc_sunfb.sf_fbsize * 4) {
371 return (bus_space_mmap(sc->sc_bustag, sc->sc_paddr,
372 PX_PLANE24_OFFSET + offset, prot, BUS_SPACE_MAP_LINEAR));
373 }
374
375 return (-1);
376 }
377
378 void
tvtwo_burner(void * v,u_int on,u_int flags)379 tvtwo_burner(void *v, u_int on, u_int flags)
380 {
381 struct tvtwo_softc *sc = v;
382 u_int32_t dispkluge;
383
384 if (on)
385 dispkluge = DISPKLUGE_DEFAULT & ~DISPKLUGE_BLANK;
386 else {
387 dispkluge = DISPKLUGE_DEFAULT | DISPKLUGE_BLANK;
388 if (flags & WSDISPLAY_BURN_VBLANK)
389 dispkluge |= DISPKLUGE_SYNC;
390 }
391
392 *(volatile u_int32_t *)(sc->sc_regs + PX_REG_DISPKLUGE) = dispkluge;
393 }
394
395 void
tvtwo_reset(struct tvtwo_softc * sc,u_int depth)396 tvtwo_reset(struct tvtwo_softc *sc, u_int depth)
397 {
398 if (depth == 32) {
399 /* Initialize a direct color map. */
400 tvtwo_directcmap(sc);
401 } else {
402 fbwscons_setcolormap(&sc->sc_sunfb, tvtwo_setcolor);
403 }
404 }
405
406 /*
407 * Simple Bt463 programming routines.
408 */
409
410 static __inline__ void
tvtwo_ramdac_wraddr(struct tvtwo_softc * sc,u_int32_t addr)411 tvtwo_ramdac_wraddr(struct tvtwo_softc *sc, u_int32_t addr)
412 {
413 volatile u_int32_t *dac = (u_int32_t *)(sc->sc_regs + PX_REG_BT463_RED);
414
415 dac[0] = (addr & 0xff); /* lo addr */
416 dac[1] = ((addr >> 8) & 0xff); /* hi addr */
417 }
418
419 void
tvtwo_directcmap(struct tvtwo_softc * sc)420 tvtwo_directcmap(struct tvtwo_softc *sc)
421 {
422 volatile u_int32_t *dac = (u_int32_t *)(sc->sc_regs + PX_REG_BT463_RED);
423 u_int32_t c;
424
425 tvtwo_ramdac_wraddr(sc, 0);
426 for (c = 0; c < 256; c++) {
427 dac[3] = c; /* R */
428 dac[3] = c; /* G */
429 dac[3] = c; /* B */
430 }
431 }
432
433 void
tvtwo_setcolor(void * v,u_int index,u_int8_t r,u_int8_t g,u_int8_t b)434 tvtwo_setcolor(void *v, u_int index, u_int8_t r, u_int8_t g, u_int8_t b)
435 {
436 struct tvtwo_softc *sc = v;
437 volatile u_int32_t *dac = (u_int32_t *)(sc->sc_regs + PX_REG_BT463_RED);
438
439 tvtwo_ramdac_wraddr(sc, index);
440 dac[3] = r;
441 dac[3] = g;
442 dac[3] = b;
443 }
444