520 lines
10 KiB
C
520 lines
10 KiB
C
/*
|
|
* ATI Radeon [789]XXX vga driver
|
|
* see /sys/src/cmd/aux/vga/radeon.c
|
|
*/
|
|
#include "u.h"
|
|
#include "../port/lib.h"
|
|
#include "mem.h"
|
|
#include "dat.h"
|
|
#include "fns.h"
|
|
#include "io.h"
|
|
#include "../port/error.h"
|
|
|
|
#define Image IMAGE
|
|
#include <draw.h>
|
|
#include <memdraw.h>
|
|
#include <cursor.h>
|
|
#include "screen.h"
|
|
|
|
#include "/sys/src/cmd/aux/vga/radeon.h" /* ugh */
|
|
|
|
/* #define HW_ACCEL */
|
|
|
|
enum {
|
|
Kilo = 1024,
|
|
Meg = Kilo * Kilo,
|
|
};
|
|
|
|
static Pcidev*
|
|
radeonpci(void)
|
|
{
|
|
static Pcidev *p = nil;
|
|
struct pciids *ids;
|
|
|
|
while ((p = pcimatch(p, ATI_PCIVID, 0)) != nil)
|
|
for (ids = radeon_pciids; ids->did; ids++)
|
|
if (ids->did == p->did)
|
|
return p;
|
|
return nil;
|
|
}
|
|
|
|
/* mmio access */
|
|
|
|
static void
|
|
OUTREG8(ulong mmio, ulong offset, uchar val)
|
|
{
|
|
((uchar*)KADDR((mmio + offset)))[0] = val;
|
|
}
|
|
|
|
static void
|
|
OUTREG(ulong mmio, ulong offset, ulong val)
|
|
{
|
|
((ulong*)KADDR((mmio + offset)))[0] = val;
|
|
}
|
|
|
|
static ulong
|
|
INREG(ulong mmio, ulong offset)
|
|
{
|
|
return ((ulong*)KADDR((mmio + offset)))[0];
|
|
}
|
|
|
|
static void
|
|
OUTREGP(ulong mmio, ulong offset, ulong val, ulong mask)
|
|
{
|
|
OUTREG(mmio, offset, (INREG(mmio, offset) & mask) | val);
|
|
}
|
|
|
|
static void
|
|
OUTPLL(ulong mmio, ulong offset, ulong val)
|
|
{
|
|
OUTREG8(mmio, CLOCK_CNTL_INDEX, (offset & 0x3f) | PLL_WR_EN);
|
|
OUTREG(mmio, CLOCK_CNTL_DATA, val);
|
|
}
|
|
|
|
static ulong
|
|
INPLL(ulong mmio, ulong offset)
|
|
{
|
|
OUTREG8(mmio, CLOCK_CNTL_INDEX, offset & 0x3f);
|
|
return INREG(mmio, CLOCK_CNTL_DATA);
|
|
}
|
|
|
|
static void
|
|
OUTPLLP(ulong mmio, ulong offset, ulong val, ulong mask)
|
|
{
|
|
OUTPLL(mmio, offset, (INPLL(mmio, offset) & mask) | val);
|
|
}
|
|
|
|
static void
|
|
radeonlinear(VGAscr *, int, int)
|
|
{
|
|
}
|
|
|
|
static void
|
|
radeonenable(VGAscr *scr)
|
|
{
|
|
Pcidev *p;
|
|
|
|
if (scr->mmio)
|
|
return;
|
|
p = radeonpci();
|
|
if (p == nil)
|
|
return;
|
|
scr->id = p->did;
|
|
scr->pci = p;
|
|
|
|
scr->mmio = vmap(p->mem[2].bar & ~0x0f, p->mem[2].size);
|
|
if(scr->mmio == 0)
|
|
return;
|
|
addvgaseg("radeonmmio", p->mem[2].bar & ~0x0f, p->mem[2].size);
|
|
|
|
vgalinearpci(scr);
|
|
if(scr->apsize)
|
|
addvgaseg("radeonscreen", scr->paddr, scr->apsize);
|
|
}
|
|
|
|
static void
|
|
radeoncurload(VGAscr *scr, Cursor *curs)
|
|
{
|
|
int x, y;
|
|
ulong *p;
|
|
|
|
if(scr->mmio == nil)
|
|
return;
|
|
|
|
p = (ulong*)KADDR(scr->storage);
|
|
|
|
for(y = 0; y < 64; y++){
|
|
int cv, sv;
|
|
|
|
if (y < 16) {
|
|
cv = curs->clr[2*y] << 8 | curs->clr[2*y+1];
|
|
sv = curs->set[2*y] << 8 | curs->set[2*y+1];
|
|
} else
|
|
cv = sv = 0;
|
|
|
|
for(x = 0; x < 64; x++){
|
|
ulong col = 0;
|
|
int c, s;
|
|
|
|
if (x < 16) {
|
|
c = (cv >> (15 - x)) & 1;
|
|
s = (sv >> (15 - x)) & 1;
|
|
} else
|
|
c = s = 0;
|
|
|
|
switch(c | s<<1) {
|
|
case 0:
|
|
col = 0;
|
|
break;
|
|
case 1:
|
|
col = ~0ul; /* white */
|
|
break;
|
|
case 2:
|
|
case 3:
|
|
col = 0xff000000; /* black */
|
|
break;
|
|
}
|
|
|
|
*p++ = col;
|
|
}
|
|
}
|
|
|
|
scr->offset.x = curs->offset.x;
|
|
scr->offset.y = curs->offset.y;
|
|
}
|
|
|
|
static int
|
|
radeoncurmove(VGAscr *scr, Point p)
|
|
{
|
|
int x, y, ox, oy;
|
|
static ulong storage = 0;
|
|
|
|
if(scr->mmio == nil)
|
|
return 1;
|
|
|
|
if (storage == 0)
|
|
storage = scr->apsize - 1*Meg;
|
|
|
|
x = p.x + scr->offset.x;
|
|
y = p.y + scr->offset.y;
|
|
ox = oy = 0;
|
|
|
|
if (x < 0) {
|
|
ox = -x - 1;
|
|
x = 0;
|
|
}
|
|
if (y < 0) {
|
|
oy = -y - 1;
|
|
y = 0;
|
|
}
|
|
|
|
OUTREG((ulong)scr->mmio, CUR_OFFSET, storage + oy * 256);
|
|
OUTREG((ulong)scr->mmio, CUR_HORZ_VERT_OFF,
|
|
(ox & 0x7fff) << 16 | (oy & 0x7fff));
|
|
OUTREG((ulong)scr->mmio, CUR_HORZ_VERT_POSN,
|
|
(x & 0x7fff) << 16 | (y & 0x7fff));
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
radeoncurdisable(VGAscr *scr)
|
|
{
|
|
if(scr->mmio == nil)
|
|
return;
|
|
OUTREGP((ulong)scr->mmio, CRTC_GEN_CNTL, 0, ~CRTC_CUR_EN);
|
|
}
|
|
|
|
static void
|
|
radeoncurenable(VGAscr *scr)
|
|
{
|
|
ulong storage;
|
|
|
|
if(scr->mmio == 0)
|
|
return;
|
|
|
|
radeoncurdisable(scr);
|
|
storage = scr->apsize - 1*Meg;
|
|
scr->storage = (ulong)KADDR(scr->paddr + storage);
|
|
radeoncurload(scr, &arrow);
|
|
radeoncurmove(scr, ZP);
|
|
|
|
OUTREGP((ulong)scr->mmio, CRTC_GEN_CNTL, CRTC_CUR_EN | 2<<20,
|
|
~(CRTC_CUR_EN | 3<<20));
|
|
}
|
|
|
|
/* hw blank */
|
|
|
|
static void
|
|
radeonblank(VGAscr* scr, int blank)
|
|
{
|
|
ulong mask;
|
|
char *cp;
|
|
|
|
if (scr->mmio == 0)
|
|
return;
|
|
|
|
// iprint("radeon: hwblank(%d)\n", blank);
|
|
|
|
mask = CRTC_DISPLAY_DIS | CRTC_HSYNC_DIS | CRTC_VSYNC_DIS;
|
|
if (blank == 0) {
|
|
OUTREGP((ulong)scr->mmio, CRTC_EXT_CNTL, 0, ~mask);
|
|
return;
|
|
}
|
|
|
|
cp = getconf("*dpms");
|
|
if (cp) {
|
|
if (strcmp(cp, "standby") == 0)
|
|
OUTREGP((ulong)scr->mmio, CRTC_EXT_CNTL,
|
|
CRTC_DISPLAY_DIS | CRTC_HSYNC_DIS, ~mask);
|
|
else if (strcmp(cp, "suspend") == 0)
|
|
OUTREGP((ulong)scr->mmio, CRTC_EXT_CNTL,
|
|
CRTC_DISPLAY_DIS | CRTC_VSYNC_DIS, ~mask);
|
|
else if (strcmp(cp, "off") == 0)
|
|
OUTREGP((ulong)scr->mmio, CRTC_EXT_CNTL, mask, ~mask);
|
|
return;
|
|
}
|
|
|
|
OUTREGP((ulong)scr->mmio, CRTC_EXT_CNTL, mask, ~mask);
|
|
}
|
|
|
|
/* hw acceleration */
|
|
|
|
static void
|
|
radeonwaitfifo(VGAscr *scr, int entries)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < 2000000; i++)
|
|
if (INREG((ulong)scr->mmio, RBBM_STATUS) & RBBM_FIFOCNT_MASK >=
|
|
entries)
|
|
return;
|
|
iprint("radeon: fifo timeout\n");
|
|
}
|
|
|
|
static void
|
|
radeonwaitidle(VGAscr *scr)
|
|
{
|
|
radeonwaitfifo(scr, 64);
|
|
|
|
for (; ; ) {
|
|
int i;
|
|
|
|
for (i = 0; i < 2000000; i++)
|
|
if (!(INREG((ulong)scr->mmio, RBBM_STATUS) & RBBM_ACTIVE))
|
|
return;
|
|
|
|
iprint("radeon: idle timed out: %uld entries, stat=0x%.8ulx\n",
|
|
INREG((ulong)scr->mmio, RBBM_STATUS) & RBBM_FIFOCNT_MASK,
|
|
INREG((ulong)scr->mmio, RBBM_STATUS));
|
|
}
|
|
}
|
|
|
|
static ulong dp_gui_master_cntl = 0;
|
|
|
|
static int
|
|
radeonfill(VGAscr *scr, Rectangle r, ulong color)
|
|
{
|
|
if (scr->mmio == nil)
|
|
return 0;
|
|
|
|
radeonwaitfifo(scr, 6);
|
|
OUTREG((ulong)scr->mmio, DP_GUI_MASTER_CNTL,
|
|
dp_gui_master_cntl | GMC_BRUSH_SOLID_COLOR |
|
|
GMC_SRC_DATATYPE_COLOR | ROP3_P);
|
|
OUTREG((ulong)scr->mmio, DP_BRUSH_FRGD_CLR, color);
|
|
OUTREG((ulong)scr->mmio, DP_WRITE_MASK, ~0ul);
|
|
OUTREG((ulong)scr->mmio, DP_CNTL,
|
|
DST_X_LEFT_TO_RIGHT | DST_Y_TOP_TO_BOTTOM);
|
|
OUTREG((ulong)scr->mmio, DST_Y_X, r.min.y << 16 | r.min.x);
|
|
OUTREG((ulong)scr->mmio, DST_WIDTH_HEIGHT, Dx(r) << 16 | Dy(r));
|
|
|
|
radeonwaitidle(scr);
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
radeonscroll(VGAscr*scr, Rectangle dst, Rectangle src)
|
|
{
|
|
int xs, ys, xd, yd, w, h;
|
|
ulong dp_cntl = 0x20;
|
|
|
|
if (scr->mmio == nil)
|
|
return 0;
|
|
|
|
// iprint("radeon: hwscroll(dst:%R, src:%R)\n", dst, src);
|
|
|
|
xd = dst.min.x;
|
|
yd = dst.min.y;
|
|
xs = src.min.x;
|
|
ys = src.min.y;
|
|
w = Dx(dst);
|
|
h = Dy(dst);
|
|
|
|
if (ys < yd) {
|
|
ys += h - 1;
|
|
yd += h - 1;
|
|
} else
|
|
dp_cntl |= DST_Y_TOP_TO_BOTTOM;
|
|
|
|
if (xs < xd) {
|
|
xs += w - 1;
|
|
xd += w - 1;
|
|
} else
|
|
dp_cntl |= DST_X_LEFT_TO_RIGHT;
|
|
|
|
radeonwaitfifo(scr, 6);
|
|
OUTREG((ulong)scr->mmio, DP_GUI_MASTER_CNTL, dp_gui_master_cntl |
|
|
GMC_BRUSH_NONE | GMC_SRC_DATATYPE_COLOR | DP_SRC_SOURCE_MEMORY |
|
|
ROP3_S);
|
|
OUTREG((ulong)scr->mmio, DP_WRITE_MASK, ~0ul);
|
|
OUTREG((ulong)scr->mmio, DP_CNTL, dp_cntl);
|
|
OUTREG((ulong)scr->mmio, SRC_Y_X, ys << 16 | xs);
|
|
OUTREG((ulong)scr->mmio, DST_Y_X, yd << 16 | xd);
|
|
OUTREG((ulong)scr->mmio, DST_WIDTH_HEIGHT, w << 16 | h);
|
|
|
|
radeonwaitidle(scr);
|
|
|
|
// iprint("radeon: hwscroll(xs=%d ys=%d xd=%d yd=%d w=%d h=%d)\n",
|
|
// xs, ys, xd, yd, w, h);
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
radeondrawinit(VGAscr*scr)
|
|
{
|
|
ulong bpp, dtype, i, pitch, clock_cntl_index, mclk_cntl, rbbm_soft_reset;
|
|
|
|
if (scr->mmio == 0)
|
|
return;
|
|
|
|
switch (scr->gscreen->depth) {
|
|
case 6:
|
|
case 8:
|
|
dtype = 2;
|
|
bpp = 1;
|
|
break;
|
|
case 15:
|
|
dtype = 3;
|
|
bpp = 2;
|
|
break;
|
|
case 16:
|
|
dtype = 4;
|
|
bpp = 2;
|
|
break;
|
|
case 32:
|
|
dtype = 6;
|
|
bpp = 4;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
/* disable 3D */
|
|
OUTREG((ulong)scr->mmio, RB3D_CNTL, 0);
|
|
|
|
/* flush engine */
|
|
OUTREGP((ulong)scr->mmio, RB2D_DSTCACHE_CTLSTAT,
|
|
RB2D_DC_FLUSH_ALL, ~RB2D_DC_FLUSH_ALL);
|
|
for (i = 0; i < 2000000; i++)
|
|
if (!(INREG((ulong)scr->mmio, RB2D_DSTCACHE_CTLSTAT) &
|
|
RB2D_DC_BUSY))
|
|
break;
|
|
|
|
/* reset 2D engine */
|
|
clock_cntl_index = INREG((ulong)scr->mmio, CLOCK_CNTL_INDEX);
|
|
|
|
mclk_cntl = INPLL((ulong)scr->mmio, MCLK_CNTL);
|
|
OUTPLL((ulong)scr->mmio, MCLK_CNTL, mclk_cntl | FORCEON_MCLKA |
|
|
FORCEON_MCLKB | FORCEON_YCLKA | FORCEON_YCLKB | FORCEON_MC |
|
|
FORCEON_AIC);
|
|
rbbm_soft_reset = INREG((ulong)scr->mmio, RBBM_SOFT_RESET);
|
|
|
|
OUTREG((ulong)scr->mmio, RBBM_SOFT_RESET, rbbm_soft_reset |
|
|
SOFT_RESET_CP | SOFT_RESET_HI | SOFT_RESET_SE | SOFT_RESET_RE |
|
|
SOFT_RESET_PP | SOFT_RESET_E2 | SOFT_RESET_RB);
|
|
INREG((ulong)scr->mmio, RBBM_SOFT_RESET);
|
|
OUTREG((ulong)scr->mmio, RBBM_SOFT_RESET, rbbm_soft_reset &
|
|
~(SOFT_RESET_CP | SOFT_RESET_HI | SOFT_RESET_SE | SOFT_RESET_RE |
|
|
SOFT_RESET_PP | SOFT_RESET_E2 | SOFT_RESET_RB));
|
|
INREG((ulong)scr->mmio, RBBM_SOFT_RESET);
|
|
|
|
OUTPLL((ulong)scr->mmio, MCLK_CNTL, mclk_cntl);
|
|
OUTREG((ulong)scr->mmio, CLOCK_CNTL_INDEX, clock_cntl_index);
|
|
|
|
/* init 2D engine */
|
|
radeonwaitfifo(scr, 1);
|
|
OUTREG((ulong)scr->mmio, RB2D_DSTCACHE_MODE, 0);
|
|
|
|
pitch = Dx(scr->gscreen->r) * bpp;
|
|
radeonwaitfifo(scr, 4);
|
|
OUTREG((ulong)scr->mmio, DEFAULT_PITCH, pitch);
|
|
OUTREG((ulong)scr->mmio, DST_PITCH, pitch);
|
|
OUTREG((ulong)scr->mmio, SRC_PITCH, pitch);
|
|
OUTREG((ulong)scr->mmio, DST_PITCH_OFFSET_C, 0);
|
|
|
|
radeonwaitfifo(scr, 3);
|
|
OUTREG((ulong)scr->mmio, DEFAULT_OFFSET, 0);
|
|
OUTREG((ulong)scr->mmio, DST_OFFSET, 0);
|
|
OUTREG((ulong)scr->mmio, SRC_OFFSET, 0);
|
|
|
|
radeonwaitfifo(scr, 1);
|
|
OUTREGP((ulong)scr->mmio, DP_DATATYPE, 0, ~HOST_BIG_ENDIAN_EN);
|
|
|
|
radeonwaitfifo(scr, 1);
|
|
OUTREG((ulong)scr->mmio, DEFAULT_SC_BOTTOM_RIGHT,
|
|
DEFAULT_SC_RIGHT_MAX | DEFAULT_SC_BOTTOM_MAX);
|
|
|
|
dp_gui_master_cntl = dtype << GMC_DST_DATATYPE_SHIFT |
|
|
GMC_SRC_PITCH_OFFSET_CNTL | GMC_DST_PITCH_OFFSET_CNTL |
|
|
GMC_CLR_CMP_CNTL_DIS;
|
|
radeonwaitfifo(scr, 1);
|
|
OUTREG((ulong)scr->mmio, DP_GUI_MASTER_CNTL,
|
|
dp_gui_master_cntl | GMC_BRUSH_SOLID_COLOR | GMC_SRC_DATATYPE_COLOR);
|
|
|
|
radeonwaitfifo(scr, 7);
|
|
OUTREG((ulong)scr->mmio, DST_LINE_START, 0);
|
|
OUTREG((ulong)scr->mmio, DST_LINE_END, 0);
|
|
OUTREG((ulong)scr->mmio, DP_BRUSH_FRGD_CLR, ~0ul);
|
|
OUTREG((ulong)scr->mmio, DP_BRUSH_BKGD_CLR, 0);
|
|
OUTREG((ulong)scr->mmio, DP_SRC_FRGD_CLR, ~0ul);
|
|
OUTREG((ulong)scr->mmio, DP_SRC_BKGD_CLR, 0);
|
|
OUTREG((ulong)scr->mmio, DP_WRITE_MASK, ~0ul);
|
|
|
|
radeonwaitidle(scr);
|
|
|
|
scr->fill = radeonfill;
|
|
scr->scroll = radeonscroll;
|
|
hwaccel = 1;
|
|
|
|
scr->blank = radeonblank;
|
|
hwblank = 1;
|
|
}
|
|
|
|
/* hw overlay */
|
|
|
|
static void
|
|
radeonovlctl(VGAscr *scr, Chan *c, void *data, int len)
|
|
{
|
|
USED(scr, c, data, len);
|
|
}
|
|
|
|
static int
|
|
radeonovlwrite(VGAscr *scr, void *data, int len, vlong opt)
|
|
{
|
|
USED(scr, data, len, opt);
|
|
return -1;
|
|
}
|
|
|
|
static void
|
|
radeonflush(VGAscr *scr, Rectangle r)
|
|
{
|
|
USED(scr, r);
|
|
}
|
|
|
|
/* Export */
|
|
|
|
VGAdev vgaradeondev = {
|
|
"radeon",
|
|
|
|
radeonenable,
|
|
0, /* disable */
|
|
0, /* page */
|
|
radeonlinear,
|
|
|
|
radeondrawinit,
|
|
#ifdef HW_ACCEL
|
|
radeonfill,
|
|
|
|
radeonovlctl,
|
|
radeonovlwrite,
|
|
radeonflush,
|
|
#endif
|
|
};
|
|
VGAcur vgaradeoncur = {
|
|
"radeonhwgc",
|
|
radeoncurenable,
|
|
radeoncurdisable,
|
|
radeoncurload,
|
|
radeoncurmove,
|
|
0 /* doespanning */
|
|
};
|