Logo Search packages:      
Sourcecode: xserver-xorg-video-ati version File versions

radeon_modes.c

/*
 * Copyright 2000 ATI Technologies Inc., Markham, Ontario, and
 *                VA Linux Systems Inc., Fremont, California.
 *
 * All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation on the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the
 * next paragraph) shall be included in all copies or substantial
 * portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NON-INFRINGEMENT.  IN NO EVENT SHALL ATI, VA LINUX SYSTEMS AND/OR
 * THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

/*
 * Authors:
 *   Kevin E. Martin <martin@xfree86.org>
 *   Rickard E. Faith <faith@valinux.com>
 *   Alan Hourihane <alanh@fairlite.demon.co.uk>
 */

#include <string.h>
#include <stdio.h>

#include "xf86.h"
                        /* Driver data structures */
#include "randrstr.h"
#include "radeon_probe.h"
#include "radeon.h"
#include "radeon_reg.h"
#include "radeon_macros.h"
#include "radeon_version.h"
#include "radeon_atombios.h"

#include "xf86Modes.h"
                        /* DDC support */
#include "xf86DDC.h"
#include <randrstr.h>

void RADEONSetPitch (ScrnInfoPtr pScrn)
{
    int  dummy = pScrn->virtualX;
    RADEONInfoPtr info = RADEONPTR(pScrn);
    int pitch_mask = 0;
    int align_large;

    align_large = info->allowColorTiling || IS_AVIVO_VARIANT;

    /* FIXME: May need to validate line pitch here */
    switch (pScrn->depth / 8) {
    case 1: pitch_mask = align_large ? 255 : 127;
      break;
    case 2: pitch_mask = align_large ? 127 : 31;
      break;
    case 3:
    case 4: pitch_mask = align_large ? 63 : 15;
      break;
    }
    dummy = (pScrn->virtualX + pitch_mask) & ~pitch_mask;
    pScrn->displayWidth = dummy;
    info->CurrentLayout.displayWidth = pScrn->displayWidth;

}

static DisplayModePtr
RADEONTVModes(xf86OutputPtr output)
{
    DisplayModePtr new  = NULL;

    /* just a place holder */
    new = xf86CVTMode(800, 600, 60.00, FALSE, FALSE);
    new->type = M_T_DRIVER | M_T_PREFERRED;

    return new;
}

static DisplayModePtr
RADEONATOMTVModes(xf86OutputPtr output)
{
    DisplayModePtr  last       = NULL;
    DisplayModePtr  new        = NULL;
    DisplayModePtr  first      = NULL;
    int i;
    /* Add some common sizes */
    int widths[5] =  {640, 720, 800, 848, 1024};
    int heights[5] = {480, 480, 600, 480,  768};

    for (i = 0; i < 5; i++) {
      new = xf86CVTMode(widths[i], heights[i], 60.0, FALSE, FALSE);

      new->type       = M_T_DRIVER;

      new->next       = NULL;
      new->prev       = last;

      if (last) last->next = new;
      last = new;
      if (!first) first = new;
    }

    if (last) {
      last->next   = NULL; //first;
      first->prev  = NULL; //last;
    }

    return first;
}

/* This is used only when no mode is specified for FP and no ddc is
 * available.  We force it to native mode, if possible.
 */
static DisplayModePtr RADEONFPNativeMode(xf86OutputPtr output)
{
    ScrnInfoPtr pScrn = output->scrn;
    RADEONOutputPrivatePtr radeon_output = output->driver_private;
    radeon_native_mode_ptr native_mode = &radeon_output->native_mode;
    DisplayModePtr  new   = NULL;
    char            stmp[32];

    if (native_mode->PanelXRes != 0 &&
      native_mode->PanelYRes != 0 &&
      native_mode->DotClock != 0) {

      new             = xnfcalloc(1, sizeof (DisplayModeRec));
      sprintf(stmp, "%dx%d", native_mode->PanelXRes, native_mode->PanelYRes);
      new->name       = xnfalloc(strlen(stmp) + 1);
      strcpy(new->name, stmp);
      new->HDisplay   = native_mode->PanelXRes;
      new->VDisplay   = native_mode->PanelYRes;

      new->HTotal     = new->HDisplay + native_mode->HBlank;
      new->HSyncStart = new->HDisplay + native_mode->HOverPlus;
      new->HSyncEnd   = new->HSyncStart + native_mode->HSyncWidth;
      new->VTotal     = new->VDisplay + native_mode->VBlank;
      new->VSyncStart = new->VDisplay + native_mode->VOverPlus;
      new->VSyncEnd   = new->VSyncStart + native_mode->VSyncWidth;

      new->Clock      = native_mode->DotClock;
      new->Flags      = 0;

      if (new) {
          new->type       = M_T_DRIVER | M_T_PREFERRED;

          new->next       = NULL;
          new->prev       = NULL;
      }

      xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Added native panel mode: %dx%d\n",
               native_mode->PanelXRes, native_mode->PanelYRes);
    }

    return new;
}

