18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci *  drivers/video/chipsfb.c -- frame buffer device for
38c2ecf20Sopenharmony_ci *  Chips & Technologies 65550 chip.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 1998-2002 Paul Mackerras
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *  This file is derived from the Powermac "chips" driver:
88c2ecf20Sopenharmony_ci *  Copyright (C) 1997 Fabio Riccardi.
98c2ecf20Sopenharmony_ci *  And from the frame buffer device for Open Firmware-initialized devices:
108c2ecf20Sopenharmony_ci *  Copyright (C) 1997 Geert Uytterhoeven.
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci *  This file is subject to the terms and conditions of the GNU General Public
138c2ecf20Sopenharmony_ci *  License. See the file COPYING in the main directory of this archive for
148c2ecf20Sopenharmony_ci *  more details.
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <linux/module.h>
188c2ecf20Sopenharmony_ci#include <linux/kernel.h>
198c2ecf20Sopenharmony_ci#include <linux/errno.h>
208c2ecf20Sopenharmony_ci#include <linux/string.h>
218c2ecf20Sopenharmony_ci#include <linux/mm.h>
228c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
238c2ecf20Sopenharmony_ci#include <linux/delay.h>
248c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
258c2ecf20Sopenharmony_ci#include <linux/fb.h>
268c2ecf20Sopenharmony_ci#include <linux/pm.h>
278c2ecf20Sopenharmony_ci#include <linux/init.h>
288c2ecf20Sopenharmony_ci#include <linux/pci.h>
298c2ecf20Sopenharmony_ci#include <linux/console.h>
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#ifdef CONFIG_PMAC_BACKLIGHT
328c2ecf20Sopenharmony_ci#include <asm/backlight.h>
338c2ecf20Sopenharmony_ci#endif
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/*
368c2ecf20Sopenharmony_ci * Since we access the display with inb/outb to fixed port numbers,
378c2ecf20Sopenharmony_ci * we can only handle one 6555x chip.  -- paulus
388c2ecf20Sopenharmony_ci */
398c2ecf20Sopenharmony_ci#define write_ind(num, val, ap, dp)	do { \
408c2ecf20Sopenharmony_ci	outb((num), (ap)); outb((val), (dp)); \
418c2ecf20Sopenharmony_ci} while (0)
428c2ecf20Sopenharmony_ci#define read_ind(num, var, ap, dp)	do { \
438c2ecf20Sopenharmony_ci	outb((num), (ap)); var = inb((dp)); \
448c2ecf20Sopenharmony_ci} while (0)
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci/* extension registers */
478c2ecf20Sopenharmony_ci#define write_xr(num, val)	write_ind(num, val, 0x3d6, 0x3d7)
488c2ecf20Sopenharmony_ci#define read_xr(num, var)	read_ind(num, var, 0x3d6, 0x3d7)
498c2ecf20Sopenharmony_ci/* flat panel registers */
508c2ecf20Sopenharmony_ci#define write_fr(num, val)	write_ind(num, val, 0x3d0, 0x3d1)
518c2ecf20Sopenharmony_ci#define read_fr(num, var)	read_ind(num, var, 0x3d0, 0x3d1)
528c2ecf20Sopenharmony_ci/* CRTC registers */
538c2ecf20Sopenharmony_ci#define write_cr(num, val)	write_ind(num, val, 0x3d4, 0x3d5)
548c2ecf20Sopenharmony_ci#define read_cr(num, var)	read_ind(num, var, 0x3d4, 0x3d5)
558c2ecf20Sopenharmony_ci/* graphics registers */
568c2ecf20Sopenharmony_ci#define write_gr(num, val)	write_ind(num, val, 0x3ce, 0x3cf)
578c2ecf20Sopenharmony_ci#define read_gr(num, var)	read_ind(num, var, 0x3ce, 0x3cf)
588c2ecf20Sopenharmony_ci/* sequencer registers */
598c2ecf20Sopenharmony_ci#define write_sr(num, val)	write_ind(num, val, 0x3c4, 0x3c5)
608c2ecf20Sopenharmony_ci#define read_sr(num, var)	read_ind(num, var, 0x3c4, 0x3c5)
618c2ecf20Sopenharmony_ci/* attribute registers - slightly strange */
628c2ecf20Sopenharmony_ci#define write_ar(num, val)	do { \
638c2ecf20Sopenharmony_ci	inb(0x3da); write_ind(num, val, 0x3c0, 0x3c0); \
648c2ecf20Sopenharmony_ci} while (0)
658c2ecf20Sopenharmony_ci#define read_ar(num, var)	do { \
668c2ecf20Sopenharmony_ci	inb(0x3da); read_ind(num, var, 0x3c0, 0x3c1); \
678c2ecf20Sopenharmony_ci} while (0)
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci/*
708c2ecf20Sopenharmony_ci * Exported functions
718c2ecf20Sopenharmony_ci */
728c2ecf20Sopenharmony_ciint chips_init(void);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic int chipsfb_pci_init(struct pci_dev *dp, const struct pci_device_id *);
758c2ecf20Sopenharmony_cistatic int chipsfb_check_var(struct fb_var_screeninfo *var,
768c2ecf20Sopenharmony_ci			     struct fb_info *info);
778c2ecf20Sopenharmony_cistatic int chipsfb_set_par(struct fb_info *info);
788c2ecf20Sopenharmony_cistatic int chipsfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
798c2ecf20Sopenharmony_ci			     u_int transp, struct fb_info *info);
808c2ecf20Sopenharmony_cistatic int chipsfb_blank(int blank, struct fb_info *info);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic const struct fb_ops chipsfb_ops = {
838c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
848c2ecf20Sopenharmony_ci	.fb_check_var	= chipsfb_check_var,
858c2ecf20Sopenharmony_ci	.fb_set_par	= chipsfb_set_par,
868c2ecf20Sopenharmony_ci	.fb_setcolreg	= chipsfb_setcolreg,
878c2ecf20Sopenharmony_ci	.fb_blank	= chipsfb_blank,
888c2ecf20Sopenharmony_ci	.fb_fillrect	= cfb_fillrect,
898c2ecf20Sopenharmony_ci	.fb_copyarea	= cfb_copyarea,
908c2ecf20Sopenharmony_ci	.fb_imageblit	= cfb_imageblit,
918c2ecf20Sopenharmony_ci};
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic int chipsfb_check_var(struct fb_var_screeninfo *var,
948c2ecf20Sopenharmony_ci			     struct fb_info *info)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	if (var->xres > 800 || var->yres > 600
978c2ecf20Sopenharmony_ci	    || var->xres_virtual > 800 || var->yres_virtual > 600
988c2ecf20Sopenharmony_ci	    || (var->bits_per_pixel != 8 && var->bits_per_pixel != 16)
998c2ecf20Sopenharmony_ci	    || var->nonstd
1008c2ecf20Sopenharmony_ci	    || (var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED)
1018c2ecf20Sopenharmony_ci		return -EINVAL;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	var->xres = var->xres_virtual = 800;
1048c2ecf20Sopenharmony_ci	var->yres = var->yres_virtual = 600;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	return 0;
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic int chipsfb_set_par(struct fb_info *info)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	if (info->var.bits_per_pixel == 16) {
1128c2ecf20Sopenharmony_ci		write_cr(0x13, 200);		// Set line length (doublewords)
1138c2ecf20Sopenharmony_ci		write_xr(0x81, 0x14);		// 15 bit (555) color mode
1148c2ecf20Sopenharmony_ci		write_xr(0x82, 0x00);		// Disable palettes
1158c2ecf20Sopenharmony_ci		write_xr(0x20, 0x10);		// 16 bit blitter mode
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci		info->fix.line_length = 800*2;
1188c2ecf20Sopenharmony_ci		info->fix.visual = FB_VISUAL_TRUECOLOR;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci		info->var.red.offset = 10;
1218c2ecf20Sopenharmony_ci		info->var.green.offset = 5;
1228c2ecf20Sopenharmony_ci		info->var.blue.offset = 0;
1238c2ecf20Sopenharmony_ci		info->var.red.length = info->var.green.length =
1248c2ecf20Sopenharmony_ci			info->var.blue.length = 5;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	} else {
1278c2ecf20Sopenharmony_ci		/* p->var.bits_per_pixel == 8 */
1288c2ecf20Sopenharmony_ci		write_cr(0x13, 100);		// Set line length (doublewords)
1298c2ecf20Sopenharmony_ci		write_xr(0x81, 0x12);		// 8 bit color mode
1308c2ecf20Sopenharmony_ci		write_xr(0x82, 0x08);		// Graphics gamma enable
1318c2ecf20Sopenharmony_ci		write_xr(0x20, 0x00);		// 8 bit blitter mode
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci		info->fix.line_length = 800;
1348c2ecf20Sopenharmony_ci		info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci 		info->var.red.offset = info->var.green.offset =
1378c2ecf20Sopenharmony_ci			info->var.blue.offset = 0;
1388c2ecf20Sopenharmony_ci		info->var.red.length = info->var.green.length =
1398c2ecf20Sopenharmony_ci			info->var.blue.length = 8;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci	return 0;
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic int chipsfb_blank(int blank, struct fb_info *info)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	return 1;	/* get fb_blank to set the colormap to all black */
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic int chipsfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
1518c2ecf20Sopenharmony_ci			     u_int transp, struct fb_info *info)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	if (regno > 255)
1548c2ecf20Sopenharmony_ci		return 1;
1558c2ecf20Sopenharmony_ci	red >>= 8;
1568c2ecf20Sopenharmony_ci	green >>= 8;
1578c2ecf20Sopenharmony_ci	blue >>= 8;
1588c2ecf20Sopenharmony_ci	outb(regno, 0x3c8);
1598c2ecf20Sopenharmony_ci	udelay(1);
1608c2ecf20Sopenharmony_ci	outb(red, 0x3c9);
1618c2ecf20Sopenharmony_ci	outb(green, 0x3c9);
1628c2ecf20Sopenharmony_ci	outb(blue, 0x3c9);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	return 0;
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistruct chips_init_reg {
1688c2ecf20Sopenharmony_ci	unsigned char addr;
1698c2ecf20Sopenharmony_ci	unsigned char data;
1708c2ecf20Sopenharmony_ci};
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cistatic struct chips_init_reg chips_init_sr[] = {
1738c2ecf20Sopenharmony_ci	{ 0x00, 0x03 },
1748c2ecf20Sopenharmony_ci	{ 0x01, 0x01 },
1758c2ecf20Sopenharmony_ci	{ 0x02, 0x0f },
1768c2ecf20Sopenharmony_ci	{ 0x04, 0x0e }
1778c2ecf20Sopenharmony_ci};
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistatic struct chips_init_reg chips_init_gr[] = {
1808c2ecf20Sopenharmony_ci	{ 0x05, 0x00 },
1818c2ecf20Sopenharmony_ci	{ 0x06, 0x0d },
1828c2ecf20Sopenharmony_ci	{ 0x08, 0xff }
1838c2ecf20Sopenharmony_ci};
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic struct chips_init_reg chips_init_ar[] = {
1868c2ecf20Sopenharmony_ci	{ 0x10, 0x01 },
1878c2ecf20Sopenharmony_ci	{ 0x12, 0x0f },
1888c2ecf20Sopenharmony_ci	{ 0x13, 0x00 }
1898c2ecf20Sopenharmony_ci};
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cistatic struct chips_init_reg chips_init_cr[] = {
1928c2ecf20Sopenharmony_ci	{ 0x00, 0x7f },
1938c2ecf20Sopenharmony_ci	{ 0x01, 0x63 },
1948c2ecf20Sopenharmony_ci	{ 0x02, 0x63 },
1958c2ecf20Sopenharmony_ci	{ 0x03, 0x83 },
1968c2ecf20Sopenharmony_ci	{ 0x04, 0x66 },
1978c2ecf20Sopenharmony_ci	{ 0x05, 0x10 },
1988c2ecf20Sopenharmony_ci	{ 0x06, 0x72 },
1998c2ecf20Sopenharmony_ci	{ 0x07, 0x3e },
2008c2ecf20Sopenharmony_ci	{ 0x08, 0x00 },
2018c2ecf20Sopenharmony_ci	{ 0x09, 0x40 },
2028c2ecf20Sopenharmony_ci	{ 0x0c, 0x00 },
2038c2ecf20Sopenharmony_ci	{ 0x0d, 0x00 },
2048c2ecf20Sopenharmony_ci	{ 0x10, 0x59 },
2058c2ecf20Sopenharmony_ci	{ 0x11, 0x0d },
2068c2ecf20Sopenharmony_ci	{ 0x12, 0x57 },
2078c2ecf20Sopenharmony_ci	{ 0x13, 0x64 },
2088c2ecf20Sopenharmony_ci	{ 0x14, 0x00 },
2098c2ecf20Sopenharmony_ci	{ 0x15, 0x57 },
2108c2ecf20Sopenharmony_ci	{ 0x16, 0x73 },
2118c2ecf20Sopenharmony_ci	{ 0x17, 0xe3 },
2128c2ecf20Sopenharmony_ci	{ 0x18, 0xff },
2138c2ecf20Sopenharmony_ci	{ 0x30, 0x02 },
2148c2ecf20Sopenharmony_ci	{ 0x31, 0x02 },
2158c2ecf20Sopenharmony_ci	{ 0x32, 0x02 },
2168c2ecf20Sopenharmony_ci	{ 0x33, 0x02 },
2178c2ecf20Sopenharmony_ci	{ 0x40, 0x00 },
2188c2ecf20Sopenharmony_ci	{ 0x41, 0x00 },
2198c2ecf20Sopenharmony_ci	{ 0x40, 0x80 }
2208c2ecf20Sopenharmony_ci};
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_cistatic struct chips_init_reg chips_init_fr[] = {
2238c2ecf20Sopenharmony_ci	{ 0x01, 0x02 },
2248c2ecf20Sopenharmony_ci	{ 0x03, 0x08 },
2258c2ecf20Sopenharmony_ci	{ 0x04, 0x81 },
2268c2ecf20Sopenharmony_ci	{ 0x05, 0x21 },
2278c2ecf20Sopenharmony_ci	{ 0x08, 0x0c },
2288c2ecf20Sopenharmony_ci	{ 0x0a, 0x74 },
2298c2ecf20Sopenharmony_ci	{ 0x0b, 0x11 },
2308c2ecf20Sopenharmony_ci	{ 0x10, 0x0c },
2318c2ecf20Sopenharmony_ci	{ 0x11, 0xe0 },
2328c2ecf20Sopenharmony_ci	/* { 0x12, 0x40 }, -- 3400 needs 40, 2400 needs 48, no way to tell */
2338c2ecf20Sopenharmony_ci	{ 0x20, 0x63 },
2348c2ecf20Sopenharmony_ci	{ 0x21, 0x68 },
2358c2ecf20Sopenharmony_ci	{ 0x22, 0x19 },
2368c2ecf20Sopenharmony_ci	{ 0x23, 0x7f },
2378c2ecf20Sopenharmony_ci	{ 0x24, 0x68 },
2388c2ecf20Sopenharmony_ci	{ 0x26, 0x00 },
2398c2ecf20Sopenharmony_ci	{ 0x27, 0x0f },
2408c2ecf20Sopenharmony_ci	{ 0x30, 0x57 },
2418c2ecf20Sopenharmony_ci	{ 0x31, 0x58 },
2428c2ecf20Sopenharmony_ci	{ 0x32, 0x0d },
2438c2ecf20Sopenharmony_ci	{ 0x33, 0x72 },
2448c2ecf20Sopenharmony_ci	{ 0x34, 0x02 },
2458c2ecf20Sopenharmony_ci	{ 0x35, 0x22 },
2468c2ecf20Sopenharmony_ci	{ 0x36, 0x02 },
2478c2ecf20Sopenharmony_ci	{ 0x37, 0x00 }
2488c2ecf20Sopenharmony_ci};
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_cistatic struct chips_init_reg chips_init_xr[] = {
2518c2ecf20Sopenharmony_ci	{ 0xce, 0x00 },		/* set default memory clock */
2528c2ecf20Sopenharmony_ci	{ 0xcc, 0x43 },		/* memory clock ratio */
2538c2ecf20Sopenharmony_ci	{ 0xcd, 0x18 },
2548c2ecf20Sopenharmony_ci	{ 0xce, 0xa1 },
2558c2ecf20Sopenharmony_ci	{ 0xc8, 0x84 },
2568c2ecf20Sopenharmony_ci	{ 0xc9, 0x0a },
2578c2ecf20Sopenharmony_ci	{ 0xca, 0x00 },
2588c2ecf20Sopenharmony_ci	{ 0xcb, 0x20 },
2598c2ecf20Sopenharmony_ci	{ 0xcf, 0x06 },
2608c2ecf20Sopenharmony_ci	{ 0xd0, 0x0e },
2618c2ecf20Sopenharmony_ci	{ 0x09, 0x01 },
2628c2ecf20Sopenharmony_ci	{ 0x0a, 0x02 },
2638c2ecf20Sopenharmony_ci	{ 0x0b, 0x01 },
2648c2ecf20Sopenharmony_ci	{ 0x20, 0x00 },
2658c2ecf20Sopenharmony_ci	{ 0x40, 0x03 },
2668c2ecf20Sopenharmony_ci	{ 0x41, 0x01 },
2678c2ecf20Sopenharmony_ci	{ 0x42, 0x00 },
2688c2ecf20Sopenharmony_ci	{ 0x80, 0x82 },
2698c2ecf20Sopenharmony_ci	{ 0x81, 0x12 },
2708c2ecf20Sopenharmony_ci	{ 0x82, 0x08 },
2718c2ecf20Sopenharmony_ci	{ 0xa0, 0x00 },
2728c2ecf20Sopenharmony_ci	{ 0xa8, 0x00 }
2738c2ecf20Sopenharmony_ci};
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_cistatic void chips_hw_init(void)
2768c2ecf20Sopenharmony_ci{
2778c2ecf20Sopenharmony_ci	int i;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(chips_init_xr); ++i)
2808c2ecf20Sopenharmony_ci		write_xr(chips_init_xr[i].addr, chips_init_xr[i].data);
2818c2ecf20Sopenharmony_ci	outb(0x29, 0x3c2); /* set misc output reg */
2828c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(chips_init_sr); ++i)
2838c2ecf20Sopenharmony_ci		write_sr(chips_init_sr[i].addr, chips_init_sr[i].data);
2848c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(chips_init_gr); ++i)
2858c2ecf20Sopenharmony_ci		write_gr(chips_init_gr[i].addr, chips_init_gr[i].data);
2868c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(chips_init_ar); ++i)
2878c2ecf20Sopenharmony_ci		write_ar(chips_init_ar[i].addr, chips_init_ar[i].data);
2888c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(chips_init_cr); ++i)
2898c2ecf20Sopenharmony_ci		write_cr(chips_init_cr[i].addr, chips_init_cr[i].data);
2908c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(chips_init_fr); ++i)
2918c2ecf20Sopenharmony_ci		write_fr(chips_init_fr[i].addr, chips_init_fr[i].data);
2928c2ecf20Sopenharmony_ci}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_cistatic const struct fb_fix_screeninfo chipsfb_fix = {
2958c2ecf20Sopenharmony_ci	.id =		"C&T 65550",
2968c2ecf20Sopenharmony_ci	.type =		FB_TYPE_PACKED_PIXELS,
2978c2ecf20Sopenharmony_ci	.visual =	FB_VISUAL_PSEUDOCOLOR,
2988c2ecf20Sopenharmony_ci	.accel =	FB_ACCEL_NONE,
2998c2ecf20Sopenharmony_ci	.line_length =	800,
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci// FIXME: Assumes 1MB frame buffer, but 65550 supports 1MB or 2MB.
3028c2ecf20Sopenharmony_ci// * "3500" PowerBook G3 (the original PB G3) has 2MB.
3038c2ecf20Sopenharmony_ci// * 2400 has 1MB composed of 2 Mitsubishi M5M4V4265CTP DRAM chips.
3048c2ecf20Sopenharmony_ci//   Motherboard actually supports 2MB -- there are two blank locations
3058c2ecf20Sopenharmony_ci//   for a second pair of DRAMs.  (Thanks, Apple!)
3068c2ecf20Sopenharmony_ci// * 3400 has 1MB (I think).  Don't know if it's expandable.
3078c2ecf20Sopenharmony_ci// -- Tim Seufert
3088c2ecf20Sopenharmony_ci	.smem_len =	0x100000,	/* 1MB */
3098c2ecf20Sopenharmony_ci};
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_cistatic const struct fb_var_screeninfo chipsfb_var = {
3128c2ecf20Sopenharmony_ci	.xres = 800,
3138c2ecf20Sopenharmony_ci	.yres = 600,
3148c2ecf20Sopenharmony_ci	.xres_virtual = 800,
3158c2ecf20Sopenharmony_ci	.yres_virtual = 600,
3168c2ecf20Sopenharmony_ci	.bits_per_pixel = 8,
3178c2ecf20Sopenharmony_ci	.red = { .length = 8 },
3188c2ecf20Sopenharmony_ci	.green = { .length = 8 },
3198c2ecf20Sopenharmony_ci	.blue = { .length = 8 },
3208c2ecf20Sopenharmony_ci	.height = -1,
3218c2ecf20Sopenharmony_ci	.width = -1,
3228c2ecf20Sopenharmony_ci	.vmode = FB_VMODE_NONINTERLACED,
3238c2ecf20Sopenharmony_ci	.pixclock = 10000,
3248c2ecf20Sopenharmony_ci	.left_margin = 16,
3258c2ecf20Sopenharmony_ci	.right_margin = 16,
3268c2ecf20Sopenharmony_ci	.upper_margin = 16,
3278c2ecf20Sopenharmony_ci	.lower_margin = 16,
3288c2ecf20Sopenharmony_ci	.hsync_len = 8,
3298c2ecf20Sopenharmony_ci	.vsync_len = 8,
3308c2ecf20Sopenharmony_ci};
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_cistatic void init_chips(struct fb_info *p, unsigned long addr)
3338c2ecf20Sopenharmony_ci{
3348c2ecf20Sopenharmony_ci	fb_memset(p->screen_base, 0, 0x100000);
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	p->fix = chipsfb_fix;
3378c2ecf20Sopenharmony_ci	p->fix.smem_start = addr;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	p->var = chipsfb_var;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	p->fbops = &chipsfb_ops;
3428c2ecf20Sopenharmony_ci	p->flags = FBINFO_DEFAULT;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	fb_alloc_cmap(&p->cmap, 256, 0);
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	chips_hw_init();
3478c2ecf20Sopenharmony_ci}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_cistatic int chipsfb_pci_init(struct pci_dev *dp, const struct pci_device_id *ent)
3508c2ecf20Sopenharmony_ci{
3518c2ecf20Sopenharmony_ci	struct fb_info *p;
3528c2ecf20Sopenharmony_ci	unsigned long addr;
3538c2ecf20Sopenharmony_ci	unsigned short cmd;
3548c2ecf20Sopenharmony_ci	int rc = -ENODEV;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	if (pci_enable_device(dp) < 0) {
3578c2ecf20Sopenharmony_ci		dev_err(&dp->dev, "Cannot enable PCI device\n");
3588c2ecf20Sopenharmony_ci		goto err_out;
3598c2ecf20Sopenharmony_ci	}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	if ((dp->resource[0].flags & IORESOURCE_MEM) == 0)
3628c2ecf20Sopenharmony_ci		goto err_disable;
3638c2ecf20Sopenharmony_ci	addr = pci_resource_start(dp, 0);
3648c2ecf20Sopenharmony_ci	if (addr == 0)
3658c2ecf20Sopenharmony_ci		goto err_disable;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	p = framebuffer_alloc(0, &dp->dev);
3688c2ecf20Sopenharmony_ci	if (p == NULL) {
3698c2ecf20Sopenharmony_ci		rc = -ENOMEM;
3708c2ecf20Sopenharmony_ci		goto err_disable;
3718c2ecf20Sopenharmony_ci	}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	if (pci_request_region(dp, 0, "chipsfb") != 0) {
3748c2ecf20Sopenharmony_ci		dev_err(&dp->dev, "Cannot request framebuffer\n");
3758c2ecf20Sopenharmony_ci		rc = -EBUSY;
3768c2ecf20Sopenharmony_ci		goto err_release_fb;
3778c2ecf20Sopenharmony_ci	}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci#ifdef __BIG_ENDIAN
3808c2ecf20Sopenharmony_ci	addr += 0x800000;	// Use big-endian aperture
3818c2ecf20Sopenharmony_ci#endif
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	/* we should use pci_enable_device here, but,
3848c2ecf20Sopenharmony_ci	   the device doesn't declare its I/O ports in its BARs
3858c2ecf20Sopenharmony_ci	   so pci_enable_device won't turn on I/O responses */
3868c2ecf20Sopenharmony_ci	pci_read_config_word(dp, PCI_COMMAND, &cmd);
3878c2ecf20Sopenharmony_ci	cmd |= 3;	/* enable memory and IO space */
3888c2ecf20Sopenharmony_ci	pci_write_config_word(dp, PCI_COMMAND, cmd);
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci#ifdef CONFIG_PMAC_BACKLIGHT
3918c2ecf20Sopenharmony_ci	/* turn on the backlight */
3928c2ecf20Sopenharmony_ci	mutex_lock(&pmac_backlight_mutex);
3938c2ecf20Sopenharmony_ci	if (pmac_backlight) {
3948c2ecf20Sopenharmony_ci		pmac_backlight->props.power = FB_BLANK_UNBLANK;
3958c2ecf20Sopenharmony_ci		backlight_update_status(pmac_backlight);
3968c2ecf20Sopenharmony_ci	}
3978c2ecf20Sopenharmony_ci	mutex_unlock(&pmac_backlight_mutex);
3988c2ecf20Sopenharmony_ci#endif /* CONFIG_PMAC_BACKLIGHT */
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC
4018c2ecf20Sopenharmony_ci	p->screen_base = ioremap_wc(addr, 0x200000);
4028c2ecf20Sopenharmony_ci#else
4038c2ecf20Sopenharmony_ci	p->screen_base = ioremap(addr, 0x200000);
4048c2ecf20Sopenharmony_ci#endif
4058c2ecf20Sopenharmony_ci	if (p->screen_base == NULL) {
4068c2ecf20Sopenharmony_ci		dev_err(&dp->dev, "Cannot map framebuffer\n");
4078c2ecf20Sopenharmony_ci		rc = -ENOMEM;
4088c2ecf20Sopenharmony_ci		goto err_release_pci;
4098c2ecf20Sopenharmony_ci	}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	pci_set_drvdata(dp, p);
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	init_chips(p, addr);
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	if (register_framebuffer(p) < 0) {
4168c2ecf20Sopenharmony_ci		dev_err(&dp->dev,"C&T 65550 framebuffer failed to register\n");
4178c2ecf20Sopenharmony_ci		goto err_unmap;
4188c2ecf20Sopenharmony_ci	}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	dev_info(&dp->dev,"fb%d: Chips 65550 frame buffer"
4218c2ecf20Sopenharmony_ci		 " (%dK RAM detected)\n",
4228c2ecf20Sopenharmony_ci		 p->node, p->fix.smem_len / 1024);
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	return 0;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci err_unmap:
4278c2ecf20Sopenharmony_ci	iounmap(p->screen_base);
4288c2ecf20Sopenharmony_ci err_release_pci:
4298c2ecf20Sopenharmony_ci	pci_release_region(dp, 0);
4308c2ecf20Sopenharmony_ci err_release_fb:
4318c2ecf20Sopenharmony_ci	framebuffer_release(p);
4328c2ecf20Sopenharmony_ci err_disable:
4338c2ecf20Sopenharmony_ci	pci_disable_device(dp);
4348c2ecf20Sopenharmony_ci err_out:
4358c2ecf20Sopenharmony_ci	return rc;
4368c2ecf20Sopenharmony_ci}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_cistatic void chipsfb_remove(struct pci_dev *dp)
4398c2ecf20Sopenharmony_ci{
4408c2ecf20Sopenharmony_ci	struct fb_info *p = pci_get_drvdata(dp);
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	if (p->screen_base == NULL)
4438c2ecf20Sopenharmony_ci		return;
4448c2ecf20Sopenharmony_ci	unregister_framebuffer(p);
4458c2ecf20Sopenharmony_ci	iounmap(p->screen_base);
4468c2ecf20Sopenharmony_ci	p->screen_base = NULL;
4478c2ecf20Sopenharmony_ci	pci_release_region(dp, 0);
4488c2ecf20Sopenharmony_ci}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
4518c2ecf20Sopenharmony_cistatic int chipsfb_pci_suspend(struct pci_dev *pdev, pm_message_t state)
4528c2ecf20Sopenharmony_ci{
4538c2ecf20Sopenharmony_ci        struct fb_info *p = pci_get_drvdata(pdev);
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	if (state.event == pdev->dev.power.power_state.event)
4568c2ecf20Sopenharmony_ci		return 0;
4578c2ecf20Sopenharmony_ci	if (!(state.event & PM_EVENT_SLEEP))
4588c2ecf20Sopenharmony_ci		goto done;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	console_lock();
4618c2ecf20Sopenharmony_ci	chipsfb_blank(1, p);
4628c2ecf20Sopenharmony_ci	fb_set_suspend(p, 1);
4638c2ecf20Sopenharmony_ci	console_unlock();
4648c2ecf20Sopenharmony_ci done:
4658c2ecf20Sopenharmony_ci	pdev->dev.power.power_state = state;
4668c2ecf20Sopenharmony_ci	return 0;
4678c2ecf20Sopenharmony_ci}
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_cistatic int chipsfb_pci_resume(struct pci_dev *pdev)
4708c2ecf20Sopenharmony_ci{
4718c2ecf20Sopenharmony_ci        struct fb_info *p = pci_get_drvdata(pdev);
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	console_lock();
4748c2ecf20Sopenharmony_ci	fb_set_suspend(p, 0);
4758c2ecf20Sopenharmony_ci	chipsfb_blank(0, p);
4768c2ecf20Sopenharmony_ci	console_unlock();
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	pdev->dev.power.power_state = PMSG_ON;
4798c2ecf20Sopenharmony_ci	return 0;
4808c2ecf20Sopenharmony_ci}
4818c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_cistatic struct pci_device_id chipsfb_pci_tbl[] = {
4858c2ecf20Sopenharmony_ci	{ PCI_VENDOR_ID_CT, PCI_DEVICE_ID_CT_65550, PCI_ANY_ID, PCI_ANY_ID },
4868c2ecf20Sopenharmony_ci	{ 0 }
4878c2ecf20Sopenharmony_ci};
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, chipsfb_pci_tbl);
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_cistatic struct pci_driver chipsfb_driver = {
4928c2ecf20Sopenharmony_ci	.name =		"chipsfb",
4938c2ecf20Sopenharmony_ci	.id_table =	chipsfb_pci_tbl,
4948c2ecf20Sopenharmony_ci	.probe =	chipsfb_pci_init,
4958c2ecf20Sopenharmony_ci	.remove =	chipsfb_remove,
4968c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
4978c2ecf20Sopenharmony_ci	.suspend =	chipsfb_pci_suspend,
4988c2ecf20Sopenharmony_ci	.resume =	chipsfb_pci_resume,
4998c2ecf20Sopenharmony_ci#endif
5008c2ecf20Sopenharmony_ci};
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ciint __init chips_init(void)
5038c2ecf20Sopenharmony_ci{
5048c2ecf20Sopenharmony_ci	if (fb_get_options("chipsfb", NULL))
5058c2ecf20Sopenharmony_ci		return -ENODEV;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	return pci_register_driver(&chipsfb_driver);
5088c2ecf20Sopenharmony_ci}
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_cimodule_init(chips_init);
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_cistatic void __exit chipsfb_exit(void)
5138c2ecf20Sopenharmony_ci{
5148c2ecf20Sopenharmony_ci	pci_unregister_driver(&chipsfb_driver);
5158c2ecf20Sopenharmony_ci}
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
518