18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/drivers/video/cyber2000fb.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 1998-2002 Russell King 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * MIPS and 50xx clock support 88c2ecf20Sopenharmony_ci * Copyright (C) 2001 Bradley D. LaRonde <brad@ltc.com> 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * 32 bit support, text color and panning fixes for modes != 8 bit 118c2ecf20Sopenharmony_ci * Copyright (C) 2002 Denis Oliver Kropp <dok@directfb.org> 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * Integraphics CyberPro 2000, 2010 and 5000 frame buffer device 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * Based on cyberfb.c. 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * Note that we now use the new fbcon fix, var and cmap scheme. We do 188c2ecf20Sopenharmony_ci * still have to check which console is the currently displayed one 198c2ecf20Sopenharmony_ci * however, especially for the colourmap stuff. 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci * We also use the new hotplug PCI subsystem. I'm not sure if there 228c2ecf20Sopenharmony_ci * are any such cards, but I'm erring on the side of caution. We don't 238c2ecf20Sopenharmony_ci * want to go pop just because someone does have one. 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * Note that this doesn't work fully in the case of multiple CyberPro 268c2ecf20Sopenharmony_ci * cards with grabbers. We currently can only attach to the first 278c2ecf20Sopenharmony_ci * CyberPro card found. 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci * When we're in truecolour mode, we power down the LUT RAM as a power 308c2ecf20Sopenharmony_ci * saving feature. Also, when we enter any of the powersaving modes 318c2ecf20Sopenharmony_ci * (except soft blanking) we power down the RAMDACs. This saves about 328c2ecf20Sopenharmony_ci * 1W, which is roughly 8% of the power consumption of a NetWinder 338c2ecf20Sopenharmony_ci * (which, incidentally, is about the same saving as a 2.5in hard disk 348c2ecf20Sopenharmony_ci * entering standby mode.) 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_ci#include <linux/module.h> 378c2ecf20Sopenharmony_ci#include <linux/kernel.h> 388c2ecf20Sopenharmony_ci#include <linux/errno.h> 398c2ecf20Sopenharmony_ci#include <linux/string.h> 408c2ecf20Sopenharmony_ci#include <linux/mm.h> 418c2ecf20Sopenharmony_ci#include <linux/slab.h> 428c2ecf20Sopenharmony_ci#include <linux/delay.h> 438c2ecf20Sopenharmony_ci#include <linux/fb.h> 448c2ecf20Sopenharmony_ci#include <linux/pci.h> 458c2ecf20Sopenharmony_ci#include <linux/init.h> 468c2ecf20Sopenharmony_ci#include <linux/io.h> 478c2ecf20Sopenharmony_ci#include <linux/i2c.h> 488c2ecf20Sopenharmony_ci#include <linux/i2c-algo-bit.h> 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#ifdef __arm__ 528c2ecf20Sopenharmony_ci#include <asm/mach-types.h> 538c2ecf20Sopenharmony_ci#endif 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#include "cyber2000fb.h" 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistruct cfb_info { 588c2ecf20Sopenharmony_ci struct fb_info fb; 598c2ecf20Sopenharmony_ci struct display_switch *dispsw; 608c2ecf20Sopenharmony_ci unsigned char __iomem *region; 618c2ecf20Sopenharmony_ci unsigned char __iomem *regs; 628c2ecf20Sopenharmony_ci u_int id; 638c2ecf20Sopenharmony_ci u_int irq; 648c2ecf20Sopenharmony_ci int func_use_count; 658c2ecf20Sopenharmony_ci u_long ref_ps; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci /* 688c2ecf20Sopenharmony_ci * Clock divisors 698c2ecf20Sopenharmony_ci */ 708c2ecf20Sopenharmony_ci u_int divisors[4]; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci struct { 738c2ecf20Sopenharmony_ci u8 red, green, blue; 748c2ecf20Sopenharmony_ci } palette[NR_PALETTE]; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci u_char mem_ctl1; 778c2ecf20Sopenharmony_ci u_char mem_ctl2; 788c2ecf20Sopenharmony_ci u_char mclk_mult; 798c2ecf20Sopenharmony_ci u_char mclk_div; 808c2ecf20Sopenharmony_ci /* 818c2ecf20Sopenharmony_ci * RAMDAC control register is both of these or'ed together 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_ci u_char ramdac_ctrl; 848c2ecf20Sopenharmony_ci u_char ramdac_powerdown; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci u32 pseudo_palette[16]; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci spinlock_t reg_b0_lock; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci#ifdef CONFIG_FB_CYBER2000_DDC 918c2ecf20Sopenharmony_ci bool ddc_registered; 928c2ecf20Sopenharmony_ci struct i2c_adapter ddc_adapter; 938c2ecf20Sopenharmony_ci struct i2c_algo_bit_data ddc_algo; 948c2ecf20Sopenharmony_ci#endif 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci#ifdef CONFIG_FB_CYBER2000_I2C 978c2ecf20Sopenharmony_ci struct i2c_adapter i2c_adapter; 988c2ecf20Sopenharmony_ci struct i2c_algo_bit_data i2c_algo; 998c2ecf20Sopenharmony_ci#endif 1008c2ecf20Sopenharmony_ci}; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic char *default_font = "Acorn8x8"; 1038c2ecf20Sopenharmony_cimodule_param(default_font, charp, 0); 1048c2ecf20Sopenharmony_ciMODULE_PARM_DESC(default_font, "Default font name"); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci/* 1078c2ecf20Sopenharmony_ci * Our access methods. 1088c2ecf20Sopenharmony_ci */ 1098c2ecf20Sopenharmony_ci#define cyber2000fb_writel(val, reg, cfb) writel(val, (cfb)->regs + (reg)) 1108c2ecf20Sopenharmony_ci#define cyber2000fb_writew(val, reg, cfb) writew(val, (cfb)->regs + (reg)) 1118c2ecf20Sopenharmony_ci#define cyber2000fb_writeb(val, reg, cfb) writeb(val, (cfb)->regs + (reg)) 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci#define cyber2000fb_readb(reg, cfb) readb((cfb)->regs + (reg)) 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic inline void 1168c2ecf20Sopenharmony_cicyber2000_crtcw(unsigned int reg, unsigned int val, struct cfb_info *cfb) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci cyber2000fb_writew((reg & 255) | val << 8, 0x3d4, cfb); 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic inline void 1228c2ecf20Sopenharmony_cicyber2000_grphw(unsigned int reg, unsigned int val, struct cfb_info *cfb) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci cyber2000fb_writew((reg & 255) | val << 8, 0x3ce, cfb); 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic inline unsigned int 1288c2ecf20Sopenharmony_cicyber2000_grphr(unsigned int reg, struct cfb_info *cfb) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci cyber2000fb_writeb(reg, 0x3ce, cfb); 1318c2ecf20Sopenharmony_ci return cyber2000fb_readb(0x3cf, cfb); 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic inline void 1358c2ecf20Sopenharmony_cicyber2000_attrw(unsigned int reg, unsigned int val, struct cfb_info *cfb) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci cyber2000fb_readb(0x3da, cfb); 1388c2ecf20Sopenharmony_ci cyber2000fb_writeb(reg, 0x3c0, cfb); 1398c2ecf20Sopenharmony_ci cyber2000fb_readb(0x3c1, cfb); 1408c2ecf20Sopenharmony_ci cyber2000fb_writeb(val, 0x3c0, cfb); 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic inline void 1448c2ecf20Sopenharmony_cicyber2000_seqw(unsigned int reg, unsigned int val, struct cfb_info *cfb) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci cyber2000fb_writew((reg & 255) | val << 8, 0x3c4, cfb); 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci/* -------------------- Hardware specific routines ------------------------- */ 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci/* 1528c2ecf20Sopenharmony_ci * Hardware Cyber2000 Acceleration 1538c2ecf20Sopenharmony_ci */ 1548c2ecf20Sopenharmony_cistatic void 1558c2ecf20Sopenharmony_cicyber2000fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci struct cfb_info *cfb = container_of(info, struct cfb_info, fb); 1588c2ecf20Sopenharmony_ci unsigned long dst, col; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (!(cfb->fb.var.accel_flags & FB_ACCELF_TEXT)) { 1618c2ecf20Sopenharmony_ci cfb_fillrect(info, rect); 1628c2ecf20Sopenharmony_ci return; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci cyber2000fb_writeb(0, CO_REG_CONTROL, cfb); 1668c2ecf20Sopenharmony_ci cyber2000fb_writew(rect->width - 1, CO_REG_PIXWIDTH, cfb); 1678c2ecf20Sopenharmony_ci cyber2000fb_writew(rect->height - 1, CO_REG_PIXHEIGHT, cfb); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci col = rect->color; 1708c2ecf20Sopenharmony_ci if (cfb->fb.var.bits_per_pixel > 8) 1718c2ecf20Sopenharmony_ci col = ((u32 *)cfb->fb.pseudo_palette)[col]; 1728c2ecf20Sopenharmony_ci cyber2000fb_writel(col, CO_REG_FGCOLOUR, cfb); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci dst = rect->dx + rect->dy * cfb->fb.var.xres_virtual; 1758c2ecf20Sopenharmony_ci if (cfb->fb.var.bits_per_pixel == 24) { 1768c2ecf20Sopenharmony_ci cyber2000fb_writeb(dst, CO_REG_X_PHASE, cfb); 1778c2ecf20Sopenharmony_ci dst *= 3; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci cyber2000fb_writel(dst, CO_REG_DEST_PTR, cfb); 1818c2ecf20Sopenharmony_ci cyber2000fb_writeb(CO_FG_MIX_SRC, CO_REG_FGMIX, cfb); 1828c2ecf20Sopenharmony_ci cyber2000fb_writew(CO_CMD_L_PATTERN_FGCOL, CO_REG_CMD_L, cfb); 1838c2ecf20Sopenharmony_ci cyber2000fb_writew(CO_CMD_H_BLITTER, CO_REG_CMD_H, cfb); 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic void 1878c2ecf20Sopenharmony_cicyber2000fb_copyarea(struct fb_info *info, const struct fb_copyarea *region) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci struct cfb_info *cfb = container_of(info, struct cfb_info, fb); 1908c2ecf20Sopenharmony_ci unsigned int cmd = CO_CMD_L_PATTERN_FGCOL; 1918c2ecf20Sopenharmony_ci unsigned long src, dst; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci if (!(cfb->fb.var.accel_flags & FB_ACCELF_TEXT)) { 1948c2ecf20Sopenharmony_ci cfb_copyarea(info, region); 1958c2ecf20Sopenharmony_ci return; 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci cyber2000fb_writeb(0, CO_REG_CONTROL, cfb); 1998c2ecf20Sopenharmony_ci cyber2000fb_writew(region->width - 1, CO_REG_PIXWIDTH, cfb); 2008c2ecf20Sopenharmony_ci cyber2000fb_writew(region->height - 1, CO_REG_PIXHEIGHT, cfb); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci src = region->sx + region->sy * cfb->fb.var.xres_virtual; 2038c2ecf20Sopenharmony_ci dst = region->dx + region->dy * cfb->fb.var.xres_virtual; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci if (region->sx < region->dx) { 2068c2ecf20Sopenharmony_ci src += region->width - 1; 2078c2ecf20Sopenharmony_ci dst += region->width - 1; 2088c2ecf20Sopenharmony_ci cmd |= CO_CMD_L_INC_LEFT; 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci if (region->sy < region->dy) { 2128c2ecf20Sopenharmony_ci src += (region->height - 1) * cfb->fb.var.xres_virtual; 2138c2ecf20Sopenharmony_ci dst += (region->height - 1) * cfb->fb.var.xres_virtual; 2148c2ecf20Sopenharmony_ci cmd |= CO_CMD_L_INC_UP; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci if (cfb->fb.var.bits_per_pixel == 24) { 2188c2ecf20Sopenharmony_ci cyber2000fb_writeb(dst, CO_REG_X_PHASE, cfb); 2198c2ecf20Sopenharmony_ci src *= 3; 2208c2ecf20Sopenharmony_ci dst *= 3; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci cyber2000fb_writel(src, CO_REG_SRC1_PTR, cfb); 2238c2ecf20Sopenharmony_ci cyber2000fb_writel(dst, CO_REG_DEST_PTR, cfb); 2248c2ecf20Sopenharmony_ci cyber2000fb_writew(CO_FG_MIX_SRC, CO_REG_FGMIX, cfb); 2258c2ecf20Sopenharmony_ci cyber2000fb_writew(cmd, CO_REG_CMD_L, cfb); 2268c2ecf20Sopenharmony_ci cyber2000fb_writew(CO_CMD_H_FGSRCMAP | CO_CMD_H_BLITTER, 2278c2ecf20Sopenharmony_ci CO_REG_CMD_H, cfb); 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic void 2318c2ecf20Sopenharmony_cicyber2000fb_imageblit(struct fb_info *info, const struct fb_image *image) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci cfb_imageblit(info, image); 2348c2ecf20Sopenharmony_ci return; 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic int cyber2000fb_sync(struct fb_info *info) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci struct cfb_info *cfb = container_of(info, struct cfb_info, fb); 2408c2ecf20Sopenharmony_ci int count = 100000; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (!(cfb->fb.var.accel_flags & FB_ACCELF_TEXT)) 2438c2ecf20Sopenharmony_ci return 0; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci while (cyber2000fb_readb(CO_REG_CONTROL, cfb) & CO_CTRL_BUSY) { 2468c2ecf20Sopenharmony_ci if (!count--) { 2478c2ecf20Sopenharmony_ci debug_printf("accel_wait timed out\n"); 2488c2ecf20Sopenharmony_ci cyber2000fb_writeb(0, CO_REG_CONTROL, cfb); 2498c2ecf20Sopenharmony_ci break; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci udelay(1); 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci return 0; 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci/* 2578c2ecf20Sopenharmony_ci * =========================================================================== 2588c2ecf20Sopenharmony_ci */ 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic inline u32 convert_bitfield(u_int val, struct fb_bitfield *bf) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci u_int mask = (1 << bf->length) - 1; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci return (val >> (16 - bf->length) & mask) << bf->offset; 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci/* 2688c2ecf20Sopenharmony_ci * Set a single color register. Return != 0 for invalid regno. 2698c2ecf20Sopenharmony_ci */ 2708c2ecf20Sopenharmony_cistatic int 2718c2ecf20Sopenharmony_cicyber2000fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, 2728c2ecf20Sopenharmony_ci u_int transp, struct fb_info *info) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci struct cfb_info *cfb = container_of(info, struct cfb_info, fb); 2758c2ecf20Sopenharmony_ci struct fb_var_screeninfo *var = &cfb->fb.var; 2768c2ecf20Sopenharmony_ci u32 pseudo_val; 2778c2ecf20Sopenharmony_ci int ret = 1; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci switch (cfb->fb.fix.visual) { 2808c2ecf20Sopenharmony_ci default: 2818c2ecf20Sopenharmony_ci return 1; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci /* 2848c2ecf20Sopenharmony_ci * Pseudocolour: 2858c2ecf20Sopenharmony_ci * 8 8 2868c2ecf20Sopenharmony_ci * pixel --/--+--/--> red lut --> red dac 2878c2ecf20Sopenharmony_ci * | 8 2888c2ecf20Sopenharmony_ci * +--/--> green lut --> green dac 2898c2ecf20Sopenharmony_ci * | 8 2908c2ecf20Sopenharmony_ci * +--/--> blue lut --> blue dac 2918c2ecf20Sopenharmony_ci */ 2928c2ecf20Sopenharmony_ci case FB_VISUAL_PSEUDOCOLOR: 2938c2ecf20Sopenharmony_ci if (regno >= NR_PALETTE) 2948c2ecf20Sopenharmony_ci return 1; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci red >>= 8; 2978c2ecf20Sopenharmony_ci green >>= 8; 2988c2ecf20Sopenharmony_ci blue >>= 8; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci cfb->palette[regno].red = red; 3018c2ecf20Sopenharmony_ci cfb->palette[regno].green = green; 3028c2ecf20Sopenharmony_ci cfb->palette[regno].blue = blue; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci cyber2000fb_writeb(regno, 0x3c8, cfb); 3058c2ecf20Sopenharmony_ci cyber2000fb_writeb(red, 0x3c9, cfb); 3068c2ecf20Sopenharmony_ci cyber2000fb_writeb(green, 0x3c9, cfb); 3078c2ecf20Sopenharmony_ci cyber2000fb_writeb(blue, 0x3c9, cfb); 3088c2ecf20Sopenharmony_ci return 0; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci /* 3118c2ecf20Sopenharmony_ci * Direct colour: 3128c2ecf20Sopenharmony_ci * n rl 3138c2ecf20Sopenharmony_ci * pixel --/--+--/--> red lut --> red dac 3148c2ecf20Sopenharmony_ci * | gl 3158c2ecf20Sopenharmony_ci * +--/--> green lut --> green dac 3168c2ecf20Sopenharmony_ci * | bl 3178c2ecf20Sopenharmony_ci * +--/--> blue lut --> blue dac 3188c2ecf20Sopenharmony_ci * n = bpp, rl = red length, gl = green length, bl = blue length 3198c2ecf20Sopenharmony_ci */ 3208c2ecf20Sopenharmony_ci case FB_VISUAL_DIRECTCOLOR: 3218c2ecf20Sopenharmony_ci red >>= 8; 3228c2ecf20Sopenharmony_ci green >>= 8; 3238c2ecf20Sopenharmony_ci blue >>= 8; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if (var->green.length == 6 && regno < 64) { 3268c2ecf20Sopenharmony_ci cfb->palette[regno << 2].green = green; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci /* 3298c2ecf20Sopenharmony_ci * The 6 bits of the green component are applied 3308c2ecf20Sopenharmony_ci * to the high 6 bits of the LUT. 3318c2ecf20Sopenharmony_ci */ 3328c2ecf20Sopenharmony_ci cyber2000fb_writeb(regno << 2, 0x3c8, cfb); 3338c2ecf20Sopenharmony_ci cyber2000fb_writeb(cfb->palette[regno >> 1].red, 3348c2ecf20Sopenharmony_ci 0x3c9, cfb); 3358c2ecf20Sopenharmony_ci cyber2000fb_writeb(green, 0x3c9, cfb); 3368c2ecf20Sopenharmony_ci cyber2000fb_writeb(cfb->palette[regno >> 1].blue, 3378c2ecf20Sopenharmony_ci 0x3c9, cfb); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci green = cfb->palette[regno << 3].green; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci ret = 0; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci if (var->green.length >= 5 && regno < 32) { 3458c2ecf20Sopenharmony_ci cfb->palette[regno << 3].red = red; 3468c2ecf20Sopenharmony_ci cfb->palette[regno << 3].green = green; 3478c2ecf20Sopenharmony_ci cfb->palette[regno << 3].blue = blue; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci /* 3508c2ecf20Sopenharmony_ci * The 5 bits of each colour component are 3518c2ecf20Sopenharmony_ci * applied to the high 5 bits of the LUT. 3528c2ecf20Sopenharmony_ci */ 3538c2ecf20Sopenharmony_ci cyber2000fb_writeb(regno << 3, 0x3c8, cfb); 3548c2ecf20Sopenharmony_ci cyber2000fb_writeb(red, 0x3c9, cfb); 3558c2ecf20Sopenharmony_ci cyber2000fb_writeb(green, 0x3c9, cfb); 3568c2ecf20Sopenharmony_ci cyber2000fb_writeb(blue, 0x3c9, cfb); 3578c2ecf20Sopenharmony_ci ret = 0; 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (var->green.length == 4 && regno < 16) { 3618c2ecf20Sopenharmony_ci cfb->palette[regno << 4].red = red; 3628c2ecf20Sopenharmony_ci cfb->palette[regno << 4].green = green; 3638c2ecf20Sopenharmony_ci cfb->palette[regno << 4].blue = blue; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci /* 3668c2ecf20Sopenharmony_ci * The 5 bits of each colour component are 3678c2ecf20Sopenharmony_ci * applied to the high 5 bits of the LUT. 3688c2ecf20Sopenharmony_ci */ 3698c2ecf20Sopenharmony_ci cyber2000fb_writeb(regno << 4, 0x3c8, cfb); 3708c2ecf20Sopenharmony_ci cyber2000fb_writeb(red, 0x3c9, cfb); 3718c2ecf20Sopenharmony_ci cyber2000fb_writeb(green, 0x3c9, cfb); 3728c2ecf20Sopenharmony_ci cyber2000fb_writeb(blue, 0x3c9, cfb); 3738c2ecf20Sopenharmony_ci ret = 0; 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci /* 3778c2ecf20Sopenharmony_ci * Since this is only used for the first 16 colours, we 3788c2ecf20Sopenharmony_ci * don't have to care about overflowing for regno >= 32 3798c2ecf20Sopenharmony_ci */ 3808c2ecf20Sopenharmony_ci pseudo_val = regno << var->red.offset | 3818c2ecf20Sopenharmony_ci regno << var->green.offset | 3828c2ecf20Sopenharmony_ci regno << var->blue.offset; 3838c2ecf20Sopenharmony_ci break; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci /* 3868c2ecf20Sopenharmony_ci * True colour: 3878c2ecf20Sopenharmony_ci * n rl 3888c2ecf20Sopenharmony_ci * pixel --/--+--/--> red dac 3898c2ecf20Sopenharmony_ci * | gl 3908c2ecf20Sopenharmony_ci * +--/--> green dac 3918c2ecf20Sopenharmony_ci * | bl 3928c2ecf20Sopenharmony_ci * +--/--> blue dac 3938c2ecf20Sopenharmony_ci * n = bpp, rl = red length, gl = green length, bl = blue length 3948c2ecf20Sopenharmony_ci */ 3958c2ecf20Sopenharmony_ci case FB_VISUAL_TRUECOLOR: 3968c2ecf20Sopenharmony_ci pseudo_val = convert_bitfield(transp ^ 0xffff, &var->transp); 3978c2ecf20Sopenharmony_ci pseudo_val |= convert_bitfield(red, &var->red); 3988c2ecf20Sopenharmony_ci pseudo_val |= convert_bitfield(green, &var->green); 3998c2ecf20Sopenharmony_ci pseudo_val |= convert_bitfield(blue, &var->blue); 4008c2ecf20Sopenharmony_ci ret = 0; 4018c2ecf20Sopenharmony_ci break; 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci /* 4058c2ecf20Sopenharmony_ci * Now set our pseudo palette for the CFB16/24/32 drivers. 4068c2ecf20Sopenharmony_ci */ 4078c2ecf20Sopenharmony_ci if (regno < 16) 4088c2ecf20Sopenharmony_ci ((u32 *)cfb->fb.pseudo_palette)[regno] = pseudo_val; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci return ret; 4118c2ecf20Sopenharmony_ci} 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_cistruct par_info { 4148c2ecf20Sopenharmony_ci /* 4158c2ecf20Sopenharmony_ci * Hardware 4168c2ecf20Sopenharmony_ci */ 4178c2ecf20Sopenharmony_ci u_char clock_mult; 4188c2ecf20Sopenharmony_ci u_char clock_div; 4198c2ecf20Sopenharmony_ci u_char extseqmisc; 4208c2ecf20Sopenharmony_ci u_char co_pixfmt; 4218c2ecf20Sopenharmony_ci u_char crtc_ofl; 4228c2ecf20Sopenharmony_ci u_char crtc[19]; 4238c2ecf20Sopenharmony_ci u_int width; 4248c2ecf20Sopenharmony_ci u_int pitch; 4258c2ecf20Sopenharmony_ci u_int fetch; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci /* 4288c2ecf20Sopenharmony_ci * Other 4298c2ecf20Sopenharmony_ci */ 4308c2ecf20Sopenharmony_ci u_char ramdac; 4318c2ecf20Sopenharmony_ci}; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic const u_char crtc_idx[] = { 4348c2ecf20Sopenharmony_ci 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 4358c2ecf20Sopenharmony_ci 0x08, 0x09, 4368c2ecf20Sopenharmony_ci 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18 4378c2ecf20Sopenharmony_ci}; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_cistatic void cyber2000fb_write_ramdac_ctrl(struct cfb_info *cfb) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci unsigned int i; 4428c2ecf20Sopenharmony_ci unsigned int val = cfb->ramdac_ctrl | cfb->ramdac_powerdown; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci cyber2000fb_writeb(0x56, 0x3ce, cfb); 4458c2ecf20Sopenharmony_ci i = cyber2000fb_readb(0x3cf, cfb); 4468c2ecf20Sopenharmony_ci cyber2000fb_writeb(i | 4, 0x3cf, cfb); 4478c2ecf20Sopenharmony_ci cyber2000fb_writeb(val, 0x3c6, cfb); 4488c2ecf20Sopenharmony_ci cyber2000fb_writeb(i, 0x3cf, cfb); 4498c2ecf20Sopenharmony_ci /* prevent card lock-up observed on x86 with CyberPro 2000 */ 4508c2ecf20Sopenharmony_ci cyber2000fb_readb(0x3cf, cfb); 4518c2ecf20Sopenharmony_ci} 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_cistatic void cyber2000fb_set_timing(struct cfb_info *cfb, struct par_info *hw) 4548c2ecf20Sopenharmony_ci{ 4558c2ecf20Sopenharmony_ci u_int i; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci /* 4588c2ecf20Sopenharmony_ci * Blank palette 4598c2ecf20Sopenharmony_ci */ 4608c2ecf20Sopenharmony_ci for (i = 0; i < NR_PALETTE; i++) { 4618c2ecf20Sopenharmony_ci cyber2000fb_writeb(i, 0x3c8, cfb); 4628c2ecf20Sopenharmony_ci cyber2000fb_writeb(0, 0x3c9, cfb); 4638c2ecf20Sopenharmony_ci cyber2000fb_writeb(0, 0x3c9, cfb); 4648c2ecf20Sopenharmony_ci cyber2000fb_writeb(0, 0x3c9, cfb); 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci cyber2000fb_writeb(0xef, 0x3c2, cfb); 4688c2ecf20Sopenharmony_ci cyber2000_crtcw(0x11, 0x0b, cfb); 4698c2ecf20Sopenharmony_ci cyber2000_attrw(0x11, 0x00, cfb); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci cyber2000_seqw(0x00, 0x01, cfb); 4728c2ecf20Sopenharmony_ci cyber2000_seqw(0x01, 0x01, cfb); 4738c2ecf20Sopenharmony_ci cyber2000_seqw(0x02, 0x0f, cfb); 4748c2ecf20Sopenharmony_ci cyber2000_seqw(0x03, 0x00, cfb); 4758c2ecf20Sopenharmony_ci cyber2000_seqw(0x04, 0x0e, cfb); 4768c2ecf20Sopenharmony_ci cyber2000_seqw(0x00, 0x03, cfb); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci for (i = 0; i < sizeof(crtc_idx); i++) 4798c2ecf20Sopenharmony_ci cyber2000_crtcw(crtc_idx[i], hw->crtc[i], cfb); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci for (i = 0x0a; i < 0x10; i++) 4828c2ecf20Sopenharmony_ci cyber2000_crtcw(i, 0, cfb); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci cyber2000_grphw(EXT_CRT_VRTOFL, hw->crtc_ofl, cfb); 4858c2ecf20Sopenharmony_ci cyber2000_grphw(0x00, 0x00, cfb); 4868c2ecf20Sopenharmony_ci cyber2000_grphw(0x01, 0x00, cfb); 4878c2ecf20Sopenharmony_ci cyber2000_grphw(0x02, 0x00, cfb); 4888c2ecf20Sopenharmony_ci cyber2000_grphw(0x03, 0x00, cfb); 4898c2ecf20Sopenharmony_ci cyber2000_grphw(0x04, 0x00, cfb); 4908c2ecf20Sopenharmony_ci cyber2000_grphw(0x05, 0x60, cfb); 4918c2ecf20Sopenharmony_ci cyber2000_grphw(0x06, 0x05, cfb); 4928c2ecf20Sopenharmony_ci cyber2000_grphw(0x07, 0x0f, cfb); 4938c2ecf20Sopenharmony_ci cyber2000_grphw(0x08, 0xff, cfb); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci /* Attribute controller registers */ 4968c2ecf20Sopenharmony_ci for (i = 0; i < 16; i++) 4978c2ecf20Sopenharmony_ci cyber2000_attrw(i, i, cfb); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci cyber2000_attrw(0x10, 0x01, cfb); 5008c2ecf20Sopenharmony_ci cyber2000_attrw(0x11, 0x00, cfb); 5018c2ecf20Sopenharmony_ci cyber2000_attrw(0x12, 0x0f, cfb); 5028c2ecf20Sopenharmony_ci cyber2000_attrw(0x13, 0x00, cfb); 5038c2ecf20Sopenharmony_ci cyber2000_attrw(0x14, 0x00, cfb); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci /* PLL registers */ 5068c2ecf20Sopenharmony_ci spin_lock(&cfb->reg_b0_lock); 5078c2ecf20Sopenharmony_ci cyber2000_grphw(EXT_DCLK_MULT, hw->clock_mult, cfb); 5088c2ecf20Sopenharmony_ci cyber2000_grphw(EXT_DCLK_DIV, hw->clock_div, cfb); 5098c2ecf20Sopenharmony_ci cyber2000_grphw(EXT_MCLK_MULT, cfb->mclk_mult, cfb); 5108c2ecf20Sopenharmony_ci cyber2000_grphw(EXT_MCLK_DIV, cfb->mclk_div, cfb); 5118c2ecf20Sopenharmony_ci cyber2000_grphw(0x90, 0x01, cfb); 5128c2ecf20Sopenharmony_ci cyber2000_grphw(0xb9, 0x80, cfb); 5138c2ecf20Sopenharmony_ci cyber2000_grphw(0xb9, 0x00, cfb); 5148c2ecf20Sopenharmony_ci spin_unlock(&cfb->reg_b0_lock); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci cfb->ramdac_ctrl = hw->ramdac; 5178c2ecf20Sopenharmony_ci cyber2000fb_write_ramdac_ctrl(cfb); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci cyber2000fb_writeb(0x20, 0x3c0, cfb); 5208c2ecf20Sopenharmony_ci cyber2000fb_writeb(0xff, 0x3c6, cfb); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci cyber2000_grphw(0x14, hw->fetch, cfb); 5238c2ecf20Sopenharmony_ci cyber2000_grphw(0x15, ((hw->fetch >> 8) & 0x03) | 5248c2ecf20Sopenharmony_ci ((hw->pitch >> 4) & 0x30), cfb); 5258c2ecf20Sopenharmony_ci cyber2000_grphw(EXT_SEQ_MISC, hw->extseqmisc, cfb); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci /* 5288c2ecf20Sopenharmony_ci * Set up accelerator registers 5298c2ecf20Sopenharmony_ci */ 5308c2ecf20Sopenharmony_ci cyber2000fb_writew(hw->width, CO_REG_SRC_WIDTH, cfb); 5318c2ecf20Sopenharmony_ci cyber2000fb_writew(hw->width, CO_REG_DEST_WIDTH, cfb); 5328c2ecf20Sopenharmony_ci cyber2000fb_writeb(hw->co_pixfmt, CO_REG_PIXFMT, cfb); 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_cistatic inline int 5368c2ecf20Sopenharmony_cicyber2000fb_update_start(struct cfb_info *cfb, struct fb_var_screeninfo *var) 5378c2ecf20Sopenharmony_ci{ 5388c2ecf20Sopenharmony_ci u_int base = var->yoffset * var->xres_virtual + var->xoffset; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci base *= var->bits_per_pixel; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci /* 5438c2ecf20Sopenharmony_ci * Convert to bytes and shift two extra bits because DAC 5448c2ecf20Sopenharmony_ci * can only start on 4 byte aligned data. 5458c2ecf20Sopenharmony_ci */ 5468c2ecf20Sopenharmony_ci base >>= 5; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci if (base >= 1 << 20) 5498c2ecf20Sopenharmony_ci return -EINVAL; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci cyber2000_grphw(0x10, base >> 16 | 0x10, cfb); 5528c2ecf20Sopenharmony_ci cyber2000_crtcw(0x0c, base >> 8, cfb); 5538c2ecf20Sopenharmony_ci cyber2000_crtcw(0x0d, base, cfb); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci return 0; 5568c2ecf20Sopenharmony_ci} 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_cistatic int 5598c2ecf20Sopenharmony_cicyber2000fb_decode_crtc(struct par_info *hw, struct cfb_info *cfb, 5608c2ecf20Sopenharmony_ci struct fb_var_screeninfo *var) 5618c2ecf20Sopenharmony_ci{ 5628c2ecf20Sopenharmony_ci u_int Htotal, Hblankend, Hsyncend; 5638c2ecf20Sopenharmony_ci u_int Vtotal, Vdispend, Vblankstart, Vblankend, Vsyncstart, Vsyncend; 5648c2ecf20Sopenharmony_ci#define ENCODE_BIT(v, b1, m, b2) ((((v) >> (b1)) & (m)) << (b2)) 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci hw->crtc[13] = hw->pitch; 5678c2ecf20Sopenharmony_ci hw->crtc[17] = 0xe3; 5688c2ecf20Sopenharmony_ci hw->crtc[14] = 0; 5698c2ecf20Sopenharmony_ci hw->crtc[8] = 0; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci Htotal = var->xres + var->right_margin + 5728c2ecf20Sopenharmony_ci var->hsync_len + var->left_margin; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci if (Htotal > 2080) 5758c2ecf20Sopenharmony_ci return -EINVAL; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci hw->crtc[0] = (Htotal >> 3) - 5; 5788c2ecf20Sopenharmony_ci hw->crtc[1] = (var->xres >> 3) - 1; 5798c2ecf20Sopenharmony_ci hw->crtc[2] = var->xres >> 3; 5808c2ecf20Sopenharmony_ci hw->crtc[4] = (var->xres + var->right_margin) >> 3; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci Hblankend = (Htotal - 4 * 8) >> 3; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci hw->crtc[3] = ENCODE_BIT(Hblankend, 0, 0x1f, 0) | 5858c2ecf20Sopenharmony_ci ENCODE_BIT(1, 0, 0x01, 7); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci Hsyncend = (var->xres + var->right_margin + var->hsync_len) >> 3; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci hw->crtc[5] = ENCODE_BIT(Hsyncend, 0, 0x1f, 0) | 5908c2ecf20Sopenharmony_ci ENCODE_BIT(Hblankend, 5, 0x01, 7); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci Vdispend = var->yres - 1; 5938c2ecf20Sopenharmony_ci Vsyncstart = var->yres + var->lower_margin; 5948c2ecf20Sopenharmony_ci Vsyncend = var->yres + var->lower_margin + var->vsync_len; 5958c2ecf20Sopenharmony_ci Vtotal = var->yres + var->lower_margin + var->vsync_len + 5968c2ecf20Sopenharmony_ci var->upper_margin - 2; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci if (Vtotal > 2047) 5998c2ecf20Sopenharmony_ci return -EINVAL; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci Vblankstart = var->yres + 6; 6028c2ecf20Sopenharmony_ci Vblankend = Vtotal - 10; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci hw->crtc[6] = Vtotal; 6058c2ecf20Sopenharmony_ci hw->crtc[7] = ENCODE_BIT(Vtotal, 8, 0x01, 0) | 6068c2ecf20Sopenharmony_ci ENCODE_BIT(Vdispend, 8, 0x01, 1) | 6078c2ecf20Sopenharmony_ci ENCODE_BIT(Vsyncstart, 8, 0x01, 2) | 6088c2ecf20Sopenharmony_ci ENCODE_BIT(Vblankstart, 8, 0x01, 3) | 6098c2ecf20Sopenharmony_ci ENCODE_BIT(1, 0, 0x01, 4) | 6108c2ecf20Sopenharmony_ci ENCODE_BIT(Vtotal, 9, 0x01, 5) | 6118c2ecf20Sopenharmony_ci ENCODE_BIT(Vdispend, 9, 0x01, 6) | 6128c2ecf20Sopenharmony_ci ENCODE_BIT(Vsyncstart, 9, 0x01, 7); 6138c2ecf20Sopenharmony_ci hw->crtc[9] = ENCODE_BIT(0, 0, 0x1f, 0) | 6148c2ecf20Sopenharmony_ci ENCODE_BIT(Vblankstart, 9, 0x01, 5) | 6158c2ecf20Sopenharmony_ci ENCODE_BIT(1, 0, 0x01, 6); 6168c2ecf20Sopenharmony_ci hw->crtc[10] = Vsyncstart; 6178c2ecf20Sopenharmony_ci hw->crtc[11] = ENCODE_BIT(Vsyncend, 0, 0x0f, 0) | 6188c2ecf20Sopenharmony_ci ENCODE_BIT(1, 0, 0x01, 7); 6198c2ecf20Sopenharmony_ci hw->crtc[12] = Vdispend; 6208c2ecf20Sopenharmony_ci hw->crtc[15] = Vblankstart; 6218c2ecf20Sopenharmony_ci hw->crtc[16] = Vblankend; 6228c2ecf20Sopenharmony_ci hw->crtc[18] = 0xff; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci /* 6258c2ecf20Sopenharmony_ci * overflow - graphics reg 0x11 6268c2ecf20Sopenharmony_ci * 0=VTOTAL:10 1=VDEND:10 2=VRSTART:10 3=VBSTART:10 6278c2ecf20Sopenharmony_ci * 4=LINECOMP:10 5-IVIDEO 6=FIXCNT 6288c2ecf20Sopenharmony_ci */ 6298c2ecf20Sopenharmony_ci hw->crtc_ofl = 6308c2ecf20Sopenharmony_ci ENCODE_BIT(Vtotal, 10, 0x01, 0) | 6318c2ecf20Sopenharmony_ci ENCODE_BIT(Vdispend, 10, 0x01, 1) | 6328c2ecf20Sopenharmony_ci ENCODE_BIT(Vsyncstart, 10, 0x01, 2) | 6338c2ecf20Sopenharmony_ci ENCODE_BIT(Vblankstart, 10, 0x01, 3) | 6348c2ecf20Sopenharmony_ci EXT_CRT_VRTOFL_LINECOMP10; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci /* woody: set the interlaced bit... */ 6378c2ecf20Sopenharmony_ci /* FIXME: what about doublescan? */ 6388c2ecf20Sopenharmony_ci if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) 6398c2ecf20Sopenharmony_ci hw->crtc_ofl |= EXT_CRT_VRTOFL_INTERLACE; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci return 0; 6428c2ecf20Sopenharmony_ci} 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci/* 6458c2ecf20Sopenharmony_ci * The following was discovered by a good monitor, bit twiddling, theorising 6468c2ecf20Sopenharmony_ci * and but mostly luck. Strangely, it looks like everyone elses' PLL! 6478c2ecf20Sopenharmony_ci * 6488c2ecf20Sopenharmony_ci * Clock registers: 6498c2ecf20Sopenharmony_ci * fclock = fpll / div2 6508c2ecf20Sopenharmony_ci * fpll = fref * mult / div1 6518c2ecf20Sopenharmony_ci * where: 6528c2ecf20Sopenharmony_ci * fref = 14.318MHz (69842ps) 6538c2ecf20Sopenharmony_ci * mult = reg0xb0.7:0 6548c2ecf20Sopenharmony_ci * div1 = (reg0xb1.5:0 + 1) 6558c2ecf20Sopenharmony_ci * div2 = 2^(reg0xb1.7:6) 6568c2ecf20Sopenharmony_ci * fpll should be between 115 and 260 MHz 6578c2ecf20Sopenharmony_ci * (8696ps and 3846ps) 6588c2ecf20Sopenharmony_ci */ 6598c2ecf20Sopenharmony_cistatic int 6608c2ecf20Sopenharmony_cicyber2000fb_decode_clock(struct par_info *hw, struct cfb_info *cfb, 6618c2ecf20Sopenharmony_ci struct fb_var_screeninfo *var) 6628c2ecf20Sopenharmony_ci{ 6638c2ecf20Sopenharmony_ci u_long pll_ps = var->pixclock; 6648c2ecf20Sopenharmony_ci const u_long ref_ps = cfb->ref_ps; 6658c2ecf20Sopenharmony_ci u_int div2, t_div1, best_div1, best_mult; 6668c2ecf20Sopenharmony_ci int best_diff; 6678c2ecf20Sopenharmony_ci int vco; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci /* 6708c2ecf20Sopenharmony_ci * Step 1: 6718c2ecf20Sopenharmony_ci * find div2 such that 115MHz < fpll < 260MHz 6728c2ecf20Sopenharmony_ci * and 0 <= div2 < 4 6738c2ecf20Sopenharmony_ci */ 6748c2ecf20Sopenharmony_ci for (div2 = 0; div2 < 4; div2++) { 6758c2ecf20Sopenharmony_ci u_long new_pll; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci new_pll = pll_ps / cfb->divisors[div2]; 6788c2ecf20Sopenharmony_ci if (8696 > new_pll && new_pll > 3846) { 6798c2ecf20Sopenharmony_ci pll_ps = new_pll; 6808c2ecf20Sopenharmony_ci break; 6818c2ecf20Sopenharmony_ci } 6828c2ecf20Sopenharmony_ci } 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci if (div2 == 4) 6858c2ecf20Sopenharmony_ci return -EINVAL; 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci /* 6888c2ecf20Sopenharmony_ci * Step 2: 6898c2ecf20Sopenharmony_ci * Given pll_ps and ref_ps, find: 6908c2ecf20Sopenharmony_ci * pll_ps * 0.995 < pll_ps_calc < pll_ps * 1.005 6918c2ecf20Sopenharmony_ci * where { 1 < best_div1 < 32, 1 < best_mult < 256 } 6928c2ecf20Sopenharmony_ci * pll_ps_calc = best_div1 / (ref_ps * best_mult) 6938c2ecf20Sopenharmony_ci */ 6948c2ecf20Sopenharmony_ci best_diff = 0x7fffffff; 6958c2ecf20Sopenharmony_ci best_mult = 2; 6968c2ecf20Sopenharmony_ci best_div1 = 32; 6978c2ecf20Sopenharmony_ci for (t_div1 = 2; t_div1 < 32; t_div1 += 1) { 6988c2ecf20Sopenharmony_ci u_int rr, t_mult, t_pll_ps; 6998c2ecf20Sopenharmony_ci int diff; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci /* 7028c2ecf20Sopenharmony_ci * Find the multiplier for this divisor 7038c2ecf20Sopenharmony_ci */ 7048c2ecf20Sopenharmony_ci rr = ref_ps * t_div1; 7058c2ecf20Sopenharmony_ci t_mult = (rr + pll_ps / 2) / pll_ps; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci /* 7088c2ecf20Sopenharmony_ci * Is the multiplier within the correct range? 7098c2ecf20Sopenharmony_ci */ 7108c2ecf20Sopenharmony_ci if (t_mult > 256 || t_mult < 2) 7118c2ecf20Sopenharmony_ci continue; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci /* 7148c2ecf20Sopenharmony_ci * Calculate the actual clock period from this multiplier 7158c2ecf20Sopenharmony_ci * and divisor, and estimate the error. 7168c2ecf20Sopenharmony_ci */ 7178c2ecf20Sopenharmony_ci t_pll_ps = (rr + t_mult / 2) / t_mult; 7188c2ecf20Sopenharmony_ci diff = pll_ps - t_pll_ps; 7198c2ecf20Sopenharmony_ci if (diff < 0) 7208c2ecf20Sopenharmony_ci diff = -diff; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci if (diff < best_diff) { 7238c2ecf20Sopenharmony_ci best_diff = diff; 7248c2ecf20Sopenharmony_ci best_mult = t_mult; 7258c2ecf20Sopenharmony_ci best_div1 = t_div1; 7268c2ecf20Sopenharmony_ci } 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci /* 7298c2ecf20Sopenharmony_ci * If we hit an exact value, there is no point in continuing. 7308c2ecf20Sopenharmony_ci */ 7318c2ecf20Sopenharmony_ci if (diff == 0) 7328c2ecf20Sopenharmony_ci break; 7338c2ecf20Sopenharmony_ci } 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci /* 7368c2ecf20Sopenharmony_ci * Step 3: 7378c2ecf20Sopenharmony_ci * combine values 7388c2ecf20Sopenharmony_ci */ 7398c2ecf20Sopenharmony_ci hw->clock_mult = best_mult - 1; 7408c2ecf20Sopenharmony_ci hw->clock_div = div2 << 6 | (best_div1 - 1); 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci vco = ref_ps * best_div1 / best_mult; 7438c2ecf20Sopenharmony_ci if ((ref_ps == 40690) && (vco < 5556)) 7448c2ecf20Sopenharmony_ci /* Set VFSEL when VCO > 180MHz (5.556 ps). */ 7458c2ecf20Sopenharmony_ci hw->clock_div |= EXT_DCLK_DIV_VFSEL; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci return 0; 7488c2ecf20Sopenharmony_ci} 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci/* 7518c2ecf20Sopenharmony_ci * Set the User Defined Part of the Display 7528c2ecf20Sopenharmony_ci */ 7538c2ecf20Sopenharmony_cistatic int 7548c2ecf20Sopenharmony_cicyber2000fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) 7558c2ecf20Sopenharmony_ci{ 7568c2ecf20Sopenharmony_ci struct cfb_info *cfb = container_of(info, struct cfb_info, fb); 7578c2ecf20Sopenharmony_ci struct par_info hw; 7588c2ecf20Sopenharmony_ci unsigned int mem; 7598c2ecf20Sopenharmony_ci int err; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci var->transp.msb_right = 0; 7628c2ecf20Sopenharmony_ci var->red.msb_right = 0; 7638c2ecf20Sopenharmony_ci var->green.msb_right = 0; 7648c2ecf20Sopenharmony_ci var->blue.msb_right = 0; 7658c2ecf20Sopenharmony_ci var->transp.offset = 0; 7668c2ecf20Sopenharmony_ci var->transp.length = 0; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci switch (var->bits_per_pixel) { 7698c2ecf20Sopenharmony_ci case 8: /* PSEUDOCOLOUR, 256 */ 7708c2ecf20Sopenharmony_ci var->red.offset = 0; 7718c2ecf20Sopenharmony_ci var->red.length = 8; 7728c2ecf20Sopenharmony_ci var->green.offset = 0; 7738c2ecf20Sopenharmony_ci var->green.length = 8; 7748c2ecf20Sopenharmony_ci var->blue.offset = 0; 7758c2ecf20Sopenharmony_ci var->blue.length = 8; 7768c2ecf20Sopenharmony_ci break; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci case 16:/* DIRECTCOLOUR, 64k or 32k */ 7798c2ecf20Sopenharmony_ci switch (var->green.length) { 7808c2ecf20Sopenharmony_ci case 6: /* RGB565, 64k */ 7818c2ecf20Sopenharmony_ci var->red.offset = 11; 7828c2ecf20Sopenharmony_ci var->red.length = 5; 7838c2ecf20Sopenharmony_ci var->green.offset = 5; 7848c2ecf20Sopenharmony_ci var->green.length = 6; 7858c2ecf20Sopenharmony_ci var->blue.offset = 0; 7868c2ecf20Sopenharmony_ci var->blue.length = 5; 7878c2ecf20Sopenharmony_ci break; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci default: 7908c2ecf20Sopenharmony_ci case 5: /* RGB555, 32k */ 7918c2ecf20Sopenharmony_ci var->red.offset = 10; 7928c2ecf20Sopenharmony_ci var->red.length = 5; 7938c2ecf20Sopenharmony_ci var->green.offset = 5; 7948c2ecf20Sopenharmony_ci var->green.length = 5; 7958c2ecf20Sopenharmony_ci var->blue.offset = 0; 7968c2ecf20Sopenharmony_ci var->blue.length = 5; 7978c2ecf20Sopenharmony_ci break; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci case 4: /* RGB444, 4k + transparency? */ 8008c2ecf20Sopenharmony_ci var->transp.offset = 12; 8018c2ecf20Sopenharmony_ci var->transp.length = 4; 8028c2ecf20Sopenharmony_ci var->red.offset = 8; 8038c2ecf20Sopenharmony_ci var->red.length = 4; 8048c2ecf20Sopenharmony_ci var->green.offset = 4; 8058c2ecf20Sopenharmony_ci var->green.length = 4; 8068c2ecf20Sopenharmony_ci var->blue.offset = 0; 8078c2ecf20Sopenharmony_ci var->blue.length = 4; 8088c2ecf20Sopenharmony_ci break; 8098c2ecf20Sopenharmony_ci } 8108c2ecf20Sopenharmony_ci break; 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci case 24:/* TRUECOLOUR, 16m */ 8138c2ecf20Sopenharmony_ci var->red.offset = 16; 8148c2ecf20Sopenharmony_ci var->red.length = 8; 8158c2ecf20Sopenharmony_ci var->green.offset = 8; 8168c2ecf20Sopenharmony_ci var->green.length = 8; 8178c2ecf20Sopenharmony_ci var->blue.offset = 0; 8188c2ecf20Sopenharmony_ci var->blue.length = 8; 8198c2ecf20Sopenharmony_ci break; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci case 32:/* TRUECOLOUR, 16m */ 8228c2ecf20Sopenharmony_ci var->transp.offset = 24; 8238c2ecf20Sopenharmony_ci var->transp.length = 8; 8248c2ecf20Sopenharmony_ci var->red.offset = 16; 8258c2ecf20Sopenharmony_ci var->red.length = 8; 8268c2ecf20Sopenharmony_ci var->green.offset = 8; 8278c2ecf20Sopenharmony_ci var->green.length = 8; 8288c2ecf20Sopenharmony_ci var->blue.offset = 0; 8298c2ecf20Sopenharmony_ci var->blue.length = 8; 8308c2ecf20Sopenharmony_ci break; 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci default: 8338c2ecf20Sopenharmony_ci return -EINVAL; 8348c2ecf20Sopenharmony_ci } 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci mem = var->xres_virtual * var->yres_virtual * (var->bits_per_pixel / 8); 8378c2ecf20Sopenharmony_ci if (mem > cfb->fb.fix.smem_len) 8388c2ecf20Sopenharmony_ci var->yres_virtual = cfb->fb.fix.smem_len * 8 / 8398c2ecf20Sopenharmony_ci (var->bits_per_pixel * var->xres_virtual); 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci if (var->yres > var->yres_virtual) 8428c2ecf20Sopenharmony_ci var->yres = var->yres_virtual; 8438c2ecf20Sopenharmony_ci if (var->xres > var->xres_virtual) 8448c2ecf20Sopenharmony_ci var->xres = var->xres_virtual; 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci err = cyber2000fb_decode_clock(&hw, cfb, var); 8478c2ecf20Sopenharmony_ci if (err) 8488c2ecf20Sopenharmony_ci return err; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci err = cyber2000fb_decode_crtc(&hw, cfb, var); 8518c2ecf20Sopenharmony_ci if (err) 8528c2ecf20Sopenharmony_ci return err; 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci return 0; 8558c2ecf20Sopenharmony_ci} 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_cistatic int cyber2000fb_set_par(struct fb_info *info) 8588c2ecf20Sopenharmony_ci{ 8598c2ecf20Sopenharmony_ci struct cfb_info *cfb = container_of(info, struct cfb_info, fb); 8608c2ecf20Sopenharmony_ci struct fb_var_screeninfo *var = &cfb->fb.var; 8618c2ecf20Sopenharmony_ci struct par_info hw; 8628c2ecf20Sopenharmony_ci unsigned int mem; 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci hw.width = var->xres_virtual; 8658c2ecf20Sopenharmony_ci hw.ramdac = RAMDAC_VREFEN | RAMDAC_DAC8BIT; 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci switch (var->bits_per_pixel) { 8688c2ecf20Sopenharmony_ci case 8: 8698c2ecf20Sopenharmony_ci hw.co_pixfmt = CO_PIXFMT_8BPP; 8708c2ecf20Sopenharmony_ci hw.pitch = hw.width >> 3; 8718c2ecf20Sopenharmony_ci hw.extseqmisc = EXT_SEQ_MISC_8; 8728c2ecf20Sopenharmony_ci break; 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci case 16: 8758c2ecf20Sopenharmony_ci hw.co_pixfmt = CO_PIXFMT_16BPP; 8768c2ecf20Sopenharmony_ci hw.pitch = hw.width >> 2; 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci switch (var->green.length) { 8798c2ecf20Sopenharmony_ci case 6: /* RGB565, 64k */ 8808c2ecf20Sopenharmony_ci hw.extseqmisc = EXT_SEQ_MISC_16_RGB565; 8818c2ecf20Sopenharmony_ci break; 8828c2ecf20Sopenharmony_ci case 5: /* RGB555, 32k */ 8838c2ecf20Sopenharmony_ci hw.extseqmisc = EXT_SEQ_MISC_16_RGB555; 8848c2ecf20Sopenharmony_ci break; 8858c2ecf20Sopenharmony_ci case 4: /* RGB444, 4k + transparency? */ 8868c2ecf20Sopenharmony_ci hw.extseqmisc = EXT_SEQ_MISC_16_RGB444; 8878c2ecf20Sopenharmony_ci break; 8888c2ecf20Sopenharmony_ci default: 8898c2ecf20Sopenharmony_ci BUG(); 8908c2ecf20Sopenharmony_ci } 8918c2ecf20Sopenharmony_ci break; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci case 24:/* TRUECOLOUR, 16m */ 8948c2ecf20Sopenharmony_ci hw.co_pixfmt = CO_PIXFMT_24BPP; 8958c2ecf20Sopenharmony_ci hw.width *= 3; 8968c2ecf20Sopenharmony_ci hw.pitch = hw.width >> 3; 8978c2ecf20Sopenharmony_ci hw.ramdac |= (RAMDAC_BYPASS | RAMDAC_RAMPWRDN); 8988c2ecf20Sopenharmony_ci hw.extseqmisc = EXT_SEQ_MISC_24_RGB888; 8998c2ecf20Sopenharmony_ci break; 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci case 32:/* TRUECOLOUR, 16m */ 9028c2ecf20Sopenharmony_ci hw.co_pixfmt = CO_PIXFMT_32BPP; 9038c2ecf20Sopenharmony_ci hw.pitch = hw.width >> 1; 9048c2ecf20Sopenharmony_ci hw.ramdac |= (RAMDAC_BYPASS | RAMDAC_RAMPWRDN); 9058c2ecf20Sopenharmony_ci hw.extseqmisc = EXT_SEQ_MISC_32; 9068c2ecf20Sopenharmony_ci break; 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci default: 9098c2ecf20Sopenharmony_ci BUG(); 9108c2ecf20Sopenharmony_ci } 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci /* 9138c2ecf20Sopenharmony_ci * Sigh, this is absolutely disgusting, but caused by 9148c2ecf20Sopenharmony_ci * the way the fbcon developers want to separate out 9158c2ecf20Sopenharmony_ci * the "checking" and the "setting" of the video mode. 9168c2ecf20Sopenharmony_ci * 9178c2ecf20Sopenharmony_ci * If the mode is not suitable for the hardware here, 9188c2ecf20Sopenharmony_ci * we can't prevent it being set by returning an error. 9198c2ecf20Sopenharmony_ci * 9208c2ecf20Sopenharmony_ci * In theory, since NetWinders contain just one VGA card, 9218c2ecf20Sopenharmony_ci * we should never end up hitting this problem. 9228c2ecf20Sopenharmony_ci */ 9238c2ecf20Sopenharmony_ci BUG_ON(cyber2000fb_decode_clock(&hw, cfb, var) != 0); 9248c2ecf20Sopenharmony_ci BUG_ON(cyber2000fb_decode_crtc(&hw, cfb, var) != 0); 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci hw.width -= 1; 9278c2ecf20Sopenharmony_ci hw.fetch = hw.pitch; 9288c2ecf20Sopenharmony_ci if (!(cfb->mem_ctl2 & MEM_CTL2_64BIT)) 9298c2ecf20Sopenharmony_ci hw.fetch <<= 1; 9308c2ecf20Sopenharmony_ci hw.fetch += 1; 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci cfb->fb.fix.line_length = var->xres_virtual * var->bits_per_pixel / 8; 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci /* 9358c2ecf20Sopenharmony_ci * Same here - if the size of the video mode exceeds the 9368c2ecf20Sopenharmony_ci * available RAM, we can't prevent this mode being set. 9378c2ecf20Sopenharmony_ci * 9388c2ecf20Sopenharmony_ci * In theory, since NetWinders contain just one VGA card, 9398c2ecf20Sopenharmony_ci * we should never end up hitting this problem. 9408c2ecf20Sopenharmony_ci */ 9418c2ecf20Sopenharmony_ci mem = cfb->fb.fix.line_length * var->yres_virtual; 9428c2ecf20Sopenharmony_ci BUG_ON(mem > cfb->fb.fix.smem_len); 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci /* 9458c2ecf20Sopenharmony_ci * 8bpp displays are always pseudo colour. 16bpp and above 9468c2ecf20Sopenharmony_ci * are direct colour or true colour, depending on whether 9478c2ecf20Sopenharmony_ci * the RAMDAC palettes are bypassed. (Direct colour has 9488c2ecf20Sopenharmony_ci * palettes, true colour does not.) 9498c2ecf20Sopenharmony_ci */ 9508c2ecf20Sopenharmony_ci if (var->bits_per_pixel == 8) 9518c2ecf20Sopenharmony_ci cfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; 9528c2ecf20Sopenharmony_ci else if (hw.ramdac & RAMDAC_BYPASS) 9538c2ecf20Sopenharmony_ci cfb->fb.fix.visual = FB_VISUAL_TRUECOLOR; 9548c2ecf20Sopenharmony_ci else 9558c2ecf20Sopenharmony_ci cfb->fb.fix.visual = FB_VISUAL_DIRECTCOLOR; 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci cyber2000fb_set_timing(cfb, &hw); 9588c2ecf20Sopenharmony_ci cyber2000fb_update_start(cfb, var); 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci return 0; 9618c2ecf20Sopenharmony_ci} 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci/* 9648c2ecf20Sopenharmony_ci * Pan or Wrap the Display 9658c2ecf20Sopenharmony_ci */ 9668c2ecf20Sopenharmony_cistatic int 9678c2ecf20Sopenharmony_cicyber2000fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) 9688c2ecf20Sopenharmony_ci{ 9698c2ecf20Sopenharmony_ci struct cfb_info *cfb = container_of(info, struct cfb_info, fb); 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci if (cyber2000fb_update_start(cfb, var)) 9728c2ecf20Sopenharmony_ci return -EINVAL; 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci cfb->fb.var.xoffset = var->xoffset; 9758c2ecf20Sopenharmony_ci cfb->fb.var.yoffset = var->yoffset; 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci if (var->vmode & FB_VMODE_YWRAP) { 9788c2ecf20Sopenharmony_ci cfb->fb.var.vmode |= FB_VMODE_YWRAP; 9798c2ecf20Sopenharmony_ci } else { 9808c2ecf20Sopenharmony_ci cfb->fb.var.vmode &= ~FB_VMODE_YWRAP; 9818c2ecf20Sopenharmony_ci } 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci return 0; 9848c2ecf20Sopenharmony_ci} 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci/* 9878c2ecf20Sopenharmony_ci * (Un)Blank the display. 9888c2ecf20Sopenharmony_ci * 9898c2ecf20Sopenharmony_ci * Blank the screen if blank_mode != 0, else unblank. If 9908c2ecf20Sopenharmony_ci * blank == NULL then the caller blanks by setting the CLUT 9918c2ecf20Sopenharmony_ci * (Color Look Up Table) to all black. Return 0 if blanking 9928c2ecf20Sopenharmony_ci * succeeded, != 0 if un-/blanking failed due to e.g. a 9938c2ecf20Sopenharmony_ci * video mode which doesn't support it. Implements VESA 9948c2ecf20Sopenharmony_ci * suspend and powerdown modes on hardware that supports 9958c2ecf20Sopenharmony_ci * disabling hsync/vsync: 9968c2ecf20Sopenharmony_ci * blank_mode == 2: suspend vsync 9978c2ecf20Sopenharmony_ci * blank_mode == 3: suspend hsync 9988c2ecf20Sopenharmony_ci * blank_mode == 4: powerdown 9998c2ecf20Sopenharmony_ci * 10008c2ecf20Sopenharmony_ci * wms...Enable VESA DMPS compatible powerdown mode 10018c2ecf20Sopenharmony_ci * run "setterm -powersave powerdown" to take advantage 10028c2ecf20Sopenharmony_ci */ 10038c2ecf20Sopenharmony_cistatic int cyber2000fb_blank(int blank, struct fb_info *info) 10048c2ecf20Sopenharmony_ci{ 10058c2ecf20Sopenharmony_ci struct cfb_info *cfb = container_of(info, struct cfb_info, fb); 10068c2ecf20Sopenharmony_ci unsigned int sync = 0; 10078c2ecf20Sopenharmony_ci int i; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci switch (blank) { 10108c2ecf20Sopenharmony_ci case FB_BLANK_POWERDOWN: /* powerdown - both sync lines down */ 10118c2ecf20Sopenharmony_ci sync = EXT_SYNC_CTL_VS_0 | EXT_SYNC_CTL_HS_0; 10128c2ecf20Sopenharmony_ci break; 10138c2ecf20Sopenharmony_ci case FB_BLANK_HSYNC_SUSPEND: /* hsync off */ 10148c2ecf20Sopenharmony_ci sync = EXT_SYNC_CTL_VS_NORMAL | EXT_SYNC_CTL_HS_0; 10158c2ecf20Sopenharmony_ci break; 10168c2ecf20Sopenharmony_ci case FB_BLANK_VSYNC_SUSPEND: /* vsync off */ 10178c2ecf20Sopenharmony_ci sync = EXT_SYNC_CTL_VS_0 | EXT_SYNC_CTL_HS_NORMAL; 10188c2ecf20Sopenharmony_ci break; 10198c2ecf20Sopenharmony_ci case FB_BLANK_NORMAL: /* soft blank */ 10208c2ecf20Sopenharmony_ci default: /* unblank */ 10218c2ecf20Sopenharmony_ci break; 10228c2ecf20Sopenharmony_ci } 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci cyber2000_grphw(EXT_SYNC_CTL, sync, cfb); 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci if (blank <= 1) { 10278c2ecf20Sopenharmony_ci /* turn on ramdacs */ 10288c2ecf20Sopenharmony_ci cfb->ramdac_powerdown &= ~(RAMDAC_DACPWRDN | RAMDAC_BYPASS | 10298c2ecf20Sopenharmony_ci RAMDAC_RAMPWRDN); 10308c2ecf20Sopenharmony_ci cyber2000fb_write_ramdac_ctrl(cfb); 10318c2ecf20Sopenharmony_ci } 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci /* 10348c2ecf20Sopenharmony_ci * Soft blank/unblank the display. 10358c2ecf20Sopenharmony_ci */ 10368c2ecf20Sopenharmony_ci if (blank) { /* soft blank */ 10378c2ecf20Sopenharmony_ci for (i = 0; i < NR_PALETTE; i++) { 10388c2ecf20Sopenharmony_ci cyber2000fb_writeb(i, 0x3c8, cfb); 10398c2ecf20Sopenharmony_ci cyber2000fb_writeb(0, 0x3c9, cfb); 10408c2ecf20Sopenharmony_ci cyber2000fb_writeb(0, 0x3c9, cfb); 10418c2ecf20Sopenharmony_ci cyber2000fb_writeb(0, 0x3c9, cfb); 10428c2ecf20Sopenharmony_ci } 10438c2ecf20Sopenharmony_ci } else { /* unblank */ 10448c2ecf20Sopenharmony_ci for (i = 0; i < NR_PALETTE; i++) { 10458c2ecf20Sopenharmony_ci cyber2000fb_writeb(i, 0x3c8, cfb); 10468c2ecf20Sopenharmony_ci cyber2000fb_writeb(cfb->palette[i].red, 0x3c9, cfb); 10478c2ecf20Sopenharmony_ci cyber2000fb_writeb(cfb->palette[i].green, 0x3c9, cfb); 10488c2ecf20Sopenharmony_ci cyber2000fb_writeb(cfb->palette[i].blue, 0x3c9, cfb); 10498c2ecf20Sopenharmony_ci } 10508c2ecf20Sopenharmony_ci } 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci if (blank >= 2) { 10538c2ecf20Sopenharmony_ci /* turn off ramdacs */ 10548c2ecf20Sopenharmony_ci cfb->ramdac_powerdown |= RAMDAC_DACPWRDN | RAMDAC_BYPASS | 10558c2ecf20Sopenharmony_ci RAMDAC_RAMPWRDN; 10568c2ecf20Sopenharmony_ci cyber2000fb_write_ramdac_ctrl(cfb); 10578c2ecf20Sopenharmony_ci } 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci return 0; 10608c2ecf20Sopenharmony_ci} 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_cistatic const struct fb_ops cyber2000fb_ops = { 10638c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 10648c2ecf20Sopenharmony_ci .fb_check_var = cyber2000fb_check_var, 10658c2ecf20Sopenharmony_ci .fb_set_par = cyber2000fb_set_par, 10668c2ecf20Sopenharmony_ci .fb_setcolreg = cyber2000fb_setcolreg, 10678c2ecf20Sopenharmony_ci .fb_blank = cyber2000fb_blank, 10688c2ecf20Sopenharmony_ci .fb_pan_display = cyber2000fb_pan_display, 10698c2ecf20Sopenharmony_ci .fb_fillrect = cyber2000fb_fillrect, 10708c2ecf20Sopenharmony_ci .fb_copyarea = cyber2000fb_copyarea, 10718c2ecf20Sopenharmony_ci .fb_imageblit = cyber2000fb_imageblit, 10728c2ecf20Sopenharmony_ci .fb_sync = cyber2000fb_sync, 10738c2ecf20Sopenharmony_ci}; 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci/* 10768c2ecf20Sopenharmony_ci * This is the only "static" reference to the internal data structures 10778c2ecf20Sopenharmony_ci * of this driver. It is here solely at the moment to support the other 10788c2ecf20Sopenharmony_ci * CyberPro modules external to this driver. 10798c2ecf20Sopenharmony_ci */ 10808c2ecf20Sopenharmony_cistatic struct cfb_info *int_cfb_info; 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci/* 10838c2ecf20Sopenharmony_ci * Enable access to the extended registers 10848c2ecf20Sopenharmony_ci */ 10858c2ecf20Sopenharmony_civoid cyber2000fb_enable_extregs(struct cfb_info *cfb) 10868c2ecf20Sopenharmony_ci{ 10878c2ecf20Sopenharmony_ci cfb->func_use_count += 1; 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci if (cfb->func_use_count == 1) { 10908c2ecf20Sopenharmony_ci int old; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci old = cyber2000_grphr(EXT_FUNC_CTL, cfb); 10938c2ecf20Sopenharmony_ci old |= EXT_FUNC_CTL_EXTREGENBL; 10948c2ecf20Sopenharmony_ci cyber2000_grphw(EXT_FUNC_CTL, old, cfb); 10958c2ecf20Sopenharmony_ci } 10968c2ecf20Sopenharmony_ci} 10978c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cyber2000fb_enable_extregs); 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci/* 11008c2ecf20Sopenharmony_ci * Disable access to the extended registers 11018c2ecf20Sopenharmony_ci */ 11028c2ecf20Sopenharmony_civoid cyber2000fb_disable_extregs(struct cfb_info *cfb) 11038c2ecf20Sopenharmony_ci{ 11048c2ecf20Sopenharmony_ci if (cfb->func_use_count == 1) { 11058c2ecf20Sopenharmony_ci int old; 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci old = cyber2000_grphr(EXT_FUNC_CTL, cfb); 11088c2ecf20Sopenharmony_ci old &= ~EXT_FUNC_CTL_EXTREGENBL; 11098c2ecf20Sopenharmony_ci cyber2000_grphw(EXT_FUNC_CTL, old, cfb); 11108c2ecf20Sopenharmony_ci } 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci if (cfb->func_use_count == 0) 11138c2ecf20Sopenharmony_ci printk(KERN_ERR "disable_extregs: count = 0\n"); 11148c2ecf20Sopenharmony_ci else 11158c2ecf20Sopenharmony_ci cfb->func_use_count -= 1; 11168c2ecf20Sopenharmony_ci} 11178c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cyber2000fb_disable_extregs); 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci/* 11208c2ecf20Sopenharmony_ci * Attach a capture/tv driver to the core CyberX0X0 driver. 11218c2ecf20Sopenharmony_ci */ 11228c2ecf20Sopenharmony_ciint cyber2000fb_attach(struct cyberpro_info *info, int idx) 11238c2ecf20Sopenharmony_ci{ 11248c2ecf20Sopenharmony_ci if (int_cfb_info != NULL) { 11258c2ecf20Sopenharmony_ci info->dev = int_cfb_info->fb.device; 11268c2ecf20Sopenharmony_ci#ifdef CONFIG_FB_CYBER2000_I2C 11278c2ecf20Sopenharmony_ci info->i2c = &int_cfb_info->i2c_adapter; 11288c2ecf20Sopenharmony_ci#else 11298c2ecf20Sopenharmony_ci info->i2c = NULL; 11308c2ecf20Sopenharmony_ci#endif 11318c2ecf20Sopenharmony_ci info->regs = int_cfb_info->regs; 11328c2ecf20Sopenharmony_ci info->irq = int_cfb_info->irq; 11338c2ecf20Sopenharmony_ci info->fb = int_cfb_info->fb.screen_base; 11348c2ecf20Sopenharmony_ci info->fb_size = int_cfb_info->fb.fix.smem_len; 11358c2ecf20Sopenharmony_ci info->info = int_cfb_info; 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci strlcpy(info->dev_name, int_cfb_info->fb.fix.id, 11388c2ecf20Sopenharmony_ci sizeof(info->dev_name)); 11398c2ecf20Sopenharmony_ci } 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci return int_cfb_info != NULL; 11428c2ecf20Sopenharmony_ci} 11438c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cyber2000fb_attach); 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci/* 11468c2ecf20Sopenharmony_ci * Detach a capture/tv driver from the core CyberX0X0 driver. 11478c2ecf20Sopenharmony_ci */ 11488c2ecf20Sopenharmony_civoid cyber2000fb_detach(int idx) 11498c2ecf20Sopenharmony_ci{ 11508c2ecf20Sopenharmony_ci} 11518c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cyber2000fb_detach); 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci#ifdef CONFIG_FB_CYBER2000_DDC 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci#define DDC_REG 0xb0 11568c2ecf20Sopenharmony_ci#define DDC_SCL_OUT (1 << 0) 11578c2ecf20Sopenharmony_ci#define DDC_SDA_OUT (1 << 4) 11588c2ecf20Sopenharmony_ci#define DDC_SCL_IN (1 << 2) 11598c2ecf20Sopenharmony_ci#define DDC_SDA_IN (1 << 6) 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_cistatic void cyber2000fb_enable_ddc(struct cfb_info *cfb) 11628c2ecf20Sopenharmony_ci __acquires(&cfb->reg_b0_lock) 11638c2ecf20Sopenharmony_ci{ 11648c2ecf20Sopenharmony_ci spin_lock(&cfb->reg_b0_lock); 11658c2ecf20Sopenharmony_ci cyber2000fb_writew(0x1bf, 0x3ce, cfb); 11668c2ecf20Sopenharmony_ci} 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_cistatic void cyber2000fb_disable_ddc(struct cfb_info *cfb) 11698c2ecf20Sopenharmony_ci __releases(&cfb->reg_b0_lock) 11708c2ecf20Sopenharmony_ci{ 11718c2ecf20Sopenharmony_ci cyber2000fb_writew(0x0bf, 0x3ce, cfb); 11728c2ecf20Sopenharmony_ci spin_unlock(&cfb->reg_b0_lock); 11738c2ecf20Sopenharmony_ci} 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_cistatic void cyber2000fb_ddc_setscl(void *data, int val) 11778c2ecf20Sopenharmony_ci{ 11788c2ecf20Sopenharmony_ci struct cfb_info *cfb = data; 11798c2ecf20Sopenharmony_ci unsigned char reg; 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci cyber2000fb_enable_ddc(cfb); 11828c2ecf20Sopenharmony_ci reg = cyber2000_grphr(DDC_REG, cfb); 11838c2ecf20Sopenharmony_ci if (!val) /* bit is inverted */ 11848c2ecf20Sopenharmony_ci reg |= DDC_SCL_OUT; 11858c2ecf20Sopenharmony_ci else 11868c2ecf20Sopenharmony_ci reg &= ~DDC_SCL_OUT; 11878c2ecf20Sopenharmony_ci cyber2000_grphw(DDC_REG, reg, cfb); 11888c2ecf20Sopenharmony_ci cyber2000fb_disable_ddc(cfb); 11898c2ecf20Sopenharmony_ci} 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_cistatic void cyber2000fb_ddc_setsda(void *data, int val) 11928c2ecf20Sopenharmony_ci{ 11938c2ecf20Sopenharmony_ci struct cfb_info *cfb = data; 11948c2ecf20Sopenharmony_ci unsigned char reg; 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci cyber2000fb_enable_ddc(cfb); 11978c2ecf20Sopenharmony_ci reg = cyber2000_grphr(DDC_REG, cfb); 11988c2ecf20Sopenharmony_ci if (!val) /* bit is inverted */ 11998c2ecf20Sopenharmony_ci reg |= DDC_SDA_OUT; 12008c2ecf20Sopenharmony_ci else 12018c2ecf20Sopenharmony_ci reg &= ~DDC_SDA_OUT; 12028c2ecf20Sopenharmony_ci cyber2000_grphw(DDC_REG, reg, cfb); 12038c2ecf20Sopenharmony_ci cyber2000fb_disable_ddc(cfb); 12048c2ecf20Sopenharmony_ci} 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_cistatic int cyber2000fb_ddc_getscl(void *data) 12078c2ecf20Sopenharmony_ci{ 12088c2ecf20Sopenharmony_ci struct cfb_info *cfb = data; 12098c2ecf20Sopenharmony_ci int retval; 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci cyber2000fb_enable_ddc(cfb); 12128c2ecf20Sopenharmony_ci retval = !!(cyber2000_grphr(DDC_REG, cfb) & DDC_SCL_IN); 12138c2ecf20Sopenharmony_ci cyber2000fb_disable_ddc(cfb); 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci return retval; 12168c2ecf20Sopenharmony_ci} 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_cistatic int cyber2000fb_ddc_getsda(void *data) 12198c2ecf20Sopenharmony_ci{ 12208c2ecf20Sopenharmony_ci struct cfb_info *cfb = data; 12218c2ecf20Sopenharmony_ci int retval; 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci cyber2000fb_enable_ddc(cfb); 12248c2ecf20Sopenharmony_ci retval = !!(cyber2000_grphr(DDC_REG, cfb) & DDC_SDA_IN); 12258c2ecf20Sopenharmony_ci cyber2000fb_disable_ddc(cfb); 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci return retval; 12288c2ecf20Sopenharmony_ci} 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_cistatic int cyber2000fb_setup_ddc_bus(struct cfb_info *cfb) 12318c2ecf20Sopenharmony_ci{ 12328c2ecf20Sopenharmony_ci strlcpy(cfb->ddc_adapter.name, cfb->fb.fix.id, 12338c2ecf20Sopenharmony_ci sizeof(cfb->ddc_adapter.name)); 12348c2ecf20Sopenharmony_ci cfb->ddc_adapter.owner = THIS_MODULE; 12358c2ecf20Sopenharmony_ci cfb->ddc_adapter.class = I2C_CLASS_DDC; 12368c2ecf20Sopenharmony_ci cfb->ddc_adapter.algo_data = &cfb->ddc_algo; 12378c2ecf20Sopenharmony_ci cfb->ddc_adapter.dev.parent = cfb->fb.device; 12388c2ecf20Sopenharmony_ci cfb->ddc_algo.setsda = cyber2000fb_ddc_setsda; 12398c2ecf20Sopenharmony_ci cfb->ddc_algo.setscl = cyber2000fb_ddc_setscl; 12408c2ecf20Sopenharmony_ci cfb->ddc_algo.getsda = cyber2000fb_ddc_getsda; 12418c2ecf20Sopenharmony_ci cfb->ddc_algo.getscl = cyber2000fb_ddc_getscl; 12428c2ecf20Sopenharmony_ci cfb->ddc_algo.udelay = 10; 12438c2ecf20Sopenharmony_ci cfb->ddc_algo.timeout = 20; 12448c2ecf20Sopenharmony_ci cfb->ddc_algo.data = cfb; 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci i2c_set_adapdata(&cfb->ddc_adapter, cfb); 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci return i2c_bit_add_bus(&cfb->ddc_adapter); 12498c2ecf20Sopenharmony_ci} 12508c2ecf20Sopenharmony_ci#endif /* CONFIG_FB_CYBER2000_DDC */ 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci#ifdef CONFIG_FB_CYBER2000_I2C 12538c2ecf20Sopenharmony_cistatic void cyber2000fb_i2c_setsda(void *data, int state) 12548c2ecf20Sopenharmony_ci{ 12558c2ecf20Sopenharmony_ci struct cfb_info *cfb = data; 12568c2ecf20Sopenharmony_ci unsigned int latch2; 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci spin_lock(&cfb->reg_b0_lock); 12598c2ecf20Sopenharmony_ci latch2 = cyber2000_grphr(EXT_LATCH2, cfb); 12608c2ecf20Sopenharmony_ci latch2 &= EXT_LATCH2_I2C_CLKEN; 12618c2ecf20Sopenharmony_ci if (state) 12628c2ecf20Sopenharmony_ci latch2 |= EXT_LATCH2_I2C_DATEN; 12638c2ecf20Sopenharmony_ci cyber2000_grphw(EXT_LATCH2, latch2, cfb); 12648c2ecf20Sopenharmony_ci spin_unlock(&cfb->reg_b0_lock); 12658c2ecf20Sopenharmony_ci} 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_cistatic void cyber2000fb_i2c_setscl(void *data, int state) 12688c2ecf20Sopenharmony_ci{ 12698c2ecf20Sopenharmony_ci struct cfb_info *cfb = data; 12708c2ecf20Sopenharmony_ci unsigned int latch2; 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci spin_lock(&cfb->reg_b0_lock); 12738c2ecf20Sopenharmony_ci latch2 = cyber2000_grphr(EXT_LATCH2, cfb); 12748c2ecf20Sopenharmony_ci latch2 &= EXT_LATCH2_I2C_DATEN; 12758c2ecf20Sopenharmony_ci if (state) 12768c2ecf20Sopenharmony_ci latch2 |= EXT_LATCH2_I2C_CLKEN; 12778c2ecf20Sopenharmony_ci cyber2000_grphw(EXT_LATCH2, latch2, cfb); 12788c2ecf20Sopenharmony_ci spin_unlock(&cfb->reg_b0_lock); 12798c2ecf20Sopenharmony_ci} 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_cistatic int cyber2000fb_i2c_getsda(void *data) 12828c2ecf20Sopenharmony_ci{ 12838c2ecf20Sopenharmony_ci struct cfb_info *cfb = data; 12848c2ecf20Sopenharmony_ci int ret; 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci spin_lock(&cfb->reg_b0_lock); 12878c2ecf20Sopenharmony_ci ret = !!(cyber2000_grphr(EXT_LATCH2, cfb) & EXT_LATCH2_I2C_DAT); 12888c2ecf20Sopenharmony_ci spin_unlock(&cfb->reg_b0_lock); 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci return ret; 12918c2ecf20Sopenharmony_ci} 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_cistatic int cyber2000fb_i2c_getscl(void *data) 12948c2ecf20Sopenharmony_ci{ 12958c2ecf20Sopenharmony_ci struct cfb_info *cfb = data; 12968c2ecf20Sopenharmony_ci int ret; 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci spin_lock(&cfb->reg_b0_lock); 12998c2ecf20Sopenharmony_ci ret = !!(cyber2000_grphr(EXT_LATCH2, cfb) & EXT_LATCH2_I2C_CLK); 13008c2ecf20Sopenharmony_ci spin_unlock(&cfb->reg_b0_lock); 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci return ret; 13038c2ecf20Sopenharmony_ci} 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_cistatic int cyber2000fb_i2c_register(struct cfb_info *cfb) 13068c2ecf20Sopenharmony_ci{ 13078c2ecf20Sopenharmony_ci strlcpy(cfb->i2c_adapter.name, cfb->fb.fix.id, 13088c2ecf20Sopenharmony_ci sizeof(cfb->i2c_adapter.name)); 13098c2ecf20Sopenharmony_ci cfb->i2c_adapter.owner = THIS_MODULE; 13108c2ecf20Sopenharmony_ci cfb->i2c_adapter.algo_data = &cfb->i2c_algo; 13118c2ecf20Sopenharmony_ci cfb->i2c_adapter.dev.parent = cfb->fb.device; 13128c2ecf20Sopenharmony_ci cfb->i2c_algo.setsda = cyber2000fb_i2c_setsda; 13138c2ecf20Sopenharmony_ci cfb->i2c_algo.setscl = cyber2000fb_i2c_setscl; 13148c2ecf20Sopenharmony_ci cfb->i2c_algo.getsda = cyber2000fb_i2c_getsda; 13158c2ecf20Sopenharmony_ci cfb->i2c_algo.getscl = cyber2000fb_i2c_getscl; 13168c2ecf20Sopenharmony_ci cfb->i2c_algo.udelay = 5; 13178c2ecf20Sopenharmony_ci cfb->i2c_algo.timeout = msecs_to_jiffies(100); 13188c2ecf20Sopenharmony_ci cfb->i2c_algo.data = cfb; 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci return i2c_bit_add_bus(&cfb->i2c_adapter); 13218c2ecf20Sopenharmony_ci} 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_cistatic void cyber2000fb_i2c_unregister(struct cfb_info *cfb) 13248c2ecf20Sopenharmony_ci{ 13258c2ecf20Sopenharmony_ci i2c_del_adapter(&cfb->i2c_adapter); 13268c2ecf20Sopenharmony_ci} 13278c2ecf20Sopenharmony_ci#else 13288c2ecf20Sopenharmony_ci#define cyber2000fb_i2c_register(cfb) (0) 13298c2ecf20Sopenharmony_ci#define cyber2000fb_i2c_unregister(cfb) do { } while (0) 13308c2ecf20Sopenharmony_ci#endif 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci/* 13338c2ecf20Sopenharmony_ci * These parameters give 13348c2ecf20Sopenharmony_ci * 640x480, hsync 31.5kHz, vsync 60Hz 13358c2ecf20Sopenharmony_ci */ 13368c2ecf20Sopenharmony_cistatic const struct fb_videomode cyber2000fb_default_mode = { 13378c2ecf20Sopenharmony_ci .refresh = 60, 13388c2ecf20Sopenharmony_ci .xres = 640, 13398c2ecf20Sopenharmony_ci .yres = 480, 13408c2ecf20Sopenharmony_ci .pixclock = 39722, 13418c2ecf20Sopenharmony_ci .left_margin = 56, 13428c2ecf20Sopenharmony_ci .right_margin = 16, 13438c2ecf20Sopenharmony_ci .upper_margin = 34, 13448c2ecf20Sopenharmony_ci .lower_margin = 9, 13458c2ecf20Sopenharmony_ci .hsync_len = 88, 13468c2ecf20Sopenharmony_ci .vsync_len = 2, 13478c2ecf20Sopenharmony_ci .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 13488c2ecf20Sopenharmony_ci .vmode = FB_VMODE_NONINTERLACED 13498c2ecf20Sopenharmony_ci}; 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_cistatic char igs_regs[] = { 13528c2ecf20Sopenharmony_ci EXT_CRT_IRQ, 0, 13538c2ecf20Sopenharmony_ci EXT_CRT_TEST, 0, 13548c2ecf20Sopenharmony_ci EXT_SYNC_CTL, 0, 13558c2ecf20Sopenharmony_ci EXT_SEG_WRITE_PTR, 0, 13568c2ecf20Sopenharmony_ci EXT_SEG_READ_PTR, 0, 13578c2ecf20Sopenharmony_ci EXT_BIU_MISC, EXT_BIU_MISC_LIN_ENABLE | 13588c2ecf20Sopenharmony_ci EXT_BIU_MISC_COP_ENABLE | 13598c2ecf20Sopenharmony_ci EXT_BIU_MISC_COP_BFC, 13608c2ecf20Sopenharmony_ci EXT_FUNC_CTL, 0, 13618c2ecf20Sopenharmony_ci CURS_H_START, 0, 13628c2ecf20Sopenharmony_ci CURS_H_START + 1, 0, 13638c2ecf20Sopenharmony_ci CURS_H_PRESET, 0, 13648c2ecf20Sopenharmony_ci CURS_V_START, 0, 13658c2ecf20Sopenharmony_ci CURS_V_START + 1, 0, 13668c2ecf20Sopenharmony_ci CURS_V_PRESET, 0, 13678c2ecf20Sopenharmony_ci CURS_CTL, 0, 13688c2ecf20Sopenharmony_ci EXT_ATTRIB_CTL, EXT_ATTRIB_CTL_EXT, 13698c2ecf20Sopenharmony_ci EXT_OVERSCAN_RED, 0, 13708c2ecf20Sopenharmony_ci EXT_OVERSCAN_GREEN, 0, 13718c2ecf20Sopenharmony_ci EXT_OVERSCAN_BLUE, 0, 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci /* some of these are questionable when we have a BIOS */ 13748c2ecf20Sopenharmony_ci EXT_MEM_CTL0, EXT_MEM_CTL0_7CLK | 13758c2ecf20Sopenharmony_ci EXT_MEM_CTL0_RAS_1 | 13768c2ecf20Sopenharmony_ci EXT_MEM_CTL0_MULTCAS, 13778c2ecf20Sopenharmony_ci EXT_HIDDEN_CTL1, 0x30, 13788c2ecf20Sopenharmony_ci EXT_FIFO_CTL, 0x0b, 13798c2ecf20Sopenharmony_ci EXT_FIFO_CTL + 1, 0x17, 13808c2ecf20Sopenharmony_ci 0x76, 0x00, 13818c2ecf20Sopenharmony_ci EXT_HIDDEN_CTL4, 0xc8 13828c2ecf20Sopenharmony_ci}; 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci/* 13858c2ecf20Sopenharmony_ci * Initialise the CyberPro hardware. On the CyberPro5XXXX, 13868c2ecf20Sopenharmony_ci * ensure that we're using the correct PLL (5XXX's may be 13878c2ecf20Sopenharmony_ci * programmed to use an additional set of PLLs.) 13888c2ecf20Sopenharmony_ci */ 13898c2ecf20Sopenharmony_cistatic void cyberpro_init_hw(struct cfb_info *cfb) 13908c2ecf20Sopenharmony_ci{ 13918c2ecf20Sopenharmony_ci int i; 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci for (i = 0; i < sizeof(igs_regs); i += 2) 13948c2ecf20Sopenharmony_ci cyber2000_grphw(igs_regs[i], igs_regs[i + 1], cfb); 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci if (cfb->id == ID_CYBERPRO_5000) { 13978c2ecf20Sopenharmony_ci unsigned char val; 13988c2ecf20Sopenharmony_ci cyber2000fb_writeb(0xba, 0x3ce, cfb); 13998c2ecf20Sopenharmony_ci val = cyber2000fb_readb(0x3cf, cfb) & 0x80; 14008c2ecf20Sopenharmony_ci cyber2000fb_writeb(val, 0x3cf, cfb); 14018c2ecf20Sopenharmony_ci } 14028c2ecf20Sopenharmony_ci} 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_cistatic struct cfb_info *cyberpro_alloc_fb_info(unsigned int id, char *name) 14058c2ecf20Sopenharmony_ci{ 14068c2ecf20Sopenharmony_ci struct cfb_info *cfb; 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_ci cfb = kzalloc(sizeof(struct cfb_info), GFP_KERNEL); 14098c2ecf20Sopenharmony_ci if (!cfb) 14108c2ecf20Sopenharmony_ci return NULL; 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci cfb->id = id; 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci if (id == ID_CYBERPRO_5000) 14168c2ecf20Sopenharmony_ci cfb->ref_ps = 40690; /* 24.576 MHz */ 14178c2ecf20Sopenharmony_ci else 14188c2ecf20Sopenharmony_ci cfb->ref_ps = 69842; /* 14.31818 MHz (69841?) */ 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_ci cfb->divisors[0] = 1; 14218c2ecf20Sopenharmony_ci cfb->divisors[1] = 2; 14228c2ecf20Sopenharmony_ci cfb->divisors[2] = 4; 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci if (id == ID_CYBERPRO_2000) 14258c2ecf20Sopenharmony_ci cfb->divisors[3] = 8; 14268c2ecf20Sopenharmony_ci else 14278c2ecf20Sopenharmony_ci cfb->divisors[3] = 6; 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci strcpy(cfb->fb.fix.id, name); 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci cfb->fb.fix.type = FB_TYPE_PACKED_PIXELS; 14328c2ecf20Sopenharmony_ci cfb->fb.fix.type_aux = 0; 14338c2ecf20Sopenharmony_ci cfb->fb.fix.xpanstep = 0; 14348c2ecf20Sopenharmony_ci cfb->fb.fix.ypanstep = 1; 14358c2ecf20Sopenharmony_ci cfb->fb.fix.ywrapstep = 0; 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci switch (id) { 14388c2ecf20Sopenharmony_ci case ID_IGA_1682: 14398c2ecf20Sopenharmony_ci cfb->fb.fix.accel = 0; 14408c2ecf20Sopenharmony_ci break; 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_ci case ID_CYBERPRO_2000: 14438c2ecf20Sopenharmony_ci cfb->fb.fix.accel = FB_ACCEL_IGS_CYBER2000; 14448c2ecf20Sopenharmony_ci break; 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_ci case ID_CYBERPRO_2010: 14478c2ecf20Sopenharmony_ci cfb->fb.fix.accel = FB_ACCEL_IGS_CYBER2010; 14488c2ecf20Sopenharmony_ci break; 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci case ID_CYBERPRO_5000: 14518c2ecf20Sopenharmony_ci cfb->fb.fix.accel = FB_ACCEL_IGS_CYBER5000; 14528c2ecf20Sopenharmony_ci break; 14538c2ecf20Sopenharmony_ci } 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_ci cfb->fb.var.nonstd = 0; 14568c2ecf20Sopenharmony_ci cfb->fb.var.activate = FB_ACTIVATE_NOW; 14578c2ecf20Sopenharmony_ci cfb->fb.var.height = -1; 14588c2ecf20Sopenharmony_ci cfb->fb.var.width = -1; 14598c2ecf20Sopenharmony_ci cfb->fb.var.accel_flags = FB_ACCELF_TEXT; 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_ci cfb->fb.fbops = &cyber2000fb_ops; 14628c2ecf20Sopenharmony_ci cfb->fb.flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN; 14638c2ecf20Sopenharmony_ci cfb->fb.pseudo_palette = cfb->pseudo_palette; 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_ci spin_lock_init(&cfb->reg_b0_lock); 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci fb_alloc_cmap(&cfb->fb.cmap, NR_PALETTE, 0); 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci return cfb; 14708c2ecf20Sopenharmony_ci} 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_cistatic void cyberpro_free_fb_info(struct cfb_info *cfb) 14738c2ecf20Sopenharmony_ci{ 14748c2ecf20Sopenharmony_ci if (cfb) { 14758c2ecf20Sopenharmony_ci /* 14768c2ecf20Sopenharmony_ci * Free the colourmap 14778c2ecf20Sopenharmony_ci */ 14788c2ecf20Sopenharmony_ci fb_alloc_cmap(&cfb->fb.cmap, 0, 0); 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci kfree(cfb); 14818c2ecf20Sopenharmony_ci } 14828c2ecf20Sopenharmony_ci} 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci/* 14858c2ecf20Sopenharmony_ci * Parse Cyber2000fb options. Usage: 14868c2ecf20Sopenharmony_ci * video=cyber2000:font:fontname 14878c2ecf20Sopenharmony_ci */ 14888c2ecf20Sopenharmony_ci#ifndef MODULE 14898c2ecf20Sopenharmony_cistatic int cyber2000fb_setup(char *options) 14908c2ecf20Sopenharmony_ci{ 14918c2ecf20Sopenharmony_ci char *opt; 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_ci if (!options || !*options) 14948c2ecf20Sopenharmony_ci return 0; 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci while ((opt = strsep(&options, ",")) != NULL) { 14978c2ecf20Sopenharmony_ci if (!*opt) 14988c2ecf20Sopenharmony_ci continue; 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_ci if (strncmp(opt, "font:", 5) == 0) { 15018c2ecf20Sopenharmony_ci static char default_font_storage[40]; 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci strlcpy(default_font_storage, opt + 5, 15048c2ecf20Sopenharmony_ci sizeof(default_font_storage)); 15058c2ecf20Sopenharmony_ci default_font = default_font_storage; 15068c2ecf20Sopenharmony_ci continue; 15078c2ecf20Sopenharmony_ci } 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci printk(KERN_ERR "CyberPro20x0: unknown parameter: %s\n", opt); 15108c2ecf20Sopenharmony_ci } 15118c2ecf20Sopenharmony_ci return 0; 15128c2ecf20Sopenharmony_ci} 15138c2ecf20Sopenharmony_ci#endif /* MODULE */ 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ci/* 15168c2ecf20Sopenharmony_ci * The CyberPro chips can be placed on many different bus types. 15178c2ecf20Sopenharmony_ci * This probe function is common to all bus types. The bus-specific 15188c2ecf20Sopenharmony_ci * probe function is expected to have: 15198c2ecf20Sopenharmony_ci * - enabled access to the linear memory region 15208c2ecf20Sopenharmony_ci * - memory mapped access to the registers 15218c2ecf20Sopenharmony_ci * - initialised mem_ctl1 and mem_ctl2 appropriately. 15228c2ecf20Sopenharmony_ci */ 15238c2ecf20Sopenharmony_cistatic int cyberpro_common_probe(struct cfb_info *cfb) 15248c2ecf20Sopenharmony_ci{ 15258c2ecf20Sopenharmony_ci u_long smem_size; 15268c2ecf20Sopenharmony_ci u_int h_sync, v_sync; 15278c2ecf20Sopenharmony_ci int err; 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_ci cyberpro_init_hw(cfb); 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_ci /* 15328c2ecf20Sopenharmony_ci * Get the video RAM size and width from the VGA register. 15338c2ecf20Sopenharmony_ci * This should have been already initialised by the BIOS, 15348c2ecf20Sopenharmony_ci * but if it's garbage, claim default 1MB VRAM (woody) 15358c2ecf20Sopenharmony_ci */ 15368c2ecf20Sopenharmony_ci cfb->mem_ctl1 = cyber2000_grphr(EXT_MEM_CTL1, cfb); 15378c2ecf20Sopenharmony_ci cfb->mem_ctl2 = cyber2000_grphr(EXT_MEM_CTL2, cfb); 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_ci /* 15408c2ecf20Sopenharmony_ci * Determine the size of the memory. 15418c2ecf20Sopenharmony_ci */ 15428c2ecf20Sopenharmony_ci switch (cfb->mem_ctl2 & MEM_CTL2_SIZE_MASK) { 15438c2ecf20Sopenharmony_ci case MEM_CTL2_SIZE_4MB: 15448c2ecf20Sopenharmony_ci smem_size = 0x00400000; 15458c2ecf20Sopenharmony_ci break; 15468c2ecf20Sopenharmony_ci case MEM_CTL2_SIZE_2MB: 15478c2ecf20Sopenharmony_ci smem_size = 0x00200000; 15488c2ecf20Sopenharmony_ci break; 15498c2ecf20Sopenharmony_ci case MEM_CTL2_SIZE_1MB: 15508c2ecf20Sopenharmony_ci smem_size = 0x00100000; 15518c2ecf20Sopenharmony_ci break; 15528c2ecf20Sopenharmony_ci default: 15538c2ecf20Sopenharmony_ci smem_size = 0x00100000; 15548c2ecf20Sopenharmony_ci break; 15558c2ecf20Sopenharmony_ci } 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_ci cfb->fb.fix.smem_len = smem_size; 15588c2ecf20Sopenharmony_ci cfb->fb.fix.mmio_len = MMIO_SIZE; 15598c2ecf20Sopenharmony_ci cfb->fb.screen_base = cfb->region; 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_ci#ifdef CONFIG_FB_CYBER2000_DDC 15628c2ecf20Sopenharmony_ci if (cyber2000fb_setup_ddc_bus(cfb) == 0) 15638c2ecf20Sopenharmony_ci cfb->ddc_registered = true; 15648c2ecf20Sopenharmony_ci#endif 15658c2ecf20Sopenharmony_ci 15668c2ecf20Sopenharmony_ci err = -EINVAL; 15678c2ecf20Sopenharmony_ci if (!fb_find_mode(&cfb->fb.var, &cfb->fb, NULL, NULL, 0, 15688c2ecf20Sopenharmony_ci &cyber2000fb_default_mode, 8)) { 15698c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: no valid mode found\n", cfb->fb.fix.id); 15708c2ecf20Sopenharmony_ci goto failed; 15718c2ecf20Sopenharmony_ci } 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_ci cfb->fb.var.yres_virtual = cfb->fb.fix.smem_len * 8 / 15748c2ecf20Sopenharmony_ci (cfb->fb.var.bits_per_pixel * cfb->fb.var.xres_virtual); 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_ci if (cfb->fb.var.yres_virtual < cfb->fb.var.yres) 15778c2ecf20Sopenharmony_ci cfb->fb.var.yres_virtual = cfb->fb.var.yres; 15788c2ecf20Sopenharmony_ci 15798c2ecf20Sopenharmony_ci/* fb_set_var(&cfb->fb.var, -1, &cfb->fb); */ 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci /* 15828c2ecf20Sopenharmony_ci * Calculate the hsync and vsync frequencies. Note that 15838c2ecf20Sopenharmony_ci * we split the 1e12 constant up so that we can preserve 15848c2ecf20Sopenharmony_ci * the precision and fit the results into 32-bit registers. 15858c2ecf20Sopenharmony_ci * (1953125000 * 512 = 1e12) 15868c2ecf20Sopenharmony_ci */ 15878c2ecf20Sopenharmony_ci h_sync = 1953125000 / cfb->fb.var.pixclock; 15888c2ecf20Sopenharmony_ci h_sync = h_sync * 512 / (cfb->fb.var.xres + cfb->fb.var.left_margin + 15898c2ecf20Sopenharmony_ci cfb->fb.var.right_margin + cfb->fb.var.hsync_len); 15908c2ecf20Sopenharmony_ci v_sync = h_sync / (cfb->fb.var.yres + cfb->fb.var.upper_margin + 15918c2ecf20Sopenharmony_ci cfb->fb.var.lower_margin + cfb->fb.var.vsync_len); 15928c2ecf20Sopenharmony_ci 15938c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: %dKiB VRAM, using %dx%d, %d.%03dkHz, %dHz\n", 15948c2ecf20Sopenharmony_ci cfb->fb.fix.id, cfb->fb.fix.smem_len >> 10, 15958c2ecf20Sopenharmony_ci cfb->fb.var.xres, cfb->fb.var.yres, 15968c2ecf20Sopenharmony_ci h_sync / 1000, h_sync % 1000, v_sync); 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_ci err = cyber2000fb_i2c_register(cfb); 15998c2ecf20Sopenharmony_ci if (err) 16008c2ecf20Sopenharmony_ci goto failed; 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_ci err = register_framebuffer(&cfb->fb); 16038c2ecf20Sopenharmony_ci if (err) 16048c2ecf20Sopenharmony_ci cyber2000fb_i2c_unregister(cfb); 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_cifailed: 16078c2ecf20Sopenharmony_ci#ifdef CONFIG_FB_CYBER2000_DDC 16088c2ecf20Sopenharmony_ci if (err && cfb->ddc_registered) 16098c2ecf20Sopenharmony_ci i2c_del_adapter(&cfb->ddc_adapter); 16108c2ecf20Sopenharmony_ci#endif 16118c2ecf20Sopenharmony_ci return err; 16128c2ecf20Sopenharmony_ci} 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_cistatic void cyberpro_common_remove(struct cfb_info *cfb) 16158c2ecf20Sopenharmony_ci{ 16168c2ecf20Sopenharmony_ci unregister_framebuffer(&cfb->fb); 16178c2ecf20Sopenharmony_ci#ifdef CONFIG_FB_CYBER2000_DDC 16188c2ecf20Sopenharmony_ci if (cfb->ddc_registered) 16198c2ecf20Sopenharmony_ci i2c_del_adapter(&cfb->ddc_adapter); 16208c2ecf20Sopenharmony_ci#endif 16218c2ecf20Sopenharmony_ci cyber2000fb_i2c_unregister(cfb); 16228c2ecf20Sopenharmony_ci} 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_cistatic void cyberpro_common_resume(struct cfb_info *cfb) 16258c2ecf20Sopenharmony_ci{ 16268c2ecf20Sopenharmony_ci cyberpro_init_hw(cfb); 16278c2ecf20Sopenharmony_ci 16288c2ecf20Sopenharmony_ci /* 16298c2ecf20Sopenharmony_ci * Reprogram the MEM_CTL1 and MEM_CTL2 registers 16308c2ecf20Sopenharmony_ci */ 16318c2ecf20Sopenharmony_ci cyber2000_grphw(EXT_MEM_CTL1, cfb->mem_ctl1, cfb); 16328c2ecf20Sopenharmony_ci cyber2000_grphw(EXT_MEM_CTL2, cfb->mem_ctl2, cfb); 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci /* 16358c2ecf20Sopenharmony_ci * Restore the old video mode and the palette. 16368c2ecf20Sopenharmony_ci * We also need to tell fbcon to redraw the console. 16378c2ecf20Sopenharmony_ci */ 16388c2ecf20Sopenharmony_ci cyber2000fb_set_par(&cfb->fb); 16398c2ecf20Sopenharmony_ci} 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci/* 16428c2ecf20Sopenharmony_ci * We need to wake up the CyberPro, and make sure its in linear memory 16438c2ecf20Sopenharmony_ci * mode. Unfortunately, this is specific to the platform and card that 16448c2ecf20Sopenharmony_ci * we are running on. 16458c2ecf20Sopenharmony_ci * 16468c2ecf20Sopenharmony_ci * On x86 and ARM, should we be initialising the CyberPro first via the 16478c2ecf20Sopenharmony_ci * IO registers, and then the MMIO registers to catch all cases? Can we 16488c2ecf20Sopenharmony_ci * end up in the situation where the chip is in MMIO mode, but not awake 16498c2ecf20Sopenharmony_ci * on an x86 system? 16508c2ecf20Sopenharmony_ci */ 16518c2ecf20Sopenharmony_cistatic int cyberpro_pci_enable_mmio(struct cfb_info *cfb) 16528c2ecf20Sopenharmony_ci{ 16538c2ecf20Sopenharmony_ci unsigned char val; 16548c2ecf20Sopenharmony_ci 16558c2ecf20Sopenharmony_ci#if defined(__sparc_v9__) 16568c2ecf20Sopenharmony_ci#error "You lose, consult DaveM." 16578c2ecf20Sopenharmony_ci#elif defined(__sparc__) 16588c2ecf20Sopenharmony_ci /* 16598c2ecf20Sopenharmony_ci * SPARC does not have an "outb" instruction, so we generate 16608c2ecf20Sopenharmony_ci * I/O cycles storing into a reserved memory space at 16618c2ecf20Sopenharmony_ci * physical address 0x3000000 16628c2ecf20Sopenharmony_ci */ 16638c2ecf20Sopenharmony_ci unsigned char __iomem *iop; 16648c2ecf20Sopenharmony_ci 16658c2ecf20Sopenharmony_ci iop = ioremap(0x3000000, 0x5000); 16668c2ecf20Sopenharmony_ci if (iop == NULL) { 16678c2ecf20Sopenharmony_ci printk(KERN_ERR "iga5000: cannot map I/O\n"); 16688c2ecf20Sopenharmony_ci return -ENOMEM; 16698c2ecf20Sopenharmony_ci } 16708c2ecf20Sopenharmony_ci 16718c2ecf20Sopenharmony_ci writeb(0x18, iop + 0x46e8); 16728c2ecf20Sopenharmony_ci writeb(0x01, iop + 0x102); 16738c2ecf20Sopenharmony_ci writeb(0x08, iop + 0x46e8); 16748c2ecf20Sopenharmony_ci writeb(EXT_BIU_MISC, iop + 0x3ce); 16758c2ecf20Sopenharmony_ci writeb(EXT_BIU_MISC_LIN_ENABLE, iop + 0x3cf); 16768c2ecf20Sopenharmony_ci 16778c2ecf20Sopenharmony_ci iounmap(iop); 16788c2ecf20Sopenharmony_ci#else 16798c2ecf20Sopenharmony_ci /* 16808c2ecf20Sopenharmony_ci * Most other machine types are "normal", so 16818c2ecf20Sopenharmony_ci * we use the standard IO-based wakeup. 16828c2ecf20Sopenharmony_ci */ 16838c2ecf20Sopenharmony_ci outb(0x18, 0x46e8); 16848c2ecf20Sopenharmony_ci outb(0x01, 0x102); 16858c2ecf20Sopenharmony_ci outb(0x08, 0x46e8); 16868c2ecf20Sopenharmony_ci outb(EXT_BIU_MISC, 0x3ce); 16878c2ecf20Sopenharmony_ci outb(EXT_BIU_MISC_LIN_ENABLE, 0x3cf); 16888c2ecf20Sopenharmony_ci#endif 16898c2ecf20Sopenharmony_ci 16908c2ecf20Sopenharmony_ci /* 16918c2ecf20Sopenharmony_ci * Allow the CyberPro to accept PCI burst accesses 16928c2ecf20Sopenharmony_ci */ 16938c2ecf20Sopenharmony_ci if (cfb->id == ID_CYBERPRO_2010) { 16948c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: NOT enabling PCI bursts\n", 16958c2ecf20Sopenharmony_ci cfb->fb.fix.id); 16968c2ecf20Sopenharmony_ci } else { 16978c2ecf20Sopenharmony_ci val = cyber2000_grphr(EXT_BUS_CTL, cfb); 16988c2ecf20Sopenharmony_ci if (!(val & EXT_BUS_CTL_PCIBURST_WRITE)) { 16998c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: enabling PCI bursts\n", 17008c2ecf20Sopenharmony_ci cfb->fb.fix.id); 17018c2ecf20Sopenharmony_ci 17028c2ecf20Sopenharmony_ci val |= EXT_BUS_CTL_PCIBURST_WRITE; 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_ci if (cfb->id == ID_CYBERPRO_5000) 17058c2ecf20Sopenharmony_ci val |= EXT_BUS_CTL_PCIBURST_READ; 17068c2ecf20Sopenharmony_ci 17078c2ecf20Sopenharmony_ci cyber2000_grphw(EXT_BUS_CTL, val, cfb); 17088c2ecf20Sopenharmony_ci } 17098c2ecf20Sopenharmony_ci } 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_ci return 0; 17128c2ecf20Sopenharmony_ci} 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_cistatic int cyberpro_pci_probe(struct pci_dev *dev, 17158c2ecf20Sopenharmony_ci const struct pci_device_id *id) 17168c2ecf20Sopenharmony_ci{ 17178c2ecf20Sopenharmony_ci struct cfb_info *cfb; 17188c2ecf20Sopenharmony_ci char name[16]; 17198c2ecf20Sopenharmony_ci int err; 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_ci sprintf(name, "CyberPro%4X", id->device); 17228c2ecf20Sopenharmony_ci 17238c2ecf20Sopenharmony_ci err = pci_enable_device(dev); 17248c2ecf20Sopenharmony_ci if (err) 17258c2ecf20Sopenharmony_ci return err; 17268c2ecf20Sopenharmony_ci 17278c2ecf20Sopenharmony_ci err = -ENOMEM; 17288c2ecf20Sopenharmony_ci cfb = cyberpro_alloc_fb_info(id->driver_data, name); 17298c2ecf20Sopenharmony_ci if (!cfb) 17308c2ecf20Sopenharmony_ci goto failed_release; 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_ci err = pci_request_regions(dev, cfb->fb.fix.id); 17338c2ecf20Sopenharmony_ci if (err) 17348c2ecf20Sopenharmony_ci goto failed_regions; 17358c2ecf20Sopenharmony_ci 17368c2ecf20Sopenharmony_ci cfb->irq = dev->irq; 17378c2ecf20Sopenharmony_ci cfb->region = pci_ioremap_bar(dev, 0); 17388c2ecf20Sopenharmony_ci if (!cfb->region) { 17398c2ecf20Sopenharmony_ci err = -ENOMEM; 17408c2ecf20Sopenharmony_ci goto failed_ioremap; 17418c2ecf20Sopenharmony_ci } 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci cfb->regs = cfb->region + MMIO_OFFSET; 17448c2ecf20Sopenharmony_ci cfb->fb.device = &dev->dev; 17458c2ecf20Sopenharmony_ci cfb->fb.fix.mmio_start = pci_resource_start(dev, 0) + MMIO_OFFSET; 17468c2ecf20Sopenharmony_ci cfb->fb.fix.smem_start = pci_resource_start(dev, 0); 17478c2ecf20Sopenharmony_ci 17488c2ecf20Sopenharmony_ci /* 17498c2ecf20Sopenharmony_ci * Bring up the hardware. This is expected to enable access 17508c2ecf20Sopenharmony_ci * to the linear memory region, and allow access to the memory 17518c2ecf20Sopenharmony_ci * mapped registers. Also, mem_ctl1 and mem_ctl2 must be 17528c2ecf20Sopenharmony_ci * initialised. 17538c2ecf20Sopenharmony_ci */ 17548c2ecf20Sopenharmony_ci err = cyberpro_pci_enable_mmio(cfb); 17558c2ecf20Sopenharmony_ci if (err) 17568c2ecf20Sopenharmony_ci goto failed; 17578c2ecf20Sopenharmony_ci 17588c2ecf20Sopenharmony_ci /* 17598c2ecf20Sopenharmony_ci * Use MCLK from BIOS. FIXME: what about hotplug? 17608c2ecf20Sopenharmony_ci */ 17618c2ecf20Sopenharmony_ci cfb->mclk_mult = cyber2000_grphr(EXT_MCLK_MULT, cfb); 17628c2ecf20Sopenharmony_ci cfb->mclk_div = cyber2000_grphr(EXT_MCLK_DIV, cfb); 17638c2ecf20Sopenharmony_ci 17648c2ecf20Sopenharmony_ci#ifdef __arm__ 17658c2ecf20Sopenharmony_ci /* 17668c2ecf20Sopenharmony_ci * MCLK on the NetWinder and the Shark is fixed at 75MHz 17678c2ecf20Sopenharmony_ci */ 17688c2ecf20Sopenharmony_ci if (machine_is_netwinder()) { 17698c2ecf20Sopenharmony_ci cfb->mclk_mult = 0xdb; 17708c2ecf20Sopenharmony_ci cfb->mclk_div = 0x54; 17718c2ecf20Sopenharmony_ci } 17728c2ecf20Sopenharmony_ci#endif 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_ci err = cyberpro_common_probe(cfb); 17758c2ecf20Sopenharmony_ci if (err) 17768c2ecf20Sopenharmony_ci goto failed; 17778c2ecf20Sopenharmony_ci 17788c2ecf20Sopenharmony_ci /* 17798c2ecf20Sopenharmony_ci * Our driver data 17808c2ecf20Sopenharmony_ci */ 17818c2ecf20Sopenharmony_ci pci_set_drvdata(dev, cfb); 17828c2ecf20Sopenharmony_ci if (int_cfb_info == NULL) 17838c2ecf20Sopenharmony_ci int_cfb_info = cfb; 17848c2ecf20Sopenharmony_ci 17858c2ecf20Sopenharmony_ci return 0; 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_cifailed: 17888c2ecf20Sopenharmony_ci iounmap(cfb->region); 17898c2ecf20Sopenharmony_cifailed_ioremap: 17908c2ecf20Sopenharmony_ci pci_release_regions(dev); 17918c2ecf20Sopenharmony_cifailed_regions: 17928c2ecf20Sopenharmony_ci cyberpro_free_fb_info(cfb); 17938c2ecf20Sopenharmony_cifailed_release: 17948c2ecf20Sopenharmony_ci return err; 17958c2ecf20Sopenharmony_ci} 17968c2ecf20Sopenharmony_ci 17978c2ecf20Sopenharmony_cistatic void cyberpro_pci_remove(struct pci_dev *dev) 17988c2ecf20Sopenharmony_ci{ 17998c2ecf20Sopenharmony_ci struct cfb_info *cfb = pci_get_drvdata(dev); 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_ci if (cfb) { 18028c2ecf20Sopenharmony_ci cyberpro_common_remove(cfb); 18038c2ecf20Sopenharmony_ci iounmap(cfb->region); 18048c2ecf20Sopenharmony_ci cyberpro_free_fb_info(cfb); 18058c2ecf20Sopenharmony_ci 18068c2ecf20Sopenharmony_ci if (cfb == int_cfb_info) 18078c2ecf20Sopenharmony_ci int_cfb_info = NULL; 18088c2ecf20Sopenharmony_ci 18098c2ecf20Sopenharmony_ci pci_release_regions(dev); 18108c2ecf20Sopenharmony_ci } 18118c2ecf20Sopenharmony_ci} 18128c2ecf20Sopenharmony_ci 18138c2ecf20Sopenharmony_cistatic int __maybe_unused cyberpro_pci_suspend(struct device *dev) 18148c2ecf20Sopenharmony_ci{ 18158c2ecf20Sopenharmony_ci return 0; 18168c2ecf20Sopenharmony_ci} 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ci/* 18198c2ecf20Sopenharmony_ci * Re-initialise the CyberPro hardware 18208c2ecf20Sopenharmony_ci */ 18218c2ecf20Sopenharmony_cistatic int __maybe_unused cyberpro_pci_resume(struct device *dev) 18228c2ecf20Sopenharmony_ci{ 18238c2ecf20Sopenharmony_ci struct cfb_info *cfb = dev_get_drvdata(dev); 18248c2ecf20Sopenharmony_ci 18258c2ecf20Sopenharmony_ci if (cfb) { 18268c2ecf20Sopenharmony_ci cyberpro_pci_enable_mmio(cfb); 18278c2ecf20Sopenharmony_ci cyberpro_common_resume(cfb); 18288c2ecf20Sopenharmony_ci } 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci return 0; 18318c2ecf20Sopenharmony_ci} 18328c2ecf20Sopenharmony_ci 18338c2ecf20Sopenharmony_cistatic struct pci_device_id cyberpro_pci_table[] = { 18348c2ecf20Sopenharmony_ci/* Not yet 18358c2ecf20Sopenharmony_ci * { PCI_VENDOR_ID_INTERG, PCI_DEVICE_ID_INTERG_1682, 18368c2ecf20Sopenharmony_ci * PCI_ANY_ID, PCI_ANY_ID, 0, 0, ID_IGA_1682 }, 18378c2ecf20Sopenharmony_ci */ 18388c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_INTERG, PCI_DEVICE_ID_INTERG_2000, 18398c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, ID_CYBERPRO_2000 }, 18408c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_INTERG, PCI_DEVICE_ID_INTERG_2010, 18418c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, ID_CYBERPRO_2010 }, 18428c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_INTERG, PCI_DEVICE_ID_INTERG_5000, 18438c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, ID_CYBERPRO_5000 }, 18448c2ecf20Sopenharmony_ci { 0, } 18458c2ecf20Sopenharmony_ci}; 18468c2ecf20Sopenharmony_ci 18478c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, cyberpro_pci_table); 18488c2ecf20Sopenharmony_ci 18498c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(cyberpro_pci_pm_ops, 18508c2ecf20Sopenharmony_ci cyberpro_pci_suspend, 18518c2ecf20Sopenharmony_ci cyberpro_pci_resume); 18528c2ecf20Sopenharmony_ci 18538c2ecf20Sopenharmony_cistatic struct pci_driver cyberpro_driver = { 18548c2ecf20Sopenharmony_ci .name = "CyberPro", 18558c2ecf20Sopenharmony_ci .probe = cyberpro_pci_probe, 18568c2ecf20Sopenharmony_ci .remove = cyberpro_pci_remove, 18578c2ecf20Sopenharmony_ci .driver.pm = &cyberpro_pci_pm_ops, 18588c2ecf20Sopenharmony_ci .id_table = cyberpro_pci_table 18598c2ecf20Sopenharmony_ci}; 18608c2ecf20Sopenharmony_ci 18618c2ecf20Sopenharmony_ci/* 18628c2ecf20Sopenharmony_ci * I don't think we can use the "module_init" stuff here because 18638c2ecf20Sopenharmony_ci * the fbcon stuff may not be initialised yet. Hence the #ifdef 18648c2ecf20Sopenharmony_ci * around module_init. 18658c2ecf20Sopenharmony_ci * 18668c2ecf20Sopenharmony_ci * Tony: "module_init" is now required 18678c2ecf20Sopenharmony_ci */ 18688c2ecf20Sopenharmony_cistatic int __init cyber2000fb_init(void) 18698c2ecf20Sopenharmony_ci{ 18708c2ecf20Sopenharmony_ci int ret = -1, err; 18718c2ecf20Sopenharmony_ci 18728c2ecf20Sopenharmony_ci#ifndef MODULE 18738c2ecf20Sopenharmony_ci char *option = NULL; 18748c2ecf20Sopenharmony_ci 18758c2ecf20Sopenharmony_ci if (fb_get_options("cyber2000fb", &option)) 18768c2ecf20Sopenharmony_ci return -ENODEV; 18778c2ecf20Sopenharmony_ci cyber2000fb_setup(option); 18788c2ecf20Sopenharmony_ci#endif 18798c2ecf20Sopenharmony_ci 18808c2ecf20Sopenharmony_ci err = pci_register_driver(&cyberpro_driver); 18818c2ecf20Sopenharmony_ci if (!err) 18828c2ecf20Sopenharmony_ci ret = 0; 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_ci return ret ? err : 0; 18858c2ecf20Sopenharmony_ci} 18868c2ecf20Sopenharmony_cimodule_init(cyber2000fb_init); 18878c2ecf20Sopenharmony_ci 18888c2ecf20Sopenharmony_cistatic void __exit cyberpro_exit(void) 18898c2ecf20Sopenharmony_ci{ 18908c2ecf20Sopenharmony_ci pci_unregister_driver(&cyberpro_driver); 18918c2ecf20Sopenharmony_ci} 18928c2ecf20Sopenharmony_cimodule_exit(cyberpro_exit); 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_ciMODULE_AUTHOR("Russell King"); 18958c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("CyberPro 2000, 2010 and 5000 framebuffer driver"); 18968c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1897