linux/drivers/video/fbdev/matrox/matroxfb_g450.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 *
   4 * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200, G400 and G450.
   5 *
   6 * (c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz>
   7 *
   8 * Portions Copyright (c) 2001 Matrox Graphics Inc.
   9 *
  10 * Version: 1.65 2002/08/14
  11 *
  12 * See matroxfb_base.c for contributors.
  13 *
  14 */
  15
  16#include "matroxfb_base.h"
  17#include "matroxfb_misc.h"
  18#include "matroxfb_DAC1064.h"
  19#include "g450_pll.h"
  20#include <linux/matroxfb.h>
  21#include <asm/div64.h>
  22
  23#include "matroxfb_g450.h"
  24
  25/* Definition of the various controls */
  26struct mctl {
  27        struct v4l2_queryctrl desc;
  28        size_t control;
  29};
  30
  31#define BLMIN   0xF3
  32#define WLMAX   0x3FF
  33
  34static const struct mctl g450_controls[] =
  35{       { { V4L2_CID_BRIGHTNESS, V4L2_CTRL_TYPE_INTEGER, 
  36          "brightness",
  37          0, WLMAX-BLMIN, 1, 370-BLMIN, 
  38          0,
  39        }, offsetof(struct matrox_fb_info, altout.tvo_params.brightness) },
  40        { { V4L2_CID_CONTRAST, V4L2_CTRL_TYPE_INTEGER, 
  41          "contrast",
  42          0, 1023, 1, 127, 
  43          0,
  44        }, offsetof(struct matrox_fb_info, altout.tvo_params.contrast) },
  45        { { V4L2_CID_SATURATION, V4L2_CTRL_TYPE_INTEGER,
  46          "saturation",
  47          0, 255, 1, 165, 
  48          0,
  49        }, offsetof(struct matrox_fb_info, altout.tvo_params.saturation) },
  50        { { V4L2_CID_HUE, V4L2_CTRL_TYPE_INTEGER,
  51          "hue",
  52          0, 255, 1, 0, 
  53          0,
  54        }, offsetof(struct matrox_fb_info, altout.tvo_params.hue) },
  55        { { MATROXFB_CID_TESTOUT, V4L2_CTRL_TYPE_BOOLEAN,
  56          "test output",
  57          0, 1, 1, 0, 
  58          0,
  59        }, offsetof(struct matrox_fb_info, altout.tvo_params.testout) },
  60};
  61
  62#define G450CTRLS ARRAY_SIZE(g450_controls)
  63
  64/* Return: positive number: id found
  65           -EINVAL:         id not found, return failure
  66           -ENOENT:         id not found, create fake disabled control */
  67static int get_ctrl_id(__u32 v4l2_id) {
  68        int i;
  69
  70        for (i = 0; i < G450CTRLS; i++) {
  71                if (v4l2_id < g450_controls[i].desc.id) {
  72                        if (g450_controls[i].desc.id == 0x08000000) {
  73                                return -EINVAL;
  74                        }
  75                        return -ENOENT;
  76                }
  77                if (v4l2_id == g450_controls[i].desc.id) {
  78                        return i;
  79                }
  80        }
  81        return -EINVAL;
  82}
  83
  84static inline int *get_ctrl_ptr(struct matrox_fb_info *minfo, unsigned int idx)
  85{
  86        return (int*)((char*)minfo + g450_controls[idx].control);
  87}
  88
  89static void tvo_fill_defaults(struct matrox_fb_info *minfo)
  90{
  91        unsigned int i;
  92        
  93        for (i = 0; i < G450CTRLS; i++) {
  94                *get_ctrl_ptr(minfo, i) = g450_controls[i].desc.default_value;
  95        }
  96}
  97
  98static int cve2_get_reg(struct matrox_fb_info *minfo, int reg)
  99{
 100        unsigned long flags;
 101        int val;
 102        
 103        matroxfb_DAC_lock_irqsave(flags);
 104        matroxfb_DAC_out(minfo, 0x87, reg);
 105        val = matroxfb_DAC_in(minfo, 0x88);
 106        matroxfb_DAC_unlock_irqrestore(flags);
 107        return val;
 108}
 109
 110static void cve2_set_reg(struct matrox_fb_info *minfo, int reg, int val)
 111{
 112        unsigned long flags;
 113
 114        matroxfb_DAC_lock_irqsave(flags);
 115        matroxfb_DAC_out(minfo, 0x87, reg);
 116        matroxfb_DAC_out(minfo, 0x88, val);
 117        matroxfb_DAC_unlock_irqrestore(flags);
 118}
 119
 120static void cve2_set_reg10(struct matrox_fb_info *minfo, int reg, int val)
 121{
 122        unsigned long flags;
 123
 124        matroxfb_DAC_lock_irqsave(flags);
 125        matroxfb_DAC_out(minfo, 0x87, reg);
 126        matroxfb_DAC_out(minfo, 0x88, val >> 2);
 127        matroxfb_DAC_out(minfo, 0x87, reg + 1);
 128        matroxfb_DAC_out(minfo, 0x88, val & 3);
 129        matroxfb_DAC_unlock_irqrestore(flags);
 130}
 131
 132static void g450_compute_bwlevel(const struct matrox_fb_info *minfo, int *bl,
 133                                 int *wl)
 134{
 135        const int b = minfo->altout.tvo_params.brightness + BLMIN;
 136        const int c = minfo->altout.tvo_params.contrast;
 137
 138        *bl = max(b - c, BLMIN);
 139        *wl = min(b + c, WLMAX);
 140}
 141
 142static int g450_query_ctrl(void* md, struct v4l2_queryctrl *p) {
 143        int i;
 144        
 145        i = get_ctrl_id(p->id);
 146        if (i >= 0) {
 147                *p = g450_controls[i].desc;
 148                return 0;
 149        }
 150        if (i == -ENOENT) {
 151                static const struct v4l2_queryctrl disctrl = 
 152                        { .flags = V4L2_CTRL_FLAG_DISABLED };
 153                        
 154                i = p->id;
 155                *p = disctrl;
 156                p->id = i;
 157                sprintf(p->name, "Ctrl #%08X", i);
 158                return 0;
 159        }
 160        return -EINVAL;
 161}
 162
 163static int g450_set_ctrl(void* md, struct v4l2_control *p) {
 164        int i;
 165        struct matrox_fb_info *minfo = md;
 166        
 167        i = get_ctrl_id(p->id);
 168        if (i < 0) return -EINVAL;
 169
 170        /*
 171         * Check if changed.
 172         */
 173        if (p->value == *get_ctrl_ptr(minfo, i)) return 0;
 174
 175        /*
 176         * Check limits.
 177         */
 178        if (p->value > g450_controls[i].desc.maximum) return -EINVAL;
 179        if (p->value < g450_controls[i].desc.minimum) return -EINVAL;
 180
 181        /*
 182         * Store new value.
 183         */
 184        *get_ctrl_ptr(minfo, i) = p->value;
 185
 186        switch (p->id) {
 187                case V4L2_CID_BRIGHTNESS:
 188                case V4L2_CID_CONTRAST:
 189                        {
 190                                int blacklevel, whitelevel;
 191                                g450_compute_bwlevel(minfo, &blacklevel, &whitelevel);
 192                                cve2_set_reg10(minfo, 0x0e, blacklevel);
 193                                cve2_set_reg10(minfo, 0x1e, whitelevel);
 194                        }
 195                        break;
 196                case V4L2_CID_SATURATION:
 197                        cve2_set_reg(minfo, 0x20, p->value);
 198                        cve2_set_reg(minfo, 0x22, p->value);
 199                        break;
 200                case V4L2_CID_HUE:
 201                        cve2_set_reg(minfo, 0x25, p->value);
 202                        break;
 203                case MATROXFB_CID_TESTOUT:
 204                        {
 205                                unsigned char val = cve2_get_reg(minfo, 0x05);
 206                                if (p->value) val |=  0x02;
 207                                else          val &= ~0x02;
 208                                cve2_set_reg(minfo, 0x05, val);
 209                        }
 210                        break;
 211        }
 212        
 213
 214        return 0;
 215}
 216
 217static int g450_get_ctrl(void* md, struct v4l2_control *p) {
 218        int i;
 219        struct matrox_fb_info *minfo = md;
 220        
 221        i = get_ctrl_id(p->id);
 222        if (i < 0) return -EINVAL;
 223        p->value = *get_ctrl_ptr(minfo, i);
 224        return 0;
 225}
 226
 227struct output_desc {
 228        unsigned int    h_vis;
 229        unsigned int    h_f_porch;
 230        unsigned int    h_sync;
 231        unsigned int    h_b_porch;
 232        unsigned long long int  chromasc;
 233        unsigned int    burst;
 234        unsigned int    v_total;
 235};
 236
 237static void computeRegs(struct matrox_fb_info *minfo, struct mavenregs *r,
 238                        struct my_timming *mt, const struct output_desc *outd)
 239{
 240        u_int32_t chromasc;
 241        u_int32_t hlen;
 242        u_int32_t hsl;
 243        u_int32_t hbp;
 244        u_int32_t hfp;
 245        u_int32_t hvis;
 246        unsigned int pixclock;
 247        unsigned long long piic;
 248        int mnp;
 249        int over;
 250        
 251        r->regs[0x80] = 0x03;   /* | 0x40 for SCART */
 252
 253        hvis = ((mt->HDisplay << 1) + 3) & ~3;
 254        
 255        if (hvis >= 2048) {
 256                hvis = 2044;
 257        }
 258        
 259        piic = 1000000000ULL * hvis;
 260        do_div(piic, outd->h_vis);
 261
 262        dprintk(KERN_DEBUG "Want %u kHz pixclock\n", (unsigned int)piic);
 263        
 264        mnp = matroxfb_g450_setclk(minfo, piic, M_VIDEO_PLL);
 265        
 266        mt->mnp = mnp;
 267        mt->pixclock = g450_mnp2f(minfo, mnp);
 268
 269        dprintk(KERN_DEBUG "MNP=%08X\n", mnp);
 270
 271        pixclock = 1000000000U / mt->pixclock;
 272
 273        dprintk(KERN_DEBUG "Got %u ps pixclock\n", pixclock);
 274
 275        piic = outd->chromasc;
 276        do_div(piic, mt->pixclock);
 277        chromasc = piic;
 278        
 279        dprintk(KERN_DEBUG "Chroma is %08X\n", chromasc);
 280
 281        r->regs[0] = piic >> 24;
 282        r->regs[1] = piic >> 16;
 283        r->regs[2] = piic >>  8;
 284        r->regs[3] = piic >>  0;
 285        hbp = (((outd->h_b_porch + pixclock) / pixclock)) & ~1;
 286        hfp = (((outd->h_f_porch + pixclock) / pixclock)) & ~1;
 287        hsl = (((outd->h_sync + pixclock) / pixclock)) & ~1;
 288        hlen = hvis + hfp + hsl + hbp;
 289        over = hlen & 0x0F;
 290        
 291        dprintk(KERN_DEBUG "WL: vis=%u, hf=%u, hs=%u, hb=%u, total=%u\n", hvis, hfp, hsl, hbp, hlen);
 292
 293        if (over) {
 294                hfp -= over;
 295                hlen -= over;
 296                if (over <= 2) {
 297                } else if (over < 10) {
 298                        hfp += 4;
 299                        hlen += 4;
 300                } else {
 301                        hfp += 16;
 302                        hlen += 16;
 303                }
 304        }
 305
 306        /* maybe cve2 has requirement 800 < hlen < 1184 */
 307        r->regs[0x08] = hsl;
 308        r->regs[0x09] = (outd->burst + pixclock - 1) / pixclock;        /* burst length */
 309        r->regs[0x0A] = hbp;
 310        r->regs[0x2C] = hfp;
 311        r->regs[0x31] = hvis / 8;
 312        r->regs[0x32] = hvis & 7;
 313        
 314        dprintk(KERN_DEBUG "PG: vis=%04X, hf=%02X, hs=%02X, hb=%02X, total=%04X\n", hvis, hfp, hsl, hbp, hlen);
 315
 316        r->regs[0x84] = 1;      /* x sync point */
 317        r->regs[0x85] = 0;
 318        hvis = hvis >> 1;
 319        hlen = hlen >> 1;
 320        
 321        dprintk(KERN_DEBUG "hlen=%u hvis=%u\n", hlen, hvis);
 322
 323        mt->interlaced = 1;
 324
 325        mt->HDisplay = hvis & ~7;
 326        mt->HSyncStart = mt->HDisplay + 8;
 327        mt->HSyncEnd = (hlen & ~7) - 8;
 328        mt->HTotal = hlen;
 329
 330        {
 331                int upper;
 332                unsigned int vtotal;
 333                unsigned int vsyncend;
 334                unsigned int vdisplay;
 335                
 336                vtotal = mt->VTotal;
 337                vsyncend = mt->VSyncEnd;
 338                vdisplay = mt->VDisplay;
 339                if (vtotal < outd->v_total) {
 340                        unsigned int yovr = outd->v_total - vtotal;
 341                        
 342                        vsyncend += yovr >> 1;
 343                } else if (vtotal > outd->v_total) {
 344                        vdisplay = outd->v_total - 4;
 345                        vsyncend = outd->v_total;
 346                }
 347                upper = (outd->v_total - vsyncend) >> 1;        /* in field lines */
 348                r->regs[0x17] = outd->v_total / 4;
 349                r->regs[0x18] = outd->v_total & 3;
 350                r->regs[0x33] = upper - 1;      /* upper blanking */
 351                r->regs[0x82] = upper;          /* y sync point */
 352                r->regs[0x83] = upper >> 8;
 353                
 354                mt->VDisplay = vdisplay;
 355                mt->VSyncStart = outd->v_total - 2;
 356                mt->VSyncEnd = outd->v_total;
 357                mt->VTotal = outd->v_total;
 358        }
 359}
 360
 361static void cve2_init_TVdata(int norm, struct mavenregs* data, const struct output_desc** outd) {
 362        static const struct output_desc paloutd = {
 363                .h_vis     = 52148148,  // ps
 364                .h_f_porch =  1407407,  // ps
 365                .h_sync    =  4666667,  // ps
 366                .h_b_porch =  5777778,  // ps
 367                .chromasc  = 19042247534182ULL, // 4433618.750 Hz
 368                .burst     =  2518518,  // ps
 369                .v_total   =      625,
 370        };
 371        static const struct output_desc ntscoutd = {
 372                .h_vis     = 52888889,  // ps
 373                .h_f_porch =  1333333,  // ps
 374                .h_sync    =  4666667,  // ps
 375                .h_b_porch =  4666667,  // ps
 376                .chromasc  = 15374030659475ULL, // 3579545.454 Hz
 377                .burst     =  2418418,  // ps
 378                .v_total   =      525,  // lines
 379        };
 380
 381        static const struct mavenregs palregs = { {
 382                0x2A, 0x09, 0x8A, 0xCB, /* 00: chroma subcarrier */
 383                0x00,
 384                0x00,   /* test */
 385                0xF9,   /* modified by code (F9 written...) */
 386                0x00,   /* ? not written */
 387                0x7E,   /* 08 */
 388                0x44,   /* 09 */
 389                0x9C,   /* 0A */
 390                0x2E,   /* 0B */
 391                0x21,   /* 0C */
 392                0x00,   /* ? not written */
 393//              0x3F, 0x03, /* 0E-0F */
 394                0x3C, 0x03,
 395                0x3C, 0x03, /* 10-11 */
 396                0x1A,   /* 12 */
 397                0x2A,   /* 13 */
 398                0x1C, 0x3D, 0x14, /* 14-16 */
 399                0x9C, 0x01, /* 17-18 */
 400                0x00,   /* 19 */
 401                0xFE,   /* 1A */
 402                0x7E,   /* 1B */
 403                0x60,   /* 1C */
 404                0x05,   /* 1D */
 405//              0x89, 0x03, /* 1E-1F */
 406                0xAD, 0x03,
 407//              0x72,   /* 20 */
 408                0xA5,
 409                0x07,   /* 21 */
 410//              0x72,   /* 22 */
 411                0xA5,
 412                0x00,   /* 23 */
 413                0x00,   /* 24 */
 414                0x00,   /* 25 */
 415                0x08,   /* 26 */
 416                0x04,   /* 27 */
 417                0x00,   /* 28 */
 418                0x1A,   /* 29 */
 419                0x55, 0x01, /* 2A-2B */
 420                0x26,   /* 2C */
 421                0x07, 0x7E, /* 2D-2E */
 422                0x02, 0x54, /* 2F-30 */
 423                0xB0, 0x00, /* 31-32 */
 424                0x14,   /* 33 */
 425                0x49,   /* 34 */
 426                0x00,   /* 35 written multiple times */
 427                0x00,   /* 36 not written */
 428                0xA3,   /* 37 */
 429                0xC8,   /* 38 */
 430                0x22,   /* 39 */
 431                0x02,   /* 3A */
 432                0x22,   /* 3B */
 433                0x3F, 0x03, /* 3C-3D */
 434                0x00,   /* 3E written multiple times */
 435                0x00,   /* 3F not written */
 436        } };
 437        static const struct mavenregs ntscregs = { {
 438                0x21, 0xF0, 0x7C, 0x1F, /* 00: chroma subcarrier */
 439                0x00,
 440                0x00,   /* test */
 441                0xF9,   /* modified by code (F9 written...) */
 442                0x00,   /* ? not written */
 443                0x7E,   /* 08 */
 444                0x43,   /* 09 */
 445                0x7E,   /* 0A */
 446                0x3D,   /* 0B */
 447                0x00,   /* 0C */
 448                0x00,   /* ? not written */
 449                0x41, 0x00, /* 0E-0F */
 450                0x3C, 0x00, /* 10-11 */
 451                0x17,   /* 12 */
 452                0x21,   /* 13 */
 453                0x1B, 0x1B, 0x24, /* 14-16 */
 454                0x83, 0x01, /* 17-18 */
 455                0x00,   /* 19 */
 456                0x0F,   /* 1A */
 457                0x0F,   /* 1B */
 458                0x60,   /* 1C */
 459                0x05,   /* 1D */
 460                //0x89, 0x02, /* 1E-1F */
 461                0xC0, 0x02, /* 1E-1F */
 462                //0x5F, /* 20 */
 463                0x9C,   /* 20 */
 464                0x04,   /* 21 */
 465                //0x5F, /* 22 */
 466                0x9C,   /* 22 */
 467                0x01,   /* 23 */
 468                0x02,   /* 24 */
 469                0x00,   /* 25 */
 470                0x0A,   /* 26 */
 471                0x05,   /* 27 */
 472                0x00,   /* 28 */
 473                0x10,   /* 29 */
 474                0xFF, 0x03, /* 2A-2B */
 475                0x24,   /* 2C */
 476                0x0F, 0x78, /* 2D-2E */
 477                0x00, 0x00, /* 2F-30 */
 478                0xB2, 0x04, /* 31-32 */
 479                0x14,   /* 33 */
 480                0x02,   /* 34 */
 481                0x00,   /* 35 written multiple times */
 482                0x00,   /* 36 not written */
 483                0xA3,   /* 37 */
 484                0xC8,   /* 38 */
 485                0x15,   /* 39 */
 486                0x05,   /* 3A */
 487                0x3B,   /* 3B */
 488                0x3C, 0x00, /* 3C-3D */
 489                0x00,   /* 3E written multiple times */
 490                0x00,   /* never written */
 491        } };
 492
 493        if (norm == MATROXFB_OUTPUT_MODE_PAL) {
 494                *data = palregs;
 495                *outd = &paloutd;
 496        } else {
 497                *data = ntscregs;
 498                *outd = &ntscoutd;
 499        }
 500        return;
 501}
 502
 503#define LR(x) cve2_set_reg(minfo, (x), m->regs[(x)])
 504static void cve2_init_TV(struct matrox_fb_info *minfo,
 505                         const struct mavenregs *m)
 506{
 507        int i;
 508
 509        LR(0x80);
 510        LR(0x82); LR(0x83);
 511        LR(0x84); LR(0x85);
 512        
 513        cve2_set_reg(minfo, 0x3E, 0x01);
 514        
 515        for (i = 0; i < 0x3E; i++) {
 516                LR(i);
 517        }
 518        cve2_set_reg(minfo, 0x3E, 0x00);
 519}
 520
 521static int matroxfb_g450_compute(void* md, struct my_timming* mt) {
 522        struct matrox_fb_info *minfo = md;
 523
 524        dprintk(KERN_DEBUG "Computing, mode=%u\n", minfo->outputs[1].mode);
 525
 526        if (mt->crtc == MATROXFB_SRC_CRTC2 &&
 527            minfo->outputs[1].mode != MATROXFB_OUTPUT_MODE_MONITOR) {
 528                const struct output_desc* outd;
 529
 530                cve2_init_TVdata(minfo->outputs[1].mode, &minfo->hw.maven, &outd);
 531                {
 532                        int blacklevel, whitelevel;
 533                        g450_compute_bwlevel(minfo, &blacklevel, &whitelevel);
 534                        minfo->hw.maven.regs[0x0E] = blacklevel >> 2;
 535                        minfo->hw.maven.regs[0x0F] = blacklevel & 3;
 536                        minfo->hw.maven.regs[0x1E] = whitelevel >> 2;
 537                        minfo->hw.maven.regs[0x1F] = whitelevel & 3;
 538
 539                        minfo->hw.maven.regs[0x20] =
 540                        minfo->hw.maven.regs[0x22] = minfo->altout.tvo_params.saturation;
 541
 542                        minfo->hw.maven.regs[0x25] = minfo->altout.tvo_params.hue;
 543
 544                        if (minfo->altout.tvo_params.testout) {
 545                                minfo->hw.maven.regs[0x05] |= 0x02;
 546                        }
 547                }
 548                computeRegs(minfo, &minfo->hw.maven, mt, outd);
 549        } else if (mt->mnp < 0) {
 550                /* We must program clocks before CRTC2, otherwise interlaced mode
 551                   startup may fail */
 552                mt->mnp = matroxfb_g450_setclk(minfo, mt->pixclock, (mt->crtc == MATROXFB_SRC_CRTC1) ? M_PIXEL_PLL_C : M_VIDEO_PLL);
 553                mt->pixclock = g450_mnp2f(minfo, mt->mnp);
 554        }
 555        dprintk(KERN_DEBUG "Pixclock = %u\n", mt->pixclock);
 556        return 0;
 557}
 558
 559static int matroxfb_g450_program(void* md) {
 560        struct matrox_fb_info *minfo = md;
 561        
 562        if (minfo->outputs[1].mode != MATROXFB_OUTPUT_MODE_MONITOR) {
 563                cve2_init_TV(minfo, &minfo->hw.maven);
 564        }
 565        return 0;
 566}
 567
 568static int matroxfb_g450_verify_mode(void* md, u_int32_t arg) {
 569        switch (arg) {
 570                case MATROXFB_OUTPUT_MODE_PAL:
 571                case MATROXFB_OUTPUT_MODE_NTSC:
 572                case MATROXFB_OUTPUT_MODE_MONITOR:
 573                        return 0;
 574        }
 575        return -EINVAL;
 576}
 577
 578static int g450_dvi_compute(void* md, struct my_timming* mt) {
 579        struct matrox_fb_info *minfo = md;
 580
 581        if (mt->mnp < 0) {
 582                mt->mnp = matroxfb_g450_setclk(minfo, mt->pixclock, (mt->crtc == MATROXFB_SRC_CRTC1) ? M_PIXEL_PLL_C : M_VIDEO_PLL);
 583                mt->pixclock = g450_mnp2f(minfo, mt->mnp);
 584        }
 585        return 0;
 586}
 587
 588static struct matrox_altout matroxfb_g450_altout = {
 589        .name           = "Secondary output",
 590        .compute        = matroxfb_g450_compute,
 591        .program        = matroxfb_g450_program,
 592        .verifymode     = matroxfb_g450_verify_mode,
 593        .getqueryctrl   = g450_query_ctrl,
 594        .getctrl        = g450_get_ctrl,
 595        .setctrl        = g450_set_ctrl,
 596};
 597
 598static struct matrox_altout matroxfb_g450_dvi = {
 599        .name           = "DVI output",
 600        .compute        = g450_dvi_compute,
 601};
 602
 603void matroxfb_g450_connect(struct matrox_fb_info *minfo)
 604{
 605        if (minfo->devflags.g450dac) {
 606                down_write(&minfo->altout.lock);
 607                tvo_fill_defaults(minfo);
 608                minfo->outputs[1].src = minfo->outputs[1].default_src;
 609                minfo->outputs[1].data = minfo;
 610                minfo->outputs[1].output = &matroxfb_g450_altout;
 611                minfo->outputs[1].mode = MATROXFB_OUTPUT_MODE_MONITOR;
 612                minfo->outputs[2].src = minfo->outputs[2].default_src;
 613                minfo->outputs[2].data = minfo;
 614                minfo->outputs[2].output = &matroxfb_g450_dvi;
 615                minfo->outputs[2].mode = MATROXFB_OUTPUT_MODE_MONITOR;
 616                up_write(&minfo->altout.lock);
 617        }
 618}
 619
 620void matroxfb_g450_shutdown(struct matrox_fb_info *minfo)
 621{
 622        if (minfo->devflags.g450dac) {
 623                down_write(&minfo->altout.lock);
 624                minfo->outputs[1].src = MATROXFB_SRC_NONE;
 625                minfo->outputs[1].output = NULL;
 626                minfo->outputs[1].data = NULL;
 627                minfo->outputs[1].mode = MATROXFB_OUTPUT_MODE_MONITOR;
 628                minfo->outputs[2].src = MATROXFB_SRC_NONE;
 629                minfo->outputs[2].output = NULL;
 630                minfo->outputs[2].data = NULL;
 631                minfo->outputs[2].mode = MATROXFB_OUTPUT_MODE_MONITOR;
 632                up_write(&minfo->altout.lock);
 633        }
 634}
 635
 636EXPORT_SYMBOL(matroxfb_g450_connect);
 637EXPORT_SYMBOL(matroxfb_g450_shutdown);
 638
 639MODULE_AUTHOR("(c) 2000-2002 Petr Vandrovec <vandrove@vc.cvut.cz>");
 640MODULE_DESCRIPTION("Matrox G450/G550 output driver");
 641MODULE_LICENSE("GPL");
 642