diff options
Diffstat (limited to 'drivers/video/matrox/matroxfb_crtc2.c')
-rw-r--r-- | drivers/video/matrox/matroxfb_crtc2.c | 741 |
1 files changed, 741 insertions, 0 deletions
diff --git a/drivers/video/matrox/matroxfb_crtc2.c b/drivers/video/matrox/matroxfb_crtc2.c new file mode 100644 index 000000000000..429047ac615a --- /dev/null +++ b/drivers/video/matrox/matroxfb_crtc2.c | |||
@@ -0,0 +1,741 @@ | |||
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, | ||
579 | .fb_cursor = soft_cursor, | ||
580 | }; | ||
581 | |||
582 | static struct fb_var_screeninfo matroxfb_dh_defined = { | ||
583 | 640,480,640,480,/* W,H, virtual W,H */ | ||
584 | 0,0, /* offset */ | ||
585 | 32, /* depth */ | ||
586 | 0, /* gray */ | ||
587 | {0,0,0}, /* R */ | ||
588 | {0,0,0}, /* G */ | ||
589 | {0,0,0}, /* B */ | ||
590 | {0,0,0}, /* alpha */ | ||
591 | 0, /* nonstd */ | ||
592 | FB_ACTIVATE_NOW, | ||
593 | -1,-1, /* display size */ | ||
594 | 0, /* accel flags */ | ||
595 | 39721L,48L,16L,33L,10L, | ||
596 | 96L,2,0, /* no sync info */ | ||
597 | FB_VMODE_NONINTERLACED, | ||
598 | 0, {0,0,0,0,0} | ||
599 | }; | ||
600 | |||
601 | static int matroxfb_dh_regit(CPMINFO struct matroxfb_dh_fb_info* m2info) { | ||
602 | #define minfo (m2info->primary_dev) | ||
603 | void* oldcrtc2; | ||
604 | |||
605 | m2info->fbcon.fbops = &matroxfb_dh_ops; | ||
606 | m2info->fbcon.flags = FBINFO_FLAG_DEFAULT; | ||
607 | m2info->fbcon.flags |= FBINFO_HWACCEL_XPAN | | ||
608 | FBINFO_HWACCEL_YPAN; | ||
609 | m2info->fbcon.pseudo_palette = m2info->cmap; | ||
610 | fb_alloc_cmap(&m2info->fbcon.cmap, 256, 1); | ||
611 | |||
612 | if (mem < 64) | ||
613 | mem *= 1024; | ||
614 | if (mem < 64*1024) | ||
615 | mem *= 1024; | ||
616 | mem &= ~0x00000FFF; /* PAGE_MASK? */ | ||
617 | if (ACCESS_FBINFO(video.len_usable) + mem <= ACCESS_FBINFO(video.len)) | ||
618 | m2info->video.offbase = ACCESS_FBINFO(video.len) - mem; | ||
619 | else if (ACCESS_FBINFO(video.len) < mem) { | ||
620 | return -ENOMEM; | ||
621 | } else { /* check yres on first head... */ | ||
622 | m2info->video.borrowed = mem; | ||
623 | ACCESS_FBINFO(video.len_usable) -= mem; | ||
624 | m2info->video.offbase = ACCESS_FBINFO(video.len_usable); | ||
625 | } | ||
626 | m2info->video.base = ACCESS_FBINFO(video.base) + m2info->video.offbase; | ||
627 | m2info->video.len = m2info->video.len_usable = m2info->video.len_maximum = mem; | ||
628 | m2info->video.vbase.vaddr = vaddr_va(ACCESS_FBINFO(video.vbase)) + m2info->video.offbase; | ||
629 | m2info->mmio.base = ACCESS_FBINFO(mmio.base); | ||
630 | m2info->mmio.vbase = ACCESS_FBINFO(mmio.vbase); | ||
631 | m2info->mmio.len = ACCESS_FBINFO(mmio.len); | ||
632 | |||
633 | matroxfb_dh_init_fix(m2info); | ||
634 | if (register_framebuffer(&m2info->fbcon)) { | ||
635 | return -ENXIO; | ||
636 | } | ||
637 | if (!m2info->initialized) | ||
638 | fb_set_var(&m2info->fbcon, &matroxfb_dh_defined); | ||
639 | down_write(&ACCESS_FBINFO(crtc2.lock)); | ||
640 | oldcrtc2 = ACCESS_FBINFO(crtc2.info); | ||
641 | ACCESS_FBINFO(crtc2.info) = m2info; | ||
642 | up_write(&ACCESS_FBINFO(crtc2.lock)); | ||
643 | if (oldcrtc2) { | ||
644 | printk(KERN_ERR "matroxfb_crtc2: Internal consistency check failed: crtc2 already present: %p\n", | ||
645 | oldcrtc2); | ||
646 | } | ||
647 | return 0; | ||
648 | #undef minfo | ||
649 | } | ||
650 | |||
651 | /* ************************** */ | ||
652 | |||
653 | static int matroxfb_dh_registerfb(struct matroxfb_dh_fb_info* m2info) { | ||
654 | #define minfo (m2info->primary_dev) | ||
655 | if (matroxfb_dh_regit(PMINFO m2info)) { | ||
656 | printk(KERN_ERR "matroxfb_crtc2: secondary head failed to register\n"); | ||
657 | return -1; | ||
658 | } | ||
659 | printk(KERN_INFO "matroxfb_crtc2: secondary head of fb%u was registered as fb%u\n", | ||
660 | ACCESS_FBINFO(fbcon.node), m2info->fbcon.node); | ||
661 | m2info->fbcon_registered = 1; | ||
662 | return 0; | ||
663 | #undef minfo | ||
664 | } | ||
665 | |||
666 | static void matroxfb_dh_deregisterfb(struct matroxfb_dh_fb_info* m2info) { | ||
667 | #define minfo (m2info->primary_dev) | ||
668 | if (m2info->fbcon_registered) { | ||
669 | int id; | ||
670 | struct matroxfb_dh_fb_info* crtc2; | ||
671 | |||
672 | down_write(&ACCESS_FBINFO(crtc2.lock)); | ||
673 | crtc2 = ACCESS_FBINFO(crtc2.info); | ||
674 | if (crtc2 == m2info) | ||
675 | ACCESS_FBINFO(crtc2.info) = NULL; | ||
676 | up_write(&ACCESS_FBINFO(crtc2.lock)); | ||
677 | if (crtc2 != m2info) { | ||
678 | printk(KERN_ERR "matroxfb_crtc2: Internal consistency check failed: crtc2 mismatch at unload: %p != %p\n", | ||
679 | crtc2, m2info); | ||
680 | printk(KERN_ERR "matroxfb_crtc2: Expect kernel crash after module unload.\n"); | ||
681 | return; | ||
682 | } | ||
683 | id = m2info->fbcon.node; | ||
684 | unregister_framebuffer(&m2info->fbcon); | ||
685 | /* return memory back to primary head */ | ||
686 | ACCESS_FBINFO(video.len_usable) += m2info->video.borrowed; | ||
687 | printk(KERN_INFO "matroxfb_crtc2: fb%u unregistered\n", id); | ||
688 | m2info->fbcon_registered = 0; | ||
689 | } | ||
690 | #undef minfo | ||
691 | } | ||
692 | |||
693 | static void* matroxfb_crtc2_probe(struct matrox_fb_info* minfo) { | ||
694 | struct matroxfb_dh_fb_info* m2info; | ||
695 | |||
696 | /* hardware is CRTC2 incapable... */ | ||
697 | if (!ACCESS_FBINFO(devflags.crtc2)) | ||
698 | return NULL; | ||
699 | m2info = (struct matroxfb_dh_fb_info*)kmalloc(sizeof(*m2info), GFP_KERNEL); | ||
700 | if (!m2info) { | ||
701 | printk(KERN_ERR "matroxfb_crtc2: Not enough memory for CRTC2 control structs\n"); | ||
702 | return NULL; | ||
703 | } | ||
704 | memset(m2info, 0, sizeof(*m2info)); | ||
705 | m2info->primary_dev = MINFO; | ||
706 | if (matroxfb_dh_registerfb(m2info)) { | ||
707 | kfree(m2info); | ||
708 | printk(KERN_ERR "matroxfb_crtc2: CRTC2 framebuffer failed to register\n"); | ||
709 | return NULL; | ||
710 | } | ||
711 | return m2info; | ||
712 | } | ||
713 | |||
714 | static void matroxfb_crtc2_remove(struct matrox_fb_info* minfo, void* crtc2) { | ||
715 | matroxfb_dh_deregisterfb(crtc2); | ||
716 | kfree(crtc2); | ||
717 | } | ||
718 | |||
719 | static struct matroxfb_driver crtc2 = { | ||
720 | .name = "Matrox G400 CRTC2", | ||
721 | .probe = matroxfb_crtc2_probe, | ||
722 | .remove = matroxfb_crtc2_remove }; | ||
723 | |||
724 | static int matroxfb_crtc2_init(void) { | ||
725 | if (fb_get_options("matrox_crtc2fb", NULL)) | ||
726 | return -ENODEV; | ||
727 | |||
728 | matroxfb_register_driver(&crtc2); | ||
729 | return 0; | ||
730 | } | ||
731 | |||
732 | static void matroxfb_crtc2_exit(void) { | ||
733 | matroxfb_unregister_driver(&crtc2); | ||
734 | } | ||
735 | |||
736 | MODULE_AUTHOR("(c) 1999-2002 Petr Vandrovec <vandrove@vc.cvut.cz>"); | ||
737 | MODULE_DESCRIPTION("Matrox G400 CRTC2 driver"); | ||
738 | MODULE_LICENSE("GPL"); | ||
739 | module_init(matroxfb_crtc2_init); | ||
740 | module_exit(matroxfb_crtc2_exit); | ||
741 | /* we do not have __setup() yet */ | ||