18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * i740fb - framebuffer driver for Intel740 48c2ecf20Sopenharmony_ci * Copyright (c) 2011 Ondrej Zary 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Based on old i740fb driver (c) 2001-2002 Andrey Ulanov <drey@rt.mipt.ru> 78c2ecf20Sopenharmony_ci * which was partially based on: 88c2ecf20Sopenharmony_ci * VGA 16-color framebuffer driver (c) 1999 Ben Pfaff <pfaffben@debian.org> 98c2ecf20Sopenharmony_ci * and Petr Vandrovec <VANDROVE@vc.cvut.cz> 108c2ecf20Sopenharmony_ci * i740 driver from XFree86 (c) 1998-1999 Precision Insight, Inc., Cedar Park, 118c2ecf20Sopenharmony_ci * Texas. 128c2ecf20Sopenharmony_ci * i740fb by Patrick LERDA, v0.9 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/kernel.h> 178c2ecf20Sopenharmony_ci#include <linux/errno.h> 188c2ecf20Sopenharmony_ci#include <linux/string.h> 198c2ecf20Sopenharmony_ci#include <linux/mm.h> 208c2ecf20Sopenharmony_ci#include <linux/slab.h> 218c2ecf20Sopenharmony_ci#include <linux/delay.h> 228c2ecf20Sopenharmony_ci#include <linux/fb.h> 238c2ecf20Sopenharmony_ci#include <linux/init.h> 248c2ecf20Sopenharmony_ci#include <linux/pci.h> 258c2ecf20Sopenharmony_ci#include <linux/pci_ids.h> 268c2ecf20Sopenharmony_ci#include <linux/i2c.h> 278c2ecf20Sopenharmony_ci#include <linux/i2c-algo-bit.h> 288c2ecf20Sopenharmony_ci#include <linux/console.h> 298c2ecf20Sopenharmony_ci#include <video/vga.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include "i740_reg.h" 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic char *mode_option; 348c2ecf20Sopenharmony_cistatic int mtrr = 1; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistruct i740fb_par { 378c2ecf20Sopenharmony_ci unsigned char __iomem *regs; 388c2ecf20Sopenharmony_ci bool has_sgram; 398c2ecf20Sopenharmony_ci int wc_cookie; 408c2ecf20Sopenharmony_ci bool ddc_registered; 418c2ecf20Sopenharmony_ci struct i2c_adapter ddc_adapter; 428c2ecf20Sopenharmony_ci struct i2c_algo_bit_data ddc_algo; 438c2ecf20Sopenharmony_ci u32 pseudo_palette[16]; 448c2ecf20Sopenharmony_ci struct mutex open_lock; 458c2ecf20Sopenharmony_ci unsigned int ref_count; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci u8 crtc[VGA_CRT_C]; 488c2ecf20Sopenharmony_ci u8 atc[VGA_ATT_C]; 498c2ecf20Sopenharmony_ci u8 gdc[VGA_GFX_C]; 508c2ecf20Sopenharmony_ci u8 seq[VGA_SEQ_C]; 518c2ecf20Sopenharmony_ci u8 misc; 528c2ecf20Sopenharmony_ci u8 vss; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci /* i740 specific registers */ 558c2ecf20Sopenharmony_ci u8 display_cntl; 568c2ecf20Sopenharmony_ci u8 pixelpipe_cfg0; 578c2ecf20Sopenharmony_ci u8 pixelpipe_cfg1; 588c2ecf20Sopenharmony_ci u8 pixelpipe_cfg2; 598c2ecf20Sopenharmony_ci u8 video_clk2_m; 608c2ecf20Sopenharmony_ci u8 video_clk2_n; 618c2ecf20Sopenharmony_ci u8 video_clk2_mn_msbs; 628c2ecf20Sopenharmony_ci u8 video_clk2_div_sel; 638c2ecf20Sopenharmony_ci u8 pll_cntl; 648c2ecf20Sopenharmony_ci u8 address_mapping; 658c2ecf20Sopenharmony_ci u8 io_cntl; 668c2ecf20Sopenharmony_ci u8 bitblt_cntl; 678c2ecf20Sopenharmony_ci u8 ext_vert_total; 688c2ecf20Sopenharmony_ci u8 ext_vert_disp_end; 698c2ecf20Sopenharmony_ci u8 ext_vert_sync_start; 708c2ecf20Sopenharmony_ci u8 ext_vert_blank_start; 718c2ecf20Sopenharmony_ci u8 ext_horiz_total; 728c2ecf20Sopenharmony_ci u8 ext_horiz_blank; 738c2ecf20Sopenharmony_ci u8 ext_offset; 748c2ecf20Sopenharmony_ci u8 interlace_cntl; 758c2ecf20Sopenharmony_ci u32 lmi_fifo_watermark; 768c2ecf20Sopenharmony_ci u8 ext_start_addr; 778c2ecf20Sopenharmony_ci u8 ext_start_addr_hi; 788c2ecf20Sopenharmony_ci}; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci#define DACSPEED8 203 818c2ecf20Sopenharmony_ci#define DACSPEED16 163 828c2ecf20Sopenharmony_ci#define DACSPEED24_SG 136 838c2ecf20Sopenharmony_ci#define DACSPEED24_SD 128 848c2ecf20Sopenharmony_ci#define DACSPEED32 86 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic const struct fb_fix_screeninfo i740fb_fix = { 878c2ecf20Sopenharmony_ci .id = "i740fb", 888c2ecf20Sopenharmony_ci .type = FB_TYPE_PACKED_PIXELS, 898c2ecf20Sopenharmony_ci .visual = FB_VISUAL_TRUECOLOR, 908c2ecf20Sopenharmony_ci .xpanstep = 8, 918c2ecf20Sopenharmony_ci .ypanstep = 1, 928c2ecf20Sopenharmony_ci .accel = FB_ACCEL_NONE, 938c2ecf20Sopenharmony_ci}; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic inline void i740outb(struct i740fb_par *par, u16 port, u8 val) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci vga_mm_w(par->regs, port, val); 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_cistatic inline u8 i740inb(struct i740fb_par *par, u16 port) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci return vga_mm_r(par->regs, port); 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_cistatic inline void i740outreg(struct i740fb_par *par, u16 port, u8 reg, u8 val) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci vga_mm_w_fast(par->regs, port, reg, val); 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_cistatic inline u8 i740inreg(struct i740fb_par *par, u16 port, u8 reg) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci vga_mm_w(par->regs, port, reg); 1108c2ecf20Sopenharmony_ci return vga_mm_r(par->regs, port+1); 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_cistatic inline void i740outreg_mask(struct i740fb_par *par, u16 port, u8 reg, 1138c2ecf20Sopenharmony_ci u8 val, u8 mask) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci vga_mm_w_fast(par->regs, port, reg, (val & mask) 1168c2ecf20Sopenharmony_ci | (i740inreg(par, port, reg) & ~mask)); 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci#define REG_DDC_DRIVE 0x62 1208c2ecf20Sopenharmony_ci#define REG_DDC_STATE 0x63 1218c2ecf20Sopenharmony_ci#define DDC_SCL (1 << 3) 1228c2ecf20Sopenharmony_ci#define DDC_SDA (1 << 2) 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic void i740fb_ddc_setscl(void *data, int val) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci struct i740fb_par *par = data; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci i740outreg_mask(par, XRX, REG_DDC_DRIVE, DDC_SCL, DDC_SCL); 1298c2ecf20Sopenharmony_ci i740outreg_mask(par, XRX, REG_DDC_STATE, val ? DDC_SCL : 0, DDC_SCL); 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic void i740fb_ddc_setsda(void *data, int val) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci struct i740fb_par *par = data; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci i740outreg_mask(par, XRX, REG_DDC_DRIVE, DDC_SDA, DDC_SDA); 1378c2ecf20Sopenharmony_ci i740outreg_mask(par, XRX, REG_DDC_STATE, val ? DDC_SDA : 0, DDC_SDA); 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic int i740fb_ddc_getscl(void *data) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci struct i740fb_par *par = data; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci i740outreg_mask(par, XRX, REG_DDC_DRIVE, 0, DDC_SCL); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci return !!(i740inreg(par, XRX, REG_DDC_STATE) & DDC_SCL); 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic int i740fb_ddc_getsda(void *data) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci struct i740fb_par *par = data; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci i740outreg_mask(par, XRX, REG_DDC_DRIVE, 0, DDC_SDA); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci return !!(i740inreg(par, XRX, REG_DDC_STATE) & DDC_SDA); 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic int i740fb_setup_ddc_bus(struct fb_info *info) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci struct i740fb_par *par = info->par; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci strlcpy(par->ddc_adapter.name, info->fix.id, 1638c2ecf20Sopenharmony_ci sizeof(par->ddc_adapter.name)); 1648c2ecf20Sopenharmony_ci par->ddc_adapter.owner = THIS_MODULE; 1658c2ecf20Sopenharmony_ci par->ddc_adapter.class = I2C_CLASS_DDC; 1668c2ecf20Sopenharmony_ci par->ddc_adapter.algo_data = &par->ddc_algo; 1678c2ecf20Sopenharmony_ci par->ddc_adapter.dev.parent = info->device; 1688c2ecf20Sopenharmony_ci par->ddc_algo.setsda = i740fb_ddc_setsda; 1698c2ecf20Sopenharmony_ci par->ddc_algo.setscl = i740fb_ddc_setscl; 1708c2ecf20Sopenharmony_ci par->ddc_algo.getsda = i740fb_ddc_getsda; 1718c2ecf20Sopenharmony_ci par->ddc_algo.getscl = i740fb_ddc_getscl; 1728c2ecf20Sopenharmony_ci par->ddc_algo.udelay = 10; 1738c2ecf20Sopenharmony_ci par->ddc_algo.timeout = 20; 1748c2ecf20Sopenharmony_ci par->ddc_algo.data = par; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci i2c_set_adapdata(&par->ddc_adapter, par); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci return i2c_bit_add_bus(&par->ddc_adapter); 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic int i740fb_open(struct fb_info *info, int user) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci struct i740fb_par *par = info->par; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci mutex_lock(&(par->open_lock)); 1868c2ecf20Sopenharmony_ci par->ref_count++; 1878c2ecf20Sopenharmony_ci mutex_unlock(&(par->open_lock)); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci return 0; 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic int i740fb_release(struct fb_info *info, int user) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci struct i740fb_par *par = info->par; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci mutex_lock(&(par->open_lock)); 1978c2ecf20Sopenharmony_ci if (par->ref_count == 0) { 1988c2ecf20Sopenharmony_ci fb_err(info, "release called with zero refcount\n"); 1998c2ecf20Sopenharmony_ci mutex_unlock(&(par->open_lock)); 2008c2ecf20Sopenharmony_ci return -EINVAL; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci par->ref_count--; 2048c2ecf20Sopenharmony_ci mutex_unlock(&(par->open_lock)); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci return 0; 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic u32 i740_calc_fifo(struct i740fb_par *par, u32 freq, int bpp) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci /* 2128c2ecf20Sopenharmony_ci * Would like to calculate these values automatically, but a generic 2138c2ecf20Sopenharmony_ci * algorithm does not seem possible. Note: These FIFO water mark 2148c2ecf20Sopenharmony_ci * values were tested on several cards and seem to eliminate the 2158c2ecf20Sopenharmony_ci * all of the snow and vertical banding, but fine adjustments will 2168c2ecf20Sopenharmony_ci * probably be required for other cards. 2178c2ecf20Sopenharmony_ci */ 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci u32 wm; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci switch (bpp) { 2228c2ecf20Sopenharmony_ci case 8: 2238c2ecf20Sopenharmony_ci if (freq > 200) 2248c2ecf20Sopenharmony_ci wm = 0x18120000; 2258c2ecf20Sopenharmony_ci else if (freq > 175) 2268c2ecf20Sopenharmony_ci wm = 0x16110000; 2278c2ecf20Sopenharmony_ci else if (freq > 135) 2288c2ecf20Sopenharmony_ci wm = 0x120E0000; 2298c2ecf20Sopenharmony_ci else 2308c2ecf20Sopenharmony_ci wm = 0x100D0000; 2318c2ecf20Sopenharmony_ci break; 2328c2ecf20Sopenharmony_ci case 15: 2338c2ecf20Sopenharmony_ci case 16: 2348c2ecf20Sopenharmony_ci if (par->has_sgram) { 2358c2ecf20Sopenharmony_ci if (freq > 140) 2368c2ecf20Sopenharmony_ci wm = 0x2C1D0000; 2378c2ecf20Sopenharmony_ci else if (freq > 120) 2388c2ecf20Sopenharmony_ci wm = 0x2C180000; 2398c2ecf20Sopenharmony_ci else if (freq > 100) 2408c2ecf20Sopenharmony_ci wm = 0x24160000; 2418c2ecf20Sopenharmony_ci else if (freq > 90) 2428c2ecf20Sopenharmony_ci wm = 0x18120000; 2438c2ecf20Sopenharmony_ci else if (freq > 50) 2448c2ecf20Sopenharmony_ci wm = 0x16110000; 2458c2ecf20Sopenharmony_ci else if (freq > 32) 2468c2ecf20Sopenharmony_ci wm = 0x13100000; 2478c2ecf20Sopenharmony_ci else 2488c2ecf20Sopenharmony_ci wm = 0x120E0000; 2498c2ecf20Sopenharmony_ci } else { 2508c2ecf20Sopenharmony_ci if (freq > 160) 2518c2ecf20Sopenharmony_ci wm = 0x28200000; 2528c2ecf20Sopenharmony_ci else if (freq > 140) 2538c2ecf20Sopenharmony_ci wm = 0x2A1E0000; 2548c2ecf20Sopenharmony_ci else if (freq > 130) 2558c2ecf20Sopenharmony_ci wm = 0x2B1A0000; 2568c2ecf20Sopenharmony_ci else if (freq > 120) 2578c2ecf20Sopenharmony_ci wm = 0x2C180000; 2588c2ecf20Sopenharmony_ci else if (freq > 100) 2598c2ecf20Sopenharmony_ci wm = 0x24180000; 2608c2ecf20Sopenharmony_ci else if (freq > 90) 2618c2ecf20Sopenharmony_ci wm = 0x18120000; 2628c2ecf20Sopenharmony_ci else if (freq > 50) 2638c2ecf20Sopenharmony_ci wm = 0x16110000; 2648c2ecf20Sopenharmony_ci else if (freq > 32) 2658c2ecf20Sopenharmony_ci wm = 0x13100000; 2668c2ecf20Sopenharmony_ci else 2678c2ecf20Sopenharmony_ci wm = 0x120E0000; 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci break; 2708c2ecf20Sopenharmony_ci case 24: 2718c2ecf20Sopenharmony_ci if (par->has_sgram) { 2728c2ecf20Sopenharmony_ci if (freq > 130) 2738c2ecf20Sopenharmony_ci wm = 0x31200000; 2748c2ecf20Sopenharmony_ci else if (freq > 120) 2758c2ecf20Sopenharmony_ci wm = 0x2E200000; 2768c2ecf20Sopenharmony_ci else if (freq > 100) 2778c2ecf20Sopenharmony_ci wm = 0x2C1D0000; 2788c2ecf20Sopenharmony_ci else if (freq > 80) 2798c2ecf20Sopenharmony_ci wm = 0x25180000; 2808c2ecf20Sopenharmony_ci else if (freq > 64) 2818c2ecf20Sopenharmony_ci wm = 0x24160000; 2828c2ecf20Sopenharmony_ci else if (freq > 49) 2838c2ecf20Sopenharmony_ci wm = 0x18120000; 2848c2ecf20Sopenharmony_ci else if (freq > 32) 2858c2ecf20Sopenharmony_ci wm = 0x16110000; 2868c2ecf20Sopenharmony_ci else 2878c2ecf20Sopenharmony_ci wm = 0x13100000; 2888c2ecf20Sopenharmony_ci } else { 2898c2ecf20Sopenharmony_ci if (freq > 120) 2908c2ecf20Sopenharmony_ci wm = 0x311F0000; 2918c2ecf20Sopenharmony_ci else if (freq > 100) 2928c2ecf20Sopenharmony_ci wm = 0x2C1D0000; 2938c2ecf20Sopenharmony_ci else if (freq > 80) 2948c2ecf20Sopenharmony_ci wm = 0x25180000; 2958c2ecf20Sopenharmony_ci else if (freq > 64) 2968c2ecf20Sopenharmony_ci wm = 0x24160000; 2978c2ecf20Sopenharmony_ci else if (freq > 49) 2988c2ecf20Sopenharmony_ci wm = 0x18120000; 2998c2ecf20Sopenharmony_ci else if (freq > 32) 3008c2ecf20Sopenharmony_ci wm = 0x16110000; 3018c2ecf20Sopenharmony_ci else 3028c2ecf20Sopenharmony_ci wm = 0x13100000; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci break; 3058c2ecf20Sopenharmony_ci case 32: 3068c2ecf20Sopenharmony_ci if (par->has_sgram) { 3078c2ecf20Sopenharmony_ci if (freq > 80) 3088c2ecf20Sopenharmony_ci wm = 0x2A200000; 3098c2ecf20Sopenharmony_ci else if (freq > 60) 3108c2ecf20Sopenharmony_ci wm = 0x281A0000; 3118c2ecf20Sopenharmony_ci else if (freq > 49) 3128c2ecf20Sopenharmony_ci wm = 0x25180000; 3138c2ecf20Sopenharmony_ci else if (freq > 32) 3148c2ecf20Sopenharmony_ci wm = 0x18120000; 3158c2ecf20Sopenharmony_ci else 3168c2ecf20Sopenharmony_ci wm = 0x16110000; 3178c2ecf20Sopenharmony_ci } else { 3188c2ecf20Sopenharmony_ci if (freq > 80) 3198c2ecf20Sopenharmony_ci wm = 0x29200000; 3208c2ecf20Sopenharmony_ci else if (freq > 60) 3218c2ecf20Sopenharmony_ci wm = 0x281A0000; 3228c2ecf20Sopenharmony_ci else if (freq > 49) 3238c2ecf20Sopenharmony_ci wm = 0x25180000; 3248c2ecf20Sopenharmony_ci else if (freq > 32) 3258c2ecf20Sopenharmony_ci wm = 0x18120000; 3268c2ecf20Sopenharmony_ci else 3278c2ecf20Sopenharmony_ci wm = 0x16110000; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci break; 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci return wm; 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci/* clock calculation from i740fb by Patrick LERDA */ 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci#define I740_RFREQ 1000000 3388c2ecf20Sopenharmony_ci#define TARGET_MAX_N 30 3398c2ecf20Sopenharmony_ci#define I740_FFIX (1 << 8) 3408c2ecf20Sopenharmony_ci#define I740_RFREQ_FIX (I740_RFREQ / I740_FFIX) 3418c2ecf20Sopenharmony_ci#define I740_REF_FREQ (6667 * I740_FFIX / 100) /* 66.67 MHz */ 3428c2ecf20Sopenharmony_ci#define I740_MAX_VCO_FREQ (450 * I740_FFIX) /* 450 MHz */ 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cistatic void i740_calc_vclk(u32 freq, struct i740fb_par *par) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci const u32 err_max = freq / (200 * I740_RFREQ / I740_FFIX); 3478c2ecf20Sopenharmony_ci const u32 err_target = freq / (1000 * I740_RFREQ / I740_FFIX); 3488c2ecf20Sopenharmony_ci u32 err_best = 512 * I740_FFIX; 3498c2ecf20Sopenharmony_ci u32 f_err, f_vco; 3508c2ecf20Sopenharmony_ci int m_best = 0, n_best = 0, p_best = 0; 3518c2ecf20Sopenharmony_ci int m, n; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci p_best = min(15, ilog2(I740_MAX_VCO_FREQ / (freq / I740_RFREQ_FIX))); 3548c2ecf20Sopenharmony_ci f_vco = (freq * (1 << p_best)) / I740_RFREQ_FIX; 3558c2ecf20Sopenharmony_ci freq = freq / I740_RFREQ_FIX; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci n = 2; 3588c2ecf20Sopenharmony_ci do { 3598c2ecf20Sopenharmony_ci n++; 3608c2ecf20Sopenharmony_ci m = ((f_vco * n) / I740_REF_FREQ + 2) / 4; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci if (m < 3) 3638c2ecf20Sopenharmony_ci m = 3; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci { 3668c2ecf20Sopenharmony_ci u32 f_out = (((m * I740_REF_FREQ * 4) 3678c2ecf20Sopenharmony_ci / n) + ((1 << p_best) / 2)) / (1 << p_best); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci f_err = (freq - f_out); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci if (abs(f_err) < err_max) { 3728c2ecf20Sopenharmony_ci m_best = m; 3738c2ecf20Sopenharmony_ci n_best = n; 3748c2ecf20Sopenharmony_ci err_best = f_err; 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci } while ((abs(f_err) >= err_target) && 3788c2ecf20Sopenharmony_ci ((n <= TARGET_MAX_N) || (abs(err_best) > err_max))); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci if (abs(f_err) < err_target) { 3818c2ecf20Sopenharmony_ci m_best = m; 3828c2ecf20Sopenharmony_ci n_best = n; 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci par->video_clk2_m = (m_best - 2) & 0xFF; 3868c2ecf20Sopenharmony_ci par->video_clk2_n = (n_best - 2) & 0xFF; 3878c2ecf20Sopenharmony_ci par->video_clk2_mn_msbs = ((((n_best - 2) >> 4) & VCO_N_MSBS) 3888c2ecf20Sopenharmony_ci | (((m_best - 2) >> 8) & VCO_M_MSBS)); 3898c2ecf20Sopenharmony_ci par->video_clk2_div_sel = ((p_best << 4) | REF_DIV_1); 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_cistatic int i740fb_decode_var(const struct fb_var_screeninfo *var, 3938c2ecf20Sopenharmony_ci struct i740fb_par *par, struct fb_info *info) 3948c2ecf20Sopenharmony_ci{ 3958c2ecf20Sopenharmony_ci /* 3968c2ecf20Sopenharmony_ci * Get the video params out of 'var'. 3978c2ecf20Sopenharmony_ci * If a value doesn't fit, round it up, if it's too big, return -EINVAL. 3988c2ecf20Sopenharmony_ci */ 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci u32 xres, right, hslen, left, xtotal; 4018c2ecf20Sopenharmony_ci u32 yres, lower, vslen, upper, ytotal; 4028c2ecf20Sopenharmony_ci u32 vxres, xoffset, vyres, yoffset; 4038c2ecf20Sopenharmony_ci u32 bpp, base, dacspeed24, mem, freq; 4048c2ecf20Sopenharmony_ci u8 r7; 4058c2ecf20Sopenharmony_ci int i; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci dev_dbg(info->device, "decode_var: xres: %i, yres: %i, xres_v: %i, xres_v: %i\n", 4088c2ecf20Sopenharmony_ci var->xres, var->yres, var->xres_virtual, var->xres_virtual); 4098c2ecf20Sopenharmony_ci dev_dbg(info->device, " xoff: %i, yoff: %i, bpp: %i, graysc: %i\n", 4108c2ecf20Sopenharmony_ci var->xoffset, var->yoffset, var->bits_per_pixel, 4118c2ecf20Sopenharmony_ci var->grayscale); 4128c2ecf20Sopenharmony_ci dev_dbg(info->device, " activate: %i, nonstd: %i, vmode: %i\n", 4138c2ecf20Sopenharmony_ci var->activate, var->nonstd, var->vmode); 4148c2ecf20Sopenharmony_ci dev_dbg(info->device, " pixclock: %i, hsynclen:%i, vsynclen:%i\n", 4158c2ecf20Sopenharmony_ci var->pixclock, var->hsync_len, var->vsync_len); 4168c2ecf20Sopenharmony_ci dev_dbg(info->device, " left: %i, right: %i, up:%i, lower:%i\n", 4178c2ecf20Sopenharmony_ci var->left_margin, var->right_margin, var->upper_margin, 4188c2ecf20Sopenharmony_ci var->lower_margin); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci bpp = var->bits_per_pixel; 4228c2ecf20Sopenharmony_ci switch (bpp) { 4238c2ecf20Sopenharmony_ci case 1 ... 8: 4248c2ecf20Sopenharmony_ci bpp = 8; 4258c2ecf20Sopenharmony_ci if ((1000000 / var->pixclock) > DACSPEED8) { 4268c2ecf20Sopenharmony_ci dev_err(info->device, "requested pixclock %i MHz out of range (max. %i MHz at 8bpp)\n", 4278c2ecf20Sopenharmony_ci 1000000 / var->pixclock, DACSPEED8); 4288c2ecf20Sopenharmony_ci return -EINVAL; 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci break; 4318c2ecf20Sopenharmony_ci case 9 ... 15: 4328c2ecf20Sopenharmony_ci bpp = 15; 4338c2ecf20Sopenharmony_ci fallthrough; 4348c2ecf20Sopenharmony_ci case 16: 4358c2ecf20Sopenharmony_ci if ((1000000 / var->pixclock) > DACSPEED16) { 4368c2ecf20Sopenharmony_ci dev_err(info->device, "requested pixclock %i MHz out of range (max. %i MHz at 15/16bpp)\n", 4378c2ecf20Sopenharmony_ci 1000000 / var->pixclock, DACSPEED16); 4388c2ecf20Sopenharmony_ci return -EINVAL; 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci break; 4418c2ecf20Sopenharmony_ci case 17 ... 24: 4428c2ecf20Sopenharmony_ci bpp = 24; 4438c2ecf20Sopenharmony_ci dacspeed24 = par->has_sgram ? DACSPEED24_SG : DACSPEED24_SD; 4448c2ecf20Sopenharmony_ci if ((1000000 / var->pixclock) > dacspeed24) { 4458c2ecf20Sopenharmony_ci dev_err(info->device, "requested pixclock %i MHz out of range (max. %i MHz at 24bpp)\n", 4468c2ecf20Sopenharmony_ci 1000000 / var->pixclock, dacspeed24); 4478c2ecf20Sopenharmony_ci return -EINVAL; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci break; 4508c2ecf20Sopenharmony_ci case 25 ... 32: 4518c2ecf20Sopenharmony_ci bpp = 32; 4528c2ecf20Sopenharmony_ci if ((1000000 / var->pixclock) > DACSPEED32) { 4538c2ecf20Sopenharmony_ci dev_err(info->device, "requested pixclock %i MHz out of range (max. %i MHz at 32bpp)\n", 4548c2ecf20Sopenharmony_ci 1000000 / var->pixclock, DACSPEED32); 4558c2ecf20Sopenharmony_ci return -EINVAL; 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci break; 4588c2ecf20Sopenharmony_ci default: 4598c2ecf20Sopenharmony_ci return -EINVAL; 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci xres = ALIGN(var->xres, 8); 4638c2ecf20Sopenharmony_ci vxres = ALIGN(var->xres_virtual, 16); 4648c2ecf20Sopenharmony_ci if (vxres < xres) 4658c2ecf20Sopenharmony_ci vxres = xres; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci xoffset = ALIGN(var->xoffset, 8); 4688c2ecf20Sopenharmony_ci if (xres + xoffset > vxres) 4698c2ecf20Sopenharmony_ci xoffset = vxres - xres; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci left = ALIGN(var->left_margin, 8); 4728c2ecf20Sopenharmony_ci right = ALIGN(var->right_margin, 8); 4738c2ecf20Sopenharmony_ci hslen = ALIGN(var->hsync_len, 8); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci yres = var->yres; 4768c2ecf20Sopenharmony_ci vyres = var->yres_virtual; 4778c2ecf20Sopenharmony_ci if (yres > vyres) 4788c2ecf20Sopenharmony_ci vyres = yres; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci yoffset = var->yoffset; 4818c2ecf20Sopenharmony_ci if (yres + yoffset > vyres) 4828c2ecf20Sopenharmony_ci yoffset = vyres - yres; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci lower = var->lower_margin; 4858c2ecf20Sopenharmony_ci vslen = var->vsync_len; 4868c2ecf20Sopenharmony_ci upper = var->upper_margin; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci mem = vxres * vyres * ((bpp + 1) / 8); 4898c2ecf20Sopenharmony_ci if (mem > info->screen_size) { 4908c2ecf20Sopenharmony_ci dev_err(info->device, "not enough video memory (%d KB requested, %ld KB available)\n", 4918c2ecf20Sopenharmony_ci mem >> 10, info->screen_size >> 10); 4928c2ecf20Sopenharmony_ci return -ENOMEM; 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci if (yoffset + yres > vyres) 4968c2ecf20Sopenharmony_ci yoffset = vyres - yres; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci xtotal = xres + right + hslen + left; 4998c2ecf20Sopenharmony_ci ytotal = yres + lower + vslen + upper; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci par->crtc[VGA_CRTC_H_TOTAL] = (xtotal >> 3) - 5; 5028c2ecf20Sopenharmony_ci par->crtc[VGA_CRTC_H_DISP] = (xres >> 3) - 1; 5038c2ecf20Sopenharmony_ci par->crtc[VGA_CRTC_H_BLANK_START] = ((xres + right) >> 3) - 1; 5048c2ecf20Sopenharmony_ci par->crtc[VGA_CRTC_H_SYNC_START] = (xres + right) >> 3; 5058c2ecf20Sopenharmony_ci par->crtc[VGA_CRTC_H_SYNC_END] = (((xres + right + hslen) >> 3) & 0x1F) 5068c2ecf20Sopenharmony_ci | ((((xres + right + hslen) >> 3) & 0x20) << 2); 5078c2ecf20Sopenharmony_ci par->crtc[VGA_CRTC_H_BLANK_END] = ((xres + right + hslen) >> 3 & 0x1F) 5088c2ecf20Sopenharmony_ci | 0x80; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci par->crtc[VGA_CRTC_V_TOTAL] = ytotal - 2; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci r7 = 0x10; /* disable linecompare */ 5138c2ecf20Sopenharmony_ci if (ytotal & 0x100) 5148c2ecf20Sopenharmony_ci r7 |= 0x01; 5158c2ecf20Sopenharmony_ci if (ytotal & 0x200) 5168c2ecf20Sopenharmony_ci r7 |= 0x20; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci par->crtc[VGA_CRTC_PRESET_ROW] = 0; 5198c2ecf20Sopenharmony_ci par->crtc[VGA_CRTC_MAX_SCAN] = 0x40; /* 1 scanline, no linecmp */ 5208c2ecf20Sopenharmony_ci if (var->vmode & FB_VMODE_DOUBLE) 5218c2ecf20Sopenharmony_ci par->crtc[VGA_CRTC_MAX_SCAN] |= 0x80; 5228c2ecf20Sopenharmony_ci par->crtc[VGA_CRTC_CURSOR_START] = 0x00; 5238c2ecf20Sopenharmony_ci par->crtc[VGA_CRTC_CURSOR_END] = 0x00; 5248c2ecf20Sopenharmony_ci par->crtc[VGA_CRTC_CURSOR_HI] = 0x00; 5258c2ecf20Sopenharmony_ci par->crtc[VGA_CRTC_CURSOR_LO] = 0x00; 5268c2ecf20Sopenharmony_ci par->crtc[VGA_CRTC_V_DISP_END] = yres-1; 5278c2ecf20Sopenharmony_ci if ((yres-1) & 0x100) 5288c2ecf20Sopenharmony_ci r7 |= 0x02; 5298c2ecf20Sopenharmony_ci if ((yres-1) & 0x200) 5308c2ecf20Sopenharmony_ci r7 |= 0x40; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci par->crtc[VGA_CRTC_V_BLANK_START] = yres + lower - 1; 5338c2ecf20Sopenharmony_ci par->crtc[VGA_CRTC_V_SYNC_START] = yres + lower - 1; 5348c2ecf20Sopenharmony_ci if ((yres + lower - 1) & 0x100) 5358c2ecf20Sopenharmony_ci r7 |= 0x0C; 5368c2ecf20Sopenharmony_ci if ((yres + lower - 1) & 0x200) { 5378c2ecf20Sopenharmony_ci par->crtc[VGA_CRTC_MAX_SCAN] |= 0x20; 5388c2ecf20Sopenharmony_ci r7 |= 0x80; 5398c2ecf20Sopenharmony_ci } 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci /* disabled IRQ */ 5428c2ecf20Sopenharmony_ci par->crtc[VGA_CRTC_V_SYNC_END] = 5438c2ecf20Sopenharmony_ci ((yres + lower - 1 + vslen) & 0x0F) & ~0x10; 5448c2ecf20Sopenharmony_ci /* 0x7F for VGA, but some SVGA chips require all 8 bits to be set */ 5458c2ecf20Sopenharmony_ci par->crtc[VGA_CRTC_V_BLANK_END] = (yres + lower - 1 + vslen) & 0xFF; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci par->crtc[VGA_CRTC_UNDERLINE] = 0x00; 5488c2ecf20Sopenharmony_ci par->crtc[VGA_CRTC_MODE] = 0xC3 ; 5498c2ecf20Sopenharmony_ci par->crtc[VGA_CRTC_LINE_COMPARE] = 0xFF; 5508c2ecf20Sopenharmony_ci par->crtc[VGA_CRTC_OVERFLOW] = r7; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci par->vss = 0x00; /* 3DA */ 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci for (i = 0x00; i < 0x10; i++) 5558c2ecf20Sopenharmony_ci par->atc[i] = i; 5568c2ecf20Sopenharmony_ci par->atc[VGA_ATC_MODE] = 0x81; 5578c2ecf20Sopenharmony_ci par->atc[VGA_ATC_OVERSCAN] = 0x00; /* 0 for EGA, 0xFF for VGA */ 5588c2ecf20Sopenharmony_ci par->atc[VGA_ATC_PLANE_ENABLE] = 0x0F; 5598c2ecf20Sopenharmony_ci par->atc[VGA_ATC_COLOR_PAGE] = 0x00; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci par->misc = 0xC3; 5628c2ecf20Sopenharmony_ci if (var->sync & FB_SYNC_HOR_HIGH_ACT) 5638c2ecf20Sopenharmony_ci par->misc &= ~0x40; 5648c2ecf20Sopenharmony_ci if (var->sync & FB_SYNC_VERT_HIGH_ACT) 5658c2ecf20Sopenharmony_ci par->misc &= ~0x80; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci par->seq[VGA_SEQ_CLOCK_MODE] = 0x01; 5688c2ecf20Sopenharmony_ci par->seq[VGA_SEQ_PLANE_WRITE] = 0x0F; 5698c2ecf20Sopenharmony_ci par->seq[VGA_SEQ_CHARACTER_MAP] = 0x00; 5708c2ecf20Sopenharmony_ci par->seq[VGA_SEQ_MEMORY_MODE] = 0x06; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci par->gdc[VGA_GFX_SR_VALUE] = 0x00; 5738c2ecf20Sopenharmony_ci par->gdc[VGA_GFX_SR_ENABLE] = 0x00; 5748c2ecf20Sopenharmony_ci par->gdc[VGA_GFX_COMPARE_VALUE] = 0x00; 5758c2ecf20Sopenharmony_ci par->gdc[VGA_GFX_DATA_ROTATE] = 0x00; 5768c2ecf20Sopenharmony_ci par->gdc[VGA_GFX_PLANE_READ] = 0; 5778c2ecf20Sopenharmony_ci par->gdc[VGA_GFX_MODE] = 0x02; 5788c2ecf20Sopenharmony_ci par->gdc[VGA_GFX_MISC] = 0x05; 5798c2ecf20Sopenharmony_ci par->gdc[VGA_GFX_COMPARE_MASK] = 0x0F; 5808c2ecf20Sopenharmony_ci par->gdc[VGA_GFX_BIT_MASK] = 0xFF; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci base = (yoffset * vxres + (xoffset & ~7)) >> 2; 5838c2ecf20Sopenharmony_ci switch (bpp) { 5848c2ecf20Sopenharmony_ci case 8: 5858c2ecf20Sopenharmony_ci par->crtc[VGA_CRTC_OFFSET] = vxres >> 3; 5868c2ecf20Sopenharmony_ci par->ext_offset = vxres >> 11; 5878c2ecf20Sopenharmony_ci par->pixelpipe_cfg1 = DISPLAY_8BPP_MODE; 5888c2ecf20Sopenharmony_ci par->bitblt_cntl = COLEXP_8BPP; 5898c2ecf20Sopenharmony_ci break; 5908c2ecf20Sopenharmony_ci case 15: /* 0rrrrrgg gggbbbbb */ 5918c2ecf20Sopenharmony_ci case 16: /* rrrrrggg gggbbbbb */ 5928c2ecf20Sopenharmony_ci par->pixelpipe_cfg1 = (var->green.length == 6) ? 5938c2ecf20Sopenharmony_ci DISPLAY_16BPP_MODE : DISPLAY_15BPP_MODE; 5948c2ecf20Sopenharmony_ci par->crtc[VGA_CRTC_OFFSET] = vxres >> 2; 5958c2ecf20Sopenharmony_ci par->ext_offset = vxres >> 10; 5968c2ecf20Sopenharmony_ci par->bitblt_cntl = COLEXP_16BPP; 5978c2ecf20Sopenharmony_ci base *= 2; 5988c2ecf20Sopenharmony_ci break; 5998c2ecf20Sopenharmony_ci case 24: 6008c2ecf20Sopenharmony_ci par->crtc[VGA_CRTC_OFFSET] = (vxres * 3) >> 3; 6018c2ecf20Sopenharmony_ci par->ext_offset = (vxres * 3) >> 11; 6028c2ecf20Sopenharmony_ci par->pixelpipe_cfg1 = DISPLAY_24BPP_MODE; 6038c2ecf20Sopenharmony_ci par->bitblt_cntl = COLEXP_24BPP; 6048c2ecf20Sopenharmony_ci base &= 0xFFFFFFFE; /* ...ignore the last bit. */ 6058c2ecf20Sopenharmony_ci base *= 3; 6068c2ecf20Sopenharmony_ci break; 6078c2ecf20Sopenharmony_ci case 32: 6088c2ecf20Sopenharmony_ci par->crtc[VGA_CRTC_OFFSET] = vxres >> 1; 6098c2ecf20Sopenharmony_ci par->ext_offset = vxres >> 9; 6108c2ecf20Sopenharmony_ci par->pixelpipe_cfg1 = DISPLAY_32BPP_MODE; 6118c2ecf20Sopenharmony_ci par->bitblt_cntl = COLEXP_RESERVED; /* Unimplemented on i740 */ 6128c2ecf20Sopenharmony_ci base *= 4; 6138c2ecf20Sopenharmony_ci break; 6148c2ecf20Sopenharmony_ci } 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci par->crtc[VGA_CRTC_START_LO] = base & 0x000000FF; 6178c2ecf20Sopenharmony_ci par->crtc[VGA_CRTC_START_HI] = (base & 0x0000FF00) >> 8; 6188c2ecf20Sopenharmony_ci par->ext_start_addr = 6198c2ecf20Sopenharmony_ci ((base & 0x003F0000) >> 16) | EXT_START_ADDR_ENABLE; 6208c2ecf20Sopenharmony_ci par->ext_start_addr_hi = (base & 0x3FC00000) >> 22; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci par->pixelpipe_cfg0 = DAC_8_BIT; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci par->pixelpipe_cfg2 = DISPLAY_GAMMA_ENABLE | OVERLAY_GAMMA_ENABLE; 6258c2ecf20Sopenharmony_ci par->io_cntl = EXTENDED_CRTC_CNTL; 6268c2ecf20Sopenharmony_ci par->address_mapping = LINEAR_MODE_ENABLE | PAGE_MAPPING_ENABLE; 6278c2ecf20Sopenharmony_ci par->display_cntl = HIRES_MODE; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci /* Set the MCLK freq */ 6308c2ecf20Sopenharmony_ci par->pll_cntl = PLL_MEMCLK_100000KHZ; /* 100 MHz -- use as default */ 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci /* Calculate the extended CRTC regs */ 6338c2ecf20Sopenharmony_ci par->ext_vert_total = (ytotal - 2) >> 8; 6348c2ecf20Sopenharmony_ci par->ext_vert_disp_end = (yres - 1) >> 8; 6358c2ecf20Sopenharmony_ci par->ext_vert_sync_start = (yres + lower) >> 8; 6368c2ecf20Sopenharmony_ci par->ext_vert_blank_start = (yres + lower) >> 8; 6378c2ecf20Sopenharmony_ci par->ext_horiz_total = ((xtotal >> 3) - 5) >> 8; 6388c2ecf20Sopenharmony_ci par->ext_horiz_blank = (((xres + right) >> 3) & 0x40) >> 6; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci par->interlace_cntl = INTERLACE_DISABLE; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci /* Set the overscan color to 0. (NOTE: This only affects >8bpp mode) */ 6438c2ecf20Sopenharmony_ci par->atc[VGA_ATC_OVERSCAN] = 0; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci /* Calculate VCLK that most closely matches the requested dot clock */ 6468c2ecf20Sopenharmony_ci freq = (((u32)1e9) / var->pixclock) * (u32)(1e3); 6478c2ecf20Sopenharmony_ci if (freq < I740_RFREQ_FIX) { 6488c2ecf20Sopenharmony_ci fb_dbg(info, "invalid pixclock\n"); 6498c2ecf20Sopenharmony_ci freq = I740_RFREQ_FIX; 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci i740_calc_vclk(freq, par); 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci /* Since we program the clocks ourselves, always use VCLK2. */ 6548c2ecf20Sopenharmony_ci par->misc |= 0x0C; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci /* Calculate the FIFO Watermark and Burst Length. */ 6578c2ecf20Sopenharmony_ci par->lmi_fifo_watermark = 6588c2ecf20Sopenharmony_ci i740_calc_fifo(par, 1000000 / var->pixclock, bpp); 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci return 0; 6618c2ecf20Sopenharmony_ci} 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_cistatic int i740fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) 6648c2ecf20Sopenharmony_ci{ 6658c2ecf20Sopenharmony_ci if (!var->pixclock) 6668c2ecf20Sopenharmony_ci return -EINVAL; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci switch (var->bits_per_pixel) { 6698c2ecf20Sopenharmony_ci case 8: 6708c2ecf20Sopenharmony_ci var->red.offset = var->green.offset = var->blue.offset = 0; 6718c2ecf20Sopenharmony_ci var->red.length = var->green.length = var->blue.length = 8; 6728c2ecf20Sopenharmony_ci break; 6738c2ecf20Sopenharmony_ci case 16: 6748c2ecf20Sopenharmony_ci switch (var->green.length) { 6758c2ecf20Sopenharmony_ci default: 6768c2ecf20Sopenharmony_ci case 5: 6778c2ecf20Sopenharmony_ci var->red.offset = 10; 6788c2ecf20Sopenharmony_ci var->green.offset = 5; 6798c2ecf20Sopenharmony_ci var->blue.offset = 0; 6808c2ecf20Sopenharmony_ci var->red.length = 5; 6818c2ecf20Sopenharmony_ci var->green.length = 5; 6828c2ecf20Sopenharmony_ci var->blue.length = 5; 6838c2ecf20Sopenharmony_ci break; 6848c2ecf20Sopenharmony_ci case 6: 6858c2ecf20Sopenharmony_ci var->red.offset = 11; 6868c2ecf20Sopenharmony_ci var->green.offset = 5; 6878c2ecf20Sopenharmony_ci var->blue.offset = 0; 6888c2ecf20Sopenharmony_ci var->red.length = var->blue.length = 5; 6898c2ecf20Sopenharmony_ci break; 6908c2ecf20Sopenharmony_ci } 6918c2ecf20Sopenharmony_ci break; 6928c2ecf20Sopenharmony_ci case 24: 6938c2ecf20Sopenharmony_ci var->red.offset = 16; 6948c2ecf20Sopenharmony_ci var->green.offset = 8; 6958c2ecf20Sopenharmony_ci var->blue.offset = 0; 6968c2ecf20Sopenharmony_ci var->red.length = var->green.length = var->blue.length = 8; 6978c2ecf20Sopenharmony_ci break; 6988c2ecf20Sopenharmony_ci case 32: 6998c2ecf20Sopenharmony_ci var->transp.offset = 24; 7008c2ecf20Sopenharmony_ci var->red.offset = 16; 7018c2ecf20Sopenharmony_ci var->green.offset = 8; 7028c2ecf20Sopenharmony_ci var->blue.offset = 0; 7038c2ecf20Sopenharmony_ci var->transp.length = 8; 7048c2ecf20Sopenharmony_ci var->red.length = var->green.length = var->blue.length = 8; 7058c2ecf20Sopenharmony_ci break; 7068c2ecf20Sopenharmony_ci default: 7078c2ecf20Sopenharmony_ci return -EINVAL; 7088c2ecf20Sopenharmony_ci } 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci if (var->xres > var->xres_virtual) 7118c2ecf20Sopenharmony_ci var->xres_virtual = var->xres; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci if (var->yres > var->yres_virtual) 7148c2ecf20Sopenharmony_ci var->yres_virtual = var->yres; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci if (info->monspecs.hfmax && info->monspecs.vfmax && 7178c2ecf20Sopenharmony_ci info->monspecs.dclkmax && fb_validate_mode(var, info) < 0) 7188c2ecf20Sopenharmony_ci return -EINVAL; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci return 0; 7218c2ecf20Sopenharmony_ci} 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_cistatic void vga_protect(struct i740fb_par *par) 7248c2ecf20Sopenharmony_ci{ 7258c2ecf20Sopenharmony_ci /* disable the display */ 7268c2ecf20Sopenharmony_ci i740outreg_mask(par, VGA_SEQ_I, VGA_SEQ_CLOCK_MODE, 0x20, 0x20); 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci i740inb(par, 0x3DA); 7298c2ecf20Sopenharmony_ci i740outb(par, VGA_ATT_W, 0x00); /* enable palette access */ 7308c2ecf20Sopenharmony_ci} 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_cistatic void vga_unprotect(struct i740fb_par *par) 7338c2ecf20Sopenharmony_ci{ 7348c2ecf20Sopenharmony_ci /* reenable display */ 7358c2ecf20Sopenharmony_ci i740outreg_mask(par, VGA_SEQ_I, VGA_SEQ_CLOCK_MODE, 0, 0x20); 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci i740inb(par, 0x3DA); 7388c2ecf20Sopenharmony_ci i740outb(par, VGA_ATT_W, 0x20); /* disable palette access */ 7398c2ecf20Sopenharmony_ci} 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_cistatic int i740fb_set_par(struct fb_info *info) 7428c2ecf20Sopenharmony_ci{ 7438c2ecf20Sopenharmony_ci struct i740fb_par *par = info->par; 7448c2ecf20Sopenharmony_ci u32 itemp; 7458c2ecf20Sopenharmony_ci int i; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci i = i740fb_decode_var(&info->var, par, info); 7488c2ecf20Sopenharmony_ci if (i) 7498c2ecf20Sopenharmony_ci return i; 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci memset(info->screen_base, 0, info->screen_size); 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci vga_protect(par); 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci i740outreg(par, XRX, DRAM_EXT_CNTL, DRAM_REFRESH_DISABLE); 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci mdelay(1); 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci i740outreg(par, XRX, VCLK2_VCO_M, par->video_clk2_m); 7608c2ecf20Sopenharmony_ci i740outreg(par, XRX, VCLK2_VCO_N, par->video_clk2_n); 7618c2ecf20Sopenharmony_ci i740outreg(par, XRX, VCLK2_VCO_MN_MSBS, par->video_clk2_mn_msbs); 7628c2ecf20Sopenharmony_ci i740outreg(par, XRX, VCLK2_VCO_DIV_SEL, par->video_clk2_div_sel); 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci i740outreg_mask(par, XRX, PIXPIPE_CONFIG_0, 7658c2ecf20Sopenharmony_ci par->pixelpipe_cfg0 & DAC_8_BIT, 0x80); 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci i740inb(par, 0x3DA); 7688c2ecf20Sopenharmony_ci i740outb(par, 0x3C0, 0x00); 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci /* update misc output register */ 7718c2ecf20Sopenharmony_ci i740outb(par, VGA_MIS_W, par->misc | 0x01); 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci /* synchronous reset on */ 7748c2ecf20Sopenharmony_ci i740outreg(par, VGA_SEQ_I, VGA_SEQ_RESET, 0x01); 7758c2ecf20Sopenharmony_ci /* write sequencer registers */ 7768c2ecf20Sopenharmony_ci i740outreg(par, VGA_SEQ_I, VGA_SEQ_CLOCK_MODE, 7778c2ecf20Sopenharmony_ci par->seq[VGA_SEQ_CLOCK_MODE] | 0x20); 7788c2ecf20Sopenharmony_ci for (i = 2; i < VGA_SEQ_C; i++) 7798c2ecf20Sopenharmony_ci i740outreg(par, VGA_SEQ_I, i, par->seq[i]); 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci /* synchronous reset off */ 7828c2ecf20Sopenharmony_ci i740outreg(par, VGA_SEQ_I, VGA_SEQ_RESET, 0x03); 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci /* deprotect CRT registers 0-7 */ 7858c2ecf20Sopenharmony_ci i740outreg(par, VGA_CRT_IC, VGA_CRTC_V_SYNC_END, 7868c2ecf20Sopenharmony_ci par->crtc[VGA_CRTC_V_SYNC_END]); 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci /* write CRT registers */ 7898c2ecf20Sopenharmony_ci for (i = 0; i < VGA_CRT_C; i++) 7908c2ecf20Sopenharmony_ci i740outreg(par, VGA_CRT_IC, i, par->crtc[i]); 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci /* write graphics controller registers */ 7938c2ecf20Sopenharmony_ci for (i = 0; i < VGA_GFX_C; i++) 7948c2ecf20Sopenharmony_ci i740outreg(par, VGA_GFX_I, i, par->gdc[i]); 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci /* write attribute controller registers */ 7978c2ecf20Sopenharmony_ci for (i = 0; i < VGA_ATT_C; i++) { 7988c2ecf20Sopenharmony_ci i740inb(par, VGA_IS1_RC); /* reset flip-flop */ 7998c2ecf20Sopenharmony_ci i740outb(par, VGA_ATT_IW, i); 8008c2ecf20Sopenharmony_ci i740outb(par, VGA_ATT_IW, par->atc[i]); 8018c2ecf20Sopenharmony_ci } 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci i740inb(par, VGA_IS1_RC); 8048c2ecf20Sopenharmony_ci i740outb(par, VGA_ATT_IW, 0x20); 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci i740outreg(par, VGA_CRT_IC, EXT_VERT_TOTAL, par->ext_vert_total); 8078c2ecf20Sopenharmony_ci i740outreg(par, VGA_CRT_IC, EXT_VERT_DISPLAY, par->ext_vert_disp_end); 8088c2ecf20Sopenharmony_ci i740outreg(par, VGA_CRT_IC, EXT_VERT_SYNC_START, 8098c2ecf20Sopenharmony_ci par->ext_vert_sync_start); 8108c2ecf20Sopenharmony_ci i740outreg(par, VGA_CRT_IC, EXT_VERT_BLANK_START, 8118c2ecf20Sopenharmony_ci par->ext_vert_blank_start); 8128c2ecf20Sopenharmony_ci i740outreg(par, VGA_CRT_IC, EXT_HORIZ_TOTAL, par->ext_horiz_total); 8138c2ecf20Sopenharmony_ci i740outreg(par, VGA_CRT_IC, EXT_HORIZ_BLANK, par->ext_horiz_blank); 8148c2ecf20Sopenharmony_ci i740outreg(par, VGA_CRT_IC, EXT_OFFSET, par->ext_offset); 8158c2ecf20Sopenharmony_ci i740outreg(par, VGA_CRT_IC, EXT_START_ADDR_HI, par->ext_start_addr_hi); 8168c2ecf20Sopenharmony_ci i740outreg(par, VGA_CRT_IC, EXT_START_ADDR, par->ext_start_addr); 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci i740outreg_mask(par, VGA_CRT_IC, INTERLACE_CNTL, 8198c2ecf20Sopenharmony_ci par->interlace_cntl, INTERLACE_ENABLE); 8208c2ecf20Sopenharmony_ci i740outreg_mask(par, XRX, ADDRESS_MAPPING, par->address_mapping, 0x1F); 8218c2ecf20Sopenharmony_ci i740outreg_mask(par, XRX, BITBLT_CNTL, par->bitblt_cntl, COLEXP_MODE); 8228c2ecf20Sopenharmony_ci i740outreg_mask(par, XRX, DISPLAY_CNTL, 8238c2ecf20Sopenharmony_ci par->display_cntl, VGA_WRAP_MODE | GUI_MODE); 8248c2ecf20Sopenharmony_ci i740outreg_mask(par, XRX, PIXPIPE_CONFIG_0, par->pixelpipe_cfg0, 0x9B); 8258c2ecf20Sopenharmony_ci i740outreg_mask(par, XRX, PIXPIPE_CONFIG_2, par->pixelpipe_cfg2, 0x0C); 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci i740outreg(par, XRX, PLL_CNTL, par->pll_cntl); 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci i740outreg_mask(par, XRX, PIXPIPE_CONFIG_1, 8308c2ecf20Sopenharmony_ci par->pixelpipe_cfg1, DISPLAY_COLOR_MODE); 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci itemp = readl(par->regs + FWATER_BLC); 8338c2ecf20Sopenharmony_ci itemp &= ~(LMI_BURST_LENGTH | LMI_FIFO_WATERMARK); 8348c2ecf20Sopenharmony_ci itemp |= par->lmi_fifo_watermark; 8358c2ecf20Sopenharmony_ci writel(itemp, par->regs + FWATER_BLC); 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci i740outreg(par, XRX, DRAM_EXT_CNTL, DRAM_REFRESH_60HZ); 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci i740outreg_mask(par, MRX, COL_KEY_CNTL_1, 0, BLANK_DISP_OVERLAY); 8408c2ecf20Sopenharmony_ci i740outreg_mask(par, XRX, IO_CTNL, 8418c2ecf20Sopenharmony_ci par->io_cntl, EXTENDED_ATTR_CNTL | EXTENDED_CRTC_CNTL); 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci if (par->pixelpipe_cfg1 != DISPLAY_8BPP_MODE) { 8448c2ecf20Sopenharmony_ci i740outb(par, VGA_PEL_MSK, 0xFF); 8458c2ecf20Sopenharmony_ci i740outb(par, VGA_PEL_IW, 0x00); 8468c2ecf20Sopenharmony_ci for (i = 0; i < 256; i++) { 8478c2ecf20Sopenharmony_ci itemp = (par->pixelpipe_cfg0 & DAC_8_BIT) ? i : i >> 2; 8488c2ecf20Sopenharmony_ci i740outb(par, VGA_PEL_D, itemp); 8498c2ecf20Sopenharmony_ci i740outb(par, VGA_PEL_D, itemp); 8508c2ecf20Sopenharmony_ci i740outb(par, VGA_PEL_D, itemp); 8518c2ecf20Sopenharmony_ci } 8528c2ecf20Sopenharmony_ci } 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci /* Wait for screen to stabilize. */ 8558c2ecf20Sopenharmony_ci mdelay(50); 8568c2ecf20Sopenharmony_ci vga_unprotect(par); 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci info->fix.line_length = 8598c2ecf20Sopenharmony_ci info->var.xres_virtual * info->var.bits_per_pixel / 8; 8608c2ecf20Sopenharmony_ci if (info->var.bits_per_pixel == 8) 8618c2ecf20Sopenharmony_ci info->fix.visual = FB_VISUAL_PSEUDOCOLOR; 8628c2ecf20Sopenharmony_ci else 8638c2ecf20Sopenharmony_ci info->fix.visual = FB_VISUAL_TRUECOLOR; 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci return 0; 8668c2ecf20Sopenharmony_ci} 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_cistatic int i740fb_setcolreg(unsigned regno, unsigned red, unsigned green, 8698c2ecf20Sopenharmony_ci unsigned blue, unsigned transp, 8708c2ecf20Sopenharmony_ci struct fb_info *info) 8718c2ecf20Sopenharmony_ci{ 8728c2ecf20Sopenharmony_ci u32 r, g, b; 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci dev_dbg(info->device, "setcolreg: regno: %i, red=%d, green=%d, blue=%d, transp=%d, bpp=%d\n", 8758c2ecf20Sopenharmony_ci regno, red, green, blue, transp, info->var.bits_per_pixel); 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci switch (info->fix.visual) { 8788c2ecf20Sopenharmony_ci case FB_VISUAL_PSEUDOCOLOR: 8798c2ecf20Sopenharmony_ci if (regno >= 256) 8808c2ecf20Sopenharmony_ci return -EINVAL; 8818c2ecf20Sopenharmony_ci i740outb(info->par, VGA_PEL_IW, regno); 8828c2ecf20Sopenharmony_ci i740outb(info->par, VGA_PEL_D, red >> 8); 8838c2ecf20Sopenharmony_ci i740outb(info->par, VGA_PEL_D, green >> 8); 8848c2ecf20Sopenharmony_ci i740outb(info->par, VGA_PEL_D, blue >> 8); 8858c2ecf20Sopenharmony_ci break; 8868c2ecf20Sopenharmony_ci case FB_VISUAL_TRUECOLOR: 8878c2ecf20Sopenharmony_ci if (regno >= 16) 8888c2ecf20Sopenharmony_ci return -EINVAL; 8898c2ecf20Sopenharmony_ci r = (red >> (16 - info->var.red.length)) 8908c2ecf20Sopenharmony_ci << info->var.red.offset; 8918c2ecf20Sopenharmony_ci b = (blue >> (16 - info->var.blue.length)) 8928c2ecf20Sopenharmony_ci << info->var.blue.offset; 8938c2ecf20Sopenharmony_ci g = (green >> (16 - info->var.green.length)) 8948c2ecf20Sopenharmony_ci << info->var.green.offset; 8958c2ecf20Sopenharmony_ci ((u32 *) info->pseudo_palette)[regno] = r | g | b; 8968c2ecf20Sopenharmony_ci break; 8978c2ecf20Sopenharmony_ci default: 8988c2ecf20Sopenharmony_ci return -EINVAL; 8998c2ecf20Sopenharmony_ci } 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci return 0; 9028c2ecf20Sopenharmony_ci} 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_cistatic int i740fb_pan_display(struct fb_var_screeninfo *var, 9058c2ecf20Sopenharmony_ci struct fb_info *info) 9068c2ecf20Sopenharmony_ci{ 9078c2ecf20Sopenharmony_ci struct i740fb_par *par = info->par; 9088c2ecf20Sopenharmony_ci u32 base = (var->yoffset * info->var.xres_virtual 9098c2ecf20Sopenharmony_ci + (var->xoffset & ~7)) >> 2; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci dev_dbg(info->device, "pan_display: xoffset: %i yoffset: %i base: %i\n", 9128c2ecf20Sopenharmony_ci var->xoffset, var->yoffset, base); 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci switch (info->var.bits_per_pixel) { 9158c2ecf20Sopenharmony_ci case 8: 9168c2ecf20Sopenharmony_ci break; 9178c2ecf20Sopenharmony_ci case 15: 9188c2ecf20Sopenharmony_ci case 16: 9198c2ecf20Sopenharmony_ci base *= 2; 9208c2ecf20Sopenharmony_ci break; 9218c2ecf20Sopenharmony_ci case 24: 9228c2ecf20Sopenharmony_ci /* 9238c2ecf20Sopenharmony_ci * The last bit does not seem to have any effect on the start 9248c2ecf20Sopenharmony_ci * address register in 24bpp mode, so... 9258c2ecf20Sopenharmony_ci */ 9268c2ecf20Sopenharmony_ci base &= 0xFFFFFFFE; /* ...ignore the last bit. */ 9278c2ecf20Sopenharmony_ci base *= 3; 9288c2ecf20Sopenharmony_ci break; 9298c2ecf20Sopenharmony_ci case 32: 9308c2ecf20Sopenharmony_ci base *= 4; 9318c2ecf20Sopenharmony_ci break; 9328c2ecf20Sopenharmony_ci } 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci par->crtc[VGA_CRTC_START_LO] = base & 0x000000FF; 9358c2ecf20Sopenharmony_ci par->crtc[VGA_CRTC_START_HI] = (base & 0x0000FF00) >> 8; 9368c2ecf20Sopenharmony_ci par->ext_start_addr_hi = (base & 0x3FC00000) >> 22; 9378c2ecf20Sopenharmony_ci par->ext_start_addr = 9388c2ecf20Sopenharmony_ci ((base & 0x003F0000) >> 16) | EXT_START_ADDR_ENABLE; 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci i740outreg(par, VGA_CRT_IC, VGA_CRTC_START_LO, base & 0x000000FF); 9418c2ecf20Sopenharmony_ci i740outreg(par, VGA_CRT_IC, VGA_CRTC_START_HI, 9428c2ecf20Sopenharmony_ci (base & 0x0000FF00) >> 8); 9438c2ecf20Sopenharmony_ci i740outreg(par, VGA_CRT_IC, EXT_START_ADDR_HI, 9448c2ecf20Sopenharmony_ci (base & 0x3FC00000) >> 22); 9458c2ecf20Sopenharmony_ci i740outreg(par, VGA_CRT_IC, EXT_START_ADDR, 9468c2ecf20Sopenharmony_ci ((base & 0x003F0000) >> 16) | EXT_START_ADDR_ENABLE); 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci return 0; 9498c2ecf20Sopenharmony_ci} 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_cistatic int i740fb_blank(int blank_mode, struct fb_info *info) 9528c2ecf20Sopenharmony_ci{ 9538c2ecf20Sopenharmony_ci struct i740fb_par *par = info->par; 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci unsigned char SEQ01; 9568c2ecf20Sopenharmony_ci int DPMSSyncSelect; 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci switch (blank_mode) { 9598c2ecf20Sopenharmony_ci case FB_BLANK_UNBLANK: 9608c2ecf20Sopenharmony_ci case FB_BLANK_NORMAL: 9618c2ecf20Sopenharmony_ci SEQ01 = 0x00; 9628c2ecf20Sopenharmony_ci DPMSSyncSelect = HSYNC_ON | VSYNC_ON; 9638c2ecf20Sopenharmony_ci break; 9648c2ecf20Sopenharmony_ci case FB_BLANK_VSYNC_SUSPEND: 9658c2ecf20Sopenharmony_ci SEQ01 = 0x20; 9668c2ecf20Sopenharmony_ci DPMSSyncSelect = HSYNC_ON | VSYNC_OFF; 9678c2ecf20Sopenharmony_ci break; 9688c2ecf20Sopenharmony_ci case FB_BLANK_HSYNC_SUSPEND: 9698c2ecf20Sopenharmony_ci SEQ01 = 0x20; 9708c2ecf20Sopenharmony_ci DPMSSyncSelect = HSYNC_OFF | VSYNC_ON; 9718c2ecf20Sopenharmony_ci break; 9728c2ecf20Sopenharmony_ci case FB_BLANK_POWERDOWN: 9738c2ecf20Sopenharmony_ci SEQ01 = 0x20; 9748c2ecf20Sopenharmony_ci DPMSSyncSelect = HSYNC_OFF | VSYNC_OFF; 9758c2ecf20Sopenharmony_ci break; 9768c2ecf20Sopenharmony_ci default: 9778c2ecf20Sopenharmony_ci return -EINVAL; 9788c2ecf20Sopenharmony_ci } 9798c2ecf20Sopenharmony_ci /* Turn the screen on/off */ 9808c2ecf20Sopenharmony_ci i740outb(par, SRX, 0x01); 9818c2ecf20Sopenharmony_ci SEQ01 |= i740inb(par, SRX + 1) & ~0x20; 9828c2ecf20Sopenharmony_ci i740outb(par, SRX, 0x01); 9838c2ecf20Sopenharmony_ci i740outb(par, SRX + 1, SEQ01); 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci /* Set the DPMS mode */ 9868c2ecf20Sopenharmony_ci i740outreg(par, XRX, DPMS_SYNC_SELECT, DPMSSyncSelect); 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci /* Let fbcon do a soft blank for us */ 9898c2ecf20Sopenharmony_ci return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0; 9908c2ecf20Sopenharmony_ci} 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_cistatic const struct fb_ops i740fb_ops = { 9938c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 9948c2ecf20Sopenharmony_ci .fb_open = i740fb_open, 9958c2ecf20Sopenharmony_ci .fb_release = i740fb_release, 9968c2ecf20Sopenharmony_ci .fb_check_var = i740fb_check_var, 9978c2ecf20Sopenharmony_ci .fb_set_par = i740fb_set_par, 9988c2ecf20Sopenharmony_ci .fb_setcolreg = i740fb_setcolreg, 9998c2ecf20Sopenharmony_ci .fb_blank = i740fb_blank, 10008c2ecf20Sopenharmony_ci .fb_pan_display = i740fb_pan_display, 10018c2ecf20Sopenharmony_ci .fb_fillrect = cfb_fillrect, 10028c2ecf20Sopenharmony_ci .fb_copyarea = cfb_copyarea, 10038c2ecf20Sopenharmony_ci .fb_imageblit = cfb_imageblit, 10048c2ecf20Sopenharmony_ci}; 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */ 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_cistatic int i740fb_probe(struct pci_dev *dev, const struct pci_device_id *ent) 10098c2ecf20Sopenharmony_ci{ 10108c2ecf20Sopenharmony_ci struct fb_info *info; 10118c2ecf20Sopenharmony_ci struct i740fb_par *par; 10128c2ecf20Sopenharmony_ci int ret, tmp; 10138c2ecf20Sopenharmony_ci bool found = false; 10148c2ecf20Sopenharmony_ci u8 *edid; 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci info = framebuffer_alloc(sizeof(struct i740fb_par), &(dev->dev)); 10178c2ecf20Sopenharmony_ci if (!info) 10188c2ecf20Sopenharmony_ci return -ENOMEM; 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci par = info->par; 10218c2ecf20Sopenharmony_ci mutex_init(&par->open_lock); 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci info->var.activate = FB_ACTIVATE_NOW; 10248c2ecf20Sopenharmony_ci info->var.bits_per_pixel = 8; 10258c2ecf20Sopenharmony_ci info->fbops = &i740fb_ops; 10268c2ecf20Sopenharmony_ci info->pseudo_palette = par->pseudo_palette; 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci ret = pci_enable_device(dev); 10298c2ecf20Sopenharmony_ci if (ret) { 10308c2ecf20Sopenharmony_ci dev_err(info->device, "cannot enable PCI device\n"); 10318c2ecf20Sopenharmony_ci goto err_enable_device; 10328c2ecf20Sopenharmony_ci } 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci ret = pci_request_regions(dev, info->fix.id); 10358c2ecf20Sopenharmony_ci if (ret) { 10368c2ecf20Sopenharmony_ci dev_err(info->device, "error requesting regions\n"); 10378c2ecf20Sopenharmony_ci goto err_request_regions; 10388c2ecf20Sopenharmony_ci } 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci info->screen_base = pci_ioremap_wc_bar(dev, 0); 10418c2ecf20Sopenharmony_ci if (!info->screen_base) { 10428c2ecf20Sopenharmony_ci dev_err(info->device, "error remapping base\n"); 10438c2ecf20Sopenharmony_ci ret = -ENOMEM; 10448c2ecf20Sopenharmony_ci goto err_ioremap_1; 10458c2ecf20Sopenharmony_ci } 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci par->regs = pci_ioremap_bar(dev, 1); 10488c2ecf20Sopenharmony_ci if (!par->regs) { 10498c2ecf20Sopenharmony_ci dev_err(info->device, "error remapping MMIO\n"); 10508c2ecf20Sopenharmony_ci ret = -ENOMEM; 10518c2ecf20Sopenharmony_ci goto err_ioremap_2; 10528c2ecf20Sopenharmony_ci } 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci /* detect memory size */ 10558c2ecf20Sopenharmony_ci if ((i740inreg(par, XRX, DRAM_ROW_TYPE) & DRAM_ROW_1) 10568c2ecf20Sopenharmony_ci == DRAM_ROW_1_SDRAM) 10578c2ecf20Sopenharmony_ci i740outb(par, XRX, DRAM_ROW_BNDRY_1); 10588c2ecf20Sopenharmony_ci else 10598c2ecf20Sopenharmony_ci i740outb(par, XRX, DRAM_ROW_BNDRY_0); 10608c2ecf20Sopenharmony_ci info->screen_size = i740inb(par, XRX + 1) * 1024 * 1024; 10618c2ecf20Sopenharmony_ci /* detect memory type */ 10628c2ecf20Sopenharmony_ci tmp = i740inreg(par, XRX, DRAM_ROW_CNTL_LO); 10638c2ecf20Sopenharmony_ci par->has_sgram = !((tmp & DRAM_RAS_TIMING) || 10648c2ecf20Sopenharmony_ci (tmp & DRAM_RAS_PRECHARGE)); 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci fb_info(info, "Intel740 on %s, %ld KB %s\n", 10678c2ecf20Sopenharmony_ci pci_name(dev), info->screen_size >> 10, 10688c2ecf20Sopenharmony_ci par->has_sgram ? "SGRAM" : "SDRAM"); 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci info->fix = i740fb_fix; 10718c2ecf20Sopenharmony_ci info->fix.mmio_start = pci_resource_start(dev, 1); 10728c2ecf20Sopenharmony_ci info->fix.mmio_len = pci_resource_len(dev, 1); 10738c2ecf20Sopenharmony_ci info->fix.smem_start = pci_resource_start(dev, 0); 10748c2ecf20Sopenharmony_ci info->fix.smem_len = info->screen_size; 10758c2ecf20Sopenharmony_ci info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN; 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci if (i740fb_setup_ddc_bus(info) == 0) { 10788c2ecf20Sopenharmony_ci par->ddc_registered = true; 10798c2ecf20Sopenharmony_ci edid = fb_ddc_read(&par->ddc_adapter); 10808c2ecf20Sopenharmony_ci if (edid) { 10818c2ecf20Sopenharmony_ci fb_edid_to_monspecs(edid, &info->monspecs); 10828c2ecf20Sopenharmony_ci kfree(edid); 10838c2ecf20Sopenharmony_ci if (!info->monspecs.modedb) 10848c2ecf20Sopenharmony_ci dev_err(info->device, 10858c2ecf20Sopenharmony_ci "error getting mode database\n"); 10868c2ecf20Sopenharmony_ci else { 10878c2ecf20Sopenharmony_ci const struct fb_videomode *m; 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci fb_videomode_to_modelist( 10908c2ecf20Sopenharmony_ci info->monspecs.modedb, 10918c2ecf20Sopenharmony_ci info->monspecs.modedb_len, 10928c2ecf20Sopenharmony_ci &info->modelist); 10938c2ecf20Sopenharmony_ci m = fb_find_best_display(&info->monspecs, 10948c2ecf20Sopenharmony_ci &info->modelist); 10958c2ecf20Sopenharmony_ci if (m) { 10968c2ecf20Sopenharmony_ci fb_videomode_to_var(&info->var, m); 10978c2ecf20Sopenharmony_ci /* fill all other info->var's fields */ 10988c2ecf20Sopenharmony_ci if (!i740fb_check_var(&info->var, info)) 10998c2ecf20Sopenharmony_ci found = true; 11008c2ecf20Sopenharmony_ci } 11018c2ecf20Sopenharmony_ci } 11028c2ecf20Sopenharmony_ci } 11038c2ecf20Sopenharmony_ci } 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci if (!mode_option && !found) 11068c2ecf20Sopenharmony_ci mode_option = "640x480-8@60"; 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci if (mode_option) { 11098c2ecf20Sopenharmony_ci ret = fb_find_mode(&info->var, info, mode_option, 11108c2ecf20Sopenharmony_ci info->monspecs.modedb, 11118c2ecf20Sopenharmony_ci info->monspecs.modedb_len, 11128c2ecf20Sopenharmony_ci NULL, info->var.bits_per_pixel); 11138c2ecf20Sopenharmony_ci if (!ret || ret == 4) { 11148c2ecf20Sopenharmony_ci dev_err(info->device, "mode %s not found\n", 11158c2ecf20Sopenharmony_ci mode_option); 11168c2ecf20Sopenharmony_ci ret = -EINVAL; 11178c2ecf20Sopenharmony_ci } 11188c2ecf20Sopenharmony_ci } 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci fb_destroy_modedb(info->monspecs.modedb); 11218c2ecf20Sopenharmony_ci info->monspecs.modedb = NULL; 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci /* maximize virtual vertical size for fast scrolling */ 11248c2ecf20Sopenharmony_ci info->var.yres_virtual = info->fix.smem_len * 8 / 11258c2ecf20Sopenharmony_ci (info->var.bits_per_pixel * info->var.xres_virtual); 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci if (ret == -EINVAL) 11288c2ecf20Sopenharmony_ci goto err_find_mode; 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci ret = fb_alloc_cmap(&info->cmap, 256, 0); 11318c2ecf20Sopenharmony_ci if (ret) { 11328c2ecf20Sopenharmony_ci dev_err(info->device, "cannot allocate colormap\n"); 11338c2ecf20Sopenharmony_ci goto err_alloc_cmap; 11348c2ecf20Sopenharmony_ci } 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci ret = register_framebuffer(info); 11378c2ecf20Sopenharmony_ci if (ret) { 11388c2ecf20Sopenharmony_ci dev_err(info->device, "error registering framebuffer\n"); 11398c2ecf20Sopenharmony_ci goto err_reg_framebuffer; 11408c2ecf20Sopenharmony_ci } 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci fb_info(info, "%s frame buffer device\n", info->fix.id); 11438c2ecf20Sopenharmony_ci pci_set_drvdata(dev, info); 11448c2ecf20Sopenharmony_ci if (mtrr) 11458c2ecf20Sopenharmony_ci par->wc_cookie = arch_phys_wc_add(info->fix.smem_start, 11468c2ecf20Sopenharmony_ci info->fix.smem_len); 11478c2ecf20Sopenharmony_ci return 0; 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_cierr_reg_framebuffer: 11508c2ecf20Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 11518c2ecf20Sopenharmony_cierr_alloc_cmap: 11528c2ecf20Sopenharmony_cierr_find_mode: 11538c2ecf20Sopenharmony_ci if (par->ddc_registered) 11548c2ecf20Sopenharmony_ci i2c_del_adapter(&par->ddc_adapter); 11558c2ecf20Sopenharmony_ci pci_iounmap(dev, par->regs); 11568c2ecf20Sopenharmony_cierr_ioremap_2: 11578c2ecf20Sopenharmony_ci pci_iounmap(dev, info->screen_base); 11588c2ecf20Sopenharmony_cierr_ioremap_1: 11598c2ecf20Sopenharmony_ci pci_release_regions(dev); 11608c2ecf20Sopenharmony_cierr_request_regions: 11618c2ecf20Sopenharmony_ci/* pci_disable_device(dev); */ 11628c2ecf20Sopenharmony_cierr_enable_device: 11638c2ecf20Sopenharmony_ci framebuffer_release(info); 11648c2ecf20Sopenharmony_ci return ret; 11658c2ecf20Sopenharmony_ci} 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_cistatic void i740fb_remove(struct pci_dev *dev) 11688c2ecf20Sopenharmony_ci{ 11698c2ecf20Sopenharmony_ci struct fb_info *info = pci_get_drvdata(dev); 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci if (info) { 11728c2ecf20Sopenharmony_ci struct i740fb_par *par = info->par; 11738c2ecf20Sopenharmony_ci arch_phys_wc_del(par->wc_cookie); 11748c2ecf20Sopenharmony_ci unregister_framebuffer(info); 11758c2ecf20Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 11768c2ecf20Sopenharmony_ci if (par->ddc_registered) 11778c2ecf20Sopenharmony_ci i2c_del_adapter(&par->ddc_adapter); 11788c2ecf20Sopenharmony_ci pci_iounmap(dev, par->regs); 11798c2ecf20Sopenharmony_ci pci_iounmap(dev, info->screen_base); 11808c2ecf20Sopenharmony_ci pci_release_regions(dev); 11818c2ecf20Sopenharmony_ci/* pci_disable_device(dev); */ 11828c2ecf20Sopenharmony_ci framebuffer_release(info); 11838c2ecf20Sopenharmony_ci } 11848c2ecf20Sopenharmony_ci} 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_cistatic int __maybe_unused i740fb_suspend(struct device *dev) 11878c2ecf20Sopenharmony_ci{ 11888c2ecf20Sopenharmony_ci struct fb_info *info = dev_get_drvdata(dev); 11898c2ecf20Sopenharmony_ci struct i740fb_par *par = info->par; 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci console_lock(); 11928c2ecf20Sopenharmony_ci mutex_lock(&(par->open_lock)); 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci /* do nothing if framebuffer is not active */ 11958c2ecf20Sopenharmony_ci if (par->ref_count == 0) { 11968c2ecf20Sopenharmony_ci mutex_unlock(&(par->open_lock)); 11978c2ecf20Sopenharmony_ci console_unlock(); 11988c2ecf20Sopenharmony_ci return 0; 11998c2ecf20Sopenharmony_ci } 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci fb_set_suspend(info, 1); 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci mutex_unlock(&(par->open_lock)); 12048c2ecf20Sopenharmony_ci console_unlock(); 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci return 0; 12078c2ecf20Sopenharmony_ci} 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_cistatic int __maybe_unused i740fb_resume(struct device *dev) 12108c2ecf20Sopenharmony_ci{ 12118c2ecf20Sopenharmony_ci struct fb_info *info = dev_get_drvdata(dev); 12128c2ecf20Sopenharmony_ci struct i740fb_par *par = info->par; 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci console_lock(); 12158c2ecf20Sopenharmony_ci mutex_lock(&(par->open_lock)); 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci if (par->ref_count == 0) 12188c2ecf20Sopenharmony_ci goto fail; 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci i740fb_set_par(info); 12218c2ecf20Sopenharmony_ci fb_set_suspend(info, 0); 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_cifail: 12248c2ecf20Sopenharmony_ci mutex_unlock(&(par->open_lock)); 12258c2ecf20Sopenharmony_ci console_unlock(); 12268c2ecf20Sopenharmony_ci return 0; 12278c2ecf20Sopenharmony_ci} 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_cistatic const struct dev_pm_ops i740fb_pm_ops = { 12308c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 12318c2ecf20Sopenharmony_ci .suspend = i740fb_suspend, 12328c2ecf20Sopenharmony_ci .resume = i740fb_resume, 12338c2ecf20Sopenharmony_ci .freeze = NULL, 12348c2ecf20Sopenharmony_ci .thaw = i740fb_resume, 12358c2ecf20Sopenharmony_ci .poweroff = i740fb_suspend, 12368c2ecf20Sopenharmony_ci .restore = i740fb_resume, 12378c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 12388c2ecf20Sopenharmony_ci}; 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci#define I740_ID_PCI 0x00d1 12418c2ecf20Sopenharmony_ci#define I740_ID_AGP 0x7800 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_cistatic const struct pci_device_id i740fb_id_table[] = { 12448c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_INTEL, I740_ID_PCI) }, 12458c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_INTEL, I740_ID_AGP) }, 12468c2ecf20Sopenharmony_ci { 0 } 12478c2ecf20Sopenharmony_ci}; 12488c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, i740fb_id_table); 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_cistatic struct pci_driver i740fb_driver = { 12518c2ecf20Sopenharmony_ci .name = "i740fb", 12528c2ecf20Sopenharmony_ci .id_table = i740fb_id_table, 12538c2ecf20Sopenharmony_ci .probe = i740fb_probe, 12548c2ecf20Sopenharmony_ci .remove = i740fb_remove, 12558c2ecf20Sopenharmony_ci .driver.pm = &i740fb_pm_ops, 12568c2ecf20Sopenharmony_ci}; 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci#ifndef MODULE 12598c2ecf20Sopenharmony_cistatic int __init i740fb_setup(char *options) 12608c2ecf20Sopenharmony_ci{ 12618c2ecf20Sopenharmony_ci char *opt; 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_ci if (!options || !*options) 12648c2ecf20Sopenharmony_ci return 0; 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci while ((opt = strsep(&options, ",")) != NULL) { 12678c2ecf20Sopenharmony_ci if (!*opt) 12688c2ecf20Sopenharmony_ci continue; 12698c2ecf20Sopenharmony_ci else if (!strncmp(opt, "mtrr:", 5)) 12708c2ecf20Sopenharmony_ci mtrr = simple_strtoul(opt + 5, NULL, 0); 12718c2ecf20Sopenharmony_ci else 12728c2ecf20Sopenharmony_ci mode_option = opt; 12738c2ecf20Sopenharmony_ci } 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci return 0; 12768c2ecf20Sopenharmony_ci} 12778c2ecf20Sopenharmony_ci#endif 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_cistatic int __init i740fb_init(void) 12808c2ecf20Sopenharmony_ci{ 12818c2ecf20Sopenharmony_ci#ifndef MODULE 12828c2ecf20Sopenharmony_ci char *option = NULL; 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci if (fb_get_options("i740fb", &option)) 12858c2ecf20Sopenharmony_ci return -ENODEV; 12868c2ecf20Sopenharmony_ci i740fb_setup(option); 12878c2ecf20Sopenharmony_ci#endif 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci return pci_register_driver(&i740fb_driver); 12908c2ecf20Sopenharmony_ci} 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_cistatic void __exit i740fb_exit(void) 12938c2ecf20Sopenharmony_ci{ 12948c2ecf20Sopenharmony_ci pci_unregister_driver(&i740fb_driver); 12958c2ecf20Sopenharmony_ci} 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_cimodule_init(i740fb_init); 12988c2ecf20Sopenharmony_cimodule_exit(i740fb_exit); 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ciMODULE_AUTHOR("(c) 2011 Ondrej Zary <linux@rainbow-software.org>"); 13018c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 13028c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("fbdev driver for Intel740"); 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_cimodule_param(mode_option, charp, 0444); 13058c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mode_option, "Default video mode ('640x480-8@60', etc)"); 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_cimodule_param(mtrr, int, 0444); 13088c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mtrr, "Enable write-combining with MTRR (1=enable, 0=disable, default=1)"); 1309