diff --git a/Controllers/ZotacV2GPUController/RGBController_ZotacV2GPU.cpp b/Controllers/ZotacV2GPUController/RGBController_ZotacV2GPU.cpp new file mode 100644 index 000000000..9a93ae2c9 --- /dev/null +++ b/Controllers/ZotacV2GPUController/RGBController_ZotacV2GPU.cpp @@ -0,0 +1,469 @@ +/*-----------------------------------------*\ +| RGBController_ZotacV2GPU.cpp | +| | +| Generic RGB Interface for OpenRGB | +| ZOTAC RTX 30/40 series GPU Driver | +| | +| Krzysztof Haładyn (krzys_h) 3/16/2023 | +\*-----------------------------------------*/ + +#include "RGBController_ZotacV2GPU.h" +#include "LogManager.h" +#include + +std::map ZOTAC_V2_GPU_CONFIG = +{ + { "N653E-1013", { 2, false } }, // ZOTAC GAMING GeForce RTX 3070 Ti Trinity OC + { "N612A-1012", { 2, false } }, // ZOTAC GAMING GeForce RTX 3080 Ti AMP Holo + { "N618A-1015", { 4, true } }, // ZOTAC GAMING GeForce RTX 3090 AMP Extreme Holo + { "N675E-1019", { 1, true } }, // ZOTAC GAMING GeForce RTX 4090 Trinity OC + { "N675A-1019", { 5, true } }, // ZOTAC GAMING GeForce RTX 4090 AMP Extreme AIRO +}; + +std::vector> ZOTAC_V2_GPU_DUET_PRESETS = +{ + { ToRGBColor(0x32, 0xCF, 0xA7), ToRGBColor(0x93, 0x34, 0xC2) }, + { ToRGBColor(0x00, 0xC9, 0x14), ToRGBColor(0x00, 0x20, 0xF5) }, + { ToRGBColor(0xD1, 0xFC, 0x00), ToRGBColor(0xF1, 0x0C, 0x00) }, + { ToRGBColor(0xFF, 0x68, 0x7C), ToRGBColor(0xD4, 0x00, 0x4D) }, +}; + +/**------------------------------------------------------------------*\ + @name ZOTAC 30/40 series GPU + @category GPU + @type I2C + @save :robot: + @direct :x: + @effects :tools: + @detectors DetectZotacV2GPUControllers + @comment + OpenRGB does not support per-zone effect modes, so only + the synchronized mode is supported for now. Sound based + effects are not supported. Idle/active config is not + supported. +\*-------------------------------------------------------------------*/ + +RGBController_ZotacV2GPU::RGBController_ZotacV2GPU(ZotacV2GPUController* controller_ptr) +{ + controller = controller_ptr; + + name = "ZOTAC GPU"; + vendor = "ZOTAC"; + description = "ZOTAC 30/40 series RGB GPU Device (" + controller->GetVersion() + ")"; + location = controller->GetDeviceLocation(); + type = DEVICE_TYPE_GPU; + + try + { + config = ZOTAC_V2_GPU_CONFIG.at(controller->GetVersion()); + } + catch(std::out_of_range) + { + LOG_ERROR("No zone config found for %s", version.c_str()); + config = ZotacV2GPUConfig(); + } + version += std::to_string(config.numberOfZones) + " zones, " + + (config.supportsExternalLEDStrip ? "with" : "without") + " external LED strip support"; + + mode STATIC; + STATIC.name = "Static"; + STATIC.value = ZOTAC_V2_GPU_MODE_STATIC; + STATIC.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + STATIC.brightness_min = 0; + STATIC.brightness_max = 100; + STATIC.brightness = 100; + STATIC.color_mode = MODE_COLORS_MODE_SPECIFIC; + STATIC.colors_min = 1; + STATIC.colors_max = 1; + STATIC.colors.resize(1); + STATIC.colors[0] = ToRGBColor(0, 0, 255); + modes.push_back(STATIC); + + mode BREATH; + BREATH.name = "Breath"; + BREATH.value = ZOTAC_V2_GPU_MODE_BREATH; + BREATH.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + BREATH.brightness_min = 0; + BREATH.brightness_max = 100; + BREATH.brightness = 100; + BREATH.speed_min = 0; + BREATH.speed_max = 100; + BREATH.speed = 20; + BREATH.color_mode = MODE_COLORS_MODE_SPECIFIC; + BREATH.colors_min = 1; + BREATH.colors_max = 1; + BREATH.colors.resize(1); + BREATH.colors[0] = ToRGBColor(0, 0, 255); + modes.push_back(BREATH); + + mode FADE; + FADE.name = "Fade"; + FADE.value = ZOTAC_V2_GPU_MODE_FADE; + FADE.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_SPEED; + FADE.speed_min = 0; + FADE.speed_max = 100; + FADE.speed = 20; + FADE.color_mode = MODE_COLORS_NONE; + modes.push_back(FADE); + + mode WINK; + WINK.name = "Wink"; + WINK.value = ZOTAC_V2_GPU_MODE_WINK; + WINK.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + WINK.brightness_min = 0; + WINK.brightness_max = 100; + WINK.brightness = 100; + WINK.speed_min = 0; + WINK.speed_max = 100; + WINK.speed = 20; + WINK.color_mode = MODE_COLORS_MODE_SPECIFIC; + WINK.colors_min = 1; + WINK.colors_max = 1; + WINK.colors.resize(1); + WINK.colors[0] = ToRGBColor(0, 0, 255); + modes.push_back(WINK); + + if(config.numberOfZones > 1) + { + // This mode is only supported on GPUs with more than one zone, + // because it spans multiple zones. + + // It's also supported in synchronized mode only (which is the only + // thing this RGBController supports for now anyway) + + mode FLASH; + FLASH.name = "Flash"; + FLASH.value = ZOTAC_V2_GPU_MODE_FLASH; + FLASH.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_SPEED; + FLASH.speed_min = 0; + FLASH.speed_max = 100; + FLASH.speed = 20; + FLASH.color_mode = MODE_COLORS_NONE; + modes.push_back(FLASH); + } + + // (Sound activated - not supported) + //mode SHINE; + //SHINE.name = "Shine"; + //SHINE.value = ZOTAC_V2_GPU_MODE_SHINE; + //SHINE.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + //SHINE.brightness_min = 0; + //SHINE.brightness_max = 100; + //SHINE.brightness = 100; + //SHINE.color_mode = MODE_COLORS_MODE_SPECIFIC; + //SHINE.colors_min = 1; + //SHINE.colors_max = 1; + //SHINE.colors.resize(1); + //SHINE.colors[0] = ToRGBColor(0, 0, 255); + //modes.push_back(SHINE); + + mode RANDOM; + RANDOM.name = "Random"; + RANDOM.value = ZOTAC_V2_GPU_MODE_RANDOM; + RANDOM.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR; + RANDOM.brightness_min = 0; + RANDOM.brightness_max = 100; + RANDOM.brightness = 100; + RANDOM.speed_min = 0; + RANDOM.speed_max = 100; + RANDOM.speed = 20; + RANDOM.color_mode = MODE_COLORS_MODE_SPECIFIC; + RANDOM.colors_min = 1; + RANDOM.colors_max = 1; + RANDOM.colors.resize(1); + RANDOM.colors[0] = ToRGBColor(0, 0, 255); + modes.push_back(RANDOM); + + mode SLIDE; + SLIDE.name = "Slide"; + SLIDE.value = ZOTAC_V2_GPU_MODE_SLIDE; + SLIDE.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + SLIDE.brightness_min = 0; + SLIDE.brightness_max = 100; + SLIDE.brightness = 100; + SLIDE.speed_min = 0; + SLIDE.speed_max = 100; + SLIDE.speed = 20; + SLIDE.color_mode = MODE_COLORS_MODE_SPECIFIC; + SLIDE.colors_min = 1; + SLIDE.colors_max = 1; + SLIDE.colors.resize(1); + SLIDE.colors[0] = ToRGBColor(0, 0, 255); + modes.push_back(SLIDE); + + mode RAINBOW; + RAINBOW.name = "Rainbow"; + RAINBOW.value = ZOTAC_V2_GPU_MODE_RAINBOW; + RAINBOW.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + RAINBOW.speed_min = 0; + RAINBOW.speed_max = 100; + RAINBOW.speed = 20; + RAINBOW.color_mode = MODE_COLORS_NONE; + modes.push_back(RAINBOW); + mode RAINBOW_CIRCUIT = RAINBOW; + RAINBOW_CIRCUIT.name = "Rainbow (circuit)"; + modes.push_back(RAINBOW_CIRCUIT); + + mode MARQUEE; + MARQUEE.name = "Marquee"; + MARQUEE.value = ZOTAC_V2_GPU_MODE_MARQUEE; + MARQUEE.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + MARQUEE.brightness_min = 0; + MARQUEE.brightness_max = 100; + MARQUEE.brightness = 100; + MARQUEE.speed_min = 0; + MARQUEE.speed_max = 100; + MARQUEE.speed = 20; + MARQUEE.color_mode = MODE_COLORS_MODE_SPECIFIC; + MARQUEE.colors_min = 1; + MARQUEE.colors_max = 1; + MARQUEE.colors.resize(1); + MARQUEE.colors[0] = ToRGBColor(0, 0, 255); + modes.push_back(MARQUEE); + mode MARQUEE_CIRCUIT = MARQUEE; + MARQUEE_CIRCUIT.name = "Marquee (circuit)"; + modes.push_back(MARQUEE_CIRCUIT); + + mode DRIP; + DRIP.name = "Drip"; + DRIP.value = ZOTAC_V2_GPU_MODE_DRIP; + DRIP.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + DRIP.brightness_min = 0; + DRIP.brightness_max = 100; + DRIP.brightness = 100; + DRIP.speed_min = 0; + DRIP.speed_max = 100; + DRIP.speed = 20; + DRIP.color_mode = MODE_COLORS_MODE_SPECIFIC; + DRIP.colors_min = 1; + DRIP.colors_max = 1; + DRIP.colors.resize(1); + DRIP.colors[0] = ToRGBColor(0, 0, 255); + modes.push_back(DRIP); + mode DRIP_CIRCUIT = DRIP; + DRIP_CIRCUIT.name = "Drip (circuit)"; + modes.push_back(DRIP_CIRCUIT); + + // (Sound activated - not supported) + //mode DANCE; + //DANCE.name = "Dance (sound activated)"; + //DANCE.value = ZOTAC_V2_GPU_MODE_DANCE; + //DANCE.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + //DANCE.brightness_min = 0; + //DANCE.brightness_max = 100; + //DANCE.brightness = 100; + //DANCE.color_mode = MODE_COLORS_MODE_SPECIFIC; + //DANCE.colors_min = 1; + //DANCE.colors_max = 1; + //DANCE.colors.resize(1); + //DANCE.colors[0] = ToRGBColor(0, 0, 255); + //modes.push_back(DANCE); + + mode DUET; + DUET.name = "Duet"; + DUET.value = ZOTAC_V2_GPU_MODE_DUET; + DUET.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_MODE_SPECIFIC_COLOR | MODE_FLAG_HAS_DIRECTION_LR; + DUET.speed_min = 0; + DUET.speed_max = 100; + DUET.speed = 20; + DUET.color_mode = MODE_COLORS_MODE_SPECIFIC; + DUET.colors_min = 2; + DUET.colors_max = 2; + DUET.colors.resize(2); + DUET.colors[0] = ZOTAC_V2_GPU_DUET_PRESETS[1].first; + DUET.colors[1] = ZOTAC_V2_GPU_DUET_PRESETS[1].second; + modes.push_back(DUET); + mode DUET_CIRCUIT = DUET; + DUET_CIRCUIT.name = "Duet (circuit)"; + modes.push_back(DUET_CIRCUIT); + + + mode PATH; + PATH.name = "Path"; + PATH.value = ZOTAC_V2_GPU_MODE_PATH; + PATH.flags = MODE_FLAG_AUTOMATIC_SAVE | MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_DIRECTION_LR; + PATH.brightness_min = 0; + PATH.brightness_max = 100; + PATH.brightness = 100; + PATH.speed_min = 0; + PATH.speed_max = 100; + PATH.speed = 20; + PATH.color_mode = MODE_COLORS_NONE; + modes.push_back(PATH); + + SetupZones(); +} + +RGBController_ZotacV2GPU::~RGBController_ZotacV2GPU() +{ + delete controller; +} + +void RGBController_ZotacV2GPU::SetupZones() +{ + led new_led; + new_led.name = "GPU LED"; + leds.push_back(new_led); + + zone new_zone; + new_zone.name = "GPU Zone"; + new_zone.type = ZONE_TYPE_SINGLE; + new_zone.leds_min = 1; + new_zone.leds_max = 1; + new_zone.leds_count = 1; + new_zone.matrix_map = NULL; + zones.push_back(new_zone); + + SetupColors(); + SetupInitialValues(); +} + +void RGBController_ZotacV2GPU::SetupInitialValues() +{ + /*---------------------------------------------------------*\ + | Retrieve current values by reading the device | + \*---------------------------------------------------------*/ + + bool on; + int syncMode; + ZotacV2GPUZone zoneConfig; + + // We don't support anything other than synchronized mode, so read the last + // config used in synchronized mode for idle settings. + int zoneNum = FindSynchronizedZoneNum(ZOTAC_V2_GPU_SYNC_SYNCHRONIZED); + if(!controller->GetMode(zoneNum, ZOTAC_V2_GPU_CONFIG_IDLE, syncMode, zoneConfig, on)) + { + return; + } + + for(unsigned int i = 0; i < modes.size(); ++i) + { + if(zoneConfig.mode != modes[i].value) + { + continue; + } + + if(zoneConfig.mode == ZOTAC_V2_GPU_MODE_RAINBOW || + zoneConfig.mode == ZOTAC_V2_GPU_MODE_MARQUEE || + zoneConfig.mode == ZOTAC_V2_GPU_MODE_DRIP || + zoneConfig.mode == ZOTAC_V2_GPU_MODE_DUET) + { + if((zoneConfig.circuit == ZOTAC_V2_GPU_CIRCUIT_ON) != (modes[i].name.find("(circuit)") != std::string::npos)) + { + continue; + } + } + + active_mode = i; + } + + colors[0] = zoneConfig.color1; + if(modes[active_mode].colors.size() >= 1) + { + modes[active_mode].colors[0] = zoneConfig.color1; + } + if(modes[active_mode].colors.size() >= 2) + { + modes[active_mode].colors[1] = zoneConfig.color2; + } + modes[active_mode].speed = zoneConfig.speed; + modes[active_mode].brightness = zoneConfig.brightness; + modes[active_mode].direction = zoneConfig.direction; + + SignalUpdate(); +} + +void RGBController_ZotacV2GPU::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_ZotacV2GPU::DeviceUpdateLEDs() +{ + DeviceUpdateMode(); +} + +void RGBController_ZotacV2GPU::UpdateZoneLEDs(int /*zone*/) +{ + DeviceUpdateMode(); +} + +void RGBController_ZotacV2GPU::UpdateSingleLED(int /*led*/) +{ + DeviceUpdateMode(); +} + +void RGBController_ZotacV2GPU::DeviceUpdateMode() +{ + ZotacV2GPUZone zoneConfig; + zoneConfig.mode = modes[active_mode].value; + + zoneConfig.color1 = modes[active_mode].colors.size() >= 1 ? modes[active_mode].colors[0] : ToRGBColor(0, 0, 0); + zoneConfig.color2 = modes[active_mode].colors.size() >= 2 ? modes[active_mode].colors[1] : ToRGBColor(0, 0, 0); + + // This is probably not strictly neccessary + zoneConfig.colorPreset = 0; + if(zoneConfig.mode == ZOTAC_V2_GPU_MODE_DUET) + { + zoneConfig.colorPreset = ZOTAC_V2_GPU_DUET_PRESETS.size(); // custom + for(int i = 0; i < ZOTAC_V2_GPU_DUET_PRESETS.size(); ++i) + { + if(zoneConfig.color1 == ZOTAC_V2_GPU_DUET_PRESETS[i].first && + zoneConfig.color2 == ZOTAC_V2_GPU_DUET_PRESETS[i].second) + { + zoneConfig.colorPreset = i; + } + } + } + + zoneConfig.speed = modes[active_mode].speed; + zoneConfig.brightness = modes[active_mode].brightness; + zoneConfig.direction = modes[active_mode].direction == MODE_DIRECTION_RIGHT ? ZOTAC_V2_GPU_DIR_RIGHT : ZOTAC_V2_GPU_DIR_LEFT; + + if(zoneConfig.mode == ZOTAC_V2_GPU_MODE_RAINBOW || + zoneConfig.mode == ZOTAC_V2_GPU_MODE_MARQUEE || + zoneConfig.mode == ZOTAC_V2_GPU_MODE_DRIP || + zoneConfig.mode == ZOTAC_V2_GPU_MODE_DUET) + { + zoneConfig.circuit = modes[active_mode].name.find("(circuit)") != std::string::npos ? ZOTAC_V2_GPU_CIRCUIT_ON : ZOTAC_V2_GPU_CIRCUIT_OFF; + } + else + { + zoneConfig.circuit = 0; + } + + int zoneNum = FindSynchronizedZoneNum(ZOTAC_V2_GPU_SYNC_SYNCHRONIZED); + controller->TurnOnOff(true); + controller->SetMode(zoneNum, ZOTAC_V2_GPU_CONFIG_IDLE, ZOTAC_V2_GPU_SYNC_SYNCHRONIZED, zoneConfig); + controller->SetMode(zoneNum, ZOTAC_V2_GPU_CONFIG_ACTIVE, ZOTAC_V2_GPU_SYNC_SYNCHRONIZED, zoneConfig); +} + +int RGBController_ZotacV2GPU::FindSynchronizedZoneNum(int syncMode) +{ + // Figure out the index of the zone used for ZOTAC_V2_GPU_SYNC_SYNCHRONIZED + // or ZOTAC_V2_GPU_SYNC_SYNCHRONIZED_WITH_EXTERNAL settings based on the GPU + // zone config + + int lastRealZone = config.numberOfZones - 1; + if(config.supportsExternalLEDStrip) + { + lastRealZone += 1; + } + + if(syncMode == ZOTAC_V2_GPU_SYNC_SYNCHRONIZED) + { + return lastRealZone + 1; + } + else if(syncMode == ZOTAC_V2_GPU_SYNC_SYNCHRONIZED_WITH_EXTERNAL) + { + assert(config.supportsExternalLEDStrip); + return lastRealZone + 2; + } + else + { + assert(false); + return 0; + } +} diff --git a/Controllers/ZotacV2GPUController/RGBController_ZotacV2GPU.h b/Controllers/ZotacV2GPUController/RGBController_ZotacV2GPU.h new file mode 100644 index 000000000..31d3304a0 --- /dev/null +++ b/Controllers/ZotacV2GPUController/RGBController_ZotacV2GPU.h @@ -0,0 +1,38 @@ +/*-----------------------------------------*\ +| RGBController_ZotacV2GPU.h | +| | +| Generic RGB Interface for OpenRGB | +| ZOTAC RTX 30/40 series GPU Driver | +| | +| Krzysztof Haładyn (krzys_h) 3/16/2023 | +\*-----------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "ZotacV2GPUController.h" + +class RGBController_ZotacV2GPU : public RGBController +{ +public: + RGBController_ZotacV2GPU(ZotacV2GPUController* controller_ptr); + ~RGBController_ZotacV2GPU(); + + void SetupInitialValues(); + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + + ZotacV2GPUConfig config; + +private: + ZotacV2GPUController* controller; + + int FindSynchronizedZoneNum(int syncMode); +}; diff --git a/Controllers/ZotacV2GPUController/ZotacV2GPUController.cpp b/Controllers/ZotacV2GPUController/ZotacV2GPUController.cpp new file mode 100644 index 000000000..5f1335d11 --- /dev/null +++ b/Controllers/ZotacV2GPUController/ZotacV2GPUController.cpp @@ -0,0 +1,204 @@ +/*------------------------------------------*\ +| ZotacV2GPUController.cpp | +| | +| Driver for ZOTAC GeForce RTX 30/40 series | +| GPU lighting controller | +| | +| Krzysztof Haładyn (krzys_h) 3/16/2023 | +\*------------------------------------------*/ + +#include "ZotacV2GPUController.h" +#include "LogManager.h" + +ZotacV2GPUController::ZotacV2GPUController(i2c_smbus_interface* bus, u8 dev) +{ + this->bus = bus; + this->dev = dev; + + if(dev) + { + ReadVersion(); + } +} + +ZotacV2GPUController::~ZotacV2GPUController() +{ +} + +std::string ZotacV2GPUController::GetDeviceLocation() +{ + std::string return_string(bus->device_name); + char addr[5]; + snprintf(addr, 5, "0x%02X", dev); + return_string.append(", address "); + return_string.append(addr); + return ("I2C: " + return_string); +} + +std::string ZotacV2GPUController::GetVersion() +{ + return version; +} + +bool ZotacV2GPUController::ReadVersion() +{ + u8 data_pkt[] = { ZOTAC_V2_GPU_REG_RGB, 0xF1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + if(bus->i2c_write_block(dev, sizeof(data_pkt), data_pkt) < 0) + { + return false; + } + + u8 rdata_pkt[I2C_SMBUS_BLOCK_MAX] = { 0x00 }; + int rdata_len = sizeof(rdata_pkt); + if(bus->i2c_read_block(dev, &rdata_len, rdata_pkt) < 0) + { + return false; + } + + version = std::string((char*)rdata_pkt); + return true; +} + +bool ZotacV2GPUController::TurnOnOff(bool on) +{ + return SendCommand(on, false, 0, 0, 0, ZotacV2GPUZone()); +} + +bool ZotacV2GPUController::ResetToDefaults() +{ + return SendCommand(true, true, 0, 0, 0, ZotacV2GPUZone()); +} + +bool ZotacV2GPUController::SetMode(int zone, int idleActive, int syncMode, ZotacV2GPUZone zoneConfig) +{ + // NOTE: This only works if the device is in the ON state. Otherwise, the SetMode command will behave + // like TurnOnOff(true), and the change will be ignored. + + // NOTE: syncMode is per idleActive, NOT per (zone, idleActive) pair like zoneConfig is - as in, + // you can have ACTIVE in INDIVIDUAL mode and IDLE in SYNCHRONIZED mode, but you can't have + // different syncModes in different zones. The last written value is always applied, so make + // sure you don't change it accidentally between writes. + // TODO: Verify what I said above - it doesn't match the GUI, but it seems to match the NvAPISpy traces + // From the GUI it seems like syncMode should be global, period. + + return SendCommand(true, false, zone, idleActive, syncMode, zoneConfig); +} + +bool ZotacV2GPUController::GetMode(int zone, int idleActive, int& syncMode, ZotacV2GPUZone& zoneConfig, bool& on) +{ + u8 data_pkt[] = + { + ZOTAC_V2_GPU_REG_RGB, + 0xF0, + 0x00, + 0x00, + 0x00, + (u8)idleActive, + (u8)zone, + 0x00, + }; + + if(bus->i2c_write_block(dev, sizeof(data_pkt), data_pkt) < 0) + { + return false; + } + + + u8 rdata_pkt[I2C_SMBUS_BLOCK_MAX] = { 0x00 }; + int rdata_len = sizeof(rdata_pkt); + if(bus->i2c_read_block(dev, &rdata_len, rdata_pkt) < 0) + { + return false; + } + + bool readReset; + int readZone; + int readIdleActive; + if(!ParseCommand(on, readReset, readZone, readIdleActive, syncMode, zoneConfig)) + { + return false; + } + + if(readReset != 0) + { + LOG_WARNING("Reset byte was not 0?!"); + } + + if(readZone != zone || readIdleActive != idleActive) + { + LOG_WARNING("Got unexpected data - expected to recieve data for (%d, %d) but got for (%d, %d)", zone, idleActive, readZone, readIdleActive); + return false; + } + + return true; +} + +bool ZotacV2GPUController::SendCommand(bool on, bool reset, int zone, int idleActive, int syncMode, ZotacV2GPUZone zoneConfig) +{ + u8 data_pkt[] = + { + ZOTAC_V2_GPU_REG_RGB, + on ? (u8)0x01 : (u8)0x00, + reset ? (u8)0x01 : (u8)0x00, + 0x00, + 0x00, + (u8)idleActive, + (u8)zone, + (u8)zoneConfig.mode, + (u8)RGBGetRValue(zoneConfig.color1), + (u8)RGBGetGValue(zoneConfig.color1), + (u8)RGBGetBValue(zoneConfig.color1), + (u8)zoneConfig.speed, + (u8)zoneConfig.brightness, + (u8)zoneConfig.direction, + 0x00, + (u8)syncMode, + (u8)zoneConfig.circuit, + (u8)RGBGetRValue(zoneConfig.color2), + (u8)RGBGetGValue(zoneConfig.color2), + (u8)RGBGetBValue(zoneConfig.color2), + (u8)zoneConfig.colorPreset, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + }; + + if(bus->i2c_write_block(dev, sizeof(data_pkt), data_pkt) < 0) + { + return false; + } + return true; +} + +bool ZotacV2GPUController::ParseCommand(bool& on, bool& reset, int& zone, int& idleActive, int& syncMode, ZotacV2GPUZone& zoneConfig) +{ + u8 rdata_pkt[I2C_SMBUS_BLOCK_MAX] = { 0x00 }; + int rdata_len = sizeof(rdata_pkt); + + if(bus->i2c_read_block(dev, &rdata_len, rdata_pkt) < 0) + { + return false; + } + + on = rdata_pkt[0] != 0x00; + reset = rdata_pkt[1] != 0x00; + idleActive = rdata_pkt[4]; + zone = rdata_pkt[5]; + zoneConfig.mode = rdata_pkt[6]; + zoneConfig.color1 = ToRGBColor(rdata_pkt[7], rdata_pkt[8], rdata_pkt[9]); + zoneConfig.speed = rdata_pkt[10]; + zoneConfig.brightness = rdata_pkt[11]; + zoneConfig.direction = rdata_pkt[12]; + syncMode = rdata_pkt[14]; + zoneConfig.circuit = rdata_pkt[15]; + zoneConfig.color2 = ToRGBColor(rdata_pkt[16], rdata_pkt[17], rdata_pkt[18]); + zoneConfig.colorPreset = rdata_pkt[19]; + return true; +} diff --git a/Controllers/ZotacV2GPUController/ZotacV2GPUController.h b/Controllers/ZotacV2GPUController/ZotacV2GPUController.h new file mode 100644 index 000000000..bdd0cb076 --- /dev/null +++ b/Controllers/ZotacV2GPUController/ZotacV2GPUController.h @@ -0,0 +1,106 @@ +/*-----------------------------------------*\ +| ZotacV2GPUController.cpp | +| | +| Definitions and types for ZOTAC GeForce | +| RTX 30/40 series GPU lighting controller | +| | +| Krzysztof Haładyn (krzys_h) 3/16/2023 | +\*-----------------------------------------*/ + +#include +#include "i2c_smbus.h" +#include "RGBController.h" + +#pragma once + +enum +{ + ZOTAC_V2_GPU_REG_RGB = 0xA0, +}; + +enum +{ + ZOTAC_V2_GPU_CONFIG_IDLE = 0x00, // Config for when there is no load + ZOTAC_V2_GPU_CONFIG_ACTIVE = 0x01, // Config for when GPU is under load +}; + +enum +{ + ZOTAC_V2_GPU_SYNC_INDIVIDUAL = 0x00, // Everything separated + ZOTAC_V2_GPU_SYNC_SYNCHRONIZED = 0x01, // All internal zones synchronized, external is separated + ZOTAC_V2_GPU_SYNC_SYNCHRONIZED_WITH_EXTERNAL = 0x02, // Everything synchronized +}; + +enum +{ + ZOTAC_V2_GPU_MODE_STATIC = 0x00, // Basic static color + ZOTAC_V2_GPU_MODE_BREATH = 0x01, // Single color fades on and off + ZOTAC_V2_GPU_MODE_FADE = 0x02, // All colors fade through the spectrum + ZOTAC_V2_GPU_MODE_WINK = 0x03, // Single color flashes on and off + ZOTAC_V2_GPU_MODE_FLASH = 0x04, // Each zone flashes a different color (only supported in SYNCHRONIZED or SYNCHRONIZED_WITH_EXTERNAL mode) + ZOTAC_V2_GPU_MODE_SHINE = 0x05, // (Sound activated) Single color, on and off + ZOTAC_V2_GPU_MODE_RANDOM = 0x06, // Single color, random patern + ZOTAC_V2_GPU_MODE_SLIDE = 0x07, // Single color, moves one side to the other + ZOTAC_V2_GPU_MODE_RAINBOW = 0x08, // All colors move one side to the other + ZOTAC_V2_GPU_MODE_MARQUEE = 0x09, // Very similar to SLIDE effect + ZOTAC_V2_GPU_MODE_DRIP = 0x0A, // Similar to SLIDE as well, less color moves + ZOTAC_V2_GPU_MODE_DANCE = 0x0B, // (Sound activated) Single color, equalizer effect + ZOTAC_V2_GPU_MODE_DUET = 0x17, // Dual colors + ZOTAC_V2_GPU_MODE_PATH = 0x18, // Very similar to RAINBOW effect +}; + +enum +{ + ZOTAC_V2_GPU_DIR_LEFT = 0x00, + ZOTAC_V2_GPU_DIR_RIGHT = 0x01, +}; + +enum +{ + ZOTAC_V2_GPU_CIRCUIT_ON = 0x00, + ZOTAC_V2_GPU_CIRCUIT_OFF = 0x01, +}; + +struct ZotacV2GPUConfig +{ + int numberOfZones = 0; + bool supportsExternalLEDStrip = false; +}; + +struct ZotacV2GPUZone +{ + int mode = 0; + RGBColor color1 = ToRGBColor(0, 0, 0); + RGBColor color2 = ToRGBColor(0, 0, 0); + unsigned int colorPreset = 0; + unsigned int speed = 0; + unsigned int brightness = 0; + unsigned int direction = 0; + unsigned int circuit = 0; +}; + + +class ZotacV2GPUController +{ +public: + ZotacV2GPUController(i2c_smbus_interface* bus, u8 dev); + ~ZotacV2GPUController(); + + std::string GetDeviceLocation(); + std::string GetVersion(); + + bool TurnOnOff(bool on); + bool ResetToDefaults(); + bool GetMode(int zone, int idleActive, int& syncMode, ZotacV2GPUZone& zoneConfig, bool& on); + bool SetMode(int zone, int idleActive, int syncMode, ZotacV2GPUZone zoneConfig); + +private: + bool ReadVersion(); + bool SendCommand(bool on, bool reset, int zone, int idleActive, int syncMode, ZotacV2GPUZone zoneConfig); + bool ParseCommand(bool& on, bool& reset, int& zone, int& idleActive, int& syncMode, ZotacV2GPUZone& zoneConfig); + + i2c_smbus_interface* bus; + u8 dev; + std::string version; + +}; diff --git a/Controllers/ZotacV2GPUController/ZotacV2GPUControllerDetect.cpp b/Controllers/ZotacV2GPUController/ZotacV2GPUControllerDetect.cpp new file mode 100644 index 000000000..5a62ca166 --- /dev/null +++ b/Controllers/ZotacV2GPUController/ZotacV2GPUControllerDetect.cpp @@ -0,0 +1,36 @@ +#include "Detector.h" +#include "ZotacV2GPUController.h" +#include "RGBController.h" +#include "RGBController_ZotacV2GPU.h" +#include "i2c_smbus.h" +#include "pci_ids.h" + +/******************************************************************************************\ +* * +* DetectZotacV2GPUControllers * +* * +* Detect ZOTAC 30/40 series RGB controllers on the enumerated I2C busses * +* at address 0x49. * +* * +* bus - pointer to i2c_smbus_interface where RGB device is connected * +* dev - I2C address of RGB device * +* * +\******************************************************************************************/ + +void DetectZotacV2GPUControllers(i2c_smbus_interface* bus, u8 i2c_addr, const std::string& name) +{ + ZotacV2GPUController* controller = new ZotacV2GPUController(bus, i2c_addr); + RGBController_ZotacV2GPU* rgb_controller = new RGBController_ZotacV2GPU(controller); + rgb_controller->name = name; + + if(rgb_controller->config.numberOfZones > 0) + { + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +REGISTER_I2C_PCI_DETECTOR("ZOTAC GAMING GeForce RTX 3070 Ti Trinity OC", DetectZotacV2GPUControllers, NVIDIA_VEN, NVIDIA_RTX3070TI_DEV, ZOTAC_SUB_VEN, ZOTAC_RTX3070TI_TRINITY_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("ZOTAC GAMING GeForce RTX 3080 Ti AMP Holo", DetectZotacV2GPUControllers, NVIDIA_VEN, NVIDIA_RTX3080TI_DEV, ZOTAC_SUB_VEN, ZOTAC_RTX3080TI_AMP_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("ZOTAC GAMING GeForce RTX 3090 AMP Extreme Holo", DetectZotacV2GPUControllers, NVIDIA_VEN, NVIDIA_RTX3090_DEV, ZOTAC_SUB_VEN, ZOTAC_RTX3090_AMP_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("ZOTAC GAMING GeForce RTX 4090 Trinity OC", DetectZotacV2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, ZOTAC_SUB_VEN, ZOTAC_RTX4090_TRINITY_SUB_DEV, 0x49); +REGISTER_I2C_PCI_DETECTOR("ZOTAC GAMING GeForce RTX 4090 AMP Extreme AIRO", DetectZotacV2GPUControllers, NVIDIA_VEN, NVIDIA_RTX4090_DEV, ZOTAC_SUB_VEN, ZOTAC_RTX4090_AMP_SUB_DEV, 0x49); diff --git a/OpenRGB.pro b/OpenRGB.pro index 06f9d38ba..cec6cce40 100644 --- a/OpenRGB.pro +++ b/OpenRGB.pro @@ -189,6 +189,7 @@ INCLUDEPATH += Controllers/YeelightController/ \ Controllers/ZalmanZSyncController/ \ Controllers/ZotacTuringGPUController/ \ + Controllers/ZotacV2GPUController/ \ KeyboardLayoutManager/ \ RGBController/ \ qt/ @@ -734,6 +735,8 @@ HEADERS += Controllers/ZETKeyboardController/RGBController_ZETBladeOptical.h \ Controllers/ZotacTuringGPUController/ZotacTuringGPUController.h \ Controllers/ZotacTuringGPUController/RGBController_ZotacTuringGPU.h \ + Controllers/ZotacV2GPUController/ZotacV2GPUController.h \ + Controllers/ZotacV2GPUController/RGBController_ZotacV2GPU.h \ KeyboardLayoutManager/KeyboardLayoutManager.h \ RGBController/RGBController.h \ RGBController/RGBController_Dummy.h \ @@ -1431,6 +1434,9 @@ SOURCES += Controllers/ZotacTuringGPUController/ZotacTuringGPUController.cpp \ Controllers/ZotacTuringGPUController/ZotacTuringGPUControllerDetect.cpp \ Controllers/ZotacTuringGPUController/RGBController_ZotacTuringGPU.cpp \ + Controllers/ZotacV2GPUController/ZotacV2GPUController.cpp \ + Controllers/ZotacV2GPUController/ZotacV2GPUControllerDetect.cpp \ + Controllers/ZotacV2GPUController/RGBController_ZotacV2GPU.cpp \ KeyboardLayoutManager/KeyboardLayoutManager.cpp \ RGBController/RGBController.cpp \ RGBController/RGBController_Dummy.cpp \ diff --git a/pci_ids/pci_ids.h b/pci_ids/pci_ids.h index e93a117df..7aef875ef 100644 --- a/pci_ids/pci_ids.h +++ b/pci_ids/pci_ids.h @@ -628,6 +628,11 @@ #define ZOTAC_RTX2060S_AMP_SUB_DEV 0x5511 #define ZOTAC_RTX2070S_GAMING_SUB_DEV 0x7500 #define ZOTAC_RTX2080_AMP_SUB_DEV 0x3500 +#define ZOTAC_RTX3070TI_TRINITY_SUB_DEV 0x1653 +#define ZOTAC_RTX3080TI_AMP_SUB_DEV 0x2612 +#define ZOTAC_RTX3090_AMP_SUB_DEV 0x1619 +#define ZOTAC_RTX4090_TRINITY_SUB_DEV 0x3675 +#define ZOTAC_RTX4090_AMP_SUB_DEV 0x4675 /*-----------------------------------------------------*\ | Manli Sub-Device IDs |