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