//
// ATI Radeon [789]???
// 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"
#include "/sys/src/cmd/aux/vga/radeon_pciids.h"
static int debug = 1;
enum {
Kilo = 1024,
Meg = Kilo * Kilo,
};
// Pci Match
static Pcidev*
radeonpci(void)
{
static Pcidev *p = nil;
struct pciids *ids;
if (p != nil)
return p;
p = pcimatch(nil, ATI_PCIVID, 0);
if (p == nil)
return nil;
for (ids = radeon_pciids; ids->did; ids++) {
if (ids->did == p->did)
return p;
}
p = nil;
return p;
}
// 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)
{
ulong tmp;
tmp = INREG(mmio, offset);
tmp &= mask;
tmp |= val;
OUTREG(mmio, offset, tmp);
}
static void
OUTPLL(ulong mmio, ulong offset, ulong val)
{
uchar tmp;
tmp = (offset & 0x3f) | RADEON_PLL_WR_EN;
OUTREG8(mmio, RADEON_CLOCK_CNTL_INDEX, tmp);
OUTREG(mmio, RADEON_CLOCK_CNTL_DATA, val);
}
static ulong
INPLL(ulong mmio, ulong offset)
{
ulong data;
OUTREG8(mmio, RADEON_CLOCK_CNTL_INDEX, offset & 0x3f);
data = INREG(mmio, RADEON_CLOCK_CNTL_DATA);
return data;
}
static void
OUTPLLP(ulong mmio, ulong offset, ulong val, ulong mask)
{
ulong tmp;
tmp = INPLL(mmio, offset);
tmp &= mask;
tmp |= val;
OUTPLL(mmio, offset, tmp);
}
// Init
static ulong
radeonlinear(VGAscr *scr, int *size, int *align)
{
ulong aperture, oaperture;
int oapsize, wasupamem;
Pcidev * p;
oaperture = scr->aperture;
oapsize = scr->apsize;
wasupamem = scr->isupamem;
aperture = 0;
p = radeonpci();
if (p) {
aperture = p->mem[0].bar & ~0x0F;
*size = p->mem[0].size;
}
if(wasupamem) {
if(oaperture == aperture)
return oaperture;
upafree(oaperture, oapsize);
}
scr->isupamem = 0;
aperture = upamalloc(aperture, *size, *align);
if(aperture == 0){
if(wasupamem && upamalloc(oaperture, oapsize, 0)) {
aperture = oaperture;
scr->isupamem = 1;
}
else
scr->isupamem = 0;
}
else
scr->isupamem = 1;
return aperture;
}
static void
radeonenable(VGAscr* scr)
{
Pcidev * p;
int size, align;
ulong aperture;
if (scr->io)
return;
p = radeonpci();
if (p == nil)
return;
scr->io = upamalloc(p->mem[2].bar & ~0x0f, p->mem[2].size, 0);
if(scr->io == 0)
return;
addvgaseg("radeonmmio", scr->io, p->mem[2].size);
scr->io = (ulong) KADDR(scr->io);
size = p->mem[0].size;
align = 0;
aperture = radeonlinear(scr, &size, &align);
if (aperture) {
scr->aperture = aperture;
scr->apsize = size;
addvgaseg("radeonscreen", aperture, size);
}
}
// Cursor
static void
radeoncurload(VGAscr* scr, Cursor* curs)
{
int x, y;
ulong *p;
if(scr->io == 0)
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 = 0x00000000;
break;
case 1:
col = 0xffffffff; // 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->io == 0)
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(scr->io, RADEON_CUR_OFFSET, storage + oy * 256);
OUTREG(scr->io, RADEON_CUR_HORZ_VERT_OFF, ((ox & 0x7fff) << 16) | (oy & 0x7fff));
OUTREG(scr->io, RADEON_CUR_HORZ_VERT_POSN, ((x & 0x7fff) << 16) | (y & 0x7fff));
return 0;
}
static void
radeoncurdisable(VGAscr* scr)
{
if(scr->io == 0)
return;
OUTREGP(scr->io, RADEON_CRTC_GEN_CNTL, 0, ~RADEON_CRTC_CUR_EN);
}
static void
radeoncurenable(VGAscr* scr)
{
ulong storage;
if(scr->io == 0)
return;
radeoncurdisable(scr);
storage = (scr->apsize - 1*Meg);
scr->storage = (ulong) KADDR(scr->aperture + storage);
radeoncurload(scr, &arrow);
radeoncurmove(scr, ZP);
OUTREGP(scr->io, RADEON_CRTC_GEN_CNTL,
RADEON_CRTC_CUR_EN | (2<<20), ~(RADEON_CRTC_CUR_EN | (3<<20)));
}
// Hw Blank
static void
radeonblank(VGAscr* scr, int blank)
{
ulong mask;
char* cp;
if (scr->io == 0)
return;
// iprint("radeon: hwblank(%d)\n", blank);
mask = RADEON_CRTC_DISPLAY_DIS |
RADEON_CRTC_HSYNC_DIS |
RADEON_CRTC_VSYNC_DIS;
if (blank == 0) {
OUTREGP(scr->io, RADEON_CRTC_EXT_CNTL, 0, ~mask);
return;
}
cp = getconf("*dpms");
if (cp) {
if (cistrcmp(cp, "standby") == 0) {
OUTREGP(scr->io, RADEON_CRTC_EXT_CNTL,
RADEON_CRTC_DISPLAY_DIS | RADEON_CRTC_HSYNC_DIS, ~mask);
} else if (cistrcmp(cp, "suspend") == 0) {
OUTREGP(scr->io, RADEON_CRTC_EXT_CNTL,
RADEON_CRTC_DISPLAY_DIS | RADEON_CRTC_VSYNC_DIS, ~mask);
} else if (cistrcmp(cp, "off") == 0) {
OUTREGP(scr->io, RADEON_CRTC_EXT_CNTL, mask, ~mask);
}
return;
}
OUTREGP(scr->io, RADEON_CRTC_EXT_CNTL, mask, ~mask);
}
// Hw Accel
static void
radeonwaitfifo(VGAscr* scr, int entries)
{
int i;
for (i = 0; i < 2000000; i++) {
int slots;
slots = INREG(scr->io, RADEON_RBBM_STATUS) &
RADEON_RBBM_FIFOCNT_MASK;
if (slots >= entries) {
return;
}
}
iprint("radeon: fifo timeout\n");
}
static void
radeonwaitidle(VGAscr* scr)
{
radeonwaitfifo(scr, 64);
for (;;) {
int i;
for (i = 0; i < 2000000; i++) {
ulong status;
status = INREG(scr->io, RADEON_RBBM_STATUS);
if (status & RADEON_RBBM_ACTIVE)
continue;
return;
}
iprint("radeon: idle timed out: %uld entries, stat=0x%.8ulx\n",
INREG(scr->io, RADEON_RBBM_STATUS) & RADEON_RBBM_FIFOCNT_MASK,
INREG(scr->io, RADEON_RBBM_STATUS));
}
}
static ulong dp_gui_master_cntl = 0;
static int
radeonfill(VGAscr* scr, Rectangle r, ulong color)
{
if(scr->io == 0)
return 0;
radeonwaitfifo(scr, 6);
OUTREG(scr->io, RADEON_DP_GUI_MASTER_CNTL,
dp_gui_master_cntl | RADEON_GMC_BRUSH_SOLID_COLOR
| RADEON_GMC_SRC_DATATYPE_COLOR | RADEON_ROP3_P);
OUTREG(scr->io, RADEON_DP_BRUSH_FRGD_CLR, color);
OUTREG(scr->io, RADEON_DP_WRITE_MASK, 0xffffffff);
OUTREG(scr->io, RADEON_DP_CNTL,
(RADEON_DST_X_LEFT_TO_RIGHT | RADEON_DST_Y_TOP_TO_BOTTOM));
OUTREG(scr->io, RADEON_DST_Y_X, (r.min.y << 16) | r.min.x);
OUTREG(scr->io, RADEON_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->io == 0)
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 |= RADEON_DST_Y_TOP_TO_BOTTOM;
if (xs < xd) {
xs += w - 1;
xd += w - 1;
} else
dp_cntl |= RADEON_DST_X_LEFT_TO_RIGHT;
radeonwaitfifo(scr, 6);
OUTREG(scr->io, RADEON_DP_GUI_MASTER_CNTL,
dp_gui_master_cntl
| RADEON_GMC_BRUSH_NONE
| RADEON_GMC_SRC_DATATYPE_COLOR
| RADEON_DP_SRC_SOURCE_MEMORY
| RADEON_ROP3_S);
OUTREG(scr->io, RADEON_DP_WRITE_MASK, 0xffffffff);
OUTREG(scr->io, RADEON_DP_CNTL, dp_cntl);
OUTREG(scr->io, RADEON_SRC_Y_X, (ys << 16) | xs);
OUTREG(scr->io, RADEON_DST_Y_X, (yd << 16) | xd);
OUTREG(scr->io, RADEON_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;
ulong clock_cntl_index, mclk_cntl, rbbm_soft_reset;
if (scr->io == 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(scr->io, RADEON_RB3D_CNTL, 0);
// flush engine
OUTREGP(scr->io, RADEON_RB2D_DSTCACHE_CTLSTAT,
RADEON_RB2D_DC_FLUSH_ALL,
~RADEON_RB2D_DC_FLUSH_ALL);
for (i = 0; i < 2000000; i++) {
if (!(INREG(scr->io, RADEON_RB2D_DSTCACHE_CTLSTAT) &
RADEON_RB2D_DC_BUSY))
break;
}
// reset 2D engine
clock_cntl_index = INREG(scr->io, RADEON_CLOCK_CNTL_INDEX);
mclk_cntl = INPLL(scr->io, RADEON_MCLK_CNTL);
OUTPLL(scr->io, RADEON_MCLK_CNTL,
(mclk_cntl |
RADEON_FORCEON_MCLKA |
RADEON_FORCEON_MCLKB |
RADEON_FORCEON_YCLKA |
RADEON_FORCEON_YCLKB |
RADEON_FORCEON_MC |
RADEON_FORCEON_AIC));
rbbm_soft_reset = INREG(scr->io, RADEON_RBBM_SOFT_RESET);
OUTREG(scr->io, RADEON_RBBM_SOFT_RESET,
(rbbm_soft_reset |
RADEON_SOFT_RESET_CP |
RADEON_SOFT_RESET_HI |
RADEON_SOFT_RESET_SE |
RADEON_SOFT_RESET_RE |
RADEON_SOFT_RESET_PP |
RADEON_SOFT_RESET_E2 |
RADEON_SOFT_RESET_RB));
INREG(scr->io, RADEON_RBBM_SOFT_RESET);
OUTREG(scr->io, RADEON_RBBM_SOFT_RESET,
(rbbm_soft_reset &
~(RADEON_SOFT_RESET_CP |
RADEON_SOFT_RESET_HI |
RADEON_SOFT_RESET_SE |
RADEON_SOFT_RESET_RE |
RADEON_SOFT_RESET_PP |
RADEON_SOFT_RESET_E2 |
RADEON_SOFT_RESET_RB)));
INREG(scr->io, RADEON_RBBM_SOFT_RESET);
OUTPLL(scr->io, RADEON_MCLK_CNTL, mclk_cntl);
OUTREG(scr->io, RADEON_CLOCK_CNTL_INDEX, clock_cntl_index);
// init 2D engine
radeonwaitfifo(scr, 1);
OUTREG(scr->io, RADEON_RB2D_DSTCACHE_MODE, 0);
pitch = (Dx(scr->gscreen->r) * bpp);
radeonwaitfifo(scr, 4);
OUTREG(scr->io, RADEON_DEFAULT_PITCH, pitch);
OUTREG(scr->io, RADEON_DST_PITCH, pitch);
OUTREG(scr->io, RADEON_SRC_PITCH, pitch);
OUTREG(scr->io, RADEON_DST_PITCH_OFFSET_C, 0);
radeonwaitfifo(scr, 3);
OUTREG(scr->io, RADEON_DEFAULT_OFFSET, 0);
OUTREG(scr->io, RADEON_DST_OFFSET, 0);
OUTREG(scr->io, RADEON_SRC_OFFSET, 0);
radeonwaitfifo(scr, 1);
OUTREGP(scr->io, RADEON_DP_DATATYPE, 0,
~RADEON_HOST_BIG_ENDIAN_EN);
radeonwaitfifo(scr, 1);
OUTREG(scr->io, RADEON_DEFAULT_SC_BOTTOM_RIGHT,
(RADEON_DEFAULT_SC_RIGHT_MAX
| RADEON_DEFAULT_SC_BOTTOM_MAX));
dp_gui_master_cntl =
(dtype << RADEON_GMC_DST_DATATYPE_SHIFT)
| RADEON_GMC_SRC_PITCH_OFFSET_CNTL
| RADEON_GMC_DST_PITCH_OFFSET_CNTL
| RADEON_GMC_CLR_CMP_CNTL_DIS;
radeonwaitfifo(scr, 1);
OUTREG(scr->io, RADEON_DP_GUI_MASTER_CNTL,
dp_gui_master_cntl
| RADEON_GMC_BRUSH_SOLID_COLOR
| RADEON_GMC_SRC_DATATYPE_COLOR);
radeonwaitfifo(scr, 7);
OUTREG(scr->io, RADEON_DST_LINE_START, 0);
OUTREG(scr->io, RADEON_DST_LINE_END, 0);
OUTREG(scr->io, RADEON_DP_BRUSH_FRGD_CLR, 0xffffffff);
OUTREG(scr->io, RADEON_DP_BRUSH_BKGD_CLR, 0x00000000);
OUTREG(scr->io, RADEON_DP_SRC_FRGD_CLR, 0xffffffff);
OUTREG(scr->io, RADEON_DP_SRC_BKGD_CLR, 0x00000000);
OUTREG(scr->io, RADEON_DP_WRITE_MASK, 0xffffffff);
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", /* name */
radeonenable, /* enable */
0, /* disable */
0, /* page */
radeonlinear, /* linear */
radeondrawinit, /* drawinit */
#ifdef RADEON_HW_ACCEL
radeonfill, /* fill */
radeonovlctl, /* ovlctl */
radeonovlwrite, /* ovlwrite */
radeonflush, /* flush */
#endif
};
VGAcur vgaradeoncur = {
"radeonhwgc", /* name */
radeoncurenable, /* enable */
radeoncurdisable, /* disable */
radeoncurload, /* load */
radeoncurmove, /* move */
0 /* doespanning */
};
|