blob: 68d0977c716a19177e3f94f8e615ad830fcd94c8 [file] [log] [blame]
// Copyright 2023 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/graphics/display/lib/api-types-cpp/display-timing.h"
#include <fuchsia/hardware/display/controller/c/banjo.h>
#include <fuchsia/hardware/dsiimpl/c/banjo.h>
#include <zircon/assert.h>
namespace display {
namespace {
constexpr uint32_t ToBanjoModeFlag(const DisplayTiming& display_timing_params) {
uint32_t banjo_mode_flag = 0;
if (display_timing_params.vsync_polarity == SyncPolarity::kPositive) {
banjo_mode_flag |= MODE_FLAG_VSYNC_POSITIVE;
}
if (display_timing_params.hsync_polarity == SyncPolarity::kPositive) {
banjo_mode_flag |= MODE_FLAG_HSYNC_POSITIVE;
}
if (display_timing_params.fields_per_frame == FieldsPerFrame::kInterlaced) {
banjo_mode_flag |= MODE_FLAG_INTERLACED;
}
if (display_timing_params.vblank_alternates) {
banjo_mode_flag |= MODE_FLAG_ALTERNATING_VBLANK;
}
ZX_DEBUG_ASSERT_MSG(
display_timing_params.pixel_repetition == 0 || display_timing_params.pixel_repetition == 1,
"Unsupported pixel_repetition: %d", display_timing_params.pixel_repetition);
if (display_timing_params.pixel_repetition == 1) {
banjo_mode_flag |= MODE_FLAG_DOUBLE_CLOCKED;
}
return banjo_mode_flag;
}
constexpr void DebugAssertBanjoDisplayModeIsValid(const display_mode_t& display_mode) {
// The >= 0 assertions are always true for uint32_t members in the
// `display_mode_t` struct and will be eventually optimized by the compiler.
//
// These assertions, depsite being always true, match the member
// definitions in `DisplayTiming` and they make it easier for readers to
// reason about the code without checking the types of each struct member.
ZX_DEBUG_ASSERT(display_mode.pixel_clock_hz >= 0);
ZX_DEBUG_ASSERT(display_mode.pixel_clock_hz <= kMaxPixelClockHz);
ZX_DEBUG_ASSERT(display_mode.h_addressable >= 0);
ZX_DEBUG_ASSERT(display_mode.h_addressable <= kMaxTimingValue);
ZX_DEBUG_ASSERT(display_mode.h_front_porch >= 0);
ZX_DEBUG_ASSERT(display_mode.h_front_porch <= kMaxTimingValue);
ZX_DEBUG_ASSERT(display_mode.h_sync_pulse >= 0);
ZX_DEBUG_ASSERT(display_mode.h_sync_pulse <= kMaxTimingValue);
// `h_front_porch` and `h_sync_pulse` are both within [0..kMaxTimingValue],
// so adding these two values won't cause an unsigned overflow.
ZX_DEBUG_ASSERT(display_mode.h_blanking >=
display_mode.h_front_porch + display_mode.h_sync_pulse);
ZX_DEBUG_ASSERT(display_mode.h_blanking -
(display_mode.h_front_porch + display_mode.h_sync_pulse) <=
kMaxTimingValue);
ZX_DEBUG_ASSERT(display_mode.v_addressable >= 0);
ZX_DEBUG_ASSERT(display_mode.v_addressable <= kMaxTimingValue);
ZX_DEBUG_ASSERT(display_mode.v_front_porch >= 0);
ZX_DEBUG_ASSERT(display_mode.v_front_porch <= kMaxTimingValue);
ZX_DEBUG_ASSERT(display_mode.v_sync_pulse >= 0);
ZX_DEBUG_ASSERT(display_mode.v_sync_pulse <= kMaxTimingValue);
// `v_front_porch` and `v_sync_pulse` are both within [0..kMaxTimingValue],
// so adding these two values won't cause an unsigned overflow.
ZX_DEBUG_ASSERT(display_mode.v_blanking >=
display_mode.v_front_porch + display_mode.v_sync_pulse);
ZX_DEBUG_ASSERT(display_mode.v_blanking -
(display_mode.v_front_porch + display_mode.v_sync_pulse) <=
kMaxTimingValue);
constexpr uint32_t kFlagMask = MODE_FLAG_VSYNC_POSITIVE | MODE_FLAG_HSYNC_POSITIVE |
MODE_FLAG_INTERLACED | MODE_FLAG_ALTERNATING_VBLANK |
MODE_FLAG_DOUBLE_CLOCKED;
ZX_DEBUG_ASSERT_MSG((display_mode.flags & (~kFlagMask)) == 0u,
"flags 0x%x has unknown bits: 0x%x", display_mode.flags,
display_mode.flags & (~kFlagMask));
}
constexpr void DebugAssertBanjoDisplaySettingIsValid(const display_setting_t& display_setting) {
// The >= 0 assertions are always true for uint32_t members in the
// `display_setting_t` struct and will be eventually optimized by the
// compiler.
//
// These assertions, despite being always true, match the member
// definitions in `DisplayTiming` and they make it easier for readers to
// reason about the code without checking the types of each struct member.
ZX_DEBUG_ASSERT(display_setting.lcd_clock >= 0);
ZX_DEBUG_ASSERT(int64_t{display_setting.lcd_clock} <= kMaxPixelClockHz);
ZX_DEBUG_ASSERT(display_setting.h_active >= 0);
ZX_DEBUG_ASSERT(display_setting.h_active <= kMaxTimingValue);
ZX_DEBUG_ASSERT(display_setting.h_period >= 0);
ZX_DEBUG_ASSERT(display_setting.h_active <= kMaxTimingValue);
ZX_DEBUG_ASSERT(display_setting.hsync_bp >= 0);
ZX_DEBUG_ASSERT(display_setting.hsync_bp <= kMaxTimingValue);
ZX_DEBUG_ASSERT(display_setting.hsync_width >= 0);
ZX_DEBUG_ASSERT(display_setting.hsync_width <= kMaxTimingValue);
ZX_DEBUG_ASSERT(display_setting.hsync_pol == 0 || display_setting.hsync_pol == 1);
// `h_active`, `hsync_bp` and `hsync_width` are all within
// [0..kMaxTimingValue], so adding these values won't cause an unsigned
// overflow.
ZX_DEBUG_ASSERT(display_setting.h_period >= display_setting.h_active + display_setting.hsync_bp +
display_setting.hsync_width);
ZX_DEBUG_ASSERT(display_setting.h_period - (display_setting.h_active + display_setting.hsync_bp +
display_setting.hsync_width) <=
kMaxTimingValue);
ZX_DEBUG_ASSERT(display_setting.v_active >= 0);
ZX_DEBUG_ASSERT(display_setting.v_active <= kMaxTimingValue);
ZX_DEBUG_ASSERT(display_setting.v_period >= 0);
ZX_DEBUG_ASSERT(display_setting.v_active <= kMaxTimingValue);
ZX_DEBUG_ASSERT(display_setting.vsync_bp >= 0);
ZX_DEBUG_ASSERT(display_setting.vsync_bp <= kMaxTimingValue);
ZX_DEBUG_ASSERT(display_setting.vsync_width >= 0);
ZX_DEBUG_ASSERT(display_setting.vsync_width <= kMaxTimingValue);
// `v_active`, `vsync_bp` and `vsync_width` are all within
// [0..kMaxTimingValue], so adding these values won't cause an unsigned
// overflow.
ZX_DEBUG_ASSERT(display_setting.v_period >= display_setting.v_active + display_setting.vsync_bp +
display_setting.vsync_width);
ZX_DEBUG_ASSERT(display_setting.v_period - (display_setting.v_active + display_setting.vsync_bp +
display_setting.vsync_width) <=
kMaxTimingValue);
ZX_DEBUG_ASSERT(display_setting.vsync_pol == 0 || display_setting.vsync_pol == 1);
}
} // namespace
DisplayTiming ToDisplayTiming(const display_mode_t& banjo_display_mode) {
DebugAssertBanjoDisplayModeIsValid(banjo_display_mode);
// A valid display_mode_t guarantees that both h_front_porch and h_sync_pulse
// are no more than kMaxTimingValue, so (h_front_porch + h_addressable) won't
// overflow.
//
// It also guarantees that h_blanking >= h_front_porch + h_sync_pulse, and
// h_blanking - (h_front_porch + h_sync_pulse) won't overflow and will fit
// in [0, kMaxTimingValue] -- so we can use int32_t.
int32_t horizontal_back_porch_px =
static_cast<int32_t>(banjo_display_mode.h_blanking -
(banjo_display_mode.h_front_porch + banjo_display_mode.h_sync_pulse));
// Ditto for the vertical back porch.
int32_t vertical_back_porch_lines =
static_cast<int32_t>(banjo_display_mode.v_blanking -
(banjo_display_mode.v_front_porch + banjo_display_mode.v_sync_pulse));
return DisplayTiming{
.horizontal_active_px = static_cast<int32_t>(banjo_display_mode.h_addressable),
.horizontal_front_porch_px = static_cast<int32_t>(banjo_display_mode.h_front_porch),
.horizontal_sync_width_px = static_cast<int32_t>(banjo_display_mode.h_sync_pulse),
.horizontal_back_porch_px = horizontal_back_porch_px,
.vertical_active_lines = static_cast<int32_t>(banjo_display_mode.v_addressable),
.vertical_front_porch_lines = static_cast<int32_t>(banjo_display_mode.v_front_porch),
.vertical_sync_width_lines = static_cast<int32_t>(banjo_display_mode.v_sync_pulse),
.vertical_back_porch_lines = vertical_back_porch_lines,
.pixel_clock_frequency_hz = banjo_display_mode.pixel_clock_hz,
.fields_per_frame = (banjo_display_mode.flags & MODE_FLAG_INTERLACED)
? FieldsPerFrame::kInterlaced
: FieldsPerFrame::kProgressive,
.hsync_polarity = (banjo_display_mode.flags & MODE_FLAG_HSYNC_POSITIVE)
? SyncPolarity::kPositive
: SyncPolarity::kNegative,
.vsync_polarity = (banjo_display_mode.flags & MODE_FLAG_VSYNC_POSITIVE)
? SyncPolarity::kPositive
: SyncPolarity::kNegative,
.vblank_alternates = (banjo_display_mode.flags & MODE_FLAG_ALTERNATING_VBLANK) != 0,
.pixel_repetition = (banjo_display_mode.flags & MODE_FLAG_DOUBLE_CLOCKED) ? 1 : 0,
};
}
display_mode_t ToBanjoDisplayMode(const DisplayTiming& display_timing_params) {
display_timing_params.DebugAssertIsValid();
return display_mode_t{
.pixel_clock_hz = display_timing_params.pixel_clock_frequency_hz,
.h_addressable = static_cast<uint32_t>(display_timing_params.horizontal_active_px),
.h_front_porch = static_cast<uint32_t>(display_timing_params.horizontal_front_porch_px),
.h_sync_pulse = static_cast<uint32_t>(display_timing_params.horizontal_sync_width_px),
// Hfront, hsync and hback are all within [0, kMaxTimingValue], so the
// sum is also a valid 32-bit unsigned integer.
.h_blanking = static_cast<uint32_t>(display_timing_params.horizontal_front_porch_px +
display_timing_params.horizontal_sync_width_px +
display_timing_params.horizontal_back_porch_px),
.v_addressable = static_cast<uint32_t>(display_timing_params.vertical_active_lines),
.v_front_porch = static_cast<uint32_t>(display_timing_params.vertical_front_porch_lines),
.v_sync_pulse = static_cast<uint32_t>(display_timing_params.vertical_sync_width_lines),
// Vfront, vsync and vback are all within [0, kMaxTimingValue], so the
// sum is also a valid 32-bit unsigned integer.
.v_blanking = static_cast<uint32_t>(display_timing_params.vertical_front_porch_lines +
display_timing_params.vertical_sync_width_lines +
display_timing_params.vertical_back_porch_lines),
.flags = ToBanjoModeFlag(display_timing_params),
};
}
DisplayTiming ToDisplayTiming(const display_setting_t& banjo_display_setting) {
DebugAssertBanjoDisplaySettingIsValid(banjo_display_setting);
// A valid display_setting_t guarantees that `h_active`, `hsync_bp` and
// `hsync_width` are all within [0..kMaxTimingValue], so (h_active +
// hsync_bp + hsync_width) won't overflow.
//
// It also guarantees that h_period >= (h_active + hsync_bp + hsync_width),
// and h_period - (h_active + hsync_bp + hsync_width) is within
// [0, kMaxTimingValue], so we can use int32_t to store its value.
const int32_t horizontal_front_porch_px =
static_cast<int32_t>(banjo_display_setting.h_period -
(banjo_display_setting.h_active + banjo_display_setting.hsync_bp +
banjo_display_setting.hsync_width));
// Using an argument similar to the above, we can prove that the vertical
// front porch value can be also stored in an int32_t.
const int32_t vertical_front_porch_lines =
static_cast<int32_t>(banjo_display_setting.v_period -
(banjo_display_setting.v_active + banjo_display_setting.vsync_bp +
banjo_display_setting.vsync_width));
return DisplayTiming{
.horizontal_active_px = static_cast<int32_t>(banjo_display_setting.h_active),
.horizontal_front_porch_px = static_cast<int32_t>(horizontal_front_porch_px),
.horizontal_sync_width_px = static_cast<int32_t>(banjo_display_setting.hsync_width),
.horizontal_back_porch_px = static_cast<int32_t>(banjo_display_setting.hsync_bp),
.vertical_active_lines = static_cast<int32_t>(banjo_display_setting.v_active),
.vertical_front_porch_lines = vertical_front_porch_lines,
.vertical_sync_width_lines = static_cast<int32_t>(banjo_display_setting.vsync_width),
.vertical_back_porch_lines = static_cast<int32_t>(banjo_display_setting.vsync_bp),
.pixel_clock_frequency_hz = banjo_display_setting.lcd_clock,
.fields_per_frame = FieldsPerFrame::kProgressive,
.hsync_polarity = (banjo_display_setting.hsync_pol == 1) ? SyncPolarity::kPositive
: SyncPolarity::kNegative,
.vsync_polarity = (banjo_display_setting.vsync_pol == 1) ? SyncPolarity::kPositive
: SyncPolarity::kNegative,
.vblank_alternates = false,
.pixel_repetition = 0,
};
}
} // namespace display