diff options
author | Tomi Valkeinen <tomi.valkeinen@ti.com> | 2014-02-13 15:31:38 +0200 |
---|---|---|
committer | Tomi Valkeinen <tomi.valkeinen@ti.com> | 2014-04-17 08:10:19 +0300 |
commit | f7018c21350204c4cf628462f229d44d03545254 (patch) | |
tree | 408787177164cf51cc06f7aabdb04fcff8d2b6aa /drivers/video/fbdev/matrox/matroxfb_base.c | |
parent | c26ef3eb3c11274bad1b64498d0a134f85755250 (diff) |
video: move fbdev to drivers/video/fbdev
The drivers/video directory is a mess. It contains generic video related
files, directories for backlight, console, linux logo, lots of fbdev
device drivers, fbdev framework files.
Make some order into the chaos by creating drivers/video/fbdev
directory, and move all fbdev related files there.
No functionality is changed, although I guess it is possible that some
subtle Makefile build order related issue could be created by this
patch.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Geert Uytterhoeven <geert@linux-m68k.org>
Acked-by: Rob Clark <robdclark@gmail.com>
Acked-by: Jingoo Han <jg1.han@samsung.com>
Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Diffstat (limited to 'drivers/video/fbdev/matrox/matroxfb_base.c')
-rw-r--r-- | drivers/video/fbdev/matrox/matroxfb_base.c | 2584 |
1 files changed, 2584 insertions, 0 deletions
diff --git a/drivers/video/fbdev/matrox/matroxfb_base.c b/drivers/video/fbdev/matrox/matroxfb_base.c new file mode 100644 index 000000000000..7116c5309c7d --- /dev/null +++ b/drivers/video/fbdev/matrox/matroxfb_base.c @@ -0,0 +1,2584 @@ +/* + * + * 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 writing this driver) + * + * FreeVBE/AF (Matrox), "Shawn Hargreaves" <shawn@talula.demon.co.uk> + * + */ + +#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 <linux/slab.h> +#include <linux/uaccess.h> + +#ifdef CONFIG_PPC_PMAC +#include <asm/machdep.h> +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, +}; + + + +/* --------------------------------------------------------------------- */ +static void update_crtc2(struct matrox_fb_info *minfo, unsigned int pos) +{ + struct matroxfb_dh_fb_info *info = minfo->crtc2.info; + + /* Make sure that displays are compatible */ + if (info && (info->fbcon.var.bits_per_pixel == minfo->fbcon.var.bits_per_pixel) + && (info->fbcon.var.xres_virtual == minfo->fbcon.var.xres_virtual) + && (info->fbcon.var.green.length == minfo->fbcon.var.green.length) + ) { + switch (minfo->fbcon.var.bits_per_pixel) { + case 16: + case 32: + pos = pos * 8; + if (info->interlaced) { + mga_outl(0x3C2C, pos); + mga_outl(0x3C28, pos + minfo->fbcon.var.xres_virtual * minfo->fbcon.var.bits_per_pixel / 8); + } else { + mga_outl(0x3C28, pos); + } + break; + } + } +} + +static void matroxfb_crtc1_panpos(struct matrox_fb_info *minfo) +{ + if (minfo->crtc1.panpos >= 0) { + unsigned long flags; + int panpos; + + matroxfb_DAC_lock_irqsave(flags); + panpos = minfo->crtc1.panpos; + if (panpos >= 0) { + unsigned int extvga_reg; + + minfo->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) +{ + u_int32_t status; + int handled = 0; + struct matrox_fb_info *minfo = dev_id; + + status = mga_inl(M_STATUS); + + if (status & 0x20) { + mga_outl(M_ICLEAR, 0x20); + minfo->crtc1.vsync.cnt++; + matroxfb_crtc1_panpos(minfo); + wake_up_interruptible(&minfo->crtc1.vsync.wait); + handled = 1; + } + if (status & 0x200) { + mga_outl(M_ICLEAR, 0x200); + minfo->crtc2.vsync.cnt++; + wake_up_interruptible(&minfo->crtc2.vsync.wait); + handled = 1; + } + return IRQ_RETVAL(handled); +} + +int matroxfb_enable_irq(struct matrox_fb_info *minfo, int reenable) +{ + u_int32_t bm; + + if (minfo->devflags.accelerator == FB_ACCEL_MATROX_MGAG400) + bm = 0x220; + else + bm = 0x020; + + if (!test_and_set_bit(0, &minfo->irq_flags)) { + if (request_irq(minfo->pcidev->irq, matrox_irq, + IRQF_SHARED, "matroxfb", minfo)) { + clear_bit(0, &minfo->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", ien); + mga_outl(M_IEN, ien | bm); + } + } + return 0; +} + +static void matroxfb_disable_irq(struct matrox_fb_info *minfo) +{ + if (test_and_clear_bit(0, &minfo->irq_flags)) { + /* Flush pending pan-at-vbl request... */ + matroxfb_crtc1_panpos(minfo); + if (minfo->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(minfo->pcidev->irq, minfo); + } +} + +int matroxfb_wait_for_sync(struct matrox_fb_info *minfo, u_int32_t crtc) +{ + struct matrox_vsync *vs; + unsigned int cnt; + int ret; + + switch (crtc) { + case 0: + vs = &minfo->crtc1.vsync; + break; + case 1: + if (minfo->devflags.accelerator != FB_ACCEL_MATROX_MGAG400) { + return -ENODEV; + } + vs = &minfo->crtc2.vsync; + break; + default: + return -ENODEV; + } + ret = matroxfb_enable_irq(minfo, 0); + if (ret) { + return ret; + } + + cnt = vs->cnt; + ret = wait_event_interruptible_timeout(vs->wait, cnt != vs->cnt, HZ/10); + if (ret < 0) { + return ret; + } + if (ret == 0) { + matroxfb_enable_irq(minfo, 1); + return -ETIMEDOUT; + } + return 0; +} + +/* --------------------------------------------------------------------- */ + +static void matrox_pan_var(struct matrox_fb_info *minfo, + struct fb_var_screeninfo *var) +{ + unsigned int pos; + unsigned short p0, p1, p2; + unsigned int p3; + int vbl; + unsigned long flags; + + CRITFLAGS + + DBG(__func__) + + if (minfo->dead) + return; + + minfo->fbcon.var.xoffset = var->xoffset; + minfo->fbcon.var.yoffset = var->yoffset; + pos = (minfo->fbcon.var.yoffset * minfo->fbcon.var.xres_virtual + minfo->fbcon.var.xoffset) * minfo->curr.final_bppShift / 32; + pos += minfo->curr.ydstorg.chunks; + p0 = minfo->hw.CRTC[0x0D] = pos & 0xFF; + p1 = minfo->hw.CRTC[0x0C] = (pos & 0xFF00) >> 8; + p2 = minfo->hw.CRTCEXT[0] = (minfo->hw.CRTCEXT[0] & 0xB0) | ((pos >> 16) & 0x0F) | ((pos >> 14) & 0x40); + p3 = minfo->hw.CRTCEXT[8] = pos >> 21; + + /* FB_ACTIVATE_VBL and we can acquire interrupts? Honor FB_ACTIVATE_VBL then... */ + vbl = (var->activate & FB_ACTIVATE_VBL) && (matroxfb_enable_irq(minfo, 0) == 0); + + CRITBEGIN + + matroxfb_DAC_lock_irqsave(flags); + mga_setr(M_CRTC_INDEX, 0x0D, p0); + mga_setr(M_CRTC_INDEX, 0x0C, p1); + if (minfo->devflags.support32MB) + mga_setr(M_EXTVGA_INDEX, 0x08, p3); + if (vbl) { + minfo->crtc1.panpos = p2; + } else { + /* Abort any pending change */ + minfo->crtc1.panpos = -1; + mga_setr(M_EXTVGA_INDEX, 0x00, p2); + } + matroxfb_DAC_unlock_irqrestore(flags); + + update_crtc2(minfo, pos); + + CRITEND +} + +static void matroxfb_remove(struct matrox_fb_info *minfo, 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... + */ + + minfo->dead = 1; + if (minfo->usecount) { + /* destroy it later */ + return; + } + matroxfb_unregister_device(minfo); + unregister_framebuffer(&minfo->fbcon); + matroxfb_g450_shutdown(minfo); +#ifdef CONFIG_MTRR + if (minfo->mtrr.vram_valid) + mtrr_del(minfo->mtrr.vram, minfo->video.base, minfo->video.len); +#endif + mga_iounmap(minfo->mmio.vbase); + mga_iounmap(minfo->video.vbase); + release_mem_region(minfo->video.base, minfo->video.len_maximum); + release_mem_region(minfo->mmio.base, 16384); + kfree(minfo); +} + + /* + * Open/Release the frame buffer device + */ + +static int matroxfb_open(struct fb_info *info, int user) +{ + struct matrox_fb_info *minfo = info2minfo(info); + + DBG_LOOP(__func__) + + if (minfo->dead) { + return -ENXIO; + } + minfo->usecount++; + if (user) { + minfo->userusecount++; + } + return(0); +} + +static int matroxfb_release(struct fb_info *info, int user) +{ + struct matrox_fb_info *minfo = info2minfo(info); + + DBG_LOOP(__func__) + + if (user) { + if (0 == --minfo->userusecount) { + matroxfb_disable_irq(minfo); + } + } + if (!(--minfo->usecount) && minfo->dead) { + matroxfb_remove(minfo, 0); + } + return(0); +} + +static int matroxfb_pan_display(struct fb_var_screeninfo *var, + struct fb_info* info) { + struct matrox_fb_info *minfo = info2minfo(info); + + DBG(__func__) + + matrox_pan_var(minfo, var); + return 0; +} + +static int matroxfb_get_final_bppShift(const struct matrox_fb_info *minfo, + int bpp) +{ + int bppshft2; + + DBG(__func__) + + bppshft2 = bpp; + if (!bppshft2) { + return 8; + } + if (isInterleave(minfo)) + bppshft2 >>= 1; + if (minfo->devflags.video64bits) + bppshft2 >>= 1; + return bppshft2; +} + +static int matroxfb_test_and_set_rounding(const struct matrox_fb_info *minfo, + int xres, int bpp) +{ + int over; + int rounding; + + DBG(__func__) + + 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 (minfo->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(const struct matrox_fb_info *minfo, int xres, + int bpp) +{ + const int* width; + int xres_new; + + DBG(__func__) + + if (!bpp) return xres; + + width = minfo->capable.vxres; + + if (minfo->devflags.precise_width) { + while (*width) { + if ((*width >= xres) && (matroxfb_test_and_set_rounding(minfo, *width, bpp) == *width)) { + break; + } + width++; + } + xres_new = *width; + } else { + xres_new = matroxfb_test_and_set_rounding(minfo, xres, bpp); + } + return xres_new; +} + +static int matroxfb_get_cmap_len(struct fb_var_screeninfo *var) { + + DBG(__func__) + + 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(const struct matrox_fb_info *minfo, + 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(__func__) + + switch (bpp) { + case 4: if (!minfo->capable.cfb4) return -EINVAL; + break; + case 8: break; + case 16: break; + case 24: break; + case 32: break; + default: return -EINVAL; + } + *ydstorg = 0; + vramlen = minfo->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(minfo, 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 (!minfo->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 artificial 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) +{ + struct matrox_fb_info* minfo = container_of(fb_info, struct matrox_fb_info, fbcon); + + DBG(__func__) + + /* + * 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 >= minfo->curr.cmap_len) + return 1; + + if (minfo->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, minfo->fbcon.var.red.length); + green = CNVT_TOHW(green, minfo->fbcon.var.green.length); + blue = CNVT_TOHW(blue, minfo->fbcon.var.blue.length); + transp = CNVT_TOHW(transp, minfo->fbcon.var.transp.length); + + switch (minfo->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: + if (regno >= 16) + break; + { + u_int16_t col = + (red << minfo->fbcon.var.red.offset) | + (green << minfo->fbcon.var.green.offset) | + (blue << minfo->fbcon.var.blue.offset) | + (transp << minfo->fbcon.var.transp.offset); /* for 1:5:5:5 */ + minfo->cmap[regno] = col | (col << 16); + } + break; + case 24: + case 32: + if (regno >= 16) + break; + minfo->cmap[regno] = + (red << minfo->fbcon.var.red.offset) | + (green << minfo->fbcon.var.green.offset) | + (blue << minfo->fbcon.var.blue.offset) | + (transp << minfo->fbcon.var.transp.offset); /* 8:8:8:8 */ + break; + } + return 0; +} + +static void matroxfb_init_fix(struct matrox_fb_info *minfo) +{ + struct fb_fix_screeninfo *fix = &minfo->fbcon.fix; + DBG(__func__) + + 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 = minfo->mmio.base; + fix->mmio_len = minfo->mmio.len; + fix->accel = minfo->devflags.accelerator; +} + +static void matroxfb_update_fix(struct matrox_fb_info *minfo) +{ + struct fb_fix_screeninfo *fix = &minfo->fbcon.fix; + DBG(__func__) + + mutex_lock(&minfo->fbcon.mm_lock); + fix->smem_start = minfo->video.base + minfo->curr.ydstorg.bytes; + fix->smem_len = minfo->video.len_usable - minfo->curr.ydstorg.bytes; + mutex_unlock(&minfo->fbcon.mm_lock); +} + +static int matroxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + int err; + int visual; + int cmap_len; + unsigned int ydstorg; + struct matrox_fb_info *minfo = info2minfo(info); + + if (minfo->dead) { + return -ENXIO; + } + if ((err = matroxfb_decode_var(minfo, 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; + struct matrox_fb_info *minfo = info2minfo(info); + + DBG(__func__) + + if (minfo->dead) { + return -ENXIO; + } + + var = &info->var; + if ((err = matroxfb_decode_var(minfo, var, &visual, &cmap_len, &ydstorg)) != 0) + return err; + minfo->fbcon.screen_base = vaddr_va(minfo->video.vbase) + ydstorg; + matroxfb_update_fix(minfo); + minfo->fbcon.fix.visual = visual; + minfo->fbcon.fix.type = FB_TYPE_PACKED_PIXELS; + minfo->fbcon.fix.type_aux = 0; + minfo->fbcon.fix.line_length = (var->xres_virtual * var->bits_per_pixel) >> 3; + { + unsigned int pos; + + minfo->curr.cmap_len = cmap_len; + ydstorg += minfo->devflags.ydstorg; + minfo->curr.ydstorg.bytes = ydstorg; + minfo->curr.ydstorg.chunks = ydstorg >> (isInterleave(minfo) ? 3 : 2); + if (var->bits_per_pixel == 4) + minfo->curr.ydstorg.pixels = ydstorg; + else + minfo->curr.ydstorg.pixels = (ydstorg * 8) / var->bits_per_pixel; + minfo->curr.final_bppShift = matroxfb_get_final_bppShift(minfo, 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 = &minfo->hw; + + down_read(&minfo->altout.lock); + for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { + if (minfo->outputs[out].src == MATROXFB_SRC_CRTC1 && + minfo->outputs[out].output->compute) { + minfo->outputs[out].output->compute(minfo->outputs[out].data, &mt); + } + } + up_read(&minfo->altout.lock); + minfo->crtc1.pixclock = mt.pixclock; + minfo->crtc1.mnp = mt.mnp; + minfo->hw_switch->init(minfo, &mt); + pos = (var->yoffset * var->xres_virtual + var->xoffset) * minfo->curr.final_bppShift / 32; + pos += minfo->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; + minfo->hw_switch->restore(minfo); + update_crtc2(minfo, pos); + down_read(&minfo->altout.lock); + for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { + if (minfo->outputs[out].src == MATROXFB_SRC_CRTC1 && + minfo->outputs[out].output->program) { + minfo->outputs[out].output->program(minfo->outputs[out].data); + } + } + for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { + if (minfo->outputs[out].src == MATROXFB_SRC_CRTC1 && + minfo->outputs[out].output->start) { + minfo->outputs[out].output->start(minfo->outputs[out].data); + } + } + up_read(&minfo->altout.lock); + matrox_cfbX_init(minfo); + } + } + minfo->initialized = 1; + return 0; +} + +static int matroxfb_get_vblank(struct matrox_fb_info *minfo, + struct fb_vblank *vblank) +{ + unsigned int sts1; + + matroxfb_enable_irq(minfo, 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 >= minfo->fbcon.var.yres) + vblank->flags |= FB_VBLANK_VBLANKING; + if (test_bit(0, &minfo->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 = minfo->crtc1.vsync.cnt; + } + return 0; +} + +static struct matrox_altout panellink_output = { + .name = "Panellink output", +}; + +static int matroxfb_ioctl(struct fb_info *info, + unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + struct matrox_fb_info *minfo = info2minfo(info); + + DBG(__func__) + + if (minfo->dead) { + return -ENXIO; + } + + switch (cmd) { + case FBIOGET_VBLANK: + { + struct fb_vblank vblank; + int err; + + err = matroxfb_get_vblank(minfo, &vblank); + if (err) + return err; + if (copy_to_user(argp, &vblank, sizeof(vblank))) + return -EFAULT; + return 0; + } + case FBIO_WAITFORVSYNC: + { + u_int32_t crt; + + if (get_user(crt, (u_int32_t __user *)arg)) + return -EFAULT; + + return matroxfb_wait_for_sync(minfo, crt); + } + case MATROXFB_SET_OUTPUT_MODE: + { + struct matroxioc_output_mode mom; + struct matrox_altout *oproc; + int val; + + if (copy_from_user(&mom, argp, sizeof(mom))) + return -EFAULT; + if (mom.output >= MATROXFB_MAX_OUTPUTS) + return -ENXIO; + down_read(&minfo->altout.lock); + oproc = minfo->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(minfo->outputs[mom.output].data, mom.mode); + } + if (!val) { + if (minfo->outputs[mom.output].mode != mom.mode) { + minfo->outputs[mom.output].mode = mom.mode; + val = 1; + } + } + up_read(&minfo->altout.lock); + if (val != 1) + return val; + switch (minfo->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(&minfo->crtc2.lock); + crtc2 = minfo->crtc2.info; + if (crtc2) + crtc2->fbcon.fbops->fb_set_par(&crtc2->fbcon); + up_read(&minfo->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, argp, sizeof(mom))) + return -EFAULT; + if (mom.output >= MATROXFB_MAX_OUTPUTS) + return -ENXIO; + down_read(&minfo->altout.lock); + oproc = minfo->outputs[mom.output].output; + if (!oproc) { + val = -ENXIO; + } else { + mom.mode = minfo->outputs[mom.output].mode; + val = 0; + } + up_read(&minfo->altout.lock); + if (val) + return val; + if (copy_to_user(argp, &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, argp, sizeof(tmp))) + return -EFAULT; + for (i = 0; i < 32; i++) { + if (tmp & (1 << i)) { + if (i >= MATROXFB_MAX_OUTPUTS) + return -ENXIO; + if (!minfo->outputs[i].output) + return -ENXIO; + switch (minfo->outputs[i].src) { + case MATROXFB_SRC_NONE: + case MATROXFB_SRC_CRTC1: + break; + default: + return -EBUSY; + } + } + } + if (minfo->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 (minfo->outputs[i].src == MATROXFB_SRC_CRTC2) { + return -EBUSY; + } + } + } + } + changes = 0; + for (i = 0; i < MATROXFB_MAX_OUTPUTS; i++) { + if (tmp & (1 << i)) { + if (minfo->outputs[i].src != MATROXFB_SRC_CRTC1) { + changes = 1; + minfo->outputs[i].src = MATROXFB_SRC_CRTC1; + } + } else if (minfo->outputs[i].src == MATROXFB_SRC_CRTC1) { + changes = 1; + minfo->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 (minfo->outputs[i].src == MATROXFB_SRC_CRTC1) { + conn |= 1 << i; + } + } + if (put_user(conn, (u_int32_t __user *)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 (minfo->outputs[i].output) { + switch (minfo->outputs[i].src) { + case MATROXFB_SRC_NONE: + case MATROXFB_SRC_CRTC1: + conn |= 1 << i; + break; + } + } + } + if (minfo->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 __user *)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 (minfo->outputs[i].output) { + conn |= 1 << i; + } + } + if (put_user(conn, (u_int32_t __user *)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"); + sprintf(r.bus_info, "PCI:%s", pci_name(minfo->pcidev)); + r.version = KERNEL_VERSION(1,0,0); + r.capabilities = V4L2_CAP_VIDEO_OUTPUT; + if (copy_to_user(argp, &r, sizeof(r))) + return -EFAULT; + return 0; + + } + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl qctrl; + int err; + + if (copy_from_user(&qctrl, argp, sizeof(qctrl))) + return -EFAULT; + + down_read(&minfo->altout.lock); + if (!m |