lx_display.c revision 861b9fee
1/* Copyright (c) 2008 Advanced Micro Devices, Inc.
2 *
3 * Permission is hereby granted, free of charge, to any person obtaining a copy
4 * of this software and associated documentation files (the "Software"), to
5 * deal in the Software without restriction, including without limitation the
6 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7 * sell copies of the Software, and to permit persons to whom the Software is
8 * furnished to do so, subject to the following conditions:
9 *
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19 * IN THE SOFTWARE.
20 *
21 * Neither the name of the Advanced Micro Devices, Inc. nor the names of its
22 * contributors may be used to endorse or promote products derived from this
23 * software without specific prior written permission.
24 */
25
26#ifdef HAVE_CONFIG_H
27#include "config.h"
28#endif
29
30#include "xorg-server.h"
31
32#include "xf86.h"
33#include "geode.h"
34#include "xf86Crtc.h"
35#include "cim/cim_defs.h"
36#include "cim/cim_regs.h"
37
38typedef struct _LXOutputPrivateRec {
39    int video_enable;
40    unsigned long video_flags;
41    GeodeMemPtr rotate_mem;
42} LXCrtcPrivateRec, *LXCrtcPrivatePtr;
43
44void
45lx_enable_dac_power(ScrnInfoPtr pScrni, int option)
46{
47    GeodeRec *pGeode = GEODEPTR(pScrni);
48
49    df_set_crt_enable(DF_CRT_ENABLE);
50
51    /* Turn off the DAC if we don't need the CRT */
52
53    if (option && (!(pGeode->Output & OUTPUT_CRT))) {
54        unsigned int misc = READ_VID32(DF_VID_MISC);
55
56        misc |= DF_DAC_POWER_DOWN;
57        WRITE_VID32(DF_VID_MISC, misc);
58    }
59
60    if (pGeode->Output & OUTPUT_PANEL)
61        df_set_panel_enable(1);
62}
63
64void
65lx_disable_dac_power(ScrnInfoPtr pScrni, int option)
66{
67    GeodeRec *pGeode = GEODEPTR(pScrni);
68
69    if (pGeode->Output & OUTPUT_PANEL)
70        df_set_panel_enable(0);
71
72    if (pGeode->Output & OUTPUT_CRT) {
73
74        /* Wait for the panel to finish its procedure */
75
76        if (pGeode->Output & OUTPUT_PANEL)
77            while ((READ_VID32(DF_POWER_MANAGEMENT) & 2) == 0);
78        df_set_crt_enable(option);
79    }
80}
81
82static void
83lx_set_panel_mode(VG_DISPLAY_MODE * mode, DisplayModePtr pMode)
84{
85    int hsync, vsync;
86
87    mode->mode_width = mode->panel_width = pMode->HDisplay;
88    mode->mode_height = mode->panel_height = pMode->VDisplay;
89
90    mode->hactive = pMode->HDisplay;
91    mode->hblankstart = pMode->HDisplay;
92    mode->hsyncstart = pMode->HSyncStart;
93    mode->hsyncend = pMode->HSyncEnd;
94    mode->hblankend = pMode->HTotal;
95    mode->htotal = pMode->HTotal;
96
97    mode->vactive = pMode->VDisplay;
98    mode->vblankstart = pMode->VDisplay;
99    mode->vsyncstart = pMode->VSyncStart;
100    mode->vsyncend = pMode->VSyncEnd;
101    mode->vblankend = pMode->VTotal;
102    mode->vtotal = pMode->VTotal;
103
104    mode->vactive_even = pMode->VDisplay;
105    mode->vblankstart_even = pMode->VDisplay;
106    mode->vsyncstart_even = pMode->VSyncStart;
107    mode->vsyncend_even = pMode->VSyncEnd;
108    mode->vblankend_even = pMode->VTotal;
109    mode->vtotal_even = pMode->VTotal;
110
111    mode->frequency = (int) ((pMode->Clock / 1000.0) * 0x10000);
112
113    /* In panel mode, Cimarron purposely swizzles these,
114     * so we swizzle them first  */
115
116    hsync = (pMode->Flags & V_NHSYNC) ? 0 : 1;
117    vsync = (pMode->Flags & V_NVSYNC) ? 0 : 1;
118
119    mode->flags |= (hsync) ? VG_MODEFLAG_NEG_HSYNC : 0;
120    mode->flags |= (vsync) ? VG_MODEFLAG_NEG_VSYNC : 0;
121}
122
123static void
124lx_set_crt_mode(VG_DISPLAY_MODE * mode, DisplayModePtr pMode)
125{
126    int hsync, vsync;
127
128    mode->mode_width = mode->panel_width = pMode->HDisplay;
129    mode->mode_height = mode->panel_height = pMode->VDisplay;
130
131    mode->hactive = pMode->CrtcHDisplay;
132    mode->hblankstart = pMode->CrtcHBlankStart;
133    mode->hsyncstart = pMode->CrtcHSyncStart;
134    mode->hsyncend = pMode->CrtcHSyncEnd;
135    mode->hblankend = pMode->CrtcHBlankEnd;
136    mode->htotal = pMode->CrtcHTotal;
137
138    mode->vactive = pMode->CrtcVDisplay;
139    mode->vblankstart = pMode->CrtcVBlankStart;
140    mode->vsyncstart = pMode->CrtcVSyncStart;
141    mode->vsyncend = pMode->CrtcVSyncEnd;
142    mode->vblankend = pMode->CrtcVBlankEnd;
143    mode->vtotal = pMode->CrtcVTotal;
144
145    mode->vactive_even = pMode->CrtcVDisplay;
146    mode->vblankstart_even = pMode->CrtcVBlankStart;
147    mode->vsyncstart_even = pMode->CrtcVSyncStart;
148    mode->vsyncend_even = pMode->CrtcVSyncEnd;
149    mode->vblankend_even = pMode->CrtcVBlankEnd;
150    mode->vtotal_even = pMode->CrtcVTotal;
151
152    mode->frequency = (int) ((pMode->Clock / 1000.0) * 0x10000);
153
154    hsync = (pMode->Flags & V_NHSYNC) ? 1 : 0;
155    vsync = (pMode->Flags & V_NVSYNC) ? 1 : 0;
156
157    mode->flags |= (hsync) ? VG_MODEFLAG_NEG_HSYNC : 0;
158    mode->flags |= (vsync) ? VG_MODEFLAG_NEG_VSYNC : 0;
159}
160
161static int
162lx_set_mode(ScrnInfoPtr pScrni, DisplayModePtr pMode, int bpp)
163{
164    GeodeRec *pGeode = GEODEPTR(pScrni);
165    VG_DISPLAY_MODE mode;
166    int ret;
167
168    memset(&mode, 0, sizeof(mode));
169
170    mode.flags |= pGeode->Output & OUTPUT_CRT ? VG_MODEFLAG_CRT_AND_FP : 0;
171
172    if (pGeode->Output & OUTPUT_PANEL) {
173        mode.flags |= VG_MODEFLAG_PANELOUT;
174        if (pGeode->Output & OUTPUT_CRT)
175            mode.flags |= VG_MODEFLAG_CRT_AND_FP;
176    }
177
178    if (pGeode->Output & OUTPUT_PANEL && pGeode->Scale)
179        lx_set_panel_mode(&mode, pGeode->panelMode);
180    else
181        lx_set_crt_mode(&mode, pMode);
182
183    mode.src_width = pMode->HDisplay;
184    mode.src_height = pMode->VDisplay;
185
186    /* Set the filter coefficients to the default values */
187    vg_set_scaler_filter_coefficients(NULL, NULL);
188
189    ret = vg_set_custom_mode(&mode, bpp);
190    return (ret == CIM_STATUS_OK) ? 0 : -1;
191}
192
193static void
194lx_crtc_dpms(xf86CrtcPtr crtc, int mode)
195{
196    ScrnInfoPtr pScrni = crtc->scrn;
197    GeodeRec *pGeode = GEODEPTR(pScrni);
198
199    if (pGeode->Output & OUTPUT_DCON)
200        DCONDPMSSet(pScrni, mode);
201
202    switch (mode) {
203    case DPMSModeOn:
204        lx_enable_dac_power(pScrni, 1);
205        break;
206
207    case DPMSModeStandby:
208        lx_disable_dac_power(pScrni, DF_CRT_STANDBY);
209        break;
210
211    case DPMSModeSuspend:
212        lx_disable_dac_power(pScrni, DF_CRT_SUSPEND);
213        break;
214
215    case DPMSModeOff:
216        lx_disable_dac_power(pScrni, DF_CRT_DISABLE);
217        break;
218    }
219}
220
221static Bool
222lx_crtc_lock(xf86CrtcPtr crtc)
223{
224    /* Wait until the GPU is idle */
225    gp_wait_until_idle();
226    return TRUE;
227}
228
229static void
230lx_crtc_unlock(xf86CrtcPtr crtc)
231{
232    /* Nothing to do here */
233}
234
235static void
236lx_crtc_prepare(xf86CrtcPtr crtc)
237{
238    LXCrtcPrivatePtr lx_crtc = crtc->driver_private;
239
240    /* Disable the video */
241    df_get_video_enable(&lx_crtc->video_enable, &lx_crtc->video_flags);
242
243    if (lx_crtc->video_enable)
244        df_set_video_enable(0, 0);
245
246    /* Turn off compression */
247    vg_set_compression_enable(0);
248
249    /* Hide the cursor */
250    crtc->funcs->hide_cursor(crtc);
251
252    /* Turn off the display */
253    crtc->funcs->dpms(crtc, DPMSModeOff);
254}
255
256static Bool
257lx_crtc_mode_fixup(xf86CrtcPtr crtc, DisplayModePtr mode,
258                   DisplayModePtr adjusted_mode)
259{
260    return TRUE;
261}
262
263static void
264lx_crtc_mode_set(xf86CrtcPtr crtc, DisplayModePtr mode,
265                 DisplayModePtr adjusted_mode, int x, int y)
266{
267    ScrnInfoPtr pScrni = crtc->scrn;
268    GeodeRec *pGeode = GEODEPTR(pScrni);
269    DF_VIDEO_SOURCE_PARAMS vs_odd, vs_even;
270    unsigned int rpitch;
271
272    df_get_video_source_configuration(&vs_odd, &vs_even);
273
274    /* Note - the memory gets adjusted when virtualX/virtualY
275     * gets changed - so we don't need to worry about it here
276     */
277
278    if (lx_set_mode(pScrni, adjusted_mode, pScrni->bitsPerPixel))
279        ErrorF("ERROR!  Unable to set the mode!\n");
280
281    /* The output gets turned in in the output code as
282     * per convention */
283
284    /* For rotation, any write to the frame buffer region marks
285     * the retire frame as dirty.
286     */
287    if (crtc->rotatedData != NULL) {
288        rpitch = pScrni->displayWidth * (pScrni->bitsPerPixel / 8);
289        vg_set_display_pitch(rpitch);
290    }
291    else
292        vg_set_display_pitch(pGeode->Pitch);
293    gp_set_bpp(pScrni->bitsPerPixel);
294
295    /* Set the acceleration offset if we are drawing to a shadow */
296    if (crtc->rotatedData != NULL)
297        vg_set_display_offset((unsigned int) ((char *) crtc->rotatedData -
298                                              (char *) pGeode->FBBase));
299    else
300        vg_set_display_offset(0);
301
302    /* FIXME: Whats up with X and Y?  Does that come into play
303     * here? */
304
305    df_configure_video_source(&vs_odd, &vs_even);
306
307    vg_wait_vertical_blank();
308}
309
310static void
311lx_crtc_commit(xf86CrtcPtr crtc)
312{
313    LXCrtcPrivatePtr lx_crtc = crtc->driver_private;
314    ScrnInfoPtr pScrni = crtc->scrn;
315    GeodeRec *pGeode = GEODEPTR(pScrni);
316
317    /* Turn back on the sreen */
318    crtc->funcs->dpms(crtc, DPMSModeOn);
319
320    /* Turn on compression */
321
322    if (pGeode->Compression) {
323        vg_configure_compression(&(pGeode->CBData));
324        vg_set_compression_enable(1);
325    }
326
327    /* Load the cursor */
328    if (crtc->scrn->pScreen != NULL) {
329#ifdef HAVE_XF86_CURSOR_RESET_CURSOR
330        xf86CursorResetCursor(crtc->scrn->pScreen);
331#else
332        xf86_reload_cursors(crtc->scrn->pScreen);
333#endif
334        crtc->funcs->hide_cursor(crtc);
335        crtc->cursor_shown = FALSE;
336    }
337
338    /* Renable the video */
339
340    if (lx_crtc->video_enable)
341        df_set_video_enable(lx_crtc->video_enable, lx_crtc->video_flags);
342
343    lx_crtc->video_enable = 0;
344    lx_crtc->video_flags = 0;
345}
346
347static void
348lx_crtc_gamma_set(xf86CrtcPtr crtc, CARD16 *red, CARD16 *green,
349                  CARD16 *blue, int size)
350{
351    unsigned int dcfg;
352    int i;
353
354    assert(size == 256);
355
356    /* We need the Gamma Correction for video - fading operation,
357     * the values address should be plused for every cycle.
358     * Special for Screensaver Operation.
359     */
360
361    for (i = 0; i < 256; i++) {
362        unsigned int val;
363
364        (*red) &= 0xff00;
365        (*green) &= 0xff00;
366        (*blue) &= 0xff00;
367        val = (*(red++) << 8) | *(green++) | (*(blue++) >> 8);
368
369        df_set_video_palette_entry(i, val);
370    }
371
372    /* df_set_video_palette_entry automatically turns on
373     * gamma for video - if this gets called, we assume that
374     * RandR wants it set for graphics, so reverse cimarron
375     */
376
377    dcfg = READ_VID32(DF_DISPLAY_CONFIG);
378    dcfg &= ~DF_DCFG_GV_PAL_BYP;
379    WRITE_VID32(DF_DISPLAY_CONFIG, dcfg);
380}
381
382    /* The Xserver has a scratch pixmap allocation routine that will
383     * try to use the existing scratch pixmap if possible. When the driver
384     * or any other user stop using it, it need to clear out any pixmap
385     * state (private data etc) otherwise the next user may get stale data.
386     */
387
388    /* Use our own wrapper to allocate a pixmap for wrapping a buffer object
389     * It removes using scratch pixmaps for rotate.
390     */
391static PixmapPtr
392lx_create_bo_pixmap(ScreenPtr pScreen,
393                    int width, int height,
394                    int depth, int bpp, int pitch, pointer pPixData)
395{
396    PixmapPtr pixmap;
397
398#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,5,0,0,0)
399    pixmap = (*pScreen->CreatePixmap) (pScreen, 0, 0, depth, 0);
400#else
401    pixmap = (*pScreen->CreatePixmap) (pScreen, 0, 0, depth);
402#endif
403
404    if (!pixmap)
405        return NULL;
406    if (!(*pScreen->ModifyPixmapHeader) (pixmap, width, height,
407                                         depth, bpp, pitch, pPixData)) {
408        /* ModifyPixmapHeader failed, so we can't use it as scratch pixmap
409         */
410        (*pScreen->DestroyPixmap) (pixmap);
411        return NULL;
412    }
413
414    return pixmap;
415}
416
417static void
418lx_destory_bo_pixmap(PixmapPtr pixmap)
419{
420    ScreenPtr pScreen = pixmap->drawable.pScreen;
421
422    (*pScreen->DestroyPixmap) (pixmap);
423}
424
425    /* Allocates shadow memory, and allocating a new space for Rotation.
426     * The size is measured in bytes, and the offset from the beginning
427     * of card space is returned.
428     */
429
430static Bool
431LXAllocShadow(ScrnInfoPtr pScrni, int size)
432{
433    GeodeRec *pGeode = GEODEPTR(pScrni);
434
435    if (pGeode->shadowArea) {
436        if (pGeode->shadowArea->size != size) {
437            exaOffscreenFree(pScrni->pScreen, pGeode->shadowArea);
438            pGeode->shadowArea = NULL;
439        }
440    }
441
442    if (pGeode->shadowArea == NULL) {
443        pGeode->shadowArea =
444            exaOffscreenAlloc(pScrni->pScreen, size, 4, TRUE, NULL, NULL);
445
446        if (pGeode->shadowArea == NULL)
447            return FALSE;
448    }
449
450    pScrni->fbOffset = pGeode->shadowArea->offset;
451    return TRUE;
452}
453
454static void *
455lx_crtc_shadow_allocate(xf86CrtcPtr crtc, int width, int height)
456{
457    ScrnInfoPtr pScrni = crtc->scrn;
458    GeodePtr pGeode = GEODEPTR(pScrni);
459    unsigned int rpitch, size;
460
461    rpitch = pScrni->displayWidth * (pScrni->bitsPerPixel / 8);
462    size = rpitch * height;
463
464    /* Allocate shadow memory */
465    if (LXAllocShadow(pScrni, size) == FALSE) {
466        xf86DrvMsg(pScrni->scrnIndex, X_ERROR,
467                   "Couldn't allocate the shadow memory for rotation\n");
468        xf86DrvMsg(pScrni->scrnIndex, X_ERROR,
469                   " You need 0x%x bytes, but only 0x%x bytes are available\n",
470                   size, GeodeOffscreenFreeSize(pGeode));
471
472        return NULL;
473    }
474
475    memset(pGeode->FBBase + pGeode->shadowArea->offset, 0, size);
476    return pGeode->FBBase + pGeode->shadowArea->offset;
477}
478
479static PixmapPtr
480lx_crtc_shadow_create(xf86CrtcPtr crtc, void *data, int width, int height)
481{
482    ScrnInfoPtr pScrni = crtc->scrn;
483    PixmapPtr rpixmap;
484    unsigned int rpitch;
485
486    rpitch = pScrni->displayWidth * (pScrni->bitsPerPixel / 8);
487    if (!data)
488        data = lx_crtc_shadow_allocate(crtc, width, height);
489
490    rpixmap = lx_create_bo_pixmap(pScrni->pScreen,
491                                  width, height, pScrni->depth,
492                                  pScrni->bitsPerPixel, rpitch, data);
493
494    if (rpixmap == NULL) {
495        xf86DrvMsg(pScrni->scrnIndex, X_ERROR,
496                   "Couldn't allocate shadow pixmap for rotated CRTC\n");
497    }
498
499    return rpixmap;
500}
501
502static void
503lx_crtc_shadow_destroy(xf86CrtcPtr crtc, PixmapPtr rpixmap, void *data)
504{
505    ScrnInfoPtr pScrni = crtc->scrn;
506    GeodeRec *pGeode = GEODEPTR(pScrni);
507
508    if (rpixmap)
509        lx_destory_bo_pixmap(rpixmap);
510
511    /* Free shadow memory */
512    if (data) {
513        gp_wait_until_idle();
514        if (pGeode->shadowArea != NULL) {
515            exaOffscreenFree(pScrni->pScreen, pGeode->shadowArea);
516            pGeode->shadowArea = NULL;
517        }
518    }
519}
520
521static void
522lx_crtc_set_cursor_colors(xf86CrtcPtr crtc, int bg, int fg)
523{
524    vg_set_mono_cursor_colors(bg, fg);
525}
526
527static void
528lx_crtc_set_cursor_position(xf86CrtcPtr crtc, int x, int y)
529{
530    VG_PANNING_COORDINATES panning;
531
532    vg_set_cursor_position(x, y, &panning);
533}
534
535static void
536lx_crtc_show_cursor(xf86CrtcPtr crtc)
537{
538    vg_set_cursor_enable(1);
539}
540
541static void
542lx_crtc_hide_cursor(xf86CrtcPtr crtc)
543{
544    vg_set_cursor_enable(0);
545}
546
547static void
548lx_crtc_load_cursor_argb(xf86CrtcPtr crtc, CARD32 *image)
549{
550    LXLoadARGBCursorImage(crtc->scrn, (unsigned char *) image);
551}
552
553static const xf86CrtcFuncsRec lx_crtc_funcs = {
554    .dpms = lx_crtc_dpms,
555    .lock = lx_crtc_lock,
556    .unlock = lx_crtc_unlock,
557    .mode_fixup = lx_crtc_mode_fixup,
558    .prepare = lx_crtc_prepare,
559    .mode_set = lx_crtc_mode_set,
560    .commit = lx_crtc_commit,
561    .gamma_set = lx_crtc_gamma_set,
562    .shadow_create = lx_crtc_shadow_create,
563    .shadow_allocate = lx_crtc_shadow_allocate,
564    .shadow_destroy = lx_crtc_shadow_destroy,
565    .set_cursor_colors = lx_crtc_set_cursor_colors,
566    .set_cursor_position = lx_crtc_set_cursor_position,
567    .show_cursor = lx_crtc_show_cursor,
568    .hide_cursor = lx_crtc_hide_cursor,
569    .load_cursor_argb = lx_crtc_load_cursor_argb,
570};
571
572void
573LXSetupCrtc(ScrnInfoPtr pScrni)
574{
575    xf86CrtcPtr crtc;
576    LXCrtcPrivatePtr lxpriv;
577
578    crtc = xf86CrtcCreate(pScrni, &lx_crtc_funcs);
579
580    if (crtc == NULL) {
581        ErrorF("ERROR - failed to create a CRTC\n");
582        return;
583    }
584
585    lxpriv = xnfcalloc(1, sizeof(LXCrtcPrivateRec));
586
587    if (!lxpriv) {
588        xf86CrtcDestroy(crtc);
589        ErrorF("unable to allocate memory for lxpriv\n");
590        return;
591    }
592
593    crtc->driver_private = lxpriv;
594}
595