Subversion Repositories shark

Compare Revisions

Ignore whitespace Rev 488 → Rev 489

/shark/trunk/drivers/fb/fun16.c
144,7 → 144,6
{
 
grx_info = registered_fb[num];
grx_info->fix.visual = FB_VISUAL_DIRECTCOLOR;
 
}
 
/shark/trunk/drivers/fb/fbmem.c
1404,7 → 1404,6
 
if (info->var.bits_per_pixel > 8)
info->fix.visual = FB_VISUAL_TRUECOLOR;
info->fix.line_length = info->var.xres * info->var.bits_per_pixel;
 
rect.dx = 0;
rect.dy = 0;
1415,13 → 1414,6
cfb_fillrect(info,&rect);
if (info->var.bits_per_pixel >= 8) {
fb_prepare_logo(info);
fb_show_logo(info);
}
 
return 0;
}
/shark/trunk/drivers/fb/matrox/matroxfb_Ti3026.h
0,0 → 1,13
#ifndef __MATROXFB_TI3026_H__
#define __MATROXFB_TI3026_H__
 
/* make checkconfig does not walk through whole include tree */
#include <linux/config.h>
 
#include "matroxfb_base.h"
 
#ifdef CONFIG_FB_MATROX_MILLENIUM
extern struct matrox_switch matrox_millennium;
#endif
 
#endif /* __MATROXFB_TI3026_H__ */
/shark/trunk/drivers/fb/matrox/matroxfb_misc.h
0,0 → 1,18
#ifndef __MATROXFB_MISC_H__
#define __MATROXFB_MISC_H__
 
#include "matroxfb_base.h"
 
/* also for modules */
int matroxfb_PLL_calcclock(const struct matrox_pll_features* pll, unsigned int freq, unsigned int fmax,
unsigned int* in, unsigned int* feed, unsigned int* post);
static inline int PLL_calcclock(CPMINFO unsigned int freq, unsigned int fmax,
unsigned int* in, unsigned int* feed, unsigned int* post) {
return matroxfb_PLL_calcclock(&ACCESS_FBINFO(features.pll), freq, fmax, in, feed, post);
}
 
int matroxfb_vgaHWinit(WPMINFO struct my_timming* m);
void matroxfb_vgaHWrestore(WPMINFO2);
void matroxfb_read_pins(WPMINFO2);
 
#endif /* __MATROXFB_MISC_H__ */
/shark/trunk/drivers/fb/matrox/matroxfb_DAC1064.c
0,0 → 1,1094
/*
*
* Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200, G400 and G450.
*
* (c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz>
*
* Portions Copyright (c) 2001 Matrox Graphics Inc.
*
* Version: 1.65 2002/08/14
*
* See matroxfb_base.c for contributors.
*
*/
 
/* make checkconfig does not walk through include tree :-( */
 
#include <linuxcomp.h>
 
#include <linux/config.h>
 
#include "matroxfb_DAC1064.h"
#include "matroxfb_misc.h"
#include "matroxfb_accel.h"
#include "g450_pll.h"
#include <linux/matroxfb.h>
 
#ifdef NEED_DAC1064
#define outDAC1064 matroxfb_DAC_out
#define inDAC1064 matroxfb_DAC_in
 
#define DAC1064_OPT_SCLK_PCI 0x00
#define DAC1064_OPT_SCLK_PLL 0x01
#define DAC1064_OPT_SCLK_EXT 0x02
#define DAC1064_OPT_SCLK_MASK 0x03
#define DAC1064_OPT_GDIV1 0x04 /* maybe it is GDIV2 on G100 ?! */
#define DAC1064_OPT_GDIV3 0x00
#define DAC1064_OPT_MDIV1 0x08
#define DAC1064_OPT_MDIV2 0x00
#define DAC1064_OPT_RESERVED 0x10
 
static void DAC1064_calcclock(CPMINFO unsigned int freq, unsigned int fmax, unsigned int* in, unsigned int* feed, unsigned int* post) {
unsigned int fvco;
unsigned int p;
 
DBG(__FUNCTION__)
/* only for devices older than G450 */
 
fvco = PLL_calcclock(PMINFO freq, fmax, in, feed, &p);
p = (1 << p) - 1;
if (fvco <= 100000)
;
else if (fvco <= 140000)
p |= 0x08;
else if (fvco <= 180000)
p |= 0x10;
else
p |= 0x18;
*post = p;
}
 
/* they must be in POS order */
static const unsigned char MGA1064_DAC_regs[] = {
M1064_XCURADDL, M1064_XCURADDH, M1064_XCURCTRL,
M1064_XCURCOL0RED, M1064_XCURCOL0GREEN, M1064_XCURCOL0BLUE,
M1064_XCURCOL1RED, M1064_XCURCOL1GREEN, M1064_XCURCOL1BLUE,
M1064_XCURCOL2RED, M1064_XCURCOL2GREEN, M1064_XCURCOL2BLUE,
DAC1064_XVREFCTRL, M1064_XMULCTRL, M1064_XPIXCLKCTRL, M1064_XGENCTRL,
M1064_XMISCCTRL,
M1064_XGENIOCTRL, M1064_XGENIODATA, M1064_XZOOMCTRL, M1064_XSENSETEST,
M1064_XCRCBITSEL,
M1064_XCOLKEYMASKL, M1064_XCOLKEYMASKH, M1064_XCOLKEYL, M1064_XCOLKEYH };
 
static const unsigned char MGA1064_DAC[] = {
0x00, 0x00, M1064_XCURCTRL_DIS,
0x00, 0x00, 0x00, /* black */
0xFF, 0xFF, 0xFF, /* white */
0xFF, 0x00, 0x00, /* red */
0x00, 0,
M1064_XPIXCLKCTRL_PLL_UP | M1064_XPIXCLKCTRL_EN | M1064_XPIXCLKCTRL_SRC_PLL,
M1064_XGENCTRL_VS_0 | M1064_XGENCTRL_ALPHA_DIS | M1064_XGENCTRL_BLACK_0IRE | M1064_XGENCTRL_NO_SYNC_ON_GREEN,
M1064_XMISCCTRL_DAC_8BIT,
0x00, 0x00, M1064_XZOOMCTRL_1, M1064_XSENSETEST_BCOMP | M1064_XSENSETEST_GCOMP | M1064_XSENSETEST_RCOMP | M1064_XSENSETEST_PDOWN,
0x00,
0x00, 0x00, 0xFF, 0xFF};
 
static void DAC1064_setpclk(WPMINFO unsigned long fout) {
unsigned int m, n, p;
 
DBG(__FUNCTION__)
 
DAC1064_calcclock(PMINFO fout, ACCESS_FBINFO(max_pixel_clock), &m, &n, &p);
ACCESS_FBINFO(hw).DACclk[0] = m;
ACCESS_FBINFO(hw).DACclk[1] = n;
ACCESS_FBINFO(hw).DACclk[2] = p;
}
 
static void DAC1064_setmclk(WPMINFO int oscinfo, unsigned long fmem) {
u_int32_t mx;
struct matrox_hw_state* hw = &ACCESS_FBINFO(hw);
 
DBG(__FUNCTION__)
 
if (ACCESS_FBINFO(devflags.noinit)) {
/* read MCLK and give up... */
hw->DACclk[3] = inDAC1064(PMINFO DAC1064_XSYSPLLM);
hw->DACclk[4] = inDAC1064(PMINFO DAC1064_XSYSPLLN);
hw->DACclk[5] = inDAC1064(PMINFO DAC1064_XSYSPLLP);
return;
}
mx = hw->MXoptionReg | 0x00000004;
pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, mx);
mx &= ~0x000000BB;
if (oscinfo & DAC1064_OPT_GDIV1)
mx |= 0x00000008;
if (oscinfo & DAC1064_OPT_MDIV1)
mx |= 0x00000010;
if (oscinfo & DAC1064_OPT_RESERVED)
mx |= 0x00000080;
if ((oscinfo & DAC1064_OPT_SCLK_MASK) == DAC1064_OPT_SCLK_PLL) {
/* select PCI clock until we have setup oscilator... */
int clk;
unsigned int m, n, p;
 
/* powerup system PLL, select PCI clock */
mx |= 0x00000020;
pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, mx);
mx &= ~0x00000004;
pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, mx);
 
/* !!! you must not access device if MCLK is not running !!!
Doing so cause immediate PCI lockup :-( Maybe they should
generate ABORT or I/O (parity...) error and Linux should
recover from this... (kill driver/process). But world is not
perfect... */
/* (bit 2 of PCI_OPTION_REG must be 0... and bits 0,1 must not
select PLL... because of PLL can be stopped at this time) */
DAC1064_calcclock(PMINFO fmem, ACCESS_FBINFO(max_pixel_clock), &m, &n, &p);
outDAC1064(PMINFO DAC1064_XSYSPLLM, hw->DACclk[3] = m);
outDAC1064(PMINFO DAC1064_XSYSPLLN, hw->DACclk[4] = n);
outDAC1064(PMINFO DAC1064_XSYSPLLP, hw->DACclk[5] = p);
for (clk = 65536; clk; --clk) {
if (inDAC1064(PMINFO DAC1064_XSYSPLLSTAT) & 0x40)
break;
}
if (!clk)
printk(KERN_ERR "matroxfb: aiee, SYSPLL not locked\n");
/* select PLL */
mx |= 0x00000005;
} else {
/* select specified system clock source */
mx |= oscinfo & DAC1064_OPT_SCLK_MASK;
}
pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, mx);
mx &= ~0x00000004;
pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, mx);
hw->MXoptionReg = mx;
}
 
#ifdef CONFIG_FB_MATROX_G450
static void g450_set_plls(WPMINFO2) {
u_int32_t c2_ctl;
unsigned int pxc;
struct matrox_hw_state* hw = &ACCESS_FBINFO(hw);
int pixelmnp;
int videomnp;
c2_ctl = hw->crtc2.ctl & ~0x4007; /* Clear PLL + enable for CRTC2 */
c2_ctl |= 0x0001; /* Enable CRTC2 */
hw->DACreg[POS1064_XPWRCTRL] &= ~0x02; /* Stop VIDEO PLL */
pixelmnp = ACCESS_FBINFO(crtc1).mnp;
videomnp = ACCESS_FBINFO(crtc2).mnp;
if (videomnp < 0) {
c2_ctl &= ~0x0001; /* Disable CRTC2 */
hw->DACreg[POS1064_XPWRCTRL] &= ~0x10; /* Powerdown CRTC2 */
} else if (ACCESS_FBINFO(crtc2).pixclock == ACCESS_FBINFO(features).pll.ref_freq) {
c2_ctl |= 0x4002; /* Use reference directly */
} else if (videomnp == pixelmnp) {
c2_ctl |= 0x0004; /* Use pixel PLL */
} else {
if (0 == ((videomnp ^ pixelmnp) & 0xFFFFFF00)) {
/* PIXEL and VIDEO PLL must not use same frequency. We modify N
of PIXEL PLL in such case because of VIDEO PLL may be source
of TVO clocks, and chroma subcarrier is derived from its
pixel clocks */
pixelmnp += 0x000100;
}
c2_ctl |= 0x0006; /* Use video PLL */
hw->DACreg[POS1064_XPWRCTRL] |= 0x02;
outDAC1064(PMINFO M1064_XPWRCTRL, hw->DACreg[POS1064_XPWRCTRL]);
matroxfb_g450_setpll_cond(PMINFO videomnp, M_VIDEO_PLL);
}
 
hw->DACreg[POS1064_XPIXCLKCTRL] &= ~M1064_XPIXCLKCTRL_PLL_UP;
if (pixelmnp >= 0) {
hw->DACreg[POS1064_XPIXCLKCTRL] |= M1064_XPIXCLKCTRL_PLL_UP;
outDAC1064(PMINFO M1064_XPIXCLKCTRL, hw->DACreg[POS1064_XPIXCLKCTRL]);
matroxfb_g450_setpll_cond(PMINFO pixelmnp, M_PIXEL_PLL_C);
}
if (c2_ctl != hw->crtc2.ctl) {
hw->crtc2.ctl = c2_ctl;
mga_outl(0x3C10, c2_ctl);
}
 
pxc = ACCESS_FBINFO(crtc1).pixclock;
if (pxc == 0 || ACCESS_FBINFO(outputs[2]).src == MATROXFB_SRC_CRTC2) {
pxc = ACCESS_FBINFO(crtc2).pixclock;
}
if (ACCESS_FBINFO(chip) == MGA_G550) {
if (pxc < 45000) {
hw->DACreg[POS1064_XPANMODE] = 0x00; /* 0-50 */
} else if (pxc < 55000) {
hw->DACreg[POS1064_XPANMODE] = 0x08; /* 34-62 */
} else if (pxc < 70000) {
hw->DACreg[POS1064_XPANMODE] = 0x10; /* 42-78 */
} else if (pxc < 85000) {
hw->DACreg[POS1064_XPANMODE] = 0x18; /* 62-92 */
} else if (pxc < 100000) {
hw->DACreg[POS1064_XPANMODE] = 0x20; /* 74-108 */
} else if (pxc < 115000) {
hw->DACreg[POS1064_XPANMODE] = 0x28; /* 94-122 */
} else if (pxc < 125000) {
hw->DACreg[POS1064_XPANMODE] = 0x30; /* 108-132 */
} else {
hw->DACreg[POS1064_XPANMODE] = 0x38; /* 120-168 */
}
} else {
/* G450 */
if (pxc < 45000) {
hw->DACreg[POS1064_XPANMODE] = 0x00; /* 0-54 */
} else if (pxc < 65000) {
hw->DACreg[POS1064_XPANMODE] = 0x08; /* 38-70 */
} else if (pxc < 85000) {
hw->DACreg[POS1064_XPANMODE] = 0x10; /* 56-96 */
} else if (pxc < 105000) {
hw->DACreg[POS1064_XPANMODE] = 0x18; /* 80-114 */
} else if (pxc < 135000) {
hw->DACreg[POS1064_XPANMODE] = 0x20; /* 102-144 */
} else if (pxc < 160000) {
hw->DACreg[POS1064_XPANMODE] = 0x28; /* 132-166 */
} else if (pxc < 175000) {
hw->DACreg[POS1064_XPANMODE] = 0x30; /* 154-182 */
} else {
hw->DACreg[POS1064_XPANMODE] = 0x38; /* 170-204 */
}
}
}
#endif
 
void DAC1064_global_init(WPMINFO2) {
struct matrox_hw_state* hw = &ACCESS_FBINFO(hw);
 
hw->DACreg[POS1064_XMISCCTRL] &= M1064_XMISCCTRL_DAC_WIDTHMASK;
hw->DACreg[POS1064_XMISCCTRL] |= M1064_XMISCCTRL_LUT_EN;
hw->DACreg[POS1064_XPIXCLKCTRL] = M1064_XPIXCLKCTRL_PLL_UP | M1064_XPIXCLKCTRL_EN | M1064_XPIXCLKCTRL_SRC_PLL;
#ifdef CONFIG_FB_MATROX_G450
if (ACCESS_FBINFO(devflags.g450dac)) {
hw->DACreg[POS1064_XPWRCTRL] = 0x1F; /* powerup everything */
hw->DACreg[POS1064_XOUTPUTCONN] = 0x00; /* disable outputs */
hw->DACreg[POS1064_XMISCCTRL] |= M1064_XMISCCTRL_DAC_EN;
switch (ACCESS_FBINFO(outputs[0]).src) {
case MATROXFB_SRC_CRTC1:
case MATROXFB_SRC_CRTC2:
hw->DACreg[POS1064_XOUTPUTCONN] |= 0x01; /* enable output; CRTC1/2 selection is in CRTC2 ctl */
break;
case MATROXFB_SRC_NONE:
hw->DACreg[POS1064_XMISCCTRL] &= ~M1064_XMISCCTRL_DAC_EN;
break;
}
switch (ACCESS_FBINFO(outputs[1]).src) {
case MATROXFB_SRC_CRTC1:
hw->DACreg[POS1064_XOUTPUTCONN] |= 0x04;
break;
case MATROXFB_SRC_CRTC2:
if (ACCESS_FBINFO(outputs[1]).mode == MATROXFB_OUTPUT_MODE_MONITOR) {
hw->DACreg[POS1064_XOUTPUTCONN] |= 0x08;
} else {
hw->DACreg[POS1064_XOUTPUTCONN] |= 0x0C;
}
break;
case MATROXFB_SRC_NONE:
hw->DACreg[POS1064_XPWRCTRL] &= ~0x01; /* Poweroff DAC2 */
break;
}
switch (ACCESS_FBINFO(outputs[2]).src) {
case MATROXFB_SRC_CRTC1:
hw->DACreg[POS1064_XOUTPUTCONN] |= 0x20;
break;
case MATROXFB_SRC_CRTC2:
hw->DACreg[POS1064_XOUTPUTCONN] |= 0x40;
break;
case MATROXFB_SRC_NONE:
#if 0
/* HELP! If we boot without DFP connected to DVI, we can
poweroff TMDS. But if we boot with DFP connected,
TMDS generated clocks are used instead of ALL pixclocks
available... If someone knows which register
handles it, please reveal this secret to me... */
hw->DACreg[POS1064_XPWRCTRL] &= ~0x04; /* Poweroff TMDS */
#endif
break;
}
/* Now set timming related variables... */
g450_set_plls(PMINFO2);
} else
#endif
{
if (ACCESS_FBINFO(outputs[1]).src == MATROXFB_SRC_CRTC1) {
hw->DACreg[POS1064_XPIXCLKCTRL] = M1064_XPIXCLKCTRL_PLL_UP | M1064_XPIXCLKCTRL_EN | M1064_XPIXCLKCTRL_SRC_EXT;
hw->DACreg[POS1064_XMISCCTRL] |= GX00_XMISCCTRL_MFC_MAFC | G400_XMISCCTRL_VDO_MAFC12;
} else if (ACCESS_FBINFO(outputs[1]).src == MATROXFB_SRC_CRTC2) {
hw->DACreg[POS1064_XMISCCTRL] |= GX00_XMISCCTRL_MFC_MAFC | G400_XMISCCTRL_VDO_C2_MAFC12;
} else if (ACCESS_FBINFO(outputs[2]).src == MATROXFB_SRC_CRTC1)
hw->DACreg[POS1064_XMISCCTRL] |= GX00_XMISCCTRL_MFC_PANELLINK | G400_XMISCCTRL_VDO_MAFC12;
else
hw->DACreg[POS1064_XMISCCTRL] |= GX00_XMISCCTRL_MFC_DIS;
 
if (ACCESS_FBINFO(outputs[0]).src != MATROXFB_SRC_NONE)
hw->DACreg[POS1064_XMISCCTRL] |= M1064_XMISCCTRL_DAC_EN;
}
}
 
void DAC1064_global_restore(WPMINFO2) {
struct matrox_hw_state* hw = &ACCESS_FBINFO(hw);
 
outDAC1064(PMINFO M1064_XPIXCLKCTRL, hw->DACreg[POS1064_XPIXCLKCTRL]);
outDAC1064(PMINFO M1064_XMISCCTRL, hw->DACreg[POS1064_XMISCCTRL]);
if (ACCESS_FBINFO(devflags.accelerator) == FB_ACCEL_MATROX_MGAG400) {
outDAC1064(PMINFO 0x20, 0x04);
outDAC1064(PMINFO 0x1F, ACCESS_FBINFO(devflags.dfp_type));
if (ACCESS_FBINFO(devflags.g450dac)) {
outDAC1064(PMINFO M1064_XSYNCCTRL, 0xCC);
outDAC1064(PMINFO M1064_XPWRCTRL, hw->DACreg[POS1064_XPWRCTRL]);
outDAC1064(PMINFO M1064_XPANMODE, hw->DACreg[POS1064_XPANMODE]);
outDAC1064(PMINFO M1064_XOUTPUTCONN, hw->DACreg[POS1064_XOUTPUTCONN]);
}
}
}
 
static int DAC1064_init_1(WPMINFO struct my_timming* m) {
struct matrox_hw_state* hw = &ACCESS_FBINFO(hw);
 
DBG(__FUNCTION__)
 
memcpy(hw->DACreg, MGA1064_DAC, sizeof(MGA1064_DAC_regs));
switch (ACCESS_FBINFO(fbcon).var.bits_per_pixel) {
/* case 4: not supported by MGA1064 DAC */
case 8:
hw->DACreg[POS1064_XMULCTRL] = M1064_XMULCTRL_DEPTH_8BPP | M1064_XMULCTRL_GRAPHICS_PALETIZED;
break;
case 16:
if (ACCESS_FBINFO(fbcon).var.green.length == 5)
hw->DACreg[POS1064_XMULCTRL] = M1064_XMULCTRL_DEPTH_15BPP_1BPP | M1064_XMULCTRL_GRAPHICS_PALETIZED;
else
hw->DACreg[POS1064_XMULCTRL] = M1064_XMULCTRL_DEPTH_16BPP | M1064_XMULCTRL_GRAPHICS_PALETIZED;
break;
case 24:
hw->DACreg[POS1064_XMULCTRL] = M1064_XMULCTRL_DEPTH_24BPP | M1064_XMULCTRL_GRAPHICS_PALETIZED;
break;
case 32:
hw->DACreg[POS1064_XMULCTRL] = M1064_XMULCTRL_DEPTH_32BPP | M1064_XMULCTRL_GRAPHICS_PALETIZED;
break;
default:
return 1; /* unsupported depth */
}
hw->DACreg[POS1064_XVREFCTRL] = ACCESS_FBINFO(features.DAC1064.xvrefctrl);
hw->DACreg[POS1064_XGENCTRL] &= ~M1064_XGENCTRL_SYNC_ON_GREEN_MASK;
hw->DACreg[POS1064_XGENCTRL] |= (m->sync & FB_SYNC_ON_GREEN)?M1064_XGENCTRL_SYNC_ON_GREEN:M1064_XGENCTRL_NO_SYNC_ON_GREEN;
hw->DACreg[POS1064_XCURADDL] = 0;
hw->DACreg[POS1064_XCURADDH] = 0;
 
DAC1064_global_init(PMINFO2);
return 0;
}
 
static int DAC1064_init_2(WPMINFO struct my_timming* m) {
struct matrox_hw_state* hw = &ACCESS_FBINFO(hw);
 
DBG(__FUNCTION__)
 
if (ACCESS_FBINFO(fbcon).var.bits_per_pixel > 16) { /* 256 entries */
int i;
 
for (i = 0; i < 256; i++) {
hw->DACpal[i * 3 + 0] = i;
hw->DACpal[i * 3 + 1] = i;
hw->DACpal[i * 3 + 2] = i;
}
} else if (ACCESS_FBINFO(fbcon).var.bits_per_pixel > 8) {
if (ACCESS_FBINFO(fbcon).var.green.length == 5) { /* 0..31, 128..159 */
int i;
 
for (i = 0; i < 32; i++) {
/* with p15 == 0 */
hw->DACpal[i * 3 + 0] = i << 3;
hw->DACpal[i * 3 + 1] = i << 3;
hw->DACpal[i * 3 + 2] = i << 3;
/* with p15 == 1 */
hw->DACpal[(i + 128) * 3 + 0] = i << 3;
hw->DACpal[(i + 128) * 3 + 1] = i << 3;
hw->DACpal[(i + 128) * 3 + 2] = i << 3;
}
} else {
int i;
 
for (i = 0; i < 64; i++) { /* 0..63 */
hw->DACpal[i * 3 + 0] = i << 3;
hw->DACpal[i * 3 + 1] = i << 2;
hw->DACpal[i * 3 + 2] = i << 3;
}
}
} else {
memset(hw->DACpal, 0, 768);
}
return 0;
}
 
static void DAC1064_restore_1(WPMINFO2) {
struct matrox_hw_state* hw = &ACCESS_FBINFO(hw);
 
CRITFLAGS
 
DBG(__FUNCTION__)
 
CRITBEGIN
 
if ((inDAC1064(PMINFO DAC1064_XSYSPLLM) != hw->DACclk[3]) ||
(inDAC1064(PMINFO DAC1064_XSYSPLLN) != hw->DACclk[4]) ||
(inDAC1064(PMINFO DAC1064_XSYSPLLP) != hw->DACclk[5])) {
outDAC1064(PMINFO DAC1064_XSYSPLLM, hw->DACclk[3]);
outDAC1064(PMINFO DAC1064_XSYSPLLN, hw->DACclk[4]);
outDAC1064(PMINFO DAC1064_XSYSPLLP, hw->DACclk[5]);
}
{
unsigned int i;
 
for (i = 0; i < sizeof(MGA1064_DAC_regs); i++) {
if ((i != POS1064_XPIXCLKCTRL) && (i != POS1064_XMISCCTRL))
outDAC1064(PMINFO MGA1064_DAC_regs[i], hw->DACreg[i]);
}
}
 
DAC1064_global_restore(PMINFO2);
 
CRITEND
};
 
static void DAC1064_restore_2(WPMINFO2) {
#ifdef DEBUG
unsigned int i;
#endif
 
DBG(__FUNCTION__)
 
#ifdef DEBUG
dprintk(KERN_DEBUG "DAC1064regs ");
for (i = 0; i < sizeof(MGA1064_DAC_regs); i++) {
dprintk("R%02X=%02X ", MGA1064_DAC_regs[i], ACCESS_FBINFO(hw).DACreg[i]);
if ((i & 0x7) == 0x7) dprintk("\n" KERN_DEBUG "continuing... ");
}
dprintk("\n" KERN_DEBUG "DAC1064clk ");
for (i = 0; i < 6; i++)
dprintk("C%02X=%02X ", i, ACCESS_FBINFO(hw).DACclk[i]);
dprintk("\n");
#endif
}
 
static int m1064_compute(void* out, struct my_timming* m) {
#define minfo ((struct matrox_fb_info*)out)
{
int i;
int tmout;
CRITFLAGS
 
DAC1064_setpclk(PMINFO m->pixclock);
 
CRITBEGIN
 
for (i = 0; i < 3; i++)
outDAC1064(PMINFO M1064_XPIXPLLCM + i, ACCESS_FBINFO(hw).DACclk[i]);
for (tmout = 500000; tmout; tmout--) {
if (inDAC1064(PMINFO M1064_XPIXPLLSTAT) & 0x40)
break;
udelay(10);
};
 
CRITEND
 
if (!tmout)
printk(KERN_ERR "matroxfb: Pixel PLL not locked after 5 secs\n");
}
#undef minfo
return 0;
}
 
static struct matrox_altout m1064 = {
.name = "Primary output",
.compute = m1064_compute,
};
 
#ifdef CONFIG_FB_MATROX_G450
static int g450_compute(void* out, struct my_timming* m) {
#define minfo ((struct matrox_fb_info*)out)
if (m->mnp < 0) {
m->mnp = matroxfb_g450_setclk(PMINFO m->pixclock, (m->crtc == MATROXFB_SRC_CRTC1) ? M_PIXEL_PLL_C : M_VIDEO_PLL);
if (m->mnp >= 0) {
m->pixclock = g450_mnp2f(PMINFO m->mnp);
}
}
#undef minfo
return 0;
}
 
static struct matrox_altout g450out = {
.name = "Primary output",
.compute = g450_compute,
};
#endif
 
#endif /* NEED_DAC1064 */
 
#ifdef CONFIG_FB_MATROX_MYSTIQUE
static int MGA1064_init(WPMINFO struct my_timming* m) {
struct matrox_hw_state* hw = &ACCESS_FBINFO(hw);
 
DBG(__FUNCTION__)
 
if (DAC1064_init_1(PMINFO m)) return 1;
if (matroxfb_vgaHWinit(PMINFO m)) return 1;
 
hw->MiscOutReg = 0xCB;
if (m->sync & FB_SYNC_HOR_HIGH_ACT)
hw->MiscOutReg &= ~0x40;
if (m->sync & FB_SYNC_VERT_HIGH_ACT)
hw->MiscOutReg &= ~0x80;
if (m->sync & FB_SYNC_COMP_HIGH_ACT) /* should be only FB_SYNC_COMP */
hw->CRTCEXT[3] |= 0x40;
 
if (DAC1064_init_2(PMINFO m)) return 1;
return 0;
}
#endif
 
#ifdef CONFIG_FB_MATROX_G100
static int MGAG100_init(WPMINFO struct my_timming* m) {
struct matrox_hw_state* hw = &ACCESS_FBINFO(hw);
 
DBG(__FUNCTION__)
 
if (DAC1064_init_1(PMINFO m)) return 1;
hw->MXoptionReg &= ~0x2000;
if (matroxfb_vgaHWinit(PMINFO m)) return 1;
 
hw->MiscOutReg = 0xEF;
if (m->sync & FB_SYNC_HOR_HIGH_ACT)
hw->MiscOutReg &= ~0x40;
if (m->sync & FB_SYNC_VERT_HIGH_ACT)
hw->MiscOutReg &= ~0x80;
if (m->sync & FB_SYNC_COMP_HIGH_ACT) /* should be only FB_SYNC_COMP */
hw->CRTCEXT[3] |= 0x40;
 
if (DAC1064_init_2(PMINFO m)) return 1;
return 0;
}
#endif /* G100 */
 
#ifdef CONFIG_FB_MATROX_MYSTIQUE
static void MGA1064_ramdac_init(WPMINFO2) {
 
DBG(__FUNCTION__)
 
/* ACCESS_FBINFO(features.DAC1064.vco_freq_min) = 120000; */
ACCESS_FBINFO(features.pll.vco_freq_min) = 62000;
ACCESS_FBINFO(features.pll.ref_freq) = 14318;
ACCESS_FBINFO(features.pll.feed_div_min) = 100;
ACCESS_FBINFO(features.pll.feed_div_max) = 127;
ACCESS_FBINFO(features.pll.in_div_min) = 1;
ACCESS_FBINFO(features.pll.in_div_max) = 31;
ACCESS_FBINFO(features.pll.post_shift_max) = 3;
ACCESS_FBINFO(features.DAC1064.xvrefctrl) = DAC1064_XVREFCTRL_EXTERNAL;
/* maybe cmdline MCLK= ?, doc says gclk=44MHz, mclk=66MHz... it was 55/83 with old values */
DAC1064_setmclk(PMINFO DAC1064_OPT_MDIV2 | DAC1064_OPT_GDIV3 | DAC1064_OPT_SCLK_PLL, 133333);
}
#endif
 
#ifdef CONFIG_FB_MATROX_G100
/* BIOS environ */
static int x7AF4 = 0x10; /* flags, maybe 0x10 = SDRAM, 0x00 = SGRAM??? */
/* G100 wants 0x10, G200 SGRAM does not care... */
#if 0
static int def50 = 0; /* reg50, & 0x0F, & 0x3000 (only 0x0000, 0x1000, 0x2000 (0x3000 disallowed and treated as 0) */
#endif
 
static void MGAG100_progPixClock(CPMINFO int flags, int m, int n, int p) {
int reg;
int selClk;
int clk;
 
DBG(__FUNCTION__)
 
outDAC1064(PMINFO M1064_XPIXCLKCTRL, inDAC1064(PMINFO M1064_XPIXCLKCTRL) | M1064_XPIXCLKCTRL_DIS |
M1064_XPIXCLKCTRL_PLL_UP);
switch (flags & 3) {
case 0: reg = M1064_XPIXPLLAM; break;
case 1: reg = M1064_XPIXPLLBM; break;
default: reg = M1064_XPIXPLLCM; break;
}
outDAC1064(PMINFO reg++, m);
outDAC1064(PMINFO reg++, n);
outDAC1064(PMINFO reg, p);
selClk = mga_inb(M_MISC_REG_READ) & ~0xC;
/* there should be flags & 0x03 & case 0/1/else */
/* and we should first select source and after that we should wait for PLL */
/* and we are waiting for PLL with oscilator disabled... Is it right? */
switch (flags & 0x03) {
case 0x00: break;
case 0x01: selClk |= 4; break;
default: selClk |= 0x0C; break;
}
mga_outb(M_MISC_REG, selClk);
for (clk = 500000; clk; clk--) {
if (inDAC1064(PMINFO M1064_XPIXPLLSTAT) & 0x40)
break;
udelay(10);
};
if (!clk)
printk(KERN_ERR "matroxfb: Pixel PLL%c not locked after usual time\n", (reg-M1064_XPIXPLLAM-2)/4 + 'A');
selClk = inDAC1064(PMINFO M1064_XPIXCLKCTRL) & ~M1064_XPIXCLKCTRL_SRC_MASK;
switch (flags & 0x0C) {
case 0x00: selClk |= M1064_XPIXCLKCTRL_SRC_PCI; break;
case 0x04: selClk |= M1064_XPIXCLKCTRL_SRC_PLL; break;
default: selClk |= M1064_XPIXCLKCTRL_SRC_EXT; break;
}
outDAC1064(PMINFO M1064_XPIXCLKCTRL, selClk);
outDAC1064(PMINFO M1064_XPIXCLKCTRL, inDAC1064(PMINFO M1064_XPIXCLKCTRL) & ~M1064_XPIXCLKCTRL_DIS);
}
 
static void MGAG100_setPixClock(CPMINFO int flags, int freq) {
unsigned int m, n, p;
 
DBG(__FUNCTION__)
 
DAC1064_calcclock(PMINFO freq, ACCESS_FBINFO(max_pixel_clock), &m, &n, &p);
MGAG100_progPixClock(PMINFO flags, m, n, p);
}
#endif
 
#ifdef CONFIG_FB_MATROX_MYSTIQUE
static int MGA1064_preinit(WPMINFO2) {
static const int vxres_mystique[] = { 512, 640, 768, 800, 832, 960,
1024, 1152, 1280, 1600, 1664, 1920,
2048, 0};
struct matrox_hw_state* hw = &ACCESS_FBINFO(hw);
 
DBG(__FUNCTION__)
 
/* ACCESS_FBINFO(capable.cfb4) = 0; ... preinitialized by 0 */
ACCESS_FBINFO(capable.text) = 1;
ACCESS_FBINFO(capable.vxres) = vxres_mystique;
ACCESS_FBINFO(features.accel.has_cacheflush) = 1;
 
ACCESS_FBINFO(outputs[0]).output = &m1064;
ACCESS_FBINFO(outputs[0]).src = MATROXFB_SRC_CRTC1;
ACCESS_FBINFO(outputs[0]).data = MINFO;
ACCESS_FBINFO(outputs[0]).mode = MATROXFB_OUTPUT_MODE_MONITOR;
 
if (ACCESS_FBINFO(devflags.noinit))
return 0; /* do not modify settings */
hw->MXoptionReg &= 0xC0000100;
hw->MXoptionReg |= 0x00094E20;
if (ACCESS_FBINFO(devflags.novga))
hw->MXoptionReg &= ~0x00000100;
if (ACCESS_FBINFO(devflags.nobios))
hw->MXoptionReg &= ~0x40000000;
if (ACCESS_FBINFO(devflags.nopciretry))
hw->MXoptionReg |= 0x20000000;
pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg);
mga_setr(M_SEQ_INDEX, 0x01, 0x20);
mga_outl(M_CTLWTST, 0x00000000);
udelay(200);
mga_outl(M_MACCESS, 0x00008000);
udelay(100);
mga_outl(M_MACCESS, 0x0000C000);
return 0;
}
 
static void MGA1064_reset(WPMINFO2) {
 
DBG(__FUNCTION__);
 
MGA1064_ramdac_init(PMINFO2);
}
#endif
 
#ifdef CONFIG_FB_MATROX_G100
#ifdef CONFIG_FB_MATROX_G450
static void g450_mclk_init(WPMINFO2) {
/* switch all clocks to PCI source */
pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg | 4);
pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION3_REG, ACCESS_FBINFO(values).reg.opt3 & ~0x00300C03);
pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg);
if (((ACCESS_FBINFO(values).reg.opt3 & 0x000003) == 0x000003) ||
((ACCESS_FBINFO(values).reg.opt3 & 0x000C00) == 0x000C00) ||
((ACCESS_FBINFO(values).reg.opt3 & 0x300000) == 0x300000)) {
matroxfb_g450_setclk(PMINFO ACCESS_FBINFO(values.pll.video), M_VIDEO_PLL);
} else {
unsigned long flags;
unsigned int pwr;
matroxfb_DAC_lock_irqsave(flags);
pwr = inDAC1064(PMINFO M1064_XPWRCTRL) & ~0x02;
outDAC1064(PMINFO M1064_XPWRCTRL, pwr);
matroxfb_DAC_unlock_irqrestore(flags);
}
matroxfb_g450_setclk(PMINFO ACCESS_FBINFO(values.pll.system), M_SYSTEM_PLL);
/* switch clocks to their real PLL source(s) */
pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg | 4);
pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION3_REG, ACCESS_FBINFO(values).reg.opt3);
pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg);
 
}
 
static void g450_memory_init(WPMINFO2) {
/* disable memory refresh */
ACCESS_FBINFO(hw).MXoptionReg &= ~0x001F8000;
pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg);
/* set memory interface parameters */
ACCESS_FBINFO(hw).MXoptionReg &= ~0x00207E00;
ACCESS_FBINFO(hw).MXoptionReg |= 0x00207E00 & ACCESS_FBINFO(values).reg.opt;
pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg);
pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION2_REG, ACCESS_FBINFO(values).reg.opt2);
mga_outl(M_CTLWTST, ACCESS_FBINFO(values).reg.mctlwtst);
/* first set up memory interface with disabled memory interface clocks */
pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_MEMMISC_REG, ACCESS_FBINFO(values).reg.memmisc & ~0x80000000U);
mga_outl(M_MEMRDBK, ACCESS_FBINFO(values).reg.memrdbk);
mga_outl(M_MACCESS, ACCESS_FBINFO(values).reg.maccess);
/* start memory clocks */
pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_MEMMISC_REG, ACCESS_FBINFO(values).reg.memmisc | 0x80000000U);
 
udelay(200);
if (ACCESS_FBINFO(values).memory.ddr && (!ACCESS_FBINFO(values).memory.emrswen || !ACCESS_FBINFO(values).memory.dll)) {
mga_outl(M_MEMRDBK, ACCESS_FBINFO(values).reg.memrdbk & ~0x1000);
}
mga_outl(M_MACCESS, ACCESS_FBINFO(values).reg.maccess | 0x8000);
udelay(200);
ACCESS_FBINFO(hw).MXoptionReg |= 0x001F8000 & ACCESS_FBINFO(values).reg.opt;
pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg);
/* value is written to memory chips only if old != new */
mga_outl(M_PLNWT, 0);
mga_outl(M_PLNWT, ~0);
if (ACCESS_FBINFO(values).reg.mctlwtst != ACCESS_FBINFO(values).reg.mctlwtst_core) {
mga_outl(M_CTLWTST, ACCESS_FBINFO(values).reg.mctlwtst_core);
}
}
 
static void g450_preinit(WPMINFO2) {
u_int32_t c2ctl;
u_int8_t curctl;
u_int8_t c1ctl;
/* ACCESS_FBINFO(hw).MXoptionReg = minfo->values.reg.opt; */
ACCESS_FBINFO(hw).MXoptionReg &= 0xC0000100;
ACCESS_FBINFO(hw).MXoptionReg |= 0x00000020;
if (ACCESS_FBINFO(devflags.novga))
ACCESS_FBINFO(hw).MXoptionReg &= ~0x00000100;
if (ACCESS_FBINFO(devflags.nobios))
ACCESS_FBINFO(hw).MXoptionReg &= ~0x40000000;
if (ACCESS_FBINFO(devflags.nopciretry))
ACCESS_FBINFO(hw).MXoptionReg |= 0x20000000;
ACCESS_FBINFO(hw).MXoptionReg |= ACCESS_FBINFO(values).reg.opt & 0x03400040;
pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg);
 
/* Init system clocks */
/* stop crtc2 */
c2ctl = mga_inl(M_C2CTL);
mga_outl(M_C2CTL, c2ctl & ~1);
/* stop cursor */
curctl = inDAC1064(PMINFO M1064_XCURCTRL);
outDAC1064(PMINFO M1064_XCURCTRL, 0);
/* stop crtc1 */
c1ctl = mga_readr(M_SEQ_INDEX, 1);
mga_setr(M_SEQ_INDEX, 1, c1ctl | 0x20);
 
g450_mclk_init(PMINFO2);
g450_memory_init(PMINFO2);
/* set legacy VGA clock sources for DOSEmu or VMware... */
matroxfb_g450_setclk(PMINFO 25175, M_PIXEL_PLL_A);
matroxfb_g450_setclk(PMINFO 28322, M_PIXEL_PLL_B);
 
/* restore crtc1 */
mga_setr(M_SEQ_INDEX, 1, c1ctl);
/* restore cursor */
outDAC1064(PMINFO M1064_XCURCTRL, curctl);
 
/* restore crtc2 */
mga_outl(M_C2CTL, c2ctl);
return;
}
#else
static inline void g450_preinit(WPMINFO2) {
}
#endif
 
static int MGAG100_preinit(WPMINFO2) {
static const int vxres_g100[] = { 512, 640, 768, 800, 832, 960,
1024, 1152, 1280, 1600, 1664, 1920,
2048, 0};
struct matrox_hw_state* hw = &ACCESS_FBINFO(hw);
 
u_int32_t reg50;
#if 0
u_int32_t q;
#endif
 
DBG(__FUNCTION__)
 
/* there are some instabilities if in_div > 19 && vco < 61000 */
if (ACCESS_FBINFO(devflags.g450dac)) {
ACCESS_FBINFO(features.pll.vco_freq_min) = 130000; /* my sample: >118 */
} else {
ACCESS_FBINFO(features.pll.vco_freq_min) = 62000;
}
if (!ACCESS_FBINFO(features.pll.ref_freq)) {
ACCESS_FBINFO(features.pll.ref_freq) = 27000;
}
ACCESS_FBINFO(features.pll.feed_div_min) = 7;
ACCESS_FBINFO(features.pll.feed_div_max) = 127;
ACCESS_FBINFO(features.pll.in_div_min) = 1;
ACCESS_FBINFO(features.pll.in_div_max) = 31;
ACCESS_FBINFO(features.pll.post_shift_max) = 3;
ACCESS_FBINFO(features.DAC1064.xvrefctrl) = DAC1064_XVREFCTRL_G100_DEFAULT;
/* ACCESS_FBINFO(capable.cfb4) = 0; ... preinitialized by 0 */
ACCESS_FBINFO(capable.text) = 1;
ACCESS_FBINFO(capable.vxres) = vxres_g100;
ACCESS_FBINFO(features.accel.has_cacheflush) = 1;
ACCESS_FBINFO(capable.plnwt) = ACCESS_FBINFO(devflags.accelerator) == FB_ACCEL_MATROX_MGAG100
? ACCESS_FBINFO(devflags.sgram) : 1;
 
#ifdef CONFIG_FB_MATROX_G450
if (ACCESS_FBINFO(devflags.g450dac)) {
ACCESS_FBINFO(outputs[0]).output = &g450out;
} else
#endif
{
ACCESS_FBINFO(outputs[0]).output = &m1064;
}
ACCESS_FBINFO(outputs[0]).src = MATROXFB_SRC_CRTC1;
ACCESS_FBINFO(outputs[0]).data = MINFO;
ACCESS_FBINFO(outputs[0]).mode = MATROXFB_OUTPUT_MODE_MONITOR;
 
if (ACCESS_FBINFO(devflags.g450dac)) {
/* we must do this always, BIOS does not do it for us
and accelerator dies without it */
mga_outl(0x1C0C, 0);
}
if (ACCESS_FBINFO(devflags.noinit))
return 0;
if (ACCESS_FBINFO(devflags.g450dac)) {
g450_preinit(PMINFO2);
return 0;
}
hw->MXoptionReg &= 0xC0000100;
hw->MXoptionReg |= 0x00000020;
if (ACCESS_FBINFO(devflags.novga))
hw->MXoptionReg &= ~0x00000100;
if (ACCESS_FBINFO(devflags.nobios))
hw->MXoptionReg &= ~0x40000000;
if (ACCESS_FBINFO(devflags.nopciretry))
hw->MXoptionReg |= 0x20000000;
pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg);
DAC1064_setmclk(PMINFO DAC1064_OPT_MDIV2 | DAC1064_OPT_GDIV3 | DAC1064_OPT_SCLK_PCI, 133333);
 
if (ACCESS_FBINFO(devflags.accelerator) == FB_ACCEL_MATROX_MGAG100) {
pci_read_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION2_REG, &reg50);
reg50 &= ~0x3000;
pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION2_REG, reg50);
 
hw->MXoptionReg |= 0x1080;
pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg);
mga_outl(M_CTLWTST, ACCESS_FBINFO(values).reg.mctlwtst);
udelay(100);
mga_outb(0x1C05, 0x00);
mga_outb(0x1C05, 0x80);
udelay(100);
mga_outb(0x1C05, 0x40);
mga_outb(0x1C05, 0xC0);
udelay(100);
reg50 &= ~0xFF;
reg50 |= 0x07;
pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION2_REG, reg50);
/* it should help with G100 */
mga_outb(M_GRAPHICS_INDEX, 6);
mga_outb(M_GRAPHICS_DATA, (mga_inb(M_GRAPHICS_DATA) & 3) | 4);
mga_setr(M_EXTVGA_INDEX, 0x03, 0x81);
mga_setr(M_EXTVGA_INDEX, 0x04, 0x00);
mga_writeb(ACCESS_FBINFO(video.vbase), 0x0000, 0xAA);
mga_writeb(ACCESS_FBINFO(video.vbase), 0x0800, 0x55);
mga_writeb(ACCESS_FBINFO(video.vbase), 0x4000, 0x55);
#if 0
if (mga_readb(ACCESS_FBINFO(video.vbase), 0x0000) != 0xAA) {
hw->MXoptionReg &= ~0x1000;
}
#endif
hw->MXoptionReg |= 0x00078020;
} else if (ACCESS_FBINFO(devflags.accelerator) == FB_ACCEL_MATROX_MGAG200) {
pci_read_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION2_REG, &reg50);
reg50 &= ~0x3000;
pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION2_REG, reg50);
 
if (ACCESS_FBINFO(devflags.memtype) == -1)
hw->MXoptionReg |= ACCESS_FBINFO(values).reg.opt & 0x1C00;
else
hw->MXoptionReg |= (ACCESS_FBINFO(devflags.memtype) & 7) << 10;
if (ACCESS_FBINFO(devflags.sgram))
hw->MXoptionReg |= 0x4000;
mga_outl(M_CTLWTST, ACCESS_FBINFO(values).reg.mctlwtst);
mga_outl(M_MEMRDBK, ACCESS_FBINFO(values).reg.memrdbk);
udelay(200);
mga_outl(M_MACCESS, 0x00000000);
mga_outl(M_MACCESS, 0x00008000);
udelay(100);
mga_outw(M_MEMRDBK, ACCESS_FBINFO(values).reg.memrdbk);
hw->MXoptionReg |= 0x00078020;
} else {
pci_read_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION2_REG, &reg50);
reg50 &= ~0x00000100;
reg50 |= 0x00000000;
pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION2_REG, reg50);
 
if (ACCESS_FBINFO(devflags.memtype) == -1)
hw->MXoptionReg |= ACCESS_FBINFO(values).reg.opt & 0x1C00;
else
hw->MXoptionReg |= (ACCESS_FBINFO(devflags.memtype) & 7) << 10;
if (ACCESS_FBINFO(devflags.sgram))
hw->MXoptionReg |= 0x4000;
mga_outl(M_CTLWTST, ACCESS_FBINFO(values).reg.mctlwtst);
mga_outl(M_MEMRDBK, ACCESS_FBINFO(values).reg.memrdbk);
udelay(200);
mga_outl(M_MACCESS, 0x00000000);
mga_outl(M_MACCESS, 0x00008000);
udelay(100);
mga_outl(M_MEMRDBK, ACCESS_FBINFO(values).reg.memrdbk);
hw->MXoptionReg |= 0x00040020;
}
pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg);
return 0;
}
 
static void MGAG100_reset(WPMINFO2) {
u_int8_t b;
struct matrox_hw_state* hw = &ACCESS_FBINFO(hw);
 
DBG(__FUNCTION__)
 
{
#ifdef G100_BROKEN_IBM_82351
u_int32_t d;
 
find 1014/22 (IBM/82351); /* if found and bridging Matrox, do some strange stuff */
pci_read_config_byte(ibm, PCI_SECONDARY_BUS, &b);
if (b == ACCESS_FBINFO(pcidev)->bus->number) {
pci_write_config_byte(ibm, PCI_COMMAND+1, 0); /* disable back-to-back & SERR */
pci_write_config_byte(ibm, 0x41, 0xF4); /* ??? */
pci_write_config_byte(ibm, PCI_IO_BASE, 0xF0); /* ??? */
pci_write_config_byte(ibm, PCI_IO_LIMIT, 0x00); /* ??? */
}
#endif
if (!ACCESS_FBINFO(devflags.noinit)) {
if (x7AF4 & 8) {
hw->MXoptionReg |= 0x40; /* FIXME... */
pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg);
}
mga_setr(M_EXTVGA_INDEX, 0x06, 0x50);
}
}
if (ACCESS_FBINFO(devflags.g450dac)) {
/* either leave MCLK as is... or they were set in preinit */
hw->DACclk[3] = inDAC1064(PMINFO DAC1064_XSYSPLLM);
hw->DACclk[4] = inDAC1064(PMINFO DAC1064_XSYSPLLN);
hw->DACclk[5] = inDAC1064(PMINFO DAC1064_XSYSPLLP);
} else {
DAC1064_setmclk(PMINFO DAC1064_OPT_RESERVED | DAC1064_OPT_MDIV2 | DAC1064_OPT_GDIV1 | DAC1064_OPT_SCLK_PLL, 133333);
}
if (ACCESS_FBINFO(devflags.accelerator) == FB_ACCEL_MATROX_MGAG400) {
if (ACCESS_FBINFO(devflags.dfp_type) == -1) {
ACCESS_FBINFO(devflags.dfp_type) = inDAC1064(PMINFO 0x1F);
}
}
if (ACCESS_FBINFO(devflags.noinit))
return;
if (ACCESS_FBINFO(devflags.g450dac)) {
} else {
MGAG100_setPixClock(PMINFO 4, 25175);
MGAG100_setPixClock(PMINFO 5, 28322);
if (x7AF4 & 0x10) {
b = inDAC1064(PMINFO M1064_XGENIODATA) & ~1;
outDAC1064(PMINFO M1064_XGENIODATA, b);
b = inDAC1064(PMINFO M1064_XGENIOCTRL) | 1;
outDAC1064(PMINFO M1064_XGENIOCTRL, b);
}
}
}
#endif
 
#ifdef CONFIG_FB_MATROX_MYSTIQUE
static void MGA1064_restore(WPMINFO2) {
int i;
struct matrox_hw_state* hw = &ACCESS_FBINFO(hw);
 
CRITFLAGS
 
DBG(__FUNCTION__)
 
CRITBEGIN
 
pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg);
mga_outb(M_IEN, 0x00);
mga_outb(M_CACHEFLUSH, 0x00);
 
CRITEND
 
DAC1064_restore_1(PMINFO2);
matroxfb_vgaHWrestore(PMINFO2);
ACCESS_FBINFO(crtc1.panpos) = -1;
for (i = 0; i < 6; i++)
mga_setr(M_EXTVGA_INDEX, i, hw->CRTCEXT[i]);
DAC1064_restore_2(PMINFO2);
}
#endif
 
#ifdef CONFIG_FB_MATROX_G100
static void MGAG100_restore(WPMINFO2) {
int i;
struct matrox_hw_state* hw = &ACCESS_FBINFO(hw);
 
CRITFLAGS
 
DBG(__FUNCTION__)
 
CRITBEGIN
 
pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg);
CRITEND
 
DAC1064_restore_1(PMINFO2);
matroxfb_vgaHWrestore(PMINFO2);
#ifdef CONFIG_FB_MATROX_32MB
if (ACCESS_FBINFO(devflags.support32MB))
mga_setr(M_EXTVGA_INDEX, 8, hw->CRTCEXT[8]);
#endif
ACCESS_FBINFO(crtc1.panpos) = -1;
for (i = 0; i < 6; i++)
mga_setr(M_EXTVGA_INDEX, i, hw->CRTCEXT[i]);
DAC1064_restore_2(PMINFO2);
}
#endif
 
#ifdef CONFIG_FB_MATROX_MYSTIQUE
struct matrox_switch matrox_mystique = {
MGA1064_preinit, MGA1064_reset, MGA1064_init, MGA1064_restore,
};
EXPORT_SYMBOL(matrox_mystique);
#endif
 
#ifdef CONFIG_FB_MATROX_G100
struct matrox_switch matrox_G100 = {
MGAG100_preinit, MGAG100_reset, MGAG100_init, MGAG100_restore,
};
EXPORT_SYMBOL(matrox_G100);
#endif
 
#ifdef NEED_DAC1064
EXPORT_SYMBOL(DAC1064_global_init);
EXPORT_SYMBOL(DAC1064_global_restore);
#endif
MODULE_LICENSE("GPL");
/shark/trunk/drivers/fb/matrox/matroxfb_maven.c
0,0 → 1,1329
/*
*
* Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200, G400 and G450.
*
* (c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz>
*
* Portions Copyright (c) 2001 Matrox Graphics Inc.
*
* Version: 1.65 2002/08/14
*
* See matroxfb_base.c for contributors.
*
*/
 
#include <linuxcomp.h>
 
#include "matroxfb_maven.h"
#include "matroxfb_misc.h"
#include "matroxfb_DAC1064.h"
#include <linux/i2c.h>
#include <linux/matroxfb.h>
#include <asm/div64.h>
#include <asm/uaccess.h>
 
#define MAVEN_I2CID (0x1B)
 
#define MGATVO_B 1
#define MGATVO_C 2
 
static const struct maven_gamma {
unsigned char reg83;
unsigned char reg84;
unsigned char reg85;
unsigned char reg86;
unsigned char reg87;
unsigned char reg88;
unsigned char reg89;
unsigned char reg8a;
unsigned char reg8b;
} maven_gamma[] = {
{ 131, 57, 223, 15, 117, 212, 251, 91, 156},
{ 133, 61, 128, 63, 180, 147, 195, 100, 180},
{ 131, 19, 63, 31, 50, 66, 171, 64, 176},
{ 0, 0, 0, 31, 16, 16, 16, 100, 200},
{ 8, 23, 47, 73, 147, 244, 220, 80, 195},
{ 22, 43, 64, 80, 147, 115, 58, 85, 168},
{ 34, 60, 80, 214, 147, 212, 188, 85, 167},
{ 45, 77, 96, 216, 147, 99, 91, 85, 159},
{ 56, 76, 112, 107, 147, 212, 148, 64, 144},
{ 65, 91, 128, 137, 147, 196, 17, 69, 148},
{ 72, 104, 136, 138, 147, 180, 245, 73, 147},
{ 87, 116, 143, 126, 16, 83, 229, 77, 144},
{ 95, 119, 152, 254, 244, 83, 221, 77, 151},
{ 100, 129, 159, 156, 244, 148, 197, 77, 160},
{ 105, 141, 167, 247, 244, 132, 181, 84, 166},
{ 105, 147, 168, 247, 244, 245, 181, 90, 170},
{ 120, 153, 175, 248, 212, 229, 165, 90, 180},
{ 119, 156, 176, 248, 244, 229, 84, 74, 160},
{ 119, 158, 183, 248, 244, 229, 149, 78, 165}
};
 
/* Definition of the various controls */
struct mctl {
struct v4l2_queryctrl desc;
size_t control;
};
 
#define BLMIN 0x0FF
#define WLMAX 0x3FF
 
static const struct mctl maven_controls[] =
{ { { V4L2_CID_BRIGHTNESS, V4L2_CTRL_TYPE_INTEGER,
"brightness",
0, WLMAX - BLMIN, 1, 379 - BLMIN,
0,
}, offsetof(struct matrox_fb_info, altout.tvo_params.brightness) },
{ { V4L2_CID_CONTRAST, V4L2_CTRL_TYPE_INTEGER,
"contrast",
0, 1023, 1, 127,
0,
}, offsetof(struct matrox_fb_info, altout.tvo_params.contrast) },
{ { V4L2_CID_SATURATION, V4L2_CTRL_TYPE_INTEGER,
"saturation",
0, 255, 1, 155,
0,
}, offsetof(struct matrox_fb_info, altout.tvo_params.saturation) },
{ { V4L2_CID_HUE, V4L2_CTRL_TYPE_INTEGER,
"hue",
0, 255, 1, 0,
0,
}, offsetof(struct matrox_fb_info, altout.tvo_params.hue) },
{ { V4L2_CID_GAMMA, V4L2_CTRL_TYPE_INTEGER,
"gamma",
0, sizeof(maven_gamma)/sizeof(maven_gamma[0])-1, 1, 3,
0,
}, offsetof(struct matrox_fb_info, altout.tvo_params.gamma) },
{ { MATROXFB_CID_TESTOUT, V4L2_CTRL_TYPE_BOOLEAN,
"test output",
0, 1, 1, 0,
0,
}, offsetof(struct matrox_fb_info, altout.tvo_params.testout) },
{ { MATROXFB_CID_DEFLICKER, V4L2_CTRL_TYPE_INTEGER,
"deflicker mode",
0, 2, 1, 0,
0,
}, offsetof(struct matrox_fb_info, altout.tvo_params.deflicker) },
 
};
 
#define MAVCTRLS (sizeof(maven_controls)/sizeof(maven_controls[0]))
 
/* Return: positive number: id found
-EINVAL: id not found, return failure
-ENOENT: id not found, create fake disabled control */
static int get_ctrl_id(__u32 v4l2_id) {
int i;
 
for (i = 0; i < MAVCTRLS; i++) {
if (v4l2_id < maven_controls[i].desc.id) {
if (maven_controls[i].desc.id == 0x08000000) {
return -EINVAL;
}
return -ENOENT;
}
if (v4l2_id == maven_controls[i].desc.id) {
return i;
}
}
return -EINVAL;
}
 
struct maven_data {
struct matrox_fb_info* primary_head;
struct i2c_client* client;
int version;
};
 
static int* get_ctrl_ptr(struct maven_data* md, int idx) {
return (int*)((char*)(md->primary_head) + maven_controls[idx].control);
}
 
static int maven_get_reg(struct i2c_client* c, char reg) {
char dst;
struct i2c_msg msgs[] = {{ c->addr, I2C_M_REV_DIR_ADDR, sizeof(reg), &reg },
{ c->addr, I2C_M_RD | I2C_M_NOSTART, sizeof(dst), &dst }};
s32 err;
 
err = i2c_transfer(c->adapter, msgs, 2);
if (err < 0)
printk(KERN_INFO "ReadReg(%d) failed\n", reg);
return dst & 0xFF;
}
 
static int maven_set_reg(struct i2c_client* c, int reg, int val) {
s32 err;
 
err = i2c_smbus_write_byte_data(c, reg, val);
if (err)
printk(KERN_INFO "WriteReg(%d) failed\n", reg);
return err;
}
 
static int maven_set_reg_pair(struct i2c_client* c, int reg, int val) {
s32 err;
 
err = i2c_smbus_write_word_data(c, reg, val);
if (err)
printk(KERN_INFO "WriteRegPair(%d) failed\n", reg);
return err;
}
 
static const struct matrox_pll_features maven_pll = {
50000,
27000,
4, 127,
2, 31,
3
};
 
struct matrox_pll_features2 {
unsigned int vco_freq_min;
unsigned int vco_freq_max;
unsigned int feed_div_min;
unsigned int feed_div_max;
unsigned int in_div_min;
unsigned int in_div_max;
unsigned int post_shift_max;
};
 
struct matrox_pll_ctl {
unsigned int ref_freq;
unsigned int den;
};
 
static const struct matrox_pll_features2 maven1000_pll = {
50000000,
300000000,
5, 128,
3, 32,
3
};
 
static const struct matrox_pll_ctl maven_PAL = {
540000,
50
};
 
static const struct matrox_pll_ctl maven_NTSC = {
450450, /* 27027000/60 == 27000000/59.94005994 */
60
};
 
static int matroxfb_PLL_mavenclock(const struct matrox_pll_features2* pll,
const struct matrox_pll_ctl* ctl,
unsigned int htotal, unsigned int vtotal,
unsigned int* in, unsigned int* feed, unsigned int* post,
unsigned int* h2) {
unsigned int besth2 = 0;
unsigned int fxtal = ctl->ref_freq;
unsigned int fmin = pll->vco_freq_min / ctl->den;
unsigned int fwant;
unsigned int p;
unsigned int scrlen;
unsigned int fmax;
 
DBG(__FUNCTION__)
 
scrlen = htotal * (vtotal - 1);
fwant = htotal * vtotal;
fmax = pll->vco_freq_max / ctl->den;
 
dprintk(KERN_DEBUG "want: %u, xtal: %u, h: %u, v: %u, fmax: %u\n",
fwant, fxtal, htotal, vtotal, fmax);
for (p = 1; p <= pll->post_shift_max; p++) {
if (fwant * 2 > fmax)
break;
fwant *= 2;
}
if (fwant > fmax)
return 0;
for (; p-- > 0; fwant >>= 1) {
unsigned int m;
 
if (fwant < fmin) break;
for (m = pll->in_div_min; m <= pll->in_div_max; m++) {
unsigned int n;
unsigned int dvd;
unsigned int ln;
 
n = (fwant * m) / fxtal;
if (n < pll->feed_div_min)
continue;
if (n > pll->feed_div_max)
break;
 
ln = fxtal * n;
dvd = m << p;
 
if (ln % dvd)
continue;
ln = ln / dvd;
 
if (ln < scrlen + 2)
continue;
ln = ln - scrlen;
if (ln > htotal)
continue;
dprintk(KERN_DEBUG "Match: %u / %u / %u / %u\n", n, m, p, ln);
if (ln > besth2) {
dprintk(KERN_DEBUG "Better...\n");
*h2 = besth2 = ln;
*post = p;
*in = m;
*feed = n;
}
}
}
if (besth2 < 2)
return 0;
dprintk(KERN_ERR "clk: %02X %02X %02X %d %d\n", *in, *feed, *post, fxtal, fwant);
return fxtal * (*feed) / (*in) * ctl->den;
}
 
static unsigned int matroxfb_mavenclock(const struct matrox_pll_ctl* ctl,
unsigned int htotal, unsigned int vtotal,
unsigned int* in, unsigned int* feed, unsigned int* post,
unsigned int* htotal2) {
unsigned int fvco;
unsigned int p;
 
fvco = matroxfb_PLL_mavenclock(&maven1000_pll, ctl, htotal, vtotal, in, feed, &p, htotal2);
if (!fvco)
return -EINVAL;
p = (1 << p) - 1;
if (fvco <= 100000000)
;
else if (fvco <= 140000000)
p |= 0x08;
else if (fvco <= 180000000)
p |= 0x10;
else
p |= 0x18;
*post = p;
return 0;
}
 
static void DAC1064_calcclock(unsigned int freq, unsigned int fmax,
unsigned int* in, unsigned int* feed, unsigned int* post) {
unsigned int fvco;
unsigned int p;
 
fvco = matroxfb_PLL_calcclock(&maven_pll, freq, fmax, in, feed, &p);
p = (1 << p) - 1;
if (fvco <= 100000)
;
else if (fvco <= 140000)
p |= 0x08;
else if (fvco <= 180000)
p |= 0x10;
else
p |= 0x18;
*post = p;
return;
}
 
static unsigned char maven_compute_deflicker (const struct maven_data* md) {
unsigned char df;
df = (md->version == MGATVO_B?0x40:0x00);
switch (md->primary_head->altout.tvo_params.deflicker) {
case 0:
/* df |= 0x00; */
break;
case 1:
df |= 0xB1;
break;
case 2:
df |= 0xA2;
break;
}
return df;
}
 
static void maven_compute_bwlevel (const struct maven_data* md,
int *bl, int *wl) {
int b = md->primary_head->altout.tvo_params.brightness + BLMIN;
int c = md->primary_head->altout.tvo_params.contrast;
 
*bl = max(b - c, BLMIN);
*wl = min(b + c, WLMAX);
}
 
static const struct maven_gamma* maven_compute_gamma (const struct maven_data* md) {
return maven_gamma + md->primary_head->altout.tvo_params.gamma;
}
 
 
static void maven_init_TVdata(const struct maven_data* md, struct mavenregs* data) {
static struct mavenregs palregs = { {
0x2A, 0x09, 0x8A, 0xCB, /* 00: chroma subcarrier */
0x00,
0x00, /* ? not written */
0x00, /* modified by code (F9 written...) */
0x00, /* ? not written */
0x7E, /* 08 */
0x44, /* 09 */
0x9C, /* 0A */
0x2E, /* 0B */
0x21, /* 0C */
0x00, /* ? not written */
0x3F, 0x03, /* 0E-0F */
0x3F, 0x03, /* 10-11 */
0x1A, /* 12 */
0x2A, /* 13 */
0x1C, 0x3D, 0x14, /* 14-16 */
0x9C, 0x01, /* 17-18 */
0x00, /* 19 */
0xFE, /* 1A */
0x7E, /* 1B */
0x60, /* 1C */
0x05, /* 1D */
0x89, 0x03, /* 1E-1F */
0x72, /* 20 */
0x07, /* 21 */
0x72, /* 22 */
0x00, /* 23 */
0x00, /* 24 */
0x00, /* 25 */
0x08, /* 26 */
0x04, /* 27 */
0x00, /* 28 */
0x1A, /* 29 */
0x55, 0x01, /* 2A-2B */
0x26, /* 2C */
0x07, 0x7E, /* 2D-2E */
0x02, 0x54, /* 2F-30 */
0xB0, 0x00, /* 31-32 */
0x14, /* 33 */
0x49, /* 34 */
0x00, /* 35 written multiple times */
0x00, /* 36 not written */
0xA3, /* 37 */
0xC8, /* 38 */
0x22, /* 39 */
0x02, /* 3A */
0x22, /* 3B */
0x3F, 0x03, /* 3C-3D */
0x00, /* 3E written multiple times */
0x00, /* 3F not written */
}, MATROXFB_OUTPUT_MODE_PAL, 625, 50 };
static struct mavenregs ntscregs = { {
0x21, 0xF0, 0x7C, 0x1F, /* 00: chroma subcarrier */
0x00,
0x00, /* ? not written */
0x00, /* modified by code (F9 written...) */
0x00, /* ? not written */
0x7E, /* 08 */
0x43, /* 09 */
0x7E, /* 0A */
0x3D, /* 0B */
0x00, /* 0C */
0x00, /* ? not written */
0x41, 0x00, /* 0E-0F */
0x3C, 0x00, /* 10-11 */
0x17, /* 12 */
0x21, /* 13 */
0x1B, 0x1B, 0x24, /* 14-16 */
0x83, 0x01, /* 17-18 */
0x00, /* 19 */
0x0F, /* 1A */
0x0F, /* 1B */
0x60, /* 1C */
0x05, /* 1D */
0x89, 0x02, /* 1E-1F */
0x5F, /* 20 */
0x04, /* 21 */
0x5F, /* 22 */
0x01, /* 23 */
0x02, /* 24 */
0x00, /* 25 */
0x0A, /* 26 */
0x05, /* 27 */
0x00, /* 28 */
0x10, /* 29 */
0xFF, 0x03, /* 2A-2B */
0x24, /* 2C */
0x0F, 0x78, /* 2D-2E */
0x00, 0x00, /* 2F-30 */
0xB2, 0x04, /* 31-32 */
0x14, /* 33 */
0x02, /* 34 */
0x00, /* 35 written multiple times */
0x00, /* 36 not written */
0xA3, /* 37 */
0xC8, /* 38 */
0x15, /* 39 */
0x05, /* 3A */
0x3B, /* 3B */
0x3C, 0x00, /* 3C-3D */
0x00, /* 3E written multiple times */
0x00, /* never written */
}, MATROXFB_OUTPUT_MODE_NTSC, 525, 60 };
MINFO_FROM(md->primary_head);
 
if (ACCESS_FBINFO(outputs[1]).mode == MATROXFB_OUTPUT_MODE_PAL)
*data = palregs;
else
*data = ntscregs;
 
/* Set deflicker */
data->regs[0x93] = maven_compute_deflicker(md);
/* set gamma */
{
const struct maven_gamma* g;
g = maven_compute_gamma(md);
data->regs[0x83] = g->reg83;
data->regs[0x84] = g->reg84;
data->regs[0x85] = g->reg85;
data->regs[0x86] = g->reg86;
data->regs[0x87] = g->reg87;
data->regs[0x88] = g->reg88;
data->regs[0x89] = g->reg89;
data->regs[0x8A] = g->reg8a;
data->regs[0x8B] = g->reg8b;
}
/* Set contrast / brightness */
{
int bl, wl;
maven_compute_bwlevel (md, &bl, &wl);
data->regs[0x0e] = bl >> 2;
data->regs[0x0f] = bl & 3;
data->regs[0x1e] = wl >> 2;
data->regs[0x1f] = wl & 3;
}
 
/* Set saturation */
{
data->regs[0x20] =
data->regs[0x22] = ACCESS_FBINFO(altout.tvo_params.saturation);
}
/* Set HUE */
data->regs[0x25] = ACCESS_FBINFO(altout.tvo_params.hue);
return;
}
 
#define LR(x) maven_set_reg(c, (x), m->regs[(x)])
#define LRP(x) maven_set_reg_pair(c, (x), m->regs[(x)] | (m->regs[(x)+1] << 8))
static void maven_init_TV(struct i2c_client* c, const struct mavenregs* m) {
int val;
 
 
maven_set_reg(c, 0x3E, 0x01);
maven_get_reg(c, 0x82); /* fetch oscillator state? */
maven_set_reg(c, 0x8C, 0x00);
maven_get_reg(c, 0x94); /* get 0x82 */
maven_set_reg(c, 0x94, 0xA2);
/* xmiscctrl */
 
maven_set_reg_pair(c, 0x8E, 0x1EFF);
maven_set_reg(c, 0xC6, 0x01);
 
/* removed code... */
 
maven_get_reg(c, 0x06);
maven_set_reg(c, 0x06, 0xF9); /* or read |= 0xF0 ? */
 
/* removed code here... */
 
/* real code begins here? */
/* chroma subcarrier */
LR(0x00); LR(0x01); LR(0x02); LR(0x03);
 
LR(0x04);
 
LR(0x2C);
LR(0x08);
LR(0x0A);
LR(0x09);
LR(0x29);
LRP(0x31);
LRP(0x17);
LR(0x0B);
LR(0x0C);
if (m->mode == MATROXFB_OUTPUT_MODE_PAL) {
maven_set_reg(c, 0x35, 0x10); /* ... */
} else {
maven_set_reg(c, 0x35, 0x0F); /* ... */
}
 
LRP(0x10);
 
LRP(0x0E);
LRP(0x1E);
 
LR(0x20); /* saturation #1 */
LR(0x22); /* saturation #2 */
LR(0x25); /* hue */
LR(0x34);
LR(0x33);
LR(0x19);
LR(0x12);
LR(0x3B);
LR(0x13);
LR(0x39);
LR(0x1D);
LR(0x3A);
LR(0x24);
LR(0x14);
LR(0x15);
LR(0x16);
LRP(0x2D);
LRP(0x2F);
LR(0x1A);
LR(0x1B);
LR(0x1C);
LR(0x23);
LR(0x26);
LR(0x28);
LR(0x27);
LR(0x21);
LRP(0x2A);
if (m->mode == MATROXFB_OUTPUT_MODE_PAL)
maven_set_reg(c, 0x35, 0x1D); /* ... */
else
maven_set_reg(c, 0x35, 0x1C);
 
LRP(0x3C);
LR(0x37);
LR(0x38);
maven_set_reg(c, 0xB3, 0x01);
 
maven_get_reg(c, 0xB0); /* read 0x80 */
maven_set_reg(c, 0xB0, 0x08); /* ugh... */
maven_get_reg(c, 0xB9); /* read 0x7C */
maven_set_reg(c, 0xB9, 0x78);
maven_get_reg(c, 0xBF); /* read 0x00 */
maven_set_reg(c, 0xBF, 0x02);
maven_get_reg(c, 0x94); /* read 0x82 */
maven_set_reg(c, 0x94, 0xB3);
 
LR(0x80); /* 04 1A 91 or 05 21 91 */
LR(0x81);
LR(0x82);
 
maven_set_reg(c, 0x8C, 0x20);
maven_get_reg(c, 0x8D);
maven_set_reg(c, 0x8D, 0x10);
 
LR(0x90); /* 4D 50 52 or 4E 05 45 */
LR(0x91);
LR(0x92);
 
LRP(0x9A); /* 0049 or 004F */
LRP(0x9C); /* 0004 or 0004 */
LRP(0x9E); /* 0458 or 045E */
LRP(0xA0); /* 05DA or 051B */
LRP(0xA2); /* 00CC or 00CF */
LRP(0xA4); /* 007D or 007F */
LRP(0xA6); /* 007C or 007E */
LRP(0xA8); /* 03CB or 03CE */
LRP(0x98); /* 0000 or 0000 */
LRP(0xAE); /* 0044 or 003A */
LRP(0x96); /* 05DA or 051B */
LRP(0xAA); /* 04BC or 046A */
LRP(0xAC); /* 004D or 004E */
 
LR(0xBE);
LR(0xC2);
 
maven_get_reg(c, 0x8D);
maven_set_reg(c, 0x8D, 0x04);
 
LR(0x20); /* saturation #1 */
LR(0x22); /* saturation #2 */
LR(0x93); /* whoops */
LR(0x20); /* oh, saturation #1 again */
LR(0x22); /* oh, saturation #2 again */
LR(0x25); /* hue */
LRP(0x0E);
LRP(0x1E);
LRP(0x0E); /* problems with memory? */
LRP(0x1E); /* yes, matrox must have problems in memory area... */
 
/* load gamma correction stuff */
LR(0x83);
LR(0x84);
LR(0x85);
LR(0x86);
LR(0x87);
LR(0x88);
LR(0x89);
LR(0x8A);
LR(0x8B);
 
val = maven_get_reg(c, 0x8D);
val &= 0x14; /* 0x10 or anything ored with it */
maven_set_reg(c, 0x8D, val);
 
LR(0x33);
LR(0x19);
LR(0x12);
LR(0x3B);
LR(0x13);
LR(0x39);
LR(0x1D);
LR(0x3A);
LR(0x24);
LR(0x14);
LR(0x15);
LR(0x16);
LRP(0x2D);
LRP(0x2F);
LR(0x1A);
LR(0x1B);
LR(0x1C);
LR(0x23);
LR(0x26);
LR(0x28);
LR(0x27);
LR(0x21);
LRP(0x2A);
if (m->mode == MATROXFB_OUTPUT_MODE_PAL)
maven_set_reg(c, 0x35, 0x1D);
else
maven_set_reg(c, 0x35, 0x1C);
LRP(0x3C);
LR(0x37);
LR(0x38);
 
maven_get_reg(c, 0xB0);
LR(0xB0); /* output mode */
LR(0x90);
LR(0xBE);
LR(0xC2);
 
LRP(0x9A);
LRP(0xA2);
LRP(0x9E);
LRP(0xA6);
LRP(0xAA);
LRP(0xAC);
maven_set_reg(c, 0x3E, 0x00);
maven_set_reg(c, 0x95, 0x20);
}
 
static int maven_find_exact_clocks(unsigned int ht, unsigned int vt,
struct mavenregs* m) {
unsigned int x;
unsigned int err = ~0;
 
/* 1:1 */
m->regs[0x80] = 0x0F;
m->regs[0x81] = 0x07;
m->regs[0x82] = 0x81;
 
for (x = 0; x < 8; x++) {
unsigned int a, b, c, h2;
unsigned int h = ht + 2 + x;
 
if (!matroxfb_mavenclock((m->mode == MATROXFB_OUTPUT_MODE_PAL) ? &maven_PAL : &maven_NTSC, h, vt, &a, &b, &c, &h2)) {
unsigned int diff = h - h2;
 
if (diff < err) {
err = diff;
m->regs[0x80] = a - 1;
m->regs[0x81] = b - 1;
m->regs[0x82] = c | 0x80;
m->hcorr = h2 - 2;
m->htotal = h - 2;
}
}
}
return err != ~0U;
}
 
static inline int maven_compute_timming(struct maven_data* md,
struct my_timming* mt,
struct mavenregs* m) {
unsigned int tmpi;
unsigned int a, bv, c;
MINFO_FROM(md->primary_head);
 
m->mode = ACCESS_FBINFO(outputs[1]).mode;
if (m->mode != MATROXFB_OUTPUT_MODE_MONITOR) {
unsigned int lmargin;
unsigned int umargin;
unsigned int vslen;
unsigned int hcrt;
unsigned int slen;
 
maven_init_TVdata(md, m);
 
if (maven_find_exact_clocks(mt->HTotal, mt->VTotal, m) == 0)
return -EINVAL;
 
lmargin = mt->HTotal - mt->HSyncEnd;
slen = mt->HSyncEnd - mt->HSyncStart;
hcrt = mt->HTotal - slen - mt->delay;
umargin = mt->VTotal - mt->VSyncEnd;
vslen = mt->VSyncEnd - mt->VSyncStart;
 
if (m->hcorr < mt->HTotal)
hcrt += m->hcorr;
if (hcrt > mt->HTotal)
hcrt -= mt->HTotal;
if (hcrt + 2 > mt->HTotal)
hcrt = 0; /* or issue warning? */
 
/* last (first? middle?) line in picture can have different length */
/* hlen - 2 */
m->regs[0x96] = m->hcorr;
m->regs[0x97] = m->hcorr >> 8;
/* ... */
m->regs[0x98] = 0x00; m->regs[0x99] = 0x00;
/* hblanking end */
m->regs[0x9A] = lmargin; /* 100% */
m->regs[0x9B] = lmargin >> 8; /* 100% */
/* who knows */
m->regs[0x9C] = 0x04;
m->regs[0x9D] = 0x00;
/* htotal - 2 */
m->regs[0xA0] = m->htotal;
m->regs[0xA1] = m->htotal >> 8;
/* vblanking end */
m->regs[0xA2] = mt->VTotal - mt->VSyncStart - 1; /* stop vblanking */
m->regs[0xA3] = (mt->VTotal - mt->VSyncStart - 1) >> 8;
/* something end... [A6]+1..[A8] */
if (md->version == MGATVO_B) {
m->regs[0xA4] = 0x04;
m->regs[0xA5] = 0x00;
} else {
m->regs[0xA4] = 0x01;
m->regs[0xA5] = 0x00;
}
/* something start... 0..[A4]-1 */
m->regs[0xA6] = 0x00;
m->regs[0xA7] = 0x00;
/* vertical line count - 1 */
m->regs[0xA8] = mt->VTotal - 1;
m->regs[0xA9] = (mt->VTotal - 1) >> 8;
/* horizontal vidrst pos */
m->regs[0xAA] = hcrt; /* 0 <= hcrt <= htotal - 2 */
m->regs[0xAB] = hcrt >> 8;
/* vertical vidrst pos */
m->regs[0xAC] = mt->VTotal - 2;
m->regs[0xAD] = (mt->VTotal - 2) >> 8;
/* moves picture up/down and so on... */
m->regs[0xAE] = 0x01; /* Fix this... 0..VTotal */
m->regs[0xAF] = 0x00;
{
int hdec;
int hlen;
unsigned int ibmin = 4 + lmargin + mt->HDisplay;
unsigned int ib;
int i;
 
/* Verify! */
/* Where 94208 came from? */
if (mt->HTotal)
hdec = 94208 / (mt->HTotal);
else
hdec = 0x81;
if (hdec > 0x81)
hdec = 0x81;
if (hdec < 0x41)
hdec = 0x41;
hdec--;
hlen = 98304 - 128 - ((lmargin + mt->HDisplay - 8) * hdec);
if (hlen < 0)
hlen = 0;
hlen = hlen >> 8;
if (hlen > 0xFF)
hlen = 0xFF;
/* Now we have to compute input buffer length.
If you want any picture, it must be between
4 + lmargin + xres
and
94208 / hdec
If you want perfect picture even on the top
of screen, it must be also
0x3C0000 * i / hdec + Q - R / hdec
where
R Qmin Qmax
0x07000 0x5AE 0x5BF
0x08000 0x5CF 0x5FF
0x0C000 0x653 0x67F
0x10000 0x6F8 0x6FF
*/
i = 1;
do {
ib = ((0x3C0000 * i - 0x8000)/ hdec + 0x05E7) >> 8;
i++;
} while (ib < ibmin);
if (ib >= m->htotal + 2) {
ib = ibmin;
}
 
m->regs[0x90] = hdec; /* < 0x40 || > 0x80 is bad... 0x80 is questionable */
m->regs[0xC2] = hlen;
/* 'valid' input line length */
m->regs[0x9E] = ib;
m->regs[0x9F] = ib >> 8;
}
{
int vdec;
int vlen;
 
#define MATROX_USE64BIT_DIVIDE
if (mt->VTotal) {
#ifdef MATROX_USE64BIT_DIVIDE
u64 f1;
u32 a;
u32 b;
 
a = m->vlines * (m->htotal + 2);
b = (mt->VTotal - 1) * (m->htotal + 2) + m->hcorr + 2;
 
f1 = ((u64)a) << 15; /* *32768 */
do_div(f1, b);
vdec = f1;
#else
vdec = m->vlines * 32768 / mt->VTotal;
#endif
} else
vdec = 0x8000;
if (vdec > 0x8000)
vdec = 0x8000;
vlen = (vslen + umargin + mt->VDisplay) * vdec;
vlen = (vlen >> 16) - 146; /* FIXME: 146?! */
if (vlen < 0)
vlen = 0;
if (vlen > 0xFF)
vlen = 0xFF;
vdec--;
m->regs[0x91] = vdec;
m->regs[0x92] = vdec >> 8;
m->regs[0xBE] = vlen;
}
m->regs[0xB0] = 0x08; /* output: SVideo/Composite */
return 0;
}
 
DAC1064_calcclock(mt->pixclock, 450000, &a, &bv, &c);
m->regs[0x80] = a;
m->regs[0x81] = bv;
m->regs[0x82] = c | 0x80;
 
m->regs[0xB3] = 0x01;
m->regs[0x94] = 0xB2;
 
/* htotal... */
m->regs[0x96] = mt->HTotal;
m->regs[0x97] = mt->HTotal >> 8;
/* ?? */
m->regs[0x98] = 0x00;
m->regs[0x99] = 0x00;
/* hsync len */
tmpi = mt->HSyncEnd - mt->HSyncStart;
m->regs[0x9A] = tmpi;
m->regs[0x9B] = tmpi >> 8;
/* hblank end */
tmpi = mt->HTotal - mt->HSyncStart;
m->regs[0x9C] = tmpi;
m->regs[0x9D] = tmpi >> 8;
/* hblank start */
tmpi += mt->HDisplay;
m->regs[0x9E] = tmpi;
m->regs[0x9F] = tmpi >> 8;
/* htotal + 1 */
tmpi = mt->HTotal + 1;
m->regs[0xA0] = tmpi;
m->regs[0xA1] = tmpi >> 8;
/* vsync?! */
tmpi = mt->VSyncEnd - mt->VSyncStart - 1;
m->regs[0xA2] = tmpi;
m->regs[0xA3] = tmpi >> 8;
/* ignored? */
tmpi = mt->VTotal - mt->VSyncStart;
m->regs[0xA4] = tmpi;
m->regs[0xA5] = tmpi >> 8;
/* ignored? */
tmpi = mt->VTotal - 1;
m->regs[0xA6] = tmpi;
m->regs[0xA7] = tmpi >> 8;
/* vtotal - 1 */
m->regs[0xA8] = tmpi;
m->regs[0xA9] = tmpi >> 8;
/* hor vidrst */
tmpi = mt->HTotal - mt->delay;
m->regs[0xAA] = tmpi;
m->regs[0xAB] = tmpi >> 8;
/* vert vidrst */
tmpi = mt->VTotal - 2;
m->regs[0xAC] = tmpi;
m->regs[0xAD] = tmpi >> 8;
/* ignored? */
m->regs[0xAE] = 0x00;
m->regs[0xAF] = 0x00;
 
m->regs[0xB0] = 0x03; /* output: monitor */
m->regs[0xB1] = 0xA0; /* ??? */
m->regs[0x8C] = 0x20; /* must be set... */
m->regs[0x8D] = 0x04; /* defaults to 0x10: test signal */
m->regs[0xB9] = 0x1A; /* defaults to 0x2C: too bright */
m->regs[0xBF] = 0x22; /* makes picture stable */
 
return 0;
}
 
static inline int maven_program_timming(struct maven_data* md,
const struct mavenregs* m) {
struct i2c_client* c = md->client;
 
if (m->mode == MATROXFB_OUTPUT_MODE_MONITOR) {
LR(0x80);
LR(0x81);
LR(0x82);
 
LR(0xB3);
LR(0x94);
 
LRP(0x96);
LRP(0x98);
LRP(0x9A);
LRP(0x9C);
LRP(0x9E);
LRP(0xA0);
LRP(0xA2);
LRP(0xA4);
LRP(0xA6);
LRP(0xA8);
LRP(0xAA);
LRP(0xAC);
LRP(0xAE);
 
LR(0xB0); /* output: monitor */
LR(0xB1); /* ??? */
LR(0x8C); /* must be set... */
LR(0x8D); /* defaults to 0x10: test signal */
LR(0xB9); /* defaults to 0x2C: too bright */
LR(0xBF); /* makes picture stable */
} else {
maven_init_TV(c, m);
}
return 0;
}
 
static inline int maven_resync(struct maven_data* md) {
struct i2c_client* c = md->client;
maven_set_reg(c, 0x95, 0x20); /* start whole thing */
return 0;
}
 
static int maven_get_queryctrl (struct maven_data* md,
struct v4l2_queryctrl *p) {
int i;
i = get_ctrl_id(p->id);
if (i >= 0) {
*p = maven_controls[i].desc;
return 0;
}
if (i == -ENOENT) {
static const struct v4l2_queryctrl disctrl =
{ .flags = V4L2_CTRL_FLAG_DISABLED };
i = p->id;
*p = disctrl;
p->id = i;
sprintf26(p->name, "Ctrl #%08X", i);
return 0;
}
return -EINVAL;
}
 
static int maven_set_control (struct maven_data* md,
struct v4l2_control *p) {
int i;
i = get_ctrl_id(p->id);
if (i < 0) return -EINVAL;
 
/*
* Check if changed.
*/
if (p->value == *get_ctrl_ptr(md, i)) return 0;
 
/*
* Check limits.
*/
if (p->value > maven_controls[i].desc.maximum) return -EINVAL;
if (p->value < maven_controls[i].desc.minimum) return -EINVAL;
 
/*
* Store new value.
*/
*get_ctrl_ptr(md, i) = p->value;
 
switch (p->id) {
case V4L2_CID_BRIGHTNESS:
case V4L2_CID_CONTRAST:
{
int blacklevel, whitelevel;
maven_compute_bwlevel(md, &blacklevel, &whitelevel);
blacklevel = (blacklevel >> 2) | ((blacklevel & 3) << 8);
whitelevel = (whitelevel >> 2) | ((whitelevel & 3) << 8);
maven_set_reg_pair(md->client, 0x0e, blacklevel);
maven_set_reg_pair(md->client, 0x1e, whitelevel);
}
break;
case V4L2_CID_SATURATION:
{
maven_set_reg(md->client, 0x20, p->value);
maven_set_reg(md->client, 0x22, p->value);
}
break;
case V4L2_CID_HUE:
{
maven_set_reg(md->client, 0x25, p->value);
}
break;
case V4L2_CID_GAMMA:
{
const struct maven_gamma* g;
g = maven_compute_gamma(md);
maven_set_reg(md->client, 0x83, g->reg83);
maven_set_reg(md->client, 0x84, g->reg84);
maven_set_reg(md->client, 0x85, g->reg85);
maven_set_reg(md->client, 0x86, g->reg86);
maven_set_reg(md->client, 0x87, g->reg87);
maven_set_reg(md->client, 0x88, g->reg88);
maven_set_reg(md->client, 0x89, g->reg89);
maven_set_reg(md->client, 0x8a, g->reg8a);
maven_set_reg(md->client, 0x8b, g->reg8b);
}
break;
case MATROXFB_CID_TESTOUT:
{
unsigned char val
= maven_get_reg (md->client,0x8d);
if (p->value) val |= 0x10;
else val &= ~0x10;
maven_set_reg (md->client, 0x8d, val);
}
break;
case MATROXFB_CID_DEFLICKER:
{
maven_set_reg(md->client, 0x93, maven_compute_deflicker(md));
}
break;
}
 
return 0;
}
 
static int maven_get_control (struct maven_data* md,
struct v4l2_control *p) {
int i;
i = get_ctrl_id(p->id);
if (i < 0) return -EINVAL;
p->value = *get_ctrl_ptr(md, i);
return 0;
}
 
/******************************************************/
 
static int maven_out_compute(void* md, struct my_timming* mt) {
#define mdinfo ((struct maven_data*)md)
#define minfo (mdinfo->primary_head)
return maven_compute_timming(md, mt, &ACCESS_FBINFO(hw).maven);
#undef minfo
#undef mdinfo
}
 
static int maven_out_program(void* md) {
#define mdinfo ((struct maven_data*)md)
#define minfo (mdinfo->primary_head)
return maven_program_timming(md, &ACCESS_FBINFO(hw).maven);
#undef minfo
#undef mdinfo
}
 
static int maven_out_start(void* md) {
return maven_resync(md);
}
 
static int maven_out_verify_mode(void* md, u_int32_t arg) {
switch (arg) {
case MATROXFB_OUTPUT_MODE_PAL:
case MATROXFB_OUTPUT_MODE_NTSC:
case MATROXFB_OUTPUT_MODE_MONITOR:
return 0;
}
return -EINVAL;
}
 
static int maven_out_get_queryctrl(void* md, struct v4l2_queryctrl* p) {
return maven_get_queryctrl(md, p);
}
 
static int maven_out_get_ctrl(void* md, struct v4l2_control* p) {
return maven_get_control(md, p);
}
 
static int maven_out_set_ctrl(void* md, struct v4l2_control* p) {
return maven_set_control(md, p);
}
 
static struct matrox_altout maven_altout = {
.name = "Secondary output",
.compute = maven_out_compute,
.program = maven_out_program,
.start = maven_out_start,
.verifymode = maven_out_verify_mode,
.getqueryctrl = maven_out_get_queryctrl,
.getctrl = maven_out_get_ctrl,
.setctrl = maven_out_set_ctrl,
};
 
static int maven_init_client(struct i2c_client* clnt) {
struct maven_data* md = i2c_get_clientdata(clnt);
MINFO_FROM(container_of(clnt->adapter, struct i2c_bit_adapter, adapter)->minfo);
 
md->primary_head = MINFO;
md->client = clnt;
down_write(&ACCESS_FBINFO(altout.lock));
ACCESS_FBINFO(outputs[1]).output = &maven_altout;
ACCESS_FBINFO(outputs[1]).src = MATROXFB_SRC_NONE;
ACCESS_FBINFO(outputs[1]).data = md;
ACCESS_FBINFO(outputs[1]).mode = MATROXFB_OUTPUT_MODE_MONITOR;
up_write(&ACCESS_FBINFO(altout.lock));
if (maven_get_reg(clnt, 0xB2) < 0x14) {
md->version = MGATVO_B;
/* Tweak some things for this old chip */
} else {
md->version = MGATVO_C;
}
/*
* Set all parameters to its initial values.
*/
{
unsigned int i;
 
for (i = 0; i < MAVCTRLS; ++i) {
*get_ctrl_ptr(md, i) = maven_controls[i].desc.default_value;
}
}
 
return 0;
}
 
static int maven_shutdown_client(struct i2c_client* clnt) {
struct maven_data* md = i2c_get_clientdata(clnt);
 
if (md->primary_head) {
MINFO_FROM(md->primary_head);
 
down_write(&ACCESS_FBINFO(altout.lock));
ACCESS_FBINFO(outputs[1]).src = MATROXFB_SRC_NONE;
ACCESS_FBINFO(outputs[1]).output = NULL;
ACCESS_FBINFO(outputs[1]).data = NULL;
ACCESS_FBINFO(outputs[1]).mode = MATROXFB_OUTPUT_MODE_MONITOR;
up_write(&ACCESS_FBINFO(altout.lock));
md->primary_head = NULL;
}
return 0;
}
 
static unsigned short normal_i2c[] = { MAVEN_I2CID, I2C_CLIENT_END };
static unsigned short normal_i2c_range[] = { MAVEN_I2CID, MAVEN_I2CID, I2C_CLIENT_END };
I2C_CLIENT_INSMOD;
 
static struct i2c_driver maven_driver;
 
static int maven_detect_client(struct i2c_adapter* adapter, int address, int kind) {
int err = 0;
struct i2c_client* new_client;
struct maven_data* data;
 
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_WORD_DATA |
I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_PROTOCOL_MANGLING))
goto ERROR0;
if (!(new_client = (struct i2c_client*)kmalloc(sizeof(*new_client) + sizeof(*data),
GFP_KERNEL))) {
err = -ENOMEM;
goto ERROR0;
}
data = (struct maven_data*)(new_client + 1);
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &maven_driver;
new_client->flags = 0;
strcpy(new_client->name, "maven client");
if ((err = i2c_attach_client(new_client)))
goto ERROR3;
err = maven_init_client(new_client);
if (err)
goto ERROR4;
return 0;
ERROR4:;
i2c_detach_client(new_client);
ERROR3:;
kfree(new_client);
ERROR0:;
return err;
}
 
static int maven_attach_adapter(struct i2c_adapter* adapter) {
if (adapter->id == (I2C_ALGO_BIT | I2C_HW_B_G400))
return i2c_probe(adapter, &addr_data, &maven_detect_client);
return 0;
}
 
static int maven_detach_client(struct i2c_client* client) {
int err;
 
if ((err = i2c_detach_client(client))) {
printk(KERN_ERR "maven: Cannot deregister client\n");
return err;
}
maven_shutdown_client(client);
kfree(client);
return 0;
}
 
static int maven_command(struct i2c_client* client, unsigned int cmd, void* arg) {
return -ENOIOCTLCMD; /* or -EINVAL, depends on who will call this */
}
 
static struct i2c_driver maven_driver={
.owner = THIS_MODULE,
.name = "maven",
.id = I2C_DRIVERID_MGATVO,
.flags = I2C_DF_NOTIFY,
.attach_adapter = maven_attach_adapter,
.detach_client = maven_detach_client,
.command = maven_command,
};
 
/* ************************** */
 
static int matroxfb_maven_init(void) {
int err;
 
err = i2c_add_driver(&maven_driver);
if (err) {
printk(KERN_ERR "maven: Maven driver failed to register (%d).\n", err);
return err;
}
return 0;
}
 
static void matroxfb_maven_exit(void) {
i2c_del_driver(&maven_driver);
}
 
MODULE_AUTHOR("(c) 1999-2002 Petr Vandrovec <vandrove@vc.cvut.cz>");
MODULE_DESCRIPTION("Matrox G200/G400 Matrox MGA-TVO driver");
MODULE_LICENSE("GPL");
module_init(matroxfb_maven_init);
module_exit(matroxfb_maven_exit);
/* we do not have __setup() yet */
/shark/trunk/drivers/fb/matrox/matroxfb_accel.c
0,0 → 1,507
/*
*
* Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200 and G400
*
* (c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz>
*
* Version: 1.65 2002/08/14
*
* MTRR stuff: 1998 Tom Rini <trini@kernel.crashing.org>
*
* Contributors: "menion?" <menion@mindless.com>
* Betatesting, fixes, ideas
*
* "Kurt Garloff" <garloff@suse.de>
* Betatesting, fixes, ideas, videomodes, videomodes timmings
*
* "Tom Rini" <trini@kernel.crashing.org>
* MTRR stuff, PPC cleanups, betatesting, fixes, ideas
*
* "Bibek Sahu" <scorpio@dodds.net>
* Access device through readb|w|l and write b|w|l
* Extensive debugging stuff
*
* "Daniel Haun" <haund@usa.net>
* Testing, hardware cursor fixes
*
* "Scott Wood" <sawst46+@pitt.edu>
* Fixes
*
* "Gerd Knorr" <kraxel@goldbach.isdn.cs.tu-berlin.de>
* Betatesting
*
* "Kelly French" <targon@hazmat.com>
* "Fernando Herrera" <fherrera@eurielec.etsit.upm.es>
* Betatesting, bug reporting
*
* "Pablo Bianucci" <pbian@pccp.com.ar>
* Fixes, ideas, betatesting
*
* "Inaky Perez Gonzalez" <inaky@peloncho.fis.ucm.es>
* Fixes, enhandcements, ideas, betatesting
*
* "Ryuichi Oikawa" <roikawa@rr.iiij4u.or.jp>
* PPC betatesting, PPC support, backward compatibility
*
* "Paul Womar" <Paul@pwomar.demon.co.uk>
* "Owen Waller" <O.Waller@ee.qub.ac.uk>
* PPC betatesting
*
* "Thomas Pornin" <pornin@bolet.ens.fr>
* Alpha betatesting
*
* "Pieter van Leuven" <pvl@iae.nl>
* "Ulf Jaenicke-Roessler" <ujr@physik.phy.tu-dresden.de>
* G100 testing
*
* "H. Peter Arvin" <hpa@transmeta.com>
* Ideas
*
* "Cort Dougan" <cort@cs.nmt.edu>
* CHRP fixes and PReP cleanup
*
* "Mark Vojkovich" <mvojkovi@ucsd.edu>
* G400 support
*
* (following author is not in any relation with this code, but his code
* is included in this driver)
*
* Based on framebuffer driver for VBE 2.0 compliant graphic boards
* (c) 1998 Gerd Knorr <kraxel@cs.tu-berlin.de>
*
* (following author is not in any relation with this code, but his ideas
* were used when writting this driver)
*
* FreeVBE/AF (Matrox), "Shawn Hargreaves" <shawn@talula.demon.co.uk>
*
*/
 
#include <linuxcomp.h>
 
#include "matroxfb_accel.h"
#include "matroxfb_DAC1064.h"
#include "matroxfb_Ti3026.h"
#include "matroxfb_misc.h"
 
#define curr_ydstorg(x) ACCESS_FBINFO2(x, curr.ydstorg.pixels)
 
#define mga_ydstlen(y,l) mga_outl(M_YDSTLEN | M_EXEC, ((y) << 16) | (l))
 
static inline void matrox_cfb4_pal(u_int32_t* pal) {
unsigned int i;
for (i = 0; i < 16; i++) {
pal[i] = i * 0x11111111U;
}
pal[i] = 0xFFFFFFFF;
}
 
static inline void matrox_cfb8_pal(u_int32_t* pal) {
unsigned int i;
for (i = 0; i < 16; i++) {
pal[i] = i * 0x01010101U;
}
pal[i] = 0x0F0F0F0F;
}
 
static void matroxfb_copyarea(struct fb_info* info, const struct fb_copyarea* area);
static void matroxfb_fillrect(struct fb_info* info, const struct fb_fillrect* rect);
static void matroxfb_imageblit(struct fb_info* info, const struct fb_image* image);
static void matroxfb_cfb4_fillrect(struct fb_info* info, const struct fb_fillrect* rect);
static void matroxfb_cfb4_copyarea(struct fb_info* info, const struct fb_copyarea* area);
 
void matrox_cfbX_init(WPMINFO2) {
u_int32_t maccess;
u_int32_t mpitch;
u_int32_t mopmode;
int accel;
 
DBG(__FUNCTION__)
 
mpitch = ACCESS_FBINFO(fbcon).var.xres_virtual;
 
ACCESS_FBINFO(fbops).fb_copyarea = cfb_copyarea;
ACCESS_FBINFO(fbops).fb_fillrect = cfb_fillrect;
ACCESS_FBINFO(fbops).fb_imageblit = cfb_imageblit;
ACCESS_FBINFO(fbops).fb_cursor = soft_cursor;
 
accel = (ACCESS_FBINFO(fbcon).var.accel_flags & FB_ACCELF_TEXT) == FB_ACCELF_TEXT;
 
switch (ACCESS_FBINFO(fbcon).var.bits_per_pixel) {
case 4: maccess = 0x00000000; /* accelerate as 8bpp video */
mpitch = (mpitch >> 1) | 0x8000; /* disable linearization */
mopmode = M_OPMODE_4BPP;
matrox_cfb4_pal(ACCESS_FBINFO(cmap));
if (accel && !(mpitch & 1)) {
ACCESS_FBINFO(fbops).fb_copyarea = matroxfb_cfb4_copyarea;
ACCESS_FBINFO(fbops).fb_fillrect = matroxfb_cfb4_fillrect;
}
break;
case 8: maccess = 0x00000000;
mopmode = M_OPMODE_8BPP;
matrox_cfb8_pal(ACCESS_FBINFO(cmap));
if (accel) {
ACCESS_FBINFO(fbops).fb_copyarea = matroxfb_copyarea;
ACCESS_FBINFO(fbops).fb_fillrect = matroxfb_fillrect;
ACCESS_FBINFO(fbops).fb_imageblit = matroxfb_imageblit;
}
break;
case 16: if (ACCESS_FBINFO(fbcon).var.green.length == 5) {
maccess = 0xC0000001;
ACCESS_FBINFO(cmap[16]) = 0x7FFF7FFF;
} else {
maccess = 0x40000001;
ACCESS_FBINFO(cmap[16]) = 0xFFFFFFFF;
}
mopmode = M_OPMODE_16BPP;
if (accel) {
ACCESS_FBINFO(fbops).fb_copyarea = matroxfb_copyarea;
ACCESS_FBINFO(fbops).fb_fillrect = matroxfb_fillrect;
ACCESS_FBINFO(fbops).fb_imageblit = matroxfb_imageblit;
}
break;
case 24: maccess = 0x00000003;
mopmode = M_OPMODE_24BPP;
ACCESS_FBINFO(cmap[16]) = 0xFFFFFFFF;
if (accel) {
ACCESS_FBINFO(fbops).fb_copyarea = matroxfb_copyarea;
ACCESS_FBINFO(fbops).fb_fillrect = matroxfb_fillrect;
ACCESS_FBINFO(fbops).fb_imageblit = matroxfb_imageblit;
}
break;
case 32: maccess = 0x00000002;
mopmode = M_OPMODE_32BPP;
ACCESS_FBINFO(cmap[16]) = 0xFFFFFFFF;
if (accel) {
ACCESS_FBINFO(fbops).fb_copyarea = matroxfb_copyarea;
ACCESS_FBINFO(fbops).fb_fillrect = matroxfb_fillrect;
ACCESS_FBINFO(fbops).fb_imageblit = matroxfb_imageblit;
}
break;
default: maccess = 0x00000000;
mopmode = 0x00000000;
break; /* turn off acceleration!!! */
}
mga_fifo(8);
mga_outl(M_PITCH, mpitch);
mga_outl(M_YDSTORG, curr_ydstorg(MINFO));
if (ACCESS_FBINFO(capable.plnwt))
mga_outl(M_PLNWT, -1);
if (ACCESS_FBINFO(capable.srcorg)) {
mga_outl(M_SRCORG, 0);
mga_outl(M_DSTORG, 0);
}
mga_outl(M_OPMODE, mopmode);
mga_outl(M_CXBNDRY, 0xFFFF0000);
mga_outl(M_YTOP, 0);
mga_outl(M_YBOT, 0x01FFFFFF);
mga_outl(M_MACCESS, maccess);
ACCESS_FBINFO(accel.m_dwg_rect) = M_DWG_TRAP | M_DWG_SOLID | M_DWG_ARZERO | M_DWG_SGNZERO | M_DWG_SHIFTZERO;
if (isMilleniumII(MINFO)) ACCESS_FBINFO(accel.m_dwg_rect) |= M_DWG_TRANSC;
ACCESS_FBINFO(accel.m_opmode) = mopmode;
}
 
EXPORT_SYMBOL(matrox_cfbX_init);
 
static void matrox_accel_bmove(WPMINFO int vxres, int sy, int sx, int dy, int dx, int height, int width) {
int start, end;
CRITFLAGS
 
DBG(__FUNCTION__)
 
CRITBEGIN
 
if ((dy < sy) || ((dy == sy) && (dx <= sx))) {
mga_fifo(2);
mga_outl(M_DWGCTL, M_DWG_BITBLT | M_DWG_SHIFTZERO | M_DWG_SGNZERO |
M_DWG_BFCOL | M_DWG_REPLACE);
mga_outl(M_AR5, vxres);
width--;
start = sy*vxres+sx+curr_ydstorg(MINFO);
end = start+width;
} else {
mga_fifo(3);
mga_outl(M_DWGCTL, M_DWG_BITBLT | M_DWG_SHIFTZERO | M_DWG_BFCOL | M_DWG_REPLACE);
mga_outl(M_SGN, 5);
mga_outl(M_AR5, -vxres);
width--;
end = (sy+height-1)*vxres+sx+curr_ydstorg(MINFO);
start = end+width;
dy += height-1;
}
mga_fifo(4);
mga_outl(M_AR0, end);
mga_outl(M_AR3, start);
mga_outl(M_FXBNDRY, ((dx+width)<<16) | dx);
mga_ydstlen(dy, height);
WaitTillIdle();
 
CRITEND
}
 
static void matrox_accel_bmove_lin(WPMINFO int vxres, int sy, int sx, int dy, int dx, int height, int width) {
int start, end;
CRITFLAGS
 
DBG(__FUNCTION__)
 
CRITBEGIN
 
if ((dy < sy) || ((dy == sy) && (dx <= sx))) {
mga_fifo(2);
mga_outl(M_DWGCTL, M_DWG_BITBLT | M_DWG_SHIFTZERO | M_DWG_SGNZERO |
M_DWG_BFCOL | M_DWG_REPLACE);
mga_outl(M_AR5, vxres);
width--;
start = sy*vxres+sx+curr_ydstorg(MINFO);
end = start+width;
} else {
mga_fifo(3);
mga_outl(M_DWGCTL, M_DWG_BITBLT | M_DWG_SHIFTZERO | M_DWG_BFCOL | M_DWG_REPLACE);
mga_outl(M_SGN, 5);
mga_outl(M_AR5, -vxres);
width--;
end = (sy+height-1)*vxres+sx+curr_ydstorg(MINFO);
start = end+width;
dy += height-1;
}
mga_fifo(5);
mga_outl(M_AR0, end);
mga_outl(M_AR3, start);
mga_outl(M_FXBNDRY, ((dx+width)<<16) | dx);
mga_outl(M_YDST, dy*vxres >> 5);
mga_outl(M_LEN | M_EXEC, height);
WaitTillIdle();
 
CRITEND
}
 
static void matroxfb_cfb4_copyarea(struct fb_info* info, const struct fb_copyarea* area) {
MINFO_FROM_INFO(info);
 
if ((area->sx | area->dx | area->width) & 1)
cfb_copyarea(info, area);
else
matrox_accel_bmove_lin(PMINFO ACCESS_FBINFO(fbcon.var.xres_virtual) >> 1, area->sy, area->sx >> 1, area->dy, area->dx >> 1, area->height, area->width >> 1);
}
 
static void matroxfb_copyarea(struct fb_info* info, const struct fb_copyarea* area) {
MINFO_FROM_INFO(info);
 
matrox_accel_bmove(PMINFO ACCESS_FBINFO(fbcon.var.xres_virtual), area->sy, area->sx, area->dy, area->dx, area->height, area->width);
}
 
static void matroxfb_accel_clear(WPMINFO u_int32_t color, int sy, int sx, int height,
int width) {
CRITFLAGS
 
DBG(__FUNCTION__)
 
CRITBEGIN
 
mga_fifo(5);
mga_outl(M_DWGCTL, ACCESS_FBINFO(accel.m_dwg_rect) | M_DWG_REPLACE);
mga_outl(M_FCOL, color);
mga_outl(M_FXBNDRY, ((sx + width) << 16) | sx);
mga_ydstlen(sy, height);
WaitTillIdle();
 
CRITEND
}
 
static void matroxfb_fillrect(struct fb_info* info, const struct fb_fillrect* rect) {
MINFO_FROM_INFO(info);
 
switch (rect->rop) {
case ROP_COPY:
matroxfb_accel_clear(PMINFO ((u_int32_t*)info->pseudo_palette)[rect->color], rect->dy, rect->dx, rect->height, rect->width);
break;
}
}
 
static void matroxfb_cfb4_clear(WPMINFO u_int32_t bgx, int sy, int sx, int height, int width) {
int whattodo;
CRITFLAGS
 
DBG(__FUNCTION__)
 
CRITBEGIN
 
whattodo = 0;
if (sx & 1) {
sx ++;
if (!width) return;
width --;
whattodo = 1;
}
if (width & 1) {
whattodo |= 2;
}
width >>= 1;
sx >>= 1;
if (width) {
mga_fifo(5);
mga_outl(M_DWGCTL, ACCESS_FBINFO(accel.m_dwg_rect) | M_DWG_REPLACE2);
mga_outl(M_FCOL, bgx);
mga_outl(M_FXBNDRY, ((sx + width) << 16) | sx);
mga_outl(M_YDST, sy * ACCESS_FBINFO(fbcon).var.xres_virtual >> 6);
mga_outl(M_LEN | M_EXEC, height);
WaitTillIdle();
}
if (whattodo) {
u_int32_t step = ACCESS_FBINFO(fbcon).var.xres_virtual >> 1;
vaddr_t vbase = ACCESS_FBINFO(video.vbase);
if (whattodo & 1) {
unsigned int uaddr = sy * step + sx - 1;
u_int32_t loop;
u_int8_t bgx2 = bgx & 0xF0;
for (loop = height; loop > 0; loop --) {
mga_writeb(vbase, uaddr, (mga_readb(vbase, uaddr) & 0x0F) | bgx2);
uaddr += step;
}
}
if (whattodo & 2) {
unsigned int uaddr = sy * step + sx + width;
u_int32_t loop;
u_int8_t bgx2 = bgx & 0x0F;
for (loop = height; loop > 0; loop --) {
mga_writeb(vbase, uaddr, (mga_readb(vbase, uaddr) & 0xF0) | bgx2);
uaddr += step;
}
}
}
 
CRITEND
}
 
static void matroxfb_cfb4_fillrect(struct fb_info* info, const struct fb_fillrect* rect) {
MINFO_FROM_INFO(info);
 
switch (rect->rop) {
case ROP_COPY:
matroxfb_cfb4_clear(PMINFO ((u_int32_t*)info->pseudo_palette)[rect->color], rect->dy, rect->dx, rect->height, rect->width);
break;
}
}
 
static void matroxfb_1bpp_imageblit(WPMINFO u_int32_t fgx, u_int32_t bgx,
const u_int8_t* chardata, int width, int height, int yy, int xx) {
u_int32_t step;
u_int32_t ydstlen;
u_int32_t xlen;
u_int32_t ar0;
u_int32_t charcell;
u_int32_t fxbndry;
vaddr_t mmio;
int easy;
CRITFLAGS
 
DBG_HEAVY(__FUNCTION__);
 
step = (width + 7) >> 3;
charcell = height * step;
xlen = (charcell + 3) & ~3;
ydstlen = (yy << 16) | height;
if (width == step << 3) {
ar0 = height * width - 1;
easy = 1;
} else {
ar0 = width - 1;
easy = 0;
}
 
CRITBEGIN
 
#ifdef __BIG_ENDIAN
WaitTillIdle();
mga_outl(M_OPMODE, M_OPMODE_8BPP);
#else
mga_fifo(3);
#endif
if (easy)
mga_outl(M_DWGCTL, M_DWG_ILOAD | M_DWG_SGNZERO | M_DWG_SHIFTZERO | M_DWG_BMONOWF | M_DWG_LINEAR | M_DWG_REPLACE);
else
mga_outl(M_DWGCTL, M_DWG_ILOAD | M_DWG_SGNZERO | M_DWG_SHIFTZERO | M_DWG_BMONOWF | M_DWG_REPLACE);
mga_outl(M_FCOL, fgx);
mga_outl(M_BCOL, bgx);
fxbndry = ((xx + width - 1) << 16) | xx;
mmio = ACCESS_FBINFO(mmio.vbase);
 
mga_fifo(6);
mga_writel(mmio, M_FXBNDRY, fxbndry);
mga_writel(mmio, M_AR0, ar0);
mga_writel(mmio, M_AR3, 0);
if (easy) {
mga_writel(mmio, M_YDSTLEN | M_EXEC, ydstlen);
mga_memcpy_toio(mmio, 0, chardata, xlen);
} else {
mga_writel(mmio, M_AR5, 0);
mga_writel(mmio, M_YDSTLEN | M_EXEC, ydstlen);
if ((step & 3) == 0) {
/* Great. Source has 32bit aligned lines, so we can feed them
directly to the accelerator. */
mga_memcpy_toio(mmio, 0, chardata, charcell);
} else if (step == 1) {
/* Special case for 1..8bit widths */
while (height--) {
#ifdef __LITTLE_ENDIAN
mga_writel(mmio, 0, *chardata);
#else
mga_writel(mmio, 0, (*chardata) << 24);
#endif
chardata++;
}
} else if (step == 2) {
/* Special case for 9..15bit widths */
while (height--) {
#ifdef __LITTLE_ENDIAN
mga_writel(mmio, 0, *(u_int16_t*)chardata);
#else
mga_writel(mmio, 0, (*(u_int16_t*)chardata) << 16);
#endif
chardata += 2;
}
} else {
/* Tell... well, why bother... */
while (height--) {
size_t i;
for (i = 0; i < step; i += 4) {
/* Hope that there are at least three readable bytes beyond the end of bitmap */
mga_writel(mmio, 0, get_unaligned((u_int32_t*)(chardata + i)));
}
chardata += step;
}
}
}
WaitTillIdle();
#ifdef __BIG_ENDIAN
mga_outl(M_OPMODE, ACCESS_FBINFO(accel.m_opmode));
#endif
CRITEND
}
 
 
static void matroxfb_imageblit(struct fb_info* info, const struct fb_image* image) {
MINFO_FROM_INFO(info);
 
DBG_HEAVY(__FUNCTION__);
 
if (image->depth == 0) {
u_int32_t fgx, bgx;
 
fgx = ((u_int32_t*)info->pseudo_palette)[image->fg_color];
bgx = ((u_int32_t*)info->pseudo_palette)[image->bg_color];
matroxfb_1bpp_imageblit(PMINFO fgx, bgx, image->data, image->width, image->height, image->dy, image->dx);
} else {
/* Danger! image->depth is useless: logo painting code always
passes framebuffer color depth here, although logo data are
always 8bpp and info->pseudo_palette is changed to contain
logo palette to be used (but only for true/direct-color... sic...).
So do it completely in software... */
cfb_imageblit(info, image);
}
}
 
MODULE_LICENSE("GPL");
/shark/trunk/drivers/fb/matrox/matroxfb_DAC1064.h
0,0 → 1,164
#ifndef __MATROXFB_DAC1064_H__
#define __MATROXFB_DAC1064_H__
 
/* make checkconfig does not walk through include tree */
#include <linux/config.h>
 
#include "matroxfb_base.h"
 
#ifdef CONFIG_FB_MATROX_MYSTIQUE
extern struct matrox_switch matrox_mystique;
#endif
#ifdef CONFIG_FB_MATROX_G100
extern struct matrox_switch matrox_G100;
#endif
#ifdef NEED_DAC1064
void DAC1064_global_init(WPMINFO2);
void DAC1064_global_restore(WPMINFO2);
#endif
 
#define M1064_INDEX 0x00
#define M1064_PALWRADD 0x00
#define M1064_PALDATA 0x01
#define M1064_PIXRDMSK 0x02
#define M1064_PALRDADD 0x03
#define M1064_X_DATAREG 0x0A
#define M1064_CURPOSXL 0x0C /* can be accessed as DWORD */
#define M1064_CURPOSXH 0x0D
#define M1064_CURPOSYL 0x0E
#define M1064_CURPOSYH 0x0F
 
#define M1064_XCURADDL 0x04
#define M1064_XCURADDH 0x05
#define M1064_XCURCTRL 0x06
#define M1064_XCURCTRL_DIS 0x00 /* transparent, transparent, transparent, transparent */
#define M1064_XCURCTRL_3COLOR 0x01 /* transparent, 0, 1, 2 */
#define M1064_XCURCTRL_XGA 0x02 /* 0, 1, transparent, complement */
#define M1064_XCURCTRL_XWIN 0x03 /* transparent, transparent, 0, 1 */
#define M1064_XCURCOL0RED 0x08
#define M1064_XCURCOL0GREEN 0x09
#define M1064_XCURCOL0BLUE 0x0A
#define M1064_XCURCOL1RED 0x0C
#define M1064_XCURCOL1GREEN 0x0D
#define M1064_XCURCOL1BLUE 0x0E
#define M1064_XCURCOL2RED 0x10
#define M1064_XCURCOL2GREEN 0x11
#define M1064_XCURCOL2BLUE 0x12
#define DAC1064_XVREFCTRL 0x18
#define DAC1064_XVREFCTRL_INTERNAL 0x3F
#define DAC1064_XVREFCTRL_EXTERNAL 0x00
#define DAC1064_XVREFCTRL_G100_DEFAULT 0x03
#define M1064_XMULCTRL 0x19
#define M1064_XMULCTRL_DEPTH_8BPP 0x00 /* 8 bpp paletized */
#define M1064_XMULCTRL_DEPTH_15BPP_1BPP 0x01 /* 15 bpp paletized + 1 bpp overlay */
#define M1064_XMULCTRL_DEPTH_16BPP 0x02 /* 16 bpp paletized */
#define M1064_XMULCTRL_DEPTH_24BPP 0x03 /* 24 bpp paletized */
#define M1064_XMULCTRL_DEPTH_24BPP_8BPP 0x04 /* 24 bpp direct + 8 bpp overlay paletized */
#define M1064_XMULCTRL_2G8V16 0x05 /* 15 bpp video direct, half xres, 8bpp paletized */
#define M1064_XMULCTRL_G16V16 0x06 /* 15 bpp video, 15bpp graphics, one of them paletized */
#define M1064_XMULCTRL_DEPTH_32BPP 0x07 /* 24 bpp paletized + 8 bpp unused */
#define M1064_XMULCTRL_GRAPHICS_PALETIZED 0x00
#define M1064_XMULCTRL_VIDEO_PALETIZED 0x08
#define M1064_XPIXCLKCTRL 0x1A
#define M1064_XPIXCLKCTRL_SRC_PCI 0x00
#define M1064_XPIXCLKCTRL_SRC_PLL 0x01
#define M1064_XPIXCLKCTRL_SRC_EXT 0x02
#define M1064_XPIXCLKCTRL_SRC_SYS 0x03 /* G200/G400 */
#define M1064_XPIXCLKCTRL_SRC_PLL2 0x03 /* G450 */
#define M1064_XPIXCLKCTRL_SRC_MASK 0x03
#define M1064_XPIXCLKCTRL_EN 0x00
#define M1064_XPIXCLKCTRL_DIS 0x04
#define M1064_XPIXCLKCTRL_PLL_DOWN 0x00
#define M1064_XPIXCLKCTRL_PLL_UP 0x08
#define M1064_XGENCTRL 0x1D
#define M1064_XGENCTRL_VS_0 0x00
#define M1064_XGENCTRL_VS_1 0x01
#define M1064_XGENCTRL_ALPHA_DIS 0x00
#define M1064_XGENCTRL_ALPHA_EN 0x02
#define M1064_XGENCTRL_BLACK_0IRE 0x00
#define M1064_XGENCTRL_BLACK_75IRE 0x10
#define M1064_XGENCTRL_SYNC_ON_GREEN 0x00
#define M1064_XGENCTRL_NO_SYNC_ON_GREEN 0x20
#define M1064_XGENCTRL_SYNC_ON_GREEN_MASK 0x20
#define M1064_XMISCCTRL 0x1E
#define M1064_XMISCCTRL_DAC_DIS 0x00
#define M1064_XMISCCTRL_DAC_EN 0x01
#define M1064_XMISCCTRL_MFC_VGA 0x00
#define M1064_XMISCCTRL_MFC_MAFC 0x02
#define M1064_XMISCCTRL_MFC_DIS 0x06
#define GX00_XMISCCTRL_MFC_MAFC 0x02
#define GX00_XMISCCTRL_MFC_PANELLINK 0x04
#define GX00_XMISCCTRL_MFC_DIS 0x06
#define GX00_XMISCCTRL_MFC_MASK 0x06
#define M1064_XMISCCTRL_DAC_6BIT 0x00
#define M1064_XMISCCTRL_DAC_8BIT 0x08
#define M1064_XMISCCTRL_DAC_WIDTHMASK 0x08
#define M1064_XMISCCTRL_LUT_DIS 0x00
#define M1064_XMISCCTRL_LUT_EN 0x10
#define G400_XMISCCTRL_VDO_MAFC12 0x00
#define G400_XMISCCTRL_VDO_BYPASS656 0x40
#define G400_XMISCCTRL_VDO_C2_MAFC12 0x80
#define G400_XMISCCTRL_VDO_C2_BYPASS656 0xC0
#define G400_XMISCCTRL_VDO_MASK 0xE0
#define M1064_XGENIOCTRL 0x2A
#define M1064_XGENIODATA 0x2B
#define DAC1064_XSYSPLLM 0x2C
#define DAC1064_XSYSPLLN 0x2D
#define DAC1064_XSYSPLLP 0x2E
#define DAC1064_XSYSPLLSTAT 0x2F
#define M1064_XZOOMCTRL 0x38
#define M1064_XZOOMCTRL_1 0x00
#define M1064_XZOOMCTRL_2 0x01
#define M1064_XZOOMCTRL_4 0x03
#define M1064_XSENSETEST 0x3A
#define M1064_XSENSETEST_BCOMP 0x01
#define M1064_XSENSETEST_GCOMP 0x02
#define M1064_XSENSETEST_RCOMP 0x04
#define M1064_XSENSETEST_PDOWN 0x00
#define M1064_XSENSETEST_PUP 0x80
#define M1064_XCRCREML 0x3C
#define M1064_XCRCREMH 0x3D
#define M1064_XCRCBITSEL 0x3E
#define M1064_XCOLKEYMASKL 0x40
#define M1064_XCOLKEYMASKH 0x41
#define M1064_XCOLKEYL 0x42
#define M1064_XCOLKEYH 0x43
#define M1064_XPIXPLLAM 0x44
#define M1064_XPIXPLLAN 0x45
#define M1064_XPIXPLLAP 0x46
#define M1064_XPIXPLLBM 0x48
#define M1064_XPIXPLLBN 0x49
#define M1064_XPIXPLLBP 0x4A
#define M1064_XPIXPLLCM 0x4C
#define M1064_XPIXPLLCN 0x4D
#define M1064_XPIXPLLCP 0x4E
#define M1064_XPIXPLLSTAT 0x4F
 
#define M1064_XTVO_IDX 0x87
#define M1064_XTVO_DATA 0x88
 
#define M1064_XOUTPUTCONN 0x8A
#define M1064_XSYNCCTRL 0x8B
#define M1064_XVIDPLLSTAT 0x8C
#define M1064_XVIDPLLP 0x8D
#define M1064_XVIDPLLM 0x8E
#define M1064_XVIDPLLN 0x8F
 
#define M1064_XPWRCTRL 0xA0
 
#define M1064_XPANMODE 0xA2
 
enum POS1064 {
POS1064_XCURADDL=0, POS1064_XCURADDH, POS1064_XCURCTRL,
POS1064_XCURCOL0RED, POS1064_XCURCOL0GREEN, POS1064_XCURCOL0BLUE,
POS1064_XCURCOL1RED, POS1064_XCURCOL1GREEN, POS1064_XCURCOL1BLUE,
POS1064_XCURCOL2RED, POS1064_XCURCOL2GREEN, POS1064_XCURCOL2BLUE,
POS1064_XVREFCTRL, POS1064_XMULCTRL, POS1064_XPIXCLKCTRL, POS1064_XGENCTRL,
POS1064_XMISCCTRL,
POS1064_XGENIOCTRL, POS1064_XGENIODATA, POS1064_XZOOMCTRL, POS1064_XSENSETEST,
POS1064_XCRCBITSEL,
POS1064_XCOLKEYMASKL, POS1064_XCOLKEYMASKH, POS1064_XCOLKEYL, POS1064_XCOLKEYH,
POS1064_XOUTPUTCONN, POS1064_XPANMODE, POS1064_XPWRCTRL };
 
 
#endif /* __MATROXFB_DAC1064_H__ */
/shark/trunk/drivers/fb/matrox/matroxfb_base.c
0,0 → 1,2519
/*
*
* Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200 and G400
*
* (c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz>
*
* Portions Copyright (c) 2001 Matrox Graphics Inc.
*
* Version: 1.65 2002/08/14
*
* MTRR stuff: 1998 Tom Rini <trini@kernel.crashing.org>
*
* Contributors: "menion?" <menion@mindless.com>
* Betatesting, fixes, ideas
*
* "Kurt Garloff" <garloff@suse.de>
* Betatesting, fixes, ideas, videomodes, videomodes timmings
*
* "Tom Rini" <trini@kernel.crashing.org>
* MTRR stuff, PPC cleanups, betatesting, fixes, ideas
*
* "Bibek Sahu" <scorpio@dodds.net>
* Access device through readb|w|l and write b|w|l
* Extensive debugging stuff
*
* "Daniel Haun" <haund@usa.net>
* Testing, hardware cursor fixes
*
* "Scott Wood" <sawst46+@pitt.edu>
* Fixes
*
* "Gerd Knorr" <kraxel@goldbach.isdn.cs.tu-berlin.de>
* Betatesting
*
* "Kelly French" <targon@hazmat.com>
* "Fernando Herrera" <fherrera@eurielec.etsit.upm.es>
* Betatesting, bug reporting
*
* "Pablo Bianucci" <pbian@pccp.com.ar>
* Fixes, ideas, betatesting
*
* "Inaky Perez Gonzalez" <inaky@peloncho.fis.ucm.es>
* Fixes, enhandcements, ideas, betatesting
*
* "Ryuichi Oikawa" <roikawa@rr.iiij4u.or.jp>
* PPC betatesting, PPC support, backward compatibility
*
* "Paul Womar" <Paul@pwomar.demon.co.uk>
* "Owen Waller" <O.Waller@ee.qub.ac.uk>
* PPC betatesting
*
* "Thomas Pornin" <pornin@bolet.ens.fr>
* Alpha betatesting
*
* "Pieter van Leuven" <pvl@iae.nl>
* "Ulf Jaenicke-Roessler" <ujr@physik.phy.tu-dresden.de>
* G100 testing
*
* "H. Peter Arvin" <hpa@transmeta.com>
* Ideas
*
* "Cort Dougan" <cort@cs.nmt.edu>
* CHRP fixes and PReP cleanup
*
* "Mark Vojkovich" <mvojkovi@ucsd.edu>
* G400 support
*
* "Samuel Hocevar" <sam@via.ecp.fr>
* Fixes
*
* "Anton Altaparmakov" <AntonA@bigfoot.com>
* G400 MAX/non-MAX distinction
*
* "Ken Aaker" <kdaaker@rchland.vnet.ibm.com>
* memtype extension (needed for GXT130P RS/6000 adapter)
*
* "Uns Lider" <unslider@miranda.org>
* G100 PLNWT fixes
*
* "Denis Zaitsev" <zzz@cd-club.ru>
* Fixes
*
* "Mike Pieper" <mike@pieper-family.de>
* TVOut enhandcements, V4L2 control interface.
*
* "Diego Biurrun" <diego@biurrun.de>
* DFP testing
*
* (following author is not in any relation with this code, but his code
* is included in this driver)
*
* Based on framebuffer driver for VBE 2.0 compliant graphic boards
* (c) 1998 Gerd Knorr <kraxel@cs.tu-berlin.de>
*
* (following author is not in any relation with this code, but his ideas
* were used when writting this driver)
*
* FreeVBE/AF (Matrox), "Shawn Hargreaves" <shawn@talula.demon.co.uk>
*
*/
 
/* make checkconfig does not check included files... */
 
#include <linuxcomp.h>
 
#include <linux/config.h>
#include <linux/version.h>
 
#include "matroxfb_base.h"
#include "matroxfb_misc.h"
#include "matroxfb_accel.h"
#include "matroxfb_DAC1064.h"
#include "matroxfb_Ti3026.h"
#include "matroxfb_maven.h"
#include "matroxfb_crtc2.h"
#include "matroxfb_g450.h"
#include <linux/matroxfb.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
 
#ifdef CONFIG_PPC_PMAC
unsigned char nvram_read_byte(int);
static int default_vmode = VMODE_NVRAM;
static int default_cmode = CMODE_NVRAM;
#endif
 
static void matroxfb_unregister_device(struct matrox_fb_info* minfo);
 
/* --------------------------------------------------------------------- */
 
/*
* card parameters
*/
 
/* --------------------------------------------------------------------- */
 
static struct fb_var_screeninfo vesafb_defined = {
640,480,640,480,/* W,H, W, H (virtual) load xres,xres_virtual*/
0,0, /* virtual -> visible no offset */
8, /* depth -> load bits_per_pixel */
0, /* greyscale ? */
{0,0,0}, /* R */
{0,0,0}, /* G */
{0,0,0}, /* B */
{0,0,0}, /* transparency */
0, /* standard pixel format */
FB_ACTIVATE_NOW,
-1,-1,
FB_ACCELF_TEXT, /* accel flags */
39721L,48L,16L,33L,10L,
96L,2L,~0, /* No sync info */
FB_VMODE_NONINTERLACED,
0, {0,0,0,0,0}
};
 
 
 
/* --------------------------------------------------------------------- */
static void update_crtc2(WPMINFO unsigned int pos) {
struct matroxfb_dh_fb_info* info = ACCESS_FBINFO(crtc2.info);
 
/* Make sure that displays are compatible */
if (info && (info->fbcon.var.bits_per_pixel == ACCESS_FBINFO(fbcon).var.bits_per_pixel)
&& (info->fbcon.var.xres_virtual == ACCESS_FBINFO(fbcon).var.xres_virtual)
&& (info->fbcon.var.green.length == ACCESS_FBINFO(fbcon).var.green.length)
) {
switch (ACCESS_FBINFO(fbcon).var.bits_per_pixel) {
case 16:
case 32:
pos = pos * 8;
if (info->interlaced) {
mga_outl(0x3C2C, pos);
mga_outl(0x3C28, pos + ACCESS_FBINFO(fbcon).var.xres_virtual * ACCESS_FBINFO(fbcon).var.bits_per_pixel / 8);
} else {
mga_outl(0x3C28, pos);
}
break;
}
}
}
 
static void matroxfb_crtc1_panpos(WPMINFO2) {
if (ACCESS_FBINFO(crtc1.panpos) >= 0) {
unsigned long flags;
int panpos;
 
matroxfb_DAC_lock_irqsave(flags);
panpos = ACCESS_FBINFO(crtc1.panpos);
if (panpos >= 0) {
unsigned int extvga_reg;
 
ACCESS_FBINFO(crtc1.panpos) = -1; /* No update pending anymore */
extvga_reg = mga_inb(M_EXTVGA_INDEX);
mga_setr(M_EXTVGA_INDEX, 0x00, panpos);
if (extvga_reg != 0x00) {
mga_outb(M_EXTVGA_INDEX, extvga_reg);
}
}
matroxfb_DAC_unlock_irqrestore(flags);
}
}
 
static irqreturn_t matrox_irq(int irq, void *dev_id, struct pt_regs *fp)
{
u_int32_t status;
int handled = 0;
 
MINFO_FROM(dev_id);
 
status = mga_inl(M_STATUS);
 
if (status & 0x20) {
mga_outl(M_ICLEAR, 0x20);
ACCESS_FBINFO(crtc1.vsync.cnt)++;
matroxfb_crtc1_panpos(PMINFO2);
//wake_up_interruptible(&ACCESS_FBINFO(crtc1.vsync.wait));
handled = 1;
}
if (status & 0x200) {
mga_outl(M_ICLEAR, 0x200);
ACCESS_FBINFO(crtc2.vsync.cnt)++;
//wake_up_interruptible(&ACCESS_FBINFO(crtc2.vsync.wait));
handled = 1;
}
return IRQ_RETVAL(handled);
}
 
int matroxfb_enable_irq(WPMINFO int reenable) {
u_int32_t bm;
if (ACCESS_FBINFO(devflags.accelerator) == FB_ACCEL_MATROX_MGAG400)
bm = 0x220;
else
bm = 0x020;
 
if (!test_and_set_bit(0, &ACCESS_FBINFO(irq_flags))) {
if (request_irq(ACCESS_FBINFO(pcidev)->irq, matrox_irq,
SA_SHIRQ, "matroxfb", MINFO)) {
clear_bit(0, &ACCESS_FBINFO(irq_flags));
return -EINVAL;
}
/* Clear any pending field interrupts */
mga_outl(M_ICLEAR, bm);
mga_outl(M_IEN, mga_inl(M_IEN) | bm);
} else if (reenable) {
u_int32_t ien;
ien = mga_inl(M_IEN);
if ((ien & bm) != bm) {
printk(KERN_DEBUG "matroxfb: someone disabled IRQ [%08X]\n", (int)ien);
mga_outl(M_IEN, ien | bm);
}
}
return 0;
}
 
static void matroxfb_disable_irq(WPMINFO2) {
if (test_and_clear_bit(0, &ACCESS_FBINFO(irq_flags))) {
/* Flush pending pan-at-vbl request... */
matroxfb_crtc1_panpos(PMINFO2);
if (ACCESS_FBINFO(devflags.accelerator) == FB_ACCEL_MATROX_MGAG400)
mga_outl(M_IEN, mga_inl(M_IEN) & ~0x220);
else
mga_outl(M_IEN, mga_inl(M_IEN) & ~0x20);
free_irq(ACCESS_FBINFO(pcidev)->irq, MINFO);
}
}
 
int matroxfb_wait_for_sync(WPMINFO u_int32_t crtc) {
wait_queue_t __wait;
struct matrox_vsync *vs;
unsigned int cnt;
int ret;
 
switch (crtc) {
case 0:
vs = &ACCESS_FBINFO(crtc1.vsync);
break;
case 1:
if (ACCESS_FBINFO(devflags.accelerator) != FB_ACCEL_MATROX_MGAG400) {
return -ENODEV;
}
vs = &ACCESS_FBINFO(crtc2.vsync);
break;
default:
return -ENODEV;
}
ret = matroxfb_enable_irq(PMINFO 0);
if (ret) {
return ret;
}
//init_waitqueue_entry(&__wait, current);
 
cnt = vs->cnt;
ret = 1;//ret = wait_event_interruptible_timeout(vs->wait, cnt != vs->cnt, HZ/10);
if (ret < 0) {
return ret;
}
if (ret == 0) {
matroxfb_enable_irq(PMINFO 1);
return -ETIMEDOUT;
}
return 0;
}
 
/* --------------------------------------------------------------------- */
 
static void matrox_pan_var(WPMINFO struct fb_var_screeninfo *var) {
unsigned int pos;
unsigned short p0, p1, p2;
#ifdef CONFIG_FB_MATROX_32MB
unsigned int p3;
#endif
int vbl;
unsigned long flags;
 
CRITFLAGS
 
DBG(__FUNCTION__)
 
if (ACCESS_FBINFO(dead))
return;
 
ACCESS_FBINFO(fbcon).var.xoffset = var->xoffset;
ACCESS_FBINFO(fbcon).var.yoffset = var->yoffset;
pos = (ACCESS_FBINFO(fbcon).var.yoffset * ACCESS_FBINFO(fbcon).var.xres_virtual + ACCESS_FBINFO(fbcon).var.xoffset) * ACCESS_FBINFO(curr.final_bppShift) / 32;
pos += ACCESS_FBINFO(curr.ydstorg.chunks);
p0 = ACCESS_FBINFO(hw).CRTC[0x0D] = pos & 0xFF;
p1 = ACCESS_FBINFO(hw).CRTC[0x0C] = (pos & 0xFF00) >> 8;
p2 = ACCESS_FBINFO(hw).CRTCEXT[0] = (ACCESS_FBINFO(hw).CRTCEXT[0] & 0xB0) | ((pos >> 16) & 0x0F) | ((pos >> 14) & 0x40);
#ifdef CONFIG_FB_MATROX_32MB
p3 = ACCESS_FBINFO(hw).CRTCEXT[8] = pos >> 21;
#endif
 
/* FB_ACTIVATE_VBL and we can acquire interrupts? Honor FB_ACTIVATE_VBL then... */
vbl = (var->activate & FB_ACTIVATE_VBL) && (matroxfb_enable_irq(PMINFO 0) == 0);
 
CRITBEGIN
 
matroxfb_DAC_lock_irqsave(flags);
mga_setr(M_CRTC_INDEX, 0x0D, p0);
mga_setr(M_CRTC_INDEX, 0x0C, p1);
#ifdef CONFIG_FB_MATROX_32MB
if (ACCESS_FBINFO(devflags.support32MB))
mga_setr(M_EXTVGA_INDEX, 0x08, p3);
#endif
if (vbl) {
ACCESS_FBINFO(crtc1.panpos) = p2;
} else {
/* Abort any pending change */
ACCESS_FBINFO(crtc1.panpos) = -1;
mga_setr(M_EXTVGA_INDEX, 0x00, p2);
}
matroxfb_DAC_unlock_irqrestore(flags);
update_crtc2(PMINFO pos);
 
CRITEND
}
 
static void matroxfb_remove(WPMINFO int dummy) {
/* Currently we are holding big kernel lock on all dead & usecount updates.
* Destroy everything after all users release it. Especially do not unregister
* framebuffer and iounmap memory, neither fbmem nor fbcon-cfb* does not check
* for device unplugged when in use.
* In future we should point mmio.vbase & video.vbase somewhere where we can
* write data without causing too much damage...
*/
 
ACCESS_FBINFO(dead) = 1;
if (ACCESS_FBINFO(usecount)) {
/* destroy it later */
return;
}
matroxfb_unregister_device(MINFO);
unregister_framebuffer(&ACCESS_FBINFO(fbcon));
matroxfb_g450_shutdown(PMINFO2);
#ifdef CONFIG_MTRR
if (ACCESS_FBINFO(mtrr.vram_valid))
mtrr_del(ACCESS_FBINFO(mtrr.vram), ACCESS_FBINFO(video.base), ACCESS_FBINFO(video.len));
#endif
mga_iounmap(ACCESS_FBINFO(mmio.vbase));
mga_iounmap(ACCESS_FBINFO(video.vbase));
release_mem_region(ACCESS_FBINFO(video.base), ACCESS_FBINFO(video.len_maximum));
release_mem_region(ACCESS_FBINFO(mmio.base), 16384);
#ifdef CONFIG_FB_MATROX_MULTIHEAD
kfree(minfo);
#endif
}
 
/*
* Open/Release the frame buffer device
*/
 
static int matroxfb_open(struct fb_info *info, int user)
{
MINFO_FROM_INFO(info);
DBG_LOOP(__FUNCTION__)
 
if (ACCESS_FBINFO(dead)) {
return -ENXIO;
}
ACCESS_FBINFO(usecount)++;
if (user) {
ACCESS_FBINFO(userusecount)++;
}
return(0);
}
 
static int matroxfb_release(struct fb_info *info, int user)
{
MINFO_FROM_INFO(info);
DBG_LOOP(__FUNCTION__)
 
if (user) {
if (0 == --ACCESS_FBINFO(userusecount)) {
matroxfb_disable_irq(PMINFO2);
}
}
if (!(--ACCESS_FBINFO(usecount)) && ACCESS_FBINFO(dead)) {
matroxfb_remove(PMINFO 0);
}
return(0);
}
 
static int matroxfb_pan_display(struct fb_var_screeninfo *var,
struct fb_info* info) {
MINFO_FROM_INFO(info);
 
DBG(__FUNCTION__)
 
matrox_pan_var(PMINFO var);
return 0;
}
 
static int matroxfb_get_final_bppShift(CPMINFO int bpp) {
int bppshft2;
 
DBG(__FUNCTION__)
 
bppshft2 = bpp;
if (!bppshft2) {
return 8;
}
if (isInterleave(MINFO))
bppshft2 >>= 1;
if (ACCESS_FBINFO(devflags.video64bits))
bppshft2 >>= 1;
return bppshft2;
}
 
static int matroxfb_test_and_set_rounding(CPMINFO int xres, int bpp) {
int over;
int rounding;
 
DBG(__FUNCTION__)
 
switch (bpp) {
case 0: return xres;
case 4: rounding = 128;
break;
case 8: rounding = 64; /* doc says 64; 32 is OK for G400 */
break;
case 16: rounding = 32;
break;
case 24: rounding = 64; /* doc says 64; 32 is OK for G400 */
break;
default: rounding = 16;
/* on G400, 16 really does not work */
if (ACCESS_FBINFO(devflags.accelerator) == FB_ACCEL_MATROX_MGAG400)
rounding = 32;
break;
}
if (isInterleave(MINFO)) {
rounding *= 2;
}
over = xres % rounding;
if (over)
xres += rounding-over;
return xres;
}
 
static int matroxfb_pitch_adjust(CPMINFO int xres, int bpp) {
const int* width;
int xres_new;
 
DBG(__FUNCTION__)
 
if (!bpp) return xres;
 
width = ACCESS_FBINFO(capable.vxres);
 
if (ACCESS_FBINFO(devflags.precise_width)) {
while (*width) {
if ((*width >= xres) && (matroxfb_test_and_set_rounding(PMINFO *width, bpp) == *width)) {
break;
}
width++;
}
xres_new = *width;
} else {
xres_new = matroxfb_test_and_set_rounding(PMINFO xres, bpp);
}
if (!xres_new) return 0;
if (xres != xres_new) {
printk(KERN_INFO "matroxfb: cannot set xres to %d, rounded up to %d\n", xres, xres_new);
}
return xres_new;
}
 
static int matroxfb_get_cmap_len(struct fb_var_screeninfo *var) {
 
DBG(__FUNCTION__)
 
switch (var->bits_per_pixel) {
case 4:
return 16; /* pseudocolor... 16 entries HW palette */
case 8:
return 256; /* pseudocolor... 256 entries HW palette */
case 16:
return 16; /* directcolor... 16 entries SW palette */
/* Mystique: truecolor, 16 entries SW palette, HW palette hardwired into 1:1 mapping */
case 24:
return 16; /* directcolor... 16 entries SW palette */
/* Mystique: truecolor, 16 entries SW palette, HW palette hardwired into 1:1 mapping */
case 32:
return 16; /* directcolor... 16 entries SW palette */
/* Mystique: truecolor, 16 entries SW palette, HW palette hardwired into 1:1 mapping */
}
return 16; /* return something reasonable... or panic()? */
}
 
static int matroxfb_decode_var(CPMINFO struct fb_var_screeninfo *var, int *visual, int *video_cmap_len, unsigned int* ydstorg) {
struct RGBT {
unsigned char bpp;
struct {
unsigned char offset,
length;
} red,
green,
blue,
transp;
signed char visual;
};
static const struct RGBT table[]= {
{ 8,{ 0,8},{0,8},{0,8},{ 0,0},MX_VISUAL_PSEUDOCOLOR},
{15,{10,5},{5,5},{0,5},{15,1},MX_VISUAL_DIRECTCOLOR},
{16,{11,5},{5,6},{0,5},{ 0,0},MX_VISUAL_DIRECTCOLOR},
{24,{16,8},{8,8},{0,8},{ 0,0},MX_VISUAL_DIRECTCOLOR},
{32,{16,8},{8,8},{0,8},{24,8},MX_VISUAL_DIRECTCOLOR}
};
struct RGBT const *rgbt;
unsigned int bpp = var->bits_per_pixel;
unsigned int vramlen;
unsigned int memlen;
 
DBG(__FUNCTION__)
 
switch (bpp) {
case 4: if (!ACCESS_FBINFO(capable.cfb4)) return -EINVAL;
break;
case 8: break;
case 16: break;
case 24: break;
case 32: break;
default: return -EINVAL;
}
*ydstorg = 0;
vramlen = ACCESS_FBINFO(video.len_usable);
if (var->yres_virtual < var->yres)
var->yres_virtual = var->yres;
if (var->xres_virtual < var->xres)
var->xres_virtual = var->xres;
 
var->xres_virtual = matroxfb_pitch_adjust(PMINFO var->xres_virtual, bpp);
memlen = var->xres_virtual * bpp * var->yres_virtual / 8;
if (memlen > vramlen) {
var->yres_virtual = vramlen * 8 / (var->xres_virtual * bpp);
memlen = var->xres_virtual * bpp * var->yres_virtual / 8;
}
/* There is hardware bug that no line can cross 4MB boundary */
/* give up for CFB24, it is impossible to easy workaround it */
/* for other try to do something */
if (!ACCESS_FBINFO(capable.cross4MB) && (memlen > 0x400000)) {
if (bpp == 24) {
/* sorry */
} else {
unsigned int linelen;
unsigned int m1 = linelen = var->xres_virtual * bpp / 8;
unsigned int m2 = PAGE_SIZE; /* or 128 if you do not need PAGE ALIGNED address */
unsigned int max_yres;
 
while (m1) {
int t;
 
while (m2 >= m1) m2 -= m1;
t = m1;
m1 = m2;
m2 = t;
}
m2 = linelen * PAGE_SIZE / m2;
*ydstorg = m2 = 0x400000 % m2;
max_yres = (vramlen - m2) / linelen;
if (var->yres_virtual > max_yres)
var->yres_virtual = max_yres;
}
}
/* YDSTLEN contains only signed 16bit value */
if (var->yres_virtual > 32767)
var->yres_virtual = 32767;
/* we must round yres/xres down, we already rounded y/xres_virtual up
if it was possible. We should return -EINVAL, but I disagree */
if (var->yres_virtual < var->yres)
var->yres = var->yres_virtual;
if (var->xres_virtual < var->xres)
var->xres = var->xres_virtual;
if (var->xoffset + var->xres > var->xres_virtual)
var->xoffset = var->xres_virtual - var->xres;
if (var->yoffset + var->yres > var->yres_virtual)
var->yoffset = var->yres_virtual - var->yres;
 
if (bpp == 16 && var->green.length == 5) {
bpp--; /* an artifical value - 15 */
}
 
for (rgbt = table; rgbt->bpp < bpp; rgbt++);
#define SETCLR(clr)\
var->clr.offset = rgbt->clr.offset;\
var->clr.length = rgbt->clr.length
SETCLR(red);
SETCLR(green);
SETCLR(blue);
SETCLR(transp);
#undef SETCLR
*visual = rgbt->visual;
 
if (bpp > 8)
dprintk("matroxfb: truecolor: "
"size=%d:%d:%d:%d, shift=%d:%d:%d:%d\n",
var->transp.length, var->red.length, var->green.length, var->blue.length,
var->transp.offset, var->red.offset, var->green.offset, var->blue.offset);
 
*video_cmap_len = matroxfb_get_cmap_len(var);
dprintk(KERN_INFO "requested %d*%d/%dbpp (%d*%d)\n", var->xres, var->yres, var->bits_per_pixel,
var->xres_virtual, var->yres_virtual);
return 0;
}
 
static int matroxfb_setcolreg(unsigned regno, unsigned red, unsigned green,
unsigned blue, unsigned transp,
struct fb_info *fb_info)
{
#ifdef CONFIG_FB_MATROX_MULTIHEAD
struct matrox_fb_info* minfo = container_of(fb_info, struct matrox_fb_info, fbcon);
#endif
 
DBG(__FUNCTION__)
 
/*
* Set a single color register. The values supplied are
* already rounded down to the hardware's capabilities
* (according to the entries in the `var' structure). Return
* != 0 for invalid regno.
*/
 
if (regno >= ACCESS_FBINFO(curr.cmap_len))
return 1;
 
if (ACCESS_FBINFO(fbcon).var.grayscale) {
/* gray = 0.30*R + 0.59*G + 0.11*B */
red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
}
 
red = CNVT_TOHW(red, ACCESS_FBINFO(fbcon).var.red.length);
green = CNVT_TOHW(green, ACCESS_FBINFO(fbcon).var.green.length);
blue = CNVT_TOHW(blue, ACCESS_FBINFO(fbcon).var.blue.length);
transp = CNVT_TOHW(transp, ACCESS_FBINFO(fbcon).var.transp.length);
 
switch (ACCESS_FBINFO(fbcon).var.bits_per_pixel) {
case 4:
case 8:
mga_outb(M_DAC_REG, regno);
mga_outb(M_DAC_VAL, red);
mga_outb(M_DAC_VAL, green);
mga_outb(M_DAC_VAL, blue);
break;
case 16:
{
u_int16_t col =
(red << ACCESS_FBINFO(fbcon).var.red.offset) |
(green << ACCESS_FBINFO(fbcon).var.green.offset) |
(blue << ACCESS_FBINFO(fbcon).var.blue.offset) |
(transp << ACCESS_FBINFO(fbcon).var.transp.offset); /* for 1:5:5:5 */
ACCESS_FBINFO(cmap[regno]) = col | (col << 16);
}
break;
case 24:
case 32:
ACCESS_FBINFO(cmap[regno]) =
(red << ACCESS_FBINFO(fbcon).var.red.offset) |
(green << ACCESS_FBINFO(fbcon).var.green.offset) |
(blue << ACCESS_FBINFO(fbcon).var.blue.offset) |
(transp << ACCESS_FBINFO(fbcon).var.transp.offset); /* 8:8:8:8 */
break;
}
return 0;
}
 
static void matroxfb_init_fix(WPMINFO2)
{
struct fb_fix_screeninfo *fix = &ACCESS_FBINFO(fbcon).fix;
DBG(__FUNCTION__)
 
strcpy(fix->id,"MATROX");
 
fix->xpanstep = 8; /* 8 for 8bpp, 4 for 16bpp, 2 for 32bpp */
fix->ypanstep = 1;
fix->ywrapstep = 0;
fix->mmio_start = ACCESS_FBINFO(mmio.base);
fix->mmio_len = ACCESS_FBINFO(mmio.len);
fix->accel = ACCESS_FBINFO(devflags.accelerator);
}
 
static void matroxfb_update_fix(WPMINFO2)
{
struct fb_fix_screeninfo *fix = &ACCESS_FBINFO(fbcon).fix;
DBG(__FUNCTION__)
 
fix->smem_start = ACCESS_FBINFO(video.base) + ACCESS_FBINFO(curr.ydstorg.bytes);
fix->smem_len = ACCESS_FBINFO(video.len_usable) - ACCESS_FBINFO(curr.ydstorg.bytes);
}
 
static int matroxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
int err;
int visual;
int cmap_len;
unsigned int ydstorg;
MINFO_FROM_INFO(info);
 
if (ACCESS_FBINFO(dead)) {
return -ENXIO;
}
if ((err = matroxfb_decode_var(PMINFO var, &visual, &cmap_len, &ydstorg)) != 0)
return err;
return 0;
}
 
static int matroxfb_set_par(struct fb_info *info)
{
int err;
int visual;
int cmap_len;
unsigned int ydstorg;
struct fb_var_screeninfo *var;
MINFO_FROM_INFO(info);
 
DBG(__FUNCTION__)
 
if (ACCESS_FBINFO(dead)) {
return -ENXIO;
}
 
var = &info->var;
if ((err = matroxfb_decode_var(PMINFO var, &visual, &cmap_len, &ydstorg)) != 0)
return err;
ACCESS_FBINFO(fbcon.screen_base) = vaddr_va(ACCESS_FBINFO(video.vbase)) + ydstorg;
matroxfb_update_fix(PMINFO2);
ACCESS_FBINFO(fbcon).fix.visual = visual;
ACCESS_FBINFO(fbcon).fix.type = FB_TYPE_PACKED_PIXELS;
ACCESS_FBINFO(fbcon).fix.type_aux = 0;
ACCESS_FBINFO(fbcon).fix.line_length = (var->xres_virtual * var->bits_per_pixel) >> 3;
{
unsigned int pos;
 
ACCESS_FBINFO(curr.cmap_len) = cmap_len;
ydstorg += ACCESS_FBINFO(devflags.ydstorg);
ACCESS_FBINFO(curr.ydstorg.bytes) = ydstorg;
ACCESS_FBINFO(curr.ydstorg.chunks) = ydstorg >> (isInterleave(MINFO)?3:2);
if (var->bits_per_pixel == 4)
ACCESS_FBINFO(curr.ydstorg.pixels) = ydstorg;
else
ACCESS_FBINFO(curr.ydstorg.pixels) = (ydstorg * 8) / var->bits_per_pixel;
ACCESS_FBINFO(curr.final_bppShift) = matroxfb_get_final_bppShift(PMINFO var->bits_per_pixel);
{ struct my_timming mt;
struct matrox_hw_state* hw;
int out;
 
matroxfb_var2my(var, &mt);
mt.crtc = MATROXFB_SRC_CRTC1;
/* CRTC1 delays */
switch (var->bits_per_pixel) {
case 0: mt.delay = 31 + 0; break;
case 16: mt.delay = 21 + 8; break;
case 24: mt.delay = 17 + 8; break;
case 32: mt.delay = 16 + 8; break;
default: mt.delay = 31 + 8; break;
}
 
hw = &ACCESS_FBINFO(hw);
 
//down_read(&ACCESS_FBINFO(altout).lock);
for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
if (ACCESS_FBINFO(outputs[out]).src == MATROXFB_SRC_CRTC1 &&
ACCESS_FBINFO(outputs[out]).output->compute) {
ACCESS_FBINFO(outputs[out]).output->compute(ACCESS_FBINFO(outputs[out]).data, &mt);
}
}
//up_read(&ACCESS_FBINFO(altout).lock);
ACCESS_FBINFO(crtc1).pixclock = mt.pixclock;
ACCESS_FBINFO(crtc1).mnp = mt.mnp;
ACCESS_FBINFO(hw_switch->init(PMINFO &mt));
pos = (var->yoffset * var->xres_virtual + var->xoffset) * ACCESS_FBINFO(curr.final_bppShift) / 32;
pos += ACCESS_FBINFO(curr.ydstorg.chunks);
 
hw->CRTC[0x0D] = pos & 0xFF;
hw->CRTC[0x0C] = (pos & 0xFF00) >> 8;
hw->CRTCEXT[0] = (hw->CRTCEXT[0] & 0xF0) | ((pos >> 16) & 0x0F) | ((pos >> 14) & 0x40);
hw->CRTCEXT[8] = pos >> 21;
ACCESS_FBINFO(hw_switch->restore(PMINFO2));
update_crtc2(PMINFO pos);
//down_read(&ACCESS_FBINFO(altout).lock);
for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
if (ACCESS_FBINFO(outputs[out]).src == MATROXFB_SRC_CRTC1 &&
ACCESS_FBINFO(outputs[out]).output->program) {
ACCESS_FBINFO(outputs[out]).output->program(ACCESS_FBINFO(outputs[out]).data);
}
}
for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
if (ACCESS_FBINFO(outputs[out]).src == MATROXFB_SRC_CRTC1 &&
ACCESS_FBINFO(outputs[out]).output->start) {
ACCESS_FBINFO(outputs[out]).output->start(ACCESS_FBINFO(outputs[out]).data);
}
}
//up_read(&ACCESS_FBINFO(altout).lock);
matrox_cfbX_init(PMINFO2);
}
}
return 0;
}
 
static int matroxfb_get_vblank(WPMINFO struct fb_vblank *vblank)
{
unsigned int sts1;
 
matroxfb_enable_irq(PMINFO 0);
memset(vblank, 0, sizeof(*vblank));
vblank->flags = FB_VBLANK_HAVE_VCOUNT | FB_VBLANK_HAVE_VSYNC |
FB_VBLANK_HAVE_VBLANK | FB_VBLANK_HAVE_HBLANK;
sts1 = mga_inb(M_INSTS1);
vblank->vcount = mga_inl(M_VCOUNT);
/* BTW, on my PIII/450 with G400, reading M_INSTS1
byte makes this call about 12% slower (1.70 vs. 2.05 us
per ioctl()) */
if (sts1 & 1)
vblank->flags |= FB_VBLANK_HBLANKING;
if (sts1 & 8)
vblank->flags |= FB_VBLANK_VSYNCING;
if (vblank->vcount >= ACCESS_FBINFO(fbcon).var.yres)
vblank->flags |= FB_VBLANK_VBLANKING;
if (test_bit(0, &ACCESS_FBINFO(irq_flags))) {
vblank->flags |= FB_VBLANK_HAVE_COUNT;
/* Only one writer, aligned int value...
it should work without lock and without atomic_t */
vblank->count = ACCESS_FBINFO(crtc1).vsync.cnt;
}
return 0;
}
 
static struct matrox_altout panellink_output = {
.name = "Panellink output",
};
 
static int matroxfb_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg,
struct fb_info *info)
{
MINFO_FROM_INFO(info);
DBG(__FUNCTION__)
 
if (ACCESS_FBINFO(dead)) {
return -ENXIO;
}
 
switch (cmd) {
case FBIOGET_VBLANK:
{
struct fb_vblank vblank;
int err;
 
err = matroxfb_get_vblank(PMINFO &vblank);
if (err)
return err;
if (copy_to_user((struct fb_vblank*)arg, &vblank, sizeof(vblank)))
return -EFAULT;
return 0;
}
case FBIO_WAITFORVSYNC:
{
u_int32_t crt;
 
crt = *(u_int32_t *)arg;
 
return matroxfb_wait_for_sync(PMINFO crt);
}
case MATROXFB_SET_OUTPUT_MODE:
{
struct matroxioc_output_mode mom;
struct matrox_altout *oproc;
int val;
 
if (copy_from_user(&mom, (struct matroxioc_output_mode*)arg, sizeof(mom)))
return -EFAULT;
if (mom.output >= MATROXFB_MAX_OUTPUTS)
return -ENXIO;
//down_read(&ACCESS_FBINFO(altout.lock));
oproc = ACCESS_FBINFO(outputs[mom.output]).output;
if (!oproc) {
val = -ENXIO;
} else if (!oproc->verifymode) {
if (mom.mode == MATROXFB_OUTPUT_MODE_MONITOR) {
val = 0;
} else {
val = -EINVAL;
}
} else {
val = oproc->verifymode(ACCESS_FBINFO(outputs[mom.output]).data, mom.mode);
}
if (!val) {
if (ACCESS_FBINFO(outputs[mom.output]).mode != mom.mode) {
ACCESS_FBINFO(outputs[mom.output]).mode = mom.mode;
val = 1;
}
}
//up_read(&ACCESS_FBINFO(altout.lock));
if (val != 1)
return val;
switch (ACCESS_FBINFO(outputs[mom.output]).src) {
case MATROXFB_SRC_CRTC1:
matroxfb_set_par(info);
break;
case MATROXFB_SRC_CRTC2:
{
struct matroxfb_dh_fb_info* crtc2;
 
//down_read(&ACCESS_FBINFO(crtc2.lock));
crtc2 = ACCESS_FBINFO(crtc2.info);
if (crtc2)
crtc2->fbcon.fbops->fb_set_par(&crtc2->fbcon);
//up_read(&ACCESS_FBINFO(crtc2.lock));
}
break;
}
return 0;
}
case MATROXFB_GET_OUTPUT_MODE:
{
struct matroxioc_output_mode mom;
struct matrox_altout *oproc;
int val;
 
if (copy_from_user(&mom, (struct matroxioc_output_mode*)arg, sizeof(mom)))
return -EFAULT;
if (mom.output >= MATROXFB_MAX_OUTPUTS)
return -ENXIO;
//down_read(&ACCESS_FBINFO(altout.lock));
oproc = ACCESS_FBINFO(outputs[mom.output]).output;
if (!oproc) {
val = -ENXIO;
} else {
mom.mode = ACCESS_FBINFO(outputs[mom.output]).mode;
val = 0;
}
//up_read(&ACCESS_FBINFO(altout.lock));
if (val)
return val;
if (copy_to_user((struct matroxioc_output_mode*)arg, &mom, sizeof(mom)))
return -EFAULT;
return 0;
}
case MATROXFB_SET_OUTPUT_CONNECTION:
{
u_int32_t tmp;
int i;
int changes;
 
if (copy_from_user(&tmp, (u_int32_t*)arg, sizeof(tmp)))
return -EFAULT;
for (i = 0; i < 32; i++) {
if (tmp & (1 << i)) {
if (i >= MATROXFB_MAX_OUTPUTS)
return -ENXIO;
if (!ACCESS_FBINFO(outputs[i]).output)
return -ENXIO;
switch (ACCESS_FBINFO(outputs[i]).src) {
case MATROXFB_SRC_NONE:
case MATROXFB_SRC_CRTC1:
break;
default:
return -EBUSY;
}
}
}
if (ACCESS_FBINFO(devflags.panellink)) {
if (tmp & MATROXFB_OUTPUT_CONN_DFP) {
if (tmp & MATROXFB_OUTPUT_CONN_SECONDARY)
return -EINVAL;
for (i = 0; i < MATROXFB_MAX_OUTPUTS; i++) {
if (ACCESS_FBINFO(outputs[i]).src == MATROXFB_SRC_CRTC2) {
return -EBUSY;
}
}
}
}
changes = 0;
for (i = 0; i < MATROXFB_MAX_OUTPUTS; i++) {
if (tmp & (1 << i)) {
if (ACCESS_FBINFO(outputs[i]).src != MATROXFB_SRC_CRTC1) {
changes = 1;
ACCESS_FBINFO(outputs[i]).src = MATROXFB_SRC_CRTC1;
}
} else if (ACCESS_FBINFO(outputs[i]).src == MATROXFB_SRC_CRTC1) {
changes = 1;
ACCESS_FBINFO(outputs[i]).src = MATROXFB_SRC_NONE;
}
}
if (!changes)
return 0;
matroxfb_set_par(info);
return 0;
}
case MATROXFB_GET_OUTPUT_CONNECTION:
{
u_int32_t conn = 0;
int i;
 
for (i = 0; i < MATROXFB_MAX_OUTPUTS; i++) {
if (ACCESS_FBINFO(outputs[i]).src == MATROXFB_SRC_CRTC1) {
conn |= 1 << i;
}
}
if (put_user(conn, (u_int32_t*)arg))
return -EFAULT;
return 0;
}
case MATROXFB_GET_AVAILABLE_OUTPUTS:
{
u_int32_t conn = 0;
int i;
 
for (i = 0; i < MATROXFB_MAX_OUTPUTS; i++) {
if (ACCESS_FBINFO(outputs[i]).output) {
switch (ACCESS_FBINFO(outputs[i]).src) {
case MATROXFB_SRC_NONE:
case MATROXFB_SRC_CRTC1:
conn |= 1 << i;
break;
}
}
}
if (ACCESS_FBINFO(devflags.panellink)) {
if (conn & MATROXFB_OUTPUT_CONN_DFP)
conn &= ~MATROXFB_OUTPUT_CONN_SECONDARY;
if (conn & MATROXFB_OUTPUT_CONN_SECONDARY)
conn &= ~MATROXFB_OUTPUT_CONN_DFP;
}
if (put_user(conn, (u_int32_t*)arg))
return -EFAULT;
return 0;
}
case MATROXFB_GET_ALL_OUTPUTS:
{
u_int32_t conn = 0;
int i;
 
for (i = 0; i < MATROXFB_MAX_OUTPUTS; i++) {
if (ACCESS_FBINFO(outputs[i]).output) {
conn |= 1 << i;
}
}
if (put_user(conn, (u_int32_t*)arg))
return -EFAULT;
return 0;
}
case VIDIOC_QUERYCAP:
{
struct v4l2_capability r;
memset(&r, 0, sizeof(r));
strcpy(r.driver, "matroxfb");
strcpy(r.card, "Matrox");
sprintf26(r.bus_info, "PCI:%s", pci_name(ACCESS_FBINFO(pcidev)));
r.version = KERNEL_VERSION(1,0,0);
r.capabilities = V4L2_CAP_VIDEO_OUTPUT;
if (copy_to_user((void*)arg, &r, sizeof(r)))
return -EFAULT;
return 0;
}
case VIDIOC_QUERYCTRL:
{
struct v4l2_queryctrl qctrl;
int err;
 
if (copy_from_user(&qctrl, (struct v4l2_queryctrl*)arg, sizeof(qctrl)))
return -EFAULT;
 
//down_read(&ACCESS_FBINFO(altout).lock);
if (!ACCESS_FBINFO(outputs[1]).output) {
err = -ENXIO;
} else if (ACCESS_FBINFO(outputs[1]).output->getqueryctrl) {
err = ACCESS_FBINFO(outputs[1]).output->getqueryctrl(ACCESS_FBINFO(outputs[1]).data, &qctrl);
} else {
err = -EINVAL;
}
//up_read(&ACCESS_FBINFO(altout).lock);
if (err >= 0 &&
copy_to_user((struct v4l2_queryctrl*)arg, &qctrl, sizeof(qctrl)))
return -EFAULT;
return err;
}
case VIDIOC_G_CTRL:
{
struct v4l2_control ctrl;
int err;
 
if (copy_from_user(&ctrl, (struct v4l2_control*)arg, sizeof(ctrl)))
return -EFAULT;
 
//down_read(&ACCESS_FBINFO(altout).lock);
if (!ACCESS_FBINFO(outputs[1]).output) {
err = -ENXIO;
} else if (ACCESS_FBINFO(outputs[1]).output->getctrl) {
err = ACCESS_FBINFO(outputs[1]).output->getctrl(ACCESS_FBINFO(outputs[1]).data, &ctrl);
} else {
err = -EINVAL;
}
//up_read(&ACCESS_FBINFO(altout).lock);
if (err >= 0 &&
copy_to_user((struct v4l2_control*)arg, &ctrl, sizeof(ctrl)))
return -EFAULT;
return err;
}
case VIDIOC_S_CTRL:
{
struct v4l2_control ctrl;
int err;
 
if (copy_from_user(&ctrl, (struct v4l2_control*)arg, sizeof(ctrl)))
return -EFAULT;
 
//down_read(&ACCESS_FBINFO(altout).lock);
if (!ACCESS_FBINFO(outputs[1]).output) {
err = -ENXIO;
} else if (ACCESS_FBINFO(outputs[1]).output->setctrl) {
err = ACCESS_FBINFO(outputs[1]).output->setctrl(ACCESS_FBINFO(outputs[1]).data, &ctrl);
} else {
err = -EINVAL;
}
//up_read(&ACCESS_FBINFO(altout).lock);
return err;
}
}
return -ENOTTY;
}
 
/* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */
 
static int matroxfb_blank(int blank, struct fb_info *info)
{
int seq;
int crtc;
CRITFLAGS
MINFO_FROM_INFO(info);
 
DBG(__FUNCTION__)
 
if (ACCESS_FBINFO(dead))
return 1;
 
switch (blank) {
case 1: seq = 0x20; crtc = 0x00; break; /* works ??? */
case 2: seq = 0x20; crtc = 0x10; break;
case 3: seq = 0x20; crtc = 0x20; break;
case 4: seq = 0x20; crtc = 0x30; break;
default: seq = 0x00; crtc = 0x00; break;
}
 
CRITBEGIN
 
mga_outb(M_SEQ_INDEX, 1);
mga_outb(M_SEQ_DATA, (mga_inb(M_SEQ_DATA) & ~0x20) | seq);
mga_outb(M_EXTVGA_INDEX, 1);
mga_outb(M_EXTVGA_DATA, (mga_inb(M_EXTVGA_DATA) & ~0x30) | crtc);
 
CRITEND
return 0;
}
 
static struct fb_ops matroxfb_ops = {
.owner = THIS_MODULE,
.fb_open = matroxfb_open,
.fb_release = matroxfb_release,
.fb_check_var = matroxfb_check_var,
.fb_set_par = matroxfb_set_par,
.fb_setcolreg = matroxfb_setcolreg,
.fb_pan_display =matroxfb_pan_display,
.fb_blank = matroxfb_blank,
.fb_ioctl = matroxfb_ioctl,
/* .fb_fillrect = <set by matrox_cfbX_init>, */
/* .fb_copyarea = <set by matrox_cfbX_init>, */
/* .fb_imageblit = <set by matrox_cfbX_init>, */
/* .fb_cursor = <set by matrox_cfbX_init>, */
};
 
#define RSDepth(X) (((X) >> 8) & 0x0F)
#define RS8bpp 0x1
#define RS15bpp 0x2
#define RS16bpp 0x3
#define RS32bpp 0x4
#define RS4bpp 0x5
#define RS24bpp 0x6
#define RSText 0x7
#define RSText8 0x8
/* 9-F */
static struct { struct fb_bitfield red, green, blue, transp; int bits_per_pixel; } colors[] = {
{ { 0, 8, 0}, { 0, 8, 0}, { 0, 8, 0}, { 0, 0, 0}, 8 },
{ { 10, 5, 0}, { 5, 5, 0}, { 0, 5, 0}, { 15, 1, 0}, 16 },
{ { 11, 5, 0}, { 5, 6, 0}, { 0, 5, 0}, { 0, 0, 0}, 16 },
{ { 16, 8, 0}, { 8, 8, 0}, { 0, 8, 0}, { 24, 8, 0}, 32 },
{ { 0, 8, 0}, { 0, 8, 0}, { 0, 8, 0}, { 0, 0, 0}, 4 },
{ { 16, 8, 0}, { 8, 8, 0}, { 0, 8, 0}, { 0, 0, 0}, 24 },
{ { 0, 6, 0}, { 0, 6, 0}, { 0, 6, 0}, { 0, 0, 0}, 0 }, /* textmode with (default) VGA8x16 */
{ { 0, 6, 0}, { 0, 6, 0}, { 0, 6, 0}, { 0, 0, 0}, 0 }, /* textmode hardwired to VGA8x8 */
};
 
/* initialized by setup, see explanation at end of file (search for MODULE_PARM_DESC) */
static unsigned int mem; /* "matrox:mem:xxxxxM" */
static int option_precise_width = 1; /* cannot be changed, option_precise_width==0 must imply noaccel */
static int inv24; /* "matrox:inv24" */
static int cross4MB = -1; /* "matrox:cross4MB" */
static int disabled; /* "matrox:disabled" */
static int noaccel; /* "matrox:noaccel" */
static int nopan; /* "matrox:nopan" */
static int no_pci_retry; /* "matrox:nopciretry" */
static int novga; /* "matrox:novga" */
static int nobios; /* "matrox:nobios" */
static int noinit = 1; /* "matrox:init" */
static int inverse; /* "matrox:inverse" */
static int sgram; /* "matrox:sgram" */
#ifdef CONFIG_MTRR
static int mtrr = 1; /* "matrox:nomtrr" */
#endif
static int grayscale; /* "matrox:grayscale" */
static int dev = -1; /* "matrox:dev:xxxxx" */
static unsigned int vesa = ~0; /* "matrox:vesa:xxxxx" */
static int depth = -1; /* "matrox:depth:xxxxx" */
static unsigned int xres; /* "matrox:xres:xxxxx" */
static unsigned int yres; /* "matrox:yres:xxxxx" */
static unsigned int upper = ~0; /* "matrox:upper:xxxxx" */
static unsigned int lower = ~0; /* "matrox:lower:xxxxx" */
static unsigned int vslen; /* "matrox:vslen:xxxxx" */
static unsigned int left = ~0; /* "matrox:left:xxxxx" */
static unsigned int right = ~0; /* "matrox:right:xxxxx" */
static unsigned int hslen; /* "matrox:hslen:xxxxx" */
static unsigned int pixclock; /* "matrox:pixclock:xxxxx" */
static int sync = -1; /* "matrox:sync:xxxxx" */
static unsigned int fv; /* "matrox:fv:xxxxx" */
static unsigned int fh; /* "matrox:fh:xxxxxk" */
static unsigned int maxclk; /* "matrox:maxclk:xxxxM" */
static int dfp; /* "matrox:dfp */
static int dfp_type = -1; /* "matrox:dfp:xxx */
static int memtype = -1; /* "matrox:memtype:xxx" */
 
#ifndef MODULE
static char videomode[64]; /* "matrox:mode:xxxxx" or "matrox:xxxxx" */
#endif
 
static int matroxfb_getmemory(WPMINFO unsigned int maxSize, unsigned int *realSize){
vaddr_t vm;
unsigned int offs;
unsigned int offs2;
unsigned char store;
unsigned char bytes[32];
unsigned char* tmp;
 
DBG(__FUNCTION__)
 
vm = ACCESS_FBINFO(video.vbase);
maxSize &= ~0x1FFFFF; /* must be X*2MB (really it must be 2 or X*4MB) */
/* at least 2MB */
if (maxSize < 0x0200000) return 0;
if (maxSize > 0x2000000) maxSize = 0x2000000;
 
mga_outb(M_EXTVGA_INDEX, 0x03);
mga_outb(M_EXTVGA_DATA, mga_inb(M_EXTVGA_DATA) | 0x80);
 
store = mga_readb(vm, 0x1234);
tmp = bytes;
for (offs = 0x100000; offs < maxSize; offs += 0x200000)
*tmp++ = mga_readb(vm, offs);
for (offs = 0x100000; offs < maxSize; offs += 0x200000)
mga_writeb(vm, offs, 0x02);
if (ACCESS_FBINFO(features.accel.has_cacheflush))
mga_outb(M_CACHEFLUSH, 0x00);
else
mga_writeb(vm, 0x1234, 0x99);
for (offs = 0x100000; offs < maxSize; offs += 0x200000) {
if (mga_readb(vm, offs) != 0x02)
break;
mga_writeb(vm, offs, mga_readb(vm, offs) - 0x02);
if (mga_readb(vm, offs))
break;
}
tmp = bytes;
for (offs2 = 0x100000; offs2 < maxSize; offs2 += 0x200000)
mga_writeb(vm, offs2, *tmp++);
mga_writeb(vm, 0x1234, store);
 
mga_outb(M_EXTVGA_INDEX, 0x03);
mga_outb(M_EXTVGA_DATA, mga_inb(M_EXTVGA_DATA) & ~0x80);
 
*realSize = offs - 0x100000;
#ifdef CONFIG_FB_MATROX_MILLENIUM
ACCESS_FBINFO(interleave) = !(!isMillenium(MINFO) || ((offs - 0x100000) & 0x3FFFFF));
#endif
return 1;
}
 
struct video_board {
int maxvram;
int maxdisplayable;
int accelID;
struct matrox_switch* lowlevel;
};
#ifdef CONFIG_FB_MATROX_MILLENIUM
static struct video_board vbMillennium = {0x0800000, 0x0800000, FB_ACCEL_MATROX_MGA2064W, &matrox_millennium};
static struct video_board vbMillennium2 = {0x1000000, 0x0800000, FB_ACCEL_MATROX_MGA2164W, &matrox_millennium};
static struct video_board vbMillennium2A = {0x1000000, 0x0800000, FB_ACCEL_MATROX_MGA2164W_AGP, &matrox_millennium};
#endif /* CONFIG_FB_MATROX_MILLENIUM */
#ifdef CONFIG_FB_MATROX_MYSTIQUE
static struct video_board vbMystique = {0x0800000, 0x0800000, FB_ACCEL_MATROX_MGA1064SG, &matrox_mystique};
#endif /* CONFIG_FB_MATROX_MYSTIQUE */
#ifdef CONFIG_FB_MATROX_G100
static struct video_board vbG100 = {0x0800000, 0x0800000, FB_ACCEL_MATROX_MGAG100, &matrox_G100};
static struct video_board vbG200 = {0x1000000, 0x1000000, FB_ACCEL_MATROX_MGAG200, &matrox_G100};
#ifdef CONFIG_FB_MATROX_32MB
/* from doc it looks like that accelerator can draw only to low 16MB :-( Direct accesses & displaying are OK for
whole 32MB */
static struct video_board vbG400 = {0x2000000, 0x1000000, FB_ACCEL_MATROX_MGAG400, &matrox_G100};
#else
static struct video_board vbG400 = {0x2000000, 0x1000000, FB_ACCEL_MATROX_MGAG400, &matrox_G100};
#endif
#endif
 
#define DEVF_VIDEO64BIT 0x0001
#define DEVF_SWAPS 0x0002
#define DEVF_SRCORG 0x0004
#define DEVF_DUALHEAD 0x0008
#define DEVF_CROSS4MB 0x0010
#define DEVF_TEXT4B 0x0020
/* #define DEVF_recycled 0x0040 */
/* #define DEVF_recycled 0x0080 */
#define DEVF_SUPPORT32MB 0x0100
#define DEVF_ANY_VXRES 0x0200
#define DEVF_TEXT16B 0x0400
#define DEVF_CRTC2 0x0800
#define DEVF_MAVEN_CAPABLE 0x1000
#define DEVF_PANELLINK_CAPABLE 0x2000
#define DEVF_G450DAC 0x4000
 
#define DEVF_GCORE (DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB)
#define DEVF_G2CORE (DEVF_GCORE | DEVF_ANY_VXRES | DEVF_MAVEN_CAPABLE | DEVF_PANELLINK_CAPABLE | DEVF_SRCORG | DEVF_DUALHEAD)
#define DEVF_G100 (DEVF_GCORE) /* no doc, no vxres... */
#define DEVF_G200 (DEVF_G2CORE)
#define DEVF_G400 (DEVF_G2CORE | DEVF_SUPPORT32MB | DEVF_TEXT16B | DEVF_CRTC2)
/* if you'll find how to drive DFP... */
#define DEVF_G450 (DEVF_GCORE | DEVF_ANY_VXRES | DEVF_SUPPORT32MB | DEVF_TEXT16B | DEVF_CRTC2 | DEVF_G450DAC | DEVF_SRCORG | DEVF_DUALHEAD)
#define DEVF_G550 (DEVF_G450)
 
static struct board {
unsigned short vendor, device, rev, svid, sid;
unsigned int flags;
unsigned int maxclk;
enum mga_chip chip;
struct video_board* base;
const char* name;
} dev_list[] = {
#ifdef CONFIG_FB_MATROX_MILLENIUM
{PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL, 0xFF,
0, 0,
DEVF_TEXT4B,
230000,
MGA_2064,
&vbMillennium,
"Millennium (PCI)"},
{PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL_2, 0xFF,
0, 0,
DEVF_SWAPS,
220000,
MGA_2164,
&vbMillennium2,
"Millennium II (PCI)"},
{PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL_2_AGP, 0xFF,
0, 0,
DEVF_SWAPS,
250000,
MGA_2164,
&vbMillennium2A,
"Millennium II (AGP)"},
#endif
#ifdef CONFIG_FB_MATROX_MYSTIQUE
{PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MYS, 0x02,
0, 0,
DEVF_VIDEO64BIT | DEVF_CROSS4MB,
180000,
MGA_1064,
&vbMystique,
"Mystique (PCI)"},
{PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MYS, 0xFF,
0, 0,
DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB,
220000,
MGA_1164,
&vbMystique,
"Mystique 220 (PCI)"},
#endif
#ifdef CONFIG_FB_MATROX_G100
{PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G100_MM, 0xFF,
0, 0,
DEVF_G100,
230000,
MGA_G100,
&vbG100,
"MGA-G100 (PCI)"},
{PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G100_AGP, 0xFF,
0, 0,
DEVF_G100,
230000,
MGA_G100,
&vbG100,
"MGA-G100 (AGP)"},
{PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G200_PCI, 0xFF,
0, 0,
DEVF_G200,
250000,
MGA_G200,
&vbG200,
"MGA-G200 (PCI)"},
{PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G200_AGP, 0xFF,
PCI_SS_VENDOR_ID_MATROX, PCI_SS_ID_MATROX_GENERIC,
DEVF_G200,
220000,
MGA_G200,
&vbG200,
"MGA-G200 (AGP)"},
{PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G200_AGP, 0xFF,
PCI_SS_VENDOR_ID_MATROX, PCI_SS_ID_MATROX_MYSTIQUE_G200_AGP,
DEVF_G200,
230000,
MGA_G200,
&vbG200,
"Mystique G200 (AGP)"},
{PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G200_AGP, 0xFF,
PCI_SS_VENDOR_ID_MATROX, PCI_SS_ID_MATROX_MILLENIUM_G200_AGP,
DEVF_G200,
250000,
MGA_G200,
&vbG200,
"Millennium G200 (AGP)"},
{PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G200_AGP, 0xFF,
PCI_SS_VENDOR_ID_MATROX, PCI_SS_ID_MATROX_MARVEL_G200_AGP,
DEVF_G200,
230000,
MGA_G200,
&vbG200,
"Marvel G200 (AGP)"},
{PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G200_AGP, 0xFF,
PCI_SS_VENDOR_ID_SIEMENS_NIXDORF, PCI_SS_ID_SIEMENS_MGA_G200_AGP,
DEVF_G200,
230000,
MGA_G200,
&vbG200,
"MGA-G200 (AGP)"},
{PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G200_AGP, 0xFF,
0, 0,
DEVF_G200,
230000,
MGA_G200,
&vbG200,
"G200 (AGP)"},
{PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G400, 0x80,
PCI_SS_VENDOR_ID_MATROX, PCI_SS_ID_MATROX_MILLENNIUM_G400_MAX_AGP,
DEVF_G400,
360000,
MGA_G400,
&vbG400,
"Millennium G400 MAX (AGP)"},
{PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G400, 0x80,
0, 0,
DEVF_G400,
300000,
MGA_G400,
&vbG400,
"G400 (AGP)"},
{PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G400, 0xFF,
0, 0,
DEVF_G450,
360000,
MGA_G450,
&vbG400,
"G450"},
{PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G550, 0xFF,
0, 0,
DEVF_G550,
360000,
MGA_G550,
&vbG400,
"G550"},
#endif
{0, 0, 0xFF,
0, 0,
0,
0,
0,
NULL,
NULL}};
 
#ifndef MODULE
static struct fb_videomode defaultmode = {
/* 640x480 @ 60Hz, 31.5 kHz */
NULL, 60, 640, 480, 39721, 40, 24, 32, 11, 96, 2,
0, FB_VMODE_NONINTERLACED
};
#endif /* !MODULE */
 
static int hotplug = 0;
 
static int initMatrox2(WPMINFO struct board* b){
unsigned long ctrlptr_phys = 0;
unsigned long video_base_phys = 0;
unsigned int memsize;
int err;
 
DBG(__FUNCTION__)
 
/* set default values... */
vesafb_defined.accel_flags = FB_ACCELF_TEXT;
 
ACCESS_FBINFO(hw_switch) = b->base->lowlevel;
ACCESS_FBINFO(devflags.accelerator) = b->base->accelID;
ACCESS_FBINFO(max_pixel_clock) = b->maxclk;
 
printk(KERN_INFO "matroxfb: Matrox %s detected\n", b->name);
ACCESS_FBINFO(capable.plnwt) = 1;
ACCESS_FBINFO(chip) = b->chip;
ACCESS_FBINFO(capable.srcorg) = b->flags & DEVF_SRCORG;
ACCESS_FBINFO(devflags.video64bits) = b->flags & DEVF_VIDEO64BIT;
if (b->flags & DEVF_TEXT4B) {
ACCESS_FBINFO(devflags.vgastep) = 4;
ACCESS_FBINFO(devflags.textmode) = 4;
ACCESS_FBINFO(devflags.text_type_aux) = FB_AUX_TEXT_MGA_STEP16;
} else if (b->flags & DEVF_TEXT16B) {
ACCESS_FBINFO(devflags.vgastep) = 16;
ACCESS_FBINFO(devflags.textmode) = 1;
ACCESS_FBINFO(devflags.text_type_aux) = FB_AUX_TEXT_MGA_STEP16;
} else {
ACCESS_FBINFO(devflags.vgastep) = 8;
ACCESS_FBINFO(devflags.textmode) = 1;
ACCESS_FBINFO(devflags.text_type_aux) = FB_AUX_TEXT_MGA_STEP8;
}
#ifdef CONFIG_FB_MATROX_32MB
ACCESS_FBINFO(devflags.support32MB) = (b->flags & DEVF_SUPPORT32MB) != 0;
#endif
ACCESS_FBINFO(devflags.precise_width) = !(b->flags & DEVF_ANY_VXRES);
ACCESS_FBINFO(devflags.crtc2) = (b->flags & DEVF_CRTC2) != 0;
ACCESS_FBINFO(devflags.maven_capable) = (b->flags & DEVF_MAVEN_CAPABLE) != 0;
ACCESS_FBINFO(devflags.dualhead) = (b->flags & DEVF_DUALHEAD) != 0;
if (b->flags & DEVF_PANELLINK_CAPABLE) {
ACCESS_FBINFO(outputs[2]).data = MINFO;
ACCESS_FBINFO(outputs[2]).output = &panellink_output;
if (dfp)
ACCESS_FBINFO(outputs[2]).src = MATROXFB_SRC_CRTC1;
else
ACCESS_FBINFO(outputs[2]).src = MATROXFB_SRC_NONE;
ACCESS_FBINFO(outputs[2]).mode = MATROXFB_OUTPUT_MODE_MONITOR;
ACCESS_FBINFO(devflags.panellink) = 1;
}
ACCESS_FBINFO(devflags.dfp_type) = dfp_type;
ACCESS_FBINFO(devflags.g450dac) = (b->flags & DEVF_G450DAC) != 0;
ACCESS_FBINFO(devflags.textstep) = ACCESS_FBINFO(devflags.vgastep) * ACCESS_FBINFO(devflags.textmode);
ACCESS_FBINFO(devflags.textvram) = 65536 / ACCESS_FBINFO(devflags.textmode);
 
if (ACCESS_FBINFO(capable.cross4MB) < 0)
ACCESS_FBINFO(capable.cross4MB) = b->flags & DEVF_CROSS4MB;
if (b->flags & DEVF_SWAPS) {
ctrlptr_phys = pci_resource_start(ACCESS_FBINFO(pcidev), 1);
video_base_phys = pci_resource_start(ACCESS_FBINFO(pcidev), 0);
ACCESS_FBINFO(devflags.fbResource) = PCI_BASE_ADDRESS_0;
} else {
ctrlptr_phys = pci_resource_start(ACCESS_FBINFO(pcidev), 0);
video_base_phys = pci_resource_start(ACCESS_FBINFO(pcidev), 1);
ACCESS_FBINFO(devflags.fbResource) = PCI_BASE_ADDRESS_1;
}
err = -EINVAL;
if (!ctrlptr_phys) {
printk(KERN_ERR "matroxfb: control registers are not available, matroxfb disabled\n");
goto fail;
}
if (!video_base_phys) {
printk(KERN_ERR "matroxfb: video RAM is not available in PCI address space, matroxfb disabled\n");
goto fail;
}
memsize = b->base->maxvram;
if (!request_mem_region(ctrlptr_phys, 16384, "matroxfb MMIO")) {
goto fail;
}
if (!request_mem_region(video_base_phys, memsize, "matroxfb FB")) {
goto failCtrlMR;
}
ACCESS_FBINFO(video.len_maximum) = memsize;
/* convert mem (autodetect k, M) */
if (mem < 1024) mem *= 1024;
if (mem < 0x00100000) mem *= 1024;
 
if (mem && (mem < memsize))
memsize = mem;
err = -ENOMEM;
if (mga_ioremap(ctrlptr_phys, 16384, MGA_IOREMAP_MMIO, &ACCESS_FBINFO(mmio.vbase))) {
printk(KERN_ERR "matroxfb: cannot ioremap(%lX, 16384), matroxfb disabled\n", ctrlptr_phys);
goto failVideoMR;
}
ACCESS_FBINFO(mmio.base) = ctrlptr_phys;
ACCESS_FBINFO(mmio.len) = 16384;
ACCESS_FBINFO(video.base) = video_base_phys;
if (mga_ioremap(video_base_phys, memsize, MGA_IOREMAP_FB, &ACCESS_FBINFO(video.vbase))) {
printk(KERN_ERR "matroxfb: cannot ioremap(%lX, %d), matroxfb disabled\n",
video_base_phys, memsize);
goto failCtrlIO;
}
{
u_int32_t cmd;
u_int32_t mga_option;
 
pci_read_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, (void *)&mga_option);
pci_read_config_dword(ACCESS_FBINFO(pcidev), PCI_COMMAND, (void *)&cmd);
mga_option &= 0x7FFFFFFF; /* clear BIG_ENDIAN */
mga_option |= MX_OPTION_BSWAP;
/* disable palette snooping */
cmd &= ~PCI_COMMAND_VGA_PALETTE;
if (pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437, NULL)) {
if (!(mga_option & 0x20000000) && !ACCESS_FBINFO(devflags.nopciretry)) {
printk(KERN_WARNING "matroxfb: Disabling PCI retries due to i82437 present\n");
}
mga_option |= 0x20000000;
ACCESS_FBINFO(devflags.nopciretry) = 1;
}
pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_COMMAND, cmd);
pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, mga_option);
ACCESS_FBINFO(hw).MXoptionReg = mga_option;
 
/* select non-DMA memory for PCI_MGA_DATA, otherwise dump of PCI cfg space can lock PCI bus */
/* maybe preinit() candidate, but it is same... for all devices... at this time... */
pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_MGA_INDEX, 0x00003C00);
}
 
err = -ENXIO;
matroxfb_read_pins(PMINFO2);
if (ACCESS_FBINFO(hw_switch)->preinit(PMINFO2)) {
goto failVideoIO;
}
 
err = -ENOMEM;
if (!matroxfb_getmemory(PMINFO memsize, &ACCESS_FBINFO(video.len)) || !ACCESS_FBINFO(video.len)) {
printk(KERN_ERR "matroxfb: cannot determine memory size\n");
goto failVideoIO;
}
ACCESS_FBINFO(devflags.ydstorg) = 0;
 
ACCESS_FBINFO(fbcon.currcon) = -1;
ACCESS_FBINFO(video.base) = video_base_phys;
ACCESS_FBINFO(video.len_usable) = ACCESS_FBINFO(video.len);
if (ACCESS_FBINFO(video.len_usable) > b->base->maxdisplayable)
ACCESS_FBINFO(video.len_usable) = b->base->maxdisplayable;
#ifdef CONFIG_MTRR
if (mtrr) {
ACCESS_FBINFO(mtrr.vram) = mtrr_add(video_base_phys, ACCESS_FBINFO(video.len), MTRR_TYPE_WRCOMB, 1);
ACCESS_FBINFO(mtrr.vram_valid) = 1;
printk(KERN_INFO "matroxfb: MTRR's turned on\n");
}
#endif /* CONFIG_MTRR */
 
if (!ACCESS_FBINFO(devflags.novga))
request_region(0x3C0, 32, "matrox");
matroxfb_g450_connect(PMINFO2);
ACCESS_FBINFO(hw_switch->reset(PMINFO2));
 
ACCESS_FBINFO(fbcon.monspecs.hfmin) = 0;
ACCESS_FBINFO(fbcon.monspecs.hfmax) = fh;
ACCESS_FBINFO(fbcon.monspecs.vfmin) = 0;
ACCESS_FBINFO(fbcon.monspecs.vfmax) = fv;
ACCESS_FBINFO(fbcon.monspecs.dpms) = 0; /* TBD */
 
/* static settings */
vesafb_defined.red = colors[depth-1].red;
vesafb_defined.green = colors[depth-1].green;
vesafb_defined.blue = colors[depth-1].blue;
vesafb_defined.bits_per_pixel = colors[depth-1].bits_per_pixel;
vesafb_defined.grayscale = grayscale;
vesafb_defined.vmode = 0;
if (noaccel)
vesafb_defined.accel_flags &= ~FB_ACCELF_TEXT;
 
ACCESS_FBINFO(fbops) = matroxfb_ops;
ACCESS_FBINFO(fbcon.fbops) = &ACCESS_FBINFO(fbops);
ACCESS_FBINFO(fbcon.pseudo_palette) = ACCESS_FBINFO(cmap);
/* after __init time we are like module... no logo */
ACCESS_FBINFO(fbcon.flags) = hotplug ? FBINFO_FLAG_MODULE : FBINFO_FLAG_DEFAULT;
ACCESS_FBINFO(video.len_usable) &= PAGE_MASK;
fb_alloc_cmap(&ACCESS_FBINFO(fbcon.cmap), 256, 1);
 
#ifndef MODULE
/* mode database is marked __init!!! */
if (!hotplug) {
fb_find_mode(&vesafb_defined, &ACCESS_FBINFO(fbcon), videomode[0]?videomode:NULL,
NULL, 0, &defaultmode, vesafb_defined.bits_per_pixel);
}
#endif /* !MODULE */
 
/* mode modifiers */
if (hslen)
vesafb_defined.hsync_len = hslen;
if (vslen)
vesafb_defined.vsync_len = vslen;
if (left != ~0)
vesafb_defined.left_margin = left;
if (right != ~0)
vesafb_defined.right_margin = right;
if (upper != ~0)
vesafb_defined.upper_margin = upper;
if (lower != ~0)
vesafb_defined.lower_margin = lower;
if (xres)
vesafb_defined.xres = xres;
if (yres)
vesafb_defined.yres = yres;
if (sync != -1)
vesafb_defined.sync = sync;
else if (vesafb_defined.sync == ~0) {
vesafb_defined.sync = 0;
if (yres < 400)
vesafb_defined.sync |= FB_SYNC_HOR_HIGH_ACT;
else if (yres < 480)
vesafb_defined.sync |= FB_SYNC_VERT_HIGH_ACT;
}
 
/* fv, fh, maxclk limits was specified */
{
unsigned int tmp;
 
if (fv) {
tmp = fv * (vesafb_defined.upper_margin + vesafb_defined.yres
+ vesafb_defined.lower_margin + vesafb_defined.vsync_len);
if ((tmp < fh) || (fh == 0)) fh = tmp;
}
if (fh) {
tmp = fh * (vesafb_defined.left_margin + vesafb_defined.xres
+ vesafb_defined.right_margin + vesafb_defined.hsync_len);
if ((tmp < maxclk) || (maxclk == 0)) maxclk = tmp;
}
tmp = (maxclk + 499) / 500;
if (tmp) {
tmp = (2000000000 + tmp) / tmp;
if (tmp > pixclock) pixclock = tmp;
}
}
if (pixclock) {
if (pixclock < 2000) /* > 500MHz */
pixclock = 4000; /* 250MHz */
if (pixclock > 1000000)
pixclock = 1000000; /* 1MHz */
vesafb_defined.pixclock = pixclock;
}
 
/* FIXME: Where to move this?! */
#if defined(CONFIG_PPC_PMAC)
#ifndef MODULE
if (_machine == _MACH_Pmac) {
struct fb_var_screeninfo var;
if (default_vmode <= 0 || default_vmode > VMODE_MAX)
default_vmode = VMODE_640_480_60;
#ifdef CONFIG_NVRAM
if (default_cmode == CMODE_NVRAM)
default_cmode = nvram_read_byte(NV_CMODE);
#endif
if (default_cmode < CMODE_8 || default_cmode > CMODE_32)
default_cmode = CMODE_8;
if (!mac_vmode_to_var(default_vmode, default_cmode, &var)) {
var.accel_flags = vesafb_defined.accel_flags;
var.xoffset = var.yoffset = 0;
/* Note: mac_vmode_to_var() does not set all parameters */
vesafb_defined = var;
}
}
#endif /* !MODULE */
#endif /* CONFIG_PPC_PMAC */
vesafb_defined.xres_virtual = vesafb_defined.xres;
if (nopan) {
vesafb_defined.yres_virtual = vesafb_defined.yres;
} else {
vesafb_defined.yres_virtual = 65536; /* large enough to be INF, but small enough
to yres_virtual * xres_virtual < 2^32 */
}
matroxfb_init_fix(PMINFO2);
err = -EINVAL;
 
printk(KERN_INFO "matroxfb: %dx%dx%dbpp (virtual: %dx%d)\n",
vesafb_defined.xres, vesafb_defined.yres, vesafb_defined.bits_per_pixel,
vesafb_defined.xres_virtual, vesafb_defined.yres_virtual);
printk(KERN_INFO "matroxfb: framebuffer at 0x%lX, mapped to 0x%p, size %d\n",
ACCESS_FBINFO(video.base), vaddr_va(ACCESS_FBINFO(video.vbase)), ACCESS_FBINFO(video.len));
 
/* We do not have to set currcon to 0... register_framebuffer do it for us on first console
* and we do not want currcon == 0 for subsequent framebuffers */
 
if (register_framebuffer(&ACCESS_FBINFO(fbcon)) < 0) {
goto failVideoIO;
}
printk("fb%d: %s frame buffer device\n",
ACCESS_FBINFO(fbcon.node), ACCESS_FBINFO(fbcon.fix.id));
if (ACCESS_FBINFO(fbcon.currcon) < 0) {
/* there is no console on this fb... but we have to initialize hardware
* until someone tells me what is proper thing to do */
printk(KERN_INFO "fb%d: initializing hardware\n",
ACCESS_FBINFO(fbcon.node));
fb_set_var(&ACCESS_FBINFO(fbcon), &vesafb_defined);
}
return 0;
failVideoIO:;
matroxfb_g450_shutdown(PMINFO2);
mga_iounmap(ACCESS_FBINFO(video.vbase));
failCtrlIO:;
mga_iounmap(ACCESS_FBINFO(mmio.vbase));
failVideoMR:;
release_mem_region(video_base_phys, ACCESS_FBINFO(video.len_maximum));
failCtrlMR:;
release_mem_region(ctrlptr_phys, 16384);
fail:;
return err;
}
 
LIST_HEAD(matroxfb_list);
LIST_HEAD(matroxfb_driver_list);
 
#define matroxfb_l(x) list_entry(x, struct matrox_fb_info, next_fb)
#define matroxfb_driver_l(x) list_entry(x, struct matroxfb_driver, node)
int matroxfb_register_driver(struct matroxfb_driver* drv) {
struct matrox_fb_info* minfo;
 
list_add(&drv->node, &matroxfb_driver_list);
for (minfo = matroxfb_l(matroxfb_list.next);
minfo != matroxfb_l(&matroxfb_list);
minfo = matroxfb_l(minfo->next_fb.next)) {
void* p;
 
if (minfo->drivers_count == MATROXFB_MAX_FB_DRIVERS)
continue;
p = drv->probe(minfo);
if (p) {
minfo->drivers_data[minfo->drivers_count] = p;
minfo->drivers[minfo->drivers_count++] = drv;
}
}
return 0;
}
 
void matroxfb_unregister_driver(struct matroxfb_driver* drv) {
struct matrox_fb_info* minfo;
 
list_del(&drv->node);
for (minfo = matroxfb_l(matroxfb_list.next);
minfo != matroxfb_l(&matroxfb_list);
minfo = matroxfb_l(minfo->next_fb.next)) {
int i;
 
for (i = 0; i < minfo->drivers_count; ) {
if (minfo->drivers[i] == drv) {
if (drv && drv->remove)
drv->remove(minfo, minfo->drivers_data[i]);
minfo->drivers[i] = minfo->drivers[--minfo->drivers_count];
minfo->drivers_data[i] = minfo->drivers_data[minfo->drivers_count];
} else
i++;
}
}
}
 
static void matroxfb_register_device(struct matrox_fb_info* minfo) {
struct matroxfb_driver* drv;
int i = 0;
list_add(&ACCESS_FBINFO(next_fb), &matroxfb_list);
for (drv = matroxfb_driver_l(matroxfb_driver_list.next);
drv != matroxfb_driver_l(&matroxfb_driver_list);
drv = matroxfb_driver_l(drv->node.next)) {
if (drv && drv->probe) {
void *p = drv->probe(minfo);
if (p) {
minfo->drivers_data[i] = p;
minfo->drivers[i++] = drv;
if (i == MATROXFB_MAX_FB_DRIVERS)
break;
}
}
}
minfo->drivers_count = i;
}
 
static void matroxfb_unregister_device(struct matrox_fb_info* minfo) {
int i;
 
list_del(&ACCESS_FBINFO(next_fb));
for (i = 0; i < minfo->drivers_count; i++) {
struct matroxfb_driver* drv = minfo->drivers[i];
 
if (drv && drv->remove)
drv->remove(minfo, minfo->drivers_data[i]);
}
}
 
static int matroxfb_probe(struct pci_dev* pdev, const struct pci_device_id* dummy) {
struct board* b;
u_int8_t rev;
u_int16_t svid;
u_int16_t sid;
struct matrox_fb_info* minfo;
int err;
u_int32_t cmd;
#ifndef CONFIG_FB_MATROX_MULTIHEAD
static int registered = 0;
#endif
DBG(__FUNCTION__)
 
pci_read_config_byte(pdev, PCI_REVISION_ID, &rev);
svid = pdev->subsystem_vendor;
sid = pdev->subsystem_device;
for (b = dev_list; b->vendor; b++) {
if ((b->vendor != pdev->vendor) || (b->device != pdev->device) || (b->rev < rev)) continue;
if (b->svid)
if ((b->svid != svid) || (b->sid != sid)) continue;
break;
}
/* not match... */
if (!b->vendor)
return -1;
if (dev > 0) {
/* not requested one... */
dev--;
return -1;
}
pci_read_config_dword(pdev, PCI_COMMAND, (void *)&cmd);
if (pci_enable_device(pdev)) {
return -1;
}
 
#ifdef CONFIG_FB_MATROX_MULTIHEAD
minfo = (struct matrox_fb_info*)kmalloc(sizeof(*minfo), GFP_KERNEL);
if (!minfo)
return -1;
#else
if (registered) /* singlehead driver... */
return -1;
minfo = &matroxfb_global_mxinfo;
#endif
memset(MINFO, 0, sizeof(*MINFO));
 
ACCESS_FBINFO(pcidev) = pdev;
ACCESS_FBINFO(dead) = 0;
ACCESS_FBINFO(usecount) = 0;
ACCESS_FBINFO(userusecount) = 0;
 
pci_set_drvdata(pdev, MINFO);
/* DEVFLAGS */
ACCESS_FBINFO(devflags.memtype) = memtype;
if (memtype != -1)
noinit = 0;
if (cmd & PCI_COMMAND_MEMORY) {
ACCESS_FBINFO(devflags.novga) = novga;
ACCESS_FBINFO(devflags.nobios) = nobios;
ACCESS_FBINFO(devflags.noinit) = noinit;
/* subsequent heads always needs initialization and must not enable BIOS */
novga = 1;
nobios = 1;
noinit = 0;
} else {
ACCESS_FBINFO(devflags.novga) = 1;
ACCESS_FBINFO(devflags.nobios) = 1;
ACCESS_FBINFO(devflags.noinit) = 0;
}
 
ACCESS_FBINFO(devflags.nopciretry) = no_pci_retry;
ACCESS_FBINFO(devflags.mga_24bpp_fix) = inv24;
ACCESS_FBINFO(devflags.precise_width) = option_precise_width;
ACCESS_FBINFO(devflags.sgram) = sgram;
ACCESS_FBINFO(capable.cross4MB) = cross4MB;
 
spin_lock_init(&ACCESS_FBINFO(lock.DAC));
spin_lock_init(&ACCESS_FBINFO(lock.accel));
init_rwsem(&ACCESS_FBINFO(crtc2.lock));
init_rwsem(&ACCESS_FBINFO(altout.lock));
ACCESS_FBINFO(irq_flags) = 0;
//init_waitqueue_head(&ACCESS_FBINFO(crtc1.vsync.wait));
//init_waitqueue_head(&ACCESS_FBINFO(crtc2.vsync.wait));
ACCESS_FBINFO(crtc1.panpos) = -1;
 
err = initMatrox2(PMINFO b);
if (!err) {
#ifndef CONFIG_FB_MATROX_MULTIHEAD
registered = 1;
#endif
matroxfb_register_device(MINFO);
return 0;
}
#ifdef CONFIG_FB_MATROX_MULTIHEAD
kfree(minfo);
#endif
return -1;
}
 
static void pci_remove_matrox(struct pci_dev* pdev) {
struct matrox_fb_info* minfo;
 
minfo = pci_get_drvdata(pdev);
matroxfb_remove(PMINFO 1);
}
 
static struct pci_device_id matroxfb_devices[] = {
#ifdef CONFIG_FB_MATROX_MILLENIUM
{PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL_2,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL_2_AGP,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
#endif
#ifdef CONFIG_FB_MATROX_MYSTIQUE
{PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MYS,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
#endif
#ifdef CONFIG_FB_MATROX_G100
{PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G100_MM,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G100_AGP,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G200_PCI,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G200_AGP,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G400,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G550,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
#endif
{0, 0,
0, 0, 0, 0, 0}
};
 
MODULE_DEVICE_TABLE(pci, matroxfb_devices);
 
 
static struct pci_driver matroxfb_driver = {
.name = "matroxfb",
.id_table = matroxfb_devices,
.probe = matroxfb_probe,
.remove = pci_remove_matrox,
};
 
/* **************************** init-time only **************************** */
 
#define RSResolution(X) ((X) & 0x0F)
#define RS640x400 1
#define RS640x480 2
#define RS800x600 3
#define RS1024x768 4
#define RS1280x1024 5
#define RS1600x1200 6
#define RS768x576 7
#define RS960x720 8
#define RS1152x864 9
#define RS1408x1056 10
#define RS640x350 11
#define RS1056x344 12 /* 132 x 43 text */
#define RS1056x400 13 /* 132 x 50 text */
#define RS1056x480 14 /* 132 x 60 text */
#define RSNoxNo 15
/* 10-FF */
static struct { int xres, yres, left, right, upper, lower, hslen, vslen, vfreq; } timmings[] __initdata = {
{ 640, 400, 48, 16, 39, 8, 96, 2, 70 },
{ 640, 480, 48, 16, 33, 10, 96, 2, 60 },
{ 800, 600, 144, 24, 28, 8, 112, 6, 60 },
{ 1024, 768, 160, 32, 30, 4, 128, 4, 60 },
{ 1280, 1024, 224, 32, 32, 4, 136, 4, 60 },
{ 1600, 1200, 272, 48, 32, 5, 152, 5, 60 },
{ 768, 576, 144, 16, 28, 6, 112, 4, 60 },
{ 960, 720, 144, 24, 28, 8, 112, 4, 60 },
{ 1152, 864, 192, 32, 30, 4, 128, 4, 60 },
{ 1408, 1056, 256, 40, 32, 5, 144, 5, 60 },
{ 640, 350, 48, 16, 39, 8, 96, 2, 70 },
{ 1056, 344, 96, 24, 59, 44, 160, 2, 70 },
{ 1056, 400, 96, 24, 39, 8, 160, 2, 70 },
{ 1056, 480, 96, 24, 36, 12, 160, 3, 60 },
{ 0, 0, ~0, ~0, ~0, ~0, 0, 0, 0 }
};
 
#define RSCreate(X,Y) ((X) | ((Y) << 8))
static struct { unsigned int vesa; unsigned int info; } *RSptr, vesamap[] __initdata = {
/* default must be first */
{ ~0, RSCreate(RSNoxNo, RS8bpp ) },
{ 0x101, RSCreate(RS640x480, RS8bpp ) },
{ 0x100, RSCreate(RS640x400, RS8bpp ) },
{ 0x180, RSCreate(RS768x576, RS8bpp ) },
{ 0x103, RSCreate(RS800x600, RS8bpp ) },
{ 0x188, RSCreate(RS960x720, RS8bpp ) },
{ 0x105, RSCreate(RS1024x768, RS8bpp ) },
{ 0x190, RSCreate(RS1152x864, RS8bpp ) },
{ 0x107, RSCreate(RS1280x1024, RS8bpp ) },
{ 0x198, RSCreate(RS1408x1056, RS8bpp ) },
{ 0x11C, RSCreate(RS1600x1200, RS8bpp ) },
{ 0x110, RSCreate(RS640x480, RS15bpp) },
{ 0x181, RSCreate(RS768x576, RS15bpp) },
{ 0x113, RSCreate(RS800x600, RS15bpp) },
{ 0x189, RSCreate(RS960x720, RS15bpp) },
{ 0x116, RSCreate(RS1024x768, RS15bpp) },
{ 0x191, RSCreate(RS1152x864, RS15bpp) },
{ 0x119, RSCreate(RS1280x1024, RS15bpp) },
{ 0x199, RSCreate(RS1408x1056, RS15bpp) },
{ 0x11D, RSCreate(RS1600x1200, RS15bpp) },
{ 0x111, RSCreate(RS640x480, RS16bpp) },
{ 0x182, RSCreate(RS768x576, RS16bpp) },
{ 0x114, RSCreate(RS800x600, RS16bpp) },
{ 0x18A, RSCreate(RS960x720, RS16bpp) },
{ 0x117, RSCreate(RS1024x768, RS16bpp) },
{ 0x192, RSCreate(RS1152x864, RS16bpp) },
{ 0x11A, RSCreate(RS1280x1024, RS16bpp) },
{ 0x19A, RSCreate(RS1408x1056, RS16bpp) },
{ 0x11E, RSCreate(RS1600x1200, RS16bpp) },
{ 0x1B2, RSCreate(RS640x480, RS24bpp) },
{ 0x184, RSCreate(RS768x576, RS24bpp) },
{ 0x1B5, RSCreate(RS800x600, RS24bpp) },
{ 0x18C, RSCreate(RS960x720, RS24bpp) },
{ 0x1B8, RSCreate(RS1024x768, RS24bpp) },
{ 0x194, RSCreate(RS1152x864, RS24bpp) },
{ 0x1BB, RSCreate(RS1280x1024, RS24bpp) },
{ 0x19C, RSCreate(RS1408x1056, RS24bpp) },
{ 0x1BF, RSCreate(RS1600x1200, RS24bpp) },
{ 0x112, RSCreate(RS640x480, RS32bpp) },
{ 0x183, RSCreate(RS768x576, RS32bpp) },
{ 0x115, RSCreate(RS800x600, RS32bpp) },
{ 0x18B, RSCreate(RS960x720, RS32bpp) },
{ 0x118, RSCreate(RS1024x768, RS32bpp) },
{ 0x193, RSCreate(RS1152x864, RS32bpp) },
{ 0x11B, RSCreate(RS1280x1024, RS32bpp) },
{ 0x19B, RSCreate(RS1408x1056, RS32bpp) },
{ 0x11F, RSCreate(RS1600x1200, RS32bpp) },
{ 0x010, RSCreate(RS640x350, RS4bpp ) },
{ 0x012, RSCreate(RS640x480, RS4bpp ) },
{ 0x102, RSCreate(RS800x600, RS4bpp ) },
{ 0x104, RSCreate(RS1024x768, RS4bpp ) },
{ 0x106, RSCreate(RS1280x1024, RS4bpp ) },
{ 0, 0 }};
 
static void __init matroxfb_init_params(void) {
/* fh from kHz to Hz */
if (fh < 1000)
fh *= 1000; /* 1kHz minimum */
/* maxclk */
if (maxclk < 1000) maxclk *= 1000; /* kHz -> Hz, MHz -> kHz */
if (maxclk < 1000000) maxclk *= 1000; /* kHz -> Hz, 1MHz minimum */
/* fix VESA number */
if (vesa != ~0)
vesa &= 0x1DFF; /* mask out clearscreen, acceleration and so on */
 
/* static settings */
for (RSptr = vesamap; RSptr->vesa; RSptr++) {
if (RSptr->vesa == vesa) break;
}
if (!RSptr->vesa) {
printk(KERN_ERR "Invalid vesa mode 0x%04X\n", vesa);
RSptr = vesamap;
}
{
int res = RSResolution(RSptr->info)-1;
if (left == ~0)
left = timmings[res].left;
if (!xres)
xres = timmings[res].xres;
if (right == ~0)
right = timmings[res].right;
if (!hslen)
hslen = timmings[res].hslen;
if (upper == ~0)
upper = timmings[res].upper;
if (!yres)
yres = timmings[res].yres;
if (lower == ~0)
lower = timmings[res].lower;
if (!vslen)
vslen = timmings[res].vslen;
if (!(fv||fh||maxclk||pixclock))
fv = timmings[res].vfreq;
if (depth == -1)
depth = RSDepth(RSptr->info);
}
}
 
static void __init matrox_init(void) {
matroxfb_init_params();
pci_register_driver(&matroxfb_driver);
dev = -1; /* accept all new devices... */
}
 
/* **************************** exit-time only **************************** */
 
static void __exit matrox_done(void) {
pci_unregister_driver(&matroxfb_driver);
}
 
#ifndef MODULE
 
/* ************************* init in-kernel code ************************** */
 
int __init matroxfb_setup(char *options) {
char *this_opt;
 
DBG(__FUNCTION__)
 
if (!options || !*options)
return 0;
 
while ((this_opt = strsep(&options, ",")) != NULL) {
if (!*this_opt) continue;
 
dprintk("matroxfb_setup: option %s\n", this_opt);
 
if (!strncmp(this_opt, "dev:", 4))
dev = simple_strtoul(this_opt+4, NULL, 0);
else if (!strncmp(this_opt, "depth:", 6)) {
switch (simple_strtoul(this_opt+6, NULL, 0)) {
case 0: depth = RSText; break;
case 4: depth = RS4bpp; break;
case 8: depth = RS8bpp; break;
case 15:depth = RS15bpp; break;
case 16:depth = RS16bpp; break;
case 24:depth = RS24bpp; break;
case 32:depth = RS32bpp; break;
default:
printk(KERN_ERR "matroxfb: unsupported color depth\n");
}
} else if (!strncmp(this_opt, "xres:", 5))
xres = simple_strtoul(this_opt+5, NULL, 0);
else if (!strncmp(this_opt, "yres:", 5))
yres = simple_strtoul(this_opt+5, NULL, 0);
else if (!strncmp(this_opt, "vslen:", 6))
vslen = simple_strtoul(this_opt+6, NULL, 0);
else if (!strncmp(this_opt, "hslen:", 6))
hslen = simple_strtoul(this_opt+6, NULL, 0);
else if (!strncmp(this_opt, "left:", 5))
left = simple_strtoul(this_opt+5, NULL, 0);
else if (!strncmp(this_opt, "right:", 6))
right = simple_strtoul(this_opt+6, NULL, 0);
else if (!strncmp(this_opt, "upper:", 6))
upper = simple_strtoul(this_opt+6, NULL, 0);
else if (!strncmp(this_opt, "lower:", 6))
lower = simple_strtoul(this_opt+6, NULL, 0);
else if (!strncmp(this_opt, "pixclock:", 9))
pixclock = simple_strtoul(this_opt+9, NULL, 0);
else if (!strncmp(this_opt, "sync:", 5))
sync = simple_strtoul(this_opt+5, NULL, 0);
else if (!strncmp(this_opt, "vesa:", 5))
vesa = simple_strtoul(this_opt+5, NULL, 0);
else if (!strncmp(this_opt, "maxclk:", 7))
maxclk = simple_strtoul(this_opt+7, NULL, 0);
else if (!strncmp(this_opt, "fh:", 3))
fh = simple_strtoul(this_opt+3, NULL, 0);
else if (!strncmp(this_opt, "fv:", 3))
fv = simple_strtoul(this_opt+3, NULL, 0);
else if (!strncmp(this_opt, "mem:", 4))
mem = simple_strtoul(this_opt+4, NULL, 0);
else if (!strncmp(this_opt, "mode:", 5))
strncpy(videomode, this_opt+5, sizeof(videomode));
else if (!strncmp(this_opt, "dfp:", 4)) {
dfp_type = simple_strtoul(this_opt+4, NULL, 0);
dfp = 1;
}
#ifdef CONFIG_PPC_PMAC
else if (!strncmp(this_opt, "vmode:", 6)) {
unsigned int vmode = simple_strtoul(this_opt+6, NULL, 0);
if (vmode > 0 && vmode <= VMODE_MAX)
default_vmode = vmode;
} else if (!strncmp(this_opt, "cmode:", 6)) {
unsigned int cmode = simple_strtoul(this_opt+6, NULL, 0);
switch (cmode) {
case 0:
case 8:
default_cmode = CMODE_8;
break;
case 15:
case 16:
default_cmode = CMODE_16;
break;
case 24:
case 32:
default_cmode = CMODE_32;
break;
}
}
#endif
else if (!strcmp(this_opt, "disabled")) /* nodisabled does not exist */
disabled = 1;
else if (!strcmp(this_opt, "enabled")) /* noenabled does not exist */
disabled = 0;
else if (!strcmp(this_opt, "sgram")) /* nosgram == sdram */
sgram = 1;
else if (!strcmp(this_opt, "sdram"))
sgram = 0;
else if (!strncmp(this_opt, "memtype:", 8))
memtype = simple_strtoul(this_opt+8, NULL, 0);
else {
int value = 1;
 
if (!strncmp(this_opt, "no", 2)) {
value = 0;
this_opt += 2;
}
if (! strcmp(this_opt, "inverse"))
inverse = value;
else if (!strcmp(this_opt, "accel"))
noaccel = !value;
else if (!strcmp(this_opt, "pan"))
nopan = !value;
else if (!strcmp(this_opt, "pciretry"))
no_pci_retry = !value;
else if (!strcmp(this_opt, "vga"))
novga = !value;
else if (!strcmp(this_opt, "bios"))
nobios = !value;
else if (!strcmp(this_opt, "init"))
noinit = !value;
#ifdef CONFIG_MTRR
else if (!strcmp(this_opt, "mtrr"))
mtrr = value;
#endif
else if (!strcmp(this_opt, "inv24"))
inv24 = value;
else if (!strcmp(this_opt, "cross4MB"))
cross4MB = value;
else if (!strcmp(this_opt, "grayscale"))
grayscale = value;
else if (!strcmp(this_opt, "dfp"))
dfp = value;
else {
strncpy(videomode, this_opt, sizeof(videomode));
}
}
}
return 0;
}
 
static int __initdata initialized = 0;
 
int __init matroxfb_init(void)
{
DBG(__FUNCTION__)
 
if (disabled)
return -ENXIO;
if (!initialized) {
initialized = 1;
matrox_init();
}
hotplug = 1;
/* never return failure, user can hotplug matrox later... */
return 0;
}
 
#else
 
/* *************************** init module code **************************** */
 
MODULE_AUTHOR("(c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz>");
MODULE_DESCRIPTION("Accelerated FBDev driver for Matrox Millennium/Mystique/G100/G200/G400/G450/G550");
MODULE_LICENSE("GPL");
 
MODULE_PARM(mem, "i");
MODULE_PARM_DESC(mem, "Size of available memory in MB, KB or B (2,4,8,12,16MB, default=autodetect)");
MODULE_PARM(disabled, "i");
MODULE_PARM_DESC(disabled, "Disabled (0 or 1=disabled) (default=0)");
MODULE_PARM(noaccel, "i");
MODULE_PARM_DESC(noaccel, "Do not use accelerating engine (0 or 1=disabled) (default=0)");
MODULE_PARM(nopan, "i");
MODULE_PARM_DESC(nopan, "Disable pan on startup (0 or 1=disabled) (default=0)");
MODULE_PARM(no_pci_retry, "i");
MODULE_PARM_DESC(no_pci_retry, "PCI retries enabled (0 or 1=disabled) (default=0)");
MODULE_PARM(novga, "i");
MODULE_PARM_DESC(novga, "VGA I/O (0x3C0-0x3DF) disabled (0 or 1=disabled) (default=0)");
MODULE_PARM(nobios, "i");
MODULE_PARM_DESC(nobios, "Disables ROM BIOS (0 or 1=disabled) (default=do not change BIOS state)");
MODULE_PARM(noinit, "i");
MODULE_PARM_DESC(noinit, "Disables W/SG/SD-RAM and bus interface initialization (0 or 1=do not initialize) (default=0)");
MODULE_PARM(memtype, "i");
MODULE_PARM_DESC(memtype, "Memory type for G200/G400 (see Documentation/fb/matroxfb.txt for explanation) (default=3 for G200, 0 for G400)");
MODULE_PARM(mtrr, "i");
MODULE_PARM_DESC(mtrr, "This speeds up video memory accesses (0=disabled or 1) (default=1)");
MODULE_PARM(sgram, "i");
MODULE_PARM_DESC(sgram, "Indicates that G100/G200/G400 has SGRAM memory (0=SDRAM, 1=SGRAM) (default=0)");
MODULE_PARM(inv24, "i");
MODULE_PARM_DESC(inv24, "Inverts clock polarity for 24bpp and loop frequency > 100MHz (default=do not invert polarity)");
MODULE_PARM(inverse, "i");
MODULE_PARM_DESC(inverse, "Inverse (0 or 1) (default=0)");
#ifdef CONFIG_FB_MATROX_MULTIHEAD
MODULE_PARM(dev, "i");
MODULE_PARM_DESC(dev, "Multihead support, attach to device ID (0..N) (default=all working)");
#else
MODULE_PARM(dev, "i");
MODULE_PARM_DESC(dev, "Multihead support, attach to device ID (0..N) (default=first working)");
#endif
MODULE_PARM(vesa, "i");
MODULE_PARM_DESC(vesa, "Startup videomode (0x000-0x1FF) (default=0x101)");
MODULE_PARM(xres, "i");
MODULE_PARM_DESC(xres, "Horizontal resolution (px), overrides xres from vesa (default=vesa)");
MODULE_PARM(yres, "i");
MODULE_PARM_DESC(yres, "Vertical resolution (scans), overrides yres from vesa (default=vesa)");
MODULE_PARM(upper, "i");
MODULE_PARM_DESC(upper, "Upper blank space (scans), overrides upper from vesa (default=vesa)");
MODULE_PARM(lower, "i");
MODULE_PARM_DESC(lower, "Lower blank space (scans), overrides lower from vesa (default=vesa)");
MODULE_PARM(vslen, "i");
MODULE_PARM_DESC(vslen, "Vertical sync length (scans), overrides lower from vesa (default=vesa)");
MODULE_PARM(left, "i");
MODULE_PARM_DESC(left, "Left blank space (px), overrides left from vesa (default=vesa)");
MODULE_PARM(right, "i");
MODULE_PARM_DESC(right, "Right blank space (px), overrides right from vesa (default=vesa)");
MODULE_PARM(hslen, "i");
MODULE_PARM_DESC(hslen, "Horizontal sync length (px), overrides hslen from vesa (default=vesa)");
MODULE_PARM(pixclock, "i");
MODULE_PARM_DESC(pixclock, "Pixelclock (ns), overrides pixclock from vesa (default=vesa)");
MODULE_PARM(sync, "i");
MODULE_PARM_DESC(sync, "Sync polarity, overrides sync from vesa (default=vesa)");
MODULE_PARM(depth, "i");
MODULE_PARM_DESC(depth, "Color depth (0=text,8,15,16,24,32) (default=vesa)");
MODULE_PARM(maxclk, "i");
MODULE_PARM_DESC(maxclk, "Startup maximal clock, 0-999MHz, 1000-999999kHz, 1000000-INF Hz");
MODULE_PARM(fh, "i");
MODULE_PARM_DESC(fh, "Startup horizontal frequency, 0-999kHz, 1000-INF Hz");
MODULE_PARM(fv, "i");
MODULE_PARM_DESC(fv, "Startup vertical frequency, 0-INF Hz\n"
"You should specify \"fv:max_monitor_vsync,fh:max_monitor_hsync,maxclk:max_monitor_dotclock\"\n");
MODULE_PARM(grayscale, "i");
MODULE_PARM_DESC(grayscale, "Sets display into grayscale. Works perfectly with paletized videomode (4, 8bpp), some limitations apply to 16, 24 and 32bpp videomodes (default=nograyscale)");
MODULE_PARM(cross4MB, "i");
MODULE_PARM_DESC(cross4MB, "Specifies that 4MB boundary can be in middle of line. (default=autodetected)");
MODULE_PARM(dfp, "i");
MODULE_PARM_DESC(dfp, "Specifies whether to use digital flat panel interface of G200/G400 (0 or 1) (default=0)");
MODULE_PARM(dfp_type, "i");
MODULE_PARM_DESC(dfp_type, "Specifies DFP interface type (0 to 255) (default=read from hardware)");
#ifdef CONFIG_PPC_PMAC
MODULE_PARM(vmode, "i");
MODULE_PARM_DESC(vmode, "Specify the vmode mode number that should be used (640x480 default)");
MODULE_PARM(cmode, "i");
MODULE_PARM_DESC(cmode, "Specify the video depth that should be used (8bit default)");
#endif
 
int __init init_module(void){
 
DBG(__FUNCTION__)
 
if (disabled)
return -ENXIO;
 
if (depth == 0)
depth = RSText;
else if (depth == 4)
depth = RS4bpp;
else if (depth == 8)
depth = RS8bpp;
else if (depth == 15)
depth = RS15bpp;
else if (depth == 16)
depth = RS16bpp;
else if (depth == 24)
depth = RS24bpp;
else if (depth == 32)
depth = RS32bpp;
else if (depth != -1) {
printk(KERN_ERR "matroxfb: depth %d is not supported, using default\n", depth);
depth = -1;
}
matrox_init();
/* never return failure; user can hotplug matrox later... */
return 0;
}
#endif /* MODULE */
 
module_exit(matrox_done);
EXPORT_SYMBOL(matroxfb_register_driver);
EXPORT_SYMBOL(matroxfb_unregister_driver);
EXPORT_SYMBOL(matroxfb_wait_for_sync);
EXPORT_SYMBOL(matroxfb_enable_irq);
 
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* ---------------------------------------------------------------------------
* Local variables:
* c-basic-offset: 8
* End:
*/
 
/shark/trunk/drivers/fb/matrox/matroxfb_maven.h
0,0 → 1,20
#ifndef __MATROXFB_MAVEN_H__
#define __MATROXFB_MAVEN_H__
 
#include <linux/ioctl.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include "matroxfb_base.h"
 
struct i2c_bit_adapter {
struct i2c_adapter adapter;
int initialized;
struct i2c_algo_bit_data bac;
struct matrox_fb_info* minfo;
struct {
unsigned int data;
unsigned int clock;
} mask;
};
 
#endif /* __MATROXFB_MAVEN_H__ */
/shark/trunk/drivers/fb/matrox/matroxfb_accel.h
0,0 → 1,8
#ifndef __MATROXFB_ACCEL_H__
#define __MATROXFB_ACCEL_H__
 
#include "matroxfb_base.h"
 
void matrox_cfbX_init(WPMINFO2);
 
#endif
/shark/trunk/drivers/fb/matrox/matroxfb_crtc2.c
0,0 → 1,748
/*
*
* Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200, G400 and G450.
*
* (c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz>
*
* Portions Copyright (c) 2001 Matrox Graphics Inc.
*
* Version: 1.65 2002/08/14
*
*/
 
#include <linuxcomp.h>
 
#include "matroxfb_maven.h"
#include "matroxfb_crtc2.h"
#include "matroxfb_misc.h"
#include "matroxfb_DAC1064.h"
#include <linux/matroxfb.h>
#include <asm/uaccess.h>
 
/* **************************************************** */
 
static int mem = 8192;
 
MODULE_PARM(mem, "i");
MODULE_PARM_DESC(mem, "Memory size reserved for dualhead (default=8MB)");
 
/* **************************************************** */
 
static int matroxfb_dh_setcolreg(unsigned regno, unsigned red, unsigned green,
unsigned blue, unsigned transp, struct fb_info* info) {
u_int32_t col;
#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
 
if (regno >= 16)
return 1;
if (m2info->fbcon.var.grayscale) {
/* gray = 0.30*R + 0.59*G + 0.11*B */
red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
}
red = CNVT_TOHW(red, m2info->fbcon.var.red.length);
green = CNVT_TOHW(green, m2info->fbcon.var.green.length);
blue = CNVT_TOHW(blue, m2info->fbcon.var.blue.length);
transp = CNVT_TOHW(transp, m2info->fbcon.var.transp.length);
 
col = (red << m2info->fbcon.var.red.offset) |
(green << m2info->fbcon.var.green.offset) |
(blue << m2info->fbcon.var.blue.offset) |
(transp << m2info->fbcon.var.transp.offset);
 
switch (m2info->fbcon.var.bits_per_pixel) {
case 16:
m2info->cmap[regno] = col | (col << 16);
break;
case 32:
m2info->cmap[regno] = col;
break;
}
return 0;
#undef m2info
}
 
static void matroxfb_dh_restore(struct matroxfb_dh_fb_info* m2info,
struct my_timming* mt,
int mode,
unsigned int pos) {
u_int32_t tmp;
u_int32_t datactl;
MINFO_FROM(m2info->primary_dev);
 
switch (mode) {
case 15:
tmp = 0x00200000;
break;
case 16:
tmp = 0x00400000;
break;
/* case 32: */
default:
tmp = 0x00800000;
break;
}
tmp |= 0x00000001; /* enable CRTC2 */
datactl = 0;
if (ACCESS_FBINFO(outputs[1]).src == MATROXFB_SRC_CRTC2) {
if (ACCESS_FBINFO(devflags.g450dac)) {
tmp |= 0x00000006; /* source from secondary pixel PLL */
/* no vidrst when in monitor mode */
if (ACCESS_FBINFO(outputs[1]).mode != MATROXFB_OUTPUT_MODE_MONITOR) {
tmp |= 0xC0001000; /* Enable H/V vidrst */
}
} else {
tmp |= 0x00000002; /* source from VDOCLK */
tmp |= 0xC0000000; /* enable vvidrst & hvidrst */
/* MGA TVO is our clock source */
}
} else if (ACCESS_FBINFO(outputs[0]).src == MATROXFB_SRC_CRTC2) {
tmp |= 0x00000004; /* source from pixclock */
/* PIXPLL is our clock source */
}
if (ACCESS_FBINFO(outputs[0]).src == MATROXFB_SRC_CRTC2) {
tmp |= 0x00100000; /* connect CRTC2 to DAC */
}
if (mt->interlaced) {
tmp |= 0x02000000; /* interlaced, second field is bigger, as G450 apparently ignores it */
mt->VDisplay >>= 1;
mt->VSyncStart >>= 1;
mt->VSyncEnd >>= 1;
mt->VTotal >>= 1;
}
if ((mt->HTotal & 7) == 2) {
datactl |= 0x00000010;
mt->HTotal &= ~7;
}
tmp |= 0x10000000; /* 0x10000000 is VIDRST polarity */
mga_outl(0x3C14, ((mt->HDisplay - 8) << 16) | (mt->HTotal - 8));
mga_outl(0x3C18, ((mt->HSyncEnd - 8) << 16) | (mt->HSyncStart - 8));
mga_outl(0x3C1C, ((mt->VDisplay - 1) << 16) | (mt->VTotal - 1));
mga_outl(0x3C20, ((mt->VSyncEnd - 1) << 16) | (mt->VSyncStart - 1));
mga_outl(0x3C24, ((mt->VSyncStart) << 16) | (mt->HSyncStart)); /* preload */
{
u_int32_t linelen = m2info->fbcon.var.xres_virtual * (m2info->fbcon.var.bits_per_pixel >> 3);
if (tmp & 0x02000000) {
/* field #0 is smaller, so... */
mga_outl(0x3C2C, pos); /* field #1 vmemory start */
mga_outl(0x3C28, pos + linelen); /* field #0 vmemory start */
linelen <<= 1;
m2info->interlaced = 1;
} else {
mga_outl(0x3C28, pos); /* vmemory start */
m2info->interlaced = 0;
}
mga_outl(0x3C40, linelen);
}
mga_outl(0x3C4C, datactl); /* data control */
if (tmp & 0x02000000) {
int i;
 
mga_outl(0x3C10, tmp & ~0x02000000);
for (i = 0; i < 2; i++) {
unsigned int nl;
unsigned int lastl = 0;
 
while ((nl = mga_inl(0x3C48) & 0xFFF) >= lastl) {
lastl = nl;
}
}
}
mga_outl(0x3C10, tmp);
ACCESS_FBINFO(hw).crtc2.ctl = tmp;
 
tmp = mt->VDisplay << 16; /* line compare */
if (mt->sync & FB_SYNC_HOR_HIGH_ACT)
tmp |= 0x00000100;
if (mt->sync & FB_SYNC_VERT_HIGH_ACT)
tmp |= 0x00000200;
mga_outl(0x3C44, tmp);
}
 
static void matroxfb_dh_disable(struct matroxfb_dh_fb_info* m2info) {
MINFO_FROM(m2info->primary_dev);
 
mga_outl(0x3C10, 0x00000004); /* disable CRTC2, CRTC1->DAC1, PLL as clock source */
ACCESS_FBINFO(hw).crtc2.ctl = 0x00000004;
}
 
static void matroxfb_dh_cfbX_init(struct matroxfb_dh_fb_info* m2info) {
/* no acceleration for secondary head... */
m2info->cmap[16] = 0xFFFFFFFF;
}
 
static void matroxfb_dh_pan_var(struct matroxfb_dh_fb_info* m2info,
struct fb_var_screeninfo* var) {
unsigned int pos;
unsigned int linelen;
unsigned int pixelsize;
MINFO_FROM(m2info->primary_dev);
 
m2info->fbcon.var.xoffset = var->xoffset;
m2info->fbcon.var.yoffset = var->yoffset;
pixelsize = m2info->fbcon.var.bits_per_pixel >> 3;
linelen = m2info->fbcon.var.xres_virtual * pixelsize;
pos = m2info->fbcon.var.yoffset * linelen + m2info->fbcon.var.xoffset * pixelsize;
pos += m2info->video.offbase;
if (m2info->interlaced) {
mga_outl(0x3C2C, pos);
mga_outl(0x3C28, pos + linelen);
} else {
mga_outl(0x3C28, pos);
}
}
 
static int matroxfb_dh_decode_var(struct matroxfb_dh_fb_info* m2info,
struct fb_var_screeninfo* var,
int *visual,
int *video_cmap_len,
int *mode) {
unsigned int mask;
unsigned int memlen;
unsigned int vramlen;
 
switch (var->bits_per_pixel) {
case 16: mask = 0x1F;
break;
case 32: mask = 0x0F;
break;
default: return -EINVAL;
}
vramlen = m2info->video.len_usable;
if (var->yres_virtual < var->yres)
var->yres_virtual = var->yres;
if (var->xres_virtual < var->xres)
var->xres_virtual = var->xres;
var->xres_virtual = (var->xres_virtual + mask) & ~mask;
if (var->yres_virtual > 32767)
return -EINVAL;
memlen = var->xres_virtual * var->yres_virtual * (var->bits_per_pixel >> 3);
if (memlen > vramlen)
return -EINVAL;
if (var->xoffset + var->xres > var->xres_virtual)
var->xoffset = var->xres_virtual - var->xres;
if (var->yoffset + var->yres > var->yres_virtual)
var->yoffset = var->yres_virtual - var->yres;
 
var->xres &= ~7;
var->left_margin &= ~7;
var->right_margin &= ~7;
var->hsync_len &= ~7;
 
*mode = var->bits_per_pixel;
if (var->bits_per_pixel == 16) {
if (var->green.length == 5) {
var->red.offset = 10;
var->red.length = 5;
var->green.offset = 5;
var->green.length = 5;
var->blue.offset = 0;
var->blue.length = 5;
var->transp.offset = 15;
var->transp.length = 1;
*mode = 15;
} else {
var->red.offset = 11;
var->red.length = 5;
var->green.offset = 5;
var->green.length = 6;
var->blue.offset = 0;
var->blue.length = 5;
var->transp.offset = 0;
var->transp.length = 0;
}
} else {
var->red.offset = 16;
var->red.length = 8;
var->green.offset = 8;
var->green.length = 8;
var->blue.offset = 0;
var->blue.length = 8;
var->transp.offset = 24;
var->transp.length = 8;
}
*visual = FB_VISUAL_TRUECOLOR;
*video_cmap_len = 16;
return 0;
}
 
static int matroxfb_dh_open(struct fb_info* info, int user) {
#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
MINFO_FROM(m2info->primary_dev);
 
if (MINFO) {
int err;
 
if (ACCESS_FBINFO(dead)) {
return -ENXIO;
}
err = ACCESS_FBINFO(fbops).fb_open(&ACCESS_FBINFO(fbcon), user);
if (err) {
return err;
}
}
return 0;
#undef m2info
}
 
static int matroxfb_dh_release(struct fb_info* info, int user) {
#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
int err = 0;
MINFO_FROM(m2info->primary_dev);
 
if (MINFO) {
err = ACCESS_FBINFO(fbops).fb_release(&ACCESS_FBINFO(fbcon), user);
}
return err;
#undef m2info
}
 
static void matroxfb_dh_init_fix(struct matroxfb_dh_fb_info *m2info) {
struct fb_fix_screeninfo *fix = &m2info->fbcon.fix;
 
strcpy(fix->id, "MATROX DH");
 
fix->smem_start = m2info->video.base;
fix->smem_len = m2info->video.len_usable;
fix->ypanstep = 1;
fix->ywrapstep = 0;
fix->xpanstep = 8; /* TBD */
fix->mmio_start = m2info->mmio.base;
fix->mmio_len = m2info->mmio.len;
fix->accel = 0; /* no accel... */
}
 
static int matroxfb_dh_check_var(struct fb_var_screeninfo* var, struct fb_info* info) {
#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
int visual;
int cmap_len;
int mode;
 
return matroxfb_dh_decode_var(m2info, var, &visual, &cmap_len, &mode);
#undef m2info
}
 
static int matroxfb_dh_set_par(struct fb_info* info) {
#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
int visual;
int cmap_len;
int mode;
int err;
struct fb_var_screeninfo* var = &info->var;
MINFO_FROM(m2info->primary_dev);
 
if ((err = matroxfb_dh_decode_var(m2info, var, &visual, &cmap_len, &mode)) != 0)
return err;
/* cmap */
{
m2info->fbcon.screen_base = vaddr_va(m2info->video.vbase);
m2info->fbcon.fix.visual = visual;
m2info->fbcon.fix.type = FB_TYPE_PACKED_PIXELS;
m2info->fbcon.fix.type_aux = 0;
m2info->fbcon.fix.line_length = (var->xres_virtual * var->bits_per_pixel) >> 3;
}
{
struct my_timming mt;
unsigned int pos;
int out;
int cnt;
 
matroxfb_var2my(&m2info->fbcon.var, &mt);
mt.crtc = MATROXFB_SRC_CRTC2;
/* CRTC2 delay */
mt.delay = 34;
 
pos = (m2info->fbcon.var.yoffset * m2info->fbcon.var.xres_virtual + m2info->fbcon.var.xoffset) * m2info->fbcon.var.bits_per_pixel >> 3;
pos += m2info->video.offbase;
cnt = 0;
down_read(&ACCESS_FBINFO(altout).lock);
for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
if (ACCESS_FBINFO(outputs[out]).src == MATROXFB_SRC_CRTC2) {
cnt++;
if (ACCESS_FBINFO(outputs[out]).output->compute) {
ACCESS_FBINFO(outputs[out]).output->compute(ACCESS_FBINFO(outputs[out]).data, &mt);
}
}
}
ACCESS_FBINFO(crtc2).pixclock = mt.pixclock;
ACCESS_FBINFO(crtc2).mnp = mt.mnp;
up_read(&ACCESS_FBINFO(altout).lock);
if (cnt) {
matroxfb_dh_restore(m2info, &mt, mode, pos);
} else {
matroxfb_dh_disable(m2info);
}
DAC1064_global_init(PMINFO2);
DAC1064_global_restore(PMINFO2);
down_read(&ACCESS_FBINFO(altout).lock);
for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
if (ACCESS_FBINFO(outputs[out]).src == MATROXFB_SRC_CRTC2 &&
ACCESS_FBINFO(outputs[out]).output->program) {
ACCESS_FBINFO(outputs[out]).output->program(ACCESS_FBINFO(outputs[out]).data);
}
}
for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
if (ACCESS_FBINFO(outputs[out]).src == MATROXFB_SRC_CRTC2 &&
ACCESS_FBINFO(outputs[out]).output->start) {
ACCESS_FBINFO(outputs[out]).output->start(ACCESS_FBINFO(outputs[out]).data);
}
}
up_read(&ACCESS_FBINFO(altout).lock);
matroxfb_dh_cfbX_init(m2info);
}
return 0;
#undef m2info
}
 
static int matroxfb_dh_pan_display(struct fb_var_screeninfo* var, struct fb_info* info) {
#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
matroxfb_dh_pan_var(m2info, var);
return 0;
#undef m2info
}
 
static int matroxfb_dh_get_vblank(const struct matroxfb_dh_fb_info* m2info, struct fb_vblank* vblank) {
MINFO_FROM(m2info->primary_dev);
 
matroxfb_enable_irq(PMINFO 0);
memset(vblank, 0, sizeof(*vblank));
vblank->flags = FB_VBLANK_HAVE_VCOUNT | FB_VBLANK_HAVE_VBLANK;
/* mask out reserved bits + field number (odd/even) */
vblank->vcount = mga_inl(0x3C48) & 0x000007FF;
/* compatibility stuff */
if (vblank->vcount >= m2info->fbcon.var.yres)
vblank->flags |= FB_VBLANK_VBLANKING;
if (test_bit(0, &ACCESS_FBINFO(irq_flags))) {
vblank->flags |= FB_VBLANK_HAVE_COUNT;
/* Only one writer, aligned int value...
it should work without lock and without atomic_t */
vblank->count = ACCESS_FBINFO(crtc2).vsync.cnt;
}
return 0;
}
 
static int matroxfb_dh_ioctl(struct inode* inode,
struct file* file,
unsigned int cmd,
unsigned long arg,
struct fb_info* info) {
#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
MINFO_FROM(m2info->primary_dev);
 
DBG(__FUNCTION__)
 
switch (cmd) {
case FBIOGET_VBLANK:
{
struct fb_vblank vblank;
int err;
 
err = matroxfb_dh_get_vblank(m2info, &vblank);
if (err)
return err;
if (copy_to_user((struct fb_vblank*)arg, &vblank, sizeof(vblank)))
return -EFAULT;
return 0;
}
case FBIO_WAITFORVSYNC:
{
u_int32_t crt;
 
if (get_user(crt, (u_int32_t *)arg))
return -EFAULT;
 
if (crt != 0)
return -ENODEV;
return matroxfb_wait_for_sync(PMINFO 1);
}
case MATROXFB_SET_OUTPUT_MODE:
case MATROXFB_GET_OUTPUT_MODE:
case MATROXFB_GET_ALL_OUTPUTS:
{
return ACCESS_FBINFO(fbcon.fbops)->fb_ioctl(inode, file, cmd, arg, &ACCESS_FBINFO(fbcon));
}
case MATROXFB_SET_OUTPUT_CONNECTION:
{
u_int32_t tmp;
int out;
int changes;
 
if (get_user(tmp, (u_int32_t*)arg))
return -EFAULT;
for (out = 0; out < 32; out++) {
if (tmp & (1 << out)) {
if (out >= MATROXFB_MAX_OUTPUTS)
return -ENXIO;
if (!ACCESS_FBINFO(outputs[out]).output)
return -ENXIO;
switch (ACCESS_FBINFO(outputs[out]).src) {
case MATROXFB_SRC_NONE:
case MATROXFB_SRC_CRTC2:
break;
default:
return -EBUSY;
}
}
}
if (ACCESS_FBINFO(devflags.panellink)) {
if (tmp & MATROXFB_OUTPUT_CONN_DFP)
return -EINVAL;
if ((ACCESS_FBINFO(outputs[2]).src == MATROXFB_SRC_CRTC1) && tmp)
return -EBUSY;
}
changes = 0;
for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
if (tmp & (1 << out)) {
if (ACCESS_FBINFO(outputs[out]).src != MATROXFB_SRC_CRTC2) {
changes = 1;
ACCESS_FBINFO(outputs[out]).src = MATROXFB_SRC_CRTC2;
}
} else if (ACCESS_FBINFO(outputs[out]).src == MATROXFB_SRC_CRTC2) {
changes = 1;
ACCESS_FBINFO(outputs[out]).src = MATROXFB_SRC_NONE;
}
}
if (!changes)
return 0;
matroxfb_dh_set_par(info);
return 0;
}
case MATROXFB_GET_OUTPUT_CONNECTION:
{
u_int32_t conn = 0;
int out;
 
for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
if (ACCESS_FBINFO(outputs[out]).src == MATROXFB_SRC_CRTC2) {
conn |= 1 << out;
}
}
if (put_user(conn, (u_int32_t*)arg))
return -EFAULT;
return 0;
}
case MATROXFB_GET_AVAILABLE_OUTPUTS:
{
u_int32_t tmp = 0;
int out;
 
for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
if (ACCESS_FBINFO(outputs[out]).output) {
switch (ACCESS_FBINFO(outputs[out]).src) {
case MATROXFB_SRC_NONE:
case MATROXFB_SRC_CRTC2:
tmp |= 1 << out;
break;
}
}
}
if (ACCESS_FBINFO(devflags.panellink)) {
tmp &= ~MATROXFB_OUTPUT_CONN_DFP;
if (ACCESS_FBINFO(outputs[2]).src == MATROXFB_SRC_CRTC1) {
tmp = 0;
}
}
if (put_user(tmp, (u_int32_t*)arg))
return -EFAULT;
return 0;
}
}
return -ENOTTY;
#undef m2info
}
 
static int matroxfb_dh_blank(int blank, struct fb_info* info) {
#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
switch (blank) {
case 1:
case 2:
case 3:
case 4:
default:;
}
/* do something... */
return 0;
#undef m2info
}
 
static struct fb_ops matroxfb_dh_ops = {
.owner = THIS_MODULE,
.fb_open = matroxfb_dh_open,
.fb_release = matroxfb_dh_release,
.fb_check_var = matroxfb_dh_check_var,
.fb_set_par = matroxfb_dh_set_par,
.fb_setcolreg = matroxfb_dh_setcolreg,
.fb_pan_display =matroxfb_dh_pan_display,
.fb_blank = matroxfb_dh_blank,
.fb_ioctl = matroxfb_dh_ioctl,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
.fb_cursor = soft_cursor,
};
 
static struct fb_var_screeninfo matroxfb_dh_defined = {
640,480,640,480,/* W,H, virtual W,H */
0,0, /* offset */
32, /* depth */
0, /* gray */
{0,0,0}, /* R */
{0,0,0}, /* G */
{0,0,0}, /* B */
{0,0,0}, /* alpha */
0, /* nonstd */
FB_ACTIVATE_NOW,
-1,-1, /* display size */
0, /* accel flags */
39721L,48L,16L,33L,10L,
96L,2,0, /* no sync info */
FB_VMODE_NONINTERLACED,
0, {0,0,0,0,0}
};
 
static int matroxfb_dh_regit(CPMINFO struct matroxfb_dh_fb_info* m2info) {
#define minfo (m2info->primary_dev)
void* oldcrtc2;
 
m2info->fbcon.fbops = &matroxfb_dh_ops;
m2info->fbcon.flags = FBINFO_FLAG_DEFAULT;
m2info->fbcon.currcon = -1;
m2info->fbcon.pseudo_palette = m2info->cmap;
fb_alloc_cmap(&m2info->fbcon.cmap, 256, 1);
 
if (mem < 64)
mem *= 1024;
if (mem < 64*1024)
mem *= 1024;
mem &= ~0x00000FFF; /* PAGE_MASK? */
if (ACCESS_FBINFO(video.len_usable) + mem <= ACCESS_FBINFO(video.len))
m2info->video.offbase = ACCESS_FBINFO(video.len) - mem;
else if (ACCESS_FBINFO(video.len) < mem) {
return -ENOMEM;
} else { /* check yres on first head... */
m2info->video.borrowed = mem;
ACCESS_FBINFO(video.len_usable) -= mem;
m2info->video.offbase = ACCESS_FBINFO(video.len_usable);
}
m2info->video.base = ACCESS_FBINFO(video.base) + m2info->video.offbase;
m2info->video.len = m2info->video.len_usable = m2info->video.len_maximum = mem;
m2info->video.vbase.vaddr = vaddr_va(ACCESS_FBINFO(video.vbase)) + m2info->video.offbase;
m2info->mmio.base = ACCESS_FBINFO(mmio.base);
m2info->mmio.vbase = ACCESS_FBINFO(mmio.vbase);
m2info->mmio.len = ACCESS_FBINFO(mmio.len);
 
/*
* If we have unused output, connect CRTC2 to it...
*/
if (ACCESS_FBINFO(outputs[1]).output &&
ACCESS_FBINFO(outputs[1]).src == MATROXFB_SRC_NONE &&
ACCESS_FBINFO(outputs[2]).src == MATROXFB_SRC_NONE) {
ACCESS_FBINFO(outputs[1]).src = MATROXFB_SRC_CRTC2;
}
 
matroxfb_dh_init_fix(m2info);
if (register_framebuffer(&m2info->fbcon)) {
return -ENXIO;
}
if (m2info->fbcon.currcon < 0) {
fb_set_var(&m2info->fbcon, &matroxfb_dh_defined);
}
down_write(&ACCESS_FBINFO(crtc2.lock));
oldcrtc2 = ACCESS_FBINFO(crtc2.info);
ACCESS_FBINFO(crtc2.info) = m2info;
up_write(&ACCESS_FBINFO(crtc2.lock));
if (oldcrtc2) {
printk(KERN_ERR "matroxfb_crtc2: Internal consistency check failed: crtc2 already present: %p\n",
oldcrtc2);
}
return 0;
#undef minfo
}
 
/* ************************** */
 
static int matroxfb_dh_registerfb(struct matroxfb_dh_fb_info* m2info) {
#define minfo (m2info->primary_dev)
if (matroxfb_dh_regit(PMINFO m2info)) {
printk(KERN_ERR "matroxfb_crtc2: secondary head failed to register\n");
return -1;
}
printk(KERN_INFO "matroxfb_crtc2: secondary head of fb%u was registered as fb%u\n",
ACCESS_FBINFO(fbcon.node), m2info->fbcon.node);
m2info->fbcon_registered = 1;
return 0;
#undef minfo
}
 
static void matroxfb_dh_deregisterfb(struct matroxfb_dh_fb_info* m2info) {
#define minfo (m2info->primary_dev)
if (m2info->fbcon_registered) {
int id;
struct matroxfb_dh_fb_info* crtc2;
 
down_write(&ACCESS_FBINFO(crtc2.lock));
crtc2 = ACCESS_FBINFO(crtc2.info);
if (crtc2 == m2info)
ACCESS_FBINFO(crtc2.info) = NULL;
up_write(&ACCESS_FBINFO(crtc2.lock));
if (crtc2 != m2info) {
printk(KERN_ERR "matroxfb_crtc2: Internal consistency check failed: crtc2 mismatch at unload: %p != %p\n",
crtc2, m2info);
printk(KERN_ERR "matroxfb_crtc2: Expect kernel crash after module unload.\n");
return;
}
id = m2info->fbcon.node;
unregister_framebuffer(&m2info->fbcon);
/* return memory back to primary head */
ACCESS_FBINFO(video.len_usable) += m2info->video.borrowed;
printk(KERN_INFO "matroxfb_crtc2: fb%u unregistered\n", id);
m2info->fbcon_registered = 0;
}
#undef minfo
}
 
static void* matroxfb_crtc2_probe(struct matrox_fb_info* minfo) {
struct matroxfb_dh_fb_info* m2info;
 
/* hardware is CRTC2 incapable... */
if (!ACCESS_FBINFO(devflags.crtc2))
return NULL;
m2info = (struct matroxfb_dh_fb_info*)kmalloc(sizeof(*m2info), GFP_KERNEL);
if (!m2info) {
printk(KERN_ERR "matroxfb_crtc2: Not enough memory for CRTC2 control structs\n");
return NULL;
}
memset(m2info, 0, sizeof(*m2info));
m2info->primary_dev = MINFO;
if (matroxfb_dh_registerfb(m2info)) {
kfree(m2info);
printk(KERN_ERR "matroxfb_crtc2: CRTC2 framebuffer failed to register\n");
return NULL;
}
return m2info;
}
 
static void matroxfb_crtc2_remove(struct matrox_fb_info* minfo, void* crtc2) {
matroxfb_dh_deregisterfb(crtc2);
kfree(crtc2);
}
 
static struct matroxfb_driver crtc2 = {
.name = "Matrox G400 CRTC2",
.probe = matroxfb_crtc2_probe,
.remove = matroxfb_crtc2_remove };
 
static int matroxfb_crtc2_init(void) {
matroxfb_register_driver(&crtc2);
return 0;
}
 
static void matroxfb_crtc2_exit(void) {
matroxfb_unregister_driver(&crtc2);
}
 
MODULE_AUTHOR("(c) 1999-2002 Petr Vandrovec <vandrove@vc.cvut.cz>");
MODULE_DESCRIPTION("Matrox G400 CRTC2 driver");
MODULE_LICENSE("GPL");
module_init(matroxfb_crtc2_init);
module_exit(matroxfb_crtc2_exit);
/* we do not have __setup() yet */
/shark/trunk/drivers/fb/matrox/matroxfb_g450.c
0,0 → 1,628
/*
*
* Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200, G400 and G450.
*
* (c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz>
*
* Portions Copyright (c) 2001 Matrox Graphics Inc.
*
* Version: 1.65 2002/08/14
*
* See matroxfb_base.c for contributors.
*
*/
 
#include <linuxcomp.h>
 
#include "matroxfb_base.h"
#include "matroxfb_misc.h"
#include "matroxfb_DAC1064.h"
#include "g450_pll.h"
#include <linux/matroxfb.h>
#include <asm/uaccess.h>
#include <asm/div64.h>
 
/* Definition of the various controls */
struct mctl {
struct v4l2_queryctrl desc;
size_t control;
};
 
#define BLMIN 0xF3
#define WLMAX 0x3FF
 
static const struct mctl g450_controls[] =
{ { { V4L2_CID_BRIGHTNESS, V4L2_CTRL_TYPE_INTEGER,
"brightness",
0, WLMAX-BLMIN, 1, 370-BLMIN,
0,
}, offsetof(struct matrox_fb_info, altout.tvo_params.brightness) },
{ { V4L2_CID_CONTRAST, V4L2_CTRL_TYPE_INTEGER,
"contrast",
0, 1023, 1, 127,
0,
}, offsetof(struct matrox_fb_info, altout.tvo_params.contrast) },
{ { V4L2_CID_SATURATION, V4L2_CTRL_TYPE_INTEGER,
"saturation",
0, 255, 1, 165,
0,
}, offsetof(struct matrox_fb_info, altout.tvo_params.saturation) },
{ { V4L2_CID_HUE, V4L2_CTRL_TYPE_INTEGER,
"hue",
0, 255, 1, 0,
0,
}, offsetof(struct matrox_fb_info, altout.tvo_params.hue) },
{ { MATROXFB_CID_TESTOUT, V4L2_CTRL_TYPE_BOOLEAN,
"test output",
0, 1, 1, 0,
0,
}, offsetof(struct matrox_fb_info, altout.tvo_params.testout) },
};
 
#define G450CTRLS (sizeof(g450_controls)/sizeof(g450_controls[0]))
 
/* Return: positive number: id found
-EINVAL: id not found, return failure
-ENOENT: id not found, create fake disabled control */
static int get_ctrl_id(__u32 v4l2_id) {
int i;
 
for (i = 0; i < G450CTRLS; i++) {
if (v4l2_id < g450_controls[i].desc.id) {
if (g450_controls[i].desc.id == 0x08000000) {
return -EINVAL;
}
return -ENOENT;
}
if (v4l2_id == g450_controls[i].desc.id) {
return i;
}
}
return -EINVAL;
}
 
static inline int* get_ctrl_ptr(WPMINFO unsigned int idx) {
return (int*)((char*)MINFO + g450_controls[idx].control);
}
 
static void tvo_fill_defaults(WPMINFO2) {
unsigned int i;
for (i = 0; i < G450CTRLS; i++) {
*get_ctrl_ptr(PMINFO i) = g450_controls[i].desc.default_value;
}
}
 
static int cve2_get_reg(WPMINFO int reg) {
unsigned long flags;
int val;
matroxfb_DAC_lock_irqsave(flags);
matroxfb_DAC_out(PMINFO 0x87, reg);
val = matroxfb_DAC_in(PMINFO 0x88);
matroxfb_DAC_unlock_irqrestore(flags);
return val;
}
 
static void cve2_set_reg(WPMINFO int reg, int val) {
unsigned long flags;
 
matroxfb_DAC_lock_irqsave(flags);
matroxfb_DAC_out(PMINFO 0x87, reg);
matroxfb_DAC_out(PMINFO 0x88, val);
matroxfb_DAC_unlock_irqrestore(flags);
}
 
static void cve2_set_reg10(WPMINFO int reg, int val) {
unsigned long flags;
 
matroxfb_DAC_lock_irqsave(flags);
matroxfb_DAC_out(PMINFO 0x87, reg);
matroxfb_DAC_out(PMINFO 0x88, val >> 2);
matroxfb_DAC_out(PMINFO 0x87, reg + 1);
matroxfb_DAC_out(PMINFO 0x88, val & 3);
matroxfb_DAC_unlock_irqrestore(flags);
}
 
static void g450_compute_bwlevel(CPMINFO int *bl, int *wl) {
int b = ACCESS_FBINFO(altout.tvo_params.brightness) + BLMIN;
int c = ACCESS_FBINFO(altout.tvo_params.contrast);
 
*bl = max(b - c, BLMIN);
*wl = min(b + c, WLMAX);
}
 
static int g450_query_ctrl(void* md, struct v4l2_queryctrl *p) {
int i;
i = get_ctrl_id(p->id);
if (i >= 0) {
*p = g450_controls[i].desc;
return 0;
}
if (i == -ENOENT) {
static const struct v4l2_queryctrl disctrl =
{ .flags = V4L2_CTRL_FLAG_DISABLED };
i = p->id;
*p = disctrl;
p->id = i;
sprintf26(p->name, "Ctrl #%08X", i);
return 0;
}
return -EINVAL;
}
 
static int g450_set_ctrl(void* md, struct v4l2_control *p) {
int i;
MINFO_FROM(md);
i = get_ctrl_id(p->id);
if (i < 0) return -EINVAL;
 
/*
* Check if changed.
*/
if (p->value == *get_ctrl_ptr(PMINFO i)) return 0;
 
/*
* Check limits.
*/
if (p->value > g450_controls[i].desc.maximum) return -EINVAL;
if (p->value < g450_controls[i].desc.minimum) return -EINVAL;
 
/*
* Store new value.
*/
*get_ctrl_ptr(PMINFO i) = p->value;
 
switch (p->id) {
case V4L2_CID_BRIGHTNESS:
case V4L2_CID_CONTRAST:
{
int blacklevel, whitelevel;
g450_compute_bwlevel(PMINFO &blacklevel, &whitelevel);
cve2_set_reg10(PMINFO 0x0e, blacklevel);
cve2_set_reg10(PMINFO 0x1e, whitelevel);
}
break;
case V4L2_CID_SATURATION:
cve2_set_reg(PMINFO 0x20, p->value);
cve2_set_reg(PMINFO 0x22, p->value);
break;
case V4L2_CID_HUE:
cve2_set_reg(PMINFO 0x25, p->value);
break;
case MATROXFB_CID_TESTOUT:
{
unsigned char val = cve2_get_reg (PMINFO 0x05);
if (p->value) val |= 0x02;
else val &= ~0x02;
cve2_set_reg(PMINFO 0x05, val);
}
break;
}
 
return 0;
}
 
static int g450_get_ctrl(void* md, struct v4l2_control *p) {
int i;
MINFO_FROM(md);
i = get_ctrl_id(p->id);
if (i < 0) return -EINVAL;
p->value = *get_ctrl_ptr(PMINFO i);
return 0;
}
 
struct output_desc {
unsigned int h_vis;
unsigned int h_f_porch;
unsigned int h_sync;
unsigned int h_b_porch;
unsigned long long int chromasc;
unsigned int burst;
unsigned int v_total;
};
 
static void computeRegs(WPMINFO struct mavenregs* r, struct my_timming* mt, const struct output_desc* outd) {
u_int32_t chromasc;
u_int32_t hlen;
u_int32_t hsl;
u_int32_t hbp;
u_int32_t hfp;
u_int32_t hvis;
unsigned int pixclock;
unsigned long long piic;
int mnp;
int over;
r->regs[0x80] = 0x03; /* | 0x40 for SCART */
 
hvis = ((mt->HDisplay << 1) + 3) & ~3;
if (hvis >= 2048) {
hvis = 2044;
}
piic = 1000000000ULL * hvis;
do_div(piic, outd->h_vis);
 
dprintk(KERN_DEBUG "Want %u kHz pixclock\n", (unsigned int)piic);
mnp = matroxfb_g450_setclk(PMINFO piic, M_VIDEO_PLL);
mt->mnp = mnp;
mt->pixclock = g450_mnp2f(PMINFO mnp);
 
dprintk(KERN_DEBUG "MNP=%08X\n", mnp);
 
pixclock = 1000000000U / mt->pixclock;
 
dprintk(KERN_DEBUG "Got %u ps pixclock\n", pixclock);
 
piic = outd->chromasc;
do_div(piic, mt->pixclock);
chromasc = piic;
dprintk(KERN_DEBUG "Chroma is %08X\n", chromasc);
 
r->regs[0] = piic >> 24;
r->regs[1] = piic >> 16;
r->regs[2] = piic >> 8;
r->regs[3] = piic >> 0;
hbp = (((outd->h_b_porch + pixclock) / pixclock)) & ~1;
hfp = (((outd->h_f_porch + pixclock) / pixclock)) & ~1;
hsl = (((outd->h_sync + pixclock) / pixclock)) & ~1;
hlen = hvis + hfp + hsl + hbp;
over = hlen & 0x0F;
dprintk(KERN_DEBUG "WL: vis=%u, hf=%u, hs=%u, hb=%u, total=%u\n", hvis, hfp, hsl, hbp, hlen);
 
if (over) {
hfp -= over;
hlen -= over;
if (over <= 2) {
} else if (over < 10) {
hfp += 4;
hlen += 4;
} else {
hfp += 16;
hlen += 16;
}
}
 
/* maybe cve2 has requirement 800 < hlen < 1184 */
r->regs[0x08] = hsl;
r->regs[0x09] = (outd->burst + pixclock - 1) / pixclock; /* burst length */
r->regs[0x0A] = hbp;
r->regs[0x2C] = hfp;
r->regs[0x31] = hvis / 8;
r->regs[0x32] = hvis & 7;
dprintk(KERN_DEBUG "PG: vis=%04X, hf=%02X, hs=%02X, hb=%02X, total=%04X\n", hvis, hfp, hsl, hbp, hlen);
 
r->regs[0x84] = 1; /* x sync point */
r->regs[0x85] = 0;
hvis = hvis >> 1;
hlen = hlen >> 1;
dprintk(KERN_DEBUG "hlen=%u hvis=%u\n", hlen, hvis);
 
mt->interlaced = 1;
 
mt->HDisplay = hvis & ~7;
mt->HSyncStart = mt->HDisplay + 8;
mt->HSyncEnd = (hlen & ~7) - 8;
mt->HTotal = hlen;
 
{
int upper;
unsigned int vtotal;
unsigned int vsyncend;
unsigned int vdisplay;
vtotal = mt->VTotal;
vsyncend = mt->VSyncEnd;
vdisplay = mt->VDisplay;
if (vtotal < outd->v_total) {
unsigned int yovr = outd->v_total - vtotal;
vsyncend += yovr >> 1;
} else if (vtotal > outd->v_total) {
vdisplay = outd->v_total - 4;
vsyncend = outd->v_total;
}
upper = (outd->v_total - vsyncend) >> 1; /* in field lines */
r->regs[0x17] = outd->v_total / 4;
r->regs[0x18] = outd->v_total & 3;
r->regs[0x33] = upper - 1; /* upper blanking */
r->regs[0x82] = upper; /* y sync point */
r->regs[0x83] = upper >> 8;
mt->VDisplay = vdisplay;
mt->VSyncStart = outd->v_total - 2;
mt->VSyncEnd = outd->v_total;
mt->VTotal = outd->v_total;
}
}
 
static void cve2_init_TVdata(int norm, struct mavenregs* data, const struct output_desc** outd) {
static const struct output_desc paloutd = {
.h_vis = 52148148, // ps
.h_f_porch = 1407407, // ps
.h_sync = 4666667, // ps
.h_b_porch = 5777778, // ps
.chromasc = 19042247534182ULL, // 4433618.750 Hz
.burst = 2518518, // ps
.v_total = 625,
};
static const struct output_desc ntscoutd = {
.h_vis = 52888889, // ps
.h_f_porch = 1333333, // ps
.h_sync = 4666667, // ps
.h_b_porch = 4666667, // ps
.chromasc = 15374030659475ULL, // 3579545.454 Hz
.burst = 2418418, // ps
.v_total = 525, // lines
};
 
static const struct mavenregs palregs = { {
0x2A, 0x09, 0x8A, 0xCB, /* 00: chroma subcarrier */
0x00,
0x00, /* test */
0xF9, /* modified by code (F9 written...) */
0x00, /* ? not written */
0x7E, /* 08 */
0x44, /* 09 */
0x9C, /* 0A */
0x2E, /* 0B */
0x21, /* 0C */
0x00, /* ? not written */
// 0x3F, 0x03, /* 0E-0F */
0x3C, 0x03,
0x3C, 0x03, /* 10-11 */
0x1A, /* 12 */
0x2A, /* 13 */
0x1C, 0x3D, 0x14, /* 14-16 */
0x9C, 0x01, /* 17-18 */
0x00, /* 19 */
0xFE, /* 1A */
0x7E, /* 1B */
0x60, /* 1C */
0x05, /* 1D */
// 0x89, 0x03, /* 1E-1F */
0xAD, 0x03,
// 0x72, /* 20 */
0xA5,
0x07, /* 21 */
// 0x72, /* 22 */
0xA5,
0x00, /* 23 */
0x00, /* 24 */
0x00, /* 25 */
0x08, /* 26 */
0x04, /* 27 */
0x00, /* 28 */
0x1A, /* 29 */
0x55, 0x01, /* 2A-2B */
0x26, /* 2C */
0x07, 0x7E, /* 2D-2E */
0x02, 0x54, /* 2F-30 */
0xB0, 0x00, /* 31-32 */
0x14, /* 33 */
0x49, /* 34 */
0x00, /* 35 written multiple times */
0x00, /* 36 not written */
0xA3, /* 37 */
0xC8, /* 38 */
0x22, /* 39 */
0x02, /* 3A */
0x22, /* 3B */
0x3F, 0x03, /* 3C-3D */
0x00, /* 3E written multiple times */
0x00, /* 3F not written */
} };
static struct mavenregs ntscregs = { {
0x21, 0xF0, 0x7C, 0x1F, /* 00: chroma subcarrier */
0x00,
0x00, /* test */
0xF9, /* modified by code (F9 written...) */
0x00, /* ? not written */
0x7E, /* 08 */
0x43, /* 09 */
0x7E, /* 0A */
0x3D, /* 0B */
0x00, /* 0C */
0x00, /* ? not written */
0x41, 0x00, /* 0E-0F */
0x3C, 0x00, /* 10-11 */
0x17, /* 12 */
0x21, /* 13 */
0x1B, 0x1B, 0x24, /* 14-16 */
0x83, 0x01, /* 17-18 */
0x00, /* 19 */
0x0F, /* 1A */
0x0F, /* 1B */
0x60, /* 1C */
0x05, /* 1D */
//0x89, 0x02, /* 1E-1F */
0xC0, 0x02, /* 1E-1F */
//0x5F, /* 20 */
0x9C, /* 20 */
0x04, /* 21 */
//0x5F, /* 22 */
0x9C, /* 22 */
0x01, /* 23 */
0x02, /* 24 */
0x00, /* 25 */
0x0A, /* 26 */
0x05, /* 27 */
0x00, /* 28 */
0x10, /* 29 */
0xFF, 0x03, /* 2A-2B */
0x24, /* 2C */
0x0F, 0x78, /* 2D-2E */
0x00, 0x00, /* 2F-30 */
0xB2, 0x04, /* 31-32 */
0x14, /* 33 */
0x02, /* 34 */
0x00, /* 35 written multiple times */
0x00, /* 36 not written */
0xA3, /* 37 */
0xC8, /* 38 */
0x15, /* 39 */
0x05, /* 3A */
0x3B, /* 3B */
0x3C, 0x00, /* 3C-3D */
0x00, /* 3E written multiple times */
0x00, /* never written */
} };
 
if (norm == MATROXFB_OUTPUT_MODE_PAL) {
*data = palregs;
*outd = &paloutd;
} else {
*data = ntscregs;
*outd = &ntscoutd;
}
return;
}
 
#define LR(x) cve2_set_reg(PMINFO (x), m->regs[(x)])
static void cve2_init_TV(WPMINFO const struct mavenregs* m) {
int i;
 
LR(0x80);
LR(0x82); LR(0x83);
LR(0x84); LR(0x85);
cve2_set_reg(PMINFO 0x3E, 0x01);
for (i = 0; i < 0x3E; i++) {
LR(i);
}
cve2_set_reg(PMINFO 0x3E, 0x00);
}
 
static int matroxfb_g450_compute(void* md, struct my_timming* mt) {
MINFO_FROM(md);
 
dprintk(KERN_DEBUG "Computing, mode=%u\n", ACCESS_FBINFO(outputs[1]).mode);
 
if (mt->crtc == MATROXFB_SRC_CRTC2 &&
ACCESS_FBINFO(outputs[1]).mode != MATROXFB_OUTPUT_MODE_MONITOR) {
const struct output_desc* outd;
 
cve2_init_TVdata(ACCESS_FBINFO(outputs[1]).mode, &ACCESS_FBINFO(hw).maven, &outd);
{
int blacklevel, whitelevel;
g450_compute_bwlevel(PMINFO &blacklevel, &whitelevel);
ACCESS_FBINFO(hw).maven.regs[0x0E] = blacklevel >> 2;
ACCESS_FBINFO(hw).maven.regs[0x0F] = blacklevel & 3;
ACCESS_FBINFO(hw).maven.regs[0x1E] = whitelevel >> 2;
ACCESS_FBINFO(hw).maven.regs[0x1F] = whitelevel & 3;
 
ACCESS_FBINFO(hw).maven.regs[0x20] =
ACCESS_FBINFO(hw).maven.regs[0x22] = ACCESS_FBINFO(altout.tvo_params.saturation);
 
ACCESS_FBINFO(hw).maven.regs[0x25] = ACCESS_FBINFO(altout.tvo_params.hue);
 
if (ACCESS_FBINFO(altout.tvo_params.testout)) {
ACCESS_FBINFO(hw).maven.regs[0x05] |= 0x02;
}
}
computeRegs(PMINFO &ACCESS_FBINFO(hw).maven, mt, outd);
} else if (mt->mnp < 0) {
/* We must program clocks before CRTC2, otherwise interlaced mode
startup may fail */
mt->mnp = matroxfb_g450_setclk(PMINFO mt->pixclock, (mt->crtc == MATROXFB_SRC_CRTC1) ? M_PIXEL_PLL_C : M_VIDEO_PLL);
mt->pixclock = g450_mnp2f(PMINFO mt->mnp);
}
dprintk(KERN_DEBUG "Pixclock = %u\n", mt->pixclock);
return 0;
}
 
static int matroxfb_g450_program(void* md) {
MINFO_FROM(md);
if (ACCESS_FBINFO(outputs[1]).mode != MATROXFB_OUTPUT_MODE_MONITOR) {
cve2_init_TV(PMINFO &ACCESS_FBINFO(hw).maven);
}
return 0;
}
 
static int matroxfb_g450_verify_mode(void* md, u_int32_t arg) {
switch (arg) {
case MATROXFB_OUTPUT_MODE_PAL:
case MATROXFB_OUTPUT_MODE_NTSC:
case MATROXFB_OUTPUT_MODE_MONITOR:
return 0;
}
return -EINVAL;
}
 
static int g450_dvi_compute(void* md, struct my_timming* mt) {
MINFO_FROM(md);
 
if (mt->mnp < 0) {
mt->mnp = matroxfb_g450_setclk(PMINFO mt->pixclock, (mt->crtc == MATROXFB_SRC_CRTC1) ? M_PIXEL_PLL_C : M_VIDEO_PLL);
mt->pixclock = g450_mnp2f(PMINFO mt->mnp);
}
return 0;
}
 
static struct matrox_altout matroxfb_g450_altout = {
.name = "Secondary output",
.compute = matroxfb_g450_compute,
.program = matroxfb_g450_program,
.verifymode = matroxfb_g450_verify_mode,
.getqueryctrl = g450_query_ctrl,
.getctrl = g450_get_ctrl,
.setctrl = g450_set_ctrl,
};
 
static struct matrox_altout matroxfb_g450_dvi = {
.name = "DVI output",
.compute = g450_dvi_compute,
};
 
void matroxfb_g450_connect(WPMINFO2) {
if (ACCESS_FBINFO(devflags.g450dac)) {
down_write(&ACCESS_FBINFO(altout.lock));
tvo_fill_defaults(PMINFO2);
ACCESS_FBINFO(outputs[1]).src = MATROXFB_SRC_CRTC1;
ACCESS_FBINFO(outputs[1]).data = MINFO;
ACCESS_FBINFO(outputs[1]).output = &matroxfb_g450_altout;
ACCESS_FBINFO(outputs[1]).mode = MATROXFB_OUTPUT_MODE_MONITOR;
ACCESS_FBINFO(outputs[2]).src = MATROXFB_SRC_CRTC1;
ACCESS_FBINFO(outputs[2]).data = MINFO;
ACCESS_FBINFO(outputs[2]).output = &matroxfb_g450_dvi;
ACCESS_FBINFO(outputs[2]).mode = MATROXFB_OUTPUT_MODE_MONITOR;
up_write(&ACCESS_FBINFO(altout.lock));
}
}
 
void matroxfb_g450_shutdown(WPMINFO2) {
if (ACCESS_FBINFO(devflags.g450dac)) {
down_write(&ACCESS_FBINFO(altout.lock));
ACCESS_FBINFO(outputs[1]).src = MATROXFB_SRC_NONE;
ACCESS_FBINFO(outputs[1]).output = NULL;
ACCESS_FBINFO(outputs[1]).data = NULL;
ACCESS_FBINFO(outputs[1]).mode = MATROXFB_OUTPUT_MODE_MONITOR;
ACCESS_FBINFO(outputs[2]).src = MATROXFB_SRC_NONE;
ACCESS_FBINFO(outputs[2]).output = NULL;
ACCESS_FBINFO(outputs[2]).data = NULL;
ACCESS_FBINFO(outputs[2]).mode = MATROXFB_OUTPUT_MODE_MONITOR;
up_write(&ACCESS_FBINFO(altout.lock));
}
}
 
EXPORT_SYMBOL(matroxfb_g450_connect);
EXPORT_SYMBOL(matroxfb_g450_shutdown);
 
MODULE_AUTHOR("(c) 2000-2002 Petr Vandrovec <vandrove@vc.cvut.cz>");
MODULE_DESCRIPTION("Matrox G450/G550 output driver");
MODULE_LICENSE("GPL");
/shark/trunk/drivers/fb/matrox/matroxfb_base.h
0,0 → 1,852
/*
*
* Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200, G400 and G450
*
* (c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz>
*
*/
#ifndef __MATROXFB_H__
#define __MATROXFB_H__
 
/* general, but fairly heavy, debugging */
#undef MATROXFB_DEBUG
 
/* heavy debugging: */
/* -- logs putc[s], so everytime a char is displayed, it's logged */
#undef MATROXFB_DEBUG_HEAVY
 
/* This one _could_ cause infinite loops */
/* It _does_ cause lots and lots of messages during idle loops */
#undef MATROXFB_DEBUG_LOOP
 
/* Debug register calls, too? */
#undef MATROXFB_DEBUG_REG
 
/* Guard accelerator accesses with spin_lock_irqsave... */
#undef MATROXFB_USE_SPINLOCKS
 
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/tty.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/fb.h>
#include <linux/selection.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/pci.h>
#include <linux/spinlock.h>
#include <linux/kd.h>
 
#include <asm/io.h>
#include <asm/unaligned.h>
#ifdef CONFIG_MTRR
#include <asm/mtrr.h>
#endif
 
#if defined(CONFIG_PPC_PMAC)
#include <asm/prom.h>
#include <asm/pci-bridge.h>
#include "../macmodes.h"
#endif
 
/* always compile support for 32MB... It cost almost nothing */
#define CONFIG_FB_MATROX_32MB
 
#ifdef MATROXFB_DEBUG
 
#define DEBUG
#define DBG(x) printk(KERN_DEBUG "matroxfb: %s\n", (x));
 
#ifdef MATROXFB_DEBUG_HEAVY
#define DBG_HEAVY(x) DBG(x)
#else /* MATROXFB_DEBUG_HEAVY */
#define DBG_HEAVY(x) /* DBG_HEAVY */
#endif /* MATROXFB_DEBUG_HEAVY */
 
#ifdef MATROXFB_DEBUG_LOOP
#define DBG_LOOP(x) DBG(x)
#else /* MATROXFB_DEBUG_LOOP */
#define DBG_LOOP(x) /* DBG_LOOP */
#endif /* MATROXFB_DEBUG_LOOP */
 
#ifdef MATROXFB_DEBUG_REG
#define DBG_REG(x) DBG(x)
#else /* MATROXFB_DEBUG_REG */
#define DBG_REG(x) /* DBG_REG */
#endif /* MATROXFB_DEBUG_REG */
 
#else /* MATROXFB_DEBUG */
 
#define DBG(x) /* DBG */
#define DBG_HEAVY(x) /* DBG_HEAVY */
#define DBG_REG(x) /* DBG_REG */
#define DBG_LOOP(x) /* DBG_LOOP */
 
#endif /* MATROXFB_DEBUG */
 
#if !defined(__i386__) && !defined(__x86_64__)
#ifndef ioremap_nocache
#define ioremap_nocache(X,Y) ioremap(X,Y)
#endif
#endif
 
#if defined(__alpha__) || defined(__mc68000__)
#define READx_WORKS
#define MEMCPYTOIO_WORKS
#else
#define READx_FAILS
/* recheck __ppc__, maybe that __ppc__ needs MEMCPYTOIO_WRITEL */
/* I benchmarked PII/350MHz with G200... MEMCPY, MEMCPYTOIO and WRITEL are on same speed ( <2% diff) */
/* so that means that G200 speed (or AGP speed?) is our limit... I do not have benchmark to test, how */
/* much of PCI bandwidth is used during transfers... */
#if defined(__i386__) || defined(__x86_64__)
#define MEMCPYTOIO_MEMCPY
#else
#define MEMCPYTOIO_WRITEL
#endif
#endif
 
#if defined(__mc68000__)
#define MAP_BUSTOVIRT
#else
#define MAP_IOREMAP
#endif
 
#ifdef DEBUG
#define dprintk(X...) printk(X)
#else
#define dprintk(X...)
#endif
 
#ifndef PCI_SS_VENDOR_ID_SIEMENS_NIXDORF
#define PCI_SS_VENDOR_ID_SIEMENS_NIXDORF 0x110A
#endif
#ifndef PCI_SS_VENDOR_ID_MATROX
#define PCI_SS_VENDOR_ID_MATROX PCI_VENDOR_ID_MATROX
#endif
 
#ifndef PCI_SS_ID_MATROX_PRODUCTIVA_G100_AGP
#define PCI_SS_ID_MATROX_GENERIC 0xFF00
#define PCI_SS_ID_MATROX_PRODUCTIVA_G100_AGP 0xFF01
#define PCI_SS_ID_MATROX_MYSTIQUE_G200_AGP 0xFF02
#define PCI_SS_ID_MATROX_MILLENIUM_G200_AGP 0xFF03
#define PCI_SS_ID_MATROX_MARVEL_G200_AGP 0xFF04
#define PCI_SS_ID_MATROX_MGA_G100_PCI 0xFF05
#define PCI_SS_ID_MATROX_MGA_G100_AGP 0x1001
#define PCI_SS_ID_MATROX_MILLENNIUM_G400_MAX_AGP 0x2179
#define PCI_SS_ID_SIEMENS_MGA_G100_AGP 0x001E /* 30 */
#define PCI_SS_ID_SIEMENS_MGA_G200_AGP 0x0032 /* 50 */
#endif
 
#define MX_VISUAL_TRUECOLOR FB_VISUAL_DIRECTCOLOR
#define MX_VISUAL_DIRECTCOLOR FB_VISUAL_TRUECOLOR
#define MX_VISUAL_PSEUDOCOLOR FB_VISUAL_PSEUDOCOLOR
 
#define CNVT_TOHW(val,width) ((((val)<<(width))+0x7FFF-(val))>>16)
 
/* G-series and Mystique have (almost) same DAC */
#undef NEED_DAC1064
#if defined(CONFIG_FB_MATROX_MYSTIQUE) || defined(CONFIG_FB_MATROX_G100)
#define NEED_DAC1064 1
#endif
 
typedef struct {
u_int8_t* vaddr;
} vaddr_t;
 
#ifdef READx_WORKS
static inline unsigned int mga_readb(vaddr_t va, unsigned int offs) {
return readb(va.vaddr + offs);
}
 
static inline unsigned int mga_readw(vaddr_t va, unsigned int offs) {
return readw(va.vaddr + offs);
}
 
static inline u_int32_t mga_readl(vaddr_t va, unsigned int offs) {
return readl(va.vaddr + offs);
}
 
static inline void mga_writeb(vaddr_t va, unsigned int offs, u_int8_t value) {
writeb(value, va.vaddr + offs);
}
 
static inline void mga_writew(vaddr_t va, unsigned int offs, u_int16_t value) {
writew(value, va.vaddr + offs);
}
 
static inline void mga_writel(vaddr_t va, unsigned int offs, u_int32_t value) {
writel(value, va.vaddr + offs);
}
#else
static inline unsigned int mga_readb(vaddr_t va, unsigned int offs) {
return *(volatile u_int8_t*)(va.vaddr + offs);
}
 
static inline unsigned int mga_readw(vaddr_t va, unsigned int offs) {
return *(volatile u_int16_t*)(va.vaddr + offs);
}
 
static inline u_int32_t mga_readl(vaddr_t va, unsigned int offs) {
return *(volatile u_int32_t*)(va.vaddr + offs);
}
 
static inline void mga_writeb(vaddr_t va, unsigned int offs, u_int8_t value) {
*(volatile u_int8_t*)(va.vaddr + offs) = value;
}
 
static inline void mga_writew(vaddr_t va, unsigned int offs, u_int16_t value) {
*(volatile u_int16_t*)(va.vaddr + offs) = value;
}
 
static inline void mga_writel(vaddr_t va, unsigned int offs, u_int32_t value) {
*(volatile u_int32_t*)(va.vaddr + offs) = value;
}
#endif
 
static inline void mga_memcpy_toio(vaddr_t va, unsigned int offs, const void* src, int len) {
#ifdef MEMCPYTOIO_WORKS
memcpy_toio(va.vaddr + offs, src, len);
#elif defined(MEMCPYTOIO_WRITEL)
#define srcd ((const u_int32_t*)src)
if (offs & 3) {
while (len >= 4) {
mga_writel(va, offs, get_unaligned(srcd++));
offs += 4;
len -= 4;
}
} else {
while (len >= 4) {
mga_writel(va, offs, *srcd++);
offs += 4;
len -= 4;
}
}
#undef srcd
if (len) {
u_int32_t tmp;
 
memcpy(&tmp, src, len);
mga_writel(va, offs, tmp);
}
#elif defined(MEMCPYTOIO_MEMCPY)
memcpy(va.vaddr + offs, src, len);
#else
#error "Sorry, do not know how to write block of data to device"
#endif
}
 
static inline void vaddr_add(vaddr_t* va, unsigned long offs) {
va->vaddr += offs;
}
 
static inline void* vaddr_va(vaddr_t va) {
return va.vaddr;
}
 
#define MGA_IOREMAP_NORMAL 0
#define MGA_IOREMAP_NOCACHE 1
 
#define MGA_IOREMAP_FB MGA_IOREMAP_NOCACHE
#define MGA_IOREMAP_MMIO MGA_IOREMAP_NOCACHE
static inline int mga_ioremap(unsigned long phys, unsigned long size, int flags, vaddr_t* virt) {
#ifdef MAP_IOREMAP
if (flags & MGA_IOREMAP_NOCACHE)
virt->vaddr = ioremap(phys, size);
else
virt->vaddr = ioremap(phys, size);
#else
#ifdef MAP_BUSTOVIRT
virt->vaddr = bus_to_virt(phys);
#else
#error "Your architecture does not have neither ioremap nor bus_to_virt... Giving up"
#endif
#endif
return (virt->vaddr == 0); /* 0, !0... 0, error_code in future */
}
 
static inline void mga_iounmap(vaddr_t va) {
#ifdef MAP_IOREMAP
iounmap(va.vaddr);
#endif
}
 
struct my_timming {
unsigned int pixclock;
int mnp;
unsigned int crtc;
unsigned int HDisplay;
unsigned int HSyncStart;
unsigned int HSyncEnd;
unsigned int HTotal;
unsigned int VDisplay;
unsigned int VSyncStart;
unsigned int VSyncEnd;
unsigned int VTotal;
unsigned int sync;
int dblscan;
int interlaced;
unsigned int delay; /* CRTC delay */
};
 
enum { M_SYSTEM_PLL, M_PIXEL_PLL_A, M_PIXEL_PLL_B, M_PIXEL_PLL_C, M_VIDEO_PLL };
 
struct matrox_pll_cache {
unsigned int valid;
struct {
unsigned int mnp_key;
unsigned int mnp_value;
} data[4];
};
 
struct matrox_pll_limits {
unsigned int vcomin;
unsigned int vcomax;
};
 
struct matrox_pll_features {
unsigned int vco_freq_min;
unsigned int ref_freq;
unsigned int feed_div_min;
unsigned int feed_div_max;
unsigned int in_div_min;
unsigned int in_div_max;
unsigned int post_shift_max;
};
 
struct matroxfb_par
{
unsigned int final_bppShift;
unsigned int cmap_len;
struct {
unsigned int bytes;
unsigned int pixels;
unsigned int chunks;
} ydstorg;
};
 
struct matrox_fb_info;
 
struct matrox_DAC1064_features {
u_int8_t xvrefctrl;
u_int8_t xmiscctrl;
};
 
struct matrox_accel_features {
int has_cacheflush;
};
 
/* current hardware status */
struct mavenregs {
u_int8_t regs[256];
int mode;
int vlines;
int xtal;
int fv;
 
u_int16_t htotal;
u_int16_t hcorr;
};
 
struct matrox_crtc2 {
u_int32_t ctl;
};
 
struct matrox_hw_state {
u_int32_t MXoptionReg;
unsigned char DACclk[6];
unsigned char DACreg[80];
unsigned char MiscOutReg;
unsigned char DACpal[768];
unsigned char CRTC[25];
unsigned char CRTCEXT[9];
unsigned char SEQ[5];
/* unused for MGA mode, but who knows... */
unsigned char GCTL[9];
/* unused for MGA mode, but who knows... */
unsigned char ATTR[21];
 
/* TVOut only */
struct mavenregs maven;
 
struct matrox_crtc2 crtc2;
};
 
struct matrox_accel_data {
#ifdef CONFIG_FB_MATROX_MILLENIUM
unsigned char ramdac_rev;
#endif
u_int32_t m_dwg_rect;
u_int32_t m_opmode;
};
 
struct v4l2_queryctrl;
struct v4l2_control;
 
struct matrox_altout {
const char *name;
int (*compute)(void* altout_dev, struct my_timming* input);
int (*program)(void* altout_dev);
int (*start)(void* altout_dev);
int (*verifymode)(void* altout_dev, u_int32_t mode);
int (*getqueryctrl)(void* altout_dev,
struct v4l2_queryctrl* ctrl);
int (*getctrl)(void* altout_dev,
struct v4l2_control* ctrl);
int (*setctrl)(void* altout_dev,
struct v4l2_control* ctrl);
};
 
#define MATROXFB_SRC_NONE 0
#define MATROXFB_SRC_CRTC1 1
#define MATROXFB_SRC_CRTC2 2
 
enum mga_chip { MGA_2064, MGA_2164, MGA_1064, MGA_1164, MGA_G100, MGA_G200, MGA_G400, MGA_G450, MGA_G550 };
 
struct matrox_bios {
unsigned int bios_valid : 1;
unsigned int pins_len;
unsigned char pins[128];
struct {
unsigned char vMaj, vMin, vRev;
} version;
struct {
unsigned char state, tvout;
} output;
};
 
extern struct display fb_display[];
 
struct matrox_switch;
struct matroxfb_driver;
struct matroxfb_dh_fb_info;
 
struct matrox_vsync {
wait_queue_head_t wait;
unsigned int cnt;
};
 
struct matrox_fb_info {
struct fb_info fbcon;
 
struct list_head next_fb;
 
int dead;
unsigned int usecount;
 
unsigned int userusecount;
unsigned long irq_flags;
 
struct matroxfb_par curr;
struct matrox_hw_state hw;
 
struct matrox_accel_data accel;
 
struct pci_dev* pcidev;
 
struct {
struct matrox_vsync vsync;
unsigned int pixclock;
int mnp;
int panpos;
} crtc1;
struct {
struct matrox_vsync vsync;
unsigned int pixclock;
int mnp;
struct matroxfb_dh_fb_info* info;
struct rw_semaphore lock;
} crtc2;
struct {
struct rw_semaphore lock;
struct {
int brightness, contrast, saturation, hue, gamma;
int testout, deflicker;
} tvo_params;
} altout;
#define MATROXFB_MAX_OUTPUTS 3
struct {
unsigned int src;
struct matrox_altout* output;
void* data;
unsigned int mode;
} outputs[MATROXFB_MAX_OUTPUTS];
 
#define MATROXFB_MAX_FB_DRIVERS 5
struct matroxfb_driver* (drivers[MATROXFB_MAX_FB_DRIVERS]);
void* (drivers_data[MATROXFB_MAX_FB_DRIVERS]);
unsigned int drivers_count;
 
struct {
unsigned long base; /* physical */
vaddr_t vbase; /* CPU view */
unsigned int len;
unsigned int len_usable;
unsigned int len_maximum;
} video;
 
struct {
unsigned long base; /* physical */
vaddr_t vbase; /* CPU view */
unsigned int len;
} mmio;
 
unsigned int max_pixel_clock;
 
struct matrox_switch* hw_switch;
 
struct {
struct matrox_pll_features pll;
struct matrox_DAC1064_features DAC1064;
struct matrox_accel_features accel;
} features;
struct {
spinlock_t DAC;
spinlock_t accel;
} lock;
 
enum mga_chip chip;
 
int interleave;
int millenium;
int milleniumII;
struct {
int cfb4;
const int* vxres;
int cross4MB;
int text;
int plnwt;
int srcorg;
} capable;
#ifdef CONFIG_MTRR
struct {
int vram;
int vram_valid;
} mtrr;
#endif
struct {
int precise_width;
int mga_24bpp_fix;
int novga;
int nobios;
int nopciretry;
int noinit;
int sgram;
#ifdef CONFIG_FB_MATROX_32MB
int support32MB;
#endif
 
int accelerator;
int text_type_aux;
int video64bits;
int crtc2;
int maven_capable;
unsigned int vgastep;
unsigned int textmode;
unsigned int textstep;
unsigned int textvram; /* character cells */
unsigned int ydstorg; /* offset in bytes from video start to usable memory */
/* 0 except for 6MB Millenium */
int memtype;
int g450dac;
int dfp_type;
int panellink; /* G400 DFP possible (not G450/G550) */
int dualhead;
unsigned int fbResource;
} devflags;
struct fb_ops fbops;
struct matrox_bios bios;
struct {
struct matrox_pll_limits pixel;
struct matrox_pll_limits system;
struct matrox_pll_limits video;
} limits;
struct {
struct matrox_pll_cache pixel;
struct matrox_pll_cache system;
struct matrox_pll_cache video;
} cache;
struct {
struct {
unsigned int video;
unsigned int system;
} pll;
struct {
u_int32_t opt;
u_int32_t opt2;
u_int32_t opt3;
u_int32_t mctlwtst;
u_int32_t mctlwtst_core;
u_int32_t memmisc;
u_int32_t memrdbk;
u_int32_t maccess;
} reg;
struct {
unsigned int ddr:1,
emrswen:1,
dll:1;
} memory;
} values;
u_int32_t cmap[17];
};
 
#define info2minfo(info) container_of(info, struct matrox_fb_info, fbcon)
 
#ifdef CONFIG_FB_MATROX_MULTIHEAD
#define ACCESS_FBINFO2(info, x) (info->x)
#define ACCESS_FBINFO(x) ACCESS_FBINFO2(minfo,x)
 
#define MINFO minfo
 
#define WPMINFO2 struct matrox_fb_info* minfo
#define WPMINFO WPMINFO2 ,
#define CPMINFO2 const struct matrox_fb_info* minfo
#define CPMINFO CPMINFO2 ,
#define PMINFO2 minfo
#define PMINFO PMINFO2 ,
 
#define MINFO_FROM(x) struct matrox_fb_info* minfo = x
#else
 
extern struct matrox_fb_info matroxfb_global_mxinfo;
 
#define ACCESS_FBINFO(x) (matroxfb_global_mxinfo.x)
#define ACCESS_FBINFO2(info, x) (matroxfb_global_mxinfo.x)
 
#define MINFO (&matroxfb_global_mxinfo)
 
#define WPMINFO2 void
#define WPMINFO
#define CPMINFO2 void
#define CPMINFO
#define PMINFO2
#define PMINFO
 
#define MINFO_FROM(x)
 
#endif
 
#define MINFO_FROM_INFO(x) MINFO_FROM(info2minfo(x))
 
struct matrox_switch {
int (*preinit)(WPMINFO2);
void (*reset)(WPMINFO2);
int (*init)(WPMINFO struct my_timming*);
void (*restore)(WPMINFO2);
};
 
struct matroxfb_driver {
struct list_head node;
char* name;
void* (*probe)(struct matrox_fb_info* info);
void (*remove)(struct matrox_fb_info* info, void* data);
};
 
int matroxfb_register_driver(struct matroxfb_driver* drv);
void matroxfb_unregister_driver(struct matroxfb_driver* drv);
 
#define PCI_OPTION_REG 0x40
#define PCI_OPTION_ENABLE_ROM 0x40000000
 
#define PCI_MGA_INDEX 0x44
#define PCI_MGA_DATA 0x48
#define PCI_OPTION2_REG 0x50
#define PCI_OPTION3_REG 0x54
#define PCI_MEMMISC_REG 0x58
 
#define M_DWGCTL 0x1C00
#define M_MACCESS 0x1C04
#define M_CTLWTST 0x1C08
 
#define M_PLNWT 0x1C1C
 
#define M_BCOL 0x1C20
#define M_FCOL 0x1C24
 
#define M_SGN 0x1C58
#define M_LEN 0x1C5C
#define M_AR0 0x1C60
#define M_AR1 0x1C64
#define M_AR2 0x1C68
#define M_AR3 0x1C6C
#define M_AR4 0x1C70
#define M_AR5 0x1C74
#define M_AR6 0x1C78
 
#define M_CXBNDRY 0x1C80
#define M_FXBNDRY 0x1C84
#define M_YDSTLEN 0x1C88
#define M_PITCH 0x1C8C
#define M_YDST 0x1C90
#define M_YDSTORG 0x1C94
#define M_YTOP 0x1C98
#define M_YBOT 0x1C9C
 
/* mystique only */
#define M_CACHEFLUSH 0x1FFF
 
#define M_EXEC 0x0100
 
#define M_DWG_TRAP 0x04
#define M_DWG_BITBLT 0x08
#define M_DWG_ILOAD 0x09
 
#define M_DWG_LINEAR 0x0080
#define M_DWG_SOLID 0x0800
#define M_DWG_ARZERO 0x1000
#define M_DWG_SGNZERO 0x2000
#define M_DWG_SHIFTZERO 0x4000
 
#define M_DWG_REPLACE 0x000C0000
#define M_DWG_REPLACE2 (M_DWG_REPLACE | 0x40)
#define M_DWG_XOR 0x00060010
 
#define M_DWG_BFCOL 0x04000000
#define M_DWG_BMONOWF 0x08000000
 
#define M_DWG_TRANSC 0x40000000
 
#define M_FIFOSTATUS 0x1E10
#define M_STATUS 0x1E14
#define M_ICLEAR 0x1E18
#define M_IEN 0x1E1C
 
#define M_VCOUNT 0x1E20
 
#define M_RESET 0x1E40
#define M_MEMRDBK 0x1E44
 
#define M_AGP2PLL 0x1E4C
 
#define M_OPMODE 0x1E54
#define M_OPMODE_DMA_GEN_WRITE 0x00
#define M_OPMODE_DMA_BLIT 0x04
#define M_OPMODE_DMA_VECTOR_WRITE 0x08
#define M_OPMODE_DMA_LE 0x0000 /* little endian - no transformation */
#define M_OPMODE_DMA_BE_8BPP 0x0000
#define M_OPMODE_DMA_BE_16BPP 0x0100
#define M_OPMODE_DMA_BE_32BPP 0x0200
#define M_OPMODE_DIR_LE 0x000000 /* little endian - no transformation */
#define M_OPMODE_DIR_BE_8BPP 0x000000
#define M_OPMODE_DIR_BE_16BPP 0x010000
#define M_OPMODE_DIR_BE_32BPP 0x020000
 
#define M_ATTR_INDEX 0x1FC0
#define M_ATTR_DATA 0x1FC1
 
#define M_MISC_REG 0x1FC2
#define M_3C2_RD 0x1FC2
 
#define M_SEQ_INDEX 0x1FC4
#define M_SEQ_DATA 0x1FC5
 
#define M_MISC_REG_READ 0x1FCC
 
#define M_GRAPHICS_INDEX 0x1FCE
#define M_GRAPHICS_DATA 0x1FCF
 
#define M_CRTC_INDEX 0x1FD4
 
#define M_ATTR_RESET 0x1FDA
#define M_3DA_WR 0x1FDA
#define M_INSTS1 0x1FDA
 
#define M_EXTVGA_INDEX 0x1FDE
#define M_EXTVGA_DATA 0x1FDF
 
/* G200 only */
#define M_SRCORG 0x2CB4
#define M_DSTORG 0x2CB8
 
#define M_RAMDAC_BASE 0x3C00
 
/* fortunately, same on TVP3026 and MGA1064 */
#define M_DAC_REG (M_RAMDAC_BASE+0)
#define M_DAC_VAL (M_RAMDAC_BASE+1)
#define M_PALETTE_MASK (M_RAMDAC_BASE+2)
 
#define M_X_INDEX 0x00
#define M_X_DATAREG 0x0A
 
#define DAC_XGENIOCTRL 0x2A
#define DAC_XGENIODATA 0x2B
 
#define M_C2CTL 0x3E10
 
#ifdef __LITTLE_ENDIAN
#define MX_OPTION_BSWAP 0x00000000
 
#define M_OPMODE_4BPP (M_OPMODE_DMA_LE | M_OPMODE_DIR_LE | M_OPMODE_DMA_BLIT)
#define M_OPMODE_8BPP (M_OPMODE_DMA_LE | M_OPMODE_DIR_LE | M_OPMODE_DMA_BLIT)
#define M_OPMODE_16BPP (M_OPMODE_DMA_LE | M_OPMODE_DIR_LE | M_OPMODE_DMA_BLIT)
#define M_OPMODE_24BPP (M_OPMODE_DMA_LE | M_OPMODE_DIR_LE | M_OPMODE_DMA_BLIT)
#define M_OPMODE_32BPP (M_OPMODE_DMA_LE | M_OPMODE_DIR_LE | M_OPMODE_DMA_BLIT)
#else
#ifdef __BIG_ENDIAN
#define MX_OPTION_BSWAP 0x80000000
 
#define M_OPMODE_4BPP (M_OPMODE_DMA_LE | M_OPMODE_DIR_LE | M_OPMODE_DMA_BLIT) /* TODO */
#define M_OPMODE_8BPP (M_OPMODE_DMA_BE_8BPP | M_OPMODE_DIR_BE_8BPP | M_OPMODE_DMA_BLIT)
#define M_OPMODE_16BPP (M_OPMODE_DMA_BE_16BPP | M_OPMODE_DIR_BE_16BPP | M_OPMODE_DMA_BLIT)
#define M_OPMODE_24BPP (M_OPMODE_DMA_BE_8BPP | M_OPMODE_DIR_BE_8BPP | M_OPMODE_DMA_BLIT) /* TODO, ?32 */
#define M_OPMODE_32BPP (M_OPMODE_DMA_BE_32BPP | M_OPMODE_DIR_BE_32BPP | M_OPMODE_DMA_BLIT)
#else
#error "Byte ordering have to be defined. Cannot continue."
#endif
#endif
 
#define mga_inb(addr) mga_readb(ACCESS_FBINFO(mmio.vbase), (addr))
#define mga_inl(addr) mga_readl(ACCESS_FBINFO(mmio.vbase), (addr))
#define mga_outb(addr,val) mga_writeb(ACCESS_FBINFO(mmio.vbase), (addr), (val))
#define mga_outw(addr,val) mga_writew(ACCESS_FBINFO(mmio.vbase), (addr), (val))
#define mga_outl(addr,val) mga_writel(ACCESS_FBINFO(mmio.vbase), (addr), (val))
#define mga_readr(port,idx) (mga_outb((port),(idx)), mga_inb((port)+1))
#ifdef __LITTLE_ENDIAN
#define mga_setr(addr,port,val) mga_outw(addr, ((val)<<8) | (port))
#else
#define mga_setr(addr,port,val) do { mga_outb(addr, port); mga_outb((addr)+1, val); } while (0)
#endif
 
#define mga_fifo(n) do {} while ((mga_inl(M_FIFOSTATUS) & 0xFF) < (n))
 
#define WaitTillIdle() do {} while (mga_inl(M_STATUS) & 0x10000)
 
/* code speedup */
#ifdef CONFIG_FB_MATROX_MILLENIUM
#define isInterleave(x) (x->interleave)
#define isMillenium(x) (x->millenium)
#define isMilleniumII(x) (x->milleniumII)
#else
#define isInterleave(x) (0)
#define isMillenium(x) (0)
#define isMilleniumII(x) (0)
#endif
 
#define matroxfb_DAC_lock() spin_lock(&ACCESS_FBINFO(lock.DAC))
#define matroxfb_DAC_unlock() spin_unlock(&ACCESS_FBINFO(lock.DAC))
#define matroxfb_DAC_lock_irqsave(flags) spin_lock_irqsave(&ACCESS_FBINFO(lock.DAC),flags)
#define matroxfb_DAC_unlock_irqrestore(flags) spin_unlock_irqrestore(&ACCESS_FBINFO(lock.DAC),flags)
extern void matroxfb_DAC_out(CPMINFO int reg, int val);
extern int matroxfb_DAC_in(CPMINFO int reg);
extern struct list_head matroxfb_list;
extern void matroxfb_var2my(struct fb_var_screeninfo* fvsi, struct my_timming* mt);
extern int matroxfb_wait_for_sync(WPMINFO u_int32_t crtc);
extern int matroxfb_enable_irq(WPMINFO int reenable);
 
#ifdef MATROXFB_USE_SPINLOCKS
#define CRITBEGIN spin_lock_irqsave(&ACCESS_FBINFO(lock.accel), critflags);
#define CRITEND spin_unlock_irqrestore(&ACCESS_FBINFO(lock.accel), critflags);
#define CRITFLAGS unsigned long critflags;
#else
#define CRITBEGIN
#define CRITEND
#define CRITFLAGS
#endif
 
#endif /* __MATROXFB_H__ */
/shark/trunk/drivers/fb/matrox/matroxfb_crtc2.h
0,0 → 1,35
#ifndef __MATROXFB_CRTC2_H__
#define __MATROXFB_CRTC2_H__
 
#include <linux/ioctl.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include "matroxfb_base.h"
 
struct matroxfb_dh_fb_info {
struct fb_info fbcon;
int fbcon_registered;
 
struct matrox_fb_info* primary_dev;
 
struct {
unsigned long base; /* physical */
vaddr_t vbase; /* virtual */
unsigned int len;
unsigned int len_usable;
unsigned int len_maximum;
unsigned int offbase;
unsigned int borrowed;
} video;
struct {
unsigned long base;
vaddr_t vbase;
unsigned int len;
} mmio;
 
int interlaced:1;
 
u_int32_t cmap[17];
};
 
#endif /* __MATROXFB_CRTC2_H__ */
/shark/trunk/drivers/fb/matrox/matroxfb_g450.h
0,0 → 1,14
#ifndef __MATROXFB_G450_H__
#define __MATROXFB_G450_H__
 
#include "matroxfb_base.h"
 
#ifdef CONFIG_FB_MATROX_G450
void matroxfb_g450_connect(WPMINFO2);
void matroxfb_g450_shutdown(WPMINFO2);
#else
static inline void matroxfb_g450_connect(WPMINFO2) { };
static inline void matroxfb_g450_shutdown(WPMINFO2) { };
#endif
 
#endif /* __MATROXFB_G450_H__ */
/shark/trunk/drivers/fb/matrox/g450_pll.c
0,0 → 1,481
/*
*
* Hardware accelerated Matrox PCI cards - G450/G550 PLL control.
*
* (c) 2001-2002 Petr Vandrovec <vandrove@vc.cvut.cz>
*
* Portions Copyright (c) 2001 Matrox Graphics Inc.
*
* Version: 1.64 2002/06/10
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive for
* more details.
*
*/
 
#include <linuxcomp.h>
 
#include "g450_pll.h"
#include "matroxfb_DAC1064.h"
 
static inline unsigned int g450_vco2f(unsigned char p, unsigned int fvco) {
return (p & 0x40) ? fvco : fvco >> ((p & 3) + 1);
}
 
static inline unsigned int g450_f2vco(unsigned char p, unsigned int fin) {
return (p & 0x40) ? fin : fin << ((p & 3) + 1);
}
 
static unsigned int g450_mnp2vco(CPMINFO unsigned int mnp) {
unsigned int m, n;
 
m = ((mnp >> 16) & 0x0FF) + 1;
n = ((mnp >> 7) & 0x1FE) + 4;
return (ACCESS_FBINFO(features).pll.ref_freq * n + (m >> 1)) / m;
}
 
unsigned int g450_mnp2f(CPMINFO unsigned int mnp) {
return g450_vco2f(mnp, g450_mnp2vco(PMINFO mnp));
}
 
static inline unsigned int pll_freq_delta(unsigned int f1, unsigned int f2) {
if (f2 < f1) {
f2 = f1 - f2;
} else {
f2 = f2 - f1;
}
return f2;
}
 
#define NO_MORE_MNP 0x01FFFFFF
#define G450_MNP_FREQBITS (0xFFFFFF43) /* do not mask high byte so we'll catch NO_MORE_MNP */
 
static unsigned int g450_nextpll(CPMINFO const struct matrox_pll_limits* pi, unsigned int* fvco, unsigned int mnp) {
unsigned int m, n, p;
unsigned int tvco = *fvco;
 
m = (mnp >> 16) & 0xFF;
p = mnp & 0xFF;
 
do {
if (m == 0 || m == 0xFF) {
if (m == 0) {
if (p & 0x40) {
return NO_MORE_MNP;
}
if (p & 3) {
p--;
} else {
p = 0x40;
}
tvco >>= 1;
if (tvco < pi->vcomin) {
return NO_MORE_MNP;
}
*fvco = tvco;
}
 
p &= 0x43;
if (tvco < 550000) {
/* p |= 0x00; */
} else if (tvco < 700000) {
p |= 0x08;
} else if (tvco < 1000000) {
p |= 0x10;
} else if (tvco < 1150000) {
p |= 0x18;
} else {
p |= 0x20;
}
m = 9;
} else {
m--;
}
n = ((tvco * (m+1) + ACCESS_FBINFO(features).pll.ref_freq) / (ACCESS_FBINFO(features).pll.ref_freq * 2)) - 2;
} while (n < 0x03 || n > 0x7A);
return (m << 16) | (n << 8) | p;
}
 
static unsigned int g450_firstpll(CPMINFO const struct matrox_pll_limits* pi, unsigned int* vco, unsigned int fout) {
unsigned int p;
unsigned int vcomax;
 
vcomax = pi->vcomax;
if (fout > (vcomax / 2)) {
if (fout > vcomax) {
*vco = vcomax;
} else {
*vco = fout;
}
p = 0x40;
} else {
unsigned int tvco;
 
p = 3;
tvco = g450_f2vco(p, fout);
while (p && (tvco > vcomax)) {
p--;
tvco >>= 1;
}
if (tvco < pi->vcomin) {
tvco = pi->vcomin;
}
*vco = tvco;
}
return g450_nextpll(PMINFO pi, vco, 0xFF0000 | p);
}
 
static inline unsigned int g450_setpll(CPMINFO unsigned int mnp, unsigned int pll) {
switch (pll) {
case M_PIXEL_PLL_A:
matroxfb_DAC_out(PMINFO M1064_XPIXPLLAM, mnp >> 16);
matroxfb_DAC_out(PMINFO M1064_XPIXPLLAN, mnp >> 8);
matroxfb_DAC_out(PMINFO M1064_XPIXPLLAP, mnp);
return M1064_XPIXPLLSTAT;
 
case M_PIXEL_PLL_B:
matroxfb_DAC_out(PMINFO M1064_XPIXPLLBM, mnp >> 16);
matroxfb_DAC_out(PMINFO M1064_XPIXPLLBN, mnp >> 8);
matroxfb_DAC_out(PMINFO M1064_XPIXPLLBP, mnp);
return M1064_XPIXPLLSTAT;
 
case M_PIXEL_PLL_C:
matroxfb_DAC_out(PMINFO M1064_XPIXPLLCM, mnp >> 16);
matroxfb_DAC_out(PMINFO M1064_XPIXPLLCN, mnp >> 8);
matroxfb_DAC_out(PMINFO M1064_XPIXPLLCP, mnp);
return M1064_XPIXPLLSTAT;
 
case M_SYSTEM_PLL:
matroxfb_DAC_out(PMINFO DAC1064_XSYSPLLM, mnp >> 16);
matroxfb_DAC_out(PMINFO DAC1064_XSYSPLLN, mnp >> 8);
matroxfb_DAC_out(PMINFO DAC1064_XSYSPLLP, mnp);
return DAC1064_XSYSPLLSTAT;
 
case M_VIDEO_PLL:
matroxfb_DAC_out(PMINFO M1064_XVIDPLLM, mnp >> 16);
matroxfb_DAC_out(PMINFO M1064_XVIDPLLN, mnp >> 8);
matroxfb_DAC_out(PMINFO M1064_XVIDPLLP, mnp);
return M1064_XVIDPLLSTAT;
}
return 0;
}
 
static inline unsigned int g450_cmppll(CPMINFO unsigned int mnp, unsigned int pll) {
unsigned char m = mnp >> 16;
unsigned char n = mnp >> 8;
unsigned char p = mnp;
 
switch (pll) {
case M_PIXEL_PLL_A:
return (matroxfb_DAC_in(PMINFO M1064_XPIXPLLAM) != m ||
matroxfb_DAC_in(PMINFO M1064_XPIXPLLAN) != n ||
matroxfb_DAC_in(PMINFO M1064_XPIXPLLAP) != p);
 
case M_PIXEL_PLL_B:
return (matroxfb_DAC_in(PMINFO M1064_XPIXPLLBM) != m ||
matroxfb_DAC_in(PMINFO M1064_XPIXPLLBN) != n ||
matroxfb_DAC_in(PMINFO M1064_XPIXPLLBP) != p);
 
case M_PIXEL_PLL_C:
return (matroxfb_DAC_in(PMINFO M1064_XPIXPLLCM) != m ||
matroxfb_DAC_in(PMINFO M1064_XPIXPLLCN) != n ||
matroxfb_DAC_in(PMINFO M1064_XPIXPLLCP) != p);
 
case M_SYSTEM_PLL:
return (matroxfb_DAC_in(PMINFO DAC1064_XSYSPLLM) != m ||
matroxfb_DAC_in(PMINFO DAC1064_XSYSPLLN) != n ||
matroxfb_DAC_in(PMINFO DAC1064_XSYSPLLP) != p);
 
case M_VIDEO_PLL:
return (matroxfb_DAC_in(PMINFO M1064_XVIDPLLM) != m ||
matroxfb_DAC_in(PMINFO M1064_XVIDPLLN) != n ||
matroxfb_DAC_in(PMINFO M1064_XVIDPLLP) != p);
}
return 1;
}
 
static inline int g450_isplllocked(CPMINFO unsigned int regidx) {
unsigned int j;
 
for (j = 0; j < 1000; j++) {
if (matroxfb_DAC_in(PMINFO regidx) & 0x40) {
unsigned int r = 0;
int i;
 
for (i = 0; i < 100; i++) {
r += matroxfb_DAC_in(PMINFO regidx) & 0x40;
}
return r >= (90 * 0x40);
}
/* udelay(1)... but DAC_in is much slower... */
}
return 0;
}
 
static int g450_testpll(CPMINFO unsigned int mnp, unsigned int pll) {
return g450_isplllocked(PMINFO g450_setpll(PMINFO mnp, pll));
}
 
static void updatehwstate_clk(struct matrox_hw_state* hw, unsigned int mnp, unsigned int pll) {
switch (pll) {
case M_SYSTEM_PLL:
hw->DACclk[3] = mnp >> 16;
hw->DACclk[4] = mnp >> 8;
hw->DACclk[5] = mnp;
break;
}
}
 
void matroxfb_g450_setpll_cond(WPMINFO unsigned int mnp, unsigned int pll) {
if (g450_cmppll(PMINFO mnp, pll)) {
g450_setpll(PMINFO mnp, pll);
}
}
 
static inline unsigned int g450_findworkingpll(WPMINFO unsigned int pll, unsigned int* mnparray, unsigned int mnpcount) {
unsigned int found = 0;
unsigned int idx;
unsigned int mnpfound = mnparray[0];
for (idx = 0; idx < mnpcount; idx++) {
unsigned int sarray[3];
unsigned int *sptr;
{
unsigned int mnp;
sptr = sarray;
mnp = mnparray[idx];
if (mnp & 0x38) {
*sptr++ = mnp - 8;
}
if ((mnp & 0x38) != 0x38) {
*sptr++ = mnp + 8;
}
*sptr = mnp;
}
while (sptr >= sarray) {
unsigned int mnp = *sptr--;
if (g450_testpll(PMINFO mnp - 0x0300, pll) &&
g450_testpll(PMINFO mnp + 0x0300, pll) &&
g450_testpll(PMINFO mnp - 0x0200, pll) &&
g450_testpll(PMINFO mnp + 0x0200, pll) &&
g450_testpll(PMINFO mnp - 0x0100, pll) &&
g450_testpll(PMINFO mnp + 0x0100, pll)) {
if (g450_testpll(PMINFO mnp, pll)) {
return mnp;
}
} else if (!found && g450_testpll(PMINFO mnp, pll)) {
mnpfound = mnp;
found = 1;
}
}
}
g450_setpll(PMINFO mnpfound, pll);
return mnpfound;
}
 
static void g450_addcache(struct matrox_pll_cache* ci, unsigned int mnp_key, unsigned int mnp_value) {
if (++ci->valid > ARRAY_SIZE(ci->data)) {
ci->valid = ARRAY_SIZE(ci->data);
}
memmove(ci->data + 1, ci->data, (ci->valid - 1) * sizeof(*ci->data));
ci->data[0].mnp_key = mnp_key & G450_MNP_FREQBITS;
ci->data[0].mnp_value = mnp_value;
}
 
static int g450_checkcache(WPMINFO struct matrox_pll_cache* ci, unsigned int mnp_key) {
unsigned int i;
mnp_key &= G450_MNP_FREQBITS;
for (i = 0; i < ci->valid; i++) {
if (ci->data[i].mnp_key == mnp_key) {
unsigned int mnp;
mnp = ci->data[i].mnp_value;
if (i) {
memmove(ci->data + 1, ci->data, i * sizeof(*ci->data));
ci->data[0].mnp_key = mnp_key;
ci->data[0].mnp_value = mnp;
}
return mnp;
}
}
return NO_MORE_MNP;
}
 
static int __g450_setclk(WPMINFO unsigned int fout, unsigned int pll,
unsigned int* mnparray, unsigned int* deltaarray) {
unsigned int mnpcount;
unsigned int pixel_vco;
const struct matrox_pll_limits* pi;
struct matrox_pll_cache* ci;
 
pixel_vco = 0;
switch (pll) {
case M_PIXEL_PLL_A:
case M_PIXEL_PLL_B:
case M_PIXEL_PLL_C:
{
u_int8_t tmp;
unsigned long flags;
matroxfb_DAC_lock_irqsave(flags);
tmp = matroxfb_DAC_in(PMINFO M1064_XPIXCLKCTRL);
if (!(tmp & M1064_XPIXCLKCTRL_PLL_UP)) {
matroxfb_DAC_out(PMINFO M1064_XPIXCLKCTRL, tmp | M1064_XPIXCLKCTRL_PLL_UP);
}
matroxfb_DAC_unlock_irqrestore(flags);
}
{
u_int8_t misc;
misc = mga_inb(M_MISC_REG_READ) & ~0x0C;
switch (pll) {
case M_PIXEL_PLL_A:
break;
case M_PIXEL_PLL_B:
misc |= 0x04;
break;
default:
misc |= 0x0C;
break;
}
mga_outb(M_MISC_REG, misc);
}
pi = &ACCESS_FBINFO(limits.pixel);
ci = &ACCESS_FBINFO(cache.pixel);
break;
case M_SYSTEM_PLL:
{
u_int32_t opt;
 
pci_read_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, (void *)&opt);
if (!(opt & 0x20)) {
pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, opt | 0x20);
}
}
pi = &ACCESS_FBINFO(limits.system);
ci = &ACCESS_FBINFO(cache.system);
break;
case M_VIDEO_PLL:
{
u_int8_t tmp;
unsigned int mnp;
unsigned long flags;
matroxfb_DAC_lock_irqsave(flags);
tmp = matroxfb_DAC_in(PMINFO M1064_XPWRCTRL);
if (!(tmp & 2)) {
matroxfb_DAC_out(PMINFO M1064_XPWRCTRL, tmp | 2);
}
mnp = matroxfb_DAC_in(PMINFO M1064_XPIXPLLCM) << 16;
mnp |= matroxfb_DAC_in(PMINFO M1064_XPIXPLLCN) << 8;
pixel_vco = g450_mnp2vco(PMINFO mnp);
matroxfb_DAC_unlock_irqrestore(flags);
}
pi = &ACCESS_FBINFO(limits.video);
ci = &ACCESS_FBINFO(cache.video);
break;
default:
return -EINVAL;
}
 
mnpcount = 0;
{
unsigned int mnp;
unsigned int xvco;
 
for(mnp = g450_firstpll(PMINFO pi, &xvco, fout); mnp != NO_MORE_MNP; mnp = g450_nextpll(PMINFO pi, &xvco, mnp)) {
unsigned int idx;
unsigned int vco;
unsigned int delta;
 
vco = g450_mnp2vco(PMINFO mnp);
#if 0
if (pll == M_VIDEO_PLL) {
unsigned int big, small;
 
if (vco < pixel_vco) {
small = vco;
big = pixel_vco;
} else {
small = pixel_vco;
big = vco;
}
while (big > small) {
big >>= 1;
}
if (big == small) {
continue;
}
}
#endif
delta = pll_freq_delta(fout, g450_vco2f(mnp, vco));
for (idx = mnpcount; idx > 0; idx--) {
/* == is important; due to nextpll algorithm we get
sorted equally good frequencies from lower VCO
frequency to higher - with <= lowest wins, while
with < highest one wins */
if (delta <= deltaarray[idx-1]) {
mnparray[idx] = mnparray[idx-1];
deltaarray[idx] = deltaarray[idx-1];
} else {
break;
}
}
mnparray[idx] = mnp;
deltaarray[idx] = delta;
mnpcount++;
}
}
/* VideoPLL and PixelPLL matched: do nothing... In all other cases we should get at least one frequency */
if (!mnpcount) {
return -EBUSY;
}
{
unsigned long flags;
unsigned int mnp;
matroxfb_DAC_lock_irqsave(flags);
mnp = g450_checkcache(PMINFO ci, mnparray[0]);
if (mnp != NO_MORE_MNP) {
matroxfb_g450_setpll_cond(PMINFO mnp, pll);
} else {
mnp = g450_findworkingpll(PMINFO pll, mnparray, mnpcount);
g450_addcache(ci, mnparray[0], mnp);
}
updatehwstate_clk(&ACCESS_FBINFO(hw), mnp, pll);
matroxfb_DAC_unlock_irqrestore(flags);
return mnp;
}
}
 
/* It must be greater than number of possible PLL values.
* Currently there is 5(p) * 10(m) = 50 possible values. */
#define MNP_TABLE_SIZE 64
 
int matroxfb_g450_setclk(WPMINFO unsigned int fout, unsigned int pll) {
unsigned int* arr;
arr = kmalloc(sizeof(*arr) * MNP_TABLE_SIZE * 2, GFP_KERNEL);
if (arr) {
int r;
 
r = __g450_setclk(PMINFO fout, pll, arr, arr + MNP_TABLE_SIZE);
kfree(arr);
return r;
}
return -ENOMEM;
}
 
EXPORT_SYMBOL(matroxfb_g450_setclk);
EXPORT_SYMBOL(g450_mnp2f);
EXPORT_SYMBOL(matroxfb_g450_setpll_cond);
 
MODULE_AUTHOR("(c) 2001-2002 Petr Vandrovec <vandrove@vc.cvut.cz>");
MODULE_DESCRIPTION("Matrox G450/G550 PLL driver");
 
MODULE_LICENSE("GPL");
/shark/trunk/drivers/fb/matrox/matroxfb_Ti3026.c
0,0 → 1,741
/*
*
* Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200 and G400
*
* (c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz>
*
* Portions Copyright (c) 2001 Matrox Graphics Inc.
*
* Version: 1.65 2002/08/14
*
* MTRR stuff: 1998 Tom Rini <trini@kernel.crashing.org>
*
* Contributors: "menion?" <menion@mindless.com>
* Betatesting, fixes, ideas
*
* "Kurt Garloff" <garloff@suse.de>
* Betatesting, fixes, ideas, videomodes, videomodes timmings
*
* "Tom Rini" <trini@kernel.crashing.org>
* MTRR stuff, PPC cleanups, betatesting, fixes, ideas
*
* "Bibek Sahu" <scorpio@dodds.net>
* Access device through readb|w|l and write b|w|l
* Extensive debugging stuff
*
* "Daniel Haun" <haund@usa.net>
* Testing, hardware cursor fixes
*
* "Scott Wood" <sawst46+@pitt.edu>
* Fixes
*
* "Gerd Knorr" <kraxel@goldbach.isdn.cs.tu-berlin.de>
* Betatesting
*
* "Kelly French" <targon@hazmat.com>
* "Fernando Herrera" <fherrera@eurielec.etsit.upm.es>
* Betatesting, bug reporting
*
* "Pablo Bianucci" <pbian@pccp.com.ar>
* Fixes, ideas, betatesting
*
* "Inaky Perez Gonzalez" <inaky@peloncho.fis.ucm.es>
* Fixes, enhandcements, ideas, betatesting
*
* "Ryuichi Oikawa" <roikawa@rr.iiij4u.or.jp>
* PPC betatesting, PPC support, backward compatibility
*
* "Paul Womar" <Paul@pwomar.demon.co.uk>
* "Owen Waller" <O.Waller@ee.qub.ac.uk>
* PPC betatesting
*
* "Thomas Pornin" <pornin@bolet.ens.fr>
* Alpha betatesting
*
* "Pieter van Leuven" <pvl@iae.nl>
* "Ulf Jaenicke-Roessler" <ujr@physik.phy.tu-dresden.de>
* G100 testing
*
* "H. Peter Arvin" <hpa@transmeta.com>
* Ideas
*
* "Cort Dougan" <cort@cs.nmt.edu>
* CHRP fixes and PReP cleanup
*
* "Mark Vojkovich" <mvojkovi@ucsd.edu>
* G400 support
*
* (following author is not in any relation with this code, but his code
* is included in this driver)
*
* Based on framebuffer driver for VBE 2.0 compliant graphic boards
* (c) 1998 Gerd Knorr <kraxel@cs.tu-berlin.de>
*
* (following author is not in any relation with this code, but his ideas
* were used when writting this driver)
*
* FreeVBE/AF (Matrox), "Shawn Hargreaves" <shawn@talula.demon.co.uk>
*
*/
 
/* make checkconfig does not verify included files... */
#include <linuxcomp.h>
 
#include <linux/config.h>
 
#include "matroxfb_Ti3026.h"
#include "matroxfb_misc.h"
#include "matroxfb_accel.h"
#include <linux/matroxfb.h>
 
#ifdef CONFIG_FB_MATROX_MILLENIUM
#define outTi3026 matroxfb_DAC_out
#define inTi3026 matroxfb_DAC_in
 
#define TVP3026_INDEX 0x00
#define TVP3026_PALWRADD 0x00
#define TVP3026_PALDATA 0x01
#define TVP3026_PIXRDMSK 0x02
#define TVP3026_PALRDADD 0x03
#define TVP3026_CURCOLWRADD 0x04
#define TVP3026_CLOVERSCAN 0x00
#define TVP3026_CLCOLOR0 0x01
#define TVP3026_CLCOLOR1 0x02
#define TVP3026_CLCOLOR2 0x03
#define TVP3026_CURCOLDATA 0x05
#define TVP3026_CURCOLRDADD 0x07
#define TVP3026_CURCTRL 0x09
#define TVP3026_X_DATAREG 0x0A
#define TVP3026_CURRAMDATA 0x0B
#define TVP3026_CURPOSXL 0x0C
#define TVP3026_CURPOSXH 0x0D
#define TVP3026_CURPOSYL 0x0E
#define TVP3026_CURPOSYH 0x0F
 
#define TVP3026_XSILICONREV 0x01
#define TVP3026_XCURCTRL 0x06
#define TVP3026_XCURCTRL_DIS 0x00 /* transparent, transparent, transparent, transparent */
#define TVP3026_XCURCTRL_3COLOR 0x01 /* transparent, 0, 1, 2 */
#define TVP3026_XCURCTRL_XGA 0x02 /* 0, 1, transparent, complement */
#define TVP3026_XCURCTRL_XWIN 0x03 /* transparent, transparent, 0, 1 */
#define TVP3026_XCURCTRL_BLANK2048 0x00
#define TVP3026_XCURCTRL_BLANK4096 0x10
#define TVP3026_XCURCTRL_INTERLACED 0x20
#define TVP3026_XCURCTRL_ODD 0x00 /* ext.signal ODD/\EVEN */
#define TVP3026_XCURCTRL_EVEN 0x40 /* ext.signal EVEN/\ODD */
#define TVP3026_XCURCTRL_INDIRECT 0x00
#define TVP3026_XCURCTRL_DIRECT 0x80
#define TVP3026_XLATCHCTRL 0x0F
#define TVP3026_XLATCHCTRL_1_1 0x06
#define TVP3026_XLATCHCTRL_2_1 0x07
#define TVP3026_XLATCHCTRL_4_1 0x06
#define TVP3026_XLATCHCTRL_8_1 0x06
#define TVP3026_XLATCHCTRL_16_1 0x06
#define TVP3026A_XLATCHCTRL_4_3 0x06 /* ??? do not understand... but it works... !!! */
#define TVP3026A_XLATCHCTRL_8_3 0x07
#define TVP3026B_XLATCHCTRL_4_3 0x08
#define TVP3026B_XLATCHCTRL_8_3 0x06 /* ??? do not understand... but it works... !!! */
#define TVP3026_XTRUECOLORCTRL 0x18
#define TVP3026_XTRUECOLORCTRL_VRAM_SHIFT_ACCEL 0x00
#define TVP3026_XTRUECOLORCTRL_VRAM_SHIFT_TVP 0x20
#define TVP3026_XTRUECOLORCTRL_PSEUDOCOLOR 0x80
#define TVP3026_XTRUECOLORCTRL_TRUECOLOR 0x40 /* paletized */
#define TVP3026_XTRUECOLORCTRL_DIRECTCOLOR 0x00
#define TVP3026_XTRUECOLORCTRL_24_ALTERNATE 0x08 /* 5:4/5:2 instead of 4:3/8:3 */
#define TVP3026_XTRUECOLORCTRL_RGB_888 0x16 /* 4:3/8:3 (or 5:4/5:2) */
#define TVP3026_XTRUECOLORCTRL_BGR_888 0x17
#define TVP3026_XTRUECOLORCTRL_ORGB_8888 0x06
#define TVP3026_XTRUECOLORCTRL_BGRO_8888 0x07
#define TVP3026_XTRUECOLORCTRL_RGB_565 0x05
#define TVP3026_XTRUECOLORCTRL_ORGB_1555 0x04
#define TVP3026_XTRUECOLORCTRL_RGB_664 0x03
#define TVP3026_XTRUECOLORCTRL_RGBO_4444 0x01
#define TVP3026_XMUXCTRL 0x19
#define TVP3026_XMUXCTRL_MEMORY_8BIT 0x01 /* - */
#define TVP3026_XMUXCTRL_MEMORY_16BIT 0x02 /* - */
#define TVP3026_XMUXCTRL_MEMORY_32BIT 0x03 /* 2MB RAM, 512K * 4 */
#define TVP3026_XMUXCTRL_MEMORY_64BIT 0x04 /* >2MB RAM, 512K * 8 & more */
#define TVP3026_XMUXCTRL_PIXEL_4BIT 0x40 /* L0,H0,L1,H1... */
#define TVP3026_XMUXCTRL_PIXEL_4BIT_SWAPPED 0x60 /* H0,L0,H1,L1... */
#define TVP3026_XMUXCTRL_PIXEL_8BIT 0x48
#define TVP3026_XMUXCTRL_PIXEL_16BIT 0x50
#define TVP3026_XMUXCTRL_PIXEL_32BIT 0x58
#define TVP3026_XMUXCTRL_VGA 0x98 /* VGA MEMORY, 8BIT PIXEL */
#define TVP3026_XCLKCTRL 0x1A
#define TVP3026_XCLKCTRL_DIV1 0x00
#define TVP3026_XCLKCTRL_DIV2 0x10
#define TVP3026_XCLKCTRL_DIV4 0x20
#define TVP3026_XCLKCTRL_DIV8 0x30
#define TVP3026_XCLKCTRL_DIV16 0x40
#define TVP3026_XCLKCTRL_DIV32 0x50
#define TVP3026_XCLKCTRL_DIV64 0x60
#define TVP3026_XCLKCTRL_CLKSTOPPED 0x70
#define TVP3026_XCLKCTRL_SRC_CLK0 0x00
#define TVP3026_XCLKCTRL_SRC_CLK1 0x01
#define TVP3026_XCLKCTRL_SRC_CLK2 0x02 /* CLK2 is TTL source*/
#define TVP3026_XCLKCTRL_SRC_NCLK2 0x03 /* not CLK2 is TTL source */
#define TVP3026_XCLKCTRL_SRC_ECLK2 0x04 /* CLK2 and not CLK2 is ECL source */
#define TVP3026_XCLKCTRL_SRC_PLL 0x05
#define TVP3026_XCLKCTRL_SRC_DIS 0x06 /* disable & poweroff internal clock */
#define TVP3026_XCLKCTRL_SRC_CLK0VGA 0x07
#define TVP3026_XPALETTEPAGE 0x1C
#define TVP3026_XGENCTRL 0x1D
#define TVP3026_XGENCTRL_HSYNC_POS 0x00
#define TVP3026_XGENCTRL_HSYNC_NEG 0x01
#define TVP3026_XGENCTRL_VSYNC_POS 0x00
#define TVP3026_XGENCTRL_VSYNC_NEG 0x02
#define TVP3026_XGENCTRL_LITTLE_ENDIAN 0x00
#define TVP3026_XGENCTRL_BIG_ENDIAN 0x08
#define TVP3026_XGENCTRL_BLACK_0IRE 0x00
#define TVP3026_XGENCTRL_BLACK_75IRE 0x10
#define TVP3026_XGENCTRL_NO_SYNC_ON_GREEN 0x00
#define TVP3026_XGENCTRL_SYNC_ON_GREEN 0x20
#define TVP3026_XGENCTRL_OVERSCAN_DIS 0x00
#define TVP3026_XGENCTRL_OVERSCAN_EN 0x40
#define TVP3026_XMISCCTRL 0x1E
#define TVP3026_XMISCCTRL_DAC_PUP 0x00
#define TVP3026_XMISCCTRL_DAC_PDOWN 0x01
#define TVP3026_XMISCCTRL_DAC_EXT 0x00 /* or 8, bit 3 is ignored */
#define TVP3026_XMISCCTRL_DAC_6BIT 0x04
#define TVP3026_XMISCCTRL_DAC_8BIT 0x0C
#define TVP3026_XMISCCTRL_PSEL_DIS 0x00
#define TVP3026_XMISCCTRL_PSEL_EN 0x10
#define TVP3026_XMISCCTRL_PSEL_LOW 0x00 /* PSEL high selects directcolor */
#define TVP3026_XMISCCTRL_PSEL_HIGH 0x20 /* PSEL high selects truecolor or pseudocolor */
#define TVP3026_XGENIOCTRL 0x2A
#define TVP3026_XGENIODATA 0x2B
#define TVP3026_XPLLADDR 0x2C
#define TVP3026_XPLLADDR_X(LOOP,MCLK,PIX) (((LOOP)<<4) | ((MCLK)<<2) | (PIX))
#define TVP3026_XPLLDATA_N 0x00
#define TVP3026_XPLLDATA_M 0x01
#define TVP3026_XPLLDATA_P 0x02
#define TVP3026_XPLLDATA_STAT 0x03
#define TVP3026_XPIXPLLDATA 0x2D
#define TVP3026_XMEMPLLDATA 0x2E
#define TVP3026_XLOOPPLLDATA 0x2F
#define TVP3026_XCOLKEYOVRMIN 0x30
#define TVP3026_XCOLKEYOVRMAX 0x31
#define TVP3026_XCOLKEYREDMIN 0x32
#define TVP3026_XCOLKEYREDMAX 0x33
#define TVP3026_XCOLKEYGREENMIN 0x34
#define TVP3026_XCOLKEYGREENMAX 0x35
#define TVP3026_XCOLKEYBLUEMIN 0x36
#define TVP3026_XCOLKEYBLUEMAX 0x37
#define TVP3026_XCOLKEYCTRL 0x38
#define TVP3026_XCOLKEYCTRL_OVR_EN 0x01
#define TVP3026_XCOLKEYCTRL_RED_EN 0x02
#define TVP3026_XCOLKEYCTRL_GREEN_EN 0x04
#define TVP3026_XCOLKEYCTRL_BLUE_EN 0x08
#define TVP3026_XCOLKEYCTRL_NEGATE 0x10
#define TVP3026_XCOLKEYCTRL_ZOOM1 0x00
#define TVP3026_XCOLKEYCTRL_ZOOM2 0x20
#define TVP3026_XCOLKEYCTRL_ZOOM4 0x40
#define TVP3026_XCOLKEYCTRL_ZOOM8 0x60
#define TVP3026_XCOLKEYCTRL_ZOOM16 0x80
#define TVP3026_XCOLKEYCTRL_ZOOM32 0xA0
#define TVP3026_XMEMPLLCTRL 0x39
#define TVP3026_XMEMPLLCTRL_DIV(X) (((X)-1)>>1) /* 2,4,6,8,10,12,14,16, division applied to LOOP PLL after divide by 2^P */
#define TVP3026_XMEMPLLCTRL_STROBEMKC4 0x08
#define TVP3026_XMEMPLLCTRL_MCLK_DOTCLOCK 0x00 /* MKC4 */
#define TVP3026_XMEMPLLCTRL_MCLK_MCLKPLL 0x10 /* MKC4 */
#define TVP3026_XMEMPLLCTRL_RCLK_PIXPLL 0x00
#define TVP3026_XMEMPLLCTRL_RCLK_LOOPPLL 0x20
#define TVP3026_XMEMPLLCTRL_RCLK_DOTDIVN 0x40 /* dot clock divided by loop pclk N prescaler */
#define TVP3026_XSENSETEST 0x3A
#define TVP3026_XTESTMODEDATA 0x3B
#define TVP3026_XCRCREML 0x3C
#define TVP3026_XCRCREMH 0x3D
#define TVP3026_XCRCBITSEL 0x3E
#define TVP3026_XID 0x3F
 
static const unsigned char DACseq[] =
{ TVP3026_XLATCHCTRL, TVP3026_XTRUECOLORCTRL,
TVP3026_XMUXCTRL, TVP3026_XCLKCTRL,
TVP3026_XPALETTEPAGE,
TVP3026_XGENCTRL,
TVP3026_XMISCCTRL,
TVP3026_XGENIOCTRL,
TVP3026_XGENIODATA,
TVP3026_XCOLKEYOVRMIN, TVP3026_XCOLKEYOVRMAX, TVP3026_XCOLKEYREDMIN, TVP3026_XCOLKEYREDMAX,
TVP3026_XCOLKEYGREENMIN, TVP3026_XCOLKEYGREENMAX, TVP3026_XCOLKEYBLUEMIN, TVP3026_XCOLKEYBLUEMAX,
TVP3026_XCOLKEYCTRL,
TVP3026_XMEMPLLCTRL, TVP3026_XSENSETEST, TVP3026_XCURCTRL };
 
#define POS3026_XLATCHCTRL 0
#define POS3026_XTRUECOLORCTRL 1
#define POS3026_XMUXCTRL 2
#define POS3026_XCLKCTRL 3
#define POS3026_XGENCTRL 5
#define POS3026_XMISCCTRL 6
#define POS3026_XMEMPLLCTRL 18
#define POS3026_XCURCTRL 20
 
static const unsigned char MGADACbpp32[] =
{ TVP3026_XLATCHCTRL_2_1, TVP3026_XTRUECOLORCTRL_DIRECTCOLOR | TVP3026_XTRUECOLORCTRL_ORGB_8888,
0x00, TVP3026_XCLKCTRL_DIV1 | TVP3026_XCLKCTRL_SRC_PLL,
0x00,
TVP3026_XGENCTRL_HSYNC_POS | TVP3026_XGENCTRL_VSYNC_POS | TVP3026_XGENCTRL_LITTLE_ENDIAN | TVP3026_XGENCTRL_BLACK_0IRE | TVP3026_XGENCTRL_NO_SYNC_ON_GREEN | TVP3026_XGENCTRL_OVERSCAN_DIS,
TVP3026_XMISCCTRL_DAC_PUP | TVP3026_XMISCCTRL_DAC_8BIT | TVP3026_XMISCCTRL_PSEL_DIS | TVP3026_XMISCCTRL_PSEL_HIGH,
0x00,
0x1E,
0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF,
TVP3026_XCOLKEYCTRL_ZOOM1,
0x00, 0x00, TVP3026_XCURCTRL_DIS };
 
static int Ti3026_calcclock(CPMINFO unsigned int freq, unsigned int fmax, int* in, int* feed, int* post) {
unsigned int fvco;
unsigned int lin, lfeed, lpost;
 
DBG(__FUNCTION__)
 
fvco = PLL_calcclock(PMINFO freq, fmax, &lin, &lfeed, &lpost);
fvco >>= (*post = lpost);
*in = 64 - lin;
*feed = 64 - lfeed;
return fvco;
}
 
static int Ti3026_setpclk(WPMINFO int clk) {
unsigned int f_pll;
unsigned int pixfeed, pixin, pixpost;
struct matrox_hw_state* hw = &ACCESS_FBINFO(hw);
 
DBG(__FUNCTION__)
 
f_pll = Ti3026_calcclock(PMINFO clk, ACCESS_FBINFO(max_pixel_clock), &pixin, &pixfeed, &pixpost);
 
hw->DACclk[0] = pixin | 0xC0;
hw->DACclk[1] = pixfeed;
hw->DACclk[2] = pixpost | 0xB0;
 
{
unsigned int loopfeed, loopin, looppost, loopdiv, z;
unsigned int Bpp;
 
Bpp = ACCESS_FBINFO(curr.final_bppShift);
 
if (ACCESS_FBINFO(fbcon).var.bits_per_pixel == 24) {
loopfeed = 3; /* set lm to any possible value */
loopin = 3 * 32 / Bpp;
} else {
loopfeed = 4;
loopin = 4 * 32 / Bpp;
}
z = (110000 * loopin) / (f_pll * loopfeed);
loopdiv = 0; /* div 2 */
if (z < 2)
looppost = 0;
else if (z < 4)
looppost = 1;
else if (z < 8)
looppost = 2;
else {
looppost = 3;
loopdiv = z/16;
}
if (ACCESS_FBINFO(fbcon).var.bits_per_pixel == 24) {
hw->DACclk[3] = ((65 - loopin) & 0x3F) | 0xC0;
hw->DACclk[4] = (65 - loopfeed) | 0x80;
if (ACCESS_FBINFO(accel.ramdac_rev) > 0x20) {
if (isInterleave(MINFO))
hw->DACreg[POS3026_XLATCHCTRL] = TVP3026B_XLATCHCTRL_8_3;
else {
hw->DACclk[4] &= ~0xC0;
hw->DACreg[POS3026_XLATCHCTRL] = TVP3026B_XLATCHCTRL_4_3;
}
} else {
if (isInterleave(MINFO))
; /* default... */
else {
hw->DACclk[4] ^= 0xC0; /* change from 0x80 to 0x40 */
hw->DACreg[POS3026_XLATCHCTRL] = TVP3026A_XLATCHCTRL_4_3;
}
}
hw->DACclk[5] = looppost | 0xF8;
if (ACCESS_FBINFO(devflags.mga_24bpp_fix))
hw->DACclk[5] ^= 0x40;
} else {
hw->DACclk[3] = ((65 - loopin) & 0x3F) | 0xC0;
hw->DACclk[4] = 65 - loopfeed;
hw->DACclk[5] = looppost | 0xF0;
}
hw->DACreg[POS3026_XMEMPLLCTRL] = loopdiv | TVP3026_XMEMPLLCTRL_MCLK_MCLKPLL | TVP3026_XMEMPLLCTRL_RCLK_LOOPPLL;
}
return 0;
}
 
static int Ti3026_init(WPMINFO struct my_timming* m) {
u_int8_t muxctrl = isInterleave(MINFO) ? TVP3026_XMUXCTRL_MEMORY_64BIT : TVP3026_XMUXCTRL_MEMORY_32BIT;
struct matrox_hw_state* hw = &ACCESS_FBINFO(hw);
 
DBG(__FUNCTION__)
 
memcpy(hw->DACreg, MGADACbpp32, sizeof(hw->DACreg));
switch (ACCESS_FBINFO(fbcon).var.bits_per_pixel) {
case 4: hw->DACreg[POS3026_XLATCHCTRL] = TVP3026_XLATCHCTRL_16_1; /* or _8_1, they are same */
hw->DACreg[POS3026_XTRUECOLORCTRL] = TVP3026_XTRUECOLORCTRL_PSEUDOCOLOR;
hw->DACreg[POS3026_XMUXCTRL] = muxctrl | TVP3026_XMUXCTRL_PIXEL_4BIT;
hw->DACreg[POS3026_XCLKCTRL] = TVP3026_XCLKCTRL_SRC_PLL | TVP3026_XCLKCTRL_DIV8;
hw->DACreg[POS3026_XMISCCTRL] = TVP3026_XMISCCTRL_DAC_PUP | TVP3026_XMISCCTRL_DAC_8BIT | TVP3026_XMISCCTRL_PSEL_DIS | TVP3026_XMISCCTRL_PSEL_LOW;
break;
case 8: hw->DACreg[POS3026_XLATCHCTRL] = TVP3026_XLATCHCTRL_8_1; /* or _4_1, they are same */
hw->DACreg[POS3026_XTRUECOLORCTRL] = TVP3026_XTRUECOLORCTRL_PSEUDOCOLOR;
hw->DACreg[POS3026_XMUXCTRL] = muxctrl | TVP3026_XMUXCTRL_PIXEL_8BIT;
hw->DACreg[POS3026_XCLKCTRL] = TVP3026_XCLKCTRL_SRC_PLL | TVP3026_XCLKCTRL_DIV4;
hw->DACreg[POS3026_XMISCCTRL] = TVP3026_XMISCCTRL_DAC_PUP | TVP3026_XMISCCTRL_DAC_8BIT | TVP3026_XMISCCTRL_PSEL_DIS | TVP3026_XMISCCTRL_PSEL_LOW;
break;
case 16:
/* XLATCHCTRL should be _4_1 / _2_1... Why is not? (_2_1 is used everytime) */
hw->DACreg[POS3026_XTRUECOLORCTRL] = (ACCESS_FBINFO(fbcon).var.green.length == 5)? (TVP3026_XTRUECOLORCTRL_DIRECTCOLOR | TVP3026_XTRUECOLORCTRL_ORGB_1555 ) : (TVP3026_XTRUECOLORCTRL_DIRECTCOLOR | TVP3026_XTRUECOLORCTRL_RGB_565);
hw->DACreg[POS3026_XMUXCTRL] = muxctrl | TVP3026_XMUXCTRL_PIXEL_16BIT;
hw->DACreg[POS3026_XCLKCTRL] = TVP3026_XCLKCTRL_SRC_PLL | TVP3026_XCLKCTRL_DIV2;
break;
case 24:
/* XLATCHCTRL is: for (A) use _4_3 (?_8_3 is same? TBD), for (B) it is set in setpclk */
hw->DACreg[POS3026_XTRUECOLORCTRL] = TVP3026_XTRUECOLORCTRL_DIRECTCOLOR | TVP3026_XTRUECOLORCTRL_RGB_888;
hw->DACreg[POS3026_XMUXCTRL] = muxctrl | TVP3026_XMUXCTRL_PIXEL_32BIT;
hw->DACreg[POS3026_XCLKCTRL] = TVP3026_XCLKCTRL_SRC_PLL | TVP3026_XCLKCTRL_DIV4;
break;
case 32:
/* XLATCHCTRL should be _2_1 / _1_1... Why is not? (_2_1 is used everytime) */
hw->DACreg[POS3026_XMUXCTRL] = muxctrl | TVP3026_XMUXCTRL_PIXEL_32BIT;
break;
default:
return 1; /* TODO: failed */
}
if (matroxfb_vgaHWinit(PMINFO m)) return 1;
 
/* set SYNC */
hw->MiscOutReg = 0xCB;
if (m->sync & FB_SYNC_HOR_HIGH_ACT)
hw->DACreg[POS3026_XGENCTRL] |= TVP3026_XGENCTRL_HSYNC_NEG;
if (m->sync & FB_SYNC_VERT_HIGH_ACT)
hw->DACreg[POS3026_XGENCTRL] |= TVP3026_XGENCTRL_VSYNC_NEG;
if (m->sync & FB_SYNC_ON_GREEN)
hw->DACreg[POS3026_XGENCTRL] |= TVP3026_XGENCTRL_SYNC_ON_GREEN;
 
/* set DELAY */
if (ACCESS_FBINFO(video.len) < 0x400000)
hw->CRTCEXT[3] |= 0x08;
else if (ACCESS_FBINFO(video.len) > 0x400000)
hw->CRTCEXT[3] |= 0x10;
 
/* set HWCURSOR */
if (m->interlaced) {
hw->DACreg[POS3026_XCURCTRL] |= TVP3026_XCURCTRL_INTERLACED;
}
if (m->HTotal >= 1536)
hw->DACreg[POS3026_XCURCTRL] |= TVP3026_XCURCTRL_BLANK4096;
 
/* set interleaving */
hw->MXoptionReg &= ~0x00001000;
if (isInterleave(MINFO)) hw->MXoptionReg |= 0x00001000;
 
/* set DAC */
Ti3026_setpclk(PMINFO m->pixclock);
return 0;
}
 
static void ti3026_setMCLK(WPMINFO int fout){
unsigned int f_pll;
unsigned int pclk_m, pclk_n, pclk_p;
unsigned int mclk_m, mclk_n, mclk_p;
unsigned int rfhcnt, mclk_ctl;
int tmout;
 
DBG(__FUNCTION__)
 
f_pll = Ti3026_calcclock(PMINFO fout, ACCESS_FBINFO(max_pixel_clock), &mclk_n, &mclk_m, &mclk_p);
 
/* save pclk */
outTi3026(PMINFO TVP3026_XPLLADDR, 0xFC);
pclk_n = inTi3026(PMINFO TVP3026_XPIXPLLDATA);
outTi3026(PMINFO TVP3026_XPLLADDR, 0xFD);
pclk_m = inTi3026(PMINFO TVP3026_XPIXPLLDATA);
outTi3026(PMINFO TVP3026_XPLLADDR, 0xFE);
pclk_p = inTi3026(PMINFO TVP3026_XPIXPLLDATA);
 
/* stop pclk */
outTi3026(PMINFO TVP3026_XPLLADDR, 0xFE);
outTi3026(PMINFO TVP3026_XPIXPLLDATA, 0x00);
 
/* set pclk to new mclk */
outTi3026(PMINFO TVP3026_XPLLADDR, 0xFC);
outTi3026(PMINFO TVP3026_XPIXPLLDATA, mclk_n | 0xC0);
outTi3026(PMINFO TVP3026_XPIXPLLDATA, mclk_m);
outTi3026(PMINFO TVP3026_XPIXPLLDATA, mclk_p | 0xB0);
 
/* wait for PLL to lock */
for (tmout = 500000; tmout; tmout--) {
if (inTi3026(PMINFO TVP3026_XPIXPLLDATA) & 0x40)
break;
udelay(10);
};
if (!tmout)
printk(KERN_ERR "matroxfb: Temporary pixel PLL not locked after 5 secs\n");
 
/* output pclk on mclk pin */
mclk_ctl = inTi3026(PMINFO TVP3026_XMEMPLLCTRL);
outTi3026(PMINFO TVP3026_XMEMPLLCTRL, mclk_ctl & 0xE7);
outTi3026(PMINFO TVP3026_XMEMPLLCTRL, (mclk_ctl & 0xE7) | TVP3026_XMEMPLLCTRL_STROBEMKC4);
 
/* stop MCLK */
outTi3026(PMINFO TVP3026_XPLLADDR, 0xFB);
outTi3026(PMINFO TVP3026_XMEMPLLDATA, 0x00);
 
/* set mclk to new freq */
outTi3026(PMINFO TVP3026_XPLLADDR, 0xF3);
outTi3026(PMINFO TVP3026_XMEMPLLDATA, mclk_n | 0xC0);
outTi3026(PMINFO TVP3026_XMEMPLLDATA, mclk_m);
outTi3026(PMINFO TVP3026_XMEMPLLDATA, mclk_p | 0xB0);
 
/* wait for PLL to lock */
for (tmout = 500000; tmout; tmout--) {
if (inTi3026(PMINFO TVP3026_XMEMPLLDATA) & 0x40)
break;
udelay(10);
}
if (!tmout)
printk(KERN_ERR "matroxfb: Memory PLL not locked after 5 secs\n");
 
f_pll = f_pll * 333 / (10000 << mclk_p);
if (isMilleniumII(MINFO)) {
rfhcnt = (f_pll - 128) / 256;
if (rfhcnt > 15)
rfhcnt = 15;
} else {
rfhcnt = (f_pll - 64) / 128;
if (rfhcnt > 15)
rfhcnt = 0;
}
ACCESS_FBINFO(hw).MXoptionReg = (ACCESS_FBINFO(hw).MXoptionReg & ~0x000F0000) | (rfhcnt << 16);
pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, ACCESS_FBINFO(hw).MXoptionReg);
 
/* output MCLK to MCLK pin */
outTi3026(PMINFO TVP3026_XMEMPLLCTRL, (mclk_ctl & 0xE7) | TVP3026_XMEMPLLCTRL_MCLK_MCLKPLL);
outTi3026(PMINFO TVP3026_XMEMPLLCTRL, (mclk_ctl ) | TVP3026_XMEMPLLCTRL_MCLK_MCLKPLL | TVP3026_XMEMPLLCTRL_STROBEMKC4);
 
/* stop PCLK */
outTi3026(PMINFO TVP3026_XPLLADDR, 0xFE);
outTi3026(PMINFO TVP3026_XPIXPLLDATA, 0x00);
 
/* restore pclk */
outTi3026(PMINFO TVP3026_XPLLADDR, 0xFC);
outTi3026(PMINFO TVP3026_XPIXPLLDATA, pclk_n);
outTi3026(PMINFO TVP3026_XPIXPLLDATA, pclk_m);
outTi3026(PMINFO TVP3026_XPIXPLLDATA, pclk_p);
 
/* wait for PLL to lock */
for (tmout = 500000; tmout; tmout--) {
if (inTi3026(PMINFO TVP3026_XPIXPLLDATA) & 0x40)
break;
udelay(10);
}
if (!tmout)
printk(KERN_ERR "matroxfb: Pixel PLL not locked after 5 secs\n");
}
 
static void ti3026_ramdac_init(WPMINFO2) {
 
DBG(__FUNCTION__)
 
ACCESS_FBINFO(features.pll.vco_freq_min) = 110000;
ACCESS_FBINFO(features.pll.ref_freq) = 114545;
ACCESS_FBINFO(features.pll.feed_div_min) = 2;
ACCESS_FBINFO(features.pll.feed_div_max) = 24;
ACCESS_FBINFO(features.pll.in_div_min) = 2;
ACCESS_FBINFO(features.pll.in_div_max) = 63;
ACCESS_FBINFO(features.pll.post_shift_max) = 3;
if (ACCESS_FBINFO(devflags.noinit))
return;
ti3026_setMCLK(PMINFO 60000);
}
 
static void Ti3026_restore(WPMINFO2) {
int i;
unsigned char progdac[6];
struct matrox_hw_state* hw = &ACCESS_FBINFO(hw);
CRITFLAGS
 
DBG(__FUNCTION__)
 
#ifdef DEBUG
dprintk(KERN_INFO "EXTVGA regs: ");
for (i = 0; i < 6; i++)
dprintk("%02X:", hw->CRTCEXT[i]);
dprintk("\n");
#endif
 
CRITBEGIN
 
pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg);
 
CRITEND
 
matroxfb_vgaHWrestore(PMINFO2);
 
CRITBEGIN
 
ACCESS_FBINFO(crtc1.panpos) = -1;
for (i = 0; i < 6; i++)
mga_setr(M_EXTVGA_INDEX, i, hw->CRTCEXT[i]);
 
for (i = 0; i < 21; i++) {
outTi3026(PMINFO DACseq[i], hw->DACreg[i]);
}
 
outTi3026(PMINFO TVP3026_XPLLADDR, 0x00);
progdac[0] = inTi3026(PMINFO TVP3026_XPIXPLLDATA);
progdac[3] = inTi3026(PMINFO TVP3026_XLOOPPLLDATA);
outTi3026(PMINFO TVP3026_XPLLADDR, 0x15);
progdac[1] = inTi3026(PMINFO TVP3026_XPIXPLLDATA);
progdac[4] = inTi3026(PMINFO TVP3026_XLOOPPLLDATA);
outTi3026(PMINFO TVP3026_XPLLADDR, 0x2A);
progdac[2] = inTi3026(PMINFO TVP3026_XPIXPLLDATA);
progdac[5] = inTi3026(PMINFO TVP3026_XLOOPPLLDATA);
 
CRITEND
if (memcmp(hw->DACclk, progdac, 6)) {
/* agrhh... setting up PLL is very slow on Millennium... */
/* Mystique PLL is locked in few ms, but Millennium PLL lock takes about 0.15 s... */
/* Maybe even we should call schedule() ? */
 
CRITBEGIN
outTi3026(PMINFO TVP3026_XCLKCTRL, hw->DACreg[POS3026_XCLKCTRL]);
outTi3026(PMINFO TVP3026_XPLLADDR, 0x2A);
outTi3026(PMINFO TVP3026_XLOOPPLLDATA, 0);
outTi3026(PMINFO TVP3026_XPIXPLLDATA, 0);
 
outTi3026(PMINFO TVP3026_XPLLADDR, 0x00);
for (i = 0; i < 3; i++)
outTi3026(PMINFO TVP3026_XPIXPLLDATA, hw->DACclk[i]);
/* wait for PLL only if PLL clock requested (always for PowerMode, never for VGA) */
if (hw->MiscOutReg & 0x08) {
int tmout;
outTi3026(PMINFO TVP3026_XPLLADDR, 0x3F);
for (tmout = 500000; tmout; --tmout) {
if (inTi3026(PMINFO TVP3026_XPIXPLLDATA) & 0x40)
break;
udelay(10);
}
 
CRITEND
 
if (!tmout)
printk(KERN_ERR "matroxfb: Pixel PLL not locked after 5 secs\n");
else
dprintk(KERN_INFO "PixelPLL: %d\n", 500000-tmout);
CRITBEGIN
}
outTi3026(PMINFO TVP3026_XMEMPLLCTRL, hw->DACreg[POS3026_XMEMPLLCTRL]);
outTi3026(PMINFO TVP3026_XPLLADDR, 0x00);
for (i = 3; i < 6; i++)
outTi3026(PMINFO TVP3026_XLOOPPLLDATA, hw->DACclk[i]);
CRITEND
if ((hw->MiscOutReg & 0x08) && ((hw->DACclk[5] & 0x80) == 0x80)) {
int tmout;
 
CRITBEGIN
outTi3026(PMINFO TVP3026_XPLLADDR, 0x3F);
for (tmout = 500000; tmout; --tmout) {
if (inTi3026(PMINFO TVP3026_XLOOPPLLDATA) & 0x40)
break;
udelay(10);
}
CRITEND
if (!tmout)
printk(KERN_ERR "matroxfb: Loop PLL not locked after 5 secs\n");
else
dprintk(KERN_INFO "LoopPLL: %d\n", 500000-tmout);
}
}
 
#ifdef DEBUG
dprintk(KERN_DEBUG "3026DACregs ");
for (i = 0; i < 21; i++) {
dprintk("R%02X=%02X ", DACseq[i], hw->DACreg[i]);
if ((i & 0x7) == 0x7) dprintk("\n" KERN_DEBUG "continuing... ");
}
dprintk("\n" KERN_DEBUG "DACclk ");
for (i = 0; i < 6; i++)
dprintk("C%02X=%02X ", i, hw->DACclk[i]);
dprintk("\n");
#endif
}
 
static void Ti3026_reset(WPMINFO2) {
 
DBG(__FUNCTION__)
 
ti3026_ramdac_init(PMINFO2);
}
 
static struct matrox_altout ti3026_output = {
.name = "Primary output",
};
 
static int Ti3026_preinit(WPMINFO2) {
static const int vxres_mill2[] = { 512, 640, 768, 800, 832, 960,
1024, 1152, 1280, 1600, 1664, 1920,
2048, 0};
static const int vxres_mill1[] = { 640, 768, 800, 960,
1024, 1152, 1280, 1600, 1920,
2048, 0};
struct matrox_hw_state* hw = &ACCESS_FBINFO(hw);
 
DBG(__FUNCTION__)
 
ACCESS_FBINFO(millenium) = 1;
ACCESS_FBINFO(milleniumII) = (ACCESS_FBINFO(pcidev)->device != PCI_DEVICE_ID_MATROX_MIL);
ACCESS_FBINFO(capable.cfb4) = 1;
ACCESS_FBINFO(capable.text) = 1; /* isMilleniumII(MINFO); */
ACCESS_FBINFO(capable.vxres) = isMilleniumII(MINFO)?vxres_mill2:vxres_mill1;
 
ACCESS_FBINFO(outputs[0]).data = MINFO;
ACCESS_FBINFO(outputs[0]).output = &ti3026_output;
ACCESS_FBINFO(outputs[0]).src = MATROXFB_SRC_CRTC1;
ACCESS_FBINFO(outputs[0]).mode = MATROXFB_OUTPUT_MODE_MONITOR;
 
if (ACCESS_FBINFO(devflags.noinit))
return 0;
/* preserve VGA I/O, BIOS and PPC */
hw->MXoptionReg &= 0xC0000100;
hw->MXoptionReg |= 0x002C0000;
if (ACCESS_FBINFO(devflags.novga))
hw->MXoptionReg &= ~0x00000100;
if (ACCESS_FBINFO(devflags.nobios))
hw->MXoptionReg &= ~0x40000000;
if (ACCESS_FBINFO(devflags.nopciretry))
hw->MXoptionReg |= 0x20000000;
pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg);
 
ACCESS_FBINFO(accel.ramdac_rev) = inTi3026(PMINFO TVP3026_XSILICONREV);
 
outTi3026(PMINFO TVP3026_XCLKCTRL, TVP3026_XCLKCTRL_SRC_CLK0VGA | TVP3026_XCLKCTRL_CLKSTOPPED);
outTi3026(PMINFO TVP3026_XTRUECOLORCTRL, TVP3026_XTRUECOLORCTRL_PSEUDOCOLOR);
outTi3026(PMINFO TVP3026_XMUXCTRL, TVP3026_XMUXCTRL_VGA);
 
outTi3026(PMINFO TVP3026_XPLLADDR, 0x2A);
outTi3026(PMINFO TVP3026_XLOOPPLLDATA, 0x00);
outTi3026(PMINFO TVP3026_XPIXPLLDATA, 0x00);
 
mga_outb(M_MISC_REG, 0x67);
 
outTi3026(PMINFO TVP3026_XMEMPLLCTRL, TVP3026_XMEMPLLCTRL_STROBEMKC4 | TVP3026_XMEMPLLCTRL_MCLK_MCLKPLL);
 
mga_outl(M_RESET, 1);
udelay(250);
mga_outl(M_RESET, 0);
udelay(250);
mga_outl(M_MACCESS, 0x00008000);
udelay(10);
return 0;
}
 
struct matrox_switch matrox_millennium = {
Ti3026_preinit, Ti3026_reset, Ti3026_init, Ti3026_restore
};
EXPORT_SYMBOL(matrox_millennium);
#endif
MODULE_LICENSE("GPL");
/shark/trunk/drivers/fb/matrox/g450_pll.h
0,0 → 1,10
#ifndef __G450_PLL_H__
#define __G450_PLL_H__
 
#include "matroxfb_base.h"
 
int matroxfb_g450_setclk(WPMINFO unsigned int fout, unsigned int pll);
unsigned int g450_mnp2f(CPMINFO unsigned int mnp);
void matroxfb_g450_setpll_cond(WPMINFO unsigned int mnp, unsigned int pll);
 
#endif /* __G450_PLL_H__ */
/shark/trunk/drivers/fb/matrox/i2c-matroxfb.c
0,0 → 1,223
/*
*
* Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200, G400 and G450.
*
* (c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz>
*
* Version: 1.64 2002/06/10
*
* See matroxfb_base.c for contributors.
*
*/
 
#include "matroxfb_base.h"
#include "matroxfb_maven.h"
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
 
/* MGA-TVO I2C for G200, G400 */
#define MAT_CLK 0x20
#define MAT_DATA 0x10
/* primary head DDC for Mystique(?), G100, G200, G400 */
#define DDC1_CLK 0x08
#define DDC1_DATA 0x02
/* primary head DDC for Millennium, Millennium II */
#define DDC1B_CLK 0x10
#define DDC1B_DATA 0x04
/* secondary head DDC for G400 */
#define DDC2_CLK 0x04
#define DDC2_DATA 0x01
 
/******************************************************/
 
struct matroxfb_dh_maven_info {
struct i2c_bit_adapter maven;
struct i2c_bit_adapter ddc1;
struct i2c_bit_adapter ddc2;
};
 
static int matroxfb_read_gpio(struct matrox_fb_info* minfo) {
unsigned long flags;
int v;
 
matroxfb_DAC_lock_irqsave(flags);
v = matroxfb_DAC_in(PMINFO DAC_XGENIODATA);
matroxfb_DAC_unlock_irqrestore(flags);
return v;
}
 
static void matroxfb_set_gpio(struct matrox_fb_info* minfo, int mask, int val) {
unsigned long flags;
int v;
 
matroxfb_DAC_lock_irqsave(flags);
v = (matroxfb_DAC_in(PMINFO DAC_XGENIOCTRL) & mask) | val;
matroxfb_DAC_out(PMINFO DAC_XGENIOCTRL, v);
/* We must reset GENIODATA very often... XFree plays with this register */
matroxfb_DAC_out(PMINFO DAC_XGENIODATA, 0x00);
matroxfb_DAC_unlock_irqrestore(flags);
}
 
/* software I2C functions */
static inline void matroxfb_i2c_set(struct matrox_fb_info* minfo, int mask, int state) {
if (state)
state = 0;
else
state = mask;
matroxfb_set_gpio(minfo, ~mask, state);
}
 
static void matroxfb_gpio_setsda(void* data, int state) {
struct i2c_bit_adapter* b = data;
matroxfb_i2c_set(b->minfo, b->mask.data, state);
}
 
static void matroxfb_gpio_setscl(void* data, int state) {
struct i2c_bit_adapter* b = data;
matroxfb_i2c_set(b->minfo, b->mask.clock, state);
}
 
static int matroxfb_gpio_getsda(void* data) {
struct i2c_bit_adapter* b = data;
return (matroxfb_read_gpio(b->minfo) & b->mask.data) ? 1 : 0;
}
 
static int matroxfb_gpio_getscl(void* data) {
struct i2c_bit_adapter* b = data;
return (matroxfb_read_gpio(b->minfo) & b->mask.clock) ? 1 : 0;
}
 
static struct i2c_adapter matrox_i2c_adapter_template =
{
.owner = THIS_MODULE,
.id = I2C_HW_B_G400,
};
 
static struct i2c_algo_bit_data matrox_i2c_algo_template =
{
NULL,
matroxfb_gpio_setsda,
matroxfb_gpio_setscl,
matroxfb_gpio_getsda,
matroxfb_gpio_getscl,
10, 10, 100,
};
 
static int i2c_bus_reg(struct i2c_bit_adapter* b, struct matrox_fb_info* minfo,
unsigned int data, unsigned int clock, const char* name) {
int err;
 
b->minfo = minfo;
b->mask.data = data;
b->mask.clock = clock;
b->adapter = matrox_i2c_adapter_template;
snprintf(b->adapter.name, I2C_NAME_SIZE, name,
minfo->fbcon.node);
i2c_set_adapdata(&b->adapter, b);
b->adapter.algo_data = &b->bac;
b->bac = matrox_i2c_algo_template;
b->bac.data = b;
err = i2c_bit_add_bus(&b->adapter);
b->initialized = !err;
return err;
}
 
static void i2c_bit_bus_del(struct i2c_bit_adapter* b) {
if (b->initialized) {
i2c_bit_del_bus(&b->adapter);
b->initialized = 0;
}
}
 
static inline void i2c_maven_done(struct matroxfb_dh_maven_info* minfo2) {
i2c_bit_bus_del(&minfo2->maven);
}
 
static inline void i2c_ddc1_done(struct matroxfb_dh_maven_info* minfo2) {
i2c_bit_bus_del(&minfo2->ddc1);
}
 
static inline void i2c_ddc2_done(struct matroxfb_dh_maven_info* minfo2) {
i2c_bit_bus_del(&minfo2->ddc2);
}
 
static void* i2c_matroxfb_probe(struct matrox_fb_info* minfo) {
int err;
unsigned long flags;
struct matroxfb_dh_maven_info* m2info;
 
m2info = (struct matroxfb_dh_maven_info*)kmalloc(sizeof(*m2info), GFP_KERNEL);
if (!m2info)
return NULL;
 
matroxfb_DAC_lock_irqsave(flags);
matroxfb_DAC_out(PMINFO DAC_XGENIODATA, 0xFF);
matroxfb_DAC_out(PMINFO DAC_XGENIOCTRL, 0x00);
matroxfb_DAC_unlock_irqrestore(flags);
 
memset(m2info, 0, sizeof(*m2info));
 
switch (ACCESS_FBINFO(chip)) {
case MGA_2064:
case MGA_2164:
err = i2c_bus_reg(&m2info->ddc1, minfo, DDC1B_DATA, DDC1B_CLK, "DDC:fb%u #0");
break;
default:
err = i2c_bus_reg(&m2info->ddc1, minfo, DDC1_DATA, DDC1_CLK, "DDC:fb%u #0");
break;
}
if (err)
goto fail_ddc1;
if (ACCESS_FBINFO(devflags.dualhead)) {
err = i2c_bus_reg(&m2info->ddc2, minfo, DDC2_DATA, DDC2_CLK, "DDC:fb%u #1");
if (err == -ENODEV) {
printk(KERN_INFO "i2c-matroxfb: VGA->TV plug detected, DDC unavailable.\n");
} else if (err)
printk(KERN_INFO "i2c-matroxfb: Could not register secondary output i2c bus. Continuing anyway.\n");
/* Register maven bus even on G450/G550 */
err = i2c_bus_reg(&m2info->maven, minfo, MAT_DATA, MAT_CLK, "MAVEN:fb%u");
if (err)
printk(KERN_INFO "i2c-matroxfb: Could not register Maven i2c bus. Continuing anyway.\n");
}
return m2info;
fail_ddc1:;
kfree(m2info);
printk(KERN_ERR "i2c-matroxfb: Could not register primary adapter DDC bus.\n");
return NULL;
}
 
static void i2c_matroxfb_remove(struct matrox_fb_info* minfo, void* data) {
struct matroxfb_dh_maven_info* m2info = data;
 
i2c_maven_done(m2info);
i2c_ddc2_done(m2info);
i2c_ddc1_done(m2info);
kfree(m2info);
}
 
static struct matroxfb_driver i2c_matroxfb = {
.node = LIST_HEAD_INIT(i2c_matroxfb.node),
.name = "i2c-matroxfb",
.probe = i2c_matroxfb_probe,
.remove = i2c_matroxfb_remove,
};
 
static int __init i2c_matroxfb_init(void) {
if (matroxfb_register_driver(&i2c_matroxfb)) {
printk(KERN_ERR "i2c-matroxfb: failed to register driver\n");
return -ENXIO;
}
return 0;
}
 
static void __exit i2c_matroxfb_exit(void) {
matroxfb_unregister_driver(&i2c_matroxfb);
}
 
MODULE_AUTHOR("(c) 1999-2002 Petr Vandrovec <vandrove@vc.cvut.cz>");
MODULE_DESCRIPTION("Support module providing I2C buses present on Matrox videocards");
 
module_init(i2c_matroxfb_init);
module_exit(i2c_matroxfb_exit);
/* no __setup required */
MODULE_LICENSE("GPL");
/shark/trunk/drivers/fb/matrox/matroxfb_misc.c
0,0 → 1,780
/*
*
* Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200 and G400
*
* (c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz>
*
* Portions Copyright (c) 2001 Matrox Graphics Inc.
*
* Version: 1.65 2002/08/14
*
* MTRR stuff: 1998 Tom Rini <trini@kernel.crashing.org>
*
* Contributors: "menion?" <menion@mindless.com>
* Betatesting, fixes, ideas
*
* "Kurt Garloff" <garloff@suse.de>
* Betatesting, fixes, ideas, videomodes, videomodes timmings
*
* "Tom Rini" <trini@kernel.crashing.org>
* MTRR stuff, PPC cleanups, betatesting, fixes, ideas
*
* "Bibek Sahu" <scorpio@dodds.net>
* Access device through readb|w|l and write b|w|l
* Extensive debugging stuff
*
* "Daniel Haun" <haund@usa.net>
* Testing, hardware cursor fixes
*
* "Scott Wood" <sawst46+@pitt.edu>
* Fixes
*
* "Gerd Knorr" <kraxel@goldbach.isdn.cs.tu-berlin.de>
* Betatesting
*
* "Kelly French" <targon@hazmat.com>
* "Fernando Herrera" <fherrera@eurielec.etsit.upm.es>
* Betatesting, bug reporting
*
* "Pablo Bianucci" <pbian@pccp.com.ar>
* Fixes, ideas, betatesting
*
* "Inaky Perez Gonzalez" <inaky@peloncho.fis.ucm.es>
* Fixes, enhandcements, ideas, betatesting
*
* "Ryuichi Oikawa" <roikawa@rr.iiij4u.or.jp>
* PPC betatesting, PPC support, backward compatibility
*
* "Paul Womar" <Paul@pwomar.demon.co.uk>
* "Owen Waller" <O.Waller@ee.qub.ac.uk>
* PPC betatesting
*
* "Thomas Pornin" <pornin@bolet.ens.fr>
* Alpha betatesting
*
* "Pieter van Leuven" <pvl@iae.nl>
* "Ulf Jaenicke-Roessler" <ujr@physik.phy.tu-dresden.de>
* G100 testing
*
* "H. Peter Arvin" <hpa@transmeta.com>
* Ideas
*
* "Cort Dougan" <cort@cs.nmt.edu>
* CHRP fixes and PReP cleanup
*
* "Mark Vojkovich" <mvojkovi@ucsd.edu>
* G400 support
*
* "David C. Hansen" <haveblue@us.ibm.com>
* Fixes
*
* (following author is not in any relation with this code, but his code
* is included in this driver)
*
* Based on framebuffer driver for VBE 2.0 compliant graphic boards
* (c) 1998 Gerd Knorr <kraxel@cs.tu-berlin.de>
*
* (following author is not in any relation with this code, but his ideas
* were used when writting this driver)
*
* FreeVBE/AF (Matrox), "Shawn Hargreaves" <shawn@talula.demon.co.uk>
*
*/
 
/* make checkconfig does not check includes for this... */
 
#include <linuxcomp.h>
 
#include <linux/config.h>
 
#include "matroxfb_misc.h"
#include <linux/interrupt.h>
#include <linux/matroxfb.h>
 
void matroxfb_DAC_out(CPMINFO int reg, int val) {
DBG_REG(__FUNCTION__)
mga_outb(M_RAMDAC_BASE+M_X_INDEX, reg);
mga_outb(M_RAMDAC_BASE+M_X_DATAREG, val);
}
 
int matroxfb_DAC_in(CPMINFO int reg) {
DBG_REG(__FUNCTION__)
mga_outb(M_RAMDAC_BASE+M_X_INDEX, reg);
return mga_inb(M_RAMDAC_BASE+M_X_DATAREG);
}
 
void matroxfb_var2my(struct fb_var_screeninfo* var, struct my_timming* mt) {
unsigned int pixclock = var->pixclock;
 
DBG(__FUNCTION__)
 
if (!pixclock) pixclock = 10000; /* 10ns = 100MHz */
mt->pixclock = 1000000000 / pixclock;
if (mt->pixclock < 1) mt->pixclock = 1;
mt->mnp = -1;
mt->dblscan = var->vmode & FB_VMODE_DOUBLE;
mt->interlaced = var->vmode & FB_VMODE_INTERLACED;
mt->HDisplay = var->xres;
mt->HSyncStart = mt->HDisplay + var->right_margin;
mt->HSyncEnd = mt->HSyncStart + var->hsync_len;
mt->HTotal = mt->HSyncEnd + var->left_margin;
mt->VDisplay = var->yres;
mt->VSyncStart = mt->VDisplay + var->lower_margin;
mt->VSyncEnd = mt->VSyncStart + var->vsync_len;
mt->VTotal = mt->VSyncEnd + var->upper_margin;
mt->sync = var->sync;
}
 
int matroxfb_PLL_calcclock(const struct matrox_pll_features* pll, unsigned int freq, unsigned int fmax,
unsigned int* in, unsigned int* feed, unsigned int* post) {
unsigned int bestdiff = ~0;
unsigned int bestvco = 0;
unsigned int fxtal = pll->ref_freq;
unsigned int fwant;
unsigned int p;
 
DBG(__FUNCTION__)
 
fwant = freq;
 
#ifdef DEBUG
printk(KERN_ERR "post_shift_max: %d\n", pll->post_shift_max);
printk(KERN_ERR "ref_freq: %d\n", pll->ref_freq);
printk(KERN_ERR "freq: %d\n", freq);
printk(KERN_ERR "vco_freq_min: %d\n", pll->vco_freq_min);
printk(KERN_ERR "in_div_min: %d\n", pll->in_div_min);
printk(KERN_ERR "in_div_max: %d\n", pll->in_div_max);
printk(KERN_ERR "feed_div_min: %d\n", pll->feed_div_min);
printk(KERN_ERR "feed_div_max: %d\n", pll->feed_div_max);
printk(KERN_ERR "fmax: %d\n", fmax);
#endif
for (p = 1; p <= pll->post_shift_max; p++) {
if (fwant * 2 > fmax)
break;
fwant *= 2;
}
if (fwant < pll->vco_freq_min) fwant = pll->vco_freq_min;
if (fwant > fmax) fwant = fmax;
for (; p-- > 0; fwant >>= 1, bestdiff >>= 1) {
unsigned int m;
 
if (fwant < pll->vco_freq_min) break;
for (m = pll->in_div_min; m <= pll->in_div_max; m++) {
unsigned int diff, fvco;
unsigned int n;
 
n = (fwant * (m + 1) + (fxtal >> 1)) / fxtal - 1;
if (n > pll->feed_div_max)
break;
if (n < pll->feed_div_min)
n = pll->feed_div_min;
fvco = (fxtal * (n + 1)) / (m + 1);
if (fvco < fwant)
diff = fwant - fvco;
else
diff = fvco - fwant;
if (diff < bestdiff) {
bestdiff = diff;
*post = p;
*in = m;
*feed = n;
bestvco = fvco;
}
}
}
dprintk(KERN_ERR "clk: %02X %02X %02X %d %d %d\n", *in, *feed, *post, fxtal, bestvco, fwant);
return bestvco;
}
 
int matroxfb_vgaHWinit(WPMINFO struct my_timming* m) {
unsigned int hd, hs, he, hbe, ht;
unsigned int vd, vs, ve, vt, lc;
unsigned int wd;
unsigned int divider;
int i;
int fwidth;
struct matrox_hw_state * const hw = &ACCESS_FBINFO(hw);
 
fwidth = 8;
 
DBG(__FUNCTION__)
 
hw->SEQ[0] = 0x00;
if (fwidth == 9)
hw->SEQ[1] = 0x00;
else
hw->SEQ[1] = 0x01; /* or 0x09 */
hw->SEQ[2] = 0x0F; /* bitplanes */
hw->SEQ[3] = 0x00;
hw->SEQ[4] = 0x0E;
/* CRTC 0..7, 9, 16..19, 21, 22 are reprogrammed by Matrox Millennium code... Hope that by MGA1064 too */
if (m->dblscan) {
m->VTotal <<= 1;
m->VDisplay <<= 1;
m->VSyncStart <<= 1;
m->VSyncEnd <<= 1;
}
if (m->interlaced) {
m->VTotal >>= 1;
m->VDisplay >>= 1;
m->VSyncStart >>= 1;
m->VSyncEnd >>= 1;
}
 
/* GCTL is ignored when not using 0xA0000 aperture */
hw->GCTL[0] = 0x00;
hw->GCTL[1] = 0x00;
hw->GCTL[2] = 0x00;
hw->GCTL[3] = 0x00;
hw->GCTL[4] = 0x00;
hw->GCTL[5] = 0x40;
hw->GCTL[6] = 0x05;
hw->GCTL[7] = 0x0F;
hw->GCTL[8] = 0xFF;
 
/* Whole ATTR is ignored in PowerGraphics mode */
for (i = 0; i < 16; i++)
hw->ATTR[i] = i;
hw->ATTR[16] = 0x41;
hw->ATTR[17] = 0xFF;
hw->ATTR[18] = 0x0F;
if (fwidth == 9)
hw->ATTR[19] = 0x08;
else
hw->ATTR[19] = 0x00;
hw->ATTR[20] = 0x00;
 
hd = m->HDisplay >> 3;
hs = m->HSyncStart >> 3;
he = m->HSyncEnd >> 3;
ht = m->HTotal >> 3;
/* standard timmings are in 8pixels, but for interleaved we cannot */
/* do it for 4bpp (because of (4bpp >> 1(interleaved))/4 == 0) */
/* using 16 or more pixels per unit can save us */
divider = ACCESS_FBINFO(curr.final_bppShift);
while (divider & 3) {
hd >>= 1;
hs >>= 1;
he >>= 1;
ht >>= 1;
divider <<= 1;
}
divider = divider / 4;
/* divider can be from 1 to 8 */
while (divider > 8) {
hd <<= 1;
hs <<= 1;
he <<= 1;
ht <<= 1;
divider >>= 1;
}
hd = hd - 1;
hs = hs - 1;
he = he - 1;
ht = ht - 1;
vd = m->VDisplay - 1;
vs = m->VSyncStart - 1;
ve = m->VSyncEnd - 1;
vt = m->VTotal - 2;
lc = vd;
/* G200 cannot work with (ht & 7) == 6 */
if (((ht & 0x07) == 0x06) || ((ht & 0x0F) == 0x04))
ht++;
hbe = ht;
wd = ACCESS_FBINFO(fbcon).var.xres_virtual * ACCESS_FBINFO(curr.final_bppShift) / 64;
 
hw->CRTCEXT[0] = 0;
hw->CRTCEXT[5] = 0;
if (m->interlaced) {
hw->CRTCEXT[0] = 0x80;
hw->CRTCEXT[5] = (hs + he - ht) >> 1;
if (!m->dblscan)
wd <<= 1;
vt &= ~1;
}
hw->CRTCEXT[0] |= (wd & 0x300) >> 4;
hw->CRTCEXT[1] = (((ht - 4) & 0x100) >> 8) |
((hd & 0x100) >> 7) | /* blanking */
((hs & 0x100) >> 6) | /* sync start */
(hbe & 0x040); /* end hor. blanking */
/* FIXME: Enable vidrst only on G400, and only if TV-out is used */
if (ACCESS_FBINFO(outputs[1]).src == MATROXFB_SRC_CRTC1)
hw->CRTCEXT[1] |= 0x88; /* enable horizontal and vertical vidrst */
hw->CRTCEXT[2] = ((vt & 0xC00) >> 10) |
((vd & 0x400) >> 8) | /* disp end */
((vd & 0xC00) >> 7) | /* vblanking start */
((vs & 0xC00) >> 5) |
((lc & 0x400) >> 3);
hw->CRTCEXT[3] = (divider - 1) | 0x80;
hw->CRTCEXT[4] = 0;
 
hw->CRTC[0] = ht-4;
hw->CRTC[1] = hd;
hw->CRTC[2] = hd;
hw->CRTC[3] = (hbe & 0x1F) | 0x80;
hw->CRTC[4] = hs;
hw->CRTC[5] = ((hbe & 0x20) << 2) | (he & 0x1F);
hw->CRTC[6] = vt & 0xFF;
hw->CRTC[7] = ((vt & 0x100) >> 8) |
((vd & 0x100) >> 7) |
((vs & 0x100) >> 6) |
((vd & 0x100) >> 5) |
((lc & 0x100) >> 4) |
((vt & 0x200) >> 4) |
((vd & 0x200) >> 3) |
((vs & 0x200) >> 2);
hw->CRTC[8] = 0x00;
hw->CRTC[9] = ((vd & 0x200) >> 4) |
((lc & 0x200) >> 3);
if (m->dblscan && !m->interlaced)
hw->CRTC[9] |= 0x80;
for (i = 10; i < 16; i++)
hw->CRTC[i] = 0x00;
hw->CRTC[16] = vs /* & 0xFF */;
hw->CRTC[17] = (ve & 0x0F) | 0x20;
hw->CRTC[18] = vd /* & 0xFF */;
hw->CRTC[19] = wd /* & 0xFF */;
hw->CRTC[20] = 0x00;
hw->CRTC[21] = vd /* & 0xFF */;
hw->CRTC[22] = (vt + 1) /* & 0xFF */;
hw->CRTC[23] = 0xC3;
hw->CRTC[24] = lc;
return 0;
};
 
void matroxfb_vgaHWrestore(WPMINFO2) {
int i;
struct matrox_hw_state * const hw = &ACCESS_FBINFO(hw);
CRITFLAGS
 
DBG(__FUNCTION__)
 
dprintk(KERN_INFO "MiscOutReg: %02X\n", hw->MiscOutReg);
dprintk(KERN_INFO "SEQ regs: ");
for (i = 0; i < 5; i++)
dprintk("%02X:", hw->SEQ[i]);
dprintk("\n");
dprintk(KERN_INFO "GDC regs: ");
for (i = 0; i < 9; i++)
dprintk("%02X:", hw->GCTL[i]);
dprintk("\n");
dprintk(KERN_INFO "CRTC regs: ");
for (i = 0; i < 25; i++)
dprintk("%02X:", hw->CRTC[i]);
dprintk("\n");
dprintk(KERN_INFO "ATTR regs: ");
for (i = 0; i < 21; i++)
dprintk("%02X:", hw->ATTR[i]);
dprintk("\n");
 
CRITBEGIN
 
mga_inb(M_ATTR_RESET);
mga_outb(M_ATTR_INDEX, 0);
mga_outb(M_MISC_REG, hw->MiscOutReg);
for (i = 1; i < 5; i++)
mga_setr(M_SEQ_INDEX, i, hw->SEQ[i]);
mga_setr(M_CRTC_INDEX, 17, hw->CRTC[17] & 0x7F);
for (i = 0; i < 25; i++)
mga_setr(M_CRTC_INDEX, i, hw->CRTC[i]);
for (i = 0; i < 9; i++)
mga_setr(M_GRAPHICS_INDEX, i, hw->GCTL[i]);
for (i = 0; i < 21; i++) {
mga_inb(M_ATTR_RESET);
mga_outb(M_ATTR_INDEX, i);
mga_outb(M_ATTR_INDEX, hw->ATTR[i]);
}
mga_outb(M_PALETTE_MASK, 0xFF);
mga_outb(M_DAC_REG, 0x00);
for (i = 0; i < 768; i++)
mga_outb(M_DAC_VAL, hw->DACpal[i]);
mga_inb(M_ATTR_RESET);
mga_outb(M_ATTR_INDEX, 0x20);
 
CRITEND
}
 
static void get_pins(unsigned char* pins, struct matrox_bios* bd) {
unsigned int b0 = readb(pins);
if (b0 == 0x2E && readb(pins+1) == 0x41) {
unsigned int pins_len = readb(pins+2);
unsigned int i;
unsigned char cksum;
unsigned char* dst = bd->pins;
 
if (pins_len < 3 || pins_len > 128) {
return;
}
*dst++ = 0x2E;
*dst++ = 0x41;
*dst++ = pins_len;
cksum = 0x2E + 0x41 + pins_len;
for (i = 3; i < pins_len; i++) {
cksum += *dst++ = readb(pins+i);
}
if (cksum) {
return;
}
bd->pins_len = pins_len;
} else if (b0 == 0x40 && readb(pins+1) == 0x00) {
unsigned int i;
unsigned char* dst = bd->pins;
 
*dst++ = 0x40;
*dst++ = 0;
for (i = 2; i < 0x40; i++) {
*dst++ = readb(pins+i);
}
bd->pins_len = 0x40;
}
}
 
static void get_bios_version(unsigned char* vbios, struct matrox_bios* bd) {
unsigned int pcir_offset;
pcir_offset = readb(vbios + 24) | (readb(vbios + 25) << 8);
if (pcir_offset >= 26 && pcir_offset < 0xFFE0 &&
readb(vbios + pcir_offset ) == 'P' &&
readb(vbios + pcir_offset + 1) == 'C' &&
readb(vbios + pcir_offset + 2) == 'I' &&
readb(vbios + pcir_offset + 3) == 'R') {
unsigned char h;
 
h = readb(vbios + pcir_offset + 0x12);
bd->version.vMaj = (h >> 4) & 0xF;
bd->version.vMin = h & 0xF;
bd->version.vRev = readb(vbios + pcir_offset + 0x13);
} else {
unsigned char h;
 
h = readb(vbios + 5);
bd->version.vMaj = (h >> 4) & 0xF;
bd->version.vMin = h & 0xF;
bd->version.vRev = 0;
}
}
 
static void get_bios_output(unsigned char* vbios, struct matrox_bios* bd) {
unsigned char b;
b = readb(vbios + 0x7FF1);
if (b == 0xFF) {
b = 0;
}
bd->output.state = b;
}
 
static void get_bios_tvout(unsigned char* vbios, struct matrox_bios* bd) {
unsigned int i;
/* Check for 'IBM .*(V....TVO' string - it means TVO BIOS */
bd->output.tvout = 0;
if (readb(vbios + 0x1D) != 'I' ||
readb(vbios + 0x1E) != 'B' ||
readb(vbios + 0x1F) != 'M' ||
readb(vbios + 0x20) != ' ') {
return;
}
for (i = 0x2D; i < 0x2D + 128; i++) {
unsigned char b = readb(vbios + i);
if (b == '(' && readb(vbios + i + 1) == 'V') {
if (readb(vbios + i + 6) == 'T' &&
readb(vbios + i + 7) == 'V' &&
readb(vbios + i + 8) == 'O') {
bd->output.tvout = 1;
}
return;
}
if (b == 0)
break;
}
}
 
static void parse_bios(unsigned char* vbios, struct matrox_bios* bd) {
unsigned int pins_offset;
if (readb(vbios) != 0x55 || readb(vbios + 1) != 0xAA) {
return;
}
bd->bios_valid = 1;
get_bios_version(vbios, bd);
get_bios_output(vbios, bd);
get_bios_tvout(vbios, bd);
pins_offset = readb(vbios + 0x7FFC) | (readb(vbios + 0x7FFD) << 8);
if (pins_offset <= 0xFF80) {
get_pins(vbios + pins_offset, bd);
}
}
 
#define get_u16(x) (le16_to_cpu(get_unaligned((__u16*)(x))))
#define get_u32(x) (le32_to_cpu(get_unaligned((__u32*)(x))))
static int parse_pins1(WPMINFO const struct matrox_bios* bd) {
unsigned int maxdac;
 
switch (bd->pins[22]) {
case 0: maxdac = 175000; break;
case 1: maxdac = 220000; break;
default: maxdac = 240000; break;
}
if (get_u16(bd->pins + 24)) {
maxdac = get_u16(bd->pins + 24) * 10;
}
MINFO->limits.pixel.vcomax = maxdac;
MINFO->values.pll.system = get_u16(bd->pins + 28) ? get_u16(bd->pins + 28) * 10 : 50000;
/* ignore 4MB, 8MB, module clocks */
MINFO->features.pll.ref_freq = 14318;
MINFO->values.reg.mctlwtst = 0x00030101;
return 0;
}
 
static void default_pins1(WPMINFO2) {
/* Millennium */
MINFO->limits.pixel.vcomax = 220000;
MINFO->values.pll.system = 50000;
MINFO->features.pll.ref_freq = 14318;
MINFO->values.reg.mctlwtst = 0x00030101;
}
 
static int parse_pins2(WPMINFO const struct matrox_bios* bd) {
MINFO->limits.pixel.vcomax =
MINFO->limits.system.vcomax = (bd->pins[41] == 0xFF) ? 230000 : ((bd->pins[41] + 100) * 1000);
MINFO->values.reg.mctlwtst = ((bd->pins[51] & 0x01) ? 0x00000001 : 0) |
((bd->pins[51] & 0x02) ? 0x00000100 : 0) |
((bd->pins[51] & 0x04) ? 0x00010000 : 0) |
((bd->pins[51] & 0x08) ? 0x00020000 : 0);
MINFO->values.pll.system = (bd->pins[43] == 0xFF) ? 50000 : ((bd->pins[43] + 100) * 1000);
MINFO->features.pll.ref_freq = 14318;
return 0;
}
 
static void default_pins2(WPMINFO2) {
/* Millennium II, Mystique */
MINFO->limits.pixel.vcomax =
MINFO->limits.system.vcomax = 230000;
MINFO->values.reg.mctlwtst = 0x00030101;
MINFO->values.pll.system = 50000;
MINFO->features.pll.ref_freq = 14318;
}
 
static int parse_pins3(WPMINFO const struct matrox_bios* bd) {
MINFO->limits.pixel.vcomax =
MINFO->limits.system.vcomax = (bd->pins[36] == 0xFF) ? 230000 : ((bd->pins[36] + 100) * 1000);
MINFO->values.reg.mctlwtst = get_u32(bd->pins + 48) == 0xFFFFFFFF ? 0x01250A21 : get_u32(bd->pins + 48);
/* memory config */
MINFO->values.reg.memrdbk = ((bd->pins[57] << 21) & 0x1E000000) |
((bd->pins[57] << 22) & 0x00C00000) |
((bd->pins[56] << 1) & 0x000001E0) |
( bd->pins[56] & 0x0000000F);
MINFO->values.reg.opt = (bd->pins[54] & 7) << 10;
MINFO->values.reg.opt2 = bd->pins[58] << 12;
MINFO->features.pll.ref_freq = (bd->pins[52] & 0x20) ? 14318 : 27000;
return 0;
}
 
static void default_pins3(WPMINFO2) {
/* G100, G200 */
MINFO->limits.pixel.vcomax =
MINFO->limits.system.vcomax = 230000;
MINFO->values.reg.mctlwtst = 0x01250A21;
MINFO->values.reg.memrdbk = 0x00000000;
MINFO->values.reg.opt = 0x00000C00;
MINFO->values.reg.opt2 = 0x00000000;
MINFO->features.pll.ref_freq = 27000;
}
 
static int parse_pins4(WPMINFO const struct matrox_bios* bd) {
MINFO->limits.pixel.vcomax = (bd->pins[ 39] == 0xFF) ? 230000 : bd->pins[ 39] * 4000;
MINFO->limits.system.vcomax = (bd->pins[ 38] == 0xFF) ? MINFO->limits.pixel.vcomax : bd->pins[ 38] * 4000;
MINFO->values.reg.mctlwtst = get_u32(bd->pins + 71);
MINFO->values.reg.memrdbk = ((bd->pins[87] << 21) & 0x1E000000) |
((bd->pins[87] << 22) & 0x00C00000) |
((bd->pins[86] << 1) & 0x000001E0) |
( bd->pins[86] & 0x0000000F);
MINFO->values.reg.opt = ((bd->pins[53] << 15) & 0x00400000) |
((bd->pins[53] << 22) & 0x10000000) |
((bd->pins[53] << 7) & 0x00001C00);
MINFO->values.reg.opt3 = get_u32(bd->pins + 67);
MINFO->values.pll.system = (bd->pins[ 65] == 0xFF) ? 200000 : bd->pins[ 65] * 4000;
MINFO->features.pll.ref_freq = (bd->pins[ 92] & 0x01) ? 14318 : 27000;
return 0;
}
 
static void default_pins4(WPMINFO2) {
/* G400 */
MINFO->limits.pixel.vcomax =
MINFO->limits.system.vcomax = 252000;
MINFO->values.reg.mctlwtst = 0x04A450A1;
MINFO->values.reg.memrdbk = 0x000000E7;
MINFO->values.reg.opt = 0x10000400;
MINFO->values.reg.opt3 = 0x0190A419;
MINFO->values.pll.system = 200000;
MINFO->features.pll.ref_freq = 27000;
}
 
static int parse_pins5(WPMINFO const struct matrox_bios* bd) {
unsigned int mult;
mult = bd->pins[4]?8000:6000;
MINFO->limits.pixel.vcomax = (bd->pins[ 38] == 0xFF) ? 600000 : bd->pins[ 38] * mult;
MINFO->limits.system.vcomax = (bd->pins[ 36] == 0xFF) ? MINFO->limits.pixel.vcomax : bd->pins[ 36] * mult;
MINFO->limits.video.vcomax = (bd->pins[ 37] == 0xFF) ? MINFO->limits.system.vcomax : bd->pins[ 37] * mult;
MINFO->limits.pixel.vcomin = (bd->pins[123] == 0xFF) ? 256000 : bd->pins[123] * mult;
MINFO->limits.system.vcomin = (bd->pins[121] == 0xFF) ? MINFO->limits.pixel.vcomin : bd->pins[121] * mult;
MINFO->limits.video.vcomin = (bd->pins[122] == 0xFF) ? MINFO->limits.system.vcomin : bd->pins[122] * mult;
MINFO->values.pll.system =
MINFO->values.pll.video = (bd->pins[ 92] == 0xFF) ? 284000 : bd->pins[ 92] * 4000;
MINFO->values.reg.opt = get_u32(bd->pins+ 48);
MINFO->values.reg.opt2 = get_u32(bd->pins+ 52);
MINFO->values.reg.opt3 = get_u32(bd->pins+ 94);
MINFO->values.reg.mctlwtst = get_u32(bd->pins+ 98);
MINFO->values.reg.memmisc = get_u32(bd->pins+102);
MINFO->values.reg.memrdbk = get_u32(bd->pins+106);
MINFO->features.pll.ref_freq = (bd->pins[110] & 0x01) ? 14318 : 27000;
MINFO->values.memory.ddr = (bd->pins[114] & 0x60) == 0x20;
MINFO->values.memory.dll = (bd->pins[115] & 0x02) != 0;
MINFO->values.memory.emrswen = (bd->pins[115] & 0x01) != 0;
MINFO->values.reg.maccess = MINFO->values.memory.emrswen ? 0x00004000 : 0x00000000;
if (bd->pins[115] & 4) {
MINFO->values.reg.mctlwtst_core = MINFO->values.reg.mctlwtst;
} else {
u_int32_t wtst_xlat[] = { 0, 1, 5, 6, 7, 5, 2, 3 };
MINFO->values.reg.mctlwtst_core = (MINFO->values.reg.mctlwtst & ~7) |
wtst_xlat[MINFO->values.reg.mctlwtst & 7];
}
return 0;
}
 
static void default_pins5(WPMINFO2) {
/* Mine 16MB G450 with SDRAM DDR */
MINFO->limits.pixel.vcomax =
MINFO->limits.system.vcomax =
MINFO->limits.video.vcomax = 600000;
MINFO->limits.pixel.vcomin =
MINFO->limits.system.vcomin =
MINFO->limits.video.vcomin = 256000;
MINFO->values.pll.system =
MINFO->values.pll.video = 284000;
MINFO->values.reg.opt = 0x404A1160;
MINFO->values.reg.opt2 = 0x0000AC00;
MINFO->values.reg.opt3 = 0x0090A409;
MINFO->values.reg.mctlwtst_core =
MINFO->values.reg.mctlwtst = 0x0C81462B;
MINFO->values.reg.memmisc = 0x80000004;
MINFO->values.reg.memrdbk = 0x01001103;
MINFO->features.pll.ref_freq = 27000;
MINFO->values.memory.ddr = 1;
MINFO->values.memory.dll = 1;
MINFO->values.memory.emrswen = 1;
MINFO->values.reg.maccess = 0x00004000;
}
 
static int matroxfb_set_limits(WPMINFO const struct matrox_bios* bd) {
unsigned int pins_version;
static const unsigned int pinslen[] = { 64, 64, 64, 128, 128 };
 
switch (ACCESS_FBINFO(chip)) {
case MGA_2064: default_pins1(PMINFO2); break;
case MGA_2164:
case MGA_1064:
case MGA_1164: default_pins2(PMINFO2); break;
case MGA_G100:
case MGA_G200: default_pins3(PMINFO2); break;
case MGA_G400: default_pins4(PMINFO2); break;
case MGA_G450:
case MGA_G550: default_pins5(PMINFO2); break;
}
if (!bd->bios_valid) {
printk(KERN_INFO "matroxfb: Your Matrox device does not have BIOS\n");
return -1;
}
if (bd->pins_len < 64) {
printk(KERN_INFO "matroxfb: BIOS on your Matrox device does not contain powerup info\n");
return -1;
}
if (bd->pins[0] == 0x2E && bd->pins[1] == 0x41) {
pins_version = bd->pins[5];
if (pins_version < 2 || pins_version > 5) {
printk(KERN_INFO "matroxfb: Unknown version (%u) of powerup info\n", pins_version);
return -1;
}
} else {
pins_version = 1;
}
if (bd->pins_len != pinslen[pins_version - 1]) {
printk(KERN_INFO "matroxfb: Invalid powerup info\n");
return -1;
}
switch (pins_version) {
case 1:
return parse_pins1(PMINFO bd);
case 2:
return parse_pins2(PMINFO bd);
case 3:
return parse_pins3(PMINFO bd);
case 4:
return parse_pins4(PMINFO bd);
case 5:
return parse_pins5(PMINFO bd);
default:
printk(KERN_DEBUG "matroxfb: Powerup info version %u is not yet supported\n", pins_version);
return -1;
}
}
 
void matroxfb_read_pins(WPMINFO2) {
u32 opt;
u32 biosbase;
u32 fbbase;
struct pci_dev* pdev = ACCESS_FBINFO(pcidev);
memset(&ACCESS_FBINFO(bios), 0, sizeof(ACCESS_FBINFO(bios)));
pci_read_config_dword(pdev, PCI_OPTION_REG, &opt);
pci_write_config_dword(pdev, PCI_OPTION_REG, opt | PCI_OPTION_ENABLE_ROM);
pci_read_config_dword(pdev, PCI_ROM_ADDRESS, &biosbase);
pci_read_config_dword(pdev, ACCESS_FBINFO(devflags.fbResource), &fbbase);
pci_write_config_dword(pdev, PCI_ROM_ADDRESS, (fbbase & PCI_ROM_ADDRESS_MASK) | PCI_ROM_ADDRESS_ENABLE);
parse_bios(vaddr_va(ACCESS_FBINFO(video).vbase), &ACCESS_FBINFO(bios));
pci_write_config_dword(pdev, PCI_ROM_ADDRESS, biosbase);
pci_write_config_dword(pdev, PCI_OPTION_REG, opt);
#ifdef CONFIG_X86
if (!ACCESS_FBINFO(bios).bios_valid) {
unsigned char* b;
 
b = ioremap(0x000C0000, 65536);
if (!b) {
printk(KERN_INFO "matroxfb: Unable to map legacy BIOS\n");
} else {
unsigned int ven = readb(b+0x64+0) | (readb(b+0x64+1) << 8);
unsigned int dev = readb(b+0x64+2) | (readb(b+0x64+3) << 8);
if (ven != pdev->vendor || dev != pdev->device) {
printk(KERN_INFO "matroxfb: Legacy BIOS is for %04X:%04X, while this device is %04X:%04X\n",
ven, dev, pdev->vendor, pdev->device);
} else {
parse_bios(b, &ACCESS_FBINFO(bios));
}
iounmap(b);
}
}
#endif
matroxfb_set_limits(PMINFO &ACCESS_FBINFO(bios));
}
 
EXPORT_SYMBOL(matroxfb_DAC_in);
EXPORT_SYMBOL(matroxfb_DAC_out);
EXPORT_SYMBOL(matroxfb_var2my);
EXPORT_SYMBOL(matroxfb_PLL_calcclock);
#ifndef CONFIG_FB_MATROX_MULTIHEAD
struct matrox_fb_info matroxfb_global_mxinfo;
EXPORT_SYMBOL(matroxfb_global_mxinfo);
#endif
EXPORT_SYMBOL(matroxfb_vgaHWinit); /* DAC1064, Ti3026 */
EXPORT_SYMBOL(matroxfb_vgaHWrestore); /* DAC1064, Ti3026 */
EXPORT_SYMBOL(matroxfb_read_pins);
 
MODULE_AUTHOR("(c) 1999-2002 Petr Vandrovec <vandrove@vc.cvut.cz>");
MODULE_DESCRIPTION("Miscellaneous support for Matrox video cards");
MODULE_LICENSE("GPL");
/shark/trunk/drivers/fb/makefile
12,7 → 12,10
 
OBJS = fbmem.o fbcmap.o cfbfillrect.o softcursor.o cfbcopyarea.o cfbimgblt.o\
modedb.o vga16fb.o vgastate.o vesafb.o radeonfb.o shark_fb26.o gdvesa.o\
logo.o logo_bmp.o ./riva/fbdev.o ./riva/nv_driver.o ./riva/riva_hw.o fun16.o
logo.o logo_bmp.o ./riva/fbdev.o ./riva/nv_driver.o ./riva/riva_hw.o fun16.o\
./matrox/g450_pll.o ./matrox/matroxfb_accel.o ./matrox/matroxfb_base.o\
./matrox/matroxfb_crtc2.o ./matrox/matroxfb_DAC1064.o ./matrox/matroxfb_g450.o\
./matrox/matroxfb_maven.o ./matrox/matroxfb_misc.o ./matrox/matroxfb_Ti3026.o
 
OTHERINCL += -I$(BASE)/drivers/linuxc26/include -I./include -I.