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