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