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