/*
* Copyright (c) 1995 Michael Teske
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Ezra Story, by Kari
* Mettinen, and Michael Teske.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "opt_amigacons.h"
/*
* Graphics routines for the CyberVision 64/3D board, using the S3 ViRGE.
*
* Modified for CV64/3D from Michael Teske's CV driver by Tobias Abt 10/97.
* Bugfixes by Bernd Ernesti 10/97.
* Many thanks to Richard Hartmann who gave us his board so we could make
* the driver.
*
* TODO:
* - ZorroII support
* - Blitter support
* - Memcheck for 2MB boards (if they exists)
*/
/* Thanks to Frank Mariak for these infos
BOARDBASE
+0x4000000 Memorybase start
+0x4ffffff Memorybase end
+0x5000000 Img TransPort start
+0x5007fff Img TransPort end
+0x5008000 MMIO Regbase start
+0x500ffff MMIO Regbase end
+0x5800000 Img TransPort (rot) start
+0x5807fff Img TransPort (rot) end
+0x7000000 Img TransPort (rot) start
+0x7007fff Img TransPort (rot) end
+0x8000000 VCodeSwitch start
+0x8000fff VCodeSwitch end
+0xc000000 IO Regbase start
+0xc00ffff IO Regbase end
+0xc0e0000 PCI Cfg Base start
+0xc0e0fff PCI Cfg Base end
Note: IO Regbase is needed for wakeup of the board otherwise use
MMIO Regbase
*/
int cv3d_zorroIII = 0; /* CV64/3D in ZorroII or ZorroIII mode */
unsigned char cv3d_pass_toggle; /* passthru status tracker */
/* Console display definition.
* Default hardcoded text mode. This grf_cv3d is set up to
* use one text mode only, and this is it. You may use
* grfconfig to change the mode after boot.
*/
#define CV3D_ULCURSOR 1 /* Underlined Cursor in textmode */
/*
* Get framebuffer memory size.
* phase5 didn't provide the bit in CR36,
* so we have to do it this way.
* Return 0 for 2MB, 1 for 4MB
*/
static int
cv3d_has_4mb(volatile void *fb)
{
#if 0 /* XXX */
volatile unsigned long *testfbw, *testfbr;
/* write patterns in memory and test if they can be read */
testfbw = (volatile unsigned long *)fb;
testfbr = (volatile unsigned long *)(fb + 0x02000000);
*testfbw = 0x87654321;
if (*testfbr != 0x87654321)
return (0);
/* upper memory region */
testfbw = (volatile unsigned long *)(fb + 0x00200000);
testfbr = (volatile unsigned long *)(fb + 0x02200000);
*testfbw = 0x87654321;
if (*testfbr != 0x87654321)
return (0);
*testfbw = 0xAAAAAAAA;
if (*testfbr != 0xAAAAAAAA)
return (0);
*testfbw = 0x55555555;
if (*testfbr != 0x55555555)
return (0);
#endif
return (1);
}
int
grfcv3dmatch(device_t parent, cfdata_t cf, void *aux)
{
#ifdef CV3DCONSOLE
static int cv3dcons_unit = -1;
#endif
struct zbus_args *zap;
zap = aux;
if (amiga_realconfig == 0)
#ifdef CV3DCONSOLE
if (cv3dcons_unit != -1)
#endif
return (0);
/*
* Distinct between ZorroII or ZorroIII mode.
* Note that iszthreepa(x) is true for the Z2 bus on the DraCo;
* therefore we check for the size instead.
*/
cv3d_zorroIII = zap->size > 4*1024*1024;
/* Lets be Paranoid: Test man and prod id */
if (zap->manid != 8512 || zap->prodid != 67)
return (0);
/*
* attach grf
*/
if (amiga_config_found(cfdata, gp->g_device, gp, grfcv3dprint,
CFARGS_NONE)) {
if (self != NULL)
printf("%s: CyberVision64/3D with %dMB being used\n",
device_xname(self), cv3d_fbsize / 0x100000);
attachflag = 1;
} else {
if (!attachflag)
/*printf("grfcv3d unattached!!\n")*/;
}
}
int
grfcv3dprint(void *aux, const char *pnp)
{
if (pnp)
aprint_normal("ite at %s: ", pnp);
return (UNCONF);
}
/*
* Computes M, N, and R values from
* given input frequency. It uses a table of
* precomputed values, to keep CPU time low.
*
* The return value consist of:
* lower byte: Bits 4-0: N Divider Value
* Bits 5-6: R Value for e.g. SR10 or SR12
* higher byte: Bits 0-6: M divider value for e.g. SR11 or SR13
*/
static unsigned short
cv3d_compute_clock(unsigned long freq)
{
static unsigned char *mnr, *save; /* M, N + R vals */
unsigned long work_freq, r;
unsigned short erg;
long diff, d2;
if (freq < 12500000 || freq > MAXPIXELCLOCK) {
printf("grfcv3d: Illegal clock frequency: %ldMHz\n", freq/1000000);
printf("grfcv3d: Using default frequency: 25MHz\n");
printf("grfcv3d: See the manpage of grfconfig for more informations.\n");
freq = 25000000;
}
mnr = clocks; /* there the vals are stored */
d2 = 0x7fffffff;
int
cv3d_isblank(struct grf_softc *gp)
{
volatile void *ba;
int r;
ba = gp->g_regkva;
r = RSeq(ba, SEQ_ID_CLOCKING_MODE);
return (r & 0x20) != 0;
}
/*
* Change the mode of the display.
* Return a UNIX error number or 0 for success.
*/
int
cv3d_mode(register struct grf_softc *gp, u_long cmd, void *arg, u_long a2,
int a3)
{
int error;
case GRFIOCSSPRITEPOS:
return(cv3d_setspritepos (gp, (struct grf_position *) data));
case GRFIOCSSPRITEINF:
return(cv3d_setspriteinfo (gp, (struct grf_spriteinfo *) data));
case GRFIOCGSPRITEINF:
return(cv3d_getspriteinfo (gp, (struct grf_spriteinfo *) data));
case GRFIOCGSPRITEMAX:
return(cv3d_getspritemax (gp, (struct grf_position *) data));
#else /* CV3D_HARDWARE_CURSOR */
case GRFIOCGSPRITEPOS:
case GRFIOCSSPRITEPOS:
case GRFIOCSSPRITEINF:
case GRFIOCGSPRITEINF:
case GRFIOCGSPRITEMAX:
break;
#endif /* CV3D_HARDWARE_CURSOR */
case GRFIOCGETCMAP:
return (cv3d_getcmap (gp, (struct grf_colormap *) data));
case GRFIOCPUTCMAP:
return (cv3d_putcmap (gp, (struct grf_colormap *) data));
case GRFIOCBITBLT:
break;
case GRFTOGGLE:
return (cv3d_toggle (gp));
case GRFIOCSETMON:
return (cv3d_setmonitor (gp, (struct grfvideo_mode *)data));
/*
* Prevent user from crashing the system by using
* grfconfig while in X
*/
if (gp->g_flags & GF_GRFON)
if (md == monitor_current) {
printf("grfcv3d: Changing the used mode not allowed!\n");
return (EINVAL);
}
memcpy(md, gv, sizeof(struct grfvideo_mode));
/* adjust pixel oriented values to internal rep. */
int
cv3d_mondefok(struct grfvideo_mode *gv)
{
unsigned long maxpix;
if (gv->mode_num < 1 || gv->mode_num > monitor_def_max) {
if (gv->mode_num != 255 || gv->depth != 4)
return (0);
}
switch(gv->depth) {
case 4:
maxpix = MAXPIXELCLOCK - 55000000;
break;
case 8:
maxpix = MAXPIXELCLOCK;
break;
case 15:
case 16:
#ifdef CV3D_AGGRESSIVE_TIMING
maxpix = MAXPIXELCLOCK - 35000000;
#else
maxpix = MAXPIXELCLOCK - 55000000;
#endif
break;
case 24:
case 32:
#ifdef CV3D_AGGRESSIVE_TIMING
maxpix = MAXPIXELCLOCK - 75000000;
#else
maxpix = MAXPIXELCLOCK - 85000000;
#endif
break;
default:
printf("grfcv3d: Illegal depth in mode %d\n",
(int) gv->mode_num);
return (0);
}
if (gv->pixel_clock > maxpix) {
printf("grfcv3d: Pixelclock too high in mode %d\n",
(int) gv->mode_num);
return (0);
}
if (gv->mode_num == 255) { /* console mode */
if ((gv->disp_width / 8) > MAXCOLS) {
printf ("grfcv3d: Too many columns for console\n");
return (0);
} else if ((gv->disp_height / S3FONTY) > MAXROWS) {
printf ("grfcv3d: Too many rows for console\n");
return (0);
}
}
if (gv->disp_flags & GRF_FLAGS_SYNC_ON_GREEN) {
printf("grfcv3d: sync-on-green is not supported\n");
return (0);
}
return (1);
}
int
cv3d_load_mon(struct grf_softc *gp, struct grfcv3dtext_mode *md)
{
struct grfvideo_mode *gv;
struct grfinfo *gi;
volatile void *ba;
unsigned short mnr;
unsigned short HT, HDE, HBS, HBE, HSS, HSE, VDE, VBS, VBE, VSS,
VSE, VT;
int cr50, cr66, sr15, sr18, clock_mode, test;
int hmul; /* Multiplier for hor. Values */
int fb_flag = 2; /* default value for 8bit memory access */
unsigned char hvsync_pulse;
char TEXT, CONSOLE;
/* identity */
gv = &md->gv;
TEXT = (gv->depth == 4);
CONSOLE = (gv->mode_num == 255);
if (!cv3d_mondefok(gv)) {
printf("grfcv3d: Monitor definition not ok\n");
return (0);
}
ba = gp->g_regkva;
/* turn gfx off, don't mess up the display */
cv3d_gfx_on_off(1, ba);
/*
* Disable enhanced Mode for text display
*
* XXX You need to set this bit in CRT_ID_EXT_MISC_CNTL_1
* _and_ MR_ADVANCED_FUNCTION_CONTROL, because the same
* function exists in both registers.
*/
cr66 = RCrt(ba, CRT_ID_EXT_MISC_CNTL_1);
if (TEXT) {
cr66 &= ~0x01;
vgaw32(cv3d_memory_io_base, MR_ADVANCED_FUNCTION_CONTROL,
0x00000010);
} else {
cr66 |= 0x01;
vgaw32(cv3d_memory_io_base, MR_ADVANCED_FUNCTION_CONTROL,
0x00000011);
}
WCrt(ba, CRT_ID_EXT_MISC_CNTL_1, cr66);
/* load text font into beginning of display memory.
* Each character cell is 32 bytes long (enough for 4 planes)
* In linear addressing text mode, the memory is organized
* so, that the Bytes of all 4 planes are interleaved.
* 1st byte plane 0, 1st byte plane 1, 1st byte plane 2,
* 1st byte plane 3, 2nd byte plane 0, 2nd byte plane 1,...
* The font is loaded in plane 2.
*/
c = (volatile unsigned char *) fb;
/* clear screen */
for (z = 0; z < tm->cols * tm->rows * 3; z++) {
*c++ = 0x20;
*c++ = 0x07;
*c++ = 0;
*c++ = 0;
}
c = (volatile unsigned char *)fb + (32 * tm->fdstart * 4 + 2);
f = tm->fdata;
for (z = tm->fdstart; z <= tm->fdend; z++, c += (32 - tm->fy) * 4)
for (y = 0; y < tm->fy; y++) {
*c = *f++;
c += 4;
}
/* print out a little init msg */
c = (volatile unsigned char *)fb + (tm->cols - 9) * 4;
*c++ = 'C';
*c++ = 0x0c;
c +=2;
*c++ = 'V';
*c++ = 0x0c;
c +=2;
*c++ = '6';
*c++ = 0x0b;
c +=2;
*c++ = '4';
*c++ = 0x0f;
c +=2;
*c++ = '/';
*c++ = 0x0e;
c +=2;
*c++ = '3';
*c++ = 0x0a;
c +=2;
*c++ = 'D';
*c++ = 0x0a;
}
if (gp->g_display.gd_planes <= 4)
cv3d_cursor_on = 0; /* don't enable hwc in text modes */
if (cv3d_cursor_on == 0)
return;
/* reset colour stack */
#if !defined(__m68k__)
test = RCrt(ba, CRT_ID_HWGC_MODE);
cpu_sync();
#else
/* do it in assembler, the above does't seem to work */
__asm volatile ("moveb #0x45, %1@(0x3d4); \
moveb %1@(0x3d5),%0" : "=r" (test) : "a" (ba));
#endif
WCrt (ba, CRT_ID_HWGC_FG_STACK, 0);
hwc = ba + CRT_ADDRESS_W;
*hwc = 0;
*hwc = 0;
#if !defined(__m68k__)
test = RCrt(ba, CRT_ID_HWGC_MODE);
cpu_sync();
#else
/* do it in assembler, the above does't seem to work */
__asm volatile ("moveb #0x45, %1@(0x3d4); \
moveb %1@(0x3d5),%0" : "=r" (test) : "a" (ba));
#endif
switch (gp->g_display.gd_planes) {
case 8:
WCrt (ba, CRT_ID_HWGC_BG_STACK, 0x1);
*hwc = 1;
break;
default:
WCrt (ba, CRT_ID_HWGC_BG_STACK, 0xff);
*hwc = 0xff;
*hwc = 0xff;
}
WCrt (ba, CRT_ID_EXT_DAC_CNTL, 0x10); /* Cursor X11 Mode */
/*
* Put it into Windoze Mode or you'll see sometimes a white stripe
* on the right side (in double clocking modes with a screen bigger
* > 1023 pixels).
*/
WCrt (ba, CRT_ID_EXT_DAC_CNTL, 0x00); /* Cursor Windoze Mode */
WCrt (ba, CRT_ID_HWGC_MODE, 0x01);
}
/*
* This was the reason why you shouldn't use the HWC in the Kernel:(
* Obsoleted now by use of interrupts :-)
*/
int
cv3d_setspriteinfo(struct grf_softc *gp, struct grf_spriteinfo *info)
{
volatile void *ba, fb;
int depth = gp->g_display.gd_planes;
ba = gp->g_regkva;
fb = gp->g_fbkva;
if (info->set & GRFSPRSET_SHAPE) {
/*
* For an explanation of these weird actions here, see above
* when reading the shape. We set the shape directly into
* the video memory, there's no reason to keep 1k on the
* kernel stack just as template
*/
u_char *image, *mask;
volatile u_short *hwp;
u_char *imp, *mp;
unsigned short row;
/* Cursor off */
WCrt (ba, CRT_ID_HWGC_MODE, 0x00);
/*
* The Trio64 crashes if the cursor data is written
* while the cursor is displayed.
* Sadly, turning the cursor off is not enough.
* What we have to do is:
* 1. Wait for vertical retrace, to make sure no-one
* has moved the cursor in this sync period (because
* another write then would have no effect, argh!).
* 2. Move the cursor off-screen
* 3. Another wait for v. retrace to make sure the cursor
* is really off.
* 4. Write the data, finally.
* (thanks to Harald Koenig for this tip!)
*/
/*
* Remark 06/06/96: Update in interrupt obsoletes this,
* but the warning should stay there!
*/
/* One must not write twice per vertical blank :-( */
VerticalRetraceWait(ba);
cv3d_setspritepos(gp, &info->pos);
}
if (info->set & GRFSPRSET_CMAP) {
volatile void *hwc;
int test;
/* reset colour stack */
test = RCrt(ba, CRT_ID_HWGC_MODE);
cpu_sync();
switch (depth) {
case 8:
case 15:
case 16:
WCrt (ba, CRT_ID_HWGC_FG_STACK, 0);
hwc = ba + CRT_ADDRESS_W;
*hwc = 0;
break;
case 32:
case 24:
WCrt (ba, CRT_ID_HWGC_FG_STACK, 0);
hwc = ba + CRT_ADDRESS_W;
*hwc = 0;
*hwc = 0;
break;
}
test = RCrt(ba, CRT_ID_HWGC_MODE);
cpu_sync();
switch (depth) {
case 8:
WCrt (ba, CRT_ID_HWGC_BG_STACK, 1);
hwc = ba + CRT_ADDRESS_W;
*hwc = 1;
break;
case 15:
case 16:
WCrt (ba, CRT_ID_HWGC_BG_STACK, 0xff);
hwc = ba + CRT_ADDRESS_W;
*hwc = 0xff;
break;
case 32:
case 24:
WCrt (ba, CRT_ID_HWGC_BG_STACK, 0xff);
hwc = ba + CRT_ADDRESS_W;
*hwc = 0xff;
*hwc = 0xff;
break;
}
}
if (info->set & GRFSPRSET_ENABLE) {
if (info->enable) {
cv3d_cursor_on = 1;
cv3d_setup_hwc(gp);
/* WCrt(ba, CRT_ID_HWGC_MODE, 0x01); */
} else
WCrt(ba, CRT_ID_HWGC_MODE, 0x00);
}
if (info->set & GRFSPRSET_POS)
cv3d_setspritepos(gp, &info->pos);
if (info->set & GRFSPRSET_HOT) {
int
cv3d_getspritemax(struct grf_softc *gp, struct grf_position *pos)
{
pos->x = 64;
pos->y = 64;
return(0);
}
#endif /* CV3D_HARDWARE_CURSOR */
#if NWSDISPLAY > 0
static void
cv3d_wscursor(void *c, int on, int row, int col)
{
struct rasops_info *ri;
struct vcons_screen *scr;
struct grf_softc *gp;
volatile void *ba;
int offs;
ri = c;
scr = ri->ri_hw;
gp = scr->scr_cookie;
ba = gp->g_regkva;
if ((ri->ri_flg & RI_CURSOR) && !on) {
/* cursor was visible, but we want to remove it */
/*WCrt(ba, CRT_ID_CURSOR_START, | 0x20);*/
ri->ri_flg &= ~RI_CURSOR;
}
ri->ri_crow = row;
ri->ri_ccol = col;
if (on) {
/* move cursor to new location */
if (!(ri->ri_flg & RI_CURSOR)) {
/*WCrt(ba, CRT_ID_CURSOR_START, | 0x20);*/
ri->ri_flg |= RI_CURSOR;
}
offs = gp->g_rowoffset[row] + col;
WCrt(ba, CRT_ID_CURSOR_LOC_LOW, offs & 0xff);
WCrt(ba, CRT_ID_CURSOR_LOC_HIGH, offs >> 8);
}
}
static void
cv3d_wsputchar(void *cookie, int row, int col, u_int ch, long attr)
{
struct rasops_info *ri;
struct vcons_screen *scr;
struct grf_softc *gp;
volatile unsigned char *cp;
switch (cmd) {
case WSDISPLAYIO_GETCMAP:
/* Note: wsdisplay_cmap and grf_colormap have same format */
if (gp->g_display.gd_planes == 8)
return cv3d_getcmap(gp, (struct grf_colormap *)data);
return EINVAL;
case WSDISPLAYIO_PUTCMAP:
/* Note: wsdisplay_cmap and grf_colormap have same format */
if (gp->g_display.gd_planes == 8)
return cv3d_putcmap(gp, (struct grf_colormap *)data);
return EINVAL;
case WSDISPLAYIO_GVIDEO:
if (cv3d_isblank(gp))
*(u_int *)data = WSDISPLAYIO_VIDEO_OFF;
else
*(u_int *)data = WSDISPLAYIO_VIDEO_ON;
return 0;
case WSDISPLAYIO_SVIDEO:
return cv3d_blank(gp, *(u_int *)data == WSDISPLAYIO_VIDEO_ON);
case WSDISPLAYIO_SMODE:
if ((*(int *)data) != gp->g_wsmode) {
if (*(int *)data == WSDISPLAYIO_MODE_EMUL) {
/* load console text mode, redraw screen */
(void)cv3d_load_mon(gp, &cv3dconsole_mode);
if (vd->active != NULL)
vcons_redraw_screen(vd->active);
} else {
/* switch to current graphics mode */
if (!cv3d_load_mon(gp,
(struct grfcv3dtext_mode *)monitor_current))
return EINVAL;
}
gp->g_wsmode = *(int *)data;
}
return 0;
case WSDISPLAYIO_GET_FBINFO:
return cv3d_get_fbinfo(gp, data);
}
/* handle this command hw-independent in grf(4) */
return grf_wsioctl(v, vs, cmd, data, flag, l);
}
/*
* Fill the wsdisplayio_fbinfo structure with information from the current
* graphics mode. Even when text mode is active.
*/
static int
cv3d_get_fbinfo(struct grf_softc *gp, struct wsdisplayio_fbinfo *fbi)
{
struct grfvideo_mode *md;
uint32_t rbits, gbits, bbits, abits;