lx_display.c revision 00be8644
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        xf86_reload_cursors(crtc->scrn->pScreen);
330        crtc->funcs->hide_cursor(crtc);
331        crtc->cursor_shown = FALSE;
332    }
333
334    /* Renable the video */
335
336    if (lx_crtc->video_enable)
337        df_set_video_enable(lx_crtc->video_enable, lx_crtc->video_flags);
338
339    lx_crtc->video_enable = 0;
340    lx_crtc->video_flags = 0;
341}
342
343static void
344lx_crtc_gamma_set(xf86CrtcPtr crtc, CARD16 *red, CARD16 *green,
345                  CARD16 *blue, int size)
346{
347    unsigned int dcfg;
348    int i;
349
350    assert(size == 256);
351
352    /* We need the Gamma Correction for video - fading operation,
353     * the values address should be plused for every cycle.
354     * Special for Screensaver Operation.
355     */
356
357    for (i = 0; i < 256; i++) {
358        unsigned int val;
359
360        (*red) &= 0xff00;
361        (*green) &= 0xff00;
362        (*blue) &= 0xff00;
363        val = (*(red++) << 8) | *(green++) | (*(blue++) >> 8);
364
365        df_set_video_palette_entry(i, val);
366    }
367
368    /* df_set_video_palette_entry automatically turns on
369     * gamma for video - if this gets called, we assume that
370     * RandR wants it set for graphics, so reverse cimarron
371     */
372
373    dcfg = READ_VID32(DF_DISPLAY_CONFIG);
374    dcfg &= ~DF_DCFG_GV_PAL_BYP;
375    WRITE_VID32(DF_DISPLAY_CONFIG, dcfg);
376}
377
378    /* The Xserver has a scratch pixmap allocation routine that will
379     * try to use the existing scratch pixmap if possible. When the driver
380     * or any other user stop using it, it need to clear out any pixmap
381     * state (private data etc) otherwise the next user may get stale data.
382     */
383
384    /* Use our own wrapper to allocate a pixmap for wrapping a buffer object
385     * It removes using scratch pixmaps for rotate.
386     */
387static PixmapPtr
388lx_create_bo_pixmap(ScreenPtr pScreen,
389                    int width, int height,
390                    int depth, int bpp, int pitch, pointer pPixData)
391{
392    PixmapPtr pixmap;
393
394#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,5,0,0,0)
395    pixmap = (*pScreen->CreatePixmap) (pScreen, 0, 0, depth, 0);
396#else
397    pixmap = (*pScreen->CreatePixmap) (pScreen, 0, 0, depth);
398#endif
399
400    if (!pixmap)
401        return NULL;
402    if (!(*pScreen->ModifyPixmapHeader) (pixmap, width, height,
403                                         depth, bpp, pitch, pPixData)) {
404        /* ModifyPixmapHeader failed, so we can't use it as scratch pixmap
405         */
406        (*pScreen->DestroyPixmap) (pixmap);
407        return NULL;
408    }
409
410    return pixmap;
411}
412
413static void
414lx_destory_bo_pixmap(PixmapPtr pixmap)
415{
416    ScreenPtr pScreen = pixmap->drawable.pScreen;
417
418    (*pScreen->DestroyPixmap) (pixmap);
419}
420
421    /* Allocates shadow memory, and allocating a new space for Rotation.
422     * The size is measured in bytes, and the offset from the beginning
423     * of card space is returned.
424     */
425
426static Bool
427LXAllocShadow(ScrnInfoPtr pScrni, int size)
428{
429    GeodeRec *pGeode = GEODEPTR(pScrni);
430
431    if (pGeode->shadowArea) {
432        if (pGeode->shadowArea->size != size) {
433            exaOffscreenFree(pScrni->pScreen, pGeode->shadowArea);
434            pGeode->shadowArea = NULL;
435        }
436    }
437
438    if (pGeode->shadowArea == NULL) {
439        pGeode->shadowArea =
440            exaOffscreenAlloc(pScrni->pScreen, size, 4, TRUE, NULL, NULL);
441
442        if (pGeode->shadowArea == NULL)
443            return FALSE;
444    }
445
446    pScrni->fbOffset = pGeode->shadowArea->offset;
447    return TRUE;
448}
449
450static void *
451lx_crtc_shadow_allocate(xf86CrtcPtr crtc, int width, int height)
452{
453    ScrnInfoPtr pScrni = crtc->scrn;
454    GeodePtr pGeode = GEODEPTR(pScrni);
455    unsigned int rpitch, size;
456
457    rpitch = pScrni->displayWidth * (pScrni->bitsPerPixel / 8);
458    size = rpitch * height;
459
460    /* Allocate shadow memory */
461    if (LXAllocShadow(pScrni, size) == FALSE) {
462        xf86DrvMsg(pScrni->scrnIndex, X_ERROR,
463                   "Couldn't allocate the shadow memory for rotation\n");
464        xf86DrvMsg(pScrni->scrnIndex, X_ERROR,
465                   " You need 0x%x bytes, but only 0x%x bytes are available\n",
466                   size, GeodeOffscreenFreeSize(pGeode));
467
468        return NULL;
469    }
470
471    memset(pGeode->FBBase + pGeode->shadowArea->offset, 0, size);
472    return pGeode->FBBase + pGeode->shadowArea->offset;
473}
474
475static PixmapPtr
476lx_crtc_shadow_create(xf86CrtcPtr crtc, void *data, int width, int height)
477{
478    ScrnInfoPtr pScrni = crtc->scrn;
479    PixmapPtr rpixmap;
480    unsigned int rpitch;
481
482    rpitch = pScrni->displayWidth * (pScrni->bitsPerPixel / 8);
483    if (!data)
484        data = lx_crtc_shadow_allocate(crtc, width, height);
485
486    rpixmap = lx_create_bo_pixmap(pScrni->pScreen,
487                                  width, height, pScrni->depth,
488                                  pScrni->bitsPerPixel, rpitch, data);
489
490    if (rpixmap == NULL) {
491        xf86DrvMsg(pScrni->scrnIndex, X_ERROR,
492                   "Couldn't allocate shadow pixmap for rotated CRTC\n");
493    }
494
495    return rpixmap;
496}
497
498static void
499lx_crtc_shadow_destroy(xf86CrtcPtr crtc, PixmapPtr rpixmap, void *data)
500{
501    ScrnInfoPtr pScrni = crtc->scrn;
502    GeodeRec *pGeode = GEODEPTR(pScrni);
503
504    if (rpixmap)
505        lx_destory_bo_pixmap(rpixmap);
506
507    /* Free shadow memory */
508    if (data) {
509        gp_wait_until_idle();
510        if (pGeode->shadowArea != NULL) {
511            exaOffscreenFree(pScrni->pScreen, pGeode->shadowArea);
512            pGeode->shadowArea = NULL;
513        }
514    }
515}
516
517static void
518lx_crtc_set_cursor_colors(xf86CrtcPtr crtc, int bg, int fg)
519{
520    vg_set_mono_cursor_colors(bg, fg);
521}
522
523static void
524lx_crtc_set_cursor_position(xf86CrtcPtr crtc, int x, int y)
525{
526    VG_PANNING_COORDINATES panning;
527
528    vg_set_cursor_position(x, y, &panning);
529}
530
531static void
532lx_crtc_show_cursor(xf86CrtcPtr crtc)
533{
534    vg_set_cursor_enable(1);
535}
536
537static void
538lx_crtc_hide_cursor(xf86CrtcPtr crtc)
539{
540    vg_set_cursor_enable(0);
541}
542
543static void
544lx_crtc_load_cursor_argb(xf86CrtcPtr crtc, CARD32 *image)
545{
546    LXLoadARGBCursorImage(crtc->scrn, (unsigned char *) image);
547}
548
549static const xf86CrtcFuncsRec lx_crtc_funcs = {
550    .dpms = lx_crtc_dpms,
551    .lock = lx_crtc_lock,
552    .unlock = lx_crtc_unlock,
553    .mode_fixup = lx_crtc_mode_fixup,
554    .prepare = lx_crtc_prepare,
555    .mode_set = lx_crtc_mode_set,
556    .commit = lx_crtc_commit,
557    .gamma_set = lx_crtc_gamma_set,
558    .shadow_create = lx_crtc_shadow_create,
559    .shadow_allocate = lx_crtc_shadow_allocate,
560    .shadow_destroy = lx_crtc_shadow_destroy,
561    .set_cursor_colors = lx_crtc_set_cursor_colors,
562    .set_cursor_position = lx_crtc_set_cursor_position,
563    .show_cursor = lx_crtc_show_cursor,
564    .hide_cursor = lx_crtc_hide_cursor,
565    .load_cursor_argb = lx_crtc_load_cursor_argb,
566};
567
568void
569LXSetupCrtc(ScrnInfoPtr pScrni)
570{
571    xf86CrtcPtr crtc;
572    LXCrtcPrivatePtr lxpriv;
573
574    crtc = xf86CrtcCreate(pScrni, &lx_crtc_funcs);
575
576    if (crtc == NULL) {
577        ErrorF("ERROR - failed to create a CRTC\n");
578        return;
579    }
580
581    lxpriv = xnfcalloc(1, sizeof(LXCrtcPrivateRec));
582
583    if (!lxpriv) {
584        xf86CrtcDestroy(crtc);
585        ErrorF("unable to allocate memory for lxpriv\n");
586        return;
587    }
588
589    crtc->driver_private = lxpriv;
590}
591