* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT license.
*
* Authors:
* Gerald Zajac
*/
Haiku Intel-810 video driver was adapted from the X.org intel driver which
has the following copyright.
Copyright 1998-1999 Precision Insight, Inc., Cedar Park, Texas.
All Rights Reserved.
*/
#include "accelerant.h"
#include "i810_regs.h"
#include <create_display_modes.h>
#include <math.h>
#include <unistd.h>
#define MAX_VCO_FREQ 600.0
#define TARGET_MAX_N 30
#define REF_FREQ 24.0
#define CALC_VCLK(m,n,p) (double)m / ((double)n * (1 << p)) * 4 * REF_FREQ
static void
CalcVCLK(double freq, uint16& clkM, uint16& clkN, uint16& clkP) {
int m, n, p;
double f_out, f_best;
double f_err;
double f_vco;
int m_best = 0, n_best = 0, p_best = 0;
double f_target = freq;
double errMax = 0.005;
double errTarget = 0.001;
double errBest = 999999.0;
p_best = p = int(log(MAX_VCO_FREQ / f_target) / log((double)2));
if (p_best > 5) {
p_best = p = 5;
}
f_vco = f_target * (1 << p);
n = 2;
do {
n++;
m = int(f_vco / (REF_FREQ / (double)n) / (double)4.0 + 0.5);
if (m < 3)
m = 3;
f_out = CALC_VCLK(m, n, p);
f_err = 1.0 - (f_target / f_out);
if (fabs(f_err) < errMax) {
m_best = m;
n_best = n;
f_best = f_out;
errBest = f_err;
}
} while ((fabs(f_err) >= errTarget) && ((n <= TARGET_MAX_N)
|| (fabs(errBest) > errMax)));
if (fabs(f_err) < errTarget) {
m_best = m;
n_best = n;
}
clkM = (m_best - 2) & 0x3FF;
clkN = (n_best - 2) & 0x3FF;
clkP = (p_best << 4);
TRACE("Setting dot clock to %.1f MHz [ 0x%x 0x%x 0x%x ] [ %d %d %d ]\n",
CALC_VCLK(m_best, n_best, p_best),
clkM, clkN, clkP, m_best, n_best, p_best);
}
static void
SetCrtcTimingValues(const DisplayModeEx& mode)
{
int hTotal = mode.timing.h_total / 8 - 5;
int hDisp_e = mode.timing.h_display / 8 - 1;
int hSync_s = mode.timing.h_sync_start / 8;
int hSync_e = mode.timing.h_sync_end / 8;
int hBlank_s = hDisp_e + 1;
int hBlank_e = hTotal;
int vTotal = mode.timing.v_total - 2;
int vDisp_e = mode.timing.v_display - 1;
int vSync_s = mode.timing.v_sync_start;
int vSync_e = mode.timing.v_sync_end;
int vBlank_s = vDisp_e;
int vBlank_e = vTotal;
uint16 offset = mode.bytesPerRow / 8;
uint8 crtc[25];
crtc[0x00] = hTotal;
crtc[0x01] = hDisp_e;
crtc[0x02] = hBlank_s;
crtc[0x03] = (hBlank_e & 0x1f) | 0x80;
crtc[0x04] = hSync_s;
crtc[0x05] = ((hSync_e & 0x1f) | ((hBlank_e & 0x20) << 2));
crtc[0x06] = vTotal;
crtc[0x07] = (((vTotal & 0x100) >> 8)
| ((vDisp_e & 0x100) >> 7)
| ((vSync_s & 0x100) >> 6)
| ((vBlank_s & 0x100) >> 5)
| 0x10
| ((vTotal & 0x200) >> 4)
| ((vDisp_e & 0x200) >> 3)
| ((vSync_s & 0x200) >> 2));
crtc[0x08] = 0x00;
crtc[0x09] = ((vBlank_s & 0x200) >> 4) | 0x40;
crtc[0x0a] = 0x00;
crtc[0x0b] = 0x00;
crtc[0x0c] = 0x00;
crtc[0x0d] = 0x00;
crtc[0x0e] = 0x00;
crtc[0x0f] = 0x00;
crtc[0x10] = vSync_s;
crtc[0x11] = (vSync_e & 0x0f) | 0x20;
crtc[0x12] = vDisp_e;
crtc[0x13] = offset;
crtc[0x14] = 0x00;
crtc[0x15] = vBlank_s;
crtc[0x16] = vBlank_e;
crtc[0x17] = 0xc3;
crtc[0x18] = 0xff;
WriteCrtcReg(0x11, crtc[0x11] & ~0x80);
for (uint8 j = 0; j <= 0x18; j++)
WriteCrtcReg(j, crtc[j]);
WriteCrtcReg(EXT_VERT_TOTAL, vTotal >> 8);
WriteCrtcReg(EXT_VERT_DISPLAY, vDisp_e >> 8);
WriteCrtcReg(EXT_VERT_SYNC_START, vSync_s >> 8);
WriteCrtcReg(EXT_VERT_BLANK_START, vBlank_s >> 8);
WriteCrtcReg(EXT_HORIZ_TOTAL, hTotal >> 8);
WriteCrtcReg(EXT_HORIZ_BLANK, (hBlank_e & 0x40) >> 6);
WriteCrtcReg(EXT_OFFSET, offset >> 8);
WriteCrtcReg(INTERLACE_CNTL, INTERLACE_DISABLE);
WriteCrtcReg(IO_CTNL, ReadCrtcReg(IO_CTNL) | EXTENDED_CRTC_CNTL);
}
status_t
I810_SetDisplayMode(const DisplayModeEx& mode)
{
if (mode.bitsPerPixel != 8 && mode.bitsPerPixel != 16) {
TRACE("Unsupported color depth: %d bpp\n", mode.bitsPerPixel);
return B_ERROR;
}
snooze(50000);
uint8 temp = INREG8(DRAM_ROW_CNTL_HI) & ~DRAM_REFRESH_RATE;
OUTREG8(DRAM_ROW_CNTL_HI, temp | DRAM_REFRESH_DISABLE);
snooze(1000);
uint16 m, n, p;
CalcVCLK(mode.timing.pixel_clock / 1000.0, m, n, p);
OUTREG16(VCLK2_VCO_M, m);
OUTREG16(VCLK2_VCO_N, n);
OUTREG8(VCLK2_VCO_DIV_SEL, p);
uint8 miscOutReg = 0x08 | 0x01;
if (!(mode.timing.flags & B_POSITIVE_HSYNC))
miscOutReg |= 0x40;
if (!(mode.timing.flags & B_POSITIVE_VSYNC))
miscOutReg |= 0x80;
OUTREG8(MISC_OUT_W, miscOutReg);
SetCrtcTimingValues(mode);
OUTREG32(MEM_MODE, INREG32(MEM_MODE) | 4);
uint8 addrMapping = ReadGraphReg(ADDRESS_MAPPING);
addrMapping &= 0xE0;
addrMapping |= (GTT_MEM_MAP_ENABLE | LINEAR_MODE_ENABLE);
WriteGraphReg(ADDRESS_MAPPING, addrMapping);
temp = INREG8(DRAM_ROW_CNTL_HI) & ~DRAM_REFRESH_RATE;
OUTREG8(DRAM_ROW_CNTL_HI, temp | DRAM_REFRESH_60HZ);
temp = INREG8(BITBLT_CNTL) & ~COLEXP_MODE;
temp |= (mode.bitsPerPixel == 8 ? COLEXP_8BPP : COLEXP_16BPP);
OUTREG8(BITBLT_CNTL, temp);
uint32 temp32 = INREG32(PIXPIPE_CONFIG) & 0xF3E062FC;
temp32 |= (DAC_8_BIT | HIRES_MODE | NO_BLANK_DELAY |
(mode.bitsPerPixel == 8 ? DISPLAY_8BPP_MODE : DISPLAY_16BPP_MODE));
OUTREG32(PIXPIPE_CONFIG, temp32);
OUTREG16(EIR, 0);
temp32 = INREG32(FWATER_BLC);
temp32 &= ~(LM_BURST_LENGTH | LM_FIFO_WATERMARK
| MM_BURST_LENGTH | MM_FIFO_WATERMARK);
temp32 |= I810_GetWatermark(mode);
OUTREG32(FWATER_BLC, temp32);
WriteCrtcReg(IO_CTNL, ReadCrtcReg(IO_CTNL) | EXTENDED_CRTC_CNTL);
I810_AdjustFrame(mode);
return B_OK;
}
void
I810_AdjustFrame(const DisplayModeEx& mode)
{
uint32 address = ((mode.v_display_start * mode.virtual_width
+ mode.h_display_start) * mode.bytesPerPixel) >> 2;
WriteCrtcReg(START_ADDR_LO, address & 0xff);
WriteCrtcReg(START_ADDR_HI, (address >> 8) & 0xff);
WriteCrtcReg(EXT_START_ADDR_HI, (address >> 22) & 0xff);
WriteCrtcReg(EXT_START_ADDR,
((address >> 16) & 0x3f) | EXT_START_ADDR_ENABLE);
}
void
I810_SetIndexedColors(uint count, uint8 first, uint8* colorData, uint32 flags)
{
(void)flags;
if (gInfo.sharedInfo->displayMode.space != B_CMAP8)
return ;
OUTREG8(DAC_MASK, 0xff);
OUTREG8(DAC_W_INDEX, first);
while (count--) {
OUTREG8(DAC_DATA, colorData[0]);
OUTREG8(DAC_DATA, colorData[1]);
OUTREG8(DAC_DATA, colorData[2]);
colorData += 3;
}
}