kernel/hil/screen.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335
// Licensed under the Apache License, Version 2.0 or the MIT License.
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright Tock Contributors 2022.
//! Interface for screens and displays.
//!
//! The interfaces exposed here cover both configurable (`ScreenSetup`), and
//! less configurable hardware (only `Screen`).
//!
//! It's composed of 4 main kinds of requests:
//! - set power,
//! - read configuration (e.g. `get_resolution`),
//! - configure (e.g. `set_invert`),
//! - write buffer.
//!
//! All requests, except for `Screen::set_power`, can return `OFF` under some
//! circumstances.
//!
//! For buffer writes, it's when the display is powered off.
//!
//! While the display is not powered on, the user could try to configure it. In
//! that case, the driver MUST either cache the value, or return `OFF`. This is
//! to let the user power the display in the desired configuration.
//!
//! Configuration reads shall return the actual state of the display. In
//! situations where a parameter cannot be configured (e.g. fixed resolution),
//! they return value may be hardcoded. Otherwise, the driver should query the
//! hardware directly, and return OFF if it's not powered.
//!
//! Configuration sets cause a `command_complete` callback unless noted
//! otherwise.
use crate::utilities::leasable_buffer::SubSliceMut;
use crate::ErrorCode;
use core::ops::Add;
use core::ops::Sub;
#[derive(Copy, Clone, PartialEq)]
pub enum ScreenRotation {
Normal,
Rotated90,
Rotated180,
Rotated270,
}
impl Add for ScreenRotation {
type Output = Self;
fn add(self, other: Self) -> Self {
match (self, other) {
(ScreenRotation::Normal, _) => other,
(_, ScreenRotation::Normal) => self,
(ScreenRotation::Rotated90, ScreenRotation::Rotated90) => ScreenRotation::Rotated180,
(ScreenRotation::Rotated90, ScreenRotation::Rotated180) => ScreenRotation::Rotated270,
(ScreenRotation::Rotated90, ScreenRotation::Rotated270) => ScreenRotation::Normal,
(ScreenRotation::Rotated180, ScreenRotation::Rotated90) => ScreenRotation::Rotated270,
(ScreenRotation::Rotated180, ScreenRotation::Rotated180) => ScreenRotation::Normal,
(ScreenRotation::Rotated180, ScreenRotation::Rotated270) => ScreenRotation::Rotated90,
(ScreenRotation::Rotated270, ScreenRotation::Rotated90) => ScreenRotation::Normal,
(ScreenRotation::Rotated270, ScreenRotation::Rotated180) => ScreenRotation::Rotated90,
(ScreenRotation::Rotated270, ScreenRotation::Rotated270) => ScreenRotation::Rotated180,
}
}
}
impl Sub for ScreenRotation {
type Output = Self;
fn sub(self, other: Self) -> Self {
match (self, other) {
(_, ScreenRotation::Normal) => self,
(ScreenRotation::Normal, ScreenRotation::Rotated90) => ScreenRotation::Rotated270,
(ScreenRotation::Normal, ScreenRotation::Rotated180) => ScreenRotation::Rotated180,
(ScreenRotation::Normal, ScreenRotation::Rotated270) => ScreenRotation::Rotated90,
(ScreenRotation::Rotated90, ScreenRotation::Rotated90) => ScreenRotation::Normal,
(ScreenRotation::Rotated90, ScreenRotation::Rotated180) => ScreenRotation::Rotated270,
(ScreenRotation::Rotated90, ScreenRotation::Rotated270) => ScreenRotation::Rotated180,
(ScreenRotation::Rotated180, ScreenRotation::Rotated90) => ScreenRotation::Rotated90,
(ScreenRotation::Rotated180, ScreenRotation::Rotated180) => ScreenRotation::Normal,
(ScreenRotation::Rotated180, ScreenRotation::Rotated270) => ScreenRotation::Rotated270,
(ScreenRotation::Rotated270, ScreenRotation::Rotated90) => ScreenRotation::Rotated180,
(ScreenRotation::Rotated270, ScreenRotation::Rotated180) => ScreenRotation::Rotated90,
(ScreenRotation::Rotated270, ScreenRotation::Rotated270) => ScreenRotation::Normal,
}
}
}
/// How pixels are encoded for the screen.
#[derive(Copy, Clone, PartialEq)]
#[repr(usize)]
#[allow(non_camel_case_types)]
pub enum ScreenPixelFormat {
/// Pixels encoded as 1-bit, used for monochromatic displays.
Mono,
/// Pixels encoded as 1-bit blue, 1-bit green, 1-bit red,
/// and 1-bit for opaque (1) vs transparent (0)
RGB_4BIT,
/// Pixels encoded as 3-bit red channel, 3-bit green channel, 2-bit blue
/// channel.
RGB_332,
/// Pixels encoded as 5-bit red channel, 6-bit green channel, 5-bit blue
/// channel.
RGB_565,
/// Pixels encoded as 8-bit red channel, 8-bit green channel, 8-bit blue
/// channel.
RGB_888,
/// Pixels encoded as 8-bit alpha channel, 8-bit red channel, 8-bit green
/// channel, 8-bit blue channel.
ARGB_8888,
// other pixel formats may be defined.
}
impl ScreenPixelFormat {
pub fn get_bits_per_pixel(&self) -> usize {
match self {
Self::Mono => 1,
Self::RGB_4BIT => 4,
Self::RGB_332 => 8,
Self::RGB_565 => 16,
Self::RGB_888 => 24,
Self::ARGB_8888 => 32,
}
}
}
/// Interface to configure the screen.
pub trait ScreenSetup<'a> {
fn set_client(&self, client: &'a dyn ScreenSetupClient);
/// Set the screen resolution in pixels with `(X, Y)`.
///
/// Returns `Ok(())` if the request is registered and will be sent to the
/// screen. A `command_complete` callback function will be triggered when
/// the resolution change is finished and will provide a `Result<(),
/// ErrorCode>` to indicate if the resolution change was successful.
///
/// Returns `Err(NOSUPPORT)` if the resolution is not supported. No callback will
/// be triggered.
fn set_resolution(&self, resolution: (usize, usize)) -> Result<(), ErrorCode>;
/// Set the pixel format.
///
/// Returns `Ok(())` if the request is registered and will be sent to the
/// screen. A `command_complete` callback function will be triggered when
/// the pixel format change is finished and will provide a `Result<(),
/// ErrorCode>` to indicate if the pixel format change was successful.
///
/// Returns `Err(NOSUPPORT)` if the pixel format is not supported.
fn set_pixel_format(&self, format: ScreenPixelFormat) -> Result<(), ErrorCode>;
/// Set the rotation of the display.
///
/// Returns `Ok(())` if the request is registered and will be sent to the
/// screen. A `command_complete` callback function will be triggered when
/// the rotation update is finished and will provide a `Result<(),
/// ErrorCode>` to indicate if the rotation change was successful.
///
/// Note that `Rotated90` or `Rotated270` will swap the width and height.
fn set_rotation(&self, rotation: ScreenRotation) -> Result<(), ErrorCode>;
/// Get the number of supported resolutions.
///
/// This must return at least one (the current resolution).
///
/// This function is synchronous as the driver should know this value
/// without requesting it from the screen (most screens do not support such
/// a request, resolutions are described in the data sheet).
fn get_num_supported_resolutions(&self) -> usize;
/// Get the resolution specified by the given index.
///
/// `index` is from `0..get_num_supported_resolutions()-1` and this returns
/// a tuple `(width, height)` of the associated resolution (in pixels). Note
/// that width and height may change due to rotation. Returns `None` if
/// `index` is invalid.
///
/// This function is synchronous as the driver should know this value
/// without requesting it from the screen.
fn get_supported_resolution(&self, index: usize) -> Option<(usize, usize)>;
/// Get the number of the pixel formats supported.
///
/// This function is synchronous as the driver should know this value
/// without requesting it from the screen (most screens do not support such
/// a request, pixel formats are described in the data sheet).
fn get_num_supported_pixel_formats(&self) -> usize;
/// Get the pixel format specified by the given index.
///
/// `index` is from `0..get_num_supported_pixel_formats()-1` and this
/// returns the associated pixel format. Returns `None` if `index` is
/// invalid.
///
/// This function is synchronous as the driver should know this value
/// without requesting it from the screen.
fn get_supported_pixel_format(&self, index: usize) -> Option<ScreenPixelFormat>;
}
/// Basic interface for screens.
pub trait Screen<'a> {
/// Set the object to receive the asynchronous command callbacks.
fn set_client(&self, client: &'a dyn ScreenClient);
/// Get a tuple `(width, height)` with the current resolution (in pixels).
///
/// This function is synchronous as the driver should know this value
/// without requesting it from the screen.
///
/// Note that width and height may change due to rotation.
fn get_resolution(&self) -> (usize, usize);
/// Get the current pixel format.
///
/// This function is synchronous as the driver should know this value
/// without requesting it from the screen.
fn get_pixel_format(&self) -> ScreenPixelFormat;
/// Get the current rotation.
///
/// This function is synchronous as the driver should know this value
/// without requesting it from the screen.
fn get_rotation(&self) -> ScreenRotation;
/// Sets the write frame.
///
/// This function has to be called before the first call to the write
/// function. This will generate a `command_complete()` callback when
/// finished.
///
/// Return values:
/// - `Ok(())`: The write frame is valid.
/// - `INVAL`: The parameters of the write frame are not valid.
/// - `BUSY`: Unable to set the write frame on the device.
fn set_write_frame(
&self,
x: usize,
y: usize,
width: usize,
height: usize,
) -> Result<(), ErrorCode>;
/// Write data from `buffer` to the selected write frame.
///
/// When finished, the driver will call the `write_complete()` callback.
///
/// This function can be called multiple times if the write frame is larger
/// than the size of the available buffer by setting `continue_write` to
/// `true`. If `continue_write` is false, the buffer write position will be
/// reset before the data are written.
///
/// Return values:
/// - `Ok(())`: Write is valid and will be sent to the screen.
/// - `SIZE`: The buffer is too long for the selected write frame.
/// - `BUSY`: Another write is in progress.
fn write(
&self,
buffer: SubSliceMut<'static, u8>,
continue_write: bool,
) -> Result<(), ErrorCode>;
/// Set the display brightness value.
///
/// Depending on the display, this may not cause any actual changes
/// until and unless power is enabled (see `set_power`).
///
/// The following values must be supported:
/// - 0: completely no light emitted
/// - 1..65536: set brightness to the given level
///
/// The display should interpret the brightness value as *lightness* (each
/// increment should change perceived brightness the same). 1 shall be the
/// minimum supported brightness, 65536 is the maximum brightness. Values in
/// between should approximate the intermediate values; minimum and maximum
/// included (e.g. when there is only 1 level).
fn set_brightness(&self, brightness: u16) -> Result<(), ErrorCode>;
/// Controls the screen power supply.
///
/// Use it to initialize the display device.
///
/// For screens where display needs nonzero brightness (e.g. LED), this
/// shall set brightness to a default value if `set_brightness` was not
/// called first.
///
/// The device may implement power independently from brightness, so call
/// `set_brightness` to turn on/off the module completely.
///
/// To allow starting in the correct configuration, the driver is allowed to
/// cache values like brightness or invert mode and apply them together when
/// power is enabled. If the display cannot use selected configuration, this
/// call returns `INVAL`.
///
/// When finished, calls `ScreenClient::screen_is_ready`, both when power
/// is enabled and disabled.
fn set_power(&self, enabled: bool) -> Result<(), ErrorCode>;
/// Controls the color inversion mode.
///
/// Pixels already in the frame buffer, as well as newly submitted, will be
/// inverted. What that means depends on the current pixel format. May get
/// disabled when switching to another pixel format. Returns `NOSUPPORT` if
/// the device does not accelerate color inversion. Returns `INVAL` if the
/// current pixel format does not support color inversion.
fn set_invert(&self, enabled: bool) -> Result<(), ErrorCode>;
}
pub trait ScreenAdvanced<'a>: Screen<'a> + ScreenSetup<'a> {}
// Provide blanket implementations for trait group
impl<'a, T: Screen<'a> + ScreenSetup<'a>> ScreenAdvanced<'a> for T {}
pub trait ScreenSetupClient {
/// The screen will call this function to notify that a command has finished.
fn command_complete(&self, r: Result<(), ErrorCode>);
}
pub trait ScreenClient {
/// The screen will call this function to notify that a command (except
/// write) has finished.
fn command_complete(&self, result: Result<(), ErrorCode>);
/// The screen will call this function to notify that the write command has
/// finished. This is different from `command_complete` as it has to pass
/// back the write buffer
fn write_complete(&self, buffer: SubSliceMut<'static, u8>, result: Result<(), ErrorCode>);
/// Some screens need some time to start, this function is called when the
/// screen is ready.
fn screen_is_ready(&self);
}