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