/*
* 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, Michael Teske and by Bernd Ernesti.
* 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 board, using the S3 Trio64.
*
* Modified for CV64 from
* Kari Mettinen's Cirrus driver by Michael Teske 10/95
*
* Thanks to Tekelec Airtronic for providing me with a S3 Trio64 documentation.
* Thanks to Bernd 'the fabulous bug-finder' Ernesti for bringing my messy
* source to NetBSD style :)
* Thanks to Harald Koenig for providing information about undocumented
* Trio64 Bugs.
*/
unsigned char cv_pass_toggle; /* passthru status tracker */
/* Console display definition.
* Default hardcoded text mode. This grf_cv is set up to
* use one text mode only, and this is it. You may use
* grfconfig to change the mode after boot.
*/
/* Board Address of CV64 */
static volatile void *cv_boardaddr;
static int cv_fbsize;
/*
* Memory clock (binpatchable).
* Let's be defensive: 50 MHz runs on all boards I know of.
* 55 MHz runs on most boards. But you should know what you're doing
* if you set this flag. Again: This flag may destroy your CV Board.
* Use it at your own risk!!!
* Anyway, this doesn't imply that I'm responsible if your board breaks
* without setting this flag :-).
*/
#ifdef CV_AGGRESSIVE_TIMING
long cv_memclk = 55000000;
#else
long cv_memclk = 50000000;
#endif
static unsigned short cv_cursor_storage[HWC_SIZE/2];
static short curs_update_flag = 0;
#endif /* !CV_NO_HARDWARE_CURSOR */
/*
* Interrupt handler
* This is used for updating the cursor shape (because it _must not_
* be changed while cursor is displayed)
* and maybe later to avoid busy waiting
* for Vertical Blank and/or gfx engine busy
*/
int
cvintr(void *arg)
{
#ifndef CV_NO_HARDWARE_CURSOR
volatile unsigned long *csrc, *cdest;
int i;
#endif
struct grf_softc *gp = arg;
volatile void *ba = gp->g_regkva;
unsigned char test;
unsigned char cridx; /* Save the cr Register index */
if (gp == NULL)
return 0;
test = vgar(ba, GREG_INPUT_STATUS0_R);
if (test & 0x80) { /* VR int pending */
/* Save old CR index */
cridx = vgar (ba, CRT_ADDRESS);
#if !defined(__m68k__)
test = RCrt(ba, CRT_ID_END_VER_RETR);
/* Clear int (bit 4) */
test &= ~0x10;
WCrt(ba, CRT_ID_END_VER_RETR, test);
#else
vgaw(ba, CRT_ADDRESS, CRT_ID_END_VER_RETR);
__asm volatile("bclr #4,%0@(0x3d5);nop" : : "a" (ba));
#endif
#ifndef CV_NO_HARDWARE_CURSOR
/* update the hardware cursor, if necessary */
if (curs_update_flag) {
csrc = (unsigned long *)cv_cursor_storage;
cdest = (volatile unsigned long *)
((volatile char*)gp->g_fbkva + HWC_OFF);
for (i = 0; i < HWC_SIZE / sizeof(long); i++)
*cdest++ = *csrc++;
curs_update_flag = 0;
}
/* Reenable int */
#if !defined(__m68k__)
test |= 0x10;
WCrt(ba, CRT_ID_END_VER_RETR, test);
#else
/* I don't trust the optimizer here... */
__asm volatile("bset #4,%0@(0x3d5);nop" : : "a" (ba));
#endif
cv_setspritepos (gp, NULL);
/* Restore the old CR index */
vgaw(ba, CRT_ADDRESS, cridx);
amiga_cpu_sync();
#endif /* !CV_NO_HARDWARE_CURSOR */
return (1);
}
return (0);
}
/*
* 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
cv_has_4mb(volatile void *fb)
{
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 *)((volatile char*)fb + 0x02000000);
*testfbw = 0x87654321;
amiga_cpu_sync();
if (*testfbr != 0x87654321)
return (0);
/* upper memory region */
testfbw = (volatile unsigned long *)((volatile char*)fb + 0x00200000);
testfbr = (volatile unsigned long *)((volatile char*)fb + 0x02200000);
*testfbw = 0x87654321;
amiga_cpu_sync();
if (*testfbr != 0x87654321)
return (0);
*testfbw = 0xAAAAAAAA;
amiga_cpu_sync();
if (*testfbr != 0xAAAAAAAA)
return (0);
*testfbw = 0x55555555;
amiga_cpu_sync();
if (*testfbr != 0x55555555)
return (0);
return (1);
}
int
grfcvmatch(device_t parent, cfdata_t cf, void *aux)
{
#ifdef CV64CONSOLE
static int cvcons_unit = -1;
#endif
struct zbus_args *zap;
zap = aux;
if (amiga_realconfig == 0)
#ifdef CV64CONSOLE
if (cvcons_unit != -1)
#endif
return (0);
/* Lets be Paranoid: Test man and prod id */
if (zap->manid != 8512 || zap->prodid != 34)
return (0);
/*
* attach grf
*/
if (amiga_config_found(cfdata, gp->g_device, gp, grfcvprint,
CFARGS_NONE)) {
if (self != NULL)
printf("grfcv: CyberVision64 with %dMB being used\n",
cv_fbsize/0x100000);
attachflag = 1;
} else {
if (!attachflag)
/*printf("grfcv unattached!!\n")*/;
}
}
int
grfcvprint(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
cv_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("grfcv: Illegal clock frequency: %ldMHz\n", freq/1000000);
printf("grfcv: Using default frequency: 25MHz\n");
printf("grfcv: See the manpage of grfconfig for more informations.\n");
freq = 25000000;
}
mnr = clocks; /* there the vals are stored */
d2 = 0x7fffffff;
/*
* The default board interrupt is #6.
* Set the roxxler register to use interrupt #2, not #6.
*/
#if CV_INT_NUM == 2
cv_write_port(0x8080, (volatile char*)ba - 0x02000000);
#endif
test = RCrt(ba, CRT_ID_SYSTEM_CONFIG);
test = test | 0x01; /* enable enhanced register access */
test = test & 0xEF; /* clear bit 4, 0 wait state */
WCrt(ba, CRT_ID_SYSTEM_CONFIG, test);
/*
* bit 1=1: enable enhanced mode functions
* bit 4=1: enable linear addressing
* bit 5=1: enable MMIO
*/
vgaw(ba, ECR_ADV_FUNC_CNTL, 0x31);
/* enable color mode (bit0), CPU access (bit1), high 64k page (bit5) */
vgaw(ba, GREG_MISC_OUTPUT_W, 0xe3);
/* CPU base addr */
WCrt(ba, CRT_ID_EXT_SYS_CNTL_4, 0x00);
/* Reset. This does nothing, but everyone does it:) */
WSeq(ba, SEQ_ID_RESET, 0x03);
int
cv_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
cv_mode(register struct grf_softc *gp, u_long cmd, void *arg, u_long a2,
int a3)
{
int error;
case GRFIOCSSPRITEPOS:
return(cv_setspritepos (gp, (struct grf_position *) data));
case GRFIOCSSPRITEINF:
return(cv_setspriteinfo (gp, (struct grf_spriteinfo *) data));
case GRFIOCGSPRITEINF:
return(cv_getspriteinfo (gp, (struct grf_spriteinfo *) data));
case GRFIOCGSPRITEMAX:
return(cv_getspritemax (gp, (struct grf_position *) data));
#else /* !CV_NO_HARDWARE_CURSOR */
case GRFIOCGSPRITEPOS:
case GRFIOCSSPRITEPOS:
case GRFIOCSSPRITEINF:
case GRFIOCGSPRITEINF:
case GRFIOCGSPRITEMAX:
break;
#endif /* !CV_NO_HARDWARE_CURSOR */
case GRFIOCGETCMAP:
return (cv_getcmap (gp, (struct grf_colormap *) data));
case GRFIOCPUTCMAP:
return (cv_putcmap (gp, (struct grf_colormap *) data));
case GRFIOCBITBLT:
break;
case GRFTOGGLE:
return (cv_toggle (gp));
case GRFIOCSETMON:
return (cv_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("grfcv: Changing the used mode not allowed!\n");
return (EINVAL);
}
memcpy(md, gv, sizeof(struct grfvideo_mode));
/* adjust pixel oriented values to internal rep. */
int
cv_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 CV_AGGRESSIVE_TIMING
maxpix = MAXPIXELCLOCK - 35000000;
#else
maxpix = MAXPIXELCLOCK - 55000000;
#endif
break;
case 24:
case 32:
#ifdef CV_AGGRESSIVE_TIMING
maxpix = MAXPIXELCLOCK - 75000000;
#else
maxpix = MAXPIXELCLOCK - 85000000;
#endif
break;
default:
printf("grfcv: Illegal depth in mode %d\n",
(int) gv->mode_num);
return (0);
}
if (gv->pixel_clock > maxpix) {
printf("grfcv: 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 ("grfcv: Too many columns for console\n");
return (0);
} else if ((gv->disp_height / S3FONTY) > MAXROWS) {
printf ("grfcv: Too many rows for console\n");
return (0);
}
}
if (gv->disp_flags & GRF_FLAGS_SYNC_ON_GREEN) {
printf("grfcv: sync-on-green is not supported\n");
return (0);
}
return (1);
}
int
cv_load_mon(struct grf_softc *gp, struct grfcvtext_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, sr15, sr18, clock_mode, test;
int m, n; /* For calc'ing display FIFO */
int tfillm, temptym; /* FIFO fill and empty mclk's */
int hmul; /* Multiplier for hor. Values */
unsigned char hvsync_pulse;
char TEXT, CONSOLE;
/* identity */
gv = &md->gv;
TEXT = (gv->depth == 4);
CONSOLE = (gv->mode_num == 255);
if (!cv_mondefok(gv)) {
printf("grfcv: Monitor definition not ok\n");
return (0);
}
ba = gp->g_regkva;
/* Disable Interrupts */
test = RCrt(ba, CRT_ID_BACKWAD_COMP_1);
test &= ~0x10;
WCrt(ba, CRT_ID_BACKWAD_COMP_1, test);
/* turn gfx off, don't mess up the display */
gfx_on_off(1, ba);
test = RCrt(ba, CRT_ID_EXT_SYS_CNTL_2);
test &= ~0x30;
/* HDE Overflow in bits 4-5 */
test |= (HDE >> 4) & 0x30;
WCrt(ba, CRT_ID_EXT_SYS_CNTL_2, test);
/* Set up graphics engine */
switch (gv->disp_width) {
case 1024:
cr50 |= 0x00;
break;
case 640:
cr50 |= 0x40;
break;
case 800:
cr50 |= 0x80;
break;
case 1280:
cr50 |= 0xc0;
break;
case 1152:
cr50 |= 0x01;
break;
case 1600:
cr50 |= 0x81;
break;
default: /* XXX The Xserver has to handle this */
break;
}
/*
* M-Parameter of Display FIFO
* This is dependent on the pixel clock and the memory clock.
* The FIFO filling bandwidth is 240 MHz and the FIFO is 96 Byte wide.
* Then the time to fill the FIFO is tfill = (96/240000000) sec, the time
* to empty the FIFO is tempty = (96/pixelclock) sec.
* Then the M parameter maximum is ((tempty-tfill)*cv_memclk-9)/2.
* This seems to be logical, ain't it?
* Remember: We have to use integer arithmetics :(
* Divide by 1000 to prevent overflows.
*/
tfillm = (96 * (cv_memclk/1000))/240000;
switch(gv->depth) {
case 32:
case 24:
temptym = (24 * (cv_memclk/1000)) / (gv->pixel_clock/1000);
break;
case 15:
case 16:
temptym = (48 * (cv_memclk/1000)) / (gv->pixel_clock/1000);
break;
case 4:
temptym = (192 * (cv_memclk/1000)) / (gv->pixel_clock/1000);
break;
default:
temptym = (96 * (cv_memclk/1000)) / (gv->pixel_clock/1000);
break;
}
m = (temptym - tfillm - 9) / 2;
if (m < 0)
m = 0; /* prevent underflow */
m = (m & 0x1f) << 3;
if (m < 0x18)
m = 0x18;
n = 0xff;
/* 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 - 6) * 4;
*c++ = 'C';
*c++ = 0x0a;
c +=2;
*c++ = 'V';
*c++ = 0x0b;
c +=2;
*c++ = '6';
*c++ = 0x0c;
c +=2;
*c++ = '4';
*c++ = 0x0d;
}
static inline void
cv_write_port(unsigned short bits, volatile void *BoardAddr)
{
volatile char *addr;
static unsigned char CVPortBits = 0; /* mirror port bits here */
if (gp->g_display.gd_planes <= 4)
cv_cursor_on = 0; /* don't enable hwc in text modes */
if (cv_cursor_on == 0)
return;
/* reset colour stack */
#if !defined(__m68k__)
test = RCrt(ba, CRT_ID_HWGC_MODE);
amiga_cpu_sync();
#else
/* do it in assembler, the above does't seem to work */
__asm volatile ("moveb #0x45, %1@(0x3d4); \
moveb %1@(0x3d5),%0" : "=d" (test) : "a" (ba));
#endif
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
cv_setspriteinfo(struct grf_softc *gp, struct grf_spriteinfo *info)
{
volatile void *ba;
int depth = gp->g_display.gd_planes;
ba = gp->g_regkva;
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;
#ifdef CV_NO_INT
/* 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!
*/
/* This is necessary in order not to crash the board */
VerticalRetraceWait(ba);
#else /* CV_NO_INT */
hwp = (u_short *) cv_cursor_storage;
#endif /* CV_NO_INT */
/*
* setting it is slightly more difficult, because we can't
* force the application to not pass a *smaller* than
* supported bitmap
*/
int
cv_getspritemax (struct grf_softc *gp, struct grf_position *pos)
{
pos->x = 64;
pos->y = 64;
return(0);
}
#endif /* !CV_NO_HARDWARE_CURSOR */
#if NWSDISPLAY > 0
static void
cv_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 cv_wsputchar(void *c, 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 cv_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 cv_putcmap(gp, (struct grf_colormap *)data);
return EINVAL;
case WSDISPLAYIO_GVIDEO:
if (cv_isblank(gp))
*(u_int *)data = WSDISPLAYIO_VIDEO_OFF;
else
*(u_int *)data = WSDISPLAYIO_VIDEO_ON;
return 0;
case WSDISPLAYIO_SVIDEO:
return cv_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)cv_load_mon(gp, &cvconsole_mode);
if (vd->active != NULL)
vcons_redraw_screen(vd->active);
} else {
/* switch to current graphics mode */
if (!cv_load_mon(gp,
(struct grfcvtext_mode *)monitor_current))
return EINVAL;
}
gp->g_wsmode = *(int *)data;
}
return 0;
case WSDISPLAYIO_GET_FBINFO:
return cv_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
cv_get_fbinfo(struct grf_softc *gp, struct wsdisplayio_fbinfo *fbi)
{
struct grfvideo_mode *md;
uint32_t rbits, gbits, bbits, abits;