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.IO;
10 using Antmicro.Renode.Core;
11 using Antmicro.Renode.Core.Structure.Registers;
12 using Antmicro.Renode.Logging;
13 using Antmicro.Renode.Peripherals.Sensor;
14 using Antmicro.Renode.Peripherals.I2C;
15 using Antmicro.Renode.Peripherals.SPI;
16 using Antmicro.Renode.Utilities;
17 
18 namespace Antmicro.Renode.Peripherals.Sensors
19 {
20     public class OV2640 : II2CPeripheral, IProvidesRegisterCollection<ByteRegisterCollection>, ISensor
21     {
OV2640(IPeripheral parent)22         public OV2640(IPeripheral parent)
23         {
24             this.parent = parent;
25 
26             dspRegisters = new ByteRegisterCollection(this);
27             sensorRegisters = new ByteRegisterCollection(this);
28 
29             DefineDSPRegisters();
30             DefineSensorRegisters();
31         }
32 
FinishTransmission()33         public void FinishTransmission()
34         {
35             parent.NoisyLog("OV2640: Finishing transmission, going idle");
36             state = State.Idle;
37         }
38 
Write(byte[] data)39         public void Write(byte[] data)
40         {
41             parent.NoisyLog("OV2640: Received the following bytes: {0}", Misc.PrettyPrintCollectionHex(data));
42             foreach(var d in data)
43             {
44                 Write(d);
45             }
46         }
47 
Read(int count)48         public byte[] Read(int count)
49         {
50             parent.NoisyLog("OV2640: Reading from the device in state {0}", state);
51             switch(state)
52             {
53                 case State.Processing:
54                 {
55                     var result = RegistersCollection.Read(address);
56                     parent.NoisyLog("OV2640: Read 0x{0:X} from register {1}", result, DecodeRegister(address));
57                     return new [] { result };
58                 }
59 
60                 default:
61                     parent.Log(LogLevel.Error, "OV2640: Reading in an unexpected state: {0}", state);
62                     return new byte[0];
63             }
64         }
65 
Reset()66         public void Reset()
67         {
68             state = State.Idle;
69             sensorRegisterBankSelected = false;
70             address = 0;
71 
72             dspRegisters.Reset();
73             sensorRegisters.Reset();
74         }
75 
76         public OutputFormat Format => outputFormat.Value;
77 
78         public ByteRegisterCollection RegistersCollection => sensorRegisterBankSelected ? sensorRegisters : dspRegisters;
79 
80         public uint OutputWidth
81         {
82             get
83             {
84                 return (uint)((zoomOutputWidthHigh.Value << 8) + zoomOutputWidthLow.Value) * 4;
85             }
86         }
87 
88         public uint OutputHeight
89         {
90             get
91             {
92                 return (uint)((zoomOutputHeightHigh.Value << 8) + zoomOutputHeightLow.Value) * 4;
93             }
94         }
95 
Write(byte b)96         private void Write(byte b)
97         {
98             parent.NoisyLog("OV2640: Received byte 0x{0:X} in state {1}", b, state);
99             switch(state)
100             {
101                 case State.Idle:
102                 {
103                     address = b;
104                     state = State.Processing;
105 
106                     parent.NoisyLog("OV2640: Selected register {0}", DecodeRegister(address));
107                     break;
108                 }
109 
110                 case State.Processing:
111                 {
112                     parent.NoisyLog("OV2640: Writing 0x{0:X} to register {1}", b, DecodeRegister(address));
113                     RegistersCollection.Write(address, b);
114                     break;
115                 }
116 
117                 default:
118                     parent.Log(LogLevel.Error, "OV2640: Writing byte in an unexpected state: {0}", state);
119                     break;
120             }
121         }
122 
DefineDSPRegisters()123         private void DefineDSPRegisters()
124         {
125             DSPRegister.ImageMode.Define(dspRegisters)
126                 .WithTag("Byte swap enable for DVP", 0, 1)
127                 .WithTag("HREF timing select in DVP JPEG output mode", 1, 1)
128                 .WithEnumField<ByteRegister, OutputFormat>(2, 2, out outputFormat, name: "DVP output format")
129                 .WithTag("JPEG output enable", 4, 1)
130                 .WithReservedBits(5, 1)
131                 .WithTag("Y8 enable for DVP", 6, 1)
132                 .WithReservedBits(7, 1)
133             ;
134 
135             DSPRegister.ZoomOutputHeight.Define(dspRegisters)
136                 .WithValueField(0, 8, out zoomOutputHeightLow, name: "OUTH[7:0] (real/4)")
137             ;
138 
139             DSPRegister.ZoomOutputWidth.Define(dspRegisters)
140                 .WithValueField(0, 8, out zoomOutputWidthLow, name: "OUTW[7:0] (real/4)")
141             ;
142 
143             DSPRegister.ZoomOutputRest.Define(dspRegisters)
144                 .WithValueField(0, 2, out zoomOutputWidthHigh, name: "OUTW[9:8] (real/4)")
145                 .WithValueField(2, 1, out zoomOutputHeightHigh, name: "OUTH[8] (real/4)")
146                 .WithReservedBits(3, 1)
147                 .WithTag("ZMSPD (zoom speed)", 4, 3)
148             ;
149 
150             DefineBankSelectRegister(dspRegisters);
151         }
152 
DefineSensorRegisters()153         private void DefineSensorRegisters()
154         {
155             SensorRegister.COM7.Define(sensorRegisters)
156                 .WithReservedBits(0, 1)
157                 .WithTag("Color bar test pattern", 1, 1)
158                 .WithTag("Zoom mode", 2, 1)
159                 .WithReservedBits(3, 1)
160                 .WithEnumField<ByteRegister, ResolutionMode>(4, 3, out resolution, name: "Resolution selection",
161                     writeCallback: (_, val) => parent.Log(LogLevel.Debug, "Resolution set to {0}", val))
162                 .WithFlag(7, name: "SRST", writeCallback: (_, val) =>
163                 {
164                     if(!val)
165                     {
166                         return;
167                     }
168 
169                     parent.NoisyLog("OV2640: Initiating system reset");
170                     Reset();
171                 })
172             ;
173 
174             DefineBankSelectRegister(sensorRegisters);
175         }
176 
DefineBankSelectRegister(ByteRegisterCollection registers)177         private void DefineBankSelectRegister(ByteRegisterCollection registers)
178         {
179             // this register is the same for both banks
180             // so there is no difference which enum we use
181             DSPRegister.RegisterBankSelect.Define(registers)
182                 .WithFlag(0, name: "Register bank select",
183                     valueProviderCallback: _ => sensorRegisterBankSelected,
184                     writeCallback: (_, val) =>
185                     {
186                         sensorRegisterBankSelected = val;
187                         parent.NoisyLog("OV2640: Register bank selected: {0}", sensorRegisterBankSelected ? "sensor" : "DSP");
188                     })
189                 .WithReservedBits(1, 7)
190             ;
191         }
192 
DecodeRegister(byte address)193         private string DecodeRegister(byte address)
194         {
195             return "{0} (0x{1:X})".FormatWith(sensorRegisterBankSelected
196                                                  ? ((SensorRegister)address).ToString()
197                                                  : ((DSPRegister)address).ToString(),
198                                              address);
199         }
200 
201         private State state;
202         private bool sensorRegisterBankSelected;
203         private byte address;
204 
205         private IValueRegisterField zoomOutputHeightHigh;
206         private IValueRegisterField zoomOutputHeightLow;
207 
208         private IValueRegisterField zoomOutputWidthHigh;
209         private IValueRegisterField zoomOutputWidthLow;
210 
211         private IEnumRegisterField<OutputFormat> outputFormat;
212         private IEnumRegisterField<ResolutionMode> resolution;
213 
214         private readonly ByteRegisterCollection dspRegisters;
215         private readonly ByteRegisterCollection sensorRegisters;
216 
217         private readonly IPeripheral parent;
218 
219         private enum State
220         {
221             Idle,
222             Processing
223         }
224 
225         private enum ResolutionMode
226         {
227             UXGA_1600_1200 = 0,
228             CIF_352_288 = 2,
229             SVGA_800_600 = 4
230         }
231 
232         public enum OutputFormat
233         {
234             YUV422 = 0,
235             RAW10_DVP = 1,
236             RGB565 = 2,
237             Reserved = 3
238         }
239 
240         private enum DSPRegister
241         {
242             // unlisted registers are reserved
243             BypassDSP = 0x5,
244 
245             QuantizationScaleFactor = 0x44,
246 
247             ControlI = 0x50,
248             HorizontalSizeLow = 0x51,
249             VerticalSizeLow = 0x52,
250             OffsetXLow = 0x53,
251             OffsetYLow = 0x54,
252             SizeOffsetHigh = 0x55,
253 
254             DPRP = 0x56,
255             Test = 0x57,
256 
257             ZoomOutputWidth = 0x5A,
258             ZoomOutputHeight = 0x5B,
259             ZoomOutputRest = 0x5C,
260 
261             SDEIndirectRegisterAccess_Address = 0x7C,
262             SDEIndirectRegisterAccess_Data = 0x7D,
263 
264             Control2_ModuleEnable = 0x86,
265             Control3_ModuleEnable = 0x87,
266 
267             SIZEL = 0x8C,
268 
269             HSIZE8 = 0xC0,
270             VSIZE8 = 0xC1,
271             Control0_ModuleEnable = 0xC2,
272             Control1_ModuleEnable = 0xC3,
273 
274             R_DVP_SP = 0xD3,
275 
276             ImageMode = 0xDA,
277 
278             Reset = 0xE0,
279 
280             MS_SP = 0xF0,
281 
282             SS_ID = 0xF7,
283             SS_CTRL = 0xF8,
284             MC_BIST = 0xF9,
285             MC_AL = 0xFA,
286             MC_AH = 0xFB,
287             MC_D = 0xFC,
288             P_CMD = 0xFD,
289             P_STATUS = 0xFE,
290             RegisterBankSelect = 0xFF
291         }
292 
293         private enum SensorRegister
294         {
295             // unlisted registers are reserved
296             GAIN = 0x00,
297 
298             COM1 = 0x3,
299             REG04 = 0x04,
300 
301             REG08 = 0x08,
302             COM2 = 0x09,
303             PIDH = 0x0A,
304             PIDL = 0x0B,
305             COM3 = 0x0C,
306             COM4 = 0x0D,
307 
308             AEC = 0x10,
309             CLKRC = 0x11,
310             COM7 = 0x12,
311             COM8 = 0x13,
312             COM9 = 0x14,
313             COM10 = 0x15,
314 
315             HREFST = 0x17,
316             HREFEND = 0x18,
317             VSTRT = 0x19,
318             VEND = 0x1A,
319 
320             MIDH = 0x1C,
321             MIDL = 0x1D,
322 
323             AEW = 0x24,
324             AWB = 0x25,
325             VV = 0x26,
326 
327             REG2A = 0x2A,
328             FRARL = 0x2B,
329 
330             ADDVSL = 0x2D,
331             ADDVSH = 0x2E,
332             YAVG = 0x2F,
333             HSDY = 0x30,
334             HEDY = 0x31,
335             REG32 = 0x32,
336 
337             ARCOM2 = 0x34,
338 
339             REG45 = 0x45,
340             FLL = 0x46,
341             FLH = 0x47,
342             COM19 = 0x48,
343             ZOOMS = 0x49,
344 
345             COM22 = 0x4B,
346 
347             COM25 = 0x4E,
348             BD50 = 0x4F,
349             BD60 = 0x50,
350 
351             REG5D = 0x5D,
352             REG5E = 0x5E,
353             REG5F = 0x5F,
354             REG60 = 0x60,
355 
356             HISTO_LOW = 0x61,
357             HISTO_HIGH = 0x62,
358 
359             RegisterBankSelect = 0xFF
360         }
361     }
362 }
363