cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

matroxfb_crtc2.c (20339B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 *
      4 * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200, G400 and G450.
      5 *
      6 * (c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz>
      7 *
      8 * Portions Copyright (c) 2001 Matrox Graphics Inc.
      9 *
     10 * Version: 1.65 2002/08/14
     11 *
     12 */
     13
     14#include "matroxfb_maven.h"
     15#include "matroxfb_crtc2.h"
     16#include "matroxfb_misc.h"
     17#include "matroxfb_DAC1064.h"
     18#include <linux/matroxfb.h>
     19#include <linux/slab.h>
     20#include <linux/uaccess.h>
     21
     22/* **************************************************** */
     23
     24static int mem = 8192;
     25
     26module_param(mem, int, 0);
     27MODULE_PARM_DESC(mem, "Memory size reserved for dualhead (default=8MB)");
     28
     29/* **************************************************** */
     30
     31static int matroxfb_dh_setcolreg(unsigned regno, unsigned red, unsigned green,
     32		unsigned blue, unsigned transp, struct fb_info* info) {
     33	u_int32_t col;
     34#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
     35
     36	if (regno >= 16)
     37		return 1;
     38	if (m2info->fbcon.var.grayscale) {
     39		/* gray = 0.30*R + 0.59*G + 0.11*B */
     40		red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
     41	}
     42	red = CNVT_TOHW(red, m2info->fbcon.var.red.length);
     43	green = CNVT_TOHW(green, m2info->fbcon.var.green.length);
     44	blue = CNVT_TOHW(blue, m2info->fbcon.var.blue.length);
     45	transp = CNVT_TOHW(transp, m2info->fbcon.var.transp.length);
     46
     47	col = (red << m2info->fbcon.var.red.offset)     |
     48	      (green << m2info->fbcon.var.green.offset) |
     49	      (blue << m2info->fbcon.var.blue.offset)   |
     50	      (transp << m2info->fbcon.var.transp.offset);
     51
     52	switch (m2info->fbcon.var.bits_per_pixel) {
     53		case 16:
     54			m2info->cmap[regno] = col | (col << 16);
     55			break;
     56		case 32:
     57			m2info->cmap[regno] = col;
     58			break;
     59	}
     60	return 0;
     61#undef m2info
     62}
     63
     64static void matroxfb_dh_restore(struct matroxfb_dh_fb_info* m2info,
     65		struct my_timming* mt,
     66		int mode,
     67		unsigned int pos) {
     68	u_int32_t tmp;
     69	u_int32_t datactl;
     70	struct matrox_fb_info *minfo = m2info->primary_dev;
     71
     72	switch (mode) {
     73		case 15:
     74			tmp = 0x00200000;
     75			break;
     76		case 16:
     77			tmp = 0x00400000;
     78			break;
     79/*		case 32: */
     80		default:
     81			tmp = 0x00800000;
     82			break;
     83	}
     84	tmp |= 0x00000001;	/* enable CRTC2 */
     85	datactl = 0;
     86	if (minfo->outputs[1].src == MATROXFB_SRC_CRTC2) {
     87		if (minfo->devflags.g450dac) {
     88			tmp |= 0x00000006; /* source from secondary pixel PLL */
     89			/* no vidrst when in monitor mode */
     90			if (minfo->outputs[1].mode != MATROXFB_OUTPUT_MODE_MONITOR) {
     91				tmp |=  0xC0001000; /* Enable H/V vidrst */
     92			}
     93		} else {
     94			tmp |= 0x00000002; /* source from VDOCLK */
     95			tmp |= 0xC0000000; /* enable vvidrst & hvidrst */
     96			/* MGA TVO is our clock source */
     97		}
     98	} else if (minfo->outputs[0].src == MATROXFB_SRC_CRTC2) {
     99		tmp |= 0x00000004; /* source from pixclock */
    100		/* PIXPLL is our clock source */
    101	}
    102	if (minfo->outputs[0].src == MATROXFB_SRC_CRTC2) {
    103		tmp |= 0x00100000;	/* connect CRTC2 to DAC */
    104	}
    105	if (mt->interlaced) {
    106		tmp |= 0x02000000;	/* interlaced, second field is bigger, as G450 apparently ignores it */
    107		mt->VDisplay >>= 1;
    108		mt->VSyncStart >>= 1;
    109		mt->VSyncEnd >>= 1;
    110		mt->VTotal >>= 1;
    111	}
    112	if ((mt->HTotal & 7) == 2) {
    113		datactl |= 0x00000010;
    114		mt->HTotal &= ~7;
    115	}
    116	tmp |= 0x10000000;	/* 0x10000000 is VIDRST polarity */
    117	mga_outl(0x3C14, ((mt->HDisplay - 8) << 16) | (mt->HTotal - 8));
    118	mga_outl(0x3C18, ((mt->HSyncEnd - 8) << 16) | (mt->HSyncStart - 8));
    119	mga_outl(0x3C1C, ((mt->VDisplay - 1) << 16) | (mt->VTotal - 1));
    120	mga_outl(0x3C20, ((mt->VSyncEnd - 1) << 16) | (mt->VSyncStart - 1));
    121	mga_outl(0x3C24, ((mt->VSyncStart) << 16) | (mt->HSyncStart));	/* preload */
    122	{
    123		u_int32_t linelen = m2info->fbcon.var.xres_virtual * (m2info->fbcon.var.bits_per_pixel >> 3);
    124		if (tmp & 0x02000000) {
    125			/* field #0 is smaller, so... */
    126			mga_outl(0x3C2C, pos);			/* field #1 vmemory start */
    127			mga_outl(0x3C28, pos + linelen);	/* field #0 vmemory start */
    128			linelen <<= 1;
    129			m2info->interlaced = 1;
    130		} else {
    131			mga_outl(0x3C28, pos);		/* vmemory start */
    132			m2info->interlaced = 0;
    133		}
    134		mga_outl(0x3C40, linelen);
    135	}
    136	mga_outl(0x3C4C, datactl);	/* data control */
    137	if (tmp & 0x02000000) {
    138		int i;
    139
    140		mga_outl(0x3C10, tmp & ~0x02000000);
    141		for (i = 0; i < 2; i++) {
    142			unsigned int nl;
    143			unsigned int lastl = 0;
    144
    145			while ((nl = mga_inl(0x3C48) & 0xFFF) >= lastl) {
    146				lastl = nl;
    147			}
    148		}
    149	}
    150	mga_outl(0x3C10, tmp);
    151	minfo->hw.crtc2.ctl = tmp;
    152
    153	tmp = mt->VDisplay << 16;	/* line compare */
    154	if (mt->sync & FB_SYNC_HOR_HIGH_ACT)
    155		tmp |= 0x00000100;
    156	if (mt->sync & FB_SYNC_VERT_HIGH_ACT)
    157		tmp |= 0x00000200;
    158	mga_outl(0x3C44, tmp);
    159}
    160
    161static void matroxfb_dh_disable(struct matroxfb_dh_fb_info* m2info) {
    162	struct matrox_fb_info *minfo = m2info->primary_dev;
    163
    164	mga_outl(0x3C10, 0x00000004);	/* disable CRTC2, CRTC1->DAC1, PLL as clock source */
    165	minfo->hw.crtc2.ctl = 0x00000004;
    166}
    167
    168static void matroxfb_dh_pan_var(struct matroxfb_dh_fb_info* m2info,
    169		struct fb_var_screeninfo* var) {
    170	unsigned int pos;
    171	unsigned int linelen;
    172	unsigned int pixelsize;
    173	struct matrox_fb_info *minfo = m2info->primary_dev;
    174
    175	m2info->fbcon.var.xoffset = var->xoffset;
    176	m2info->fbcon.var.yoffset = var->yoffset;
    177	pixelsize = m2info->fbcon.var.bits_per_pixel >> 3;
    178	linelen = m2info->fbcon.var.xres_virtual * pixelsize;
    179	pos = m2info->fbcon.var.yoffset * linelen + m2info->fbcon.var.xoffset * pixelsize;
    180	pos += m2info->video.offbase;
    181	if (m2info->interlaced) {
    182		mga_outl(0x3C2C, pos);
    183		mga_outl(0x3C28, pos + linelen);
    184	} else {
    185		mga_outl(0x3C28, pos);
    186	}
    187}
    188
    189static int matroxfb_dh_decode_var(struct matroxfb_dh_fb_info* m2info,
    190		struct fb_var_screeninfo* var,
    191		int *visual,
    192		int *video_cmap_len,
    193		int *mode) {
    194	unsigned int mask;
    195	unsigned int memlen;
    196	unsigned int vramlen;
    197
    198	switch (var->bits_per_pixel) {
    199		case 16:	mask = 0x1F;
    200				break;
    201		case 32:	mask = 0x0F;
    202				break;
    203		default:	return -EINVAL;
    204	}
    205	vramlen = m2info->video.len_usable;
    206	if (var->yres_virtual < var->yres)
    207		var->yres_virtual = var->yres;
    208	if (var->xres_virtual < var->xres)
    209		var->xres_virtual = var->xres;
    210	var->xres_virtual = (var->xres_virtual + mask) & ~mask;
    211	if (var->yres_virtual > 32767)
    212		return -EINVAL;
    213	memlen = var->xres_virtual * var->yres_virtual * (var->bits_per_pixel >> 3);
    214	if (memlen > vramlen)
    215		return -EINVAL;
    216	if (var->xoffset + var->xres > var->xres_virtual)
    217		var->xoffset = var->xres_virtual - var->xres;
    218	if (var->yoffset + var->yres > var->yres_virtual)
    219		var->yoffset = var->yres_virtual - var->yres;
    220
    221	var->xres &= ~7;
    222	var->left_margin &= ~7;
    223	var->right_margin &= ~7;
    224	var->hsync_len &= ~7;
    225
    226	*mode = var->bits_per_pixel;
    227	if (var->bits_per_pixel == 16) {
    228		if (var->green.length == 5) {
    229			var->red.offset = 10;
    230			var->red.length = 5;
    231			var->green.offset = 5;
    232			var->green.length = 5;
    233			var->blue.offset = 0;
    234			var->blue.length = 5;
    235			var->transp.offset = 15;
    236			var->transp.length = 1;
    237			*mode = 15;
    238		} else {
    239			var->red.offset = 11;
    240			var->red.length = 5;
    241			var->green.offset = 5;
    242			var->green.length = 6;
    243			var->blue.offset = 0;
    244			var->blue.length = 5;
    245			var->transp.offset = 0;
    246			var->transp.length = 0;
    247		}
    248	} else {
    249			var->red.offset = 16;
    250			var->red.length = 8;
    251			var->green.offset = 8;
    252			var->green.length = 8;
    253			var->blue.offset = 0;
    254			var->blue.length = 8;
    255			var->transp.offset = 24;
    256			var->transp.length = 8;
    257	}
    258	*visual = FB_VISUAL_TRUECOLOR;
    259	*video_cmap_len = 16;
    260	return 0;
    261}
    262
    263static int matroxfb_dh_open(struct fb_info* info, int user) {
    264#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
    265	struct matrox_fb_info *minfo = m2info->primary_dev;
    266
    267	if (minfo) {
    268		int err;
    269
    270		if (minfo->dead) {
    271			return -ENXIO;
    272		}
    273		err = minfo->fbops.fb_open(&minfo->fbcon, user);
    274		if (err) {
    275			return err;
    276		}
    277	}
    278	return 0;
    279#undef m2info
    280}
    281
    282static int matroxfb_dh_release(struct fb_info* info, int user) {
    283#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
    284	int err = 0;
    285	struct matrox_fb_info *minfo = m2info->primary_dev;
    286
    287	if (minfo) {
    288		err = minfo->fbops.fb_release(&minfo->fbcon, user);
    289	}
    290	return err;
    291#undef m2info
    292}
    293
    294/*
    295 * This function is called before the register_framebuffer so
    296 * no locking is needed.
    297 */
    298static void matroxfb_dh_init_fix(struct matroxfb_dh_fb_info *m2info)
    299{
    300	struct fb_fix_screeninfo *fix = &m2info->fbcon.fix;
    301
    302	strcpy(fix->id, "MATROX DH");
    303
    304	fix->smem_start = m2info->video.base;
    305	fix->smem_len = m2info->video.len_usable;
    306	fix->ypanstep = 1;
    307	fix->ywrapstep = 0;
    308	fix->xpanstep = 8;	/* TBD */
    309	fix->mmio_start = m2info->mmio.base;
    310	fix->mmio_len = m2info->mmio.len;
    311	fix->accel = 0;		/* no accel... */
    312}
    313
    314static int matroxfb_dh_check_var(struct fb_var_screeninfo* var, struct fb_info* info) {
    315#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
    316	int visual;
    317	int cmap_len;
    318	int mode;
    319
    320	return matroxfb_dh_decode_var(m2info, var, &visual, &cmap_len, &mode);
    321#undef m2info
    322}
    323
    324static int matroxfb_dh_set_par(struct fb_info* info) {
    325#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
    326	int visual;
    327	int cmap_len;
    328	int mode;
    329	int err;
    330	struct fb_var_screeninfo* var = &info->var;
    331	struct matrox_fb_info *minfo = m2info->primary_dev;
    332
    333	if ((err = matroxfb_dh_decode_var(m2info, var, &visual, &cmap_len, &mode)) != 0)
    334		return err;
    335	/* cmap */
    336	{
    337		m2info->fbcon.screen_base = vaddr_va(m2info->video.vbase);
    338		m2info->fbcon.fix.visual = visual;
    339		m2info->fbcon.fix.type = FB_TYPE_PACKED_PIXELS;
    340		m2info->fbcon.fix.type_aux = 0;
    341		m2info->fbcon.fix.line_length = (var->xres_virtual * var->bits_per_pixel) >> 3;
    342	}
    343	{
    344		struct my_timming mt;
    345		unsigned int pos;
    346		int out;
    347		int cnt;
    348
    349		matroxfb_var2my(&m2info->fbcon.var, &mt);
    350		mt.crtc = MATROXFB_SRC_CRTC2;
    351		/* CRTC2 delay */
    352		mt.delay = 34;
    353
    354		pos = (m2info->fbcon.var.yoffset * m2info->fbcon.var.xres_virtual + m2info->fbcon.var.xoffset) * m2info->fbcon.var.bits_per_pixel >> 3;
    355		pos += m2info->video.offbase;
    356		cnt = 0;
    357		down_read(&minfo->altout.lock);
    358		for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
    359			if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2) {
    360				cnt++;
    361				if (minfo->outputs[out].output->compute) {
    362					minfo->outputs[out].output->compute(minfo->outputs[out].data, &mt);
    363				}
    364			}
    365		}
    366		minfo->crtc2.pixclock = mt.pixclock;
    367		minfo->crtc2.mnp = mt.mnp;
    368		up_read(&minfo->altout.lock);
    369		if (cnt) {
    370			matroxfb_dh_restore(m2info, &mt, mode, pos);
    371		} else {
    372			matroxfb_dh_disable(m2info);
    373		}
    374		DAC1064_global_init(minfo);
    375		DAC1064_global_restore(minfo);
    376		down_read(&minfo->altout.lock);
    377		for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
    378			if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2 &&
    379			    minfo->outputs[out].output->program) {
    380				minfo->outputs[out].output->program(minfo->outputs[out].data);
    381			}
    382		}
    383		for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
    384			if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2 &&
    385			    minfo->outputs[out].output->start) {
    386				minfo->outputs[out].output->start(minfo->outputs[out].data);
    387			}
    388		}
    389		up_read(&minfo->altout.lock);
    390	}
    391	m2info->initialized = 1;
    392	return 0;
    393#undef m2info
    394}
    395
    396static int matroxfb_dh_pan_display(struct fb_var_screeninfo* var, struct fb_info* info) {
    397#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
    398	matroxfb_dh_pan_var(m2info, var);
    399	return 0;
    400#undef m2info
    401}
    402
    403static int matroxfb_dh_get_vblank(const struct matroxfb_dh_fb_info* m2info, struct fb_vblank* vblank) {
    404	struct matrox_fb_info *minfo = m2info->primary_dev;
    405
    406	matroxfb_enable_irq(minfo, 0);
    407	memset(vblank, 0, sizeof(*vblank));
    408	vblank->flags = FB_VBLANK_HAVE_VCOUNT | FB_VBLANK_HAVE_VBLANK;
    409	/* mask out reserved bits + field number (odd/even) */
    410	vblank->vcount = mga_inl(0x3C48) & 0x000007FF;
    411	/* compatibility stuff */
    412	if (vblank->vcount >= m2info->fbcon.var.yres)
    413		vblank->flags |= FB_VBLANK_VBLANKING;
    414	if (test_bit(0, &minfo->irq_flags)) {
    415                vblank->flags |= FB_VBLANK_HAVE_COUNT;
    416                /* Only one writer, aligned int value...
    417                   it should work without lock and without atomic_t */
    418		vblank->count = minfo->crtc2.vsync.cnt;
    419        }
    420	return 0;
    421}
    422
    423static int matroxfb_dh_ioctl(struct fb_info *info,
    424		unsigned int cmd,
    425		unsigned long arg)
    426{
    427#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
    428	struct matrox_fb_info *minfo = m2info->primary_dev;
    429
    430	DBG(__func__)
    431
    432	switch (cmd) {
    433		case FBIOGET_VBLANK:
    434			{
    435				struct fb_vblank vblank;
    436				int err;
    437
    438				err = matroxfb_dh_get_vblank(m2info, &vblank);
    439				if (err)
    440					return err;
    441				if (copy_to_user((void __user *)arg, &vblank, sizeof(vblank)))
    442					return -EFAULT;
    443				return 0;
    444			}
    445		case FBIO_WAITFORVSYNC:
    446			{
    447				u_int32_t crt;
    448
    449				if (get_user(crt, (u_int32_t __user *)arg))
    450					return -EFAULT;
    451
    452				if (crt != 0)
    453					return -ENODEV;
    454				return matroxfb_wait_for_sync(minfo, 1);
    455			}
    456		case MATROXFB_SET_OUTPUT_MODE:
    457		case MATROXFB_GET_OUTPUT_MODE:
    458		case MATROXFB_GET_ALL_OUTPUTS:
    459			{
    460				return minfo->fbcon.fbops->fb_ioctl(&minfo->fbcon, cmd, arg);
    461			}
    462		case MATROXFB_SET_OUTPUT_CONNECTION:
    463			{
    464				u_int32_t tmp;
    465				int out;
    466				int changes;
    467
    468				if (get_user(tmp, (u_int32_t __user *)arg))
    469					return -EFAULT;
    470				for (out = 0; out < 32; out++) {
    471					if (tmp & (1 << out)) {
    472						if (out >= MATROXFB_MAX_OUTPUTS)
    473							return -ENXIO;
    474						if (!minfo->outputs[out].output)
    475							return -ENXIO;
    476						switch (minfo->outputs[out].src) {
    477							case MATROXFB_SRC_NONE:
    478							case MATROXFB_SRC_CRTC2:
    479								break;
    480							default:
    481								return -EBUSY;
    482						}
    483					}
    484				}
    485				if (minfo->devflags.panellink) {
    486					if (tmp & MATROXFB_OUTPUT_CONN_DFP)
    487						return -EINVAL;
    488					if ((minfo->outputs[2].src == MATROXFB_SRC_CRTC1) && tmp)
    489						return -EBUSY;
    490				}
    491				changes = 0;
    492				for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
    493					if (tmp & (1 << out)) {
    494						if (minfo->outputs[out].src != MATROXFB_SRC_CRTC2) {
    495							changes = 1;
    496							minfo->outputs[out].src = MATROXFB_SRC_CRTC2;
    497						}
    498					} else if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2) {
    499						changes = 1;
    500						minfo->outputs[out].src = MATROXFB_SRC_NONE;
    501					}
    502				}
    503				if (!changes)
    504					return 0;
    505				matroxfb_dh_set_par(info);
    506				return 0;
    507			}
    508		case MATROXFB_GET_OUTPUT_CONNECTION:
    509			{
    510				u_int32_t conn = 0;
    511				int out;
    512
    513				for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
    514					if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2) {
    515						conn |= 1 << out;
    516					}
    517				}
    518				if (put_user(conn, (u_int32_t __user *)arg))
    519					return -EFAULT;
    520				return 0;
    521			}
    522		case MATROXFB_GET_AVAILABLE_OUTPUTS:
    523			{
    524				u_int32_t tmp = 0;
    525				int out;
    526
    527				for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
    528					if (minfo->outputs[out].output) {
    529						switch (minfo->outputs[out].src) {
    530							case MATROXFB_SRC_NONE:
    531							case MATROXFB_SRC_CRTC2:
    532								tmp |= 1 << out;
    533								break;
    534						}
    535					}
    536				}
    537				if (minfo->devflags.panellink) {
    538					tmp &= ~MATROXFB_OUTPUT_CONN_DFP;
    539					if (minfo->outputs[2].src == MATROXFB_SRC_CRTC1) {
    540						tmp = 0;
    541					}
    542				}
    543				if (put_user(tmp, (u_int32_t __user *)arg))
    544					return -EFAULT;
    545				return 0;
    546			}
    547	}
    548	return -ENOTTY;
    549#undef m2info
    550}
    551
    552static int matroxfb_dh_blank(int blank, struct fb_info* info) {
    553#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
    554	switch (blank) {
    555		case 1:
    556		case 2:
    557		case 3:
    558		case 4:
    559		default:;
    560	}
    561	/* do something... */
    562	return 0;
    563#undef m2info
    564}
    565
    566static const struct fb_ops matroxfb_dh_ops = {
    567	.owner =	THIS_MODULE,
    568	.fb_open =	matroxfb_dh_open,
    569	.fb_release =	matroxfb_dh_release,
    570	.fb_check_var =	matroxfb_dh_check_var,
    571	.fb_set_par =	matroxfb_dh_set_par,
    572	.fb_setcolreg =	matroxfb_dh_setcolreg,
    573	.fb_pan_display =matroxfb_dh_pan_display,
    574	.fb_blank =	matroxfb_dh_blank,
    575	.fb_ioctl =	matroxfb_dh_ioctl,
    576	.fb_fillrect =	cfb_fillrect,
    577	.fb_copyarea =	cfb_copyarea,
    578	.fb_imageblit =	cfb_imageblit,
    579};
    580
    581static struct fb_var_screeninfo matroxfb_dh_defined = {
    582		640,480,640,480,/* W,H, virtual W,H */
    583		0,0,		/* offset */
    584		32,		/* depth */
    585		0,		/* gray */
    586		{0,0,0},	/* R */
    587		{0,0,0},	/* G */
    588		{0,0,0},	/* B */
    589		{0,0,0},	/* alpha */
    590		0,		/* nonstd */
    591		FB_ACTIVATE_NOW,
    592		-1,-1,		/* display size */
    593		0,		/* accel flags */
    594		39721L,48L,16L,33L,10L,
    595		96L,2,0,	/* no sync info */
    596		FB_VMODE_NONINTERLACED,
    597};
    598
    599static int matroxfb_dh_regit(const struct matrox_fb_info *minfo,
    600			     struct matroxfb_dh_fb_info *m2info)
    601{
    602#define minfo (m2info->primary_dev)
    603	void* oldcrtc2;
    604
    605	m2info->fbcon.fbops = &matroxfb_dh_ops;
    606	m2info->fbcon.flags = FBINFO_FLAG_DEFAULT;
    607	m2info->fbcon.flags |= FBINFO_HWACCEL_XPAN |
    608			       FBINFO_HWACCEL_YPAN;
    609	m2info->fbcon.pseudo_palette = m2info->cmap;
    610	fb_alloc_cmap(&m2info->fbcon.cmap, 256, 1);
    611
    612	if (mem < 64)
    613		mem *= 1024;
    614	if (mem < 64*1024)
    615		mem *= 1024;
    616	mem &= ~0x00000FFF;	/* PAGE_MASK? */
    617	if (minfo->video.len_usable + mem <= minfo->video.len)
    618		m2info->video.offbase = minfo->video.len - mem;
    619	else if (minfo->video.len < mem) {
    620		return -ENOMEM;
    621	} else { /* check yres on first head... */
    622		m2info->video.borrowed = mem;
    623		minfo->video.len_usable -= mem;
    624		m2info->video.offbase = minfo->video.len_usable;
    625	}
    626	m2info->video.base = minfo->video.base + m2info->video.offbase;
    627	m2info->video.len = m2info->video.len_usable = m2info->video.len_maximum = mem;
    628	m2info->video.vbase.vaddr = vaddr_va(minfo->video.vbase) + m2info->video.offbase;
    629	m2info->mmio.base = minfo->mmio.base;
    630	m2info->mmio.vbase = minfo->mmio.vbase;
    631	m2info->mmio.len = minfo->mmio.len;
    632
    633	matroxfb_dh_init_fix(m2info);
    634	if (register_framebuffer(&m2info->fbcon)) {
    635		return -ENXIO;
    636	}
    637	if (!m2info->initialized)
    638		fb_set_var(&m2info->fbcon, &matroxfb_dh_defined);
    639	down_write(&minfo->crtc2.lock);
    640	oldcrtc2 = minfo->crtc2.info;
    641	minfo->crtc2.info = m2info;
    642	up_write(&minfo->crtc2.lock);
    643	if (oldcrtc2) {
    644		printk(KERN_ERR "matroxfb_crtc2: Internal consistency check failed: crtc2 already present: %p\n",
    645			oldcrtc2);
    646	}
    647	return 0;
    648#undef minfo
    649}
    650
    651/* ************************** */
    652
    653static int matroxfb_dh_registerfb(struct matroxfb_dh_fb_info* m2info) {
    654#define minfo (m2info->primary_dev)
    655	if (matroxfb_dh_regit(minfo, m2info)) {
    656		printk(KERN_ERR "matroxfb_crtc2: secondary head failed to register\n");
    657		return -1;
    658	}
    659	printk(KERN_INFO "matroxfb_crtc2: secondary head of fb%u was registered as fb%u\n",
    660		minfo->fbcon.node, m2info->fbcon.node);
    661	m2info->fbcon_registered = 1;
    662	return 0;
    663#undef minfo
    664}
    665
    666static void matroxfb_dh_deregisterfb(struct matroxfb_dh_fb_info* m2info) {
    667#define minfo (m2info->primary_dev)
    668	if (m2info->fbcon_registered) {
    669		int id;
    670		struct matroxfb_dh_fb_info* crtc2;
    671
    672		down_write(&minfo->crtc2.lock);
    673		crtc2 = minfo->crtc2.info;
    674		if (crtc2 == m2info)
    675			minfo->crtc2.info = NULL;
    676		up_write(&minfo->crtc2.lock);
    677		if (crtc2 != m2info) {
    678			printk(KERN_ERR "matroxfb_crtc2: Internal consistency check failed: crtc2 mismatch at unload: %p != %p\n",
    679				crtc2, m2info);
    680			printk(KERN_ERR "matroxfb_crtc2: Expect kernel crash after module unload.\n");
    681			return;
    682		}
    683		id = m2info->fbcon.node;
    684		unregister_framebuffer(&m2info->fbcon);
    685		/* return memory back to primary head */
    686		minfo->video.len_usable += m2info->video.borrowed;
    687		printk(KERN_INFO "matroxfb_crtc2: fb%u unregistered\n", id);
    688		m2info->fbcon_registered = 0;
    689	}
    690#undef minfo
    691}
    692
    693static void* matroxfb_crtc2_probe(struct matrox_fb_info* minfo) {
    694	struct matroxfb_dh_fb_info* m2info;
    695
    696	/* hardware is CRTC2 incapable... */
    697	if (!minfo->devflags.crtc2)
    698		return NULL;
    699	m2info = kzalloc(sizeof(*m2info), GFP_KERNEL);
    700	if (!m2info)
    701		return NULL;
    702
    703	m2info->primary_dev = minfo;
    704	if (matroxfb_dh_registerfb(m2info)) {
    705		kfree(m2info);
    706		printk(KERN_ERR "matroxfb_crtc2: CRTC2 framebuffer failed to register\n");
    707		return NULL;
    708	}
    709	return m2info;
    710}
    711
    712static void matroxfb_crtc2_remove(struct matrox_fb_info* minfo, void* crtc2) {
    713	matroxfb_dh_deregisterfb(crtc2);
    714	kfree(crtc2);
    715}
    716
    717static struct matroxfb_driver crtc2 = {
    718		.name =		"Matrox G400 CRTC2",
    719		.probe =	matroxfb_crtc2_probe,
    720		.remove =	matroxfb_crtc2_remove };
    721
    722static int matroxfb_crtc2_init(void) {
    723	if (fb_get_options("matrox_crtc2fb", NULL))
    724		return -ENODEV;
    725
    726	matroxfb_register_driver(&crtc2);
    727	return 0;
    728}
    729
    730static void matroxfb_crtc2_exit(void) {
    731	matroxfb_unregister_driver(&crtc2);
    732}
    733
    734MODULE_AUTHOR("(c) 1999-2002 Petr Vandrovec <vandrove@vc.cvut.cz>");
    735MODULE_DESCRIPTION("Matrox G400 CRTC2 driver");
    736MODULE_LICENSE("GPL");
    737module_init(matroxfb_crtc2_init);
    738module_exit(matroxfb_crtc2_exit);
    739/* we do not have __setup() yet */