1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * drivers/video/chipsfb.c -- frame buffer device for
3*4882a593Smuzhiyun * Chips & Technologies 65550 chip.
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (C) 1998-2002 Paul Mackerras
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * This file is derived from the Powermac "chips" driver:
8*4882a593Smuzhiyun * Copyright (C) 1997 Fabio Riccardi.
9*4882a593Smuzhiyun * And from the frame buffer device for Open Firmware-initialized devices:
10*4882a593Smuzhiyun * Copyright (C) 1997 Geert Uytterhoeven.
11*4882a593Smuzhiyun *
12*4882a593Smuzhiyun * This file is subject to the terms and conditions of the GNU General Public
13*4882a593Smuzhiyun * License. See the file COPYING in the main directory of this archive for
14*4882a593Smuzhiyun * more details.
15*4882a593Smuzhiyun */
16*4882a593Smuzhiyun
17*4882a593Smuzhiyun #include <linux/module.h>
18*4882a593Smuzhiyun #include <linux/kernel.h>
19*4882a593Smuzhiyun #include <linux/errno.h>
20*4882a593Smuzhiyun #include <linux/string.h>
21*4882a593Smuzhiyun #include <linux/mm.h>
22*4882a593Smuzhiyun #include <linux/vmalloc.h>
23*4882a593Smuzhiyun #include <linux/delay.h>
24*4882a593Smuzhiyun #include <linux/interrupt.h>
25*4882a593Smuzhiyun #include <linux/fb.h>
26*4882a593Smuzhiyun #include <linux/pm.h>
27*4882a593Smuzhiyun #include <linux/init.h>
28*4882a593Smuzhiyun #include <linux/pci.h>
29*4882a593Smuzhiyun #include <linux/console.h>
30*4882a593Smuzhiyun
31*4882a593Smuzhiyun #ifdef CONFIG_PMAC_BACKLIGHT
32*4882a593Smuzhiyun #include <asm/backlight.h>
33*4882a593Smuzhiyun #endif
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun /*
36*4882a593Smuzhiyun * Since we access the display with inb/outb to fixed port numbers,
37*4882a593Smuzhiyun * we can only handle one 6555x chip. -- paulus
38*4882a593Smuzhiyun */
39*4882a593Smuzhiyun #define write_ind(num, val, ap, dp) do { \
40*4882a593Smuzhiyun outb((num), (ap)); outb((val), (dp)); \
41*4882a593Smuzhiyun } while (0)
42*4882a593Smuzhiyun #define read_ind(num, var, ap, dp) do { \
43*4882a593Smuzhiyun outb((num), (ap)); var = inb((dp)); \
44*4882a593Smuzhiyun } while (0)
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun /* extension registers */
47*4882a593Smuzhiyun #define write_xr(num, val) write_ind(num, val, 0x3d6, 0x3d7)
48*4882a593Smuzhiyun #define read_xr(num, var) read_ind(num, var, 0x3d6, 0x3d7)
49*4882a593Smuzhiyun /* flat panel registers */
50*4882a593Smuzhiyun #define write_fr(num, val) write_ind(num, val, 0x3d0, 0x3d1)
51*4882a593Smuzhiyun #define read_fr(num, var) read_ind(num, var, 0x3d0, 0x3d1)
52*4882a593Smuzhiyun /* CRTC registers */
53*4882a593Smuzhiyun #define write_cr(num, val) write_ind(num, val, 0x3d4, 0x3d5)
54*4882a593Smuzhiyun #define read_cr(num, var) read_ind(num, var, 0x3d4, 0x3d5)
55*4882a593Smuzhiyun /* graphics registers */
56*4882a593Smuzhiyun #define write_gr(num, val) write_ind(num, val, 0x3ce, 0x3cf)
57*4882a593Smuzhiyun #define read_gr(num, var) read_ind(num, var, 0x3ce, 0x3cf)
58*4882a593Smuzhiyun /* sequencer registers */
59*4882a593Smuzhiyun #define write_sr(num, val) write_ind(num, val, 0x3c4, 0x3c5)
60*4882a593Smuzhiyun #define read_sr(num, var) read_ind(num, var, 0x3c4, 0x3c5)
61*4882a593Smuzhiyun /* attribute registers - slightly strange */
62*4882a593Smuzhiyun #define write_ar(num, val) do { \
63*4882a593Smuzhiyun inb(0x3da); write_ind(num, val, 0x3c0, 0x3c0); \
64*4882a593Smuzhiyun } while (0)
65*4882a593Smuzhiyun #define read_ar(num, var) do { \
66*4882a593Smuzhiyun inb(0x3da); read_ind(num, var, 0x3c0, 0x3c1); \
67*4882a593Smuzhiyun } while (0)
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun /*
70*4882a593Smuzhiyun * Exported functions
71*4882a593Smuzhiyun */
72*4882a593Smuzhiyun int chips_init(void);
73*4882a593Smuzhiyun
74*4882a593Smuzhiyun static int chipsfb_pci_init(struct pci_dev *dp, const struct pci_device_id *);
75*4882a593Smuzhiyun static int chipsfb_check_var(struct fb_var_screeninfo *var,
76*4882a593Smuzhiyun struct fb_info *info);
77*4882a593Smuzhiyun static int chipsfb_set_par(struct fb_info *info);
78*4882a593Smuzhiyun static int chipsfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
79*4882a593Smuzhiyun u_int transp, struct fb_info *info);
80*4882a593Smuzhiyun static int chipsfb_blank(int blank, struct fb_info *info);
81*4882a593Smuzhiyun
82*4882a593Smuzhiyun static const struct fb_ops chipsfb_ops = {
83*4882a593Smuzhiyun .owner = THIS_MODULE,
84*4882a593Smuzhiyun .fb_check_var = chipsfb_check_var,
85*4882a593Smuzhiyun .fb_set_par = chipsfb_set_par,
86*4882a593Smuzhiyun .fb_setcolreg = chipsfb_setcolreg,
87*4882a593Smuzhiyun .fb_blank = chipsfb_blank,
88*4882a593Smuzhiyun .fb_fillrect = cfb_fillrect,
89*4882a593Smuzhiyun .fb_copyarea = cfb_copyarea,
90*4882a593Smuzhiyun .fb_imageblit = cfb_imageblit,
91*4882a593Smuzhiyun };
92*4882a593Smuzhiyun
chipsfb_check_var(struct fb_var_screeninfo * var,struct fb_info * info)93*4882a593Smuzhiyun static int chipsfb_check_var(struct fb_var_screeninfo *var,
94*4882a593Smuzhiyun struct fb_info *info)
95*4882a593Smuzhiyun {
96*4882a593Smuzhiyun if (var->xres > 800 || var->yres > 600
97*4882a593Smuzhiyun || var->xres_virtual > 800 || var->yres_virtual > 600
98*4882a593Smuzhiyun || (var->bits_per_pixel != 8 && var->bits_per_pixel != 16)
99*4882a593Smuzhiyun || var->nonstd
100*4882a593Smuzhiyun || (var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED)
101*4882a593Smuzhiyun return -EINVAL;
102*4882a593Smuzhiyun
103*4882a593Smuzhiyun var->xres = var->xres_virtual = 800;
104*4882a593Smuzhiyun var->yres = var->yres_virtual = 600;
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun return 0;
107*4882a593Smuzhiyun }
108*4882a593Smuzhiyun
chipsfb_set_par(struct fb_info * info)109*4882a593Smuzhiyun static int chipsfb_set_par(struct fb_info *info)
110*4882a593Smuzhiyun {
111*4882a593Smuzhiyun if (info->var.bits_per_pixel == 16) {
112*4882a593Smuzhiyun write_cr(0x13, 200); // Set line length (doublewords)
113*4882a593Smuzhiyun write_xr(0x81, 0x14); // 15 bit (555) color mode
114*4882a593Smuzhiyun write_xr(0x82, 0x00); // Disable palettes
115*4882a593Smuzhiyun write_xr(0x20, 0x10); // 16 bit blitter mode
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun info->fix.line_length = 800*2;
118*4882a593Smuzhiyun info->fix.visual = FB_VISUAL_TRUECOLOR;
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun info->var.red.offset = 10;
121*4882a593Smuzhiyun info->var.green.offset = 5;
122*4882a593Smuzhiyun info->var.blue.offset = 0;
123*4882a593Smuzhiyun info->var.red.length = info->var.green.length =
124*4882a593Smuzhiyun info->var.blue.length = 5;
125*4882a593Smuzhiyun
126*4882a593Smuzhiyun } else {
127*4882a593Smuzhiyun /* p->var.bits_per_pixel == 8 */
128*4882a593Smuzhiyun write_cr(0x13, 100); // Set line length (doublewords)
129*4882a593Smuzhiyun write_xr(0x81, 0x12); // 8 bit color mode
130*4882a593Smuzhiyun write_xr(0x82, 0x08); // Graphics gamma enable
131*4882a593Smuzhiyun write_xr(0x20, 0x00); // 8 bit blitter mode
132*4882a593Smuzhiyun
133*4882a593Smuzhiyun info->fix.line_length = 800;
134*4882a593Smuzhiyun info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
135*4882a593Smuzhiyun
136*4882a593Smuzhiyun info->var.red.offset = info->var.green.offset =
137*4882a593Smuzhiyun info->var.blue.offset = 0;
138*4882a593Smuzhiyun info->var.red.length = info->var.green.length =
139*4882a593Smuzhiyun info->var.blue.length = 8;
140*4882a593Smuzhiyun
141*4882a593Smuzhiyun }
142*4882a593Smuzhiyun return 0;
143*4882a593Smuzhiyun }
144*4882a593Smuzhiyun
chipsfb_blank(int blank,struct fb_info * info)145*4882a593Smuzhiyun static int chipsfb_blank(int blank, struct fb_info *info)
146*4882a593Smuzhiyun {
147*4882a593Smuzhiyun return 1; /* get fb_blank to set the colormap to all black */
148*4882a593Smuzhiyun }
149*4882a593Smuzhiyun
chipsfb_setcolreg(u_int regno,u_int red,u_int green,u_int blue,u_int transp,struct fb_info * info)150*4882a593Smuzhiyun static int chipsfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
151*4882a593Smuzhiyun u_int transp, struct fb_info *info)
152*4882a593Smuzhiyun {
153*4882a593Smuzhiyun if (regno > 255)
154*4882a593Smuzhiyun return 1;
155*4882a593Smuzhiyun red >>= 8;
156*4882a593Smuzhiyun green >>= 8;
157*4882a593Smuzhiyun blue >>= 8;
158*4882a593Smuzhiyun outb(regno, 0x3c8);
159*4882a593Smuzhiyun udelay(1);
160*4882a593Smuzhiyun outb(red, 0x3c9);
161*4882a593Smuzhiyun outb(green, 0x3c9);
162*4882a593Smuzhiyun outb(blue, 0x3c9);
163*4882a593Smuzhiyun
164*4882a593Smuzhiyun return 0;
165*4882a593Smuzhiyun }
166*4882a593Smuzhiyun
167*4882a593Smuzhiyun struct chips_init_reg {
168*4882a593Smuzhiyun unsigned char addr;
169*4882a593Smuzhiyun unsigned char data;
170*4882a593Smuzhiyun };
171*4882a593Smuzhiyun
172*4882a593Smuzhiyun static struct chips_init_reg chips_init_sr[] = {
173*4882a593Smuzhiyun { 0x00, 0x03 },
174*4882a593Smuzhiyun { 0x01, 0x01 },
175*4882a593Smuzhiyun { 0x02, 0x0f },
176*4882a593Smuzhiyun { 0x04, 0x0e }
177*4882a593Smuzhiyun };
178*4882a593Smuzhiyun
179*4882a593Smuzhiyun static struct chips_init_reg chips_init_gr[] = {
180*4882a593Smuzhiyun { 0x05, 0x00 },
181*4882a593Smuzhiyun { 0x06, 0x0d },
182*4882a593Smuzhiyun { 0x08, 0xff }
183*4882a593Smuzhiyun };
184*4882a593Smuzhiyun
185*4882a593Smuzhiyun static struct chips_init_reg chips_init_ar[] = {
186*4882a593Smuzhiyun { 0x10, 0x01 },
187*4882a593Smuzhiyun { 0x12, 0x0f },
188*4882a593Smuzhiyun { 0x13, 0x00 }
189*4882a593Smuzhiyun };
190*4882a593Smuzhiyun
191*4882a593Smuzhiyun static struct chips_init_reg chips_init_cr[] = {
192*4882a593Smuzhiyun { 0x00, 0x7f },
193*4882a593Smuzhiyun { 0x01, 0x63 },
194*4882a593Smuzhiyun { 0x02, 0x63 },
195*4882a593Smuzhiyun { 0x03, 0x83 },
196*4882a593Smuzhiyun { 0x04, 0x66 },
197*4882a593Smuzhiyun { 0x05, 0x10 },
198*4882a593Smuzhiyun { 0x06, 0x72 },
199*4882a593Smuzhiyun { 0x07, 0x3e },
200*4882a593Smuzhiyun { 0x08, 0x00 },
201*4882a593Smuzhiyun { 0x09, 0x40 },
202*4882a593Smuzhiyun { 0x0c, 0x00 },
203*4882a593Smuzhiyun { 0x0d, 0x00 },
204*4882a593Smuzhiyun { 0x10, 0x59 },
205*4882a593Smuzhiyun { 0x11, 0x0d },
206*4882a593Smuzhiyun { 0x12, 0x57 },
207*4882a593Smuzhiyun { 0x13, 0x64 },
208*4882a593Smuzhiyun { 0x14, 0x00 },
209*4882a593Smuzhiyun { 0x15, 0x57 },
210*4882a593Smuzhiyun { 0x16, 0x73 },
211*4882a593Smuzhiyun { 0x17, 0xe3 },
212*4882a593Smuzhiyun { 0x18, 0xff },
213*4882a593Smuzhiyun { 0x30, 0x02 },
214*4882a593Smuzhiyun { 0x31, 0x02 },
215*4882a593Smuzhiyun { 0x32, 0x02 },
216*4882a593Smuzhiyun { 0x33, 0x02 },
217*4882a593Smuzhiyun { 0x40, 0x00 },
218*4882a593Smuzhiyun { 0x41, 0x00 },
219*4882a593Smuzhiyun { 0x40, 0x80 }
220*4882a593Smuzhiyun };
221*4882a593Smuzhiyun
222*4882a593Smuzhiyun static struct chips_init_reg chips_init_fr[] = {
223*4882a593Smuzhiyun { 0x01, 0x02 },
224*4882a593Smuzhiyun { 0x03, 0x08 },
225*4882a593Smuzhiyun { 0x04, 0x81 },
226*4882a593Smuzhiyun { 0x05, 0x21 },
227*4882a593Smuzhiyun { 0x08, 0x0c },
228*4882a593Smuzhiyun { 0x0a, 0x74 },
229*4882a593Smuzhiyun { 0x0b, 0x11 },
230*4882a593Smuzhiyun { 0x10, 0x0c },
231*4882a593Smuzhiyun { 0x11, 0xe0 },
232*4882a593Smuzhiyun /* { 0x12, 0x40 }, -- 3400 needs 40, 2400 needs 48, no way to tell */
233*4882a593Smuzhiyun { 0x20, 0x63 },
234*4882a593Smuzhiyun { 0x21, 0x68 },
235*4882a593Smuzhiyun { 0x22, 0x19 },
236*4882a593Smuzhiyun { 0x23, 0x7f },
237*4882a593Smuzhiyun { 0x24, 0x68 },
238*4882a593Smuzhiyun { 0x26, 0x00 },
239*4882a593Smuzhiyun { 0x27, 0x0f },
240*4882a593Smuzhiyun { 0x30, 0x57 },
241*4882a593Smuzhiyun { 0x31, 0x58 },
242*4882a593Smuzhiyun { 0x32, 0x0d },
243*4882a593Smuzhiyun { 0x33, 0x72 },
244*4882a593Smuzhiyun { 0x34, 0x02 },
245*4882a593Smuzhiyun { 0x35, 0x22 },
246*4882a593Smuzhiyun { 0x36, 0x02 },
247*4882a593Smuzhiyun { 0x37, 0x00 }
248*4882a593Smuzhiyun };
249*4882a593Smuzhiyun
250*4882a593Smuzhiyun static struct chips_init_reg chips_init_xr[] = {
251*4882a593Smuzhiyun { 0xce, 0x00 }, /* set default memory clock */
252*4882a593Smuzhiyun { 0xcc, 0x43 }, /* memory clock ratio */
253*4882a593Smuzhiyun { 0xcd, 0x18 },
254*4882a593Smuzhiyun { 0xce, 0xa1 },
255*4882a593Smuzhiyun { 0xc8, 0x84 },
256*4882a593Smuzhiyun { 0xc9, 0x0a },
257*4882a593Smuzhiyun { 0xca, 0x00 },
258*4882a593Smuzhiyun { 0xcb, 0x20 },
259*4882a593Smuzhiyun { 0xcf, 0x06 },
260*4882a593Smuzhiyun { 0xd0, 0x0e },
261*4882a593Smuzhiyun { 0x09, 0x01 },
262*4882a593Smuzhiyun { 0x0a, 0x02 },
263*4882a593Smuzhiyun { 0x0b, 0x01 },
264*4882a593Smuzhiyun { 0x20, 0x00 },
265*4882a593Smuzhiyun { 0x40, 0x03 },
266*4882a593Smuzhiyun { 0x41, 0x01 },
267*4882a593Smuzhiyun { 0x42, 0x00 },
268*4882a593Smuzhiyun { 0x80, 0x82 },
269*4882a593Smuzhiyun { 0x81, 0x12 },
270*4882a593Smuzhiyun { 0x82, 0x08 },
271*4882a593Smuzhiyun { 0xa0, 0x00 },
272*4882a593Smuzhiyun { 0xa8, 0x00 }
273*4882a593Smuzhiyun };
274*4882a593Smuzhiyun
chips_hw_init(void)275*4882a593Smuzhiyun static void chips_hw_init(void)
276*4882a593Smuzhiyun {
277*4882a593Smuzhiyun int i;
278*4882a593Smuzhiyun
279*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(chips_init_xr); ++i)
280*4882a593Smuzhiyun write_xr(chips_init_xr[i].addr, chips_init_xr[i].data);
281*4882a593Smuzhiyun outb(0x29, 0x3c2); /* set misc output reg */
282*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(chips_init_sr); ++i)
283*4882a593Smuzhiyun write_sr(chips_init_sr[i].addr, chips_init_sr[i].data);
284*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(chips_init_gr); ++i)
285*4882a593Smuzhiyun write_gr(chips_init_gr[i].addr, chips_init_gr[i].data);
286*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(chips_init_ar); ++i)
287*4882a593Smuzhiyun write_ar(chips_init_ar[i].addr, chips_init_ar[i].data);
288*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(chips_init_cr); ++i)
289*4882a593Smuzhiyun write_cr(chips_init_cr[i].addr, chips_init_cr[i].data);
290*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(chips_init_fr); ++i)
291*4882a593Smuzhiyun write_fr(chips_init_fr[i].addr, chips_init_fr[i].data);
292*4882a593Smuzhiyun }
293*4882a593Smuzhiyun
294*4882a593Smuzhiyun static const struct fb_fix_screeninfo chipsfb_fix = {
295*4882a593Smuzhiyun .id = "C&T 65550",
296*4882a593Smuzhiyun .type = FB_TYPE_PACKED_PIXELS,
297*4882a593Smuzhiyun .visual = FB_VISUAL_PSEUDOCOLOR,
298*4882a593Smuzhiyun .accel = FB_ACCEL_NONE,
299*4882a593Smuzhiyun .line_length = 800,
300*4882a593Smuzhiyun
301*4882a593Smuzhiyun // FIXME: Assumes 1MB frame buffer, but 65550 supports 1MB or 2MB.
302*4882a593Smuzhiyun // * "3500" PowerBook G3 (the original PB G3) has 2MB.
303*4882a593Smuzhiyun // * 2400 has 1MB composed of 2 Mitsubishi M5M4V4265CTP DRAM chips.
304*4882a593Smuzhiyun // Motherboard actually supports 2MB -- there are two blank locations
305*4882a593Smuzhiyun // for a second pair of DRAMs. (Thanks, Apple!)
306*4882a593Smuzhiyun // * 3400 has 1MB (I think). Don't know if it's expandable.
307*4882a593Smuzhiyun // -- Tim Seufert
308*4882a593Smuzhiyun .smem_len = 0x100000, /* 1MB */
309*4882a593Smuzhiyun };
310*4882a593Smuzhiyun
311*4882a593Smuzhiyun static const struct fb_var_screeninfo chipsfb_var = {
312*4882a593Smuzhiyun .xres = 800,
313*4882a593Smuzhiyun .yres = 600,
314*4882a593Smuzhiyun .xres_virtual = 800,
315*4882a593Smuzhiyun .yres_virtual = 600,
316*4882a593Smuzhiyun .bits_per_pixel = 8,
317*4882a593Smuzhiyun .red = { .length = 8 },
318*4882a593Smuzhiyun .green = { .length = 8 },
319*4882a593Smuzhiyun .blue = { .length = 8 },
320*4882a593Smuzhiyun .height = -1,
321*4882a593Smuzhiyun .width = -1,
322*4882a593Smuzhiyun .vmode = FB_VMODE_NONINTERLACED,
323*4882a593Smuzhiyun .pixclock = 10000,
324*4882a593Smuzhiyun .left_margin = 16,
325*4882a593Smuzhiyun .right_margin = 16,
326*4882a593Smuzhiyun .upper_margin = 16,
327*4882a593Smuzhiyun .lower_margin = 16,
328*4882a593Smuzhiyun .hsync_len = 8,
329*4882a593Smuzhiyun .vsync_len = 8,
330*4882a593Smuzhiyun };
331*4882a593Smuzhiyun
init_chips(struct fb_info * p,unsigned long addr)332*4882a593Smuzhiyun static void init_chips(struct fb_info *p, unsigned long addr)
333*4882a593Smuzhiyun {
334*4882a593Smuzhiyun fb_memset(p->screen_base, 0, 0x100000);
335*4882a593Smuzhiyun
336*4882a593Smuzhiyun p->fix = chipsfb_fix;
337*4882a593Smuzhiyun p->fix.smem_start = addr;
338*4882a593Smuzhiyun
339*4882a593Smuzhiyun p->var = chipsfb_var;
340*4882a593Smuzhiyun
341*4882a593Smuzhiyun p->fbops = &chipsfb_ops;
342*4882a593Smuzhiyun p->flags = FBINFO_DEFAULT;
343*4882a593Smuzhiyun
344*4882a593Smuzhiyun fb_alloc_cmap(&p->cmap, 256, 0);
345*4882a593Smuzhiyun
346*4882a593Smuzhiyun chips_hw_init();
347*4882a593Smuzhiyun }
348*4882a593Smuzhiyun
chipsfb_pci_init(struct pci_dev * dp,const struct pci_device_id * ent)349*4882a593Smuzhiyun static int chipsfb_pci_init(struct pci_dev *dp, const struct pci_device_id *ent)
350*4882a593Smuzhiyun {
351*4882a593Smuzhiyun struct fb_info *p;
352*4882a593Smuzhiyun unsigned long addr;
353*4882a593Smuzhiyun unsigned short cmd;
354*4882a593Smuzhiyun int rc = -ENODEV;
355*4882a593Smuzhiyun
356*4882a593Smuzhiyun if (pci_enable_device(dp) < 0) {
357*4882a593Smuzhiyun dev_err(&dp->dev, "Cannot enable PCI device\n");
358*4882a593Smuzhiyun goto err_out;
359*4882a593Smuzhiyun }
360*4882a593Smuzhiyun
361*4882a593Smuzhiyun if ((dp->resource[0].flags & IORESOURCE_MEM) == 0)
362*4882a593Smuzhiyun goto err_disable;
363*4882a593Smuzhiyun addr = pci_resource_start(dp, 0);
364*4882a593Smuzhiyun if (addr == 0)
365*4882a593Smuzhiyun goto err_disable;
366*4882a593Smuzhiyun
367*4882a593Smuzhiyun p = framebuffer_alloc(0, &dp->dev);
368*4882a593Smuzhiyun if (p == NULL) {
369*4882a593Smuzhiyun rc = -ENOMEM;
370*4882a593Smuzhiyun goto err_disable;
371*4882a593Smuzhiyun }
372*4882a593Smuzhiyun
373*4882a593Smuzhiyun if (pci_request_region(dp, 0, "chipsfb") != 0) {
374*4882a593Smuzhiyun dev_err(&dp->dev, "Cannot request framebuffer\n");
375*4882a593Smuzhiyun rc = -EBUSY;
376*4882a593Smuzhiyun goto err_release_fb;
377*4882a593Smuzhiyun }
378*4882a593Smuzhiyun
379*4882a593Smuzhiyun #ifdef __BIG_ENDIAN
380*4882a593Smuzhiyun addr += 0x800000; // Use big-endian aperture
381*4882a593Smuzhiyun #endif
382*4882a593Smuzhiyun
383*4882a593Smuzhiyun /* we should use pci_enable_device here, but,
384*4882a593Smuzhiyun the device doesn't declare its I/O ports in its BARs
385*4882a593Smuzhiyun so pci_enable_device won't turn on I/O responses */
386*4882a593Smuzhiyun pci_read_config_word(dp, PCI_COMMAND, &cmd);
387*4882a593Smuzhiyun cmd |= 3; /* enable memory and IO space */
388*4882a593Smuzhiyun pci_write_config_word(dp, PCI_COMMAND, cmd);
389*4882a593Smuzhiyun
390*4882a593Smuzhiyun #ifdef CONFIG_PMAC_BACKLIGHT
391*4882a593Smuzhiyun /* turn on the backlight */
392*4882a593Smuzhiyun mutex_lock(&pmac_backlight_mutex);
393*4882a593Smuzhiyun if (pmac_backlight) {
394*4882a593Smuzhiyun pmac_backlight->props.power = FB_BLANK_UNBLANK;
395*4882a593Smuzhiyun backlight_update_status(pmac_backlight);
396*4882a593Smuzhiyun }
397*4882a593Smuzhiyun mutex_unlock(&pmac_backlight_mutex);
398*4882a593Smuzhiyun #endif /* CONFIG_PMAC_BACKLIGHT */
399*4882a593Smuzhiyun
400*4882a593Smuzhiyun #ifdef CONFIG_PPC
401*4882a593Smuzhiyun p->screen_base = ioremap_wc(addr, 0x200000);
402*4882a593Smuzhiyun #else
403*4882a593Smuzhiyun p->screen_base = ioremap(addr, 0x200000);
404*4882a593Smuzhiyun #endif
405*4882a593Smuzhiyun if (p->screen_base == NULL) {
406*4882a593Smuzhiyun dev_err(&dp->dev, "Cannot map framebuffer\n");
407*4882a593Smuzhiyun rc = -ENOMEM;
408*4882a593Smuzhiyun goto err_release_pci;
409*4882a593Smuzhiyun }
410*4882a593Smuzhiyun
411*4882a593Smuzhiyun pci_set_drvdata(dp, p);
412*4882a593Smuzhiyun
413*4882a593Smuzhiyun init_chips(p, addr);
414*4882a593Smuzhiyun
415*4882a593Smuzhiyun if (register_framebuffer(p) < 0) {
416*4882a593Smuzhiyun dev_err(&dp->dev,"C&T 65550 framebuffer failed to register\n");
417*4882a593Smuzhiyun goto err_unmap;
418*4882a593Smuzhiyun }
419*4882a593Smuzhiyun
420*4882a593Smuzhiyun dev_info(&dp->dev,"fb%d: Chips 65550 frame buffer"
421*4882a593Smuzhiyun " (%dK RAM detected)\n",
422*4882a593Smuzhiyun p->node, p->fix.smem_len / 1024);
423*4882a593Smuzhiyun
424*4882a593Smuzhiyun return 0;
425*4882a593Smuzhiyun
426*4882a593Smuzhiyun err_unmap:
427*4882a593Smuzhiyun iounmap(p->screen_base);
428*4882a593Smuzhiyun err_release_pci:
429*4882a593Smuzhiyun pci_release_region(dp, 0);
430*4882a593Smuzhiyun err_release_fb:
431*4882a593Smuzhiyun framebuffer_release(p);
432*4882a593Smuzhiyun err_disable:
433*4882a593Smuzhiyun pci_disable_device(dp);
434*4882a593Smuzhiyun err_out:
435*4882a593Smuzhiyun return rc;
436*4882a593Smuzhiyun }
437*4882a593Smuzhiyun
chipsfb_remove(struct pci_dev * dp)438*4882a593Smuzhiyun static void chipsfb_remove(struct pci_dev *dp)
439*4882a593Smuzhiyun {
440*4882a593Smuzhiyun struct fb_info *p = pci_get_drvdata(dp);
441*4882a593Smuzhiyun
442*4882a593Smuzhiyun if (p->screen_base == NULL)
443*4882a593Smuzhiyun return;
444*4882a593Smuzhiyun unregister_framebuffer(p);
445*4882a593Smuzhiyun iounmap(p->screen_base);
446*4882a593Smuzhiyun p->screen_base = NULL;
447*4882a593Smuzhiyun pci_release_region(dp, 0);
448*4882a593Smuzhiyun }
449*4882a593Smuzhiyun
450*4882a593Smuzhiyun #ifdef CONFIG_PM
chipsfb_pci_suspend(struct pci_dev * pdev,pm_message_t state)451*4882a593Smuzhiyun static int chipsfb_pci_suspend(struct pci_dev *pdev, pm_message_t state)
452*4882a593Smuzhiyun {
453*4882a593Smuzhiyun struct fb_info *p = pci_get_drvdata(pdev);
454*4882a593Smuzhiyun
455*4882a593Smuzhiyun if (state.event == pdev->dev.power.power_state.event)
456*4882a593Smuzhiyun return 0;
457*4882a593Smuzhiyun if (!(state.event & PM_EVENT_SLEEP))
458*4882a593Smuzhiyun goto done;
459*4882a593Smuzhiyun
460*4882a593Smuzhiyun console_lock();
461*4882a593Smuzhiyun chipsfb_blank(1, p);
462*4882a593Smuzhiyun fb_set_suspend(p, 1);
463*4882a593Smuzhiyun console_unlock();
464*4882a593Smuzhiyun done:
465*4882a593Smuzhiyun pdev->dev.power.power_state = state;
466*4882a593Smuzhiyun return 0;
467*4882a593Smuzhiyun }
468*4882a593Smuzhiyun
chipsfb_pci_resume(struct pci_dev * pdev)469*4882a593Smuzhiyun static int chipsfb_pci_resume(struct pci_dev *pdev)
470*4882a593Smuzhiyun {
471*4882a593Smuzhiyun struct fb_info *p = pci_get_drvdata(pdev);
472*4882a593Smuzhiyun
473*4882a593Smuzhiyun console_lock();
474*4882a593Smuzhiyun fb_set_suspend(p, 0);
475*4882a593Smuzhiyun chipsfb_blank(0, p);
476*4882a593Smuzhiyun console_unlock();
477*4882a593Smuzhiyun
478*4882a593Smuzhiyun pdev->dev.power.power_state = PMSG_ON;
479*4882a593Smuzhiyun return 0;
480*4882a593Smuzhiyun }
481*4882a593Smuzhiyun #endif /* CONFIG_PM */
482*4882a593Smuzhiyun
483*4882a593Smuzhiyun
484*4882a593Smuzhiyun static struct pci_device_id chipsfb_pci_tbl[] = {
485*4882a593Smuzhiyun { PCI_VENDOR_ID_CT, PCI_DEVICE_ID_CT_65550, PCI_ANY_ID, PCI_ANY_ID },
486*4882a593Smuzhiyun { 0 }
487*4882a593Smuzhiyun };
488*4882a593Smuzhiyun
489*4882a593Smuzhiyun MODULE_DEVICE_TABLE(pci, chipsfb_pci_tbl);
490*4882a593Smuzhiyun
491*4882a593Smuzhiyun static struct pci_driver chipsfb_driver = {
492*4882a593Smuzhiyun .name = "chipsfb",
493*4882a593Smuzhiyun .id_table = chipsfb_pci_tbl,
494*4882a593Smuzhiyun .probe = chipsfb_pci_init,
495*4882a593Smuzhiyun .remove = chipsfb_remove,
496*4882a593Smuzhiyun #ifdef CONFIG_PM
497*4882a593Smuzhiyun .suspend = chipsfb_pci_suspend,
498*4882a593Smuzhiyun .resume = chipsfb_pci_resume,
499*4882a593Smuzhiyun #endif
500*4882a593Smuzhiyun };
501*4882a593Smuzhiyun
chips_init(void)502*4882a593Smuzhiyun int __init chips_init(void)
503*4882a593Smuzhiyun {
504*4882a593Smuzhiyun if (fb_get_options("chipsfb", NULL))
505*4882a593Smuzhiyun return -ENODEV;
506*4882a593Smuzhiyun
507*4882a593Smuzhiyun return pci_register_driver(&chipsfb_driver);
508*4882a593Smuzhiyun }
509*4882a593Smuzhiyun
510*4882a593Smuzhiyun module_init(chips_init);
511*4882a593Smuzhiyun
chipsfb_exit(void)512*4882a593Smuzhiyun static void __exit chipsfb_exit(void)
513*4882a593Smuzhiyun {
514*4882a593Smuzhiyun pci_unregister_driver(&chipsfb_driver);
515*4882a593Smuzhiyun }
516*4882a593Smuzhiyun
517*4882a593Smuzhiyun MODULE_LICENSE("GPL");
518