#if defined(__powerpc__)
/* Apple eMacs need special modes for the internal CRT, e.g.,
 * Modeline "640x480"    62.12   640  680  752  864  480 481 484  521 +HSync +Vsync
 * Modeline "800x600"    76.84   800  848  936 1072  600 601 604  640 +HSync +Vsync
 * Modeline "1024x768"   99.07  1024 1088 1200 1376  768 769 772  809 +HSync +Vsync
 * Modeline "1152x864"  112.36  1152 1224 1352 1552  864 865 868  905 +HSync +Vsync
 * Modeline "1280x960"  124.54  1280 1368 1504 1728  960 961 964 1001 +HSync +Vsync
 */
static DisplayModePtr RADEONeMacModes(xf86OutputPtr output)
{
    ScrnInfoPtr pScrn = output->scrn;
    DisplayModePtr last=NULL, new=NULL, first=NULL;
    int i, *modep;
    static const char *modenames[5] = {
      "640x480", "800x600", "1024x768", "1152x864", "1280x960"
    };
    static int modes[9*5] = {
       62120,  640,  680,  752,  864, 480, 481, 484,  521,
       76840,  800,  848,  936, 1072, 600, 601, 604,  640,
       99070, 1024, 1088, 1200, 1376, 768, 769, 772,  809,
      112360, 1152, 1224, 1352, 1552, 864, 865, 868,  905,
      124540, 1280, 1368, 1504, 1728, 960, 961, 964, 1001
    };
    modep = modes;

    for (i=0; i<5; i++) {
      new = xnfcalloc(1, sizeof (DisplayModeRec));
      if (new) {
          new->name       = xnfalloc(strlen(modenames[i]) + 1);
          strcpy(new->name, modenames[i]);
          new->Clock      = *modep++;

          new->HDisplay   = *modep++;
          new->HSyncStart = *modep++;
          new->HSyncEnd   = *modep++;
          new->HTotal     = *modep++;

          new->VDisplay   = *modep++;
          new->VSyncStart = *modep++;
          new->VSyncEnd   = *modep++;
          new->VTotal     = *modep++;

          new->Flags      = 0;
          new->type       = M_T_DRIVER;
          if (i==2)
            new->type |= M_T_PREFERRED;
          new->next       = NULL;
          new->prev       = last;
          if (last) last->next = new;
          last = new;
          if (!first) first = new;
          xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Added eMac mode %s\n", modenames[i]);
      }
    }

    return first;
}
#endif

/* this function is basically a hack to add the screen modes */
static void RADEONAddScreenModes(xf86OutputPtr output, DisplayModePtr *modeList)
{
    ScrnInfoPtr pScrn = output->scrn;
    RADEONOutputPrivatePtr radeon_output = output->driver_private;
    radeon_native_mode_ptr native_mode = &radeon_output->native_mode;
    DisplayModePtr  last       = NULL;
    DisplayModePtr  new        = NULL;
    DisplayModePtr  first      = NULL;
    int             count      = 0;
    int             i, width, height;
    char **ppModeName = pScrn->display->modes;

    first = last = *modeList;

    /* We have a flat panel connected to the primary display, and we
     * don't have any DDC info.
     */
    for (i = 0; ppModeName[i] != NULL; i++) {

      if (sscanf(ppModeName[i], "%dx%d", &width, &height) != 2) continue;

      if (radeon_output->active_device & (ATOM_DEVICE_LCD_SUPPORT)) {
          /* already added the native mode */
          if (width == native_mode->PanelXRes && height == native_mode->PanelYRes)
            continue;

          /* Note: We allow all non-standard modes as long as they do not
           * exceed the native resolution of the panel.  Since these modes
           * need the internal RMX unit in the video chips (and there is
           * only one per card), this will only apply to the primary head.
           */
          if (width < 320 || width > native_mode->PanelXRes ||
            height < 200 || height > native_mode->PanelYRes) {
            xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
                     "Mode %s is out of range.\n", ppModeName[i]);
            xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
                     "Valid FP modes must be between 320x200-%dx%d\n",
                     native_mode->PanelXRes, native_mode->PanelYRes);
            continue;
          }
      }

      new = xf86CVTMode(width, height, 60.0, FALSE, FALSE);

      new->type      |= M_T_USERDEF;

      new->next       = NULL;
      new->prev       = last;

      if (last) last->next = new;
      last = new;
      if (!first) first = new;

      count++;
      xf86DrvMsg(pScrn->scrnIndex, X_INFO,
               "Adding Screen mode: %s\n", new->name);
    }


    /* Close the doubly-linked mode list, if we found any usable modes */
    if (last) {
      last->next   = NULL; //first;
      first->prev  = NULL; //last;
      *modeList = first;
    }

    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
             "Total number of valid Screen mode(s) added: %d\n", count);

}

