| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "aml-mipi-regs.h" |
| #include "aml-mipi.h" |
| #include <ddk/binding.h> |
| #include <ddk/debug.h> |
| #include <ddk/metadata.h> |
| #include <ddk/metadata/camera.h> |
| #include <fbl/alloc_checker.h> |
| #include <fbl/auto_call.h> |
| #include <fbl/unique_ptr.h> |
| #include <hw/reg.h> |
| #include <math.h> |
| #include <stdint.h> |
| #include <threads.h> |
| #include <zircon/types.h> |
| |
| // NOTE: A lot of magic numbers, they come from vendor |
| // source code. |
| |
| namespace camera { |
| |
| namespace { |
| |
| constexpr uint32_t kFrontEnd0Size = 0x400; |
| constexpr uint32_t kReaderSize = 0x100; |
| constexpr uint32_t kPixelSize = 0x100; |
| constexpr uint32_t kAlignSize = 0x200; |
| constexpr uint32_t kSize1Mb = 0x100000; |
| constexpr uint32_t kDdrModeSize = 48 * kSize1Mb; |
| |
| } // namespace |
| |
| uint32_t AmlMipiDevice::AdapGetDepth(const mipi_adap_info_t* info) { |
| uint32_t depth = 0; |
| switch (info->format) { |
| case IMAGE_FORMAT_AM_RAW6: |
| depth = 6; |
| break; |
| case IMAGE_FORMAT_AM_RAW7: |
| depth = 7; |
| break; |
| case IMAGE_FORMAT_AM_RAW8: |
| depth = 8; |
| break; |
| case IMAGE_FORMAT_AM_RAW10: |
| depth = 10; |
| break; |
| case IMAGE_FORMAT_AM_RAW12: |
| depth = 12; |
| break; |
| case IMAGE_FORMAT_AM_RAW14: |
| depth = 14; |
| break; |
| default: |
| zxlogf(ERROR, "%s, unsupported data format.\n", __func__); |
| break; |
| } |
| return depth; |
| } |
| |
| zx_status_t AmlMipiDevice::InitBuffer(const mipi_adap_info_t* info, size_t size) { |
| // Create a VMO for the ring buffer. |
| zx_status_t status = zx_vmo_create_contiguous(bti_.get(), size, 0, |
| ring_buffer_vmo_.reset_and_get_address()); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "%s failed to allocate ring buffer vmo - %d\n", __func__, status); |
| return status; |
| } |
| // Pin the ring buffer. |
| status = pinned_ring_buffer_.Pin(ring_buffer_vmo_, bti_, ZX_VM_PERM_READ | ZX_VM_PERM_WRITE); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "%s failed to pin ring buffer vmo - %d\n", __func__, status); |
| return status; |
| } |
| // Validate the pinned buffer. |
| if (pinned_ring_buffer_.region_count() != 1) { |
| zxlogf(ERROR, "%s buffer is not contiguous", __func__); |
| return ZX_ERR_NO_MEMORY; |
| } |
| return ZX_OK; |
| } |
| |
| /* |
| *======================== ADAPTER FRONTEND INTERFACE======================== |
| * Frontend is the HW block which configures if the data goes |
| * to the memory or takes the direct path. |
| * Register information 8.1.2 (page 312) |
| */ |
| |
| zx_status_t AmlMipiDevice::AdapFrontendInit(const mipi_adap_info_t* info) { |
| // TODO(braval): Add support for DOL_MODE |
| auto frontend_reg = mipi_adap_mmio_->View(FRONTEND_BASE, kFrontEnd0Size); |
| |
| // release from reset |
| frontend_reg.Write32(0x0, CSI2_CLK_RESET); |
| // enable frontend module clock and disable auto clock gating |
| frontend_reg.Write32(0x6, CSI2_CLK_RESET); |
| |
| if (info->mode == MIPI_MODES_DIR_MODE) { |
| if (info->path == MIPI_PATH_PATH0) { |
| // bit[0] 1:enable virtual channel 0 |
| frontend_reg.Write32(0x001f0001, CSI2_GEN_CTRL0); |
| } |
| } else if (info->mode == MIPI_MODES_DDR_MODE) { |
| if (info->path == MIPI_PATH_PATH0) { |
| frontend_reg.Write32(0x001f0011, CSI2_GEN_CTRL0); |
| } |
| } else { |
| zxlogf(ERROR, "%s, unsupported mode.\n", __func__); |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| |
| // applicable only to Raw data, direct MEM path |
| frontend_reg.Write32(0xffff0000, CSI2_X_START_END_MEM); |
| frontend_reg.Write32(0xffff0000, CSI2_Y_START_END_MEM); |
| |
| if (info->mode == MIPI_MODES_DDR_MODE) { |
| // config ddr_buf[0] address |
| frontend_reg.ModifyBits32( |
| static_cast<uint32_t>(pinned_ring_buffer_.region(0).phys_addr), |
| 0, 32, CSI2_DDR_START_PIX); |
| } else if (info->mode == MIPI_MODES_DOL_MODE) { |
| // TODO(braval): Add support for DOL_MODE. |
| } |
| |
| // set frame size |
| if (info->mode == MIPI_MODES_DOL_MODE) { |
| zxlogf(ERROR, "%s, unsupported mode.\n", __func__); |
| } else { |
| frontend_reg.Write32(0x00000780, CSI2_DDR_STRIDE_PIX); |
| } |
| |
| // enable vs_rise_isp interrupt & enable ddr_wdone interrupt |
| frontend_reg.Write32(0x5, CSI2_INTERRUPT_CTRL_STAT); |
| return ZX_OK; |
| } |
| |
| void AmlMipiDevice::AdapFrontEndStart(const mipi_adap_info_t* info) { |
| uint32_t width = info->resolution.width; |
| uint32_t depth, val; |
| depth = AdapGetDepth(info); |
| if (!depth) { |
| zxlogf(ERROR, "%s, unsupported format \n", __func__); |
| } |
| auto frontend_reg = mipi_adap_mmio_->View(FRONTEND_BASE, kFrontEnd0Size); |
| |
| frontend_reg.SetBits32(1 << 0, CSI2_GEN_CTRL0); |
| // This register information is missing in the datasheet. |
| // Best assumption is that theunit of values programmed in the register is 128bit. |
| val = static_cast<uint32_t>(ceil((width * depth) / static_cast<double>((8 * 16)))); |
| frontend_reg.ModifyBits32(val, 4, 28, CSI2_DDR_STRIDE_PIX); |
| } |
| |
| /* |
| *======================== ADAPTER READER INTERFACE========================== |
| * Reader is the HW block which is configured to read the data from |
| * the memory or direct oath. It also configures for multi-exposures. |
| * Register information 8.1.2 (page 322) |
| */ |
| |
| zx_status_t AmlMipiDevice::AdapReaderInit(const mipi_adap_info_t* info) { |
| // TODO(braval): Add support for DOL_MODE |
| auto reader_reg = mipi_adap_mmio_->View(RD_BASE, kReaderSize); |
| |
| if (info->mode == MIPI_MODES_DIR_MODE) { |
| reader_reg.Write32(0x02d00078, MIPI_ADAPT_DDR_RD0_CNTL1); |
| reader_reg.Write32(0xb5000005, MIPI_ADAPT_DDR_RD0_CNTL0); |
| } else if (info->mode == MIPI_MODES_DDR_MODE) { |
| reader_reg.Write32(0x02d00078, MIPI_ADAPT_DDR_RD0_CNTL1); |
| // ddr mode config frame address |
| reader_reg.ModifyBits32( |
| static_cast<uint32_t>(pinned_ring_buffer_.region(0).phys_addr), |
| 0, 32, MIPI_ADAPT_DDR_RD0_CNTL2); |
| reader_reg.Write32(0x70000001, MIPI_ADAPT_DDR_RD0_CNTL0); |
| } else { |
| zxlogf(ERROR, "%s, unsupported mode.\n", __func__); |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| return ZX_OK; |
| } |
| |
| void AmlMipiDevice::AdapReaderStart(const mipi_adap_info_t* info) { |
| uint32_t height = info->resolution.height; |
| uint32_t width = info->resolution.width; |
| uint32_t val, depth; |
| depth = AdapGetDepth(info); |
| if (!depth) { |
| zxlogf(ERROR, "%s, unsupported format \n", __func__); |
| } |
| |
| val = static_cast<uint32_t>(ceil((width * depth) / (8 * 16))); |
| |
| auto reader_reg = mipi_adap_mmio_->View(RD_BASE, kReaderSize); |
| |
| reader_reg.ModifyBits32(height, 16, 13, MIPI_ADAPT_DDR_RD0_CNTL1); |
| reader_reg.ModifyBits32(val, 0, 10, MIPI_ADAPT_DDR_RD0_CNTL1); |
| // TODO(braval): Add support for DOL_MODE |
| |
| reader_reg.SetBits32(1 << 0, MIPI_ADAPT_DDR_RD0_CNTL0); |
| } |
| |
| /* |
| *======================== ADAPTER PIXEL INTERFACE=========================== |
| * Setting the width to 1280 and default mode to RAW12 |
| * Register information 8.1.2 (page 330) |
| */ |
| |
| zx_status_t AmlMipiDevice::AdapPixelInit(const mipi_adap_info_t* info) { |
| // TODO(braval): Add support for DOL_MODE |
| auto pixel_reg = mipi_adap_mmio_->View(PIXEL_BASE, kPixelSize); |
| |
| if (info->mode == MIPI_MODES_DIR_MODE) { |
| // default width 1280 |
| pixel_reg.Write32(0x8000a500, MIPI_ADAPT_PIXEL0_CNTL0); |
| pixel_reg.Write32(0x80000808, MIPI_ADAPT_PIXEL0_CNTL1); |
| } else if (info->mode == MIPI_MODES_DDR_MODE) { |
| pixel_reg.Write32(0x0000a500, MIPI_ADAPT_PIXEL0_CNTL0); |
| pixel_reg.Write32(0x80000008, MIPI_ADAPT_PIXEL0_CNTL1); |
| } else { |
| zxlogf(ERROR, "%s, unsupported mode.\n", __func__); |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| return ZX_OK; |
| } |
| |
| void AmlMipiDevice::AdapPixelStart(const mipi_adap_info_t* info) { |
| auto pixel_reg = mipi_adap_mmio_->View(PIXEL_BASE, kPixelSize); |
| |
| pixel_reg.ModifyBits32(info->format, 13, 3, MIPI_ADAPT_PIXEL0_CNTL0); |
| pixel_reg.ModifyBits32(info->resolution.width, 0, 13, MIPI_ADAPT_PIXEL0_CNTL0); |
| |
| // TODO(braval): Add support for DOL_MODE |
| pixel_reg.SetBits32(1 << 31, MIPI_ADAPT_PIXEL0_CNTL1); |
| } |
| |
| /* |
| *======================== ADAPTER ALIGNMENT INTERFACE======================= |
| * Register information 8.1.2 (page 333) |
| */ |
| |
| zx_status_t AmlMipiDevice::AdapAlignInit(const mipi_adap_info_t* info) { |
| // TODO(braval): Add support for DDR_MODE & DOL_MODE |
| auto align_reg = mipi_adap_mmio_->View(ALIGN_BASE, kAlignSize); |
| |
| if (info->mode == MIPI_MODES_DOL_MODE) { |
| zxlogf(ERROR, "%s, unsupported mode.\n", __func__); |
| return ZX_ERR_NOT_SUPPORTED; |
| } else { |
| // default width 1280, height 720 |
| align_reg.Write32(0x02f80528, MIPI_ADAPT_ALIG_CNTL0); // associate width and height |
| align_reg.Write32(0x05000000, MIPI_ADAPT_ALIG_CNTL1); // associate width |
| align_reg.Write32(0x02d00000, MIPI_ADAPT_ALIG_CNTL2); // associate height |
| } |
| |
| if (info->mode == MIPI_MODES_DIR_MODE) { |
| align_reg.Write32(0x00fff011, MIPI_ADAPT_ALIG_CNTL6); |
| align_reg.Write32(0xc350c000, MIPI_ADAPT_ALIG_CNTL7); |
| align_reg.Write32(0x85231020, MIPI_ADAPT_ALIG_CNTL8); |
| } else if (info->mode == MIPI_MODES_DDR_MODE) { |
| align_reg.Write32(0x00fff001, MIPI_ADAPT_ALIG_CNTL6); |
| align_reg.Write32(0x0, MIPI_ADAPT_ALIG_CNTL7); |
| align_reg.Write32(0x80000020, MIPI_ADAPT_ALIG_CNTL8); |
| } else { |
| zxlogf(ERROR, "%s, unsupported mode.\n", __func__); |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| |
| align_reg.Write32(0x00082000, MIPI_ADAPT_IRQ_MASK0); |
| return ZX_OK; |
| } |
| |
| void AmlMipiDevice::AdapAlignStart(const mipi_adap_info_t* info) { |
| auto align_reg = mipi_adap_mmio_->View(ALIGN_BASE, kAlignSize); |
| |
| uint32_t width, height, alig_width, alig_height, val; |
| width = info->resolution.width; |
| height = info->resolution.height; |
| alig_width = width + 40; // hblank > 32 cycles |
| alig_height = height + 60; // vblank > 48 lines |
| val = width + 35; // width < val < alig_width |
| align_reg.ModifyBits32(alig_width, 0, 13, MIPI_ADAPT_ALIG_CNTL0); |
| align_reg.ModifyBits32(alig_height, 16, 13, MIPI_ADAPT_ALIG_CNTL0); |
| align_reg.ModifyBits32(width, 16, 13, MIPI_ADAPT_ALIG_CNTL1); |
| align_reg.ModifyBits32(height, 16, 13, MIPI_ADAPT_ALIG_CNTL2); |
| align_reg.ModifyBits32(val, 16, 13, MIPI_ADAPT_ALIG_CNTL8); |
| align_reg.ModifyBits32(1, 31, 1, MIPI_ADAPT_ALIG_CNTL8); |
| } |
| |
| /* |
| *======================== ADAPTER INTERFACE========================== |
| */ |
| |
| int AmlMipiDevice::AdapterIrqHandler() { |
| zxlogf(INFO, "%s start\n", __func__); |
| zx_status_t status; |
| |
| while (running_.load()) { |
| status = adap_irq_.wait(NULL); |
| if (status != ZX_OK) { |
| return status; |
| } |
| // TODO(braval) : Add ISR implementation here. |
| } |
| return status; |
| } |
| |
| zx_status_t AmlMipiDevice::MipiAdapInit(const mipi_adap_info_t* info) { |
| |
| // TODO(braval): Add support for DOL_MODE |
| |
| if (info->mode == MIPI_MODES_DDR_MODE) { |
| zx_status_t status = InitBuffer(info, kDdrModeSize); |
| if (status != ZX_OK) { |
| return status; |
| } |
| // TODO(braval): Setup ring buffers phys. address. |
| |
| // Start thermal notification thread. |
| auto start_thread = [](void* arg) -> int { |
| return static_cast<AmlMipiDevice*>(arg)->AdapterIrqHandler(); |
| }; |
| |
| running_.store(true); |
| |
| int rc = thrd_create_with_name(&irq_thread_, |
| start_thread, |
| this, |
| "adapter_irq_thread"); |
| if (rc != thrd_success) { |
| return ZX_ERR_INTERNAL; |
| } |
| } |
| |
| // Reset the Frontend |
| auto frontend_reg = mipi_adap_mmio_->View(FRONTEND_BASE, kFrontEnd0Size); |
| frontend_reg.Write32(1, CSI2_CLK_RESET); |
| frontend_reg.Write32(0, CSI2_CLK_RESET); |
| |
| // default setting : 720p & RAW12 |
| zx_status_t status = AdapFrontendInit(info); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| status = AdapReaderInit(info); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| status = AdapPixelInit(info); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| status = AdapAlignInit(info); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| return status; |
| } |
| |
| void AmlMipiDevice::MipiAdapStart(const mipi_adap_info_t* info) { |
| AdapAlignStart(info); |
| AdapPixelStart(info); |
| AdapReaderStart(info); |
| AdapFrontEndStart(info); |
| } |
| |
| void AmlMipiDevice::MipiAdapReset() { |
| auto frontend_reg = mipi_adap_mmio_->View(FRONTEND_BASE, kFrontEnd0Size); |
| auto align_reg = mipi_adap_mmio_->View(ALIGN_BASE, kAlignSize); |
| |
| frontend_reg.Write32(0x0, CSI2_CLK_RESET); |
| frontend_reg.Write32(0x6, CSI2_CLK_RESET); |
| frontend_reg.Write32(0x001f0000, CSI2_GEN_CTRL0); |
| align_reg.Write32(0xf0000000, MIPI_OTHER_CNTL0); |
| align_reg.Write32(0x00000000, MIPI_OTHER_CNTL0); |
| } |
| |
| } // namespace camera |