diff options
author | 2025-05-04 01:10:58 -0400 | |
---|---|---|
committer | 2025-05-04 01:12:00 -0400 | |
commit | 397df5f4c5cb8d3e05ddd1c0e8da2386f61386f5 (patch) | |
tree | b4d83b71fc79fdd53571dbbf3724b9adeb02399c /linux-legacy-oled-brightness.patch | |
download | linux-legacy-oled-brightness-397df5f4c5cb8d3e05ddd1c0e8da2386f61386f5.tar.xz |
Diffstat (limited to 'linux-legacy-oled-brightness.patch')
-rw-r--r-- | linux-legacy-oled-brightness.patch | 582 |
1 files changed, 582 insertions, 0 deletions
diff --git a/linux-legacy-oled-brightness.patch b/linux-legacy-oled-brightness.patch new file mode 100644 index 0000000..c060432 --- /dev/null +++ b/linux-legacy-oled-brightness.patch @@ -0,0 +1,582 @@ +diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile +index 3dda9f0eda82..ee413332a9ae 100644 +--- a/drivers/gpu/drm/i915/Makefile ++++ b/drivers/gpu/drm/i915/Makefile +@@ -328,6 +328,7 @@ i915-y += \ + display/intel_dp.o \ + display/intel_dp_aux.o \ + display/intel_dp_aux_backlight.o \ ++ display/intel_dp_legacy_oled_brightness.o \ + display/intel_dp_hdcp.o \ + display/intel_dp_link_training.o \ + display/intel_dp_mst.o \ +diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h +index 8271e50e3644..de512541628f 100644 +--- a/drivers/gpu/drm/i915/display/intel_display_types.h ++++ b/drivers/gpu/drm/i915/display/intel_display_types.h +@@ -50,6 +50,7 @@ + #include "intel_display_power.h" + #include "intel_dpll_mgr.h" + #include "intel_wm_types.h" ++#include "intel_dp_legacy_oled_brightness.h" + + struct cec_notifier; + struct drm_printer; +@@ -421,6 +422,9 @@ struct intel_panel { + bool supports_sdp_colorimetry; + bool supports_tone_mapping; + } intel_cap; ++ struct { ++ struct intel_legacy_panel_data panel_data; ++ } legacy; + } edp; + + struct backlight_device *device; +diff --git a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c +index c846ef4acf5b..8a9eae55b362 100644 +--- a/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c ++++ b/drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c +@@ -39,6 +39,7 @@ + #include "intel_display_core.h" + #include "intel_display_types.h" + #include "intel_dp.h" ++#include "intel_dp_legacy_oled_brightness.h" + #include "intel_dp_aux_backlight.h" + + /* +@@ -99,6 +100,7 @@ enum intel_dp_aux_backlight_modparam { + INTEL_DP_AUX_BACKLIGHT_ON = 1, + INTEL_DP_AUX_BACKLIGHT_FORCE_VESA = 2, + INTEL_DP_AUX_BACKLIGHT_FORCE_INTEL = 3, ++ INTEL_DP_AUX_BACKLIGHT_LEGACY = 99, + }; + + static bool is_intel_tcon_cap(const u8 tcon_cap[4]) +@@ -606,7 +608,7 @@ int intel_dp_aux_init_backlight_funcs(struct intel_connector *connector) + struct intel_display *display = to_intel_display(connector); + struct drm_device *dev = connector->base.dev; + struct intel_panel *panel = &connector->panel; +- bool try_intel_interface = false, try_vesa_interface = false; ++ bool try_intel_interface = false, try_vesa_interface = false, try_legacy_interface = false; + + /* Check the VBT and user's module parameters to figure out which + * interfaces to probe +@@ -638,6 +640,9 @@ int intel_dp_aux_init_backlight_funcs(struct intel_connector *connector) + case INTEL_DP_AUX_BACKLIGHT_FORCE_INTEL: + try_intel_interface = true; + break; ++ case INTEL_DP_AUX_BACKLIGHT_LEGACY: ++ try_legacy_interface = true; ++ break; + } + + /* +@@ -667,5 +672,10 @@ int intel_dp_aux_init_backlight_funcs(struct intel_connector *connector) + return 0; + } + ++ if (try_legacy_interface && intel_dp_aux_legacy_brightness_supported(connector)) { ++ panel->backlight.funcs = intel_dp_aux_legacy_get_bl_funcs(); ++ return 0; ++ } ++ + return -ENODEV; + } +diff --git a/drivers/gpu/drm/i915/display/intel_dp_legacy_oled_brightness.c b/drivers/gpu/drm/i915/display/intel_dp_legacy_oled_brightness.c +new file mode 100644 +index 000000000000..6959b34c621e +--- /dev/null ++++ b/drivers/gpu/drm/i915/display/intel_dp_legacy_oled_brightness.c +@@ -0,0 +1,446 @@ ++/* ++ * Copyright © 2025 Chris Xiong ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a ++ * copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without limitation ++ * the rights to use, copy, modify, merge, publish, distribute, sublicense, ++ * and/or sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice (including the next ++ * paragraph) shall be included in all copies or substantial portions of the ++ * Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS ++ * IN THE SOFTWARE. ++ * ++ */ ++ ++#include "intel_backlight.h" ++#include "intel_display_core.h" ++#include "intel_display_types.h" ++#include "intel_dp.h" ++#include "intel_dp_legacy_oled_brightness.h" ++#include "linux/firmware.h" ++#include "linux/slab.h" ++#include "linux/stdarg.h" ++ ++static bool intel_dp_aux_legacy_write_sequence(struct drm_dp_aux *aux, const u32 *address_seq, const u8 *value_seq, size_t length) { ++ drm_dbg_kms(aux->drm_dev, "intel_dp_aux_legacy_write_sequence: %*phN", (int)length, value_seq); ++ for (size_t p = 0; p < length; ++p) { ++ if (drm_dp_dpcd_writeb(aux, address_seq[p], value_seq[p]) < 1) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++static bool intel_dp_aux_legacy_write_sequence_parameterized(struct drm_dp_aux *aux, const u32 *address_seq, const u8 *value_seq, size_t length, size_t nparameters, ...) { ++ u8 *value_buf = NULL; ++ va_list args; ++ bool ret = false; ++ ++ if (!nparameters) ++ return intel_dp_aux_legacy_write_sequence(aux, address_seq, value_seq, length); ++ value_buf = kmalloc(length, GFP_KERNEL); ++ memcpy(value_buf, value_seq, length); ++ ++ va_start(args, nparameters); ++ for (size_t p = 0; p < nparameters; ++p) { ++ int pos = va_arg(args, int); ++ int val = va_arg(args, int); ++ if (pos >= 0 && pos < length) ++ value_buf[pos] = val; ++ } ++ va_end(args); ++ ret = intel_dp_aux_legacy_write_sequence(aux, address_seq, value_buf, length); ++ ++ kfree(value_buf); ++ return ret; ++} ++ ++static bool intel_dp_aux_legacy_handshake(struct intel_connector *connector) { ++ struct intel_display *display = to_intel_display(connector); ++ struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder); ++ struct drm_dp_aux *aux = &intel_dp->aux; ++ char *handshake_str = "PARAAUX-REG"; ++ u8 v; ++ int ret; ++ ++ ret = drm_dp_dpcd_readb(aux, 0x490, &v); ++ if (ret < 1) ++ drm_info(display->drm, "intel_dp_aux_legacy_handshake: read dpcd register 0x490 failed: %d", ret); ++ else { ++ if (v != 1) { ++ for (int retries = 0; retries < 20; ++retries) { ++ for (int p = 0; p < 11; ++p) { ++ ret = drm_dp_dpcd_writeb(aux, 0x490, (u8)handshake_str[p]); ++ if (ret < 1) break; ++ } ++ ret = drm_dp_dpcd_readb(aux, 0x490, &v); ++ if (ret == 1 && v == 1) ++ return true; ++ } ++ } else ++ return true; ++ } ++ drm_err(display->drm, "intel_dp_aux_legacy_handshake: handshake failed"); ++ return false; ++} ++ ++static void intel_dp_aux_legacy_get_elvss_max_offset(struct intel_connector *connector) { ++ struct intel_display *display = to_intel_display(connector); ++ struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder); ++ struct drm_dp_aux *aux = &intel_dp->aux; ++ struct intel_legacy_panel_data *panel_data = &connector->panel.backlight.edp.legacy.panel_data; ++ const u32 address_seq1[11] = {0x491, 0x492, 0x493, 0x491, 0x492, 0x493, 0x492, 0x493, 0x492, 0x493, 0x492}; ++ const u8 value_seq1[11] = { 0x02, 0x7f, 0x01, 0x07, 0xff, 0x04, 0x03, 0x04, 0x06, 0x26, 0x0a}; ++ if (intel_dp_aux_legacy_handshake(connector)) { ++ if (!intel_dp_aux_legacy_write_sequence(aux, address_seq1, value_seq1, 11)) { ++ drm_err(display->drm, "get_elvss_max_offset: write failed"); ++ return; ++ } ++ if (drm_dp_dpcd_readb(aux, 0x493, &panel_data->elvss_max_offset) < 1) { ++ drm_err(display->drm, "get_elvss_max_offset: read failed"); ++ return; ++ } ++ drm_dbg_kms(display->drm, "elvss_max_offset: %d", (int)panel_data->elvss_max_offset); ++ } else ++ drm_err(display->drm, "get_elvss_max_offset: handshake failed"); ++} ++ ++static bool intel_dp_aux_legacy_get_tcon_gamma(struct intel_connector *connector) { ++ struct intel_display *display = to_intel_display(connector); ++ struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder); ++ struct drm_dp_aux *aux = &intel_dp->aux; ++ struct intel_legacy_panel_data *panel_data = &connector->panel.backlight.edp.legacy.panel_data; ++ struct intel_legacy_panel_data_intermediate *interm = panel_data->intermediates; ++ ++ const u32 address_seq1[6] = {0x491, 0x492, 0x493, 0x491, 0x492, 0x493}; ++ const u8 value_seq1[6] = { 0x2, 0x7f, 0x1, 0x7, 0xff, 0x4}; ++ const u32 address_seq2[8] = {0x492, 0x493, 0x492, 0x493, 0x492, 0x493, 0x491, 0x492}; ++ const u8 value_seq2[8] = { 0x0, 0, 0x1, 0, 0x2, 0, 0x7, 0xc}; ++ ++ if (!intel_dp_aux_legacy_handshake(connector)) { ++ drm_err(display->drm, "get_tcon_gamma: handshake failed"); ++ return false; ++ } ++ if (!intel_dp_aux_legacy_write_sequence(aux, address_seq1, value_seq1, 6)) { ++ drm_err(display->drm, "get_tcon_gamma: write failed"); ++ return false; ++ } ++ if (!interm) { ++ drm_err(display->drm, "get_tcon_gamma: could not get hold of intermediate data"); ++ return false; ++ } ++ for (u16 i = 0; i < 0x20; ++i) { ++ u16 k = i * 0xb; ++ for (u16 j = 0; j <= 0x20; ++j) { ++ intel_dp_aux_legacy_write_sequence_parameterized(aux, address_seq2, value_seq2, 8, 3, ++ 1, (j < 0xb ? 0x2 : (j < 0x16 ? 0x4 : 0x8)), ++ 3, (u8)(k << 7), ++ 5, (u8)(k >> 1)); ++ if (i == 0 && j == 0) udelay(100); ++ drm_dp_dpcd_readb(aux, 0x493, &interm->tcon_gamma[i][j]); ++ if (++k >= (i + 1) * 0xb) ++ k = i * 0xb; ++ } ++ } ++ u16 *vp = &interm->gamma_values[0][0]; ++ u8 *gp = &interm->tcon_gamma[0][0]; ++ for (u16 i = 0; i < 0x20 ; ++i) { ++ for (u16 j = 0; j <= 0x20; ++j) { ++ switch (j) { ++ case 0x7: case 0x12: case 0x1d: ++ *vp = (u16)(*(gp + 2) & 0x80) * 2 | *gp; ++ break; ++ case 0x9: case 0x14: case 0x1f: ++ *vp = *gp & 0x7f; ++ break; ++ case 0xa: case 0x15: case 0x20: ++ *vp = *gp & 0xf; ++ break; ++ default: ++ *vp = *gp; ++ } ++ ++vp; ++ ++gp; ++ } ++ } ++ return true; ++} ++ ++static void intel_dp_aux_legacy_write_brightness(struct intel_connector *connector, u8 brightness) { ++ struct intel_display *display = to_intel_display(connector); ++ struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder); ++ struct drm_dp_aux *aux = &intel_dp->aux; ++ struct intel_legacy_panel_data *panel_data = &connector->panel.backlight.edp.legacy.panel_data; ++ u8 *brightness_gamma = panel_data->interpolated_gamma[100 - brightness]; ++ u32 *write_addr_buf = NULL; ++ u8 *write_val_buf = NULL; ++ const u32 address_seq1[7] = {0x491, 0x492, 0x493, 0x492, 0x493, 0x492, 0x493}; ++ const u8 value_seq1[7] = { 0x0f, 0xff, 0x00, 0x9d, 0, 0x9b, 0}; ++ const u32 address_seq2[7] = {0x491, 0x492, 0x493, 0x492, 0x493, 0x492, 0x493}; ++ const u8 value_seq2[7] = { 0x0f, 0xff, 0x02, 0x30, 0, 0x33, 0}; ++ const u32 address_seq3[7] = {0x491, 0x492, 0x493, 0x492, 0x493, 0x492, 0x493}; ++ const u8 value_seq3[7] = { 0x0e, 0x80, 0, 0x90, 0, 0x91, 0}; ++ const u32 address_seq4[6] = {0x491, 0x492, 0x493, 0x491, 0x492, 0x493}; ++ const u8 value_seq4[6] = { 0x02, 0x7f, 0x01, 0x07, 0xff, 0x04}; ++ const u32 address_seq5[14] = {0x492, 0x493, 0x491, 0x492, 0x493, 0x491, 0x492, 0x493, 0x492, 0x493, 0x491, 0x492, 0x493, 0x491}; ++ const u8 value_seq5[14] = { 0x32, 0x01, 0x02, 0xeb, 0x91, 0x09, 0xf0, 0xef, 0xf9, 0, 0x02, 0xeb, 0x96, 0x0e}; ++ ++ if (!intel_dp_aux_legacy_handshake(connector)) { ++ drm_err(display->drm, "write_brightness: handshake failed"); ++ return; ++ } ++ drm_dbg_kms(display->drm, "write_brightness %3d", brightness); ++ ++ if (!intel_dp_aux_legacy_write_sequence_parameterized(aux, address_seq1, value_seq1, 7, 2, ++ 4, panel_data->aor_buf[brightness * 2], //LSB of AOR[brightness] ++ 6, panel_data->aor_buf[brightness * 2 + 1])) //MSB of AOR[brightness] ++ goto fail; ++ ++ if (!intel_dp_aux_legacy_write_sequence_parameterized(aux, address_seq2, value_seq2, 7, 2, ++ 4, panel_data->delvss[brightness], ++ 6, panel_data->elvss_max_offset)) ++ goto fail; ++ ++ if (brightness > panel_data->brightness_for_acl) { ++ if (!intel_dp_aux_legacy_write_sequence_parameterized(aux, address_seq3, value_seq3, 7, 3, ++ 2, 0x90, ++ 4, panel_data->acl_cutoff_and_delta_buf[(brightness - panel_data->brightness_for_acl - 1) * 2], // LSB of ACLCutoffAndDelta[brightness - brightness_acl - 1] ++ 6, panel_data->acl_cutoff_and_delta_buf[(brightness - panel_data->brightness_for_acl - 1) * 2 + 1])) // MSB of ACLCutoffAndDelta[brightness - brightness_acl - 1] ++ goto fail; ++ } else { ++ if (!intel_dp_aux_legacy_write_sequence_parameterized(aux, address_seq3, value_seq3, 3, 1, ++ 2, 0x80)) ++ goto fail; ++ } ++ ++ if (!intel_dp_aux_legacy_write_sequence(aux, address_seq4, value_seq4, 6)) goto fail; ++ drm_dbg_kms(display->drm, "brightness_gamma @ %d: %*ph\n", 100 - brightness, 32, brightness_gamma); ++ write_addr_buf = kmalloc_array(0x21 * 2, 4, GFP_KERNEL); ++ write_val_buf = kmalloc_array(0x21 * 2, 1, GFP_KERNEL); ++ for (int i = 0; i <= 0x20; ++i) { ++ write_addr_buf[i * 2] = 0x492; ++ write_addr_buf[i * 2 + 1] = 0x493; ++ write_val_buf[i * 2] = i + 0x10; ++ if (drm_dp_dpcd_writeb(aux, 0x492, i + 0x10) < 1) goto fail; ++ if (i == 10 || i == 21 || i == 32) ++ write_val_buf[i * 2 + 1] = brightness_gamma[i] & 0xf; ++ else ++ write_val_buf[i * 2 + 1] = brightness_gamma[i]; ++ } ++ if (!intel_dp_aux_legacy_write_sequence(aux, write_addr_buf, write_val_buf, 0x21 * 2)) ++ goto fail; ++ udelay(1000); ++ if (!intel_dp_aux_legacy_write_sequence_parameterized(aux, address_seq5, value_seq5, 14, 1, ++ 9, panel_data->SP[brightness])) ++ goto fail; ++ for (int i = 0; i < 9; ++i) { ++ //reuse current data in write_addr_buf ++ //as well as values written to 0x492 ++ write_val_buf[i * 2 + 1] = panel_data->IRC[brightness][i]; ++ } ++ if (!intel_dp_aux_legacy_write_sequence(aux, write_addr_buf, write_val_buf, 9 * 2)) ++ goto fail; ++ kfree(write_addr_buf); ++ kfree(write_val_buf); ++ return; ++fail: ++ if (write_addr_buf) ++ kfree(write_addr_buf); ++ if (write_val_buf) ++ kfree(write_val_buf); ++ drm_err(display->drm, "write_brightness: failed to write dpcd registers"); ++} ++ ++inline s16 sign(s16 v) { return v < 0 ? -1 : 1; } ++ ++static void intel_dp_aux_legacy_calculate_interpolated_values(struct intel_connector *connector) { ++ struct intel_display *display = to_intel_display(connector); ++ struct intel_legacy_panel_data *panel_data = &connector->panel.backlight.edp.legacy.panel_data; ++ struct intel_legacy_panel_data_intermediate *interm = panel_data->intermediates; ++ ++ s16 dv; ++ u8 old_d; ++ u8 base, delta, multiplier, bias, attenuation; ++ for (int i = 0; i <= 0x20; ++i) { ++ old_d = 0xff; ++ for (int br = 0; br <= 100; ++br) { ++ base = interm->interpolation[br][0]; ++ delta = interm->interpolation[br][1]; ++ multiplier = interm->interpolation[br][2]; ++ bias = interm->interpolation[br][3]; ++ attenuation = interm->interpolation[br][4]; ++ if (old_d != delta) { ++ dv = interm->gamma_values[delta][i] - interm->gamma_values[delta + 1][i]; ++ old_d = delta; ++ } ++ if (unlikely(attenuation == 0)) attenuation = 1; ++ interm->interpolated_values[br][i] = interm->gamma_values[base][i] + sign(dv) * (abs(dv * multiplier) + bias) / attenuation; ++ } ++ } ++ ++ u8 *pint_gamma = &panel_data->interpolated_gamma[0][0]; ++ u16 *pint_values = &interm->interpolated_values[0][0]; ++ ++ for (int i = 0; i < 101; ++i) { ++ for (int j = 0; j <= 32; ++j) { ++ switch (j) { ++ case 9: case 20: case 31: ++ *pint_gamma = (((u8)*(pint_values - 2) >> 1 ^ (u8)*pint_values) & 0x7f) ^ (u8)(*(pint_values - 2) >> 1); ++ break; ++ case 10: case 21: case 32: ++ *pint_gamma = (u8)*pint_values & 0xf; ++ break; ++ default: ++ *pint_gamma = (u8)*pint_values; ++ } ++ ++pint_gamma; ++ ++pint_values; ++ } ++ } ++ for (int i = 0; i < 101; ++i) { ++ drm_dbg_kms(display->drm, "interpolated gamma %3d %*phN", i, 33, panel_data->interpolated_gamma[i]); ++ } ++} ++ ++static int intel_dp_aux_legacy_load_panel_properties(struct intel_connector *connector) ++{ ++ struct intel_display *display = to_intel_display(connector); ++ struct device *dev = connector->encoder->base.dev->dev; ++ struct intel_legacy_panel_data *panel_data = &connector->panel.backlight.edp.legacy.panel_data; ++ struct intel_legacy_panel_data_intermediate *interm = panel_data->intermediates; ++ int ret; ++ const struct firmware *fw; ++ const u8 *pdata; ++ u16 len; ++ ++ ret = request_firmware(&fw, "intel_legacy_panel_data.bin", dev); ++ if (ret < 0) ++ return ret; ++ ret = 0; ++ pdata = fw->data; ++ ++#define load_array(arr, maxlen) \ ++ if (fw->size - (pdata - fw->data) < 2) goto fail_invalid_data; \ ++ memcpy(&len, pdata, 2); \ ++ pdata += 2; \ ++ len = __le16_to_cpu(len); \ ++ if (len > maxlen) goto fail_invalid_data; \ ++ if (fw->size - (pdata - fw->data) < len) goto fail_invalid_data; \ ++ memcpy(arr, pdata, len); \ ++ pdata += len; ++ ++ load_array(panel_data->aor_buf, 202); ++ load_array(panel_data->delvss, 101); ++ ++ if (fw->size - (pdata - fw->data) < 2) goto fail_invalid_data; ++ memcpy(&panel_data->brightness_for_acl, pdata, 2); ++ pdata += 2; ++ panel_data->brightness_for_acl = __le16_to_cpu(panel_data->brightness_for_acl); ++ ++ load_array(panel_data->acl_cutoff_and_delta_buf, 200); ++ load_array(panel_data->IRC, 909); ++ load_array(panel_data->SP, 101); ++ load_array(interm->interpolation, 505); ++ ++ release_firmware(fw); ++ return ret; ++fail_invalid_data: ++ release_firmware(fw); ++ drm_err(display->drm, "invalid intel_legacy_panel_data.bin"); ++ return -EINVAL; ++} ++ ++static int intel_dp_aux_legacy_setup_backlight(struct intel_connector *connector, enum pipe unused) ++{ ++ struct intel_panel *panel = &connector->panel; ++ struct intel_legacy_panel_data *panel_data = &connector->panel.backlight.edp.legacy.panel_data; ++ int ret; ++ ++ panel_data->intermediates = kzalloc(sizeof(struct intel_legacy_panel_data_intermediate), GFP_KERNEL); ++ ret = intel_dp_aux_legacy_load_panel_properties(connector); ++ if (ret < 0) ++ goto fail; ++ panel->backlight.max = 100; ++ panel->backlight.min = 0; ++ panel->backlight.level = 100; ++ if (!intel_dp_aux_legacy_handshake(connector)) { ++ ret = -ENODEV; ++ goto fail; ++ } ++ intel_dp_aux_legacy_get_elvss_max_offset(connector); ++ if (!intel_dp_aux_legacy_get_tcon_gamma(connector)) { ++ ret = -ENODEV; ++ goto fail; ++ } ++ intel_dp_aux_legacy_calculate_interpolated_values(connector); ++ panel_data->values_initialized = true; ++ ret = 0; ++fail: ++ kfree(panel_data->intermediates); ++ panel_data->intermediates = NULL; ++ return ret; ++} ++static void intel_dp_aux_legacy_disable_backlight(const struct drm_connector_state *old_conn_state, ++ u32 level) ++{ ++ struct intel_connector *connector = to_intel_connector(old_conn_state->connector); ++ struct intel_legacy_panel_data *panel_data = &connector->panel.backlight.edp.legacy.panel_data; ++ if (!panel_data->values_initialized) ++ intel_dp_aux_legacy_setup_backlight(connector, PIPE_A); ++ intel_dp_aux_legacy_write_brightness(connector, 0); ++} ++static void ++intel_dp_aux_legacy_enable_backlight(const struct intel_crtc_state *crtc_state, ++ const struct drm_connector_state *conn_state, u32 level) ++{ ++ struct intel_connector *connector = to_intel_connector(conn_state->connector); ++ struct intel_panel *panel = &connector->panel; ++ struct intel_legacy_panel_data *panel_data = &panel->backlight.edp.legacy.panel_data; ++ if (level > 100) level = 100; ++ panel->backlight.level = level; ++ if (!panel_data->values_initialized) ++ intel_dp_aux_legacy_setup_backlight(connector, PIPE_A); ++ intel_dp_aux_legacy_write_brightness(connector, level); ++} ++static u32 intel_dp_aux_legacy_get_backlight(struct intel_connector *connector, enum pipe unused) ++{ ++ struct intel_panel *panel = &connector->panel; ++ return panel->backlight.level; ++} ++static void ++intel_dp_aux_legacy_set_backlight(const struct drm_connector_state *conn_state, u32 level) ++{ ++ struct intel_connector *connector = to_intel_connector(conn_state->connector); ++ struct intel_panel *panel = &connector->panel; ++ struct intel_legacy_panel_data *panel_data = &panel->backlight.edp.legacy.panel_data; ++ if (level > 100) level = 100; ++ panel->backlight.level = level; ++ if (!panel_data->values_initialized) ++ intel_dp_aux_legacy_setup_backlight(connector, PIPE_A); ++ intel_dp_aux_legacy_write_brightness(connector, level); ++} ++ ++static const struct intel_panel_bl_funcs intel_dp_aux_legacy_bl_funcs = { ++ .setup = intel_dp_aux_legacy_setup_backlight, ++ .enable = intel_dp_aux_legacy_enable_backlight, ++ .disable = intel_dp_aux_legacy_disable_backlight, ++ .set = intel_dp_aux_legacy_set_backlight, ++ .get = intel_dp_aux_legacy_get_backlight, ++}; ++ ++const struct intel_panel_bl_funcs* intel_dp_aux_legacy_get_bl_funcs(void) { ++ return &intel_dp_aux_legacy_bl_funcs; ++} ++bool intel_dp_aux_legacy_brightness_supported(struct intel_connector *connector) { ++ return intel_dp_aux_legacy_handshake(connector); ++} +diff --git a/drivers/gpu/drm/i915/display/intel_dp_legacy_oled_brightness.h b/drivers/gpu/drm/i915/display/intel_dp_legacy_oled_brightness.h +new file mode 100644 +index 000000000000..785509e608c8 +--- /dev/null ++++ b/drivers/gpu/drm/i915/display/intel_dp_legacy_oled_brightness.h +@@ -0,0 +1,40 @@ ++/* SPDX-License-Identifier: MIT */ ++/* ++ * Copyright © 2025 Chris Xiong ++ */ ++ ++#ifndef __INTEL_DP_LEGACY_OLED_BRIGHTNESS_H__ ++#define __INTEL_DP_LEGACY_OLED_BRIGHTNESS_H__ ++ ++#include <linux/types.h> ++ ++struct intel_connector; ++struct intel_panel_bl_funcs; ++ ++struct intel_legacy_panel_data_intermediate { ++ u8 interpolation[101][5]; ++ ++ u16 gamma_values[32][33]; ++ u16 interpolated_values[101][33]; ++ u8 tcon_gamma[32][33]; ++}; ++ ++struct intel_legacy_panel_data { ++ //registry values ++ u8 aor_buf[202]; // u16[101] ++ u8 delvss[101]; ++ u16 brightness_for_acl; ++ u8 acl_cutoff_and_delta_buf[200]; // u16[100] ++ u8 IRC[101][9]; ++ u8 SP[101]; ++ ++ u8 interpolated_gamma[101][33]; ++ u8 elvss_max_offset; ++ bool values_initialized; ++ struct intel_legacy_panel_data_intermediate* intermediates; ++}; ++ ++const struct intel_panel_bl_funcs* intel_dp_aux_legacy_get_bl_funcs(void); ++bool intel_dp_aux_legacy_brightness_supported(struct intel_connector *connector); ++ ++#endif /* __INTEL_DP_LEGACY_OLED_BRIGHTNESS_H__ */ |