162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * drivers/video/chipsfb.c -- frame buffer device for 362306a36Sopenharmony_ci * Chips & Technologies 65550 chip. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 1998-2002 Paul Mackerras 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * This file is derived from the Powermac "chips" driver: 862306a36Sopenharmony_ci * Copyright (C) 1997 Fabio Riccardi. 962306a36Sopenharmony_ci * And from the frame buffer device for Open Firmware-initialized devices: 1062306a36Sopenharmony_ci * Copyright (C) 1997 Geert Uytterhoeven. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 1362306a36Sopenharmony_ci * License. See the file COPYING in the main directory of this archive for 1462306a36Sopenharmony_ci * more details. 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/aperture.h> 1862306a36Sopenharmony_ci#include <linux/module.h> 1962306a36Sopenharmony_ci#include <linux/kernel.h> 2062306a36Sopenharmony_ci#include <linux/errno.h> 2162306a36Sopenharmony_ci#include <linux/string.h> 2262306a36Sopenharmony_ci#include <linux/mm.h> 2362306a36Sopenharmony_ci#include <linux/vmalloc.h> 2462306a36Sopenharmony_ci#include <linux/delay.h> 2562306a36Sopenharmony_ci#include <linux/interrupt.h> 2662306a36Sopenharmony_ci#include <linux/fb.h> 2762306a36Sopenharmony_ci#include <linux/pm.h> 2862306a36Sopenharmony_ci#include <linux/init.h> 2962306a36Sopenharmony_ci#include <linux/pci.h> 3062306a36Sopenharmony_ci#include <linux/console.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#ifdef CONFIG_PMAC_BACKLIGHT 3362306a36Sopenharmony_ci#include <asm/backlight.h> 3462306a36Sopenharmony_ci#endif 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* 3762306a36Sopenharmony_ci * Since we access the display with inb/outb to fixed port numbers, 3862306a36Sopenharmony_ci * we can only handle one 6555x chip. -- paulus 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_ci#define write_ind(num, val, ap, dp) do { \ 4162306a36Sopenharmony_ci outb((num), (ap)); outb((val), (dp)); \ 4262306a36Sopenharmony_ci} while (0) 4362306a36Sopenharmony_ci#define read_ind(num, var, ap, dp) do { \ 4462306a36Sopenharmony_ci outb((num), (ap)); var = inb((dp)); \ 4562306a36Sopenharmony_ci} while (0) 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* extension registers */ 4862306a36Sopenharmony_ci#define write_xr(num, val) write_ind(num, val, 0x3d6, 0x3d7) 4962306a36Sopenharmony_ci#define read_xr(num, var) read_ind(num, var, 0x3d6, 0x3d7) 5062306a36Sopenharmony_ci/* flat panel registers */ 5162306a36Sopenharmony_ci#define write_fr(num, val) write_ind(num, val, 0x3d0, 0x3d1) 5262306a36Sopenharmony_ci#define read_fr(num, var) read_ind(num, var, 0x3d0, 0x3d1) 5362306a36Sopenharmony_ci/* CRTC registers */ 5462306a36Sopenharmony_ci#define write_cr(num, val) write_ind(num, val, 0x3d4, 0x3d5) 5562306a36Sopenharmony_ci#define read_cr(num, var) read_ind(num, var, 0x3d4, 0x3d5) 5662306a36Sopenharmony_ci/* graphics registers */ 5762306a36Sopenharmony_ci#define write_gr(num, val) write_ind(num, val, 0x3ce, 0x3cf) 5862306a36Sopenharmony_ci#define read_gr(num, var) read_ind(num, var, 0x3ce, 0x3cf) 5962306a36Sopenharmony_ci/* sequencer registers */ 6062306a36Sopenharmony_ci#define write_sr(num, val) write_ind(num, val, 0x3c4, 0x3c5) 6162306a36Sopenharmony_ci#define read_sr(num, var) read_ind(num, var, 0x3c4, 0x3c5) 6262306a36Sopenharmony_ci/* attribute registers - slightly strange */ 6362306a36Sopenharmony_ci#define write_ar(num, val) do { \ 6462306a36Sopenharmony_ci inb(0x3da); write_ind(num, val, 0x3c0, 0x3c0); \ 6562306a36Sopenharmony_ci} while (0) 6662306a36Sopenharmony_ci#define read_ar(num, var) do { \ 6762306a36Sopenharmony_ci inb(0x3da); read_ind(num, var, 0x3c0, 0x3c1); \ 6862306a36Sopenharmony_ci} while (0) 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/* 7162306a36Sopenharmony_ci * Exported functions 7262306a36Sopenharmony_ci */ 7362306a36Sopenharmony_ciint chips_init(void); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic int chipsfb_pci_init(struct pci_dev *dp, const struct pci_device_id *); 7662306a36Sopenharmony_cistatic int chipsfb_check_var(struct fb_var_screeninfo *var, 7762306a36Sopenharmony_ci struct fb_info *info); 7862306a36Sopenharmony_cistatic int chipsfb_set_par(struct fb_info *info); 7962306a36Sopenharmony_cistatic int chipsfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, 8062306a36Sopenharmony_ci u_int transp, struct fb_info *info); 8162306a36Sopenharmony_cistatic int chipsfb_blank(int blank, struct fb_info *info); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic const struct fb_ops chipsfb_ops = { 8462306a36Sopenharmony_ci .owner = THIS_MODULE, 8562306a36Sopenharmony_ci FB_DEFAULT_IOMEM_OPS, 8662306a36Sopenharmony_ci .fb_check_var = chipsfb_check_var, 8762306a36Sopenharmony_ci .fb_set_par = chipsfb_set_par, 8862306a36Sopenharmony_ci .fb_setcolreg = chipsfb_setcolreg, 8962306a36Sopenharmony_ci .fb_blank = chipsfb_blank, 9062306a36Sopenharmony_ci}; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic int chipsfb_check_var(struct fb_var_screeninfo *var, 9362306a36Sopenharmony_ci struct fb_info *info) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci if (var->xres > 800 || var->yres > 600 9662306a36Sopenharmony_ci || var->xres_virtual > 800 || var->yres_virtual > 600 9762306a36Sopenharmony_ci || (var->bits_per_pixel != 8 && var->bits_per_pixel != 16) 9862306a36Sopenharmony_ci || var->nonstd 9962306a36Sopenharmony_ci || (var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED) 10062306a36Sopenharmony_ci return -EINVAL; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci var->xres = var->xres_virtual = 800; 10362306a36Sopenharmony_ci var->yres = var->yres_virtual = 600; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci return 0; 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic int chipsfb_set_par(struct fb_info *info) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci if (info->var.bits_per_pixel == 16) { 11162306a36Sopenharmony_ci write_cr(0x13, 200); // Set line length (doublewords) 11262306a36Sopenharmony_ci write_xr(0x81, 0x14); // 15 bit (555) color mode 11362306a36Sopenharmony_ci write_xr(0x82, 0x00); // Disable palettes 11462306a36Sopenharmony_ci write_xr(0x20, 0x10); // 16 bit blitter mode 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci info->fix.line_length = 800*2; 11762306a36Sopenharmony_ci info->fix.visual = FB_VISUAL_TRUECOLOR; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci info->var.red.offset = 10; 12062306a36Sopenharmony_ci info->var.green.offset = 5; 12162306a36Sopenharmony_ci info->var.blue.offset = 0; 12262306a36Sopenharmony_ci info->var.red.length = info->var.green.length = 12362306a36Sopenharmony_ci info->var.blue.length = 5; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci } else { 12662306a36Sopenharmony_ci /* p->var.bits_per_pixel == 8 */ 12762306a36Sopenharmony_ci write_cr(0x13, 100); // Set line length (doublewords) 12862306a36Sopenharmony_ci write_xr(0x81, 0x12); // 8 bit color mode 12962306a36Sopenharmony_ci write_xr(0x82, 0x08); // Graphics gamma enable 13062306a36Sopenharmony_ci write_xr(0x20, 0x00); // 8 bit blitter mode 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci info->fix.line_length = 800; 13362306a36Sopenharmony_ci info->fix.visual = FB_VISUAL_PSEUDOCOLOR; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci info->var.red.offset = info->var.green.offset = 13662306a36Sopenharmony_ci info->var.blue.offset = 0; 13762306a36Sopenharmony_ci info->var.red.length = info->var.green.length = 13862306a36Sopenharmony_ci info->var.blue.length = 8; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci return 0; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic int chipsfb_blank(int blank, struct fb_info *info) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci return 1; /* get fb_blank to set the colormap to all black */ 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic int chipsfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, 15062306a36Sopenharmony_ci u_int transp, struct fb_info *info) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci if (regno > 255) 15362306a36Sopenharmony_ci return 1; 15462306a36Sopenharmony_ci red >>= 8; 15562306a36Sopenharmony_ci green >>= 8; 15662306a36Sopenharmony_ci blue >>= 8; 15762306a36Sopenharmony_ci outb(regno, 0x3c8); 15862306a36Sopenharmony_ci udelay(1); 15962306a36Sopenharmony_ci outb(red, 0x3c9); 16062306a36Sopenharmony_ci outb(green, 0x3c9); 16162306a36Sopenharmony_ci outb(blue, 0x3c9); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci return 0; 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistruct chips_init_reg { 16762306a36Sopenharmony_ci unsigned char addr; 16862306a36Sopenharmony_ci unsigned char data; 16962306a36Sopenharmony_ci}; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic struct chips_init_reg chips_init_sr[] = { 17262306a36Sopenharmony_ci { 0x00, 0x03 }, 17362306a36Sopenharmony_ci { 0x01, 0x01 }, 17462306a36Sopenharmony_ci { 0x02, 0x0f }, 17562306a36Sopenharmony_ci { 0x04, 0x0e } 17662306a36Sopenharmony_ci}; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic struct chips_init_reg chips_init_gr[] = { 17962306a36Sopenharmony_ci { 0x05, 0x00 }, 18062306a36Sopenharmony_ci { 0x06, 0x0d }, 18162306a36Sopenharmony_ci { 0x08, 0xff } 18262306a36Sopenharmony_ci}; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic struct chips_init_reg chips_init_ar[] = { 18562306a36Sopenharmony_ci { 0x10, 0x01 }, 18662306a36Sopenharmony_ci { 0x12, 0x0f }, 18762306a36Sopenharmony_ci { 0x13, 0x00 } 18862306a36Sopenharmony_ci}; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic struct chips_init_reg chips_init_cr[] = { 19162306a36Sopenharmony_ci { 0x00, 0x7f }, 19262306a36Sopenharmony_ci { 0x01, 0x63 }, 19362306a36Sopenharmony_ci { 0x02, 0x63 }, 19462306a36Sopenharmony_ci { 0x03, 0x83 }, 19562306a36Sopenharmony_ci { 0x04, 0x66 }, 19662306a36Sopenharmony_ci { 0x05, 0x10 }, 19762306a36Sopenharmony_ci { 0x06, 0x72 }, 19862306a36Sopenharmony_ci { 0x07, 0x3e }, 19962306a36Sopenharmony_ci { 0x08, 0x00 }, 20062306a36Sopenharmony_ci { 0x09, 0x40 }, 20162306a36Sopenharmony_ci { 0x0c, 0x00 }, 20262306a36Sopenharmony_ci { 0x0d, 0x00 }, 20362306a36Sopenharmony_ci { 0x10, 0x59 }, 20462306a36Sopenharmony_ci { 0x11, 0x0d }, 20562306a36Sopenharmony_ci { 0x12, 0x57 }, 20662306a36Sopenharmony_ci { 0x13, 0x64 }, 20762306a36Sopenharmony_ci { 0x14, 0x00 }, 20862306a36Sopenharmony_ci { 0x15, 0x57 }, 20962306a36Sopenharmony_ci { 0x16, 0x73 }, 21062306a36Sopenharmony_ci { 0x17, 0xe3 }, 21162306a36Sopenharmony_ci { 0x18, 0xff }, 21262306a36Sopenharmony_ci { 0x30, 0x02 }, 21362306a36Sopenharmony_ci { 0x31, 0x02 }, 21462306a36Sopenharmony_ci { 0x32, 0x02 }, 21562306a36Sopenharmony_ci { 0x33, 0x02 }, 21662306a36Sopenharmony_ci { 0x40, 0x00 }, 21762306a36Sopenharmony_ci { 0x41, 0x00 }, 21862306a36Sopenharmony_ci { 0x40, 0x80 } 21962306a36Sopenharmony_ci}; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic struct chips_init_reg chips_init_fr[] = { 22262306a36Sopenharmony_ci { 0x01, 0x02 }, 22362306a36Sopenharmony_ci { 0x03, 0x08 }, 22462306a36Sopenharmony_ci { 0x04, 0x81 }, 22562306a36Sopenharmony_ci { 0x05, 0x21 }, 22662306a36Sopenharmony_ci { 0x08, 0x0c }, 22762306a36Sopenharmony_ci { 0x0a, 0x74 }, 22862306a36Sopenharmony_ci { 0x0b, 0x11 }, 22962306a36Sopenharmony_ci { 0x10, 0x0c }, 23062306a36Sopenharmony_ci { 0x11, 0xe0 }, 23162306a36Sopenharmony_ci /* { 0x12, 0x40 }, -- 3400 needs 40, 2400 needs 48, no way to tell */ 23262306a36Sopenharmony_ci { 0x20, 0x63 }, 23362306a36Sopenharmony_ci { 0x21, 0x68 }, 23462306a36Sopenharmony_ci { 0x22, 0x19 }, 23562306a36Sopenharmony_ci { 0x23, 0x7f }, 23662306a36Sopenharmony_ci { 0x24, 0x68 }, 23762306a36Sopenharmony_ci { 0x26, 0x00 }, 23862306a36Sopenharmony_ci { 0x27, 0x0f }, 23962306a36Sopenharmony_ci { 0x30, 0x57 }, 24062306a36Sopenharmony_ci { 0x31, 0x58 }, 24162306a36Sopenharmony_ci { 0x32, 0x0d }, 24262306a36Sopenharmony_ci { 0x33, 0x72 }, 24362306a36Sopenharmony_ci { 0x34, 0x02 }, 24462306a36Sopenharmony_ci { 0x35, 0x22 }, 24562306a36Sopenharmony_ci { 0x36, 0x02 }, 24662306a36Sopenharmony_ci { 0x37, 0x00 } 24762306a36Sopenharmony_ci}; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic struct chips_init_reg chips_init_xr[] = { 25062306a36Sopenharmony_ci { 0xce, 0x00 }, /* set default memory clock */ 25162306a36Sopenharmony_ci { 0xcc, 0x43 }, /* memory clock ratio */ 25262306a36Sopenharmony_ci { 0xcd, 0x18 }, 25362306a36Sopenharmony_ci { 0xce, 0xa1 }, 25462306a36Sopenharmony_ci { 0xc8, 0x84 }, 25562306a36Sopenharmony_ci { 0xc9, 0x0a }, 25662306a36Sopenharmony_ci { 0xca, 0x00 }, 25762306a36Sopenharmony_ci { 0xcb, 0x20 }, 25862306a36Sopenharmony_ci { 0xcf, 0x06 }, 25962306a36Sopenharmony_ci { 0xd0, 0x0e }, 26062306a36Sopenharmony_ci { 0x09, 0x01 }, 26162306a36Sopenharmony_ci { 0x0a, 0x02 }, 26262306a36Sopenharmony_ci { 0x0b, 0x01 }, 26362306a36Sopenharmony_ci { 0x20, 0x00 }, 26462306a36Sopenharmony_ci { 0x40, 0x03 }, 26562306a36Sopenharmony_ci { 0x41, 0x01 }, 26662306a36Sopenharmony_ci { 0x42, 0x00 }, 26762306a36Sopenharmony_ci { 0x80, 0x82 }, 26862306a36Sopenharmony_ci { 0x81, 0x12 }, 26962306a36Sopenharmony_ci { 0x82, 0x08 }, 27062306a36Sopenharmony_ci { 0xa0, 0x00 }, 27162306a36Sopenharmony_ci { 0xa8, 0x00 } 27262306a36Sopenharmony_ci}; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic void chips_hw_init(void) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci int i; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(chips_init_xr); ++i) 27962306a36Sopenharmony_ci write_xr(chips_init_xr[i].addr, chips_init_xr[i].data); 28062306a36Sopenharmony_ci outb(0x29, 0x3c2); /* set misc output reg */ 28162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(chips_init_sr); ++i) 28262306a36Sopenharmony_ci write_sr(chips_init_sr[i].addr, chips_init_sr[i].data); 28362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(chips_init_gr); ++i) 28462306a36Sopenharmony_ci write_gr(chips_init_gr[i].addr, chips_init_gr[i].data); 28562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(chips_init_ar); ++i) 28662306a36Sopenharmony_ci write_ar(chips_init_ar[i].addr, chips_init_ar[i].data); 28762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(chips_init_cr); ++i) 28862306a36Sopenharmony_ci write_cr(chips_init_cr[i].addr, chips_init_cr[i].data); 28962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(chips_init_fr); ++i) 29062306a36Sopenharmony_ci write_fr(chips_init_fr[i].addr, chips_init_fr[i].data); 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic const struct fb_fix_screeninfo chipsfb_fix = { 29462306a36Sopenharmony_ci .id = "C&T 65550", 29562306a36Sopenharmony_ci .type = FB_TYPE_PACKED_PIXELS, 29662306a36Sopenharmony_ci .visual = FB_VISUAL_PSEUDOCOLOR, 29762306a36Sopenharmony_ci .accel = FB_ACCEL_NONE, 29862306a36Sopenharmony_ci .line_length = 800, 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci// FIXME: Assumes 1MB frame buffer, but 65550 supports 1MB or 2MB. 30162306a36Sopenharmony_ci// * "3500" PowerBook G3 (the original PB G3) has 2MB. 30262306a36Sopenharmony_ci// * 2400 has 1MB composed of 2 Mitsubishi M5M4V4265CTP DRAM chips. 30362306a36Sopenharmony_ci// Motherboard actually supports 2MB -- there are two blank locations 30462306a36Sopenharmony_ci// for a second pair of DRAMs. (Thanks, Apple!) 30562306a36Sopenharmony_ci// * 3400 has 1MB (I think). Don't know if it's expandable. 30662306a36Sopenharmony_ci// -- Tim Seufert 30762306a36Sopenharmony_ci .smem_len = 0x100000, /* 1MB */ 30862306a36Sopenharmony_ci}; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cistatic const struct fb_var_screeninfo chipsfb_var = { 31162306a36Sopenharmony_ci .xres = 800, 31262306a36Sopenharmony_ci .yres = 600, 31362306a36Sopenharmony_ci .xres_virtual = 800, 31462306a36Sopenharmony_ci .yres_virtual = 600, 31562306a36Sopenharmony_ci .bits_per_pixel = 8, 31662306a36Sopenharmony_ci .red = { .length = 8 }, 31762306a36Sopenharmony_ci .green = { .length = 8 }, 31862306a36Sopenharmony_ci .blue = { .length = 8 }, 31962306a36Sopenharmony_ci .height = -1, 32062306a36Sopenharmony_ci .width = -1, 32162306a36Sopenharmony_ci .vmode = FB_VMODE_NONINTERLACED, 32262306a36Sopenharmony_ci .pixclock = 10000, 32362306a36Sopenharmony_ci .left_margin = 16, 32462306a36Sopenharmony_ci .right_margin = 16, 32562306a36Sopenharmony_ci .upper_margin = 16, 32662306a36Sopenharmony_ci .lower_margin = 16, 32762306a36Sopenharmony_ci .hsync_len = 8, 32862306a36Sopenharmony_ci .vsync_len = 8, 32962306a36Sopenharmony_ci}; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_cistatic void init_chips(struct fb_info *p, unsigned long addr) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci fb_memset_io(p->screen_base, 0, 0x100000); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci p->fix = chipsfb_fix; 33662306a36Sopenharmony_ci p->fix.smem_start = addr; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci p->var = chipsfb_var; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci p->fbops = &chipsfb_ops; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci fb_alloc_cmap(&p->cmap, 256, 0); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci chips_hw_init(); 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistatic int chipsfb_pci_init(struct pci_dev *dp, const struct pci_device_id *ent) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci struct fb_info *p; 35062306a36Sopenharmony_ci unsigned long addr; 35162306a36Sopenharmony_ci unsigned short cmd; 35262306a36Sopenharmony_ci int rc; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci rc = aperture_remove_conflicting_pci_devices(dp, "chipsfb"); 35562306a36Sopenharmony_ci if (rc) 35662306a36Sopenharmony_ci return rc; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci rc = pci_enable_device(dp); 35962306a36Sopenharmony_ci if (rc < 0) { 36062306a36Sopenharmony_ci dev_err(&dp->dev, "Cannot enable PCI device\n"); 36162306a36Sopenharmony_ci goto err_out; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci if ((dp->resource[0].flags & IORESOURCE_MEM) == 0) { 36562306a36Sopenharmony_ci rc = -ENODEV; 36662306a36Sopenharmony_ci goto err_disable; 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci addr = pci_resource_start(dp, 0); 36962306a36Sopenharmony_ci if (addr == 0) { 37062306a36Sopenharmony_ci rc = -ENODEV; 37162306a36Sopenharmony_ci goto err_disable; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci p = framebuffer_alloc(0, &dp->dev); 37562306a36Sopenharmony_ci if (p == NULL) { 37662306a36Sopenharmony_ci rc = -ENOMEM; 37762306a36Sopenharmony_ci goto err_disable; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci if (pci_request_region(dp, 0, "chipsfb") != 0) { 38162306a36Sopenharmony_ci dev_err(&dp->dev, "Cannot request framebuffer\n"); 38262306a36Sopenharmony_ci rc = -EBUSY; 38362306a36Sopenharmony_ci goto err_release_fb; 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci#ifdef __BIG_ENDIAN 38762306a36Sopenharmony_ci addr += 0x800000; // Use big-endian aperture 38862306a36Sopenharmony_ci#endif 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci /* we should use pci_enable_device here, but, 39162306a36Sopenharmony_ci the device doesn't declare its I/O ports in its BARs 39262306a36Sopenharmony_ci so pci_enable_device won't turn on I/O responses */ 39362306a36Sopenharmony_ci pci_read_config_word(dp, PCI_COMMAND, &cmd); 39462306a36Sopenharmony_ci cmd |= 3; /* enable memory and IO space */ 39562306a36Sopenharmony_ci pci_write_config_word(dp, PCI_COMMAND, cmd); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci#ifdef CONFIG_PMAC_BACKLIGHT 39862306a36Sopenharmony_ci /* turn on the backlight */ 39962306a36Sopenharmony_ci mutex_lock(&pmac_backlight_mutex); 40062306a36Sopenharmony_ci if (pmac_backlight) { 40162306a36Sopenharmony_ci pmac_backlight->props.power = FB_BLANK_UNBLANK; 40262306a36Sopenharmony_ci backlight_update_status(pmac_backlight); 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci mutex_unlock(&pmac_backlight_mutex); 40562306a36Sopenharmony_ci#endif /* CONFIG_PMAC_BACKLIGHT */ 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci#ifdef CONFIG_PPC 40862306a36Sopenharmony_ci p->screen_base = ioremap_wc(addr, 0x200000); 40962306a36Sopenharmony_ci#else 41062306a36Sopenharmony_ci p->screen_base = ioremap(addr, 0x200000); 41162306a36Sopenharmony_ci#endif 41262306a36Sopenharmony_ci if (p->screen_base == NULL) { 41362306a36Sopenharmony_ci dev_err(&dp->dev, "Cannot map framebuffer\n"); 41462306a36Sopenharmony_ci rc = -ENOMEM; 41562306a36Sopenharmony_ci goto err_release_pci; 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci pci_set_drvdata(dp, p); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci init_chips(p, addr); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci rc = register_framebuffer(p); 42362306a36Sopenharmony_ci if (rc < 0) { 42462306a36Sopenharmony_ci dev_err(&dp->dev,"C&T 65550 framebuffer failed to register\n"); 42562306a36Sopenharmony_ci goto err_unmap; 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci dev_info(&dp->dev,"fb%d: Chips 65550 frame buffer" 42962306a36Sopenharmony_ci " (%dK RAM detected)\n", 43062306a36Sopenharmony_ci p->node, p->fix.smem_len / 1024); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci return 0; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci err_unmap: 43562306a36Sopenharmony_ci iounmap(p->screen_base); 43662306a36Sopenharmony_ci err_release_pci: 43762306a36Sopenharmony_ci pci_release_region(dp, 0); 43862306a36Sopenharmony_ci err_release_fb: 43962306a36Sopenharmony_ci framebuffer_release(p); 44062306a36Sopenharmony_ci err_disable: 44162306a36Sopenharmony_ci pci_disable_device(dp); 44262306a36Sopenharmony_ci err_out: 44362306a36Sopenharmony_ci return rc; 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cistatic void chipsfb_remove(struct pci_dev *dp) 44762306a36Sopenharmony_ci{ 44862306a36Sopenharmony_ci struct fb_info *p = pci_get_drvdata(dp); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci if (p->screen_base == NULL) 45162306a36Sopenharmony_ci return; 45262306a36Sopenharmony_ci unregister_framebuffer(p); 45362306a36Sopenharmony_ci iounmap(p->screen_base); 45462306a36Sopenharmony_ci p->screen_base = NULL; 45562306a36Sopenharmony_ci pci_release_region(dp, 0); 45662306a36Sopenharmony_ci} 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci#ifdef CONFIG_PM 45962306a36Sopenharmony_cistatic int chipsfb_pci_suspend(struct pci_dev *pdev, pm_message_t state) 46062306a36Sopenharmony_ci{ 46162306a36Sopenharmony_ci struct fb_info *p = pci_get_drvdata(pdev); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci if (state.event == pdev->dev.power.power_state.event) 46462306a36Sopenharmony_ci return 0; 46562306a36Sopenharmony_ci if (!(state.event & PM_EVENT_SLEEP)) 46662306a36Sopenharmony_ci goto done; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci console_lock(); 46962306a36Sopenharmony_ci chipsfb_blank(1, p); 47062306a36Sopenharmony_ci fb_set_suspend(p, 1); 47162306a36Sopenharmony_ci console_unlock(); 47262306a36Sopenharmony_ci done: 47362306a36Sopenharmony_ci pdev->dev.power.power_state = state; 47462306a36Sopenharmony_ci return 0; 47562306a36Sopenharmony_ci} 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_cistatic int chipsfb_pci_resume(struct pci_dev *pdev) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci struct fb_info *p = pci_get_drvdata(pdev); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci console_lock(); 48262306a36Sopenharmony_ci fb_set_suspend(p, 0); 48362306a36Sopenharmony_ci chipsfb_blank(0, p); 48462306a36Sopenharmony_ci console_unlock(); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci pdev->dev.power.power_state = PMSG_ON; 48762306a36Sopenharmony_ci return 0; 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ci#endif /* CONFIG_PM */ 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_cistatic struct pci_device_id chipsfb_pci_tbl[] = { 49362306a36Sopenharmony_ci { PCI_VENDOR_ID_CT, PCI_DEVICE_ID_CT_65550, PCI_ANY_ID, PCI_ANY_ID }, 49462306a36Sopenharmony_ci { 0 } 49562306a36Sopenharmony_ci}; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, chipsfb_pci_tbl); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_cistatic struct pci_driver chipsfb_driver = { 50062306a36Sopenharmony_ci .name = "chipsfb", 50162306a36Sopenharmony_ci .id_table = chipsfb_pci_tbl, 50262306a36Sopenharmony_ci .probe = chipsfb_pci_init, 50362306a36Sopenharmony_ci .remove = chipsfb_remove, 50462306a36Sopenharmony_ci#ifdef CONFIG_PM 50562306a36Sopenharmony_ci .suspend = chipsfb_pci_suspend, 50662306a36Sopenharmony_ci .resume = chipsfb_pci_resume, 50762306a36Sopenharmony_ci#endif 50862306a36Sopenharmony_ci}; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ciint __init chips_init(void) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci if (fb_modesetting_disabled("chipsfb")) 51362306a36Sopenharmony_ci return -ENODEV; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci if (fb_get_options("chipsfb", NULL)) 51662306a36Sopenharmony_ci return -ENODEV; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci return pci_register_driver(&chipsfb_driver); 51962306a36Sopenharmony_ci} 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_cimodule_init(chips_init); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_cistatic void __exit chipsfb_exit(void) 52462306a36Sopenharmony_ci{ 52562306a36Sopenharmony_ci pci_unregister_driver(&chipsfb_driver); 52662306a36Sopenharmony_ci} 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 529