mirror of
https://github.com/libretro/RetroArch
synced 2025-03-29 22:20:21 +00:00
Merge pull request #12403 from alphanu1/master
New Switchres (CRT Resolution switching) For Retroarch
This commit is contained in:
commit
2259e9b38e
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
59
deps/switchres/.gitlab-ci.yml
vendored
Normal 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
60
deps/switchres/README.md
vendored
Normal 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
178
deps/switchres/custom_video.cpp
vendored
Normal 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
107
deps/switchres/custom_video.h
vendored
Normal 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
532
deps/switchres/custom_video_adl.cpp
vendored
Normal 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
201
deps/switchres/custom_video_adl.h
vendored
Normal 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
343
deps/switchres/custom_video_ati.cpp
vendored
Normal 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
53
deps/switchres/custom_video_ati.h
vendored
Normal 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;
|
||||
};
|
848
deps/switchres/custom_video_ati_family.cpp
vendored
Normal file
848
deps/switchres/custom_video_ati_family.cpp
vendored
Normal 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
795
deps/switchres/custom_video_drmkms.cpp
vendored
Normal 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
81
deps/switchres/custom_video_drmkms.h
vendored
Normal 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
628
deps/switchres/custom_video_pstrip.cpp
vendored
Normal 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
83
deps/switchres/custom_video_pstrip.h
vendored
Normal 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
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
123
deps/switchres/custom_video_xrandr.h
vendored
Normal 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
481
deps/switchres/display.cpp
vendored
Normal 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
162
deps/switchres/display.h
vendored
Normal 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
168
deps/switchres/display_linux.cpp
vendored
Normal 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
30
deps/switchres/display_linux.h
vendored
Normal 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
260
deps/switchres/display_windows.cpp
vendored
Normal 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
45
deps/switchres/display_windows.h
vendored
Normal 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
244
deps/switchres/edid.cpp
vendored
Normal 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
37
deps/switchres/edid.h
vendored
Normal 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
56
deps/switchres/examples/README.md
vendored
Normal 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
81
deps/switchres/examples/test_dlopen.cpp
vendored
Normal 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);
|
||||
}
|
30
deps/switchres/examples/test_liblink.cpp
vendored
Normal file
30
deps/switchres/examples/test_liblink.cpp
vendored
Normal 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
228
deps/switchres/grid.cpp
vendored
Normal 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
78
deps/switchres/log.cpp
vendored
Normal 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
38
deps/switchres/log.h
vendored
Normal 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
771
deps/switchres/modeline.cpp
vendored
Normal 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
140
deps/switchres/modeline.h
vendored
Normal 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
431
deps/switchres/monitor.cpp
vendored
Normal 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
64
deps/switchres/monitor.h
vendored
Normal 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
171
deps/switchres/resync_windows.cpp
vendored
Normal 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
43
deps/switchres/resync_windows.h
vendored
Normal 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
377
deps/switchres/switchres.cpp
vendored
Normal 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
110
deps/switchres/switchres.h
vendored
Normal 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
160
deps/switchres/switchres.ini
vendored
Normal 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
305
deps/switchres/switchres_main.cpp
vendored
Normal 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
223
deps/switchres/switchres_wrapper.cpp
vendored
Normal 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
128
deps/switchres/switchres_wrapper.h
vendored
Normal 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
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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"
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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++)
|
||||
|
@ -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;
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
41
retroarch.c
41
retroarch.c
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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"}
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user