/* * Copyright 2017-2018, 2020 NXP * All rights reserved. * * * SPDX-License-Identifier: BSD-3-Clause */ #include "fsl_video_common.h" #include "fsl_camera.h" #include "fsl_camera_device.h" #include "fsl_ov5640.h" /******************************************************************************* * Definitions ******************************************************************************/ #define OV5640_DelayMs VIDEO_DelayMs #define OV5640_SCCB_ADDR 0x3CU #define OV5640_RESOLUTION_PARAM_NUM 0x16U #define OV5640_WriteReg(handle, reg, val) \ SCCB_WriteReg(OV5640_SCCB_ADDR, kSCCB_RegAddr16Bit, (reg), (val), \ ((ov5640_resource_t *)((handle)->resource))->i2cSendFunc) #define OV5640_WriteMultiRegs(handle, reg, val, len) \ SCCB_WriteMultiRegs(OV5640_SCCB_ADDR, kSCCB_RegAddr16Bit, (reg), (val), len, \ ((ov5640_resource_t *)((handle)->resource))->i2cSendFunc) #define OV5640_ReadReg(handle, reg, val) \ SCCB_ReadReg(OV5640_SCCB_ADDR, kSCCB_RegAddr16Bit, (reg), (val), \ ((ov5640_resource_t *)((handle)->resource))->i2cReceiveFunc) #define OV5640_ModifyReg(handle, reg, clrMask, val) \ SCCB_ModifyReg(OV5640_SCCB_ADDR, kSCCB_RegAddr16Bit, (reg), (clrMask), (val), \ ((ov5640_resource_t *)((handle)->resource))->i2cReceiveFunc, \ ((ov5640_resource_t *)((handle)->resource))->i2cSendFunc) #define OV5640_POLARITY_CTRL00_VSYNC_MASK (1U << 0U) #define OV5640_POLARITY_CTRL00_HREF_MASK (1U << 1U) #define OV5640_POLARITY_CTRL00_GATE_PCLK_HREF_MASK (1U << 2U) #define OV5640_POLARITY_CTRL00_GATE_PCLK_VSYNC_MASK (1U << 3U) #define OV5640_POLARITY_CTRL00_PCLK_MASK (1U << 5U) #define OV5640_SDE_CTRL0_REG 0x5580 #define OV5640_SDE_CTRL1_REG 0x5581 #define OV5640_SDE_CTRL2_REG 0x5582 #define OV5640_SDE_CTRL3_REG 0x5583 #define OV5640_SDE_CTRL4_REG 0x5584 #define OV5640_SDE_CTRL5_REG 0x5585 #define OV5640_SDE_CTRL6_REG 0x5586 #define OV5640_SDE_CTRL7_REG 0x5587 #define OV5640_SDE_CTRL8_REG 0x5588 #define OV5640_SDE_CTRL9_REG 0x5589 #define OV5640_SDE_CTRL10_REG 0x558a #define OV5640_SDE_CTRL11_REG 0x558b #define OV5640_SDE_CTRL12_REG 0x558c #define OV5640_AWB_R_H_REG 0x3400 #define OV5640_AWB_R_L_REG 0x3401 #define OV5640_AWB_G_H_REG 0x3402 #define OV5640_AWB_G_L_REG 0x3403 #define OV5640_AWB_B_H_REG 0x3404 #define OV5640_AWB_B_L_REG 0x3405 #define OV5640_AWB_CTRL_REG 0x3406 #define OV5640_CHECK_RET(x) \ do \ { \ status = (x); \ if (kStatus_Success != status) \ { \ return status; \ } \ } while (false) typedef struct _ov5640_reg_val { uint16_t regAddr; /*!< Register address. */ uint8_t regVal; /*!interface) { for (i = 0; i < ARRAY_SIZE(s_ov5640MipiClockConfigs); i++) { if ((config->framePerSec == s_ov5640MipiClockConfigs[i].framePerSec) && (config->resolution == s_ov5640MipiClockConfigs[i].resolution)) { return &s_ov5640MipiClockConfigs[i]; } } } else { for (i = 0; i < ARRAY_SIZE(s_ov5640DvpClockConfigs); i++) { if ((config->framePerSec == s_ov5640DvpClockConfigs[i].framePerSec) && (config->resolution == s_ov5640DvpClockConfigs[i].resolution)) { return &s_ov5640DvpClockConfigs[i]; } } } return NULL; } static status_t OV5640_SetPixelFormat(camera_device_handle_t *handle, video_pixel_format_t pixelFormat) { status_t status; uint8_t param[2]; switch (pixelFormat) { case kVIDEO_PixelFormatYUYV: param[0] = 0x3F; param[1] = 0x00; break; case kVIDEO_PixelFormatRGB565: default: param[0] = 0x6f; param[1] = 0x01; break; } OV5640_CHECK_RET(OV5640_WriteReg(handle, 0x4300, param[0])); OV5640_CHECK_RET(OV5640_WriteReg(handle, 0x501f, param[1])); return kStatus_Success; } status_t OV5640_Init(camera_device_handle_t *handle, const camera_config_t *config) { status_t status; ov5640_resource_t *resource = (ov5640_resource_t *)(handle->resource); uint8_t reg; const ov5640_clock_config_t *clockConfig; /* Verify the configuration. */ const uint8_t *resParam = OV5640_GetResolutionParam(config->resolution); if (NULL == resParam) { return kStatus_InvalidArgument; } if ((kVIDEO_PixelFormatYUYV != config->pixelFormat) && (kVIDEO_PixelFormatRGB565 != config->pixelFormat)) { return kStatus_InvalidArgument; } if ((kCAMERA_InterfaceNonGatedClock != config->interface) && (kCAMERA_InterfaceGatedClock != config->interface) && (kCAMERA_InterfaceCCIR656 != config->interface) && (kCAMERA_InterfaceMIPI != config->interface)) { return kStatus_InvalidArgument; } /* Only support 2 data lanes. */ if ((kCAMERA_InterfaceMIPI == config->interface) && (2U != config->csiLanes)) { return kStatus_InvalidArgument; } clockConfig = OV5640_GetClockConfig(config); if (NULL == clockConfig) { return kStatus_InvalidArgument; } resource->pullPowerDownPin(true); resource->pullResetPin(false); /* Delay 5ms. */ OV5640_DelayMs(5); resource->pullPowerDownPin(false); /* Delay 1ms. */ OV5640_DelayMs(1); resource->pullResetPin(true); /* Delay 20ms. */ OV5640_DelayMs(20); OV5640_CHECK_RET(OV5640_SoftwareReset(handle)); /* Delay 5ms. */ OV5640_DelayMs(5); /* Initialize. */ status = OV5640_LoadRegVal(handle, ov5640InitParam, ARRAY_SIZE(ov5640InitParam)); if (kStatus_Success != status) { return status; } /* Resolution. */ OV5640_CHECK_RET(OV5640_WriteMultiRegs(handle, 0x3800, resParam, OV5640_RESOLUTION_PARAM_NUM)); if ((uint32_t)kVIDEO_Resolution1080P == config->resolution) { OV5640_CHECK_RET(OV5640_WriteReg(handle, 0x3709, 0x12)); OV5640_CHECK_RET(OV5640_WriteReg(handle, 0x3821, 0x06)); } OV5640_CHECK_RET(OV5640_WriteReg(handle, 0x302c, 0xc2)); /* Pixel format. */ OV5640_CHECK_RET(OV5640_SetPixelFormat(handle, config->pixelFormat)); /* Clock. */ OV5640_CHECK_RET(OV5640_WriteReg(handle, 0x3035, clockConfig->pllCtrl1)); OV5640_CHECK_RET(OV5640_WriteReg(handle, 0x3036, clockConfig->pllCtrl2)); OV5640_CHECK_RET(OV5640_WriteReg(handle, 0x460c, clockConfig->vfifoCtrl0C)); OV5640_CHECK_RET(OV5640_WriteReg(handle, 0x3824, clockConfig->pclkDiv)); OV5640_CHECK_RET(OV5640_WriteReg(handle, 0x4837, clockConfig->pclkPeriod)); /* Interface. */ if (kCAMERA_InterfaceMIPI == config->interface) { OV5640_CHECK_RET(OV5640_WriteReg(handle, 0x3034, 0x18)); /* Set Frex, Vsync, Href, PCLK, data, GPIO to input. */ OV5640_CHECK_RET(OV5640_WriteReg(handle, 0x3017, 0x00)); OV5640_CHECK_RET(OV5640_WriteReg(handle, 0x3018, 0x00)); /* * Set to MIPI mode, set data lane. Currently only support 2 data lanes, * if need to use 1 data lane, write 0x25 to register 0x300e. */ OV5640_CHECK_RET(OV5640_WriteReg(handle, 0x300e, 0x45)); /* Virtual channel. */ OV5640_CHECK_RET(OV5640_ModifyReg(handle, 0x4814, (3U << 6), (uint8_t)(config->mipiChannel) << 6)); OV5640_CHECK_RET(OV5640_WriteReg(handle, 0x4800, 0x04)); } else { OV5640_CHECK_RET(OV5640_WriteReg(handle, 0x3034, 0x1a)); /* Set Frex, Vsync, Href, PCLK, data, GPIO to output. */ OV5640_CHECK_RET(OV5640_WriteReg(handle, 0x3017, 0xFF)); OV5640_CHECK_RET(OV5640_WriteReg(handle, 0x3018, 0xFF)); /* DVP mode */ OV5640_CHECK_RET(OV5640_WriteReg(handle, 0x300e, 0x58)); if (kCAMERA_InterfaceCCIR656 == config->interface) { OV5640_CHECK_RET(OV5640_WriteReg(handle, 0x4719, 0x01)); OV5640_CHECK_RET(OV5640_WriteReg(handle, 0x4730, 0x01)); } } /* Signal polarity */ reg = 0; if ((uint32_t)kCAMERA_HrefActiveHigh != (config->controlFlags & (uint32_t)kCAMERA_HrefActiveHigh)) { reg |= OV5640_POLARITY_CTRL00_HREF_MASK; } if ((uint32_t)kCAMERA_VsyncActiveHigh != (config->controlFlags & (uint32_t)kCAMERA_VsyncActiveHigh)) { reg |= OV5640_POLARITY_CTRL00_VSYNC_MASK; } if ((uint32_t)kCAMERA_DataLatchOnRisingEdge == (config->controlFlags & (uint32_t)kCAMERA_DataLatchOnRisingEdge)) { reg |= OV5640_POLARITY_CTRL00_PCLK_MASK; } if (kCAMERA_InterfaceNonGatedClock == config->interface) { reg |= OV5640_POLARITY_CTRL00_GATE_PCLK_HREF_MASK | OV5640_POLARITY_CTRL00_GATE_PCLK_VSYNC_MASK; } OV5640_CHECK_RET(OV5640_WriteReg(handle, 0x4740, reg)); /* Lenc on, raw gamma on, BPC on, WPC on, CIP on. */ OV5640_CHECK_RET(OV5640_WriteReg(handle, 0x5000, 0xa7)); OV5640_CHECK_RET(OV5640_WriteReg(handle, 0x3008, 0x02)); return kStatus_Success; } status_t OV5640_Deinit(camera_device_handle_t *handle) { ((ov5640_resource_t *)(handle->resource))->pullPowerDownPin(true); return kStatus_Success; } status_t OV5640_Control(camera_device_handle_t *handle, camera_device_cmd_t cmd, int32_t arg) { for (uint8_t i = 0; i < ARRAY_SIZE(s_ov5640CmdFuncMap); i++) { if (s_ov5640CmdFuncMap[i].cmd == cmd) { return s_ov5640CmdFuncMap[i].func(handle, arg); } } return kStatus_InvalidArgument; } status_t OV5640_Start(camera_device_handle_t *handle) { return OV5640_WriteReg(handle, 0x3008, 0x02); } status_t OV5640_Stop(camera_device_handle_t *handle) { return OV5640_WriteReg(handle, 0x3008, 0x42); } status_t OV5640_InitExt(camera_device_handle_t *handle, const camera_config_t *config, const void *specialConfig) { return OV5640_Init(handle, config); } status_t OV5640_SetBrightness(camera_device_handle_t *handle, int32_t brightness) { status_t status; if ((brightness < -4) || (brightness > 4)) { return kStatus_InvalidArgument; } OV5640_CHECK_RET(OV5640_WriteReg(handle, 0x3212, 0x03)); if (brightness >= 0) { status = OV5640_WriteReg(handle, OV5640_SDE_CTRL8_REG, 0x01); } else { brightness = -brightness; status = OV5640_WriteReg(handle, OV5640_SDE_CTRL8_REG, 0x09); } if (kStatus_Success != status) { return status; } OV5640_CHECK_RET(OV5640_WriteReg(handle, OV5640_SDE_CTRL7_REG, ((uint8_t)brightness) << 4U)); OV5640_CHECK_RET(OV5640_WriteReg(handle, 0x3212, 0x13)); return OV5640_WriteReg(handle, 0x3212, 0xa3); } status_t OV5640_SetContrast(camera_device_handle_t *handle, int32_t contrast) { status_t status; uint8_t regVal; if ((-4 > contrast) || (4 < contrast)) { return kStatus_InvalidArgument; } contrast = 0x20 + contrast * 0x04; regVal = (uint8_t)contrast; OV5640_CHECK_RET(OV5640_WriteReg(handle, 0x3212, 0x03)); OV5640_CHECK_RET(OV5640_WriteReg(handle, OV5640_SDE_CTRL0_REG, 0x04)); OV5640_CHECK_RET(OV5640_WriteReg(handle, OV5640_SDE_CTRL5_REG, regVal)); OV5640_CHECK_RET(OV5640_WriteReg(handle, OV5640_SDE_CTRL6_REG, regVal)); OV5640_CHECK_RET(OV5640_WriteReg(handle, 0x3212, 0x13)); return OV5640_WriteReg(handle, 0x3212, 0xa3); } status_t OV5640_SetSaturation(camera_device_handle_t *handle, int32_t saturation) { status_t status; uint8_t regVal; if ((-4 > saturation) || (4 < saturation)) { return kStatus_InvalidArgument; } saturation = 0x40 + saturation * 0x10; regVal = (uint8_t)saturation; OV5640_CHECK_RET(OV5640_WriteReg(handle, 0x3212, 0x03)); OV5640_CHECK_RET(OV5640_WriteReg(handle, OV5640_SDE_CTRL0_REG, 0x02)); OV5640_CHECK_RET(OV5640_WriteReg(handle, OV5640_SDE_CTRL3_REG, regVal)); OV5640_CHECK_RET(OV5640_WriteReg(handle, OV5640_SDE_CTRL4_REG, regVal)); OV5640_CHECK_RET(OV5640_WriteReg(handle, OV5640_SDE_CTRL8_REG, 0x41)); OV5640_CHECK_RET(OV5640_WriteReg(handle, 0x3212, 0x13)); return OV5640_WriteReg(handle, 0x3212, 0xa3); } status_t OV5640_SetLightMode(camera_device_handle_t *handle, int32_t lightMode) { status_t status; uint8_t i; for (i = 0; i < ARRAY_SIZE(s_ov5640LightModeConfigs); i++) { if (lightMode == (int32_t)s_ov5640LightModeConfigs[i].lightMode) { OV5640_CHECK_RET(OV5640_WriteReg(handle, 0x3212, 0x03)); OV5640_CHECK_RET(OV5640_WriteReg(handle, OV5640_AWB_CTRL_REG, s_ov5640LightModeConfigs[i].awbCtrl)); OV5640_CHECK_RET(OV5640_WriteReg(handle, OV5640_AWB_R_H_REG, s_ov5640LightModeConfigs[i].awbR_H)); OV5640_CHECK_RET(OV5640_WriteReg(handle, OV5640_AWB_R_L_REG, s_ov5640LightModeConfigs[i].awbR_L)); OV5640_CHECK_RET(OV5640_WriteReg(handle, OV5640_AWB_G_H_REG, s_ov5640LightModeConfigs[i].awbG_H)); OV5640_CHECK_RET(OV5640_WriteReg(handle, OV5640_AWB_G_L_REG, s_ov5640LightModeConfigs[i].awbG_L)); OV5640_CHECK_RET(OV5640_WriteReg(handle, OV5640_AWB_B_H_REG, s_ov5640LightModeConfigs[i].awbB_H)); OV5640_CHECK_RET(OV5640_WriteReg(handle, OV5640_AWB_B_L_REG, s_ov5640LightModeConfigs[i].awbB_L)); OV5640_CHECK_RET(OV5640_WriteReg(handle, 0x3212, 0x13)); return OV5640_WriteReg(handle, 0x3212, 0xa3); } } /* No configuration found. */ return kStatus_InvalidArgument; } status_t OV5640_SetSpecialEffect(camera_device_handle_t *handle, int32_t effect) { status_t status; uint8_t i; for (i = 0; i < ARRAY_SIZE(s_ov5640SpecialEffectConfigs); i++) { if (effect == (int32_t)s_ov5640SpecialEffectConfigs[i].effect) { OV5640_CHECK_RET(OV5640_WriteReg(handle, 0x3212, 0x03)); OV5640_CHECK_RET(OV5640_WriteReg(handle, OV5640_SDE_CTRL0_REG, s_ov5640SpecialEffectConfigs[i].sdeCtrl0)); OV5640_CHECK_RET(OV5640_WriteReg(handle, OV5640_SDE_CTRL3_REG, s_ov5640SpecialEffectConfigs[i].sdeCtrl3)); OV5640_CHECK_RET(OV5640_WriteReg(handle, OV5640_SDE_CTRL4_REG, s_ov5640SpecialEffectConfigs[i].sdeCtrl4)); OV5640_CHECK_RET(OV5640_WriteReg(handle, 0x5003, 0x08)); OV5640_CHECK_RET(OV5640_WriteReg(handle, 0x3212, 0x13)); return OV5640_WriteReg(handle, 0x3212, 0xa3); } } /* No configuration found. */ return kStatus_InvalidArgument; }