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> | |
5a0e3ad6 | 18 | #include <linux/slab.h> |
84902b7a | 19 | #include <linux/uaccess.h> |
1da177e4 LT |
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; | |
ee5a2749 | 69 | struct matrox_fb_info *minfo = m2info->primary_dev; |
1da177e4 LT |
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; | |
fc2d10dd JD |
85 | if (minfo->outputs[1].src == MATROXFB_SRC_CRTC2) { |
86 | if (minfo->devflags.g450dac) { | |
1da177e4 LT |
87 | tmp |= 0x00000006; /* source from secondary pixel PLL */ |
88 | /* no vidrst when in monitor mode */ | |
fc2d10dd | 89 | if (minfo->outputs[1].mode != MATROXFB_OUTPUT_MODE_MONITOR) { |
1da177e4 LT |
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 | } | |
fc2d10dd | 97 | } else if (minfo->outputs[0].src == MATROXFB_SRC_CRTC2) { |
1da177e4 LT |
98 | tmp |= 0x00000004; /* source from pixclock */ |
99 | /* PIXPLL is our clock source */ | |
100 | } | |
fc2d10dd | 101 | if (minfo->outputs[0].src == MATROXFB_SRC_CRTC2) { |
1da177e4 LT |
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); | |
fc2d10dd | 150 | minfo->hw.crtc2.ctl = tmp; |
1da177e4 LT |
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) { | |
ee5a2749 | 161 | struct matrox_fb_info *minfo = m2info->primary_dev; |
1da177e4 LT |
162 | |
163 | mga_outl(0x3C10, 0x00000004); /* disable CRTC2, CRTC1->DAC1, PLL as clock source */ | |
fc2d10dd | 164 | minfo->hw.crtc2.ctl = 0x00000004; |
1da177e4 LT |
165 | } |
166 | ||
1da177e4 LT |
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; | |
ee5a2749 | 172 | struct matrox_fb_info *minfo = m2info->primary_dev; |
1da177e4 LT |
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)) | |
ee5a2749 | 264 | struct matrox_fb_info *minfo = m2info->primary_dev; |
1da177e4 | 265 | |
fc2d10dd | 266 | if (minfo) { |
1da177e4 LT |
267 | int err; |
268 | ||
fc2d10dd | 269 | if (minfo->dead) { |
1da177e4 LT |
270 | return -ENXIO; |
271 | } | |
fc2d10dd | 272 | err = minfo->fbops.fb_open(&minfo->fbcon, user); |
1da177e4 LT |
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; | |
ee5a2749 | 284 | struct matrox_fb_info *minfo = m2info->primary_dev; |
1da177e4 | 285 | |
fc2d10dd JD |
286 | if (minfo) { |
287 | err = minfo->fbops.fb_release(&minfo->fbcon, user); | |
1da177e4 LT |
288 | } |
289 | return err; | |
290 | #undef m2info | |
291 | } | |
292 | ||
99f5d48b KH |
293 | /* |
294 | * This function is called before the register_framebuffer so | |
295 | * no locking is needed. | |
296 | */ | |
537a1bf0 KH |
297 | static void matroxfb_dh_init_fix(struct matroxfb_dh_fb_info *m2info) |
298 | { | |
1da177e4 LT |
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; | |
ee5a2749 | 330 | struct matrox_fb_info *minfo = m2info->primary_dev; |
1da177e4 LT |
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; | |
fc2d10dd | 356 | down_read(&minfo->altout.lock); |
1da177e4 | 357 | for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { |
fc2d10dd | 358 | if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2) { |
1da177e4 | 359 | cnt++; |
fc2d10dd JD |
360 | if (minfo->outputs[out].output->compute) { |
361 | minfo->outputs[out].output->compute(minfo->outputs[out].data, &mt); | |
1da177e4 LT |
362 | } |
363 | } | |
364 | } | |
fc2d10dd JD |
365 | minfo->crtc2.pixclock = mt.pixclock; |
366 | minfo->crtc2.mnp = mt.mnp; | |
367 | up_read(&minfo->altout.lock); | |
1da177e4 LT |
368 | if (cnt) { |
369 | matroxfb_dh_restore(m2info, &mt, mode, pos); | |
370 | } else { | |
371 | matroxfb_dh_disable(m2info); | |
372 | } | |
316b4d64 JD |
373 | DAC1064_global_init(minfo); |
374 | DAC1064_global_restore(minfo); | |
fc2d10dd | 375 | down_read(&minfo->altout.lock); |
1da177e4 | 376 | for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { |
fc2d10dd JD |
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); | |
1da177e4 LT |
380 | } |
381 | } | |
382 | for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { | |
fc2d10dd JD |
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); | |
1da177e4 LT |
386 | } |
387 | } | |
fc2d10dd | 388 | up_read(&minfo->altout.lock); |
1da177e4 LT |
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) { | |
ee5a2749 | 403 | struct matrox_fb_info *minfo = m2info->primary_dev; |
1da177e4 | 404 | |
316b4d64 | 405 | matroxfb_enable_irq(minfo, 0); |
1da177e4 LT |
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; | |
fc2d10dd | 413 | if (test_bit(0, &minfo->irq_flags)) { |
1da177e4 LT |
414 | vblank->flags |= FB_VBLANK_HAVE_COUNT; |
415 | /* Only one writer, aligned int value... | |
416 | it should work without lock and without atomic_t */ | |
fc2d10dd | 417 | vblank->count = minfo->crtc2.vsync.cnt; |
1da177e4 LT |
418 | } |
419 | return 0; | |
420 | } | |
421 | ||
67a6680d | 422 | static int matroxfb_dh_ioctl(struct fb_info *info, |
1da177e4 | 423 | unsigned int cmd, |
67a6680d CH |
424 | unsigned long arg) |
425 | { | |
1da177e4 | 426 | #define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) |
ee5a2749 | 427 | struct matrox_fb_info *minfo = m2info->primary_dev; |
1da177e4 | 428 | |
5ae12170 | 429 | DBG(__func__) |
1da177e4 LT |
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; | |
316b4d64 | 453 | return matroxfb_wait_for_sync(minfo, 1); |
1da177e4 LT |
454 | } |
455 | case MATROXFB_SET_OUTPUT_MODE: | |
456 | case MATROXFB_GET_OUTPUT_MODE: | |
457 | case MATROXFB_GET_ALL_OUTPUTS: | |
458 | { | |
fc2d10dd | 459 | return minfo->fbcon.fbops->fb_ioctl(&minfo->fbcon, cmd, arg); |
1da177e4 LT |
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; | |
fc2d10dd | 473 | if (!minfo->outputs[out].output) |
1da177e4 | 474 | return -ENXIO; |
fc2d10dd | 475 | switch (minfo->outputs[out].src) { |
1da177e4 LT |
476 | case MATROXFB_SRC_NONE: |
477 | case MATROXFB_SRC_CRTC2: | |
478 | break; | |
479 | default: | |
480 | return -EBUSY; | |
481 | } | |
482 | } | |
483 | } | |
fc2d10dd | 484 | if (minfo->devflags.panellink) { |
1da177e4 LT |
485 | if (tmp & MATROXFB_OUTPUT_CONN_DFP) |
486 | return -EINVAL; | |
fc2d10dd | 487 | if ((minfo->outputs[2].src == MATROXFB_SRC_CRTC1) && tmp) |
1da177e4 LT |
488 | return -EBUSY; |
489 | } | |
490 | changes = 0; | |
491 | for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { | |
492 | if (tmp & (1 << out)) { | |
fc2d10dd | 493 | if (minfo->outputs[out].src != MATROXFB_SRC_CRTC2) { |
1da177e4 | 494 | changes = 1; |
fc2d10dd | 495 | minfo->outputs[out].src = MATROXFB_SRC_CRTC2; |
1da177e4 | 496 | } |
fc2d10dd | 497 | } else if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2) { |
1da177e4 | 498 | changes = 1; |
fc2d10dd | 499 | minfo->outputs[out].src = MATROXFB_SRC_NONE; |
1da177e4 LT |
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++) { | |
fc2d10dd | 513 | if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2) { |
1da177e4 LT |
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++) { | |
fc2d10dd JD |
527 | if (minfo->outputs[out].output) { |
528 | switch (minfo->outputs[out].src) { | |
1da177e4 LT |
529 | case MATROXFB_SRC_NONE: |
530 | case MATROXFB_SRC_CRTC2: | |
531 | tmp |= 1 << out; | |
532 | break; | |
533 | } | |
534 | } | |
535 | } | |
fc2d10dd | 536 | if (minfo->devflags.panellink) { |
1da177e4 | 537 | tmp &= ~MATROXFB_OUTPUT_CONN_DFP; |
fc2d10dd | 538 | if (minfo->outputs[2].src == MATROXFB_SRC_CRTC1) { |
1da177e4 LT |
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, | |
1da177e4 LT |
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 | 0, {0,0,0,0,0} | |
597 | }; | |
598 | ||
316b4d64 JD |
599 | static int matroxfb_dh_regit(const struct matrox_fb_info *minfo, |
600 | struct matroxfb_dh_fb_info *m2info) | |
601 | { | |
1da177e4 LT |
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? */ | |
fc2d10dd JD |
617 | if (minfo->video.len_usable + mem <= minfo->video.len) |
618 | m2info->video.offbase = minfo->video.len - mem; | |
619 | else if (minfo->video.len < mem) { | |
1da177e4 LT |
620 | return -ENOMEM; |
621 | } else { /* check yres on first head... */ | |
622 | m2info->video.borrowed = mem; | |
fc2d10dd JD |
623 | minfo->video.len_usable -= mem; |
624 | m2info->video.offbase = minfo->video.len_usable; | |
1da177e4 | 625 | } |
fc2d10dd | 626 | m2info->video.base = minfo->video.base + m2info->video.offbase; |
1da177e4 | 627 | m2info->video.len = m2info->video.len_usable = m2info->video.len_maximum = mem; |
fc2d10dd JD |
628 | m2info->video.vbase.vaddr = vaddr_va(minfo->video.vbase) + m2info->video.offbase; |
629 | m2info->mmio.base = minfo->mmio.base; | |
630 | m2info->mmio.vbase = minfo->mmio.vbase; | |
631 | m2info->mmio.len = minfo->mmio.len; | |
1da177e4 LT |
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); | |
fc2d10dd JD |
639 | down_write(&minfo->crtc2.lock); |
640 | oldcrtc2 = minfo->crtc2.info; | |
641 | minfo->crtc2.info = m2info; | |
642 | up_write(&minfo->crtc2.lock); | |
1da177e4 LT |
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) | |
316b4d64 | 655 | if (matroxfb_dh_regit(minfo, m2info)) { |
1da177e4 LT |
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", | |
fc2d10dd | 660 | minfo->fbcon.node, m2info->fbcon.node); |
1da177e4 LT |
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 | ||
fc2d10dd JD |
672 | down_write(&minfo->crtc2.lock); |
673 | crtc2 = minfo->crtc2.info; | |
1da177e4 | 674 | if (crtc2 == m2info) |
fc2d10dd JD |
675 | minfo->crtc2.info = NULL; |
676 | up_write(&minfo->crtc2.lock); | |
1da177e4 LT |
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 */ | |
fc2d10dd | 686 | minfo->video.len_usable += m2info->video.borrowed; |
1da177e4 LT |
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... */ | |
fc2d10dd | 697 | if (!minfo->devflags.crtc2) |
1da177e4 | 698 | return NULL; |
2fdbe5cf | 699 | m2info = kzalloc(sizeof(*m2info), GFP_KERNEL); |
1da177e4 LT |
700 | if (!m2info) { |
701 | printk(KERN_ERR "matroxfb_crtc2: Not enough memory for CRTC2 control structs\n"); | |
702 | return NULL; | |
703 | } | |
fc2d10dd | 704 | m2info->primary_dev = minfo; |
1da177e4 LT |
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 */ |