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