blob: d29bf0b84b4f4527343e9347baaf34b3555d8af8 [file] [log] [blame]
// 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