1
0
mirror of https://github.com/libretro/RetroArch synced 2024-07-08 20:25:47 +00:00

Merge pull request #12403 from alphanu1/master

New Switchres (CRT Resolution switching) For Retroarch
This commit is contained in:
Autechre 2021-06-05 00:27:00 +02:00 committed by GitHub
commit 2259e9b38e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
61 changed files with 10705 additions and 242 deletions

View File

@ -304,7 +304,6 @@ OBJ += \
input/common/input_hid_common.o \
led/led_driver.o \
gfx/video_coord_array.o \
gfx/video_crt_switch.o \
gfx/gfx_display.o \
gfx/gfx_animation.o \
gfx/gfx_thumbnail_path.o \
@ -2274,6 +2273,39 @@ ifeq ($(HAVE_FFMPEG), 1)
INCLUDE_DIRS += -Iffmpeg
endif
# CRT mode switching
ifeq ($(HAVE_SR2), 1)
INCLUDE_DIRS += -I$(DEPS_DIR)/switchres
OBJ += gfx/video_crt_switch.o \
$(DEPS_DIR)/switchres/monitor.o \
$(DEPS_DIR)/switchres/modeline.o \
$(DEPS_DIR)/switchres/switchres.o \
$(DEPS_DIR)/switchres/display.o \
$(DEPS_DIR)/switchres/custom_video.o \
$(DEPS_DIR)/switchres/log.o \
$(DEPS_DIR)/switchres/switchres_wrapper.o \
$(DEPS_DIR)/switchres/edid.o
ifneq ($(findstring Win32,$(OS)),)
OBJ += $(DEPS_DIR)/switchres/display_windows.o \
$(DEPS_DIR)/switchres/custom_video_ati_family.o \
$(DEPS_DIR)/switchres/custom_video_ati.o \
$(DEPS_DIR)/switchres/custom_video_adl.o \
$(DEPS_DIR)/switchres/custom_video_pstrip.o \
$(DEPS_DIR)/switchres/resync_windows.o
endif
ifneq ($(findstring Linux,$(OS)),)
OBJ += $(DEPS_DIR)/switchres/display_linux.o
ifeq ($(HAVE_X11)$(HAVE_XRANDR), 11)
OBJ += $(DEPS_DIR)/switchres/custom_video_xrandr.o
DEFINES += -DSR_WITH_XRANDR
endif
endif
ifneq ($(findstring Win32,$(OS)),)
DEFINES += -DSR_WIN32_STATIC
endif
LIBS += -lstdc++
endif
ifeq ($(HAVE_COMPRESSION), 1)
DEFINES += -DHAVE_COMPRESSION
OBJ += tasks/task_decompress.o

View File

@ -157,6 +157,8 @@
#define DEFAULT_CRT_SWITCH_PORCH_ADJUST 0
#define DEFAULT_CRT_SWITCH_HIRES_MENU true
#define DEFAULT_HISTORY_LIST_ENABLE true
#define DEFAULT_PLAYLIST_ENTRY_RENAME true

View File

@ -1460,6 +1460,7 @@ static struct config_bool_setting *populate_settings_bool(
SETTING_BOOL("frame_time_counter_reset_after_load_state", &settings->bools.frame_time_counter_reset_after_load_state, true, false, false);
SETTING_BOOL("frame_time_counter_reset_after_save_state", &settings->bools.frame_time_counter_reset_after_save_state, true, false, false);
SETTING_BOOL("crt_switch_resolution_use_custom_refresh_rate", &settings->bools.crt_switch_custom_refresh_enable, true, false, false);
SETTING_BOOL("crt_switch_hires_menu", &settings->bools.crt_switch_hires_menu, true, false, true);
SETTING_BOOL("ui_companion_start_on_boot", &settings->bools.ui_companion_start_on_boot, true, ui_companion_start_on_boot, false);
SETTING_BOOL("ui_companion_enable", &settings->bools.ui_companion_enable, true, ui_companion_enable, false);
SETTING_BOOL("ui_companion_toggle", &settings->bools.ui_companion_toggle, false, ui_companion_toggle, false);

View File

@ -67,7 +67,9 @@ enum crt_switch_type
{
CRT_SWITCH_NONE = 0,
CRT_SWITCH_15KHZ,
CRT_SWITCH_31KHZ
CRT_SWITCH_31KHZ,
CRT_SWITCH_32_120,
CRT_SWITCH_INI
};
enum override_type
@ -697,6 +699,7 @@ typedef struct settings
bool kiosk_mode_enable;
bool crt_switch_custom_refresh_enable;
bool crt_switch_hires_menu;
/* Netplay */
bool netplay_public_announce;

59
deps/switchres/.gitlab-ci.yml vendored Normal file
View File

@ -0,0 +1,59 @@
# This file is a template, and might need editing before it works on your project.
# use the official gcc image, based on debian
# can use verions as well, like gcc:5.2
# see https://hub.docker.com/_/gcc/
image: gcc:latest
before_script:
- apt update
- apt -y install make
.pre_requisites_linux: &prerequisiteslinux
before_script:
- apt update
- apt -y install make
.pre_requisites_win32: &prerequisiteswin32
image: "ubuntu:rolling"
before_script:
- apt update
- apt -y install make mingw-w64
linux:x86_64:standalone:
stage: build
<<: *prerequisiteslinux
script:
- make
linux:x86_64:lib:
stage: build
<<: *prerequisiteslinux
script:
- make libswitchres
win32:x86_64:standalone:
stage: build
<<: *prerequisiteswin32
script:
- make PLATFORM=NT CROSS_COMPILE=x86_64-w64-mingw32-
win32:x86_64:lib:
stage: build
<<: *prerequisiteswin32
script:
- make PLATFORM=NT CROSS_COMPILE=x86_64-w64-mingw32- libswitchres
win32:i686:standalone:
stage: build
<<: *prerequisiteswin32
script:
- make PLATFORM=NT CROSS_COMPILE=i686-w64-mingw32-
win32:i686:lib:
stage: build
<<: *prerequisiteswin32
script:
- make PLATFORM=NT CROSS_COMPILE=i686-w64-mingw32- libswitchres

60
deps/switchres/README.md vendored Normal file
View File

@ -0,0 +1,60 @@
# What is Switchres 2.0
Switchres is a modeline generation engine for emulation.
Its purpose is on-the-fly creation of fully customized video modes that accurately reproduce those of the emulated systems. Based on a monitor profile, it will provide the best video mode for a given width, height, and refresh rate.
Switchres features the most versatile modeline generation ever, ranging from 15-kHz low resolutions up to modern 8K, with full geometry control, smart scaling, refresh scaling, mode rotation, aspect ratio correction and much more.
Switchres can be integrated into open-source emulators either as a library, or used as a standalone emulator launcher. It's written in C++ and a C wrapper is also available.
Switchres 2.0 is a rewrite of the original Switchres code used in GroovyMAME. It currently supports mode switching on the following platforms, with their respective backends:
- **Windows**:
- AMD ADL (AMD Radeon HD 5000+)
- ATI legacy (ATI Radeon pre-HD 5000)
- PowerStrip (ATI, Nvidia, Matrox, etc., models up to 2012)
- **Linux**:
- X11/Xorg
- KMS/DRM (WIP)
Each platform supports a different feature set, being X11/Xorg the most performant currently. In general, AMD video cards offer the best compatibility, and are a real requirement for the Windows platform.
# Using Switchres as a library
If you are an emulator writer, you can integrate Switchres into your emulator in two ways:
- **Switchres shared library** (.dll or .so). This method offers a simplified way to add advanced mode switching features to your emulator, with minimal knowledge of Switchres internals.
- **Full Switchres integration**. If your emulator is written in C++, you can gain full access to Switchres' gears by including a Switchres manager class into your project, à la GroovyMAME.
Ask our devs for help and advice.
# Using Switchres standalone
The standalone binary supports the following options:
```
Usage: switchres <width> <height> <refresh> [options]
Options:
-c, --calc Calculate video mode and exit
-s, --switch Switch to video mode
-l, --launch <command> Launch <command>
-m, --monitor <preset> Monitor preset (generic_15, arcade_15, pal, ntsc, etc.)
-a --aspect <num:den> Monitor aspect ratio
-r --rotated Original mode's native orientation is rotated
-d, --display <OS_display_name> Use target display (Windows: \\\\.\\DISPLAY1, ... Linux: VGA-0, ...)
-f, --force <w>x<h>@<r> Force a specific video mode from display mode list
-i, --ini <file.ini> Specify an ini file
-b, --backend <api_name> Specify the api name
-k, --keep Keep changes on exit (warning: this disables cleanup)
```
A default `switchres.ini` file will be searched in the current working directory, then in `.\ini` on Windows, `./ini` then `/etc` on Linux. The repo has a switchres.ini example.
## Examples
`switchres 320 240 60 --calc` will calculate and show a modeline for 320x240@60, computed using the current monitor preset in `switchres.ini`.
`switchres 320 240 60 -m ntsc -s` will switch your primary screen to 320x240 at 60Hz using the ntsc monitor model. Then it will wait until you press enter, and restore your initial screen resolution on exit.
`switchres 384 256 55.017605 -m arcade_15 -s -d \\.\DISPLAY1 -l "mame rtype"` will switch your display #1 to 384x256@55.017605 using the arcade_15 preset, then launch ``mame rtype``. Once mame has exited, it will restore the original resolution.
`switchres 640 480 57 -d 0 -m arcade_15 -d 1 -m arcade_31 -s` will set 640x480@57i (15-kHz preset) on your first display (index #0), 640x480@57p (31-kHz preset) on your second display (index #1)
# License
GNU General Public License, version 2 or later (GPL-2.0+).

178
deps/switchres/custom_video.cpp vendored Normal file
View File

@ -0,0 +1,178 @@
/**************************************************************
custom_video.cpp - Custom video library
---------------------------------------------------------
Switchres Modeline generation engine for emulation
License GPL-2.0+
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
Alexandre Wodarczyk, Gil Delescluse
**************************************************************/
#include <stdio.h>
#include "custom_video.h"
#include "log.h"
#if defined(_WIN32)
#include "custom_video_ati.h"
#include "custom_video_adl.h"
#include "custom_video_pstrip.h"
#elif defined(__linux__)
#ifdef SR_WITH_XRANDR
#include "custom_video_xrandr.h"
#endif
#ifdef SR_WITH_KMSDRM
#include "custom_video_drmkms.h"
#endif
#endif
extern bool ati_is_legacy(int vendor, int device);
//============================================================
// custom_video::make
//============================================================
custom_video *custom_video::make(char *device_name, char *device_id, int method, custom_video_settings *vs)
{
#if defined(_WIN32)
if (method == CUSTOM_VIDEO_TIMING_POWERSTRIP)
{
m_custom_video = new pstrip_timing(device_name, vs);
if (m_custom_video)
{
m_custom_method = CUSTOM_VIDEO_TIMING_POWERSTRIP;
return m_custom_video;
}
}
else
{
int vendor, device;
sscanf(device_id, "PCI\\VEN_%x&DEV_%x", &vendor, &device);
if (vendor == 0x1002) // ATI/AMD
{
if (ati_is_legacy(vendor, device))
{
m_custom_video = new ati_timing(device_name, vs);
if (m_custom_video)
{
m_custom_method = CUSTOM_VIDEO_TIMING_ATI_LEGACY;
return m_custom_video;
}
}
else
{
m_custom_video = new adl_timing(device_name, vs);
if (m_custom_video)
{
m_custom_method = CUSTOM_VIDEO_TIMING_ATI_ADL;
return m_custom_video;
}
}
}
else
log_info("Video chipset is not compatible.\n");
}
#elif defined(__linux__)
if (device_id != NULL)
log_info("Device value is %s.\n", device_id);
#ifdef SR_WITH_XRANDR
if (method == CUSTOM_VIDEO_TIMING_XRANDR || method == 0)
{
try
{
m_custom_video = new xrandr_timing(device_name, vs);
}
catch (...) {};
if (m_custom_video)
{
m_custom_method = CUSTOM_VIDEO_TIMING_XRANDR;
return m_custom_video;
}
}
#endif /* SR_WITH_XRANDR */
#ifdef SR_WITH_KMSDRM
if (method == CUSTOM_VIDEO_TIMING_DRMKMS || method == 0)
{
m_custom_video = new drmkms_timing(device_name, vs);
if (m_custom_video)
{
m_custom_method = CUSTOM_VIDEO_TIMING_DRMKMS;
return m_custom_video;
}
}
#endif /* SR_WITH_KMSDRM */
#endif
return this;
}
//============================================================
// custom_video::init
//============================================================
bool custom_video::init() { return false; }
//============================================================
// custom_video::get_timing
//============================================================
bool custom_video::get_timing(modeline *mode)
{
log_verbose("system mode\n");
mode->type |= CUSTOM_VIDEO_TIMING_SYSTEM;
return false;
}
//============================================================
// custom_video::set_timing
//============================================================
bool custom_video::set_timing(modeline *)
{
return false;
}
//============================================================
// custom_video::add_mode
//============================================================
bool custom_video::add_mode(modeline *)
{
return false;
}
//============================================================
// custom_video::delete_mode
//============================================================
bool custom_video::delete_mode(modeline *)
{
return false;
}
//============================================================
// custom_video::update_mode
//============================================================
bool custom_video::update_mode(modeline *)
{
return false;
}
//============================================================
// custom_video::process_modelist
//============================================================
bool custom_video::process_modelist(std::vector<modeline *>)
{
return false;
}

107
deps/switchres/custom_video.h vendored Normal file
View File

@ -0,0 +1,107 @@
/**************************************************************
custom_video.h - Custom video library header
---------------------------------------------------------
Switchres Modeline generation engine for emulation
License GPL-2.0+
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
Alexandre Wodarczyk, Gil Delescluse
**************************************************************/
#ifndef __CUSTOM_VIDEO__
#define __CUSTOM_VIDEO__
#include <vector>
#include <cstring>
#include "modeline.h"
#define CUSTOM_VIDEO_TIMING_MASK 0x00000ff0
#define CUSTOM_VIDEO_TIMING_AUTO 0x00000000
#define CUSTOM_VIDEO_TIMING_SYSTEM 0x00000010
#define CUSTOM_VIDEO_TIMING_XRANDR 0x00000020
#define CUSTOM_VIDEO_TIMING_POWERSTRIP 0x00000040
#define CUSTOM_VIDEO_TIMING_ATI_LEGACY 0x00000080
#define CUSTOM_VIDEO_TIMING_ATI_ADL 0x00000100
#define CUSTOM_VIDEO_TIMING_DRMKMS 0x00000200
// Custom video caps
#define CUSTOM_VIDEO_CAPS_UPDATE 0x001
#define CUSTOM_VIDEO_CAPS_ADD 0x002
#define CUSTOM_VIDEO_CAPS_DESKTOP_EDITABLE 0x004
#define CUSTOM_VIDEO_CAPS_SCAN_EDITABLE 0x008
// Timing creation commands
#define TIMING_DELETE 0x001
#define TIMING_CREATE 0x002
#define TIMING_UPDATE 0x004
#define TIMING_UPDATE_LIST 0x008
typedef struct custom_video_settings
{
bool screen_compositing;
bool screen_reordering;
bool allow_hardware_refresh;
char device_reg_key[128];
char custom_timing[256];
} custom_video_settings;
class custom_video
{
public:
custom_video() {};
virtual ~custom_video()
{
if (m_custom_video)
{
delete m_custom_video;
m_custom_video = nullptr;
}
}
custom_video *make(char *device_name, char *device_id, int method, custom_video_settings *vs);
virtual const char *api_name() { return "empty"; }
virtual bool init();
virtual int caps() { return 0; }
virtual bool add_mode(modeline *mode);
virtual bool delete_mode(modeline *mode);
virtual bool update_mode(modeline *mode);
virtual bool get_timing(modeline *mode);
virtual bool set_timing(modeline *mode);
virtual bool process_modelist(std::vector<modeline *>);
// getters
bool screen_compositing() { return m_vs.screen_compositing; }
bool screen_reordering() { return m_vs.screen_reordering; }
bool allow_hardware_refresh() { return m_vs.allow_hardware_refresh; }
const char *custom_timing() { return (const char*) &m_vs.custom_timing; }
// setters
void set_screen_compositing(bool value) { m_vs.screen_compositing = value; }
void set_screen_reordering(bool value) { m_vs.screen_reordering = value; }
void set_allow_hardware_refresh(bool value) { m_vs.allow_hardware_refresh = value; }
void set_custom_timing(const char *custom_timing) { strncpy(m_vs.custom_timing, custom_timing, sizeof(m_vs.custom_timing)-1); }
// options
custom_video_settings m_vs = {};
modeline m_user_mode = {};
modeline m_backup_mode = {};
private:
char m_device_name[32];
char m_device_key[128];
custom_video *m_custom_video = 0;
int m_custom_method;
};
#endif

532
deps/switchres/custom_video_adl.cpp vendored Normal file
View File

@ -0,0 +1,532 @@
/**************************************************************
custom_video_adl.cpp - ATI/AMD ADL library
---------------------------------------------------------
Switchres Modeline generation engine for emulation
License GPL-2.0+
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
Alexandre Wodarczyk, Gil Delescluse
**************************************************************/
// Constants and structures ported from AMD ADL SDK files
#include <windows.h>
#include <stdio.h>
#include "custom_video_adl.h"
#include "log.h"
//============================================================
// memory allocation callbacks
//============================================================
void* __stdcall ADL_Main_Memory_Alloc(int iSize)
{
void* lpBuffer = malloc(iSize);
return lpBuffer;
}
void __stdcall ADL_Main_Memory_Free(void** lpBuffer)
{
if (NULL != *lpBuffer)
{
free(*lpBuffer);
*lpBuffer = NULL;
}
}
//============================================================
// adl_timing::adl_timing
//============================================================
adl_timing::adl_timing(char *display_name, custom_video_settings *vs)
{
m_vs = *vs;
strcpy (m_display_name, display_name);
strcpy (m_device_key, m_vs.device_reg_key);
}
//============================================================
// adl_timing::~adl_timing
//============================================================
adl_timing::~adl_timing()
{
close();
}
//============================================================
// adl_timing::init
//============================================================
bool adl_timing::init()
{
int ADL_Err = ADL_ERR;
log_verbose("ATI/AMD ADL init\n");
ADL_Err = open();
if (ADL_Err != ADL_OK)
{
log_verbose("ERROR: ADL Initialization error!\n");
return false;
}
ADL2_Adapter_NumberOfAdapters_Get = (ADL2_ADAPTER_NUMBEROFADAPTERS_GET) (void *) GetProcAddress(hDLL,"ADL2_Adapter_NumberOfAdapters_Get");
if (ADL2_Adapter_NumberOfAdapters_Get == NULL)
{
log_verbose("ERROR: ADL2_Adapter_NumberOfAdapters_Get not available!");
return false;
}
ADL2_Adapter_AdapterInfo_Get = (ADL2_ADAPTER_ADAPTERINFO_GET) (void *) GetProcAddress(hDLL,"ADL2_Adapter_AdapterInfo_Get");
if (ADL2_Adapter_AdapterInfo_Get == NULL)
{
log_verbose("ERROR: ADL2_Adapter_AdapterInfo_Get not available!");
return false;
}
ADL2_Display_DisplayInfo_Get = (ADL2_DISPLAY_DISPLAYINFO_GET) (void *) GetProcAddress(hDLL,"ADL2_Display_DisplayInfo_Get");
if (ADL2_Display_DisplayInfo_Get == NULL)
{
log_verbose("ERROR: ADL2_Display_DisplayInfo_Get not available!");
return false;
}
ADL2_Display_ModeTimingOverride_Get = (ADL2_DISPLAY_MODETIMINGOVERRIDE_GET) (void *) GetProcAddress(hDLL,"ADL2_Display_ModeTimingOverride_Get");
if (ADL2_Display_ModeTimingOverride_Get == NULL)
{
log_verbose("ERROR: ADL2_Display_ModeTimingOverride_Get not available!");
return false;
}
ADL2_Display_ModeTimingOverride_Set = (ADL2_DISPLAY_MODETIMINGOVERRIDE_SET) (void *) GetProcAddress(hDLL,"ADL2_Display_ModeTimingOverride_Set");
if (ADL2_Display_ModeTimingOverride_Set == NULL)
{
log_verbose("ERROR: ADL2_Display_ModeTimingOverride_Set not available!");
return false;
}
ADL2_Display_ModeTimingOverrideList_Get = (ADL2_DISPLAY_MODETIMINGOVERRIDELIST_GET) (void *) GetProcAddress(hDLL,"ADL2_Display_ModeTimingOverrideList_Get");
if (ADL2_Display_ModeTimingOverrideList_Get == NULL)
{
log_verbose("ERROR: ADL2_Display_ModeTimingOverrideList_Get not available!");
return false;
}
ADL2_Flush_Driver_Data = (ADL2_FLUSH_DRIVER_DATA) (void *) GetProcAddress(hDLL,"ADL2_Flush_Driver_Data");
if (ADL2_Flush_Driver_Data == NULL)
{
log_verbose("ERROR: ADL2_Flush_Driver_Data not available!");
return false;
}
if (!enum_displays())
{
log_error("ADL error enumerating displays.\n");
return false;
}
if (!get_device_mapping_from_display_name())
{
log_error("ADL error mapping display.\n");
return false;
}
if (!get_driver_version(m_device_key))
{
log_error("ADL driver version unknown!.\n");
}
if (!get_timing_list())
{
log_error("ADL error getting list of timing overrides.\n");
}
log_verbose("ADL functions retrieved successfully.\n");
return true;
}
//============================================================
// adl_timing::adl_open
//============================================================
int adl_timing::open()
{
ADL2_MAIN_CONTROL_CREATE ADL2_Main_Control_Create;
int ADL_Err = ADL_ERR;
hDLL = LoadLibraryA("atiadlxx.dll");
if (hDLL == NULL) hDLL = LoadLibraryA("atiadlxy.dll");
if (hDLL != NULL)
{
ADL2_Main_Control_Create = (ADL2_MAIN_CONTROL_CREATE) (void *) GetProcAddress(hDLL, "ADL2_Main_Control_Create");
if (ADL2_Main_Control_Create != NULL)
ADL_Err = ADL2_Main_Control_Create(ADL_Main_Memory_Alloc, 1, &m_adl);
}
else
{
log_verbose("ADL Library not found!\n");
}
return ADL_Err;
}
//============================================================
// adl_timing::close
//============================================================
void adl_timing::close()
{
ADL2_MAIN_CONTROL_DESTROY ADL2_Main_Control_Destroy;
log_verbose("ATI/AMD ADL close\n");
for (int i = 0; i <= iNumberAdapters - 1; i++)
ADL_Main_Memory_Free((void **)&lpAdapter[i].m_display_list);
ADL_Main_Memory_Free((void **)&lpAdapterInfo);
ADL_Main_Memory_Free((void **)&lpAdapter);
ADL2_Main_Control_Destroy = (ADL2_MAIN_CONTROL_DESTROY) (void *) GetProcAddress(hDLL, "ADL2_Main_Control_Destroy");
if (ADL2_Main_Control_Destroy != NULL)
ADL2_Main_Control_Destroy(m_adl);
FreeLibrary(hDLL);
}
//============================================================
// adl_timing::get_driver_version
//============================================================
bool adl_timing::get_driver_version(char *device_key)
{
HKEY hkey;
bool found = false;
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, device_key, 0, KEY_READ , &hkey) == ERROR_SUCCESS)
{
BYTE cat_ver[32];
DWORD length = sizeof(cat_ver);
if ((RegQueryValueExA(hkey, "Catalyst_Version", NULL, NULL, cat_ver, &length) == ERROR_SUCCESS) ||
(RegQueryValueExA(hkey, "RadeonSoftwareVersion", NULL, NULL, cat_ver, &length) == ERROR_SUCCESS) ||
(RegQueryValueExA(hkey, "DriverVersion", NULL, NULL, cat_ver, &length) == ERROR_SUCCESS))
{
found = true;
is_patched = (RegQueryValueExA(hkey, "CalamityRelease", NULL, NULL, NULL, NULL) == ERROR_SUCCESS);
sscanf((char *)cat_ver, "%d.%d", &cat_version, &sub_version);
log_verbose("AMD driver version %d.%d%s\n", cat_version, sub_version, is_patched? "(patched)":"");
}
RegCloseKey(hkey);
}
return found;
}
//============================================================
// adl_timing::enum_displays
//============================================================
bool adl_timing::enum_displays()
{
ADL2_Adapter_NumberOfAdapters_Get(m_adl, &iNumberAdapters);
lpAdapterInfo = (LPAdapterInfo)malloc(sizeof(AdapterInfo) * iNumberAdapters);
memset(lpAdapterInfo, '\0', sizeof(AdapterInfo) * iNumberAdapters);
ADL2_Adapter_AdapterInfo_Get(m_adl, lpAdapterInfo, sizeof(AdapterInfo) * iNumberAdapters);
lpAdapter = (LPAdapterList)malloc(sizeof(AdapterList) * iNumberAdapters);
for (int i = 0; i <= iNumberAdapters - 1; i++)
{
lpAdapter[i].m_index = lpAdapterInfo[i].iAdapterIndex;
lpAdapter[i].m_bus = lpAdapterInfo[i].iBusNumber;
memcpy(&lpAdapter[i].m_name, &lpAdapterInfo[i].strAdapterName, ADL_MAX_PATH);
memcpy(&lpAdapter[i].m_display_name, &lpAdapterInfo[i].strDisplayName, ADL_MAX_PATH);
lpAdapter[i].m_num_of_displays = 0;
lpAdapter[i].m_display_list = 0;
// Only get display info from target adapter (this api is very slow!)
if (!strcmp(lpAdapter[i].m_display_name, m_display_name))
ADL2_Display_DisplayInfo_Get(m_adl, lpAdapter[i].m_index, &lpAdapter[i].m_num_of_displays, &lpAdapter[i].m_display_list, 1);
}
return true;
}
//============================================================
// adl_timing::get_device_mapping_from_display_name
//============================================================
bool adl_timing::get_device_mapping_from_display_name()
{
for (int i = 0; i <= iNumberAdapters -1; i++)
{
if (!strcmp(m_display_name, lpAdapter[i].m_display_name))
{
ADLDisplayInfo *display_list;
display_list = lpAdapter[i].m_display_list;
for (int j = 0; j <= lpAdapter[i].m_num_of_displays - 1; j++)
{
if (lpAdapter[i].m_index == display_list[j].displayID.iDisplayLogicalAdapterIndex)
{
m_adapter_index = lpAdapter[i].m_index;
m_display_index = display_list[j].displayID.iDisplayLogicalIndex;
return true;
}
}
}
}
return false;
}
//============================================================
// adl_timing::display_mode_info_to_modeline
//============================================================
bool adl_timing::display_mode_info_to_modeline(ADLDisplayModeInfo *dmi, modeline *m)
{
if (dmi->sDetailedTiming.sHTotal == 0) return false;
ADLDetailedTiming dt;
memcpy(&dt, &dmi->sDetailedTiming, sizeof(ADLDetailedTiming));
if (dt.sHTotal == 0) return false;
m->htotal = dt.sHTotal;
m->hactive = dt.sHDisplay;
m->hbegin = dt.sHSyncStart;
m->hend = dt.sHSyncWidth + m->hbegin;
m->vtotal = dt.sVTotal;
m->vactive = dt.sVDisplay;
m->vbegin = dt.sVSyncStart;
m->vend = dt.sVSyncWidth + m->vbegin;
m->interlace = (dt.sTimingFlags & ADL_DL_TIMINGFLAG_INTERLACED)? 1 : 0;
m->doublescan = (dt.sTimingFlags & ADL_DL_TIMINGFLAG_DOUBLE_SCAN)? 1 : 0;
m->hsync = ((dt.sTimingFlags & ADL_DL_TIMINGFLAG_H_SYNC_POLARITY)? 1 : 0) ^ invert_pol(1);
m->vsync = ((dt.sTimingFlags & ADL_DL_TIMINGFLAG_V_SYNC_POLARITY)? 1 : 0) ^ invert_pol(1) ;
m->pclock = dt.sPixelClock * 10000;
m->height = m->height? m->height : dmi->iPelsHeight;
m->width = m->width? m->width : dmi->iPelsWidth;
m->refresh = m->refresh? m->refresh : dmi->iRefreshRate / interlace_factor(m->interlace, 1);;
m->hfreq = float(m->pclock / m->htotal);
m->vfreq = float(m->hfreq / m->vtotal) * (m->interlace? 2 : 1);
return true;
}
//============================================================
// adl_timing::get_timing_list
//============================================================
bool adl_timing::get_timing_list()
{
if (ADL2_Display_ModeTimingOverrideList_Get(m_adl, m_adapter_index, m_display_index, MAX_MODELINES, adl_mode, &m_num_of_adl_modes) != ADL_OK) return false;
return true;
}
//============================================================
// adl_timing::get_timing_from_cache
//============================================================
bool adl_timing::get_timing_from_cache(modeline *m)
{
ADLDisplayModeInfo *mode = 0;
for (int i = 0; i < m_num_of_adl_modes; i++)
{
mode = &adl_mode[i];
if (mode->iPelsWidth == m->width && mode->iPelsHeight == m->height && mode->iRefreshRate == m->refresh)
{
if ((m->interlace) && !(mode->sDetailedTiming.sTimingFlags & ADL_DL_TIMINGFLAG_INTERLACED))
continue;
goto found;
}
}
return false;
found:
if (display_mode_info_to_modeline(mode, m)) return true;
return false;
}
//============================================================
// adl_timing::get_timing
//============================================================
bool adl_timing::get_timing(modeline *m)
{
ADLDisplayMode mode_in;
ADLDisplayModeInfo mode_info_out;
modeline m_temp = *m;
//modeline to ADLDisplayMode
mode_in.iPelsHeight = m->height;
mode_in.iPelsWidth = m->width;
mode_in.iBitsPerPel = 32;
mode_in.iDisplayFrequency = m->refresh * interlace_factor(m->interlace, 1);
if (ADL2_Display_ModeTimingOverride_Get(m_adl, m_adapter_index, m_display_index, &mode_in, &mode_info_out) != ADL_OK) goto not_found;
if (display_mode_info_to_modeline(&mode_info_out, &m_temp))
{
if (m_temp.interlace == m->interlace)
{
memcpy(m, &m_temp, sizeof(modeline));
m->type |= CUSTOM_VIDEO_TIMING_ATI_ADL;
return true;
}
}
not_found:
// Try to get timing from our cache (interlaced modes are not properly retrieved by ADL_Display_ModeTimingOverride_Get)
if (get_timing_from_cache(m))
{
m->type |= CUSTOM_VIDEO_TIMING_ATI_ADL;
return true;
}
return false;
}
//============================================================
// adl_timing::set_timing
//============================================================
bool adl_timing::set_timing(modeline *m)
{
return set_timing_override(m, TIMING_UPDATE);
}
//============================================================
// adl_timing::set_timing_override
//============================================================
bool adl_timing::set_timing_override(modeline *m, int update_mode)
{
ADLDisplayModeInfo mode_info = {};
ADLDetailedTiming *dt;
modeline m_temp;
//modeline to ADLDisplayModeInfo
mode_info.iTimingStandard = (update_mode & TIMING_DELETE)? ADL_DL_MODETIMING_STANDARD_DRIVER_DEFAULT : ADL_DL_MODETIMING_STANDARD_CUSTOM;
mode_info.iPossibleStandard = 0;
mode_info.iRefreshRate = m->refresh * interlace_factor(m->interlace, 0);
mode_info.iPelsWidth = m->width;
mode_info.iPelsHeight = m->height;
//modeline to ADLDetailedTiming
dt = &mode_info.sDetailedTiming;
dt->sTimingFlags = (m->interlace? ADL_DL_TIMINGFLAG_INTERLACED : 0) |
(m->doublescan? ADL_DL_TIMINGFLAG_DOUBLE_SCAN: 0) |
(m->hsync ^ invert_pol(0)? ADL_DL_TIMINGFLAG_H_SYNC_POLARITY : 0) |
(m->vsync ^ invert_pol(0)? ADL_DL_TIMINGFLAG_V_SYNC_POLARITY : 0);
dt->sHTotal = m->htotal;
dt->sHDisplay = m->hactive;
dt->sHSyncStart = m->hbegin;
dt->sHSyncWidth = m->hend - m->hbegin;
dt->sVTotal = m->vtotal;
dt->sVDisplay = m->vactive;
dt->sVSyncStart = m->vbegin;
dt->sVSyncWidth = m->vend - m->vbegin;
dt->sPixelClock = m->pclock / 10000;
dt->sHOverscanRight = 0;
dt->sHOverscanLeft = 0;
dt->sVOverscanBottom = 0;
dt->sVOverscanTop = 0;
if (ADL2_Display_ModeTimingOverride_Set(m_adl, m_adapter_index, m_display_index, &mode_info, (update_mode & TIMING_UPDATE_LIST)? 1 : 0) != ADL_OK) return false;
//ADL2_Flush_Driver_Data(m_adl, m_adapter_index);
// read modeline to trigger timing refresh on modded drivers
memcpy(&m_temp, m, sizeof(modeline));
if (update_mode & TIMING_UPDATE) get_timing(&m_temp);
return true;
}
//============================================================
// adl_timing::add_mode
//============================================================
bool adl_timing::add_mode(modeline *mode)
{
if (!set_timing_override(mode, TIMING_UPDATE_LIST))
{
return false;
}
m_resync.wait();
mode->type |= CUSTOM_VIDEO_TIMING_ATI_ADL;
return true;
}
//============================================================
// adl_timing::delete_mode
//============================================================
bool adl_timing::delete_mode(modeline *mode)
{
if (!set_timing_override(mode, TIMING_DELETE | TIMING_UPDATE_LIST))
{
return false;
}
m_resync.wait();
return true;
}
//============================================================
// adl_timing::update_mode
//============================================================
bool adl_timing::update_mode(modeline *mode)
{
bool refresh_required = !is_patched || (mode->type & MODE_DESKTOP);
if (!set_timing_override(mode, refresh_required? TIMING_UPDATE_LIST : TIMING_UPDATE))
{
return false;
}
if (refresh_required) m_resync.wait();
mode->type |= CUSTOM_VIDEO_TIMING_ATI_ADL;
return true;
}
//============================================================
// adl_timing::process_modelist
//============================================================
bool adl_timing::process_modelist(std::vector<modeline *> modelist)
{
bool refresh_required = false;
bool error = false;
for (auto &mode : modelist)
{
if (mode->type & MODE_DELETE || mode->type & MODE_ADD || (mode->type & MODE_UPDATE && (!is_patched || (mode->type & MODE_DESKTOP))))
refresh_required = true;
bool is_last = (mode == modelist.back());
if (!set_timing_override(mode, (mode->type & MODE_DELETE? TIMING_DELETE : TIMING_UPDATE) | (is_last && refresh_required? TIMING_UPDATE_LIST : 0)))
{
mode->type |= MODE_ERROR;
error = true;
}
else
{
mode->type &= ~MODE_ERROR;
mode->type |= CUSTOM_VIDEO_TIMING_ATI_ADL;
}
}
if (refresh_required) m_resync.wait();
return !error;
}

201
deps/switchres/custom_video_adl.h vendored Normal file
View File

@ -0,0 +1,201 @@
/**************************************************************
custom_video_adl.h - ATI/AMD ADL library header
---------------------------------------------------------
Switchres Modeline generation engine for emulation
License GPL-2.0+
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
Alexandre Wodarczyk, Gil Delescluse
**************************************************************/
#include <windows.h>
#include "custom_video.h"
#include "resync_windows.h"
// Constants and structures ported from AMD ADL SDK files
#define ADL_MAX_PATH 256
#define ADL_OK 0
#define ADL_ERR -1
//ADL_DETAILED_TIMING.sTimingFlags
#define ADL_DL_TIMINGFLAG_DOUBLE_SCAN 0x0001
#define ADL_DL_TIMINGFLAG_INTERLACED 0x0002
#define ADL_DL_TIMINGFLAG_H_SYNC_POLARITY 0x0004
#define ADL_DL_TIMINGFLAG_V_SYNC_POLARITY 0x0008
//ADL_DISPLAY_MODE_INFO.iTimingStandard
#define ADL_DL_MODETIMING_STANDARD_CVT 0x00000001 // CVT Standard
#define ADL_DL_MODETIMING_STANDARD_GTF 0x00000002 // GFT Standard
#define ADL_DL_MODETIMING_STANDARD_DMT 0x00000004 // DMT Standard
#define ADL_DL_MODETIMING_STANDARD_CUSTOM 0x00000008 // User-defined standard
#define ADL_DL_MODETIMING_STANDARD_DRIVER_DEFAULT 0x00000010 // Remove Mode from overriden list
#define ADL_DL_MODETIMING_STANDARD_CVT_RB 0x00000020 // CVT-RB Standard
typedef struct AdapterInfo
{
int iSize;
int iAdapterIndex;
char strUDID[ADL_MAX_PATH];
int iBusNumber;
int iDeviceNumber;
int iFunctionNumber;
int iVendorID;
char strAdapterName[ADL_MAX_PATH];
char strDisplayName[ADL_MAX_PATH];
int iPresent;
int iExist;
char strDriverPath[ADL_MAX_PATH];
char strDriverPathExt[ADL_MAX_PATH];
char strPNPString[ADL_MAX_PATH];
int iOSDisplayIndex;
} AdapterInfo, *LPAdapterInfo;
typedef struct ADLDisplayID
{
int iDisplayLogicalIndex;
int iDisplayPhysicalIndex;
int iDisplayLogicalAdapterIndex;
int iDisplayPhysicalAdapterIndex;
} ADLDisplayID, *LPADLDisplayID;
typedef struct ADLDisplayInfo
{
ADLDisplayID displayID;
int iDisplayControllerIndex;
char strDisplayName[ADL_MAX_PATH];
char strDisplayManufacturerName[ADL_MAX_PATH];
int iDisplayType;
int iDisplayOutputType;
int iDisplayConnector;
int iDisplayInfoMask;
int iDisplayInfoValue;
} ADLDisplayInfo, *LPADLDisplayInfo;
typedef struct ADLDisplayMode
{
int iPelsHeight;
int iPelsWidth;
int iBitsPerPel;
int iDisplayFrequency;
} ADLDisplayMode;
typedef struct ADLDetailedTiming
{
int iSize;
short sTimingFlags;
short sHTotal;
short sHDisplay;
short sHSyncStart;
short sHSyncWidth;
short sVTotal;
short sVDisplay;
short sVSyncStart;
short sVSyncWidth;
unsigned short sPixelClock;
short sHOverscanRight;
short sHOverscanLeft;
short sVOverscanBottom;
short sVOverscanTop;
short sOverscan8B;
short sOverscanGR;
} ADLDetailedTiming;
typedef struct ADLDisplayModeInfo
{
int iTimingStandard;
int iPossibleStandard;
int iRefreshRate;
int iPelsWidth;
int iPelsHeight;
ADLDetailedTiming sDetailedTiming;
} ADLDisplayModeInfo;
typedef struct AdapterList
{
int m_index;
int m_bus;
char m_name[ADL_MAX_PATH];
char m_display_name[ADL_MAX_PATH];
int m_num_of_displays;
ADLDisplayInfo *m_display_list;
} AdapterList, *LPAdapterList;
typedef void* ADL_CONTEXT_HANDLE;
typedef void* (__stdcall *ADL_MAIN_MALLOC_CALLBACK)(int);
typedef int (*ADL2_MAIN_CONTROL_CREATE)(ADL_MAIN_MALLOC_CALLBACK, int, ADL_CONTEXT_HANDLE *);
typedef int (*ADL2_MAIN_CONTROL_DESTROY)(ADL_CONTEXT_HANDLE);
typedef int (*ADL2_ADAPTER_NUMBEROFADAPTERS_GET) (ADL_CONTEXT_HANDLE, int*);
typedef int (*ADL2_ADAPTER_ADAPTERINFO_GET) (ADL_CONTEXT_HANDLE, LPAdapterInfo, int);
typedef int (*ADL2_DISPLAY_DISPLAYINFO_GET) (ADL_CONTEXT_HANDLE, int, int *, ADLDisplayInfo **, int);
typedef int (*ADL2_DISPLAY_MODETIMINGOVERRIDE_GET) (ADL_CONTEXT_HANDLE, int iAdapterIndex, int iDisplayIndex, ADLDisplayMode *lpModeIn, ADLDisplayModeInfo *lpModeInfoOut);
typedef int (*ADL2_DISPLAY_MODETIMINGOVERRIDE_SET) (ADL_CONTEXT_HANDLE, int iAdapterIndex, int iDisplayIndex, ADLDisplayModeInfo *lpMode, int iForceUpdate);
typedef int (*ADL2_DISPLAY_MODETIMINGOVERRIDELIST_GET) (ADL_CONTEXT_HANDLE, int iAdapterIndex, int iDisplayIndex, int iMaxNumOfOverrides, ADLDisplayModeInfo *lpModeInfoList, int *lpNumOfOverrides);
typedef int (*ADL2_FLUSH_DRIVER_DATA) (ADL_CONTEXT_HANDLE, int iAdapterIndex);
class adl_timing : public custom_video
{
public:
adl_timing(char *display_name, custom_video_settings *vs);
~adl_timing();
const char *api_name() { return "AMD ADL"; }
bool init();
void close();
int caps() { return allow_hardware_refresh()? CUSTOM_VIDEO_CAPS_UPDATE | CUSTOM_VIDEO_CAPS_ADD | CUSTOM_VIDEO_CAPS_DESKTOP_EDITABLE : is_patched? CUSTOM_VIDEO_CAPS_UPDATE : 0; }
bool add_mode(modeline *mode);
bool delete_mode(modeline *mode);
bool update_mode(modeline *mode);
bool get_timing(modeline *m);
bool set_timing(modeline *m);
bool process_modelist(std::vector<modeline *>);
private:
int open();
bool get_driver_version(char *device_key);
bool enum_displays();
bool get_device_mapping_from_display_name();
bool display_mode_info_to_modeline(ADLDisplayModeInfo *dmi, modeline *m);
bool get_timing_list();
bool get_timing_from_cache(modeline *m);
bool set_timing_override(modeline *m, int update_mode);
char m_display_name[32];
char m_device_key[128];
int m_adapter_index = 0;
int m_display_index = 0;
ADL2_ADAPTER_NUMBEROFADAPTERS_GET ADL2_Adapter_NumberOfAdapters_Get;
ADL2_ADAPTER_ADAPTERINFO_GET ADL2_Adapter_AdapterInfo_Get;
ADL2_DISPLAY_DISPLAYINFO_GET ADL2_Display_DisplayInfo_Get;
ADL2_DISPLAY_MODETIMINGOVERRIDE_GET ADL2_Display_ModeTimingOverride_Get;
ADL2_DISPLAY_MODETIMINGOVERRIDE_SET ADL2_Display_ModeTimingOverride_Set;
ADL2_DISPLAY_MODETIMINGOVERRIDELIST_GET ADL2_Display_ModeTimingOverrideList_Get;
ADL2_FLUSH_DRIVER_DATA ADL2_Flush_Driver_Data;
HINSTANCE hDLL;
LPAdapterInfo lpAdapterInfo = NULL;
LPAdapterList lpAdapter = NULL;;
int iNumberAdapters = 0;
int cat_version = 0;
int sub_version = 0;
bool is_patched = false;
ADL_CONTEXT_HANDLE m_adl = 0;
ADLDisplayModeInfo adl_mode[MAX_MODELINES];
int m_num_of_adl_modes = 0;
resync_handler m_resync;
int invert_pol(bool on_read) { return ((cat_version <= 12) || (cat_version >= 15 && on_read)); }
int interlace_factor(bool interlace, bool on_read) { return interlace && ((cat_version <= 12) || (cat_version >= 15 && on_read))? 2 : 1; }
};

343
deps/switchres/custom_video_ati.cpp vendored Normal file
View File

@ -0,0 +1,343 @@
/**************************************************************
custom_video_ati.cpp - ATI legacy library
---------------------------------------------------------
Switchres Modeline generation engine for emulation
License GPL-2.0+
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
Alexandre Wodarczyk, Gil Delescluse
**************************************************************/
#include <windows.h>
#include <stdio.h>
#include "custom_video_ati.h"
#include "log.h"
//============================================================
// ati_timing::ati_timing
//============================================================
ati_timing::ati_timing(char *device_name, custom_video_settings *vs)
{
m_vs = *vs;
strcpy (m_device_name, device_name);
strcpy (m_device_key, m_vs.device_reg_key);
}
//============================================================
// ati_timing::ati_timing
//============================================================
bool ati_timing::init()
{
log_verbose("ATI legacy init\n");
// Get Windows version
win_version = os_version();
if (win_version > 5 && !is_elevated())
{
log_error("ATI legacy error: the program needs administrator rights.\n");
return false;
}
return true;
}
//============================================================
// ati_timing::get_timing
//============================================================
bool ati_timing::get_timing(modeline *mode)
{
HKEY hKey;
char lp_name[1024];
char lp_data[68];
DWORD length;
bool found = false;
int refresh_label = mode->refresh_label? mode->refresh_label : mode->refresh * win_interlace_factor(mode);
int vfreq_incr = 0;
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, m_device_key, 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS)
{
sprintf(lp_name, "DALDTMCRTBCD%dx%dx0x%d", mode->width, mode->height, refresh_label);
length = sizeof(lp_data);
if (RegQueryValueExA(hKey, lp_name, NULL, NULL, (LPBYTE)lp_data, &length) == ERROR_SUCCESS && length == sizeof(lp_data))
found = true;
else if (win_version > 5 && mode->interlace)
{
vfreq_incr = 1;
sprintf(lp_name, "DALDTMCRTBCD%dx%dx0x%d", mode->width, mode->height, refresh_label + vfreq_incr);
if (RegQueryValueExA(hKey, lp_name, NULL, NULL, (LPBYTE)lp_data, &length) == ERROR_SUCCESS && length == sizeof(lp_data))
found = true;
}
if (found)
{
mode->pclock = get_DWORD_BCD(36, lp_data) * 10000;
mode->hactive = get_DWORD_BCD(8, lp_data);
mode->hbegin = get_DWORD_BCD(12, lp_data);
mode->hend = get_DWORD_BCD(16, lp_data) + mode->hbegin;
mode->htotal = get_DWORD_BCD(4, lp_data);
mode->vactive = get_DWORD_BCD(24, lp_data);
mode->vbegin = get_DWORD_BCD(28, lp_data);
mode->vend = get_DWORD_BCD(32, lp_data) + mode->vbegin;
mode->vtotal = get_DWORD_BCD(20, lp_data);
mode->interlace = (get_DWORD(0, lp_data) & CRTC_INTERLACED)?1:0;
mode->hsync = (get_DWORD(0, lp_data) & CRTC_H_SYNC_POLARITY)?0:1;
mode->vsync = (get_DWORD(0, lp_data) & CRTC_V_SYNC_POLARITY)?0:1;
mode->hfreq = mode->pclock / mode->htotal;
mode->vfreq = mode->hfreq / mode->vtotal * (mode->interlace?2:1);
mode->refresh_label = refresh_label;
mode->type |= CUSTOM_VIDEO_TIMING_ATI_LEGACY;
int checksum = 65535 - get_DWORD(0, lp_data) - mode->htotal - mode->hactive - mode->hend
- mode->vtotal - mode->vactive - mode->vend - mode->pclock/10000;
if (checksum != get_DWORD(64, lp_data))
log_verbose("bad checksum! ");
}
RegCloseKey(hKey);
return (found);
}
log_verbose("Failed opening registry entry for mode. ");
return false;
}
//============================================================
// ati_timing::set_timing
//============================================================
bool ati_timing::set_timing(modeline *mode)
{
HKEY hKey;
char lp_name[1024];
char lp_data[68];
long checksum;
bool found = false;
int refresh_label = mode->refresh_label? mode->refresh_label : mode->refresh * win_interlace_factor(mode);
int vfreq_incr = 0;
memset(lp_data, 0, sizeof(lp_data));
set_DWORD_BCD(lp_data, (int)mode->pclock/10000, 36);
set_DWORD_BCD(lp_data, mode->hactive, 8);
set_DWORD_BCD(lp_data, mode->hbegin, 12);
set_DWORD_BCD(lp_data, mode->hend - mode->hbegin, 16);
set_DWORD_BCD(lp_data, mode->htotal, 4);
set_DWORD_BCD(lp_data, mode->vactive, 24);
set_DWORD_BCD(lp_data, mode->vbegin, 28);
set_DWORD_BCD(lp_data, mode->vend - mode->vbegin, 32);
set_DWORD_BCD(lp_data, mode->vtotal, 20);
set_DWORD(lp_data, (mode->interlace?CRTC_INTERLACED:0) | (mode->hsync?0:CRTC_H_SYNC_POLARITY) | (mode->vsync?0:CRTC_V_SYNC_POLARITY), 0);
checksum = 65535 - get_DWORD(0, lp_data) - mode->htotal - mode->hactive - mode->hend
- mode->vtotal - mode->vactive - mode->vend - mode->pclock/10000;
set_DWORD(lp_data, checksum, 64);
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, m_device_key, 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS)
{
sprintf (lp_name, "DALDTMCRTBCD%dx%dx0x%d", mode->width, mode->height, refresh_label);
if (RegQueryValueExA(hKey, lp_name, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
found = true;
else if (win_version > 5 && mode->interlace)
{
vfreq_incr = 1;
sprintf(lp_name, "DALDTMCRTBCD%dx%dx0x%d", mode->width, mode->height, refresh_label + vfreq_incr);
if (RegQueryValueExA(hKey, lp_name, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
found = true;
}
if (!(found && RegSetValueExA(hKey, lp_name, 0, REG_BINARY, (LPBYTE)lp_data, 68) == ERROR_SUCCESS))
log_info("Failed saving registry entry %s\n", lp_name);
RegCloseKey(hKey);
return (found);
}
log_info("Failed updating registry entry for mode.\n");
return 0;
}
//============================================================
// ati_timing::update_mode
//============================================================
bool ati_timing::update_mode(modeline *mode)
{
if (!set_timing(mode))
return false;
mode->type |= CUSTOM_VIDEO_TIMING_ATI_LEGACY;
// ATI needs a call to EnumDisplaySettings to refresh timings
refresh_timings();
return true;
}
//============================================================
// ati_refresh_timings
//============================================================
void ati_timing::refresh_timings(void)
{
int iModeNum = 0;
DEVMODEA lpDevMode;
memset(&lpDevMode, 0, sizeof(DEVMODEA));
lpDevMode.dmSize = sizeof(DEVMODEA);
while (EnumDisplaySettingsExA(m_device_name, iModeNum, &lpDevMode, 0) != 0)
iModeNum++;
}
//============================================================
// adl_timing::process_modelist
//============================================================
bool ati_timing::process_modelist(std::vector<modeline *> modelist)
{
bool error = false;
for (auto &mode : modelist)
{
if (!set_timing(mode))
{
mode->type |= MODE_ERROR;
error = true;
}
else
{
mode->type &= ~MODE_ERROR;
mode->type |= CUSTOM_VIDEO_TIMING_ATI_LEGACY;
}
}
refresh_timings();
return !error;
}
//============================================================
// get_DWORD
//============================================================
int ati_timing::get_DWORD(int i, char *lp_data)
{
char out[32] = "";
UINT32 x;
sprintf(out, "%02X%02X%02X%02X", lp_data[i]&0xFF, lp_data[i+1]&0xFF, lp_data[i+2]&0xFF, lp_data[i+3]&0xFF);
sscanf(out, "%08X", &x);
return x;
}
//============================================================
// get_DWORD_BCD
//============================================================
int ati_timing::get_DWORD_BCD(int i, char *lp_data)
{
char out[32] = "";
UINT32 x;
sprintf(out, "%02X%02X%02X%02X", lp_data[i]&0xFF, lp_data[i+1]&0xFF, lp_data[i+2]&0xFF, lp_data[i+3]&0xFF);
sscanf(out, "%d", &x);
return x;
}
//============================================================
// set_DWORD
//============================================================
void ati_timing::set_DWORD(char *data_string, UINT32 data_dword, int offset)
{
char *p_dword = (char*)&data_dword;
data_string[offset] = p_dword[3]&0xFF;
data_string[offset+1] = p_dword[2]&0xFF;
data_string[offset+2] = p_dword[1]&0xFF;
data_string[offset+3] = p_dword[0]&0xFF;
}
//============================================================
// set_DWORD_BCD
//============================================================
void ati_timing::set_DWORD_BCD(char *data_string, UINT32 data_dword, int offset)
{
if (data_dword < 100000000)
{
int low_word, high_word;
int a, b, c, d;
char out[32] = "";
low_word = data_dword % 10000;
high_word = data_dword / 10000;
sprintf(out, "%d %d %d %d", high_word / 100, high_word % 100 , low_word / 100, low_word % 100);
sscanf(out, "%02X %02X %02X %02X", &a, &b, &c, &d);
data_string[offset] = a;
data_string[offset+1] = b;
data_string[offset+2] = c;
data_string[offset+3] = d;
}
}
//============================================================
// os_version
//============================================================
int ati_timing::os_version(void)
{
OSVERSIONINFOA lpVersionInfo;
memset(&lpVersionInfo, 0, sizeof(OSVERSIONINFOA));
lpVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
GetVersionExA (&lpVersionInfo);
return lpVersionInfo.dwMajorVersion;
}
//============================================================
// is_elevated
//============================================================
bool ati_timing::is_elevated()
{
HANDLE htoken;
bool result = false;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &htoken))
return false;
TOKEN_ELEVATION te = {0};
DWORD dw_return_length;
if (GetTokenInformation(htoken, TokenElevation, &te, sizeof(te), &dw_return_length))
{
if (te.TokenIsElevated)
{
result = true;
}
}
CloseHandle(htoken);
return (result);
}
//============================================================
// win_interlace_factor
//============================================================
int ati_timing::win_interlace_factor(modeline *mode)
{
if (win_version > 5 && mode->interlace)
return 2;
return 1;
}

53
deps/switchres/custom_video_ati.h vendored Normal file
View File

@ -0,0 +1,53 @@
/**************************************************************
custom_video_ati.h - ATI legacy library header
---------------------------------------------------------
Switchres Modeline generation engine for emulation
License GPL-2.0+
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
Alexandre Wodarczyk, Gil Delescluse
**************************************************************/
#include <windows.h>
#include "custom_video.h"
#define CRTC_DOUBLE_SCAN 0x0001
#define CRTC_INTERLACED 0x0002
#define CRTC_H_SYNC_POLARITY 0x0004
#define CRTC_V_SYNC_POLARITY 0x0008
class ati_timing : public custom_video
{
public:
ati_timing(char *device_name, custom_video_settings *vs);
~ati_timing() {};
const char *api_name() { return "ATI Legacy"; }
bool init();
int caps() { return CUSTOM_VIDEO_CAPS_UPDATE | CUSTOM_VIDEO_CAPS_SCAN_EDITABLE; }
bool update_mode(modeline *mode);
bool get_timing(modeline *mode);
bool set_timing(modeline *mode);
bool process_modelist(std::vector<modeline *>);
private:
void refresh_timings(void);
int get_DWORD(int i, char *lp_data);
int get_DWORD_BCD(int i, char *lp_data);
void set_DWORD(char *data_string, UINT32 data_word, int offset);
void set_DWORD_BCD(char *data_string, UINT32 data_word, int offset);
int os_version(void);
bool is_elevated();
int win_interlace_factor(modeline *mode);
char m_device_name[32];
char m_device_key[256];
int win_version;
};

View File

@ -0,0 +1,848 @@
/**************************************************************
custom_video_ati_family.cpp - ATI/AMD Radeon family
---------------------------------------------------------
Switchres Modeline generation engine for emulation
License GPL-2.0+
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
Alexandre Wodarczyk, Gil Delescluse
**************************************************************/
/* Constants and structures ported from Linux open source drivers:
drivers\gpu\drm\radeon\radeon.h
drivers\gpu\drm\radeon\radeon_family.h
include\drm\drm_pciids.h
*/
#ifndef RADEON_FAMILY_H
#define RADEON_FAMILY_H
struct pci_device_id
{
int vendor, device;
int subvendor, subdevice;
int _class, _class_mask;
int driver_data;
};
enum radeon_family
{
CHIP_R100 = 0,
CHIP_RV100,
CHIP_RS100,
CHIP_RV200,
CHIP_RS200,
CHIP_R200,
CHIP_RV250,
CHIP_RS300,
CHIP_RV280,
CHIP_R300,
CHIP_R350,
CHIP_RV350,
CHIP_RV380,
CHIP_R420,
CHIP_R423,
CHIP_RV410,
CHIP_RS400,
CHIP_RS480,
CHIP_RS600,
CHIP_RS690,
CHIP_RS740,
CHIP_RV515,
CHIP_R520,
CHIP_RV530,
CHIP_RV560,
CHIP_RV570,
CHIP_R580,
CHIP_R600,
CHIP_RV610,
CHIP_RV630,
CHIP_RV670,
CHIP_RV620,
CHIP_RV635,
CHIP_RS780,
CHIP_RS880,
CHIP_RV770,
CHIP_RV730,
CHIP_RV710,
CHIP_RV740,
CHIP_CEDAR,
CHIP_REDWOOD,
CHIP_JUNIPER,
CHIP_CYPRESS,
CHIP_HEMLOCK,
CHIP_PALM,
CHIP_SUMO,
CHIP_SUMO2,
CHIP_BARTS,
CHIP_TURKS,
CHIP_CAICOS,
CHIP_CAYMAN,
CHIP_ARUBA,
CHIP_TAHITI,
CHIP_PITCAIRN,
CHIP_VERDE,
CHIP_OLAND,
CHIP_HAINAN,
CHIP_BONAIRE,
CHIP_KAVERI,
CHIP_KABINI,
CHIP_HAWAII,
CHIP_MULLINS,
CHIP_LAST,
};
enum radeon_chip_flags
{
RADEON_FAMILY_MASK = 0x0000ffffUL,
RADEON_FLAGS_MASK = 0xffff0000UL,
RADEON_IS_MOBILITY = 0x00010000UL,
RADEON_IS_IGP = 0x00020000UL,
RADEON_SINGLE_CRTC = 0x00040000UL,
RADEON_IS_AGP = 0x00080000UL,
RADEON_HAS_HIERZ = 0x00100000UL,
RADEON_IS_PCIE = 0x00200000UL,
RADEON_NEW_MEMMAP = 0x00400000UL,
RADEON_IS_PCI = 0x00800000UL,
RADEON_IS_IGPGART = 0x01000000UL,
RADEON_IS_PX = 0x02000000UL,
};
#define PCI_ANY_ID (~0)
#define radeon_PCI_IDS \
{0x1002, 0x1304, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x1305, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x1306, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x1307, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x1309, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x130A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x130B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x130C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x130D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x130E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x130F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x1310, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x1311, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x1312, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x1313, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x1315, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x1316, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x1317, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x1318, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x131B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x131C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x131D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x3150, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY}, \
{0x1002, 0x3151, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x3152, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x3154, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x3155, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x3E50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \
{0x1002, 0x3E54, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \
{0x1002, 0x4136, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS100|RADEON_IS_IGP}, \
{0x1002, 0x4137, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|RADEON_IS_IGP}, \
{0x1002, 0x4144, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
{0x1002, 0x4145, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
{0x1002, 0x4146, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
{0x1002, 0x4147, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
{0x1002, 0x4148, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
{0x1002, 0x4149, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
{0x1002, 0x414A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
{0x1002, 0x414B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
{0x1002, 0x4150, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \
{0x1002, 0x4151, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \
{0x1002, 0x4152, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \
{0x1002, 0x4153, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \
{0x1002, 0x4154, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \
{0x1002, 0x4155, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \
{0x1002, 0x4156, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \
{0x1002, 0x4237, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|RADEON_IS_IGP}, \
{0x1002, 0x4242, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \
{0x1002, 0x4336, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS100|RADEON_IS_IGP|RADEON_IS_MOBILITY}, \
{0x1002, 0x4337, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|RADEON_IS_IGP|RADEON_IS_MOBILITY}, \
{0x1002, 0x4437, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|RADEON_IS_IGP|RADEON_IS_MOBILITY}, \
{0x1002, 0x4966, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250}, \
{0x1002, 0x4967, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250}, \
{0x1002, 0x4A48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
{0x1002, 0x4A49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
{0x1002, 0x4A4A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
{0x1002, 0x4A4B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
{0x1002, 0x4A4C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
{0x1002, 0x4A4D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
{0x1002, 0x4A4E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x4A4F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
{0x1002, 0x4A50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
{0x1002, 0x4A54, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
{0x1002, 0x4B48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
{0x1002, 0x4B49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
{0x1002, 0x4B4A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
{0x1002, 0x4B4B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
{0x1002, 0x4B4C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
{0x1002, 0x4C57, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV200|RADEON_IS_MOBILITY}, \
{0x1002, 0x4C58, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV200|RADEON_IS_MOBILITY}, \
{0x1002, 0x4C59, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100|RADEON_IS_MOBILITY}, \
{0x1002, 0x4C5A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100|RADEON_IS_MOBILITY}, \
{0x1002, 0x4C64, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250|RADEON_IS_MOBILITY}, \
{0x1002, 0x4C66, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250|RADEON_IS_MOBILITY}, \
{0x1002, 0x4C67, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250|RADEON_IS_MOBILITY}, \
{0x1002, 0x4C6E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280|RADEON_IS_MOBILITY}, \
{0x1002, 0x4E44, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
{0x1002, 0x4E45, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
{0x1002, 0x4E46, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
{0x1002, 0x4E47, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
{0x1002, 0x4E48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
{0x1002, 0x4E49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
{0x1002, 0x4E4A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
{0x1002, 0x4E4B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
{0x1002, 0x4E50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \
{0x1002, 0x4E51, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \
{0x1002, 0x4E52, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \
{0x1002, 0x4E53, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \
{0x1002, 0x4E54, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \
{0x1002, 0x4E56, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \
{0x1002, 0x5144, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|RADEON_SINGLE_CRTC}, \
{0x1002, 0x5145, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|RADEON_SINGLE_CRTC}, \
{0x1002, 0x5146, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|RADEON_SINGLE_CRTC}, \
{0x1002, 0x5147, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|RADEON_SINGLE_CRTC}, \
{0x1002, 0x5148, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \
{0x1002, 0x514C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \
{0x1002, 0x514D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \
{0x1002, 0x5157, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV200}, \
{0x1002, 0x5158, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV200}, \
{0x1002, 0x5159, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100}, \
{0x1002, 0x515A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100}, \
{0x1002, 0x515E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100|RADEON_SINGLE_CRTC}, \
{0x1002, 0x5460, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY}, \
{0x1002, 0x5462, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY}, \
{0x1002, 0x5464, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY}, \
{0x1002, 0x5548, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
{0x1002, 0x5549, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
{0x1002, 0x554A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
{0x1002, 0x554B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
{0x1002, 0x554C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
{0x1002, 0x554D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
{0x1002, 0x554E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
{0x1002, 0x554F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
{0x1002, 0x5550, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
{0x1002, 0x5551, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
{0x1002, 0x5552, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
{0x1002, 0x5554, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
{0x1002, 0x564A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x564B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x564F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x5652, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x5653, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x5657, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \
{0x1002, 0x5834, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|RADEON_IS_IGP}, \
{0x1002, 0x5835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|RADEON_IS_IGP|RADEON_IS_MOBILITY}, \
{0x1002, 0x5954, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS480|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART}, \
{0x1002, 0x5955, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS480|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART}, \
{0x1002, 0x5974, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS480|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART}, \
{0x1002, 0x5975, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS480|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART}, \
{0x1002, 0x5960, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \
{0x1002, 0x5961, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \
{0x1002, 0x5962, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \
{0x1002, 0x5964, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \
{0x1002, 0x5965, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \
{0x1002, 0x5969, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100|RADEON_SINGLE_CRTC}, \
{0x1002, 0x5a41, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS400|RADEON_IS_IGP|RADEON_IS_IGPGART}, \
{0x1002, 0x5a42, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS400|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART}, \
{0x1002, 0x5a61, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS400|RADEON_IS_IGP|RADEON_IS_IGPGART}, \
{0x1002, 0x5a62, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS400|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_IS_IGPGART}, \
{0x1002, 0x5b60, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \
{0x1002, 0x5b62, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \
{0x1002, 0x5b63, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \
{0x1002, 0x5b64, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \
{0x1002, 0x5b65, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \
{0x1002, 0x5c61, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280|RADEON_IS_MOBILITY}, \
{0x1002, 0x5c63, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280|RADEON_IS_MOBILITY}, \
{0x1002, 0x5d48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x5d49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x5d4a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x5d4c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
{0x1002, 0x5d4d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
{0x1002, 0x5d4e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
{0x1002, 0x5d4f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
{0x1002, 0x5d50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
{0x1002, 0x5d52, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
{0x1002, 0x5d57, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R423|RADEON_NEW_MEMMAP}, \
{0x1002, 0x5e48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \
{0x1002, 0x5e4a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \
{0x1002, 0x5e4b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \
{0x1002, 0x5e4c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \
{0x1002, 0x5e4d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \
{0x1002, 0x5e4f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6600, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6601, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6602, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6603, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6604, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6605, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6606, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6607, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6608, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6610, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6611, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6613, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6620, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6621, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6623, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6631, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6640, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6641, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6646, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6647, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6649, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6650, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6651, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6658, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_NEW_MEMMAP}, \
{0x1002, 0x665c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_NEW_MEMMAP}, \
{0x1002, 0x665d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6660, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6663, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6664, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6665, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6667, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x666F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6700, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6701, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6702, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6703, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6704, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6705, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6706, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6707, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6708, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6709, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6718, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6719, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
{0x1002, 0x671c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
{0x1002, 0x671d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
{0x1002, 0x671f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAYMAN|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6720, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6721, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6722, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6723, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6724, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6725, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6726, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6727, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6728, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6729, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6738, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6739, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x673e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6740, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6741, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6742, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6743, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6744, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6745, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6746, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6747, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6748, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6749, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x674A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6750, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6751, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6758, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6759, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x675B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x675D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x675F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6760, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6761, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6762, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6763, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6764, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6765, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6766, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6767, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6768, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6770, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6771, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6772, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6778, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6779, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x677B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CAICOS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6780, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6784, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6788, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
{0x1002, 0x678A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6790, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6791, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6792, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6798, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6799, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
{0x1002, 0x679A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
{0x1002, 0x679B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
{0x1002, 0x679E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
{0x1002, 0x679F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI|RADEON_NEW_MEMMAP}, \
{0x1002, 0x67A0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
{0x1002, 0x67A1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
{0x1002, 0x67A2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
{0x1002, 0x67A8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
{0x1002, 0x67A9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
{0x1002, 0x67AA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
{0x1002, 0x67B0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
{0x1002, 0x67B1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
{0x1002, 0x67B8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
{0x1002, 0x67B9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
{0x1002, 0x67BA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
{0x1002, 0x67BE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6800, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6801, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6802, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6806, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6808, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6809, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6810, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6811, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6816, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6817, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6818, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6819, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6820, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6821, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6822, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6823, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6824, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6825, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6826, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6827, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6828, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6829, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \
{0x1002, 0x682A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x682B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x682C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \
{0x1002, 0x682D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x682F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6830, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6831, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6837, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6838, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6839, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \
{0x1002, 0x683B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \
{0x1002, 0x683D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \
{0x1002, 0x683F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6840, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6841, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6842, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6843, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6849, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x684C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6850, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6858, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6859, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6880, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6888, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6889, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x688A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x688C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x688D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6898, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6899, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x689b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x689c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HEMLOCK|RADEON_NEW_MEMMAP}, \
{0x1002, 0x689d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HEMLOCK|RADEON_NEW_MEMMAP}, \
{0x1002, 0x689e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \
{0x1002, 0x68a0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x68a1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x68a8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \
{0x1002, 0x68a9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \
{0x1002, 0x68b0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x68b8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \
{0x1002, 0x68b9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \
{0x1002, 0x68ba, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \
{0x1002, 0x68be, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \
{0x1002, 0x68bf, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \
{0x1002, 0x68c0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x68c1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x68c7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x68c8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_NEW_MEMMAP}, \
{0x1002, 0x68c9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_NEW_MEMMAP}, \
{0x1002, 0x68d8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_NEW_MEMMAP}, \
{0x1002, 0x68d9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_NEW_MEMMAP}, \
{0x1002, 0x68da, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_NEW_MEMMAP}, \
{0x1002, 0x68de, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_NEW_MEMMAP}, \
{0x1002, 0x68e0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x68e1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x68e4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x68e5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x68e8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_NEW_MEMMAP}, \
{0x1002, 0x68e9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_NEW_MEMMAP}, \
{0x1002, 0x68f1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_NEW_MEMMAP}, \
{0x1002, 0x68f2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_NEW_MEMMAP}, \
{0x1002, 0x68f8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_NEW_MEMMAP}, \
{0x1002, 0x68f9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_NEW_MEMMAP}, \
{0x1002, 0x68fa, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_NEW_MEMMAP}, \
{0x1002, 0x68fe, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CEDAR|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7101, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7103, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7104, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7105, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7106, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7108, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7109, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_NEW_MEMMAP}, \
{0x1002, 0x710A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_NEW_MEMMAP}, \
{0x1002, 0x710B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_NEW_MEMMAP}, \
{0x1002, 0x710C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_NEW_MEMMAP}, \
{0x1002, 0x710E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_NEW_MEMMAP}, \
{0x1002, 0x710F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R520|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7140, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7141, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7142, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7143, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7144, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7145, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7146, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7147, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7149, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x714A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x714B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x714C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x714D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
{0x1002, 0x714E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
{0x1002, 0x714F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7151, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7152, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7153, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
{0x1002, 0x715E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
{0x1002, 0x715F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7180, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7181, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7183, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7186, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7187, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7188, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x718A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x718B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x718C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x718D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x718F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7193, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7196, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x719B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
{0x1002, 0x719F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
{0x1002, 0x71C0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_NEW_MEMMAP}, \
{0x1002, 0x71C1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_NEW_MEMMAP}, \
{0x1002, 0x71C2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_NEW_MEMMAP}, \
{0x1002, 0x71C3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_NEW_MEMMAP}, \
{0x1002, 0x71C4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x71C5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x71C6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_NEW_MEMMAP}, \
{0x1002, 0x71C7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_NEW_MEMMAP}, \
{0x1002, 0x71CD, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_NEW_MEMMAP}, \
{0x1002, 0x71CE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_NEW_MEMMAP}, \
{0x1002, 0x71D2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_NEW_MEMMAP}, \
{0x1002, 0x71D4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x71D5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x71D6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x71DA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_NEW_MEMMAP}, \
{0x1002, 0x71DE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV530|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7200, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7210, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV515|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7240, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7243, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7244, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7245, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7246, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7247, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7248, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7249, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
{0x1002, 0x724A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
{0x1002, 0x724B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
{0x1002, 0x724C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
{0x1002, 0x724D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
{0x1002, 0x724E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
{0x1002, 0x724F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7280, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV570|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7281, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV560|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7283, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV560|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7284, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R580|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7287, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV560|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7288, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV570|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7289, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV570|RADEON_NEW_MEMMAP}, \
{0x1002, 0x728B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV570|RADEON_NEW_MEMMAP}, \
{0x1002, 0x728C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV570|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7290, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV560|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7291, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV560|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7293, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV560|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7297, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV560|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7834, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|RADEON_IS_IGP|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x791e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS690|RADEON_IS_IGP|RADEON_NEW_MEMMAP|RADEON_IS_IGPGART}, \
{0x1002, 0x791f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS690|RADEON_IS_IGP|RADEON_NEW_MEMMAP|RADEON_IS_IGPGART}, \
{0x1002, 0x793f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS600|RADEON_IS_IGP|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7941, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS600|RADEON_IS_IGP|RADEON_NEW_MEMMAP}, \
{0x1002, 0x7942, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS600|RADEON_IS_IGP|RADEON_NEW_MEMMAP}, \
{0x1002, 0x796c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS740|RADEON_IS_IGP|RADEON_NEW_MEMMAP|RADEON_IS_IGPGART}, \
{0x1002, 0x796d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS740|RADEON_IS_IGP|RADEON_NEW_MEMMAP|RADEON_IS_IGPGART}, \
{0x1002, 0x796e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS740|RADEON_IS_IGP|RADEON_NEW_MEMMAP|RADEON_IS_IGPGART}, \
{0x1002, 0x796f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS740|RADEON_IS_IGP|RADEON_NEW_MEMMAP|RADEON_IS_IGPGART}, \
{0x1002, 0x9400, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R600|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9401, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R600|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9402, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R600|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9403, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R600|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9405, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R600|RADEON_NEW_MEMMAP}, \
{0x1002, 0x940A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R600|RADEON_NEW_MEMMAP}, \
{0x1002, 0x940B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R600|RADEON_NEW_MEMMAP}, \
{0x1002, 0x940F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R600|RADEON_NEW_MEMMAP}, \
{0x1002, 0x94A0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x94A1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x94A3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x94B1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_NEW_MEMMAP}, \
{0x1002, 0x94B3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_NEW_MEMMAP}, \
{0x1002, 0x94B4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_NEW_MEMMAP}, \
{0x1002, 0x94B5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_NEW_MEMMAP}, \
{0x1002, 0x94B9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV740|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9440, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9441, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9442, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9443, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9444, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9446, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
{0x1002, 0x944A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x944B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x944C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
{0x1002, 0x944E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9450, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9452, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9456, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
{0x1002, 0x945A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x945B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x945E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9460, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9462, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_NEW_MEMMAP}, \
{0x1002, 0x946A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x946B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x947A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x947B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV770|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9480, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9487, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9488, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9489, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x948A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x948F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9490, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9491, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9495, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9498, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_NEW_MEMMAP}, \
{0x1002, 0x949C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_NEW_MEMMAP}, \
{0x1002, 0x949E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_NEW_MEMMAP}, \
{0x1002, 0x949F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV730|RADEON_NEW_MEMMAP}, \
{0x1002, 0x94C0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_NEW_MEMMAP}, \
{0x1002, 0x94C1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_NEW_MEMMAP}, \
{0x1002, 0x94C3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_NEW_MEMMAP}, \
{0x1002, 0x94C4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_NEW_MEMMAP}, \
{0x1002, 0x94C5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_NEW_MEMMAP}, \
{0x1002, 0x94C6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_NEW_MEMMAP}, \
{0x1002, 0x94C7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_NEW_MEMMAP}, \
{0x1002, 0x94C8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x94C9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x94CB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x94CC, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_NEW_MEMMAP}, \
{0x1002, 0x94CD, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV610|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9500, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9501, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9504, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9505, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9506, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9507, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9508, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9509, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x950F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9511, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9515, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9517, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9519, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV670|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9540, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9541, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9542, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_NEW_MEMMAP}, \
{0x1002, 0x954E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_NEW_MEMMAP}, \
{0x1002, 0x954F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9552, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9553, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9555, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9557, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x955f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV710|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9580, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9581, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9583, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9586, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9587, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9588, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9589, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_NEW_MEMMAP}, \
{0x1002, 0x958A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_NEW_MEMMAP}, \
{0x1002, 0x958B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x958C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_NEW_MEMMAP}, \
{0x1002, 0x958D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_NEW_MEMMAP}, \
{0x1002, 0x958E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_NEW_MEMMAP}, \
{0x1002, 0x958F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV630|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9590, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV635|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9591, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV635|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9593, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV635|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9595, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV635|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9596, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV635|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9597, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV635|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9598, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV635|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9599, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV635|RADEON_NEW_MEMMAP}, \
{0x1002, 0x959B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV635|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x95C0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_NEW_MEMMAP}, \
{0x1002, 0x95C2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x95C4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x95C5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_NEW_MEMMAP}, \
{0x1002, 0x95C6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_NEW_MEMMAP}, \
{0x1002, 0x95C7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_NEW_MEMMAP}, \
{0x1002, 0x95C9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_NEW_MEMMAP}, \
{0x1002, 0x95CC, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_NEW_MEMMAP}, \
{0x1002, 0x95CD, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_NEW_MEMMAP}, \
{0x1002, 0x95CE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_NEW_MEMMAP}, \
{0x1002, 0x95CF, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV620|RADEON_NEW_MEMMAP}, \
{0x1002, 0x9610, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS780|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9611, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS780|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9612, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS780|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9613, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS780|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9614, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS780|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9615, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS780|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9616, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS780|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9640, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9641, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9642, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO2|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9643, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO2|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9644, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO2|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9645, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO2|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9647, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP},\
{0x1002, 0x9648, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP},\
{0x1002, 0x9649, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO2|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP},\
{0x1002, 0x964a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x964b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x964c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x964e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP},\
{0x1002, 0x964f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SUMO|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP},\
{0x1002, 0x9710, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS880|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9711, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS880|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9712, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS880|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9713, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS880|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9714, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS880|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9715, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS880|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9802, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9803, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9804, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9805, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9806, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9807, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9808, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9809, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x980A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9830, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9831, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9832, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9833, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9834, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9836, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9837, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9838, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9839, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x983a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x983b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x983c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x983d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x983e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x983f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9850, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9851, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9852, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9853, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9854, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9855, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9856, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9857, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9858, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9859, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x985A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x985B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x985C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x985D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x985E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x985F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9900, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9901, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9903, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9904, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9905, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9906, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9907, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9908, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9909, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x990A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x990B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x990C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x990D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x990E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x990F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9910, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9913, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9917, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9918, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9919, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9990, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9991, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9992, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9993, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9994, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9995, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9996, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9997, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9998, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x9999, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x999A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x999B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x999C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x999D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x99A0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x99A2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0x1002, 0x99A4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
{0, 0, 0, 0, 0, 0, 0}
static struct pci_device_id pciidlist[] = {radeon_PCI_IDS};
//============================================================
// ati_family
//============================================================
int ati_family(int vendor, int device)
{
int i = 0;
while (pciidlist[i].vendor)
{
if (pciidlist[i].vendor == vendor && pciidlist[i].device == device)
return (pciidlist[i].driver_data & RADEON_FAMILY_MASK);
i++;
}
// Not found, must be newer
if (vendor == 0x1002)
return CHIP_LAST;
return 0;
}
//============================================================
// ati_is_legacy
//============================================================
bool ati_is_legacy(int vendor, int device)
{
return (ati_family(vendor, device) < CHIP_CEDAR);
}
#endif

795
deps/switchres/custom_video_drmkms.cpp vendored Normal file
View File

@ -0,0 +1,795 @@
/**************************************************************
custom_video_drmkms.cpp - Linux DRM/KMS video management layer
---------------------------------------------------------
Switchres Modeline generation engine for emulation
License GPL-2.0+
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
Alexandre Wodarczyk, Gil Delescluse
**************************************************************/
#include <stdio.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include "custom_video_drmkms.h"
#include "log.h"
#define drmGetVersion p_drmGetVersion
#define drmFreeVersion p_drmFreeVersion
#define drmModeGetResources p_drmModeGetResources
#define drmModeGetConnector p_drmModeGetConnector
#define drmModeFreeConnector p_drmModeFreeConnector
#define drmModeFreeResources p_drmModeFreeResources
#define drmModeGetEncoder p_drmModeGetEncoder
#define drmModeFreeEncoder p_drmModeFreeEncoder
#define drmModeGetCrtc p_drmModeGetCrtc
#define drmModeSetCrtc p_drmModeSetCrtc
#define drmModeFreeCrtc p_drmModeFreeCrtc
#define drmModeAttachMode p_drmModeAttachMode
#define drmModeAddFB p_drmModeAddFB
#define drmModeRmFB p_drmModeRmFB
#define drmModeGetFB p_drmModeGetFB
#define drmModeFreeFB p_drmModeFreeFB
#define drmPrimeHandleToFD p_drmPrimeHandleToFD
#define drmModeGetPlaneResources p_drmModeGetPlaneResources
#define drmModeFreePlaneResources p_drmModeFreePlaneResources
#define drmIoctl p_drmIoctl
#define drmGetCap p_drmGetCap
#define drmIsMaster p_drmIsMaster
#define drmSetMaster p_drmSetMaster
#define drmDropMaster p_drmDropMaster
//============================================================
// shared the privileges of the master fd
//============================================================
static int s_shared_fd[10] = {};
static int s_shared_count[10] = {};
//============================================================
// list connector types
//============================================================
const char *get_connector_name(int mode)
{
switch (mode)
{
case DRM_MODE_CONNECTOR_Unknown:
return "Unknown";
case DRM_MODE_CONNECTOR_VGA:
return "VGA-";
case DRM_MODE_CONNECTOR_DVII:
return "DVI-I-";
case DRM_MODE_CONNECTOR_DVID:
return "DVI-D-";
case DRM_MODE_CONNECTOR_DVIA:
return "DVI-A-";
case DRM_MODE_CONNECTOR_Composite:
return "Composite-";
case DRM_MODE_CONNECTOR_SVIDEO:
return "SVIDEO-";
case DRM_MODE_CONNECTOR_LVDS:
return "LVDS-";
case DRM_MODE_CONNECTOR_Component:
return "Component-";
case DRM_MODE_CONNECTOR_9PinDIN:
return "9PinDIN-";
case DRM_MODE_CONNECTOR_DisplayPort:
return "DisplayPort-";
case DRM_MODE_CONNECTOR_HDMIA:
return "HDMI-A-";
case DRM_MODE_CONNECTOR_HDMIB:
return "HDMI-B-";
case DRM_MODE_CONNECTOR_TV:
return "TV-";
case DRM_MODE_CONNECTOR_eDP:
return "eDP-";
case DRM_MODE_CONNECTOR_VIRTUAL:
return "VIRTUAL-";
case DRM_MODE_CONNECTOR_DSI:
return "DSI-";
case DRM_MODE_CONNECTOR_DPI:
return "DPI-";
default:
return "not_defined-";
}
}
//============================================================
// id for class object (static)
//============================================================
static int static_id = 0;
//============================================================
// drmkms_timing::drmkms_timing
//============================================================
drmkms_timing::drmkms_timing(char *device_name, custom_video_settings *vs)
{
m_vs = *vs;
m_id = ++static_id;
log_verbose("DRM/KMS: <%d> (drmkms_timing) creation (%s)\n", m_id, device_name);
// Copy screen device name and limit size
if ((strlen(device_name) + 1) > 32)
{
strncpy(m_device_name, device_name, 31);
log_error("DRM/KMS: <%d> (drmkms_timing) [ERROR] the devine name is too long it has been trucated to %s\n", m_id, m_device_name);
}
else
strcpy(m_device_name, device_name);
}
//============================================================
// drmkms_timing::~drmkms_timing
//============================================================
drmkms_timing::~drmkms_timing()
{
// close DRM/KMS library
if (mp_drm_handle)
dlclose(mp_drm_handle);
if (m_drm_fd > 0)
{
if (!--s_shared_count[m_card_id])
close(m_drm_fd);
}
}
//============================================================
// drmkms_timing::init
//============================================================
bool drmkms_timing::init()
{
log_verbose("DRM/KMS: <%d> (init) loading DRM/KMS library\n", m_id);
mp_drm_handle = dlopen("libdrm.so", RTLD_NOW);
if (mp_drm_handle)
{
p_drmGetVersion = (__typeof__(drmGetVersion)) dlsym(mp_drm_handle, "drmGetVersion");
if (p_drmGetVersion == NULL)
{
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmGetVersion", "DRM_LIBRARY");
return false;
}
p_drmFreeVersion = (__typeof__(drmFreeVersion)) dlsym(mp_drm_handle, "drmFreeVersion");
if (p_drmFreeVersion == NULL)
{
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmFreeVersion", "DRM_LIBRARY");
return false;
}
p_drmModeGetResources = (__typeof__(drmModeGetResources)) dlsym(mp_drm_handle, "drmModeGetResources");
if (p_drmModeGetResources == NULL)
{
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeGetResources", "DRM_LIBRARY");
return false;
}
p_drmModeGetConnector = (__typeof__(drmModeGetConnector)) dlsym(mp_drm_handle, "drmModeGetConnector");
if (p_drmModeGetConnector == NULL)
{
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeGetConnector", "DRM_LIBRARY");
return false;
}
p_drmModeFreeConnector = (__typeof__(drmModeFreeConnector)) dlsym(mp_drm_handle, "drmModeFreeConnector");
if (p_drmModeFreeConnector == NULL)
{
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeFreeConnector", "DRM_LIBRARY");
return false;
}
p_drmModeFreeResources = (__typeof__(drmModeFreeResources)) dlsym(mp_drm_handle, "drmModeFreeResources");
if (p_drmModeFreeResources == NULL)
{
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeFreeResources", "DRM_LIBRARY");
return false;
}
p_drmModeGetEncoder = (__typeof__(drmModeGetEncoder)) dlsym(mp_drm_handle, "drmModeGetEncoder");
if (p_drmModeGetEncoder == NULL)
{
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeGetEncoder", "DRM_LIBRARY");
return false;
}
p_drmModeFreeEncoder = (__typeof__(drmModeFreeEncoder)) dlsym(mp_drm_handle, "drmModeFreeEncoder");
if (p_drmModeFreeEncoder == NULL)
{
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeFreeEncoder", "DRM_LIBRARY");
return false;
}
p_drmModeGetCrtc = (__typeof__(drmModeGetCrtc)) dlsym(mp_drm_handle, "drmModeGetCrtc");
if (p_drmModeGetCrtc == NULL)
{
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeGetCrtc", "DRM_LIBRARY");
return false;
}
p_drmModeSetCrtc = (__typeof__(drmModeSetCrtc)) dlsym(mp_drm_handle, "drmModeSetCrtc");
if (p_drmModeSetCrtc == NULL)
{
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeSetCrtc", "DRM_LIBRARY");
return false;
}
p_drmModeFreeCrtc = (__typeof__(drmModeFreeCrtc)) dlsym(mp_drm_handle, "drmModeFreeCrtc");
if (p_drmModeFreeCrtc == NULL)
{
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeFreeCrtc", "DRM_LIBRARY");
return false;
}
p_drmModeAttachMode = (__typeof__(drmModeAttachMode)) dlsym(mp_drm_handle, "drmModeAttachMode");
if (p_drmModeAttachMode == NULL)
{
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeAttachMode", "DRM_LIBRARY");
return false;
}
p_drmModeAddFB = (__typeof__(drmModeAddFB)) dlsym(mp_drm_handle, "drmModeAddFB");
if (p_drmModeAddFB == NULL)
{
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeAddFB", "DRM_LIBRARY");
return false;
}
p_drmModeRmFB = (__typeof__(drmModeRmFB)) dlsym(mp_drm_handle, "drmModeRmFB");
if (p_drmModeRmFB == NULL)
{
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeRmFB", "DRM_LIBRARY");
return false;
}
p_drmModeGetFB = (__typeof__(drmModeGetFB)) dlsym(mp_drm_handle, "drmModeGetFB");
if (p_drmModeGetFB == NULL)
{
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeGetFB", "DRM_LIBRARY");
return false;
}
p_drmModeFreeFB = (__typeof__(drmModeFreeFB)) dlsym(mp_drm_handle, "drmModeFreeFB");
if (p_drmModeFreeFB == NULL)
{
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeFreeFB", "DRM_LIBRARY");
return false;
}
p_drmPrimeHandleToFD = (__typeof__(drmPrimeHandleToFD)) dlsym(mp_drm_handle, "drmPrimeHandleToFD");
if (p_drmPrimeHandleToFD == NULL)
{
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmPrimeHandleToFD", "DRM_LIBRARY");
return false;
}
p_drmModeGetPlaneResources = (__typeof__(drmModeGetPlaneResources)) dlsym(mp_drm_handle, "drmModeGetPlaneResources");
if (p_drmModeGetPlaneResources == NULL)
{
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeGetPlaneResources", "DRM_LIBRARY");
return false;
}
p_drmModeFreePlaneResources = (__typeof__(drmModeFreePlaneResources)) dlsym(mp_drm_handle, "drmModeFreePlaneResources");
if (p_drmModeFreePlaneResources == NULL)
{
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeFreePlaneResources", "DRM_LIBRARY");
return false;
}
p_drmIoctl = (__typeof__(drmIoctl)) dlsym(mp_drm_handle, "drmIoctl");
if (p_drmIoctl == NULL)
{
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmIoctl", "DRM_LIBRARY");
return false;
}
p_drmGetCap = (__typeof__(drmGetCap)) dlsym(mp_drm_handle, "drmGetCap");
if (p_drmGetCap == NULL)
{
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmGetCap", "DRM_LIBRARY");
return false;
}
p_drmIsMaster = (__typeof__(drmIsMaster)) dlsym(mp_drm_handle, "drmIsMaster");
if (p_drmIsMaster == NULL)
{
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmIsMaster", "DRM_LIBRARY");
return false;
}
p_drmSetMaster = (__typeof__(drmSetMaster)) dlsym(mp_drm_handle, "drmSetMaster");
if (p_drmSetMaster == NULL)
{
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmSetMaster", "DRM_LIBRARY");
return false;
}
p_drmDropMaster = (__typeof__(drmDropMaster)) dlsym(mp_drm_handle, "drmDropMaster");
if (p_drmDropMaster == NULL)
{
log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmDropMaster", "DRM_LIBRARY");
return false;
}
}
else
{
log_error("DRM/KMS: <%d> (init) [ERROR] missing %s library\n", m_id, "DRM/KMS_LIBRARY");
return false;
}
int screen_pos = -1;
// Handle the screen name, "auto", "screen[0-9]" and device name
if (strlen(m_device_name) == 7 && !strncmp(m_device_name, "screen", 6) && m_device_name[6] >= '0' && m_device_name[6] <= '9')
screen_pos = m_device_name[6] - '0';
else if (strlen(m_device_name) == 1 && m_device_name[0] >= '0' && m_device_name[0] <= '9')
screen_pos = m_device_name[0] - '0';
char drm_name[15] = "/dev/dri/card_";
drmModeRes *p_res;
drmModeConnector *p_connector;
int output_position = 0;
for (int num = 0; !m_desktop_output && num < 10; num++)
{
drm_name[13] = '0' + num;
m_drm_fd = open(drm_name, O_RDWR | O_CLOEXEC);
if (m_drm_fd > 0)
{
drmVersion *version = drmGetVersion(m_drm_fd);
log_verbose("DRM/KMS: <%d> (init) version %d.%d.%d type %s\n", m_id, version->version_major, version->version_minor, version->version_patchlevel, version->name);
drmFreeVersion(version);
uint64_t check_dumb = 0;
if (drmGetCap(m_drm_fd, DRM_CAP_DUMB_BUFFER, &check_dumb) < 0)
log_error("DRM/KMS: <%d> (init) [ERROR] ioctl DRM_CAP_DUMB_BUFFER\n", m_id);
if (!check_dumb)
log_error("DRM/KMS: <%d> (init) [ERROR] dumb buffer not supported\n", m_id);
p_res = drmModeGetResources(m_drm_fd);
for (int i = 0; i < p_res->count_connectors; i++)
{
p_connector = drmModeGetConnector(m_drm_fd, p_res->connectors[i]);
if (p_connector)
{
char connector_name[32];
snprintf(connector_name, 32, "%s%d", get_connector_name(p_connector->connector_type), p_connector->connector_type_id);
log_verbose("DRM/KMS: <%d> (init) card %d connector %d id %d name %s status %d - modes %d\n", m_id, num, i, p_connector->connector_id, connector_name, p_connector->connection, p_connector->count_modes);
// detect desktop connector
if (!m_desktop_output && p_connector->connection == DRM_MODE_CONNECTED)
{
if (!strcmp(m_device_name, "auto") || !strcmp(m_device_name, connector_name) || output_position == screen_pos)
{
m_desktop_output = p_connector->connector_id;
m_card_id = num;
log_verbose("DRM/KMS: <%d> (init) card %d connector %d id %d name %s selected as primary output\n", m_id, num, i, m_desktop_output, connector_name);
drmModeEncoder *p_encoder = drmModeGetEncoder(m_drm_fd, p_connector->encoder_id);
if (p_encoder)
{
for (int e = 0; e < p_res->count_crtcs; e++)
{
mp_crtc_desktop = drmModeGetCrtc(m_drm_fd, p_res->crtcs[e]);
if (mp_crtc_desktop->crtc_id == p_encoder->crtc_id)
{
log_verbose("DRM/KMS: <%d> (init) desktop mode name %s crtc %d fb %d valid %d\n", m_id, mp_crtc_desktop->mode.name, mp_crtc_desktop->crtc_id, mp_crtc_desktop->buffer_id, mp_crtc_desktop->mode_valid);
break;
}
drmModeFreeCrtc(mp_crtc_desktop);
}
}
if (!mp_crtc_desktop)
log_error("DRM/KMS: <%d> (init) [ERROR] no crtc found\n", m_id);
drmModeFreeEncoder(p_encoder);
}
output_position++;
}
drmModeFreeConnector(p_connector);
}
else
log_error("DRM/KMS: <%d> (init) [ERROR] card %d connector %d - %d\n", m_id, num, i, p_res->connectors[i]);
}
drmModeFreeResources(p_res);
if (!m_desktop_output)
close(m_drm_fd);
else
{
if (drmIsMaster(m_drm_fd))
{
s_shared_fd[m_card_id] = m_drm_fd;
s_shared_count[m_card_id] = 1;
drmDropMaster(m_drm_fd);
}
else
{
if (s_shared_count[m_card_id] > 0)
{
close(m_drm_fd);
m_drm_fd = s_shared_fd[m_card_id];
s_shared_count[m_card_id]++;
}
else if (m_id == 1)
{
log_verbose("DRM/KMS: <%d> (init) looking for the DRM master\n", m_id);
int fd = drm_master_hook(m_drm_fd);
if (fd)
{
close(m_drm_fd);
m_drm_fd = fd;
s_shared_fd[m_card_id] = m_drm_fd;
// start at 2 to disable closing the fd
s_shared_count[m_card_id] = 2;
}
}
}
if (!drmIsMaster(m_drm_fd))
log_error("DRM/KMS: <%d> (init) [ERROR] limited DRM rights on this screen\n", m_id);
}
}
else
{
if (!num)
log_error("DRM/KMS: <%d> (init) [ERROR] cannot open device %s\n", m_id, drm_name);
break;
}
}
// Handle no screen detected case
if (!m_desktop_output)
{
log_error("DRM/KMS: <%d> (init) [ERROR] no screen detected\n", m_id);
return false;
}
else
{
}
return true;
}
//============================================================
// drmkms_timing::drm_master_hook
//============================================================
int drmkms_timing::drm_master_hook(int last_fd)
{
for (int fd = 4; fd < last_fd; fd++)
{
struct stat st;
if (!fstat(fd, &st))
{
// in case of multiple video cards, it wouldd be better to compare dri number
if (S_ISCHR(st.st_mode))
{
if (drmIsMaster(fd))
{
drmVersion *version_hook = drmGetVersion(m_drm_fd);
log_verbose("DRM/KMS: <%d> (init) DRM hook created version %d.%d.%d type %s\n", m_id, version_hook->version_major, version_hook->version_minor, version_hook->version_patchlevel, version_hook->name);
drmFreeVersion(version_hook);
return fd;
}
}
}
}
return 0;
}
//============================================================
// drmkms_timing::update_mode
//============================================================
bool drmkms_timing::update_mode(modeline *mode)
{
if (!mode)
return false;
if (!m_desktop_output)
{
log_error("DRM/KMS: <%d> (update_mode) [ERROR] no screen detected\n", m_id);
return false;
}
if (!delete_mode(mode))
{
log_error("DRM/KMS: <%d> (update_mode) [ERROR] delete operation not successful", m_id);
return false;
}
if (!add_mode(mode))
{
log_error("DRM/KMS: <%d> (update_mode) [ERROR] add operation not successful", m_id);
return false;
}
return true;
}
//============================================================
// drmkms_timing::add_mode
//============================================================
bool drmkms_timing::add_mode(modeline *mode)
{
if (!mode)
return false;
// Handle no screen detected case
if (!m_desktop_output)
{
log_error("DRM/KMS: <%d> (add_mode) [ERROR] no screen detected\n", m_id);
return false;
}
if (!mp_crtc_desktop)
{
log_error("DRM/KMS: <%d> (add_mode) [ERROR] no desktop crtc\n", m_id);
return false;
}
if (!mode)
return false;
return true;
}
//============================================================
// drmkms_timing::set_timing
//============================================================
bool drmkms_timing::set_timing(modeline *mode)
{
if (!mode)
return false;
// Handle no screen detected case
if (!m_desktop_output)
{
log_error("DRM/KMS: <%d> (set_timing) [ERROR] no screen detected\n", m_id);
return false;
}
drmSetMaster(m_drm_fd);
// Setup the DRM mode structure
drmModeModeInfo dmode = {};
// Create specific mode name
snprintf(dmode.name, 32, "SR-%d_%dx%d@%.02f%s", m_id, mode->hactive, mode->vactive, mode->vfreq, mode->interlace ? "i" : "");
dmode.clock = mode->pclock / 1000;
dmode.hdisplay = mode->hactive;
dmode.hsync_start = mode->hbegin;
dmode.hsync_end = mode->hend;
dmode.htotal = mode->htotal;
dmode.vdisplay = mode->vactive;
dmode.vsync_start = mode->vbegin;
dmode.vsync_end = mode->vend;
dmode.vtotal = mode->vtotal;
dmode.flags = (mode->interlace ? DRM_MODE_FLAG_INTERLACE : 0) | (mode->doublescan ? DRM_MODE_FLAG_DBLSCAN : 0) | (mode->hsync ? DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC) | (mode->vsync ? DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC);
dmode.hskew = 0;
dmode.vscan = 0;
dmode.vrefresh = mode->refresh; // Used only for human readable output
dmode.type = DRM_MODE_TYPE_USERDEF; //DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
mode->type |= CUSTOM_VIDEO_TIMING_DRMKMS;
if (mode->platform_data == 4815162342)
{
log_verbose("DRM/KMS: <%d> (set_timing) <debug> restore desktop mode\n", m_id);
drmModeSetCrtc(m_drm_fd, mp_crtc_desktop->crtc_id, mp_crtc_desktop->buffer_id, mp_crtc_desktop->x, mp_crtc_desktop->y, &m_desktop_output, 1, &mp_crtc_desktop->mode);
if (m_dumb_handle)
{
int ret = ioctl(m_drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &m_dumb_handle);
if (ret)
log_verbose("DRM/KMS: <%d> (add_mode) [ERROR] ioctl DRM_IOCTL_MODE_DESTROY_DUMB %d\n", m_id, ret);
m_dumb_handle = 0;
}
if (m_framebuffer_id && m_framebuffer_id != mp_crtc_desktop->buffer_id)
{
if (drmModeRmFB(m_drm_fd, m_framebuffer_id))
log_verbose("DRM/KMS: <%d> (add_mode) [ERROR] remove frame buffer\n", m_id);
m_framebuffer_id = 0;
}
}
else
{
unsigned int old_dumb_handle = m_dumb_handle;
drmModeFB *pframebuffer = drmModeGetFB(m_drm_fd, mp_crtc_desktop->buffer_id);
log_verbose("DRM/KMS: <%d> (add_mode) <debug> existing frame buffer id %d size %dx%d bpp %d\n", m_id, mp_crtc_desktop->buffer_id, pframebuffer->width, pframebuffer->height, pframebuffer->bpp);
//drmModePlaneRes *pplanes = drmModeGetPlaneResources(m_drm_fd);
//log_verbose("DRM/KMS: <%d> (add_mode) <debug> total planes %d\n", m_id, pplanes->count_planes);
//drmModeFreePlaneResources(pplanes);
unsigned int framebuffer_id = mp_crtc_desktop->buffer_id;
//if (pframebuffer->width < dmode.hdisplay || pframebuffer->height < dmode.vdisplay)
if (1)
{
log_verbose("DRM/KMS: <%d> (add_mode) <debug> creating new frame buffer with size %dx%d\n", m_id, dmode.hdisplay, dmode.vdisplay);
// create a new dumb fb (not driver specefic)
drm_mode_create_dumb create_dumb = {};
create_dumb.width = dmode.hdisplay;
create_dumb.height = dmode.vdisplay;
create_dumb.bpp = pframebuffer->bpp;
int ret = ioctl(m_drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb);
if (ret)
log_verbose("DRM/KMS: <%d> (add_mode) [ERROR] ioctl DRM_IOCTL_MODE_CREATE_DUMB %d\n", m_id, ret);
if (drmModeAddFB(m_drm_fd, dmode.hdisplay, dmode.vdisplay, pframebuffer->depth, pframebuffer->bpp, create_dumb.pitch, create_dumb.handle, &framebuffer_id))
log_error("DRM/KMS: <%d> (add_mode) [ERROR] cannot add frame buffer\n", m_id);
else
m_dumb_handle = create_dumb.handle;
drm_mode_map_dumb map_dumb = {};
map_dumb.handle = create_dumb.handle;
ret = drmIoctl(m_drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb);
if (ret)
log_verbose("DRM/KMS: <%d> (add_mode) [ERROR] ioctl DRM_IOCTL_MODE_MAP_DUMB %d\n", m_id, ret);
void *map = mmap(0, create_dumb.size, PROT_READ | PROT_WRITE, MAP_SHARED, m_drm_fd, map_dumb.offset);
if (map != MAP_FAILED)
{
// clear the frame buffer
memset(map, 0, create_dumb.size);
}
else
log_verbose("DRM/KMS: <%d> (add_mode) [ERROR] failed to map frame buffer %p\n", m_id, map);
}
else
log_verbose("DRM/KMS: <%d> (add_mode) <debug> use existing frame buffer\n", m_id);
drmModeFreeFB(pframebuffer);
pframebuffer = drmModeGetFB(m_drm_fd, framebuffer_id);
log_verbose("DRM/KMS: <%d> (add_mode) <debug> frame buffer id %d size %dx%d bpp %d\n", m_id, framebuffer_id, pframebuffer->width, pframebuffer->height, pframebuffer->bpp);
drmModeFreeFB(pframebuffer);
// set the mode on the crtc
if (drmModeSetCrtc(m_drm_fd, mp_crtc_desktop->crtc_id, framebuffer_id, 0, 0, &m_desktop_output, 1, &dmode))
log_error("DRM/KMS: <%d> (add_mode) [ERROR] cannot attach the mode to the crtc %d frame buffer %d\n", m_id, mp_crtc_desktop->crtc_id, framebuffer_id);
else
{
if (old_dumb_handle)
{
log_verbose("DRM/KMS: <%d> (add_mode) <debug> remove old dumb %d\n", m_id, old_dumb_handle);
int ret = ioctl(m_drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &old_dumb_handle);
if (ret)
log_verbose("DRM/KMS: <%d> (add_mode) [ERROR] ioctl DRM_IOCTL_MODE_DESTROY_DUMB %d\n", m_id, ret);
old_dumb_handle = 0;
}
if (m_framebuffer_id && framebuffer_id != mp_crtc_desktop->buffer_id)
{
log_verbose("DRM/KMS: <%d> (add_mode) <debug> remove old frame buffer %d\n", m_id, m_framebuffer_id);
drmModeRmFB(m_drm_fd, m_framebuffer_id);
m_framebuffer_id = 0;
}
m_framebuffer_id = framebuffer_id;
}
}
drmDropMaster(m_drm_fd);
return true;
}
//============================================================
// drmkms_timing::delete_mode
//============================================================
bool drmkms_timing::delete_mode(modeline *mode)
{
if (!mode)
return false;
// Handle no screen detected case
if (!m_desktop_output)
{
log_error("DRM/KMS: <%d> (delete_mode) [ERROR] no screen detected\n", m_id);
return false;
}
return true;
}
//============================================================
// drmkms_timing::get_timing
//============================================================
bool drmkms_timing::get_timing(modeline *mode)
{
// Handle no screen detected case
if (!m_desktop_output)
{
log_error("DRM/KMS: <%d> (get_timing) [ERROR] no screen detected\n", m_id);
return false;
}
// INFO: not used vrefresh, hskew, vscan
drmModeRes *p_res = drmModeGetResources(m_drm_fd);
for (int i = 0; i < p_res->count_connectors; i++)
{
drmModeConnector *p_connector = drmModeGetConnector(m_drm_fd, p_res->connectors[i]);
// Cycle through the modelines and report them back to the display manager
if (p_connector && m_desktop_output == p_connector->connector_id)
{
if (m_video_modes_position < p_connector->count_modes)
{
drmModeModeInfo *pdmode = &p_connector->modes[m_video_modes_position++];
// Use mode position as index
mode->platform_data = m_video_modes_position;
mode->pclock = pdmode->clock * 1000;
mode->hactive = pdmode->hdisplay;
mode->hbegin = pdmode->hsync_start;
mode->hend = pdmode->hsync_end;
mode->htotal = pdmode->htotal;
mode->vactive = pdmode->vdisplay;
mode->vbegin = pdmode->vsync_start;
mode->vend = pdmode->vsync_end;
mode->vtotal = pdmode->vtotal;
mode->interlace = (pdmode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0;
mode->doublescan = (pdmode->flags & DRM_MODE_FLAG_DBLSCAN) ? 1 : 0;
mode->hsync = (pdmode->flags & DRM_MODE_FLAG_PHSYNC) ? 1 : 0;
mode->vsync = (pdmode->flags & DRM_MODE_FLAG_PVSYNC) ? 1 : 0;
mode->hfreq = mode->pclock / mode->htotal;
mode->vfreq = mode->hfreq / mode->vtotal * (mode->interlace ? 2 : 1);
mode->refresh = mode->vfreq;
mode->width = pdmode->hdisplay;
mode->height = pdmode->vdisplay;
// Add the rotation flag from the plane (DRM_MODE_ROTATE_xxx)
// TODO: mode->type |= MODE_ROTATED;
mode->type |= CUSTOM_VIDEO_TIMING_DRMKMS;
if (strncmp(pdmode->name, "SR-", 3) == 0)
log_verbose("DRM/KMS: <%d> (get_timing) [WARNING] modeline %s detected\n", m_id, pdmode->name);
else if (!strcmp(pdmode->name, mp_crtc_desktop->mode.name) && pdmode->clock == mp_crtc_desktop->mode.clock && pdmode->vrefresh == mp_crtc_desktop->mode.vrefresh)
{
// Add the desktop flag to desktop modeline
log_verbose("DRM/KMS: <%d> (get_timing) desktop mode name %s refresh %d found\n", m_id, mp_crtc_desktop->mode.name, mp_crtc_desktop->mode.vrefresh);
mode->type |= MODE_DESKTOP;
mode->platform_data = 4815162342;
}
}
else
{
// Inititalise the position for the modeline list
m_video_modes_position = 0;
}
}
drmModeFreeConnector(p_connector);
}
drmModeFreeResources(p_res);
return true;
}

81
deps/switchres/custom_video_drmkms.h vendored Normal file
View File

@ -0,0 +1,81 @@
/**************************************************************
custom_video_drmkms.h - Linux DRM/KMS video management layer
---------------------------------------------------------
Switchres Modeline generation engine for emulation
License GPL-2.0+
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
Alexandre Wodarczyk, Gil Delescluse
**************************************************************/
#ifndef __CUSTOM_VIDEO_DRMKMS_
#define __CUSTOM_VIDEO_DRMKMS_
// DRM headers
#include <xf86drm.h>
#include <xf86drmMode.h>
#include "custom_video.h"
class drmkms_timing : public custom_video
{
public:
drmkms_timing(char *device_name, custom_video_settings *vs);
~drmkms_timing();
const char *api_name() { return "DRMKMS"; }
int caps() { return CUSTOM_VIDEO_CAPS_ADD; }
bool init();
bool add_mode(modeline *mode);
bool delete_mode(modeline *mode);
bool update_mode(modeline *mode);
bool get_timing(modeline *mode);
bool set_timing(modeline *mode);
private:
int m_id = 0;
int m_drm_fd = 0;
drmModeCrtc *mp_crtc_desktop = NULL;
int m_card_id = 0;
int drm_master_hook(int fd);
char m_device_name[32];
unsigned int m_desktop_output = 0;
int m_video_modes_position = 0;
void *mp_drm_handle = NULL;
unsigned int m_dumb_handle = 0;
unsigned int m_framebuffer_id = 0;
__typeof__(drmGetVersion) *p_drmGetVersion;
__typeof__(drmFreeVersion) *p_drmFreeVersion;
__typeof__(drmModeGetResources) *p_drmModeGetResources;
__typeof__(drmModeGetConnector) *p_drmModeGetConnector;
__typeof__(drmModeFreeConnector) *p_drmModeFreeConnector;
__typeof__(drmModeFreeResources) *p_drmModeFreeResources;
__typeof__(drmModeGetEncoder) *p_drmModeGetEncoder;
__typeof__(drmModeFreeEncoder) *p_drmModeFreeEncoder;
__typeof__(drmModeGetCrtc) *p_drmModeGetCrtc;
__typeof__(drmModeSetCrtc) *p_drmModeSetCrtc;
__typeof__(drmModeFreeCrtc) *p_drmModeFreeCrtc;
__typeof__(drmModeAttachMode) *p_drmModeAttachMode;
__typeof__(drmModeAddFB) *p_drmModeAddFB;
__typeof__(drmModeRmFB) *p_drmModeRmFB;
__typeof__(drmModeGetFB) *p_drmModeGetFB;
__typeof__(drmModeFreeFB) *p_drmModeFreeFB;
__typeof__(drmPrimeHandleToFD) *p_drmPrimeHandleToFD;
__typeof__(drmModeGetPlaneResources) *p_drmModeGetPlaneResources;
__typeof__(drmModeFreePlaneResources) *p_drmModeFreePlaneResources;
__typeof__(drmIoctl) *p_drmIoctl;
__typeof__(drmGetCap) *p_drmGetCap;
__typeof__(drmIsMaster) *p_drmIsMaster;
__typeof__(drmSetMaster) *p_drmSetMaster;
__typeof__(drmDropMaster) *p_drmDropMaster;
};
#endif

628
deps/switchres/custom_video_pstrip.cpp vendored Normal file
View File

@ -0,0 +1,628 @@
/**************************************************************
custom_video_pstrip.cpp - PowerStrip interface routines
---------------------------------------------------------
Switchres Modeline generation engine for emulation
License GPL-2.0+
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
Alexandre Wodarczyk, Gil Delescluse
**************************************************************/
/* http://forums.entechtaiwan.com/index.php?topic=5534.msg20902;topicseen#msg20902
UM_SETCUSTOMTIMING = WM_USER+200;
wparam = monitor number, zero-based
lparam = atom for string pointer
lresult = -1 for failure else current pixel clock (integer in Hz)
Note: pass full PowerStrip timing string*
UM_SETREFRESHRATE = WM_USER+201;
wparam = monitor number, zero-based
lparam = refresh rate (integer in Hz), or 0 for read-only
lresult = -1 for failure else current refresh rate (integer in Hz)
UM_SETPOLARITY = WM_USER+202;
wparam = monitor number, zero-based
lparam = polarity bits
lresult = -1 for failure else current polarity bits+1
UM_REMOTECONTROL = WM_USER+210;
wparam = 99
lparam =
0 to hide tray icon
1 to show tray icon,
2 to get build number
10 to show Performance profiles
11 to show Color profiles
12 to show Display profiles
13 to show Application profiles
14 to show Adapter information
15 to show Monitor information
16 to show Hotkey manager
17 to show Resource manager
18 to show Preferences
19 to show Online services
20 to show About screen
21 to show Tip-of-the-day
22 to show Setup wizard
23 to show Screen fonts
24 to show Advanced timing options
25 to show Custom resolutions
99 to close PS
lresult = -1 for failure else lparam+1 for success or build number (e.g., 335)
if lparam was 2
UM_SETGAMMARAMP = WM_USER+203;
wparam = monitor number, zero-based
lparam = atom for string pointer
lresult = -1 for failure, 1 for success
UM_CREATERESOLUTION = WM_USER+204;
wparam = monitor number, zero-based
lparam = atom for string pointer
lresult = -1 for failure, 1 for success
Note: pass full PowerStrip timing string*; reboot is usually necessary to see if
the resolution is accepted by the display driver
UM_GETTIMING = WM_USER+205;
wparam = monitor number, zero-based
lresult = -1 for failure else GlobalAtom number identifiying the timing string*
Note: be sure to call GlobalDeleteAtom after reading the string associated with
the atom
UM_GETSETCLOCKS = WM_USER+206;
wparam = monitor number, zero-based
lparam = atom for string pointer
lresult = -1 for failure else GlobalAtom number identifiying the performance
string**
Note: pass full PowerStrip performance string** to set the clocks, and ull to
get clocks; be sure to call GlobalDeleteAtom after reading the string associated
with the atom
NegativeHorizontalPolarity = 0x02;
NegativeVerticalPolarity = 0x04;
*Timing string parameter definition:
1 = horizontal active pixels
2 = horizontal front porch
3 = horizontal sync width
4 = horizontal back porch
5 = vertical active pixels
6 = vertical front porch
7 = vertical sync width
8 = vertical back porch
9 = pixel clock in hertz
10 = timing flags, where bit:
1 = negative horizontal porlarity
2 = negative vertical polarity
3 = interlaced
5 = composite sync
7 = sync-on-green
all other bits reserved
**Performance string parameter definition:
1 = memory clock in hertz
2 = engine clock in hertz
3 = reserved
4 = reserved
5 = reserved
6 = reserved
7 = reserved
8 = reserved
9 = 2D memory clock in hertz (if different from 3D)
10 = 2D engine clock in hertz (if different from 3D) */
#include <windows.h>
#include <stdio.h>
#include "custom_video_pstrip.h"
#include "log.h"
//============================================================
// CONSTANTS
//============================================================
#define UM_SETCUSTOMTIMING (WM_USER+200)
#define UM_SETREFRESHRATE (WM_USER+201)
#define UM_SETPOLARITY (WM_USER+202)
#define UM_REMOTECONTROL (WM_USER+210)
#define UM_SETGAMMARAMP (WM_USER+203)
#define UM_CREATERESOLUTION (WM_USER+204)
#define UM_GETTIMING (WM_USER+205)
#define UM_GETSETCLOCKS (WM_USER+206)
#define UM_SETCUSTOMTIMINGFAST (WM_USER+211) // glitches vertical sync with PS 3.65 build 568
#define NegativeHorizontalPolarity 0x02
#define NegativeVerticalPolarity 0x04
#define Interlace 0x08
#define HideTrayIcon 0x00
#define ShowTrayIcon 0x01
#define ClosePowerStrip 0x63
//============================================================
// pstrip_timing::pstrip_timing
//============================================================
pstrip_timing::pstrip_timing(char *device_name, custom_video_settings *vs)
{
m_vs = *vs;
strcpy (m_device_name, device_name);
strcpy (m_ps_timing, m_vs.custom_timing);
}
//============================================================
// pstrip_timing::~pstrip_timing()
//============================================================
pstrip_timing::~pstrip_timing()
{
ps_reset();
}
//============================================================
// pstrip_timing::init
//============================================================
bool pstrip_timing::init()
{
m_monitor_index = ps_monitor_index(m_device_name);
hPSWnd = FindWindowA("TPShidden", NULL);
if (hPSWnd)
{
log_verbose("PStrip: PowerStrip found!\n");
// Save current settings
ps_get_monitor_timing(&m_timing_backup);
// If we have a -ps_timing string defined, use it as user defined modeline
if (strcmp(m_ps_timing, "auto"))
{
MonitorTiming timing;
if (ps_read_timing_string(m_ps_timing, &timing))
{
ps_pstiming_to_modeline(&timing, &m_user_mode);
m_user_mode.type |= CUSTOM_VIDEO_TIMING_POWERSTRIP;
char modeline_txt[256]={'\x00'};
log_verbose("SwitchRes: ps_string: %s (%s)\n", m_ps_timing, modeline_print(&m_user_mode, modeline_txt, MS_PARAMS));
}
else
log_verbose("Switchres: ps_timing string with invalid format\n");
}
}
else
{
log_verbose("PStrip: Could not get PowerStrip API interface\n");
return false;
}
return true;
}
//============================================================
// pstrip_timing::get_timing
//============================================================
bool pstrip_timing::get_timing(modeline *mode)
{
// If we have an user defined mode (ps_timing), lock any non matching mode
if (m_user_mode.hactive)
{
if (mode->width != m_user_mode.width || mode->height != m_user_mode.height)
{
mode->type |= MODE_DISABLED;
return false;
}
}
modeline m_temp = {};
if (ps_get_modeline(&m_temp))
{
// We can only get the timings of the current desktop mode, so filter out anything different
if (m_temp.width == mode->width && m_temp.height == mode->height && m_temp.refresh == mode->refresh)
{
*mode = m_temp;
}
mode->type |= CUSTOM_VIDEO_TIMING_POWERSTRIP;
return true;
}
return false;
}
//============================================================
// pstrip_timing::set_timing
//============================================================
bool pstrip_timing::set_timing(modeline *mode)
{
// In case -ps_timing is provided, pass it as raw string
if (m_user_mode.hactive)
ps_set_monitor_timing_string(m_ps_timing);
// Otherwise pass it as modeline
else
ps_set_modeline(mode);
Sleep(100);
return true;
}
//============================================================
// pstrip_timing::ps_reset
//============================================================
int pstrip_timing::ps_reset()
{
return ps_set_monitor_timing(&m_timing_backup);
}
//============================================================
// ps_get_modeline
//============================================================
int pstrip_timing::ps_get_modeline(modeline *modeline)
{
MonitorTiming timing = {};
if (ps_get_monitor_timing(&timing))
{
ps_pstiming_to_modeline(&timing, modeline);
return 1;
}
else return 0;
}
//============================================================
// pstrip_timing::ps_set_modeline
//============================================================
bool pstrip_timing::ps_set_modeline(modeline *modeline)
{
MonitorTiming timing = {};
if (!ps_modeline_to_pstiming(modeline, &timing))
return false;
timing.PixelClockInKiloHertz = ps_best_pclock(&timing, timing.PixelClockInKiloHertz);
if (ps_set_monitor_timing(&timing))
return true;
else
return false;
}
//============================================================
// pstrip_timing::ps_get_monitor_timing
//============================================================
int pstrip_timing::ps_get_monitor_timing(MonitorTiming *timing)
{
LRESULT lresult;
char in[256];
if (!hPSWnd) return 0;
lresult = SendMessage(hPSWnd, UM_GETTIMING, m_monitor_index, 0);
if (lresult == -1)
{
log_verbose("PStrip: Could not get PowerStrip timing string\n");
return 0;
}
if (!GlobalGetAtomNameA(lresult, in, sizeof(in)))
{
log_verbose("PStrip: GlobalGetAtomName failed\n");
return 0;
}
log_verbose("PStrip: ps_get_monitor_timing(%d): %s\n", m_monitor_index, in);
ps_read_timing_string(in, timing);
GlobalDeleteAtom(lresult); // delete atom created by PowerStrip
return 1;
}
//============================================================
// pstrip_timing::ps_set_monitor_timing
//============================================================
int pstrip_timing::ps_set_monitor_timing(MonitorTiming *timing)
{
LRESULT lresult;
ATOM atom;
char out[256];
if (!hPSWnd) return 0;
ps_fill_timing_string(out, timing);
atom = GlobalAddAtomA(out);
if (atom)
{
lresult = SendMessage(hPSWnd, UM_SETCUSTOMTIMING, m_monitor_index, atom);
if (lresult < 0)
{
log_verbose("PStrip: SendMessage failed\n");
GlobalDeleteAtom(atom);
}
else
{
log_verbose("PStrip: ps_set_monitor_timing(%d): %s\n", m_monitor_index, out);
return 1;
}
}
else log_verbose("PStrip: ps_set_monitor_timing atom creation failed\n");
return 0;
}
//============================================================
// pstrip_timing::ps_set_monitor_timing_string
//============================================================
int pstrip_timing::ps_set_monitor_timing_string(char *in)
{
MonitorTiming timing = {};
ps_read_timing_string(in, &timing);
return ps_set_monitor_timing(&timing);
}
//============================================================
// pstrip_timing::ps_set_refresh
//============================================================
int pstrip_timing::ps_set_refresh(double vfreq)
{
MonitorTiming timing = {};
int hht, vvt, new_vvt;
int desired_pClock;
int best_pClock;
memcpy(&timing, &m_timing_backup, sizeof(MonitorTiming));
hht = timing.HorizontalActivePixels
+ timing.HorizontalFrontPorch
+ timing.HorizontalSyncWidth
+ timing.HorizontalBackPorch;
vvt = timing.VerticalActivePixels
+ timing.VerticalFrontPorch
+ timing.VerticalSyncWidth
+ timing.VerticalBackPorch;
desired_pClock = hht * vvt * vfreq / 1000;
best_pClock = ps_best_pclock(&timing, desired_pClock);
new_vvt = best_pClock * 1000 / (vfreq * hht);
timing.VerticalBackPorch += (new_vvt - vvt);
timing.PixelClockInKiloHertz = best_pClock;
ps_set_monitor_timing(&timing);
ps_get_monitor_timing(&timing);
return 1;
}
//============================================================
// pstrip_timing::ps_best_pclock
//============================================================
int pstrip_timing::ps_best_pclock(MonitorTiming *timing, int desired_pclock)
{
MonitorTiming timing_read = {};
int best_pclock = 0;
log_verbose("PStrip: ps_best_pclock(%d), getting stable dotclocks for %d...\n", m_monitor_index, desired_pclock);
for (int i = -50; i <= 50; i += 25)
{
timing->PixelClockInKiloHertz = desired_pclock + i;
ps_set_monitor_timing(timing);
ps_get_monitor_timing(&timing_read);
if (abs(timing_read.PixelClockInKiloHertz - desired_pclock) < abs(desired_pclock - best_pclock))
best_pclock = timing_read.PixelClockInKiloHertz;
}
log_verbose("PStrip: ps_best_pclock(%d), new dotclock: %d\n", m_monitor_index, best_pclock);
return best_pclock;
}
//============================================================
// pstrip_timing::ps_create_resolution
//============================================================
int pstrip_timing::ps_create_resolution(modeline *modeline)
{
LRESULT lresult;
ATOM atom;
char out[256];
MonitorTiming timing = {};
if (!hPSWnd) return 0;
ps_modeline_to_pstiming(modeline, &timing);
ps_fill_timing_string(out, &timing);
atom = GlobalAddAtomA(out);
if (atom)
{
lresult = SendMessage(hPSWnd, UM_CREATERESOLUTION, m_monitor_index, atom);
if (lresult < 0)
{
log_verbose("PStrip: SendMessage failed\n");
GlobalDeleteAtom(atom);
}
else
{
log_verbose("PStrip: ps_create_resolution(%d): %dx%d succeded \n",
modeline->width, modeline->height, m_monitor_index);
return 1;
}
}
else log_verbose("PStrip: ps_create_resolution atom creation failed\n");
return 0;
}
//============================================================
// pstrip_timing::ps_read_timing_string
//============================================================
bool pstrip_timing::ps_read_timing_string(char *in, MonitorTiming *timing)
{
if (sscanf(in,"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
&timing->HorizontalActivePixels,
&timing->HorizontalFrontPorch,
&timing->HorizontalSyncWidth,
&timing->HorizontalBackPorch,
&timing->VerticalActivePixels,
&timing->VerticalFrontPorch,
&timing->VerticalSyncWidth,
&timing->VerticalBackPorch,
&timing->PixelClockInKiloHertz,
&timing->TimingFlags.w) == 10) return true;
return false;
}
//============================================================
// pstrip_timing::ps_fill_timing_string
//============================================================
void pstrip_timing::ps_fill_timing_string(char *out, MonitorTiming *timing)
{
sprintf(out, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
timing->HorizontalActivePixels,
timing->HorizontalFrontPorch,
timing->HorizontalSyncWidth,
timing->HorizontalBackPorch,
timing->VerticalActivePixels,
timing->VerticalFrontPorch,
timing->VerticalSyncWidth,
timing->VerticalBackPorch,
timing->PixelClockInKiloHertz,
timing->TimingFlags.w);
}
//============================================================
// pstrip_timing::ps_modeline_to_pstiming
//============================================================
bool pstrip_timing::ps_modeline_to_pstiming(modeline *modeline, MonitorTiming *timing)
{
if (modeline->pclock == 0 || modeline->hactive == 0 || modeline->vactive == 0)
{
log_verbose("ps_modeline_to_pstiming error: invalid modeline\n");
return false;
}
timing->HorizontalActivePixels = modeline->hactive;
timing->HorizontalFrontPorch = modeline->hbegin - modeline->hactive;
timing->HorizontalSyncWidth = modeline->hend - modeline->hbegin;
timing->HorizontalBackPorch = modeline->htotal - modeline->hend;
timing->VerticalActivePixels = modeline->vactive;
timing->VerticalFrontPorch = modeline->vbegin - modeline->vactive;
timing->VerticalSyncWidth = modeline->vend - modeline->vbegin;
timing->VerticalBackPorch = modeline->vtotal - modeline->vend;
timing->PixelClockInKiloHertz = modeline->pclock / 1000;
if (modeline->hsync == 0)
timing->TimingFlags.w |= NegativeHorizontalPolarity;
if (modeline->vsync == 0)
timing->TimingFlags.w |= NegativeVerticalPolarity;
if (modeline->interlace)
timing->TimingFlags.w |= Interlace;
return true;
}
//============================================================
// pstrip_timing::ps_pstiming_to_modeline
//============================================================
int pstrip_timing::ps_pstiming_to_modeline(MonitorTiming *timing, modeline *modeline)
{
modeline->hactive = timing->HorizontalActivePixels;
modeline->hbegin = modeline->hactive + timing->HorizontalFrontPorch;
modeline->hend = modeline->hbegin + timing->HorizontalSyncWidth;
modeline->htotal = modeline->hend + timing->HorizontalBackPorch;
modeline->vactive = timing->VerticalActivePixels;
modeline->vbegin = modeline->vactive + timing->VerticalFrontPorch;
modeline->vend = modeline->vbegin + timing->VerticalSyncWidth;
modeline->vtotal = modeline->vend + timing->VerticalBackPorch;
modeline->width = modeline->hactive;
modeline->height = modeline->vactive;
modeline->pclock = timing->PixelClockInKiloHertz * 1000;
if (!(timing->TimingFlags.w & NegativeHorizontalPolarity))
modeline->hsync = 1;
if (!(timing->TimingFlags.w & NegativeVerticalPolarity))
modeline->vsync = 1;
if ((timing->TimingFlags.w & Interlace))
modeline->interlace = 1;
modeline->hfreq = modeline->pclock / modeline->htotal;
modeline->vfreq = modeline->hfreq / modeline->vtotal * (modeline->interlace?2:1);
modeline->refresh = int(modeline->vfreq);
return 0;
}
//============================================================
// pstrip_timing::ps_monitor_index
//============================================================
int pstrip_timing::ps_monitor_index (const char *display_name)
{
int monitor_index = 0;
char sub_index[2];
sub_index[0] = display_name[strlen(display_name)-1];
sub_index[1] = 0;
if (sscanf(sub_index,"%d", &monitor_index) == 1)
monitor_index --;
return monitor_index;
}
//============================================================
// pstrip_timing::update_mode
//============================================================
bool pstrip_timing::update_mode(modeline *mode)
{
if (!set_timing(mode))
{
return false;
}
mode->type |= CUSTOM_VIDEO_TIMING_POWERSTRIP;
return true;
}

83
deps/switchres/custom_video_pstrip.h vendored Normal file
View File

@ -0,0 +1,83 @@
/**************************************************************
custom_video_powerstrip.h - PowerStrip interface routines
---------------------------------------------------------
Switchres Modeline generation engine for emulation
License GPL-2.0+
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
Alexandre Wodarczyk, Gil Delescluse
**************************************************************/
#include "custom_video.h"
//============================================================
// TYPE DEFINITIONS
//============================================================
typedef struct
{
int HorizontalActivePixels;
int HorizontalFrontPorch;
int HorizontalSyncWidth;
int HorizontalBackPorch;
int VerticalActivePixels;
int VerticalFrontPorch;
int VerticalSyncWidth;
int VerticalBackPorch;
int PixelClockInKiloHertz;
union
{
int w;
struct
{
unsigned :1;
unsigned HorizontalPolarityNegative:1;
unsigned VerticalPolarityNegative:1;
unsigned :29;
} b;
} TimingFlags;
} MonitorTiming;
class pstrip_timing : public custom_video
{
public:
pstrip_timing(char *device_name, custom_video_settings *vs);
~pstrip_timing();
const char *api_name() { return "PowerStrip"; }
bool init();
int caps() { return CUSTOM_VIDEO_CAPS_UPDATE | CUSTOM_VIDEO_CAPS_SCAN_EDITABLE | CUSTOM_VIDEO_CAPS_DESKTOP_EDITABLE; }
bool update_mode(modeline *mode);
bool get_timing(modeline *mode);
bool set_timing(modeline *m);
private:
int ps_reset();
int ps_get_modeline(modeline *modeline);
bool ps_set_modeline(modeline *modeline);
int ps_get_monitor_timing(MonitorTiming *timing);
int ps_set_monitor_timing(MonitorTiming *timing);
int ps_set_monitor_timing_string(char *in);
int ps_set_refresh(double vfreq);
int ps_best_pclock(MonitorTiming *timing, int desired_pclock);
int ps_create_resolution(modeline *modeline);
bool ps_read_timing_string(char *in, MonitorTiming *timing);
void ps_fill_timing_string(char *out, MonitorTiming *timing);
bool ps_modeline_to_pstiming(modeline *modeline, MonitorTiming *timing);
int ps_pstiming_to_modeline(MonitorTiming *timing, modeline *modeline);
int ps_monitor_index (const char *display_name);
char m_device_name[32];
char m_ps_timing[256];
int m_monitor_index = 0;
modeline m_user_mode = {};
MonitorTiming m_timing_backup = {};
HWND hPSWnd = 0;
};

1190
deps/switchres/custom_video_xrandr.cpp vendored Normal file

File diff suppressed because it is too large Load Diff

123
deps/switchres/custom_video_xrandr.h vendored Normal file
View File

@ -0,0 +1,123 @@
/**************************************************************
custom_video_xrandr.h - Linux XRANDR video management layer
---------------------------------------------------------
Switchres Modeline generation engine for emulation
License GPL-2.0+
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
Alexandre Wodarczyk, Gil Delescluse
**************************************************************/
#ifndef __CUSTOM_VIDEO_XRANDR__
#define __CUSTOM_VIDEO_XRANDR__
// X11 Xrandr headers
#include <X11/extensions/Xrandr.h>
#include "custom_video.h"
// Set timing option flags
#define XRANDR_DISABLE_CRTC_RELOCATION 0x00000001
#define XRANDR_ENABLE_SCREEN_REORDERING 0x00000002
// Set timing internal flags
#define XRANDR_SETMODE_IS_DESKTOP 0x00000001
#define XRANDR_SETMODE_RESTORE_DESKTOP 0x00000002
#define XRANDR_SETMODE_UPDATE_DESKTOP_CRTC 0x00000010
#define XRANDR_SETMODE_UPDATE_OTHER_CRTC 0x00000020
#define XRANDR_SETMODE_UPDATE_REORDERING 0x00000040
#define XRANDR_SETMODE_INFO_MASK 0x0000000F
#define XRANDR_SETMODE_UPDATE_MASK 0x000000F0
// Super resolution placement, vertical stacking, reserved XRANDR_REORDERING_MAXIMUM_HEIGHT pixels
//TODO confirm 1024 height is sufficient
#define XRANDR_REORDERING_MAXIMUM_HEIGHT 1024
class xrandr_timing : public custom_video
{
public:
xrandr_timing(char *device_name, custom_video_settings *vs);
~xrandr_timing();
const char *api_name() { return "XRANDR"; }
int caps() { return CUSTOM_VIDEO_CAPS_ADD; }
bool init();
bool add_mode(modeline *mode);
bool delete_mode(modeline *mode);
bool update_mode(modeline *mode);
bool get_timing(modeline *mode);
bool set_timing(modeline *mode);
bool process_modelist(std::vector<modeline *>);
static int ms_xerrors;
static int ms_xerrors_flag;
private:
int m_id = 0;
int m_managed = 0;
int m_enable_screen_reordering = 0;
int m_enable_screen_compositing = 0;
XRRModeInfo *find_mode(modeline *mode);
XRRModeInfo *find_mode_by_name(char *name);
bool set_timing(modeline *mode, int flags);
int m_video_modes_position = 0;
char m_device_name[32];
Rotation m_desktop_rotation;
unsigned int m_min_width;
unsigned int m_max_width;
unsigned int m_min_height;
unsigned int m_max_height;
Display *m_pdisplay = NULL;
Window m_root;
int m_screen;
int m_desktop_output = -1;
XRRModeInfo m_desktop_mode = {};
int m_crtc_flags = 0;
XRRCrtcInfo m_last_crtc = {};
void *m_xrandr_handle = 0;
__typeof__(XRRAddOutputMode) *p_XRRAddOutputMode;
__typeof__(XRRConfigCurrentConfiguration) *p_XRRConfigCurrentConfiguration;
__typeof__(XRRCreateMode) *p_XRRCreateMode;
__typeof__(XRRDeleteOutputMode) *p_XRRDeleteOutputMode;
__typeof__(XRRDestroyMode) *p_XRRDestroyMode;
__typeof__(XRRFreeCrtcInfo) *p_XRRFreeCrtcInfo;
__typeof__(XRRFreeOutputInfo) *p_XRRFreeOutputInfo;
__typeof__(XRRFreeScreenConfigInfo) *p_XRRFreeScreenConfigInfo;
__typeof__(XRRFreeScreenResources) *p_XRRFreeScreenResources;
__typeof__(XRRGetCrtcInfo) *p_XRRGetCrtcInfo;
__typeof__(XRRGetOutputInfo) *p_XRRGetOutputInfo;
__typeof__(XRRGetScreenInfo) *p_XRRGetScreenInfo;
__typeof__(XRRGetScreenResourcesCurrent) *p_XRRGetScreenResourcesCurrent;
__typeof__(XRRQueryVersion) *p_XRRQueryVersion;
__typeof__(XRRSetCrtcConfig) *p_XRRSetCrtcConfig;
__typeof__(XRRSetScreenSize) *p_XRRSetScreenSize;
__typeof__(XRRGetScreenSizeRange) *p_XRRGetScreenSizeRange;
void *m_x11_handle = 0;
__typeof__(XCloseDisplay) *p_XCloseDisplay;
__typeof__(XGrabServer) *p_XGrabServer;
__typeof__(XOpenDisplay) *p_XOpenDisplay;
__typeof__(XSync) *p_XSync;
__typeof__(XUngrabServer) *p_XUngrabServer;
__typeof__(XSetErrorHandler) *p_XSetErrorHandler;
__typeof__(XClearWindow) *p_XClearWindow;
__typeof__(XFillRectangle) *p_XFillRectangle;
__typeof__(XCreateGC) *p_XCreateGC;
};
#endif

481
deps/switchres/display.cpp vendored Normal file
View File

@ -0,0 +1,481 @@
/**************************************************************
display.cpp - Display manager
---------------------------------------------------------
Switchres Modeline generation engine for emulation
License GPL-2.0+
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
Alexandre Wodarczyk, Gil Delescluse
**************************************************************/
#include <stdio.h>
#include "display.h"
#if defined(_WIN32)
#include "display_windows.h"
#elif defined(__linux__)
#include <string.h>
#include "display_linux.h"
#endif
#include "log.h"
//============================================================
// display_manager::make
//============================================================
display_manager *display_manager::make(display_settings *ds)
{
display_manager *display = nullptr;
#if defined(_WIN32)
display = new windows_display(ds);
#elif defined(__linux__)
display = new linux_display(ds);
#endif
return display;
}
//============================================================
// display_manager::parse_options
//============================================================
void display_manager::parse_options()
{
// Get user_mode as <w>x<h>@<r>
set_user_mode(&m_ds.user_mode);
// Get user defined modeline (overrides user_mode)
modeline user_mode = {};
if (m_ds.modeline_generation)
{
if (modeline_parse(m_ds.user_modeline, &user_mode))
{
user_mode.type |= MODE_USER_DEF;
set_user_mode(&user_mode);
}
}
// Get monitor specs
if (user_mode.hactive)
{
modeline_to_monitor_range(range, &user_mode);
monitor_show_range(range);
}
else
{
char default_monitor[] = "generic_15";
memset(&range[0], 0, sizeof(struct monitor_range) * MAX_RANGES);
if (!strcmp(m_ds.monitor, "custom"))
for (int i = 0; i < MAX_RANGES; i++) monitor_fill_range(&range[i], m_ds.crt_range[i]);
else if (!strcmp(m_ds.monitor, "lcd"))
monitor_fill_lcd_range(&range[0], m_ds.lcd_range);
else if (monitor_set_preset(m_ds.monitor, range) == 0)
monitor_set_preset(default_monitor, range);
}
}
//============================================================
// display_manager::init
//============================================================
bool display_manager::init()
{
sprintf(m_ds.screen, "ram");
return true;
}
//============================================================
// display_manager::caps
//============================================================
int display_manager::caps()
{
if (video())
return video()->caps();
else
return CUSTOM_VIDEO_CAPS_ADD;
}
//============================================================
// display_manager::add_mode
//============================================================
bool display_manager::add_mode(modeline *mode)
{
if (video() == nullptr)
return false;
// Add new mode
if (!video()->add_mode(mode))
{
log_verbose("Switchres: error adding mode ");
log_mode(mode);
return false;
}
mode->type &= ~MODE_ADD;
log_verbose("Switchres: added ");
log_mode(mode);
return true;
}
//============================================================
// display_manager::delete_mode
//============================================================
bool display_manager::delete_mode(modeline *mode)
{
if (video() == nullptr)
return false;
if (!video()->delete_mode(mode))
{
log_verbose("Switchres: error deleting mode ");
log_mode(mode);
return false;
}
log_verbose("Switchres: deleted ");
log_mode(mode);
return true;
}
//============================================================
// display_manager::update_mode
//============================================================
bool display_manager::update_mode(modeline *mode)
{
if (video() == nullptr)
return false;
// Apply new timings
if (!video()->update_mode(mode))
{
log_verbose("Switchres: error updating mode ");
log_mode(mode);
return false;
}
mode->type &= ~MODE_UPDATE;
log_verbose("Switchres: updated ");
log_mode(mode);
return true;
}
//============================================================
// display_manager::set_mode
//============================================================
bool display_manager::set_mode(modeline *)
{
return false;
}
//============================================================
// display_manager::log_mode
//============================================================
void display_manager::log_mode(modeline *mode)
{
char modeline_txt[256];
log_verbose("%s timing %s\n", video()->api_name(), modeline_print(mode, modeline_txt, MS_FULL));
}
//============================================================
// display_manager::restore_modes
//============================================================
bool display_manager::restore_modes()
{
// Compare each mode in our table with its original state
for (unsigned i = video_modes.size(); i-- > 0; )
{
// First, delete all modes we've added
if (i + 1 > backup_modes.size())
video_modes[i].type |= MODE_DELETE;
// Now restore all modes which timings have been modified
else if (modeline_is_different(&video_modes[i], &backup_modes[i]))
{
video_modes[i] = backup_modes[i];
video_modes[i].type |= MODE_UPDATE;
}
}
// Finally, flush pending changes to driver
return flush_modes();
}
//============================================================
// display_manager::flush_modes
//============================================================
bool display_manager::flush_modes()
{
bool error = false;
std::vector<modeline *> modified_modes = {};
if (video() == nullptr)
return false;
// Loop through our mode table to collect all pending changes
for (auto &mode : video_modes)
if (mode.type & (MODE_UPDATE | MODE_ADD | MODE_DELETE))
modified_modes.push_back(&mode);
// Flush pending changes to driver
if (modified_modes.size() > 0)
{
video()->process_modelist(modified_modes);
// Log error/success result for each mode
for (auto &mode : modified_modes)
{
log_verbose("Switchres: %s %s mode ", mode->type & MODE_ERROR? "error" : "success", mode->type & MODE_DELETE? "deleting" : mode->type & MODE_ADD? "adding" : "updating");
log_mode(mode);
if (mode->type & MODE_ERROR)
error = true;
}
// Update our internal mode table to reflect the changes
for (unsigned i = video_modes.size(); i-- > 0; )
{
if (video_modes[i].type & MODE_ERROR)
continue;
if (video_modes[i].type & MODE_DELETE)
{
video_modes.erase(video_modes.begin() + i);
m_best_mode = 0;
}
else
video_modes[i].type &= ~(MODE_UPDATE | MODE_ADD);
}
}
return !error;
}
//============================================================
// display_manager::filter_modes
//============================================================
bool display_manager::filter_modes()
{
for (auto &mode : video_modes)
{
// apply options to mode type
if (m_ds.refresh_dont_care)
mode.type |= V_FREQ_EDITABLE;
if ((caps() & CUSTOM_VIDEO_CAPS_UPDATE))
mode.type |= V_FREQ_EDITABLE;
if (caps() & CUSTOM_VIDEO_CAPS_SCAN_EDITABLE)
mode.type |= SCAN_EDITABLE;
if (!m_ds.modeline_generation)
mode.type &= ~(XYV_EDITABLE | SCAN_EDITABLE);
if ((mode.type & MODE_DESKTOP) && !(caps() & CUSTOM_VIDEO_CAPS_DESKTOP_EDITABLE))
mode.type &= ~V_FREQ_EDITABLE;
if (m_ds.lock_system_modes && (mode.type & CUSTOM_VIDEO_TIMING_SYSTEM))
mode.type |= MODE_DISABLED;
// Make sure to unlock the desktop mode as fallback
if (mode.type & MODE_DESKTOP)
mode.type &= ~MODE_DISABLED;
// Lock all modes that don't match the user's -resolution rules
if (m_user_mode.width != 0 || m_user_mode.height != 0 || m_user_mode.refresh == !0)
{
if (!( (mode.width == m_user_mode.width || (mode.type & X_RES_EDITABLE) || m_user_mode.width == 0)
&& (mode.height == m_user_mode.height || (mode.type & Y_RES_EDITABLE) || m_user_mode.height == 0)
&& (mode.refresh == m_user_mode.refresh || (mode.type & V_FREQ_EDITABLE) || m_user_mode.refresh == 0) ))
mode.type |= MODE_DISABLED;
else
mode.type &= ~MODE_DISABLED;
}
}
return true;
}
//============================================================
// display_manager::get_video_mode
//============================================================
modeline *display_manager::get_mode(int width, int height, float refresh, bool interlaced)
{
modeline s_mode = {};
modeline t_mode = {};
modeline best_mode = {};
char result[256]={'\x00'};
log_verbose("Switchres: Calculating best video mode for %dx%d@%.6f%s orientation: %s\n",
width, height, refresh, interlaced?"i":"", rotation()?"rotated":"normal");
best_mode.result.weight |= R_OUT_OF_RANGE;
s_mode.interlace = interlaced;
s_mode.vfreq = refresh;
s_mode.hactive = normalize(width, 8);
s_mode.vactive = height;
if (rotation()) std::swap(s_mode.hactive, s_mode.vactive);
// Create a dummy mode entry if allowed
if (caps() & CUSTOM_VIDEO_CAPS_ADD && m_ds.modeline_generation)
{
modeline new_mode = {};
new_mode.type = XYV_EDITABLE | V_FREQ_EDITABLE | SCAN_EDITABLE | MODE_ADD | (desktop_is_rotated()? MODE_ROTATED : MODE_OK);
video_modes.push_back(new_mode);
}
// Run through our mode list and find the most suitable mode
for (auto &mode : video_modes)
{
log_verbose("\nSwitchres: %s%4d%sx%s%4d%s_%s%d=%.6fHz%s%s\n",
mode.type & X_RES_EDITABLE?"(":"[", mode.width, mode.type & X_RES_EDITABLE?")":"]",
mode.type & Y_RES_EDITABLE?"(":"[", mode.height, mode.type & Y_RES_EDITABLE?")":"]",
mode.type & V_FREQ_EDITABLE?"(":"[", mode.refresh, mode.vfreq, mode.type & V_FREQ_EDITABLE?")":"]",
mode.type & MODE_DISABLED?" - locked":"");
// now get the mode if allowed
if (!(mode.type & MODE_DISABLED))
{
for (int i = 0 ; i < MAX_RANGES ; i++)
{
if (range[i].hfreq_min)
{
t_mode = mode;
// init all editable fields with source or user values
if (t_mode.type & X_RES_EDITABLE)
t_mode.hactive = m_user_mode.width? m_user_mode.width : s_mode.hactive;
if (t_mode.type & Y_RES_EDITABLE)
t_mode.vactive = m_user_mode.height? m_user_mode.height : s_mode.vactive;
if (t_mode.type & V_FREQ_EDITABLE)
{
// If user's vfreq is defined, it means we have an user modeline, so force it
if (m_user_mode.vfreq)
t_mode = m_user_mode;
else
t_mode.vfreq = s_mode.vfreq;
}
// lock resolution fields if required
if (m_user_mode.width) t_mode.type &= ~X_RES_EDITABLE;
if (m_user_mode.height) t_mode.type &= ~Y_RES_EDITABLE;
if (m_user_mode.vfreq) t_mode.type &= ~V_FREQ_EDITABLE;
modeline_create(&s_mode, &t_mode, &range[i], &m_ds.gs);
t_mode.range = i;
log_verbose("%s\n", modeline_result(&t_mode, result));
if (modeline_compare(&t_mode, &best_mode))
{
best_mode = t_mode;
m_best_mode = &mode;
}
}
}
}
}
// If we didn't need to create a new mode, remove our dummy entry
if (caps() & CUSTOM_VIDEO_CAPS_ADD && m_ds.modeline_generation && m_best_mode != &video_modes.back())
video_modes.pop_back();
// If we didn't find a suitable mode, exit now
if (best_mode.result.weight & R_OUT_OF_RANGE)
{
m_best_mode = 0;
log_error("Switchres: could not find a video mode that meets your specs\n");
return nullptr;
}
log_verbose("\nSwitchres: %s (%dx%d@%.6f)->(%dx%d@%.6f)\n", rotation()?"rotated":"normal",
width, height, refresh, best_mode.hactive, best_mode.vactive, best_mode.vfreq);
log_verbose("%s\n", modeline_result(&best_mode, result));
// Copy the new modeline to our mode list
if (m_ds.modeline_generation)
{
if (best_mode.type & MODE_ADD)
{
best_mode.width = best_mode.hactive;
best_mode.height = best_mode.vactive;
best_mode.refresh = int(best_mode.vfreq);
// lock new mode
best_mode.type &= ~(X_RES_EDITABLE | Y_RES_EDITABLE | (caps() & CUSTOM_VIDEO_CAPS_UPDATE? 0 : V_FREQ_EDITABLE));
}
else if (modeline_is_different(&best_mode, m_best_mode) != 0)
best_mode.type |= MODE_UPDATE;
char modeline[256]={'\x00'};
log_info("Switchres: Modeline %s\n", modeline_print(&best_mode, modeline, MS_FULL));
}
// Check if new best mode is different than previous one
m_switching_required = (m_current_mode != m_best_mode || best_mode.type & MODE_UPDATE);
*m_best_mode = best_mode;
return m_best_mode;
}
//============================================================
// display_manager::auto_specs
//============================================================
bool display_manager::auto_specs()
{
// Make sure we have a valid mode
if (desktop_mode.width == 0 || desktop_mode.height == 0 || desktop_mode.refresh == 0)
{
log_error("Switchres: Invalid desktop mode %dx%d@%d\n", desktop_mode.width, desktop_mode.height, desktop_mode.refresh);
return false;
}
log_verbose("Switchres: Creating automatic specs for LCD based on %s\n", (desktop_mode.type & CUSTOM_VIDEO_TIMING_SYSTEM)? "VESA GTF" : "current timings");
// Make sure our current refresh is within range if set to auto
if (!strcmp(m_ds.lcd_range, "auto"))
{
sprintf(m_ds.lcd_range, "%d-%d", desktop_mode.refresh - 1, desktop_mode.refresh + 1);
monitor_fill_lcd_range(range, m_ds.lcd_range);
}
// Create a working range with the best possible information
if (desktop_mode.type & CUSTOM_VIDEO_TIMING_SYSTEM) modeline_vesa_gtf(&desktop_mode);
modeline_to_monitor_range(range, &desktop_mode);
monitor_show_range(range);
// Force our resolution to LCD's native one
modeline user_mode = {};
user_mode.width = desktop_mode.width;
user_mode.height = desktop_mode.height;
user_mode.refresh = desktop_mode.refresh;
set_user_mode(&user_mode);
return true;
}

162
deps/switchres/display.h vendored Normal file
View File

@ -0,0 +1,162 @@
/**************************************************************
display.h - Display manager
---------------------------------------------------------
Switchres Modeline generation engine for emulation
License GPL-2.0+
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
Alexandre Wodarczyk, Gil Delescluse
**************************************************************/
#ifndef __DISPLAY_H__
#define __DISPLAY_H__
#include <vector>
#include "modeline.h"
#include "custom_video.h"
typedef struct display_settings
{
char screen[32];
char api[32];
bool modeline_generation;
bool lock_unsupported_modes;
bool lock_system_modes;
bool refresh_dont_care;
bool keep_changes;
char monitor[32];
char crt_range[MAX_RANGES][256];
char lcd_range[256];
char user_modeline[256];
modeline user_mode;
generator_settings gs;
custom_video_settings vs;
} display_settings;
class display_manager
{
public:
display_manager() {};
virtual ~display_manager()
{
if (!m_ds.keep_changes) restore_modes();
if (m_factory) delete m_factory;
};
display_manager *make(display_settings *ds);
void parse_options();
virtual bool init();
virtual int caps();
// getters
custom_video *factory() const { return m_factory; }
custom_video *video() const { return m_video; }
modeline user_mode() const { return m_user_mode; }
modeline *best_mode() const { return m_best_mode; }
modeline *current_mode() const { return m_current_mode; }
int index() const { return m_index; }
bool desktop_is_rotated() const { return m_desktop_is_rotated; }
// getters (display manager)
const char *set_monitor() { return (const char*) &m_ds.monitor; }
const char *user_modeline() { return (const char*) &m_ds.user_modeline; }
const char *crt_range(int i) { return (const char*) &m_ds.crt_range[i]; }
const char *lcd_range() { return (const char*) &m_ds.lcd_range; }
const char *screen() { return (const char*) &m_ds.screen; }
const char *api() { return (const char*) &m_ds.api; }
bool modeline_generation() { return m_ds.modeline_generation; }
bool lock_unsupported_modes() { return m_ds.lock_unsupported_modes; }
bool lock_system_modes() { return m_ds.lock_system_modes; }
bool refresh_dont_care() { return m_ds.refresh_dont_care; }
bool keep_changes() { return m_ds.keep_changes; }
// getters (modeline generator)
bool interlace() { return m_ds.gs.interlace; }
bool doublescan() { return m_ds.gs.doublescan; }
double dotclock_min() { return m_ds.gs.pclock_min; }
double refresh_tolerance() { return m_ds.gs.refresh_tolerance; }
int super_width() { return m_ds.gs.super_width; }
bool rotation() { return m_ds.gs.rotation; }
double monitor_aspect() { return m_ds.gs.monitor_aspect; }
int v_shift_correct() { return m_ds.gs.v_shift_correct; }
int pixel_precision() { return m_ds.gs.pixel_precision; }
int interlace_force_even() { return m_ds.gs.interlace_force_even; }
// getters (modeline result)
bool got_mode() { return (m_best_mode != nullptr); }
int width() { return m_best_mode != nullptr? m_best_mode->width : 0; }
int height() { return m_best_mode != nullptr? m_best_mode->height : 0; }
int refresh() { return m_best_mode != nullptr? m_best_mode->refresh : 0; }
double v_freq() { return m_best_mode != nullptr? m_best_mode->vfreq : 0; }
double h_freq() { return m_best_mode != nullptr? m_best_mode->hfreq : 0; }
int x_scale() { return m_best_mode != nullptr? m_best_mode->result.x_scale : 0; }
int y_scale() { return m_best_mode != nullptr? m_best_mode->result.y_scale : 0; }
int v_scale() { return m_best_mode != nullptr? m_best_mode->result.v_scale : 0; }
bool is_interlaced() { return m_best_mode != nullptr? m_best_mode->interlace : false; }
bool is_doublescanned() { return m_best_mode != nullptr? m_best_mode->doublescan : false; }
bool is_stretched() { return m_best_mode != nullptr? m_best_mode->result.weight & R_RES_STRETCH : false; }
bool is_refresh_off() { return m_best_mode != nullptr? m_best_mode->result.weight & R_V_FREQ_OFF : false; }
bool is_switching_required() { return m_switching_required; }
bool is_mode_updated() { return m_best_mode != nullptr? m_best_mode->type & MODE_UPDATE : false; }
bool is_mode_new() { return m_best_mode != nullptr? m_best_mode->type & MODE_ADD : false; }
// setters
void set_factory(custom_video *factory) { m_factory = factory; }
void set_custom_video(custom_video *video) { m_video = video; }
void set_user_mode(modeline *mode) { m_user_mode = *mode; filter_modes(); }
void set_current_mode(modeline *mode) { m_current_mode = mode; }
void set_index(int index) { m_index = index; }
void set_desktop_is_rotated(bool value) { m_desktop_is_rotated = value; }
void set_rotation(bool value) { m_ds.gs.rotation = value; }
void set_monitor_aspect(float aspect) { m_ds.gs.monitor_aspect = aspect; }
void set_v_shift_correct(int value) { m_ds.gs.v_shift_correct = value; }
void set_pixel_precision(int value) { m_ds.gs.pixel_precision = value; }
// options
display_settings m_ds = {};
// mode setting interface
modeline *get_mode(int width, int height, float refresh, bool interlaced);
bool add_mode(modeline *mode);
bool delete_mode(modeline *mode);
bool update_mode(modeline *mode);
virtual bool set_mode(modeline *);
void log_mode(modeline *mode);
// mode list handling
bool filter_modes();
bool restore_modes();
bool flush_modes();
bool auto_specs();
// mode list
std::vector<modeline> video_modes = {};
std::vector<modeline> backup_modes = {};
modeline desktop_mode = {};
// monitor preset
monitor_range range[MAX_RANGES];
private:
// custom video backend
custom_video *m_factory = 0;
custom_video *m_video = 0;
modeline m_user_mode = {};
modeline *m_best_mode = 0;
modeline *m_current_mode = 0;
int m_index = 0;
bool m_desktop_is_rotated = 0;
bool m_switching_required = 0;
};
#endif

168
deps/switchres/display_linux.cpp vendored Normal file
View File

@ -0,0 +1,168 @@
/**************************************************************
display_linux.cpp - Display manager for Linux
---------------------------------------------------------
Switchres Modeline generation engine for emulation
License GPL-2.0+
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
Alexandre Wodarczyk, Gil Delescluse
**************************************************************/
#include <stdio.h>
#include <string.h>
#include "display_linux.h"
#include "log.h"
//============================================================
// linux_display::linux_display
//============================================================
linux_display::linux_display(display_settings *ds)
{
// Get display settings
m_ds = *ds;
}
//============================================================
// linux_display::~linux_display
//============================================================
linux_display::~linux_display()
{
if (!m_ds.keep_changes)
restore_desktop_mode();
}
//============================================================
// linux_display::init
//============================================================
bool linux_display::init()
{
// Initialize custom video
int method = CUSTOM_VIDEO_TIMING_AUTO;
#ifdef SR_WITH_XRANDR
if (!strcmp(m_ds.api, "xrandr"))
method = CUSTOM_VIDEO_TIMING_XRANDR;
#endif
#ifdef SR_WITH_KMSDRM
if (!strcmp(m_ds.api, "drmkms"))
method = CUSTOM_VIDEO_TIMING_DRMKMS;
#endif
set_factory(new custom_video);
set_custom_video(factory()->make(m_ds.screen, NULL, method, &m_ds.vs));
if (!video() or !video()->init())
return false;
// Build our display's mode list
video_modes.clear();
backup_modes.clear();
get_desktop_mode();
get_available_video_modes();
if (!strcmp(m_ds.monitor, "lcd")) auto_specs();
filter_modes();
return true;
}
//============================================================
// linux_display::set_mode
//============================================================
bool linux_display::set_mode(modeline *mode)
{
if (mode && set_desktop_mode(mode, 0))
{
set_current_mode(mode);
return true;
}
return false;
}
//============================================================
// linux_display::get_desktop_mode
//============================================================
bool linux_display::get_desktop_mode()
{
if (video() == NULL)
return false;
return true;
}
//============================================================
// linux_display::set_desktop_mode
//============================================================
bool linux_display::set_desktop_mode(modeline *mode, int flags)
{
if (!mode)
return false;
if (video() == NULL)
return false;
if (flags != 0)
log_info("Set desktop mode flags value is 0x%x.\n", flags);
return video()->set_timing(mode);
}
//============================================================
// linux_display::restore_desktop_mode
//============================================================
bool linux_display::restore_desktop_mode()
{
if (video() == NULL)
return false;
return video()->set_timing(&desktop_mode);
}
//============================================================
// linux_display::get_available_video_modes
//============================================================
int linux_display::get_available_video_modes()
{
if (video() == NULL)
return false;
// loop through all modes until NULL mode type is received
for (;;)
{
modeline mode;
memset(&mode, 0, sizeof(struct modeline));
// get next mode
video()->get_timing(&mode);
if (mode.type == 0 || mode.platform_data == 0)
break;
// set the desktop mode
if (mode.type & MODE_DESKTOP)
{
memcpy(&desktop_mode, &mode, sizeof(modeline));
if (current_mode() == nullptr)
set_current_mode(&mode);
}
video_modes.push_back(mode);
backup_modes.push_back(mode);
log_verbose("Switchres: [%3ld] %4dx%4d @%3d%s%s %s: ", video_modes.size(), mode.width, mode.height, mode.refresh, mode.interlace ? "i" : "p", mode.type & MODE_DESKTOP ? "*" : "", mode.type & MODE_ROTATED ? "rot" : "");
log_mode(&mode);
};
return true;
}

30
deps/switchres/display_linux.h vendored Normal file
View File

@ -0,0 +1,30 @@
/**************************************************************
display_linux.h - Display manager for Linux
---------------------------------------------------------
Switchres Modeline generation engine for emulation
License GPL-2.0+
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
Alexandre Wodarczyk, Gil Delescluse
**************************************************************/
#include "display.h"
class linux_display : public display_manager
{
public:
linux_display(display_settings *ds);
~linux_display();
bool init();
bool set_mode(modeline *mode);
private:
bool get_desktop_mode();
bool set_desktop_mode(modeline *mode, int flags);
bool restore_desktop_mode();
int get_available_video_modes();
};

260
deps/switchres/display_windows.cpp vendored Normal file
View File

@ -0,0 +1,260 @@
/**************************************************************
display_windows.cpp - Display manager for Windows
---------------------------------------------------------
Switchres Modeline generation engine for emulation
License GPL-2.0+
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
Alexandre Wodarczyk, Gil Delescluse
**************************************************************/
#include <stdio.h>
#include "display_windows.h"
#include "log.h"
//============================================================
// windows_display::windows_display
//============================================================
windows_display::windows_display(display_settings *ds)
{
// Get display settings
m_ds = *ds;
}
//============================================================
// windows_display::~windows_display
//============================================================
windows_display::~windows_display()
{
// Restore previous settings
if (!m_ds.keep_changes) ChangeDisplaySettingsExA(m_device_name, NULL, NULL, 0, 0);
}
//============================================================
// windows_display::init
//============================================================
bool windows_display::init()
{
DISPLAY_DEVICEA lpDisplayDevice[DISPLAY_MAX];
int idev = 0;
int found = -1;
while (idev < DISPLAY_MAX)
{
memset(&lpDisplayDevice[idev], 0, sizeof(DISPLAY_DEVICEA));
lpDisplayDevice[idev].cb = sizeof(DISPLAY_DEVICEA);
if (EnumDisplayDevicesA(NULL, idev, &lpDisplayDevice[idev], 0) == FALSE)
break;
if ((!strcmp(m_ds.screen, "auto") && (lpDisplayDevice[idev].StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE))
|| !strcmp(m_ds.screen, lpDisplayDevice[idev].DeviceName) || m_ds.screen[0] - '0' == idev)
found = idev;
idev++;
}
if (found != -1)
{
strncpy(m_device_name, lpDisplayDevice[found].DeviceName, sizeof(m_device_name) -1);
strncpy(m_device_id, lpDisplayDevice[found].DeviceID, sizeof(m_device_id) -1);
log_verbose("Switchres: %s: %s (%s)\n", m_device_name, lpDisplayDevice[found].DeviceString, m_device_id);
char *pch;
int i;
for (i = 0; i < idev; i++)
{
pch = strstr(lpDisplayDevice[i].DeviceString, lpDisplayDevice[found].DeviceString);
if (pch)
{
found = i;
break;
}
}
char *chsrc, *chdst;
chdst = m_device_key;
for (chsrc = lpDisplayDevice[i].DeviceKey + 18; *chsrc != 0; chsrc++)
*chdst++ = *chsrc;
*chdst = 0;
}
else
{
log_verbose("Switchres: Failed obtaining default video registry key\n");
return false;
}
log_verbose("Switchres: Device key: %s\n", m_device_key);
// Initialize custom video
int method = CUSTOM_VIDEO_TIMING_AUTO;
if(!strcmp(m_ds.api, "powerstrip")) method = CUSTOM_VIDEO_TIMING_POWERSTRIP;
strcpy(m_ds.vs.device_reg_key, m_device_key);
// Create custom video backend
set_factory(new custom_video);
set_custom_video(factory()->make(m_device_name, m_device_id, method, &m_ds.vs));
if (video()) video()->init();
// Build our display's mode list
video_modes.clear();
backup_modes.clear();
get_desktop_mode();
get_available_video_modes();
if (!strcmp(m_ds.monitor, "lcd")) auto_specs();
filter_modes();
return true;
}
//============================================================
// windows_display::set_mode
//============================================================
bool windows_display::set_mode(modeline *mode)
{
if (mode && set_desktop_mode(mode, (m_ds.keep_changes? CDS_UPDATEREGISTRY : CDS_FULLSCREEN) | CDS_RESET))
{
set_current_mode(mode);
return true;
}
return false;
}
//============================================================
// windows_display::get_desktop_mode
//============================================================
bool windows_display::get_desktop_mode()
{
memset(&m_devmode, 0, sizeof(DEVMODEA));
m_devmode.dmSize = sizeof(DEVMODEA);
if (EnumDisplaySettingsExA(!strcmp(m_device_name, "auto")?NULL:m_device_name, ENUM_CURRENT_SETTINGS, &m_devmode, 0))
{
desktop_mode.width = m_devmode.dmDisplayOrientation == DMDO_DEFAULT || m_devmode.dmDisplayOrientation == DMDO_180? m_devmode.dmPelsWidth:m_devmode.dmPelsHeight;
desktop_mode.height = m_devmode.dmDisplayOrientation == DMDO_DEFAULT || m_devmode.dmDisplayOrientation == DMDO_180? m_devmode.dmPelsHeight:m_devmode.dmPelsWidth;
desktop_mode.refresh = m_devmode.dmDisplayFrequency;
desktop_mode.interlace = (m_devmode.dmDisplayFlags & DM_INTERLACED)?1:0;
return true;
}
return false;
}
//============================================================
// windows_display::set_desktop_mode
//============================================================
bool windows_display::set_desktop_mode(modeline *mode, int flags)
{
if (mode)
{
DEVMODEA lpDevMode;
memset(&lpDevMode, 0, sizeof(DEVMODEA));
lpDevMode.dmSize = sizeof(DEVMODEA);
lpDevMode.dmPelsWidth = mode->type & MODE_ROTATED? mode->height : mode->width;
lpDevMode.dmPelsHeight = mode->type & MODE_ROTATED? mode->width : mode->height;
lpDevMode.dmDisplayFrequency = (int)mode->refresh;
lpDevMode.dmDisplayFlags = mode->interlace? DM_INTERLACED : 0;
lpDevMode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY | DM_DISPLAYFLAGS;
log_info("set_desktop_mode: %s (%dx%d@%d) flags(%x)\n", m_device_name, (int)lpDevMode.dmPelsWidth, (int)lpDevMode.dmPelsHeight, (int)lpDevMode.dmDisplayFrequency, (int)lpDevMode.dmDisplayFlags);
int result = ChangeDisplaySettingsExA(m_device_name, &lpDevMode, NULL, flags, 0);
if (result == DISP_CHANGE_SUCCESSFUL)
return true;
log_error("ChangeDisplaySettingsExA error(%x)\n", (int)result);
}
return false;
}
//============================================================
// windows_display::restore_desktop_mode
//============================================================
bool windows_display::restore_desktop_mode()
{
if (ChangeDisplaySettingsExA(m_device_name, &m_devmode, NULL, 0, 0) == DISP_CHANGE_SUCCESSFUL)
return true;
return false;
}
//============================================================
// windows_display::get_available_video_modes
//============================================================
int windows_display::get_available_video_modes()
{
int iModeNum = 0, j = 0, k = 0;
DEVMODEA lpDevMode;
memset(&lpDevMode, 0, sizeof(DEVMODEA));
lpDevMode.dmSize = sizeof(DEVMODEA);
log_verbose("Switchres: Searching for custom video modes...\n");
while (EnumDisplaySettingsExA(m_device_name, iModeNum, &lpDevMode, m_ds.lock_unsupported_modes?0:EDS_RAWMODE) != 0)
{
if (lpDevMode.dmBitsPerPel == 32 && lpDevMode.dmDisplayFixedOutput == DMDFO_DEFAULT)
{
modeline m;
memset(&m, 0, sizeof(struct modeline));
m.interlace = (lpDevMode.dmDisplayFlags & DM_INTERLACED)?1:0;
m.width = lpDevMode.dmDisplayOrientation == DMDO_DEFAULT || lpDevMode.dmDisplayOrientation == DMDO_180? lpDevMode.dmPelsWidth:lpDevMode.dmPelsHeight;
m.height = lpDevMode.dmDisplayOrientation == DMDO_DEFAULT || lpDevMode.dmDisplayOrientation == DMDO_180? lpDevMode.dmPelsHeight:lpDevMode.dmPelsWidth;
m.refresh = lpDevMode.dmDisplayFrequency;
m.hactive = m.width;
m.vactive = m.height;
m.vfreq = m.refresh;
m.type |= lpDevMode.dmDisplayOrientation == DMDO_90 || lpDevMode.dmDisplayOrientation == DMDO_270? MODE_ROTATED : MODE_OK;
for (auto &mode : video_modes) if (mode.width == m.width && mode.height == m.height && mode.refresh == m.refresh && m.interlace == mode.interlace) goto found;
if (m.width == desktop_mode.width && m.height == desktop_mode.height && m.refresh == desktop_mode.refresh && m.interlace == desktop_mode.interlace)
{
m.type |= MODE_DESKTOP;
if (m.type & MODE_ROTATED) set_desktop_is_rotated(true);
if (current_mode() == nullptr)
set_current_mode(&m);
}
log_verbose("Switchres: [%3d] %4dx%4d @%3d%s%s %s: ", k, m.width, m.height, m.refresh, m.interlace?"i":"p", m.type & MODE_DESKTOP?"*":"", m.type & MODE_ROTATED?"rot":"");
if (video() && video()->get_timing(&m))
{
j++;
log_mode(&m);
}
else
{
m.type |= CUSTOM_VIDEO_TIMING_SYSTEM;
log_verbose("system mode\n");
}
// Save our desktop mode now that we queried detailed timings
if (m.type & MODE_DESKTOP) desktop_mode = m;
video_modes.push_back(m);
backup_modes.push_back(m);
k++;
}
found:
iModeNum++;
}
k--;
log_verbose("Switchres: Found %d custom of %d active video modes\n", j, k);
return k;
}

45
deps/switchres/display_windows.h vendored Normal file
View File

@ -0,0 +1,45 @@
/**************************************************************
display_windows.h - Display manager for Windows
---------------------------------------------------------
Switchres Modeline generation engine for emulation
License GPL-2.0+
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
Alexandre Wodarczyk, Gil Delescluse
**************************************************************/
#include <windows.h>
#include "display.h"
//============================================================
// PARAMETERS
//============================================================
// display modes
#define DM_INTERLACED 0x00000002
#define DISPLAY_MAX 16
class windows_display : public display_manager
{
public:
windows_display(display_settings *ds);
~windows_display();
bool init();
bool set_mode(modeline *mode);
private:
bool get_desktop_mode();
bool set_desktop_mode(modeline *mode, int flags);
bool restore_desktop_mode();
int get_available_video_modes();
char m_device_name[32];
char m_device_id[128];
char m_device_key[128];
DEVMODEA m_devmode;
};

244
deps/switchres/edid.cpp vendored Normal file
View File

@ -0,0 +1,244 @@
/**************************************************************
edid.c - Basic EDID generation
(based on edid.S: EDID data template by Carsten Emde)
---------------------------------------------------------
Switchres Modeline generation engine for emulation
License GPL-2.0+
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
Alexandre Wodarczyk, Gil Delescluse
**************************************************************/
#include <stdio.h>
#include <string.h>
#include "switchres.h"
#include "edid.h"
//============================================================
// edid_from_modeline
//============================================================
int edid_from_modeline(modeline *mode, monitor_range *range, char *name, edid_block *edid)
{
if (!edid) return 0;
// header
edid->b[0] = 0x00;
edid->b[1] = 0xff;
edid->b[2] = 0xff;
edid->b[3] = 0xff;
edid->b[4] = 0xff;
edid->b[5] = 0xff;
edid->b[6] = 0xff;
edid->b[7] = 0x00;
// Manufacturer ID = "SWR"
edid->b[8] = 0x4e;
edid->b[9] = 0xf2;
// Manufacturer product code
edid->b[10] = 0x00;
edid->b[11] = 0x00;
// Serial number
edid->b[12] = 0x00;
edid->b[13] = 0x00;
edid->b[14] = 0x00;
edid->b[15] = 0x00;
// Week of manufacture
edid->b[16] = 5;
// Year of manufacture
edid->b[17] = 2021 - 1990;
// EDID version and revision
edid->b[18] = 1;
edid->b[19] = 3;
// video params
edid->b[20] = 0x6d;
// Maximum H & V size in cm
edid->b[21] = 48;
edid->b[22] = 36;
// Gamma
edid->b[23] = 120;
// Display features
edid->b[24] = 0x0A;
// Chromacity coordinates;
edid->b[25] = 0x5e;
edid->b[26] = 0xc0;
edid->b[27] = 0xa4;
edid->b[28] = 0x59;
edid->b[29] = 0x4a;
edid->b[30] = 0x98;
edid->b[31] = 0x25;
edid->b[32] = 0x20;
edid->b[33] = 0x50;
edid->b[34] = 0x54;
// Established timings
edid->b[35] = 0x00;
edid->b[36] = 0x00;
edid->b[37] = 0x00;
// Standard timing information
edid->b[38] = 0x01;
edid->b[39] = 0x01;
edid->b[40] = 0x01;
edid->b[41] = 0x01;
edid->b[42] = 0x01;
edid->b[43] = 0x01;
edid->b[44] = 0x01;
edid->b[45] = 0x01;
edid->b[46] = 0x01;
edid->b[47] = 0x01;
edid->b[48] = 0x01;
edid->b[49] = 0x01;
edid->b[50] = 0x01;
edid->b[51] = 0x01;
edid->b[52] = 0x01;
edid->b[53] = 0x01;
// Pixel clock in 10 kHz units. (0.-655.35 MHz, little-endian)
edid->b[54] = (mode->pclock / 10000) & 0xff;
edid->b[55] = (mode->pclock / 10000) >> 8;
int h_active = mode->hactive;
int h_blank = mode->htotal - mode->hactive;
int h_offset = mode->hbegin - mode->hactive;
int h_pulse = mode->hend - mode->hbegin;
int v_active = mode->vactive;
int v_blank = (int)mode->vtotal - mode->vactive;
int v_offset = mode->vbegin - mode->vactive;
int v_pulse = mode->vend - mode->vbegin;
// Horizontal active pixels 8 lsbits (0-4095)
edid->b[56] = h_active & 0xff;
// Horizontal blanking pixels 8 lsbits (0-4095)
edid->b[57] = h_blank & 0xff;
// Bits 7-4 Horizontal active pixels 4 msbits
// Bits 3-0 Horizontal blanking pixels 4 msbits
edid->b[58] = (((h_active >> 8) & 0x0f) << 4) + ((h_blank >> 8) & 0x0f);
// Vertical active lines 8 lsbits (0-4095)
edid->b[59] = v_active & 0xff;
// Vertical blanking lines 8 lsbits (0-4095)
edid->b[60] = v_blank & 0xff;
// Bits 7-4 Vertical active lines 4 msbits
// Bits 3-0 Vertical blanking lines 4 msbits
edid->b[61] = (((v_active >> 8) & 0x0f) << 4) + ((v_blank >> 8) & 0x0f);
// Horizontal sync offset pixels 8 lsbits (0-1023) From blanking start
edid->b[62] = h_offset & 0xff;
// Horizontal sync pulse width pixels 8 lsbits (0-1023)
edid->b[63] = h_pulse & 0xff;
// Bits 7-4 Vertical sync offset lines 4 lsbits 0-63)
// Bits 3-0 Vertical sync pulse width lines 4 lsbits 0-63)
edid->b[64] = ((v_offset & 0x0f) << 4) + (v_pulse & 0x0f);
// Bits 7-6 Horizontal sync offset pixels 2 msbits
// Bits 5-4 Horizontal sync pulse width pixels 2 msbits
// Bits 3-2 Vertical sync offset lines 2 msbits
// Bits 1-0 Vertical sync pulse width lines 2 msbits
edid->b[65] = (((h_offset >> 8) & 0x03) << 6) +
(((h_pulse >> 8) & 0x03) << 4) +
(((v_offset >> 8) & 0x03) << 2) +
((v_pulse >> 8) & 0x03);
// Horizontal display size, mm, 8 lsbits (0-4095 mm, 161 in)
edid->b[66] = 485 & 0xff;
// Vertical display size, mm, 8 lsbits (0-4095 mm, 161 in)
edid->b[67] = 364 & 0xff;
// Bits 7-4 Horizontal display size, mm, 4 msbits
// Bits 3-0 Vertical display size, mm, 4 msbits
edid->b[68] = (((485 >> 8) & 0x0f) << 4) + ((364 >> 8) & 0x0f);
// Horizontal border pixels (each side; total is twice this)
edid->b[69] = 0;
// Vertical border lines (each side; total is twice this)
edid->b[70] = 0;
// Features bitmap
edid->b[71] = ((mode->interlace & 0x01) << 7) + 0x18 + (mode->vsync << 2) + (mode->hsync << 2);
// Descriptor: monitor serial number
edid->b[72] = 0;
edid->b[73] = 0;
edid->b[74] = 0;
edid->b[75] = 0xff;
edid->b[76] = 0;
edid->b[77] = 'S';
edid->b[78] = 'w';
edid->b[79] = 'i';
edid->b[80] = 't';
edid->b[81] = 'c';
edid->b[82] = 'h';
edid->b[83] = 'r';
edid->b[84] = 'e';
edid->b[85] = 's';
edid->b[86] = '2';
edid->b[87] = '0';
edid->b[88] = '0';
edid->b[89] = 0x0a;
// Descriptor: monitor range limits
edid->b[90] = 0;
edid->b[91] = 0;
edid->b[92] = 0;
edid->b[93] = 0xfd;
edid->b[94] = 0;
edid->b[95] = ((int)range->vfreq_min) & 0xff;
edid->b[96] = ((int)range->vfreq_max) & 0xff;
edid->b[97] = ((int)range->hfreq_min / 1000) & 0xff;
edid->b[98] = ((int)range->hfreq_max / 1000) & 0xff;
edid->b[99] = 0xff;
edid->b[100] = 0;
edid->b[101] = 0x0a;
edid->b[102] = 0x20;
edid->b[103] = 0x20;
edid->b[104] = 0x20;
edid->b[105] = 0x20;
edid->b[106] = 0x20;
edid->b[107] = 0x20;
// Descriptor: text
edid->b[108] = 0;
edid->b[109] = 0;
edid->b[110] = 0;
edid->b[111] = 0xfc;
edid->b[112] = 0;
snprintf(&edid->b[113], 13, "%s", name);
edid->b[125] = 0x0a;
// Extensions to follow
edid->b[126] = 0;
// Compute checksum
char checksum = 0;
int i;
for (i = 0; i <= 126; i++)
checksum += edid->b[i];
edid->b[127] = 256 - checksum;
return 1;
}

37
deps/switchres/edid.h vendored Normal file
View File

@ -0,0 +1,37 @@
/**************************************************************
edid.h - Basic EDID generation
(based on edid.S: EDID data template by Carsten Emde)
---------------------------------------------------------
Switchres Modeline generation engine for emulation
License GPL-2.0+
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
Alexandre Wodarczyk, Gil Delescluse
**************************************************************/
#ifndef __EDID_H__
#define __EDID_H__
//============================================================
// TYPE DEFINITIONS
//============================================================
typedef struct edid_block
{
char b[128];
/* char ext1[128];
char ext2[128];
char ext3[128];*/
} edid_block;
//============================================================
// PROTOTYPES
//============================================================
int edid_from_modeline(modeline *mode, monitor_range *range, char *name, edid_block *edid);
#endif

56
deps/switchres/examples/README.md vendored Normal file
View File

@ -0,0 +1,56 @@
# ANY OS - BASIC INFORMATION
## Build libswitchres
It supports cross compilation, and will build both dynamic and static libs as per the target OS
```bash
make libswitchres
```
## Basic usage as a client with examples
libswitchres can be called in 2 different ways (with example code):
* `test_dlopen.c` -> by explicitely opening a .so/.dll, import the srlib object and call associated functions
* `test_liblink.c` -> by simply linking libswitchres at build time
These options are generic whether you build for Linux or Windows
* -I ../ (to get libswitchres_wrapper.h)
* -L ../ or -L ./ (for win32, when the dll has been copied in the examples folder)
* -lswitchres to link the lib if not manually opening it in the code
#please note#: static libs aven't been tested yet
# LINUX
You'll need a few extra parameters for gcc:
* -ldl (will try later to find a way to statically link libdl.a)
When running, dont forget to add before the binary LD_LIBRARY_PATH=<libswitchres.so pass, even if it's ./>:$LD_LIBRARY_PATH
## Examples:
```bash
make libswitchres
cd examples
g++ -o linux_dl_test test_dlopen.cpp -I ../ -ldl
LD_LIBRARY_PATH=../:$LD_LIBRARY_PATH ./linux_dl_test
g++ -o linux_link_lib test_liblink.cpp -I ../ -L../ -lswitchres -ldl
LD_LIBRARY_PATH=../:$LD_LIBRARY_PATH ./linux_link_lib
```
# WINDOWS
Pretty much the same as Linux, but with mingw64. The resulting exe and dll can be tested with wine
## Examples (cross-building from windows)
```
make PLATFORM=NT CROSS_COMPILE=x86_64-w64-mingw32- libswitchres
(copy the dll to examples)
x86_64-w64-mingw32-g++-win32 test_dlopen.cpp -o w32_loaddll.exe -I ../ -static-libgcc -static-libstdc++
w32_loaddll.exe
x86_64-w64-mingw32-g++-win32 test_liblink.cpp -o w32_linkdll.exe -I ../ -static-libgcc -static-libstdc++ -L ./ -lswitchres
w32_linkdll.exe
```
Note that, when building w32_linkdll.exe, I couldn't point to another dir else than ./ with -L

81
deps/switchres/examples/test_dlopen.cpp vendored Normal file
View File

@ -0,0 +1,81 @@
#include <stdio.h>
#include <stdlib.h>
#ifdef __cplusplus
#include <cstring> // required for strcpy
#endif
#ifdef __linux__
#define LIBSWR "libswitchres.so"
#elif _WIN32
#define LIBSWR "libswitchres.dll"
#endif
#include <switchres/switchres_wrapper.h>
int main(int argc, char** argv) {
const char* err_msg;
printf("About to open %s.\n", LIBSWR);
// Load the lib
LIBTYPE dlp = OPENLIB(LIBSWR);
// Loading failed, inform and exit
if (!dlp) {
printf("Loading %s failed.\n", LIBSWR);
printf("Error: %s\n", LIBERROR());
exit(EXIT_FAILURE);
}
printf("Loading %s succeded.\n", LIBSWR);
// Load the init()
LIBERROR();
srAPI* SRobj = (srAPI*)LIBFUNC(dlp, "srlib");
if ((err_msg = LIBERROR()) != NULL) {
printf("Failed to load srAPI: %s\n", err_msg);
CLOSELIB(dlp);
exit(EXIT_FAILURE);
}
// Testing the function
printf("Init a new switchres_manager object:\n");
SRobj->init();
SRobj->sr_init_disp();
// Call mode + get result values
int w = 384, h = 224;
double rr = 59.583393;
unsigned char interlace = 0, ret;
sr_mode srm;
printf("Orignial resolution expected: %dx%d@%f-%d\n", w, h, rr, interlace);
ret = SRobj->sr_add_mode(w, h, rr, interlace, &srm);
if(!ret)
{
printf("ERROR: couldn't add the required mode. Exiting!\n");
SRobj->deinit();
exit(1);
}
printf("Got resolution: %dx%d%c@%f\n", srm.width, srm.height, srm.interlace, srm.refresh);
printf("Press Any Key to switch to new mode\n");
getchar();
ret = SRobj->sr_switch_to_mode(srm.width, srm.height, rr, srm.interlace, &srm);
if(!ret)
{
printf("ERROR: couldn't switch to the required mode. Exiting!\n");
SRobj->deinit();
exit(1);
}
printf("Press Any Key to quit.\n");
getchar();
// Clean the mess, kiss goodnight SR
SRobj->deinit();
// We're done, let's closer
CLOSELIB(dlp);
}

View File

@ -0,0 +1,30 @@
#include <stdio.h>
#include <stdlib.h>
#include <switchres/switchres_wrapper.h>
int main(int argc, char** argv) {
sr_mode srm;
unsigned char ret;
sr_init();
sr_init_disp();
ret = sr_add_mode(384, 224, 59.63, 0, &srm);
if(!ret)
{
printf("ERROR: couldn't add the required mode. Exiting!\n");
sr_deinit();
exit(1);
}
printf("SR returned resolution: %dx%d%c@%f\n", srm.width, srm.height, srm.interlace, srm.refresh);
ret = sr_switch_to_mode(384, 224, 59.63, 0, &srm);
if(!ret)
{
printf("ERROR: couldn't switch to the required mode. Exiting!\n");
sr_deinit();
exit(1);
}
sr_deinit();
}

228
deps/switchres/grid.cpp vendored Normal file
View File

@ -0,0 +1,228 @@
/**************************************************************
grid.cpp - Simple test grid
---------------------------------------------------------
Switchres Modeline generation engine for emulation
License GPL-2.0+
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
Alexandre Wodarczyk, Gil Delescluse
**************************************************************/
#define SDL_MAIN_HANDLED
#define NUM_GRIDS 2
#include <SDL2/SDL.h>
typedef struct grid_display
{
int index;
int width;
int height;
SDL_Window *window;
SDL_Renderer *renderer;
} GRID_DISPLAY;
//============================================================
// draw_grid
//============================================================
void draw_grid(int num_grid, int width, int height, SDL_Renderer *renderer)
{
// Clean the surface
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
SDL_RenderClear(renderer);
SDL_Rect rect {0, 0, width, height};
switch (num_grid)
{
case 0:
// 16 x 12 squares
{
// Fill the screen with red
rect = {0, 0, width, height};
SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
SDL_RenderFillRect(renderer, &rect);
// Draw white rectangle
rect = {width / 32, height / 24 , width - width / 16, height - height / 12};
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
SDL_RenderFillRect(renderer, &rect);
// Draw grid using black rectangles
SDL_Rect rects[16 * 12];
// Set the thickness of horizontal and vertical lines based on the screen resolution
int line_w = round(float(width) / 320.0);
int line_h = round(float(height) / 240.0);
if ( line_w < 1 ) line_w = 1;
if ( line_h < 1 ) line_h = 1;
float rect_w = (width - line_w * 17) / 16.0;
float rect_h = (height - line_h * 13) / 12.0;
for (int i = 0; i < 16; i++)
{
int x_pos1 = ceil(i * rect_w);
int x_pos2 = ceil((i+1) * rect_w);
for (int j = 0; j < 12; j++)
{
int y_pos1 = ceil(j * rect_h);
int y_pos2 = ceil((j+1) * rect_h);
rects[i + j * 16] = {x_pos1 + (i+1) * line_w , y_pos1 + (j+1) * line_h, x_pos2 - x_pos1, y_pos2 - y_pos1};
}
}
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderFillRects(renderer, rects, 16 * 12);
}
break;
case 1:
// cps2 grid
// Draw outer rectangle
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
SDL_RenderDrawRect(renderer, &rect);
for (int i = 0; i < width / 16; i++)
{
for (int j = 0; j < height / 16; j++)
{
if (i == 0 || j == 0 || i == (width / 16) - 1 || j == (height / 16) - 1)
SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
else
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
rect = {i * 16, j * 16, 16, 16};
SDL_RenderDrawRect(renderer, &rect);
rect = {i * 16 + 7, j * 16 + 7, 2, 2};
SDL_RenderDrawRect(renderer, &rect);
}
}
break;
}
SDL_RenderPresent(renderer);
}
//============================================================
// main
//============================================================
int main(int argc, char **argv)
{
SDL_Window* win_array[10] = {};
GRID_DISPLAY display_array[10] = {};
int display_total = 0;
// Initialize SDL
if (SDL_Init(SDL_INIT_VIDEO) != 0)
{
printf("error initializing SDL: %s\n", SDL_GetError());
return 1;
}
// Get target displays
if (argc > 1)
{
// Parse command line for display indexes
int display_index = 0;
int num_displays = SDL_GetNumVideoDisplays();
for (int arg = 1; arg < argc; arg++)
{
sscanf(argv[arg], "%d", &display_index);
if (display_index < 0 || display_index > num_displays - 1)
{
printf("error, bad display_index: %d\n", display_index);
return 1;
}
display_array[display_total].index = display_index;
display_total++;
}
}
else
{
// No display specified, use default
display_array[0].index = 0;
display_total = 1;
}
// Create windows
for (int disp = 0; disp < display_total; disp++)
{
// Get target display size
SDL_DisplayMode dm;
SDL_GetCurrentDisplayMode(display_array[disp].index, &dm);
SDL_ShowCursor(SDL_DISABLE);
display_array[disp].width = dm.w;
display_array[disp].height = dm.h;
// Create window
display_array[disp].window = SDL_CreateWindow("Switchres test grid", SDL_WINDOWPOS_CENTERED_DISPLAY(display_array[disp].index), SDL_WINDOWPOS_CENTERED, dm.w, dm.h, SDL_WINDOW_FULLSCREEN_DESKTOP);
// Required by Window multi-monitor
SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
// Create renderer
display_array[disp].renderer = SDL_CreateRenderer(display_array[disp].window, -1, SDL_RENDERER_ACCELERATED);
// Draw grid
draw_grid(0, display_array[disp].width, display_array[disp].height, display_array[disp].renderer);
}
// Wait for escape key
bool close = false;
int num_grid = 0;
while (!close)
{
SDL_Event event;
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_QUIT:
close = true;
break;
case SDL_KEYDOWN:
switch (event.key.keysym.scancode)
{
case SDL_SCANCODE_ESCAPE:
close = true;
break;
case SDL_SCANCODE_TAB:
num_grid ++;
for (int disp = 0; disp < display_total; disp++)
draw_grid(num_grid % NUM_GRIDS, display_array[disp].width, display_array[disp].height, display_array[disp].renderer);
break;
default:
break;
}
}
}
}
// Destroy all windows
for (int disp = 0; disp < display_total; disp++)
SDL_DestroyWindow(display_array[disp].window);
SDL_Quit();
return 0;
}

78
deps/switchres/log.cpp vendored Normal file
View File

@ -0,0 +1,78 @@
/**************************************************************
log.cpp - Simple logging for Switchres
---------------------------------------------------------
Switchres Modeline generation engine for emulation
License GPL-2.0+
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
Alexandre Wodarczyk, Gil Delescluse
**************************************************************/
#include "log.h"
enum log_verbosity { NONE, ERROR, INFO, DEBUG };
static log_verbosity log_level = INFO;
void log_dummy(const char *, ...) {}
LOG_VERBOSE log_verbose = &log_dummy;
LOG_INFO log_info = &log_dummy;
LOG_ERROR log_error = &log_dummy;
/*
* These bakup pointers are here to let the user modify the log level at runtime
* We can't sadly unify a log function and test the log level to test if it should
* output a log, because it would imply frewriting log_ functions with va_args
* and wouldn't work with emulators log functions anymore
*/
LOG_VERBOSE log_verbose_bak = &log_dummy;
LOG_INFO log_info_bak = &log_dummy;
LOG_ERROR log_error_bak = &log_dummy;
void set_log_verbose(void *func_ptr)
{
if (log_level >= DEBUG)
log_verbose = (LOG_VERBOSE)func_ptr;
log_verbose_bak = (LOG_VERBOSE)func_ptr;
}
void set_log_info(void *func_ptr)
{
if (log_level >= INFO)
log_info = (LOG_INFO)func_ptr;
log_info_bak = (LOG_INFO)func_ptr;
}
void set_log_error(void *func_ptr)
{
if (log_level >= ERROR)
log_error = (LOG_ERROR)func_ptr;
log_error_bak = (LOG_ERROR)func_ptr;
}
void set_log_verbosity(int level)
{
// Keep the log in the enum bounds
if (level < NONE)
level = NONE;
if(level > DEBUG)
level = DEBUG;
log_error = &log_dummy;
log_info = &log_dummy;
log_verbose = &log_dummy;
if (level >= ERROR)
log_error = log_error_bak;
if (level >= INFO)
log_info = log_info_bak;
if (level >= DEBUG)
log_verbose = log_verbose_bak;
}

38
deps/switchres/log.h vendored Normal file
View File

@ -0,0 +1,38 @@
/**************************************************************
log.h - Simple logging for Switchres
---------------------------------------------------------
Switchres Modeline generation engine for emulation
License GPL-2.0+
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
Alexandre Wodarczyk, Gil Delescluse
**************************************************************/
#ifndef __LOG__
#define __LOG__
#if defined(__GNUC__)
#define ATTR_PRINTF(x,y) __attribute__((format(printf, x, y)))
#else
#define ATTR_PRINTF(x,y)
#endif
typedef void (*LOG_VERBOSE)(const char *format, ...) ATTR_PRINTF(1,2);
extern LOG_VERBOSE log_verbose;
typedef void (*LOG_INFO)(const char *format, ...) ATTR_PRINTF(1,2);
extern LOG_INFO log_info;
typedef void (*LOG_ERROR)(const char *format, ...) ATTR_PRINTF(1,2);
extern LOG_ERROR log_error;
void set_log_verbosity(int);
void set_log_verbose(void *func_ptr);
void set_log_info(void *func_ptr);
void set_log_error(void *func_ptr);
#endif

771
deps/switchres/modeline.cpp vendored Normal file
View File

@ -0,0 +1,771 @@
/**************************************************************
modeline.cpp - Modeline generation and scoring routines
---------------------------------------------------------
Switchres Modeline generation engine for emulation
License GPL-2.0+
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
Alexandre Wodarczyk, Gil Delescluse
**************************************************************/
#include <stdio.h>
#include <string.h>
#include <cstddef>
#include "modeline.h"
#include "log.h"
#define max(a,b)({ __typeof__ (a) _a = (a);__typeof__ (b) _b = (b);_a > _b ? _a : _b; })
#define min(a,b)({ __typeof__ (a) _a = (a);__typeof__ (b) _b = (b);_a < _b ? _a : _b; })
//============================================================
// PROTOTYPES
//============================================================
int get_line_params(modeline *mode, monitor_range *range, int char_size);
int scale_into_range (int value, int lower_limit, int higher_limit);
int scale_into_range (double value, double lower_limit, double higher_limit);
int scale_into_aspect (int source_res, int tot_res, double original_monitor_aspect, double users_monitor_aspect, double *best_diff);
int stretch_into_range(double vfreq, monitor_range *range, double borders, bool interlace_allowed, double *interlace);
int total_lines_for_yres(int yres, double vfreq, monitor_range *range, double borders, double interlace);
double max_vfreq_for_yres (int yres, monitor_range *range, double borders, double interlace);
//============================================================
// modeline_create
//============================================================
int modeline_create(modeline *s_mode, modeline *t_mode, monitor_range *range, generator_settings *cs)
{
double vfreq_real = 0;
double interlace = 1;
double doublescan = 1;
double scan_factor = 1;
int x_scale = 0;
int y_scale = 0;
int v_scale = 0;
double x_diff = 0;
double y_diff = 0;
double v_diff = 0;
double y_ratio = 0;
double x_ratio = 0;
double borders = 0;
t_mode->result.weight = 0;
// ≈≈≈ Vertical refresh ≈≈≈
// try to fit vertical frequency into current range
v_scale = scale_into_range(t_mode->vfreq, range->vfreq_min, range->vfreq_max);
if (!v_scale && (t_mode->type & V_FREQ_EDITABLE))
{
t_mode->vfreq = t_mode->vfreq < range->vfreq_min? range->vfreq_min : range->vfreq_max;
v_scale = 1;
}
else if (v_scale != 1 && !(t_mode->type & V_FREQ_EDITABLE))
{
t_mode->result.weight |= R_OUT_OF_RANGE;
return -1;
}
// ≈≈≈ Vertical resolution ≈≈≈
// try to fit active lines in the progressive range first
if (range->progressive_lines_min && (!t_mode->interlace || (t_mode->type & SCAN_EDITABLE)))
y_scale = scale_into_range(t_mode->vactive, range->progressive_lines_min, range->progressive_lines_max);
// if not possible, try to fit in the interlaced range, if any
if (!y_scale && range->interlaced_lines_min && cs->interlace && (t_mode->interlace || (t_mode->type & SCAN_EDITABLE)))
{
y_scale = scale_into_range(t_mode->vactive, range->interlaced_lines_min, range->interlaced_lines_max);
interlace = 2;
}
// if we succeeded, let's see if we can apply integer scaling
if (y_scale == 1 || (y_scale > 1 && (t_mode->type & Y_RES_EDITABLE)))
{
// check if we should apply doublescan
if (cs->doublescan && y_scale % 2 == 0)
{
y_scale /= 2;
doublescan = 0.5;
}
scan_factor = interlace * doublescan;
// Calculate top border in case of multi-standard consumer TVs
if (cs->v_shift_correct)
borders = (range->progressive_lines_max - t_mode->vactive * y_scale / interlace) * (1.0 / range->hfreq_min) / 2;
// calculate expected achievable refresh for this height
vfreq_real = min(t_mode->vfreq * v_scale, max_vfreq_for_yres(t_mode->vactive * y_scale, range, borders, scan_factor));
if (vfreq_real != t_mode->vfreq * v_scale && !(t_mode->type & V_FREQ_EDITABLE))
{
t_mode->result.weight |= R_OUT_OF_RANGE;
return -1;
}
// calculate the ratio that our scaled yres represents with respect to the original height
y_ratio = double(t_mode->vactive) * y_scale / s_mode->vactive;
int y_source_scaled = s_mode->vactive * floor(y_ratio);
// if our original height doesn't fit the target height, we're forced to stretch
if (!y_source_scaled)
t_mode->result.weight |= R_RES_STRETCH;
// otherwise we try to perform integer scaling
else
{
// exclude lcd ranges from raw border computation
if (t_mode->type & V_FREQ_EDITABLE && range->progressive_lines_max - range->progressive_lines_min > 0)
{
// calculate y borders considering physical lines (instead of logical resolution)
int tot_yres = total_lines_for_yres(t_mode->vactive * y_scale, vfreq_real, range, borders, scan_factor);
int tot_source = total_lines_for_yres(y_source_scaled, t_mode->vfreq * v_scale, range, borders, scan_factor);
y_diff = tot_yres > tot_source?double(tot_yres % tot_source) / tot_yres * 100:0;
// we penalize for the logical lines we need to add in order to meet the user's lower active lines limit
int y_min = interlace == 2?range->interlaced_lines_min:range->progressive_lines_min;
int tot_rest = (y_min >= y_source_scaled / doublescan)? y_min % int(y_source_scaled / doublescan):0;
y_diff += double(tot_rest) / tot_yres * 100;
}
else
y_diff = double((t_mode->vactive * y_scale) % y_source_scaled) / (t_mode->vactive * y_scale) * 100;
// we save the integer ratio between source and target resolutions, this will be used for prescaling
y_scale = floor(y_ratio);
// now if the borders obtained are low enough (< 10%) we'll finally apply integer scaling
// otherwise we'll stretch the original resolution over the target one
if (!(y_ratio >= 1.0 && y_ratio < 16.0 && y_diff < 10.0))
t_mode->result.weight |= R_RES_STRETCH;
}
}
// otherwise, check if we're allowed to apply fractional scaling
else if (t_mode->type & Y_RES_EDITABLE)
t_mode->result.weight |= R_RES_STRETCH;
// if there's nothing we can do, we're out of range
else
{
t_mode->result.weight |= R_OUT_OF_RANGE;
return -1;
}
// ≈≈≈ Horizontal resolution ≈≈≈
// make the best possible adjustment of xres depending on what happened in the previous steps
// let's start with the SCALED case
if (!(t_mode->result.weight & R_RES_STRETCH))
{
// apply integer scaling to yres
if (t_mode->type & Y_RES_EDITABLE) t_mode->vactive *= y_scale;
// if we can, let's apply the same scaling to both directions
if (t_mode->type & X_RES_EDITABLE)
{
x_scale = y_scale;
double aspect_corrector = max(1.0f, cs->monitor_aspect / (cs->rotation? (1.0/(STANDARD_CRT_ASPECT)) : (STANDARD_CRT_ASPECT)));
t_mode->hactive = normalize(double(t_mode->hactive) * double(x_scale) * aspect_corrector, 8);
}
// otherwise, try to get the best out of our current xres
else
{
x_scale = t_mode->hactive / s_mode->hactive;
// if the source width fits our xres, try applying integer scaling
if (x_scale)
{
x_scale = scale_into_aspect(s_mode->hactive, t_mode->hactive, cs->rotation?1.0/(STANDARD_CRT_ASPECT):STANDARD_CRT_ASPECT, cs->monitor_aspect, &x_diff);
if (x_diff > 15.0 && t_mode->hactive < cs->super_width)
t_mode->result.weight |= R_RES_STRETCH;
}
// otherwise apply fractional scaling
else
t_mode->result.weight |= R_RES_STRETCH;
}
}
// if the result was fractional scaling in any of the previous steps, deal with it
if (t_mode->result.weight & R_RES_STRETCH)
{
if (t_mode->type & Y_RES_EDITABLE)
{
// always try to use the interlaced range first if it exists, for better resolution
t_mode->vactive = stretch_into_range(t_mode->vfreq * v_scale, range, borders, cs->interlace, &interlace);
// check in case we couldn't achieve the desired refresh
vfreq_real = min(t_mode->vfreq * v_scale, max_vfreq_for_yres(t_mode->vactive, range, borders, interlace));
}
// check if we can create a normal aspect resolution
if (t_mode->type & X_RES_EDITABLE)
t_mode->hactive = max(t_mode->hactive, normalize(STANDARD_CRT_ASPECT * t_mode->vactive, 8));
// calculate integer scale for prescaling
x_scale = max(1, scale_into_aspect(s_mode->hactive, t_mode->hactive, cs->rotation?1.0/(STANDARD_CRT_ASPECT):STANDARD_CRT_ASPECT, cs->monitor_aspect, &x_diff));
y_scale = max(1, floor(double(t_mode->vactive) / s_mode->vactive));
scan_factor = interlace;
doublescan = 1;
}
x_ratio = double(t_mode->hactive) / s_mode->hactive;
y_ratio = double(t_mode->vactive) / s_mode->vactive;
v_scale = max(round_near(vfreq_real / s_mode->vfreq), 1);
v_diff = (vfreq_real / v_scale) - s_mode->vfreq;
if (fabs(v_diff) > cs->refresh_tolerance)
t_mode->result.weight |= R_V_FREQ_OFF;
// ≈≈≈ Modeline generation ≈≈≈
// compute new modeline if we are allowed to
if (t_mode->type & V_FREQ_EDITABLE)
{
double margin = 0;
double vblank_lines = 0;
double vvt_ini = 0;
// Get resulting refresh
t_mode->vfreq = vfreq_real;
// Get total vertical lines
vvt_ini = total_lines_for_yres(t_mode->vactive, t_mode->vfreq, range, borders, scan_factor) + (interlace == 2?0.5:0);
// Calculate horizontal frequency
t_mode->hfreq = t_mode->vfreq * vvt_ini;
horizontal_values:
// Fill horizontal part of modeline
get_line_params(t_mode, range, cs->pixel_precision? 1 : 8);
// Calculate pixel clock
t_mode->pclock = t_mode->htotal * t_mode->hfreq;
if (t_mode->pclock <= cs->pclock_min)
{
if (t_mode->type & X_RES_EDITABLE)
{
x_scale *= 2;
t_mode->hactive *= 2;
goto horizontal_values;
}
else
{
t_mode->result.weight |= R_OUT_OF_RANGE;
return -1;
}
}
// Vertical blanking
t_mode->vtotal = vvt_ini * scan_factor;
vblank_lines = int(t_mode->hfreq * (range->vertical_blank + borders)) + (interlace == 2?0.5:0);
margin = (t_mode->vtotal - t_mode->vactive - vblank_lines * scan_factor) / (cs->v_shift_correct? 1 : 2);
t_mode->vbegin = t_mode->vactive + max(round_near(t_mode->hfreq * range->vfront_porch * scan_factor + margin), 1);
t_mode->vend = t_mode->vbegin + max(round_near(t_mode->hfreq * range->vsync_pulse * scan_factor), 1);
// Recalculate final vfreq
t_mode->vfreq = (t_mode->hfreq / t_mode->vtotal) * scan_factor;
t_mode->hsync = range->hsync_polarity;
t_mode->vsync = range->vsync_polarity;
t_mode->interlace = interlace == 2?1:0;
t_mode->doublescan = doublescan == 1?0:1;
// Apply interlace fixes
if (cs->interlace_force_even && interlace == 2)
{
t_mode->vbegin = (t_mode->vbegin / 2) * 2;
t_mode->vend = (t_mode->vend / 2) * 2;
t_mode->vtotal++;
}
}
// finally, store result
t_mode->result.scan_penalty = (s_mode->interlace != t_mode->interlace? 1:0) + (s_mode->doublescan != t_mode->doublescan? 1:0);
t_mode->result.x_scale = x_scale;
t_mode->result.y_scale = y_scale;
t_mode->result.v_scale = v_scale;
t_mode->result.x_diff = x_diff;
t_mode->result.y_diff = y_diff;
t_mode->result.v_diff = v_diff;
t_mode->result.x_ratio = x_ratio;
t_mode->result.y_ratio = y_ratio;
t_mode->result.v_ratio = 0;
return 0;
}
//============================================================
// get_line_params
//============================================================
int get_line_params(modeline *mode, monitor_range *range, int char_size)
{
int hhi, hhf, hht;
int hh, hs, he, ht;
double line_time, char_time, new_char_time;
double hfront_porch_min, hsync_pulse_min, hback_porch_min;
hfront_porch_min = range->hfront_porch * .90;
hsync_pulse_min = range->hsync_pulse * .90;
hback_porch_min = range->hback_porch * .90;
line_time = 1 / mode->hfreq * 1000000;
hh = round(mode->hactive / char_size);
hs = he = ht = 1;
do {
char_time = line_time / (hh + hs + he + ht);
if (hs * char_time < hfront_porch_min ||
fabs((hs + 1) * char_time - range->hfront_porch) < fabs(hs * char_time - range->hfront_porch))
hs++;
if (he * char_time < hsync_pulse_min ||
fabs((he + 1) * char_time - range->hsync_pulse) < fabs(he * char_time - range->hsync_pulse))
he++;
if (ht * char_time < hback_porch_min ||
fabs((ht + 1) * char_time - range->hback_porch) < fabs(ht * char_time - range->hback_porch))
ht++;
new_char_time = line_time / (hh + hs + he + ht);
} while (new_char_time != char_time);
hhi = (hh + hs) * char_size;
hhf = (hh + hs + he) * char_size;
hht = (hh + hs + he + ht) * char_size;
mode->hbegin = hhi;
mode->hend = hhf;
mode->htotal = hht;
return 0;
}
//============================================================
// scale_into_range
//============================================================
int scale_into_range (int value, int lower_limit, int higher_limit)
{
int scale = 1;
while (value * scale < lower_limit) scale ++;
if (value * scale <= higher_limit)
return scale;
else
return 0;
}
//============================================================
// scale_into_range
//============================================================
int scale_into_range (double value, double lower_limit, double higher_limit)
{
int scale = 1;
while (value * scale < lower_limit) scale ++;
if (value * scale <= higher_limit)
return scale;
else
return 0;
}
//============================================================
// scale_into_aspect
//============================================================
int scale_into_aspect (int source_res, int tot_res, double original_monitor_aspect, double users_monitor_aspect, double *best_diff)
{
int scale = 1, best_scale = 1;
double diff = 0;
*best_diff = 0;
while (source_res * scale <= tot_res)
{
diff = fabs(1.0 - (users_monitor_aspect / (double(tot_res) / double(source_res * scale) * original_monitor_aspect))) * 100.0;
if (diff < *best_diff || *best_diff == 0)
{
*best_diff = diff;
best_scale = scale;
}
scale ++;
}
return best_scale;
}
//============================================================
// stretch_into_range
//============================================================
int stretch_into_range(double vfreq, monitor_range *range, double borders, bool interlace_allowed, double *interlace)
{
int yres, lower_limit;
if (range->interlaced_lines_min && interlace_allowed)
{
yres = range->interlaced_lines_max;
lower_limit = range->interlaced_lines_min;
*interlace = 2;
}
else
{
yres = range->progressive_lines_max;
lower_limit = range->progressive_lines_min;
}
while (yres > lower_limit && max_vfreq_for_yres(yres, range, borders, *interlace) < vfreq)
yres -= 8;
return yres;
}
//============================================================
// total_lines_for_yres
//============================================================
int total_lines_for_yres(int yres, double vfreq, monitor_range *range, double borders, double interlace)
{
int vvt = max(yres / interlace + round_near(vfreq * yres / (interlace * (1.0 - vfreq * (range->vertical_blank + borders))) * (range->vertical_blank + borders)), 1);
while ((vfreq * vvt < range->hfreq_min) && (vfreq * (vvt + 1) < range->hfreq_max)) vvt++;
return vvt;
}
//============================================================
// max_vfreq_for_yres
//============================================================
double max_vfreq_for_yres (int yres, monitor_range *range, double borders, double interlace)
{
return range->hfreq_max / (yres / interlace + round_near(range->hfreq_max * (range->vertical_blank + borders)));
}
//============================================================
// modeline_print
//============================================================
char * modeline_print(modeline *mode, char *modeline, int flags)
{
char label[48]={'\x00'};
char params[192]={'\x00'};
if (flags & MS_LABEL)
sprintf(label, "\"%dx%d_%d%s %.6fKHz %.6fHz\"", mode->hactive, mode->vactive, mode->refresh, mode->interlace?"i":"", mode->hfreq/1000, mode->vfreq);
if (flags & MS_LABEL_SDL)
sprintf(label, "\"%dx%d_%.6f\"", mode->hactive, mode->vactive, mode->vfreq);
if (flags & MS_PARAMS)
sprintf(params, " %.6f %d %d %d %d %d %d %d %d %s %s %s %s", double(mode->pclock)/1000000.0, mode->hactive, mode->hbegin, mode->hend, mode->htotal, mode->vactive, mode->vbegin, mode->vend, mode->vtotal,
mode->interlace?"interlace":"", mode->doublescan?"doublescan":"", mode->hsync?"+hsync":"-hsync", mode->vsync?"+vsync":"-vsync");
sprintf(modeline, "%s%s", label, params);
return modeline;
}
//============================================================
// modeline_result
//============================================================
char * modeline_result(modeline *mode, char *result)
{
log_verbose(" rng(%d): ", mode->range);
if (mode->result.weight & R_OUT_OF_RANGE)
sprintf(result, " out of range");
else
sprintf(result, "%4d x%4d_%3.6f%s%s %3.6f [%s] scale(%d, %d, %d) diff(%.2f, %.2f, %.4f) ratio(%.3f, %.3f)",
mode->hactive, mode->vactive, mode->vfreq, mode->interlace?"i":"p", mode->doublescan?"d":"", mode->hfreq/1000, mode->result.weight & R_RES_STRETCH?"fract":"integ",
mode->result.x_scale, mode->result.y_scale, mode->result.v_scale, mode->result.x_diff, mode->result.y_diff, mode->result.v_diff, mode->result.x_ratio, mode->result.y_ratio);
return result;
}
//============================================================
// modeline_compare
//============================================================
int modeline_compare(modeline *t, modeline *best)
{
bool vector = (t->hactive == (int)t->result.x_ratio);
if (t->result.weight < best->result.weight)
return 1;
else if (t->result.weight <= best->result.weight)
{
double t_v_diff = fabs(t->result.v_diff);
double b_v_diff = fabs(best->result.v_diff);
if (t->result.weight & R_RES_STRETCH || vector)
{
double t_y_score = t->result.y_ratio * (t->interlace?(2.0/3.0):1.0);
double b_y_score = best->result.y_ratio * (best->interlace?(2.0/3.0):1.0);
if ((t_v_diff < b_v_diff) ||
((t_v_diff == b_v_diff) && (t_y_score > b_y_score)) ||
((t_v_diff == b_v_diff) && (t_y_score == b_y_score) && (t->result.x_ratio > best->result.x_ratio)))
return 1;
}
else
{
int t_y_score = t->result.y_scale + t->result.scan_penalty;
int b_y_score = best->result.y_scale + best->result.scan_penalty;
double xy_diff = roundf((t->result.x_diff + t->result.y_diff) * 100) / 100;
double best_xy_diff = roundf((best->result.x_diff + best->result.y_diff) * 100) / 100;
if ((t_y_score < b_y_score) ||
((t_y_score == b_y_score) && (xy_diff < best_xy_diff)) ||
((t_y_score == b_y_score) && (xy_diff == best_xy_diff) && (t->result.x_scale < best->result.x_scale)) ||
((t_y_score == b_y_score) && (xy_diff == best_xy_diff) && (t->result.x_scale == best->result.x_scale) && (t_v_diff < b_v_diff)))
return 1;
}
}
return 0;
}
//============================================================
// modeline_vesa_gtf
// Based on the VESA GTF spreadsheet by Andy Morrish 1/5/97
//============================================================
int modeline_vesa_gtf(modeline *m)
{
int C, M;
int v_sync_lines, v_porch_lines_min, v_front_porch_lines, v_back_porch_lines, v_sync_v_back_porch_lines, v_total_lines;
int h_sync_width_percent, h_sync_width_pixels, h_blanking_pixels, h_front_porch_pixels, h_total_pixels;
double v_freq, v_freq_est, v_freq_real, v_sync_v_back_porch;
double h_freq, h_period, h_period_real, h_ideal_blanking;
double pixel_freq, interlace;
// Check if there's a value defined for vfreq. We're assuming input vfreq is the total field vfreq regardless interlace
v_freq = m->vfreq? m->vfreq:double(m->refresh);
// These values are GTF defined defaults
v_sync_lines = 3;
v_porch_lines_min = 1;
v_front_porch_lines = v_porch_lines_min;
v_sync_v_back_porch = 550;
h_sync_width_percent = 8;
M = 128.0 / 256 * 600;
C = ((40 - 20) * 128.0 / 256) + 20;
// GTF calculation
interlace = m->interlace?0.5:0;
h_period = ((1.0 / v_freq) - (v_sync_v_back_porch / 1000000)) / ((double)m->height + v_front_porch_lines + interlace) * 1000000;
v_sync_v_back_porch_lines = round_near(v_sync_v_back_porch / h_period);
v_back_porch_lines = v_sync_v_back_porch_lines - v_sync_lines;
v_total_lines = m->height + v_front_porch_lines + v_sync_lines + v_back_porch_lines;
v_freq_est = (1.0 / h_period) / v_total_lines * 1000000;
h_period_real = h_period / (v_freq / v_freq_est);
v_freq_real = (1.0 / h_period_real) / v_total_lines * 1000000;
h_ideal_blanking = double(C - (M * h_period_real / 1000));
h_blanking_pixels = round_near(m->width * h_ideal_blanking /(100 - h_ideal_blanking) / (2 * 8)) * (2 * 8);
h_total_pixels = m->width + h_blanking_pixels;
pixel_freq = h_total_pixels / h_period_real * 1000000;
h_freq = 1000000 / h_period_real;
h_sync_width_pixels = round_near(h_sync_width_percent * h_total_pixels / 100 / 8) * 8;
h_front_porch_pixels = (h_blanking_pixels / 2) - h_sync_width_pixels;
// Results
m->hactive = m->width;
m->hbegin = m->hactive + h_front_porch_pixels;
m->hend = m->hbegin + h_sync_width_pixels;
m->htotal = h_total_pixels;
m->vactive = m->height;
m->vbegin = m->vactive + v_front_porch_lines;
m->vend = m->vbegin + v_sync_lines;
m->vtotal = v_total_lines;
m->hfreq = h_freq;
m->vfreq = v_freq_real;
m->pclock = pixel_freq;
m->hsync = 0;
m->vsync = 1;
return true;
}
//============================================================
// modeline_parse
//============================================================
int modeline_parse(const char *user_modeline, modeline *mode)
{
char modeline_txt[256]={'\x00'};
if (!strcmp(user_modeline, "auto"))
return false;
// Remove quotes
char *quote_start, *quote_end;
quote_start = strstr((char*)user_modeline, "\"");
if (quote_start)
{
quote_start++;
quote_end = strstr(quote_start, "\"");
if (!quote_end || *quote_end++ == 0)
return false;
user_modeline = quote_end;
}
// Get timing flags
mode->interlace = strstr(user_modeline, "interlace")?1:0;
mode->doublescan = strstr(user_modeline, "doublescan")?1:0;
mode->hsync = strstr(user_modeline, "+hsync")?1:0;
mode->vsync = strstr(user_modeline, "+vsync")?1:0;
// Get timing values
double pclock;
int e = sscanf(user_modeline, " %lf %d %d %d %d %d %d %d %d",
&pclock,
&mode->hactive, &mode->hbegin, &mode->hend, &mode->htotal,
&mode->vactive, &mode->vbegin, &mode->vend, &mode->vtotal);
if (e != 9)
{
log_error("SwitchRes: missing parameter in user modeline\n %s\n", user_modeline);
memset(mode, 0, sizeof(struct modeline));
return false;
}
// Calculate timings
mode->pclock = pclock * 1000000.0;
mode->hfreq = mode->pclock / mode->htotal;
mode->vfreq = mode->hfreq / mode->vtotal * (mode->interlace?2:1);
mode->refresh = mode->vfreq;
mode->width = mode->hactive;
mode->height = mode->vactive;
log_verbose("SwitchRes: user modeline %s\n", modeline_print(mode, modeline_txt, MS_FULL));
return true;
}
//============================================================
// modeline_to_monitor_range
//============================================================
int modeline_to_monitor_range(monitor_range *range, modeline *mode)
{
if (range->vfreq_min == 0)
{
range->vfreq_min = mode->vfreq - 0.2;
range->vfreq_max = mode->vfreq + 0.2;
}
double line_time = 1 / mode->hfreq;
double pixel_time = line_time / mode->htotal * 1000000;
range->hfront_porch = pixel_time * (mode->hbegin - mode->hactive);
range->hsync_pulse = pixel_time * (mode->hend - mode->hbegin);
range->hback_porch = pixel_time * (mode->htotal - mode->hend);
range->vfront_porch = line_time * (mode->vbegin - mode->vactive);
range->vsync_pulse = line_time * (mode->vend - mode->vbegin);
range->vback_porch = line_time * (mode->vtotal - mode->vend);
range->vertical_blank = range->vfront_porch + range->vsync_pulse + range->vback_porch;
range->hsync_polarity = mode->hsync;
range->vsync_polarity = mode->vsync;
range->progressive_lines_min = mode->interlace?0:mode->vactive;
range->progressive_lines_max = mode->interlace?0:mode->vactive;
range->interlaced_lines_min = mode->interlace?mode->vactive:0;
range->interlaced_lines_max= mode->interlace?mode->vactive:0;
range->hfreq_min = range->vfreq_min * mode->vtotal;
range->hfreq_max = range->vfreq_max * mode->vtotal;
return 1;
}
//============================================================
// modeline_is_different
//============================================================
int modeline_is_different(modeline *n, modeline *p)
{
// Remove on last fields in modeline comparison
return memcmp(n, p, offsetof(struct modeline, vfreq));
}
//============================================================
// monitor_fill_vesa_gtf
//============================================================
int monitor_fill_vesa_gtf(monitor_range *range, const char *max_lines)
{
int lines = 0;
sscanf(max_lines, "vesa_%d", &lines);
if (!lines)
return 0;
int i = 0;
if (lines >= 480)
i += monitor_fill_vesa_range(&range[i], 384, 480);
if (lines >= 600)
i += monitor_fill_vesa_range(&range[i], 480, 600);
if (lines >= 768)
i += monitor_fill_vesa_range(&range[i], 600, 768);
if (lines >= 1024)
i += monitor_fill_vesa_range(&range[i], 768, 1024);
return i;
}
//============================================================
// monitor_fill_vesa_range
//============================================================
int monitor_fill_vesa_range(monitor_range *range, int lines_min, int lines_max)
{
modeline mode;
memset(&mode, 0, sizeof(modeline));
mode.width = real_res(STANDARD_CRT_ASPECT * lines_max);
mode.height = lines_max;
mode.refresh = 60;
range->vfreq_min = 50;
range->vfreq_max = 65;
modeline_vesa_gtf(&mode);
modeline_to_monitor_range(range, &mode);
range->progressive_lines_min = lines_min;
range->hfreq_min = mode.hfreq - 500;
range->hfreq_max = mode.hfreq + 500;
monitor_show_range(range);
return 1;
}
//============================================================
// round_near
//============================================================
int round_near(double number)
{
return number < 0.0 ? ceil(number - 0.5) : floor(number + 0.5);
}
//============================================================
// normalize
//============================================================
int normalize(int a, int b)
{
int c, d;
c = a % b;
d = a / b;
if (c) d++;
return d * b;
}
//============================================================
// real_res
//============================================================
int real_res(int x) {return (int) (x / 8) * 8;}

140
deps/switchres/modeline.h vendored Normal file
View File

@ -0,0 +1,140 @@
/**************************************************************
modeline.h - Modeline generation header
---------------------------------------------------------
Switchres Modeline generation engine for emulation
License GPL-2.0+
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
Alexandre Wodarczyk, Gil Delescluse
**************************************************************/
#ifndef __MODELINE_H__
#define __MODELINE_H__
#include <stdint.h>
#include <math.h>
#include <cstddef>
#include "monitor.h"
//============================================================
// CONSTANTS
//============================================================
// Modeline print flags
#define MS_LABEL 0x00000001
#define MS_LABEL_SDL 0x00000002
#define MS_PARAMS 0x00000004
#define MS_FULL MS_LABEL | MS_PARAMS
// Modeline result
#define R_V_FREQ_OFF 0x00000001
#define R_RES_STRETCH 0x00000002
#define R_OUT_OF_RANGE 0x00000004
// Mode types
#define MODE_OK 0x00000000
#define MODE_DESKTOP 0x01000000
#define MODE_ROTATED 0x02000000
#define MODE_DISABLED 0x04000000
#define MODE_USER_DEF 0x08000000
#define MODE_UPDATE 0x10000000
#define MODE_ADD 0x20000000
#define MODE_DELETE 0x40000000
#define MODE_ERROR 0x80000000
#define V_FREQ_EDITABLE 0x00000001
#define X_RES_EDITABLE 0x00000002
#define Y_RES_EDITABLE 0x00000004
#define SCAN_EDITABLE 0x00000008
#define XYV_EDITABLE (X_RES_EDITABLE | Y_RES_EDITABLE | V_FREQ_EDITABLE )
#define DUMMY_WIDTH 1234
#define MAX_MODELINES 256
//============================================================
// TYPE DEFINITIONS
//============================================================
typedef struct mode_result
{
int weight;
int scan_penalty;
int x_scale;
int y_scale;
int v_scale;
double x_diff;
double y_diff;
double v_diff;
double x_ratio;
double y_ratio;
double v_ratio;
} mode_result;
typedef struct modeline
{
uint64_t pclock;
int hactive;
int hbegin;
int hend;
int htotal;
int vactive;
int vbegin;
int vend;
int vtotal;
int interlace;
int doublescan;
int hsync;
int vsync;
//
double vfreq;
double hfreq;
//
int width;
int height;
int refresh;
int refresh_label;
//
int type;
int range;
uint64_t platform_data;
//
mode_result result;
} modeline;
typedef struct generator_settings
{
int interlace;
int doublescan;
uint64_t pclock_min;
bool rotation;
double monitor_aspect;
double refresh_tolerance;
int super_width;
int v_shift_correct;
int pixel_precision;
int interlace_force_even;
} generator_settings;
//============================================================
// PROTOTYPES
//============================================================
int modeline_create(modeline *s_mode, modeline *t_mode, monitor_range *range, generator_settings *cs);
int modeline_compare(modeline *t_mode, modeline *best_mode);
char * modeline_print(modeline *mode, char *modeline, int flags);
char * modeline_result(modeline *mode, char *result);
int modeline_vesa_gtf(modeline *m);
int modeline_parse(const char *user_modeline, modeline *mode);
int modeline_to_monitor_range(monitor_range *range, modeline *mode);
int modeline_is_different(modeline *n, modeline *p);
int round_near(double number);
int normalize(int a, int b);
int real_res(int x);
#endif

431
deps/switchres/monitor.cpp vendored Normal file
View File

@ -0,0 +1,431 @@
/**************************************************************
monitor.cpp - Monitor presets and custom monitor definition
---------------------------------------------------------
Switchres Modeline generation engine for emulation
License GPL-2.0+
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
Alexandre Wodarczyk, Gil Delescluse
**************************************************************/
#include <stdio.h>
#include <string.h>
#include "monitor.h"
#include "log.h"
//============================================================
// CONSTANTS
//============================================================
#define HFREQ_MIN 14000
#define HFREQ_MAX 540672 // 8192 * 1.1 * 60
#define VFREQ_MIN 40
#define VFREQ_MAX 200
#define PROGRESSIVE_LINES_MIN 128
//============================================================
// monitor_fill_range
//============================================================
int monitor_fill_range(monitor_range *range, const char *specs_line)
{
monitor_range new_range;
if (strcmp(specs_line, "auto")) {
int e = sscanf(specs_line, "%lf-%lf,%lf-%lf,%lf,%lf,%lf,%lf,%lf,%lf,%d,%d,%d,%d,%d,%d",
&new_range.hfreq_min, &new_range.hfreq_max,
&new_range.vfreq_min, &new_range.vfreq_max,
&new_range.hfront_porch, &new_range.hsync_pulse, &new_range.hback_porch,
&new_range.vfront_porch, &new_range.vsync_pulse, &new_range.vback_porch,
&new_range.hsync_polarity, &new_range.vsync_polarity,
&new_range.progressive_lines_min, &new_range.progressive_lines_max,
&new_range.interlaced_lines_min, &new_range.interlaced_lines_max);
if (e != 16) {
log_error("Switchres: Error trying to fill monitor range with\n %s\n", specs_line);
return -1;
}
new_range.vfront_porch /= 1000;
new_range.vsync_pulse /= 1000;
new_range.vback_porch /= 1000;
new_range.vertical_blank = (new_range.vfront_porch + new_range.vsync_pulse + new_range.vback_porch);
if (monitor_evaluate_range(&new_range))
{
log_error("Switchres: Error in monitor range (ignoring): %s\n", specs_line);
return -1;
}
else
{
memcpy(range, &new_range, sizeof(struct monitor_range));
monitor_show_range(range);
}
}
return 0;
}
//============================================================
// monitor_fill_lcd_range
//============================================================
int monitor_fill_lcd_range(monitor_range *range, const char *specs_line)
{
if (strcmp(specs_line, "auto"))
{
if (sscanf(specs_line, "%lf-%lf", &range->vfreq_min, &range->vfreq_max) == 2)
{
log_verbose("Switchres: LCD vfreq range set by user as %f-%f\n", range->vfreq_min, range->vfreq_max);
return true;
}
else
log_error("Switchres: Error trying to fill LCD range with\n %s\n", specs_line);
}
// Use default values
range->vfreq_min = 59;
range->vfreq_max = 61;
log_verbose("Switchres: Using default vfreq range for LCD %f-%f\n", range->vfreq_min, range->vfreq_max);
return 0;
}
//============================================================
// monitor_show_range
//============================================================
int monitor_show_range(monitor_range *range)
{
log_verbose("Switchres: Monitor range %.2f-%.2f,%.2f-%.2f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%d,%d,%d,%d,%d,%d\n",
range->hfreq_min, range->hfreq_max,
range->vfreq_min, range->vfreq_max,
range->hfront_porch, range->hsync_pulse, range->hback_porch,
range->vfront_porch * 1000, range->vsync_pulse * 1000, range->vback_porch * 1000,
range->hsync_polarity, range->vsync_polarity,
range->progressive_lines_min, range->progressive_lines_max,
range->interlaced_lines_min, range->interlaced_lines_max);
return 0;
}
//============================================================
// monitor_set_preset
//============================================================
int monitor_set_preset(char *type, monitor_range *range)
{
// PAL TV - 50 Hz/625
if (!strcmp(type, "pal"))
{
monitor_fill_range(&range[0], "15625.00-15625.00, 50.00-50.00, 1.500, 4.700, 5.800, 0.064, 0.160, 1.056, 0, 0, 192, 288, 448, 576");
return 1;
}
// NTSC TV - 60 Hz/525
else if (!strcmp(type, "ntsc"))
{
monitor_fill_range(&range[0], "15734.26-15734.26, 59.94-59.94, 1.500, 4.700, 4.700, 0.191, 0.191, 0.953, 0, 0, 192, 240, 448, 480");
return 1;
}
// Generic 15.7 kHz
else if (!strcmp(type, "generic_15"))
{
monitor_fill_range(&range[0], "15625-15750, 49.50-65.00, 2.000, 4.700, 8.000, 0.064, 0.192, 1.024, 0, 0, 192, 288, 448, 576");
return 1;
}
// Arcade 15.7 kHz - standard resolution
else if (!strcmp(type, "arcade_15"))
{
monitor_fill_range(&range[0], "15625-16200, 49.50-65.00, 2.000, 4.700, 8.000, 0.064, 0.192, 1.024, 0, 0, 192, 288, 448, 576");
return 1;
}
// Arcade 15.7-16.5 kHz - extended resolution
else if (!strcmp(type, "arcade_15ex"))
{
monitor_fill_range(&range[0], "15625-16500, 49.50-65.00, 2.000, 4.700, 8.000, 0.064, 0.192, 1.024, 0, 0, 192, 288, 448, 576");
return 1;
}
// Arcade 25.0 kHz - medium resolution
else if (!strcmp(type, "arcade_25"))
{
monitor_fill_range(&range[0], "24960-24960, 49.50-65.00, 0.800, 4.000, 3.200, 0.080, 0.200, 1.000, 0, 0, 384, 400, 768, 800");
return 1;
}
// Arcade 31.5 kHz - medium resolution
else if (!strcmp(type, "arcade_31"))
{
monitor_fill_range(&range[0], "31400-31500, 49.50-65.00, 0.940, 3.770, 1.890, 0.349, 0.064, 1.017, 0, 0, 400, 512, 0, 0");
return 1;
}
// Arcade 15.7/25.0 kHz - dual-sync
else if (!strcmp(type, "arcade_15_25"))
{
monitor_fill_range(&range[0], "15625-16200, 49.50-65.00, 2.000, 4.700, 8.000, 0.064, 0.192, 1.024, 0, 0, 192, 288, 448, 576");
monitor_fill_range(&range[1], "24960-24960, 49.50-65.00, 0.800, 4.000, 3.200, 0.080, 0.200, 1.000, 0, 0, 384, 400, 768, 800");
return 2;
}
// Arcade 15.7/31.5 kHz - dual-sync
else if (!strcmp(type, "arcade_15_31"))
{
monitor_fill_range(&range[0], "15625-16200, 49.50-65.00, 2.000, 4.700, 8.000, 0.064, 0.192, 1.024, 0, 0, 192, 288, 448, 576");
monitor_fill_range(&range[1], "31400-31500, 49.50-65.00, 0.940, 3.770, 1.890, 0.349, 0.064, 1.017, 0, 0, 400, 512, 0, 0");
return 2;
}
// Arcade 15.7/25.0/31.5 kHz - tri-sync
else if (!strcmp(type, "arcade_15_25_31"))
{
monitor_fill_range(&range[0], "15625-16200, 49.50-65.00, 2.000, 4.700, 8.000, 0.064, 0.192, 1.024, 0, 0, 192, 288, 448, 576");
monitor_fill_range(&range[1], "24960-24960, 49.50-65.00, 0.800, 4.000, 3.200, 0.080, 0.200, 1.000, 0, 0, 384, 400, 768, 800");
monitor_fill_range(&range[2], "31400-31500, 49.50-65.00, 0.940, 3.770, 1.890, 0.349, 0.064, 1.017, 0, 0, 400, 512, 0, 0");
return 3;
}
// Makvision 2929D
else if (!strcmp(type, "m2929"))
{
monitor_fill_range(&range[0], "30000-40000, 47.00-90.00, 0.600, 2.500, 2.800, 0.032, 0.096, 0.448, 0, 0, 384, 640, 0, 0");
return 1;
}
// Wells Gardner D9800, D9400
else if (!strcmp(type, "d9800") || !strcmp(type, "d9400"))
{
monitor_fill_range(&range[0], "15250-18000, 40-80, 2.187, 4.688, 6.719, 0.190, 0.191, 1.018, 0, 0, 224, 288, 448, 576");
monitor_fill_range(&range[1], "18001-19000, 40-80, 2.187, 4.688, 6.719, 0.140, 0.191, 0.950, 0, 0, 288, 320, 0, 0");
monitor_fill_range(&range[2], "20501-29000, 40-80, 2.910, 3.000, 4.440, 0.451, 0.164, 1.048, 0, 0, 320, 384, 0, 0");
monitor_fill_range(&range[3], "29001-32000, 40-80, 0.636, 3.813, 1.906, 0.318, 0.064, 1.048, 0, 0, 384, 480, 0, 0");
monitor_fill_range(&range[4], "32001-34000, 40-80, 0.636, 3.813, 1.906, 0.020, 0.106, 0.607, 0, 0, 480, 576, 0, 0");
monitor_fill_range(&range[5], "34001-38000, 40-80, 1.000, 3.200, 2.200, 0.020, 0.106, 0.607, 0, 0, 576, 600, 0, 0");
return 6;
}
// Wells Gardner D9200
else if (!strcmp(type, "d9200"))
{
monitor_fill_range(&range[0], "15250-16500, 40-80, 2.187, 4.688, 6.719, 0.190, 0.191, 1.018, 0, 0, 224, 288, 448, 576");
monitor_fill_range(&range[1], "23900-24420, 40-80, 2.910, 3.000, 4.440, 0.451, 0.164, 1.148, 0, 0, 384, 400, 0, 0");
monitor_fill_range(&range[2], "31000-32000, 40-80, 0.636, 3.813, 1.906, 0.318, 0.064, 1.048, 0, 0, 400, 512, 0, 0");
monitor_fill_range(&range[3], "37000-38000, 40-80, 1.000, 3.200, 2.200, 0.020, 0.106, 0.607, 0, 0, 512, 600, 0, 0");
return 4;
}
// Wells Gardner K7000
else if (!strcmp(type, "k7000"))
{
monitor_fill_range(&range[0], "15625-15800, 49.50-63.00, 2.000, 4.700, 8.000, 0.064, 0.160, 1.056, 0, 0, 192, 288, 448, 576");
return 1;
}
// Wells Gardner 25K7131
else if (!strcmp(type, "k7131"))
{
monitor_fill_range(&range[0], "15625-16670, 49.5-65, 2.000, 4.700, 8.000, 0.064, 0.160, 1.056, 0, 0, 192, 288, 448, 576");
return 1;
}
// Wei-Ya M3129
else if (!strcmp(type, "m3129"))
{
monitor_fill_range(&range[0], "15250-16500, 40-80, 2.187, 4.688, 6.719, 0.190, 0.191, 1.018, 1, 1, 192, 288, 448, 576");
monitor_fill_range(&range[1], "23900-24420, 40-80, 2.910, 3.000, 4.440, 0.451, 0.164, 1.048, 1, 1, 384, 400, 0, 0");
monitor_fill_range(&range[2], "31000-32000, 40-80, 0.636, 3.813, 1.906, 0.318, 0.064, 1.048, 1, 1, 400, 512, 0, 0");
return 3;
}
// Hantarex MTC 9110
else if (!strcmp(type, "h9110") || !strcmp(type, "polo"))
{
monitor_fill_range(&range[0], "15625-16670, 49.5-65, 2.000, 4.700, 8.000, 0.064, 0.160, 1.056, 0, 0, 192, 288, 448, 576");
return 1;
}
// Hantarex Polostar 25
else if (!strcmp(type, "pstar"))
{
monitor_fill_range(&range[0], "15700-15800, 50-65, 1.800, 0.400, 7.400, 0.064, 0.160, 1.056, 0, 0, 192, 256, 0, 0");
monitor_fill_range(&range[1], "16200-16300, 50-65, 0.200, 0.400, 8.000, 0.040, 0.040, 0.640, 0, 0, 256, 264, 512, 528");
monitor_fill_range(&range[2], "25300-25400, 50-65, 0.200, 0.400, 8.000, 0.040, 0.040, 0.640, 0, 0, 384, 400, 768, 800");
monitor_fill_range(&range[3], "31500-31600, 50-65, 0.170, 0.350, 5.500, 0.040, 0.040, 0.640, 0, 0, 400, 512, 0, 0");
return 4;
}
// Nanao MS-2930, MS-2931
else if (!strcmp(type, "ms2930"))
{
monitor_fill_range(&range[0], "15450-16050, 50-65, 3.190, 4.750, 6.450, 0.191, 0.191, 1.164, 0, 0, 192, 288, 448, 576");
monitor_fill_range(&range[1], "23900-24900, 50-65, 2.870, 3.000, 4.440, 0.451, 0.164, 1.148, 0, 0, 384, 400, 0, 0");
monitor_fill_range(&range[2], "31000-32000, 50-65, 0.330, 3.580, 1.750, 0.316, 0.063, 1.137, 0, 0, 480, 512, 0, 0");
return 3;
}
// Nanao MS9-29
else if (!strcmp(type, "ms929"))
{
monitor_fill_range(&range[0], "15450-16050, 50-65, 3.910, 4.700, 6.850, 0.190, 0.191, 1.018, 0, 0, 192, 288, 448, 576");
monitor_fill_range(&range[1], "23900-24900, 50-65, 2.910, 3.000, 4.440, 0.451, 0.164, 1.048, 0, 0, 384, 400, 0, 0");
return 2;
}
// Rodotron 666B-29
else if (!strcmp(type, "r666b"))
{
monitor_fill_range(&range[0], "15450-16050, 50-65, 3.190, 4.750, 6.450, 0.191, 0.191, 1.164, 0, 0, 192, 288, 448, 576");
monitor_fill_range(&range[1], "23900-24900, 50-65, 2.870, 3.000, 4.440, 0.451, 0.164, 1.148, 0, 0, 384, 400, 0, 0");
monitor_fill_range(&range[2], "31000-32500, 50-65, 0.330, 3.580, 1.750, 0.316, 0.063, 1.137, 0, 0, 400, 512, 0, 0");
return 3;
}
// PC CRT 70kHz/120Hz
else if (!strcmp(type, "pc_31_120"))
{
monitor_fill_range(&range[0], "31400-31600, 100-130, 0.671, 2.683, 3.353, 0.034, 0.101, 0.436, 0, 0, 200, 256, 0, 0");
monitor_fill_range(&range[1], "31400-31600, 50-65, 0.671, 2.683, 3.353, 0.034, 0.101, 0.436, 0, 0, 400, 512, 0, 0");
return 2;
}
// PC CRT 70kHz/120Hz
else if (!strcmp(type, "pc_70_120"))
{
monitor_fill_range(&range[0], "30000-70000, 100-130, 2.201, 0.275, 4.678, 0.063, 0.032, 0.633, 0, 0, 192, 320, 0, 0");
monitor_fill_range(&range[1], "30000-70000, 50-65, 2.201, 0.275, 4.678, 0.063, 0.032, 0.633, 0, 0, 400, 1024, 0, 0");
return 2;
}
// VESA GTF
else if (!strcmp(type, "vesa_480") || !strcmp(type, "vesa_600") || !strcmp(type, "vesa_768") || !strcmp(type, "vesa_1024"))
{
return monitor_fill_vesa_gtf(&range[0], type);
}
log_error("Switchres: Monitor type unknown: %s\n", type);
return 0;
}
//============================================================
// monitor_evaluate_range
//============================================================
int monitor_evaluate_range(monitor_range *range)
{
// First we check that all frequency ranges are reasonable
if (range->hfreq_min < HFREQ_MIN || range->hfreq_min > HFREQ_MAX)
{
log_error("Switchres: hfreq_min %.2f out of range\n", range->hfreq_min);
return 1;
}
if (range->hfreq_max < HFREQ_MIN || range->hfreq_max < range->hfreq_min || range->hfreq_max > HFREQ_MAX)
{
log_error("Switchres: hfreq_max %.2f out of range\n", range->hfreq_max);
return 1;
}
if (range->vfreq_min < VFREQ_MIN || range->vfreq_min > VFREQ_MAX)
{
log_error("Switchres: vfreq_min %.2f out of range\n", range->vfreq_min);
return 1;
}
if (range->vfreq_max < VFREQ_MIN || range->vfreq_max < range->vfreq_min || range->vfreq_max > VFREQ_MAX)
{
log_error("Switchres: vfreq_max %.2f out of range\n", range->vfreq_max);
return 1;
}
// line_time in μs. We check that no horizontal value is longer than a whole line
double line_time = 1 / range->hfreq_max * 1000000;
if (range->hfront_porch <= 0 || range->hfront_porch > line_time)
{
log_error("Switchres: hfront_porch %.3f out of range\n", range->hfront_porch);
return 1;
}
if (range->hsync_pulse <= 0 || range->hsync_pulse > line_time)
{
log_error("Switchres: hsync_pulse %.3f out of range\n", range->hsync_pulse);
return 1;
}
if (range->hback_porch <= 0 || range->hback_porch > line_time)
{
log_error("Switchres: hback_porch %.3f out of range\n", range->hback_porch);
return 1;
}
// frame_time in ms. We check that no vertical value is longer than a whole frame
double frame_time = 1 / range->vfreq_max * 1000;
if (range->vfront_porch <= 0 || range->vfront_porch > frame_time)
{
log_error("Switchres: vfront_porch %.3f out of range\n", range->vfront_porch);
return 1;
}
if (range->vsync_pulse <= 0 || range->vsync_pulse > frame_time)
{
log_error("Switchres: vsync_pulse %.3f out of range\n", range->vsync_pulse);
return 1;
}
if (range->vback_porch <= 0 || range->vback_porch > frame_time)
{
log_error("Switchres: vback_porch %.3f out of range\n", range->vback_porch);
return 1;
}
// Now we check sync polarities
if (range->hsync_polarity != 0 && range->hsync_polarity != 1)
{
log_error("Switchres: Hsync polarity can be only 0 or 1\n");
return 1;
}
if (range->vsync_polarity != 0 && range->vsync_polarity != 1)
{
log_error("Switchres: Vsync polarity can be only 0 or 1\n");
return 1;
}
// Finally we check that the line limiters are reasonable
// Progressive range:
if (range->progressive_lines_min > 0 && range->progressive_lines_min < PROGRESSIVE_LINES_MIN)
{
log_error("Switchres: progressive_lines_min must be greater than %d\n", PROGRESSIVE_LINES_MIN);
return 1;
}
if ((range->progressive_lines_min + range->hfreq_max * range->vertical_blank) * range->vfreq_min > range->hfreq_max)
{
log_error("Switchres: progressive_lines_min %d out of range\n", range->progressive_lines_min);
return 1;
}
if (range->progressive_lines_max < range->progressive_lines_min)
{
log_error("Switchres: progressive_lines_max must greater than progressive_lines_min\n");
return 1;
}
if ((range->progressive_lines_max + range->hfreq_max * range->vertical_blank) * range->vfreq_min > range->hfreq_max)
{
log_error("Switchres: progressive_lines_max %d out of range\n", range->progressive_lines_max);
return 1;
}
// Interlaced range:
if (range->interlaced_lines_min != 0)
{
if (range->interlaced_lines_min < range->progressive_lines_max)
{
log_error("Switchres: interlaced_lines_min must greater than progressive_lines_max\n");
return 1;
}
if (range->interlaced_lines_min < PROGRESSIVE_LINES_MIN * 2)
{
log_error("Switchres: interlaced_lines_min must be greater than %d\n", PROGRESSIVE_LINES_MIN * 2);
return 1;
}
if ((range->interlaced_lines_min / 2 + range->hfreq_max * range->vertical_blank) * range->vfreq_min > range->hfreq_max)
{
log_error("Switchres: interlaced_lines_min %d out of range\n", range->interlaced_lines_min);
return 1;
}
if (range->interlaced_lines_max < range->interlaced_lines_min)
{
log_error("Switchres: interlaced_lines_max must greater than interlaced_lines_min\n");
return 1;
}
if ((range->interlaced_lines_max / 2 + range->hfreq_max * range->vertical_blank) * range->vfreq_min > range->hfreq_max)
{
log_error("Switchres: interlaced_lines_max %d out of range\n", range->interlaced_lines_max);
return 1;
}
}
else
{
if (range->interlaced_lines_max != 0)
{
log_error("Switchres: interlaced_lines_max must be zero if interlaced_lines_min is not defined\n");
return 1;
}
}
return 0;
}

64
deps/switchres/monitor.h vendored Normal file
View File

@ -0,0 +1,64 @@
/**************************************************************
monitor.h - Monitor presets header
---------------------------------------------------------
Switchres Modeline generation engine for emulation
License GPL-2.0+
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
Alexandre Wodarczyk, Gil Delescluse
**************************************************************/
#ifndef __MONITOR_H__
#define __MONITOR_H__
//============================================================
// CONSTANTS
//============================================================
#define MAX_RANGES 10
#define MONITOR_CRT 0
#define MONITOR_LCD 1
#define STANDARD_CRT_ASPECT 4.0/3.0
//============================================================
// TYPE DEFINITIONS
//============================================================
typedef struct monitor_range
{
double hfreq_min;
double hfreq_max;
double vfreq_min;
double vfreq_max;
double hfront_porch;
double hsync_pulse;
double hback_porch;
double vfront_porch;
double vsync_pulse;
double vback_porch;
int hsync_polarity;
int vsync_polarity;
int progressive_lines_min;
int progressive_lines_max;
int interlaced_lines_min;
int interlaced_lines_max;
double vertical_blank;
} monitor_range;
//============================================================
// PROTOTYPES
//============================================================
int monitor_fill_range(monitor_range *range, const char *specs_line);
int monitor_show_range(monitor_range *range);
int monitor_set_preset(char *type, monitor_range *range);
int monitor_fill_lcd_range(monitor_range *range, const char *specs_line);
int monitor_fill_vesa_gtf(monitor_range *range, const char *max_lines);
int monitor_fill_vesa_range(monitor_range *range, int lines_min, int lines_max);
int monitor_evaluate_range(monitor_range *range);
#endif

171
deps/switchres/resync_windows.cpp vendored Normal file
View File

@ -0,0 +1,171 @@
/**************************************************************
resync_windows.cpp - Windows device change notifying helper
---------------------------------------------------------
Switchres Modeline generation engine for emulation
License GPL-2.0+
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
Alexandre Wodarczyk, Gil Delescluse
**************************************************************/
#include <functional>
#include "resync_windows.h"
#include "log.h"
GUID GUID_DEVINTERFACE_MONITOR = { 0xe6f07b5f, 0xee97, 0x4a90, 0xb0, 0x76, 0x33, 0xf5, 0x7b, 0xf4, 0xea, 0xa7 };
//============================================================
// resync_handler::resync_handler
//============================================================
resync_handler::resync_handler()
{
m_event = CreateEvent(NULL, FALSE, FALSE, NULL);
CreateThread(NULL, 0, handler_thread, (LPVOID)this, 0, &my_thread);
}
//============================================================
// resync_handler::~resync_handler
//============================================================
resync_handler::~resync_handler()
{
SendMessage(m_hwnd, WM_CLOSE, 0, 0);
if (m_event) CloseHandle(m_event);
}
//============================================================
// resync_handler::handler_thread
//============================================================
DWORD WINAPI resync_handler::handler_thread(LPVOID lpParameter)
{
return ((resync_handler *)lpParameter)->handler_thread_wt();
}
DWORD resync_handler::handler_thread_wt()
{
WNDCLASSEX wc;
MSG msg;
HINSTANCE hinst = GetModuleHandle(NULL);
wc.cbSize = sizeof(wc);
wc.lpfnWndProc = this->resync_wnd_proc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.cbWndExtra = 0;
wc.cbClsExtra = 0;
wc.hInstance = hinst;
wc.hbrBackground = 0;
wc.lpszMenuName = NULL;
wc.lpszClassName = "resync_handler";
wc.hIcon = NULL;
wc.hIconSm = wc.hIcon;
wc.hCursor = LoadCursor(NULL, IDC_HAND);
RegisterClassEx(&wc);
m_hwnd = CreateWindowEx(0, "resync_handler", NULL, WS_POPUP, CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, NULL, NULL, hinst, NULL);
SetWindowLongPtr(m_hwnd, GWLP_USERDATA, (LONG_PTR)this);
// Register notifications of display monitor events
DEV_BROADCAST_DEVICEINTERFACE filter;
ZeroMemory(&filter, sizeof(filter));
filter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
filter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
filter.dbcc_classguid = GUID_DEVINTERFACE_MONITOR;
HDEVNOTIFY hDeviceNotify = RegisterDeviceNotification(m_hwnd, &filter, DEVICE_NOTIFY_WINDOW_HANDLE);
if (hDeviceNotify == NULL)
log_error("Error registering notification\n");
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return -1;
}
//============================================================
// resync_handler::wait
//============================================================
void resync_handler::wait()
{
m_is_notified_1 = false;
m_is_notified_2 = false;
auto start = std::chrono::steady_clock::now();
while (!m_is_notified_1 || !m_is_notified_2)
WaitForSingleObject(m_event, 10);
auto end = std::chrono::steady_clock::now();
log_verbose("resync time elapsed %I64d ms\n", std::chrono::duration_cast<std::chrono::milliseconds>(end-start).count());
}
//============================================================
// resync_handler::resync_wnd_proc
//============================================================
LRESULT CALLBACK resync_handler::resync_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
resync_handler *me = reinterpret_cast<resync_handler*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
if (me) return me->my_wnd_proc(hwnd, msg, wparam, lparam);
return DefWindowProc(hwnd, msg, wparam, lparam);
}
LRESULT CALLBACK resync_handler::my_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
switch (msg)
{
case WM_DEVICECHANGE:
{
switch (wparam)
{
case DBT_DEVICEARRIVAL:
{
log_verbose("Message: DBT_DEVICEARRIVAL\n");
PDEV_BROADCAST_DEVICEINTERFACE db = (PDEV_BROADCAST_DEVICEINTERFACE) lparam;
if (db != nullptr)
{
if (db->dbcc_classguid == GUID_DEVINTERFACE_MONITOR)
{
m_is_notified_1 = true;
SetEvent(m_event);
}
}
break;
}
case DBT_DEVICEREMOVECOMPLETE:
log_verbose("Message: DBT_DEVICEREMOVECOMPLETE\n");
break;
case DBT_DEVNODES_CHANGED:
log_verbose("Message: DBT_DEVNODES_CHANGED\n");
m_is_notified_2 = true;
SetEvent(m_event);
break;
default:
log_verbose("Message: WM_DEVICECHANGE message received, value %x unhandled.\n", (int)wparam);
break;
}
return 0;
}
break;
case WM_CLOSE:
{
PostQuitMessage(0);
return 0;
}
default:
return DefWindowProc(hwnd, msg, wparam, lparam);
}
return 0;
}

43
deps/switchres/resync_windows.h vendored Normal file
View File

@ -0,0 +1,43 @@
/**************************************************************
resync_windows.h - Windows device change notifying helper
---------------------------------------------------------
Switchres Modeline generation engine for emulation
License GPL-2.0+
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
Alexandre Wodarczyk, Gil Delescluse
**************************************************************/
#ifndef __RESYNC_WINDOWS__
#define __RESYNC_WINDOWS__
#include <chrono>
#include <windows.h>
#include <dbt.h>
class resync_handler
{
public:
resync_handler();
~resync_handler();
void wait();
private:
static LRESULT CALLBACK resync_wnd_proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK my_wnd_proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
static DWORD WINAPI handler_thread(LPVOID lpParameter);
DWORD handler_thread_wt();
HWND m_hwnd;
DWORD my_thread;
bool m_is_notified_1;
bool m_is_notified_2;
HANDLE m_event;
};
#endif

377
deps/switchres/switchres.cpp vendored Normal file
View File

@ -0,0 +1,377 @@
/**************************************************************
switchres.cpp - Swichres manager
---------------------------------------------------------
Switchres Modeline generation engine for emulation
License GPL-2.0+
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
Alexandre Wodarczyk, Gil Delescluse
**************************************************************/
#include <fstream>
#include <string.h>
#include <algorithm>
#include "switchres.h"
#include "log.h"
using namespace std;
const string WHITESPACE = " \n\r\t\f\v";
#if defined(_WIN32)
#define SR_CONFIG_PATHS ".\\;.\\ini\\;"
#elif defined(__linux__)
#define SR_CONFIG_PATHS "./;./ini/;/etc/;"
#endif
//============================================================
// logging
//============================================================
void switchres_manager::set_log_level(int log_level) { set_log_verbosity(log_level); }
void switchres_manager::set_log_verbose_fn(void *func_ptr) { set_log_verbose((void *)func_ptr); }
void switchres_manager::set_log_info_fn(void *func_ptr) { set_log_info((void *)func_ptr); }
void switchres_manager::set_log_error_fn(void *func_ptr) { set_log_error((void *)func_ptr); }
//============================================================
// File parsing helpers
//============================================================
string ltrim(const string& s)
{
size_t start = s.find_first_not_of(WHITESPACE);
return (start == string::npos) ? "" : s.substr(start);
}
string rtrim(const string& s)
{
size_t end = s.find_last_not_of(WHITESPACE);
return (end == string::npos) ? "" : s.substr(0, end + 1);
}
string trim(const string& s)
{
return rtrim(ltrim(s));
}
bool get_value(const string& line, string& key, string& value)
{
size_t key_end = line.find_first_of(WHITESPACE);
key = line.substr(0, key_end);
value = ltrim(line.substr(key_end + 1));
if (key.length() > 0 && value.length() > 0)
return true;
return false;
}
constexpr unsigned int s2i(const char* str, int h = 0)
{
return !str[h] ? 5381 : (s2i(str, h+1)*33) ^ str[h];
}
//============================================================
// switchres_manager::switchres_manager
//============================================================
switchres_manager::switchres_manager()
{
// Set Switchres default config options
set_monitor("generic_15");
set_modeline("auto");
set_lcd_range("auto");
for (int i = 0; i++ < MAX_RANGES;) set_crt_range(i, "auto");
// Set display manager default options
set_screen("auto");
set_modeline_generation(true);
set_lock_unsupported_modes(true);
set_lock_system_modes(true);
set_refresh_dont_care(false);
// Set modeline generator default options
set_interlace(true);
set_doublescan(true);
set_dotclock_min(0.0f);
set_rotation(false);
set_monitor_aspect(STANDARD_CRT_ASPECT);
set_refresh_tolerance(2.0f);
set_super_width(2560);
set_v_shift_correct(0);
set_pixel_precision(1);
set_interlace_force_even(0);
// Create our display manager
m_display_factory = new display_manager();
// Set logger properties
set_log_info_fn((void*)printf);
set_log_error_fn((void*)printf);
set_log_verbose_fn((void*)printf);
set_log_level(2);
}
//============================================================
// switchres_manager::~switchres_manager
//============================================================
switchres_manager::~switchres_manager()
{
if (m_display_factory) delete m_display_factory;
for (auto &display : displays)
delete display;
};
//============================================================
// switchres_manager::add_display
//============================================================
display_manager* switchres_manager::add_display()
{
// Parse display specific ini, if it exists
display_settings base_ds = ds;
char file_name[32] = {0};
sprintf(file_name, "display%d.ini", (int)displays.size());
parse_config(file_name);
// Create new display
display_manager *display = m_display_factory->make(&ds);
display->set_index(displays.size());
displays.push_back(display);
log_verbose("Switchres(v%s) display[%d]: monitor[%s] generation[%s]\n",
SWITCHRES_VERSION, display->index(), ds.monitor, ds.modeline_generation?"on":"off");
display->parse_options();
// restore base display settings
ds = base_ds;
return display;
}
//============================================================
// switchres_manager::parse_config
//============================================================
bool switchres_manager::parse_config(const char *file_name)
{
ifstream config_file;
// Search for ini file in our config paths
auto start = 0U;
while (true)
{
char full_path[256] = "";
string paths = SR_CONFIG_PATHS;
auto end = paths.find(";", start);
if (end == string::npos) return false;
snprintf(full_path, sizeof(full_path), "%s%s", paths.substr(start, end - start).c_str(), file_name);
config_file.open(full_path);
if (config_file.is_open())
{
log_verbose("parsing %s\n", full_path);
break;
}
start = end + 1;
}
// Ini file found, parse it
string line;
while (getline(config_file, line))
{
line = trim(line);
if (line.length() == 0 || line.at(0) == '#')
continue;
string key, value;
if(get_value(line, key, value))
{
switch (s2i(key.c_str()))
{
// Switchres options
case s2i("verbose"):
if (atoi(value.c_str())) set_log_verbose_fn((void*)printf);
break;
case s2i("monitor"):
transform(value.begin(), value.end(), value.begin(), ::tolower);
set_monitor(value.c_str());
break;
case s2i("crt_range0"):
set_crt_range(0, value.c_str());
break;
case s2i("crt_range1"):
set_crt_range(1, value.c_str());
break;
case s2i("crt_range2"):
set_crt_range(2, value.c_str());
break;
case s2i("crt_range3"):
set_crt_range(3, value.c_str());
break;
case s2i("crt_range4"):
set_crt_range(4, value.c_str());
break;
case s2i("crt_range5"):
set_crt_range(5, value.c_str());
break;
case s2i("crt_range6"):
set_crt_range(6, value.c_str());
break;
case s2i("crt_range7"):
set_crt_range(7, value.c_str());
break;
case s2i("crt_range8"):
set_crt_range(8, value.c_str());
break;
case s2i("crt_range9"):
set_crt_range(9, value.c_str());
break;
case s2i("lcd_range"):
set_lcd_range(value.c_str());
break;
case s2i("modeline"):
set_modeline(value.c_str());
break;
case s2i("user_mode"):
{
if (strcmp(value.c_str(), "auto"))
{
modeline user_mode = {};
if (sscanf(value.c_str(), "%dx%d@%d", &user_mode.width, &user_mode.height, &user_mode.refresh) < 1)
log_error("Error: use format resolution <w>x<h>@<r>\n");
else
set_user_mode(&user_mode);
}
break;
}
// Display options
case s2i("display"):
set_screen(value.c_str());
break;
case s2i("api"):
set_api(value.c_str());
break;
case s2i("modeline_generation"):
set_modeline_generation(atoi(value.c_str()));
break;
case s2i("lock_unsupported_modes"):
set_lock_unsupported_modes(atoi(value.c_str()));
break;
case s2i("lock_system_modes"):
set_lock_system_modes(atoi(value.c_str()));
break;
case s2i("refresh_dont_care"):
set_refresh_dont_care(atoi(value.c_str()));
break;
case s2i("keep_changes"):
set_keep_changes(atoi(value.c_str()));
break;
// Modeline generation options
case s2i("interlace"):
set_interlace(atoi(value.c_str()));
break;
case s2i("doublescan"):
set_doublescan(atoi(value.c_str()));
break;
case s2i("dotclock_min"):
{
double pclock_min = 0.0f;
sscanf(value.c_str(), "%lf", &pclock_min);
set_dotclock_min(pclock_min);
break;
}
case s2i("sync_refresh_tolerance"):
{
double refresh_tolerance = 0.0f;
sscanf(value.c_str(), "%lf", &refresh_tolerance);
set_refresh_tolerance(refresh_tolerance);
break;
}
case s2i("super_width"):
{
int super_width = 0;
sscanf(value.c_str(), "%d", &super_width);
set_super_width(super_width);
break;
}
case s2i("aspect"):
set_monitor_aspect(get_aspect(value.c_str()));
break;
case s2i("v_shift_correct"):
set_v_shift_correct(atoi(value.c_str()));
break;
case s2i("pixel_precision"):
set_pixel_precision(atoi(value.c_str()));
break;
case s2i("interlace_force_even"):
set_interlace_force_even(atoi(value.c_str()));
break;
// Custom video backend options
case s2i("screen_compositing"):
set_screen_compositing(atoi(value.c_str()));
break;
case s2i("screen_reordering"):
set_screen_reordering(atoi(value.c_str()));
break;
case s2i("allow_hardware_refresh"):
set_allow_hardware_refresh(atoi(value.c_str()));
break;
case s2i("custom_timing"):
set_custom_timing(value.c_str());
break;
// Various
case s2i("verbosity"):
{
int verbosity_level = 1;
sscanf(value.c_str(), "%d", &verbosity_level);
set_log_level(verbosity_level);
break;
}
default:
log_error("Invalid option %s\n", key.c_str());
break;
}
}
}
config_file.close();
return true;
}
//============================================================
// switchres_manager::get_aspect
//============================================================
double switchres_manager::get_aspect(const char* aspect)
{
int num, den;
if (sscanf(aspect, "%d:%d", &num, &den) == 2)
{
if (den == 0)
{
log_error("Error: denominator can't be zero\n");
return STANDARD_CRT_ASPECT;
}
return (double(num)/double(den));
}
log_error("Error: use format --aspect <num:den>\n");
return STANDARD_CRT_ASPECT;
}

110
deps/switchres/switchres.h vendored Normal file
View File

@ -0,0 +1,110 @@
/**************************************************************
switchres.h - SwichRes general header
---------------------------------------------------------
Switchres Modeline generation engine for emulation
License GPL-2.0+
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
Alexandre Wodarczyk, Gil Delescluse
**************************************************************/
#ifndef __SWITCHRES_H__
#define __SWITCHRES_H__
#include <cstring>
#include <vector>
#include "monitor.h"
#include "modeline.h"
#include "display.h"
#include "edid.h"
//============================================================
// CONSTANTS
//============================================================
#define SWITCHRES_VERSION "2.002"
//============================================================
// TYPE DEFINITIONS
//============================================================
typedef struct config_settings
{
bool mode_switching;
} config_settings;
class switchres_manager
{
public:
switchres_manager();
~switchres_manager();
// getters
display_manager *display() const { return displays[0]; }
display_manager *display(int i) const { return i < (int)displays.size()? displays[i] : nullptr; }
// setters (log manager)
void set_log_level(int log_level);
void set_log_verbose_fn(void *func_ptr);
void set_log_info_fn(void *func_ptr);
void set_log_error_fn(void *func_ptr);
// setters (display manager)
void set_monitor(const char *preset) { strncpy(ds.monitor, preset, sizeof(ds.monitor)-1); }
void set_modeline(const char *modeline) { strncpy(ds.user_modeline, modeline, sizeof(ds.user_modeline)-1); }
void set_user_mode(modeline *user_mode) { ds.user_mode = *user_mode;}
void set_crt_range(int i, const char *range) { strncpy(ds.crt_range[i], range, sizeof(ds.crt_range[i])-1); }
void set_lcd_range(const char *range) { strncpy(ds.lcd_range, range, sizeof(ds.lcd_range)-1); }
void set_screen(const char *screen) { strncpy(ds.screen, screen, sizeof(ds.screen)-1); }
void set_api(const char *api) { strncpy(ds.api, api, sizeof(ds.api)-1); }
void set_modeline_generation(bool value) { ds.modeline_generation = value; }
void set_lock_unsupported_modes(bool value) { ds.lock_unsupported_modes = value; }
void set_lock_system_modes(bool value) { ds.lock_system_modes = value; }
void set_refresh_dont_care(bool value) { ds.refresh_dont_care = value; }
void set_keep_changes(bool value) { ds.keep_changes = value; }
// setters (modeline generator)
void set_interlace(bool value) { ds.gs.interlace = value; }
void set_doublescan(bool value) { ds.gs.doublescan = value; }
void set_dotclock_min(double value) { ds.gs.pclock_min = value * 1000000; }
void set_refresh_tolerance(double value) { ds.gs.refresh_tolerance = value; }
void set_super_width(int value) { ds.gs.super_width = value; }
void set_rotation(bool value) { ds.gs.rotation = value; }
void set_monitor_aspect(double value) { ds.gs.monitor_aspect = value; }
void set_monitor_aspect(const char* aspect) { set_monitor_aspect(get_aspect(aspect)); }
void set_v_shift_correct(int value) { ds.gs.v_shift_correct = value; }
void set_pixel_precision(int value) { ds.gs.pixel_precision = value; }
void set_interlace_force_even(int value) { ds.gs.interlace_force_even = value; }
// setters (custom_video backend)
void set_screen_compositing(bool value) { ds.vs.screen_compositing = value; }
void set_screen_reordering(bool value) { ds.vs.screen_reordering = value; }
void set_allow_hardware_refresh(bool value) { ds.vs.allow_hardware_refresh = value; }
void set_custom_timing(const char *custom_timing) { strncpy(ds.vs.custom_timing, custom_timing, sizeof(ds.vs.custom_timing)-1); }
// interface
display_manager* add_display();
bool parse_config(const char *file_name);
//settings
config_settings cs = {};
display_settings ds = {};
// display list
std::vector<display_manager *> displays;
private:
display_manager *m_display_factory = 0;
double get_aspect(const char* aspect);
};
#endif

160
deps/switchres/switchres.ini vendored Normal file
View File

@ -0,0 +1,160 @@
#
# Switchres config
#
# Monitor preset. Sets typical monitor operational ranges:
#
# generic_15, ntsc, pal Generic CRT standards
# arcade_15, arcade_15ex Arcade fixed frequency
# arcade_25, arcade_31 Arcade fixed frequency
# arcade_15_25, arcade_15_25_31 Arcade multisync
# vesa_480, vesa_600, vesa_768, vesa_1024 VESA GTF
# pc_31_120, pc_70_120 PC monitor 120 Hz
# h9110, polo, pstar Hantarex
# k7000, k7131, d9200, d9800, d9400 Wells Gardner
# m2929 Makvision
# m3129 Wei-Ya
# ms2930, ms929 Nanao
# r666b Rodotron
#
# Special presets:
# custom Defines a custom preset. Use in combination with crt_range0-9 options below.
# lcd Will keep desktop's resolution but attempt variable refresh, use in combination with lcd_range
#
monitor arcade_15
# Define a custom preset, use monitor custom to activate
# crt_range0-9 HfreqMin-HfreqMax, VfreqMin-VfreqMax, HFrontPorch, HSyncPulse, HBackPorch, VfrontPorch, VSyncPulse, VBackPorch, HSyncPol, VSyncPol, ProgressiveLinesMin, ProgressiveLinesMax, InterlacedLinesMin, InterlacedLinesMax
# e.g.: crt_range0 15625-15750, 49.50-65.00, 2.000, 4.700, 8.000, 0.064, 0.192, 1.024, 0, 0, 192, 288, 448, 576
crt_range0 auto
crt_range1 auto
crt_range2 auto
crt_range3 auto
crt_range4 auto
crt_range5 auto
crt_range6 auto
crt_range7 auto
crt_range8 auto
crt_range9 auto
# Set the operational refresh range for LCD monitor, e.g. lcd_range 50-61
lcd_range auto
# Force a custom modeline, in XFree86 format. This option overrides the active monitor preset configuration.
modeline auto
# Forces an user mode, in the format: width x height @ refresh. Here, 0 can used as a wildcard. At least one of the three values
# must be defined. E.g. user_mode 0x240 -> SR can freely choose any width based on the game's requested video mode, but will
# force height as 240.
user_mode auto
#
# Display config
#
# Select target display
# auto Pick the default display
# 0, 1, 2, ... Pick a display by index
# \\.\DISPLAY1, ... Windows display name
# VGA-0, ... X11 display name
display auto
# Choose a custom video backend when more than one is available.
# auto Let Switchres decide
# adl Windows - AMD ADL (AMD Radeon HD 5000+)
# ati Windows - ATI legacy (ATI Radeon pre-HD 5000)
# powerstrip Windows - PowerStrip (ATI, Nvidia, Matrox, etc., models up to 2012)
# xrandr Linux - X11/Xorg
# drmkms Linux - KMS/DRM (WIP)
api auto
# [Windows] Lock video modes reported as unsupported by your monitor's EDID
lock_unsupported_modes 1
# Lock system (non-custom) video modes, only use modes that have full detailed timings available
lock_system_modes 0
# Ignore video mode's refresh reported by the OS when checking ranges
refresh_dont_care 0
# Keep changes on exit (warning: this skips video mode cleanup)
keep_changes 0
#
# Modeline generation config
#
# Enable on-the-fly generation of video modes
modeline_generation 1
# Allow interlaced modes (existing or generated)
interlace 1
# Allow doublescan modes (warning: doublescan support is broken in most drivers)
doublescan 0
# Force a minimum dotclock value, in MHz, e.g. dotclock_min 25.0
dotclock_min 0
# Maximum refresh difference, in Hz, allowed in order to synchronize. Below this value, the mismatch does not involve penalization
sync_refresh_tolerance 2.0
# Super resolution width: above this width, fractional scaling on the horizontal axis is applied without penalization
super_width 2560
# Physical aspect ratio of the target monitor. Used to compensate aspect ratio when the target monitor is not 4:3
aspect 4:3
# [Experimental] Attempts to compensate consumer TVs vertical centering issues
v_shift_correct 0
# Calculate horizontal borders with 1-pixel precision, instead of the default 8-pixels blocks that were required by old drivers.
# Greatly improves horizontal centering of video modes.
pixel_precision 1
# Calculate all vertical values of interlaced modes as even numbers. Required by AMD APU hardware on Linux
interlace_force_even 0
#
# Custom video backend config
#
# [X11] adjusts the crtc position after a new video mode is set, maintaining the relative position of screens in a multi-monitor setup.
screen_compositing 0
# [X11] stacks the screens vertically on startup to allow each screen to freely resize up to the maximum width. Useful to avoid video
# glitches when using super-resolutions. screen_reordering overrides screen_compositing.
screen_reordering 0
# [Windows] dynamically adds new modes or updates existing ones, even on stock AMD drivers*. This feature is experimental and is
# disabled by default. It has the following limitations and problems:
# - Synchronization is not perfect yet and the new modes may not always be ready on time for mode switching, causing a wrong display
# output.
# - A plug-n-play audio notification will be present on startup and exit, if the explorer shell is used.
# - Refreshing the hardware is an expensive task that takes time, specially if the app has already entered fullscreen mode. This
# makes it unpractical for games that switch video modes more than once.
# * When used with stock AMD drivers instead of CRT Emudriver, usual limitations apply: no support for low resolutions (below 640x480)
# nor low dotclocks.
# Not a problem however if you're using a 31 kHz monitor.
allow_hardware_refresh 0
# Pass a custom video timing string in the native backend's format. E.g. pstring timing for Powerstrip
custom_timing auto
#
# Logging
#
# Enables verbose mode (0|1)
verbose 0
# Set verbosity level (from 0 to 3)
# 0: no messages from SR
# 1: only errors
# 2: general information
# 3: debug messages
verbosity 2

305
deps/switchres/switchres_main.cpp vendored Normal file
View File

@ -0,0 +1,305 @@
/**************************************************************
switchres_main.cpp - Swichres standalone launcher
---------------------------------------------------------
Switchres Modeline generation engine for emulation
License GPL-2.0+
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
Alexandre Wodarczyk, Gil Delescluse
**************************************************************/
#include <iostream>
#include <cstring>
#include <getopt.h>
#include "switchres.h"
#include "log.h"
using namespace std;
int show_version();
int show_usage();
//============================================================
// main
//============================================================
int main(int argc, char **argv)
{
switchres_manager switchres;
switchres.parse_config("switchres.ini");
int width = 0;
int height = 0;
float refresh = 0.0;
modeline user_mode = {};
int index = 0;
int version_flag = false;
bool help_flag = false;
bool resolution_flag = false;
bool calculate_flag = false;
bool edid_flag = false;
bool switch_flag = false;
bool launch_flag = false;
bool force_flag = false;
bool interlaced_flag = false;
bool user_ini_flag = false;
bool keep_changes_flag = false;
string ini_file;
string launch_command;
while (1)
{
static struct option long_options[] =
{
{"version", no_argument, &version_flag, '1'},
{"help", no_argument, 0, 'h'},
{"calc", no_argument, 0, 'c'},
{"switch", no_argument, 0, 's'},
{"launch", required_argument, 0, 'l'},
{"monitor", required_argument, 0, 'm'},
{"aspect", required_argument, 0, 'a'},
{"edid", no_argument, 0, 'e'},
{"rotated", no_argument, 0, 'r'},
{"display", required_argument, 0, 'd'},
{"force", required_argument, 0, 'f'},
{"ini", required_argument, 0, 'i'},
{"verbose", no_argument, 0, 'v'},
{"backend", required_argument, 0, 'b'},
{"keep", no_argument, 0, 'k'},
{0, 0, 0, 0}
};
int option_index = 0;
int c = getopt_long(argc, argv, "vhcsl:m:a:erd:f:i:b:k", long_options, &option_index);
if (c == -1)
break;
if (version_flag)
{
show_version();
return 0;
}
switch (c)
{
case 'v':
switchres.set_log_level(3);
switchres.set_log_error_fn((void*)printf);
switchres.set_log_info_fn((void*)printf);
switchres.set_log_verbose_fn((void*)printf);
break;
case 'h':
help_flag = true;
break;
case 'c':
calculate_flag = true;
break;
case 's':
switch_flag = true;
break;
case 'l':
launch_flag = true;
launch_command = optarg;
break;
case 'm':
switchres.set_monitor(optarg);
break;
case 'r':
switchres.set_rotation(true);
break;
case 'd':
// Add new display in multi-monitor case
if (index > 0) switchres.add_display();
index ++;
switchres.set_screen(optarg);
break;
case 'a':
switchres.set_monitor_aspect(optarg);
break;
case 'e':
edid_flag = true;
break;
case 'f':
force_flag = true;
if (sscanf(optarg, "%dx%d@%d", &user_mode.width, &user_mode.height, &user_mode.refresh) < 1)
log_error("Error: use format --force <w>x<h>@<r>\n");
break;
case 'i':
user_ini_flag = true;
ini_file = optarg;
break;
case 'b':
switchres.set_api(optarg);
break;
case 'k':
keep_changes_flag = true;
switchres.set_keep_changes(true);
break;
default:
return 0;
}
}
if (help_flag)
goto usage;
// Get user video mode information from command line
if ((argc - optind) < 3)
{
log_error("Error: missing argument\n");
goto usage;
}
else if ((argc - optind) > 3)
{
log_error("Error: too many arguments\n");
goto usage;
}
else
{
resolution_flag = true;
width = atoi(argv[optind]);
height = atoi(argv[optind + 1]);
refresh = atof(argv[optind + 2]);
char scan_mode = argv[optind + 2][strlen(argv[optind + 2]) -1];
if (scan_mode == 'i')
interlaced_flag = true;
}
if (user_ini_flag)
switchres.parse_config(ini_file.c_str());
switchres.add_display();
if (force_flag)
switchres.display()->set_user_mode(&user_mode);
if (!calculate_flag && !edid_flag)
{
for (auto &display : switchres.displays)
display->init();
}
if (resolution_flag)
{
for (auto &display : switchres.displays)
{
modeline *mode = display->get_mode(width, height, refresh, interlaced_flag);
if (mode) display->flush_modes();
}
if (edid_flag)
{
edid_block edid = {};
modeline *mode = switchres.display()->best_mode();
if (mode)
{
monitor_range *range = &switchres.display()->range[mode->range];
edid_from_modeline(mode, range, switchres.ds.monitor, &edid);
char file_name[sizeof(switchres.ds.monitor) + 4];
sprintf(file_name, "%s.bin", switchres.ds.monitor);
FILE *file = fopen(file_name, "wb");
if (file)
{
fwrite(&edid, sizeof(edid), 1, file);
fclose (file);
log_info("EDID saved as %s\n", file_name);
}
}
}
if (switch_flag) for (auto &display : switchres.displays) display->set_mode(display->best_mode());
if (switch_flag && !launch_flag && !keep_changes_flag)
{
log_info("Press ENTER to exit...\n");
cin.get();
}
if (launch_flag)
{
int status_code = system(launch_command.c_str());
log_info("Process exited with value %d\n", status_code);
}
}
return (0);
usage:
show_usage();
return 0;
}
//============================================================
// show_version
//============================================================
int show_version()
{
char version[]
{
"Switchres " SWITCHRES_VERSION "\n"
"Modeline generation engine for emulation\n"
"Copyright (C) 2010-2021 - Chris Kennedy, Antonio Giner, Alexandre Wodarczyk, Gil Delescluse\n"
"License GPL-2.0+\n"
"This is free software: you are free to change and redistribute it.\n"
"There is NO WARRANTY, to the extent permitted by law.\n"
};
log_info("%s", version);
return 0;
}
//============================================================
// show_usage
//============================================================
int show_usage()
{
char usage[] =
{
"Usage: switchres <width> <height> <refresh> [options]\n"
"Options:\n"
" -c, --calc Calculate video mode and exit\n"
" -s, --switch Switch to video mode\n"
" -l, --launch <command> Launch <command>\n"
" -m, --monitor <preset> Monitor preset (generic_15, arcade_15, pal, ntsc, etc.)\n"
" -a --aspect <num:den> Monitor aspect ratio\n"
" -r --rotated Original mode's native orientation is rotated\n"
" -d, --display <OS_display_name> Use target display (Windows: \\\\.\\DISPLAY1, ... Linux: VGA-0, ...)\n"
" -f, --force <w>x<h>@<r> Force a specific video mode from display mode list\n"
" -i, --ini <file.ini> Specify an ini file\n"
" -b, --backend <api_name> Specify the api name\n"
" -e, --edid Create an EDID binary with calculated video modes\n"
" -k, --keep Keep changes on exit (warning: this disables cleanup)\n"
};
log_info("%s", usage);
return 0;
}

223
deps/switchres/switchres_wrapper.cpp vendored Normal file
View File

@ -0,0 +1,223 @@
/**************************************************************
switchres_wrapper.cpp - Switchres C wrapper API
---------------------------------------------------------
Switchres Modeline generation engine for emulation
License GPL-2.0+
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
Alexandre Wodarczyk, Gil Delescluse
**************************************************************/
#define MODULE_API_EXPORTS
#include "switchres.h"
#include "switchres_wrapper.h"
#include "log.h"
#include <stdio.h>
#include <locale>
#ifdef __cplusplus
extern "C" {
#endif
switchres_manager* swr;
MODULE_API void sr_init() {
setlocale(LC_NUMERIC, "C");
swr = new switchres_manager;
swr->parse_config("switchres.ini");
}
MODULE_API void sr_load_ini(char* config) {
swr->parse_config(config);
}
MODULE_API unsigned char sr_init_disp(const char* scr) {
if (scr)
swr->set_screen(scr);
swr->add_display();
if (!swr->display()->init())
return 0;
return 1;
}
MODULE_API void sr_deinit() {
delete swr;
}
MODULE_API void sr_set_monitor(const char *preset) {
swr->set_monitor(preset);
}
MODULE_API void sr_set_user_mode(int width, int height, int refresh) {
modeline user_mode = {};
user_mode.width = width;
user_mode.height = height;
user_mode.refresh = refresh;
swr->set_user_mode(&user_mode);
}
void disp_best_mode_to_sr_mode(display_manager* disp, sr_mode* srm)
{
srm->width = disp->width();
srm->height = disp->height();
srm->refresh = disp->v_freq();
srm->is_refresh_off = (disp->is_refresh_off() ? 1 : 0);
srm->is_stretched = (disp->is_stretched() ? 1 : 0);
srm->x_scale = disp->x_scale();
srm->y_scale = disp->y_scale();
srm->interlace = (disp->is_interlaced() ? 105 : 0);
}
bool sr_refresh_display(display_manager *disp)
{
if (disp->is_mode_updated())
{
if (disp->update_mode(disp->best_mode()))
{
log_info("sr_refresh_display: mode was updated\n");
return true;
}
}
else if (disp->is_mode_new())
{
if (disp->add_mode(disp->best_mode()))
{
log_info("sr_refresh_display: mode was added\n");
return true;
}
}
else
{
log_info("sr_refresh_display: no refresh required\n");
return true;
}
log_error("sr_refresh_display: error refreshing display\n");
return false;
}
MODULE_API unsigned char sr_add_mode(int width, int height, double refresh, unsigned char interlace, sr_mode *return_mode) {
log_verbose("Inside sr_add_mode(%dx%d@%f%s)\n", width, height, refresh, interlace > 0? "i":"");
display_manager *disp = swr->display();
if (disp == nullptr)
{
log_error("sr_add_mode: error, didn't get a display\n");
return 0;
}
disp->get_mode(width, height, refresh, (interlace > 0? true : false));
if (disp->got_mode())
{
log_verbose("sr_add_mode: got mode %dx%d@%f type(%x)\n", disp->width(), disp->height(), disp->v_freq(), disp->best_mode()->type);
if (return_mode != nullptr) disp_best_mode_to_sr_mode(disp, return_mode);
if (sr_refresh_display(disp))
return 1;
}
printf("sr_add_mode: error adding mode\n");
return 0;
}
MODULE_API unsigned char sr_switch_to_mode(int width, int height, double refresh, unsigned char interlace, sr_mode *return_mode) {
log_verbose("Inside sr_switch_to_mode(%dx%d@%f%s)\n", width, height, refresh, interlace > 0? "i":"");
display_manager *disp = swr->display();
if (disp == nullptr)
{
log_error("sr_switch_to_mode: error, didn't get a display\n");
return 0;
}
disp->get_mode(width, height, refresh, (interlace > 0? true : false));
if (disp->got_mode())
{
log_verbose("sr_switch_to_mode: got mode %dx%d@%f type(%x)\n", disp->width(), disp->height(), disp->v_freq(), disp->best_mode()->type);
if (return_mode != nullptr) disp_best_mode_to_sr_mode(disp, return_mode);
if (!sr_refresh_display(disp))
return 0;
}
if (disp->is_switching_required())
{
if (disp->set_mode(disp->best_mode()))
{
log_info("sr_switch_to_mode: successfully switched to %dx%d@%f\n", disp->width(), disp->height(), disp->v_freq());
return 1;
}
}
else
{
log_info("sr_switch_to_mode: switching not required\n");
return 1;
}
log_error("sr_switch_to_mode: error switching to mode\n");
return 0;
}
MODULE_API void sr_set_rotation (unsigned char r) {
if (r > 0)
{
swr->set_rotation(true);
}
else
{
swr->set_rotation(false);
}
}
MODULE_API void sr_set_log_level (int l) {
swr->set_log_level(l);
}
MODULE_API void sr_set_log_callback_info (void * f) {
swr->set_log_info_fn((void *)f);
}
MODULE_API void sr_set_log_callback_debug (void * f) {
swr->set_log_verbose_fn((void *)f);
}
MODULE_API void sr_set_log_callback_error (void * f) {
swr->set_log_error_fn((void *)f);
}
MODULE_API srAPI srlib = {
sr_init,
sr_load_ini,
sr_deinit,
sr_init_disp,
sr_add_mode,
sr_switch_to_mode,
sr_set_monitor,
sr_set_rotation,
sr_set_user_mode,
sr_set_log_level,
sr_set_log_callback_error,
sr_set_log_callback_info,
sr_set_log_callback_debug,
};
#ifdef __cplusplus
}
#endif

128
deps/switchres/switchres_wrapper.h vendored Normal file
View File

@ -0,0 +1,128 @@
/**************************************************************
switchres_wrapper.h - Switchres C wrapper API header file
---------------------------------------------------------
Switchres Modeline generation engine for emulation
License GPL-2.0+
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
Alexandre Wodarczyk, Gil Delescluse
**************************************************************/
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __linux__
#include <dlfcn.h>
#define LIBTYPE void*
#define OPENLIB(libname) dlopen((libname), RTLD_LAZY)
#define LIBFUNC(libh, fn) dlsym((libh), (fn))
#define LIBERROR dlerror
#define CLOSELIB(libh) dlclose((libh))
#elif defined _WIN32
#include <windows.h>
#define LIBTYPE HINSTANCE
#define OPENLIB(libname) LoadLibrary(TEXT((libname)))
#define LIBFUNC(lib, fn) GetProcAddress((lib), (fn))
#define CLOSELIB(libp) FreeLibrary((libp))
#endif
#ifdef _WIN32
/*
* This is a trick to avoid exporting some functions thus having the binary
* flagged as a virus. If switchres_wrapper.cpp is included in the compilation
* LIBERROR() is just declared and not compiled. If switchres_wrapper.cpp is
* not compiled, LIBERROR is defined here
*/
#ifndef SR_WIN32_STATIC
char* LIBERROR()
{
DWORD errorMessageID = GetLastError();
if(errorMessageID == 0)
return NULL;
LPSTR messageBuffer;
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
SetLastError(0);
static char error_msg[256] = {0};
strncpy(error_msg, messageBuffer, sizeof(error_msg)-1);
LocalFree(messageBuffer);
return error_msg;
}
#endif /* SR_WIN32_STATIC */
#ifndef SR_WIN32_STATIC
#define MODULE_API __declspec(dllexport)
#else
#define MODULE_API
#endif
#else
#define MODULE_API
#endif /* _WIN32 */
#ifdef __linux__
#define LIBSWR "libswitchres.so"
#elif _WIN32
#define LIBSWR "libswitchres.dll"
#endif
/* That's all the exposed data from Switchres calculation */
typedef struct MODULE_API {
int width;
int height;
double refresh;
unsigned char is_refresh_off;
unsigned char is_stretched;
int x_scale;
int y_scale;
unsigned char interlace;
} sr_mode;
/* Declaration of the wrapper functions */
MODULE_API void sr_init();
MODULE_API void sr_load_ini(char* config);
MODULE_API void sr_deinit();
MODULE_API unsigned char sr_init_disp(const char* src);
MODULE_API unsigned char sr_add_mode(int, int, double, unsigned char, sr_mode*);
MODULE_API unsigned char sr_switch_to_mode(int, int, double, unsigned char, sr_mode*);
MODULE_API void sr_set_monitor(const char*);
MODULE_API void sr_set_rotation(unsigned char);
MODULE_API void sr_set_user_mode(int, int, int);
/* Logging related functions */
MODULE_API void sr_set_log_level (int);
MODULE_API void sr_set_log_callback_error(void *);
MODULE_API void sr_set_log_callback_info(void *);
MODULE_API void sr_set_log_callback_debug(void *);
/* Inspired by https://stackoverflow.com/a/1067684 */
typedef struct MODULE_API {
void (*init)(void);
void (*sr_sr_load_ini)(char*);
void (*deinit)(void);
unsigned char (*sr_init_disp)(const char*);
unsigned char (*sr_add_mode)(int, int, double, unsigned char, sr_mode*);
unsigned char (*sr_switch_to_mode)(int, int, double, unsigned char, sr_mode*);
void (*sr_set_monitor)(const char*);
void (*sr_set_rotation)(unsigned char);
void (*sr_set_user_mode)(int, int, int);
void (*sr_set_log_level) (int);
void (*sr_set_log_callback_error)(void *);
void (*sr_set_log_callback_info)(void *);
void (*sr_set_log_callback_debug)(void *);
} srAPI;
#ifdef __cplusplus
}
#endif

View File

@ -642,6 +642,26 @@ static void win32_save_position(void)
}
}
static void win32_resize_after_display_change(HWND hwnd)
{
HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
if (monitor != NULL)
{
MONITORINFO info;
memset(&info, 0, sizeof(info));
info.cbSize = sizeof(info);
if (GetMonitorInfo(monitor, &info))
{
int new_width = abs(info.rcMonitor.right - info.rcMonitor.left);
int new_height = abs(info.rcMonitor.bottom - info.rcMonitor.top);
SetWindowPos(hwnd, 0, 0, 0, new_width, new_height, SWP_NOMOVE);
}
}
}
static bool win32_browser(
HWND owner,
char *filename,
@ -1049,6 +1069,9 @@ static LRESULT CALLBACK wnd_proc_common_internal(HWND hwnd,
win32_clip_window(false);
break;
#endif
case WM_DISPLAYCHANGE: /* fix size after display mode switch when using SR */
win32_resize_after_display_change(hwnd);
break;
}
return DefWindowProc(hwnd, message, wparam, lparam);
@ -1165,6 +1188,9 @@ static LRESULT CALLBACK wnd_proc_common_dinput_internal(HWND hwnd,
win32_clip_window(false);
break;
#endif
case WM_DISPLAYCHANGE: /* fix size after display mode switch when using SR */
win32_resize_after_display_change(hwnd);
break;
}
return DefWindowProc(hwnd, message, wparam, lparam);

View File

@ -19,10 +19,22 @@
#include <stddef.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <libretro.h>
#include <math.h>
#include "../retroarch.h"
#include <retro_common_api.h>
#include "video_crt_switch.h"
#include "video_display_server.h"
#include "../core_info.h"
#include "../verbosity.h"
#include "gfx_display.h"
#if !defined(HAVE_VIDEOCORE)
#include "../deps/switchres/switchres_wrapper.h"
static sr_mode srm;
#endif
#ifdef HAVE_CONFIG_H
#include "../config.h"
@ -30,231 +42,352 @@
#if defined(HAVE_VIDEOCORE)
#include "include/userland/interface/vmcs_host/vc_vchi_gencmd.h"
static void crt_rpi_switch(int width, int height, float hz, int xoffset);
static void crt_rpi_switch(videocrt_switch_t *p_switch,int width, int height, float hz, int xoffset, int native_width);
#endif
static void switch_crt_hz(videocrt_switch_t *p_switch)
static bool crt_check_for_changes(videocrt_switch_t *p_switch)
{
float ra_core_hz = p_switch->ra_core_hz;
if ((p_switch->ra_tmp_height != p_switch->ra_core_height) ||
(p_switch->ra_core_width != p_switch->ra_tmp_width) ||
(p_switch->center_adjust != p_switch->tmp_center_adjust||
p_switch->porch_adjust != p_switch->tmp_porch_adjust ) ||
(p_switch->ra_core_hz != p_switch->ra_tmp_core_hz))
return true;
/* set hz float to an int for windows switching */
if (ra_core_hz < 100)
{
if (ra_core_hz < 53)
p_switch->ra_set_core_hz = 50;
if (ra_core_hz >= 53 && ra_core_hz < 57)
p_switch->ra_set_core_hz = 55;
if (ra_core_hz >= 57)
p_switch->ra_set_core_hz = 60;
}
if (ra_core_hz > 100)
{
if (ra_core_hz < 106)
p_switch->ra_set_core_hz = 120;
if (ra_core_hz >= 106 && ra_core_hz < 114)
p_switch->ra_set_core_hz = 110;
if (ra_core_hz >= 114)
p_switch->ra_set_core_hz = 120;
}
video_monitor_set_refresh_rate(p_switch->ra_set_core_hz);
p_switch->ra_tmp_core_hz = ra_core_hz;
return false;
}
static void crt_aspect_ratio_switch(
videocrt_switch_t *p_switch,
unsigned width, unsigned height)
static void crt_store_temp_changes(videocrt_switch_t *p_switch)
{
/* send aspect float to video_driver */
p_switch->fly_aspect = (float)width / height;
video_driver_set_aspect_ratio_value((float)p_switch->fly_aspect);
}
static void switch_res_crt(
videocrt_switch_t *p_switch,
unsigned width, unsigned height)
{
video_display_server_set_resolution(width, height,
p_switch->ra_set_core_hz,
p_switch->ra_core_hz,
p_switch->center_adjust,
p_switch->index,
p_switch->center_adjust,
p_switch->porch_adjust);
#if defined(HAVE_VIDEOCORE)
crt_rpi_switch(width, height,
p_switch->ra_core_hz,
p_switch->center_adjust);
video_monitor_set_refresh_rate(p_switch->ra_core_hz);
crt_switch_driver_reinit();
#endif
video_driver_apply_state_changes();
}
/* Create correct aspect to fit video
* if resolution does not exist */
static void crt_screen_setup_aspect(
videocrt_switch_t *p_switch,
unsigned width, unsigned height)
{
#if defined(HAVE_VIDEOCORE)
if (height > 300)
height = height/2;
#endif
if (p_switch->ra_core_hz != p_switch->ra_tmp_core_hz)
switch_crt_hz(p_switch);
/* Get original resolution of core */
if (height == 4)
{
/* Detect menu only */
if (width < 700)
width = 320;
height = 240;
crt_aspect_ratio_switch(p_switch, width, height);
}
if (height < 200 && height != 144)
{
crt_aspect_ratio_switch(p_switch, width, height);
height = 200;
}
if (height > 200)
crt_aspect_ratio_switch(p_switch, width, height);
if (height == 144 && p_switch->ra_set_core_hz == 50)
{
height = 288;
crt_aspect_ratio_switch(p_switch, width, height);
}
if (height > 200 && height < 224)
{
crt_aspect_ratio_switch(p_switch, width, height);
height = 224;
}
if (height > 224 && height < 240)
{
crt_aspect_ratio_switch(p_switch, width, height);
height = 240;
}
if (height > 240 && height < 255)
{
crt_aspect_ratio_switch(p_switch, width, height);
height = 254;
}
if (height == 528 && p_switch->ra_set_core_hz == 60)
{
crt_aspect_ratio_switch(p_switch, width, height);
height = 480;
}
if (height >= 240 && height < 255 && p_switch->ra_set_core_hz == 55)
{
crt_aspect_ratio_switch(p_switch, width, height);
height = 254;
}
switch_res_crt(p_switch, width, height);
}
static int crt_compute_dynamic_width(
videocrt_switch_t *p_switch,
int width)
{
unsigned i;
int dynamic_width = 0;
unsigned min_height = 261;
#if defined(HAVE_VIDEOCORE)
p_switch->p_clock = 32000000;
#else
p_switch->p_clock = 21000000;
#endif
for (i = 0; i < 10; i++)
{
dynamic_width = width * i;
if ((dynamic_width * min_height * p_switch->ra_core_hz)
> p_switch->p_clock)
break;
}
return dynamic_width;
}
void crt_switch_res_core(
videocrt_switch_t *p_switch,
unsigned width, unsigned height,
float hz, unsigned crt_mode,
int crt_switch_center_adjust,
int crt_switch_porch_adjust,
int monitor_index, bool dynamic)
{
/* ra_core_hz float passed from within
* video_driver_monitor_adjust_system_rates() */
if (width == 4)
{
width = 320;
height = 240;
}
p_switch->porch_adjust = crt_switch_porch_adjust;
p_switch->ra_core_height = height;
p_switch->ra_core_hz = hz;
if (dynamic)
p_switch->ra_core_width = crt_compute_dynamic_width(p_switch, width);
else
p_switch->ra_core_width = width;
p_switch->center_adjust = crt_switch_center_adjust;
p_switch->index = monitor_index;
if (crt_mode == 2)
{
if (hz > 53)
p_switch->ra_core_hz = hz * 2;
if (hz <= 53)
p_switch->ra_core_hz = 120.0f;
}
/* Detect resolution change and switch */
if (
(p_switch->ra_tmp_height != p_switch->ra_core_height) ||
(p_switch->ra_core_width != p_switch->ra_tmp_width) ||
(p_switch->center_adjust != p_switch->tmp_center_adjust||
p_switch->porch_adjust != p_switch->tmp_porch_adjust )
)
crt_screen_setup_aspect(
p_switch,
p_switch->ra_core_width,
p_switch->ra_core_height);
p_switch->ra_tmp_height = p_switch->ra_core_height;
p_switch->ra_tmp_width = p_switch->ra_core_width;
p_switch->tmp_center_adjust = p_switch->center_adjust;
p_switch->tmp_porch_adjust = p_switch->porch_adjust;
p_switch->tmp_porch_adjust = p_switch->porch_adjust;
p_switch->ra_tmp_core_hz = p_switch->ra_core_hz;
/* Check if aspect is correct, if not change */
if (video_driver_get_aspect_ratio() != p_switch->fly_aspect)
{
video_driver_set_aspect_ratio_value((float)p_switch->fly_aspect);
video_driver_apply_state_changes();
}
}
static void switch_crt_hz(videocrt_switch_t *p_switch)
{
video_monitor_set_refresh_rate(p_switch->sr_core_hz);
}
static void crt_aspect_ratio_switch(
videocrt_switch_t *p_switch,
unsigned width, unsigned height, unsigned srm_width, unsigned srm_height)
{
/* send aspect float to video_driver */
RARCH_LOG("[CRT]: Setting Video Screen Size to: %dx%d \n", width, height);
video_driver_set_size(srm_width , srm_height);
video_driver_set_viewport(srm_width , srm_height,1,1);
p_switch->fly_aspect = (float)width / (float)height;
video_driver_set_aspect_ratio_value((float)p_switch->fly_aspect);
RARCH_LOG("[CRT]: Setting Aspect Ratio: %f \n", (float)p_switch->fly_aspect);
video_driver_apply_state_changes();
}
static void set_aspect(videocrt_switch_t *p_switch, unsigned int width,
unsigned int height, unsigned int srm_width, unsigned srm_height,
unsigned int srm_xscale, unsigned srm_yscale)
{
unsigned int patched_width = 0;
unsigned int patched_height = 0;
int scaled_width = 0;
int scaled_height = 0;
/* used to fix aspect shoule SR not find a resolution */
if (srm_width == 0)
{
video_driver_get_size(&patched_width, &patched_height);
}else{
patched_width = width;
patched_height = height;
}
scaled_width = roundf(patched_width*srm_xscale);
scaled_height = roundf(patched_height*srm_yscale);
crt_aspect_ratio_switch(p_switch, scaled_width, scaled_height, srm_width, srm_height);
}
#if !defined(HAVE_VIDEOCORE)
static bool crt_sr2_init(videocrt_switch_t *p_switch, int monitor_index, unsigned int crt_mode, unsigned int super_width)
{
const char* err_msg;
char* mode;
char index = 0;
char mindex[1];
if (monitor_index+1 >= 0 && monitor_index+1 < 10)
index = monitor_index+48;
else
index = '0';
mindex[0] = index;
if (!p_switch->sr2_active)
{
RARCH_LOG("[CRT]: SR init \n");
sr_init();
#if (__STDC_VERSION__ >= 199409L) /* no logs for C98 or less */
sr_set_log_callback_info(RARCH_LOG);
sr_set_log_callback_debug(RARCH_DBG);
sr_set_log_callback_error(RARCH_ERR);
#endif
if (crt_mode == 1)
{
sr_set_monitor("arcade_15");
RARCH_LOG("[CRT]: CRT Mode: %d - arcade_15 \n", crt_mode) ;
}else if (crt_mode == 2)
{
sr_set_monitor("arcade_31");
RARCH_LOG("[CRT]: CRT Mode: %d - arcade_31 \n", crt_mode) ;
}else if (crt_mode == 3)
{
sr_set_monitor("pc_31_120");
RARCH_LOG("[CRT]: CRT Mode: %d - pc_31_120 \n", crt_mode) ;
}else if (crt_mode == 4)
{
RARCH_LOG("[CRT]: CRT Mode: %d - Selected from ini \n", crt_mode) ;
}
if (super_width >2 )
sr_set_user_mode(super_width, 0, 0);
RARCH_LOG("[CRT]: SR init_disp \n");
if (monitor_index+1 > 0)
{
RARCH_LOG("SRobj: RA Monitor Index: %s\n",mindex);
p_switch->rtn = sr_init_disp(mindex);
RARCH_LOG("[CRT]: SR Disp Monitor Index: %s \n", mindex);
}
if (monitor_index == -1)
{
RARCH_LOG("SRobj: RA Monitor Index: %s\n",NULL);
p_switch->rtn = sr_init_disp(NULL);
RARCH_LOG("[CRT]: SR Disp Monitor Index: Auto \n");
}
RARCH_LOG("[CRT]: SR rtn %d \n", p_switch->rtn);
}
if (p_switch->rtn == 1)
{
p_switch->sr2_active = true;
return true;
}else{
RARCH_LOG("[CRT]: SR failed to init \n");
sr_deinit();
p_switch->sr2_active = false;
}
return false;
}
static void switch_res_crt(
videocrt_switch_t *p_switch,
unsigned width, unsigned height, unsigned crt_mode, unsigned native_width, int monitor_index, int super_width)
{
unsigned char interlace = 0, ret;
const char* err_msg;
int w = native_width, h = height;
double rr = p_switch->ra_core_hz;
if (crt_sr2_init(p_switch, monitor_index, crt_mode, super_width)) /* Checked SR2 is loded if not Load it */
{
ret = sr_switch_to_mode(w, h, rr, interlace, &srm);
if(!ret)
{
RARCH_LOG("[CRT]: SR failed to switch mode");
/*sr_deinit();*/
}
p_switch->sr_core_hz = srm.refresh;
set_aspect(p_switch, w , h, srm.width, srm.height, srm.x_scale, srm.y_scale);
RARCH_LOG("[CRT]: SR scaled X:%d Y:%d \n",srm.x_scale, srm.y_scale);
}else {
set_aspect(p_switch, width , height, width, height ,1,1);
video_driver_set_size(width , height);
video_driver_apply_state_changes();
}
}
#endif
void crt_destroy_modes(videocrt_switch_t *p_switch)
{
if (p_switch->sr2_active == true)
{
p_switch->sr2_active = false;
sr_deinit();
/*RARCH_LOG("[CRT]: SR Destroyed \n"); */
}
}
static void crt_check_hh_core(videocrt_switch_t *p_switch)
{
/*
char* handheld[8] = {"mGBA","Gambatte","gpSP","Gearboy","VBA Next","VBA-M","SameBoy","TGB Dual"};
int i = 0;
for(i = 0; i < 7; i++)
{
if (strcmp(handheld[i],p_switch->core_name) == 0)
{
RARCH_LOG("[CRT]: Handheld core detected %s adjusting resolutions.\n", p_switch->core_name);
p_switch->hh_core = true;
break;
}
else
{
p_switch->hh_core = false;
}
}
*/
p_switch->hh_core = false;
}
#if !defined(HAVE_VIDEOCORE)
static void crt_fix_hh_res(videocrt_switch_t *p_switch, int native_width, int width,
int height, int crt_mode, int monitor_index, int super_width)
{
int corrected_width = 320;
int corrected_height = 240;
switch_res_crt(p_switch, corrected_width, corrected_height , crt_mode, corrected_width, monitor_index-1, super_width);
set_aspect(p_switch, native_width , height, native_width, height ,1,1);
video_driver_set_size(native_width , height);
}
#endif
/*
static void crt_menu_restore(videocrt_switch_t *p_switch)
{
video_driver_get_size(&p_switch->fb_width, &p_switch->fb_height);
RARCH_LOG("[CRT]: Menu Only Restoring Aspect: %dx%d \n", p_switch->fb_width, p_switch->fb_height);
crt_aspect_ratio_switch(p_switch, p_switch->fb_width, p_switch->fb_height, p_switch->fb_width, p_switch->fb_height);
}
static bool crt_get_desktop_res(videocrt_switch_t *p_switch, unsigned width, unsigned height, float hz)
{
if (p_switch->menu_active == false)
{
if (p_switch->fb_width == 0)
video_driver_get_size(&p_switch->fb_width, &p_switch->fb_height);
p_switch->fb_ra_core_hz = 60.0;
RARCH_LOG("[CRT]: Storing Desktop Resolution: %dx%d@%f \n", p_switch->fb_width, p_switch->fb_height, p_switch->fb_ra_core_hz);
crt_menu_restore(p_switch);
p_switch->menu_active = true;
return true;
}
return false;
}
*/
void crt_switch_res_core(
videocrt_switch_t *p_switch,
unsigned native_width, unsigned width, unsigned height,
float hz, unsigned crt_mode,
int crt_switch_center_adjust,
int crt_switch_porch_adjust,
int monitor_index, bool dynamic,
int super_width, bool hires_menu)
{
if (height <= 4)
{
if (hires_menu == true)
{
native_width = 640;
width = 640;
height = 480;
hz = 60;
}else{
native_width = 320;
width = 320;
height = 240;
hz = 60;
}
}
if (height != 4 )
{
p_switch->menu_active = false;
p_switch->porch_adjust = crt_switch_porch_adjust;
p_switch->ra_core_height = height;
p_switch->ra_core_hz = hz;
p_switch->ra_core_width = width;
p_switch->center_adjust = crt_switch_center_adjust;
p_switch->index = monitor_index;
if (p_switch->core_name != crt_switch_core_name())
{
p_switch->core_name = crt_switch_core_name();
RARCH_LOG("[CRT]: Current running core %s \n", p_switch->core_name);
crt_check_hh_core(p_switch);
}
/* Detect resolution change and switch */
if (crt_check_for_changes(p_switch))
{
RARCH_LOG("[CRT]: Requested Reolution: %dx%d@%f \n", native_width, height, hz);
#if defined(HAVE_VIDEOCORE)
crt_rpi_switch(p_switch, width, height, hz, 0, native_width);
#else
if (p_switch->hh_core == false)
switch_res_crt(p_switch, p_switch->ra_core_width, p_switch->ra_core_height , crt_mode, native_width, monitor_index-1, super_width);
else
crt_fix_hh_res(p_switch, native_width, width, height, crt_mode, monitor_index, super_width);
#endif
switch_crt_hz(p_switch);
crt_store_temp_changes(p_switch);
}
if (video_driver_get_aspect_ratio() != p_switch->fly_aspect)
{
RARCH_LOG("[CRT]: Restoring Aspect Ratio: %f \n", (float)p_switch->fly_aspect);
video_driver_set_aspect_ratio_value((float)p_switch->fly_aspect);
video_driver_apply_state_changes();
}
}
}
/* only used for RPi3 */
#if defined(HAVE_VIDEOCORE)
static void crt_rpi_switch(int width, int height, float hz, int xoffset)
static void crt_rpi_switch(videocrt_switch_t *p_switch, int width, int height, float hz, int xoffset, int native_width)
{
char buffer[1024];
VCHI_INSTANCE_T vchi_instance;
@ -279,25 +412,37 @@ static void crt_rpi_switch(int width, int height, float hz, int xoffset)
float roundw = 0.0f;
float roundh = 0.0f;
float pixel_clock = 0.0f;
int xscale = 1;
int yscale = 1;
if (height > 300)
height = height/2;
/* set core refresh from hz */
video_monitor_set_refresh_rate(hz);
/* following code is the mode line generator */
hsp = (width * 0.117) - (xoffset*4);
if (width < 700)
set_aspect(p_switch, width,
height, width, height,
1, 1);
int w = width;
while (w < 1920)
{
hfp = (width * 0.065);
hbp = width * 0.35-hsp-hfp;
}
else
{
hfp = (width * 0.033) + (width / 112);
hbp = (width * 0.225) + (width /58);
xoffset = xoffset*2;
w = w+width;
}
hmax = hbp;
if (w > 2000)
w =w- width;
width = w;
crt_aspect_ratio_switch(p_switch, width,height);
/* following code is the mode line generator */
hfp = ((width * 0.044) + (width / 112));
hbp = ((width * 0.172) + (width /64));
hsp = (width * 0.117);
if (height < 241)
vmax = 261;
@ -376,5 +521,7 @@ static void crt_rpi_switch(int width, int height, float hz, int xoffset)
"fbset -g %d %d %d %d 24 > /dev/null",
width, height, width, height);
system(output2);
crt_switch_driver_refresh();
}
#endif

View File

@ -25,6 +25,7 @@
#include <boolean.h>
#include <retro_common_api.h>
RETRO_BEGIN_DECLS
typedef struct videocrt_switch
@ -35,21 +36,33 @@ typedef struct videocrt_switch
int porch_adjust;
int tmp_porch_adjust;
int tmp_center_adjust;
int rtn;
unsigned ra_core_width;
unsigned ra_core_height;
unsigned ra_tmp_width;
unsigned ra_tmp_height;
unsigned ra_set_core_hz;
unsigned index;
unsigned int fb_width;
unsigned int fb_height;
float ra_core_hz;
float sr_core_hz;
float ra_tmp_core_hz;
float fly_aspect;
float fb_ra_core_hz;
bool sr2_active;
bool menu_active;
char* core_name;
bool hh_core;
} videocrt_switch_t;
void crt_switch_res_core(
videocrt_switch_t *p_switch,
unsigned naitive_width,
unsigned width,
unsigned height,
float hz,
@ -57,7 +70,11 @@ void crt_switch_res_core(
int crt_switch_center_adjust,
int crt_switch_porch_adjust,
int monitor_index,
bool dynamic);
bool dynamic,
int super_width,
bool hires_menu);
void crt_destroy_modes(videocrt_switch_t *p_switch);
RETRO_END_DECLS

View File

@ -971,7 +971,7 @@ MIDI
/*============================================================
DRIVERS
============================================================ */
#include "../gfx/video_crt_switch.c"
/*#include "../gfx/video_crt_switch.c" */
#include "../gfx/gfx_animation.c"
#include "../gfx/gfx_display.c"
#include "../gfx/gfx_thumbnail_path.c"

View File

@ -62,6 +62,7 @@ typedef struct
/* TODO/FIXME - static globals */
static winraw_mouse_t *g_mice = NULL;
static RECT *prev_rect = NULL; /* Needed to store RECT to checking for a windows size change */
#define WINRAW_KEYBOARD_PRESSED(wr, key) (wr->keyboard.keys[rarch_keysym_lut[(enum retro_key)(key)]])
@ -337,6 +338,19 @@ static void winraw_update_mouse_state(winraw_input_t *wr,
winraw_mouse_t *mouse, RAWMOUSE *state)
{
POINT crs_pos;
RECT *tmp_rect = NULL;
/* used for fixing cordinates after switching resolutions */
GetClientRect((HWND)video_driver_window_get(), tmp_rect);
if (!prev_rect)
{
GetClientRect((HWND)video_driver_window_get(), prev_rect);
winraw_init_mouse_xy_mapping(wr);
}
else if (tmp_rect != prev_rect)
{
GetClientRect((HWND)video_driver_window_get(), prev_rect);
winraw_init_mouse_xy_mapping(wr);
}
if (state->usFlags & MOUSE_MOVE_ABSOLUTE)
{

View File

@ -4472,6 +4472,10 @@ MSG_HASH(
MENU_ENUM_LABEL_CHEAT_DELETE_MATCH,
"cheat_delete_match"
)
MSG_HASH(
MENU_ENUM_LABEL_CRT_SWITCH_HIRES_MENU,
"crt_switch_hires_menu"
)
MSG_HASH(
MENU_ENUM_LABEL_CRT_SWITCH_RESOLUTION_USE_CUSTOM_REFRESH_RATE,
"crt_switch_resolution_use_custom_refresh_rate"

View File

@ -1437,9 +1437,17 @@ MSG_HASH(
MENU_ENUM_SUBLABEL_CRT_SWITCH_PORCH_ADJUST,
"Cycle through these options to adjust the porch settings to change the image size."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CRT_SWITCH_HIRES_MENU,
"Use high resolution menu"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_CRT_SWITCH_HIRES_MENU,
"Use high resolution menu"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CRT_SWITCH_RESOLUTION_USE_CUSTOM_REFRESH_RATE,
"Use Custom Refresh Rate"
"Use a custom refresh rate specified in the configuration file if needed."
)
MSG_HASH(
MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION_USE_CUSTOM_REFRESH_RATE,

View File

@ -137,6 +137,7 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_crt_switchres_super, MENU_ENUM
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_crt_switchres_x_axis_centering, MENU_ENUM_SUBLABEL_CRT_SWITCH_X_AXIS_CENTERING)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_crt_switchres_porch_adjust, MENU_ENUM_SUBLABEL_CRT_SWITCH_PORCH_ADJUST)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_crt_switchres_use_custom_refresh_rate, MENU_ENUM_SUBLABEL_CRT_SWITCH_RESOLUTION_USE_CUSTOM_REFRESH_RATE)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_crt_switchres_hires_menu, MENU_ENUM_SUBLABEL_CRT_SWITCH_HIRES_MENU)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_driver_settings_list, MENU_ENUM_SUBLABEL_DRIVER_SETTINGS)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_retro_achievements_settings_list, MENU_ENUM_SUBLABEL_RETRO_ACHIEVEMENTS_SETTINGS)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_saving_settings_list, MENU_ENUM_SUBLABEL_SAVING_SETTINGS)
@ -1839,6 +1840,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs,
case MENU_ENUM_LABEL_CRT_SWITCH_RESOLUTION_USE_CUSTOM_REFRESH_RATE:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_crt_switchres_use_custom_refresh_rate);
break;
case MENU_ENUM_LABEL_CRT_SWITCH_HIRES_MENU:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_crt_switchres_hires_menu);
break;
case MENU_ENUM_LABEL_AUDIO_RESAMPLER_QUALITY:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_audio_resampler_quality);
break;

View File

@ -7799,6 +7799,7 @@ unsigned menu_displaylist_build_list(
{MENU_ENUM_LABEL_CRT_SWITCH_X_AXIS_CENTERING, PARSE_ONLY_INT },
{MENU_ENUM_LABEL_CRT_SWITCH_PORCH_ADJUST, PARSE_ONLY_INT },
{MENU_ENUM_LABEL_CRT_SWITCH_RESOLUTION_USE_CUSTOM_REFRESH_RATE, PARSE_ONLY_BOOL},
{MENU_ENUM_LABEL_CRT_SWITCH_HIRES_MENU, PARSE_ONLY_BOOL},
};
for (i = 0; i < ARRAY_SIZE(build_list); i++)

View File

@ -6132,7 +6132,13 @@ static void setting_get_string_representation_uint_crt_switch_resolutions(
strlcpy(s, "15 KHz", len);
break;
case CRT_SWITCH_31KHZ:
strlcpy(s, "31 KHz", len);
strlcpy(s, "31 KHz, Standard", len);
break;
case CRT_SWITCH_32_120:
strlcpy(s, "31 KHz, 120Hz", len);
break;
case CRT_SWITCH_INI:
strlcpy(s, "INI", len);
break;
}
}
@ -11661,7 +11667,7 @@ static bool setting_append_list(
(*list)[list_info->index - 1].get_string_representation =
&setting_get_string_representation_uint_crt_switch_resolutions;
SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_ADVANCED);
menu_settings_list_current_add_range(list, list_info, CRT_SWITCH_NONE, CRT_SWITCH_31KHZ, 1.0, true, true);
menu_settings_list_current_add_range(list, list_info, CRT_SWITCH_NONE, CRT_SWITCH_INI, 1.0, true, true);
CONFIG_UINT(
list, list_info,
@ -11730,6 +11736,22 @@ static bool setting_append_list(
SD_FLAG_NONE
);
CONFIG_BOOL(
list, list_info,
&settings->bools.crt_switch_hires_menu,
MENU_ENUM_LABEL_CRT_SWITCH_HIRES_MENU,
MENU_ENUM_LABEL_VALUE_CRT_SWITCH_HIRES_MENU,
false,
MENU_ENUM_LABEL_VALUE_OFF,
MENU_ENUM_LABEL_VALUE_ON,
&group_info,
&subgroup_info,
parent_group,
general_write_handler,
general_read_handler,
SD_FLAG_NONE
);
END_SUB_GROUP(list, list_info, parent_group);
END_GROUP(list, list_info, parent_group);
break;

View File

@ -1014,6 +1014,7 @@ enum msg_hash_enums
MENU_LABEL(CRT_SWITCH_RESOLUTION),
MENU_LABEL(CRT_SWITCH_RESOLUTION_SUPER),
MENU_LABEL(CRT_SWITCH_RESOLUTION_OUTPUT_DISPLAY_ID),
MENU_LABEL(CRT_SWITCH_HIRES_MENU),
MENU_LABEL(CRT_SWITCH_RESOLUTION_USE_CUSTOM_REFRESH_RATE),
MENU_LABEL(CRT_SWITCH_X_AXIS_CENTERING),
MENU_LABEL(CRT_SWITCH_PORCH_ADJUST),

View File

@ -262,6 +262,7 @@ if [ "$OS" = 'Darwin' ]; then
check_lib '' AL "-framework OpenAL" alcOpenDevice
HAVE_X11=no # X11 breaks on recent OSXes even if present.
HAVE_SDL=no
HAVE_SW2=no
else
check_lib '' AL -lopenal alcOpenDevice
fi
@ -676,3 +677,11 @@ fi
check_enabled 'ZLIB BUILTINZLIB' RPNG RPNG 'zlib is' false
check_enabled V4L2 VIDEOPROCESSOR 'video processor' 'Video4linux2 is' true
if [ "$HAVE_CXX11" = 'yes' ]; then
if [ "$OS" = 'Linux' ]; then
check_enabled 'VIDEOCORE X11' SR2 'CRT modeswitching' 'CRT is' true
else
check_platform Win32 SR2 'CRT modeswitching is' true
fi
fi

View File

@ -195,3 +195,5 @@ HAVE_ODROIDGO2=no # ODROID-GO Advance rotation support (requires librga
HAVE_LIBSHAKE=no # libShake haptic feedback support
HAVE_CHECK=no # check support for unit tests
HAVE_WIFI=no # wifi driver support
HAVE_CRTSWITCHRES=auto # CRT mode switching support
C89_CRTSWITCHRES=no

View File

@ -2,9 +2,9 @@
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
* Copyright (C) 2011-2017 - Daniel De Matteis
* Copyright (C) 2012-2015 - Michael Lelli
* Copyright (C) 2014-2017 - Jean-André Santoni
* Copyright (C) 2014-2017 - Jean-Andr<EFBFBD> Santoni
* Copyright (C) 2016-2019 - Brad Parker
* Copyright (C) 2016-2019 - Andrés Suárez (input mapper/Discord code)
* Copyright (C) 2016-2019 - Andr<EFBFBD>s Su<EFBFBD>rez (input mapper/Discord code)
* Copyright (C) 2016-2017 - Gregor Richards (network code)
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
@ -226,7 +226,9 @@
#include "gfx/video_thread_wrapper.h"
#endif
#include "gfx/video_display_server.h"
#include "gfx/video_crt_switch.h"
#ifdef HAVE_SR2
#include "gfx/video_crt_switch.h"
#endif
#include "bluetooth/bluetooth_driver.h"
#include "wifi/wifi_driver.h"
#include "misc/cpufreq/cpufreq.h"
@ -31600,6 +31602,7 @@ static void video_driver_frame(const void *data, unsigned width,
#if defined(HAVE_GFX_WIDGETS)
bool widgets_active = p_rarch->widgets_active;
#endif
static int native_width = 0;
status_text[0] = '\0';
video_driver_msg[0] = '\0';
@ -31969,7 +31972,7 @@ static void video_driver_frame(const void *data, unsigned width,
if (video_info.crt_switch_resolution)
{
p_rarch->video_driver_crt_switching_active = true;
native_width = width;
switch (video_info.crt_switch_resolution_super)
{
case 2560:
@ -31986,27 +31989,36 @@ static void video_driver_frame(const void *data, unsigned width,
p_rarch->video_driver_crt_dynamic_super_width = false;
break;
}
#if defined(HAVE_SR2) && !defined(ANDROID)
crt_switch_res_core(
&p_rarch->crt_switch_st,
width,
native_width, width,
height,
p_rarch->video_driver_core_hz,
video_info.crt_switch_resolution,
video_info.crt_switch_center_adjust,
video_info.crt_switch_porch_adjust,
video_info.monitor_index,
p_rarch->video_driver_crt_dynamic_super_width);
p_rarch->video_driver_crt_dynamic_super_width,
video_info.crt_switch_resolution_super,
video_info.crt_switch_hires_menu);
#endif
}
else if (!video_info.crt_switch_resolution)
p_rarch->video_driver_crt_switching_active = false;
}
void crt_switch_driver_reinit(void)
void crt_switch_driver_refresh(void)
{
/*video_context_driver_reset();*/
video_driver_reinit(DRIVERS_CMD_ALL);
}
char* crt_switch_core_name(void)
{
return (char*)runloop_state.system.info.library_name;
}
void video_driver_display_type_set(enum rarch_display_type type)
{
struct rarch_state *p_rarch = &rarch_st;
@ -32106,7 +32118,8 @@ void video_driver_build_info(video_frame_info_t *video_info)
video_info->crt_switch_resolution = settings->uints.crt_switch_resolution;
video_info->crt_switch_resolution_super = settings->uints.crt_switch_resolution_super;
video_info->crt_switch_center_adjust = settings->ints.crt_switch_center_adjust;
video_info->crt_switch_porch_adjust = settings->ints.crt_switch_porch_adjust;
video_info->crt_switch_porch_adjust = settings->ints.crt_switch_porch_adjust;
video_info->crt_switch_hires_menu = settings->bools.crt_switch_hires_menu;
video_info->black_frame_insertion = settings->uints.video_black_frame_insertion;
video_info->hard_sync = settings->bools.video_hard_sync;
video_info->hard_sync_frames = settings->uints.video_hard_sync_frames;
@ -33504,6 +33517,15 @@ static void retroarch_deinit_drivers(
}
#endif
/* Switchres deinit */
if (p_rarch->video_driver_crt_switching_active) {
/* RARCH_LOG("[CRT]: Getting video info\n");
RARCH_LOG("[CRT]: About to destroy SR\n");
*/
#ifdef HAVE_SR2
crt_destroy_modes(&p_rarch->crt_switch_st);
#endif
}
/* Video */
video_display_server_destroy();
@ -33551,6 +33573,7 @@ static void retroarch_deinit_drivers(
cbs->state_cb = NULL;
p_rarch->current_core.inited = false;
}
bool driver_ctl(enum driver_ctl_state state, void *data)

View File

@ -1237,6 +1237,8 @@ typedef struct video_frame_info
bool menu_is_alive;
bool menu_screensaver_active;
bool msg_bgcolor_enable;
bool crt_switch_hires_menu;
} video_frame_info_t;
typedef void (*update_window_title_cb)(void*);
@ -1715,7 +1717,9 @@ void video_monitor_set_refresh_rate(float hz);
bool video_monitor_fps_statistics(double *refresh_rate,
double *deviation, unsigned *sample_points);
void crt_switch_driver_reinit(void);
void crt_switch_driver_refresh(void);
char* crt_switch_core_name(void);
#define video_driver_translate_coord_viewport_wrap(vp, mouse_x, mouse_y, res_x, res_y, res_screen_x, res_screen_y) \
(video_driver_get_viewport_info(vp) ? video_driver_translate_coord_viewport(vp, mouse_x, mouse_y, res_x, res_y, res_screen_x, res_screen_y) : false)

View File

@ -1719,8 +1719,9 @@ struct rarch_state
double audio_source_ratio_original;
double audio_source_ratio_current;
struct retro_system_av_info video_driver_av_info; /* double alignment */
#ifdef HAVE_SR2
videocrt_switch_t crt_switch_st; /* double alignment */
#endif
retro_time_t frame_limit_minimum_time;
retro_time_t frame_limit_last_time;
retro_time_t libretro_core_runtime_last;
@ -2626,7 +2627,7 @@ struct key_desc key_descriptors[RARCH_MAX_KEYS] =
{RETROK_BREAK, "Break"},
{RETROK_MENU, "Menu"},
{RETROK_POWER, "Power"},
{RETROK_EURO, {-30, -126, -84, 0}}, /* "" */
{RETROK_EURO, {-30, -126, -84, 0}}, /* "<EFBFBD>" */
{RETROK_UNDO, "Undo"},
{RETROK_OEM_102, "OEM-102"}
};