intel_extreme: improve brightness setting support
- Newer devices use a different layout for the backlight PWM registers - Get the min brightness level from the BDB Change-Id: I99745a022dd38733a4c2386f91c4c57016dd2acd Reviewed-on: https://review.haiku-os.org/c/haiku/+/5162 Reviewed-by: Jérôme Duval <jerome.duval@gmail.com> Tested-by: Commit checker robot <no-reply+buildbot@haiku-os.org>
This commit is contained in:
parent
83cb10bcbc
commit
890aa41134
@ -293,6 +293,7 @@ struct intel_shared_info {
|
||||
uint32 bytes_per_row;
|
||||
uint32 bits_per_pixel;
|
||||
uint32 dpms_mode;
|
||||
uint16 min_brightness;
|
||||
|
||||
area_id registers_area; // area of memory mapped registers
|
||||
uint32 register_blocks[REGISTER_BLOCK_COUNT];
|
||||
@ -1178,11 +1179,21 @@ struct intel_free_graphics_memory {
|
||||
#define PANEL_DIVISOR_POW_CYCLE_DLY_SHIFT 0x1f
|
||||
|
||||
// Backlight control registers
|
||||
#define PCH_BLC_PWM_CTL2 (0x8250 | REGS_NORTH_SHARED)
|
||||
#define PCH_BLC_PWM_CTL (0x8254 | REGS_NORTH_SHARED)
|
||||
#define PCH_SBLC_PWM_CTL2 (0x8254 | REGS_SOUTH_SHARED)
|
||||
// These have moved around, initially they were per pipe, then they were moved in the "north" part
|
||||
// of the PCH with a single backlight control (independant of pipes), and then moved again to the
|
||||
// "south" part of the PCH, with a simplified register layout.
|
||||
#define PCH_BLC_PWM_CTL2 (0x8250 | REGS_NORTH_SHARED) // Linux BLC_PWM_CPU_CTL2
|
||||
#define PCH_BLC_PWM_CTL (0x8254 | REGS_NORTH_SHARED) // Linux BLC_PWM_CPU_CTL
|
||||
|
||||
// Devices after Cannonlake have a new register layout, with separate registers for the period
|
||||
// and duty cycle instead of having two 16bit values in a 32bit register
|
||||
#define PCH_SOUTH_BLC_PWM_CONTROL (0x8250 | REGS_SOUTH_SHARED) // Linux _BXT_BLC_PWM_CTL1
|
||||
#define PCH_SOUTH_BLC_PWM_PERIOD (0x8254 | REGS_SOUTH_SHARED) // Linux _BXT_BLC_PWM_FREQ1
|
||||
#define PCH_SOUTH_BLC_PWM_DUTY_CYCLE (0x8258 | REGS_SOUTH_SHARED) // Linux _BXT_BLC_PWM_DUTY1
|
||||
|
||||
#define MCH_BLC_PWM_CTL (0x1254 | REGS_NORTH_PIPE_AND_PORT)
|
||||
// Linux VLV_BLC_PWM_CTL (one register per pipe) or BLC_PWM_CTL (a single register that can be
|
||||
// programmed for use on either pipe)
|
||||
|
||||
// ring buffer commands
|
||||
|
||||
|
@ -12,10 +12,10 @@
|
||||
#include <OS.h>
|
||||
|
||||
|
||||
typedef struct lock {
|
||||
struct lock {
|
||||
sem_id sem;
|
||||
int32 count;
|
||||
} lock;
|
||||
};
|
||||
|
||||
|
||||
static inline status_t
|
||||
|
@ -10,6 +10,7 @@
|
||||
*/
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
@ -572,14 +573,28 @@ intel_get_edid_info(void* info, size_t size, uint32* _version)
|
||||
}
|
||||
|
||||
|
||||
// Get the backlight registers. We need the backlight frequency (we never write it, but we ned to
|
||||
// know it's value as the duty cycle/brihtness level is proportional to it), and the duty cycle
|
||||
// register (read to get the current backlight value, written to set it). On older generations,
|
||||
// the two values are in the same register (16 bits each), on newer ones there are two separate
|
||||
// registers.
|
||||
static int32_t
|
||||
intel_get_backlight_register(bool read)
|
||||
intel_get_backlight_register(bool period)
|
||||
{
|
||||
if (gInfo->shared_info->pch_info >= INTEL_PCH_CNP) {
|
||||
if (period)
|
||||
return PCH_SOUTH_BLC_PWM_PERIOD;
|
||||
else
|
||||
return PCH_SOUTH_BLC_PWM_DUTY_CYCLE;
|
||||
}
|
||||
|
||||
if (gInfo->shared_info->pch_info == INTEL_PCH_NONE)
|
||||
return MCH_BLC_PWM_CTL;
|
||||
|
||||
if (read)
|
||||
return PCH_SBLC_PWM_CTL2;
|
||||
|
||||
// FIXME this mixup of south and north registers seems very strange; it should either be
|
||||
// a single register with both period and duty in it, or two separate registers.
|
||||
if (period)
|
||||
return PCH_SOUTH_BLC_PWM_PERIOD;
|
||||
else
|
||||
return PCH_BLC_PWM_CTL;
|
||||
}
|
||||
@ -593,21 +608,32 @@ intel_set_brightness(float brightness)
|
||||
if (brightness < 0 || brightness > 1)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
uint32_t period = read32(intel_get_backlight_register(true)) >> 16;
|
||||
|
||||
// The "duty cycle" is a proportion of the period (0 = backlight off,
|
||||
// period = maximum brightness). The low bit must be masked out because
|
||||
// it is apparently used for something else on some Atom machines (no
|
||||
// reference to that in the documentation that I know of).
|
||||
// period = maximum brightness).
|
||||
// Additionally we don't want it to be completely 0 here, because then
|
||||
// it becomes hard to turn the display on again (at least until we get
|
||||
// working ACPI keyboard shortcuts for this). So always keep the backlight
|
||||
// at least a little bit on for now.
|
||||
uint32_t duty = (uint32_t)(period * brightness) & 0xfffe;
|
||||
if (duty == 0 && period != 0)
|
||||
duty = 2;
|
||||
|
||||
write32(intel_get_backlight_register(false), duty | (period << 16));
|
||||
if (gInfo->shared_info->device_type.Generation() >= 11) {
|
||||
uint32_t period = read32(intel_get_backlight_register(true));
|
||||
|
||||
uint32_t duty = (uint32_t)(period * brightness);
|
||||
duty = std::max(duty, (uint32_t)gInfo->shared_info->min_brightness);
|
||||
|
||||
write32(intel_get_backlight_register(false), duty);
|
||||
} else {
|
||||
// On older devices there is a single register with both period and duty cycle
|
||||
uint32_t period = read32(intel_get_backlight_register(true)) >> 16;
|
||||
|
||||
// The low bit must be masked out because
|
||||
// it is apparently used for something else on some Atom machines (no
|
||||
// reference to that in the documentation that I know of).
|
||||
uint32_t duty = (uint32_t)(period * brightness) & 0xfffe;
|
||||
duty = std::max(duty, (uint32_t)gInfo->shared_info->min_brightness);
|
||||
|
||||
write32(intel_get_backlight_register(false), duty | (period << 16));
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
@ -621,8 +647,16 @@ intel_get_brightness(float* brightness)
|
||||
if (brightness == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
uint16_t period = read32(intel_get_backlight_register(true)) >> 16;
|
||||
uint16_t duty = read32(intel_get_backlight_register(false)) & 0xffff;
|
||||
uint32_t duty;
|
||||
uint32_t period;
|
||||
|
||||
if (gInfo->shared_info->device_type.Generation() >= 11) {
|
||||
period = read32(intel_get_backlight_register(true));
|
||||
duty = read32(intel_get_backlight_register(false));
|
||||
} else {
|
||||
period = read32(intel_get_backlight_register(true)) >> 16;
|
||||
duty = read32(intel_get_backlight_register(false)) & 0xffff;
|
||||
}
|
||||
*brightness = (float)duty / period;
|
||||
|
||||
return B_OK;
|
||||
|
@ -46,6 +46,7 @@ struct bdb_header {
|
||||
enum bdb_block_id {
|
||||
BDB_LVDS_OPTIONS = 40,
|
||||
BDB_LVDS_LFP_DATA_PTRS = 41,
|
||||
BDB_LVDS_BACKLIGHT = 43,
|
||||
BDB_GENERIC_DTD = 58
|
||||
};
|
||||
|
||||
@ -62,6 +63,9 @@ enum bdb_block_id {
|
||||
#define _V_SYNC_OFF(x) ((x[10] >> 4) + ((x[11] & 0x0C) << 2))
|
||||
#define _V_SYNC_WIDTH(x) ((x[10] & 0x0F) + ((x[11] & 0x03) << 4))
|
||||
|
||||
#define BDB_BACKLIGHT_TYPE_NONE 0
|
||||
#define BDB_BACKLIGHT_TYPE_PWM 2
|
||||
|
||||
|
||||
struct lvds_bdb1 {
|
||||
uint8 id;
|
||||
@ -137,6 +141,40 @@ struct bdb_generic_dtd {
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
struct bdb_lfp_backlight_data_entry {
|
||||
uint8 type: 2;
|
||||
uint8 active_low_pwm: 1;
|
||||
uint8 reserved1: 5;
|
||||
uint16 pwm_freq_hz;
|
||||
uint8 min_brightness; // Versions < 234
|
||||
uint8 reserved2;
|
||||
uint8 reserved3;
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
struct bdb_lfp_backlight_control_method {
|
||||
uint8 type: 4;
|
||||
uint8 controller: 4;
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
struct lfp_brightness_level {
|
||||
uint16 level;
|
||||
uint16 reserved;
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
struct bdb_lfp_backlight_data {
|
||||
uint8 entry_size;
|
||||
struct bdb_lfp_backlight_data_entry data[16];
|
||||
uint8 level [16]; // Only for versions < 234
|
||||
struct bdb_lfp_backlight_control_method backlight_control[16];
|
||||
struct lfp_brightness_level brightness_level[16]; // Versions >= 234
|
||||
struct lfp_brightness_level brightness_min_level[16]; // Versions >= 234
|
||||
uint8 brightness_precision_bits[16]; // Versions >= 236
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
static struct vbios {
|
||||
area_id area;
|
||||
uint8* memory;
|
||||
@ -384,7 +422,7 @@ sanitize_panel_timing(display_timing& timing)
|
||||
|
||||
|
||||
bool
|
||||
get_lvds_mode_from_bios(display_timing* panelTiming)
|
||||
get_lvds_mode_from_bios(display_timing* panelTiming, uint16* minBrightness)
|
||||
{
|
||||
int vbtOffset = 0;
|
||||
if (!get_bios(&vbtOffset))
|
||||
@ -431,6 +469,10 @@ get_lvds_mode_from_bios(display_timing* panelTiming)
|
||||
if (panelType == -1)
|
||||
break;
|
||||
|
||||
// on newer versions, check also generic DTD, use LFP panel DTD as a fallback
|
||||
if (bdb->version >= 229 && panelTimingFound)
|
||||
break;
|
||||
|
||||
struct lvds_bdb2 *lvds2;
|
||||
struct lvds_bdb2_lfp_info *lvds2_lfp_info;
|
||||
|
||||
@ -460,13 +502,9 @@ get_lvds_mode_from_bios(display_timing* panelTiming)
|
||||
|
||||
sanitize_panel_timing(*panelTiming);
|
||||
|
||||
// on newer versions, check also generic DTD, use LFP panel DTD as a fallback
|
||||
if (bdb->version >= 229) {
|
||||
panelTimingFound = true;
|
||||
break;
|
||||
}
|
||||
delete_area(vbios.area);
|
||||
return true;
|
||||
panelTimingFound = true;
|
||||
break;
|
||||
|
||||
}
|
||||
case BDB_GENERIC_DTD:
|
||||
{
|
||||
@ -511,9 +549,41 @@ get_lvds_mode_from_bios(display_timing* panelTiming)
|
||||
panelTiming->flags |= B_POSITIVE_VSYNC;
|
||||
|
||||
sanitize_panel_timing(*panelTiming);
|
||||
delete_area(vbios.area);
|
||||
return true;
|
||||
panelTimingFound = true;
|
||||
break;
|
||||
}
|
||||
case BDB_LVDS_BACKLIGHT:
|
||||
{
|
||||
TRACE((DEVICE_NAME ": found bdb lvds backlight info\n"));
|
||||
// First make sure we found block BDB_LVDS_OPTIONS and the panel type
|
||||
if (panelType == -1)
|
||||
break;
|
||||
|
||||
bdb_lfp_backlight_data* backlightData
|
||||
= (bdb_lfp_backlight_data*)(vbios.memory + start);
|
||||
|
||||
const struct bdb_lfp_backlight_data_entry* entry = &backlightData->data[panelType];
|
||||
|
||||
if (entry->type == BDB_BACKLIGHT_TYPE_PWM) {
|
||||
uint16 minLevel;
|
||||
if (bdb->version < 234) {
|
||||
minLevel = entry->min_brightness;
|
||||
} else {
|
||||
minLevel = backlightData->brightness_min_level[panelType].level;
|
||||
if (bdb->version >= 236
|
||||
&& backlightData->brightness_precision_bits[panelType] == 16) {
|
||||
TRACE((DEVICE_NAME ": divide level by 255\n"));
|
||||
minLevel /= 255;
|
||||
}
|
||||
}
|
||||
|
||||
*minBrightness = minLevel;
|
||||
TRACE((DEVICE_NAME ": display %d min brightness level is %u\n", panelType,
|
||||
minLevel));
|
||||
} else {
|
||||
TRACE((DEVICE_NAME ": display %d does not have PWM\n", panelType));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -675,10 +675,11 @@ intel_extreme_init(intel_info &info)
|
||||
info.shared_info->graphics_memory_size = apertureInfo.size;
|
||||
info.shared_info->frame_buffer = 0;
|
||||
info.shared_info->dpms_mode = B_DPMS_ON;
|
||||
info.shared_info->min_brightness = 2;
|
||||
|
||||
// Pull VBIOS panel mode for later use
|
||||
info.shared_info->got_vbt = get_lvds_mode_from_bios(
|
||||
&info.shared_info->panel_timing);
|
||||
&info.shared_info->panel_timing, &info.shared_info->min_brightness);
|
||||
|
||||
/* at least 855gm can't drive more than one head at time */
|
||||
if (info.device_type.InFamily(INTEL_FAMILY_8xx))
|
||||
|
@ -72,7 +72,7 @@ find_reg(const intel_info& info, uint32 target)
|
||||
}
|
||||
|
||||
|
||||
extern bool get_lvds_mode_from_bios(display_timing *timing);
|
||||
extern bool get_lvds_mode_from_bios(display_timing *timing, uint16 *minBrightness);
|
||||
extern status_t intel_free_memory(intel_info& info, addr_t offset);
|
||||
extern status_t intel_allocate_memory(intel_info& info, size_t size,
|
||||
size_t alignment, uint32 flags, addr_t* _offset,
|
||||
|
Loading…
Reference in New Issue
Block a user