/* * Copyright 2004 ATI Technologies Inc., Markham, Ontario * Copyright 2007-8 Advanced Micro Devices, Inc. * Copyright 2008 Red Hat Inc. * * 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 * 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 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 NONINFRINGEMENT. IN NO EVENT SHALL * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. * * Authors: Dave Airlie * Alex Deucher */ #include "drmP.h" #include "radeon_drm.h" #include "radeon_drv.h" /* old legacy ATI BIOS routines */ enum radeon_combios_ddc { DDC_NONE_DETECTED, DDC_MONID, DDC_DVI, DDC_VGA, DDC_CRT2, DDC_LCD, DDC_GPIO, }; enum radeon_combios_connector { CONNECTOR_NONE_LEGACY, CONNECTOR_PROPRIETARY_LEGACY, CONNECTOR_CRT_LEGACY, CONNECTOR_DVI_I_LEGACY, CONNECTOR_DVI_D_LEGACY, CONNECTOR_CTV_LEGACY, CONNECTOR_STV_LEGACY, CONNECTOR_UNSUPPORTED_LEGACY }; struct radeon_i2c_bus_rec combios_setup_i2c_bus(int ddc_line) { struct radeon_i2c_bus_rec i2c; i2c.mask_clk_mask = RADEON_GPIO_EN_1; i2c.mask_data_mask = RADEON_GPIO_EN_0; i2c.a_clk_mask = RADEON_GPIO_A_1; i2c.a_data_mask = RADEON_GPIO_A_0; i2c.put_clk_mask = RADEON_GPIO_EN_1; i2c.put_data_mask = RADEON_GPIO_EN_0; i2c.get_clk_mask = RADEON_GPIO_Y_1; i2c.get_data_mask = RADEON_GPIO_Y_0; if ((ddc_line == RADEON_LCD_GPIO_MASK) || (ddc_line == RADEON_MDGPIO_EN_REG)) { i2c.mask_clk_reg = ddc_line; i2c.mask_data_reg = ddc_line; i2c.a_clk_reg = ddc_line; i2c.a_data_reg = ddc_line; i2c.put_clk_reg = ddc_line; i2c.put_data_reg = ddc_line; i2c.get_clk_reg = ddc_line + 4; i2c.get_data_reg = ddc_line + 4; } else { i2c.mask_clk_reg = ddc_line; i2c.mask_data_reg = ddc_line; i2c.a_clk_reg = ddc_line; i2c.a_data_reg = ddc_line; i2c.put_clk_reg = ddc_line; i2c.put_data_reg = ddc_line; i2c.get_clk_reg = ddc_line; i2c.get_data_reg = ddc_line; } if (ddc_line) i2c.valid = true; else i2c.valid = false; return i2c; } bool radeon_combios_get_clock_info(struct drm_device *dev) { struct drm_radeon_private *dev_priv = dev->dev_private; struct radeon_mode_info *mode_info = &dev_priv->mode_info; uint16_t pll_info_block; struct radeon_pll *pll = &mode_info->pll; int rev; pll_info_block = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x30); rev = radeon_bios8(dev_priv, pll_info_block); pll->reference_freq = radeon_bios16(dev_priv, pll_info_block + 0xe); pll->reference_div = radeon_bios16(dev_priv, pll_info_block + 0x10); pll->pll_out_min = radeon_bios32(dev_priv, pll_info_block + 0x12); pll->pll_out_max = radeon_bios32(dev_priv, pll_info_block + 0x16); if (rev > 9) { pll->pll_in_min = radeon_bios32(dev_priv, pll_info_block + 0x36); pll->pll_in_max = radeon_bios32(dev_priv, pll_info_block + 0x3a); } else { pll->pll_in_min = 40; pll->pll_in_max = 500; } pll->xclk = radeon_bios16(dev_priv, pll_info_block + 0x08); // sclk/mclk use fixed point return true; } bool radeon_combios_get_lvds_info(struct radeon_encoder *encoder) { struct drm_device *dev = encoder->base.dev; struct drm_radeon_private *dev_priv = dev->dev_private; uint16_t tmp; char stmp[30]; int tmp0; int i; tmp = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x40); if (!tmp) { DRM_INFO("No panel info found in BIOS\n"); return false; } for (i = 0; i < 24; i++) stmp[i] = radeon_bios8(dev_priv, tmp + i + 1); stmp[24] = 0; DRM_INFO("Panel ID String: %s\n", stmp); encoder->panel_xres = radeon_bios16(dev_priv, tmp + 25); encoder->panel_yres = radeon_bios16(dev_priv, tmp + 27); DRM_INFO("Panel Size %dx%d\n", encoder->panel_xres, encoder->panel_yres); encoder->panel_pwr_delay = radeon_bios16(dev_priv, tmp + 44); if (encoder->panel_pwr_delay > 2000 || encoder->panel_pwr_delay < 0) encoder->panel_pwr_delay = 2000; for (i = 0; i < 32; i++) { tmp0 = radeon_bios16(dev_priv, tmp + 64 + i * 2); if (tmp0 == 0) break; if ((radeon_bios16(dev_priv, tmp0) == encoder->panel_xres) && (radeon_bios16(dev_priv, tmp0 + 2) == encoder->panel_yres)) { encoder->hblank = (radeon_bios16(dev_priv, tmp0 + 17) - radeon_bios16(dev_priv, tmp0 + 19)) * 8; encoder->hoverplus = (radeon_bios16(dev_priv, tmp0 + 21) - radeon_bios16(dev_priv, tmp0 + 19) - 1) * 8; encoder->hsync_width = radeon_bios8(dev_priv, tmp0 + 23) * 8; encoder->vblank = (radeon_bios16(dev_priv, tmp0 + 24) - radeon_bios16(dev_priv, tmp0 + 26)); encoder->voverplus = ((radeon_bios16(dev_priv, tmp0 + 28) & 0x7fff) - radeon_bios16(dev_priv, tmp0 + 26)); encoder->vsync_width = ((radeon_bios16(dev_priv, tmp0 + 28) & 0xf800) >> 11); encoder->dotclock = radeon_bios16(dev_priv, tmp0 + 9) * 10; encoder->flags = 0; } } return true; } bool radeon_combios_get_tmds_info(struct radeon_encoder *encoder) { struct drm_device *dev = encoder->base.dev; struct drm_radeon_private *dev_priv = dev->dev_private; uint16_t tmp; int i, n; uint8_t ver; tmp = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x34); if (!tmp) { DRM_INFO("No TMDS info found in BIOS\n"); return false; } ver = radeon_bios8(dev_priv, tmp); DRM_INFO("DFP table revision: %d\n", ver); if (ver == 3) { n = radeon_bios8(dev_priv, tmp + 5) + 1; if (n > 4) n = 4; for (i = 0; i < n; i++) { encoder->tmds_pll[i].value = radeon_bios32(dev_priv, tmp+i*10+0x08); encoder->tmds_pll[i].freq = radeon_bios16(dev_priv, tmp+i*10+0x10); } return true; } else if (ver == 4) { int stride = 0; n = radeon_bios8(dev_priv, tmp + 5) + 1; if (n > 4) n = 4; for (i = 0; i < n; i++) { encoder->tmds_pll[i].value = radeon_bios32(dev_priv, tmp+stride+0x08); encoder->tmds_pll[i].freq = radeon_bios16(dev_priv, tmp+stride+0x10); if (i == 0) stride += 10; else stride += 6; } return true; } return false; } static void radeon_apply_legacy_quirks(struct drm_device *dev, int bios_index) { struct drm_radeon_private *dev_priv = dev->dev_private; struct radeon_mode_info *mode_info = &dev_priv->mode_info; /* on XPRESS chips, CRT2_DDC and MONID_DCC both use the * MONID gpio, but use different pins. * CRT2_DDC uses the standard pinout, MONID_DDC uses * something else. */ if ((dev_priv->chip_family == CHIP_RS400 || dev_priv->chip_family == CHIP_RS480) && mode_info->bios_connector[bios_index].connector_type == CONNECTOR_VGA && mode_info->bios_connector[bios_index].ddc_i2c.mask_clk_reg == RADEON_GPIO_CRT2_DDC) { mode_info->bios_connector[bios_index].ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_MONID); } /* Certain IBM chipset RN50s have a BIOS reporting two VGAs, one with VGA DDC and one with CRT2 DDC. - kill the CRT2 DDC one */ if (dev->pdev->device == 0x515e && dev->pdev->subsystem_vendor == 0x1014) { if (mode_info->bios_connector[bios_index].connector_type == CONNECTOR_VGA && mode_info->bios_connector[bios_index].ddc_i2c.mask_clk_reg == RADEON_GPIO_CRT2_DDC) { mode_info->bios_connector[bios_index].valid = false; } } /* Some RV100 cards with 2 VGA ports show up with DVI+VGA */ if (dev->pdev->device == 0x5159 && dev->pdev->subsystem_vendor == 0x1002 && dev->pdev->subsystem_device == 0x013a) { if (mode_info->bios_connector[bios_index].connector_type == CONNECTOR_DVI_I) mode_info->bios_connector[bios_index].connector_type = CONNECTOR_VGA; } } bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev) { struct drm_radeon_private *dev_priv = dev->dev_private; struct radeon_mode_info *mode_info = &dev_priv->mode_info; uint32_t offset, entry; uint16_t tmp0, tmp1, tmp; enum radeon_combios_ddc ddctype; enum radeon_combios_connector connector_type; int i; DRM_DEBUG("\n"); offset = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x50); if (offset) { for (i = 0; i < 4; i++) { entry = offset + 2 + i * 2; if (!radeon_bios16(dev_priv, entry)) break; mode_info->bios_connector[i].valid = true; tmp = radeon_bios16(dev_priv, entry); connector_type = (tmp >> 12) & 0xf; mode_info->bios_connector[i].connector_type = connector_type; switch(connector_type) { case CONNECTOR_PROPRIETARY_LEGACY: mode_info->bios_connector[i].connector_type = CONNECTOR_DVI_D; break; case CONNECTOR_CRT_LEGACY: mode_info->bios_connector[i].connector_type = CONNECTOR_VGA; break; case CONNECTOR_DVI_I_LEGACY: mode_info->bios_connector[i].connector_type = CONNECTOR_DVI_I; break; case CONNECTOR_DVI_D_LEGACY: mode_info->bios_connector[i].connector_type = CONNECTOR_DVI_D; break; case CONNECTOR_CTV_LEGACY: mode_info->bios_connector[i].connector_type = CONNECTOR_CTV; break; case CONNECTOR_STV_LEGACY: mode_info->bios_connector[i].connector_type = CONNECTOR_STV; break; default: DRM_ERROR("Unknown connector type: %d\n", connector_type); mode_info->bios_connector[i].valid = false; break; } mode_info->bios_connector[i].ddc_i2c.valid = false; ddctype = (tmp >> 8) & 0xf; switch (ddctype) { case DDC_MONID: mode_info->bios_connector[i].ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_MONID); break; case DDC_DVI: mode_info->bios_connector[i].ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_DVI_DDC); break; case DDC_VGA: mode_info->bios_connector[i].ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC); break; case DDC_CRT2: mode_info->bios_connector[i].ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_CRT2_DDC); break; default: break; } if (tmp & 0x1) mode_info->bios_connector[i].dac_type = DAC_TVDAC; else mode_info->bios_connector[i].dac_type = DAC_PRIMARY; if ((dev_priv->chip_family == CHIP_RS300) || (dev_priv->chip_family == CHIP_RS400) || (dev_priv->chip_family == CHIP_RS480)) mode_info->bios_connector[i].dac_type = DAC_TVDAC; if ((tmp >> 4) & 0x1) mode_info->bios_connector[i].tmds_type = TMDS_EXT; else mode_info->bios_connector[i].tmds_type = TMDS_INT; radeon_apply_legacy_quirks(dev, i); } } else { DRM_INFO("no connector table found in BIOS\n"); offset = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x34); if (offset) { DRM_DEBUG("Found DFP table, assuming DVI connector\n"); mode_info->bios_connector[0].valid = true; mode_info->bios_connector[0].connector_type = CONNECTOR_DVI_I; mode_info->bios_connector[0].dac_type = DAC_PRIMARY; mode_info->bios_connector[0].tmds_type = TMDS_INT; mode_info->bios_connector[0].ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_DVI_DDC); } else { DRM_DEBUG("No table found\n"); return false; } } if (dev_priv->flags & RADEON_IS_MOBILITY) { offset = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x40); if (offset) { mode_info->bios_connector[4].valid = true; mode_info->bios_connector[4].connector_type = CONNECTOR_LVDS; mode_info->bios_connector[4].dac_type = DAC_NONE; mode_info->bios_connector[4].tmds_type = TMDS_NONE; mode_info->bios_connector[4].ddc_i2c.valid = false; tmp = radeon_bios16(dev_priv, dev_priv->bios_header_start + 0x42); if (tmp) { tmp0 = radeon_bios16(dev_priv, tmp + 0x15); if (tmp0) { tmp1 = radeon_bios8(dev_priv, tmp0 + 2) & 0x07; if (tmp1) { ddctype = tmp1; switch(ddctype) { case DDC_MONID: case DDC_DVI: case DDC_CRT2: case DDC_LCD: case DDC_GPIO: default: break; } DRM_DEBUG("LCD DDC Info Table found!\n"); } } } else mode_info->bios_connector[4].ddc_i2c.valid = false; } } DRM_DEBUG("BIOS Connector table\n"); for (i = 0; i < ATOM_MAX_SUPPORTED_DEVICE; i++) { if (!mode_info->bios_connector[i].valid) continue; DRM_DEBUG("Port %d: ddc_type 0x%x, dac_type %d, tmds_type %d, connector type %d, hpd_mask %d\n", i, mode_info->bios_connector[i].ddc_i2c.mask_clk_reg, mode_info->bios_connector[i].dac_type, mode_info->bios_connector[i].tmds_type, mode_info->bios_connector[i].connector_type, mode_info->bios_connector[i].hpd_mask); } return true; } href='#n296'>296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429
/**************************************************************************
*
* Copyright 2007 Tungsten Graphics, Inc., Cedar Park, TX., USA
* 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 the rights to use, copy, modify, merge, publish,
* distribute, sub license, 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 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
* THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS 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.
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial portions
* of the Software.
*
*
**************************************************************************/
/*
* Authors: Thomas Hellström <thomas-at-tungstengraphics-dot-com>
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <drm/drm.h>
#include "xf86dri.h"
#include "xf86drm.h"
#include "stdio.h"
#include "sys/types.h"
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include "sys/mman.h"
typedef struct
{
enum
{
haveNothing,
haveDisplay,
haveConnection,
haveDriverName,
haveDeviceInfo,
haveDRM,
haveContext
}
state;
Display *display;
int screen;
drm_handle_t sAreaOffset;
char *curBusID;
char *driverName;
int drmFD;
XVisualInfo visualInfo;
XID id;
drm_context_t hwContext;
void *driPriv;
int driPrivSize;
int fbSize;
int fbOrigin;
int fbStride;
drm_handle_t fbHandle;
int ddxDriverMajor;
int ddxDriverMinor;
int ddxDriverPatch;
} TinyDRIContext;
#ifndef __x86_64__
static unsigned
fastrdtsc(void)
{
unsigned eax;
__asm__ volatile ("\t"
"pushl %%ebx\n\t"
"cpuid\n\t" ".byte 0x0f, 0x31\n\t" "popl %%ebx\n":"=a" (eax)
:"0"(0)
:"ecx", "edx", "cc");
return eax;
}
#else
static unsigned
fastrdtsc(void)
{
unsigned eax;
__asm__ volatile ("\t" "cpuid\n\t" ".byte 0x0f, 0x31\n\t":"=a" (eax)
:"0"(0)
:"ecx", "edx", "ebx", "cc");
return eax;
}
#endif
void
bmError(int val, const char *file, const char *function, int line)
{
fprintf(stderr, "Fatal video memory manager error \"%s\".\n"
"Check kernel logs or set the LIBGL_DEBUG\n"
"environment variable to \"verbose\" for more info.\n"
"Detected in file %s, line %d, function %s.\n",
strerror(-val), file, line, function);
abort();
}
#define BM_CKFATAL(val) \
do{ \
int tstVal = (val); \
if (tstVal) \
bmError(tstVal, __FILE__, __FUNCTION__, __LINE__); \
} while(0);
static unsigned
time_diff(unsigned t, unsigned t2)
{
return ((t < t2) ? t2 - t : 0xFFFFFFFFU - (t - t2 - 1));
}
static int
releaseContext(TinyDRIContext * ctx)
{
switch (ctx->state) {
case haveContext:
uniDRIDestroyContext(ctx->display, ctx->screen, ctx->id);
case haveDRM:
drmClose(ctx->drmFD);
case haveDeviceInfo:
XFree(ctx->driPriv);
case haveDriverName:
XFree(ctx->driverName);
case haveConnection:
XFree(ctx->curBusID);
uniDRICloseConnection(ctx->display, ctx->screen);
case haveDisplay:
XCloseDisplay(ctx->display);
default:
break;
}
return -1;
}
static void
readBuf(void *buf, unsigned long size)
{
volatile unsigned *buf32 = (unsigned *)buf;
unsigned *end = (unsigned *)buf32 + size / sizeof(*buf32);
while (buf32 < end) {
(void)*buf32++;
}
}
static int
benchmarkBuffer(TinyDRIContext * ctx, unsigned long size,
unsigned long *ticks)
{
unsigned long curTime, oldTime;
int ret;
drmBO buf;
void *virtual;
/*
* Test system memory objects.
*/
oldTime = fastrdtsc();