summaryrefslogtreecommitdiffstats
path: root/drivers/video/fbdev/matrox/matroxfb_base.c
diff options
context:
space:
mode:
authorTomi Valkeinen <tomi.valkeinen@ti.com>2014-02-13 15:31:38 +0200
committerTomi Valkeinen <tomi.valkeinen@ti.com>2014-04-17 08:10:19 +0300
commitf7018c21350204c4cf628462f229d44d03545254 (patch)
tree408787177164cf51cc06f7aabdb04fcff8d2b6aa /drivers/video/fbdev/matrox/matroxfb_base.c
parentc26ef3eb3c11274bad1b64498d0a134f85755250 (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.c2584
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