/* BIOS may not have right panel size, we search through all supported
 * DDC modes looking for the maximum panel size.
 */
static void
RADEONUpdatePanelSize(xf86OutputPtr output)
{
    ScrnInfoPtr pScrn = output->scrn;
    RADEONInfoPtr  info       = RADEONPTR(pScrn);
    RADEONOutputPrivatePtr radeon_output = output->driver_private;
    radeon_native_mode_ptr native_mode = &radeon_output->native_mode;
    int             j;
    xf86MonPtr ddc = output->MonInfo;
    DisplayModePtr  p;

    // update output's native mode
    if (radeon_output->active_device & (ATOM_DEVICE_LCD_SUPPORT)) {
      radeon_encoder_ptr radeon_encoder = radeon_get_encoder(output);
      if (radeon_encoder) {
          radeon_lvds_ptr lvds = (radeon_lvds_ptr)radeon_encoder->dev_priv;
          if (lvds)
            radeon_output->native_mode = lvds->native_mode;
      }
    }

    // crtc should handle?
    if ((info->UseBiosDividers && native_mode->DotClock != 0) || (ddc == NULL))
       return;

    /* Go thru detailed timing table first */
    for (j = 0; j < 4; j++) {
      if (ddc->det_mon[j].type == 0) {
          struct detailed_timings *d_timings =
            &ddc->det_mon[j].section.d_timings;
           int match = 0;

           /* If we didn't get a panel clock or guessed one, try to match the
            * mode with the panel size. We do that because we _need_ a panel
            * clock, or ValidateFPModes will fail, even when UseBiosDividers
            * is set.
            */
           if (native_mode->DotClock == 0 &&
               native_mode->PanelXRes == d_timings->h_active &&
               native_mode->PanelYRes == d_timings->v_active)
               match = 1;

           /* If we don't have a BIOS provided panel data with fixed dividers,
            * check for a larger panel size
            */
          if (native_mode->PanelXRes < d_timings->h_active &&
            native_mode->PanelYRes < d_timings->v_active &&
            !info->UseBiosDividers)
            match = 1;

             if (match) {
            native_mode->PanelXRes  = d_timings->h_active;
            native_mode->PanelYRes  = d_timings->v_active;
            native_mode->DotClock   = d_timings->clock / 1000;
            native_mode->HOverPlus  = d_timings->h_sync_off;
            native_mode->HSyncWidth = d_timings->h_sync_width;
            native_mode->HBlank     = d_timings->h_blanking;
            native_mode->VOverPlus  = d_timings->v_sync_off;
            native_mode->VSyncWidth = d_timings->v_sync_width;
            native_mode->VBlank     = d_timings->v_blanking;
                native_mode->Flags      = (d_timings->interlaced ? V_INTERLACE : 0);
                switch (d_timings->misc) {
                case 0: native_mode->Flags |= V_NHSYNC | V_NVSYNC; break;
                case 1: native_mode->Flags |= V_PHSYNC | V_NVSYNC; break;
                case 2: native_mode->Flags |= V_NHSYNC | V_PVSYNC; break;
                case 3: native_mode->Flags |= V_PHSYNC | V_PVSYNC; break;
                }
                xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Panel infos found from DDC detailed: %dx%d\n",
                           native_mode->PanelXRes, native_mode->PanelYRes);
          }
      }
    }

    if (info->UseBiosDividers && native_mode->DotClock != 0)
       return;

    /* Search thru standard VESA modes from EDID */
    for (j = 0; j < 8; j++) {
      if ((native_mode->PanelXRes < ddc->timings2[j].hsize) &&
          (native_mode->PanelYRes < ddc->timings2[j].vsize)) {
          for (p = pScrn->monitor->Modes; p; p = p->next) {
            if ((ddc->timings2[j].hsize == p->HDisplay) &&
                (ddc->timings2[j].vsize == p->VDisplay)) {
                float  refresh =
                  (float)p->Clock * 1000.0 / p->HTotal / p->VTotal;

                if (abs((float)ddc->timings2[j].refresh - refresh) < 1.0) {
                  /* Is this good enough? */
                  native_mode->PanelXRes  = ddc->timings2[j].hsize;
                  native_mode->PanelYRes  = ddc->timings2[j].vsize;
                  native_mode->HBlank     = p->HTotal - p->HDisplay;
                  native_mode->HOverPlus  = p->HSyncStart - p->HDisplay;
                  native_mode->HSyncWidth = p->HSyncEnd - p->HSyncStart;
                  native_mode->VBlank     = p->VTotal - p->VDisplay;
                  native_mode->VOverPlus  = p->VSyncStart - p->VDisplay;
                  native_mode->VSyncWidth = p->VSyncEnd - p->VSyncStart;
                  native_mode->DotClock   = p->Clock;
                        native_mode->Flags      = p->Flags;
                        xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Panel infos found from DDC VESA/EDID: %dx%d\n",
                                   native_mode->PanelXRes, native_mode->PanelYRes);
                }
            }
          }
      }
    }
}

