1 #include <linux/kernel.h>
2 #include <linux/errno.h>
3 #include <linux/string.h>
4 #include <linux/mm.h>
5 #include <linux/tty.h>
6 #include <linux/slab.h>
7 #include <linux/delay.h>
8 #include <linux/interrupt.h>
9 
10 #include <asm/uaccess.h>
11 #include <asm/setup.h>
12 #include <asm/segment.h>
13 #include <asm/system.h>
14 /*#include <asm/irq.h>*/
15 #include <asm/q40_master.h>
16 #include <linux/fb.h>
17 #include <linux/module.h>
18 #include <asm/pgtable.h>
19 
20 #include <video/fbcon.h>
21 #include <video/fbcon-cfb16.h>
22 
23 #define FBIOSETSCROLLMODE   0x4611
24 
25 #define Q40_PHYS_SCREEN_ADDR 0xFE800000
26 static unsigned long q40_screen_addr;
27 
28 static u16 fbcon_cmap_cfb16[16];
29 
30 /* frame buffer operations */
31 
32 static int q40fb_get_fix(struct fb_fix_screeninfo *fix, int con,
33 			struct fb_info *info);
34 static int q40fb_get_var(struct fb_var_screeninfo *var, int con,
35 			struct fb_info *info);
36 static int q40fb_set_var(struct fb_var_screeninfo *var, int con,
37 			struct fb_info *info);
38 static int q40fb_get_cmap(struct fb_cmap *cmap,int kspc,int con,
39 			 struct fb_info *info);
40 static int q40fb_set_cmap(struct fb_cmap *cmap,int kspc,int con,
41 			 struct fb_info *info);
42 static int q40fb_ioctl(struct inode *inode, struct file *file,
43 		      unsigned int cmd, unsigned long arg, int con,
44 		      struct fb_info *info);
45 
46 static int q40con_switch(int con, struct fb_info *info);
47 static int q40con_updatevar(int con, struct fb_info *info);
48 static void q40con_blank(int blank, struct fb_info *info);
49 
50 static void q40fb_set_disp(int con, struct fb_info *info);
51 
52 static struct display disp[MAX_NR_CONSOLES];
53 static struct fb_info fb_info;
54 static struct fb_ops q40fb_ops = {
55 	owner:		THIS_MODULE,
56 	fb_get_fix:	q40fb_get_fix,
57 	fb_get_var:	q40fb_get_var,
58 	fb_set_var:	q40fb_set_var,
59 	fb_get_cmap:	q40fb_get_cmap,
60 	fb_set_cmap:	q40fb_set_cmap,
61 	fb_ioctl:	q40fb_ioctl,
62 };
63 
64 static int currcon=0;
65 
66 static char q40fb_name[]="Q40";
67 
q40fb_get_fix(struct fb_fix_screeninfo * fix,int con,struct fb_info * info)68 static int q40fb_get_fix(struct fb_fix_screeninfo *fix, int con,
69 			struct fb_info *info)
70 {
71 	memset(fix, 0, sizeof(struct fb_fix_screeninfo));
72 
73 	strcpy(fix->id,"Q40");
74 	fix->smem_start=q40_screen_addr;
75 	fix->smem_len=1024*1024;
76 	fix->type=FB_TYPE_PACKED_PIXELS;
77 	fix->type_aux=0;
78 	fix->visual=FB_VISUAL_TRUECOLOR;  /* good approximation so far ..*/;
79 	fix->xpanstep=0;
80 	fix->ypanstep=0;
81 	fix->ywrapstep=0;
82         fix->line_length=1024*2;
83 
84 	/* no mmio,accel ...*/
85 
86 	return 0;
87 
88 }
89 
q40fb_get_var(struct fb_var_screeninfo * var,int con,struct fb_info * info)90 static int q40fb_get_var(struct fb_var_screeninfo *var, int con,
91 			struct fb_info *info)
92 {
93 	memset(var, 0, sizeof(struct fb_var_screeninfo));
94 
95 	var->xres=1024;
96 	var->yres=512;
97 	var->xres_virtual=1024;
98 	var->yres_virtual=512;
99 	var->xoffset=0;
100 	var->yoffset=0;
101 	var->bits_per_pixel=16;
102 	var->grayscale=0;
103 	var->nonstd=0;
104 	var->activate=FB_ACTIVATE_NOW;
105 	var->height=230;     /* approx for my 17" monitor, more important */
106 	var->width=300;      /* than the absolute values is the unusual aspect ratio*/
107 
108 	var->red.offset=6; /*6*/
109 	var->red.length=5;
110 	var->green.offset=11; /*11*/
111 	var->green.length=5;
112 	var->blue.offset=0;
113 	var->blue.length=6;
114 	var->transp.length=0;
115 
116 	var->pixclock=0;
117 	var->left_margin=0;
118 	var->right_margin=0;
119 	var->hsync_len=0;
120 	var->vsync_len=0;
121 	var->sync=0;
122 	var->vmode=FB_VMODE_NONINTERLACED;
123 
124 	return 0;
125 
126 }
127 
q40fb_set_var(struct fb_var_screeninfo * var,int con,struct fb_info * info)128 static int q40fb_set_var(struct fb_var_screeninfo *var, int con,
129 			struct fb_info *info)
130 {
131 	if(var->xres!=1024)
132 		return -EINVAL;
133 	if(var->yres!=512)
134 		return -EINVAL;
135 	if(var->xres_virtual!=1024)
136 		return -EINVAL;
137 	if(var->yres_virtual!=512)
138 		return -EINVAL;
139 	if(var->xoffset!=0)
140 		return -EINVAL;
141 	if(var->yoffset!=0)
142 		return -EINVAL;
143 	if(var->bits_per_pixel!=16)
144 		return -EINVAL;
145 	if(var->grayscale!=0)
146 		return -EINVAL;
147 	if(var->nonstd!=0)
148 		return -EINVAL;
149 	if(var->activate!=FB_ACTIVATE_NOW)
150 		return -EINVAL;
151 // ignore broken tools trying to set these values
152 #if 0
153 	if(var->pixclock!=0)
154 		return -EINVAL;
155 	if(var->left_margin!=0)
156 		return -EINVAL;
157 	if(var->right_margin!=0)
158 		return -EINVAL;
159 	if(var->hsync_len!=0)
160 		return -EINVAL;
161 	if(var->vsync_len!=0)
162 		return -EINVAL;
163 	if(var->sync!=0)
164 		return -EINVAL;
165 	if(var->vmode!=FB_VMODE_NONINTERLACED)
166 		return -EINVAL;
167 #endif
168 
169 	return 0;
170 
171 }
172 
q40_getcolreg(unsigned regno,unsigned * red,unsigned * green,unsigned * blue,unsigned * transp,struct fb_info * info)173 static int q40_getcolreg(unsigned regno, unsigned *red, unsigned *green,
174 			 unsigned *blue, unsigned *transp,
175 			 struct fb_info *info)
176 {
177     /*
178      *  Read a single color register and split it into colors/transparent.
179      *  The return values must have a 16 bit magnitude.
180      *  Return != 0 for invalid regno.
181      */
182     if (regno>=16) return 1;
183 
184     *transp=0;
185     *green = ((fbcon_cmap_cfb16[regno]>>11) & 31)<<11;
186     *red   = ((fbcon_cmap_cfb16[regno]>>6) & 31)<<11;
187     *blue  = ((fbcon_cmap_cfb16[regno]) & 63)<<10;
188 
189     return 0;
190 }
191 
q40_setcolreg(unsigned regno,unsigned red,unsigned green,unsigned blue,unsigned transp,struct fb_info * info)192 static int q40_setcolreg(unsigned regno, unsigned red, unsigned green,
193 			 unsigned blue, unsigned transp, struct fb_info *info)
194 {
195     /*
196      *  Set a single color register. The values supplied have a 16 bit
197      *  magnitude.
198      *  Return != 0 for invalid regno.
199      */
200 
201   red>>=11;
202   green>>=11;
203   blue>>=10;
204 
205     if (regno < 16) {
206       fbcon_cmap_cfb16[regno] = ((red & 31) <<6) |
207 	                         ((green & 31) << 11) |
208 	                         (blue & 63);
209     }
210     return 0;
211 }
212 
q40fb_get_cmap(struct fb_cmap * cmap,int kspc,int con,struct fb_info * info)213 static int q40fb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
214 			 struct fb_info *info)
215 {
216 #if 1
217 	if (con == currcon) /* current console? */
218 		return fb_get_cmap(cmap, kspc, q40_getcolreg, info);
219 	else if (fb_display[con].cmap.len) /* non default colormap? */
220 		fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
221 	else
222 		fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
223 			     cmap, kspc ? 0 : 2);
224 	return 0;
225 #else
226 	printk(KERN_ERR "get cmap not supported\n");
227 
228 	return -EINVAL;
229 #endif
230 }
231 
q40fb_set_cmap(struct fb_cmap * cmap,int kspc,int con,struct fb_info * info)232 static int q40fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
233 			 struct fb_info *info)
234 {
235 #if 1
236 	int err;
237 
238 	if (!fb_display[con].cmap.len) {	/* no colormap allocated? */
239 		if ((err = fb_alloc_cmap(&fb_display[con].cmap,
240 					 1<<fb_display[con].var.bits_per_pixel,
241 					 0)))
242 			return err;
243 	}
244 	if (con == currcon)			/* current console? */
245 		return fb_set_cmap(cmap, kspc, q40_setcolreg, info);
246 	else
247 		fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
248 	return 0;
249 #else
250 	printk(KERN_ERR "set cmap not supported\n");
251 
252 	return -EINVAL;
253 #endif
254 }
255 
q40fb_ioctl(struct inode * inode,struct file * file,unsigned int cmd,unsigned long arg,int con,struct fb_info * info)256 static int q40fb_ioctl(struct inode *inode, struct file *file,
257 		      unsigned int cmd, unsigned long arg, int con,
258 		      struct fb_info *info)
259 {
260 #if 0
261         unsigned long i;
262 	struct display *display;
263 
264 	if (con>=0)
265 	  display = &fb_display[con];
266 	else
267 	  display = &disp[0];
268 
269         if (cmd == FBIOSETSCROLLMODE)
270 	  {
271 	    i = verify_area(VERIFY_READ, (void *)arg, sizeof(unsigned long));
272 	    if (!i)
273 	      {
274 		copy_from_user(&i, (void *)arg, sizeof(unsigned long));
275 		display->scrollmode = i;
276 	      }
277 	    q40_updatescrollmode(display);
278 	    return i;
279 	  }
280 #endif
281 	return -EINVAL;
282 }
283 
q40fb_set_disp(int con,struct fb_info * info)284 static void q40fb_set_disp(int con, struct fb_info *info)
285 {
286   struct fb_fix_screeninfo fix;
287   struct display *display;
288 
289   q40fb_get_fix(&fix, con, info);
290 
291   if (con>=0)
292     display = &fb_display[con];
293   else
294     display = &disp[0];
295 
296   if (con<0) con=0;
297 
298    display->screen_base = (char *)fix.smem_start;
299    display->visual = fix.visual;
300    display->type = fix.type;
301    display->type_aux = fix.type_aux;
302    display->ypanstep = fix.ypanstep;
303    display->ywrapstep = fix.ywrapstep;
304    display->can_soft_blank = 0;
305    display->inverse = 0;
306    display->line_length = fix.line_length;
307 
308    display->scrollmode = SCROLL_YREDRAW;
309 
310 #ifdef FBCON_HAS_CFB16
311    display->dispsw = &fbcon_cfb16;
312    disp->dispsw_data = fbcon_cmap_cfb16;
313 #else
314    display->dispsw = &fbcon_dummy;
315 #endif
316 }
317 
q40fb_init(void)318 int __init q40fb_init(void)
319 {
320 
321         if ( !MACH_IS_Q40)
322 	  return -ENXIO;
323 #if 0
324         q40_screen_addr = kernel_map(Q40_PHYS_SCREEN_ADDR, 1024*1024,
325 					   KERNELMAP_NO_COPYBACK, NULL);
326 #else
327 	q40_screen_addr = Q40_PHYS_SCREEN_ADDR; /* mapped in q40/config.c */
328 #endif
329 
330 	fb_info.changevar=NULL;
331 	strcpy(&fb_info.modename[0],q40fb_name);
332 	fb_info.fontname[0]=0;
333 	fb_info.disp=disp;
334 	fb_info.switch_con=&q40con_switch;
335 	fb_info.updatevar=&q40con_updatevar;
336 	fb_info.blank=&q40con_blank;
337 	fb_info.node = -1;
338 	fb_info.fbops = &q40fb_ops;
339 	fb_info.flags = FBINFO_FLAG_DEFAULT;  /* not as module for now */
340 
341 	master_outb(3,DISPLAY_CONTROL_REG);
342 
343         q40fb_get_var(&disp[0].var, 0, &fb_info);
344 	q40fb_set_disp(-1, &fb_info);
345 
346 	if (register_framebuffer(&fb_info) < 0) {
347 		printk(KERN_ERR "unable to register Q40 frame buffer\n");
348 		return -EINVAL;
349 	}
350 
351         printk(KERN_INFO "fb%d: Q40 frame buffer alive and kicking !\n",
352 	       GET_FB_IDX(fb_info.node));
353 	return 0;
354 }
355 
356 
q40con_switch(int con,struct fb_info * info)357 static int q40con_switch(int con, struct fb_info *info)
358 {
359 	currcon=con;
360 
361 	return 0;
362 
363 }
364 
q40con_updatevar(int con,struct fb_info * info)365 static int q40con_updatevar(int con, struct fb_info *info)
366 {
367 	return 0;
368 }
369 
q40con_blank(int blank,struct fb_info * info)370 static void q40con_blank(int blank, struct fb_info *info)
371 {
372 }
373 
374 MODULE_LICENSE("GPL");
375