| // 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 <fuchsia/hardware/camera/sensor/c/banjo.h> |
| #include <lib/ddk/binding_driver.h> |
| #include <lib/ddk/debug.h> |
| #include <lib/ddk/metadata.h> |
| #include <lib/driver-unit-test/utils.h> |
| #include <lib/fpromise/result.h> |
| #include <lib/trace/event.h> |
| #include <threads.h> |
| #include <zircon/status.h> |
| #include <zircon/types.h> |
| |
| #include <ddk/metadata/camera.h> |
| #include <fbl/auto_lock.h> |
| #include <safemath/safe_conversions.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 { |
| |
| fpromise::result<uint8_t, zx_status_t> Imx227Device::GetRegisterValueFromSequence( |
| uint8_t index, uint16_t address) { |
| TRACE_DURATION("camera", "Imx227Device::GetRegisterValueFromSequence"); |
| if (index >= kSEQUENCE_TABLE.size()) { |
| return fpromise::error(ZX_ERR_INVALID_ARGS); |
| } |
| const InitSeqFmt* sequence = kSEQUENCE_TABLE[index]; |
| while (true) { |
| auto register_address = sequence->address; |
| auto register_value = sequence->value; |
| auto register_len = sequence->len; |
| if (register_address == kEndOfSequence && register_value == 0 && register_len == 0) { |
| break; |
| } |
| if (address == register_address) { |
| return fpromise::ok(register_value); |
| } |
| sequence++; |
| } |
| return fpromise::error(ZX_ERR_NOT_FOUND); |
| } |
| |
| fpromise::result<uint16_t, zx_status_t> Imx227Device::GetRegisterValueFromSequence16( |
| uint8_t index, uint16_t address) { |
| TRACE_DURATION("camera", "Imx227Device::GetRegisterValueFromSequence16"); |
| auto result_hi = GetRegisterValueFromSequence(index, address); |
| auto result_lo = GetRegisterValueFromSequence(index, address + 1); |
| if (result_hi.is_error()) { |
| return fpromise::error(result_hi.error()); |
| } |
| if (result_lo.is_error()) { |
| return fpromise::error(result_lo.error()); |
| } |
| return fpromise::ok(static_cast<uint16_t>(result_hi.value() << 8 | result_lo.value())); |
| } |
| |
| 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; |
| } |
| |
| // Set the GPIO to output and set them to their initial values |
| // before the power up sequence. |
| fidl::WireResult cam_rst_result = gpio_cam_rst_->ConfigOut(1); |
| if (!cam_rst_result.ok()) { |
| zxlogf(ERROR, "Failed to send ConfigOut request to gpio_cam_rst: %s", |
| cam_rst_result.status_string()); |
| return cam_rst_result.status(); |
| } |
| if (cam_rst_result->is_error()) { |
| zxlogf(ERROR, "Failed to configure gpio_cam_rst as output: %s", |
| zx_status_get_string(cam_rst_result->error_value())); |
| return cam_rst_result->error_value(); |
| } |
| |
| fidl::WireResult vana_enable_result = gpio_vana_enable_->ConfigOut(0); |
| if (!vana_enable_result.ok()) { |
| zxlogf(ERROR, "Failed to send ConfigOut request to gpio_vana_enable: %s", |
| vana_enable_result.status_string()); |
| return vana_enable_result.status(); |
| } |
| if (vana_enable_result->is_error()) { |
| zxlogf(ERROR, "Failed to configure gpio_vana_enable as output: %s", |
| zx_status_get_string(vana_enable_result->error_value())); |
| return vana_enable_result->error_value(); |
| } |
| |
| fidl::WireResult vdig_enable_result = gpio_vdig_enable_->ConfigOut(0); |
| if (!vdig_enable_result.ok()) { |
| zxlogf(ERROR, "Failed to send ConfigOut request to gpio_vdig_enable: %s", |
| vdig_enable_result.status_string()); |
| return vdig_enable_result.status(); |
| } |
| if (vdig_enable_result->is_error()) { |
| zxlogf(ERROR, "Failed to configure gpio_vdig_enable as output: %s", |
| zx_status_get_string(vdig_enable_result->error_value())); |
| return vdig_enable_result->error_value(); |
| } |
| return ZX_OK; |
| } |
| |
| fpromise::result<uint16_t, zx_status_t> Imx227Device::Read16(uint16_t addr) { |
| TRACE_DURATION("camera", "Imx227Device::Read16", "addr", 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 = safemath::checked_cast<uint16_t>(upper_byte << kByteShift) | lower_byte; |
| return fpromise::ok(reg_value); |
| } |
| |
| fpromise::result<uint8_t, zx_status_t> Imx227Device::Read8(uint16_t addr) { |
| TRACE_DURATION("camera", "Imx227Device::Read8", "addr", 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 fpromise::error(status); |
| } |
| return fpromise::ok(val); |
| } |
| |
| zx_status_t Imx227Device::Write16(uint16_t addr, uint16_t val) { |
| TRACE_DURATION("camera", "Imx227Device::Write16", "addr", addr, "val", val); |
| // Convert the arguments to big endian to match the register spec. |
| // First two bytes are the address, third and fourth are the value to be written. |
| auto reg_addr = htobe16(addr); |
| auto reg_val = htobe16(val); |
| std::array<uint8_t, 4> buf; |
| buf[0] = static_cast<uint8_t>(reg_addr & kByteMask); |
| buf[1] = static_cast<uint8_t>((reg_addr >> kByteShift) & kByteMask); |
| buf[2] = static_cast<uint8_t>(reg_val & kByteMask); |
| buf[3] = static_cast<uint8_t>((reg_val >> kByteShift) & kByteMask); |
| 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; |
| } |
| |
| zx_status_t Imx227Device::Write8(uint16_t addr, uint8_t val) { |
| TRACE_DURATION("camera", "Imx227Device::Write8", "addr", addr, "val", val); |
| // Convert the arguments to big endian to match the register spec. |
| // First two bytes are the address, third one is the value to be written. |
| auto reg_addr = htobe16(addr); |
| std::array<uint8_t, 3> buf; |
| buf[0] = static_cast<uint8_t>(reg_addr & kByteMask); |
| buf[1] = static_cast<uint8_t>((reg_addr >> kByteShift) & 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::ValidateSensorID() { |
| auto result = Read16(kSensorModelIdReg); |
| if (result.is_error()) { |
| return false; |
| } |
| return result.value() == kSensorId; |
| } |
| |
| zx_status_t Imx227Device::InitSensor(uint8_t idx) { |
| TRACE_DURATION("camera", "Imx227Device::InitSensor"); |
| 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++; |
| } |
| |
| RefreshCachedExposureParams(); |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t Imx227Device::HwInit() { |
| TRACE_DURATION("camera", "Imx227Device::HwInit"); |
| |
| // Power up sequence. Reference: Page 51- IMX227-0AQH5-C datasheet. |
| fidl::WireResult vana_enable_result = gpio_vana_enable_->Write(1); |
| if (!vana_enable_result.ok()) { |
| zxlogf(ERROR, "Failed to send Write request to gpio_vana_enable: %s", |
| vana_enable_result.status_string()); |
| return vana_enable_result.status(); |
| } |
| if (vana_enable_result->is_error()) { |
| zxlogf(ERROR, "Failed to write to gpio_vana_enable: %s", |
| zx_status_get_string(vana_enable_result->error_value())); |
| return vana_enable_result->error_value(); |
| } |
| zx_nanosleep(zx_deadline_after(ZX_MSEC(50))); |
| |
| fidl::WireResult vdig_enable_result = gpio_vdig_enable_->Write(1); |
| if (!vdig_enable_result.ok()) { |
| zxlogf(ERROR, "Failed to send Write request to gpio_vdig_enable: %s", |
| vdig_enable_result.status_string()); |
| return vdig_enable_result.status(); |
| } |
| if (vdig_enable_result->is_error()) { |
| zxlogf(ERROR, "Failed to write to gpio_vdig_enable: %s", |
| zx_status_get_string(vdig_enable_result->error_value())); |
| return vdig_enable_result->error_value(); |
| } |
| zx_nanosleep(zx_deadline_after(ZX_MSEC(50))); |
| |
| // Enable 24M clock for sensor. |
| fidl::WireResult result = clk24_->Enable(); |
| if (!result.ok()) { |
| zxlogf(ERROR, "Failed to send request to enable 24M clock: %s", result.status_string()); |
| return result.status(); |
| } |
| if (result->is_error()) { |
| zxlogf(ERROR, "Failed to enable 24M clock: %s", zx_status_get_string(result->error_value())); |
| return result->error_value(); |
| } |
| zx_nanosleep(zx_deadline_after(ZX_MSEC(10))); |
| |
| fidl::WireResult cam_rst_result = gpio_cam_rst_->Write(0); |
| if (!cam_rst_result.ok()) { |
| zxlogf(ERROR, "Failed to send Write request to gpio_cam_rst: %s", |
| cam_rst_result.status_string()); |
| return cam_rst_result.status(); |
| } |
| if (cam_rst_result->is_error()) { |
| zxlogf(ERROR, "Failed to write to gpio_cam_rst: %s", |
| zx_status_get_string(cam_rst_result->error_value())); |
| return cam_rst_result->error_value(); |
| } |
| zx_nanosleep(zx_deadline_after(ZX_MSEC(50))); |
| |
| RefreshCachedExposureParams(); |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t Imx227Device::HwDeInit() { |
| TRACE_DURATION("camera", "Imx227Device::HwDeInit"); |
| |
| fidl::WireResult cam_rst_result = gpio_cam_rst_->Write(1); |
| if (!cam_rst_result.ok()) { |
| zxlogf(ERROR, "Failed to send Write request to gpio_cam_rst: %s", |
| cam_rst_result.status_string()); |
| return cam_rst_result.status(); |
| } |
| if (cam_rst_result->is_error()) { |
| zxlogf(ERROR, "Failed to write to gpio_cam_rst: %s", |
| zx_status_get_string(cam_rst_result->error_value())); |
| return cam_rst_result->error_value(); |
| } |
| zx_nanosleep(zx_deadline_after(ZX_MSEC(50))); |
| |
| fidl::WireResult result = clk24_->Disable(); |
| if (!result.ok() || result->is_error()) { |
| zxlogf(ERROR, "Failed to send request to disable 24M clock: %s", result.status_string()); |
| return result.status(); |
| } |
| if (result->is_error()) { |
| zxlogf(ERROR, "Failed to disable 24M clock: %s", zx_status_get_string(result->error_value())); |
| return result->error_value(); |
| } |
| zx_nanosleep(zx_deadline_after(ZX_MSEC(10))); |
| |
| fidl::WireResult vdig_enable_result = gpio_vdig_enable_->Write(0); |
| if (!vdig_enable_result.ok()) { |
| zxlogf(ERROR, "Failed to send Write request to gpio_vdig_enable: %s", |
| vdig_enable_result.status_string()); |
| return vdig_enable_result.status(); |
| } |
| if (vdig_enable_result->is_error()) { |
| zxlogf(ERROR, "Failed to write to gpio_vdig_enable: %s", |
| zx_status_get_string(vdig_enable_result->error_value())); |
| return vdig_enable_result->error_value(); |
| } |
| zx_nanosleep(zx_deadline_after(ZX_MSEC(50))); |
| |
| fidl::WireResult vana_enable_result = gpio_vana_enable_->Write(0); |
| if (!vana_enable_result.ok()) { |
| zxlogf(ERROR, "Failed to send Write request to gpio_vana_enable: %s", |
| vana_enable_result.status_string()); |
| return vana_enable_result.status(); |
| } |
| if (vana_enable_result->is_error()) { |
| zxlogf(ERROR, "Failed to write to gpio_vana_enable: %s", |
| zx_status_get_string(vana_enable_result->error_value())); |
| return vana_enable_result->error_value(); |
| } |
| zx_nanosleep(zx_deadline_after(ZX_MSEC(50))); |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t Imx227Device::CycleResetOnAndOff() { |
| fidl::WireResult cam_rst_result1 = gpio_cam_rst_->Write(1); |
| if (!cam_rst_result1.ok()) { |
| zxlogf(ERROR, "Failed to send Write request to gpio_cam_rst: %s", |
| cam_rst_result1.status_string()); |
| return cam_rst_result1.status(); |
| } |
| if (cam_rst_result1->is_error()) { |
| zxlogf(ERROR, "Failed to write to gpio_cam_rst: %s", |
| zx_status_get_string(cam_rst_result1->error_value())); |
| return cam_rst_result1->error_value(); |
| } |
| zx_nanosleep(zx_deadline_after(ZX_MSEC(50))); |
| fidl::WireResult cam_rst_result2 = gpio_cam_rst_->Write(0); |
| if (!cam_rst_result2.ok()) { |
| zxlogf(ERROR, "Failed to send Write request to gpio_cam_rst: %s", |
| cam_rst_result2.status_string()); |
| return cam_rst_result2.status(); |
| } |
| if (cam_rst_result2->is_error()) { |
| zxlogf(ERROR, "Failed to write to gpio_cam_rst: %s", |
| zx_status_get_string(cam_rst_result2->error_value())); |
| return cam_rst_result2->error_value(); |
| } |
| zx_nanosleep(zx_deadline_after(ZX_MSEC(50))); |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t Imx227Device::InitMipiCsi(uint32_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.x = available_modes[mode].resolution_in.x; |
| adap_info.resolution.y = 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; |
| } |
| |
| fpromise::result<int32_t, zx_status_t> Imx227Device::GetTemperature() { |
| std::lock_guard guard(lock_); |
| // Enable temperature control |
| zx_status_t status = Write8(kTempCtrlReg, 0x01); |
| if (status != ZX_OK) { |
| return fpromise::error(status); |
| } |
| auto result = Read8(kTempOutputReg); |
| if (result.is_error()) { |
| return result.take_error_result(); |
| } |
| auto retval = static_cast<int32_t>(result.value()); |
| return fpromise::ok(retval); |
| } |
| |
| fpromise::result<uint32_t, zx_status_t> Imx227Device::GetLinesPerSecond() { |
| auto result_hi = |
| GetRegisterValueFromSequence(available_modes[current_mode_].idx, kLineLengthPckReg); |
| auto result_lo = |
| GetRegisterValueFromSequence(available_modes[current_mode_].idx, kLineLengthPckReg + 1); |
| if (result_hi.is_error() || result_lo.is_error()) { |
| return fpromise::error(ZX_ERR_INTERNAL); |
| } |
| uint16_t line_length_pclk = |
| safemath::checked_cast<uint16_t>(result_hi.value() << 8) | result_lo.value(); |
| uint32_t lines_per_second = kMasterClock / line_length_pclk; |
| return fpromise::ok(lines_per_second); |
| } |
| |
| float Imx227Device::AnalogRegValueToTotalGain(uint16_t reg_value) { |
| return (static_cast<float>(analog_gain_.m0 * reg_value + analog_gain_.c0)) / |
| (static_cast<float>(analog_gain_.m1 * reg_value + analog_gain_.c1)); |
| } |
| |
| uint16_t Imx227Device::AnalogTotalGainToRegValue(float gain) { |
| float value; |
| // Compute the register value. |
| if (analog_gain_.m0 == 0) { |
| value = ((analog_gain_.c0 / gain) - analog_gain_.c1) / analog_gain_.m1; |
| } else { |
| value = (analog_gain_.c1 * gain - analog_gain_.c0) / analog_gain_.m0; |
| } |
| |
| // Round the final result, which is quantized to the gain code step size. |
| value += 0.5f * analog_gain_.gain_code_step_size; |
| |
| // Convert and clamp. |
| auto register_value = safemath::checked_cast<uint16_t>(value); |
| |
| if (register_value < analog_gain_.gain_code_min) { |
| register_value = analog_gain_.gain_code_min; |
| } |
| |
| register_value = (register_value - analog_gain_.gain_code_min) / analog_gain_.gain_code_step_size; |
| register_value = register_value * analog_gain_.gain_code_step_size + analog_gain_.gain_code_min; |
| |
| if (register_value > analog_gain_.gain_code_max) { |
| register_value = analog_gain_.gain_code_max; |
| } |
| |
| return register_value; |
| } |
| |
| float Imx227Device::DigitalRegValueToTotalGain(uint16_t reg_value) { |
| return static_cast<float>(reg_value) / (1 << kDigitalGainShift); |
| } |
| |
| uint16_t Imx227Device::DigitalTotalGainToRegValue(float gain) { |
| float value; |
| |
| // Compute the register value. |
| value = gain * (1 << kDigitalGainShift); |
| |
| // Round the final result, which is quantized to the gain code step size. |
| value += 0.5f * digital_gain_.gain_step_size; |
| |
| // Convert and clamp. |
| auto register_value = safemath::checked_cast<uint16_t>(value); |
| |
| if (register_value < digital_gain_.gain_min) { |
| register_value = digital_gain_.gain_min; |
| } |
| |
| register_value = (register_value - digital_gain_.gain_min) / digital_gain_.gain_step_size; |
| register_value = register_value * digital_gain_.gain_step_size + digital_gain_.gain_min; |
| |
| if (register_value > digital_gain_.gain_max) { |
| register_value = digital_gain_.gain_max; |
| } |
| |
| return register_value; |
| } |
| zx_status_t Imx227Device::ReadAnalogGainConstants() { |
| // Since these are contiguous, we do a single read for the block or registers. |
| Imx227AnalogGainRegisters regs; |
| |
| // Convert the address to big endian format, as required by the sensor. |
| uint16_t i2c_addr = htobe16(Imx227AnalogGainRegisters::kBaseAddress); |
| auto status = i2c_.WriteReadSync(reinterpret_cast<uint8_t*>(&i2c_addr), sizeof(i2c_addr), |
| reinterpret_cast<uint8_t*>(®s), sizeof(regs)); |
| if (status != ZX_OK) { |
| return ZX_ERR_BAD_STATE; |
| } |
| // Validate the m0,1 constraint |
| if (!(regs.m0 == 0) ^ (regs.m1 == 0)) { |
| return ZX_ERR_BAD_STATE; |
| } |
| analog_gain_.m0 = be16toh(regs.m0); |
| analog_gain_.m1 = be16toh(regs.m1); |
| analog_gain_.c0 = be16toh(regs.c0); |
| analog_gain_.c1 = be16toh(regs.c1); |
| analog_gain_.gain_code_min = be16toh(regs.code_min); |
| analog_gain_.gain_code_max = be16toh(regs.code_max); |
| analog_gain_.gain_code_step_size = be16toh(regs.code_step); |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t Imx227Device::ReadDigitalGainConstants() { |
| // Since these are contiguous, we do a single read for the block or registers. |
| Imx227DigitalGainRegisters regs; |
| uint16_t i2c_addr = htobe16(Imx227DigitalGainRegisters::kBaseAddress); |
| auto status = i2c_.WriteReadSync(reinterpret_cast<uint8_t*>(&i2c_addr), sizeof(i2c_addr), |
| reinterpret_cast<uint8_t*>(®s), sizeof(regs)); |
| if (status != ZX_OK) { |
| return ZX_ERR_BAD_STATE; |
| } |
| |
| digital_gain_.gain_min = be16toh(regs.gain_min); |
| digital_gain_.gain_max = be16toh(regs.gain_max); |
| digital_gain_.gain_step_size = be16toh(regs.gain_step_size); |
| return ZX_OK; |
| } |
| |
| // TODO(jsasinowski): Determine if this can be called less frequently. |
| zx_status_t Imx227Device::ReadGainConstants() { |
| if (gain_constants_valid_) { |
| return ZX_OK; |
| } |
| |
| auto status = ReadAnalogGainConstants(); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| status = ReadDigitalGainConstants(); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| gain_constants_valid_ = true; |
| return ZX_OK; |
| } |
| |
| zx_status_t Imx227Device::SetGroupedParameterHold(bool enable) { |
| auto status = Write8(kGroupedParameterHoldReg, enable ? 1 : 0); |
| return status; |
| } |
| |
| // Update the cached exposure values if possible. |
| // Any read failures here imply that the sensor is not |
| // available and can be ignored, as the sensor registers will |
| // need to be updated again before the sensor can be used. |
| void Imx227Device::RefreshCachedExposureParams() { |
| auto result = Read16(kAnalogGainCodeGlobalReg); |
| if (result.is_ok()) { |
| analog_gain_.gain_code_global = result.value(); |
| } |
| |
| result = Read16(kDigitalGainGlobalReg); |
| if (result.is_ok()) { |
| digital_gain_.gain = result.value(); |
| } |
| |
| result = Read16(kCoarseIntegrationTimeReg); |
| if (result.is_ok()) { |
| integration_time_.coarse_integration_time = result.value(); |
| } |
| } |
| |
| zx_status_t Imx227Device::Create(zx_device_t* parent, std::unique_ptr<Imx227Device>* device_out) { |
| const char* kClockFragmentName = "clock-sensor"; |
| zx::result clock_client = |
| ddk::Device<void>::DdkConnectFragmentFidlProtocol<fuchsia_hardware_clock::Service::Clock>( |
| parent, kClockFragmentName); |
| if (clock_client.is_error()) { |
| zxlogf(ERROR, "Failed to connect to clock protocol from fragment %s: %s", kClockFragmentName, |
| clock_client.status_string()); |
| return clock_client.error_value(); |
| } |
| |
| const char* kGpioVanaEnableFragmentName = "gpio-vana"; |
| zx::result gpio_vana_enable = |
| ddk::Device<void>::DdkConnectFragmentFidlProtocol<fuchsia_hardware_gpio::Service::Device>( |
| parent, kGpioVanaEnableFragmentName); |
| if (gpio_vana_enable.is_error()) { |
| zxlogf(ERROR, "Failed to connect to gpio protocol from fragment %s: %s", |
| kGpioVanaEnableFragmentName, gpio_vana_enable.status_string()); |
| return gpio_vana_enable.error_value(); |
| } |
| |
| const char* kGpioVdigEnableFragmentName = "gpio-vdig"; |
| zx::result gpio_vdig_enable = |
| ddk::Device<void>::DdkConnectFragmentFidlProtocol<fuchsia_hardware_gpio::Service::Device>( |
| parent, kGpioVdigEnableFragmentName); |
| if (gpio_vdig_enable.is_error()) { |
| zxlogf(ERROR, "Failed to connect to gpio protocol from fragment %s: %s", |
| kGpioVdigEnableFragmentName, gpio_vdig_enable.status_string()); |
| return gpio_vdig_enable.error_value(); |
| } |
| |
| const char* kGpioCamRstFragmentName = "gpio-reset"; |
| zx::result gpio_cam_rst = |
| ddk::Device<void>::DdkConnectFragmentFidlProtocol<fuchsia_hardware_gpio::Service::Device>( |
| parent, kGpioCamRstFragmentName); |
| if (gpio_cam_rst.is_error()) { |
| zxlogf(ERROR, "Failed to connect to gpio protocol from fragment %s: %s", |
| kGpioCamRstFragmentName, gpio_cam_rst.status_string()); |
| return gpio_cam_rst.error_value(); |
| } |
| |
| auto sensor_device = std::make_unique<Imx227Device>( |
| parent, std::move(clock_client.value()), std::move(gpio_vana_enable.value()), |
| std::move(gpio_vdig_enable.value()), std::move(gpio_cam_rst.value())); |
| |
| 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::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; |
| } |
| |
| status = device->DdkAdd(ddk::DeviceAddArgs("imx227").set_flags(DEVICE_ADD_ALLOW_MULTI_COMPOSITE)); |
| 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. |
| [[maybe_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(imx227, camera::driver_ops, "imx227", "0.1"); |