162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200, G400 and G450. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * (c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Portions Copyright (c) 2001 Matrox Graphics Inc. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Version: 1.65 2002/08/14 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "matroxfb_maven.h" 1562306a36Sopenharmony_ci#include "matroxfb_crtc2.h" 1662306a36Sopenharmony_ci#include "matroxfb_misc.h" 1762306a36Sopenharmony_ci#include "matroxfb_DAC1064.h" 1862306a36Sopenharmony_ci#include <linux/matroxfb.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include <linux/uaccess.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* **************************************************** */ 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic int mem = 8192; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cimodule_param(mem, int, 0); 2762306a36Sopenharmony_ciMODULE_PARM_DESC(mem, "Memory size reserved for dualhead (default=8MB)"); 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* **************************************************** */ 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic int matroxfb_dh_setcolreg(unsigned regno, unsigned red, unsigned green, 3262306a36Sopenharmony_ci unsigned blue, unsigned transp, struct fb_info* info) { 3362306a36Sopenharmony_ci u_int32_t col; 3462306a36Sopenharmony_ci#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci if (regno >= 16) 3762306a36Sopenharmony_ci return 1; 3862306a36Sopenharmony_ci if (m2info->fbcon.var.grayscale) { 3962306a36Sopenharmony_ci /* gray = 0.30*R + 0.59*G + 0.11*B */ 4062306a36Sopenharmony_ci red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8; 4162306a36Sopenharmony_ci } 4262306a36Sopenharmony_ci red = CNVT_TOHW(red, m2info->fbcon.var.red.length); 4362306a36Sopenharmony_ci green = CNVT_TOHW(green, m2info->fbcon.var.green.length); 4462306a36Sopenharmony_ci blue = CNVT_TOHW(blue, m2info->fbcon.var.blue.length); 4562306a36Sopenharmony_ci transp = CNVT_TOHW(transp, m2info->fbcon.var.transp.length); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci col = (red << m2info->fbcon.var.red.offset) | 4862306a36Sopenharmony_ci (green << m2info->fbcon.var.green.offset) | 4962306a36Sopenharmony_ci (blue << m2info->fbcon.var.blue.offset) | 5062306a36Sopenharmony_ci (transp << m2info->fbcon.var.transp.offset); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci switch (m2info->fbcon.var.bits_per_pixel) { 5362306a36Sopenharmony_ci case 16: 5462306a36Sopenharmony_ci m2info->cmap[regno] = col | (col << 16); 5562306a36Sopenharmony_ci break; 5662306a36Sopenharmony_ci case 32: 5762306a36Sopenharmony_ci m2info->cmap[regno] = col; 5862306a36Sopenharmony_ci break; 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci return 0; 6162306a36Sopenharmony_ci#undef m2info 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic void matroxfb_dh_restore(struct matroxfb_dh_fb_info* m2info, 6562306a36Sopenharmony_ci struct my_timming* mt, 6662306a36Sopenharmony_ci int mode, 6762306a36Sopenharmony_ci unsigned int pos) { 6862306a36Sopenharmony_ci u_int32_t tmp; 6962306a36Sopenharmony_ci u_int32_t datactl; 7062306a36Sopenharmony_ci struct matrox_fb_info *minfo = m2info->primary_dev; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci switch (mode) { 7362306a36Sopenharmony_ci case 15: 7462306a36Sopenharmony_ci tmp = 0x00200000; 7562306a36Sopenharmony_ci break; 7662306a36Sopenharmony_ci case 16: 7762306a36Sopenharmony_ci tmp = 0x00400000; 7862306a36Sopenharmony_ci break; 7962306a36Sopenharmony_ci/* case 32: */ 8062306a36Sopenharmony_ci default: 8162306a36Sopenharmony_ci tmp = 0x00800000; 8262306a36Sopenharmony_ci break; 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci tmp |= 0x00000001; /* enable CRTC2 */ 8562306a36Sopenharmony_ci datactl = 0; 8662306a36Sopenharmony_ci if (minfo->outputs[1].src == MATROXFB_SRC_CRTC2) { 8762306a36Sopenharmony_ci if (minfo->devflags.g450dac) { 8862306a36Sopenharmony_ci tmp |= 0x00000006; /* source from secondary pixel PLL */ 8962306a36Sopenharmony_ci /* no vidrst when in monitor mode */ 9062306a36Sopenharmony_ci if (minfo->outputs[1].mode != MATROXFB_OUTPUT_MODE_MONITOR) { 9162306a36Sopenharmony_ci tmp |= 0xC0001000; /* Enable H/V vidrst */ 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci } else { 9462306a36Sopenharmony_ci tmp |= 0x00000002; /* source from VDOCLK */ 9562306a36Sopenharmony_ci tmp |= 0xC0000000; /* enable vvidrst & hvidrst */ 9662306a36Sopenharmony_ci /* MGA TVO is our clock source */ 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci } else if (minfo->outputs[0].src == MATROXFB_SRC_CRTC2) { 9962306a36Sopenharmony_ci tmp |= 0x00000004; /* source from pixclock */ 10062306a36Sopenharmony_ci /* PIXPLL is our clock source */ 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci if (minfo->outputs[0].src == MATROXFB_SRC_CRTC2) { 10362306a36Sopenharmony_ci tmp |= 0x00100000; /* connect CRTC2 to DAC */ 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci if (mt->interlaced) { 10662306a36Sopenharmony_ci tmp |= 0x02000000; /* interlaced, second field is bigger, as G450 apparently ignores it */ 10762306a36Sopenharmony_ci mt->VDisplay >>= 1; 10862306a36Sopenharmony_ci mt->VSyncStart >>= 1; 10962306a36Sopenharmony_ci mt->VSyncEnd >>= 1; 11062306a36Sopenharmony_ci mt->VTotal >>= 1; 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci if ((mt->HTotal & 7) == 2) { 11362306a36Sopenharmony_ci datactl |= 0x00000010; 11462306a36Sopenharmony_ci mt->HTotal &= ~7; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci tmp |= 0x10000000; /* 0x10000000 is VIDRST polarity */ 11762306a36Sopenharmony_ci mga_outl(0x3C14, ((mt->HDisplay - 8) << 16) | (mt->HTotal - 8)); 11862306a36Sopenharmony_ci mga_outl(0x3C18, ((mt->HSyncEnd - 8) << 16) | (mt->HSyncStart - 8)); 11962306a36Sopenharmony_ci mga_outl(0x3C1C, ((mt->VDisplay - 1) << 16) | (mt->VTotal - 1)); 12062306a36Sopenharmony_ci mga_outl(0x3C20, ((mt->VSyncEnd - 1) << 16) | (mt->VSyncStart - 1)); 12162306a36Sopenharmony_ci mga_outl(0x3C24, ((mt->VSyncStart) << 16) | (mt->HSyncStart)); /* preload */ 12262306a36Sopenharmony_ci { 12362306a36Sopenharmony_ci u_int32_t linelen = m2info->fbcon.var.xres_virtual * (m2info->fbcon.var.bits_per_pixel >> 3); 12462306a36Sopenharmony_ci if (tmp & 0x02000000) { 12562306a36Sopenharmony_ci /* field #0 is smaller, so... */ 12662306a36Sopenharmony_ci mga_outl(0x3C2C, pos); /* field #1 vmemory start */ 12762306a36Sopenharmony_ci mga_outl(0x3C28, pos + linelen); /* field #0 vmemory start */ 12862306a36Sopenharmony_ci linelen <<= 1; 12962306a36Sopenharmony_ci m2info->interlaced = 1; 13062306a36Sopenharmony_ci } else { 13162306a36Sopenharmony_ci mga_outl(0x3C28, pos); /* vmemory start */ 13262306a36Sopenharmony_ci m2info->interlaced = 0; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci mga_outl(0x3C40, linelen); 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci mga_outl(0x3C4C, datactl); /* data control */ 13762306a36Sopenharmony_ci if (tmp & 0x02000000) { 13862306a36Sopenharmony_ci int i; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci mga_outl(0x3C10, tmp & ~0x02000000); 14162306a36Sopenharmony_ci for (i = 0; i < 2; i++) { 14262306a36Sopenharmony_ci unsigned int nl; 14362306a36Sopenharmony_ci unsigned int lastl = 0; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci while ((nl = mga_inl(0x3C48) & 0xFFF) >= lastl) { 14662306a36Sopenharmony_ci lastl = nl; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci mga_outl(0x3C10, tmp); 15162306a36Sopenharmony_ci minfo->hw.crtc2.ctl = tmp; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci tmp = mt->VDisplay << 16; /* line compare */ 15462306a36Sopenharmony_ci if (mt->sync & FB_SYNC_HOR_HIGH_ACT) 15562306a36Sopenharmony_ci tmp |= 0x00000100; 15662306a36Sopenharmony_ci if (mt->sync & FB_SYNC_VERT_HIGH_ACT) 15762306a36Sopenharmony_ci tmp |= 0x00000200; 15862306a36Sopenharmony_ci mga_outl(0x3C44, tmp); 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic void matroxfb_dh_disable(struct matroxfb_dh_fb_info* m2info) { 16262306a36Sopenharmony_ci struct matrox_fb_info *minfo = m2info->primary_dev; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci mga_outl(0x3C10, 0x00000004); /* disable CRTC2, CRTC1->DAC1, PLL as clock source */ 16562306a36Sopenharmony_ci minfo->hw.crtc2.ctl = 0x00000004; 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic void matroxfb_dh_pan_var(struct matroxfb_dh_fb_info* m2info, 16962306a36Sopenharmony_ci struct fb_var_screeninfo* var) { 17062306a36Sopenharmony_ci unsigned int pos; 17162306a36Sopenharmony_ci unsigned int linelen; 17262306a36Sopenharmony_ci unsigned int pixelsize; 17362306a36Sopenharmony_ci struct matrox_fb_info *minfo = m2info->primary_dev; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci m2info->fbcon.var.xoffset = var->xoffset; 17662306a36Sopenharmony_ci m2info->fbcon.var.yoffset = var->yoffset; 17762306a36Sopenharmony_ci pixelsize = m2info->fbcon.var.bits_per_pixel >> 3; 17862306a36Sopenharmony_ci linelen = m2info->fbcon.var.xres_virtual * pixelsize; 17962306a36Sopenharmony_ci pos = m2info->fbcon.var.yoffset * linelen + m2info->fbcon.var.xoffset * pixelsize; 18062306a36Sopenharmony_ci pos += m2info->video.offbase; 18162306a36Sopenharmony_ci if (m2info->interlaced) { 18262306a36Sopenharmony_ci mga_outl(0x3C2C, pos); 18362306a36Sopenharmony_ci mga_outl(0x3C28, pos + linelen); 18462306a36Sopenharmony_ci } else { 18562306a36Sopenharmony_ci mga_outl(0x3C28, pos); 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic int matroxfb_dh_decode_var(struct matroxfb_dh_fb_info* m2info, 19062306a36Sopenharmony_ci struct fb_var_screeninfo* var, 19162306a36Sopenharmony_ci int *visual, 19262306a36Sopenharmony_ci int *video_cmap_len, 19362306a36Sopenharmony_ci int *mode) { 19462306a36Sopenharmony_ci unsigned int mask; 19562306a36Sopenharmony_ci unsigned int memlen; 19662306a36Sopenharmony_ci unsigned int vramlen; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci switch (var->bits_per_pixel) { 19962306a36Sopenharmony_ci case 16: mask = 0x1F; 20062306a36Sopenharmony_ci break; 20162306a36Sopenharmony_ci case 32: mask = 0x0F; 20262306a36Sopenharmony_ci break; 20362306a36Sopenharmony_ci default: return -EINVAL; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci vramlen = m2info->video.len_usable; 20662306a36Sopenharmony_ci if (var->yres_virtual < var->yres) 20762306a36Sopenharmony_ci var->yres_virtual = var->yres; 20862306a36Sopenharmony_ci if (var->xres_virtual < var->xres) 20962306a36Sopenharmony_ci var->xres_virtual = var->xres; 21062306a36Sopenharmony_ci var->xres_virtual = (var->xres_virtual + mask) & ~mask; 21162306a36Sopenharmony_ci if (var->yres_virtual > 32767) 21262306a36Sopenharmony_ci return -EINVAL; 21362306a36Sopenharmony_ci memlen = var->xres_virtual * var->yres_virtual * (var->bits_per_pixel >> 3); 21462306a36Sopenharmony_ci if (memlen > vramlen) 21562306a36Sopenharmony_ci return -EINVAL; 21662306a36Sopenharmony_ci if (var->xoffset + var->xres > var->xres_virtual) 21762306a36Sopenharmony_ci var->xoffset = var->xres_virtual - var->xres; 21862306a36Sopenharmony_ci if (var->yoffset + var->yres > var->yres_virtual) 21962306a36Sopenharmony_ci var->yoffset = var->yres_virtual - var->yres; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci var->xres &= ~7; 22262306a36Sopenharmony_ci var->left_margin &= ~7; 22362306a36Sopenharmony_ci var->right_margin &= ~7; 22462306a36Sopenharmony_ci var->hsync_len &= ~7; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci *mode = var->bits_per_pixel; 22762306a36Sopenharmony_ci if (var->bits_per_pixel == 16) { 22862306a36Sopenharmony_ci if (var->green.length == 5) { 22962306a36Sopenharmony_ci var->red.offset = 10; 23062306a36Sopenharmony_ci var->red.length = 5; 23162306a36Sopenharmony_ci var->green.offset = 5; 23262306a36Sopenharmony_ci var->green.length = 5; 23362306a36Sopenharmony_ci var->blue.offset = 0; 23462306a36Sopenharmony_ci var->blue.length = 5; 23562306a36Sopenharmony_ci var->transp.offset = 15; 23662306a36Sopenharmony_ci var->transp.length = 1; 23762306a36Sopenharmony_ci *mode = 15; 23862306a36Sopenharmony_ci } else { 23962306a36Sopenharmony_ci var->red.offset = 11; 24062306a36Sopenharmony_ci var->red.length = 5; 24162306a36Sopenharmony_ci var->green.offset = 5; 24262306a36Sopenharmony_ci var->green.length = 6; 24362306a36Sopenharmony_ci var->blue.offset = 0; 24462306a36Sopenharmony_ci var->blue.length = 5; 24562306a36Sopenharmony_ci var->transp.offset = 0; 24662306a36Sopenharmony_ci var->transp.length = 0; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci } else { 24962306a36Sopenharmony_ci var->red.offset = 16; 25062306a36Sopenharmony_ci var->red.length = 8; 25162306a36Sopenharmony_ci var->green.offset = 8; 25262306a36Sopenharmony_ci var->green.length = 8; 25362306a36Sopenharmony_ci var->blue.offset = 0; 25462306a36Sopenharmony_ci var->blue.length = 8; 25562306a36Sopenharmony_ci var->transp.offset = 24; 25662306a36Sopenharmony_ci var->transp.length = 8; 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci *visual = FB_VISUAL_TRUECOLOR; 25962306a36Sopenharmony_ci *video_cmap_len = 16; 26062306a36Sopenharmony_ci return 0; 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic int matroxfb_dh_open(struct fb_info* info, int user) { 26462306a36Sopenharmony_ci#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) 26562306a36Sopenharmony_ci struct matrox_fb_info *minfo = m2info->primary_dev; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (minfo) { 26862306a36Sopenharmony_ci int err; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci if (minfo->dead) { 27162306a36Sopenharmony_ci return -ENXIO; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci err = minfo->fbops.fb_open(&minfo->fbcon, user); 27462306a36Sopenharmony_ci if (err) { 27562306a36Sopenharmony_ci return err; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci return 0; 27962306a36Sopenharmony_ci#undef m2info 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistatic int matroxfb_dh_release(struct fb_info* info, int user) { 28362306a36Sopenharmony_ci#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) 28462306a36Sopenharmony_ci int err = 0; 28562306a36Sopenharmony_ci struct matrox_fb_info *minfo = m2info->primary_dev; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci if (minfo) { 28862306a36Sopenharmony_ci err = minfo->fbops.fb_release(&minfo->fbcon, user); 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci return err; 29162306a36Sopenharmony_ci#undef m2info 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci/* 29562306a36Sopenharmony_ci * This function is called before the register_framebuffer so 29662306a36Sopenharmony_ci * no locking is needed. 29762306a36Sopenharmony_ci */ 29862306a36Sopenharmony_cistatic void matroxfb_dh_init_fix(struct matroxfb_dh_fb_info *m2info) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci struct fb_fix_screeninfo *fix = &m2info->fbcon.fix; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci strcpy(fix->id, "MATROX DH"); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci fix->smem_start = m2info->video.base; 30562306a36Sopenharmony_ci fix->smem_len = m2info->video.len_usable; 30662306a36Sopenharmony_ci fix->ypanstep = 1; 30762306a36Sopenharmony_ci fix->ywrapstep = 0; 30862306a36Sopenharmony_ci fix->xpanstep = 8; /* TBD */ 30962306a36Sopenharmony_ci fix->mmio_start = m2info->mmio.base; 31062306a36Sopenharmony_ci fix->mmio_len = m2info->mmio.len; 31162306a36Sopenharmony_ci fix->accel = 0; /* no accel... */ 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic int matroxfb_dh_check_var(struct fb_var_screeninfo* var, struct fb_info* info) { 31562306a36Sopenharmony_ci#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) 31662306a36Sopenharmony_ci int visual; 31762306a36Sopenharmony_ci int cmap_len; 31862306a36Sopenharmony_ci int mode; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci return matroxfb_dh_decode_var(m2info, var, &visual, &cmap_len, &mode); 32162306a36Sopenharmony_ci#undef m2info 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cistatic int matroxfb_dh_set_par(struct fb_info* info) { 32562306a36Sopenharmony_ci#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) 32662306a36Sopenharmony_ci int visual; 32762306a36Sopenharmony_ci int cmap_len; 32862306a36Sopenharmony_ci int mode; 32962306a36Sopenharmony_ci int err; 33062306a36Sopenharmony_ci struct fb_var_screeninfo* var = &info->var; 33162306a36Sopenharmony_ci struct matrox_fb_info *minfo = m2info->primary_dev; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if ((err = matroxfb_dh_decode_var(m2info, var, &visual, &cmap_len, &mode)) != 0) 33462306a36Sopenharmony_ci return err; 33562306a36Sopenharmony_ci /* cmap */ 33662306a36Sopenharmony_ci { 33762306a36Sopenharmony_ci m2info->fbcon.screen_base = vaddr_va(m2info->video.vbase); 33862306a36Sopenharmony_ci m2info->fbcon.fix.visual = visual; 33962306a36Sopenharmony_ci m2info->fbcon.fix.type = FB_TYPE_PACKED_PIXELS; 34062306a36Sopenharmony_ci m2info->fbcon.fix.type_aux = 0; 34162306a36Sopenharmony_ci m2info->fbcon.fix.line_length = (var->xres_virtual * var->bits_per_pixel) >> 3; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci { 34462306a36Sopenharmony_ci struct my_timming mt; 34562306a36Sopenharmony_ci unsigned int pos; 34662306a36Sopenharmony_ci int out; 34762306a36Sopenharmony_ci int cnt; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci matroxfb_var2my(&m2info->fbcon.var, &mt); 35062306a36Sopenharmony_ci mt.crtc = MATROXFB_SRC_CRTC2; 35162306a36Sopenharmony_ci /* CRTC2 delay */ 35262306a36Sopenharmony_ci mt.delay = 34; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci pos = (m2info->fbcon.var.yoffset * m2info->fbcon.var.xres_virtual + m2info->fbcon.var.xoffset) * m2info->fbcon.var.bits_per_pixel >> 3; 35562306a36Sopenharmony_ci pos += m2info->video.offbase; 35662306a36Sopenharmony_ci cnt = 0; 35762306a36Sopenharmony_ci down_read(&minfo->altout.lock); 35862306a36Sopenharmony_ci for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { 35962306a36Sopenharmony_ci if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2) { 36062306a36Sopenharmony_ci cnt++; 36162306a36Sopenharmony_ci if (minfo->outputs[out].output->compute) { 36262306a36Sopenharmony_ci minfo->outputs[out].output->compute(minfo->outputs[out].data, &mt); 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci minfo->crtc2.pixclock = mt.pixclock; 36762306a36Sopenharmony_ci minfo->crtc2.mnp = mt.mnp; 36862306a36Sopenharmony_ci up_read(&minfo->altout.lock); 36962306a36Sopenharmony_ci if (cnt) { 37062306a36Sopenharmony_ci matroxfb_dh_restore(m2info, &mt, mode, pos); 37162306a36Sopenharmony_ci } else { 37262306a36Sopenharmony_ci matroxfb_dh_disable(m2info); 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci DAC1064_global_init(minfo); 37562306a36Sopenharmony_ci DAC1064_global_restore(minfo); 37662306a36Sopenharmony_ci down_read(&minfo->altout.lock); 37762306a36Sopenharmony_ci for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { 37862306a36Sopenharmony_ci if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2 && 37962306a36Sopenharmony_ci minfo->outputs[out].output->program) { 38062306a36Sopenharmony_ci minfo->outputs[out].output->program(minfo->outputs[out].data); 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { 38462306a36Sopenharmony_ci if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2 && 38562306a36Sopenharmony_ci minfo->outputs[out].output->start) { 38662306a36Sopenharmony_ci minfo->outputs[out].output->start(minfo->outputs[out].data); 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci up_read(&minfo->altout.lock); 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci m2info->initialized = 1; 39262306a36Sopenharmony_ci return 0; 39362306a36Sopenharmony_ci#undef m2info 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_cistatic int matroxfb_dh_pan_display(struct fb_var_screeninfo* var, struct fb_info* info) { 39762306a36Sopenharmony_ci#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) 39862306a36Sopenharmony_ci matroxfb_dh_pan_var(m2info, var); 39962306a36Sopenharmony_ci return 0; 40062306a36Sopenharmony_ci#undef m2info 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cistatic int matroxfb_dh_get_vblank(const struct matroxfb_dh_fb_info* m2info, struct fb_vblank* vblank) { 40462306a36Sopenharmony_ci struct matrox_fb_info *minfo = m2info->primary_dev; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci matroxfb_enable_irq(minfo, 0); 40762306a36Sopenharmony_ci memset(vblank, 0, sizeof(*vblank)); 40862306a36Sopenharmony_ci vblank->flags = FB_VBLANK_HAVE_VCOUNT | FB_VBLANK_HAVE_VBLANK; 40962306a36Sopenharmony_ci /* mask out reserved bits + field number (odd/even) */ 41062306a36Sopenharmony_ci vblank->vcount = mga_inl(0x3C48) & 0x000007FF; 41162306a36Sopenharmony_ci /* compatibility stuff */ 41262306a36Sopenharmony_ci if (vblank->vcount >= m2info->fbcon.var.yres) 41362306a36Sopenharmony_ci vblank->flags |= FB_VBLANK_VBLANKING; 41462306a36Sopenharmony_ci if (test_bit(0, &minfo->irq_flags)) { 41562306a36Sopenharmony_ci vblank->flags |= FB_VBLANK_HAVE_COUNT; 41662306a36Sopenharmony_ci /* Only one writer, aligned int value... 41762306a36Sopenharmony_ci it should work without lock and without atomic_t */ 41862306a36Sopenharmony_ci vblank->count = minfo->crtc2.vsync.cnt; 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci return 0; 42162306a36Sopenharmony_ci} 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_cistatic int matroxfb_dh_ioctl(struct fb_info *info, 42462306a36Sopenharmony_ci unsigned int cmd, 42562306a36Sopenharmony_ci unsigned long arg) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) 42862306a36Sopenharmony_ci struct matrox_fb_info *minfo = m2info->primary_dev; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci DBG(__func__) 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci switch (cmd) { 43362306a36Sopenharmony_ci case FBIOGET_VBLANK: 43462306a36Sopenharmony_ci { 43562306a36Sopenharmony_ci struct fb_vblank vblank; 43662306a36Sopenharmony_ci int err; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci err = matroxfb_dh_get_vblank(m2info, &vblank); 43962306a36Sopenharmony_ci if (err) 44062306a36Sopenharmony_ci return err; 44162306a36Sopenharmony_ci if (copy_to_user((void __user *)arg, &vblank, sizeof(vblank))) 44262306a36Sopenharmony_ci return -EFAULT; 44362306a36Sopenharmony_ci return 0; 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci case FBIO_WAITFORVSYNC: 44662306a36Sopenharmony_ci { 44762306a36Sopenharmony_ci u_int32_t crt; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci if (get_user(crt, (u_int32_t __user *)arg)) 45062306a36Sopenharmony_ci return -EFAULT; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci if (crt != 0) 45362306a36Sopenharmony_ci return -ENODEV; 45462306a36Sopenharmony_ci return matroxfb_wait_for_sync(minfo, 1); 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci case MATROXFB_SET_OUTPUT_MODE: 45762306a36Sopenharmony_ci case MATROXFB_GET_OUTPUT_MODE: 45862306a36Sopenharmony_ci case MATROXFB_GET_ALL_OUTPUTS: 45962306a36Sopenharmony_ci { 46062306a36Sopenharmony_ci return minfo->fbcon.fbops->fb_ioctl(&minfo->fbcon, cmd, arg); 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci case MATROXFB_SET_OUTPUT_CONNECTION: 46362306a36Sopenharmony_ci { 46462306a36Sopenharmony_ci u_int32_t tmp; 46562306a36Sopenharmony_ci int out; 46662306a36Sopenharmony_ci int changes; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci if (get_user(tmp, (u_int32_t __user *)arg)) 46962306a36Sopenharmony_ci return -EFAULT; 47062306a36Sopenharmony_ci for (out = 0; out < 32; out++) { 47162306a36Sopenharmony_ci if (tmp & (1 << out)) { 47262306a36Sopenharmony_ci if (out >= MATROXFB_MAX_OUTPUTS) 47362306a36Sopenharmony_ci return -ENXIO; 47462306a36Sopenharmony_ci if (!minfo->outputs[out].output) 47562306a36Sopenharmony_ci return -ENXIO; 47662306a36Sopenharmony_ci switch (minfo->outputs[out].src) { 47762306a36Sopenharmony_ci case MATROXFB_SRC_NONE: 47862306a36Sopenharmony_ci case MATROXFB_SRC_CRTC2: 47962306a36Sopenharmony_ci break; 48062306a36Sopenharmony_ci default: 48162306a36Sopenharmony_ci return -EBUSY; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci if (minfo->devflags.panellink) { 48662306a36Sopenharmony_ci if (tmp & MATROXFB_OUTPUT_CONN_DFP) 48762306a36Sopenharmony_ci return -EINVAL; 48862306a36Sopenharmony_ci if ((minfo->outputs[2].src == MATROXFB_SRC_CRTC1) && tmp) 48962306a36Sopenharmony_ci return -EBUSY; 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci changes = 0; 49262306a36Sopenharmony_ci for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { 49362306a36Sopenharmony_ci if (tmp & (1 << out)) { 49462306a36Sopenharmony_ci if (minfo->outputs[out].src != MATROXFB_SRC_CRTC2) { 49562306a36Sopenharmony_ci changes = 1; 49662306a36Sopenharmony_ci minfo->outputs[out].src = MATROXFB_SRC_CRTC2; 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci } else if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2) { 49962306a36Sopenharmony_ci changes = 1; 50062306a36Sopenharmony_ci minfo->outputs[out].src = MATROXFB_SRC_NONE; 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci if (!changes) 50462306a36Sopenharmony_ci return 0; 50562306a36Sopenharmony_ci matroxfb_dh_set_par(info); 50662306a36Sopenharmony_ci return 0; 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci case MATROXFB_GET_OUTPUT_CONNECTION: 50962306a36Sopenharmony_ci { 51062306a36Sopenharmony_ci u_int32_t conn = 0; 51162306a36Sopenharmony_ci int out; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { 51462306a36Sopenharmony_ci if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2) { 51562306a36Sopenharmony_ci conn |= 1 << out; 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci if (put_user(conn, (u_int32_t __user *)arg)) 51962306a36Sopenharmony_ci return -EFAULT; 52062306a36Sopenharmony_ci return 0; 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci case MATROXFB_GET_AVAILABLE_OUTPUTS: 52362306a36Sopenharmony_ci { 52462306a36Sopenharmony_ci u_int32_t tmp = 0; 52562306a36Sopenharmony_ci int out; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { 52862306a36Sopenharmony_ci if (minfo->outputs[out].output) { 52962306a36Sopenharmony_ci switch (minfo->outputs[out].src) { 53062306a36Sopenharmony_ci case MATROXFB_SRC_NONE: 53162306a36Sopenharmony_ci case MATROXFB_SRC_CRTC2: 53262306a36Sopenharmony_ci tmp |= 1 << out; 53362306a36Sopenharmony_ci break; 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci } 53762306a36Sopenharmony_ci if (minfo->devflags.panellink) { 53862306a36Sopenharmony_ci tmp &= ~MATROXFB_OUTPUT_CONN_DFP; 53962306a36Sopenharmony_ci if (minfo->outputs[2].src == MATROXFB_SRC_CRTC1) { 54062306a36Sopenharmony_ci tmp = 0; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci if (put_user(tmp, (u_int32_t __user *)arg)) 54462306a36Sopenharmony_ci return -EFAULT; 54562306a36Sopenharmony_ci return 0; 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci return -ENOTTY; 54962306a36Sopenharmony_ci#undef m2info 55062306a36Sopenharmony_ci} 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_cistatic int matroxfb_dh_blank(int blank, struct fb_info* info) { 55362306a36Sopenharmony_ci#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) 55462306a36Sopenharmony_ci switch (blank) { 55562306a36Sopenharmony_ci case 1: 55662306a36Sopenharmony_ci case 2: 55762306a36Sopenharmony_ci case 3: 55862306a36Sopenharmony_ci case 4: 55962306a36Sopenharmony_ci default:; 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci /* do something... */ 56262306a36Sopenharmony_ci return 0; 56362306a36Sopenharmony_ci#undef m2info 56462306a36Sopenharmony_ci} 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_cistatic const struct fb_ops matroxfb_dh_ops = { 56762306a36Sopenharmony_ci .owner = THIS_MODULE, 56862306a36Sopenharmony_ci .fb_open = matroxfb_dh_open, 56962306a36Sopenharmony_ci .fb_release = matroxfb_dh_release, 57062306a36Sopenharmony_ci .fb_check_var = matroxfb_dh_check_var, 57162306a36Sopenharmony_ci .fb_set_par = matroxfb_dh_set_par, 57262306a36Sopenharmony_ci .fb_setcolreg = matroxfb_dh_setcolreg, 57362306a36Sopenharmony_ci .fb_pan_display =matroxfb_dh_pan_display, 57462306a36Sopenharmony_ci .fb_blank = matroxfb_dh_blank, 57562306a36Sopenharmony_ci .fb_ioctl = matroxfb_dh_ioctl, 57662306a36Sopenharmony_ci .fb_fillrect = cfb_fillrect, 57762306a36Sopenharmony_ci .fb_copyarea = cfb_copyarea, 57862306a36Sopenharmony_ci .fb_imageblit = cfb_imageblit, 57962306a36Sopenharmony_ci}; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_cistatic struct fb_var_screeninfo matroxfb_dh_defined = { 58262306a36Sopenharmony_ci 640,480,640,480,/* W,H, virtual W,H */ 58362306a36Sopenharmony_ci 0,0, /* offset */ 58462306a36Sopenharmony_ci 32, /* depth */ 58562306a36Sopenharmony_ci 0, /* gray */ 58662306a36Sopenharmony_ci {0,0,0}, /* R */ 58762306a36Sopenharmony_ci {0,0,0}, /* G */ 58862306a36Sopenharmony_ci {0,0,0}, /* B */ 58962306a36Sopenharmony_ci {0,0,0}, /* alpha */ 59062306a36Sopenharmony_ci 0, /* nonstd */ 59162306a36Sopenharmony_ci FB_ACTIVATE_NOW, 59262306a36Sopenharmony_ci -1,-1, /* display size */ 59362306a36Sopenharmony_ci 0, /* accel flags */ 59462306a36Sopenharmony_ci 39721L,48L,16L,33L,10L, 59562306a36Sopenharmony_ci 96L,2,0, /* no sync info */ 59662306a36Sopenharmony_ci FB_VMODE_NONINTERLACED, 59762306a36Sopenharmony_ci}; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_cistatic int matroxfb_dh_regit(const struct matrox_fb_info *minfo, 60062306a36Sopenharmony_ci struct matroxfb_dh_fb_info *m2info) 60162306a36Sopenharmony_ci{ 60262306a36Sopenharmony_ci#define minfo (m2info->primary_dev) 60362306a36Sopenharmony_ci void* oldcrtc2; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci m2info->fbcon.fbops = &matroxfb_dh_ops; 60662306a36Sopenharmony_ci m2info->fbcon.flags = FBINFO_HWACCEL_XPAN | 60762306a36Sopenharmony_ci FBINFO_HWACCEL_YPAN; 60862306a36Sopenharmony_ci m2info->fbcon.pseudo_palette = m2info->cmap; 60962306a36Sopenharmony_ci fb_alloc_cmap(&m2info->fbcon.cmap, 256, 1); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci if (mem < 64) 61262306a36Sopenharmony_ci mem *= 1024; 61362306a36Sopenharmony_ci if (mem < 64*1024) 61462306a36Sopenharmony_ci mem *= 1024; 61562306a36Sopenharmony_ci mem &= ~0x00000FFF; /* PAGE_MASK? */ 61662306a36Sopenharmony_ci if (minfo->video.len_usable + mem <= minfo->video.len) 61762306a36Sopenharmony_ci m2info->video.offbase = minfo->video.len - mem; 61862306a36Sopenharmony_ci else if (minfo->video.len < mem) { 61962306a36Sopenharmony_ci return -ENOMEM; 62062306a36Sopenharmony_ci } else { /* check yres on first head... */ 62162306a36Sopenharmony_ci m2info->video.borrowed = mem; 62262306a36Sopenharmony_ci minfo->video.len_usable -= mem; 62362306a36Sopenharmony_ci m2info->video.offbase = minfo->video.len_usable; 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci m2info->video.base = minfo->video.base + m2info->video.offbase; 62662306a36Sopenharmony_ci m2info->video.len = m2info->video.len_usable = m2info->video.len_maximum = mem; 62762306a36Sopenharmony_ci m2info->video.vbase.vaddr = vaddr_va(minfo->video.vbase) + m2info->video.offbase; 62862306a36Sopenharmony_ci m2info->mmio.base = minfo->mmio.base; 62962306a36Sopenharmony_ci m2info->mmio.vbase = minfo->mmio.vbase; 63062306a36Sopenharmony_ci m2info->mmio.len = minfo->mmio.len; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci matroxfb_dh_init_fix(m2info); 63362306a36Sopenharmony_ci if (register_framebuffer(&m2info->fbcon)) { 63462306a36Sopenharmony_ci return -ENXIO; 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci if (!m2info->initialized) 63762306a36Sopenharmony_ci fb_set_var(&m2info->fbcon, &matroxfb_dh_defined); 63862306a36Sopenharmony_ci down_write(&minfo->crtc2.lock); 63962306a36Sopenharmony_ci oldcrtc2 = minfo->crtc2.info; 64062306a36Sopenharmony_ci minfo->crtc2.info = m2info; 64162306a36Sopenharmony_ci up_write(&minfo->crtc2.lock); 64262306a36Sopenharmony_ci if (oldcrtc2) { 64362306a36Sopenharmony_ci printk(KERN_ERR "matroxfb_crtc2: Internal consistency check failed: crtc2 already present: %p\n", 64462306a36Sopenharmony_ci oldcrtc2); 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci return 0; 64762306a36Sopenharmony_ci#undef minfo 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci/* ************************** */ 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_cistatic int matroxfb_dh_registerfb(struct matroxfb_dh_fb_info* m2info) { 65362306a36Sopenharmony_ci#define minfo (m2info->primary_dev) 65462306a36Sopenharmony_ci if (matroxfb_dh_regit(minfo, m2info)) { 65562306a36Sopenharmony_ci printk(KERN_ERR "matroxfb_crtc2: secondary head failed to register\n"); 65662306a36Sopenharmony_ci return -1; 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci printk(KERN_INFO "matroxfb_crtc2: secondary head of fb%u was registered as fb%u\n", 65962306a36Sopenharmony_ci minfo->fbcon.node, m2info->fbcon.node); 66062306a36Sopenharmony_ci m2info->fbcon_registered = 1; 66162306a36Sopenharmony_ci return 0; 66262306a36Sopenharmony_ci#undef minfo 66362306a36Sopenharmony_ci} 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_cistatic void matroxfb_dh_deregisterfb(struct matroxfb_dh_fb_info* m2info) { 66662306a36Sopenharmony_ci#define minfo (m2info->primary_dev) 66762306a36Sopenharmony_ci if (m2info->fbcon_registered) { 66862306a36Sopenharmony_ci int id; 66962306a36Sopenharmony_ci struct matroxfb_dh_fb_info* crtc2; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci down_write(&minfo->crtc2.lock); 67262306a36Sopenharmony_ci crtc2 = minfo->crtc2.info; 67362306a36Sopenharmony_ci if (crtc2 == m2info) 67462306a36Sopenharmony_ci minfo->crtc2.info = NULL; 67562306a36Sopenharmony_ci up_write(&minfo->crtc2.lock); 67662306a36Sopenharmony_ci if (crtc2 != m2info) { 67762306a36Sopenharmony_ci printk(KERN_ERR "matroxfb_crtc2: Internal consistency check failed: crtc2 mismatch at unload: %p != %p\n", 67862306a36Sopenharmony_ci crtc2, m2info); 67962306a36Sopenharmony_ci printk(KERN_ERR "matroxfb_crtc2: Expect kernel crash after module unload.\n"); 68062306a36Sopenharmony_ci return; 68162306a36Sopenharmony_ci } 68262306a36Sopenharmony_ci id = m2info->fbcon.node; 68362306a36Sopenharmony_ci unregister_framebuffer(&m2info->fbcon); 68462306a36Sopenharmony_ci /* return memory back to primary head */ 68562306a36Sopenharmony_ci minfo->video.len_usable += m2info->video.borrowed; 68662306a36Sopenharmony_ci printk(KERN_INFO "matroxfb_crtc2: fb%u unregistered\n", id); 68762306a36Sopenharmony_ci m2info->fbcon_registered = 0; 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci#undef minfo 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_cistatic void* matroxfb_crtc2_probe(struct matrox_fb_info* minfo) { 69362306a36Sopenharmony_ci struct matroxfb_dh_fb_info* m2info; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci /* hardware is CRTC2 incapable... */ 69662306a36Sopenharmony_ci if (!minfo->devflags.crtc2) 69762306a36Sopenharmony_ci return NULL; 69862306a36Sopenharmony_ci m2info = kzalloc(sizeof(*m2info), GFP_KERNEL); 69962306a36Sopenharmony_ci if (!m2info) 70062306a36Sopenharmony_ci return NULL; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci m2info->primary_dev = minfo; 70362306a36Sopenharmony_ci if (matroxfb_dh_registerfb(m2info)) { 70462306a36Sopenharmony_ci kfree(m2info); 70562306a36Sopenharmony_ci printk(KERN_ERR "matroxfb_crtc2: CRTC2 framebuffer failed to register\n"); 70662306a36Sopenharmony_ci return NULL; 70762306a36Sopenharmony_ci } 70862306a36Sopenharmony_ci return m2info; 70962306a36Sopenharmony_ci} 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_cistatic void matroxfb_crtc2_remove(struct matrox_fb_info* minfo, void* crtc2) { 71262306a36Sopenharmony_ci matroxfb_dh_deregisterfb(crtc2); 71362306a36Sopenharmony_ci kfree(crtc2); 71462306a36Sopenharmony_ci} 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_cistatic struct matroxfb_driver crtc2 = { 71762306a36Sopenharmony_ci .name = "Matrox G400 CRTC2", 71862306a36Sopenharmony_ci .probe = matroxfb_crtc2_probe, 71962306a36Sopenharmony_ci .remove = matroxfb_crtc2_remove }; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_cistatic int matroxfb_crtc2_init(void) { 72262306a36Sopenharmony_ci if (fb_get_options("matrox_crtc2fb", NULL)) 72362306a36Sopenharmony_ci return -ENODEV; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci matroxfb_register_driver(&crtc2); 72662306a36Sopenharmony_ci return 0; 72762306a36Sopenharmony_ci} 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_cistatic void matroxfb_crtc2_exit(void) { 73062306a36Sopenharmony_ci matroxfb_unregister_driver(&crtc2); 73162306a36Sopenharmony_ci} 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ciMODULE_AUTHOR("(c) 1999-2002 Petr Vandrovec <vandrove@vc.cvut.cz>"); 73462306a36Sopenharmony_ciMODULE_DESCRIPTION("Matrox G400 CRTC2 driver"); 73562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 73662306a36Sopenharmony_cimodule_init(matroxfb_crtc2_init); 73762306a36Sopenharmony_cimodule_exit(matroxfb_crtc2_exit); 73862306a36Sopenharmony_ci/* we do not have __setup() yet */ 739