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> | |
84902b7a | 18 | #include <linux/uaccess.h> |
1da177e4 LT |
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 | ||
1da177e4 LT |
166 | static void matroxfb_dh_pan_var(struct matroxfb_dh_fb_info* m2info, |
167 | struct fb_var_screeninfo* var) { | |
168 | unsigned int pos; | |
169 | unsigned int linelen; | |
170 | unsigned int pixelsize; | |
171 | MINFO_FROM(m2info->primary_dev); | |
172 | ||
173 | m2info->fbcon.var.xoffset = var->xoffset; | |
174 | m2info->fbcon.var.yoffset = var->yoffset; | |
175 | pixelsize = m2info->fbcon.var.bits_per_pixel >> 3; | |
176 | linelen = m2info->fbcon.var.xres_virtual * pixelsize; | |
177 | pos = m2info->fbcon.var.yoffset * linelen + m2info->fbcon.var.xoffset * pixelsize; | |
178 | pos += m2info->video.offbase; | |
179 | if (m2info->interlaced) { | |
180 | mga_outl(0x3C2C, pos); | |
181 | mga_outl(0x3C28, pos + linelen); | |
182 | } else { | |
183 | mga_outl(0x3C28, pos); | |
184 | } | |
185 | } | |
186 | ||
187 | static int matroxfb_dh_decode_var(struct matroxfb_dh_fb_info* m2info, | |
188 | struct fb_var_screeninfo* var, | |
189 | int *visual, | |
190 | int *video_cmap_len, | |
191 | int *mode) { | |
192 | unsigned int mask; | |
193 | unsigned int memlen; | |
194 | unsigned int vramlen; | |
195 | ||
196 | switch (var->bits_per_pixel) { | |
197 | case 16: mask = 0x1F; | |
198 | break; | |
199 | case 32: mask = 0x0F; | |
200 | break; | |
201 | default: return -EINVAL; | |
202 | } | |
203 | vramlen = m2info->video.len_usable; | |
204 | if (var->yres_virtual < var->yres) | |
205 | var->yres_virtual = var->yres; | |
206 | if (var->xres_virtual < var->xres) | |
207 | var->xres_virtual = var->xres; | |
208 | var->xres_virtual = (var->xres_virtual + mask) & ~mask; | |
209 | if (var->yres_virtual > 32767) | |
210 | return -EINVAL; | |
211 | memlen = var->xres_virtual * var->yres_virtual * (var->bits_per_pixel >> 3); | |
212 | if (memlen > vramlen) | |
213 | return -EINVAL; | |
214 | if (var->xoffset + var->xres > var->xres_virtual) | |
215 | var->xoffset = var->xres_virtual - var->xres; | |
216 | if (var->yoffset + var->yres > var->yres_virtual) | |
217 | var->yoffset = var->yres_virtual - var->yres; | |
218 | ||
219 | var->xres &= ~7; | |
220 | var->left_margin &= ~7; | |
221 | var->right_margin &= ~7; | |
222 | var->hsync_len &= ~7; | |
223 | ||
224 | *mode = var->bits_per_pixel; | |
225 | if (var->bits_per_pixel == 16) { | |
226 | if (var->green.length == 5) { | |
227 | var->red.offset = 10; | |
228 | var->red.length = 5; | |
229 | var->green.offset = 5; | |
230 | var->green.length = 5; | |
231 | var->blue.offset = 0; | |
232 | var->blue.length = 5; | |
233 | var->transp.offset = 15; | |
234 | var->transp.length = 1; | |
235 | *mode = 15; | |
236 | } else { | |
237 | var->red.offset = 11; | |
238 | var->red.length = 5; | |
239 | var->green.offset = 5; | |
240 | var->green.length = 6; | |
241 | var->blue.offset = 0; | |
242 | var->blue.length = 5; | |
243 | var->transp.offset = 0; | |
244 | var->transp.length = 0; | |
245 | } | |
246 | } else { | |
247 | var->red.offset = 16; | |
248 | var->red.length = 8; | |
249 | var->green.offset = 8; | |
250 | var->green.length = 8; | |
251 | var->blue.offset = 0; | |
252 | var->blue.length = 8; | |
253 | var->transp.offset = 24; | |
254 | var->transp.length = 8; | |
255 | } | |
256 | *visual = FB_VISUAL_TRUECOLOR; | |
257 | *video_cmap_len = 16; | |
258 | return 0; | |
259 | } | |
260 | ||
261 | static int matroxfb_dh_open(struct fb_info* info, int user) { | |
262 | #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) | |
263 | MINFO_FROM(m2info->primary_dev); | |
264 | ||
265 | if (MINFO) { | |
266 | int err; | |
267 | ||
268 | if (ACCESS_FBINFO(dead)) { | |
269 | return -ENXIO; | |
270 | } | |
271 | err = ACCESS_FBINFO(fbops).fb_open(&ACCESS_FBINFO(fbcon), user); | |
272 | if (err) { | |
273 | return err; | |
274 | } | |
275 | } | |
276 | return 0; | |
277 | #undef m2info | |
278 | } | |
279 | ||
280 | static int matroxfb_dh_release(struct fb_info* info, int user) { | |
281 | #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) | |
282 | int err = 0; | |
283 | MINFO_FROM(m2info->primary_dev); | |
284 | ||
285 | if (MINFO) { | |
286 | err = ACCESS_FBINFO(fbops).fb_release(&ACCESS_FBINFO(fbcon), user); | |
287 | } | |
288 | return err; | |
289 | #undef m2info | |
290 | } | |
291 | ||
292 | static void matroxfb_dh_init_fix(struct matroxfb_dh_fb_info *m2info) { | |
293 | struct fb_fix_screeninfo *fix = &m2info->fbcon.fix; | |
294 | ||
295 | strcpy(fix->id, "MATROX DH"); | |
296 | ||
297 | fix->smem_start = m2info->video.base; | |
298 | fix->smem_len = m2info->video.len_usable; | |
299 | fix->ypanstep = 1; | |
300 | fix->ywrapstep = 0; | |
301 | fix->xpanstep = 8; /* TBD */ | |
302 | fix->mmio_start = m2info->mmio.base; | |
303 | fix->mmio_len = m2info->mmio.len; | |
304 | fix->accel = 0; /* no accel... */ | |
305 | } | |
306 | ||
307 | static int matroxfb_dh_check_var(struct fb_var_screeninfo* var, struct fb_info* info) { | |
308 | #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) | |
309 | int visual; | |
310 | int cmap_len; | |
311 | int mode; | |
312 | ||
313 | return matroxfb_dh_decode_var(m2info, var, &visual, &cmap_len, &mode); | |
314 | #undef m2info | |
315 | } | |
316 | ||
317 | static int matroxfb_dh_set_par(struct fb_info* info) { | |
318 | #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) | |
319 | int visual; | |
320 | int cmap_len; | |
321 | int mode; | |
322 | int err; | |
323 | struct fb_var_screeninfo* var = &info->var; | |
324 | MINFO_FROM(m2info->primary_dev); | |
325 | ||
326 | if ((err = matroxfb_dh_decode_var(m2info, var, &visual, &cmap_len, &mode)) != 0) | |
327 | return err; | |
328 | /* cmap */ | |
329 | { | |
330 | m2info->fbcon.screen_base = vaddr_va(m2info->video.vbase); | |
331 | m2info->fbcon.fix.visual = visual; | |
332 | m2info->fbcon.fix.type = FB_TYPE_PACKED_PIXELS; | |
333 | m2info->fbcon.fix.type_aux = 0; | |
334 | m2info->fbcon.fix.line_length = (var->xres_virtual * var->bits_per_pixel) >> 3; | |
335 | } | |
336 | { | |
337 | struct my_timming mt; | |
338 | unsigned int pos; | |
339 | int out; | |
340 | int cnt; | |
341 | ||
342 | matroxfb_var2my(&m2info->fbcon.var, &mt); | |
343 | mt.crtc = MATROXFB_SRC_CRTC2; | |
344 | /* CRTC2 delay */ | |
345 | mt.delay = 34; | |
346 | ||
347 | pos = (m2info->fbcon.var.yoffset * m2info->fbcon.var.xres_virtual + m2info->fbcon.var.xoffset) * m2info->fbcon.var.bits_per_pixel >> 3; | |
348 | pos += m2info->video.offbase; | |
349 | cnt = 0; | |
350 | down_read(&ACCESS_FBINFO(altout).lock); | |
351 | for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { | |
352 | if (ACCESS_FBINFO(outputs[out]).src == MATROXFB_SRC_CRTC2) { | |
353 | cnt++; | |
354 | if (ACCESS_FBINFO(outputs[out]).output->compute) { | |
355 | ACCESS_FBINFO(outputs[out]).output->compute(ACCESS_FBINFO(outputs[out]).data, &mt); | |
356 | } | |
357 | } | |
358 | } | |
359 | ACCESS_FBINFO(crtc2).pixclock = mt.pixclock; | |
360 | ACCESS_FBINFO(crtc2).mnp = mt.mnp; | |
361 | up_read(&ACCESS_FBINFO(altout).lock); | |
362 | if (cnt) { | |
363 | matroxfb_dh_restore(m2info, &mt, mode, pos); | |
364 | } else { | |
365 | matroxfb_dh_disable(m2info); | |
366 | } | |
367 | DAC1064_global_init(PMINFO2); | |
368 | DAC1064_global_restore(PMINFO2); | |
369 | down_read(&ACCESS_FBINFO(altout).lock); | |
370 | for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { | |
371 | if (ACCESS_FBINFO(outputs[out]).src == MATROXFB_SRC_CRTC2 && | |
372 | ACCESS_FBINFO(outputs[out]).output->program) { | |
373 | ACCESS_FBINFO(outputs[out]).output->program(ACCESS_FBINFO(outputs[out]).data); | |
374 | } | |
375 | } | |
376 | for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { | |
377 | if (ACCESS_FBINFO(outputs[out]).src == MATROXFB_SRC_CRTC2 && | |
378 | ACCESS_FBINFO(outputs[out]).output->start) { | |
379 | ACCESS_FBINFO(outputs[out]).output->start(ACCESS_FBINFO(outputs[out]).data); | |
380 | } | |
381 | } | |
382 | up_read(&ACCESS_FBINFO(altout).lock); | |
1da177e4 LT |
383 | } |
384 | m2info->initialized = 1; | |
385 | return 0; | |
386 | #undef m2info | |
387 | } | |
388 | ||
389 | static int matroxfb_dh_pan_display(struct fb_var_screeninfo* var, struct fb_info* info) { | |
390 | #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) | |
391 | matroxfb_dh_pan_var(m2info, var); | |
392 | return 0; | |
393 | #undef m2info | |
394 | } | |
395 | ||
396 | static int matroxfb_dh_get_vblank(const struct matroxfb_dh_fb_info* m2info, struct fb_vblank* vblank) { | |
397 | MINFO_FROM(m2info->primary_dev); | |
398 | ||
399 | matroxfb_enable_irq(PMINFO 0); | |
400 | memset(vblank, 0, sizeof(*vblank)); | |
401 | vblank->flags = FB_VBLANK_HAVE_VCOUNT | FB_VBLANK_HAVE_VBLANK; | |
402 | /* mask out reserved bits + field number (odd/even) */ | |
403 | vblank->vcount = mga_inl(0x3C48) & 0x000007FF; | |
404 | /* compatibility stuff */ | |
405 | if (vblank->vcount >= m2info->fbcon.var.yres) | |
406 | vblank->flags |= FB_VBLANK_VBLANKING; | |
407 | if (test_bit(0, &ACCESS_FBINFO(irq_flags))) { | |
408 | vblank->flags |= FB_VBLANK_HAVE_COUNT; | |
409 | /* Only one writer, aligned int value... | |
410 | it should work without lock and without atomic_t */ | |
411 | vblank->count = ACCESS_FBINFO(crtc2).vsync.cnt; | |
412 | } | |
413 | return 0; | |
414 | } | |
415 | ||
67a6680d | 416 | static int matroxfb_dh_ioctl(struct fb_info *info, |
1da177e4 | 417 | unsigned int cmd, |
67a6680d CH |
418 | unsigned long arg) |
419 | { | |
1da177e4 LT |
420 | #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) |
421 | MINFO_FROM(m2info->primary_dev); | |
422 | ||
5ae12170 | 423 | DBG(__func__) |
1da177e4 LT |
424 | |
425 | switch (cmd) { | |
426 | case FBIOGET_VBLANK: | |
427 | { | |
428 | struct fb_vblank vblank; | |
429 | int err; | |
430 | ||
431 | err = matroxfb_dh_get_vblank(m2info, &vblank); | |
432 | if (err) | |
433 | return err; | |
434 | if (copy_to_user((void __user *)arg, &vblank, sizeof(vblank))) | |
435 | return -EFAULT; | |
436 | return 0; | |
437 | } | |
438 | case FBIO_WAITFORVSYNC: | |
439 | { | |
440 | u_int32_t crt; | |
441 | ||
442 | if (get_user(crt, (u_int32_t __user *)arg)) | |
443 | return -EFAULT; | |
444 | ||
445 | if (crt != 0) | |
446 | return -ENODEV; | |
447 | return matroxfb_wait_for_sync(PMINFO 1); | |
448 | } | |
449 | case MATROXFB_SET_OUTPUT_MODE: | |
450 | case MATROXFB_GET_OUTPUT_MODE: | |
451 | case MATROXFB_GET_ALL_OUTPUTS: | |
452 | { | |
67a6680d | 453 | return ACCESS_FBINFO(fbcon.fbops)->fb_ioctl(&ACCESS_FBINFO(fbcon), cmd, arg); |
1da177e4 LT |
454 | } |
455 | case MATROXFB_SET_OUTPUT_CONNECTION: | |
456 | { | |
457 | u_int32_t tmp; | |
458 | int out; | |
459 | int changes; | |
460 | ||
461 | if (get_user(tmp, (u_int32_t __user *)arg)) | |
462 | return -EFAULT; | |
463 | for (out = 0; out < 32; out++) { | |
464 | if (tmp & (1 << out)) { | |
465 | if (out >= MATROXFB_MAX_OUTPUTS) | |
466 | return -ENXIO; | |
467 | if (!ACCESS_FBINFO(outputs[out]).output) | |
468 | return -ENXIO; | |
469 | switch (ACCESS_FBINFO(outputs[out]).src) { | |
470 | case MATROXFB_SRC_NONE: | |
471 | case MATROXFB_SRC_CRTC2: | |
472 | break; | |
473 | default: | |
474 | return -EBUSY; | |
475 | } | |
476 | } | |
477 | } | |
478 | if (ACCESS_FBINFO(devflags.panellink)) { | |
479 | if (tmp & MATROXFB_OUTPUT_CONN_DFP) | |
480 | return -EINVAL; | |
481 | if ((ACCESS_FBINFO(outputs[2]).src == MATROXFB_SRC_CRTC1) && tmp) | |
482 | return -EBUSY; | |
483 | } | |
484 | changes = 0; | |
485 | for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { | |
486 | if (tmp & (1 << out)) { | |
487 | if (ACCESS_FBINFO(outputs[out]).src != MATROXFB_SRC_CRTC2) { | |
488 | changes = 1; | |
489 | ACCESS_FBINFO(outputs[out]).src = MATROXFB_SRC_CRTC2; | |
490 | } | |
491 | } else if (ACCESS_FBINFO(outputs[out]).src == MATROXFB_SRC_CRTC2) { | |
492 | changes = 1; | |
493 | ACCESS_FBINFO(outputs[out]).src = MATROXFB_SRC_NONE; | |
494 | } | |
495 | } | |
496 | if (!changes) | |
497 | return 0; | |
498 | matroxfb_dh_set_par(info); | |
499 | return 0; | |
500 | } | |
501 | case MATROXFB_GET_OUTPUT_CONNECTION: | |
502 | { | |
503 | u_int32_t conn = 0; | |
504 | int out; | |
505 | ||
506 | for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { | |
507 | if (ACCESS_FBINFO(outputs[out]).src == MATROXFB_SRC_CRTC2) { | |
508 | conn |= 1 << out; | |
509 | } | |
510 | } | |
511 | if (put_user(conn, (u_int32_t __user *)arg)) | |
512 | return -EFAULT; | |
513 | return 0; | |
514 | } | |
515 | case MATROXFB_GET_AVAILABLE_OUTPUTS: | |
516 | { | |
517 | u_int32_t tmp = 0; | |
518 | int out; | |
519 | ||
520 | for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { | |
521 | if (ACCESS_FBINFO(outputs[out]).output) { | |
522 | switch (ACCESS_FBINFO(outputs[out]).src) { | |
523 | case MATROXFB_SRC_NONE: | |
524 | case MATROXFB_SRC_CRTC2: | |
525 | tmp |= 1 << out; | |
526 | break; | |
527 | } | |
528 | } | |
529 | } | |
530 | if (ACCESS_FBINFO(devflags.panellink)) { | |
531 | tmp &= ~MATROXFB_OUTPUT_CONN_DFP; | |
532 | if (ACCESS_FBINFO(outputs[2]).src == MATROXFB_SRC_CRTC1) { | |
533 | tmp = 0; | |
534 | } | |
535 | } | |
536 | if (put_user(tmp, (u_int32_t __user *)arg)) | |
537 | return -EFAULT; | |
538 | return 0; | |
539 | } | |
540 | } | |
541 | return -ENOTTY; | |
542 | #undef m2info | |
543 | } | |
544 | ||
545 | static int matroxfb_dh_blank(int blank, struct fb_info* info) { | |
546 | #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) | |
547 | switch (blank) { | |
548 | case 1: | |
549 | case 2: | |
550 | case 3: | |
551 | case 4: | |
552 | default:; | |
553 | } | |
554 | /* do something... */ | |
555 | return 0; | |
556 | #undef m2info | |
557 | } | |
558 | ||
559 | static struct fb_ops matroxfb_dh_ops = { | |
560 | .owner = THIS_MODULE, | |
561 | .fb_open = matroxfb_dh_open, | |
562 | .fb_release = matroxfb_dh_release, | |
563 | .fb_check_var = matroxfb_dh_check_var, | |
564 | .fb_set_par = matroxfb_dh_set_par, | |
565 | .fb_setcolreg = matroxfb_dh_setcolreg, | |
566 | .fb_pan_display =matroxfb_dh_pan_display, | |
567 | .fb_blank = matroxfb_dh_blank, | |
568 | .fb_ioctl = matroxfb_dh_ioctl, | |
569 | .fb_fillrect = cfb_fillrect, | |
570 | .fb_copyarea = cfb_copyarea, | |
571 | .fb_imageblit = cfb_imageblit, | |
1da177e4 LT |
572 | }; |
573 | ||
574 | static struct fb_var_screeninfo matroxfb_dh_defined = { | |
575 | 640,480,640,480,/* W,H, virtual W,H */ | |
576 | 0,0, /* offset */ | |
577 | 32, /* depth */ | |
578 | 0, /* gray */ | |
579 | {0,0,0}, /* R */ | |
580 | {0,0,0}, /* G */ | |
581 | {0,0,0}, /* B */ | |
582 | {0,0,0}, /* alpha */ | |
583 | 0, /* nonstd */ | |
584 | FB_ACTIVATE_NOW, | |
585 | -1,-1, /* display size */ | |
586 | 0, /* accel flags */ | |
587 | 39721L,48L,16L,33L,10L, | |
588 | 96L,2,0, /* no sync info */ | |
589 | FB_VMODE_NONINTERLACED, | |
590 | 0, {0,0,0,0,0} | |
591 | }; | |
592 | ||
593 | static int matroxfb_dh_regit(CPMINFO struct matroxfb_dh_fb_info* m2info) { | |
594 | #define minfo (m2info->primary_dev) | |
595 | void* oldcrtc2; | |
596 | ||
597 | m2info->fbcon.fbops = &matroxfb_dh_ops; | |
598 | m2info->fbcon.flags = FBINFO_FLAG_DEFAULT; | |
599 | m2info->fbcon.flags |= FBINFO_HWACCEL_XPAN | | |
600 | FBINFO_HWACCEL_YPAN; | |
601 | m2info->fbcon.pseudo_palette = m2info->cmap; | |
602 | fb_alloc_cmap(&m2info->fbcon.cmap, 256, 1); | |
603 | ||
604 | if (mem < 64) | |
605 | mem *= 1024; | |
606 | if (mem < 64*1024) | |
607 | mem *= 1024; | |
608 | mem &= ~0x00000FFF; /* PAGE_MASK? */ | |
609 | if (ACCESS_FBINFO(video.len_usable) + mem <= ACCESS_FBINFO(video.len)) | |
610 | m2info->video.offbase = ACCESS_FBINFO(video.len) - mem; | |
611 | else if (ACCESS_FBINFO(video.len) < mem) { | |
612 | return -ENOMEM; | |
613 | } else { /* check yres on first head... */ | |
614 | m2info->video.borrowed = mem; | |
615 | ACCESS_FBINFO(video.len_usable) -= mem; | |
616 | m2info->video.offbase = ACCESS_FBINFO(video.len_usable); | |
617 | } | |
618 | m2info->video.base = ACCESS_FBINFO(video.base) + m2info->video.offbase; | |
619 | m2info->video.len = m2info->video.len_usable = m2info->video.len_maximum = mem; | |
620 | m2info->video.vbase.vaddr = vaddr_va(ACCESS_FBINFO(video.vbase)) + m2info->video.offbase; | |
621 | m2info->mmio.base = ACCESS_FBINFO(mmio.base); | |
622 | m2info->mmio.vbase = ACCESS_FBINFO(mmio.vbase); | |
623 | m2info->mmio.len = ACCESS_FBINFO(mmio.len); | |
624 | ||
625 | matroxfb_dh_init_fix(m2info); | |
626 | if (register_framebuffer(&m2info->fbcon)) { | |
627 | return -ENXIO; | |
628 | } | |
629 | if (!m2info->initialized) | |
630 | fb_set_var(&m2info->fbcon, &matroxfb_dh_defined); | |
631 | down_write(&ACCESS_FBINFO(crtc2.lock)); | |
632 | oldcrtc2 = ACCESS_FBINFO(crtc2.info); | |
633 | ACCESS_FBINFO(crtc2.info) = m2info; | |
634 | up_write(&ACCESS_FBINFO(crtc2.lock)); | |
635 | if (oldcrtc2) { | |
636 | printk(KERN_ERR "matroxfb_crtc2: Internal consistency check failed: crtc2 already present: %p\n", | |
637 | oldcrtc2); | |
638 | } | |
639 | return 0; | |
640 | #undef minfo | |
641 | } | |
642 | ||
643 | /* ************************** */ | |
644 | ||
645 | static int matroxfb_dh_registerfb(struct matroxfb_dh_fb_info* m2info) { | |
646 | #define minfo (m2info->primary_dev) | |
647 | if (matroxfb_dh_regit(PMINFO m2info)) { | |
648 | printk(KERN_ERR "matroxfb_crtc2: secondary head failed to register\n"); | |
649 | return -1; | |
650 | } | |
651 | printk(KERN_INFO "matroxfb_crtc2: secondary head of fb%u was registered as fb%u\n", | |
652 | ACCESS_FBINFO(fbcon.node), m2info->fbcon.node); | |
653 | m2info->fbcon_registered = 1; | |
654 | return 0; | |
655 | #undef minfo | |
656 | } | |
657 | ||
658 | static void matroxfb_dh_deregisterfb(struct matroxfb_dh_fb_info* m2info) { | |
659 | #define minfo (m2info->primary_dev) | |
660 | if (m2info->fbcon_registered) { | |
661 | int id; | |
662 | struct matroxfb_dh_fb_info* crtc2; | |
663 | ||
664 | down_write(&ACCESS_FBINFO(crtc2.lock)); | |
665 | crtc2 = ACCESS_FBINFO(crtc2.info); | |
666 | if (crtc2 == m2info) | |
667 | ACCESS_FBINFO(crtc2.info) = NULL; | |
668 | up_write(&ACCESS_FBINFO(crtc2.lock)); | |
669 | if (crtc2 != m2info) { | |
670 | printk(KERN_ERR "matroxfb_crtc2: Internal consistency check failed: crtc2 mismatch at unload: %p != %p\n", | |
671 | crtc2, m2info); | |
672 | printk(KERN_ERR "matroxfb_crtc2: Expect kernel crash after module unload.\n"); | |
673 | return; | |
674 | } | |
675 | id = m2info->fbcon.node; | |
676 | unregister_framebuffer(&m2info->fbcon); | |
677 | /* return memory back to primary head */ | |
678 | ACCESS_FBINFO(video.len_usable) += m2info->video.borrowed; | |
679 | printk(KERN_INFO "matroxfb_crtc2: fb%u unregistered\n", id); | |
680 | m2info->fbcon_registered = 0; | |
681 | } | |
682 | #undef minfo | |
683 | } | |
684 | ||
685 | static void* matroxfb_crtc2_probe(struct matrox_fb_info* minfo) { | |
686 | struct matroxfb_dh_fb_info* m2info; | |
687 | ||
688 | /* hardware is CRTC2 incapable... */ | |
689 | if (!ACCESS_FBINFO(devflags.crtc2)) | |
690 | return NULL; | |
2fdbe5cf | 691 | m2info = kzalloc(sizeof(*m2info), GFP_KERNEL); |
1da177e4 LT |
692 | if (!m2info) { |
693 | printk(KERN_ERR "matroxfb_crtc2: Not enough memory for CRTC2 control structs\n"); | |
694 | return NULL; | |
695 | } | |
1da177e4 LT |
696 | m2info->primary_dev = MINFO; |
697 | if (matroxfb_dh_registerfb(m2info)) { | |
698 | kfree(m2info); | |
699 | printk(KERN_ERR "matroxfb_crtc2: CRTC2 framebuffer failed to register\n"); | |
700 | return NULL; | |
701 | } | |
702 | return m2info; | |
703 | } | |
704 | ||
705 | static void matroxfb_crtc2_remove(struct matrox_fb_info* minfo, void* crtc2) { | |
706 | matroxfb_dh_deregisterfb(crtc2); | |
707 | kfree(crtc2); | |
708 | } | |
709 | ||
710 | static struct matroxfb_driver crtc2 = { | |
711 | .name = "Matrox G400 CRTC2", | |
712 | .probe = matroxfb_crtc2_probe, | |
713 | .remove = matroxfb_crtc2_remove }; | |
714 | ||
715 | static int matroxfb_crtc2_init(void) { | |
716 | if (fb_get_options("matrox_crtc2fb", NULL)) | |
717 | return -ENODEV; | |
718 | ||
719 | matroxfb_register_driver(&crtc2); | |
720 | return 0; | |
721 | } | |
722 | ||
723 | static void matroxfb_crtc2_exit(void) { | |
724 | matroxfb_unregister_driver(&crtc2); | |
725 | } | |
726 | ||
727 | MODULE_AUTHOR("(c) 1999-2002 Petr Vandrovec <vandrove@vc.cvut.cz>"); | |
728 | MODULE_DESCRIPTION("Matrox G400 CRTC2 driver"); | |
729 | MODULE_LICENSE("GPL"); | |
730 | module_init(matroxfb_crtc2_init); | |
731 | module_exit(matroxfb_crtc2_exit); | |
732 | /* we do not have __setup() yet */ |