Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
matroxfb_crtc2.c
Go to the documentation of this file.
1 /*
2  *
3  * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200, G400 and G450.
4  *
5  * (c) 1998-2002 Petr Vandrovec <[email protected]>
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);
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));
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  }
458  {
459  return minfo->fbcon.fbops->fb_ioctl(&minfo->fbcon, cmd, arg);
460  }
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  }
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  }
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 */
591  -1,-1, /* display size */
592  0, /* accel flags */
593  39721L,48L,16L,33L,10L,
594  96L,2,0, /* no sync info */
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 |
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) {
732 }
733 
734 MODULE_AUTHOR("(c) 1999-2002 Petr Vandrovec <[email protected]>");
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 */