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