Add ASRock RX 9070 XT Steel Legend RGB support

This commit is contained in:
Maylton Fernandes
2026-07-31 10:19:59 -05:00
committed by Adam Honse
parent 67a81f219d
commit 565f1dad74
5 changed files with 461 additions and 0 deletions
@@ -0,0 +1,199 @@
/*---------------------------------------------------------*\
| ASRockGPUSMBusController.cpp |
| |
| Driver for ASRock GPU RGB controllers |
| |
| Based on SignalRGB ASRock GPU.js plugin |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "ASRockGPUSMBusController.h"
#include "LogManager.h"
#include <chrono>
#include <cstdio>
#include <cstring>
#include <thread>
using namespace std::chrono_literals;
ASRockGPUSMBusController::ASRockGPUSMBusController(i2c_smbus_interface* bus, asrock_gpu_dev_id dev, std::string name)
{
this->bus = bus;
this->dev = dev;
this->device_name = name;
this->num_channels = 0;
memset(enabled_channels, 0, sizeof(enabled_channels));
memset(led_counts, 0, sizeof(led_counts));
}
ASRockGPUSMBusController::~ASRockGPUSMBusController()
{
}
std::string ASRockGPUSMBusController::GetDeviceLocation()
{
std::string return_string(bus->info.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 ASRockGPUSMBusController::GetDeviceName()
{
return(device_name);
}
bool ASRockGPUSMBusController::WriteBlock(uint8_t command, uint8_t subcommand, const uint8_t* data, uint8_t len)
{
/*-----------------------------------------------------*\
| SignalRGB packet format |
| [command, 0, subcommand, data...] |
| Uses pure I2C rather than SMBus |
\*-----------------------------------------------------*/
uint8_t packet[12];
memset(packet, 0, sizeof(packet));
packet[0] = command;
packet[1] = 0x00;
packet[2] = subcommand;
if(data != nullptr && len > 0)
{
uint8_t copy_len = (len > 9) ? 9 : len;
memcpy(&packet[3], data, copy_len);
}
int size = 12;
int result = bus->i2c_write_block(dev, size, packet);
if(result < 0)
{
LOG_DEBUG("[ASRock GPU] Write block failed: %d", result);
return(false);
}
return(true);
}
bool ASRockGPUSMBusController::ReadBlock(uint8_t* response, uint8_t len)
{
memset(response, 0, len);
int size = len;
int result = bus->i2c_read_block(dev, &size, response);
if(result < 0)
{
LOG_DEBUG("[ASRock GPU] Read block failed: %d", result);
return(false);
}
return(true);
}
bool ASRockGPUSMBusController::DetectChannels()
{
/*-----------------------------------------------------*\
| Get enabled channels |
| Command 0x14, subcommand 0x01 |
\*-----------------------------------------------------*/
if(!WriteBlock(0x14, 0x01, nullptr, 0))
{
LOG_WARNING("[ASRock GPU] Failed to request enabled channels");
return(false);
}
std::this_thread::sleep_for(20ms);
uint8_t response[I2C_SMBUS_BLOCK_MAX];
if(!ReadBlock(response, sizeof(response)))
{
LOG_WARNING("[ASRock GPU] Failed to read enabled channels");
return(false);
}
LOG_TRACE("[ASRock GPU] Enabled channels response: %02X %02X %02X %02X %02X %02X", response[0], response[1], response[2], response[3], response[4], response[5]);
/*-----------------------------------------------------*\
| Response format |
| Bytes 4-5 contain the channel bitmap |
\*-----------------------------------------------------*/
uint16_t channel_bits = response[4] | (response[5] << 8);
LOG_TRACE("[ASRock GPU] Channel bits: 0x%04X", channel_bits);
/*-----------------------------------------------------*\
| Get LED counts |
| Command 0x14, subcommand 0x02 |
\*-----------------------------------------------------*/
if(!WriteBlock(0x14, 0x02, nullptr, 0))
{
LOG_WARNING("[ASRock GPU] Failed to request LED counts");
return(false);
}
std::this_thread::sleep_for(20ms);
if(!ReadBlock(response, sizeof(response)))
{
LOG_WARNING("[ASRock GPU] Failed to read LED counts");
return(false);
}
num_channels = 0;
for(int i = 0; i < 16; i++)
{
if(channel_bits & (1 << i))
{
enabled_channels[num_channels] = i;
led_counts[num_channels] = response[4 + i];
const char* name = "Unknown";
if(i == ASROCK_GPU_CHANNEL_ARGB_HEADER)
{
name = "ARGB Header";
}
else if(i == ASROCK_GPU_CHANNEL_TOP_SIDE)
{
name = "Top Side";
}
else if(i == ASROCK_GPU_CHANNEL_FAN)
{
name = "Fan";
}
LOG_DEBUG("[ASRock GPU] Channel %d (%s) active with %d LEDs", i, name, response[4 + i]);
num_channels++;
}
}
LOG_DEBUG("[ASRock GPU] Found %d active channels", num_channels);
return(num_channels > 0);
}
void ASRockGPUSMBusController::SetChannelColor(uint8_t channel_idx, uint8_t red, uint8_t green, uint8_t blue)
{
/*-----------------------------------------------------*\
| SignalRGB color packet |
| Command 0x10, subcommand is the channel index |
\*-----------------------------------------------------*/
uint8_t data[8] =
{
0x01, // Mode: direct/static
red,
green,
blue,
0x80, // Brightness
0xFF, // Effect speed
0x00, // Effect direction
0x1A // Unknown constant
};
if(!WriteBlock(0x10, channel_idx, data, sizeof(data)))
{
LOG_WARNING("[ASRock GPU] Failed to set color for channel %d", channel_idx);
}
}
@@ -0,0 +1,55 @@
/*---------------------------------------------------------*\
| ASRockGPUSMBusController.h |
| |
| Driver for ASRock GPU RGB controllers |
| |
| Based on SignalRGB ASRock GPU.js plugin |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include <string>
#include "i2c_smbus.h"
typedef uint8_t asrock_gpu_dev_id;
#define ASROCK_GPU_CONTROLLER_NAME "ASRock GPU SMBus Controller"
#define ASROCK_GPU_SMBUS_ADDRESS 0x36
/*-----------------------------------------------------*\
| ASRock GPU Channel Names (from SignalRGB) |
\*-----------------------------------------------------*/
enum
{
ASROCK_GPU_CHANNEL_ARGB_HEADER = 3,
ASROCK_GPU_CHANNEL_TOP_SIDE = 6,
ASROCK_GPU_CHANNEL_FAN = 7,
};
class ASRockGPUSMBusController
{
public:
ASRockGPUSMBusController(i2c_smbus_interface* bus, asrock_gpu_dev_id dev, std::string name);
~ASRockGPUSMBusController();
std::string GetDeviceLocation();
std::string GetDeviceName();
void SetChannelColor(uint8_t channel_idx, uint8_t red, uint8_t green, uint8_t blue);
bool DetectChannels();
uint8_t enabled_channels[16];
uint8_t led_counts[16];
uint8_t num_channels;
private:
i2c_smbus_interface* bus;
asrock_gpu_dev_id dev;
std::string device_name;
bool WriteBlock(uint8_t command, uint8_t subcommand, const uint8_t* data, uint8_t len);
bool ReadBlock(uint8_t* response, uint8_t len);
};
@@ -0,0 +1,46 @@
/*---------------------------------------------------------*\
| ASRockGPUSMBusControllerDetect.cpp |
| |
| Detector for ASRock GPU RGB controllers |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "DetectionManager.h"
#include "ASRockGPUSMBusController.h"
#include "LogManager.h"
#include "RGBController_ASRockGPUSMBus.h"
#include "i2c_smbus.h"
#include "pci_ids.h"
DetectedControllers DetectASRockGPUSMBusControllers(i2c_smbus_interface* bus, uint8_t address, const std::string& name)
{
DetectedControllers detected_controllers;
LOG_DEBUG("[ASRock GPU] Checking GPU I2C bus for RGB controller at 0x%02X", address);
if(bus->i2c_smbus_write_quick(address, I2C_SMBUS_WRITE) < 0)
{
LOG_DEBUG("[ASRock GPU] No response at address 0x%02X", address);
return(detected_controllers);
}
LOG_DEBUG("[ASRock GPU] Device responded at 0x%02X, creating controller", address);
ASRockGPUSMBusController* controller = new ASRockGPUSMBusController(bus, address, name);
if(controller->DetectChannels())
{
detected_controllers.push_back(new RGBController_ASRockGPUSMBus(controller));
}
else
{
LOG_WARNING("[ASRock GPU] No active channels found, device not registered");
delete controller;
}
return(detected_controllers);
}
REGISTER_I2C_PCI_DETECTOR("ASRock Radeon RX 9070 XT Steel Legend", DetectASRockGPUSMBusControllers, AMD_GPU_VEN, AMD_NAVI48_DEV, ASROCK_SUB_VEN, 0x5403, ASROCK_GPU_SMBUS_ADDRESS);
@@ -0,0 +1,127 @@
/*---------------------------------------------------------*\
| RGBController_ASRockGPUSMBus.cpp |
| |
| RGBController for ASRock GPU RGB controllers |
| |
| Based on SignalRGB ASRock GPU.js plugin |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include "RGBController_ASRockGPUSMBus.h"
RGBController_ASRockGPUSMBus::RGBController_ASRockGPUSMBus(ASRockGPUSMBusController* controller_ptr)
{
controller = controller_ptr;
name = controller->GetDeviceName();
vendor = "ASRock";
type = DEVICE_TYPE_GPU;
description = "ASRock GPU RGB Controller";
location = controller->GetDeviceLocation();
mode Direct;
Direct.name = "Direct";
Direct.value = 0;
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR;
Direct.color_mode = MODE_COLORS_PER_LED;
modes.push_back(Direct);
SetupZones();
}
RGBController_ASRockGPUSMBus::~RGBController_ASRockGPUSMBus()
{
delete controller;
}
void RGBController_ASRockGPUSMBus::SetupZones()
{
/*-----------------------------------------------------*\
| Set up zones - one zone per channel |
\*-----------------------------------------------------*/
for(unsigned int i = 0; i < controller->num_channels; i++)
{
uint8_t channel_idx = controller->enabled_channels[i];
zone new_zone;
new_zone.type = ZONE_TYPE_SINGLE;
new_zone.leds_min = 1;
new_zone.leds_max = 1;
new_zone.leds_count = 1;
/*-----------------------------------------------------*\
| Channel names from SignalRGB |
\*-----------------------------------------------------*/
if(channel_idx == ASROCK_GPU_CHANNEL_ARGB_HEADER)
{
new_zone.name = "ARGB Header";
}
else if(channel_idx == ASROCK_GPU_CHANNEL_TOP_SIDE)
{
new_zone.name = "Top Side";
}
else if(channel_idx == ASROCK_GPU_CHANNEL_FAN)
{
new_zone.name = "Fan";
}
else
{
new_zone.name = "Channel " + std::to_string(channel_idx);
}
zones.push_back(new_zone);
/*-----------------------------------------------------*\
| Set up LED for this zone |
\*-----------------------------------------------------*/
led new_led;
new_led.name = new_zone.name;
leds.push_back(new_led);
}
SetupColors();
}
void RGBController_ASRockGPUSMBus::ResizeZone(int /*zone*/, int /*new_size*/)
{
/*-----------------------------------------------------*\
| This device does not support resizing zones |
\*-----------------------------------------------------*/
}
void RGBController_ASRockGPUSMBus::DeviceUpdateLEDs()
{
for(unsigned int i = 0; i < controller->num_channels; i++)
{
uint8_t channel_idx = controller->enabled_channels[i];
RGBColor color = colors[i];
controller->SetChannelColor(channel_idx,
RGBGetRValue(color),
RGBGetGValue(color),
RGBGetBValue(color));
}
}
void RGBController_ASRockGPUSMBus::UpdateZoneLEDs(int zone)
{
uint8_t channel_idx = controller->enabled_channels[zone];
RGBColor color = colors[zone];
controller->SetChannelColor(channel_idx,
RGBGetRValue(color),
RGBGetGValue(color),
RGBGetBValue(color));
}
void RGBController_ASRockGPUSMBus::UpdateSingleLED(int led)
{
UpdateZoneLEDs(led);
}
void RGBController_ASRockGPUSMBus::DeviceUpdateMode()
{
DeviceUpdateLEDs();
}
@@ -0,0 +1,34 @@
/*---------------------------------------------------------*\
| RGBController_ASRockGPUSMBus.h |
| |
| RGBController for ASRock GPU RGB controllers |
| |
| Based on SignalRGB ASRock GPU.js plugin |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#pragma once
#include "RGBController.h"
#include "ASRockGPUSMBusController.h"
class RGBController_ASRockGPUSMBus : public RGBController
{
public:
RGBController_ASRockGPUSMBus(ASRockGPUSMBusController* controller_ptr);
~RGBController_ASRockGPUSMBus();
void SetupZones();
void ResizeZone(int zone, int new_size);
void DeviceUpdateLEDs();
void UpdateZoneLEDs(int zone);
void UpdateSingleLED(int led);
void DeviceUpdateMode();
private:
ASRockGPUSMBusController* controller;
};