diff --git a/workspace/all/common/displaycal.c b/workspace/all/common/displaycal.c new file mode 100644 index 000000000..6a7cab98d --- /dev/null +++ b/workspace/all/common/displaycal.c @@ -0,0 +1,147 @@ +#include "displaycal.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DISP_LCD_SET_GAMMA_TABLE 0x10b +#define DISP_LCD_GAMMA_CORRECTION_ENABLE 0x10c +#define DISP_LCD_GAMMA_CORRECTION_DISABLE 0x10d + +#define DISPLAYCAL_LUT_ENTRIES 256 +#define DISPLAYCAL_LUT_BYTES (DISPLAYCAL_LUT_ENTRIES * 4) + +typedef struct { + double red_gain; + double green_gain; + double blue_gain; +} DisplayCalGains; + +static unsigned char clamp_u8(double value) { + if (value < 0.0) + return 0; + if (value > 255.0) + return 255; + return (unsigned char)(value + 0.5); +} + +static double srgb_to_linear(double c) { + if (c <= 0.04045) + return c / 12.92; + return pow((c + 0.055) / 1.055, 2.4); +} + +static double linear_to_srgb(double c) { + if (c <= 0.0) + return 0.0; + if (c <= 0.0031308) + return c * 12.92; + return 1.055 * pow(c, 1.0 / 2.4) - 0.055; +} + +static void fill_linear_gain_table(uint32_t table[DISPLAYCAL_LUT_ENTRIES], const DisplayCalGains *gains) { + for (int i = 0; i < DISPLAYCAL_LUT_ENTRIES; i++) { + double linear = srgb_to_linear(i / 255.0); + uint32_t r = clamp_u8(linear_to_srgb(linear * gains->red_gain) * 255.0); + uint32_t g = clamp_u8(linear_to_srgb(linear * gains->green_gain) * 255.0); + uint32_t b = clamp_u8(linear_to_srgb(linear * gains->blue_gain) * 255.0); + table[i] = (r << 16) | (g << 8) | b; + } +} + +static void fill_identity_table(uint32_t table[DISPLAYCAL_LUT_ENTRIES]) { + for (int i = 0; i < DISPLAYCAL_LUT_ENTRIES; i++) + table[i] = ((uint32_t)i << 16) | ((uint32_t)i << 8) | (uint32_t)i; +} + +static int open_disp(void) { + int fd = open("/dev/disp", O_RDWR); + if (fd < 0) + fprintf(stderr, "displaycal: open /dev/disp failed: %s\n", strerror(errno)); + return fd; +} + +static int disable_gamma(int fd, int screen) { + unsigned long param[4] = { (unsigned long)screen, 0, 0, 0 }; + if (ioctl(fd, DISP_LCD_GAMMA_CORRECTION_DISABLE, param) < 0) { + fprintf(stderr, "displaycal: disable gamma failed: %s\n", strerror(errno)); + return -1; + } + return 0; +} + +static int set_gamma_table(int fd, int screen, uint32_t table[DISPLAYCAL_LUT_ENTRIES]) { + unsigned long set_param[4] = { + (unsigned long)screen, + (unsigned long)table, + DISPLAYCAL_LUT_BYTES, + 0, + }; + + if (ioctl(fd, DISP_LCD_SET_GAMMA_TABLE, set_param) < 0) { + fprintf(stderr, "displaycal: set gamma table failed: %s\n", strerror(errno)); + return -1; + } + + return 0; +} + +static int enable_gamma(int fd, int screen) { + unsigned long enable_param[4] = { (unsigned long)screen, 0, 0, 0 }; + if (ioctl(fd, DISP_LCD_GAMMA_CORRECTION_ENABLE, enable_param) < 0) { + fprintf(stderr, "displaycal: enable gamma failed: %s\n", strerror(errno)); + return -1; + } + + return 0; +} + +static int apply_table(int fd, int screen, uint32_t table[DISPLAYCAL_LUT_ENTRIES]) { + if (set_gamma_table(fd, screen, table) < 0) + return -1; + return enable_gamma(fd, screen); +} + +static int reset_table_and_disable(int fd, int screen) { + uint32_t table[DISPLAYCAL_LUT_ENTRIES]; + fill_identity_table(table); + if (set_gamma_table(fd, screen, table) < 0) + return -1; + return disable_gamma(fd, screen); +} + +static int apply_gains(const DisplayCalGains *gains) { + int fd = open_disp(); + if (fd < 0) + return -1; + + uint32_t table[DISPLAYCAL_LUT_ENTRIES]; + fill_linear_gain_table(table, gains); + int ret = apply_table(fd, 0, table); + close(fd); + return ret; +} + +int DisplayCal_enableWithValues(int red_gain, int green_gain, int blue_gain) { + DisplayCalGains gains = { + .red_gain = (double)DisplayCal_clampGainValue(red_gain) / DISPLAYCAL_GAIN_SCALE, + .green_gain = (double)DisplayCal_clampGainValue(green_gain) / DISPLAYCAL_GAIN_SCALE, + .blue_gain = (double)DisplayCal_clampGainValue(blue_gain) / DISPLAYCAL_GAIN_SCALE, + }; + return apply_gains(&gains); +} + +int DisplayCal_disable(void) { + int fd = open_disp(); + if (fd < 0) + return -1; + + int ret = reset_table_and_disable(fd, 0); + close(fd); + return ret; +} diff --git a/workspace/all/common/displaycal.h b/workspace/all/common/displaycal.h new file mode 100644 index 000000000..8a0a65e3f --- /dev/null +++ b/workspace/all/common/displaycal.h @@ -0,0 +1,92 @@ +#ifndef DISPLAYCAL_H +#define DISPLAYCAL_H + +#define DISPLAYCAL_GAIN_SCALE 100 +#define DISPLAYCAL_GAIN_MIN 0 +#define DISPLAYCAL_GAIN_MAX 200 +#define DISPLAYCAL_DEFAULT_ENABLED 0 +#define DISPLAYCAL_DEFAULT_RED_GAIN 100 +#define DISPLAYCAL_DEFAULT_GREEN_GAIN 100 +#define DISPLAYCAL_DEFAULT_BLUE_GAIN 100 + +#define DISPLAYCAL_BRICK_DEFAULT_ENABLED 1 +#define DISPLAYCAL_BRICK_DEFAULT_RED_GAIN DISPLAYCAL_DEFAULT_RED_GAIN +#define DISPLAYCAL_BRICK_DEFAULT_GREEN_GAIN 92 +#define DISPLAYCAL_BRICK_DEFAULT_BLUE_GAIN 58 + +#define DISPLAYCAL_SMARTPRO_DEFAULT_ENABLED 1 +#define DISPLAYCAL_SMARTPRO_DEFAULT_RED_GAIN DISPLAYCAL_DEFAULT_RED_GAIN +#define DISPLAYCAL_SMARTPRO_DEFAULT_GREEN_GAIN 77 +#define DISPLAYCAL_SMARTPRO_DEFAULT_BLUE_GAIN 50 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct DisplayCalDefaults { + int enabled; + int red_gain; + int green_gain; + int blue_gain; +} DisplayCalDefaults; + +// TrimUI Brick, measured with X-Rite i1Display Pro, calibrated to sRGB D65 2.2 +static const struct DisplayCalDefaults DisplayCalDefaults_Brick = { + DISPLAYCAL_BRICK_DEFAULT_ENABLED, + DISPLAYCAL_BRICK_DEFAULT_RED_GAIN, + DISPLAYCAL_BRICK_DEFAULT_GREEN_GAIN, + DISPLAYCAL_BRICK_DEFAULT_BLUE_GAIN +}; + +// TrimUI Smart Pro, measured with Spyder 5 Pro and slightly modified by eye to match the Brick +static const struct DisplayCalDefaults DisplayCalDefaults_SmartPro = { + DISPLAYCAL_SMARTPRO_DEFAULT_ENABLED, + DISPLAYCAL_SMARTPRO_DEFAULT_RED_GAIN, + DISPLAYCAL_SMARTPRO_DEFAULT_GREEN_GAIN, + DISPLAYCAL_SMARTPRO_DEFAULT_BLUE_GAIN +}; + +enum DisplayCalPreset { + DISPLAYCAL_PRESET_DEFAULT = 0, + DISPLAYCAL_PRESET_BRICK, + DISPLAYCAL_PRESET_SMARTPRO +}; + +static inline DisplayCalDefaults DisplayCal_getDefaultSettings(enum DisplayCalPreset preset) { + if(preset == DISPLAYCAL_PRESET_SMARTPRO) { + return DisplayCalDefaults_SmartPro; + } + if(preset == DISPLAYCAL_PRESET_BRICK) { + return DisplayCalDefaults_Brick; + } + // Default preset + DisplayCalDefaults defaults = { + DISPLAYCAL_DEFAULT_ENABLED, + DISPLAYCAL_DEFAULT_RED_GAIN, + DISPLAYCAL_DEFAULT_GREEN_GAIN, + DISPLAYCAL_DEFAULT_BLUE_GAIN, + }; + return defaults; +} + +// Clamp a display calibration gain value to the supported 0-200 range. +static inline int DisplayCal_clampGainValue(int value) { + if (value < DISPLAYCAL_GAIN_MIN) + return DISPLAYCAL_GAIN_MIN; + if (value > DISPLAYCAL_GAIN_MAX) + return DISPLAYCAL_GAIN_MAX; + return value; +} + +// Apply the LUT using integer red, green, and blue gains in the 0-200 range. +// A value of 100 is neutral. +int DisplayCal_enableWithValues(int red_gain, int green_gain, int blue_gain); + +// Load the identity LUT, then disable gamma correction. +int DisplayCal_disable(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/workspace/all/settings/settings.cpp b/workspace/all/settings/settings.cpp index 53fc6b7a6..f40153e1c 100644 --- a/workspace/all/settings/settings.cpp +++ b/workspace/all/settings/settings.cpp @@ -5,11 +5,13 @@ extern "C" #include "defines.h" #include "api.h" #include "utils.h" +#include "displaycal.h" #include "ra_auth.h" #include "ra_sync.h" } #include +#include #include #include #include @@ -286,6 +288,10 @@ namespace { return m_platform == tg5040; } + bool hasDisplayCal() const { + return m_platform == tg5040; + } + bool hasActiveCooling() const { return m_platform == tg5050; } @@ -502,6 +508,32 @@ int main(int argc, char *argv[]) { SetExposure(std::any_cast(value)); }, []() { SetExposure(SETTINGS_DEFAULT_EXPOSURE);}}); } + if(deviceInfo.hasDisplayCal()) + { + const DisplayCalDefaults defaultDisplayCal = DisplayCal_getDefaultSettings( + deviceInfo.getModel() == DeviceInfo::Brick ? DISPLAYCAL_PRESET_BRICK : + deviceInfo.getModel() == DeviceInfo::SmartPro ? DISPLAYCAL_PRESET_SMARTPRO : DISPLAYCAL_PRESET_DEFAULT); + displayItems.push_back( + new MenuItem{ListItemType::Generic, "White point correction", "Corrects the display white point to better match the \nsRGB standard, at the expense of some peak brightness.", {false, true}, on_off, []() -> std::any + { return GetDisplayCalEnabled() != 0; }, [](const std::any &value) + { SetDisplayCalEnabled(std::any_cast(value)); }, + [defaultDisplayCal]() { SetDisplayCalEnabled(defaultDisplayCal.enabled); }}); + displayItems.push_back( + new MenuItem{ListItemType::Generic, "Red gain", "White point correction red channel gain (0 to 200)", DISPLAYCAL_GAIN_MIN, DISPLAYCAL_GAIN_MAX, "", []() -> std::any + { return GetDisplayCalRedGain(); }, [](const std::any &value) + { SetDisplayCalRedGain(std::any_cast(value)); }, + [defaultDisplayCal]() { SetDisplayCalRedGain(defaultDisplayCal.red_gain); }}); + displayItems.push_back( + new MenuItem{ListItemType::Generic, "Green gain", "White point correction green channel gain (0 to 200)", DISPLAYCAL_GAIN_MIN, DISPLAYCAL_GAIN_MAX, "", []() -> std::any + { return GetDisplayCalGreenGain(); }, [](const std::any &value) + { SetDisplayCalGreenGain(std::any_cast(value)); }, + [defaultDisplayCal]() { SetDisplayCalGreenGain(defaultDisplayCal.green_gain); }}); + displayItems.push_back( + new MenuItem{ListItemType::Generic, "Blue gain", "White point correction blue channel gain (0 to 200)", DISPLAYCAL_GAIN_MIN, DISPLAYCAL_GAIN_MAX, "", []() -> std::any + { return GetDisplayCalBlueGain(); }, [](const std::any &value) + { SetDisplayCalBlueGain(std::any_cast(value)); }, + [defaultDisplayCal]() { SetDisplayCalBlueGain(defaultDisplayCal.blue_gain); }}); + } displayItems.push_back( new MenuItem{ListItemType::Button, "Reset to defaults", "Resets all options in this menu to their default values.", ResetCurrentMenu}); diff --git a/workspace/desktop/libmsettings/makefile b/workspace/desktop/libmsettings/makefile index fb0eacfd6..f525df7f9 100644 --- a/workspace/desktop/libmsettings/makefile +++ b/workspace/desktop/libmsettings/makefile @@ -11,13 +11,13 @@ TARGET=msettings .PHONY: clean CC = $(CROSS_COMPILE)gcc +CFLAGS = -I../../all/common ifeq ($(PROFILE), 1) CFLAGS += -pg LDFLAGS += -pg endif -#CFLAGS = -g #LDFLAGS = -ldl -lrt -s LDFLAGS = -ldl @@ -35,4 +35,4 @@ clean: rm -f *.o rm -f "lib$(TARGET).so" rm -f $(PREFIX_LOCAL)/include/$(TARGET).h - rm -f $(PREFIX_LOCAL)/lib/lib$(TARGET).so \ No newline at end of file + rm -f $(PREFIX_LOCAL)/lib/lib$(TARGET).so diff --git a/workspace/desktop/libmsettings/msettings.c b/workspace/desktop/libmsettings/msettings.c index ffad64125..d327a34d1 100644 --- a/workspace/desktop/libmsettings/msettings.c +++ b/workspace/desktop/libmsettings/msettings.c @@ -1,4 +1,5 @@ #include "msettings.h" +#include "displaycal.h" // desktop #include @@ -137,10 +138,45 @@ typedef struct SettingsV9 { int jack; } SettingsV9; +typedef struct SettingsV10 { + int version; // future proofing + int brightness; + int colortemperature; + int headphones; + int speaker; + int mute; + int contrast; + int saturation; + int exposure; + int toggled_brightness; + int toggled_colortemperature; + int toggled_contrast; + int toggled_saturation; + int toggled_exposure; + int toggled_volume; + int disable_dpad_on_mute; + int emulate_joystick_on_mute; + int turbo_a; + int turbo_b; + int turbo_x; + int turbo_y; + int turbo_l1; + int turbo_l2; + int turbo_r1; + int turbo_r2; + int unused[2]; // for future use + // NOTE: doesn't really need to be persisted but still needs to be shared + int jack; + int displaycal_enabled; + int displaycal_red_gain; + int displaycal_green_gain; + int displaycal_blue_gain; +} SettingsV10; + // When incrementing SETTINGS_VERSION, update the Settings typedef and add // backwards compatibility to InitSettings! -#define SETTINGS_VERSION 9 -typedef SettingsV9 Settings; +#define SETTINGS_VERSION 10 +typedef SettingsV10 Settings; static Settings DefaultSettings = { .version = SETTINGS_VERSION, .brightness = SETTINGS_DEFAULT_BRIGHTNESS, @@ -168,11 +204,17 @@ static Settings DefaultSettings = { .turbo_r1 = 0, .turbo_r2 = 0, .jack = 0, + .displaycal_enabled = DISPLAYCAL_DEFAULT_ENABLED, + .displaycal_red_gain = DISPLAYCAL_DEFAULT_RED_GAIN, + .displaycal_green_gain = DISPLAYCAL_DEFAULT_GREEN_GAIN, + .displaycal_blue_gain = DISPLAYCAL_DEFAULT_BLUE_GAIN, }; static Settings* msettings; static char SettingsPath[256]; +static inline void applyDisplayCalSettings(void); + /////////////////////////////////////// int peekVersion(const char *filename) { @@ -204,7 +246,15 @@ void InitSettings(void){ memcpy(msettings, &DefaultSettings, sizeof(Settings)); // overwrite with migrated data - if(version==8) { + if(version==9) { + printf("Found settings v9.\n"); + SettingsV9 old; + read(fd, &old, sizeof(SettingsV9)); + + memcpy(msettings, &old, sizeof(SettingsV9)); + msettings->version = SETTINGS_VERSION; + } + else if(version==8) { printf("Found settings v8.\n"); SettingsV8 old; read(fd, &old, sizeof(SettingsV8)); @@ -325,6 +375,8 @@ void InitSettings(void){ // load defaults memcpy(msettings, &DefaultSettings, sizeof(Settings)); } + + applyDisplayCalSettings(); } static inline void SaveSettings(void) { FILE *file = fopen(SettingsPath, "w"); @@ -333,6 +385,10 @@ static inline void SaveSettings(void) { fclose(file); } } +static inline void applyDisplayCalSettings(void) { + if (msettings->displaycal_enabled) + SetRawDisplayCal(1, msettings->displaycal_red_gain, msettings->displaycal_green_gain, msettings->displaycal_blue_gain); +} void QuitSettings(void){ SaveSettings(); // dealloc settings @@ -350,6 +406,10 @@ int GetColortemp(void) { return 0; } int GetContrast(void) { return 0; } int GetSaturation(void) { return 0; } int GetExposure(void) { return 0; } +int GetDisplayCalEnabled(void) { return msettings->displaycal_enabled; } +int GetDisplayCalRedGain(void) { return msettings->displaycal_red_gain; } +int GetDisplayCalGreenGain(void) { return msettings->displaycal_green_gain; } +int GetDisplayCalBlueGain(void) { return msettings->displaycal_blue_gain; } int GetVolume(void) { return 0; } int GetMutedBrightness(void) { return 0; } @@ -387,6 +447,9 @@ void SetMuteTurboR1(int value) {} void SetMuteTurboR2(int value) {} void SetRawBrightness(int value) {} +void SetRawDisplayCal(int enabled, int red_gain, int green_gain, int blue_gain) { + printf("SetRawDisplayCal(%i,%i,%i,%i)\n", enabled, red_gain, green_gain, blue_gain); fflush(stdout); +} void SetRawVolume(int value){} void SetBrightness(int value) {} @@ -394,6 +457,36 @@ void SetColortemp(int value) {} void SetContrast(int value) {} void SetSaturation(int value) {} void SetExposure(int value) {} +void SetDisplayCalEnabled(int is_enabled) { + int was_enabled = msettings->displaycal_enabled; + is_enabled = (is_enabled != 0); + msettings->displaycal_enabled = is_enabled; + + // Disabling only needs raw writes when we are turning an active LUT off. + if (is_enabled) + applyDisplayCalSettings(); + else if (was_enabled) + SetRawDisplayCal(0, msettings->displaycal_red_gain, msettings->displaycal_green_gain, msettings->displaycal_blue_gain); + SaveSettings(); +} +void SetDisplayCalRedGain(int value) { + value = DisplayCal_clampGainValue(value); + msettings->displaycal_red_gain = value; + applyDisplayCalSettings(); + SaveSettings(); +} +void SetDisplayCalGreenGain(int value) { + value = DisplayCal_clampGainValue(value); + msettings->displaycal_green_gain = value; + applyDisplayCalSettings(); + SaveSettings(); +} +void SetDisplayCalBlueGain(int value) { + value = DisplayCal_clampGainValue(value); + msettings->displaycal_blue_gain = value; + applyDisplayCalSettings(); + SaveSettings(); +} void SetVolume(int value) {} int GetJack(void) { return 0; } diff --git a/workspace/desktop/libmsettings/msettings.h b/workspace/desktop/libmsettings/msettings.h index c01d81b9f..0b302d08f 100644 --- a/workspace/desktop/libmsettings/msettings.h +++ b/workspace/desktop/libmsettings/msettings.h @@ -21,9 +21,14 @@ int GetColortemp(void); int GetContrast(void); int GetSaturation(void); int GetExposure(void); +int GetDisplayCalEnabled(void); +int GetDisplayCalRedGain(void); +int GetDisplayCalGreenGain(void); +int GetDisplayCalBlueGain(void); int GetVolume(void); void SetRawBrightness(int value); // 0-255 +void SetRawDisplayCal(int enabled, int red_gain, int green_gain, int blue_gain); void SetRawVolume(int value); // 0-160 void SetBrightness(int value); // 0-10 @@ -31,6 +36,10 @@ void SetColortemp(int value); // 0-40 void SetContrast(int value); // -4-5 void SetSaturation(int value); // -5-5 void SetExposure(int value); // -4-5 +void SetDisplayCalEnabled(int value); // 0-1 +void SetDisplayCalRedGain(int value); // 0-200, 100 is neutral +void SetDisplayCalGreenGain(int value); // 0-200, 100 is neutral +void SetDisplayCalBlueGain(int value); // 0-200, 100 is neutral void SetVolume(int value); // 0-20 int GetJack(void); diff --git a/workspace/tg5040/libmsettings/makefile b/workspace/tg5040/libmsettings/makefile index 06b41044e..e666406ff 100644 --- a/workspace/tg5040/libmsettings/makefile +++ b/workspace/tg5040/libmsettings/makefile @@ -12,14 +12,15 @@ TARGET=msettings CC = $(CROSS_COMPILE)gcc -CFLAGS = -LDFLAGS = -ltinyalsa -ldl -lrt -s +CFLAGS = -I../../all/common +LDFLAGS = -ltinyalsa -ldl -lrt -lm -s OPTM=-Ofast build: $(CC) -c -Werror -fpic "$(TARGET).c" $(CFLAGS) -Wl,--no-as-needed $(LDFLAGS) - $(CC) -shared -o "lib$(TARGET).so" "$(TARGET).o" $(LDFLAGS) + $(CC) -c -Werror -fpic "../../all/common/displaycal.c" $(CFLAGS) -Wl,--no-as-needed $(LDFLAGS) + $(CC) -shared -o "lib$(TARGET).so" "$(TARGET).o" "displaycal.o" $(LDFLAGS) mkdir -p "$(PREFIX_LOCAL)/include" mkdir -p "$(PREFIX_LOCAL)/lib" cp "$(TARGET).h" "$(PREFIX_LOCAL)/include" @@ -29,4 +30,4 @@ clean: rm -f *.o rm -f "lib$(TARGET).so" rm -f $(PREFIX_LOCAL)/include/$(TARGET).h - rm -f $(PREFIX_LOCAL)/lib/lib$(TARGET).so \ No newline at end of file + rm -f $(PREFIX_LOCAL)/lib/lib$(TARGET).so diff --git a/workspace/tg5040/libmsettings/msettings.c b/workspace/tg5040/libmsettings/msettings.c index 8671ba41b..6ec93eefd 100644 --- a/workspace/tg5040/libmsettings/msettings.c +++ b/workspace/tg5040/libmsettings/msettings.c @@ -12,6 +12,7 @@ #include #include +#include "displaycal.h" #include "msettings.h" /////////////////////////////////////// @@ -172,10 +173,46 @@ typedef struct SettingsV10 { int audiosink; // was bluetooth true/false before } SettingsV10; +typedef struct SettingsV11 { + int version; // future proofing + int brightness; + int colortemperature; + int headphones; + int speaker; + int mute; + int contrast; + int saturation; + int exposure; + int toggled_brightness; + int toggled_colortemperature; + int toggled_contrast; + int toggled_saturation; + int toggled_exposure; + int toggled_volume; + int disable_dpad_on_mute; + int emulate_joystick_on_mute; + int turbo_a; + int turbo_b; + int turbo_x; + int turbo_y; + int turbo_l1; + int turbo_l2; + int turbo_r1; + int turbo_r2; + int unused[2]; // for future use + // NOTE: doesn't really need to be persisted but still needs to be shared + int jack; + int audiosink; // was bluetooth true/false before + int displaycal_enabled; + int displaycal_red_gain; + int displaycal_green_gain; + int displaycal_blue_gain; +} SettingsV11; + // When incrementing SETTINGS_VERSION, update the Settings typedef and add // backwards compatibility to InitSettings! -#define SETTINGS_VERSION 10 -typedef SettingsV10 Settings; +#define SETTINGS_VERSION 11 +typedef SettingsV11 Settings; static Settings DefaultSettings = { .version = SETTINGS_VERSION, .brightness = SETTINGS_DEFAULT_BRIGHTNESS, @@ -204,6 +241,10 @@ static Settings DefaultSettings = { .turbo_r2 = 0, .jack = 0, .audiosink = AUDIO_SINK_DEFAULT, + .displaycal_enabled = DISPLAYCAL_DEFAULT_ENABLED, + .displaycal_red_gain = DISPLAYCAL_DEFAULT_RED_GAIN, + .displaycal_green_gain = DISPLAYCAL_DEFAULT_GREEN_GAIN, + .displaycal_blue_gain = DISPLAYCAL_DEFAULT_BLUE_GAIN, }; static Settings* settings; @@ -274,10 +315,23 @@ int peekVersion(const char *filename) { } static int is_brick = 0; +static int is_smartpro = 0; + +static void applyDisplayCalDefaultsForDevice(Settings *target) { + DisplayCalDefaults defaults = DisplayCal_getDefaultSettings( + is_brick ? DISPLAYCAL_PRESET_BRICK + : is_smartpro ? DISPLAYCAL_PRESET_SMARTPRO + : DISPLAYCAL_PRESET_DEFAULT); + target->displaycal_enabled = defaults.enabled; + target->displaycal_red_gain = defaults.red_gain; + target->displaycal_green_gain = defaults.green_gain; + target->displaycal_blue_gain = defaults.blue_gain; +} void InitSettings(void) { char* device = getenv("DEVICE"); is_brick = exactMatch("brick", device); + is_smartpro = exactMatch("smartpro", device); sprintf(SettingsPath, "%s/msettings.bin", getenv("USERDATA_PATH")); @@ -307,7 +361,15 @@ void InitSettings(void) { memcpy(settings, &DefaultSettings, shm_size); // overwrite with migrated data - if(version==9) { + if(version==10) { + printf("Found settings v10.\n"); + SettingsV10 old; + read(fd, &old, sizeof(SettingsV10)); + + memcpy(settings, &old, sizeof(SettingsV10)); + settings->version = SETTINGS_VERSION; + } + else if(version==9) { printf("Found settings v9.\n"); SettingsV9 old; read(fd, &old, sizeof(SettingsV9)); @@ -478,6 +540,8 @@ void InitSettings(void) { system("amixer sset 'DAC Swap' Off"); // Fix L/R channels } + applyDisplayCalDefaultsForDevice(settings); + // This will implicitly update all other settings based on FN switch state SetMute(settings->mute); } @@ -497,6 +561,11 @@ static inline void SaveSettings(void) { } } +static inline void applyDisplayCalSettings(void) { + if (settings->displaycal_enabled) + SetRawDisplayCal(1, settings->displaycal_red_gain, settings->displaycal_green_gain, settings->displaycal_blue_gain); +} + ///////// Getters exposed in public API int GetBrightness(void) { // 0-10 @@ -541,6 +610,22 @@ int GetExposure(void) return settings->exposure; } +int GetDisplayCalEnabled(void) +{ + return settings->displaycal_enabled; +} +int GetDisplayCalRedGain(void) +{ + return settings->displaycal_red_gain; +} +int GetDisplayCalGreenGain(void) +{ + return settings->displaycal_green_gain; +} +int GetDisplayCalBlueGain(void) +{ + return settings->displaycal_blue_gain; +} // monitored and set by thread in keymon int GetJack(void) { return settings->jack; @@ -664,6 +749,36 @@ void SetExposure(int value){ settings->exposure = value; SaveSettings(); } +void SetDisplayCalEnabled(int is_enabled) { + int was_enabled = settings->displaycal_enabled; + is_enabled = (is_enabled != 0); + settings->displaycal_enabled = is_enabled; + + // Disabling only needs hardware writes when we are turning an active LUT off. + if (is_enabled) + applyDisplayCalSettings(); + else if (was_enabled) + SetRawDisplayCal(0, settings->displaycal_red_gain, settings->displaycal_green_gain, settings->displaycal_blue_gain); + SaveSettings(); +} +void SetDisplayCalRedGain(int value) { + value = DisplayCal_clampGainValue(value); + settings->displaycal_red_gain = value; + applyDisplayCalSettings(); + SaveSettings(); +} +void SetDisplayCalGreenGain(int value) { + value = DisplayCal_clampGainValue(value); + settings->displaycal_green_gain = value; + applyDisplayCalSettings(); + SaveSettings(); +} +void SetDisplayCalBlueGain(int value) { + value = DisplayCal_clampGainValue(value); + settings->displaycal_blue_gain = value; + applyDisplayCalSettings(); + SaveSettings(); +} void SetVolume(int value) // 0-20 { if (settings->mute && GetMutedVolume() != SETTINGS_DEFAULT_MUTE_NO_CHANGE) @@ -702,6 +817,7 @@ void SetMute(int value) { SetContrast(GetContrast()); SetSaturation(GetSaturation()); SetExposure(GetExposure()); + applyDisplayCalSettings(); if(is_brick && GetMuteDisablesDpad()) disableDpad(settings->mute); @@ -1283,3 +1399,15 @@ void SetRawExposure(int val){ fclose(fd); } } +void SetRawDisplayCal(int enabled, int red_gain, int green_gain, int blue_gain) { + printf("SetRawDisplayCal(%i,%i,%i,%i)\n", enabled, red_gain, green_gain, blue_gain); fflush(stdout); + + int ret = enabled + ? DisplayCal_enableWithValues(red_gain, green_gain, blue_gain) + : DisplayCal_disable(); + if (ret != 0) { + fprintf(stderr, "SetRawDisplayCal failed to %s display calibration: %i\n", + enabled ? "enable" : "disable", ret); + fflush(stderr); + } +} diff --git a/workspace/tg5040/libmsettings/msettings.h b/workspace/tg5040/libmsettings/msettings.h index 5a03981e3..db46458bf 100644 --- a/workspace/tg5040/libmsettings/msettings.h +++ b/workspace/tg5040/libmsettings/msettings.h @@ -21,6 +21,10 @@ int GetColortemp(void); int GetContrast(void); int GetSaturation(void); int GetExposure(void); +int GetDisplayCalEnabled(void); +int GetDisplayCalRedGain(void); +int GetDisplayCalGreenGain(void); +int GetDisplayCalBlueGain(void); int GetVolume(void); void SetRawBrightness(int value); // 0-255 @@ -28,6 +32,7 @@ void SetRawColortemp(int value); // 0-255 void SetRawContrast(int value); // 0-100 void SetRawSaturation(int value); // 0-100 void SetRawExposure(int value); // 0-100 +void SetRawDisplayCal(int enabled, int red_gain, int green_gain, int blue_gain); void SetRawVolume(int value); // 0-100 void SetBrightness(int value); // 0-10 @@ -35,6 +40,10 @@ void SetColortemp(int value); // 0-40 void SetContrast(int value); // -4-5 void SetSaturation(int value); // -5-5 void SetExposure(int value); // -4-5 +void SetDisplayCalEnabled(int value); // 0-1 +void SetDisplayCalRedGain(int value); // 0-200, 100 is neutral +void SetDisplayCalGreenGain(int value); // 0-200, 100 is neutral +void SetDisplayCalBlueGain(int value); // 0-200, 100 is neutral void SetVolume(int value); // 0-20 int GetJack(void); diff --git a/workspace/tg5050/libmsettings/makefile b/workspace/tg5050/libmsettings/makefile index cc516d79b..804df7d52 100644 --- a/workspace/tg5050/libmsettings/makefile +++ b/workspace/tg5050/libmsettings/makefile @@ -12,8 +12,8 @@ TARGET=msettings CC = $(CROSS_COMPILE)gcc -CFLAGS = -LDFLAGS = -ltinyalsa -ldl -lrt -s +CFLAGS = -I../../all/common +LDFLAGS = -ltinyalsa -ldl -lrt -lm -s #LDFLAGS = -ldl -lrt -s OPTM=-Ofast @@ -30,4 +30,4 @@ clean: rm -f *.o rm -f "lib$(TARGET).so" rm -f $(PREFIX_LOCAL)/include/$(TARGET).h - rm -f $(PREFIX_LOCAL)/lib/lib$(TARGET).so \ No newline at end of file + rm -f $(PREFIX_LOCAL)/lib/lib$(TARGET).so diff --git a/workspace/tg5050/libmsettings/msettings.c b/workspace/tg5050/libmsettings/msettings.c index a214ffe95..a8ddea691 100644 --- a/workspace/tg5050/libmsettings/msettings.c +++ b/workspace/tg5050/libmsettings/msettings.c @@ -13,6 +13,7 @@ #include #include "msettings.h" +#include "displaycal.h" /////////////////////////////////////// @@ -47,10 +48,45 @@ typedef struct SettingsV1 { int fanSpeed; // 0-100, -1 for auto } SettingsV1; +typedef struct SettingsV2 { + int version; // future proofing + int brightness; + int colortemperature; + int headphones; + int speaker; + int mute; + int contrast; + int saturation; + int exposure; + int toggled_brightness; + int toggled_colortemperature; + int toggled_contrast; + int toggled_saturation; + int toggled_exposure; + int toggled_volume; + int turbo_a; + int turbo_b; + int turbo_x; + int turbo_y; + int turbo_l1; + int turbo_l2; + int turbo_r1; + int turbo_r2; + int unused[2]; // for future use + // NOTE: doesn't really need to be persisted but still needs to be shared + int jack; + int audiosink; // was bluetooth true/false before + int fanSpeed; // 0-100, -1 for auto + int displaycal_enabled; + int displaycal_red_gain; + int displaycal_green_gain; + int displaycal_blue_gain; +} SettingsV2; + // When incrementing SETTINGS_VERSION, update the Settings typedef and add // backwards compatibility to InitSettings! -#define SETTINGS_VERSION 1 -typedef SettingsV1 Settings; +#define SETTINGS_VERSION 2 +typedef SettingsV2 Settings; static Settings DefaultSettings = { .version = SETTINGS_VERSION, .brightness = SETTINGS_DEFAULT_BRIGHTNESS, @@ -78,6 +114,10 @@ static Settings DefaultSettings = { .jack = 0, .audiosink = AUDIO_SINK_DEFAULT, .fanSpeed = SETTINGS_DEFAULT_FAN_SPEED, + .displaycal_enabled = DISPLAYCAL_DEFAULT_ENABLED, + .displaycal_red_gain = DISPLAYCAL_DEFAULT_RED_GAIN, + .displaycal_green_gain = DISPLAYCAL_DEFAULT_GREEN_GAIN, + .displaycal_blue_gain = DISPLAYCAL_DEFAULT_BLUE_GAIN, }; static Settings* settings; @@ -177,8 +217,12 @@ void InitSettings(void) { memcpy(settings, &DefaultSettings, shm_size); // overwrite with migrated data - if(version == 42) { - // do migration (TODO when needed) + if(version == 1) { + SettingsV1 old; + read(fd, &old, sizeof(SettingsV1)); + + memcpy(settings, &old, sizeof(SettingsV1)); + settings->version = SETTINGS_VERSION; } else { printf("Found unsupported settings version: %i.\n", version); @@ -216,6 +260,7 @@ void InitSettings(void) { } // This will implicitly update all other settings based on FN switch state + settings->displaycal_enabled = 0; SetMute(settings->mute); SetFanSpeed(settings->fanSpeed); @@ -236,6 +281,10 @@ static inline void SaveSettings(void) { } } +static inline void applyDisplayCalSettings(void) { + // tg5050 does not expose /dev/disp, so display calibration is unsupported. +} + ///////// Getters exposed in public API int GetBrightness(void) { // 0-10 @@ -280,6 +329,22 @@ int GetExposure(void) return settings->exposure; } +int GetDisplayCalEnabled(void) +{ + return settings->displaycal_enabled; +} +int GetDisplayCalRedGain(void) +{ + return settings->displaycal_red_gain; +} +int GetDisplayCalGreenGain(void) +{ + return settings->displaycal_green_gain; +} +int GetDisplayCalBlueGain(void) +{ + return settings->displaycal_blue_gain; +} // monitored and set by thread in keymon int GetJack(void) { return settings->jack; @@ -406,6 +471,29 @@ void SetExposure(int value){ settings->exposure = value; SaveSettings(); } +void SetDisplayCalEnabled(int is_enabled) { + (void)is_enabled; + settings->displaycal_enabled = 0; + SaveSettings(); +} +void SetDisplayCalRedGain(int value) { + value = DisplayCal_clampGainValue(value); + settings->displaycal_red_gain = value; + applyDisplayCalSettings(); + SaveSettings(); +} +void SetDisplayCalGreenGain(int value) { + value = DisplayCal_clampGainValue(value); + settings->displaycal_green_gain = value; + applyDisplayCalSettings(); + SaveSettings(); +} +void SetDisplayCalBlueGain(int value) { + value = DisplayCal_clampGainValue(value); + settings->displaycal_blue_gain = value; + applyDisplayCalSettings(); + SaveSettings(); +} void SetVolume(int value) { // 0-20 if (settings->mute && GetMutedVolume() != SETTINGS_DEFAULT_MUTE_NO_CHANGE) return SetRawVolume(scaleVolume(GetMutedVolume())); @@ -443,6 +531,7 @@ void SetMute(int value) { SetContrast(GetContrast()); SetSaturation(GetSaturation()); SetExposure(GetExposure()); + applyDisplayCalSettings(); if(GetMuteTurboA()) turboA(settings->mute); @@ -986,6 +1075,14 @@ void SetRawExposure(int val){ } } +void SetRawDisplayCal(int enabled, int red_gain, int green_gain, int blue_gain) { + (void)enabled; + (void)red_gain; + (void)green_gain; + (void)blue_gain; + printf("SetRawDisplayCal unsupported on tg5050\n"); fflush(stdout); +} + void SetRawColortemp(int val) { // 0 - 255 printf("SetRawColortemp(%i)\n", val); fflush(stdout); @@ -1028,4 +1125,4 @@ void SetRawFanSpeed(int val) { // 0-31, -1/-2-3 for auto low/med/high system(FAN_SPEED_CONTROL " &"); return; } -} \ No newline at end of file +} diff --git a/workspace/tg5050/libmsettings/msettings.h b/workspace/tg5050/libmsettings/msettings.h index ddb43d296..ae756b649 100644 --- a/workspace/tg5050/libmsettings/msettings.h +++ b/workspace/tg5050/libmsettings/msettings.h @@ -21,6 +21,10 @@ int GetColortemp(void); int GetContrast(void); int GetSaturation(void); int GetExposure(void); +int GetDisplayCalEnabled(void); +int GetDisplayCalRedGain(void); +int GetDisplayCalGreenGain(void); +int GetDisplayCalBlueGain(void); int GetVolume(void); int GetFanSpeed(void); @@ -29,6 +33,7 @@ void SetRawColortemp(int value); // 0-255 void SetRawContrast(int value); // 0-100 void SetRawSaturation(int value); // 0-100 void SetRawExposure(int value); // 0-100 +void SetRawDisplayCal(int enabled, int red_gain, int green_gain, int blue_gain); void SetRawVolume(int value); // 0-100 void SetRawFanSpeed(int value); // 0-31, -1/-2-3 for auto low/med/high @@ -37,6 +42,10 @@ void SetColortemp(int value); // 0-40 void SetContrast(int value); // -4-5 void SetSaturation(int value); // -5-5 void SetExposure(int value); // -4-5 +void SetDisplayCalEnabled(int value); // 0-1 +void SetDisplayCalRedGain(int value); // 0-200, 100 is neutral +void SetDisplayCalGreenGain(int value); // 0-200, 100 is neutral +void SetDisplayCalBlueGain(int value); // 0-200, 100 is neutral void SetVolume(int value); // 0-20 void SetFanSpeed(int value); // 0-100, -1 for auto