1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * controlfb.c -- frame buffer device for the PowerMac 'control' display
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * Created 12 July 1998 by Dan Jacobowitz <dan@debian.org>
5*4882a593Smuzhiyun * Copyright (C) 1998 Dan Jacobowitz
6*4882a593Smuzhiyun * Copyright (C) 2001 Takashi Oe
7*4882a593Smuzhiyun *
8*4882a593Smuzhiyun * Mmap code by Michel Lanners <mlan@cpu.lu>
9*4882a593Smuzhiyun *
10*4882a593Smuzhiyun * Frame buffer structure from:
11*4882a593Smuzhiyun * drivers/video/chipsfb.c -- frame buffer device for
12*4882a593Smuzhiyun * Chips & Technologies 65550 chip.
13*4882a593Smuzhiyun *
14*4882a593Smuzhiyun * Copyright (C) 1998 Paul Mackerras
15*4882a593Smuzhiyun *
16*4882a593Smuzhiyun * This file is derived from the Powermac "chips" driver:
17*4882a593Smuzhiyun * Copyright (C) 1997 Fabio Riccardi.
18*4882a593Smuzhiyun * And from the frame buffer device for Open Firmware-initialized devices:
19*4882a593Smuzhiyun * Copyright (C) 1997 Geert Uytterhoeven.
20*4882a593Smuzhiyun *
21*4882a593Smuzhiyun * Hardware information from:
22*4882a593Smuzhiyun * control.c: Console support for PowerMac "control" display adaptor.
23*4882a593Smuzhiyun * Copyright (C) 1996 Paul Mackerras
24*4882a593Smuzhiyun *
25*4882a593Smuzhiyun * Updated to 2.5 framebuffer API by Ben Herrenschmidt
26*4882a593Smuzhiyun * <benh@kernel.crashing.org>, Paul Mackerras <paulus@samba.org>,
27*4882a593Smuzhiyun * and James Simmons <jsimmons@infradead.org>.
28*4882a593Smuzhiyun *
29*4882a593Smuzhiyun * This file is subject to the terms and conditions of the GNU General Public
30*4882a593Smuzhiyun * License. See the file COPYING in the main directory of this archive for
31*4882a593Smuzhiyun * more details.
32*4882a593Smuzhiyun */
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun #include <linux/kernel.h>
35*4882a593Smuzhiyun #include <linux/errno.h>
36*4882a593Smuzhiyun #include <linux/string.h>
37*4882a593Smuzhiyun #include <linux/mm.h>
38*4882a593Smuzhiyun #include <linux/slab.h>
39*4882a593Smuzhiyun #include <linux/vmalloc.h>
40*4882a593Smuzhiyun #include <linux/delay.h>
41*4882a593Smuzhiyun #include <linux/interrupt.h>
42*4882a593Smuzhiyun #include <linux/of.h>
43*4882a593Smuzhiyun #include <linux/of_address.h>
44*4882a593Smuzhiyun #include <linux/fb.h>
45*4882a593Smuzhiyun #include <linux/init.h>
46*4882a593Smuzhiyun #include <linux/pci.h>
47*4882a593Smuzhiyun #include <linux/nvram.h>
48*4882a593Smuzhiyun #include <linux/adb.h>
49*4882a593Smuzhiyun #include <linux/cuda.h>
50*4882a593Smuzhiyun #ifdef CONFIG_PPC_PMAC
51*4882a593Smuzhiyun #include <asm/prom.h>
52*4882a593Smuzhiyun #endif
53*4882a593Smuzhiyun #ifdef CONFIG_BOOTX_TEXT
54*4882a593Smuzhiyun #include <asm/btext.h>
55*4882a593Smuzhiyun #endif
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun #include "macmodes.h"
58*4882a593Smuzhiyun #include "controlfb.h"
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun #if !defined(CONFIG_PPC_PMAC) || !defined(CONFIG_PPC32)
61*4882a593Smuzhiyun #define invalid_vram_cache(addr)
62*4882a593Smuzhiyun #undef in_8
63*4882a593Smuzhiyun #undef out_8
64*4882a593Smuzhiyun #undef in_le32
65*4882a593Smuzhiyun #undef out_le32
66*4882a593Smuzhiyun #define in_8(addr) 0
67*4882a593Smuzhiyun #define out_8(addr, val) (void)(val)
68*4882a593Smuzhiyun #define in_le32(addr) 0
69*4882a593Smuzhiyun #define out_le32(addr, val) (void)(val)
70*4882a593Smuzhiyun #ifndef pgprot_cached_wthru
71*4882a593Smuzhiyun #define pgprot_cached_wthru(prot) (prot)
72*4882a593Smuzhiyun #endif
73*4882a593Smuzhiyun #else
invalid_vram_cache(void __force * addr)74*4882a593Smuzhiyun static void invalid_vram_cache(void __force *addr)
75*4882a593Smuzhiyun {
76*4882a593Smuzhiyun eieio();
77*4882a593Smuzhiyun dcbf(addr);
78*4882a593Smuzhiyun mb();
79*4882a593Smuzhiyun eieio();
80*4882a593Smuzhiyun dcbf(addr);
81*4882a593Smuzhiyun mb();
82*4882a593Smuzhiyun }
83*4882a593Smuzhiyun #endif
84*4882a593Smuzhiyun
85*4882a593Smuzhiyun struct fb_par_control {
86*4882a593Smuzhiyun int vmode, cmode;
87*4882a593Smuzhiyun int xres, yres;
88*4882a593Smuzhiyun int vxres, vyres;
89*4882a593Smuzhiyun int xoffset, yoffset;
90*4882a593Smuzhiyun int pitch;
91*4882a593Smuzhiyun struct control_regvals regvals;
92*4882a593Smuzhiyun unsigned long sync;
93*4882a593Smuzhiyun unsigned char ctrl;
94*4882a593Smuzhiyun };
95*4882a593Smuzhiyun
96*4882a593Smuzhiyun #define DIRTY(z) ((x)->z != (y)->z)
97*4882a593Smuzhiyun #define DIRTY_CMAP(z) (memcmp(&((x)->z), &((y)->z), sizeof((y)->z)))
PAR_EQUAL(struct fb_par_control * x,struct fb_par_control * y)98*4882a593Smuzhiyun static inline int PAR_EQUAL(struct fb_par_control *x, struct fb_par_control *y)
99*4882a593Smuzhiyun {
100*4882a593Smuzhiyun int i, results;
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun results = 1;
103*4882a593Smuzhiyun for (i = 0; i < 3; i++)
104*4882a593Smuzhiyun results &= !DIRTY(regvals.clock_params[i]);
105*4882a593Smuzhiyun if (!results)
106*4882a593Smuzhiyun return 0;
107*4882a593Smuzhiyun for (i = 0; i < 16; i++)
108*4882a593Smuzhiyun results &= !DIRTY(regvals.regs[i]);
109*4882a593Smuzhiyun if (!results)
110*4882a593Smuzhiyun return 0;
111*4882a593Smuzhiyun return (!DIRTY(cmode) && !DIRTY(xres) && !DIRTY(yres)
112*4882a593Smuzhiyun && !DIRTY(vxres) && !DIRTY(vyres));
113*4882a593Smuzhiyun }
VAR_MATCH(struct fb_var_screeninfo * x,struct fb_var_screeninfo * y)114*4882a593Smuzhiyun static inline int VAR_MATCH(struct fb_var_screeninfo *x, struct fb_var_screeninfo *y)
115*4882a593Smuzhiyun {
116*4882a593Smuzhiyun return (!DIRTY(bits_per_pixel) && !DIRTY(xres)
117*4882a593Smuzhiyun && !DIRTY(yres) && !DIRTY(xres_virtual)
118*4882a593Smuzhiyun && !DIRTY(yres_virtual)
119*4882a593Smuzhiyun && !DIRTY_CMAP(red) && !DIRTY_CMAP(green) && !DIRTY_CMAP(blue));
120*4882a593Smuzhiyun }
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun struct fb_info_control {
123*4882a593Smuzhiyun struct fb_info info;
124*4882a593Smuzhiyun struct fb_par_control par;
125*4882a593Smuzhiyun u32 pseudo_palette[16];
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun struct cmap_regs __iomem *cmap_regs;
128*4882a593Smuzhiyun unsigned long cmap_regs_phys;
129*4882a593Smuzhiyun
130*4882a593Smuzhiyun struct control_regs __iomem *control_regs;
131*4882a593Smuzhiyun unsigned long control_regs_phys;
132*4882a593Smuzhiyun unsigned long control_regs_size;
133*4882a593Smuzhiyun
134*4882a593Smuzhiyun __u8 __iomem *frame_buffer;
135*4882a593Smuzhiyun unsigned long frame_buffer_phys;
136*4882a593Smuzhiyun unsigned long fb_orig_base;
137*4882a593Smuzhiyun unsigned long fb_orig_size;
138*4882a593Smuzhiyun
139*4882a593Smuzhiyun int control_use_bank2;
140*4882a593Smuzhiyun unsigned long total_vram;
141*4882a593Smuzhiyun unsigned char vram_attr;
142*4882a593Smuzhiyun };
143*4882a593Smuzhiyun
144*4882a593Smuzhiyun /* control register access macro */
145*4882a593Smuzhiyun #define CNTRL_REG(INFO,REG) (&(((INFO)->control_regs->REG).r))
146*4882a593Smuzhiyun
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun /************************** Internal variables *******************************/
149*4882a593Smuzhiyun
150*4882a593Smuzhiyun static struct fb_info_control *control_fb;
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun static int default_vmode __initdata = VMODE_NVRAM;
153*4882a593Smuzhiyun static int default_cmode __initdata = CMODE_NVRAM;
154*4882a593Smuzhiyun
155*4882a593Smuzhiyun
controlfb_setcolreg(u_int regno,u_int red,u_int green,u_int blue,u_int transp,struct fb_info * info)156*4882a593Smuzhiyun static int controlfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
157*4882a593Smuzhiyun u_int transp, struct fb_info *info)
158*4882a593Smuzhiyun {
159*4882a593Smuzhiyun struct fb_info_control *p =
160*4882a593Smuzhiyun container_of(info, struct fb_info_control, info);
161*4882a593Smuzhiyun __u8 r, g, b;
162*4882a593Smuzhiyun
163*4882a593Smuzhiyun if (regno > 255)
164*4882a593Smuzhiyun return 1;
165*4882a593Smuzhiyun
166*4882a593Smuzhiyun r = red >> 8;
167*4882a593Smuzhiyun g = green >> 8;
168*4882a593Smuzhiyun b = blue >> 8;
169*4882a593Smuzhiyun
170*4882a593Smuzhiyun out_8(&p->cmap_regs->addr, regno); /* tell clut what addr to fill */
171*4882a593Smuzhiyun out_8(&p->cmap_regs->lut, r); /* send one color channel at */
172*4882a593Smuzhiyun out_8(&p->cmap_regs->lut, g); /* a time... */
173*4882a593Smuzhiyun out_8(&p->cmap_regs->lut, b);
174*4882a593Smuzhiyun
175*4882a593Smuzhiyun if (regno < 16) {
176*4882a593Smuzhiyun int i;
177*4882a593Smuzhiyun switch (p->par.cmode) {
178*4882a593Smuzhiyun case CMODE_16:
179*4882a593Smuzhiyun p->pseudo_palette[regno] =
180*4882a593Smuzhiyun (regno << 10) | (regno << 5) | regno;
181*4882a593Smuzhiyun break;
182*4882a593Smuzhiyun case CMODE_32:
183*4882a593Smuzhiyun i = (regno << 8) | regno;
184*4882a593Smuzhiyun p->pseudo_palette[regno] = (i << 16) | i;
185*4882a593Smuzhiyun break;
186*4882a593Smuzhiyun }
187*4882a593Smuzhiyun }
188*4882a593Smuzhiyun
189*4882a593Smuzhiyun return 0;
190*4882a593Smuzhiyun }
191*4882a593Smuzhiyun
192*4882a593Smuzhiyun
193*4882a593Smuzhiyun /******************** End of controlfb_ops implementation ******************/
194*4882a593Smuzhiyun
195*4882a593Smuzhiyun
196*4882a593Smuzhiyun
set_control_clock(unsigned char * params)197*4882a593Smuzhiyun static void set_control_clock(unsigned char *params)
198*4882a593Smuzhiyun {
199*4882a593Smuzhiyun #ifdef CONFIG_ADB_CUDA
200*4882a593Smuzhiyun struct adb_request req;
201*4882a593Smuzhiyun int i;
202*4882a593Smuzhiyun
203*4882a593Smuzhiyun for (i = 0; i < 3; ++i) {
204*4882a593Smuzhiyun cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC,
205*4882a593Smuzhiyun 0x50, i + 1, params[i]);
206*4882a593Smuzhiyun while (!req.complete)
207*4882a593Smuzhiyun cuda_poll();
208*4882a593Smuzhiyun }
209*4882a593Smuzhiyun #endif
210*4882a593Smuzhiyun }
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun /*
213*4882a593Smuzhiyun * Set screen start address according to var offset values
214*4882a593Smuzhiyun */
set_screen_start(int xoffset,int yoffset,struct fb_info_control * p)215*4882a593Smuzhiyun static inline void set_screen_start(int xoffset, int yoffset,
216*4882a593Smuzhiyun struct fb_info_control *p)
217*4882a593Smuzhiyun {
218*4882a593Smuzhiyun struct fb_par_control *par = &p->par;
219*4882a593Smuzhiyun
220*4882a593Smuzhiyun par->xoffset = xoffset;
221*4882a593Smuzhiyun par->yoffset = yoffset;
222*4882a593Smuzhiyun out_le32(CNTRL_REG(p,start_addr),
223*4882a593Smuzhiyun par->yoffset * par->pitch + (par->xoffset << par->cmode));
224*4882a593Smuzhiyun }
225*4882a593Smuzhiyun
226*4882a593Smuzhiyun #define RADACAL_WRITE(a,d) \
227*4882a593Smuzhiyun out_8(&p->cmap_regs->addr, (a)); \
228*4882a593Smuzhiyun out_8(&p->cmap_regs->dat, (d))
229*4882a593Smuzhiyun
230*4882a593Smuzhiyun /* Now how about actually saying, Make it so! */
231*4882a593Smuzhiyun /* Some things in here probably don't need to be done each time. */
control_set_hardware(struct fb_info_control * p,struct fb_par_control * par)232*4882a593Smuzhiyun static void control_set_hardware(struct fb_info_control *p, struct fb_par_control *par)
233*4882a593Smuzhiyun {
234*4882a593Smuzhiyun struct control_regvals *r;
235*4882a593Smuzhiyun volatile struct preg __iomem *rp;
236*4882a593Smuzhiyun int i, cmode;
237*4882a593Smuzhiyun
238*4882a593Smuzhiyun if (PAR_EQUAL(&p->par, par)) {
239*4882a593Smuzhiyun /*
240*4882a593Smuzhiyun * check if only xoffset or yoffset differs.
241*4882a593Smuzhiyun * this prevents flickers in typical VT switch case.
242*4882a593Smuzhiyun */
243*4882a593Smuzhiyun if (p->par.xoffset != par->xoffset ||
244*4882a593Smuzhiyun p->par.yoffset != par->yoffset)
245*4882a593Smuzhiyun set_screen_start(par->xoffset, par->yoffset, p);
246*4882a593Smuzhiyun
247*4882a593Smuzhiyun return;
248*4882a593Smuzhiyun }
249*4882a593Smuzhiyun
250*4882a593Smuzhiyun p->par = *par;
251*4882a593Smuzhiyun cmode = p->par.cmode;
252*4882a593Smuzhiyun r = &par->regvals;
253*4882a593Smuzhiyun
254*4882a593Smuzhiyun /* Turn off display */
255*4882a593Smuzhiyun out_le32(CNTRL_REG(p,ctrl), 0x400 | par->ctrl);
256*4882a593Smuzhiyun
257*4882a593Smuzhiyun set_control_clock(r->clock_params);
258*4882a593Smuzhiyun
259*4882a593Smuzhiyun RADACAL_WRITE(0x20, r->radacal_ctrl);
260*4882a593Smuzhiyun RADACAL_WRITE(0x21, p->control_use_bank2 ? 0 : 1);
261*4882a593Smuzhiyun RADACAL_WRITE(0x10, 0);
262*4882a593Smuzhiyun RADACAL_WRITE(0x11, 0);
263*4882a593Smuzhiyun
264*4882a593Smuzhiyun rp = &p->control_regs->vswin;
265*4882a593Smuzhiyun for (i = 0; i < 16; ++i, ++rp)
266*4882a593Smuzhiyun out_le32(&rp->r, r->regs[i]);
267*4882a593Smuzhiyun
268*4882a593Smuzhiyun out_le32(CNTRL_REG(p,pitch), par->pitch);
269*4882a593Smuzhiyun out_le32(CNTRL_REG(p,mode), r->mode);
270*4882a593Smuzhiyun out_le32(CNTRL_REG(p,vram_attr), p->vram_attr);
271*4882a593Smuzhiyun out_le32(CNTRL_REG(p,start_addr), par->yoffset * par->pitch
272*4882a593Smuzhiyun + (par->xoffset << cmode));
273*4882a593Smuzhiyun out_le32(CNTRL_REG(p,rfrcnt), 0x1e5);
274*4882a593Smuzhiyun out_le32(CNTRL_REG(p,intr_ena), 0);
275*4882a593Smuzhiyun
276*4882a593Smuzhiyun /* Turn on display */
277*4882a593Smuzhiyun out_le32(CNTRL_REG(p,ctrl), par->ctrl);
278*4882a593Smuzhiyun
279*4882a593Smuzhiyun #ifdef CONFIG_BOOTX_TEXT
280*4882a593Smuzhiyun btext_update_display(p->frame_buffer_phys + CTRLFB_OFF,
281*4882a593Smuzhiyun p->par.xres, p->par.yres,
282*4882a593Smuzhiyun (cmode == CMODE_32? 32: cmode == CMODE_16? 16: 8),
283*4882a593Smuzhiyun p->par.pitch);
284*4882a593Smuzhiyun #endif /* CONFIG_BOOTX_TEXT */
285*4882a593Smuzhiyun }
286*4882a593Smuzhiyun
287*4882a593Smuzhiyun /* Work out which banks of VRAM we have installed. */
288*4882a593Smuzhiyun /* danj: I guess the card just ignores writes to nonexistant VRAM... */
289*4882a593Smuzhiyun
find_vram_size(struct fb_info_control * p)290*4882a593Smuzhiyun static void __init find_vram_size(struct fb_info_control *p)
291*4882a593Smuzhiyun {
292*4882a593Smuzhiyun int bank1, bank2;
293*4882a593Smuzhiyun
294*4882a593Smuzhiyun /*
295*4882a593Smuzhiyun * Set VRAM in 2MB (bank 1) mode
296*4882a593Smuzhiyun * VRAM Bank 2 will be accessible through offset 0x600000 if present
297*4882a593Smuzhiyun * and VRAM Bank 1 will not respond at that offset even if present
298*4882a593Smuzhiyun */
299*4882a593Smuzhiyun out_le32(CNTRL_REG(p,vram_attr), 0x31);
300*4882a593Smuzhiyun
301*4882a593Smuzhiyun out_8(&p->frame_buffer[0x600000], 0xb3);
302*4882a593Smuzhiyun out_8(&p->frame_buffer[0x600001], 0x71);
303*4882a593Smuzhiyun invalid_vram_cache(&p->frame_buffer[0x600000]);
304*4882a593Smuzhiyun
305*4882a593Smuzhiyun bank2 = (in_8(&p->frame_buffer[0x600000]) == 0xb3)
306*4882a593Smuzhiyun && (in_8(&p->frame_buffer[0x600001]) == 0x71);
307*4882a593Smuzhiyun
308*4882a593Smuzhiyun /*
309*4882a593Smuzhiyun * Set VRAM in 2MB (bank 2) mode
310*4882a593Smuzhiyun * VRAM Bank 1 will be accessible through offset 0x000000 if present
311*4882a593Smuzhiyun * and VRAM Bank 2 will not respond at that offset even if present
312*4882a593Smuzhiyun */
313*4882a593Smuzhiyun out_le32(CNTRL_REG(p,vram_attr), 0x39);
314*4882a593Smuzhiyun
315*4882a593Smuzhiyun out_8(&p->frame_buffer[0], 0x5a);
316*4882a593Smuzhiyun out_8(&p->frame_buffer[1], 0xc7);
317*4882a593Smuzhiyun invalid_vram_cache(&p->frame_buffer[0]);
318*4882a593Smuzhiyun
319*4882a593Smuzhiyun bank1 = (in_8(&p->frame_buffer[0]) == 0x5a)
320*4882a593Smuzhiyun && (in_8(&p->frame_buffer[1]) == 0xc7);
321*4882a593Smuzhiyun
322*4882a593Smuzhiyun if (bank2) {
323*4882a593Smuzhiyun if (!bank1) {
324*4882a593Smuzhiyun /*
325*4882a593Smuzhiyun * vram bank 2 only
326*4882a593Smuzhiyun */
327*4882a593Smuzhiyun p->control_use_bank2 = 1;
328*4882a593Smuzhiyun p->vram_attr = 0x39;
329*4882a593Smuzhiyun p->frame_buffer += 0x600000;
330*4882a593Smuzhiyun p->frame_buffer_phys += 0x600000;
331*4882a593Smuzhiyun } else {
332*4882a593Smuzhiyun /*
333*4882a593Smuzhiyun * 4 MB vram
334*4882a593Smuzhiyun */
335*4882a593Smuzhiyun p->vram_attr = 0x51;
336*4882a593Smuzhiyun }
337*4882a593Smuzhiyun } else {
338*4882a593Smuzhiyun /*
339*4882a593Smuzhiyun * vram bank 1 only
340*4882a593Smuzhiyun */
341*4882a593Smuzhiyun p->vram_attr = 0x31;
342*4882a593Smuzhiyun }
343*4882a593Smuzhiyun
344*4882a593Smuzhiyun p->total_vram = (bank1 + bank2) * 0x200000;
345*4882a593Smuzhiyun
346*4882a593Smuzhiyun printk(KERN_INFO "controlfb: VRAM Total = %dMB "
347*4882a593Smuzhiyun "(%dMB @ bank 1, %dMB @ bank 2)\n",
348*4882a593Smuzhiyun (bank1 + bank2) << 1, bank1 << 1, bank2 << 1);
349*4882a593Smuzhiyun }
350*4882a593Smuzhiyun
351*4882a593Smuzhiyun /*
352*4882a593Smuzhiyun * Get the monitor sense value.
353*4882a593Smuzhiyun * Note that this can be called before calibrate_delay,
354*4882a593Smuzhiyun * so we can't use udelay.
355*4882a593Smuzhiyun */
read_control_sense(struct fb_info_control * p)356*4882a593Smuzhiyun static int read_control_sense(struct fb_info_control *p)
357*4882a593Smuzhiyun {
358*4882a593Smuzhiyun int sense;
359*4882a593Smuzhiyun
360*4882a593Smuzhiyun out_le32(CNTRL_REG(p,mon_sense), 7); /* drive all lines high */
361*4882a593Smuzhiyun __delay(200);
362*4882a593Smuzhiyun out_le32(CNTRL_REG(p,mon_sense), 077); /* turn off drivers */
363*4882a593Smuzhiyun __delay(2000);
364*4882a593Smuzhiyun sense = (in_le32(CNTRL_REG(p,mon_sense)) & 0x1c0) << 2;
365*4882a593Smuzhiyun
366*4882a593Smuzhiyun /* drive each sense line low in turn and collect the other 2 */
367*4882a593Smuzhiyun out_le32(CNTRL_REG(p,mon_sense), 033); /* drive A low */
368*4882a593Smuzhiyun __delay(2000);
369*4882a593Smuzhiyun sense |= (in_le32(CNTRL_REG(p,mon_sense)) & 0xc0) >> 2;
370*4882a593Smuzhiyun out_le32(CNTRL_REG(p,mon_sense), 055); /* drive B low */
371*4882a593Smuzhiyun __delay(2000);
372*4882a593Smuzhiyun sense |= ((in_le32(CNTRL_REG(p,mon_sense)) & 0x100) >> 5)
373*4882a593Smuzhiyun | ((in_le32(CNTRL_REG(p,mon_sense)) & 0x40) >> 4);
374*4882a593Smuzhiyun out_le32(CNTRL_REG(p,mon_sense), 066); /* drive C low */
375*4882a593Smuzhiyun __delay(2000);
376*4882a593Smuzhiyun sense |= (in_le32(CNTRL_REG(p,mon_sense)) & 0x180) >> 7;
377*4882a593Smuzhiyun
378*4882a593Smuzhiyun out_le32(CNTRL_REG(p,mon_sense), 077); /* turn off drivers */
379*4882a593Smuzhiyun
380*4882a593Smuzhiyun return sense;
381*4882a593Smuzhiyun }
382*4882a593Smuzhiyun
383*4882a593Smuzhiyun /********************** Various translation functions **********************/
384*4882a593Smuzhiyun
385*4882a593Smuzhiyun #define CONTROL_PIXCLOCK_BASE 256016
386*4882a593Smuzhiyun #define CONTROL_PIXCLOCK_MIN 5000 /* ~ 200 MHz dot clock */
387*4882a593Smuzhiyun
388*4882a593Smuzhiyun /*
389*4882a593Smuzhiyun * calculate the clock paramaters to be sent to CUDA according to given
390*4882a593Smuzhiyun * pixclock in pico second.
391*4882a593Smuzhiyun */
calc_clock_params(unsigned long clk,unsigned char * param)392*4882a593Smuzhiyun static int calc_clock_params(unsigned long clk, unsigned char *param)
393*4882a593Smuzhiyun {
394*4882a593Smuzhiyun unsigned long p0, p1, p2, k, l, m, n, min;
395*4882a593Smuzhiyun
396*4882a593Smuzhiyun if (clk > (CONTROL_PIXCLOCK_BASE << 3))
397*4882a593Smuzhiyun return 1;
398*4882a593Smuzhiyun
399*4882a593Smuzhiyun p2 = ((clk << 4) < CONTROL_PIXCLOCK_BASE)? 3: 2;
400*4882a593Smuzhiyun l = clk << p2;
401*4882a593Smuzhiyun p0 = 0;
402*4882a593Smuzhiyun p1 = 0;
403*4882a593Smuzhiyun for (k = 1, min = l; k < 32; k++) {
404*4882a593Smuzhiyun unsigned long rem;
405*4882a593Smuzhiyun
406*4882a593Smuzhiyun m = CONTROL_PIXCLOCK_BASE * k;
407*4882a593Smuzhiyun n = m / l;
408*4882a593Smuzhiyun rem = m % l;
409*4882a593Smuzhiyun if (n && (n < 128) && rem < min) {
410*4882a593Smuzhiyun p0 = k;
411*4882a593Smuzhiyun p1 = n;
412*4882a593Smuzhiyun min = rem;
413*4882a593Smuzhiyun }
414*4882a593Smuzhiyun }
415*4882a593Smuzhiyun if (!p0 || !p1)
416*4882a593Smuzhiyun return 1;
417*4882a593Smuzhiyun
418*4882a593Smuzhiyun param[0] = p0;
419*4882a593Smuzhiyun param[1] = p1;
420*4882a593Smuzhiyun param[2] = p2;
421*4882a593Smuzhiyun
422*4882a593Smuzhiyun return 0;
423*4882a593Smuzhiyun }
424*4882a593Smuzhiyun
425*4882a593Smuzhiyun
426*4882a593Smuzhiyun /*
427*4882a593Smuzhiyun * This routine takes a user-supplied var, and picks the best vmode/cmode
428*4882a593Smuzhiyun * from it.
429*4882a593Smuzhiyun */
430*4882a593Smuzhiyun
control_var_to_par(struct fb_var_screeninfo * var,struct fb_par_control * par,const struct fb_info * fb_info)431*4882a593Smuzhiyun static int control_var_to_par(struct fb_var_screeninfo *var,
432*4882a593Smuzhiyun struct fb_par_control *par, const struct fb_info *fb_info)
433*4882a593Smuzhiyun {
434*4882a593Smuzhiyun int cmode, piped_diff, hstep;
435*4882a593Smuzhiyun unsigned hperiod, hssync, hsblank, hesync, heblank, piped, heq, hlfln,
436*4882a593Smuzhiyun hserr, vperiod, vssync, vesync, veblank, vsblank, vswin, vewin;
437*4882a593Smuzhiyun unsigned long pixclock;
438*4882a593Smuzhiyun struct fb_info_control *p =
439*4882a593Smuzhiyun container_of(fb_info, struct fb_info_control, info);
440*4882a593Smuzhiyun struct control_regvals *r = &par->regvals;
441*4882a593Smuzhiyun
442*4882a593Smuzhiyun switch (var->bits_per_pixel) {
443*4882a593Smuzhiyun case 8:
444*4882a593Smuzhiyun par->cmode = CMODE_8;
445*4882a593Smuzhiyun if (p->total_vram > 0x200000) {
446*4882a593Smuzhiyun r->mode = 3;
447*4882a593Smuzhiyun r->radacal_ctrl = 0x20;
448*4882a593Smuzhiyun piped_diff = 13;
449*4882a593Smuzhiyun } else {
450*4882a593Smuzhiyun r->mode = 2;
451*4882a593Smuzhiyun r->radacal_ctrl = 0x10;
452*4882a593Smuzhiyun piped_diff = 9;
453*4882a593Smuzhiyun }
454*4882a593Smuzhiyun break;
455*4882a593Smuzhiyun case 15:
456*4882a593Smuzhiyun case 16:
457*4882a593Smuzhiyun par->cmode = CMODE_16;
458*4882a593Smuzhiyun if (p->total_vram > 0x200000) {
459*4882a593Smuzhiyun r->mode = 2;
460*4882a593Smuzhiyun r->radacal_ctrl = 0x24;
461*4882a593Smuzhiyun piped_diff = 5;
462*4882a593Smuzhiyun } else {
463*4882a593Smuzhiyun r->mode = 1;
464*4882a593Smuzhiyun r->radacal_ctrl = 0x14;
465*4882a593Smuzhiyun piped_diff = 3;
466*4882a593Smuzhiyun }
467*4882a593Smuzhiyun break;
468*4882a593Smuzhiyun case 32:
469*4882a593Smuzhiyun par->cmode = CMODE_32;
470*4882a593Smuzhiyun if (p->total_vram > 0x200000) {
471*4882a593Smuzhiyun r->mode = 1;
472*4882a593Smuzhiyun r->radacal_ctrl = 0x28;
473*4882a593Smuzhiyun } else {
474*4882a593Smuzhiyun r->mode = 0;
475*4882a593Smuzhiyun r->radacal_ctrl = 0x18;
476*4882a593Smuzhiyun }
477*4882a593Smuzhiyun piped_diff = 1;
478*4882a593Smuzhiyun break;
479*4882a593Smuzhiyun default:
480*4882a593Smuzhiyun return -EINVAL;
481*4882a593Smuzhiyun }
482*4882a593Smuzhiyun
483*4882a593Smuzhiyun /*
484*4882a593Smuzhiyun * adjust xres and vxres so that the corresponding memory widths are
485*4882a593Smuzhiyun * 32-byte aligned
486*4882a593Smuzhiyun */
487*4882a593Smuzhiyun hstep = 31 >> par->cmode;
488*4882a593Smuzhiyun par->xres = (var->xres + hstep) & ~hstep;
489*4882a593Smuzhiyun par->vxres = (var->xres_virtual + hstep) & ~hstep;
490*4882a593Smuzhiyun par->xoffset = (var->xoffset + hstep) & ~hstep;
491*4882a593Smuzhiyun if (par->vxres < par->xres)
492*4882a593Smuzhiyun par->vxres = par->xres;
493*4882a593Smuzhiyun par->pitch = par->vxres << par->cmode;
494*4882a593Smuzhiyun
495*4882a593Smuzhiyun par->yres = var->yres;
496*4882a593Smuzhiyun par->vyres = var->yres_virtual;
497*4882a593Smuzhiyun par->yoffset = var->yoffset;
498*4882a593Smuzhiyun if (par->vyres < par->yres)
499*4882a593Smuzhiyun par->vyres = par->yres;
500*4882a593Smuzhiyun
501*4882a593Smuzhiyun par->sync = var->sync;
502*4882a593Smuzhiyun
503*4882a593Smuzhiyun if (par->pitch * par->vyres + CTRLFB_OFF > p->total_vram)
504*4882a593Smuzhiyun return -EINVAL;
505*4882a593Smuzhiyun
506*4882a593Smuzhiyun if (par->xoffset + par->xres > par->vxres)
507*4882a593Smuzhiyun par->xoffset = par->vxres - par->xres;
508*4882a593Smuzhiyun if (par->yoffset + par->yres > par->vyres)
509*4882a593Smuzhiyun par->yoffset = par->vyres - par->yres;
510*4882a593Smuzhiyun
511*4882a593Smuzhiyun pixclock = (var->pixclock < CONTROL_PIXCLOCK_MIN)? CONTROL_PIXCLOCK_MIN:
512*4882a593Smuzhiyun var->pixclock;
513*4882a593Smuzhiyun if (calc_clock_params(pixclock, r->clock_params))
514*4882a593Smuzhiyun return -EINVAL;
515*4882a593Smuzhiyun
516*4882a593Smuzhiyun hperiod = ((var->left_margin + par->xres + var->right_margin
517*4882a593Smuzhiyun + var->hsync_len) >> 1) - 2;
518*4882a593Smuzhiyun hssync = hperiod + 1;
519*4882a593Smuzhiyun hsblank = hssync - (var->right_margin >> 1);
520*4882a593Smuzhiyun hesync = (var->hsync_len >> 1) - 1;
521*4882a593Smuzhiyun heblank = (var->left_margin >> 1) + hesync;
522*4882a593Smuzhiyun piped = heblank - piped_diff;
523*4882a593Smuzhiyun heq = var->hsync_len >> 2;
524*4882a593Smuzhiyun hlfln = (hperiod+2) >> 1;
525*4882a593Smuzhiyun hserr = hssync-hesync;
526*4882a593Smuzhiyun vperiod = (var->vsync_len + var->lower_margin + par->yres
527*4882a593Smuzhiyun + var->upper_margin) << 1;
528*4882a593Smuzhiyun vssync = vperiod - 2;
529*4882a593Smuzhiyun vesync = (var->vsync_len << 1) - vperiod + vssync;
530*4882a593Smuzhiyun veblank = (var->upper_margin << 1) + vesync;
531*4882a593Smuzhiyun vsblank = vssync - (var->lower_margin << 1);
532*4882a593Smuzhiyun vswin = (vsblank+vssync) >> 1;
533*4882a593Smuzhiyun vewin = (vesync+veblank) >> 1;
534*4882a593Smuzhiyun
535*4882a593Smuzhiyun r->regs[0] = vswin;
536*4882a593Smuzhiyun r->regs[1] = vsblank;
537*4882a593Smuzhiyun r->regs[2] = veblank;
538*4882a593Smuzhiyun r->regs[3] = vewin;
539*4882a593Smuzhiyun r->regs[4] = vesync;
540*4882a593Smuzhiyun r->regs[5] = vssync;
541*4882a593Smuzhiyun r->regs[6] = vperiod;
542*4882a593Smuzhiyun r->regs[7] = piped;
543*4882a593Smuzhiyun r->regs[8] = hperiod;
544*4882a593Smuzhiyun r->regs[9] = hsblank;
545*4882a593Smuzhiyun r->regs[10] = heblank;
546*4882a593Smuzhiyun r->regs[11] = hesync;
547*4882a593Smuzhiyun r->regs[12] = hssync;
548*4882a593Smuzhiyun r->regs[13] = heq;
549*4882a593Smuzhiyun r->regs[14] = hlfln;
550*4882a593Smuzhiyun r->regs[15] = hserr;
551*4882a593Smuzhiyun
552*4882a593Smuzhiyun if (par->xres >= 1280 && par->cmode >= CMODE_16)
553*4882a593Smuzhiyun par->ctrl = 0x7f;
554*4882a593Smuzhiyun else
555*4882a593Smuzhiyun par->ctrl = 0x3b;
556*4882a593Smuzhiyun
557*4882a593Smuzhiyun if (mac_var_to_vmode(var, &par->vmode, &cmode))
558*4882a593Smuzhiyun par->vmode = 0;
559*4882a593Smuzhiyun
560*4882a593Smuzhiyun return 0;
561*4882a593Smuzhiyun }
562*4882a593Smuzhiyun
563*4882a593Smuzhiyun
564*4882a593Smuzhiyun /*
565*4882a593Smuzhiyun * Convert hardware data in par to an fb_var_screeninfo
566*4882a593Smuzhiyun */
567*4882a593Smuzhiyun
control_par_to_var(struct fb_par_control * par,struct fb_var_screeninfo * var)568*4882a593Smuzhiyun static void control_par_to_var(struct fb_par_control *par, struct fb_var_screeninfo *var)
569*4882a593Smuzhiyun {
570*4882a593Smuzhiyun struct control_regints *rv;
571*4882a593Smuzhiyun
572*4882a593Smuzhiyun rv = (struct control_regints *) par->regvals.regs;
573*4882a593Smuzhiyun
574*4882a593Smuzhiyun memset(var, 0, sizeof(*var));
575*4882a593Smuzhiyun var->xres = par->xres;
576*4882a593Smuzhiyun var->yres = par->yres;
577*4882a593Smuzhiyun var->xres_virtual = par->vxres;
578*4882a593Smuzhiyun var->yres_virtual = par->vyres;
579*4882a593Smuzhiyun var->xoffset = par->xoffset;
580*4882a593Smuzhiyun var->yoffset = par->yoffset;
581*4882a593Smuzhiyun
582*4882a593Smuzhiyun switch(par->cmode) {
583*4882a593Smuzhiyun default:
584*4882a593Smuzhiyun case CMODE_8:
585*4882a593Smuzhiyun var->bits_per_pixel = 8;
586*4882a593Smuzhiyun var->red.length = 8;
587*4882a593Smuzhiyun var->green.length = 8;
588*4882a593Smuzhiyun var->blue.length = 8;
589*4882a593Smuzhiyun break;
590*4882a593Smuzhiyun case CMODE_16: /* RGB 555 */
591*4882a593Smuzhiyun var->bits_per_pixel = 16;
592*4882a593Smuzhiyun var->red.offset = 10;
593*4882a593Smuzhiyun var->red.length = 5;
594*4882a593Smuzhiyun var->green.offset = 5;
595*4882a593Smuzhiyun var->green.length = 5;
596*4882a593Smuzhiyun var->blue.length = 5;
597*4882a593Smuzhiyun break;
598*4882a593Smuzhiyun case CMODE_32: /* RGB 888 */
599*4882a593Smuzhiyun var->bits_per_pixel = 32;
600*4882a593Smuzhiyun var->red.offset = 16;
601*4882a593Smuzhiyun var->red.length = 8;
602*4882a593Smuzhiyun var->green.offset = 8;
603*4882a593Smuzhiyun var->green.length = 8;
604*4882a593Smuzhiyun var->blue.length = 8;
605*4882a593Smuzhiyun var->transp.offset = 24;
606*4882a593Smuzhiyun var->transp.length = 8;
607*4882a593Smuzhiyun break;
608*4882a593Smuzhiyun }
609*4882a593Smuzhiyun var->height = -1;
610*4882a593Smuzhiyun var->width = -1;
611*4882a593Smuzhiyun var->vmode = FB_VMODE_NONINTERLACED;
612*4882a593Smuzhiyun
613*4882a593Smuzhiyun var->left_margin = (rv->heblank - rv->hesync) << 1;
614*4882a593Smuzhiyun var->right_margin = (rv->hssync - rv->hsblank) << 1;
615*4882a593Smuzhiyun var->hsync_len = (rv->hperiod + 2 - rv->hssync + rv->hesync) << 1;
616*4882a593Smuzhiyun
617*4882a593Smuzhiyun var->upper_margin = (rv->veblank - rv->vesync) >> 1;
618*4882a593Smuzhiyun var->lower_margin = (rv->vssync - rv->vsblank) >> 1;
619*4882a593Smuzhiyun var->vsync_len = (rv->vperiod - rv->vssync + rv->vesync) >> 1;
620*4882a593Smuzhiyun
621*4882a593Smuzhiyun var->sync = par->sync;
622*4882a593Smuzhiyun
623*4882a593Smuzhiyun /*
624*4882a593Smuzhiyun * 10^12 * clock_params[0] / (3906400 * clock_params[1]
625*4882a593Smuzhiyun * * 2^clock_params[2])
626*4882a593Smuzhiyun * (10^12 * clock_params[0] / (3906400 * clock_params[1]))
627*4882a593Smuzhiyun * >> clock_params[2]
628*4882a593Smuzhiyun */
629*4882a593Smuzhiyun /* (255990.17 * clock_params[0] / clock_params[1]) >> clock_params[2] */
630*4882a593Smuzhiyun var->pixclock = CONTROL_PIXCLOCK_BASE * par->regvals.clock_params[0];
631*4882a593Smuzhiyun var->pixclock /= par->regvals.clock_params[1];
632*4882a593Smuzhiyun var->pixclock >>= par->regvals.clock_params[2];
633*4882a593Smuzhiyun }
634*4882a593Smuzhiyun
635*4882a593Smuzhiyun /******************** The functions for controlfb_ops ********************/
636*4882a593Smuzhiyun
637*4882a593Smuzhiyun /*
638*4882a593Smuzhiyun * Checks a var structure
639*4882a593Smuzhiyun */
controlfb_check_var(struct fb_var_screeninfo * var,struct fb_info * info)640*4882a593Smuzhiyun static int controlfb_check_var (struct fb_var_screeninfo *var, struct fb_info *info)
641*4882a593Smuzhiyun {
642*4882a593Smuzhiyun struct fb_par_control par;
643*4882a593Smuzhiyun int err;
644*4882a593Smuzhiyun
645*4882a593Smuzhiyun err = control_var_to_par(var, &par, info);
646*4882a593Smuzhiyun if (err)
647*4882a593Smuzhiyun return err;
648*4882a593Smuzhiyun control_par_to_var(&par, var);
649*4882a593Smuzhiyun
650*4882a593Smuzhiyun return 0;
651*4882a593Smuzhiyun }
652*4882a593Smuzhiyun
653*4882a593Smuzhiyun /*
654*4882a593Smuzhiyun * Applies current var to display
655*4882a593Smuzhiyun */
controlfb_set_par(struct fb_info * info)656*4882a593Smuzhiyun static int controlfb_set_par (struct fb_info *info)
657*4882a593Smuzhiyun {
658*4882a593Smuzhiyun struct fb_info_control *p =
659*4882a593Smuzhiyun container_of(info, struct fb_info_control, info);
660*4882a593Smuzhiyun struct fb_par_control par;
661*4882a593Smuzhiyun int err;
662*4882a593Smuzhiyun
663*4882a593Smuzhiyun if((err = control_var_to_par(&info->var, &par, info))) {
664*4882a593Smuzhiyun printk (KERN_ERR "controlfb_set_par: error calling"
665*4882a593Smuzhiyun " control_var_to_par: %d.\n", err);
666*4882a593Smuzhiyun return err;
667*4882a593Smuzhiyun }
668*4882a593Smuzhiyun
669*4882a593Smuzhiyun control_set_hardware(p, &par);
670*4882a593Smuzhiyun
671*4882a593Smuzhiyun info->fix.visual = (p->par.cmode == CMODE_8) ?
672*4882a593Smuzhiyun FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR;
673*4882a593Smuzhiyun info->fix.line_length = p->par.pitch;
674*4882a593Smuzhiyun info->fix.xpanstep = 32 >> p->par.cmode;
675*4882a593Smuzhiyun info->fix.ypanstep = 1;
676*4882a593Smuzhiyun
677*4882a593Smuzhiyun return 0;
678*4882a593Smuzhiyun }
679*4882a593Smuzhiyun
controlfb_pan_display(struct fb_var_screeninfo * var,struct fb_info * info)680*4882a593Smuzhiyun static int controlfb_pan_display(struct fb_var_screeninfo *var,
681*4882a593Smuzhiyun struct fb_info *info)
682*4882a593Smuzhiyun {
683*4882a593Smuzhiyun unsigned int xoffset, hstep;
684*4882a593Smuzhiyun struct fb_info_control *p =
685*4882a593Smuzhiyun container_of(info, struct fb_info_control, info);
686*4882a593Smuzhiyun struct fb_par_control *par = &p->par;
687*4882a593Smuzhiyun
688*4882a593Smuzhiyun /*
689*4882a593Smuzhiyun * make sure start addr will be 32-byte aligned
690*4882a593Smuzhiyun */
691*4882a593Smuzhiyun hstep = 0x1f >> par->cmode;
692*4882a593Smuzhiyun xoffset = (var->xoffset + hstep) & ~hstep;
693*4882a593Smuzhiyun
694*4882a593Smuzhiyun if (xoffset+par->xres > par->vxres ||
695*4882a593Smuzhiyun var->yoffset+par->yres > par->vyres)
696*4882a593Smuzhiyun return -EINVAL;
697*4882a593Smuzhiyun
698*4882a593Smuzhiyun set_screen_start(xoffset, var->yoffset, p);
699*4882a593Smuzhiyun
700*4882a593Smuzhiyun return 0;
701*4882a593Smuzhiyun }
702*4882a593Smuzhiyun
controlfb_blank(int blank_mode,struct fb_info * info)703*4882a593Smuzhiyun static int controlfb_blank(int blank_mode, struct fb_info *info)
704*4882a593Smuzhiyun {
705*4882a593Smuzhiyun struct fb_info_control __maybe_unused *p =
706*4882a593Smuzhiyun container_of(info, struct fb_info_control, info);
707*4882a593Smuzhiyun unsigned ctrl;
708*4882a593Smuzhiyun
709*4882a593Smuzhiyun ctrl = in_le32(CNTRL_REG(p, ctrl));
710*4882a593Smuzhiyun if (blank_mode > 0)
711*4882a593Smuzhiyun switch (blank_mode) {
712*4882a593Smuzhiyun case FB_BLANK_VSYNC_SUSPEND:
713*4882a593Smuzhiyun ctrl &= ~3;
714*4882a593Smuzhiyun break;
715*4882a593Smuzhiyun case FB_BLANK_HSYNC_SUSPEND:
716*4882a593Smuzhiyun ctrl &= ~0x30;
717*4882a593Smuzhiyun break;
718*4882a593Smuzhiyun case FB_BLANK_POWERDOWN:
719*4882a593Smuzhiyun ctrl &= ~0x33;
720*4882a593Smuzhiyun fallthrough;
721*4882a593Smuzhiyun case FB_BLANK_NORMAL:
722*4882a593Smuzhiyun ctrl |= 0x400;
723*4882a593Smuzhiyun break;
724*4882a593Smuzhiyun default:
725*4882a593Smuzhiyun break;
726*4882a593Smuzhiyun }
727*4882a593Smuzhiyun else {
728*4882a593Smuzhiyun ctrl &= ~0x400;
729*4882a593Smuzhiyun ctrl |= 0x33;
730*4882a593Smuzhiyun }
731*4882a593Smuzhiyun out_le32(CNTRL_REG(p,ctrl), ctrl);
732*4882a593Smuzhiyun
733*4882a593Smuzhiyun return 0;
734*4882a593Smuzhiyun }
735*4882a593Smuzhiyun
736*4882a593Smuzhiyun /*
737*4882a593Smuzhiyun * Private mmap since we want to have a different caching on the framebuffer
738*4882a593Smuzhiyun * for controlfb.
739*4882a593Smuzhiyun * Note there's no locking in here; it's done in fb_mmap() in fbmem.c.
740*4882a593Smuzhiyun */
controlfb_mmap(struct fb_info * info,struct vm_area_struct * vma)741*4882a593Smuzhiyun static int controlfb_mmap(struct fb_info *info,
742*4882a593Smuzhiyun struct vm_area_struct *vma)
743*4882a593Smuzhiyun {
744*4882a593Smuzhiyun unsigned long mmio_pgoff;
745*4882a593Smuzhiyun unsigned long start;
746*4882a593Smuzhiyun u32 len;
747*4882a593Smuzhiyun
748*4882a593Smuzhiyun start = info->fix.smem_start;
749*4882a593Smuzhiyun len = info->fix.smem_len;
750*4882a593Smuzhiyun mmio_pgoff = PAGE_ALIGN((start & ~PAGE_MASK) + len) >> PAGE_SHIFT;
751*4882a593Smuzhiyun if (vma->vm_pgoff >= mmio_pgoff) {
752*4882a593Smuzhiyun if (info->var.accel_flags)
753*4882a593Smuzhiyun return -EINVAL;
754*4882a593Smuzhiyun vma->vm_pgoff -= mmio_pgoff;
755*4882a593Smuzhiyun start = info->fix.mmio_start;
756*4882a593Smuzhiyun len = info->fix.mmio_len;
757*4882a593Smuzhiyun vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
758*4882a593Smuzhiyun } else {
759*4882a593Smuzhiyun /* framebuffer */
760*4882a593Smuzhiyun vma->vm_page_prot = pgprot_cached_wthru(vma->vm_page_prot);
761*4882a593Smuzhiyun }
762*4882a593Smuzhiyun
763*4882a593Smuzhiyun return vm_iomap_memory(vma, start, len);
764*4882a593Smuzhiyun }
765*4882a593Smuzhiyun
766*4882a593Smuzhiyun static const struct fb_ops controlfb_ops = {
767*4882a593Smuzhiyun .owner = THIS_MODULE,
768*4882a593Smuzhiyun .fb_check_var = controlfb_check_var,
769*4882a593Smuzhiyun .fb_set_par = controlfb_set_par,
770*4882a593Smuzhiyun .fb_setcolreg = controlfb_setcolreg,
771*4882a593Smuzhiyun .fb_pan_display = controlfb_pan_display,
772*4882a593Smuzhiyun .fb_blank = controlfb_blank,
773*4882a593Smuzhiyun .fb_mmap = controlfb_mmap,
774*4882a593Smuzhiyun .fb_fillrect = cfb_fillrect,
775*4882a593Smuzhiyun .fb_copyarea = cfb_copyarea,
776*4882a593Smuzhiyun .fb_imageblit = cfb_imageblit,
777*4882a593Smuzhiyun };
778*4882a593Smuzhiyun
779*4882a593Smuzhiyun /*
780*4882a593Smuzhiyun * Set misc info vars for this driver
781*4882a593Smuzhiyun */
control_init_info(struct fb_info * info,struct fb_info_control * p)782*4882a593Smuzhiyun static void __init control_init_info(struct fb_info *info, struct fb_info_control *p)
783*4882a593Smuzhiyun {
784*4882a593Smuzhiyun /* Fill fb_info */
785*4882a593Smuzhiyun info->par = &p->par;
786*4882a593Smuzhiyun info->fbops = &controlfb_ops;
787*4882a593Smuzhiyun info->pseudo_palette = p->pseudo_palette;
788*4882a593Smuzhiyun info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN;
789*4882a593Smuzhiyun info->screen_base = p->frame_buffer + CTRLFB_OFF;
790*4882a593Smuzhiyun
791*4882a593Smuzhiyun fb_alloc_cmap(&info->cmap, 256, 0);
792*4882a593Smuzhiyun
793*4882a593Smuzhiyun /* Fill fix common fields */
794*4882a593Smuzhiyun strcpy(info->fix.id, "control");
795*4882a593Smuzhiyun info->fix.mmio_start = p->control_regs_phys;
796*4882a593Smuzhiyun info->fix.mmio_len = sizeof(struct control_regs);
797*4882a593Smuzhiyun info->fix.type = FB_TYPE_PACKED_PIXELS;
798*4882a593Smuzhiyun info->fix.smem_start = p->frame_buffer_phys + CTRLFB_OFF;
799*4882a593Smuzhiyun info->fix.smem_len = p->total_vram - CTRLFB_OFF;
800*4882a593Smuzhiyun info->fix.ywrapstep = 0;
801*4882a593Smuzhiyun info->fix.type_aux = 0;
802*4882a593Smuzhiyun info->fix.accel = FB_ACCEL_NONE;
803*4882a593Smuzhiyun }
804*4882a593Smuzhiyun
805*4882a593Smuzhiyun /*
806*4882a593Smuzhiyun * Parse user specified options (`video=controlfb:')
807*4882a593Smuzhiyun */
control_setup(char * options)808*4882a593Smuzhiyun static void __init control_setup(char *options)
809*4882a593Smuzhiyun {
810*4882a593Smuzhiyun char *this_opt;
811*4882a593Smuzhiyun
812*4882a593Smuzhiyun if (!options || !*options)
813*4882a593Smuzhiyun return;
814*4882a593Smuzhiyun
815*4882a593Smuzhiyun while ((this_opt = strsep(&options, ",")) != NULL) {
816*4882a593Smuzhiyun if (!strncmp(this_opt, "vmode:", 6)) {
817*4882a593Smuzhiyun int vmode = simple_strtoul(this_opt+6, NULL, 0);
818*4882a593Smuzhiyun if (vmode > 0 && vmode <= VMODE_MAX &&
819*4882a593Smuzhiyun control_mac_modes[vmode - 1].m[1] >= 0)
820*4882a593Smuzhiyun default_vmode = vmode;
821*4882a593Smuzhiyun } else if (!strncmp(this_opt, "cmode:", 6)) {
822*4882a593Smuzhiyun int depth = simple_strtoul(this_opt+6, NULL, 0);
823*4882a593Smuzhiyun switch (depth) {
824*4882a593Smuzhiyun case CMODE_8:
825*4882a593Smuzhiyun case CMODE_16:
826*4882a593Smuzhiyun case CMODE_32:
827*4882a593Smuzhiyun default_cmode = depth;
828*4882a593Smuzhiyun break;
829*4882a593Smuzhiyun case 8:
830*4882a593Smuzhiyun default_cmode = CMODE_8;
831*4882a593Smuzhiyun break;
832*4882a593Smuzhiyun case 15:
833*4882a593Smuzhiyun case 16:
834*4882a593Smuzhiyun default_cmode = CMODE_16;
835*4882a593Smuzhiyun break;
836*4882a593Smuzhiyun case 24:
837*4882a593Smuzhiyun case 32:
838*4882a593Smuzhiyun default_cmode = CMODE_32;
839*4882a593Smuzhiyun break;
840*4882a593Smuzhiyun }
841*4882a593Smuzhiyun }
842*4882a593Smuzhiyun }
843*4882a593Smuzhiyun }
844*4882a593Smuzhiyun
845*4882a593Smuzhiyun /*
846*4882a593Smuzhiyun * finish off the driver initialization and register
847*4882a593Smuzhiyun */
init_control(struct fb_info_control * p)848*4882a593Smuzhiyun static int __init init_control(struct fb_info_control *p)
849*4882a593Smuzhiyun {
850*4882a593Smuzhiyun int full, sense, vmode, cmode, vyres;
851*4882a593Smuzhiyun struct fb_var_screeninfo var;
852*4882a593Smuzhiyun int rc;
853*4882a593Smuzhiyun
854*4882a593Smuzhiyun printk(KERN_INFO "controlfb: ");
855*4882a593Smuzhiyun
856*4882a593Smuzhiyun full = p->total_vram == 0x400000;
857*4882a593Smuzhiyun
858*4882a593Smuzhiyun /* Try to pick a video mode out of NVRAM if we have one. */
859*4882a593Smuzhiyun cmode = default_cmode;
860*4882a593Smuzhiyun if (IS_REACHABLE(CONFIG_NVRAM) && cmode == CMODE_NVRAM)
861*4882a593Smuzhiyun cmode = nvram_read_byte(NV_CMODE);
862*4882a593Smuzhiyun if (cmode < CMODE_8 || cmode > CMODE_32)
863*4882a593Smuzhiyun cmode = CMODE_8;
864*4882a593Smuzhiyun
865*4882a593Smuzhiyun vmode = default_vmode;
866*4882a593Smuzhiyun if (IS_REACHABLE(CONFIG_NVRAM) && vmode == VMODE_NVRAM)
867*4882a593Smuzhiyun vmode = nvram_read_byte(NV_VMODE);
868*4882a593Smuzhiyun if (vmode < 1 || vmode > VMODE_MAX ||
869*4882a593Smuzhiyun control_mac_modes[vmode - 1].m[full] < cmode) {
870*4882a593Smuzhiyun sense = read_control_sense(p);
871*4882a593Smuzhiyun printk(KERN_CONT "Monitor sense value = 0x%x, ", sense);
872*4882a593Smuzhiyun vmode = mac_map_monitor_sense(sense);
873*4882a593Smuzhiyun if (control_mac_modes[vmode - 1].m[full] < 0)
874*4882a593Smuzhiyun vmode = VMODE_640_480_60;
875*4882a593Smuzhiyun cmode = min(cmode, control_mac_modes[vmode - 1].m[full]);
876*4882a593Smuzhiyun }
877*4882a593Smuzhiyun
878*4882a593Smuzhiyun /* Initialize info structure */
879*4882a593Smuzhiyun control_init_info(&p->info, p);
880*4882a593Smuzhiyun
881*4882a593Smuzhiyun /* Setup default var */
882*4882a593Smuzhiyun if (mac_vmode_to_var(vmode, cmode, &var) < 0) {
883*4882a593Smuzhiyun /* This shouldn't happen! */
884*4882a593Smuzhiyun printk("mac_vmode_to_var(%d, %d,) failed\n", vmode, cmode);
885*4882a593Smuzhiyun try_again:
886*4882a593Smuzhiyun vmode = VMODE_640_480_60;
887*4882a593Smuzhiyun cmode = CMODE_8;
888*4882a593Smuzhiyun if (mac_vmode_to_var(vmode, cmode, &var) < 0) {
889*4882a593Smuzhiyun printk(KERN_ERR "controlfb: mac_vmode_to_var() failed\n");
890*4882a593Smuzhiyun return -ENXIO;
891*4882a593Smuzhiyun }
892*4882a593Smuzhiyun printk(KERN_INFO "controlfb: ");
893*4882a593Smuzhiyun }
894*4882a593Smuzhiyun printk("using video mode %d and color mode %d.\n", vmode, cmode);
895*4882a593Smuzhiyun
896*4882a593Smuzhiyun vyres = (p->total_vram - CTRLFB_OFF) / (var.xres << cmode);
897*4882a593Smuzhiyun if (vyres > var.yres)
898*4882a593Smuzhiyun var.yres_virtual = vyres;
899*4882a593Smuzhiyun
900*4882a593Smuzhiyun /* Apply default var */
901*4882a593Smuzhiyun var.activate = FB_ACTIVATE_NOW;
902*4882a593Smuzhiyun rc = fb_set_var(&p->info, &var);
903*4882a593Smuzhiyun if (rc && (vmode != VMODE_640_480_60 || cmode != CMODE_8))
904*4882a593Smuzhiyun goto try_again;
905*4882a593Smuzhiyun
906*4882a593Smuzhiyun /* Register with fbdev layer */
907*4882a593Smuzhiyun if (register_framebuffer(&p->info) < 0)
908*4882a593Smuzhiyun return -ENXIO;
909*4882a593Smuzhiyun
910*4882a593Smuzhiyun fb_info(&p->info, "control display adapter\n");
911*4882a593Smuzhiyun
912*4882a593Smuzhiyun return 0;
913*4882a593Smuzhiyun }
914*4882a593Smuzhiyun
control_cleanup(void)915*4882a593Smuzhiyun static void control_cleanup(void)
916*4882a593Smuzhiyun {
917*4882a593Smuzhiyun struct fb_info_control *p = control_fb;
918*4882a593Smuzhiyun
919*4882a593Smuzhiyun if (!p)
920*4882a593Smuzhiyun return;
921*4882a593Smuzhiyun
922*4882a593Smuzhiyun if (p->cmap_regs)
923*4882a593Smuzhiyun iounmap(p->cmap_regs);
924*4882a593Smuzhiyun if (p->control_regs)
925*4882a593Smuzhiyun iounmap(p->control_regs);
926*4882a593Smuzhiyun if (p->frame_buffer) {
927*4882a593Smuzhiyun if (p->control_use_bank2)
928*4882a593Smuzhiyun p->frame_buffer -= 0x600000;
929*4882a593Smuzhiyun iounmap(p->frame_buffer);
930*4882a593Smuzhiyun }
931*4882a593Smuzhiyun if (p->cmap_regs_phys)
932*4882a593Smuzhiyun release_mem_region(p->cmap_regs_phys, 0x1000);
933*4882a593Smuzhiyun if (p->control_regs_phys)
934*4882a593Smuzhiyun release_mem_region(p->control_regs_phys, p->control_regs_size);
935*4882a593Smuzhiyun if (p->fb_orig_base)
936*4882a593Smuzhiyun release_mem_region(p->fb_orig_base, p->fb_orig_size);
937*4882a593Smuzhiyun kfree(p);
938*4882a593Smuzhiyun }
939*4882a593Smuzhiyun
940*4882a593Smuzhiyun /*
941*4882a593Smuzhiyun * find "control" and initialize
942*4882a593Smuzhiyun */
control_of_init(struct device_node * dp)943*4882a593Smuzhiyun static int __init control_of_init(struct device_node *dp)
944*4882a593Smuzhiyun {
945*4882a593Smuzhiyun struct fb_info_control *p;
946*4882a593Smuzhiyun struct resource fb_res, reg_res;
947*4882a593Smuzhiyun
948*4882a593Smuzhiyun if (control_fb) {
949*4882a593Smuzhiyun printk(KERN_ERR "controlfb: only one control is supported\n");
950*4882a593Smuzhiyun return -ENXIO;
951*4882a593Smuzhiyun }
952*4882a593Smuzhiyun
953*4882a593Smuzhiyun if (of_pci_address_to_resource(dp, 2, &fb_res) ||
954*4882a593Smuzhiyun of_pci_address_to_resource(dp, 1, ®_res)) {
955*4882a593Smuzhiyun printk(KERN_ERR "can't get 2 addresses for control\n");
956*4882a593Smuzhiyun return -ENXIO;
957*4882a593Smuzhiyun }
958*4882a593Smuzhiyun p = kzalloc(sizeof(*p), GFP_KERNEL);
959*4882a593Smuzhiyun if (!p)
960*4882a593Smuzhiyun return -ENOMEM;
961*4882a593Smuzhiyun control_fb = p; /* save it for cleanups */
962*4882a593Smuzhiyun
963*4882a593Smuzhiyun /* Map in frame buffer and registers */
964*4882a593Smuzhiyun p->fb_orig_base = fb_res.start;
965*4882a593Smuzhiyun p->fb_orig_size = resource_size(&fb_res);
966*4882a593Smuzhiyun /* use the big-endian aperture (??) */
967*4882a593Smuzhiyun p->frame_buffer_phys = fb_res.start + 0x800000;
968*4882a593Smuzhiyun p->control_regs_phys = reg_res.start;
969*4882a593Smuzhiyun p->control_regs_size = resource_size(®_res);
970*4882a593Smuzhiyun
971*4882a593Smuzhiyun if (!p->fb_orig_base ||
972*4882a593Smuzhiyun !request_mem_region(p->fb_orig_base,p->fb_orig_size,"controlfb")) {
973*4882a593Smuzhiyun p->fb_orig_base = 0;
974*4882a593Smuzhiyun goto error_out;
975*4882a593Smuzhiyun }
976*4882a593Smuzhiyun /* map at most 8MB for the frame buffer */
977*4882a593Smuzhiyun p->frame_buffer = ioremap_wt(p->frame_buffer_phys, 0x800000);
978*4882a593Smuzhiyun
979*4882a593Smuzhiyun if (!p->control_regs_phys ||
980*4882a593Smuzhiyun !request_mem_region(p->control_regs_phys, p->control_regs_size,
981*4882a593Smuzhiyun "controlfb regs")) {
982*4882a593Smuzhiyun p->control_regs_phys = 0;
983*4882a593Smuzhiyun goto error_out;
984*4882a593Smuzhiyun }
985*4882a593Smuzhiyun p->control_regs = ioremap(p->control_regs_phys, p->control_regs_size);
986*4882a593Smuzhiyun
987*4882a593Smuzhiyun p->cmap_regs_phys = 0xf301b000; /* XXX not in prom? */
988*4882a593Smuzhiyun if (!request_mem_region(p->cmap_regs_phys, 0x1000, "controlfb cmap")) {
989*4882a593Smuzhiyun p->cmap_regs_phys = 0;
990*4882a593Smuzhiyun goto error_out;
991*4882a593Smuzhiyun }
992*4882a593Smuzhiyun p->cmap_regs = ioremap(p->cmap_regs_phys, 0x1000);
993*4882a593Smuzhiyun
994*4882a593Smuzhiyun if (!p->cmap_regs || !p->control_regs || !p->frame_buffer)
995*4882a593Smuzhiyun goto error_out;
996*4882a593Smuzhiyun
997*4882a593Smuzhiyun find_vram_size(p);
998*4882a593Smuzhiyun if (!p->total_vram)
999*4882a593Smuzhiyun goto error_out;
1000*4882a593Smuzhiyun
1001*4882a593Smuzhiyun if (init_control(p) < 0)
1002*4882a593Smuzhiyun goto error_out;
1003*4882a593Smuzhiyun
1004*4882a593Smuzhiyun return 0;
1005*4882a593Smuzhiyun
1006*4882a593Smuzhiyun error_out:
1007*4882a593Smuzhiyun control_cleanup();
1008*4882a593Smuzhiyun return -ENXIO;
1009*4882a593Smuzhiyun }
1010*4882a593Smuzhiyun
control_init(void)1011*4882a593Smuzhiyun static int __init control_init(void)
1012*4882a593Smuzhiyun {
1013*4882a593Smuzhiyun struct device_node *dp;
1014*4882a593Smuzhiyun char *option = NULL;
1015*4882a593Smuzhiyun int ret = -ENXIO;
1016*4882a593Smuzhiyun
1017*4882a593Smuzhiyun if (fb_get_options("controlfb", &option))
1018*4882a593Smuzhiyun return -ENODEV;
1019*4882a593Smuzhiyun control_setup(option);
1020*4882a593Smuzhiyun
1021*4882a593Smuzhiyun dp = of_find_node_by_name(NULL, "control");
1022*4882a593Smuzhiyun if (dp && !control_of_init(dp))
1023*4882a593Smuzhiyun ret = 0;
1024*4882a593Smuzhiyun of_node_put(dp);
1025*4882a593Smuzhiyun
1026*4882a593Smuzhiyun return ret;
1027*4882a593Smuzhiyun }
1028*4882a593Smuzhiyun
1029*4882a593Smuzhiyun device_initcall(control_init);
1030