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