diff options
Diffstat (limited to 'drivers/video/sgivwfb.c')
-rw-r--r-- | drivers/video/sgivwfb.c | 901 |
1 files changed, 901 insertions, 0 deletions
diff --git a/drivers/video/sgivwfb.c b/drivers/video/sgivwfb.c new file mode 100644 index 000000000000..8413907b379a --- /dev/null +++ b/drivers/video/sgivwfb.c | |||
@@ -0,0 +1,901 @@ | |||
1 | /* | ||
2 | * linux/drivers/video/sgivwfb.c -- SGI DBE frame buffer device | ||
3 | * | ||
4 | * Copyright (C) 1999 Silicon Graphics, Inc. | ||
5 | * Jeffrey Newquist, newquist@engr.sgi.som | ||
6 | * | ||
7 | * This file is subject to the terms and conditions of the GNU General Public | ||
8 | * License. See the file COPYING in the main directory of this archive for | ||
9 | * more details. | ||
10 | */ | ||
11 | |||
12 | #include <linux/config.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/mm.h> | ||
16 | #include <linux/errno.h> | ||
17 | #include <linux/delay.h> | ||
18 | #include <linux/fb.h> | ||
19 | #include <linux/init.h> | ||
20 | #include <linux/ioport.h> | ||
21 | #include <asm/io.h> | ||
22 | #include <asm/mtrr.h> | ||
23 | |||
24 | #define INCLUDE_TIMING_TABLE_DATA | ||
25 | #define DBE_REG_BASE par->regs | ||
26 | #include <video/sgivw.h> | ||
27 | |||
28 | struct sgivw_par { | ||
29 | struct asregs *regs; | ||
30 | u32 cmap_fifo; | ||
31 | u_long timing_num; | ||
32 | }; | ||
33 | |||
34 | #define FLATPANEL_SGI_1600SW 5 | ||
35 | |||
36 | /* | ||
37 | * RAM we reserve for the frame buffer. This defines the maximum screen | ||
38 | * size | ||
39 | * | ||
40 | * The default can be overridden if the driver is compiled as a module | ||
41 | */ | ||
42 | |||
43 | /* set by arch/i386/kernel/setup.c */ | ||
44 | extern unsigned long sgivwfb_mem_phys; | ||
45 | extern unsigned long sgivwfb_mem_size; | ||
46 | |||
47 | static int ypan = 0; | ||
48 | static int ywrap = 0; | ||
49 | |||
50 | static int flatpanel_id = -1; | ||
51 | |||
52 | static struct fb_fix_screeninfo sgivwfb_fix __initdata = { | ||
53 | .id = "SGI Vis WS FB", | ||
54 | .type = FB_TYPE_PACKED_PIXELS, | ||
55 | .visual = FB_VISUAL_PSEUDOCOLOR, | ||
56 | .mmio_start = DBE_REG_PHYS, | ||
57 | .mmio_len = DBE_REG_SIZE, | ||
58 | .accel = FB_ACCEL_NONE, | ||
59 | .line_length = 640, | ||
60 | }; | ||
61 | |||
62 | static struct fb_var_screeninfo sgivwfb_var __initdata = { | ||
63 | /* 640x480, 8 bpp */ | ||
64 | .xres = 640, | ||
65 | .yres = 480, | ||
66 | .xres_virtual = 640, | ||
67 | .yres_virtual = 480, | ||
68 | .bits_per_pixel = 8, | ||
69 | .red = { 0, 8, 0 }, | ||
70 | .green = { 0, 8, 0 }, | ||
71 | .blue = { 0, 8, 0 }, | ||
72 | .height = -1, | ||
73 | .width = -1, | ||
74 | .pixclock = 20000, | ||
75 | .left_margin = 64, | ||
76 | .right_margin = 64, | ||
77 | .upper_margin = 32, | ||
78 | .lower_margin = 32, | ||
79 | .hsync_len = 64, | ||
80 | .vsync_len = 2, | ||
81 | .vmode = FB_VMODE_NONINTERLACED | ||
82 | }; | ||
83 | |||
84 | static struct fb_var_screeninfo sgivwfb_var1600sw __initdata = { | ||
85 | /* 1600x1024, 8 bpp */ | ||
86 | .xres = 1600, | ||
87 | .yres = 1024, | ||
88 | .xres_virtual = 1600, | ||
89 | .yres_virtual = 1024, | ||
90 | .bits_per_pixel = 8, | ||
91 | .red = { 0, 8, 0 }, | ||
92 | .green = { 0, 8, 0 }, | ||
93 | .blue = { 0, 8, 0 }, | ||
94 | .height = -1, | ||
95 | .width = -1, | ||
96 | .pixclock = 9353, | ||
97 | .left_margin = 20, | ||
98 | .right_margin = 30, | ||
99 | .upper_margin = 37, | ||
100 | .lower_margin = 3, | ||
101 | .hsync_len = 20, | ||
102 | .vsync_len = 3, | ||
103 | .vmode = FB_VMODE_NONINTERLACED | ||
104 | }; | ||
105 | |||
106 | /* | ||
107 | * Interface used by the world | ||
108 | */ | ||
109 | int sgivwfb_init(void); | ||
110 | |||
111 | static int sgivwfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info); | ||
112 | static int sgivwfb_set_par(struct fb_info *info); | ||
113 | static int sgivwfb_setcolreg(u_int regno, u_int red, u_int green, | ||
114 | u_int blue, u_int transp, | ||
115 | struct fb_info *info); | ||
116 | static int sgivwfb_mmap(struct fb_info *info, struct file *file, | ||
117 | struct vm_area_struct *vma); | ||
118 | |||
119 | static struct fb_ops sgivwfb_ops = { | ||
120 | .owner = THIS_MODULE, | ||
121 | .fb_check_var = sgivwfb_check_var, | ||
122 | .fb_set_par = sgivwfb_set_par, | ||
123 | .fb_setcolreg = sgivwfb_setcolreg, | ||
124 | .fb_fillrect = cfb_fillrect, | ||
125 | .fb_copyarea = cfb_copyarea, | ||
126 | .fb_imageblit = cfb_imageblit, | ||
127 | .fb_cursor = soft_cursor, | ||
128 | .fb_mmap = sgivwfb_mmap, | ||
129 | }; | ||
130 | |||
131 | /* | ||
132 | * Internal routines | ||
133 | */ | ||
134 | static unsigned long bytes_per_pixel(int bpp) | ||
135 | { | ||
136 | switch (bpp) { | ||
137 | case 8: | ||
138 | return 1; | ||
139 | case 16: | ||
140 | return 2; | ||
141 | case 32: | ||
142 | return 4; | ||
143 | default: | ||
144 | printk(KERN_INFO "sgivwfb: unsupported bpp %d\n", bpp); | ||
145 | return 0; | ||
146 | } | ||
147 | } | ||
148 | |||
149 | static unsigned long get_line_length(int xres_virtual, int bpp) | ||
150 | { | ||
151 | return (xres_virtual * bytes_per_pixel(bpp)); | ||
152 | } | ||
153 | |||
154 | /* | ||
155 | * Function: dbe_TurnOffDma | ||
156 | * Parameters: (None) | ||
157 | * Description: This should turn off the monitor and dbe. This is used | ||
158 | * when switching between the serial console and the graphics | ||
159 | * console. | ||
160 | */ | ||
161 | |||
162 | static void dbe_TurnOffDma(struct sgivw_par *par) | ||
163 | { | ||
164 | unsigned int readVal; | ||
165 | int i; | ||
166 | |||
167 | // Check to see if things are already turned off: | ||
168 | // 1) Check to see if dbe is not using the internal dotclock. | ||
169 | // 2) Check to see if the xy counter in dbe is already off. | ||
170 | |||
171 | DBE_GETREG(ctrlstat, readVal); | ||
172 | if (GET_DBE_FIELD(CTRLSTAT, PCLKSEL, readVal) < 2) | ||
173 | return; | ||
174 | |||
175 | DBE_GETREG(vt_xy, readVal); | ||
176 | if (GET_DBE_FIELD(VT_XY, VT_FREEZE, readVal) == 1) | ||
177 | return; | ||
178 | |||
179 | // Otherwise, turn off dbe | ||
180 | |||
181 | DBE_GETREG(ovr_control, readVal); | ||
182 | SET_DBE_FIELD(OVR_CONTROL, OVR_DMA_ENABLE, readVal, 0); | ||
183 | DBE_SETREG(ovr_control, readVal); | ||
184 | udelay(1000); | ||
185 | DBE_GETREG(frm_control, readVal); | ||
186 | SET_DBE_FIELD(FRM_CONTROL, FRM_DMA_ENABLE, readVal, 0); | ||
187 | DBE_SETREG(frm_control, readVal); | ||
188 | udelay(1000); | ||
189 | DBE_GETREG(did_control, readVal); | ||
190 | SET_DBE_FIELD(DID_CONTROL, DID_DMA_ENABLE, readVal, 0); | ||
191 | DBE_SETREG(did_control, readVal); | ||
192 | udelay(1000); | ||
193 | |||
194 | // XXX HACK: | ||
195 | // | ||
196 | // This was necessary for GBE--we had to wait through two | ||
197 | // vertical retrace periods before the pixel DMA was | ||
198 | // turned off for sure. I've left this in for now, in | ||
199 | // case dbe needs it. | ||
200 | |||
201 | for (i = 0; i < 10000; i++) { | ||
202 | DBE_GETREG(frm_inhwctrl, readVal); | ||
203 | if (GET_DBE_FIELD(FRM_INHWCTRL, FRM_DMA_ENABLE, readVal) == | ||
204 | 0) | ||
205 | udelay(10); | ||
206 | else { | ||
207 | DBE_GETREG(ovr_inhwctrl, readVal); | ||
208 | if (GET_DBE_FIELD | ||
209 | (OVR_INHWCTRL, OVR_DMA_ENABLE, readVal) == 0) | ||
210 | udelay(10); | ||
211 | else { | ||
212 | DBE_GETREG(did_inhwctrl, readVal); | ||
213 | if (GET_DBE_FIELD | ||
214 | (DID_INHWCTRL, DID_DMA_ENABLE, | ||
215 | readVal) == 0) | ||
216 | udelay(10); | ||
217 | else | ||
218 | break; | ||
219 | } | ||
220 | } | ||
221 | } | ||
222 | } | ||
223 | |||
224 | /* | ||
225 | * Set the User Defined Part of the Display. Again if par use it to get | ||
226 | * real video mode. | ||
227 | */ | ||
228 | static int sgivwfb_check_var(struct fb_var_screeninfo *var, | ||
229 | struct fb_info *info) | ||
230 | { | ||
231 | struct sgivw_par *par = (struct sgivw_par *)info->par; | ||
232 | struct dbe_timing_info *timing; | ||
233 | u_long line_length; | ||
234 | u_long min_mode; | ||
235 | int req_dot; | ||
236 | int test_mode; | ||
237 | |||
238 | /* | ||
239 | * FB_VMODE_CONUPDATE and FB_VMODE_SMOOTH_XPAN are equal! | ||
240 | * as FB_VMODE_SMOOTH_XPAN is only used internally | ||
241 | */ | ||
242 | |||
243 | if (var->vmode & FB_VMODE_CONUPDATE) { | ||
244 | var->vmode |= FB_VMODE_YWRAP; | ||
245 | var->xoffset = info->var.xoffset; | ||
246 | var->yoffset = info->var.yoffset; | ||
247 | } | ||
248 | |||
249 | /* XXX FIXME - forcing var's */ | ||
250 | var->xoffset = 0; | ||
251 | var->yoffset = 0; | ||
252 | |||
253 | /* Limit bpp to 8, 16, and 32 */ | ||
254 | if (var->bits_per_pixel <= 8) | ||
255 | var->bits_per_pixel = 8; | ||
256 | else if (var->bits_per_pixel <= 16) | ||
257 | var->bits_per_pixel = 16; | ||
258 | else if (var->bits_per_pixel <= 32) | ||
259 | var->bits_per_pixel = 32; | ||
260 | else | ||
261 | return -EINVAL; | ||
262 | |||
263 | var->grayscale = 0; /* No grayscale for now */ | ||
264 | |||
265 | /* determine valid resolution and timing */ | ||
266 | for (min_mode = 0; min_mode < DBE_VT_SIZE; min_mode++) { | ||
267 | if (dbeVTimings[min_mode].width >= var->xres && | ||
268 | dbeVTimings[min_mode].height >= var->yres) | ||
269 | break; | ||
270 | } | ||
271 | |||
272 | if (min_mode == DBE_VT_SIZE) | ||
273 | return -EINVAL; /* Resolution to high */ | ||
274 | |||
275 | /* XXX FIXME - should try to pick best refresh rate */ | ||
276 | /* for now, pick closest dot-clock within 3MHz */ | ||
277 | req_dot = PICOS2KHZ(var->pixclock); | ||
278 | printk(KERN_INFO "sgivwfb: requested pixclock=%d ps (%d KHz)\n", | ||
279 | var->pixclock, req_dot); | ||
280 | test_mode = min_mode; | ||
281 | while (dbeVTimings[min_mode].width == dbeVTimings[test_mode].width) { | ||
282 | if (dbeVTimings[test_mode].cfreq + 3000 > req_dot) | ||
283 | break; | ||
284 | test_mode++; | ||
285 | } | ||
286 | if (dbeVTimings[min_mode].width != dbeVTimings[test_mode].width) | ||
287 | test_mode--; | ||
288 | min_mode = test_mode; | ||
289 | timing = &dbeVTimings[min_mode]; | ||
290 | printk(KERN_INFO "sgivwfb: granted dot-clock=%d KHz\n", timing->cfreq); | ||
291 | |||
292 | /* Adjust virtual resolution, if necessary */ | ||
293 | if (var->xres > var->xres_virtual || (!ywrap && !ypan)) | ||
294 | var->xres_virtual = var->xres; | ||
295 | if (var->yres > var->yres_virtual || (!ywrap && !ypan)) | ||
296 | var->yres_virtual = var->yres; | ||
297 | |||
298 | /* | ||
299 | * Memory limit | ||
300 | */ | ||
301 | line_length = get_line_length(var->xres_virtual, var->bits_per_pixel); | ||
302 | if (line_length * var->yres_virtual > sgivwfb_mem_size) | ||
303 | return -ENOMEM; /* Virtual resolution to high */ | ||
304 | |||
305 | info->fix.line_length = line_length; | ||
306 | |||
307 | switch (var->bits_per_pixel) { | ||
308 | case 8: | ||
309 | var->red.offset = 0; | ||
310 | var->red.length = 8; | ||
311 | var->green.offset = 0; | ||
312 | var->green.length = 8; | ||
313 | var->blue.offset = 0; | ||
314 | var->blue.length = 8; | ||
315 | var->transp.offset = 0; | ||
316 | var->transp.length = 0; | ||
317 | break; | ||
318 | case 16: /* RGBA 5551 */ | ||
319 | var->red.offset = 11; | ||
320 | var->red.length = 5; | ||
321 | var->green.offset = 6; | ||
322 | var->green.length = 5; | ||
323 | var->blue.offset = 1; | ||
324 | var->blue.length = 5; | ||
325 | var->transp.offset = 0; | ||
326 | var->transp.length = 0; | ||
327 | break; | ||
328 | case 32: /* RGB 8888 */ | ||
329 | var->red.offset = 0; | ||
330 | var->red.length = 8; | ||
331 | var->green.offset = 8; | ||
332 | var->green.length = 8; | ||
333 | var->blue.offset = 16; | ||
334 | var->blue.length = 8; | ||
335 | var->transp.offset = 24; | ||
336 | var->transp.length = 8; | ||
337 | break; | ||
338 | } | ||
339 | var->red.msb_right = 0; | ||
340 | var->green.msb_right = 0; | ||
341 | var->blue.msb_right = 0; | ||
342 | var->transp.msb_right = 0; | ||
343 | |||
344 | /* set video timing information */ | ||
345 | var->pixclock = KHZ2PICOS(timing->cfreq); | ||
346 | var->left_margin = timing->htotal - timing->hsync_end; | ||
347 | var->right_margin = timing->hsync_start - timing->width; | ||
348 | var->upper_margin = timing->vtotal - timing->vsync_end; | ||
349 | var->lower_margin = timing->vsync_start - timing->height; | ||
350 | var->hsync_len = timing->hsync_end - timing->hsync_start; | ||
351 | var->vsync_len = timing->vsync_end - timing->vsync_start; | ||
352 | |||
353 | /* Ouch. This breaks the rules but timing_num is only important if you | ||
354 | * change a video mode */ | ||
355 | par->timing_num = min_mode; | ||
356 | |||
357 | printk(KERN_INFO "sgivwfb: new video mode xres=%d yres=%d bpp=%d\n", | ||
358 | var->xres, var->yres, var->bits_per_pixel); | ||
359 | printk(KERN_INFO " vxres=%d vyres=%d\n", var->xres_virtual, | ||
360 | var->yres_virtual); | ||
361 | return 0; | ||
362 | } | ||
363 | |||
364 | /* | ||
365 | * Setup flatpanel related registers. | ||
366 | */ | ||
367 | static void sgivwfb_setup_flatpanel(struct sgivw_par *par, struct dbe_timing_info *currentTiming) | ||
368 | { | ||
369 | int fp_wid, fp_hgt, fp_vbs, fp_vbe; | ||
370 | u32 outputVal = 0; | ||
371 | |||
372 | SET_DBE_FIELD(VT_FLAGS, HDRV_INVERT, outputVal, | ||
373 | (currentTiming->flags & FB_SYNC_HOR_HIGH_ACT) ? 0 : 1); | ||
374 | SET_DBE_FIELD(VT_FLAGS, VDRV_INVERT, outputVal, | ||
375 | (currentTiming->flags & FB_SYNC_VERT_HIGH_ACT) ? 0 : 1); | ||
376 | DBE_SETREG(vt_flags, outputVal); | ||
377 | |||
378 | /* Turn on the flat panel */ | ||
379 | switch (flatpanel_id) { | ||
380 | case FLATPANEL_SGI_1600SW: | ||
381 | fp_wid = 1600; | ||
382 | fp_hgt = 1024; | ||
383 | fp_vbs = 0; | ||
384 | fp_vbe = 1600; | ||
385 | currentTiming->pll_m = 4; | ||
386 | currentTiming->pll_n = 1; | ||
387 | currentTiming->pll_p = 0; | ||
388 | break; | ||
389 | default: | ||
390 | fp_wid = fp_hgt = fp_vbs = fp_vbe = 0xfff; | ||
391 | } | ||
392 | |||
393 | outputVal = 0; | ||
394 | SET_DBE_FIELD(FP_DE, FP_DE_ON, outputVal, fp_vbs); | ||
395 | SET_DBE_FIELD(FP_DE, FP_DE_OFF, outputVal, fp_vbe); | ||
396 | DBE_SETREG(fp_de, outputVal); | ||
397 | outputVal = 0; | ||
398 | SET_DBE_FIELD(FP_HDRV, FP_HDRV_OFF, outputVal, fp_wid); | ||
399 | DBE_SETREG(fp_hdrv, outputVal); | ||
400 | outputVal = 0; | ||
401 | SET_DBE_FIELD(FP_VDRV, FP_VDRV_ON, outputVal, 1); | ||
402 | SET_DBE_FIELD(FP_VDRV, FP_VDRV_OFF, outputVal, fp_hgt + 1); | ||
403 | DBE_SETREG(fp_vdrv, outputVal); | ||
404 | } | ||
405 | |||
406 | /* | ||
407 | * Set the hardware according to 'par'. | ||
408 | */ | ||
409 | static int sgivwfb_set_par(struct fb_info *info) | ||
410 | { | ||
411 | struct sgivw_par *par = info->par; | ||
412 | int i, j, htmp, temp; | ||
413 | u32 readVal, outputVal; | ||
414 | int wholeTilesX, maxPixelsPerTileX; | ||
415 | int frmWrite1, frmWrite2, frmWrite3b; | ||
416 | struct dbe_timing_info *currentTiming; /* Current Video Timing */ | ||
417 | int xpmax, ypmax; // Monitor resolution | ||
418 | int bytesPerPixel; // Bytes per pixel | ||
419 | |||
420 | currentTiming = &dbeVTimings[par->timing_num]; | ||
421 | bytesPerPixel = bytes_per_pixel(info->var.bits_per_pixel); | ||
422 | xpmax = currentTiming->width; | ||
423 | ypmax = currentTiming->height; | ||
424 | |||
425 | /* dbe_InitGraphicsBase(); */ | ||
426 | /* Turn on dotclock PLL */ | ||
427 | DBE_SETREG(ctrlstat, 0x20000000); | ||
428 | |||
429 | dbe_TurnOffDma(par); | ||
430 | |||
431 | /* dbe_CalculateScreenParams(); */ | ||
432 | maxPixelsPerTileX = 512 / bytesPerPixel; | ||
433 | wholeTilesX = xpmax / maxPixelsPerTileX; | ||
434 | if (wholeTilesX * maxPixelsPerTileX < xpmax) | ||
435 | wholeTilesX++; | ||
436 | |||
437 | printk(KERN_DEBUG "sgivwfb: pixPerTile=%d wholeTilesX=%d\n", | ||
438 | maxPixelsPerTileX, wholeTilesX); | ||
439 | |||
440 | /* dbe_InitGammaMap(); */ | ||
441 | udelay(10); | ||
442 | |||
443 | for (i = 0; i < 256; i++) { | ||
444 | DBE_ISETREG(gmap, i, (i << 24) | (i << 16) | (i << 8)); | ||
445 | } | ||
446 | |||
447 | /* dbe_TurnOn(); */ | ||
448 | DBE_GETREG(vt_xy, readVal); | ||
449 | if (GET_DBE_FIELD(VT_XY, VT_FREEZE, readVal) == 1) { | ||
450 | DBE_SETREG(vt_xy, 0x00000000); | ||
451 | udelay(1); | ||
452 | } else | ||
453 | dbe_TurnOffDma(par); | ||
454 | |||
455 | /* dbe_Initdbe(); */ | ||
456 | for (i = 0; i < 256; i++) { | ||
457 | for (j = 0; j < 100; j++) { | ||
458 | DBE_GETREG(cm_fifo, readVal); | ||
459 | if (readVal != 0x00000000) | ||
460 | break; | ||
461 | else | ||
462 | udelay(10); | ||
463 | } | ||
464 | |||
465 | // DBE_ISETREG(cmap, i, 0x00000000); | ||
466 | DBE_ISETREG(cmap, i, (i << 8) | (i << 16) | (i << 24)); | ||
467 | } | ||
468 | |||
469 | /* dbe_InitFramebuffer(); */ | ||
470 | frmWrite1 = 0; | ||
471 | SET_DBE_FIELD(FRM_SIZE_TILE, FRM_WIDTH_TILE, frmWrite1, | ||
472 | wholeTilesX); | ||
473 | SET_DBE_FIELD(FRM_SIZE_TILE, FRM_RHS, frmWrite1, 0); | ||
474 | |||
475 | switch (bytesPerPixel) { | ||
476 | case 1: | ||
477 | SET_DBE_FIELD(FRM_SIZE_TILE, FRM_DEPTH, frmWrite1, | ||
478 | DBE_FRM_DEPTH_8); | ||
479 | break; | ||
480 | case 2: | ||
481 | SET_DBE_FIELD(FRM_SIZE_TILE, FRM_DEPTH, frmWrite1, | ||
482 | DBE_FRM_DEPTH_16); | ||
483 | break; | ||
484 | case 4: | ||
485 | SET_DBE_FIELD(FRM_SIZE_TILE, FRM_DEPTH, frmWrite1, | ||
486 | DBE_FRM_DEPTH_32); | ||
487 | break; | ||
488 | } | ||
489 | |||
490 | frmWrite2 = 0; | ||
491 | SET_DBE_FIELD(FRM_SIZE_PIXEL, FB_HEIGHT_PIX, frmWrite2, ypmax); | ||
492 | |||
493 | // Tell dbe about the framebuffer location and type | ||
494 | // XXX What format is the FRM_TILE_PTR?? 64K aligned address? | ||
495 | frmWrite3b = 0; | ||
496 | SET_DBE_FIELD(FRM_CONTROL, FRM_TILE_PTR, frmWrite3b, | ||
497 | sgivwfb_mem_phys >> 9); | ||
498 | SET_DBE_FIELD(FRM_CONTROL, FRM_DMA_ENABLE, frmWrite3b, 1); | ||
499 | SET_DBE_FIELD(FRM_CONTROL, FRM_LINEAR, frmWrite3b, 1); | ||
500 | |||
501 | /* Initialize DIDs */ | ||
502 | |||
503 | outputVal = 0; | ||
504 | switch (bytesPerPixel) { | ||
505 | case 1: | ||
506 | SET_DBE_FIELD(WID, TYP, outputVal, DBE_CMODE_I8); | ||
507 | break; | ||
508 | case 2: | ||
509 | SET_DBE_FIELD(WID, TYP, outputVal, DBE_CMODE_RGBA5); | ||
510 | break; | ||
511 | case 4: | ||
512 | SET_DBE_FIELD(WID, TYP, outputVal, DBE_CMODE_RGB8); | ||
513 | break; | ||
514 | } | ||
515 | SET_DBE_FIELD(WID, BUF, outputVal, DBE_BMODE_BOTH); | ||
516 | |||
517 | for (i = 0; i < 32; i++) { | ||
518 | DBE_ISETREG(mode_regs, i, outputVal); | ||
519 | } | ||
520 | |||
521 | /* dbe_InitTiming(); */ | ||
522 | DBE_SETREG(vt_intr01, 0xffffffff); | ||
523 | DBE_SETREG(vt_intr23, 0xffffffff); | ||
524 | |||
525 | DBE_GETREG(dotclock, readVal); | ||
526 | DBE_SETREG(dotclock, readVal & 0xffff); | ||
527 | |||
528 | DBE_SETREG(vt_xymax, 0x00000000); | ||
529 | outputVal = 0; | ||
530 | SET_DBE_FIELD(VT_VSYNC, VT_VSYNC_ON, outputVal, | ||
531 | currentTiming->vsync_start); | ||
532 | SET_DBE_FIELD(VT_VSYNC, VT_VSYNC_OFF, outputVal, | ||
533 | currentTiming->vsync_end); | ||
534 | DBE_SETREG(vt_vsync, outputVal); | ||
535 | outputVal = 0; | ||
536 | SET_DBE_FIELD(VT_HSYNC, VT_HSYNC_ON, outputVal, | ||
537 | currentTiming->hsync_start); | ||
538 | SET_DBE_FIELD(VT_HSYNC, VT_HSYNC_OFF, outputVal, | ||
539 | currentTiming->hsync_end); | ||
540 | DBE_SETREG(vt_hsync, outputVal); | ||
541 | outputVal = 0; | ||
542 | SET_DBE_FIELD(VT_VBLANK, VT_VBLANK_ON, outputVal, | ||
543 | currentTiming->vblank_start); | ||
544 | SET_DBE_FIELD(VT_VBLANK, VT_VBLANK_OFF, outputVal, | ||
545 | currentTiming->vblank_end); | ||
546 | DBE_SETREG(vt_vblank, outputVal); | ||
547 | outputVal = 0; | ||
548 | SET_DBE_FIELD(VT_HBLANK, VT_HBLANK_ON, outputVal, | ||
549 | currentTiming->hblank_start); | ||
550 | SET_DBE_FIELD(VT_HBLANK, VT_HBLANK_OFF, outputVal, | ||
551 | currentTiming->hblank_end - 3); | ||
552 | DBE_SETREG(vt_hblank, outputVal); | ||
553 | outputVal = 0; | ||
554 | SET_DBE_FIELD(VT_VCMAP, VT_VCMAP_ON, outputVal, | ||
555 | currentTiming->vblank_start); | ||
556 | SET_DBE_FIELD(VT_VCMAP, VT_VCMAP_OFF, outputVal, | ||
557 | currentTiming->vblank_end); | ||
558 | DBE_SETREG(vt_vcmap, outputVal); | ||
559 | outputVal = 0; | ||
560 | SET_DBE_FIELD(VT_HCMAP, VT_HCMAP_ON, outputVal, | ||
561 | currentTiming->hblank_start); | ||
562 | SET_DBE_FIELD(VT_HCMAP, VT_HCMAP_OFF, outputVal, | ||
563 | currentTiming->hblank_end - 3); | ||
564 | DBE_SETREG(vt_hcmap, outputVal); | ||
565 | |||
566 | if (flatpanel_id != -1) | ||
567 | sgivwfb_setup_flatpanel(par, currentTiming); | ||
568 | |||
569 | outputVal = 0; | ||
570 | temp = currentTiming->vblank_start - currentTiming->vblank_end - 1; | ||
571 | if (temp > 0) | ||
572 | temp = -temp; | ||
573 | |||
574 | SET_DBE_FIELD(DID_START_XY, DID_STARTY, outputVal, (u32) temp); | ||
575 | if (currentTiming->hblank_end >= 20) | ||
576 | SET_DBE_FIELD(DID_START_XY, DID_STARTX, outputVal, | ||
577 | currentTiming->hblank_end - 20); | ||
578 | else | ||
579 | SET_DBE_FIELD(DID_START_XY, DID_STARTX, outputVal, | ||
580 | currentTiming->htotal - (20 - | ||
581 | currentTiming-> | ||
582 | hblank_end)); | ||
583 | DBE_SETREG(did_start_xy, outputVal); | ||
584 | |||
585 | outputVal = 0; | ||
586 | SET_DBE_FIELD(CRS_START_XY, CRS_STARTY, outputVal, | ||
587 | (u32) (temp + 1)); | ||
588 | if (currentTiming->hblank_end >= DBE_CRS_MAGIC) | ||
589 | SET_DBE_FIELD(CRS_START_XY, CRS_STARTX, outputVal, | ||
590 | currentTiming->hblank_end - DBE_CRS_MAGIC); | ||
591 | else | ||
592 | SET_DBE_FIELD(CRS_START_XY, CRS_STARTX, outputVal, | ||
593 | currentTiming->htotal - (DBE_CRS_MAGIC - | ||
594 | currentTiming-> | ||
595 | hblank_end)); | ||
596 | DBE_SETREG(crs_start_xy, outputVal); | ||
597 | |||
598 | outputVal = 0; | ||
599 | SET_DBE_FIELD(VC_START_XY, VC_STARTY, outputVal, (u32) temp); | ||
600 | SET_DBE_FIELD(VC_START_XY, VC_STARTX, outputVal, | ||
601 | currentTiming->hblank_end - 4); | ||
602 | DBE_SETREG(vc_start_xy, outputVal); | ||
603 | |||
604 | DBE_SETREG(frm_size_tile, frmWrite1); | ||
605 | DBE_SETREG(frm_size_pixel, frmWrite2); | ||
606 | |||
607 | outputVal = 0; | ||
608 | SET_DBE_FIELD(DOTCLK, M, outputVal, currentTiming->pll_m - 1); | ||
609 | SET_DBE_FIELD(DOTCLK, N, outputVal, currentTiming->pll_n - 1); | ||
610 | SET_DBE_FIELD(DOTCLK, P, outputVal, currentTiming->pll_p); | ||
611 | SET_DBE_FIELD(DOTCLK, RUN, outputVal, 1); | ||
612 | DBE_SETREG(dotclock, outputVal); | ||
613 | |||
614 | udelay(11 * 1000); | ||
615 | |||
616 | DBE_SETREG(vt_vpixen, 0xffffff); | ||
617 | DBE_SETREG(vt_hpixen, 0xffffff); | ||
618 | |||
619 | outputVal = 0; | ||
620 | SET_DBE_FIELD(VT_XYMAX, VT_MAXX, outputVal, currentTiming->htotal); | ||
621 | SET_DBE_FIELD(VT_XYMAX, VT_MAXY, outputVal, currentTiming->vtotal); | ||
622 | DBE_SETREG(vt_xymax, outputVal); | ||
623 | |||
624 | outputVal = frmWrite1; | ||
625 | SET_DBE_FIELD(FRM_SIZE_TILE, FRM_FIFO_RESET, outputVal, 1); | ||
626 | DBE_SETREG(frm_size_tile, outputVal); | ||
627 | DBE_SETREG(frm_size_tile, frmWrite1); | ||
628 | |||
629 | outputVal = 0; | ||
630 | SET_DBE_FIELD(OVR_WIDTH_TILE, OVR_FIFO_RESET, outputVal, 1); | ||
631 | DBE_SETREG(ovr_width_tile, outputVal); | ||
632 | DBE_SETREG(ovr_width_tile, 0); | ||
633 | |||
634 | DBE_SETREG(frm_control, frmWrite3b); | ||
635 | DBE_SETREG(did_control, 0); | ||
636 | |||
637 | // Wait for dbe to take frame settings | ||
638 | for (i = 0; i < 100000; i++) { | ||
639 | DBE_GETREG(frm_inhwctrl, readVal); | ||
640 | if (GET_DBE_FIELD(FRM_INHWCTRL, FRM_DMA_ENABLE, readVal) != | ||
641 | 0) | ||
642 | break; | ||
643 | else | ||
644 | udelay(1); | ||
645 | } | ||
646 | |||
647 | if (i == 100000) | ||
648 | printk(KERN_INFO | ||
649 | "sgivwfb: timeout waiting for frame DMA enable.\n"); | ||
650 | |||
651 | outputVal = 0; | ||
652 | htmp = currentTiming->hblank_end - 19; | ||
653 | if (htmp < 0) | ||
654 | htmp += currentTiming->htotal; /* allow blank to wrap around */ | ||
655 | SET_DBE_FIELD(VT_HPIXEN, VT_HPIXEN_ON, outputVal, htmp); | ||
656 | SET_DBE_FIELD(VT_HPIXEN, VT_HPIXEN_OFF, outputVal, | ||
657 | ((htmp + currentTiming->width - | ||
658 | 2) % currentTiming->htotal)); | ||
659 | DBE_SETREG(vt_hpixen, outputVal); | ||
660 | |||
661 | outputVal = 0; | ||
662 | SET_DBE_FIELD(VT_VPIXEN, VT_VPIXEN_OFF, outputVal, | ||
663 | currentTiming->vblank_start); | ||
664 | SET_DBE_FIELD(VT_VPIXEN, VT_VPIXEN_ON, outputVal, | ||
665 | currentTiming->vblank_end); | ||
666 | DBE_SETREG(vt_vpixen, outputVal); | ||
667 | |||
668 | // Turn off mouse cursor | ||
669 | par->regs->crs_ctl = 0; | ||
670 | |||
671 | // XXX What's this section for?? | ||
672 | DBE_GETREG(ctrlstat, readVal); | ||
673 | readVal &= 0x02000000; | ||
674 | |||
675 | if (readVal != 0) { | ||
676 | DBE_SETREG(ctrlstat, 0x30000000); | ||
677 | } | ||
678 | return 0; | ||
679 | } | ||
680 | |||
681 | /* | ||
682 | * Set a single color register. The values supplied are already | ||
683 | * rounded down to the hardware's capabilities (according to the | ||
684 | * entries in the var structure). Return != 0 for invalid regno. | ||
685 | */ | ||
686 | |||
687 | static int sgivwfb_setcolreg(u_int regno, u_int red, u_int green, | ||
688 | u_int blue, u_int transp, | ||
689 | struct fb_info *info) | ||
690 | { | ||
691 | struct sgivw_par *par = (struct sgivw_par *) info->par; | ||
692 | |||
693 | if (regno > 255) | ||
694 | return 1; | ||
695 | red >>= 8; | ||
696 | green >>= 8; | ||
697 | blue >>= 8; | ||
698 | |||
699 | /* wait for the color map FIFO to have a free entry */ | ||
700 | while (par->cmap_fifo == 0) | ||
701 | par->cmap_fifo = par->regs->cm_fifo; | ||
702 | |||
703 | par->regs->cmap[regno] = (red << 24) | (green << 16) | (blue << 8); | ||
704 | par->cmap_fifo--; /* assume FIFO is filling up */ | ||
705 | return 0; | ||
706 | } | ||
707 | |||
708 | static int sgivwfb_mmap(struct fb_info *info, struct file *file, | ||
709 | struct vm_area_struct *vma) | ||
710 | { | ||
711 | unsigned long size = vma->vm_end - vma->vm_start; | ||
712 | unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; | ||
713 | |||
714 | if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) | ||
715 | return -EINVAL; | ||
716 | if (offset + size > sgivwfb_mem_size) | ||
717 | return -EINVAL; | ||
718 | offset += sgivwfb_mem_phys; | ||
719 | pgprot_val(vma->vm_page_prot) = | ||
720 | pgprot_val(vma->vm_page_prot) | _PAGE_PCD; | ||
721 | vma->vm_flags |= VM_IO; | ||
722 | if (remap_pfn_range(vma, vma->vm_start, offset >> PAGE_SHIFT, | ||
723 | size, vma->vm_page_prot)) | ||
724 | return -EAGAIN; | ||
725 | vma->vm_file = file; | ||
726 | printk(KERN_DEBUG "sgivwfb: mmap framebuffer P(%lx)->V(%lx)\n", | ||
727 | offset, vma->vm_start); | ||
728 | return 0; | ||
729 | } | ||
730 | |||
731 | int __init sgivwfb_setup(char *options) | ||
732 | { | ||
733 | char *this_opt; | ||
734 | |||
735 | if (!options || !*options) | ||
736 | return 0; | ||
737 | |||
738 | while ((this_opt = strsep(&options, ",")) != NULL) { | ||
739 | if (!strncmp(this_opt, "monitor:", 8)) { | ||
740 | if (!strncmp(this_opt + 8, "crt", 3)) | ||
741 | flatpanel_id = -1; | ||
742 | else if (!strncmp(this_opt + 8, "1600sw", 6)) | ||
743 | flatpanel_id = FLATPANEL_SGI_1600SW; | ||
744 | } | ||
745 | } | ||
746 | return 0; | ||
747 | } | ||
748 | |||
749 | /* | ||
750 | * Initialisation | ||
751 | */ | ||
752 | static void sgivwfb_release(struct device *device) | ||
753 | { | ||
754 | } | ||
755 | |||
756 | static int __init sgivwfb_probe(struct device *device) | ||
757 | { | ||
758 | struct platform_device *dev = to_platform_device(device); | ||
759 | struct sgivw_par *par; | ||
760 | struct fb_info *info; | ||
761 | char *monitor; | ||
762 | |||
763 | info = framebuffer_alloc(sizeof(struct sgivw_par) + sizeof(u32) * 256, &dev->dev); | ||
764 | if (!info) | ||
765 | return -ENOMEM; | ||
766 | par = info->par; | ||
767 | |||
768 | if (!request_mem_region(DBE_REG_PHYS, DBE_REG_SIZE, "sgivwfb")) { | ||
769 | printk(KERN_ERR "sgivwfb: couldn't reserve mmio region\n"); | ||
770 | framebuffer_release(info); | ||
771 | return -EBUSY; | ||
772 | } | ||
773 | |||
774 | par->regs = (struct asregs *) ioremap_nocache(DBE_REG_PHYS, DBE_REG_SIZE); | ||
775 | if (!par->regs) { | ||
776 | printk(KERN_ERR "sgivwfb: couldn't ioremap registers\n"); | ||
777 | goto fail_ioremap_regs; | ||
778 | } | ||
779 | |||
780 | mtrr_add(sgivwfb_mem_phys, sgivwfb_mem_size, MTRR_TYPE_WRCOMB, 1); | ||
781 | |||
782 | sgivwfb_fix.smem_start = sgivwfb_mem_phys; | ||
783 | sgivwfb_fix.smem_len = sgivwfb_mem_size; | ||
784 | sgivwfb_fix.ywrapstep = ywrap; | ||
785 | sgivwfb_fix.ypanstep = ypan; | ||
786 | |||
787 | info->fix = sgivwfb_fix; | ||
788 | |||
789 | switch (flatpanel_id) { | ||
790 | case FLATPANEL_SGI_1600SW: | ||
791 | info->var = sgivwfb_var1600sw; | ||
792 | monitor = "SGI 1600SW flatpanel"; | ||
793 | break; | ||
794 | default: | ||
795 | info->var = sgivwfb_var; | ||
796 | monitor = "CRT"; | ||
797 | } | ||
798 | |||
799 | printk(KERN_INFO "sgivwfb: %s monitor selected\n", monitor); | ||
800 | |||
801 | info->fbops = &sgivwfb_ops; | ||
802 | info->pseudo_palette = (void *) (par + 1); | ||
803 | info->flags = FBINFO_DEFAULT; | ||
804 | |||
805 | info->screen_base = ioremap_nocache((unsigned long) sgivwfb_mem_phys, sgivwfb_mem_size); | ||
806 | if (!info->screen_base) { | ||
807 | printk(KERN_ERR "sgivwfb: couldn't ioremap screen_base\n"); | ||
808 | goto fail_ioremap_fbmem; | ||
809 | } | ||
810 | |||
811 | if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) | ||
812 | goto fail_color_map; | ||
813 | |||
814 | if (register_framebuffer(info) < 0) { | ||
815 | printk(KERN_ERR "sgivwfb: couldn't register framebuffer\n"); | ||
816 | goto fail_register_framebuffer; | ||
817 | } | ||
818 | |||
819 | dev_set_drvdata(&dev->dev, info); | ||
820 | |||
821 | printk(KERN_INFO "fb%d: SGI DBE frame buffer device, using %ldK of video memory at %#lx\n", | ||
822 | info->node, sgivwfb_mem_size >> 10, sgivwfb_mem_phys); | ||
823 | return 0; | ||
824 | |||
825 | fail_register_framebuffer: | ||
826 | fb_dealloc_cmap(&info->cmap); | ||
827 | fail_color_map: | ||
828 | iounmap((char *) info->screen_base); | ||
829 | fail_ioremap_fbmem: | ||
830 | iounmap(par->regs); | ||
831 | fail_ioremap_regs: | ||
832 | release_mem_region(DBE_REG_PHYS, DBE_REG_SIZE); | ||
833 | framebuffer_release(info); | ||
834 | return -ENXIO; | ||
835 | } | ||
836 | |||
837 | static int sgivwfb_remove(struct device *device) | ||
838 | { | ||
839 | struct fb_info *info = dev_get_drvdata(device); | ||
840 | |||
841 | if (info) { | ||
842 | struct sgivw_par *par = info->par; | ||
843 | |||
844 | unregister_framebuffer(info); | ||
845 | dbe_TurnOffDma(par); | ||
846 | iounmap(par->regs); | ||
847 | iounmap(info->screen_base); | ||
848 | release_mem_region(DBE_REG_PHYS, DBE_REG_SIZE); | ||
849 | } | ||
850 | return 0; | ||
851 | } | ||
852 | |||
853 | static struct device_driver sgivwfb_driver = { | ||
854 | .name = "sgivwfb", | ||
855 | .bus = &platform_bus_type, | ||
856 | .probe = sgivwfb_probe, | ||
857 | .remove = sgivwfb_remove, | ||
858 | }; | ||
859 | |||
860 | static struct platform_device sgivwfb_device = { | ||
861 | .name = "sgivwfb", | ||
862 | .id = 0, | ||
863 | .dev = { | ||
864 | .release = sgivwfb_release, | ||
865 | } | ||
866 | }; | ||
867 | |||
868 | int __init sgivwfb_init(void) | ||
869 | { | ||
870 | int ret; | ||
871 | |||
872 | #ifndef MODULE | ||
873 | char *option = NULL; | ||
874 | |||
875 | if (fb_get_options("sgivwfb", &option)) | ||
876 | return -ENODEV; | ||
877 | sgivwfb_setup(option); | ||
878 | #endif | ||
879 | ret = driver_register(&sgivwfb_driver); | ||
880 | if (!ret) { | ||
881 | ret = platform_device_register(&sgivwfb_device); | ||
882 | if (ret) | ||
883 | driver_unregister(&sgivwfb_driver); | ||
884 | } | ||
885 | return ret; | ||
886 | } | ||
887 | |||
888 | module_init(sgivwfb_init); | ||
889 | |||
890 | #ifdef MODULE | ||
891 | MODULE_LICENSE("GPL"); | ||
892 | |||
893 | static void __exit sgivwfb_exit(void) | ||
894 | { | ||
895 | platform_device_unregister(&sgivwfb_device); | ||
896 | driver_unregister(&sgivwfb_driver); | ||
897 | } | ||
898 | |||
899 | module_exit(sgivwfb_exit); | ||
900 | |||
901 | #endif /* MODULE */ | ||