blob: ec77f3299382b1645a79e9bd597e067e0f5e59e5 [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 "imx227.h"
#include <endian.h>
#include <lib/device-protocol/i2c.h>
#include <lib/driver-unit-test/utils.h>
#include <lib/fit/result.h>
#include <threads.h>
#include <zircon/types.h>
#include <ddk/binding.h>
#include <ddk/debug.h>
#include <ddk/metadata.h>
#include <ddk/metadata/camera.h>
#include <ddk/protocol/camera/sensor.h>
#include <fbl/auto_call.h>
#include <fbl/auto_lock.h>
#include <hw/reg.h>
#include "src/camera/drivers/sensors/imx227/constants.h"
#include "src/camera/drivers/sensors/imx227/imx227_modes.h"
#include "src/camera/drivers/sensors/imx227/imx227_seq.h"
#include "src/camera/drivers/sensors/imx227/mipi_ccs_regs.h"
namespace camera {
namespace {
constexpr uint8_t kRaw10Bits = 10;
constexpr uint8_t kRaw12Bits = 12;
constexpr uint8_t kByteMask = 0xFF;
constexpr uint16_t kSensorId = 0x0227;
constexpr uint32_t kAGainPrecision = 12;
constexpr uint32_t kDGainPrecision = 8;
constexpr int32_t kLog2GainShift = 18;
constexpr int32_t kSensorExpNumber = 1;
constexpr uint32_t kMasterClock = 288000000;
constexpr uint32_t kMaxIntegrationTime =
0x15BC; // Max allowed for 30fps = 2782 (dec)=0x0ADE (hex) 15fps = 5564 (dec)=0x15BC (hex).
constexpr uint16_t kEndOfSequence = 0x0000;
} // namespace
// Gets the register value from the sequence table.
// |id| : Index of the sequence table.
// |address| : Address of the register.
static fit::result<uint8_t, zx_status_t> GetRegisterValueFromSequence(uint8_t index,
uint16_t address) {
if (index >= kSEQUENCE_TABLE.size()) {
return fit::error(ZX_ERR_INVALID_ARGS);
}
const InitSeqFmt* sequence = kSEQUENCE_TABLE[index];
while (true) {
uint16_t register_address = sequence->address;
uint16_t register_value = sequence->value;
uint16_t register_len = sequence->len;
if (register_address == kEndOfSequence && register_value == 0 && register_len == 0) {
break;
}
if (address == register_address) {
return fit::ok(register_value);
}
sequence++;
}
return fit::error(ZX_ERR_NOT_FOUND);
}
zx_status_t Imx227Device::InitPdev() {
std::lock_guard guard(lock_);
// I2c for communicating with the sensor.
if (!i2c_.is_valid()) {
zxlogf(ERROR, "%s; I2C not available", __func__);
return ZX_ERR_NO_RESOURCES;
}
// Clk for gating clocks for sensor.
if (!clk24_.is_valid()) {
zxlogf(ERROR, "%s; clk24_ not available", __func__);
return ZX_ERR_NO_RESOURCES;
}
// Mipi for init and de-init.
if (!mipi_.is_valid()) {
zxlogf(ERROR, "%s; mipi_ not available", __func__);
return ZX_ERR_NO_RESOURCES;
}
// GPIOs
if (!gpio_vana_enable_.is_valid()) {
zxlogf(ERROR, "%s; gpio_vana_enable_ not available", __func__);
return ZX_ERR_NO_RESOURCES;
}
if (!gpio_vdig_enable_.is_valid()) {
zxlogf(ERROR, "%s; gpio_vdig_enable_ not available", __func__);
return ZX_ERR_NO_RESOURCES;
}
if (!gpio_cam_rst_.is_valid()) {
zxlogf(ERROR, "%s; gpio_cam_rst_ not available", __func__);
return ZX_ERR_NO_RESOURCES;
}
// Set the GPIO to output and set them to their initial values
// before the power up sequence.
gpio_cam_rst_.ConfigOut(1);
gpio_vana_enable_.ConfigOut(0);
gpio_vdig_enable_.ConfigOut(0);
return ZX_OK;
}
fit::result<uint16_t, zx_status_t> Imx227Device::Read16(uint16_t addr) {
auto result = Read8(addr);
if (result.is_error()) {
return result.take_error_result();
}
auto upper_byte = result.value();
result = Read8(addr + 1);
if (result.is_error()) {
return result.take_error_result();
}
auto lower_byte = result.value();
uint16_t reg_value = upper_byte << kByteShift | lower_byte;
return fit::ok(reg_value);
}
fit::result<uint8_t, zx_status_t> Imx227Device::Read8(uint16_t addr) {
// Convert the address to Big Endian format.
// The camera sensor expects in this format.
uint16_t buf = htobe16(addr);
uint8_t val = 0;
auto status =
i2c_.WriteReadSync(reinterpret_cast<uint8_t*>(&buf), sizeof(buf), &val, sizeof(val));
if (status != ZX_OK) {
zxlogf(ERROR, "Imx227Device: could not read reg addr: 0x%08x status: %d", addr, status);
return fit::error(status);
}
return fit::ok(val);
}
zx_status_t Imx227Device::Write8(uint16_t addr, uint8_t val) {
// Convert the address to Big Endian format.
// The camera sensor expects in this format.
// First two bytes are the address, third one is the value to be written.
std::array<uint8_t, 3> buf;
buf[0] = static_cast<uint8_t>((addr >> kByteShift) & kByteMask);
buf[1] = static_cast<uint8_t>(addr & kByteMask);
buf[2] = val;
auto status = i2c_.WriteSync(buf.data(), buf.size());
if (status != ZX_OK) {
zxlogf(ERROR,
"Imx227Device: could not write reg addr/val: 0x%08x/0x%08x status: "
"%d\n",
addr, val, status);
}
return status;
}
bool Imx227Device::CameraSensorIsPoweredUp() {
std::lock_guard guard(lock_);
return ValidateSensorID();
}
bool Imx227Device::ValidateSensorID() {
auto result = Read16(kSensorModelIdReg);
if (result.is_error()) {
return false;
}
return result.value() == kSensorId;
}
zx_status_t Imx227Device::InitSensor(uint8_t idx) {
if (idx >= kSEQUENCE_TABLE.size()) {
return ZX_ERR_INVALID_ARGS;
}
const InitSeqFmt* sequence = kSEQUENCE_TABLE[idx];
bool init_command = true;
while (init_command) {
uint16_t address = sequence->address;
uint8_t value = sequence->value;
switch (address) {
case 0x0000: {
if (sequence->value == 0 && sequence->len == 0) {
init_command = false;
} else {
Write8(address, value);
}
break;
}
default:
Write8(address, value);
break;
}
sequence++;
}
return ZX_OK;
}
void Imx227Device::HwInit() {
// Power up sequence. Reference: Page 51- IMX227-0AQH5-C datasheet.
gpio_vana_enable_.Write(1);
zx_nanosleep(zx_deadline_after(ZX_MSEC(50)));
gpio_vdig_enable_.Write(1);
zx_nanosleep(zx_deadline_after(ZX_MSEC(50)));
// Enable 24M clock for sensor.
clk24_.Enable();
zx_nanosleep(zx_deadline_after(ZX_MSEC(10)));
gpio_cam_rst_.Write(0);
zx_nanosleep(zx_deadline_after(ZX_MSEC(50)));
}
void Imx227Device::HwDeInit() {
gpio_cam_rst_.Write(1);
zx_nanosleep(zx_deadline_after(ZX_MSEC(50)));
clk24_.Disable();
zx_nanosleep(zx_deadline_after(ZX_MSEC(10)));
gpio_vdig_enable_.Write(0);
zx_nanosleep(zx_deadline_after(ZX_MSEC(50)));
gpio_vana_enable_.Write(0);
zx_nanosleep(zx_deadline_after(ZX_MSEC(50)));
}
zx_status_t Imx227Device::CameraSensorInit() {
std::lock_guard guard(lock_);
HwInit();
// Initialize Sensor Context.
ctx_.seq_width = 1;
ctx_.streaming_flag = 0;
ctx_.again_old = 0;
ctx_.change_flag = 0;
ctx_.again_limit = 8 << kAGainPrecision;
ctx_.dgain_limit = 15 << kDGainPrecision;
// Initialize Sensor Parameters.
ctx_.param.again_accuracy = 1 << kLog2GainShift;
ctx_.param.sensor_exp_number = kSensorExpNumber;
ctx_.param.again_log2_max = 3 << kLog2GainShift;
ctx_.param.dgain_log2_max = 3 << kLog2GainShift;
ctx_.param.integration_time_apply_delay = 2;
ctx_.param.isp_exposure_channel_delay = 0;
initialized_ = true;
return ZX_OK;
}
void Imx227Device::CameraSensorDeInit() {
std::lock_guard guard(lock_);
mipi_.DeInit();
// Reference code has it, mostly likely needed for the clock to
// stabalize. No other way of knowing for sure if sensor is now off.
zx_nanosleep(zx_deadline_after(ZX_MSEC(10)));
initialized_ = false;
}
zx_status_t Imx227Device::CameraSensorGetInfo(camera_sensor_info_t* out_info) {
std::lock_guard guard(lock_);
memcpy(out_info, &ctx_.param, sizeof(camera_sensor_info_t));
return ZX_OK;
}
zx_status_t Imx227Device::CameraSensorGetSupportedModes(camera_sensor_mode_t* out_modes_list,
size_t modes_count,
size_t* out_modes_actual) {
std::lock_guard guard(lock_);
if (modes_count > supported_modes.size()) {
return ZX_ERR_INVALID_ARGS;
}
memcpy(out_modes_list, &supported_modes, sizeof(camera_sensor_mode_t) * supported_modes.size());
*out_modes_actual = supported_modes.size();
return ZX_OK;
}
// TODO(braval): Update the Banjo documentation to indicate that SensorSetMode() will
// return ZX_OK even when the sensor is powered down and not
// initialized into the requested mode.
zx_status_t Imx227Device::CameraSensorSetMode(uint8_t mode) {
std::lock_guard guard(lock_);
zxlogf(DEBUG, "%s IMX227 Camera Sensor Mode Set request to %d", __func__, mode);
HwInit();
if (!IsSensorInitialized()) {
return ZX_ERR_INTERNAL;
}
if (mode >= supported_modes.size()) {
return ZX_ERR_INVALID_ARGS;
}
switch (supported_modes[mode].wdr_mode) {
case CAMERASENSOR_WDR_MODE_LINEAR: {
if (IsSensorOutOfReset()) {
InitSensor(supported_modes[mode].idx);
}
ctx_.again_delay = 0;
ctx_.dgain_delay = 0;
ctx_.param.integration_time_apply_delay = 2;
ctx_.param.isp_exposure_channel_delay = 0;
ctx_.hdr_flag = 0;
break;
}
// TODO(41260) : Support other modes.
default:
return ZX_ERR_NOT_SUPPORTED;
}
ctx_.param.active.width = supported_modes[mode].resolution.width;
ctx_.param.active.height = supported_modes[mode].resolution.height;
auto hmax_result = GetRegisterValueFromSequence(supported_modes[mode].idx, kLineLengthPckReg);
auto vmax_result = GetRegisterValueFromSequence(supported_modes[mode].idx, kFrameLengthLinesReg);
if (hmax_result.is_error() || vmax_result.is_error()) {
return ZX_ERR_INTERNAL;
}
ctx_.HMAX = hmax_result.value();
ctx_.VMAX = vmax_result.value();
ctx_.int_max = kMaxIntegrationTime;
ctx_.int_time_min = 1;
ctx_.int_time_limit = ctx_.int_max;
ctx_.param.total.height = ctx_.VMAX;
ctx_.param.total.width = ctx_.HMAX;
ctx_.param.pixels_per_line = ctx_.param.total.width;
uint32_t master_clock = kMasterClock;
ctx_.param.lines_per_second = master_clock / ctx_.HMAX;
ctx_.param.integration_time_min = ctx_.int_time_min;
ctx_.param.integration_time_limit = ctx_.int_time_limit;
ctx_.param.integration_time_max = ctx_.int_time_limit;
ctx_.param.integration_time_long_max = ctx_.int_time_limit;
ctx_.param.mode = mode;
ctx_.param.bayer = supported_modes[mode].bayer;
ctx_.wdr_mode = supported_modes[mode].wdr_mode;
mipi_info_t mipi_info;
mipi_adap_info_t adap_info;
mipi_info.lanes = supported_modes[mode].lanes;
mipi_info.ui_value = 1000 / supported_modes[mode].mbps;
if ((1000 % supported_modes[mode].mbps) != 0) {
mipi_info.ui_value += 1;
}
switch (supported_modes[mode].bits) {
case kRaw10Bits:
adap_info.format = MIPI_IMAGE_FORMAT_AM_RAW10;
break;
case kRaw12Bits:
adap_info.format = MIPI_IMAGE_FORMAT_AM_RAW12;
break;
default:
adap_info.format = MIPI_IMAGE_FORMAT_AM_RAW10;
}
adap_info.resolution.width = supported_modes[mode].resolution.width;
adap_info.resolution.height = supported_modes[mode].resolution.height;
adap_info.path = MIPI_PATH_PATH0;
adap_info.mode = MIPI_MODES_DIR_MODE;
auto status = mipi_.Init(&mipi_info, &adap_info);
return status;
}
zx_status_t Imx227Device::InitMipiCsi(uint8_t mode) {
mipi_info_t mipi_info;
mipi_adap_info_t adap_info;
mipi_info.lanes = available_modes[mode].lanes;
mipi_info.ui_value = 1000 / available_modes[mode].mbps;
if ((1000 % available_modes[mode].mbps) != 0) {
mipi_info.ui_value += 1;
}
adap_info.format = MIPI_IMAGE_FORMAT_AM_RAW10;
adap_info.resolution.width = available_modes[mode].resolution_in.x;
adap_info.resolution.height = available_modes[mode].resolution_in.y;
adap_info.path = MIPI_PATH_PATH0;
adap_info.mode = MIPI_MODES_DIR_MODE;
auto status = mipi_.Init(&mipi_info, &adap_info);
return status;
}
zx_status_t Imx227Device::CameraSensorStartStreaming() {
std::lock_guard guard(lock_);
if (!IsSensorInitialized() || ctx_.streaming_flag) {
return ZX_ERR_BAD_STATE;
}
zxlogf(DEBUG, "%s Camera Sensor Start Streaming", __func__);
ctx_.streaming_flag = 1;
Write8(kModeSelectReg, 0x01);
return ZX_OK;
}
zx_status_t Imx227Device::CameraSensorStopStreaming() {
std::lock_guard guard(lock_);
if (!IsSensorInitialized() || !ctx_.streaming_flag) {
return ZX_ERR_BAD_STATE;
}
ctx_.streaming_flag = 0;
HwDeInit();
return ZX_OK;
}
int32_t Imx227Device::CameraSensorSetAnalogGain(int32_t gain) {
std::lock_guard guard(lock_);
return ZX_ERR_NOT_SUPPORTED;
}
int32_t Imx227Device::CameraSensorSetDigitalGain(int32_t gain) {
std::lock_guard guard(lock_);
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t Imx227Device::CameraSensorSetIntegrationTime(int32_t int_time) {
std::lock_guard guard(lock_);
// TODO(41260): Add support for this.
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t Imx227Device::CameraSensorUpdate() {
std::lock_guard guard(lock_);
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t Imx227Device::Create(zx_device_t* parent, std::unique_ptr<Imx227Device>* device_out) {
ddk::CompositeProtocolClient composite(parent);
if (!composite.is_valid()) {
zxlogf(ERROR, "%s could not get composite protocoln", __func__);
return ZX_ERR_NOT_SUPPORTED;
}
std::array<zx_device_t*, FRAGMENT_COUNT> fragments;
size_t actual;
composite.GetFragments(fragments.data(), FRAGMENT_COUNT, &actual);
if (actual != FRAGMENT_COUNT) {
zxlogf(ERROR, "%s Could not get fragments", __func__);
return ZX_ERR_NOT_SUPPORTED;
}
auto sensor_device = std::make_unique<Imx227Device>(
parent, fragments[FRAGMENT_I2C], fragments[FRAGMENT_GPIO_VANA], fragments[FRAGMENT_GPIO_VDIG],
fragments[FRAGMENT_GPIO_CAM_RST], fragments[FRAGMENT_CLK24], fragments[FRAGMENT_MIPICSI]);
zx_status_t status = sensor_device->InitPdev();
if (status != ZX_OK) {
zxlogf(ERROR, "%s InitPdev failed", __func__);
return status;
}
*device_out = std::move(sensor_device);
return status;
}
void Imx227Device::ShutDown() {}
void Imx227Device::DdkUnbindNew(ddk::UnbindTxn txn) { txn.Reply(); }
void Imx227Device::DdkRelease() {
ShutDown();
delete this;
}
zx_status_t Imx227Device::CreateAndBind(void* /*ctx*/, zx_device_t* parent) {
std::unique_ptr<Imx227Device> device;
zx_status_t status = Imx227Device::Create(parent, &device);
if (status != ZX_OK) {
zxlogf(ERROR, "imx227: Could not setup imx227 sensor device: %d", status);
return status;
}
std::array<zx_device_prop_t, 1> props = {{
{BIND_PLATFORM_PROTO, 0, ZX_PROTOCOL_CAMERA_SENSOR2},
}};
status = device->DdkAdd(
ddk::DeviceAddArgs("imx227").set_flags(DEVICE_ADD_ALLOW_MULTI_COMPOSITE).set_props(props));
if (status != ZX_OK) {
zxlogf(ERROR, "imx227: Could not add imx227 sensor device: %d", status);
return status;
}
zxlogf(INFO, "imx227 driver added");
// `device` intentionally leaked as it is now held by DevMgr.
__UNUSED auto* dev = device.release();
return ZX_OK;
}
bool Imx227Device::RunUnitTests(void* ctx, zx_device_t* parent, zx_handle_t channel) {
return driver_unit_test::RunZxTests("Imx227Tests", parent, channel);
}
static constexpr zx_driver_ops_t driver_ops = []() {
zx_driver_ops_t ops = {};
ops.version = DRIVER_OPS_VERSION;
ops.bind = Imx227Device::CreateAndBind;
ops.run_unit_tests = Imx227Device::RunUnitTests;
return ops;
}();
} // namespace camera
// clang-format off
ZIRCON_DRIVER_BEGIN(imx227, camera::driver_ops, "imx227", "0.1", 4)
BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_COMPOSITE),
BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_SONY),
BI_ABORT_IF(NE, BIND_PLATFORM_DEV_PID, PDEV_PID_SONY_IMX227),
BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_DID, PDEV_DID_CAMERA_SENSOR),
ZIRCON_DRIVER_END(imx227)