18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200, G400 and G450. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * (c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Portions Copyright (c) 2001 Matrox Graphics Inc. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Version: 1.65 2002/08/14 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "matroxfb_maven.h" 158c2ecf20Sopenharmony_ci#include "matroxfb_crtc2.h" 168c2ecf20Sopenharmony_ci#include "matroxfb_misc.h" 178c2ecf20Sopenharmony_ci#include "matroxfb_DAC1064.h" 188c2ecf20Sopenharmony_ci#include <linux/matroxfb.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* **************************************************** */ 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic int mem = 8192; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cimodule_param(mem, int, 0); 278c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mem, "Memory size reserved for dualhead (default=8MB)"); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* **************************************************** */ 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic int matroxfb_dh_setcolreg(unsigned regno, unsigned red, unsigned green, 328c2ecf20Sopenharmony_ci unsigned blue, unsigned transp, struct fb_info* info) { 338c2ecf20Sopenharmony_ci u_int32_t col; 348c2ecf20Sopenharmony_ci#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci if (regno >= 16) 378c2ecf20Sopenharmony_ci return 1; 388c2ecf20Sopenharmony_ci if (m2info->fbcon.var.grayscale) { 398c2ecf20Sopenharmony_ci /* gray = 0.30*R + 0.59*G + 0.11*B */ 408c2ecf20Sopenharmony_ci red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8; 418c2ecf20Sopenharmony_ci } 428c2ecf20Sopenharmony_ci red = CNVT_TOHW(red, m2info->fbcon.var.red.length); 438c2ecf20Sopenharmony_ci green = CNVT_TOHW(green, m2info->fbcon.var.green.length); 448c2ecf20Sopenharmony_ci blue = CNVT_TOHW(blue, m2info->fbcon.var.blue.length); 458c2ecf20Sopenharmony_ci transp = CNVT_TOHW(transp, m2info->fbcon.var.transp.length); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci col = (red << m2info->fbcon.var.red.offset) | 488c2ecf20Sopenharmony_ci (green << m2info->fbcon.var.green.offset) | 498c2ecf20Sopenharmony_ci (blue << m2info->fbcon.var.blue.offset) | 508c2ecf20Sopenharmony_ci (transp << m2info->fbcon.var.transp.offset); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci switch (m2info->fbcon.var.bits_per_pixel) { 538c2ecf20Sopenharmony_ci case 16: 548c2ecf20Sopenharmony_ci m2info->cmap[regno] = col | (col << 16); 558c2ecf20Sopenharmony_ci break; 568c2ecf20Sopenharmony_ci case 32: 578c2ecf20Sopenharmony_ci m2info->cmap[regno] = col; 588c2ecf20Sopenharmony_ci break; 598c2ecf20Sopenharmony_ci } 608c2ecf20Sopenharmony_ci return 0; 618c2ecf20Sopenharmony_ci#undef m2info 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic void matroxfb_dh_restore(struct matroxfb_dh_fb_info* m2info, 658c2ecf20Sopenharmony_ci struct my_timming* mt, 668c2ecf20Sopenharmony_ci int mode, 678c2ecf20Sopenharmony_ci unsigned int pos) { 688c2ecf20Sopenharmony_ci u_int32_t tmp; 698c2ecf20Sopenharmony_ci u_int32_t datactl; 708c2ecf20Sopenharmony_ci struct matrox_fb_info *minfo = m2info->primary_dev; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci switch (mode) { 738c2ecf20Sopenharmony_ci case 15: 748c2ecf20Sopenharmony_ci tmp = 0x00200000; 758c2ecf20Sopenharmony_ci break; 768c2ecf20Sopenharmony_ci case 16: 778c2ecf20Sopenharmony_ci tmp = 0x00400000; 788c2ecf20Sopenharmony_ci break; 798c2ecf20Sopenharmony_ci/* case 32: */ 808c2ecf20Sopenharmony_ci default: 818c2ecf20Sopenharmony_ci tmp = 0x00800000; 828c2ecf20Sopenharmony_ci break; 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci tmp |= 0x00000001; /* enable CRTC2 */ 858c2ecf20Sopenharmony_ci datactl = 0; 868c2ecf20Sopenharmony_ci if (minfo->outputs[1].src == MATROXFB_SRC_CRTC2) { 878c2ecf20Sopenharmony_ci if (minfo->devflags.g450dac) { 888c2ecf20Sopenharmony_ci tmp |= 0x00000006; /* source from secondary pixel PLL */ 898c2ecf20Sopenharmony_ci /* no vidrst when in monitor mode */ 908c2ecf20Sopenharmony_ci if (minfo->outputs[1].mode != MATROXFB_OUTPUT_MODE_MONITOR) { 918c2ecf20Sopenharmony_ci tmp |= 0xC0001000; /* Enable H/V vidrst */ 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci } else { 948c2ecf20Sopenharmony_ci tmp |= 0x00000002; /* source from VDOCLK */ 958c2ecf20Sopenharmony_ci tmp |= 0xC0000000; /* enable vvidrst & hvidrst */ 968c2ecf20Sopenharmony_ci /* MGA TVO is our clock source */ 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci } else if (minfo->outputs[0].src == MATROXFB_SRC_CRTC2) { 998c2ecf20Sopenharmony_ci tmp |= 0x00000004; /* source from pixclock */ 1008c2ecf20Sopenharmony_ci /* PIXPLL is our clock source */ 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci if (minfo->outputs[0].src == MATROXFB_SRC_CRTC2) { 1038c2ecf20Sopenharmony_ci tmp |= 0x00100000; /* connect CRTC2 to DAC */ 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci if (mt->interlaced) { 1068c2ecf20Sopenharmony_ci tmp |= 0x02000000; /* interlaced, second field is bigger, as G450 apparently ignores it */ 1078c2ecf20Sopenharmony_ci mt->VDisplay >>= 1; 1088c2ecf20Sopenharmony_ci mt->VSyncStart >>= 1; 1098c2ecf20Sopenharmony_ci mt->VSyncEnd >>= 1; 1108c2ecf20Sopenharmony_ci mt->VTotal >>= 1; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci if ((mt->HTotal & 7) == 2) { 1138c2ecf20Sopenharmony_ci datactl |= 0x00000010; 1148c2ecf20Sopenharmony_ci mt->HTotal &= ~7; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci tmp |= 0x10000000; /* 0x10000000 is VIDRST polarity */ 1178c2ecf20Sopenharmony_ci mga_outl(0x3C14, ((mt->HDisplay - 8) << 16) | (mt->HTotal - 8)); 1188c2ecf20Sopenharmony_ci mga_outl(0x3C18, ((mt->HSyncEnd - 8) << 16) | (mt->HSyncStart - 8)); 1198c2ecf20Sopenharmony_ci mga_outl(0x3C1C, ((mt->VDisplay - 1) << 16) | (mt->VTotal - 1)); 1208c2ecf20Sopenharmony_ci mga_outl(0x3C20, ((mt->VSyncEnd - 1) << 16) | (mt->VSyncStart - 1)); 1218c2ecf20Sopenharmony_ci mga_outl(0x3C24, ((mt->VSyncStart) << 16) | (mt->HSyncStart)); /* preload */ 1228c2ecf20Sopenharmony_ci { 1238c2ecf20Sopenharmony_ci u_int32_t linelen = m2info->fbcon.var.xres_virtual * (m2info->fbcon.var.bits_per_pixel >> 3); 1248c2ecf20Sopenharmony_ci if (tmp & 0x02000000) { 1258c2ecf20Sopenharmony_ci /* field #0 is smaller, so... */ 1268c2ecf20Sopenharmony_ci mga_outl(0x3C2C, pos); /* field #1 vmemory start */ 1278c2ecf20Sopenharmony_ci mga_outl(0x3C28, pos + linelen); /* field #0 vmemory start */ 1288c2ecf20Sopenharmony_ci linelen <<= 1; 1298c2ecf20Sopenharmony_ci m2info->interlaced = 1; 1308c2ecf20Sopenharmony_ci } else { 1318c2ecf20Sopenharmony_ci mga_outl(0x3C28, pos); /* vmemory start */ 1328c2ecf20Sopenharmony_ci m2info->interlaced = 0; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci mga_outl(0x3C40, linelen); 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci mga_outl(0x3C4C, datactl); /* data control */ 1378c2ecf20Sopenharmony_ci if (tmp & 0x02000000) { 1388c2ecf20Sopenharmony_ci int i; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci mga_outl(0x3C10, tmp & ~0x02000000); 1418c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) { 1428c2ecf20Sopenharmony_ci unsigned int nl; 1438c2ecf20Sopenharmony_ci unsigned int lastl = 0; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci while ((nl = mga_inl(0x3C48) & 0xFFF) >= lastl) { 1468c2ecf20Sopenharmony_ci lastl = nl; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci mga_outl(0x3C10, tmp); 1518c2ecf20Sopenharmony_ci minfo->hw.crtc2.ctl = tmp; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci tmp = mt->VDisplay << 16; /* line compare */ 1548c2ecf20Sopenharmony_ci if (mt->sync & FB_SYNC_HOR_HIGH_ACT) 1558c2ecf20Sopenharmony_ci tmp |= 0x00000100; 1568c2ecf20Sopenharmony_ci if (mt->sync & FB_SYNC_VERT_HIGH_ACT) 1578c2ecf20Sopenharmony_ci tmp |= 0x00000200; 1588c2ecf20Sopenharmony_ci mga_outl(0x3C44, tmp); 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic void matroxfb_dh_disable(struct matroxfb_dh_fb_info* m2info) { 1628c2ecf20Sopenharmony_ci struct matrox_fb_info *minfo = m2info->primary_dev; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci mga_outl(0x3C10, 0x00000004); /* disable CRTC2, CRTC1->DAC1, PLL as clock source */ 1658c2ecf20Sopenharmony_ci minfo->hw.crtc2.ctl = 0x00000004; 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic void matroxfb_dh_pan_var(struct matroxfb_dh_fb_info* m2info, 1698c2ecf20Sopenharmony_ci struct fb_var_screeninfo* var) { 1708c2ecf20Sopenharmony_ci unsigned int pos; 1718c2ecf20Sopenharmony_ci unsigned int linelen; 1728c2ecf20Sopenharmony_ci unsigned int pixelsize; 1738c2ecf20Sopenharmony_ci struct matrox_fb_info *minfo = m2info->primary_dev; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci m2info->fbcon.var.xoffset = var->xoffset; 1768c2ecf20Sopenharmony_ci m2info->fbcon.var.yoffset = var->yoffset; 1778c2ecf20Sopenharmony_ci pixelsize = m2info->fbcon.var.bits_per_pixel >> 3; 1788c2ecf20Sopenharmony_ci linelen = m2info->fbcon.var.xres_virtual * pixelsize; 1798c2ecf20Sopenharmony_ci pos = m2info->fbcon.var.yoffset * linelen + m2info->fbcon.var.xoffset * pixelsize; 1808c2ecf20Sopenharmony_ci pos += m2info->video.offbase; 1818c2ecf20Sopenharmony_ci if (m2info->interlaced) { 1828c2ecf20Sopenharmony_ci mga_outl(0x3C2C, pos); 1838c2ecf20Sopenharmony_ci mga_outl(0x3C28, pos + linelen); 1848c2ecf20Sopenharmony_ci } else { 1858c2ecf20Sopenharmony_ci mga_outl(0x3C28, pos); 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic int matroxfb_dh_decode_var(struct matroxfb_dh_fb_info* m2info, 1908c2ecf20Sopenharmony_ci struct fb_var_screeninfo* var, 1918c2ecf20Sopenharmony_ci int *visual, 1928c2ecf20Sopenharmony_ci int *video_cmap_len, 1938c2ecf20Sopenharmony_ci int *mode) { 1948c2ecf20Sopenharmony_ci unsigned int mask; 1958c2ecf20Sopenharmony_ci unsigned int memlen; 1968c2ecf20Sopenharmony_ci unsigned int vramlen; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci switch (var->bits_per_pixel) { 1998c2ecf20Sopenharmony_ci case 16: mask = 0x1F; 2008c2ecf20Sopenharmony_ci break; 2018c2ecf20Sopenharmony_ci case 32: mask = 0x0F; 2028c2ecf20Sopenharmony_ci break; 2038c2ecf20Sopenharmony_ci default: return -EINVAL; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci vramlen = m2info->video.len_usable; 2068c2ecf20Sopenharmony_ci if (var->yres_virtual < var->yres) 2078c2ecf20Sopenharmony_ci var->yres_virtual = var->yres; 2088c2ecf20Sopenharmony_ci if (var->xres_virtual < var->xres) 2098c2ecf20Sopenharmony_ci var->xres_virtual = var->xres; 2108c2ecf20Sopenharmony_ci var->xres_virtual = (var->xres_virtual + mask) & ~mask; 2118c2ecf20Sopenharmony_ci if (var->yres_virtual > 32767) 2128c2ecf20Sopenharmony_ci return -EINVAL; 2138c2ecf20Sopenharmony_ci memlen = var->xres_virtual * var->yres_virtual * (var->bits_per_pixel >> 3); 2148c2ecf20Sopenharmony_ci if (memlen > vramlen) 2158c2ecf20Sopenharmony_ci return -EINVAL; 2168c2ecf20Sopenharmony_ci if (var->xoffset + var->xres > var->xres_virtual) 2178c2ecf20Sopenharmony_ci var->xoffset = var->xres_virtual - var->xres; 2188c2ecf20Sopenharmony_ci if (var->yoffset + var->yres > var->yres_virtual) 2198c2ecf20Sopenharmony_ci var->yoffset = var->yres_virtual - var->yres; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci var->xres &= ~7; 2228c2ecf20Sopenharmony_ci var->left_margin &= ~7; 2238c2ecf20Sopenharmony_ci var->right_margin &= ~7; 2248c2ecf20Sopenharmony_ci var->hsync_len &= ~7; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci *mode = var->bits_per_pixel; 2278c2ecf20Sopenharmony_ci if (var->bits_per_pixel == 16) { 2288c2ecf20Sopenharmony_ci if (var->green.length == 5) { 2298c2ecf20Sopenharmony_ci var->red.offset = 10; 2308c2ecf20Sopenharmony_ci var->red.length = 5; 2318c2ecf20Sopenharmony_ci var->green.offset = 5; 2328c2ecf20Sopenharmony_ci var->green.length = 5; 2338c2ecf20Sopenharmony_ci var->blue.offset = 0; 2348c2ecf20Sopenharmony_ci var->blue.length = 5; 2358c2ecf20Sopenharmony_ci var->transp.offset = 15; 2368c2ecf20Sopenharmony_ci var->transp.length = 1; 2378c2ecf20Sopenharmony_ci *mode = 15; 2388c2ecf20Sopenharmony_ci } else { 2398c2ecf20Sopenharmony_ci var->red.offset = 11; 2408c2ecf20Sopenharmony_ci var->red.length = 5; 2418c2ecf20Sopenharmony_ci var->green.offset = 5; 2428c2ecf20Sopenharmony_ci var->green.length = 6; 2438c2ecf20Sopenharmony_ci var->blue.offset = 0; 2448c2ecf20Sopenharmony_ci var->blue.length = 5; 2458c2ecf20Sopenharmony_ci var->transp.offset = 0; 2468c2ecf20Sopenharmony_ci var->transp.length = 0; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci } else { 2498c2ecf20Sopenharmony_ci var->red.offset = 16; 2508c2ecf20Sopenharmony_ci var->red.length = 8; 2518c2ecf20Sopenharmony_ci var->green.offset = 8; 2528c2ecf20Sopenharmony_ci var->green.length = 8; 2538c2ecf20Sopenharmony_ci var->blue.offset = 0; 2548c2ecf20Sopenharmony_ci var->blue.length = 8; 2558c2ecf20Sopenharmony_ci var->transp.offset = 24; 2568c2ecf20Sopenharmony_ci var->transp.length = 8; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci *visual = FB_VISUAL_TRUECOLOR; 2598c2ecf20Sopenharmony_ci *video_cmap_len = 16; 2608c2ecf20Sopenharmony_ci return 0; 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic int matroxfb_dh_open(struct fb_info* info, int user) { 2648c2ecf20Sopenharmony_ci#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) 2658c2ecf20Sopenharmony_ci struct matrox_fb_info *minfo = m2info->primary_dev; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci if (minfo) { 2688c2ecf20Sopenharmony_ci int err; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci if (minfo->dead) { 2718c2ecf20Sopenharmony_ci return -ENXIO; 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci err = minfo->fbops.fb_open(&minfo->fbcon, user); 2748c2ecf20Sopenharmony_ci if (err) { 2758c2ecf20Sopenharmony_ci return err; 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci return 0; 2798c2ecf20Sopenharmony_ci#undef m2info 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic int matroxfb_dh_release(struct fb_info* info, int user) { 2838c2ecf20Sopenharmony_ci#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) 2848c2ecf20Sopenharmony_ci int err = 0; 2858c2ecf20Sopenharmony_ci struct matrox_fb_info *minfo = m2info->primary_dev; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci if (minfo) { 2888c2ecf20Sopenharmony_ci err = minfo->fbops.fb_release(&minfo->fbcon, user); 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci return err; 2918c2ecf20Sopenharmony_ci#undef m2info 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci/* 2958c2ecf20Sopenharmony_ci * This function is called before the register_framebuffer so 2968c2ecf20Sopenharmony_ci * no locking is needed. 2978c2ecf20Sopenharmony_ci */ 2988c2ecf20Sopenharmony_cistatic void matroxfb_dh_init_fix(struct matroxfb_dh_fb_info *m2info) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci struct fb_fix_screeninfo *fix = &m2info->fbcon.fix; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci strcpy(fix->id, "MATROX DH"); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci fix->smem_start = m2info->video.base; 3058c2ecf20Sopenharmony_ci fix->smem_len = m2info->video.len_usable; 3068c2ecf20Sopenharmony_ci fix->ypanstep = 1; 3078c2ecf20Sopenharmony_ci fix->ywrapstep = 0; 3088c2ecf20Sopenharmony_ci fix->xpanstep = 8; /* TBD */ 3098c2ecf20Sopenharmony_ci fix->mmio_start = m2info->mmio.base; 3108c2ecf20Sopenharmony_ci fix->mmio_len = m2info->mmio.len; 3118c2ecf20Sopenharmony_ci fix->accel = 0; /* no accel... */ 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic int matroxfb_dh_check_var(struct fb_var_screeninfo* var, struct fb_info* info) { 3158c2ecf20Sopenharmony_ci#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) 3168c2ecf20Sopenharmony_ci int visual; 3178c2ecf20Sopenharmony_ci int cmap_len; 3188c2ecf20Sopenharmony_ci int mode; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci return matroxfb_dh_decode_var(m2info, var, &visual, &cmap_len, &mode); 3218c2ecf20Sopenharmony_ci#undef m2info 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic int matroxfb_dh_set_par(struct fb_info* info) { 3258c2ecf20Sopenharmony_ci#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) 3268c2ecf20Sopenharmony_ci int visual; 3278c2ecf20Sopenharmony_ci int cmap_len; 3288c2ecf20Sopenharmony_ci int mode; 3298c2ecf20Sopenharmony_ci int err; 3308c2ecf20Sopenharmony_ci struct fb_var_screeninfo* var = &info->var; 3318c2ecf20Sopenharmony_ci struct matrox_fb_info *minfo = m2info->primary_dev; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci if ((err = matroxfb_dh_decode_var(m2info, var, &visual, &cmap_len, &mode)) != 0) 3348c2ecf20Sopenharmony_ci return err; 3358c2ecf20Sopenharmony_ci /* cmap */ 3368c2ecf20Sopenharmony_ci { 3378c2ecf20Sopenharmony_ci m2info->fbcon.screen_base = vaddr_va(m2info->video.vbase); 3388c2ecf20Sopenharmony_ci m2info->fbcon.fix.visual = visual; 3398c2ecf20Sopenharmony_ci m2info->fbcon.fix.type = FB_TYPE_PACKED_PIXELS; 3408c2ecf20Sopenharmony_ci m2info->fbcon.fix.type_aux = 0; 3418c2ecf20Sopenharmony_ci m2info->fbcon.fix.line_length = (var->xres_virtual * var->bits_per_pixel) >> 3; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci { 3448c2ecf20Sopenharmony_ci struct my_timming mt; 3458c2ecf20Sopenharmony_ci unsigned int pos; 3468c2ecf20Sopenharmony_ci int out; 3478c2ecf20Sopenharmony_ci int cnt; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci matroxfb_var2my(&m2info->fbcon.var, &mt); 3508c2ecf20Sopenharmony_ci mt.crtc = MATROXFB_SRC_CRTC2; 3518c2ecf20Sopenharmony_ci /* CRTC2 delay */ 3528c2ecf20Sopenharmony_ci mt.delay = 34; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci pos = (m2info->fbcon.var.yoffset * m2info->fbcon.var.xres_virtual + m2info->fbcon.var.xoffset) * m2info->fbcon.var.bits_per_pixel >> 3; 3558c2ecf20Sopenharmony_ci pos += m2info->video.offbase; 3568c2ecf20Sopenharmony_ci cnt = 0; 3578c2ecf20Sopenharmony_ci down_read(&minfo->altout.lock); 3588c2ecf20Sopenharmony_ci for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { 3598c2ecf20Sopenharmony_ci if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2) { 3608c2ecf20Sopenharmony_ci cnt++; 3618c2ecf20Sopenharmony_ci if (minfo->outputs[out].output->compute) { 3628c2ecf20Sopenharmony_ci minfo->outputs[out].output->compute(minfo->outputs[out].data, &mt); 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci minfo->crtc2.pixclock = mt.pixclock; 3678c2ecf20Sopenharmony_ci minfo->crtc2.mnp = mt.mnp; 3688c2ecf20Sopenharmony_ci up_read(&minfo->altout.lock); 3698c2ecf20Sopenharmony_ci if (cnt) { 3708c2ecf20Sopenharmony_ci matroxfb_dh_restore(m2info, &mt, mode, pos); 3718c2ecf20Sopenharmony_ci } else { 3728c2ecf20Sopenharmony_ci matroxfb_dh_disable(m2info); 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci DAC1064_global_init(minfo); 3758c2ecf20Sopenharmony_ci DAC1064_global_restore(minfo); 3768c2ecf20Sopenharmony_ci down_read(&minfo->altout.lock); 3778c2ecf20Sopenharmony_ci for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { 3788c2ecf20Sopenharmony_ci if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2 && 3798c2ecf20Sopenharmony_ci minfo->outputs[out].output->program) { 3808c2ecf20Sopenharmony_ci minfo->outputs[out].output->program(minfo->outputs[out].data); 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { 3848c2ecf20Sopenharmony_ci if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2 && 3858c2ecf20Sopenharmony_ci minfo->outputs[out].output->start) { 3868c2ecf20Sopenharmony_ci minfo->outputs[out].output->start(minfo->outputs[out].data); 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci up_read(&minfo->altout.lock); 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci m2info->initialized = 1; 3928c2ecf20Sopenharmony_ci return 0; 3938c2ecf20Sopenharmony_ci#undef m2info 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_cistatic int matroxfb_dh_pan_display(struct fb_var_screeninfo* var, struct fb_info* info) { 3978c2ecf20Sopenharmony_ci#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) 3988c2ecf20Sopenharmony_ci matroxfb_dh_pan_var(m2info, var); 3998c2ecf20Sopenharmony_ci return 0; 4008c2ecf20Sopenharmony_ci#undef m2info 4018c2ecf20Sopenharmony_ci} 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_cistatic int matroxfb_dh_get_vblank(const struct matroxfb_dh_fb_info* m2info, struct fb_vblank* vblank) { 4048c2ecf20Sopenharmony_ci struct matrox_fb_info *minfo = m2info->primary_dev; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci matroxfb_enable_irq(minfo, 0); 4078c2ecf20Sopenharmony_ci memset(vblank, 0, sizeof(*vblank)); 4088c2ecf20Sopenharmony_ci vblank->flags = FB_VBLANK_HAVE_VCOUNT | FB_VBLANK_HAVE_VBLANK; 4098c2ecf20Sopenharmony_ci /* mask out reserved bits + field number (odd/even) */ 4108c2ecf20Sopenharmony_ci vblank->vcount = mga_inl(0x3C48) & 0x000007FF; 4118c2ecf20Sopenharmony_ci /* compatibility stuff */ 4128c2ecf20Sopenharmony_ci if (vblank->vcount >= m2info->fbcon.var.yres) 4138c2ecf20Sopenharmony_ci vblank->flags |= FB_VBLANK_VBLANKING; 4148c2ecf20Sopenharmony_ci if (test_bit(0, &minfo->irq_flags)) { 4158c2ecf20Sopenharmony_ci vblank->flags |= FB_VBLANK_HAVE_COUNT; 4168c2ecf20Sopenharmony_ci /* Only one writer, aligned int value... 4178c2ecf20Sopenharmony_ci it should work without lock and without atomic_t */ 4188c2ecf20Sopenharmony_ci vblank->count = minfo->crtc2.vsync.cnt; 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci return 0; 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_cistatic int matroxfb_dh_ioctl(struct fb_info *info, 4248c2ecf20Sopenharmony_ci unsigned int cmd, 4258c2ecf20Sopenharmony_ci unsigned long arg) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) 4288c2ecf20Sopenharmony_ci struct matrox_fb_info *minfo = m2info->primary_dev; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci DBG(__func__) 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci switch (cmd) { 4338c2ecf20Sopenharmony_ci case FBIOGET_VBLANK: 4348c2ecf20Sopenharmony_ci { 4358c2ecf20Sopenharmony_ci struct fb_vblank vblank; 4368c2ecf20Sopenharmony_ci int err; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci err = matroxfb_dh_get_vblank(m2info, &vblank); 4398c2ecf20Sopenharmony_ci if (err) 4408c2ecf20Sopenharmony_ci return err; 4418c2ecf20Sopenharmony_ci if (copy_to_user((void __user *)arg, &vblank, sizeof(vblank))) 4428c2ecf20Sopenharmony_ci return -EFAULT; 4438c2ecf20Sopenharmony_ci return 0; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci case FBIO_WAITFORVSYNC: 4468c2ecf20Sopenharmony_ci { 4478c2ecf20Sopenharmony_ci u_int32_t crt; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci if (get_user(crt, (u_int32_t __user *)arg)) 4508c2ecf20Sopenharmony_ci return -EFAULT; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci if (crt != 0) 4538c2ecf20Sopenharmony_ci return -ENODEV; 4548c2ecf20Sopenharmony_ci return matroxfb_wait_for_sync(minfo, 1); 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci case MATROXFB_SET_OUTPUT_MODE: 4578c2ecf20Sopenharmony_ci case MATROXFB_GET_OUTPUT_MODE: 4588c2ecf20Sopenharmony_ci case MATROXFB_GET_ALL_OUTPUTS: 4598c2ecf20Sopenharmony_ci { 4608c2ecf20Sopenharmony_ci return minfo->fbcon.fbops->fb_ioctl(&minfo->fbcon, cmd, arg); 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci case MATROXFB_SET_OUTPUT_CONNECTION: 4638c2ecf20Sopenharmony_ci { 4648c2ecf20Sopenharmony_ci u_int32_t tmp; 4658c2ecf20Sopenharmony_ci int out; 4668c2ecf20Sopenharmony_ci int changes; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci if (get_user(tmp, (u_int32_t __user *)arg)) 4698c2ecf20Sopenharmony_ci return -EFAULT; 4708c2ecf20Sopenharmony_ci for (out = 0; out < 32; out++) { 4718c2ecf20Sopenharmony_ci if (tmp & (1 << out)) { 4728c2ecf20Sopenharmony_ci if (out >= MATROXFB_MAX_OUTPUTS) 4738c2ecf20Sopenharmony_ci return -ENXIO; 4748c2ecf20Sopenharmony_ci if (!minfo->outputs[out].output) 4758c2ecf20Sopenharmony_ci return -ENXIO; 4768c2ecf20Sopenharmony_ci switch (minfo->outputs[out].src) { 4778c2ecf20Sopenharmony_ci case MATROXFB_SRC_NONE: 4788c2ecf20Sopenharmony_ci case MATROXFB_SRC_CRTC2: 4798c2ecf20Sopenharmony_ci break; 4808c2ecf20Sopenharmony_ci default: 4818c2ecf20Sopenharmony_ci return -EBUSY; 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci } 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci if (minfo->devflags.panellink) { 4868c2ecf20Sopenharmony_ci if (tmp & MATROXFB_OUTPUT_CONN_DFP) 4878c2ecf20Sopenharmony_ci return -EINVAL; 4888c2ecf20Sopenharmony_ci if ((minfo->outputs[2].src == MATROXFB_SRC_CRTC1) && tmp) 4898c2ecf20Sopenharmony_ci return -EBUSY; 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci changes = 0; 4928c2ecf20Sopenharmony_ci for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { 4938c2ecf20Sopenharmony_ci if (tmp & (1 << out)) { 4948c2ecf20Sopenharmony_ci if (minfo->outputs[out].src != MATROXFB_SRC_CRTC2) { 4958c2ecf20Sopenharmony_ci changes = 1; 4968c2ecf20Sopenharmony_ci minfo->outputs[out].src = MATROXFB_SRC_CRTC2; 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci } else if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2) { 4998c2ecf20Sopenharmony_ci changes = 1; 5008c2ecf20Sopenharmony_ci minfo->outputs[out].src = MATROXFB_SRC_NONE; 5018c2ecf20Sopenharmony_ci } 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci if (!changes) 5048c2ecf20Sopenharmony_ci return 0; 5058c2ecf20Sopenharmony_ci matroxfb_dh_set_par(info); 5068c2ecf20Sopenharmony_ci return 0; 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci case MATROXFB_GET_OUTPUT_CONNECTION: 5098c2ecf20Sopenharmony_ci { 5108c2ecf20Sopenharmony_ci u_int32_t conn = 0; 5118c2ecf20Sopenharmony_ci int out; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { 5148c2ecf20Sopenharmony_ci if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2) { 5158c2ecf20Sopenharmony_ci conn |= 1 << out; 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci if (put_user(conn, (u_int32_t __user *)arg)) 5198c2ecf20Sopenharmony_ci return -EFAULT; 5208c2ecf20Sopenharmony_ci return 0; 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci case MATROXFB_GET_AVAILABLE_OUTPUTS: 5238c2ecf20Sopenharmony_ci { 5248c2ecf20Sopenharmony_ci u_int32_t tmp = 0; 5258c2ecf20Sopenharmony_ci int out; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { 5288c2ecf20Sopenharmony_ci if (minfo->outputs[out].output) { 5298c2ecf20Sopenharmony_ci switch (minfo->outputs[out].src) { 5308c2ecf20Sopenharmony_ci case MATROXFB_SRC_NONE: 5318c2ecf20Sopenharmony_ci case MATROXFB_SRC_CRTC2: 5328c2ecf20Sopenharmony_ci tmp |= 1 << out; 5338c2ecf20Sopenharmony_ci break; 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci } 5368c2ecf20Sopenharmony_ci } 5378c2ecf20Sopenharmony_ci if (minfo->devflags.panellink) { 5388c2ecf20Sopenharmony_ci tmp &= ~MATROXFB_OUTPUT_CONN_DFP; 5398c2ecf20Sopenharmony_ci if (minfo->outputs[2].src == MATROXFB_SRC_CRTC1) { 5408c2ecf20Sopenharmony_ci tmp = 0; 5418c2ecf20Sopenharmony_ci } 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci if (put_user(tmp, (u_int32_t __user *)arg)) 5448c2ecf20Sopenharmony_ci return -EFAULT; 5458c2ecf20Sopenharmony_ci return 0; 5468c2ecf20Sopenharmony_ci } 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci return -ENOTTY; 5498c2ecf20Sopenharmony_ci#undef m2info 5508c2ecf20Sopenharmony_ci} 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_cistatic int matroxfb_dh_blank(int blank, struct fb_info* info) { 5538c2ecf20Sopenharmony_ci#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) 5548c2ecf20Sopenharmony_ci switch (blank) { 5558c2ecf20Sopenharmony_ci case 1: 5568c2ecf20Sopenharmony_ci case 2: 5578c2ecf20Sopenharmony_ci case 3: 5588c2ecf20Sopenharmony_ci case 4: 5598c2ecf20Sopenharmony_ci default:; 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci /* do something... */ 5628c2ecf20Sopenharmony_ci return 0; 5638c2ecf20Sopenharmony_ci#undef m2info 5648c2ecf20Sopenharmony_ci} 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_cistatic const struct fb_ops matroxfb_dh_ops = { 5678c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 5688c2ecf20Sopenharmony_ci .fb_open = matroxfb_dh_open, 5698c2ecf20Sopenharmony_ci .fb_release = matroxfb_dh_release, 5708c2ecf20Sopenharmony_ci .fb_check_var = matroxfb_dh_check_var, 5718c2ecf20Sopenharmony_ci .fb_set_par = matroxfb_dh_set_par, 5728c2ecf20Sopenharmony_ci .fb_setcolreg = matroxfb_dh_setcolreg, 5738c2ecf20Sopenharmony_ci .fb_pan_display =matroxfb_dh_pan_display, 5748c2ecf20Sopenharmony_ci .fb_blank = matroxfb_dh_blank, 5758c2ecf20Sopenharmony_ci .fb_ioctl = matroxfb_dh_ioctl, 5768c2ecf20Sopenharmony_ci .fb_fillrect = cfb_fillrect, 5778c2ecf20Sopenharmony_ci .fb_copyarea = cfb_copyarea, 5788c2ecf20Sopenharmony_ci .fb_imageblit = cfb_imageblit, 5798c2ecf20Sopenharmony_ci}; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_cistatic struct fb_var_screeninfo matroxfb_dh_defined = { 5828c2ecf20Sopenharmony_ci 640,480,640,480,/* W,H, virtual W,H */ 5838c2ecf20Sopenharmony_ci 0,0, /* offset */ 5848c2ecf20Sopenharmony_ci 32, /* depth */ 5858c2ecf20Sopenharmony_ci 0, /* gray */ 5868c2ecf20Sopenharmony_ci {0,0,0}, /* R */ 5878c2ecf20Sopenharmony_ci {0,0,0}, /* G */ 5888c2ecf20Sopenharmony_ci {0,0,0}, /* B */ 5898c2ecf20Sopenharmony_ci {0,0,0}, /* alpha */ 5908c2ecf20Sopenharmony_ci 0, /* nonstd */ 5918c2ecf20Sopenharmony_ci FB_ACTIVATE_NOW, 5928c2ecf20Sopenharmony_ci -1,-1, /* display size */ 5938c2ecf20Sopenharmony_ci 0, /* accel flags */ 5948c2ecf20Sopenharmony_ci 39721L,48L,16L,33L,10L, 5958c2ecf20Sopenharmony_ci 96L,2,0, /* no sync info */ 5968c2ecf20Sopenharmony_ci FB_VMODE_NONINTERLACED, 5978c2ecf20Sopenharmony_ci}; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_cistatic int matroxfb_dh_regit(const struct matrox_fb_info *minfo, 6008c2ecf20Sopenharmony_ci struct matroxfb_dh_fb_info *m2info) 6018c2ecf20Sopenharmony_ci{ 6028c2ecf20Sopenharmony_ci#define minfo (m2info->primary_dev) 6038c2ecf20Sopenharmony_ci void* oldcrtc2; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci m2info->fbcon.fbops = &matroxfb_dh_ops; 6068c2ecf20Sopenharmony_ci m2info->fbcon.flags = FBINFO_FLAG_DEFAULT; 6078c2ecf20Sopenharmony_ci m2info->fbcon.flags |= FBINFO_HWACCEL_XPAN | 6088c2ecf20Sopenharmony_ci FBINFO_HWACCEL_YPAN; 6098c2ecf20Sopenharmony_ci m2info->fbcon.pseudo_palette = m2info->cmap; 6108c2ecf20Sopenharmony_ci fb_alloc_cmap(&m2info->fbcon.cmap, 256, 1); 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci if (mem < 64) 6138c2ecf20Sopenharmony_ci mem *= 1024; 6148c2ecf20Sopenharmony_ci if (mem < 64*1024) 6158c2ecf20Sopenharmony_ci mem *= 1024; 6168c2ecf20Sopenharmony_ci mem &= ~0x00000FFF; /* PAGE_MASK? */ 6178c2ecf20Sopenharmony_ci if (minfo->video.len_usable + mem <= minfo->video.len) 6188c2ecf20Sopenharmony_ci m2info->video.offbase = minfo->video.len - mem; 6198c2ecf20Sopenharmony_ci else if (minfo->video.len < mem) { 6208c2ecf20Sopenharmony_ci return -ENOMEM; 6218c2ecf20Sopenharmony_ci } else { /* check yres on first head... */ 6228c2ecf20Sopenharmony_ci m2info->video.borrowed = mem; 6238c2ecf20Sopenharmony_ci minfo->video.len_usable -= mem; 6248c2ecf20Sopenharmony_ci m2info->video.offbase = minfo->video.len_usable; 6258c2ecf20Sopenharmony_ci } 6268c2ecf20Sopenharmony_ci m2info->video.base = minfo->video.base + m2info->video.offbase; 6278c2ecf20Sopenharmony_ci m2info->video.len = m2info->video.len_usable = m2info->video.len_maximum = mem; 6288c2ecf20Sopenharmony_ci m2info->video.vbase.vaddr = vaddr_va(minfo->video.vbase) + m2info->video.offbase; 6298c2ecf20Sopenharmony_ci m2info->mmio.base = minfo->mmio.base; 6308c2ecf20Sopenharmony_ci m2info->mmio.vbase = minfo->mmio.vbase; 6318c2ecf20Sopenharmony_ci m2info->mmio.len = minfo->mmio.len; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci matroxfb_dh_init_fix(m2info); 6348c2ecf20Sopenharmony_ci if (register_framebuffer(&m2info->fbcon)) { 6358c2ecf20Sopenharmony_ci return -ENXIO; 6368c2ecf20Sopenharmony_ci } 6378c2ecf20Sopenharmony_ci if (!m2info->initialized) 6388c2ecf20Sopenharmony_ci fb_set_var(&m2info->fbcon, &matroxfb_dh_defined); 6398c2ecf20Sopenharmony_ci down_write(&minfo->crtc2.lock); 6408c2ecf20Sopenharmony_ci oldcrtc2 = minfo->crtc2.info; 6418c2ecf20Sopenharmony_ci minfo->crtc2.info = m2info; 6428c2ecf20Sopenharmony_ci up_write(&minfo->crtc2.lock); 6438c2ecf20Sopenharmony_ci if (oldcrtc2) { 6448c2ecf20Sopenharmony_ci printk(KERN_ERR "matroxfb_crtc2: Internal consistency check failed: crtc2 already present: %p\n", 6458c2ecf20Sopenharmony_ci oldcrtc2); 6468c2ecf20Sopenharmony_ci } 6478c2ecf20Sopenharmony_ci return 0; 6488c2ecf20Sopenharmony_ci#undef minfo 6498c2ecf20Sopenharmony_ci} 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci/* ************************** */ 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_cistatic int matroxfb_dh_registerfb(struct matroxfb_dh_fb_info* m2info) { 6548c2ecf20Sopenharmony_ci#define minfo (m2info->primary_dev) 6558c2ecf20Sopenharmony_ci if (matroxfb_dh_regit(minfo, m2info)) { 6568c2ecf20Sopenharmony_ci printk(KERN_ERR "matroxfb_crtc2: secondary head failed to register\n"); 6578c2ecf20Sopenharmony_ci return -1; 6588c2ecf20Sopenharmony_ci } 6598c2ecf20Sopenharmony_ci printk(KERN_INFO "matroxfb_crtc2: secondary head of fb%u was registered as fb%u\n", 6608c2ecf20Sopenharmony_ci minfo->fbcon.node, m2info->fbcon.node); 6618c2ecf20Sopenharmony_ci m2info->fbcon_registered = 1; 6628c2ecf20Sopenharmony_ci return 0; 6638c2ecf20Sopenharmony_ci#undef minfo 6648c2ecf20Sopenharmony_ci} 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_cistatic void matroxfb_dh_deregisterfb(struct matroxfb_dh_fb_info* m2info) { 6678c2ecf20Sopenharmony_ci#define minfo (m2info->primary_dev) 6688c2ecf20Sopenharmony_ci if (m2info->fbcon_registered) { 6698c2ecf20Sopenharmony_ci int id; 6708c2ecf20Sopenharmony_ci struct matroxfb_dh_fb_info* crtc2; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci down_write(&minfo->crtc2.lock); 6738c2ecf20Sopenharmony_ci crtc2 = minfo->crtc2.info; 6748c2ecf20Sopenharmony_ci if (crtc2 == m2info) 6758c2ecf20Sopenharmony_ci minfo->crtc2.info = NULL; 6768c2ecf20Sopenharmony_ci up_write(&minfo->crtc2.lock); 6778c2ecf20Sopenharmony_ci if (crtc2 != m2info) { 6788c2ecf20Sopenharmony_ci printk(KERN_ERR "matroxfb_crtc2: Internal consistency check failed: crtc2 mismatch at unload: %p != %p\n", 6798c2ecf20Sopenharmony_ci crtc2, m2info); 6808c2ecf20Sopenharmony_ci printk(KERN_ERR "matroxfb_crtc2: Expect kernel crash after module unload.\n"); 6818c2ecf20Sopenharmony_ci return; 6828c2ecf20Sopenharmony_ci } 6838c2ecf20Sopenharmony_ci id = m2info->fbcon.node; 6848c2ecf20Sopenharmony_ci unregister_framebuffer(&m2info->fbcon); 6858c2ecf20Sopenharmony_ci /* return memory back to primary head */ 6868c2ecf20Sopenharmony_ci minfo->video.len_usable += m2info->video.borrowed; 6878c2ecf20Sopenharmony_ci printk(KERN_INFO "matroxfb_crtc2: fb%u unregistered\n", id); 6888c2ecf20Sopenharmony_ci m2info->fbcon_registered = 0; 6898c2ecf20Sopenharmony_ci } 6908c2ecf20Sopenharmony_ci#undef minfo 6918c2ecf20Sopenharmony_ci} 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_cistatic void* matroxfb_crtc2_probe(struct matrox_fb_info* minfo) { 6948c2ecf20Sopenharmony_ci struct matroxfb_dh_fb_info* m2info; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci /* hardware is CRTC2 incapable... */ 6978c2ecf20Sopenharmony_ci if (!minfo->devflags.crtc2) 6988c2ecf20Sopenharmony_ci return NULL; 6998c2ecf20Sopenharmony_ci m2info = kzalloc(sizeof(*m2info), GFP_KERNEL); 7008c2ecf20Sopenharmony_ci if (!m2info) 7018c2ecf20Sopenharmony_ci return NULL; 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci m2info->primary_dev = minfo; 7048c2ecf20Sopenharmony_ci if (matroxfb_dh_registerfb(m2info)) { 7058c2ecf20Sopenharmony_ci kfree(m2info); 7068c2ecf20Sopenharmony_ci printk(KERN_ERR "matroxfb_crtc2: CRTC2 framebuffer failed to register\n"); 7078c2ecf20Sopenharmony_ci return NULL; 7088c2ecf20Sopenharmony_ci } 7098c2ecf20Sopenharmony_ci return m2info; 7108c2ecf20Sopenharmony_ci} 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_cistatic void matroxfb_crtc2_remove(struct matrox_fb_info* minfo, void* crtc2) { 7138c2ecf20Sopenharmony_ci matroxfb_dh_deregisterfb(crtc2); 7148c2ecf20Sopenharmony_ci kfree(crtc2); 7158c2ecf20Sopenharmony_ci} 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_cistatic struct matroxfb_driver crtc2 = { 7188c2ecf20Sopenharmony_ci .name = "Matrox G400 CRTC2", 7198c2ecf20Sopenharmony_ci .probe = matroxfb_crtc2_probe, 7208c2ecf20Sopenharmony_ci .remove = matroxfb_crtc2_remove }; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_cistatic int matroxfb_crtc2_init(void) { 7238c2ecf20Sopenharmony_ci if (fb_get_options("matrox_crtc2fb", NULL)) 7248c2ecf20Sopenharmony_ci return -ENODEV; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci matroxfb_register_driver(&crtc2); 7278c2ecf20Sopenharmony_ci return 0; 7288c2ecf20Sopenharmony_ci} 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_cistatic void matroxfb_crtc2_exit(void) { 7318c2ecf20Sopenharmony_ci matroxfb_unregister_driver(&crtc2); 7328c2ecf20Sopenharmony_ci} 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ciMODULE_AUTHOR("(c) 1999-2002 Petr Vandrovec <vandrove@vc.cvut.cz>"); 7358c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Matrox G400 CRTC2 driver"); 7368c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 7378c2ecf20Sopenharmony_cimodule_init(matroxfb_crtc2_init); 7388c2ecf20Sopenharmony_cimodule_exit(matroxfb_crtc2_exit); 7398c2ecf20Sopenharmony_ci/* we do not have __setup() yet */ 740