1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "aml-mipi-regs.h"
6 #include "aml-mipi.h"
7 #include <ddk/binding.h>
8 #include <ddk/debug.h>
9 #include <ddk/metadata.h>
10 #include <ddk/metadata/camera.h>
11 #include <fbl/alloc_checker.h>
12 #include <fbl/auto_call.h>
13 #include <fbl/unique_ptr.h>
14 #include <hw/reg.h>
15 #include <math.h>
16 #include <stdint.h>
17 #include <threads.h>
18 #include <zircon/types.h>
19 
20 // NOTE: A lot of magic numbers, they come from vendor
21 //       source code.
22 
23 namespace camera {
24 
25 namespace {
26 
27 constexpr uint32_t kFrontEnd0Size = 0x400;
28 constexpr uint32_t kReaderSize = 0x100;
29 constexpr uint32_t kPixelSize = 0x100;
30 constexpr uint32_t kAlignSize = 0x200;
31 constexpr uint32_t kSize1Mb = 0x100000;
32 constexpr uint32_t kDdrModeSize = 48 * kSize1Mb;
33 
34 } // namespace
35 
AdapGetDepth(const mipi_adap_info_t * info)36 uint32_t AmlMipiDevice::AdapGetDepth(const mipi_adap_info_t* info) {
37     uint32_t depth = 0;
38     switch (info->format) {
39     case IMAGE_FORMAT_AM_RAW6:
40         depth = 6;
41         break;
42     case IMAGE_FORMAT_AM_RAW7:
43         depth = 7;
44         break;
45     case IMAGE_FORMAT_AM_RAW8:
46         depth = 8;
47         break;
48     case IMAGE_FORMAT_AM_RAW10:
49         depth = 10;
50         break;
51     case IMAGE_FORMAT_AM_RAW12:
52         depth = 12;
53         break;
54     case IMAGE_FORMAT_AM_RAW14:
55         depth = 14;
56         break;
57     default:
58         zxlogf(ERROR, "%s, unsupported data format.\n", __func__);
59         break;
60     }
61     return depth;
62 }
63 
InitBuffer(const mipi_adap_info_t * info,size_t size)64 zx_status_t AmlMipiDevice::InitBuffer(const mipi_adap_info_t* info, size_t size) {
65     // Create a VMO for the ring buffer.
66     zx_status_t status = zx_vmo_create_contiguous(bti_.get(), size, 0,
67                                                   ring_buffer_vmo_.reset_and_get_address());
68     if (status != ZX_OK) {
69         zxlogf(ERROR, "%s failed to allocate ring buffer vmo - %d\n", __func__, status);
70         return status;
71     }
72     // Pin the ring buffer.
73     status = pinned_ring_buffer_.Pin(ring_buffer_vmo_, bti_, ZX_VM_PERM_READ | ZX_VM_PERM_WRITE);
74     if (status != ZX_OK) {
75         zxlogf(ERROR, "%s failed to pin ring buffer vmo - %d\n", __func__, status);
76         return status;
77     }
78     // Validate the pinned buffer.
79     if (pinned_ring_buffer_.region_count() != 1) {
80         zxlogf(ERROR, "%s buffer is not contiguous", __func__);
81         return ZX_ERR_NO_MEMORY;
82     }
83     return ZX_OK;
84 }
85 
86 /*
87  *======================== ADAPTER FRONTEND INTERFACE========================
88  * Frontend is the HW block which configures if the data goes
89  * to the memory or takes the direct path.
90  * Register information 8.1.2 (page 312)
91  */
92 
AdapFrontendInit(const mipi_adap_info_t * info)93 zx_status_t AmlMipiDevice::AdapFrontendInit(const mipi_adap_info_t* info) {
94     // TODO(braval):    Add support for DOL_MODE
95     auto frontend_reg = mipi_adap_mmio_->View(FRONTEND_BASE, kFrontEnd0Size);
96 
97     // release from reset
98     frontend_reg.Write32(0x0, CSI2_CLK_RESET);
99     // enable frontend module clock and disable auto clock gating
100     frontend_reg.Write32(0x6, CSI2_CLK_RESET);
101 
102     if (info->mode == MIPI_MODES_DIR_MODE) {
103         if (info->path == MIPI_PATH_PATH0) {
104             // bit[0] 1:enable virtual channel 0
105             frontend_reg.Write32(0x001f0001, CSI2_GEN_CTRL0);
106         }
107     } else if (info->mode == MIPI_MODES_DDR_MODE) {
108         if (info->path == MIPI_PATH_PATH0) {
109             frontend_reg.Write32(0x001f0011, CSI2_GEN_CTRL0);
110         }
111     } else {
112         zxlogf(ERROR, "%s, unsupported mode.\n", __func__);
113         return ZX_ERR_NOT_SUPPORTED;
114     }
115 
116     // applicable only to Raw data, direct MEM path
117     frontend_reg.Write32(0xffff0000, CSI2_X_START_END_MEM);
118     frontend_reg.Write32(0xffff0000, CSI2_Y_START_END_MEM);
119 
120     if (info->mode == MIPI_MODES_DDR_MODE) {
121         // config ddr_buf[0] address
122         frontend_reg.ModifyBits32(
123             static_cast<uint32_t>(pinned_ring_buffer_.region(0).phys_addr),
124             0, 32, CSI2_DDR_START_PIX);
125     } else if (info->mode == MIPI_MODES_DOL_MODE) {
126         // TODO(braval):    Add support for DOL_MODE.
127     }
128 
129     // set frame size
130     if (info->mode == MIPI_MODES_DOL_MODE) {
131         zxlogf(ERROR, "%s, unsupported mode.\n", __func__);
132     } else {
133         frontend_reg.Write32(0x00000780, CSI2_DDR_STRIDE_PIX);
134     }
135 
136     // enable vs_rise_isp interrupt & enable ddr_wdone interrupt
137     frontend_reg.Write32(0x5, CSI2_INTERRUPT_CTRL_STAT);
138     return ZX_OK;
139 }
140 
AdapFrontEndStart(const mipi_adap_info_t * info)141 void AmlMipiDevice::AdapFrontEndStart(const mipi_adap_info_t* info) {
142     uint32_t width = info->resolution.width;
143     uint32_t depth, val;
144     depth = AdapGetDepth(info);
145     if (!depth) {
146         zxlogf(ERROR, "%s, unsupported format \n", __func__);
147     }
148     auto frontend_reg = mipi_adap_mmio_->View(FRONTEND_BASE, kFrontEnd0Size);
149 
150     frontend_reg.SetBits32(1 << 0, CSI2_GEN_CTRL0);
151     // This register information is missing in the datasheet.
152     // Best assumption is that theunit of values programmed in the register is 128bit.
153     val = static_cast<uint32_t>(ceil((width * depth) / static_cast<double>((8 * 16))));
154     frontend_reg.ModifyBits32(val, 4, 28, CSI2_DDR_STRIDE_PIX);
155 }
156 
157 /*
158  *======================== ADAPTER READER INTERFACE==========================
159  * Reader is the HW block which is configured to read the data from
160  * the memory or direct oath. It also configures for multi-exposures.
161  * Register information 8.1.2 (page 322)
162  */
163 
AdapReaderInit(const mipi_adap_info_t * info)164 zx_status_t AmlMipiDevice::AdapReaderInit(const mipi_adap_info_t* info) {
165     // TODO(braval):    Add support for DOL_MODE
166     auto reader_reg = mipi_adap_mmio_->View(RD_BASE, kReaderSize);
167 
168     if (info->mode == MIPI_MODES_DIR_MODE) {
169         reader_reg.Write32(0x02d00078, MIPI_ADAPT_DDR_RD0_CNTL1);
170         reader_reg.Write32(0xb5000005, MIPI_ADAPT_DDR_RD0_CNTL0);
171     } else if (info->mode == MIPI_MODES_DDR_MODE) {
172         reader_reg.Write32(0x02d00078, MIPI_ADAPT_DDR_RD0_CNTL1);
173         // ddr mode config frame address
174         reader_reg.ModifyBits32(
175             static_cast<uint32_t>(pinned_ring_buffer_.region(0).phys_addr),
176             0, 32, MIPI_ADAPT_DDR_RD0_CNTL2);
177         reader_reg.Write32(0x70000001, MIPI_ADAPT_DDR_RD0_CNTL0);
178     } else {
179         zxlogf(ERROR, "%s, unsupported mode.\n", __func__);
180         return ZX_ERR_NOT_SUPPORTED;
181     }
182     return ZX_OK;
183 }
184 
AdapReaderStart(const mipi_adap_info_t * info)185 void AmlMipiDevice::AdapReaderStart(const mipi_adap_info_t* info) {
186     uint32_t height = info->resolution.height;
187     uint32_t width = info->resolution.width;
188     uint32_t val, depth;
189     depth = AdapGetDepth(info);
190     if (!depth) {
191         zxlogf(ERROR, "%s, unsupported format \n", __func__);
192     }
193 
194     val = static_cast<uint32_t>(ceil((width * depth) / (8 * 16)));
195 
196     auto reader_reg = mipi_adap_mmio_->View(RD_BASE, kReaderSize);
197 
198     reader_reg.ModifyBits32(height, 16, 13, MIPI_ADAPT_DDR_RD0_CNTL1);
199     reader_reg.ModifyBits32(val, 0, 10, MIPI_ADAPT_DDR_RD0_CNTL1);
200     // TODO(braval):    Add support for DOL_MODE
201 
202     reader_reg.SetBits32(1 << 0, MIPI_ADAPT_DDR_RD0_CNTL0);
203 }
204 
205 /*
206  *======================== ADAPTER PIXEL INTERFACE===========================
207  * Setting the width to 1280 and default mode to RAW12
208  * Register information 8.1.2 (page 330)
209  */
210 
AdapPixelInit(const mipi_adap_info_t * info)211 zx_status_t AmlMipiDevice::AdapPixelInit(const mipi_adap_info_t* info) {
212     // TODO(braval):    Add support for  DOL_MODE
213     auto pixel_reg = mipi_adap_mmio_->View(PIXEL_BASE, kPixelSize);
214 
215     if (info->mode == MIPI_MODES_DIR_MODE) {
216         // default width 1280
217         pixel_reg.Write32(0x8000a500, MIPI_ADAPT_PIXEL0_CNTL0);
218         pixel_reg.Write32(0x80000808, MIPI_ADAPT_PIXEL0_CNTL1);
219     } else if (info->mode == MIPI_MODES_DDR_MODE) {
220         pixel_reg.Write32(0x0000a500, MIPI_ADAPT_PIXEL0_CNTL0);
221         pixel_reg.Write32(0x80000008, MIPI_ADAPT_PIXEL0_CNTL1);
222     } else {
223         zxlogf(ERROR, "%s, unsupported mode.\n", __func__);
224         return ZX_ERR_NOT_SUPPORTED;
225     }
226     return ZX_OK;
227 }
228 
AdapPixelStart(const mipi_adap_info_t * info)229 void AmlMipiDevice::AdapPixelStart(const mipi_adap_info_t* info) {
230     auto pixel_reg = mipi_adap_mmio_->View(PIXEL_BASE, kPixelSize);
231 
232     pixel_reg.ModifyBits32(info->format, 13, 3, MIPI_ADAPT_PIXEL0_CNTL0);
233     pixel_reg.ModifyBits32(info->resolution.width, 0, 13, MIPI_ADAPT_PIXEL0_CNTL0);
234 
235     // TODO(braval):    Add support for DOL_MODE
236     pixel_reg.SetBits32(1 << 31, MIPI_ADAPT_PIXEL0_CNTL1);
237 }
238 
239 /*
240  *======================== ADAPTER ALIGNMENT INTERFACE=======================
241  * Register information 8.1.2 (page 333)
242  */
243 
AdapAlignInit(const mipi_adap_info_t * info)244 zx_status_t AmlMipiDevice::AdapAlignInit(const mipi_adap_info_t* info) {
245     // TODO(braval):    Add support for DDR_MODE & DOL_MODE
246     auto align_reg = mipi_adap_mmio_->View(ALIGN_BASE, kAlignSize);
247 
248     if (info->mode == MIPI_MODES_DOL_MODE) {
249         zxlogf(ERROR, "%s, unsupported mode.\n", __func__);
250         return ZX_ERR_NOT_SUPPORTED;
251     } else {
252         // default width 1280, height 720
253         align_reg.Write32(0x02f80528, MIPI_ADAPT_ALIG_CNTL0); // associate width and height
254         align_reg.Write32(0x05000000, MIPI_ADAPT_ALIG_CNTL1); // associate width
255         align_reg.Write32(0x02d00000, MIPI_ADAPT_ALIG_CNTL2); // associate height
256     }
257 
258     if (info->mode == MIPI_MODES_DIR_MODE) {
259         align_reg.Write32(0x00fff011, MIPI_ADAPT_ALIG_CNTL6);
260         align_reg.Write32(0xc350c000, MIPI_ADAPT_ALIG_CNTL7);
261         align_reg.Write32(0x85231020, MIPI_ADAPT_ALIG_CNTL8);
262     } else if (info->mode == MIPI_MODES_DDR_MODE) {
263         align_reg.Write32(0x00fff001, MIPI_ADAPT_ALIG_CNTL6);
264         align_reg.Write32(0x0, MIPI_ADAPT_ALIG_CNTL7);
265         align_reg.Write32(0x80000020, MIPI_ADAPT_ALIG_CNTL8);
266     } else {
267         zxlogf(ERROR, "%s, unsupported mode.\n", __func__);
268         return ZX_ERR_NOT_SUPPORTED;
269     }
270 
271     align_reg.Write32(0x00082000, MIPI_ADAPT_IRQ_MASK0);
272     return ZX_OK;
273 }
274 
AdapAlignStart(const mipi_adap_info_t * info)275 void AmlMipiDevice::AdapAlignStart(const mipi_adap_info_t* info) {
276     auto align_reg = mipi_adap_mmio_->View(ALIGN_BASE, kAlignSize);
277 
278     uint32_t width, height, alig_width, alig_height, val;
279     width = info->resolution.width;
280     height = info->resolution.height;
281     alig_width = width + 40;   // hblank > 32 cycles
282     alig_height = height + 60; // vblank > 48 lines
283     val = width + 35;          // width < val < alig_width
284     align_reg.ModifyBits32(alig_width, 0, 13, MIPI_ADAPT_ALIG_CNTL0);
285     align_reg.ModifyBits32(alig_height, 16, 13, MIPI_ADAPT_ALIG_CNTL0);
286     align_reg.ModifyBits32(width, 16, 13, MIPI_ADAPT_ALIG_CNTL1);
287     align_reg.ModifyBits32(height, 16, 13, MIPI_ADAPT_ALIG_CNTL2);
288     align_reg.ModifyBits32(val, 16, 13, MIPI_ADAPT_ALIG_CNTL8);
289     align_reg.ModifyBits32(1, 31, 1, MIPI_ADAPT_ALIG_CNTL8);
290 }
291 
292 /*
293  *======================== ADAPTER INTERFACE==========================
294  */
295 
AdapterIrqHandler()296 int AmlMipiDevice::AdapterIrqHandler() {
297     zxlogf(INFO, "%s start\n", __func__);
298     zx_status_t status;
299 
300     while (running_.load()) {
301         status = adap_irq_.wait(NULL);
302         if (status != ZX_OK) {
303             return status;
304         }
305         // TODO(braval) : Add ISR implementation here.
306     }
307     return status;
308 }
309 
MipiAdapInit(const mipi_adap_info_t * info)310 zx_status_t AmlMipiDevice::MipiAdapInit(const mipi_adap_info_t* info) {
311 
312     // TODO(braval):    Add support for DOL_MODE
313 
314     if (info->mode == MIPI_MODES_DDR_MODE) {
315         zx_status_t status = InitBuffer(info, kDdrModeSize);
316         if (status != ZX_OK) {
317             return status;
318         }
319         // TODO(braval): Setup ring buffers phys. address.
320 
321         // Start thermal notification thread.
322         auto start_thread = [](void* arg) -> int {
323             return static_cast<AmlMipiDevice*>(arg)->AdapterIrqHandler();
324         };
325 
326         running_.store(true);
327 
328         int rc = thrd_create_with_name(&irq_thread_,
329                                        start_thread,
330                                        this,
331                                        "adapter_irq_thread");
332         if (rc != thrd_success) {
333             return ZX_ERR_INTERNAL;
334         }
335     }
336 
337     // Reset the Frontend
338     auto frontend_reg = mipi_adap_mmio_->View(FRONTEND_BASE, kFrontEnd0Size);
339     frontend_reg.Write32(1, CSI2_CLK_RESET);
340     frontend_reg.Write32(0, CSI2_CLK_RESET);
341 
342     // default setting : 720p & RAW12
343     zx_status_t status = AdapFrontendInit(info);
344     if (status != ZX_OK) {
345         return status;
346     }
347 
348     status = AdapReaderInit(info);
349     if (status != ZX_OK) {
350         return status;
351     }
352 
353     status = AdapPixelInit(info);
354     if (status != ZX_OK) {
355         return status;
356     }
357 
358     status = AdapAlignInit(info);
359     if (status != ZX_OK) {
360         return status;
361     }
362 
363     return status;
364 }
365 
MipiAdapStart(const mipi_adap_info_t * info)366 void AmlMipiDevice::MipiAdapStart(const mipi_adap_info_t* info) {
367     AdapAlignStart(info);
368     AdapPixelStart(info);
369     AdapReaderStart(info);
370     AdapFrontEndStart(info);
371 }
372 
MipiAdapReset()373 void AmlMipiDevice::MipiAdapReset() {
374     auto frontend_reg = mipi_adap_mmio_->View(FRONTEND_BASE, kFrontEnd0Size);
375     auto align_reg = mipi_adap_mmio_->View(ALIGN_BASE, kAlignSize);
376 
377     frontend_reg.Write32(0x0, CSI2_CLK_RESET);
378     frontend_reg.Write32(0x6, CSI2_CLK_RESET);
379     frontend_reg.Write32(0x001f0000, CSI2_GEN_CTRL0);
380     align_reg.Write32(0xf0000000, MIPI_OTHER_CNTL0);
381     align_reg.Write32(0x00000000, MIPI_OTHER_CNTL0);
382 }
383 
384 } // namespace camera
385