1 //
2 // Copyright (c) 2010 - 2023 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 
8 using System;
9 using System.Linq;
10 using Antmicro.Renode.Backends.Display;
11 using Antmicro.Renode.Core;
12 using Antmicro.Renode.Core.Structure.Registers;
13 using Antmicro.Renode.Logging;
14 using Antmicro.Renode.Peripherals.Bus;
15 using Antmicro.Renode.Peripherals.Memory;
16 using Antmicro.Renode.Utilities;
17 
18 namespace Antmicro.Renode.Peripherals.Video
19 {
20     public class LiteX_Framebuffer : AutoRepaintingVideo, IDoubleWordPeripheral, IProvidesRegisterCollection<DoubleWordRegisterCollection>, IKnownSize
21     {
LiteX_Framebuffer(IMachine machine, PixelFormat format, IBusPeripheral memory)22         public LiteX_Framebuffer(IMachine machine, PixelFormat format, IBusPeripheral memory) : base(machine)
23         {
24             this.memory = memory;
25             sysbus = machine.GetSystemBus(this);
26             this.format = format;
27 
28             RegistersCollection = new DoubleWordRegisterCollection(this);
29             DefineRegisters();
30         }
31 
WriteDoubleWord(long address, uint value)32         public void WriteDoubleWord(long address, uint value)
33         {
34             RegistersCollection.Write(address, value);
35         }
36 
ReadDoubleWord(long offset)37         public uint ReadDoubleWord(long offset)
38         {
39             return RegistersCollection.Read(offset);
40         }
41 
Reset()42         public override void Reset()
43         {
44             RegistersCollection.Reset();
45             bufferAddress = 0;
46         }
47 
48         public long Size => 0x100;
49         public DoubleWordRegisterCollection RegistersCollection { get; private set; }
50 
Repaint()51         protected override void Repaint()
52         {
53             sysbus.ReadBytes(bufferAddress, buffer.Length, buffer, 0);
54         }
55 
DefineRegisters()56         private void DefineRegisters()
57         {
58             Registers.DriverClockingMMCMDrdy.Define(this)
59                 .WithFlag(0, FieldMode.Read, valueProviderCallback: _ => true) // this should always be ready
60             ;
61 
62             Registers.InitiatorHres.DefineMany(this, 2, stepInBytes: 4, setup: (reg, idx) =>
63                 reg.WithValueField(0, 8, out hres[idx], name: $"h_res_{idx}").WithIgnoredBits(8, 24)) // ignore upper bits to avoid unnecessary warnings in log
64             ;
65 
66             Registers.InitiatorVres.DefineMany(this, 2, stepInBytes: 4, setup: (reg, idx) =>
67                 reg.WithValueField(0, 8, out vres[idx], name: $"v_res_{idx}").WithIgnoredBits(8, 24)) // ignore upper bits to avoid unnecessary warnings in log
68             ;
69 
70             Registers.InitiatorBase.DefineMany(this, 4, stepInBytes: 4, setup: (reg, idx) =>
71                 reg.WithValueField(0, 8, out bufferRegisters[idx], name: $"base_{idx}").WithIgnoredBits(8, 24)) // ignore upper bits to avoid unnecessary warnings in log
72             ;
73 
74             Registers.InitiatorEnable.Define(this)
75                 .WithFlag(0, writeCallback: (_, val) =>
76                 {
77                     if(val)
78                     {
79                         var height = (int)((vres[0].Value << 8) | vres[1].Value);
80                         var width = (int)((hres[0].Value << 8) | hres[1].Value);
81                         bufferAddress = (uint)((bufferRegisters[0].Value << 24) | (bufferRegisters[1].Value << 16) | (bufferRegisters[2].Value << 8) | bufferRegisters[3].Value);
82 
83                         var memoryBase = (uint)sysbus.GetRegistrationPoints(memory).First().Range.StartAddress;
84                         bufferAddress += memoryBase;
85 
86                         this.Log(LogLevel.Debug, "Reconfiguring screen to {0}x{1}", width, height);
87 
88                         Reconfigure(width, height, format);
89                     }
90                     else
91                     {
92                         // it stops the repainter by passing nulls
93                         Reconfigure();
94                     }
95                 })
96             ;
97         }
98 
99         private IValueRegisterField[] vres = new IValueRegisterField[2];
100         private IValueRegisterField[] hres = new IValueRegisterField[2];
101         private IValueRegisterField[] bufferRegisters = new IValueRegisterField[4];
102 
103         private uint bufferAddress;
104 
105         private readonly IBusPeripheral memory;
106         private readonly IBusController sysbus;
107         private readonly PixelFormat format;
108 
109         private enum Registers
110         {
111             UnderflowEnable = 0x0,
112             UnderflowUpdate = 0x4,
113             UnderflowCounter = 0x8,
114 
115             InitiatorEnable = 0x18,
116             InitiatorHres = 0x1c,
117             InitiatorHsyncStart = 0x24,
118             InitiatorHsyncEnd = 0x2c,
119             InitiatorHscan = 0x34,
120             InitiatorVres = 0x3c,
121             InitiatorVsyncStart = 0x44,
122             InitiatorVsyncEnd = 0x4c,
123             InitatorVscan = 0x54,
124             InitiatorBase = 0x5c,
125             InitiatorLenght = 0x6c,
126 
127             DmaDelay = 0x7c,
128 
129             DriverClockingMMCMReset = 0x8c,
130             DriverClockingMMCMRead = 0x90,
131             DriverClockingMMCMWrite = 0x94,
132             DriverClockingMMCMDrdy = 0x98,
133             DriverClockingMMCMAdr = 0x9c,
134             DriverClockingMMCMDatW = 0xa0,
135             DriverClockingMMCMDatR = 0xa8
136         }
137     }
138 }
139