1 // 2 // Copyright (c) 2010-2023 Antmicro 3 // Copyright (c) 2020-2021 Microsoft 4 // 5 // This file is licensed under the MIT License. 6 // Full license text is available in 'licenses/MIT.txt'. 7 // 8 9 using Antmicro.Renode.Backends.Display; 10 using Antmicro.Renode.Core; 11 using Antmicro.Renode.Peripherals.Bus; 12 using Antmicro.Renode.Core.Structure.Registers; 13 using System.Collections.Generic; 14 using Antmicro.Renode.Peripherals.DMA; 15 using Antmicro.Renode.Logging; 16 using Antmicro.Migrant; 17 using Antmicro.Migrant.Hooks; 18 19 namespace Antmicro.Renode.Peripherals.Video 20 { 21 public class STM32LTDC : AutoRepaintingVideo, IDoubleWordPeripheral, IKnownSize 22 { STM32LTDC(IMachine machine)23 public STM32LTDC(IMachine machine) : base(machine) 24 { 25 Reconfigure(format: PixelFormat.RGBX8888); 26 27 IRQ = new GPIO(); 28 29 sysbus = machine.GetSystemBus(this); 30 internalLock = new object(); 31 32 var activeWidthConfigurationRegister = new DoubleWordRegister(this); 33 accumulatedActiveHeightField = activeWidthConfigurationRegister.DefineValueField(0, 11, name: "AAH"); 34 accumulatedActiveWidthField = activeWidthConfigurationRegister.DefineValueField(16, 12, name: "AAW", writeCallback: (_, __) => HandleActiveDisplayChange()); 35 36 var backPorchConfigurationRegister = new DoubleWordRegister(this); 37 accumulatedVerticalBackPorchField = backPorchConfigurationRegister.DefineValueField(0, 11, name: "AVBP"); 38 accumulatedHorizontalBackPorchField = backPorchConfigurationRegister.DefineValueField(16, 12, name: "AHBP", writeCallback: (_, __) => HandleActiveDisplayChange()); 39 40 var backgroundColorConfigurationRegister = new DoubleWordRegister(this); 41 backgroundColorBlueChannelField = backgroundColorConfigurationRegister.DefineValueField(0, 8, name: "BCBLUE"); 42 backgroundColorGreenChannelField = backgroundColorConfigurationRegister.DefineValueField(8, 8, name: "BCGREEN"); 43 backgroundColorRedChannelField = backgroundColorConfigurationRegister.DefineValueField(16, 8, name: "BCRED", writeCallback: (_, __) => HandleBackgroundColorChange()); 44 45 var interruptEnableRegister = new DoubleWordRegister(this); 46 lineInterruptEnableFlag = interruptEnableRegister.DefineFlagField(0, name: "LIE"); 47 48 var interruptClearRegister = new DoubleWordRegister(this); 49 interruptClearRegister.DefineFlagField(0, FieldMode.Write, name: "CLIF", writeCallback: (_, @new) => 50 { 51 if(!@new) return; 52 lineInterruptFlag.Value = false; 53 UpdateInterrupts(); 54 }); 55 56 var interruptStatusRegister = new DoubleWordRegister(this); 57 lineInterruptFlag = interruptStatusRegister.DefineFlagField(0, FieldMode.Read, name: "LIF"); 58 59 lineInterruptPositionConfigurationRegister = new DoubleWordRegister(this).WithValueField(0, 11, name: "LIPOS"); 60 61 var registerMappings = new Dictionary<long, DoubleWordRegister> 62 { 63 { (long)Register.BackPorchConfigurationRegister, backPorchConfigurationRegister }, 64 { (long)Register.ActiveWidthConfigurationRegister, activeWidthConfigurationRegister }, 65 { (long)Register.BackgroundColorConfigurationRegister, backgroundColorConfigurationRegister }, 66 { (long)Register.InterruptEnableRegister, interruptEnableRegister }, 67 { (long)Register.InterruptStatusRegister, interruptStatusRegister }, 68 { (long)Register.InterruptClearRegister, interruptClearRegister }, 69 { (long)Register.LineInterruptPositionConfigurationRegister, lineInterruptPositionConfigurationRegister } 70 }; 71 72 localLayerBuffer = new byte[2][]; 73 layer = new Layer[2]; 74 for(var i = 0; i < layer.Length; i++) 75 { 76 layer[i] = new Layer(this, i); 77 78 var offset = 0x80 * i; 79 registerMappings.Add(0x84 + offset, layer[i].ControlRegister); 80 registerMappings.Add(0x88 + offset, layer[i].WindowHorizontalPositionConfigurationRegister); 81 registerMappings.Add(0x8C + offset, layer[i].WindowVerticalPositionConfigurationRegister); 82 registerMappings.Add(0x94 + offset, layer[i].PixelFormatConfigurationRegister); 83 registerMappings.Add(0x98 + offset, layer[i].ConstantAlphaConfigurationRegister); 84 registerMappings.Add(0x9C + offset, layer[i].DefaultColorConfigurationRegister); 85 registerMappings.Add(0xA0 + offset, layer[i].BlendingFactorConfigurationRegister); 86 registerMappings.Add(0xAC + offset, layer[i].ColorFrameBufferAddressRegister); 87 } 88 89 registers = new DoubleWordRegisterCollection(this, registerMappings); 90 registers.Reset(); 91 HandlePixelFormatChange(); 92 } 93 94 public GPIO IRQ { get; private set; } 95 96 public long Size { get { return 0xC00; } } 97 WriteDoubleWord(long address, uint value)98 public void WriteDoubleWord(long address, uint value) 99 { 100 registers.Write(address, value); 101 } 102 ReadDoubleWord(long offset)103 public uint ReadDoubleWord(long offset) 104 { 105 return registers.Read(offset); 106 } 107 Reset()108 public override void Reset() 109 { 110 registers.Reset(); 111 } 112 Repaint()113 protected override void Repaint() 114 { 115 lock(internalLock) 116 { 117 if(Width == 0 || Height == 0) 118 { 119 return; 120 } 121 122 for(var i = 0; i < 2; i++) 123 { 124 if(layer[i].LayerEnableFlag.Value && layer[i].ColorFrameBufferAddressRegister.Value != 0) 125 { 126 sysbus.ReadBytes(layer[i].ColorFrameBufferAddressRegister.Value, layer[i].LayerBuffer.Length, layer[i].LayerBuffer, 0); 127 localLayerBuffer[i] = layer[i].LayerBuffer; 128 } 129 else 130 { 131 localLayerBuffer[i] = layer[i].LayerBackgroundBuffer; 132 } 133 } 134 135 blender.Blend(localLayerBuffer[0], localLayerBuffer[1], 136 ref buffer, 137 backgroundColor, 138 (byte)layer[0].ConstantAlphaConfigurationRegister.Value, 139 layer[1].blendingFactor2.Value == BlendingFactor2.Multiply ? PixelBlendingMode.Multiply : PixelBlendingMode.NoModification, 140 (byte)layer[1].ConstantAlphaConfigurationRegister.Value, 141 layer[1].blendingFactor1.Value == BlendingFactor1.Multiply ? PixelBlendingMode.Multiply : PixelBlendingMode.NoModification); 142 143 lineInterruptFlag.Value = true; 144 UpdateInterrupts(); 145 } 146 } 147 148 private readonly byte[][] localLayerBuffer; 149 HandleBackgroundColorChange()150 private void HandleBackgroundColorChange() 151 { 152 backgroundColor = new Pixel( 153 (byte)backgroundColorRedChannelField.Value, 154 (byte)backgroundColorGreenChannelField.Value, 155 (byte)backgroundColorBlueChannelField.Value, 156 (byte)0xFF); 157 } 158 HandleActiveDisplayChange()159 private void HandleActiveDisplayChange() 160 { 161 lock(internalLock) 162 { 163 var width = (int)(accumulatedActiveWidthField.Value - accumulatedHorizontalBackPorchField.Value); 164 var height = (int)(accumulatedActiveHeightField.Value - accumulatedVerticalBackPorchField.Value); 165 166 if((width == Width && height == Height) || width < 0 || height < 0) 167 { 168 return; 169 } 170 171 Reconfigure(width, height); 172 layer[0].RestoreBuffers(); 173 layer[1].RestoreBuffers(); 174 } 175 } 176 177 [PostDeserialization] HandlePixelFormatChange()178 private void HandlePixelFormatChange() 179 { 180 lock(internalLock) 181 { 182 blender = PixelManipulationTools.GetBlender(layer[0].PixelFormatField.Value.ToPixelFormat(), Endianess, layer[1].PixelFormatField.Value.ToPixelFormat(), Endianess, Format, Endianess); 183 } 184 } 185 UpdateInterrupts()186 private void UpdateInterrupts() 187 { 188 IRQ.Set(lineInterruptEnableFlag.Value && lineInterruptFlag.Value); 189 } 190 191 private readonly IValueRegisterField accumulatedVerticalBackPorchField; 192 private readonly IValueRegisterField accumulatedHorizontalBackPorchField; 193 private readonly IValueRegisterField accumulatedActiveHeightField; 194 private readonly IValueRegisterField accumulatedActiveWidthField; 195 private readonly IValueRegisterField backgroundColorBlueChannelField; 196 private readonly IValueRegisterField backgroundColorGreenChannelField; 197 private readonly IValueRegisterField backgroundColorRedChannelField; 198 private readonly IFlagRegisterField lineInterruptEnableFlag; 199 private readonly IFlagRegisterField lineInterruptFlag; 200 private readonly DoubleWordRegister lineInterruptPositionConfigurationRegister; 201 private readonly Layer[] layer; 202 private readonly DoubleWordRegisterCollection registers; 203 204 private readonly object internalLock; 205 private readonly IBusController sysbus; 206 207 [Transient] 208 private IPixelBlender blender; 209 private Pixel backgroundColor; 210 211 private enum BlendingFactor1 212 { 213 Constant = 0x100, 214 Multiply = 0x110 215 } 216 217 private enum BlendingFactor2 218 { 219 Constant = 0x101, 220 Multiply = 0x111 221 } 222 223 private enum Register : long 224 { 225 BackPorchConfigurationRegister = 0x0C, 226 ActiveWidthConfigurationRegister = 0x10, 227 BackgroundColorConfigurationRegister = 0x2C, 228 InterruptEnableRegister = 0x34, 229 InterruptStatusRegister = 0x38, 230 InterruptClearRegister = 0x3C, 231 LineInterruptPositionConfigurationRegister = 0x40, 232 } 233 234 private class Layer 235 { Layer(STM32LTDC video, int layerId)236 public Layer(STM32LTDC video, int layerId) 237 { 238 ControlRegister = new DoubleWordRegister(video); 239 LayerEnableFlag = ControlRegister.DefineFlagField(0, name: "LEN", writeCallback: (_, __) => WarnAboutWrongBufferConfiguration()); 240 241 WindowHorizontalPositionConfigurationRegister = new DoubleWordRegister(video); 242 WindowHorizontalStartPositionField = WindowHorizontalPositionConfigurationRegister.DefineValueField(0, 12, name: "WHSTPOS"); 243 WindowHorizontalStopPositionField = WindowHorizontalPositionConfigurationRegister.DefineValueField(16, 12, name: "WHSPPOS", writeCallback: (_, __) => HandleLayerWindowConfigurationChange()); 244 245 WindowVerticalPositionConfigurationRegister = new DoubleWordRegister(video); 246 WindowVerticalStartPositionField = WindowVerticalPositionConfigurationRegister.DefineValueField(0, 12, name: "WVSTPOS"); 247 WindowVerticalStopPositionField = WindowVerticalPositionConfigurationRegister.DefineValueField(16, 12, name: "WVSPPOS", writeCallback: (_, __) => HandleLayerWindowConfigurationChange()); 248 249 PixelFormatConfigurationRegister = new DoubleWordRegister(video); 250 PixelFormatField = PixelFormatConfigurationRegister.DefineEnumField<Dma2DColorMode>(0, 3, name: "PF", writeCallback: (_, __) => { RestoreBuffers(); video.HandlePixelFormatChange(); }); 251 252 ConstantAlphaConfigurationRegister = new DoubleWordRegister(video, 0xFF).WithValueField(0, 8, name: "CONSTA"); 253 254 BlendingFactorConfigurationRegister = new DoubleWordRegister(video, 0x0607); 255 blendingFactor1 = BlendingFactorConfigurationRegister.DefineEnumField<BlendingFactor1>(8, 3, name: "BF1", writeCallback: (_, __) => RestoreBuffers()); 256 blendingFactor2 = BlendingFactorConfigurationRegister.DefineEnumField<BlendingFactor2>(0, 3, name: "BF2", writeCallback: (_, __) => RestoreBuffers()); 257 258 ColorFrameBufferAddressRegister = new DoubleWordRegister(video).WithValueField(0, 32, name: "CFBADD", writeCallback: (_, __) => WarnAboutWrongBufferConfiguration()); 259 260 DefaultColorConfigurationRegister = new DoubleWordRegister(video); 261 DefaultColorBlueField = DefaultColorConfigurationRegister.DefineValueField(0, 8, name: "DCBLUE"); 262 DefaultColorGreenField = DefaultColorConfigurationRegister.DefineValueField(8, 8, name: "DCGREEN"); 263 DefaultColorRedField = DefaultColorConfigurationRegister.DefineValueField(16, 8, name: "DCRED"); 264 DefaultColorAlphaField = DefaultColorConfigurationRegister.DefineValueField(24, 8, name: "DCALPHA", writeCallback: (_, __) => HandleLayerBackgroundColorChange()); 265 266 this.layerId = layerId; 267 this.video = video; 268 } 269 RestoreBuffers()270 public void RestoreBuffers() 271 { 272 lock(video.internalLock) 273 { 274 var layerPixelFormat = PixelFormatField.Value.ToPixelFormat(); 275 var colorDepth = layerPixelFormat.GetColorDepth(); 276 LayerBuffer = new byte[video.Width * video.Height * colorDepth]; 277 LayerBackgroundBuffer = new byte[LayerBuffer.Length]; 278 279 HandleLayerBackgroundColorChange(); 280 } 281 } 282 WarnAboutWrongBufferConfiguration()283 private void WarnAboutWrongBufferConfiguration() 284 { 285 lock(video.internalLock) 286 { 287 if(LayerEnableFlag.Value && ColorFrameBufferAddressRegister.Value == 0) 288 { 289 if(!warningAlreadyIssued) 290 { 291 video.Log(LogLevel.Warning, "Layer {0} is enabled, but no frame buffer register is set", layerId); 292 warningAlreadyIssued = true; 293 } 294 } 295 else 296 { 297 warningAlreadyIssued = false; 298 } 299 } 300 } 301 HandleLayerWindowConfigurationChange()302 private void HandleLayerWindowConfigurationChange() 303 { 304 lock(video.internalLock) 305 { 306 var width = (int)(WindowHorizontalStopPositionField.Value - WindowHorizontalStartPositionField.Value) + 1; 307 var height = (int)(WindowVerticalStopPositionField.Value - WindowVerticalStartPositionField.Value) + 1; 308 309 if(width != video.Width || height != video.Height) 310 { 311 video.Log(LogLevel.Warning, "Windowing is not supported yet for layer {0}.", layerId); 312 } 313 } 314 } 315 HandleLayerBackgroundColorChange()316 private void HandleLayerBackgroundColorChange() 317 { 318 var colorBuffer = new byte[4 * video.Width * video.Height]; 319 for(var i = 0; i < colorBuffer.Length; i += 4) 320 { 321 colorBuffer[i] = (byte)DefaultColorAlphaField.Value; 322 colorBuffer[i + 1] = (byte)DefaultColorRedField.Value; 323 colorBuffer[i + 2] = (byte)DefaultColorGreenField.Value; 324 colorBuffer[i + 3] = (byte)DefaultColorBlueField.Value; 325 } 326 327 PixelManipulationTools.GetConverter(PixelFormat.ARGB8888, video.Endianess, PixelFormatField.Value.ToPixelFormat(), video.Endianess) 328 .Convert(colorBuffer, ref LayerBackgroundBuffer); 329 } 330 331 public DoubleWordRegister ControlRegister; 332 public IFlagRegisterField LayerEnableFlag; 333 334 public DoubleWordRegister PixelFormatConfigurationRegister; 335 public IEnumRegisterField<Dma2DColorMode> PixelFormatField; 336 337 public DoubleWordRegister ConstantAlphaConfigurationRegister; 338 public DoubleWordRegister BlendingFactorConfigurationRegister; 339 public IEnumRegisterField<BlendingFactor1> blendingFactor1; 340 public IEnumRegisterField<BlendingFactor2> blendingFactor2; 341 342 public DoubleWordRegister ColorFrameBufferAddressRegister; 343 344 public DoubleWordRegister WindowHorizontalPositionConfigurationRegister; 345 public IValueRegisterField WindowHorizontalStopPositionField; 346 public IValueRegisterField WindowHorizontalStartPositionField; 347 348 public DoubleWordRegister WindowVerticalPositionConfigurationRegister; 349 public IValueRegisterField WindowVerticalStopPositionField; 350 public IValueRegisterField WindowVerticalStartPositionField; 351 352 public DoubleWordRegister DefaultColorConfigurationRegister; 353 public IValueRegisterField DefaultColorBlueField; 354 public IValueRegisterField DefaultColorGreenField; 355 public IValueRegisterField DefaultColorRedField; 356 public IValueRegisterField DefaultColorAlphaField; 357 358 public byte[] LayerBuffer; 359 public byte[] LayerBackgroundBuffer; 360 361 private bool warningAlreadyIssued; 362 private readonly int layerId; 363 private readonly STM32LTDC video; 364 } 365 } 366 } 367