r128_output.c revision e1efbb8a
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#ifdef __NetBSD__
193	if (info->HaveBacklightControl) {
194	    struct wsdisplay_param p;
195
196	    p.param = WSDISPLAYIO_PARAM_BACKLIGHT;
197	    p.curval = 1;
198	    ioctl(xf86Info.screenFd, WSDISPLAYIO_SETPARAM, &p);
199	} else
200#endif
201	{
202            OUTREGP(R128_LVDS_GEN_CNTL, R128_LVDS_BLON, ~R128_LVDS_BLON);
203            usleep(r128_output->PanelPwrDly * 1000);
204            OUTREGP(R128_LVDS_GEN_CNTL, R128_LVDS_ON, ~R128_LVDS_ON);
205        }
206        save->lvds_gen_cntl |=     (R128_LVDS_ON | R128_LVDS_BLON);
207        break;
208    case MT_DFP:
209        OUTREGP(R128_FP_GEN_CNTL,  (R128_FP_FPON | R128_FP_TMDS_EN), ~(R128_FP_FPON | R128_FP_TMDS_EN));
210        save->fp_gen_cntl   |=     (R128_FP_FPON | R128_FP_TMDS_EN);
211        break;
212    case MT_CRT:
213        OUTREGP(R128_CRTC_EXT_CNTL, R128_CRTC_CRT_ON, ~R128_CRTC_CRT_ON);
214        save->crtc_ext_cntl |=      R128_CRTC_CRT_ON;
215        break;
216    default:
217        break;
218    }
219}
220
221void R128DPMSSetOff(xf86OutputPtr output)
222{
223    ScrnInfoPtr pScrn = output->scrn;
224    R128InfoPtr info = R128PTR(pScrn);
225    unsigned char *R128MMIO = info->MMIO;
226    R128OutputPrivatePtr r128_output = output->driver_private;
227    R128MonitorType MonType = r128_output->MonType;
228    R128SavePtr save = &info->ModeReg;
229
230    switch(MonType) {
231    case MT_LCD:
232#ifdef __NetBSD__
233	if (info->HaveBacklightControl) {
234	    struct wsdisplay_param p;
235
236	    p.param = WSDISPLAYIO_PARAM_BACKLIGHT;
237	    p.curval = 0;
238	    ioctl(xf86Info.screenFd, WSDISPLAYIO_SETPARAM, &p);
239	} else
240#endif
241	{
242            OUTREGP(R128_LVDS_GEN_CNTL, 0, ~(R128_LVDS_BLON | R128_LVDS_ON));
243        }
244        save->lvds_gen_cntl &=         ~(R128_LVDS_BLON | R128_LVDS_ON);
245        break;
246    case MT_DFP:
247        OUTREGP(R128_FP_GEN_CNTL,   0, ~(R128_FP_FPON | R128_FP_TMDS_EN));
248        save->fp_gen_cntl   &=         ~(R128_FP_FPON | R128_FP_TMDS_EN);
249        break;
250    case MT_CRT:
251        OUTREGP(R128_CRTC_EXT_CNTL, 0, ~(R128_CRTC_CRT_ON));
252        save->crtc_ext_cntl &=         ~(R128_CRTC_CRT_ON);
253        break;
254    default:
255        break;
256    }
257}
258
259static R128MonitorType R128DisplayDDCConnected(xf86OutputPtr output)
260{
261    ScrnInfoPtr pScrn = output->scrn;
262    R128InfoPtr info = R128PTR(pScrn);
263    unsigned char *R128MMIO = info->MMIO;
264    R128OutputPrivatePtr r128_output = output->driver_private;
265
266    R128MonitorType MonType = MT_NONE;
267    xf86MonPtr *MonInfo = &output->MonInfo;
268    uint32_t mask1, mask2;
269
270    if (r128_output->type == OUTPUT_LVDS) {
271#ifdef __NetBSD__
272	if (info->HaveWSDisplay) {
273	    struct wsdisplayio_edid_info ei;
274	    char *buffer;
275	    xf86MonPtr tmp;
276
277	    buffer = malloc(1024);
278	    ei.edid_data = buffer;
279	    ei.buffer_size = 1024;
280	    if (ioctl(xf86Info.screenFd, WSDISPLAYIO_GET_EDID, &ei) != -1) {
281		xf86Msg(X_INFO, "got %d bytes worth of EDID from wsdisplay\n",
282	    	ei.data_size);
283	    	tmp = xf86InterpretEEDID(pScrn->scrnIndex, buffer);
284	    	tmp->flags |= MONITOR_EDID_COMPLETE_RAWDATA;
285	    	*MonInfo = tmp;
286	    	xf86OutputSetEDID(output, tmp);
287	    } else
288	        free(buffer);
289	}
290#endif
291        return MT_LCD;
292    } else if (r128_output->type == OUTPUT_VGA) {
293        mask1 = R128_GPIO_MONID_MASK_1 | (info->isPro2 ? R128_GPIO_MONID_MASK_2 : R128_GPIO_MONID_MASK_3);
294        mask2 = R128_GPIO_MONID_A_1    | (info->isPro2 ? R128_GPIO_MONID_A_2    : R128_GPIO_MONID_A_3);
295    } else {
296        mask1 = R128_GPIO_MONID_MASK_0 | R128_GPIO_MONID_MASK_3;
297        mask2 = R128_GPIO_MONID_A_0    | R128_GPIO_MONID_A_3;
298    }
299
300    if (r128_output->pI2CBus) {
301        R128I2CBusPtr pR128I2CBus = &(r128_output->ddc_i2c);
302
303	/* XXX: Radeon does something here to appease old monitors. */
304	OUTREG(pR128I2CBus->ddc_reg, INREG(pR128I2CBus->ddc_reg)  |  mask1);
305	OUTREG(pR128I2CBus->ddc_reg, INREG(pR128I2CBus->ddc_reg)  & ~mask2);
306	*MonInfo = xf86DoEDID_DDC2(XF86_SCRN_ARG(pScrn), r128_output->pI2CBus);
307    } else {
308        xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "DDC2/I2C is not properly initialized\n");
309        return MT_NONE;
310    }
311
312    if (*MonInfo) {
313        if (r128_output->type == OUTPUT_VGA) {
314            MonType = MT_CRT;
315        } else {
316            if ((*MonInfo)->rawData[0x14] & 0x80)
317                MonType = MT_DFP;
318            else
319                MonType = MT_CRT;
320	}
321    } else if (xf86I2CProbeAddress(r128_output->pI2CBus, 0x0060)) {
322        /* Just in case. */
323        MonType = MT_CRT;
324    }
325
326    return MonType;
327}
328
329static void R128ConnectorFindMonitor(ScrnInfoPtr pScrn, xf86OutputPtr output)
330{
331    R128OutputPrivatePtr r128_output = output->driver_private;
332
333    /* XXX: We should figure out how the DAC and BIOS scratch registers work
334     * to handle the non-DDC case. */
335    if (r128_output->MonType == MT_UNKNOWN)
336        r128_output->MonType = R128DisplayDDCConnected(output);
337}
338
339DisplayModePtr R128ProbeOutputModes(xf86OutputPtr output)
340{
341    ScrnInfoPtr pScrn = output->scrn;
342    R128OutputPrivatePtr r128_output = output->driver_private;
343    DisplayModePtr modes = NULL;
344    DisplayModePtr mode;
345    xf86MonPtr edid_mon;
346
347    if (r128_output->pI2CBus) {
348        edid_mon = xf86OutputGetEDID(output, r128_output->pI2CBus);
349        xf86OutputSetEDID(output, edid_mon);
350    }
351    modes = xf86OutputGetEDIDModes(output);
352
353    /* Letting this function return NULL would be a bad idea. With old cards
354     * like r128, users often specify a small resolution in order to get DRI.
355     * If the X server has to guess modes, the list it comes up with includes
356     * high resolutions.
357     */
358    if (!modes)
359        modes = xf86GetDefaultModes();
360
361    for (mode = modes; mode != NULL; mode = mode->next) {
362        if (r128_output->type == OUTPUT_DVI) {
363            if (mode->type & (M_T_DRIVER | M_T_PREFERRED)) {
364                r128_output->PanelXRes = mode->HDisplay;
365                r128_output->PanelYRes = mode->VDisplay;
366            }
367        }
368
369	xf86SetModeCrtc(mode, INTERLACE_HALVE_V);
370	if (mode->status == MODE_OK)
371            mode->status = R128DoValidMode(output, mode, MODECHECK_FINAL);
372    }
373
374    xf86ValidateModesUserConfig(pScrn, modes);
375    xf86PruneInvalidModes(pScrn, &modes, FALSE);
376
377    return modes;
378}
379
380static xf86OutputPtr R128OutputCreate(ScrnInfoPtr pScrn, const char *name, int i)
381{
382    char buf[32];
383    sprintf(buf, name, i);
384    return xf86OutputCreate(pScrn, &r128_output_funcs, buf);
385}
386
387static void R128I2CGetBits(I2CBusPtr b, int *Clock, int *data)
388{
389    ScrnInfoPtr   pScrn       = xf86Screens[b->scrnIndex];
390    R128InfoPtr   info        = R128PTR(pScrn);
391    unsigned long val;
392    unsigned char *R128MMIO   = info->MMIO;
393    R128I2CBusPtr pR128I2CBus = b->DriverPrivate.ptr;
394
395    /* Get the result. */
396    val = INREG(pR128I2CBus->ddc_reg);
397    *Clock = (val & pR128I2CBus->get_clk_mask)  != 0;
398    *data  = (val & pR128I2CBus->get_data_mask) != 0;
399}
400
401static void R128I2CPutBits(I2CBusPtr b, int Clock, int data)
402{
403    ScrnInfoPtr   pScrn       = xf86Screens[b->scrnIndex];
404    R128InfoPtr   info        = R128PTR(pScrn);
405    unsigned long val;
406    unsigned char *R128MMIO   = info->MMIO;
407    R128I2CBusPtr pR128I2CBus = b->DriverPrivate.ptr;
408
409    val = INREG(pR128I2CBus->ddc_reg)
410              & ~(uint32_t)(pR128I2CBus->put_clk_mask | pR128I2CBus->put_data_mask);
411    val |= (Clock ? 0 : pR128I2CBus->put_clk_mask);
412    val |= (data  ? 0 : pR128I2CBus->put_data_mask);
413    OUTREG(pR128I2CBus->ddc_reg, val);
414}
415
416static Bool R128I2CInit(xf86OutputPtr output, I2CBusPtr *bus_ptr, char *name)
417{
418    ScrnInfoPtr pScrn = output->scrn;
419    R128OutputPrivatePtr r128_output = output->driver_private;
420    R128I2CBusPtr pR128I2CBus = &(r128_output->ddc_i2c);
421    I2CBusPtr pI2CBus;
422
423    pI2CBus = xf86CreateI2CBusRec();
424    if(!pI2CBus) return FALSE;
425
426    pI2CBus->BusName     = name;
427    pI2CBus->scrnIndex   = pScrn->scrnIndex;
428    pI2CBus->I2CPutBits  = R128I2CPutBits;
429    pI2CBus->I2CGetBits  = R128I2CGetBits;
430    pI2CBus->AcknTimeout = 5;
431
432    pI2CBus->DriverPrivate.ptr = (pointer)pR128I2CBus;
433    if (!xf86I2CBusInit(pI2CBus)) return FALSE;
434
435    *bus_ptr = pI2CBus;
436    return TRUE;
437}
438
439void R128SetupGenericConnectors(ScrnInfoPtr pScrn, R128OutputType *otypes)
440{
441    R128InfoPtr info    = R128PTR(pScrn);
442    R128EntPtr pR128Ent = R128EntPriv(pScrn);
443
444    if (!pR128Ent->HasCRTC2 && !info->isDFP) {
445        otypes[0] = OUTPUT_VGA;
446        otypes[1] = OUTPUT_NONE;
447        return;
448    } else if (!pR128Ent->HasCRTC2) {
449        otypes[0] = OUTPUT_DVI;
450        otypes[1] = OUTPUT_NONE;
451	return;
452    }
453
454    otypes[0] = OUTPUT_LVDS;
455    otypes[1] = OUTPUT_VGA;
456}
457
458void R128GetConnectorInfoFromBIOS(ScrnInfoPtr pScrn, R128OutputType *otypes)
459{
460    R128InfoPtr info = R128PTR(pScrn);
461    uint16_t bios_header;
462    int offset;
463
464    /* XXX: Currently, this function only finds VGA ports misidentified as DVI. */
465    if (!info->VBIOS || otypes[0] != OUTPUT_DVI) return;
466
467    bios_header = R128_BIOS16(0x48);
468    offset = R128_BIOS16(bios_header + 0x60);
469
470    if (offset) {
471        otypes[0] = OUTPUT_VGA;
472        xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Found CRT table, assuming VGA connector\n");
473    }
474}
475
476Bool R128SetupConnectors(ScrnInfoPtr pScrn)
477{
478    R128InfoPtr info    = R128PTR(pScrn);
479    R128EntPtr pR128Ent = R128EntPriv(pScrn);
480
481    R128OutputType otypes[R128_MAX_BIOS_CONNECTOR];
482    xf86OutputPtr  output;
483    int num_vga = 0;
484    int num_dvi = 0;
485    int i;
486
487    R128SetupGenericConnectors(pScrn, otypes);
488    R128GetConnectorInfoFromBIOS(pScrn, otypes);
489
490    for (i = 0; i < R128_MAX_BIOS_CONNECTOR; i++) {
491        if (otypes[i] == OUTPUT_VGA)
492            num_vga++;
493        else if (otypes[i] == OUTPUT_DVI)
494            num_dvi++;
495    }
496
497    for (i = 0; i < R128_MAX_BIOS_CONNECTOR; i++) {
498        if (otypes[i] == OUTPUT_NONE) continue;
499
500	R128I2CBusRec i2c;
501        R128OutputPrivatePtr r128_output;
502
503        r128_output = xnfcalloc(sizeof(R128OutputPrivateRec), 1);
504        if (!r128_output) return FALSE;
505
506        r128_output->MonType = MT_UNKNOWN;
507        r128_output->type = otypes[i];
508        r128_output->num = i;
509
510        if (otypes[i] == OUTPUT_LVDS) {
511            output = R128OutputCreate(pScrn, "LVDS", 0);
512        } else if (otypes[i] == OUTPUT_VGA) {
513            output = R128OutputCreate(pScrn, "VGA-%d", --num_vga);
514        } else {
515            output = R128OutputCreate(pScrn, "DVI-%d", --num_dvi);
516        }
517
518        if (!output) return FALSE;
519        output->interlaceAllowed = TRUE;
520        output->doubleScanAllowed = TRUE;
521        output->driver_private = r128_output;
522	output->possible_clones = 0;
523	if (otypes[i] == OUTPUT_LVDS || !pR128Ent->HasCRTC2)
524	    output->possible_crtcs = 1;
525	else
526	    output->possible_crtcs = 2;
527
528        if (otypes[i] != OUTPUT_LVDS && info->DDC) {
529            i2c.ddc_reg      = R128_GPIO_MONID;
530            if (otypes[i] == OUTPUT_VGA && info->isPro2) {
531                i2c.put_clk_mask = R128_GPIO_MONID_EN_2;
532                i2c.get_clk_mask = R128_GPIO_MONID_Y_2;
533            } else {
534                i2c.put_clk_mask = R128_GPIO_MONID_EN_3;
535                i2c.get_clk_mask = R128_GPIO_MONID_Y_3;
536            }
537            if (otypes[i] == OUTPUT_VGA) {
538                i2c.put_data_mask = R128_GPIO_MONID_EN_1;
539                i2c.get_data_mask = R128_GPIO_MONID_Y_1;
540            } else {
541                i2c.put_data_mask = R128_GPIO_MONID_EN_0;
542                i2c.get_data_mask = R128_GPIO_MONID_Y_0;
543            }
544            r128_output->ddc_i2c = i2c;
545            R128I2CInit(output, &r128_output->pI2CBus, output->name);
546        }
547
548        if (otypes[i] == OUTPUT_LVDS)
549            R128GetPanelInfoFromBIOS(output);
550    }
551
552    return TRUE;
553}
554