Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * | |
3 | * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200, G400 and G450. | |
4 | * | |
5 | * (c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz> | |
6 | * | |
7 | * Portions Copyright (c) 2001 Matrox Graphics Inc. | |
8 | * | |
9 | * Version: 1.65 2002/08/14 | |
10 | * | |
11 | */ | |
12 | ||
13 | #include "matroxfb_maven.h" | |
14 | #include "matroxfb_crtc2.h" | |
15 | #include "matroxfb_misc.h" | |
16 | #include "matroxfb_DAC1064.h" | |
17 | #include <linux/matroxfb.h> | |
18 | #include <asm/uaccess.h> | |
19 | ||
20 | /* **************************************************** */ | |
21 | ||
22 | static int mem = 8192; | |
23 | ||
24 | module_param(mem, int, 0); | |
25 | MODULE_PARM_DESC(mem, "Memory size reserved for dualhead (default=8MB)"); | |
26 | ||
27 | /* **************************************************** */ | |
28 | ||
29 | static int matroxfb_dh_setcolreg(unsigned regno, unsigned red, unsigned green, | |
30 | unsigned blue, unsigned transp, struct fb_info* info) { | |
31 | u_int32_t col; | |
32 | #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) | |
33 | ||
34 | if (regno >= 16) | |
35 | return 1; | |
36 | if (m2info->fbcon.var.grayscale) { | |
37 | /* gray = 0.30*R + 0.59*G + 0.11*B */ | |
38 | red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8; | |
39 | } | |
40 | red = CNVT_TOHW(red, m2info->fbcon.var.red.length); | |
41 | green = CNVT_TOHW(green, m2info->fbcon.var.green.length); | |
42 | blue = CNVT_TOHW(blue, m2info->fbcon.var.blue.length); | |
43 | transp = CNVT_TOHW(transp, m2info->fbcon.var.transp.length); | |
44 | ||
45 | col = (red << m2info->fbcon.var.red.offset) | | |
46 | (green << m2info->fbcon.var.green.offset) | | |
47 | (blue << m2info->fbcon.var.blue.offset) | | |
48 | (transp << m2info->fbcon.var.transp.offset); | |
49 | ||
50 | switch (m2info->fbcon.var.bits_per_pixel) { | |
51 | case 16: | |
52 | m2info->cmap[regno] = col | (col << 16); | |
53 | break; | |
54 | case 32: | |
55 | m2info->cmap[regno] = col; | |
56 | break; | |
57 | } | |
58 | return 0; | |
59 | #undef m2info | |
60 | } | |
61 | ||
62 | static void matroxfb_dh_restore(struct matroxfb_dh_fb_info* m2info, | |
63 | struct my_timming* mt, | |
64 | int mode, | |
65 | unsigned int pos) { | |
66 | u_int32_t tmp; | |
67 | u_int32_t datactl; | |
68 | MINFO_FROM(m2info->primary_dev); | |
69 | ||
70 | switch (mode) { | |
71 | case 15: | |
72 | tmp = 0x00200000; | |
73 | break; | |
74 | case 16: | |
75 | tmp = 0x00400000; | |
76 | break; | |
77 | /* case 32: */ | |
78 | default: | |
79 | tmp = 0x00800000; | |
80 | break; | |
81 | } | |
82 | tmp |= 0x00000001; /* enable CRTC2 */ | |
83 | datactl = 0; | |
84 | if (ACCESS_FBINFO(outputs[1]).src == MATROXFB_SRC_CRTC2) { | |
85 | if (ACCESS_FBINFO(devflags.g450dac)) { | |
86 | tmp |= 0x00000006; /* source from secondary pixel PLL */ | |
87 | /* no vidrst when in monitor mode */ | |
88 | if (ACCESS_FBINFO(outputs[1]).mode != MATROXFB_OUTPUT_MODE_MONITOR) { | |
89 | tmp |= 0xC0001000; /* Enable H/V vidrst */ | |
90 | } | |
91 | } else { | |
92 | tmp |= 0x00000002; /* source from VDOCLK */ | |
93 | tmp |= 0xC0000000; /* enable vvidrst & hvidrst */ | |
94 | /* MGA TVO is our clock source */ | |
95 | } | |
96 | } else if (ACCESS_FBINFO(outputs[0]).src == MATROXFB_SRC_CRTC2) { | |
97 | tmp |= 0x00000004; /* source from pixclock */ | |
98 | /* PIXPLL is our clock source */ | |
99 | } | |
100 | if (ACCESS_FBINFO(outputs[0]).src == MATROXFB_SRC_CRTC2) { | |
101 | tmp |= 0x00100000; /* connect CRTC2 to DAC */ | |
102 | } | |
103 | if (mt->interlaced) { | |
104 | tmp |= 0x02000000; /* interlaced, second field is bigger, as G450 apparently ignores it */ | |
105 | mt->VDisplay >>= 1; | |
106 | mt->VSyncStart >>= 1; | |
107 | mt->VSyncEnd >>= 1; | |
108 | mt->VTotal >>= 1; | |
109 | } | |
110 | if ((mt->HTotal & 7) == 2) { | |
111 | datactl |= 0x00000010; | |
112 | mt->HTotal &= ~7; | |
113 | } | |
114 | tmp |= 0x10000000; /* 0x10000000 is VIDRST polarity */ | |
115 | mga_outl(0x3C14, ((mt->HDisplay - 8) << 16) | (mt->HTotal - 8)); | |
116 | mga_outl(0x3C18, ((mt->HSyncEnd - 8) << 16) | (mt->HSyncStart - 8)); | |
117 | mga_outl(0x3C1C, ((mt->VDisplay - 1) << 16) | (mt->VTotal - 1)); | |
118 | mga_outl(0x3C20, ((mt->VSyncEnd - 1) << 16) | (mt->VSyncStart - 1)); | |
119 | mga_outl(0x3C24, ((mt->VSyncStart) << 16) | (mt->HSyncStart)); /* preload */ | |
120 | { | |
121 | u_int32_t linelen = m2info->fbcon.var.xres_virtual * (m2info->fbcon.var.bits_per_pixel >> 3); | |
122 | if (tmp & 0x02000000) { | |
123 | /* field #0 is smaller, so... */ | |
124 | mga_outl(0x3C2C, pos); /* field #1 vmemory start */ | |
125 | mga_outl(0x3C28, pos + linelen); /* field #0 vmemory start */ | |
126 | linelen <<= 1; | |
127 | m2info->interlaced = 1; | |
128 | } else { | |
129 | mga_outl(0x3C28, pos); /* vmemory start */ | |
130 | m2info->interlaced = 0; | |
131 | } | |
132 | mga_outl(0x3C40, linelen); | |
133 | } | |
134 | mga_outl(0x3C4C, datactl); /* data control */ | |
135 | if (tmp & 0x02000000) { | |
136 | int i; | |
137 | ||
138 | mga_outl(0x3C10, tmp & ~0x02000000); | |
139 | for (i = 0; i < 2; i++) { | |
140 | unsigned int nl; | |
141 | unsigned int lastl = 0; | |
142 | ||
143 | while ((nl = mga_inl(0x3C48) & 0xFFF) >= lastl) { | |
144 | lastl = nl; | |
145 | } | |
146 | } | |
147 | } | |
148 | mga_outl(0x3C10, tmp); | |
149 | ACCESS_FBINFO(hw).crtc2.ctl = tmp; | |
150 | ||
151 | tmp = mt->VDisplay << 16; /* line compare */ | |
152 | if (mt->sync & FB_SYNC_HOR_HIGH_ACT) | |
153 | tmp |= 0x00000100; | |
154 | if (mt->sync & FB_SYNC_VERT_HIGH_ACT) | |
155 | tmp |= 0x00000200; | |
156 | mga_outl(0x3C44, tmp); | |
157 | } | |
158 | ||
159 | static void matroxfb_dh_disable(struct matroxfb_dh_fb_info* m2info) { | |
160 | MINFO_FROM(m2info->primary_dev); | |
161 | ||
162 | mga_outl(0x3C10, 0x00000004); /* disable CRTC2, CRTC1->DAC1, PLL as clock source */ | |
163 | ACCESS_FBINFO(hw).crtc2.ctl = 0x00000004; | |
164 | } | |
165 | ||
166 | static void matroxfb_dh_cfbX_init(struct matroxfb_dh_fb_info* m2info) { | |
167 | /* no acceleration for secondary head... */ | |
168 | m2info->cmap[16] = 0xFFFFFFFF; | |
169 | } | |
170 | ||
171 | static void matroxfb_dh_pan_var(struct matroxfb_dh_fb_info* m2info, | |
172 | struct fb_var_screeninfo* var) { | |
173 | unsigned int pos; | |
174 | unsigned int linelen; | |
175 | unsigned int pixelsize; | |
176 | MINFO_FROM(m2info->primary_dev); | |
177 | ||
178 | m2info->fbcon.var.xoffset = var->xoffset; | |
179 | m2info->fbcon.var.yoffset = var->yoffset; | |
180 | pixelsize = m2info->fbcon.var.bits_per_pixel >> 3; | |
181 | linelen = m2info->fbcon.var.xres_virtual * pixelsize; | |
182 | pos = m2info->fbcon.var.yoffset * linelen + m2info->fbcon.var.xoffset * pixelsize; | |
183 | pos += m2info->video.offbase; | |
184 | if (m2info->interlaced) { | |
185 | mga_outl(0x3C2C, pos); | |
186 | mga_outl(0x3C28, pos + linelen); | |
187 | } else { | |
188 | mga_outl(0x3C28, pos); | |
189 | } | |
190 | } | |
191 | ||
192 | static int matroxfb_dh_decode_var(struct matroxfb_dh_fb_info* m2info, | |
193 | struct fb_var_screeninfo* var, | |
194 | int *visual, | |
195 | int *video_cmap_len, | |
196 | int *mode) { | |
197 | unsigned int mask; | |
198 | unsigned int memlen; | |
199 | unsigned int vramlen; | |
200 | ||
201 | switch (var->bits_per_pixel) { | |
202 | case 16: mask = 0x1F; | |
203 | break; | |
204 | case 32: mask = 0x0F; | |
205 | break; | |
206 | default: return -EINVAL; | |
207 | } | |
208 | vramlen = m2info->video.len_usable; | |
209 | if (var->yres_virtual < var->yres) | |
210 | var->yres_virtual = var->yres; | |
211 | if (var->xres_virtual < var->xres) | |
212 | var->xres_virtual = var->xres; | |
213 | var->xres_virtual = (var->xres_virtual + mask) & ~mask; | |
214 | if (var->yres_virtual > 32767) | |
215 | return -EINVAL; | |
216 | memlen = var->xres_virtual * var->yres_virtual * (var->bits_per_pixel >> 3); | |
217 | if (memlen > vramlen) | |
218 | return -EINVAL; | |
219 | if (var->xoffset + var->xres > var->xres_virtual) | |
220 | var->xoffset = var->xres_virtual - var->xres; | |
221 | if (var->yoffset + var->yres > var->yres_virtual) | |
222 | var->yoffset = var->yres_virtual - var->yres; | |
223 | ||
224 | var->xres &= ~7; | |
225 | var->left_margin &= ~7; | |
226 | var->right_margin &= ~7; | |
227 | var->hsync_len &= ~7; | |
228 | ||
229 | *mode = var->bits_per_pixel; | |
230 | if (var->bits_per_pixel == 16) { | |
231 | if (var->green.length == 5) { | |
232 | var->red.offset = 10; | |
233 | var->red.length = 5; | |
234 | var->green.offset = 5; | |
235 | var->green.length = 5; | |
236 | var->blue.offset = 0; | |
237 | var->blue.length = 5; | |
238 | var->transp.offset = 15; | |
239 | var->transp.length = 1; | |
240 | *mode = 15; | |
241 | } else { | |
242 | var->red.offset = 11; | |
243 | var->red.length = 5; | |
244 | var->green.offset = 5; | |
245 | var->green.length = 6; | |
246 | var->blue.offset = 0; | |
247 | var->blue.length = 5; | |
248 | var->transp.offset = 0; | |
249 | var->transp.length = 0; | |
250 | } | |
251 | } else { | |
252 | var->red.offset = 16; | |
253 | var->red.length = 8; | |
254 | var->green.offset = 8; | |
255 | var->green.length = 8; | |
256 | var->blue.offset = 0; | |
257 | var->blue.length = 8; | |
258 | var->transp.offset = 24; | |
259 | var->transp.length = 8; | |
260 | } | |
261 | *visual = FB_VISUAL_TRUECOLOR; | |
262 | *video_cmap_len = 16; | |
263 | return 0; | |
264 | } | |
265 | ||
266 | static int matroxfb_dh_open(struct fb_info* info, int user) { | |
267 | #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) | |
268 | MINFO_FROM(m2info->primary_dev); | |
269 | ||
270 | if (MINFO) { | |
271 | int err; | |
272 | ||
273 | if (ACCESS_FBINFO(dead)) { | |
274 | return -ENXIO; | |
275 | } | |
276 | err = ACCESS_FBINFO(fbops).fb_open(&ACCESS_FBINFO(fbcon), user); | |
277 | if (err) { | |
278 | return err; | |
279 | } | |
280 | } | |
281 | return 0; | |
282 | #undef m2info | |
283 | } | |
284 | ||
285 | static int matroxfb_dh_release(struct fb_info* info, int user) { | |
286 | #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) | |
287 | int err = 0; | |
288 | MINFO_FROM(m2info->primary_dev); | |
289 | ||
290 | if (MINFO) { | |
291 | err = ACCESS_FBINFO(fbops).fb_release(&ACCESS_FBINFO(fbcon), user); | |
292 | } | |
293 | return err; | |
294 | #undef m2info | |
295 | } | |
296 | ||
297 | static void matroxfb_dh_init_fix(struct matroxfb_dh_fb_info *m2info) { | |
298 | struct fb_fix_screeninfo *fix = &m2info->fbcon.fix; | |
299 | ||
300 | strcpy(fix->id, "MATROX DH"); | |
301 | ||
302 | fix->smem_start = m2info->video.base; | |
303 | fix->smem_len = m2info->video.len_usable; | |
304 | fix->ypanstep = 1; | |
305 | fix->ywrapstep = 0; | |
306 | fix->xpanstep = 8; /* TBD */ | |
307 | fix->mmio_start = m2info->mmio.base; | |
308 | fix->mmio_len = m2info->mmio.len; | |
309 | fix->accel = 0; /* no accel... */ | |
310 | } | |
311 | ||
312 | static int matroxfb_dh_check_var(struct fb_var_screeninfo* var, struct fb_info* info) { | |
313 | #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) | |
314 | int visual; | |
315 | int cmap_len; | |
316 | int mode; | |
317 | ||
318 | return matroxfb_dh_decode_var(m2info, var, &visual, &cmap_len, &mode); | |
319 | #undef m2info | |
320 | } | |
321 | ||
322 | static int matroxfb_dh_set_par(struct fb_info* info) { | |
323 | #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) | |
324 | int visual; | |
325 | int cmap_len; | |
326 | int mode; | |
327 | int err; | |
328 | struct fb_var_screeninfo* var = &info->var; | |
329 | MINFO_FROM(m2info->primary_dev); | |
330 | ||
331 | if ((err = matroxfb_dh_decode_var(m2info, var, &visual, &cmap_len, &mode)) != 0) | |
332 | return err; | |
333 | /* cmap */ | |
334 | { | |
335 | m2info->fbcon.screen_base = vaddr_va(m2info->video.vbase); | |
336 | m2info->fbcon.fix.visual = visual; | |
337 | m2info->fbcon.fix.type = FB_TYPE_PACKED_PIXELS; | |
338 | m2info->fbcon.fix.type_aux = 0; | |
339 | m2info->fbcon.fix.line_length = (var->xres_virtual * var->bits_per_pixel) >> 3; | |
340 | } | |
341 | { | |
342 | struct my_timming mt; | |
343 | unsigned int pos; | |
344 | int out; | |
345 | int cnt; | |
346 | ||
347 | matroxfb_var2my(&m2info->fbcon.var, &mt); | |
348 | mt.crtc = MATROXFB_SRC_CRTC2; | |
349 | /* CRTC2 delay */ | |
350 | mt.delay = 34; | |
351 | ||
352 | pos = (m2info->fbcon.var.yoffset * m2info->fbcon.var.xres_virtual + m2info->fbcon.var.xoffset) * m2info->fbcon.var.bits_per_pixel >> 3; | |
353 | pos += m2info->video.offbase; | |
354 | cnt = 0; | |
355 | down_read(&ACCESS_FBINFO(altout).lock); | |
356 | for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { | |
357 | if (ACCESS_FBINFO(outputs[out]).src == MATROXFB_SRC_CRTC2) { | |
358 | cnt++; | |
359 | if (ACCESS_FBINFO(outputs[out]).output->compute) { | |
360 | ACCESS_FBINFO(outputs[out]).output->compute(ACCESS_FBINFO(outputs[out]).data, &mt); | |
361 | } | |
362 | } | |
363 | } | |
364 | ACCESS_FBINFO(crtc2).pixclock = mt.pixclock; | |
365 | ACCESS_FBINFO(crtc2).mnp = mt.mnp; | |
366 | up_read(&ACCESS_FBINFO(altout).lock); | |
367 | if (cnt) { | |
368 | matroxfb_dh_restore(m2info, &mt, mode, pos); | |
369 | } else { | |
370 | matroxfb_dh_disable(m2info); | |
371 | } | |
372 | DAC1064_global_init(PMINFO2); | |
373 | DAC1064_global_restore(PMINFO2); | |
374 | down_read(&ACCESS_FBINFO(altout).lock); | |
375 | for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { | |
376 | if (ACCESS_FBINFO(outputs[out]).src == MATROXFB_SRC_CRTC2 && | |
377 | ACCESS_FBINFO(outputs[out]).output->program) { | |
378 | ACCESS_FBINFO(outputs[out]).output->program(ACCESS_FBINFO(outputs[out]).data); | |
379 | } | |
380 | } | |
381 | for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { | |
382 | if (ACCESS_FBINFO(outputs[out]).src == MATROXFB_SRC_CRTC2 && | |
383 | ACCESS_FBINFO(outputs[out]).output->start) { | |
384 | ACCESS_FBINFO(outputs[out]).output->start(ACCESS_FBINFO(outputs[out]).data); | |
385 | } | |
386 | } | |
387 | up_read(&ACCESS_FBINFO(altout).lock); | |
388 | matroxfb_dh_cfbX_init(m2info); | |
389 | } | |
390 | m2info->initialized = 1; | |
391 | return 0; | |
392 | #undef m2info | |
393 | } | |
394 | ||
395 | static int matroxfb_dh_pan_display(struct fb_var_screeninfo* var, struct fb_info* info) { | |
396 | #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) | |
397 | matroxfb_dh_pan_var(m2info, var); | |
398 | return 0; | |
399 | #undef m2info | |
400 | } | |
401 | ||
402 | static int matroxfb_dh_get_vblank(const struct matroxfb_dh_fb_info* m2info, struct fb_vblank* vblank) { | |
403 | MINFO_FROM(m2info->primary_dev); | |
404 | ||
405 | matroxfb_enable_irq(PMINFO 0); | |
406 | memset(vblank, 0, sizeof(*vblank)); | |
407 | vblank->flags = FB_VBLANK_HAVE_VCOUNT | FB_VBLANK_HAVE_VBLANK; | |
408 | /* mask out reserved bits + field number (odd/even) */ | |
409 | vblank->vcount = mga_inl(0x3C48) & 0x000007FF; | |
410 | /* compatibility stuff */ | |
411 | if (vblank->vcount >= m2info->fbcon.var.yres) | |
412 | vblank->flags |= FB_VBLANK_VBLANKING; | |
413 | if (test_bit(0, &ACCESS_FBINFO(irq_flags))) { | |
414 | vblank->flags |= FB_VBLANK_HAVE_COUNT; | |
415 | /* Only one writer, aligned int value... | |
416 | it should work without lock and without atomic_t */ | |
417 | vblank->count = ACCESS_FBINFO(crtc2).vsync.cnt; | |
418 | } | |
419 | return 0; | |
420 | } | |
421 | ||
422 | static int matroxfb_dh_ioctl(struct inode* inode, | |
423 | struct file* file, | |
424 | unsigned int cmd, | |
425 | unsigned long arg, | |
426 | struct fb_info* info) { | |
427 | #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) | |
428 | MINFO_FROM(m2info->primary_dev); | |
429 | ||
430 | DBG(__FUNCTION__) | |
431 | ||
432 | switch (cmd) { | |
433 | case FBIOGET_VBLANK: | |
434 | { | |
435 | struct fb_vblank vblank; | |
436 | int err; | |
437 | ||
438 | err = matroxfb_dh_get_vblank(m2info, &vblank); | |
439 | if (err) | |
440 | return err; | |
441 | if (copy_to_user((void __user *)arg, &vblank, sizeof(vblank))) | |
442 | return -EFAULT; | |
443 | return 0; | |
444 | } | |
445 | case FBIO_WAITFORVSYNC: | |
446 | { | |
447 | u_int32_t crt; | |
448 | ||
449 | if (get_user(crt, (u_int32_t __user *)arg)) | |
450 | return -EFAULT; | |
451 | ||
452 | if (crt != 0) | |
453 | return -ENODEV; | |
454 | return matroxfb_wait_for_sync(PMINFO 1); | |
455 | } | |
456 | case MATROXFB_SET_OUTPUT_MODE: | |
457 | case MATROXFB_GET_OUTPUT_MODE: | |
458 | case MATROXFB_GET_ALL_OUTPUTS: | |
459 | { | |
460 | return ACCESS_FBINFO(fbcon.fbops)->fb_ioctl(inode, file, cmd, arg, &ACCESS_FBINFO(fbcon)); | |
461 | } | |
462 | case MATROXFB_SET_OUTPUT_CONNECTION: | |
463 | { | |
464 | u_int32_t tmp; | |
465 | int out; | |
466 | int changes; | |
467 | ||
468 | if (get_user(tmp, (u_int32_t __user *)arg)) | |
469 | return -EFAULT; | |
470 | for (out = 0; out < 32; out++) { | |
471 | if (tmp & (1 << out)) { | |
472 | if (out >= MATROXFB_MAX_OUTPUTS) | |
473 | return -ENXIO; | |
474 | if (!ACCESS_FBINFO(outputs[out]).output) | |
475 | return -ENXIO; | |
476 | switch (ACCESS_FBINFO(outputs[out]).src) { | |
477 | case MATROXFB_SRC_NONE: | |
478 | case MATROXFB_SRC_CRTC2: | |
479 | break; | |
480 | default: | |
481 | return -EBUSY; | |
482 | } | |
483 | } | |
484 | } | |
485 | if (ACCESS_FBINFO(devflags.panellink)) { | |
486 | if (tmp & MATROXFB_OUTPUT_CONN_DFP) | |
487 | return -EINVAL; | |
488 | if ((ACCESS_FBINFO(outputs[2]).src == MATROXFB_SRC_CRTC1) && tmp) | |
489 | return -EBUSY; | |
490 | } | |
491 | changes = 0; | |
492 | for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { | |
493 | if (tmp & (1 << out)) { | |
494 | if (ACCESS_FBINFO(outputs[out]).src != MATROXFB_SRC_CRTC2) { | |
495 | changes = 1; | |
496 | ACCESS_FBINFO(outputs[out]).src = MATROXFB_SRC_CRTC2; | |
497 | } | |
498 | } else if (ACCESS_FBINFO(outputs[out]).src == MATROXFB_SRC_CRTC2) { | |
499 | changes = 1; | |
500 | ACCESS_FBINFO(outputs[out]).src = MATROXFB_SRC_NONE; | |
501 | } | |
502 | } | |
503 | if (!changes) | |
504 | return 0; | |
505 | matroxfb_dh_set_par(info); | |
506 | return 0; | |
507 | } | |
508 | case MATROXFB_GET_OUTPUT_CONNECTION: | |
509 | { | |
510 | u_int32_t conn = 0; | |
511 | int out; | |
512 | ||
513 | for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { | |
514 | if (ACCESS_FBINFO(outputs[out]).src == MATROXFB_SRC_CRTC2) { | |
515 | conn |= 1 << out; | |
516 | } | |
517 | } | |
518 | if (put_user(conn, (u_int32_t __user *)arg)) | |
519 | return -EFAULT; | |
520 | return 0; | |
521 | } | |
522 | case MATROXFB_GET_AVAILABLE_OUTPUTS: | |
523 | { | |
524 | u_int32_t tmp = 0; | |
525 | int out; | |
526 | ||
527 | for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { | |
528 | if (ACCESS_FBINFO(outputs[out]).output) { | |
529 | switch (ACCESS_FBINFO(outputs[out]).src) { | |
530 | case MATROXFB_SRC_NONE: | |
531 | case MATROXFB_SRC_CRTC2: | |
532 | tmp |= 1 << out; | |
533 | break; | |
534 | } | |
535 | } | |
536 | } | |
537 | if (ACCESS_FBINFO(devflags.panellink)) { | |
538 | tmp &= ~MATROXFB_OUTPUT_CONN_DFP; | |
539 | if (ACCESS_FBINFO(outputs[2]).src == MATROXFB_SRC_CRTC1) { | |
540 | tmp = 0; | |
541 | } | |
542 | } | |
543 | if (put_user(tmp, (u_int32_t __user *)arg)) | |
544 | return -EFAULT; | |
545 | return 0; | |
546 | } | |
547 | } | |
548 | return -ENOTTY; | |
549 | #undef m2info | |
550 | } | |
551 | ||
552 | static int matroxfb_dh_blank(int blank, struct fb_info* info) { | |
553 | #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) | |
554 | switch (blank) { | |
555 | case 1: | |
556 | case 2: | |
557 | case 3: | |
558 | case 4: | |
559 | default:; | |
560 | } | |
561 | /* do something... */ | |
562 | return 0; | |
563 | #undef m2info | |
564 | } | |
565 | ||
566 | static struct fb_ops matroxfb_dh_ops = { | |
567 | .owner = THIS_MODULE, | |
568 | .fb_open = matroxfb_dh_open, | |
569 | .fb_release = matroxfb_dh_release, | |
570 | .fb_check_var = matroxfb_dh_check_var, | |
571 | .fb_set_par = matroxfb_dh_set_par, | |
572 | .fb_setcolreg = matroxfb_dh_setcolreg, | |
573 | .fb_pan_display =matroxfb_dh_pan_display, | |
574 | .fb_blank = matroxfb_dh_blank, | |
575 | .fb_ioctl = matroxfb_dh_ioctl, | |
576 | .fb_fillrect = cfb_fillrect, | |
577 | .fb_copyarea = cfb_copyarea, | |
578 | .fb_imageblit = cfb_imageblit, | |
1da177e4 LT |
579 | }; |
580 | ||
581 | static struct fb_var_screeninfo matroxfb_dh_defined = { | |
582 | 640,480,640,480,/* W,H, virtual W,H */ | |
583 | 0,0, /* offset */ | |
584 | 32, /* depth */ | |
585 | 0, /* gray */ | |
586 | {0,0,0}, /* R */ | |
587 | {0,0,0}, /* G */ | |
588 | {0,0,0}, /* B */ | |
589 | {0,0,0}, /* alpha */ | |
590 | 0, /* nonstd */ | |
591 | FB_ACTIVATE_NOW, | |
592 | -1,-1, /* display size */ | |
593 | 0, /* accel flags */ | |
594 | 39721L,48L,16L,33L,10L, | |
595 | 96L,2,0, /* no sync info */ | |
596 | FB_VMODE_NONINTERLACED, | |
597 | 0, {0,0,0,0,0} | |
598 | }; | |
599 | ||
600 | static int matroxfb_dh_regit(CPMINFO struct matroxfb_dh_fb_info* m2info) { | |
601 | #define minfo (m2info->primary_dev) | |
602 | void* oldcrtc2; | |
603 | ||
604 | m2info->fbcon.fbops = &matroxfb_dh_ops; | |
605 | m2info->fbcon.flags = FBINFO_FLAG_DEFAULT; | |
606 | m2info->fbcon.flags |= FBINFO_HWACCEL_XPAN | | |
607 | FBINFO_HWACCEL_YPAN; | |
608 | m2info->fbcon.pseudo_palette = m2info->cmap; | |
609 | fb_alloc_cmap(&m2info->fbcon.cmap, 256, 1); | |
610 | ||
611 | if (mem < 64) | |
612 | mem *= 1024; | |
613 | if (mem < 64*1024) | |
614 | mem *= 1024; | |
615 | mem &= ~0x00000FFF; /* PAGE_MASK? */ | |
616 | if (ACCESS_FBINFO(video.len_usable) + mem <= ACCESS_FBINFO(video.len)) | |
617 | m2info->video.offbase = ACCESS_FBINFO(video.len) - mem; | |
618 | else if (ACCESS_FBINFO(video.len) < mem) { | |
619 | return -ENOMEM; | |
620 | } else { /* check yres on first head... */ | |
621 | m2info->video.borrowed = mem; | |
622 | ACCESS_FBINFO(video.len_usable) -= mem; | |
623 | m2info->video.offbase = ACCESS_FBINFO(video.len_usable); | |
624 | } | |
625 | m2info->video.base = ACCESS_FBINFO(video.base) + m2info->video.offbase; | |
626 | m2info->video.len = m2info->video.len_usable = m2info->video.len_maximum = mem; | |
627 | m2info->video.vbase.vaddr = vaddr_va(ACCESS_FBINFO(video.vbase)) + m2info->video.offbase; | |
628 | m2info->mmio.base = ACCESS_FBINFO(mmio.base); | |
629 | m2info->mmio.vbase = ACCESS_FBINFO(mmio.vbase); | |
630 | m2info->mmio.len = ACCESS_FBINFO(mmio.len); | |
631 | ||
632 | matroxfb_dh_init_fix(m2info); | |
633 | if (register_framebuffer(&m2info->fbcon)) { | |
634 | return -ENXIO; | |
635 | } | |
636 | if (!m2info->initialized) | |
637 | fb_set_var(&m2info->fbcon, &matroxfb_dh_defined); | |
638 | down_write(&ACCESS_FBINFO(crtc2.lock)); | |
639 | oldcrtc2 = ACCESS_FBINFO(crtc2.info); | |
640 | ACCESS_FBINFO(crtc2.info) = m2info; | |
641 | up_write(&ACCESS_FBINFO(crtc2.lock)); | |
642 | if (oldcrtc2) { | |
643 | printk(KERN_ERR "matroxfb_crtc2: Internal consistency check failed: crtc2 already present: %p\n", | |
644 | oldcrtc2); | |
645 | } | |
646 | return 0; | |
647 | #undef minfo | |
648 | } | |
649 | ||
650 | /* ************************** */ | |
651 | ||
652 | static int matroxfb_dh_registerfb(struct matroxfb_dh_fb_info* m2info) { | |
653 | #define minfo (m2info->primary_dev) | |
654 | if (matroxfb_dh_regit(PMINFO m2info)) { | |
655 | printk(KERN_ERR "matroxfb_crtc2: secondary head failed to register\n"); | |
656 | return -1; | |
657 | } | |
658 | printk(KERN_INFO "matroxfb_crtc2: secondary head of fb%u was registered as fb%u\n", | |
659 | ACCESS_FBINFO(fbcon.node), m2info->fbcon.node); | |
660 | m2info->fbcon_registered = 1; | |
661 | return 0; | |
662 | #undef minfo | |
663 | } | |
664 | ||
665 | static void matroxfb_dh_deregisterfb(struct matroxfb_dh_fb_info* m2info) { | |
666 | #define minfo (m2info->primary_dev) | |
667 | if (m2info->fbcon_registered) { | |
668 | int id; | |
669 | struct matroxfb_dh_fb_info* crtc2; | |
670 | ||
671 | down_write(&ACCESS_FBINFO(crtc2.lock)); | |
672 | crtc2 = ACCESS_FBINFO(crtc2.info); | |
673 | if (crtc2 == m2info) | |
674 | ACCESS_FBINFO(crtc2.info) = NULL; | |
675 | up_write(&ACCESS_FBINFO(crtc2.lock)); | |
676 | if (crtc2 != m2info) { | |
677 | printk(KERN_ERR "matroxfb_crtc2: Internal consistency check failed: crtc2 mismatch at unload: %p != %p\n", | |
678 | crtc2, m2info); | |
679 | printk(KERN_ERR "matroxfb_crtc2: Expect kernel crash after module unload.\n"); | |
680 | return; | |
681 | } | |
682 | id = m2info->fbcon.node; | |
683 | unregister_framebuffer(&m2info->fbcon); | |
684 | /* return memory back to primary head */ | |
685 | ACCESS_FBINFO(video.len_usable) += m2info->video.borrowed; | |
686 | printk(KERN_INFO "matroxfb_crtc2: fb%u unregistered\n", id); | |
687 | m2info->fbcon_registered = 0; | |
688 | } | |
689 | #undef minfo | |
690 | } | |
691 | ||
692 | static void* matroxfb_crtc2_probe(struct matrox_fb_info* minfo) { | |
693 | struct matroxfb_dh_fb_info* m2info; | |
694 | ||
695 | /* hardware is CRTC2 incapable... */ | |
696 | if (!ACCESS_FBINFO(devflags.crtc2)) | |
697 | return NULL; | |
698 | m2info = (struct matroxfb_dh_fb_info*)kmalloc(sizeof(*m2info), GFP_KERNEL); | |
699 | if (!m2info) { | |
700 | printk(KERN_ERR "matroxfb_crtc2: Not enough memory for CRTC2 control structs\n"); | |
701 | return NULL; | |
702 | } | |
703 | memset(m2info, 0, sizeof(*m2info)); | |
704 | m2info->primary_dev = MINFO; | |
705 | if (matroxfb_dh_registerfb(m2info)) { | |
706 | kfree(m2info); | |
707 | printk(KERN_ERR "matroxfb_crtc2: CRTC2 framebuffer failed to register\n"); | |
708 | return NULL; | |
709 | } | |
710 | return m2info; | |
711 | } | |
712 | ||
713 | static void matroxfb_crtc2_remove(struct matrox_fb_info* minfo, void* crtc2) { | |
714 | matroxfb_dh_deregisterfb(crtc2); | |
715 | kfree(crtc2); | |
716 | } | |
717 | ||
718 | static struct matroxfb_driver crtc2 = { | |
719 | .name = "Matrox G400 CRTC2", | |
720 | .probe = matroxfb_crtc2_probe, | |
721 | .remove = matroxfb_crtc2_remove }; | |
722 | ||
723 | static int matroxfb_crtc2_init(void) { | |
724 | if (fb_get_options("matrox_crtc2fb", NULL)) | |
725 | return -ENODEV; | |
726 | ||
727 | matroxfb_register_driver(&crtc2); | |
728 | return 0; | |
729 | } | |
730 | ||
731 | static void matroxfb_crtc2_exit(void) { | |
732 | matroxfb_unregister_driver(&crtc2); | |
733 | } | |
734 | ||
735 | MODULE_AUTHOR("(c) 1999-2002 Petr Vandrovec <vandrove@vc.cvut.cz>"); | |
736 | MODULE_DESCRIPTION("Matrox G400 CRTC2 driver"); | |
737 | MODULE_LICENSE("GPL"); | |
738 | module_init(matroxfb_crtc2_init); | |
739 | module_exit(matroxfb_crtc2_exit); | |
740 | /* we do not have __setup() yet */ |