r128_output.c revision 4303943b
1/*
2 * Copyright 2000 ATI Technologies Inc., Markham, Ontario, and
3 *                VA Linux Systems Inc., Fremont, California.
4 *
5 * All Rights Reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining
8 * a copy of this software and associated documentation files (the
9 * "Software"), to deal in the Software without restriction, including
10 * without limitation on the rights to use, copy, modify, merge,
11 * publish, distribute, sublicense, and/or sell copies of the Software,
12 * and to permit persons to whom the Software is furnished to do so,
13 * subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice (including the
16 * next paragraph) shall be included in all copies or substantial
17 * portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 * NON-INFRINGEMENT.  IN NO EVENT SHALL ATI, VA LINUX SYSTEMS AND/OR
23 * THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
24 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 * DEALINGS IN THE SOFTWARE.
27 */
28
29#ifdef HAVE_CONFIG_H
30#include "config.h"
31#endif
32
33#include <string.h>
34#include <stdio.h>
35
36#include "xf86.h"
37#include "xf86Modes.h"
38
39#ifdef HAVE_XEXTPROTO_71
40#include "X11/extensions/dpmsconst.h"
41#else
42#define DPMS_SERVER
43#include "X11/extensions/dpms.h"
44#endif
45
46#include "r128.h"
47#include "r128_probe.h"
48#include "r128_reg.h"
49#include "xf86Priv.h"
50#include "xf86Privstr.h"
51
52#ifdef __NetBSD__
53#include <sys/time.h>
54#include <sys/ioctl.h>
55#include <dev/wscons/wsconsio.h>
56#endif
57
58
59static void R128ConnectorFindMonitor(ScrnInfoPtr pScrn, xf86OutputPtr output);
60
61/* Define DAC registers for the requested video mode. */
62void R128InitDACRegisters(R128SavePtr orig, R128SavePtr save, xf86OutputPtr output)
63{
64    ScrnInfoPtr pScrn = output->scrn;
65    R128InfoPtr info = R128PTR(pScrn);
66    xf86CrtcPtr crtc = output->crtc;
67    R128CrtcPrivatePtr r128_crtc = crtc->driver_private;
68
69    save->dac_cntl = (R128_DAC_MASK_ALL | R128_DAC_VGA_ADR_EN |
70                      (!r128_crtc->crtc_id ? 0 : R128_DAC_CRT_SEL_CRTC2) |
71                      (info->dac6bits      ? 0 : R128_DAC_8BIT_EN));
72}
73
74/* Write DAC registers */
75void R128RestoreDACRegisters(ScrnInfoPtr pScrn, R128SavePtr restore)
76{
77    R128InfoPtr   info      = R128PTR(pScrn);
78    unsigned char *R128MMIO = info->MMIO;
79
80    OUTREGP(R128_DAC_CNTL, restore->dac_cntl,
81        R128_DAC_RANGE_CNTL | R128_DAC_BLANKING);
82}
83
84static void r128_dpms(xf86OutputPtr output, int mode)
85{
86    switch(mode) {
87    case DPMSModeOn:
88        R128DPMSSetOn(output);
89        break;
90    case DPMSModeStandby:
91    case DPMSModeSuspend:
92    case DPMSModeOff:
93        R128DPMSSetOff(output);
94        break;
95    }
96}
97
98static void r128_save(xf86OutputPtr output)
99{
100}
101
102static void r128_restore(xf86OutputPtr output)
103{
104}
105
106static int r128_mode_valid(xf86OutputPtr output, DisplayModePtr mode)
107{
108    return MODE_OK;
109}
110
111static Bool r128_mode_fixup(xf86OutputPtr output, DisplayModePtr mode, DisplayModePtr adjusted_mode)
112{
113    return TRUE;
114}
115
116static void r128_mode_prepare(xf86OutputPtr output)
117{
118    r128_dpms(output, DPMSModeOff);
119}
120
121static void r128_mode_set(xf86OutputPtr output, DisplayModePtr mode, DisplayModePtr adjusted_mode)
122{
123    ScrnInfoPtr pScrn = output->scrn;
124    R128InfoPtr info = R128PTR(pScrn);
125    R128OutputPrivatePtr r128_output = output->driver_private;
126    xf86CrtcPtr crtc = output->crtc;
127    R128CrtcPrivatePtr r128_crtc = crtc->driver_private;
128
129    if (r128_crtc->crtc_id == 0 && !info->isPro2)
130        R128InitRMXRegisters(&info->SavedReg, &info->ModeReg, output, adjusted_mode);
131
132    if (r128_output->MonType == MT_DFP)
133        R128InitFPRegisters(&info->SavedReg, &info->ModeReg, output);
134    else if (r128_output->MonType == MT_LCD)
135        R128InitLVDSRegisters(&info->SavedReg, &info->ModeReg, output);
136    else if (r128_output->MonType == MT_CRT)
137        R128InitDACRegisters(&info->SavedReg, &info->ModeReg, output);
138
139    if (r128_crtc->crtc_id == 0 && !info->isPro2)
140        R128RestoreRMXRegisters(pScrn, &info->ModeReg);
141
142    if (r128_output->MonType == MT_DFP)
143        R128RestoreFPRegisters(pScrn, &info->ModeReg);
144    else if (r128_output->MonType == MT_LCD)
145        R128RestoreLVDSRegisters(pScrn, &info->ModeReg);
146    else if (r128_output->MonType == MT_CRT)
147        R128RestoreDACRegisters(pScrn, &info->ModeReg);
148}
149
150static void r128_mode_commit(xf86OutputPtr output)
151{
152    r128_dpms(output, DPMSModeOn);
153}
154
155static xf86OutputStatus r128_detect(xf86OutputPtr output)
156{
157    ScrnInfoPtr pScrn = output->scrn;
158    R128OutputPrivatePtr r128_output = output->driver_private;
159
160    r128_output->MonType = MT_UNKNOWN;
161    R128ConnectorFindMonitor(pScrn, output);
162
163    if (r128_output->MonType == MT_UNKNOWN) {
164        output->subpixel_order = SubPixelUnknown;
165        return XF86OutputStatusUnknown;
166    } else if (r128_output->MonType == MT_NONE) {
167        output->subpixel_order = SubPixelUnknown;
168        return XF86OutputStatusDisconnected;
169    } else {
170        switch(r128_output->MonType) {
171        case MT_LCD:
172        case MT_DFP:
173            output->subpixel_order = SubPixelHorizontalRGB;
174            break;
175        default:
176            output->subpixel_order = SubPixelNone;
177            break;
178        }
179
180        return XF86OutputStatusConnected;
181    }
182}
183
184static DisplayModePtr r128_get_modes(xf86OutputPtr output)
185{
186    DisplayModePtr modes;
187    modes = R128ProbeOutputModes(output);
188    return modes;
189}
190
191static void r128_destroy(xf86OutputPtr output)
192{
193    if (output->driver_private)
194        free(output->driver_private);
195}
196
197static const xf86OutputFuncsRec r128_output_funcs = {
198    .dpms = r128_dpms,
199    .save = r128_save,
200    .restore = r128_restore,
201    .mode_valid = r128_mode_valid,
202    .mode_fixup = r128_mode_fixup,
203    .prepare = r128_mode_prepare,
204    .mode_set = r128_mode_set,
205    .commit = r128_mode_commit,
206    .detect = r128_detect,
207    .get_modes = r128_get_modes,
208    .destroy = r128_destroy,
209};
210
211void R128DPMSSetOn(xf86OutputPtr output)
212{
213    ScrnInfoPtr pScrn = output->scrn;
214    R128InfoPtr info = R128PTR(pScrn);
215    unsigned char *R128MMIO = info->MMIO;
216    R128OutputPrivatePtr r128_output = output->driver_private;
217    R128MonitorType MonType = r128_output->MonType;
218    R128SavePtr save = &info->ModeReg;
219
220    switch(MonType) {
221    case MT_LCD:
222#ifdef __NetBSD__
223	if (info->HaveBacklightControl) {
224	    struct wsdisplay_param p;
225
226	    p.param = WSDISPLAYIO_PARAM_BACKLIGHT;
227	    p.curval = 1;
228	    ioctl(xf86Info.consoleFd, WSDISPLAYIO_SETPARAM, &p);
229	} else
230#endif
231	{
232            OUTREGP(R128_LVDS_GEN_CNTL, R128_LVDS_BLON, ~R128_LVDS_BLON);
233            usleep(r128_output->PanelPwrDly * 1000);
234            OUTREGP(R128_LVDS_GEN_CNTL, R128_LVDS_ON, ~R128_LVDS_ON);
235        }
236        save->lvds_gen_cntl |=     (R128_LVDS_ON | R128_LVDS_BLON);
237        break;
238    case MT_DFP:
239        OUTREGP(R128_FP_GEN_CNTL,  (R128_FP_FPON | R128_FP_TMDS_EN), ~(R128_FP_FPON | R128_FP_TMDS_EN));
240        save->fp_gen_cntl   |=     (R128_FP_FPON | R128_FP_TMDS_EN);
241        break;
242    case MT_CRT:
243        OUTREGP(R128_CRTC_EXT_CNTL, R128_CRTC_CRT_ON, ~R128_CRTC_CRT_ON);
244        save->crtc_ext_cntl |=      R128_CRTC_CRT_ON;
245        break;
246    default:
247        break;
248    }
249}
250
251void R128DPMSSetOff(xf86OutputPtr output)
252{
253    ScrnInfoPtr pScrn = output->scrn;
254    R128InfoPtr info = R128PTR(pScrn);
255    unsigned char *R128MMIO = info->MMIO;
256    R128OutputPrivatePtr r128_output = output->driver_private;
257    R128MonitorType MonType = r128_output->MonType;
258    R128SavePtr save = &info->ModeReg;
259
260    switch(MonType) {
261    case MT_LCD:
262#ifdef __NetBSD__
263	if (info->HaveBacklightControl) {
264	    struct wsdisplay_param p;
265
266	    p.param = WSDISPLAYIO_PARAM_BACKLIGHT;
267	    p.curval = 0;
268	    ioctl(xf86Info.consoleFd, WSDISPLAYIO_SETPARAM, &p);
269	} else
270#endif
271	{
272            OUTREGP(R128_LVDS_GEN_CNTL, 0, ~(R128_LVDS_BLON | R128_LVDS_ON));
273        }
274        save->lvds_gen_cntl &=         ~(R128_LVDS_BLON | R128_LVDS_ON);
275        break;
276    case MT_DFP:
277        OUTREGP(R128_FP_GEN_CNTL,   0, ~(R128_FP_FPON | R128_FP_TMDS_EN));
278        save->fp_gen_cntl   &=         ~(R128_FP_FPON | R128_FP_TMDS_EN);
279        break;
280    case MT_CRT:
281        OUTREGP(R128_CRTC_EXT_CNTL, 0, ~(R128_CRTC_CRT_ON));
282        save->crtc_ext_cntl &=         ~(R128_CRTC_CRT_ON);
283        break;
284    default:
285        break;
286    }
287}
288
289static R128MonitorType R128DisplayDDCConnected(xf86OutputPtr output)
290{
291    ScrnInfoPtr pScrn = output->scrn;
292    R128InfoPtr info = R128PTR(pScrn);
293    R128EntPtr pR128Ent = R128EntPriv(pScrn);
294    unsigned char *R128MMIO = info->MMIO;
295    R128OutputPrivatePtr r128_output = output->driver_private;
296
297    R128MonitorType MonType = MT_NONE;
298    xf86MonPtr *MonInfo = &output->MonInfo;
299    uint32_t mask1, mask2;
300
301    if (r128_output->type == OUTPUT_LVDS) {
302#ifdef __NetBSD__
303	if (info->HaveWSDisplay) {
304	    struct wsdisplayio_edid_info ei;
305	    char *buffer;
306	    xf86MonPtr tmp;
307
308	    buffer = malloc(1024);
309	    ei.edid_data = buffer;
310	    ei.buffer_size = 1024;
311	    if (ioctl(xf86Info.consoleFd, WSDISPLAYIO_GET_EDID, &ei) != -1) {
312		xf86Msg(X_INFO, "got %d bytes worth of EDID from wsdisplay\n",
313	    	ei.data_size);
314	    	tmp = xf86InterpretEEDID(pScrn->scrnIndex, buffer);
315	    	tmp->flags |= MONITOR_EDID_COMPLETE_RAWDATA;
316	    	*MonInfo = tmp;
317	    	xf86OutputSetEDID(output, tmp);
318	    } else
319	        free(buffer);
320	}
321#endif
322        return MT_LCD;
323    } else if (r128_output->type == OUTPUT_VGA) {
324        mask1 = R128_GPIO_MONID_MASK_1 | (pR128Ent->HasCRTC2 ? R128_GPIO_MONID_MASK_3 : R128_GPIO_MONID_MASK_2);
325        mask2 = R128_GPIO_MONID_A_1    | (pR128Ent->HasCRTC2 ? R128_GPIO_MONID_A_3    : R128_GPIO_MONID_A_2);
326    } else {
327        mask1 = R128_GPIO_MONID_MASK_0 | R128_GPIO_MONID_MASK_3;
328        mask2 = R128_GPIO_MONID_A_0    | R128_GPIO_MONID_A_3;
329    }
330
331    if (r128_output->pI2CBus) {
332        R128I2CBusPtr pR128I2CBus = &(r128_output->ddc_i2c);
333
334        /* XXX: Radeon does something here to appease old monitors. */
335        OUTREG(pR128I2CBus->ddc_reg, INREG(pR128I2CBus->ddc_reg)  |  mask1);
336        OUTREG(pR128I2CBus->ddc_reg, INREG(pR128I2CBus->ddc_reg)  & ~mask2);
337        *MonInfo = xf86DoEDID_DDC2(XF86_SCRN_ARG(pScrn), r128_output->pI2CBus);
338    } else {
339        xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "DDC2/I2C is not properly initialized\n");
340        return MT_NONE;
341    }
342
343    if (*MonInfo) {
344        if (r128_output->type == OUTPUT_VGA) {
345            MonType = MT_CRT;
346        } else {
347            if ((*MonInfo)->rawData[0x14] & 0x80)
348                MonType = MT_DFP;
349            else
350                MonType = MT_CRT;
351        }
352    }
353
354    return MonType;
355}
356
357static void R128ConnectorFindMonitor(ScrnInfoPtr pScrn, xf86OutputPtr output)
358{
359    R128OutputPrivatePtr r128_output = output->driver_private;
360
361    /* XXX: We should figure out how the DAC and BIOS scratch registers work
362     * to handle the non-DDC case. */
363    if (r128_output->MonType == MT_UNKNOWN)
364        r128_output->MonType = R128DisplayDDCConnected(output);
365}
366
367DisplayModePtr R128ProbeOutputModes(xf86OutputPtr output)
368{
369    ScrnInfoPtr pScrn = output->scrn;
370    R128OutputPrivatePtr r128_output = output->driver_private;
371    DisplayModePtr modes = NULL;
372    DisplayModePtr mode;
373    xf86MonPtr edid_mon;
374
375    if (r128_output->pI2CBus) {
376        edid_mon = xf86OutputGetEDID(output, r128_output->pI2CBus);
377        xf86OutputSetEDID(output, edid_mon);
378    }
379    modes = xf86OutputGetEDIDModes(output);
380
381    /* Letting this function return NULL would be a bad idea. With old cards
382     * like r128, users often specify a small resolution in order to get DRI.
383     * If the X server has to guess modes, the list it comes up with includes
384     * high resolutions.
385     */
386    if (!modes)
387        modes = xf86GetDefaultModes();
388
389    for (mode = modes; mode != NULL; mode = mode->next) {
390        if (r128_output->type == OUTPUT_DVI) {
391            if (mode->type & (M_T_DRIVER | M_T_PREFERRED)) {
392                r128_output->PanelXRes = mode->HDisplay;
393                r128_output->PanelYRes = mode->VDisplay;
394            }
395        }
396
397        xf86SetModeCrtc(mode, INTERLACE_HALVE_V);
398        if (mode->status == MODE_OK)
399            mode->status = R128DoValidMode(output, mode, MODECHECK_FINAL);
400    }
401
402    xf86ValidateModesUserConfig(pScrn, modes);
403    xf86PruneInvalidModes(pScrn, &modes, FALSE);
404
405    return modes;
406}
407
408static xf86OutputPtr R128OutputCreate(ScrnInfoPtr pScrn, const char *name, int i)
409{
410    char buf[32];
411    sprintf(buf, name, i);
412    return xf86OutputCreate(pScrn, &r128_output_funcs, buf);
413}
414
415static void R128I2CGetBits(I2CBusPtr b, int *Clock, int *data)
416{
417    ScrnInfoPtr   pScrn       = xf86Screens[b->scrnIndex];
418    R128InfoPtr   info        = R128PTR(pScrn);
419    unsigned long val;
420    unsigned char *R128MMIO   = info->MMIO;
421    R128I2CBusPtr pR128I2CBus = b->DriverPrivate.ptr;
422
423    /* Get the result. */
424    val = INREG(pR128I2CBus->ddc_reg);
425    *Clock = (val & pR128I2CBus->get_clk_mask)  != 0;
426    *data  = (val & pR128I2CBus->get_data_mask) != 0;
427}
428
429static void R128I2CPutBits(I2CBusPtr b, int Clock, int data)
430{
431    ScrnInfoPtr   pScrn       = xf86Screens[b->scrnIndex];
432    R128InfoPtr   info        = R128PTR(pScrn);
433    unsigned long val;
434    unsigned char *R128MMIO   = info->MMIO;
435    R128I2CBusPtr pR128I2CBus = b->DriverPrivate.ptr;
436
437    val = INREG(pR128I2CBus->ddc_reg)
438              & ~(uint32_t)(pR128I2CBus->put_clk_mask | pR128I2CBus->put_data_mask);
439    val |= (Clock ? 0 : pR128I2CBus->put_clk_mask);
440    val |= (data  ? 0 : pR128I2CBus->put_data_mask);
441    OUTREG(pR128I2CBus->ddc_reg, val);
442}
443
444static Bool R128I2CInit(xf86OutputPtr output, I2CBusPtr *bus_ptr, char *name)
445{
446    ScrnInfoPtr pScrn = output->scrn;
447    R128OutputPrivatePtr r128_output = output->driver_private;
448    R128I2CBusPtr pR128I2CBus = &(r128_output->ddc_i2c);
449    I2CBusPtr pI2CBus;
450
451    pI2CBus = xf86CreateI2CBusRec();
452    if(!pI2CBus) return FALSE;
453
454    pI2CBus->BusName     = name;
455    pI2CBus->scrnIndex   = pScrn->scrnIndex;
456    pI2CBus->I2CPutBits  = R128I2CPutBits;
457    pI2CBus->I2CGetBits  = R128I2CGetBits;
458    pI2CBus->AcknTimeout = 5;
459
460    pI2CBus->DriverPrivate.ptr = (pointer)pR128I2CBus;
461    if (!xf86I2CBusInit(pI2CBus)) return FALSE;
462
463    *bus_ptr = pI2CBus;
464    return TRUE;
465}
466
467void R128GetConnectorInfoFromBIOS(ScrnInfoPtr pScrn, R128OutputType *otypes)
468{
469    R128InfoPtr info = R128PTR(pScrn);
470    uint16_t bios_header, offset;
471    uint32_t i;
472
473    for (i = 0; i < R128_MAX_BIOS_CONNECTOR; i++) {
474        otypes[i] = OUTPUT_NONE;
475    }
476
477    /* non-x86 platform */
478    if (!info->VBIOS) {
479    	if (info->isDFP) {
480    	    /* XXX assume LVDS on chips that can have them */
481            otypes[0] = OUTPUT_LVDS;
482            otypes[1] = OUTPUT_VGA;
483        } else {
484            otypes[0] = OUTPUT_VGA;
485        }
486        return;
487    }
488
489    bios_header = R128_BIOS16(0x48);
490    offset = R128_BIOS16(bios_header + 0x40);
491    if (offset) {
492        otypes[0] = OUTPUT_LVDS;
493        xf86DrvMsg(pScrn->scrnIndex, X_INFO,
494                    "Found FP table, assuming FP connector.\n");
495    } else {
496        bios_header = R128_BIOS16(0x48);
497        offset = R128_BIOS16(bios_header + 0x34);
498        if (offset) {
499            otypes[0] = OUTPUT_DVI;
500            xf86DrvMsg(pScrn->scrnIndex, X_INFO,
501                        "Found DVI table, assuming DVI connector.\n");
502        }
503    }
504
505    offset = R128_BIOS16(bios_header + 0x2e);
506    if (offset) {
507        if (otypes[0] == OUTPUT_NONE) {
508            otypes[0] = OUTPUT_VGA;
509        } else {
510            otypes[1] = OUTPUT_VGA;
511        }
512
513        xf86DrvMsg(pScrn->scrnIndex, X_INFO,
514                    "Found CRT table, assuming VGA connector.\n");
515    }
516}
517
518Bool R128SetupConnectors(ScrnInfoPtr pScrn)
519{
520    R128InfoPtr info    = R128PTR(pScrn);
521    R128EntPtr pR128Ent = R128EntPriv(pScrn);
522
523    R128OutputType otypes[R128_MAX_BIOS_CONNECTOR];
524    xf86OutputPtr  output;
525    int num_vga = 0;
526    int num_dvi = 0;
527    int i;
528
529    R128GetConnectorInfoFromBIOS(pScrn, otypes);
530
531    for (i = 0; i < R128_MAX_BIOS_CONNECTOR; i++) {
532        if (otypes[i] == OUTPUT_VGA)
533            num_vga++;
534        else if (otypes[i] == OUTPUT_DVI)
535            num_dvi++;
536    }
537
538    for (i = 0; i < R128_MAX_BIOS_CONNECTOR; i++) {
539        if (otypes[i] == OUTPUT_NONE) continue;
540
541        R128I2CBusRec i2c;
542        R128OutputPrivatePtr r128_output;
543
544        r128_output = xnfcalloc(sizeof(R128OutputPrivateRec), 1);
545        if (!r128_output) return FALSE;
546
547        r128_output->MonType = MT_UNKNOWN;
548        r128_output->type = otypes[i];
549        r128_output->num = i;
550
551        if (otypes[i] == OUTPUT_LVDS) {
552            output = R128OutputCreate(pScrn, "LVDS", 0);
553        } else if (otypes[i] == OUTPUT_VGA) {
554            output = R128OutputCreate(pScrn, "VGA-%d", --num_vga);
555        } else {
556            output = R128OutputCreate(pScrn, "DVI-%d", --num_dvi);
557        }
558
559        if (!output) return FALSE;
560        output->interlaceAllowed = TRUE;
561        output->doubleScanAllowed = TRUE;
562        output->driver_private = r128_output;
563        output->possible_clones = 0;
564        if (otypes[i] == OUTPUT_LVDS || !pR128Ent->HasCRTC2)
565            output->possible_crtcs = 1;
566        else
567            output->possible_crtcs = 2;
568
569        if (otypes[i] != OUTPUT_LVDS && info->DDC) {
570            i2c.ddc_reg      = R128_GPIO_MONID;
571            if (otypes[i] == OUTPUT_VGA && !pR128Ent->HasCRTC2) {
572                i2c.put_clk_mask = R128_GPIO_MONID_EN_2;
573                i2c.get_clk_mask = R128_GPIO_MONID_Y_2;
574            } else {
575                i2c.put_clk_mask = R128_GPIO_MONID_EN_3;
576                i2c.get_clk_mask = R128_GPIO_MONID_Y_3;
577            }
578            if (otypes[i] == OUTPUT_VGA) {
579                i2c.put_data_mask = R128_GPIO_MONID_EN_1;
580                i2c.get_data_mask = R128_GPIO_MONID_Y_1;
581            } else {
582                i2c.put_data_mask = R128_GPIO_MONID_EN_0;
583                i2c.get_data_mask = R128_GPIO_MONID_Y_0;
584            }
585            r128_output->ddc_i2c = i2c;
586            R128I2CInit(output, &r128_output->pI2CBus, output->name);
587        }
588
589        if (otypes[i] == OUTPUT_LVDS)
590            R128GetPanelInfoFromBIOS(output);
591    }
592
593    return TRUE;
594}
595