DisplayModePtr
RADEONProbeOutputModes(xf86OutputPtr output)
{
    RADEONOutputPrivatePtr radeon_output = output->driver_private;
    ScrnInfoPtr pScrn = output->scrn;
    RADEONInfoPtr info = RADEONPTR(pScrn);
    DisplayModePtr          modes = NULL;
    AtomBiosArgRec atomBiosArg;
    AtomBiosResult atomBiosResult;

    if (output->status == XF86OutputStatusConnected) {
      if (radeon_output->active_device & (ATOM_DEVICE_TV_SUPPORT)) {
          if (IS_AVIVO_VARIANT)
            modes = RADEONATOMTVModes(output);
          else
            modes = RADEONTVModes(output);
      } else if (radeon_output->active_device & (ATOM_DEVICE_CV_SUPPORT)) {
          atomBiosResult = RHDAtomBiosFunc(pScrn->scrnIndex, info->atomBIOS,
                                   ATOMBIOS_GET_CV_MODES, &atomBiosArg);
          if (atomBiosResult == ATOM_SUCCESS) {
            modes = atomBiosArg.modes;
          }
      } else {
          if (radeon_output->active_device & (ATOM_DEVICE_DFP_SUPPORT | ATOM_DEVICE_LCD_SUPPORT))
            RADEONUpdatePanelSize(output);
          if (output->MonInfo)
            modes = xf86OutputGetEDIDModes (output);
#if defined(__powerpc__)
          if ((info->MacModel == RADEON_MAC_EMAC) &&
            (radeon_output->active_device & ATOM_DEVICE_CRT1_SUPPORT) &&
            (modes == NULL))
            modes = RADEONeMacModes(output);
#endif
          if (modes == NULL) {
            if ((radeon_output->active_device & (ATOM_DEVICE_LCD_SUPPORT)) && info->IsAtomBios) {
                atomBiosResult = RHDAtomBiosFunc(pScrn->scrnIndex,
                                         info->atomBIOS,
                                         ATOMBIOS_GET_PANEL_EDID, &atomBiosArg);
                if (atomBiosResult == ATOM_SUCCESS) {
                  output->MonInfo = xf86InterpretEDID(pScrn->scrnIndex,
                                              atomBiosArg.EDIDBlock);
                  modes = xf86OutputGetEDIDModes(output);
                }
            }
            if (modes == NULL) {
                if (radeon_output->active_device & (ATOM_DEVICE_LCD_SUPPORT))
                  modes = RADEONFPNativeMode(output);
                /* add the screen modes */
                RADEONAddScreenModes(output, &modes);
            }
          }
      }
    }

    return modes;
}


Generated by  Doxygen 1.6.0   Back to index