r128_output.c revision 6f68ce78
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
61static void r128_dpms(xf86OutputPtr output, int mode)
62{
63    switch(mode) {
64    case DPMSModeOn:
65        R128DPMSSetOn(output);
66        break;
67    case DPMSModeStandby:
68    case DPMSModeSuspend:
69    case DPMSModeOff:
70        R128DPMSSetOff(output);
71        break;
72    }
73}
74
75static void r128_save(xf86OutputPtr output)
76{
77}
78
79static void r128_restore(xf86OutputPtr output)
80{
81}
82
83static int r128_mode_valid(xf86OutputPtr output, DisplayModePtr mode)
84{
85    return MODE_OK;
86}
87
88static Bool r128_mode_fixup(xf86OutputPtr output, DisplayModePtr mode, DisplayModePtr adjusted_mode)
89{
90    return TRUE;
91}
92
93static void r128_mode_prepare(xf86OutputPtr output)
94{
95    r128_dpms(output, DPMSModeOff);
96}
97
98static void r128_mode_set(xf86OutputPtr output, DisplayModePtr mode, DisplayModePtr adjusted_mode)
99{
100    ScrnInfoPtr pScrn = output->scrn;
101    R128InfoPtr info = R128PTR(pScrn);
102    R128OutputPrivatePtr r128_output = output->driver_private;
103    xf86CrtcPtr crtc = output->crtc;
104    R128CrtcPrivatePtr r128_crtc = crtc->driver_private;
105
106    if (r128_crtc->crtc_id == 0 && !info->isPro2)
107        R128InitRMXRegisters(&info->SavedReg, &info->ModeReg, output, adjusted_mode);
108
109    if (r128_output->MonType == MT_DFP)
110        R128InitFPRegisters(&info->SavedReg, &info->ModeReg, output);
111    else if (r128_output->MonType == MT_LCD)
112        R128InitLVDSRegisters(&info->SavedReg, &info->ModeReg, output);
113    else if (r128_output->MonType == MT_CRT)
114        R128InitDACRegisters(&info->SavedReg, &info->ModeReg, output);
115
116    if (r128_crtc->crtc_id == 0 && !info->isPro2)
117        R128RestoreRMXRegisters(pScrn, &info->ModeReg);
118
119    if (r128_output->MonType == MT_DFP)
120        R128RestoreFPRegisters(pScrn, &info->ModeReg);
121    else if (r128_output->MonType == MT_LCD)
122        R128RestoreLVDSRegisters(pScrn, &info->ModeReg);
123    else if (r128_output->MonType == MT_CRT)
124        R128RestoreDACRegisters(pScrn, &info->ModeReg);
125}
126
127static void r128_mode_commit(xf86OutputPtr output)
128{
129    r128_dpms(output, DPMSModeOn);
130}
131
132static xf86OutputStatus r128_detect(xf86OutputPtr output)
133{
134    ScrnInfoPtr pScrn = output->scrn;
135    R128OutputPrivatePtr r128_output = output->driver_private;
136
137    r128_output->MonType = MT_UNKNOWN;
138    R128ConnectorFindMonitor(pScrn, output);
139
140    if (r128_output->MonType == MT_UNKNOWN) {
141        output->subpixel_order = SubPixelUnknown;
142        return XF86OutputStatusUnknown;
143    } else if (r128_output->MonType == MT_NONE) {
144        output->subpixel_order = SubPixelUnknown;
145        return XF86OutputStatusDisconnected;
146    } else {
147        switch(r128_output->MonType) {
148        case MT_LCD:
149        case MT_DFP:
150            output->subpixel_order = SubPixelHorizontalRGB;
151            break;
152        default:
153            output->subpixel_order = SubPixelNone;
154            break;
155        }
156
157        return XF86OutputStatusConnected;
158    }
159}
160
161static DisplayModePtr r128_get_modes(xf86OutputPtr output)
162{
163    DisplayModePtr modes;
164    modes = R128ProbeOutputModes(output);
165    return modes;
166}
167
168static void r128_destroy(xf86OutputPtr output)
169{
170    if (output->driver_private)
171        free(output->driver_private);
172}
173
174static const xf86OutputFuncsRec r128_output_funcs = {
175    .dpms = r128_dpms,
176    .save = r128_save,
177    .restore = r128_restore,
178    .mode_valid = r128_mode_valid,
179    .mode_fixup = r128_mode_fixup,
180    .prepare = r128_mode_prepare,
181    .mode_set = r128_mode_set,
182    .commit = r128_mode_commit,
183    .detect = r128_detect,
184    .get_modes = r128_get_modes,
185    .destroy = r128_destroy,
186};
187
188void R128DPMSSetOn(xf86OutputPtr output)
189{
190    ScrnInfoPtr pScrn = output->scrn;
191    R128InfoPtr info = R128PTR(pScrn);
192    unsigned char *R128MMIO = info->MMIO;
193    R128OutputPrivatePtr r128_output = output->driver_private;
194    R128MonitorType MonType = r128_output->MonType;
195    R128SavePtr save = &info->ModeReg;
196
197    switch(MonType) {
198    case MT_LCD:
199#ifdef __NetBSD__
200	if (info->HaveBacklightControl) {
201	    struct wsdisplay_param p;
202
203	    p.param = WSDISPLAYIO_PARAM_BACKLIGHT;
204	    p.curval = 1;
205	    ioctl(xf86Info.consoleFd, WSDISPLAYIO_SETPARAM, &p);
206	} else
207#endif
208	{
209            OUTREGP(R128_LVDS_GEN_CNTL, R128_LVDS_BLON, ~R128_LVDS_BLON);
210            usleep(r128_output->PanelPwrDly * 1000);
211            OUTREGP(R128_LVDS_GEN_CNTL, R128_LVDS_ON, ~R128_LVDS_ON);
212        }
213        save->lvds_gen_cntl |=     (R128_LVDS_ON | R128_LVDS_BLON);
214        break;
215    case MT_DFP:
216        OUTREGP(R128_FP_GEN_CNTL,  (R128_FP_FPON | R128_FP_TMDS_EN), ~(R128_FP_FPON | R128_FP_TMDS_EN));
217        save->fp_gen_cntl   |=     (R128_FP_FPON | R128_FP_TMDS_EN);
218        break;
219    case MT_CRT:
220        OUTREGP(R128_CRTC_EXT_CNTL, R128_CRTC_CRT_ON, ~R128_CRTC_CRT_ON);
221        save->crtc_ext_cntl |=      R128_CRTC_CRT_ON;
222        break;
223    default:
224        break;
225    }
226}
227
228void R128DPMSSetOff(xf86OutputPtr output)
229{
230    ScrnInfoPtr pScrn = output->scrn;
231    R128InfoPtr info = R128PTR(pScrn);
232    unsigned char *R128MMIO = info->MMIO;
233    R128OutputPrivatePtr r128_output = output->driver_private;
234    R128MonitorType MonType = r128_output->MonType;
235    R128SavePtr save = &info->ModeReg;
236
237    switch(MonType) {
238    case MT_LCD:
239#ifdef __NetBSD__
240	if (info->HaveBacklightControl) {
241	    struct wsdisplay_param p;
242
243	    p.param = WSDISPLAYIO_PARAM_BACKLIGHT;
244	    p.curval = 0;
245	    ioctl(xf86Info.consoleFd, WSDISPLAYIO_SETPARAM, &p);
246	} else
247#endif
248	{
249            OUTREGP(R128_LVDS_GEN_CNTL, 0, ~(R128_LVDS_BLON | R128_LVDS_ON));
250        }
251        save->lvds_gen_cntl &=         ~(R128_LVDS_BLON | R128_LVDS_ON);
252        break;
253    case MT_DFP:
254        OUTREGP(R128_FP_GEN_CNTL,   0, ~(R128_FP_FPON | R128_FP_TMDS_EN));
255        save->fp_gen_cntl   &=         ~(R128_FP_FPON | R128_FP_TMDS_EN);
256        break;
257    case MT_CRT:
258        OUTREGP(R128_CRTC_EXT_CNTL, 0, ~(R128_CRTC_CRT_ON));
259        save->crtc_ext_cntl &=         ~(R128_CRTC_CRT_ON);
260        break;
261    default:
262        break;
263    }
264}
265
266static R128MonitorType R128DisplayDDCConnected(xf86OutputPtr output)
267{
268    ScrnInfoPtr pScrn = output->scrn;
269    R128InfoPtr info = R128PTR(pScrn);
270    R128EntPtr pR128Ent = R128EntPriv(pScrn);
271    unsigned char *R128MMIO = info->MMIO;
272    R128OutputPrivatePtr r128_output = output->driver_private;
273
274    R128MonitorType MonType = MT_CRT;
275    xf86MonPtr *MonInfo = &output->MonInfo;
276    uint32_t mask1, mask2;
277
278    if (r128_output->type == OUTPUT_LVDS) {
279#ifdef __NetBSD__
280	if (info->HaveWSDisplay) {
281	    struct wsdisplayio_edid_info ei;
282	    char *buffer;
283	    xf86MonPtr tmp;
284
285	    buffer = malloc(1024);
286	    ei.edid_data = buffer;
287	    ei.buffer_size = 1024;
288	    if (ioctl(xf86Info.consoleFd, WSDISPLAYIO_GET_EDID, &ei) != -1) {
289		xf86Msg(X_INFO, "got %d bytes worth of EDID from wsdisplay\n",
290	    	ei.data_size);
291	    	tmp = xf86InterpretEEDID(pScrn->scrnIndex, buffer);
292	    	tmp->flags |= MONITOR_EDID_COMPLETE_RAWDATA;
293	    	*MonInfo = tmp;
294	    	xf86OutputSetEDID(output, tmp);
295	    } else
296	        free(buffer);
297	}
298#endif
299        return MT_LCD;
300    } else if (r128_output->type == OUTPUT_VGA) {
301        mask1 = R128_GPIO_MONID_MASK_1 | (pR128Ent->HasCRTC2 ? R128_GPIO_MONID_MASK_3 : R128_GPIO_MONID_MASK_2);
302        mask2 = R128_GPIO_MONID_A_1    | (pR128Ent->HasCRTC2 ? R128_GPIO_MONID_A_3    : R128_GPIO_MONID_A_2);
303    } else {
304        mask1 = R128_GPIO_MONID_MASK_0 | R128_GPIO_MONID_MASK_3;
305        mask2 = R128_GPIO_MONID_A_0    | R128_GPIO_MONID_A_3;
306    }
307
308    if (r128_output->pI2CBus) {
309        R128I2CBusPtr pR128I2CBus = &(r128_output->ddc_i2c);
310
311	/* XXX: Radeon does something here to appease old monitors. */
312	OUTREG(pR128I2CBus->ddc_reg, INREG(pR128I2CBus->ddc_reg)  |  mask1);
313	OUTREG(pR128I2CBus->ddc_reg, INREG(pR128I2CBus->ddc_reg)  & ~mask2);
314	*MonInfo = xf86DoEDID_DDC2(XF86_SCRN_ARG(pScrn), r128_output->pI2CBus);
315    } else {
316        xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "DDC2/I2C is not properly initialized\n");
317        return MT_NONE;
318    }
319
320    if (*MonInfo) {
321        if (r128_output->type == OUTPUT_VGA) {
322            MonType = MT_CRT;
323        } else {
324            if ((*MonInfo)->rawData[0x14] & 0x80)
325                MonType = MT_DFP;
326            else
327                MonType = MT_CRT;
328	}
329    }
330
331    return MonType;
332}
333
334static void R128ConnectorFindMonitor(ScrnInfoPtr pScrn, xf86OutputPtr output)
335{
336    R128OutputPrivatePtr r128_output = output->driver_private;
337
338    /* XXX: We should figure out how the DAC and BIOS scratch registers work
339     * to handle the non-DDC case. */
340    if (r128_output->MonType == MT_UNKNOWN)
341        r128_output->MonType = R128DisplayDDCConnected(output);
342}
343
344DisplayModePtr R128ProbeOutputModes(xf86OutputPtr output)
345{
346    ScrnInfoPtr pScrn = output->scrn;
347    R128OutputPrivatePtr r128_output = output->driver_private;
348    DisplayModePtr modes = NULL;
349    DisplayModePtr mode;
350    xf86MonPtr edid_mon;
351
352    if (r128_output->pI2CBus) {
353        edid_mon = xf86OutputGetEDID(output, r128_output->pI2CBus);
354        xf86OutputSetEDID(output, edid_mon);
355    }
356    modes = xf86OutputGetEDIDModes(output);
357
358    /* Letting this function return NULL would be a bad idea. With old cards
359     * like r128, users often specify a small resolution in order to get DRI.
360     * If the X server has to guess modes, the list it comes up with includes
361     * high resolutions.
362     */
363    if (!modes)
364        modes = xf86GetDefaultModes();
365
366    for (mode = modes; mode != NULL; mode = mode->next) {
367        if (r128_output->type == OUTPUT_DVI) {
368            if (mode->type & (M_T_DRIVER | M_T_PREFERRED)) {
369                r128_output->PanelXRes = mode->HDisplay;
370                r128_output->PanelYRes = mode->VDisplay;
371            }
372        }
373
374	xf86SetModeCrtc(mode, INTERLACE_HALVE_V);
375	if (mode->status == MODE_OK)
376            mode->status = R128DoValidMode(output, mode, MODECHECK_FINAL);
377    }
378
379    xf86ValidateModesUserConfig(pScrn, modes);
380    xf86PruneInvalidModes(pScrn, &modes, FALSE);
381
382    return modes;
383}
384
385static xf86OutputPtr R128OutputCreate(ScrnInfoPtr pScrn, const char *name, int i)
386{
387    char buf[32];
388    sprintf(buf, name, i);
389    return xf86OutputCreate(pScrn, &r128_output_funcs, buf);
390}
391
392static void R128I2CGetBits(I2CBusPtr b, int *Clock, int *data)
393{
394    ScrnInfoPtr   pScrn       = xf86Screens[b->scrnIndex];
395    R128InfoPtr   info        = R128PTR(pScrn);
396    unsigned long val;
397    unsigned char *R128MMIO   = info->MMIO;
398    R128I2CBusPtr pR128I2CBus = b->DriverPrivate.ptr;
399
400    /* Get the result. */
401    val = INREG(pR128I2CBus->ddc_reg);
402    *Clock = (val & pR128I2CBus->get_clk_mask)  != 0;
403    *data  = (val & pR128I2CBus->get_data_mask) != 0;
404}
405
406static void R128I2CPutBits(I2CBusPtr b, int Clock, int data)
407{
408    ScrnInfoPtr   pScrn       = xf86Screens[b->scrnIndex];
409    R128InfoPtr   info        = R128PTR(pScrn);
410    unsigned long val;
411    unsigned char *R128MMIO   = info->MMIO;
412    R128I2CBusPtr pR128I2CBus = b->DriverPrivate.ptr;
413
414    val = INREG(pR128I2CBus->ddc_reg)
415              & ~(uint32_t)(pR128I2CBus->put_clk_mask | pR128I2CBus->put_data_mask);
416    val |= (Clock ? 0 : pR128I2CBus->put_clk_mask);
417    val |= (data  ? 0 : pR128I2CBus->put_data_mask);
418    OUTREG(pR128I2CBus->ddc_reg, val);
419}
420
421static Bool R128I2CInit(xf86OutputPtr output, I2CBusPtr *bus_ptr, char *name)
422{
423    ScrnInfoPtr pScrn = output->scrn;
424    R128OutputPrivatePtr r128_output = output->driver_private;
425    R128I2CBusPtr pR128I2CBus = &(r128_output->ddc_i2c);
426    I2CBusPtr pI2CBus;
427
428    pI2CBus = xf86CreateI2CBusRec();
429    if(!pI2CBus) return FALSE;
430
431    pI2CBus->BusName     = name;
432    pI2CBus->scrnIndex   = pScrn->scrnIndex;
433    pI2CBus->I2CPutBits  = R128I2CPutBits;
434    pI2CBus->I2CGetBits  = R128I2CGetBits;
435    pI2CBus->AcknTimeout = 5;
436
437    pI2CBus->DriverPrivate.ptr = (pointer)pR128I2CBus;
438    if (!xf86I2CBusInit(pI2CBus)) return FALSE;
439
440    *bus_ptr = pI2CBus;
441    return TRUE;
442}
443
444void R128SetupGenericConnectors(ScrnInfoPtr pScrn, R128OutputType *otypes)
445{
446    R128InfoPtr info    = R128PTR(pScrn);
447    R128EntPtr pR128Ent = R128EntPriv(pScrn);
448
449    if (!pR128Ent->HasCRTC2 && !info->isDFP) {
450        otypes[0] = OUTPUT_VGA;
451        otypes[1] = OUTPUT_NONE;
452        return;
453    } else if (!pR128Ent->HasCRTC2) {
454        otypes[0] = OUTPUT_DVI;
455        otypes[1] = OUTPUT_NONE;
456	return;
457    }
458
459    otypes[0] = OUTPUT_LVDS;
460    otypes[1] = OUTPUT_VGA;
461}
462
463void R128GetConnectorInfoFromBIOS(ScrnInfoPtr pScrn, R128OutputType *otypes)
464{
465    R128InfoPtr info = R128PTR(pScrn);
466    uint16_t bios_header;
467    int offset;
468
469    /* XXX: Currently, this function only finds VGA ports misidentified as DVI. */
470    if (!info->VBIOS || otypes[0] != OUTPUT_DVI) return;
471
472    bios_header = R128_BIOS16(0x48);
473    offset = R128_BIOS16(bios_header + 0x60);
474
475    if (offset) {
476        otypes[0] = OUTPUT_VGA;
477        xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Found CRT table, assuming VGA connector\n");
478    }
479}
480
481Bool R128SetupConnectors(ScrnInfoPtr pScrn)
482{
483    R128InfoPtr info    = R128PTR(pScrn);
484    R128EntPtr pR128Ent = R128EntPriv(pScrn);
485
486    R128OutputType otypes[R128_MAX_BIOS_CONNECTOR];
487    xf86OutputPtr  output;
488    int num_vga = 0;
489    int num_dvi = 0;
490    int i;
491
492    R128SetupGenericConnectors(pScrn, otypes);
493    R128GetConnectorInfoFromBIOS(pScrn, otypes);
494
495    for (i = 0; i < R128_MAX_BIOS_CONNECTOR; i++) {
496        if (otypes[i] == OUTPUT_VGA)
497            num_vga++;
498        else if (otypes[i] == OUTPUT_DVI)
499            num_dvi++;
500    }
501
502    for (i = 0; i < R128_MAX_BIOS_CONNECTOR; i++) {
503        if (otypes[i] == OUTPUT_NONE) continue;
504
505	R128I2CBusRec i2c;
506        R128OutputPrivatePtr r128_output;
507
508        r128_output = xnfcalloc(sizeof(R128OutputPrivateRec), 1);
509        if (!r128_output) return FALSE;
510
511        r128_output->MonType = MT_UNKNOWN;
512        r128_output->type = otypes[i];
513        r128_output->num = i;
514
515        if (otypes[i] == OUTPUT_LVDS) {
516            output = R128OutputCreate(pScrn, "LVDS", 0);
517        } else if (otypes[i] == OUTPUT_VGA) {
518            output = R128OutputCreate(pScrn, "VGA-%d", --num_vga);
519        } else {
520            output = R128OutputCreate(pScrn, "DVI-%d", --num_dvi);
521        }
522
523        if (!output) return FALSE;
524        output->interlaceAllowed = TRUE;
525        output->doubleScanAllowed = TRUE;
526        output->driver_private = r128_output;
527	output->possible_clones = 0;
528	if (otypes[i] == OUTPUT_LVDS || !pR128Ent->HasCRTC2)
529	    output->possible_crtcs = 1;
530	else
531	    output->possible_crtcs = 2;
532
533        if (otypes[i] != OUTPUT_LVDS && info->DDC) {
534            i2c.ddc_reg      = R128_GPIO_MONID;
535            if (otypes[i] == OUTPUT_VGA && !pR128Ent->HasCRTC2) {
536                i2c.put_clk_mask = R128_GPIO_MONID_EN_2;
537                i2c.get_clk_mask = R128_GPIO_MONID_Y_2;
538            } else {
539                i2c.put_clk_mask = R128_GPIO_MONID_EN_3;
540                i2c.get_clk_mask = R128_GPIO_MONID_Y_3;
541            }
542            if (otypes[i] == OUTPUT_VGA) {
543                i2c.put_data_mask = R128_GPIO_MONID_EN_1;
544                i2c.get_data_mask = R128_GPIO_MONID_Y_1;
545            } else {
546                i2c.put_data_mask = R128_GPIO_MONID_EN_0;
547                i2c.get_data_mask = R128_GPIO_MONID_Y_0;
548            }
549            r128_output->ddc_i2c = i2c;
550            R128I2CInit(output, &r128_output->pI2CBus, output->name);
551        }
552
553        if (otypes[i] == OUTPUT_LVDS)
554            R128GetPanelInfoFromBIOS(output);
555    }
556
557    return TRUE;
558}
559