Subversion Repositories shark

Rev

Details | Last modification | View Log | RSS feed

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