diff options
Diffstat (limited to 'drivers/video/matrox/matroxfb_crtc2.c')
-rw-r--r-- | drivers/video/matrox/matroxfb_crtc2.c | 788 |
1 files changed, 788 insertions, 0 deletions
diff --git a/drivers/video/matrox/matroxfb_crtc2.c b/drivers/video/matrox/matroxfb_crtc2.c new file mode 100644 index 000000000..46fdb5fad --- /dev/null +++ b/drivers/video/matrox/matroxfb_crtc2.c @@ -0,0 +1,788 @@ +#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_getcolreg(unsigned regno, unsigned *red, unsigned *green, + unsigned *blue, unsigned *transp, struct fb_info* info) { +#define m2info ((struct matroxfb_dh_fb_info*)info) + if (regno > 16) + return 1; + *red = m2info->palette[regno].red; + *blue = m2info->palette[regno].blue; + *green = m2info->palette[regno].green; + *transp = m2info->palette[regno].transp; + return 0; +#undef m2info +} + +static int matroxfb_dh_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, struct fb_info* info) { +#define m2info ((struct matroxfb_dh_fb_info*)info) + struct display* p; + + if (regno > 16) + return 1; + m2info->palette[regno].red = red; + m2info->palette[regno].blue = blue; + m2info->palette[regno].green = green; + m2info->palette[regno].transp = transp; + p = m2info->currcon_display; + if (p->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, p->var.red.length); + green = CNVT_TOHW(green, p->var.green.length); + blue = CNVT_TOHW(blue, p->var.blue.length); + transp = CNVT_TOHW(transp, p->var.transp.length); + + switch (p->var.bits_per_pixel) { +#ifdef FBCON_HAS_CFB16 + case 16: + m2info->cmap.cfb16[regno] = + (red << p->var.red.offset) | + (green << p->var.green.offset) | + (blue << p->var.blue.offset) | + (transp << p->var.transp.offset); + break; +#endif +#ifdef FBCON_HAS_CFB32 + case 32: + m2info->cmap.cfb32[regno] = + (red << p->var.red.offset) | + (green << p->var.green.offset) | + (blue << p->var.blue.offset) | + (transp << p->var.transp.offset); + break; +#endif + } + return 0; +#undef m2info +} + +static void do_install_cmap(struct matroxfb_dh_fb_info* m2info, struct display* p) { + if (p->cmap.len) + fb_set_cmap(&p->cmap, 1, matroxfb_dh_setcolreg, &m2info->fbcon); + else + fb_set_cmap(fb_default_cmap(16), 1, matroxfb_dh_setcolreg, &m2info->fbcon); +} + +static void matroxfb_dh_restore(struct matroxfb_dh_fb_info* m2info, + struct my_timming* mt, + struct display* p, + int mode, + unsigned int pos) { + u_int32_t tmp; + struct matrox_fb_info* minfo = m2info->primary_dev; + + switch (mode) { + case 15: + tmp = 0x00200000; + break; + case 16: + tmp = 0x00400000; + break; +/* case 32: */ + default: + tmp = 0x00800000; + break; + } + + if (ACCESS_FBINFO(output.sh)) { + tmp |= 0x00000001; /* enable CRTC2 */ + + if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_SECONDARY) { + tmp |= 0x00000002; /* source from VDOCLK */ + tmp |= 0xC0000000; /* enable vvidrst & hvidrst */ + /* MGA TVO is our clock source */ + } else if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_PRIMARY) { + tmp |= 0x00000004; /* source from pixclock */ + /* PIXPLL is our clock source */ + } + + if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_PRIMARY) + tmp |= 0x00100000; /* connect CRTC2 to DAC */ + } + mga_outl(0x3C10, tmp | 0x10000000); /* depth and so on... 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 */ + mga_outl(0x3C28, pos); /* vmemory start */ + mga_outl(0x3C40, p->var.xres_virtual * (p->var.bits_per_pixel >> 3)); + tmp = 0x0FFF0000; /* 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); + mga_outl(0x3C4C, 0); /* data control */ +} + +static void matroxfb_dh_cfbX_init(struct matroxfb_dh_fb_info* m2info, + struct display* p) { + /* no acceleration for secondary head... */ +} + +static void matroxfb_dh_pan_var(struct matroxfb_dh_fb_info* m2info, + struct fb_var_screeninfo* var) { + unsigned int pos; + +#define minfo (m2info->primary_dev) + pos = (var->yoffset * var->xres_virtual + var->xoffset) * var->bits_per_pixel >> 3; + pos += m2info->video.offbase; + mga_outl(0x3C28, pos); +#undef minfo +} + +static int matroxfb_dh_decode_var(struct matroxfb_dh_fb_info* m2info, + struct display* p, + 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) { +#ifdef FBCON_HAS_CFB16 + case 16: mask = 0x1F; + break; +#endif +#ifdef FBCON_HAS_CFB32 + case 32: mask = 0x0F; + break; +#endif + 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 void initMatroxDH(struct matroxfb_dh_fb_info* m2info, struct display* p) { + switch (p->var.bits_per_pixel) { +#ifdef FBCON_HAS_CFB16 + case 16: + p->dispsw_data = m2info->cmap.cfb16; + p->dispsw = &fbcon_cfb16; + break; +#endif +#ifdef FBCON_HAS_CFB32 + case 32: + p->dispsw_data = m2info->cmap.cfb32; + p->dispsw = &fbcon_cfb32; + break; +#endif + default: + p->dispsw_data = NULL; + p->dispsw = &fbcon_dummy; + break; + } +} + +static int matroxfb_dh_open(struct fb_info* info, int user) { +#define m2info ((struct matroxfb_dh_fb_info*)info) + struct matrox_fb_info* minfo = m2info->primary_dev; + MOD_INC_USE_COUNT; + + if (minfo) { + if (ACCESS_FBINFO(dead)) { + return -ENXIO; + } + } + return 0; +#undef m2info +} + +static int matroxfb_dh_release(struct fb_info* info, int user) { +#define m2info ((struct matroxfb_dh_fb_info*)info) + struct matrox_fb_info* minfo = m2info->primary_dev; + + if (minfo) { + } + MOD_DEC_USE_COUNT; + return 0; +#undef m2info +} + +static int matroxfb_dh_get_fix(struct fb_fix_screeninfo* fix, int con, + struct fb_info* info) { +#define m2info ((struct matroxfb_dh_fb_info*)info) + struct display* p; + + if (con >= 0) + p = fb_display + con; + else + p = m2info->fbcon.disp; + + memset(fix, 0, sizeof(*fix)); + strcpy(fix->id, "MATROX DH"); + + fix->smem_start = m2info->video.base; + fix->smem_len = m2info->video.len_usable; + fix->type = p->type; + fix->type_aux = p->type_aux; + fix->visual = p->visual; + fix->xpanstep = 8; /* TBD */ + fix->ypanstep = 1; + fix->ywrapstep = 0; + fix->line_length = p->line_length; + fix->mmio_start = m2info->mmio.base; + fix->mmio_len = m2info->mmio.len; + fix->accel = 0; /* no accel... */ + return 0; +#undef m2info +} + +static int matroxfb_dh_get_var(struct fb_var_screeninfo* var, int con, + struct fb_info* info) { +#define m2info ((struct matroxfb_dh_fb_info*)info) + if (con < 0) + *var = m2info->fbcon.disp->var; + else + *var = fb_display[con].var; + return 0; +#undef m2info +} + +static int matroxfb_dh_set_var(struct fb_var_screeninfo* var, int con, + struct fb_info* info) { +#define m2info ((struct matroxfb_dh_fb_info*)info) + struct display* p; + int chgvar; + int visual; + int cmap_len; + int mode; + int err; + struct matrox_fb_info* minfo = m2info->primary_dev; + + if (con < 0) + p = m2info->fbcon.disp; + else + p = fb_display + con; + if ((err = matroxfb_dh_decode_var(m2info, p, var, &visual, &cmap_len, &mode)) != 0) + return err; + switch (var->activate & FB_ACTIVATE_MASK) { + case FB_ACTIVATE_TEST: return 0; + case FB_ACTIVATE_NXTOPEN: + case FB_ACTIVATE_NOW: break; + default: return -EINVAL; + } + if (con >= 0) { + chgvar = (p->var.xres != var->xres) || + (p->var.yres != var->yres) || + (p->var.xres_virtual != var->xres_virtual) || + (p->var.yres_virtual != var->yres_virtual) || + (p->var.bits_per_pixel != var->bits_per_pixel) || + memcmp(&p->var.red, &var->red, sizeof(var->red)) || + memcmp(&p->var.green, &var->green, sizeof(var->green)) || + memcmp(&p->var.blue, &var->blue, sizeof(var->blue)); + } else + chgvar = 0; + p->var = *var; + /* cmap */ + p->screen_base = vaddr_va(m2info->video.vbase); + p->visual = visual; + p->ypanstep = 1; + p->ywrapstep = 0; + p->type = FB_TYPE_PACKED_PIXELS; + p->type_aux = 0; + p->next_line = p->line_length = (var->xres_virtual * var->bits_per_pixel) >> 3; + p->can_soft_blank = 0; + p->inverse = 0; /* TBD */ + initMatroxDH(m2info, p); + if (chgvar && info && info->changevar) + info->changevar(con); + if (con == m2info->currcon) { + struct my_timming mt; + struct matrox_hw_state* hw; + struct matrox_hw_state* ohw; + unsigned int pos; + + matroxfb_var2my(var, &mt); + /* CRTC2 delay */ + mt.delay = 34; + + hw = ACCESS_FBINFO(newhw); + ohw = ACCESS_FBINFO(currenthw); + + /* copy last setting... */ + memcpy(hw, ohw, sizeof(*hw)); + + pos = (var->yoffset * var->xres_virtual + var->xoffset) * var->bits_per_pixel >> 3; + pos += m2info->video.offbase; + DAC1064_global_init(PMINFO hw); + if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_PRIMARY) { + if (ACCESS_FBINFO(primout)) + ACCESS_FBINFO(primout)->compute(MINFO, &mt, hw); + } + if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_SECONDARY) { + down_read(&ACCESS_FBINFO(altout.lock)); + if (ACCESS_FBINFO(altout.output)) + ACCESS_FBINFO(altout.output)->compute(ACCESS_FBINFO(altout.device), &mt, hw); + up_read(&ACCESS_FBINFO(altout.lock)); + } + matroxfb_dh_restore(m2info, &mt, p, mode, pos); + DAC1064_global_restore(PMINFO hw); + if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_PRIMARY) { + if (ACCESS_FBINFO(primout)) + ACCESS_FBINFO(primout)->program(MINFO, hw); + } + if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_SECONDARY) { + down_read(&ACCESS_FBINFO(altout.lock)); + if (ACCESS_FBINFO(altout.output)) + ACCESS_FBINFO(altout.output)->program(ACCESS_FBINFO(altout.device), hw); + up_read(&ACCESS_FBINFO(altout.lock)); + } + if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_PRIMARY) { + if (ACCESS_FBINFO(primout)) + ACCESS_FBINFO(primout)->start(MINFO); + } + if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_SECONDARY) { + down_read(&ACCESS_FBINFO(altout.lock)); + if (ACCESS_FBINFO(altout.output)) + ACCESS_FBINFO(altout.output)->start(ACCESS_FBINFO(altout.device)); + up_read(&ACCESS_FBINFO(altout.lock)); + } + matroxfb_dh_cfbX_init(m2info, p); + do_install_cmap(m2info, p); + } + return 0; +#undef m2info +} + +static int matroxfb_dh_get_cmap(struct fb_cmap* cmap, int kspc, int con, + struct fb_info* info) { +#define m2info ((struct matroxfb_dh_fb_info*)info) + struct display* dsp; + + if (con < 0) + dsp = m2info->fbcon.disp; + else + dsp = fb_display + con; + if (con == m2info->currcon) + return fb_get_cmap(cmap, kspc, matroxfb_dh_getcolreg, info); + else if (dsp->cmap.len) + fb_copy_cmap(&dsp->cmap, cmap, kspc ? 0 : 2); + else + fb_copy_cmap(fb_default_cmap(16), cmap, kspc ? 0 : 2); + return 0; +#undef m2info +} + +static int matroxfb_dh_set_cmap(struct fb_cmap* cmap, int kspc, int con, + struct fb_info* info) { +#define m2info ((struct matroxfb_dh_fb_info*)info) + struct display* dsp; + + if (con < 0) + dsp = m2info->fbcon.disp; + else + dsp = fb_display + con; + if (dsp->cmap.len != 16) { + int err; + + err = fb_alloc_cmap(&dsp->cmap, 16, 0); + if (err) + return err; + } + if (con == m2info->currcon) + return fb_set_cmap(cmap, kspc, matroxfb_dh_setcolreg, info); + else + fb_copy_cmap(cmap, &dsp->cmap, kspc ? 0 : 1); + return 0; +#undef m2info +} + +static int matroxfb_dh_pan_display(struct fb_var_screeninfo* var, int con, + struct fb_info* info) { +#define m2info ((struct matroxfb_dh_fb_info*)info) + if (var->xoffset + fb_display[con].var.xres > fb_display[con].var.xres_virtual || + var->yoffset + fb_display[con].var.yres > fb_display[con].var.yres_virtual) + return -EINVAL; + if (con == m2info->currcon) + matroxfb_dh_pan_var(m2info, var); + fb_display[con].var.xoffset = var->xoffset; + fb_display[con].var.yoffset = var->yoffset; + return 0; +#undef m2info +} + +static int matroxfb_dh_switch(int con, struct fb_info* info); + +static int matroxfb_dh_get_vblank(const struct matroxfb_dh_fb_info* m2info, struct fb_vblank* vblank) { + struct matrox_fb_info* minfo = m2info->primary_dev; + + 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->currcon_display->var.yres) + vblank->flags |= FB_VBLANK_VBLANKING; + return 0; +} + +static int matroxfb_dh_ioctl(struct inode* inode, + struct file* file, + unsigned int cmd, + unsigned long arg, + int con, + struct fb_info* info) { +#define m2info ((struct matroxfb_dh_fb_info*)info) + struct matrox_fb_info* minfo = m2info->primary_dev; + + DBG("matroxfb_crtc2_ioctl") + + switch (cmd) { + case FBIOGET_VBLANK: + { + struct fb_vblank vblank; + int err; + + err = matroxfb_dh_get_vblank(m2info, &vblank); + if (err) + return err; + copy_to_user_ret((struct fb_vblank*)arg, &vblank, sizeof(vblank), -EFAULT); + return 0; + } + 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, con, &minfo->fbcon); + } + case MATROXFB_SET_OUTPUT_CONNECTION: + { + u_int32_t tmp; + + get_user_ret(tmp, (u_int32_t*)arg, -EFAULT); + if (tmp & ~ACCESS_FBINFO(output.all)) + return -EINVAL; + if (tmp & ACCESS_FBINFO(output.ph)) + return -EINVAL; + if (tmp == ACCESS_FBINFO(output.sh)) + return 0; + ACCESS_FBINFO(output.sh) = tmp; + matroxfb_dh_switch(m2info->currcon, info); + return 0; + } + case MATROXFB_GET_OUTPUT_CONNECTION: + { + put_user_ret(ACCESS_FBINFO(output.sh), (u_int32_t*)arg, -EFAULT); + return 0; + } + case MATROXFB_GET_AVAILABLE_OUTPUTS: + { + u_int32_t tmp; + + tmp = ACCESS_FBINFO(output.all) & ~ACCESS_FBINFO(output.ph); + put_user_ret(tmp, (u_int32_t*)arg, -EFAULT); + return 0; + } + } + return -EINVAL; +#undef m2info +} + +static struct fb_ops matroxfb_dh_ops = { + matroxfb_dh_open, + matroxfb_dh_release, + matroxfb_dh_get_fix, + matroxfb_dh_get_var, + matroxfb_dh_set_var, + matroxfb_dh_get_cmap, + matroxfb_dh_set_cmap, + matroxfb_dh_pan_display, + matroxfb_dh_ioctl, + NULL /* mmap */ +}; + +static int matroxfb_dh_switch(int con, struct fb_info* info) { +#define m2info ((struct matroxfb_dh_fb_info*)info) + struct fb_cmap* cmap; + struct display* p; + + if (m2info->currcon >= 0) { + cmap = &m2info->currcon_display->cmap; + if (cmap->len) { + fb_get_cmap(cmap, 1, matroxfb_dh_getcolreg, info); + } + } + m2info->currcon = con; + if (con < 0) + p = m2info->fbcon.disp; + else + p = fb_display + con; + m2info->currcon_display = p; + p->var.activate = FB_ACTIVATE_NOW; + matroxfb_dh_set_var(&p->var, con, info); + return 0; +#undef m2info +} + +static int matroxfb_dh_updatevar(int con, struct fb_info* info) { +#define m2info ((struct matroxfb_dh_fb_info*)info) + matroxfb_dh_pan_var(m2info, &fb_display[con].var); + return 0; +#undef m2info +} + +static void matroxfb_dh_blank(int blank, struct fb_info* info) { +#define m2info ((struct matroxfb_dh_fb_info*)info) + switch (blank) { + case 1: + case 2: + case 3: + case 4: + default:; + } + /* do something... */ +#undef m2info +} + +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) + struct display* d; + void* oldcrtc2; + + d = kmalloc(sizeof(*d), GFP_KERNEL); + + memset(d, 0, sizeof(*d)); + + strcpy(m2info->fbcon.modename, "MATROX CRTC2"); + m2info->fbcon.changevar = NULL; + m2info->fbcon.node = -1; + m2info->fbcon.fbops = &matroxfb_dh_ops; + m2info->fbcon.disp = d; + m2info->fbcon.switch_con = &matroxfb_dh_switch; + m2info->fbcon.updatevar = &matroxfb_dh_updatevar; + m2info->fbcon.blank = &matroxfb_dh_blank; + m2info->fbcon.flags = FBINFO_FLAG_DEFAULT; + m2info->currcon = -1; + m2info->currcon_display = d; + + if (mem < 64) + mem *= 1024; + if (mem < 64*1024) + mem *= 1024; + mem &= ~0x00000FFF; /* PAGE_MASK? */ + if (minfo->video.len_usable + mem <= minfo->video.len) + m2info->video.offbase = minfo->video.len - mem; + else if (minfo->video.len < mem) { + kfree(d); + return -ENOMEM; + } else { /* check yres on first head... */ + m2info->video.borrowed = mem; + minfo->video.len_usable -= mem; + m2info->video.offbase = minfo->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 two outputs, connect CRTC2 to it... + */ + if (ACCESS_FBINFO(output.all) & MATROXFB_OUTPUT_CONN_SECONDARY) { + ACCESS_FBINFO(output.sh) |= MATROXFB_OUTPUT_CONN_SECONDARY; + ACCESS_FBINFO(output.ph) &= ~MATROXFB_OUTPUT_CONN_SECONDARY; + } + + matroxfb_dh_set_var(&matroxfb_dh_defined, -2, &m2info->fbcon); + if (register_framebuffer(&m2info->fbcon)) { + kfree(d); + return -ENXIO; + } + if (m2info->currcon < 0) { + matroxfb_dh_set_var(&matroxfb_dh_defined, -1, &m2info->fbcon); + } + down_write(&ACCESS_FBINFO(crtc2.lock)); + oldcrtc2 = minfo->crtc2.info; + minfo->crtc2.info = &m2info->fbcon; + 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", + GET_FB_IDX(ACCESS_FBINFO(fbcon.node)), GET_FB_IDX(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 fb_info* crtc2; + + down_write(&ACCESS_FBINFO(crtc2.lock)); + crtc2 = minfo->crtc2.info; + if (crtc2 == &m2info->fbcon) + minfo->crtc2.info = NULL; + up_write(&ACCESS_FBINFO(crtc2.lock)); + if (crtc2 != &m2info->fbcon) { + printk(KERN_ERR "matroxfb_crtc2: Internal consistency check failed: crtc2 mismatch at unload: %p != %p\n", + crtc2, &m2info->fbcon); + printk(KERN_ERR "matroxfb_crtc2: Expect kernel crash after module unload.\n"); + return; + } + id = GET_FB_IDX(m2info->fbcon.node); + unregister_framebuffer(&m2info->fbcon); + kfree(m2info->fbcon.disp); + /* 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 (!minfo->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); +} + +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,2000 Petr Vandrovec <vandrove@vc.cvut.cz>"); +MODULE_DESCRIPTION("Matrox G400 CRTC2 driver"); +module_init(matroxfb_crtc2_init); +module_exit(matroxfb_crtc2_exit); +/* we do not have __setup() yet */ |