Commit b0f3e5d4 authored by Petr Vandrovec's avatar Petr Vandrovec

Add TV-Out support for Matrox G450/G550. Due to the hardware only secondary

CRTC can be used as a source for TV output.
parent 705e41f8
...@@ -425,12 +425,21 @@ CONFIG_FB_MATROX_G450 ...@@ -425,12 +425,21 @@ CONFIG_FB_MATROX_G450
support" here in the framebuffer section. G450/G550 secondary head support" here in the framebuffer section. G450/G550 secondary head
and digital output are supported without additional modules. and digital output are supported without additional modules.
The driver starts in monitor mode. You must use the matroxset tool
(available at <ftp://platan.vc.cvut.cz/pub/linux/matrox-latest/>) to
swap primary and secondary head outputs, or to change output mode.
Secondary head driver always start in 640x480 resolution and you
must use fbset to change it.
Do not forget that second head supports only 16 and 32 bpp Do not forget that second head supports only 16 and 32 bpp
packed pixels, so it is a good idea to compile them into the kernel packed pixels, so it is a good idea to compile them into the kernel
too. You can use only some font widths, as the driver uses generic too. You can use only some font widths, as the driver uses generic
painting procedures (the secondary head does not use acceleration painting procedures (the secondary head does not use acceleration
engine). engine).
G450/G550 hardware can display TV picture only from secondary CRTC,
and it performs no scaling, so picture must have 525 or 625 lines.
CONFIG_FB_MATROX_G100A CONFIG_FB_MATROX_G100A
Say Y here if you have a Matrox G100, G200 or G400 based Say Y here if you have a Matrox G100, G200 or G400 based
video card. If you select "Advanced lowlevel driver options", you video card. If you select "Advanced lowlevel driver options", you
......
...@@ -2,11 +2,11 @@ ...@@ -2,11 +2,11 @@
* *
* Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200, G400 and G450. * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200, G400 and G450.
* *
* (c) 1998-2001 Petr Vandrovec <vandrove@vc.cvut.cz> * (c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz>
* *
* Portions Copyright (c) 2001 Matrox Graphics Inc. * Portions Copyright (c) 2001 Matrox Graphics Inc.
* *
* Version: 1.62 2001/11/29 * Version: 1.64 2002/06/10
* *
*/ */
...@@ -103,6 +103,7 @@ static void matroxfb_dh_restore(struct matroxfb_dh_fb_info* m2info, ...@@ -103,6 +103,7 @@ static void matroxfb_dh_restore(struct matroxfb_dh_fb_info* m2info,
int mode, int mode,
unsigned int pos) { unsigned int pos) {
u_int32_t tmp; u_int32_t tmp;
u_int32_t datactl;
MINFO_FROM(m2info->primary_dev); MINFO_FROM(m2info->primary_dev);
switch (mode) { switch (mode) {
...@@ -117,12 +118,15 @@ static void matroxfb_dh_restore(struct matroxfb_dh_fb_info* m2info, ...@@ -117,12 +118,15 @@ static void matroxfb_dh_restore(struct matroxfb_dh_fb_info* m2info,
tmp = 0x00800000; tmp = 0x00800000;
break; break;
} }
tmp |= 0x00000001; /* enable CRTC2 */ tmp |= 0x00000001; /* enable CRTC2 */
datactl = 0;
if (ACCESS_FBINFO(outputs[1]).src == MATROXFB_SRC_CRTC2) { if (ACCESS_FBINFO(outputs[1]).src == MATROXFB_SRC_CRTC2) {
if (ACCESS_FBINFO(devflags.g450dac)) { if (ACCESS_FBINFO(devflags.g450dac)) {
tmp |= 0x00000006; /* source from secondary pixel PLL */ tmp |= 0x00000006; /* source from secondary pixel PLL */
/* no vidrst */ /* no vidrst when in monitor mode */
if (ACCESS_FBINFO(outputs[1]).mode != MATROXFB_OUTPUT_MODE_MONITOR) {
tmp |= 0xC0001000; /* Enable H/V vidrst */
}
} else { } else {
tmp |= 0x00000002; /* source from VDOCLK */ tmp |= 0x00000002; /* source from VDOCLK */
tmp |= 0xC0000000; /* enable vvidrst & hvidrst */ tmp |= 0xC0000000; /* enable vvidrst & hvidrst */
...@@ -142,6 +146,10 @@ static void matroxfb_dh_restore(struct matroxfb_dh_fb_info* m2info, ...@@ -142,6 +146,10 @@ static void matroxfb_dh_restore(struct matroxfb_dh_fb_info* m2info,
mt->VSyncEnd >>= 1; mt->VSyncEnd >>= 1;
mt->VTotal >>= 1; mt->VTotal >>= 1;
} }
if ((mt->HTotal & 7) == 2) {
datactl |= 0x00000010;
mt->HTotal &= ~7;
}
tmp |= 0x10000000; /* 0x10000000 is VIDRST polarity */ tmp |= 0x10000000; /* 0x10000000 is VIDRST polarity */
mga_outl(0x3C14, ((mt->HDisplay - 8) << 16) | (mt->HTotal - 8)); mga_outl(0x3C14, ((mt->HDisplay - 8) << 16) | (mt->HTotal - 8));
mga_outl(0x3C18, ((mt->HSyncEnd - 8) << 16) | (mt->HSyncStart - 8)); mga_outl(0x3C18, ((mt->HSyncEnd - 8) << 16) | (mt->HSyncStart - 8));
...@@ -155,12 +163,14 @@ static void matroxfb_dh_restore(struct matroxfb_dh_fb_info* m2info, ...@@ -155,12 +163,14 @@ static void matroxfb_dh_restore(struct matroxfb_dh_fb_info* m2info,
mga_outl(0x3C2C, pos); /* field #1 vmemory start */ mga_outl(0x3C2C, pos); /* field #1 vmemory start */
mga_outl(0x3C28, pos + linelen); /* field #0 vmemory start */ mga_outl(0x3C28, pos + linelen); /* field #0 vmemory start */
linelen <<= 1; linelen <<= 1;
m2info->interlaced = 1;
} else { } else {
mga_outl(0x3C28, pos); /* vmemory start */ mga_outl(0x3C28, pos); /* vmemory start */
m2info->interlaced = 0;
} }
mga_outl(0x3C40, linelen); mga_outl(0x3C40, linelen);
} }
mga_outl(0x3C4C, 0); /* data control */ mga_outl(0x3C4C, datactl); /* data control */
if (tmp & 0x02000000) { if (tmp & 0x02000000) {
int i; int i;
...@@ -208,7 +218,7 @@ static void matroxfb_dh_pan_var(struct matroxfb_dh_fb_info* m2info, ...@@ -208,7 +218,7 @@ static void matroxfb_dh_pan_var(struct matroxfb_dh_fb_info* m2info,
linelen = var->xres_virtual * pixelsize; linelen = var->xres_virtual * pixelsize;
pos = var->yoffset * linelen + var->xoffset * pixelsize; pos = var->yoffset * linelen + var->xoffset * pixelsize;
pos += m2info->video.offbase; pos += m2info->video.offbase;
if (var->vmode & FB_VMODE_INTERLACED) { if (m2info->interlaced) {
mga_outl(0x3C2C, pos); mga_outl(0x3C2C, pos);
mga_outl(0x3C28, pos + linelen); mga_outl(0x3C28, pos + linelen);
} else { } else {
......
...@@ -30,6 +30,8 @@ struct matroxfb_dh_fb_info { ...@@ -30,6 +30,8 @@ struct matroxfb_dh_fb_info {
int currcon; int currcon;
struct display* currcon_display; struct display* currcon_display;
int interlaced:1;
union { union {
#ifdef FBCON_HAS_CFB16 #ifdef FBCON_HAS_CFB16
u_int16_t cfb16[16]; u_int16_t cfb16[16];
......
...@@ -2,39 +2,363 @@ ...@@ -2,39 +2,363 @@
* *
* Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200, G400 and G450. * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200, G400 and G450.
* *
* (c) 1998-2001 Petr Vandrovec <vandrove@vc.cvut.cz> * (c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz>
* *
* Portions Copyright (c) 2001 Matrox Graphics Inc. * Portions Copyright (c) 2001 Matrox Graphics Inc.
* *
* Version: 1.62 2001/11/29 * Version: 1.64 2002/06/02
* *
* See matroxfb_base.c for contributors. * See matroxfb_base.c for contributors.
* *
*/ */
#include "matroxfb_g450.h" #include "matroxfb_base.h"
#include "matroxfb_misc.h" #include "matroxfb_misc.h"
#include "matroxfb_DAC1064.h" #include "matroxfb_DAC1064.h"
#include "g450_pll.h" #include "g450_pll.h"
#include <linux/matroxfb.h> #include <linux/matroxfb.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/div64.h>
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);
}
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) { static int matroxfb_g450_compute(void* md, struct my_timming* mt) {
MINFO_FROM(md); MINFO_FROM(md);
if (mt->mnp < 0) { 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);
computeRegs(PMINFO &ACCESS_FBINFO(hw).maven, mt, outd);
} else if (mt->mnp < 0) {
/* We must program clocks before CRTC2, otherwise interlaced mode /* We must program clocks before CRTC2, otherwise interlaced mode
startup may fail */ startup may fail */
mt->mnp = matroxfb_g450_setclk(PMINFO mt->pixclock, (mt->crtc == MATROXFB_SRC_CRTC1) ? M_PIXEL_PLL_C : M_VIDEO_PLL); 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); mt->pixclock = g450_mnp2f(PMINFO mt->mnp);
} }
dprintk(KERN_DEBUG "Pixclock = %u\n", mt->pixclock);
return 0; return 0;
} }
static int matroxfb_g450_program(void* md) { 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; 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) { static int g450_dvi_compute(void* md, struct my_timming* mt) {
MINFO_FROM(md); MINFO_FROM(md);
...@@ -49,6 +373,7 @@ static struct matrox_altout matroxfb_g450_altout = { ...@@ -49,6 +373,7 @@ static struct matrox_altout matroxfb_g450_altout = {
.name = "Secondary output", .name = "Secondary output",
.compute = matroxfb_g450_compute, .compute = matroxfb_g450_compute,
.program = matroxfb_g450_program, .program = matroxfb_g450_program,
.verifymode = matroxfb_g450_verify_mode,
}; };
static struct matrox_altout matroxfb_g450_dvi = { static struct matrox_altout matroxfb_g450_dvi = {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment