diff options
Diffstat (limited to 'drivers/video/fbdev/matrox/matroxfb_crtc2.c')
-rw-r--r-- | drivers/video/fbdev/matrox/matroxfb_crtc2.c | 739 |
1 files changed, 739 insertions, 0 deletions
diff --git a/drivers/video/fbdev/matrox/matroxfb_crtc2.c b/drivers/video/fbdev/matrox/matroxfb_crtc2.c new file mode 100644 index 000000000000..02796a4317a9 --- /dev/null +++ b/drivers/video/fbdev/matrox/matroxfb_crtc2.c | |||
@@ -0,0 +1,739 @@ | |||
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 <linux/slab.h> | ||
19 | #include <linux/uaccess.h> | ||
20 | |||
21 | /* **************************************************** */ | ||
22 | |||
23 | static int mem = 8192; | ||
24 | |||
25 | module_param(mem, int, 0); | ||
26 | MODULE_PARM_DESC(mem, "Memory size reserved for dualhead (default=8MB)"); | ||
27 | |||
28 | /* **************************************************** */ | ||
29 | |||
30 | static int matroxfb_dh_setcolreg(unsigned regno, unsigned red, unsigned green, | ||
31 | unsigned blue, unsigned transp, struct fb_info* info) { | ||
32 | u_int32_t col; | ||
33 | #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) | ||
34 | |||
35 | if (regno >= 16) | ||
36 | return 1; | ||
37 | if (m2info->fbcon.var.grayscale) { | ||
38 | /* gray = 0.30*R + 0.59*G + 0.11*B */ | ||
39 | red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8; | ||
40 | } | ||
41 | red = CNVT_TOHW(red, m2info->fbcon.var.red.length); | ||
42 | green = CNVT_TOHW(green, m2info->fbcon.var.green.length); | ||
43 | blue = CNVT_TOHW(blue, m2info->fbcon.var.blue.length); | ||
44 | transp = CNVT_TOHW(transp, m2info->fbcon.var.transp.length); | ||
45 | |||
46 | col = (red << m2info->fbcon.var.red.offset) | | ||
47 | (green << m2info->fbcon.var.green.offset) | | ||
48 | (blue << m2info->fbcon.var.blue.offset) | | ||
49 | (transp << m2info->fbcon.var.transp.offset); | ||
50 | |||
51 | switch (m2info->fbcon.var.bits_per_pixel) { | ||
52 | case 16: | ||
53 | m2info->cmap[regno] = col | (col << 16); | ||
54 | break; | ||
55 | case 32: | ||
56 | m2info->cmap[regno] = col; | ||
57 | break; | ||
58 | } | ||
59 | return 0; | ||
60 | #undef m2info | ||
61 | } | ||
62 | |||
63 | static void matroxfb_dh_restore(struct matroxfb_dh_fb_info* m2info, | ||
64 | struct my_timming* mt, | ||
65 | int mode, | ||
66 | unsigned int pos) { | ||
67 | u_int32_t tmp; | ||
68 | u_int32_t datactl; | ||
69 | struct matrox_fb_info *minfo = m2info->primary_dev; | ||
70 | |||
71 | switch (mode) { | ||
72 | case 15: | ||
73 | tmp = 0x00200000; | ||
74 | break; | ||
75 | case 16: | ||
76 | tmp = 0x00400000; | ||
77 | break; | ||
78 | /* case 32: */ | ||
79 | default: | ||
80 | tmp = 0x00800000; | ||
81 | break; | ||
82 | } | ||
83 | tmp |= 0x00000001; /* enable CRTC2 */ | ||
84 | datactl = 0; | ||
85 | if (minfo->outputs[1].src == MATROXFB_SRC_CRTC2) { | ||
86 | if (minfo->devflags.g450dac) { | ||
87 | tmp |= 0x00000006; /* source from secondary pixel PLL */ | ||
88 | /* no vidrst when in monitor mode */ | ||
89 | if (minfo->outputs[1].mode != MATROXFB_OUTPUT_MODE_MONITOR) { | ||
90 | tmp |= 0xC0001000; /* Enable H/V vidrst */ | ||
91 | } | ||
92 | } else { | ||
93 | tmp |= 0x00000002; /* source from VDOCLK */ | ||
94 | tmp |= 0xC0000000; /* enable vvidrst & hvidrst */ | ||
95 | /* MGA TVO is our clock source */ | ||
96 | } | ||
97 | } else if (minfo->outputs[0].src == MATROXFB_SRC_CRTC2) { | ||
98 | tmp |= 0x00000004; /* source from pixclock */ | ||
99 | /* PIXPLL is our clock source */ | ||
100 | } | ||
101 | if (minfo->outputs[0].src == MATROXFB_SRC_CRTC2) { | ||
102 | tmp |= 0x00100000; /* connect CRTC2 to DAC */ | ||
103 | } | ||
104 | if (mt->interlaced) { | ||
105 | tmp |= 0x02000000; /* interlaced, second field is bigger, as G450 apparently ignores it */ | ||
106 | mt->VDisplay >>= 1; | ||
107 | mt->VSyncStart >>= 1; | ||
108 | mt->VSyncEnd >>= 1; | ||
109 | mt->VTotal >>= 1; | ||
110 | } | ||
111 | if ((mt->HTotal & 7) == 2) { | ||
112 | datactl |= 0x00000010; | ||
113 | mt->HTotal &= ~7; | ||
114 | } | ||
115 | tmp |= 0x10000000; /* 0x10000000 is VIDRST polarity */ | ||
116 | mga_outl(0x3C14, ((mt->HDisplay - 8) << 16) | (mt->HTotal - 8)); | ||
117 | mga_outl(0x3C18, ((mt->HSyncEnd - 8) << 16) | (mt->HSyncStart - 8)); | ||
118 | mga_outl(0x3C1C, ((mt->VDisplay - 1) << 16) | (mt->VTotal - 1)); | ||
119 | mga_outl(0x3C20, ((mt->VSyncEnd - 1) << 16) | (mt->VSyncStart - 1)); | ||
120 | mga_outl(0x3C24, ((mt->VSyncStart) << 16) | (mt->HSyncStart)); /* preload */ | ||
121 | { | ||
122 | u_int32_t linelen = m2info->fbcon.var.xres_virtual * (m2info->fbcon.var.bits_per_pixel >> 3); | ||
123 | if (tmp & 0x02000000) { | ||
124 | /* field #0 is smaller, so... */ | ||
125 | mga_outl(0x3C2C, pos); /* field #1 vmemory start */ | ||
126 | mga_outl(0x3C28, pos + linelen); /* field #0 vmemory start */ | ||
127 | linelen <<= 1; | ||
128 | m2info->interlaced = 1; | ||
129 | } else { | ||
130 | mga_outl(0x3C28, pos); /* vmemory start */ | ||
131 | m2info->interlaced = 0; | ||
132 | } | ||
133 | mga_outl(0x3C40, linelen); | ||
134 | } | ||
135 | mga_outl(0x3C4C, datactl); /* data control */ | ||
136 | if (tmp & 0x02000000) { | ||
137 | int i; | ||
138 | |||
139 | mga_outl(0x3C10, tmp & ~0x02000000); | ||
140 | for (i = 0; i < 2; i++) { | ||
141 | unsigned int nl; | ||
142 | unsigned int lastl = 0; | ||
143 | |||
144 | while ((nl = mga_inl(0x3C48) & 0xFFF) >= lastl) { | ||
145 | lastl = nl; | ||
146 | } | ||
147 | } | ||
148 | } | ||
149 | mga_outl(0x3C10, tmp); | ||
150 | minfo->hw.crtc2.ctl = tmp; | ||
151 | |||
152 | tmp = mt->VDisplay << 16; /* line compare */ | ||
153 | if (mt->sync & FB_SYNC_HOR_HIGH_ACT) | ||
154 | tmp |= 0x00000100; | ||
155 | if (mt->sync & FB_SYNC_VERT_HIGH_ACT) | ||
156 | tmp |= 0x00000200; | ||
157 | mga_outl(0x3C44, tmp); | ||
158 | } | ||
159 | |||
160 | static void matroxfb_dh_disable(struct matroxfb_dh_fb_info* m2info) { | ||
161 | struct matrox_fb_info *minfo = m2info->primary_dev; | ||
162 | |||
163 | mga_outl(0x3C10, 0x00000004); /* disable CRTC2, CRTC1->DAC1, PLL as clock source */ | ||
164 | minfo->hw.crtc2.ctl = 0x00000004; | ||
165 | } | ||
166 | |||
167 | static void matroxfb_dh_pan_var(struct matroxfb_dh_fb_info* m2info, | ||
168 | struct fb_var_screeninfo* var) { | ||
169 | unsigned int pos; | ||
170 | unsigned int linelen; | ||
171 | unsigned int pixelsize; | ||
172 | struct matrox_fb_info *minfo = m2info->primary_dev; | ||
173 | |||
174 | m2info->fbcon.var.xoffset = var->xoffset; | ||
175 | m2info->fbcon.var.yoffset = var->yoffset; | ||
176 | pixelsize = m2info->fbcon.var.bits_per_pixel >> 3; | ||
177 | linelen = m2info->fbcon.var.xres_virtual * pixelsize; | ||
178 | pos = m2info->fbcon.var.yoffset * linelen + m2info->fbcon.var.xoffset * pixelsize; | ||
179 | pos += m2info->video.offbase; | ||
180 | if (m2info->interlaced) { | ||
181 | mga_outl(0x3C2C, pos); | ||
182 | mga_outl(0x3C28, pos + linelen); | ||
183 | } else { | ||
184 | mga_outl(0x3C28, pos); | ||
185 | } | ||
186 | } | ||
187 | |||
188 | static int matroxfb_dh_decode_var(struct matroxfb_dh_fb_info* m2info, | ||
189 | struct fb_var_screeninfo* var, | ||
190 | int *visual, | ||
191 | int *video_cmap_len, | ||
192 | int *mode) { | ||
193 | unsigned int mask; | ||
194 | unsigned int memlen; | ||
195 | unsigned int vramlen; | ||
196 | |||
197 | switch (var->bits_per_pixel) { | ||
198 | case 16: mask = 0x1F; | ||
199 | break; | ||
200 | case 32: mask = 0x0F; | ||
201 | break; | ||
202 | default: return -EINVAL; | ||
203 | } | ||
204 | vramlen = m2info->video.len_usable; | ||
205 | if (var->yres_virtual < var->yres) | ||
206 | var->yres_virtual = var->yres; | ||
207 | if (var->xres_virtual < var->xres) | ||
208 | var->xres_virtual = var->xres; | ||
209 | var->xres_virtual = (var->xres_virtual + mask) & ~mask; | ||
210 | if (var->yres_virtual > 32767) | ||
211 | return -EINVAL; | ||
212 | memlen = var->xres_virtual * var->yres_virtual * (var->bits_per_pixel >> 3); | ||
213 | if (memlen > vramlen) | ||
214 | return -EINVAL; | ||
215 | if (var->xoffset + var->xres > var->xres_virtual) | ||
216 | var->xoffset = var->xres_virtual - var->xres; | ||
217 | if (var->yoffset + var->yres > var->yres_virtual) | ||
218 | var->yoffset = var->yres_virtual - var->yres; | ||
219 | |||
220 | var->xres &= ~7; | ||
221 | var->left_margin &= ~7; | ||
222 | var->right_margin &= ~7; | ||
223 | var->hsync_len &= ~7; | ||
224 | |||
225 | *mode = var->bits_per_pixel; | ||
226 | if (var->bits_per_pixel == 16) { | ||
227 | if (var->green.length == 5) { | ||
228 | var->red.offset = 10; | ||
229 | var->red.length = 5; | ||
230 | var->green.offset = 5; | ||
231 | var->green.length = 5; | ||
232 | var->blue.offset = 0; | ||
233 | var->blue.length = 5; | ||
234 | var->transp.offset = 15; | ||
235 | var->transp.length = 1; | ||
236 | *mode = 15; | ||
237 | } else { | ||
238 | var->red.offset = 11; | ||
239 | var->red.length = 5; | ||
240 | var->green.offset = 5; | ||
241 | var->green.length = 6; | ||
242 | var->blue.offset = 0; | ||
243 | var->blue.length = 5; | ||
244 | var->transp.offset = 0; | ||
245 | var->transp.length = 0; | ||
246 | } | ||
247 | } else { | ||
248 | var->red.offset = 16; | ||
249 | var->red.length = 8; | ||
250 | var->green.offset = 8; | ||
251 | var->green.length = 8; | ||
252 | var->blue.offset = 0; | ||
253 | var->blue.length = 8; | ||
254 | var->transp.offset = 24; | ||
255 | var->transp.length = 8; | ||
256 | } | ||
257 | *visual = FB_VISUAL_TRUECOLOR; | ||
258 | *video_cmap_len = 16; | ||
259 | return 0; | ||
260 | } | ||
261 | |||
262 | static int matroxfb_dh_open(struct fb_info* info, int user) { | ||
263 | #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) | ||
264 | struct matrox_fb_info *minfo = m2info->primary_dev; | ||
265 | |||
266 | if (minfo) { | ||
267 | int err; | ||
268 | |||
269 | if (minfo->dead) { | ||
270 | return -ENXIO; | ||
271 | } | ||
272 | err = minfo->fbops.fb_open(&minfo->fbcon, user); | ||
273 | if (err) { | ||
274 | return err; | ||
275 | } | ||
276 | } | ||
277 | return 0; | ||
278 | #undef m2info | ||
279 | } | ||
280 | |||
281 | static int matroxfb_dh_release(struct fb_info* info, int user) { | ||
282 | #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) | ||
283 | int err = 0; | ||
284 | struct matrox_fb_info *minfo = m2info->primary_dev; | ||
285 | |||
286 | if (minfo) { | ||
287 | err = minfo->fbops.fb_release(&minfo->fbcon, user); | ||
288 | } | ||
289 | return err; | ||
290 | #undef m2info | ||
291 | } | ||
292 | |||
293 | /* | ||
294 | * This function is called before the register_framebuffer so | ||
295 | * no locking is needed. | ||
296 | */ | ||
297 | static void matroxfb_dh_init_fix(struct matroxfb_dh_fb_info *m2info) | ||
298 | { | ||
299 | struct fb_fix_screeninfo *fix = &m2info->fbcon.fix; | ||
300 | |||
301 | strcpy(fix->id, "MATROX DH"); | ||
302 | |||
303 | fix->smem_start = m2info->video.base; | ||
304 | fix->smem_len = m2info->video.len_usable; | ||
305 | fix->ypanstep = 1; | ||
306 | fix->ywrapstep = 0; | ||
307 | fix->xpanstep = 8; /* TBD */ | ||
308 | fix->mmio_start = m2info->mmio.base; | ||
309 | fix->mmio_len = m2info->mmio.len; | ||
310 | fix->accel = 0; /* no accel... */ | ||
311 | } | ||
312 | |||
313 | static int matroxfb_dh_check_var(struct fb_var_screeninfo* var, struct fb_info* info) { | ||
314 | #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) | ||
315 | int visual; | ||
316 | int cmap_len; | ||
317 | int mode; | ||
318 | |||
319 | return matroxfb_dh_decode_var(m2info, var, &visual, &cmap_len, &mode); | ||
320 | #undef m2info | ||
321 | } | ||
322 | |||
323 | static int matroxfb_dh_set_par(struct fb_info* info) { | ||
324 | #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) | ||
325 | int visual; | ||
326 | int cmap_len; | ||
327 | int mode; | ||
328 | int err; | ||
329 | struct fb_var_screeninfo* var = &info->var; | ||
330 | struct matrox_fb_info *minfo = m2info->primary_dev; | ||
331 | |||
332 | if ((err = matroxfb_dh_decode_var(m2info, var, &visual, &cmap_len, &mode)) != 0) | ||
333 | return err; | ||
334 | /* cmap */ | ||
335 | { | ||
336 | m2info->fbcon.screen_base = vaddr_va(m2info->video.vbase); | ||
337 | m2info->fbcon.fix.visual = visual; | ||
338 | m2info->fbcon.fix.type = FB_TYPE_PACKED_PIXELS; | ||
339 | m2info->fbcon.fix.type_aux = 0; | ||
340 | m2info->fbcon.fix.line_length = (var->xres_virtual * var->bits_per_pixel) >> 3; | ||
341 | } | ||
342 | { | ||
343 | struct my_timming mt; | ||
344 | unsigned int pos; | ||
345 | int out; | ||
346 | int cnt; | ||
347 | |||
348 | matroxfb_var2my(&m2info->fbcon.var, &mt); | ||
349 | mt.crtc = MATROXFB_SRC_CRTC2; | ||
350 | /* CRTC2 delay */ | ||
351 | mt.delay = 34; | ||
352 | |||
353 | pos = (m2info->fbcon.var.yoffset * m2info->fbcon.var.xres_virtual + m2info->fbcon.var.xoffset) * m2info->fbcon.var.bits_per_pixel >> 3; | ||
354 | pos += m2info->video.offbase; | ||
355 | cnt = 0; | ||
356 | down_read(&minfo->altout.lock); | ||
357 | for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { | ||
358 | if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2) { | ||
359 | cnt++; | ||
360 | if (minfo->outputs[out].output->compute) { | ||
361 | minfo->outputs[out].output->compute(minfo->outputs[out].data, &mt); | ||
362 | } | ||
363 | } | ||
364 | } | ||
365 | minfo->crtc2.pixclock = mt.pixclock; | ||
366 | minfo->crtc2.mnp = mt.mnp; | ||
367 | up_read(&minfo->altout.lock); | ||
368 | if (cnt) { | ||
369 | matroxfb_dh_restore(m2info, &mt, mode, pos); | ||
370 | } else { | ||
371 | matroxfb_dh_disable(m2info); | ||
372 | } | ||
373 | DAC1064_global_init(minfo); | ||
374 | DAC1064_global_restore(minfo); | ||
375 | down_read(&minfo->altout.lock); | ||
376 | for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { | ||
377 | if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2 && | ||
378 | minfo->outputs[out].output->program) { | ||
379 | minfo->outputs[out].output->program(minfo->outputs[out].data); | ||
380 | } | ||
381 | } | ||
382 | for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { | ||
383 | if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2 && | ||
384 | minfo->outputs[out].output->start) { | ||
385 | minfo->outputs[out].output->start(minfo->outputs[out].data); | ||
386 | } | ||
387 | } | ||
388 | up_read(&minfo->altout.lock); | ||
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 | struct matrox_fb_info *minfo = m2info->primary_dev; | ||
404 | |||
405 | matroxfb_enable_irq(minfo, 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, &minfo->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 = minfo->crtc2.vsync.cnt; | ||
418 | } | ||
419 | return 0; | ||
420 | } | ||
421 | |||
422 | static int matroxfb_dh_ioctl(struct fb_info *info, | ||
423 | unsigned int cmd, | ||
424 | unsigned long arg) | ||
425 | { | ||
426 | #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) | ||
427 | struct matrox_fb_info *minfo = m2info->primary_dev; | ||
428 | |||
429 | DBG(__func__) | ||
430 | |||
431 | switch (cmd) { | ||
432 | case FBIOGET_VBLANK: | ||
433 | { | ||
434 | struct fb_vblank vblank; | ||
435 | int err; | ||
436 | |||
437 | err = matroxfb_dh_get_vblank(m2info, &vblank); | ||
438 | if (err) | ||
439 | return err; | ||
440 | if (copy_to_user((void __user *)arg, &vblank, sizeof(vblank))) | ||
441 | return -EFAULT; | ||
442 | return 0; | ||
443 | } | ||
444 | case FBIO_WAITFORVSYNC: | ||
445 | { | ||
446 | u_int32_t crt; | ||
447 | |||
448 | if (get_user(crt, (u_int32_t __user *)arg)) | ||
449 | return -EFAULT; | ||
450 | |||
451 | if (crt != 0) | ||
452 | return -ENODEV; | ||
453 | return matroxfb_wait_for_sync(minfo, 1); | ||
454 | } | ||
455 | case MATROXFB_SET_OUTPUT_MODE: | ||
456 | case MATROXFB_GET_OUTPUT_MODE: | ||
457 | case MATROXFB_GET_ALL_OUTPUTS: | ||
458 | { | ||
459 | return minfo->fbcon.fbops->fb_ioctl(&minfo->fbcon, cmd, arg); | ||
460 | } | ||
461 | case MATROXFB_SET_OUTPUT_CONNECTION: | ||
462 | { | ||
463 | u_int32_t tmp; | ||
464 | int out; | ||
465 | int changes; | ||
466 | |||
467 | if (get_user(tmp, (u_int32_t __user *)arg)) | ||
468 | return -EFAULT; | ||
469 | for (out = 0; out < 32; out++) { | ||
470 | if (tmp & (1 << out)) { | ||
471 | if (out >= MATROXFB_MAX_OUTPUTS) | ||
472 | return -ENXIO; | ||
473 | if (!minfo->outputs[out].output) | ||
474 | return -ENXIO; | ||
475 | switch (minfo->outputs[out].src) { | ||
476 | case MATROXFB_SRC_NONE: | ||
477 | case MATROXFB_SRC_CRTC2: | ||
478 | break; | ||
479 | default: | ||
480 | return -EBUSY; | ||
481 | } | ||
482 | } | ||
483 | } | ||
484 | if (minfo->devflags.panellink) { | ||
485 | if (tmp & MATROXFB_OUTPUT_CONN_DFP) | ||
486 | return -EINVAL; | ||
487 | if ((minfo->outputs[2].src == MATROXFB_SRC_CRTC1) && tmp) | ||
488 | return -EBUSY; | ||
489 | } | ||
490 | changes = 0; | ||
491 | for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { | ||
492 | if (tmp & (1 << out)) { | ||
493 | if (minfo->outputs[out].src != MATROXFB_SRC_CRTC2) { | ||
494 | changes = 1; | ||
495 | minfo->outputs[out].src = MATROXFB_SRC_CRTC2; | ||
496 | } | ||
497 | } else if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2) { | ||
498 | changes = 1; | ||
499 | minfo->outputs[out].src = MATROXFB_SRC_NONE; | ||
500 | } | ||
501 | } | ||
502 | if (!changes) | ||
503 | return 0; | ||
504 | matroxfb_dh_set_par(info); | ||
505 | return 0; | ||
506 | } | ||
507 | case MATROXFB_GET_OUTPUT_CONNECTION: | ||
508 | { | ||
509 | u_int32_t conn = 0; | ||
510 | int out; | ||
511 | |||
512 | for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { | ||
513 | if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2) { | ||
514 | conn |= 1 << out; | ||
515 | } | ||
516 | } | ||
517 | if (put_user(conn, (u_int32_t __user *)arg)) | ||
518 | return -EFAULT; | ||
519 | return 0; | ||
520 | } | ||
521 | case MATROXFB_GET_AVAILABLE_OUTPUTS: | ||
522 | { | ||
523 | u_int32_t tmp = 0; | ||
524 | int out; | ||
525 | |||
526 | for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { | ||
527 | if (minfo->outputs[out].output) { | ||
528 | switch (minfo->outputs[out].src) { | ||
529 | case MATROXFB_SRC_NONE: | ||
530 | case MATROXFB_SRC_CRTC2: | ||
531 | tmp |= 1 << out; | ||
532 | break; | ||
533 | } | ||
534 | } | ||
535 | } | ||
536 | if (minfo->devflags.panellink) { | ||
537 | tmp &= ~MATROXFB_OUTPUT_CONN_DFP; | ||
538 | if (minfo->outputs[2].src == MATROXFB_SRC_CRTC1) { | ||
539 | tmp = 0; | ||
540 | } | ||
541 | } | ||
542 | if (put_user(tmp, (u_int32_t __user *)arg)) | ||
543 | return -EFAULT; | ||
544 | return 0; | ||
545 | } | ||
546 | } | ||
547 | return -ENOTTY; | ||
548 | #undef m2info | ||
549 | } | ||
550 | |||
551 | static int matroxfb_dh_blank(int blank, struct fb_info* info) { | ||
552 | #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) | ||
553 | switch (blank) { | ||
554 | case 1: | ||
555 | case 2: | ||
556 | case 3: | ||
557 | case 4: | ||
558 | default:; | ||
559 | } | ||
560 | /* do something... */ | ||
561 | return 0; | ||
562 | #undef m2info | ||
563 | } | ||
564 | |||
565 | static struct fb_ops matroxfb_dh_ops = { | ||
566 | .owner = THIS_MODULE, | ||
567 | .fb_open = matroxfb_dh_open, | ||
568 | .fb_release = matroxfb_dh_release, | ||
569 | .fb_check_var = matroxfb_dh_check_var, | ||
570 | .fb_set_par = matroxfb_dh_set_par, | ||
571 | .fb_setcolreg = matroxfb_dh_setcolreg, | ||
572 | .fb_pan_display =matroxfb_dh_pan_display, | ||
573 | .fb_blank = matroxfb_dh_blank, | ||
574 | .fb_ioctl = matroxfb_dh_ioctl, | ||
575 | .fb_fillrect = cfb_fillrect, | ||
576 | .fb_copyarea = cfb_copyarea, | ||
577 | .fb_imageblit = cfb_imageblit, | ||
578 | }; | ||
579 | |||
580 | static struct fb_var_screeninfo matroxfb_dh_defined = { | ||
581 | 640,480,640,480,/* W,H, virtual W,H */ | ||
582 | 0,0, /* offset */ | ||
583 | 32, /* depth */ | ||
584 | 0, /* gray */ | ||
585 | {0,0,0}, /* R */ | ||
586 | {0,0,0}, /* G */ | ||
587 | {0,0,0}, /* B */ | ||
588 | {0,0,0}, /* alpha */ | ||
589 | 0, /* nonstd */ | ||
590 | FB_ACTIVATE_NOW, | ||
591 | -1,-1, /* display size */ | ||
592 | 0, /* accel flags */ | ||
593 | 39721L,48L,16L,33L,10L, | ||
594 | 96L,2,0, /* no sync info */ | ||
595 | FB_VMODE_NONINTERLACED, | ||
596 | }; | ||
597 | |||
598 | static int matroxfb_dh_regit(const struct matrox_fb_info *minfo, | ||
599 | struct matroxfb_dh_fb_info *m2info) | ||
600 | { | ||
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 (minfo->video.len_usable + mem <= minfo->video.len) | ||
617 | m2info->video.offbase = minfo->video.len - mem; | ||
618 | else if (minfo->video.len < mem) { | ||
619 | return -ENOMEM; | ||
620 | } else { /* check yres on first head... */ | ||
621 | m2info->video.borrowed = mem; | ||
622 | minfo->video.len_usable -= mem; | ||
623 | m2info->video.offbase = minfo->video.len_usable; | ||
624 | } | ||
625 | m2info->video.base = minfo->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(minfo->video.vbase) + m2info->video.offbase; | ||
628 | m2info->mmio.base = minfo->mmio.base; | ||
629 | m2info->mmio.vbase = minfo->mmio.vbase; | ||
630 | m2info->mmio.len = minfo->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(&minfo->crtc2.lock); | ||
639 | oldcrtc2 = minfo->crtc2.info; | ||
640 | minfo->crtc2.info = m2info; | ||
641 | up_write(&minfo->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(minfo, 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 | minfo->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(&minfo->crtc2.lock); | ||
672 | crtc2 = minfo->crtc2.info; | ||
673 | if (crtc2 == m2info) | ||
674 | minfo->crtc2.info = NULL; | ||
675 | up_write(&minfo->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 | minfo->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 (!minfo->devflags.crtc2) | ||
697 | return NULL; | ||
698 | m2info = kzalloc(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 | m2info->primary_dev = minfo; | ||
704 | if (matroxfb_dh_registerfb(m2info)) { | ||
705 | kfree(m2info); | ||
706 | printk(KERN_ERR "matroxfb_crtc2: CRTC2 framebuffer failed to register\n"); | ||
707 | return NULL; | ||
708 | } | ||
709 | return m2info; | ||
710 | } | ||
711 | |||
712 | static void matroxfb_crtc2_remove(struct matrox_fb_info* minfo, void* crtc2) { | ||
713 | matroxfb_dh_deregisterfb(crtc2); | ||
714 | kfree(crtc2); | ||
715 | } | ||
716 | |||
717 | static struct matroxfb_driver crtc2 = { | ||
718 | .name = "Matrox G400 CRTC2", | ||
719 | .probe = matroxfb_crtc2_probe, | ||
720 | .remove = matroxfb_crtc2_remove }; | ||
721 | |||
722 | static int matroxfb_crtc2_init(void) { | ||
723 | if (fb_get_options("matrox_crtc2fb", NULL)) | ||
724 | return -ENODEV; | ||
725 | |||
726 | matroxfb_register_driver(&crtc2); | ||
727 | return 0; | ||
728 | } | ||
729 | |||
730 | static void matroxfb_crtc2_exit(void) { | ||
731 | matroxfb_unregister_driver(&crtc2); | ||
732 | } | ||
733 | |||
734 | MODULE_AUTHOR("(c) 1999-2002 Petr Vandrovec <vandrove@vc.cvut.cz>"); | ||
735 | MODULE_DESCRIPTION("Matrox G400 CRTC2 driver"); | ||
736 | MODULE_LICENSE("GPL"); | ||
737 | module_init(matroxfb_crtc2_init); | ||
738 | module_exit(matroxfb_crtc2_exit); | ||
739 | /* we do not have __setup() yet */ | ||