From c366de98e7379766c1b06d56c0dc3c0d00263284 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20=C4=8Cerm=C3=A1k?= Date: Tue, 23 Jul 2024 19:37:15 +0000 Subject: [PATCH] Support for Kingston Fury DDR4/5 DIMMs --- .../KingstonFuryDRAMController.cpp | 347 ++++++++ .../KingstonFuryDRAMController.h | 155 ++++ .../KingstonFuryDRAMControllerDetect.cpp | 226 ++++++ .../RGBController_KingstonFuryDRAM.cpp | 746 ++++++++++++++++++ .../RGBController_KingstonFuryDRAM.h | 36 + 5 files changed, 1510 insertions(+) create mode 100644 Controllers/KingstonFuryDRAMController/KingstonFuryDRAMController.cpp create mode 100644 Controllers/KingstonFuryDRAMController/KingstonFuryDRAMController.h create mode 100644 Controllers/KingstonFuryDRAMController/KingstonFuryDRAMControllerDetect.cpp create mode 100644 Controllers/KingstonFuryDRAMController/RGBController_KingstonFuryDRAM.cpp create mode 100644 Controllers/KingstonFuryDRAMController/RGBController_KingstonFuryDRAM.h diff --git a/Controllers/KingstonFuryDRAMController/KingstonFuryDRAMController.cpp b/Controllers/KingstonFuryDRAMController/KingstonFuryDRAMController.cpp new file mode 100644 index 00000000..9ead59e8 --- /dev/null +++ b/Controllers/KingstonFuryDRAMController/KingstonFuryDRAMController.cpp @@ -0,0 +1,347 @@ +/*---------------------------------------------------------*\ +| KingstonFuryDRAMController.cpp | +| | +| Driver for Kingston Fury DDR4/5 RAM modules | +| | +| Geofrey Mon (geofbot) 14 Jul 2024 | +| Milan Cermak (krysmanta) | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-only | +\*---------------------------------------------------------*/ + +#include +#include +#include "KingstonFuryDRAMController.h" +#include "RGBController.h" +#include "LogManager.h" + +KingstonFuryDRAMController::KingstonFuryDRAMController( + i2c_smbus_interface* bus, unsigned char base_addr, std::vector slots) +{ + this->bus = bus; + this->base_addr = base_addr; + this->slots = slots; + + reg_cache.resize(slots.size()); +} + +std::string KingstonFuryDRAMController::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + return_string.append(", addresses ["); + for(std::size_t idx = 0; idx < slots.size(); idx++) + { + char addr[5]; + snprintf(addr, 5, "0x%02X", base_addr + slots[idx]); + return_string.append(addr); + if(idx < slots.size() - 1) + { + return_string.append(","); + } + else + { + return_string.append("]"); + } + } + return("I2C: " + return_string); +} + +unsigned KingstonFuryDRAMController::GetLEDCount() +{ + return GetLEDPerDIMM() * slots.size(); +} + +unsigned int KingstonFuryDRAMController::GetSlotCount() +{ + return slots.size(); +} + +unsigned char KingstonFuryDRAMController::GetMode() +{ + unsigned char mode = 0; + CachedRead(0, FURY_REG_MODE, &mode); + return mode; +} + +bool KingstonFuryDRAMController::SmbusRead(int slot_idx, unsigned char reg, unsigned char *val) +{ + if(val == NULL) + { + return false; + } + + unsigned char device_addr = base_addr + slots[slot_idx]; + int res; + + for(int retries = 1; retries <= 5; retries++) + { + res = bus->i2c_smbus_read_word_data(device_addr, reg); + if(res >= 0) + { + *val = (res >> 8) & 0xff; + LOG_DEBUG("[%s] %02X reading register &%02X=%02X; res=%02X", + FURY_CONTROLLER_NAME, device_addr, reg, *val, res); + return true; + } + else + { + std::this_thread::sleep_for(3 * retries * FURY_DELAY); + } + } + return false; +} + +bool KingstonFuryDRAMController::SmbusWrite(int slot_idx, unsigned char reg, unsigned char val) +{ + unsigned char device_addr = base_addr + slots[slot_idx]; + int res; + + for(int retries = 1; retries <= 5; retries++) + { + res = bus->i2c_smbus_write_byte_data(device_addr, + reg, val); + LOG_DEBUG("[%s] %02X setting register &%02X=%02X; res=%02X", + FURY_CONTROLLER_NAME, device_addr, reg, val, res); + if(res >= 0) + { + return true; + } + else + { + std::this_thread::sleep_for(3 * retries * FURY_DELAY); + } + } + return false; +} + +// returns whether a read was successful +bool KingstonFuryDRAMController::CachedRead(int slot_idx, unsigned char reg, unsigned char *val) +{ + if(val == NULL) + { + return false; + } + + unsigned char device_addr = base_addr + slots[slot_idx]; + if(reg_cache[slot_idx].find(reg) == reg_cache[slot_idx].end()) + { + if(SmbusRead(slot_idx, reg, val)) + { + reg_cache[slot_idx][reg] = *val; + return true; + } + LOG_ERROR("[%s] %02X failed to get register &%02X", + FURY_CONTROLLER_NAME, device_addr, reg); + return false; + } + else + { + *val = reg_cache[slot_idx][reg]; + return true; + } +} + +// returns whether a write was actually performed +bool KingstonFuryDRAMController::CachedWrite(int slot_idx, unsigned char reg, unsigned char val) +{ + unsigned char device_addr = base_addr + slots[slot_idx]; + if(reg_cache[slot_idx].find(reg) == reg_cache[slot_idx].end() || + reg_cache[slot_idx][reg] != val) + { + if(SmbusWrite(slot_idx, reg, val)) + { + reg_cache[slot_idx][reg] = val; + return true; + } + LOG_ERROR("[%s] %02X failed to set register &%02X=%02X", + FURY_CONTROLLER_NAME, device_addr, reg, val); + return false; + } + else + { + LOG_DEBUG("[%s] %02X register already set &%02X=%02X", + FURY_CONTROLLER_NAME, device_addr, reg, val); + return false; + } +} + +void KingstonFuryDRAMController::SendPreamble(bool synchronize) +{ + SendBegin(); + + for(std::size_t idx = 0; idx < slots.size(); idx++) + { + char written_index = 0; +#ifdef FURY_SYNC + if(!synchronize) + { + // some modes set all indices to 0 so that the + // individual sticks don't sync with each other + written_index = 0; + } + else + { + /*--------------------------------------------------------------*\ + | The index tells physical location of the RAM slot | + | from the border to the CPU slot. On most motherboards, | + | the address relates to the slot location, | + | but there are exceptions. | + | The official software writes the indices in decreasing order. | + | | + | Hardware effects seem to support only up to 4 sticks. | + | So we give the first 4 and last 4 sticks separate numbering. | + \*--------------------------------------------------------------*/ + written_index = idx % 4; + } +#endif + LOG_DEBUG("[%s] %02X writing index %d", + FURY_CONTROLLER_NAME, base_addr + slots[idx], + written_index); + SmbusWrite(idx, FURY_REG_INDEX, written_index); + } + // The RGB controller is a bit slow and requires delay; + // however, we can delay once for all of the sticks instead of + // delaying individually for each stick. + std::this_thread::sleep_for(FURY_DELAY); + + SendApply(); +} + +void KingstonFuryDRAMController::SendBegin() +{ + for(std::size_t idx = 0; idx < slots.size(); idx++) + { + LOG_DEBUG("[%s] %02X beginning transaction", + FURY_CONTROLLER_NAME, base_addr + slots[idx]); + SmbusWrite(idx, FURY_REG_APPLY, FURY_BEGIN_TRNSFER); + } + std::this_thread::sleep_for(FURY_DELAY); +} + +void KingstonFuryDRAMController::SendApply() +{ + for(std::size_t idx = 0; idx < slots.size(); idx++) + { + LOG_DEBUG("[%s] %02X ending transaction", + FURY_CONTROLLER_NAME, base_addr + slots[idx]); + SmbusWrite(idx, FURY_REG_APPLY, FURY_END_TRNSFER); + } + std::this_thread::sleep_for(FURY_DELAY); +} + +void KingstonFuryDRAMController::SetMode(unsigned char val) +{ + SetRegister(FURY_REG_MODE, val); +} + +void KingstonFuryDRAMController::SetNumSlots() +{ + if(slots.size() <= 4) + { + SetRegister(FURY_REG_NUM_SLOTS, slots.size()); + } + else + { + // hardware effects seem to only support at most 4 slots; + // if there are >= 4 slots, then essentially the first 4 slots + // run their effects independent of the last 4 slots + SetRegister(FURY_REG_NUM_SLOTS, 4); + } +} + +void KingstonFuryDRAMController::SetRegister(int reg, unsigned char val) +{ + bool write_occurred = false; + for(std::size_t idx = 0; idx < slots.size(); idx++) + { + write_occurred = CachedWrite(idx, reg, val) || write_occurred; + } + if(write_occurred) + { + std::this_thread::sleep_for(FURY_DELAY); + } +} + +void KingstonFuryDRAMController::SetRegister(int reg, std::vector vals) +{ + bool write_occurred = false; + if(vals.size() < slots.size()) + { + LOG_ERROR("[%s] vector of values has wrong size when setting register &%02X", + FURY_CONTROLLER_NAME, reg); + return; + } + for(std::size_t idx = 0; idx < slots.size(); idx++) + { + write_occurred = CachedWrite(idx, reg, vals[idx]) || write_occurred; + } + if(write_occurred) + { + std::this_thread::sleep_for(FURY_DELAY); + } +} + +void KingstonFuryDRAMController::SetModeColors(std::vector colors) +{ + if(colors.empty() || colors.size() > FURY_MAX_MODE_COLORS) + { + return; + } + SetRegister(FURY_REG_NUM_COLORS, colors.size()); + for(std::size_t idx = 0; idx < colors.size(); idx++) + { + RGBColor color = colors[idx]; + unsigned char red = RGBGetRValue(color); + unsigned char green = RGBGetGValue(color); + unsigned char blue = RGBGetBValue(color); + + int red_idx = FURY_REG_MODE_BASE_RED + idx * 3; + int green_idx = FURY_REG_MODE_BASE_GREEN + idx * 3; + int blue_idx = FURY_REG_MODE_BASE_BLUE + idx * 3; + SetRegister(red_idx, red); + SetRegister(green_idx, green); + SetRegister(blue_idx, blue); + } +} + +void KingstonFuryDRAMController::SetLEDColors(std::vector colors) +{ + if(colors.size() != GetLEDCount()) + { + return; + } + + unsigned int led_per_dimm = GetLEDPerDIMM(); + for(unsigned int led_idx = 0; led_idx < led_per_dimm; led_idx++) + { + int red_register = FURY_REG_BASE_RED + 3 * led_idx; + int green_register = FURY_REG_BASE_GREEN + 3 * led_idx; + int blue_register = FURY_REG_BASE_BLUE + 3 * led_idx; + + std::vector reds, greens, blues; + for(std::size_t slot_idx = 0; slot_idx < GetSlotCount(); slot_idx++) + { + RGBColor color = colors[slot_idx * led_per_dimm + led_idx]; + unsigned char red = RGBGetRValue(color); + unsigned char green = RGBGetGValue(color); + unsigned char blue = RGBGetBValue(color); + reds.push_back(red); + greens.push_back(green); + blues.push_back(blue); + } + + SetRegister(red_register, reds); + SetRegister(blue_register, blues); + SetRegister(green_register, greens); + } +} + +unsigned int KingstonFuryDRAMController::GetLEDPerDIMM() +{ + if(base_addr == FURY_BASE_ADDR_DDR4) + { + return FURY_LEDS_PER_DIMM_DDR4; + } + return FURY_LEDS_PER_DIMM_DDR5; +} diff --git a/Controllers/KingstonFuryDRAMController/KingstonFuryDRAMController.h b/Controllers/KingstonFuryDRAMController/KingstonFuryDRAMController.h new file mode 100644 index 00000000..5e63a389 --- /dev/null +++ b/Controllers/KingstonFuryDRAMController/KingstonFuryDRAMController.h @@ -0,0 +1,155 @@ +/*---------------------------------------------------------*\ +| KingstonFuryDRAMController.h | +| | +| Driver for Kingston Fury DDR4/5 RAM modules | +| | +| Geofrey Mon (geofbot) 14 Jul 2024 | +| Milan Cermak (krysmanta) | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-only | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "i2c_smbus.h" +#include "RGBController.h" + +#define FURY_CONTROLLER_NAME "Kingston Fury DDR4/5 DRAM" +#define FURY_BASE_ADDR_DDR4 0x58 +#define FURY_BASE_ADDR_DDR5 0x60 +#define FURY_DELAY std::chrono::milliseconds(10) +#define FURY_MAX_SLOTS 8 +#define FURY_LEDS_PER_DIMM_DDR4 10 +#define FURY_LEDS_PER_DIMM_DDR5 12 +#define FURY_MAX_MODE_COLORS 10 +#define FURY_DEFAULT_BG_COLOR ToRGBColor(16,16,16) +#define FURY_ALT_DIRECTIONS {\ + FURY_DIR_BOTTOM_TO_TOP,\ + FURY_DIR_TOP_TO_BOTTOM,\ + FURY_DIR_BOTTOM_TO_TOP,\ + FURY_DIR_TOP_TO_BOTTOM,\ + FURY_DIR_BOTTOM_TO_TOP,\ + FURY_DIR_TOP_TO_BOTTOM,\ + FURY_DIR_BOTTOM_TO_TOP,\ + FURY_DIR_TOP_TO_BOTTOM} + +enum +{ + FURY_MODEL_BEAST_DDR5 = 0x10, + FURY_MODEL_RENEGADE_DDR5 = 0x11, + FURY_MODEL_BEAST_WHITE_DDR4 = 0x21, + FURY_MODEL_BEAST_DDR4 = 0x23, +}; + +enum +{ + FURY_REG_MODEL = 0x06, + FURY_REG_APPLY = 0x08, + FURY_REG_MODE = 0x09, + FURY_REG_INDEX = 0x0B, + FURY_REG_DIRECTION = 0x0C, + FURY_REG_DELAY = 0x0D, + FURY_REG_SPEED = 0x0E, + FURY_REG_DYNAMIC_HOLD_A = 0x12, + FURY_REG_DYNAMIC_HOLD_B = 0x13, + FURY_REG_DYNAMIC_FADE_A = 0x14, + FURY_REG_DYNAMIC_FADE_B = 0x15, + FURY_REG_BREATH_MIN_TO_MID = 0x16, + FURY_REG_BREATH_MID_TO_MAX = 0x17, + FURY_REG_BREATH_MAX_TO_MID = 0x18, + FURY_REG_BREATH_MID_TO_MIN = 0x19, + FURY_REG_BREATH_MIN_HOLD = 0x1A, + FURY_REG_BREATH_MAX_BRIGHTNESS = 0x1B, + FURY_REG_BREATH_MID_BRIGHTNESS = 0x1C, + FURY_REG_BREATH_MIN_BRIGHTNESS = 0x1D, + FURY_REG_BRIGHTNESS = 0x20, + FURY_REG_BG_RED = 0x23, + FURY_REG_BG_GREEN = 0x24, + FURY_REG_BG_BLUE = 0x25, + FURY_REG_LENGTH = 0x26, + FURY_REG_NUM_SLOTS = 0x27, + FURY_REG_NUM_COLORS = 0x30, + FURY_REG_MODE_BASE_RED = 0x31, + FURY_REG_MODE_BASE_GREEN = 0x32, + FURY_REG_MODE_BASE_BLUE = 0x33, + FURY_REG_BASE_RED = 0x50, + FURY_REG_BASE_GREEN = 0x51, + FURY_REG_BASE_BLUE = 0x52, +}; + +enum +{ + FURY_BEGIN_TRNSFER = 0x53, + FURY_END_TRNSFER = 0x44, +}; + +// Differentiate modes which use the same written value using the upper bytes. +// The lowest order byte is generally the value written to the mode register. +enum +{ + FURY_MODE_STATIC = 0x00, + FURY_MODE_RAINBOW = 0x001, + FURY_MODE_SPECTRUM = 0x101, + FURY_MODE_RHYTHM = 0x02, + FURY_MODE_BREATH = 0x03, + FURY_MODE_DYNAMIC = 0x04, + FURY_MODE_SLIDE = 0x005, + FURY_MODE_SLITHER = 0x105, + FURY_MODE_TELEPORT = 0x205, + FURY_MODE_WIND = 0x305, + FURY_MODE_COMET = 0x006, + FURY_MODE_RAIN = 0x106, + FURY_MODE_FIREWORK = 0x206, + FURY_MODE_VOLTAGE = 0x07, + FURY_MODE_COUNTDOWN = 0x08, + FURY_MODE_FLAME = 0x09, + FURY_MODE_TWILIGHT = 0x0A, + FURY_MODE_FURY = 0x0B, + FURY_MODE_DIRECT = 0x10, + FURY_MODE_PRISM = 0x11, + FURY_MODE_BREATH_DIRECT = 0x13, +}; + +enum +{ + FURY_DIR_BOTTOM_TO_TOP = 0x01, + FURY_DIR_TOP_TO_BOTTOM = 0x02, +}; + +class KingstonFuryDRAMController +{ +public: + KingstonFuryDRAMController(i2c_smbus_interface* bus, unsigned char base_addr, std::vector slots); + + std::string GetDeviceLocation(); + unsigned int GetLEDCount(); + unsigned int GetLEDPerDIMM(); + unsigned int GetSlotCount(); + unsigned char GetMode(); + + void SendPreamble(bool synchronize); + void SendBegin(); + void SendApply(); + void SetMode(unsigned char val); + void SetNumSlots(); + + void SetRegister(int reg, unsigned char val); + void SetRegister(int reg, std::vector vals); + void SetModeColors(std::vector colors); + void SetLEDColors(std::vector colors); + +private: + bool CachedRead(int slot_idx, unsigned char reg, unsigned char *val); + bool CachedWrite(int slot_idx, unsigned char reg, unsigned char val); + bool SmbusRead(int slot_idx, unsigned char reg, unsigned char *val); + bool SmbusWrite(int slot_idx, unsigned char reg, unsigned char val); + + i2c_smbus_interface* bus; + std::vector slots; + unsigned char base_addr; + std::vector> reg_cache; +}; diff --git a/Controllers/KingstonFuryDRAMController/KingstonFuryDRAMControllerDetect.cpp b/Controllers/KingstonFuryDRAMController/KingstonFuryDRAMControllerDetect.cpp new file mode 100644 index 00000000..09e0122b --- /dev/null +++ b/Controllers/KingstonFuryDRAMController/KingstonFuryDRAMControllerDetect.cpp @@ -0,0 +1,226 @@ +/*---------------------------------------------------------*\ +| KingstonFuryDRAMControllerDetect.cpp | +| | +| Detection of Kingston Fury DDR4/5 RAM modules | +| | +| Geofrey Mon (geofbot) 14 Jul 2024 | +| Milan Cermak (krysmanta) | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-only | +\*---------------------------------------------------------*/ + +#include +#include +#include +#include +#include "Detector.h" +#include "KingstonFuryDRAMController.h" +#include "LogManager.h" +#include "RGBController.h" +#include "RGBController_KingstonFuryDRAM.h" +#include "i2c_smbus.h" +#include "pci_ids.h" + +using namespace std::chrono_literals; + +typedef enum +{ + RESULT_PASS = 0, + RESULT_FAIL = 1, + RESULT_ERROR = 2 +} TestResult; + +TestResult TestForFurySlot(i2c_smbus_interface *bus, unsigned int slot_addr, bool (*modelChecker)(char)) +{ + char model_code = 0; + int res = bus->i2c_smbus_write_quick(slot_addr, I2C_SMBUS_WRITE); + + LOG_DEBUG("[%s] Probing address %02X, res=%02X", FURY_CONTROLLER_NAME, slot_addr, res); + if(res < 0) + { + return RESULT_FAIL; + } + + // Get the model code + res = bus->i2c_smbus_read_word_data(slot_addr, FURY_REG_MODEL); + if(res < 0) + { + return RESULT_ERROR; + } + model_code = res >> 8; + std::this_thread::sleep_for(FURY_DELAY); + LOG_DEBUG("[%s] Reading model code at address %02X register %02X, res=%02X", + FURY_CONTROLLER_NAME, slot_addr, FURY_REG_MODEL, model_code); + + if(!modelChecker(model_code)) + { + LOG_DEBUG("[%s] Unknown model code 0x%02X", FURY_CONTROLLER_NAME, model_code); + return RESULT_FAIL; + } + return RESULT_PASS; +} + +bool TestDDR4Models(char code) +{ + return (code == FURY_MODEL_BEAST_WHITE_DDR4 || + code == FURY_MODEL_BEAST_DDR4); +} + +bool TestDDR5Models(char code) +{ + return (code == FURY_MODEL_BEAST_DDR5 || + code == FURY_MODEL_RENEGADE_DDR5); +} + +// Checking Fury signature in the RGB address space +TestResult TestForFurySignature(i2c_smbus_interface *bus, unsigned int slot_addr) +{ + bool passed = true; + char test_str[] = "FURY"; + int res; + + // Start transaction + res = bus->i2c_smbus_write_byte_data(slot_addr, FURY_REG_APPLY, FURY_BEGIN_TRNSFER); + if(res < 0) + { + return RESULT_ERROR; + } + + std::this_thread::sleep_for(FURY_DELAY); + LOG_DEBUG("[%s] %02X beginning transaction; res=%02X", + FURY_CONTROLLER_NAME, slot_addr, res); + + // Read and check the signature + for(int i = 1; i <= 4; i++) + { + for(int retry = 3; retry > 0; retry--) + { + res = bus->i2c_smbus_read_word_data(slot_addr, i); + std::this_thread::sleep_for(FURY_DELAY); + if(res >= 0) + { + break; + } + } + if(res < 0) + { + return RESULT_ERROR; + } + + char shifted = (res >> 8) & 0xff; + LOG_DEBUG("[%s] Testing address %02X register %02X, res=%02X", + FURY_CONTROLLER_NAME, slot_addr, i, shifted); + if(shifted != test_str[i-1]) + { + passed = false; + break; + } + } + + // Close transaction + res = bus->i2c_smbus_write_byte_data(slot_addr, FURY_REG_APPLY, FURY_END_TRNSFER); + if(res < 0) + { + return RESULT_ERROR; + } + std::this_thread::sleep_for(FURY_DELAY); + LOG_DEBUG("[%s] %02X ending transaction; res=%02X", + FURY_CONTROLLER_NAME, slot_addr, res); + + if(!passed) + { + return RESULT_FAIL; + } + return RESULT_PASS; +} + +/******************************************************************************************\ +* * +* DetectKingstonFuryDRAMControllers * +* * +* Detect Kingston Fury DDR4/5 DRAM controllers on the enumerated I2C busses. * +* * +\******************************************************************************************/ + +void DetectKingstonFuryDRAMControllers(std::vector &busses) +{ + enum { FURY_UNKNOWN, FURY_DDR4, FURY_DDR5 } fury_type = FURY_UNKNOWN; + + for(unsigned int bus = 0; bus < busses.size(); bus++) + { + IF_DRAM_SMBUS(busses[bus]->pci_vendor, busses[bus]->pci_device) + { + std::vector slots; + for(int slot_index = 0; slot_index < FURY_MAX_SLOTS; slot_index++) + { + int retries = 0; + TestResult result = RESULT_ERROR; + while(retries < 3 && result == RESULT_ERROR) + { + // Check for DDR4 module (no point, if we already found DDR5 module) + if(fury_type != FURY_DDR5) + { + result = TestForFurySlot(busses[bus], + FURY_BASE_ADDR_DDR4 + slot_index, TestDDR4Models); + if(result == RESULT_PASS) + { + result = TestForFurySignature(busses[bus], + FURY_BASE_ADDR_DDR4 + slot_index); + } + if(result == RESULT_PASS) + { + fury_type = FURY_DDR4; + break; + } + } + // Check for DDR5 module (no point, if we already found DDR4 module) + if(fury_type != FURY_DDR4 && result != RESULT_PASS) + { + result = TestForFurySlot(busses[bus], + FURY_BASE_ADDR_DDR5 + slot_index, TestDDR5Models); + if(result == RESULT_PASS) + { + result = TestForFurySignature(busses[bus], + FURY_BASE_ADDR_DDR5 + slot_index); + } + if(result == RESULT_PASS) + { + fury_type = FURY_DDR5; + break; + } + } + if(result == RESULT_ERROR) + { + // I/O error - wait for a bit and retry + retries++; + std::this_thread::sleep_for(100ms); + } + } + + // RAM module successfully detected in the slot 'slot_index' + if(result == RESULT_PASS) + { + LOG_DEBUG("[%s] detected at slot index %d", + FURY_CONTROLLER_NAME, slot_index); + slots.push_back(slot_index); + } + } + + if(!slots.empty() && fury_type != FURY_UNKNOWN) + { + unsigned char base_addr = + (fury_type == FURY_DDR4) ? FURY_BASE_ADDR_DDR4 : FURY_BASE_ADDR_DDR5; + KingstonFuryDRAMController* controller = + new KingstonFuryDRAMController(busses[bus], base_addr, slots); + RGBController_KingstonFuryDRAM* rgb_controller = + new RGBController_KingstonFuryDRAM(controller); + rgb_controller->name = + (fury_type == FURY_DDR4) ? "Kingston Fury DDR4 RGB" : "Kingston Fury DDR5 RGB"; + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + } + } +} + +REGISTER_I2C_DETECTOR("Kingston Fury DDR4/5 DRAM", DetectKingstonFuryDRAMControllers); diff --git a/Controllers/KingstonFuryDRAMController/RGBController_KingstonFuryDRAM.cpp b/Controllers/KingstonFuryDRAMController/RGBController_KingstonFuryDRAM.cpp new file mode 100644 index 00000000..1e779ef6 --- /dev/null +++ b/Controllers/KingstonFuryDRAMController/RGBController_KingstonFuryDRAM.cpp @@ -0,0 +1,746 @@ +/*---------------------------------------------------------*\ +| RGBController_KingstonFuryDRAM.cpp | +| | +| Driver for Kingston Fury DDR4/5 RAM modules | +| | +| Geofrey Mon (geofbot) 14 Jul 2024 | +| Milan Cermak (krysmanta) | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-only | +\*---------------------------------------------------------*/ + +#include "RGBController_KingstonFuryDRAM.h" +#include "KingstonFuryDRAMController.h" +#include "LogManager.h" + +const RGBColor default_colors[] = +{ + ToRGBColor(0xFF, 0x00, 0x00), + ToRGBColor(0x00, 0xFF, 0x00), + ToRGBColor(0xFF, 0x64, 0x00), + ToRGBColor(0x00, 0x00, 0xFF), + ToRGBColor(0xEF, 0xEF, 0x00), + ToRGBColor(0x80, 0x00, 0x80), + ToRGBColor(0x00, 0x6D, 0x77), + ToRGBColor(0xFF, 0xC8, 0x00), + ToRGBColor(0xFF, 0x55, 0xFF), + ToRGBColor(0x3C, 0x7D, 0xFF), +}; + + +/**------------------------------------------------------------------*\ + @name Kingston Fury DDR4/5 DRAM + @category RAM + @type SMBus + @save :x: + @direct :white_check_mark: + @effects :white_check_mark: + @detectors DetectKingstonFuryDRAMControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_KingstonFuryDRAM::RGBController_KingstonFuryDRAM(KingstonFuryDRAMController* controller_ptr) +{ + controller = controller_ptr; + + name = "Kingston Fury DDR4/5 DRAM"; + vendor = "Kingston"; + type = DEVICE_TYPE_DRAM; + description = "Kingston Fury Beast/Renegade DDR4/5 DRAM Device"; + location = controller->GetDeviceLocation(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = FURY_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness_min = 0; + Direct.brightness_max = 100; + Direct.brightness = 80; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = FURY_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_BRIGHTNESS; + Static.color_mode = MODE_COLORS_MODE_SPECIFIC; + Static.colors_min = 1; + Static.colors_max = 1; + Static.colors.assign(default_colors, default_colors + 1); + Static.brightness_min = 0; + Static.brightness_max = 100; + Static.brightness = 80; + modes.push_back(Static); + + // All speed values are inverted + mode Rainbow; + Rainbow.name = "Rainbow"; + Rainbow.value = FURY_MODE_RAINBOW; + Rainbow.flags = MODE_FLAG_HAS_SPEED | + MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_UD; + Rainbow.speed_min = 60; + Rainbow.speed_max = 0; + Rainbow.speed = 25; + Rainbow.direction = MODE_DIRECTION_UP; + Rainbow.brightness_min = 0; + Rainbow.brightness_max = 100; + Rainbow.brightness = 80; + Rainbow.color_mode = MODE_COLORS_NONE; + modes.push_back(Rainbow); + + mode Spectrum; + Spectrum.name = "Spectrum"; + Spectrum.value = FURY_MODE_SPECTRUM; + Spectrum.flags = MODE_FLAG_HAS_SPEED | + MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_UD; + Spectrum.speed_min = 60; + Spectrum.speed_max = 0; + Spectrum.speed = 25; + Spectrum.direction = MODE_DIRECTION_UP; + Spectrum.brightness_min = 0; + Spectrum.brightness_max = 100; + Spectrum.brightness = 80; + Spectrum.color_mode = MODE_COLORS_NONE; + modes.push_back(Spectrum); + + mode Rhythm; + Rhythm.name = "Rhythm"; + Rhythm.value = FURY_MODE_RHYTHM; + Rhythm.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | + MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Rhythm.color_mode = MODE_COLORS_MODE_SPECIFIC; + Rhythm.colors_min = 2; + Rhythm.colors_max = 11; + Rhythm.colors.assign(default_colors, default_colors + 10); + Rhythm.colors.push_back(FURY_DEFAULT_BG_COLOR); + Rhythm.speed_min = 10; + Rhythm.speed_max = 0; + Rhythm.speed = 0; + Rhythm.brightness_min = 0; + Rhythm.brightness_max = 100; + Rhythm.brightness = 80; + modes.push_back(Rhythm); + + mode Breath; + Breath.name = "Breath"; + Breath.value = FURY_MODE_BREATH; + Breath.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | + MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_SPEED | + MODE_FLAG_HAS_BRIGHTNESS; + Breath.color_mode = MODE_COLORS_MODE_SPECIFIC; + Breath.colors_min = 1; + Breath.colors_max = 10; + Breath.colors.assign(default_colors, default_colors + 10); + Breath.speed_min = 10; + Breath.speed_max = 1; + Breath.speed = 5; + Breath.brightness_min = 0; + Breath.brightness_max = 100; + Breath.brightness = 80; + modes.push_back(Breath); + + mode Dynamic; + Dynamic.name = "Dynamic"; + Dynamic.value = FURY_MODE_DYNAMIC; + Dynamic.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | + MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Dynamic.color_mode = MODE_COLORS_MODE_SPECIFIC; + Dynamic.colors_min = 1; + Dynamic.colors_max = 10; + Dynamic.colors.assign(default_colors, default_colors + 10); + Dynamic.speed_min = 1000; + Dynamic.speed_max = 100; + Dynamic.speed = 300; + Dynamic.brightness_min = 0; + Dynamic.brightness_max = 100; + Dynamic.brightness = 80; + modes.push_back(Dynamic); + + mode Slide; + Slide.name = "Slide"; + Slide.value = FURY_MODE_SLIDE; + Slide.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | + MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | + MODE_FLAG_HAS_DIRECTION_UD; + Slide.color_mode = MODE_COLORS_MODE_SPECIFIC; + Slide.colors_min = 2; + Slide.colors_max = 11; + Slide.colors.assign(default_colors, default_colors + 10); + Slide.colors.push_back(FURY_DEFAULT_BG_COLOR); + Slide.speed_min = 255; + Slide.speed_max = 0; + Slide.speed = 8; + Slide.direction = MODE_DIRECTION_UP; + Slide.brightness_min = 0; + Slide.brightness_max = 100; + Slide.brightness = 80; + modes.push_back(Slide); + + mode Slither; + Slither.name = "Slither"; + Slither.value = FURY_MODE_SLITHER; + Slither.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | + MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Slither.color_mode = MODE_COLORS_MODE_SPECIFIC; + Slither.colors_min = 2; + Slither.colors_max = 11; + Slither.colors.assign(default_colors, default_colors + 10); + Slither.colors.push_back(FURY_DEFAULT_BG_COLOR); + Slither.speed_min = 255; + Slither.speed_max = 0; + Slither.speed = 40; + Slither.brightness_min = 0; + Slither.brightness_max = 100; + Slither.brightness = 80; + modes.push_back(Slither); + + mode Teleport; + Teleport.name = "Teleport"; + Teleport.value = FURY_MODE_TELEPORT; + Teleport.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | + MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Teleport.color_mode = MODE_COLORS_MODE_SPECIFIC; + Teleport.colors_min = 2; + Teleport.colors_max = 11; + Teleport.colors.assign(default_colors, default_colors + 10); + Teleport.colors.push_back(FURY_DEFAULT_BG_COLOR); + Teleport.speed_min = 255; + Teleport.speed_max = 0; + Teleport.speed = 8; + Teleport.brightness_min = 0; + Teleport.brightness_max = 100; + Teleport.brightness = 80; + modes.push_back(Teleport); + + mode Wind; + Wind.name = "Wind"; + Wind.value = FURY_MODE_WIND; + Wind.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | + MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | + MODE_FLAG_HAS_DIRECTION_UD; + Wind.color_mode = MODE_COLORS_MODE_SPECIFIC; + Wind.colors_min = 2; + Wind.colors_max = 11; + Wind.colors.assign(default_colors, default_colors + 10); + Wind.colors.push_back(FURY_DEFAULT_BG_COLOR); + Wind.speed_min = 255; + Wind.speed_max = 0; + Wind.speed = 8; + Wind.direction = MODE_DIRECTION_UP; + Wind.brightness_min = 0; + Wind.brightness_max = 100; + Wind.brightness = 80; + modes.push_back(Wind); + + mode Comet; + Comet.name = "Comet"; + Comet.value = FURY_MODE_COMET; + Comet.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | + MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | + MODE_FLAG_HAS_DIRECTION_UD; + Comet.color_mode = MODE_COLORS_MODE_SPECIFIC; + Comet.colors_min = 1; + Comet.colors_max = 10; + Comet.colors.assign(default_colors, default_colors + 10); + Comet.speed_min = 255; + Comet.speed_max = 0; + Comet.speed = 25; + Comet.direction = MODE_DIRECTION_UP; + Comet.brightness_min = 0; + Comet.brightness_max = 100; + Comet.brightness = 80; + modes.push_back(Comet); + + mode Rain; + Rain.name = "Rain"; + Rain.value = FURY_MODE_RAIN; + Rain.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | + MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | + MODE_FLAG_HAS_DIRECTION_UD; + Rain.color_mode = MODE_COLORS_MODE_SPECIFIC; + Rain.colors_min = 1; + Rain.colors_max = 10; + Rain.colors.assign(default_colors, default_colors + 10); + Rain.speed_min = 28; + Rain.speed_max = 8; + Rain.speed = 25; + Rain.direction = MODE_DIRECTION_DOWN; + Rain.brightness_min = 0; + Rain.brightness_max = 100; + Rain.brightness = 80; + modes.push_back(Rain); + + mode Firework; + Firework.name = "Firework"; + Firework.value = FURY_MODE_FIREWORK; + Firework.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | + MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | + MODE_FLAG_HAS_DIRECTION_UD; + Firework.color_mode = MODE_COLORS_MODE_SPECIFIC; + Firework.colors_min = 1; + Firework.colors_max = 10; + Firework.colors.assign(default_colors, default_colors + 10); + Firework.speed_min = 83; + Firework.speed_max = 33; + Firework.speed = 33; + Firework.direction = MODE_DIRECTION_UP; + Firework.brightness_min = 0; + Firework.brightness_max = 100; + Firework.brightness = 80; + modes.push_back(Firework); + + mode Voltage; + Voltage.name = "Voltage"; + Voltage.value = FURY_MODE_VOLTAGE; + Voltage.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | + MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | + MODE_FLAG_HAS_DIRECTION_UD; + Voltage.color_mode = MODE_COLORS_MODE_SPECIFIC; + Voltage.colors_min = 2; + Voltage.colors_max = 11; + Voltage.colors.assign(default_colors, default_colors + 10); + Voltage.colors.push_back(FURY_DEFAULT_BG_COLOR); + Voltage.speed_min = 18; + Voltage.speed_max = 5; + Voltage.speed = 16; + Voltage.direction = MODE_DIRECTION_UP; + Voltage.brightness_min = 0; + Voltage.brightness_max = 100; + Voltage.brightness = 80; + modes.push_back(Voltage); + +#ifdef FURY_SYNC + mode Countdown; + Countdown.name = "Countdown"; + Countdown.value = FURY_MODE_COUNTDOWN; + Countdown.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | + MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | + MODE_FLAG_HAS_DIRECTION_UD; + Countdown.color_mode = MODE_COLORS_MODE_SPECIFIC; + Countdown.colors_min = 2; + Countdown.colors_max = 11; + Countdown.colors.assign(default_colors, default_colors + 10); + Countdown.colors.push_back(FURY_DEFAULT_BG_COLOR); + Countdown.speed_min = 76; + Countdown.speed_max = 20; + Countdown.speed = 76; + Countdown.direction = MODE_DIRECTION_UP; + Countdown.brightness_min = 0; + Countdown.brightness_max = 100; + Countdown.brightness = 80; + modes.push_back(Countdown); +#endif + + mode Flame; + Flame.name = "Flame"; + Flame.value = FURY_MODE_FLAME; + Flame.flags = MODE_FLAG_HAS_SPEED | + MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_DIRECTION_UD; + Flame.speed_min = 64; + Flame.speed_max = 40; + Flame.speed = 64; + Flame.direction = MODE_DIRECTION_UP; + Flame.brightness_min = 0; + Flame.brightness_max = 100; + Flame.brightness = 80; + Flame.color_mode = MODE_COLORS_NONE; + modes.push_back(Flame); + + mode Twilight; + Twilight.name = "Twilight"; + Twilight.value = FURY_MODE_TWILIGHT; + Twilight.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Twilight.speed_min = 255; + Twilight.speed_max = 0; + Twilight.speed = 64; + Twilight.brightness_min = 0; + Twilight.brightness_max = 100; + Twilight.brightness = 80; + Twilight.color_mode = MODE_COLORS_NONE; + modes.push_back(Twilight); + + mode Fury; + Fury.name = "Fury"; + Fury.value = FURY_MODE_FURY; + Fury.flags = MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | + MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS | + MODE_FLAG_HAS_DIRECTION_UD; + Fury.color_mode = MODE_COLORS_MODE_SPECIFIC; + Fury.colors_min = 2; + Fury.colors_max = 11; + Fury.colors.assign(default_colors, default_colors + 10); + Fury.colors.push_back(FURY_DEFAULT_BG_COLOR); + Fury.speed_min = 255; + Fury.speed_max = 0; + Fury.speed = 76; + Fury.direction = MODE_DIRECTION_UP; + Fury.brightness_min = 0; + Fury.brightness_max = 100; + Fury.brightness = 80; + modes.push_back(Fury); + + mode Prism; + Prism.name = "Prism"; + Prism.value = FURY_MODE_PRISM; + Prism.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Prism.speed_min = 60; + Prism.speed_max = 0; + Prism.speed = 40; + Prism.brightness_min = 0; + Prism.brightness_max = 100; + Prism.brightness = 80; + Prism.color_mode = MODE_COLORS_NONE; + modes.push_back(Prism); + + SetupZones(); + + // default per-LED color is red + colors.assign(colors.size(), default_colors[0]); +} + +RGBController_KingstonFuryDRAM::~RGBController_KingstonFuryDRAM() +{ + delete controller; +} + +void RGBController_KingstonFuryDRAM::SetupZones() +{ + for(unsigned int slot = 0; slot < controller->GetSlotCount(); slot++) + { + zone* new_zone = new zone; + + new_zone->name = "Fury Slot "; + new_zone->name.append(std::to_string(slot + 1)); + new_zone->type = ZONE_TYPE_LINEAR; + new_zone->leds_min = controller->GetLEDPerDIMM(); + new_zone->leds_max = new_zone->leds_min; + new_zone->leds_count = new_zone->leds_min; + new_zone->matrix_map = NULL; + + zones.push_back(*new_zone); + } + + for(std::size_t zone_idx = 0; zone_idx < zones.size(); zone_idx++) + { + for(std::size_t led_idx = 0; led_idx < zones[zone_idx].leds_count; led_idx++) + { + led* new_led = new led(); + + new_led->name = "Fury Slot "; + new_led->name.append(std::to_string(zone_idx + 1)); + new_led->name.append(", LED "); + new_led->name.append(std::to_string(led_idx + 1)); + + new_led->value = leds.size(); + + leds.push_back(*new_led); + } + } + + SetupColors(); +} + +void RGBController_KingstonFuryDRAM::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ + LOG_DEBUG("[%s] resize zone", + FURY_CONTROLLER_NAME); +} + +// some modes have different actual values to be written, depending on the color mode +unsigned char RGBController_KingstonFuryDRAM::GetRealModeValue() +{ + int mode_value = modes[active_mode].value; + switch(mode_value) + { + case FURY_MODE_BREATH: + if(modes[active_mode].color_mode == MODE_COLORS_MODE_SPECIFIC) + { + return FURY_MODE_BREATH; + } + else + { + return FURY_MODE_BREATH_DIRECT; + } + } + return mode_value; +} + +void RGBController_KingstonFuryDRAM::DeviceUpdateLEDs() +{ + controller->SendBegin(); + controller->SetMode(GetRealModeValue()); + + // Fixed mode specific parameters + switch(modes[active_mode].value) + { + case FURY_MODE_STATIC: + controller->SetRegister(FURY_REG_DIRECTION, FURY_DIR_BOTTOM_TO_TOP); + controller->SetRegister(FURY_REG_DELAY, 0); + controller->SetRegister(FURY_REG_SPEED, 0); + break; + + case FURY_MODE_RAINBOW: + case FURY_MODE_VOLTAGE: + case FURY_MODE_COUNTDOWN: + case FURY_MODE_FLAME: + case FURY_MODE_TWILIGHT: + case FURY_MODE_FURY: + controller->SetRegister(FURY_REG_DELAY, 0); + break; + + case FURY_MODE_RHYTHM: + controller->SetRegister(FURY_REG_DIRECTION, + FURY_DIR_BOTTOM_TO_TOP); + break; + + case FURY_MODE_BREATH: + controller->SetRegister(FURY_REG_DIRECTION, + FURY_DIR_BOTTOM_TO_TOP); + controller->SetRegister(FURY_REG_DELAY, 0); + break; + + case FURY_MODE_DYNAMIC: + controller->SetRegister(FURY_REG_DIRECTION, + FURY_DIR_BOTTOM_TO_TOP); + controller->SetRegister(FURY_REG_DELAY, 0); + break; + + case FURY_MODE_SLITHER: + controller->SetRegister(FURY_REG_DELAY, 12); + controller->SetRegister(FURY_REG_DIRECTION, + FURY_ALT_DIRECTIONS); + break; + + case FURY_MODE_TELEPORT: + controller->SetRegister(FURY_REG_DELAY, 0); + controller->SetRegister(FURY_REG_DIRECTION, + FURY_ALT_DIRECTIONS); + break; + + case FURY_MODE_RAIN: + controller->SetRegister(FURY_REG_DELAY, 0); + controller->SetRegister(FURY_REG_LENGTH, 3); + break; + + case FURY_MODE_FIREWORK: + controller->SetRegister(FURY_REG_DELAY, 0); + controller->SetRegister(FURY_REG_LENGTH, 7); + break; + } + + // Mode-specific parameters that are customizable in Kingston's software + // but which are not yet available in the OpenRGB interface. + // Default values are used here and the parameter ranges are annotated + switch(modes[active_mode].value) + { + case FURY_MODE_RHYTHM: + // between 2 and 5 + controller->SetRegister(FURY_REG_DELAY, 3); + break; + + + case FURY_MODE_SLIDE: + // between 1 and 4 + controller->SetRegister(FURY_REG_DELAY, 3); + // between 1 and 12 + controller->SetRegister(FURY_REG_LENGTH, 4); + break; + + case FURY_MODE_SLITHER: + // between 1 and 32 + controller->SetRegister(FURY_REG_LENGTH, 12); + break; + + case FURY_MODE_TELEPORT: + // between 1 and 12 + controller->SetRegister(FURY_REG_LENGTH, 3); + + case FURY_MODE_WIND: + // between 0 and 32 + controller->SetRegister(FURY_REG_DELAY, 0); + // between 1 and 32 + controller->SetRegister(FURY_REG_LENGTH, 12); + break; + + case FURY_MODE_COMET: + // between 0 and 20 + controller->SetRegister(FURY_REG_DELAY, 0); + // between 1 and 18 + controller->SetRegister(FURY_REG_LENGTH, 7); + break; + + case FURY_MODE_PRISM: + // between 2 and 4 + controller->SetRegister(FURY_REG_DELAY, 2); + break; + + case FURY_MODE_SPECTRUM: + // between 2 and 6 + controller->SetRegister(FURY_REG_DELAY, 4); + break; + } + + switch(modes[active_mode].color_mode) + { + case MODE_COLORS_PER_LED: + controller->SetLEDColors(colors); + break; + + case MODE_COLORS_MODE_SPECIFIC: + switch(modes[active_mode].value) + { + case FURY_MODE_RHYTHM: + case FURY_MODE_SLIDE: + case FURY_MODE_SLITHER: + case FURY_MODE_TELEPORT: + case FURY_MODE_WIND: + case FURY_MODE_VOLTAGE: + case FURY_MODE_COUNTDOWN: + case FURY_MODE_FURY: + { + std::vector mode_colors(modes[active_mode].colors.begin(), + modes[active_mode].colors.end() - 1); + controller->SetModeColors(mode_colors); + // handle background color + RGBColor color = modes[active_mode].colors[mode_colors.size()]; + unsigned char red = RGBGetRValue(color); + unsigned char green = RGBGetGValue(color); + unsigned char blue = RGBGetBValue(color); + + controller->SetRegister(FURY_REG_BG_RED, red); + controller->SetRegister(FURY_REG_BG_GREEN, green); + controller->SetRegister(FURY_REG_BG_BLUE, blue); + break; + } + default: + controller->SetModeColors(modes[active_mode].colors); + break; + } + break; + } + + if(modes[active_mode].flags & MODE_FLAG_HAS_DIRECTION_UD) + { + if(modes[active_mode].direction == MODE_DIRECTION_UP) + { + controller->SetRegister(FURY_REG_DIRECTION, + FURY_DIR_BOTTOM_TO_TOP); + } + else + { + controller->SetRegister(FURY_REG_DIRECTION, + FURY_DIR_TOP_TO_BOTTOM); + } + } + + if(modes[active_mode].flags & MODE_FLAG_HAS_SPEED) + { + switch(modes[active_mode].value) + { + case FURY_MODE_DYNAMIC: + controller->SetRegister(FURY_REG_SPEED, 0); + + // time spent holding a color + controller->SetRegister(FURY_REG_DYNAMIC_HOLD_A, + modes[active_mode].speed >> 8); + // set to 1 as long as the time above is nonzero + controller->SetRegister(FURY_REG_DYNAMIC_HOLD_B, 1); + + // time spent fading to next color + controller->SetRegister(FURY_REG_DYNAMIC_FADE_A, + (modes[active_mode].speed * 5) >> 8); + // set to 1 as long as the time above is nonzero + controller->SetRegister(FURY_REG_DYNAMIC_FADE_B, 1); + break; + + case FURY_MODE_BREATH: + controller->SetRegister(FURY_REG_SPEED, 0); + + // These are the speed values used by Kingston's software, + // representing the time spent fading between two brightness levels + controller->SetRegister(FURY_REG_BREATH_MIN_TO_MID, + modes[active_mode].speed * 3); + controller->SetRegister(FURY_REG_BREATH_MID_TO_MAX, + modes[active_mode].speed); + controller->SetRegister(FURY_REG_BREATH_MAX_TO_MID, + modes[active_mode].speed); + controller->SetRegister(FURY_REG_BREATH_MID_TO_MIN, + modes[active_mode].speed * 3); + + // Time spent holding min brightness + controller->SetRegister(FURY_REG_BREATH_MIN_HOLD, 1); + + // Brightness values (relative to overall brightness) + controller->SetRegister(FURY_REG_BREATH_MAX_BRIGHTNESS, 100); + controller->SetRegister(FURY_REG_BREATH_MID_BRIGHTNESS, 64); + controller->SetRegister(FURY_REG_BREATH_MIN_BRIGHTNESS, 0); + // Kingston software uses 1 for min brightness, + // but 0 seems to look better. + + break; + + case FURY_MODE_RAIN: + { + // speed offsets taken from Kingston software + unsigned char offsets[4] = {11, 0, 15, 9}; + std::vector speeds; + for (std::size_t idx = 0; idx < controller->GetSlotCount(); idx++) + { + speeds.push_back(modes[active_mode].speed + offsets[idx % 4]); + } + controller->SetRegister(FURY_REG_SPEED, speeds); + break; + } + + case FURY_MODE_FIREWORK: + { + // speed offsets taken from Kingston software + unsigned char offsets[4] = {15, 0, 19, 4}; + std::vector speeds; + for (std::size_t idx = 0; idx < controller->GetSlotCount(); idx++) + { + speeds.push_back(modes[active_mode].speed + offsets[idx % 4]); + } + controller->SetRegister(FURY_REG_SPEED, speeds); + break; + } + + default: + controller->SetRegister(FURY_REG_SPEED, modes[active_mode].speed); + break; + } + } + + controller->SetRegister(FURY_REG_BRIGHTNESS, + modes[active_mode].brightness); + controller->SetNumSlots(); + controller->SendApply(); +} + +void RGBController_KingstonFuryDRAM::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_KingstonFuryDRAM::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateLEDs(); +} + +void RGBController_KingstonFuryDRAM::DeviceUpdateMode() +{ + LOG_DEBUG("[%s] device update mode", + FURY_CONTROLLER_NAME); + // Preamble only necessary when changing modes. + if(GetRealModeValue() != controller->GetMode()) + { + controller->SendPreamble(modes[active_mode].value != FURY_MODE_RAIN && + modes[active_mode].value != FURY_MODE_FIREWORK && + modes[active_mode].value != FURY_MODE_DIRECT); + } + DeviceUpdateLEDs(); +} diff --git a/Controllers/KingstonFuryDRAMController/RGBController_KingstonFuryDRAM.h b/Controllers/KingstonFuryDRAMController/RGBController_KingstonFuryDRAM.h new file mode 100644 index 00000000..21ebc283 --- /dev/null +++ b/Controllers/KingstonFuryDRAMController/RGBController_KingstonFuryDRAM.h @@ -0,0 +1,36 @@ +/*---------------------------------------------------------*\ +| RGBController_KingstonFuryDRAM.h | +| | +| Driver for Kingston Fury DDR4/5 RAM modules | +| | +| Geofrey Mon (geofbot) 14 Jul 2024 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-only | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "KingstonFuryDRAMController.h" + +class RGBController_KingstonFuryDRAM : public RGBController +{ +public: + RGBController_KingstonFuryDRAM(KingstonFuryDRAMController* controller_ptr); + ~RGBController_KingstonFuryDRAM(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + +private: + unsigned char GetRealModeValue(); + KingstonFuryDRAMController* controller; +};