xref: /OK3568_Linux_fs/kernel/drivers/video/fbdev/platinumfb.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  *  platinumfb.c -- frame buffer device for the PowerMac 'platinum' display
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  *  Copyright (C) 1998 Franz Sirl
5*4882a593Smuzhiyun  *
6*4882a593Smuzhiyun  *  Frame buffer structure from:
7*4882a593Smuzhiyun  *    drivers/video/controlfb.c -- frame buffer device for
8*4882a593Smuzhiyun  *    Apple 'control' display chip.
9*4882a593Smuzhiyun  *    Copyright (C) 1998 Dan Jacobowitz
10*4882a593Smuzhiyun  *
11*4882a593Smuzhiyun  *  Hardware information from:
12*4882a593Smuzhiyun  *    platinum.c: Console support for PowerMac "platinum" display adaptor.
13*4882a593Smuzhiyun  *    Copyright (C) 1996 Paul Mackerras and Mark Abene
14*4882a593Smuzhiyun  *
15*4882a593Smuzhiyun  *  This file is subject to the terms and conditions of the GNU General Public
16*4882a593Smuzhiyun  *  License. See the file COPYING in the main directory of this archive for
17*4882a593Smuzhiyun  *  more details.
18*4882a593Smuzhiyun  */
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun #undef DEBUG
21*4882a593Smuzhiyun 
22*4882a593Smuzhiyun #include <linux/module.h>
23*4882a593Smuzhiyun #include <linux/kernel.h>
24*4882a593Smuzhiyun #include <linux/errno.h>
25*4882a593Smuzhiyun #include <linux/string.h>
26*4882a593Smuzhiyun #include <linux/mm.h>
27*4882a593Smuzhiyun #include <linux/vmalloc.h>
28*4882a593Smuzhiyun #include <linux/delay.h>
29*4882a593Smuzhiyun #include <linux/interrupt.h>
30*4882a593Smuzhiyun #include <linux/fb.h>
31*4882a593Smuzhiyun #include <linux/init.h>
32*4882a593Smuzhiyun #include <linux/nvram.h>
33*4882a593Smuzhiyun #include <linux/of_device.h>
34*4882a593Smuzhiyun #include <linux/of_platform.h>
35*4882a593Smuzhiyun #include <asm/prom.h>
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun #include "macmodes.h"
38*4882a593Smuzhiyun #include "platinumfb.h"
39*4882a593Smuzhiyun 
40*4882a593Smuzhiyun static int default_vmode = VMODE_NVRAM;
41*4882a593Smuzhiyun static int default_cmode = CMODE_NVRAM;
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun struct fb_info_platinum {
44*4882a593Smuzhiyun 	struct fb_info			*info;
45*4882a593Smuzhiyun 
46*4882a593Smuzhiyun 	int				vmode, cmode;
47*4882a593Smuzhiyun 	int				xres, yres;
48*4882a593Smuzhiyun 	int				vxres, vyres;
49*4882a593Smuzhiyun 	int				xoffset, yoffset;
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun 	struct {
52*4882a593Smuzhiyun 		__u8 red, green, blue;
53*4882a593Smuzhiyun 	}				palette[256];
54*4882a593Smuzhiyun 	u32				pseudo_palette[16];
55*4882a593Smuzhiyun 
56*4882a593Smuzhiyun 	volatile struct cmap_regs	__iomem *cmap_regs;
57*4882a593Smuzhiyun 	unsigned long			cmap_regs_phys;
58*4882a593Smuzhiyun 
59*4882a593Smuzhiyun 	volatile struct platinum_regs	__iomem *platinum_regs;
60*4882a593Smuzhiyun 	unsigned long			platinum_regs_phys;
61*4882a593Smuzhiyun 
62*4882a593Smuzhiyun 	__u8				__iomem *frame_buffer;
63*4882a593Smuzhiyun 	volatile __u8			__iomem *base_frame_buffer;
64*4882a593Smuzhiyun 	unsigned long			frame_buffer_phys;
65*4882a593Smuzhiyun 
66*4882a593Smuzhiyun 	unsigned long			total_vram;
67*4882a593Smuzhiyun 	int				clktype;
68*4882a593Smuzhiyun 	int				dactype;
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun 	struct resource			rsrc_fb, rsrc_reg;
71*4882a593Smuzhiyun };
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun /*
74*4882a593Smuzhiyun  * Frame buffer device API
75*4882a593Smuzhiyun  */
76*4882a593Smuzhiyun 
77*4882a593Smuzhiyun static int platinumfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
78*4882a593Smuzhiyun 	u_int transp, struct fb_info *info);
79*4882a593Smuzhiyun static int platinumfb_blank(int blank_mode, struct fb_info *info);
80*4882a593Smuzhiyun static int platinumfb_set_par (struct fb_info *info);
81*4882a593Smuzhiyun static int platinumfb_check_var (struct fb_var_screeninfo *var, struct fb_info *info);
82*4882a593Smuzhiyun 
83*4882a593Smuzhiyun /*
84*4882a593Smuzhiyun  * internal functions
85*4882a593Smuzhiyun  */
86*4882a593Smuzhiyun 
87*4882a593Smuzhiyun static inline int platinum_vram_reqd(int video_mode, int color_mode);
88*4882a593Smuzhiyun static int read_platinum_sense(struct fb_info_platinum *pinfo);
89*4882a593Smuzhiyun static void set_platinum_clock(struct fb_info_platinum *pinfo);
90*4882a593Smuzhiyun static void platinum_set_hardware(struct fb_info_platinum *pinfo);
91*4882a593Smuzhiyun static int platinum_var_to_par(struct fb_var_screeninfo *var,
92*4882a593Smuzhiyun 			       struct fb_info_platinum *pinfo,
93*4882a593Smuzhiyun 			       int check_only);
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun /*
96*4882a593Smuzhiyun  * Interface used by the world
97*4882a593Smuzhiyun  */
98*4882a593Smuzhiyun 
99*4882a593Smuzhiyun static const struct fb_ops platinumfb_ops = {
100*4882a593Smuzhiyun 	.owner =	THIS_MODULE,
101*4882a593Smuzhiyun 	.fb_check_var	= platinumfb_check_var,
102*4882a593Smuzhiyun 	.fb_set_par	= platinumfb_set_par,
103*4882a593Smuzhiyun 	.fb_setcolreg	= platinumfb_setcolreg,
104*4882a593Smuzhiyun 	.fb_blank	= platinumfb_blank,
105*4882a593Smuzhiyun 	.fb_fillrect	= cfb_fillrect,
106*4882a593Smuzhiyun 	.fb_copyarea	= cfb_copyarea,
107*4882a593Smuzhiyun 	.fb_imageblit	= cfb_imageblit,
108*4882a593Smuzhiyun };
109*4882a593Smuzhiyun 
110*4882a593Smuzhiyun /*
111*4882a593Smuzhiyun  * Checks a var structure
112*4882a593Smuzhiyun  */
platinumfb_check_var(struct fb_var_screeninfo * var,struct fb_info * info)113*4882a593Smuzhiyun static int platinumfb_check_var (struct fb_var_screeninfo *var, struct fb_info *info)
114*4882a593Smuzhiyun {
115*4882a593Smuzhiyun 	return platinum_var_to_par(var, info->par, 1);
116*4882a593Smuzhiyun }
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun /*
119*4882a593Smuzhiyun  * Applies current var to display
120*4882a593Smuzhiyun  */
platinumfb_set_par(struct fb_info * info)121*4882a593Smuzhiyun static int platinumfb_set_par (struct fb_info *info)
122*4882a593Smuzhiyun {
123*4882a593Smuzhiyun 	struct fb_info_platinum *pinfo = info->par;
124*4882a593Smuzhiyun 	struct platinum_regvals *init;
125*4882a593Smuzhiyun 	int err, offset = 0x20;
126*4882a593Smuzhiyun 
127*4882a593Smuzhiyun 	if((err = platinum_var_to_par(&info->var, pinfo, 0))) {
128*4882a593Smuzhiyun 		printk (KERN_ERR "platinumfb_set_par: error calling"
129*4882a593Smuzhiyun 				 " platinum_var_to_par: %d.\n", err);
130*4882a593Smuzhiyun 		return err;
131*4882a593Smuzhiyun 	}
132*4882a593Smuzhiyun 
133*4882a593Smuzhiyun 	platinum_set_hardware(pinfo);
134*4882a593Smuzhiyun 
135*4882a593Smuzhiyun 	init = platinum_reg_init[pinfo->vmode-1];
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun  	if ((pinfo->vmode == VMODE_832_624_75) && (pinfo->cmode > CMODE_8))
138*4882a593Smuzhiyun   		offset = 0x10;
139*4882a593Smuzhiyun 
140*4882a593Smuzhiyun 	info->screen_base = pinfo->frame_buffer + init->fb_offset + offset;
141*4882a593Smuzhiyun 	mutex_lock(&info->mm_lock);
142*4882a593Smuzhiyun 	info->fix.smem_start = (pinfo->frame_buffer_phys) + init->fb_offset + offset;
143*4882a593Smuzhiyun 	mutex_unlock(&info->mm_lock);
144*4882a593Smuzhiyun 	info->fix.visual = (pinfo->cmode == CMODE_8) ?
145*4882a593Smuzhiyun 		FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR;
146*4882a593Smuzhiyun  	info->fix.line_length = vmode_attrs[pinfo->vmode-1].hres * (1<<pinfo->cmode)
147*4882a593Smuzhiyun 		+ offset;
148*4882a593Smuzhiyun 	printk("line_length: %x\n", info->fix.line_length);
149*4882a593Smuzhiyun 	return 0;
150*4882a593Smuzhiyun }
151*4882a593Smuzhiyun 
platinumfb_blank(int blank,struct fb_info * fb)152*4882a593Smuzhiyun static int platinumfb_blank(int blank,  struct fb_info *fb)
153*4882a593Smuzhiyun {
154*4882a593Smuzhiyun /*
155*4882a593Smuzhiyun  *  Blank the screen if blank_mode != 0, else unblank. If blank == NULL
156*4882a593Smuzhiyun  *  then the caller blanks by setting the CLUT (Color Look Up Table) to all
157*4882a593Smuzhiyun  *  black. Return 0 if blanking succeeded, != 0 if un-/blanking failed due
158*4882a593Smuzhiyun  *  to e.g. a video mode which doesn't support it. Implements VESA suspend
159*4882a593Smuzhiyun  *  and powerdown modes on hardware that supports disabling hsync/vsync:
160*4882a593Smuzhiyun  *    blank_mode == 2: suspend vsync
161*4882a593Smuzhiyun  *    blank_mode == 3: suspend hsync
162*4882a593Smuzhiyun  *    blank_mode == 4: powerdown
163*4882a593Smuzhiyun  */
164*4882a593Smuzhiyun /* [danj] I think there's something fishy about those constants... */
165*4882a593Smuzhiyun /*
166*4882a593Smuzhiyun 	struct fb_info_platinum *info = (struct fb_info_platinum *) fb;
167*4882a593Smuzhiyun 	int	ctrl;
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun 	ctrl = le32_to_cpup(&info->platinum_regs->ctrl.r) | 0x33;
170*4882a593Smuzhiyun 	if (blank)
171*4882a593Smuzhiyun 		--blank_mode;
172*4882a593Smuzhiyun 	if (blank & VESA_VSYNC_SUSPEND)
173*4882a593Smuzhiyun 		ctrl &= ~3;
174*4882a593Smuzhiyun 	if (blank & VESA_HSYNC_SUSPEND)
175*4882a593Smuzhiyun 		ctrl &= ~0x30;
176*4882a593Smuzhiyun 	out_le32(&info->platinum_regs->ctrl.r, ctrl);
177*4882a593Smuzhiyun */
178*4882a593Smuzhiyun /* TODO: Figure out how the heck to powerdown this thing! */
179*4882a593Smuzhiyun 	return 0;
180*4882a593Smuzhiyun }
181*4882a593Smuzhiyun 
platinumfb_setcolreg(u_int regno,u_int red,u_int green,u_int blue,u_int transp,struct fb_info * info)182*4882a593Smuzhiyun static int platinumfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
183*4882a593Smuzhiyun 			      u_int transp, struct fb_info *info)
184*4882a593Smuzhiyun {
185*4882a593Smuzhiyun 	struct fb_info_platinum *pinfo = info->par;
186*4882a593Smuzhiyun 	volatile struct cmap_regs __iomem *cmap_regs = pinfo->cmap_regs;
187*4882a593Smuzhiyun 
188*4882a593Smuzhiyun 	if (regno > 255)
189*4882a593Smuzhiyun 		return 1;
190*4882a593Smuzhiyun 
191*4882a593Smuzhiyun 	red >>= 8;
192*4882a593Smuzhiyun 	green >>= 8;
193*4882a593Smuzhiyun 	blue >>= 8;
194*4882a593Smuzhiyun 
195*4882a593Smuzhiyun 	pinfo->palette[regno].red = red;
196*4882a593Smuzhiyun 	pinfo->palette[regno].green = green;
197*4882a593Smuzhiyun 	pinfo->palette[regno].blue = blue;
198*4882a593Smuzhiyun 
199*4882a593Smuzhiyun 	out_8(&cmap_regs->addr, regno);		/* tell clut what addr to fill	*/
200*4882a593Smuzhiyun 	out_8(&cmap_regs->lut, red);		/* send one color channel at	*/
201*4882a593Smuzhiyun 	out_8(&cmap_regs->lut, green);		/* a time...			*/
202*4882a593Smuzhiyun 	out_8(&cmap_regs->lut, blue);
203*4882a593Smuzhiyun 
204*4882a593Smuzhiyun 	if (regno < 16) {
205*4882a593Smuzhiyun 		int i;
206*4882a593Smuzhiyun 		u32 *pal = info->pseudo_palette;
207*4882a593Smuzhiyun 		switch (pinfo->cmode) {
208*4882a593Smuzhiyun 		case CMODE_16:
209*4882a593Smuzhiyun 			pal[regno] = (regno << 10) | (regno << 5) | regno;
210*4882a593Smuzhiyun 			break;
211*4882a593Smuzhiyun 		case CMODE_32:
212*4882a593Smuzhiyun 			i = (regno << 8) | regno;
213*4882a593Smuzhiyun 			pal[regno] = (i << 16) | i;
214*4882a593Smuzhiyun 			break;
215*4882a593Smuzhiyun 		}
216*4882a593Smuzhiyun 	}
217*4882a593Smuzhiyun 
218*4882a593Smuzhiyun 	return 0;
219*4882a593Smuzhiyun }
220*4882a593Smuzhiyun 
platinum_vram_reqd(int video_mode,int color_mode)221*4882a593Smuzhiyun static inline int platinum_vram_reqd(int video_mode, int color_mode)
222*4882a593Smuzhiyun {
223*4882a593Smuzhiyun 	int baseval = vmode_attrs[video_mode-1].hres * (1<<color_mode);
224*4882a593Smuzhiyun 
225*4882a593Smuzhiyun 	if ((video_mode == VMODE_832_624_75) && (color_mode > CMODE_8))
226*4882a593Smuzhiyun 		baseval += 0x10;
227*4882a593Smuzhiyun 	else
228*4882a593Smuzhiyun 		baseval += 0x20;
229*4882a593Smuzhiyun 
230*4882a593Smuzhiyun 	return vmode_attrs[video_mode-1].vres * baseval + 0x1000;
231*4882a593Smuzhiyun }
232*4882a593Smuzhiyun 
233*4882a593Smuzhiyun #define STORE_D2(a, d) { \
234*4882a593Smuzhiyun 	out_8(&cmap_regs->addr, (a+32)); \
235*4882a593Smuzhiyun 	out_8(&cmap_regs->d2, (d)); \
236*4882a593Smuzhiyun }
237*4882a593Smuzhiyun 
set_platinum_clock(struct fb_info_platinum * pinfo)238*4882a593Smuzhiyun static void set_platinum_clock(struct fb_info_platinum *pinfo)
239*4882a593Smuzhiyun {
240*4882a593Smuzhiyun 	volatile struct cmap_regs __iomem *cmap_regs = pinfo->cmap_regs;
241*4882a593Smuzhiyun 	struct platinum_regvals	*init;
242*4882a593Smuzhiyun 
243*4882a593Smuzhiyun 	init = platinum_reg_init[pinfo->vmode-1];
244*4882a593Smuzhiyun 
245*4882a593Smuzhiyun 	STORE_D2(6, 0xc6);
246*4882a593Smuzhiyun 	out_8(&cmap_regs->addr,3+32);
247*4882a593Smuzhiyun 
248*4882a593Smuzhiyun 	if (in_8(&cmap_regs->d2) == 2) {
249*4882a593Smuzhiyun 		STORE_D2(7, init->clock_params[pinfo->clktype][0]);
250*4882a593Smuzhiyun 		STORE_D2(8, init->clock_params[pinfo->clktype][1]);
251*4882a593Smuzhiyun 		STORE_D2(3, 3);
252*4882a593Smuzhiyun 	} else {
253*4882a593Smuzhiyun 		STORE_D2(4, init->clock_params[pinfo->clktype][0]);
254*4882a593Smuzhiyun 		STORE_D2(5, init->clock_params[pinfo->clktype][1]);
255*4882a593Smuzhiyun 		STORE_D2(3, 2);
256*4882a593Smuzhiyun 	}
257*4882a593Smuzhiyun 
258*4882a593Smuzhiyun 	__delay(5000);
259*4882a593Smuzhiyun 	STORE_D2(9, 0xa6);
260*4882a593Smuzhiyun }
261*4882a593Smuzhiyun 
262*4882a593Smuzhiyun 
263*4882a593Smuzhiyun /* Now how about actually saying, Make it so! */
264*4882a593Smuzhiyun /* Some things in here probably don't need to be done each time. */
platinum_set_hardware(struct fb_info_platinum * pinfo)265*4882a593Smuzhiyun static void platinum_set_hardware(struct fb_info_platinum *pinfo)
266*4882a593Smuzhiyun {
267*4882a593Smuzhiyun 	volatile struct platinum_regs	__iomem *platinum_regs = pinfo->platinum_regs;
268*4882a593Smuzhiyun 	volatile struct cmap_regs	__iomem *cmap_regs = pinfo->cmap_regs;
269*4882a593Smuzhiyun 	struct platinum_regvals		*init;
270*4882a593Smuzhiyun 	int				i;
271*4882a593Smuzhiyun 	int				vmode, cmode;
272*4882a593Smuzhiyun 
273*4882a593Smuzhiyun 	vmode = pinfo->vmode;
274*4882a593Smuzhiyun 	cmode = pinfo->cmode;
275*4882a593Smuzhiyun 
276*4882a593Smuzhiyun 	init = platinum_reg_init[vmode - 1];
277*4882a593Smuzhiyun 
278*4882a593Smuzhiyun 	/* Initialize display timing registers */
279*4882a593Smuzhiyun 	out_be32(&platinum_regs->reg[24].r, 7);	/* turn display off */
280*4882a593Smuzhiyun 
281*4882a593Smuzhiyun 	for (i = 0; i < 26; ++i)
282*4882a593Smuzhiyun 		out_be32(&platinum_regs->reg[i+32].r, init->regs[i]);
283*4882a593Smuzhiyun 
284*4882a593Smuzhiyun 	out_be32(&platinum_regs->reg[26+32].r, (pinfo->total_vram == 0x100000 ?
285*4882a593Smuzhiyun 						init->offset[cmode] + 4 - cmode :
286*4882a593Smuzhiyun 						init->offset[cmode]));
287*4882a593Smuzhiyun 	out_be32(&platinum_regs->reg[16].r, (unsigned) pinfo->frame_buffer_phys+init->fb_offset+0x10);
288*4882a593Smuzhiyun 	out_be32(&platinum_regs->reg[18].r, init->pitch[cmode]);
289*4882a593Smuzhiyun 	out_be32(&platinum_regs->reg[19].r, (pinfo->total_vram == 0x100000 ?
290*4882a593Smuzhiyun 					     init->mode[cmode+1] :
291*4882a593Smuzhiyun 					     init->mode[cmode]));
292*4882a593Smuzhiyun 	out_be32(&platinum_regs->reg[20].r, (pinfo->total_vram == 0x100000 ? 0x11 : 0x1011));
293*4882a593Smuzhiyun 	out_be32(&platinum_regs->reg[21].r, 0x100);
294*4882a593Smuzhiyun 	out_be32(&platinum_regs->reg[22].r, 1);
295*4882a593Smuzhiyun 	out_be32(&platinum_regs->reg[23].r, 1);
296*4882a593Smuzhiyun 	out_be32(&platinum_regs->reg[26].r, 0xc00);
297*4882a593Smuzhiyun 	out_be32(&platinum_regs->reg[27].r, 0x235);
298*4882a593Smuzhiyun 	/* out_be32(&platinum_regs->reg[27].r, 0x2aa); */
299*4882a593Smuzhiyun 
300*4882a593Smuzhiyun 	STORE_D2(0, (pinfo->total_vram == 0x100000 ?
301*4882a593Smuzhiyun 		     init->dacula_ctrl[cmode] & 0xf :
302*4882a593Smuzhiyun 		     init->dacula_ctrl[cmode]));
303*4882a593Smuzhiyun 	STORE_D2(1, 4);
304*4882a593Smuzhiyun 	STORE_D2(2, 0);
305*4882a593Smuzhiyun 
306*4882a593Smuzhiyun 	set_platinum_clock(pinfo);
307*4882a593Smuzhiyun 
308*4882a593Smuzhiyun 	out_be32(&platinum_regs->reg[24].r, 0);	/* turn display on */
309*4882a593Smuzhiyun }
310*4882a593Smuzhiyun 
311*4882a593Smuzhiyun /*
312*4882a593Smuzhiyun  * Set misc info vars for this driver
313*4882a593Smuzhiyun  */
platinum_init_info(struct fb_info * info,struct fb_info_platinum * pinfo)314*4882a593Smuzhiyun static void platinum_init_info(struct fb_info *info,
315*4882a593Smuzhiyun 			       struct fb_info_platinum *pinfo)
316*4882a593Smuzhiyun {
317*4882a593Smuzhiyun 	/* Fill fb_info */
318*4882a593Smuzhiyun 	info->fbops = &platinumfb_ops;
319*4882a593Smuzhiyun 	info->pseudo_palette = pinfo->pseudo_palette;
320*4882a593Smuzhiyun         info->flags = FBINFO_DEFAULT;
321*4882a593Smuzhiyun 	info->screen_base = pinfo->frame_buffer + 0x20;
322*4882a593Smuzhiyun 
323*4882a593Smuzhiyun 	fb_alloc_cmap(&info->cmap, 256, 0);
324*4882a593Smuzhiyun 
325*4882a593Smuzhiyun 	/* Fill fix common fields */
326*4882a593Smuzhiyun 	strcpy(info->fix.id, "platinum");
327*4882a593Smuzhiyun 	info->fix.mmio_start = pinfo->platinum_regs_phys;
328*4882a593Smuzhiyun 	info->fix.mmio_len = 0x1000;
329*4882a593Smuzhiyun 	info->fix.type = FB_TYPE_PACKED_PIXELS;
330*4882a593Smuzhiyun 	info->fix.smem_start = pinfo->frame_buffer_phys + 0x20; /* will be updated later */
331*4882a593Smuzhiyun 	info->fix.smem_len = pinfo->total_vram - 0x20;
332*4882a593Smuzhiyun         info->fix.ywrapstep = 0;
333*4882a593Smuzhiyun 	info->fix.xpanstep = 0;
334*4882a593Smuzhiyun 	info->fix.ypanstep = 0;
335*4882a593Smuzhiyun         info->fix.type_aux = 0;
336*4882a593Smuzhiyun         info->fix.accel = FB_ACCEL_NONE;
337*4882a593Smuzhiyun }
338*4882a593Smuzhiyun 
339*4882a593Smuzhiyun 
platinum_init_fb(struct fb_info * info)340*4882a593Smuzhiyun static int platinum_init_fb(struct fb_info *info)
341*4882a593Smuzhiyun {
342*4882a593Smuzhiyun 	struct fb_info_platinum *pinfo = info->par;
343*4882a593Smuzhiyun 	struct fb_var_screeninfo var;
344*4882a593Smuzhiyun 	int sense, rc;
345*4882a593Smuzhiyun 
346*4882a593Smuzhiyun 	sense = read_platinum_sense(pinfo);
347*4882a593Smuzhiyun 	printk(KERN_INFO "platinumfb: Monitor sense value = 0x%x, ", sense);
348*4882a593Smuzhiyun 
349*4882a593Smuzhiyun 	if (IS_REACHABLE(CONFIG_NVRAM) && default_vmode == VMODE_NVRAM)
350*4882a593Smuzhiyun 		default_vmode = nvram_read_byte(NV_VMODE);
351*4882a593Smuzhiyun 	if (default_vmode <= 0 || default_vmode > VMODE_MAX ||
352*4882a593Smuzhiyun 	    !platinum_reg_init[default_vmode - 1]) {
353*4882a593Smuzhiyun 		default_vmode = mac_map_monitor_sense(sense);
354*4882a593Smuzhiyun 		if (!platinum_reg_init[default_vmode - 1])
355*4882a593Smuzhiyun 			default_vmode = VMODE_640_480_60;
356*4882a593Smuzhiyun 	}
357*4882a593Smuzhiyun 
358*4882a593Smuzhiyun 	if (IS_REACHABLE(CONFIG_NVRAM) && default_cmode == CMODE_NVRAM)
359*4882a593Smuzhiyun 		default_cmode = nvram_read_byte(NV_CMODE);
360*4882a593Smuzhiyun 	if (default_cmode < CMODE_8 || default_cmode > CMODE_32)
361*4882a593Smuzhiyun 		default_cmode = CMODE_8;
362*4882a593Smuzhiyun 	/*
363*4882a593Smuzhiyun 	 * Reduce the pixel size if we don't have enough VRAM.
364*4882a593Smuzhiyun 	 */
365*4882a593Smuzhiyun 	while(default_cmode > CMODE_8 &&
366*4882a593Smuzhiyun 	      platinum_vram_reqd(default_vmode, default_cmode) > pinfo->total_vram)
367*4882a593Smuzhiyun 		default_cmode--;
368*4882a593Smuzhiyun 
369*4882a593Smuzhiyun 	printk("platinumfb:  Using video mode %d and color mode %d.\n", default_vmode, default_cmode);
370*4882a593Smuzhiyun 
371*4882a593Smuzhiyun 	/* Setup default var */
372*4882a593Smuzhiyun 	if (mac_vmode_to_var(default_vmode, default_cmode, &var) < 0) {
373*4882a593Smuzhiyun 		/* This shouldn't happen! */
374*4882a593Smuzhiyun 		printk("mac_vmode_to_var(%d, %d,) failed\n", default_vmode, default_cmode);
375*4882a593Smuzhiyun try_again:
376*4882a593Smuzhiyun 		default_vmode = VMODE_640_480_60;
377*4882a593Smuzhiyun 		default_cmode = CMODE_8;
378*4882a593Smuzhiyun 		if (mac_vmode_to_var(default_vmode, default_cmode, &var) < 0) {
379*4882a593Smuzhiyun 			printk(KERN_ERR "platinumfb: mac_vmode_to_var() failed\n");
380*4882a593Smuzhiyun 			return -ENXIO;
381*4882a593Smuzhiyun 		}
382*4882a593Smuzhiyun 	}
383*4882a593Smuzhiyun 
384*4882a593Smuzhiyun 	/* Initialize info structure */
385*4882a593Smuzhiyun 	platinum_init_info(info, pinfo);
386*4882a593Smuzhiyun 
387*4882a593Smuzhiyun 	/* Apply default var */
388*4882a593Smuzhiyun 	info->var = var;
389*4882a593Smuzhiyun 	var.activate = FB_ACTIVATE_NOW;
390*4882a593Smuzhiyun 	rc = fb_set_var(info, &var);
391*4882a593Smuzhiyun 	if (rc && (default_vmode != VMODE_640_480_60 || default_cmode != CMODE_8))
392*4882a593Smuzhiyun 		goto try_again;
393*4882a593Smuzhiyun 
394*4882a593Smuzhiyun 	/* Register with fbdev layer */
395*4882a593Smuzhiyun 	rc = register_framebuffer(info);
396*4882a593Smuzhiyun 	if (rc < 0)
397*4882a593Smuzhiyun 		return rc;
398*4882a593Smuzhiyun 
399*4882a593Smuzhiyun 	fb_info(info, "Apple Platinum frame buffer device\n");
400*4882a593Smuzhiyun 
401*4882a593Smuzhiyun 	return 0;
402*4882a593Smuzhiyun }
403*4882a593Smuzhiyun 
404*4882a593Smuzhiyun /*
405*4882a593Smuzhiyun  * Get the monitor sense value.
406*4882a593Smuzhiyun  * Note that this can be called before calibrate_delay,
407*4882a593Smuzhiyun  * so we can't use udelay.
408*4882a593Smuzhiyun  */
read_platinum_sense(struct fb_info_platinum * info)409*4882a593Smuzhiyun static int read_platinum_sense(struct fb_info_platinum *info)
410*4882a593Smuzhiyun {
411*4882a593Smuzhiyun 	volatile struct platinum_regs __iomem *platinum_regs = info->platinum_regs;
412*4882a593Smuzhiyun 	int sense;
413*4882a593Smuzhiyun 
414*4882a593Smuzhiyun 	out_be32(&platinum_regs->reg[23].r, 7);	/* turn off drivers */
415*4882a593Smuzhiyun 	__delay(2000);
416*4882a593Smuzhiyun 	sense = (~in_be32(&platinum_regs->reg[23].r) & 7) << 8;
417*4882a593Smuzhiyun 
418*4882a593Smuzhiyun 	/* drive each sense line low in turn and collect the other 2 */
419*4882a593Smuzhiyun 	out_be32(&platinum_regs->reg[23].r, 3);	/* drive A low */
420*4882a593Smuzhiyun 	__delay(2000);
421*4882a593Smuzhiyun 	sense |= (~in_be32(&platinum_regs->reg[23].r) & 3) << 4;
422*4882a593Smuzhiyun 	out_be32(&platinum_regs->reg[23].r, 5);	/* drive B low */
423*4882a593Smuzhiyun 	__delay(2000);
424*4882a593Smuzhiyun 	sense |= (~in_be32(&platinum_regs->reg[23].r) & 4) << 1;
425*4882a593Smuzhiyun 	sense |= (~in_be32(&platinum_regs->reg[23].r) & 1) << 2;
426*4882a593Smuzhiyun 	out_be32(&platinum_regs->reg[23].r, 6);	/* drive C low */
427*4882a593Smuzhiyun 	__delay(2000);
428*4882a593Smuzhiyun 	sense |= (~in_be32(&platinum_regs->reg[23].r) & 6) >> 1;
429*4882a593Smuzhiyun 
430*4882a593Smuzhiyun 	out_be32(&platinum_regs->reg[23].r, 7);	/* turn off drivers */
431*4882a593Smuzhiyun 
432*4882a593Smuzhiyun 	return sense;
433*4882a593Smuzhiyun }
434*4882a593Smuzhiyun 
435*4882a593Smuzhiyun /*
436*4882a593Smuzhiyun  * This routine takes a user-supplied var, and picks the best vmode/cmode from it.
437*4882a593Smuzhiyun  * It also updates the var structure to the actual mode data obtained
438*4882a593Smuzhiyun  */
platinum_var_to_par(struct fb_var_screeninfo * var,struct fb_info_platinum * pinfo,int check_only)439*4882a593Smuzhiyun static int platinum_var_to_par(struct fb_var_screeninfo *var,
440*4882a593Smuzhiyun 			       struct fb_info_platinum *pinfo,
441*4882a593Smuzhiyun 			       int check_only)
442*4882a593Smuzhiyun {
443*4882a593Smuzhiyun 	int vmode, cmode;
444*4882a593Smuzhiyun 
445*4882a593Smuzhiyun 	if (mac_var_to_vmode(var, &vmode, &cmode) != 0) {
446*4882a593Smuzhiyun 		printk(KERN_ERR "platinum_var_to_par: mac_var_to_vmode unsuccessful.\n");
447*4882a593Smuzhiyun 		printk(KERN_ERR "platinum_var_to_par: var->xres = %d\n", var->xres);
448*4882a593Smuzhiyun 		printk(KERN_ERR "platinum_var_to_par: var->yres = %d\n", var->yres);
449*4882a593Smuzhiyun 		printk(KERN_ERR "platinum_var_to_par: var->xres_virtual = %d\n", var->xres_virtual);
450*4882a593Smuzhiyun 		printk(KERN_ERR "platinum_var_to_par: var->yres_virtual = %d\n", var->yres_virtual);
451*4882a593Smuzhiyun 		printk(KERN_ERR "platinum_var_to_par: var->bits_per_pixel = %d\n", var->bits_per_pixel);
452*4882a593Smuzhiyun 		printk(KERN_ERR "platinum_var_to_par: var->pixclock = %d\n", var->pixclock);
453*4882a593Smuzhiyun 		printk(KERN_ERR "platinum_var_to_par: var->vmode = %d\n", var->vmode);
454*4882a593Smuzhiyun 		return -EINVAL;
455*4882a593Smuzhiyun 	}
456*4882a593Smuzhiyun 
457*4882a593Smuzhiyun 	if (!platinum_reg_init[vmode-1]) {
458*4882a593Smuzhiyun 		printk(KERN_ERR "platinum_var_to_par, vmode %d not valid.\n", vmode);
459*4882a593Smuzhiyun 		return -EINVAL;
460*4882a593Smuzhiyun 	}
461*4882a593Smuzhiyun 
462*4882a593Smuzhiyun 	if (platinum_vram_reqd(vmode, cmode) > pinfo->total_vram) {
463*4882a593Smuzhiyun 		printk(KERN_ERR "platinum_var_to_par, not enough ram for vmode %d, cmode %d.\n", vmode, cmode);
464*4882a593Smuzhiyun 		return -EINVAL;
465*4882a593Smuzhiyun 	}
466*4882a593Smuzhiyun 
467*4882a593Smuzhiyun 	if (mac_vmode_to_var(vmode, cmode, var))
468*4882a593Smuzhiyun 		return -EINVAL;
469*4882a593Smuzhiyun 
470*4882a593Smuzhiyun 	if (check_only)
471*4882a593Smuzhiyun 		return 0;
472*4882a593Smuzhiyun 
473*4882a593Smuzhiyun 	pinfo->vmode = vmode;
474*4882a593Smuzhiyun 	pinfo->cmode = cmode;
475*4882a593Smuzhiyun 	pinfo->xres = vmode_attrs[vmode-1].hres;
476*4882a593Smuzhiyun 	pinfo->yres = vmode_attrs[vmode-1].vres;
477*4882a593Smuzhiyun 	pinfo->xoffset = 0;
478*4882a593Smuzhiyun 	pinfo->yoffset = 0;
479*4882a593Smuzhiyun 	pinfo->vxres = pinfo->xres;
480*4882a593Smuzhiyun 	pinfo->vyres = pinfo->yres;
481*4882a593Smuzhiyun 
482*4882a593Smuzhiyun 	return 0;
483*4882a593Smuzhiyun }
484*4882a593Smuzhiyun 
485*4882a593Smuzhiyun 
486*4882a593Smuzhiyun /*
487*4882a593Smuzhiyun  * Parse user specified options (`video=platinumfb:')
488*4882a593Smuzhiyun  */
platinumfb_setup(char * options)489*4882a593Smuzhiyun static int __init platinumfb_setup(char *options)
490*4882a593Smuzhiyun {
491*4882a593Smuzhiyun 	char *this_opt;
492*4882a593Smuzhiyun 
493*4882a593Smuzhiyun 	if (!options || !*options)
494*4882a593Smuzhiyun 		return 0;
495*4882a593Smuzhiyun 
496*4882a593Smuzhiyun 	while ((this_opt = strsep(&options, ",")) != NULL) {
497*4882a593Smuzhiyun 		if (!strncmp(this_opt, "vmode:", 6)) {
498*4882a593Smuzhiyun 	    		int vmode = simple_strtoul(this_opt+6, NULL, 0);
499*4882a593Smuzhiyun 			if (vmode > 0 && vmode <= VMODE_MAX)
500*4882a593Smuzhiyun 				default_vmode = vmode;
501*4882a593Smuzhiyun 		} else if (!strncmp(this_opt, "cmode:", 6)) {
502*4882a593Smuzhiyun 			int depth = simple_strtoul(this_opt+6, NULL, 0);
503*4882a593Smuzhiyun 			switch (depth) {
504*4882a593Smuzhiyun 			 case 0:
505*4882a593Smuzhiyun 			 case 8:
506*4882a593Smuzhiyun 			    default_cmode = CMODE_8;
507*4882a593Smuzhiyun 			    break;
508*4882a593Smuzhiyun 			 case 15:
509*4882a593Smuzhiyun 			 case 16:
510*4882a593Smuzhiyun 			    default_cmode = CMODE_16;
511*4882a593Smuzhiyun 			    break;
512*4882a593Smuzhiyun 			 case 24:
513*4882a593Smuzhiyun 			 case 32:
514*4882a593Smuzhiyun 			    default_cmode = CMODE_32;
515*4882a593Smuzhiyun 			    break;
516*4882a593Smuzhiyun 			}
517*4882a593Smuzhiyun 		}
518*4882a593Smuzhiyun 	}
519*4882a593Smuzhiyun 	return 0;
520*4882a593Smuzhiyun }
521*4882a593Smuzhiyun 
522*4882a593Smuzhiyun #ifdef __powerpc__
523*4882a593Smuzhiyun #define invalidate_cache(addr) \
524*4882a593Smuzhiyun 	asm volatile("eieio; dcbf 0,%1" \
525*4882a593Smuzhiyun 	: "=m" (*(addr)) : "r" (addr) : "memory");
526*4882a593Smuzhiyun #else
527*4882a593Smuzhiyun #define invalidate_cache(addr)
528*4882a593Smuzhiyun #endif
529*4882a593Smuzhiyun 
platinumfb_probe(struct platform_device * odev)530*4882a593Smuzhiyun static int platinumfb_probe(struct platform_device* odev)
531*4882a593Smuzhiyun {
532*4882a593Smuzhiyun 	struct device_node	*dp = odev->dev.of_node;
533*4882a593Smuzhiyun 	struct fb_info		*info;
534*4882a593Smuzhiyun 	struct fb_info_platinum	*pinfo;
535*4882a593Smuzhiyun 	volatile __u8		*fbuffer;
536*4882a593Smuzhiyun 	int			bank0, bank1, bank2, bank3, rc;
537*4882a593Smuzhiyun 
538*4882a593Smuzhiyun 	dev_info(&odev->dev, "Found Apple Platinum video hardware\n");
539*4882a593Smuzhiyun 
540*4882a593Smuzhiyun 	info = framebuffer_alloc(sizeof(*pinfo), &odev->dev);
541*4882a593Smuzhiyun 	if (!info)
542*4882a593Smuzhiyun 		return -ENOMEM;
543*4882a593Smuzhiyun 
544*4882a593Smuzhiyun 	pinfo = info->par;
545*4882a593Smuzhiyun 
546*4882a593Smuzhiyun 	if (of_address_to_resource(dp, 0, &pinfo->rsrc_reg) ||
547*4882a593Smuzhiyun 	    of_address_to_resource(dp, 1, &pinfo->rsrc_fb)) {
548*4882a593Smuzhiyun 		dev_err(&odev->dev, "Can't get resources\n");
549*4882a593Smuzhiyun 		framebuffer_release(info);
550*4882a593Smuzhiyun 		return -ENXIO;
551*4882a593Smuzhiyun 	}
552*4882a593Smuzhiyun 	dev_dbg(&odev->dev, " registers  : 0x%llx...0x%llx\n",
553*4882a593Smuzhiyun 		(unsigned long long)pinfo->rsrc_reg.start,
554*4882a593Smuzhiyun 		(unsigned long long)pinfo->rsrc_reg.end);
555*4882a593Smuzhiyun 	dev_dbg(&odev->dev, " framebuffer: 0x%llx...0x%llx\n",
556*4882a593Smuzhiyun 		(unsigned long long)pinfo->rsrc_fb.start,
557*4882a593Smuzhiyun 		(unsigned long long)pinfo->rsrc_fb.end);
558*4882a593Smuzhiyun 
559*4882a593Smuzhiyun 	/* Do not try to request register space, they overlap with the
560*4882a593Smuzhiyun 	 * northbridge and that can fail. Only request framebuffer
561*4882a593Smuzhiyun 	 */
562*4882a593Smuzhiyun 	if (!request_mem_region(pinfo->rsrc_fb.start,
563*4882a593Smuzhiyun 				resource_size(&pinfo->rsrc_fb),
564*4882a593Smuzhiyun 				"platinumfb framebuffer")) {
565*4882a593Smuzhiyun 		printk(KERN_ERR "platinumfb: Can't request framebuffer !\n");
566*4882a593Smuzhiyun 		framebuffer_release(info);
567*4882a593Smuzhiyun 		return -ENXIO;
568*4882a593Smuzhiyun 	}
569*4882a593Smuzhiyun 
570*4882a593Smuzhiyun 	/* frame buffer - map only 4MB */
571*4882a593Smuzhiyun 	pinfo->frame_buffer_phys = pinfo->rsrc_fb.start;
572*4882a593Smuzhiyun 	pinfo->frame_buffer = ioremap_wt(pinfo->rsrc_fb.start, 0x400000);
573*4882a593Smuzhiyun 	pinfo->base_frame_buffer = pinfo->frame_buffer;
574*4882a593Smuzhiyun 
575*4882a593Smuzhiyun 	/* registers */
576*4882a593Smuzhiyun 	pinfo->platinum_regs_phys = pinfo->rsrc_reg.start;
577*4882a593Smuzhiyun 	pinfo->platinum_regs = ioremap(pinfo->rsrc_reg.start, 0x1000);
578*4882a593Smuzhiyun 
579*4882a593Smuzhiyun 	pinfo->cmap_regs_phys = 0xf301b000;	/* XXX not in prom? */
580*4882a593Smuzhiyun 	request_mem_region(pinfo->cmap_regs_phys, 0x1000, "platinumfb cmap");
581*4882a593Smuzhiyun 	pinfo->cmap_regs = ioremap(pinfo->cmap_regs_phys, 0x1000);
582*4882a593Smuzhiyun 
583*4882a593Smuzhiyun 	/* Grok total video ram */
584*4882a593Smuzhiyun 	out_be32(&pinfo->platinum_regs->reg[16].r, (unsigned)pinfo->frame_buffer_phys);
585*4882a593Smuzhiyun 	out_be32(&pinfo->platinum_regs->reg[20].r, 0x1011);	/* select max vram */
586*4882a593Smuzhiyun 	out_be32(&pinfo->platinum_regs->reg[24].r, 0);	/* switch in vram */
587*4882a593Smuzhiyun 
588*4882a593Smuzhiyun 	fbuffer = pinfo->base_frame_buffer;
589*4882a593Smuzhiyun 	fbuffer[0x100000] = 0x34;
590*4882a593Smuzhiyun 	fbuffer[0x100008] = 0x0;
591*4882a593Smuzhiyun 	invalidate_cache(&fbuffer[0x100000]);
592*4882a593Smuzhiyun 	fbuffer[0x200000] = 0x56;
593*4882a593Smuzhiyun 	fbuffer[0x200008] = 0x0;
594*4882a593Smuzhiyun 	invalidate_cache(&fbuffer[0x200000]);
595*4882a593Smuzhiyun 	fbuffer[0x300000] = 0x78;
596*4882a593Smuzhiyun 	fbuffer[0x300008] = 0x0;
597*4882a593Smuzhiyun 	invalidate_cache(&fbuffer[0x300000]);
598*4882a593Smuzhiyun 	bank0 = 1; /* builtin 1MB vram, always there */
599*4882a593Smuzhiyun 	bank1 = fbuffer[0x100000] == 0x34;
600*4882a593Smuzhiyun 	bank2 = fbuffer[0x200000] == 0x56;
601*4882a593Smuzhiyun 	bank3 = fbuffer[0x300000] == 0x78;
602*4882a593Smuzhiyun 	pinfo->total_vram = (bank0 + bank1 + bank2 + bank3) * 0x100000;
603*4882a593Smuzhiyun 	printk(KERN_INFO "platinumfb: Total VRAM = %dMB (%d%d%d%d)\n",
604*4882a593Smuzhiyun 	       (unsigned int) (pinfo->total_vram / 1024 / 1024),
605*4882a593Smuzhiyun 	       bank3, bank2, bank1, bank0);
606*4882a593Smuzhiyun 
607*4882a593Smuzhiyun 	/*
608*4882a593Smuzhiyun 	 * Try to determine whether we have an old or a new DACula.
609*4882a593Smuzhiyun 	 */
610*4882a593Smuzhiyun 	out_8(&pinfo->cmap_regs->addr, 0x40);
611*4882a593Smuzhiyun 	pinfo->dactype = in_8(&pinfo->cmap_regs->d2);
612*4882a593Smuzhiyun 	switch (pinfo->dactype) {
613*4882a593Smuzhiyun 	case 0x3c:
614*4882a593Smuzhiyun 		pinfo->clktype = 1;
615*4882a593Smuzhiyun 		printk(KERN_INFO "platinumfb: DACula type 0x3c\n");
616*4882a593Smuzhiyun 		break;
617*4882a593Smuzhiyun 	case 0x84:
618*4882a593Smuzhiyun 		pinfo->clktype = 0;
619*4882a593Smuzhiyun 		printk(KERN_INFO "platinumfb: DACula type 0x84\n");
620*4882a593Smuzhiyun 		break;
621*4882a593Smuzhiyun 	default:
622*4882a593Smuzhiyun 		pinfo->clktype = 0;
623*4882a593Smuzhiyun 		printk(KERN_INFO "platinumfb: Unknown DACula type: %x\n", pinfo->dactype);
624*4882a593Smuzhiyun 		break;
625*4882a593Smuzhiyun 	}
626*4882a593Smuzhiyun 	dev_set_drvdata(&odev->dev, info);
627*4882a593Smuzhiyun 
628*4882a593Smuzhiyun 	rc = platinum_init_fb(info);
629*4882a593Smuzhiyun 	if (rc != 0) {
630*4882a593Smuzhiyun 		iounmap(pinfo->frame_buffer);
631*4882a593Smuzhiyun 		iounmap(pinfo->platinum_regs);
632*4882a593Smuzhiyun 		iounmap(pinfo->cmap_regs);
633*4882a593Smuzhiyun 		framebuffer_release(info);
634*4882a593Smuzhiyun 	}
635*4882a593Smuzhiyun 
636*4882a593Smuzhiyun 	return rc;
637*4882a593Smuzhiyun }
638*4882a593Smuzhiyun 
platinumfb_remove(struct platform_device * odev)639*4882a593Smuzhiyun static int platinumfb_remove(struct platform_device* odev)
640*4882a593Smuzhiyun {
641*4882a593Smuzhiyun 	struct fb_info		*info = dev_get_drvdata(&odev->dev);
642*4882a593Smuzhiyun 	struct fb_info_platinum	*pinfo = info->par;
643*4882a593Smuzhiyun 
644*4882a593Smuzhiyun         unregister_framebuffer (info);
645*4882a593Smuzhiyun 
646*4882a593Smuzhiyun 	/* Unmap frame buffer and registers */
647*4882a593Smuzhiyun 	iounmap(pinfo->frame_buffer);
648*4882a593Smuzhiyun 	iounmap(pinfo->platinum_regs);
649*4882a593Smuzhiyun 	iounmap(pinfo->cmap_regs);
650*4882a593Smuzhiyun 
651*4882a593Smuzhiyun 	release_mem_region(pinfo->rsrc_fb.start,
652*4882a593Smuzhiyun 			   resource_size(&pinfo->rsrc_fb));
653*4882a593Smuzhiyun 
654*4882a593Smuzhiyun 	release_mem_region(pinfo->cmap_regs_phys, 0x1000);
655*4882a593Smuzhiyun 
656*4882a593Smuzhiyun 	framebuffer_release(info);
657*4882a593Smuzhiyun 
658*4882a593Smuzhiyun 	return 0;
659*4882a593Smuzhiyun }
660*4882a593Smuzhiyun 
661*4882a593Smuzhiyun static struct of_device_id platinumfb_match[] =
662*4882a593Smuzhiyun {
663*4882a593Smuzhiyun 	{
664*4882a593Smuzhiyun 	.name 		= "platinum",
665*4882a593Smuzhiyun 	},
666*4882a593Smuzhiyun 	{},
667*4882a593Smuzhiyun };
668*4882a593Smuzhiyun 
669*4882a593Smuzhiyun static struct platform_driver platinum_driver =
670*4882a593Smuzhiyun {
671*4882a593Smuzhiyun 	.driver = {
672*4882a593Smuzhiyun 		.name = "platinumfb",
673*4882a593Smuzhiyun 		.of_match_table = platinumfb_match,
674*4882a593Smuzhiyun 	},
675*4882a593Smuzhiyun 	.probe		= platinumfb_probe,
676*4882a593Smuzhiyun 	.remove		= platinumfb_remove,
677*4882a593Smuzhiyun };
678*4882a593Smuzhiyun 
platinumfb_init(void)679*4882a593Smuzhiyun static int __init platinumfb_init(void)
680*4882a593Smuzhiyun {
681*4882a593Smuzhiyun #ifndef MODULE
682*4882a593Smuzhiyun 	char *option = NULL;
683*4882a593Smuzhiyun 
684*4882a593Smuzhiyun 	if (fb_get_options("platinumfb", &option))
685*4882a593Smuzhiyun 		return -ENODEV;
686*4882a593Smuzhiyun 	platinumfb_setup(option);
687*4882a593Smuzhiyun #endif
688*4882a593Smuzhiyun 	platform_driver_register(&platinum_driver);
689*4882a593Smuzhiyun 
690*4882a593Smuzhiyun 	return 0;
691*4882a593Smuzhiyun }
692*4882a593Smuzhiyun 
platinumfb_exit(void)693*4882a593Smuzhiyun static void __exit platinumfb_exit(void)
694*4882a593Smuzhiyun {
695*4882a593Smuzhiyun 	platform_driver_unregister(&platinum_driver);
696*4882a593Smuzhiyun }
697*4882a593Smuzhiyun 
698*4882a593Smuzhiyun MODULE_LICENSE("GPL");
699*4882a593Smuzhiyun MODULE_DESCRIPTION("framebuffer driver for Apple Platinum video");
700*4882a593Smuzhiyun module_init(platinumfb_init);
701*4882a593Smuzhiyun 
702*4882a593Smuzhiyun #ifdef MODULE
703*4882a593Smuzhiyun module_exit(platinumfb_exit);
704*4882a593Smuzhiyun #endif
705