diff --git a/headers/private/graphics/intel_extreme/intel_extreme.h b/headers/private/graphics/intel_extreme/intel_extreme.h index 9ca331b1e2..3a4d34fa0d 100644 --- a/headers/private/graphics/intel_extreme/intel_extreme.h +++ b/headers/private/graphics/intel_extreme/intel_extreme.h @@ -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 diff --git a/headers/private/graphics/intel_extreme/lock.h b/headers/private/graphics/intel_extreme/lock.h index 255e177f94..1df4bf821c 100644 --- a/headers/private/graphics/intel_extreme/lock.h +++ b/headers/private/graphics/intel_extreme/lock.h @@ -12,10 +12,10 @@ #include -typedef struct lock { +struct lock { sem_id sem; int32 count; -} lock; +}; static inline status_t diff --git a/src/add-ons/accelerants/intel_extreme/mode.cpp b/src/add-ons/accelerants/intel_extreme/mode.cpp index 7def6b8e6f..91f65ac524 100644 --- a/src/add-ons/accelerants/intel_extreme/mode.cpp +++ b/src/add-ons/accelerants/intel_extreme/mode.cpp @@ -10,6 +10,7 @@ */ +#include #include #include #include @@ -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; diff --git a/src/add-ons/kernel/drivers/graphics/intel_extreme/bios.cpp b/src/add-ons/kernel/drivers/graphics/intel_extreme/bios.cpp index e48617288f..24de7395e5 100644 --- a/src/add-ons/kernel/drivers/graphics/intel_extreme/bios.cpp +++ b/src/add-ons/kernel/drivers/graphics/intel_extreme/bios.cpp @@ -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; } } } diff --git a/src/add-ons/kernel/drivers/graphics/intel_extreme/intel_extreme.cpp b/src/add-ons/kernel/drivers/graphics/intel_extreme/intel_extreme.cpp index 484e8bcea6..6d64e697fb 100644 --- a/src/add-ons/kernel/drivers/graphics/intel_extreme/intel_extreme.cpp +++ b/src/add-ons/kernel/drivers/graphics/intel_extreme/intel_extreme.cpp @@ -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)) diff --git a/src/add-ons/kernel/drivers/graphics/intel_extreme/intel_extreme_private.h b/src/add-ons/kernel/drivers/graphics/intel_extreme/intel_extreme_private.h index 94fc955981..bb58cae97d 100644 --- a/src/add-ons/kernel/drivers/graphics/intel_extreme/intel_extreme_private.h +++ b/src/add-ons/kernel/drivers/graphics/intel_extreme/intel_extreme_private.h @@ -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,